diff options
-rw-r--r-- | drivers/mfd/88pm860x-core.c | 61 | ||||
-rw-r--r-- | drivers/mfd/88pm860x-i2c.c | 172 | ||||
-rw-r--r-- | drivers/regulator/88pm8607.c | 32 | ||||
-rw-r--r-- | include/linux/mfd/88pm860x.h (renamed from include/linux/mfd/88pm8607.h) | 52 |
4 files changed, 203 insertions, 114 deletions
diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index d1464e54e65..72b00304dc3 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -14,7 +14,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/mfd/core.h> -#include <linux/mfd/88pm8607.h> +#include <linux/mfd/88pm860x.h> #define PM8607_REG_RESOURCE(_start, _end) \ @@ -67,18 +67,23 @@ static struct mfd_cell pm8607_devs[] = { PM8607_REG_DEVS(ldo14, LDO14), }; -int pm860x_device_init(struct pm8607_chip *chip, - struct pm8607_platform_data *pdata) +static void device_8606_init(struct pm860x_chip *chip, struct i2c_client *i2c, + struct pm860x_platform_data *pdata) +{ +} + +static void device_8607_init(struct pm860x_chip *chip, struct i2c_client *i2c, + struct pm860x_platform_data *pdata) { int i, count; int ret; - ret = pm8607_reg_read(chip, PM8607_CHIP_ID); + ret = pm860x_reg_read(i2c, PM8607_CHIP_ID); if (ret < 0) { dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret); goto out; } - if ((ret & PM8607_ID_MASK) == PM8607_ID) + if ((ret & PM8607_VERSION_MASK) == PM8607_VERSION) dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n", ret); else { @@ -86,9 +91,9 @@ int pm860x_device_init(struct pm8607_chip *chip, "Chip ID: %02x\n", ret); goto out; } - chip->chip_id = ret; + chip->chip_version = ret; - ret = pm8607_reg_read(chip, PM8607_BUCK3); + ret = pm860x_reg_read(i2c, PM8607_BUCK3); if (ret < 0) { dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret); goto out; @@ -96,20 +101,11 @@ int pm860x_device_init(struct pm8607_chip *chip, if (ret & PM8607_BUCK3_DOUBLE) chip->buck3_double = 1; - ret = pm8607_reg_read(chip, PM8607_MISC1); + ret = pm860x_reg_read(i2c, PM8607_MISC1); if (ret < 0) { dev_err(chip->dev, "Failed to read MISC1 register: %d\n", ret); goto out; } - if (pdata->i2c_port == PI2C_PORT) - ret |= PM8607_MISC1_PI2C; - else - ret &= ~PM8607_MISC1_PI2C; - ret = pm8607_reg_write(chip, PM8607_MISC1, ret); - if (ret < 0) { - dev_err(chip->dev, "Failed to write MISC1 register: %d\n", ret); - goto out; - } count = ARRAY_SIZE(pm8607_devs); for (i = 0; i < count; i++) { @@ -121,14 +117,39 @@ int pm860x_device_init(struct pm8607_chip *chip, } } out: - return ret; + return; +} + +int pm860x_device_init(struct pm860x_chip *chip, + struct pm860x_platform_data *pdata) +{ + switch (chip->id) { + case CHIP_PM8606: + device_8606_init(chip, chip->client, pdata); + break; + case CHIP_PM8607: + device_8607_init(chip, chip->client, pdata); + break; + } + + if (chip->companion) { + switch (chip->id) { + case CHIP_PM8607: + device_8606_init(chip, chip->companion, pdata); + break; + case CHIP_PM8606: + device_8607_init(chip, chip->companion, pdata); + break; + } + } + return 0; } -void pm8607_device_exit(struct pm8607_chip *chip) +void pm860x_device_exit(struct pm860x_chip *chip) { mfd_remove_devices(chip->dev); } -MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM8607"); +MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM860x"); MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>"); MODULE_LICENSE("GPL"); diff --git a/drivers/mfd/88pm860x-i2c.c b/drivers/mfd/88pm860x-i2c.c index dda23cbfe41..6d7dba2bce8 100644 --- a/drivers/mfd/88pm860x-i2c.c +++ b/drivers/mfd/88pm860x-i2c.c @@ -1,5 +1,5 @@ /* - * I2C driver for Marvell 88PM8607 + * I2C driver for Marvell 88PM860x * * Copyright (C) 2009 Marvell International Ltd. * Haojian Zhuang <haojian.zhuang@marvell.com> @@ -12,12 +12,11 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/i2c.h> -#include <linux/mfd/88pm8607.h> +#include <linux/mfd/88pm860x.h> -static inline int pm8607_read_device(struct pm8607_chip *chip, +static inline int pm860x_read_device(struct i2c_client *i2c, int reg, int bytes, void *dest) { - struct i2c_client *i2c = chip->client; unsigned char data; int ret; @@ -32,10 +31,9 @@ static inline int pm8607_read_device(struct pm8607_chip *chip, return 0; } -static inline int pm8607_write_device(struct pm8607_chip *chip, +static inline int pm860x_write_device(struct i2c_client *i2c, int reg, int bytes, void *src) { - struct i2c_client *i2c = chip->client; unsigned char buf[bytes + 1]; int ret; @@ -48,13 +46,14 @@ static inline int pm8607_write_device(struct pm8607_chip *chip, return 0; } -int pm8607_reg_read(struct pm8607_chip *chip, int reg) +int pm860x_reg_read(struct i2c_client *i2c, int reg) { + struct pm860x_chip *chip = i2c_get_clientdata(i2c); unsigned char data; int ret; mutex_lock(&chip->io_lock); - ret = chip->read(chip, reg, 1, &data); + ret = pm860x_read_device(i2c, reg, 1, &data); mutex_unlock(&chip->io_lock); if (ret < 0) @@ -62,111 +61,178 @@ int pm8607_reg_read(struct pm8607_chip *chip, int reg) else return (int)data; } -EXPORT_SYMBOL(pm8607_reg_read); +EXPORT_SYMBOL(pm860x_reg_read); -int pm8607_reg_write(struct pm8607_chip *chip, int reg, +int pm860x_reg_write(struct i2c_client *i2c, int reg, unsigned char data) { + struct pm860x_chip *chip = i2c_get_clientdata(i2c); int ret; mutex_lock(&chip->io_lock); - ret = chip->write(chip, reg, 1, &data); + ret = pm860x_write_device(i2c, reg, 1, &data); mutex_unlock(&chip->io_lock); return ret; } -EXPORT_SYMBOL(pm8607_reg_write); +EXPORT_SYMBOL(pm860x_reg_write); -int pm8607_bulk_read(struct pm8607_chip *chip, int reg, +int pm860x_bulk_read(struct i2c_client *i2c, int reg, int count, unsigned char *buf) { + struct pm860x_chip *chip = i2c_get_clientdata(i2c); int ret; mutex_lock(&chip->io_lock); - ret = chip->read(chip, reg, count, buf); + ret = pm860x_read_device(i2c, reg, count, buf); mutex_unlock(&chip->io_lock); return ret; } -EXPORT_SYMBOL(pm8607_bulk_read); +EXPORT_SYMBOL(pm860x_bulk_read); -int pm8607_bulk_write(struct pm8607_chip *chip, int reg, +int pm860x_bulk_write(struct i2c_client *i2c, int reg, int count, unsigned char *buf) { + struct pm860x_chip *chip = i2c_get_clientdata(i2c); int ret; mutex_lock(&chip->io_lock); - ret = chip->write(chip, reg, count, buf); + ret = pm860x_write_device(i2c, reg, count, buf); mutex_unlock(&chip->io_lock); return ret; } -EXPORT_SYMBOL(pm8607_bulk_write); +EXPORT_SYMBOL(pm860x_bulk_write); -int pm8607_set_bits(struct pm8607_chip *chip, int reg, +int pm860x_set_bits(struct i2c_client *i2c, int reg, unsigned char mask, unsigned char data) { + struct pm860x_chip *chip = i2c_get_clientdata(i2c); unsigned char value; int ret; mutex_lock(&chip->io_lock); - ret = chip->read(chip, reg, 1, &value); + ret = pm860x_read_device(i2c, reg, 1, &value); if (ret < 0) goto out; value &= ~mask; value |= data; - ret = chip->write(chip, reg, 1, &value); + ret = pm860x_write_device(i2c, reg, 1, &value); out: mutex_unlock(&chip->io_lock); return ret; } -EXPORT_SYMBOL(pm8607_set_bits); +EXPORT_SYMBOL(pm860x_set_bits); static const struct i2c_device_id pm860x_id_table[] = { - { "88PM8607", 0 }, + { "88PM860x", 0 }, {} }; MODULE_DEVICE_TABLE(i2c, pm860x_id_table); +static int verify_addr(struct i2c_client *i2c) +{ + unsigned short addr_8607[] = {0x30, 0x34}; + unsigned short addr_8606[] = {0x10, 0x11}; + int size, i; + + if (i2c == NULL) + return 0; + size = ARRAY_SIZE(addr_8606); + for (i = 0; i < size; i++) { + if (i2c->addr == *(addr_8606 + i)) + return CHIP_PM8606; + } + size = ARRAY_SIZE(addr_8607); + for (i = 0; i < size; i++) { + if (i2c->addr == *(addr_8607 + i)) + return CHIP_PM8607; + } + return 0; +} + static int __devinit pm860x_probe(struct i2c_client *client, const struct i2c_device_id *id) { - struct pm8607_platform_data *pdata = client->dev.platform_data; - struct pm8607_chip *chip; - int ret; - - chip = kzalloc(sizeof(struct pm8607_chip), GFP_KERNEL); - if (chip == NULL) - return -ENOMEM; - - chip->client = client; - chip->dev = &client->dev; - chip->read = pm8607_read_device; - chip->write = pm8607_write_device; - memcpy(&chip->id, id, sizeof(struct i2c_device_id)); - i2c_set_clientdata(client, chip); - - mutex_init(&chip->io_lock); - dev_set_drvdata(chip->dev, chip); - - ret = pm860x_device_init(chip, pdata); - if (ret < 0) - goto out; - - + struct pm860x_platform_data *pdata = client->dev.platform_data; + static struct pm860x_chip *chip; + struct i2c_board_info i2c_info = { + .type = "88PM860x", + .platform_data = client->dev.platform_data, + }; + int addr_c, found_companion = 0; + + if (pdata == NULL) { + pr_info("No platform data in %s!\n", __func__); + return -EINVAL; + } + + /* + * Both client and companion client shares same platform driver. + * Driver distinguishes them by pdata->companion_addr. + * pdata->companion_addr is only assigned if companion chip exists. + * At the same time, the companion_addr shouldn't equal to client + * address. + */ + addr_c = pdata->companion_addr; + if (addr_c && (addr_c != client->addr)) { + i2c_info.addr = addr_c; + found_companion = 1; + } + + if (found_companion || (addr_c == 0)) { + chip = kzalloc(sizeof(struct pm860x_chip), GFP_KERNEL); + if (chip == NULL) + return -ENOMEM; + + chip->id = verify_addr(client); + chip->companion_addr = addr_c; + chip->client = client; + i2c_set_clientdata(client, chip); + chip->dev = &client->dev; + mutex_init(&chip->io_lock); + dev_set_drvdata(chip->dev, chip); + + if (found_companion) { + /* + * If this driver is built in, probe function is + * recursive. + * If this driver is built as module, the next probe + * function is called after the first one finished. + */ + chip->companion = i2c_new_device(client->adapter, + &i2c_info); + } + } + + /* + * If companion chip existes, it's called by companion probe. + * If there's no companion chip, it's called by client probe. + */ + if ((addr_c == 0) || (addr_c == client->addr)) { + chip->companion = client; + i2c_set_clientdata(chip->companion, chip); + pm860x_device_init(chip, pdata); + } return 0; - -out: - i2c_set_clientdata(client, NULL); - kfree(chip); - return ret; } static int __devexit pm860x_remove(struct i2c_client *client) { - struct pm8607_chip *chip = i2c_get_clientdata(client); - + struct pm860x_chip *chip = i2c_get_clientdata(client); + + /* + * If companion existes, companion client is removed first. + * Because companion client is registered last and removed first. + */ + if (chip->companion_addr == client->addr) + return 0; + pm860x_device_exit(chip); + i2c_unregister_device(chip->companion); + i2c_set_clientdata(chip->companion, NULL); + i2c_set_clientdata(chip->client, NULL); kfree(chip); return 0; } diff --git a/drivers/regulator/88pm8607.c b/drivers/regulator/88pm8607.c index 04719551381..97897a6bf4f 100644 --- a/drivers/regulator/88pm8607.c +++ b/drivers/regulator/88pm8607.c @@ -11,15 +11,17 @@ #include <linux/kernel.h> #include <linux/init.h> #include <linux/err.h> +#include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/regulator/driver.h> #include <linux/regulator/machine.h> -#include <linux/mfd/88pm8607.h> +#include <linux/mfd/88pm860x.h> struct pm8607_regulator_info { struct regulator_desc desc; - struct pm8607_chip *chip; + struct pm860x_chip *chip; struct regulator_dev *regulator; + struct i2c_client *i2c; int min_uV; int max_uV; @@ -46,7 +48,7 @@ static inline int check_range(struct pm8607_regulator_info *info, static int pm8607_list_voltage(struct regulator_dev *rdev, unsigned index) { struct pm8607_regulator_info *info = rdev_get_drvdata(rdev); - uint8_t chip_id = info->chip->chip_id; + uint8_t chip_id = info->chip->chip_version; int ret = -EINVAL; switch (info->desc.id) { @@ -169,7 +171,7 @@ static int pm8607_list_voltage(struct regulator_dev *rdev, unsigned index) static int choose_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) { struct pm8607_regulator_info *info = rdev_get_drvdata(rdev); - uint8_t chip_id = info->chip->chip_id; + uint8_t chip_id = info->chip->chip_version; int val = -ENOENT; int ret; @@ -428,7 +430,6 @@ static int pm8607_set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) { struct pm8607_regulator_info *info = rdev_get_drvdata(rdev); - struct pm8607_chip *chip = info->chip; uint8_t val, mask; int ret; @@ -443,13 +444,13 @@ static int pm8607_set_voltage(struct regulator_dev *rdev, val = (uint8_t)(ret << info->vol_shift); mask = ((1 << info->vol_nbits) - 1) << info->vol_shift; - ret = pm8607_set_bits(chip, info->vol_reg, mask, val); + ret = pm860x_set_bits(info->i2c, info->vol_reg, mask, val); if (ret) return ret; switch (info->desc.id) { case PM8607_ID_BUCK1: case PM8607_ID_BUCK3: - ret = pm8607_set_bits(chip, info->update_reg, + ret = pm860x_set_bits(info->i2c, info->update_reg, 1 << info->update_bit, 1 << info->update_bit); break; @@ -460,11 +461,10 @@ static int pm8607_set_voltage(struct regulator_dev *rdev, static int pm8607_get_voltage(struct regulator_dev *rdev) { struct pm8607_regulator_info *info = rdev_get_drvdata(rdev); - struct pm8607_chip *chip = info->chip; uint8_t val, mask; int ret; - ret = pm8607_reg_read(chip, info->vol_reg); + ret = pm860x_reg_read(info->i2c, info->vol_reg); if (ret < 0) return ret; @@ -477,9 +477,8 @@ static int pm8607_get_voltage(struct regulator_dev *rdev) static int pm8607_enable(struct regulator_dev *rdev) { struct pm8607_regulator_info *info = rdev_get_drvdata(rdev); - struct pm8607_chip *chip = info->chip; - return pm8607_set_bits(chip, info->enable_reg, + return pm860x_set_bits(info->i2c, info->enable_reg, 1 << info->enable_bit, 1 << info->enable_bit); } @@ -487,19 +486,17 @@ static int pm8607_enable(struct regulator_dev *rdev) static int pm8607_disable(struct regulator_dev *rdev) { struct pm8607_regulator_info *info = rdev_get_drvdata(rdev); - struct pm8607_chip *chip = info->chip; - return pm8607_set_bits(chip, info->enable_reg, + return pm860x_set_bits(info->i2c, info->enable_reg, 1 << info->enable_bit, 0); } static int pm8607_is_enabled(struct regulator_dev *rdev) { struct pm8607_regulator_info *info = rdev_get_drvdata(rdev); - struct pm8607_chip *chip = info->chip; int ret; - ret = pm8607_reg_read(chip, info->enable_reg); + ret = pm860x_reg_read(info->i2c, info->enable_reg); if (ret < 0) return ret; @@ -589,8 +586,8 @@ static inline struct pm8607_regulator_info *find_regulator_info(int id) static int __devinit pm8607_regulator_probe(struct platform_device *pdev) { - struct pm8607_chip *chip = dev_get_drvdata(pdev->dev.parent); - struct pm8607_platform_data *pdata = chip->dev->platform_data; + struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + struct pm860x_platform_data *pdata = chip->dev->platform_data; struct pm8607_regulator_info *info = NULL; info = find_regulator_info(pdev->id); @@ -599,6 +596,7 @@ static int __devinit pm8607_regulator_probe(struct platform_device *pdev) return -EINVAL; } + info->i2c = (chip->id == CHIP_PM8607) ? chip->client : chip->companion; info->chip = chip; info->regulator = regulator_register(&info->desc, &pdev->dev, diff --git a/include/linux/mfd/88pm8607.h b/include/linux/mfd/88pm860x.h index 6e4dcdca02a..5845ae47df3 100644 --- a/include/linux/mfd/88pm8607.h +++ b/include/linux/mfd/88pm860x.h @@ -1,5 +1,5 @@ /* - * Marvell 88PM8607 Interface + * Marvell 88PM860x Interface * * Copyright (C) 2009 Marvell International Ltd. * Haojian Zhuang <haojian.zhuang@marvell.com> @@ -9,8 +9,15 @@ * published by the Free Software Foundation. */ -#ifndef __LINUX_MFD_88PM8607_H -#define __LINUX_MFD_88PM8607_H +#ifndef __LINUX_MFD_88PM860X_H +#define __LINUX_MFD_88PM860X_H + +enum { + CHIP_INVALID = 0, + CHIP_PM8606, + CHIP_PM8607, + CHIP_MAX, +}; enum { PM8607_ID_BUCK1 = 0, @@ -33,8 +40,8 @@ enum { PM8607_ID_RG_MAX, }; -#define PM8607_ID (0x40) /* 8607 chip ID */ -#define PM8607_ID_MASK (0xF8) /* 8607 chip ID mask */ +#define PM8607_VERSION (0x40) /* 8607 chip ID */ +#define PM8607_VERSION_MASK (0xF0) /* 8607 chip ID mask */ /* Interrupt Registers */ #define PM8607_STATUS_1 (0x01) @@ -180,18 +187,16 @@ enum { PM8607_CHIP_B0 = 0x48, }; - -struct pm8607_chip { +struct pm860x_chip { struct device *dev; struct mutex io_lock; struct i2c_client *client; - struct i2c_device_id id; - - int (*read)(struct pm8607_chip *chip, int reg, int bytes, void *dest); - int (*write)(struct pm8607_chip *chip, int reg, int bytes, void *src); + struct i2c_client *companion; /* companion chip client */ int buck3_double; /* DVC ramp slope double */ - unsigned char chip_id; + unsigned short companion_addr; + int id; + unsigned char chip_version; }; @@ -202,22 +207,21 @@ enum { PI2C_PORT, }; -struct pm8607_platform_data { - int i2c_port; /* Controlled by GI2C or PI2C */ +struct pm860x_platform_data { + unsigned short companion_addr; /* I2C address of companion chip */ + int i2c_port; /* Controlled by GI2C or PI2C */ struct regulator_init_data *regulator[PM8607_MAX_REGULATOR]; }; -extern int pm8607_reg_read(struct pm8607_chip *, int); -extern int pm8607_reg_write(struct pm8607_chip *, int, unsigned char); -extern int pm8607_bulk_read(struct pm8607_chip *, int, int, - unsigned char *); -extern int pm8607_bulk_write(struct pm8607_chip *, int, int, - unsigned char *); -extern int pm8607_set_bits(struct pm8607_chip *, int, unsigned char, +extern int pm860x_reg_read(struct i2c_client *, int); +extern int pm860x_reg_write(struct i2c_client *, int, unsigned char); +extern int pm860x_bulk_read(struct i2c_client *, int, int, unsigned char *); +extern int pm860x_bulk_write(struct i2c_client *, int, int, unsigned char *); +extern int pm860x_set_bits(struct i2c_client *, int, unsigned char, unsigned char); -extern int pm860x_device_init(struct pm8607_chip *chip, - struct pm8607_platform_data *pdata); -extern void pm860x_device_exit(struct pm8607_chip *chip); +extern int pm860x_device_init(struct pm860x_chip *chip, + struct pm860x_platform_data *pdata); +extern void pm860x_device_exit(struct pm860x_chip *chip); #endif /* __LINUX_MFD_88PM860X_H */ |