File Index
Files and directories
Sign in
FILE

pf1550-charger.c

File preview with syntax highlighting.

linux-6.19.14/drivers/power/supply/pf1550-charger.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * charger driver for the PF1550
  4 *
  5 * Copyright (C) 2016 Freescale Semiconductor, Inc.
  6 * Robin Gong <yibin.gong@freescale.com>
  7 *
  8 * Portions Copyright (c) 2025 Savoir-faire Linux Inc.
  9 * Samuel Kayode <samuel.kayode@savoirfairelinux.com>
 10 */
 11
 12#include <linux/devm-helpers.h>
 13#include <linux/interrupt.h>
 14#include <linux/mfd/pf1550.h>
 15#include <linux/module.h>
 16#include <linux/platform_device.h>
 17#include <linux/power_supply.h>
 18
 19#define PF1550_DEFAULT_CONSTANT_VOLT	4200000
 20#define PF1550_DEFAULT_MIN_SYSTEM_VOLT	3500000
 21#define PF1550_DEFAULT_THERMAL_TEMP	95
 22#define PF1550_CHARGER_IRQ_NR		5
 23
 24struct pf1550_charger {
 25	struct device *dev;
 26	const struct pf1550_ddata *pf1550;
 27	struct power_supply *charger;
 28	struct power_supply *battery;
 29	struct delayed_work vbus_sense_work;
 30	struct delayed_work chg_sense_work;
 31	struct delayed_work bat_sense_work;
 32	int virqs[PF1550_CHARGER_IRQ_NR];
 33
 34	u32 constant_volt;
 35	u32 min_system_volt;
 36	u32 thermal_regulation_temp;
 37};
 38
 39static int pf1550_get_charger_state(struct regmap *regmap, int *val)
 40{
 41	unsigned int data;
 42	int ret;
 43
 44	ret = regmap_read(regmap, PF1550_CHARG_REG_CHG_SNS, &data);
 45	if (ret < 0)
 46		return ret;
 47
 48	data &= PF1550_CHG_SNS_MASK;
 49
 50	switch (data) {
 51	case PF1550_CHG_PRECHARGE:
 52	case PF1550_CHG_CONSTANT_CURRENT:
 53	case PF1550_CHG_CONSTANT_VOL:
 54	case PF1550_CHG_EOC:
 55		*val = POWER_SUPPLY_STATUS_CHARGING;
 56		break;
 57	case PF1550_CHG_DONE:
 58		*val = POWER_SUPPLY_STATUS_FULL;
 59		break;
 60	case PF1550_CHG_TIMER_FAULT:
 61	case PF1550_CHG_SUSPEND:
 62		*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
 63		break;
 64	case PF1550_CHG_OFF_INV:
 65	case PF1550_CHG_OFF_TEMP:
 66	case PF1550_CHG_LINEAR_ONLY:
 67		*val = POWER_SUPPLY_STATUS_DISCHARGING;
 68		break;
 69	default:
 70		*val = POWER_SUPPLY_STATUS_UNKNOWN;
 71	}
 72
 73	return 0;
 74}
 75
 76static int pf1550_get_charge_type(struct regmap *regmap, int *val)
 77{
 78	unsigned int data;
 79	int ret;
 80
 81	ret = regmap_read(regmap, PF1550_CHARG_REG_CHG_SNS, &data);
 82	if (ret < 0)
 83		return ret;
 84
 85	data &= PF1550_CHG_SNS_MASK;
 86
 87	switch (data) {
 88	case PF1550_CHG_SNS_MASK:
 89		*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
 90		break;
 91	case PF1550_CHG_CONSTANT_CURRENT:
 92	case PF1550_CHG_CONSTANT_VOL:
 93	case PF1550_CHG_EOC:
 94		*val = POWER_SUPPLY_CHARGE_TYPE_FAST;
 95		break;
 96	case PF1550_CHG_DONE:
 97	case PF1550_CHG_TIMER_FAULT:
 98	case PF1550_CHG_SUSPEND:
 99	case PF1550_CHG_OFF_INV:
100	case PF1550_CHG_BAT_OVER:
101	case PF1550_CHG_OFF_TEMP:
102	case PF1550_CHG_LINEAR_ONLY:
103		*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
104		break;
105	default:
106		*val = POWER_SUPPLY_CHARGE_TYPE_UNKNOWN;
107	}
108
109	return 0;
110}
111
112/*
113 * Supported health statuses:
114 *  - POWER_SUPPLY_HEALTH_DEAD
115 *  - POWER_SUPPLY_HEALTH_GOOD
116 *  - POWER_SUPPLY_HEALTH_OVERVOLTAGE
117 *  - POWER_SUPPLY_HEALTH_UNKNOWN
118 */
119static int pf1550_get_battery_health(struct regmap *regmap, int *val)
120{
121	unsigned int data;
122	int ret;
123
124	ret = regmap_read(regmap, PF1550_CHARG_REG_BATT_SNS, &data);
125	if (ret < 0)
126		return ret;
127
128	data &= PF1550_BAT_SNS_MASK;
129
130	switch (data) {
131	case PF1550_BAT_NO_DETECT:
132		*val = POWER_SUPPLY_HEALTH_NO_BATTERY;
133		break;
134	case PF1550_BAT_NO_VBUS:
135	case PF1550_BAT_LOW_THAN_PRECHARG:
136	case PF1550_BAT_CHARG_FAIL:
137	case PF1550_BAT_HIGH_THAN_PRECHARG:
138		*val = POWER_SUPPLY_HEALTH_GOOD;
139		break;
140	case PF1550_BAT_OVER_VOL:
141		*val = POWER_SUPPLY_HEALTH_OVERVOLTAGE;
142		break;
143	default:
144		*val = POWER_SUPPLY_HEALTH_UNKNOWN;
145		break;
146	}
147
148	return 0;
149}
150
151static int pf1550_get_present(struct regmap *regmap, int *val)
152{
153	unsigned int data;
154	int ret;
155
156	ret = regmap_read(regmap, PF1550_CHARG_REG_BATT_SNS, &data);
157	if (ret < 0)
158		return ret;
159
160	data &= PF1550_BAT_SNS_MASK;
161	*val = (data == PF1550_BAT_NO_DETECT) ? 0 : 1;
162
163	return 0;
164}
165
166static int pf1550_get_online(struct regmap *regmap, int *val)
167{
168	unsigned int data;
169	int ret;
170
171	ret = regmap_read(regmap, PF1550_CHARG_REG_VBUS_SNS, &data);
172	if (ret < 0)
173		return ret;
174
175	*val = (data & PF1550_VBUS_VALID) ? 1 : 0;
176
177	return 0;
178}
179
180static void pf1550_chg_bat_work(struct work_struct *work)
181{
182	struct pf1550_charger *chg = container_of(to_delayed_work(work),
183						  struct pf1550_charger,
184						  bat_sense_work);
185	unsigned int data;
186
187	if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_BATT_SNS, &data)) {
188		dev_err(chg->dev, "Read BATT_SNS error.\n");
189		return;
190	}
191
192	switch (data & PF1550_BAT_SNS_MASK) {
193	case PF1550_BAT_NO_VBUS:
194		dev_dbg(chg->dev, "No valid VBUS input.\n");
195		break;
196	case PF1550_BAT_LOW_THAN_PRECHARG:
197		dev_dbg(chg->dev, "VBAT < VPRECHG.LB.\n");
198		break;
199	case PF1550_BAT_CHARG_FAIL:
200		dev_dbg(chg->dev, "Battery charging failed.\n");
201		break;
202	case PF1550_BAT_HIGH_THAN_PRECHARG:
203		dev_dbg(chg->dev, "VBAT > VPRECHG.LB.\n");
204		break;
205	case PF1550_BAT_OVER_VOL:
206		dev_dbg(chg->dev, "VBAT > VBATOV.\n");
207		break;
208	case PF1550_BAT_NO_DETECT:
209		dev_dbg(chg->dev, "Battery not detected.\n");
210		break;
211	default:
212		dev_err(chg->dev, "Unknown value read:%x\n",
213			data & PF1550_CHG_SNS_MASK);
214	}
215}
216
217static void pf1550_chg_chg_work(struct work_struct *work)
218{
219	struct pf1550_charger *chg = container_of(to_delayed_work(work),
220						  struct pf1550_charger,
221						  chg_sense_work);
222	unsigned int data;
223
224	if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_SNS, &data)) {
225		dev_err(chg->dev, "Read CHG_SNS error.\n");
226		return;
227	}
228
229	switch (data & PF1550_CHG_SNS_MASK) {
230	case PF1550_CHG_PRECHARGE:
231		dev_dbg(chg->dev, "In pre-charger mode.\n");
232		break;
233	case PF1550_CHG_CONSTANT_CURRENT:
234		dev_dbg(chg->dev, "In fast-charge constant current mode.\n");
235		break;
236	case PF1550_CHG_CONSTANT_VOL:
237		dev_dbg(chg->dev, "In fast-charge constant voltage mode.\n");
238		break;
239	case PF1550_CHG_EOC:
240		dev_dbg(chg->dev, "In EOC mode.\n");
241		break;
242	case PF1550_CHG_DONE:
243		dev_dbg(chg->dev, "In DONE mode.\n");
244		break;
245	case PF1550_CHG_TIMER_FAULT:
246		dev_info(chg->dev, "In timer fault mode.\n");
247		break;
248	case PF1550_CHG_SUSPEND:
249		dev_info(chg->dev, "In thermistor suspend mode.\n");
250		break;
251	case PF1550_CHG_OFF_INV:
252		dev_info(chg->dev, "Input invalid, charger off.\n");
253		break;
254	case PF1550_CHG_BAT_OVER:
255		dev_warn(chg->dev, "Battery over-voltage.\n");
256		break;
257	case PF1550_CHG_OFF_TEMP:
258		dev_info(chg->dev, "Temp high, charger off.\n");
259		break;
260	case PF1550_CHG_LINEAR_ONLY:
261		dev_dbg(chg->dev, "In Linear mode, not charging.\n");
262		break;
263	default:
264		dev_err(chg->dev, "Unknown value read:%x\n",
265			data & PF1550_CHG_SNS_MASK);
266	}
267}
268
269static void pf1550_chg_vbus_work(struct work_struct *work)
270{
271	struct pf1550_charger *chg = container_of(to_delayed_work(work),
272						  struct pf1550_charger,
273						  vbus_sense_work);
274	unsigned int data;
275
276	if (regmap_read(chg->pf1550->regmap, PF1550_CHARG_REG_VBUS_SNS, &data)) {
277		dev_err(chg->dev, "Read VBUS_SNS error.\n");
278		return;
279	}
280
281	if (data & PF1550_VBUS_UVLO) {
282		dev_dbg(chg->dev, "VBUS detached.\n");
283		power_supply_changed(chg->battery);
284	}
285	if (data & PF1550_VBUS_IN2SYS)
286		dev_dbg(chg->dev, "VBUS_IN2SYS_SNS.\n");
287	if (data & PF1550_VBUS_OVLO)
288		dev_dbg(chg->dev, "VBUS_OVLO_SNS.\n");
289	if (data & PF1550_VBUS_VALID) {
290		dev_dbg(chg->dev, "VBUS attached.\n");
291		power_supply_changed(chg->charger);
292	}
293}
294
295static irqreturn_t pf1550_charger_irq_handler(int irq, void *data)
296{
297	struct pf1550_charger *chg = data;
298	struct device *dev = chg->dev;
299	int i, irq_type = -1;
300
301	for (i = 0; i < PF1550_CHARGER_IRQ_NR; i++)
302		if (irq == chg->virqs[i])
303			irq_type = i;
304
305	switch (irq_type) {
306	case PF1550_CHARG_IRQ_BAT2SOCI:
307		dev_info(dev, "BAT to SYS Overcurrent interrupt.\n");
308		break;
309	case PF1550_CHARG_IRQ_BATI:
310		schedule_delayed_work(&chg->bat_sense_work,
311				      msecs_to_jiffies(10));
312		break;
313	case PF1550_CHARG_IRQ_CHGI:
314		schedule_delayed_work(&chg->chg_sense_work,
315				      msecs_to_jiffies(10));
316		break;
317	case PF1550_CHARG_IRQ_VBUSI:
318		schedule_delayed_work(&chg->vbus_sense_work,
319				      msecs_to_jiffies(10));
320		break;
321	case PF1550_CHARG_IRQ_THMI:
322		dev_info(dev, "Thermal interrupt.\n");
323		break;
324	default:
325		dev_err(dev, "unknown interrupt occurred.\n");
326	}
327
328	return IRQ_HANDLED;
329}
330
331static enum power_supply_property pf1550_charger_props[] = {
332	POWER_SUPPLY_PROP_ONLINE,
333	POWER_SUPPLY_PROP_MODEL_NAME,
334	POWER_SUPPLY_PROP_MANUFACTURER,
335};
336
337static enum power_supply_property pf1550_battery_props[] = {
338	POWER_SUPPLY_PROP_STATUS,
339	POWER_SUPPLY_PROP_CHARGE_TYPE,
340	POWER_SUPPLY_PROP_HEALTH,
341	POWER_SUPPLY_PROP_PRESENT,
342	POWER_SUPPLY_PROP_MODEL_NAME,
343	POWER_SUPPLY_PROP_MANUFACTURER,
344};
345
346static int pf1550_charger_get_property(struct power_supply *psy,
347				       enum power_supply_property psp,
348				       union power_supply_propval *val)
349{
350	struct pf1550_charger *chg = power_supply_get_drvdata(psy);
351	struct regmap *regmap = chg->pf1550->regmap;
352	int ret = 0;
353
354	switch (psp) {
355	case POWER_SUPPLY_PROP_STATUS:
356		ret = pf1550_get_charger_state(regmap, &val->intval);
357		break;
358	case POWER_SUPPLY_PROP_CHARGE_TYPE:
359		ret = pf1550_get_charge_type(regmap, &val->intval);
360		break;
361	case POWER_SUPPLY_PROP_HEALTH:
362		ret = pf1550_get_battery_health(regmap, &val->intval);
363		break;
364	case POWER_SUPPLY_PROP_PRESENT:
365		ret = pf1550_get_present(regmap, &val->intval);
366		break;
367	case POWER_SUPPLY_PROP_ONLINE:
368		ret = pf1550_get_online(regmap, &val->intval);
369		break;
370	case POWER_SUPPLY_PROP_MODEL_NAME:
371		val->strval = "PF1550";
372		break;
373	case POWER_SUPPLY_PROP_MANUFACTURER:
374		val->strval = "NXP";
375		break;
376	default:
377		return -EINVAL;
378	}
379
380	return ret;
381}
382
383static const struct power_supply_desc pf1550_charger_desc = {
384	.name = "pf1550-charger",
385	.type = POWER_SUPPLY_TYPE_MAINS,
386	.properties = pf1550_charger_props,
387	.num_properties = ARRAY_SIZE(pf1550_charger_props),
388	.get_property = pf1550_charger_get_property,
389};
390
391static const struct power_supply_desc pf1550_battery_desc = {
392	.name = "pf1550-battery",
393	.type = POWER_SUPPLY_TYPE_BATTERY,
394	.properties = pf1550_battery_props,
395	.num_properties = ARRAY_SIZE(pf1550_battery_props),
396	.get_property = pf1550_charger_get_property,
397};
398
399static int pf1550_set_constant_volt(struct pf1550_charger *chg,
400				    unsigned int uvolt)
401{
402	unsigned int data;
403
404	if (uvolt >= 3500000 && uvolt <= 4440000)
405		data = 8 + (uvolt - 3500000) / 20000;
406	else
407		return dev_err_probe(chg->dev, -EINVAL,
408				     "Wrong value for constant voltage\n");
409
410	dev_dbg(chg->dev, "Charging constant voltage: %u (0x%x)\n", uvolt,
411		data);
412
413	return regmap_update_bits(chg->pf1550->regmap,
414				  PF1550_CHARG_REG_BATT_REG,
415				  PF1550_CHARG_REG_BATT_REG_CHGCV_MASK, data);
416}
417
418static int pf1550_set_min_system_volt(struct pf1550_charger *chg,
419				      unsigned int uvolt)
420{
421	unsigned int data;
422
423	switch (uvolt) {
424	case 3500000:
425		data = 0x0;
426		break;
427	case 3700000:
428		data = 0x1;
429		break;
430	case 4300000:
431		data = 0x2;
432		break;
433	default:
434		return dev_err_probe(chg->dev, -EINVAL,
435				     "Wrong value for minimum system voltage\n");
436	}
437
438	data <<= PF1550_CHARG_REG_BATT_REG_VMINSYS_SHIFT;
439
440	dev_dbg(chg->dev, "Minimum system regulation voltage: %u (0x%x)\n",
441		uvolt, data);
442
443	return regmap_update_bits(chg->pf1550->regmap,
444				  PF1550_CHARG_REG_BATT_REG,
445				  PF1550_CHARG_REG_BATT_REG_VMINSYS_MASK, data);
446}
447
448static int pf1550_set_thermal_regulation_temp(struct pf1550_charger *chg,
449					      unsigned int cells)
450{
451	unsigned int data;
452
453	switch (cells) {
454	case 80:
455		data = 0x0;
456		break;
457	case 95:
458		data = 0x1;
459		break;
460	case 110:
461		data = 0x2;
462		break;
463	case 125:
464		data = 0x3;
465		break;
466	default:
467		return dev_err_probe(chg->dev, -EINVAL,
468				     "Wrong value for thermal temperature\n");
469	}
470
471	data <<= PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_SHIFT;
472
473	dev_dbg(chg->dev, "Thermal regulation loop temperature: %u (0x%x)\n",
474		cells, data);
475
476	return regmap_update_bits(chg->pf1550->regmap,
477				  PF1550_CHARG_REG_THM_REG_CNFG,
478				  PF1550_CHARG_REG_THM_REG_CNFG_REGTEMP_MASK,
479				  data);
480}
481
482/*
483 * Sets charger registers to proper and safe default values.
484 */
485static int pf1550_reg_init(struct pf1550_charger *chg)
486{
487	struct power_supply_battery_info *info;
488	struct device *dev = chg->dev;
489	int ret;
490
491	/* Unmask charger interrupt, mask DPMI and reserved bit */
492	ret =  regmap_write(chg->pf1550->regmap, PF1550_CHARG_REG_CHG_INT_MASK,
493			    PF1550_CHG_INT_MASK);
494	if (ret)
495		return dev_err_probe(dev, ret,
496				     "Error unmask charger interrupt\n");
497
498	ret = pf1550_set_constant_volt(chg, chg->constant_volt);
499	if (ret)
500		return ret;
501
502	ret = pf1550_set_min_system_volt(chg, chg->min_system_volt);
503	if (ret)
504		return ret;
505
506	ret = pf1550_set_thermal_regulation_temp(chg,
507						 chg->thermal_regulation_temp);
508	if (ret)
509		return ret;
510
511	/*
512	 * The PF1550 charger has 3 modes of operation. By default, the charger
513	 * is in mode 1; it remains off. Appropriate for applications not using
514	 * a battery. The other supported mode is mode 2, the charger is turned
515	 * on to charge a battery when present.
516	 */
517	if (power_supply_get_battery_info(chg->charger, &info)) {
518		ret = regmap_write(chg->pf1550->regmap,
519				   PF1550_CHARG_REG_CHG_OPER,
520				   PF1550_CHG_BAT_ON);
521		if (ret)
522			return dev_err_probe(dev, ret,
523					     "Error turn on charger\n");
524	}
525
526	return 0;
527}
528
529static void pf1550_dt_parse_dev_info(struct pf1550_charger *chg)
530{
531	struct power_supply_battery_info *info;
532	struct device *dev = chg->dev;
533
534	if (device_property_read_u32(dev->parent, "nxp,min-system-microvolt",
535				     &chg->min_system_volt))
536		chg->min_system_volt = PF1550_DEFAULT_MIN_SYSTEM_VOLT;
537
538	if (device_property_read_u32(dev->parent,
539				     "nxp,thermal-regulation-celsius",
540				     &chg->thermal_regulation_temp))
541		chg->thermal_regulation_temp = PF1550_DEFAULT_THERMAL_TEMP;
542
543	if (power_supply_get_battery_info(chg->charger, &info))
544		chg->constant_volt = PF1550_DEFAULT_CONSTANT_VOLT;
545	else
546		chg->constant_volt = info->constant_charge_voltage_max_uv;
547}
548
549static int pf1550_charger_probe(struct platform_device *pdev)
550{
551	const struct pf1550_ddata *pf1550 = dev_get_drvdata(pdev->dev.parent);
552	struct power_supply_config psy_cfg = {};
553	struct pf1550_charger *chg;
554	int i, irq, ret;
555
556	chg = devm_kzalloc(&pdev->dev, sizeof(*chg), GFP_KERNEL);
557	if (!chg)
558		return -ENOMEM;
559
560	chg->dev = &pdev->dev;
561	chg->pf1550 = pf1550;
562
563	if (!chg->pf1550->regmap)
564		return dev_err_probe(&pdev->dev, -ENODEV,
565				     "failed to get regmap\n");
566
567	platform_set_drvdata(pdev, chg);
568
569	ret = devm_delayed_work_autocancel(chg->dev, &chg->vbus_sense_work,
570					   pf1550_chg_vbus_work);
571	if (ret)
572		return dev_err_probe(chg->dev, ret,
573				     "failed to add vbus sense work\n");
574
575	ret = devm_delayed_work_autocancel(chg->dev, &chg->chg_sense_work,
576					   pf1550_chg_chg_work);
577	if (ret)
578		return dev_err_probe(chg->dev, ret,
579				     "failed to add charger sense work\n");
580
581	ret = devm_delayed_work_autocancel(chg->dev, &chg->bat_sense_work,
582					   pf1550_chg_bat_work);
583	if (ret)
584		return dev_err_probe(chg->dev, ret,
585				     "failed to add battery sense work\n");
586
587	psy_cfg.drv_data = chg;
588
589	chg->charger = devm_power_supply_register(&pdev->dev,
590						  &pf1550_charger_desc,
591						  &psy_cfg);
592	if (IS_ERR(chg->charger))
593		return dev_err_probe(&pdev->dev, PTR_ERR(chg->charger),
594				     "failed: power supply register\n");
595
596	chg->battery = devm_power_supply_register(&pdev->dev,
597						  &pf1550_battery_desc,
598						  &psy_cfg);
599	if (IS_ERR(chg->battery))
600		return dev_err_probe(&pdev->dev, PTR_ERR(chg->battery),
601				     "failed: power supply register\n");
602
603	for (i = 0; i < PF1550_CHARGER_IRQ_NR; i++) {
604		irq = platform_get_irq(pdev, i);
605		if (irq < 0)
606			return irq;
607
608		chg->virqs[i] = irq;
609
610		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
611						pf1550_charger_irq_handler,
612						IRQF_NO_SUSPEND,
613						"pf1550-charger", chg);
614		if (ret)
615			return dev_err_probe(&pdev->dev, ret,
616					     "failed irq request\n");
617	}
618
619	pf1550_dt_parse_dev_info(chg);
620
621	return pf1550_reg_init(chg);
622}
623
624static const struct platform_device_id pf1550_charger_id[] = {
625	{ "pf1550-charger", },
626	{ /* sentinel */ }
627};
628MODULE_DEVICE_TABLE(platform, pf1550_charger_id);
629
630static struct platform_driver pf1550_charger_driver = {
631	.driver = {
632		.name	= "pf1550-charger",
633	},
634	.probe		= pf1550_charger_probe,
635	.id_table	= pf1550_charger_id,
636};
637module_platform_driver(pf1550_charger_driver);
638
639MODULE_AUTHOR("Robin Gong <yibin.gong@freescale.com>");
640MODULE_DESCRIPTION("PF1550 charger driver");
641MODULE_LICENSE("GPL");
~/ Read-only file preview.