mfd: Add 88PM8607 driver

This adds a core driver for 88PM8607 found in Marvell DKB development
platform. This driver is a proxy for all accesses to 88PM8607
sub-drivers which will be merged on top of this one, RTC, regulators,
battery and so on.

This chip is manufactured by Marvell.

Signed-off-by: Haojian Zhuang <haojian.zhuang@marvell.com>
Reviewed-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Haojian Zhuang 2009-09-17 08:54:03 -04:00 committed by Samuel Ortiz
parent f40542532e
commit 4107da2a28
4 changed files with 530 additions and 0 deletions

302
drivers/mfd/88pm8607.c Normal file
View File

@ -0,0 +1,302 @@
/*
* Base driver for Marvell 88PM8607
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/i2c.h>
#include <linux/mfd/core.h>
#include <linux/mfd/88pm8607.h>
#define PM8607_REG_RESOURCE(_start, _end) \
{ \
.start = PM8607_##_start, \
.end = PM8607_##_end, \
.flags = IORESOURCE_IO, \
}
static struct resource pm8607_regulator_resources[] = {
PM8607_REG_RESOURCE(BUCK1, BUCK1),
PM8607_REG_RESOURCE(BUCK2, BUCK2),
PM8607_REG_RESOURCE(BUCK3, BUCK3),
PM8607_REG_RESOURCE(LDO1, LDO1),
PM8607_REG_RESOURCE(LDO2, LDO2),
PM8607_REG_RESOURCE(LDO3, LDO3),
PM8607_REG_RESOURCE(LDO4, LDO4),
PM8607_REG_RESOURCE(LDO5, LDO5),
PM8607_REG_RESOURCE(LDO6, LDO6),
PM8607_REG_RESOURCE(LDO7, LDO7),
PM8607_REG_RESOURCE(LDO8, LDO8),
PM8607_REG_RESOURCE(LDO9, LDO9),
PM8607_REG_RESOURCE(LDO10, LDO10),
PM8607_REG_RESOURCE(LDO12, LDO12),
PM8607_REG_RESOURCE(LDO14, LDO14),
};
#define PM8607_REG_DEVS(_name, _id) \
{ \
.name = "88pm8607-" #_name, \
.num_resources = 1, \
.resources = &pm8607_regulator_resources[PM8607_ID_##_id], \
}
static struct mfd_cell pm8607_devs[] = {
PM8607_REG_DEVS(buck1, BUCK1),
PM8607_REG_DEVS(buck2, BUCK2),
PM8607_REG_DEVS(buck3, BUCK3),
PM8607_REG_DEVS(ldo1, LDO1),
PM8607_REG_DEVS(ldo2, LDO2),
PM8607_REG_DEVS(ldo3, LDO3),
PM8607_REG_DEVS(ldo4, LDO4),
PM8607_REG_DEVS(ldo5, LDO5),
PM8607_REG_DEVS(ldo6, LDO6),
PM8607_REG_DEVS(ldo7, LDO7),
PM8607_REG_DEVS(ldo8, LDO8),
PM8607_REG_DEVS(ldo9, LDO9),
PM8607_REG_DEVS(ldo10, LDO10),
PM8607_REG_DEVS(ldo12, LDO12),
PM8607_REG_DEVS(ldo14, LDO14),
};
static inline int pm8607_read_device(struct pm8607_chip *chip,
int reg, int bytes, void *dest)
{
struct i2c_client *i2c = chip->client;
unsigned char data;
int ret;
data = (unsigned char)reg;
ret = i2c_master_send(i2c, &data, 1);
if (ret < 0)
return ret;
ret = i2c_master_recv(i2c, dest, bytes);
if (ret < 0)
return ret;
return 0;
}
static inline int pm8607_write_device(struct pm8607_chip *chip,
int reg, int bytes, void *src)
{
struct i2c_client *i2c = chip->client;
unsigned char buf[bytes + 1];
int ret;
buf[0] = (unsigned char)reg;
memcpy(&buf[1], src, bytes);
ret = i2c_master_send(i2c, buf, bytes + 1);
if (ret < 0)
return ret;
return 0;
}
int pm8607_reg_read(struct pm8607_chip *chip, int reg)
{
unsigned char data;
int ret;
mutex_lock(&chip->io_lock);
ret = chip->read(chip, reg, 1, &data);
mutex_unlock(&chip->io_lock);
if (ret < 0)
return ret;
else
return (int)data;
}
EXPORT_SYMBOL(pm8607_reg_read);
int pm8607_reg_write(struct pm8607_chip *chip, int reg,
unsigned char data)
{
int ret;
mutex_lock(&chip->io_lock);
ret = chip->write(chip, reg, 1, &data);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm8607_reg_write);
int pm8607_bulk_read(struct pm8607_chip *chip, int reg,
int count, unsigned char *buf)
{
int ret;
mutex_lock(&chip->io_lock);
ret = chip->read(chip, reg, count, buf);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm8607_bulk_read);
int pm8607_bulk_write(struct pm8607_chip *chip, int reg,
int count, unsigned char *buf)
{
int ret;
mutex_lock(&chip->io_lock);
ret = chip->write(chip, reg, count, buf);
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm8607_bulk_write);
int pm8607_set_bits(struct pm8607_chip *chip, int reg,
unsigned char mask, unsigned char data)
{
unsigned char value;
int ret;
mutex_lock(&chip->io_lock);
ret = chip->read(chip, reg, 1, &value);
if (ret < 0)
goto out;
value &= ~mask;
value |= data;
ret = chip->write(chip, reg, 1, &value);
out:
mutex_unlock(&chip->io_lock);
return ret;
}
EXPORT_SYMBOL(pm8607_set_bits);
static const struct i2c_device_id pm8607_id_table[] = {
{ "88PM8607", 0 },
{}
};
MODULE_DEVICE_TABLE(i2c, pm8607_id_table);
static int __devinit pm8607_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 i, count;
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;
i2c_set_clientdata(client, chip);
mutex_init(&chip->io_lock);
dev_set_drvdata(chip->dev, chip);
ret = pm8607_reg_read(chip, PM8607_CHIP_ID);
if (ret < 0) {
dev_err(chip->dev, "Failed to read CHIP ID: %d\n", ret);
goto out;
}
if ((ret & CHIP_ID_MASK) == CHIP_ID)
dev_info(chip->dev, "Marvell 88PM8607 (ID: %02x) detected\n",
ret);
else {
dev_err(chip->dev, "Failed to detect Marvell 88PM8607. "
"Chip ID: %02x\n", ret);
goto out;
}
chip->chip_id = ret;
ret = pm8607_reg_read(chip, PM8607_BUCK3);
if (ret < 0) {
dev_err(chip->dev, "Failed to read BUCK3 register: %d\n", ret);
goto out;
}
if (ret & PM8607_BUCK3_DOUBLE)
chip->buck3_double = 1;
ret = pm8607_reg_read(chip, 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++) {
ret = mfd_add_devices(chip->dev, i, &pm8607_devs[i],
1, NULL, 0);
if (ret != 0) {
dev_err(chip->dev, "Failed to add subdevs\n");
goto out;
}
}
return 0;
out:
i2c_set_clientdata(client, NULL);
kfree(chip);
return ret;
}
static int __devexit pm8607_remove(struct i2c_client *client)
{
struct pm8607_chip *chip = i2c_get_clientdata(client);
mfd_remove_devices(chip->dev);
kfree(chip);
return 0;
}
static struct i2c_driver pm8607_driver = {
.driver = {
.name = "88PM8607",
.owner = THIS_MODULE,
},
.probe = pm8607_probe,
.remove = __devexit_p(pm8607_remove),
.id_table = pm8607_id_table,
};
static int __init pm8607_init(void)
{
int ret;
ret = i2c_add_driver(&pm8607_driver);
if (ret != 0)
pr_err("Failed to register 88PM8607 I2C driver: %d\n", ret);
return ret;
}
subsys_initcall(pm8607_init);
static void __exit pm8607_exit(void)
{
i2c_del_driver(&pm8607_driver);
}
module_exit(pm8607_exit);
MODULE_DESCRIPTION("PMIC Driver for Marvell 88PM8607");
MODULE_AUTHOR("Haojian Zhuang <haojian.zhuang@marvell.com>");
MODULE_LICENSE("GPL");

View File

@ -319,6 +319,16 @@ config EZX_PCAP
This enables the PCAP ASIC present on EZX Phones. This is This enables the PCAP ASIC present on EZX Phones. This is
needed for MMC, TouchScreen, Sound, USB, etc.. needed for MMC, TouchScreen, Sound, USB, etc..
config MFD_88PM8607
bool "Support Marvell 88PM8607"
depends on I2C
select MFD_CORE
help
This supports for Marvell 88PM8607 Power Management IC. This includes
the I2C driver and the core APIs _only_, you have to select
individual components like voltage regulators, RTC and
battery-charger under the corresponding menus.
endmenu endmenu
menu "Multimedia Capabilities Port drivers" menu "Multimedia Capabilities Port drivers"

View File

@ -52,3 +52,4 @@ obj-$(CONFIG_PCF50633_ADC) += pcf50633-adc.o
obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o
obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
obj-$(CONFIG_MFD_88PM8607) += 88pm8607.o

View File

@ -0,0 +1,217 @@
/*
* Marvell 88PM8607 Interface
*
* Copyright (C) 2009 Marvell International Ltd.
* Haojian Zhuang <haojian.zhuang@marvell.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __LINUX_MFD_88PM8607_H
#define __LINUX_MFD_88PM8607_H
enum {
PM8607_ID_BUCK1 = 0,
PM8607_ID_BUCK2,
PM8607_ID_BUCK3,
PM8607_ID_LDO1,
PM8607_ID_LDO2,
PM8607_ID_LDO3,
PM8607_ID_LDO4,
PM8607_ID_LDO5,
PM8607_ID_LDO6,
PM8607_ID_LDO7,
PM8607_ID_LDO8,
PM8607_ID_LDO9,
PM8607_ID_LDO10,
PM8607_ID_LDO12,
PM8607_ID_LDO14,
PM8607_ID_RG_MAX,
};
#define CHIP_ID (0x40)
#define CHIP_ID_MASK (0xF8)
/* Interrupt Registers */
#define PM8607_STATUS_1 (0x01)
#define PM8607_STATUS_2 (0x02)
#define PM8607_INT_STATUS1 (0x03)
#define PM8607_INT_STATUS2 (0x04)
#define PM8607_INT_STATUS3 (0x05)
#define PM8607_INT_MASK_1 (0x06)
#define PM8607_INT_MASK_2 (0x07)
#define PM8607_INT_MASK_3 (0x08)
/* Regulator Control Registers */
#define PM8607_LDO1 (0x10)
#define PM8607_LDO2 (0x11)
#define PM8607_LDO3 (0x12)
#define PM8607_LDO4 (0x13)
#define PM8607_LDO5 (0x14)
#define PM8607_LDO6 (0x15)
#define PM8607_LDO7 (0x16)
#define PM8607_LDO8 (0x17)
#define PM8607_LDO9 (0x18)
#define PM8607_LDO10 (0x19)
#define PM8607_LDO12 (0x1A)
#define PM8607_LDO14 (0x1B)
#define PM8607_SLEEP_MODE1 (0x1C)
#define PM8607_SLEEP_MODE2 (0x1D)
#define PM8607_SLEEP_MODE3 (0x1E)
#define PM8607_SLEEP_MODE4 (0x1F)
#define PM8607_GO (0x20)
#define PM8607_SLEEP_BUCK1 (0x21)
#define PM8607_SLEEP_BUCK2 (0x22)
#define PM8607_SLEEP_BUCK3 (0x23)
#define PM8607_BUCK1 (0x24)
#define PM8607_BUCK2 (0x25)
#define PM8607_BUCK3 (0x26)
#define PM8607_BUCK_CONTROLS (0x27)
#define PM8607_SUPPLIES_EN11 (0x2B)
#define PM8607_SUPPLIES_EN12 (0x2C)
#define PM8607_GROUP1 (0x2D)
#define PM8607_GROUP2 (0x2E)
#define PM8607_GROUP3 (0x2F)
#define PM8607_GROUP4 (0x30)
#define PM8607_GROUP5 (0x31)
#define PM8607_GROUP6 (0x32)
#define PM8607_SUPPLIES_EN21 (0x33)
#define PM8607_SUPPLIES_EN22 (0x34)
/* RTC Control Registers */
#define PM8607_RTC1 (0xA0)
#define PM8607_RTC_COUNTER1 (0xA1)
#define PM8607_RTC_COUNTER2 (0xA2)
#define PM8607_RTC_COUNTER3 (0xA3)
#define PM8607_RTC_COUNTER4 (0xA4)
#define PM8607_RTC_EXPIRE1 (0xA5)
#define PM8607_RTC_EXPIRE2 (0xA6)
#define PM8607_RTC_EXPIRE3 (0xA7)
#define PM8607_RTC_EXPIRE4 (0xA8)
#define PM8607_RTC_TRIM1 (0xA9)
#define PM8607_RTC_TRIM2 (0xAA)
#define PM8607_RTC_TRIM3 (0xAB)
#define PM8607_RTC_TRIM4 (0xAC)
#define PM8607_RTC_MISC1 (0xAD)
#define PM8607_RTC_MISC2 (0xAE)
#define PM8607_RTC_MISC3 (0xAF)
/* Misc Registers */
#define PM8607_CHIP_ID (0x00)
#define PM8607_LDO1 (0x10)
#define PM8607_DVC3 (0x26)
#define PM8607_MISC1 (0x40)
/* bit definitions for PM8607 events */
#define PM8607_EVENT_ONKEY (1 << 0)
#define PM8607_EVENT_EXTON (1 << 1)
#define PM8607_EVENT_CHG (1 << 2)
#define PM8607_EVENT_BAT (1 << 3)
#define PM8607_EVENT_RTC (1 << 4)
#define PM8607_EVENT_CC (1 << 5)
#define PM8607_EVENT_VBAT (1 << 8)
#define PM8607_EVENT_VCHG (1 << 9)
#define PM8607_EVENT_VSYS (1 << 10)
#define PM8607_EVENT_TINT (1 << 11)
#define PM8607_EVENT_GPADC0 (1 << 12)
#define PM8607_EVENT_GPADC1 (1 << 13)
#define PM8607_EVENT_GPADC2 (1 << 14)
#define PM8607_EVENT_GPADC3 (1 << 15)
#define PM8607_EVENT_AUDIO_SHORT (1 << 16)
#define PM8607_EVENT_PEN (1 << 17)
#define PM8607_EVENT_HEADSET (1 << 18)
#define PM8607_EVENT_HOOK (1 << 19)
#define PM8607_EVENT_MICIN (1 << 20)
#define PM8607_EVENT_CHG_TIMEOUT (1 << 21)
#define PM8607_EVENT_CHG_DONE (1 << 22)
#define PM8607_EVENT_CHG_FAULT (1 << 23)
/* bit definitions of Status Query Interface */
#define PM8607_STATUS_CC (1 << 3)
#define PM8607_STATUS_PEN (1 << 4)
#define PM8607_STATUS_HEADSET (1 << 5)
#define PM8607_STATUS_HOOK (1 << 6)
#define PM8607_STATUS_MICIN (1 << 7)
#define PM8607_STATUS_ONKEY (1 << 8)
#define PM8607_STATUS_EXTON (1 << 9)
#define PM8607_STATUS_CHG (1 << 10)
#define PM8607_STATUS_BAT (1 << 11)
#define PM8607_STATUS_VBUS (1 << 12)
#define PM8607_STATUS_OV (1 << 13)
/* bit definitions of BUCK3 */
#define PM8607_BUCK3_DOUBLE (1 << 6)
/* bit definitions of Misc1 */
#define PM8607_MISC1_PI2C (1 << 0)
/* Interrupt Number in 88PM8607 */
enum {
PM8607_IRQ_ONKEY = 0,
PM8607_IRQ_EXTON,
PM8607_IRQ_CHG,
PM8607_IRQ_BAT,
PM8607_IRQ_RTC,
PM8607_IRQ_VBAT = 8,
PM8607_IRQ_VCHG,
PM8607_IRQ_VSYS,
PM8607_IRQ_TINT,
PM8607_IRQ_GPADC0,
PM8607_IRQ_GPADC1,
PM8607_IRQ_GPADC2,
PM8607_IRQ_GPADC3,
PM8607_IRQ_AUDIO_SHORT = 16,
PM8607_IRQ_PEN,
PM8607_IRQ_HEADSET,
PM8607_IRQ_HOOK,
PM8607_IRQ_MICIN,
PM8607_IRQ_CHG_FAIL,
PM8607_IRQ_CHG_DONE,
PM8607_IRQ_CHG_FAULT,
};
enum {
PM8607_CHIP_A0 = 0x40,
PM8607_CHIP_A1 = 0x41,
PM8607_CHIP_B0 = 0x48,
};
struct pm8607_chip {
struct device *dev;
struct mutex io_lock;
struct i2c_client *client;
int (*read)(struct pm8607_chip *chip, int reg, int bytes, void *dest);
int (*write)(struct pm8607_chip *chip, int reg, int bytes, void *src);
int buck3_double; /* DVC ramp slope double */
unsigned char chip_id;
};
#define PM8607_MAX_REGULATOR 15 /* 3 Bucks, 12 LDOs */
enum {
GI2C_PORT = 0,
PI2C_PORT,
};
struct pm8607_platform_data {
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,
unsigned char);
#endif /* __LINUX_MFD_88PM8607_H */