All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC/PATCH 1/2] Triton Battery charger interface driver for OMAP3430
@ 2008-06-20 12:03 x0070977
  2008-06-23  8:49 ` Felipe Balbi
  0 siblings, 1 reply; 6+ messages in thread
From: x0070977 @ 2008-06-20 12:03 UTC (permalink / raw)
  To: linux-omap

From: Madhusudhan Chikkature <madhu.cr@ti.com>

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@ti.com>
---
 arch/arm/mach-omap2/devices.c       |   17
 drivers/power/Kconfig               |    8
 drivers/power/Makefile              |    1
 drivers/power/twl4030_bci_battery.c | 1144 ++++++++++++++++++++++++++++++++++++
 4 files changed, 1170 insertions(+)

Index: linux-omap-2.6/arch/arm/mach-omap2/devices.c
===================================================================
--- linux-omap-2.6.orig/arch/arm/mach-omap2/devices.c	2008-06-20
15:39:56.000000000 +0530
+++ linux-omap-2.6/arch/arm/mach-omap2/devices.c	2008-06-20 15:42:05.000000000
+0530
@@ -358,6 +358,22 @@
 static inline void omap_hdq_init(void) {}
 #endif

+#ifdef CONFIG_TWL4030_BCI_BATTERY
+static struct platform_device omap_bci_battery_device = {
+	.name           = "twl4030-bci-battery",
+	.id             = 1,
+	.num_resources  = 0,
+	.resource       = NULL,
+};
+
+static inline void omap_bci_battery_init(void)
+{
+	(void) platform_device_register(&omap_bci_battery_device);
+}
+#else
+static inline void omap_bci_battery_init(void) {}
+#endif
+
 /*-------------------------------------------------------------------------*/

 static int __init omap2_init_devices(void)
@@ -369,6 +385,7 @@
 	omap_init_mbox();
 	omap_init_mcspi();
 	omap_hdq_init();
+	omap_bci_battery_init();
 	omap_init_sti();
 	omap_init_sha1_md5();

Index: linux-omap-2.6/drivers/power/Kconfig
===================================================================
--- linux-omap-2.6.orig/drivers/power/Kconfig	2008-06-20 15:39:56.000000000 +0530
+++ linux-omap-2.6/drivers/power/Kconfig	2008-06-20 15:42:05.000000000 +0530
@@ -70,4 +70,12 @@
 	help
 	  Say Y here to enable support for batteries with BQ27200(I2C) chip.

+config TWL4030_BCI_BATTERY
+	bool "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-20 15:39:56.000000000 +0530
+++ linux-omap-2.6/drivers/power/Makefile	2008-06-20 15:42:05.000000000 +0530
@@ -21,3 +21,4 @@
 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-06-20
15:42:05.000000000 +0530
@@ -0,0 +1,1144 @@
+/*
+ * 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
+};
+
+/*
+ * 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 =
+			(struct twl4030_bci_device_info *)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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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)
+{
+	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();
+	return;
+}
+
+static void twl4030_bk_bci_battery_work(struct delayed_work *work)
+{
+	struct twl4030_bci_device_info *di = container_of(work,
+		struct twl4030_bci_device_info, twl4030_bk_bci_monitor_work);
+
+	twl4030_bk_bci_battery_read_status(di);
+	schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 500);
+	return;
+}
+
+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();
+
+	return;
+}
+
+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;
+
+	return;
+}
+
+static void twl4030_bci_battery_work(struct delayed_work *work)
+{
+	struct twl4030_bci_device_info *di = container_of(work,
+		struct twl4030_bci_device_info, twl4030_bci_monitor_work);
+
+	twl4030_bci_battery_update_status(di);
+	schedule_delayed_work(&di->twl4030_bci_monitor_work, 100);
+	return;
+}
+
+
+#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(&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(&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");



^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2008-06-24  8:09 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-06-20 12:03 [RFC/PATCH 1/2] Triton Battery charger interface driver for OMAP3430 x0070977
2008-06-23  8:49 ` Felipe Balbi
2008-06-23 13:34   ` Madhusudhan Chikkature
2008-06-23 14:53     ` Felipe Balbi
2008-06-24  7:50       ` Tony Lindgren
2008-06-24  8:07         ` [RFC/PATCH 1/2] Triton Battery charger interface driver forOMAP3430 Madhusudhan Chikkature

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.