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