kernel: samsung moto charger feature
authorxuwei9 <xuwei9@lenovo.com>
Fri, 31 Aug 2018 08:15:16 +0000 (16:15 +0800)
committerCosmin Tanislav <demonsingur@gmail.com>
Mon, 22 Apr 2024 17:23:49 +0000 (20:23 +0300)
add moto charge feature
to suamsung platform.

Change-Id: I0bde27594291cc794bba155a597c6d83eebdeb43
Signed-off-by: xuwei9 <xuwei9@mt.com>
Reviewed-on: https://gerrit.mot.com/1246419
SLTApproved: Slta Waiver
SME-Granted: SME Approvals Granted
Tested-by: Jira Key
Reviewed-by: Xiangpo Zhao <zhaoxp3@motorola.com>
Submit-Approved: Jira Key

drivers/power/supply/s2mu00x_battery.c
drivers/power/supply/s2mu106_charger.c
drivers/power/supply/s2mu106_fuelgauge.c
include/linux/power/s2mu106_charger.h
include/linux/power/s2mu106_fuelgauge.h
include/linux/power_supply.h

index 7bfd0b9615fb2a29cce98d9cdbf86ccab186bb90..a5eaaa957b97e15f10b8ebd759447ffb3b14c0a7 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/power/s2mu00x_battery.h>
 #include <linux/power/s2mu106_pmeter.h>
 #include <linux/alarmtimer.h>
-
+#include <linux/reboot.h>
 #if defined(CONFIG_MUIC_NOTIFIER)
 #include <linux/muic/s2mu004-muic-notifier.h>
 #include <linux/muic/muic.h>
@@ -82,12 +82,74 @@ static enum power_supply_property s2mu00x_battery_props[] = {
        POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION,
        POWER_SUPPLY_PROP_CALIBRATE,
        POWER_SUPPLY_PROP_SOH,
+       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT,
+       POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX,
+       POWER_SUPPLY_PROP_CHARGING_ENABLED,
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_TECHNOLOGY,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
+       POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
 };
 
 static enum power_supply_property s2mu00x_power_props[] = {
        POWER_SUPPLY_PROP_ONLINE,
 };
 
