// SPDX-License-Identifier: GPL-2.0-only // // Copyright (C) 2025 Richtek Technology Corp. // // Authors: ChiYuan Huang #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define RT9756_REG_INTFLAG1 0x0B #define RT9756_REG_INTFLAG2 0x0D #define RT9756_REG_INTFLAG3 0x0F #define RT9756_REG_ADCCTL 0x11 #define RT9756_REG_VBUSADC 0x12 #define RT9756_REG_BC12FLAG 0x45 #define RT9756_REG_INTFLAG4 0x49 /* Flag1 */ #define RT9756_EVT_BUSOVP BIT(3) #define RT9756_EVT_BUSOCP BIT(2) #define RT9756_EVT_BUSUCP BIT(0) /* Flag2 */ #define RT9756_EVT_BATOVP BIT(7) #define RT9756_EVT_BATOCP BIT(6) #define RT9756_EVT_TDIEOTP BIT(3) #define RT9756_EVT_VBUSLOW_ERR BIT(2) #define RT9756_EVT_VAC_INSERT BIT(0) /* Flag3 */ #define RT9756_EVT_WDT BIT(5) #define RT9756_EVT_VAC_UVLO BIT(4) /* ADCCTL */ #define RT9756_ADCEN_MASK BIT(7) #define RT9756_ADCONCE_MASK BIT(6) /* Bc12_flag */ #define RT9756_EVT_BC12_DONE BIT(3) /* Flag4 */ #define RT9756_EVT_OUTOVP BIT(0) #define RICHTEK_DEVID 7 #define RT9756_REVID 0 #define RT9756A_REVID 1 #define RT9757_REVID 2 #define RT9757A_REVID 3 #define RT9756_ADC_CONVTIME 1200 #define RT9756_ADC_MAXWAIT 16000 enum rt9756_model { MODEL_RT9756 = 0, MODEL_RT9757, MODEL_RT9770, MODEL_MAX }; enum rt9756_adc_chan { ADC_VBUS = 0, ADC_IBUS, ADC_VBAT, ADC_IBAT, ADC_TDIE, ADC_MAX_CHANNEL }; enum rt9756_usb_type { USB_NO_VBUS = 0, USB_SDP = 2, USB_NSTD, USB_DCP, USB_CDP, MAX_USB_TYPE }; enum rt9756_fields { F_VBATOVP = 0, F_VBATOVP_EN, F_IBATOCP, F_IBATOCP_EN, F_VBUSOVP, F_VBUSOVP_EN, F_IBUSOCP, F_IBUSOCP_EN, F_SWITCHING, F_REG_RST, F_CHG_EN, F_OP_MODE, F_WDT_DIS, F_WDT_TMR, F_DEV_ID, F_BC12_EN, F_USB_STATE, F_VBUS_STATE, F_IBAT_RSEN, F_REVISION, F_MAX_FIELD }; enum rt9756_ranges { R_VBATOVP = 0, R_IBATOCP, R_VBUSOVP, R_IBUSOCP, R_MAX_RANGE }; static const struct reg_field rt9756_chg_fields[F_MAX_FIELD] = { [F_VBATOVP] = REG_FIELD(0x08, 0, 4), [F_VBATOVP_EN] = REG_FIELD(0x08, 7, 7), [F_IBATOCP] = REG_FIELD(0x09, 0, 5), [F_IBATOCP_EN] = REG_FIELD(0x09, 7, 7), [F_VBUSOVP] = REG_FIELD(0x06, 0, 5), [F_VBUSOVP_EN] = REG_FIELD(0x06, 7, 7), [F_IBUSOCP] = REG_FIELD(0x07, 0, 4), [F_IBUSOCP_EN] = REG_FIELD(0x07, 5, 5), [F_SWITCHING] = REG_FIELD(0x5c, 7, 7), [F_REG_RST] = REG_FIELD(0x00, 7, 7), [F_CHG_EN] = REG_FIELD(0x00, 6, 6), [F_OP_MODE] = REG_FIELD(0x00, 5, 5), [F_WDT_DIS] = REG_FIELD(0x00, 3, 3), [F_WDT_TMR] = REG_FIELD(0x00, 0, 2), [F_DEV_ID] = REG_FIELD(0x03, 0, 3), [F_BC12_EN] = REG_FIELD(0x44, 7, 7), [F_USB_STATE] = REG_FIELD(0x46, 5, 7), [F_VBUS_STATE] = REG_FIELD(0x4c, 0, 0), [F_IBAT_RSEN] = REG_FIELD(0x5e, 0, 1), [F_REVISION] = REG_FIELD(0x62, 0, 1), }; static const struct reg_field rt9770_chg_fields[F_MAX_FIELD] = { [F_VBATOVP] = REG_FIELD(0x08, 0, 4), [F_VBATOVP_EN] = REG_FIELD(0x08, 7, 7), [F_IBATOCP] = REG_FIELD(0x09, 0, 5), [F_IBATOCP_EN] = REG_FIELD(0x09, 7, 7), [F_VBUSOVP] = REG_FIELD(0x06, 0, 5), [F_VBUSOVP_EN] = REG_FIELD(0x06, 7, 7), [F_IBUSOCP] = REG_FIELD(0x07, 0, 4), [F_IBUSOCP_EN] = REG_FIELD(0x07, 5, 5), [F_SWITCHING] = REG_FIELD(0x5c, 7, 7), [F_REG_RST] = REG_FIELD(0x00, 7, 7), [F_CHG_EN] = REG_FIELD(0x00, 6, 6), [F_OP_MODE] = REG_FIELD(0x00, 5, 5), [F_WDT_DIS] = REG_FIELD(0x00, 3, 3), [F_WDT_TMR] = REG_FIELD(0x00, 0, 2), [F_DEV_ID] = REG_FIELD(0x60, 0, 3), [F_BC12_EN] = REG_FIELD(0x03, 7, 7), [F_USB_STATE] = REG_FIELD(0x02, 5, 7), [F_VBUS_STATE] = REG_FIELD(0x4c, 0, 0), [F_IBAT_RSEN] = REG_FIELD(0x5e, 0, 1), [F_REVISION] = REG_FIELD(0x62, 3, 7), }; /* All converted to microvolt or microamp */ static const struct linear_range rt9756_chg_ranges[R_MAX_RANGE] = { LINEAR_RANGE_IDX(R_VBATOVP, 4200000, 0, 31, 25000), LINEAR_RANGE_IDX(R_IBATOCP, 2000000, 0, 63, 100000), LINEAR_RANGE_IDX(R_VBUSOVP, 3000000, 0, 63, 50000), LINEAR_RANGE_IDX(R_IBUSOCP, 1000000, 0, 31, 250000), }; struct charger_event { unsigned int flag1; unsigned int flag2; unsigned int flag3; unsigned int flag4; }; struct rt9756_data { struct device *dev; struct regmap *regmap; struct regmap_field *rm_fields[F_MAX_FIELD]; struct power_supply *psy; struct power_supply *bat_psy; struct mutex adc_lock; struct power_supply_desc psy_desc; struct power_supply_desc bat_psy_desc; struct charger_event chg_evt; unsigned int rg_resistor; unsigned int real_resistor; enum rt9756_model model; atomic_t usb_type; }; struct rt975x_dev_data { const struct regmap_config *regmap_config; const struct reg_field *reg_fields; const struct reg_sequence *init_regs; size_t num_init_regs; int (*check_device_model)(struct rt9756_data *data); }; static int rt9756_get_value_field_range(struct rt9756_data *data, enum rt9756_fields en_field, enum rt9756_fields field, enum rt9756_ranges rsel, int *val) { const struct linear_range *range = rt9756_chg_ranges + rsel; unsigned int enable, selector, value; int ret; ret = regmap_field_read(data->rm_fields[en_field], &enable); if (ret) return ret; if (!enable) { *val = 0; return 0; } ret = regmap_field_read(data->rm_fields[field], &selector); if (ret) return ret; ret = linear_range_get_value(range, selector, &value); if (ret) return ret; *val = (int)value; return 0; } static int rt9756_set_value_field_range(struct rt9756_data *data, enum rt9756_fields en_field, enum rt9756_fields field, enum rt9756_ranges rsel, int val) { const struct linear_range *range = rt9756_chg_ranges + rsel; unsigned int selector, value; int ret; if (!val) return regmap_field_write(data->rm_fields[en_field], 0); value = (unsigned int)val; linear_range_get_selector_within(range, value, &selector); ret = regmap_field_write(data->rm_fields[field], selector); if (ret) return ret; return regmap_field_write(data->rm_fields[en_field], 1); } static int rt9756_get_adc(struct rt9756_data *data, enum rt9756_adc_chan chan, int *val) { struct regmap *regmap = data->regmap; unsigned int reg_addr = RT9756_REG_VBUSADC + chan * 2; unsigned int mask = RT9756_ADCEN_MASK | RT9756_ADCONCE_MASK; unsigned int shift = 0, adc_cntl; __be16 raws; int scale, offset = 0, ret; guard(mutex)(&data->adc_lock); ret = regmap_update_bits(regmap, RT9756_REG_ADCCTL, mask, mask); if (ret) return ret; ret = regmap_read_poll_timeout(regmap, RT9756_REG_ADCCTL, adc_cntl, !(adc_cntl & RT9756_ADCEN_MASK), RT9756_ADC_CONVTIME, RT9756_ADC_MAXWAIT); if (ret && ret != -ETIMEDOUT) return ret; ret = regmap_raw_read(regmap, reg_addr, &raws, sizeof(raws)); if (ret) return ret; /* * TDIE LSB 1'c, others LSB 1000uV or 1000uA. * Rsense ratio is needed for IBAT channel */ if (chan == ADC_TDIE) { scale = 10; shift = 8; offset = -40; } else if (chan == ADC_IBAT) scale = 1000 * data->rg_resistor / data->real_resistor; else scale = 1000; *val = ((be16_to_cpu(raws) >> shift) + offset) * scale; return regmap_update_bits(regmap, RT9756_REG_ADCCTL, mask, 0); } static int rt9756_get_switching_state(struct rt9756_data *data, int *status) { unsigned int switching_state; int ret; ret = regmap_field_read(data->rm_fields[F_SWITCHING], &switching_state); if (ret) return ret; if (switching_state) *status = POWER_SUPPLY_STATUS_CHARGING; else *status = POWER_SUPPLY_STATUS_NOT_CHARGING; return 0; } static int rt9756_get_charger_health(struct rt9756_data *data) { struct charger_event *evt = &data->chg_evt; if (evt->flag2 & RT9756_EVT_VBUSLOW_ERR) return POWER_SUPPLY_HEALTH_UNDERVOLTAGE; if (evt->flag1 & RT9756_EVT_BUSOVP || evt->flag2 & RT9756_EVT_BATOVP || evt->flag4 & RT9756_EVT_OUTOVP) return POWER_SUPPLY_HEALTH_OVERVOLTAGE; if (evt->flag1 & RT9756_EVT_BUSOCP || evt->flag2 & RT9756_EVT_BATOCP) return POWER_SUPPLY_HEALTH_OVERCURRENT; if (evt->flag1 & RT9756_EVT_BUSUCP) return POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; if (evt->flag2 & RT9756_EVT_TDIEOTP) return POWER_SUPPLY_HEALTH_OVERHEAT; if (evt->flag3 & RT9756_EVT_WDT) return POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE; return POWER_SUPPLY_HEALTH_GOOD; } static int rt9756_get_charger_online(struct rt9756_data *data, int *val) { unsigned int online; int ret; ret = regmap_field_read(data->rm_fields[F_VBUS_STATE], &online); if (ret) return ret; *val = !!online; return 0; } static int rt9756_get_vbus_ovp(struct rt9756_data *data, int *val) { unsigned int opmode; int ovpval, ret; /* operating mode -> 0 bypass, 1 div2 */ ret = regmap_field_read(data->rm_fields[F_OP_MODE], &opmode); if (ret) return ret; ret = rt9756_get_value_field_range(data, F_VBUSOVP_EN, F_VBUSOVP, R_VBUSOVP, &ovpval); if (ret) return ret; *val = opmode ? ovpval * 2 : ovpval; return 0; } static int rt9756_set_vbus_ovp(struct rt9756_data *data, int val) { unsigned int opmode; int ret; /* operating mode -> 0 bypass, 1 div2 */ ret = regmap_field_read(data->rm_fields[F_OP_MODE], &opmode); if (ret) return ret; return rt9756_set_value_field_range(data, F_VBUSOVP_EN, F_VBUSOVP, R_VBUSOVP, opmode ? val / 2 : val); } static const char * const rt9756_manufacturer = "Richtek Technology Corp."; static const char * const rt9756_model[MODEL_MAX] = { "RT9756", "RT9757", "RT9770" }; static int rt9756_psy_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct rt9756_data *data = power_supply_get_drvdata(psy); int *pval = &val->intval; switch (psp) { case POWER_SUPPLY_PROP_STATUS: return rt9756_get_switching_state(data, pval); case POWER_SUPPLY_PROP_HEALTH: *pval = rt9756_get_charger_health(data); return 0; case POWER_SUPPLY_PROP_ONLINE: return rt9756_get_charger_online(data, pval); case POWER_SUPPLY_PROP_VOLTAGE_MAX: return rt9756_get_vbus_ovp(data, pval); case POWER_SUPPLY_PROP_VOLTAGE_NOW: return rt9756_get_adc(data, ADC_VBUS, pval); case POWER_SUPPLY_PROP_CURRENT_MAX: return rt9756_get_value_field_range(data, F_IBUSOCP_EN, F_IBUSOCP, R_IBUSOCP, pval); case POWER_SUPPLY_PROP_CURRENT_NOW: return rt9756_get_adc(data, ADC_IBUS, pval); case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: return rt9756_get_value_field_range(data, F_VBATOVP_EN, F_VBATOVP, R_VBATOVP, pval); case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: return rt9756_get_value_field_range(data, F_IBATOCP_EN, F_IBATOCP, R_IBATOCP, pval); case POWER_SUPPLY_PROP_TEMP: return rt9756_get_adc(data, ADC_TDIE, pval); case POWER_SUPPLY_PROP_USB_TYPE: *pval = atomic_read(&data->usb_type); return 0; case POWER_SUPPLY_PROP_MODEL_NAME: val->strval = rt9756_model[data->model]; return 0; case POWER_SUPPLY_PROP_MANUFACTURER: val->strval = rt9756_manufacturer; return 0; default: return -ENODATA; } } static int rt9756_psy_set_property(struct power_supply *psy, enum power_supply_property psp, const union power_supply_propval *val) { struct rt9756_data *data = power_supply_get_drvdata(psy); int intval = val->intval; switch (psp) { case POWER_SUPPLY_PROP_STATUS: memset(&data->chg_evt, 0, sizeof(data->chg_evt)); return regmap_field_write(data->rm_fields[F_CHG_EN], !!intval); case POWER_SUPPLY_PROP_VOLTAGE_MAX: return rt9756_set_vbus_ovp(data, intval); case POWER_SUPPLY_PROP_CURRENT_MAX: return rt9756_set_value_field_range(data, F_IBUSOCP_EN, F_IBUSOCP, R_IBUSOCP, intval); case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: return rt9756_set_value_field_range(data, F_VBATOVP_EN, F_VBATOVP, R_VBATOVP, intval); case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: return rt9756_set_value_field_range(data, F_IBATOCP_EN, F_IBATOCP, R_IBATOCP, intval); case POWER_SUPPLY_PROP_USB_TYPE: return regmap_field_write(data->rm_fields[F_BC12_EN], !!intval); default: return -EINVAL; } } static const enum power_supply_property rt9756_psy_properties[] = { POWER_SUPPLY_PROP_STATUS, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_HEALTH, POWER_SUPPLY_PROP_ONLINE, POWER_SUPPLY_PROP_VOLTAGE_MAX, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_MAX, POWER_SUPPLY_PROP_CURRENT_NOW, POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, POWER_SUPPLY_PROP_TEMP, POWER_SUPPLY_PROP_USB_TYPE, POWER_SUPPLY_PROP_MODEL_NAME, POWER_SUPPLY_PROP_MANUFACTURER, }; static int rt9756_bat_psy_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct rt9756_data *data = power_supply_get_drvdata(psy); int *pval = &val->intval; switch (psp) { case POWER_SUPPLY_PROP_TECHNOLOGY: *pval = POWER_SUPPLY_TECHNOLOGY_LION; return 0; case POWER_SUPPLY_PROP_VOLTAGE_NOW: return rt9756_get_adc(data, ADC_VBAT, pval); case POWER_SUPPLY_PROP_CURRENT_NOW: return rt9756_get_adc(data, ADC_IBAT, pval); default: return -ENODATA; } } static const enum power_supply_property rt9756_bat_psy_properties[] = { POWER_SUPPLY_PROP_TECHNOLOGY, POWER_SUPPLY_PROP_VOLTAGE_NOW, POWER_SUPPLY_PROP_CURRENT_NOW, }; static int rt9756_psy_property_is_writeable(struct power_supply *psy, enum power_supply_property psp) { switch (psp) { case POWER_SUPPLY_PROP_STATUS: case POWER_SUPPLY_PROP_ONLINE: case POWER_SUPPLY_PROP_VOLTAGE_MAX: case POWER_SUPPLY_PROP_CURRENT_MAX: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: case POWER_SUPPLY_PROP_USB_TYPE: return 1; default: return 0; } } static const unsigned int rt9756_wdt_millisecond[] = { 500, 1000, 5000, 30000, 40000, 80000, 128000, 255000 }; static ssize_t watchdog_timer_show(struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *psy = to_power_supply(dev); struct rt9756_data *data = power_supply_get_drvdata(psy); unsigned int wdt_tmr_now = 0, wdt_sel, wdt_dis; int ret; ret = regmap_field_read(data->rm_fields[F_WDT_DIS], &wdt_dis); if (ret) return ret; if (!wdt_dis) { ret = regmap_field_read(data->rm_fields[F_WDT_TMR], &wdt_sel); if (ret) return ret; wdt_tmr_now = rt9756_wdt_millisecond[wdt_sel]; } return sysfs_emit(buf, "%d\n", wdt_tmr_now); } static ssize_t watchdog_timer_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct power_supply *psy = to_power_supply(dev); struct rt9756_data *data = power_supply_get_drvdata(psy); unsigned int wdt_set, wdt_sel; int ret; ret = kstrtouint(buf, 10, &wdt_set); if (ret) return ret; ret = regmap_field_write(data->rm_fields[F_WDT_DIS], 1); if (ret) return ret; wdt_sel = find_closest(wdt_set, rt9756_wdt_millisecond, ARRAY_SIZE(rt9756_wdt_millisecond)); ret = regmap_field_write(data->rm_fields[F_WDT_TMR], wdt_sel); if (ret) return ret; if (wdt_set) { ret = regmap_field_write(data->rm_fields[F_WDT_DIS], 0); if (ret) return ret; } return count; } static const char * const rt9756_opmode_str[] = { "bypass", "div2" }; static ssize_t operation_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { struct power_supply *psy = to_power_supply(dev); struct rt9756_data *data = power_supply_get_drvdata(psy); unsigned int opmode; int ret; ret = regmap_field_read(data->rm_fields[F_OP_MODE], &opmode); if (ret) return ret; return sysfs_emit(buf, "%s\n", rt9756_opmode_str[opmode]); } static ssize_t operation_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct power_supply *psy = to_power_supply(dev); struct rt9756_data *data = power_supply_get_drvdata(psy); int index, ret; index = sysfs_match_string(rt9756_opmode_str, buf); if (index < 0) return index; ret = regmap_field_write(data->rm_fields[F_OP_MODE], index); return ret ?: count; } static DEVICE_ATTR_RW(watchdog_timer); static DEVICE_ATTR_RW(operation_mode); static struct attribute *rt9756_sysfs_attrs[] = { &dev_attr_watchdog_timer.attr, &dev_attr_operation_mode.attr, NULL }; ATTRIBUTE_GROUPS(rt9756_sysfs); static int rt9756_register_psy(struct rt9756_data *data) { struct power_supply_desc *desc = &data->psy_desc; struct power_supply_desc *bat_desc = &data->bat_psy_desc; struct power_supply_config cfg = {}, bat_cfg = {}; struct device *dev = data->dev; char *psy_name, *bat_psy_name, **supplied_to; bat_cfg.drv_data = data; bat_cfg.fwnode = dev_fwnode(dev); bat_psy_name = devm_kasprintf(dev, GFP_KERNEL, "rt9756-%s-battery", dev_name(dev)); if (!bat_psy_name) return -ENOMEM; bat_desc->name = bat_psy_name; bat_desc->type = POWER_SUPPLY_TYPE_BATTERY; bat_desc->properties = rt9756_bat_psy_properties; bat_desc->num_properties = ARRAY_SIZE(rt9756_bat_psy_properties); bat_desc->get_property = rt9756_bat_psy_get_property; data->bat_psy = devm_power_supply_register(dev, bat_desc, &bat_cfg); if (IS_ERR(data->bat_psy)) return dev_err_probe(dev, PTR_ERR(data->bat_psy), "Failed to register battery\n"); supplied_to = devm_kzalloc(dev, sizeof(*supplied_to), GFP_KERNEL); if (!supplied_to) return -ENOMEM; /* Link charger psy to battery psy */ supplied_to[0] = bat_psy_name; cfg.drv_data = data; cfg.fwnode = dev_fwnode(dev); cfg.attr_grp = rt9756_sysfs_groups; cfg.supplied_to = supplied_to; cfg.num_supplicants = 1; psy_name = devm_kasprintf(dev, GFP_KERNEL, "rt9756-%s", dev_name(dev)); if (!psy_name) return -ENOMEM; desc->name = psy_name; desc->type = POWER_SUPPLY_TYPE_USB; desc->usb_types = BIT(POWER_SUPPLY_USB_TYPE_UNKNOWN) | BIT(POWER_SUPPLY_USB_TYPE_SDP) | BIT(POWER_SUPPLY_USB_TYPE_DCP) | BIT(POWER_SUPPLY_USB_TYPE_CDP); desc->properties = rt9756_psy_properties; desc->num_properties = ARRAY_SIZE(rt9756_psy_properties); desc->property_is_writeable = rt9756_psy_property_is_writeable; desc->get_property = rt9756_psy_get_property; desc->set_property = rt9756_psy_set_property; data->psy = devm_power_supply_register(dev, desc, &cfg); return PTR_ERR_OR_ZERO(data->psy); } static int rt9756_get_usb_type(struct rt9756_data *data) { unsigned int type; int report_type, ret; ret = regmap_field_read(data->rm_fields[F_USB_STATE], &type); if (ret) return ret; switch (type) { case USB_SDP: case USB_NSTD: report_type = POWER_SUPPLY_USB_TYPE_SDP; break; case USB_DCP: report_type = POWER_SUPPLY_USB_TYPE_DCP; break; case USB_CDP: report_type = POWER_SUPPLY_USB_TYPE_CDP; break; case USB_NO_VBUS: default: report_type = POWER_SUPPLY_USB_TYPE_UNKNOWN; break; } atomic_set(&data->usb_type, report_type); return 0; } static irqreturn_t rt9756_irq_handler(int irq, void *devid) { struct rt9756_data *data = devid; struct regmap *regmap = data->regmap; struct charger_event *evt = &data->chg_evt; unsigned int bc12_flag = 0; int ret; ret = regmap_read(regmap, RT9756_REG_INTFLAG1, &evt->flag1); if (ret) return IRQ_NONE; ret = regmap_read(regmap, RT9756_REG_INTFLAG2, &evt->flag2); if (ret) return IRQ_NONE; ret = regmap_read(regmap, RT9756_REG_INTFLAG3, &evt->flag3); if (ret) return IRQ_NONE; if (data->model != MODEL_RT9770) { ret = regmap_read(regmap, RT9756_REG_INTFLAG4, &evt->flag4); if (ret) return IRQ_NONE; ret = regmap_read(regmap, RT9756_REG_BC12FLAG, &bc12_flag); if (ret) return IRQ_NONE; } dev_dbg(data->dev, "events: 0x%02x,%02x,%02x,%02x,%02x\n", evt->flag1, evt->flag2, evt->flag3, evt->flag4, bc12_flag); if (evt->flag2 & RT9756_EVT_VAC_INSERT) { ret = regmap_field_write(data->rm_fields[F_BC12_EN], 1); if (ret) return IRQ_NONE; } if (evt->flag3 & RT9756_EVT_VAC_UVLO) atomic_set(&data->usb_type, POWER_SUPPLY_USB_TYPE_UNKNOWN); if (bc12_flag & RT9756_EVT_BC12_DONE) { ret = rt9756_get_usb_type(data); if (ret) return IRQ_NONE; } power_supply_changed(data->psy); return IRQ_HANDLED; } static int rt9756_config_batsense_resistor(struct rt9756_data *data) { unsigned int shunt_resistor_uohms = 2000, rsense_sel; device_property_read_u32(data->dev, "shunt-resistor-micro-ohms", &shunt_resistor_uohms); if (!shunt_resistor_uohms || shunt_resistor_uohms > 5000) return -EINVAL; data->real_resistor = shunt_resistor_uohms; /* Always choose the larger or equal one to prevent false ocp alarm */ if (shunt_resistor_uohms <= 1000) { rsense_sel = 0; data->rg_resistor = 1000; } else if (shunt_resistor_uohms <= 2000) { rsense_sel = 1; data->rg_resistor = 2000; } else { rsense_sel = 2; data->rg_resistor = 5000; } return regmap_field_write(data->rm_fields[F_IBAT_RSEN], rsense_sel); } static const struct reg_sequence rt9756_init_regs[] = { REG_SEQ(0x00, 0x80, 1000), /* REG_RESET */ REG_SEQ0(0x04, 0x13), /* VACOVP/OVPGATE 12V */ REG_SEQ0(0x00, 0x28), /* WDT_DIS = 1 */ REG_SEQ0(0x0c, 0x02), /* MASK FLAG1 */ REG_SEQ0(0x0e, 0x06), /* MASK FLAG2 */ REG_SEQ0(0x10, 0xca), /* MASK FLAG3 */ REG_SEQ0(0x44, 0xa0), /* BC12_EN */ REG_SEQ0(0x47, 0x07), /* MASK BC12FLAG */ REG_SEQ0(0x4a, 0xfe), /* MASK FLAG4 */ REG_SEQ0(0x5c, 0x40), /* MASK CON_SWITCHING */ REG_SEQ0(0x63, 0x01), /* MASK VDDA_UVLO */ }; static const struct reg_sequence rt9770_init_regs[] = { REG_SEQ(0x00, 0x80, 1000), /* REG_RESET */ REG_SEQ0(0x04, 0x13), /* VACOVP/OVPGATE 12V */ REG_SEQ0(0x00, 0x28), /* WDT_DIS = 1 */ REG_SEQ0(0x0c, 0x02), /* MASK FLAG1 */ REG_SEQ0(0x0e, 0x06), /* MASK FLAG2 */ REG_SEQ0(0x10, 0xca), /* MASK FLAG3 */ REG_SEQ0(0x5c, 0x40), /* MASK CON_SWITCHING */ REG_SEQ0(0x63, 0x01), /* MASK VDDA_UVLO */ }; static const struct regmap_config rt9756_regmap_config = { .name = "rt9756", .reg_bits = 16, .val_bits = 8, .max_register = 0x1ff, }; static const struct regmap_config rt9770_regmap_config = { .name = "rt9770", .reg_bits = 8, .val_bits = 8, .max_register = 0xff, }; static int rt9756_check_device_model(struct rt9756_data *data) { struct device *dev = data->dev; unsigned int revid; int ret; ret = regmap_field_read(data->rm_fields[F_REVISION], &revid); if (ret) return dev_err_probe(dev, ret, "Failed to read revid\n"); if (revid == RT9757_REVID || revid == RT9757A_REVID) data->model = MODEL_RT9757; else if (revid == RT9756_REVID || revid == RT9756A_REVID) data->model = MODEL_RT9756; else return dev_err_probe(dev, -EINVAL, "Unknown revision %d\n", revid); return 0; } static int rt9770_check_device_model(struct rt9756_data *data) { data->model = MODEL_RT9770; return 0; } static int rt9756_probe(struct i2c_client *i2c) { const struct rt975x_dev_data *dev_data; struct device *dev = &i2c->dev; struct rt9756_data *data; struct regmap *regmap; unsigned int devid; int ret; dev_data = device_get_match_data(dev); if (!dev_data) return dev_err_probe(dev, -EINVAL, "No device data found\n"); data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->dev = dev; mutex_init(&data->adc_lock); atomic_set(&data->usb_type, POWER_SUPPLY_USB_TYPE_UNKNOWN); i2c_set_clientdata(i2c, data); regmap = devm_regmap_init_i2c(i2c, dev_data->regmap_config); if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n"); data->regmap = regmap; ret = devm_regmap_field_bulk_alloc(dev, regmap, data->rm_fields, dev_data->reg_fields, F_MAX_FIELD); if (ret) return dev_err_probe(dev, ret, "Failed to alloc regmap fields\n"); /* Richtek Device ID check */ ret = regmap_field_read(data->rm_fields[F_DEV_ID], &devid); if (ret) return dev_err_probe(dev, ret, "Failed to read devid\n"); if (devid != RICHTEK_DEVID) return dev_err_probe(dev, -ENODEV, "Incorrect VID 0x%02x\n", devid); /* Get specific model */ ret = dev_data->check_device_model(data); if (ret) return ret; ret = regmap_register_patch(regmap, dev_data->init_regs, dev_data->num_init_regs); if (ret) return dev_err_probe(dev, ret, "Failed to init registers\n"); ret = rt9756_config_batsense_resistor(data); if (ret) return dev_err_probe(dev, ret, "Failed to config batsense resistor\n"); ret = rt9756_register_psy(data); if (ret) return dev_err_probe(dev, ret, "Failed to init power supply\n"); return devm_request_threaded_irq(dev, i2c->irq, NULL, rt9756_irq_handler, IRQF_ONESHOT, dev_name(dev), data); } static void rt9756_shutdown(struct i2c_client *i2c) { struct rt9756_data *data = i2c_get_clientdata(i2c); regmap_field_write(data->rm_fields[F_REG_RST], 1); } static const struct rt975x_dev_data rt9756_dev_data = { .regmap_config = &rt9756_regmap_config, .reg_fields = rt9756_chg_fields, .init_regs = rt9756_init_regs, .num_init_regs = ARRAY_SIZE(rt9756_init_regs), .check_device_model = rt9756_check_device_model, }; static const struct rt975x_dev_data rt9770_dev_data = { .regmap_config = &rt9770_regmap_config, .reg_fields = rt9770_chg_fields, .init_regs = rt9770_init_regs, .num_init_regs = ARRAY_SIZE(rt9770_init_regs), .check_device_model = rt9770_check_device_model, }; static const struct of_device_id rt9756_device_match_table[] = { { .compatible = "richtek,rt9756", .data = &rt9756_dev_data }, { .compatible = "richtek,rt9770", .data = &rt9770_dev_data }, {} }; MODULE_DEVICE_TABLE(of, rt9756_device_match_table); static struct i2c_driver rt9756_charger_driver = { .driver = { .name = "rt9756", .of_match_table = rt9756_device_match_table, }, .probe = rt9756_probe, .shutdown = rt9756_shutdown, }; module_i2c_driver(rt9756_charger_driver); MODULE_DESCRIPTION("Richtek RT9756 charger driver"); MODULE_AUTHOR("ChiYuan Huang "); MODULE_LICENSE("GPL");