18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Driver for Linear Technology LTC4215 I2C Hot Swap Controller
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2009 Ira W. Snyder <iws@ovro.caltech.edu>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Datasheet:
88c2ecf20Sopenharmony_ci * http://www.linear.com/pc/downloadDocument.do?navId=H0,C1,C1003,C1006,C1163,P17572,D12697
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/i2c.h>
178c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
188c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
198c2ecf20Sopenharmony_ci#include <linux/jiffies.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/* Here are names of the chip's registers (a.k.a. commands) */
228c2ecf20Sopenharmony_cienum ltc4215_cmd {
238c2ecf20Sopenharmony_ci	LTC4215_CONTROL			= 0x00, /* rw */
248c2ecf20Sopenharmony_ci	LTC4215_ALERT			= 0x01, /* rw */
258c2ecf20Sopenharmony_ci	LTC4215_STATUS			= 0x02, /* ro */
268c2ecf20Sopenharmony_ci	LTC4215_FAULT			= 0x03, /* rw */
278c2ecf20Sopenharmony_ci	LTC4215_SENSE			= 0x04, /* rw */
288c2ecf20Sopenharmony_ci	LTC4215_SOURCE			= 0x05, /* rw */
298c2ecf20Sopenharmony_ci	LTC4215_ADIN			= 0x06, /* rw */
308c2ecf20Sopenharmony_ci};
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistruct ltc4215_data {
338c2ecf20Sopenharmony_ci	struct i2c_client *client;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	struct mutex update_lock;
368c2ecf20Sopenharmony_ci	bool valid;
378c2ecf20Sopenharmony_ci	unsigned long last_updated; /* in jiffies */
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	/* Registers */
408c2ecf20Sopenharmony_ci	u8 regs[7];
418c2ecf20Sopenharmony_ci};
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic struct ltc4215_data *ltc4215_update_device(struct device *dev)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct ltc4215_data *data = dev_get_drvdata(dev);
468c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
478c2ecf20Sopenharmony_ci	s32 val;
488c2ecf20Sopenharmony_ci	int i;
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	/* The chip's A/D updates 10 times per second */
538c2ecf20Sopenharmony_ci	if (time_after(jiffies, data->last_updated + HZ / 10) || !data->valid) {
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci		dev_dbg(&client->dev, "Starting ltc4215 update\n");
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci		/* Read all registers */
588c2ecf20Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(data->regs); i++) {
598c2ecf20Sopenharmony_ci			val = i2c_smbus_read_byte_data(client, i);
608c2ecf20Sopenharmony_ci			if (unlikely(val < 0))
618c2ecf20Sopenharmony_ci				data->regs[i] = 0;
628c2ecf20Sopenharmony_ci			else
638c2ecf20Sopenharmony_ci				data->regs[i] = val;
648c2ecf20Sopenharmony_ci		}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		data->last_updated = jiffies;
678c2ecf20Sopenharmony_ci		data->valid = 1;
688c2ecf20Sopenharmony_ci	}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	return data;
738c2ecf20Sopenharmony_ci}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci/* Return the voltage from the given register in millivolts */
768c2ecf20Sopenharmony_cistatic int ltc4215_get_voltage(struct device *dev, u8 reg)
778c2ecf20Sopenharmony_ci{
788c2ecf20Sopenharmony_ci	struct ltc4215_data *data = ltc4215_update_device(dev);
798c2ecf20Sopenharmony_ci	const u8 regval = data->regs[reg];
808c2ecf20Sopenharmony_ci	u32 voltage = 0;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	switch (reg) {
838c2ecf20Sopenharmony_ci	case LTC4215_SENSE:
848c2ecf20Sopenharmony_ci		/* 151 uV per increment */
858c2ecf20Sopenharmony_ci		voltage = regval * 151 / 1000;
868c2ecf20Sopenharmony_ci		break;
878c2ecf20Sopenharmony_ci	case LTC4215_SOURCE:
888c2ecf20Sopenharmony_ci		/* 60.5 mV per increment */
898c2ecf20Sopenharmony_ci		voltage = regval * 605 / 10;
908c2ecf20Sopenharmony_ci		break;
918c2ecf20Sopenharmony_ci	case LTC4215_ADIN:
928c2ecf20Sopenharmony_ci		/*
938c2ecf20Sopenharmony_ci		 * The ADIN input is divided by 12.5, and has 4.82 mV
948c2ecf20Sopenharmony_ci		 * per increment, so we have the additional multiply
958c2ecf20Sopenharmony_ci		 */
968c2ecf20Sopenharmony_ci		voltage = regval * 482 * 125 / 1000;
978c2ecf20Sopenharmony_ci		break;
988c2ecf20Sopenharmony_ci	default:
998c2ecf20Sopenharmony_ci		/* If we get here, the developer messed up */
1008c2ecf20Sopenharmony_ci		WARN_ON_ONCE(1);
1018c2ecf20Sopenharmony_ci		break;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return voltage;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci/* Return the current from the sense resistor in mA */
1088c2ecf20Sopenharmony_cistatic unsigned int ltc4215_get_current(struct device *dev)
1098c2ecf20Sopenharmony_ci{
1108c2ecf20Sopenharmony_ci	struct ltc4215_data *data = ltc4215_update_device(dev);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	/*
1138c2ecf20Sopenharmony_ci	 * The strange looking conversions that follow are fixed-point
1148c2ecf20Sopenharmony_ci	 * math, since we cannot do floating point in the kernel.
1158c2ecf20Sopenharmony_ci	 *
1168c2ecf20Sopenharmony_ci	 * Step 1: convert sense register to microVolts
1178c2ecf20Sopenharmony_ci	 * Step 2: convert voltage to milliAmperes
1188c2ecf20Sopenharmony_ci	 *
1198c2ecf20Sopenharmony_ci	 * If you play around with the V=IR equation, you come up with
1208c2ecf20Sopenharmony_ci	 * the following: X uV / Y mOhm == Z mA
1218c2ecf20Sopenharmony_ci	 *
1228c2ecf20Sopenharmony_ci	 * With the resistors that are fractions of a milliOhm, we multiply
1238c2ecf20Sopenharmony_ci	 * the voltage and resistance by 10, to shift the decimal point.
1248c2ecf20Sopenharmony_ci	 * Now we can use the normal division operator again.
1258c2ecf20Sopenharmony_ci	 */
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	/* Calculate voltage in microVolts (151 uV per increment) */
1288c2ecf20Sopenharmony_ci	const unsigned int voltage = data->regs[LTC4215_SENSE] * 151;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	/* Calculate current in milliAmperes (4 milliOhm sense resistor) */
1318c2ecf20Sopenharmony_ci	const unsigned int curr = voltage / 4;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	return curr;
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic ssize_t ltc4215_voltage_show(struct device *dev,
1378c2ecf20Sopenharmony_ci				    struct device_attribute *da, char *buf)
1388c2ecf20Sopenharmony_ci{
1398c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1408c2ecf20Sopenharmony_ci	const int voltage = ltc4215_get_voltage(dev, attr->index);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%d\n", voltage);
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic ssize_t ltc4215_current_show(struct device *dev,
1468c2ecf20Sopenharmony_ci				    struct device_attribute *da, char *buf)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	const unsigned int curr = ltc4215_get_current(dev);
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%u\n", curr);
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic ssize_t ltc4215_power_show(struct device *dev,
1548c2ecf20Sopenharmony_ci				  struct device_attribute *da, char *buf)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	const unsigned int curr = ltc4215_get_current(dev);
1578c2ecf20Sopenharmony_ci	const int output_voltage = ltc4215_get_voltage(dev, LTC4215_ADIN);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	/* current in mA * voltage in mV == power in uW */
1608c2ecf20Sopenharmony_ci	const unsigned int power = abs(output_voltage * curr);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%u\n", power);
1638c2ecf20Sopenharmony_ci}
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_cistatic ssize_t ltc4215_alarm_show(struct device *dev,
1668c2ecf20Sopenharmony_ci				  struct device_attribute *da, char *buf)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
1698c2ecf20Sopenharmony_ci	struct ltc4215_data *data = ltc4215_update_device(dev);
1708c2ecf20Sopenharmony_ci	const u8 reg = data->regs[LTC4215_STATUS];
1718c2ecf20Sopenharmony_ci	const u32 mask = attr->index;
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return snprintf(buf, PAGE_SIZE, "%u\n", !!(reg & mask));
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci/*
1778c2ecf20Sopenharmony_ci * These macros are used below in constructing device attribute objects
1788c2ecf20Sopenharmony_ci * for use with sysfs_create_group() to make a sysfs device file
1798c2ecf20Sopenharmony_ci * for each register.
1808c2ecf20Sopenharmony_ci */
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/* Construct a sensor_device_attribute structure for each register */
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci/* Current */
1858c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(curr1_input, ltc4215_current, 0);
1868c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(curr1_max_alarm, ltc4215_alarm, 1 << 2);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci/* Power (virtual) */
1898c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(power1_input, ltc4215_power, 0);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/* Input Voltage */
1928c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_input, ltc4215_voltage, LTC4215_ADIN);
1938c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_max_alarm, ltc4215_alarm, 1 << 0);
1948c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in1_min_alarm, ltc4215_alarm, 1 << 1);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/* Output Voltage */
1978c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_input, ltc4215_voltage, LTC4215_SOURCE);
1988c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(in2_min_alarm, ltc4215_alarm, 1 << 3);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci/*
2018c2ecf20Sopenharmony_ci * Finally, construct an array of pointers to members of the above objects,
2028c2ecf20Sopenharmony_ci * as required for sysfs_create_group()
2038c2ecf20Sopenharmony_ci */
2048c2ecf20Sopenharmony_cistatic struct attribute *ltc4215_attrs[] = {
2058c2ecf20Sopenharmony_ci	&sensor_dev_attr_curr1_input.dev_attr.attr,
2068c2ecf20Sopenharmony_ci	&sensor_dev_attr_curr1_max_alarm.dev_attr.attr,
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	&sensor_dev_attr_power1_input.dev_attr.attr,
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	&sensor_dev_attr_in1_input.dev_attr.attr,
2118c2ecf20Sopenharmony_ci	&sensor_dev_attr_in1_max_alarm.dev_attr.attr,
2128c2ecf20Sopenharmony_ci	&sensor_dev_attr_in1_min_alarm.dev_attr.attr,
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	&sensor_dev_attr_in2_input.dev_attr.attr,
2158c2ecf20Sopenharmony_ci	&sensor_dev_attr_in2_min_alarm.dev_attr.attr,
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	NULL,
2188c2ecf20Sopenharmony_ci};
2198c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(ltc4215);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic int ltc4215_probe(struct i2c_client *client)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	struct i2c_adapter *adapter = client->adapter;
2248c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
2258c2ecf20Sopenharmony_ci	struct ltc4215_data *data;
2268c2ecf20Sopenharmony_ci	struct device *hwmon_dev;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
2298c2ecf20Sopenharmony_ci		return -ENODEV;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
2328c2ecf20Sopenharmony_ci	if (!data)
2338c2ecf20Sopenharmony_ci		return -ENOMEM;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	data->client = client;
2368c2ecf20Sopenharmony_ci	mutex_init(&data->update_lock);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	/* Initialize the LTC4215 chip */
2398c2ecf20Sopenharmony_ci	i2c_smbus_write_byte_data(client, LTC4215_FAULT, 0x00);
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
2428c2ecf20Sopenharmony_ci							   data,
2438c2ecf20Sopenharmony_ci							   ltc4215_groups);
2448c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(hwmon_dev);
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic const struct i2c_device_id ltc4215_id[] = {
2488c2ecf20Sopenharmony_ci	{ "ltc4215", 0 },
2498c2ecf20Sopenharmony_ci	{ }
2508c2ecf20Sopenharmony_ci};
2518c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, ltc4215_id);
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci/* This is the driver that will be inserted */
2548c2ecf20Sopenharmony_cistatic struct i2c_driver ltc4215_driver = {
2558c2ecf20Sopenharmony_ci	.driver = {
2568c2ecf20Sopenharmony_ci		.name	= "ltc4215",
2578c2ecf20Sopenharmony_ci	},
2588c2ecf20Sopenharmony_ci	.probe_new	= ltc4215_probe,
2598c2ecf20Sopenharmony_ci	.id_table	= ltc4215_id,
2608c2ecf20Sopenharmony_ci};
2618c2ecf20Sopenharmony_ci
2628c2ecf20Sopenharmony_cimodule_i2c_driver(ltc4215_driver);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ciMODULE_AUTHOR("Ira W. Snyder <iws@ovro.caltech.edu>");
2658c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("LTC4215 driver");
2668c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
267