+//moto
+struct pchg_current_map {
+       int requested;
+       int primary;
+       int secondary;
+};
+
+enum stepchg_state {
+       STEP_MAX,
+       STEP_ONE,
+       STEP_TAPER,
+       STEP_FULL,
+       STEP_NONE = 0xFF,
+};
+
+enum charging_limit_modes {
+       CHARGING_LIMIT_OFF,
+       CHARGING_LIMIT_RUN,
+       CHARGING_LIMIT_UNKNOWN,
+};
+
+static struct s2mu00x_battery_info *the_chip;
+static char *smb_health_text[] = {
+       "Unknown", "Good", "Overheat", "Dead", "Over voltage",
+       "Unspecified failure", "Cold", "Watchdog timer expire",
+       "Safety timer expire", "Under voltage", "Warm", "Cool", "Hot", "Slightly Cool"
+};
+
+static int smbchg_debug_mask = 0xff;
+enum print_reason {
+       PR_REGISTER     = BIT(0),
+       PR_INTERRUPT    = BIT(1),
+       PR_STATUS       = BIT(2),
+       PR_DUMP         = BIT(3),
+       PR_PM           = BIT(4),
+       PR_MISC         = BIT(5),
+       PR_WIPOWER      = BIT(6),
+};
+
+#define pr_smb(reason, fmt, ...)                               \
+       do {                                                    \
+               if (smbchg_debug_mask & (reason))               \
+                       pr_info(fmt, ##__VA_ARGS__);            \
+               else                                            \
+                       pr_debug(fmt, ##__VA_ARGS__);           \
+       } while (0)
+
+#define pr_smb_rt(reason, fmt, ...)                                    \
+       do {                                                            \
+               if (smbchg_debug_mask & (reason))                       \
+                       pr_info_ratelimited(fmt, ##__VA_ARGS__);        \
+               else                                                    \
+                       pr_debug_ratelimited(fmt, ##__VA_ARGS__);       \
+       } while (0)
+
 typedef struct s2mu00x_battery_platform_data {
        s2mu00x_charging_current_t *charging_current;
        char *charger_name;
@@ -236,10 +298,211 @@ struct s2mu00x_battery_info {
 
        int thermal_enable;
        int thermal_fast_charge_percentage;
+
+       //moto
+       struct wake_lock heartbeat_wake_lock;
+       bool                            test_mode;
+       int                             test_mode_soc;
+       int                             test_mode_temp;
+       u8                              revision[4];
+
+       /* configuration parameters */
+       int                             iterm_ma;
+       int                             usb_max_current_ma;
+       int                             dc_max_current_ma;
+       int                             usb_target_current_ma;
+       int                             target_fastchg_current_ma;
+       int                             allowed_fastchg_current_ma;
+       bool                            update_allowed_fastchg_current_ma;
+       int                             fastchg_current_ma;
+       unsigned int                    pchg_current_map_len;
+       struct pchg_current_map        *pchg_current_map_data;
+       int                             vfloat_mv;
+       int                             vfloat_parallel_mv;
+       int                             fastchg_current_comp;
+       int                             float_voltage_comp;
+       int                             resume_delta_mv;
+       int                             safety_time;
+       int                             prechg_safety_time;
+       int                             jeita_temp_hard_limit;
+       bool                            use_vfloat_adjustments;
+       bool                            iterm_disabled;
+       bool                            bmd_algo_disabled;
+       bool                            soft_vfloat_comp_disabled;
+       bool                            chg_enabled;
+       bool                            battery_unknown;
+       bool                            charge_unknown_battery;
+       bool                            chg_inhibit_en;
+       bool                            chg_inhibit_source_fg;
+       bool                            low_volt_dcin;
+       bool                            vbat_above_headroom;
+       bool                            force_aicl_rerun;
+       bool                            enable_hvdcp_9v;
+       u8                              original_usbin_allowance;
+
+       int                             usb_online;
+       bool                            dc_present;
+       bool                            usb_present;
+       bool                            batt_present;
+       int                             otg_retries;
+       ktime_t                         otg_enable_time;
+       bool                            sw_esr_pulse_en;
+       bool                            safety_timer_en;
+       bool                            usb_ov_det;
+       bool                            otg_pulse_skip_dis;
+       u32                             wa_flags;
+
+       /* jeita and temperature */
+       bool                            batt_hot;
+       bool                            batt_cold;
+       bool                            batt_warm;
+       bool                            batt_cool;
+       unsigned int                    chg_thermal_levels;
+       unsigned int                    chg_therm_lvl_sel;
+       unsigned int                    *chg_thermal_mitigation;
+
+       unsigned int                    thermal_levels;
+       unsigned int                    therm_lvl_sel;
+       unsigned int                    *thermal_mitigation;
+
+       unsigned int                    dc_thermal_levels;
+       unsigned int                    dc_therm_lvl_sel;
+       unsigned int                    *dc_thermal_mitigation;
+
+       /* irqs */
+       int                             batt_hot_irq;
+       int                             batt_warm_irq;
+       int                             batt_cool_irq;
+       int                             batt_cold_irq;
+       int                             batt_missing_irq;
+       int                             vbat_low_irq;
+       int                             chg_hot_irq;
+       int                             chg_term_irq;
+       int                             taper_irq;
+       bool                            taper_irq_enabled;
+       struct mutex                    taper_irq_lock;
+       int                             recharge_irq;
+       int                             fastchg_irq;
+       int                             safety_timeout_irq;
+#ifdef QCOM_BASE
+       int                             power_ok_irq;
+#endif
+       int                             dcin_uv_irq;
+       int                             usbin_uv_irq;
+       int                             usbin_ov_irq;
+       int                             src_detect_irq;
+       int                             otg_fail_irq;
+       int                             otg_oc_irq;
+       int                             aicl_done_irq;
+       int                             usbid_change_irq;
+       int                             chg_error_irq;
+       bool                            enable_aicl_wake;
+
+       /* psy */
+       struct power_supply             *usb_psy;
+       struct power_supply             batt_psy;
+       struct power_supply             dc_psy;
+       struct power_supply             *bms_psy;
+       struct power_supply             *charger_psy;
+       struct power_supply             *otg_psy;
+       bool                            psy_registered;
+
+       struct work_struct              usb_set_online_work;
+       struct delayed_work             vfloat_adjust_work;
+       struct delayed_work             hvdcp_det_work;
+       spinlock_t                      sec_access_lock;
+       struct mutex                    current_change_lock;
+       struct mutex                    usb_set_online_lock;
+       struct mutex                    usb_set_present_lock;
+       struct mutex                    dc_set_present_lock;
+       struct mutex                    battchg_disabled_lock;
+       struct mutex                    usb_en_lock;
+       struct mutex                    dc_en_lock;
+       struct mutex                    fcc_lock;
+       struct mutex                    pm_lock;
+       /* aicl deglitch workaround */
+       unsigned long                   first_aicl_seconds;
+       int                             aicl_irq_count;
+       bool                            factory_mode;
+       bool                            factory_cable;
+       struct delayed_work             heartbeat_work;
+       struct mutex                    check_temp_lock;
+       bool                            enable_charging_limit;
+       bool                            is_factory_image;
+       enum charging_limit_modes       charging_limit_modes;
+       int                             upper_limit_capacity;
+       int                             lower_limit_capacity;
+       int                             temp_state;
+       int                             hotspot_temp;
+       int                             hotspot_thrs_c;
+       int                             hot_temp_c;
+       int                             cold_temp_c;
+       int                             warm_temp_c;
+       int                             cool_temp_c;
+       int                             slightly_cool_temp_c;
+       int                             ext_high_temp;
+       int                             ext_temp_volt_mv;
+       int                             stepchg_voltage_mv;
+       int                             stepchg_current_ma;
+       int                             stepchg_taper_ma;
+       int                             stepchg_iterm_ma;
+       int                             stepchg_max_voltage_mv;
+       int                             stepchg_max_current_ma;
+       enum stepchg_state              stepchg_state;
+       unsigned int                    stepchg_state_holdoff;
+       struct wakeup_source            smbchg_wake_source;
+       struct delayed_work             usb_insertion_work;
+       int                             charger_rate;
+       bool                            usbid_disabled;
+       bool                            usbid_gpio_enabled;
+       int                             demo_mode;
+       bool                            batt_therm_wa;
+       struct notifier_block           smb_reboot;
+       int                             aicl_wait_retries;
+       bool                            hvdcp_det_done;
+       int                             afvc_mv;
+       enum power_supply_type          supply_type;
+       bool                            enabled_weak_charger_check;
+       bool                            adapter_vbus_collapse_flag;
+       int                             weak_charger_valid_cnt;
+       bool                            is_weak_charger;
+       ktime_t                 weak_charger_valid_cnt_ktmr;
+       ktime_t                 adapter_vbus_collapse_ktmr;
+       int                             temp_good_current_ma;
+       int                             temp_warm_current_ma;
+       int                             temp_cool_current_ma;
+       int                             temp_slightly_cool_current_ma;
+       int                             temp_allowed_fastchg_current_ma;
+       bool                            enable_factory_wa;
+       int                         max_chrg_temp;
 };
 
 static int is_charging_mode = S2MU00X_NOR_MODE;
 
+static void smbchg_stay_awake(struct s2mu00x_battery_info *chip);
+static void smbchg_relax(struct s2mu00x_battery_info *chip);
+bool is_dc_present(struct s2mu00x_battery_info *chip);
+bool is_usb_present(struct s2mu00x_battery_info *chip);
+static int smbchg_usb_en(struct s2mu00x_battery_info *chip, bool enable);
+void factory_usb_shutdown(struct s2mu00x_battery_info *chip);
+static int set_property_on_charger(struct s2mu00x_battery_info *chip,
+               enum power_supply_property prop, int val);
+static int set_property_on_otg(struct s2mu00x_battery_info *chip,
+               enum power_supply_property prop, int val);
+static int get_property_from_otg(struct s2mu00x_battery_info *chip,
+               enum power_supply_property prop, int *val);
+static int get_property_from_charger(struct s2mu00x_battery_info *chip,
+               enum power_supply_property prop, int *val);
+#if 0
+static int set_property_on_fg(struct s2mu00x_battery_info *chip,
+               enum power_supply_property prop, int val);
+#endif
+
+static int get_property_from_fg(struct s2mu00x_battery_info *chip,
+               enum power_supply_property prop, int *val);
+static int smbchg_charging_en(struct s2mu00x_battery_info *chip, bool en);
+static int smbchg_chg_system_temp_level_set(struct s2mu00x_battery_info *chip, int lvl_sel);
+static int get_prop_batt_current_now(struct s2mu00x_battery_info *chip);
 
 static char *s2mu00x_supplied_to[] = {
        "s2mu00x-battery",
@@ -273,7 +536,7 @@ static int set_charging_current(struct s2mu00x_battery_info *battery)
        struct power_supply *psy;
        int ret = 0;
 
-       pr_info("%s: cable_type(%d), current(%d, %d, %d)\n", __func__,
+       pr_info("%s: cable_type(%d), current(input:%d, fast:%d,term: %d)\n", __func__,
                        battery->cable_type, input_current, charging_current, topoff_current);
        mutex_lock(&battery->iolock);
 
@@ -395,7 +658,6 @@ out:
        return ret;
 }
 
-
 /*
  * set_charger_mode(): charger_mode must have one of following values.
  * 1. S2MU00X_BAT_CHG_MODE_CHARGING
@@ -560,7 +822,7 @@ static int set_battery_status(struct s2mu00x_battery_info *battery,
 
 static void set_bat_status_by_cable(struct s2mu00x_battery_info *battery)
 {
-       if (battery->is_factory) {
+       if (battery->is_factory){
                pr_info("%s: factory image support mode. Skip!\n", __func__);
                return;
        }
@@ -568,7 +830,7 @@ static void set_bat_status_by_cable(struct s2mu00x_battery_info *battery)
 #if defined(CONFIG_CHARGER_S2MU106)
        if (battery->cable_type == POWER_SUPPLY_TYPE_BATTERY ||
                battery->cable_type == POWER_SUPPLY_TYPE_UNKNOWN ||
-               battery->cable_type == POWER_SUPPLY_TYPE_OTG) {
+               battery->cable_type == POWER_SUPPLY_TYPE_OTG || battery->factory_mode) {
                battery->is_recharging = false;
 #if defined(CONFIG_USE_CCIC)
                battery->pdo_sel_num = 0;
@@ -619,7 +881,11 @@ static int s2mu00x_battery_get_property(struct power_supply *psy,
                val->intval = battery->voltage_avg * 1000;
                break;
        case POWER_SUPPLY_PROP_TEMP:
-               val->intval = battery->temperature;
+               if (battery->test_mode && !(battery->test_mode_temp < -350)
+                   && !(battery->test_mode_temp > 1250))
+                       val->intval = battery->test_mode_temp;
+               else
+                       val->intval = battery->temperature;
                break;
        case POWER_SUPPLY_PROP_CHARGE_TEMP:
                val->intval = battery->charge_temp;
@@ -637,9 +903,6 @@ static int s2mu00x_battery_get_property(struct power_supply *psy,
                                val->intval = battery->capacity;
                }
                break;
-       case POWER_SUPPLY_PROP_CURRENT_NOW:
-               val->intval = battery->current_now;
-               break;
        case POWER_SUPPLY_PROP_CURRENT_AVG:
                val->intval = battery->current_avg;
                break;
@@ -662,6 +925,35 @@ static int s2mu00x_battery_get_property(struct power_supply *psy,
                break;
        case POWER_SUPPLY_PROP_INPUT_VOLTAGE_REGULATION:
                val->intval = battery->vchg_voltage;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               /* Make charge rate and USB input throttling mutually
+                  exclusive for now */
+               if (battery->chg_thermal_mitigation)
+                       val->intval = battery->chg_therm_lvl_sel;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX:
+               /* Make charge rate and USB input throttling mutually
+                  exclusive for now */
+               if (battery->chg_thermal_mitigation)
+                       val->intval = battery->chg_thermal_levels;
+               break;
+       case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+               get_property_from_charger(battery, POWER_SUPPLY_PROP_CHARGING_ENABLED, &ret);
+               val->intval = ret;
+               break;
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               get_property_from_charger(battery, POWER_SUPPLY_PROP_CHARGE_TYPE, &ret);
+               val->intval = ret;
+               break;
+       case POWER_SUPPLY_PROP_TECHNOLOGY:
+               val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               val->intval = get_prop_batt_current_now(battery);
+               break;
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
+       case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+               val->intval = 3000;
                break;
        case POWER_SUPPLY_PROP_SOH:
                val->intval = battery->soh;
@@ -727,6 +1019,26 @@ static int s2mu00x_battery_set_property(struct power_supply *psy,
 
                        set_bat_status_by_cable(battery);
                }
+       case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT:
+               /* Make charge rate and USB input throttling mutually
+                  exclusive for now */
+               if (battery->chg_thermal_mitigation)
+                       smbchg_chg_system_temp_level_set(battery, val->intval);
+               break;
+       case POWER_SUPPLY_PROP_CHARGING_ENABLED:
+               smbchg_usb_en(battery, val->intval);
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               if (battery->test_mode)
+                       battery->test_mode_soc = val->intval;
+               power_supply_changed(battery->psy_battery);
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               if (battery->test_mode)
+                       battery->test_mode_temp = val->intval;
+               cancel_delayed_work(&battery->heartbeat_work);
+               schedule_delayed_work(&battery->heartbeat_work,
+                                       msecs_to_jiffies(0));
                break;
        default:
                ret = -EINVAL;
@@ -744,7 +1056,7 @@ static int s2mu00x_battery_property_is_writeable(struct power_supply *psy,
                        ret = 1;
                        break;
                default:
-                       ret = 0;
+               ret = 0;
        }
 
        return ret;
@@ -880,6 +1192,12 @@ static int s2mu00x_bat_cable_check(struct s2mu00x_battery_info *battery,
                                __func__, attached_dev);
        }
 
+       if (battery->factory_mode && (current_cable_type == POWER_SUPPLY_TYPE_USB ||
+                               current_cable_type == POWER_SUPPLY_TYPE_USB_CDP)) {
+               pr_err("SMB - Factory Kill Armed\n");
+               battery->factory_cable = true;
+       }
+
        return current_cable_type;
 }
 #endif
@@ -1169,6 +1487,8 @@ static int s2mu00x_ifconn_handle_notification(struct notifier_block *nb,
 #endif
                cmd = "DETACH";
                cable_type = POWER_SUPPLY_TYPE_BATTERY;
+               //moto
+               factory_usb_shutdown(battery);
                break;
        case IFCONN_NOTIFY_ID_ATTACH:
 #if defined(CONFIG_USE_CCIC)
@@ -1239,6 +1559,9 @@ static int s2mu00x_ifconn_handle_notification(struct notifier_block *nb,
                pr_info("%s: Battery is disconnected\n", __func__);
 
        battery->cable_type = cable_type;
+       //moot
+       battery->usb_present =  is_usb_present(battery);
+       battery->dc_present =  is_dc_present(battery);
 
 #if 0 // defined(CONFIG_USE_CCIC)
        if (cable_type == POWER_SUPPLY_TYPE_PREPARE_TA)
@@ -1288,19 +1611,33 @@ end_ifconn_handle:
        wake_lock(&battery->monitor_wake_lock);
        queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
        mutex_unlock(&battery->ifconn_lock);
+
+       if (action == IFCONN_NOTIFY_ID_DETACH) {
+               smbchg_relax(battery);
+               cancel_delayed_work_sync(&battery->heartbeat_work);
+       } else {
+               smbchg_stay_awake(battery);
+               cancel_delayed_work(&battery->heartbeat_work);
+               schedule_delayed_work(&battery->heartbeat_work, msecs_to_jiffies(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;
        int new_capacity = 0;
 
+       if (battery->test_mode && !(battery->test_mode_soc < 0)
+           && !(battery->test_mode_soc > 100)) {
+               battery->capacity = battery->test_mode_soc;
+               return ;
+       }
+
        psy = power_supply_get_by_name(battery->pdata->fuelgauge_name);
        ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CAPACITY, &value);
        if (ret < 0)
@@ -1676,9 +2013,297 @@ continue_monitor:
        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
+#define OF_PROP_READ(chip, prop, dt_property, retval, optional)                \
+do {                                                                   \
+       retval = of_property_read_u32(chip,             \
+                                       "moto," dt_property,            \
+                                       &prop);                         \
+                                                                       \
+       if (retval)                                             \
+               pr_info("%s : %s  is empty\n", __func__, dt_property); \
+       else \
+               pr_info("%s : %s  is find\n", __func__, dt_property); \
+} while (0)
+
+static int parse_dt_pchg_current_map(const u32 *arr,
+                                    struct pchg_current_map *current_map,
+                                    int count)
+{
+       u32 len = 0;
+       u32 requested;
+       u32 primary;
+       u32 secondary;
+       int i;
+
+       if (!arr)
+               return 0;
+
+       for (i = 0; i < count*3; i += 3) {
+               requested = be32_to_cpu(arr[i]);
+               primary = be32_to_cpu(arr[i + 1]);
+               secondary = be32_to_cpu(arr[i + 2]);
+               current_map->requested = requested;
+               current_map->primary = primary;
+               current_map->secondary = secondary;
+               len++;
+               current_map++;
+       }
+       return len;
+}
+
+#define DC_MA_MIN 300
+#define DC_MA_MAX 2000
+static int mmi_parse_dt(struct device_node *np,
+                       struct s2mu00x_battery_info *chip, struct device *dev)
+{
+       int rc = 0;
+       struct device_node *node = np;
+       const u32 *current_map;
+       int ret = 0;
+
+       if (!node) {
+               dev_err(chip->dev, "device tree info. missing\n");
+               return -EINVAL;
+       }
+
+       /* read optional u32 properties */
+       ret = of_property_read_u32(node, "moto,iterm-ma",
+                       &chip->iterm_ma);
+       if (ret)
+               pr_info("%s : moto,iterm-ma is empty\n", __func__);
+
+       OF_PROP_READ(node, chip->target_fastchg_current_ma,
+                       "fastchg-current-ma", rc, 1);
+
+       OF_PROP_READ(node, chip->vfloat_mv, "float-voltage-mv", rc, 1);
+       OF_PROP_READ(node, chip->fastchg_current_comp, "fastchg-current-comp",
+                       rc, 1);
+       OF_PROP_READ(node, chip->float_voltage_comp, "float-voltage-comp",
+                       rc, 1);
+       OF_PROP_READ(node, chip->afvc_mv, "auto-voltage-comp-mv",
+                       rc, 1);
+       OF_PROP_READ(node, chip->resume_delta_mv, "resume-delta-mv", rc, 1);
+       OF_PROP_READ(node, chip->jeita_temp_hard_limit,
+                       "jeita-temp-hard-limit", rc, 1);
+
+       OF_PROP_READ(node, chip->hot_temp_c,
+                    "hot-temp-c", rc, 1);
+       if (chip->hot_temp_c == -EINVAL)
+               chip->hot_temp_c = 60;
+
+       OF_PROP_READ(node, chip->cold_temp_c,
+                    "cold-temp-c", rc, 1);
+       if (chip->cold_temp_c == -EINVAL)
+               chip->cold_temp_c = -20;
+
+       OF_PROP_READ(node, chip->warm_temp_c,
+                    "warm-temp-c", rc, 1);
+       if (chip->warm_temp_c == -EINVAL)
+               chip->warm_temp_c = 45;
+
+       OF_PROP_READ(node, chip->cool_temp_c,
+                    "cool-temp-c", rc, 1);
+       if (chip->cool_temp_c == -EINVAL)
+               chip->cool_temp_c = 0;
+       OF_PROP_READ(node, chip->slightly_cool_temp_c,
+                    "slightly-cool-temp-c", rc, 1);
+       if (chip->slightly_cool_temp_c == -EINVAL)
+               chip->slightly_cool_temp_c = 15;
+       OF_PROP_READ(node, chip->ext_temp_volt_mv,
+                    "ext-temp-volt-mv", rc, 1);
+       if (chip->ext_temp_volt_mv == -EINVAL)
+               chip->ext_temp_volt_mv = 4200;
+
+       OF_PROP_READ(node, chip->hotspot_thrs_c,
+                    "hotspot-thrs-c", rc, 1);
+       if (chip->hotspot_thrs_c == -EINVAL)
+               chip->hotspot_thrs_c = 50;
+
+       OF_PROP_READ(node, chip->upper_limit_capacity,
+                    "upper-limit-capacity", rc, 1);
+       if (chip->upper_limit_capacity == -EINVAL)
+               chip->upper_limit_capacity = 75;
+
+       OF_PROP_READ(node, chip->lower_limit_capacity,
+                    "lower-limit-capacity", rc, 1);
+       if (chip->lower_limit_capacity == -EINVAL)
+               chip->lower_limit_capacity = 60;
+
+       OF_PROP_READ(node, chip->stepchg_voltage_mv,
+                       "stepchg-voltage-mv", rc, 1);
+
+       OF_PROP_READ(node, chip->stepchg_current_ma,
+                       "stepchg-current-ma", rc, 1);
+
+       OF_PROP_READ(node, chip->stepchg_taper_ma,
+                       "stepchg-taper-ma", rc, 1);
+
+       OF_PROP_READ(node, chip->stepchg_iterm_ma,
+                       "stepchg-iterm-ma", rc, 1);
+       if ((chip->stepchg_current_ma != -EINVAL) &&
+           (chip->stepchg_voltage_mv != -EINVAL) &&
+           (chip->stepchg_taper_ma != -EINVAL) &&
+           (chip->stepchg_iterm_ma != -EINVAL)) {
+               chip->stepchg_max_current_ma = chip->target_fastchg_current_ma;
+               chip->allowed_fastchg_current_ma =
+                       chip->target_fastchg_current_ma;
+               chip->stepchg_max_voltage_mv = chip->vfloat_mv;
+       }
+       OF_PROP_READ(node, chip->temp_warm_current_ma,
+                       "temp-warm-current-ma", rc, 1);
+       if (chip->temp_warm_current_ma == -EINVAL)
+               chip->temp_warm_current_ma = chip->target_fastchg_current_ma;
+       OF_PROP_READ(node, chip->temp_cool_current_ma,
+                       "temp-cool-current-ma", rc, 1);
+       if (chip->temp_cool_current_ma == -EINVAL)
+               chip->temp_cool_current_ma = chip->target_fastchg_current_ma;
+       OF_PROP_READ(node, chip->temp_slightly_cool_current_ma,
+                       "temp-slightly-cool-current-ma", rc, 1);
+       if (chip->temp_slightly_cool_current_ma == -EINVAL)
+               chip->temp_slightly_cool_current_ma =
+                       chip->target_fastchg_current_ma;
+       chip->temp_good_current_ma = chip->target_fastchg_current_ma;
+       chip->temp_allowed_fastchg_current_ma = chip->temp_good_current_ma;
+
+       /* read boolean configuration properties */
+       chip->use_vfloat_adjustments = of_property_read_bool(node,
+                                               "moto,autoadjust-vfloat");
+       chip->chg_enabled = !(of_property_read_bool(node,
+                                               "moto,charging-disabled"));
+       chip->charge_unknown_battery = of_property_read_bool(node,
+                                               "moto,charge-unknown-battery");
+       chip->chg_inhibit_en = of_property_read_bool(node,
+                                       "moto,chg-inhibit-en");
+       chip->chg_inhibit_source_fg = of_property_read_bool(node,
+                                               "moto,chg-inhibit-fg");
+       chip->low_volt_dcin = of_property_read_bool(node,
+                                       "moto,low-volt-dcin");
+       chip->usbid_disabled = of_property_read_bool(node,
+                                               "moto,usbid-disabled");
+       chip->usbid_gpio_enabled = of_property_read_bool(node,
+                                               "moto,usbid-gpio-enabled");
+       chip->enable_hvdcp_9v = of_property_read_bool(node,
+                                       "moto,enable-hvdcp-9v");
+       chip->enable_charging_limit = of_property_read_bool(node,
+                                       "moto,enable-charging-limit");
+       chip->enabled_weak_charger_check = of_property_read_bool(node,
+                                       "moto,weak-charger-check-enable");
+
+       /* parse the dc power supply configuration */
+       current_map = of_get_property(node, "moto,parallel-charge-current-map",
+                                     &chip->pchg_current_map_len);
+       if ((!current_map) || (chip->pchg_current_map_len <= 0))
+               dev_err(chip->dev, "No parallel charge current map defined\n");
+       else {
+               chip->pchg_current_map_len /= 3 * sizeof(u32);
+               dev_err(chip->dev, "length=%d\n", chip->pchg_current_map_len);
+               if (chip->pchg_current_map_len > 30)
+                       chip->pchg_current_map_len = 30;
+
+               chip->pchg_current_map_data =
+                       devm_kzalloc(dev,
+                                    (sizeof(struct pchg_current_map) *
+                                     chip->pchg_current_map_len),
+                                    GFP_KERNEL);
+               if (chip->pchg_current_map_data == NULL) {
+                       dev_err(chip->dev,
+                        "Failed to kzalloc memory for parallel charge map.\n");
+                       return -ENOMEM;
+               }
+
+               chip->pchg_current_map_len =
+                       parse_dt_pchg_current_map(current_map,
+                                                 chip->pchg_current_map_data,
+                                                 chip->pchg_current_map_len);
+
+               if (chip->pchg_current_map_len <= 0) {
+                       dev_err(chip->dev,
+                       "Couldn't read parallel charge currents rc = %d\n", rc);
+                       return rc;
+               }
+               dev_err(chip->dev, "num parallel charge entries=%d\n",
+                       chip->pchg_current_map_len);
+       }
+
+
+       if (of_find_property(node, "moto,chg-thermal-mitigation",
+                                       &chip->chg_thermal_levels)) {
+               chip->chg_thermal_mitigation = devm_kzalloc(dev,
+                       chip->chg_thermal_levels,
+                       GFP_KERNEL);
+               if (chip->chg_thermal_mitigation == NULL) {
+                       pr_info("%s : thermal mitigation kzalloc() failed. \n", __func__);
+                       dev_err(chip->dev,
+                       "thermal mitigation kzalloc() failed.\n");
+               return -ENOMEM;
+               }
+
+               chip->chg_thermal_levels /= sizeof(int);
+               rc = of_property_read_u32_array(node,
+                                       "moto,chg-thermal-mitigation",
+                                       chip->chg_thermal_mitigation,
+                                       chip->chg_thermal_levels);
+               if (rc) {
+                       dev_err(chip->dev,
+                               "Couldn't read therm limits rc = %d\n", rc);
+                       return rc;
+               }
+       } else
+               chip->chg_thermal_levels = 0;
+
+       if (of_find_property(node, "moto,thermal-mitigation",
+                                       &chip->thermal_levels)) {
+               chip->thermal_mitigation = devm_kzalloc(dev,
+                       chip->thermal_levels,
+                       GFP_KERNEL);
+
+               if (chip->thermal_mitigation == NULL) {
+                       dev_err(chip->dev, "thermal mitigation kzalloc() failed.\n");
+                       return -ENOMEM;
+               }
+
+               chip->thermal_levels /= sizeof(int);
+               rc = of_property_read_u32_array(node,
+                               "moto,thermal-mitigation",
+                               chip->thermal_mitigation, chip->thermal_levels);
+               if (rc) {
+                       dev_err(chip->dev,
+                               "Couldn't read therm limits rc = %d\n", rc);
+                       return rc;
+               }
+       } else
+               chip->thermal_levels = 0;
+
+       if (of_find_property(node, "moto,dc-thermal-mitigation",
+                            &chip->dc_thermal_levels)) {
+               chip->dc_thermal_mitigation = devm_kzalloc(dev,
+                       chip->dc_thermal_levels,
+                       GFP_KERNEL);
+
+               if (chip->dc_thermal_mitigation == NULL) {
+                       pr_info("DC thermal mitigation kzalloc() failed.\n");
+                       return -ENOMEM;
+               }
+
+               chip->dc_thermal_levels /= sizeof(int);
+               rc = of_property_read_u32_array(node,
+                               "moto,dc-thermal-mitigation",
+                               chip->dc_thermal_mitigation,
+                               chip->dc_thermal_levels);
+               if (rc) {
+                       pr_info("Couldn't read DC therm limits rc = %d\n", rc);
+                       return rc;
+               }
+       } else
+               chip->dc_thermal_levels = 0;
+
+       return 0;
+}
+
 static int s2mu00x_battery_parse_dt(struct device *dev,
                struct s2mu00x_battery_info *battery)
 {
@@ -1882,6 +2507,11 @@ static int s2mu00x_battery_parse_dt(struct device *dev,
 
        pr_info("%s:DT parsing is done, vendor : %s, technology : %d\n",
                        __func__, pdata->vendor, pdata->technology);
+       //moto
+       mmi_parse_dt(np, battery, dev);
+
+       pr_info("%s:DT parsing is done for mmi, vendor : %s, technology : %d\n",
+                       __func__, pdata->vendor, pdata->technology);
        return ret;
 }
 #else
@@ -1909,7 +2539,22 @@ static enum alarmtimer_restart bat_monitor_alarm(
        return ALARMTIMER_NORESTART;
 }
 
-static void soc_control_worker(struct work_struct *work)
+//moto
+#define HYSTERISIS_DEGC 2
+#define MAX_TEMP_C 60
+#define MIN_MAX_TEMP_C 47
+
+enum wake_reason {
+       PM_PARALLEL_CHECK = BIT(0),
+       PM_REASON_VFLOAT_ADJUST = BIT(1),
+       PM_ESR_PULSE = BIT(2),
+       PM_HEARTBEAT = BIT(3),
+       PM_CHARGER = BIT(4),
+       PM_WIRELESS = BIT(5),
+};
+
+static int smbchg_chg_system_temp_level_set(struct s2mu00x_battery_info *chip,
+                                           int lvl_sel)
 {
        struct s2mu00x_battery_info *battery =
                container_of(work, struct s2mu00x_battery_info, soc_control.work);
@@ -2003,12 +2648,15 @@ static struct device_attribute s2mu00x_battery_attrs[] = {
 };
 #endif
 
-       pr_info("%s: S2MU00x battery capacity = %d, status = %d\n",
-               __func__, battery->capacity, battery->status);
+       if (!chip->chg_thermal_mitigation) {
+               dev_err(chip->dev, "Charge thermal mitigation not supported\n");
+               return -EINVAL;
+       }
 
-       if ((battery->capacity >= 75) && (battery->status == POWER_SUPPLY_STATUS_CHARGING)) {
-               pr_info("%s: Capacity is more than 75, stop charging\n", __func__);
-               set_battery_status(battery, POWER_SUPPLY_STATUS_DISCHARGING);
+       if (lvl_sel < 0) {
+               dev_err(chip->dev, "Unsupported charge level selected %d\n",
+                       lvl_sel);
+               return -EINVAL;
        }
 
        queue_delayed_work(battery->monitor_wqueue, &battery->soc_control, 10*HZ);
@@ -2032,172 +2680,1601 @@ create_attrs_succeed:
        return ret;
 }
 
-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
+       if (lvl_sel == chip->chg_therm_lvl_sel)
+               return 0;
 
-       pr_info("%s: S2MU00x battery driver loading\n", __func__);
+       pr_info("%s,set thermal level:%d", lvl_sel);
 
-       /* Allocate necessary device data structures */
-       battery = kzalloc(sizeof(*battery), GFP_KERNEL);
-       if (!battery)
-               return -ENOMEM;
+//     mutex_lock(&chip->current_change_lock);
+       prev_therm_lvl = chip->chg_therm_lvl_sel;
+       chip->chg_therm_lvl_sel = lvl_sel;
 
-       pr_info("%s: battery is allocated\n", __func__);
+       chip->allowed_fastchg_current_ma =
+               chip->chg_thermal_mitigation[lvl_sel];
+       chip->update_allowed_fastchg_current_ma = true;
 
-       battery->pdata = devm_kzalloc(&pdev->dev, sizeof(*(battery->pdata)),
-                       GFP_KERNEL);
-       if (!battery->pdata) {
-               ret = -ENOMEM;
-               goto err_bat_free;
-       }
+       cancel_delayed_work(&chip->heartbeat_work);
+       schedule_delayed_work(&chip->heartbeat_work,
+                             msecs_to_jiffies(0));
+//     mutex_unlock(&chip->current_change_lock);
+       return rc;
+}
 
-       pr_info("%s: pdata is allocated\n", __func__);
+//may need add some type
+bool is_usb_present(struct s2mu00x_battery_info *chip)
+{
+       int type = chip->cable_type;
 
-       /* 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;
-       }
+       if (type == POWER_SUPPLY_TYPE_USB || type == POWER_SUPPLY_TYPE_USB_CDP)
+               return true;
+       else
+               return false;
+}
 
-       pr_info("%s: DT parsing is done\n", __func__);
+bool is_dc_present(struct s2mu00x_battery_info *chip)
+{
+       int type = chip->cable_type;
 
-       /* Set driver data */
-       platform_set_drvdata(pdev, battery);
-       battery->dev = &pdev->dev;
+       if (type == POWER_SUPPLY_TYPE_USB_DCP || type == POWER_SUPPLY_TYPE_MAINS)
+               return true;
+       else
+               return false;
+}
 
-       mutex_init(&battery->iolock);
-       mutex_init(&battery->ifconn_lock);
+static void smbchg_stay_awake(struct s2mu00x_battery_info *chip)
+{
+       wake_lock(&chip->heartbeat_wake_lock);
+}
 
-       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");
+static void smbchg_relax(struct s2mu00x_battery_info *chip)
+{
+       wake_unlock(&chip->heartbeat_wake_lock);
+};
 
-       /* Inintialization of battery information */
-       battery->status = POWER_SUPPLY_STATUS_DISCHARGING;
-       battery->health = POWER_SUPPLY_HEALTH_GOOD;
+#define CHG_SHOW_MAX_SIZE 50
+static ssize_t factory_charge_upper_show(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       int state;
 
-       battery->input_current = 0;
-       battery->charging_current = 0;
-       battery->topoff_current = 0;
-#if defined(CONFIG_SMALL_CHARGER)
-       battery->small_input_flag = 0;
-#endif
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
+       }
 
-       battery->max_input_current = battery->pdata->max_input_current;
-       battery->max_charging_current = battery->pdata->max_charging_current;
-#if defined(CONFIG_USE_CCIC)
-       battery->pdo_max_input_vol = battery->pdata->pdo_max_input_vol;
-       battery->pdo_max_chg_power = battery->pdata->pdo_max_chg_power;
-       battery->pd_input_current = 2000;
-       battery->pd_attach = false;
-       battery->rp_attach = false;
-#endif
-       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;
+       state = the_chip->upper_limit_capacity;
 
-       battery->max_rawsoc = battery->pdata->max_rawsoc;
-       battery->max_rawsoc_offset = battery->pdata->max_rawsoc_offset;
+       return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state);
+}
 
-       battery->is_factory = false;
+static DEVICE_ATTR(factory_charge_upper, 0444,
+               factory_charge_upper_show,
+               NULL);
 
-       battery->is_recharging = false;
-       battery->cable_type = POWER_SUPPLY_TYPE_BATTERY;
-       battery->pd_attach = false;
+static ssize_t force_demo_mode_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       unsigned long r;
+       unsigned long mode;
 
-#if defined(CONFIG_CHARGER_S2MU106)
-       psy = power_supply_get_by_name(battery->pdata->charger_name);
-       if (!psy)
+       r = kstrtoul(buf, 0, &mode);
+       if (r) {
+               pr_err("Invalid usb suspend mode value = %lu\n", mode);
                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;
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
+       }
+
+       the_chip->stepchg_state_holdoff = 0;
+
+       if ((mode >= 35) && (mode <= 80))
+               the_chip->demo_mode = mode;
        else
-               battery->battery_valid = true;
-#else
-       battery->battery_valid = true;
-#endif
-       /* 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.property_is_writeable =  s2mu00x_battery_property_is_writeable;
-       battery->psy_battery_desc.properties = s2mu00x_battery_props;
-       battery->psy_battery_desc.num_properties =  ARRAY_SIZE(s2mu00x_battery_props);
+               the_chip->demo_mode = 35;
 
-       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);
+       return r ? r : count;
+}
 
-       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;
+static ssize_t force_demo_mode_show(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       int state;
 
-       /* 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;
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
        }
 
-       /* 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;
+       state = the_chip->demo_mode;
 
-#if defined(CONFIG_USE_CCIC)
-#if defined(CONFIG_USE_PDO_SELECT)
-       INIT_DELAYED_WORK(&battery->select_pdo_work, usbpd_select_pdo_work);
-#endif
-#endif
-       /* 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);
+       return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state);
+}
 
-       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;
+static ssize_t factory_image_mode_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       unsigned long r;
+       unsigned long mode;
+
+       r = kstrtoul(buf, 0, &mode);
+       if (r) {
+               pr_err("Invalid factory image mode value = %lu\n", mode);
+               return -EINVAL;
        }
-       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;
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
        }
-       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);
+       the_chip->is_factory_image = (mode) ? true : false;
+
+       return r ? r : count;
+}
+
+static ssize_t factory_image_mode_show(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       int state;
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
+       }
+
+       state = (the_chip->is_factory_image) ? 1 : 0;
+
+       return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state);
+}
+
+static ssize_t force_max_chrg_temp_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       unsigned long r;
+       unsigned long mode;
+
+       r = kstrtoul(buf, 0, &mode);
+       if (r) {
+               pr_err("Invalid max temp value = %lu\n", mode);
+               return -EINVAL;
+       }
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
+       }
+
+       if ((mode >= MIN_MAX_TEMP_C) && (mode <= MAX_TEMP_C))
+               the_chip->max_chrg_temp = mode;
+       else
+               the_chip->max_chrg_temp = MAX_TEMP_C;
+
+       return r ? r : count;
+}
+
+static ssize_t force_max_chrg_temp_show(struct device *dev,
+                                   struct device_attribute *attr,
+                                   char *buf)
+{
+       int state;
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
+       }
+
+       state = the_chip->max_chrg_temp;
+
+       return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state);
+}
+
+static DEVICE_ATTR(factory_image_mode, 0644,
+               factory_image_mode_show,
+               factory_image_mode_store);
+
+static DEVICE_ATTR(force_demo_mode, 0644,
+               force_demo_mode_show,
+               force_demo_mode_store);
+
+static DEVICE_ATTR(force_max_chrg_temp, 0644,
+               force_max_chrg_temp_show,
+               force_max_chrg_temp_store);
+
+static ssize_t force_chg_usb_suspend_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       unsigned long r;
+       unsigned long mode;
+
+       r = kstrtoul(buf, 0, &mode);
+       if (r) {
+               pr_err("Invalid usb suspend mode value = %lu\n", mode);
+               return -EINVAL;
+       }
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
+       }
+       smbchg_usb_en(the_chip, false);
+
+       return r ? r : count;
+}
+
+static ssize_t force_chg_usb_suspend_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       int state;
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
+       }
+
+       if (the_chip->status == POWER_SUPPLY_STATUS_NOT_CHARGING)
+               state = 1;
+       else
+               state = 0;
+
+       return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state);
+}
+
+static DEVICE_ATTR(force_chg_usb_suspend, 0664,
+               force_chg_usb_suspend_show,
+               force_chg_usb_suspend_store);
+
+static ssize_t force_chg_fail_clear_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       unsigned long r;
+       unsigned long mode;
+
+       r = kstrtoul(buf, 0, &mode);
+       if (r) {
+               pr_err("Invalid chg fail mode value = %lu\n", mode);
+               return -EINVAL;
+       }
+
+       return r ? r : count;
+}
+
+static ssize_t force_chg_fail_clear_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       /* do nothing for SMBCHG */
+       return scnprintf(buf, CHG_SHOW_MAX_SIZE, "0\n");
+}
+
+static DEVICE_ATTR(force_chg_fail_clear, 0664,
+               force_chg_fail_clear_show,
+               force_chg_fail_clear_store);
+
+static ssize_t force_chg_auto_enable_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       unsigned long r;
+       unsigned long mode;
+
+       r = kstrtoul(buf, 0, &mode);
+       if (r) {
+               pr_err("Invalid chrg enable value = %lu\n", mode);
+               return -EINVAL;
+       }
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
+       }
+
+       if (mode == 0)
+               set_battery_status(the_chip, POWER_SUPPLY_STATUS_DISCHARGING);
+       else
+               set_battery_status(the_chip, POWER_SUPPLY_STATUS_CHARGING);
+
+       return r ? r : count;
+}
+
+static ssize_t force_chg_auto_enable_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       int state;
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               state = -ENODEV;
+               goto end;
+       }
+
+       if (the_chip->status == POWER_SUPPLY_STATUS_CHARGING)
+               state = 1;
+       else
+               state = 0 ;
+
+end:
+       return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state);
+}
+
+static DEVICE_ATTR(force_chg_auto_enable, 0664,
+               force_chg_auto_enable_show,
+               force_chg_auto_enable_store);
+
+static ssize_t force_chg_ibatt_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       unsigned long r;
+       unsigned long chg_current;
+
+       r = kstrtoul(buf, 0, &chg_current);
+       if (r) {
+               pr_err("Invalid ibatt value = %lu\n", chg_current);
+               return -EINVAL;
+       }
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
+       }
+
+       set_property_on_charger(the_chip, POWER_SUPPLY_PROP_CURRENT_NOW, chg_current);
+
+       return r ? r : count;
+}
+
+static ssize_t force_chg_ibatt_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       int state;
+
+       get_property_from_charger(the_chip, POWER_SUPPLY_PROP_CURRENT_NOW, &state);
+
+       return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state);
+}
+
+static DEVICE_ATTR(force_chg_ibatt, 0664,
+               force_chg_ibatt_show,
+               force_chg_ibatt_store);
+
+static int smbchg_set_high_usb_chg_current_fac(struct s2mu00x_battery_info *chip,
+                                              int current_ma)
+{
+       set_property_on_charger(the_chip, POWER_SUPPLY_PROP_CURRENT_MAX, current_ma);
+
+       return 1;
+}
+
+static ssize_t force_chg_iusb_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       unsigned long r;
+       unsigned long usb_curr;
+
+       r = kstrtoul(buf, 0, &usb_curr);
+       if (r) {
+               pr_err("Invalid iusb value = %lu\n", usb_curr);
+               return -EINVAL;
+       }
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
+       }
+
+       r = smbchg_set_high_usb_chg_current_fac(the_chip,
+                                               usb_curr);
+       if (r < 0) {
+               pr_info("Couldn't set USBIN Current = %d r = %d\n",
+                       (int)usb_curr, (int)r);
+               return r;
+       }
+       return r ? r : count;
+}
+
+static ssize_t force_chg_iusb_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       int state;
+       int ret;
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               ret = -ENODEV;
+               goto end;
+       }
+
+       get_property_from_charger(the_chip, POWER_SUPPLY_PROP_CURRENT_MAX, &state);
+
+end:
+       return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state);
+}
+
+static DEVICE_ATTR(force_chg_iusb, 0664,
+               force_chg_iusb_show,
+               force_chg_iusb_store);
+
+
+#define PRECHG_OFFSET 100
+#define PRECHG_STEP 50
+#define PRECHG_TOP 250
+#define PRECHG_REG_SHIFT 5
+#define PRECHG_MASK 0x7
+#define PRECHG_CFG 0xF1
+#define PRECHG_MAX 550
+#define PRECHG_MAX_LVL 0x4
+static ssize_t force_chg_itrick_store(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+#if 0
+       unsigned long r;
+       unsigned long chg_current;
+       int i;
+
+       r = kstrtoul(buf, 0, &chg_current);
+       if (r) {
+               pr_err("Invalid pre-charge value = %lu\n", chg_current);
+               return -EINVAL;
+       }
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
+       }
+
+       if (chg_current >= PRECHG_MAX) {
+               i = PRECHG_MAX_LVL;
+               goto prechg_write;
+       }
+
+       for (i = PRECHG_TOP; i > PRECHG_OFFSET; i = i - PRECHG_STEP) {
+               if (chg_current >= i)
+                       break;
+       }
+
+       i = (i - PRECHG_OFFSET) / PRECHG_STEP;
+
+       i = i & PRECHG_MASK;
+
+prechg_write:
+       r = smbchg_sec_masked_write_fac(the_chip,
+                                       the_chip->chgr_base + PRECHG_CFG,
+                                       PRECHG_MASK, i);
+       if (r < 0) {
+               dev_err(the_chip->dev,
+                       "Couldn't set Pre-Charge Current = %d r = %d\n",
+                       (int)chg_current, (int)r);
+               return r;
+       }
+
+
+       return 1;
+#else
+       return count;
+#endif
+}
+
+static ssize_t force_chg_itrick_show(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *buf)
+{
+       int state;
+#if 0
+       int ret;
+       u8 value;
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               state = -ENODEV;
+               goto end;
+       }
+
+       ret = smbchg_read(the_chip,
+                         &value, the_chip->chgr_base + PRECHG_CFG, 1);
+       if (ret) {
+               pr_err("Pre-Charge Current failed ret = %d\n", ret);
+               state = -EFAULT;
+               goto end;
+       }
+
+       state = value & PRECHG_MASK;
+
+       if (state >= PRECHG_MAX_LVL)
+               state = PRECHG_MAX;
+       else
+               state = (state * PRECHG_STEP) + PRECHG_OFFSET;
+#else
+       state = 50;//fixed value at samsung.
+#endif
+
+       return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state);
+}
+
+static DEVICE_ATTR(force_chg_itrick, 0664,
+                  force_chg_itrick_show,
+                  force_chg_itrick_store);
+
+static ssize_t force_chg_usb_otg_ctl_store(struct device *dev,
+                                     struct device_attribute *attr,
+                                     const char *buf, size_t count)
+{
+       unsigned long r;
+       unsigned long mode;
+       int value;
+
+       r = kstrtoul(buf, 0, &mode);
+       if (r) {
+               pr_err("Invalid otg ctl value = %lu\n", mode);
+               return -EINVAL;
+       }
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               return -ENODEV;
+       }
+
+       if (mode)
+               value = 1;
+       else
+               value = 0;
+
+       set_property_on_otg(the_chip, POWER_SUPPLY_PROP_ONLINE, value);
+
+       return r ? r : count;
+}
+
+static ssize_t force_chg_usb_otg_ctl_show(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *buf)
+{
+       int state;
+       int value;
+
+       if (!the_chip) {
+               pr_err("chip not valid\n");
+               state = -ENODEV;
+               goto end;
+       }
+
+       get_property_from_otg(the_chip, POWER_SUPPLY_PROP_ONLINE, &value);
+
+end:
+       return scnprintf(buf, CHG_SHOW_MAX_SIZE, "%d\n", state);
+}
+
+static DEVICE_ATTR(force_chg_usb_otg_ctl, 0664,
+                  force_chg_usb_otg_ctl_show,
+                  force_chg_usb_otg_ctl_store);
+
+static bool smbchg_is_max_thermal_level(struct s2mu00x_battery_info *chip)
+{
+       if ((chip->chg_thermal_levels == 0) ||
+           ((chip->chg_thermal_levels > 0) &&
+            ((chip->usb_present) &&
+             ((chip->chg_therm_lvl_sel >= (chip->chg_thermal_levels - 1)) ||
+              (chip->chg_therm_lvl_sel == -EINVAL)))))
+               return true;
+       else if ((chip->dc_thermal_levels == 0) ||
+                ((chip->dc_thermal_levels > 0) &&
+                 ((chip->dc_present) &&
+                  ((chip->dc_therm_lvl_sel >=
+                    (chip->dc_thermal_levels - 1)) ||
+                   (chip->dc_therm_lvl_sel == -EINVAL)))))
+               return true;
+       else
+               return false;
+}
+
+static int smbchg_check_temp_range(struct s2mu00x_battery_info *chip,
+                                  int batt_volt,
+                                  int batt_soc,
+                                  int batt_health,
+                                  int prev_batt_health)
+{
+       int ext_high_temp = 0;
+
+       if (((batt_health == POWER_SUPPLY_HEALTH_COOL) ||
+           ((batt_health == POWER_SUPPLY_HEALTH_WARM)
+           && (smbchg_is_max_thermal_level(chip))))
+           && (batt_volt > chip->ext_temp_volt_mv))
+               ext_high_temp = 1;
+
+       if ((((prev_batt_health == POWER_SUPPLY_HEALTH_COOL) &&
+           (batt_health == POWER_SUPPLY_HEALTH_COOL)) ||
+           ((prev_batt_health == POWER_SUPPLY_HEALTH_WARM) &&
+           (batt_health == POWER_SUPPLY_HEALTH_WARM))) &&
+           !chip->ext_high_temp)
+               ext_high_temp = 0;
+
+       if (chip->ext_high_temp != ext_high_temp) {
+               chip->ext_high_temp = ext_high_temp;
+               pr_info("Ext High = %s\n",
+                       chip->ext_high_temp ? "High" : "Low");
+
+               return 1;
+       }
+
+       return 0;
+}
+
+static void smbchg_check_temp_state(struct s2mu00x_battery_info *chip, int batt_temp)
+{
+       int hotspot;
+       int temp_state = POWER_SUPPLY_HEALTH_GOOD;
+       int max_temp = 0;
+
+       if (!chip)
+               return;
+
+       if (chip->max_chrg_temp >= MIN_MAX_TEMP_C)
+               max_temp = chip->max_chrg_temp;
+       else
+               max_temp = chip->hot_temp_c;
+
+       mutex_lock(&chip->check_temp_lock);
+
+       /* Convert to Degrees C */
+       hotspot = chip->hotspot_temp / 1000;
+
+       /* Override batt_temp if battery hot spot condition
+          is active */
+       if ((batt_temp > chip->cool_temp_c) &&
+           (hotspot > batt_temp) &&
+           (hotspot >= chip->hotspot_thrs_c)) {
+               batt_temp = hotspot;
+       }
+
+       if (chip->temp_state == POWER_SUPPLY_HEALTH_WARM) {
+               if (batt_temp >= max_temp)
+                       /* Warm to Hot */
+                       temp_state = POWER_SUPPLY_HEALTH_OVERHEAT;
+               else if (batt_temp <=
+                        chip->warm_temp_c - HYSTERISIS_DEGC)
+                       /* Warm to Normal */
+                       temp_state = POWER_SUPPLY_HEALTH_GOOD;
+               else
+                       /* Stay Warm */
+                       temp_state = POWER_SUPPLY_HEALTH_WARM;
+       } else if ((chip->temp_state == POWER_SUPPLY_HEALTH_GOOD) ||
+                  (chip->temp_state == POWER_SUPPLY_HEALTH_UNKNOWN)) {
+               if (batt_temp >= chip->warm_temp_c)
+                       /* Normal to Warm */
+                       temp_state = POWER_SUPPLY_HEALTH_WARM;
+               else if (batt_temp <= chip->slightly_cool_temp_c)
+                       /* Normal to slightly Cool */
+                       temp_state = POWER_SUPPLY_HEALTH_SLIGHTLY_COOL;
+               else
+                       /* Stay Normal */
+                       temp_state = POWER_SUPPLY_HEALTH_GOOD;
+       } else if (chip->temp_state == POWER_SUPPLY_HEALTH_SLIGHTLY_COOL) {
+               if (batt_temp >=
+                   chip->slightly_cool_temp_c + HYSTERISIS_DEGC)
+                       /* Slightly Cool to Normal */
+                       temp_state = POWER_SUPPLY_HEALTH_GOOD;
+               else if (batt_temp <= chip->cool_temp_c)
+                       /*Slightly Cool  to Cool */
+                       temp_state = POWER_SUPPLY_HEALTH_COOL;
+               else
+                       /* Stay Slightly Cool  */
+                       temp_state = POWER_SUPPLY_HEALTH_SLIGHTLY_COOL;
+       } else if (chip->temp_state == POWER_SUPPLY_HEALTH_COOL) {
+               if (batt_temp >=
+                   chip->cool_temp_c + HYSTERISIS_DEGC)
+                       /*Cool to Slightly Cool  */
+                       temp_state = POWER_SUPPLY_HEALTH_SLIGHTLY_COOL;
+               else if (batt_temp <= chip->cold_temp_c)
+                       /* Cool to Cold */
+                       temp_state = POWER_SUPPLY_HEALTH_COLD;
+               else
+                       /* Stay Cool */
+                       temp_state = POWER_SUPPLY_HEALTH_COOL;
+       } else if (chip->temp_state == POWER_SUPPLY_HEALTH_COLD) {
+               if (batt_temp >=
+                   chip->cold_temp_c + HYSTERISIS_DEGC)
+                       /* Cold to Cool */
+                       temp_state = POWER_SUPPLY_HEALTH_COOL;
+               else
+                       /* Stay Cold */
+                       temp_state = POWER_SUPPLY_HEALTH_COLD;
+       } else if (chip->temp_state == POWER_SUPPLY_HEALTH_OVERHEAT) {
+               if (batt_temp <= max_temp - HYSTERISIS_DEGC)
+                       /* Hot to Warm */
+                       temp_state = POWER_SUPPLY_HEALTH_WARM;
+               else
+                       /* Stay Hot */
+                       temp_state = POWER_SUPPLY_HEALTH_OVERHEAT;
+       }
+
+       if (chip->temp_state != temp_state) {
+               chip->temp_state = temp_state;
+               pr_info("Battery Temp State = %s\n",
+                       smb_health_text[chip->temp_state]);
+       }
+       mutex_unlock(&chip->check_temp_lock);
+
+       return;
+}
+
+
+#define DEMO_MODE_VOLTAGE 4000
+static void smbchg_set_temp_chgpath(struct s2mu00x_battery_info *chip, int prev_temp)
+{
+       if (chip->factory_mode)
+               return;
+
+       if (chip->demo_mode)
+               set_property_on_charger(chip, POWER_SUPPLY_PROP_VOLTAGE_MAX, DEMO_MODE_VOLTAGE);
+       else if (((chip->temp_state == POWER_SUPPLY_HEALTH_COOL)
+                 || (chip->temp_state == POWER_SUPPLY_HEALTH_WARM))
+                && !chip->ext_high_temp)
+               set_property_on_charger(chip, POWER_SUPPLY_PROP_VOLTAGE_MAX, chip->ext_temp_volt_mv);
+       else {
+               set_property_on_charger(chip, POWER_SUPPLY_PROP_VOLTAGE_MAX, chip->vfloat_mv);
+       }
+
+       if ((chip->temp_state == POWER_SUPPLY_HEALTH_COOL) ||
+               (chip->temp_state == POWER_SUPPLY_HEALTH_COLD))
+               chip->temp_allowed_fastchg_current_ma =
+                       chip->temp_cool_current_ma;
+       else if ((chip->temp_state == POWER_SUPPLY_HEALTH_WARM) ||
+               (chip->temp_state == POWER_SUPPLY_HEALTH_OVERHEAT))
+               chip->temp_allowed_fastchg_current_ma =
+                       chip->temp_warm_current_ma;
+       else if (chip->temp_state == POWER_SUPPLY_HEALTH_SLIGHTLY_COOL)
+               chip->temp_allowed_fastchg_current_ma =
+                       chip->temp_slightly_cool_current_ma;
+       else if (chip->temp_state == POWER_SUPPLY_HEALTH_GOOD)
+               chip->temp_allowed_fastchg_current_ma =
+                       chip->temp_good_current_ma;
+
+       if (chip->ext_high_temp ||
+           (chip->temp_state == POWER_SUPPLY_HEALTH_COLD) ||
+           (chip->temp_state == POWER_SUPPLY_HEALTH_OVERHEAT) ||
+           (chip->stepchg_state == STEP_FULL))
+               smbchg_charging_en(chip, 0);
+       else {
+               if (((prev_temp == POWER_SUPPLY_HEALTH_COOL) ||
+                   (prev_temp == POWER_SUPPLY_HEALTH_WARM)) &&
+                   (chip->temp_state == POWER_SUPPLY_HEALTH_GOOD ||
+                   chip->temp_state == POWER_SUPPLY_HEALTH_SLIGHTLY_COOL)) {
+                       smbchg_charging_en(chip, 0);
+                       mdelay(10);
+               }
+               smbchg_charging_en(chip, 1);
+       }
+}
+
+static int smbchg_charging_en(struct s2mu00x_battery_info *chip, bool en)
+{
+       bool is_charging = S2MU00X_BAT_CHG_MODE_CHARGING_OFF;
+
+       if ((chip->charging_limit_modes == CHARGING_LIMIT_RUN)
+               && (chip->enable_charging_limit)
+               && (chip->is_factory_image))
+               en = 0;
+
+       if (en == true) {
+               if ((!chip->ext_high_temp) &&
+               (chip->temp_state != POWER_SUPPLY_HEALTH_COLD) &&
+               (chip->temp_state != POWER_SUPPLY_HEALTH_OVERHEAT) &&
+               (chip->stepchg_state != STEP_FULL))
+                       is_charging = S2MU00X_BAT_CHG_MODE_CHARGING;
+               else {
+                       pr_info("Enable conflict! ext_high_temp: %d,temp_state: %d,step_chg_state %d\n",
+                               chip->ext_high_temp, chip->temp_state,
+                               chip->stepchg_state);
+                       return -EINVAL;
+               }
+       }
+
+               /*
+        * 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.
+        */
+       pr_info("%s, mode:%d", __func__, is_charging);
+       set_charger_mode(chip, is_charging);
+
+       return 1;
+}
+
+void update_charging_limit_modes(struct s2mu00x_battery_info *chip,
+               int batt_soc)
+{
+       enum charging_limit_modes charging_limit_modes
+                                               = chip->charging_limit_modes;
+
+
+       if ((charging_limit_modes != CHARGING_LIMIT_RUN)
+               && (batt_soc >= chip->upper_limit_capacity)) {
+               charging_limit_modes = CHARGING_LIMIT_RUN;
+       } else if ((charging_limit_modes != CHARGING_LIMIT_OFF)
+                       && (batt_soc <= chip->lower_limit_capacity)) {
+               charging_limit_modes = CHARGING_LIMIT_OFF;
+       }
+
+       pr_info("%s: charging_limit_modes:%d\n", __func__, chip->charging_limit_modes);
+
+       if (charging_limit_modes != chip->charging_limit_modes) {
+               chip->charging_limit_modes = charging_limit_modes;
+
+               if (charging_limit_modes == CHARGING_LIMIT_RUN)
+                       smbchg_charging_en(chip, 0);
+               else
+                       smbchg_charging_en(chip, 1);
+       }
+}
+
+static int set_property_on_otg(struct s2mu00x_battery_info *chip,
+               enum power_supply_property prop, int val)
+{
+       int rc;
+       union power_supply_propval ret = {0, };
+
+       if (!chip->otg_psy)
+               chip->otg_psy =
+                       power_supply_get_by_name("otg");
+       if (!chip->otg_psy) {
+               pr_smb(PR_STATUS, "no otg psy found\n");
+               return -EINVAL;
+       }
+
+       ret.intval = val;
+       rc = power_supply_set_property(chip->otg_psy, prop, &ret);
+       if (rc)
+               pr_smb(PR_STATUS,
+                       "otg psy does not allow updating prop %d rc = %d\n",
+                       prop, rc);
+
+       return rc;
+}
+
+static int get_property_from_otg(struct s2mu00x_battery_info *chip,
+               enum power_supply_property prop, int *val)
+{
+       int rc;
+       union power_supply_propval ret = {0, };
+
+       if (!chip->otg_psy)
+               chip->otg_psy =
+                       power_supply_get_by_name("otg");
+       if (!chip->otg_psy) {
+               pr_smb(PR_STATUS, "no otg psy found\n");
+               return -EINVAL;
+       }
+
+       rc = power_supply_get_property(chip->otg_psy, prop, &ret);
+       if (rc) {
+               pr_smb(PR_STATUS,
+                       "otg psy doesn't support reading prop %d rc = %d\n",
+                       prop, rc);
+               return rc;
+       }
+
+       *val = ret.intval;
+       return rc;
+}
+
+static int set_property_on_charger(struct s2mu00x_battery_info *chip,
+               enum power_supply_property prop, int val)
+{
+       int rc;
+       union power_supply_propval ret = {0, };
+
+       if (!chip->charger_psy && chip->pdata->charger_name)
+               chip->charger_psy =
+                       power_supply_get_by_name((char *)chip->pdata->charger_name);
+       if (!chip->charger_psy) {
+               pr_smb(PR_STATUS, "no charger psy found\n");
+               return -EINVAL;
+       }
+
+       ret.intval = val;
+       rc = power_supply_set_property(chip->charger_psy, prop, &ret);
+       if (rc)
+               pr_smb(PR_STATUS,
+                       "charger psy does not allow updating prop %d rc = %d\n",
+                       prop, rc);
+
+       return rc;
+}
+static int get_property_from_charger(struct s2mu00x_battery_info *chip,
+               enum power_supply_property prop, int *val)
+{
+       int rc;
+       union power_supply_propval ret = {0, };
+
+       if (!chip->charger_psy && chip->pdata->charger_name)
+               chip->charger_psy =
+                       power_supply_get_by_name((char *)chip->pdata->charger_name);
+       if (!chip->charger_psy) {
+               pr_smb(PR_STATUS, "no charger psy found\n");
+               return -EINVAL;
+       }
+
+       rc = power_supply_get_property(chip->charger_psy, prop, &ret);
+       if (rc) {
+               pr_smb(PR_STATUS,
+                       "charger psy doesn't support reading prop %d rc = %d\n",
+                       prop, rc);
+               return rc;
+       }
+
+       *val = ret.intval;
+       return rc;
+}
+
+#if 0
+static int set_property_on_fg(struct s2mu00x_battery_info *chip,
+               enum power_supply_property prop, int val)
+{
+       int rc;
+       union power_supply_propval ret = {0, };
+
+       if (!chip->bms_psy && chip->pdata->fuelgauge_name)
+               chip->bms_psy =
+                       power_supply_get_by_name((char *)chip->pdata->fuelgauge_name);
+       if (!chip->bms_psy) {
+               pr_smb(PR_STATUS, "no bms psy found\n");
+               return -EINVAL;
+       }
+
+       ret.intval = val;
+       rc = power_supply_set_property(chip->bms_psy, prop, &ret);
+       if (rc)
+               pr_smb(PR_STATUS,
+                       "bms psy does not allow updating prop %d rc = %d\n",
+                       prop, rc);
+
+       return rc;
+}
+#endif
+
+static int get_property_from_fg(struct s2mu00x_battery_info *chip,
+               enum power_supply_property prop, int *val)
+{
+       int rc;
+       union power_supply_propval ret = {0, };
+
+       if (!chip->bms_psy && chip->pdata->fuelgauge_name)
+               chip->bms_psy =
+                       power_supply_get_by_name((char *)chip->pdata->fuelgauge_name);
+       if (!chip->bms_psy) {
+               pr_smb(PR_STATUS, "no bms psy found\n");
+               return -EINVAL;
+       }
+       rc = power_supply_get_property(chip->bms_psy, prop, &ret);
+       if (rc) {
+               pr_smb(PR_STATUS,
+                       "bms psy doesn't support reading prop %d rc = %d\n",
+                       prop, rc);
+               return rc;
+       }
+
+       *val = ret.intval;
+       return rc;
+}
+
+#define DEFAULT_BATT_CAPACITY  50
+static int get_prop_batt_capacity(struct s2mu00x_battery_info *chip)
+{
+       int capacity, rc;
+
+       if (chip->test_mode && !(chip->test_mode_soc < 0)
+           && !(chip->test_mode_soc > 100))
+               return chip->test_mode_soc;
+
+       rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CAPACITY, &capacity);
+       if (rc) {
+               pr_smb(PR_STATUS, "Couldn't get capacity rc = %d\n", rc);
+               capacity = DEFAULT_BATT_CAPACITY;
+       }
+       return capacity;
+}
+
+#define DEFAULT_BATT_TEMP              200
+#define GLITCH_BATT_TEMP               600
+#define ERROR_BATT_TEMP                597
+static int get_prop_batt_temp(struct s2mu00x_battery_info *chip)
+{
+       int temp, rc;
+
+       rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_TEMP, &temp);
+       if (rc) {
+               pr_smb(PR_STATUS, "Couldn't get temperature rc = %d\n", rc);
+               temp = DEFAULT_BATT_TEMP;
+       }
+
+       return temp;
+}
+
+#define DEFAULT_BATT_CURRENT_NOW       0
+static int get_prop_batt_current_now(struct s2mu00x_battery_info *chip)
+{
+       int ua, rc;
+
+       rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_CURRENT_NOW, &ua);
+       if (rc) {
+               pr_smb(PR_STATUS, "Couldn't get current rc = %d\n", rc);
+               ua = DEFAULT_BATT_CURRENT_NOW;
+       }
+       return ua;
+}
+
+#define DEFAULT_BATT_VOLTAGE_NOW       0
+static int get_prop_batt_voltage_now(struct s2mu00x_battery_info *chip)
+{
+       int uv, rc;
+
+       rc = get_property_from_fg(chip, POWER_SUPPLY_PROP_VOLTAGE_NOW, &uv);
+       if (rc) {
+               pr_smb(PR_STATUS, "Couldn't get voltage rc = %d\n", rc);
+               uv = DEFAULT_BATT_VOLTAGE_NOW;
+       }
+       return uv;
+}
+
+//true -keep sys on, charging depend on is_charging.
+//false-suspend usb.
+static int smbchg_usb_en(struct s2mu00x_battery_info *chip, bool enable)
+{
+       if (enable)
+               set_battery_status(chip, POWER_SUPPLY_STATUS_CHARGING);
+       else
+               set_battery_status(chip, POWER_SUPPLY_STATUS_NOT_CHARGING);
+
+       return 1;
+}
+
+static int smbchg_get_pchg_current_map_index(struct s2mu00x_battery_info *chip)
+{
+       int i;
+
+       for (i = 0; i < chip->pchg_current_map_len; i++) {
+               if (chip->target_fastchg_current_ma >=
+                   chip->pchg_current_map_data[i].requested) {
+                       break;
+               }
+       }
+
+       if (i >= chip->pchg_current_map_len)
+               i = (chip->pchg_current_map_len - 1);
+
+       return i;
+}
+static void set_max_allowed_current_ma(struct s2mu00x_battery_info *chip,
+                                      int current_ma)
+{
+       if (!chip->usb_present) {
+               pr_smb(PR_STATUS, "NO allowed current, No USB\n");
+               chip->target_fastchg_current_ma = current_ma;
+               return;
+       }
+
+       chip->target_fastchg_current_ma =
+               min(current_ma, chip->allowed_fastchg_current_ma);
+       chip->target_fastchg_current_ma =
+               min(chip->target_fastchg_current_ma,
+               chip->temp_allowed_fastchg_current_ma);
+
+       pr_smb(PR_STATUS, "requested=%d: allowed=%d: temp_step=%d: result=%d\n",
+              current_ma, chip->allowed_fastchg_current_ma,
+              chip->temp_allowed_fastchg_current_ma,
+              chip->target_fastchg_current_ma);
+}
+
+#define HEARTBEAT_DELAY_MS 30000
+#define HEARTBEAT_HOLDOFF_MS 10000
+#define STEPCHG_MAX_FV_COMP 60
+#define STEPCHG_ONE_FV_COMP 40
+#define STEPCHG_FULL_FV_COMP 100
+#define STEPCHG_CURR_ADJ 200
+#define DEMO_MODE_HYS_SOC 5
+#define HYST_STEP_MV 50
+static void smbchg_heartbeat_work(struct work_struct *work)
+{
+       struct s2mu00x_battery_info *chip = container_of(work,
+                                               struct s2mu00x_battery_info,
+                                               heartbeat_work.work);
+       int batt_mv;
+       int batt_ma;
+       int batt_soc;
+       int batt_temp;
+       int prev_batt_health;
+       int prev_ext_lvl;
+       int prev_step;
+       int index;
+       int state;
+
+       pr_info("%s: start heatbeat\n", __func__);
+
+       smbchg_stay_awake(chip);
+       //dump register of charger
+       get_property_from_charger(chip, POWER_SUPPLY_PROP_PRESENT, &state);
+
+       batt_mv = get_prop_batt_voltage_now(chip) ;
+       batt_ma = get_prop_batt_current_now(chip) / 1000;
+       batt_soc = get_prop_batt_capacity(chip);
+
+       batt_temp = get_prop_batt_temp(chip) / 10;
+       pr_info("%s, batt=%d mV, %d mA, %d C ,soc = %d\n",
+               __func__, batt_mv, batt_ma, batt_temp, batt_soc);
+
+       if ((chip->enable_charging_limit) && (chip->is_factory_image))
+               update_charging_limit_modes(chip, batt_soc);
+
+       prev_step = chip->stepchg_state;
+
+       if (chip->demo_mode) {
+               static int demo_full_soc = 100;
+               bool voltage_full = false;
+
+               if (batt_ma < 0)
+                       batt_ma *= -1;
+               if (/*(!!!(chip->usb_suspended & REASON_DEMO)) &&*/
+                   ((batt_mv + HYST_STEP_MV) >= DEMO_MODE_VOLTAGE) &&
+                   (batt_ma <= chip->stepchg_iterm_ma) &&
+                   (chip->allowed_fastchg_current_ma >=
+                    chip->stepchg_iterm_ma)) {
+                       if (chip->stepchg_state_holdoff >= 2) {
+                               voltage_full = true;
+                               chip->stepchg_state_holdoff = 0;
+                       } else
+                               chip->stepchg_state_holdoff++;
+               } else {
+                       chip->stepchg_state_holdoff = 0;
+               }
+
+               chip->stepchg_state = STEP_NONE;
+               pr_warn("Battery in Demo Mode charging Limited per%d\n",
+                        chip->demo_mode);
+               if (/*(!!!(chip->usb_suspended & REASON_DEMO)) &&*/
+                   ((batt_soc >= chip->demo_mode) ||
+                    voltage_full)) {
+                       demo_full_soc = batt_soc;
+                       smbchg_usb_en(chip, false);
+                       pr_warn("Battery in Demo Mode charging false\n");
+               } else if (/*!!(chip->usb_suspended & REASON_DEMO) &&*/
+                       (batt_soc <=
+                        (demo_full_soc - DEMO_MODE_HYS_SOC))) {
+                       smbchg_usb_en(chip, true);
+                       chip->stepchg_state_holdoff = 0;
+                       pr_warn("Battery in Demo Mode charging true\n");
+               }
+               smbchg_set_temp_chgpath(chip, chip->temp_state);
+       } else if ((chip->stepchg_state == STEP_NONE) && (chip->usb_present)) {
+               if (batt_mv >= chip->stepchg_voltage_mv)
+                       chip->stepchg_state = STEP_ONE;
+               else
+                       chip->stepchg_state = STEP_MAX;
+               chip->stepchg_state_holdoff = 0;
+       } else if ((chip->stepchg_state == STEP_MAX) &&
+                   (chip->usb_present) &&
+                  ((batt_mv + HYST_STEP_MV) >= chip->stepchg_voltage_mv)) {
+                       if (batt_ma < 0)
+                               batt_ma *= -1;
+
+               index = smbchg_get_pchg_current_map_index(chip);
+               if (chip->pchg_current_map_data[index].primary ==
+                   chip->stepchg_current_ma)
+                       batt_ma -= STEPCHG_CURR_ADJ;
+
+               if (batt_ma <= min(chip->stepchg_current_ma,
+                   chip->allowed_fastchg_current_ma))
+                       if (chip->stepchg_state_holdoff >= 2) {
+                                       chip->stepchg_state = STEP_ONE;
+                                       chip->stepchg_state_holdoff = 0;
+                       } else
+                               chip->stepchg_state_holdoff++;
+               else
+                       chip->stepchg_state_holdoff = 0;
+       } else if ((chip->stepchg_state == STEP_ONE) &&
+                  (batt_ma < 0) && (chip->usb_present) &&
+                  ((batt_mv + HYST_STEP_MV) >=
+                   chip->stepchg_max_voltage_mv)) {
+               batt_ma *= -1;
+               if (batt_ma <= min(chip->stepchg_taper_ma,
+                   chip->allowed_fastchg_current_ma))
+                       if (chip->stepchg_state_holdoff >= 2) {
+                               chip->stepchg_state = STEP_TAPER;
+                               chip->stepchg_state_holdoff = 0;
+                       } else
+                               chip->stepchg_state_holdoff++;
+               else
+                       chip->stepchg_state_holdoff = 0;
+       } else if ((chip->stepchg_state == STEP_TAPER) &&
+                  (batt_ma < 0) && (chip->usb_present)) {
+               batt_ma *= -1;
+               if ((batt_soc >= 100) &&
+                   (batt_ma <= chip->stepchg_iterm_ma) &&
+                   (chip->allowed_fastchg_current_ma >=
+                    chip->stepchg_iterm_ma))
+                       if (chip->stepchg_state_holdoff >= 2) {
+                               chip->stepchg_state = STEP_FULL;
+                               chip->stepchg_state_holdoff = 0;
+                       } else
+                               chip->stepchg_state_holdoff++;
+               else
+                       chip->stepchg_state_holdoff = 0;
+       }  else if ((chip->stepchg_state == STEP_FULL) &&
+                   (chip->usb_present) && (batt_soc < 100)) {
+               chip->stepchg_state = STEP_TAPER;
+       } else if (!chip->usb_present) {
+               chip->stepchg_state = STEP_NONE;
+               chip->stepchg_state_holdoff = 0;
+       } else
+               chip->stepchg_state_holdoff = 0;
+
+       switch (chip->stepchg_state) {
+       case STEP_FULL:
+       case STEP_TAPER:
+               //if (smbchg_hvdcp_det_check(chip) &&
+               //    (chip->usb_target_current_ma != HVDCP_ICL_TAPER)) {
+               //      mutex_lock(&chip->current_change_lock);
+               //      chip->usb_target_current_ma = HVDCP_ICL_TAPER;
+               //      mutex_unlock(&chip->current_change_lock);
+               //}
+               chip->vfloat_mv = chip->stepchg_max_voltage_mv;
+               chip->vfloat_parallel_mv =
+                       chip->stepchg_max_voltage_mv - STEPCHG_FULL_FV_COMP;
+               set_max_allowed_current_ma(chip, chip->stepchg_current_ma);
+               break;
+       case STEP_ONE:
+       case STEP_NONE:
+               chip->vfloat_mv =
+                       chip->stepchg_max_voltage_mv;
+               chip->vfloat_parallel_mv = chip->stepchg_max_voltage_mv;
+               set_max_allowed_current_ma(chip, chip->stepchg_current_ma);
+               break;
+       case STEP_MAX:
+               chip->vfloat_mv =
+                       chip->stepchg_voltage_mv;
+               chip->vfloat_parallel_mv =
+                       chip->stepchg_voltage_mv + STEPCHG_MAX_FV_COMP;
+               set_max_allowed_current_ma(chip, chip->stepchg_max_current_ma);
+               break;
+       default:
+               break;
+       }
+
+       pr_info("%s, Step State = %d,vfloat:%d,current:%d\n",
+               __func__, (int)chip->stepchg_state, chip->vfloat_mv, chip->target_fastchg_current_ma);
+
+       prev_batt_health = chip->temp_state;
+       smbchg_check_temp_state(chip, batt_temp);
+       prev_ext_lvl = chip->ext_high_temp;
+       smbchg_check_temp_range(chip, batt_mv, batt_soc,
+                               chip->temp_state, prev_batt_health);
+
+       if ((prev_batt_health != chip->temp_state) ||
+           (prev_ext_lvl != chip->ext_high_temp) ||
+           (prev_step != chip->stepchg_state) ||
+           (chip->update_allowed_fastchg_current_ma)) {
+               pr_info("%s, temp state: %d\n",
+                       __func__, chip->temp_state);
+               smbchg_set_temp_chgpath(chip, prev_batt_health);
+               if (chip->stepchg_state == STEP_MAX)
+                       set_max_allowed_current_ma(chip,
+                                     chip->stepchg_max_current_ma);
+               else
+                       set_max_allowed_current_ma(chip,
+                                     chip->stepchg_current_ma);
+
+               pr_info("%s, target_fastchg_current_ma:%d\n", __func__, chip->target_fastchg_current_ma);
+               if (is_usb_present(chip)) {
+                       pr_info("%s, usb\n",__func__);
+                       //need test usb charging.
+                       chip->update_allowed_fastchg_current_ma = false;
+               } else if (is_dc_present(chip)) {
+                       pr_info("%s, dc,current:%d\n", __func__, chip->target_fastchg_current_ma);
+                       set_property_on_charger(chip, POWER_SUPPLY_PROP_CURRENT_NOW, chip->target_fastchg_current_ma);
+               }
+       }
+
+       pr_info("%s, end heartbeat\n", __func__);
+       //dump register of charger
+       get_property_from_charger(chip, POWER_SUPPLY_PROP_PRESENT, &state);
+
+//end_hb:
+       power_supply_changed(chip->psy_battery);
+
+       if (!chip->stepchg_state_holdoff)
+               schedule_delayed_work(&chip->heartbeat_work,
+                                     msecs_to_jiffies(HEARTBEAT_DELAY_MS));
+       else
+               schedule_delayed_work(&chip->heartbeat_work,
+                                     msecs_to_jiffies(HEARTBEAT_HOLDOFF_MS));
+
+       smbchg_relax(chip);
+}
+
+static bool smbchg_charger_mmi_factory(void)
+{
+       struct device_node *np = of_find_node_by_path("/chosen");
+       bool factory = false;
+
+       if (np)
+               factory = of_property_read_bool(np, "mmi,factory-cable");
+
+       of_node_put(np);
+
+       return factory;
+}
+
+static bool qpnp_smbcharger_test_mode(void)
+{
+       struct device_node *np = of_find_node_by_path("/chosen");
+       const char *mode;
+       int rc;
+       bool test = false;
+
+       if (!np)
+               return test;
+
+       rc = of_property_read_string(np, "mmi,battery", &mode);
+       if ((rc >= 0) && mode) {
+               if (strcmp(mode, "test") == 0)
+                       test = true;
+       }
+       of_node_put(np);
+
+       return test;
+}
+#if 0
+static int smbchg_reboot(struct notifier_block *nb,
+                        unsigned long event, void *unused)
+{
+       struct s2mu00x_battery_info *chip =
+                       container_of(nb, struct s2mu00x_battery_info, smb_reboot);
+
+       dev_dbg(chip->dev, "SMB Reboot\n");
+       if (!chip) {
+               dev_warn(chip->dev, "called before chip valid!\n");
+               return NOTIFY_DONE;
+       }
+
+       switch (event) {
+       case SYS_POWER_OFF:
+               /* Disable Charging */
+               smbchg_charging_en(chip, 0);
+
+               /* Suspend USB and DC */
+//             smbchg_usb_suspend(chip, true);
+//             smbchg_dc_suspend(chip, true);
+               smbchg_usb_en(chip, 0);
+//             s2mu106_set_buck(chip, 0);
+
+               power_supply_set_present(chip->psy_chg, 0);
+               power_supply_set_chg_present(chip->psy_chg, 0);
+               power_supply_set_online(chip->psy_chg, 0);
+
+               if (!chip->factory_mode)
+                       break;
+
+               while (is_usb_present(chip))
+                       msleep(100);
+
+               dev_warn(chip->dev, "VBUS UV wait 1 sec!\n");
+               /* Delay 1 sec to allow more VBUS decay */
+               msleep(1000);
+               break;
+       default:
+               break;
+       }
+       return NOTIFY_DONE;
+}
+#endif
+
+#define DEFAULT_TEST_MODE_SOC  52
+#define DEFAULT_TEST_MODE_TEMP  225
+static int factory_kill_disable;
+module_param(factory_kill_disable, int, 0644);
+void factory_usb_shutdown(struct s2mu00x_battery_info *chip)
+{
+
+       if (chip->factory_cable) {
+               if (!factory_kill_disable) {
+                       printk(KERN_ERR "SMB - Factory Cable removed, power-off\n");
+                       kernel_power_off();
+               } else
+                       pr_err("SMB - Factory cable removed - kill disabled\n");
+               chip->factory_cable = false;
+       }
+}
+
+static void soc_control_worker(struct work_struct *work)
+{
+       struct s2mu00x_battery_info *battery =
+               container_of(work, struct s2mu00x_battery_info, soc_control.work);
+
+       pr_info("%s: S2MU00x battery capacity = %d, status = %d\n",
+               __func__, battery->capacity, battery->status);
+
+       if ((battery->capacity >= 75) && (battery->status == POWER_SUPPLY_STATUS_CHARGING)) {
+               pr_info("%s: Capacity is more than 75, stop charging\n", __func__);
+               set_battery_status(battery, POWER_SUPPLY_STATUS_DISCHARGING);
+       }
+
+       queue_delayed_work(battery->monitor_wqueue, &battery->soc_control, 10*HZ);
+}
+
+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
+       int rc;
+
+       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);
+               pr_info("%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;
+#if defined(CONFIG_USE_CCIC)
+       battery->pdo_max_input_vol = battery->pdata->pdo_max_input_vol;
+       battery->pdo_max_chg_power = battery->pdata->pdo_max_chg_power;
+#endif
+       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;
+#if defined(CHARGER_S2MU106)
+       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;
+#else
+       battery->battery_valid = true;
+#endif
+       /* 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.property_is_writeable =  s2mu00x_battery_property_is_writeable;
+       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;
+
+#if defined(CONFIG_USE_CCIC)
+       INIT_DELAYED_WORK(&battery->select_pdo_work, usbpd_select_pdo_work);
+#endif
+       /* 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__);
@@ -2224,6 +4301,7 @@ static int s2mu00x_battery_probe(struct platform_device *pdev)
                pr_err("%s: Fail to execute property\n", __func__);
 
 
+#if 0
 #if defined(CONFIG_IFCONN_NOTIFIER)
        ifconn_notifier_register(&battery->ifconn_nb,
                        s2mu00x_ifconn_handle_notification,
@@ -2242,6 +4320,7 @@ static int s2mu00x_battery_probe(struct platform_device *pdev)
        /* Kick off monitoring thread */
        pr_info("%s: start battery monitoring work\n", __func__);
        queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 5*HZ);
