1 // SPDX-License-Identifier: GPL-2.0+
3 * Power supply driver for the RICOH RN5T618 power management chip family
5 * Copyright (C) 2020 Andreas Kemnade
8 #include <linux/kernel.h>
9 #include <linux/device.h>
10 #include <linux/bitops.h>
11 #include <linux/errno.h>
12 #include <linux/init.h>
13 #include <linux/interrupt.h>
14 #include <linux/module.h>
15 #include <linux/mfd/rn5t618.h>
16 #include <linux/platform_device.h>
17 #include <linux/power_supply.h>
18 #include <linux/regmap.h>
19 #include <linux/slab.h>
21 #define CHG_STATE_ADP_INPUT 0x40
22 #define CHG_STATE_USB_INPUT 0x80
23 #define CHG_STATE_MASK 0x1f
24 #define CHG_STATE_CHG_OFF 0
25 #define CHG_STATE_CHG_READY_VADP 1
26 #define CHG_STATE_CHG_TRICKLE 2
27 #define CHG_STATE_CHG_RAPID 3
28 #define CHG_STATE_CHG_COMPLETE 4
29 #define CHG_STATE_SUSPEND 5
30 #define CHG_STATE_VCHG_OVER_VOL 6
31 #define CHG_STATE_BAT_ERROR 7
32 #define CHG_STATE_NO_BAT 8
33 #define CHG_STATE_BAT_OVER_VOL 9
34 #define CHG_STATE_BAT_TEMP_ERR 10
35 #define CHG_STATE_DIE_ERR 11
36 #define CHG_STATE_DIE_SHUTDOWN 12
37 #define CHG_STATE_NO_BAT2 13
38 #define CHG_STATE_CHG_READY_VUSB 14
42 struct rn5t618_power_info
{
43 struct rn5t618
*rn5t618
;
44 struct platform_device
*pdev
;
45 struct power_supply
*battery
;
46 struct power_supply
*usb
;
47 struct power_supply
*adp
;
51 static enum power_supply_property rn5t618_usb_props
[] = {
52 POWER_SUPPLY_PROP_STATUS
,
53 POWER_SUPPLY_PROP_ONLINE
,
56 static enum power_supply_property rn5t618_adp_props
[] = {
57 POWER_SUPPLY_PROP_STATUS
,
58 POWER_SUPPLY_PROP_ONLINE
,
62 static enum power_supply_property rn5t618_battery_props
[] = {
63 POWER_SUPPLY_PROP_STATUS
,
64 POWER_SUPPLY_PROP_PRESENT
,
65 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
66 POWER_SUPPLY_PROP_CURRENT_NOW
,
67 POWER_SUPPLY_PROP_CAPACITY
,
68 POWER_SUPPLY_PROP_TEMP
,
69 POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW
,
70 POWER_SUPPLY_PROP_TIME_TO_FULL_NOW
,
71 POWER_SUPPLY_PROP_TECHNOLOGY
,
72 POWER_SUPPLY_PROP_CHARGE_FULL
,
73 POWER_SUPPLY_PROP_CHARGE_NOW
,
76 static int rn5t618_battery_read_doublereg(struct rn5t618_power_info
*info
,
84 /* Prevent races when registers are changing. */
85 for (i
= 0; i
< 3; i
++) {
86 ret
= regmap_bulk_read(info
->rn5t618
->regmap
,
87 reg
, data
, sizeof(data
));
104 static int rn5t618_decode_status(unsigned int status
)
106 switch (status
& CHG_STATE_MASK
) {
107 case CHG_STATE_CHG_OFF
:
108 case CHG_STATE_SUSPEND
:
109 case CHG_STATE_VCHG_OVER_VOL
:
110 case CHG_STATE_DIE_SHUTDOWN
:
111 return POWER_SUPPLY_STATUS_DISCHARGING
;
113 case CHG_STATE_CHG_TRICKLE
:
114 case CHG_STATE_CHG_RAPID
:
115 return POWER_SUPPLY_STATUS_CHARGING
;
117 case CHG_STATE_CHG_COMPLETE
:
118 return POWER_SUPPLY_STATUS_FULL
;
121 return POWER_SUPPLY_STATUS_NOT_CHARGING
;
125 static int rn5t618_battery_status(struct rn5t618_power_info
*info
,
126 union power_supply_propval
*val
)
131 ret
= regmap_read(info
->rn5t618
->regmap
, RN5T618_CHGSTATE
, &v
);
135 val
->intval
= POWER_SUPPLY_STATUS_UNKNOWN
;
137 if (v
& 0xc0) { /* USB or ADP plugged */
138 val
->intval
= rn5t618_decode_status(v
);
140 val
->intval
= POWER_SUPPLY_STATUS_DISCHARGING
;
145 static int rn5t618_battery_present(struct rn5t618_power_info
*info
,
146 union power_supply_propval
*val
)
151 ret
= regmap_read(info
->rn5t618
->regmap
, RN5T618_CHGSTATE
, &v
);
156 if ((v
== CHG_STATE_NO_BAT
) || (v
== CHG_STATE_NO_BAT2
))
164 static int rn5t618_battery_voltage_now(struct rn5t618_power_info
*info
,
165 union power_supply_propval
*val
)
170 ret
= rn5t618_battery_read_doublereg(info
, RN5T618_VOLTAGE_1
, &res
);
174 val
->intval
= res
* 2 * 2500 / 4095 * 1000;
179 static int rn5t618_battery_current_now(struct rn5t618_power_info
*info
,
180 union power_supply_propval
*val
)
185 ret
= rn5t618_battery_read_doublereg(info
, RN5T618_CC_AVEREG1
, &res
);
189 /* current is negative when discharging */
190 val
->intval
= sign_extend32(res
, 13) * 1000;
195 static int rn5t618_battery_capacity(struct rn5t618_power_info
*info
,
196 union power_supply_propval
*val
)
201 ret
= regmap_read(info
->rn5t618
->regmap
, RN5T618_SOC
, &v
);
210 static int rn5t618_battery_temp(struct rn5t618_power_info
*info
,
211 union power_supply_propval
*val
)
216 ret
= rn5t618_battery_read_doublereg(info
, RN5T618_TEMP_1
, &res
);
220 val
->intval
= sign_extend32(res
, 11) * 10 / 16;
225 static int rn5t618_battery_tte(struct rn5t618_power_info
*info
,
226 union power_supply_propval
*val
)
231 ret
= rn5t618_battery_read_doublereg(info
, RN5T618_TT_EMPTY_H
, &res
);
238 val
->intval
= res
* 60;
243 static int rn5t618_battery_ttf(struct rn5t618_power_info
*info
,
244 union power_supply_propval
*val
)
249 ret
= rn5t618_battery_read_doublereg(info
, RN5T618_TT_FULL_H
, &res
);
256 val
->intval
= res
* 60;
261 static int rn5t618_battery_charge_full(struct rn5t618_power_info
*info
,
262 union power_supply_propval
*val
)
267 ret
= rn5t618_battery_read_doublereg(info
, RN5T618_FA_CAP_H
, &res
);
271 val
->intval
= res
* 1000;
276 static int rn5t618_battery_charge_now(struct rn5t618_power_info
*info
,
277 union power_supply_propval
*val
)
282 ret
= rn5t618_battery_read_doublereg(info
, RN5T618_RE_CAP_H
, &res
);
286 val
->intval
= res
* 1000;
291 static int rn5t618_battery_get_property(struct power_supply
*psy
,
292 enum power_supply_property psp
,
293 union power_supply_propval
*val
)
296 struct rn5t618_power_info
*info
= power_supply_get_drvdata(psy
);
299 case POWER_SUPPLY_PROP_STATUS
:
300 ret
= rn5t618_battery_status(info
, val
);
302 case POWER_SUPPLY_PROP_PRESENT
:
303 ret
= rn5t618_battery_present(info
, val
);
305 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
306 ret
= rn5t618_battery_voltage_now(info
, val
);
308 case POWER_SUPPLY_PROP_CURRENT_NOW
:
309 ret
= rn5t618_battery_current_now(info
, val
);
311 case POWER_SUPPLY_PROP_CAPACITY
:
312 ret
= rn5t618_battery_capacity(info
, val
);
314 case POWER_SUPPLY_PROP_TEMP
:
315 ret
= rn5t618_battery_temp(info
, val
);
317 case POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW
:
318 ret
= rn5t618_battery_tte(info
, val
);
320 case POWER_SUPPLY_PROP_TIME_TO_FULL_NOW
:
321 ret
= rn5t618_battery_ttf(info
, val
);
323 case POWER_SUPPLY_PROP_TECHNOLOGY
:
324 val
->intval
= POWER_SUPPLY_TECHNOLOGY_LION
;
326 case POWER_SUPPLY_PROP_CHARGE_FULL
:
327 ret
= rn5t618_battery_charge_full(info
, val
);
329 case POWER_SUPPLY_PROP_CHARGE_NOW
:
330 ret
= rn5t618_battery_charge_now(info
, val
);
339 static int rn5t618_adp_get_property(struct power_supply
*psy
,
340 enum power_supply_property psp
,
341 union power_supply_propval
*val
)
343 struct rn5t618_power_info
*info
= power_supply_get_drvdata(psy
);
344 unsigned int chgstate
;
348 ret
= regmap_read(info
->rn5t618
->regmap
, RN5T618_CHGSTATE
, &chgstate
);
352 online
= !!(chgstate
& CHG_STATE_ADP_INPUT
);
355 case POWER_SUPPLY_PROP_ONLINE
:
356 val
->intval
= online
;
358 case POWER_SUPPLY_PROP_STATUS
:
360 val
->intval
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
363 val
->intval
= rn5t618_decode_status(chgstate
);
364 if (val
->intval
!= POWER_SUPPLY_STATUS_CHARGING
)
365 val
->intval
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
375 static int rn5t618_usb_get_property(struct power_supply
*psy
,
376 enum power_supply_property psp
,
377 union power_supply_propval
*val
)
379 struct rn5t618_power_info
*info
= power_supply_get_drvdata(psy
);
380 unsigned int chgstate
;
384 ret
= regmap_read(info
->rn5t618
->regmap
, RN5T618_CHGSTATE
, &chgstate
);
388 online
= !!(chgstate
& CHG_STATE_USB_INPUT
);
391 case POWER_SUPPLY_PROP_ONLINE
:
392 val
->intval
= online
;
394 case POWER_SUPPLY_PROP_STATUS
:
396 val
->intval
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
399 val
->intval
= rn5t618_decode_status(chgstate
);
400 if (val
->intval
!= POWER_SUPPLY_STATUS_CHARGING
)
401 val
->intval
= POWER_SUPPLY_STATUS_NOT_CHARGING
;
411 static const struct power_supply_desc rn5t618_battery_desc
= {
412 .name
= "rn5t618-battery",
413 .type
= POWER_SUPPLY_TYPE_BATTERY
,
414 .properties
= rn5t618_battery_props
,
415 .num_properties
= ARRAY_SIZE(rn5t618_battery_props
),
416 .get_property
= rn5t618_battery_get_property
,
419 static const struct power_supply_desc rn5t618_adp_desc
= {
420 .name
= "rn5t618-adp",
421 .type
= POWER_SUPPLY_TYPE_MAINS
,
422 .properties
= rn5t618_adp_props
,
423 .num_properties
= ARRAY_SIZE(rn5t618_adp_props
),
424 .get_property
= rn5t618_adp_get_property
,
427 static const struct power_supply_desc rn5t618_usb_desc
= {
428 .name
= "rn5t618-usb",
429 .type
= POWER_SUPPLY_TYPE_USB
,
430 .properties
= rn5t618_usb_props
,
431 .num_properties
= ARRAY_SIZE(rn5t618_usb_props
),
432 .get_property
= rn5t618_usb_get_property
,
435 static irqreturn_t
rn5t618_charger_irq(int irq
, void *data
)
437 struct device
*dev
= data
;
438 struct rn5t618_power_info
*info
= dev_get_drvdata(dev
);
440 unsigned int ctrl
, stat1
, stat2
, err
;
442 regmap_read(info
->rn5t618
->regmap
, RN5T618_CHGERR_IRR
, &err
);
443 regmap_read(info
->rn5t618
->regmap
, RN5T618_CHGCTRL_IRR
, &ctrl
);
444 regmap_read(info
->rn5t618
->regmap
, RN5T618_CHGSTAT_IRR1
, &stat1
);
445 regmap_read(info
->rn5t618
->regmap
, RN5T618_CHGSTAT_IRR2
, &stat2
);
447 regmap_write(info
->rn5t618
->regmap
, RN5T618_CHGERR_IRR
, 0);
448 regmap_write(info
->rn5t618
->regmap
, RN5T618_CHGCTRL_IRR
, 0);
449 regmap_write(info
->rn5t618
->regmap
, RN5T618_CHGSTAT_IRR1
, 0);
450 regmap_write(info
->rn5t618
->regmap
, RN5T618_CHGSTAT_IRR2
, 0);
452 dev_dbg(dev
, "chgerr: %x chgctrl: %x chgstat: %x chgstat2: %x\n",
453 err
, ctrl
, stat1
, stat2
);
455 power_supply_changed(info
->usb
);
456 power_supply_changed(info
->adp
);
457 power_supply_changed(info
->battery
);
462 static int rn5t618_power_probe(struct platform_device
*pdev
)
466 struct power_supply_config psy_cfg
= {};
467 struct rn5t618_power_info
*info
;
469 info
= devm_kzalloc(&pdev
->dev
, sizeof(*info
), GFP_KERNEL
);
474 info
->rn5t618
= dev_get_drvdata(pdev
->dev
.parent
);
477 platform_set_drvdata(pdev
, info
);
479 ret
= regmap_read(info
->rn5t618
->regmap
, RN5T618_CONTROL
, &v
);
483 if (!(v
& FG_ENABLE
)) {
484 /* E.g. the vendor kernels of various Kobo and Tolino Ebook
485 * readers disable the fuel gauge on shutdown. If a kernel
486 * without fuel gauge support is booted after that, the fuel
487 * gauge will get decalibrated.
489 dev_info(&pdev
->dev
, "Fuel gauge not enabled, enabling now\n");
490 dev_info(&pdev
->dev
, "Expect imprecise results\n");
491 regmap_update_bits(info
->rn5t618
->regmap
, RN5T618_CONTROL
,
492 FG_ENABLE
, FG_ENABLE
);
495 psy_cfg
.drv_data
= info
;
496 info
->battery
= devm_power_supply_register(&pdev
->dev
,
497 &rn5t618_battery_desc
,
499 if (IS_ERR(info
->battery
)) {
500 ret
= PTR_ERR(info
->battery
);
501 dev_err(&pdev
->dev
, "failed to register battery: %d\n", ret
);
505 info
->adp
= devm_power_supply_register(&pdev
->dev
,
508 if (IS_ERR(info
->adp
)) {
509 ret
= PTR_ERR(info
->adp
);
510 dev_err(&pdev
->dev
, "failed to register adp: %d\n", ret
);
514 info
->usb
= devm_power_supply_register(&pdev
->dev
,
517 if (IS_ERR(info
->usb
)) {
518 ret
= PTR_ERR(info
->usb
);
519 dev_err(&pdev
->dev
, "failed to register usb: %d\n", ret
);
523 if (info
->rn5t618
->irq_data
)
524 info
->irq
= regmap_irq_get_virq(info
->rn5t618
->irq_data
,
530 ret
= devm_request_threaded_irq(&pdev
->dev
, info
->irq
, NULL
,
537 dev_err(&pdev
->dev
, "request IRQ:%d fail\n",
546 static struct platform_driver rn5t618_power_driver
= {
548 .name
= "rn5t618-power",
550 .probe
= rn5t618_power_probe
,
553 module_platform_driver(rn5t618_power_driver
);
554 MODULE_ALIAS("platform:rn5t618-power");
555 MODULE_DESCRIPTION("Power supply driver for RICOH RN5T618");
556 MODULE_LICENSE("GPL");