18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * ROHM BD99954 charger driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2020 Rohm Semiconductors 68c2ecf20Sopenharmony_ci * Originally written by: 78c2ecf20Sopenharmony_ci * Mikko Mutanen <mikko.mutanen@fi.rohmeurope.com> 88c2ecf20Sopenharmony_ci * Markus Laine <markus.laine@fi.rohmeurope.com> 98c2ecf20Sopenharmony_ci * Bugs added by: 108c2ecf20Sopenharmony_ci * Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com> 118c2ecf20Sopenharmony_ci */ 128c2ecf20Sopenharmony_ci 138c2ecf20Sopenharmony_ci/* 148c2ecf20Sopenharmony_ci * The battery charging profile of BD99954. 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Curve (1) represents charging current. 178c2ecf20Sopenharmony_ci * Curve (2) represents battery voltage. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * The BD99954 data sheet divides charging to three phases. 208c2ecf20Sopenharmony_ci * a) Trickle-charge with constant current (8). 218c2ecf20Sopenharmony_ci * b) pre-charge with constant current (6) 228c2ecf20Sopenharmony_ci * c) fast-charge, first with constant current (5) phase. After 238c2ecf20Sopenharmony_ci * the battery voltage has reached target level (4) we have constant 248c2ecf20Sopenharmony_ci * voltage phase until charging current has dropped to termination 258c2ecf20Sopenharmony_ci * level (7) 268c2ecf20Sopenharmony_ci * 278c2ecf20Sopenharmony_ci * V ^ ^ I 288c2ecf20Sopenharmony_ci * . . 298c2ecf20Sopenharmony_ci * . . 308c2ecf20Sopenharmony_ci *(4)` `.` ` ` ` ` ` ` ` ` ` ` ` ` ` ----------------------------. 318c2ecf20Sopenharmony_ci * . :/ . 328c2ecf20Sopenharmony_ci * . o----+/:/ ` ` ` ` ` ` ` ` ` ` ` ` `.` ` (5) 338c2ecf20Sopenharmony_ci * . + :: + . 348c2ecf20Sopenharmony_ci * . + /- -- . 358c2ecf20Sopenharmony_ci * . +`/- + . 368c2ecf20Sopenharmony_ci * . o/- -: . 378c2ecf20Sopenharmony_ci * . .s. +` . 388c2ecf20Sopenharmony_ci * . .--+ `/ . 398c2ecf20Sopenharmony_ci * . ..`` + .: . 408c2ecf20Sopenharmony_ci * . -` + -- . 418c2ecf20Sopenharmony_ci * . (2) ...`` + :- . 428c2ecf20Sopenharmony_ci * . ...`` + -: . 438c2ecf20Sopenharmony_ci *(3)` `.`."" ` ` ` `+-------- ` ` ` ` ` ` `.:` ` ` ` ` ` ` ` ` .` ` (6) 448c2ecf20Sopenharmony_ci * . + `:. . 458c2ecf20Sopenharmony_ci * . + -: . 468c2ecf20Sopenharmony_ci * . + -:. . 478c2ecf20Sopenharmony_ci * . + .--. . 488c2ecf20Sopenharmony_ci * . (1) + `.+` ` ` `.` ` (7) 498c2ecf20Sopenharmony_ci * -..............` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` ` + ` ` ` .` ` (8) 508c2ecf20Sopenharmony_ci * . + - 518c2ecf20Sopenharmony_ci * -------------------------------------------------+++++++++--> 528c2ecf20Sopenharmony_ci * | trickle | pre | fast | 538c2ecf20Sopenharmony_ci * 548c2ecf20Sopenharmony_ci * Details of DT properties for different limits can be found from BD99954 558c2ecf20Sopenharmony_ci * device tree binding documentation. 568c2ecf20Sopenharmony_ci */ 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#include <linux/delay.h> 598c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h> 608c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 618c2ecf20Sopenharmony_ci#include <linux/i2c.h> 628c2ecf20Sopenharmony_ci#include <linux/kernel.h> 638c2ecf20Sopenharmony_ci#include <linux/linear_range.h> 648c2ecf20Sopenharmony_ci#include <linux/module.h> 658c2ecf20Sopenharmony_ci#include <linux/mod_devicetable.h> 668c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 678c2ecf20Sopenharmony_ci#include <linux/property.h> 688c2ecf20Sopenharmony_ci#include <linux/regmap.h> 698c2ecf20Sopenharmony_ci#include <linux/types.h> 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#include "bd99954-charger.h" 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistruct battery_data { 748c2ecf20Sopenharmony_ci u16 precharge_current; /* Trickle-charge Current */ 758c2ecf20Sopenharmony_ci u16 fc_reg_voltage; /* Fast Charging Regulation Voltage */ 768c2ecf20Sopenharmony_ci u16 voltage_min; 778c2ecf20Sopenharmony_ci u16 voltage_max; 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* Initial field values, converted to initial register values */ 818c2ecf20Sopenharmony_cistruct bd9995x_init_data { 828c2ecf20Sopenharmony_ci u16 vsysreg_set; /* VSYS Regulation Setting */ 838c2ecf20Sopenharmony_ci u16 ibus_lim_set; /* VBUS input current limitation */ 848c2ecf20Sopenharmony_ci u16 icc_lim_set; /* VCC/VACP Input Current Limit Setting */ 858c2ecf20Sopenharmony_ci u16 itrich_set; /* Trickle-charge Current Setting */ 868c2ecf20Sopenharmony_ci u16 iprech_set; /* Pre-Charge Current Setting */ 878c2ecf20Sopenharmony_ci u16 ichg_set; /* Fast-Charge constant current */ 888c2ecf20Sopenharmony_ci u16 vfastchg_reg_set1; /* Fast Charging Regulation Voltage */ 898c2ecf20Sopenharmony_ci u16 vprechg_th_set; /* Pre-charge Voltage Threshold Setting */ 908c2ecf20Sopenharmony_ci u16 vrechg_set; /* Re-charge Battery Voltage Setting */ 918c2ecf20Sopenharmony_ci u16 vbatovp_set; /* Battery Over Voltage Threshold Setting */ 928c2ecf20Sopenharmony_ci u16 iterm_set; /* Charging termination current */ 938c2ecf20Sopenharmony_ci}; 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_cistruct bd9995x_state { 968c2ecf20Sopenharmony_ci u8 online; 978c2ecf20Sopenharmony_ci u16 chgstm_status; 988c2ecf20Sopenharmony_ci u16 vbat_vsys_status; 998c2ecf20Sopenharmony_ci u16 vbus_vcc_status; 1008c2ecf20Sopenharmony_ci}; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistruct bd9995x_device { 1038c2ecf20Sopenharmony_ci struct i2c_client *client; 1048c2ecf20Sopenharmony_ci struct device *dev; 1058c2ecf20Sopenharmony_ci struct power_supply *charger; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci struct regmap *rmap; 1088c2ecf20Sopenharmony_ci struct regmap_field *rmap_fields[F_MAX_FIELDS]; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci int chip_id; 1118c2ecf20Sopenharmony_ci int chip_rev; 1128c2ecf20Sopenharmony_ci struct bd9995x_init_data init_data; 1138c2ecf20Sopenharmony_ci struct bd9995x_state state; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci struct mutex lock; /* Protect state data */ 1168c2ecf20Sopenharmony_ci}; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_cistatic const struct regmap_range bd9995x_readonly_reg_ranges[] = { 1198c2ecf20Sopenharmony_ci regmap_reg_range(CHGSTM_STATUS, SEL_ILIM_VAL), 1208c2ecf20Sopenharmony_ci regmap_reg_range(IOUT_DACIN_VAL, IOUT_DACIN_VAL), 1218c2ecf20Sopenharmony_ci regmap_reg_range(VCC_UCD_STATUS, VCC_IDD_STATUS), 1228c2ecf20Sopenharmony_ci regmap_reg_range(VBUS_UCD_STATUS, VBUS_IDD_STATUS), 1238c2ecf20Sopenharmony_ci regmap_reg_range(CHIP_ID, CHIP_REV), 1248c2ecf20Sopenharmony_ci regmap_reg_range(SYSTEM_STATUS, SYSTEM_STATUS), 1258c2ecf20Sopenharmony_ci regmap_reg_range(IBATP_VAL, VBAT_AVE_VAL), 1268c2ecf20Sopenharmony_ci regmap_reg_range(VTH_VAL, EXTIADP_AVE_VAL), 1278c2ecf20Sopenharmony_ci}; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_cistatic const struct regmap_access_table bd9995x_writeable_regs = { 1308c2ecf20Sopenharmony_ci .no_ranges = bd9995x_readonly_reg_ranges, 1318c2ecf20Sopenharmony_ci .n_no_ranges = ARRAY_SIZE(bd9995x_readonly_reg_ranges), 1328c2ecf20Sopenharmony_ci}; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic const struct regmap_range bd9995x_volatile_reg_ranges[] = { 1358c2ecf20Sopenharmony_ci regmap_reg_range(CHGSTM_STATUS, WDT_STATUS), 1368c2ecf20Sopenharmony_ci regmap_reg_range(VCC_UCD_STATUS, VCC_IDD_STATUS), 1378c2ecf20Sopenharmony_ci regmap_reg_range(VBUS_UCD_STATUS, VBUS_IDD_STATUS), 1388c2ecf20Sopenharmony_ci regmap_reg_range(INT0_STATUS, INT7_STATUS), 1398c2ecf20Sopenharmony_ci regmap_reg_range(SYSTEM_STATUS, SYSTEM_CTRL_SET), 1408c2ecf20Sopenharmony_ci regmap_reg_range(IBATP_VAL, EXTIADP_AVE_VAL), /* Measurement regs */ 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic const struct regmap_access_table bd9995x_volatile_regs = { 1448c2ecf20Sopenharmony_ci .yes_ranges = bd9995x_volatile_reg_ranges, 1458c2ecf20Sopenharmony_ci .n_yes_ranges = ARRAY_SIZE(bd9995x_volatile_reg_ranges), 1468c2ecf20Sopenharmony_ci}; 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic const struct regmap_range_cfg regmap_range_cfg[] = { 1498c2ecf20Sopenharmony_ci { 1508c2ecf20Sopenharmony_ci .selector_reg = MAP_SET, 1518c2ecf20Sopenharmony_ci .selector_mask = 0xFFFF, 1528c2ecf20Sopenharmony_ci .selector_shift = 0, 1538c2ecf20Sopenharmony_ci .window_start = 0, 1548c2ecf20Sopenharmony_ci .window_len = 0x100, 1558c2ecf20Sopenharmony_ci .range_min = 0 * 0x100, 1568c2ecf20Sopenharmony_ci .range_max = 3 * 0x100, 1578c2ecf20Sopenharmony_ci }, 1588c2ecf20Sopenharmony_ci}; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_cistatic const struct regmap_config bd9995x_regmap_config = { 1618c2ecf20Sopenharmony_ci .reg_bits = 8, 1628c2ecf20Sopenharmony_ci .val_bits = 16, 1638c2ecf20Sopenharmony_ci .reg_stride = 1, 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci .max_register = 3 * 0x100, 1668c2ecf20Sopenharmony_ci .cache_type = REGCACHE_RBTREE, 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci .ranges = regmap_range_cfg, 1698c2ecf20Sopenharmony_ci .num_ranges = ARRAY_SIZE(regmap_range_cfg), 1708c2ecf20Sopenharmony_ci .val_format_endian = REGMAP_ENDIAN_LITTLE, 1718c2ecf20Sopenharmony_ci .wr_table = &bd9995x_writeable_regs, 1728c2ecf20Sopenharmony_ci .volatile_table = &bd9995x_volatile_regs, 1738c2ecf20Sopenharmony_ci}; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cienum bd9995x_chrg_fault { 1768c2ecf20Sopenharmony_ci CHRG_FAULT_NORMAL, 1778c2ecf20Sopenharmony_ci CHRG_FAULT_INPUT, 1788c2ecf20Sopenharmony_ci CHRG_FAULT_THERMAL_SHUTDOWN, 1798c2ecf20Sopenharmony_ci CHRG_FAULT_TIMER_EXPIRED, 1808c2ecf20Sopenharmony_ci}; 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int bd9995x_get_prop_batt_health(struct bd9995x_device *bd) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci int ret, tmp; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_BATTEMP], &tmp); 1878c2ecf20Sopenharmony_ci if (ret) 1888c2ecf20Sopenharmony_ci return POWER_SUPPLY_HEALTH_UNKNOWN; 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci /* TODO: Check these against datasheet page 34 */ 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci switch (tmp) { 1938c2ecf20Sopenharmony_ci case ROOM: 1948c2ecf20Sopenharmony_ci return POWER_SUPPLY_HEALTH_GOOD; 1958c2ecf20Sopenharmony_ci case HOT1: 1968c2ecf20Sopenharmony_ci case HOT2: 1978c2ecf20Sopenharmony_ci case HOT3: 1988c2ecf20Sopenharmony_ci return POWER_SUPPLY_HEALTH_OVERHEAT; 1998c2ecf20Sopenharmony_ci case COLD1: 2008c2ecf20Sopenharmony_ci case COLD2: 2018c2ecf20Sopenharmony_ci return POWER_SUPPLY_HEALTH_COLD; 2028c2ecf20Sopenharmony_ci case TEMP_DIS: 2038c2ecf20Sopenharmony_ci case BATT_OPEN: 2048c2ecf20Sopenharmony_ci default: 2058c2ecf20Sopenharmony_ci return POWER_SUPPLY_HEALTH_UNKNOWN; 2068c2ecf20Sopenharmony_ci } 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic int bd9995x_get_prop_charge_type(struct bd9995x_device *bd) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci int ret, tmp; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_CHGSTM_STATE], &tmp); 2148c2ecf20Sopenharmony_ci if (ret) 2158c2ecf20Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_UNKNOWN; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci switch (tmp) { 2188c2ecf20Sopenharmony_ci case CHGSTM_TRICKLE_CHARGE: 2198c2ecf20Sopenharmony_ci case CHGSTM_PRE_CHARGE: 2208c2ecf20Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 2218c2ecf20Sopenharmony_ci case CHGSTM_FAST_CHARGE: 2228c2ecf20Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_FAST; 2238c2ecf20Sopenharmony_ci case CHGSTM_TOP_OFF: 2248c2ecf20Sopenharmony_ci case CHGSTM_DONE: 2258c2ecf20Sopenharmony_ci case CHGSTM_SUSPEND: 2268c2ecf20Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_NONE; 2278c2ecf20Sopenharmony_ci default: /* Rest of the states are error related, no charging */ 2288c2ecf20Sopenharmony_ci return POWER_SUPPLY_CHARGE_TYPE_NONE; 2298c2ecf20Sopenharmony_ci } 2308c2ecf20Sopenharmony_ci} 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_cistatic bool bd9995x_get_prop_batt_present(struct bd9995x_device *bd) 2338c2ecf20Sopenharmony_ci{ 2348c2ecf20Sopenharmony_ci int ret, tmp; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_BATTEMP], &tmp); 2378c2ecf20Sopenharmony_ci if (ret) 2388c2ecf20Sopenharmony_ci return false; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return tmp != BATT_OPEN; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic int bd9995x_get_prop_batt_voltage(struct bd9995x_device *bd) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci int ret, tmp; 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_VBAT_VAL], &tmp); 2488c2ecf20Sopenharmony_ci if (ret) 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci tmp = min(tmp, 19200); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci return tmp * 1000; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int bd9995x_get_prop_batt_current(struct bd9995x_device *bd) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci int ret, tmp; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_IBATP_VAL], &tmp); 2618c2ecf20Sopenharmony_ci if (ret) 2628c2ecf20Sopenharmony_ci return 0; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci return tmp * 1000; 2658c2ecf20Sopenharmony_ci} 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci#define DEFAULT_BATTERY_TEMPERATURE 250 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_cistatic int bd9995x_get_prop_batt_temp(struct bd9995x_device *bd) 2708c2ecf20Sopenharmony_ci{ 2718c2ecf20Sopenharmony_ci int ret, tmp; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_THERM_VAL], &tmp); 2748c2ecf20Sopenharmony_ci if (ret) 2758c2ecf20Sopenharmony_ci return DEFAULT_BATTERY_TEMPERATURE; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return (200 - tmp) * 10; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_cistatic int bd9995x_power_supply_get_property(struct power_supply *psy, 2818c2ecf20Sopenharmony_ci enum power_supply_property psp, 2828c2ecf20Sopenharmony_ci union power_supply_propval *val) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci int ret, tmp; 2858c2ecf20Sopenharmony_ci struct bd9995x_device *bd = power_supply_get_drvdata(psy); 2868c2ecf20Sopenharmony_ci struct bd9995x_state state; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci mutex_lock(&bd->lock); 2898c2ecf20Sopenharmony_ci state = bd->state; 2908c2ecf20Sopenharmony_ci mutex_unlock(&bd->lock); 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci switch (psp) { 2938c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 2948c2ecf20Sopenharmony_ci switch (state.chgstm_status) { 2958c2ecf20Sopenharmony_ci case CHGSTM_TRICKLE_CHARGE: 2968c2ecf20Sopenharmony_ci case CHGSTM_PRE_CHARGE: 2978c2ecf20Sopenharmony_ci case CHGSTM_FAST_CHARGE: 2988c2ecf20Sopenharmony_ci case CHGSTM_TOP_OFF: 2998c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 3008c2ecf20Sopenharmony_ci break; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci case CHGSTM_DONE: 3038c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_FULL; 3048c2ecf20Sopenharmony_ci break; 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci case CHGSTM_SUSPEND: 3078c2ecf20Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_1: 3088c2ecf20Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_2: 3098c2ecf20Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_3: 3108c2ecf20Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_4: 3118c2ecf20Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_5: 3128c2ecf20Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_6: 3138c2ecf20Sopenharmony_ci case CHGSTM_TEMPERATURE_ERROR_7: 3148c2ecf20Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_1: 3158c2ecf20Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_2: 3168c2ecf20Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_3: 3178c2ecf20Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_4: 3188c2ecf20Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_5: 3198c2ecf20Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_6: 3208c2ecf20Sopenharmony_ci case CHGSTM_THERMAL_SHUT_DOWN_7: 3218c2ecf20Sopenharmony_ci case CHGSTM_BATTERY_ERROR: 3228c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci default: 3268c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_UNKNOWN; 3278c2ecf20Sopenharmony_ci break; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_MANUFACTURER: 3328c2ecf20Sopenharmony_ci val->strval = BD9995X_MANUFACTURER; 3338c2ecf20Sopenharmony_ci break; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 3368c2ecf20Sopenharmony_ci val->intval = state.online; 3378c2ecf20Sopenharmony_ci break; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 3408c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_IBATP_VAL], &tmp); 3418c2ecf20Sopenharmony_ci if (ret) 3428c2ecf20Sopenharmony_ci return ret; 3438c2ecf20Sopenharmony_ci val->intval = tmp * 1000; 3448c2ecf20Sopenharmony_ci break; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_AVG: 3478c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_IBATP_AVE_VAL], &tmp); 3488c2ecf20Sopenharmony_ci if (ret) 3498c2ecf20Sopenharmony_ci return ret; 3508c2ecf20Sopenharmony_ci val->intval = tmp * 1000; 3518c2ecf20Sopenharmony_ci break; 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: 3548c2ecf20Sopenharmony_ci /* 3558c2ecf20Sopenharmony_ci * Currently the DT uses this property to give the 3568c2ecf20Sopenharmony_ci * target current for fast-charging constant current phase. 3578c2ecf20Sopenharmony_ci * I think it is correct in a sense. 3588c2ecf20Sopenharmony_ci * 3598c2ecf20Sopenharmony_ci * Yet, this prop we read and return here is the programmed 3608c2ecf20Sopenharmony_ci * safety limit for combined input currents. This feels 3618c2ecf20Sopenharmony_ci * also correct in a sense. 3628c2ecf20Sopenharmony_ci * 3638c2ecf20Sopenharmony_ci * However, this results a mismatch to DT value and value 3648c2ecf20Sopenharmony_ci * read from sysfs. 3658c2ecf20Sopenharmony_ci */ 3668c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_SEL_ILIM_VAL], &tmp); 3678c2ecf20Sopenharmony_ci if (ret) 3688c2ecf20Sopenharmony_ci return ret; 3698c2ecf20Sopenharmony_ci val->intval = tmp * 1000; 3708c2ecf20Sopenharmony_ci break; 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: 3738c2ecf20Sopenharmony_ci if (!state.online) { 3748c2ecf20Sopenharmony_ci val->intval = 0; 3758c2ecf20Sopenharmony_ci break; 3768c2ecf20Sopenharmony_ci } 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_VFASTCHG_REG_SET1], 3798c2ecf20Sopenharmony_ci &tmp); 3808c2ecf20Sopenharmony_ci if (ret) 3818c2ecf20Sopenharmony_ci return ret; 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci /* 3848c2ecf20Sopenharmony_ci * The actual range : 2560 to 19200 mV. No matter what the 3858c2ecf20Sopenharmony_ci * register says 3868c2ecf20Sopenharmony_ci */ 3878c2ecf20Sopenharmony_ci val->intval = clamp_val(tmp << 4, 2560, 19200); 3888c2ecf20Sopenharmony_ci val->intval *= 1000; 3898c2ecf20Sopenharmony_ci break; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: 3928c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_ITERM_SET], &tmp); 3938c2ecf20Sopenharmony_ci if (ret) 3948c2ecf20Sopenharmony_ci return ret; 3958c2ecf20Sopenharmony_ci /* Start step is 64 mA */ 3968c2ecf20Sopenharmony_ci val->intval = tmp << 6; 3978c2ecf20Sopenharmony_ci /* Maximum is 1024 mA - no matter what register says */ 3988c2ecf20Sopenharmony_ci val->intval = min(val->intval, 1024); 3998c2ecf20Sopenharmony_ci val->intval *= 1000; 4008c2ecf20Sopenharmony_ci break; 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_ci /* Battery properties which we access through charger */ 4038c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 4048c2ecf20Sopenharmony_ci val->intval = bd9995x_get_prop_batt_present(bd); 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_NOW: 4088c2ecf20Sopenharmony_ci val->intval = bd9995x_get_prop_batt_voltage(bd); 4098c2ecf20Sopenharmony_ci break; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_NOW: 4128c2ecf20Sopenharmony_ci val->intval = bd9995x_get_prop_batt_current(bd); 4138c2ecf20Sopenharmony_ci break; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TYPE: 4168c2ecf20Sopenharmony_ci val->intval = bd9995x_get_prop_charge_type(bd); 4178c2ecf20Sopenharmony_ci break; 4188c2ecf20Sopenharmony_ci 4198c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 4208c2ecf20Sopenharmony_ci val->intval = bd9995x_get_prop_batt_health(bd); 4218c2ecf20Sopenharmony_ci break; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TEMP: 4248c2ecf20Sopenharmony_ci val->intval = bd9995x_get_prop_batt_temp(bd); 4258c2ecf20Sopenharmony_ci break; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 4288c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_MODEL_NAME: 4328c2ecf20Sopenharmony_ci val->strval = "bd99954"; 4338c2ecf20Sopenharmony_ci break; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci default: 4368c2ecf20Sopenharmony_ci return -EINVAL; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci } 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return 0; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int bd9995x_get_chip_state(struct bd9995x_device *bd, 4448c2ecf20Sopenharmony_ci struct bd9995x_state *state) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci int i, ret, tmp; 4478c2ecf20Sopenharmony_ci struct { 4488c2ecf20Sopenharmony_ci struct regmap_field *id; 4498c2ecf20Sopenharmony_ci u16 *data; 4508c2ecf20Sopenharmony_ci } state_fields[] = { 4518c2ecf20Sopenharmony_ci { 4528c2ecf20Sopenharmony_ci bd->rmap_fields[F_CHGSTM_STATE], &state->chgstm_status, 4538c2ecf20Sopenharmony_ci }, { 4548c2ecf20Sopenharmony_ci bd->rmap_fields[F_VBAT_VSYS_STATUS], 4558c2ecf20Sopenharmony_ci &state->vbat_vsys_status, 4568c2ecf20Sopenharmony_ci }, { 4578c2ecf20Sopenharmony_ci bd->rmap_fields[F_VBUS_VCC_STATUS], 4588c2ecf20Sopenharmony_ci &state->vbus_vcc_status, 4598c2ecf20Sopenharmony_ci }, 4608c2ecf20Sopenharmony_ci }; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(state_fields); i++) { 4648c2ecf20Sopenharmony_ci ret = regmap_field_read(state_fields[i].id, &tmp); 4658c2ecf20Sopenharmony_ci if (ret) 4668c2ecf20Sopenharmony_ci return ret; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci *state_fields[i].data = tmp; 4698c2ecf20Sopenharmony_ci } 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (state->vbus_vcc_status & STATUS_VCC_DET || 4728c2ecf20Sopenharmony_ci state->vbus_vcc_status & STATUS_VBUS_DET) 4738c2ecf20Sopenharmony_ci state->online = 1; 4748c2ecf20Sopenharmony_ci else 4758c2ecf20Sopenharmony_ci state->online = 0; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci return 0; 4788c2ecf20Sopenharmony_ci} 4798c2ecf20Sopenharmony_ci 4808c2ecf20Sopenharmony_cistatic irqreturn_t bd9995x_irq_handler_thread(int irq, void *private) 4818c2ecf20Sopenharmony_ci{ 4828c2ecf20Sopenharmony_ci struct bd9995x_device *bd = private; 4838c2ecf20Sopenharmony_ci int ret, status, mask, i; 4848c2ecf20Sopenharmony_ci unsigned long tmp; 4858c2ecf20Sopenharmony_ci struct bd9995x_state state; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* 4888c2ecf20Sopenharmony_ci * The bd9995x does not seem to generate big amount of interrupts. 4898c2ecf20Sopenharmony_ci * The logic regarding which interrupts can cause relevant 4908c2ecf20Sopenharmony_ci * status changes seem to be pretty complex. 4918c2ecf20Sopenharmony_ci * 4928c2ecf20Sopenharmony_ci * So lets implement really simple and hopefully bullet-proof handler: 4938c2ecf20Sopenharmony_ci * It does not really matter which IRQ we handle, we just go and 4948c2ecf20Sopenharmony_ci * re-read all interesting statuses + give the framework a nudge. 4958c2ecf20Sopenharmony_ci * 4968c2ecf20Sopenharmony_ci * Other option would be building a _complex_ and error prone logic 4978c2ecf20Sopenharmony_ci * trying to decide what could have been changed (resulting this IRQ 4988c2ecf20Sopenharmony_ci * we are now handling). During the normal operation the BD99954 does 4998c2ecf20Sopenharmony_ci * not seem to be generating much of interrupts so benefit from such 5008c2ecf20Sopenharmony_ci * logic would probably be minimal. 5018c2ecf20Sopenharmony_ci */ 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci ret = regmap_read(bd->rmap, INT0_STATUS, &status); 5048c2ecf20Sopenharmony_ci if (ret) { 5058c2ecf20Sopenharmony_ci dev_err(bd->dev, "Failed to read IRQ status\n"); 5068c2ecf20Sopenharmony_ci return IRQ_NONE; 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_INT0_SET], &mask); 5108c2ecf20Sopenharmony_ci if (ret) { 5118c2ecf20Sopenharmony_ci dev_err(bd->dev, "Failed to read IRQ mask\n"); 5128c2ecf20Sopenharmony_ci return IRQ_NONE; 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* Handle only IRQs that are not masked */ 5168c2ecf20Sopenharmony_ci status &= mask; 5178c2ecf20Sopenharmony_ci tmp = status; 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci /* Lowest bit does not represent any sub-registers */ 5208c2ecf20Sopenharmony_ci tmp >>= 1; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci /* 5238c2ecf20Sopenharmony_ci * Mask and ack IRQs we will handle (+ the idiot bit) 5248c2ecf20Sopenharmony_ci */ 5258c2ecf20Sopenharmony_ci ret = regmap_field_write(bd->rmap_fields[F_INT0_SET], 0); 5268c2ecf20Sopenharmony_ci if (ret) { 5278c2ecf20Sopenharmony_ci dev_err(bd->dev, "Failed to mask F_INT0\n"); 5288c2ecf20Sopenharmony_ci return IRQ_NONE; 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci ret = regmap_write(bd->rmap, INT0_STATUS, status); 5328c2ecf20Sopenharmony_ci if (ret) { 5338c2ecf20Sopenharmony_ci dev_err(bd->dev, "Failed to ack F_INT0\n"); 5348c2ecf20Sopenharmony_ci goto err_umask; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci for_each_set_bit(i, &tmp, 7) { 5388c2ecf20Sopenharmony_ci int sub_status, sub_mask; 5398c2ecf20Sopenharmony_ci int sub_status_reg[] = { 5408c2ecf20Sopenharmony_ci INT1_STATUS, INT2_STATUS, INT3_STATUS, INT4_STATUS, 5418c2ecf20Sopenharmony_ci INT5_STATUS, INT6_STATUS, INT7_STATUS, 5428c2ecf20Sopenharmony_ci }; 5438c2ecf20Sopenharmony_ci struct regmap_field *sub_mask_f[] = { 5448c2ecf20Sopenharmony_ci bd->rmap_fields[F_INT1_SET], 5458c2ecf20Sopenharmony_ci bd->rmap_fields[F_INT2_SET], 5468c2ecf20Sopenharmony_ci bd->rmap_fields[F_INT3_SET], 5478c2ecf20Sopenharmony_ci bd->rmap_fields[F_INT4_SET], 5488c2ecf20Sopenharmony_ci bd->rmap_fields[F_INT5_SET], 5498c2ecf20Sopenharmony_ci bd->rmap_fields[F_INT6_SET], 5508c2ecf20Sopenharmony_ci bd->rmap_fields[F_INT7_SET], 5518c2ecf20Sopenharmony_ci }; 5528c2ecf20Sopenharmony_ci 5538c2ecf20Sopenharmony_ci /* Clear sub IRQs */ 5548c2ecf20Sopenharmony_ci ret = regmap_read(bd->rmap, sub_status_reg[i], &sub_status); 5558c2ecf20Sopenharmony_ci if (ret) { 5568c2ecf20Sopenharmony_ci dev_err(bd->dev, "Failed to read IRQ sub-status\n"); 5578c2ecf20Sopenharmony_ci goto err_umask; 5588c2ecf20Sopenharmony_ci } 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci ret = regmap_field_read(sub_mask_f[i], &sub_mask); 5618c2ecf20Sopenharmony_ci if (ret) { 5628c2ecf20Sopenharmony_ci dev_err(bd->dev, "Failed to read IRQ sub-mask\n"); 5638c2ecf20Sopenharmony_ci goto err_umask; 5648c2ecf20Sopenharmony_ci } 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci /* Ack active sub-statuses */ 5678c2ecf20Sopenharmony_ci sub_status &= sub_mask; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci ret = regmap_write(bd->rmap, sub_status_reg[i], sub_status); 5708c2ecf20Sopenharmony_ci if (ret) { 5718c2ecf20Sopenharmony_ci dev_err(bd->dev, "Failed to ack sub-IRQ\n"); 5728c2ecf20Sopenharmony_ci goto err_umask; 5738c2ecf20Sopenharmony_ci } 5748c2ecf20Sopenharmony_ci } 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci ret = regmap_field_write(bd->rmap_fields[F_INT0_SET], mask); 5778c2ecf20Sopenharmony_ci if (ret) 5788c2ecf20Sopenharmony_ci /* May as well retry once */ 5798c2ecf20Sopenharmony_ci goto err_umask; 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci /* Read whole chip state */ 5828c2ecf20Sopenharmony_ci ret = bd9995x_get_chip_state(bd, &state); 5838c2ecf20Sopenharmony_ci if (ret < 0) { 5848c2ecf20Sopenharmony_ci dev_err(bd->dev, "Failed to read chip state\n"); 5858c2ecf20Sopenharmony_ci } else { 5868c2ecf20Sopenharmony_ci mutex_lock(&bd->lock); 5878c2ecf20Sopenharmony_ci bd->state = state; 5888c2ecf20Sopenharmony_ci mutex_unlock(&bd->lock); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci power_supply_changed(bd->charger); 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci return IRQ_HANDLED; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cierr_umask: 5968c2ecf20Sopenharmony_ci ret = regmap_field_write(bd->rmap_fields[F_INT0_SET], mask); 5978c2ecf20Sopenharmony_ci if (ret) 5988c2ecf20Sopenharmony_ci dev_err(bd->dev, 5998c2ecf20Sopenharmony_ci "Failed to un-mask F_INT0 - IRQ permanently disabled\n"); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci return IRQ_NONE; 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_cistatic int __bd9995x_chip_reset(struct bd9995x_device *bd) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci int ret, state; 6078c2ecf20Sopenharmony_ci int rst_check_counter = 10; 6088c2ecf20Sopenharmony_ci u16 tmp = ALLRST | OTPLD; 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci ret = regmap_raw_write(bd->rmap, SYSTEM_CTRL_SET, &tmp, 2); 6118c2ecf20Sopenharmony_ci if (ret < 0) 6128c2ecf20Sopenharmony_ci return ret; 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_ci do { 6158c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_OTPLD_STATE], &state); 6168c2ecf20Sopenharmony_ci if (ret) 6178c2ecf20Sopenharmony_ci return ret; 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci msleep(10); 6208c2ecf20Sopenharmony_ci } while (state == 0 && --rst_check_counter); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci if (!rst_check_counter) { 6238c2ecf20Sopenharmony_ci dev_err(bd->dev, "chip reset not completed\n"); 6248c2ecf20Sopenharmony_ci return -ETIMEDOUT; 6258c2ecf20Sopenharmony_ci } 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci tmp = 0; 6288c2ecf20Sopenharmony_ci ret = regmap_raw_write(bd->rmap, SYSTEM_CTRL_SET, &tmp, 2); 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_ci return ret; 6318c2ecf20Sopenharmony_ci} 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_cistatic int bd9995x_hw_init(struct bd9995x_device *bd) 6348c2ecf20Sopenharmony_ci{ 6358c2ecf20Sopenharmony_ci int ret; 6368c2ecf20Sopenharmony_ci int i; 6378c2ecf20Sopenharmony_ci struct bd9995x_state state; 6388c2ecf20Sopenharmony_ci struct bd9995x_init_data *id = &bd->init_data; 6398c2ecf20Sopenharmony_ci 6408c2ecf20Sopenharmony_ci const struct { 6418c2ecf20Sopenharmony_ci enum bd9995x_fields id; 6428c2ecf20Sopenharmony_ci u16 value; 6438c2ecf20Sopenharmony_ci } init_data[] = { 6448c2ecf20Sopenharmony_ci /* Enable the charging trigger after SDP charger attached */ 6458c2ecf20Sopenharmony_ci {F_SDP_CHG_TRIG_EN, 1}, 6468c2ecf20Sopenharmony_ci /* Enable charging trigger after SDP charger attached */ 6478c2ecf20Sopenharmony_ci {F_SDP_CHG_TRIG, 1}, 6488c2ecf20Sopenharmony_ci /* Disable charging trigger by BC1.2 detection */ 6498c2ecf20Sopenharmony_ci {F_VBUS_BC_DISEN, 1}, 6508c2ecf20Sopenharmony_ci /* Disable charging trigger by BC1.2 detection */ 6518c2ecf20Sopenharmony_ci {F_VCC_BC_DISEN, 1}, 6528c2ecf20Sopenharmony_ci /* Disable automatic limitation of the input current */ 6538c2ecf20Sopenharmony_ci {F_ILIM_AUTO_DISEN, 1}, 6548c2ecf20Sopenharmony_ci /* Select current limitation when SDP charger attached*/ 6558c2ecf20Sopenharmony_ci {F_SDP_500_SEL, 1}, 6568c2ecf20Sopenharmony_ci /* Select current limitation when DCP charger attached */ 6578c2ecf20Sopenharmony_ci {F_DCP_2500_SEL, 1}, 6588c2ecf20Sopenharmony_ci {F_VSYSREG_SET, id->vsysreg_set}, 6598c2ecf20Sopenharmony_ci /* Activate USB charging and DC/DC converter */ 6608c2ecf20Sopenharmony_ci {F_USB_SUS, 0}, 6618c2ecf20Sopenharmony_ci /* DCDC clock: 1200 kHz*/ 6628c2ecf20Sopenharmony_ci {F_DCDC_CLK_SEL, 3}, 6638c2ecf20Sopenharmony_ci /* Enable charging */ 6648c2ecf20Sopenharmony_ci {F_CHG_EN, 1}, 6658c2ecf20Sopenharmony_ci /* Disable Input current Limit setting voltage measurement */ 6668c2ecf20Sopenharmony_ci {F_EXTIADPEN, 0}, 6678c2ecf20Sopenharmony_ci /* Disable input current limiting */ 6688c2ecf20Sopenharmony_ci {F_VSYS_PRIORITY, 1}, 6698c2ecf20Sopenharmony_ci {F_IBUS_LIM_SET, id->ibus_lim_set}, 6708c2ecf20Sopenharmony_ci {F_ICC_LIM_SET, id->icc_lim_set}, 6718c2ecf20Sopenharmony_ci /* Charge Termination Current Setting to 0*/ 6728c2ecf20Sopenharmony_ci {F_ITERM_SET, id->iterm_set}, 6738c2ecf20Sopenharmony_ci /* Trickle-charge Current Setting */ 6748c2ecf20Sopenharmony_ci {F_ITRICH_SET, id->itrich_set}, 6758c2ecf20Sopenharmony_ci /* Pre-charge Current setting */ 6768c2ecf20Sopenharmony_ci {F_IPRECH_SET, id->iprech_set}, 6778c2ecf20Sopenharmony_ci /* Fast Charge Current for constant current phase */ 6788c2ecf20Sopenharmony_ci {F_ICHG_SET, id->ichg_set}, 6798c2ecf20Sopenharmony_ci /* Fast Charge Voltage Regulation Setting */ 6808c2ecf20Sopenharmony_ci {F_VFASTCHG_REG_SET1, id->vfastchg_reg_set1}, 6818c2ecf20Sopenharmony_ci /* Set Pre-charge Voltage Threshold for trickle charging. */ 6828c2ecf20Sopenharmony_ci {F_VPRECHG_TH_SET, id->vprechg_th_set}, 6838c2ecf20Sopenharmony_ci {F_VRECHG_SET, id->vrechg_set}, 6848c2ecf20Sopenharmony_ci {F_VBATOVP_SET, id->vbatovp_set}, 6858c2ecf20Sopenharmony_ci /* Reverse buck boost voltage Setting */ 6868c2ecf20Sopenharmony_ci {F_VRBOOST_SET, 0}, 6878c2ecf20Sopenharmony_ci /* Disable fast-charging watchdog */ 6888c2ecf20Sopenharmony_ci {F_WDT_FST, 0}, 6898c2ecf20Sopenharmony_ci /* Disable pre-charging watchdog */ 6908c2ecf20Sopenharmony_ci {F_WDT_PRE, 0}, 6918c2ecf20Sopenharmony_ci /* Power save off */ 6928c2ecf20Sopenharmony_ci {F_POWER_SAVE_MODE, 0}, 6938c2ecf20Sopenharmony_ci {F_INT1_SET, INT1_ALL}, 6948c2ecf20Sopenharmony_ci {F_INT2_SET, INT2_ALL}, 6958c2ecf20Sopenharmony_ci {F_INT3_SET, INT3_ALL}, 6968c2ecf20Sopenharmony_ci {F_INT4_SET, INT4_ALL}, 6978c2ecf20Sopenharmony_ci {F_INT5_SET, INT5_ALL}, 6988c2ecf20Sopenharmony_ci {F_INT6_SET, INT6_ALL}, 6998c2ecf20Sopenharmony_ci {F_INT7_SET, INT7_ALL}, 7008c2ecf20Sopenharmony_ci }; 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* 7038c2ecf20Sopenharmony_ci * Currently we initialize charger to a known state at startup. 7048c2ecf20Sopenharmony_ci * If we want to allow for example the boot code to initialize 7058c2ecf20Sopenharmony_ci * charger we should get rid of this. 7068c2ecf20Sopenharmony_ci */ 7078c2ecf20Sopenharmony_ci ret = __bd9995x_chip_reset(bd); 7088c2ecf20Sopenharmony_ci if (ret < 0) 7098c2ecf20Sopenharmony_ci return ret; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_ci /* Initialize currents/voltages and other parameters */ 7128c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(init_data); i++) { 7138c2ecf20Sopenharmony_ci ret = regmap_field_write(bd->rmap_fields[init_data[i].id], 7148c2ecf20Sopenharmony_ci init_data[i].value); 7158c2ecf20Sopenharmony_ci if (ret) { 7168c2ecf20Sopenharmony_ci dev_err(bd->dev, "failed to initialize charger (%d)\n", 7178c2ecf20Sopenharmony_ci ret); 7188c2ecf20Sopenharmony_ci return ret; 7198c2ecf20Sopenharmony_ci } 7208c2ecf20Sopenharmony_ci } 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci ret = bd9995x_get_chip_state(bd, &state); 7238c2ecf20Sopenharmony_ci if (ret < 0) 7248c2ecf20Sopenharmony_ci return ret; 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci mutex_lock(&bd->lock); 7278c2ecf20Sopenharmony_ci bd->state = state; 7288c2ecf20Sopenharmony_ci mutex_unlock(&bd->lock); 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci return 0; 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic enum power_supply_property bd9995x_power_supply_props[] = { 7348c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MANUFACTURER, 7358c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 7368c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 7378c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 7388c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_AVG, 7398c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX, 7408c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE, 7418c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT, 7428c2ecf20Sopenharmony_ci /* Battery props we access through charger */ 7438c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 7448c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_NOW, 7458c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_NOW, 7468c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TYPE, 7478c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 7488c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TEMP, 7498c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 7508c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_MODEL_NAME, 7518c2ecf20Sopenharmony_ci}; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_cistatic const struct power_supply_desc bd9995x_power_supply_desc = { 7548c2ecf20Sopenharmony_ci .name = "bd9995x-charger", 7558c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 7568c2ecf20Sopenharmony_ci .properties = bd9995x_power_supply_props, 7578c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(bd9995x_power_supply_props), 7588c2ecf20Sopenharmony_ci .get_property = bd9995x_power_supply_get_property, 7598c2ecf20Sopenharmony_ci}; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci/* 7628c2ecf20Sopenharmony_ci * Limit configurations for vbus-input-current and vcc-vacp-input-current 7638c2ecf20Sopenharmony_ci * Minimum limit is 0 uA. Max is 511 * 32000 uA = 16352000 uA. This is 7648c2ecf20Sopenharmony_ci * configured by writing a register so that each increment in register 7658c2ecf20Sopenharmony_ci * value equals to 32000 uA limit increment. 7668c2ecf20Sopenharmony_ci * 7678c2ecf20Sopenharmony_ci * Eg, value 0x0 is limit 0, value 0x1 is limit 32000, ... 7688c2ecf20Sopenharmony_ci * Describe the setting in linear_range table. 7698c2ecf20Sopenharmony_ci */ 7708c2ecf20Sopenharmony_cistatic const struct linear_range input_current_limit_ranges[] = { 7718c2ecf20Sopenharmony_ci { 7728c2ecf20Sopenharmony_ci .min = 0, 7738c2ecf20Sopenharmony_ci .step = 32000, 7748c2ecf20Sopenharmony_ci .min_sel = 0x0, 7758c2ecf20Sopenharmony_ci .max_sel = 0x1ff, 7768c2ecf20Sopenharmony_ci }, 7778c2ecf20Sopenharmony_ci}; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci/* Possible trickle, pre-charging and termination current values */ 7808c2ecf20Sopenharmony_cistatic const struct linear_range charging_current_ranges[] = { 7818c2ecf20Sopenharmony_ci { 7828c2ecf20Sopenharmony_ci .min = 0, 7838c2ecf20Sopenharmony_ci .step = 64000, 7848c2ecf20Sopenharmony_ci .min_sel = 0x0, 7858c2ecf20Sopenharmony_ci .max_sel = 0x10, 7868c2ecf20Sopenharmony_ci }, { 7878c2ecf20Sopenharmony_ci .min = 1024000, 7888c2ecf20Sopenharmony_ci .step = 0, 7898c2ecf20Sopenharmony_ci .min_sel = 0x11, 7908c2ecf20Sopenharmony_ci .max_sel = 0x1f, 7918c2ecf20Sopenharmony_ci }, 7928c2ecf20Sopenharmony_ci}; 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci/* 7958c2ecf20Sopenharmony_ci * Fast charging voltage regulation, starting re-charging limit 7968c2ecf20Sopenharmony_ci * and battery over voltage protection have same possible values 7978c2ecf20Sopenharmony_ci */ 7988c2ecf20Sopenharmony_cistatic const struct linear_range charge_voltage_regulation_ranges[] = { 7998c2ecf20Sopenharmony_ci { 8008c2ecf20Sopenharmony_ci .min = 2560000, 8018c2ecf20Sopenharmony_ci .step = 0, 8028c2ecf20Sopenharmony_ci .min_sel = 0, 8038c2ecf20Sopenharmony_ci .max_sel = 0xA0, 8048c2ecf20Sopenharmony_ci }, { 8058c2ecf20Sopenharmony_ci .min = 2560000, 8068c2ecf20Sopenharmony_ci .step = 16000, 8078c2ecf20Sopenharmony_ci .min_sel = 0xA0, 8088c2ecf20Sopenharmony_ci .max_sel = 0x4B0, 8098c2ecf20Sopenharmony_ci }, { 8108c2ecf20Sopenharmony_ci .min = 19200000, 8118c2ecf20Sopenharmony_ci .step = 0, 8128c2ecf20Sopenharmony_ci .min_sel = 0x4B0, 8138c2ecf20Sopenharmony_ci .max_sel = 0x7FF, 8148c2ecf20Sopenharmony_ci }, 8158c2ecf20Sopenharmony_ci}; 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci/* Possible VSYS voltage regulation values */ 8188c2ecf20Sopenharmony_cistatic const struct linear_range vsys_voltage_regulation_ranges[] = { 8198c2ecf20Sopenharmony_ci { 8208c2ecf20Sopenharmony_ci .min = 2560000, 8218c2ecf20Sopenharmony_ci .step = 0, 8228c2ecf20Sopenharmony_ci .min_sel = 0, 8238c2ecf20Sopenharmony_ci .max_sel = 0x28, 8248c2ecf20Sopenharmony_ci }, { 8258c2ecf20Sopenharmony_ci .min = 2560000, 8268c2ecf20Sopenharmony_ci .step = 64000, 8278c2ecf20Sopenharmony_ci .min_sel = 0x28, 8288c2ecf20Sopenharmony_ci .max_sel = 0x12C, 8298c2ecf20Sopenharmony_ci }, { 8308c2ecf20Sopenharmony_ci .min = 19200000, 8318c2ecf20Sopenharmony_ci .step = 0, 8328c2ecf20Sopenharmony_ci .min_sel = 0x12C, 8338c2ecf20Sopenharmony_ci .max_sel = 0x1FF, 8348c2ecf20Sopenharmony_ci }, 8358c2ecf20Sopenharmony_ci}; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci/* Possible settings for switching from trickle to pre-charging limits */ 8388c2ecf20Sopenharmony_cistatic const struct linear_range trickle_to_pre_threshold_ranges[] = { 8398c2ecf20Sopenharmony_ci { 8408c2ecf20Sopenharmony_ci .min = 2048000, 8418c2ecf20Sopenharmony_ci .step = 0, 8428c2ecf20Sopenharmony_ci .min_sel = 0, 8438c2ecf20Sopenharmony_ci .max_sel = 0x20, 8448c2ecf20Sopenharmony_ci }, { 8458c2ecf20Sopenharmony_ci .min = 2048000, 8468c2ecf20Sopenharmony_ci .step = 64000, 8478c2ecf20Sopenharmony_ci .min_sel = 0x20, 8488c2ecf20Sopenharmony_ci .max_sel = 0x12C, 8498c2ecf20Sopenharmony_ci }, { 8508c2ecf20Sopenharmony_ci .min = 19200000, 8518c2ecf20Sopenharmony_ci .step = 0, 8528c2ecf20Sopenharmony_ci .min_sel = 0x12C, 8538c2ecf20Sopenharmony_ci .max_sel = 0x1FF 8548c2ecf20Sopenharmony_ci } 8558c2ecf20Sopenharmony_ci}; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci/* Possible current values for fast-charging constant current phase */ 8588c2ecf20Sopenharmony_cistatic const struct linear_range fast_charge_current_ranges[] = { 8598c2ecf20Sopenharmony_ci { 8608c2ecf20Sopenharmony_ci .min = 0, 8618c2ecf20Sopenharmony_ci .step = 64000, 8628c2ecf20Sopenharmony_ci .min_sel = 0, 8638c2ecf20Sopenharmony_ci .max_sel = 0xFF, 8648c2ecf20Sopenharmony_ci } 8658c2ecf20Sopenharmony_ci}; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_cistruct battery_init { 8688c2ecf20Sopenharmony_ci const char *name; 8698c2ecf20Sopenharmony_ci int *info_data; 8708c2ecf20Sopenharmony_ci const struct linear_range *range; 8718c2ecf20Sopenharmony_ci int ranges; 8728c2ecf20Sopenharmony_ci u16 *data; 8738c2ecf20Sopenharmony_ci}; 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_cistruct dt_init { 8768c2ecf20Sopenharmony_ci char *prop; 8778c2ecf20Sopenharmony_ci const struct linear_range *range; 8788c2ecf20Sopenharmony_ci int ranges; 8798c2ecf20Sopenharmony_ci u16 *data; 8808c2ecf20Sopenharmony_ci}; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_cistatic int bd9995x_fw_probe(struct bd9995x_device *bd) 8838c2ecf20Sopenharmony_ci{ 8848c2ecf20Sopenharmony_ci int ret; 8858c2ecf20Sopenharmony_ci struct power_supply_battery_info info; 8868c2ecf20Sopenharmony_ci u32 property; 8878c2ecf20Sopenharmony_ci int i; 8888c2ecf20Sopenharmony_ci int regval; 8898c2ecf20Sopenharmony_ci bool found; 8908c2ecf20Sopenharmony_ci struct bd9995x_init_data *init = &bd->init_data; 8918c2ecf20Sopenharmony_ci struct battery_init battery_inits[] = { 8928c2ecf20Sopenharmony_ci { 8938c2ecf20Sopenharmony_ci .name = "trickle-charging current", 8948c2ecf20Sopenharmony_ci .info_data = &info.tricklecharge_current_ua, 8958c2ecf20Sopenharmony_ci .range = &charging_current_ranges[0], 8968c2ecf20Sopenharmony_ci .ranges = 2, 8978c2ecf20Sopenharmony_ci .data = &init->itrich_set, 8988c2ecf20Sopenharmony_ci }, { 8998c2ecf20Sopenharmony_ci .name = "pre-charging current", 9008c2ecf20Sopenharmony_ci .info_data = &info.precharge_current_ua, 9018c2ecf20Sopenharmony_ci .range = &charging_current_ranges[0], 9028c2ecf20Sopenharmony_ci .ranges = 2, 9038c2ecf20Sopenharmony_ci .data = &init->iprech_set, 9048c2ecf20Sopenharmony_ci }, { 9058c2ecf20Sopenharmony_ci .name = "pre-to-trickle charge voltage threshold", 9068c2ecf20Sopenharmony_ci .info_data = &info.precharge_voltage_max_uv, 9078c2ecf20Sopenharmony_ci .range = &trickle_to_pre_threshold_ranges[0], 9088c2ecf20Sopenharmony_ci .ranges = 2, 9098c2ecf20Sopenharmony_ci .data = &init->vprechg_th_set, 9108c2ecf20Sopenharmony_ci }, { 9118c2ecf20Sopenharmony_ci .name = "charging termination current", 9128c2ecf20Sopenharmony_ci .info_data = &info.charge_term_current_ua, 9138c2ecf20Sopenharmony_ci .range = &charging_current_ranges[0], 9148c2ecf20Sopenharmony_ci .ranges = 2, 9158c2ecf20Sopenharmony_ci .data = &init->iterm_set, 9168c2ecf20Sopenharmony_ci }, { 9178c2ecf20Sopenharmony_ci .name = "charging re-start voltage", 9188c2ecf20Sopenharmony_ci .info_data = &info.charge_restart_voltage_uv, 9198c2ecf20Sopenharmony_ci .range = &charge_voltage_regulation_ranges[0], 9208c2ecf20Sopenharmony_ci .ranges = 2, 9218c2ecf20Sopenharmony_ci .data = &init->vrechg_set, 9228c2ecf20Sopenharmony_ci }, { 9238c2ecf20Sopenharmony_ci .name = "battery overvoltage limit", 9248c2ecf20Sopenharmony_ci .info_data = &info.overvoltage_limit_uv, 9258c2ecf20Sopenharmony_ci .range = &charge_voltage_regulation_ranges[0], 9268c2ecf20Sopenharmony_ci .ranges = 2, 9278c2ecf20Sopenharmony_ci .data = &init->vbatovp_set, 9288c2ecf20Sopenharmony_ci }, { 9298c2ecf20Sopenharmony_ci .name = "fast-charging max current", 9308c2ecf20Sopenharmony_ci .info_data = &info.constant_charge_current_max_ua, 9318c2ecf20Sopenharmony_ci .range = &fast_charge_current_ranges[0], 9328c2ecf20Sopenharmony_ci .ranges = 1, 9338c2ecf20Sopenharmony_ci .data = &init->ichg_set, 9348c2ecf20Sopenharmony_ci }, { 9358c2ecf20Sopenharmony_ci .name = "fast-charging voltage", 9368c2ecf20Sopenharmony_ci .info_data = &info.constant_charge_voltage_max_uv, 9378c2ecf20Sopenharmony_ci .range = &charge_voltage_regulation_ranges[0], 9388c2ecf20Sopenharmony_ci .ranges = 2, 9398c2ecf20Sopenharmony_ci .data = &init->vfastchg_reg_set1, 9408c2ecf20Sopenharmony_ci }, 9418c2ecf20Sopenharmony_ci }; 9428c2ecf20Sopenharmony_ci struct dt_init props[] = { 9438c2ecf20Sopenharmony_ci { 9448c2ecf20Sopenharmony_ci .prop = "rohm,vsys-regulation-microvolt", 9458c2ecf20Sopenharmony_ci .range = &vsys_voltage_regulation_ranges[0], 9468c2ecf20Sopenharmony_ci .ranges = 2, 9478c2ecf20Sopenharmony_ci .data = &init->vsysreg_set, 9488c2ecf20Sopenharmony_ci }, { 9498c2ecf20Sopenharmony_ci .prop = "rohm,vbus-input-current-limit-microamp", 9508c2ecf20Sopenharmony_ci .range = &input_current_limit_ranges[0], 9518c2ecf20Sopenharmony_ci .ranges = 1, 9528c2ecf20Sopenharmony_ci .data = &init->ibus_lim_set, 9538c2ecf20Sopenharmony_ci }, { 9548c2ecf20Sopenharmony_ci .prop = "rohm,vcc-input-current-limit-microamp", 9558c2ecf20Sopenharmony_ci .range = &input_current_limit_ranges[0], 9568c2ecf20Sopenharmony_ci .ranges = 1, 9578c2ecf20Sopenharmony_ci .data = &init->icc_lim_set, 9588c2ecf20Sopenharmony_ci }, 9598c2ecf20Sopenharmony_ci }; 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci /* 9628c2ecf20Sopenharmony_ci * The power_supply_get_battery_info() does not support getting values 9638c2ecf20Sopenharmony_ci * from ACPI. Let's fix it if ACPI is required here. 9648c2ecf20Sopenharmony_ci */ 9658c2ecf20Sopenharmony_ci ret = power_supply_get_battery_info(bd->charger, &info); 9668c2ecf20Sopenharmony_ci if (ret < 0) 9678c2ecf20Sopenharmony_ci return ret; 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(battery_inits); i++) { 9708c2ecf20Sopenharmony_ci int val = *battery_inits[i].info_data; 9718c2ecf20Sopenharmony_ci const struct linear_range *range = battery_inits[i].range; 9728c2ecf20Sopenharmony_ci int ranges = battery_inits[i].ranges; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci if (val == -EINVAL) 9758c2ecf20Sopenharmony_ci continue; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci ret = linear_range_get_selector_low_array(range, ranges, val, 9788c2ecf20Sopenharmony_ci ®val, &found); 9798c2ecf20Sopenharmony_ci if (ret) { 9808c2ecf20Sopenharmony_ci dev_err(bd->dev, "Unsupported value for %s\n", 9818c2ecf20Sopenharmony_ci battery_inits[i].name); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci power_supply_put_battery_info(bd->charger, &info); 9848c2ecf20Sopenharmony_ci return -EINVAL; 9858c2ecf20Sopenharmony_ci } 9868c2ecf20Sopenharmony_ci if (!found) { 9878c2ecf20Sopenharmony_ci dev_warn(bd->dev, 9888c2ecf20Sopenharmony_ci "Unsupported value for %s - using smaller\n", 9898c2ecf20Sopenharmony_ci battery_inits[i].name); 9908c2ecf20Sopenharmony_ci } 9918c2ecf20Sopenharmony_ci *(battery_inits[i].data) = regval; 9928c2ecf20Sopenharmony_ci } 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci power_supply_put_battery_info(bd->charger, &info); 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(props); i++) { 9978c2ecf20Sopenharmony_ci ret = device_property_read_u32(bd->dev, props[i].prop, 9988c2ecf20Sopenharmony_ci &property); 9998c2ecf20Sopenharmony_ci if (ret < 0) { 10008c2ecf20Sopenharmony_ci dev_err(bd->dev, "failed to read %s", props[i].prop); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci return ret; 10038c2ecf20Sopenharmony_ci } 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_ci ret = linear_range_get_selector_low_array(props[i].range, 10068c2ecf20Sopenharmony_ci props[i].ranges, 10078c2ecf20Sopenharmony_ci property, ®val, 10088c2ecf20Sopenharmony_ci &found); 10098c2ecf20Sopenharmony_ci if (ret) { 10108c2ecf20Sopenharmony_ci dev_err(bd->dev, "Unsupported value for '%s'\n", 10118c2ecf20Sopenharmony_ci props[i].prop); 10128c2ecf20Sopenharmony_ci 10138c2ecf20Sopenharmony_ci return -EINVAL; 10148c2ecf20Sopenharmony_ci } 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if (!found) { 10178c2ecf20Sopenharmony_ci dev_warn(bd->dev, 10188c2ecf20Sopenharmony_ci "Unsupported value for '%s' - using smaller\n", 10198c2ecf20Sopenharmony_ci props[i].prop); 10208c2ecf20Sopenharmony_ci } 10218c2ecf20Sopenharmony_ci 10228c2ecf20Sopenharmony_ci *(props[i].data) = regval; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci return 0; 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cistatic void bd9995x_chip_reset(void *bd) 10298c2ecf20Sopenharmony_ci{ 10308c2ecf20Sopenharmony_ci __bd9995x_chip_reset(bd); 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_cistatic int bd9995x_probe(struct i2c_client *client) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 10368c2ecf20Sopenharmony_ci struct bd9995x_device *bd; 10378c2ecf20Sopenharmony_ci struct power_supply_config psy_cfg = {}; 10388c2ecf20Sopenharmony_ci int ret; 10398c2ecf20Sopenharmony_ci int i; 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci bd = devm_kzalloc(dev, sizeof(*bd), GFP_KERNEL); 10428c2ecf20Sopenharmony_ci if (!bd) 10438c2ecf20Sopenharmony_ci return -ENOMEM; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci bd->client = client; 10468c2ecf20Sopenharmony_ci bd->dev = dev; 10478c2ecf20Sopenharmony_ci psy_cfg.drv_data = bd; 10488c2ecf20Sopenharmony_ci psy_cfg.of_node = dev->of_node; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci mutex_init(&bd->lock); 10518c2ecf20Sopenharmony_ci 10528c2ecf20Sopenharmony_ci bd->rmap = devm_regmap_init_i2c(client, &bd9995x_regmap_config); 10538c2ecf20Sopenharmony_ci if (IS_ERR(bd->rmap)) { 10548c2ecf20Sopenharmony_ci dev_err(dev, "Failed to setup register access via i2c\n"); 10558c2ecf20Sopenharmony_ci return PTR_ERR(bd->rmap); 10568c2ecf20Sopenharmony_ci } 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(bd9995x_reg_fields); i++) { 10598c2ecf20Sopenharmony_ci const struct reg_field *reg_fields = bd9995x_reg_fields; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci bd->rmap_fields[i] = devm_regmap_field_alloc(dev, bd->rmap, 10628c2ecf20Sopenharmony_ci reg_fields[i]); 10638c2ecf20Sopenharmony_ci if (IS_ERR(bd->rmap_fields[i])) { 10648c2ecf20Sopenharmony_ci dev_err(dev, "cannot allocate regmap field\n"); 10658c2ecf20Sopenharmony_ci return PTR_ERR(bd->rmap_fields[i]); 10668c2ecf20Sopenharmony_ci } 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci i2c_set_clientdata(client, bd); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_CHIP_ID], &bd->chip_id); 10728c2ecf20Sopenharmony_ci if (ret) { 10738c2ecf20Sopenharmony_ci dev_err(dev, "Cannot read chip ID.\n"); 10748c2ecf20Sopenharmony_ci return ret; 10758c2ecf20Sopenharmony_ci } 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci if (bd->chip_id != BD99954_ID) { 10788c2ecf20Sopenharmony_ci dev_err(dev, "Chip with ID=0x%x, not supported!\n", 10798c2ecf20Sopenharmony_ci bd->chip_id); 10808c2ecf20Sopenharmony_ci return -ENODEV; 10818c2ecf20Sopenharmony_ci } 10828c2ecf20Sopenharmony_ci 10838c2ecf20Sopenharmony_ci ret = regmap_field_read(bd->rmap_fields[F_CHIP_REV], &bd->chip_rev); 10848c2ecf20Sopenharmony_ci if (ret) { 10858c2ecf20Sopenharmony_ci dev_err(dev, "Cannot read revision.\n"); 10868c2ecf20Sopenharmony_ci return ret; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci dev_info(bd->dev, "Found BD99954 chip rev %d\n", bd->chip_rev); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* 10928c2ecf20Sopenharmony_ci * We need to init the psy before we can call 10938c2ecf20Sopenharmony_ci * power_supply_get_battery_info() for it 10948c2ecf20Sopenharmony_ci */ 10958c2ecf20Sopenharmony_ci bd->charger = devm_power_supply_register(bd->dev, 10968c2ecf20Sopenharmony_ci &bd9995x_power_supply_desc, 10978c2ecf20Sopenharmony_ci &psy_cfg); 10988c2ecf20Sopenharmony_ci if (IS_ERR(bd->charger)) { 10998c2ecf20Sopenharmony_ci dev_err(dev, "Failed to register power supply\n"); 11008c2ecf20Sopenharmony_ci return PTR_ERR(bd->charger); 11018c2ecf20Sopenharmony_ci } 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci ret = bd9995x_fw_probe(bd); 11048c2ecf20Sopenharmony_ci if (ret < 0) { 11058c2ecf20Sopenharmony_ci dev_err(dev, "Cannot read device properties.\n"); 11068c2ecf20Sopenharmony_ci return ret; 11078c2ecf20Sopenharmony_ci } 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci ret = bd9995x_hw_init(bd); 11108c2ecf20Sopenharmony_ci if (ret < 0) { 11118c2ecf20Sopenharmony_ci dev_err(dev, "Cannot initialize the chip.\n"); 11128c2ecf20Sopenharmony_ci return ret; 11138c2ecf20Sopenharmony_ci } 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(dev, bd9995x_chip_reset, bd); 11168c2ecf20Sopenharmony_ci if (ret) 11178c2ecf20Sopenharmony_ci return ret; 11188c2ecf20Sopenharmony_ci 11198c2ecf20Sopenharmony_ci return devm_request_threaded_irq(dev, client->irq, NULL, 11208c2ecf20Sopenharmony_ci bd9995x_irq_handler_thread, 11218c2ecf20Sopenharmony_ci IRQF_TRIGGER_LOW | IRQF_ONESHOT, 11228c2ecf20Sopenharmony_ci BD9995X_IRQ_PIN, bd); 11238c2ecf20Sopenharmony_ci} 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_cistatic const struct of_device_id bd9995x_of_match[] = { 11268c2ecf20Sopenharmony_ci { .compatible = "rohm,bd99954", }, 11278c2ecf20Sopenharmony_ci { } 11288c2ecf20Sopenharmony_ci}; 11298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, bd9995x_of_match); 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_cistatic struct i2c_driver bd9995x_driver = { 11328c2ecf20Sopenharmony_ci .driver = { 11338c2ecf20Sopenharmony_ci .name = "bd9995x-charger", 11348c2ecf20Sopenharmony_ci .of_match_table = bd9995x_of_match, 11358c2ecf20Sopenharmony_ci }, 11368c2ecf20Sopenharmony_ci .probe_new = bd9995x_probe, 11378c2ecf20Sopenharmony_ci}; 11388c2ecf20Sopenharmony_cimodule_i2c_driver(bd9995x_driver); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Laine Markus <markus.laine@fi.rohmeurope.com>"); 11418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("ROHM BD99954 charger driver"); 11428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 1143