2 * Battery driver for LEGO MINDSTORMS EV3
4 * Copyright (C) 2017 David Lechner <david@lechnology.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
10 * This program is distributed "as is" WITHOUT ANY WARRANTY of any
11 * kind, whether express or implied; without even the implied warranty
12 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
16 #include <linux/delay.h>
17 #include <linux/err.h>
18 #include <linux/gpio/consumer.h>
19 #include <linux/iio/consumer.h>
20 #include <linux/iio/types.h>
21 #include <linux/kernel.h>
22 #include <linux/module.h>
23 #include <linux/of_device.h>
24 #include <linux/platform_device.h>
25 #include <linux/power_supply.h>
27 struct lego_ev3_battery
{
28 struct iio_channel
*iio_v
;
29 struct iio_channel
*iio_i
;
30 struct gpio_desc
*rechargeable_gpio
;
31 struct power_supply
*psy
;
37 static int lego_ev3_battery_get_property(struct power_supply
*psy
,
38 enum power_supply_property psp
,
39 union power_supply_propval
*val
)
41 struct lego_ev3_battery
*batt
= power_supply_get_drvdata(psy
);
45 case POWER_SUPPLY_PROP_TECHNOLOGY
:
46 val
->intval
= batt
->technology
;
48 case POWER_SUPPLY_PROP_VOLTAGE_NOW
:
49 /* battery voltage is iio channel * 2 + Vce of transistor */
50 ret
= iio_read_channel_processed(batt
->iio_v
, &val
->intval
);
57 /* plus adjust for shunt resistor drop */
58 ret
= iio_read_channel_processed(batt
->iio_i
, &val2
);
66 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
:
67 val
->intval
= batt
->v_max
;
69 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
:
70 val
->intval
= batt
->v_min
;
72 case POWER_SUPPLY_PROP_CURRENT_NOW
:
73 /* battery current is iio channel / 15 / 0.05 ohms */
74 ret
= iio_read_channel_processed(batt
->iio_i
, &val
->intval
);
81 case POWER_SUPPLY_PROP_SCOPE
:
82 val
->intval
= POWER_SUPPLY_SCOPE_SYSTEM
;
91 static int lego_ev3_battery_set_property(struct power_supply
*psy
,
92 enum power_supply_property psp
,
93 const union power_supply_propval
*val
)
95 struct lego_ev3_battery
*batt
= power_supply_get_drvdata(psy
);
98 case POWER_SUPPLY_PROP_TECHNOLOGY
:
100 * Only allow changing technology from Unknown to NiMH. Li-ion
101 * batteries are automatically detected and should not be
102 * overridden. Rechargeable AA batteries, on the other hand,
103 * cannot be automatically detected, and so must be manually
104 * specified. This should only be set once during system init,
105 * so there is no mechanism to go back to Unknown.
107 if (batt
->technology
!= POWER_SUPPLY_TECHNOLOGY_UNKNOWN
)
109 switch (val
->intval
) {
110 case POWER_SUPPLY_TECHNOLOGY_NiMH
:
111 batt
->technology
= POWER_SUPPLY_TECHNOLOGY_NiMH
;
112 batt
->v_max
= 7800000;
113 batt
->v_min
= 5400000;
126 static int lego_ev3_battery_property_is_writeable(struct power_supply
*psy
,
127 enum power_supply_property psp
)
129 struct lego_ev3_battery
*batt
= power_supply_get_drvdata(psy
);
131 return psp
== POWER_SUPPLY_PROP_TECHNOLOGY
&&
132 batt
->technology
== POWER_SUPPLY_TECHNOLOGY_UNKNOWN
;
135 static enum power_supply_property lego_ev3_battery_props
[] = {
136 POWER_SUPPLY_PROP_TECHNOLOGY
,
137 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
138 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
,
139 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
,
140 POWER_SUPPLY_PROP_CURRENT_NOW
,
141 POWER_SUPPLY_PROP_SCOPE
,
144 static const struct power_supply_desc lego_ev3_battery_desc
= {
145 .name
= "lego-ev3-battery",
146 .type
= POWER_SUPPLY_TYPE_BATTERY
,
147 .properties
= lego_ev3_battery_props
,
148 .num_properties
= ARRAY_SIZE(lego_ev3_battery_props
),
149 .get_property
= lego_ev3_battery_get_property
,
150 .set_property
= lego_ev3_battery_set_property
,
151 .property_is_writeable
= lego_ev3_battery_property_is_writeable
,
154 static int lego_ev3_battery_probe(struct platform_device
*pdev
)
156 struct device
*dev
= &pdev
->dev
;
157 struct lego_ev3_battery
*batt
;
158 struct power_supply_config psy_cfg
= {};
161 batt
= devm_kzalloc(dev
, sizeof(*batt
), GFP_KERNEL
);
165 platform_set_drvdata(pdev
, batt
);
167 batt
->iio_v
= devm_iio_channel_get(dev
, "voltage");
168 err
= PTR_ERR_OR_ZERO(batt
->iio_v
);
170 return dev_err_probe(dev
, err
,
171 "Failed to get voltage iio channel\n");
173 batt
->iio_i
= devm_iio_channel_get(dev
, "current");
174 err
= PTR_ERR_OR_ZERO(batt
->iio_i
);
176 return dev_err_probe(dev
, err
,
177 "Failed to get current iio channel\n");
179 batt
->rechargeable_gpio
= devm_gpiod_get(dev
, "rechargeable", GPIOD_IN
);
180 err
= PTR_ERR_OR_ZERO(batt
->rechargeable_gpio
);
182 return dev_err_probe(dev
, err
,
183 "Failed to get rechargeable gpio\n");
186 * The rechargeable battery indication switch cannot be changed without
187 * removing the battery, so we only need to read it once.
189 if (gpiod_get_value(batt
->rechargeable_gpio
)) {
190 /* 2-cell Li-ion, 7.4V nominal */
191 batt
->technology
= POWER_SUPPLY_TECHNOLOGY_LION
;
192 batt
->v_max
= 84000000;
193 batt
->v_min
= 60000000;
195 /* 6x AA Alkaline, 9V nominal */
196 batt
->technology
= POWER_SUPPLY_TECHNOLOGY_UNKNOWN
;
197 batt
->v_max
= 90000000;
198 batt
->v_min
= 48000000;
201 psy_cfg
.of_node
= pdev
->dev
.of_node
;
202 psy_cfg
.drv_data
= batt
;
204 batt
->psy
= devm_power_supply_register(dev
, &lego_ev3_battery_desc
,
206 err
= PTR_ERR_OR_ZERO(batt
->psy
);
208 dev_err(dev
, "failed to register power supply\n");
215 static const struct of_device_id of_lego_ev3_battery_match
[] = {
216 { .compatible
= "lego,ev3-battery", },
219 MODULE_DEVICE_TABLE(of
, of_lego_ev3_battery_match
);
221 static struct platform_driver lego_ev3_battery_driver
= {
223 .name
= "lego-ev3-battery",
224 .of_match_table
= of_lego_ev3_battery_match
,
226 .probe
= lego_ev3_battery_probe
,
228 module_platform_driver(lego_ev3_battery_driver
);
230 MODULE_LICENSE("GPL");
231 MODULE_AUTHOR("David Lechner <david@lechnology.com>");
232 MODULE_DESCRIPTION("LEGO MINDSTORMS EV3 Battery Driver");