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