1 /* 2 * Battery driver for LEGO MINDSTORMS EV3 3 * 4 * Copyright (C) 2017 David Lechner <david@lechnology.com> 5 * 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. 9 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. 14 */ 15 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> 26 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; 32 int technology; 33 int v_max; 34 int v_min; 35 }; 36 37 static int lego_ev3_battery_get_property(struct power_supply *psy, 38 enum power_supply_property psp, 39 union power_supply_propval *val) 40 { 41 struct lego_ev3_battery *batt = power_supply_get_drvdata(psy); 42 int val2; 43 44 switch (psp) { 45 case POWER_SUPPLY_PROP_TECHNOLOGY: 46 val->intval = batt->technology; 47 break; 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); 51 val->intval *= 2000; 52 val->intval += 200000; 53 /* plus adjust for shunt resistor drop */ 54 iio_read_channel_processed(batt->iio_i, &val2); 55 val2 *= 1000; 56 val2 /= 15; 57 val->intval += val2; 58 break; 59 case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: 60 val->intval = batt->v_max; 61 break; 62 case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: 63 val->intval = batt->v_min; 64 break; 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); 68 val->intval *= 20000; 69 val->intval /= 15; 70 break; 71 case POWER_SUPPLY_PROP_SCOPE: 72 val->intval = POWER_SUPPLY_SCOPE_SYSTEM; 73 break; 74 default: 75 return -EINVAL; 76 } 77 78 return 0; 79 } 80 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) 84 { 85 struct lego_ev3_battery *batt = power_supply_get_drvdata(psy); 86 87 switch (psp) { 88 case POWER_SUPPLY_PROP_TECHNOLOGY: 89 /* 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. 96 */ 97 if (batt->technology != POWER_SUPPLY_TECHNOLOGY_UNKNOWN) 98 return -EINVAL; 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; 104 break; 105 default: 106 return -EINVAL; 107 } 108 break; 109 default: 110 return -EINVAL; 111 } 112 113 return 0; 114 } 115 116 static int lego_ev3_battery_property_is_writeable(struct power_supply *psy, 117 enum power_supply_property psp) 118 { 119 struct lego_ev3_battery *batt = power_supply_get_drvdata(psy); 120 121 return psp == POWER_SUPPLY_PROP_TECHNOLOGY && 122 batt->technology == POWER_SUPPLY_TECHNOLOGY_UNKNOWN; 123 } 124 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, 132 }; 133 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, 142 }; 143 144 static int lego_ev3_battery_probe(struct platform_device *pdev) 145 { 146 struct device *dev = &pdev->dev; 147 struct lego_ev3_battery *batt; 148 struct power_supply_config psy_cfg = {}; 149 int err; 150 151 batt = devm_kzalloc(dev, sizeof(*batt), GFP_KERNEL); 152 if (!batt) 153 return -ENOMEM; 154 155 platform_set_drvdata(pdev, batt); 156 157 batt->iio_v = devm_iio_channel_get(dev, "voltage"); 158 err = PTR_ERR_OR_ZERO(batt->iio_v); 159 if (err) { 160 if (err != -EPROBE_DEFER) 161 dev_err(dev, "Failed to get voltage iio channel\n"); 162 return err; 163 } 164 165 batt->iio_i = devm_iio_channel_get(dev, "current"); 166 err = PTR_ERR_OR_ZERO(batt->iio_i); 167 if (err) { 168 if (err != -EPROBE_DEFER) 169 dev_err(dev, "Failed to get current iio channel\n"); 170 return err; 171 } 172 173 batt->rechargeable_gpio = devm_gpiod_get(dev, "rechargeable", GPIOD_IN); 174 err = PTR_ERR_OR_ZERO(batt->rechargeable_gpio); 175 if (err) { 176 if (err != -EPROBE_DEFER) 177 dev_err(dev, "Failed to get rechargeable gpio\n"); 178 return err; 179 } 180 181 /* 182 * The rechargeable battery indication switch cannot be changed without 183 * removing the battery, so we only need to read it once. 184 */ 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; 190 } else { 191 /* 6x AA Alkaline, 9V nominal */ 192 batt->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN; 193 batt->v_max = 90000000; 194 batt->v_min = 48000000; 195 } 196 197 psy_cfg.of_node = pdev->dev.of_node; 198 psy_cfg.drv_data = batt; 199 200 batt->psy = devm_power_supply_register(dev, &lego_ev3_battery_desc, 201 &psy_cfg); 202 err = PTR_ERR_OR_ZERO(batt->psy); 203 if (err) { 204 dev_err(dev, "failed to register power supply\n"); 205 return err; 206 } 207 208 return 0; 209 } 210 211 static const struct of_device_id of_lego_ev3_battery_match[] = { 212 { .compatible = "lego,ev3-battery", }, 213 { } 214 }; 215 MODULE_DEVICE_TABLE(of, of_lego_ev3_battery_match); 216 217 static struct platform_driver lego_ev3_battery_driver = { 218 .driver = { 219 .name = "lego-ev3-battery", 220 .of_match_table = of_lego_ev3_battery_match, 221 }, 222 .probe = lego_ev3_battery_probe, 223 }; 224 module_platform_driver(lego_ev3_battery_driver); 225 226 MODULE_LICENSE("GPL"); 227 MODULE_AUTHOR("David Lechner <david@lechnology.com>"); 228 MODULE_DESCRIPTION("LEGO MINDSTORMS EV3 Battery Driver"); 229