Commit 53dbab7a authored by Haojian Zhuang's avatar Haojian Zhuang Committed by Samuel Ortiz

mfd: Support 88pm8606 in 860x driver

88PM8606 and 88PM8607 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 another chip is also used, user should mark it in
companion_addr field of platform init data. Then driver could create another
I2C client for the companion chip.

All I2C operations are accessed by 860x-i2c driver. In order to support both
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 8606 and 8607.

Since API is changed, update API in 8607 regulator driver.
Signed-off-by: default avatarHaojian Zhuang <haojian.zhuang@marvell.com>
Signed-off-by: default avatarSamuel Ortiz <sameo@linux.intel.com>
parent bbd51b1f
......@@ -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");
/*
* 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);
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;
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);
chip->dev = &client->dev;
mutex_init(&chip->io_lock);
dev_set_drvdata(chip->dev, chip);
ret = pm860x_device_init(chip, pdata);
if (ret < 0)
goto out;
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;
}
......
......@@ -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,
......
/*
* 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 {
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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment