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

max1721x_battery.c (13070B)


      1/*
      2 * 1-Wire implementation for Maxim Semiconductor
      3 * MAX7211/MAX17215 standalone fuel gauge chip
      4 *
      5 * Copyright (C) 2017 Radioavionica Corporation
      6 * Author: Alex A. Mihaylov <minimumlaw@rambler.ru>
      7 *
      8 * Use consistent with the GNU GPL is permitted,
      9 * provided that this copyright notice is
     10 * preserved in its entirety in all copies and derived works.
     11 *
     12 */
     13
     14#include <linux/module.h>
     15#include <linux/slab.h>
     16#include <linux/w1.h>
     17#include <linux/regmap.h>
     18#include <linux/power_supply.h>
     19
     20#define W1_MAX1721X_FAMILY_ID		0x26
     21#define DEF_DEV_NAME_MAX17211		"MAX17211"
     22#define DEF_DEV_NAME_MAX17215		"MAX17215"
     23#define DEF_DEV_NAME_UNKNOWN		"UNKNOWN"
     24#define DEF_MFG_NAME			"MAXIM"
     25
     26#define PSY_MAX_NAME_LEN	32
     27
     28/* Number of valid register addresses in W1 mode */
     29#define MAX1721X_MAX_REG_NR	0x1EF
     30
     31/* Factory settings (nonvolatile registers) (W1 specific) */
     32#define MAX1721X_REG_NRSENSE	0x1CF	/* RSense in 10^-5 Ohm */
     33/* Strings */
     34#define MAX1721X_REG_MFG_STR	0x1CC
     35#define MAX1721X_REG_MFG_NUMB	3
     36#define MAX1721X_REG_DEV_STR	0x1DB
     37#define MAX1721X_REG_DEV_NUMB	5
     38/* HEX Strings */
     39#define MAX1721X_REG_SER_HEX	0x1D8
     40
     41/* MAX172XX Output Registers for W1 chips */
     42#define MAX172XX_REG_STATUS	0x000	/* status reg */
     43#define MAX172XX_BAT_PRESENT	(1<<4)	/* battery connected bit */
     44#define MAX172XX_REG_DEVNAME	0x021	/* chip config */
     45#define MAX172XX_DEV_MASK	0x000F	/* chip type mask */
     46#define MAX172X1_DEV		0x0001
     47#define MAX172X5_DEV		0x0005
     48#define MAX172XX_REG_TEMP	0x008	/* Temperature */
     49#define MAX172XX_REG_BATT	0x0DA	/* Battery voltage */
     50#define MAX172XX_REG_CURRENT	0x00A	/* Actual current */
     51#define MAX172XX_REG_AVGCURRENT	0x00B	/* Average current */
     52#define MAX172XX_REG_REPSOC	0x006	/* Percentage of charge */
     53#define MAX172XX_REG_DESIGNCAP	0x018	/* Design capacity */
     54#define MAX172XX_REG_REPCAP	0x005	/* Average capacity */
     55#define MAX172XX_REG_TTE	0x011	/* Time to empty */
     56#define MAX172XX_REG_TTF	0x020	/* Time to full */
     57
     58struct max17211_device_info {
     59	char name[PSY_MAX_NAME_LEN];
     60	struct power_supply *bat;
     61	struct power_supply_desc bat_desc;
     62	struct device *w1_dev;
     63	struct regmap *regmap;
     64	/* battery design format */
     65	unsigned int rsense; /* in tenths uOhm */
     66	char DeviceName[2 * MAX1721X_REG_DEV_NUMB + 1];
     67	char ManufacturerName[2 * MAX1721X_REG_MFG_NUMB + 1];
     68	char SerialNumber[13]; /* see get_sn_str() later for comment */
     69};
     70
     71/* Convert regs value to power_supply units */
     72
     73static inline int max172xx_time_to_ps(unsigned int reg)
     74{
     75	return reg * 5625 / 1000;	/* in sec. */
     76}
     77
     78static inline int max172xx_percent_to_ps(unsigned int reg)
     79{
     80	return reg / 256;	/* in percent from 0 to 100 */
     81}
     82
     83static inline int max172xx_voltage_to_ps(unsigned int reg)
     84{
     85	return reg * 1250;	/* in uV */
     86}
     87
     88static inline int max172xx_capacity_to_ps(unsigned int reg)
     89{
     90	return reg * 500;	/* in uAh */
     91}
     92
     93/*
     94 * Current and temperature is signed values, so unsigned regs
     95 * value must be converted to signed type
     96 */
     97
     98static inline int max172xx_temperature_to_ps(unsigned int reg)
     99{
    100	int val = (int16_t)(reg);
    101
    102	return val * 10 / 256; /* in tenths of deg. C */
    103}
    104
    105/*
    106 * Calculating current registers resolution:
    107 *
    108 * RSense stored in 10^-5 Ohm, so measurement voltage must be
    109 * in 10^-11 Volts for get current in uA.
    110 * 16 bit current reg fullscale +/-51.2mV is 102400 uV.
    111 * So: 102400 / 65535 * 10^5 = 156252
    112 */
    113static inline int max172xx_current_to_voltage(unsigned int reg)
    114{
    115	int val = (int16_t)(reg);
    116
    117	return val * 156252;
    118}
    119
    120
    121static inline struct max17211_device_info *
    122to_device_info(struct power_supply *psy)
    123{
    124	return power_supply_get_drvdata(psy);
    125}
    126
    127static int max1721x_battery_get_property(struct power_supply *psy,
    128	enum power_supply_property psp,
    129	union power_supply_propval *val)
    130{
    131	struct max17211_device_info *info = to_device_info(psy);
    132	unsigned int reg = 0;
    133	int ret = 0;
    134
    135	switch (psp) {
    136	case POWER_SUPPLY_PROP_PRESENT:
    137		/*
    138		 * POWER_SUPPLY_PROP_PRESENT will always readable via
    139		 * sysfs interface. Value return 0 if battery not
    140		 * present or unaccessible via W1.
    141		 */
    142		val->intval =
    143			regmap_read(info->regmap, MAX172XX_REG_STATUS,
    144			&reg) ? 0 : !(reg & MAX172XX_BAT_PRESENT);
    145		break;
    146	case POWER_SUPPLY_PROP_CAPACITY:
    147		ret = regmap_read(info->regmap, MAX172XX_REG_REPSOC, &reg);
    148		val->intval = max172xx_percent_to_ps(reg);
    149		break;
    150	case POWER_SUPPLY_PROP_VOLTAGE_NOW:
    151		ret = regmap_read(info->regmap, MAX172XX_REG_BATT, &reg);
    152		val->intval = max172xx_voltage_to_ps(reg);
    153		break;
    154	case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
    155		ret = regmap_read(info->regmap, MAX172XX_REG_DESIGNCAP, &reg);
    156		val->intval = max172xx_capacity_to_ps(reg);
    157		break;
    158	case POWER_SUPPLY_PROP_CHARGE_AVG:
    159		ret = regmap_read(info->regmap, MAX172XX_REG_REPCAP, &reg);
    160		val->intval = max172xx_capacity_to_ps(reg);
    161		break;
    162	case POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG:
    163		ret = regmap_read(info->regmap, MAX172XX_REG_TTE, &reg);
    164		val->intval = max172xx_time_to_ps(reg);
    165		break;
    166	case POWER_SUPPLY_PROP_TIME_TO_FULL_AVG:
    167		ret = regmap_read(info->regmap, MAX172XX_REG_TTF, &reg);
    168		val->intval = max172xx_time_to_ps(reg);
    169		break;
    170	case POWER_SUPPLY_PROP_TEMP:
    171		ret = regmap_read(info->regmap, MAX172XX_REG_TEMP, &reg);
    172		val->intval = max172xx_temperature_to_ps(reg);
    173		break;
    174	/* We need signed current, so must cast info->rsense to signed type */
    175	case POWER_SUPPLY_PROP_CURRENT_NOW:
    176		ret = regmap_read(info->regmap, MAX172XX_REG_CURRENT, &reg);
    177		val->intval =
    178			max172xx_current_to_voltage(reg) / (int)info->rsense;
    179		break;
    180	case POWER_SUPPLY_PROP_CURRENT_AVG:
    181		ret = regmap_read(info->regmap, MAX172XX_REG_AVGCURRENT, &reg);
    182		val->intval =
    183			max172xx_current_to_voltage(reg) / (int)info->rsense;
    184		break;
    185	/*
    186	 * Strings already received and inited by probe.
    187	 * We do dummy read for check battery still available.
    188	 */
    189	case POWER_SUPPLY_PROP_MODEL_NAME:
    190		ret = regmap_read(info->regmap, MAX1721X_REG_DEV_STR, &reg);
    191		val->strval = info->DeviceName;
    192		break;
    193	case POWER_SUPPLY_PROP_MANUFACTURER:
    194		ret = regmap_read(info->regmap, MAX1721X_REG_MFG_STR, &reg);
    195		val->strval = info->ManufacturerName;
    196		break;
    197	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
    198		ret = regmap_read(info->regmap, MAX1721X_REG_SER_HEX, &reg);
    199		val->strval = info->SerialNumber;
    200		break;
    201	default:
    202		ret = -EINVAL;
    203	}
    204
    205	return ret;
    206}
    207
    208static enum power_supply_property max1721x_battery_props[] = {
    209	/* int */
    210	POWER_SUPPLY_PROP_PRESENT,
    211	POWER_SUPPLY_PROP_CAPACITY,
    212	POWER_SUPPLY_PROP_VOLTAGE_NOW,
    213	POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
    214	POWER_SUPPLY_PROP_CHARGE_AVG,
    215	POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG,
    216	POWER_SUPPLY_PROP_TIME_TO_FULL_AVG,
    217	POWER_SUPPLY_PROP_TEMP,
    218	POWER_SUPPLY_PROP_CURRENT_NOW,
    219	POWER_SUPPLY_PROP_CURRENT_AVG,
    220	/* strings */
    221	POWER_SUPPLY_PROP_MODEL_NAME,
    222	POWER_SUPPLY_PROP_MANUFACTURER,
    223	POWER_SUPPLY_PROP_SERIAL_NUMBER,
    224};
    225
    226static int get_string(struct max17211_device_info *info,
    227			uint16_t reg, uint8_t nr, char *str)
    228{
    229	unsigned int val;
    230
    231	if (!str || !(reg == MAX1721X_REG_MFG_STR ||
    232			reg == MAX1721X_REG_DEV_STR))
    233		return -EFAULT;
    234
    235	while (nr--) {
    236		if (regmap_read(info->regmap, reg++, &val))
    237			return -EFAULT;
    238		*str++ = val>>8 & 0x00FF;
    239		*str++ = val & 0x00FF;
    240	}
    241	return 0;
    242}
    243
    244/* Maxim say: Serial number is a hex string up to 12 hex characters */
    245static int get_sn_string(struct max17211_device_info *info, char *str)
    246{
    247	unsigned int val[3];
    248
    249	if (!str)
    250		return -EFAULT;
    251
    252	if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX, &val[0]))
    253		return -EFAULT;
    254	if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX + 1, &val[1]))
    255		return -EFAULT;
    256	if (regmap_read(info->regmap, MAX1721X_REG_SER_HEX + 2, &val[2]))
    257		return -EFAULT;
    258
    259	snprintf(str, 13, "%04X%04X%04X", val[0], val[1], val[2]);
    260	return 0;
    261}
    262
    263/*
    264 * MAX1721x registers description for w1-regmap
    265 */
    266static const struct regmap_range max1721x_allow_range[] = {
    267	regmap_reg_range(0, 0xDF),	/* volatile data */
    268	regmap_reg_range(0x180, 0x1DF),	/* non-volatile memory */
    269	regmap_reg_range(0x1E0, 0x1EF),	/* non-volatile history (unused) */
    270};
    271
    272static const struct regmap_range max1721x_deny_range[] = {
    273	/* volatile data unused registers */
    274	regmap_reg_range(0x24, 0x26),
    275	regmap_reg_range(0x30, 0x31),
    276	regmap_reg_range(0x33, 0x34),
    277	regmap_reg_range(0x37, 0x37),
    278	regmap_reg_range(0x3B, 0x3C),
    279	regmap_reg_range(0x40, 0x41),
    280	regmap_reg_range(0x43, 0x44),
    281	regmap_reg_range(0x47, 0x49),
    282	regmap_reg_range(0x4B, 0x4C),
    283	regmap_reg_range(0x4E, 0xAF),
    284	regmap_reg_range(0xB1, 0xB3),
    285	regmap_reg_range(0xB5, 0xB7),
    286	regmap_reg_range(0xBF, 0xD0),
    287	regmap_reg_range(0xDB, 0xDB),
    288	/* hole between volatile and non-volatile registers */
    289	regmap_reg_range(0xE0, 0x17F),
    290};
    291
    292static const struct regmap_access_table max1721x_regs = {
    293	.yes_ranges	= max1721x_allow_range,
    294	.n_yes_ranges	= ARRAY_SIZE(max1721x_allow_range),
    295	.no_ranges	= max1721x_deny_range,
    296	.n_no_ranges	= ARRAY_SIZE(max1721x_deny_range),
    297};
    298
    299/*
    300 * Model Gauge M5 Algorithm output register
    301 * Volatile data (must not be cached)
    302 */
    303static const struct regmap_range max1721x_volatile_allow[] = {
    304	regmap_reg_range(0, 0xDF),
    305};
    306
    307static const struct regmap_access_table max1721x_volatile_regs = {
    308	.yes_ranges	= max1721x_volatile_allow,
    309	.n_yes_ranges	= ARRAY_SIZE(max1721x_volatile_allow),
    310};
    311
    312/*
    313 * W1-regmap config
    314 */
    315static const struct regmap_config max1721x_regmap_w1_config = {
    316	.reg_bits = 16,
    317	.val_bits = 16,
    318	.rd_table = &max1721x_regs,
    319	.volatile_table = &max1721x_volatile_regs,
    320	.max_register = MAX1721X_MAX_REG_NR,
    321};
    322
    323static int devm_w1_max1721x_add_device(struct w1_slave *sl)
    324{
    325	struct power_supply_config psy_cfg = {};
    326	struct max17211_device_info *info;
    327
    328	info = devm_kzalloc(&sl->dev, sizeof(*info), GFP_KERNEL);
    329	if (!info)
    330		return -ENOMEM;
    331
    332	sl->family_data = (void *)info;
    333	info->w1_dev = &sl->dev;
    334
    335	/*
    336	 * power_supply class battery name translated from W1 slave device
    337	 * unique ID (look like 26-0123456789AB) to "max1721x-0123456789AB\0"
    338	 * so, 26 (device family) correspond to max1721x devices.
    339	 * Device name still unique for any number of connected devices.
    340	 */
    341	snprintf(info->name, sizeof(info->name),
    342		"max1721x-%012X", (unsigned int)sl->reg_num.id);
    343	info->bat_desc.name = info->name;
    344
    345	/*
    346	 * FixMe: battery device name exceed max len for thermal_zone device
    347	 * name and translation to thermal_zone must be disabled.
    348	 */
    349	info->bat_desc.no_thermal = true;
    350	info->bat_desc.type = POWER_SUPPLY_TYPE_BATTERY;
    351	info->bat_desc.properties = max1721x_battery_props;
    352	info->bat_desc.num_properties = ARRAY_SIZE(max1721x_battery_props);
    353	info->bat_desc.get_property = max1721x_battery_get_property;
    354	psy_cfg.drv_data = info;
    355
    356	/* regmap init */
    357	info->regmap = devm_regmap_init_w1(info->w1_dev,
    358					&max1721x_regmap_w1_config);
    359	if (IS_ERR(info->regmap)) {
    360		int err = PTR_ERR(info->regmap);
    361
    362		dev_err(info->w1_dev, "Failed to allocate register map: %d\n",
    363			err);
    364		return err;
    365	}
    366
    367	/* rsense init */
    368	info->rsense = 0;
    369	if (regmap_read(info->regmap, MAX1721X_REG_NRSENSE, &info->rsense)) {
    370		dev_err(info->w1_dev, "Can't read RSense. Hardware error.\n");
    371		return -ENODEV;
    372	}
    373
    374	if (!info->rsense) {
    375		dev_warn(info->w1_dev, "RSense not calibrated, set 10 mOhms!\n");
    376		info->rsense = 1000; /* in regs in 10^-5 */
    377	}
    378	dev_info(info->w1_dev, "RSense: %d mOhms.\n", info->rsense / 100);
    379
    380	if (get_string(info, MAX1721X_REG_MFG_STR,
    381			MAX1721X_REG_MFG_NUMB, info->ManufacturerName)) {
    382		dev_err(info->w1_dev, "Can't read manufacturer. Hardware error.\n");
    383		return -ENODEV;
    384	}
    385
    386	if (!info->ManufacturerName[0])
    387		strncpy(info->ManufacturerName, DEF_MFG_NAME,
    388			2 * MAX1721X_REG_MFG_NUMB);
    389
    390	if (get_string(info, MAX1721X_REG_DEV_STR,
    391			MAX1721X_REG_DEV_NUMB, info->DeviceName)) {
    392		dev_err(info->w1_dev, "Can't read device. Hardware error.\n");
    393		return -ENODEV;
    394	}
    395	if (!info->DeviceName[0]) {
    396		unsigned int dev_name;
    397
    398		if (regmap_read(info->regmap,
    399				MAX172XX_REG_DEVNAME, &dev_name)) {
    400			dev_err(info->w1_dev, "Can't read device name reg.\n");
    401			return -ENODEV;
    402		}
    403
    404		switch (dev_name & MAX172XX_DEV_MASK) {
    405		case MAX172X1_DEV:
    406			strncpy(info->DeviceName, DEF_DEV_NAME_MAX17211,
    407				2 * MAX1721X_REG_DEV_NUMB);
    408			break;
    409		case MAX172X5_DEV:
    410			strncpy(info->DeviceName, DEF_DEV_NAME_MAX17215,
    411				2 * MAX1721X_REG_DEV_NUMB);
    412			break;
    413		default:
    414			strncpy(info->DeviceName, DEF_DEV_NAME_UNKNOWN,
    415				2 * MAX1721X_REG_DEV_NUMB);
    416		}
    417	}
    418
    419	if (get_sn_string(info, info->SerialNumber)) {
    420		dev_err(info->w1_dev, "Can't read serial. Hardware error.\n");
    421		return -ENODEV;
    422	}
    423
    424	info->bat = devm_power_supply_register(&sl->dev, &info->bat_desc,
    425						&psy_cfg);
    426	if (IS_ERR(info->bat)) {
    427		dev_err(info->w1_dev, "failed to register battery\n");
    428		return PTR_ERR(info->bat);
    429	}
    430
    431	return 0;
    432}
    433
    434static const struct w1_family_ops w1_max1721x_fops = {
    435	.add_slave = devm_w1_max1721x_add_device,
    436};
    437
    438static struct w1_family w1_max1721x_family = {
    439	.fid = W1_MAX1721X_FAMILY_ID,
    440	.fops = &w1_max1721x_fops,
    441};
    442
    443module_w1_family(w1_max1721x_family);
    444
    445MODULE_LICENSE("GPL");
    446MODULE_AUTHOR("Alex A. Mihaylov <minimumlaw@rambler.ru>");
    447MODULE_DESCRIPTION("Maxim MAX17211/MAX17215 Fuel Gauage IC driver");
    448MODULE_ALIAS("w1-family-" __stringify(W1_MAX1721X_FAMILY_ID));