+#endif
 
        if ((is_charging_mode == S2MU00X_POWEROFF_CHG_MODE) ||
                (is_charging_mode == S2MU00X_NOR_MODE)) {
@@ -2256,8 +4335,133 @@ static int s2mu00x_battery_probe(struct platform_device *pdev)
        }
 
        dev_info(battery->dev, "%s: Battery driver is loaded\n", __func__);
+
+       the_chip = battery;
+       wake_lock_init(&battery->heartbeat_wake_lock, WAKE_LOCK_SUSPEND,
+                       "heartbeat suspend");
+       INIT_DELAYED_WORK(&battery->heartbeat_work,
+                         smbchg_heartbeat_work);
+
+       battery->factory_mode = smbchg_charger_mmi_factory();
+       if (battery->factory_mode) {
+               pr_err("Entering Factory Mode SMB Writes Disabled\n");
+       }
+       battery->demo_mode = 0;
+//     battery->hvdcp_det_done = false;
+       battery->is_factory_image = false;
+       battery->charging_limit_modes = CHARGING_LIMIT_UNKNOWN;
+       battery->test_mode_soc = DEFAULT_TEST_MODE_SOC;
+       battery->test_mode_temp = DEFAULT_TEST_MODE_TEMP;
+       battery->test_mode = qpnp_smbcharger_test_mode();
+       battery->max_chrg_temp = 0;
+       if (battery->test_mode)
+               pr_err("Test Mode Enabled\n");
+       battery->is_weak_charger = false;
+       battery->usb_online = -EINVAL;
+       battery->stepchg_state = STEP_NONE;
+       rc = device_create_file(battery->dev,
+                               &dev_attr_force_demo_mode);
+       if (rc) {
+               pr_err("couldn't create force_demo_mode\n");
+               goto unregister_dc_psy;
+       }
+
+       rc = device_create_file(battery->dev,
+                               &dev_attr_factory_image_mode);
+       if (rc) {
+               pr_err("couldn't create factory_image_mode\n");
+               goto unregister_dc_psy;
+       }
+
+       rc = device_create_file(battery->dev,
+                               &dev_attr_factory_charge_upper);
+       if (rc) {
+               pr_err("couldn't create factory_charge_upper\n");
+               goto unregister_dc_psy;
+       }
+
+       rc = device_create_file(battery->dev,
+                               &dev_attr_force_max_chrg_temp);
+       if (rc) {
+               pr_err("couldn't create force_max_chrg_temp\n");
+               goto unregister_dc_psy;
+       }
+
+       if (battery->factory_mode) {
+               rc = device_create_file(battery->dev,
+                                       &dev_attr_force_chg_usb_suspend);
+               if (rc) {
+                       pr_err("couldn't create force_chg_usb_suspend\n");
+                       goto unregister_dc_psy;
+               }
+
+               rc = device_create_file(battery->dev,
+                                       &dev_attr_force_chg_fail_clear);
+               if (rc) {
+                       pr_err("couldn't create force_chg_fail_clear\n");
+                       goto unregister_dc_psy;
+               }
+
+               rc = device_create_file(battery->dev,
+                                       &dev_attr_force_chg_auto_enable);
+               if (rc) {
+                       pr_err("couldn't create force_chg_auto_enable\n");
+                       goto unregister_dc_psy;
+               }
+
+               rc = device_create_file(battery->dev,
+                               &dev_attr_force_chg_ibatt);
+               if (rc) {
+                       pr_err("couldn't create force_chg_ibatt\n");
+                       goto unregister_dc_psy;
+               }
+
+               rc = device_create_file(battery->dev,
+                                       &dev_attr_force_chg_iusb);
+               if (rc) {
+                       pr_err("couldn't create force_chg_iusb\n");
+                       goto unregister_dc_psy;
+               }
+
+               rc = device_create_file(battery->dev,
+                                       &dev_attr_force_chg_itrick);
+               if (rc) {
+                       pr_err("couldn't create force_chg_itrick\n");
+                       goto unregister_dc_psy;
+               }
+
+               rc = device_create_file(battery->dev,
+                               &dev_attr_force_chg_usb_otg_ctl);
+               if (rc) {
+                       pr_err("couldn't create force_chg_usb_otg_ctl\n");
+                       goto unregister_dc_psy;
+               }
+       }
+
+       pr_info("%s: moto Battery driver is loaded\n", __func__);
+
+       #if defined(CONFIG_IFCONN_NOTIFIER)
+       ifconn_notifier_register(&battery->ifconn_nb,
+                       s2mu00x_ifconn_handle_notification,
+                       IFCONN_NOTIFY_BATTERY,
+                       IFCONN_NOTIFY_MANAGER);
+#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);
+
+       schedule_delayed_work(&battery->heartbeat_work,
+                             msecs_to_jiffies(0));
+
        return 0;
 
