// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2021 MediaTek Inc. * * Author: ChiYuan Huang */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FGADC_R_CON0 0x2E5 #define SYSTEM_INFO_CON2_H 0x2FE #define HK_TOP_RST_CON0 0x30F #define HK_TOP_INT_CON0_SET 0x311 #define HK_TOP_INT_CON0_CLR 0x312 #define HK_TOP_INT_CON1_SET 0x314 #define HK_TOP_INT_CON1_CLR 0x315 #define HK_TOP_INT_MASK_CON0 0x316 #define HK_TOP_INT_MASK_CON0_SET 0x317 #define HK_TOP_INT_MASK_CON0_CLR 0x318 #define HK_TOP_INT_MASK_CON1 0x319 #define HK_TOP_INT_STATUS0 0x31C #define HK_TOP_INT_RAW_STATUS1 0x31F #define HK_TOP_WKEY 0x328 #define AUXADC_OUT_CH3 0x408 #define AUXADC_OUT_CH11 0x40A #define AUXADC_OUT_CH0 0x410 #define AUXADC_OUT_IMP_AVG 0x41C #define AUXADC_RQST0 0x438 #define AUXADC_IMP0 0x4A8 #define AUXADC_IMP1 0x4A9 #define RG_AUXADC_LBAT0 0x4AD #define RG_AUXADC_LBAT2_0 0x4B9 #define RG_AUXADC_NAG_0 0x4D2 #define VBAT0_FLAG BIT(0) #define RG_RESET_MASK BIT(1) #define VREF_ENMASK BIT(4) #define BATON_ENMASK BIT(3) #define BATSNS_ENMASK BIT(0) #define ADC_OUT_RDY BIT(7) #define AUXADC_IMP_ENMASK BIT(0) #define AUXADC_IMP_PRDSEL_MASK GENMASK(1, 0) #define AUXADC_IMP_CNTSEL_MASK GENMASK(3, 2) #define AUXADC_IMP_CNTSEL_SHFT 2 #define INT_RAW_AUXADC_IMP BIT(0) #define NUM_IRQ_REG 2 #define AUXADC_LBAT_EN_MASK BIT(0) #define AUXADC_LBAT2_EN_MASK BIT(0) #define AUXADC_NAG_IRQ_EN_MASK BIT(5) #define AUXADC_NAG_EN_MASK BIT(0) struct mt6375_priv { struct device *dev; struct regmap *regmap; struct irq_domain *domain; struct irq_chip irq_chip; struct mutex adc_lock; struct mutex irq_lock; struct regulator *isink_load; int imix_r; int irq; u8 unmask_buf[NUM_IRQ_REG]; int pre_uisoc; struct alarm vbat0_alarm; struct work_struct vbat0_work; atomic_t vbat0_flag; struct wakeup_source *vbat0_ws; struct lock_class_key info_exist_key; }; #define VBAT0_POLL_TIME_SEC 5 #define ALARM_COUNT_MAX 12 static const int vbat_event[] = { RG_INT_STATUS_BAT_H, RG_INT_STATUS_BAT_L, RG_INT_STATUS_BAT2_H, RG_INT_STATUS_BAT2_L, RG_INT_STATUS_NAG_C_DLTV }; static const struct { u16 addr; u8 mask; } vbat_event_regs[] = { { RG_AUXADC_LBAT0, AUXADC_LBAT_EN_MASK }, { RG_AUXADC_LBAT0, AUXADC_LBAT_EN_MASK }, { RG_AUXADC_LBAT2_0, AUXADC_LBAT2_EN_MASK }, { RG_AUXADC_LBAT2_0, AUXADC_LBAT2_EN_MASK }, { RG_AUXADC_NAG_0, AUXADC_NAG_IRQ_EN_MASK | AUXADC_NAG_EN_MASK }, }; #define AUXADC_CHAN(_idx, _resolution, _type, _info) { \ .type = _type, \ .channel = MT6375_AUXADC_##_idx, \ .scan_index = MT6375_AUXADC_##_idx, \ .datasheet_name = #_idx, \ .scan_type = { \ .sign = 'u', \ .realbits = _resolution, \ .storagebits = 16, \ .endianness = IIO_CPU, \ }, \ .indexed = 1, \ .info_mask_separate = _info \ } #define AUXADC_CHAN_PROCESSED(_idx, _resolution, _type) \ AUXADC_CHAN(_idx, _resolution, _type, BIT(IIO_CHAN_INFO_PROCESSED)) #define AUXADC_CHAN_RAW_SCALE(_idx, _resolution, _type) \ AUXADC_CHAN(_idx, _resolution, _type, \ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE)) static const struct iio_chan_spec auxadc_channels[] = { AUXADC_CHAN_RAW_SCALE(BATSNS, 15, IIO_VOLTAGE), AUXADC_CHAN_RAW_SCALE(BATON, 12, IIO_VOLTAGE), AUXADC_CHAN_PROCESSED(IMP, 15, IIO_VOLTAGE), AUXADC_CHAN_RAW_SCALE(IMIX_R, 16, IIO_RESISTANCE), AUXADC_CHAN_RAW_SCALE(VREF, 12, IIO_VOLTAGE), AUXADC_CHAN_RAW_SCALE(BATSNS_DBG, 15, IIO_VOLTAGE) }; static int auxadc_get_chg_vbat(struct mt6375_priv *priv, int *chg_vbat) { static struct iio_channel *chg_vbat_chan; int ret = 0, vbat; if (IS_ERR_OR_NULL(chg_vbat_chan)) chg_vbat_chan = devm_iio_channel_get(priv->dev, "chg_vbat"); if (IS_ERR(chg_vbat_chan)) return PTR_ERR(chg_vbat_chan); ret = iio_read_channel_processed(chg_vbat_chan, &vbat); if (ret < 0) return ret; *chg_vbat = vbat / 1000; return ret; } static int auxadc_read_channel(struct mt6375_priv *priv, int chan, int dbits, int *val) { unsigned int enable, out_reg, rdy_val; u16 raw_val; int ret, chg_vbat = 0; if (chan == MT6375_AUXADC_VREF) { enable = VREF_ENMASK; out_reg = AUXADC_OUT_CH11; } else if (chan == MT6375_AUXADC_BATON) { enable = BATON_ENMASK; out_reg = AUXADC_OUT_CH3; } else if (chan == MT6375_AUXADC_BATSNS) { if (atomic_read(&priv->vbat0_flag)) { ret = auxadc_get_chg_vbat(priv, &chg_vbat); dev_info(priv->dev, "%s: use chg_vbat:%d(%d)\n", __func__, chg_vbat, ret); if (ret >= 0) *val = chg_vbat; return ret ? ret : IIO_VAL_INT; } enable = BATSNS_ENMASK; out_reg = AUXADC_OUT_CH0; } else { enable = BATSNS_ENMASK; out_reg = AUXADC_OUT_CH0; } ret = regmap_write(priv->regmap, AUXADC_RQST0, enable); if (ret) return ret; usleep_range(1000, 1200); ret = regmap_read_poll_timeout(priv->regmap, out_reg + 1, rdy_val, rdy_val & ADC_OUT_RDY, 500, 11*1000); if (ret == -ETIMEDOUT) dev_err(priv->dev, "(%d) channel timeout\n", chan); ret = regmap_raw_read(priv->regmap, out_reg, &raw_val, sizeof(raw_val)); if (ret) return ret; *val = raw_val & (BIT(dbits) - 1); return IIO_VAL_INT; } static int gauge_get_imp_ibat(struct mt6375_priv *priv) { struct power_supply *psy; union power_supply_propval prop; int ret; psy = power_supply_get_by_name("mtk-gauge"); if (!psy) return 0; ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CURRENT_NOW, &prop); if (ret) return ret; power_supply_put(psy); return prop.intval; } static int auxadc_read_imp(struct mt6375_priv *priv, int *vbat, int *ibat) { unsigned int wait_time_in_ms, regval; const unsigned int prd_sel[] = { 6, 8, 10, 12 }; const unsigned int cnt_sel[] = { 1, 2, 4, 8 }; u16 raw_val; int ret; int dbits = auxadc_channels[MT6375_AUXADC_IMP].scan_type.realbits; if (atomic_read(&priv->vbat0_flag)) { dev_info(priv->dev, "%s: vbat cell abnormal, return -EIO\n", __func__); return -EIO; } ret = regmap_write(priv->regmap, AUXADC_IMP0, 0); if (ret) return ret; ret = regmap_write(priv->regmap, HK_TOP_INT_CON1_CLR, INT_RAW_AUXADC_IMP); if (ret) return ret; ret = regmap_write(priv->regmap, HK_TOP_INT_CON1_SET, INT_RAW_AUXADC_IMP); if (ret) return ret; ret = regmap_read(priv->regmap, AUXADC_IMP1, ®val); if (ret) return ret; wait_time_in_ms = prd_sel[regval & AUXADC_IMP_PRDSEL_MASK]; wait_time_in_ms *= cnt_sel[(regval & AUXADC_IMP_CNTSEL_MASK) >> AUXADC_IMP_CNTSEL_SHFT]; ret = regmap_write(priv->regmap, AUXADC_IMP0, AUXADC_IMP_ENMASK); if (ret) return ret; msleep(wait_time_in_ms); ret = regmap_read_poll_timeout(priv->regmap, HK_TOP_INT_RAW_STATUS1, regval, (regval & INT_RAW_AUXADC_IMP), 100, 1000); if (ret == -ETIMEDOUT) dev_err(priv->dev, "IMP channel timeout\n"); ret = regmap_raw_read(priv->regmap, AUXADC_OUT_IMP_AVG, &raw_val, sizeof(raw_val)); if (ret) return ret; raw_val &= (BIT(dbits) - 1); *vbat = div_s64((s64)raw_val * 7360, BIT(dbits)); ret = regmap_write(priv->regmap, AUXADC_IMP0, 0); if (ret) return ret; *ibat = gauge_get_imp_ibat(priv); return IIO_VAL_INT; } static int auxadc_read_scale(struct mt6375_priv *priv, int chan, int dbits, int *val1, int *val2) { switch (chan) { case MT6375_AUXADC_BATSNS: if (atomic_read(&priv->vbat0_flag)) { *val1 = 1; *val2 = 1; } else { *val1 = 7360; *val2 = BIT(dbits); } return IIO_VAL_FRACTIONAL; case MT6375_AUXADC_BATSNS_DBG: *val1 = 7360; *val2 = BIT(dbits); return IIO_VAL_FRACTIONAL; case MT6375_AUXADC_BATON: case MT6375_AUXADC_VREF: *val1 = 2760; *val2 = BIT(dbits); return IIO_VAL_FRACTIONAL; } return -EINVAL; } static int auxadc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val1, int *val2, long mask) { struct mt6375_priv *priv = iio_priv(indio_dev); int dbits = chan->scan_type.realbits; int ch_idx = chan->channel; int ret; switch (mask) { case IIO_CHAN_INFO_PROCESSED: switch (ch_idx) { case MT6375_AUXADC_IMP: mutex_lock(&priv->adc_lock); pm_stay_awake(priv->dev); ret = auxadc_read_imp(priv, val1, val2); pm_relax(priv->dev); mutex_unlock(&priv->adc_lock); return ret; } break; case IIO_CHAN_INFO_RAW: switch (ch_idx) { case MT6375_AUXADC_BATSNS: case MT6375_AUXADC_BATON: case MT6375_AUXADC_VREF: case MT6375_AUXADC_BATSNS_DBG: mutex_lock(&priv->adc_lock); pm_stay_awake(priv->dev); ret = auxadc_read_channel(priv, ch_idx, dbits, val1); pm_relax(priv->dev); mutex_unlock(&priv->adc_lock); return ret; case MT6375_AUXADC_IMIX_R: *val1 = priv->imix_r; return IIO_VAL_INT; } break; case IIO_CHAN_INFO_SCALE: return auxadc_read_scale(priv, ch_idx, dbits, val1, val2); } return -EINVAL; } static const struct iio_info auxadc_iio_info = { .read_raw = auxadc_read_raw, }; static void auxadc_irq_lock(struct irq_data *data) { struct mt6375_priv *priv = irq_data_get_irq_chip_data(data); mutex_lock(&priv->irq_lock); } static void auxadc_irq_sync_unlock(struct irq_data *data) { struct mt6375_priv *priv = irq_data_get_irq_chip_data(data); int idx = data->hwirq / 8, bits = BIT(data->hwirq % 8), ret; unsigned int reg; if (priv->unmask_buf[idx] & bits) reg = HK_TOP_INT_CON0_SET + idx * 3; else reg = HK_TOP_INT_CON0_CLR + idx * 3; ret = regmap_write(priv->regmap, reg, bits); if (ret) dev_err(priv->dev, "Failed to set/clr irq con %d\n", data->hwirq); mutex_unlock(&priv->irq_lock); } static void auxadc_irq_disable(struct irq_data *data) { struct mt6375_priv *priv = irq_data_get_irq_chip_data(data); priv->unmask_buf[data->hwirq / 8] &= ~BIT(data->hwirq % 8); } static void auxadc_irq_enable(struct irq_data *data) { struct mt6375_priv *priv = irq_data_get_irq_chip_data(data); priv->unmask_buf[data->hwirq / 8] |= BIT(data->hwirq % 8); } static int auxadc_irq_map(struct irq_domain *h, unsigned int virq, irq_hw_number_t hw) { struct mt6375_priv *priv = h->host_data; irq_set_chip_data(virq, priv); irq_set_chip(virq, &priv->irq_chip); irq_set_nested_thread(virq, 1); irq_set_parent(virq, priv->irq); irq_set_noprobe(virq); return 0; } static const struct irq_domain_ops auxadc_domain_ops = { .map = auxadc_irq_map, .xlate = irq_domain_xlate_onetwocell, }; static int auxadc_vbat_is_valid(struct mt6375_priv *priv, bool *valid) { static struct iio_channel *auxadc_vbat_chan; int ret = 0, chg_vbat = 0, auxadc_vbat = 0; if (IS_ERR_OR_NULL(auxadc_vbat_chan)) auxadc_vbat_chan = devm_iio_channel_get(priv->dev, "auxadc_vbat"); if (IS_ERR(auxadc_vbat_chan)) return PTR_ERR(auxadc_vbat_chan); ret = auxadc_get_chg_vbat(priv, &chg_vbat); dev_info(priv->dev, "%s: chg_vbat = %d(%d)\n", __func__, chg_vbat, ret); ret |= iio_read_channel_processed(auxadc_vbat_chan, &auxadc_vbat); dev_info(priv->dev, "%s: auxadc_vbat = %d(%d)\n", __func__, auxadc_vbat, ret); if (!ret && abs(chg_vbat - auxadc_vbat) > 1000) { dev_info(priv->dev, "%s: unexpected vbat cell!!\n", __func__); *valid = false; } else *valid = true; return ret; } static int auxadc_handle_vbat0(struct mt6375_priv *priv, bool is_vbat0) { struct power_supply *chg_psy; union power_supply_propval val; int i, ret; /* set/clr vbat0 bits */ ret = regmap_update_bits(priv->regmap, SYSTEM_INFO_CON2_H, VBAT0_FLAG, is_vbat0 ? 0xFF : 0); if (ret < 0) { dev_notice(priv->dev, "%s: failed to clear vbat0 flag\n", __func__); return ret; } /* notify gauge & charger */ chg_psy = devm_power_supply_get_by_phandle(priv->dev, "charger"); if (IS_ERR_OR_NULL(chg_psy)) return PTR_ERR(chg_psy); val.intval = is_vbat0 ? true : false; ret = power_supply_set_property(chg_psy, POWER_SUPPLY_PROP_ENERGY_EMPTY, &val); power_supply_changed(chg_psy); /* mask/unmask irq & disable function */ for (i = 0; i < ARRAY_SIZE(vbat_event); i++) { if (is_vbat0) { ret = regmap_update_bits(priv->regmap, vbat_event_regs[i].addr, vbat_event_regs[i].mask, 0); disable_irq_nosync(irq_find_mapping(priv->domain, vbat_event[i])); } else { ret = regmap_update_bits(priv->regmap, vbat_event_regs[i].addr, vbat_event_regs[i].mask, 0xFF); enable_irq(irq_find_mapping(priv->domain, vbat_event[i])); } } atomic_set(&priv->vbat0_flag, is_vbat0); return ret; } static void auxadc_vbat0_poll_work(struct work_struct *work) { struct mt6375_priv *priv = container_of(work, struct mt6375_priv, vbat0_work); bool valid; ktime_t add; int ret; __pm_stay_awake(priv->vbat0_ws); ret = auxadc_vbat_is_valid(priv, &valid); if (ret < 0 || !valid) { dev_info(priv->dev, "%s: restart timer\n", __func__); add = ktime_set(VBAT0_POLL_TIME_SEC, 0); #ifdef CONFIG_MTK_GAUGE_VBAT0_DEBUG alarm_forward_now(&priv->vbat0_alarm, add); alarm_restart(&priv->vbat0_alarm); #endif __pm_relax(priv->vbat0_ws); return; } dev_info(priv->dev, "%s: vbat recover\n", __func__); if (auxadc_handle_vbat0(priv, false)) dev_notice(priv->dev, "%s: failed to handle vbat0\n", __func__); __pm_relax(priv->vbat0_ws); } static enum alarmtimer_restart vbat0_alarm_poll_func( struct alarm *alarm, ktime_t now) { struct mt6375_priv *priv = container_of(alarm, struct mt6375_priv, vbat0_alarm); schedule_work(&priv->vbat0_work); return ALARMTIMER_NORESTART; } static int auxadc_check_vbat_event(struct mt6375_priv *priv, u8 *status_buf) { int i, ret = 0, idx_i, idx_j; bool valid; ktime_t now, add; if (atomic_read(&priv->vbat0_flag)) return ret; for (i = 0; i < ARRAY_SIZE(vbat_event); i++) { idx_i = vbat_event[i] / 8; idx_j = vbat_event[i] % 8; if (status_buf[idx_i] & BIT(idx_j)) break; } if (i == ARRAY_SIZE(vbat_event)) { dev_info(priv->dev, "%s: without related event\n", __func__); return ret; } ret = auxadc_vbat_is_valid(priv, &valid); if (ret < 0 || valid) return ret; ret = auxadc_handle_vbat0(priv, true); if (ret < 0) { dev_notice(priv->dev, "%s: failed to handle vbat0\n", __func__); return ret; } /* start alarm */ now = ktime_get_boottime(); add = ktime_set(VBAT0_POLL_TIME_SEC, 0); alarm_start(&priv->vbat0_alarm, ktime_add(now, add)); return ret; } static irqreturn_t auxadc_irq_thread(int irq, void *data) { struct mt6375_priv *priv = data; static const u8 no_status[NUM_IRQ_REG]; static const u8 mask[NUM_IRQ_REG] = { 0x3F, 0x02 }; u8 status_buf[NUM_IRQ_REG], status; bool handled = false; int i, j, ret; ret = regmap_raw_read(priv->regmap, HK_TOP_INT_STATUS0, status_buf, sizeof(status_buf)); if (ret) { dev_err(priv->dev, "Error reading INT status\n"); return IRQ_HANDLED; } if (!memcmp(status_buf, no_status, NUM_IRQ_REG)) return IRQ_HANDLED; ret = auxadc_check_vbat_event(priv, status_buf); if (ret < 0) dev_info(priv->dev, "check vbat event failed\n"); /* mask all irqs */ for (i = 0; i < NUM_IRQ_REG; i++) { ret = regmap_write(priv->regmap, HK_TOP_INT_MASK_CON0_SET + i * 3, mask[i]); if (ret) dev_err(priv->dev, "Failed to mask irq[%d]\n", i); } for (i = 0; i < NUM_IRQ_REG; i++) { status = status_buf[i] & priv->unmask_buf[i]; if (!status) continue; for (j = 0; j < 8; j++) { if (!(status & BIT(j))) continue; handle_nested_irq(irq_find_mapping(priv->domain, i * 8 + j)); handled = true; } } /* after process, unmask all irqs */ for (i = 0; i < NUM_IRQ_REG; i++) { ret = regmap_write(priv->regmap, HK_TOP_INT_MASK_CON0_CLR + i * 3, mask[i]); if (ret) dev_err(priv->dev, "Failed to unmask irq[%d]\n", i); } ret = regmap_raw_write(priv->regmap, HK_TOP_INT_STATUS0, status_buf, sizeof(status_buf)); if (ret) dev_err(priv->dev, "Error clear INT status\n"); return handled ? IRQ_HANDLED : IRQ_NONE; } static int auxadc_add_irq_chip(struct mt6375_priv *priv) { int i, ret; for (i = 0; i < NUM_IRQ_REG; i++) { ret = regmap_write(priv->regmap, HK_TOP_INT_CON0_CLR + i * 3, 0xFF); if (ret) { dev_err(priv->dev, "Failed to disable irq con [%d]\n", i); return ret; } ret = regmap_write(priv->regmap, HK_TOP_INT_MASK_CON0 + i * 3, 0); if (ret) { dev_err(priv->dev, "Failed to init irq mask [%d]\n", i); return ret; } } /* Default mask AUXADC_IMP */ ret = regmap_update_bits(priv->regmap, HK_TOP_INT_MASK_CON1, INT_RAW_AUXADC_IMP, INT_RAW_AUXADC_IMP); if (ret) { dev_err(priv->dev, "Failed to defaut unmask AUXADC_IMP\n"); return ret; } priv->irq_chip.name = dev_name(priv->dev); priv->irq_chip.irq_bus_lock = auxadc_irq_lock; priv->irq_chip.irq_bus_sync_unlock = auxadc_irq_sync_unlock; priv->irq_chip.irq_disable = auxadc_irq_disable; priv->irq_chip.irq_enable = auxadc_irq_enable; priv->domain = irq_domain_add_linear(priv->dev->of_node, NUM_IRQ_REG * 8, &auxadc_domain_ops, priv); if (!priv->domain) { dev_err(priv->dev, "Failed to create IRQ domain\n"); return -ENOMEM; } ret = request_threaded_irq(priv->irq, NULL, auxadc_irq_thread, IRQF_SHARED | IRQF_ONESHOT, dev_name(priv->dev), priv); if (ret) { dev_err(priv->dev, "Failed to request IRQ %d for %s: %d\n", priv->irq, dev_name(priv->dev), ret); goto err_irq; } enable_irq_wake(priv->irq); return 0; err_irq: irq_domain_remove(priv->domain); return ret; } static void auxadc_del_irq_chip(void *data) { struct mt6375_priv *priv = data; unsigned int virq; int hwirq; free_irq(priv->irq, priv); for (hwirq = 0; hwirq < NUM_IRQ_REG * 8; hwirq++) { virq = irq_find_mapping(priv->domain, hwirq); if (virq) irq_dispose_mapping(virq); } irq_domain_remove(priv->domain); } static int auxadc_reset(struct mt6375_priv *priv) { u8 reset_key[2] = { 0x63, 0x63 }; int ret; ret = regmap_raw_write(priv->regmap, HK_TOP_WKEY, reset_key, sizeof(reset_key)); if (ret) return ret; ret = regmap_write(priv->regmap, HK_TOP_RST_CON0, RG_RESET_MASK); if (ret) return ret; ret = regmap_write(priv->regmap, HK_TOP_RST_CON0, 0); if (ret) return ret; reset_key[0] = reset_key[1] = 0; return regmap_raw_write(priv->regmap, HK_TOP_WKEY, reset_key, sizeof(reset_key)); } static int auxadc_get_uisoc(void) { struct power_supply *psy; union power_supply_propval prop; int ret; psy = power_supply_get_by_name("battery"); if (!psy) return -ENODEV; ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_CAPACITY, &prop); if (ret || prop.intval < 0) return -EINVAL; power_supply_put(psy); return prop.intval; } static int auxadc_get_rac(struct mt6375_priv *priv) { int vbat_1 = 0, vbat_2 = 0; int ibat_1 = 0, ibat_2 = 0; int rac = 0, ret = 0; int retry_count = 0; /* to make sure dummy load has been disabled */ if (regulator_is_enabled(priv->isink_load)) regulator_disable(priv->isink_load); do { mutex_lock(&priv->adc_lock); ret = auxadc_read_imp(priv, &vbat_1, &ibat_1); mutex_unlock(&priv->adc_lock); if (ret < 0) return ret; /* enable_dummy_load */ ret = regulator_enable(priv->isink_load); if (ret) return ret; mdelay(50); mutex_lock(&priv->adc_lock); ret = auxadc_read_imp(priv, &vbat_2, &ibat_2); mutex_unlock(&priv->adc_lock); if (ret < 0) return ret; /* disable_dummy_load */ ret = regulator_disable(priv->isink_load); if (ret) return ret; mdelay(50); /* translate to 0.1mV */ vbat_1 *= 10; vbat_2 *= 10; /* Cal.Rac: 70mA <= c_diff <= 120mA, 4mV <= v_diff <= 200mV */ if ((ibat_2 - ibat_1) >= 700 && (ibat_2 - ibat_1) <= 1200 && (vbat_1 - vbat_2) >= 40 && (vbat_1 - vbat_2) <= 2000) { /*m-ohm */ rac = ((vbat_1 - vbat_2) * 1000) / (ibat_2 - ibat_1); if (rac < 0) ret = (rac - (rac * 2)) * 1; else ret = rac * 1; if (ret < 50) ret = -1; } else ret = -1; dev_info(priv->dev, "v1=%d,v2=%d,c1=%d,c2=%d,v_diff=%d,c_diff=%d,rac=%d,ret=%d,retry=%d\n", vbat_1, vbat_2, ibat_1, ibat_2, (vbat_1 - vbat_2), (ibat_2 - ibat_1), rac, ret, retry_count); if (++retry_count >= 3) break; } while (ret == -1); return ret; } #define IMIX_R_MIN_MOHM 100 #define IMIX_R_CALI_CNT 2 static int auxadc_cali_imix_r(struct mt6375_priv *priv) { struct power_supply *psy; int cur_uisoc = auxadc_get_uisoc(); int i, imix_r_avg = 0, rac_val[IMIX_R_CALI_CNT]; psy = power_supply_get_by_name("mtk-gauge"); if (!psy) { dev_info(priv->dev, "%s gauge disabled, skip\n", __func__); return -ENODEV; } power_supply_put(psy); if (cur_uisoc < 0 || cur_uisoc == priv->pre_uisoc) { dev_info(priv->dev, "%s, pre_SOC=%d SOC= %d, skip\n", __func__, priv->pre_uisoc, cur_uisoc); return 0; } priv->pre_uisoc = cur_uisoc; for (i = 0; i < IMIX_R_CALI_CNT; i++) { rac_val[i] = auxadc_get_rac(priv); if (rac_val[i] < 0) return -EIO; imix_r_avg += rac_val[i]; } imix_r_avg /= IMIX_R_CALI_CNT; if (imix_r_avg > IMIX_R_MIN_MOHM) priv->imix_r = imix_r_avg; dev_info(priv->dev, "[%s] %d, %d, ravg:%d\n", __func__, rac_val[0], rac_val[1], imix_r_avg); return 0; } static int mt6375_auxadc_parse_dt(struct mt6375_priv *priv) { int ret = 0; struct device_node *np; u32 val = 0; np = of_find_compatible_node(NULL, NULL, "mediatek,pmic-auxadc"); if (!np) return -ENODEV; np = of_get_child_by_name(np, "imix_r"); if (!np) { dev_notice(priv->dev, "no imix_r(%d)\n", ret); return -ENODEV; } ret = of_property_read_u32(np, "val", &val); if (ret) { dev_notice(priv->dev, "no imix_r(%d)\n", ret); return ret; } priv->imix_r = val; dev_info(priv->dev, "%s: imix_r = %d\n", __func__, priv->imix_r); return ret; } static void mt6375_unregister_lockdep_key(void *data) { struct lock_class_key *key = data; lockdep_unregister_key(key); } static int mt6375_auxadc_probe(struct platform_device *pdev) { struct mt6375_priv *priv; struct iio_dev *indio_dev; struct iio_dev_opaque *iio_dev_opaque; int ret; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv)); if (!indio_dev) return -ENOMEM; priv = iio_priv(indio_dev); priv->dev = &pdev->dev; mutex_init(&priv->adc_lock); mutex_init(&priv->irq_lock); priv->pre_uisoc = 101; atomic_set(&priv->vbat0_flag, 0); device_init_wakeup(&pdev->dev, true); platform_set_drvdata(pdev, priv); priv->vbat0_ws = wakeup_source_register(&pdev->dev, "vbat0_ws"); lockdep_register_key(&priv->info_exist_key); iio_dev_opaque = to_iio_dev_opaque(indio_dev); lockdep_set_class(&iio_dev_opaque->info_exist_lock, &priv->info_exist_key); ret = devm_add_action_or_reset(&pdev->dev, mt6375_unregister_lockdep_key, &priv->info_exist_key); if (ret) return ret; ret = mt6375_auxadc_parse_dt(priv); if (ret) { dev_notice(&pdev->dev, "Failed to parse dt\n"); return ret; } priv->regmap = dev_get_regmap(pdev->dev.parent, NULL); if (!priv->regmap) { dev_err(&pdev->dev, "Failed to get parent regmap\n"); return -ENODEV; } ret = auxadc_reset(priv); if (ret) { dev_err(&pdev->dev, "Failed to reset\n"); return ret; } priv->irq = platform_get_irq(pdev, 0); if (priv->irq < 0) { dev_err(&pdev->dev, "Failed to get gm30 irq\n"); return priv->irq; } ret = auxadc_add_irq_chip(priv); if (ret) { dev_err(&pdev->dev, "Failed to add irq chip\n"); return ret; } ret = devm_add_action_or_reset(&pdev->dev, auxadc_del_irq_chip, priv); if (ret) return ret; INIT_WORK(&priv->vbat0_work, auxadc_vbat0_poll_work); alarm_init(&priv->vbat0_alarm, ALARM_BOOTTIME, vbat0_alarm_poll_func); priv->isink_load = devm_regulator_get_exclusive(&pdev->dev, "isink_load"); if (IS_ERR(priv->isink_load)) { dev_err(&pdev->dev, "Failed to get isink_load regulator [%d]\n", PTR_ERR(priv->isink_load)); return PTR_ERR(priv->isink_load); } indio_dev->name = dev_name(&pdev->dev); indio_dev->dev.parent = &pdev->dev; indio_dev->info = &auxadc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = auxadc_channels; indio_dev->num_channels = ARRAY_SIZE(auxadc_channels); return devm_iio_device_register(&pdev->dev, indio_dev); } static int mt6375_auxadc_suspend_late(struct device *dev) { struct mt6375_priv *priv = dev_get_drvdata(dev); int ret; ret = auxadc_cali_imix_r(priv); if (ret) dev_err(dev, "calibrate imix_r ret=[%d]\n", ret); return 0; } static const struct dev_pm_ops mt6375_auxadc_dev_pm_ops = { SET_LATE_SYSTEM_SLEEP_PM_OPS(mt6375_auxadc_suspend_late, NULL) }; static const struct of_device_id __maybe_unused mt6375_auxadc_of_match[] = { { .compatible = "mediatek,mt6375-auxadc", }, { } }; MODULE_DEVICE_TABLE(of, mt6375_auxadc_of_match); static struct platform_driver mt6375_auxadc_driver = { .probe = mt6375_auxadc_probe, .driver = { .name = "mt6375-auxadc", .of_match_table = mt6375_auxadc_of_match, .pm = &mt6375_auxadc_dev_pm_ops, }, }; module_platform_driver(mt6375_auxadc_driver); MODULE_AUTHOR("ChiYuan Huang "); MODULE_DESCRIPTION("MT6375 AUXADC Driver"); MODULE_LICENSE("GPL v2");