[PATCH 71/133] [Juanty SRU] ARM.imx51 Freescale:ENGR00101496 MC13892 battery driver

Brad Figg brad.figg at canonical.com
Thu Jul 9 16:49:01 UTC 2009


From: Zhou Jingyu <b02241 at freescale.com>

Add MC13892 battery driver. Apply it for MC13892 V2.0 only due to
ENGR108085

Signed-off-by: Zhou Jingyu <Jingyu.Zhou at freescale.com>
Signed-off-by: Brad Figg <brad.figg at canonical.com>
---
 arch/arm/configs/imx51_defconfig        |    7 +-
 drivers/mxc/pmic/mc13892/Makefile       |    2 +-
 drivers/mxc/pmic/mc13892/pmic_battery.c |  623 +++++++++++++++++++++++++++++++
 3 files changed, 629 insertions(+), 3 deletions(-)
 create mode 100644 drivers/mxc/pmic/mc13892/pmic_battery.c

diff --git a/arch/arm/configs/imx51_defconfig b/arch/arm/configs/imx51_defconfig
index d88f280..3d53fc5 100644
--- a/arch/arm/configs/imx51_defconfig
+++ b/arch/arm/configs/imx51_defconfig
@@ -1,7 +1,7 @@
 #
 # Automatically generated make config: don't edit
 # Linux kernel version: 2.6.28
-# Wed Apr 22 09:42:44 2009
+# Wed Apr 29 10:11:33 2009
 #
 CONFIG_ARM=y
 CONFIG_SYS_SUPPORTS_APM_EMULATION=y
@@ -846,7 +846,7 @@ CONFIG_W1_MASTER_MXC=m
 CONFIG_W1_SLAVE_DS2438=m
 # CONFIG_W1_SLAVE_DS2760 is not set
 # CONFIG_W1_SLAVE_BQ27000 is not set
-CONFIG_POWER_SUPPLY=m
+CONFIG_POWER_SUPPLY=y
 # CONFIG_POWER_SUPPLY_DEBUG is not set
 # CONFIG_PDA_POWER is not set
 # CONFIG_BATTERY_DS2760 is not set
@@ -1224,6 +1224,8 @@ CONFIG_SND_MXC_SOC=y
 CONFIG_SND_MXC_SOC_SSI=y
 CONFIG_SND_MXC_SOC_IRAM=y
 CONFIG_SND_SOC_IMX_3STACK_SGTL5000=y
+# CONFIG_SND_SOC_IMX_3STACK_AK4647 is not set
+# CONFIG_SND_SOC_IMX_3STACK_WM8580 is not set
 # CONFIG_SND_SOC_ALL_CODECS is not set
 CONFIG_SND_SOC_SGTL5000=y
 # CONFIG_SOUND_PRIME is not set
@@ -1515,6 +1517,7 @@ CONFIG_RTC_INTF_DEV_UIE_EMUL=y
 #
 # CONFIG_RTC_MXC is not set
 # CONFIG_RTC_DRV_MXC_V2 is not set
+# CONFIG_RTC_DRV_IMXDI is not set
 CONFIG_RTC_MC13892=y
 # CONFIG_DMADEVICES is not set
 CONFIG_REGULATOR=y
diff --git a/drivers/mxc/pmic/mc13892/Makefile b/drivers/mxc/pmic/mc13892/Makefile
index 5432d0b..0ed2b7e 100644
--- a/drivers/mxc/pmic/mc13892/Makefile
+++ b/drivers/mxc/pmic/mc13892/Makefile
@@ -5,6 +5,6 @@
 obj-$(CONFIG_MXC_MC13892_ADC) += pmic_adc.o
 #obj-$(CONFIG_MXC_MC13892_RTC) += pmic_rtc.o
 obj-$(CONFIG_MXC_MC13892_LIGHT) += pmic_light.o
-#obj-$(CONFIG_MXC_MC13892_BATTERY) += pmic_battery.o
+obj-$(CONFIG_MXC_MC13892_BATTERY) += pmic_battery.o
 #obj-$(CONFIG_MXC_MC13892_CONNECTIVITY) += pmic_convity.o
 #obj-$(CONFIG_MXC_MC13892_POWER) += pmic_power.o