+unregister_dc_psy:
+       wake_lock_destroy(&battery->heartbeat_wake_lock);
 err_unreg_usb:
        power_supply_unregister(battery->psy_usb);
 err_unreg_battery:
@@ -2279,6 +4483,41 @@ err_bat_free:
 
 static int s2mu00x_battery_remove(struct platform_device *pdev)
 {
+//     struct s2mu00x_battery_info *battery = dev_get_drvdata(pdev);
+       if (the_chip == NULL)
+               printk(KERN_ERR "%s, the chip null !!", __func__);
+
+       device_remove_file(the_chip->dev,
+                          &dev_attr_force_demo_mode);
+       device_remove_file(the_chip->dev,
+                          &dev_attr_factory_image_mode);
+       device_remove_file(the_chip->dev,
+                          &dev_attr_factory_charge_upper);
+       device_remove_file(the_chip->dev,
+                          &dev_attr_force_max_chrg_temp);
+       if (the_chip->factory_mode) {
+               device_remove_file(the_chip->dev,
+                                  &dev_attr_force_chg_usb_suspend);
+               device_remove_file(the_chip->dev,
+                                  &dev_attr_force_chg_fail_clear);
+               device_remove_file(the_chip->dev,
+                                  &dev_attr_force_chg_auto_enable);
+               device_remove_file(the_chip->dev,
+                                  &dev_attr_force_chg_ibatt);
+               device_remove_file(the_chip->dev,
+                                  &dev_attr_force_chg_iusb);
+               device_remove_file(the_chip->dev,
+                                  &dev_attr_force_chg_itrick);
+               device_remove_file(the_chip->dev,
+                                  &dev_attr_force_chg_usb_otg_ctl);
+       }
+
+       power_supply_unregister(the_chip->psy_battery);
+       power_supply_unregister(the_chip->psy_usb);
+       power_supply_unregister(the_chip->psy_ac);
+//     smbchg_regulator_deinit(chip);
+//     wakeup_source_trash(&chip->smbchg_wake_source);
+
        return 0;
 }
 
