diff options
Diffstat (limited to 'drivers/hwmon/max1619.c')
| -rw-r--r-- | drivers/hwmon/max1619.c | 598 |
1 files changed, 317 insertions, 281 deletions
diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 445e5d40ac82..9b6d03cff4df 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -1,363 +1,399 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * max1619.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring * Copyright (C) 2003-2004 Oleksij Rempel <bug-track@fisher-privat.net> - * Jean Delvare <khali@linux-fr.org> + * Jean Delvare <jdelvare@suse.de> * * Based on the lm90 driver. The MAX1619 is a sensor chip made by Maxim. * It reports up to two temperatures (its own plus up to * one external one). Complete datasheet can be * obtained from Maxim's website at: * http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ - -#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; } -/* - * Functions declaration - */ - -static int max1619_probe(struct i2c_client *client, - const struct i2c_device_id *id); -static int max1619_detect(struct i2c_client *client, - struct i2c_board_info *info); -static void max1619_init_client(struct i2c_client *client); -static int max1619_remove(struct i2c_client *client); -static struct max1619_data *max1619_update_device(struct device *dev); +static u16 update_intervals[] = { 16000, 8000, 4000, 2000, 1000, 500, 250, 125 }; -/* - * Driver data (common to all clients) - */ - -static const struct i2c_device_id max1619_id[] = { - { "max1619", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, max1619_id); - -static struct i2c_driver max1619_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "max1619", - }, - .probe = max1619_probe, - .remove = max1619_remove, - .id_table = max1619_id, - .detect = max1619_detect, - .address_list = normal_i2c, -}; - -/* - * Client data (each client gets its own) - */ - -struct max1619_data { - struct device *hwmon_dev; - struct mutex update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* registers values */ - u8 temp_input1; /* local */ - u8 temp_input2, temp_low2, temp_high2; /* remote */ - u8 temp_crit2; - u8 temp_hyst2; - u8 alarms; -}; - -/* - * Sysfs stuff - */ +static int max1619_chip_read(struct regmap *regmap, u32 attr, long *val) +{ + int alarms, ret; + u32 regval; + + switch (attr) { + case hwmon_chip_update_interval: + ret = regmap_read(regmap, MAX1619_REG_CONVRATE, ®val); + 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; + } + return 0; +} -#define show_temp(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - struct max1619_data *data = max1619_update_device(dev); \ - return sprintf(buf, "%d\n", temp_from_reg(data->value)); \ +static int max1619_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + 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; + } } -show_temp(temp_input1); -show_temp(temp_input2); -show_temp(temp_low2); -show_temp(temp_high2); -show_temp(temp_crit2); -show_temp(temp_hyst2); - -#define set_temp2(value, reg) \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, \ - const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct max1619_data *data = i2c_get_clientdata(client); \ - long val; \ - int err = kstrtol(buf, 10, &val); \ - if (err) \ - return err; \ -\ - mutex_lock(&data->update_lock); \ - data->value = temp_to_reg(val); \ - i2c_smbus_write_byte_data(client, reg, data->value); \ - mutex_unlock(&data->update_lock); \ - return count; \ + +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; + } } -set_temp2(temp_low2, MAX1619_REG_W_REMOTE_LOW); -set_temp2(temp_high2, MAX1619_REG_W_REMOTE_HIGH); -set_temp2(temp_crit2, MAX1619_REG_W_REMOTE_CRIT); -set_temp2(temp_hyst2, MAX1619_REG_W_TCRIT_HYST); +static int max1619_temp_write(struct regmap *regmap, + u32 attr, int channel, long val) +{ + 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 show_alarms(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 show_alarm(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 DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); -static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL); -static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_low2, - set_temp_low2); -static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_high2, - set_temp_high2); -static DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit2, - set_temp_crit2); -static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst2, - set_temp_hyst2); -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); -static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1); -static SENSOR_DEVICE_ATTR(temp2_fault, S_IRUGO, show_alarm, NULL, 2); -static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3); -static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4); - -static struct attribute *max1619_attributes[] = { - &dev_attr_temp1_input.attr, - &dev_attr_temp2_input.attr, - &dev_attr_temp2_min.attr, - &dev_attr_temp2_max.attr, - &dev_attr_temp2_crit.attr, - &dev_attr_temp2_crit_hyst.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 }; -static const struct attribute_group max1619_group = { - .attrs = max1619_attributes, +static const struct hwmon_ops max1619_hwmon_ops = { + .is_visible = max1619_is_visible, + .read = max1619_read, + .write = max1619_write, }; -/* - * Real code - */ +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; - } - strlcpy(info->type, "max1619", I2C_NAME_SIZE); + strscpy(info->type, "max1619", I2C_NAME_SIZE); return 0; } -static int max1619_probe(struct i2c_client *new_client, - const struct i2c_device_id *id) +static int max1619_init_chip(struct regmap *regmap) { - struct max1619_data *data; - int err; - - data = devm_kzalloc(&new_client->dev, sizeof(struct max1619_data), - GFP_KERNEL); - if (!data) - return -ENOMEM; - - i2c_set_clientdata(new_client, data); - data->valid = 0; - mutex_init(&data->update_lock); - - /* Initialize the MAX1619 chip */ - max1619_init_client(new_client); - - /* Register sysfs hooks */ - err = sysfs_create_group(&new_client->dev.kobj, &max1619_group); - if (err) - return err; - - data->hwmon_dev = hwmon_device_register(&new_client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove_files; - } + int ret; - return 0; + ret = regmap_write(regmap, MAX1619_REG_CONVRATE, 5); /* 2 Hz */ + if (ret) + return ret; -exit_remove_files: - sysfs_remove_group(&new_client->dev.kobj, &max1619_group); - return err; + /* Start conversions */ + return regmap_clear_bits(regmap, MAX1619_REG_CONFIG, 0x40); } -static void max1619_init_client(struct i2c_client *client) +/* regmap */ + +static int max1619_reg_read(void *context, unsigned int reg, unsigned int *val) { - 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 = i2c_smbus_read_byte_data(context, reg); + if (ret < 0) + return ret; + + *val = ret; + return 0; } -static int max1619_remove(struct i2c_client *client) +static int max1619_reg_write(void *context, unsigned int reg, unsigned int val) { - struct max1619_data *data = i2c_get_clientdata(client); + int offset = reg < MAX1619_REG_REMOTE_CRIT ? 6 : 2; - hwmon_device_unregister(data->hwmon_dev); - sysfs_remove_group(&client->dev.kobj, &max1619_group); + return i2c_smbus_write_byte_data(context, reg + offset, val); +} - return 0; +static bool max1619_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + return reg <= MAX1619_REG_STATUS; } -static struct max1619_data *max1619_update_device(struct device *dev) +static bool max1619_regmap_is_writeable(struct device *dev, unsigned int reg) { - struct i2c_client *client = to_i2c_client(dev); - struct max1619_data *data = i2c_get_clientdata(client); - - mutex_lock(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - dev_dbg(&client->dev, "Updating max1619 data.\n"); - data->temp_input1 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_LOCAL_TEMP); - data->temp_input2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_REMOTE_TEMP); - data->temp_high2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_REMOTE_HIGH); - data->temp_low2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_REMOTE_LOW); - data->temp_crit2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_REMOTE_CRIT); - data->temp_hyst2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_TCRIT_HYST); - data->alarms = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_STATUS); - - data->last_updated = jiffies; - data->valid = 1; - } + 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; + + regmap = devm_regmap_init(dev, &max1619_regmap_bus, client, + &max1619_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); - mutex_unlock(&data->update_lock); + ret = max1619_init_chip(regmap); + if (ret) + return ret; - return data; + hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, + regmap, &max1619_chip_info, NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); } +static const struct i2c_device_id max1619_id[] = { + { "max1619" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max1619_id); + +#ifdef CONFIG_OF +static const struct of_device_id max1619_of_match[] = { + { .compatible = "maxim,max1619", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, max1619_of_match); +#endif + +static struct i2c_driver max1619_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "max1619", + .of_match_table = of_match_ptr(max1619_of_match), + }, + .probe = max1619_probe, + .id_table = max1619_id, + .detect = max1619_detect, + .address_list = normal_i2c, +}; + module_i2c_driver(max1619_driver); -MODULE_AUTHOR("Oleksij Rempel <bug-track@fisher-privat.net> and " - "Jean Delvare <khali@linux-fr.org>"); +MODULE_AUTHOR("Oleksij Rempel <bug-track@fisher-privat.net>, Jean Delvare <jdelvare@suse.de>"); MODULE_DESCRIPTION("MAX1619 sensor driver"); MODULE_LICENSE("GPL"); |
