File Index
Files and directories
Sign in
FILE

ip5xxx_power.c

File preview with syntax highlighting.

linux-6.19.14/drivers/power/supply/ip5xxx_power.c C
  1// SPDX-License-Identifier: GPL-2.0
  2//
  3// Copyright (C) 2021 Samuel Holland <samuel@sholland.org>
  4
  5#include <linux/i2c.h>
  6#include <linux/module.h>
  7#include <linux/power_supply.h>
  8#include <linux/regmap.h>
  9
 10#define IP5XXX_BAT_TYPE_4_2V			0x0
 11#define IP5XXX_BAT_TYPE_4_3V			0x1
 12#define IP5XXX_BAT_TYPE_4_35V			0x2
 13#define IP5XXX_BAT_TYPE_4_4V			0x3
 14#define IP5XXX_CHG_STAT_IDLE			0x0
 15#define IP5XXX_CHG_STAT_TRICKLE		0x1
 16#define IP5XXX_CHG_STAT_CONST_VOLT		0x2
 17#define IP5XXX_CHG_STAT_CONST_CUR		0x3
 18#define IP5XXX_CHG_STAT_CONST_VOLT_STOP	0x4
 19#define IP5XXX_CHG_STAT_FULL			0x5
 20#define IP5XXX_CHG_STAT_TIMEOUT		0x6
 21
 22struct ip5xxx {
 23	struct regmap *regmap;
 24	bool initialized;
 25	struct {
 26		struct {
 27			/* Charger enable */
 28			struct regmap_field *enable;
 29			/* Constant voltage value */
 30			struct regmap_field *const_volt_sel;
 31			/* Constant current value */
 32			struct regmap_field *const_curr_sel;
 33			/* Charger status */
 34			struct regmap_field *status;
 35			/* Charging ended flag */
 36			struct regmap_field *chg_end;
 37			/* Timeout flags (CV, charge, trickle) */
 38			struct regmap_field *timeout;
 39			/* Overvoltage limit */
 40			struct regmap_field *vin_overvolt;
 41		} charger;
 42		struct {
 43			/* Boost converter enable */
 44			struct regmap_field *enable;
 45			struct {
 46				/* Light load shutdown enable */
 47				struct regmap_field *enable;
 48				/* Light load shutdown current limit */
 49				struct regmap_field *i_limit;
 50			} light_load_shutdown;
 51			/* Automatic powerup on increased load */
 52			struct regmap_field *load_powerup_en;
 53			/* Automatic powerup on VIN pull-out */
 54			struct regmap_field *vin_pullout_en;
 55			/* Undervoltage limit */
 56			struct regmap_field *undervolt_limit;
 57			/* Light load status flag */
 58			struct regmap_field *light_load_status;
 59		} boost;
 60		struct {
 61			/* NTC disable */
 62			struct regmap_field *ntc_dis;
 63			/* Battery voltage type */
 64			struct regmap_field *type;
 65			/* Battery voltage autoset from Vset pin */
 66			struct regmap_field *vset_en;
 67			struct {
 68				/* Battery measurement registers */
 69				struct ip5xxx_battery_adc_regs {
 70					struct regmap_field *low;
 71					struct regmap_field *high;
 72				} volt, curr, open_volt;
 73			} adc;
 74		} battery;
 75		struct {
 76			/* Double/long press shutdown enable */
 77			struct regmap_field *shdn_enable;
 78			/* WLED activation: double press or long press */
 79			struct regmap_field *wled_mode;
 80			/* Shutdown activation: double press or long press */
 81			struct regmap_field *shdn_mode;
 82			/* Long press time */
 83			struct regmap_field *long_press_time;
 84			/* Button pressed */
 85			struct regmap_field *pressed;
 86			/* Button long-pressed */
 87			struct regmap_field *long_pressed;
 88			/* Button short-pressed */
 89			struct regmap_field *short_pressed;
 90		} btn;
 91		struct {
 92			/* WLED enable */
 93			struct regmap_field *enable;
 94			/* WLED detect */
 95			struct regmap_field *detect_en;
 96			/* WLED present */
 97			struct regmap_field *present;
 98		} wled;
 99	} regs;
100
101	/* Maximum supported battery voltage (via regs.battery.type) */
102	int vbat_max;
103	/* Scaling constants for regs.boost.undervolt_limit */
104	struct {
105		int setpoint;
106		int microvolts_per_bit;
107	} boost_undervolt;
108	/* Scaling constants for regs.charger.const_curr_sel */
109	struct {
110		int setpoint;
111	} const_curr;
112	/* Whether regs.charger.chg_end is inverted */
113	u8 chg_end_inverted;
114};
115
116#define REG_FIELD_UNSUPPORTED { .lsb = 1 }
117/* Register fields layout. Unsupported registers marked as { .lsb = 1 } */
118struct ip5xxx_regfield_config {
119	const struct reg_field charger_enable;
120	const struct reg_field charger_const_volt_sel;
121	const struct reg_field charger_const_curr_sel;
122	const struct reg_field charger_status;
123	const struct reg_field charger_chg_end;
124	const struct reg_field charger_timeout;
125	const struct reg_field charger_vin_overvolt;
126	const struct reg_field boost_enable;
127	const struct reg_field boost_llshdn_enable;
128	const struct reg_field boost_llshdn_i_limit;
129	const struct reg_field boost_load_powerup_en;
130	const struct reg_field boost_vin_pullout_en;
131	const struct reg_field boost_undervolt_limit;
132	const struct reg_field boost_light_load_status;
133	const struct reg_field battery_ntc_dis;
134	const struct reg_field battery_type;
135	const struct reg_field battery_vset_en;
136	const struct reg_field battery_adc_volt_low;
137	const struct reg_field battery_adc_volt_high;
138	const struct reg_field battery_adc_curr_low;
139	const struct reg_field battery_adc_curr_high;
140	const struct reg_field battery_adc_ovolt_low;
141	const struct reg_field battery_adc_ovolt_high;
142	const struct reg_field btn_shdn_enable;
143	const struct reg_field btn_wled_mode;
144	const struct reg_field btn_shdn_mode;
145	const struct reg_field btn_long_press_time;
146	const struct reg_field btn_pressed;
147	const struct reg_field btn_long_pressed;
148	const struct reg_field btn_short_pressed;
149	const struct reg_field wled_enable;
150	const struct reg_field wled_detect_en;
151	const struct reg_field wled_present;
152
153	int vbat_max;
154	int boost_undervolt_setpoint;
155	int boost_undervolt_uv_per_bit;
156	int const_curr_setpoint;
157	u8  chg_end_inverted;
158};
159
160/*
161 * The IP5xxx charger only responds on I2C when it is "awake". The charger is
162 * generally only awake when VIN is powered or when its boost converter is
163 * enabled. Going into shutdown resets all register values. To handle this:
164 *  1) When any bus error occurs, assume the charger has gone into shutdown.
165 *  2) Attempt the initialization sequence on each subsequent register access
166 *     until it succeeds.
167 */
168static int ip5xxx_read(struct ip5xxx *ip5xxx, struct regmap_field *field,
169		       unsigned int *val)
170{
171	int ret;
172
173	if (!field)
174		return -EOPNOTSUPP;
175
176	ret = regmap_field_read(field, val);
177	if (ret)
178		ip5xxx->initialized = false;
179
180	return ret;
181}
182
183static int ip5xxx_write(struct ip5xxx *ip5xxx, struct regmap_field *field,
184			unsigned int val)
185{
186	int ret;
187
188	if (!field)
189		return -EOPNOTSUPP;
190
191	ret = regmap_field_write(field, val);
192	if (ret)
193		ip5xxx->initialized = false;
194
195	return ret;
196}
197
198static int ip5xxx_initialize(struct power_supply *psy)
199{
200	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
201	int ret;
202
203	if (ip5xxx->initialized)
204		return 0;
205
206	/*
207	 * Disable shutdown under light load.
208	 * Enable power on when under load.
209	 */
210	if (ip5xxx->regs.boost.light_load_shutdown.enable) {
211		ret = ip5xxx_write(ip5xxx, ip5xxx->regs.boost.light_load_shutdown.enable, 0);
212		if (ret)
213			return ret;
214	}
215	ret = ip5xxx_write(ip5xxx, ip5xxx->regs.boost.load_powerup_en, 1);
216	if (ret)
217		return ret;
218
219	/*
220	 * Enable shutdown after a long button press (as configured below).
221	 */
222	ret = ip5xxx_write(ip5xxx, ip5xxx->regs.btn.shdn_enable, 1);
223	if (ret)
224		return ret;
225
226	/*
227	 * Power on automatically when VIN is removed.
228	 */
229	ret = ip5xxx_write(ip5xxx, ip5xxx->regs.boost.vin_pullout_en, 1);
230	if (ret)
231		return ret;
232
233	/*
234	 * Enable the NTC.
235	 * Configure the button for two presses => LED, long press => shutdown.
236	 */
237	if (ip5xxx->regs.battery.ntc_dis) {
238		ret = ip5xxx_write(ip5xxx, ip5xxx->regs.battery.ntc_dis, 0);
239		if (ret)
240			return ret;
241	}
242	ret = ip5xxx_write(ip5xxx, ip5xxx->regs.btn.wled_mode, 1);
243	if (ret)
244		return ret;
245	ret = ip5xxx_write(ip5xxx, ip5xxx->regs.btn.shdn_mode, 1);
246	if (ret)
247		return ret;
248
249	ip5xxx->initialized = true;
250	dev_dbg(psy->dev.parent, "Initialized after power on\n");
251
252	return 0;
253}
254
255static const enum power_supply_property ip5xxx_battery_properties[] = {
256	POWER_SUPPLY_PROP_STATUS,
257	POWER_SUPPLY_PROP_CHARGE_TYPE,
258	POWER_SUPPLY_PROP_HEALTH,
259	POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
260	POWER_SUPPLY_PROP_VOLTAGE_NOW,
261	POWER_SUPPLY_PROP_VOLTAGE_OCV,
262	POWER_SUPPLY_PROP_CURRENT_NOW,
263	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
264	POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
265	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
266	POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
267};
268
269static int ip5xxx_battery_get_status(struct ip5xxx *ip5xxx, int *val)
270{
271	unsigned int rval;
272	int ret;
273
274	if (!ip5xxx->regs.charger.status) {
275		// Fall-back to Charging Ended bit
276		ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.chg_end, &rval);
277		if (ret)
278			return ret;
279
280		if (rval == ip5xxx->chg_end_inverted)
281			*val = POWER_SUPPLY_STATUS_CHARGING;
282		else
283			*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
284		return 0;
285	}
286
287	ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.status, &rval);
288	if (ret)
289		return ret;
290
291	switch (rval) {
292	case IP5XXX_CHG_STAT_IDLE:
293		*val = POWER_SUPPLY_STATUS_DISCHARGING;
294		break;
295	case IP5XXX_CHG_STAT_TRICKLE:
296	case IP5XXX_CHG_STAT_CONST_CUR:
297	case IP5XXX_CHG_STAT_CONST_VOLT:
298		*val = POWER_SUPPLY_STATUS_CHARGING;
299		break;
300	case IP5XXX_CHG_STAT_CONST_VOLT_STOP:
301	case IP5XXX_CHG_STAT_FULL:
302		*val = POWER_SUPPLY_STATUS_FULL;
303		break;
304	case IP5XXX_CHG_STAT_TIMEOUT:
305		*val = POWER_SUPPLY_STATUS_NOT_CHARGING;
306		break;
307	default:
308		return -EINVAL;
309	}
310
311	return 0;
312}
313
314static int ip5xxx_battery_get_charge_type(struct ip5xxx *ip5xxx, int *val)
315{
316	unsigned int rval;
317	int ret;
318
319	ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.status, &rval);
320	if (ret)
321		return ret;
322
323	switch (rval) {
324	case IP5XXX_CHG_STAT_IDLE:
325	case IP5XXX_CHG_STAT_CONST_VOLT_STOP:
326	case IP5XXX_CHG_STAT_FULL:
327	case IP5XXX_CHG_STAT_TIMEOUT:
328		*val = POWER_SUPPLY_CHARGE_TYPE_NONE;
329		break;
330	case IP5XXX_CHG_STAT_TRICKLE:
331		*val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
332		break;
333	case IP5XXX_CHG_STAT_CONST_CUR:
334	case IP5XXX_CHG_STAT_CONST_VOLT:
335		*val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
336		break;
337	default:
338		return -EINVAL;
339	}
340
341	return 0;
342}
343
344static int ip5xxx_battery_get_health(struct ip5xxx *ip5xxx, int *val)
345{
346	unsigned int rval;
347	int ret;
348
349	ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.timeout, &rval);
350	if (ret)
351		return ret;
352
353	if (rval)
354		*val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
355	else
356		*val = POWER_SUPPLY_HEALTH_GOOD;
357
358	return 0;
359}
360
361static int ip5xxx_battery_get_voltage_max(struct ip5xxx *ip5xxx, int *val)
362{
363	unsigned int rval;
364	int ret;
365
366	ret = ip5xxx_read(ip5xxx, ip5xxx->regs.battery.type, &rval);
367	if (ret)
368		return ret;
369
370	/*
371	 * It is not clear what this will return if
372	 * IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN is not set...
373	 */
374	switch (rval) {
375	case IP5XXX_BAT_TYPE_4_2V:
376		*val = 4200000;
377		break;
378	case IP5XXX_BAT_TYPE_4_3V:
379		*val = 4300000;
380		break;
381	case IP5XXX_BAT_TYPE_4_35V:
382		*val = 4350000;
383		break;
384	case IP5XXX_BAT_TYPE_4_4V:
385		*val = 4400000;
386		break;
387	default:
388		return -EINVAL;
389	}
390
391	return 0;
392}
393
394static int ip5xxx_battery_read_adc(struct ip5xxx *ip5xxx,
395				   struct ip5xxx_battery_adc_regs *regs, int *val)
396{
397	unsigned int hi, lo;
398	int ret;
399
400	ret = ip5xxx_read(ip5xxx, regs->low, &lo);
401	if (ret)
402		return ret;
403
404	ret = ip5xxx_read(ip5xxx, regs->high, &hi);
405	if (ret)
406		return ret;
407
408	*val = sign_extend32(hi << 8 | lo, 13);
409
410	return 0;
411}
412
413static int ip5xxx_battery_get_property(struct power_supply *psy,
414				       enum power_supply_property psp,
415				       union power_supply_propval *val)
416{
417	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
418	int raw, ret, vmax;
419	unsigned int rval;
420
421	ret = ip5xxx_initialize(psy);
422	if (ret)
423		return ret;
424
425	switch (psp) {
426	case POWER_SUPPLY_PROP_STATUS:
427		return ip5xxx_battery_get_status(ip5xxx, &val->intval);
428
429	case POWER_SUPPLY_PROP_CHARGE_TYPE:
430		return ip5xxx_battery_get_charge_type(ip5xxx, &val->intval);
431
432	case POWER_SUPPLY_PROP_HEALTH:
433		return ip5xxx_battery_get_health(ip5xxx, &val->intval);
434
435	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
436		return ip5xxx_battery_get_voltage_max(ip5xxx, &val->intval);
437
438	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
439		ret = ip5xxx_battery_read_adc(ip5xxx, &ip5xxx->regs.battery.adc.volt, &raw);
440		if (ret)
441			return ret;
442
443		val->intval = 2600000 + DIV_ROUND_CLOSEST(raw * 26855, 100);
444		return 0;
445
446	case POWER_SUPPLY_PROP_VOLTAGE_OCV:
447		ret = ip5xxx_battery_read_adc(ip5xxx, &ip5xxx->regs.battery.adc.open_volt, &raw);
448		if (ret)
449			return ret;
450
451		val->intval = 2600000 + DIV_ROUND_CLOSEST(raw * 26855, 100);
452		return 0;
453
454	case POWER_SUPPLY_PROP_CURRENT_NOW:
455		ret = ip5xxx_battery_read_adc(ip5xxx, &ip5xxx->regs.battery.adc.curr, &raw);
456		if (ret)
457			return ret;
458
459		val->intval = DIV_ROUND_CLOSEST(raw * 149197, 200);
460		return 0;
461
462	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
463		ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.const_curr_sel, &rval);
464		if (ret)
465			return ret;
466
467		val->intval = ip5xxx->const_curr.setpoint + 100000 * rval;
468		return 0;
469
470	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
471		val->intval = 100000 * 0x1f;
472		return 0;
473
474	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
475		ret = ip5xxx_battery_get_voltage_max(ip5xxx, &vmax);
476		if (ret)
477			return ret;
478
479		ret = ip5xxx_read(ip5xxx, ip5xxx->regs.charger.const_volt_sel, &rval);
480		if (ret)
481			return ret;
482
483		val->intval = vmax + 14000 * rval;
484		return 0;
485
486	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
487		ret = ip5xxx_battery_get_voltage_max(ip5xxx, &vmax);
488		if (ret)
489			return ret;
490
491		val->intval = vmax + 14000 * 3;
492		return 0;
493
494	default:
495		return -EINVAL;
496	}
497}
498
499static int ip5xxx_battery_set_voltage_max(struct ip5xxx *ip5xxx, int val)
500{
501	unsigned int rval;
502	int ret;
503
504	if (val > ip5xxx->vbat_max)
505		return -EINVAL;
506
507	switch (val) {
508	case 4200000:
509		rval = IP5XXX_BAT_TYPE_4_2V;
510		break;
511	case 4300000:
512		rval = IP5XXX_BAT_TYPE_4_3V;
513		break;
514	case 4350000:
515		rval = IP5XXX_BAT_TYPE_4_35V;
516		break;
517	case 4400000:
518		rval = IP5XXX_BAT_TYPE_4_4V;
519		break;
520	default:
521		return -EINVAL;
522	}
523
524	ret = ip5xxx_write(ip5xxx, ip5xxx->regs.battery.type, rval);
525	if (ret)
526		return ret;
527
528	/* Don't try to auto-detect battery type, even if the IC could */
529	if (ip5xxx->regs.battery.vset_en) {
530		ret = ip5xxx_write(ip5xxx, ip5xxx->regs.battery.vset_en, 1);
531		if (ret)
532			return ret;
533	}
534
535	return 0;
536}
537
538static int ip5xxx_battery_set_property(struct power_supply *psy,
539				       enum power_supply_property psp,
540				       const union power_supply_propval *val)
541{
542	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
543	unsigned int rval;
544	int ret, vmax;
545
546	ret = ip5xxx_initialize(psy);
547	if (ret)
548		return ret;
549
550	switch (psp) {
551	case POWER_SUPPLY_PROP_STATUS:
552		switch (val->intval) {
553		case POWER_SUPPLY_STATUS_CHARGING:
554			rval = 1;
555			break;
556		case POWER_SUPPLY_STATUS_DISCHARGING:
557		case POWER_SUPPLY_STATUS_NOT_CHARGING:
558			rval = 0;
559			break;
560		default:
561			return -EINVAL;
562		}
563		return ip5xxx_write(ip5xxx, ip5xxx->regs.charger.enable, rval);
564
565	case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
566		return ip5xxx_battery_set_voltage_max(ip5xxx, val->intval);
567
568	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
569		rval = (val->intval - ip5xxx->const_curr.setpoint) / 100000;
570		return ip5xxx_write(ip5xxx, ip5xxx->regs.charger.const_curr_sel, rval);
571
572	case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
573		ret = ip5xxx_battery_get_voltage_max(ip5xxx, &vmax);
574		if (ret)
575			return ret;
576
577		rval = (val->intval - vmax) / 14000;
578		return ip5xxx_write(ip5xxx, ip5xxx->regs.charger.const_volt_sel, rval);
579
580	default:
581		return -EINVAL;
582	}
583}
584
585static int ip5xxx_battery_property_is_writeable(struct power_supply *psy,
586						enum power_supply_property psp)
587{
588	return psp == POWER_SUPPLY_PROP_STATUS ||
589	       psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ||
590	       psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT ||
591	       psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
592}
593
594static const struct power_supply_desc ip5xxx_battery_desc = {
595	.name			= "ip5xxx-battery",
596	.type			= POWER_SUPPLY_TYPE_BATTERY,
597	.properties		= ip5xxx_battery_properties,
598	.num_properties		= ARRAY_SIZE(ip5xxx_battery_properties),
599	.get_property		= ip5xxx_battery_get_property,
600	.set_property		= ip5xxx_battery_set_property,
601	.property_is_writeable	= ip5xxx_battery_property_is_writeable,
602};
603
604static const enum power_supply_property ip5xxx_boost_properties[] = {
605	POWER_SUPPLY_PROP_ONLINE,
606	POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
607};
608
609static int ip5xxx_boost_get_property(struct power_supply *psy,
610				     enum power_supply_property psp,
611				     union power_supply_propval *val)
612{
613	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
614	unsigned int rval;
615	int ret;
616
617	ret = ip5xxx_initialize(psy);
618	if (ret)
619		return ret;
620
621	switch (psp) {
622	case POWER_SUPPLY_PROP_ONLINE:
623		ret = ip5xxx_read(ip5xxx, ip5xxx->regs.boost.enable, &rval);
624		if (ret)
625			return ret;
626
627		val->intval = !!rval;
628		return 0;
629
630	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
631		ret = ip5xxx_read(ip5xxx, ip5xxx->regs.boost.undervolt_limit, &rval);
632		if (ret)
633			return ret;
634
635		val->intval = ip5xxx->boost_undervolt.setpoint +
636			      ip5xxx->boost_undervolt.microvolts_per_bit * rval;
637		return 0;
638
639	default:
640		return -EINVAL;
641	}
642}
643
644static int ip5xxx_boost_set_property(struct power_supply *psy,
645				     enum power_supply_property psp,
646				     const union power_supply_propval *val)
647{
648	struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
649	unsigned int rval;
650	int ret;
651
652	ret = ip5xxx_initialize(psy);
653	if (ret)
654		return ret;
655
656	switch (psp) {
657	case POWER_SUPPLY_PROP_ONLINE:
658		return ip5xxx_write(ip5xxx, ip5xxx->regs.boost.enable, !!val->intval);
659
660	case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
661		rval = (val->intval - ip5xxx->boost_undervolt.setpoint) /
662			ip5xxx->boost_undervolt.microvolts_per_bit;
663		return ip5xxx_write(ip5xxx, ip5xxx->regs.boost.undervolt_limit, rval);
664
665	default:
666		return -EINVAL;
667	}
668}
669
670static int ip5xxx_boost_property_is_writeable(struct power_supply *psy,
671					      enum power_supply_property psp)
672{
673	return true;
674}
675
676static const struct power_supply_desc ip5xxx_boost_desc = {
677	.name			= "ip5xxx-boost",
678	.type			= POWER_SUPPLY_TYPE_USB,
679	.properties		= ip5xxx_boost_properties,
680	.num_properties		= ARRAY_SIZE(ip5xxx_boost_properties),
681	.get_property		= ip5xxx_boost_get_property,
682	.set_property		= ip5xxx_boost_set_property,
683	.property_is_writeable	= ip5xxx_boost_property_is_writeable,
684};
685
686static const struct regmap_config ip5xxx_regmap_config = {
687	.reg_bits		= 8,
688	.val_bits		= 8,
689	.max_register		= 0xa9,
690};
691
692static struct ip5xxx_regfield_config ip51xx_fields = {
693	.charger_enable = REG_FIELD(0x01, 1, 1),
694	.charger_const_volt_sel = REG_FIELD(0x24, 1, 2),
695	.charger_const_curr_sel = REG_FIELD(0x25, 0, 4),
696	.charger_status = REG_FIELD(0x71, 5, 7),
697	.charger_chg_end = REG_FIELD(0x71, 3, 3),
698	.charger_timeout = REG_FIELD(0x71, 0, 2),
699	.charger_vin_overvolt = REG_FIELD(0x72, 5, 5),
700	.boost_enable = REG_FIELD(0x01, 2, 2),
701	.boost_llshdn_enable = REG_FIELD(0x02, 1, 1),
702	.boost_llshdn_i_limit = REG_FIELD(0x0c, 3, 7),
703	.boost_load_powerup_en = REG_FIELD(0x02, 0, 0),
704	.boost_vin_pullout_en = REG_FIELD(0x04, 5, 5),
705	.boost_undervolt_limit = REG_FIELD(0x22, 2, 3),
706	.boost_light_load_status = REG_FIELD(0x72, 6, 6),
707	.battery_ntc_dis = REG_FIELD(0x07, 6, 6),
708	.battery_type = REG_FIELD(0x24, 5, 6),
709	.battery_vset_en = REG_FIELD(0x26, 6, 6),
710	.battery_adc_volt_low = REG_FIELD(0xa2, 0, 7),
711	.battery_adc_volt_high = REG_FIELD(0xa3, 0, 5),
712	.battery_adc_curr_low = REG_FIELD(0xa4, 0, 7),
713	.battery_adc_curr_high = REG_FIELD(0xa5, 0, 5),
714	.battery_adc_ovolt_low = REG_FIELD(0xa8, 0, 7),
715	.battery_adc_ovolt_high = REG_FIELD(0xa9, 0, 5),
716	.btn_shdn_enable = REG_FIELD(0x03, 5, 5),
717	.btn_wled_mode = REG_FIELD(0x07, 1, 1),
718	.btn_shdn_mode = REG_FIELD(0x07, 0, 0),
719	.btn_long_press_time = REG_FIELD(0x03, 6, 7),
720	.btn_pressed = REG_FIELD(0x77, 3, 3),
721	.btn_long_pressed = REG_FIELD(0x77, 1, 1),
722	.btn_short_pressed = REG_FIELD(0x77, 0, 0),
723	.wled_enable = REG_FIELD(0x01, 3, 3),
724	.wled_detect_en = REG_FIELD(0x01, 4, 4),
725	.wled_present = REG_FIELD(0x72, 7, 7),
726
727	.vbat_max = 4350000,
728	.boost_undervolt_setpoint = 4530000,
729	.boost_undervolt_uv_per_bit = 100000,
730};
731
732static struct ip5xxx_regfield_config ip5306_fields = {
733	.charger_enable = REG_FIELD(0x00, 4, 4),
734	.charger_const_volt_sel = REG_FIELD(0x22, 0, 1),
735	.charger_const_curr_sel = REG_FIELD(0x24, 0, 4),
736	.charger_status = REG_FIELD_UNSUPPORTED, // other bits...
737	.charger_chg_end = REG_FIELD(0x71, 3, 3),
738	.charger_timeout = REG_FIELD_UNSUPPORTED,
739	.charger_vin_overvolt = REG_FIELD_UNSUPPORTED,
740	.boost_enable = REG_FIELD(0x00, 5, 5),
741	.boost_llshdn_enable = REG_FIELD_UNSUPPORTED,
742	.boost_llshdn_i_limit = REG_FIELD_UNSUPPORTED,
743	.boost_load_powerup_en = REG_FIELD(0x00, 2, 2),
744	.boost_vin_pullout_en = REG_FIELD(0x01, 2, 2),
745	.boost_undervolt_limit = REG_FIELD(0x21, 2, 4),
746	.boost_light_load_status = REG_FIELD(0x72, 2, 2),
747	.battery_ntc_dis = REG_FIELD_UNSUPPORTED,
748	.battery_type = REG_FIELD(0x22, 2, 3),
749	.battery_vset_en = REG_FIELD_UNSUPPORTED,
750	.battery_adc_volt_low = REG_FIELD_UNSUPPORTED,
751	.battery_adc_volt_high = REG_FIELD_UNSUPPORTED,
752	.battery_adc_curr_low = REG_FIELD_UNSUPPORTED,
753	.battery_adc_curr_high = REG_FIELD_UNSUPPORTED,
754	.battery_adc_ovolt_low = REG_FIELD_UNSUPPORTED,
755	.battery_adc_ovolt_high = REG_FIELD_UNSUPPORTED,
756	.btn_shdn_enable = REG_FIELD(0x00, 0, 0),
757	.btn_wled_mode = REG_FIELD(0x01, 6, 6),
758	.btn_shdn_mode = REG_FIELD(0x01, 7, 7),
759	.btn_long_press_time = REG_FIELD(0x02, 4, 4), // +1s
760	.btn_pressed = REG_FIELD_UNSUPPORTED,
761	/* TODO: double press */
762	.btn_long_pressed = REG_FIELD(0x77, 1, 1),
763	.btn_short_pressed = REG_FIELD(0x77, 0, 0),
764	.wled_enable = REG_FIELD_UNSUPPORTED,
765	.wled_detect_en = REG_FIELD_UNSUPPORTED,
766	.wled_present = REG_FIELD_UNSUPPORTED,
767
768	.vbat_max = 4400000,
769	.boost_undervolt_setpoint = 4450000,
770	.boost_undervolt_uv_per_bit = 50000,
771	.const_curr_setpoint = 50000,
772	.chg_end_inverted = 1,
773};
774
775#define ip5xxx_setup_reg(_field, _reg) \
776			do { \
777				if (likely(cfg->_field.lsb <= cfg->_field.msb)) { \
778					struct regmap_field *_tmp = devm_regmap_field_alloc(dev, \
779							ip5xxx->regmap, cfg->_field); \
780					if (!IS_ERR(_tmp)) \
781						ip5xxx->regs._reg = _tmp; \
782				} \
783			} while (0)
784
785static void ip5xxx_setup_regs(struct device *dev, struct ip5xxx *ip5xxx,
786			      const struct ip5xxx_regfield_config *cfg)
787{
788	ip5xxx_setup_reg(charger_enable, charger.enable);
789	ip5xxx_setup_reg(charger_const_volt_sel, charger.const_volt_sel);
790	ip5xxx_setup_reg(charger_const_curr_sel, charger.const_curr_sel);
791	ip5xxx_setup_reg(charger_status, charger.status);
792	ip5xxx_setup_reg(charger_chg_end, charger.chg_end);
793	ip5xxx_setup_reg(charger_timeout, charger.timeout);
794	ip5xxx_setup_reg(charger_vin_overvolt, charger.vin_overvolt);
795	ip5xxx_setup_reg(boost_enable, boost.enable);
796	ip5xxx_setup_reg(boost_llshdn_enable, boost.light_load_shutdown.enable);
797	ip5xxx_setup_reg(boost_llshdn_i_limit, boost.light_load_shutdown.i_limit);
798	ip5xxx_setup_reg(boost_load_powerup_en, boost.load_powerup_en);
799	ip5xxx_setup_reg(boost_vin_pullout_en, boost.vin_pullout_en);
800	ip5xxx_setup_reg(boost_undervolt_limit, boost.undervolt_limit);
801	ip5xxx_setup_reg(boost_light_load_status, boost.light_load_status);
802	ip5xxx_setup_reg(battery_ntc_dis, battery.ntc_dis);
803	ip5xxx_setup_reg(battery_type, battery.type);
804	ip5xxx_setup_reg(battery_vset_en, battery.vset_en);
805	ip5xxx_setup_reg(battery_adc_volt_low, battery.adc.volt.low);
806	ip5xxx_setup_reg(battery_adc_volt_high, battery.adc.volt.high);
807	ip5xxx_setup_reg(battery_adc_curr_low, battery.adc.curr.low);
808	ip5xxx_setup_reg(battery_adc_curr_high, battery.adc.curr.high);
809	ip5xxx_setup_reg(battery_adc_ovolt_low, battery.adc.open_volt.low);
810	ip5xxx_setup_reg(battery_adc_ovolt_high, battery.adc.open_volt.high);
811	ip5xxx_setup_reg(btn_shdn_enable, btn.shdn_enable);
812	ip5xxx_setup_reg(btn_wled_mode, btn.wled_mode);
813	ip5xxx_setup_reg(btn_shdn_mode, btn.shdn_mode);
814	ip5xxx_setup_reg(btn_long_press_time, btn.long_press_time);
815	ip5xxx_setup_reg(btn_pressed, btn.pressed);
816	ip5xxx_setup_reg(btn_long_pressed, btn.long_pressed);
817	ip5xxx_setup_reg(btn_short_pressed, btn.short_pressed);
818	ip5xxx_setup_reg(wled_enable, wled.enable);
819	ip5xxx_setup_reg(wled_detect_en, wled.detect_en);
820	ip5xxx_setup_reg(wled_present, wled.present);
821
822	ip5xxx->vbat_max = cfg->vbat_max;
823	ip5xxx->boost_undervolt.setpoint = cfg->boost_undervolt_setpoint;
824	ip5xxx->boost_undervolt.microvolts_per_bit = cfg->boost_undervolt_uv_per_bit;
825	ip5xxx->const_curr.setpoint = cfg->const_curr_setpoint;
826	ip5xxx->chg_end_inverted = cfg->chg_end_inverted;
827}
828
829static int ip5xxx_power_probe(struct i2c_client *client)
830{
831	const struct ip5xxx_regfield_config *fields;
832	struct power_supply_config psy_cfg = {};
833	struct device *dev = &client->dev;
834	struct power_supply *psy;
835	struct ip5xxx *ip5xxx;
836
837	ip5xxx = devm_kzalloc(dev, sizeof(*ip5xxx), GFP_KERNEL);
838	if (!ip5xxx)
839		return -ENOMEM;
840
841	ip5xxx->regmap = devm_regmap_init_i2c(client, &ip5xxx_regmap_config);
842	if (IS_ERR(ip5xxx->regmap))
843		return PTR_ERR(ip5xxx->regmap);
844
845	fields = i2c_get_match_data(client) ?: &ip51xx_fields;
846	ip5xxx_setup_regs(dev, ip5xxx, fields);
847
848	psy_cfg.fwnode = dev_fwnode(dev);
849	psy_cfg.drv_data = ip5xxx;
850
851	psy = devm_power_supply_register(dev, &ip5xxx_battery_desc, &psy_cfg);
852	if (IS_ERR(psy))
853		return PTR_ERR(psy);
854
855	psy = devm_power_supply_register(dev, &ip5xxx_boost_desc, &psy_cfg);
856	if (IS_ERR(psy))
857		return PTR_ERR(psy);
858
859	return 0;
860}
861
862static const struct of_device_id ip5xxx_power_of_match[] = {
863	{ .compatible = "injoinic,ip5108", .data = &ip51xx_fields },
864	{ .compatible = "injoinic,ip5109", .data = &ip51xx_fields },
865	{ .compatible = "injoinic,ip5207", .data = &ip51xx_fields },
866	{ .compatible = "injoinic,ip5209", .data = &ip51xx_fields },
867	{ .compatible = "injoinic,ip5306", .data = &ip5306_fields },
868	{ }
869};
870MODULE_DEVICE_TABLE(of, ip5xxx_power_of_match);
871
872static struct i2c_driver ip5xxx_power_driver = {
873	.probe		= ip5xxx_power_probe,
874	.driver		= {
875		.name		= "ip5xxx-power",
876		.of_match_table	= ip5xxx_power_of_match,
877	}
878};
879module_i2c_driver(ip5xxx_power_driver);
880
881MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
882MODULE_DESCRIPTION("Injoinic IP5xxx power bank IC driver");
883MODULE_LICENSE("GPL");
~/ Read-only file preview.