@@ -2322,6 +4561,7 @@ static void s2mu00x_battery_complete(struct device *dev)
        }
        alarm_cancel(&battery->monitor_alarm);
        wake_lock(&battery->monitor_wake_lock);
+//     wake_lock(&battery->heartbeat_wake_lock);       
        queue_delayed_work(battery->monitor_wqueue, &battery->monitor_work, 0);
 }
 
index 5f5698bcdc97680d8d3b1cbd1e5d17d7922937c6..176e5e012e8d99ff357fe34174eb038f9d966e80 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/mfd/samsung/s2mu106.h>
 #include <linux/power/s2mu106_charger.h>
 #include <linux/version.h>
+#include <linux/power_supply.h>
 
 static char *s2mu106_supplied_to[] = {
        "battery",
@@ -68,6 +69,25 @@ static void s2mu106_test_read(struct i2c_client *i2c)
 
 }
 
+static void s2mu106_dump_register(struct s2mu106_charger_data *charger)
+{
+       u8 data = 0;
+       int i;
+       char str[1016] = {0,};
+
+       for (i = 0; i < 25; i++) {
+               s2mu106_read_reg(charger->i2c, S2MU106_CHG_CTRL0+i, &data);
+               sprintf(str+strlen(str), "CTRL%d:0x%02x, ", i, data);
+       }
+
+       for (i = 0; i < 5; i++) {
+               s2mu106_read_reg(charger->i2c, S2MU106_CHG_STATUS0+i, &data);
+               sprintf(str+strlen(str), "STUS%d:0x%02x, ", i, data);
+       }
+
+       pr_err("%s: %s\n", __func__, str);
+}
+
 static int s2mu106_charger_otg_control(
                struct s2mu106_charger_data *charger, bool enable)
 {
@@ -113,6 +133,7 @@ out:
        return enable;
 }
 
