root/drivers/power/supply/lego_ev3_battery.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. lego_ev3_battery_get_property
  2. lego_ev3_battery_set_property
  3. lego_ev3_battery_property_is_writeable
  4. lego_ev3_battery_probe

   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 ret, 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                 ret = iio_read_channel_processed(batt->iio_v, &val->intval);
  51                 if (ret)
  52                         return ret;
  53 
  54                 val->intval *= 2000;
  55                 val->intval += 50000;
  56 
  57                 /* plus adjust for shunt resistor drop */
  58                 ret = iio_read_channel_processed(batt->iio_i, &val2);
  59                 if (ret)
  60                         return ret;
  61 
  62                 val2 *= 1000;
  63                 val2 /= 15;
  64                 val->intval += val2;
  65                 break;
  66         case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN:
  67                 val->intval = batt->v_max;
  68                 break;
  69         case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN:
  70                 val->intval = batt->v_min;
  71                 break;
  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);
  75                 if (ret)
  76                         return ret;
  77 
  78                 val->intval *= 20000;
  79                 val->intval /= 15;
  80                 break;
  81         case POWER_SUPPLY_PROP_SCOPE:
  82                 val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
  83                 break;
  84         default:
  85                 return -EINVAL;
  86         }
  87 
  88         return 0;
  89 }
  90 
  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)
  94 {
  95         struct lego_ev3_battery *batt = power_supply_get_drvdata(psy);
  96 
  97         switch (psp) {
  98         case POWER_SUPPLY_PROP_TECHNOLOGY:
  99                 /*
 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.
 106                  */
 107                 if (batt->technology != POWER_SUPPLY_TECHNOLOGY_UNKNOWN)
 108                         return -EINVAL;
 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;
 114                         break;
 115                 default:
 116                         return -EINVAL;
 117                 }
 118                 break;
 119         default:
 120                 return -EINVAL;
 121         }
 122 
 123         return 0;
 124 }
 125 
 126 static int lego_ev3_battery_property_is_writeable(struct power_supply *psy,
 127                                                   enum power_supply_property psp)
 128 {
 129         struct lego_ev3_battery *batt = power_supply_get_drvdata(psy);
 130 
 131         return psp == POWER_SUPPLY_PROP_TECHNOLOGY &&
 132                 batt->technology == POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
 133 }
 134 
 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,
 142 };
 143 
 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,
 152 };
 153 
 154 static int lego_ev3_battery_probe(struct platform_device *pdev)
 155 {
 156         struct device *dev = &pdev->dev;
 157         struct lego_ev3_battery *batt;
 158         struct power_supply_config psy_cfg = {};
 159         int err;
 160 
 161         batt = devm_kzalloc(dev, sizeof(*batt), GFP_KERNEL);
 162         if (!batt)
 163                 return -ENOMEM;
 164 
 165         platform_set_drvdata(pdev, batt);
 166 
 167         batt->iio_v = devm_iio_channel_get(dev, "voltage");
 168         err = PTR_ERR_OR_ZERO(batt->iio_v);
 169         if (err) {
 170                 if (err != -EPROBE_DEFER)
 171                         dev_err(dev, "Failed to get voltage iio channel\n");
 172                 return err;
 173         }
 174 
 175         batt->iio_i = devm_iio_channel_get(dev, "current");
 176         err = PTR_ERR_OR_ZERO(batt->iio_i);
 177         if (err) {
 178                 if (err != -EPROBE_DEFER)
 179                         dev_err(dev, "Failed to get current iio channel\n");
 180                 return err;
 181         }
 182 
 183         batt->rechargeable_gpio = devm_gpiod_get(dev, "rechargeable", GPIOD_IN);
 184         err = PTR_ERR_OR_ZERO(batt->rechargeable_gpio);
 185         if (err) {
 186                 if (err != -EPROBE_DEFER)
 187                         dev_err(dev, "Failed to get rechargeable gpio\n");
 188                 return err;
 189         }
 190 
 191         /*
 192          * The rechargeable battery indication switch cannot be changed without
 193          * removing the battery, so we only need to read it once.
 194          */
 195         if (gpiod_get_value(batt->rechargeable_gpio)) {
 196                 /* 2-cell Li-ion, 7.4V nominal */
 197                 batt->technology = POWER_SUPPLY_TECHNOLOGY_LION;
 198                 batt->v_max = 84000000;
 199                 batt->v_min = 60000000;
 200         } else {
 201                 /* 6x AA Alkaline, 9V nominal */
 202                 batt->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
 203                 batt->v_max = 90000000;
 204                 batt->v_min = 48000000;
 205         }
 206 
 207         psy_cfg.of_node = pdev->dev.of_node;
 208         psy_cfg.drv_data = batt;
 209 
 210         batt->psy = devm_power_supply_register(dev, &lego_ev3_battery_desc,
 211                                                &psy_cfg);
 212         err = PTR_ERR_OR_ZERO(batt->psy);
 213         if (err) {
 214                 dev_err(dev, "failed to register power supply\n");
 215                 return err;
 216         }
 217 
 218         return 0;
 219 }
 220 
 221 static const struct of_device_id of_lego_ev3_battery_match[] = {
 222         { .compatible = "lego,ev3-battery", },
 223         { }
 224 };
 225 MODULE_DEVICE_TABLE(of, of_lego_ev3_battery_match);
 226 
 227 static struct platform_driver lego_ev3_battery_driver = {
 228         .driver = {
 229                 .name           = "lego-ev3-battery",
 230                 .of_match_table = of_lego_ev3_battery_match,
 231         },
 232         .probe  = lego_ev3_battery_probe,
 233 };
 234 module_platform_driver(lego_ev3_battery_driver);
 235 
 236 MODULE_LICENSE("GPL");
 237 MODULE_AUTHOR("David Lechner <david@lechnology.com>");
 238 MODULE_DESCRIPTION("LEGO MINDSTORMS EV3 Battery Driver");

/* [<][>][^][v][top][bottom][index][help] */