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

ina238.c (16400B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Driver for Texas Instruments INA238 power monitor chip
      4 * Datasheet: https://www.ti.com/product/ina238
      5 *
      6 * Copyright (C) 2021 Nathan Rossi <nathan.rossi@digi.com>
      7 */
      8
      9#include <linux/err.h>
     10#include <linux/hwmon.h>
     11#include <linux/i2c.h>
     12#include <linux/init.h>
     13#include <linux/kernel.h>
     14#include <linux/module.h>
     15#include <linux/of.h>
     16#include <linux/regmap.h>
     17
     18#include <linux/platform_data/ina2xx.h>
     19
     20/* INA238 register definitions */
     21#define INA238_CONFIG			0x0
     22#define INA238_ADC_CONFIG		0x1
     23#define INA238_SHUNT_CALIBRATION	0x2
     24#define INA238_SHUNT_VOLTAGE		0x4
     25#define INA238_BUS_VOLTAGE		0x5
     26#define INA238_DIE_TEMP			0x6
     27#define INA238_CURRENT			0x7
     28#define INA238_POWER			0x8
     29#define INA238_DIAG_ALERT		0xb
     30#define INA238_SHUNT_OVER_VOLTAGE	0xc
     31#define INA238_SHUNT_UNDER_VOLTAGE	0xd
     32#define INA238_BUS_OVER_VOLTAGE		0xe
     33#define INA238_BUS_UNDER_VOLTAGE	0xf
     34#define INA238_TEMP_LIMIT		0x10
     35#define INA238_POWER_LIMIT		0x11
     36#define INA238_DEVICE_ID		0x3f
     37
     38#define INA238_CONFIG_ADCRANGE		BIT(4)
     39
     40#define INA238_DIAG_ALERT_TMPOL		BIT(7)
     41#define INA238_DIAG_ALERT_SHNTOL	BIT(6)
     42#define INA238_DIAG_ALERT_SHNTUL	BIT(5)
     43#define INA238_DIAG_ALERT_BUSOL		BIT(4)
     44#define INA238_DIAG_ALERT_BUSUL		BIT(3)
     45#define INA238_DIAG_ALERT_POL		BIT(2)
     46
     47#define INA238_REGISTERS		0x11
     48
     49#define INA238_RSHUNT_DEFAULT		10000 /* uOhm */
     50
     51/* Default configuration of device on reset. */
     52#define INA238_CONFIG_DEFAULT		0
     53/* 16 sample averaging, 1052us conversion time, continuous mode */
     54#define INA238_ADC_CONFIG_DEFAULT	0xfb6a
     55/* Configure alerts to be based on averaged value (SLOWALERT) */
     56#define INA238_DIAG_ALERT_DEFAULT	0x2000
     57/*
     58 * This driver uses a fixed calibration value in order to scale current/power
     59 * based on a fixed shunt resistor value. This allows for conversion within the
     60 * device to avoid integer limits whilst current/power accuracy is scaled
     61 * relative to the shunt resistor value within the driver. This is similar to
     62 * how the ina2xx driver handles current/power scaling.
     63 *
     64 * The end result of this is that increasing shunt values (from a fixed 20 mOhm
     65 * shunt) increase the effective current/power accuracy whilst limiting the
     66 * range and decreasing shunt values decrease the effective accuracy but
     67 * increase the range.
     68 *
     69 * The value of the Current register is calculated given the following:
     70 *   Current (A) = (shunt voltage register * 5) * calibration / 81920
     71 *
     72 * The maximum shunt voltage is 163.835 mV (0x7fff, ADC_RANGE = 0, gain = 4).
     73 * With the maximum current value of 0x7fff and a fixed shunt value results in
     74 * a calibration value of 16384 (0x4000).
     75 *
     76 *   0x7fff = (0x7fff * 5) * calibration / 81920
     77 *   calibration = 0x4000
     78 *
     79 * Equivalent calibration is applied for the Power register (maximum value for
     80 * bus voltage is 102396.875 mV, 0x7fff), where the maximum power that can
     81 * occur is ~16776192 uW (register value 0x147a8):
     82 *
     83 * This scaling means the resulting values for Current and Power registers need
     84 * to be scaled by the difference between the fixed shunt resistor and the
     85 * actual shunt resistor:
     86 *
     87 *  shunt = 0x4000 / (819.2 * 10^6) / 0.001 = 20000 uOhms (with 1mA/lsb)
     88 *
     89 *  Current (mA) = register value * 20000 / rshunt / 4 * gain
     90 *  Power (W) = 0.2 * register value * 20000 / rshunt / 4 * gain
     91 */
     92#define INA238_CALIBRATION_VALUE	16384
     93#define INA238_FIXED_SHUNT		20000
     94
     95#define INA238_SHUNT_VOLTAGE_LSB	5 /* 5 uV/lsb */
     96#define INA238_BUS_VOLTAGE_LSB		3125 /* 3.125 mV/lsb */
     97#define INA238_DIE_TEMP_LSB		125 /* 125 mC/lsb */
     98
     99static struct regmap_config ina238_regmap_config = {
    100	.max_register = INA238_REGISTERS,
    101	.reg_bits = 8,
    102	.val_bits = 16,
    103};
    104
    105struct ina238_data {
    106	struct i2c_client *client;
    107	struct mutex config_lock;
    108	struct regmap *regmap;
    109	u32 rshunt;
    110	int gain;
    111};
    112
    113static int ina238_read_reg24(const struct i2c_client *client, u8 reg, u32 *val)
    114{
    115	u8 data[3];
    116	int err;
    117
    118	/* 24-bit register read */
    119	err = i2c_smbus_read_i2c_block_data(client, reg, 3, data);
    120	if (err < 0)
    121		return err;
    122	if (err != 3)
    123		return -EIO;
    124	*val = (data[0] << 16) | (data[1] << 8) | data[2];
    125
    126	return 0;
    127}
    128
    129static int ina238_read_in(struct device *dev, u32 attr, int channel,
    130			  long *val)
    131{
    132	struct ina238_data *data = dev_get_drvdata(dev);
    133	int reg, mask;
    134	int regval;
    135	int err;
    136
    137	switch (channel) {
    138	case 0:
    139		switch (attr) {
    140		case hwmon_in_input:
    141			reg = INA238_SHUNT_VOLTAGE;
    142			break;
    143		case hwmon_in_max:
    144			reg = INA238_SHUNT_OVER_VOLTAGE;
    145			break;
    146		case hwmon_in_min:
    147			reg = INA238_SHUNT_UNDER_VOLTAGE;
    148			break;
    149		case hwmon_in_max_alarm:
    150			reg = INA238_DIAG_ALERT;
    151			mask = INA238_DIAG_ALERT_SHNTOL;
    152			break;
    153		case hwmon_in_min_alarm:
    154			reg = INA238_DIAG_ALERT;
    155			mask = INA238_DIAG_ALERT_SHNTUL;
    156			break;
    157		default:
    158			return -EOPNOTSUPP;
    159		}
    160		break;
    161	case 1:
    162		switch (attr) {
    163		case hwmon_in_input:
    164			reg = INA238_BUS_VOLTAGE;
    165			break;
    166		case hwmon_in_max:
    167			reg = INA238_BUS_OVER_VOLTAGE;
    168			break;
    169		case hwmon_in_min:
    170			reg = INA238_BUS_UNDER_VOLTAGE;
    171			break;
    172		case hwmon_in_max_alarm:
    173			reg = INA238_DIAG_ALERT;
    174			mask = INA238_DIAG_ALERT_BUSOL;
    175			break;
    176		case hwmon_in_min_alarm:
    177			reg = INA238_DIAG_ALERT;
    178			mask = INA238_DIAG_ALERT_BUSUL;
    179			break;
    180		default:
    181			return -EOPNOTSUPP;
    182		}
    183		break;
    184	default:
    185		return -EOPNOTSUPP;
    186	}
    187
    188	err = regmap_read(data->regmap, reg, &regval);
    189	if (err < 0)
    190		return err;
    191
    192	switch (attr) {
    193	case hwmon_in_input:
    194	case hwmon_in_max:
    195	case hwmon_in_min:
    196		/* signed register, value in mV */
    197		regval = (s16)regval;
    198		if (channel == 0)
    199			/* gain of 1 -> LSB / 4 */
    200			*val = (regval * INA238_SHUNT_VOLTAGE_LSB) /
    201			       (1000 * (4 - data->gain + 1));
    202		else
    203			*val = (regval * INA238_BUS_VOLTAGE_LSB) / 1000;
    204		break;
    205	case hwmon_in_max_alarm:
    206	case hwmon_in_min_alarm:
    207		*val = !!(regval & mask);
    208		break;
    209	}
    210
    211	return 0;
    212}
    213
    214static int ina238_write_in(struct device *dev, u32 attr, int channel,
    215			   long val)
    216{
    217	struct ina238_data *data = dev_get_drvdata(dev);
    218	int regval;
    219
    220	if (attr != hwmon_in_max && attr != hwmon_in_min)
    221		return -EOPNOTSUPP;
    222
    223	/* convert decimal to register value */
    224	switch (channel) {
    225	case 0:
    226		/* signed value, clamp to max range +/-163 mV */
    227		regval = clamp_val(val, -163, 163);
    228		regval = (regval * 1000 * (4 - data->gain + 1)) /
    229			 INA238_SHUNT_VOLTAGE_LSB;
    230		regval = clamp_val(regval, S16_MIN, S16_MAX);
    231
    232		switch (attr) {
    233		case hwmon_in_max:
    234			return regmap_write(data->regmap,
    235					    INA238_SHUNT_OVER_VOLTAGE, regval);
    236		case hwmon_in_min:
    237			return regmap_write(data->regmap,
    238					    INA238_SHUNT_UNDER_VOLTAGE, regval);
    239		default:
    240			return -EOPNOTSUPP;
    241		}
    242	case 1:
    243		/* signed value, positive values only. Clamp to max 102.396 V */
    244		regval = clamp_val(val, 0, 102396);
    245		regval = (regval * 1000) / INA238_BUS_VOLTAGE_LSB;
    246		regval = clamp_val(regval, 0, S16_MAX);
    247
    248		switch (attr) {
    249		case hwmon_in_max:
    250			return regmap_write(data->regmap,
    251					    INA238_BUS_OVER_VOLTAGE, regval);
    252		case hwmon_in_min:
    253			return regmap_write(data->regmap,
    254					    INA238_BUS_UNDER_VOLTAGE, regval);
    255		default:
    256			return -EOPNOTSUPP;
    257		}
    258	default:
    259		return -EOPNOTSUPP;
    260	}
    261}
    262
    263static int ina238_read_current(struct device *dev, u32 attr, long *val)
    264{
    265	struct ina238_data *data = dev_get_drvdata(dev);
    266	int regval;
    267	int err;
    268
    269	switch (attr) {
    270	case hwmon_curr_input:
    271		err = regmap_read(data->regmap, INA238_CURRENT, &regval);
    272		if (err < 0)
    273			return err;
    274
    275		/* Signed register, fixed 1mA current lsb. result in mA */
    276		*val = div_s64((s16)regval * INA238_FIXED_SHUNT * data->gain,
    277			       data->rshunt * 4);
    278		break;
    279	default:
    280		return -EOPNOTSUPP;
    281	}
    282
    283	return 0;
    284}
    285
    286static int ina238_read_power(struct device *dev, u32 attr, long *val)
    287{
    288	struct ina238_data *data = dev_get_drvdata(dev);
    289	long long power;
    290	int regval;
    291	int err;
    292
    293	switch (attr) {
    294	case hwmon_power_input:
    295		err = ina238_read_reg24(data->client, INA238_POWER, &regval);
    296		if (err)
    297			return err;
    298
    299		/* Fixed 1mA lsb, scaled by 1000000 to have result in uW */
    300		power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT *
    301				data->gain, 20 * data->rshunt);
    302		/* Clamp value to maximum value of long */
    303		*val = clamp_val(power, 0, LONG_MAX);
    304		break;
    305	case hwmon_power_max:
    306		err = regmap_read(data->regmap, INA238_POWER_LIMIT, &regval);
    307		if (err)
    308			return err;
    309
    310		/*
    311		 * Truncated 24-bit compare register, lower 8-bits are
    312		 * truncated. Same conversion to/from uW as POWER register.
    313		 */
    314		power = div_u64((regval << 8) * 1000ULL * INA238_FIXED_SHUNT *
    315			       data->gain, 20 * data->rshunt);
    316		/* Clamp value to maximum value of long */
    317		*val = clamp_val(power, 0, LONG_MAX);
    318		break;
    319	case hwmon_power_max_alarm:
    320		err = regmap_read(data->regmap, INA238_DIAG_ALERT, &regval);
    321		if (err)
    322			return err;
    323
    324		*val = !!(regval & INA238_DIAG_ALERT_POL);
    325		break;
    326	default:
    327		return -EOPNOTSUPP;
    328	}
    329
    330	return 0;
    331}
    332
    333static int ina238_write_power(struct device *dev, u32 attr, long val)
    334{
    335	struct ina238_data *data = dev_get_drvdata(dev);
    336	long regval;
    337
    338	if (attr != hwmon_power_max)
    339		return -EOPNOTSUPP;
    340
    341	/*
    342	 * Unsigned postive values. Compared against the 24-bit power register,
    343	 * lower 8-bits are truncated. Same conversion to/from uW as POWER
    344	 * register.
    345	 */
    346	regval = clamp_val(val, 0, LONG_MAX);
    347	regval = div_u64(val * 20ULL * data->rshunt,
    348			 1000ULL * INA238_FIXED_SHUNT * data->gain);
    349	regval = clamp_val(regval >> 8, 0, U16_MAX);
    350
    351	return regmap_write(data->regmap, INA238_POWER_LIMIT, regval);
    352}
    353
    354static int ina238_read_temp(struct device *dev, u32 attr, long *val)
    355{
    356	struct ina238_data *data = dev_get_drvdata(dev);
    357	int regval;
    358	int err;
    359
    360	switch (attr) {
    361	case hwmon_temp_input:
    362		err = regmap_read(data->regmap, INA238_DIE_TEMP, &regval);
    363		if (err)
    364			return err;
    365
    366		/* Signed, bits 15-4 of register, result in mC */
    367		*val = ((s16)regval >> 4) * INA238_DIE_TEMP_LSB;
    368		break;
    369	case hwmon_temp_max:
    370		err = regmap_read(data->regmap, INA238_TEMP_LIMIT, &regval);
    371		if (err)
    372			return err;
    373
    374		/* Signed, bits 15-4 of register, result in mC */
    375		*val = ((s16)regval >> 4) * INA238_DIE_TEMP_LSB;
    376		break;
    377	case hwmon_temp_max_alarm:
    378		err = regmap_read(data->regmap, INA238_DIAG_ALERT, &regval);
    379		if (err)
    380			return err;
    381
    382		*val = !!(regval & INA238_DIAG_ALERT_TMPOL);
    383		break;
    384	default:
    385		return -EOPNOTSUPP;
    386	}
    387
    388	return 0;
    389}
    390
    391static int ina238_write_temp(struct device *dev, u32 attr, long val)
    392{
    393	struct ina238_data *data = dev_get_drvdata(dev);
    394	int regval;
    395
    396	if (attr != hwmon_temp_max)
    397		return -EOPNOTSUPP;
    398
    399	/* Signed, bits 15-4 of register */
    400	regval = (val / INA238_DIE_TEMP_LSB) << 4;
    401	regval = clamp_val(regval, S16_MIN, S16_MAX) & 0xfff0;
    402
    403	return regmap_write(data->regmap, INA238_TEMP_LIMIT, regval);
    404}
    405
    406static int ina238_read(struct device *dev, enum hwmon_sensor_types type,
    407		       u32 attr, int channel, long *val)
    408{
    409	switch (type) {
    410	case hwmon_in:
    411		return ina238_read_in(dev, attr, channel, val);
    412	case hwmon_curr:
    413		return ina238_read_current(dev, attr, val);
    414	case hwmon_power:
    415		return ina238_read_power(dev, attr, val);
    416	case hwmon_temp:
    417		return ina238_read_temp(dev, attr, val);
    418	default:
    419		return -EOPNOTSUPP;
    420	}
    421	return 0;
    422}
    423
    424static int ina238_write(struct device *dev, enum hwmon_sensor_types type,
    425		       u32 attr, int channel, long val)
    426{
    427	struct ina238_data *data = dev_get_drvdata(dev);
    428	int err;
    429
    430	mutex_lock(&data->config_lock);
    431
    432	switch (type) {
    433	case hwmon_in:
    434		err = ina238_write_in(dev, attr, channel, val);
    435		break;
    436	case hwmon_power:
    437		err = ina238_write_power(dev, attr, val);
    438		break;
    439	case hwmon_temp:
    440		err = ina238_write_temp(dev, attr, val);
    441		break;
    442	default:
    443		err = -EOPNOTSUPP;
    444		break;
    445	}
    446
    447	mutex_unlock(&data->config_lock);
    448	return err;
    449}
    450
    451static umode_t ina238_is_visible(const void *drvdata,
    452				 enum hwmon_sensor_types type,
    453				 u32 attr, int channel)
    454{
    455	switch (type) {
    456	case hwmon_in:
    457		switch (attr) {
    458		case hwmon_in_input:
    459		case hwmon_in_max_alarm:
    460		case hwmon_in_min_alarm:
    461			return 0444;
    462		case hwmon_in_max:
    463		case hwmon_in_min:
    464			return 0644;
    465		default:
    466			return 0;
    467		}
    468	case hwmon_curr:
    469		switch (attr) {
    470		case hwmon_curr_input:
    471			return 0444;
    472		default:
    473			return 0;
    474		}
    475	case hwmon_power:
    476		switch (attr) {
    477		case hwmon_power_input:
    478		case hwmon_power_max_alarm:
    479			return 0444;
    480		case hwmon_power_max:
    481			return 0644;
    482		default:
    483			return 0;
    484		}
    485	case hwmon_temp:
    486		switch (attr) {
    487		case hwmon_temp_input:
    488		case hwmon_temp_max_alarm:
    489			return 0444;
    490		case hwmon_temp_max:
    491			return 0644;
    492		default:
    493			return 0;
    494		}
    495	default:
    496		return 0;
    497	}
    498}
    499
    500#define INA238_HWMON_IN_CONFIG (HWMON_I_INPUT | \
    501				HWMON_I_MAX | HWMON_I_MAX_ALARM | \
    502				HWMON_I_MIN | HWMON_I_MIN_ALARM)
    503
    504static const struct hwmon_channel_info *ina238_info[] = {
    505	HWMON_CHANNEL_INFO(in,
    506			   /* 0: shunt voltage */
    507			   INA238_HWMON_IN_CONFIG,
    508			   /* 1: bus voltage */
    509			   INA238_HWMON_IN_CONFIG),
    510	HWMON_CHANNEL_INFO(curr,
    511			   /* 0: current through shunt */
    512			   HWMON_C_INPUT),
    513	HWMON_CHANNEL_INFO(power,
    514			   /* 0: power */
    515			   HWMON_P_INPUT | HWMON_P_MAX | HWMON_P_MAX_ALARM),
    516	HWMON_CHANNEL_INFO(temp,
    517			   /* 0: die temperature */
    518			   HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MAX_ALARM),
    519	NULL
    520};
    521
    522static const struct hwmon_ops ina238_hwmon_ops = {
    523	.is_visible = ina238_is_visible,
    524	.read = ina238_read,
    525	.write = ina238_write,
    526};
    527
    528static const struct hwmon_chip_info ina238_chip_info = {
    529	.ops = &ina238_hwmon_ops,
    530	.info = ina238_info,
    531};
    532
    533static int ina238_probe(struct i2c_client *client)
    534{
    535	struct ina2xx_platform_data *pdata = dev_get_platdata(&client->dev);
    536	struct device *dev = &client->dev;
    537	struct device *hwmon_dev;
    538	struct ina238_data *data;
    539	int config;
    540	int ret;
    541
    542	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
    543	if (!data)
    544		return -ENOMEM;
    545
    546	data->client = client;
    547	mutex_init(&data->config_lock);
    548
    549	data->regmap = devm_regmap_init_i2c(client, &ina238_regmap_config);
    550	if (IS_ERR(data->regmap)) {
    551		dev_err(dev, "failed to allocate register map\n");
    552		return PTR_ERR(data->regmap);
    553	}
    554
    555	/* load shunt value */
    556	data->rshunt = INA238_RSHUNT_DEFAULT;
    557	if (device_property_read_u32(dev, "shunt-resistor", &data->rshunt) < 0 && pdata)
    558		data->rshunt = pdata->shunt_uohms;
    559	if (data->rshunt == 0) {
    560		dev_err(dev, "invalid shunt resister value %u\n", data->rshunt);
    561		return -EINVAL;
    562	}
    563
    564	/* load shunt gain value */
    565	if (device_property_read_u32(dev, "ti,shunt-gain", &data->gain) < 0)
    566		data->gain = 4; /* Default of ADCRANGE = 0 */
    567	if (data->gain != 1 && data->gain != 4) {
    568		dev_err(dev, "invalid shunt gain value %u\n", data->gain);
    569		return -EINVAL;
    570	}
    571
    572	/* Setup CONFIG register */
    573	config = INA238_CONFIG_DEFAULT;
    574	if (data->gain == 1)
    575		config |= INA238_CONFIG_ADCRANGE; /* ADCRANGE = 1 is /1 */
    576	ret = regmap_write(data->regmap, INA238_CONFIG, config);
    577	if (ret < 0) {
    578		dev_err(dev, "error configuring the device: %d\n", ret);
    579		return -ENODEV;
    580	}
    581
    582	/* Setup ADC_CONFIG register */
    583	ret = regmap_write(data->regmap, INA238_ADC_CONFIG,
    584			   INA238_ADC_CONFIG_DEFAULT);
    585	if (ret < 0) {
    586		dev_err(dev, "error configuring the device: %d\n", ret);
    587		return -ENODEV;
    588	}
    589
    590	/* Setup SHUNT_CALIBRATION register with fixed value */
    591	ret = regmap_write(data->regmap, INA238_SHUNT_CALIBRATION,
    592			   INA238_CALIBRATION_VALUE);
    593	if (ret < 0) {
    594		dev_err(dev, "error configuring the device: %d\n", ret);
    595		return -ENODEV;
    596	}
    597
    598	/* Setup alert/alarm configuration */
    599	ret = regmap_write(data->regmap, INA238_DIAG_ALERT,
    600			   INA238_DIAG_ALERT_DEFAULT);
    601	if (ret < 0) {
    602		dev_err(dev, "error configuring the device: %d\n", ret);
    603		return -ENODEV;
    604	}
    605
    606	hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
    607							 &ina238_chip_info,
    608							 NULL);
    609	if (IS_ERR(hwmon_dev))
    610		return PTR_ERR(hwmon_dev);
    611
    612	dev_info(dev, "power monitor %s (Rshunt = %u uOhm, gain = %u)\n",
    613		 client->name, data->rshunt, data->gain);
    614
    615	return 0;
    616}
    617
    618static const struct i2c_device_id ina238_id[] = {
    619	{ "ina238", 0 },
    620	{ }
    621};
    622MODULE_DEVICE_TABLE(i2c, ina238_id);
    623
    624static const struct of_device_id __maybe_unused ina238_of_match[] = {
    625	{ .compatible = "ti,ina238" },
    626	{ },
    627};
    628MODULE_DEVICE_TABLE(of, ina238_of_match);
    629
    630static struct i2c_driver ina238_driver = {
    631	.class		= I2C_CLASS_HWMON,
    632	.driver = {
    633		.name	= "ina238",
    634		.of_match_table = of_match_ptr(ina238_of_match),
    635	},
    636	.probe_new	= ina238_probe,
    637	.id_table	= ina238_id,
    638};
    639
    640module_i2c_driver(ina238_driver);
    641
    642MODULE_AUTHOR("Nathan Rossi <nathan.rossi@digi.com>");
    643MODULE_DESCRIPTION("ina238 driver");
    644MODULE_LICENSE("GPL");