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

ltc4215.c (7199B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Driver for Linear Technology LTC4215 I2C Hot Swap Controller
      4 *
      5 * Copyright (C) 2009 Ira W. Snyder <iws@ovro.caltech.edu>
      6 *
      7 * Datasheet:
      8 * http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1163,P17572,D12697
      9 */
     10
     11#include <linux/kernel.h>
     12#include <linux/module.h>
     13#include <linux/init.h>
     14#include <linux/err.h>
     15#include <linux/slab.h>
     16#include <linux/i2c.h>
     17#include <linux/hwmon.h>
     18#include <linux/hwmon-sysfs.h>
     19#include <linux/jiffies.h>
     20
     21/* Here are names of the chip's registers (a.k.a. commands) */
     22enum ltc4215_cmd {
     23	LTC4215_CONTROL			= 0x00, /* rw */
     24	LTC4215_ALERT			= 0x01, /* rw */
     25	LTC4215_STATUS			= 0x02, /* ro */
     26	LTC4215_FAULT			= 0x03, /* rw */
     27	LTC4215_SENSE			= 0x04, /* rw */
     28	LTC4215_SOURCE			= 0x05, /* rw */
     29	LTC4215_ADIN			= 0x06, /* rw */
     30};
     31
     32struct ltc4215_data {
     33	struct i2c_client *client;
     34
     35	struct mutex update_lock;
     36	bool valid;
     37	unsigned long last_updated; /* in jiffies */
     38
     39	/* Registers */
     40	u8 regs[7];
     41};
     42
     43static struct ltc4215_data *ltc4215_update_device(struct device *dev)
     44{
     45	struct ltc4215_data *data = dev_get_drvdata(dev);
     46	struct i2c_client *client = data->client;
     47	s32 val;
     48	int i;
     49
     50	mutex_lock(&data->update_lock);
     51
     52	/* The chip's A/D updates 10 times per second */
     53	if (time_after(jiffies, data->last_updated + HZ / 10) || !data->valid) {
     54
     55		dev_dbg(&client->dev, "Starting ltc4215 update\n");
     56
     57		/* Read all registers */
     58		for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
     59			val = i2c_smbus_read_byte_data(client, i);
     60			if (unlikely(val < 0))
     61				data->regs[i] = 0;
     62			else
     63				data->regs[i] = val;
     64		}
     65
     66		data->last_updated = jiffies;
     67		data->valid = true;
     68	}
     69
     70	mutex_unlock(&data->update_lock);
     71
     72	return data;
     73}
     74
     75/* Return the voltage from the given register in millivolts */
     76static int ltc4215_get_voltage(struct device *dev, u8 reg)
     77{
     78	struct ltc4215_data *data = ltc4215_update_device(dev);
     79	const u8 regval = data->regs[reg];
     80	u32 voltage = 0;
     81
     82	switch (reg) {
     83	case LTC4215_SENSE:
     84		/* 151 uV per increment */
     85		voltage = regval * 151 / 1000;
     86		break;
     87	case LTC4215_SOURCE:
     88		/* 60.5 mV per increment */
     89		voltage = regval * 605 / 10;
     90		break;
     91	case LTC4215_ADIN:
     92		/*
     93		 * The ADIN input is divided by 12.5, and has 4.82 mV
     94		 * per increment, so we have the additional multiply
     95		 */
     96		voltage = regval * 482 * 125 / 1000;
     97		break;
     98	default:
     99		/* If we get here, the developer messed up */
    100		WARN_ON_ONCE(1);
    101		break;
    102	}
    103
    104	return voltage;
    105}
    106
    107/* Return the current from the sense resistor in mA */
    108static unsigned int ltc4215_get_current(struct device *dev)
    109{
    110	struct ltc4215_data *data = ltc4215_update_device(dev);
    111
    112	/*
    113	 * The strange looking conversions that follow are fixed-point
    114	 * math, since we cannot do floating point in the kernel.
    115	 *
    116	 * Step 1: convert sense register to microVolts
    117	 * Step 2: convert voltage to milliAmperes
    118	 *
    119	 * If you play around with the V=IR equation, you come up with
    120	 * the following: X uV / Y mOhm == Z mA
    121	 *
    122	 * With the resistors that are fractions of a milliOhm, we multiply
    123	 * the voltage and resistance by 10, to shift the decimal point.
    124	 * Now we can use the normal division operator again.
    125	 */
    126
    127	/* Calculate voltage in microVolts (151 uV per increment) */
    128	const unsigned int voltage = data->regs[LTC4215_SENSE] * 151;
    129
    130	/* Calculate current in milliAmperes (4 milliOhm sense resistor) */
    131	const unsigned int curr = voltage / 4;
    132
    133	return curr;
    134}
    135
    136static ssize_t ltc4215_voltage_show(struct device *dev,
    137				    struct device_attribute *da, char *buf)
    138{
    139	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
    140	const int voltage = ltc4215_get_voltage(dev, attr->index);
    141
    142	return sysfs_emit(buf, "%d\n", voltage);
    143}
    144
    145static ssize_t ltc4215_current_show(struct device *dev,
    146				    struct device_attribute *da, char *buf)
    147{
    148	const unsigned int curr = ltc4215_get_current(dev);
    149
    150	return sysfs_emit(buf, "%u\n", curr);
    151}
    152
    153static ssize_t ltc4215_power_show(struct device *dev,
    154				  struct device_attribute *da, char *buf)
    155{
    156	const unsigned int curr = ltc4215_get_current(dev);
    157	const int output_voltage = ltc4215_get_voltage(dev, LTC4215_ADIN);
    158
    159	/* current in mA * voltage in mV == power in uW */
    160	const unsigned int power = abs(output_voltage * curr);
    161
    162	return sysfs_emit(buf, "%u\n", power);
    163}
    164
    165static ssize_t ltc4215_alarm_show(struct device *dev,
    166				  struct device_attribute *da, char *buf)
    167{
    168	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
    169	struct ltc4215_data *data = ltc4215_update_device(dev);
    170	const u8 reg = data->regs[LTC4215_STATUS];
    171	const u32 mask = attr->index;
    172
    173	return sysfs_emit(buf, "%u\n", !!(reg & mask));
    174}
    175
    176/*
    177 * These macros are used below in constructing device attribute objects
    178 * for use with sysfs_create_group() to make a sysfs device file
    179 * for each register.
    180 */
    181
    182/* Construct a sensor_device_attribute structure for each register */
    183
    184/* Current */
    185static SENSOR_DEVICE_ATTR_RO(curr1_input, ltc4215_current, 0);
    186static SENSOR_DEVICE_ATTR_RO(curr1_max_alarm, ltc4215_alarm, 1 << 2);
    187
    188/* Power (virtual) */
    189static SENSOR_DEVICE_ATTR_RO(power1_input, ltc4215_power, 0);
    190
    191/* Input Voltage */
    192static SENSOR_DEVICE_ATTR_RO(in1_input, ltc4215_voltage, LTC4215_ADIN);
    193static SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ltc4215_alarm, 1 << 0);
    194static SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ltc4215_alarm, 1 << 1);
    195
    196/* Output Voltage */
    197static SENSOR_DEVICE_ATTR_RO(in2_input, ltc4215_voltage, LTC4215_SOURCE);
    198static SENSOR_DEVICE_ATTR_RO(in2_min_alarm, ltc4215_alarm, 1 << 3);
    199
    200/*
    201 * Finally, construct an array of pointers to members of the above objects,
    202 * as required for sysfs_create_group()
    203 */
    204static struct attribute *ltc4215_attrs[] = {
    205	&sensor_dev_attr_curr1_input.dev_attr.attr,
    206	&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
    207
    208	&sensor_dev_attr_power1_input.dev_attr.attr,
    209
    210	&sensor_dev_attr_in1_input.dev_attr.attr,
    211	&sensor_dev_attr_in1_max_alarm.dev_attr.attr,
    212	&sensor_dev_attr_in1_min_alarm.dev_attr.attr,
    213
    214	&sensor_dev_attr_in2_input.dev_attr.attr,
    215	&sensor_dev_attr_in2_min_alarm.dev_attr.attr,
    216
    217	NULL,
    218};
    219ATTRIBUTE_GROUPS(ltc4215);
    220
    221static int ltc4215_probe(struct i2c_client *client)
    222{
    223	struct i2c_adapter *adapter = client->adapter;
    224	struct device *dev = &client->dev;
    225	struct ltc4215_data *data;
    226	struct device *hwmon_dev;
    227
    228	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
    229		return -ENODEV;
    230
    231	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
    232	if (!data)
    233		return -ENOMEM;
    234
    235	data->client = client;
    236	mutex_init(&data->update_lock);
    237
    238	/* Initialize the LTC4215 chip */
    239	i2c_smbus_write_byte_data(client, LTC4215_FAULT, 0x00);
    240
    241	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
    242							   data,
    243							   ltc4215_groups);
    244	return PTR_ERR_OR_ZERO(hwmon_dev);
    245}
    246
    247static const struct i2c_device_id ltc4215_id[] = {
    248	{ "ltc4215", 0 },
    249	{ }
    250};
    251MODULE_DEVICE_TABLE(i2c, ltc4215_id);
    252
    253/* This is the driver that will be inserted */
    254static struct i2c_driver ltc4215_driver = {
    255	.driver = {
    256		.name	= "ltc4215",
    257	},
    258	.probe_new	= ltc4215_probe,
    259	.id_table	= ltc4215_id,
    260};
    261
    262module_i2c_driver(ltc4215_driver);
    263
    264MODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
    265MODULE_DESCRIPTION("LTC4215 driver");
    266MODULE_LICENSE("GPL");