summaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/max1619.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/hwmon/max1619.c')
-rw-r--r--drivers/hwmon/max1619.c499
1 files changed, 290 insertions, 209 deletions
diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c
index a89a519cf5d9..9b6d03cff4df 100644
--- a/drivers/hwmon/max1619.c
+++ b/drivers/hwmon/max1619.c
@@ -12,275 +12,356 @@
* http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf
*/
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/jiffies.h>
-#include <linux/i2c.h>
-#include <linux/hwmon.h>
-#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
-#include <linux/mutex.h>
-#include <linux/sysfs.h>
+#include <linux/hwmon.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/util_macros.h>
static const unsigned short normal_i2c[] = {
0x18, 0x19, 0x1a, 0x29, 0x2a, 0x2b, 0x4c, 0x4d, 0x4e, I2C_CLIENT_END };
-/*
- * The MAX1619 registers
- */
+#define MAX1619_REG_LOCAL_TEMP 0x00
+#define MAX1619_REG_REMOTE_TEMP 0x01
+#define MAX1619_REG_STATUS 0x02
+#define MAX1619_REG_CONFIG 0x03
+#define MAX1619_REG_CONVRATE 0x04
+#define MAX1619_REG_REMOTE_HIGH 0x07
+#define MAX1619_REG_REMOTE_LOW 0x08
+#define MAX1619_REG_REMOTE_CRIT 0x10
+#define MAX1619_REG_REMOTE_CRIT_HYST 0x11
+#define MAX1619_REG_MAN_ID 0xFE
+#define MAX1619_REG_CHIP_ID 0xFF
+
+static int get_alarms(struct regmap *regmap)
+{
+ static u32 regs[2] = { MAX1619_REG_STATUS, MAX1619_REG_CONFIG };
+ u8 regdata[2];
+ int ret;
-#define MAX1619_REG_R_MAN_ID 0xFE
-#define MAX1619_REG_R_CHIP_ID 0xFF
-#define MAX1619_REG_R_CONFIG 0x03
-#define MAX1619_REG_W_CONFIG 0x09
-#define MAX1619_REG_R_CONVRATE 0x04
-#define MAX1619_REG_W_CONVRATE 0x0A
-#define MAX1619_REG_R_STATUS 0x02
-#define MAX1619_REG_R_LOCAL_TEMP 0x00
-#define MAX1619_REG_R_REMOTE_TEMP 0x01
-#define MAX1619_REG_R_REMOTE_HIGH 0x07
-#define MAX1619_REG_W_REMOTE_HIGH 0x0D
-#define MAX1619_REG_R_REMOTE_LOW 0x08
-#define MAX1619_REG_W_REMOTE_LOW 0x0E
-#define MAX1619_REG_R_REMOTE_CRIT 0x10
-#define MAX1619_REG_W_REMOTE_CRIT 0x12
-#define MAX1619_REG_R_TCRIT_HYST 0x11
-#define MAX1619_REG_W_TCRIT_HYST 0x13
+ ret = regmap_multi_reg_read(regmap, regs, regdata, 2);
+ if (ret)
+ return ret;
-/*
- * Conversions
- */
+ /* OVERT status bit may be reversed */
+ if (!(regdata[1] & 0x20))
+ regdata[0] ^= 0x02;
-static int temp_from_reg(int val)
-{
- return (val & 0x80 ? val-0x100 : val) * 1000;
+ return regdata[0] & 0x1e;
}
-static int temp_to_reg(int val)
+static int max1619_temp_read(struct regmap *regmap, u32 attr, int channel, long *val)
{
- return (val < 0 ? val+0x100*1000 : val) / 1000;
+ int reg = -1, alarm_bit = 0;
+ u32 temp;
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ reg = channel ? MAX1619_REG_REMOTE_TEMP : MAX1619_REG_LOCAL_TEMP;
+ break;
+ case hwmon_temp_min:
+ reg = MAX1619_REG_REMOTE_LOW;
+ break;
+ case hwmon_temp_max:
+ reg = MAX1619_REG_REMOTE_HIGH;
+ break;
+ case hwmon_temp_crit:
+ reg = MAX1619_REG_REMOTE_CRIT;
+ break;
+ case hwmon_temp_crit_hyst:
+ reg = MAX1619_REG_REMOTE_CRIT_HYST;
+ break;
+ case hwmon_temp_min_alarm:
+ alarm_bit = 3;
+ break;
+ case hwmon_temp_max_alarm:
+ alarm_bit = 4;
+ break;
+ case hwmon_temp_crit_alarm:
+ alarm_bit = 1;
+ break;
+ case hwmon_temp_fault:
+ alarm_bit = 2;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ if (reg >= 0) {
+ ret = regmap_read(regmap, reg, &temp);
+ if (ret < 0)
+ return ret;
+ *val = sign_extend32(temp, 7) * 1000;
+ } else {
+ ret = get_alarms(regmap);
+ if (ret < 0)
+ return ret;
+ *val = !!(ret & BIT(alarm_bit));
+ }
+ return 0;
}
-enum temp_index {
- t_input1 = 0,
- t_input2,
- t_low2,
- t_high2,
- t_crit2,
- t_hyst2,
- t_num_regs
-};
-
-/*
- * Client data (each client gets its own)
- */
-
-struct max1619_data {
- struct i2c_client *client;
- struct mutex update_lock;
- bool valid; /* false until following fields are valid */
- unsigned long last_updated; /* in jiffies */
-
- /* registers values */
- u8 temp[t_num_regs]; /* index with enum temp_index */
- u8 alarms;
-};
+static u16 update_intervals[] = { 16000, 8000, 4000, 2000, 1000, 500, 250, 125 };
-static const u8 regs_read[t_num_regs] = {
- [t_input1] = MAX1619_REG_R_LOCAL_TEMP,
- [t_input2] = MAX1619_REG_R_REMOTE_TEMP,
- [t_low2] = MAX1619_REG_R_REMOTE_LOW,
- [t_high2] = MAX1619_REG_R_REMOTE_HIGH,
- [t_crit2] = MAX1619_REG_R_REMOTE_CRIT,
- [t_hyst2] = MAX1619_REG_R_TCRIT_HYST,
-};
-
-static const u8 regs_write[t_num_regs] = {
- [t_low2] = MAX1619_REG_W_REMOTE_LOW,
- [t_high2] = MAX1619_REG_W_REMOTE_HIGH,
- [t_crit2] = MAX1619_REG_W_REMOTE_CRIT,
- [t_hyst2] = MAX1619_REG_W_TCRIT_HYST,
-};
-
-static struct max1619_data *max1619_update_device(struct device *dev)
+static int max1619_chip_read(struct regmap *regmap, u32 attr, long *val)
{
- struct max1619_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- int config, i;
-
- mutex_lock(&data->update_lock);
-
- if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) {
- dev_dbg(&client->dev, "Updating max1619 data.\n");
- for (i = 0; i < t_num_regs; i++)
- data->temp[i] = i2c_smbus_read_byte_data(client,
- regs_read[i]);
- data->alarms = i2c_smbus_read_byte_data(client,
- MAX1619_REG_R_STATUS);
- /* If OVERT polarity is low, reverse alarm bit */
- config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
- if (!(config & 0x20))
- data->alarms ^= 0x02;
-
- data->last_updated = jiffies;
- data->valid = true;
+ int alarms, ret;
+ u32 regval;
+
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ ret = regmap_read(regmap, MAX1619_REG_CONVRATE, &regval);
+ if (ret < 0)
+ return ret;
+ *val = update_intervals[regval & 7];
+ break;
+ case hwmon_chip_alarms:
+ alarms = get_alarms(regmap);
+ if (alarms < 0)
+ return alarms;
+ *val = alarms;
+ break;
+ default:
+ return -EOPNOTSUPP;
}
-
- mutex_unlock(&data->update_lock);
-
- return data;
+ return 0;
}
-/*
- * Sysfs stuff
- */
-
-static ssize_t temp_show(struct device *dev, struct device_attribute *devattr,
- char *buf)
+static int max1619_read(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct max1619_data *data = max1619_update_device(dev);
+ struct regmap *regmap = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_chip:
+ return max1619_chip_read(regmap, attr, val);
+ case hwmon_temp:
+ return max1619_temp_read(regmap, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
- return sprintf(buf, "%d\n", temp_from_reg(data->temp[attr->index]));
+static int max1619_chip_write(struct regmap *regmap, u32 attr, long val)
+{
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ val = find_closest_descending(val, update_intervals, ARRAY_SIZE(update_intervals));
+ return regmap_write(regmap, MAX1619_REG_CONVRATE, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t temp_store(struct device *dev,
- struct device_attribute *devattr, const char *buf,
- size_t count)
+static int max1619_temp_write(struct regmap *regmap,
+ u32 attr, int channel, long val)
{
- struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
- struct max1619_data *data = dev_get_drvdata(dev);
- struct i2c_client *client = data->client;
- long val;
- int err = kstrtol(buf, 10, &val);
- if (err)
- return err;
-
- mutex_lock(&data->update_lock);
- data->temp[attr->index] = temp_to_reg(val);
- i2c_smbus_write_byte_data(client, regs_write[attr->index],
- data->temp[attr->index]);
- mutex_unlock(&data->update_lock);
- return count;
+ int reg;
+
+ switch (attr) {
+ case hwmon_temp_min:
+ reg = MAX1619_REG_REMOTE_LOW;
+ break;
+ case hwmon_temp_max:
+ reg = MAX1619_REG_REMOTE_HIGH;
+ break;
+ case hwmon_temp_crit:
+ reg = MAX1619_REG_REMOTE_CRIT;
+ break;
+ case hwmon_temp_crit_hyst:
+ reg = MAX1619_REG_REMOTE_CRIT_HYST;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+ val = DIV_ROUND_CLOSEST(clamp_val(val, -128000, 127000), 1000);
+ return regmap_write(regmap, reg, val);
}
-static ssize_t alarms_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+static int max1619_write(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, long val)
{
- struct max1619_data *data = max1619_update_device(dev);
- return sprintf(buf, "%d\n", data->alarms);
+ struct regmap *regmap = dev_get_drvdata(dev);
+
+ switch (type) {
+ case hwmon_chip:
+ return max1619_chip_write(regmap, attr, val);
+ case hwmon_temp:
+ return max1619_temp_write(regmap, attr, channel, val);
+ default:
+ return -EOPNOTSUPP;
+ }
}
-static ssize_t alarm_show(struct device *dev, struct device_attribute *attr,
- char *buf)
+static umode_t max1619_is_visible(const void *_data, enum hwmon_sensor_types type,
+ u32 attr, int channel)
{
- int bitnr = to_sensor_dev_attr(attr)->index;
- struct max1619_data *data = max1619_update_device(dev);
- return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
+ switch (type) {
+ case hwmon_chip:
+ switch (attr) {
+ case hwmon_chip_update_interval:
+ return 0644;
+ case hwmon_chip_alarms:
+ return 0444;
+ default:
+ break;
+ }
+ break;
+ case hwmon_temp:
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ case hwmon_temp_min:
+ case hwmon_temp_max:
+ case hwmon_temp_crit:
+ case hwmon_temp_crit_hyst:
+ return 0644;
+ case hwmon_temp_min_alarm:
+ case hwmon_temp_max_alarm:
+ case hwmon_temp_crit_alarm:
+ case hwmon_temp_fault:
+ return 0444;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
}
-static SENSOR_DEVICE_ATTR_RO(temp1_input, temp, t_input1);
-static SENSOR_DEVICE_ATTR_RO(temp2_input, temp, t_input2);
-static SENSOR_DEVICE_ATTR_RW(temp2_min, temp, t_low2);
-static SENSOR_DEVICE_ATTR_RW(temp2_max, temp, t_high2);
-static SENSOR_DEVICE_ATTR_RW(temp2_crit, temp, t_crit2);
-static SENSOR_DEVICE_ATTR_RW(temp2_crit_hyst, temp, t_hyst2);
-
-static DEVICE_ATTR_RO(alarms);
-static SENSOR_DEVICE_ATTR_RO(temp2_crit_alarm, alarm, 1);
-static SENSOR_DEVICE_ATTR_RO(temp2_fault, alarm, 2);
-static SENSOR_DEVICE_ATTR_RO(temp2_min_alarm, alarm, 3);
-static SENSOR_DEVICE_ATTR_RO(temp2_max_alarm, alarm, 4);
-
-static struct attribute *max1619_attrs[] = {
- &sensor_dev_attr_temp1_input.dev_attr.attr,
- &sensor_dev_attr_temp2_input.dev_attr.attr,
- &sensor_dev_attr_temp2_min.dev_attr.attr,
- &sensor_dev_attr_temp2_max.dev_attr.attr,
- &sensor_dev_attr_temp2_crit.dev_attr.attr,
- &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
-
- &dev_attr_alarms.attr,
- &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_fault.dev_attr.attr,
- &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
- &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+static const struct hwmon_channel_info * const max1619_info[] = {
+ HWMON_CHANNEL_INFO(chip, HWMON_C_ALARMS | HWMON_C_UPDATE_INTERVAL),
+ HWMON_CHANNEL_INFO(temp,
+ HWMON_T_INPUT,
+ HWMON_T_INPUT | HWMON_T_MIN | HWMON_T_MAX |
+ HWMON_T_CRIT | HWMON_T_CRIT_HYST |
+ HWMON_T_MIN_ALARM | HWMON_T_MAX_ALARM |
+ HWMON_T_CRIT_ALARM | HWMON_T_FAULT),
NULL
};
-ATTRIBUTE_GROUPS(max1619);
+
+static const struct hwmon_ops max1619_hwmon_ops = {
+ .is_visible = max1619_is_visible,
+ .read = max1619_read,
+ .write = max1619_write,
+};
+
+static const struct hwmon_chip_info max1619_chip_info = {
+ .ops = &max1619_hwmon_ops,
+ .info = max1619_info,
+};
/* Return 0 if detection is successful, -ENODEV otherwise */
static int max1619_detect(struct i2c_client *client,
struct i2c_board_info *info)
{
struct i2c_adapter *adapter = client->adapter;
- u8 reg_config, reg_convrate, reg_status, man_id, chip_id;
+ int regval;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -ENODEV;
- /* detection */
- reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
- reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONVRATE);
- reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_R_STATUS);
- if ((reg_config & 0x03) != 0x00
- || reg_convrate > 0x07 || (reg_status & 0x61) != 0x00) {
- dev_dbg(&adapter->dev, "MAX1619 detection failed at 0x%02x\n",
- client->addr);
+ regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONFIG);
+ if (regval < 0 || (regval & 0x03))
+ return -ENODEV;
+ regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CONVRATE);
+ if (regval < 0 || regval > 0x07)
+ return -ENODEV;
+ regval = i2c_smbus_read_byte_data(client, MAX1619_REG_STATUS);
+ if (regval < 0 || (regval & 0x61))
return -ENODEV;
- }
- /* identification */
- man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_MAN_ID);
- chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CHIP_ID);
- if (man_id != 0x4D || chip_id != 0x04) {
- dev_info(&adapter->dev,
- "Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n",
- man_id, chip_id);
+ regval = i2c_smbus_read_byte_data(client, MAX1619_REG_MAN_ID);
+ if (regval != 0x4d)
+ return -ENODEV;
+ regval = i2c_smbus_read_byte_data(client, MAX1619_REG_CHIP_ID);
+ if (regval != 0x04)
return -ENODEV;
- }
strscpy(info->type, "max1619", I2C_NAME_SIZE);
return 0;
}
-static void max1619_init_client(struct i2c_client *client)
+static int max1619_init_chip(struct regmap *regmap)
{
- u8 config;
-
- /*
- * Start the conversions.
- */
- i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE,
- 5); /* 2 Hz */
- config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG);
- if (config & 0x40)
- i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG,
- config & 0xBF); /* run */
+ int ret;
+
+ ret = regmap_write(regmap, MAX1619_REG_CONVRATE, 5); /* 2 Hz */
+ if (ret)
+ return ret;
+
+ /* Start conversions */
+ return regmap_clear_bits(regmap, MAX1619_REG_CONFIG, 0x40);
}
-static int max1619_probe(struct i2c_client *new_client)
+/* regmap */
+
+static int max1619_reg_read(void *context, unsigned int reg, unsigned int *val)
{
- struct max1619_data *data;
- struct device *hwmon_dev;
+ int ret;
- data = devm_kzalloc(&new_client->dev, sizeof(struct max1619_data),
- GFP_KERNEL);
- if (!data)
- return -ENOMEM;
+ ret = i2c_smbus_read_byte_data(context, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+ return 0;
+}
+
+static int max1619_reg_write(void *context, unsigned int reg, unsigned int val)
+{
+ int offset = reg < MAX1619_REG_REMOTE_CRIT ? 6 : 2;
+
+ return i2c_smbus_write_byte_data(context, reg + offset, val);
+}
+
+static bool max1619_regmap_is_volatile(struct device *dev, unsigned int reg)
+{
+ return reg <= MAX1619_REG_STATUS;
+}
+
+static bool max1619_regmap_is_writeable(struct device *dev, unsigned int reg)
+{
+ return reg > MAX1619_REG_STATUS && reg <= MAX1619_REG_REMOTE_CRIT_HYST;
+}
+
+static const struct regmap_config max1619_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MAX1619_REG_REMOTE_CRIT_HYST,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = max1619_regmap_is_volatile,
+ .writeable_reg = max1619_regmap_is_writeable,
+};
+
+static const struct regmap_bus max1619_regmap_bus = {
+ .reg_write = max1619_reg_write,
+ .reg_read = max1619_reg_read,
+};
+
+static int max1619_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct device *hwmon_dev;
+ struct regmap *regmap;
+ int ret;
- data->client = new_client;
- mutex_init(&data->update_lock);
+ regmap = devm_regmap_init(dev, &max1619_regmap_bus, client,
+ &max1619_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
- /* Initialize the MAX1619 chip */
- max1619_init_client(new_client);
+ ret = max1619_init_chip(regmap);
+ if (ret)
+ return ret;
- hwmon_dev = devm_hwmon_device_register_with_groups(&new_client->dev,
- new_client->name,
- data,
- max1619_groups);
+ hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name,
+ regmap, &max1619_chip_info, NULL);
return PTR_ERR_OR_ZERO(hwmon_dev);
}