diff options
Diffstat (limited to 'drivers/battery/max77693_charger.c')
-rw-r--r-- | drivers/battery/max77693_charger.c | 619 |
1 files changed, 488 insertions, 131 deletions
diff --git a/drivers/battery/max77693_charger.c b/drivers/battery/max77693_charger.c index c17bbac..8609e6e 100644 --- a/drivers/battery/max77693_charger.c +++ b/drivers/battery/max77693_charger.c @@ -100,11 +100,26 @@ /* MAX77693_CHG_REG_CHG_CNFG_02 */ #define MAX77693_CHG_CC 0x3F +/* MAX77693_CHG_REG_CHG_CNFG_03 */ +#define MAX77693_TO_ITH_MASK 0x06 +#define MAX77693_TO_ITH_SHIFT 0 +#define MAX77693_TO_TIME_MASK 0x38 +#define MAX77693_TO_TIME_SHIFT 3 + /* MAX77693_CHG_REG_CHG_CNFG_04 */ #define MAX77693_CHG_MINVSYS_MASK 0xE0 #define MAX77693_CHG_MINVSYS_SHIFT 5 -#define MAX77693_CHG_PRM_MASK 0x1F -#define MAX77693_CHG_PRM_SHIFT 0 +#define MAX77693_CHG_MINVSYS_3_6V 0x06 +#define MAX77693_CHG_CV_PRM_MASK 0x1F +#define MAX77693_CHG_CV_PRM_SHIFT 0 +#define MAX77693_CHG_CV_PRM_4_20V 0x16 +#define MAX77693_CHG_CV_PRM_4_35V 0x1D +#define MAX77693_CHG_CV_PRM_4_40V 0x1F + +/* MAX77693_CHG_REG_CHG_CNFG_06 */ +#define MAX77693_CHG_CHGPROT 0x0C +#define MAX77693_CHG_CHGPROT_SHIFT 2 +#define MAX77693_CHG_CHGPROT_UNLOCK 0x03 /* MAX77693_CHG_REG_CHG_CNFG_09 */ #define MAX77693_CHG_CHGIN_LIM 0x7F @@ -126,6 +141,13 @@ /* irq */ #define IRQ_DEBOUNCE_TIME 20 /* msec */ +/* charger unlock */ +#define CHG_UNLOCK_RETRY 10 +#define CHG_UNLOCK_DELAY 100 + +/* power stabe guarantee */ +#define STABLE_POWER_DELAY 500 + /* charger type detection */ #define DET_ERR_RETRY 5 #define DET_ERR_DELAY 200 @@ -138,8 +160,9 @@ /* soft regulation */ #define SW_REG_CURR_STEP_MA 100 -#define SW_REG_START_DELAY 1000 -#define SW_REG_STEP_DELAY 500 +#define SW_REG_CURR_MIN_MA 100 +#define SW_REG_START_DELAY 500 +#define SW_REG_STEP_DELAY 50 struct max77693_charger_data { struct max77693_dev *max77693; @@ -162,6 +185,9 @@ struct max77693_charger_data { unsigned int battery_state; unsigned int battery_present; unsigned int cable_type; + unsigned int cable_sub_type; + unsigned int cable_pwr_type; + unsigned int dock_type; unsigned int charging_current; unsigned int vbus_state; @@ -174,6 +200,7 @@ struct max77693_charger_data { /* software regulation */ bool soft_reg_state; int soft_reg_current; + bool soft_reg_ing; /* unsufficient power */ bool reg_loop_deted; @@ -216,39 +243,35 @@ static void max77693_dump_reg(struct max77693_charger_data *chg_data) } #if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC) -static int max77693_is_topoff_state(struct max77693_charger_data *chg_data) +static bool max77693_charger_unlock(struct max77693_charger_data *chg_data); +static void max77693_charger_reg_init(struct max77693_charger_data *chg_data); + +static void check_charger_unlock_state(struct max77693_charger_data *chg_data) { struct i2c_client *i2c = chg_data->max77693->i2c; - int state; - u8 reg_data; + bool need_reg_init = false; pr_debug("%s\n", __func__); - max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_DTLS_01, ®_data); - reg_data = ((reg_data & MAX77693_CHG_DTLS) >> MAX77693_CHG_DTLS_SHIFT); - pr_debug("%s: CHG_DTLS(0x%02x)\n", __func__, reg_data); - - if (reg_data == 0x3 || reg_data == 0x4) - return 1; - else - return 0; + need_reg_init = max77693_charger_unlock(chg_data); + if (need_reg_init) { + pr_err("%s: charger locked state, reg init\n", __func__); + max77693_charger_reg_init(chg_data); + } } +#endif -static bool check_charger_unlock_state(struct max77693_charger_data *chg_data) +static int max77693_get_topoff_state(struct max77693_charger_data *chg_data) { struct i2c_client *i2c = chg_data->max77693->i2c; u8 reg_data; pr_debug("%s\n", __func__); - max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_06, ®_data); - pr_debug("%s: chgprot = %d\n", __func__, reg_data); + max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_DTLS_01, ®_data); + reg_data = ((reg_data & MAX77693_CHG_DTLS) >> MAX77693_CHG_DTLS_SHIFT); + pr_debug("%s: CHG_DTLS(0x%02x)\n", __func__, reg_data); - if ((reg_data&0x0C) != 0x0C) { - pr_info("%s: NOT unlock!(%d)\n", __func__, reg_data); - return false; - } else - return true; + return (reg_data == 0x4); } -#endif static int max77693_get_battery_present(struct max77693_charger_data *chg_data) { @@ -348,9 +371,9 @@ static int max77693_get_charger_state(struct max77693_charger_data *chg_data) case 0x0: case 0x1: case 0x2: - case 0x3: state = POWER_SUPPLY_STATUS_CHARGING; break; + case 0x3: case 0x4: state = POWER_SUPPLY_STATUS_FULL; break; @@ -378,7 +401,7 @@ static void max77693_set_charger_state(struct max77693_charger_data *chg_data, { struct i2c_client *i2c = chg_data->max77693->i2c; u8 reg_data; - pr_debug("%s: enable=%d\n", __func__, enable); + pr_debug("%s: enable(%d)\n", __func__, enable); max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_00, ®_data); @@ -396,7 +419,7 @@ static void max77693_set_buck(struct max77693_charger_data *chg_data, { struct i2c_client *i2c = chg_data->max77693->i2c; u8 reg_data; - pr_debug("%s: enable=%d\n", __func__, enable); + pr_debug("%s: enable(%d)\n", __func__, enable); max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_00, ®_data); @@ -434,25 +457,22 @@ void max77693_set_input_current(struct max77693_charger_data *chg_data, int step; pr_debug("%s: set input current as %dmA\n", __func__, set_current); + mutex_lock(&chg_data->ops_lock); #if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC) - if (!check_charger_unlock_state(chg_data)) - pr_err("%s: charger NOT unlock state!!!\n", __func__); + check_charger_unlock_state(chg_data); #endif if (set_current == OFF_CURR) { - pr_debug("%s: buck off current(%d)\n", __func__, set_current); - max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, 0); - - max77693_set_buck(chg_data, DISABLE); + max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, set_current); if (chg_data->soft_reg_state == true) { pr_info("%s: exit soft regulation loop\n", __func__); chg_data->soft_reg_state = false; } + mutex_unlock(&chg_data->ops_lock); return; - } else - max77693_set_buck(chg_data, ENABLE); + } /* Set input current limit */ if (chg_data->soft_reg_state) { @@ -460,8 +480,9 @@ void max77693_set_input_current(struct max77693_charger_data *chg_data, chg_data->soft_reg_current); in_curr = max77693_get_input_current(chg_data); if (in_curr == chg_data->soft_reg_current) { - pr_info("%s: same input current: %dmA\n", + pr_debug("%s: same input current: %dmA\n", __func__, in_curr); + mutex_unlock(&chg_data->ops_lock); return; } set_curr_reg = (chg_data->soft_reg_current / 20); @@ -489,6 +510,8 @@ void max77693_set_input_current(struct max77693_charger_data *chg_data, } while (now_curr_reg < set_curr_reg); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, set_curr_reg); + + mutex_unlock(&chg_data->ops_lock); } int max77693_get_charge_current(struct max77693_charger_data *chg_data) @@ -516,8 +539,7 @@ void max77693_set_charge_current(struct max77693_charger_data *chg_data, pr_debug("%s: set charge current as %dmA\n", __func__, set_current); #if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC) - if (!check_charger_unlock_state(chg_data)) - pr_err("%s: charger NOT unlock state!!!\n", __func__); + check_charger_unlock_state(chg_data); #endif max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_02, ®_data); @@ -593,6 +615,7 @@ static int max77693_get_dock_type(struct max77693_charger_data *chg_data) int state = POWER_SUPPLY_TYPE_BATTERY; u8 reg_data; int muic_cb_typ; + u8 dtls_00, chgin_dtls = 0; u8 mu_st2, vbvolt = 0; pr_debug("%s\n", __func__); @@ -601,7 +624,19 @@ static int max77693_get_dock_type(struct max77693_charger_data *chg_data) /* dock detect from muic */ if ((muic_cb_typ == CABLE_TYPE_CARDOCK_MUIC) || - (muic_cb_typ == CABLE_TYPE_DESKDOCK_MUIC)) { + (muic_cb_typ == CABLE_TYPE_DESKDOCK_MUIC) || + (muic_cb_typ == CABLE_TYPE_SMARTDOCK_MUIC) || + (muic_cb_typ == CABLE_TYPE_SMARTDOCK_TA_MUIC) || + (muic_cb_typ == CABLE_TYPE_SMARTDOCK_USB_MUIC) || + (muic_cb_typ == CABLE_TYPE_AUDIODOCK_MUIC)) { + + chg_data->dock_type = muic_cb_typ; + + /* read chgin, but not use */ + max77693_read_reg(chg_data->max77693->i2c, + MAX77693_CHG_REG_CHG_DTLS_00, &dtls_00); + chgin_dtls = ((dtls_00 & MAX77693_CHGIN_DTLS) >> + MAX77693_CHGIN_DTLS_SHIFT); /* check vbvolt */ max77693_read_reg(chg_data->max77693->muic, @@ -609,8 +644,8 @@ static int max77693_get_dock_type(struct max77693_charger_data *chg_data) vbvolt = ((mu_st2 & MAX77693_VBVOLT) >> MAX77693_VBVOLT_SHIFT); - pr_info("%s: dock detected(%d), vbvolt(%d)\n", __func__, - muic_cb_typ, vbvolt); + pr_info("%s: dock detected(%d), vbvolt(%d), chgin(0x%02x)\n", + __func__, muic_cb_typ, vbvolt, chgin_dtls); if (vbvolt == ENABLE) { max77693_read_reg(chg_data->max77693->i2c, @@ -627,9 +662,11 @@ static int max77693_get_dock_type(struct max77693_charger_data *chg_data) MAX77693_CHG_REG_CHG_CNFG_00, reg_data); state = POWER_SUPPLY_TYPE_BATTERY; } - } else + } else { pr_debug("%s: dock not detected(%d), vbvolt(%d)\n", __func__, muic_cb_typ, vbvolt); + chg_data->dock_type = 0; + } return state; } @@ -640,16 +677,17 @@ static int max77693_get_cable_type(struct max77693_charger_data *chg_data) u8 reg_data, mu_adc, mu_adc1k, otg; u8 dtls_00, chgin_dtls; u8 mu_st2, chgdetrun, vbvolt, chgtyp, dxovp; + int muic_cb_typ; bool wc_state; bool retry_det, chg_det_erred; + bool otg_detected = false; int retry_cnt = 0; pr_debug("%s\n", __func__); mutex_lock(&chg_data->ops_lock); #if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC) - if (!check_charger_unlock_state(chg_data)) - pr_err("%s: charger NOT unlock state!!!\n", __func__); + check_charger_unlock_state(chg_data); #endif /* If OTG enabled, skip detecting charger cable */ @@ -666,10 +704,28 @@ static int max77693_get_cable_type(struct max77693_charger_data *chg_data) pr_debug("%s: CHG_REG_CHG_CNFG_00(0x%02x)\n", __func__, reg_data); otg = reg_data & MAX77693_MODE_OTG; - if (otg || (mu_adc == 0x00 && !mu_adc1k)) { +#ifdef CONFIG_MACH_GC1 + /* In Factory mode using anyway Jig to switch between USB <--> UART + * sees a momentary 301K resistance as that of an OTG. Disabling + * charging INTRS now can lead to USB and MTP drivers not getting + * recognized in in subsequent switches. + * Factory Mode BOOT(on) USB + */ + if (mu_adc == 0x19) { + pr_info("%s: jig usb cable(adc(0x%x))\n", __func__, mu_adc); + state = POWER_SUPPLY_TYPE_USB; + goto chg_det_finish; + } +#endif + + muic_cb_typ = max77693_muic_get_charging_type(); + /* if type detection by otg, do not otg check */ + if ((muic_cb_typ != CABLE_TYPE_AUDIODOCK_MUIC) && + (((otg || (mu_adc == 0x00 && !mu_adc1k))))) { pr_info("%s: otg enabled(otg(0x%x), adc(0x%x))\n", __func__, otg, mu_adc); state = POWER_SUPPLY_TYPE_BATTERY; + otg_detected = true; goto chg_det_finish; } @@ -740,13 +796,23 @@ static int max77693_get_cable_type(struct max77693_charger_data *chg_data) /* check D+/D- ovp */ dxovp = ((mu_st2 & MAX77693_DXOVP) >> MAX77693_DXOVP_SHIFT); - if (dxovp) { + if ((vbvolt == 0x1) && (dxovp)) { pr_err("%s: D+/D- ovp state\n", __func__); + + /* disable CHGIN protection FETs */ + max77693_read_reg(chg_data->max77693->i2c, + MAX77693_CHG_REG_CHG_CNFG_00, + ®_data); + reg_data |= CHG_CNFG_00_DIS_MUIC_CTRL_MASK; + max77693_write_reg(chg_data->max77693->i2c, + MAX77693_CHG_REG_CHG_CNFG_00, + reg_data); + chg_det_erred = true; - state = POWER_SUPPLY_TYPE_USB; + state = POWER_SUPPLY_TYPE_MAINS; goto chg_det_finish; } else { - pr_err("%s: async power and chgtyp\n", __func__); + pr_err("%s: async power & chgtyp\n", __func__); goto chg_det_err; } } @@ -787,7 +853,6 @@ chg_det_err: state = POWER_SUPPLY_TYPE_BATTERY; break; case 0x1: /* USB cabled */ - case 0x4: /* Apple 500mA charger */ state = POWER_SUPPLY_TYPE_USB; #ifdef CONFIG_BATTERY_WPC_CHARGER wc_state = max77693_get_wc_state(chg_data); @@ -799,6 +864,7 @@ chg_det_err: state = POWER_SUPPLY_TYPE_USB_CDP; break; case 0x3: /* Dedicated charger(up to 1.5A) */ + case 0x4: /* Apple 500mA charger */ case 0x5: /* Apple 1A or 2A charger */ case 0x6: /* Special charger */ state = POWER_SUPPLY_TYPE_MAINS; @@ -812,9 +878,20 @@ chg_det_finish: if (chg_det_erred) pr_err("%s: cable type(%d)\n", __func__, state); - /* if cable is nothing, clear soft reg state flag */ - if (state == POWER_SUPPLY_TYPE_BATTERY) + /* if cable is nothing,,, */ + if (state == POWER_SUPPLY_TYPE_BATTERY) { + if (!otg_detected) { + /* enable CHGIN protection FETs */ + max77693_read_reg(chg_data->max77693->i2c, + MAX77693_CHG_REG_CHG_CNFG_00, ®_data); + reg_data &= ~CHG_CNFG_00_DIS_MUIC_CTRL_MASK; + max77693_write_reg(chg_data->max77693->i2c, + MAX77693_CHG_REG_CHG_CNFG_00, reg_data); + } + + /* clear soft reg state flag */ chg_data->soft_reg_state = false; + } chg_data->cable_type = state; @@ -888,31 +965,122 @@ static int max77693_get_battery_state(struct max77693_charger_data *chg_data) return state; } +/* extended online type */ +static int max77693_get_online_type(struct max77693_charger_data *chg_data) +{ + int m_typ; + int state = 0; + pr_info("%s\n", __func__); + + m_typ = max77693_get_cable_type(chg_data); + + pr_info("%s: main(%d), sub(%d), pwr(%d)\n", __func__, m_typ, + chg_data->cable_sub_type, + chg_data->cable_pwr_type); + + state = ((m_typ << ONLINE_TYPE_MAIN_SHIFT) | + (chg_data->cable_sub_type << ONLINE_TYPE_SUB_SHIFT) | + (chg_data->cable_pwr_type << ONLINE_TYPE_PWR_SHIFT)); + + pr_info("%s: online(0x%08x)\n", __func__, state); + + return state; +} + +void max77693_set_online_type(struct max77693_charger_data *chg_data, int data) +{ + int m_typ, s_typ, p_typ; + pr_info("%s: type(0x%08x)\n", __func__, data); + + /* | 31-24: RSVD | 23-16: MAIN TYPE | + 15-8: SUB TYPE | 7-0: POWER TYPE | */ + data &= ~(ONLINE_TYPE_RSVD_MASK); + m_typ = ((data & ONLINE_TYPE_MAIN_MASK) >> ONLINE_TYPE_MAIN_SHIFT); + chg_data->cable_sub_type = s_typ = + ((data & ONLINE_TYPE_SUB_MASK) >> ONLINE_TYPE_SUB_SHIFT); + chg_data->cable_pwr_type = p_typ = + ((data & ONLINE_TYPE_PWR_MASK) >> ONLINE_TYPE_PWR_SHIFT); + pr_info("%s: main(%d), sub(%d), pwr(%d)\n", __func__, + m_typ, s_typ, p_typ); + + cancel_delayed_work(&chg_data->update_work); + wake_lock(&chg_data->update_wake_lock); + schedule_delayed_work(&chg_data->update_work, + msecs_to_jiffies(STABLE_POWER_DELAY)); +} + /* get cable type from muic */ void max77693_set_muic_cb_type(struct max77693_charger_data *chg_data, int data) { pr_info("%s: muic cable type(%d)\n", __func__, data); +#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC) + check_charger_unlock_state(chg_data); +#endif + cancel_delayed_work(&chg_data->update_work); wake_lock(&chg_data->update_wake_lock); - schedule_delayed_work(&chg_data->update_work, msecs_to_jiffies(500)); + schedule_delayed_work(&chg_data->update_work, + msecs_to_jiffies(STABLE_POWER_DELAY)); } -static void max77693_charger_reg_init(struct max77693_charger_data *chg_data) +static bool max77693_charger_unlock(struct max77693_charger_data *chg_data) { struct i2c_client *i2c = chg_data->max77693->i2c; u8 reg_data; + u8 chgprot; + int retry_cnt = 0; + bool need_init = false; pr_debug("%s\n", __func__); - /* unlock charger setting protect */ - reg_data = (0x03 << 2); - max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_06, reg_data); + max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_06, ®_data); + chgprot = ((reg_data & MAX77693_CHG_CHGPROT) >> + MAX77693_CHG_CHGPROT_SHIFT); + + if (chgprot == MAX77693_CHG_CHGPROT_UNLOCK) { + pr_debug("%s: unlocked state, return\n", __func__); + need_init = false; + goto unlock_finish; + } + + do { + max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_06, + (MAX77693_CHG_CHGPROT_UNLOCK << + MAX77693_CHG_CHGPROT_SHIFT)); + + max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_06, ®_data); + chgprot = ((reg_data & MAX77693_CHG_CHGPROT) >> + MAX77693_CHG_CHGPROT_SHIFT); + + if (chgprot != MAX77693_CHG_CHGPROT_UNLOCK) { + pr_err("%s: unlock err, chgprot(0x%x), retry(%d)\n", + __func__, chgprot, retry_cnt); + msleep(CHG_UNLOCK_DELAY); + } else { + pr_info("%s: unlock success, chgprot(0x%x)\n", + __func__, chgprot); + need_init = true; + break; + } + } while ((chgprot != MAX77693_CHG_CHGPROT_UNLOCK) && + (++retry_cnt < CHG_UNLOCK_RETRY)); + +unlock_finish: + return need_init; +} + +static void max77693_charger_reg_init(struct max77693_charger_data *chg_data) +{ + struct i2c_client *i2c = chg_data->max77693->i2c; + u8 reg_data; + pr_debug("%s\n", __func__); /* * fast charge timer 10hrs * restart threshold disable * pre-qual charge enable(default) */ + max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_01, ®_data); reg_data = (0x04 << 0) | (0x03 << 4); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_01, reg_data); @@ -920,6 +1088,7 @@ static void max77693_charger_reg_init(struct max77693_charger_data *chg_data) * charge current 466mA(default) * otg current limit 900mA */ + max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_02, ®_data); reg_data = (1 << 7); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_02, reg_data); @@ -927,51 +1096,93 @@ static void max77693_charger_reg_init(struct max77693_charger_data *chg_data) * top off current 100mA * top off timer 0min */ - if (chg_data->max77693->pmic_rev == MAX77693_REV_PASS1) - reg_data = (0x03 << 0); /* 125mA */ - else - reg_data = (0x00 << 0); /* 100mA */ - - reg_data |= (0x00 << 3); + max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_03, ®_data); + if (chg_data->max77693->pmic_rev == MAX77693_REV_PASS1) { + reg_data = (0x03 << 0); /* 125mA */ + reg_data |= (0x00 << 3); /* 0min */ + } else { +#if defined(USE_2STEP_TERM) /* now only T0 */ + reg_data = (0x04 << 0); /* 200mA */ + reg_data |= (0x04 << 3); /* 40min */ +#else +#if defined(CONFIG_MACH_GC1) + reg_data = (0x02 << 0); /* 150mA */ + reg_data |= (0x00 << 3); /* 0min */ +#else /* M0, C1,,, */ + reg_data = (0x00 << 0); /* 100mA */ + reg_data |= (0x00 << 3); /* 0min */ +#endif +#endif + } max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_03, reg_data); /* * cv voltage 4.2V or 4.35V * MINVSYS 3.6V(default) */ + max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_04, ®_data); + reg_data &= (~MAX77693_CHG_MINVSYS_MASK); + reg_data |= (MAX77693_CHG_MINVSYS_3_6V << MAX77693_CHG_MINVSYS_SHIFT); + reg_data &= (~MAX77693_CHG_CV_PRM_MASK); +#if defined(CONFIG_MACH_M0) if ((system_rev != 3) && (system_rev >= 1)) - reg_data = (0xDD << 0); + reg_data |= (MAX77693_CHG_CV_PRM_4_35V << 0); else - reg_data = (0xD6 << 0); + reg_data |= (MAX77693_CHG_CV_PRM_4_20V << 0); +#else /* C1, C2, M3, T0, ... */ + reg_data |= (MAX77693_CHG_CV_PRM_4_35V << 0); +#endif /* * For GC1 Model, MINVSYS is 3.4V. - * For GC1 Model PRMV( Primary Charge Regn. Voltage) = 4.25V. + * For GC1 Model PRMV( Primary Charge Regn. Voltage) = 4.2V. * Actual expected regulated voltage needs to be 4.2V but due to - * internal resistance and circuit deviation we need to set the - * benchmark a bit higher(4.25V). + * internal resistance and circuit deviation we might have to set the + * benchmark a bit higher sometimes. (4.225V now) */ #if defined(CONFIG_MACH_GC1) - reg_data &= (~MAX77693_CHG_PRM_MASK); - reg_data |= (0x18 << MAX77693_CHG_PRM_SHIFT); + reg_data &= (~MAX77693_CHG_CV_PRM_MASK); + reg_data |= (0x16 << MAX77693_CHG_CV_PRM_SHIFT); reg_data &= (~MAX77693_CHG_MINVSYS_MASK); reg_data |= (0x4 << MAX77693_CHG_MINVSYS_SHIFT); #endif - pr_info("%s: battery cv voltage %s, (sysrev %d)\n", __func__, - (((reg_data & MAX77693_CHG_PRM_MASK) == \ - (0x1D << MAX77693_CHG_PRM_SHIFT)) ? "4.35V" : "4.2V"), - system_rev); - + (((reg_data & MAX77693_CHG_CV_PRM_MASK) == \ + (MAX77693_CHG_CV_PRM_4_35V << MAX77693_CHG_CV_PRM_SHIFT)) ? + "4.35V" : "4.2V"), system_rev); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_04, reg_data); - /* VBYPSET 5V */ - reg_data = 0x50; - max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_11, reg_data); - max77693_dump_reg(chg_data); } +static void max77693_reduce_input(struct max77693_charger_data *chg_data, + unsigned int curr) +{ + struct i2c_client *i2c = chg_data->max77693->i2c; + u8 reg_data; + pr_debug("%s: reduce %dmA\n", __func__, curr); + + max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, ®_data); + reg_data &= MAX77693_CHG_CHGIN_LIM; + chg_data->soft_reg_current = reg_data * 20; + + if (chg_data->soft_reg_current < curr) { + pr_err("%s: recude curr(%d) is under now curr(%d)\n", __func__, + curr, chg_data->soft_reg_current); + return; + } + + chg_data->soft_reg_current -= curr; + chg_data->soft_reg_current = max(chg_data->soft_reg_current, + SW_REG_CURR_MIN_MA); + pr_info("%s: %dmA to %dmA\n", __func__, + reg_data * 20, chg_data->soft_reg_current); + + reg_data = (chg_data->soft_reg_current / 20); + pr_debug("%s: reg_data(0x%02x)\n", __func__, reg_data); + max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, reg_data); +} + static void max77693_update_work(struct work_struct *work) { struct max77693_charger_data *chg_data = container_of(work, @@ -1010,8 +1221,6 @@ static void max77693_update_work(struct work_struct *work) switch (chg_data->irq - chg_data->max77693->irq_base) { case MAX77693_CHG_IRQ_CHGIN_I: - /* guarantee detection time */ - mdelay(100); vbus_state = max77693_get_vbus_state(chg_data); if (vbus_state == POWER_SUPPLY_VBUS_WEAK) { pr_info("%s: vbus weak\n", __func__); @@ -1039,49 +1248,91 @@ static void max77693_softreg_work(struct work_struct *work) struct max77693_charger_data *chg_data = container_of(work, struct max77693_charger_data, softreg_work.work); - struct i2c_client *i2c = chg_data->max77693->i2c; - u8 reg_data; - u8 int_ok, dtls_02, byp_dtls; - pr_info("%s\n", __func__); + u8 int_ok; + u8 dtls_00, chgin_dtls; + u8 dtls_01, chg_dtls; + u8 dtls_02, byp_dtls; + u8 mu_st2, vbvolt; + u8 cnfg_09; + int in_curr = 0; + pr_debug("%s\n", __func__); mutex_lock(&chg_data->ops_lock); + /* charger */ max77693_read_reg(chg_data->max77693->i2c, - MAX77693_CHG_REG_CHG_INT_OK, - &int_ok); - + MAX77693_CHG_REG_CHG_INT_OK, &int_ok); max77693_read_reg(chg_data->max77693->i2c, - MAX77693_CHG_REG_CHG_DTLS_02, - &dtls_02); + MAX77693_CHG_REG_CHG_DTLS_00, &dtls_00); + max77693_read_reg(chg_data->max77693->i2c, + MAX77693_CHG_REG_CHG_DTLS_01, &dtls_01); + max77693_read_reg(chg_data->max77693->i2c, + MAX77693_CHG_REG_CHG_DTLS_02, &dtls_02); + chgin_dtls = ((dtls_00 & MAX77693_CHGIN_DTLS) >> + MAX77693_CHGIN_DTLS_SHIFT); + chg_dtls = ((dtls_01 & MAX77693_CHG_DTLS) >> + MAX77693_CHG_DTLS_SHIFT); byp_dtls = ((dtls_02 & MAX77693_BYP_DTLS) >> MAX77693_BYP_DTLS_SHIFT); - pr_info("%s: INT_OK(0x%02x), BYP_DTLS(0x%02x)\n", - __func__, int_ok, byp_dtls); - if (byp_dtls & MAX77693_BYP_DTLS3) { - pr_info("%s: unstable power state for %dms\n", __func__, - SW_REG_START_DELAY); + max77693_read_reg(chg_data->max77693->i2c, + MAX77693_CHG_REG_CHG_CNFG_09, &cnfg_09); + cnfg_09 &= MAX77693_CHG_CHGIN_LIM; + in_curr = cnfg_09 * 20; + + /* muic */ + max77693_read_reg(chg_data->max77693->muic, + MAX77693_MUIC_REG_STATUS2, &mu_st2); + vbvolt = ((mu_st2 & MAX77693_VBVOLT) >> + MAX77693_VBVOLT_SHIFT); + + pr_info("%s: INT_OK(0x%x), CHGIN(0x%x), CHG(0x%x), " + "BYP(0x%x), ST2(0x%x), IN_CURR(%d)\n", __func__, + int_ok, chgin_dtls, chg_dtls, + byp_dtls, mu_st2, in_curr); + + if ((in_curr > SW_REG_CURR_STEP_MA) && (chg_dtls != 0x8) && + ((byp_dtls & MAX77693_BYP_DTLS3) || + ((chgin_dtls != 0x3) && (vbvolt == 0x1)))) { + pr_info("%s: unstable power\n", __func__); + + /* set soft regulation progress */ + chg_data->soft_reg_ing = true; + /* enable soft regulation loop */ chg_data->soft_reg_state = true; - max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, ®_data); - reg_data &= MAX77693_CHG_CHGIN_LIM; - chg_data->soft_reg_current = reg_data * 20; - chg_data->soft_reg_current -= SW_REG_CURR_STEP_MA; - chg_data->soft_reg_current = max(chg_data->soft_reg_current, 0); - pr_info("%s: %dmA to %dmA\n", __func__, - reg_data * 20, chg_data->soft_reg_current); + max77693_reduce_input(chg_data, SW_REG_CURR_STEP_MA); - reg_data = (chg_data->soft_reg_current / 20); - pr_debug("%s: reg_data(0x%02x)\n", __func__, reg_data); - max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, reg_data); + /* cancel update wq */ + cancel_delayed_work(&chg_data->update_work); + /* schedule softreg wq */ wake_lock(&chg_data->softreg_wake_lock); schedule_delayed_work(&chg_data->softreg_work, msecs_to_jiffies(SW_REG_STEP_DELAY)); } else { - pr_info("%s: (recover) stable power\n", __func__); + /* check cable detached */ + if ((!in_curr) || + ((chgin_dtls == 0x0) && (vbvolt == 0x0)) || + ((byp_dtls == 0x0) && (chg_dtls == 0x8))) { + pr_info("%s: maybe cable is detached\n", __func__); + + cancel_delayed_work(&chg_data->update_work); + wake_lock(&chg_data->update_wake_lock); + schedule_delayed_work(&chg_data->update_work, + msecs_to_jiffies(STABLE_POWER_DELAY)); + } + + /* for margin */ + if (chg_data->soft_reg_ing == true) { + pr_info("%s: stable power, reduce 1 more step " + "for margin\n", __func__); + max77693_reduce_input(chg_data, SW_REG_CURR_STEP_MA); + chg_data->soft_reg_ing = false; + } + wake_unlock(&chg_data->softreg_wake_lock); } @@ -1096,7 +1347,8 @@ static enum power_supply_property max77693_charger_props[] = { POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_CURRENT_MAX, - POWER_SUPPLY_PROP_CURRENT_NOW + POWER_SUPPLY_PROP_CURRENT_NOW, + POWER_SUPPLY_PROP_CHARGE_FULL }; static int max77693_charger_get_property(struct power_supply *psy, @@ -1121,7 +1373,11 @@ static int max77693_charger_get_property(struct power_supply *psy, val->intval = max77693_get_battery_present(chg_data); break; case POWER_SUPPLY_PROP_ONLINE: +#if defined(EXTENDED_ONLINE_TYPE) + val->intval = max77693_get_online_type(chg_data); +#else val->intval = max77693_get_cable_type(chg_data); +#endif break; case POWER_SUPPLY_PROP_CURRENT_MAX: val->intval = max77693_get_input_current(chg_data); @@ -1129,11 +1385,9 @@ static int max77693_charger_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CURRENT_NOW: val->intval = max77693_get_charge_current(chg_data); break; -#if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC) case POWER_SUPPLY_PROP_CHARGE_FULL: - val->intval = max77693_is_topoff_state(chg_data); + val->intval = max77693_get_topoff_state(chg_data); break; -#endif default: return -EINVAL; } @@ -1154,7 +1408,15 @@ static int max77693_charger_set_property(struct power_supply *psy, max77693_set_charger_state(chg_data, val->intval); break; case POWER_SUPPLY_PROP_ONLINE: +#if !defined(USE_CHGIN_INTR) max77693_set_muic_cb_type(chg_data, val->intval); +#else +#if defined(EXTENDED_ONLINE_TYPE) + max77693_set_online_type(chg_data, val->intval); +#else + return -EINVAL; +#endif +#endif break; case POWER_SUPPLY_PROP_CURRENT_MAX: max77693_set_input_current(chg_data, val->intval); @@ -1172,7 +1434,8 @@ static int max77693_charger_set_property(struct power_supply *psy, static irqreturn_t max77693_bypass_irq(int irq, void *data) { struct max77693_charger_data *chg_data = data; - u8 int_ok, dtls_02; + bool need_reg_init = false; + u8 int_ok, dtls_02, cnfg_00; u8 byp_dtls; #ifdef CONFIG_USB_HOST_NOTIFY struct host_notifier_platform_data *host_noti_pdata = @@ -1182,6 +1445,13 @@ static irqreturn_t max77693_bypass_irq(int irq, void *data) mutex_lock(&chg_data->irq_lock); + /* check and unlock */ + need_reg_init = max77693_charger_unlock(chg_data); + if (need_reg_init) { + pr_err("%s: charger locked state, reg init\n", __func__); + max77693_charger_reg_init(chg_data); + } + max77693_read_reg(chg_data->max77693->i2c, MAX77693_CHG_REG_CHG_INT_OK, &int_ok); @@ -1205,9 +1475,16 @@ static irqreturn_t max77693_bypass_irq(int irq, void *data) host_state_notify(&host_noti_pdata->ndev, NOTIFY_HOST_OVERCURRENT); #endif + max77693_read_reg(chg_data->max77693->i2c, + MAX77693_CHG_REG_CHG_CNFG_00, + &cnfg_00); + cnfg_00 &= ~(CHG_CNFG_00_OTG_MASK + | CHG_CNFG_00_BOOST_MASK + | CHG_CNFG_00_DIS_MUIC_CTRL_MASK); + cnfg_00 |= CHG_CNFG_00_BUCK_MASK; max77693_write_reg(chg_data->max77693->i2c, MAX77693_CHG_REG_CHG_CNFG_00, - MAX77693_MODE_DEFAULT); + cnfg_00); break; case 0x8: pr_err("%s: chgin regulation loop is active\n", __func__); @@ -1228,7 +1505,8 @@ static irqreturn_t max77693_bypass_irq(int irq, void *data) cancel_delayed_work(&chg_data->update_work); wake_lock(&chg_data->update_wake_lock); - schedule_delayed_work(&chg_data->update_work, msecs_to_jiffies(100)); + schedule_delayed_work(&chg_data->update_work, + msecs_to_jiffies(STABLE_POWER_DELAY)); mutex_unlock(&chg_data->irq_lock); @@ -1239,13 +1517,22 @@ static irqreturn_t max77693_bypass_irq(int irq, void *data) static irqreturn_t max77693_charger_irq(int irq, void *data) { struct max77693_charger_data *chg_data = data; - u8 prev_int_ok, int_ok, dtls_00, dtls_01; - u8 thm_dtls, chgin_dtls, chg_dtls, bat_dtls; - u8 mu_st2; + bool need_reg_init = false; + u8 prev_int_ok, int_ok; + u8 dtls_00, thm_dtls, chgin_dtls; + u8 dtls_01, chg_dtls, bat_dtls; + u8 mu_st2, vbvolt; pr_info("%s: irq(%d)\n", __func__, irq); mutex_lock(&chg_data->irq_lock); + /* check and unlock */ + need_reg_init = max77693_charger_unlock(chg_data); + if (need_reg_init) { + pr_err("%s: charger locked state, reg init\n", __func__); + max77693_charger_reg_init(chg_data); + } + max77693_read_reg(chg_data->max77693->i2c, MAX77693_CHG_REG_CHG_INT_OK, &prev_int_ok); @@ -1283,14 +1570,36 @@ static irqreturn_t max77693_charger_irq(int irq, void *data) /* muic */ max77693_read_reg(chg_data->max77693->muic, MAX77693_MUIC_REG_STATUS2, &mu_st2); + vbvolt = ((mu_st2 & MAX77693_VBVOLT) >> + MAX77693_VBVOLT_SHIFT); pr_info("%s: INT_OK(0x%x), THM(0x%x), CHGIN(0x%x), CHG(0x%x), BAT(0x%x), " "ST2(0x%x)\n", __func__, int_ok, thm_dtls, chgin_dtls, chg_dtls, bat_dtls, mu_st2); +#if defined(USE_CHGIN_INTR) + if (((chgin_dtls == 0x0) || (chgin_dtls == 0x1)) && + (vbvolt == 0x1) && (chg_dtls != 0x8)) { + pr_info("%s: abnormal power state: chgin(%d), vb(%d), chg(%d)\n", + __func__, chgin_dtls, vbvolt, chg_dtls); + + /* enable soft regulation loop */ + chg_data->soft_reg_state = true; + + /* first, reduce */ + max77693_reduce_input(chg_data, SW_REG_CURR_STEP_MA); + + /* software regulation */ + wake_lock(&chg_data->softreg_wake_lock); + schedule_delayed_work(&chg_data->softreg_work, + msecs_to_jiffies(SW_REG_STEP_DELAY)); + } +#endif + cancel_delayed_work(&chg_data->update_work); wake_lock(&chg_data->update_wake_lock); - schedule_delayed_work(&chg_data->update_work, msecs_to_jiffies(500)); + schedule_delayed_work(&chg_data->update_work, + msecs_to_jiffies(STABLE_POWER_DELAY)); mutex_unlock(&chg_data->irq_lock); @@ -1301,11 +1610,19 @@ static irqreturn_t max77693_charger_irq(int irq, void *data) static irqreturn_t wpc_charger_irq(int irq, void *data) { struct max77693_charger_data *chg_data = data; + bool need_reg_init = false; int wc_w_state, wc_v_state, wc_v_pud_state; pr_info("%s: irq(%d)\n", __func__, irq); mutex_lock(&chg_data->irq_lock); + /* check and unlock */ + need_reg_init = max77693_charger_unlock(chg_data); + if (need_reg_init) { + pr_err("%s: charger locked state, reg init\n", __func__); + max77693_charger_reg_init(chg_data); + } + wc_w_state = wc_v_state = 0; wc_w_state = !gpio_get_value(chg_data->wc_w_gpio); @@ -1349,7 +1666,7 @@ static irqreturn_t wpc_charger_irq(int irq, void *data) cancel_delayed_work(&chg_data->update_work); wake_lock(&chg_data->update_wake_lock); schedule_delayed_work(&chg_data->update_work, - msecs_to_jiffies(500)); + msecs_to_jiffies(STABLE_POWER_DELAY)); } chg_data->wc_w_state = wc_w_state; @@ -1412,7 +1729,7 @@ static ssize_t max77693_debugfs_read_registers(struct file *filp, if (!buf) return -ENOMEM; - for (i = 0xB2; i <= 0xC6; i++) { + for (i = 0xB0; i <= 0xC6; i++) { max77693_read_reg(chg_data->max77693->i2c, i, &val); len += snprintf(buf + len, PAGE_SIZE - len, "%x=%02x", i, val); @@ -1442,8 +1759,12 @@ static __devinit int max77693_charger_probe(struct platform_device *pdev) struct max77693_dev *max77693 = dev_get_drvdata(pdev->dev.parent); struct max77693_platform_data *pdata = dev_get_platdata(max77693->dev); int ret; + pr_info("%s: charger init\n", __func__); - pr_info("%s: charger init start\n", __func__); + if (!pdata) { + pr_err("%s: no platform data\n", __func__); + return -ENODEV; + } chg_data = kzalloc(sizeof(struct max77693_charger_data), GFP_KERNEL); if (!chg_data) @@ -1460,6 +1781,9 @@ static __devinit int max77693_charger_probe(struct platform_device *pdev) wake_lock_init(&chg_data->softreg_wake_lock, WAKE_LOCK_SUSPEND, "charger-softreg"); + /* unlock charger setting protect */ + max77693_charger_unlock(chg_data); + chg_data->charger_pdata = pdata->charger_data; if (!pdata->charger_data->init_data) max77693_charger_reg_init(chg_data); @@ -1472,6 +1796,9 @@ static __devinit int max77693_charger_probe(struct platform_device *pdev) chg_data->irq_charge = max77693->irq_base + MAX77693_CHG_IRQ_CHG_I; chg_data->irq_chargin = max77693->irq_base + MAX77693_CHG_IRQ_CHGIN_I; + INIT_DELAYED_WORK(&chg_data->update_work, max77693_update_work); + INIT_DELAYED_WORK(&chg_data->softreg_work, max77693_softreg_work); + chg_data->charger.name = "max77693-charger", chg_data->charger.type = POWER_SUPPLY_TYPE_BATTERY, chg_data->charger.properties = max77693_charger_props, @@ -1485,12 +1812,6 @@ static __devinit int max77693_charger_probe(struct platform_device *pdev) goto err_kfree; } - INIT_DELAYED_WORK_DEFERRABLE(&chg_data->update_work, - max77693_update_work); - - INIT_DELAYED_WORK_DEFERRABLE(&chg_data->softreg_work, - max77693_softreg_work); - ret = request_threaded_irq(chg_data->irq_bypass, NULL, max77693_bypass_irq, 0, "bypass-irq", chg_data); if (ret < 0) @@ -1509,9 +1830,17 @@ static __devinit int max77693_charger_probe(struct platform_device *pdev) pr_err("%s: fail to request charge IRQ: %d: %d\n", __func__, chg_data->irq_charge, ret); +#if defined(USE_CHGIN_INTR) + ret = request_threaded_irq(chg_data->irq_chargin, NULL, + max77693_charger_irq, 0, "chargin-irq", chg_data); + if (ret < 0) + pr_err("%s: fail to request charge IRQ: %d: %d\n", + __func__, chg_data->irq_chargin, ret); +#endif + #ifdef CONFIG_BATTERY_WPC_CHARGER chg_data->wc_pwr_det = chg_data->charger_pdata->wc_pwr_det; -#if defined(CONFIG_MACH_M0) || defined(CONFIG_MACH_C1VZW) || \ +#if defined(CONFIG_MACH_M0) || \ defined(CONFIG_MACH_GRANDE) || defined(CONFIG_MACH_IRON) if (system_rev >= 0xA) chg_data->wc_pwr_det = true; @@ -1520,7 +1849,7 @@ static __devinit int max77693_charger_probe(struct platform_device *pdev) if (system_rev >= 0x6) chg_data->wc_pwr_det = true; #endif -#if defined(CONFIG_MACH_C2) +#if defined(CONFIG_MACH_M3) chg_data->wc_pwr_det = true; #endif @@ -1619,7 +1948,7 @@ wpc_init_finish: #endif #endif - pr_info("%s: charger init complete\n", __func__); + pr_info("%s: probe complete\n", __func__); return 0; @@ -1657,11 +1986,14 @@ static int __devexit max77693_charger_remove(struct platform_device *pdev) */ #ifdef CONFIG_SLP static u8 saved_int_mask; +#endif static int max77693_charger_suspend(struct device *dev) { struct max77693_dev *max77693 = dev_get_drvdata(dev->parent); u8 int_mask; + pr_info("%s\n", __func__); +#ifdef CONFIG_SLP /* Save the masking value */ max77693_read_reg(max77693->i2c, MAX77693_CHG_REG_CHG_INT_MASK, @@ -1672,31 +2004,56 @@ static int max77693_charger_suspend(struct device *dev) max77693_write_reg(max77693->i2c, MAX77693_CHG_REG_CHG_INT_MASK, int_mask); +#else +#if defined(USE_CHGIN_INTR) + /* disable chgin irq */ + max77693_read_reg(max77693->i2c, + MAX77693_CHG_REG_CHG_INT_MASK, + &int_mask); + int_mask |= MAX77693_CHGIN_IM; + max77693_write_reg(max77693->i2c, + MAX77693_CHG_REG_CHG_INT_MASK, + int_mask); +#endif +#endif return 0; } static int max77693_charger_resume(struct device *dev) { struct max77693_dev *max77693 = dev_get_drvdata(dev->parent); + u8 int_mask; + pr_info("%s\n", __func__); +#ifdef CONFIG_SLP /* Restore the saved masking value */ max77693_write_reg(max77693->i2c, MAX77693_CHG_REG_CHG_INT_MASK, saved_int_mask); +#else +#if defined(USE_CHGIN_INTR) + /* enable chgin irq */ + max77693_read_reg(max77693->i2c, + MAX77693_CHG_REG_CHG_INT_MASK, + &int_mask); + int_mask &= (~MAX77693_CHGIN_IM); + max77693_write_reg(max77693->i2c, + MAX77693_CHG_REG_CHG_INT_MASK, + int_mask); +#endif +#endif + return 0; } static SIMPLE_DEV_PM_OPS(max77693_charger_pm_ops, max77693_charger_suspend, max77693_charger_resume); -#endif static struct platform_driver max77693_charger_driver = { .driver = { .owner = THIS_MODULE, .name = "max77693-charger", -#ifdef CONFIG_SLP .pm = &max77693_charger_pm_ops, -#endif }, .probe = max77693_charger_probe, .remove = __devexit_p(max77693_charger_remove), |