[PATCH 1/3] mfd: support 88pm80x in 80x driver

Qiao Zhou zhouqiao at marvell.com
Thu Jun 14 23:17:21 EDT 2012


On 06/14/2012 08:27 PM, Arnd Bergmann wrote:
> On Wednesday 13 June 2012, Qiao Zhou wrote:
>> 88PM800 and 88PM805 are two discrete chips used for power management.
>> Hardware designer can use them together or only one of them according to
>> requirement.
>>
>> There's some logic tightly linked between these two chips. For example, USB
>> charger driver needs to access both chips by I2C interface.
>>
>> Now share one driver to these two devices. Only one I2C client is identified
>> in platform init data. If one chip is also used, user should mark it with
>> related i2c_addr field of platform init data. Then driver could create another
>> I2C client for the chip.
>>
>> All I2C operations are accessed by 80x-i2c driver. In order to support all
>> I2C client address, the read/write API is changed in below.
>>
>> reg_read(client, offset)
>> reg_write(client, offset, data)
>>
>> The benefit is that client drivers only need one kind of read/write API. I2C
>> and MFD driver can be shared in both 800 and 805.
>>
>> Signed-off-by: Qiao Zhou<zhouqiao at marvell.com>
>
>
>
>
>>   drivers/mfd/88pm80x-core.c  | 1002 +++++++++++++++++++++++++++++++++++++++++++
>>   drivers/mfd/88pm80x-i2c.c   |  370 ++++++++++++++++
>>   drivers/mfd/Kconfig         |   11 +
>>   drivers/mfd/Makefile        |    2 +
>>   include/linux/mfd/88pm80x.h |  701 ++++++++++++++++++++++++++++++
>>   5 files changed, 2086 insertions(+), 0 deletions(-)
>>   create mode 100644 drivers/mfd/88pm80x-core.c
>>   create mode 100644 drivers/mfd/88pm80x-i2c.c
>>   create mode 100644 include/linux/mfd/88pm80x.h
>>
>> +
>> +static struct resource rtc_resources[] = {
>> +	{
>> +	 .name = "88pm80x-rtc",
>> +	 .start = PM800_IRQ_RTC,
>> +	 .end = PM800_IRQ_RTC,
>> +	 .flags = IORESOURCE_IRQ,
>> +	 },
>> +};
>> +
>> +static struct mfd_cell rtc_devs[] = {
>> +	{
>> +	 .name = "88pm80x-rtc",
>> +	 .num_resources = ARRAY_SIZE(rtc_resources),
>> +	 .resources =&rtc_resources[0],
>> +	 .id = -1,
>> +	 },
>> +};
>> +
>> +static struct resource onkey_resources[] = {
>> +	{
>> +	 .name = "88pm80x-onkey",
>> +	 .start = PM800_IRQ_ONKEY,
>> +	 .end = PM800_IRQ_ONKEY,
>> +	 .flags = IORESOURCE_IRQ,
>> +	 },
>> +};
>> +
>> +static struct mfd_cell onkey_devs[] = {
>> +	{
>> +	 .name = "88pm80x-onkey",
>> +	 .num_resources = 1,
>> +	 .resources =&onkey_resources[0],
>> +	 .id = -1,
>> +	 },
>> +};
>
> I wonder if it really makes sense to use the mfd_cell abstraction here, when each
> array only contains a single device. Why not just use
> platform_device_register_simple()?
two reasons for using mfd_dev: firstly it's extensible if we have more 
onkey_devices in future; secondly it's for device registry consistency.
>
>> +
>> +static struct pm80x_irq_data pm805_irqs[] = {
>> +	[PM805_IRQ_LDO_OFF] = {	/*0 */
>> +			       .reg = PM805_INT_STATUS1,
>> +			       .mask_reg = PM805_INT_MASK1,
>> +			       .offs = 1<<  5,
>> +			       },
>> +	[PM805_IRQ_SRC_DPLL_LOCK] = {
>> +				     .reg = PM805_INT_STATUS1,
>> +				     .mask_reg = PM805_INT_MASK1,
>> +				     .offs = 1<<  4,
>> +				     },
>> +	[PM805_IRQ_CLIP_FAULT] = {
>> +				  .reg = PM805_INT_STATUS1,
>> +				  .mask_reg = PM805_INT_MASK1,
>> +				  .offs = 1<<  3,
>> +				  },
>> +	[PM805_IRQ_MIC_CONFLICT] = {
>> +				    .reg = PM805_INT_STATUS1,
>> +				    .mask_reg = PM805_INT_MASK1,
>> +				    .offs = 1<<  2,
>> +				    },
>
> [coding style] This uses a very unusual indentation. Just use a single tab to
> indent the fields in each of the irq data descriptions.
would update in next version.
>
>> +static inline struct pm80x_irq_data *irq_to_pm805(struct pm80x_chip *chip,
>> +						  int irq)
>> +{
>> +	int offset = irq - chip->pm805_chip->irq_base;
>> +	if (!chip->pm805_chip || (offset<  0)
>> +	    || (offset>= ARRAY_SIZE(pm805_irqs)))
>> +		return NULL;
>> +	return&pm805_irqs[offset];
>> +}
>> +
>> +static irqreturn_t pm805_irq(int irq, void *data)
>> +{
>> +	struct pm80x_chip *chip = data;
>> +	struct pm80x_subchip *pm805_chip = chip->pm805_chip;
>> +	struct pm80x_irq_data *irq_data;
>> +	struct i2c_client *i2c;
>> +	int i, read_reg = -1, value = 0;
>
> The functions for pm800 and pm805 look almost identical. Have you tried
> consolidating them so you can use the same irqchip and code with different
> init data structures?
would update in next version.
>
>> +int pm80x_reg_read(struct i2c_client *i2c, int reg)
>> +{
>> +	struct regmap *map = verify_regmap(i2c);
>> +	unsigned int data;
>> +	int ret;
>> +
>> +	ret = regmap_read(map, reg,&data);
>> +	if (ret<  0)
>> +		return ret;
>> +	else
>> +		return (int)data;
>> +}
>> +EXPORT_SYMBOL(pm80x_reg_read);
>> +
>> +int pm80x_reg_write(struct i2c_client *i2c, int reg,
>> +		     unsigned char data)
>> +{
>> +	struct regmap *map = verify_regmap(i2c);
>> +	int ret;
>> +
>> +	ret = regmap_write(map, reg, data);
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL(pm80x_reg_write);
>
> These functions should be EXPORT_SYMBOL_GPL if you really need them.
> My impression however is that you'd be better off without them
> and just let the client use the regmap directly after you export
> the verify_regmap function under a more appropriate name.
>
would update EXPORT_SYMBOL_GPL and export related regmap API.
>> +static const struct i2c_device_id pm80x_id_table[] = {
>> +	{"88PM80x", 0},
>> +	{}
>> +};
>> +
>> +MODULE_DEVICE_TABLE(i2c, pm80x_id_table);
>> +
>> +static int verify_addr(struct i2c_client *i2c)
>> +{
>> +	unsigned short addr_800[] = { 0x30, 0x34 };
>> +	unsigned short addr_805[] = { 0x38, 0x39 };
>> +	int size, i;
>> +
>> +	if (i2c == NULL)
>> +		return 0;
>> +	size = ARRAY_SIZE(addr_800);
>> +	for (i = 0; i<  size; i++) {
>> +		if (i2c->addr == *(addr_800 + i))
>> +			return CHIP_PM800;
>> +	}
>> +	size = ARRAY_SIZE(addr_805);
>> +	for (i = 0; i<  size; i++) {
>> +		if (i2c->addr == *(addr_805 + i))
>> +			return CHIP_PM805;
>> +	}
>> +	return 0;
>> +}
>
> I think you should just put the devices separately into the device
> tree so they get probed independently. Hardcoding the address
> in the source code does not seem appropriate. Also, I would expect
> that the device name is already the specific one (88pm800 or
> 88pm805) in the device tree. Just put both in the pm80x_id_table
> and use the driver_data field to distinguish them.
>
would update in next version.
>> +static int pm80x_pages_init(struct pm80x_chip *chip, struct i2c_client *client,
>> +			    struct pm80x_platform_data *pdata)
>> +{
>> +	/*
>> +	 * Both pm800 and pm805 shares the same platform driver.
>> +	 * check whether we have have pm805 driver.
>> +	 */
>> +	if (pdata->pm805_addr) {
>> +		chip->pm805_addr = pdata->pm805_addr;
>> +		/* in case we only have pm805, it already registers
>> +		 * i2c handle, then no need to register again.
>> +		 */
>> +		if (chip->id != CHIP_PM805) {
>> +			chip->client_pm805 =
>> +			    i2c_new_dummy(client->adapter, chip->pm805_addr);
>> +			chip->regmap_pm805 = devm_regmap_init_i2c(chip->client_pm805,&pm80x_regmap_config);
>> +			i2c_set_clientdata(chip->client_pm805, chip);
>> +
>> +			device_init_wakeup(&chip->client_pm805->dev, 1);
>> +		}
>
> No reason to put the address into platform_data, just use the regular
> i2c device address.
>
would update in next version.
>> +struct pm80x_subchip {
>> +	struct device *dev;
>> +	struct pm80x_chip *chip;
>> +	struct pm80x_platform_data *pdata;
>> +	struct i2c_client *client;
>> +	int irq;
>> +	int irq_base;
>> +	int irq_mode;
>> +};
>
> What is a "subchip"?
>
>> +struct pm80x_chip {
>> +	/*chip_version can only on the top of the struct*/
>> +	unsigned char chip800_version;
>> +	unsigned char chip805_version;
>> +	struct device *dev;
>> +	struct pm80x_subchip *pm800_chip;
>> +	struct pm80x_subchip *pm805_chip;
>> +	struct mutex io_lock;
>> +	struct mutex pm800_irq_lock;
>> +	struct mutex pm805_irq_lock;
>> +	struct i2c_client *client_pm800;
>> +	struct i2c_client *client_pm805;
>> +	struct i2c_client *base_page;	/* chip client for base page */
>> +	struct i2c_client *power_page;	/* chip client for power page */
>> +	struct i2c_client *gpadc_page;	/* chip client for gpadc page */
>> +	struct regmap *regmap_pm800;
>> +	struct regmap *regmap_pm805;
>> +	struct regmap *regmap_power;
>> +	struct regmap *regmap_gpadc;
>> +
>> +	unsigned short pm800_addr;
>> +	unsigned short pm805_addr;
>> +	unsigned short base_page_addr;	/* base page I2C address */
>> +	unsigned short power_page_addr;	/* power page I2C address */
>> +	unsigned short gpadc_page_addr;	/* gpadc page I2C address */
>> +	int id;
>> +	int irq_pm800;
>> +	int irq_pm805;
>> +	int irq_pm800_base;
>> +	int irq_pm805_base;
>> +	unsigned int wu_flag_pm800;
>> +	unsigned int wu_flag_pm805;
>> +};
>
> You have duplicated almost every field in this structure, which appears
> to be very suboptimal. Better put all common fields into one structure
> and then make a derived data structure for the specific ones, like
>
> struct pm80x_chip {
> 	char version;
> 	struct pm80x_subchip *subchip;
> 	struct mutex irq_lock;
> 	struct i2c_client i2c;
> 	...
> 	unsigned int wu_flag;
> };
>
> struct pm800_chip {
> 	struct pm80x_chip chip;
> 	struct i2c_client *base_page;
> 	...
> };
>
> struct pm805_chip {
> 	struct pm80x_chip chip;
> 	struct i2c_client *power_page;
> 	...
> };
>
you're right. the code is not well managed. would update in next version.
>> +struct pm80x_rtc_pdata {
>> +	int		(*sync)(unsigned int ticks);
>> +	int		vrtc;
>> +	int             rtc_wakeup;
>> +};
>> +
>> +struct pm80x_platform_data {
>> +	struct pm80x_rtc_pdata *rtc;
>> +	unsigned short pm800_addr;
>> +	unsigned short pm805_addr;
>> +	unsigned short base_page_addr;	/* base page I2C address */
>> +	unsigned short power_page_addr;	/* power page I2C address */
>> +	unsigned short gpadc_page_addr;	/* gpadc page I2C address */
>> +	unsigned short test_page_addr;	/* test page regs I2C address */
>> +	int i2c_port;		/* Controlled by GI2C or PI2C */
>> +	int irq_mode;		/* Clear interrupt by read/write(0/1) */
>> +	int irq_pm800;		/* IRQ of 88pm800 */
>> +	int irq_pm805;		/* IRQ of 88pm805 */
>> +	int irq_pm800_base;		/* IRQ base number of 88pm800 */
>> +	int irq_pm805_base;		/* IRQ base number of 88pm805 */
>> +	int batt_det;		/* enable/disable */
>> +
>> +	int (*pm800_plat_config)(struct pm80x_chip *chip,
>> +				struct pm80x_platform_data *pdata);
>> +	int (*pm805_plat_config)(struct pm80x_chip *chip,
>> +				struct pm80x_platform_data *pdata);
>> +};
>
> The *_plat_config fields are completely unused here, better remove them.
>
> The interrupt numbers should become irq resources.
>
> Any field in the platform data that cannot be converted to a resource
> or completely removed should have a respective property in the device
> tree binding for this device.
would update in next version.
>
> 	Arnd

Arnd, thanks for your careful review and great suggestions!

-- 

Best Regards
Qiao



More information about the linux-arm-kernel mailing list