blob: 5a2efb46e0d9a0b0f8090f817a811fe229f45396 [file] [log] [blame]
/*
* linux/drivers/power/bcm59055_batter.c - Broadcom BCM59055 Battery driver
*
* Copyright (C) 2010 Broadcom, All Rights Reserved.
*
* This program 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.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/power_supply.h>
#include <linux/types.h>
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <asm/io.h>
#include <linux/slab.h>
#include <linux/mfd/bcm590xx/core.h>
#include <linux/broadcom/bcm59055-power.h>
#include <linux/broadcom/bcm59055-fuelgauge.h>
#include <linux/broadcom/bcm59055-adc.h>
#include <linux/workqueue.h>
#include <linux/mfd/bcm590xx/bcm590xx-usb.h>
#define USB_OFFLINE 4
const char *usb_model_name[] = {
[USB_CHARGER_UNKNOWN] = "usb-unknown",
[USB_CHARGER_SDP] = "usb-sdp",
[USB_CHARGER_CDP] = "usb-cdp",
[USB_CHARGER_DCP] = "usb-dcp",
[USB_OFFLINE] = "usb-offline"
};
#define BYTE_COMBINE(msb,lsb) (msb<<8 | lsb)
#define MV_FROM_REG(regval) DIV_ROUND_CLOSEST(((regval) * 4800 ) , 1024)
#define PRE_ENUM_CURRENT CURRENT_100_MA
struct bcm59055_power {
struct bcm590xx *bcm590xx;
struct power_supply battery;
struct power_supply wall;
struct power_supply usb;
int usb_cc;
int wac_cc;
enum power_supply_type power_src;
int usb_type;
struct delayed_work charger_insert_wq;
struct delayed_work batt_lvl_wq;
unsigned short batt_min_volt;
unsigned short batt_max_volt;
unsigned short batt_technology;
u8 *batt_vol;
u8 *batt_adc;
unsigned int batt_max_cap;
// Display properties.
unsigned int batt_percentage;
unsigned int batt_prev_cap;
unsigned int batt_status;
unsigned int batt_voltage_now;
struct notifier_block nb;
};
static struct bcm59055_power *pvt_data;
static enum power_supply_property bcm59055_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_TECHNOLOGY,
POWER_SUPPLY_PROP_CAPACITY,
POWER_SUPPLY_PROP_VOLTAGE_MAX,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_VOLTAGE_MIN,
};
static enum power_supply_property bcm59055_wall_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CURRENT_NOW,
};
static enum power_supply_property bcm59055_usb_props[] = {
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_CURRENT_NOW,
POWER_SUPPLY_PROP_MODEL_NAME
};
static enum power_supply_type bcm59055_get_power_supply_type(struct
bcm59055_power
*bcm59055_power)
{
int regVal;
struct bcm590xx *bcm59055 = bcm59055_power->bcm590xx;
pr_debug("Inside %s\n", __func__);
regVal = bcm590xx_reg_read(bcm59055, BCM59055_REG_ENV2);
if (P_CGPD_ENV & regVal)
return POWER_SUPPLY_TYPE_MAINS;
else if (P_UBPD_ENV & regVal)
return POWER_SUPPLY_TYPE_USB;
return POWER_SUPPLY_TYPE_BATTERY;
}
static int bcm59055_usb_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
int ret = 0;
struct bcm59055_power *bcm59055_power =
dev_get_drvdata(psy->dev->parent);
if (unlikely(!bcm59055_power)) {
pr_info("%s: invalid driver data !!!\n",
__func__);
return -EINVAL;
}
pr_debug("Inside %s\n", __func__);
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
val->intval =
(bcm59055_power->power_src ==
POWER_SUPPLY_TYPE_USB) ? 1 : 0;
break;
case POWER_SUPPLY_PROP_MODEL_NAME:
val->strval = usb_model_name[bcm59055_power->usb_type];
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = -1;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int bcm59055_wall_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
int ret = 0;
struct bcm59055_power *bcm59055_power =
dev_get_drvdata(psy->dev->parent);
pr_debug("Inside %s\n", __func__);
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
val->intval =
(bcm59055_power->power_src ==
POWER_SUPPLY_TYPE_MAINS) ? 1 : 0;
break;
case POWER_SUPPLY_PROP_CURRENT_NOW:
val->intval = -1;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int bcm59055_battery_get_property(struct power_supply *psy,
enum power_supply_property psp,
union power_supply_propval *val)
{
int ret = 0;
struct bcm59055_power *battery_data = dev_get_drvdata(psy->dev->parent);
pr_debug("Inside %s\n", __func__);
switch (psp) {
case POWER_SUPPLY_PROP_ONLINE:
val->intval =
(battery_data->power_src ==
POWER_SUPPLY_TYPE_BATTERY) ? 1 : 0;
break;
case POWER_SUPPLY_PROP_STATUS:
val->intval = battery_data->batt_status;
break;
case POWER_SUPPLY_PROP_TECHNOLOGY:
val->intval = battery_data->batt_technology;
break;
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = battery_data->batt_percentage;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MIN:
val->intval = battery_data->batt_min_volt;
break;
case POWER_SUPPLY_PROP_VOLTAGE_MAX:
val->intval = battery_data->batt_max_volt;
break;
case POWER_SUPPLY_PROP_VOLTAGE_NOW:
val->intval = battery_data->batt_voltage_now * 1000;
break;
default:
ret = -EINVAL;
break;
}
return ret;
}
static int get_batt_percentage(struct bcm59055_power *battery_data)
{
int bat_capacity = 0;
u16 count, slp_count;
u32 accm;
int ret;
pr_debug("Inside %s\n", __func__);
if (bcm59055_fg_offset_cal(FAST_CALIBRATION))
pr_info("%s: FAST Calibration for Fuel Gauge failed\n", __func__);
if (bcm59055_fg_init_read())
pr_info("%s: Fuel Gauge Read init failed\n", __func__);
ret = bcm59055_fg_read_soc(&accm, &count, &slp_count);
/* TODO: Need to calculate Batt Capacity */
pr_debug("%s: FG capacity %d\n", __func__, bat_capacity);
if (bat_capacity < 0) {
pr_debug("%s: Fuel Gauge Accumulator read failed\n", __func__);
return bat_capacity;
}
if (battery_data->batt_prev_cap != bat_capacity) {
battery_data->batt_percentage =
(100 * (bat_capacity / battery_data->batt_max_cap));
battery_data->batt_prev_cap = bat_capacity;
return battery_data->batt_percentage;
}
return 0;
}
static int get_batt_voltage(struct bcm59055_power *battery_data)
{
int adc_val;
int voltage;
int i;
pr_debug("Inside %s\n", __func__);
/* TODO: Need to build the ADC-Voltage table for current voltage reading */
adc_val = bcm59055_saradc_read_data(ADC_VMBAT_CHANNEL);
for (i = 0; i < VOLTAGE_ADC_MAX_SAMPLE; i++)
if ((adc_val >= battery_data->batt_adc[i]) &&
adc_val < battery_data->batt_adc[i+1])
break;
if (adc_val == battery_data->batt_adc[i])
voltage = battery_data->batt_vol[i];
else
voltage = battery_data->batt_vol[i] +
((battery_data->batt_vol[i+1] - battery_data->batt_vol[i]) *
((adc_val - battery_data->batt_adc[i]) /
(battery_data->batt_adc[i+1] - battery_data->batt_adc[i])));
battery_data->batt_voltage_now = voltage;
pr_debug("%s: Adc Val %d, Battery voltage %d\n", __func__, adc_val,
voltage);
return voltage;
}
static void bcm59055_batt_lvl_wq(struct work_struct *work)
{
struct bcm59055_power *battery_data = container_of(work,
struct bcm59055_power, batt_lvl_wq.work);
/* For time being this has kept commented as battery management can be moved to
* separate driver
*/
#if 0
if (get_batt_percentage(battery_data))
power_supply_changed(&battery_data->battery);
if (battery_data->power_src != POWER_SUPPLY_TYPE_BATTERY)
get_batt_voltage(battery_data);
pr_debug("%s: Battery percentage %d\n", __func__,
battery_data->batt_percentage);
#endif
/* If charging is not happening bettery level can be measured
* with a period of 1 min else with 10 sec period
*/
if (battery_data->batt_status == POWER_SUPPLY_STATUS_DISCHARGING)
schedule_delayed_work(&battery_data->batt_lvl_wq,
msecs_to_jiffies(60000));
else
schedule_delayed_work(&battery_data->batt_lvl_wq,
msecs_to_jiffies(10000));
}
static void bcm59055_start_charging(struct bcm590xx *bcm59055, int charger_type)
{
int val = 0;
pr_debug("Inside %s\n", __func__);
val = bcm590xx_reg_read(bcm59055, BCM59055_REG_MBCCTRL3);
if (charger_type == POWER_SUPPLY_TYPE_USB)
val |= USB_HOSTEN;
if (charger_type == POWER_SUPPLY_TYPE_MAINS)
val |= WAC_HOSTEN;
if (bcm590xx_reg_write(bcm59055, BCM59055_REG_MBCCTRL3, val)) {
pr_info("%s: Failed to start charging\n", __func__);
return;
}
/* Update battery status */
pvt_data->batt_status = POWER_SUPPLY_STATUS_CHARGING;
power_supply_changed(&pvt_data->battery);
#if 0
val = (BCM59055_REG_PWMLEDCTRL1_PWM_FREQ_256 <<
BCM59055_REG_PWMLEDCTRL1_PWM_FREQ_256_SHIFT) |
(BCM59055_REG_PWMLEDCTRL1_BREATHING_REPEAT <<
BCM59055_REG_PWMLEDCTRL1_BREATHING_REPEAT_SHIFT);
if (bcm590xx_reg_write(bcm59055, BCM59055_REG_PWMLEDCTRL1, val)) {
pr_info("%s Configuring PMU LED failed \n", __func__ );
}
#endif
}
static void bcm59055_stop_charging(struct bcm590xx *bcm59055, int charger_type)
{
int val = 0;
pr_debug("Inside %s\n", __func__);
val = bcm590xx_reg_read(bcm59055, BCM59055_REG_MBCCTRL3);
if (charger_type == POWER_SUPPLY_TYPE_USB)
val &= ~USB_HOSTEN;
if (charger_type == POWER_SUPPLY_TYPE_MAINS)
val &= ~WAC_HOSTEN;
if (bcm590xx_reg_write(bcm59055, BCM59055_REG_MBCCTRL3, val)) {
pr_info("%s: Failed to stop charging\n", __func__);
return;
}
/* Update battery status */
pvt_data->batt_status = POWER_SUPPLY_STATUS_DISCHARGING;
power_supply_changed(&pvt_data->battery);
#if 0
val = (BCM59055_REG_PWMLEDCTRL1_PWM_FREQ_256 <<
BCM59055_REG_PWMLEDCTRL1_PWM_FREQ_256_SHIFT) |
(BCM59055_REG_PWMLEDCTRL1_PWMLED_PD_DISABLE <<
BCM59055_REG_PWMLEDCTRL1_PWMLED_PD_DISABLE_SHIFT) |
(BCM59055_REG_PWMLEDCTRL1_BREATHING_REPEAT <<
BCM59055_REG_PWMLEDCTRL1_BREATHING_REPEAT_SHIFT);
if (bcm590xx_reg_write(bcm59055, BCM59055_REG_PWMLEDCTRL1, val)) {
pr_info("%s Configuring PMU LED failed \n", __func__ );
}
#endif
}
/* USB driver call back once USB enumaration done */
void pmu_set_usb_enum_current(int curr)
{
struct bcm590xx *bcm59055 = pvt_data->bcm590xx;
struct bcm590xx_battery_pdata *pdata = bcm59055->pdata->battery_pdata;
int val;
pr_debug("Inside %s\n", __func__);
/* set the current */
if (curr)
pvt_data->usb_cc = curr;
else
pvt_data->usb_cc = pdata->usb_cc;
val = bcm590xx_reg_read(bcm59055, BCM59055_REG_MBCCTRL10);
val &= ~USB_FC_CC_MASK;
val |= (pvt_data->usb_cc & USB_FC_CC_MASK);
if (bcm590xx_reg_write(bcm59055, BCM59055_REG_MBCCTRL10, val))
pr_info("%s: Failed to set USB FC Current\n", __func__);
}
EXPORT_SYMBOL(pmu_set_usb_enum_current);
static void bcm59055_power_isr(int intr, void *data)
{
int val = 0;
struct bcm59055_power *battery_data = data;
struct bcm590xx *bcm59055 = battery_data->bcm590xx;
pr_debug("Inside %s\n", __func__);
switch (intr) {
case BCM59055_IRQID_INT2_CHGINS:
pr_info("%s: WAC insert interrupt\n", __func__);
battery_data->power_src = POWER_SUPPLY_TYPE_MAINS;
val = bcm590xx_reg_read(bcm59055, BCM59055_REG_ENV3);
if (!(val & P_CHGOV)) {
val = bcm590xx_reg_read(bcm59055, BCM59055_REG_MBCCTRL11);
val &= ~WAC_FC_CC_MASK;
val |= (pvt_data->wac_cc & WAC_FC_CC_MASK);
if (!bcm590xx_reg_write(bcm59055, BCM59055_REG_MBCCTRL11, val))
bcm59055_start_charging(bcm59055, battery_data->power_src);
else {
pr_info("%s: Failed to set WAC FC Current\n", __func__);
}
} else
pr_info("%s: WAC Over Voltage\n", __func__);
/* Need to start NTC block on after detecting the charger */
bcm59055_saradc_enable_ntc_block();
break;
case BCM59055_IRQID_INT2_CHGRM:
pr_info("%s: WAC remove interrupt\n", __func__);
battery_data->power_src = bcm59055_get_power_supply_type(battery_data);
bcm59055_stop_charging(bcm59055, POWER_SUPPLY_TYPE_MAINS);
/* Need to stop NTC block on after detecting the charger */
bcm59055_saradc_disable_ntc_block();
break;
}
}
static int bcm59055_battery_cb(struct notifier_block *nb, unsigned long event,
void *data)
{
struct bcm590xx *bcm59055 = pvt_data->bcm590xx;
u8 val;
pr_info("Inside %s\n", __func__);
printk("%s: Event %ld, Val = %d\n", __func__, event, *(int *)data);
switch(event) {
case BCMPMU_USB_EVENT_IN_RM:
if ((*(int *)data) == 1) {
/* USBINS notification: Need not to do anything. Wait for charger
* type notification.
*/
pvt_data->power_src = POWER_SUPPLY_TYPE_USB;
} else {
pr_info("%s: USB remove notification\n", __func__);
pvt_data->power_src = bcm59055_get_power_supply_type(pvt_data);
bcm59055_stop_charging(bcm59055, POWER_SUPPLY_TYPE_USB);
pvt_data->usb_cc = PRE_ENUM_CURRENT;
pvt_data->usb_type = USB_OFFLINE;
/* Need to stop NTC block on after detecting the charger */
bcm59055_saradc_disable_ntc_block();
}
break;
case BCMPMU_USB_EVENT_CHGR_DETECTION:
pvt_data->usb_type = *(int *)data;
val = bcm590xx_reg_read(bcm59055, BCM59055_REG_ENV3);
if (!(val & P_USBOV)) { /* NO USB OV */
val = bcm590xx_reg_read(bcm59055, BCM59055_REG_MBCCTRL10);
val &= ~USB_FC_CC_MASK;
val |= (pvt_data->usb_cc & USB_FC_CC_MASK);
if (!bcm590xx_reg_write(bcm59055, BCM59055_REG_MBCCTRL10, val))
bcm59055_start_charging(bcm59055, pvt_data->power_src);
else {
pr_info("%s: Failed to set USB FC Current\n", __func__);
}
} else
pr_info("%s: USB Over Voltage\n", __func__);
/* Need to start NTC block on after detecting the charger */
bcm59055_saradc_enable_ntc_block();
break;
}
return 0;
}
static int bcm59055_init_charger(struct bcm59055_power *battery_data)
{
int val = 0, ret = 0 ;
struct bcm590xx *bcm59055 = battery_data->bcm590xx;
pr_debug("Inside %s\n", __func__);
/* Keep NTC block off at start */
bcm59055_saradc_disable_ntc_block();
/* Enable The Fuel Gauge and do a long calibration */
ret = bcm59055_fg_enable();
if (ret) {
pr_info("%s: Failed to enable Fuel Gauge\n", __func__);
return ret;
}
ret = bcm59055_fg_offset_cal(LONG_CALIBRATION);
if (ret) {
pr_info("%s: Failed to long calibrate Fuel Gauge\n", __func__);
return ret;
}
/* Set the VFLOAT MAX..the max voltage for MBC */
val = bcm590xx_reg_read(bcm59055, BCM59055_REG_MBCCTRL6);
val &= ~VFLOAT_MASK;
val |= (VFLOAT_4_2_V | VFLOATMAX_LOCK);
ret = bcm590xx_reg_write(bcm59055, BCM59055_REG_MBCCTRL6, val);
if (ret) {
pr_info("%s Configuring MBCCTRL6 failed \n", __func__ );
return ret;
}
/* Set VFLOAT..charging voltage */
val = bcm590xx_reg_read(bcm59055, BCM59055_REG_MBCCTRL7);
val &= ~VFLOAT_MASK;
val |= VFLOAT_4_2_V;
ret = bcm590xx_reg_write(bcm59055, BCM59055_REG_MBCCTRL7, val);
if (ret) {
pr_info("%s Configuring MBCCTRL7 failed \n", __func__ );
return ret;
}
/* set ICC Max the max current for MBC */
val = bcm590xx_reg_read(bcm59055, BCM59055_REG_MBCCTRL8);
val &= ~ICCMAX_MASK;
val |= (CURRENT_900_MA | ICCMAX_LOCK);
ret = bcm590xx_reg_write(bcm59055, BCM59055_REG_MBCCTRL8, val);
if (ret) {
pr_info("%s Configuring MBCCTRL8 failed \n", __func__ );
return ret;
}
val = bcm590xx_reg_read(bcm59055, BCM59055_REG_MBCCTRL9);
val |= SWUP;
ret = bcm590xx_reg_write(bcm59055, BCM59055_REG_MBCCTRL9, val);
if (ret) {
pr_info("%s Configuring MBCCTRL8 failed \n", __func__ );
return ret;
}
battery_data->nb.notifier_call = bcm59055_battery_cb;
ret = bcmpmu_usb_add_notifier(BCMPMU_USB_EVENT_IN_RM, &battery_data->nb);
ret |= bcmpmu_usb_add_notifier(BCMPMU_USB_EVENT_CHGR_DETECTION,
&battery_data->nb);
ret = bcm590xx_request_irq(bcm59055, BCM59055_IRQID_INT2_CHGINS, true,
bcm59055_power_isr, battery_data);
ret |= bcm590xx_request_irq(bcm59055, BCM59055_IRQID_INT2_CHGRM, true,
bcm59055_power_isr, battery_data);
if (ret) {
pr_info("%s request_irq for charger inserted condition failed \n",
__func__ );
return ret;
}
return 0;
}
static int bcm59055_battery_probe(struct platform_device *pdev)
{
int ret, usb_typ;
struct bcm59055_power *battery_data;
struct bcm590xx *bcm59055 = dev_get_drvdata(pdev->dev.parent);
struct bcm590xx_battery_pdata *battery_pdata;
pr_info("BCM59055 Battery Driver Probe\n");
battery_pdata = bcm59055->pdata->battery_pdata;
battery_data = kzalloc(sizeof(struct bcm59055_power), GFP_KERNEL);
if (battery_data == NULL) {
pr_info("%s : Failed to allocate memory for bcm59055_power\n", __func__);
ret = -ENOMEM;
goto err_data_alloc;
}
battery_data->bcm590xx = bcm59055;
battery_data->batt_max_cap = battery_pdata->batt_max_capacity;
battery_data->batt_min_volt = battery_pdata->batt_min_volt;
battery_data->batt_max_volt = battery_pdata->batt_max_volt;
battery_data->batt_technology = battery_pdata->batt_technology;
battery_data->usb_cc = PRE_ENUM_CURRENT;
battery_data->batt_vol = battery_pdata->batt_vol;
battery_data->batt_adc = battery_pdata->batt_adc;
battery_data->power_src = POWER_SUPPLY_TYPE_BATTERY;
INIT_DELAYED_WORK(&battery_data->batt_lvl_wq, bcm59055_batt_lvl_wq);
platform_set_drvdata(pdev, battery_data);
pvt_data = battery_data;
ret = bcm59055_init_charger(battery_data);
if (ret < 0) {
pr_info("%s Charger initialization failed. \n", __func__);
goto err_ac_register;
}
if (get_batt_percentage(battery_data) < 0)
battery_data->batt_percentage = 50;
/* For now no FG and ADC is connected in rayboard so hardcoding the capacity */
battery_data->batt_percentage = 50;
battery_data->battery.name = "bcm59055-battery";
battery_data->battery.type = POWER_SUPPLY_TYPE_BATTERY;
battery_data->battery.properties = bcm59055_battery_props;
battery_data->battery.num_properties = ARRAY_SIZE(bcm59055_battery_props);
battery_data->battery.get_property = bcm59055_battery_get_property;
battery_data->batt_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
battery_data->wall.name = "bcm59055-usb";
battery_data->wall.type = POWER_SUPPLY_TYPE_USB;
battery_data->wall.properties = bcm59055_usb_props;
battery_data->wall.num_properties = ARRAY_SIZE(bcm59055_usb_props);
battery_data->wall.get_property = bcm59055_usb_get_property;
battery_data->usb.name = "bcm59055-wall";
battery_data->usb.type = POWER_SUPPLY_TYPE_MAINS;
battery_data->usb.properties = bcm59055_wall_props;
battery_data->usb.num_properties = ARRAY_SIZE(bcm59055_wall_props);
battery_data->usb.get_property = bcm59055_wall_get_property;
/* Register AC power supply */
ret = power_supply_register(&pdev->dev, &battery_data->wall);
if (ret) {
pr_info("%s : Failed to register WALL power supply\n", __func__);
goto err_ac_register;
}
/* Register battery power supply */
ret = power_supply_register(&pdev->dev, &battery_data->usb);
if (ret) {
pr_info("%s : Failed to register battery power supply\n", __func__);
goto err_battery_register;
}
/* Register USB power supply */
ret = power_supply_register(&pdev->dev, &battery_data->battery);
if (ret) {
pr_info("%s : Failed to register battery power supply\n", __func__);
goto err_usb_register;
}
battery_data->usb_type = USB_OFFLINE;
// Check if charger is already connected
ret = bcm59055_get_power_supply_type(battery_data);
if (ret != POWER_SUPPLY_TYPE_BATTERY /*&& usb_driver_init*/) {
pr_info("%s: Charger connected %s\n", __func__,
(ret == POWER_SUPPLY_TYPE_MAINS ? "Wall" : "USB"));
if (ret == POWER_SUPPLY_TYPE_MAINS)
bcm59055_power_isr(BCM59055_IRQID_INT2_CHGINS, battery_data);
else {
usb_typ = bcmpmu_usb_get(BCMPMU_CTRL_GET_CHARGER_TYPE, bcm59055);
battery_data->power_src = POWER_SUPPLY_TYPE_USB;
printk("%s: USB Type %d\n", __func__, usb_typ);
bcm59055_battery_cb(&battery_data->nb,
BCMPMU_USB_EVENT_CHGR_DETECTION, &usb_typ);
}
}
printk("%s: pwr_src %d, usb_typ %d\n", __func__, battery_data->power_src,
battery_data->usb_type);
schedule_delayed_work(&battery_data->batt_lvl_wq, 0 );
printk("%s: Probe Sucess\n", __func__);
return 0;
err_usb_register:
power_supply_unregister(&battery_data->usb);
err_battery_register:
power_supply_unregister(&battery_data->wall);
err_ac_register:
kfree(battery_data);
err_data_alloc:
return ret;
}
static int bcm59055_battery_remove(struct platform_device *pdev)
{
struct bcm59055_power *data = platform_get_drvdata(pdev);
struct bcm590xx *bcm59055 = data->bcm590xx;
pr_debug("Inside %s\n", __func__);
bcm59055_fg_disable();
power_supply_unregister(&data->battery);
power_supply_unregister(&data->wall);
power_supply_unregister(&data->usb);
cancel_delayed_work(&data->batt_lvl_wq);
bcm590xx_free_irq(bcm59055, BCM59055_IRQID_INT2_CHGINS);
bcm590xx_free_irq(bcm59055, BCM59055_IRQID_INT2_CHGRM);
kfree(data);
return 0;
}
static struct platform_driver bcm59055_battery_driver = {
.probe = bcm59055_battery_probe,
.remove = bcm59055_battery_remove,
.driver = {
.name = "bcm590xx-power"
}
};
static int __init bcm59055_battery_init(void)
{
return platform_driver_register(&bcm59055_battery_driver);
}
static void __exit bcm59055_battery_exit(void)
{
platform_driver_unregister(&bcm59055_battery_driver);
}
module_init(bcm59055_battery_init);
module_exit(bcm59055_battery_exit);