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 iio_read_channel_processed(batt
->iio_v
, &val
->intval
);
52 val
->intval
+= 200000;
53 /* plus adjust for shunt resistor drop */
54 iio_read_channel_processed(batt
->iio_i
, &val2
);
59 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
:
60 val
->intval
= batt
->v_max
;
62 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
:
63 val
->intval
= batt
->v_min
;
65 case POWER_SUPPLY_PROP_CURRENT_NOW
:
66 /* battery current is iio channel / 15 / 0.05 ohms */
67 iio_read_channel_processed(batt
->iio_i
, &val
->intval
);
71 case POWER_SUPPLY_PROP_SCOPE
:
72 val
->intval
= POWER_SUPPLY_SCOPE_SYSTEM
;
81 static int lego_ev3_battery_set_property(struct power_supply
*psy
,
82 enum power_supply_property psp
,
83 const union power_supply_propval
*val
)
85 struct lego_ev3_battery
*batt
= power_supply_get_drvdata(psy
);
88 case POWER_SUPPLY_PROP_TECHNOLOGY
:
90 * Only allow changing technology from Unknown to NiMH. Li-ion
91 * batteries are automatically detected and should not be
92 * overridden. Rechargeable AA batteries, on the other hand,
93 * cannot be automatically detected, and so must be manually
94 * specified. This should only be set once during system init,
95 * so there is no mechanism to go back to Unknown.
97 if (batt
->technology
!= POWER_SUPPLY_TECHNOLOGY_UNKNOWN
)
99 switch (val
->intval
) {
100 case POWER_SUPPLY_TECHNOLOGY_NiMH
:
101 batt
->technology
= POWER_SUPPLY_TECHNOLOGY_NiMH
;
102 batt
->v_max
= 7800000;
103 batt
->v_min
= 5400000;
116 static int lego_ev3_battery_property_is_writeable(struct power_supply
*psy
,
117 enum power_supply_property psp
)
119 struct lego_ev3_battery
*batt
= power_supply_get_drvdata(psy
);
121 return psp
== POWER_SUPPLY_PROP_TECHNOLOGY
&&
122 batt
->technology
== POWER_SUPPLY_TECHNOLOGY_UNKNOWN
;
125 static enum power_supply_property lego_ev3_battery_props
[] = {
126 POWER_SUPPLY_PROP_TECHNOLOGY
,
127 POWER_SUPPLY_PROP_VOLTAGE_NOW
,
128 POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN
,
129 POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN
,
130 POWER_SUPPLY_PROP_CURRENT_NOW
,
131 POWER_SUPPLY_PROP_SCOPE
,
134 static const struct power_supply_desc lego_ev3_battery_desc
= {
135 .name
= "lego-ev3-battery",
136 .type
= POWER_SUPPLY_TYPE_BATTERY
,
137 .properties
= lego_ev3_battery_props
,
138 .num_properties
= ARRAY_SIZE(lego_ev3_battery_props
),
139 .get_property
= lego_ev3_battery_get_property
,
140 .set_property
= lego_ev3_battery_set_property
,
141 .property_is_writeable
= lego_ev3_battery_property_is_writeable
,
144 static int lego_ev3_battery_probe(struct platform_device
*pdev
)
146 struct device
*dev
= &pdev
->dev
;
147 struct lego_ev3_battery
*batt
;
148 struct power_supply_config psy_cfg
= {};
151 batt
= devm_kzalloc(dev
, sizeof(*batt
), GFP_KERNEL
);
155 platform_set_drvdata(pdev
, batt
);
157 batt
->iio_v
= devm_iio_channel_get(dev
, "voltage");
158 err
= PTR_ERR_OR_ZERO(batt
->iio_v
);
160 if (err
!= -EPROBE_DEFER
)
161 dev_err(dev
, "Failed to get voltage iio channel\n");
165 batt
->iio_i
= devm_iio_channel_get(dev
, "current");
166 err
= PTR_ERR_OR_ZERO(batt
->iio_i
);
168 if (err
!= -EPROBE_DEFER
)
169 dev_err(dev
, "Failed to get current iio channel\n");
173 batt
->rechargeable_gpio
= devm_gpiod_get(dev
, "rechargeable", GPIOD_IN
);
174 err
= PTR_ERR_OR_ZERO(batt
->rechargeable_gpio
);
176 if (err
!= -EPROBE_DEFER
)
177 dev_err(dev
, "Failed to get rechargeable gpio\n");
182 * The rechargeable battery indication switch cannot be changed without
183 * removing the battery, so we only need to read it once.
185 if (gpiod_get_value(batt
->rechargeable_gpio
)) {
186 /* 2-cell Li-ion, 7.4V nominal */
187 batt
->technology
= POWER_SUPPLY_TECHNOLOGY_LION
;
188 batt
->v_max
= 84000000;
189 batt
->v_min
= 60000000;
191 /* 6x AA Alkaline, 9V nominal */
192 batt
->technology
= POWER_SUPPLY_TECHNOLOGY_UNKNOWN
;
193 batt
->v_max
= 90000000;
194 batt
->v_min
= 48000000;
197 psy_cfg
.of_node
= pdev
->dev
.of_node
;
198 psy_cfg
.drv_data
= batt
;
200 batt
->psy
= devm_power_supply_register(dev
, &lego_ev3_battery_desc
,
202 err
= PTR_ERR_OR_ZERO(batt
->psy
);
204 dev_err(dev
, "failed to register power supply\n");
211 static const struct of_device_id of_lego_ev3_battery_match
[] = {
212 { .compatible
= "lego,ev3-battery", },
215 MODULE_DEVICE_TABLE(of
, of_lego_ev3_battery_match
);
217 static struct platform_driver lego_ev3_battery_driver
= {
219 .name
= "lego-ev3-battery",
220 .of_match_table
= of_lego_ev3_battery_match
,
222 .probe
= lego_ev3_battery_probe
,
224 module_platform_driver(lego_ev3_battery_driver
);
226 MODULE_LICENSE("GPL");
227 MODULE_AUTHOR("David Lechner <david@lechnology.com>");
228 MODULE_DESCRIPTION("LEGO MINDSTORMS EV3 Battery Driver");