diff --git a/drivers/mxc/pmic/mc13892/pmic_battery.c b/drivers/mxc/pmic/mc13892/pmic_battery.c
new file mode 100644
index 0000000..8bd2553
--- /dev/null
+++ b/drivers/mxc/pmic/mc13892/pmic_battery.c
@@ -0,0 +1,623 @@
+/*
+ * Copyright 2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*
+ * Includes
+ */
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+
+#include <linux/delay.h>
+#include <linux/pmic_battery.h>
+#include <linux/pmic_adc.h>
+#include <linux/pmic_status.h>
+
+#define BIT_CHG_VOL_LSH		0
+#define BIT_CHG_VOL_WID		3
+
+#define BIT_CHG_CURR_LSH		3
+#define BIT_CHG_CURR_WID		4
+
+#define BIT_CHG_PLIM_LSH		15
+#define BIT_CHG_PLIM_WID		2
+
+#define BIT_CHG_DETS_LSH 6
+#define BIT_CHG_DETS_WID 1
+#define BIT_CHG_CURRS_LSH 11
+#define BIT_CHG_CURRS_WID 1
+
+#define TRICKLE_CHG_EN_LSH	7
+#define	LOW_POWER_BOOT_ACK_LSH	8
+#define BAT_TH_CHECK_DIS_LSH	9
+#define	BATTFET_CTL_EN_LSH	10
+#define BATTFET_CTL_LSH		11
+#define	REV_MOD_EN_LSH		13
+#define PLIM_DIS_LSH		17
+#define	CHG_LED_EN_LSH		18
+#define RESTART_CHG_STAT_LSH	20
+#define	AUTO_CHG_DIS_LSH	21
+#define CYCLING_DIS_LSH		22
+#define	VI_PROGRAM_EN_LSH	23
+
+#define TRICKLE_CHG_EN_WID	1
+#define	LOW_POWER_BOOT_ACK_WID	1
+#define BAT_TH_CHECK_DIS_WID	1
+#define	BATTFET_CTL_EN_WID	1
+#define BATTFET_CTL_WID		1
+#define	REV_MOD_EN_WID		1
+#define PLIM_DIS_WID		1
+#define	CHG_LED_EN_WID		1
+#define RESTART_CHG_STAT_WID	1
+#define	AUTO_CHG_DIS_WID	1
+#define CYCLING_DIS_WID		1
+#define	VI_PROGRAM_EN_WID	1
+
+#define ACC_STARTCC_LSH		0
+#define ACC_STARTCC_WID		1
+#define ACC_RSTCC_LSH		1
+#define ACC_RSTCC_WID		1
+#define ACC_CCFAULT_LSH		7
+#define ACC_CCFAULT_WID		7
+#define ACC_CCOUT_LSH		8
+#define ACC_CCOUT_WID		16
+#define ACC1_ONEC_LSH		0
+#define ACC1_ONEC_WID		15
+
+#define ACC_CALIBRATION 0x17
+#define ACC_START_COUNTER 0x07
+#define ACC_STOP_COUNTER 0x2
+#define ACC_CONTROL_BIT_MASK 0x1f
+#define ACC_ONEC_VALUE 2621
+#define ACC_COULOMB_PER_LSB 1
+#define ACC_CALIBRATION_DURATION_MSECS 20
+
+#define BAT_VOLTAGE_UNIT_UV 4692
+#define BAT_CURRENT_UNIT_UA 5870
+#define CHG_VOLTAGE_UINT_UV 23474
+#define CHG_MIN_CURRENT_UA 3500
+
+#define COULOMB_TO_UAH(c) (10000 * c / 36)
+
+enum chg_setting {
+       TRICKLE_CHG_EN,
+       LOW_POWER_BOOT_ACK,
+       BAT_TH_CHECK_DIS,
+       BATTFET_CTL_EN,
+       BATTFET_CTL,
+       REV_MOD_EN,
+       PLIM_DIS,
+       CHG_LED_EN,
+       RESTART_CHG_STAT,
+       AUTO_CHG_DIS,
+       CYCLING_DIS,
+       VI_PROGRAM_EN
+};
+
+static int pmic_set_chg_current(unsigned short curr)
+{
+	unsigned int mask;
+	unsigned int value;
+
+	value = BITFVAL(BIT_CHG_CURR, curr);
+	mask = BITFMASK(BIT_CHG_CURR);
+	CHECK_ERROR(pmic_write_reg(REG_CHARGE, value, mask));
+
+	return 0;
+}
+
+static int pmic_set_chg_misc(enum chg_setting type, unsigned short flag)
+{
+
+	unsigned int reg_value = 0;
+	unsigned int mask = 0;
+
+	switch (type) {
+	case TRICKLE_CHG_EN:
+		reg_value = BITFVAL(TRICKLE_CHG_EN, flag);
+		mask = BITFMASK(TRICKLE_CHG_EN);
+		break;
+	case LOW_POWER_BOOT_ACK:
+		reg_value = BITFVAL(LOW_POWER_BOOT_ACK, flag);
+		mask = BITFMASK(LOW_POWER_BOOT_ACK);
+		break;
+	case BAT_TH_CHECK_DIS:
+		reg_value = BITFVAL(BAT_TH_CHECK_DIS, flag);
+		mask = BITFMASK(BAT_TH_CHECK_DIS);
+		break;
+	case BATTFET_CTL_EN:
+		reg_value = BITFVAL(BATTFET_CTL_EN, flag);
+		mask = BITFMASK(BATTFET_CTL_EN);
+		break;
+	case BATTFET_CTL:
+		reg_value = BITFVAL(BATTFET_CTL, flag);
+		mask = BITFMASK(BATTFET_CTL);
+		break;
+	case REV_MOD_EN:
+		reg_value = BITFVAL(REV_MOD_EN, flag);
+		mask = BITFMASK(REV_MOD_EN);
+		break;
+	case PLIM_DIS:
+		reg_value = BITFVAL(PLIM_DIS, flag);
+		mask = BITFMASK(PLIM_DIS);
+		break;
+	case CHG_LED_EN:
+		reg_value = BITFVAL(CHG_LED_EN, flag);
+		mask = BITFMASK(CHG_LED_EN);
+		break;
+	case RESTART_CHG_STAT:
+		reg_value = BITFVAL(RESTART_CHG_STAT, flag);
+		mask = BITFMASK(RESTART_CHG_STAT);
+		break;
+	case AUTO_CHG_DIS:
+		reg_value = BITFVAL(AUTO_CHG_DIS, flag);
+		mask = BITFMASK(AUTO_CHG_DIS);
+		break;
+	case CYCLING_DIS:
+		reg_value = BITFVAL(CYCLING_DIS, flag);
+		mask = BITFMASK(CYCLING_DIS);
+		break;
+	case VI_PROGRAM_EN:
+		reg_value = BITFVAL(VI_PROGRAM_EN, flag);
+		mask = BITFMASK(VI_PROGRAM_EN);
+		break;
+	default:
+		return PMIC_PARAMETER_ERROR;
+	}
+
+	CHECK_ERROR(pmic_write_reg(REG_CHARGE, reg_value, mask));
+
+	return 0;
+}
+
+static int pmic_get_batt_voltage(unsigned short *voltage)
+{
+	t_channel channel;
+	unsigned short result[8];
+
+	channel = BATTERY_VOLTAGE;
+	CHECK_ERROR(pmic_adc_convert(channel, result));
+	*voltage = result[0];
+
+	return 0;
+}
+
+static int pmic_get_batt_current(unsigned short *curr)
+{
+	t_channel channel;
+	unsigned short result[8];
+
+	channel = BATTERY_CURRENT;
+	CHECK_ERROR(pmic_adc_convert(channel, result));
+	*curr = result[0];
+
+	return 0;
+}
+
+static int coulomb_counter_calibration;
+static unsigned int coulomb_counter_start_time_msecs;
+
+static int pmic_start_coulomb_counter(void)
+{
+	/* set scaler */
+	CHECK_ERROR(pmic_write_reg(REG_ACC1,
+		ACC_COULOMB_PER_LSB * ACC_ONEC_VALUE, BITFMASK(ACC1_ONEC)));
+
+	CHECK_ERROR(pmic_write_reg(
+		REG_ACC0, ACC_START_COUNTER, ACC_CONTROL_BIT_MASK));
+	coulomb_counter_start_time_msecs = jiffies_to_msecs(jiffies);
+	pr_debug("coulomb counter start time %u\n",
+		coulomb_counter_start_time_msecs);
+	return 0;
+}
+
+static int pmic_stop_coulomb_counter(void)
+{
+	CHECK_ERROR(pmic_write_reg(
+		REG_ACC0, ACC_STOP_COUNTER, ACC_CONTROL_BIT_MASK));
+	return 0;
+}
+
+static int pmic_calibrate_coulomb_counter(void)
+{
+	int ret;
+	unsigned int value;
+
+	/* set scaler */
+	CHECK_ERROR(pmic_write_reg(REG_ACC1,
+		0x1, BITFMASK(ACC1_ONEC)));
+
+	CHECK_ERROR(pmic_write_reg(
+		REG_ACC0, ACC_CALIBRATION, ACC_CONTROL_BIT_MASK));
+	msleep(ACC_CALIBRATION_DURATION_MSECS);
+
+	ret = pmic_read_reg(REG_ACC0, &value, BITFMASK(ACC_CCOUT));
+	if (ret != 0)
+		return -1;
+	value = BITFEXT(value, ACC_CCOUT);
+	pr_debug("calibrate value = %x\n", value);
+	coulomb_counter_calibration = (int)((s16)((u16) value));
+	pr_debug("coulomb_counter_calibration = %d\n",
+		coulomb_counter_calibration);
+
+	return 0;
+
+}
+
+static int pmic_get_charger_coulomb(int *coulomb)
+{
+	int ret;
+	unsigned int value;
+	int calibration;
+	unsigned int time_diff_msec;
+
+	ret = pmic_read_reg(REG_ACC0, &value, BITFMASK(ACC_CCOUT));
+	if (ret != 0)
+		return -1;
+	value = BITFEXT(value, ACC_CCOUT);
+	pr_debug("counter value = %x\n", value);
+	*coulomb = ((s16)((u16)value)) * ACC_COULOMB_PER_LSB;
+
+	if (abs(*coulomb) >= ACC_COULOMB_PER_LSB) {
+			/* calibrate */
+		time_diff_msec = jiffies_to_msecs(jiffies);
+		time_diff_msec =
+			(time_diff_msec > coulomb_counter_start_time_msecs) ?
+			(time_diff_msec - coulomb_counter_start_time_msecs) :
+			(0xffffffff - coulomb_counter_start_time_msecs
+			+ time_diff_msec);
+		calibration = coulomb_counter_calibration * (int)time_diff_msec
+			/ (ACC_ONEC_VALUE * ACC_CALIBRATION_DURATION_MSECS);
+		*coulomb -= calibration;
+	}
+
+	return 0;
+}
+
+static int pmic_restart_charging(void)
+{
+	pmic_set_chg_misc(BAT_TH_CHECK_DIS, 1);
+	pmic_set_chg_misc(AUTO_CHG_DIS, 0);
+	pmic_set_chg_misc(VI_PROGRAM_EN, 1);
+	pmic_set_chg_current(0x8);
+	pmic_set_chg_misc(RESTART_CHG_STAT, 1);
+	return 0;
+}
+
+struct mc13892_dev_info {
+	struct device *dev;
+
+	unsigned short voltage_raw;
+	int voltage_uV;
+	unsigned short current_raw;
+	int current_uA;
+	int battery_status;
+	int full_counter;
+	int charger_online;
+	int charger_voltage_uV;
+	int accum_current_uAh;
+
+	struct power_supply bat;
+	struct power_supply charger;
+
+	struct workqueue_struct *monitor_wqueue;
+	struct delayed_work monitor_work;
+};
+
+#define mc13892_SENSER	25
+#define to_mc13892_dev_info(x) container_of((x), struct mc13892_dev_info, \
+					      bat);
+
+static enum power_supply_property mc13892_battery_props[] = {
+	POWER_SUPPLY_PROP_VOLTAGE_NOW,
+	POWER_SUPPLY_PROP_CURRENT_NOW,
+	POWER_SUPPLY_PROP_CHARGE_NOW,
+	POWER_SUPPLY_PROP_STATUS,
+};
+
+static enum power_supply_property mc13892_charger_props[] = {
+	POWER_SUPPLY_PROP_ONLINE,
+};
+
+static int mc13892_charger_update_status(struct mc13892_dev_info *di)
+{
+	int ret;
+	unsigned int value;
+	int online;
+
+	ret = pmic_read_reg(REG_INT_SENSE0, &value, BITFMASK(BIT_CHG_DETS));
+
+	if (ret == 0) {
+		online = BITFEXT(value, BIT_CHG_DETS);
+		if (online != di->charger_online) {
+			di->charger_online = online;
+			dev_info(di->charger.dev, "charger status: %s\n",
+				online ? "online" : "offline");
+			power_supply_changed(&di->charger);
+
+			cancel_delayed_work(&di->monitor_work);
+			queue_delayed_work(di->monitor_wqueue,
+				&di->monitor_work, HZ / 10);
+			if (online) {
+				pmic_start_coulomb_counter();
+				pmic_restart_charging();
+			} else
+				pmic_stop_coulomb_counter();
+		}
+	}
+
+	return ret;
+}
+
+static int mc13892_charger_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	struct mc13892_dev_info *di =
+		container_of((psy), struct mc13892_dev_info, charger);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = di->charger_online;
+		return 0;
+	default:
+		break;
+	}
+	return -EINVAL;
+}
+
+static int mc13892_battery_read_status(struct mc13892_dev_info *di)
+{
+	int retval;
+	int coulomb;
+	retval = pmic_get_batt_voltage(&(di->voltage_raw));
+	if (retval == 0)
+		di->voltage_uV = di->voltage_raw * BAT_VOLTAGE_UNIT_UV;
+
+	retval = pmic_get_batt_current(&(di->current_raw));
+	if (retval == 0) {
+		if (di->current_raw & 0x200)
+			di->current_uA =
+				(0x1FF - (di->current_raw & 0x1FF)) *
+				BAT_CURRENT_UNIT_UA * (-1);
+		else
+			di->current_uA =
+				(di->current_raw & 0x1FF) * BAT_CURRENT_UNIT_UA;
+	}
+	retval = pmic_get_charger_coulomb(&coulomb);
+	if (retval == 0)
+		di->accum_current_uAh = COULOMB_TO_UAH(coulomb);
+
+	return retval;
+}
+
+static void mc13892_battery_update_status(struct mc13892_dev_info *di)
+{
+	unsigned int value;
+	int retval;
+	int old_battery_status = di->battery_status;
+
+	if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN)
+		di->full_counter = 0;
+
+	if (di->charger_online) {
+		retval = pmic_read_reg(REG_INT_SENSE0,
+					&value, BITFMASK(BIT_CHG_CURRS));
+
+		if (retval == 0) {
+			value = BITFEXT(value, BIT_CHG_CURRS);
+			if (value)
+				di->battery_status =
+					POWER_SUPPLY_STATUS_CHARGING;
+			else
+				di->battery_status =
+					POWER_SUPPLY_STATUS_NOT_CHARGING;
+		}
+
+		if (di->battery_status == POWER_SUPPLY_STATUS_NOT_CHARGING)
+			di->full_counter++;
+		else
+			di->full_counter = 0;
+	} else {
+		di->battery_status = POWER_SUPPLY_STATUS_DISCHARGING;
+		di->full_counter = 0;
+	}
+
+	dev_dbg(di->bat.dev, "bat status: %d\n",
+		di->battery_status);
+
+	if (di->battery_status != old_battery_status)
+		power_supply_changed(&di->bat);
+}
+
+static void mc13892_battery_work(struct work_struct *work)
+{
+	struct mc13892_dev_info *di = container_of(work,
+						     struct mc13892_dev_info,
+						     monitor_work.work);
+	const int interval = HZ * 60;
+
+	dev_dbg(di->dev, "%s\n", __func__);
+
+	mc13892_battery_update_status(di);
+	queue_delayed_work(di->monitor_wqueue, &di->monitor_work, interval);
+}
+
+static void charger_online_event_callback(void *para)
+{
+	struct mc13892_dev_info *di = (struct mc13892_dev_info *) para;
+	pr_info("\n\n DETECTED charger plug/unplug event\n");
+	mc13892_charger_update_status(di);
+}
+
+
+static int mc13892_battery_get_property(struct power_supply *psy,
+				       enum power_supply_property psp,
+				       union power_supply_propval *val)
+{
+	struct mc13892_dev_info *di = to_mc13892_dev_info(psy);
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		if (di->battery_status == POWER_SUPPLY_STATUS_UNKNOWN) {
+
+			mc13892_battery_update_status(di);
+			mc13892_charger_update_status(di);
+		}
+		val->intval = di->battery_status;
+		return 0;
+	default:
+		break;
+	}
+
+	mc13892_battery_read_status(di);
+
+	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_CHARGE_NOW:
+		val->intval = di->accum_current_uAh;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int pmic_battery_remove(struct platform_device *pdev)
+{
+	pmic_event_callback_t bat_event_callback;
+	struct mc13892_dev_info *di = platform_get_drvdata(pdev);
+
+	bat_event_callback.func = charger_online_event_callback;
+	bat_event_callback.param = (void *) di;
+	pmic_event_unsubscribe(EVENT_CHGDETI, bat_event_callback);
+
+	cancel_rearming_delayed_workqueue(di->monitor_wqueue,
+					  &di->monitor_work);
+	destroy_workqueue(di->monitor_wqueue);
+	power_supply_unregister(&di->bat);
+	power_supply_unregister(&di->charger);
+
+	kfree(di);
+
+	return 0;
+}
+
+static int pmic_battery_probe(struct platform_device *pdev)
+{
+	int retval = 0;
+	struct mc13892_dev_info *di;
+	pmic_event_callback_t bat_event_callback;
+	pmic_version_t pmic_version;
+
+	/* Only apply battery driver for MC13892 V2.0 due to ENGR108085 */
+	pmic_version = pmic_get_version();
+	if (pmic_version.revision < 20) {
+		pr_debug("Battery driver is only applied for MC13892 V2.0\n");
+		return -1;
+	}
+
+	di = kzalloc(sizeof(*di), GFP_KERNEL);
+	if (!di) {
+		retval = -ENOMEM;
+		goto di_alloc_failed;
+	}
+
+	platform_set_drvdata(pdev, di);
+
+	di->dev	= &pdev->dev;
+	di->bat.name	= "mc13892_bat";
+	di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+	di->bat.properties = mc13892_battery_props;
+	di->bat.num_properties = ARRAY_SIZE(mc13892_battery_props);
+	di->bat.get_property = mc13892_battery_get_property;
+
+	di->battery_status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+	retval = power_supply_register(&pdev->dev, &di->bat);
+	if (retval) {
+		dev_err(di->dev, "failed to register battery\n");
+		goto batt_failed;
+	}
+	di->charger.name	= "mc13892_charger";
+	di->charger.type = POWER_SUPPLY_TYPE_MAINS;
+	di->charger.properties = mc13892_charger_props;
+	di->charger.num_properties = ARRAY_SIZE(mc13892_charger_props);
+	di->charger.get_property = mc13892_charger_get_property;
+	retval = power_supply_register(&pdev->dev, &di->charger);
+	if (retval) {
+		dev_err(di->dev, "failed to register charger\n");
+		goto charger_failed;
+	}
+	INIT_DELAYED_WORK(&di->monitor_work, mc13892_battery_work);
+	di->monitor_wqueue = create_singlethread_workqueue(pdev->dev.bus_id);
+	if (!di->monitor_wqueue) {
+		retval = -ESRCH;
+		goto workqueue_failed;
+	}
+	queue_delayed_work(di->monitor_wqueue, &di->monitor_work, HZ * 10);
+
+	bat_event_callback.func = charger_online_event_callback;
+	bat_event_callback.param = (void *) di;
+	pmic_event_subscribe(EVENT_CHGDETI, bat_event_callback);
+
+	pmic_stop_coulomb_counter();
+	pmic_calibrate_coulomb_counter();
+	goto success;
+
+workqueue_failed:
+	power_supply_unregister(&di->charger);
+charger_failed:
+	power_supply_unregister(&di->bat);
+batt_failed:
+	kfree(di);
+di_alloc_failed:
+success:
+	dev_dbg(di->dev, "%s battery probed!\n", __func__);
+	return retval;
+
+
+	return 0;
+}
+
+static struct platform_driver pmic_battery_driver_ldm = {
+	.driver = {
+		   .name = "pmic_battery",
+		   .bus = &platform_bus_type,
+		   },
+	.probe = pmic_battery_probe,
+	.remove = pmic_battery_remove,
+};
+
+static int __init pmic_battery_init(void)
+{
+	pr_debug("PMIC Battery driver loading...\n");
+	return platform_driver_register(&pmic_battery_driver_ldm);
+}
+
+static void __exit pmic_battery_exit(void)
+{
+	platform_driver_unregister(&pmic_battery_driver_ldm);
+	pr_debug("PMIC Battery driver successfully unloaded\n");
+}
+
+module_init(pmic_battery_init);
+module_exit(pmic_battery_exit);
+
+MODULE_DESCRIPTION("pmic_battery driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
-- 
1.6.0.4





More information about the kernel-team mailing list