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

ltc4261.c (6415B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Driver for Linear Technology LTC4261 I2C Negative Voltage Hot Swap Controller
      4 *
      5 * Copyright (C) 2010 Ericsson AB.
      6 *
      7 * Derived from:
      8 *
      9 *  Driver for Linear Technology LTC4245 I2C Multiple Supply Hot Swap Controller
     10 *  Copyright (C) 2008 Ira W. Snyder <iws@ovro.caltech.edu>
     11 *
     12 * Datasheet: http://cds.linear.com/docs/Datasheet/42612fb.pdf
     13 */
     14
     15#include <linux/kernel.h>
     16#include <linux/module.h>
     17#include <linux/init.h>
     18#include <linux/err.h>
     19#include <linux/slab.h>
     20#include <linux/i2c.h>
     21#include <linux/hwmon.h>
     22#include <linux/hwmon-sysfs.h>
     23#include <linux/jiffies.h>
     24
     25/* chip registers */
     26#define LTC4261_STATUS	0x00	/* readonly */
     27#define LTC4261_FAULT	0x01
     28#define LTC4261_ALERT	0x02
     29#define LTC4261_CONTROL	0x03
     30#define LTC4261_SENSE_H	0x04
     31#define LTC4261_SENSE_L	0x05
     32#define LTC4261_ADIN2_H	0x06
     33#define LTC4261_ADIN2_L	0x07
     34#define LTC4261_ADIN_H	0x08
     35#define LTC4261_ADIN_L	0x09
     36
     37/*
     38 * Fault register bits
     39 */
     40#define FAULT_OV	(1<<0)
     41#define FAULT_UV	(1<<1)
     42#define FAULT_OC	(1<<2)
     43
     44struct ltc4261_data {
     45	struct i2c_client *client;
     46
     47	struct mutex update_lock;
     48	bool valid;
     49	unsigned long last_updated;	/* in jiffies */
     50
     51	/* Registers */
     52	u8 regs[10];
     53};
     54
     55static struct ltc4261_data *ltc4261_update_device(struct device *dev)
     56{
     57	struct ltc4261_data *data = dev_get_drvdata(dev);
     58	struct i2c_client *client = data->client;
     59	struct ltc4261_data *ret = data;
     60
     61	mutex_lock(&data->update_lock);
     62
     63	if (time_after(jiffies, data->last_updated + HZ / 4) || !data->valid) {
     64		int i;
     65
     66		/* Read registers -- 0x00 to 0x09 */
     67		for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
     68			int val;
     69
     70			val = i2c_smbus_read_byte_data(client, i);
     71			if (unlikely(val < 0)) {
     72				dev_dbg(dev,
     73					"Failed to read ADC value: error %d\n",
     74					val);
     75				ret = ERR_PTR(val);
     76				data->valid = false;
     77				goto abort;
     78			}
     79			data->regs[i] = val;
     80		}
     81		data->last_updated = jiffies;
     82		data->valid = true;
     83	}
     84abort:
     85	mutex_unlock(&data->update_lock);
     86	return ret;
     87}
     88
     89/* Return the voltage from the given register in mV or mA */
     90static int ltc4261_get_value(struct ltc4261_data *data, u8 reg)
     91{
     92	u32 val;
     93
     94	val = (data->regs[reg] << 2) + (data->regs[reg + 1] >> 6);
     95
     96	switch (reg) {
     97	case LTC4261_ADIN_H:
     98	case LTC4261_ADIN2_H:
     99		/* 2.5mV resolution. Convert to mV. */
    100		val = val * 25 / 10;
    101		break;
    102	case LTC4261_SENSE_H:
    103		/*
    104		 * 62.5uV resolution. Convert to current as measured with
    105		 * an 1 mOhm sense resistor, in mA. If a different sense
    106		 * resistor is installed, calculate the actual current by
    107		 * dividing the reported current by the sense resistor value
    108		 * in mOhm.
    109		 */
    110		val = val * 625 / 10;
    111		break;
    112	default:
    113		/* If we get here, the developer messed up */
    114		WARN_ON_ONCE(1);
    115		val = 0;
    116		break;
    117	}
    118
    119	return val;
    120}
    121
    122static ssize_t ltc4261_value_show(struct device *dev,
    123				  struct device_attribute *da, char *buf)
    124{
    125	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
    126	struct ltc4261_data *data = ltc4261_update_device(dev);
    127	int value;
    128
    129	if (IS_ERR(data))
    130		return PTR_ERR(data);
    131
    132	value = ltc4261_get_value(data, attr->index);
    133	return sysfs_emit(buf, "%d\n", value);
    134}
    135
    136static ssize_t ltc4261_bool_show(struct device *dev,
    137				 struct device_attribute *da, char *buf)
    138{
    139	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
    140	struct ltc4261_data *data = ltc4261_update_device(dev);
    141	u8 fault;
    142
    143	if (IS_ERR(data))
    144		return PTR_ERR(data);
    145
    146	fault = data->regs[LTC4261_FAULT] & attr->index;
    147	if (fault)		/* Clear reported faults in chip register */
    148		i2c_smbus_write_byte_data(data->client, LTC4261_FAULT, ~fault);
    149
    150	return sysfs_emit(buf, "%d\n", fault ? 1 : 0);
    151}
    152
    153/*
    154 * Input voltages.
    155 */
    156static SENSOR_DEVICE_ATTR_RO(in1_input, ltc4261_value, LTC4261_ADIN_H);
    157static SENSOR_DEVICE_ATTR_RO(in2_input, ltc4261_value, LTC4261_ADIN2_H);
    158
    159/*
    160 * Voltage alarms. The chip has only one set of voltage alarm status bits,
    161 * triggered by input voltage alarms. In many designs, those alarms are
    162 * associated with the ADIN2 sensor, due to the proximity of the ADIN2 pin
    163 * to the OV pin. ADIN2 is, however, not available on all chip variants.
    164 * To ensure that the alarm condition is reported to the user, report it
    165 * with both voltage sensors.
    166 */
    167static SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ltc4261_bool, FAULT_UV);
    168static SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ltc4261_bool, FAULT_OV);
    169static SENSOR_DEVICE_ATTR_RO(in2_min_alarm, ltc4261_bool, FAULT_UV);
    170static SENSOR_DEVICE_ATTR_RO(in2_max_alarm, ltc4261_bool, FAULT_OV);
    171
    172/* Currents (via sense resistor) */
    173static SENSOR_DEVICE_ATTR_RO(curr1_input, ltc4261_value, LTC4261_SENSE_H);
    174
    175/* Overcurrent alarm */
    176static SENSOR_DEVICE_ATTR_RO(curr1_max_alarm, ltc4261_bool, FAULT_OC);
    177
    178static struct attribute *ltc4261_attrs[] = {
    179	&sensor_dev_attr_in1_input.dev_attr.attr,
    180	&sensor_dev_attr_in1_min_alarm.dev_attr.attr,
    181	&sensor_dev_attr_in1_max_alarm.dev_attr.attr,
    182	&sensor_dev_attr_in2_input.dev_attr.attr,
    183	&sensor_dev_attr_in2_min_alarm.dev_attr.attr,
    184	&sensor_dev_attr_in2_max_alarm.dev_attr.attr,
    185
    186	&sensor_dev_attr_curr1_input.dev_attr.attr,
    187	&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
    188
    189	NULL,
    190};
    191ATTRIBUTE_GROUPS(ltc4261);
    192
    193static int ltc4261_probe(struct i2c_client *client)
    194{
    195	struct i2c_adapter *adapter = client->adapter;
    196	struct device *dev = &client->dev;
    197	struct ltc4261_data *data;
    198	struct device *hwmon_dev;
    199
    200	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
    201		return -ENODEV;
    202
    203	if (i2c_smbus_read_byte_data(client, LTC4261_STATUS) < 0) {
    204		dev_err(dev, "Failed to read status register\n");
    205		return -ENODEV;
    206	}
    207
    208	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
    209	if (!data)
    210		return -ENOMEM;
    211
    212	data->client = client;
    213	mutex_init(&data->update_lock);
    214
    215	/* Clear faults */
    216	i2c_smbus_write_byte_data(client, LTC4261_FAULT, 0x00);
    217
    218	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
    219							   data,
    220							   ltc4261_groups);
    221	return PTR_ERR_OR_ZERO(hwmon_dev);
    222}
    223
    224static const struct i2c_device_id ltc4261_id[] = {
    225	{"ltc4261", 0},
    226	{}
    227};
    228
    229MODULE_DEVICE_TABLE(i2c, ltc4261_id);
    230
    231/* This is the driver that will be inserted */
    232static struct i2c_driver ltc4261_driver = {
    233	.driver = {
    234		   .name = "ltc4261",
    235		   },
    236	.probe_new = ltc4261_probe,
    237	.id_table = ltc4261_id,
    238};
    239
    240module_i2c_driver(ltc4261_driver);
    241
    242MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>");
    243MODULE_DESCRIPTION("LTC4261 driver");
    244MODULE_LICENSE("GPL");