/* * max77693_charger.c * * Copyright (C) 2011 Samsung Electronics * SangYoung Son * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and * only version 2 as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CONFIG_USB_HOST_NOTIFY #include #include #endif #include #if defined(CONFIG_TARGET_LOCALE_KOR) #ifdef CONFIG_DEBUG_FS #include #endif #endif /* MAX77693 Registers(defined @max77693-private.h) */ /* MAX77693_CHG_REG_CHG_INT */ #define MAX77693_BYP_I (1 << 0) #define MAX77693_THM_I (1 << 2) #define MAX77693_BAT_I (1 << 3) #define MAX77693_CHG_I (1 << 4) #define MAX77693_CHGIN_I (1 << 6) /* MAX77693_CHG_REG_CHG_INT_MASK */ #define MAX77693_BYP_IM (1 << 0) #define MAX77693_THM_IM (1 << 2) #define MAX77693_BAT_IM (1 << 3) #define MAX77693_CHG_IM (1 << 4) #define MAX77693_CHGIN_IM (1 << 6) /* MAX77693_CHG_REG_CHG_INT_OK */ #define MAX77693_BYP_OK 0x01 #define MAX77693_BYP_OK_SHIFT 0 #define MAX77693_THM_OK 0x04 #define MAX77693_THM_OK_SHIFT 2 #define MAX77693_BAT_OK 0x08 #define MAX77693_BAT_OK_SHIFT 3 #define MAX77693_CHG_OK 0x10 #define MAX77693_CHG_OK_SHIFT 4 #define MAX77693_CHGIN_OK 0x40 #define MAX77693_CHGIN_OK_SHIFT 6 #define MAX77693_DETBAT 0x80 #define MAX77693_DETBAT_SHIFT 7 /* MAX77693_CHG_REG_CHG_DTLS_00 */ #define MAX77693_THM_DTLS 0x07 #define MAX77693_THM_DTLS_SHIFT 0 #define MAX77693_CHGIN_DTLS 0x60 #define MAX77693_CHGIN_DTLS_SHIFT 5 /* MAX77693_CHG_REG_CHG_DTLS_01 */ #define MAX77693_CHG_DTLS 0x0F #define MAX77693_CHG_DTLS_SHIFT 0 #define MAX77693_BAT_DTLS 0x70 #define MAX77693_BAT_DTLS_SHIFT 4 /* MAX77693_CHG_REG_CHG_DTLS_02 */ #define MAX77693_BYP_DTLS 0x0F #define MAX77693_BYP_DTLS_SHIFT 0 #define MAX77693_BYP_DTLS0 0x1 #define MAX77693_BYP_DTLS1 0x2 #define MAX77693_BYP_DTLS2 0x4 #define MAX77693_BYP_DTLS3 0x8 /* MAX77693_CHG_REG_CHG_CNFG_00 */ #define MAX77693_MODE_DEFAULT 0x04 #define MAX77693_MODE_CHGR 0x01 #define MAX77693_MODE_OTG 0x02 #define MAX77693_MODE_BUCK 0x04 /* MAX77693_CHG_REG_CHG_CNFG_02 */ #define MAX77693_CHG_CC 0x3F /* MAX77693_CHG_REG_CHG_CNFG_04 */ #define MAX77693_CHG_MINVSYS_MASK 0x1F #define MAX77693_CHG_MINVSYS_SHIFT 5 /* MAX77693_CHG_REG_CHG_CNFG_09 */ #define MAX77693_CHG_CHGIN_LIM 0x7F /* MAX77693_MUIC_REG_CDETCTRL1 */ #define MAX77693_CHGTYPMAN 0x02 #define MAX77693_CHGTYPMAN_SHIFT 1 /* MAX77693_MUIC_REG_STATUS2 */ #define MAX77693_VBVOLT 0x40 #define MAX77693_VBVOLT_SHIFT 6 #define MAX77693_CHGDETRUN 0x08 #define MAX77693_CHGDETRUN_SHIFT 3 #define MAX77693_CHGTYPE 0x07 #define MAX77693_CHGTYPE_SHIFT 0 /* irq */ #define IRQ_DEBOUNCE_TIME 20 /* msec */ /* charger type detection */ #define DET_ERR_RETRY 5 #define DET_ERR_DELAY 200 /* soft charging */ #define SOFT_CHG_START_CURR 100 /* mA */ #define SOFT_CHG_START_DUR 100 /* ms */ #define SOFT_CHG_CURR_STEP 100 /* mA */ #define SOFT_CHG_STEP_DUR 20 /* ms */ struct max77693_charger_data { struct max77693_dev *max77693; struct power_supply charger; struct delayed_work update_work; /* mutex */ struct mutex irq_lock; struct mutex ops_lock; /* wakelock */ struct wake_lock update_wake_lock; unsigned int charging_state; unsigned int charging_type; unsigned int battery_state; unsigned int battery_present; unsigned int cable_type; unsigned int charging_current; unsigned int vbus_state; int irq_bypass; int irq_therm; int irq_battery; int irq_charge; int irq_chargin; /* software regulation */ bool soft_reg_state; int soft_reg_current; /* unsufficient power */ bool reg_loop_deted; #ifdef CONFIG_BATTERY_WPC_CHARGER /* wireless charge, w(wpc), v(vbus) */ int wc_w_gpio; int wc_w_irq; int wc_w_state; int wc_v_gpio; int wc_v_irq; int wc_v_state; bool wc_pwr_det; #endif struct max77693_charger_platform_data *charger_pdata; int irq; u8 irq_reg; int irq_cnt; #if defined(CONFIG_TARGET_LOCALE_KOR) #ifdef CONFIG_DEBUG_FS struct dentry *charger_debugfs_dir; #endif #endif }; static void max77693_dump_reg(struct max77693_charger_data *chg_data) { struct i2c_client *i2c = chg_data->max77693->i2c; u8 reg_data; u32 reg_addr; pr_info("%s\n", __func__); for (reg_addr = 0xB0; reg_addr <= 0xC5; reg_addr++) { max77693_read_reg(i2c, reg_addr, ®_data); pr_info("max77693: c: 0x%02x(0x%02x)\n", reg_addr, reg_data); } } #if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC) static int max77693_is_topoff_state(struct max77693_charger_data *chg_data) { struct i2c_client *i2c = chg_data->max77693->i2c; int state; u8 reg_data; 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; } static bool check_charger_unlock_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); if ((reg_data&0x0C) != 0x0C) { pr_info("%s: NOT unlock!(%d)\n", __func__, reg_data); return false; } else return true; } #endif static int max77693_get_battery_present(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_INT_OK, ®_data); pr_debug("%s: CHG_INT_OK(0x%02x)\n", __func__, reg_data); reg_data = ((reg_data & MAX77693_DETBAT) >> MAX77693_DETBAT_SHIFT); return !reg_data; } static int max77693_get_vbus_state(struct max77693_charger_data *chg_data) { struct i2c_client *i2c = chg_data->max77693->i2c; int state; u8 reg_data; pr_debug("%s\n", __func__); max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_DTLS_00, ®_data); reg_data = ((reg_data & MAX77693_CHGIN_DTLS) >> MAX77693_CHGIN_DTLS_SHIFT); pr_debug("%s: CHGIN_DTLS(0x%02x)\n", __func__, reg_data); switch (reg_data) { case 0x00: state = POWER_SUPPLY_VBUS_UVLO; break; case 0x01: state = POWER_SUPPLY_VBUS_WEAK; break; case 0x02: state = POWER_SUPPLY_VBUS_OVLO; break; case 0x03: state = POWER_SUPPLY_VBUS_GOOD; break; default: state = POWER_SUPPLY_VBUS_UNKNOWN; break; } chg_data->vbus_state = state; return state; } static int max77693_get_charger_type(struct max77693_charger_data *chg_data) { struct i2c_client *i2c = chg_data->max77693->i2c; int state; u8 reg_data; 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); switch (reg_data) { case 0x0: state = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; break; case 0x1: case 0x2: case 0x3: state = POWER_SUPPLY_CHARGE_TYPE_FAST; break; case 0x4: case 0x8: case 0xA: case 0xB: state = POWER_SUPPLY_CHARGE_TYPE_NONE; break; default: state = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; break; } chg_data->charging_type = state; return state; } static int max77693_get_charger_state(struct max77693_charger_data *chg_data) { struct i2c_client *i2c = chg_data->max77693->i2c; int state; u8 reg_data; 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); #if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC) pr_info("%s: CHG_DTLS(0x%02x)\n", __func__, reg_data); #else pr_debug("%s: CHG_DTLS(0x%02x)\n", __func__, reg_data); #endif switch (reg_data) { case 0x0: case 0x1: case 0x2: case 0x3: state = POWER_SUPPLY_STATUS_CHARGING; break; case 0x4: state = POWER_SUPPLY_STATUS_FULL; break; case 0x5: case 0x6: case 0x7: state = POWER_SUPPLY_STATUS_NOT_CHARGING; break; case 0x8: case 0xA: case 0xB: state = POWER_SUPPLY_STATUS_DISCHARGING; break; default: state = POWER_SUPPLY_STATUS_UNKNOWN; break; } chg_data->charging_state = state; return state; } static void max77693_set_charger_state(struct max77693_charger_data *chg_data, int enable) { struct i2c_client *i2c = chg_data->max77693->i2c; u8 reg_data; pr_debug("%s: enable=%d\n", __func__, enable); max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_00, ®_data); if (enable) reg_data |= MAX77693_MODE_CHGR; else reg_data &= ~MAX77693_MODE_CHGR; pr_debug("%s: CHG_CNFG_00(0x%02x)\n", __func__, reg_data); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_00, reg_data); } static void max77693_set_buck(struct max77693_charger_data *chg_data, int enable) { struct i2c_client *i2c = chg_data->max77693->i2c; u8 reg_data; pr_debug("%s: enable=%d\n", __func__, enable); max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_00, ®_data); if (enable) reg_data |= MAX77693_MODE_BUCK; else reg_data &= ~MAX77693_MODE_BUCK; pr_debug("%s: CHG_CNFG_00(0x%02x)\n", __func__, reg_data); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_00, reg_data); } int max77693_get_input_current(struct max77693_charger_data *chg_data) { struct i2c_client *i2c = chg_data->max77693->i2c; u8 reg_data; int get_current = 0; pr_debug("%s\n", __func__); max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, ®_data); pr_debug("%s: CHG_CNFG_09(0x%02x)\n", __func__, reg_data); get_current = reg_data * 20; pr_debug("%s: get input current: %dmA\n", __func__, get_current); return get_current; } void max77693_set_input_current(struct max77693_charger_data *chg_data, unsigned int set_current) { struct i2c_client *i2c = chg_data->max77693->i2c; int in_curr; u8 set_curr_reg, now_curr_reg; int step; pr_debug("%s: set input 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__); #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); if (chg_data->soft_reg_state == true) { pr_info("%s: exit soft regulation loop\n", __func__); chg_data->soft_reg_state = false; } return; } else max77693_set_buck(chg_data, ENABLE); /* Set input current limit */ if (chg_data->soft_reg_state) { pr_info("%s: now in soft regulation loop: %d\n", __func__, 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", __func__, in_curr); return; } set_curr_reg = (chg_data->soft_reg_current / 20); } else set_curr_reg = (set_current / 20); /* soft charge, 1st step, under 100mA, over 50ms */ max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, (SOFT_CHG_START_CURR / 20)); pr_debug("%s: soft charge, %dmA for %dms\n", __func__, SOFT_CHG_START_CURR, SOFT_CHG_START_DUR); msleep(SOFT_CHG_START_DUR); step = 0; do { now_curr_reg = ((SOFT_CHG_START_CURR + (SOFT_CHG_CURR_STEP * (++step))) / 20); now_curr_reg = MIN(now_curr_reg, set_curr_reg); pr_debug("%s: step%d: now curr(%dmA, 0x%x)\n", __func__, step, (SOFT_CHG_START_CURR + (SOFT_CHG_CURR_STEP * (step))), now_curr_reg); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, now_curr_reg); msleep(SOFT_CHG_STEP_DUR); } while (now_curr_reg < set_curr_reg); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_09, set_curr_reg); } int max77693_get_charge_current(struct max77693_charger_data *chg_data) { struct i2c_client *i2c = chg_data->max77693->i2c; u8 reg_data; int get_current = 0; pr_debug("%s\n", __func__); max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_02, ®_data); pr_debug("%s: CHG_CNFG_02(0x%02x)\n", __func__, reg_data); reg_data &= MAX77693_CHG_CC; get_current = chg_data->charging_current = reg_data * 333 / 10; pr_debug("%s: get charge current: %dmA\n", __func__, get_current); return get_current; } void max77693_set_charge_current(struct max77693_charger_data *chg_data, unsigned int set_current) { struct i2c_client *i2c = chg_data->max77693->i2c; u8 reg_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__); #endif max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_02, ®_data); reg_data &= ~MAX77693_CHG_CC; reg_data |= ((set_current * 3 / 100) << 0); pr_debug("%s: reg_data(0x%02x)\n", __func__, reg_data); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_02, reg_data); } void max77693_reset_chgtyp(struct max77693_charger_data *chg_data) { u8 reg_data; pr_info("%s\n", __func__); /* reset charger detection mode */ max77693_read_reg(chg_data->max77693->muic, MAX77693_MUIC_REG_CDETCTRL1, ®_data); reg_data |= MAX77693_CHGTYPMAN; max77693_write_reg(chg_data->max77693->muic, MAX77693_MUIC_REG_CDETCTRL1, reg_data); } #ifdef CONFIG_BATTERY_WPC_CHARGER static bool max77693_get_wc_state(struct max77693_charger_data *chg_data) { bool state; int wc_w_state, wc_v_state; pr_debug("%s\n", __func__); wc_w_state = !gpio_get_value(chg_data->wc_w_gpio); if (chg_data->wc_pwr_det == true) { wc_v_state = !gpio_get_value(chg_data->wc_v_gpio); if ((wc_w_state == CHARGE_ENABLE) && (wc_v_state == CHARGE_DISABLE)) { pr_debug("%s: wpc(%d), vbus(%d), wc ok\n", __func__, wc_w_state, wc_v_state); state = true; } else { pr_debug("%s: wpc(%d), vbus(%d), wc not ok\n", __func__, wc_w_state, wc_v_state); state = false; } chg_data->wc_w_state = wc_w_state; chg_data->wc_v_state = wc_v_state; } else { if (wc_w_state == CHARGE_ENABLE) { pr_debug("%s: wpc(%d), wc ok\n", __func__, wc_w_state); state = true; } else { pr_debug("%s: wpc(%d), wc not ok\n", __func__, wc_w_state); state = false; } chg_data->wc_w_state = wc_w_state; } return state; } #endif /* check chargable dock */ static int max77693_get_dock_type(struct max77693_charger_data *chg_data) { int state; u8 reg_data; int muic_cb_typ; u8 mu_st2, vbvolt; pr_debug("%s\n", __func__); muic_cb_typ = max77693_muic_get_charging_type(); pr_debug("%s: muic cable type(%d)\n", __func__, muic_cb_typ); /* dock detect from muic */ if ((muic_cb_typ == CABLE_TYPE_CARDOCK_MUIC) || (muic_cb_typ == CABLE_TYPE_DESKDOCK_MUIC)) { /* check vbvolt */ max77693_read_reg(chg_data->max77693->muic, MAX77693_MUIC_REG_STATUS2, &mu_st2); vbvolt = ((mu_st2 & MAX77693_VBVOLT) >> MAX77693_VBVOLT_SHIFT); pr_info("%s: dock detected(%d), vbvolt(%d)\n", __func__, muic_cb_typ, vbvolt); if (vbvolt == ENABLE) { 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); state = POWER_SUPPLY_TYPE_DOCK; } else { 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); state = POWER_SUPPLY_TYPE_BATTERY; } } else pr_debug("%s: dock not detected(%d), vbvolt(%d)\n", __func__, muic_cb_typ, vbvolt); return state; } static int max77693_get_cable_type(struct max77693_charger_data *chg_data) { int state; u8 reg_data, mu_adc, mu_adc1k, otg; u8 dtls_00, chgin_dtls; u8 mu_st2, chgdetrun, vbvolt, chgtyp; bool wc_state; bool retry_det, chg_det_erred; 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__); #endif /* If OTG enabled, skip detecting charger cable */ /* So mhl cable does not have adc ID that below condition */ /* can`t include adc1k mask */ max77693_read_reg(chg_data->max77693->muic, MAX77693_MUIC_REG_STATUS1, ®_data); pr_debug("%s: MUIC_REG_STATUS1(0x%02x)\n", __func__, reg_data); mu_adc1k = reg_data & (0x1 << 7); /* STATUS1_ADC1K_MASK */ mu_adc = reg_data & 0x1F; max77693_read_reg(chg_data->max77693->i2c, MAX77693_CHG_REG_CHG_CNFG_00, ®_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)) { pr_info("%s: otg enabled(otg(0x%x), adc(0x%x))\n", __func__, otg, mu_adc); state = POWER_SUPPLY_TYPE_BATTERY; goto chg_det_finish; } /* dock charger */ state = max77693_get_dock_type(chg_data); if (state == POWER_SUPPLY_TYPE_DOCK) { pr_info("%s: dock charger detected\n", __func__); goto chg_det_finish; } chg_det_erred = true; /* TEMP: set as true for logging */ do { retry_det = false; max77693_read_reg(chg_data->max77693->i2c, MAX77693_CHG_REG_CHG_DTLS_00, &dtls_00); max77693_read_reg(chg_data->max77693->muic, MAX77693_MUIC_REG_STATUS2, &mu_st2); chgin_dtls = ((dtls_00 & MAX77693_CHGIN_DTLS) >> MAX77693_CHGIN_DTLS_SHIFT); chgdetrun = ((mu_st2 & MAX77693_CHGDETRUN) >> MAX77693_CHGDETRUN_SHIFT); vbvolt = ((mu_st2 & MAX77693_VBVOLT) >> MAX77693_VBVOLT_SHIFT); chgtyp = ((mu_st2 & MAX77693_CHGTYPE) >> MAX77693_CHGTYPE_SHIFT); if (chg_det_erred) pr_info("%s: CHGIN(0x%x). MU_ST2(0x%x), " "CDR(0x%x), VB(0x%x), CHGTYP(0x%x)\n", __func__, chgin_dtls, mu_st2, chgdetrun, vbvolt, chgtyp); /* input power state */ if (((chgin_dtls != 0x0) && (vbvolt == 0x1)) || ((chgin_dtls == 0x0) && (vbvolt == 0x0)) || (chg_data->reg_loop_deted == true)) { pr_info("%s: sync power: CHGIN(0x%x), VB(0x%x), REG(%d)\n", __func__, chgin_dtls, vbvolt, chg_data->reg_loop_deted); } else { pr_err("%s: async power: CHGIN(0x%x), VB(0x%x), REG(%d)\n", __func__, chgin_dtls, vbvolt, chg_data->reg_loop_deted); /* check chargable input power */ if ((chgin_dtls == 0x0) && (chg_data->cable_type == POWER_SUPPLY_TYPE_BATTERY)) { pr_err("%s: unchargable power\n", __func__); state = POWER_SUPPLY_TYPE_BATTERY; goto chg_det_finish; } } /* charger detect running */ if (chgdetrun == 0x1) { pr_info("%s: CDR(0x%x)\n", __func__, chgdetrun); goto chg_det_err; } /* muic power and charger type */ if (((vbvolt == 0x1) && (chgtyp == 0x00)) || ((vbvolt == 0x0) && (chgtyp != 0x00))) { pr_info("%s: VB(0x%x), CHGTYP(0x%x)\n", __func__, vbvolt, chgtyp); goto chg_det_err; } /* charger type ok */ if (chg_det_erred) pr_info("%s: chgtyp detect ok, " "CHGIN(0x%x). MU_ST2(0x%x), " "CDR(0x%x), VB(0x%x), CHGTYP(0x%x)\n", __func__, chgin_dtls, mu_st2, chgdetrun, vbvolt, chgtyp); break; chg_det_err: retry_det = true; chg_det_erred = true; pr_err("%s: chgtyp detect err, retry %d, " "CHGIN(0x%x). MU_ST2(0x%x), CDR(0x%x), VB(0x%x), CHGTYP(0x%x)\n", __func__, ++retry_cnt, chgin_dtls, mu_st2, chgdetrun, vbvolt, chgtyp); /* after 200ms * 5 */ if (retry_cnt == DET_ERR_RETRY) { pr_info("%s: reset charger detection mode\n", __func__); /* reset charger detection mode */ max77693_reset_chgtyp(chg_data); } msleep(DET_ERR_DELAY); } while ((retry_det == true) && (retry_cnt < DET_ERR_RETRY)); switch (chgtyp) { case 0x0: /* Noting attached */ /* clear regulation loop flag */ chg_data->reg_loop_deted = false; 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); if (wc_state == true) state = POWER_SUPPLY_TYPE_WIRELESS; #endif break; case 0x2: /* Charging downstream port */ state = POWER_SUPPLY_TYPE_USB_CDP; break; case 0x3: /* Dedicated charger(up to 1.5A) */ case 0x5: /* Apple 1A or 2A charger */ case 0x6: /* Special charger */ state = POWER_SUPPLY_TYPE_MAINS; break; default: state = POWER_SUPPLY_TYPE_BATTERY; break; } chg_det_finish: pr_info("%s: cable type(%d)\n", __func__, state); chg_data->cable_type = state; mutex_unlock(&chg_data->ops_lock); return state; } static int max77693_get_battery_state(struct max77693_charger_data *chg_data) { struct i2c_client *i2c = chg_data->max77693->i2c; int state; int vbus_state; int chg_state; u8 reg_data; pr_debug("%s\n", __func__); max77693_read_reg(i2c, MAX77693_CHG_REG_CHG_DTLS_01, ®_data); reg_data = ((reg_data & MAX77693_BAT_DTLS) >> MAX77693_BAT_DTLS_SHIFT); #if defined(CONFIG_TARGET_LOCALE_KOR) || defined(CONFIG_MACH_M0_CTC) pr_info("%s: BAT_DTLS(0x%02x)\n", __func__, reg_data); #else pr_debug("%s: BAT_DTLS(0x%02x)\n", __func__, reg_data); #endif switch (reg_data) { case 0x01: state = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; break; case 0x02: pr_info("%s: battery dead\n", __func__); state = POWER_SUPPLY_HEALTH_DEAD; break; case 0x03: state = POWER_SUPPLY_HEALTH_GOOD; break; case 0x04: pr_info("%s: battery is okay " "but its voltage is low\n", __func__); state = POWER_SUPPLY_HEALTH_GOOD; break; case 0x05: pr_info("%s: battery ovp\n", __func__); state = POWER_SUPPLY_HEALTH_OVERVOLTAGE; break; default: state = POWER_SUPPLY_HEALTH_UNKNOWN; break; } if (state == POWER_SUPPLY_HEALTH_GOOD) { /* VBUS OVP state return battery OVP state */ vbus_state = max77693_get_vbus_state(chg_data); /* read CHG_DTLS and detecting battery terminal error */ chg_state = max77693_get_charger_state(chg_data); /* OVP is higher priority */ if (vbus_state == POWER_SUPPLY_VBUS_OVLO) { pr_info("%s: vbus ovp\n", __func__); state = POWER_SUPPLY_HEALTH_OVERVOLTAGE; } else if ((reg_data == 0x4) && (chg_state == POWER_SUPPLY_STATUS_FULL)) { pr_info("%s: battery terminal error\n", __func__); state = POWER_SUPPLY_HEALTH_UNDERVOLTAGE; } } chg_data->battery_state = state; return state; } /* 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); 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)); } 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__); /* unlock charger setting protect */ reg_data = (0x03 << 2); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_06, reg_data); /* * fast charge timer 10hrs * restart threshold disable * pre-qual charge enable(default) */ reg_data = (0x04 << 0) | (0x03 << 4); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_01, reg_data); /* * charge current 466mA(default) * otg current limit 900mA */ reg_data = (1 << 7); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_02, reg_data); #if defined(CONFIG_MACH_S2PLUS) /* top off current 200mA */ reg_data = 0x04; #else /* * 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 */ #endif reg_data |= (0x00 << 3); max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_03, reg_data); /* * cv voltage 4.2V or 4.35V * MINVSYS 3.6V(default) */ if ((system_rev != 3) && (system_rev >= 1)) reg_data = (0xDD << 0); else reg_data = (0xD6 << 0); /* For GC1 Model, MINVSYS is 3.4V*/ #if defined(CONFIG_MACH_GC1) 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_MINVSYS_MASK) == \ (0x1D << 0)) ? "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); #if defined(CONFIG_MACH_S2PLUS) /* charging disable */ reg_data = 0x00; max77693_write_reg(i2c, MAX77693_CHG_REG_CHG_CNFG_00, reg_data); #endif max77693_dump_reg(chg_data); } #define SW_REG_CURR_STEP_MA 300 static void max77693_soft_regulation(struct max77693_charger_data *chg_data) { struct i2c_client *i2c = chg_data->max77693->i2c; u8 reg_data; pr_info("%s\n", __func__); /* 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); 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, struct max77693_charger_data, update_work.work); struct power_supply *battery_psy = power_supply_get_by_name("battery"); union power_supply_propval value; int vbus_state; pr_debug("%s\n", __func__); #if defined(CONFIG_CHARGER_MANAGER) /* Notify charger-manager */ enum cm_event_types type; /* only consider battery in/out and external power in/out */ /* It seems that charger interrupt does not work at all */ switch (chg_data->irq - chg_data->max77693->irq_base) { case MAX77693_CHG_IRQ_BAT_I: type = max77693_get_battery_present(chg_data) ? CM_EVENT_BATT_IN : CM_EVENT_BATT_OUT; cm_notify_event(&chg_data->charger, type, NULL); break; case MAX77693_CHG_IRQ_CHGIN_I: cm_notify_event(&chg_data->charger, CM_EVENT_EXT_PWR_IN_OUT, NULL); break; default: break; } #else if (!battery_psy) { pr_err("%s: fail to get battery power supply\n", __func__); wake_unlock(&chg_data->update_wake_lock); return; } 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__); max77693_soft_regulation(chg_data); } else pr_debug("%s: vbus not weak\n", __func__); break; default: break; } battery_psy->set_property(battery_psy, POWER_SUPPLY_PROP_STATUS, &value); #endif wake_unlock(&chg_data->update_wake_lock); } /* Support property from charger */ static enum power_supply_property max77693_charger_props[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_CHARGE_TYPE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_PRESENT, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_NOW }; static int max77693_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct max77693_charger_data *chg_data = container_of(psy, struct max77693_charger_data, charger); switch (psp) { case POWER_SUPPLY_PROP_STATUS: val->intval = max77693_get_charger_state(chg_data); break; case POWER_SUPPLY_PROP_CHARGE_TYPE: val->intval = max77693_get_charger_type(chg_data); break; case POWER_SUPPLY_PROP_HEALTH: val->intval = max77693_get_battery_state(chg_data); break; case POWER_SUPPLY_PROP_PRESENT: val->intval = max77693_get_battery_present(chg_data); break; case POWER_SUPPLY_PROP_ONLINE: val->intval = max77693_get_cable_type(chg_data); break; case POWER_SUPPLY_PROP_CURRENT_MAX: val->intval = max77693_get_input_current(chg_data); break; 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); break; #endif default: return -EINVAL; } return 0; } static int max77693_charger_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { struct max77693_charger_data *chg_data = container_of(psy, struct max77693_charger_data, charger); switch (psp) { case POWER_SUPPLY_PROP_STATUS: max77693_set_charger_state(chg_data, val->intval); break; case POWER_SUPPLY_PROP_ONLINE: max77693_set_muic_cb_type(chg_data, val->intval); break; case POWER_SUPPLY_PROP_CURRENT_MAX: max77693_set_input_current(chg_data, val->intval); break; case POWER_SUPPLY_PROP_CURRENT_NOW: max77693_set_charge_current(chg_data, val->intval); break; default: return -EINVAL; } return 0; } static irqreturn_t max77693_bypass_irq(int irq, void *data) { struct max77693_charger_data *chg_data = data; u8 int_ok, dtls_02; u8 byp_dtls; #ifdef CONFIG_USB_HOST_NOTIFY struct host_notifier_platform_data *host_noti_pdata = host_notifier_device.dev.platform_data; #endif pr_info("%s: irq(%d)\n", __func__, irq); mutex_lock(&chg_data->irq_lock); max77693_read_reg(chg_data->max77693->i2c, MAX77693_CHG_REG_CHG_INT_OK, &int_ok); max77693_read_reg(chg_data->max77693->i2c, MAX77693_CHG_REG_CHG_DTLS_02, &dtls_02); 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); switch (byp_dtls) { case 0x0: pr_info("%s: bypass node is okay\n", __func__); break; case 0x1: pr_err("%s: bypass overcurrent limit\n", __func__); #ifdef CONFIG_USB_HOST_NOTIFY host_state_notify(&host_noti_pdata->ndev, NOTIFY_HOST_OVERCURRENT); #endif max77693_write_reg(chg_data->max77693->i2c, MAX77693_CHG_REG_CHG_CNFG_00, MAX77693_MODE_DEFAULT); break; case 0x8: pr_err("%s: chgin regulation loop is active\n", __func__); if (chg_data->cable_type != POWER_SUPPLY_TYPE_WIRELESS) { /* software regulation */ max77693_soft_regulation(chg_data); } else pr_err("%s: now in wireless charging, " " do not sw regulation\n", __func__); break; default: pr_info("%s: bypass reserved\n", __func__); break; } 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)); mutex_unlock(&chg_data->irq_lock); return IRQ_HANDLED; } /* TEMP: count same state irq occured */ 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; pr_info("%s: irq(%d)\n", __func__, irq); mutex_lock(&chg_data->irq_lock); max77693_read_reg(chg_data->max77693->i2c, MAX77693_CHG_REG_CHG_INT_OK, &prev_int_ok); msleep(IRQ_DEBOUNCE_TIME); max77693_read_reg(chg_data->max77693->i2c, MAX77693_CHG_REG_CHG_INT_OK, &int_ok); if ((chg_data->irq_reg == int_ok) && (prev_int_ok != int_ok)) { pr_info("%s: irq debounced(0x%x, 0x%x, 0x%x), return\n", __func__, chg_data->irq_reg, prev_int_ok, int_ok); mutex_unlock(&chg_data->irq_lock); return IRQ_HANDLED; } chg_data->irq_reg = int_ok; chg_data->irq = irq; /* charger */ max77693_read_reg(chg_data->max77693->i2c, MAX77693_CHG_REG_CHG_DTLS_00, &dtls_00); max77693_read_reg(chg_data->max77693->i2c, MAX77693_CHG_REG_CHG_DTLS_01, &dtls_01); thm_dtls = ((dtls_00 & MAX77693_THM_DTLS) >> MAX77693_THM_DTLS_SHIFT); chgin_dtls = ((dtls_00 & MAX77693_CHGIN_DTLS) >> MAX77693_CHGIN_DTLS_SHIFT); chg_dtls = ((dtls_01 & MAX77693_CHG_DTLS) >> MAX77693_CHG_DTLS_SHIFT); bat_dtls = ((dtls_01 & MAX77693_BAT_DTLS) >> MAX77693_BAT_DTLS_SHIFT); /* muic */ max77693_read_reg(chg_data->max77693->muic, MAX77693_MUIC_REG_STATUS2, &mu_st2); 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); 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)); mutex_unlock(&chg_data->irq_lock); return IRQ_HANDLED; } #ifdef CONFIG_BATTERY_WPC_CHARGER static irqreturn_t wpc_charger_irq(int irq, void *data) { struct max77693_charger_data *chg_data = data; 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); wc_w_state = wc_v_state = 0; wc_w_state = !gpio_get_value(chg_data->wc_w_gpio); if (chg_data->wc_pwr_det == true) { if ((chg_data->wc_w_state == 0) && (wc_w_state == 1)) { pr_info("%s: wpc activated, set V_INT as PN\n", __func__); s3c_gpio_setpull(chg_data->wc_v_gpio, S3C_GPIO_PULL_NONE); enable_irq(chg_data->wc_v_irq); } else if ((chg_data->wc_w_state == 1) && (wc_w_state == 0)) { disable_irq(chg_data->wc_v_irq); pr_info("%s: wpc deactivated, set V_INT as PD\n", __func__); s3c_gpio_setpull(chg_data->wc_v_gpio, S3C_GPIO_PULL_DOWN); } else { wc_v_pud_state = s3c_gpio_getpull(chg_data->wc_v_gpio); pr_info("%s: wpc not changed, V_INT(0x%x)\n", __func__, wc_v_pud_state); } wc_v_state = !gpio_get_value(chg_data->wc_v_gpio); pr_info("%s: w(%d to %d), v(%d to %d)\n", __func__, chg_data->wc_w_state, wc_w_state, chg_data->wc_v_state, wc_v_state); if ((wc_w_state == 1) && (chg_data->wc_v_state != wc_v_state)) { pr_info("%s: wc power path is changed\n", __func__); /* limit input current as 100mA */ max77693_set_input_current(chg_data, 100); /* reset charger detection mode */ max77693_reset_chgtyp(chg_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)); } chg_data->wc_w_state = wc_w_state; chg_data->wc_v_state = wc_v_state; } else { pr_info("%s: w(%d to %d)\n", __func__, chg_data->wc_w_state, wc_w_state); chg_data->wc_w_state = wc_w_state; } mutex_unlock(&chg_data->irq_lock); return IRQ_HANDLED; } #endif static void max77693_charger_initialize(struct max77693_charger_data *chg_data) { struct max77693_charger_platform_data *charger_pdata = chg_data->charger_pdata; struct i2c_client *i2c = chg_data->max77693->i2c; int i; for (i = 0; i < charger_pdata->num_init_data; i++) max77693_write_reg(i2c, charger_pdata->init_data[i].addr, charger_pdata->init_data[i].data); } #if defined(CONFIG_TARGET_LOCALE_KOR) #ifdef CONFIG_DEBUG_FS static int max77693_debugfs_open(struct inode *inode, struct file *filp) { filp->private_data = inode->i_private; return 0; } static ssize_t max77693_debugfs_read_registers(struct file *filp, char __user *buffer, size_t count, loff_t *ppos) { struct max77693_charger_data *chg_data = filp->private_data; int i = 0; char *buf; size_t len = 0; ssize_t ret; u8 val; if (!chg_data) { pr_err("%s : chg_data is null\n", __func__); return 0; } if (*ppos != 0) return 0; if (count < sizeof(buf)) return -ENOSPC; buf = kzalloc(PAGE_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; for (i = 0xB2; i <= 0xC6; i++) { max77693_read_reg(chg_data->max77693->i2c, i, &val); len += snprintf(buf + len, PAGE_SIZE - len, "%x=%02x", i, val); if (i == 0xC6) len += snprintf(buf + len, PAGE_SIZE - len, "\n"); else len += snprintf(buf + len, PAGE_SIZE - len, " "); } ret = simple_read_from_buffer(buffer, len, ppos, buf, PAGE_SIZE); kfree(buf); return ret; } static const struct file_operations max77693_debugfs_fops = { .owner = THIS_MODULE, .open = max77693_debugfs_open, .read = max77693_debugfs_read_registers, }; #endif #endif static __devinit int max77693_charger_probe(struct platform_device *pdev) { struct max77693_charger_data *chg_data; 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 start\n", __func__); chg_data = kzalloc(sizeof(struct max77693_charger_data), GFP_KERNEL); if (!chg_data) return -ENOMEM; platform_set_drvdata(pdev, chg_data); chg_data->max77693 = max77693; mutex_init(&chg_data->irq_lock); mutex_init(&chg_data->ops_lock); wake_lock_init(&chg_data->update_wake_lock, WAKE_LOCK_SUSPEND, "charger-update"); chg_data->charger_pdata = pdata->charger_data; if (!pdata->charger_data->init_data) max77693_charger_reg_init(chg_data); else max77693_charger_initialize(chg_data); chg_data->irq_bypass = max77693->irq_base + MAX77693_CHG_IRQ_BYP_I; chg_data->irq_therm = max77693->irq_base + MAX77693_CHG_IRQ_THM_I; chg_data->irq_battery = max77693->irq_base + MAX77693_CHG_IRQ_BAT_I; chg_data->irq_charge = max77693->irq_base + MAX77693_CHG_IRQ_CHG_I; chg_data->irq_chargin = max77693->irq_base + MAX77693_CHG_IRQ_CHGIN_I; chg_data->charger.name = "max77693-charger", chg_data->charger.type = POWER_SUPPLY_TYPE_BATTERY, chg_data->charger.properties = max77693_charger_props, chg_data->charger.num_properties = ARRAY_SIZE(max77693_charger_props), chg_data->charger.get_property = max77693_charger_get_property, chg_data->charger.set_property = max77693_charger_set_property, ret = power_supply_register(&pdev->dev, &chg_data->charger); if (ret) { pr_err("%s: failed: power supply register\n", __func__); goto err_kfree; } INIT_DELAYED_WORK_DEFERRABLE(&chg_data->update_work, max77693_update_work); ret = request_threaded_irq(chg_data->irq_bypass, NULL, max77693_bypass_irq, 0, "bypass-irq", chg_data); if (ret < 0) pr_err("%s: fail to request bypass IRQ: %d: %d\n", __func__, chg_data->irq_bypass, ret); ret = request_threaded_irq(chg_data->irq_battery, NULL, max77693_charger_irq, 0, "battery-irq", chg_data); if (ret < 0) pr_err("%s: fail to request battery IRQ: %d: %d\n", __func__, chg_data->irq_battery, ret); ret = request_threaded_irq(chg_data->irq_charge, NULL, max77693_charger_irq, 0, "charge-irq", chg_data); if (ret < 0) pr_err("%s: fail to request charge IRQ: %d: %d\n", __func__, chg_data->irq_charge, ret); #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 (system_rev >= 0xA) chg_data->wc_pwr_det = true; #endif #if defined(CONFIG_MACH_C1) if (system_rev >= 0x6) chg_data->wc_pwr_det = true; #endif if (chg_data->wc_pwr_det) pr_info("%s: support wc power detection\n", __func__); else pr_info("%s: not support wc power detection\n", __func__); /* wpc 5V interrupt */ if (!chg_data->charger_pdata->wpc_irq_gpio) { pr_err("%s: no irq gpio, do not support wpc\n", __func__); goto wpc_init_finish; } chg_data->wc_w_gpio = chg_data->charger_pdata->wpc_irq_gpio; chg_data->wc_w_irq = gpio_to_irq(chg_data->wc_w_gpio); ret = gpio_request(chg_data->wc_w_gpio, "wpc_charger-irq"); if (ret < 0) { pr_err("%s: failed requesting gpio %d\n", __func__, chg_data->wc_w_gpio); goto wpc_init_finish; } gpio_direction_input(chg_data->wc_w_gpio); gpio_free(chg_data->wc_w_gpio); ret = request_threaded_irq(chg_data->wc_w_irq, NULL, wpc_charger_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "wpc-int", chg_data); if (ret) pr_err("can NOT request irq 'WPC_INT' %d", chg_data->wc_w_irq); chg_data->wc_w_state = !gpio_get_value(chg_data->wc_w_gpio); /* vbus 5V interrupt */ if (chg_data->wc_pwr_det == false) { pr_info("%s: wpc(%d)\n", __func__, chg_data->wc_w_state); goto wpc_init_finish; } if (!chg_data->charger_pdata->vbus_irq_gpio) { pr_err("%s: no irq gpio, not support wc power detection\n", __func__); goto wpc_init_finish; } chg_data->wc_v_gpio = chg_data->charger_pdata->vbus_irq_gpio; chg_data->wc_v_irq = gpio_to_irq(chg_data->wc_v_gpio); ret = gpio_request(chg_data->wc_v_gpio, "vbus_charger-irq"); if (ret < 0) { pr_err("%s: failed requesting gpio %d\n", __func__, chg_data->wc_v_gpio); goto wpc_init_finish; } gpio_direction_input(chg_data->wc_v_gpio); gpio_free(chg_data->wc_v_gpio); ret = request_threaded_irq(chg_data->wc_v_irq, NULL, wpc_charger_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, "vbus-int", chg_data); if (ret) pr_err("can NOT request irq 'V_BUS_INT' %d", chg_data->wc_v_irq); if (chg_data->wc_w_state == 1) { pr_info("%s: wpc active, set V_BUS_INT as PN\n", __func__); s3c_gpio_setpull(chg_data->wc_v_gpio, S3C_GPIO_PULL_NONE); } else { disable_irq(chg_data->wc_v_irq); pr_info("%s: wpc deactive, set V_BUS_INT as PD\n", __func__); s3c_gpio_setpull(chg_data->wc_v_gpio, S3C_GPIO_PULL_DOWN); } chg_data->wc_v_state = !gpio_get_value(chg_data->wc_v_gpio); pr_info("%s: wpc(%d), vbus(%d)\n", __func__, chg_data->wc_w_state, chg_data->wc_v_state); wpc_init_finish: #endif #if defined(CONFIG_TARGET_LOCALE_KOR) #ifdef CONFIG_DEBUG_FS chg_data->charger_debugfs_dir = debugfs_create_dir("charger_debug", NULL); if (chg_data->charger_debugfs_dir) { if (!debugfs_create_file("max77693_regs", 0644, chg_data->charger_debugfs_dir, chg_data, &max77693_debugfs_fops)) pr_err("%s : debugfs_create_file, error\n", __func__); } else pr_err("%s : debugfs_create_dir, error\n", __func__); #endif #endif pr_info("%s: charger init complete\n", __func__); return 0; err_kfree: wake_lock_destroy(&chg_data->update_wake_lock); mutex_destroy(&chg_data->ops_lock); mutex_destroy(&chg_data->irq_lock); kfree(chg_data); return ret; } static int __devexit max77693_charger_remove(struct platform_device *pdev) { struct max77693_charger_data *chg_data = platform_get_drvdata(pdev); wake_lock_destroy(&chg_data->update_wake_lock); mutex_destroy(&chg_data->ops_lock); mutex_destroy(&chg_data->irq_lock); power_supply_unregister(&chg_data->charger); kfree(chg_data); return 0; } /* * WORKAROUND: * Several interrupts occur while charging through TA. * Suspended state cannot be maintained by the interrupts. */ #ifdef CONFIG_SLP static u8 saved_int_mask; static int max77693_charger_suspend(struct device *dev) { struct max77693_dev *max77693 = dev_get_drvdata(dev->parent); u8 int_mask; /* Save the masking value */ max77693_read_reg(max77693->i2c, MAX77693_CHG_REG_CHG_INT_MASK, &saved_int_mask); /* Mask all the interrupts related to charger */ int_mask = 0xff; max77693_write_reg(max77693->i2c, MAX77693_CHG_REG_CHG_INT_MASK, int_mask); return 0; } static int max77693_charger_resume(struct device *dev) { struct max77693_dev *max77693 = dev_get_drvdata(dev->parent); /* Restore the saved masking value */ max77693_write_reg(max77693->i2c, MAX77693_CHG_REG_CHG_INT_MASK, saved_int_mask); 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), }; static int __init max77693_charger_init(void) { return platform_driver_register(&max77693_charger_driver); } static void __exit max77693_charger_exit(void) { platform_driver_unregister(&max77693_charger_driver); } module_init(max77693_charger_init); module_exit(max77693_charger_exit); MODULE_AUTHOR("SangYoung Son "); MODULE_DESCRIPTION("max77693 Charger driver"); MODULE_LICENSE("GPL");