162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ROHM BD99954 charger driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2020 Rohm Semiconductors 662306a36Sopenharmony_ci * Originally written by: 762306a36Sopenharmony_ci * Mikko Mutanen <mikko.mutanen@fi.rohmeurope.com> 862306a36Sopenharmony_ci * Markus Laine <markus.laine@fi.rohmeurope.com> 962306a36Sopenharmony_ci * Bugs added by: 1062306a36Sopenharmony_ci * Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com> 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * The battery charging profile of BD99954. 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Curve (1) represents charging current. 1762306a36Sopenharmony_ci * Curve (2) represents battery voltage. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * The BD99954 data sheet divides charging to three phases. 2062306a36Sopenharmony_ci * a) Trickle-charge with constant current (8). 2162306a36Sopenharmony_ci * b) pre-charge with constant current (6) 2262306a36Sopenharmony_ci * c) fast-charge, first with constant current (5) phase. After 2362306a36Sopenharmony_ci * the battery voltage has reached target level (4) we have constant 2462306a36Sopenharmony_ci * voltage phase until charging current has dropped to termination 2562306a36Sopenharmony_ci * level (7) 2662306a36Sopenharmony_ci * 2762306a36Sopenharmony_ci * V ^ ^ I 2862306a36Sopenharmony_ci * . . 2962306a36Sopenharmony_ci * . . 3062306a36Sopenharmony_ci *(4)` `.` ` ` ` ` ` ` ` ` ` ` ` ` ` ----------------------------. 3162306a36Sopenharmony_ci * . :/ . 3262306a36Sopenharmony_ci * . o----+/:/ ` ` ` ` ` ` ` ` ` ` ` ` `.` ` (5) 3362306a36Sopenharmony_ci * . + :: + . 3462306a36Sopenharmony_ci * . + /- -- . 3562306a36Sopenharmony_ci * . +`/- + . 3662306a36Sopenharmony_ci * . o/- -: . 3762306a36Sopenharmony_ci * . .s. +` . 3862306a36Sopenharmony_ci * . .--+ `/ . 3962306a36Sopenharmony_ci * . ..`` + .: . 4062306a36Sopenharmony_ci * . -` + -- . 4162306a36Sopenharmony_ci * . (2) ...`` + :- . 4262306a36Sopenharmony_ci * . ...`` + -: . 4362306a36Sopenharmony_ci *(3)` `.`."" ` ` ` `+-------- ` ` ` ` ` ` `.:` ` ` ` ` ` ` ` ` .` ` (6) 4462306a36Sopenharmony_ci * . + `:. . 4562306a36Sopenharmony_ci * . + -: . 4662306a36Sopenharmony_ci * . + -:. . 4762306a36Sopenharmony_ci * . + .--. . 4862306a36Sopenharmony_ci * . (1) + `.+` ` ` `.` ` (7) 4962306a36Sopenharmony_ci * -..............` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` + ` ` ` .` ` (8) 5062306a36Sopenharmony_ci * . + - 5162306a36Sopenharmony_ci * -------------------------------------------------+++++++++--> 5262306a36Sopenharmony_ci * | trickle | pre | fast | 5362306a36Sopenharmony_ci * 5462306a36Sopenharmony_ci * Details of DT properties for different limits can be found from BD99954 5562306a36Sopenharmony_ci * device tree binding documentation. 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#include <linux/delay.h> 5962306a36Sopenharmony_ci#include <linux/gpio/consumer.h> 6062306a36Sopenharmony_ci#include <linux/interrupt.h> 6162306a36Sopenharmony_ci#include <linux/i2c.h> 6262306a36Sopenharmony_ci#include <linux/kernel.h> 6362306a36Sopenharmony_ci#include <linux/linear_range.h> 6462306a36Sopenharmony_ci#include <linux/module.h> 6562306a36Sopenharmony_ci#include <linux/mod_devicetable.h> 6662306a36Sopenharmony_ci#include <linux/power_supply.h> 6762306a36Sopenharmony_ci#include <linux/property.h> 6862306a36Sopenharmony_ci#include <linux/regmap.h> 6962306a36Sopenharmony_ci#include <linux/types.h> 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci#include "bd99954-charger.h" 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistruct battery_data { 7462306a36Sopenharmony_ci u16 precharge_current; /* Trickle-charge Current */ 7562306a36Sopenharmony_ci u16 fc_reg_voltage; /* Fast Charging Regulation Voltage */ 7662306a36Sopenharmony_ci u16 voltage_min; 7762306a36Sopenharmony_ci u16 voltage_max; 7862306a36Sopenharmony_ci}; 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* Initial field values, converted to initial register values */ 8162306a36Sopenharmony_cistruct bd9995x_init_data { 8262306a36Sopenharmony_ci u16 vsysreg_set; /* VSYS Regulation Setting */ 8362306a36Sopenharmony_ci u16 ibus_lim_set; /* VBUS input current limitation */ 8462306a36Sopenharmony_ci u16 icc_lim_set; /* VCC/VACP Input Current Limit Setting */ 8562306a36Sopenharmony_ci u16 itrich_set; /* Trickle-charge Current Setting */ 8662306a36Sopenharmony_ci u16 iprech_set; /* Pre-Charge Current Setting */ 8762306a36Sopenharmony_ci u16 ichg_set; /* Fast-Charge constant current */ 8862306a36Sopenharmony_ci u16 vfastchg_reg_set1; /* Fast Charging Regulation Voltage */ 8962306a36Sopenharmony_ci u16 vprechg_th_set; /* Pre-charge Voltage Threshold Setting */ 9062306a36Sopenharmony_ci u16 vrechg_set; /* Re-charge Battery Voltage Setting */ 9162306a36Sopenharmony_ci u16 vbatovp_set; /* Battery Over Voltage Threshold Setting */ 9262306a36Sopenharmony_ci u16 iterm_set; /* Charging termination current */ 9362306a36Sopenharmony_ci}; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cistruct bd9995x_state { 9662306a36Sopenharmony_ci u8 online; 9762306a36Sopenharmony_ci u16 chgstm_status; 9862306a36Sopenharmony_ci u16 vbat_vsys_status; 9962306a36Sopenharmony_ci u16 vbus_vcc_status; 10062306a36Sopenharmony_ci}; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_cistruct bd9995x_device { 10362306a36Sopenharmony_ci struct i2c_client *client; 10462306a36Sopenharmony_ci struct device *dev; 10562306a36Sopenharmony_ci struct power_supply *charger; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci struct regmap *rmap; 10862306a36Sopenharmony_ci struct regmap_field *rmap_fields[F_MAX_FIELDS]; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci int chip_id; 11162306a36Sopenharmony_ci int chip_rev; 11262306a36Sopenharmony_ci struct bd9995x_init_data init_data; 11362306a36Sopenharmony_ci struct bd9995x_state state; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci struct mutex lock; /* Protect state data */ 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistatic const struct regmap_range bd9995x_readonly_reg_ranges[] = { 11962306a36Sopenharmony_ci regmap_reg_range(CHGSTM_STATUS, SEL_ILIM_VAL), 12062306a36Sopenharmony_ci regmap_reg_range(IOUT_DACIN_VAL, IOUT_DACIN_VAL), 12162306a36Sopenharmony_ci regmap_reg_range(VCC_UCD_STATUS, VCC_IDD_STATUS), 12262306a36Sopenharmony_ci regmap_reg_range(VBUS_UCD_STATUS, VBUS_IDD_STATUS), 12362306a36Sopenharmony_ci regmap_reg_range(CHIP_ID, CHIP_REV), 12462306a36Sopenharmony_ci regmap_reg_range(SYSTEM_STATUS, SYSTEM_STATUS), 12562306a36Sopenharmony_ci regmap_reg_range(IBATP_VAL, VBAT_AVE_VAL), 12662306a36Sopenharmony_ci regmap_reg_range(VTH_VAL, EXTIADP_AVE_VAL), 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic const struct regmap_access_table bd9995x_writeable_regs = { 13062306a36Sopenharmony_ci .no_ranges = bd9995x_readonly_reg_ranges, 13162306a36Sopenharmony_ci .n_no_ranges = ARRAY_SIZE(bd9995x_readonly_reg_ranges), 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic const struct regmap_range bd9995x_volatile_reg_ranges[] = { 13562306a36Sopenharmony_ci regmap_reg_range(CHGSTM_STATUS, WDT_STATUS), 13662306a36Sopenharmony_ci regmap_reg_range(VCC_UCD_STATUS, VCC_IDD_STATUS), 13762306a36Sopenharmony_ci regmap_reg_range(VBUS_UCD_STATUS, VBUS_IDD_STATUS), 13862306a36Sopenharmony_ci regmap_reg_range(INT0_STATUS, INT7_STATUS), 13962306a36Sopenharmony_ci regmap_reg_range(SYSTEM_STATUS, SYSTEM_CTRL_SET), 14062306a36Sopenharmony_ci regmap_reg_range(IBATP_VAL, EXTIADP_AVE_VAL), /* Measurement regs */ 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_cistatic const struct regmap_access_table bd9995x_volatile_regs = { 14462306a36Sopenharmony_ci .yes_ranges = bd9995x_volatile_reg_ranges, 14562306a36Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(bd9995x_volatile_reg_ranges), 14662306a36Sopenharmony_ci}; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic const struct regmap_range_cfg regmap_range_cfg[] = { 14962306a36Sopenharmony_ci { 15062306a36Sopenharmony_ci .selector_reg = MAP_SET, 15162306a36Sopenharmony_ci .selector_mask = 0xFFFF, 15262306a36Sopenharmony_ci .selector_shift = 0, 15362306a36Sopenharmony_ci .window_start = 0, 15462306a36Sopenharmony_ci .window_len = 0x100, 15562306a36Sopenharmony_ci .range_min = 0 * 0x100, 15662306a36Sopenharmony_ci .range_max = 3 * 0x100, 15762306a36Sopenharmony_ci }, 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic const struct regmap_config bd9995x_regmap_config = { 16162306a36Sopenharmony_ci .reg_bits = 8, 16262306a36Sopenharmony_ci .val_bits = 16, 16362306a36Sopenharmony_ci .reg_stride = 1, 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci .max_register = 3 * 0x100, 16662306a36Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci .ranges = regmap_range_cfg, 16962306a36Sopenharmony_ci .num_ranges = ARRAY_SIZE(regmap_range_cfg), 17062306a36Sopenharmony_ci .val_format_endian = REGMAP_ENDIAN_LITTLE, 17162306a36Sopenharmony_ci .wr_table = &bd9995x_writeable_regs, 17262306a36Sopenharmony_ci .volatile_table = &bd9995x_volatile_regs, 17362306a36Sopenharmony_ci}; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cienum bd9995x_chrg_fault { 17662306a36Sopenharmony_ci CHRG_FAULT_NORMAL, 17762306a36Sopenharmony_ci CHRG_FAULT_INPUT, 17862306a36Sopenharmony_ci CHRG_FAULT_THERMAL_SHUTDOWN, 17962306a36Sopenharmony_ci CHRG_FAULT_TIMER_EXPIRED, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic int bd9995x_get_prop_batt_health(struct bd9995x_device *bd) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci int ret, tmp; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_BATTEMP], &tmp); 18762306a36Sopenharmony_ci if (ret) 18862306a36Sopenharmony_ci return POWER_SUPPLY_HEALTH_UNKNOWN; 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* TODO: Check these against datasheet page 34 */ 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci switch (tmp) { 19362306a36Sopenharmony_ci case ROOM: 19462306a36Sopenharmony_ci return POWER_SUPPLY_HEALTH_GOOD; 19562306a36Sopenharmony_ci case HOT1: 19662306a36Sopenharmony_ci case HOT2: 19762306a36Sopenharmony_ci case HOT3: 19862306a36Sopenharmony_ci return POWER_SUPPLY_HEALTH_OVERHEAT; 19962306a36Sopenharmony_ci case COLD1: 20062306a36Sopenharmony_ci case COLD2: 20162306a36Sopenharmony_ci return POWER_SUPPLY_HEALTH_COLD; 20262306a36Sopenharmony_ci case TEMP_DIS: 20362306a36Sopenharmony_ci case BATT_OPEN: 20462306a36Sopenharmony_ci default: 20562306a36Sopenharmony_ci return POWER_SUPPLY_HEALTH_UNKNOWN; 20662306a36Sopenharmony_ci } 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_cistatic int bd9995x_get_prop_charge_type(struct bd9995x_device *bd) 21062306a36Sopenharmony_ci{ 21162306a36Sopenharmony_ci int ret, tmp; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_CHGSTM_STATE], &tmp); 21462306a36Sopenharmony_ci if (ret) 21562306a36Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci switch (tmp) { 21862306a36Sopenharmony_ci case CHGSTM_TRICKLE_CHARGE: 21962306a36Sopenharmony_ci case CHGSTM_PRE_CHARGE: 22062306a36Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 22162306a36Sopenharmony_ci case CHGSTM_FAST_CHARGE: 22262306a36Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_FAST; 22362306a36Sopenharmony_ci case CHGSTM_TOP_OFF: 22462306a36Sopenharmony_ci case CHGSTM_DONE: 22562306a36Sopenharmony_ci case CHGSTM_SUSPEND: 22662306a36Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_NONE; 22762306a36Sopenharmony_ci default: /* Rest of the states are error related, no charging */ 22862306a36Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_NONE; 22962306a36Sopenharmony_ci } 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic bool bd9995x_get_prop_batt_present(struct bd9995x_device *bd) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci int ret, tmp; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_BATTEMP], &tmp); 23762306a36Sopenharmony_ci if (ret) 23862306a36Sopenharmony_ci return false; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci return tmp != BATT_OPEN; 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int bd9995x_get_prop_batt_voltage(struct bd9995x_device *bd) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci int ret, tmp; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_VBAT_VAL], &tmp); 24862306a36Sopenharmony_ci if (ret) 24962306a36Sopenharmony_ci return 0; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci tmp = min(tmp, 19200); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci return tmp * 1000; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int bd9995x_get_prop_batt_current(struct bd9995x_device *bd) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci int ret, tmp; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_IBATP_VAL], &tmp); 26162306a36Sopenharmony_ci if (ret) 26262306a36Sopenharmony_ci return 0; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci return tmp * 1000; 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci#define DEFAULT_BATTERY_TEMPERATURE 250 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_cistatic int bd9995x_get_prop_batt_temp(struct bd9995x_device *bd) 27062306a36Sopenharmony_ci{ 27162306a36Sopenharmony_ci int ret, tmp; 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_THERM_VAL], &tmp); 27462306a36Sopenharmony_ci if (ret) 27562306a36Sopenharmony_ci return DEFAULT_BATTERY_TEMPERATURE; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return (200 - tmp) * 10; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_cistatic int bd9995x_power_supply_get_property(struct power_supply *psy, 28162306a36Sopenharmony_ci enum power_supply_property psp, 28262306a36Sopenharmony_ci union power_supply_propval *val) 28362306a36Sopenharmony_ci{ 28462306a36Sopenharmony_ci int ret, tmp; 28562306a36Sopenharmony_ci struct bd9995x_device *bd = power_supply_get_drvdata(psy); 28662306a36Sopenharmony_ci struct bd9995x_state state; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci mutex_lock(&bd->lock); 28962306a36Sopenharmony_ci state = bd->state; 29062306a36Sopenharmony_ci mutex_unlock(&bd->lock); 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci switch (psp) { 29362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 29462306a36Sopenharmony_ci switch (state.chgstm_status) { 29562306a36Sopenharmony_ci case CHGSTM_TRICKLE_CHARGE: 29662306a36Sopenharmony_ci case CHGSTM_PRE_CHARGE: 29762306a36Sopenharmony_ci case CHGSTM_FAST_CHARGE: 29862306a36Sopenharmony_ci case CHGSTM_TOP_OFF: 29962306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 30062306a36Sopenharmony_ci break; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci case CHGSTM_DONE: 30362306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_FULL; 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci case CHGSTM_SUSPEND: 30762306a36Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_1: 30862306a36Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_2: 30962306a36Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_3: 31062306a36Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_4: 31162306a36Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_5: 31262306a36Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_6: 31362306a36Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_7: 31462306a36Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_1: 31562306a36Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_2: 31662306a36Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_3: 31762306a36Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_4: 31862306a36Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_5: 31962306a36Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_6: 32062306a36Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_7: 32162306a36Sopenharmony_ci case CHGSTM_BATTERY_ERROR: 32262306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 32362306a36Sopenharmony_ci break; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci default: 32662306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 32762306a36Sopenharmony_ci break; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci break; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_MANUFACTURER: 33262306a36Sopenharmony_ci val->strval = BD9995X_MANUFACTURER; 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 33662306a36Sopenharmony_ci val->intval = state.online; 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 34062306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_IBATP_VAL], &tmp); 34162306a36Sopenharmony_ci if (ret) 34262306a36Sopenharmony_ci return ret; 34362306a36Sopenharmony_ci val->intval = tmp * 1000; 34462306a36Sopenharmony_ci break; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_AVG: 34762306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_IBATP_AVE_VAL], &tmp); 34862306a36Sopenharmony_ci if (ret) 34962306a36Sopenharmony_ci return ret; 35062306a36Sopenharmony_ci val->intval = tmp * 1000; 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 35462306a36Sopenharmony_ci /* 35562306a36Sopenharmony_ci * Currently the DT uses this property to give the 35662306a36Sopenharmony_ci * target current for fast-charging constant current phase. 35762306a36Sopenharmony_ci * I think it is correct in a sense. 35862306a36Sopenharmony_ci * 35962306a36Sopenharmony_ci * Yet, this prop we read and return here is the programmed 36062306a36Sopenharmony_ci * safety limit for combined input currents. This feels 36162306a36Sopenharmony_ci * also correct in a sense. 36262306a36Sopenharmony_ci * 36362306a36Sopenharmony_ci * However, this results a mismatch to DT value and value 36462306a36Sopenharmony_ci * read from sysfs. 36562306a36Sopenharmony_ci */ 36662306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_SEL_ILIM_VAL], &tmp); 36762306a36Sopenharmony_ci if (ret) 36862306a36Sopenharmony_ci return ret; 36962306a36Sopenharmony_ci val->intval = tmp * 1000; 37062306a36Sopenharmony_ci break; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 37362306a36Sopenharmony_ci if (!state.online) { 37462306a36Sopenharmony_ci val->intval = 0; 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_VFASTCHG_REG_SET1], 37962306a36Sopenharmony_ci &tmp); 38062306a36Sopenharmony_ci if (ret) 38162306a36Sopenharmony_ci return ret; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* 38462306a36Sopenharmony_ci * The actual range : 2560 to 19200 mV. No matter what the 38562306a36Sopenharmony_ci * register says 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_ci val->intval = clamp_val(tmp << 4, 2560, 19200); 38862306a36Sopenharmony_ci val->intval *= 1000; 38962306a36Sopenharmony_ci break; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: 39262306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_ITERM_SET], &tmp); 39362306a36Sopenharmony_ci if (ret) 39462306a36Sopenharmony_ci return ret; 39562306a36Sopenharmony_ci /* Start step is 64 mA */ 39662306a36Sopenharmony_ci val->intval = tmp << 6; 39762306a36Sopenharmony_ci /* Maximum is 1024 mA - no matter what register says */ 39862306a36Sopenharmony_ci val->intval = min(val->intval, 1024); 39962306a36Sopenharmony_ci val->intval *= 1000; 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci /* Battery properties which we access through charger */ 40362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 40462306a36Sopenharmony_ci val->intval = bd9995x_get_prop_batt_present(bd); 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 40862306a36Sopenharmony_ci val->intval = bd9995x_get_prop_batt_voltage(bd); 40962306a36Sopenharmony_ci break; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 41262306a36Sopenharmony_ci val->intval = bd9995x_get_prop_batt_current(bd); 41362306a36Sopenharmony_ci break; 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TYPE: 41662306a36Sopenharmony_ci val->intval = bd9995x_get_prop_charge_type(bd); 41762306a36Sopenharmony_ci break; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 42062306a36Sopenharmony_ci val->intval = bd9995x_get_prop_batt_health(bd); 42162306a36Sopenharmony_ci break; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 42462306a36Sopenharmony_ci val->intval = bd9995x_get_prop_batt_temp(bd); 42562306a36Sopenharmony_ci break; 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 42862306a36Sopenharmony_ci val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_MODEL_NAME: 43262306a36Sopenharmony_ci val->strval = "bd99954"; 43362306a36Sopenharmony_ci break; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci default: 43662306a36Sopenharmony_ci return -EINVAL; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci } 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci return 0; 44162306a36Sopenharmony_ci} 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_cistatic int bd9995x_get_chip_state(struct bd9995x_device *bd, 44462306a36Sopenharmony_ci struct bd9995x_state *state) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci int i, ret, tmp; 44762306a36Sopenharmony_ci struct { 44862306a36Sopenharmony_ci struct regmap_field *id; 44962306a36Sopenharmony_ci u16 *data; 45062306a36Sopenharmony_ci } state_fields[] = { 45162306a36Sopenharmony_ci { 45262306a36Sopenharmony_ci bd->rmap_fields[F_CHGSTM_STATE], &state->chgstm_status, 45362306a36Sopenharmony_ci }, { 45462306a36Sopenharmony_ci bd->rmap_fields[F_VBAT_VSYS_STATUS], 45562306a36Sopenharmony_ci &state->vbat_vsys_status, 45662306a36Sopenharmony_ci }, { 45762306a36Sopenharmony_ci bd->rmap_fields[F_VBUS_VCC_STATUS], 45862306a36Sopenharmony_ci &state->vbus_vcc_status, 45962306a36Sopenharmony_ci }, 46062306a36Sopenharmony_ci }; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(state_fields); i++) { 46462306a36Sopenharmony_ci ret = regmap_field_read(state_fields[i].id, &tmp); 46562306a36Sopenharmony_ci if (ret) 46662306a36Sopenharmony_ci return ret; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci *state_fields[i].data = tmp; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci if (state->vbus_vcc_status & STATUS_VCC_DET || 47262306a36Sopenharmony_ci state->vbus_vcc_status & STATUS_VBUS_DET) 47362306a36Sopenharmony_ci state->online = 1; 47462306a36Sopenharmony_ci else 47562306a36Sopenharmony_ci state->online = 0; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci return 0; 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic irqreturn_t bd9995x_irq_handler_thread(int irq, void *private) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci struct bd9995x_device *bd = private; 48362306a36Sopenharmony_ci int ret, status, mask, i; 48462306a36Sopenharmony_ci unsigned long tmp; 48562306a36Sopenharmony_ci struct bd9995x_state state; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci /* 48862306a36Sopenharmony_ci * The bd9995x does not seem to generate big amount of interrupts. 48962306a36Sopenharmony_ci * The logic regarding which interrupts can cause relevant 49062306a36Sopenharmony_ci * status changes seem to be pretty complex. 49162306a36Sopenharmony_ci * 49262306a36Sopenharmony_ci * So lets implement really simple and hopefully bullet-proof handler: 49362306a36Sopenharmony_ci * It does not really matter which IRQ we handle, we just go and 49462306a36Sopenharmony_ci * re-read all interesting statuses + give the framework a nudge. 49562306a36Sopenharmony_ci * 49662306a36Sopenharmony_ci * Other option would be building a _complex_ and error prone logic 49762306a36Sopenharmony_ci * trying to decide what could have been changed (resulting this IRQ 49862306a36Sopenharmony_ci * we are now handling). During the normal operation the BD99954 does 49962306a36Sopenharmony_ci * not seem to be generating much of interrupts so benefit from such 50062306a36Sopenharmony_ci * logic would probably be minimal. 50162306a36Sopenharmony_ci */ 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci ret = regmap_read(bd->rmap, INT0_STATUS, &status); 50462306a36Sopenharmony_ci if (ret) { 50562306a36Sopenharmony_ci dev_err(bd->dev, "Failed to read IRQ status\n"); 50662306a36Sopenharmony_ci return IRQ_NONE; 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_INT0_SET], &mask); 51062306a36Sopenharmony_ci if (ret) { 51162306a36Sopenharmony_ci dev_err(bd->dev, "Failed to read IRQ mask\n"); 51262306a36Sopenharmony_ci return IRQ_NONE; 51362306a36Sopenharmony_ci } 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci /* Handle only IRQs that are not masked */ 51662306a36Sopenharmony_ci status &= mask; 51762306a36Sopenharmony_ci tmp = status; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci /* Lowest bit does not represent any sub-registers */ 52062306a36Sopenharmony_ci tmp >>= 1; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci /* 52362306a36Sopenharmony_ci * Mask and ack IRQs we will handle (+ the idiot bit) 52462306a36Sopenharmony_ci */ 52562306a36Sopenharmony_ci ret = regmap_field_write(bd->rmap_fields[F_INT0_SET], 0); 52662306a36Sopenharmony_ci if (ret) { 52762306a36Sopenharmony_ci dev_err(bd->dev, "Failed to mask F_INT0\n"); 52862306a36Sopenharmony_ci return IRQ_NONE; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci ret = regmap_write(bd->rmap, INT0_STATUS, status); 53262306a36Sopenharmony_ci if (ret) { 53362306a36Sopenharmony_ci dev_err(bd->dev, "Failed to ack F_INT0\n"); 53462306a36Sopenharmony_ci goto err_umask; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci for_each_set_bit(i, &tmp, 7) { 53862306a36Sopenharmony_ci int sub_status, sub_mask; 53962306a36Sopenharmony_ci static const int sub_status_reg[] = { 54062306a36Sopenharmony_ci INT1_STATUS, INT2_STATUS, INT3_STATUS, INT4_STATUS, 54162306a36Sopenharmony_ci INT5_STATUS, INT6_STATUS, INT7_STATUS, 54262306a36Sopenharmony_ci }; 54362306a36Sopenharmony_ci struct regmap_field *sub_mask_f[] = { 54462306a36Sopenharmony_ci bd->rmap_fields[F_INT1_SET], 54562306a36Sopenharmony_ci bd->rmap_fields[F_INT2_SET], 54662306a36Sopenharmony_ci bd->rmap_fields[F_INT3_SET], 54762306a36Sopenharmony_ci bd->rmap_fields[F_INT4_SET], 54862306a36Sopenharmony_ci bd->rmap_fields[F_INT5_SET], 54962306a36Sopenharmony_ci bd->rmap_fields[F_INT6_SET], 55062306a36Sopenharmony_ci bd->rmap_fields[F_INT7_SET], 55162306a36Sopenharmony_ci }; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci /* Clear sub IRQs */ 55462306a36Sopenharmony_ci ret = regmap_read(bd->rmap, sub_status_reg[i], &sub_status); 55562306a36Sopenharmony_ci if (ret) { 55662306a36Sopenharmony_ci dev_err(bd->dev, "Failed to read IRQ sub-status\n"); 55762306a36Sopenharmony_ci goto err_umask; 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci ret = regmap_field_read(sub_mask_f[i], &sub_mask); 56162306a36Sopenharmony_ci if (ret) { 56262306a36Sopenharmony_ci dev_err(bd->dev, "Failed to read IRQ sub-mask\n"); 56362306a36Sopenharmony_ci goto err_umask; 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* Ack active sub-statuses */ 56762306a36Sopenharmony_ci sub_status &= sub_mask; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci ret = regmap_write(bd->rmap, sub_status_reg[i], sub_status); 57062306a36Sopenharmony_ci if (ret) { 57162306a36Sopenharmony_ci dev_err(bd->dev, "Failed to ack sub-IRQ\n"); 57262306a36Sopenharmony_ci goto err_umask; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci ret = regmap_field_write(bd->rmap_fields[F_INT0_SET], mask); 57762306a36Sopenharmony_ci if (ret) 57862306a36Sopenharmony_ci /* May as well retry once */ 57962306a36Sopenharmony_ci goto err_umask; 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci /* Read whole chip state */ 58262306a36Sopenharmony_ci ret = bd9995x_get_chip_state(bd, &state); 58362306a36Sopenharmony_ci if (ret < 0) { 58462306a36Sopenharmony_ci dev_err(bd->dev, "Failed to read chip state\n"); 58562306a36Sopenharmony_ci } else { 58662306a36Sopenharmony_ci mutex_lock(&bd->lock); 58762306a36Sopenharmony_ci bd->state = state; 58862306a36Sopenharmony_ci mutex_unlock(&bd->lock); 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci power_supply_changed(bd->charger); 59162306a36Sopenharmony_ci } 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci return IRQ_HANDLED; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cierr_umask: 59662306a36Sopenharmony_ci ret = regmap_field_write(bd->rmap_fields[F_INT0_SET], mask); 59762306a36Sopenharmony_ci if (ret) 59862306a36Sopenharmony_ci dev_err(bd->dev, 59962306a36Sopenharmony_ci "Failed to un-mask F_INT0 - IRQ permanently disabled\n"); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci return IRQ_NONE; 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic int __bd9995x_chip_reset(struct bd9995x_device *bd) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci int ret, state; 60762306a36Sopenharmony_ci int rst_check_counter = 10; 60862306a36Sopenharmony_ci u16 tmp = ALLRST | OTPLD; 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci ret = regmap_raw_write(bd->rmap, SYSTEM_CTRL_SET, &tmp, 2); 61162306a36Sopenharmony_ci if (ret < 0) 61262306a36Sopenharmony_ci return ret; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci do { 61562306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_OTPLD_STATE], &state); 61662306a36Sopenharmony_ci if (ret) 61762306a36Sopenharmony_ci return ret; 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci msleep(10); 62062306a36Sopenharmony_ci } while (state == 0 && --rst_check_counter); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci if (!rst_check_counter) { 62362306a36Sopenharmony_ci dev_err(bd->dev, "chip reset not completed\n"); 62462306a36Sopenharmony_ci return -ETIMEDOUT; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci tmp = 0; 62862306a36Sopenharmony_ci ret = regmap_raw_write(bd->rmap, SYSTEM_CTRL_SET, &tmp, 2); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci return ret; 63162306a36Sopenharmony_ci} 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_cistatic int bd9995x_hw_init(struct bd9995x_device *bd) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci int ret; 63662306a36Sopenharmony_ci int i; 63762306a36Sopenharmony_ci struct bd9995x_state state; 63862306a36Sopenharmony_ci struct bd9995x_init_data *id = &bd->init_data; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci const struct { 64162306a36Sopenharmony_ci enum bd9995x_fields id; 64262306a36Sopenharmony_ci u16 value; 64362306a36Sopenharmony_ci } init_data[] = { 64462306a36Sopenharmony_ci /* Enable the charging trigger after SDP charger attached */ 64562306a36Sopenharmony_ci {F_SDP_CHG_TRIG_EN, 1}, 64662306a36Sopenharmony_ci /* Enable charging trigger after SDP charger attached */ 64762306a36Sopenharmony_ci {F_SDP_CHG_TRIG, 1}, 64862306a36Sopenharmony_ci /* Disable charging trigger by BC1.2 detection */ 64962306a36Sopenharmony_ci {F_VBUS_BC_DISEN, 1}, 65062306a36Sopenharmony_ci /* Disable charging trigger by BC1.2 detection */ 65162306a36Sopenharmony_ci {F_VCC_BC_DISEN, 1}, 65262306a36Sopenharmony_ci /* Disable automatic limitation of the input current */ 65362306a36Sopenharmony_ci {F_ILIM_AUTO_DISEN, 1}, 65462306a36Sopenharmony_ci /* Select current limitation when SDP charger attached*/ 65562306a36Sopenharmony_ci {F_SDP_500_SEL, 1}, 65662306a36Sopenharmony_ci /* Select current limitation when DCP charger attached */ 65762306a36Sopenharmony_ci {F_DCP_2500_SEL, 1}, 65862306a36Sopenharmony_ci {F_VSYSREG_SET, id->vsysreg_set}, 65962306a36Sopenharmony_ci /* Activate USB charging and DC/DC converter */ 66062306a36Sopenharmony_ci {F_USB_SUS, 0}, 66162306a36Sopenharmony_ci /* DCDC clock: 1200 kHz*/ 66262306a36Sopenharmony_ci {F_DCDC_CLK_SEL, 3}, 66362306a36Sopenharmony_ci /* Enable charging */ 66462306a36Sopenharmony_ci {F_CHG_EN, 1}, 66562306a36Sopenharmony_ci /* Disable Input current Limit setting voltage measurement */ 66662306a36Sopenharmony_ci {F_EXTIADPEN, 0}, 66762306a36Sopenharmony_ci /* Disable input current limiting */ 66862306a36Sopenharmony_ci {F_VSYS_PRIORITY, 1}, 66962306a36Sopenharmony_ci {F_IBUS_LIM_SET, id->ibus_lim_set}, 67062306a36Sopenharmony_ci {F_ICC_LIM_SET, id->icc_lim_set}, 67162306a36Sopenharmony_ci /* Charge Termination Current Setting to 0*/ 67262306a36Sopenharmony_ci {F_ITERM_SET, id->iterm_set}, 67362306a36Sopenharmony_ci /* Trickle-charge Current Setting */ 67462306a36Sopenharmony_ci {F_ITRICH_SET, id->itrich_set}, 67562306a36Sopenharmony_ci /* Pre-charge Current setting */ 67662306a36Sopenharmony_ci {F_IPRECH_SET, id->iprech_set}, 67762306a36Sopenharmony_ci /* Fast Charge Current for constant current phase */ 67862306a36Sopenharmony_ci {F_ICHG_SET, id->ichg_set}, 67962306a36Sopenharmony_ci /* Fast Charge Voltage Regulation Setting */ 68062306a36Sopenharmony_ci {F_VFASTCHG_REG_SET1, id->vfastchg_reg_set1}, 68162306a36Sopenharmony_ci /* Set Pre-charge Voltage Threshold for trickle charging. */ 68262306a36Sopenharmony_ci {F_VPRECHG_TH_SET, id->vprechg_th_set}, 68362306a36Sopenharmony_ci {F_VRECHG_SET, id->vrechg_set}, 68462306a36Sopenharmony_ci {F_VBATOVP_SET, id->vbatovp_set}, 68562306a36Sopenharmony_ci /* Reverse buck boost voltage Setting */ 68662306a36Sopenharmony_ci {F_VRBOOST_SET, 0}, 68762306a36Sopenharmony_ci /* Disable fast-charging watchdog */ 68862306a36Sopenharmony_ci {F_WDT_FST, 0}, 68962306a36Sopenharmony_ci /* Disable pre-charging watchdog */ 69062306a36Sopenharmony_ci {F_WDT_PRE, 0}, 69162306a36Sopenharmony_ci /* Power save off */ 69262306a36Sopenharmony_ci {F_POWER_SAVE_MODE, 0}, 69362306a36Sopenharmony_ci {F_INT1_SET, INT1_ALL}, 69462306a36Sopenharmony_ci {F_INT2_SET, INT2_ALL}, 69562306a36Sopenharmony_ci {F_INT3_SET, INT3_ALL}, 69662306a36Sopenharmony_ci {F_INT4_SET, INT4_ALL}, 69762306a36Sopenharmony_ci {F_INT5_SET, INT5_ALL}, 69862306a36Sopenharmony_ci {F_INT6_SET, INT6_ALL}, 69962306a36Sopenharmony_ci {F_INT7_SET, INT7_ALL}, 70062306a36Sopenharmony_ci }; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* 70362306a36Sopenharmony_ci * Currently we initialize charger to a known state at startup. 70462306a36Sopenharmony_ci * If we want to allow for example the boot code to initialize 70562306a36Sopenharmony_ci * charger we should get rid of this. 70662306a36Sopenharmony_ci */ 70762306a36Sopenharmony_ci ret = __bd9995x_chip_reset(bd); 70862306a36Sopenharmony_ci if (ret < 0) 70962306a36Sopenharmony_ci return ret; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci /* Initialize currents/voltages and other parameters */ 71262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(init_data); i++) { 71362306a36Sopenharmony_ci ret = regmap_field_write(bd->rmap_fields[init_data[i].id], 71462306a36Sopenharmony_ci init_data[i].value); 71562306a36Sopenharmony_ci if (ret) { 71662306a36Sopenharmony_ci dev_err(bd->dev, "failed to initialize charger (%d)\n", 71762306a36Sopenharmony_ci ret); 71862306a36Sopenharmony_ci return ret; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci ret = bd9995x_get_chip_state(bd, &state); 72362306a36Sopenharmony_ci if (ret < 0) 72462306a36Sopenharmony_ci return ret; 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci mutex_lock(&bd->lock); 72762306a36Sopenharmony_ci bd->state = state; 72862306a36Sopenharmony_ci mutex_unlock(&bd->lock); 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci return 0; 73162306a36Sopenharmony_ci} 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_cistatic enum power_supply_property bd9995x_power_supply_props[] = { 73462306a36Sopenharmony_ci POWER_SUPPLY_PROP_MANUFACTURER, 73562306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 73662306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 73762306a36Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 73862306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_AVG, 73962306a36Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 74062306a36Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 74162306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, 74262306a36Sopenharmony_ci /* Battery props we access through charger */ 74362306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 74462306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 74562306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 74662306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TYPE, 74762306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 74862306a36Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, 74962306a36Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 75062306a36Sopenharmony_ci POWER_SUPPLY_PROP_MODEL_NAME, 75162306a36Sopenharmony_ci}; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_cistatic const struct power_supply_desc bd9995x_power_supply_desc = { 75462306a36Sopenharmony_ci .name = "bd9995x-charger", 75562306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 75662306a36Sopenharmony_ci .properties = bd9995x_power_supply_props, 75762306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(bd9995x_power_supply_props), 75862306a36Sopenharmony_ci .get_property = bd9995x_power_supply_get_property, 75962306a36Sopenharmony_ci}; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci/* 76262306a36Sopenharmony_ci * Limit configurations for vbus-input-current and vcc-vacp-input-current 76362306a36Sopenharmony_ci * Minimum limit is 0 uA. Max is 511 * 32000 uA = 16352000 uA. This is 76462306a36Sopenharmony_ci * configured by writing a register so that each increment in register 76562306a36Sopenharmony_ci * value equals to 32000 uA limit increment. 76662306a36Sopenharmony_ci * 76762306a36Sopenharmony_ci * Eg, value 0x0 is limit 0, value 0x1 is limit 32000, ... 76862306a36Sopenharmony_ci * Describe the setting in linear_range table. 76962306a36Sopenharmony_ci */ 77062306a36Sopenharmony_cistatic const struct linear_range input_current_limit_ranges[] = { 77162306a36Sopenharmony_ci LINEAR_RANGE(0, 0x0, 0x1ff, 32000), 77262306a36Sopenharmony_ci}; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci/* Possible trickle, pre-charging and termination current values */ 77562306a36Sopenharmony_cistatic const struct linear_range charging_current_ranges[] = { 77662306a36Sopenharmony_ci LINEAR_RANGE(0, 0x0, 0x10, 64000), 77762306a36Sopenharmony_ci LINEAR_RANGE(1024000, 0x11, 0x1f, 0), 77862306a36Sopenharmony_ci}; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci/* 78162306a36Sopenharmony_ci * Fast charging voltage regulation, starting re-charging limit 78262306a36Sopenharmony_ci * and battery over voltage protection have same possible values 78362306a36Sopenharmony_ci */ 78462306a36Sopenharmony_cistatic const struct linear_range charge_voltage_regulation_ranges[] = { 78562306a36Sopenharmony_ci LINEAR_RANGE(2560000, 0, 0xA0, 0), 78662306a36Sopenharmony_ci LINEAR_RANGE(2560000, 0xA0, 0x4B0, 16000), 78762306a36Sopenharmony_ci LINEAR_RANGE(19200000, 0x4B0, 0x7FF, 0), 78862306a36Sopenharmony_ci}; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci/* Possible VSYS voltage regulation values */ 79162306a36Sopenharmony_cistatic const struct linear_range vsys_voltage_regulation_ranges[] = { 79262306a36Sopenharmony_ci LINEAR_RANGE(2560000, 0, 0x28, 0), 79362306a36Sopenharmony_ci LINEAR_RANGE(2560000, 0x28, 0x12C, 64000), 79462306a36Sopenharmony_ci LINEAR_RANGE(19200000, 0x12C, 0x1FF, 0), 79562306a36Sopenharmony_ci}; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci/* Possible settings for switching from trickle to pre-charging limits */ 79862306a36Sopenharmony_cistatic const struct linear_range trickle_to_pre_threshold_ranges[] = { 79962306a36Sopenharmony_ci LINEAR_RANGE(2048000, 0, 0x20, 0), 80062306a36Sopenharmony_ci LINEAR_RANGE(2048000, 0x20, 0x12C, 64000), 80162306a36Sopenharmony_ci LINEAR_RANGE(19200000, 0x12C, 0x1FF, 0), 80262306a36Sopenharmony_ci}; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci/* Possible current values for fast-charging constant current phase */ 80562306a36Sopenharmony_cistatic const struct linear_range fast_charge_current_ranges[] = { 80662306a36Sopenharmony_ci LINEAR_RANGE(0, 0, 0xFF, 64000), 80762306a36Sopenharmony_ci}; 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistruct battery_init { 81062306a36Sopenharmony_ci const char *name; 81162306a36Sopenharmony_ci int *info_data; 81262306a36Sopenharmony_ci const struct linear_range *range; 81362306a36Sopenharmony_ci int ranges; 81462306a36Sopenharmony_ci u16 *data; 81562306a36Sopenharmony_ci}; 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_cistruct dt_init { 81862306a36Sopenharmony_ci char *prop; 81962306a36Sopenharmony_ci const struct linear_range *range; 82062306a36Sopenharmony_ci int ranges; 82162306a36Sopenharmony_ci u16 *data; 82262306a36Sopenharmony_ci}; 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic int bd9995x_fw_probe(struct bd9995x_device *bd) 82562306a36Sopenharmony_ci{ 82662306a36Sopenharmony_ci int ret; 82762306a36Sopenharmony_ci struct power_supply_battery_info *info; 82862306a36Sopenharmony_ci u32 property; 82962306a36Sopenharmony_ci int i; 83062306a36Sopenharmony_ci int regval; 83162306a36Sopenharmony_ci bool found; 83262306a36Sopenharmony_ci struct bd9995x_init_data *init = &bd->init_data; 83362306a36Sopenharmony_ci struct battery_init battery_inits[] = { 83462306a36Sopenharmony_ci { 83562306a36Sopenharmony_ci .name = "trickle-charging current", 83662306a36Sopenharmony_ci .range = &charging_current_ranges[0], 83762306a36Sopenharmony_ci .ranges = 2, 83862306a36Sopenharmony_ci .data = &init->itrich_set, 83962306a36Sopenharmony_ci }, { 84062306a36Sopenharmony_ci .name = "pre-charging current", 84162306a36Sopenharmony_ci .range = &charging_current_ranges[0], 84262306a36Sopenharmony_ci .ranges = 2, 84362306a36Sopenharmony_ci .data = &init->iprech_set, 84462306a36Sopenharmony_ci }, { 84562306a36Sopenharmony_ci .name = "pre-to-trickle charge voltage threshold", 84662306a36Sopenharmony_ci .range = &trickle_to_pre_threshold_ranges[0], 84762306a36Sopenharmony_ci .ranges = 2, 84862306a36Sopenharmony_ci .data = &init->vprechg_th_set, 84962306a36Sopenharmony_ci }, { 85062306a36Sopenharmony_ci .name = "charging termination current", 85162306a36Sopenharmony_ci .range = &charging_current_ranges[0], 85262306a36Sopenharmony_ci .ranges = 2, 85362306a36Sopenharmony_ci .data = &init->iterm_set, 85462306a36Sopenharmony_ci }, { 85562306a36Sopenharmony_ci .name = "charging re-start voltage", 85662306a36Sopenharmony_ci .range = &charge_voltage_regulation_ranges[0], 85762306a36Sopenharmony_ci .ranges = 2, 85862306a36Sopenharmony_ci .data = &init->vrechg_set, 85962306a36Sopenharmony_ci }, { 86062306a36Sopenharmony_ci .name = "battery overvoltage limit", 86162306a36Sopenharmony_ci .range = &charge_voltage_regulation_ranges[0], 86262306a36Sopenharmony_ci .ranges = 2, 86362306a36Sopenharmony_ci .data = &init->vbatovp_set, 86462306a36Sopenharmony_ci }, { 86562306a36Sopenharmony_ci .name = "fast-charging max current", 86662306a36Sopenharmony_ci .range = &fast_charge_current_ranges[0], 86762306a36Sopenharmony_ci .ranges = 1, 86862306a36Sopenharmony_ci .data = &init->ichg_set, 86962306a36Sopenharmony_ci }, { 87062306a36Sopenharmony_ci .name = "fast-charging voltage", 87162306a36Sopenharmony_ci .range = &charge_voltage_regulation_ranges[0], 87262306a36Sopenharmony_ci .ranges = 2, 87362306a36Sopenharmony_ci .data = &init->vfastchg_reg_set1, 87462306a36Sopenharmony_ci }, 87562306a36Sopenharmony_ci }; 87662306a36Sopenharmony_ci struct dt_init props[] = { 87762306a36Sopenharmony_ci { 87862306a36Sopenharmony_ci .prop = "rohm,vsys-regulation-microvolt", 87962306a36Sopenharmony_ci .range = &vsys_voltage_regulation_ranges[0], 88062306a36Sopenharmony_ci .ranges = 2, 88162306a36Sopenharmony_ci .data = &init->vsysreg_set, 88262306a36Sopenharmony_ci }, { 88362306a36Sopenharmony_ci .prop = "rohm,vbus-input-current-limit-microamp", 88462306a36Sopenharmony_ci .range = &input_current_limit_ranges[0], 88562306a36Sopenharmony_ci .ranges = 1, 88662306a36Sopenharmony_ci .data = &init->ibus_lim_set, 88762306a36Sopenharmony_ci }, { 88862306a36Sopenharmony_ci .prop = "rohm,vcc-input-current-limit-microamp", 88962306a36Sopenharmony_ci .range = &input_current_limit_ranges[0], 89062306a36Sopenharmony_ci .ranges = 1, 89162306a36Sopenharmony_ci .data = &init->icc_lim_set, 89262306a36Sopenharmony_ci }, 89362306a36Sopenharmony_ci }; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci /* 89662306a36Sopenharmony_ci * The power_supply_get_battery_info() does not support getting values 89762306a36Sopenharmony_ci * from ACPI. Let's fix it if ACPI is required here. 89862306a36Sopenharmony_ci */ 89962306a36Sopenharmony_ci ret = power_supply_get_battery_info(bd->charger, &info); 90062306a36Sopenharmony_ci if (ret < 0) 90162306a36Sopenharmony_ci return ret; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci /* Put pointers to the generic battery info */ 90462306a36Sopenharmony_ci battery_inits[0].info_data = &info->tricklecharge_current_ua; 90562306a36Sopenharmony_ci battery_inits[1].info_data = &info->precharge_current_ua; 90662306a36Sopenharmony_ci battery_inits[2].info_data = &info->precharge_voltage_max_uv; 90762306a36Sopenharmony_ci battery_inits[3].info_data = &info->charge_term_current_ua; 90862306a36Sopenharmony_ci battery_inits[4].info_data = &info->charge_restart_voltage_uv; 90962306a36Sopenharmony_ci battery_inits[5].info_data = &info->overvoltage_limit_uv; 91062306a36Sopenharmony_ci battery_inits[6].info_data = &info->constant_charge_current_max_ua; 91162306a36Sopenharmony_ci battery_inits[7].info_data = &info->constant_charge_voltage_max_uv; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(battery_inits); i++) { 91462306a36Sopenharmony_ci int val = *battery_inits[i].info_data; 91562306a36Sopenharmony_ci const struct linear_range *range = battery_inits[i].range; 91662306a36Sopenharmony_ci int ranges = battery_inits[i].ranges; 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci if (val == -EINVAL) 91962306a36Sopenharmony_ci continue; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci ret = linear_range_get_selector_low_array(range, ranges, val, 92262306a36Sopenharmony_ci ®val, &found); 92362306a36Sopenharmony_ci if (ret) { 92462306a36Sopenharmony_ci dev_err(bd->dev, "Unsupported value for %s\n", 92562306a36Sopenharmony_ci battery_inits[i].name); 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_ci power_supply_put_battery_info(bd->charger, info); 92862306a36Sopenharmony_ci return -EINVAL; 92962306a36Sopenharmony_ci } 93062306a36Sopenharmony_ci if (!found) { 93162306a36Sopenharmony_ci dev_warn(bd->dev, 93262306a36Sopenharmony_ci "Unsupported value for %s - using smaller\n", 93362306a36Sopenharmony_ci battery_inits[i].name); 93462306a36Sopenharmony_ci } 93562306a36Sopenharmony_ci *(battery_inits[i].data) = regval; 93662306a36Sopenharmony_ci } 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci power_supply_put_battery_info(bd->charger, info); 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(props); i++) { 94162306a36Sopenharmony_ci ret = device_property_read_u32(bd->dev, props[i].prop, 94262306a36Sopenharmony_ci &property); 94362306a36Sopenharmony_ci if (ret < 0) { 94462306a36Sopenharmony_ci dev_err(bd->dev, "failed to read %s", props[i].prop); 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci return ret; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci ret = linear_range_get_selector_low_array(props[i].range, 95062306a36Sopenharmony_ci props[i].ranges, 95162306a36Sopenharmony_ci property, ®val, 95262306a36Sopenharmony_ci &found); 95362306a36Sopenharmony_ci if (ret) { 95462306a36Sopenharmony_ci dev_err(bd->dev, "Unsupported value for '%s'\n", 95562306a36Sopenharmony_ci props[i].prop); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci return -EINVAL; 95862306a36Sopenharmony_ci } 95962306a36Sopenharmony_ci 96062306a36Sopenharmony_ci if (!found) { 96162306a36Sopenharmony_ci dev_warn(bd->dev, 96262306a36Sopenharmony_ci "Unsupported value for '%s' - using smaller\n", 96362306a36Sopenharmony_ci props[i].prop); 96462306a36Sopenharmony_ci } 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci *(props[i].data) = regval; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci return 0; 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic void bd9995x_chip_reset(void *bd) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci __bd9995x_chip_reset(bd); 97562306a36Sopenharmony_ci} 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_cistatic int bd9995x_probe(struct i2c_client *client) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci struct device *dev = &client->dev; 98062306a36Sopenharmony_ci struct bd9995x_device *bd; 98162306a36Sopenharmony_ci struct power_supply_config psy_cfg = {}; 98262306a36Sopenharmony_ci int ret; 98362306a36Sopenharmony_ci int i; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci bd = devm_kzalloc(dev, sizeof(*bd), GFP_KERNEL); 98662306a36Sopenharmony_ci if (!bd) 98762306a36Sopenharmony_ci return -ENOMEM; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci bd->client = client; 99062306a36Sopenharmony_ci bd->dev = dev; 99162306a36Sopenharmony_ci psy_cfg.drv_data = bd; 99262306a36Sopenharmony_ci psy_cfg.of_node = dev->of_node; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci mutex_init(&bd->lock); 99562306a36Sopenharmony_ci 99662306a36Sopenharmony_ci bd->rmap = devm_regmap_init_i2c(client, &bd9995x_regmap_config); 99762306a36Sopenharmony_ci if (IS_ERR(bd->rmap)) { 99862306a36Sopenharmony_ci dev_err(dev, "Failed to setup register access via i2c\n"); 99962306a36Sopenharmony_ci return PTR_ERR(bd->rmap); 100062306a36Sopenharmony_ci } 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bd9995x_reg_fields); i++) { 100362306a36Sopenharmony_ci const struct reg_field *reg_fields = bd9995x_reg_fields; 100462306a36Sopenharmony_ci 100562306a36Sopenharmony_ci bd->rmap_fields[i] = devm_regmap_field_alloc(dev, bd->rmap, 100662306a36Sopenharmony_ci reg_fields[i]); 100762306a36Sopenharmony_ci if (IS_ERR(bd->rmap_fields[i])) { 100862306a36Sopenharmony_ci dev_err(dev, "cannot allocate regmap field\n"); 100962306a36Sopenharmony_ci return PTR_ERR(bd->rmap_fields[i]); 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci } 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci i2c_set_clientdata(client, bd); 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_CHIP_ID], &bd->chip_id); 101662306a36Sopenharmony_ci if (ret) { 101762306a36Sopenharmony_ci dev_err(dev, "Cannot read chip ID.\n"); 101862306a36Sopenharmony_ci return ret; 101962306a36Sopenharmony_ci } 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (bd->chip_id != BD99954_ID) { 102262306a36Sopenharmony_ci dev_err(dev, "Chip with ID=0x%x, not supported!\n", 102362306a36Sopenharmony_ci bd->chip_id); 102462306a36Sopenharmony_ci return -ENODEV; 102562306a36Sopenharmony_ci } 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_CHIP_REV], &bd->chip_rev); 102862306a36Sopenharmony_ci if (ret) { 102962306a36Sopenharmony_ci dev_err(dev, "Cannot read revision.\n"); 103062306a36Sopenharmony_ci return ret; 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci dev_info(bd->dev, "Found BD99954 chip rev %d\n", bd->chip_rev); 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci /* 103662306a36Sopenharmony_ci * We need to init the psy before we can call 103762306a36Sopenharmony_ci * power_supply_get_battery_info() for it 103862306a36Sopenharmony_ci */ 103962306a36Sopenharmony_ci bd->charger = devm_power_supply_register(bd->dev, 104062306a36Sopenharmony_ci &bd9995x_power_supply_desc, 104162306a36Sopenharmony_ci &psy_cfg); 104262306a36Sopenharmony_ci if (IS_ERR(bd->charger)) { 104362306a36Sopenharmony_ci dev_err(dev, "Failed to register power supply\n"); 104462306a36Sopenharmony_ci return PTR_ERR(bd->charger); 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_ci ret = bd9995x_fw_probe(bd); 104862306a36Sopenharmony_ci if (ret < 0) { 104962306a36Sopenharmony_ci dev_err(dev, "Cannot read device properties.\n"); 105062306a36Sopenharmony_ci return ret; 105162306a36Sopenharmony_ci } 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci ret = bd9995x_hw_init(bd); 105462306a36Sopenharmony_ci if (ret < 0) { 105562306a36Sopenharmony_ci dev_err(dev, "Cannot initialize the chip.\n"); 105662306a36Sopenharmony_ci return ret; 105762306a36Sopenharmony_ci } 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_ci ret = devm_add_action_or_reset(dev, bd9995x_chip_reset, bd); 106062306a36Sopenharmony_ci if (ret) 106162306a36Sopenharmony_ci return ret; 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return devm_request_threaded_irq(dev, client->irq, NULL, 106462306a36Sopenharmony_ci bd9995x_irq_handler_thread, 106562306a36Sopenharmony_ci IRQF_TRIGGER_LOW | IRQF_ONESHOT, 106662306a36Sopenharmony_ci BD9995X_IRQ_PIN, bd); 106762306a36Sopenharmony_ci} 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_cistatic const struct of_device_id bd9995x_of_match[] = { 107062306a36Sopenharmony_ci { .compatible = "rohm,bd99954", }, 107162306a36Sopenharmony_ci { } 107262306a36Sopenharmony_ci}; 107362306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, bd9995x_of_match); 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_cistatic struct i2c_driver bd9995x_driver = { 107662306a36Sopenharmony_ci .driver = { 107762306a36Sopenharmony_ci .name = "bd9995x-charger", 107862306a36Sopenharmony_ci .of_match_table = bd9995x_of_match, 107962306a36Sopenharmony_ci }, 108062306a36Sopenharmony_ci .probe = bd9995x_probe, 108162306a36Sopenharmony_ci}; 108262306a36Sopenharmony_cimodule_i2c_driver(bd9995x_driver); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ciMODULE_AUTHOR("Laine Markus <markus.laine@fi.rohmeurope.com>"); 108562306a36Sopenharmony_ciMODULE_DESCRIPTION("ROHM BD99954 charger driver"); 108662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1087