483 lines
15 KiB
C
483 lines
15 KiB
C
|
/*
|
||
|
* sec_dual_battery.c
|
||
|
* Samsung Mobile Charger Driver
|
||
|
*
|
||
|
* Copyright (C) 2018 Samsung Electronics
|
||
|
*
|
||
|
*
|
||
|
* 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.
|
||
|
*/
|
||
|
#define DEBUG
|
||
|
|
||
|
#include "include/sec_battery.h"
|
||
|
#include "include/sec_dual_battery.h"
|
||
|
|
||
|
static enum power_supply_property sec_dual_battery_props[] = {
|
||
|
};
|
||
|
|
||
|
static int sec_dual_check_eoc_status(struct sec_dual_battery_info *battery)
|
||
|
{
|
||
|
union power_supply_propval value;
|
||
|
|
||
|
/* check out main battery's eoc status */
|
||
|
value.intval = SEC_BATTERY_VOLTAGE_MV;
|
||
|
psy_do_property(battery->pdata->main_limiter_name, get,
|
||
|
(enum power_supply_property)POWER_SUPPLY_EXT_PROP_BAT_VOLTAGE, value);
|
||
|
battery->main_voltage_avg = value.intval;
|
||
|
|
||
|
value.intval = SEC_BATTERY_CURRENT_MA;
|
||
|
psy_do_property(battery->pdata->main_limiter_name, get,
|
||
|
(enum power_supply_property)POWER_SUPPLY_EXT_PROP_CHG_CURRENT, value);
|
||
|
battery->main_current_avg = value.intval;
|
||
|
|
||
|
pr_info("%s %d %d %d %d \n", __func__, battery->main_voltage_avg, battery->pdata->main_full_condition_vcell, battery->main_current_avg, battery->pdata->main_full_condition_eoc);
|
||
|
if(battery->main_voltage_avg >= battery->pdata->main_full_condition_vcell &&
|
||
|
battery->main_current_avg <= battery->pdata->main_full_condition_eoc &&
|
||
|
!(battery->full_total_status & SEC_DUAL_BATTERY_MAIN_CONDITION_DONE)) {
|
||
|
pr_info("%s Main Batt eoc condtion is done \n", __func__);
|
||
|
battery->full_total_status |= SEC_DUAL_BATTERY_MAIN_CONDITION_DONE;
|
||
|
/* main supplement mode enable */
|
||
|
value.intval = 1;
|
||
|
psy_do_property(battery->pdata->main_limiter_name, set,
|
||
|
POWER_SUPPLY_PROP_CHARGE_FULL, value);
|
||
|
}
|
||
|
|
||
|
/* check out sub battery's eoc status */
|
||
|
value.intval = SEC_BATTERY_VOLTAGE_MV;
|
||
|
psy_do_property(battery->pdata->sub_limiter_name, get,
|
||
|
(enum power_supply_property)POWER_SUPPLY_EXT_PROP_BAT_VOLTAGE, value);
|
||
|
battery->sub_voltage_avg = value.intval;
|
||
|
|
||
|
value.intval = SEC_BATTERY_CURRENT_MA;
|
||
|
psy_do_property(battery->pdata->sub_limiter_name, get,
|
||
|
(enum power_supply_property)POWER_SUPPLY_EXT_PROP_CHG_CURRENT, value);
|
||
|
battery->sub_current_avg = value.intval;
|
||
|
|
||
|
pr_info("%s %d %d %d %d \n", __func__, battery->sub_voltage_avg, battery->pdata->sub_full_condition_vcell, battery->sub_current_avg, battery->pdata->sub_full_condition_eoc);
|
||
|
if(battery->sub_voltage_avg >= battery->pdata->sub_full_condition_vcell &&
|
||
|
battery->sub_current_avg <= battery->pdata->sub_full_condition_eoc &&
|
||
|
!(battery->full_total_status & SEC_DUAL_BATTERY_SUB_CONDITION_DONE)) {
|
||
|
pr_info("%s Sub Batt eoc condtion is done \n", __func__);
|
||
|
battery->full_total_status |= SEC_DUAL_BATTERY_SUB_CONDITION_DONE;
|
||
|
/* main supplement mode enable */
|
||
|
value.intval = 1;
|
||
|
psy_do_property(battery->pdata->sub_limiter_name, set,
|
||
|
POWER_SUPPLY_PROP_CHARGE_FULL, value);
|
||
|
}
|
||
|
|
||
|
pr_info("%s full satus = 0x%x \n", __func__, battery->full_total_status);
|
||
|
|
||
|
if(battery->full_total_status == (SEC_DUAL_BATTERY_MAIN_CONDITION_DONE | SEC_DUAL_BATTERY_SUB_CONDITION_DONE))
|
||
|
return POWER_SUPPLY_STATUS_FULL;
|
||
|
else
|
||
|
return POWER_SUPPLY_STATUS_CHARGING;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
static int sec_dual_check_eoc_current(struct sec_dual_battery_info *battery)
|
||
|
{
|
||
|
union power_supply_propval value;
|
||
|
|
||
|
psy_do_property(battery->pdata->main_limiter_name, get,
|
||
|
POWER_SUPPLY_EXT_PROP_CHG_CURRENT, value);
|
||
|
battery->main_current_avg = value.intval;
|
||
|
|
||
|
if(battery->main_current_avg <= battery->pdata->main_full_condition_eoc) {
|
||
|
battery->full_current_status |= SEC_DUAL_BATTERY_MAIN_CONDITION_DONE;
|
||
|
}
|
||
|
|
||
|
psy_do_property(battery->pdata->sub_limiter_name, get,
|
||
|
POWER_SUPPLY_EXT_PROP_CHG_CURRENT, value);
|
||
|
battery->sub_current_avg = value.intval;
|
||
|
|
||
|
if(battery->sub_current_avg <= battery->pdata->sub_full_condition_eoc) {
|
||
|
battery->full_current_status |= SEC_DUAL_BATTERY_MAIN_CONDITION_DONE;
|
||
|
}
|
||
|
|
||
|
if(battery->full_current_status == (SEC_DUAL_BATTERY_MAIN_CONDITION_DONE | SEC_DUAL_BATTERY_SUB_CONDITION_DONE))
|
||
|
return POWER_SUPPLY_STATUS_FULL;
|
||
|
else
|
||
|
return POWER_SUPPLY_STATUS_CHARGING;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static int sec_dual_battery_current_avg(struct sec_dual_battery_info *battery, int bat_type, int mode)
|
||
|
{
|
||
|
union power_supply_propval value;
|
||
|
int ichg = 0, idis = 0;
|
||
|
|
||
|
if(bat_type == SEC_DUAL_BATTERY_MAIN) {
|
||
|
value.intval = SEC_BATTERY_CURRENT_MA;
|
||
|
psy_do_property(battery->pdata->main_limiter_name, get,
|
||
|
(enum power_supply_property)POWER_SUPPLY_EXT_PROP_CHG_CURRENT, value);
|
||
|
ichg = value.intval;
|
||
|
value.intval = SEC_BATTERY_CURRENT_MA;
|
||
|
psy_do_property(battery->pdata->main_limiter_name, get,
|
||
|
(enum power_supply_property)POWER_SUPPLY_EXT_PROP_DISCHG_CURRENT, value);
|
||
|
idis = value.intval;
|
||
|
} else {
|
||
|
value.intval = SEC_BATTERY_CURRENT_MA;
|
||
|
psy_do_property(battery->pdata->sub_limiter_name, get,
|
||
|
(enum power_supply_property)POWER_SUPPLY_EXT_PROP_CHG_CURRENT, value);
|
||
|
ichg = value.intval;
|
||
|
value.intval = SEC_BATTERY_CURRENT_MA;
|
||
|
psy_do_property(battery->pdata->sub_limiter_name, get,
|
||
|
(enum power_supply_property)POWER_SUPPLY_EXT_PROP_DISCHG_CURRENT, value);
|
||
|
idis = value.intval;
|
||
|
}
|
||
|
|
||
|
pr_info("%s: ichg=%d, idis=%d\n", __func__, ichg, idis);
|
||
|
|
||
|
if((ichg == 0) && (idis == 0))
|
||
|
return 0;
|
||
|
else {
|
||
|
if(ichg != 0) return ichg;
|
||
|
else return idis * (-1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int sec_dual_battery_voltage_avg(struct sec_dual_battery_info *battery, int bat_type, int mode)
|
||
|
{
|
||
|
union power_supply_propval value;
|
||
|
int vbat = 0;
|
||
|
|
||
|
if(bat_type == SEC_DUAL_BATTERY_MAIN) {
|
||
|
value.intval = SEC_BATTERY_VOLTAGE_MV;
|
||
|
psy_do_property(battery->pdata->main_limiter_name, get,
|
||
|
(enum power_supply_property)POWER_SUPPLY_EXT_PROP_BAT_VOLTAGE, value);
|
||
|
vbat = value.intval;
|
||
|
} else {
|
||
|
value.intval = SEC_BATTERY_VOLTAGE_MV;
|
||
|
psy_do_property(battery->pdata->sub_limiter_name, get,
|
||
|
(enum power_supply_property)POWER_SUPPLY_EXT_PROP_BAT_VOLTAGE, value);
|
||
|
vbat = value.intval;
|
||
|
}
|
||
|
|
||
|
pr_info("%s: vbat=%d\n", __func__, vbat);
|
||
|
|
||
|
return vbat;
|
||
|
}
|
||
|
|
||
|
static int sec_dual_battery_get_property(struct power_supply *psy,
|
||
|
enum power_supply_property psp,
|
||
|
union power_supply_propval *val)
|
||
|
{
|
||
|
struct sec_dual_battery_info *battery =
|
||
|
power_supply_get_drvdata(psy);
|
||
|
enum power_supply_ext_property ext_psp = (enum power_supply_ext_property)psp;
|
||
|
union power_supply_propval value;
|
||
|
|
||
|
value.intval = val->intval;
|
||
|
|
||
|
switch (psp) {
|
||
|
case POWER_SUPPLY_PROP_STATUS:
|
||
|
val->intval = sec_dual_check_eoc_status(battery);
|
||
|
break;
|
||
|
case POWER_SUPPLY_PROP_VOLTAGE_AVG:
|
||
|
if(value.intval == SEC_DUAL_BATTERY_MAIN)
|
||
|
val->intval = sec_dual_battery_voltage_avg(battery, SEC_DUAL_BATTERY_MAIN, SEC_BATTERY_VOLTAGE_MV);
|
||
|
else
|
||
|
val->intval = sec_dual_battery_voltage_avg(battery, SEC_DUAL_BATTERY_SUB, SEC_BATTERY_VOLTAGE_MV);
|
||
|
break;
|
||
|
case POWER_SUPPLY_PROP_CURRENT_AVG:
|
||
|
if(value.intval == SEC_DUAL_BATTERY_MAIN)
|
||
|
val->intval = sec_dual_battery_current_avg(battery, SEC_DUAL_BATTERY_MAIN, SEC_BATTERY_CURRENT_MA);
|
||
|
else
|
||
|
val->intval = sec_dual_battery_current_avg(battery, SEC_DUAL_BATTERY_SUB, SEC_BATTERY_CURRENT_MA);
|
||
|
break;
|
||
|
case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX:
|
||
|
switch (ext_psp) {
|
||
|
case POWER_SUPPLY_EXT_PROP_DUAL_BAT_DET:
|
||
|
if (value.intval == SEC_DUAL_BATTERY_MAIN) {
|
||
|
if (battery->pdata->main_bat_con_det_gpio) {
|
||
|
val->intval = !gpio_get_value(battery->pdata->main_bat_con_det_gpio);
|
||
|
pr_info("%s : main det(%d) = %d \n", __func__, battery->pdata->main_bat_con_det_gpio, (int)value.intval);
|
||
|
}
|
||
|
else
|
||
|
val->intval = -1;
|
||
|
} else if (value.intval == SEC_DUAL_BATTERY_SUB) {
|
||
|
if (battery->pdata->sub_bat_con_det_gpio) {
|
||
|
val->intval = !gpio_get_value(battery->pdata->sub_bat_con_det_gpio);
|
||
|
pr_info("%s : sub det(%d) = %d \n", __func__, battery->pdata->sub_bat_con_det_gpio, (int)value.intval);
|
||
|
}
|
||
|
else
|
||
|
val->intval = -1;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int sec_dual_battery_set_property(struct power_supply *psy,
|
||
|
enum power_supply_property psp,
|
||
|
const union power_supply_propval *val)
|
||
|
{
|
||
|
struct sec_dual_battery_info *battery =
|
||
|
power_supply_get_drvdata(psy);
|
||
|
enum power_supply_ext_property ext_psp = (enum power_supply_ext_property)psp;
|
||
|
union power_supply_propval value;
|
||
|
|
||
|
//value.intval = val->intval;
|
||
|
switch (psp) {
|
||
|
case POWER_SUPPLY_PROP_CHARGING_ENABLED:
|
||
|
break;
|
||
|
case POWER_SUPPLY_PROP_CHARGE_FULL:
|
||
|
if(val->intval == 0) {
|
||
|
/* disable main/sub supplement mode */
|
||
|
value.intval = 0;
|
||
|
psy_do_property(battery->pdata->main_limiter_name, set,
|
||
|
POWER_SUPPLY_PROP_CHARGE_FULL, value);
|
||
|
psy_do_property(battery->pdata->sub_limiter_name, set,
|
||
|
POWER_SUPPLY_PROP_CHARGE_FULL, value);
|
||
|
battery->full_total_status = SEC_DUAL_BATTERY_NONE;
|
||
|
}
|
||
|
//else {
|
||
|
//value.intval = 1;
|
||
|
/* enable main/sub supplement mode */
|
||
|
//psy_do_property(battery->pdata->main_limiter_name, set,
|
||
|
// POWER_SUPPLY_PROP_CHARGE_FULL, value);
|
||
|
//psy_do_property(battery->pdata->sub_limiter_name, set,
|
||
|
// POWER_SUPPLY_PROP_CHARGE_FULL, value);
|
||
|
//}
|
||
|
break;
|
||
|
case POWER_SUPPLY_PROP_ENERGY_NOW:
|
||
|
/* SET PWR OFF MODE 2*/
|
||
|
if(val->intval == SEC_DUAL_BATTERY_MAIN) {
|
||
|
value.intval = 1;
|
||
|
psy_do_property(battery->pdata->main_limiter_name, set,
|
||
|
POWER_SUPPLY_PROP_ENERGY_NOW, value);
|
||
|
} else {
|
||
|
value.intval = 1;
|
||
|
psy_do_property(battery->pdata->sub_limiter_name, set,
|
||
|
POWER_SUPPLY_PROP_ENERGY_NOW, value);
|
||
|
}
|
||
|
break;
|
||
|
case POWER_SUPPLY_PROP_MAX ... POWER_SUPPLY_EXT_PROP_MAX:
|
||
|
switch (ext_psp) {
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
return -EINVAL;
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_OF
|
||
|
static int sec_dual_battery_parse_dt(struct device *dev,
|
||
|
struct sec_dual_battery_info *battery)
|
||
|
{
|
||
|
struct device_node *np = dev->of_node;
|
||
|
struct sec_dual_battery_platform_data *pdata = battery->pdata;
|
||
|
int ret = 0;
|
||
|
//int len;
|
||
|
//const u32 *p;
|
||
|
|
||
|
if (!np) {
|
||
|
pr_err("%s: np NULL\n", __func__);
|
||
|
return 1;
|
||
|
} else {
|
||
|
ret = of_property_read_string(np, "battery,main_current_limiter",
|
||
|
(char const **)&battery->pdata->main_limiter_name);
|
||
|
if (ret)
|
||
|
pr_err("%s: main_current_limiter is Empty\n", __func__);
|
||
|
|
||
|
ret = of_property_read_string(np, "battery,sub_current_limiter",
|
||
|
(char const **)&battery->pdata->sub_limiter_name);
|
||
|
if (ret)
|
||
|
pr_err("%s: sub_current_limiter is Empty\n", __func__);
|
||
|
|
||
|
ret = of_property_read_u32(np, "battery,main_full_condition_vcell",
|
||
|
&pdata->main_full_condition_vcell);
|
||
|
if (ret < 0) {
|
||
|
pr_info("%s : main_full_condition_vcell is empty\n", __func__);
|
||
|
pdata->main_full_condition_vcell = 4250;
|
||
|
}
|
||
|
|
||
|
ret = of_property_read_u32(np, "battery,sub_full_condition_vcell",
|
||
|
&pdata->sub_full_condition_vcell);
|
||
|
if (ret < 0) {
|
||
|
pr_info("%s : sub_full_condition_vcell is empty\n", __func__);
|
||
|
pdata->sub_full_condition_vcell = 4250;
|
||
|
}
|
||
|
|
||
|
ret = of_property_read_u32(np, "battery,main_full_condition_eoc",
|
||
|
&pdata->main_full_condition_eoc);
|
||
|
if (ret < 0) {
|
||
|
pr_info("%s : main_full_condition_eoc is empty\n", __func__);
|
||
|
pdata->main_full_condition_eoc = 100;
|
||
|
}
|
||
|
|
||
|
ret = of_property_read_u32(np, "battery,sub_full_condition_eoc",
|
||
|
&pdata->sub_full_condition_eoc);
|
||
|
if (ret < 0) {
|
||
|
pr_info("%s : sub_full_condition_eoc is empty\n", __func__);
|
||
|
pdata->sub_full_condition_eoc = 100;
|
||
|
}
|
||
|
/* MAIN_BATTERY_CON_DET */
|
||
|
ret = pdata->main_bat_con_det_gpio = of_get_named_gpio(np, "battery,main_bat_con_det_gpio", 0);
|
||
|
if (ret < 0) {
|
||
|
pr_info("%s : can't get main_bat_con_det_gpio\n", __func__);
|
||
|
}
|
||
|
/* SUB_BATTERY_CON_DET */
|
||
|
ret = pdata->sub_bat_con_det_gpio = of_get_named_gpio(np, "battery,sub_bat_con_det_gpio", 0);
|
||
|
if (ret < 0) {
|
||
|
pr_info("%s : can't get sub_bat_con_det_gpio\n", __func__);
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static const struct power_supply_desc sec_dual_battery_power_supply_desc = {
|
||
|
.name = "sec-dual-battery",
|
||
|
.type = POWER_SUPPLY_TYPE_UNKNOWN,
|
||
|
.properties = sec_dual_battery_props,
|
||
|
.num_properties = ARRAY_SIZE(sec_dual_battery_props),
|
||
|
.get_property = sec_dual_battery_get_property,
|
||
|
.set_property = sec_dual_battery_set_property,
|
||
|
};
|
||
|
|
||
|
static int sec_dual_battery_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
struct sec_dual_battery_info *battery;
|
||
|
struct sec_dual_battery_platform_data *pdata = NULL;
|
||
|
struct power_supply_config dual_battery_cfg = {};
|
||
|
int ret = 0;
|
||
|
|
||
|
dev_info(&pdev->dev,
|
||
|
"%s: SEC Dual Battery Driver Loading\n", __func__);
|
||
|
|
||
|
battery = kzalloc(sizeof(*battery), GFP_KERNEL);
|
||
|
if (!battery)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
if (pdev->dev.of_node) {
|
||
|
pdata = devm_kzalloc(&pdev->dev,
|
||
|
sizeof(struct sec_dual_battery_platform_data),
|
||
|
GFP_KERNEL);
|
||
|
if (!pdata) {
|
||
|
dev_err(&pdev->dev, "Failed to allocate memory\n");
|
||
|
ret = -ENOMEM;
|
||
|
goto err_battery_free;
|
||
|
}
|
||
|
|
||
|
battery->pdata = pdata;
|
||
|
if (sec_dual_battery_parse_dt(&pdev->dev, battery)) {
|
||
|
dev_err(&pdev->dev,
|
||
|
"%s: Failed to get sec-dual-battery dt\n", __func__);
|
||
|
ret = -EINVAL;
|
||
|
goto err_battery_free;
|
||
|
}
|
||
|
} else {
|
||
|
pdata = dev_get_platdata(&pdev->dev);
|
||
|
battery->pdata = pdata;
|
||
|
}
|
||
|
|
||
|
platform_set_drvdata(pdev, battery);
|
||
|
battery->dev = &pdev->dev;
|
||
|
dual_battery_cfg.drv_data = battery;
|
||
|
|
||
|
battery->psy_bat = power_supply_register(&pdev->dev, &sec_dual_battery_power_supply_desc, &dual_battery_cfg);
|
||
|
if (!battery->psy_bat) {
|
||
|
dev_err(battery->dev,
|
||
|
"%s: Failed to Register psy_bat\n", __func__);
|
||
|
goto err_pdata_free;
|
||
|
}
|
||
|
|
||
|
dev_info(battery->dev,
|
||
|
"%s: SEC Dual Battery Driver Loaded\n", __func__);
|
||
|
return 0;
|
||
|
|
||
|
err_pdata_free:
|
||
|
kfree(pdata);
|
||
|
err_battery_free:
|
||
|
kfree(battery);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static int sec_dual_battery_remove(struct platform_device *pdev)
|
||
|
{
|
||
|
struct sec_dual_battery_info *battery = platform_get_drvdata(pdev);
|
||
|
|
||
|
power_supply_unregister(battery->psy_bat);
|
||
|
|
||
|
dev_dbg(battery->dev, "%s: End\n", __func__);
|
||
|
|
||
|
kfree(battery->pdata);
|
||
|
kfree(battery);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int sec_dual_battery_suspend(struct device *dev)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int sec_dual_battery_resume(struct device *dev)
|
||
|
{
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void sec_dual_battery_shutdown(struct platform_device *pdev)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
#ifdef CONFIG_OF
|
||
|
static struct of_device_id sec_dual_battery_dt_ids[] = {
|
||
|
{ .compatible = "samsung,sec-dual-battery" },
|
||
|
{ }
|
||
|
};
|
||
|
MODULE_DEVICE_TABLE(of, sec_dual_battery_dt_ids);
|
||
|
#endif /* CONFIG_OF */
|
||
|
|
||
|
static const struct dev_pm_ops sec_dual_battery_pm_ops = {
|
||
|
.suspend = sec_dual_battery_suspend,
|
||
|
.resume = sec_dual_battery_resume,
|
||
|
};
|
||
|
|
||
|
static struct platform_driver sec_dual_battery_driver = {
|
||
|
.driver = {
|
||
|
.name = "sec-dual-battery",
|
||
|
.owner = THIS_MODULE,
|
||
|
.pm = &sec_dual_battery_pm_ops,
|
||
|
#ifdef CONFIG_OF
|
||
|
.of_match_table = sec_dual_battery_dt_ids,
|
||
|
#endif
|
||
|
},
|
||
|
.probe = sec_dual_battery_probe,
|
||
|
.remove = sec_dual_battery_remove,
|
||
|
.shutdown = sec_dual_battery_shutdown,
|
||
|
};
|
||
|
|
||
|
static int __init sec_dual_battery_init(void)
|
||
|
{
|
||
|
pr_info("%s: \n", __func__);
|
||
|
return platform_driver_register(&sec_dual_battery_driver);
|
||
|
}
|
||
|
|
||
|
static void __exit sec_dual_battery_exit(void)
|
||
|
{
|
||
|
platform_driver_unregister(&sec_dual_battery_driver);
|
||
|
}
|
||
|
|
||
|
device_initcall_sync(sec_dual_battery_init);
|
||
|
module_exit(sec_dual_battery_exit);
|
||
|
|
||
|
MODULE_DESCRIPTION("Samsung Dual Battery Driver");
|
||
|
MODULE_AUTHOR("Samsung Electronics");
|
||
|
MODULE_LICENSE("GPL");
|