[9610] drivers/include/dts: s2mu00x batt. driver bring-up
authorKeunho Hwang <keunho.hwang@samsung.com>
Fri, 11 May 2018 01:26:22 +0000 (10:26 +0900)
committerJaehyoung Choi <jkkkkk.choi@samsung.com>
Fri, 11 May 2018 07:38:20 +0000 (16:38 +0900)
Change-Id: I4db3c51a8327480ec109929c3ada9479acd80e95
Signed-off-by: Keunho Hwang <keunho.hwang@samsung.com>
arch/arm64/boot/dts/exynos/exynos9610-erd9610.dts
arch/arm64/boot/dts/exynos/exynos9610_battery_data.dtsi [new file with mode: 0644]
drivers/power/supply/Kconfig
drivers/power/supply/Makefile
drivers/power/supply/s2mu00x_battery.c [new file with mode: 0644]
include/linux/power/s2mu00x_battery.h [new file with mode: 0644]

index bfdc6ed6adc3f5292f020d5ec5e46d9df8f695f9..94ddc1580a3f46da09547fc6cb3c6e025aa95323 100644 (file)
@@ -11,6 +11,7 @@
 
 /dts-v1/;
 #include "exynos9610.dtsi"
+#include "exynos9610_battery_data.dtsi"
 
 / {
        model = "Samsung MAESTRO-ERD9610 board based on EXYNOS9610";
diff --git a/arch/arm64/boot/dts/exynos/exynos9610_battery_data.dtsi b/arch/arm64/boot/dts/exynos/exynos9610_battery_data.dtsi
new file mode 100644 (file)
index 0000000..941fcda
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * SAMSUNG EXYNOS9610 board device tree source
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ *             http://www.samsung.com
+ *
+ * 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.
+ */
+
+/ {
+       fragment@battery {
+               target-path = "/";
+               __overlay__ {
+                       battery {
+                               status = "okay";
+
+                               pinctrl-names = "default";
+
+                               battery,vendor = "SDI SDI";
+                               battery,charger_name = "s2mu004-charger";
+                               battery,fuelgauge_name = "s2mu004-fuelgauge";
+                               battery,technology = <2>; /* POWER_SUPPLY_TECHNOLOGY_LION */
+
+                               battery,temp_high = <500>;
+                               battery,temp_high_recovery = <450>;
+                               battery,temp_low = <100>;
+                               battery,temp_low_recovery = <150>;
+
+                               battery,full_check_count = <3>;
+                               battery,chg_full_vcell = <4310>;
+                               battery,chg_recharge_vcell = <4250>;
+
+                               battery,max_rawsoc = <100>;
+
+                               battery,default_input_current = <1000>;
+                               battery,default_charging_current = <1000>;
+                               battery,default_full_check_current = <300>;
+
+                               battery,max_input_current = <1200>;
+                               battery,max_charging_current = <1200>;
+
+                               /* Order of current setting must be same with
+                                * POWER_SUPPLY_TYPE_ of power_supply.h
+                                */
+                               battery,input_current_limit =
+                                       <500 450 500 1200 500 1200 1200 1000 1000 1000
+                                       1000 500 500 1200 1000 500 450>;
+                               battery,fast_charging_current =
+                                       <500 450 500 1200 500 1200 1200 1000 1000 1000
+                                       1000 500 500 1200 1000 500 450>;
+                               battery,full_check_current =
+                                       <300 0 300 300 300 300 300 300 300 300
+                                       300 300 300 300 300 300 0>;
+
+                               battery,battery_table3 =
+                                       <204 11 61 11 175 10 34 10 169 9
+                                       55 9 206 8 107 8 230 7 166 7
+                                       77 7 234 6 174 6 130 6 95 6
+                                       67 6 36 6 253 5 197 5 147 5
+                                       42 5 168 1 247 8 136 8 24 8
+                                       169 7 58 7 202 6 91 6 236 5
+                                       124 5 13 5 158 4 47 4 191 3
+                                       80 3 225 2 113 2 2 2 147 1
+                                       36 1 180 0 69 0 214 15>;
+
+                               battery,battery_table4 =
+                                       <62 62 61 60 61 60 59 60 60 58
+                                       58 58 58 58 58 58 59 60 62 64
+                                       76 154>;
+
+                               battery,batcap = <0xD0 0x20 0x34 0x08>; /* [0x0E] [0x0F] [0x10] [0x11] */
+                               battery,accum = <0x8 0x00>; /* [0x45] [0x44] */
+
+                               battery,soc_arr_val =
+                                       <11205 10662 10119 9575 9032 8488 7945 7402 6858 6315
+                                       5771 5228 4685 4141 3598 3054 2511 1968 1424 881
+                                       337 (-206)>;
+                               battery,ocv_arr_val =
+                                       <44747 44050 43353 42664 42077 41520 41006 40522 39872 39562
+                                       39127 38645 38349 38135 37966 37828 37676 37485 37211 36970
+                                       36454 32069>;
+                       };
+               };
+       };
+};
index f6cd93a340b4fd615248f1fe72e791f580b24e2d..d1f1da42f656d356fc55dc2c1b9b1050abf25840 100644 (file)
@@ -626,4 +626,10 @@ config CHARGER_S2MU004
       S2MU004 incluse pmic, led driver.
       You have to define MFD_S2MU004
 
+config BATTERY_S2MU00X
+    tristate "S2MU00x battery driver"
+       depends on (MFD_S2MU004) && I2C
+       help
+         Say Y here to enable support for S2MU00x battery driver.
+
 endif # POWER_SUPPLY
index dddc844e2a6980a30435f201ff223f8c816678bc..5007316540f90af6c991ba27a4e04a2edb4704d1 100644 (file)
@@ -84,3 +84,4 @@ obj-$(CONFIG_AXP288_FUEL_GAUGE) += axp288_fuel_gauge.o
 obj-$(CONFIG_AXP288_CHARGER)   += axp288_charger.o
 obj-$(CONFIG_FUELGAUGE_S2MU004) += s2mu004_fuelgauge.o
 obj-$(CONFIG_CHARGER_S2MU004)   += s2mu004_charger.o
+obj-$(CONFIG_BATTERY_S2MU00X)   += s2mu00x_battery.o
diff --git a/drivers/power/supply/s2mu00x_battery.c b/drivers/power/supply/s2mu00x_battery.c
new file mode 100644 (file)
index 0000000..bc03502
--- /dev/null
@@ -0,0 +1,1547 @@
+/*
+ * s2mu00x_battery.c - Example battery driver for S2MU00x series
+ *
+ * Copyright (C) 2017 Samsung Electronics Co.Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/printk.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/power_supply.h>
+#include <linux/power/s2mu00x_battery.h>
+#include <linux/alarmtimer.h>
+
+#if defined(CONFIG_MUIC_NOTIFIER)
+#include <linux/muic/muic_notifier.h>
+#include <linux/muic/muic.h>
+#endif /* CONFIG_MUIC_NOTIFIER */
+
+#if defined(CONFIG_IFCONN_NOTIFIER)
+#include <linux/ifconn/ifconn_notifier.h>
+#include <linux/ifconn/ifconn_manager.h>
+#include <linux/muic/muic_notifier.h>
+#include <linux/muic/muic.h>
+#endif
+
+#define FAKE_BAT_LEVEL 50
+#define DEFAULT_ALARM_INTERVAL 10
+#define SLEEP_ALARM_INTERVAL   30
+
+static char *bat_status_str[] = {
+       "Unknown",
+       "Charging",
+       "Discharging",
+       "Not-charging",
+       "Full"
+};
+
+static char *health_str[] = {
+       "Unknown",
+       "Good",
+       "Overheat",
+       "Dead",
+       "OverVoltage",
+       "UnspecFailure",
+       "Cold",
+       "WatchdogTimerExpire",
+       "SafetyTimerExpire",
+       "UnderVoltage",
+};
+
+static enum power_supply_property s2mu00x_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_HEALTH,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_PRESENT,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_VOLTAGE_AVG,
+       POWER_SUPPLY_PROP_TEMP,
+       POWER_SUPPLY_PROP_CHARGE_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+};
+
+static enum power_supply_property s2mu00x_power_props[] = {
+       POWER_SUPPLY_PROP_ONLINE,
+};
+
+typedef struct s2mu00x_battery_platform_data {
+       s2mu00x_charging_current_t *charging_current;
+       char *charger_name;
+       char *fuelgauge_name;
+
+       int max_input_current;
+       int max_charging_current;
+
+       int temp_high;
+       int temp_high_recovery;
+       int temp_low;
+       int temp_low_recovery;
+
+       /* full check */
+       unsigned int full_check_count;
+       unsigned int chg_recharge_vcell;
+       unsigned int chg_full_vcell;
+
+       /* Initial maximum raw SOC */
+       unsigned int max_rawsoc;
+
+       /* battery */
+       char *vendor;
+       int technology;
+       int battery_type;
+       void *battery_data;
+} s2mu00x_battery_platform_data_t;
+
+struct s2mu00x_battery_info {
+       struct device *dev;
+       s2mu00x_battery_platform_data_t *pdata;
+
+       struct power_supply *psy_battery;
+       struct power_supply_desc psy_battery_desc;
+       struct power_supply *psy_usb;
+       struct power_supply_desc psy_usb_desc;
+       struct power_supply *psy_ac;
+       struct power_supply_desc psy_ac_desc;
+
+       struct mutex iolock;
+
+       struct wake_lock monitor_wake_lock;
+       struct workqueue_struct *monitor_wqueue;
+       struct delayed_work monitor_work;
+       struct wake_lock vbus_wake_lock;
+
+       struct alarm monitor_alarm;
+       unsigned int monitor_alarm_interval;
+
+       int input_current;
+       int max_input_current;
+       int charging_current;
+       int max_charging_current;
+       int topoff_current;
+       int cable_type;
+       unsigned int charging_mode;
+
+#if defined(CONFIG_IFCONN_NOTIFIER)
+       struct notifier_block ifconn_nb;
+#elif defined(CONFIG_MUIC_NOTIFIER)
+       struct notifier_block batt_nb;
+#endif
+
+       int full_check_cnt;
+
+       /* charging */
+       bool is_recharging;
+
+       bool battery_valid;
+       int status;
+       int health;
+
+       int voltage_now;
+       int voltage_avg;
+       int voltage_ocv;
+
+       unsigned int capacity;
+       unsigned int max_rawsoc;
+
+       int current_now;        /* current (mA) */
+       int current_avg;        /* average current (mA) */
+       int current_max;        /* input current limit (mA) */
+
+#if defined(CONFIG_MUIC_NOTIFIER)
+       struct notifier_block cable_check;
+#endif
+
+       /* temperature check */
+       int temperature;    /* battery temperature(0.1 Celsius)*/
+       int temp_high;
+       int temp_high_recovery;
+       int temp_low;
+       int temp_low_recovery;
+};
+
+static char *s2mu00x_supplied_to[] = {
+       "s2mu00x-battery",
+};
+
+static void get_charging_current(struct s2mu00x_battery_info *battery,
+               int *input_current, int *charging_current)
+{
+       int max_input_current = battery->max_input_current;
+       int max_charging_current = battery->max_charging_current;
+
+       if (*input_current > max_input_current) {
+               *input_current = max_input_current;
+               pr_info("%s: limit input current. (%d)\n", __func__, *input_current);
+       }
+       if (*charging_current > max_charging_current) {
+               *charging_current = max_charging_current;
+               pr_info("%s: limit charging current. (%d)\n", __func__, *charging_current);
+       }
+}
+
+static int set_charging_current(struct s2mu00x_battery_info *battery)
+{
+       union power_supply_propval value;
+       int input_current =
+                       battery->pdata->charging_current[battery->cable_type].input_current_limit,
+               charging_current =
+                       battery->pdata->charging_current[battery->cable_type].fast_charging_current,
+               topoff_current =
+                       battery->pdata->charging_current[battery->cable_type].full_check_current;
+       struct power_supply *psy;
+       int ret;
+
+       pr_info("%s: cable_type(%d), current(%d, %d, %d)\n", __func__,
+                       battery->cable_type, input_current, charging_current, topoff_current);
+       mutex_lock(&battery->iolock);
+
+       /*Limit input & charging current according to the max current*/
+       get_charging_current(battery, &input_current, &charging_current);
+
+       /* set input current limit */
+       if (battery->input_current != input_current) {
+               value.intval = input_current;
+
+               psy = power_supply_get_by_name(battery->pdata->charger_name);
+               if (!psy)
+                       return -EINVAL;
+               ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX, &value);
+               if (ret < 0)
+                       pr_err("%s: Fail to execute property\n", __func__);
+
+               battery->input_current = input_current;
+       }
+       /* set fast charging current */
+       if (battery->charging_current != charging_current) {
+               value.intval = charging_current;
+
+               psy = power_supply_get_by_name(battery->pdata->charger_name);
+               if (!psy)
+                       return -EINVAL;
+               ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_CURRENT_NOW, &value);
+               if (ret < 0)
+                       pr_err("%s: Fail to execute property\n", __func__);
+
+               battery->charging_current = charging_current;
+       }
+       /* set topoff current */
+       if (battery->topoff_current != topoff_current) {
+               value.intval = topoff_current;
+
+               psy = power_supply_get_by_name(battery->pdata->charger_name);
+               if (!psy)
+                       return -EINVAL;
+               ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_CURRENT_FULL, &value);
+               if (ret < 0)
+                       pr_err("%s: Fail to execute property\n", __func__);
+
+               battery->topoff_current = topoff_current;
+       }
+
+       mutex_unlock(&battery->iolock);
+       return 0;
+}
+
+
+/*
+ * set_charger_mode(): charger_mode must have one of following values.
+ * 1. S2MU00X_BAT_CHG_MODE_CHARGING
+ *     Charger on.
+ *     Supply power to system & battery both.
+ * 2. S2MU00X_BAT_CHG_MODE_CHARGING_OFF
+ *     Buck mode. Stop battery charging.
+ *     But charger supplies system power.
+ * 3. S2MU00X_BAT_CHG_MODE_BUCK_OFF
+ *     All off. Charger is completely off.
+ *     Do not supply power to battery & system both.
+ */
+
+static int set_charger_mode(
+               struct s2mu00x_battery_info *battery,
+               int charger_mode)
+{
+       union power_supply_propval val;
+       struct power_supply *psy;
+               int ret;
+
+       if (charger_mode != S2MU00X_BAT_CHG_MODE_CHARGING)
+               battery->full_check_cnt = 0;
+
+       val.intval = charger_mode;
+
+       psy = power_supply_get_by_name(battery->pdata->charger_name);
+       if (!psy)
+               return -EINVAL;
+       ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGING_ENABLED, &val);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+
+       return 0;
+}
+
+static int set_battery_status(struct s2mu00x_battery_info *battery,
+               int status)
+{
+       union power_supply_propval value;
+       struct power_supply *psy;
+               int ret;
+
+       pr_info("%s: current status = %d, new status = %d\n", __func__, battery->status, status);
+       if (battery->status == status)
+               return 0;
+
+       switch (status) {
+       case POWER_SUPPLY_STATUS_CHARGING:
+               /* notify charger cable type */
+               value.intval = battery->cable_type;
+
+               psy = power_supply_get_by_name(battery->pdata->charger_name);
+               if (!psy)
+                       return -EINVAL;
+               ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_ONLINE, &value);
+               if (ret < 0)
+                       pr_err("%s: Fail to execute property\n", __func__);
+
+               set_charger_mode(battery, S2MU00X_BAT_CHG_MODE_CHARGING);
+               set_charging_current(battery);
+               break;
+
+       case POWER_SUPPLY_STATUS_DISCHARGING:
+               set_charging_current(battery);
+
+               /* notify charger cable type */
+               value.intval = battery->cable_type;
+
+               psy = power_supply_get_by_name(battery->pdata->charger_name);
+               if (!psy)
+                       return -EINVAL;
+               ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_ONLINE, &value);
+               if (ret < 0)
+                       pr_err("%s: Fail to execute property\n", __func__);
+
+               set_charger_mode(battery, S2MU00X_BAT_CHG_MODE_CHARGING_OFF);
+               break;
+
+       case POWER_SUPPLY_STATUS_NOT_CHARGING:
+               set_charger_mode(battery, S2MU00X_BAT_CHG_MODE_BUCK_OFF);
+
+               /* to recover charger configuration when heath is recovered */
+               battery->input_current = 0;
+               battery->charging_current = 0;
+               battery->topoff_current = 0;
+               break;
+
+       case POWER_SUPPLY_STATUS_FULL:
+               set_charger_mode(battery, S2MU00X_BAT_CHG_MODE_CHARGING_OFF);
+               break;
+       }
+
+       /* battery status update */
+       battery->status = status;
+       value.intval = battery->status;
+
+       psy = power_supply_get_by_name(battery->pdata->charger_name);
+       if (!psy)
+               return -EINVAL;
+       ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_STATUS, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+
+       return 0;
+}
+
+static void set_bat_status_by_cable(struct s2mu00x_battery_info *battery)
+{
+       if (battery->cable_type == POWER_SUPPLY_TYPE_BATTERY ||
+               battery->cable_type == POWER_SUPPLY_TYPE_UNKNOWN ||
+               battery->cable_type == POWER_SUPPLY_TYPE_OTG) {
+               battery->is_recharging = false;
+               set_battery_status(battery, POWER_SUPPLY_STATUS_DISCHARGING);
+               return;
+       }
+       if (battery->status != POWER_SUPPLY_STATUS_FULL) {
+               set_battery_status(battery, POWER_SUPPLY_STATUS_CHARGING);
+               return;
+       }
+
+       dev_info(battery->dev, "%s: abnormal cable_type or status", __func__);
+}
+
+static int s2mu00x_battery_get_property(struct power_supply *psy,
+               enum power_supply_property psp, union power_supply_propval *val)
+{
+       struct s2mu00x_battery_info *battery =  power_supply_get_drvdata(psy);
+       int ret = 0;
+
+       dev_dbg(battery->dev, "prop: %d\n", psp);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = battery->status;
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               val->intval = battery->health;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               val->intval = battery->cable_type;
+               break;
+       case POWER_SUPPLY_PROP_PRESENT:
+               val->intval = battery->battery_valid;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               if (!battery->battery_valid)
+                       val->intval = FAKE_BAT_LEVEL;
+               else
+                       val->intval = battery->voltage_now * 1000;
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_AVG:
+               val->intval = battery->voltage_avg * 1000;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = battery->temperature;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_NOW:
+               val->intval = battery->charging_mode;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               if (!battery->battery_valid)
+                       val->intval = FAKE_BAT_LEVEL;
+               else {
+                       if (battery->status == POWER_SUPPLY_STATUS_FULL)
+                               val->intval = 100;
+                       else
+                               val->intval = battery->capacity;
+               }
+               break;
+       default:
+               ret = -ENODATA;
+       }
+       return ret;
+}
+
+static int s2mu00x_battery_set_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               const union power_supply_propval *val)
+{
+       struct s2mu00x_battery_info *battery = power_supply_get_drvdata(psy);
+       int ret = 0;
+
+       dev_dbg(battery->dev, "prop: %d\n", psp);
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               set_battery_status(battery, val->intval);
+               break;
+       case POWER_SUPPLY_PROP_HEALTH:
+               battery->health = val->intval;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               battery->cable_type = val->intval;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+static int s2mu00x_usb_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct s2mu00x_battery_info *battery =  power_supply_get_drvdata(psy);
+
+       if (psp != POWER_SUPPLY_PROP_ONLINE)
+               return -EINVAL;
+
+       /* Set enable=1 only if the USB charger is connected */
+       switch (battery->cable_type) {
+       case POWER_SUPPLY_TYPE_USB:
+       case POWER_SUPPLY_TYPE_USB_DCP:
+       case POWER_SUPPLY_TYPE_USB_CDP:
+       case POWER_SUPPLY_TYPE_USB_ACA:
+               val->intval = 1;
+               break;
+       default:
+               val->intval = 0;
+               break;
+       }
+
+       return 0;
+}
+
+/*
+ * AC charger operations
+ */
+static int s2mu00x_ac_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct s2mu00x_battery_info *battery =  power_supply_get_drvdata(psy);
+
+       if (psp != POWER_SUPPLY_PROP_ONLINE)
+               return -EINVAL;
+
+       /* Set enable=1 only if the AC charger is connected */
+       switch (battery->cable_type) {
+       case POWER_SUPPLY_TYPE_MAINS:
+       case POWER_SUPPLY_TYPE_UNKNOWN:
+       case POWER_SUPPLY_TYPE_PREPARE_TA:
+       case POWER_SUPPLY_TYPE_HV_MAINS:
+               val->intval = 1;
+               break;
+       default:
+               val->intval = 0;
+               break;
+       }
+
+       return 0;
+}
+
+#if defined(CONFIG_MUIC_NOTIFIER) || defined(CONFIG_IFCONN_NOTIFIER)
+static int s2mu00x_bat_cable_check(struct s2mu00x_battery_info *battery,
+               muic_attached_dev_t attached_dev)
+{
+       int current_cable_type = -1;
+
+       pr_info("[%s]ATTACHED(%d)\n", __func__, attached_dev);
+
+       switch (attached_dev) {
+       case ATTACHED_DEV_SMARTDOCK_MUIC:
+       case ATTACHED_DEV_DESKDOCK_MUIC:
+               current_cable_type = POWER_SUPPLY_TYPE_BATTERY;
+               break;
+       case ATTACHED_DEV_OTG_MUIC:
+       case ATTACHED_DEV_HMT_MUIC:
+               current_cable_type = POWER_SUPPLY_TYPE_OTG;
+               break;
+       case ATTACHED_DEV_USB_MUIC:
+       case ATTACHED_DEV_SMARTDOCK_USB_MUIC:
+       case ATTACHED_DEV_UNOFFICIAL_ID_USB_MUIC:
+               current_cable_type = POWER_SUPPLY_TYPE_USB;
+               break;
+       case ATTACHED_DEV_TA_MUIC:
+       case ATTACHED_DEV_CARDOCK_MUIC:
+       case ATTACHED_DEV_DESKDOCK_VB_MUIC:
+       case ATTACHED_DEV_SMARTDOCK_TA_MUIC:
+       case ATTACHED_DEV_AFC_CHARGER_5V_MUIC:
+       case ATTACHED_DEV_UNOFFICIAL_TA_MUIC:
+       case ATTACHED_DEV_UNOFFICIAL_ID_TA_MUIC:
+       case ATTACHED_DEV_UNOFFICIAL_ID_ANY_MUIC:
+       case ATTACHED_DEV_QC_CHARGER_5V_MUIC:
+       case ATTACHED_DEV_UNSUPPORTED_ID_VB_MUIC:
+               current_cable_type = POWER_SUPPLY_TYPE_MAINS;
+               break;
+       case ATTACHED_DEV_CDP_MUIC:
+       case ATTACHED_DEV_UNOFFICIAL_ID_CDP_MUIC:
+               current_cable_type = POWER_SUPPLY_TYPE_USB_CDP;
+               break;
+       case ATTACHED_DEV_AFC_CHARGER_9V_MUIC:
+       case ATTACHED_DEV_QC_CHARGER_9V_MUIC:
+#if defined(CONFIG_HV_MUIC_S2MU004_PE)
+       case ATTACHED_DEV_PE_CHARGER_9V_MUIC:
+#endif
+               current_cable_type = POWER_SUPPLY_TYPE_HV_MAINS;
+               break;
+       case ATTACHED_DEV_UNDEFINED_CHARGING_MUIC:
+               current_cable_type = POWER_SUPPLY_TYPE_UNKNOWN;
+               break;
+#if defined(CONFIG_IFCONN_NOTIFIER)
+       case ATTACHED_DEV_UNDEFINED_RANGE_MUIC:
+               current_cable_type = POWER_SUPPLY_TYPE_BATTERY;
+               break;
+       case ATTACHED_DEV_TIMEOUT_OPEN_MUIC:
+               current_cable_type = POWER_SUPPLY_TYPE_USB;
+               break;
+       case ATTACHED_DEV_AFC_CHARGER_PREPARE_MUIC:
+       case ATTACHED_DEV_QC_CHARGER_PREPARE_MUIC:
+               current_cable_type = POWER_SUPPLY_TYPE_PREPARE_TA;
+               break;
+       case ATTACHED_DEV_AFC_CHARGER_9V_DUPLI_MUIC:
+               current_cable_type = POWER_SUPPLY_TYPE_HV_MAINS;
+               break;
+       case ATTACHED_DEV_VZW_INCOMPATIBLE_MUIC:
+               current_cable_type = POWER_SUPPLY_TYPE_UNKNOWN;
+               break;
+#endif
+       default:
+               current_cable_type = POWER_SUPPLY_TYPE_BATTERY;
+               pr_err("%s: invalid type for charger:%d\n",
+                               __func__, attached_dev);
+       }
+
+       return current_cable_type;
+}
+#endif
+
+#if defined(CONFIG_MUIC_NOTIFIER)
+static int s2mu00x_battery_handle_notification(struct notifier_block *nb,
+               unsigned long action, void *data)
+{
+       muic_attached_dev_t attached_dev = *(muic_attached_dev_t *)data;
+       const char *cmd;
+       int cable_type;
+       union power_supply_propval value;
+       struct s2mu00x_battery_info *battery =
+               container_of(nb, struct s2mu00x_battery_info, batt_nb);
+       struct power_supply *psy;
+       int ret;
+
+       if (attached_dev == ATTACHED_DEV_MHL_MUIC)
+               return 0;
+
+       switch (action) {
+       case MUIC_NOTIFY_CMD_DETACH:
+       case MUIC_NOTIFY_CMD_LOGICALLY_DETACH:
+               cmd = "DETACH";
+               cable_type = POWER_SUPPLY_TYPE_BATTERY;
+               break;
+       case MUIC_NOTIFY_CMD_ATTACH:
+       case MUIC_NOTIFY_CMD_LOGICALLY_ATTACH:
+               cmd = "ATTACH";
+               cable_type = s2mu00x_bat_cable_check(battery, attached_dev);
+               break;
+       default:
+               cmd = "ERROR";
+               cable_type = -1;
+               break;
+       }
+
+       pr_info("%s: current_cable(%d) former cable_type(%d) battery_valid(%d)\n",
+                       __func__, cable_type, battery->cable_type,
+                       battery->battery_valid);
+       if (battery->battery_valid == false)
+               pr_info("%s: Battery is disconnected\n", __func__);
+
+       battery->cable_type = cable_type;
+       pr_info("%s: CMD=%s, attached_dev=%d battery_cable=%d\n",
+                       __func__, cmd, attached_dev, battery->cable_type);
+
+
+       if (attached_dev == ATTACHED_DEV_OTG_MUIC) {
+               if (!strcmp(cmd, "ATTACH")) {
+                       value.intval = true;
+
+                       psy = power_supply_get_by_name(battery->pdata->charger_name);
+                       if (!psy)
+                               return -EINVAL;
+                       ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, &value);
+                       if (ret < 0)
+                               pr_err("%s: Fail to execute property\n", __func__);
+
+                       pr_info("%s: OTG cable attached\n", __func__);
+               } else {
+                       value.intval = false;
+
+                       psy = power_supply_get_by_name(battery->pdata->charger_name);
+                       if (!psy)
+                               return -EINVAL;
+                       ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, &value);
+                       if (ret < 0)
+                               pr_err("%s: Fail to execute property\n", __func__);
+
+                       pr_info("%s: OTG cable detached\n", __func__);
+               }
+       }
+
+       if (battery->cable_type == POWER_SUPPLY_TYPE_BATTERY ||
+                       battery->cable_type == POWER_SUPPLY_TYPE_UNKNOWN) {
+               battery->is_recharging = false;
+               set_battery_status(battery, POWER_SUPPLY_STATUS_DISCHARGING);
+       } else {
+               if (battery->cable_type == POWER_SUPPLY_TYPE_OTG) {
+                       set_battery_status(battery, POWER_SUPPLY_STATUS_DISCHARGING);
+               } else {
+                       if (battery->status != POWER_SUPPLY_STATUS_FULL)
+                               set_battery_status(battery, POWER_SUPPLY_STATUS_CHARGING);
+               }
+       }
+
+       pr_info(
+                       "%s: Status(%s), Health(%s), Cable(%d), Recharging(%d))"
+                       "\n", __func__,
+                       bat_status_str[battery->status],
+                       health_str[battery->health],
+                       battery->cable_type,
+                       battery->is_recharging
+                 );
+
+       power_supply_changed(battery->psy_battery);
+       alarm_cancel(&battery->monitor_alarm);
+       wake_lock(&battery->monitor_wake_lock);
+       queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
+       return 0;
+}
+#endif
+#if defined(CONFIG_IFCONN_NOTIFIER)
+static int s2mu00x_ifconn_handle_notification(struct notifier_block *nb,
+               unsigned long action, void *data)
+{
+       struct s2mu00x_battery_info *battery =
+                       container_of(nb, struct s2mu00x_battery_info, ifconn_nb);
+       struct ifconn_notifier_template *ifconn_info = (struct ifconn_notifier_template *)data;
+       muic_attached_dev_t attached_dev = (muic_attached_dev_t)ifconn_info->event;
+       const char *cmd;
+       int cable_type;
+       union power_supply_propval value;
+       struct power_supply *psy;
+       int ret;
+
+       dev_info(battery->dev, "%s: action (%ld) dump(0x%01x, 0x%01x, 0x%02x, 0x%04x, 0x%04x, 0x%04x, 0x%04x)\n",
+               __func__, action, ifconn_info->src, ifconn_info->dest, ifconn_info->id,
+               ifconn_info->attach, ifconn_info->rprd, ifconn_info->cable_type, ifconn_info->event);
+       ifconn_info->cable_type = (muic_attached_dev_t)ifconn_info->event;
+
+       action = ifconn_info->id;
+
+       if (attached_dev == ATTACHED_DEV_MHL_MUIC)
+               return 0;
+
+       switch (action) {
+       case IFCONN_NOTIFY_ID_DETACH:
+               cmd = "DETACH";
+               cable_type = POWER_SUPPLY_TYPE_BATTERY;
+               break;
+       case IFCONN_NOTIFY_ID_ATTACH:
+               cmd = "ATTACH";
+               cable_type = s2mu00x_bat_cable_check(battery, attached_dev);
+               break;
+       default:
+               cmd = "ERROR";
+               cable_type = -1;
+               break;
+       }
+
+       pr_info("%s: CMD[%s] attached_dev(%d) current_cable(%d) former cable_type(%d) battery_valid(%d)\n",
+                       __func__, cmd,  attached_dev, cable_type,
+                       battery->cable_type, battery->battery_valid);
+
+       if (battery->battery_valid == false)
+               pr_info("%s: Battery is disconnected\n", __func__);
+
+       battery->cable_type = cable_type;
+
+       if (attached_dev == ATTACHED_DEV_OTG_MUIC) {
+               if (!strcmp(cmd, "ATTACH")) {
+                       value.intval = true;
+
+                       psy = power_supply_get_by_name(battery->pdata->charger_name);
+                       if (!psy)
+                               return -EINVAL;
+                       ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, &value);
+                       if (ret < 0)
+                               pr_err("%s: Fail to execute property\n", __func__);
+
+                       pr_info("%s: OTG cable attached\n", __func__);
+               } else {
+                       value.intval = false;
+
+                       psy = power_supply_get_by_name(battery->pdata->charger_name);
+                       if (!psy)
+                               return -EINVAL;
+                       ret = power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_OTG_CONTROL, &value);
+                       if (ret < 0)
+                               pr_err("%s: Fail to execute property\n", __func__);
+
+                       pr_info("%s: OTG cable detached\n", __func__);
+               }
+       }
+       set_bat_status_by_cable(battery);
+
+       pr_info("%s: Status(%s), Health(%s), Cable(%d), Recharging(%d)\n",
+                       __func__, bat_status_str[battery->status], health_str[battery->health],
+                       battery->cable_type, battery->is_recharging);
+
+       power_supply_changed(battery->psy_battery);
+       alarm_cancel(&battery->monitor_alarm);
+       wake_lock(&battery->monitor_wake_lock);
+       queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
+       return 0;
+}
+#endif
+
+static void get_battery_capacity(struct s2mu00x_battery_info *battery)
+{
+
+       union power_supply_propval value;
+       struct power_supply *psy;
+       int ret;
+       unsigned int raw_soc = 0;
+
+       psy = power_supply_get_by_name(battery->pdata->fuelgauge_name);
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CAPACITY, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+       raw_soc = value.intval;
+
+       if (battery->status == POWER_SUPPLY_STATUS_FULL)
+               battery->max_rawsoc = raw_soc;
+
+       battery->capacity = (raw_soc*100)/battery->max_rawsoc;
+       if (battery->capacity > 100)
+               battery->capacity = 100;
+
+       dev_info(battery->dev, "%s: SOC(%u), rawsoc(%d), max_rawsoc(%u).\n",
+               __func__, battery->capacity, raw_soc, battery->max_rawsoc);
+}
+
+static int get_battery_info(struct s2mu00x_battery_info *battery)
+{
+       union power_supply_propval value;
+       struct power_supply *psy;
+       int ret;
+
+       /*Get fuelgauge psy*/
+       psy = power_supply_get_by_name(battery->pdata->fuelgauge_name);
+       if (!psy)
+               return -EINVAL;
+
+       /* Get voltage and current value */
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+       battery->voltage_now = value.intval;
+
+       value.intval = S2MU00X_BATTERY_VOLTAGE_AVERAGE;
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_AVG, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+       battery->voltage_avg = value.intval;
+
+       value.intval = S2MU00X_BATTERY_CURRENT_MA;
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_NOW, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+       battery->current_now = value.intval;
+
+       value.intval = S2MU00X_BATTERY_CURRENT_MA;
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_AVG, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+       battery->current_avg = value.intval;
+
+       /* Get temperature info */
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+       battery->temperature = value.intval;
+
+       get_battery_capacity(battery);
+
+       /*Get charger psy*/
+       psy = power_supply_get_by_name(battery->pdata->charger_name);
+       if (!psy)
+               return -EINVAL;
+
+       /* Get input current limit */
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_MAX, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+       battery->current_max = value.intval;
+
+       /* Get charger status*/
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+
+       if (battery->status != value.intval)
+               pr_err("%s: battery status = %d, charger status = %d\n",
+                               __func__, battery->status, value.intval);
+
+       dev_info(battery->dev,
+                       "%s:Vnow(%dmV),Inow(%dmA),Imax(%dmA),SOC(%d%%),Tbat(%d)"
+                       "\n", __func__,
+                       battery->voltage_now, battery->current_now,
+                       battery->current_max, battery->capacity,
+                       battery->temperature
+                       );
+       dev_dbg(battery->dev,
+                       "%s,Vavg(%dmV),Vocv(%dmV),Iavg(%dmA)\n",
+                       battery->battery_valid ? "Connected" : "Disconnected",
+                       battery->voltage_avg, battery->voltage_ocv, battery->current_avg);
+
+       return 0;
+}
+
+static int get_battery_health(struct s2mu00x_battery_info *battery)
+{
+       union power_supply_propval value;
+       int health = POWER_SUPPLY_HEALTH_UNKNOWN;
+       struct power_supply *psy;
+       int ret;
+
+       /* Get health status from charger */
+       psy = power_supply_get_by_name(battery->pdata->charger_name);
+       if (!psy)
+               return -EINVAL;
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_HEALTH, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+
+       health = value.intval;
+
+       return health;
+}
+
+static int get_temperature_health(struct s2mu00x_battery_info *battery)
+{
+       int health = POWER_SUPPLY_HEALTH_UNKNOWN;
+
+       switch (battery->health) {
+       case POWER_SUPPLY_HEALTH_OVERHEAT:
+               if (battery->temperature < battery->temp_high_recovery)
+                       health = POWER_SUPPLY_HEALTH_GOOD;
+               else
+                       health = POWER_SUPPLY_HEALTH_OVERHEAT;
+               break;
+       case POWER_SUPPLY_HEALTH_COLD:
+               if (battery->temperature > battery->temp_low_recovery)
+                       health = POWER_SUPPLY_HEALTH_GOOD;
+               else
+                       health = POWER_SUPPLY_HEALTH_COLD;
+               break;
+       case POWER_SUPPLY_HEALTH_GOOD:
+       default:
+               if (battery->temperature > battery->temp_high)
+                       health = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else if (battery->temperature < battery->temp_low)
+                       health = POWER_SUPPLY_HEALTH_COLD;
+               else
+                       health = POWER_SUPPLY_HEALTH_GOOD;
+               break;
+       }
+
+       /* For test, Temperature health is always good*/
+       health = POWER_SUPPLY_HEALTH_GOOD;
+
+       return health;
+}
+
+static void check_health(struct s2mu00x_battery_info *battery)
+{
+       int battery_health = 0;
+       int temperature_health = 0;
+
+       battery_health = get_battery_health(battery);
+       temperature_health = get_temperature_health(battery);
+
+       pr_info("%s: T = %d, bat_health(%s), T_health(%s), Charging(%s)\n",
+               __func__, battery->temperature, health_str[battery_health],
+               health_str[temperature_health], bat_status_str[battery->status]);
+
+       /* If battery & temperature both are normal,                     *
+        *      set battery->health GOOD and recover battery->status */
+       if (battery_health == POWER_SUPPLY_HEALTH_GOOD &&
+               temperature_health == POWER_SUPPLY_HEALTH_GOOD) {
+               battery->health = POWER_SUPPLY_HEALTH_GOOD;
+               if (battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING)
+                       set_bat_status_by_cable(battery);
+               return;
+       }
+
+       switch (battery_health) {
+       case POWER_SUPPLY_HEALTH_OVERVOLTAGE:
+       case POWER_SUPPLY_HEALTH_UNDERVOLTAGE:
+       case POWER_SUPPLY_HEALTH_UNKNOWN:
+               battery->health = battery_health;
+               goto abnormal_health;
+       default:
+               break;
+       }
+       switch (temperature_health) {
+       case POWER_SUPPLY_HEALTH_OVERHEAT:
+       case POWER_SUPPLY_HEALTH_COLD:
+       case POWER_SUPPLY_HEALTH_UNKNOWN:
+               battery->health = temperature_health;
+               goto abnormal_health;
+       default:
+               break;
+       }
+
+       pr_err("%s: Abnormal case of temperature & battery health.\n", __func__);
+       return;
+
+abnormal_health:
+       if (battery->status != POWER_SUPPLY_STATUS_NOT_CHARGING) {
+               battery->is_recharging = false;
+               /* Take the wakelock during 10 seconds  *
+                * when not_charging status is detected */
+               wake_lock_timeout(&battery->vbus_wake_lock, HZ * 10);
+               set_battery_status(battery, POWER_SUPPLY_STATUS_NOT_CHARGING);
+       }
+}
+
+static void check_charging_full(
+               struct s2mu00x_battery_info *battery)
+{
+       pr_info("%s Start\n", __func__);
+
+       if ((battery->status == POWER_SUPPLY_STATUS_DISCHARGING) ||
+                       (battery->status == POWER_SUPPLY_STATUS_NOT_CHARGING)) {
+               dev_dbg(battery->dev,
+                               "%s: No Need to Check Full-Charged\n", __func__);
+               return;
+       }
+
+       /* 1. Recharging check */
+       if (battery->status == POWER_SUPPLY_STATUS_FULL &&
+                       battery->voltage_now < battery->pdata->chg_recharge_vcell &&
+                       !battery->is_recharging) {
+               pr_info("%s: Recharging start\n", __func__);
+               set_battery_status(battery, POWER_SUPPLY_STATUS_CHARGING);
+               battery->is_recharging = true;
+       }
+
+       /* 2. Full charged check */
+       if ((battery->current_now > 0 && battery->current_now <
+                               battery->pdata->charging_current[
+                               battery->cable_type].full_check_current) &&
+                       (battery->voltage_avg > battery->pdata->chg_full_vcell)) {
+               battery->full_check_cnt++;
+               pr_info("%s: Full Check Cnt (%d)\n", __func__, battery->full_check_cnt);
+       } else if (battery->full_check_cnt != 0) {
+       /* Reset full check cnt when it is out of full condition */
+               battery->full_check_cnt = 0;
+               pr_info("%s: Reset Full Check Cnt\n", __func__);
+       }
+
+       /* 3. If full charged, turn off charging. */
+       if (battery->full_check_cnt >= battery->pdata->full_check_count) {
+               battery->full_check_cnt = 0;
+               battery->is_recharging = false;
+               set_battery_status(battery, POWER_SUPPLY_STATUS_FULL);
+               pr_info("%s: Full charged, charger off\n", __func__);
+       }
+}
+
+static void bat_monitor_work(struct work_struct *work)
+{
+       struct s2mu00x_battery_info *battery =
+               container_of(work, struct s2mu00x_battery_info, monitor_work.work);
+       union power_supply_propval value;
+       struct power_supply *psy;
+       int ret;
+
+       pr_info("%s: start monitoring\n", __func__);
+
+       psy = power_supply_get_by_name(battery->pdata->charger_name);
+       if (!psy)
+               return;
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+
+       if (!value.intval) {
+               battery->battery_valid = false;
+               pr_info("%s: There is no battery, skip monitoring.\n", __func__);
+               goto continue_monitor;
+       } else
+               battery->battery_valid = true;
+
+       get_battery_info(battery);
+
+       check_health(battery);
+
+       check_charging_full(battery);
+
+       power_supply_changed(battery->psy_battery);
+
+continue_monitor:
+       pr_err(
+                "%s: Status(%s), Health(%s), Cable(%d), Recharging(%d))"
+                "\n", __func__,
+                bat_status_str[battery->status],
+                health_str[battery->health],
+                battery->cable_type,
+                battery->is_recharging
+                );
+
+       alarm_cancel(&battery->monitor_alarm);
+       alarm_start_relative(&battery->monitor_alarm, ktime_set(battery->monitor_alarm_interval, 0));
+       wake_unlock(&battery->monitor_wake_lock);
+}
+
+#ifdef CONFIG_OF
+static int s2mu00x_battery_parse_dt(struct device *dev,
+               struct s2mu00x_battery_info *battery)
+{
+       struct device_node *np = of_find_node_by_name(NULL, "battery");
+       s2mu00x_battery_platform_data_t *pdata = battery->pdata;
+       int ret = 0, len;
+       unsigned int i;
+       const u32 *p;
+       u32 temp;
+       u32 default_input_current, default_charging_current, default_full_check_current;
+
+       if (!np) {
+               pr_info("%s np NULL(battery)\n", __func__);
+               return -1;
+       }
+       ret = of_property_read_string(np,
+                       "battery,vendor", (char const **)&pdata->vendor);
+       if (ret)
+               pr_info("%s: Vendor is empty\n", __func__);
+
+       ret = of_property_read_string(np,
+                       "battery,charger_name", (char const **)&pdata->charger_name);
+       if (ret)
+               pr_info("%s: Charger name is empty\n", __func__);
+
+       ret = of_property_read_string(np,
+                       "battery,fuelgauge_name", (char const **)&pdata->fuelgauge_name);
+       if (ret)
+               pr_info("%s: Fuelgauge name is empty\n", __func__);
+
+       ret = of_property_read_u32(np, "battery,technology",
+                       &pdata->technology);
+       if (ret)
+               pr_info("%s : technology is empty\n", __func__);
+
+       p = of_get_property(np, "battery,input_current_limit", &len);
+       if (!p)
+               return 1;
+
+       len = len / sizeof(u32);
+
+       if (len < POWER_SUPPLY_TYPE_END)
+               len = POWER_SUPPLY_TYPE_END;
+
+       pdata->charging_current = kzalloc(sizeof(s2mu00x_charging_current_t) * len,
+                       GFP_KERNEL);
+
+       ret = of_property_read_u32(np, "battery,default_input_current",
+                       &default_input_current);
+       if (ret)
+               pr_info("%s : default_input_current is empty\n", __func__);
+
+       ret = of_property_read_u32(np, "battery,default_charging_current",
+                       &default_charging_current);
+       if (ret)
+               pr_info("%s : default_charging_current is empty\n", __func__);
+
+       ret = of_property_read_u32(np, "battery,default_full_check_current",
+                       &default_full_check_current);
+       if (ret)
+               pr_info("%s : default_full_check_current is empty\n", __func__);
+
+       for (i = 0; i < len; i++) {
+               ret = of_property_read_u32_index(np,
+                               "battery,input_current_limit", i,
+                               &pdata->charging_current[i].input_current_limit);
+               if (ret) {
+                       pr_info("%s : Input_current_limit is empty\n",
+                                       __func__);
+                       pdata->charging_current[i].input_current_limit = default_input_current;
+               }
+
+               ret = of_property_read_u32_index(np,
+                               "battery,fast_charging_current", i,
+                               &pdata->charging_current[i].fast_charging_current);
+               if (ret) {
+                       pr_info("%s : Fast charging current is empty\n",
+                                       __func__);
+                       pdata->charging_current[i].fast_charging_current = default_charging_current;
+               }
+
+               ret = of_property_read_u32_index(np,
+                               "battery,full_check_current", i,
+                               &pdata->charging_current[i].full_check_current);
+               if (ret) {
+                       pr_info("%s : Full check current is empty\n",
+                                       __func__);
+                       pdata->charging_current[i].full_check_current = default_full_check_current;
+               }
+       }
+
+       ret = of_property_read_u32(np, "battery,max_input_current",
+                       &pdata->max_input_current);
+       if (ret)
+               pr_info("%s : max_input_current is empty\n", __func__);
+
+       ret = of_property_read_u32(np, "battery,max_charging_current",
+                       &pdata->max_charging_current);
+       if (ret)
+               pr_info("%s : max_charging_current is empty\n", __func__);
+
+       ret = of_property_read_u32(np, "battery,temp_high", &temp);
+       if (ret) {
+               pr_info("%s : temp_high is empty\n", __func__);
+               pdata->temp_high = 500;
+       } else
+               pdata->temp_high = (int)temp;
+
+       ret = of_property_read_u32(np, "battery,temp_high_recovery", &temp);
+       if (ret) {
+               pr_info("%s : temp_high_recovery is empty\n", __func__);
+               pdata->temp_high_recovery = pdata->temp_high - 50;
+       } else
+               pdata->temp_high_recovery = (int)temp;
+
+       ret = of_property_read_u32(np, "battery,temp_low", &temp);
+       if (ret) {
+               pr_info("%s : temp_low is empty\n", __func__);
+               pdata->temp_low = 100;
+       } else
+               pdata->temp_low = (int)temp;
+
+       ret = of_property_read_u32(np, "battery,temp_low_recovery", &temp);
+       if (ret) {
+               pr_info("%s : temp_low_recovery is empty\n", __func__);
+               pdata->temp_low_recovery = pdata->temp_low + 50;
+       } else
+               pdata->temp_low_recovery = (int)temp;
+
+       pr_info("%s : temp_high(%d), temp_high_recovery(%d), temp_low(%d), temp_low_recovery(%d)\n",
+                       __func__,
+                       pdata->temp_high, pdata->temp_high_recovery,
+                       pdata->temp_low, pdata->temp_low_recovery);
+
+       ret = of_property_read_u32(np, "battery,full_check_count",
+                       &pdata->full_check_count);
+       if (ret)
+               pr_info("%s : full_check_count is empty\n", __func__);
+
+       ret = of_property_read_u32(np, "battery,chg_full_vcell",
+                       &pdata->chg_full_vcell);
+       if (ret)
+               pr_info("%s : chg_full_vcell is empty\n", __func__);
+
+       ret = of_property_read_u32(np, "battery,chg_recharge_vcell",
+                       &pdata->chg_recharge_vcell);
+       if (ret)
+               pr_info("%s : chg_recharge_vcell is empty\n", __func__);
+
+       ret = of_property_read_u32(np, "battery,max_rawsoc",
+                       &pdata->max_rawsoc);
+       if (ret)
+               pr_info("%s : max_rawsoc is empty\n", __func__);
+
+       pr_info("%s:DT parsing is done, vendor : %s, technology : %d\n",
+                       __func__, pdata->vendor, pdata->technology);
+       return ret;
+}
+#else
+static int s2mu00x_battery_parse_dt(struct device *dev,
+               struct s2mu00x_battery_platform_data *pdata)
+{
+       return pdev->dev.platform_data;
+}
+#endif
+
+static const struct of_device_id s2mu00x_battery_match_table[] = {
+       { .compatible = "samsung,s2mu00x-battery",},
+       {},
+};
+
+static enum alarmtimer_restart bat_monitor_alarm(
+       struct alarm *alarm, ktime_t now)
+{
+       struct s2mu00x_battery_info *battery = container_of(alarm,
+                               struct s2mu00x_battery_info, monitor_alarm);
+
+       wake_lock(&battery->monitor_wake_lock);
+       queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
+
+       return ALARMTIMER_NORESTART;
+}
+
+static int s2mu00x_battery_probe(struct platform_device *pdev)
+{
+       struct s2mu00x_battery_info *battery;
+       struct power_supply_config psy_cfg = {};
+       union power_supply_propval value;
+       int ret = 0, temp = 0;
+       struct power_supply *psy;
+#ifndef CONFIG_OF
+       int i;
+#endif
+
+       pr_info("%s: S2MU00x battery driver loading\n", __func__);
+
+       /* Allocate necessary device data structures */
+       battery = kzalloc(sizeof(*battery), GFP_KERNEL);
+       if (!battery)
+               return -ENOMEM;
+
+       pr_info("%s: battery is allocated\n", __func__);
+
+       battery->pdata = devm_kzalloc(&pdev->dev, sizeof(*(battery->pdata)),
+                       GFP_KERNEL);
+       if (!battery->pdata) {
+               ret = -ENOMEM;
+               goto err_bat_free;
+       }
+
+       pr_info("%s: pdata is allocated\n", __func__);
+
+       /* Get device/board dependent configuration data from DT */
+       temp = s2mu00x_battery_parse_dt(&pdev->dev, battery);
+       if (temp) {
+               pr_info("%s: s2mu00x_battery_parse_dt(&pdev->dev, battery) == %d\n", __func__, temp);
+               dev_err(&pdev->dev, "%s: Failed to get battery dt\n", __func__);
+               ret = -EINVAL;
+               goto err_parse_dt_nomem;
+       }
+
+       pr_info("%s: DT parsing is done\n", __func__);
+
+       /* Set driver data */
+       platform_set_drvdata(pdev, battery);
+       battery->dev = &pdev->dev;
+
+       mutex_init(&battery->iolock);
+
+       wake_lock_init(&battery->monitor_wake_lock, WAKE_LOCK_SUSPEND,
+                       "sec-battery-monitor");
+       wake_lock_init(&battery->vbus_wake_lock, WAKE_LOCK_SUSPEND,
+                       "sec-battery-vbus");
+
+       /* Inintialization of battery information */
+       battery->status = POWER_SUPPLY_STATUS_DISCHARGING;
+       battery->health = POWER_SUPPLY_HEALTH_GOOD;
+
+       battery->input_current = 0;
+       battery->charging_current = 0;
+       battery->topoff_current = 0;
+
+       battery->max_input_current = battery->pdata->max_input_current;
+       battery->max_charging_current = battery->pdata->max_charging_current;
+
+       battery->temp_high = battery->pdata->temp_high;
+       battery->temp_high_recovery = battery->pdata->temp_high_recovery;
+       battery->temp_low = battery->pdata->temp_low;
+       battery->temp_low_recovery = battery->pdata->temp_low_recovery;
+
+       battery->max_rawsoc = battery->pdata->max_rawsoc;
+
+       battery->is_recharging = false;
+       battery->cable_type = POWER_SUPPLY_TYPE_BATTERY;
+
+       psy = power_supply_get_by_name(battery->pdata->charger_name);
+       if (!psy)
+               return -EINVAL;
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+
+       if (!value.intval)
+               battery->battery_valid = false;
+       else
+               battery->battery_valid = true;
+
+       /* Register battery as "POWER_SUPPLY_TYPE_BATTERY" */
+       battery->psy_battery_desc.name = "battery";
+       battery->psy_battery_desc.type = POWER_SUPPLY_TYPE_BATTERY;
+       battery->psy_battery_desc.get_property =  s2mu00x_battery_get_property;
+       battery->psy_battery_desc.set_property =  s2mu00x_battery_set_property;
+       battery->psy_battery_desc.properties = s2mu00x_battery_props;
+       battery->psy_battery_desc.num_properties =  ARRAY_SIZE(s2mu00x_battery_props);
+
+       battery->psy_usb_desc.name = "usb";
+       battery->psy_usb_desc.type = POWER_SUPPLY_TYPE_USB;
+       battery->psy_usb_desc.get_property = s2mu00x_usb_get_property;
+       battery->psy_usb_desc.properties = s2mu00x_power_props;
+       battery->psy_usb_desc.num_properties = ARRAY_SIZE(s2mu00x_power_props);
+
+       battery->psy_ac_desc.name = "ac";
+       battery->psy_ac_desc.type = POWER_SUPPLY_TYPE_MAINS;
+       battery->psy_ac_desc.properties = s2mu00x_power_props;
+       battery->psy_ac_desc.num_properties = ARRAY_SIZE(s2mu00x_power_props);
+       battery->psy_ac_desc.get_property = s2mu00x_ac_get_property;
+
+       /* Initialize work queue for periodic polling thread */
+       battery->monitor_wqueue =
+               create_singlethread_workqueue(dev_name(&pdev->dev));
+       if (!battery->monitor_wqueue) {
+               dev_err(battery->dev,
+                               "%s: Fail to Create Workqueue\n", __func__);
+               goto err_irr;
+       }
+
+       /* Init work & alarm for monitoring */
+       INIT_DELAYED_WORK(&battery->monitor_work, bat_monitor_work);
+       alarm_init(&battery->monitor_alarm, ALARM_BOOTTIME, bat_monitor_alarm);
+       battery->monitor_alarm_interval = DEFAULT_ALARM_INTERVAL;
+
+       /* Register power supply to framework */
+       psy_cfg.drv_data = battery;
+       psy_cfg.supplied_to = s2mu00x_supplied_to;
+       psy_cfg.num_supplicants = ARRAY_SIZE(s2mu00x_supplied_to);
+
+       battery->psy_battery = power_supply_register(&pdev->dev, &battery->psy_battery_desc, &psy_cfg);
+       if (IS_ERR(battery->psy_battery)) {
+               pr_err("%s: Failed to Register psy_battery\n", __func__);
+               ret = PTR_ERR(battery->psy_battery);
+               goto err_workqueue;
+       }
+       pr_info("%s: Registered battery as power supply\n", __func__);
+
+       battery->psy_usb = power_supply_register(&pdev->dev, &battery->psy_usb_desc, &psy_cfg);
+       if (IS_ERR(battery->psy_usb)) {
+               pr_err("%s: Failed to Register psy_usb\n", __func__);
+               ret = PTR_ERR(battery->psy_usb);
+               goto err_unreg_battery;
+       }
+       pr_info("%s: Registered USB as power supply\n", __func__);
+
+       battery->psy_ac = power_supply_register(&pdev->dev, &battery->psy_ac_desc, &psy_cfg);
+       if (IS_ERR(battery->psy_ac)) {
+               pr_err("%s: Failed to Register psy_ac\n", __func__);
+               ret = PTR_ERR(battery->psy_ac);
+               goto err_unreg_usb;
+       }
+       pr_info("%s: Registered AC as power supply\n", __func__);
+
+       /* Initialize battery level*/
+       value.intval = 0;
+
+       psy = power_supply_get_by_name(battery->pdata->fuelgauge_name);
+       if (!psy)
+               return -EINVAL;
+       ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CAPACITY, &value);
+       if (ret < 0)
+               pr_err("%s: Fail to execute property\n", __func__);
+
+       battery->capacity = value.intval;
+
+#if defined(CONFIG_IFCONN_NOTIFIER)
+       ifconn_notifier_register(&battery->ifconn_nb,
+                       s2mu00x_ifconn_handle_notification,
+                       IFCONN_NOTIFY_BATTERY,
+                       IFCONN_NOTIFY_MUIC);
+#elif defined(CONFIG_MUIC_NOTIFIER)
+       pr_info("%s: Register MUIC notifier\n", __func__);
+       muic_notifier_register(&battery->batt_nb, s2mu00x_battery_handle_notification,
+                       MUIC_NOTIFY_DEV_CHARGER);
+#endif
+
+       /* Kick off monitoring thread */
+       pr_info("%s: start battery monitoring work\n", __func__);
+       queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 5*HZ);
+
+       dev_info(battery->dev, "%s: Battery driver is loaded\n", __func__);
+       return 0;
+
+err_unreg_usb:
+       power_supply_unregister(battery->psy_usb);
+err_unreg_battery:
+       power_supply_unregister(battery->psy_battery);
+err_workqueue:
+       destroy_workqueue(battery->monitor_wqueue);
+err_irr:
+       wake_lock_destroy(&battery->monitor_wake_lock);
+       wake_lock_destroy(&battery->vbus_wake_lock);
+       mutex_destroy(&battery->iolock);
+err_parse_dt_nomem:
+       kfree(battery->pdata);
+err_bat_free:
+       kfree(battery);
+
+       return ret;
+}
+
+static int s2mu00x_battery_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+#if defined CONFIG_PM
+static int s2mu00x_battery_prepare(struct device *dev)
+{
+       struct s2mu00x_battery_info *battery = dev_get_drvdata(dev);
+
+       alarm_cancel(&battery->monitor_alarm);
+       wake_unlock(&battery->monitor_wake_lock);
+       /* If charger is connected, monitoring is required*/
+       if (battery->cable_type != POWER_SUPPLY_TYPE_BATTERY) {
+               battery->monitor_alarm_interval = SLEEP_ALARM_INTERVAL;
+               pr_info("%s: Increase battery monitoring interval -> %d\n",
+                               __func__, battery->monitor_alarm_interval);
+               alarm_start_relative(&battery->monitor_alarm,
+                               ktime_set(battery->monitor_alarm_interval, 0));
+       }
+       return 0;
+}
+
+static int s2mu00x_battery_suspend(struct device *dev)
+{
+       return 0;
+}
+
+static int s2mu00x_battery_resume(struct device *dev)
+{
+       return 0;
+}
+
+static void s2mu00x_battery_complete(struct device *dev)
+{
+       struct s2mu00x_battery_info *battery = dev_get_drvdata(dev);
+
+       if (battery->monitor_alarm_interval != DEFAULT_ALARM_INTERVAL) {
+               battery->monitor_alarm_interval = DEFAULT_ALARM_INTERVAL;
+               pr_info("%s: Recover battery monitoring interval -> %d\n",
+                       __func__, battery->monitor_alarm_interval);
+       }
+       alarm_cancel(&battery->monitor_alarm);
+       wake_lock(&battery->monitor_wake_lock);
+       queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
+}
+
+#else
+#define s2mu00x_battery_prepare NULL
+#define s2mu00x_battery_suspend NULL
+#define s2mu00x_battery_resume NULL
+#define s2mu00x_battery_complete NULL
+#endif
+
+static const struct dev_pm_ops s2mu00x_battery_pm_ops = {
+       .prepare = s2mu00x_battery_prepare,
+       .suspend = s2mu00x_battery_suspend,
+       .resume = s2mu00x_battery_resume,
+       .complete = s2mu00x_battery_complete,
+};
+
+static struct platform_driver s2mu00x_battery_driver = {
+       .driver         = {
+               .name   = "s2mu00x-battery",
+               .owner  = THIS_MODULE,
+               .pm     = &s2mu00x_battery_pm_ops,
+               .of_match_table = s2mu00x_battery_match_table,
+       },
+       .probe          = s2mu00x_battery_probe,
+       .remove     = s2mu00x_battery_remove,
+};
+
+static int __init s2mu00x_battery_init(void)
+{
+       int ret = 0;
+
+       ret = platform_driver_register(&s2mu00x_battery_driver);
+       return ret;
+}
+late_initcall(s2mu00x_battery_init);
+
+static void __exit s2mu00x_battery_exit(void)
+{
+       platform_driver_unregister(&s2mu00x_battery_driver);
+}
+module_exit(s2mu00x_battery_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Samsung Electronics");
+MODULE_DESCRIPTION("Battery driver for S2MU00x");
diff --git a/include/linux/power/s2mu00x_battery.h b/include/linux/power/s2mu00x_battery.h
new file mode 100644 (file)
index 0000000..fbe4623
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * s2mu00x_battery.h
+ *
+ * Copyright (C) 2017 Samsung Electronics, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __S2MU00X_BATTERY_H
+#define __S2MU00X_BATTERY_H
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/i2c.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/power_supply.h>
+
+enum s2mu00x_battery_voltage_mode {
+       S2MU00X_BATTERY_VOLTAGE_AVERAGE = 0,
+       S2MU00X_BATTERY_VOLTAGE_OCV,
+};
+
+enum s2mu00x_battery_current_mode {
+       S2MU00X_BATTERY_CURRENT_UA = 0,
+       S2MU00X_BATTERY_CURRENT_MA,
+};
+
+enum s2mu00x_battery_charger_mode {
+       S2MU00X_BAT_CHG_MODE_CHARGING = 0,
+       S2MU00X_BAT_CHG_MODE_CHARGING_OFF,
+       S2MU00X_BAT_CHG_MODE_BUCK_OFF,
+};
+
+struct s2mu00x_charging_current {
+#ifdef CONFIG_OF
+       unsigned int input_current_limit;
+       unsigned int fast_charging_current;
+       unsigned int full_check_current;
+#else
+       int input_current_limit;
+       int fast_charging_current;
+       int full_check_current;
+#endif
+};
+
+#define s2mu00x_charging_current_t struct s2mu00x_charging_current
+
+#define S2MU00X_FUELGAUGE_CAPACITY_TYPE_RESET   (-1)
+
+#endif /* __S2MU00X_BATTERY_H */