xref: /linux/drivers/power/supply/pm8916_lbc.c (revision c532de5a67a70f8533d495f8f2aaa9a0491c3ad0)
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