18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Copyright (c) 2014, Sony Mobile Communications Inc. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This driver is for the multi-block Switch-Mode Battery Charger and Boost 58c2ecf20Sopenharmony_ci * (SMBB) hardware, found in Qualcomm PM8941 PMICs. The charger is an 68c2ecf20Sopenharmony_ci * integrated, single-cell lithium-ion battery charger. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Sub-components: 98c2ecf20Sopenharmony_ci * - Charger core 108c2ecf20Sopenharmony_ci * - Buck 118c2ecf20Sopenharmony_ci * - DC charge-path 128c2ecf20Sopenharmony_ci * - USB charge-path 138c2ecf20Sopenharmony_ci * - Battery interface 148c2ecf20Sopenharmony_ci * - Boost (not implemented) 158c2ecf20Sopenharmony_ci * - Misc 168c2ecf20Sopenharmony_ci * - HF-Buck 178c2ecf20Sopenharmony_ci */ 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/errno.h> 208c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/module.h> 238c2ecf20Sopenharmony_ci#include <linux/mutex.h> 248c2ecf20Sopenharmony_ci#include <linux/of.h> 258c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 268c2ecf20Sopenharmony_ci#include <linux/power_supply.h> 278c2ecf20Sopenharmony_ci#include <linux/regmap.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <linux/extcon-provider.h> 308c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci#define SMBB_CHG_VMAX 0x040 338c2ecf20Sopenharmony_ci#define SMBB_CHG_VSAFE 0x041 348c2ecf20Sopenharmony_ci#define SMBB_CHG_CFG 0x043 358c2ecf20Sopenharmony_ci#define SMBB_CHG_IMAX 0x044 368c2ecf20Sopenharmony_ci#define SMBB_CHG_ISAFE 0x045 378c2ecf20Sopenharmony_ci#define SMBB_CHG_VIN_MIN 0x047 388c2ecf20Sopenharmony_ci#define SMBB_CHG_CTRL 0x049 398c2ecf20Sopenharmony_ci#define CTRL_EN BIT(7) 408c2ecf20Sopenharmony_ci#define SMBB_CHG_VBAT_WEAK 0x052 418c2ecf20Sopenharmony_ci#define SMBB_CHG_IBAT_TERM_CHG 0x05b 428c2ecf20Sopenharmony_ci#define IBAT_TERM_CHG_IEOC BIT(7) 438c2ecf20Sopenharmony_ci#define IBAT_TERM_CHG_IEOC_BMS BIT(7) 448c2ecf20Sopenharmony_ci#define IBAT_TERM_CHG_IEOC_CHG 0 458c2ecf20Sopenharmony_ci#define SMBB_CHG_VBAT_DET 0x05d 468c2ecf20Sopenharmony_ci#define SMBB_CHG_TCHG_MAX_EN 0x060 478c2ecf20Sopenharmony_ci#define TCHG_MAX_EN BIT(7) 488c2ecf20Sopenharmony_ci#define SMBB_CHG_WDOG_TIME 0x062 498c2ecf20Sopenharmony_ci#define SMBB_CHG_WDOG_EN 0x065 508c2ecf20Sopenharmony_ci#define WDOG_EN BIT(7) 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci#define SMBB_BUCK_REG_MODE 0x174 538c2ecf20Sopenharmony_ci#define BUCK_REG_MODE BIT(0) 548c2ecf20Sopenharmony_ci#define BUCK_REG_MODE_VBAT BIT(0) 558c2ecf20Sopenharmony_ci#define BUCK_REG_MODE_VSYS 0 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#define SMBB_BAT_PRES_STATUS 0x208 588c2ecf20Sopenharmony_ci#define PRES_STATUS_BAT_PRES BIT(7) 598c2ecf20Sopenharmony_ci#define SMBB_BAT_TEMP_STATUS 0x209 608c2ecf20Sopenharmony_ci#define TEMP_STATUS_OK BIT(7) 618c2ecf20Sopenharmony_ci#define TEMP_STATUS_HOT BIT(6) 628c2ecf20Sopenharmony_ci#define SMBB_BAT_BTC_CTRL 0x249 638c2ecf20Sopenharmony_ci#define BTC_CTRL_COMP_EN BIT(7) 648c2ecf20Sopenharmony_ci#define BTC_CTRL_COLD_EXT BIT(1) 658c2ecf20Sopenharmony_ci#define BTC_CTRL_HOT_EXT_N BIT(0) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define SMBB_USB_IMAX 0x344 688c2ecf20Sopenharmony_ci#define SMBB_USB_OTG_CTL 0x348 698c2ecf20Sopenharmony_ci#define OTG_CTL_EN BIT(0) 708c2ecf20Sopenharmony_ci#define SMBB_USB_ENUM_TIMER_STOP 0x34e 718c2ecf20Sopenharmony_ci#define ENUM_TIMER_STOP BIT(0) 728c2ecf20Sopenharmony_ci#define SMBB_USB_SEC_ACCESS 0x3d0 738c2ecf20Sopenharmony_ci#define SEC_ACCESS_MAGIC 0xa5 748c2ecf20Sopenharmony_ci#define SMBB_USB_REV_BST 0x3ed 758c2ecf20Sopenharmony_ci#define REV_BST_CHG_GONE BIT(7) 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define SMBB_DC_IMAX 0x444 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define SMBB_MISC_REV2 0x601 808c2ecf20Sopenharmony_ci#define SMBB_MISC_BOOT_DONE 0x642 818c2ecf20Sopenharmony_ci#define BOOT_DONE BIT(7) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define STATUS_USBIN_VALID BIT(0) /* USB connection is valid */ 848c2ecf20Sopenharmony_ci#define STATUS_DCIN_VALID BIT(1) /* DC connection is valid */ 858c2ecf20Sopenharmony_ci#define STATUS_BAT_HOT BIT(2) /* Battery temp 1=Hot, 0=Cold */ 868c2ecf20Sopenharmony_ci#define STATUS_BAT_OK BIT(3) /* Battery temp OK */ 878c2ecf20Sopenharmony_ci#define STATUS_BAT_PRESENT BIT(4) /* Battery is present */ 888c2ecf20Sopenharmony_ci#define STATUS_CHG_DONE BIT(5) /* Charge cycle is complete */ 898c2ecf20Sopenharmony_ci#define STATUS_CHG_TRKL BIT(6) /* Trickle charging */ 908c2ecf20Sopenharmony_ci#define STATUS_CHG_FAST BIT(7) /* Fast charging */ 918c2ecf20Sopenharmony_ci#define STATUS_CHG_GONE BIT(8) /* No charger is connected */ 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_cienum smbb_attr { 948c2ecf20Sopenharmony_ci ATTR_BAT_ISAFE, 958c2ecf20Sopenharmony_ci ATTR_BAT_IMAX, 968c2ecf20Sopenharmony_ci ATTR_USBIN_IMAX, 978c2ecf20Sopenharmony_ci ATTR_DCIN_IMAX, 988c2ecf20Sopenharmony_ci ATTR_BAT_VSAFE, 998c2ecf20Sopenharmony_ci ATTR_BAT_VMAX, 1008c2ecf20Sopenharmony_ci ATTR_BAT_VMIN, 1018c2ecf20Sopenharmony_ci ATTR_CHG_VDET, 1028c2ecf20Sopenharmony_ci ATTR_VIN_MIN, 1038c2ecf20Sopenharmony_ci _ATTR_CNT, 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistruct smbb_charger { 1078c2ecf20Sopenharmony_ci unsigned int revision; 1088c2ecf20Sopenharmony_ci unsigned int addr; 1098c2ecf20Sopenharmony_ci struct device *dev; 1108c2ecf20Sopenharmony_ci struct extcon_dev *edev; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci bool dc_disabled; 1138c2ecf20Sopenharmony_ci bool jeita_ext_temp; 1148c2ecf20Sopenharmony_ci unsigned long status; 1158c2ecf20Sopenharmony_ci struct mutex statlock; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci unsigned int attr[_ATTR_CNT]; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci struct power_supply *usb_psy; 1208c2ecf20Sopenharmony_ci struct power_supply *dc_psy; 1218c2ecf20Sopenharmony_ci struct power_supply *bat_psy; 1228c2ecf20Sopenharmony_ci struct regmap *regmap; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci struct regulator_desc otg_rdesc; 1258c2ecf20Sopenharmony_ci struct regulator_dev *otg_reg; 1268c2ecf20Sopenharmony_ci}; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_cistatic const unsigned int smbb_usb_extcon_cable[] = { 1298c2ecf20Sopenharmony_ci EXTCON_USB, 1308c2ecf20Sopenharmony_ci EXTCON_NONE, 1318c2ecf20Sopenharmony_ci}; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_cistatic int smbb_vbat_weak_fn(unsigned int index) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci return 2100000 + index * 100000; 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic int smbb_vin_fn(unsigned int index) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci if (index > 42) 1418c2ecf20Sopenharmony_ci return 5600000 + (index - 43) * 200000; 1428c2ecf20Sopenharmony_ci return 3400000 + index * 50000; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int smbb_vmax_fn(unsigned int index) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci return 3240000 + index * 10000; 1488c2ecf20Sopenharmony_ci} 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_cistatic int smbb_vbat_det_fn(unsigned int index) 1518c2ecf20Sopenharmony_ci{ 1528c2ecf20Sopenharmony_ci return 3240000 + index * 20000; 1538c2ecf20Sopenharmony_ci} 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_cistatic int smbb_imax_fn(unsigned int index) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci if (index < 2) 1588c2ecf20Sopenharmony_ci return 100000 + index * 50000; 1598c2ecf20Sopenharmony_ci return index * 100000; 1608c2ecf20Sopenharmony_ci} 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_cistatic int smbb_bat_imax_fn(unsigned int index) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci return index * 50000; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic unsigned int smbb_hw_lookup(unsigned int val, int (*fn)(unsigned int)) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci unsigned int widx; 1708c2ecf20Sopenharmony_ci unsigned int sel; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci for (widx = sel = 0; (*fn)(widx) <= val; ++widx) 1738c2ecf20Sopenharmony_ci sel = widx; 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return sel; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic const struct smbb_charger_attr { 1798c2ecf20Sopenharmony_ci const char *name; 1808c2ecf20Sopenharmony_ci unsigned int reg; 1818c2ecf20Sopenharmony_ci unsigned int safe_reg; 1828c2ecf20Sopenharmony_ci unsigned int max; 1838c2ecf20Sopenharmony_ci unsigned int min; 1848c2ecf20Sopenharmony_ci unsigned int fail_ok; 1858c2ecf20Sopenharmony_ci int (*hw_fn)(unsigned int); 1868c2ecf20Sopenharmony_ci} smbb_charger_attrs[] = { 1878c2ecf20Sopenharmony_ci [ATTR_BAT_ISAFE] = { 1888c2ecf20Sopenharmony_ci .name = "qcom,fast-charge-safe-current", 1898c2ecf20Sopenharmony_ci .reg = SMBB_CHG_ISAFE, 1908c2ecf20Sopenharmony_ci .max = 3000000, 1918c2ecf20Sopenharmony_ci .min = 200000, 1928c2ecf20Sopenharmony_ci .hw_fn = smbb_bat_imax_fn, 1938c2ecf20Sopenharmony_ci .fail_ok = 1, 1948c2ecf20Sopenharmony_ci }, 1958c2ecf20Sopenharmony_ci [ATTR_BAT_IMAX] = { 1968c2ecf20Sopenharmony_ci .name = "qcom,fast-charge-current-limit", 1978c2ecf20Sopenharmony_ci .reg = SMBB_CHG_IMAX, 1988c2ecf20Sopenharmony_ci .safe_reg = SMBB_CHG_ISAFE, 1998c2ecf20Sopenharmony_ci .max = 3000000, 2008c2ecf20Sopenharmony_ci .min = 200000, 2018c2ecf20Sopenharmony_ci .hw_fn = smbb_bat_imax_fn, 2028c2ecf20Sopenharmony_ci }, 2038c2ecf20Sopenharmony_ci [ATTR_DCIN_IMAX] = { 2048c2ecf20Sopenharmony_ci .name = "qcom,dc-current-limit", 2058c2ecf20Sopenharmony_ci .reg = SMBB_DC_IMAX, 2068c2ecf20Sopenharmony_ci .max = 2500000, 2078c2ecf20Sopenharmony_ci .min = 100000, 2088c2ecf20Sopenharmony_ci .hw_fn = smbb_imax_fn, 2098c2ecf20Sopenharmony_ci }, 2108c2ecf20Sopenharmony_ci [ATTR_BAT_VSAFE] = { 2118c2ecf20Sopenharmony_ci .name = "qcom,fast-charge-safe-voltage", 2128c2ecf20Sopenharmony_ci .reg = SMBB_CHG_VSAFE, 2138c2ecf20Sopenharmony_ci .max = 5000000, 2148c2ecf20Sopenharmony_ci .min = 3240000, 2158c2ecf20Sopenharmony_ci .hw_fn = smbb_vmax_fn, 2168c2ecf20Sopenharmony_ci .fail_ok = 1, 2178c2ecf20Sopenharmony_ci }, 2188c2ecf20Sopenharmony_ci [ATTR_BAT_VMAX] = { 2198c2ecf20Sopenharmony_ci .name = "qcom,fast-charge-high-threshold-voltage", 2208c2ecf20Sopenharmony_ci .reg = SMBB_CHG_VMAX, 2218c2ecf20Sopenharmony_ci .safe_reg = SMBB_CHG_VSAFE, 2228c2ecf20Sopenharmony_ci .max = 5000000, 2238c2ecf20Sopenharmony_ci .min = 3240000, 2248c2ecf20Sopenharmony_ci .hw_fn = smbb_vmax_fn, 2258c2ecf20Sopenharmony_ci }, 2268c2ecf20Sopenharmony_ci [ATTR_BAT_VMIN] = { 2278c2ecf20Sopenharmony_ci .name = "qcom,fast-charge-low-threshold-voltage", 2288c2ecf20Sopenharmony_ci .reg = SMBB_CHG_VBAT_WEAK, 2298c2ecf20Sopenharmony_ci .max = 3600000, 2308c2ecf20Sopenharmony_ci .min = 2100000, 2318c2ecf20Sopenharmony_ci .hw_fn = smbb_vbat_weak_fn, 2328c2ecf20Sopenharmony_ci }, 2338c2ecf20Sopenharmony_ci [ATTR_CHG_VDET] = { 2348c2ecf20Sopenharmony_ci .name = "qcom,auto-recharge-threshold-voltage", 2358c2ecf20Sopenharmony_ci .reg = SMBB_CHG_VBAT_DET, 2368c2ecf20Sopenharmony_ci .max = 5000000, 2378c2ecf20Sopenharmony_ci .min = 3240000, 2388c2ecf20Sopenharmony_ci .hw_fn = smbb_vbat_det_fn, 2398c2ecf20Sopenharmony_ci }, 2408c2ecf20Sopenharmony_ci [ATTR_VIN_MIN] = { 2418c2ecf20Sopenharmony_ci .name = "qcom,minimum-input-voltage", 2428c2ecf20Sopenharmony_ci .reg = SMBB_CHG_VIN_MIN, 2438c2ecf20Sopenharmony_ci .max = 9600000, 2448c2ecf20Sopenharmony_ci .min = 4200000, 2458c2ecf20Sopenharmony_ci .hw_fn = smbb_vin_fn, 2468c2ecf20Sopenharmony_ci }, 2478c2ecf20Sopenharmony_ci [ATTR_USBIN_IMAX] = { 2488c2ecf20Sopenharmony_ci .name = "usb-charge-current-limit", 2498c2ecf20Sopenharmony_ci .reg = SMBB_USB_IMAX, 2508c2ecf20Sopenharmony_ci .max = 2500000, 2518c2ecf20Sopenharmony_ci .min = 100000, 2528c2ecf20Sopenharmony_ci .hw_fn = smbb_imax_fn, 2538c2ecf20Sopenharmony_ci }, 2548c2ecf20Sopenharmony_ci}; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_cistatic int smbb_charger_attr_write(struct smbb_charger *chg, 2578c2ecf20Sopenharmony_ci enum smbb_attr which, unsigned int val) 2588c2ecf20Sopenharmony_ci{ 2598c2ecf20Sopenharmony_ci const struct smbb_charger_attr *prop; 2608c2ecf20Sopenharmony_ci unsigned int wval; 2618c2ecf20Sopenharmony_ci unsigned int out; 2628c2ecf20Sopenharmony_ci int rc; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci prop = &smbb_charger_attrs[which]; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (val > prop->max || val < prop->min) { 2678c2ecf20Sopenharmony_ci dev_err(chg->dev, "value out of range for %s [%u:%u]\n", 2688c2ecf20Sopenharmony_ci prop->name, prop->min, prop->max); 2698c2ecf20Sopenharmony_ci return -EINVAL; 2708c2ecf20Sopenharmony_ci } 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci if (prop->safe_reg) { 2738c2ecf20Sopenharmony_ci rc = regmap_read(chg->regmap, 2748c2ecf20Sopenharmony_ci chg->addr + prop->safe_reg, &wval); 2758c2ecf20Sopenharmony_ci if (rc) { 2768c2ecf20Sopenharmony_ci dev_err(chg->dev, 2778c2ecf20Sopenharmony_ci "unable to read safe value for '%s'\n", 2788c2ecf20Sopenharmony_ci prop->name); 2798c2ecf20Sopenharmony_ci return rc; 2808c2ecf20Sopenharmony_ci } 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci wval = prop->hw_fn(wval); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (val > wval) { 2858c2ecf20Sopenharmony_ci dev_warn(chg->dev, 2868c2ecf20Sopenharmony_ci "%s above safe value, clamping at %u\n", 2878c2ecf20Sopenharmony_ci prop->name, wval); 2888c2ecf20Sopenharmony_ci val = wval; 2898c2ecf20Sopenharmony_ci } 2908c2ecf20Sopenharmony_ci } 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci wval = smbb_hw_lookup(val, prop->hw_fn); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci rc = regmap_write(chg->regmap, chg->addr + prop->reg, wval); 2958c2ecf20Sopenharmony_ci if (rc) { 2968c2ecf20Sopenharmony_ci dev_err(chg->dev, "unable to update %s", prop->name); 2978c2ecf20Sopenharmony_ci return rc; 2988c2ecf20Sopenharmony_ci } 2998c2ecf20Sopenharmony_ci out = prop->hw_fn(wval); 3008c2ecf20Sopenharmony_ci if (out != val) { 3018c2ecf20Sopenharmony_ci dev_warn(chg->dev, 3028c2ecf20Sopenharmony_ci "%s inaccurate, rounded to %u\n", 3038c2ecf20Sopenharmony_ci prop->name, out); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci dev_dbg(chg->dev, "%s <= %d\n", prop->name, out); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci chg->attr[which] = out; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic int smbb_charger_attr_read(struct smbb_charger *chg, 3148c2ecf20Sopenharmony_ci enum smbb_attr which) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci const struct smbb_charger_attr *prop; 3178c2ecf20Sopenharmony_ci unsigned int val; 3188c2ecf20Sopenharmony_ci int rc; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci prop = &smbb_charger_attrs[which]; 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci rc = regmap_read(chg->regmap, chg->addr + prop->reg, &val); 3238c2ecf20Sopenharmony_ci if (rc) { 3248c2ecf20Sopenharmony_ci dev_err(chg->dev, "failed to read %s\n", prop->name); 3258c2ecf20Sopenharmony_ci return rc; 3268c2ecf20Sopenharmony_ci } 3278c2ecf20Sopenharmony_ci val = prop->hw_fn(val); 3288c2ecf20Sopenharmony_ci dev_dbg(chg->dev, "%s => %d\n", prop->name, val); 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci chg->attr[which] = val; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci return 0; 3338c2ecf20Sopenharmony_ci} 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic int smbb_charger_attr_parse(struct smbb_charger *chg, 3368c2ecf20Sopenharmony_ci enum smbb_attr which) 3378c2ecf20Sopenharmony_ci{ 3388c2ecf20Sopenharmony_ci const struct smbb_charger_attr *prop; 3398c2ecf20Sopenharmony_ci unsigned int val; 3408c2ecf20Sopenharmony_ci int rc; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci prop = &smbb_charger_attrs[which]; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci rc = of_property_read_u32(chg->dev->of_node, prop->name, &val); 3458c2ecf20Sopenharmony_ci if (rc == 0) { 3468c2ecf20Sopenharmony_ci rc = smbb_charger_attr_write(chg, which, val); 3478c2ecf20Sopenharmony_ci if (!rc || !prop->fail_ok) 3488c2ecf20Sopenharmony_ci return rc; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci return smbb_charger_attr_read(chg, which); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic void smbb_set_line_flag(struct smbb_charger *chg, int irq, int flag) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci bool state; 3568c2ecf20Sopenharmony_ci int ret; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, &state); 3598c2ecf20Sopenharmony_ci if (ret < 0) { 3608c2ecf20Sopenharmony_ci dev_err(chg->dev, "failed to read irq line\n"); 3618c2ecf20Sopenharmony_ci return; 3628c2ecf20Sopenharmony_ci } 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci mutex_lock(&chg->statlock); 3658c2ecf20Sopenharmony_ci if (state) 3668c2ecf20Sopenharmony_ci chg->status |= flag; 3678c2ecf20Sopenharmony_ci else 3688c2ecf20Sopenharmony_ci chg->status &= ~flag; 3698c2ecf20Sopenharmony_ci mutex_unlock(&chg->statlock); 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci dev_dbg(chg->dev, "status = %03lx\n", chg->status); 3728c2ecf20Sopenharmony_ci} 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_cistatic irqreturn_t smbb_usb_valid_handler(int irq, void *_data) 3758c2ecf20Sopenharmony_ci{ 3768c2ecf20Sopenharmony_ci struct smbb_charger *chg = _data; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID); 3798c2ecf20Sopenharmony_ci extcon_set_state_sync(chg->edev, EXTCON_USB, 3808c2ecf20Sopenharmony_ci chg->status & STATUS_USBIN_VALID); 3818c2ecf20Sopenharmony_ci power_supply_changed(chg->usb_psy); 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3848c2ecf20Sopenharmony_ci} 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cistatic irqreturn_t smbb_dc_valid_handler(int irq, void *_data) 3878c2ecf20Sopenharmony_ci{ 3888c2ecf20Sopenharmony_ci struct smbb_charger *chg = _data; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_DCIN_VALID); 3918c2ecf20Sopenharmony_ci if (!chg->dc_disabled) 3928c2ecf20Sopenharmony_ci power_supply_changed(chg->dc_psy); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3958c2ecf20Sopenharmony_ci} 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_cistatic irqreturn_t smbb_bat_temp_handler(int irq, void *_data) 3988c2ecf20Sopenharmony_ci{ 3998c2ecf20Sopenharmony_ci struct smbb_charger *chg = _data; 4008c2ecf20Sopenharmony_ci unsigned int val; 4018c2ecf20Sopenharmony_ci int rc; 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci rc = regmap_read(chg->regmap, chg->addr + SMBB_BAT_TEMP_STATUS, &val); 4048c2ecf20Sopenharmony_ci if (rc) 4058c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci mutex_lock(&chg->statlock); 4088c2ecf20Sopenharmony_ci if (val & TEMP_STATUS_OK) { 4098c2ecf20Sopenharmony_ci chg->status |= STATUS_BAT_OK; 4108c2ecf20Sopenharmony_ci } else { 4118c2ecf20Sopenharmony_ci chg->status &= ~STATUS_BAT_OK; 4128c2ecf20Sopenharmony_ci if (val & TEMP_STATUS_HOT) 4138c2ecf20Sopenharmony_ci chg->status |= STATUS_BAT_HOT; 4148c2ecf20Sopenharmony_ci } 4158c2ecf20Sopenharmony_ci mutex_unlock(&chg->statlock); 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci power_supply_changed(chg->bat_psy); 4188c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_cistatic irqreturn_t smbb_bat_present_handler(int irq, void *_data) 4228c2ecf20Sopenharmony_ci{ 4238c2ecf20Sopenharmony_ci struct smbb_charger *chg = _data; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_BAT_PRESENT); 4268c2ecf20Sopenharmony_ci power_supply_changed(chg->bat_psy); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4298c2ecf20Sopenharmony_ci} 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cistatic irqreturn_t smbb_chg_done_handler(int irq, void *_data) 4328c2ecf20Sopenharmony_ci{ 4338c2ecf20Sopenharmony_ci struct smbb_charger *chg = _data; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_CHG_DONE); 4368c2ecf20Sopenharmony_ci power_supply_changed(chg->bat_psy); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4398c2ecf20Sopenharmony_ci} 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_cistatic irqreturn_t smbb_chg_gone_handler(int irq, void *_data) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci struct smbb_charger *chg = _data; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_CHG_GONE); 4468c2ecf20Sopenharmony_ci power_supply_changed(chg->bat_psy); 4478c2ecf20Sopenharmony_ci power_supply_changed(chg->usb_psy); 4488c2ecf20Sopenharmony_ci if (!chg->dc_disabled) 4498c2ecf20Sopenharmony_ci power_supply_changed(chg->dc_psy); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4528c2ecf20Sopenharmony_ci} 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_cistatic irqreturn_t smbb_chg_fast_handler(int irq, void *_data) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci struct smbb_charger *chg = _data; 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_CHG_FAST); 4598c2ecf20Sopenharmony_ci power_supply_changed(chg->bat_psy); 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4628c2ecf20Sopenharmony_ci} 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_cistatic irqreturn_t smbb_chg_trkl_handler(int irq, void *_data) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci struct smbb_charger *chg = _data; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_CHG_TRKL); 4698c2ecf20Sopenharmony_ci power_supply_changed(chg->bat_psy); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic const struct smbb_irq { 4758c2ecf20Sopenharmony_ci const char *name; 4768c2ecf20Sopenharmony_ci irqreturn_t (*handler)(int, void *); 4778c2ecf20Sopenharmony_ci} smbb_charger_irqs[] = { 4788c2ecf20Sopenharmony_ci { "chg-done", smbb_chg_done_handler }, 4798c2ecf20Sopenharmony_ci { "chg-fast", smbb_chg_fast_handler }, 4808c2ecf20Sopenharmony_ci { "chg-trkl", smbb_chg_trkl_handler }, 4818c2ecf20Sopenharmony_ci { "bat-temp-ok", smbb_bat_temp_handler }, 4828c2ecf20Sopenharmony_ci { "bat-present", smbb_bat_present_handler }, 4838c2ecf20Sopenharmony_ci { "chg-gone", smbb_chg_gone_handler }, 4848c2ecf20Sopenharmony_ci { "usb-valid", smbb_usb_valid_handler }, 4858c2ecf20Sopenharmony_ci { "dc-valid", smbb_dc_valid_handler }, 4868c2ecf20Sopenharmony_ci}; 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic int smbb_usbin_get_property(struct power_supply *psy, 4898c2ecf20Sopenharmony_ci enum power_supply_property psp, 4908c2ecf20Sopenharmony_ci union power_supply_propval *val) 4918c2ecf20Sopenharmony_ci{ 4928c2ecf20Sopenharmony_ci struct smbb_charger *chg = power_supply_get_drvdata(psy); 4938c2ecf20Sopenharmony_ci int rc = 0; 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci switch (psp) { 4968c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 4978c2ecf20Sopenharmony_ci mutex_lock(&chg->statlock); 4988c2ecf20Sopenharmony_ci val->intval = !(chg->status & STATUS_CHG_GONE) && 4998c2ecf20Sopenharmony_ci (chg->status & STATUS_USBIN_VALID); 5008c2ecf20Sopenharmony_ci mutex_unlock(&chg->statlock); 5018c2ecf20Sopenharmony_ci break; 5028c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: 5038c2ecf20Sopenharmony_ci val->intval = chg->attr[ATTR_USBIN_IMAX]; 5048c2ecf20Sopenharmony_ci break; 5058c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: 5068c2ecf20Sopenharmony_ci val->intval = 2500000; 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci default: 5098c2ecf20Sopenharmony_ci rc = -EINVAL; 5108c2ecf20Sopenharmony_ci break; 5118c2ecf20Sopenharmony_ci } 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci return rc; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic int smbb_usbin_set_property(struct power_supply *psy, 5178c2ecf20Sopenharmony_ci enum power_supply_property psp, 5188c2ecf20Sopenharmony_ci const union power_supply_propval *val) 5198c2ecf20Sopenharmony_ci{ 5208c2ecf20Sopenharmony_ci struct smbb_charger *chg = power_supply_get_drvdata(psy); 5218c2ecf20Sopenharmony_ci int rc; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci switch (psp) { 5248c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: 5258c2ecf20Sopenharmony_ci rc = smbb_charger_attr_write(chg, ATTR_USBIN_IMAX, 5268c2ecf20Sopenharmony_ci val->intval); 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci default: 5298c2ecf20Sopenharmony_ci rc = -EINVAL; 5308c2ecf20Sopenharmony_ci break; 5318c2ecf20Sopenharmony_ci } 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci return rc; 5348c2ecf20Sopenharmony_ci} 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_cistatic int smbb_dcin_get_property(struct power_supply *psy, 5378c2ecf20Sopenharmony_ci enum power_supply_property psp, 5388c2ecf20Sopenharmony_ci union power_supply_propval *val) 5398c2ecf20Sopenharmony_ci{ 5408c2ecf20Sopenharmony_ci struct smbb_charger *chg = power_supply_get_drvdata(psy); 5418c2ecf20Sopenharmony_ci int rc = 0; 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci switch (psp) { 5448c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 5458c2ecf20Sopenharmony_ci mutex_lock(&chg->statlock); 5468c2ecf20Sopenharmony_ci val->intval = !(chg->status & STATUS_CHG_GONE) && 5478c2ecf20Sopenharmony_ci (chg->status & STATUS_DCIN_VALID); 5488c2ecf20Sopenharmony_ci mutex_unlock(&chg->statlock); 5498c2ecf20Sopenharmony_ci break; 5508c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: 5518c2ecf20Sopenharmony_ci val->intval = chg->attr[ATTR_DCIN_IMAX]; 5528c2ecf20Sopenharmony_ci break; 5538c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: 5548c2ecf20Sopenharmony_ci val->intval = 2500000; 5558c2ecf20Sopenharmony_ci break; 5568c2ecf20Sopenharmony_ci default: 5578c2ecf20Sopenharmony_ci rc = -EINVAL; 5588c2ecf20Sopenharmony_ci break; 5598c2ecf20Sopenharmony_ci } 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_ci return rc; 5628c2ecf20Sopenharmony_ci} 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_cistatic int smbb_dcin_set_property(struct power_supply *psy, 5658c2ecf20Sopenharmony_ci enum power_supply_property psp, 5668c2ecf20Sopenharmony_ci const union power_supply_propval *val) 5678c2ecf20Sopenharmony_ci{ 5688c2ecf20Sopenharmony_ci struct smbb_charger *chg = power_supply_get_drvdata(psy); 5698c2ecf20Sopenharmony_ci int rc; 5708c2ecf20Sopenharmony_ci 5718c2ecf20Sopenharmony_ci switch (psp) { 5728c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: 5738c2ecf20Sopenharmony_ci rc = smbb_charger_attr_write(chg, ATTR_DCIN_IMAX, 5748c2ecf20Sopenharmony_ci val->intval); 5758c2ecf20Sopenharmony_ci break; 5768c2ecf20Sopenharmony_ci default: 5778c2ecf20Sopenharmony_ci rc = -EINVAL; 5788c2ecf20Sopenharmony_ci break; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci return rc; 5828c2ecf20Sopenharmony_ci} 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_cistatic int smbb_charger_writable_property(struct power_supply *psy, 5858c2ecf20Sopenharmony_ci enum power_supply_property psp) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT; 5888c2ecf20Sopenharmony_ci} 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_cistatic int smbb_battery_get_property(struct power_supply *psy, 5918c2ecf20Sopenharmony_ci enum power_supply_property psp, 5928c2ecf20Sopenharmony_ci union power_supply_propval *val) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci struct smbb_charger *chg = power_supply_get_drvdata(psy); 5958c2ecf20Sopenharmony_ci unsigned long status; 5968c2ecf20Sopenharmony_ci int rc = 0; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci mutex_lock(&chg->statlock); 5998c2ecf20Sopenharmony_ci status = chg->status; 6008c2ecf20Sopenharmony_ci mutex_unlock(&chg->statlock); 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_ci switch (psp) { 6038c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 6048c2ecf20Sopenharmony_ci if (status & STATUS_CHG_GONE) 6058c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 6068c2ecf20Sopenharmony_ci else if (!(status & (STATUS_DCIN_VALID | STATUS_USBIN_VALID))) 6078c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 6088c2ecf20Sopenharmony_ci else if (status & STATUS_CHG_DONE) 6098c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_FULL; 6108c2ecf20Sopenharmony_ci else if (!(status & STATUS_BAT_OK)) 6118c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 6128c2ecf20Sopenharmony_ci else if (status & (STATUS_CHG_FAST | STATUS_CHG_TRKL)) 6138c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 6148c2ecf20Sopenharmony_ci else /* everything is ok for charging, but we are not... */ 6158c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 6168c2ecf20Sopenharmony_ci break; 6178c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 6188c2ecf20Sopenharmony_ci if (status & STATUS_BAT_OK) 6198c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_GOOD; 6208c2ecf20Sopenharmony_ci else if (status & STATUS_BAT_HOT) 6218c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 6228c2ecf20Sopenharmony_ci else 6238c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_COLD; 6248c2ecf20Sopenharmony_ci break; 6258c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TYPE: 6268c2ecf20Sopenharmony_ci if (status & STATUS_CHG_FAST) 6278c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; 6288c2ecf20Sopenharmony_ci else if (status & STATUS_CHG_TRKL) 6298c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 6308c2ecf20Sopenharmony_ci else 6318c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 6328c2ecf20Sopenharmony_ci break; 6338c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 6348c2ecf20Sopenharmony_ci val->intval = !!(status & STATUS_BAT_PRESENT); 6358c2ecf20Sopenharmony_ci break; 6368c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 6378c2ecf20Sopenharmony_ci val->intval = chg->attr[ATTR_BAT_IMAX]; 6388c2ecf20Sopenharmony_ci break; 6398c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX: 6408c2ecf20Sopenharmony_ci val->intval = chg->attr[ATTR_BAT_VMAX]; 6418c2ecf20Sopenharmony_ci break; 6428c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 6438c2ecf20Sopenharmony_ci /* this charger is a single-cell lithium-ion battery charger 6448c2ecf20Sopenharmony_ci * only. If you hook up some other technology, there will be 6458c2ecf20Sopenharmony_ci * fireworks. 6468c2ecf20Sopenharmony_ci */ 6478c2ecf20Sopenharmony_ci val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 6488c2ecf20Sopenharmony_ci break; 6498c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 6508c2ecf20Sopenharmony_ci val->intval = 3000000; /* single-cell li-ion low end */ 6518c2ecf20Sopenharmony_ci break; 6528c2ecf20Sopenharmony_ci default: 6538c2ecf20Sopenharmony_ci rc = -EINVAL; 6548c2ecf20Sopenharmony_ci break; 6558c2ecf20Sopenharmony_ci } 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return rc; 6588c2ecf20Sopenharmony_ci} 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_cistatic int smbb_battery_set_property(struct power_supply *psy, 6618c2ecf20Sopenharmony_ci enum power_supply_property psp, 6628c2ecf20Sopenharmony_ci const union power_supply_propval *val) 6638c2ecf20Sopenharmony_ci{ 6648c2ecf20Sopenharmony_ci struct smbb_charger *chg = power_supply_get_drvdata(psy); 6658c2ecf20Sopenharmony_ci int rc; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci switch (psp) { 6688c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 6698c2ecf20Sopenharmony_ci rc = smbb_charger_attr_write(chg, ATTR_BAT_IMAX, val->intval); 6708c2ecf20Sopenharmony_ci break; 6718c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX: 6728c2ecf20Sopenharmony_ci rc = smbb_charger_attr_write(chg, ATTR_BAT_VMAX, val->intval); 6738c2ecf20Sopenharmony_ci break; 6748c2ecf20Sopenharmony_ci default: 6758c2ecf20Sopenharmony_ci rc = -EINVAL; 6768c2ecf20Sopenharmony_ci break; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci return rc; 6808c2ecf20Sopenharmony_ci} 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic int smbb_battery_writable_property(struct power_supply *psy, 6838c2ecf20Sopenharmony_ci enum power_supply_property psp) 6848c2ecf20Sopenharmony_ci{ 6858c2ecf20Sopenharmony_ci switch (psp) { 6868c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 6878c2ecf20Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX: 6888c2ecf20Sopenharmony_ci return 1; 6898c2ecf20Sopenharmony_ci default: 6908c2ecf20Sopenharmony_ci return 0; 6918c2ecf20Sopenharmony_ci } 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic enum power_supply_property smbb_charger_properties[] = { 6958c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 6968c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, 6978c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, 6988c2ecf20Sopenharmony_ci}; 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_cistatic enum power_supply_property smbb_battery_properties[] = { 7018c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 7028c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 7038c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 7048c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TYPE, 7058c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_MAX, 7068c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MAX, 7078c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 7088c2ecf20Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 7098c2ecf20Sopenharmony_ci}; 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic const struct reg_off_mask_default { 7128c2ecf20Sopenharmony_ci unsigned int offset; 7138c2ecf20Sopenharmony_ci unsigned int mask; 7148c2ecf20Sopenharmony_ci unsigned int value; 7158c2ecf20Sopenharmony_ci unsigned int rev_mask; 7168c2ecf20Sopenharmony_ci} smbb_charger_setup[] = { 7178c2ecf20Sopenharmony_ci /* The bootloader is supposed to set this... make sure anyway. */ 7188c2ecf20Sopenharmony_ci { SMBB_MISC_BOOT_DONE, BOOT_DONE, BOOT_DONE }, 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci /* Disable software timer */ 7218c2ecf20Sopenharmony_ci { SMBB_CHG_TCHG_MAX_EN, TCHG_MAX_EN, 0 }, 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci /* Clear and disable watchdog */ 7248c2ecf20Sopenharmony_ci { SMBB_CHG_WDOG_TIME, 0xff, 160 }, 7258c2ecf20Sopenharmony_ci { SMBB_CHG_WDOG_EN, WDOG_EN, 0 }, 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci /* Use charger based EoC detection */ 7288c2ecf20Sopenharmony_ci { SMBB_CHG_IBAT_TERM_CHG, IBAT_TERM_CHG_IEOC, IBAT_TERM_CHG_IEOC_CHG }, 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_ci /* Disable GSM PA load adjustment. 7318c2ecf20Sopenharmony_ci * The PA signal is incorrectly connected on v2. 7328c2ecf20Sopenharmony_ci */ 7338c2ecf20Sopenharmony_ci { SMBB_CHG_CFG, 0xff, 0x00, BIT(3) }, 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci /* Use VBAT (not VSYS) to compensate for IR drop during fast charging */ 7368c2ecf20Sopenharmony_ci { SMBB_BUCK_REG_MODE, BUCK_REG_MODE, BUCK_REG_MODE_VBAT }, 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* Enable battery temperature comparators */ 7398c2ecf20Sopenharmony_ci { SMBB_BAT_BTC_CTRL, BTC_CTRL_COMP_EN, BTC_CTRL_COMP_EN }, 7408c2ecf20Sopenharmony_ci 7418c2ecf20Sopenharmony_ci /* Stop USB enumeration timer */ 7428c2ecf20Sopenharmony_ci { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP }, 7438c2ecf20Sopenharmony_ci 7448c2ecf20Sopenharmony_ci#if 0 /* FIXME supposedly only to disable hardware ARB termination */ 7458c2ecf20Sopenharmony_ci { SMBB_USB_SEC_ACCESS, SEC_ACCESS_MAGIC }, 7468c2ecf20Sopenharmony_ci { SMBB_USB_REV_BST, 0xff, REV_BST_CHG_GONE }, 7478c2ecf20Sopenharmony_ci#endif 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_ci /* Stop USB enumeration timer, again */ 7508c2ecf20Sopenharmony_ci { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP }, 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci /* Enable charging */ 7538c2ecf20Sopenharmony_ci { SMBB_CHG_CTRL, CTRL_EN, CTRL_EN }, 7548c2ecf20Sopenharmony_ci}; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic char *smbb_bif[] = { "smbb-bif" }; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_cistatic const struct power_supply_desc bat_psy_desc = { 7598c2ecf20Sopenharmony_ci .name = "smbb-bif", 7608c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 7618c2ecf20Sopenharmony_ci .properties = smbb_battery_properties, 7628c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(smbb_battery_properties), 7638c2ecf20Sopenharmony_ci .get_property = smbb_battery_get_property, 7648c2ecf20Sopenharmony_ci .set_property = smbb_battery_set_property, 7658c2ecf20Sopenharmony_ci .property_is_writeable = smbb_battery_writable_property, 7668c2ecf20Sopenharmony_ci}; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_cistatic const struct power_supply_desc usb_psy_desc = { 7698c2ecf20Sopenharmony_ci .name = "smbb-usbin", 7708c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 7718c2ecf20Sopenharmony_ci .properties = smbb_charger_properties, 7728c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(smbb_charger_properties), 7738c2ecf20Sopenharmony_ci .get_property = smbb_usbin_get_property, 7748c2ecf20Sopenharmony_ci .set_property = smbb_usbin_set_property, 7758c2ecf20Sopenharmony_ci .property_is_writeable = smbb_charger_writable_property, 7768c2ecf20Sopenharmony_ci}; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_cistatic const struct power_supply_desc dc_psy_desc = { 7798c2ecf20Sopenharmony_ci .name = "smbb-dcin", 7808c2ecf20Sopenharmony_ci .type = POWER_SUPPLY_TYPE_MAINS, 7818c2ecf20Sopenharmony_ci .properties = smbb_charger_properties, 7828c2ecf20Sopenharmony_ci .num_properties = ARRAY_SIZE(smbb_charger_properties), 7838c2ecf20Sopenharmony_ci .get_property = smbb_dcin_get_property, 7848c2ecf20Sopenharmony_ci .set_property = smbb_dcin_set_property, 7858c2ecf20Sopenharmony_ci .property_is_writeable = smbb_charger_writable_property, 7868c2ecf20Sopenharmony_ci}; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic int smbb_chg_otg_enable(struct regulator_dev *rdev) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci struct smbb_charger *chg = rdev_get_drvdata(rdev); 7918c2ecf20Sopenharmony_ci int rc; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_USB_OTG_CTL, 7948c2ecf20Sopenharmony_ci OTG_CTL_EN, OTG_CTL_EN); 7958c2ecf20Sopenharmony_ci if (rc) 7968c2ecf20Sopenharmony_ci dev_err(chg->dev, "failed to update OTG_CTL\n"); 7978c2ecf20Sopenharmony_ci return rc; 7988c2ecf20Sopenharmony_ci} 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_cistatic int smbb_chg_otg_disable(struct regulator_dev *rdev) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci struct smbb_charger *chg = rdev_get_drvdata(rdev); 8038c2ecf20Sopenharmony_ci int rc; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_USB_OTG_CTL, 8068c2ecf20Sopenharmony_ci OTG_CTL_EN, 0); 8078c2ecf20Sopenharmony_ci if (rc) 8088c2ecf20Sopenharmony_ci dev_err(chg->dev, "failed to update OTG_CTL\n"); 8098c2ecf20Sopenharmony_ci return rc; 8108c2ecf20Sopenharmony_ci} 8118c2ecf20Sopenharmony_ci 8128c2ecf20Sopenharmony_cistatic int smbb_chg_otg_is_enabled(struct regulator_dev *rdev) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci struct smbb_charger *chg = rdev_get_drvdata(rdev); 8158c2ecf20Sopenharmony_ci unsigned int value = 0; 8168c2ecf20Sopenharmony_ci int rc; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci rc = regmap_read(chg->regmap, chg->addr + SMBB_USB_OTG_CTL, &value); 8198c2ecf20Sopenharmony_ci if (rc) 8208c2ecf20Sopenharmony_ci dev_err(chg->dev, "failed to read OTG_CTL\n"); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci return !!(value & OTG_CTL_EN); 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic const struct regulator_ops smbb_chg_otg_ops = { 8268c2ecf20Sopenharmony_ci .enable = smbb_chg_otg_enable, 8278c2ecf20Sopenharmony_ci .disable = smbb_chg_otg_disable, 8288c2ecf20Sopenharmony_ci .is_enabled = smbb_chg_otg_is_enabled, 8298c2ecf20Sopenharmony_ci}; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic int smbb_charger_probe(struct platform_device *pdev) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci struct power_supply_config bat_cfg = {}; 8348c2ecf20Sopenharmony_ci struct power_supply_config usb_cfg = {}; 8358c2ecf20Sopenharmony_ci struct power_supply_config dc_cfg = {}; 8368c2ecf20Sopenharmony_ci struct smbb_charger *chg; 8378c2ecf20Sopenharmony_ci struct regulator_config config = { }; 8388c2ecf20Sopenharmony_ci int rc, i; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL); 8418c2ecf20Sopenharmony_ci if (!chg) 8428c2ecf20Sopenharmony_ci return -ENOMEM; 8438c2ecf20Sopenharmony_ci 8448c2ecf20Sopenharmony_ci chg->dev = &pdev->dev; 8458c2ecf20Sopenharmony_ci mutex_init(&chg->statlock); 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_ci chg->regmap = dev_get_regmap(pdev->dev.parent, NULL); 8488c2ecf20Sopenharmony_ci if (!chg->regmap) { 8498c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to locate regmap\n"); 8508c2ecf20Sopenharmony_ci return -ENODEV; 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci rc = of_property_read_u32(pdev->dev.of_node, "reg", &chg->addr); 8548c2ecf20Sopenharmony_ci if (rc) { 8558c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "missing or invalid 'reg' property\n"); 8568c2ecf20Sopenharmony_ci return rc; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci rc = regmap_read(chg->regmap, chg->addr + SMBB_MISC_REV2, &chg->revision); 8608c2ecf20Sopenharmony_ci if (rc) { 8618c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to read revision\n"); 8628c2ecf20Sopenharmony_ci return rc; 8638c2ecf20Sopenharmony_ci } 8648c2ecf20Sopenharmony_ci 8658c2ecf20Sopenharmony_ci chg->revision += 1; 8668c2ecf20Sopenharmony_ci if (chg->revision != 2 && chg->revision != 3) { 8678c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "v1 hardware not supported\n"); 8688c2ecf20Sopenharmony_ci return -ENODEV; 8698c2ecf20Sopenharmony_ci } 8708c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "Initializing SMBB rev %u", chg->revision); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_ci chg->dc_disabled = of_property_read_bool(pdev->dev.of_node, "qcom,disable-dc"); 8738c2ecf20Sopenharmony_ci 8748c2ecf20Sopenharmony_ci for (i = 0; i < _ATTR_CNT; ++i) { 8758c2ecf20Sopenharmony_ci rc = smbb_charger_attr_parse(chg, i); 8768c2ecf20Sopenharmony_ci if (rc) { 8778c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to parse/apply settings\n"); 8788c2ecf20Sopenharmony_ci return rc; 8798c2ecf20Sopenharmony_ci } 8808c2ecf20Sopenharmony_ci } 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci bat_cfg.drv_data = chg; 8838c2ecf20Sopenharmony_ci bat_cfg.of_node = pdev->dev.of_node; 8848c2ecf20Sopenharmony_ci chg->bat_psy = devm_power_supply_register(&pdev->dev, 8858c2ecf20Sopenharmony_ci &bat_psy_desc, 8868c2ecf20Sopenharmony_ci &bat_cfg); 8878c2ecf20Sopenharmony_ci if (IS_ERR(chg->bat_psy)) { 8888c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register battery\n"); 8898c2ecf20Sopenharmony_ci return PTR_ERR(chg->bat_psy); 8908c2ecf20Sopenharmony_ci } 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci usb_cfg.drv_data = chg; 8938c2ecf20Sopenharmony_ci usb_cfg.supplied_to = smbb_bif; 8948c2ecf20Sopenharmony_ci usb_cfg.num_supplicants = ARRAY_SIZE(smbb_bif); 8958c2ecf20Sopenharmony_ci chg->usb_psy = devm_power_supply_register(&pdev->dev, 8968c2ecf20Sopenharmony_ci &usb_psy_desc, 8978c2ecf20Sopenharmony_ci &usb_cfg); 8988c2ecf20Sopenharmony_ci if (IS_ERR(chg->usb_psy)) { 8998c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register USB power supply\n"); 9008c2ecf20Sopenharmony_ci return PTR_ERR(chg->usb_psy); 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci chg->edev = devm_extcon_dev_allocate(&pdev->dev, smbb_usb_extcon_cable); 9048c2ecf20Sopenharmony_ci if (IS_ERR(chg->edev)) { 9058c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate extcon device\n"); 9068c2ecf20Sopenharmony_ci return -ENOMEM; 9078c2ecf20Sopenharmony_ci } 9088c2ecf20Sopenharmony_ci 9098c2ecf20Sopenharmony_ci rc = devm_extcon_dev_register(&pdev->dev, chg->edev); 9108c2ecf20Sopenharmony_ci if (rc < 0) { 9118c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register extcon device\n"); 9128c2ecf20Sopenharmony_ci return rc; 9138c2ecf20Sopenharmony_ci } 9148c2ecf20Sopenharmony_ci 9158c2ecf20Sopenharmony_ci if (!chg->dc_disabled) { 9168c2ecf20Sopenharmony_ci dc_cfg.drv_data = chg; 9178c2ecf20Sopenharmony_ci dc_cfg.supplied_to = smbb_bif; 9188c2ecf20Sopenharmony_ci dc_cfg.num_supplicants = ARRAY_SIZE(smbb_bif); 9198c2ecf20Sopenharmony_ci chg->dc_psy = devm_power_supply_register(&pdev->dev, 9208c2ecf20Sopenharmony_ci &dc_psy_desc, 9218c2ecf20Sopenharmony_ci &dc_cfg); 9228c2ecf20Sopenharmony_ci if (IS_ERR(chg->dc_psy)) { 9238c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to register DC power supply\n"); 9248c2ecf20Sopenharmony_ci return PTR_ERR(chg->dc_psy); 9258c2ecf20Sopenharmony_ci } 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(smbb_charger_irqs); ++i) { 9298c2ecf20Sopenharmony_ci int irq; 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name); 9328c2ecf20Sopenharmony_ci if (irq < 0) { 9338c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to get irq '%s'\n", 9348c2ecf20Sopenharmony_ci smbb_charger_irqs[i].name); 9358c2ecf20Sopenharmony_ci return irq; 9368c2ecf20Sopenharmony_ci } 9378c2ecf20Sopenharmony_ci 9388c2ecf20Sopenharmony_ci smbb_charger_irqs[i].handler(irq, chg); 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci rc = devm_request_threaded_irq(&pdev->dev, irq, NULL, 9418c2ecf20Sopenharmony_ci smbb_charger_irqs[i].handler, IRQF_ONESHOT, 9428c2ecf20Sopenharmony_ci smbb_charger_irqs[i].name, chg); 9438c2ecf20Sopenharmony_ci if (rc) { 9448c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "failed to request irq '%s'\n", 9458c2ecf20Sopenharmony_ci smbb_charger_irqs[i].name); 9468c2ecf20Sopenharmony_ci return rc; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci /* 9518c2ecf20Sopenharmony_ci * otg regulator is used to control VBUS voltage direction 9528c2ecf20Sopenharmony_ci * when USB switches between host and gadget mode 9538c2ecf20Sopenharmony_ci */ 9548c2ecf20Sopenharmony_ci chg->otg_rdesc.id = -1; 9558c2ecf20Sopenharmony_ci chg->otg_rdesc.name = "otg-vbus"; 9568c2ecf20Sopenharmony_ci chg->otg_rdesc.ops = &smbb_chg_otg_ops; 9578c2ecf20Sopenharmony_ci chg->otg_rdesc.owner = THIS_MODULE; 9588c2ecf20Sopenharmony_ci chg->otg_rdesc.type = REGULATOR_VOLTAGE; 9598c2ecf20Sopenharmony_ci chg->otg_rdesc.supply_name = "usb-otg-in"; 9608c2ecf20Sopenharmony_ci chg->otg_rdesc.of_match = "otg-vbus"; 9618c2ecf20Sopenharmony_ci 9628c2ecf20Sopenharmony_ci config.dev = &pdev->dev; 9638c2ecf20Sopenharmony_ci config.driver_data = chg; 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci chg->otg_reg = devm_regulator_register(&pdev->dev, &chg->otg_rdesc, 9668c2ecf20Sopenharmony_ci &config); 9678c2ecf20Sopenharmony_ci if (IS_ERR(chg->otg_reg)) 9688c2ecf20Sopenharmony_ci return PTR_ERR(chg->otg_reg); 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node, 9718c2ecf20Sopenharmony_ci "qcom,jeita-extended-temp-range"); 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_ci /* Set temperature range to [35%:70%] or [25%:80%] accordingly */ 9748c2ecf20Sopenharmony_ci rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_BAT_BTC_CTRL, 9758c2ecf20Sopenharmony_ci BTC_CTRL_COLD_EXT | BTC_CTRL_HOT_EXT_N, 9768c2ecf20Sopenharmony_ci chg->jeita_ext_temp ? 9778c2ecf20Sopenharmony_ci BTC_CTRL_COLD_EXT : 9788c2ecf20Sopenharmony_ci BTC_CTRL_HOT_EXT_N); 9798c2ecf20Sopenharmony_ci if (rc) { 9808c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 9818c2ecf20Sopenharmony_ci "unable to set %s temperature range\n", 9828c2ecf20Sopenharmony_ci chg->jeita_ext_temp ? "JEITA extended" : "normal"); 9838c2ecf20Sopenharmony_ci return rc; 9848c2ecf20Sopenharmony_ci } 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(smbb_charger_setup); ++i) { 9878c2ecf20Sopenharmony_ci const struct reg_off_mask_default *r = &smbb_charger_setup[i]; 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci if (r->rev_mask & BIT(chg->revision)) 9908c2ecf20Sopenharmony_ci continue; 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci rc = regmap_update_bits(chg->regmap, chg->addr + r->offset, 9938c2ecf20Sopenharmony_ci r->mask, r->value); 9948c2ecf20Sopenharmony_ci if (rc) { 9958c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 9968c2ecf20Sopenharmony_ci "unable to initializing charging, bailing\n"); 9978c2ecf20Sopenharmony_ci return rc; 9988c2ecf20Sopenharmony_ci } 9998c2ecf20Sopenharmony_ci } 10008c2ecf20Sopenharmony_ci 10018c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, chg); 10028c2ecf20Sopenharmony_ci 10038c2ecf20Sopenharmony_ci return 0; 10048c2ecf20Sopenharmony_ci} 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_cistatic int smbb_charger_remove(struct platform_device *pdev) 10078c2ecf20Sopenharmony_ci{ 10088c2ecf20Sopenharmony_ci struct smbb_charger *chg; 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci chg = platform_get_drvdata(pdev); 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci regmap_update_bits(chg->regmap, chg->addr + SMBB_CHG_CTRL, CTRL_EN, 0); 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci return 0; 10158c2ecf20Sopenharmony_ci} 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_cistatic const struct of_device_id smbb_charger_id_table[] = { 10188c2ecf20Sopenharmony_ci { .compatible = "qcom,pm8941-charger" }, 10198c2ecf20Sopenharmony_ci { } 10208c2ecf20Sopenharmony_ci}; 10218c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, smbb_charger_id_table); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_cistatic struct platform_driver smbb_charger_driver = { 10248c2ecf20Sopenharmony_ci .probe = smbb_charger_probe, 10258c2ecf20Sopenharmony_ci .remove = smbb_charger_remove, 10268c2ecf20Sopenharmony_ci .driver = { 10278c2ecf20Sopenharmony_ci .name = "qcom-smbb", 10288c2ecf20Sopenharmony_ci .of_match_table = smbb_charger_id_table, 10298c2ecf20Sopenharmony_ci }, 10308c2ecf20Sopenharmony_ci}; 10318c2ecf20Sopenharmony_cimodule_platform_driver(smbb_charger_driver); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm Switch-Mode Battery Charger and Boost driver"); 10348c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1035