[i2c] [patch] Add Dialog Semiconductor DA9030 Power Management chip driver

Mike Rapoport mike at compulab.co.il
Wed Oct 24 10:10:21 CEST 2007


Below patch contains Dialog Semiconductor DA9030 Power Management chip driver.
It covers LDO, battery charger, built-in ADC and leds functionality.

Signed-off-by: Mike Rapoport <mike at compulab.co.il>
---
 drivers/i2c/chips/Kconfig  |   13 +
 drivers/i2c/chips/Makefile |    1 +
 drivers/i2c/chips/da9030.c | 1201 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/i2c/chips/da9030.h |  262 ++++++++++
 include/linux/da9030.h     |  117 +++++
 5 files changed, 1594 insertions(+), 0 deletions(-)
 create mode 100644 drivers/i2c/chips/da9030.c
 create mode 100644 drivers/i2c/chips/da9030.h
 create mode 100644 include/linux/da9030.h

diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index 2e1c24f..f80f2b1 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -163,4 +163,17 @@ config MENELAUS
 	  and other features that are often used in portable devices like
 	  cell phones and PDAs.

+config DA9030
+	tristate "Dialog Semiconductor DA9030 power management chip"
+	depends on EXPERIMENTAL && EMBEDDED
+	help
+	  If you say yes here you get support for the Dialog
+	  Semiconductor DA9030 power management chip.
+	  This includes voltage regulators, lithium ion/polymer battery
+	  charging, and other features that are often used in portable
+	  devices like PDAs, cell phones and cameras.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called da9030.
+
 endmenu
diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile
index ca924e1..69f545c 100644
--- a/drivers/i2c/chips/Makefile
+++ b/drivers/i2c/chips/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_ISP1301_OMAP)	+= isp1301_omap.o
 obj-$(CONFIG_TPS65010)		+= tps65010.o
 obj-$(CONFIG_MENELAUS)		+= menelaus.o
 obj-$(CONFIG_SENSORS_TSL2550)	+= tsl2550.o
+obj-$(CONFIG_DA9030)		+= da9030.o

 ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/i2c/chips/da9030.c b/drivers/i2c/chips/da9030.c
