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
pm8916_lbc_charger_configure(struct pm8916_lbc_charger * chg)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
pm8916_lbc_charger_get_property(struct power_supply * psy,enum power_supply_property psp,union power_supply_propval * val)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
pm8916_lbc_charger_set_property(struct power_supply * psy,enum power_supply_property prop,const union power_supply_propval * val)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
pm8916_lbc_charger_property_is_writeable(struct power_supply * psy,enum power_supply_property psp)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
pm8916_lbc_charger_state_changed_irq(int irq,void * data)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
pm8916_lbc_charger_probe_dt(struct pm8916_lbc_charger * chg)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
pm8916_lbc_charger_probe(struct platform_device * pdev)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