1 // SPDX-License-Identifier: GPL-2.0
3 * Battery driver for the Ingenic JZ47xx SoCs
4 * Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu>
6 * based on drivers/power/supply/jz4740-battery.c
9 #include <linux/iio/consumer.h>
10 #include <linux/module.h>
12 #include <linux/platform_device.h>
13 #include <linux/power_supply.h>
14 #include <linux/property.h>
16 struct ingenic_battery
{
18 struct iio_channel
*channel
;
19 struct power_supply_desc desc
;
20 struct power_supply
*battery
;
21 struct power_supply_battery_info info
;
24 static int ingenic_battery_get_property(struct power_supply
*psy
,
25 enum power_supply_property psp
,
26 union power_supply_propval
*val
)
28 struct ingenic_battery
*bat
= power_supply_get_drvdata(psy
);
29 struct power_supply_battery_info
*info
= &bat
->info
;
33 case POWER_SUPPLY_PROP_HEALTH
:
34 ret
= iio_read_channel_processed(bat
->channel
, &val
->intval
);
36 if (val
->intval
< info
->voltage_min_design_uv
)
37 val
->intval
= POWER_SUPPLY_HEALTH_DEAD
;
38 else if (val
->intval
> info
->voltage_max_design_uv
)
39 val
->intval
= POWER_SUPPLY_HEALTH_OVERVOLTAGE
;
41 val
->intval
= POWER_SUPPLY_HEALTH_GOOD
;
43 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
44 ret
= iio_read_channel_processed(bat
->channel
, &val
->intval
);
47 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
:
48 val
->intval
= info
->voltage_min_design_uv
;
50 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
:
51 val
->intval
= info
->voltage_max_design_uv
;
58 /* Set the most appropriate IIO channel voltage reference scale
59 * based on the battery's max voltage.
61 static int ingenic_battery_set_scale(struct ingenic_battery
*bat
)
64 int scale_len
, scale_type
, best_idx
= -1, best_mV
, max_raw
, i
, ret
;
67 ret
= iio_read_max_channel_raw(bat
->channel
, &max_raw
);
69 dev_err(bat
->dev
, "Unable to read max raw channel value\n");
73 ret
= iio_read_avail_channel_attribute(bat
->channel
, &scale_raw
,
74 &scale_type
, &scale_len
,
77 dev_err(bat
->dev
, "Unable to read channel avail scale\n");
80 if (ret
!= IIO_AVAIL_LIST
|| scale_type
!= IIO_VAL_FRACTIONAL_LOG2
)
83 max_mV
= bat
->info
.voltage_max_design_uv
/ 1000;
85 for (i
= 0; i
< scale_len
; i
+= 2) {
86 u64 scale_mV
= (max_raw
* scale_raw
[i
]) >> scale_raw
[i
+ 1];
88 if (scale_mV
< max_mV
)
91 if (best_idx
>= 0 && scale_mV
> best_mV
)
99 dev_err(bat
->dev
, "Unable to find matching voltage scale\n");
103 /* Only set scale if there is more than one (fractional) entry */
105 ret
= iio_write_channel_attribute(bat
->channel
,
107 scale_raw
[best_idx
+ 1],
108 IIO_CHAN_INFO_SCALE
);
116 static enum power_supply_property ingenic_battery_properties
[] = {
117 POWER_SUPPLY_PROP_HEALTH
,
118 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
119 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
,
120 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
,
123 static int ingenic_battery_probe(struct platform_device
*pdev
)
125 struct device
*dev
= &pdev
->dev
;
126 struct ingenic_battery
*bat
;
127 struct power_supply_config psy_cfg
= {};
128 struct power_supply_desc
*desc
;
131 bat
= devm_kzalloc(dev
, sizeof(*bat
), GFP_KERNEL
);
136 bat
->channel
= devm_iio_channel_get(dev
, "battery");
137 if (IS_ERR(bat
->channel
))
138 return PTR_ERR(bat
->channel
);
141 desc
->name
= "jz-battery";
142 desc
->type
= POWER_SUPPLY_TYPE_BATTERY
;
143 desc
->properties
= ingenic_battery_properties
;
144 desc
->num_properties
= ARRAY_SIZE(ingenic_battery_properties
);
145 desc
->get_property
= ingenic_battery_get_property
;
146 psy_cfg
.drv_data
= bat
;
147 psy_cfg
.of_node
= dev
->of_node
;
149 bat
->battery
= devm_power_supply_register(dev
, desc
, &psy_cfg
);
150 if (IS_ERR(bat
->battery
)) {
151 dev_err(dev
, "Unable to register battery\n");
152 return PTR_ERR(bat
->battery
);
155 ret
= power_supply_get_battery_info(bat
->battery
, &bat
->info
);
157 dev_err(dev
, "Unable to get battery info: %d\n", ret
);
160 if (bat
->info
.voltage_min_design_uv
< 0) {
161 dev_err(dev
, "Unable to get voltage min design\n");
162 return bat
->info
.voltage_min_design_uv
;
164 if (bat
->info
.voltage_max_design_uv
< 0) {
165 dev_err(dev
, "Unable to get voltage max design\n");
166 return bat
->info
.voltage_max_design_uv
;
169 return ingenic_battery_set_scale(bat
);
173 static const struct of_device_id ingenic_battery_of_match
[] = {
174 { .compatible
= "ingenic,jz4740-battery", },
177 MODULE_DEVICE_TABLE(of
, ingenic_battery_of_match
);
180 static struct platform_driver ingenic_battery_driver
= {
182 .name
= "ingenic-battery",
183 .of_match_table
= of_match_ptr(ingenic_battery_of_match
),
185 .probe
= ingenic_battery_probe
,
187 module_platform_driver(ingenic_battery_driver
);
189 MODULE_DESCRIPTION("Battery driver for Ingenic JZ47xx SoCs");
190 MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>");
191 MODULE_LICENSE("GPL");