cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

lego_ev3_battery.c (6463B)


      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
     27struct 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
     37static 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
     91static 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
    126static 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
    135static 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
    144static 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
    154static 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		return dev_err_probe(dev, err,
    171				     "Failed to get voltage iio channel\n");
    172
    173	batt->iio_i = devm_iio_channel_get(dev, "current");
    174	err = PTR_ERR_OR_ZERO(batt->iio_i);
    175	if (err)
    176		return dev_err_probe(dev, err,
    177				     "Failed to get current iio channel\n");
    178
    179	batt->rechargeable_gpio = devm_gpiod_get(dev, "rechargeable", GPIOD_IN);
    180	err = PTR_ERR_OR_ZERO(batt->rechargeable_gpio);
    181	if (err)
    182		return dev_err_probe(dev, err,
    183				     "Failed to get rechargeable gpio\n");
    184
    185	/*
    186	 * The rechargeable battery indication switch cannot be changed without
    187	 * removing the battery, so we only need to read it once.
    188	 */
    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;
    194	} else {
    195		/* 6x AA Alkaline, 9V nominal */
    196		batt->technology = POWER_SUPPLY_TECHNOLOGY_UNKNOWN;
    197		batt->v_max = 90000000;
    198		batt->v_min = 48000000;
    199	}
    200
    201	psy_cfg.of_node = pdev->dev.of_node;
    202	psy_cfg.drv_data = batt;
    203
    204	batt->psy = devm_power_supply_register(dev, &lego_ev3_battery_desc,
    205					       &psy_cfg);
    206	err = PTR_ERR_OR_ZERO(batt->psy);
    207	if (err) {
    208		dev_err(dev, "failed to register power supply\n");
    209		return err;
    210	}
    211
    212	return 0;
    213}
    214
    215static const struct of_device_id of_lego_ev3_battery_match[] = {
    216	{ .compatible = "lego,ev3-battery", },
    217	{ }
    218};
    219MODULE_DEVICE_TABLE(of, of_lego_ev3_battery_match);
    220
    221static struct platform_driver lego_ev3_battery_driver = {
    222	.driver	= {
    223		.name		= "lego-ev3-battery",
    224		.of_match_table = of_lego_ev3_battery_match,
    225	},
    226	.probe	= lego_ev3_battery_probe,
    227};
    228module_platform_driver(lego_ev3_battery_driver);
    229
    230MODULE_LICENSE("GPL");
    231MODULE_AUTHOR("David Lechner <david@lechnology.com>");
    232MODULE_DESCRIPTION("LEGO MINDSTORMS EV3 Battery Driver");