new file mode 100644
index 0000000..7230929
--- /dev/null
+++ b/drivers/i2c/chips/da9030.c
@@ -0,0 +1,1201 @@
+/*
+ * Dialog Semiconductor DA9030 power management IC driver
+ *
+ * Copyright (C) 2007 Compulab, Ltd.
+ * Mike Rapoport <mike at compulab.co.il>
+ *
+ * Some parts based on menelaus.c:
+ * Copyright (C) 2004 Texas Instruments, Inc.
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file COPYING in the main directory of this archive for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/ctype.h>
+#include <linux/uaccess.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/da9030.h>
+#include <linux/kernel_stat.h>
+#include <linux/random.h>
+#include <linux/mutex.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include "da9030.h"
+
+#define DRIVER_NAME "da9030"
+
+static unsigned short normal_i2c[] = { 0x49, I2C_CLIENT_END };
+
+I2C_CLIENT_INSMOD;
+
+struct da9030 {
+	struct mutex lock;
+	struct i2c_client *client;
+
+	struct work_struct event_work;
+
+	/* there are 24 interrupts */
+	void (*callbacks[24])(int event, void *data);
+	void *callbacks_data[24];
+
+	struct dentry *debug_file;
+};
+
+/* I hardly believe there can be more than 1 such chip in the system,
+   so keeping its global instance won't really hurt */
+static struct da9030 *the_da9030;
+
+unsigned char defined_regs[] = {
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
+	0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
+	0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a,
+	0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b,
+	0x50, 0x51,
+	0x60, 0x61, 0x62, 0x63,
+	0x80, 0x81,
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
+	0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6,
+	0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba,
+};
+
+static inline int is_reg_valid(u32 reg)
+{
+	int i;
+	for (i = 0; i < ARRAY_SIZE(defined_regs); i++)
+		if (reg == defined_regs[i])
+			return 1;
+
+	return 0;
+}
+
+s32 da9030_get_reg(u32 reg)
+{
+	if (!is_reg_valid(reg))
+		return -EINVAL;
+
+	return i2c_smbus_read_byte_data(the_da9030->client, reg);
+}
+EXPORT_SYMBOL(da9030_get_reg);
+
+s32 da9030_set_reg(u32 reg, u8 val)
+{
+	if (!is_reg_valid(reg))
+		return -EINVAL;
+
+	return i2c_smbus_write_byte_data(the_da9030->client, reg, val);
+}
+EXPORT_SYMBOL(da9030_set_reg);
+
+static s32 da9030_update_reg_bits(u32 reg, int bits, int shift, int val)
+{
+	int ret;
+	int new_val;
+
+	mutex_lock(&the_da9030->lock);
+	ret = da9030_get_reg(reg);
+	if (ret < 0)
+		goto out;
+
+	new_val = reg & ~(((1 << bits) - 1) << shift);
+	new_val |= (val << shift);
+
+	ret = da9030_set_reg(reg, new_val);
+	mutex_unlock(&the_da9030->lock);
+
+out:
+	return ret;
+}
+
+int da9030_get_status(void)
+{
+	s32 ret;
+
+	mutex_lock(&the_da9030->lock);
+	ret = da9030_get_reg(STATUS);
+	mutex_unlock(&the_da9030->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(da9030_get_status);
+
+int da9030_get_fault_log(void)
+{
+	s32 ret;
+
+	mutex_lock(&the_da9030->lock);
+	ret = da9030_get_reg(FAULT_LOG);
+	mutex_unlock(&the_da9030->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(da9030_get_fault_log);
+
+void da9030_read_adc(struct da9030_adc_res *adc)
+{
+	mutex_lock(&the_da9030->lock);
+
+	adc->vbat_res = da9030_get_reg(VBAT_RES);
+	adc->vbatmin_res = da9030_get_reg(VBATMIN_RES);
+	adc->vbatmintxon = da9030_get_reg(VBATMINTXON_RES);
+	adc->ichmax_res = da9030_get_reg(ICHMAX_RES);
+	adc->ichmin_res = da9030_get_reg(ICHMIN_RES);
+	adc->ichaverage_res = da9030_get_reg(ICHAVERAGE_RES);
+	adc->vchmax_res = da9030_get_reg(VCHMAX_RES);
+	adc->vchmin_res = da9030_get_reg(VCHMIN_RES);
+	adc->tbat_res = da9030_get_reg(TBAT_RES);
+	adc->adc_in4_res = da9030_get_reg(ADC_IN4_RES);
+	adc->adc_in5_res = da9030_get_reg(ADC_IN5_RES);
+
+	mutex_unlock(&the_da9030->lock);
+}
+EXPORT_SYMBOL(da9030_read_adc);
+
+void da9030_enable_adc(void)
+{
+	/* enable automatic A/D measurements */
+	mutex_lock(&the_da9030->lock);
+
+	da9030_set_reg(ADC_MAN_CONTROL,
+		       ADC_LDO_INT_ENABLE | ADC_TBATREF_ENABLE);
+	da9030_set_reg(ADC_MAN_CONTROL_1,
+		       ADC_LDO_INT_ENABLE | ADC_TBATREF_ENABLE);
+	da9030_set_reg(ADC_AUTO_CONTROL_1,
+		       ADC_TBAT_ENABLE | ADC_VBAT_IN_TXON | ADC_VCH_ENABLE |
+		       ADC_ICH_ENABLE | ADC_VBAT_ENABLE);
+	da9030_set_reg(ADC_AUTO_CONTROL,
+		       ADC_TBAT_ENABLE | ADC_VBAT_IN_TXON | ADC_VCH_ENABLE |
+		       ADC_ICH_ENABLE | ADC_VBAT_ENABLE);
+
+	mutex_unlock(&the_da9030->lock);
+}
+EXPORT_SYMBOL(da9030_enable_adc);
+
+void da9030_set_wled(int on, unsigned int brightness)
+{
+	u8 val;
+
+	mutex_lock(&the_da9030->lock);
+
+	if (on)
+		val = WLED_CP_ENABLE | (brightness & 0x7);
+	else
+		val = da9030_get_reg(WLED_CONTROL) & ~WLED_CP_ENABLE;
+
+	da9030_set_reg(WLED_CONTROL, val);
+
+	mutex_unlock(&the_da9030->lock);
+}
+EXPORT_SYMBOL(da9030_set_wled);
+
+int da9030_set_charger(int on, unsigned int mA, unsigned int mV)
+{
+	int ret;
+	u8 val = 0;
+	if (on) {
+		if (mA >= 1500 || mV < 4000 || mV > 4350)
+			return -EINVAL;
+
+		val = CHRG_CHARGER_ENABLE;
+		val |= (mA / 100) << 3;
+		val |= (mV - 4000) / 50;
+	}
+
+	mutex_lock(&the_da9030->lock);
+	ret = da9030_set_reg(CHARGE_CONTROL, val);
+	mutex_unlock(&the_da9030->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(da9030_set_charger);
+
+void da9030_get_charger(int *on, unsigned int *mA, unsigned int *mV)
+{
+	s32 val;
+
+	mutex_lock(&the_da9030->lock);
+
+	val = da9030_get_reg(CHARGE_CONTROL);
+
+	mutex_unlock(&the_da9030->lock);
+
+	if (on)
+		*on = (val & CHRG_CHARGER_ENABLE) ? 1 : 0;
+
+	if (mA)
+		*mA = ((val >> 3) & 0xf) * 100;
+
+	if (mV)
+		*mV = (val & 0x7) * 50 + 4000;
+}
+EXPORT_SYMBOL(da9030_get_charger);
+
+int da9030_set_led(int led, int on,
+		   enum da9030_led_rate rate,
+		   enum da9030_led_duty_cycle duty,
+		   enum da9030_led_pwm_chop pwm_chop)
+{
+	int reg;
+	int ret;
+
+	u8 val = 0;
+
+	if (led > 4)
+		return -EINVAL;
+
+	reg = LED_1_CONTROL + led;
+	if (on) {
+		val = LED_ENABLE;
+		val |= (rate & 0x3) << 5;
+		val |= (duty & 0x3) << 3;
+		val |= (pwm_chop & 0x7);
+	}
+
+	mutex_lock(&the_da9030->lock);
+	ret = da9030_set_reg(reg, val);
+	mutex_unlock(&the_da9030->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(da9030_set_led);
+
+void da9030_set_thresholds(unsigned int tbathighp, unsigned int tbathighn,
+			   unsigned int tbatlow)
+{
+	mutex_lock(&the_da9030->lock);
+
+	da9030_set_reg(TBATHIGHP, tbathighp);
+	da9030_set_reg(TBATHIGHN, tbathighn);
+	da9030_set_reg(TBATLOW, tbatlow);
+
+	mutex_unlock(&the_da9030->lock);
+}
+EXPORT_SYMBOL(da9030_set_thresholds);
+
+struct da9030_vtg_value {
+	u16 vtg;
+	u16 val;
+};
+
+struct da9030_vtg_value da9030_vtg_1V8_3V2[] = {
+	{1800, 0x0},
+	{1900, 0x1},
+	{2000, 0x2},
+	{2100, 0x3},
+	{2200, 0x4},
+	{2300, 0x5},
+	{2400, 0x6},
+	{2500, 0x7},
+	{2600, 0x8},
+	{2700, 0x9},
+	{2800, 0xa},
+	{2900, 0xb},
+	{3000, 0xc},
+	{3100, 0xd},
+	{3200, 0xe},
+	{3200, 0xf},
+};
+
+struct da9030_vtg_value da9030_vtg_1V1_2V65[] = {
+	{1100, 0x0},
+	{1150, 0x1},
+	{1200, 0x2},
+	{1250, 0x3},
+	{1300, 0x4},
+	{1350, 0x5},
+	{1400, 0x6},
+	{1450, 0x7},
+	{1500, 0x8},
+	{1550, 0x9},
+	{1600, 0xa},
+	{1650, 0xb},
+	{1700, 0xc},
+	{1750, 0xd},
+	{1800, 0xe},
+	{1850, 0xf},
+	{1900, 0x10},
+	{1950, 0x11},
+	{2000, 0x12},
+	{2050, 0x13},
+	{2100, 0x14},
+	{2150, 0x15},
+	{2200, 0x16},
+	{2250, 0x17},
+	{2300, 0x18},
+	{2350, 0x19},
+	{2400, 0x1a},
+	{2450, 0x1b},
+	{2500, 0x1c},
+	{2550, 0x1d},
+	{2600, 0x1e},
+	{2650, 0x1f},
+};
+
+struct da9030_vtg_value da9030_vtg_2V76_2V94[] = {
+	{2760, 0x7},
+	{2790, 0x6},
+	{2820, 0x5},
+	{2850, 0x4},
+	{2850, 0x3},
+	{2880, 0x1},
+	{2910, 0x2},
+	{2940, 0x3},
+};
+
+struct da9030_vtg_value da9030_vtg_0V85_1V625[] = {
+	{850, 0x0},
+	{875, 0x1},
+	{900, 0x2},
+	{925, 0x3},
+	{950, 0x4},
+	{975, 0x5},
+	{1000, 0x6},
+	{1025, 0x7},
+	{1050, 0x8},
+	{1075, 0x9},
+	{1100, 0xa},
+	{1125, 0xb},
+	{1150, 0xc},
+	{1175, 0xd},
+	{1200, 0xe},
+	{1225, 0xf},
+	{1250, 0x10},
+	{1275, 0x11},
+	{1300, 0x12},
+	{1325, 0x13},
+	{1350, 0x14},
+	{1375, 0x15},
+	{1400, 0x16},
+	{1425, 0x17},
+	{1450, 0x18},
+	{1475, 0x19},
+	{1500, 0x1a},
+	{1525, 0x1b},
+	{1550, 0x1c},
+	{1575, 0x1d},
+	{1600, 0x1e},
+	{1625, 0x1f},
+};
+
+struct ldo_param {
+	u8 reg;
+	u8 shift;
+	u8 bits;
+};
+
+struct da9030_ldo {
+	const char *name;
+
+	struct ldo_param vtg;
+	struct ldo_param sleep;
+	struct ldo_param lock;
+
+	/* several LDOs have two enable/disable bits */
+	struct ldo_param enable[2];
+
+	struct da9030_vtg_value *values;
+	int values_count;
+};
+
+static struct da9030_ldo da9030_ldos[] = {
+	[0] = {
+		.name = "LDO1",
+		.vtg = {
+			.reg = LDO_1,
+			.shift = 0,
+			.bits = 5,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 1,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = LDO_1,
+			.shift = 5,
+			.bits = 2,
+		},
+		.lock = {
+			.reg = REG_SLEEP_CONTROL1,
+			.shift = 0,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[1] = {
+		.name = "LDO2",
+		.vtg = {
+			.reg = LDO_2_3,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 2,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL1,
+			.shift = 2,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[2] = {
+		.name = "LDO3",
+		.vtg = {
+			.reg = LDO_2_3,
+			.shift = 4,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 3,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL1,
+			.shift = 4,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[3] = {
+		.name = "LDO4",
+		.vtg = {
+			.reg = LDO_4_5,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 4,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL1,
+			.shift = 6,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[4] = {
+		.name = "LDO5",
+		.vtg = {
+			.reg = LDO_4_5,
+			.shift = 4,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 5,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL2,
+			.shift = 0,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[5] = {
+		.name = "LDO6",
+		.vtg = {
+			.reg = LDO_6_SIMCP,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 6,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[6] = {
+		.name = "LDO7",
+		.vtg = {
+			.reg = LDO_7_8,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_97,
+			.shift = 7,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL2,
+			.shift = 2,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[7] = {
+		.name = "LDO8",
+		.vtg = {
+			.reg = LDO_7_8,
+			.shift = 4,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_2_98,
+			.shift = 0,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL2,
+			.shift = 4,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[8] = {
+		.name = "LDO9",
+		.vtg = {
+			.reg = LDO_9_12,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_2_98,
+			.shift = 1,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL2,
+			.shift = 6,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[9] = {
+		.name = "LDO10",
+		.vtg = {
+			.reg = LDO_10_11,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17,
+			.shift = 1,
+			.bits = 1,
+		},
+		.enable[1] = {
+			.reg = REG_CONTROL_2_98,
+			.shift = 2,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[10] = {
+		.name = "LDO11",
+		.vtg = {
+			.reg = LDO_10_11,
+			.shift = 4,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17,
+			.shift = 2,
+			.bits = 1,
+		},
+		.enable[1] = {
+			.reg = REG_CONTROL_2_98,
+			.shift = 3,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[11] = {
+		.name = "LDO12",
+		.vtg = {
+			.reg = LDO_9_12,
+			.shift = 4,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_2_98,
+			.shift = 4,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = REG_SLEEP_CONTROL3,
+			.shift = 0,
+			.bits = 2,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[12] = {
+		.name = "LDO13",
+		.vtg = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17,
+			.shift = 3,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = NULL,
+		.values_count = 0,
+	},
+	[13] = {
+		.name = "LDO14",
+		.vtg = {
+			.reg = LDO_14_16,
+			.shift = 0,
+			.bits = 3,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17, /* FIXME: or 2_98? */
+			.shift = 4,
+			.bits = 1,
+		},
+		.enable[1] = {
+			.reg = REG_CONTROL_2_98, /* FIXME: or 2_98? */
+			.shift = 5,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_2V76_2V94,
+		.values_count = ARRAY_SIZE(da9030_vtg_2V76_2V94),
+	},
+	[14] = {
+		.name = "LDO15",
+		.vtg = {
+			.reg = LDO_15,
+			.shift = 0,
+			.bits = 5,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17,
+			.shift = 5,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.lock = {
+			.reg = LDO_15,
+			.shift = 5,
+			.bits = 3,
+		},
+		.values = da9030_vtg_1V1_2V65,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V1_2V65),
+	},
+	[15] = {
+		.name = "LDO16",
+		.vtg = {
+			.reg = LDO_14_16,
+			.shift = 3,
+			.bits = 5,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17,
+			.shift = 6,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V1_2V65,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V1_2V65),
+	},
+	[16] = {
+		.name = "LDO17",
+		.vtg = {
+			.reg = LDO_17_SIMCP0,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_1_17,
+			.shift = 7,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[17] = {
+		.name = "LDO18",
+		.vtg = {
+			.reg = LDO_18_19,
+			.shift = 0,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_2_18,
+			.shift = 2,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+	[18] = {
+		.name = "LDO19",
+		.vtg = {
+			.reg = LDO_18_19,
+			.shift = 4,
+			.bits = 4,
+		},
+		.enable[0] = {
+			.reg = REG_CONTROL_2_18,
+			.shift = 1,
+			.bits = 1,
+		},
+		.sleep = {
+			.reg = 0,
+			.shift = 0,
+			.bits = 0,
+		},
+		.values = da9030_vtg_1V8_3V2,
+		.values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2),
+	},
+};
+
+static int da9030_get_vtg_value(int vtg, const struct da9030_vtg_value *tbl,
+				int n)
+{
+	int i;
+
+	for (i = 0; i < n; i++, tbl++)
+		if (tbl->vtg == vtg)
+			return tbl->val;
+	return -EINVAL;
+}
+
+static int da9030_set_ldo_volt(struct da9030_ldo *ldo, unsigned int mV)
+{
+	int val;
+
+	val = da9030_get_vtg_value(mV, ldo->values, ldo->values_count);
+	if (val < 0)
+		return val;
+
+	return da9030_update_reg_bits(ldo->vtg.reg, ldo->vtg.bits,
+				      ldo->vtg.shift, val);
+}
+
+static int da9030_enable_ldo(struct da9030_ldo *ldo, int enable)
+{
+	int ret;
+
+	ret = da9030_update_reg_bits(ldo->enable[0].reg, ldo->enable[1].bits,
+				     ldo->enable[0].shift, enable);
+
+	if (ret < 0)
+		return ret;
+
+	if (unlikely(ldo->enable[1].reg != 0))
+		ret = da9030_update_reg_bits(ldo->enable[1].reg,
+					     ldo->enable[1].bits,
+					     ldo->enable[1].shift, enable);
+
+	return ret;
+}
+
+static int da9030_set_ldo_sleep(struct da9030_ldo *ldo,
+				enum da9030_ldo_sleep_mode sleep_mode)
+{
+	return da9030_update_reg_bits(ldo->sleep.reg, ldo->sleep.bits,
+				      ldo->sleep.shift, sleep_mode);
+}
+
+int da9030_set_ldo(int ldo_num, int on, unsigned int mV,
+		   enum da9030_ldo_sleep_mode sleep_mode)
+{
+	struct da9030_ldo *ldo;
+	int ret;
+
+	if (ldo_num < 0 || ldo_num > ARRAY_SIZE(da9030_ldos))
+		return -EINVAL;
+
+	ldo = &da9030_ldos[ldo_num];
+
+	ret = da9030_set_ldo_volt(ldo, mV);
+	if (ret < 0)
+		return ret;
+
+	ret = da9030_enable_ldo(ldo, on);
+	if (ret < 0)
+		return ret;
+
+	ret = da9030_set_ldo_sleep(ldo, sleep_mode);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+EXPORT_SYMBOL(da9030_set_ldo);
+
+int da9030_set_buck(int buck, int on, unsigned int mV, int flags)
+{
+	/* FIXME: implement */
+	return 0;
+}
+EXPORT_SYMBOL(da9030_set_buck);
+
+static void da9030_disable_irq(unsigned int irq)
+{
+	int reg, val;
+
+	if (irq < 8) {
+		reg = IRQ_MASK_A;
+		val = 1 << irq;
+	} else if (irq < 16) {
+		reg = IRQ_MASK_B;
+		val = 1 << (irq - 8);
+	} else {
+		reg = IRQ_MASK_C;
+		val = 1 << (irq - 16);
+	}
+
+	i2c_smbus_write_byte_data(the_da9030->client, reg, val);
+}
+
+static void da9030_enable_irq(unsigned int irq)
+{
+	int reg, val;
+
+	if (irq < 8) {
+		reg = IRQ_MASK_A;
+		val = 1 << irq;
+	} else if (irq < 16) {
+		reg = IRQ_MASK_B;
+		val = 1 << (irq - 8);
+	} else {
+		reg = IRQ_MASK_C;
+		val = 1 << (irq - 16);
+	}
+
+	i2c_smbus_write_byte_data(
+		the_da9030->client, reg,
+		i2c_smbus_read_byte_data(the_da9030->client, reg) & ~val);
+}
+
+int da9030_register_callback(int event,
+			     void (*callback)(int event, void *data),
+			     void *data)
+{
+	int ret;
+
+	if (event < 0 || event > 23)
+		return -EINVAL;
+
+	mutex_lock(&the_da9030->lock);
+	if (the_da9030->callbacks[event])
+		ret = -EBUSY;
+	else {
+		the_da9030->callbacks[event] = callback;
+		the_da9030->callbacks_data[event] = data;
+		da9030_enable_irq(event);
+		ret = 0;
+	}
+	mutex_unlock(&the_da9030->lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(da9030_register_callback);
+
+void da9030_unregister_callback(int event)
+{
+	mutex_lock(&the_da9030->lock);
+
+	da9030_disable_irq(event);
+	the_da9030->callbacks[event] = NULL;
+	the_da9030->callbacks_data[event] = 0;
+
+	mutex_unlock(&the_da9030->lock);
+}
+EXPORT_SYMBOL(da9030_unregister_callback);
+
+#ifdef CONFIG_DEBUG_FS
+#define MAX_BUF 256
+
+static int da9030_debug_show(struct seq_file *s, void *data)
+{
+	int i, res = 0;
+	struct da9030 *da9030 = s->private;
+	struct i2c_client *client = da9030->client;
+
+	seq_printf(s, "DA9030 state: da = %p, cl = %p, s->private = %p\n",
+		   da9030, client, s->private);
+
+	for (i = 0; i < ARRAY_SIZE(defined_regs); i++) {
+		res = i2c_smbus_read_byte_data(client, defined_regs[i]);
+		seq_printf(s, "%02x %x\n", defined_regs[i], res);
+	}
+	return 0;
+}
+
+static int da9030_debug_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, da9030_debug_show, inode->i_private);
+}
+
+ssize_t da9030_debug_write(struct file *f, const char __user *buf, size_t len,
+			   loff_t *off)
+{
+	char buffer[MAX_BUF];
+	int reg, val;
+	char *endp;
+	struct da9030 *da9030 = ((struct seq_file *)f->private_data)->private;
+	struct i2c_client *client = da9030->client;
+
+	if (len > MAX_BUF) {
+		printk(KERN_INFO "%s: large buffer\n", __FUNCTION__);
+		len = MAX_BUF;
+	}
+
+	if (copy_from_user(buffer, buf, len)) {
+		printk(KERN_INFO "%s: copy_from_user failed\n", __FUNCTION__);
+		return -EFAULT;
+	}
+	buffer[len] = '\0';
+
+	reg = simple_strtoul(buffer, &endp, 0);
+	while (endp && isspace(*endp))
+		endp++;
+
+	val = simple_strtoul(endp, 0, 0);
+
+	i2c_smbus_write_byte_data(client, reg, val);
+
+	return len;
+}
+
+static const struct file_operations debug_fops = {
+	.open		= da9030_debug_open,
+	.read		= seq_read,
+	.write		= da9030_debug_write,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static struct dentry *da9030_create_debugfs(struct da9030 *da9030)
+{
+	da9030->debug_file = debugfs_create_file("da9030", 0666, 0, da9030,
+						 &debug_fops);
+	return da9030->debug_file;
+}
+
+static void da9030_remove_debugfs(struct da9030 *da9030)
+{
+	debugfs_remove(da9030->debug_file);
+}
+#else
+#define da9030_create_debugfs(x)	NULL
+#define da9030_remove_debugfs(x)	do {} while (0)
+#endif
+
+static irqreturn_t da9030_irq(int irq, void *_da9030)
+{
+	struct da9030 *da9030 = _da9030;
+
+	(void)schedule_work(&da9030->event_work);
+
+	return IRQ_HANDLED;
+}
+
+static void da9030_irq_worker(struct work_struct *work)
+{
+	struct da9030 *da9030 = container_of(work, struct da9030, event_work);
+	void (*callback)(int event, void *data);
+	u32 pending = 0;
+	u32 mask = 0;
+	int i;
+
+	while (1) {
+		pending = (i2c_smbus_read_byte_data(da9030->client,
+						    EVENT_A)) |
+			((i2c_smbus_read_byte_data(da9030->client,
+						   EVENT_B)) << 8) |
+			((i2c_smbus_read_byte_data(da9030->client,
+						   EVENT_C)) << 16);
+
+		mask = (i2c_smbus_read_byte_data(da9030->client,
+						 IRQ_MASK_A)) |
+			((i2c_smbus_read_byte_data(da9030->client,
+						   IRQ_MASK_B)) << 8) |
+			((i2c_smbus_read_byte_data(da9030->client,
+						   IRQ_MASK_C)) << 16);
+		pending &= ~mask;
+
+		if (!pending)
+			return;
+
+		while (pending) {
+			i = __ffs(pending);
+			callback = da9030->callbacks[i];
+			if (callback)
+				callback(i, da9030->callbacks_data[i]);
+			pending &= ~(1 << i);
+		}
+	}
+}
+
+static inline void da9030_disable_interrupts(struct da9030 *da9030)
+{
+	/* clear pending interruts */
+	(void)i2c_smbus_read_byte_data(da9030->client, EVENT_A);
+	(void)i2c_smbus_read_byte_data(da9030->client, EVENT_B);
+	(void)i2c_smbus_read_byte_data(da9030->client, EVENT_C);
+
+	/* disable interrupts */
+	i2c_smbus_write_byte_data(da9030->client, IRQ_MASK_A, 0xff);
+	i2c_smbus_write_byte_data(da9030->client, IRQ_MASK_B, 0xff);
+	i2c_smbus_write_byte_data(da9030->client, IRQ_MASK_C, 0xff);
+}
+
+static int da9030_probe(struct i2c_client *client)
+{
+	int ret;
+	struct da9030 *da9030;
+
+	if (the_da9030) {
+		dev_dbg(&client->dev, "only one %s for now\n",
+			DRIVER_NAME);
+		return -ENODEV;
+	}
+
+	ret = i2c_smbus_read_byte_data(client, CHIP_ID);
+
+	dev_info(&client->dev, "initialized chip revision %x\n", ret);
+
+	da9030 = kzalloc(sizeof(struct da9030), GFP_KERNEL);
+	if (da9030 == NULL) {
+		dev_err(&client->dev, "insufficient memory\n");
+		return -ENOMEM;
+	}
+	the_da9030 = da9030;
+	da9030->client = client;
+
+	mutex_init(&the_da9030->lock);
+
+	da9030_disable_interrupts(da9030);
+	INIT_WORK(&da9030->event_work, da9030_irq_worker);
+
+	ret = request_irq(client->irq, da9030_irq,
+			  IRQF_DISABLED, DRIVER_NAME, da9030);
+
+	if (ret) {
+		kfree(da9030);
+		dev_err(&client->dev,
+			"failed to allocate irq %d\n",
+			client->irq);
+		return ret;
+	}
+
+	i2c_set_clientdata(client, da9030);
+
+	da9030->debug_file = da9030_create_debugfs(da9030);
+
+	return 0;
+}
+
+/* static int da9030_detach_adapter(struct i2c_adapter *a) */
+static int da9030_remove(struct i2c_client *client)
+{
+	struct da9030 *da9030 = i2c_get_clientdata(client);
+
+	da9030_remove_debugfs(da9030);
+
+	free_irq(da9030->client->irq, da9030);
+	kfree(da9030);
+	i2c_set_clientdata(client, NULL);
+	the_da9030 = NULL;
+
+	return 0;
+}
+
+static struct i2c_driver da9030_driver = {
+	.driver = {
+		.name	= "da9030",
+		.owner	= THIS_MODULE,
+	},
+
+	.probe	= da9030_probe,
+	.remove	= da9030_remove,
+};
+
+static int da9030_init(void)
+{
+	i2c_add_driver(&da9030_driver);
+	return 0;
+}
+
+static void da9030_exit(void)
+{
+	i2c_del_driver(&da9030_driver);
+}
+
+/* NOTE:  this MUST be initialized before the other parts of the system
+ * that rely on it ... but after the i2c bus on which this relies.
+ * That is, much earlier than on PC-type systems, which don't often use
+ * I2C as a core system bus.
+ */
+subsys_initcall(da9030_init);
+module_exit(da9030_exit);
+
+MODULE_DESCRIPTION("DA9030 power manager driver");
+MODULE_AUTHOR("Mike Rapoport, Compulab");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/chips/da9030.h b/drivers/i2c/chips/da9030.h
new file mode 100644
index 0000000..f6d9bad
--- /dev/null
+++ b/drivers/i2c/chips/da9030.h
@@ -0,0 +1,262 @@
+/* DA9030 Register definintions */
+
+#define CHIP_ID	0x00
+
+#define EVENT_A			0x01
+#define EVENT_A_CHIOVER		(1 << 7)
+#define EVENT_A_VBAT_MON_TXON	(1 << 6)
+#define EVENT_A_VBAT_MON	(1 << 5)
+#define EVENT_A_TBAT		(1 << 4)
+#define EVENT_A_CHDET		(1 << 3)
+#define EVENT_A_EXTON		(1 << 2)
+#define EVENT_A_PWREN1		(1 << 1)
+#define EVENT_A_ONKEY_N		(1 << 0)
+
+#define EVENT_B			0x02
+#define EVENT_B_WDOG_INT	(1 << 7)
+#define EVENT_B_SRP_DETECT	(1 << 6)
+#define EVENT_B_SESSION_VALID	(1 << 5)
+#define EVENT_B_VBUS_VALID_4_0	(1 << 4)
+#define EVENT_B_VBUS_VALID_4_4	(1 << 3)
+#define EVENT_B_ADC_READY	(1 << 2)
+#define EVENT_B_CCTO		(1 << 1)
+#define EVENT_B_TCTO		(1 << 0)
+
+#define EVENT_C		0x03
+#define EVENT_C_ADC_IN5	(1 << 7)
+#define EVENT_C_ADC_IN4	(1 << 6)
+#define EVENT_C_BUCK2	(1 << 5)
+#define EVENT_C_LDO19	(1 << 4)
+#define EVENT_C_LDO18	(1 << 3)
+#define EVENT_C_LDO17	(1 << 2)
+#define EVENT_C_LDO16	(1 << 1)
+#define EVENT_C_LDO15	(1 << 0)
+
+#define STATUS			0x04
+#define STATUS_MCLK_DETECT	(1 << 7)
+#define STATUS_VBAT_MON_TXON	(1 << 6)
+#define STATUS_VBAT_MON		(1 << 5)
+#define STATUS_TBAT		(1 << 4)
+#define STATUS_CHDET		(1 << 3)
+#define STATUS_EXTON		(1 << 2)
+#define STATUS_PWREN1		(1 << 1)
+#define STATUS_ONKEY_N		(1 << 0)
+
+#define IRQ_MASK_A			0x05
+#define IRQ_MASK_A_CHIOVER		(1 << 7)
+#define IRQ_MASK_A_VBAT_MON_TXON	(1 << 6)
+#define IRQ_MASK_A_VBAT_MON		(1 << 5)
+#define IRQ_MASK_A_TBAT			(1 << 4)
+#define IRQ_MASK_A_CHDET		(1 << 3)
+#define IRQ_MASK_A_EXTON		(1 << 2)
+#define IRQ_MASK_A_PWREN1		(1 << 1)
+#define IRQ_MASK_A_ONKEY_N		(1 << 0)
+
+#define IRQ_MASK_B			0x06
+#define IRQ_MASK_B_WDOG_INT		(1 << 7)
+#define IRQ_MASK_B_SRP_DETECT		(1 << 6)
+#define IRQ_MASK_B_SESSION_VALID	(1 << 5)
+#define IRQ_MASK_B_VBUS_VALID_4_0	(1 << 4)
+#define IRQ_MASK_B_VBUS_VALID_4_4	(1 << 3)
+#define IRQ_MASK_B_ADC_READY		(1 << 2)
+#define IRQ_MASK_B_CCTO			(1 << 1)
+#define IRQ_MASK_B_TCTO			(1 << 0)
+
+#define IRQ_MASK_C		0x07
+#define IRQ_MASK_C_ADC_IN5	(1 << 7)
+#define IRQ_MASK_C_ADC_IN4	(1 << 6)
+#define IRQ_MASK_C_BUCK2	(1 << 5)
+#define IRQ_MASK_C_LDO19	(1 << 4)
+#define IRQ_MASK_C_LDO18	(1 << 3)
+#define IRQ_MASK_C_LDO17	(1 << 2)
+#define IRQ_MASK_C_LDO16	(1 << 1)
+#define IRQ_MASK_C_LDO15	(1 << 0)
+
+#define SYS_CTRL_A				0x08
+#define SYS_CONTROL_A_SLEEP_N_PIN_ENABLE	0x1
+#define SYS_CONTROL_A_SHUT_DOWN			(1<<1)
+#define SYS_CONTROL_A_HWRES_ENABLE		(1<<2)
+#define SYS_CONTROL_A_WDOG_ACTION		(1<<3)
+#define SYS_CONTROL_A_WATCHDOG			(1<<7)
+
+#define SYS_CONTROL_B			0x09
+#define SYS_CLTR_B_SLEEP_13MHZ_EN	(1 << 4)
+#define SYS_CLTR_B_AUTO_CLK_SWITCH	(1 << 3)
+
+#define FAULT_LOG		0x0a
+#define FAULT_LOG_OVER_TEMP	(1 << 7)
+#define FAULT_LOG_VBAT_OVER	(1 << 4)
+
+#define LDO_10_11		0x10
+#define LDO_10_11_LDO10_3V	(0xc)
+#define LDO_10_11_LDO11_3V2	(0xe << 4)
+
+#define LDO_15			0x11
+#define LDO_14_16		0x12
+#define LDO_18_19		0x13
+#define LDO_18_19_LDO18_3V2	(0xe)
+
+#define LDO_17_SIMCP0		0x14
+
+#define BUCK_2_DVC1	0x15
+#define BUCK_2_GO	(1 << 7)
+#define BUCK_2_SLEEP	(1 << 6)
+#define BUCK_2_TRIM_1V5	(0x1a)
+
+#define BUCK_2_DVC2	0x16
+
+#define REG_CONTROL_1_17	0x17
+#define RC1_LDO17_EN		(1 << 7)
+#define RC1_LDO16_EN		(1 << 6)
+#define RC1_LDO15_EN		(1 << 5)
+#define RC1_LDO14_EN		(1 << 4)
+#define RC1_LDO13_EN		(1 << 3)
+#define RC1_LDO11_EN		(1 << 2)
+#define RC1_LDO10_EN		(1 << 1)
+#define RC1_BUCK2_EN		(1 << 0)
+
+#define REG_CONTROL_2_18	0x18
+#define RC2_SIMCP_EN		(1 << 6)
+#define RC2_LDO19_EN		(1 << 1)
+#define RC2_LDO18_EN		(1 << 0)
+
+#define USBPUMP			0x19
+#define USB_PUMP_EN_USBVEP	(1 << 7)
+#define USB_PUMP_EN_USBVE	(1 << 6)
+#define USB_PUMP_SPR_DETECT	(1 << 5)
+#define USB_PUMP_SESSION_VALID	(1 << 4)
+#define USB_PUMP_VBUS_VALID_4_0 (1 << 3)
+#define USB_PUMP_VBUS_VALID_4_4 (1 << 2)
+#define USB_PUMP_USBVEP		(1 << 1)
+#define USB_PUMP_USBVE		(1 << 0)
+
+#define SLEEP_CONTROL			0x1a
+#define APP_SLEEP_CTL_PWR_EN		(1 << 7)
+#define APP_SLEEP_CTL_SYS_EN		(1 << 6)
+#define APP_SLEEP_CTL_BYPASS_LDO19	(1 << 2)
+#define APP_SLEEP_CTL_BYPASS_LDO18	(1 << 1)
+#define APP_SLEEP_CTL_BYPASS_LDO17	(1 << 0)
+
+#define STARTUP_CONTROL			0x1b
+#define STARTUP_CTL_LDO11_PWR_SYS	(1 << 3)
+#define STARTUP_CTL_LDO10_PWR_SYS	(1 << 2)
+#define STARTUP_CTL_LDO11_START		(1 << 1)
+#define STARTUP_CTL_LDO10_START		(1 << 0)
+
+#define LED_1_CONTROL	0x20
+#define LED_2_CONTROL	0x21
+#define LED_3_CONTROL	0x22
+#define LED_4_CONTROL	0x23
+#define LEDPC_CONTROL	0x24
+#define LED_ENABLE	(1 << 7)
+
+#define WLED_CONTROL	0x25
+#define WLED_CP_ENABLE	(1 << 6)
+#define WLED_ISET_10	(3)
+
+#define MISC_CONTROLA		0x26
+#define MISC_CONTROLB			0x27
+#define MISCB_SESSION_VALID_ENABLE	(1 << 3)
+#define MISCB_USB_INT_RISING		(1 << 2)
+#define MISCB_I2C_ADDRESS		(1 << 1)
+#define MISCB_STARTUP_SEQUENCE		(1 << 0)
+
+#define CHARGE_CONTROL		0x28
+#define CHRG_CHARGER_ENABLE	(1 << 7)
+
+#define CCTR_CONTROL		0x29
+#define CCTR_SET_8MIN		0x01
+
+#define TCTR_CONTROL		0x2a
+#define CHARGE_PULSE		0x2b
+
+#define ADC_MAN_CONTROL		0x30
+
+#define ADC_AUTO_CONTROL	0x31
+#define ADC_IN5_EN		(1 << 7)
+#define ADC_IN4_EN		(1 << 6)
+#define ADC_TBAT_ENABLE		(1 << 5)
+#define ADC_VBAT_IN_TXON	(1 << 4)
+#define ADC_VCH_ENABLE		(1 << 3)
+#define ADC_ICH_ENABLE		(1 << 2)
+#define ADC_VBAT_ENABLE		(1 << 1)
+#define ADC_AUDIO_SLEEP_ENABLE	(1 << 0)
+
+#define VBATMON		0x32
+#define VBATMONTXON	0x33
+#define TBATHIGHP	0x34
+#define TBATHIGHN	0x35
+#define TBATLOW		0x36
+#define ADC_IN4_MIN	0x37
+#define ADC_IN4_MAX	0x38
+#define ADC_IN5_MIN	0x39
+#define ADC_IN5_MAX	0x3a
+
+#define VBAT_RES	0x41
+#define VBATMIN_RES	0x42
+#define VBATMINTXON	0x43
+#define ICHMAX_RES	0x44
+#define ICHMIN_RES	0x45
+#define ICHAVERAGE_RES	0x46
+#define VCHMAX_RES	0x47
+#define VCHMIN_RES	0x48
+#define TBAT_RES	0x49
+#define ADC_IN4_RES	0x4a
+#define ADC_IN5_RES	0x4b
+
+#define LDO_1			0x90
+#define LDO_1_UNLOCK		(0x5 << 5)
+#define LDO_1_TRIM_3V		(0x12)
+
+#define LDO_2_3			0x91
+#define LDO_2_3_LDO2_3V2	(0xe << 4)
+#define LDO_2_3_LDO3_3V		(0xc)
+
+#define LDO_4_5			0x92
+#define LDO_6_SIMCP		0x93
+#define LDO_6_SIMCP_LDO6_3V2	(0xe)
+
+#define LDO_7_8			0x94
+#define LDO_9_12		0x95
+#define BUCK			0x96
+#define REG_CONTROL_1_97	0x97
+#define REG_CONTROL_2_98	0x98
+
+#define REG_SLEEP_CONTROL1	0x99
+#define REG_SLEEP_CONTROL2	0x9a
+#define REG_SLEEP_CONTROL3	0x9b
+
+#define ADC_MAN_CONTROL_1		0xa0
+#define ADC_DEBOUNCE_VBATMON_TXON	(1 << 7)
+#define ADC_DEBOUNCE_VBATMON		(1 << 6)
+#define ADC_TBATREF_ENABLE		(1 << 5)
+#define ADC_LDO_INT_ENABLE		(1 << 4)
+#define ADC_MAN_CONV			(1 << 3)
+#define ADC_MUX_VBAT			(0)
+
+#define ADC_AUTO_CONTROL_1	0xa1
+#define ADC_IN5_EN		(1 << 7)
+#define ADC_IN4_EN		(1 << 6)
+#define ADC_TBAT_ENABLE		(1 << 5)
+#define ADC_VBAT_IN_TXON	(1 << 4)
+#define ADC_VCH_ENABLE		(1 << 3)
+#define ADC_ICH_ENABLE		(1 << 2)
+#define ADC_VBAT_ENABLE		(1 << 1)
+#define ADC_AUDIO_SLEEP_ENABLE	(1 << 0)
+
+#define VBATMON_1		0xa2
+#define VBATMONTXMON_1		0xa3
+#define TBATHIGHP_1		0xa4
+#define TBATHIGHN_1		0xa5
+#define TBATLOW_1		0xa6
+#define MAN_RES			0xb0
+#define VBAT_RES_1		0xb1
+#define VBATMIN_RES_1		0xb2
+#define VBATMINTXON_RES		0xb3
+#define ICHMAX_RES_1		0xb4
+#define ICHMIN_RES_1		0xb5
+#define ICHAVERAGE_RES_1	0xb6
+#define VCHMAX_RES_1		0xb7
+#define VCHMIN_RES_1		0xb8
+#define TBAT_RES_1		0xb9
+#define ADC_IN4_RES_1		0xba
diff --git a/include/linux/da9030.h b/include/linux/da9030.h
new file mode 100644
index 0000000..caa0c37
--- /dev/null
+++ b/include/linux/da9030.h
@@ -0,0 +1,117 @@
+#ifndef _DA9030_H
+#define _DA9030_H
+
+/* DA9030 has 24 possible interrupts */
+#define DA9030_IRQ_COUNT 24
+
+/* EVENT A */
+#define DA9030_IRQ_ONKEY_EN	0
+#define DA9030_IRQ_PWREN1	1
+#define DA9030_IRQ_EXTON	2
+#define DA9030_IRQ_CHDET	3
+#define DA9030_IRQ_TBAT		4
+#define DA9030_IRQ_VBATMON	5
+#define DA9030_IRQ_VBATMONTXON	6
+#define DA9030_IRQ_CHIOVER	7
+
+/* EVENT B */
+#define DA9030_IRQ_TCTO		8
+#define DA9030_IRQ_CCTO		9
+#define DA9030_IRQ_ADC_READY	10
+#define DA9030_IRQ_VBUS_4_4	11
+#define DA9030_IRQ_VBUS_4_0	12
+#define DA9030_IRQ_SESSION_VALID 13
+#define DA9030_IRQ_SRP_DETECT	14
+#define DA9030_IRQ_WDOG_INTR	15
+
+/* EVENT C */
+#define DA9030_IRQ_LDO15	16
+#define DA9030_IRQ_LDO16	17
+#define DA9030_IRQ_LDO17	18
+#define DA9030_IRQ_LDO18	19
+#define DA9030_IRQ_LDO19	20
+#define DA9030_IRQ_BUCK2	21
+#define DA9030_IRQ_ADC_IN4	22
+#define DA9030_IRQ_ADC_IN5	23
+
+enum da9030_ldo_sleep_mode {
+	DA9030_LDO_SLEEP_KEEP		= 0x0,
+	DA9030_LDO_SLEEP_SHUT_DOWN	= 0x1,
+	DA9030_LDO_SLEEP_POWER_UP	= 0x2,
+	DA9030_LDO_SLEEP_HIGH_Z		= 0x3,
+};
+
+enum da9030_led_rate {
+	DA9030_LED_RATE_ON	= 0x0,
+	DA9030_LED_RATE_0_52	= 0x1,
+	DA9030_LED_RATE_1_05	= 0x2,
+	DA9030_LED_RATE_2_1	= 0x3,
+};
+
+enum da9030_led_duty_cycle {
+	DA9030_LED_DUTY_1_16	= 0x0,
+	DA9030_LED_DUTY_1_8	= 0x1,
+	DA9030_LED_DUTY_1_4	= 0x2,
+	DA9030_LED_DUTY_1_2	= 0x3,
+};
+
+enum da9030_led_pwm_chop {
+	DA9030_LED_PWM_8_8	= 0x0,
+	DA9030_LED_PWM_7_8	= 0x1,
+	DA9030_LED_PWM_6_8	= 0x2,
+	DA9030_LED_PWM_5_8	= 0x3,
+	DA9030_LED_PWM_4_8	= 0x4,
+	DA9030_LED_PWM_3_8	= 0x5,
+	DA9030_LED_PWM_2_8	= 0x6,
+	DA9030_LED_PWM_1_8	= 0x7,
+};
+
+struct da9030_adc_res {
+	int vbat_res;
+	int vbatmin_res;
+	int vbatmintxon;
+	int ichmax_res;
+	int ichmin_res;
+	int ichaverage_res;
+	int vchmax_res;
+	int vchmin_res;
+	int tbat_res;
+	int adc_in4_res;
+	int adc_in5_res;
+};
+
+extern int da9030_get_status(void);
+extern int da9030_get_fault_log(void);
+
+extern void da9030_enable_adc(void);
+extern void da9030_read_adc(struct da9030_adc_res *adc);
+
+extern void da9030_set_wled(int on, unsigned int brightness);
+
+extern int da9030_set_led(int led, int on,
+			  enum da9030_led_rate rate,
+			  enum da9030_led_duty_cycle duty,
+			  enum da9030_led_pwm_chop pwm_chop);
+
+extern int da9030_set_charger(int on, unsigned int mA, unsigned int mV);
+extern void da9030_get_charger(int *on, unsigned int *mA, unsigned int *mV);
+
+extern int da9030_set_ldo(int ldo, int on, unsigned int mV,
+			  enum da9030_ldo_sleep_mode sleep_mode);
+extern int da9030_set_buck(int buck, int on, unsigned int mV, int flags);
+
+extern void da9030_set_thresholds(unsigned int tbathighp,
+				  unsigned int tbathighn,
+				  unsigned int tbatlow);
+
+extern int da9030_register_callback(int event,
+				    void (*callback)(int event, void *data),
+				    void *data);
+extern void da9030_unregister_callback(int event);
+
+/* Keep these for now, although they are unsafe. When I'll see what
+   other methods are needed from DA9030, I'll remove these */
+extern s32 da9030_get_reg(u32 reg);
+extern s32 da9030_set_reg(u32 reg, u8 val);
+
+#endif
-- 
1.5.1.6





More information about the i2c mailing list