+
 static void s2mu106_enable_charger_switch(
        struct s2mu106_charger_data *charger, int onoff)
 {
@@ -532,6 +553,7 @@ static int s2mu106_chg_get_property(struct power_supply *psy,
                break;
        case POWER_SUPPLY_PROP_PRESENT:
                val->intval = s2mu106_get_batt_present(charger);
+               s2mu106_dump_register(charger);
                break;
        case POWER_SUPPLY_PROP_CHARGING_ENABLED:
                val->intval = charger->is_charging;
@@ -883,8 +905,7 @@ static irqreturn_t s2mu106_ovp_isr(int irq, void *data)
 
        return IRQ_HANDLED;
 }
-
-static int s2mu106_charger_parse_dt(struct device *dev,
+static int s2mu106_charger_parse_dt(struct platform_device *pdev,
                struct s2mu106_charger_platform_data *pdata)
 {
        struct device_node *np = of_find_node_by_name(NULL, "s2mu106-charger");
@@ -968,7 +989,6 @@ static const struct of_device_id s2mu106_charger_match_table[] = {
        { .compatible = "samsung,s2mu106-charger",},
        {},
 };
-
 static int s2mu106_charger_probe(struct platform_device *pdev)
 {
        struct s2mu106_dev *s2mu106 = dev_get_drvdata(pdev->dev.parent);
@@ -994,7 +1014,7 @@ static int s2mu106_charger_probe(struct platform_device *pdev)
                ret = -ENOMEM;
                goto err_parse_dt_nomem;
        }
-       ret = s2mu106_charger_parse_dt(&pdev->dev, charger->pdata);
+       ret = s2mu106_charger_parse_dt(pdev, charger->pdata);
        if (ret < 0)
                goto err_parse_dt;
 
@@ -1166,7 +1186,6 @@ static int s2mu106_charger_remove(struct platform_device *pdev)
 {
        struct s2mu106_charger_data *charger =
                platform_get_drvdata(pdev);
-
        power_supply_unregister(charger->psy_chg);
        mutex_destroy(&charger->charger_mutex);
        kfree(charger);
index 5d5920ea94071ce88508c11f6b04444989b25355..e63ca3350d671c598342abf9e6e1f7c748e444c5 100755 (executable)
@@ -183,6 +183,125 @@ static void s2mu106_fg_test_read(struct i2c_client *client)
        pr_info("[FG]%s: %s\n", __func__, str);
 }
 
+static void WA_0_issue_at_init(struct s2mu106_fuelgauge_data *fuelgauge)
+{
+       int a = 0;
+       u8 v_52 = 0, v_53 = 0, temp1, temp2;
+       int FG_volt, UI_volt, offset;
+       u8 v_40 = 0;
+       u8 temp_REG26 = 0, temp_REG27 = 0, temp = 0;
+
+       /* Step 1: [Surge test] get UI voltage (0.1mV)*/
+       UI_volt = s2mu106_get_ocv(fuelgauge);
+
+       /* current fix for soc */
+       s2mu106_read_reg_byte(fuelgauge->i2c, 0x27, &temp_REG27);
+       temp = temp_REG27;
+       temp |= 0x0F;
+       s2mu106_write_and_verify_reg_byte(fuelgauge->i2c, 0x27, temp);
+
+       s2mu106_read_reg_byte(fuelgauge->i2c, 0x26, &temp_REG26);
+       s2mu106_write_and_verify_reg_byte(fuelgauge->i2c, 0x26, 0xF7);
+
+       /* avgvbat factor value set to 0xFF */
+       s2mu106_read_reg_byte(fuelgauge->i2c, 0x40, &v_40);
+       s2mu106_write_and_verify_reg_byte(fuelgauge->i2c, 0x40, 0xFF);
+
+       s2mu106_write_and_verify_reg_byte(fuelgauge->i2c, 0x1E, 0x0F);
+       msleep(50);
+
+       /* Step 2: [Surge test] get FG voltage (0.1mV) */
+       FG_volt = s2mu106_get_vbat(fuelgauge) * 10;
+
+       /* Step 3: [Surge test] get offset */
+       offset = UI_volt - FG_volt;
+       pr_info("%s: UI_volt(%d), FG_volt(%d), offset(%d)\n",
+                       __func__, UI_volt, FG_volt, offset);
+
+       /* Step 4: [Surge test] */
+       s2mu106_read_reg_byte(fuelgauge->i2c, 0x53, &v_53);
+       s2mu106_read_reg_byte(fuelgauge->i2c, 0x52, &v_52);
+       pr_info("%s: v_53(0x%x), v_52(0x%x)\n", __func__, v_53, v_52);
+
+       a = (v_53 & 0x0F) << 8;
+       a += v_52;
+       pr_info("%s: a before add offset (0x%x)\n", __func__, a);
+
+       /* 2`s complement */
+       if (a & (0x01 << 11))
+               a = (-10000 * ((a^0xFFF) + 1)) >> 13;
+       else
+               a = (10000 * a) >> 13;
+
+       a = a + offset;
+       pr_err("%s: a after add offset (0x%x)\n", __func__, a);
+
+       /* limit upper/lower offset */
+       if (a > 2490)
+               a = 2490;
+
+       if (a < (-2490))
+               a = -2490;
+
+       a = (a << 13) / 10000;
+       if (a < 0)
+               a = -1*((a^0xFFF)+1);
+
+       pr_info("%s: a after add offset (0x%x)\n", __func__, a);
+
+       a &= 0xfff;
+       pr_info("%s: (a)&0xFFF (0x%x)\n", __func__, a);
+
+       /* modify 0x53[3:0] */
+       temp1 = v_53 & 0xF0;
+       temp2 = (u8)((a&0xF00) >> 8);
+       temp1 |= temp2;
+       s2mu106_write_and_verify_reg_byte(fuelgauge->i2c, 0x53, temp1);
+
+       /* modify 0x52[7:0] */
+       temp2 = (u8)(a & 0xFF);
+       s2mu106_write_and_verify_reg_byte(fuelgauge->i2c, 0x52, temp2);
+
+       /* restart and dumpdone */
+       s2mu106_write_and_verify_reg_byte(fuelgauge->i2c, 0x1E, 0x0F);
+       msleep(300);
+
+       /* restore current register */
+       s2mu106_write_and_verify_reg_byte(fuelgauge->i2c, 0x27, temp_REG27);
+       s2mu106_write_and_verify_reg_byte(fuelgauge->i2c, 0x26, 0xF6);
+
+       /* recovery 0x52 and 0x53 */
+       s2mu106_read_reg_byte(fuelgauge->i2c, 0x53, &temp1);
+       temp1 &= 0xF0;
+       temp1 |= (v_53 & 0x0F);
+       s2mu106_write_and_verify_reg_byte(fuelgauge->i2c, 0x53, temp1);
+       s2mu106_write_and_verify_reg_byte(fuelgauge->i2c, 0x52, v_52);
+
+       /* restore monout avgvbat factor value */
+       s2mu106_write_and_verify_reg_byte(fuelgauge->i2c, 0x40, v_40);
+}
+
+static void s2mu106_fuelgauge_select_table(struct s2mu106_fuelgauge_data *fuelgauge)
+{
+       /* Need to check which battery battery cell will be used */
+
+       if (fuelgauge->battery_profile_index == 0) {
+               fuelgauge->info.battery_table3 = fuelgauge->info.battery_table3_cell1;
+               fuelgauge->info.battery_table4 = fuelgauge->info.battery_table4_cell1;
+               fuelgauge->info.soc_arr_val = fuelgauge->info.soc_arr_val_cell1;
+               fuelgauge->info.ocv_arr_val = fuelgauge->info.ocv_arr_val_cell1;
+               fuelgauge->info.batcap = fuelgauge->info.batcap_cell1;
+               fuelgauge->info.accum = fuelgauge->info.accum_cell1;
+       } else {
+               fuelgauge->info.battery_table3 = fuelgauge->info.battery_table3_cell2;
+               fuelgauge->info.battery_table4 = fuelgauge->info.battery_table4_cell2;
+               fuelgauge->info.soc_arr_val = fuelgauge->info.soc_arr_val_cell2;
+               fuelgauge->info.ocv_arr_val = fuelgauge->info.ocv_arr_val_cell2;
+               fuelgauge->info.batcap = fuelgauge->info.batcap_cell2;
+               fuelgauge->info.accum = fuelgauge->info.accum_cell2;
+       }
+}
+
 static void s2mu106_reset_fg(struct s2mu106_fuelgauge_data *fuelgauge)
 {
        int i;
@@ -1112,7 +1231,7 @@ static int s2mu106_get_current(struct s2mu106_fuelgauge_data *fuelgauge)
 
        if (compliment & (0x1 << 15)) { /* Charging */
                curr = ((~compliment) & 0xFFFF) + 1;
-               curr = (curr * 1000) >> 12;
+               curr = ((curr * 1000) >> 12) * (-1);
        } else { /* dischaging */
                curr = compliment & 0x7FFF;
                curr = (curr * (-1000)) >> 12;
@@ -1476,6 +1595,90 @@ static irqreturn_t s2mu106_fg_irq_thread(int irq, void *irq_data)
 
        return IRQ_HANDLED;
 }
+static const char *fg_get_mmi_battid(void)
+{
+       struct device_node *np = of_find_node_by_path("/chosen");
+       const char *battsn_buf;
+       int retval;
+
+       battsn_buf = NULL;
+
+       if (np)
+               retval = of_property_read_string(np, "mmi,battid",
+                                                &battsn_buf);
+       else
+               return NULL;
+
+       if ((retval == -EINVAL) || !battsn_buf) {
+               pr_err("Battsn unused\n");
+               of_node_put(np);
+               return NULL;
+
+       } else
+               pr_err("Battsn = %s\n", battsn_buf);
+
+       of_node_put(np);
+
+       return battsn_buf;
+}
+
+static int fg_get_serialnumber(struct s2mu106_fuelgauge_data *chip,
+                                              struct device_node *np)
+{
+       const char *sn_buf, *df_sn, *dev_sn;
+       int rc;
+       int battery_cell1 = 0;
+       int battery_cell2 = 1;
+       int battery_index = 0;
+       bool battery_matched = false;
+
+       dev_sn = NULL;
+       df_sn = NULL;
+       sn_buf = NULL;
+       chip->battery_profile_index = 0;
+
+       dev_sn = fg_get_mmi_battid();
+       if (dev_sn == NULL) {
+               pr_info("%s, utag battery id did not setted", __func__);
+       }
+
+       rc = of_property_read_string(np, "df-serialnum",
+                                    &df_sn);
+       if (rc)
+               pr_info("%s,No Default Serial Number defined");
+       else if (df_sn)
+               pr_info("%s,Default Serial Number %s", __func__, df_sn);
+
+       rc = of_property_read_string(np, "serialnum_cell1",
+                                    &sn_buf);
+       if (rc)
+               pr_info("%s, No cell1 Serial Number defined", __func__);
+       else {
+               if ((dev_sn && strnstr(dev_sn, sn_buf, 32)) || (df_sn && strnstr(df_sn, sn_buf, 32))) {
+                               battery_matched = true;
+                               battery_index = battery_cell1;
+               }
+       }
+
+       rc = of_property_read_string(np, "serialnum_cell2",
+                                    &sn_buf);
+       if (rc)
+               pr_info("%s,No cell2 Serial Number defined", __func__);
+       else {
+               if ((dev_sn && strnstr(dev_sn, sn_buf, 32)) || (df_sn && strnstr(df_sn, sn_buf, 32))) {
+                               battery_matched = true;
+                               battery_index = battery_cell2;
+               }
+       }
+       if (battery_matched)
+               pr_info("%s, battery matched index:%d", __func__, battery_index);
+       else
+               pr_info("%s, battery did not matched , use default index:%d", __func__, battery_index);         
+
+       chip->battery_profile_index = battery_index;
+
+       return battery_index;
+}
 
 static int s2mu106_fuelgauge_get_cell_id(struct s2mu106_fuelgauge_data *fuelgauge)
 {
@@ -1563,6 +1766,7 @@ static int s2mu106_fuelgauge_parse_dt(struct s2mu106_fuelgauge_data *fuelgauge)
                        ret = of_property_read_u32_array(np, "battery,battery_table3_cell1", fuelgauge->info.battery_table3_cell1, 88);
                        if (ret < 0)
                                pr_err("%s error reading battery,battery_table3_cell1\n", __func__);
+
                        ret = of_property_read_u32_array(np, "battery,battery_table4_cell1", fuelgauge->info.battery_table4_cell1, 22);
                        if (ret < 0)
                                pr_err("%s error reading battery,battery_table4_cell1\n", __func__);
@@ -1574,22 +1778,18 @@ static int s2mu106_fuelgauge_parse_dt(struct s2mu106_fuelgauge_data *fuelgauge)
                        ret = of_property_read_u32_array(np, "battery,soc_arr_val_cell1", fuelgauge->info.soc_arr_val_cell1, 22);
                        if (ret < 0)
                                pr_err("%s error reading battery,soc_arr_val_cell1\n", __func__);
+
                        ret = of_property_read_u32_array(np, "battery,ocv_arr_val_cell1", fuelgauge->info.ocv_arr_val_cell1, 22);
                        if (ret < 0)
                                pr_err("%s error reading battery,ocv_arr_val_cell1\n", __func__);
 
                        ret = of_property_read_u32_array(np, "battery,accum_cell1", fuelgauge->info.accum_cell1, 2);
                        if (ret < 0) {
-                               fuelgauge->info.accum_cell1[1]=0x00; // REG 0x44
-                               fuelgauge->info.accum_cell1[0]=0x08; // REG 0x45
+                               fuelgauge->info.accum_cell1[1] = 0x00; // REG 0x44
+                               fuelgauge->info.accum_cell1[0] = 0x08; // REG 0x45
                                pr_err("%s There is no cell1 accumulative rate in DT. Use default value(0x800)\n", __func__);
                        }
 
-                       ret = of_property_read_u32(np, "battery,battery_param_ver_cell1",
-                                       &fuelgauge->info.battery_param_ver_cell1);
-                       if (ret < 0)
-                               pr_err("%s There is no cell1 battery parameter version\n", __func__);
-
                        /* get cell2 battery data */
                        ret = of_property_read_u32_array(np, "battery,battery_table3_cell2", fuelgauge->info.battery_table3_cell2, 88);
                        if (ret < 0)
@@ -1630,6 +1830,9 @@ static int s2mu106_fuelgauge_parse_dt(struct s2mu106_fuelgauge_data *fuelgauge)
                                                __func__);
                                fuelgauge->low_temp_limit = 100;
                        }
+
+                       fg_get_serialnumber(fuelgauge, np);
+
                }
        }
 
@@ -1658,6 +1861,7 @@ static const struct power_supply_desc s2mu106_fuelgauge_power_supply_desc = {
        .set_property = s2mu106_fg_set_property,
 };
 
+
 static int s2mu106_fuelgauge_probe(struct i2c_client *client,
                                const struct i2c_device_id *id)
 {
index 39602820294e43f9ee95b4c6db9d820ea1a71ba9..07b2b0dba96a635c928c642431dad2d40e0dce14 100644 (file)
 #define S2MU106_CHG_CTRL23             0x2F
 #define S2MU106_CHG_CTRL24             0x30
 
+#define S2MU106_DEVICE_TYPE1           0x47
+#define DEVICE_USB_TYPE_SHIFT          2
+#define DEVICE_USB_TYPE_WIDTH          1
+#define DEVICE_USB_TYPE_MASK           MASK(DEVICE_USB_TYPE_WIDTH, DEVICE_USB_TYPE_SHIFT)
+
+#define DEVICE_CARKIT_TYPE_SHIFT               4
+#define DEVICE_CARKIT_TYPE_WIDTH               1
+#define DEVICE_CARKIT_TYPE_MASK                MASK(DEVICE_CARKIT_TYPE_WIDTH, DEVICE_CARKIT_TYPE_SHIFT)
+
+#define DEVICE_CDP_TYPE_SHIFT          5
+#define DEVICE_CDP_TYPE_WIDTH          1
+#define DEVICE_CDP_TYPE_MASK           MASK(DEVICE_CDP_TYPE_WIDTH, DEVICE_CDP_TYPE_SHIFT)
+
+#define DEVICE_DCP_TYPE_SHIFT          6
+#define DEVICE_DCP_TYPE_WIDTH          1
+#define DEVICE_DCP_TYPE_MASK           MASK(DEVICE_DCP_TYPE_WIDTH, DEVICE_DCP_TYPE_SHIFT)
+
+#define DEVICE_OTG_TYPE_SHIFT          7
+#define DEVICE_OTG_TYPE_WIDTH          1
+#define DEVICE_OTG_TYPE_MASK           MASK(DEVICE_OTG_TYPE_WIDTH, DEVICE_OTG_TYPE_SHIFT)
 
 /* S2MU106_CHG_CTRL0 */
 #define REG_MODE_SHIFT         0
 #define SET_TIME_FC_CHG_WIDTH  3
 #define SET_TIME_FC_CHG_MASK   MASK(SET_TIME_FC_CHG_WIDTH, SET_TIME_FC_CHG_SHIFT)
 
+/* S2MU106_CHG_CTRL15 */
+#define SET_OSC_BST_SHIFT      5
+#define SET_OSC_BST_WIDTH      3
+#define SET_OSC_BST_MASK       MASK(SET_OSC_BST_WIDTH, SET_OSC_BST_SHIFT)
+
+#define SET_PC_SC_SHIFT                7
+#define SET_PC_SC_WIDTH        1
+#define SET_PC_SC_MASK MASK(SET_PC_SC_WIDTH, SET_PC_SC_SHIFT)
+#define SET_PC_SC_OFF          0
+#define SET_PC_SC_ON           1
+
+/* S2MU106_CHG_CTRL16 */
+#define SET_TIME_CHG_SHIFT     3
+#define SET_TIME_CHG_WIDTH     3
+#define SET_TIME_CHG_MASK      MASK(SET_TIME_CHG_WIDTH, SET_TIME_CHG_SHIFT)
+
 /* S2MU106_CHG_CTRL14 */
 #define TOP_OFF_TIME_SHIFT    0
 #define TOP_OFF_TIME_WIDTH    3
@@ -348,7 +384,6 @@ typedef struct s2mu106_charger_platform_data {
        int chg_switching_freq;
 } s2mu106_charger_platform_data_t;
 
-
 struct s2mu106_charger_data {
        struct i2c_client       *i2c;
        struct device *dev;
index c8f88468419c52e79526a8a1e86b6d190191f01c..d0cadba4f9410a323346c93ee6a0ca3902b4e189 100755 (executable)
@@ -160,6 +160,8 @@ struct s2mu106_fuelgauge_data {
 
        int fg_irq;
        bool probe_done;
+        int battery_profile_index;
+
 #if (TEMP_COMPEN) || (BATCAP_LEARN)
        bool bat_charging; /* battery is charging */
 #endif
index eed131d3daaf393c9c5b5be5719f4de218bac535..3f3f03714201787dc15f7b6a035f383845fd4350 100644 (file)
@@ -59,6 +59,11 @@ enum {
        POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE,
        POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE,
        POWER_SUPPLY_HEALTH_UNDERVOLTAGE,
+       //moto from 10
+       POWER_SUPPLY_HEALTH_WARM,
+       POWER_SUPPLY_HEALTH_COOL,
+       POWER_SUPPLY_HEALTH_HOT,
+       POWER_SUPPLY_HEALTH_SLIGHTLY_COOL,
 };
 
 enum {