[i2c] [patch] Add Dialog Semiconductor DA9030 Power Management chip driver
Mike Rapoport
mike at compulab.co.il
Mon Sep 10 16:53:08 CEST 2007
Hi,
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