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_SYS_CTL0 0x01
11#define IP5XXX_SYS_CTL0_WLED_DET_EN BIT(4)
12#define IP5XXX_SYS_CTL0_WLED_EN BIT(3)
13#define IP5XXX_SYS_CTL0_BOOST_EN BIT(2)
14#define IP5XXX_SYS_CTL0_CHARGER_EN BIT(1)
15#define IP5XXX_SYS_CTL1 0x02
16#define IP5XXX_SYS_CTL1_LIGHT_SHDN_EN BIT(1)
17#define IP5XXX_SYS_CTL1_LOAD_PWRUP_EN BIT(0)
18#define IP5XXX_SYS_CTL2 0x0c
19#define IP5XXX_SYS_CTL2_LIGHT_SHDN_TH GENMASK(7, 3)
20#define IP5XXX_SYS_CTL3 0x03
21#define IP5XXX_SYS_CTL3_LONG_PRESS_TIME_SEL GENMASK(7, 6)
22#define IP5XXX_SYS_CTL3_BTN_SHDN_EN BIT(5)
23#define IP5XXX_SYS_CTL4 0x04
24#define IP5XXX_SYS_CTL4_SHDN_TIME_SEL GENMASK(7, 6)
25#define IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN BIT(5)
26#define IP5XXX_SYS_CTL5 0x07
27#define IP5XXX_SYS_CTL5_NTC_DIS BIT(6)
28#define IP5XXX_SYS_CTL5_WLED_MODE_SEL BIT(1)
29#define IP5XXX_SYS_CTL5_BTN_SHDN_SEL BIT(0)
30#define IP5XXX_CHG_CTL1 0x22
31#define IP5XXX_CHG_CTL1_BOOST_UVP_SEL GENMASK(3, 2)
32#define IP5XXX_CHG_CTL2 0x24
33#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL GENMASK(6, 5)
34#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V (0x0 << 5)
35#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V (0x1 << 5)
36#define IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V (0x2 << 5)
37#define IP5XXX_CHG_CTL2_CONST_VOLT_SEL GENMASK(2, 1)
38#define IP5XXX_CHG_CTL4 0x26
39#define IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN BIT(6)
40#define IP5XXX_CHG_CTL4A 0x25
41#define IP5XXX_CHG_CTL4A_CONST_CUR_SEL GENMASK(4, 0)
42#define IP5XXX_MFP_CTL0 0x51
43#define IP5XXX_MFP_CTL1 0x52
44#define IP5XXX_GPIO_CTL2 0x53
45#define IP5XXX_GPIO_CTL2A 0x54
46#define IP5XXX_GPIO_CTL3 0x55
47#define IP5XXX_READ0 0x71
48#define IP5XXX_READ0_CHG_STAT GENMASK(7, 5)
49#define IP5XXX_READ0_CHG_STAT_IDLE (0x0 << 5)
50#define IP5XXX_READ0_CHG_STAT_TRICKLE (0x1 << 5)
51#define IP5XXX_READ0_CHG_STAT_CONST_VOLT (0x2 << 5)
52#define IP5XXX_READ0_CHG_STAT_CONST_CUR (0x3 << 5)
53#define IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP (0x4 << 5)
54#define IP5XXX_READ0_CHG_STAT_FULL (0x5 << 5)
55#define IP5XXX_READ0_CHG_STAT_TIMEOUT (0x6 << 5)
56#define IP5XXX_READ0_CHG_OP BIT(4)
57#define IP5XXX_READ0_CHG_END BIT(3)
58#define IP5XXX_READ0_CONST_VOLT_TIMEOUT BIT(2)
59#define IP5XXX_READ0_CHG_TIMEOUT BIT(1)
60#define IP5XXX_READ0_TRICKLE_TIMEOUT BIT(0)
61#define IP5XXX_READ0_TIMEOUT GENMASK(2, 0)
62#define IP5XXX_READ1 0x72
63#define IP5XXX_READ1_WLED_PRESENT BIT(7)
64#define IP5XXX_READ1_LIGHT_LOAD BIT(6)
65#define IP5XXX_READ1_VIN_OVERVOLT BIT(5)
66#define IP5XXX_READ2 0x77
67#define IP5XXX_READ2_BTN_PRESS BIT(3)
68#define IP5XXX_READ2_BTN_LONG_PRESS BIT(1)
69#define IP5XXX_READ2_BTN_SHORT_PRESS BIT(0)
70#define IP5XXX_BATVADC_DAT0 0xa2
71#define IP5XXX_BATVADC_DAT1 0xa3
72#define IP5XXX_BATIADC_DAT0 0xa4
73#define IP5XXX_BATIADC_DAT1 0xa5
74#define IP5XXX_BATOCV_DAT0 0xa8
75#define IP5XXX_BATOCV_DAT1 0xa9
76
77struct ip5xxx {
78 struct regmap *regmap;
79 bool initialized;
80};
81
82/*
83 * The IP5xxx charger only responds on I2C when it is "awake". The charger is
84 * generally only awake when VIN is powered or when its boost converter is
85 * enabled. Going into shutdown resets all register values. To handle this:
86 * 1) When any bus error occurs, assume the charger has gone into shutdown.
87 * 2) Attempt the initialization sequence on each subsequent register access
88 * until it succeeds.
89 */
90static int ip5xxx_read(struct ip5xxx *ip5xxx, unsigned int reg,
91 unsigned int *val)
92{
93 int ret;
94
95 ret = regmap_read(map: ip5xxx->regmap, reg, val);
96 if (ret)
97 ip5xxx->initialized = false;
98
99 return ret;
100}
101
102static int ip5xxx_update_bits(struct ip5xxx *ip5xxx, unsigned int reg,
103 unsigned int mask, unsigned int val)
104{
105 int ret;
106
107 ret = regmap_update_bits(map: ip5xxx->regmap, reg, mask, val);
108 if (ret)
109 ip5xxx->initialized = false;
110
111 return ret;
112}
113
114static int ip5xxx_initialize(struct power_supply *psy)
115{
116 struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
117 int ret;
118
119 if (ip5xxx->initialized)
120 return 0;
121
122 /*
123 * Disable shutdown under light load.
124 * Enable power on when under load.
125 */
126 ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL1,
127 IP5XXX_SYS_CTL1_LIGHT_SHDN_EN |
128 IP5XXX_SYS_CTL1_LOAD_PWRUP_EN,
129 IP5XXX_SYS_CTL1_LOAD_PWRUP_EN);
130 if (ret)
131 return ret;
132
133 /*
134 * Enable shutdown after a long button press (as configured below).
135 */
136 ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL3,
137 IP5XXX_SYS_CTL3_BTN_SHDN_EN,
138 IP5XXX_SYS_CTL3_BTN_SHDN_EN);
139 if (ret)
140 return ret;
141
142 /*
143 * Power on automatically when VIN is removed.
144 */
145 ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL4,
146 IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN,
147 IP5XXX_SYS_CTL4_VIN_PULLOUT_BOOST_EN);
148 if (ret)
149 return ret;
150
151 /*
152 * Enable the NTC.
153 * Configure the button for two presses => LED, long press => shutdown.
154 */
155 ret = ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL5,
156 IP5XXX_SYS_CTL5_NTC_DIS |
157 IP5XXX_SYS_CTL5_WLED_MODE_SEL |
158 IP5XXX_SYS_CTL5_BTN_SHDN_SEL,
159 IP5XXX_SYS_CTL5_WLED_MODE_SEL |
160 IP5XXX_SYS_CTL5_BTN_SHDN_SEL);
161 if (ret)
162 return ret;
163
164 ip5xxx->initialized = true;
165 dev_dbg(psy->dev.parent, "Initialized after power on\n");
166
167 return 0;
168}
169
170static const enum power_supply_property ip5xxx_battery_properties[] = {
171 POWER_SUPPLY_PROP_STATUS,
172 POWER_SUPPLY_PROP_CHARGE_TYPE,
173 POWER_SUPPLY_PROP_HEALTH,
174 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN,
175 POWER_SUPPLY_PROP_VOLTAGE_NOW,
176 POWER_SUPPLY_PROP_VOLTAGE_OCV,
177 POWER_SUPPLY_PROP_CURRENT_NOW,
178 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT,
179 POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX,
180 POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE,
181 POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX,
182};
183
184static int ip5xxx_battery_get_status(struct ip5xxx *ip5xxx, int *val)
185{
186 unsigned int rval;
187 int ret;
188
189 ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, val: &rval);
190 if (ret)
191 return ret;
192
193 switch (rval & IP5XXX_READ0_CHG_STAT) {
194 case IP5XXX_READ0_CHG_STAT_IDLE:
195 *val = POWER_SUPPLY_STATUS_DISCHARGING;
196 break;
197 case IP5XXX_READ0_CHG_STAT_TRICKLE:
198 case IP5XXX_READ0_CHG_STAT_CONST_CUR:
199 case IP5XXX_READ0_CHG_STAT_CONST_VOLT:
200 *val = POWER_SUPPLY_STATUS_CHARGING;
201 break;
202 case IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP:
203 case IP5XXX_READ0_CHG_STAT_FULL:
204 *val = POWER_SUPPLY_STATUS_FULL;
205 break;
206 case IP5XXX_READ0_CHG_STAT_TIMEOUT:
207 *val = POWER_SUPPLY_STATUS_NOT_CHARGING;
208 break;
209 default:
210 return -EINVAL;
211 }
212
213 return 0;
214}
215
216static int ip5xxx_battery_get_charge_type(struct ip5xxx *ip5xxx, int *val)
217{
218 unsigned int rval;
219 int ret;
220
221 ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, val: &rval);
222 if (ret)
223 return ret;
224
225 switch (rval & IP5XXX_READ0_CHG_STAT) {
226 case IP5XXX_READ0_CHG_STAT_IDLE:
227 case IP5XXX_READ0_CHG_STAT_CONST_VOLT_STOP:
228 case IP5XXX_READ0_CHG_STAT_FULL:
229 case IP5XXX_READ0_CHG_STAT_TIMEOUT:
230 *val = POWER_SUPPLY_CHARGE_TYPE_NONE;
231 break;
232 case IP5XXX_READ0_CHG_STAT_TRICKLE:
233 *val = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
234 break;
235 case IP5XXX_READ0_CHG_STAT_CONST_CUR:
236 case IP5XXX_READ0_CHG_STAT_CONST_VOLT:
237 *val = POWER_SUPPLY_CHARGE_TYPE_STANDARD;
238 break;
239 default:
240 return -EINVAL;
241 }
242
243 return 0;
244}
245
246static int ip5xxx_battery_get_health(struct ip5xxx *ip5xxx, int *val)
247{
248 unsigned int rval;
249 int ret;
250
251 ret = ip5xxx_read(ip5xxx, IP5XXX_READ0, val: &rval);
252 if (ret)
253 return ret;
254
255 if (rval & IP5XXX_READ0_TIMEOUT)
256 *val = POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE;
257 else
258 *val = POWER_SUPPLY_HEALTH_GOOD;
259
260 return 0;
261}
262
263static int ip5xxx_battery_get_voltage_max(struct ip5xxx *ip5xxx, int *val)
264{
265 unsigned int rval;
266 int ret;
267
268 ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL2, val: &rval);
269 if (ret)
270 return ret;
271
272 /*
273 * It is not clear what this will return if
274 * IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN is not set...
275 */
276 switch (rval & IP5XXX_CHG_CTL2_BAT_TYPE_SEL) {
277 case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V:
278 *val = 4200000;
279 break;
280 case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V:
281 *val = 4300000;
282 break;
283 case IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V:
284 *val = 4350000;
285 break;
286 default:
287 return -EINVAL;
288 }
289
290 return 0;
291}
292
293static int ip5xxx_battery_read_adc(struct ip5xxx *ip5xxx,
294 u8 lo_reg, u8 hi_reg, int *val)
295{
296 unsigned int hi, lo;
297 int ret;
298
299 ret = ip5xxx_read(ip5xxx, reg: lo_reg, val: &lo);
300 if (ret)
301 return ret;
302
303 ret = ip5xxx_read(ip5xxx, reg: hi_reg, val: &hi);
304 if (ret)
305 return ret;
306
307 *val = sign_extend32(value: hi << 8 | lo, index: 13);
308
309 return 0;
310}
311
312static int ip5xxx_battery_get_property(struct power_supply *psy,
313 enum power_supply_property psp,
314 union power_supply_propval *val)
315{
316 struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
317 int raw, ret, vmax;
318 unsigned int rval;
319
320 ret = ip5xxx_initialize(psy);
321 if (ret)
322 return ret;
323
324 switch (psp) {
325 case POWER_SUPPLY_PROP_STATUS:
326 return ip5xxx_battery_get_status(ip5xxx, val: &val->intval);
327
328 case POWER_SUPPLY_PROP_CHARGE_TYPE:
329 return ip5xxx_battery_get_charge_type(ip5xxx, val: &val->intval);
330
331 case POWER_SUPPLY_PROP_HEALTH:
332 return ip5xxx_battery_get_health(ip5xxx, val: &val->intval);
333
334 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
335 return ip5xxx_battery_get_voltage_max(ip5xxx, val: &val->intval);
336
337 case POWER_SUPPLY_PROP_VOLTAGE_NOW:
338 ret = ip5xxx_battery_read_adc(ip5xxx, IP5XXX_BATVADC_DAT0,
339 IP5XXX_BATVADC_DAT1, val: &raw);
340
341 val->intval = 2600000 + DIV_ROUND_CLOSEST(raw * 26855, 100);
342 return 0;
343
344 case POWER_SUPPLY_PROP_VOLTAGE_OCV:
345 ret = ip5xxx_battery_read_adc(ip5xxx, IP5XXX_BATOCV_DAT0,
346 IP5XXX_BATOCV_DAT1, val: &raw);
347
348 val->intval = 2600000 + DIV_ROUND_CLOSEST(raw * 26855, 100);
349 return 0;
350
351 case POWER_SUPPLY_PROP_CURRENT_NOW:
352 ret = ip5xxx_battery_read_adc(ip5xxx, IP5XXX_BATIADC_DAT0,
353 IP5XXX_BATIADC_DAT1, val: &raw);
354
355 val->intval = DIV_ROUND_CLOSEST(raw * 149197, 200);
356 return 0;
357
358 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
359 ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL4A, val: &rval);
360 if (ret)
361 return ret;
362
363 rval &= IP5XXX_CHG_CTL4A_CONST_CUR_SEL;
364 val->intval = 100000 * rval;
365 return 0;
366
367 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX:
368 val->intval = 100000 * 0x1f;
369 return 0;
370
371 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
372 ret = ip5xxx_battery_get_voltage_max(ip5xxx, val: &vmax);
373 if (ret)
374 return ret;
375
376 ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL2, val: &rval);
377 if (ret)
378 return ret;
379
380 rval &= IP5XXX_CHG_CTL2_CONST_VOLT_SEL;
381 val->intval = vmax + 14000 * (rval >> 1);
382 return 0;
383
384 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX:
385 ret = ip5xxx_battery_get_voltage_max(ip5xxx, val: &vmax);
386 if (ret)
387 return ret;
388
389 val->intval = vmax + 14000 * 3;
390 return 0;
391
392 default:
393 return -EINVAL;
394 }
395}
396
397static int ip5xxx_battery_set_voltage_max(struct ip5xxx *ip5xxx, int val)
398{
399 unsigned int rval;
400 int ret;
401
402 switch (val) {
403 case 4200000:
404 rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_2V;
405 break;
406 case 4300000:
407 rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_3V;
408 break;
409 case 4350000:
410 rval = IP5XXX_CHG_CTL2_BAT_TYPE_SEL_4_35V;
411 break;
412 default:
413 return -EINVAL;
414 }
415
416 ret = ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL2,
417 IP5XXX_CHG_CTL2_BAT_TYPE_SEL, val: rval);
418 if (ret)
419 return ret;
420
421 ret = ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL4,
422 IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN,
423 IP5XXX_CHG_CTL4_BAT_TYPE_SEL_EN);
424 if (ret)
425 return ret;
426
427 return 0;
428}
429
430static int ip5xxx_battery_set_property(struct power_supply *psy,
431 enum power_supply_property psp,
432 const union power_supply_propval *val)
433{
434 struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
435 unsigned int rval;
436 int ret, vmax;
437
438 ret = ip5xxx_initialize(psy);
439 if (ret)
440 return ret;
441
442 switch (psp) {
443 case POWER_SUPPLY_PROP_STATUS:
444 switch (val->intval) {
445 case POWER_SUPPLY_STATUS_CHARGING:
446 rval = IP5XXX_SYS_CTL0_CHARGER_EN;
447 break;
448 case POWER_SUPPLY_STATUS_DISCHARGING:
449 case POWER_SUPPLY_STATUS_NOT_CHARGING:
450 rval = 0;
451 break;
452 default:
453 return -EINVAL;
454 }
455 return ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL0,
456 IP5XXX_SYS_CTL0_CHARGER_EN, val: rval);
457
458 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
459 return ip5xxx_battery_set_voltage_max(ip5xxx, val: val->intval);
460
461 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT:
462 rval = val->intval / 100000;
463 return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL4A,
464 IP5XXX_CHG_CTL4A_CONST_CUR_SEL, val: rval);
465
466 case POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE:
467 ret = ip5xxx_battery_get_voltage_max(ip5xxx, val: &vmax);
468 if (ret)
469 return ret;
470
471 rval = ((val->intval - vmax) / 14000) << 1;
472 return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL2,
473 IP5XXX_CHG_CTL2_CONST_VOLT_SEL, val: rval);
474
475 default:
476 return -EINVAL;
477 }
478}
479
480static int ip5xxx_battery_property_is_writeable(struct power_supply *psy,
481 enum power_supply_property psp)
482{
483 return psp == POWER_SUPPLY_PROP_STATUS ||
484 psp == POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN ||
485 psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT ||
486 psp == POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE;
487}
488
489static const struct power_supply_desc ip5xxx_battery_desc = {
490 .name = "ip5xxx-battery",
491 .type = POWER_SUPPLY_TYPE_BATTERY,
492 .properties = ip5xxx_battery_properties,
493 .num_properties = ARRAY_SIZE(ip5xxx_battery_properties),
494 .get_property = ip5xxx_battery_get_property,
495 .set_property = ip5xxx_battery_set_property,
496 .property_is_writeable = ip5xxx_battery_property_is_writeable,
497};
498
499static const enum power_supply_property ip5xxx_boost_properties[] = {
500 POWER_SUPPLY_PROP_ONLINE,
501 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN,
502};
503
504static int ip5xxx_boost_get_property(struct power_supply *psy,
505 enum power_supply_property psp,
506 union power_supply_propval *val)
507{
508 struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
509 unsigned int rval;
510 int ret;
511
512 ret = ip5xxx_initialize(psy);
513 if (ret)
514 return ret;
515
516 switch (psp) {
517 case POWER_SUPPLY_PROP_ONLINE:
518 ret = ip5xxx_read(ip5xxx, IP5XXX_SYS_CTL0, val: &rval);
519 if (ret)
520 return ret;
521
522 val->intval = !!(rval & IP5XXX_SYS_CTL0_BOOST_EN);
523 return 0;
524
525 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
526 ret = ip5xxx_read(ip5xxx, IP5XXX_CHG_CTL1, val: &rval);
527 if (ret)
528 return ret;
529
530 rval &= IP5XXX_CHG_CTL1_BOOST_UVP_SEL;
531 val->intval = 4530000 + 100000 * (rval >> 2);
532 return 0;
533
534 default:
535 return -EINVAL;
536 }
537}
538
539static int ip5xxx_boost_set_property(struct power_supply *psy,
540 enum power_supply_property psp,
541 const union power_supply_propval *val)
542{
543 struct ip5xxx *ip5xxx = power_supply_get_drvdata(psy);
544 unsigned int rval;
545 int ret;
546
547 ret = ip5xxx_initialize(psy);
548 if (ret)
549 return ret;
550
551 switch (psp) {
552 case POWER_SUPPLY_PROP_ONLINE:
553 rval = val->intval ? IP5XXX_SYS_CTL0_BOOST_EN : 0;
554 return ip5xxx_update_bits(ip5xxx, IP5XXX_SYS_CTL0,
555 IP5XXX_SYS_CTL0_BOOST_EN, val: rval);
556
557 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
558 rval = ((val->intval - 4530000) / 100000) << 2;
559 return ip5xxx_update_bits(ip5xxx, IP5XXX_CHG_CTL1,
560 IP5XXX_CHG_CTL1_BOOST_UVP_SEL, val: rval);
561
562 default:
563 return -EINVAL;
564 }
565}
566
567static int ip5xxx_boost_property_is_writeable(struct power_supply *psy,
568 enum power_supply_property psp)
569{
570 return true;
571}
572
573static const struct power_supply_desc ip5xxx_boost_desc = {
574 .name = "ip5xxx-boost",
575 .type = POWER_SUPPLY_TYPE_USB,
576 .properties = ip5xxx_boost_properties,
577 .num_properties = ARRAY_SIZE(ip5xxx_boost_properties),
578 .get_property = ip5xxx_boost_get_property,
579 .set_property = ip5xxx_boost_set_property,
580 .property_is_writeable = ip5xxx_boost_property_is_writeable,
581};
582
583static const struct regmap_config ip5xxx_regmap_config = {
584 .reg_bits = 8,
585 .val_bits = 8,
586 .max_register = IP5XXX_BATOCV_DAT1,
587};
588
589static int ip5xxx_power_probe(struct i2c_client *client)
590{
591 struct power_supply_config psy_cfg = {};
592 struct device *dev = &client->dev;
593 struct power_supply *psy;
594 struct ip5xxx *ip5xxx;
595
596 ip5xxx = devm_kzalloc(dev, size: sizeof(*ip5xxx), GFP_KERNEL);
597 if (!ip5xxx)
598 return -ENOMEM;
599
600 ip5xxx->regmap = devm_regmap_init_i2c(client, &ip5xxx_regmap_config);
601 if (IS_ERR(ptr: ip5xxx->regmap))
602 return PTR_ERR(ptr: ip5xxx->regmap);
603
604 psy_cfg.of_node = dev->of_node;
605 psy_cfg.drv_data = ip5xxx;
606
607 psy = devm_power_supply_register(parent: dev, desc: &ip5xxx_battery_desc, cfg: &psy_cfg);
608 if (IS_ERR(ptr: psy))
609 return PTR_ERR(ptr: psy);
610
611 psy = devm_power_supply_register(parent: dev, desc: &ip5xxx_boost_desc, cfg: &psy_cfg);
612 if (IS_ERR(ptr: psy))
613 return PTR_ERR(ptr: psy);
614
615 return 0;
616}
617
618static const struct of_device_id ip5xxx_power_of_match[] = {
619 { .compatible = "injoinic,ip5108" },
620 { .compatible = "injoinic,ip5109" },
621 { .compatible = "injoinic,ip5207" },
622 { .compatible = "injoinic,ip5209" },
623 { }
624};
625MODULE_DEVICE_TABLE(of, ip5xxx_power_of_match);
626
627static struct i2c_driver ip5xxx_power_driver = {
628 .probe = ip5xxx_power_probe,
629 .driver = {
630 .name = "ip5xxx-power",
631 .of_match_table = ip5xxx_power_of_match,
632 }
633};
634module_i2c_driver(ip5xxx_power_driver);
635
636MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>");
637MODULE_DESCRIPTION("Injoinic IP5xxx power bank IC driver");
638MODULE_LICENSE("GPL");
639

source code of linux/drivers/power/supply/ip5xxx_power.c