brintos

brintos / linux-shallow public Read only

0
0
Text · 16.9 KiB · bd7b338 Raw
606 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Driver for the Texas Instruments / Burr Brown INA2094 * Bidirectional Current/Power Monitor5 *6 * Copyright (C) 2012 Guenter Roeck <linux@roeck-us.net>7 *8 * Derived from Ira W. Snyder's original driver submission9 *	Copyright (C) 2008 Paul Hays <Paul.Hays@cattail.ca>10 *	Copyright (C) 2008-2009 Ira W. Snyder <iws@ovro.caltech.edu>11 *12 * Aligned with ina2xx driver13 *	Copyright (C) 2012 Lothar Felten <l-felten@ti.com>14 *	Thanks to Jan Volkering15 *16 * Datasheet:17 * https://www.ti.com/lit/gpn/ina20918 */19 20#include <linux/kernel.h>21#include <linux/module.h>22#include <linux/init.h>23#include <linux/err.h>24#include <linux/slab.h>25#include <linux/bug.h>26#include <linux/i2c.h>27#include <linux/hwmon.h>28#include <linux/hwmon-sysfs.h>29 30#include <linux/platform_data/ina2xx.h>31 32/* register definitions */33#define INA209_CONFIGURATION		0x0034#define INA209_STATUS			0x0135#define INA209_STATUS_MASK		0x0236#define INA209_SHUNT_VOLTAGE		0x0337#define INA209_BUS_VOLTAGE		0x0438#define INA209_POWER			0x0539#define INA209_CURRENT			0x0640#define INA209_SHUNT_VOLTAGE_POS_PEAK	0x0741#define INA209_SHUNT_VOLTAGE_NEG_PEAK	0x0842#define INA209_BUS_VOLTAGE_MAX_PEAK	0x0943#define INA209_BUS_VOLTAGE_MIN_PEAK	0x0a44#define INA209_POWER_PEAK		0x0b45#define INA209_SHUNT_VOLTAGE_POS_WARN	0x0c46#define INA209_SHUNT_VOLTAGE_NEG_WARN	0x0d47#define INA209_POWER_WARN		0x0e48#define INA209_BUS_VOLTAGE_OVER_WARN	0x0f49#define INA209_BUS_VOLTAGE_UNDER_WARN	0x1050#define INA209_POWER_OVER_LIMIT		0x1151#define INA209_BUS_VOLTAGE_OVER_LIMIT	0x1252#define INA209_BUS_VOLTAGE_UNDER_LIMIT	0x1353#define INA209_CRITICAL_DAC_POS		0x1454#define INA209_CRITICAL_DAC_NEG		0x1555#define INA209_CALIBRATION		0x1656 57#define INA209_REGISTERS		0x1758 59#define INA209_CONFIG_DEFAULT		0x3c47	/* PGA=8, full range */60#define INA209_SHUNT_DEFAULT		10000	/* uOhm */61 62struct ina209_data {63	struct i2c_client *client;64 65	struct mutex update_lock;66	bool valid;67	unsigned long last_updated;	/* in jiffies */68 69	u16 regs[INA209_REGISTERS];	/* All chip registers */70 71	u16 config_orig;		/* Original configuration */72	u16 calibration_orig;		/* Original calibration */73	u16 update_interval;74};75 76static struct ina209_data *ina209_update_device(struct device *dev)77{78	struct ina209_data *data = dev_get_drvdata(dev);79	struct i2c_client *client = data->client;80	struct ina209_data *ret = data;81	s32 val;82	int i;83 84	mutex_lock(&data->update_lock);85 86	if (!data->valid ||87	    time_after(jiffies, data->last_updated + data->update_interval)) {88		for (i = 0; i < ARRAY_SIZE(data->regs); i++) {89			val = i2c_smbus_read_word_swapped(client, i);90			if (val < 0) {91				ret = ERR_PTR(val);92				goto abort;93			}94			data->regs[i] = val;95		}96		data->last_updated = jiffies;97		data->valid = true;98	}99abort:100	mutex_unlock(&data->update_lock);101	return ret;102}103 104/*105 * Read a value from a device register and convert it to the106 * appropriate sysfs units107 */108static long ina209_from_reg(const u8 reg, const u16 val)109{110	switch (reg) {111	case INA209_SHUNT_VOLTAGE:112	case INA209_SHUNT_VOLTAGE_POS_PEAK:113	case INA209_SHUNT_VOLTAGE_NEG_PEAK:114	case INA209_SHUNT_VOLTAGE_POS_WARN:115	case INA209_SHUNT_VOLTAGE_NEG_WARN:116		/* LSB=10 uV. Convert to mV. */117		return DIV_ROUND_CLOSEST((s16)val, 100);118 119	case INA209_BUS_VOLTAGE:120	case INA209_BUS_VOLTAGE_MAX_PEAK:121	case INA209_BUS_VOLTAGE_MIN_PEAK:122	case INA209_BUS_VOLTAGE_OVER_WARN:123	case INA209_BUS_VOLTAGE_UNDER_WARN:124	case INA209_BUS_VOLTAGE_OVER_LIMIT:125	case INA209_BUS_VOLTAGE_UNDER_LIMIT:126		/* LSB=4 mV, last 3 bits unused */127		return (val >> 3) * 4;128 129	case INA209_CRITICAL_DAC_POS:130		/* LSB=1 mV, in the upper 8 bits */131		return val >> 8;132 133	case INA209_CRITICAL_DAC_NEG:134		/* LSB=1 mV, in the upper 8 bits */135		return -1 * (val >> 8);136 137	case INA209_POWER:138	case INA209_POWER_PEAK:139	case INA209_POWER_WARN:140	case INA209_POWER_OVER_LIMIT:141		/* LSB=20 mW. Convert to uW */142		return val * 20 * 1000L;143 144	case INA209_CURRENT:145		/* LSB=1 mA (selected). Is in mA */146		return (s16)val;147	}148 149	/* programmer goofed */150	WARN_ON_ONCE(1);151	return 0;152}153 154/*155 * Take a value and convert it to register format, clamping the value156 * to the appropriate range.157 */158static int ina209_to_reg(u8 reg, u16 old, long val)159{160	switch (reg) {161	case INA209_SHUNT_VOLTAGE_POS_WARN:162	case INA209_SHUNT_VOLTAGE_NEG_WARN:163		/* Limit to +- 320 mV, 10 uV LSB */164		return clamp_val(val, -320, 320) * 100;165 166	case INA209_BUS_VOLTAGE_OVER_WARN:167	case INA209_BUS_VOLTAGE_UNDER_WARN:168	case INA209_BUS_VOLTAGE_OVER_LIMIT:169	case INA209_BUS_VOLTAGE_UNDER_LIMIT:170		/*171		 * Limit to 0-32000 mV, 4 mV LSB172		 *173		 * The last three bits aren't part of the value, but we'll174		 * preserve them in their original state.175		 */176		return (DIV_ROUND_CLOSEST(clamp_val(val, 0, 32000), 4) << 3)177		  | (old & 0x7);178 179	case INA209_CRITICAL_DAC_NEG:180		/*181		 * Limit to -255-0 mV, 1 mV LSB182		 * Convert the value to a positive value for the register183		 *184		 * The value lives in the top 8 bits only, be careful185		 * and keep original value of other bits.186		 */187		return (clamp_val(-val, 0, 255) << 8) | (old & 0xff);188 189	case INA209_CRITICAL_DAC_POS:190		/*191		 * Limit to 0-255 mV, 1 mV LSB192		 *193		 * The value lives in the top 8 bits only, be careful194		 * and keep original value of other bits.195		 */196		return (clamp_val(val, 0, 255) << 8) | (old & 0xff);197 198	case INA209_POWER_WARN:199	case INA209_POWER_OVER_LIMIT:200		/* 20 mW LSB */201		return DIV_ROUND_CLOSEST(val, 20 * 1000);202	}203 204	/* Other registers are read-only, return access error */205	return -EACCES;206}207 208static int ina209_interval_from_reg(u16 reg)209{210	return 68 >> (15 - ((reg >> 3) & 0x0f));211}212 213static u16 ina209_reg_from_interval(u16 config, long interval)214{215	int i, adc;216 217	if (interval <= 0) {218		adc = 8;219	} else {220		adc = 15;221		for (i = 34 + 34 / 2; i; i >>= 1) {222			if (i < interval)223				break;224			adc--;225		}226	}227	return (config & 0xf807) | (adc << 3) | (adc << 7);228}229 230static ssize_t ina209_interval_store(struct device *dev,231				     struct device_attribute *da,232				     const char *buf, size_t count)233{234	struct ina209_data *data = ina209_update_device(dev);235	long val;236	u16 regval;237	int ret;238 239	if (IS_ERR(data))240		return PTR_ERR(data);241 242	ret = kstrtol(buf, 10, &val);243	if (ret < 0)244		return ret;245 246	mutex_lock(&data->update_lock);247	regval = ina209_reg_from_interval(data->regs[INA209_CONFIGURATION],248					  val);249	i2c_smbus_write_word_swapped(data->client, INA209_CONFIGURATION,250				     regval);251	data->regs[INA209_CONFIGURATION] = regval;252	data->update_interval = ina209_interval_from_reg(regval);253	mutex_unlock(&data->update_lock);254	return count;255}256 257static ssize_t ina209_interval_show(struct device *dev,258				    struct device_attribute *da, char *buf)259{260	struct ina209_data *data = dev_get_drvdata(dev);261 262	return sysfs_emit(buf, "%d\n", data->update_interval);263}264 265/*266 * History is reset by writing 1 into bit 0 of the respective peak register.267 * Since more than one peak register may be affected by the scope of a268 * reset_history attribute write, use a bit mask in attr->index to identify269 * which registers are affected.270 */271static u16 ina209_reset_history_regs[] = {272	INA209_SHUNT_VOLTAGE_POS_PEAK,273	INA209_SHUNT_VOLTAGE_NEG_PEAK,274	INA209_BUS_VOLTAGE_MAX_PEAK,275	INA209_BUS_VOLTAGE_MIN_PEAK,276	INA209_POWER_PEAK277};278 279static ssize_t ina209_history_store(struct device *dev,280				    struct device_attribute *da,281				    const char *buf, size_t count)282{283	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);284	struct ina209_data *data = dev_get_drvdata(dev);285	struct i2c_client *client = data->client;286	u32 mask = attr->index;287	long val;288	int i, ret;289 290	ret = kstrtol(buf, 10, &val);291	if (ret < 0)292		return ret;293 294	mutex_lock(&data->update_lock);295	for (i = 0; i < ARRAY_SIZE(ina209_reset_history_regs); i++) {296		if (mask & (1 << i))297			i2c_smbus_write_word_swapped(client,298					ina209_reset_history_regs[i], 1);299	}300	data->valid = false;301	mutex_unlock(&data->update_lock);302	return count;303}304 305static ssize_t ina209_value_store(struct device *dev,306				  struct device_attribute *da,307				  const char *buf, size_t count)308{309	struct ina209_data *data = ina209_update_device(dev);310	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);311	int reg = attr->index;312	long val;313	int ret;314 315	if (IS_ERR(data))316		return PTR_ERR(data);317 318	ret = kstrtol(buf, 10, &val);319	if (ret < 0)320		return ret;321 322	mutex_lock(&data->update_lock);323	ret = ina209_to_reg(reg, data->regs[reg], val);324	if (ret < 0) {325		count = ret;326		goto abort;327	}328	i2c_smbus_write_word_swapped(data->client, reg, ret);329	data->regs[reg] = ret;330abort:331	mutex_unlock(&data->update_lock);332	return count;333}334 335static ssize_t ina209_value_show(struct device *dev,336				 struct device_attribute *da, char *buf)337{338	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);339	struct ina209_data *data = ina209_update_device(dev);340	long val;341 342	if (IS_ERR(data))343		return PTR_ERR(data);344 345	val = ina209_from_reg(attr->index, data->regs[attr->index]);346	return sysfs_emit(buf, "%ld\n", val);347}348 349static ssize_t ina209_alarm_show(struct device *dev,350				 struct device_attribute *da, char *buf)351{352	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);353	struct ina209_data *data = ina209_update_device(dev);354	const unsigned int mask = attr->index;355	u16 status;356 357	if (IS_ERR(data))358		return PTR_ERR(data);359 360	status = data->regs[INA209_STATUS];361 362	/*363	 * All alarms are in the INA209_STATUS register. To avoid a long364	 * switch statement, the mask is passed in attr->index365	 */366	return sysfs_emit(buf, "%u\n", !!(status & mask));367}368 369/* Shunt voltage, history, limits, alarms */370static SENSOR_DEVICE_ATTR_RO(in0_input, ina209_value, INA209_SHUNT_VOLTAGE);371static SENSOR_DEVICE_ATTR_RO(in0_input_highest, ina209_value,372			     INA209_SHUNT_VOLTAGE_POS_PEAK);373static SENSOR_DEVICE_ATTR_RO(in0_input_lowest, ina209_value,374			     INA209_SHUNT_VOLTAGE_NEG_PEAK);375static SENSOR_DEVICE_ATTR_WO(in0_reset_history, ina209_history,376			     (1 << 0) | (1 << 1));377static SENSOR_DEVICE_ATTR_RW(in0_max, ina209_value,378			     INA209_SHUNT_VOLTAGE_POS_WARN);379static SENSOR_DEVICE_ATTR_RW(in0_min, ina209_value,380			     INA209_SHUNT_VOLTAGE_NEG_WARN);381static SENSOR_DEVICE_ATTR_RW(in0_crit_max, ina209_value,382			     INA209_CRITICAL_DAC_POS);383static SENSOR_DEVICE_ATTR_RW(in0_crit_min, ina209_value,384			     INA209_CRITICAL_DAC_NEG);385 386static SENSOR_DEVICE_ATTR_RO(in0_min_alarm, ina209_alarm, 1 << 11);387static SENSOR_DEVICE_ATTR_RO(in0_max_alarm, ina209_alarm, 1 << 12);388static SENSOR_DEVICE_ATTR_RO(in0_crit_min_alarm, ina209_alarm, 1 << 6);389static SENSOR_DEVICE_ATTR_RO(in0_crit_max_alarm, ina209_alarm, 1 << 7);390 391/* Bus voltage, history, limits, alarms */392static SENSOR_DEVICE_ATTR_RO(in1_input, ina209_value, INA209_BUS_VOLTAGE);393static SENSOR_DEVICE_ATTR_RO(in1_input_highest, ina209_value,394			     INA209_BUS_VOLTAGE_MAX_PEAK);395static SENSOR_DEVICE_ATTR_RO(in1_input_lowest, ina209_value,396			     INA209_BUS_VOLTAGE_MIN_PEAK);397static SENSOR_DEVICE_ATTR_WO(in1_reset_history, ina209_history,398			     (1 << 2) | (1 << 3));399static SENSOR_DEVICE_ATTR_RW(in1_max, ina209_value,400			     INA209_BUS_VOLTAGE_OVER_WARN);401static SENSOR_DEVICE_ATTR_RW(in1_min, ina209_value,402			     INA209_BUS_VOLTAGE_UNDER_WARN);403static SENSOR_DEVICE_ATTR_RW(in1_crit_max, ina209_value,404			     INA209_BUS_VOLTAGE_OVER_LIMIT);405static SENSOR_DEVICE_ATTR_RW(in1_crit_min, ina209_value,406			     INA209_BUS_VOLTAGE_UNDER_LIMIT);407 408static SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ina209_alarm, 1 << 14);409static SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ina209_alarm, 1 << 15);410static SENSOR_DEVICE_ATTR_RO(in1_crit_min_alarm, ina209_alarm, 1 << 9);411static SENSOR_DEVICE_ATTR_RO(in1_crit_max_alarm, ina209_alarm, 1 << 10);412 413/* Power */414static SENSOR_DEVICE_ATTR_RO(power1_input, ina209_value, INA209_POWER);415static SENSOR_DEVICE_ATTR_RO(power1_input_highest, ina209_value,416			     INA209_POWER_PEAK);417static SENSOR_DEVICE_ATTR_WO(power1_reset_history, ina209_history, 1 << 4);418static SENSOR_DEVICE_ATTR_RW(power1_max, ina209_value, INA209_POWER_WARN);419static SENSOR_DEVICE_ATTR_RW(power1_crit, ina209_value,420			     INA209_POWER_OVER_LIMIT);421 422static SENSOR_DEVICE_ATTR_RO(power1_max_alarm, ina209_alarm, 1 << 13);423static SENSOR_DEVICE_ATTR_RO(power1_crit_alarm, ina209_alarm, 1 << 8);424 425/* Current */426static SENSOR_DEVICE_ATTR_RO(curr1_input, ina209_value, INA209_CURRENT);427 428static SENSOR_DEVICE_ATTR_RW(update_interval, ina209_interval, 0);429 430/*431 * Finally, construct an array of pointers to members of the above objects,432 * as required for sysfs_create_group()433 */434static struct attribute *ina209_attrs[] = {435	&sensor_dev_attr_in0_input.dev_attr.attr,436	&sensor_dev_attr_in0_input_highest.dev_attr.attr,437	&sensor_dev_attr_in0_input_lowest.dev_attr.attr,438	&sensor_dev_attr_in0_reset_history.dev_attr.attr,439	&sensor_dev_attr_in0_max.dev_attr.attr,440	&sensor_dev_attr_in0_min.dev_attr.attr,441	&sensor_dev_attr_in0_crit_max.dev_attr.attr,442	&sensor_dev_attr_in0_crit_min.dev_attr.attr,443	&sensor_dev_attr_in0_max_alarm.dev_attr.attr,444	&sensor_dev_attr_in0_min_alarm.dev_attr.attr,445	&sensor_dev_attr_in0_crit_max_alarm.dev_attr.attr,446	&sensor_dev_attr_in0_crit_min_alarm.dev_attr.attr,447 448	&sensor_dev_attr_in1_input.dev_attr.attr,449	&sensor_dev_attr_in1_input_highest.dev_attr.attr,450	&sensor_dev_attr_in1_input_lowest.dev_attr.attr,451	&sensor_dev_attr_in1_reset_history.dev_attr.attr,452	&sensor_dev_attr_in1_max.dev_attr.attr,453	&sensor_dev_attr_in1_min.dev_attr.attr,454	&sensor_dev_attr_in1_crit_max.dev_attr.attr,455	&sensor_dev_attr_in1_crit_min.dev_attr.attr,456	&sensor_dev_attr_in1_max_alarm.dev_attr.attr,457	&sensor_dev_attr_in1_min_alarm.dev_attr.attr,458	&sensor_dev_attr_in1_crit_max_alarm.dev_attr.attr,459	&sensor_dev_attr_in1_crit_min_alarm.dev_attr.attr,460 461	&sensor_dev_attr_power1_input.dev_attr.attr,462	&sensor_dev_attr_power1_input_highest.dev_attr.attr,463	&sensor_dev_attr_power1_reset_history.dev_attr.attr,464	&sensor_dev_attr_power1_max.dev_attr.attr,465	&sensor_dev_attr_power1_crit.dev_attr.attr,466	&sensor_dev_attr_power1_max_alarm.dev_attr.attr,467	&sensor_dev_attr_power1_crit_alarm.dev_attr.attr,468 469	&sensor_dev_attr_curr1_input.dev_attr.attr,470 471	&sensor_dev_attr_update_interval.dev_attr.attr,472 473	NULL,474};475ATTRIBUTE_GROUPS(ina209);476 477static void ina209_restore_conf(struct i2c_client *client,478				struct ina209_data *data)479{480	/* Restore initial configuration */481	i2c_smbus_write_word_swapped(client, INA209_CONFIGURATION,482				     data->config_orig);483	i2c_smbus_write_word_swapped(client, INA209_CALIBRATION,484				     data->calibration_orig);485}486 487static int ina209_init_client(struct i2c_client *client,488			      struct ina209_data *data)489{490	struct ina2xx_platform_data *pdata = dev_get_platdata(&client->dev);491	u32 shunt;492	int reg;493 494	reg = i2c_smbus_read_word_swapped(client, INA209_CALIBRATION);495	if (reg < 0)496		return reg;497	data->calibration_orig = reg;498 499	reg = i2c_smbus_read_word_swapped(client, INA209_CONFIGURATION);500	if (reg < 0)501		return reg;502	data->config_orig = reg;503 504	if (pdata) {505		if (pdata->shunt_uohms <= 0)506			return -EINVAL;507		shunt = pdata->shunt_uohms;508	} else if (!of_property_read_u32(client->dev.of_node, "shunt-resistor",509					 &shunt)) {510		if (shunt == 0)511			return -EINVAL;512	} else {513		shunt = data->calibration_orig ?514		  40960000 / data->calibration_orig : INA209_SHUNT_DEFAULT;515	}516 517	i2c_smbus_write_word_swapped(client, INA209_CONFIGURATION,518				     INA209_CONFIG_DEFAULT);519	data->update_interval = ina209_interval_from_reg(INA209_CONFIG_DEFAULT);520 521	/*522	 * Calibrate current LSB to 1mA. Shunt is in uOhms.523	 * See equation 13 in datasheet.524	 */525	i2c_smbus_write_word_swapped(client, INA209_CALIBRATION,526				     clamp_val(40960000 / shunt, 1, 65535));527 528	/* Clear status register */529	i2c_smbus_read_word_swapped(client, INA209_STATUS);530 531	return 0;532}533 534static int ina209_probe(struct i2c_client *client)535{536	struct i2c_adapter *adapter = client->adapter;537	struct ina209_data *data;538	struct device *hwmon_dev;539	int ret;540 541	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))542		return -ENODEV;543 544	data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL);545	if (!data)546		return -ENOMEM;547 548	i2c_set_clientdata(client, data);549	data->client = client;550	mutex_init(&data->update_lock);551 552	ret = ina209_init_client(client, data);553	if (ret)554		return ret;555 556	hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev,557							   client->name,558							   data, ina209_groups);559	if (IS_ERR(hwmon_dev)) {560		ret = PTR_ERR(hwmon_dev);561		goto out_restore_conf;562	}563 564	return 0;565 566out_restore_conf:567	ina209_restore_conf(client, data);568	return ret;569}570 571static void ina209_remove(struct i2c_client *client)572{573	struct ina209_data *data = i2c_get_clientdata(client);574 575	ina209_restore_conf(client, data);576}577 578static const struct i2c_device_id ina209_id[] = {579	{ "ina209" },580	{ }581};582MODULE_DEVICE_TABLE(i2c, ina209_id);583 584static const struct of_device_id __maybe_unused ina209_of_match[] = {585	{ .compatible = "ti,ina209" },586	{ },587};588MODULE_DEVICE_TABLE(of, ina209_of_match);589 590/* This is the driver that will be inserted */591static struct i2c_driver ina209_driver = {592	.driver = {593		.name	= "ina209",594		.of_match_table = of_match_ptr(ina209_of_match),595	},596	.probe		= ina209_probe,597	.remove		= ina209_remove,598	.id_table	= ina209_id,599};600 601module_i2c_driver(ina209_driver);602 603MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>, Paul Hays <Paul.Hays@cattail.ca>, Guenter Roeck <linux@roeck-us.net>");604MODULE_DESCRIPTION("INA209 driver");605MODULE_LICENSE("GPL");606