162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* Copyright (c) 2014, Sony Mobile Communications Inc. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This driver is for the multi-block Switch-Mode Battery Charger and Boost 562306a36Sopenharmony_ci * (SMBB) hardware, found in Qualcomm PM8941 PMICs. The charger is an 662306a36Sopenharmony_ci * integrated, single-cell lithium-ion battery charger. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Sub-components: 962306a36Sopenharmony_ci * - Charger core 1062306a36Sopenharmony_ci * - Buck 1162306a36Sopenharmony_ci * - DC charge-path 1262306a36Sopenharmony_ci * - USB charge-path 1362306a36Sopenharmony_ci * - Battery interface 1462306a36Sopenharmony_ci * - Boost (not implemented) 1562306a36Sopenharmony_ci * - Misc 1662306a36Sopenharmony_ci * - HF-Buck 1762306a36Sopenharmony_ci */ 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <linux/errno.h> 2062306a36Sopenharmony_ci#include <linux/interrupt.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/module.h> 2362306a36Sopenharmony_ci#include <linux/mutex.h> 2462306a36Sopenharmony_ci#include <linux/of.h> 2562306a36Sopenharmony_ci#include <linux/platform_device.h> 2662306a36Sopenharmony_ci#include <linux/power_supply.h> 2762306a36Sopenharmony_ci#include <linux/regmap.h> 2862306a36Sopenharmony_ci#include <linux/slab.h> 2962306a36Sopenharmony_ci#include <linux/extcon-provider.h> 3062306a36Sopenharmony_ci#include <linux/regulator/driver.h> 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci#define SMBB_CHG_VMAX 0x040 3362306a36Sopenharmony_ci#define SMBB_CHG_VSAFE 0x041 3462306a36Sopenharmony_ci#define SMBB_CHG_CFG 0x043 3562306a36Sopenharmony_ci#define SMBB_CHG_IMAX 0x044 3662306a36Sopenharmony_ci#define SMBB_CHG_ISAFE 0x045 3762306a36Sopenharmony_ci#define SMBB_CHG_VIN_MIN 0x047 3862306a36Sopenharmony_ci#define SMBB_CHG_CTRL 0x049 3962306a36Sopenharmony_ci#define CTRL_EN BIT(7) 4062306a36Sopenharmony_ci#define SMBB_CHG_VBAT_WEAK 0x052 4162306a36Sopenharmony_ci#define SMBB_CHG_IBAT_TERM_CHG 0x05b 4262306a36Sopenharmony_ci#define IBAT_TERM_CHG_IEOC BIT(7) 4362306a36Sopenharmony_ci#define IBAT_TERM_CHG_IEOC_BMS BIT(7) 4462306a36Sopenharmony_ci#define IBAT_TERM_CHG_IEOC_CHG 0 4562306a36Sopenharmony_ci#define SMBB_CHG_VBAT_DET 0x05d 4662306a36Sopenharmony_ci#define SMBB_CHG_TCHG_MAX_EN 0x060 4762306a36Sopenharmony_ci#define TCHG_MAX_EN BIT(7) 4862306a36Sopenharmony_ci#define SMBB_CHG_WDOG_TIME 0x062 4962306a36Sopenharmony_ci#define SMBB_CHG_WDOG_EN 0x065 5062306a36Sopenharmony_ci#define WDOG_EN BIT(7) 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define SMBB_BUCK_REG_MODE 0x174 5362306a36Sopenharmony_ci#define BUCK_REG_MODE BIT(0) 5462306a36Sopenharmony_ci#define BUCK_REG_MODE_VBAT BIT(0) 5562306a36Sopenharmony_ci#define BUCK_REG_MODE_VSYS 0 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define SMBB_BAT_PRES_STATUS 0x208 5862306a36Sopenharmony_ci#define PRES_STATUS_BAT_PRES BIT(7) 5962306a36Sopenharmony_ci#define SMBB_BAT_TEMP_STATUS 0x209 6062306a36Sopenharmony_ci#define TEMP_STATUS_OK BIT(7) 6162306a36Sopenharmony_ci#define TEMP_STATUS_HOT BIT(6) 6262306a36Sopenharmony_ci#define SMBB_BAT_BTC_CTRL 0x249 6362306a36Sopenharmony_ci#define BTC_CTRL_COMP_EN BIT(7) 6462306a36Sopenharmony_ci#define BTC_CTRL_COLD_EXT BIT(1) 6562306a36Sopenharmony_ci#define BTC_CTRL_HOT_EXT_N BIT(0) 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define SMBB_USB_IMAX 0x344 6862306a36Sopenharmony_ci#define SMBB_USB_OTG_CTL 0x348 6962306a36Sopenharmony_ci#define OTG_CTL_EN BIT(0) 7062306a36Sopenharmony_ci#define SMBB_USB_ENUM_TIMER_STOP 0x34e 7162306a36Sopenharmony_ci#define ENUM_TIMER_STOP BIT(0) 7262306a36Sopenharmony_ci#define SMBB_USB_SEC_ACCESS 0x3d0 7362306a36Sopenharmony_ci#define SEC_ACCESS_MAGIC 0xa5 7462306a36Sopenharmony_ci#define SMBB_USB_REV_BST 0x3ed 7562306a36Sopenharmony_ci#define REV_BST_CHG_GONE BIT(7) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define SMBB_DC_IMAX 0x444 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define SMBB_MISC_REV2 0x601 8062306a36Sopenharmony_ci#define SMBB_MISC_BOOT_DONE 0x642 8162306a36Sopenharmony_ci#define BOOT_DONE BIT(7) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define STATUS_USBIN_VALID BIT(0) /* USB connection is valid */ 8462306a36Sopenharmony_ci#define STATUS_DCIN_VALID BIT(1) /* DC connection is valid */ 8562306a36Sopenharmony_ci#define STATUS_BAT_HOT BIT(2) /* Battery temp 1=Hot, 0=Cold */ 8662306a36Sopenharmony_ci#define STATUS_BAT_OK BIT(3) /* Battery temp OK */ 8762306a36Sopenharmony_ci#define STATUS_BAT_PRESENT BIT(4) /* Battery is present */ 8862306a36Sopenharmony_ci#define STATUS_CHG_DONE BIT(5) /* Charge cycle is complete */ 8962306a36Sopenharmony_ci#define STATUS_CHG_TRKL BIT(6) /* Trickle charging */ 9062306a36Sopenharmony_ci#define STATUS_CHG_FAST BIT(7) /* Fast charging */ 9162306a36Sopenharmony_ci#define STATUS_CHG_GONE BIT(8) /* No charger is connected */ 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cienum smbb_attr { 9462306a36Sopenharmony_ci ATTR_BAT_ISAFE, 9562306a36Sopenharmony_ci ATTR_BAT_IMAX, 9662306a36Sopenharmony_ci ATTR_USBIN_IMAX, 9762306a36Sopenharmony_ci ATTR_DCIN_IMAX, 9862306a36Sopenharmony_ci ATTR_BAT_VSAFE, 9962306a36Sopenharmony_ci ATTR_BAT_VMAX, 10062306a36Sopenharmony_ci ATTR_BAT_VMIN, 10162306a36Sopenharmony_ci ATTR_CHG_VDET, 10262306a36Sopenharmony_ci ATTR_VIN_MIN, 10362306a36Sopenharmony_ci _ATTR_CNT, 10462306a36Sopenharmony_ci}; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_cistruct smbb_charger { 10762306a36Sopenharmony_ci unsigned int revision; 10862306a36Sopenharmony_ci unsigned int addr; 10962306a36Sopenharmony_ci struct device *dev; 11062306a36Sopenharmony_ci struct extcon_dev *edev; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci bool dc_disabled; 11362306a36Sopenharmony_ci bool jeita_ext_temp; 11462306a36Sopenharmony_ci unsigned long status; 11562306a36Sopenharmony_ci struct mutex statlock; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci unsigned int attr[_ATTR_CNT]; 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci struct power_supply *usb_psy; 12062306a36Sopenharmony_ci struct power_supply *dc_psy; 12162306a36Sopenharmony_ci struct power_supply *bat_psy; 12262306a36Sopenharmony_ci struct regmap *regmap; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci struct regulator_desc otg_rdesc; 12562306a36Sopenharmony_ci struct regulator_dev *otg_reg; 12662306a36Sopenharmony_ci}; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic const unsigned int smbb_usb_extcon_cable[] = { 12962306a36Sopenharmony_ci EXTCON_USB, 13062306a36Sopenharmony_ci EXTCON_NONE, 13162306a36Sopenharmony_ci}; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic int smbb_vbat_weak_fn(unsigned int index) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci return 2100000 + index * 100000; 13662306a36Sopenharmony_ci} 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_cistatic int smbb_vin_fn(unsigned int index) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci if (index > 42) 14162306a36Sopenharmony_ci return 5600000 + (index - 43) * 200000; 14262306a36Sopenharmony_ci return 3400000 + index * 50000; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int smbb_vmax_fn(unsigned int index) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci return 3240000 + index * 10000; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int smbb_vbat_det_fn(unsigned int index) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci return 3240000 + index * 20000; 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int smbb_imax_fn(unsigned int index) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci if (index < 2) 15862306a36Sopenharmony_ci return 100000 + index * 50000; 15962306a36Sopenharmony_ci return index * 100000; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic int smbb_bat_imax_fn(unsigned int index) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci return index * 50000; 16562306a36Sopenharmony_ci} 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cistatic unsigned int smbb_hw_lookup(unsigned int val, int (*fn)(unsigned int)) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci unsigned int widx; 17062306a36Sopenharmony_ci unsigned int sel; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci for (widx = sel = 0; (*fn)(widx) <= val; ++widx) 17362306a36Sopenharmony_ci sel = widx; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci return sel; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_cistatic const struct smbb_charger_attr { 17962306a36Sopenharmony_ci const char *name; 18062306a36Sopenharmony_ci unsigned int reg; 18162306a36Sopenharmony_ci unsigned int safe_reg; 18262306a36Sopenharmony_ci unsigned int max; 18362306a36Sopenharmony_ci unsigned int min; 18462306a36Sopenharmony_ci unsigned int fail_ok; 18562306a36Sopenharmony_ci int (*hw_fn)(unsigned int); 18662306a36Sopenharmony_ci} smbb_charger_attrs[] = { 18762306a36Sopenharmony_ci [ATTR_BAT_ISAFE] = { 18862306a36Sopenharmony_ci .name = "qcom,fast-charge-safe-current", 18962306a36Sopenharmony_ci .reg = SMBB_CHG_ISAFE, 19062306a36Sopenharmony_ci .max = 3000000, 19162306a36Sopenharmony_ci .min = 200000, 19262306a36Sopenharmony_ci .hw_fn = smbb_bat_imax_fn, 19362306a36Sopenharmony_ci .fail_ok = 1, 19462306a36Sopenharmony_ci }, 19562306a36Sopenharmony_ci [ATTR_BAT_IMAX] = { 19662306a36Sopenharmony_ci .name = "qcom,fast-charge-current-limit", 19762306a36Sopenharmony_ci .reg = SMBB_CHG_IMAX, 19862306a36Sopenharmony_ci .safe_reg = SMBB_CHG_ISAFE, 19962306a36Sopenharmony_ci .max = 3000000, 20062306a36Sopenharmony_ci .min = 200000, 20162306a36Sopenharmony_ci .hw_fn = smbb_bat_imax_fn, 20262306a36Sopenharmony_ci }, 20362306a36Sopenharmony_ci [ATTR_DCIN_IMAX] = { 20462306a36Sopenharmony_ci .name = "qcom,dc-current-limit", 20562306a36Sopenharmony_ci .reg = SMBB_DC_IMAX, 20662306a36Sopenharmony_ci .max = 2500000, 20762306a36Sopenharmony_ci .min = 100000, 20862306a36Sopenharmony_ci .hw_fn = smbb_imax_fn, 20962306a36Sopenharmony_ci }, 21062306a36Sopenharmony_ci [ATTR_BAT_VSAFE] = { 21162306a36Sopenharmony_ci .name = "qcom,fast-charge-safe-voltage", 21262306a36Sopenharmony_ci .reg = SMBB_CHG_VSAFE, 21362306a36Sopenharmony_ci .max = 5000000, 21462306a36Sopenharmony_ci .min = 3240000, 21562306a36Sopenharmony_ci .hw_fn = smbb_vmax_fn, 21662306a36Sopenharmony_ci .fail_ok = 1, 21762306a36Sopenharmony_ci }, 21862306a36Sopenharmony_ci [ATTR_BAT_VMAX] = { 21962306a36Sopenharmony_ci .name = "qcom,fast-charge-high-threshold-voltage", 22062306a36Sopenharmony_ci .reg = SMBB_CHG_VMAX, 22162306a36Sopenharmony_ci .safe_reg = SMBB_CHG_VSAFE, 22262306a36Sopenharmony_ci .max = 5000000, 22362306a36Sopenharmony_ci .min = 3240000, 22462306a36Sopenharmony_ci .hw_fn = smbb_vmax_fn, 22562306a36Sopenharmony_ci }, 22662306a36Sopenharmony_ci [ATTR_BAT_VMIN] = { 22762306a36Sopenharmony_ci .name = "qcom,fast-charge-low-threshold-voltage", 22862306a36Sopenharmony_ci .reg = SMBB_CHG_VBAT_WEAK, 22962306a36Sopenharmony_ci .max = 3600000, 23062306a36Sopenharmony_ci .min = 2100000, 23162306a36Sopenharmony_ci .hw_fn = smbb_vbat_weak_fn, 23262306a36Sopenharmony_ci }, 23362306a36Sopenharmony_ci [ATTR_CHG_VDET] = { 23462306a36Sopenharmony_ci .name = "qcom,auto-recharge-threshold-voltage", 23562306a36Sopenharmony_ci .reg = SMBB_CHG_VBAT_DET, 23662306a36Sopenharmony_ci .max = 5000000, 23762306a36Sopenharmony_ci .min = 3240000, 23862306a36Sopenharmony_ci .hw_fn = smbb_vbat_det_fn, 23962306a36Sopenharmony_ci }, 24062306a36Sopenharmony_ci [ATTR_VIN_MIN] = { 24162306a36Sopenharmony_ci .name = "qcom,minimum-input-voltage", 24262306a36Sopenharmony_ci .reg = SMBB_CHG_VIN_MIN, 24362306a36Sopenharmony_ci .max = 9600000, 24462306a36Sopenharmony_ci .min = 4200000, 24562306a36Sopenharmony_ci .hw_fn = smbb_vin_fn, 24662306a36Sopenharmony_ci }, 24762306a36Sopenharmony_ci [ATTR_USBIN_IMAX] = { 24862306a36Sopenharmony_ci .name = "usb-charge-current-limit", 24962306a36Sopenharmony_ci .reg = SMBB_USB_IMAX, 25062306a36Sopenharmony_ci .max = 2500000, 25162306a36Sopenharmony_ci .min = 100000, 25262306a36Sopenharmony_ci .hw_fn = smbb_imax_fn, 25362306a36Sopenharmony_ci }, 25462306a36Sopenharmony_ci}; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int smbb_charger_attr_write(struct smbb_charger *chg, 25762306a36Sopenharmony_ci enum smbb_attr which, unsigned int val) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci const struct smbb_charger_attr *prop; 26062306a36Sopenharmony_ci unsigned int wval; 26162306a36Sopenharmony_ci unsigned int out; 26262306a36Sopenharmony_ci int rc; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci prop = &smbb_charger_attrs[which]; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (val > prop->max || val < prop->min) { 26762306a36Sopenharmony_ci dev_err(chg->dev, "value out of range for %s [%u:%u]\n", 26862306a36Sopenharmony_ci prop->name, prop->min, prop->max); 26962306a36Sopenharmony_ci return -EINVAL; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci if (prop->safe_reg) { 27362306a36Sopenharmony_ci rc = regmap_read(chg->regmap, 27462306a36Sopenharmony_ci chg->addr + prop->safe_reg, &wval); 27562306a36Sopenharmony_ci if (rc) { 27662306a36Sopenharmony_ci dev_err(chg->dev, 27762306a36Sopenharmony_ci "unable to read safe value for '%s'\n", 27862306a36Sopenharmony_ci prop->name); 27962306a36Sopenharmony_ci return rc; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci wval = prop->hw_fn(wval); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (val > wval) { 28562306a36Sopenharmony_ci dev_warn(chg->dev, 28662306a36Sopenharmony_ci "%s above safe value, clamping at %u\n", 28762306a36Sopenharmony_ci prop->name, wval); 28862306a36Sopenharmony_ci val = wval; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci } 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci wval = smbb_hw_lookup(val, prop->hw_fn); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci rc = regmap_write(chg->regmap, chg->addr + prop->reg, wval); 29562306a36Sopenharmony_ci if (rc) { 29662306a36Sopenharmony_ci dev_err(chg->dev, "unable to update %s", prop->name); 29762306a36Sopenharmony_ci return rc; 29862306a36Sopenharmony_ci } 29962306a36Sopenharmony_ci out = prop->hw_fn(wval); 30062306a36Sopenharmony_ci if (out != val) { 30162306a36Sopenharmony_ci dev_warn(chg->dev, 30262306a36Sopenharmony_ci "%s inaccurate, rounded to %u\n", 30362306a36Sopenharmony_ci prop->name, out); 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci dev_dbg(chg->dev, "%s <= %d\n", prop->name, out); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci chg->attr[which] = out; 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic int smbb_charger_attr_read(struct smbb_charger *chg, 31462306a36Sopenharmony_ci enum smbb_attr which) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci const struct smbb_charger_attr *prop; 31762306a36Sopenharmony_ci unsigned int val; 31862306a36Sopenharmony_ci int rc; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci prop = &smbb_charger_attrs[which]; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci rc = regmap_read(chg->regmap, chg->addr + prop->reg, &val); 32362306a36Sopenharmony_ci if (rc) { 32462306a36Sopenharmony_ci dev_err(chg->dev, "failed to read %s\n", prop->name); 32562306a36Sopenharmony_ci return rc; 32662306a36Sopenharmony_ci } 32762306a36Sopenharmony_ci val = prop->hw_fn(val); 32862306a36Sopenharmony_ci dev_dbg(chg->dev, "%s => %d\n", prop->name, val); 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci chg->attr[which] = val; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci return 0; 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int smbb_charger_attr_parse(struct smbb_charger *chg, 33662306a36Sopenharmony_ci enum smbb_attr which) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci const struct smbb_charger_attr *prop; 33962306a36Sopenharmony_ci unsigned int val; 34062306a36Sopenharmony_ci int rc; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci prop = &smbb_charger_attrs[which]; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci rc = of_property_read_u32(chg->dev->of_node, prop->name, &val); 34562306a36Sopenharmony_ci if (rc == 0) { 34662306a36Sopenharmony_ci rc = smbb_charger_attr_write(chg, which, val); 34762306a36Sopenharmony_ci if (!rc || !prop->fail_ok) 34862306a36Sopenharmony_ci return rc; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci return smbb_charger_attr_read(chg, which); 35162306a36Sopenharmony_ci} 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic void smbb_set_line_flag(struct smbb_charger *chg, int irq, int flag) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci bool state; 35662306a36Sopenharmony_ci int ret; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, &state); 35962306a36Sopenharmony_ci if (ret < 0) { 36062306a36Sopenharmony_ci dev_err(chg->dev, "failed to read irq line\n"); 36162306a36Sopenharmony_ci return; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci mutex_lock(&chg->statlock); 36562306a36Sopenharmony_ci if (state) 36662306a36Sopenharmony_ci chg->status |= flag; 36762306a36Sopenharmony_ci else 36862306a36Sopenharmony_ci chg->status &= ~flag; 36962306a36Sopenharmony_ci mutex_unlock(&chg->statlock); 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci dev_dbg(chg->dev, "status = %03lx\n", chg->status); 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic irqreturn_t smbb_usb_valid_handler(int irq, void *_data) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci struct smbb_charger *chg = _data; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_USBIN_VALID); 37962306a36Sopenharmony_ci extcon_set_state_sync(chg->edev, EXTCON_USB, 38062306a36Sopenharmony_ci chg->status & STATUS_USBIN_VALID); 38162306a36Sopenharmony_ci power_supply_changed(chg->usb_psy); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci return IRQ_HANDLED; 38462306a36Sopenharmony_ci} 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_cistatic irqreturn_t smbb_dc_valid_handler(int irq, void *_data) 38762306a36Sopenharmony_ci{ 38862306a36Sopenharmony_ci struct smbb_charger *chg = _data; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_DCIN_VALID); 39162306a36Sopenharmony_ci if (!chg->dc_disabled) 39262306a36Sopenharmony_ci power_supply_changed(chg->dc_psy); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci return IRQ_HANDLED; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic irqreturn_t smbb_bat_temp_handler(int irq, void *_data) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct smbb_charger *chg = _data; 40062306a36Sopenharmony_ci unsigned int val; 40162306a36Sopenharmony_ci int rc; 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci rc = regmap_read(chg->regmap, chg->addr + SMBB_BAT_TEMP_STATUS, &val); 40462306a36Sopenharmony_ci if (rc) 40562306a36Sopenharmony_ci return IRQ_HANDLED; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci mutex_lock(&chg->statlock); 40862306a36Sopenharmony_ci if (val & TEMP_STATUS_OK) { 40962306a36Sopenharmony_ci chg->status |= STATUS_BAT_OK; 41062306a36Sopenharmony_ci } else { 41162306a36Sopenharmony_ci chg->status &= ~STATUS_BAT_OK; 41262306a36Sopenharmony_ci if (val & TEMP_STATUS_HOT) 41362306a36Sopenharmony_ci chg->status |= STATUS_BAT_HOT; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci mutex_unlock(&chg->statlock); 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci power_supply_changed(chg->bat_psy); 41862306a36Sopenharmony_ci return IRQ_HANDLED; 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic irqreturn_t smbb_bat_present_handler(int irq, void *_data) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci struct smbb_charger *chg = _data; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_BAT_PRESENT); 42662306a36Sopenharmony_ci power_supply_changed(chg->bat_psy); 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci return IRQ_HANDLED; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic irqreturn_t smbb_chg_done_handler(int irq, void *_data) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct smbb_charger *chg = _data; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_CHG_DONE); 43662306a36Sopenharmony_ci power_supply_changed(chg->bat_psy); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci return IRQ_HANDLED; 43962306a36Sopenharmony_ci} 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_cistatic irqreturn_t smbb_chg_gone_handler(int irq, void *_data) 44262306a36Sopenharmony_ci{ 44362306a36Sopenharmony_ci struct smbb_charger *chg = _data; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_CHG_GONE); 44662306a36Sopenharmony_ci power_supply_changed(chg->bat_psy); 44762306a36Sopenharmony_ci power_supply_changed(chg->usb_psy); 44862306a36Sopenharmony_ci if (!chg->dc_disabled) 44962306a36Sopenharmony_ci power_supply_changed(chg->dc_psy); 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci return IRQ_HANDLED; 45262306a36Sopenharmony_ci} 45362306a36Sopenharmony_ci 45462306a36Sopenharmony_cistatic irqreturn_t smbb_chg_fast_handler(int irq, void *_data) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct smbb_charger *chg = _data; 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_CHG_FAST); 45962306a36Sopenharmony_ci power_supply_changed(chg->bat_psy); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci return IRQ_HANDLED; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic irqreturn_t smbb_chg_trkl_handler(int irq, void *_data) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct smbb_charger *chg = _data; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci smbb_set_line_flag(chg, irq, STATUS_CHG_TRKL); 46962306a36Sopenharmony_ci power_supply_changed(chg->bat_psy); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci return IRQ_HANDLED; 47262306a36Sopenharmony_ci} 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_cistatic const struct smbb_irq { 47562306a36Sopenharmony_ci const char *name; 47662306a36Sopenharmony_ci irqreturn_t (*handler)(int, void *); 47762306a36Sopenharmony_ci} smbb_charger_irqs[] = { 47862306a36Sopenharmony_ci { "chg-done", smbb_chg_done_handler }, 47962306a36Sopenharmony_ci { "chg-fast", smbb_chg_fast_handler }, 48062306a36Sopenharmony_ci { "chg-trkl", smbb_chg_trkl_handler }, 48162306a36Sopenharmony_ci { "bat-temp-ok", smbb_bat_temp_handler }, 48262306a36Sopenharmony_ci { "bat-present", smbb_bat_present_handler }, 48362306a36Sopenharmony_ci { "chg-gone", smbb_chg_gone_handler }, 48462306a36Sopenharmony_ci { "usb-valid", smbb_usb_valid_handler }, 48562306a36Sopenharmony_ci { "dc-valid", smbb_dc_valid_handler }, 48662306a36Sopenharmony_ci}; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_cistatic int smbb_usbin_get_property(struct power_supply *psy, 48962306a36Sopenharmony_ci enum power_supply_property psp, 49062306a36Sopenharmony_ci union power_supply_propval *val) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci struct smbb_charger *chg = power_supply_get_drvdata(psy); 49362306a36Sopenharmony_ci int rc = 0; 49462306a36Sopenharmony_ci 49562306a36Sopenharmony_ci switch (psp) { 49662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 49762306a36Sopenharmony_ci mutex_lock(&chg->statlock); 49862306a36Sopenharmony_ci val->intval = !(chg->status & STATUS_CHG_GONE) && 49962306a36Sopenharmony_ci (chg->status & STATUS_USBIN_VALID); 50062306a36Sopenharmony_ci mutex_unlock(&chg->statlock); 50162306a36Sopenharmony_ci break; 50262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: 50362306a36Sopenharmony_ci val->intval = chg->attr[ATTR_USBIN_IMAX]; 50462306a36Sopenharmony_ci break; 50562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: 50662306a36Sopenharmony_ci val->intval = 2500000; 50762306a36Sopenharmony_ci break; 50862306a36Sopenharmony_ci default: 50962306a36Sopenharmony_ci rc = -EINVAL; 51062306a36Sopenharmony_ci break; 51162306a36Sopenharmony_ci } 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return rc; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic int smbb_usbin_set_property(struct power_supply *psy, 51762306a36Sopenharmony_ci enum power_supply_property psp, 51862306a36Sopenharmony_ci const union power_supply_propval *val) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct smbb_charger *chg = power_supply_get_drvdata(psy); 52162306a36Sopenharmony_ci int rc; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci switch (psp) { 52462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: 52562306a36Sopenharmony_ci rc = smbb_charger_attr_write(chg, ATTR_USBIN_IMAX, 52662306a36Sopenharmony_ci val->intval); 52762306a36Sopenharmony_ci break; 52862306a36Sopenharmony_ci default: 52962306a36Sopenharmony_ci rc = -EINVAL; 53062306a36Sopenharmony_ci break; 53162306a36Sopenharmony_ci } 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return rc; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic int smbb_dcin_get_property(struct power_supply *psy, 53762306a36Sopenharmony_ci enum power_supply_property psp, 53862306a36Sopenharmony_ci union power_supply_propval *val) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct smbb_charger *chg = power_supply_get_drvdata(psy); 54162306a36Sopenharmony_ci int rc = 0; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci switch (psp) { 54462306a36Sopenharmony_ci case POWER_SUPPLY_PROP_ONLINE: 54562306a36Sopenharmony_ci mutex_lock(&chg->statlock); 54662306a36Sopenharmony_ci val->intval = !(chg->status & STATUS_CHG_GONE) && 54762306a36Sopenharmony_ci (chg->status & STATUS_DCIN_VALID); 54862306a36Sopenharmony_ci mutex_unlock(&chg->statlock); 54962306a36Sopenharmony_ci break; 55062306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: 55162306a36Sopenharmony_ci val->intval = chg->attr[ATTR_DCIN_IMAX]; 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: 55462306a36Sopenharmony_ci val->intval = 2500000; 55562306a36Sopenharmony_ci break; 55662306a36Sopenharmony_ci default: 55762306a36Sopenharmony_ci rc = -EINVAL; 55862306a36Sopenharmony_ci break; 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci return rc; 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int smbb_dcin_set_property(struct power_supply *psy, 56562306a36Sopenharmony_ci enum power_supply_property psp, 56662306a36Sopenharmony_ci const union power_supply_propval *val) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct smbb_charger *chg = power_supply_get_drvdata(psy); 56962306a36Sopenharmony_ci int rc; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci switch (psp) { 57262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: 57362306a36Sopenharmony_ci rc = smbb_charger_attr_write(chg, ATTR_DCIN_IMAX, 57462306a36Sopenharmony_ci val->intval); 57562306a36Sopenharmony_ci break; 57662306a36Sopenharmony_ci default: 57762306a36Sopenharmony_ci rc = -EINVAL; 57862306a36Sopenharmony_ci break; 57962306a36Sopenharmony_ci } 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci return rc; 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic int smbb_charger_writable_property(struct power_supply *psy, 58562306a36Sopenharmony_ci enum power_supply_property psp) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_cistatic int smbb_battery_get_property(struct power_supply *psy, 59162306a36Sopenharmony_ci enum power_supply_property psp, 59262306a36Sopenharmony_ci union power_supply_propval *val) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct smbb_charger *chg = power_supply_get_drvdata(psy); 59562306a36Sopenharmony_ci unsigned long status; 59662306a36Sopenharmony_ci int rc = 0; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci mutex_lock(&chg->statlock); 59962306a36Sopenharmony_ci status = chg->status; 60062306a36Sopenharmony_ci mutex_unlock(&chg->statlock); 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci switch (psp) { 60362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_STATUS: 60462306a36Sopenharmony_ci if (status & STATUS_CHG_GONE) 60562306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 60662306a36Sopenharmony_ci else if (!(status & (STATUS_DCIN_VALID | STATUS_USBIN_VALID))) 60762306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 60862306a36Sopenharmony_ci else if (status & STATUS_CHG_DONE) 60962306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_FULL; 61062306a36Sopenharmony_ci else if (!(status & STATUS_BAT_OK)) 61162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 61262306a36Sopenharmony_ci else if (status & (STATUS_CHG_FAST | STATUS_CHG_TRKL)) 61362306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_CHARGING; 61462306a36Sopenharmony_ci else /* everything is ok for charging, but we are not... */ 61562306a36Sopenharmony_ci val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 61662306a36Sopenharmony_ci break; 61762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_HEALTH: 61862306a36Sopenharmony_ci if (status & STATUS_BAT_OK) 61962306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_GOOD; 62062306a36Sopenharmony_ci else if (status & STATUS_BAT_HOT) 62162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 62262306a36Sopenharmony_ci else 62362306a36Sopenharmony_ci val->intval = POWER_SUPPLY_HEALTH_COLD; 62462306a36Sopenharmony_ci break; 62562306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CHARGE_TYPE: 62662306a36Sopenharmony_ci if (status & STATUS_CHG_FAST) 62762306a36Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_FAST; 62862306a36Sopenharmony_ci else if (status & STATUS_CHG_TRKL) 62962306a36Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_TRICKLE; 63062306a36Sopenharmony_ci else 63162306a36Sopenharmony_ci val->intval = POWER_SUPPLY_CHARGE_TYPE_NONE; 63262306a36Sopenharmony_ci break; 63362306a36Sopenharmony_ci case POWER_SUPPLY_PROP_PRESENT: 63462306a36Sopenharmony_ci val->intval = !!(status & STATUS_BAT_PRESENT); 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 63762306a36Sopenharmony_ci val->intval = chg->attr[ATTR_BAT_IMAX]; 63862306a36Sopenharmony_ci break; 63962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX: 64062306a36Sopenharmony_ci val->intval = chg->attr[ATTR_BAT_VMAX]; 64162306a36Sopenharmony_ci break; 64262306a36Sopenharmony_ci case POWER_SUPPLY_PROP_TECHNOLOGY: 64362306a36Sopenharmony_ci /* this charger is a single-cell lithium-ion battery charger 64462306a36Sopenharmony_ci * only. If you hook up some other technology, there will be 64562306a36Sopenharmony_ci * fireworks. 64662306a36Sopenharmony_ci */ 64762306a36Sopenharmony_ci val->intval = POWER_SUPPLY_TECHNOLOGY_LION; 64862306a36Sopenharmony_ci break; 64962306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 65062306a36Sopenharmony_ci val->intval = 3000000; /* single-cell li-ion low end */ 65162306a36Sopenharmony_ci break; 65262306a36Sopenharmony_ci default: 65362306a36Sopenharmony_ci rc = -EINVAL; 65462306a36Sopenharmony_ci break; 65562306a36Sopenharmony_ci } 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci return rc; 65862306a36Sopenharmony_ci} 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic int smbb_battery_set_property(struct power_supply *psy, 66162306a36Sopenharmony_ci enum power_supply_property psp, 66262306a36Sopenharmony_ci const union power_supply_propval *val) 66362306a36Sopenharmony_ci{ 66462306a36Sopenharmony_ci struct smbb_charger *chg = power_supply_get_drvdata(psy); 66562306a36Sopenharmony_ci int rc; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci switch (psp) { 66862306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 66962306a36Sopenharmony_ci rc = smbb_charger_attr_write(chg, ATTR_BAT_IMAX, val->intval); 67062306a36Sopenharmony_ci break; 67162306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX: 67262306a36Sopenharmony_ci rc = smbb_charger_attr_write(chg, ATTR_BAT_VMAX, val->intval); 67362306a36Sopenharmony_ci break; 67462306a36Sopenharmony_ci default: 67562306a36Sopenharmony_ci rc = -EINVAL; 67662306a36Sopenharmony_ci break; 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return rc; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic int smbb_battery_writable_property(struct power_supply *psy, 68362306a36Sopenharmony_ci enum power_supply_property psp) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci switch (psp) { 68662306a36Sopenharmony_ci case POWER_SUPPLY_PROP_CURRENT_MAX: 68762306a36Sopenharmony_ci case POWER_SUPPLY_PROP_VOLTAGE_MAX: 68862306a36Sopenharmony_ci return 1; 68962306a36Sopenharmony_ci default: 69062306a36Sopenharmony_ci return 0; 69162306a36Sopenharmony_ci } 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic enum power_supply_property smbb_charger_properties[] = { 69562306a36Sopenharmony_ci POWER_SUPPLY_PROP_ONLINE, 69662306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT, 69762306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX, 69862306a36Sopenharmony_ci}; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_cistatic enum power_supply_property smbb_battery_properties[] = { 70162306a36Sopenharmony_ci POWER_SUPPLY_PROP_STATUS, 70262306a36Sopenharmony_ci POWER_SUPPLY_PROP_HEALTH, 70362306a36Sopenharmony_ci POWER_SUPPLY_PROP_PRESENT, 70462306a36Sopenharmony_ci POWER_SUPPLY_PROP_CHARGE_TYPE, 70562306a36Sopenharmony_ci POWER_SUPPLY_PROP_CURRENT_MAX, 70662306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MAX, 70762306a36Sopenharmony_ci POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, 70862306a36Sopenharmony_ci POWER_SUPPLY_PROP_TECHNOLOGY, 70962306a36Sopenharmony_ci}; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_cistatic const struct reg_off_mask_default { 71262306a36Sopenharmony_ci unsigned int offset; 71362306a36Sopenharmony_ci unsigned int mask; 71462306a36Sopenharmony_ci unsigned int value; 71562306a36Sopenharmony_ci unsigned int rev_mask; 71662306a36Sopenharmony_ci} smbb_charger_setup[] = { 71762306a36Sopenharmony_ci /* The bootloader is supposed to set this... make sure anyway. */ 71862306a36Sopenharmony_ci { SMBB_MISC_BOOT_DONE, BOOT_DONE, BOOT_DONE }, 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* Disable software timer */ 72162306a36Sopenharmony_ci { SMBB_CHG_TCHG_MAX_EN, TCHG_MAX_EN, 0 }, 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* Clear and disable watchdog */ 72462306a36Sopenharmony_ci { SMBB_CHG_WDOG_TIME, 0xff, 160 }, 72562306a36Sopenharmony_ci { SMBB_CHG_WDOG_EN, WDOG_EN, 0 }, 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci /* Use charger based EoC detection */ 72862306a36Sopenharmony_ci { SMBB_CHG_IBAT_TERM_CHG, IBAT_TERM_CHG_IEOC, IBAT_TERM_CHG_IEOC_CHG }, 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci /* Disable GSM PA load adjustment. 73162306a36Sopenharmony_ci * The PA signal is incorrectly connected on v2. 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_ci { SMBB_CHG_CFG, 0xff, 0x00, BIT(3) }, 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* Use VBAT (not VSYS) to compensate for IR drop during fast charging */ 73662306a36Sopenharmony_ci { SMBB_BUCK_REG_MODE, BUCK_REG_MODE, BUCK_REG_MODE_VBAT }, 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* Enable battery temperature comparators */ 73962306a36Sopenharmony_ci { SMBB_BAT_BTC_CTRL, BTC_CTRL_COMP_EN, BTC_CTRL_COMP_EN }, 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Stop USB enumeration timer */ 74262306a36Sopenharmony_ci { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP }, 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci#if 0 /* FIXME supposedly only to disable hardware ARB termination */ 74562306a36Sopenharmony_ci { SMBB_USB_SEC_ACCESS, SEC_ACCESS_MAGIC }, 74662306a36Sopenharmony_ci { SMBB_USB_REV_BST, 0xff, REV_BST_CHG_GONE }, 74762306a36Sopenharmony_ci#endif 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_ci /* Stop USB enumeration timer, again */ 75062306a36Sopenharmony_ci { SMBB_USB_ENUM_TIMER_STOP, ENUM_TIMER_STOP, ENUM_TIMER_STOP }, 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci /* Enable charging */ 75362306a36Sopenharmony_ci { SMBB_CHG_CTRL, CTRL_EN, CTRL_EN }, 75462306a36Sopenharmony_ci}; 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_cistatic char *smbb_bif[] = { "smbb-bif" }; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic const struct power_supply_desc bat_psy_desc = { 75962306a36Sopenharmony_ci .name = "smbb-bif", 76062306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_BATTERY, 76162306a36Sopenharmony_ci .properties = smbb_battery_properties, 76262306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(smbb_battery_properties), 76362306a36Sopenharmony_ci .get_property = smbb_battery_get_property, 76462306a36Sopenharmony_ci .set_property = smbb_battery_set_property, 76562306a36Sopenharmony_ci .property_is_writeable = smbb_battery_writable_property, 76662306a36Sopenharmony_ci}; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic const struct power_supply_desc usb_psy_desc = { 76962306a36Sopenharmony_ci .name = "smbb-usbin", 77062306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_USB, 77162306a36Sopenharmony_ci .properties = smbb_charger_properties, 77262306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(smbb_charger_properties), 77362306a36Sopenharmony_ci .get_property = smbb_usbin_get_property, 77462306a36Sopenharmony_ci .set_property = smbb_usbin_set_property, 77562306a36Sopenharmony_ci .property_is_writeable = smbb_charger_writable_property, 77662306a36Sopenharmony_ci}; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic const struct power_supply_desc dc_psy_desc = { 77962306a36Sopenharmony_ci .name = "smbb-dcin", 78062306a36Sopenharmony_ci .type = POWER_SUPPLY_TYPE_MAINS, 78162306a36Sopenharmony_ci .properties = smbb_charger_properties, 78262306a36Sopenharmony_ci .num_properties = ARRAY_SIZE(smbb_charger_properties), 78362306a36Sopenharmony_ci .get_property = smbb_dcin_get_property, 78462306a36Sopenharmony_ci .set_property = smbb_dcin_set_property, 78562306a36Sopenharmony_ci .property_is_writeable = smbb_charger_writable_property, 78662306a36Sopenharmony_ci}; 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic int smbb_chg_otg_enable(struct regulator_dev *rdev) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct smbb_charger *chg = rdev_get_drvdata(rdev); 79162306a36Sopenharmony_ci int rc; 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_USB_OTG_CTL, 79462306a36Sopenharmony_ci OTG_CTL_EN, OTG_CTL_EN); 79562306a36Sopenharmony_ci if (rc) 79662306a36Sopenharmony_ci dev_err(chg->dev, "failed to update OTG_CTL\n"); 79762306a36Sopenharmony_ci return rc; 79862306a36Sopenharmony_ci} 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_cistatic int smbb_chg_otg_disable(struct regulator_dev *rdev) 80162306a36Sopenharmony_ci{ 80262306a36Sopenharmony_ci struct smbb_charger *chg = rdev_get_drvdata(rdev); 80362306a36Sopenharmony_ci int rc; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_USB_OTG_CTL, 80662306a36Sopenharmony_ci OTG_CTL_EN, 0); 80762306a36Sopenharmony_ci if (rc) 80862306a36Sopenharmony_ci dev_err(chg->dev, "failed to update OTG_CTL\n"); 80962306a36Sopenharmony_ci return rc; 81062306a36Sopenharmony_ci} 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_cistatic int smbb_chg_otg_is_enabled(struct regulator_dev *rdev) 81362306a36Sopenharmony_ci{ 81462306a36Sopenharmony_ci struct smbb_charger *chg = rdev_get_drvdata(rdev); 81562306a36Sopenharmony_ci unsigned int value = 0; 81662306a36Sopenharmony_ci int rc; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci rc = regmap_read(chg->regmap, chg->addr + SMBB_USB_OTG_CTL, &value); 81962306a36Sopenharmony_ci if (rc) 82062306a36Sopenharmony_ci dev_err(chg->dev, "failed to read OTG_CTL\n"); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return !!(value & OTG_CTL_EN); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic const struct regulator_ops smbb_chg_otg_ops = { 82662306a36Sopenharmony_ci .enable = smbb_chg_otg_enable, 82762306a36Sopenharmony_ci .disable = smbb_chg_otg_disable, 82862306a36Sopenharmony_ci .is_enabled = smbb_chg_otg_is_enabled, 82962306a36Sopenharmony_ci}; 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cistatic int smbb_charger_probe(struct platform_device *pdev) 83262306a36Sopenharmony_ci{ 83362306a36Sopenharmony_ci struct power_supply_config bat_cfg = {}; 83462306a36Sopenharmony_ci struct power_supply_config usb_cfg = {}; 83562306a36Sopenharmony_ci struct power_supply_config dc_cfg = {}; 83662306a36Sopenharmony_ci struct smbb_charger *chg; 83762306a36Sopenharmony_ci struct regulator_config config = { }; 83862306a36Sopenharmony_ci int rc, i; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL); 84162306a36Sopenharmony_ci if (!chg) 84262306a36Sopenharmony_ci return -ENOMEM; 84362306a36Sopenharmony_ci 84462306a36Sopenharmony_ci chg->dev = &pdev->dev; 84562306a36Sopenharmony_ci mutex_init(&chg->statlock); 84662306a36Sopenharmony_ci 84762306a36Sopenharmony_ci chg->regmap = dev_get_regmap(pdev->dev.parent, NULL); 84862306a36Sopenharmony_ci if (!chg->regmap) { 84962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to locate regmap\n"); 85062306a36Sopenharmony_ci return -ENODEV; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci rc = of_property_read_u32(pdev->dev.of_node, "reg", &chg->addr); 85462306a36Sopenharmony_ci if (rc) { 85562306a36Sopenharmony_ci dev_err(&pdev->dev, "missing or invalid 'reg' property\n"); 85662306a36Sopenharmony_ci return rc; 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci rc = regmap_read(chg->regmap, chg->addr + SMBB_MISC_REV2, &chg->revision); 86062306a36Sopenharmony_ci if (rc) { 86162306a36Sopenharmony_ci dev_err(&pdev->dev, "unable to read revision\n"); 86262306a36Sopenharmony_ci return rc; 86362306a36Sopenharmony_ci } 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci chg->revision += 1; 86662306a36Sopenharmony_ci if (chg->revision != 1 && chg->revision != 2 && chg->revision != 3) { 86762306a36Sopenharmony_ci dev_err(&pdev->dev, "v%d hardware not supported\n", chg->revision); 86862306a36Sopenharmony_ci return -ENODEV; 86962306a36Sopenharmony_ci } 87062306a36Sopenharmony_ci dev_info(&pdev->dev, "Initializing SMBB rev %u", chg->revision); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci chg->dc_disabled = of_property_read_bool(pdev->dev.of_node, "qcom,disable-dc"); 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci for (i = 0; i < _ATTR_CNT; ++i) { 87562306a36Sopenharmony_ci rc = smbb_charger_attr_parse(chg, i); 87662306a36Sopenharmony_ci if (rc) { 87762306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to parse/apply settings\n"); 87862306a36Sopenharmony_ci return rc; 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci bat_cfg.drv_data = chg; 88362306a36Sopenharmony_ci bat_cfg.of_node = pdev->dev.of_node; 88462306a36Sopenharmony_ci chg->bat_psy = devm_power_supply_register(&pdev->dev, 88562306a36Sopenharmony_ci &bat_psy_desc, 88662306a36Sopenharmony_ci &bat_cfg); 88762306a36Sopenharmony_ci if (IS_ERR(chg->bat_psy)) { 88862306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register battery\n"); 88962306a36Sopenharmony_ci return PTR_ERR(chg->bat_psy); 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci usb_cfg.drv_data = chg; 89362306a36Sopenharmony_ci usb_cfg.supplied_to = smbb_bif; 89462306a36Sopenharmony_ci usb_cfg.num_supplicants = ARRAY_SIZE(smbb_bif); 89562306a36Sopenharmony_ci chg->usb_psy = devm_power_supply_register(&pdev->dev, 89662306a36Sopenharmony_ci &usb_psy_desc, 89762306a36Sopenharmony_ci &usb_cfg); 89862306a36Sopenharmony_ci if (IS_ERR(chg->usb_psy)) { 89962306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register USB power supply\n"); 90062306a36Sopenharmony_ci return PTR_ERR(chg->usb_psy); 90162306a36Sopenharmony_ci } 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci chg->edev = devm_extcon_dev_allocate(&pdev->dev, smbb_usb_extcon_cable); 90462306a36Sopenharmony_ci if (IS_ERR(chg->edev)) { 90562306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to allocate extcon device\n"); 90662306a36Sopenharmony_ci return -ENOMEM; 90762306a36Sopenharmony_ci } 90862306a36Sopenharmony_ci 90962306a36Sopenharmony_ci rc = devm_extcon_dev_register(&pdev->dev, chg->edev); 91062306a36Sopenharmony_ci if (rc < 0) { 91162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register extcon device\n"); 91262306a36Sopenharmony_ci return rc; 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (!chg->dc_disabled) { 91662306a36Sopenharmony_ci dc_cfg.drv_data = chg; 91762306a36Sopenharmony_ci dc_cfg.supplied_to = smbb_bif; 91862306a36Sopenharmony_ci dc_cfg.num_supplicants = ARRAY_SIZE(smbb_bif); 91962306a36Sopenharmony_ci chg->dc_psy = devm_power_supply_register(&pdev->dev, 92062306a36Sopenharmony_ci &dc_psy_desc, 92162306a36Sopenharmony_ci &dc_cfg); 92262306a36Sopenharmony_ci if (IS_ERR(chg->dc_psy)) { 92362306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to register DC power supply\n"); 92462306a36Sopenharmony_ci return PTR_ERR(chg->dc_psy); 92562306a36Sopenharmony_ci } 92662306a36Sopenharmony_ci } 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(smbb_charger_irqs); ++i) { 92962306a36Sopenharmony_ci int irq; 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_ci irq = platform_get_irq_byname(pdev, smbb_charger_irqs[i].name); 93262306a36Sopenharmony_ci if (irq < 0) 93362306a36Sopenharmony_ci return irq; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci smbb_charger_irqs[i].handler(irq, chg); 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci rc = devm_request_threaded_irq(&pdev->dev, irq, NULL, 93862306a36Sopenharmony_ci smbb_charger_irqs[i].handler, IRQF_ONESHOT, 93962306a36Sopenharmony_ci smbb_charger_irqs[i].name, chg); 94062306a36Sopenharmony_ci if (rc) { 94162306a36Sopenharmony_ci dev_err(&pdev->dev, "failed to request irq '%s'\n", 94262306a36Sopenharmony_ci smbb_charger_irqs[i].name); 94362306a36Sopenharmony_ci return rc; 94462306a36Sopenharmony_ci } 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci /* 94862306a36Sopenharmony_ci * otg regulator is used to control VBUS voltage direction 94962306a36Sopenharmony_ci * when USB switches between host and gadget mode 95062306a36Sopenharmony_ci */ 95162306a36Sopenharmony_ci chg->otg_rdesc.id = -1; 95262306a36Sopenharmony_ci chg->otg_rdesc.name = "otg-vbus"; 95362306a36Sopenharmony_ci chg->otg_rdesc.ops = &smbb_chg_otg_ops; 95462306a36Sopenharmony_ci chg->otg_rdesc.owner = THIS_MODULE; 95562306a36Sopenharmony_ci chg->otg_rdesc.type = REGULATOR_VOLTAGE; 95662306a36Sopenharmony_ci chg->otg_rdesc.supply_name = "usb-otg-in"; 95762306a36Sopenharmony_ci chg->otg_rdesc.of_match = "otg-vbus"; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci config.dev = &pdev->dev; 96062306a36Sopenharmony_ci config.driver_data = chg; 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci chg->otg_reg = devm_regulator_register(&pdev->dev, &chg->otg_rdesc, 96362306a36Sopenharmony_ci &config); 96462306a36Sopenharmony_ci if (IS_ERR(chg->otg_reg)) 96562306a36Sopenharmony_ci return PTR_ERR(chg->otg_reg); 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci chg->jeita_ext_temp = of_property_read_bool(pdev->dev.of_node, 96862306a36Sopenharmony_ci "qcom,jeita-extended-temp-range"); 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci /* Set temperature range to [35%:70%] or [25%:80%] accordingly */ 97162306a36Sopenharmony_ci rc = regmap_update_bits(chg->regmap, chg->addr + SMBB_BAT_BTC_CTRL, 97262306a36Sopenharmony_ci BTC_CTRL_COLD_EXT | BTC_CTRL_HOT_EXT_N, 97362306a36Sopenharmony_ci chg->jeita_ext_temp ? 97462306a36Sopenharmony_ci BTC_CTRL_COLD_EXT : 97562306a36Sopenharmony_ci BTC_CTRL_HOT_EXT_N); 97662306a36Sopenharmony_ci if (rc) { 97762306a36Sopenharmony_ci dev_err(&pdev->dev, 97862306a36Sopenharmony_ci "unable to set %s temperature range\n", 97962306a36Sopenharmony_ci chg->jeita_ext_temp ? "JEITA extended" : "normal"); 98062306a36Sopenharmony_ci return rc; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(smbb_charger_setup); ++i) { 98462306a36Sopenharmony_ci const struct reg_off_mask_default *r = &smbb_charger_setup[i]; 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (r->rev_mask & BIT(chg->revision)) 98762306a36Sopenharmony_ci continue; 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_ci rc = regmap_update_bits(chg->regmap, chg->addr + r->offset, 99062306a36Sopenharmony_ci r->mask, r->value); 99162306a36Sopenharmony_ci if (rc) { 99262306a36Sopenharmony_ci dev_err(&pdev->dev, 99362306a36Sopenharmony_ci "unable to initializing charging, bailing\n"); 99462306a36Sopenharmony_ci return rc; 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci } 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci platform_set_drvdata(pdev, chg); 99962306a36Sopenharmony_ci 100062306a36Sopenharmony_ci return 0; 100162306a36Sopenharmony_ci} 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_cistatic int smbb_charger_remove(struct platform_device *pdev) 100462306a36Sopenharmony_ci{ 100562306a36Sopenharmony_ci struct smbb_charger *chg; 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci chg = platform_get_drvdata(pdev); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci regmap_update_bits(chg->regmap, chg->addr + SMBB_CHG_CTRL, CTRL_EN, 0); 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci return 0; 101262306a36Sopenharmony_ci} 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_cistatic const struct of_device_id smbb_charger_id_table[] = { 101562306a36Sopenharmony_ci { .compatible = "qcom,pm8226-charger" }, 101662306a36Sopenharmony_ci { .compatible = "qcom,pm8941-charger" }, 101762306a36Sopenharmony_ci { } 101862306a36Sopenharmony_ci}; 101962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, smbb_charger_id_table); 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_cistatic struct platform_driver smbb_charger_driver = { 102262306a36Sopenharmony_ci .probe = smbb_charger_probe, 102362306a36Sopenharmony_ci .remove = smbb_charger_remove, 102462306a36Sopenharmony_ci .driver = { 102562306a36Sopenharmony_ci .name = "qcom-smbb", 102662306a36Sopenharmony_ci .of_match_table = smbb_charger_id_table, 102762306a36Sopenharmony_ci }, 102862306a36Sopenharmony_ci}; 102962306a36Sopenharmony_cimodule_platform_driver(smbb_charger_driver); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm Switch-Mode Battery Charger and Boost driver"); 103262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 1033