1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2023, Nikita Travkin <nikita@trvn.ru> 4 */ 5 6 #include <linux/errno.h> 7 #include <linux/module.h> 8 #include <linux/platform_device.h> 9 #include <linux/power_supply.h> 10 #include <linux/property.h> 11 #include <linux/regmap.h> 12 #include <linux/slab.h> 13 #include <linux/delay.h> 14 #include <linux/interrupt.h> 15 #include <linux/extcon-provider.h> 16 17 /* Two bytes: type + subtype */ 18 #define PM8916_PERPH_TYPE 0x04 19 #define PM8916_LBC_CHGR_TYPE 0x1502 20 #define PM8916_LBC_BAT_IF_TYPE 0x1602 21 #define PM8916_LBC_USB_TYPE 0x1702 22 #define PM8916_LBC_MISC_TYPE 0x1802 23 24 #define PM8916_LBC_CHGR_CHG_OPTION 0x08 25 #define PM8916_LBC_CHGR_PMIC_CHARGER BIT(7) 26 27 #define PM8916_LBC_CHGR_CHG_STATUS 0x09 28 29 #define PM8916_INT_RT_STS 0x10 30 31 #define PM8916_LBC_USB_USBIN_VALID BIT(1) 32 33 #define PM8916_LBC_CHGR_VDD_MAX 0x40 34 #define PM8916_LBC_CHGR_VDD_SAFE 0x41 35 #define PM8916_LBC_CHGR_IBAT_MAX 0x44 36 #define PM8916_LBC_CHGR_IBAT_SAFE 0x45 37 38 #define PM8916_LBC_CHGR_TCHG_MAX_EN 0x60 39 #define PM8916_LBC_CHGR_TCHG_MAX_ENABLED BIT(7) 40 #define PM8916_LBC_CHGR_TCHG_MAX 0x61 41 42 #define PM8916_LBC_CHGR_CHG_CTRL 0x49 43 #define PM8916_LBC_CHGR_CHG_EN BIT(7) 44 #define PM8916_LBC_CHGR_PSTG_EN BIT(5) 45 46 #define PM8916_LBC_CHGR_MIN_CURRENT 90000 47 #define PM8916_LBC_CHGR_MAX_CURRENT 1440000 48 49 #define PM8916_LBC_CHGR_MIN_VOLTAGE 4000000 50 #define PM8916_LBC_CHGR_MAX_VOLTAGE 4775000 51 #define PM8916_LBC_CHGR_VOLTAGE_STEP 25000 52 53 #define PM8916_LBC_CHGR_MIN_TIME 4 54 #define PM8916_LBC_CHGR_MAX_TIME 256 55 56 struct pm8916_lbc_charger { 57 struct device *dev; 58 struct extcon_dev *edev; 59 struct power_supply *charger; 60 struct power_supply_battery_info *info; 61 struct regmap *regmap; 62 unsigned int reg[4]; 63 bool online; 64 unsigned int charge_voltage_max; 65 unsigned int charge_voltage_safe; 66 unsigned int charge_current_max; 67 unsigned int charge_current_safe; 68 }; 69 70 static const unsigned int pm8916_lbc_charger_cable[] = { 71 EXTCON_USB, 72 EXTCON_NONE, 73 }; 74 75 enum { 76 LBC_CHGR = 0, 77 LBC_BAT_IF, 78 LBC_USB, 79 LBC_MISC, 80 }; 81 82 static int pm8916_lbc_charger_configure(struct pm8916_lbc_charger *chg) 83 { 84 int ret = 0; 85 unsigned int tmp; 86 87 chg->charge_voltage_max = clamp_t(u32, chg->charge_voltage_max, 88 PM8916_LBC_CHGR_MIN_VOLTAGE, chg->charge_voltage_safe); 89 90 tmp = chg->charge_voltage_max - PM8916_LBC_CHGR_MIN_VOLTAGE; 91 tmp /= PM8916_LBC_CHGR_VOLTAGE_STEP; 92 chg->charge_voltage_max = PM8916_LBC_CHGR_MIN_VOLTAGE + tmp * PM8916_LBC_CHGR_VOLTAGE_STEP; 93 94 ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_VDD_MAX, tmp); 95 if (ret) 96 goto error; 97 98 chg->charge_current_max = min(chg->charge_current_max, chg->charge_current_safe); 99 100 tmp = clamp_t(u32, chg->charge_current_max, 101 PM8916_LBC_CHGR_MIN_CURRENT, PM8916_LBC_CHGR_MAX_CURRENT); 102 103 tmp = chg->charge_current_max / PM8916_LBC_CHGR_MIN_CURRENT - 1; 104 chg->charge_current_max = (tmp + 1) * PM8916_LBC_CHGR_MIN_CURRENT; 105 106 ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_IBAT_MAX, tmp); 107 if (ret) 108 goto error; 109 110 ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_CHG_CTRL, 111 PM8916_LBC_CHGR_CHG_EN | PM8916_LBC_CHGR_PSTG_EN); 112 if (ret) 113 goto error; 114 115 return ret; 116 117 error: 118 dev_err(chg->dev, "Failed to configure charging: %pe\n", ERR_PTR(ret)); 119 return ret; 120 } 121 122 static int pm8916_lbc_charger_get_property(struct power_supply *psy, 123 enum power_supply_property psp, 124 union power_supply_propval *val) 125 { 126 struct pm8916_lbc_charger *chg = power_supply_get_drvdata(psy); 127 128 switch (psp) { 129 case POWER_SUPPLY_PROP_ONLINE: 130 val->intval = chg->online; 131 return 0; 132 133 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: 134 val->intval = chg->charge_voltage_max; 135 return 0; 136 137 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 138 val->intval = chg->charge_current_max; 139 return 0; 140 141 default: 142 return -EINVAL; 143 }; 144 } 145 146 static int pm8916_lbc_charger_set_property(struct power_supply *psy, 147 enum power_supply_property prop, 148 const union power_supply_propval *val) 149 { 150 struct pm8916_lbc_charger *chg = power_supply_get_drvdata(psy); 151 152 switch (prop) { 153 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 154 chg->charge_current_max = val->intval; 155 return pm8916_lbc_charger_configure(chg); 156 default: 157 return -EINVAL; 158 } 159 } 160 161 static int pm8916_lbc_charger_property_is_writeable(struct power_supply *psy, 162 enum power_supply_property psp) 163 { 164 switch (psp) { 165 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: 166 return true; 167 default: 168 return false; 169 } 170 } 171 172 static enum power_supply_property pm8916_lbc_charger_properties[] = { 173 POWER_SUPPLY_PROP_ONLINE, 174 POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX, 175 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT, 176 }; 177 178 static irqreturn_t pm8916_lbc_charger_state_changed_irq(int irq, void *data) 179 { 180 struct pm8916_lbc_charger *chg = data; 181 unsigned int tmp; 182 int ret; 183 184 ret = regmap_read(chg->regmap, chg->reg[LBC_USB] + PM8916_INT_RT_STS, &tmp); 185 if (ret) 186 return IRQ_HANDLED; 187 188 chg->online = !!(tmp & PM8916_LBC_USB_USBIN_VALID); 189 extcon_set_state_sync(chg->edev, EXTCON_USB, chg->online); 190 191 power_supply_changed(chg->charger); 192 193 return IRQ_HANDLED; 194 } 195 196 static int pm8916_lbc_charger_probe_dt(struct pm8916_lbc_charger *chg) 197 { 198 struct device *dev = chg->dev; 199 int ret = 0; 200 unsigned int tmp; 201 202 ret = device_property_read_u32(dev, "qcom,fast-charge-safe-voltage", &chg->charge_voltage_safe); 203 if (ret) 204 return ret; 205 if (chg->charge_voltage_safe < PM8916_LBC_CHGR_MIN_VOLTAGE) 206 return -EINVAL; 207 208 chg->charge_voltage_safe = clamp_t(u32, chg->charge_voltage_safe, 209 PM8916_LBC_CHGR_MIN_VOLTAGE, PM8916_LBC_CHGR_MAX_VOLTAGE); 210 211 tmp = chg->charge_voltage_safe - PM8916_LBC_CHGR_MIN_VOLTAGE; 212 tmp /= PM8916_LBC_CHGR_VOLTAGE_STEP; 213 ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_VDD_SAFE, tmp); 214 if (ret) 215 return ret; 216 217 ret = device_property_read_u32(dev, "qcom,fast-charge-safe-current", &chg->charge_current_safe); 218 if (ret) 219 return ret; 220 if (chg->charge_current_safe < PM8916_LBC_CHGR_MIN_CURRENT) 221 return -EINVAL; 222 223 chg->charge_current_safe = clamp_t(u32, chg->charge_current_safe, 224 PM8916_LBC_CHGR_MIN_CURRENT, PM8916_LBC_CHGR_MAX_CURRENT); 225 226 chg->charge_current_max = chg->charge_current_safe; 227 228 tmp = chg->charge_current_safe / PM8916_LBC_CHGR_MIN_CURRENT - 1; 229 ret = regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_IBAT_SAFE, tmp); 230 if (ret) 231 return ret; 232 233 /* Disable charger timeout. */ 234 return regmap_write(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_TCHG_MAX_EN, 0x00); 235 } 236 237 static const struct power_supply_desc pm8916_lbc_charger_psy_desc = { 238 .name = "pm8916-lbc-chgr", 239 .type = POWER_SUPPLY_TYPE_USB, 240 .properties = pm8916_lbc_charger_properties, 241 .num_properties = ARRAY_SIZE(pm8916_lbc_charger_properties), 242 .get_property = pm8916_lbc_charger_get_property, 243 .set_property = pm8916_lbc_charger_set_property, 244 .property_is_writeable = pm8916_lbc_charger_property_is_writeable, 245 }; 246 247 static int pm8916_lbc_charger_probe(struct platform_device *pdev) 248 { 249 struct device *dev = &pdev->dev; 250 struct pm8916_lbc_charger *chg; 251 struct power_supply_config psy_cfg = {}; 252 int ret, len, irq; 253 unsigned int tmp; 254 255 chg = devm_kzalloc(dev, sizeof(*chg), GFP_KERNEL); 256 if (!chg) 257 return -ENOMEM; 258 259 chg->dev = dev; 260 261 chg->regmap = dev_get_regmap(pdev->dev.parent, NULL); 262 if (!chg->regmap) 263 return -ENODEV; 264 265 len = device_property_count_u32(dev, "reg"); 266 if (len < 0) 267 return len; 268 if (len != 4) 269 return dev_err_probe(dev, -EINVAL, 270 "Wrong amount of reg values: %d (4 expected)\n", len); 271 272 ret = device_property_read_u32_array(dev, "reg", chg->reg, len); 273 if (ret) 274 return ret; 275 276 ret = regmap_bulk_read(chg->regmap, chg->reg[LBC_CHGR] + PM8916_PERPH_TYPE, &tmp, 2); 277 if (ret) 278 goto comm_error; 279 if (tmp != PM8916_LBC_CHGR_TYPE) 280 goto type_error; 281 282 ret = regmap_bulk_read(chg->regmap, chg->reg[LBC_BAT_IF] + PM8916_PERPH_TYPE, &tmp, 2); 283 if (ret) 284 goto comm_error; 285 if (tmp != PM8916_LBC_BAT_IF_TYPE) 286 goto type_error; 287 288 ret = regmap_bulk_read(chg->regmap, chg->reg[LBC_USB] + PM8916_PERPH_TYPE, &tmp, 2); 289 if (ret) 290 goto comm_error; 291 if (tmp != PM8916_LBC_USB_TYPE) 292 goto type_error; 293 294 ret = regmap_bulk_read(chg->regmap, chg->reg[LBC_MISC] + PM8916_PERPH_TYPE, &tmp, 2); 295 if (ret) 296 goto comm_error; 297 if (tmp != PM8916_LBC_MISC_TYPE) 298 goto type_error; 299 300 ret = regmap_read(chg->regmap, chg->reg[LBC_CHGR] + PM8916_LBC_CHGR_CHG_OPTION, &tmp); 301 if (ret) 302 goto comm_error; 303 if (tmp != PM8916_LBC_CHGR_PMIC_CHARGER) 304 dev_err_probe(dev, -ENODEV, "The system is using an external charger\n"); 305 306 ret = pm8916_lbc_charger_probe_dt(chg); 307 if (ret) 308 dev_err_probe(dev, ret, "Error while parsing device tree\n"); 309 310 psy_cfg.drv_data = chg; 311 psy_cfg.fwnode = dev_fwnode(dev); 312 313 chg->charger = devm_power_supply_register(dev, &pm8916_lbc_charger_psy_desc, &psy_cfg); 314 if (IS_ERR(chg->charger)) 315 return dev_err_probe(dev, PTR_ERR(chg->charger), "Unable to register charger\n"); 316 317 ret = power_supply_get_battery_info(chg->charger, &chg->info); 318 if (ret) 319 return dev_err_probe(dev, ret, "Unable to get battery info\n"); 320 321 irq = platform_get_irq_byname(pdev, "usb_vbus"); 322 if (irq < 0) 323 return irq; 324 325 chg->edev = devm_extcon_dev_allocate(dev, pm8916_lbc_charger_cable); 326 if (IS_ERR(chg->edev)) 327 return PTR_ERR(chg->edev); 328 329 ret = devm_extcon_dev_register(dev, chg->edev); 330 if (ret < 0) 331 return dev_err_probe(dev, ret, "failed to register extcon device\n"); 332 333 ret = devm_request_threaded_irq(dev, irq, NULL, pm8916_lbc_charger_state_changed_irq, 334 IRQF_ONESHOT, "pm8916_lbc", chg); 335 if (ret) 336 return ret; 337 338 ret = regmap_read(chg->regmap, chg->reg[LBC_USB] + PM8916_INT_RT_STS, &tmp); 339 if (ret) 340 goto comm_error; 341 342 chg->online = !!(tmp & PM8916_LBC_USB_USBIN_VALID); 343 extcon_set_state_sync(chg->edev, EXTCON_USB, chg->online); 344 345 chg->charge_voltage_max = chg->info->voltage_max_design_uv; 346 ret = pm8916_lbc_charger_configure(chg); 347 if (ret) 348 return ret; 349 350 return 0; 351 352 comm_error: 353 return dev_err_probe(dev, ret, "Unable to communicate with device\n"); 354 355 type_error: 356 return dev_err_probe(dev, -ENODEV, "Device reported wrong type: 0x%X\n", tmp); 357 } 358 359 static const struct of_device_id pm8916_lbc_charger_of_match[] = { 360 { .compatible = "qcom,pm8916-lbc", }, 361 {} 362 }; 363 MODULE_DEVICE_TABLE(of, pm8916_lbc_charger_of_match); 364 365 static struct platform_driver pm8916_lbc_charger_driver = { 366 .driver = { 367 .name = "pm8916-lbc", 368 .of_match_table = pm8916_lbc_charger_of_match, 369 }, 370 .probe = pm8916_lbc_charger_probe, 371 }; 372 module_platform_driver(pm8916_lbc_charger_driver); 373 374 MODULE_DESCRIPTION("pm8916 LBC driver"); 375 MODULE_AUTHOR("Nikita Travkin <nikita@trvn.ru>"); 376 MODULE_LICENSE("GPL"); 377