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_scale(bat
->channel
,
37 if (val
->intval
< info
->voltage_min_design_uv
)
38 val
->intval
= POWER_SUPPLY_HEALTH_DEAD
;
39 else if (val
->intval
> info
->voltage_max_design_uv
)
40 val
->intval
= POWER_SUPPLY_HEALTH_OVERVOLTAGE
;
42 val
->intval
= POWER_SUPPLY_HEALTH_GOOD
;
44 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
45 ret
= iio_read_channel_processed_scale(bat
->channel
,
49 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
:
50 val
->intval
= info
->voltage_min_design_uv
;
52 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
:
53 val
->intval
= info
->voltage_max_design_uv
;
60 /* Set the most appropriate IIO channel voltage reference scale
61 * based on the battery's max voltage.
63 static int ingenic_battery_set_scale(struct ingenic_battery
*bat
)
66 int scale_len
, scale_type
, best_idx
= -1, best_mV
, max_raw
, i
, ret
;
69 ret
= iio_read_max_channel_raw(bat
->channel
, &max_raw
);
71 dev_err(bat
->dev
, "Unable to read max raw channel value\n");
75 ret
= iio_read_avail_channel_attribute(bat
->channel
, &scale_raw
,
76 &scale_type
, &scale_len
,
79 dev_err(bat
->dev
, "Unable to read channel avail scale\n");
82 if (ret
!= IIO_AVAIL_LIST
|| scale_type
!= IIO_VAL_FRACTIONAL_LOG2
)
85 max_mV
= bat
->info
->voltage_max_design_uv
/ 1000;
87 for (i
= 0; i
< scale_len
; i
+= 2) {
88 u64 scale_mV
= (max_raw
* scale_raw
[i
]) >> scale_raw
[i
+ 1];
90 if (scale_mV
< max_mV
)
93 if (best_idx
>= 0 && scale_mV
> best_mV
)
101 dev_err(bat
->dev
, "Unable to find matching voltage scale\n");
105 /* Only set scale if there is more than one (fractional) entry */
107 ret
= iio_write_channel_attribute(bat
->channel
,
109 scale_raw
[best_idx
+ 1],
110 IIO_CHAN_INFO_SCALE
);
118 static enum power_supply_property ingenic_battery_properties
[] = {
119 POWER_SUPPLY_PROP_HEALTH
,
120 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
121 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
,
122 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
,
125 static int ingenic_battery_probe(struct platform_device
*pdev
)
127 struct device
*dev
= &pdev
->dev
;
128 struct ingenic_battery
*bat
;
129 struct power_supply_config psy_cfg
= {};
130 struct power_supply_desc
*desc
;
133 bat
= devm_kzalloc(dev
, sizeof(*bat
), GFP_KERNEL
);
138 bat
->channel
= devm_iio_channel_get(dev
, "battery");
139 if (IS_ERR(bat
->channel
))
140 return PTR_ERR(bat
->channel
);
143 desc
->name
= "jz-battery";
144 desc
->type
= POWER_SUPPLY_TYPE_BATTERY
;
145 desc
->properties
= ingenic_battery_properties
;
146 desc
->num_properties
= ARRAY_SIZE(ingenic_battery_properties
);
147 desc
->get_property
= ingenic_battery_get_property
;
148 psy_cfg
.drv_data
= bat
;
149 psy_cfg
.of_node
= dev
->of_node
;
151 bat
->battery
= devm_power_supply_register(dev
, desc
, &psy_cfg
);
152 if (IS_ERR(bat
->battery
))
153 return dev_err_probe(dev
, PTR_ERR(bat
->battery
),
154 "Unable to register battery\n");
156 ret
= power_supply_get_battery_info(bat
->battery
, &bat
->info
);
158 dev_err(dev
, "Unable to get battery info: %d\n", ret
);
161 if (bat
->info
->voltage_min_design_uv
< 0) {
162 dev_err(dev
, "Unable to get voltage min design\n");
163 return bat
->info
->voltage_min_design_uv
;
165 if (bat
->info
->voltage_max_design_uv
< 0) {
166 dev_err(dev
, "Unable to get voltage max design\n");
167 return bat
->info
->voltage_max_design_uv
;
170 return ingenic_battery_set_scale(bat
);
174 static const struct of_device_id ingenic_battery_of_match
[] = {
175 { .compatible
= "ingenic,jz4740-battery", },
178 MODULE_DEVICE_TABLE(of
, ingenic_battery_of_match
);
181 static struct platform_driver ingenic_battery_driver
= {
183 .name
= "ingenic-battery",
184 .of_match_table
= of_match_ptr(ingenic_battery_of_match
),
186 .probe
= ingenic_battery_probe
,
188 module_platform_driver(ingenic_battery_driver
);
190 MODULE_DESCRIPTION("Battery driver for Ingenic JZ47xx SoCs");
191 MODULE_AUTHOR("Artur Rojek <contact@artur-rojek.eu>");
192 MODULE_LICENSE("GPL");