// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2019 MediaTek Inc. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "inc/mt6370_pmu_charger.h" #include "inc/mt6370_pmu.h" #define MT6370_PMU_CHARGER_DRV_VERSION "1.1.12_MTK" /* ======================= */ /* MT6370 Charger Variable */ /* ======================= */ enum mt6370_pmu_charger_irqidx { MT6370_CHG_IRQIDX_CHGIRQ1 = 0, MT6370_CHG_IRQIDX_CHGIRQ2, MT6370_CHG_IRQIDX_CHGIRQ3, MT6370_CHG_IRQIDX_CHGIRQ4, MT6370_CHG_IRQIDX_CHGIRQ5, MT6370_CHG_IRQIDX_CHGIRQ6, MT6370_CHG_IRQIDX_QCIRQ, MT6370_CHG_IRQIDX_DICHGIRQ7, MT6370_CHG_IRQIDX_OVPCTRLIRQ, MT6370_CHG_IRQIDX_MAX, }; enum mt6370_pmu_chg_type { MT6370_CHG_TYPE_NOVBUS = 0, MT6370_CHG_TYPE_UNDER_GOING, MT6370_CHG_TYPE_SDP, MT6370_CHG_TYPE_SDPNSTD, MT6370_CHG_TYPE_DCP, MT6370_CHG_TYPE_CDP, MT6370_CHG_TYPE_MAX, }; enum mt6370_usbsw_state { MT6370_USBSW_CHG = 0, MT6370_USBSW_USB, }; struct mt6370_pmu_charger_desc { u32 ichg; u32 aicr; u32 mivr; u32 cv; u32 ieoc; u32 safety_timer; u32 ircmp_resistor; u32 ircmp_vclamp; u32 dc_wdt; bool en_te; bool en_wdt; bool en_otg_wdt; bool en_polling; const char *chg_dev_name; const char *ls_dev_name; }; struct mt6370_pmu_charger_data { /* Inherited from mtk_charger_info */ struct mtk_charger_info mchr_info; struct mt6370_pmu_charger_desc *chg_desc; struct mt6370_pmu_chip *chip; struct mutex adc_access_lock; struct mutex irq_access_lock; struct mutex aicr_access_lock; struct mutex ichg_access_lock; struct mutex bc12_access_lock; struct device *dev; wait_queue_head_t wait_queue; bool err_state; enum charger_type chg_type; bool chg_online; u8 irq_flag[MT6370_CHG_IRQIDX_MAX]; u32 zcv; bool adc_hang; struct switch_dev *usb_switch; bool bc12_en; #ifndef CONFIG_TCPC_CLASS struct work_struct chgdet_work; #endif }; /* These default values will be used if there's no property in dts */ static struct mt6370_pmu_charger_desc mt6370_default_chg_desc = { .ichg = 2000000, /* uA */ .aicr = 500000, /* uA */ .mivr = 4400000, /* uV */ .cv = 4350000, /* uA */ .ieoc = 250000, /* uA */ .safety_timer = 12, /* hour */ #ifdef CONFIG_MTK_BIF_SUPPORT .ircmp_resistor = 0, /* uohm */ .ircmp_vclamp = 0, /* uV */ #else .ircmp_resistor = 25000, /* uohm */ .ircmp_vclamp = 32000, /* uV */ #endif .dc_wdt = 4000000, /* us */ .en_te = true, .en_wdt = true, }; static const u32 mt6370_otg_oc_threshold[] = { 500000, 700000, 1100000, 1300000, 1800000, 2100000, 2400000, 3000000, }; /* uA */ static const u32 mt6370_dc_vbatov_lvl[] = { 104, 108, 119, }; /* % * VOREG */ static const u32 mt6370_dc_wdt[] = { 0, 125000, 250000, 500000, 1000000, 2000000, 4000000, 8000000, }; /* us */ enum mt6370_charging_status { MT6370_CHG_STATUS_READY = 0, MT6370_CHG_STATUS_PROGRESS, MT6370_CHG_STATUS_DONE, MT6370_CHG_STATUS_FAULT, MT6370_CHG_STATUS_MAX, }; /* Charging status name */ static const char *mt6370_chg_status_name[MT6370_CHG_STATUS_MAX] = { "ready", "progress", "done", "fault", }; static const unsigned char mt6370_reg_en_hidden_mode[] = { MT6370_PMU_REG_HIDDENPASCODE1, MT6370_PMU_REG_HIDDENPASCODE2, MT6370_PMU_REG_HIDDENPASCODE3, MT6370_PMU_REG_HIDDENPASCODE4, }; static const unsigned char mt6370_val_en_hidden_mode[] = { 0x96, 0x69, 0xC3, 0x3C, }; enum mt6370_iin_limit_sel { MT6370_IIMLMTSEL_AICR_3250 = 0, MT6370_IIMLMTSEL_CHG_TYPE, MT6370_IINLMTSEL_AICR, MT6370_IINLMTSEL_LOWER_LEVEL, /* lower of above three */ }; enum mt6370_adc_sel { MT6370_ADC_VBUS_DIV5 = 1, MT6370_ADC_VBUS_DIV2, MT6370_ADC_VSYS, MT6370_ADC_VBAT, MT6370_ADC_TS_BAT = 6, MT6370_ADC_IBUS = 8, MT6370_ADC_IBAT, MT6370_ADC_CHG_VDDP = 11, MT6370_ADC_TEMP_JC, MT6370_ADC_MAX, }; /* Unit for each ADC parameter * 0 stands for reserved * For TS_BAT/TS_BUS, the real unit is 0.25. * Here we use 25, please remember to divide 100 while showing the value */ static const int mt6370_adc_unit[MT6370_ADC_MAX] = { 0, MT6370_ADC_UNIT_VBUS_DIV5, MT6370_ADC_UNIT_VBUS_DIV2, MT6370_ADC_UNIT_VSYS, MT6370_ADC_UNIT_VBAT, 0, MT6370_ADC_UNIT_TS_BAT, 0, MT6370_ADC_UNIT_IBUS, MT6370_ADC_UNIT_IBAT, 0, MT6370_ADC_UNIT_CHG_VDDP, MT6370_ADC_UNIT_TEMP_JC, }; static const int mt6370_adc_offset[MT6370_ADC_MAX] = { 0, MT6370_ADC_OFFSET_VBUS_DIV5, MT6370_ADC_OFFSET_VBUS_DIV2, MT6370_ADC_OFFSET_VSYS, MT6370_ADC_OFFSET_VBAT, 0, MT6370_ADC_OFFSET_TS_BAT, 0, MT6370_ADC_OFFSET_IBUS, MT6370_ADC_OFFSET_IBAT, 0, MT6370_ADC_OFFSET_CHG_VDDP, MT6370_ADC_OFFSET_TEMP_JC, }; /* =============================== */ /* mt6370 Charger Register Address */ /* =============================== */ static const unsigned char mt6370_chg_reg_addr[] = { MT6370_PMU_REG_CHGCTRL1, MT6370_PMU_REG_CHGCTRL2, MT6370_PMU_REG_CHGCTRL3, MT6370_PMU_REG_CHGCTRL4, MT6370_PMU_REG_CHGCTRL5, MT6370_PMU_REG_CHGCTRL6, MT6370_PMU_REG_CHGCTRL7, MT6370_PMU_REG_CHGCTRL8, MT6370_PMU_REG_CHGCTRL9, MT6370_PMU_REG_CHGCTRL10, MT6370_PMU_REG_CHGCTRL11, MT6370_PMU_REG_CHGCTRL12, MT6370_PMU_REG_CHGCTRL13, MT6370_PMU_REG_CHGCTRL14, MT6370_PMU_REG_CHGCTRL15, MT6370_PMU_REG_CHGCTRL16, MT6370_PMU_REG_CHGADC, MT6370_PMU_REG_DEVICETYPE, MT6370_PMU_REG_QCCTRL1, MT6370_PMU_REG_QCCTRL2, MT6370_PMU_REG_QC3P0CTRL1, MT6370_PMU_REG_QC3P0CTRL2, MT6370_PMU_REG_USBSTATUS1, MT6370_PMU_REG_QCSTATUS1, MT6370_PMU_REG_QCSTATUS2, MT6370_PMU_REG_CHGPUMP, MT6370_PMU_REG_CHGCTRL17, MT6370_PMU_REG_CHGCTRL18, MT6370_PMU_REG_CHGDIRCHG1, MT6370_PMU_REG_CHGDIRCHG2, MT6370_PMU_REG_CHGDIRCHG3, MT6370_PMU_REG_CHGSTAT, MT6370_PMU_REG_CHGNTC, MT6370_PMU_REG_ADCDATAH, MT6370_PMU_REG_ADCDATAL, MT6370_PMU_REG_CHGCTRL19, MT6370_PMU_REG_CHGSTAT1, MT6370_PMU_REG_CHGSTAT2, MT6370_PMU_REG_CHGSTAT3, MT6370_PMU_REG_CHGSTAT4, MT6370_PMU_REG_CHGSTAT5, MT6370_PMU_REG_CHGSTAT6, MT6370_PMU_REG_QCSTAT, MT6370_PMU_REG_DICHGSTAT, MT6370_PMU_REG_OVPCTRLSTAT, }; /* ===================================================================== */ /* Internal Functions */ /* ===================================================================== */ static int mt6370_set_aicr(struct mtk_charger_info *mchr_info, void *data); static int mt6370_get_aicr(struct mtk_charger_info *mchr_info, void *data); static int mt6370_set_ichg(struct mtk_charger_info *mchr_info, void *data); static int mt6370_get_ichg(struct mtk_charger_info *mchr_info, void *data); static int mt6370_enable_charging(struct mtk_charger_info *mchr_info, void *data); static inline void mt6370_chg_irq_set_flag( struct mt6370_pmu_charger_data *chg_data, u8 *irq, u8 mask) { mutex_lock(&chg_data->irq_access_lock); *irq |= mask; mutex_unlock(&chg_data->irq_access_lock); } static inline void mt6370_chg_irq_clr_flag( struct mt6370_pmu_charger_data *chg_data, u8 *irq, u8 mask) { mutex_lock(&chg_data->irq_access_lock); *irq &= ~mask; mutex_unlock(&chg_data->irq_access_lock); } static inline int mt6370_pmu_reg_test_bit( struct mt6370_pmu_chip *chip, u8 cmd, u8 shift, bool *is_one) { int ret = 0; u8 data = 0; ret = mt6370_pmu_reg_read(chip, cmd); if (ret < 0) { *is_one = false; return ret; } data = ret & (1 << shift); *is_one = (data == 0 ? false : true); return ret; } static u8 mt6370_find_closest_reg_value(u32 min, u32 max, u32 step, u32 num, u32 target) { u32 i = 0, cur_val = 0, next_val = 0; /* Smaller than minimum supported value, use minimum one */ if (target < min) return 0; for (i = 0; i < num - 1; i++) { cur_val = min + i * step; next_val = cur_val + step; if (cur_val > max) cur_val = max; if (next_val > max) next_val = max; if (target >= cur_val && target < next_val) return i; } /* Greater than maximum supported value, use maximum one */ return num - 1; } static u8 mt6370_find_closest_reg_value_via_table(const u32 *value_table, u32 table_size, u32 target_value) { u32 i = 0; /* Smaller than minimum supported value, use minimum one */ if (target_value < value_table[0]) return 0; for (i = 0; i < table_size - 1; i++) { if (target_value >= value_table[i] && target_value < value_table[i + 1]) return i; } /* Greater than maximum supported value, use maximum one */ return table_size - 1; } static u32 mt6370_find_closest_real_value(u32 min, u32 max, u32 step, u8 reg_val) { u32 ret_val = 0; ret_val = min + reg_val * step; if (ret_val > max) ret_val = max; return ret_val; } static int mt6370_set_fast_charge_timer( struct mt6370_pmu_charger_data *chg_data, u32 hour) { int ret = 0; u8 reg_fct = 0; reg_fct = mt6370_find_closest_reg_value( MT6370_WT_FC_MIN, MT6370_WT_FC_MAX, MT6370_WT_FC_STEP, MT6370_WT_FC_NUM, hour ); dev_info(chg_data->dev, "%s: timer = %d (0x%02X)\n", __func__, hour, reg_fct); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGCTRL12, MT6370_MASK_WT_FC, reg_fct << MT6370_SHIFT_WT_FC ); return ret; } static int mt6370_set_usbsw_state(struct mt6370_pmu_charger_data *chg_data, int state) { dev_info(chg_data->dev, "%s: state = %d\n", __func__, state); if (chg_data->usb_switch) switch_set_state(chg_data->usb_switch, state); #ifdef CONFIG_PROJECT_PHY else { if (state == MT6370_USBSW_CHG) Charger_Detect_Init(); else Charger_Detect_Release(); } #endif return 0; } /* Hardware pin current limit */ static int mt6370_enable_ilim(struct mt6370_pmu_charger_data *chg_data, bool en) { int ret = 0; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGCTRL3, MT6370_MASK_ILIM_EN); return ret; } /* Select IINLMTSEL */ static int mt6370_select_input_current_limit( struct mt6370_pmu_charger_data *chg_data, enum mt6370_iin_limit_sel sel) { int ret = 0; dev_info(chg_data->dev, "%s: select input current limit = %d\n", __func__, sel); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGCTRL2, MT6370_MASK_IINLMTSEL, sel << MT6370_SHIFT_IINLMTSEL ); return ret; } static int mt6370_enable_hidden_mode(struct mt6370_pmu_charger_data *chg_data, bool en) { int ret = 0; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); /* Disable hidden mode */ if (!en) { ret = mt6370_pmu_reg_write(chg_data->chip, mt6370_reg_en_hidden_mode[0], 0x00); if (ret < 0) goto err; return ret; } ret = mt6370_pmu_reg_block_write( chg_data->chip, mt6370_reg_en_hidden_mode[0], ARRAY_SIZE(mt6370_val_en_hidden_mode), mt6370_val_en_hidden_mode ); if (ret < 0) goto err; return ret; err: dev_err(chg_data->dev, "%s: en = %d failed, ret = %d\n", __func__, en, ret); return ret; } /* Software workaround */ static int mt6370_chg_sw_workaround(struct mt6370_pmu_charger_data *chg_data) { int ret = 0; u8 zcv_data[2] = {0}; dev_info(chg_data->dev, "%s\n", __func__); /* Enter hidden mode */ ret = mt6370_enable_hidden_mode(chg_data, true); if (ret < 0) goto out; /* Read ZCV data */ ret = mt6370_pmu_reg_block_read(chg_data->chip, MT6370_PMU_REG_ADCBATDATAH, 2, zcv_data); if (ret < 0) dev_err(chg_data->dev, "%s: read zcv data failed\n", __func__); else { chg_data->zcv = 5000 * (zcv_data[0] * 256 + zcv_data[1]); dev_info(chg_data->dev, "%s: zcv = (0x%02X, 0x%02X, %dmV)\n", __func__, zcv_data[0], zcv_data[1], chg_data->zcv / 1000); } /* Trigger any ADC before disabling ZCV */ ret = mt6370_pmu_reg_write(chg_data->chip, MT6370_PMU_REG_CHGADC, 0x11); if (ret < 0) dev_err(chg_data->dev, "%s: trigger ADC failed\n", __func__); /* Disable ZCV */ ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_OSCCTRL, 0x04); if (ret < 0) dev_err(chg_data->dev, "%s: disable ZCV failed\n", __func__); /* Disable TS auto sensing */ ret = mt6370_pmu_reg_clr_bit(chg_data->chip, MT6370_PMU_REG_CHGHIDDENCTRL15, 0x01); mdelay(200); /* Disable SEN_DCP for charging mode */ ret = mt6370_pmu_reg_clr_bit(chg_data->chip, MT6370_PMU_REG_QCCTRL2, MT6370_MASK_EN_DCP); out: /* Exit hidden mode */ ret = mt6370_enable_hidden_mode(chg_data, false); if (ret < 0) dev_err(chg_data->dev, "%s: exit hidden mode failed\n", __func__); return ret; } static int mt6370_get_adc(struct mt6370_pmu_charger_data *chg_data, enum mt6370_adc_sel adc_sel, int *adc_val) { int ret = 0, i = 0; u8 adc_data[6] = {0}; bool adc_start = false; u32 aicr = 0, ichg = 0; s64 adc_result = 0; const int max_wait_times = 6; mutex_lock(&chg_data->adc_access_lock); mt6370_enable_hidden_mode(chg_data, true); /* Select ADC to desired channel */ ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGADC, MT6370_MASK_ADC_IN_SEL, adc_sel << MT6370_SHIFT_ADC_IN_SEL ); if (ret < 0) { dev_err(chg_data->dev, "%s: select ch to %d failed, ret = %d\n", __func__, adc_sel, ret); goto out; } /* Workaround for IBUS & IBAT */ if (adc_sel == MT6370_ADC_IBUS) { mutex_lock(&chg_data->aicr_access_lock); ret = mt6370_get_aicr(&chg_data->mchr_info, &aicr); if (ret < 0) { dev_err(chg_data->dev, "%s: get aicr failed\n", __func__); goto out_unlock_all; } } else if (adc_sel == MT6370_ADC_IBAT) { mutex_lock(&chg_data->ichg_access_lock); ret = mt6370_get_ichg(&chg_data->mchr_info, &ichg); if (ret < 0) { dev_err(chg_data->dev, "%s: get ichg failed\n", __func__); goto out_unlock_all; } } /* Start ADC conversation */ ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_CHGADC, MT6370_MASK_ADC_START); if (ret < 0) { dev_err(chg_data->dev, "%s: start conversation failed, sel = %d, ret = %d\n", __func__, adc_sel, ret); goto out_unlock_all; } for (i = 0; i < max_wait_times; i++) { msleep(35); ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGADC, MT6370_SHIFT_ADC_START, &adc_start); if (!adc_start && ret >= 0) break; } if (i == max_wait_times) { dev_err(chg_data->dev, "%s: wait conversation failed, sel = %d, ret = %d\n", __func__, adc_sel, ret); /* AEE for debug */ if (!chg_data->adc_hang) { for (i = 0; i < ARRAY_SIZE(mt6370_chg_reg_addr); i++) { ret = mt6370_pmu_reg_read(chg_data->chip, mt6370_chg_reg_addr[i]); dev_err(chg_data->dev, "%s: reg[0x%02X] = 0x%02X\n", __func__, mt6370_chg_reg_addr[i], ret); } chg_data->adc_hang = true; } /* Add for debug */ /* ZCV, reg0x10 */ ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_OSCCTRL); if (ret < 0) dev_err(chg_data->dev, "%s: read reg0x10 failed\n", __func__); else dev_err(chg_data->dev, "%s: reg0x10 = 0x%02X\n", __func__, ret); /* TS auto sensing */ ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGHIDDENCTRL15); if (ret < 0) dev_err(chg_data->dev, "%s: read reg0x3E failed\n", __func__); else dev_err(chg_data->dev, "%s: reg0x3E = 0x%02X\n", __func__, ret); } mdelay(1); /* Read ADC data */ ret = mt6370_pmu_reg_block_read(chg_data->chip, MT6370_PMU_REG_ADCDATAH, 6, adc_data); if (ret < 0) { dev_err(chg_data->dev, "%s: read ADC data failed, ret = %d\n", __func__, ret); goto out_unlock_all; } dev_dbg(chg_data->dev, "%s: adc_sel = %d, adc_h = 0x%02X, adc_l = 0x%02X\n", __func__, adc_sel, adc_data[0], adc_data[1]); dev_dbg(chg_data->dev, "%s: 0x4E~51 = (0x%02X, 0x%02X, 0x%02X, 0x%02X)\n", __func__, adc_data[2], adc_data[3], adc_data[4], adc_data[5]); /* Calculate ADC value */ adc_result = (adc_data[0] * 256 + adc_data[1]) * mt6370_adc_unit[adc_sel] + mt6370_adc_offset[adc_sel]; out_unlock_all: /* Coefficient of IBUS & IBAT */ if (adc_sel == MT6370_ADC_IBUS) { if (aicr < 400000) /* 400mA */ adc_result = adc_result * 67 / 100; mutex_unlock(&chg_data->aicr_access_lock); } else if (adc_sel == MT6370_ADC_IBAT) { if (ichg >= 100000 && ichg <= 450000) /* 100~450mA */ adc_result = adc_result * 475 / 1000; else if (ichg >= 500000 && ichg <= 850000) /* 500~850mA */ adc_result = adc_result * 536 / 1000; mutex_unlock(&chg_data->ichg_access_lock); } out: *adc_val = adc_result; mt6370_enable_hidden_mode(chg_data, false); mutex_unlock(&chg_data->adc_access_lock); return ret; } static int mt6370_enable_wdt(struct mt6370_pmu_charger_data *chg_data, bool en) { int ret = 0; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGCTRL13, MT6370_MASK_WDT_EN); return ret; } static int mt6370_is_charging_enable(struct mt6370_pmu_charger_data *chg_data, bool *en) { int ret = 0; ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL2, MT6370_SHIFT_CHG_EN, en); return ret; } static int mt6370_enable_te(struct mt6370_pmu_charger_data *chg_data, bool en) { int ret = 0; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGCTRL2, MT6370_MASK_TE_EN); return ret; } static int mt6370_enable_pump_express(struct mt6370_pmu_charger_data *chg_data, bool en) { int ret = 0, i = 0; const int max_wait_times = 5; bool pumpx_en = false; bool chg_en = true; u32 aicr = 80000; /* 10uA */ u32 ichg = 200000; /* 10uA */ dev_info(chg_data->dev, "%s: en %d\n", __func__, en); ret = mt6370_set_aicr(&chg_data->mchr_info, &aicr); if (ret < 0) return ret; ret = mt6370_set_ichg(&chg_data->mchr_info, &ichg); if (ret < 0) return ret; ret = mt6370_enable_charging(&chg_data->mchr_info, &chg_en); if (ret < 0) return ret; ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGCTRL17, MT6370_MASK_PUMPX_EN); if (ret < 0) return ret; for (i = 0; i < max_wait_times; i++) { msleep(2500); ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL17, MT6370_SHIFT_PUMPX_EN, &pumpx_en); if (!pumpx_en && ret >= 0) break; } if (i == max_wait_times) { dev_err(chg_data->dev, "%s: wait failed, ret = %d\n", __func__, ret); ret = -EIO; return ret; } return ret; } static int mt6370_get_ieoc(struct mt6370_pmu_charger_data *chg_data, u32 *ieoc) { int ret = 0; u8 reg_ieoc = 0; ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGCTRL9); if (ret < 0) return ret; reg_ieoc = (ret & MT6370_MASK_IEOC) >> MT6370_SHIFT_IEOC; *ieoc = mt6370_find_closest_real_value( MT6370_IEOC_MIN, MT6370_IEOC_MAX, MT6370_IEOC_STEP, reg_ieoc ); return ret; } static int mt6370_get_mivr(struct mt6370_pmu_charger_data *chg_data, u32 *mivr) { int ret = 0; u8 reg_mivr = 0; ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGCTRL6); if (ret < 0) return ret; reg_mivr = ((ret & MT6370_MASK_MIVR) >> MT6370_SHIFT_MIVR) & 0xFF; *mivr = mt6370_find_closest_real_value( MT6370_MIVR_MIN, MT6370_MIVR_MAX, MT6370_MIVR_STEP, reg_mivr ); return ret; } static int mt6370_set_ieoc(struct mt6370_pmu_charger_data *chg_data, u32 ieoc) { int ret = 0; /* Find corresponding reg value */ u8 reg_ieoc = mt6370_find_closest_reg_value( MT6370_IEOC_MIN, MT6370_IEOC_MAX, MT6370_IEOC_STEP, MT6370_IEOC_NUM, ieoc ); dev_info(chg_data->dev, "%s: ieoc = %d (0x%02X)\n", __func__, ieoc, reg_ieoc); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGCTRL9, MT6370_MASK_IEOC, reg_ieoc << MT6370_SHIFT_IEOC ); return ret; } static int mt6370_get_charging_status(struct mt6370_pmu_charger_data *chg_data, enum mt6370_charging_status *chg_stat) { int ret = 0; ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGSTAT); if (ret < 0) return ret; *chg_stat = (ret & MT6370_MASK_CHG_STAT) >> MT6370_SHIFT_CHG_STAT; return ret; } static int mt6370_set_dc_wdt(struct mt6370_pmu_charger_data *chg_data, u32 us) { int ret = 0; u8 reg_wdt = 0; reg_wdt = mt6370_find_closest_reg_value_via_table( mt6370_dc_wdt, ARRAY_SIZE(mt6370_dc_wdt), us ); dev_info(chg_data->dev, "%s: wdt = %dms(0x%02X)\n", __func__, us / 1000, reg_wdt); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGDIRCHG2, MT6370_MASK_DC_WDT, reg_wdt << MT6370_SHIFT_DC_WDT ); return ret; } static int mt6370_enable_jeita(struct mt6370_pmu_charger_data *chg_data, bool en) { int ret = 0; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGCTRL16, MT6370_MASK_JEITA_EN); return ret; } static int mt6370_set_aicl_vth(struct mt6370_pmu_charger_data *chg_data, u32 aicl_vth) { int ret = 0; u8 reg_aicl_vth = 0; reg_aicl_vth = mt6370_find_closest_reg_value( MT6370_AICL_VTH_MIN, MT6370_AICL_VTH_MAX, MT6370_AICL_VTH_STEP, MT6370_AICL_VTH_NUM, aicl_vth ); dev_info(chg_data->dev, "%s: vth = %d (0x%02X)\n", __func__, aicl_vth, reg_aicl_vth); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGCTRL14, MT6370_MASK_AICL_VTH, reg_aicl_vth << MT6370_SHIFT_AICL_VTH ); if (ret < 0) dev_err(chg_data->dev, "%s: set aicl vth failed, ret = %d\n", __func__, ret); return ret; } static int mt6370_enable_chgdet_flow(struct mt6370_pmu_charger_data *chg_data, bool en) { int ret = 0, i = 0; #ifdef CONFIG_TCPC_CLASS int vbus = 0; #endif const int max_wait_cnt = 200; if (en) { /* Workaround for CDP port */ for (i = 0; i < max_wait_cnt; i++) { if (is_usb_rdy()) break; dev_err(chg_data->dev, "%s: CDP block\n", __func__); #ifdef CONFIG_TCPC_CLASS ret = mt6370_get_adc(chg_data, MT6370_ADC_VBUS_DIV5, &vbus); if (ret >= 0 && vbus < 4300000) { dev_info(chg_data->dev, "%s: plug out, vbus = %dmV\n", __func__, vbus / 1000); return 0; } #endif msleep(100); } if (i == max_wait_cnt) dev_err(chg_data->dev, "%s: CDP timeout\n", __func__); else dev_info(chg_data->dev, "%s: CDP free\n", __func__); } dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); mutex_lock(&chg_data->bc12_access_lock); ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_DEVICETYPE, MT6370_MASK_USBCHGEN); if (ret >= 0) chg_data->bc12_en = en; mutex_unlock(&chg_data->bc12_access_lock); return ret; } static int _mt6370_set_mivr(struct mt6370_pmu_charger_data *chg_data, u32 uV) { int ret = 0; u8 reg_mivr = 0; /* Find corresponding reg value */ reg_mivr = mt6370_find_closest_reg_value( MT6370_MIVR_MIN, MT6370_MIVR_MAX, MT6370_MIVR_STEP, MT6370_MIVR_NUM, uV ); dev_info(chg_data->dev, "%s: mivr = %d (0x%02X)\n", __func__, uV, reg_mivr); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGCTRL6, MT6370_MASK_MIVR, reg_mivr << MT6370_SHIFT_MIVR ); return ret; } #ifndef CONFIG_TCPC_CLASS static void mt6370_chgdet_work_handler(struct work_struct *work) { int ret = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)container_of(work, struct mt6370_pmu_charger_data, chgdet_work); mt6370_set_usbsw_state(chg_data, MT6370_USBSW_CHG); /* Enable USB charger type detection */ ret = mt6370_enable_chgdet_flow(chg_data, true); if (ret < 0) dev_err(chg_data->dev, "%s: enable usb chrdet failed\n", __func__); } #endif /* CONFIG_TCPC_CLASS */ static int _mt6370_set_ichg(struct mt6370_pmu_charger_data *chg_data, u32 uA) { int ret = 0; u8 reg_ichg = 0; /* For adc workaround */ mutex_lock(&chg_data->ichg_access_lock); /* Find corresponding reg value */ reg_ichg = mt6370_find_closest_reg_value( MT6370_ICHG_MIN, MT6370_ICHG_MAX, MT6370_ICHG_STEP, MT6370_ICHG_NUM, uA ); dev_info(chg_data->dev, "%s: ichg = %d (0x%02X)\n", __func__, uA, reg_ichg); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGCTRL7, MT6370_MASK_ICHG, reg_ichg << MT6370_SHIFT_ICHG ); if (chg_data->chip->chip_vid == 0xF0) goto bypass_ieoc_workaround; /* Workaround to make IEOC accurate */ if (uA < 900000) /* 900mA */ ret = mt6370_set_ieoc(chg_data, chg_data->chg_desc->ieoc + 100000); else ret = mt6370_set_ieoc(chg_data, chg_data->chg_desc->ieoc); bypass_ieoc_workaround: /* For adc workaround */ mutex_unlock(&chg_data->ichg_access_lock); return ret; } static int _mt6370_set_aicr(struct mt6370_pmu_charger_data *chg_data, u32 uA) { int ret = 0; u8 reg_aicr = 0; mutex_lock(&chg_data->aicr_access_lock); /* Find corresponding reg value */ reg_aicr = mt6370_find_closest_reg_value( MT6370_AICR_MIN, MT6370_AICR_MAX, MT6370_AICR_STEP, MT6370_AICR_NUM, uA ); dev_info(chg_data->dev, "%s: aicr = %d (0x%02X)\n", __func__, uA, reg_aicr); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGCTRL3, MT6370_MASK_AICR, reg_aicr << MT6370_SHIFT_AICR ); mutex_unlock(&chg_data->aicr_access_lock); return ret; } static int _mt6370_get_cv(struct mt6370_pmu_charger_data *chg_data, u32 *cv) { int ret = 0; u8 reg_cv = 0; ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGCTRL4); if (ret < 0) return ret; reg_cv = (ret & MT6370_MASK_BAT_VOREG) >> MT6370_SHIFT_BAT_VOREG; *cv = mt6370_find_closest_real_value( MT6370_BAT_VOREG_MIN, MT6370_BAT_VOREG_MAX, MT6370_BAT_VOREG_STEP, reg_cv ); return ret; } static int mt6370_get_cv(struct charger_device *chg_dev, u32 *cv) { struct mt6370_pmu_charger_data *chg_data = dev_get_drvdata(&chg_dev->dev); return _mt6370_get_cv(chg_data, cv); } static int _mt6370_set_cv(struct mt6370_pmu_charger_data *chg_data, u32 uV) { int ret = 0, reg_val = 0; u8 reg_cv = 0; u32 ori_cv; /* Get the original cv to check if this step of setting cv is necessary */ ret = _mt6370_get_cv(chg_data, &ori_cv); if (ret < 0) return ret; if (ori_cv == uV) return 0; /* Enable hidden mode */ ret = mt6370_enable_hidden_mode(chg_data, true); if (ret < 0) return ret; /* Store BATOVP Level */ reg_val = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGHIDDENCTRL22); if (reg_val < 0) goto out; /* Disable BATOVP (set 0x45[6:5] = b'11) */ ret = mt6370_pmu_reg_write(chg_data->chip, MT6370_PMU_REG_CHGHIDDENCTRL22, reg_val | MT6370_MASK_BATOVP_LVL); if (ret < 0) goto out; /* Set CV */ reg_cv = mt6370_find_closest_reg_value( MT6370_BAT_VOREG_MIN, MT6370_BAT_VOREG_MAX, MT6370_BAT_VOREG_STEP, MT6370_BAT_VOREG_NUM, uV ); dev_info(chg_data->dev, "%s: bat voreg = %d (0x%02X)\n", __func__, uV, reg_cv); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGCTRL4, MT6370_MASK_BAT_VOREG, reg_cv << MT6370_SHIFT_BAT_VOREG ); if (ret < 0) goto out; /* Delay 5ms */ mdelay(5); /* Enable BATOVP and restore BATOVP level */ ret = mt6370_pmu_reg_write(chg_data->chip, MT6370_PMU_REG_CHGHIDDENCTRL22, reg_val); out: /* Disable hidden mode */ return mt6370_enable_hidden_mode(chg_data, false); } static int mt6370_set_cv(struct charger_device *chg_dev, u32 uV) { int ret = 0; struct mt6370_pmu_charger_data *chg_data = dev_get_drvdata(&chg_dev->dev); ret = _mt6370_set_cv(chg_data, uV); return ret; } static int _mt6370_enable_safety_timer(struct mt6370_pmu_charger_data *chg_data, bool en) { int ret = 0; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGCTRL12, MT6370_MASK_TMR_EN); return ret; } static int _mt6370_enable_hz(struct mt6370_pmu_charger_data *chg_data, bool en) { int ret = 0; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGCTRL1, MT6370_MASK_HZ_EN); return ret; } static int _mt6370_set_ircmp_resistor(struct mt6370_pmu_charger_data *chg_data, u32 uohm) { int ret = 0; u8 reg_resistor = 0; reg_resistor = mt6370_find_closest_reg_value( MT6370_IRCMP_RES_MIN, MT6370_IRCMP_RES_MAX, MT6370_IRCMP_RES_STEP, MT6370_IRCMP_RES_NUM, uohm ); dev_info(chg_data->dev, "%s: resistor = %d (0x%02X)\n", __func__, uohm, reg_resistor); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGCTRL18, MT6370_MASK_IRCMP_RES, reg_resistor << MT6370_SHIFT_IRCMP_RES ); return ret; } static int _mt6370_set_ircmp_vclamp(struct mt6370_pmu_charger_data *chg_data, u32 uV) { int ret = 0; u8 reg_vclamp = 0; reg_vclamp = mt6370_find_closest_reg_value( MT6370_IRCMP_VCLAMP_MIN, MT6370_IRCMP_VCLAMP_MAX, MT6370_IRCMP_VCLAMP_STEP, MT6370_IRCMP_VCLAMP_NUM, uV ); dev_info(chg_data->dev, "%s: vclamp = %d (0x%02X)\n", __func__, uV, reg_vclamp); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGCTRL18, MT6370_MASK_IRCMP_VCLAMP, reg_vclamp << MT6370_SHIFT_IRCMP_VCLAMP ); return ret; } /* =================== */ /* Released interfaces */ /* =================== */ /* This is for GM20's PE20 */ static int mt6370_hw_init(struct mtk_charger_info *mchr_info, void *data) { return 0; } static int mt6370_enable_hz(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; bool en = *((bool *)data); struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; ret = _mt6370_enable_hz(chg_data, en); return ret; } static int mt6370_run_aicl(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u32 mivr = 0, aicl_vth = 0, aicr = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; /* Check whether MIVR loop is active */ if (!(chg_data->irq_flag[MT6370_CHG_IRQIDX_CHGIRQ1] & MT6370_MASK_CHG_MIVR)) { dev_info(chg_data->dev, "%s: mivr loop is not active\n", __func__); return 0; } dev_info(chg_data->dev, "%s: mivr loop is active\n", __func__); /* Clear chg mivr event */ mt6370_chg_irq_clr_flag(chg_data, &chg_data->irq_flag[MT6370_CHG_IRQIDX_CHGIRQ1], MT6370_MASK_CHG_MIVR); ret = mt6370_get_mivr(chg_data, &mivr); if (ret < 0) goto out; /* Check if there's a suitable AICL_VTH */ aicl_vth = mivr + 200; if (aicl_vth > MT6370_AICL_VTH_MAX) { pr_info("%s: no suitable VTH, vth = %d\n", __func__, aicl_vth); ret = -EINVAL; goto out; } ret = mt6370_set_aicl_vth(chg_data, aicl_vth); if (ret < 0) goto out; /* Clear AICL measurement IRQ */ mt6370_chg_irq_clr_flag(chg_data, &chg_data->irq_flag[MT6370_CHG_IRQIDX_CHGIRQ5], MT6370_MASK_CHG_AICLMEASI); ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL14, MT6370_MASK_AICL_MEAS); if (ret < 0) goto out; ret = wait_event_interruptible_timeout(chg_data->wait_queue, chg_data->irq_flag[MT6370_CHG_IRQIDX_CHGIRQ5] & MT6370_MASK_CHG_AICLMEASI, msecs_to_jiffies(2500)); if (ret <= 0) { pr_err("%s: wait AICL time out, ret = %d\n", __func__, ret); ret = -EIO; goto out; } ret = mt6370_get_aicr(mchr_info, &aicr); if (ret < 0) goto out; *((u32 *)data) = aicr / 100; dev_info(chg_data->dev, "%s: aicr upper bound = %dmA\n", __func__, aicr / 100); goto en_mivrirq; out: *((u32 *)data) = 0; en_mivrirq: ret = mt6370_pmu_reg_clr_bit(chg_data->chip, MT6370_PMU_CHGMASK1, MT6370_MASK_CHG_MIVRM); return ret; } static int mt6370_set_ircmp_resistor(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u32 uV = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; uV = *((u32 *)data) * 1000; ret = _mt6370_set_ircmp_resistor(chg_data, uV); return ret; } static int mt6370_set_ircmp_vclamp(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u32 uV = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; uV = *((u32 *)data) * 1000; ret = _mt6370_set_ircmp_vclamp(chg_data, uV); return ret; } static int mt6370_set_error_state(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; chg_data->err_state = *((bool *)data); mt6370_enable_hz(mchr_info, &chg_data->err_state); return ret; } static int mt6370_get_charger_type(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; dev_info(chg_data->dev, "%s\n", __func__); #ifndef CONFIG_TCPC_CLASS mt6370_set_usbsw_state(chg_data, MT6370_USBSW_CHG); /* Clear attachi event */ mt6370_chg_irq_clr_flag(chg_data, &chg_data->irq_flag[MT6370_CHG_IRQIDX_QCIRQ], MT6370_MASK_ATTACHI); /* Turn off/on USB charger detection to retrigger bc1.2 */ ret = mt6370_enable_chgdet_flow(chg_data, false); if (ret < 0) pr_err("%s: disable usb chrdet failed\n", __func__); ret = mt6370_enable_chgdet_flow(chg_data, true); if (ret < 0) pr_err("%s: disable usb chrdet failed\n", __func__); #endif ret = wait_event_interruptible_timeout(chg_data->wait_queue, chg_data->irq_flag[MT6370_CHG_IRQIDX_QCIRQ] & MT6370_MASK_ATTACHI, msecs_to_jiffies(1000)); if (ret <= 0) { pr_err("%s: wait attachi failed, ret = %d\n", __func__, ret); chg_data->chg_type = CHARGER_UNKNOWN; } *(CHARGER_TYPE *)data = chg_data->chg_type; pr_info("%s: chg_type = %d\n", __func__, chg_data->chg_type); return ret; } static int mt6370_enable_charging(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; bool en = *((bool *)data); dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGCTRL2, MT6370_MASK_CHG_EN); return ret; } static int mt6370_enable_safety_timer(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; bool en = *((bool *)data); ret = _mt6370_enable_safety_timer(chg_data, en); return ret; } static int mt6370_is_safety_timer_enable(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL12, MT6370_SHIFT_TMR_EN, data); if (ret < 0) return ret; return ret; } static int mt6370_enable_power_path(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; bool en = *((bool *)data); u32 mivr = en ? 4500000 : MT6370_MIVR_MAX; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); /* * enable power path -> unmask mivr irq * mask mivr irq -> disable power path */ if (!en) ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_CHGMASK1, MT6370_MASK_CHG_MIVRM); ret = _mt6370_set_mivr(chg_data, mivr); if (en) ret = mt6370_pmu_reg_clr_bit(chg_data->chip, MT6370_PMU_CHGMASK1, MT6370_MASK_CHG_MIVRM); return ret; } static int mt6370_is_power_path_enable(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; u32 mivr = 0; ret = mt6370_get_mivr(chg_data, &mivr); *((bool *)data) = (mivr == MT6370_MIVR_MAX ? false : true); return ret; } static int mt6370_get_ichg(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u8 reg_ichg = 0; u32 ichg = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGCTRL7); if (ret < 0) return ret; reg_ichg = (ret & MT6370_MASK_ICHG) >> MT6370_SHIFT_ICHG; ichg = mt6370_find_closest_real_value(MT6370_ICHG_MIN, MT6370_ICHG_MAX, MT6370_ICHG_STEP, reg_ichg); /* MTK's current unit : 10uA */ /* Our current unit : uA */ ichg /= 10; *((u32 *)data) = ichg; return ret; } static int mt6370_set_ichg(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u32 uA = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; /* MTK's current unit : 10uA */ /* Our current unit : uA */ uA = *((u32 *)data) * 10; ret = _mt6370_set_ichg(chg_data, uA); return ret; } static int mt6370_get_aicr(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u8 reg_aicr = 0; u32 aicr = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGCTRL3); if (ret < 0) return ret; reg_aicr = (ret & MT6370_MASK_AICR) >> MT6370_SHIFT_AICR; aicr = mt6370_find_closest_real_value(MT6370_AICR_MIN, MT6370_AICR_MAX, MT6370_AICR_STEP, reg_aicr); /* MTK's current unit : 10uA */ /* Our current unit : uA */ aicr /= 10; *((u32 *)data) = aicr; return ret; } static int mt6370_set_aicr(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u32 uA = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; /* MTK's current unit : 10uA */ /* Our current unit : uA */ uA = *((u32 *)data) * 10; ret = _mt6370_set_aicr(chg_data, uA); return ret; } static int mt6370_set_mivr(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u32 uV = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; bool en = true; ret = mt6370_is_power_path_enable(mchr_info, &en); if (!en) { dev_err(chg_data->dev, "%s: power path is disabled\n", __func__); return -EINVAL; } /* MTK's current unit : mV */ /* Our current unit : uV */ uV = *((u32 *)data) * 1000; ret = _mt6370_set_mivr(chg_data, uV); return ret; } static int mt6370_set_otg_current_limit(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u8 reg_ilimit = 0; u32 uA = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; /* MTK's current unit : mA */ /* Our current unit : uA */ uA = *((u32 *)data) * 1000; reg_ilimit = mt6370_find_closest_reg_value_via_table( mt6370_otg_oc_threshold, ARRAY_SIZE(mt6370_otg_oc_threshold), uA ); dev_info(chg_data->dev, "%s: ilimit = %d (0x%02X)\n", __func__, uA, reg_ilimit); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGCTRL10, MT6370_MASK_BOOST_OC, reg_ilimit << MT6370_SHIFT_BOOST_OC ); return ret; } static int mt6370_enable_otg(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; bool en = *((bool *)data); bool en_otg = false; u32 current_limit = 500; /* mA */ struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; u8 hidden_val = en ? 0x00 : 0x0F; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); /* Set OTG_OC to 500mA */ ret = mt6370_set_otg_current_limit(mchr_info, ¤t_limit); if (ret < 0) { dev_err(chg_data->dev, "%s: set otg oc failed\n", __func__); return ret; } /* Turn off USB charger detection/Enable WDT */ if (en) { ret = mt6370_enable_chgdet_flow(chg_data, false); if (ret < 0) dev_err(chg_data->dev, "%s: disable usb chrdet failed\n", __func__); if (chg_data->chg_desc->en_otg_wdt) { ret = mt6370_enable_wdt(chg_data, true); if (ret < 0) dev_err(chg_data->dev, "%s: en wdt failed\n", __func__); } } /* Switch OPA mode to boost mode */ ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGCTRL1, MT6370_MASK_OPA_MODE); msleep(20); if (en) { ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL1, MT6370_SHIFT_OPA_MODE, &en_otg); if (ret < 0 || !en_otg) { dev_err(chg_data->dev, "%s: failed, ret = %d\n", __func__, ret); /* Disable OTG */ mt6370_pmu_reg_clr_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL1, MT6370_MASK_OPA_MODE); /* Disable WDT */ ret = mt6370_enable_wdt(chg_data, false); if (ret < 0) dev_err(chg_data->dev, "%s: disable wdt failed\n", __func__); #ifndef CONFIG_TCPC_CLASS ret = mt6370_enable_chgdet_flow(chg_data, true); if (ret < 0) dev_err(chg_data->dev, "%s: en usb chrdet failed\n", __func__); #endif return -EIO; } #ifndef CONFIG_TCPC_CLASS mt6370_set_usbsw_state(chg_data, MT6370_USBSW_USB); #endif } /* * Woraround reg[0x25] = 0x00 after entering OTG mode * reg[0x25] = 0x0F after leaving OTG mode */ ret = mt6370_enable_hidden_mode(chg_data, true); if (ret < 0) dev_err(chg_data->dev, "%s: enter hidden mode failed\n", __func__); else { ret = mt6370_pmu_reg_write(chg_data->chip, MT6370_PMU_REG_CHGHIDDENCTRL6, hidden_val); if (ret < 0) dev_err(chg_data->dev, "%s: workaroud failed, ret = %d\n", __func__, ret); ret = mt6370_enable_hidden_mode(chg_data, false); if (ret < 0) dev_err(chg_data->dev, "%s: exist hidden mode failed\n", __func__); } /* Disable WDT */ if (!en) { ret = mt6370_enable_wdt(chg_data, false); if (ret < 0) dev_err(chg_data->dev, "%s: disable wdt failed\n", __func__); } return ret; } static int mt6370_set_pep_current_pattern(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; bool is_increase = *((bool *)data); struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; dev_info(chg_data->dev, "%s: pe1.0 pump_up = %d\n", __func__, is_increase); /* Set to PE1.0 */ ret = mt6370_pmu_reg_clr_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL17, MT6370_MASK_PUMPX_20_10); /* Set Pump Up/Down */ ret = (is_increase ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGCTRL17, MT6370_MASK_PUMPX_UP_DN); if (ret < 0) return ret; /* Enable PumpX */ ret = mt6370_enable_pump_express(chg_data, true); return ret; } static int mt6370_set_pep20_reset(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u32 mivr = 4500; /* mA */ u32 ichg = 51200; /* 10uA */ u32 aicr = 10000; /* 10uA */ ret = mt6370_set_mivr(mchr_info, &mivr); if (ret < 0) return ret; ret = mt6370_set_ichg(mchr_info, &ichg); if (ret < 0) return ret; ret = mt6370_set_aicr(mchr_info, &aicr); if (ret < 0) return ret; msleep(250); aicr = 70000; /* 10uA */ ret = mt6370_set_aicr(mchr_info, &aicr); return ret; } static int mt6370_set_pep20_current_pattern(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u8 reg_volt = 0; u32 uV = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; /* MTK's current unit : mA */ /* Our current unit : uA */ uV = *((u32 *)data) * 1000; dev_info(chg_data->dev, "%s: pep2.0 = %d\n", __func__, uV); /* Set to PEP2.0 */ ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL17, MT6370_MASK_PUMPX_20_10); if (ret < 0) return ret; /* Find register value of target voltage */ reg_volt = mt6370_find_closest_reg_value( MT6370_PEP20_VOLT_MIN, MT6370_PEP20_VOLT_MAX, MT6370_PEP20_VOLT_STEP, MT6370_PEP20_VOLT_NUM, uV ); /* Set Voltage */ ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGCTRL17, MT6370_MASK_PUMPX_DEC, reg_volt << MT6370_SHIFT_PUMPX_DEC ); if (ret < 0) return ret; /* Enable PumpX */ ret = mt6370_enable_pump_express(chg_data, true); ret = (ret >= 0) ? 0 : ret; return ret; } static int mt6370_set_pep20_efficiency_table(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; pep20_profile_t *profile = (pep20_profile_t *)data; profile[0].vchr = 8000; profile[1].vchr = 8000; profile[2].vchr = 8000; profile[3].vchr = 8500; profile[4].vchr = 8500; profile[5].vchr = 8500; profile[6].vchr = 9000; profile[7].vchr = 9000; profile[8].vchr = 9500; profile[9].vchr = 9500; return ret; } static int mt6370_is_charging_done(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; enum mt6370_charging_status chg_stat = MT6370_CHG_STATUS_READY; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; ret = mt6370_get_charging_status(chg_data, &chg_stat); if (ret < 0) return ret; /* Return is charging done or not */ switch (chg_stat) { case MT6370_CHG_STATUS_READY: case MT6370_CHG_STATUS_PROGRESS: case MT6370_CHG_STATUS_FAULT: *((u32 *)data) = false; break; case MT6370_CHG_STATUS_DONE: *((u32 *)data) = true; break; default: *((u32 *)data) = false; break; } return 0; } static int mt6370_kick_wdt(struct mtk_charger_info *mchr_info, void *data) { /* Any I2C communication can kick watchdog timer */ int ret = 0; enum mt6370_charging_status chg_status; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; ret = mt6370_get_charging_status(chg_data, &chg_status); return ret; } static int mt6370_enable_direct_charge(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; bool en = *((bool *)data); struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); if (en) { ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_CHGMASK1, MT6370_MASK_CHG_MIVRM); if (ret < 0) dev_err(chg_data->dev, "%s: mask MIVR IRQ failed\n", __func__); /* Enable bypass mode */ ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL2, MT6370_MASK_BYPASS_MODE); if (ret < 0) { dev_err(chg_data->dev, "%s: en bypass mode failed\n", __func__); goto out; } /* VG_EN = 1 */ ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_REG_CHGPUMP, MT6370_MASK_VG_EN); if (ret < 0) { dev_err(chg_data->dev, "%s: en VG_EN failed\n", __func__); goto disable_bypass; } return ret; } /* Disable direct charge */ /* VG_EN = 0 */ ret = mt6370_pmu_reg_clr_bit(chg_data->chip, MT6370_PMU_REG_CHGPUMP, MT6370_MASK_VG_EN); if (ret < 0) dev_err(chg_data->dev, "%s: disable VG_EN failed\n", __func__); disable_bypass: /* Disable bypass mode */ ret = mt6370_pmu_reg_clr_bit(chg_data->chip, MT6370_PMU_REG_CHGCTRL2, MT6370_MASK_BYPASS_MODE); if (ret < 0) dev_err(chg_data->dev, "%s: disable bypass mode failed\n", __func__); out: /* Unmask MIVR IRQ */ ret = mt6370_pmu_reg_clr_bit(chg_data->chip, MT6370_PMU_CHGMASK1, MT6370_MASK_CHG_MIVRM); if (ret < 0) dev_err(chg_data->dev, "%s: unmask MIVR IRQ failed\n", __func__); return ret; } static int mt6370_enable_dc_vbusov(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; bool en = *((bool *)data); struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGDIRCHG3, MT6370_MASK_DC_VBUSOV_EN); return ret; } static int mt6370_set_dc_vbusov(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u8 reg_vbusov = 0; u32 uV = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; /* MTK's current unit : mV */ /* Our current unit : uV */ uV = *((u32 *)data) * 1000; reg_vbusov = mt6370_find_closest_reg_value( MT6370_DC_VBUSOV_LVL_MIN, MT6370_DC_VBUSOV_LVL_MAX, MT6370_DC_VBUSOV_LVL_STEP, MT6370_DC_VBUSOV_LVL_NUM, uV ); dev_info(chg_data->dev, "%s: vbusov = %d (0x%02X)\n", __func__, uV, reg_vbusov); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGDIRCHG3, MT6370_MASK_DC_VBUSOV_LVL, reg_vbusov << MT6370_SHIFT_DC_VBUSOV_LVL ); return ret; } static int mt6370_enable_dc_ibusoc(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; bool en = *((bool *)data); struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGDIRCHG1, MT6370_MASK_DC_IBUSOC_EN); return ret; } static int mt6370_set_dc_ibusoc(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; u8 reg_ibusoc = 0; u32 uA = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; /* MTK's current unit : mA */ /* Our current unit : uA */ uA = *((u32 *)data) * 1000; reg_ibusoc = mt6370_find_closest_reg_value( MT6370_DC_IBUSOC_LVL_MIN, MT6370_DC_IBUSOC_LVL_MAX, MT6370_DC_IBUSOC_LVL_STEP, MT6370_DC_IBUSOC_LVL_NUM, uA ); dev_info(chg_data->dev, "%s: ibusoc = %d (0x%02X)\n", __func__, uA, reg_ibusoc); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGDIRCHG1, MT6370_MASK_DC_IBUSOC_LVL, reg_ibusoc << MT6370_SHIFT_DC_IBUSOC_LVL ); return ret; } static int mt6370_enable_dc_vbatov(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; bool en = *((bool *)data); struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); ret = (en ? mt6370_pmu_reg_set_bit : mt6370_pmu_reg_clr_bit) (chg_data->chip, MT6370_PMU_REG_CHGDIRCHG1, MT6370_MASK_DC_VBATOV_EN); return ret; } static int mt6370_set_dc_vbatov(struct mtk_charger_info *mchr_info, void *data) { int ret = 0, i = 0; u8 reg_vbatov = 0; u32 cv = 0, vbatov = 0; u32 uV = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; /* MTK's current unit : mV */ /* Our current unit : uV */ uV = *((u32 *)data) * 1000; ret = mt6370_get_cv(mchr_info, &cv); if (ret < 0) { dev_err(chg_data->dev, "%s: get voreg failed\n", __func__); return ret; } for (i = 0; i < ARRAY_SIZE(mt6370_dc_vbatov_lvl); i++) { vbatov = (mt6370_dc_vbatov_lvl[i] * cv) / 100; /* Choose closest level */ if (uV <= vbatov) { reg_vbatov = i; break; } } if (i == ARRAY_SIZE(mt6370_dc_vbatov_lvl)) reg_vbatov = i; dev_info(chg_data->dev, "%s: vbatov = %dmV (0x%02X)\n", __func__, uV / 1000, reg_vbatov); ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_CHGDIRCHG1, MT6370_MASK_DC_VBATOV_LVL, reg_vbatov << MT6370_SHIFT_DC_VBATOV_LVL ); return ret; } static int mt6370_is_dc_enable(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGPUMP, MT6370_SHIFT_VG_EN, data); return ret; } static int mt6370_kick_dc_wdt(struct mtk_charger_info *mchr_info, void *data) { /* Any I2C communication can reset watchdog timer */ int ret = 0; enum mt6370_charging_status chg_status; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; ret = mt6370_get_charging_status(chg_data, &chg_status); return ret; } static int mt6370_get_tchg(struct mtk_charger_info *mchr_info, void *data) { int ret = 0, adc_temp = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; /* Get value from ADC */ ret = mt6370_get_adc(chg_data, MT6370_ADC_TEMP_JC, &adc_temp); if (ret < 0) return ret; ((int *)data)[0] = adc_temp; ((int *)data)[1] = adc_temp; dev_info(chg_data->dev, "%s: tchg = %d\n", __func__, adc_temp); return ret; } static int mt6370_get_ibus(struct mtk_charger_info *mchr_info, void *data) { int ret = 0, adc_ibus = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; /* Get value from ADC */ ret = mt6370_get_adc(chg_data, MT6370_ADC_IBUS, &adc_ibus); if (ret < 0) return ret; *((u32 *)data) = adc_ibus / 1000; dev_info(chg_data->dev, "%s: ibus = %dmA\n", __func__, adc_ibus / 1000); return ret; } static int mt6370_get_vbus(struct mtk_charger_info *mchr_info, void *data) { int ret = 0, adc_vbus = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; /* Get value from ADC */ ret = mt6370_get_adc(chg_data, MT6370_ADC_VBUS_DIV2, &adc_vbus); if (ret < 0) return ret; *((u32 *)data) = adc_vbus / 1000; dev_info(chg_data->dev, "%s: vbus = %dmV\n", __func__, adc_vbus / 1000); return ret; } static int mt6370_dump_register(struct mtk_charger_info *mchr_info, void *data) { int i = 0, ret = 0; u32 ichg = 0, aicr = 0, mivr = 0, ieoc = 0, cv = 0; bool chg_en = 0; int adc_vsys = 0, adc_vbat = 0, adc_ibat = 0, adc_ibus = 0; enum mt6370_charging_status chg_status = MT6370_CHG_STATUS_READY; u8 chg_stat = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; ret = mt6370_get_ichg(mchr_info, &ichg); /* 10uA */ ret = mt6370_get_aicr(mchr_info, &aicr); /* 10uA */ ret = mt6370_get_charging_status(chg_data, &chg_status); ret = mt6370_get_ieoc(chg_data, &ieoc); ret = mt6370_get_mivr(chg_data, &mivr); ret = mt6370_get_cv(mchr_info, &cv); ret = mt6370_is_charging_enable(chg_data, &chg_en); ret = mt6370_get_adc(chg_data, MT6370_ADC_VSYS, &adc_vsys); ret = mt6370_get_adc(chg_data, MT6370_ADC_VBAT, &adc_vbat); ret = mt6370_get_adc(chg_data, MT6370_ADC_IBAT, &adc_ibat); ret = mt6370_get_adc(chg_data, MT6370_ADC_IBUS, &adc_ibus); chg_stat = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_CHGSTAT1); if (chg_status == MT6370_CHG_STATUS_FAULT) { for (i = 0; i < ARRAY_SIZE(mt6370_chg_reg_addr); i++) { ret = mt6370_pmu_reg_read(chg_data->chip, mt6370_chg_reg_addr[i]); if (ret < 0) return ret; dev_dbg(chg_data->dev, "%s: reg[0x%02X] = 0x%02X\n", __func__, mt6370_chg_reg_addr[i], ret); } } dev_info(chg_data->dev, "%s: ICHG = %dmA, AICR = %dmA, MIVR = %dmV, IEOC = %dmA, CV = %dmV\n", __func__, ichg / 100, aicr / 100, mivr / 1000, ieoc / 1000, cv / 1000); dev_info(chg_data->dev, "%s: VSYS = %dmV, VBAT = %dmV, IBAT = %dmA, IBUS = %dmA\n", __func__, adc_vsys / 1000, adc_vbat / 1000, adc_ibat / 1000, adc_ibus / 1000); dev_info(chg_data->dev, "%s: CHG_EN = %d, CHG_STATUS = %s, CHG_STAT = 0x%02X\n", __func__, chg_en, mt6370_chg_status_name[chg_status], chg_stat); ret = 0; return ret; } static int mt6370_enable_chg_type_det(struct mtk_charger_info *mchr_info, void *data) { int ret = 0; #ifdef CONFIG_TCPC_CLASS bool en = *(bool *)data; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)mchr_info; dev_info(chg_data->dev, "%s: en = %d\n", __func__, en); /* TypeC attach, clear attachi event */ if (en) mt6370_chg_irq_clr_flag(chg_data, &chg_data->irq_flag[MT6370_CHG_IRQIDX_QCIRQ], MT6370_MASK_ATTACHI); else { /* TypeC detach */ ret = mt6370_enable_ilim(chg_data, true); if (ret < 0) dev_err(chg_data->dev, "%s: en ilim failed\n", __func__); chg_data->chg_type = CHARGER_UNKNOWN; } /* No matter plug in/out, make usb switch to MT6370 */ mt6370_set_usbsw_state(chg_data, MT6370_USBSW_CHG); ret = mt6370_enable_chgdet_flow(chg_data, en); if (ret < 0) dev_err(chg_data->dev, "%s: en = %d, failed\n", __func__, en); #endif return ret; } #ifdef MT6370_APPLE_SAMSUNG_TA_SUPPORT static int mt6370_detect_apple_samsung_ta( struct mt6370_pmu_charger_data *chg_data) { int ret = 0; bool dcd_timeout = false; bool dp_0_9v = false, dp_1_5v = false, dp_2_3v = false, dm_2_3v = false; /* Only SDP/CDP/DCP could possibly be Apple/Samsung TA */ if (chg_data->chg_type != STANDARD_HOST && chg_data->chg_type != CHARGING_HOST && chg_data->chg_type != STANDARD_CHARGER) return -EINVAL; if (chg_data->chg_type == STANDARD_HOST || chg_data->chg_type == CHARGING_HOST) { ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_QCSTAT, MT6370_SHIFT_DCDTI_STAT, &dcd_timeout); if (ret < 0) { dev_err(chg_data->dev, "%s: read dcd timeout failed\n", __func__); return ret; } if (!dcd_timeout) { dev_info(chg_data->dev, "%s: dcd is not timeout\n", __func__); return 0; } } /* Check DP > 0.9V */ ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_QCSTATUS2, 0x03, 0x03 ); ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_QCSTATUS2, 4, &dp_0_9v); if (ret < 0) return ret; if (!dp_0_9v) { dev_info(chg_data->dev, "%s: DP < 0.9V\n", __func__); return ret; } ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_QCSTATUS2, 5, &dp_1_5v); if (ret < 0) return ret; /* Samsung charger */ if (!dp_1_5v) { dev_info(chg_data->dev, "%s: 0.9V < DP < 1.5V\n", __func__); chg_data->chg_type = SAMSUNG_CHARGER; return ret; } /* Check DP > 2.3 V */ ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_QCSTATUS2, 0x0B, 0x0B ); ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_QCSTATUS2, 5, &dp_2_3v); if (ret < 0) return ret; /* Check DM > 2.3V */ ret = mt6370_pmu_reg_update_bits( chg_data->chip, MT6370_PMU_REG_QCSTATUS2, 0x0F, 0x0F ); ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_QCSTATUS2, 5, &dm_2_3v); if (ret < 0) return ret; /* Apple charger */ if (!dp_2_3v && !dm_2_3v) { dev_info(chg_data->dev, "%s: 1.5V < DP < 2.3V && DM < 2.3V\n", __func__); chg_data->chg_type = APPLE_0_5A_CHARGER; } else if (!dp_2_3v && dm_2_3v) { dev_info(chg_data->dev, "%s: 1.5V < DP < 2.3V && 2.3V < DM\n", __func__); chg_data->chg_type = APPLE_1_0A_CHARGER; } else if (dp_2_3v && !dm_2_3v) { dev_info(chg_data->dev, "%s: 2.3V < DP && DM < 2.3V\n", __func__); chg_data->chg_type = APPLE_2_1A_CHARGER; } else { dev_info(chg_data->dev, "%s: 2.3V < DP && 2.3V < DM\n", __func__); chg_data->chg_type = APPLE_2_4A_CHARGER; } return 0; } #endif static irqreturn_t mt6370_pmu_chg_treg_irq_handler(int irq, void *data) { int ret = 0; bool treg_stat = false; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_err(chg_data->dev, "%s\n", __func__); /* Read treg status */ ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGSTAT1, MT6370_SHIFT_CHG_TREG, &treg_stat); if (ret < 0) dev_err(chg_data->dev, "%s: read treg stat failed\n", __func__); else dev_err(chg_data->dev, "%s: treg stat = %d\n", __func__, treg_stat); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_aicr_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_mivr_irq_handler(int irq, void *data) { int ret = 0; bool mivr_stat = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGSTAT1, MT6370_SHIFT_MIVR_STAT, &mivr_stat); if (ret < 0) { dev_err(chg_data->dev, "%s: read mivr stat failed\n", __func__); goto out; } if (!mivr_stat) { dev_info(chg_data->dev, "%s: mivr stat not act\n", __func__); goto out; } /* Disable MIVR IRQ */ ret = mt6370_pmu_reg_set_bit(chg_data->chip, MT6370_PMU_CHGMASK1, MT6370_MASK_CHG_MIVRM); if (ret < 0) { dev_err(chg_data->dev, "%s: disable mivr IRQ failed\n", __func__); goto out; } mt6370_chg_irq_set_flag(chg_data, &chg_data->irq_flag[MT6370_CHG_IRQIDX_CHGIRQ1], MT6370_MASK_CHG_MIVR); out: return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_pwr_rdy_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_vinovp_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_vsysuv_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_vsysov_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_vbatov_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_vbusov_irq_handler(int irq, void *data) { int ret = 0; bool vbusov_stat = false; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_err(chg_data->dev, "%s\n", __func__); ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGSTAT2, MT6370_SHIFT_CHG_VBUSOV_STAT, &vbusov_stat); if (ret < 0) return IRQ_HANDLED; dev_info(chg_data->dev, "%s: stat = %d\n", __func__, vbusov_stat); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_ts_bat_cold_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_ts_bat_cool_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_ts_bat_warm_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_ts_bat_hot_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_tmri_irq_handler(int irq, void *data) { int ret = 0; bool tmr_stat = false; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_err(chg_data->dev, "%s\n", __func__); ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGSTAT4, MT6370_SHIFT_CHG_TMRI_STAT, &tmr_stat); if (ret < 0) return IRQ_HANDLED; dev_info(chg_data->dev, "%s: stat = %d\n", __func__, tmr_stat); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_batabsi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_adpbadi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_rvpi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_otpi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_aiclmeasi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); mt6370_chg_irq_set_flag(chg_data, &chg_data->irq_flag[MT6370_CHG_IRQIDX_CHGIRQ5], MT6370_MASK_CHG_AICLMEASI); wake_up_interruptible(&chg_data->wait_queue); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_ichgmeasi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chgdet_donei_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_wdtmri_irq_handler(int irq, void *data) { int ret = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); ret = mt6370_kick_wdt(&chg_data->mchr_info, NULL); if (ret < 0) dev_err(chg_data->dev, "%s: kick wdt failed\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_ssfinishi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_rechgi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_termi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chg_ieoci_irq_handler(int irq, void *data) { int ret = 0; bool ieoc_stat = false; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); ret = mt6370_pmu_reg_test_bit(chg_data->chip, MT6370_PMU_REG_CHGSTAT5, MT6370_SHIFT_CHG_IEOCI_STAT, &ieoc_stat); if (ret < 0) return IRQ_HANDLED; dev_info(chg_data->dev, "%s: stat = %d\n", __func__, ieoc_stat); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_adc_donei_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_pumpx_donei_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_bst_batuvi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_bst_vbusovi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_bst_olpi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_attachi_irq_handler(int irq, void *data) { int ret = 0; u8 usb_status = 0; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); /* Check bc12 enable flag */ mutex_lock(&chg_data->bc12_access_lock); if (!chg_data->bc12_en) { dev_err(chg_data->dev, "%s: bc12 disabled, ignore irq\n", __func__); mutex_unlock(&chg_data->bc12_access_lock); return IRQ_HANDLED; } mutex_unlock(&chg_data->bc12_access_lock); ret = mt6370_pmu_reg_read(chg_data->chip, MT6370_PMU_REG_USBSTATUS1); if (ret < 0) { dev_err(chg_data->dev, "%s: read charger type failed\n", __func__); return IRQ_HANDLED; } usb_status = (ret & MT6370_MASK_USB_STATUS) >> MT6370_SHIFT_USB_STATUS; chg_data->chg_online = true; switch (usb_status) { case MT6370_CHG_TYPE_SDP: chg_data->chg_type = STANDARD_HOST; break; case MT6370_CHG_TYPE_SDPNSTD: chg_data->chg_type = NONSTANDARD_CHARGER; break; case MT6370_CHG_TYPE_CDP: chg_data->chg_type = CHARGING_HOST; break; case MT6370_CHG_TYPE_DCP: chg_data->chg_type = STANDARD_CHARGER; break; default: chg_data->chg_type = CHARGER_UNKNOWN; break; } #ifdef MT6370_APPLE_SAMSUNG_TA_SUPPORT ret = mt6370_detect_apple_samsung_ta(chg_data); if (ret < 0) dev_err(chg_data->dev, "%s: detect apple/samsung ta failed, ret = %d\n", __func__, ret); #endif mt6370_chg_irq_set_flag(chg_data, &chg_data->irq_flag[MT6370_CHG_IRQIDX_QCIRQ], MT6370_MASK_ATTACHI); wake_up_interruptible(&chg_data->wait_queue); /* Turn off USB charger detection */ ret = mt6370_enable_chgdet_flow(chg_data, false); if (ret < 0) dev_err(chg_data->dev, "%s: disable usb chrdet failed\n", __func__); if (chg_data->chg_type == STANDARD_HOST || chg_data->chg_type == CHARGING_HOST) mt6370_set_usbsw_state(chg_data, MT6370_USBSW_USB); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_detachi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_qc30stpdone_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_qc_vbusdet_done_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_hvdcp_det_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_chgdeti_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_dcdti_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_dirchg_vgoki_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_info(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_dirchg_wdtmri_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_dirchg_uci_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_dirchg_oci_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_dirchg_ovi_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_ovpctrl_swon_evt_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_ovpctrl_uvp_d_evt_irq_handler(int irq, void *data) { #ifndef CONFIG_TCPC_CLASS int ret = 0; bool uvp_d_stat = false; struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_err(chg_data->dev, "%s\n", __func__); /* Check UVP_D_STAT */ ret = mt6370_pmu_reg_test_bit( chg_data->chip, MT6370_PMU_REG_OVPCTRLSTAT, MT6370_SHIFT_OVPCTRL_UVP_D_STAT, &uvp_d_stat ); if (!uvp_d_stat) { dev_info(chg_data->dev, "%s: no uvp_d_stat\n", __func__); return IRQ_HANDLED; } /* Plug out */ chg_data->chg_online = false; chg_data->chg_type = CHARGER_UNKNOWN; /* Turn on USB charger detection */ ret = mt6370_enable_chgdet_flow(chg_data, true); if (ret < 0) dev_err(chg_data->dev, "%s: en usb chrdet failed\n", __func__); /* Switch DPDM to MT6370 */ mt6370_set_usbsw_state(chg_data, MT6370_USBSW_CHG); #endif /* CONFIG_TCPC_CLASS */ return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_ovpctrl_uvp_evt_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_ovpctrl_ovp_d_evt_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static irqreturn_t mt6370_pmu_ovpctrl_ovp_evt_irq_handler(int irq, void *data) { struct mt6370_pmu_charger_data *chg_data = (struct mt6370_pmu_charger_data *)data; dev_notice(chg_data->dev, "%s\n", __func__); return IRQ_HANDLED; } static struct mt6370_pmu_irq_desc mt6370_chg_irq_desc[] = { MT6370_PMU_IRQDESC(chg_treg), MT6370_PMU_IRQDESC(chg_aicr), MT6370_PMU_IRQDESC(chg_mivr), MT6370_PMU_IRQDESC(pwr_rdy), MT6370_PMU_IRQDESC(chg_vinovp), MT6370_PMU_IRQDESC(chg_vsysuv), MT6370_PMU_IRQDESC(chg_vsysov), MT6370_PMU_IRQDESC(chg_vbatov), MT6370_PMU_IRQDESC(chg_vbusov), MT6370_PMU_IRQDESC(ts_bat_cold), MT6370_PMU_IRQDESC(ts_bat_cool), MT6370_PMU_IRQDESC(ts_bat_warm), MT6370_PMU_IRQDESC(ts_bat_hot), MT6370_PMU_IRQDESC(chg_tmri), MT6370_PMU_IRQDESC(chg_batabsi), MT6370_PMU_IRQDESC(chg_adpbadi), MT6370_PMU_IRQDESC(chg_rvpi), MT6370_PMU_IRQDESC(otpi), MT6370_PMU_IRQDESC(chg_aiclmeasi), MT6370_PMU_IRQDESC(chg_ichgmeasi), MT6370_PMU_IRQDESC(chgdet_donei), MT6370_PMU_IRQDESC(chg_wdtmri), MT6370_PMU_IRQDESC(ssfinishi), MT6370_PMU_IRQDESC(chg_rechgi), MT6370_PMU_IRQDESC(chg_termi), MT6370_PMU_IRQDESC(chg_ieoci), MT6370_PMU_IRQDESC(adc_donei), MT6370_PMU_IRQDESC(pumpx_donei), MT6370_PMU_IRQDESC(bst_batuvi), MT6370_PMU_IRQDESC(bst_vbusovi), MT6370_PMU_IRQDESC(bst_olpi), MT6370_PMU_IRQDESC(attachi), MT6370_PMU_IRQDESC(detachi), MT6370_PMU_IRQDESC(qc30stpdone), MT6370_PMU_IRQDESC(qc_vbusdet_done), MT6370_PMU_IRQDESC(hvdcp_det), MT6370_PMU_IRQDESC(chgdeti), MT6370_PMU_IRQDESC(dcdti), MT6370_PMU_IRQDESC(dirchg_vgoki), MT6370_PMU_IRQDESC(dirchg_wdtmri), MT6370_PMU_IRQDESC(dirchg_uci), MT6370_PMU_IRQDESC(dirchg_oci), MT6370_PMU_IRQDESC(dirchg_ovi), MT6370_PMU_IRQDESC(ovpctrl_swon_evt), MT6370_PMU_IRQDESC(ovpctrl_uvp_d_evt), MT6370_PMU_IRQDESC(ovpctrl_uvp_evt), MT6370_PMU_IRQDESC(ovpctrl_ovp_d_evt), MT6370_PMU_IRQDESC(ovpctrl_ovp_evt), }; static void mt6370_pmu_charger_irq_register(struct platform_device *pdev) { struct resource *res; int i, ret = 0; for (i = 0; i < ARRAY_SIZE(mt6370_chg_irq_desc); i++) { if (!mt6370_chg_irq_desc[i].name) continue; res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, mt6370_chg_irq_desc[i].name); if (!res) continue; ret = devm_request_threaded_irq(&pdev->dev, res->start, NULL, mt6370_chg_irq_desc[i].irq_handler, IRQF_TRIGGER_FALLING, mt6370_chg_irq_desc[i].name, platform_get_drvdata(pdev)); if (ret < 0) { dev_err(&pdev->dev, "request %s irq fail\n", res->name); continue; } mt6370_chg_irq_desc[i].irq = res->start; } } static inline int mt_parse_dt(struct device *dev, struct mt6370_pmu_charger_data *chg_data) { struct mt6370_pmu_charger_desc *chg_desc = NULL; struct device_node *np = dev->of_node; struct i2c_client *i2c = chg_data->chip->i2c; dev_info(chg_data->dev, "%s\n", __func__); chg_data->chg_desc = &mt6370_default_chg_desc; chg_desc = devm_kzalloc(&i2c->dev, sizeof(struct mt6370_pmu_charger_desc), GFP_KERNEL); if (!chg_desc) return -ENOMEM; memcpy(chg_desc, &mt6370_default_chg_desc, sizeof(struct mt6370_pmu_charger_desc)); if (of_property_read_string(np, "charger_name", &(chg_data->mchr_info.name)) < 0) { dev_err(chg_data->dev, "%s: no charger name\n", __func__); chg_data->mchr_info.name = "mt6370_charger"; } if (of_property_read_u32(np, "ichg", &chg_desc->ichg) < 0) dev_err(chg_data->dev, "%s: no ichg\n", __func__); if (of_property_read_u32(np, "aicr", &chg_desc->aicr) < 0) dev_err(chg_data->dev, "%s: no aicr\n", __func__); if (of_property_read_u32(np, "mivr", &chg_desc->mivr) < 0) dev_err(chg_data->dev, "%s: no mivr\n", __func__); if (of_property_read_u32(np, "cv", &chg_desc->cv) < 0) dev_err(chg_data->dev, "%s: no cv\n", __func__); if (of_property_read_u32(np, "ieoc", &chg_desc->ieoc) < 0) dev_err(chg_data->dev, "%s: no ieoc\n", __func__); if (of_property_read_u32(np, "safety_timer", &chg_desc->safety_timer) < 0) dev_err(chg_data->dev, "%s: no safety timer\n", __func__); if (of_property_read_u32(np, "dc_wdt", &chg_desc->dc_wdt) < 0) dev_err(chg_data->dev, "%s: no dc wdt\n", __func__); if (of_property_read_u32(np, "ircmp_resistor", &chg_desc->ircmp_resistor) < 0) dev_err(chg_data->dev, "%s: no ircmp resistor\n", __func__); if (of_property_read_u32(np, "ircmp_vclamp", &chg_desc->ircmp_vclamp) < 0) dev_err(chg_data->dev, "%s: no ircmp vclamp\n", __func__); chg_desc->en_te = of_property_read_bool(np, "enable_te"); chg_desc->en_wdt = of_property_read_bool(np, "enable_wdt"); chg_desc->en_otg_wdt = of_property_read_bool(np, "enable_otg_wdt"); chg_data->chg_desc = chg_desc; return 0; } static int mt6370_chg_init_setting(struct mt6370_pmu_charger_data *chg_data) { int ret = 0; struct mt6370_pmu_charger_desc *chg_desc = chg_data->chg_desc; dev_info(chg_data->dev, "%s\n", __func__); /* Select IINLMTSEL to use AICR */ ret = mt6370_select_input_current_limit(chg_data, MT6370_IINLMTSEL_AICR); if (ret < 0) dev_err(chg_data->dev, "%s: select iinlmtsel failed\n", __func__); mdelay(5); /* Disable hardware ILIM */ ret = mt6370_enable_ilim(chg_data, false); if (ret < 0) dev_err(chg_data->dev, "%s: disable ilim failed\n", __func__); ret = _mt6370_set_ichg(chg_data, chg_desc->ichg); if (ret < 0) dev_err(chg_data->dev, "%s: set ichg failed\n", __func__); ret = _mt6370_set_aicr(chg_data, chg_desc->aicr); if (ret < 0) dev_err(chg_data->dev, "%s: set aicr failed\n", __func__); ret = _mt6370_set_mivr(chg_data, chg_desc->mivr); if (ret < 0) dev_err(chg_data->dev, "%s: set mivr failed\n", __func__); ret = _mt6370_set_cv(chg_data, chg_desc->cv); if (ret < 0) dev_err(chg_data->dev, "%s: set voreg failed\n", __func__); ret = mt6370_set_ieoc(chg_data, chg_desc->ieoc); if (ret < 0) dev_err(chg_data->dev, "%s: set ieoc failed\n", __func__); ret = mt6370_enable_te(chg_data, chg_desc->en_te); if (ret < 0) dev_err(chg_data->dev, "%s: set te failed\n", __func__); ret = mt6370_set_fast_charge_timer(chg_data, chg_desc->safety_timer); if (ret < 0) dev_err(chg_data->dev, "%s: set fast timer failed\n", __func__); ret = mt6370_set_dc_wdt(chg_data, chg_desc->dc_wdt); if (ret < 0) dev_err(chg_data->dev, "%s: set dc watch dog timer failed\n", __func__); ret = _mt6370_enable_safety_timer(chg_data, true); if (ret < 0) dev_err(chg_data->dev, "%s: enable charger timer failed\n", __func__); /* Initially disable WDT to prevent 1mA power consumption */ ret = mt6370_enable_wdt(chg_data, false); if (ret < 0) dev_err(chg_data->dev, "%s: disable watchdog timer failed\n", __func__); /* Disable JEITA */ ret = mt6370_enable_jeita(chg_data, false); if (ret < 0) dev_err(chg_data->dev, "%s: disable jeita failed\n", __func__); /* Disable HZ */ ret = _mt6370_enable_hz(chg_data, false); if (ret < 0) dev_err(chg_data->dev, "%s: disable hz failed\n", __func__); ret = _mt6370_set_ircmp_resistor(chg_data, chg_desc->ircmp_resistor); if (ret < 0) dev_err(chg_data->dev, "%s: set IR compensation resistor failed\n", __func__); ret = _mt6370_set_ircmp_vclamp(chg_data, chg_desc->ircmp_vclamp); if (ret < 0) dev_err(chg_data->dev, "%s: set IR compensation vclamp failed\n", __func__); /* Disable USB charger type detection */ ret = mt6370_enable_chgdet_flow(chg_data, false); if (ret < 0) dev_err(chg_data->dev, "%s: disable usb chrdet failed\n", __func__); return ret; } static const mtk_charger_intf mt6370_mchr_intf[CHARGING_CMD_NUMBER] = { [CHARGING_CMD_INIT] = mt6370_hw_init, [CHARGING_CMD_DUMP_REGISTER] = mt6370_dump_register, [CHARGING_CMD_ENABLE] = mt6370_enable_charging, [CHARGING_CMD_SET_HIZ_SWCHR] = mt6370_enable_hz, [CHARGING_CMD_ENABLE_SAFETY_TIMER] = mt6370_enable_safety_timer, [CHARGING_CMD_ENABLE_OTG] = mt6370_enable_otg, [CHARGING_CMD_ENABLE_POWER_PATH] = mt6370_enable_power_path, [CHARGING_CMD_ENABLE_DIRECT_CHARGE] = mt6370_enable_direct_charge, [CHARGING_CMD_SET_CURRENT] = mt6370_set_ichg, [CHARGING_CMD_SET_INPUT_CURRENT] = mt6370_set_aicr, [CHARGING_CMD_SET_VINDPM] = mt6370_set_mivr, [CHARGING_CMD_SET_CV_VOLTAGE] = mt6370_set_cv, [CHARGING_CMD_SET_BOOST_CURRENT_LIMIT] = mt6370_set_otg_current_limit, [CHARGING_CMD_SET_TA_CURRENT_PATTERN] = mt6370_set_pep_current_pattern, [CHARGING_CMD_SET_TA20_RESET] = mt6370_set_pep20_reset, [CHARGING_CMD_SET_TA20_CURRENT_PATTERN] = mt6370_set_pep20_current_pattern, [CHARGING_CMD_SET_ERROR_STATE] = mt6370_set_error_state, [CHARGING_CMD_GET_CURRENT] = mt6370_get_ichg, [CHARGING_CMD_GET_INPUT_CURRENT] = mt6370_get_aicr, [CHARGING_CMD_GET_CHARGER_TEMPERATURE] = mt6370_get_tchg, [CHARGING_CMD_GET_CHARGING_STATUS] = mt6370_is_charging_done, [CHARGING_CMD_GET_IS_POWER_PATH_ENABLE] = mt6370_is_power_path_enable, [CHARGING_CMD_GET_IS_SAFETY_TIMER_ENABLE] = mt6370_is_safety_timer_enable, [CHARGING_CMD_RESET_WATCH_DOG_TIMER] = mt6370_kick_wdt, [CHARGING_CMD_GET_IBUS] = mt6370_get_ibus, [CHARGING_CMD_GET_VBUS] = mt6370_get_vbus, [CHARGING_CMD_RUN_AICL] = mt6370_run_aicl, [CHARGING_CMD_RESET_DC_WATCH_DOG_TIMER] = mt6370_kick_dc_wdt, [CHARGING_CMD_ENABLE_DC_VBUSOV] = mt6370_enable_dc_vbusov, [CHARGING_CMD_SET_DC_VBUSOV] = mt6370_set_dc_vbusov, [CHARGING_CMD_ENABLE_DC_VBUSOC] = mt6370_enable_dc_ibusoc, [CHARGING_CMD_SET_DC_VBUSOC] = mt6370_set_dc_ibusoc, [CHARGING_CMD_ENABLE_DC_VBATOV] = mt6370_enable_dc_vbatov, [CHARGING_CMD_SET_DC_VBATOV] = mt6370_set_dc_vbatov, [CHARGING_CMD_GET_IS_DC_ENABLE] = mt6370_is_dc_enable, [CHARGING_CMD_SET_IRCMP_RESISTOR] = mt6370_set_ircmp_resistor, [CHARGING_CMD_SET_IRCMP_VOLT_CLAMP] = mt6370_set_ircmp_vclamp, [CHARGING_CMD_SET_PEP20_EFFICIENCY_TABLE] = mt6370_set_pep20_efficiency_table, [CHARGING_CMD_GET_CHARGER_TYPE] = mt6370_get_charger_type, [CHARGING_CMD_ENABLE_CHR_TYPE_DET] = mt6370_enable_chg_type_det, /* * The following interfaces are not related to charger * Define in mtk_charger_intf.c */ [CHARGING_CMD_SW_INIT] = mtk_charger_sw_init, [CHARGING_CMD_SET_HV_THRESHOLD] = mtk_charger_set_hv_threshold, [CHARGING_CMD_GET_HV_STATUS] = mtk_charger_get_hv_status, [CHARGING_CMD_GET_BATTERY_STATUS] = mtk_charger_get_battery_status, [CHARGING_CMD_GET_CHARGER_DET_STATUS] = mtk_charger_get_charger_det_status, [CHARGING_CMD_GET_IS_PCM_TIMER_TRIGGER] = mtk_charger_get_is_pcm_timer_trigger, [CHARGING_CMD_SET_PLATFORM_RESET] = mtk_charger_set_platform_reset, [CHARGING_CMD_GET_PLATFORM_BOOT_MODE] = mtk_charger_get_platform_boot_mode, [CHARGING_CMD_SET_POWER_OFF] = mtk_charger_set_power_off, [CHARGING_CMD_GET_POWER_SOURCE] = mtk_charger_get_power_source, [CHARGING_CMD_GET_CSDAC_FALL_FLAG] = mtk_charger_get_csdac_full_flag, [CHARGING_CMD_DISO_INIT] = mtk_charger_diso_init, [CHARGING_CMD_GET_DISO_STATE] = mtk_charger_get_diso_state, [CHARGING_CMD_SET_VBUS_OVP_EN] = mtk_charger_set_vbus_ovp_en, [CHARGING_CMD_GET_BIF_VBAT] = mtk_charger_get_bif_vbat, [CHARGING_CMD_SET_CHRIND_CK_PDN] = mtk_charger_set_chrind_ck_pdn, [CHARGING_CMD_GET_BIF_TBAT] = mtk_charger_get_bif_tbat, [CHARGING_CMD_SET_DP] = mtk_charger_set_dp, [CHARGING_CMD_GET_BIF_IS_EXIST] = mtk_charger_get_bif_is_exist, }; static int mt6370_pmu_charger_probe(struct platform_device *pdev) { int ret = 0; struct mt6370_pmu_charger_data *chg_data; bool use_dt = pdev->dev.of_node; pr_info("%s: (%s)\n", __func__, MT6370_PMU_CHARGER_DRV_VERSION); chg_data = devm_kzalloc(&pdev->dev, sizeof(*chg_data), GFP_KERNEL); if (!chg_data) return -ENOMEM; mutex_init(&chg_data->adc_access_lock); mutex_init(&chg_data->irq_access_lock); mutex_init(&chg_data->aicr_access_lock); mutex_init(&chg_data->ichg_access_lock); mutex_init(&chg_data->bc12_access_lock); chg_data->chip = dev_get_drvdata(pdev->dev.parent); chg_data->dev = &pdev->dev; chg_data->chg_type = CHARGER_UNKNOWN; if (use_dt) { ret = mt_parse_dt(&pdev->dev, chg_data); if (ret < 0) dev_err(chg_data->dev, "%s: parse dts failed\n", __func__); } platform_set_drvdata(pdev, chg_data); /* Init wait queue head */ init_waitqueue_head(&chg_data->wait_queue); #ifndef CONFIG_TCPC_CLASS INIT_WORK(&chg_data->chgdet_work, mt6370_chgdet_work_handler); #endif /* Do initial setting */ ret = mt6370_chg_init_setting(chg_data); if (ret < 0) { dev_err(chg_data->dev, "%s: sw init failed\n", __func__); goto err_chg_init_setting; } /* SW workaround */ ret = mt6370_chg_sw_workaround(chg_data); if (ret < 0) { dev_err(chg_data->dev, "%s: software workaround failed\n", __func__); goto err_chg_sw_workaround; } mt6370_pmu_charger_irq_register(pdev); chg_data->mchr_info.mchr_intf = mt6370_mchr_intf; mtk_charger_set_info(&chg_data->mchr_info); mt6370_dump_register(&chg_data->mchr_info, NULL); /* Schedule work for microB's BC1.2 */ #ifndef CONFIG_TCPC_CLASS schedule_work(&chg_data->chgdet_work); #endif dev_info(&pdev->dev, "%s successfully\n", __func__); return ret; err_chg_sw_workaround: err_chg_init_setting: mutex_destroy(&chg_data->ichg_access_lock); mutex_destroy(&chg_data->adc_access_lock); mutex_destroy(&chg_data->irq_access_lock); mutex_destroy(&chg_data->aicr_access_lock); mutex_destroy(&chg_data->bc12_access_lock); return ret; } static int mt6370_pmu_charger_remove(struct platform_device *pdev) { struct mt6370_pmu_charger_data *chg_data = platform_get_drvdata(pdev); if (chg_data) { mutex_destroy(&chg_data->ichg_access_lock); mutex_destroy(&chg_data->adc_access_lock); mutex_destroy(&chg_data->irq_access_lock); mutex_destroy(&chg_data->aicr_access_lock); mutex_destroy(&chg_data->bc12_access_lock); dev_info(chg_data->dev, "%s successfully\n", __func__); } return 0; } static const struct of_device_id mt_ofid_table[] = { { .compatible = "mediatek,mt6370_pmu_charger", }, { }, }; MODULE_DEVICE_TABLE(of, mt_ofid_table); static const struct platform_device_id mt_id_table[] = { { "mt6370_pmu_charger", 0}, { }, }; MODULE_DEVICE_TABLE(platform, mt_id_table); static struct platform_driver mt6370_pmu_charger = { .driver = { .name = "mt6370_pmu_charger", .owner = THIS_MODULE, .of_match_table = of_match_ptr(mt_ofid_table), }, .probe = mt6370_pmu_charger_probe, .remove = mt6370_pmu_charger_remove, .id_table = mt_id_table, }; module_platform_driver(mt6370_pmu_charger); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("MediaTek MT6370 PMU Charger"); MODULE_VERSION(MT6370_PMU_CHARGER_DRV_VERSION); /* * Release Note * 1.1.12_MTK * (1) Enable charger before sending PE+/PE+20 pattern * (2) Select to use reg AICR as input limit -> disable HW limit * * 1.1.11_MTK * (1) Fix get_adc lock unbalance issue * (2) Add a flag to check enable status of bc12 * * 1.1.10_MTK * (1) Add ext usb switch control * (2) Add MT6370_APPLE_SAMSUNG_TA_SUPPORT config * (3) Remove some debug log and unncessary code * * 1.1.9_MTK * (1) Use polling mode for ADC done and PE+ * (2) Return immediately if usb is plugged out while waiting CDP * * 1.1.8_MTK * (1) Read CHG_STAT in dump register * (2) Read ZCV before disabling it and add ZCV ops * (3) Add AEE log for ADC hang issue * * 1.1.7_MTK * (1) Add a adc flag for adc_done IRQ * (2) Not waitting ssfinish IRQ after enabling OTG * (3) Add debug information for ADC * (4) For degug, read ADC data even if conversation failed * (5) Trigger any ADC before disabling ZCV * * 1.1.6_MTK * (1) Read USB STATUS(0x27) instead of device type(0x22) * to check charger type * (2) Show CV value in dump_register * (3) If PE20/30 is connected, do not run AICL * (4) Disable MIVR IRQ -> enable direct charge * Enable MIVR IRQ -> disable direct charge * * 1.1.5_MTK * (1) Modify probe sequence * (2) Change ADC log from ratelimited to normal one for debug * (3) Polling ADC_START after receiving ADC_DONE irq * (4) Disable ZCV in probe function * (5) Plug out -> enable ILIM_EN * Plug in -> sleep 1200ms -> disable ILIM_EN * * 1.1.4_MTK * (1) Add IEOC workaround * * 1.1.3_MTK * (1) Enable safety timer, rechg, vbusov, eoc IRQs * and notify charger manager if event happens * (2) Modify IBAT/IBUS ADC's coefficient * (3) Change dev_dbg to dev_dbg_ratelimited * * 1.1.2_MTK * (1) Enable charger in plug in callback * (2) Enable power path -> Enable MIVR IRQ, * Disable MIVR IRQ -> Disable power path * (3) Change "PrimarySWCHG" to "primary_chg" * Change "load_switch" to "primary_load_switch" * (4) Check ADC_START bit if ADC_DONE IRQ timeout * * 1.1.1_MTK * (1) Shorten enable to en * (2) Enable WDT for charging/OTG mode, disable WDT when no cable plugged in * (3) Use dev_ series instead pr_ series to dump log * (4) Do AICL in a workqueue after receiving MIVR IRQ * * 1.1.0_MTK * (1) Add a load switch device to support PE30 * * 1.0.9_MTK * (1) Report charger online to charger type detection * (2) Use MIVR to enable/disable power path * (3) Release PE+20 efficiency table interface * (4) For ovpctrl_uvp, read uvp status to decide it is plug-in or plug-out * * 1.0.8_MTK * (1) Release mt6370_enable_discharge interface * discharge/OTG is controlled by PD * * 1.0.7_MTK * (1) Adapt to GM30 * * 1.0.6_MTK * (1) Use wait queue instead of mdelay for waiting interrupt event * * 1.0.5_MTK * (1) Modify USB charger type detecion flow * Follow notifier from TypeC if CONFIG_TCPC_CLASS is defined * (2) Add WDT timeout interrupt and kick watchdog in irq handler * * 1.0.4_MTK * (1) Add USB charger type detection * * 1.0.3_MTK * (1) Remove reset chip operation in probe function * * 1.0.2_MTK * (1) For normal boot, set IPREC to 850mA to prevent Isys drop * (2) Modify naming rules of functions * * 1.0.0_MTK * (1) Initial Release */