Hi, Some comments inlined below. >-----Original Message----- >From: Madhusudhan Chikkature<madhu.cr@xxxxxx> > >ARM: OMAP: Triton Battery Charger Interface > >This patch provides a OMAP Triton battery charger interface >driver to moniter >the battery >through power class subsystem. > >Signed-off-by: Madhusudhan Chikkature<madhu.cr@xxxxxx> >--- > drivers/power/Kconfig | 8 > drivers/power/Makefile | 1 > drivers/power/twl4030_bci_battery.c | 1139 >++++++++++++++++++++++++++++++++++++ > 3 files changed, 1148 insertions(+) > >Index: linux-omap-2.6/drivers/power/Kconfig >=================================================================== >--- linux-omap-2.6.orig/drivers/power/Kconfig 2008-06-16 >16:46:52.000000000 +0530 >+++ linux-omap-2.6/drivers/power/Kconfig 2008-06-27 >16:01:08.000000000 +0530 >@@ -70,4 +70,12 @@ config BATTERY_BQ27200 > help > Say Y here to enable support for batteries with >BQ27200(I2C) chip. > >+config TWL4030_BCI_BATTERY >+ tristate "OMAP TWL4030 BCI Battery driver" >+ depends on (MACH_OMAP_2430SDP || MACH_OMAP_3430SDP) && >TWL4030_CORE >+ default y >+ help >+ Support for OMAP TWL4030 BCI Battery driver. >+ This driver can give support for TWL4030 Battery >Charge Interface. >+ > endif # POWER_SUPPLY >Index: linux-omap-2.6/drivers/power/Makefile >=================================================================== >--- linux-omap-2.6.orig/drivers/power/Makefile 2008-06-16 >16:46:52.000000000 +0530 >+++ linux-omap-2.6/drivers/power/Makefile 2008-06-27 >15:53:29.000000000 +0530 >@@ -21,3 +21,4 @@ obj-$(CONFIG_BATTERY_DS2760) += ds2760_b > obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o > obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o > obj-$(CONFIG_BATTERY_BQ27x00) += bq27x00_battery.o >+obj-$(CONFIG_TWL4030_BCI_BATTERY) += twl4030_bci_battery.o >Index: linux-omap-2.6/drivers/power/twl4030_bci_battery.c >=================================================================== >--- /dev/null 1970-01-01 00:00:00.000000000 +0000 >+++ linux-omap-2.6/drivers/power/twl4030_bci_battery.c 2008-07-03 >16:10:11.000000000 +0530 >@@ -0,0 +1,1139 @@ >+/* >+ * linux/drivers/power/twl4030_bci_battery.c >+ * >+ * OMAP2430/3430 BCI battery driver for Linux >+ * >+ * Copyright (C) 2008 Texas Instruments, Inc. >+ * Author: Texas Instruments, Inc. >+ * >+ * This package 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. >+ * >+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR >+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED >+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. >+ */ >+ >+#include <linux/init.h> >+#include <linux/module.h> >+#include <linux/device.h> >+#include <linux/interrupt.h> >+#include <linux/delay.h> >+#include <linux/platform_device.h> >+#include <linux/i2c/twl4030.h> >+#include <linux/power_supply.h> >+ >+#define T2_BATTERY_VOLT 0x04 >+#define T2_BATTERY_TEMP 0x06 >+#define T2_BATTERY_CUR 0x08 >+ >+/* charger constants */ >+#define NO_PW_CONN 0 >+#define AC_PW_CONN 0x01 >+#define USB_PW_CONN 0x02 >+ >+/* TWL4030_MODULE_USB */ >+#define REG_POWER_CTRL 0x0AC >+#define OTG_EN 0x020 >+#define REG_PHY_CLK_CTRL 0x0FE >+#define REG_PHY_CLK_CTRL_STS 0x0FF >+#define PHY_DPLL_CLK 0x01 >+ >+#define REG_BCICTL1 0x023 >+#define REG_BCICTL2 0x024 >+#define CGAIN 0x020 >+#define ITHEN 0x010 >+#define ITHSENS 0x007 >+ >+/* Boot BCI flag bits */ >+#define BCIAUTOWEN 0x020 >+#define CONFIG_DONE 0x010 >+#define BCIAUTOUSB 0x002 >+#define BCIAUTOAC 0x001 >+#define BCIMSTAT_MASK 0x03F >+ >+/* Boot BCI register */ >+#define REG_BOOT_BCI 0x007 >+#define REG_CTRL1 0x00 >+#define MADC_ON 0x01 >+#define REG_SW1SELECT_MSB 0x07 >+#define SW1_CH9_SEL 0x02 >+#define REG_CTRL_SW1 0x012 >+#define SW1_TRIGGER 0x020 >+#define EOC_SW1 0x002 >+#define REG_GPCH9 0x049 >+#define REG_STS_HW_CONDITIONS 0x0F >+#define STS_VBUS 0x080 >+#define STS_CHG 0x02 >+#define REG_BCIMSTATEC 0x02 >+#define REG_BCIMFSTS4 0x010 >+#define REG_BCIMFSTS2 0x00E >+#define REG_BCIMFSTS3 0x00F >+#define REG_BCIMFSTS1 0x001 >+#define USBFASTMCHG 0x004 >+#define BATSTSPCHG 0x004 >+#define BATSTSMCHG 0x040 >+#define VBATOV4 0x020 >+#define VBATOV3 0x010 >+#define VBATOV2 0x008 >+#define VBATOV1 0x004 >+#define MADC_LSB_MASK 0xC0 >+#define REG_BB_CFG 0x012 >+#define BBCHEN 0x010 >+ >+/* Power supply charge interrupt */ >+#define REG_PWR_ISR1 0x00 >+#define REG_PWR_IMR1 0x01 >+#define REG_PWR_EDR1 0x05 >+#define REG_PWR_SIH_CTRL 0x007 >+ >+#define USB_PRES 0x004 >+#define CHG_PRES 0x002 >+ >+#define USB_PRES_RISING 0x020 >+#define USB_PRES_FALLING 0x010 >+#define CHG_PRES_RISING 0x008 >+#define CHG_PRES_FALLING 0x004 >+#define AC_STATEC 0x20 >+#define COR 0x004 >+ >+/* interrupt status registers */ >+#define REG_BCIISR1A 0x0 >+#define REG_BCIISR2A 0x01 >+ >+/* Interrupt flags bits BCIISR1 */ >+#define BATSTS_ISR1 0x080 >+#define VBATLVL_ISR1 0x001 >+ >+/* Interrupt mask registers for int1*/ >+#define REG_BCIIMR1A 0x002 >+#define REG_BCIIMR2A 0x003 >+ >+ /* Interrupt masks for BCIIMR1 */ >+#define BATSTS_IMR1 0x080 >+#define VBATLVL_IMR1 0x001 >+ >+/* Interrupt edge detection register */ >+#define REG_BCIEDR1 0x00A >+#define REG_BCIEDR2 0x00B >+#define REG_BCIEDR3 0x00C >+ >+/* BCIEDR2 */ >+#define BATSTS_EDRRISIN 0x080 >+#define BATSTS_EDRFALLING 0x040 >+ >+/* BCIEDR3 */ >+#define VBATLVL_EDRRISIN 0x02 >+ >+/* Step size and prescaler ratio */ >+#define TEMP_STEP_SIZE 147 >+#define TEMP_PSR_R 100 >+ >+#define VOLT_STEP_SIZE 588 >+#define VOLT_PSR_R 100 >+ >+#define CURR_STEP_SIZE 147 >+#define CURR_PSR_R1 44 >+#define CURR_PSR_R2 80 >+ >+#define BK_VOLT_STEP_SIZE 441 >+#define BK_VOLT_PSR_R 100 >+ >+#define ENABLE 1 >+#define DISABLE 1 >+ >+static int twl4030_bci_battery_probe(struct platform_device *dev); >+static int twl4030_bci_battery_remove(struct platform_device *dev); >+#ifdef CONFIG_PM >+static int twl4030_bci_battery_suspend(struct platform_device *dev, >+ pm_message_t state); >+static int twl4030_bci_battery_resume(struct platform_device *dev); >+#endif >+ >+struct twl4030_bci_device_info { >+ struct device *dev; >+ >+ unsigned long update_time; >+ int voltage_uV; >+ int bk_voltage_uV; >+ int current_uA; >+ int temp_C; >+ int charge_rsoc; >+ int charge_status; >+ >+ struct power_supply bat; >+ struct power_supply bk_bat; >+ struct delayed_work twl4030_bci_monitor_work; >+ struct delayed_work twl4030_bk_bci_monitor_work; >+}; >+ >+static struct platform_driver twl4030_bci_battery_driver = { >+ .probe = twl4030_bci_battery_probe, >+ .remove = twl4030_bci_battery_remove, >+#ifdef CONFIG_PM >+ .suspend = twl4030_bci_battery_suspend, >+ .resume = twl4030_bci_battery_resume, >+#endif >+ .driver = { >+ .name = "twl4030-bci-battery", >+ }, >+}; >+ >+static int usb_charger_flag; >+static int LVL_1, LVL_2, LVL_3, LVL_4; >+ >+static int twl4030madc_sw1_trigger(void); >+static int read_bci_val(u8 reg_1); >+static inline int clear_n_set(u8 mod_no, u8 clear, u8 set, u8 reg); >+static int twl4030charger_presence(void); >+ >+/* >+ * Twl4030 battery temperature lookup table. >+ */ >+const int twl4030battery_temp_tbl [] = >+{ >+/* 0 C*/ >+27100, >+26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900, >+17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100, >+11600, 11200, 10800, 10400, 10000, 9630, 9280, 8950, >8620, 8310, >+8020, 7730, 7460, 7200, 6950, 6710, 6470, 6250, > 6040, 5830, >+5640, 5450, 5260, 5090, 4920, 4760, 4600, 4450, > 4310, 4170, >+4040, 3910, 3790, 3670, 3550 >+}; Please mention the thermistor name/code (and possibly a reference to the spec) which gives these temperature characteristics. How about boards with other thermistors than this? Negative temperatures? >+ >+/* >+ * Report and clear the charger presence event. >+ */ >+static inline int twl4030charger_presence_evt(void) >+{ >+ int ret; >+ u8 chg_sts, set = 0, clear = 0; >+ >+ /* read charger power supply status */ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &chg_sts, >+ REG_STS_HW_CONDITIONS); >+ if (ret) >+ return IRQ_NONE; >+ >+ /* If the AC charger have been connected */ >+ if (chg_sts & STS_CHG) { >+ /* configuring falling edge detection for CHG_PRES */ >+ set = CHG_PRES_FALLING; >+ clear = CHG_PRES_RISING; >+ } >+ /* If the AC charger have been disconnected */ >+ else { >+ /* configuring rising edge detection for CHG_PRES */ >+ set = CHG_PRES_RISING; >+ clear = CHG_PRES_FALLING; >+ } >+ >+ /* Update the interrupt edge detection register */ >+ clear_n_set(TWL4030_MODULE_INT, clear, set, REG_PWR_EDR1); >+ >+ return 0; >+} >+ >+/* >+ * Interrupt service routine >+ * >+ * Attends to TWL 4030 power module interruptions events, specifically >+ * USB_PRES (USB charger presence) CHG_PRES (AC charger >presence) events >+ * >+ */ >+static irqreturn_t twl4030charger_interrupt(int irq, void *dev_id) >+{ >+ struct twl4030_bci_device_info *di = dev_id; >+ >+ twl4030charger_presence_evt(); >+ power_supply_changed(&di->bat); >+ >+ return IRQ_HANDLED; >+} >+ >+/* >+ * This function handles the twl4030 battery presence interrupt >+ */ >+static int twl4030battery_presence_evt(void) >+{ >+ int ret; >+ u8 batstsmchg, batstspchg; >+ >+ /* check for the battery presence in main charge*/ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, >+ &batstsmchg, REG_BCIMFSTS3); >+ if (ret) >+ return ret; >+ >+ /* check for the battery presence in precharge */ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_PRECHARGE, >+ &batstspchg, REG_BCIMFSTS1); >+ if (ret) >+ return ret; >+ >+ /* >+ * REVISIT: Physically inserting/removing the batt >+ * does not seem to generate an int on 3430ES2 SDP. >+ */ >+ >+ /* In case of the battery insertion event */ >+ if ((batstspchg & BATSTSPCHG) || (batstsmchg & BATSTSMCHG)) { >+ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, >BATSTS_EDRRISIN, >+ BATSTS_EDRFALLING, REG_BCIEDR2); >+ if (ret) >+ return ret; >+ } >+ >+ /* In case of the battery removal event */ >+ else { >+ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, >BATSTS_EDRFALLING, >+ BATSTS_EDRRISIN, REG_BCIEDR2); >+ if (ret) >+ return ret; >+ } >+ >+ return 0; >+} >+ >+/* >+ * This function handles the twl4030 battery voltage level interrupt. >+ */ >+static int twl4030battery_level_evt(void) >+{ >+ int ret; >+ u8 mfst; >+ >+ /* checking for threshold event */ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, >+ &mfst, REG_BCIMFSTS2); >+ if (ret) >+ return ret; >+ >+ if (mfst & VBATOV4) { >+ LVL_4 = 1; >+ LVL_3 = LVL_2 = LVL_1 = 0; >+ } else if (mfst & VBATOV3) { >+ LVL_3 = 1; >+ LVL_4 = LVL_2 = LVL_1 = 0; >+ } else if (mfst & VBATOV2) { >+ LVL_2 = 1; >+ LVL_4 = LVL_3 = LVL_1 = 0; >+ } else { >+ LVL_1 = 1; >+ LVL_4 = LVL_3 = LVL_2 = 0; >+ } >+ >+ return 0; >+} >+ >+/* >+ * Interrupt service routine >+ * >+ * Attends to BCI interruptions events, >+ * specifically BATSTS (battery connection and removal) >+ * VBATOV (main battery voltage threshold) events >+ * >+ */ >+static irqreturn_t twl4030battery_interrupt(int irq, void *dev_id) >+{ >+ int ret; >+ u8 isr1a_val, isr2a_val, clear_2a, clear_1a; >+ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &isr1a_val, >+ REG_BCIISR1A); >+ if (ret) >+ return IRQ_NONE; >+ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &isr2a_val, >+ REG_BCIISR2A); >+ if (ret) >+ return IRQ_NONE; >+ >+ clear_2a = (isr2a_val & VBATLVL_ISR1)? (VBATLVL_ISR1): 0; >+ clear_1a = (isr1a_val & BATSTS_ISR1)? (BATSTS_ISR1): 0; >+ >+ /* cleaning BCI interrupt status flags */ >+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, >+ clear_1a , REG_BCIISR1A); >+ if (ret) >+ return IRQ_NONE; >+ >+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS, >+ clear_2a , REG_BCIISR2A); >+ if (ret) >+ return IRQ_NONE; >+ >+ /* battery connetion or removal event */ >+ if (isr1a_val & BATSTS_ISR1) >+ twl4030battery_presence_evt(); >+ /* battery voltage threshold event*/ >+ else if (isr2a_val & VBATLVL_ISR1) >+ twl4030battery_level_evt(); >+ else >+ return IRQ_NONE; >+ >+ return IRQ_HANDLED; >+} >+ >+/* >+ * Enable/Disable hardware battery level event notifications. >+ */ >+static int twl4030battery_hw_level_en(int enable) >+{ >+ int ret; >+ >+ if (enable) { >+ /* unmask VBATOV interrupt for INT1 */ >+ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, >VBATLVL_IMR1, >+ 0, REG_BCIIMR2A); >+ if (ret) >+ return ret; >+ >+ /* configuring interrupt edge detection for VBATOv */ >+ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0, >+ VBATLVL_EDRRISIN, REG_BCIEDR3); >+ if (ret) >+ return ret; >+ } else { >+ /* mask VBATOV interrupt for INT1 */ >+ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0, >+ VBATLVL_IMR1, REG_BCIIMR2A); >+ if (ret) >+ return ret; >+ } >+ >+ return 0; >+} >+ >+/* >+ * Enable/disable hardware battery presence event notifications. >+ */ >+static int twl4030battery_hw_presence_en(int enable) >+{ >+ int ret; >+ >+ if (enable) { >+ /* unmask BATSTS interrupt for INT1 */ >+ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, >BATSTS_IMR1, >+ 0, REG_BCIIMR1A); >+ if (ret) >+ return ret; >+ >+ /* configuring interrupt edge for BATSTS */ >+ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0, >+ BATSTS_EDRRISIN | BATSTS_EDRFALLING, >REG_BCIEDR2); >+ if (ret) >+ return ret; >+ } else { >+ /* mask BATSTS interrupt for INT1 */ >+ ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0, >+ BATSTS_IMR1, REG_BCIIMR1A); >+ if (ret) >+ return ret; >+ } >+ >+ return 0; >+} >+ >+/* >+ * Enable/Disable AC Charge funtionality. >+ */ >+static int twl4030charger_ac_en(int enable) >+{ >+ int ret; >+ >+ if (enable) { >+ /* forcing the field BCIAUTOAC (BOOT_BCI[0) to 1 */ >+ ret = clear_n_set(TWL4030_MODULE_PM_MASTER, 0, >+ (CONFIG_DONE | BCIAUTOWEN | BCIAUTOAC), >+ REG_BOOT_BCI); >+ if (ret) >+ return ret; >+ } else { >+ /* forcing the field BCIAUTOAC (BOOT_BCI[0) to 0*/ >+ ret = clear_n_set(TWL4030_MODULE_PM_MASTER, BCIAUTOAC, >+ (CONFIG_DONE | BCIAUTOWEN), >+ REG_BOOT_BCI); >+ if (ret) >+ return ret; >+ } >+ return 0; >+} >+ >+/* >+ * Enable/Disable USB Charge funtionality. >+ */ >+int twl4030charger_usb_en(int enable) >+{ >+ u8 value; >+ int ret; >+ unsigned long timeout; >+ >+ if (enable) { >+ /* Check for USB charger conneted */ >+ ret = twl4030charger_presence(); >+ if (ret < 0) >+ return ret; >+ >+ if (!(ret & USB_PW_CONN)) >+ return -ENXIO; >+ >+ /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */ >+ ret = clear_n_set(TWL4030_MODULE_PM_MASTER, 0, >+ (CONFIG_DONE | BCIAUTOWEN | BCIAUTOUSB), >+ REG_BOOT_BCI); >+ if (ret) >+ return ret; >+ >+ ret = clear_n_set(TWL4030_MODULE_USB, 0, PHY_DPLL_CLK, >+ REG_PHY_CLK_CTRL); >+ if (ret) >+ return ret; >+ >+ value = 0; >+ timeout = jiffies + msecs_to_jiffies(50); >+ >+ while ((!(value & PHY_DPLL_CLK)) && >+ time_before(jiffies, timeout)) { >+ udelay(10); >+ ret = >twl4030_i2c_read_u8(TWL4030_MODULE_USB, &value, >+ REG_PHY_CLK_CTRL_STS); >+ if (ret) >+ return ret; >+ } >+ >+ /* OTG_EN (POWER_CTRL[5]) to 1 */ >+ ret = clear_n_set(TWL4030_MODULE_USB, 0, OTG_EN, >+ REG_POWER_CTRL); >+ if (ret) >+ return ret; >+ >+ mdelay(50); >+ >+ /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */ >+ ret = clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0, >+ USBFASTMCHG, REG_BCIMFSTS4); >+ if (ret) >+ return ret; >+ } else { >+ twl4030charger_presence(); >+ ret = clear_n_set(TWL4030_MODULE_PM_MASTER, BCIAUTOUSB, >+ (CONFIG_DONE | BCIAUTOWEN), REG_BOOT_BCI); >+ if (ret) >+ return ret; >+ } >+ >+ return 0; >+} >+ >+/* >+ * Return battery temperature >+ * Or < 0 on failure. >+ */ >+static int twl4030battery_temperature(void) >+{ >+ u8 val; >+ int temp, curr, volt, res, ret; >+ >+ /* Getting and calculating the thermistor voltage */ >+ ret = read_bci_val(T2_BATTERY_TEMP); >+ if (ret < 0) >+ return ret; >+ >+ volt = (ret * TEMP_STEP_SIZE) / TEMP_PSR_R; >+ >+ /* Getting and calculating the supply current in micro ampers */ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, >+ REG_BCICTL2); >+ if (ret) >+ return 0; >+ >+ curr = ((val & ITHSENS) + 1) * 10; >+ >+ /* Getting and calculating the thermistor resistance in ohms*/ >+ res = volt * 1000 / curr; >+ >+ /*calculating temperature*/ >+ for (temp = 55; temp >= 0; temp--) { >+ int actual = twl4030battery_temp_tbl [temp]; >+ if ((actual - res) >= 0) >+ break; >+ } >+ >+ return temp + 1; >+} >+ >+/* >+ * Return battery voltage >+ * Or < 0 on failure. >+ */ >+static int twl4030battery_voltage(void) >+{ perhaps use twl4030-madc driver? >+ int volt = read_bci_val(T2_BATTERY_VOLT); >+ >+ return (volt * VOLT_STEP_SIZE) / VOLT_PSR_R; >+} >+ >+/* >+ * Return the battery current >+ * Or < 0 on failure. >+ */ >+static int twl4030battery_current(void) >+{ check twl4030-madc. >+ int ret, curr = read_bci_val(T2_BATTERY_CUR); >+ u8 val; >+ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, >+ REG_BCICTL1); >+ if (ret) >+ return ret; >+ >+ if (val & CGAIN) /* slope of 0.44 mV/mA */ >+ return (curr * CURR_STEP_SIZE) / CURR_PSR_R1; >+ else /* slope of 0.88 mV/mA */ >+ return (curr * CURR_STEP_SIZE) / CURR_PSR_R2; >+} >+ >+/* >+ * Return the battery backup voltage >+ * Or < 0 on failure. >+ */ >+static int twl4030backupbatt_voltage(void) >+{ > check twl4030-madc. + int ret, temp; >+ u8 volt; >+ >+ /* trigger MADC convertion */ >+ twl4030madc_sw1_trigger(); >+ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_MADC, &volt, >+ REG_GPCH9 + 1); >+ if (ret) >+ return ret; >+ >+ temp = ((int) volt) << 2; >+ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_MADC, &volt, >+ REG_GPCH9); >+ if (ret) >+ return ret; >+ >+ temp = temp + ((int) ((volt & MADC_LSB_MASK) >> 6)); >+ >+ return (temp * BK_VOLT_STEP_SIZE) / BK_VOLT_PSR_R; >+} >+ >+/* >+ * Returns an integer value, that means, >+ * NO_PW_CONN no power supply is connected >+ * AC_PW_CONN if the AC power supply is connected >+ * USB_PW_CONN if the USB power supply is connected >+ * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are >both connected >+ * >+ * Or < 0 on failure. >+ */ >+static int twl4030charger_presence(void) >+{ >+ int ret; >+ u8 hwsts; >+ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &hwsts, >+ REG_STS_HW_CONDITIONS); >+ if (ret) { >+ pr_err("BATTERY DRIVER: error reading >STS_HW_CONDITIONS \n"); >+ return ret; >+ } >+ >+ ret = (hwsts & STS_CHG)? AC_PW_CONN: NO_PW_CONN; >+ ret += (hwsts & STS_VBUS)? USB_PW_CONN: NO_PW_CONN; >+ >+ if (ret & USB_PW_CONN) >+ usb_charger_flag = 1; >+ else >+ usb_charger_flag = 0; >+ >+ return ret; >+ >+} >+ >+/* >+ * Returns the main charge FSM status >+ * Or < 0 on failure. >+ */ >+static int twl4030bci_status(void) >+{ >+ int ret; >+ u8 status; >+ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, >+ &status, REG_BCIMSTATEC); >+ if (ret) { >+ pr_err("BATTERY DRIVER: error reading BCIMSTATEC \n"); >+ return ret; >+ } >+ >+ return (int) (status & BCIMSTAT_MASK); >+} >+ >+static int read_bci_val(u8 reg) >+{ check twl4030-madc. >+ int ret, temp; >+ u8 val; >+ >+ /* reading MSB */ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, >+ reg + 1); >+ if (ret) >+ return ret; >+ >+ temp = ((int)(val & 0x03)) << 8; >+ >+ /* reading LSB */ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val, >+ reg); >+ if (ret) >+ return ret; >+ >+ return temp | val; >+} >+ >+/* >+ * Triggers the sw1 request for the twl4030 module to measure >the sw1 selected >+ * channels >+ */ >+static int twl4030madc_sw1_trigger(void) >+{ check twl4030-madc. >+ u8 val; >+ int ret; >+ >+ /* Triggering SW1 MADC convertion */ >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_MADC, &val, >+ REG_CTRL_SW1); >+ if (ret) >+ return ret; >+ >+ val |= SW1_TRIGGER; >+ >+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, val, >+ REG_CTRL_SW1); >+ if (ret) >+ return ret; >+ >+ /* Waiting until the SW1 conversion ends*/ >+ val = 0; >+ >+ while (!(val & EOC_SW1)) { >+ ret = twl4030_i2c_read_u8(TWL4030_MODULE_MADC, &val, >+ REG_CTRL_SW1); >+ if (ret) >+ return ret; >+ mdelay(10); >+ } >+ >+ return 0; >+} >+ >+/* >+ * Settup the twl4030 MADC module to measure the backup >+ * battery voltage. >+ */ >+static int twl4030backupbatt_voltage_setup(void) >+{ >+ int ret; >+ >+ /* turning adc_on */ >+ ret = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, MADC_ON, >+ REG_CTRL1); >+ if (ret) >+ return ret; >+ >+ /*setting MDC channel 9 to trigger by SW1*/ >+ ret = clear_n_set(TWL4030_MODULE_MADC, 0, SW1_CH9_SEL, >+ REG_SW1SELECT_MSB); >+ if (ret) >+ return ret; >+ >+ /* Starting backup batery charge */ >+ ret = clear_n_set(TWL4030_MODULE_PM_RECEIVER, 0, BBCHEN, >+ REG_BB_CFG); >+ if (ret) >+ return ret; >+ >+ return 0; >+} >+ >+/* >+ * Settup the twl4030 BCI and MADC module to measure battery >+ * temperature >+ */ >+static int twl4030battery_temp_setup(void) >+{ >+ int ret; >+ >+ /* Enabling thermistor current */ >+ ret = clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0, ITHEN, >+ REG_BCICTL1); >+ if (ret) >+ return ret; >+ >+ return 0; >+} >+ >+/* >+ * Sets and clears bits on an given register on a given module >+ */ >+static inline int clear_n_set(u8 mod_no, u8 clear, u8 set, u8 reg) >+{ >+ int ret; >+ u8 val = 0; >+ >+ /* Gets the initial register value */ >+ ret = twl4030_i2c_read_u8(mod_no, &val, reg); >+ if (ret) >+ return ret; >+ >+ /* Clearing all those bits to clear */ >+ val &= ~(clear); >+ >+ /* Setting all those bits to set */ >+ val |= set; >+ >+ /* Update the register */ >+ ret = twl4030_i2c_write_u8(mod_no, val, reg); >+ if (ret) >+ return ret; >+ >+ return 0; >+} >+ >+static enum power_supply_property twl4030_bci_battery_props[] = { >+ POWER_SUPPLY_PROP_STATUS, >+ POWER_SUPPLY_PROP_ONLINE, >+ POWER_SUPPLY_PROP_VOLTAGE_NOW, >+ POWER_SUPPLY_PROP_CURRENT_NOW, >+ POWER_SUPPLY_PROP_CAPACITY, >+ POWER_SUPPLY_PROP_TEMP, >+}; >+ >+static enum power_supply_property twl4030_bk_bci_battery_props[] = { >+ POWER_SUPPLY_PROP_VOLTAGE_NOW, >+}; >+ >+static void >+twl4030_bk_bci_battery_read_status(struct twl4030_bci_device_info *di) >+{ >+ di->bk_voltage_uV = twl4030backupbatt_voltage(); >+} >+ >+static void twl4030_bk_bci_battery_work(struct work_struct *work) >+{ >+ struct twl4030_bci_device_info *di = container_of(work, >+ struct twl4030_bci_device_info, >+ twl4030_bk_bci_monitor_work.work); >+ >+ twl4030_bk_bci_battery_read_status(di); >+ schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 500); >+} >+ >+static void twl4030_bci_battery_read_status(struct >twl4030_bci_device_info *di) >+{ >+ di->temp_C = twl4030battery_temperature(); >+ di->voltage_uV = twl4030battery_voltage(); >+ di->current_uA = twl4030battery_current(); >+} >+ >+static void >+twl4030_bci_battery_update_status(struct twl4030_bci_device_info *di) >+{ >+ twl4030_bci_battery_read_status(di); >+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; >+ >+ if (power_supply_am_i_supplied(&di->bat)) >+ di->charge_status = POWER_SUPPLY_STATUS_CHARGING; >+ else >+ di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING; >+} >+ >+static void twl4030_bci_battery_work(struct work_struct *work) >+{ >+ struct twl4030_bci_device_info *di = container_of(work, >+ struct twl4030_bci_device_info, >twl4030_bci_monitor_work.work); >+ >+ twl4030_bci_battery_update_status(di); >+ schedule_delayed_work(&di->twl4030_bci_monitor_work, 100); >+} >+ >+ >+#define to_twl4030_bci_device_info(x) container_of((x), \ >+ struct twl4030_bci_device_info, bat); >+ >+static void twl4030_bci_battery_external_power_changed(struct >power_supply *psy) >+{ >+ struct twl4030_bci_device_info *di = >to_twl4030_bci_device_info(psy); >+ >+ cancel_delayed_work(&di->twl4030_bci_monitor_work); >+ schedule_delayed_work(&di->twl4030_bci_monitor_work, 0); >+} >+ >+#define to_twl4030_bk_bci_device_info(x) container_of((x), \ >+ struct twl4030_bci_device_info, bk_bat); >+ >+static int twl4030_bk_bci_battery_get_property(struct >power_supply *psy, >+ enum power_supply_property psp, >+ union power_supply_propval *val) >+{ >+ struct twl4030_bci_device_info *di = >to_twl4030_bk_bci_device_info(psy); >+ >+ switch (psp) { >+ case POWER_SUPPLY_PROP_VOLTAGE_NOW: >+ val->intval = di->bk_voltage_uV; >+ break; >+ default: >+ return -EINVAL; >+ } >+ >+ return 0; >+} >+ >+static int twl4030_bci_battery_get_property(struct power_supply *psy, >+ enum power_supply_property psp, >+ union power_supply_propval *val) >+{ >+ struct twl4030_bci_device_info *di = >to_twl4030_bci_device_info(psy); >+ int status = 0; >+ >+ switch (psp) { >+ case POWER_SUPPLY_PROP_STATUS: >+ val->intval = di->charge_status; >+ return 0; >+ default: >+ break; >+ } >+ >+ switch (psp) { >+ case POWER_SUPPLY_PROP_VOLTAGE_NOW: >+ val->intval = di->voltage_uV; >+ break; >+ case POWER_SUPPLY_PROP_CURRENT_NOW: >+ val->intval = di->current_uA; >+ break; >+ case POWER_SUPPLY_PROP_TEMP: >+ val->intval = di->temp_C; >+ break; >+ case POWER_SUPPLY_PROP_ONLINE: >+ status = twl4030bci_status(); >+ if ((status & AC_STATEC) == AC_STATEC) >+ val->intval = POWER_SUPPLY_TYPE_MAINS; >+ else if (usb_charger_flag) >+ val->intval = POWER_SUPPLY_TYPE_USB; >+ else >+ val->intval = 0; >+ break; >+ case POWER_SUPPLY_PROP_CAPACITY: >+ /* >+ * need to get the correct percentage value per the >+ * battery characteristics. Approx values for now. >+ */ >+ if (di->voltage_uV < 2894 || LVL_1) { >+ val->intval = 5; >+ LVL_1 = 0; >+ } else if ((di->voltage_uV < 3451 && >di->voltage_uV > 2894) >+ || LVL_2) { >+ val->intval = 20; >+ LVL_2 = 0; >+ } else if ((di->voltage_uV < 3902 && >di->voltage_uV > 3451) >+ || LVL_3) { >+ val->intval = 50; >+ LVL_3 = 0; >+ } else if ((di->voltage_uV < 3949 && >di->voltage_uV > 3902) >+ || LVL_4) { >+ val->intval = 75; >+ LVL_4 = 0; >+ } else if (di->voltage_uV > 3949) >+ val->intval = 90; >+ break; >+ default: >+ return -EINVAL; >+ } >+ return 0; >+} >+ >+static char *twl4030_bci_supplied_to[] = { >+ "twl4030_bci_battery", >+}; >+ >+static int twl4030_bci_battery_probe(struct platform_device *dev) >+{ >+ struct twl4030_bci_device_info *di; >+ int ret; >+ >+ di = kzalloc(sizeof(*di), GFP_KERNEL); >+ if (!di) >+ return -ENOMEM; >+ >+ platform_set_drvdata(dev, di); >+ >+ di->dev = &dev->dev; >+ di->bat.name = "twl4030_bci_battery"; >+ di->bat.supplied_to = twl4030_bci_supplied_to; >+ di->bat.num_supplicants = ARRAY_SIZE(twl4030_bci_supplied_to); >+ di->bat.type = POWER_SUPPLY_TYPE_BATTERY; >+ di->bat.properties = twl4030_bci_battery_props; >+ di->bat.num_properties = ARRAY_SIZE(twl4030_bci_battery_props); >+ di->bat.get_property = twl4030_bci_battery_get_property; >+ di->bat.external_power_changed = >+ twl4030_bci_battery_external_power_changed; >+ >+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; >+ >+ di->bk_bat.name = "twl4030_bci_bk_battery"; >+ di->bk_bat.type = POWER_SUPPLY_TYPE_BATTERY; >+ di->bk_bat.properties = twl4030_bk_bci_battery_props; >+ di->bk_bat.num_properties = >ARRAY_SIZE(twl4030_bk_bci_battery_props); >+ di->bk_bat.get_property = twl4030_bk_bci_battery_get_property; >+ di->bk_bat.external_power_changed = NULL; >+ >+ twl4030charger_ac_en(ENABLE); >+ twl4030charger_usb_en(ENABLE); >+ twl4030battery_hw_level_en(ENABLE); >+ twl4030battery_hw_presence_en(ENABLE); >+ >+ /* settings for temperature sensing */ >+ ret = twl4030battery_temp_setup(); >+ if (ret) >+ goto temp_setup_fail; >+ >+ /* enabling GPCH09 for read back battery voltage */ >+ ret = twl4030backupbatt_voltage_setup(); >+ if (ret) >+ goto voltage_setup_fail; >+ >+ /* request BCI interruption */ >+ ret = request_irq(TWL4030_MODIRQ_BCI, twl4030battery_interrupt, >+ IRQF_DISABLED, dev->name, NULL); >+ if (ret) { >+ pr_err("BATTERY DRIVER: (BCI) IRQ%d is not free.\n", >+ TWL4030_MODIRQ_PWR); >+ goto batt_irq_fail; >+ } >+ >+ /* request Power interruption */ >+ ret = request_irq(TWL4030_PWRIRQ_CHG_PRES, >twl4030charger_interrupt, >+ 0, dev->name, di); >+ >+ if (ret) { >+ pr_err("BATTERY DRIVER: (POWER) IRQ%d is not free.\n", >+ TWL4030_MODIRQ_PWR); >+ goto chg_irq_fail; >+ } >+ >+ ret = power_supply_register(&dev->dev, &di->bat); >+ if (ret) { >+ pr_err("BATTERY DRIVER: failed to register main >battery\n"); >+ goto batt_failed; >+ } >+ >+ INIT_DELAYED_WORK_DEFERRABLE(&di->twl4030_bci_monitor_work, >+ twl4030_bci_battery_work); >+ schedule_delayed_work(&di->twl4030_bci_monitor_work, 0); >+ >+ ret = power_supply_register(&dev->dev, &di->bk_bat); >+ if (ret) { >+ pr_err("BATTERY DRIVER: failed to register >backup battery\n"); >+ goto bk_batt_failed; >+ } >+ >+ INIT_DELAYED_WORK_DEFERRABLE(&di->twl4030_bk_bci_monitor_work, >+ twl4030_bk_bci_battery_work); >+ schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 500); >+ >+ return 0; >+ >+bk_batt_failed: >+ power_supply_unregister(&di->bat); >+batt_failed: >+ free_irq(TWL4030_MODIRQ_PWR, di); >+chg_irq_fail: >+prev_setup_err: >+ free_irq(TWL4030_MODIRQ_BCI, NULL); >+batt_irq_fail: >+voltage_setup_fail: >+temp_setup_fail: >+ twl4030charger_ac_en(DISABLE); >+ twl4030charger_usb_en(DISABLE); >+ twl4030battery_hw_level_en(DISABLE); >+ twl4030battery_hw_presence_en(DISABLE); >+ kfree(di); >+ >+ return ret; >+} >+ >+static int twl4030_bci_battery_remove(struct platform_device *dev) >+{ >+ struct twl4030_bci_device_info *di = platform_get_drvdata(dev); >+ >+ twl4030charger_ac_en(DISABLE); >+ twl4030charger_usb_en(DISABLE); >+ twl4030battery_hw_level_en(DISABLE); >+ twl4030battery_hw_presence_en(DISABLE); >+ >+ free_irq(TWL4030_MODIRQ_BCI, NULL); >+ free_irq(TWL4030_MODIRQ_PWR, di); >+ >+ flush_scheduled_work(); >+ power_supply_unregister(&di->bat); >+ power_supply_unregister(&di->bk_bat); >+ platform_set_drvdata(dev, NULL); >+ kfree(di); >+ >+ return 0; >+} >+ >+#ifdef CONFIG_PM >+static int twl4030_bci_battery_suspend(struct platform_device *dev, >+ pm_message_t state) >+{ >+ struct twl4030_bci_device_info *di = platform_get_drvdata(dev); >+ >+ di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN; >+ cancel_delayed_work(&di->twl4030_bci_monitor_work); >+ cancel_delayed_work(&di->twl4030_bk_bci_monitor_work); >+ return 0; >+} >+ >+static int twl4030_bci_battery_resume(struct platform_device *dev) >+{ >+ struct twl4030_bci_device_info *di = platform_get_drvdata(dev); >+ >+ schedule_delayed_work(&di->twl4030_bci_monitor_work, 0); >+ schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 50); >+ return 0; >+} >+#endif /* CONFIG_PM */ >+ >+/* >+ * Battery driver module initializer function >+ * registers battery driver structure >+ */ >+static int __init twl4030_battery_init(void) >+{ >+ return platform_driver_register(&twl4030_bci_battery_driver); >+ >+} >+ >+/* >+ * Battery driver module exit function >+ * unregister battery driver structure >+ */ >+static void __exit twl4030_battery_exit(void) >+{ >+ platform_driver_unregister(&twl4030_bci_battery_driver); >+} >+ >+module_init(twl4030_battery_init); >+module_exit(twl4030_battery_exit); >+MODULE_LICENSE("GPL"); >+MODULE_ALIAS("twl4030_bci_battery"); >+MODULE_AUTHOR("Texas Instruments Inc"); > > >-- >To unsubscribe from this list: send the line "unsubscribe >linux-omap" in >the body of a message to majordomo@xxxxxxxxxxxxxxx >More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe linux-omap" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html