diff options
Diffstat (limited to 'drivers/hwmon')
46 files changed, 2606 insertions, 1386 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 650dd71f9724..2ca5668bdb62 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -246,6 +246,16 @@ config SENSORS_ADT7475 This driver can also be built as a module. If so, the module will be called adt7475. +config SENSORS_AS370 + tristate "Synaptics AS370 SoC hardware monitoring driver" + help + If you say yes here you get support for the PVT sensors of + the Synaptics AS370 SoC + + This driver can also be built as a module. If so, the module + will be called as370-hwmon. + + config SENSORS_ASC7621 tristate "Andigilog aSC7621" depends on I2C @@ -1382,8 +1392,8 @@ config SENSORS_SHTC1 tristate "Sensiron humidity and temperature sensors. SHTC1 and compat." depends on I2C help - If you say yes here you get support for the Sensiron SHTC1 and SHTW1 - humidity and temperature sensors. + If you say yes here you get support for the Sensiron SHTC1, SHTW1, + and SHTC3 humidity and temperature sensors. This driver can also be built as a module. If so, the module will be called shtc1. @@ -1570,16 +1580,6 @@ config SENSORS_ADC128D818 This driver can also be built as a module. If so, the module will be called adc128d818. -config SENSORS_ADS1015 - tristate "Texas Instruments ADS1015" - depends on I2C - help - If you say yes here you get support for Texas Instruments - ADS1015/ADS1115 12/16-bit 4-input ADC device. - - This driver can also be built as a module. If so, the module - will be called ads1015. - config SENSORS_ADS7828 tristate "Texas Instruments ADS7828 and compatibles" depends on I2C @@ -1834,17 +1834,12 @@ config SENSORS_W83795 will be called w83795. config SENSORS_W83795_FANCTRL - bool "Include automatic fan control support (DANGEROUS)" + bool "Include automatic fan control support" depends on SENSORS_W83795 help If you say yes here, support for automatic fan speed control will be included in the driver. - This part of the code wasn't carefully reviewed and tested yet, - so enabling this option is strongly discouraged on production - servers. Only developers and testers should enable it for the - time being. - Please also note that this option will create sysfs attribute files which may change in the future, so you shouldn't rely on them being stable. diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 8db472ea04f0..c86ce4d3d36b 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -35,7 +35,6 @@ obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o -obj-$(CONFIG_SENSORS_ADS1015) += ads1015.o obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o obj-$(CONFIG_SENSORS_ADS7871) += ads7871.o obj-$(CONFIG_SENSORS_ADT7X10) += adt7x10.o @@ -48,6 +47,7 @@ obj-$(CONFIG_SENSORS_ADT7475) += adt7475.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o obj-$(CONFIG_SENSORS_ARM_SCMI) += scmi-hwmon.o obj-$(CONFIG_SENSORS_ARM_SCPI) += scpi-hwmon.o +obj-$(CONFIG_SENSORS_AS370) += as370-hwmon.o obj-$(CONFIG_SENSORS_ASC7621) += asc7621.o obj-$(CONFIG_SENSORS_ASPEED) += aspeed-pwm-tacho.o obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o diff --git a/drivers/hwmon/acpi_power_meter.c b/drivers/hwmon/acpi_power_meter.c index 6ba1a08253f0..4cf25458f0b9 100644 --- a/drivers/hwmon/acpi_power_meter.c +++ b/drivers/hwmon/acpi_power_meter.c @@ -681,8 +681,8 @@ static int setup_attrs(struct acpi_power_meter_resource *resource) if (resource->caps.flags & POWER_METER_CAN_CAP) { if (!can_cap_in_hardware()) { - dev_err(&resource->acpi_dev->dev, - "Ignoring unsafe software power cap!\n"); + dev_warn(&resource->acpi_dev->dev, + "Ignoring unsafe software power cap!\n"); goto skip_unsafe_cap; } diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index 388060ff85e7..f7752a5bef31 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -10,16 +10,6 @@ * Very rare chip please let me know if you use it * * http://www.analog.com/UploadedFiles/Data_Sheets/ADM1029.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 version 2 of the License - * - * 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. */ #include <linux/module.h> diff --git a/drivers/hwmon/ads1015.c b/drivers/hwmon/ads1015.c deleted file mode 100644 index 3727a3762eb8..000000000000 --- a/drivers/hwmon/ads1015.c +++ /dev/null @@ -1,324 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * ads1015.c - lm_sensors driver for ads1015 12-bit 4-input ADC - * (C) Copyright 2010 - * Dirk Eibach, Guntermann & Drunck GmbH <eibach@gdsys.de> - * - * Based on the ads7828 driver by Steve Hardy. - * - * Datasheet available at: http://focus.ti.com/lit/ds/symlink/ads1015.pdf - */ - -#include <linux/module.h> -#include <linux/init.h> -#include <linux/slab.h> -#include <linux/delay.h> -#include <linux/i2c.h> -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/err.h> -#include <linux/mutex.h> -#include <linux/of_device.h> -#include <linux/of.h> - -#include <linux/platform_data/ads1015.h> - -/* ADS1015 registers */ -enum { - ADS1015_CONVERSION = 0, - ADS1015_CONFIG = 1, -}; - -/* PGA fullscale voltages in mV */ -static const unsigned int fullscale_table[8] = { - 6144, 4096, 2048, 1024, 512, 256, 256, 256 }; - -/* Data rates in samples per second */ -static const unsigned int data_rate_table_1015[8] = { - 128, 250, 490, 920, 1600, 2400, 3300, 3300 -}; - -static const unsigned int data_rate_table_1115[8] = { - 8, 16, 32, 64, 128, 250, 475, 860 -}; - -#define ADS1015_DEFAULT_CHANNELS 0xff -#define ADS1015_DEFAULT_PGA 2 -#define ADS1015_DEFAULT_DATA_RATE 4 - -enum ads1015_chips { - ads1015, - ads1115, -}; - -struct ads1015_data { - struct device *hwmon_dev; - struct mutex update_lock; /* mutex protect updates */ - struct ads1015_channel_data channel_data[ADS1015_CHANNELS]; - enum ads1015_chips id; -}; - -static int ads1015_read_adc(struct i2c_client *client, unsigned int channel) -{ - u16 config; - struct ads1015_data *data = i2c_get_clientdata(client); - unsigned int pga = data->channel_data[channel].pga; - unsigned int data_rate = data->channel_data[channel].data_rate; - unsigned int conversion_time_ms; - const unsigned int * const rate_table = data->id == ads1115 ? - data_rate_table_1115 : data_rate_table_1015; - int res; - - mutex_lock(&data->update_lock); - - /* get channel parameters */ - res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG); - if (res < 0) - goto err_unlock; - config = res; - conversion_time_ms = DIV_ROUND_UP(1000, rate_table[data_rate]); - - /* setup and start single conversion */ - config &= 0x001f; - config |= (1 << 15) | (1 << 8); - config |= (channel & 0x0007) << 12; - config |= (pga & 0x0007) << 9; - config |= (data_rate & 0x0007) << 5; - - res = i2c_smbus_write_word_swapped(client, ADS1015_CONFIG, config); - if (res < 0) - goto err_unlock; - - /* wait until conversion finished */ - msleep(conversion_time_ms); - res = i2c_smbus_read_word_swapped(client, ADS1015_CONFIG); - if (res < 0) - goto err_unlock; - config = res; - if (!(config & (1 << 15))) { - /* conversion not finished in time */ - res = -EIO; - goto err_unlock; - } - - res = i2c_smbus_read_word_swapped(client, ADS1015_CONVERSION); - -err_unlock: - mutex_unlock(&data->update_lock); - return res; -} - -static int ads1015_reg_to_mv(struct i2c_client *client, unsigned int channel, - s16 reg) -{ - struct ads1015_data *data = i2c_get_clientdata(client); - unsigned int pga = data->channel_data[channel].pga; - int fullscale = fullscale_table[pga]; - const int mask = data->id == ads1115 ? 0x7fff : 0x7ff0; - - return DIV_ROUND_CLOSEST(reg * fullscale, mask); -} - -/* sysfs callback function */ -static ssize_t in_show(struct device *dev, struct device_attribute *da, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(da); - struct i2c_client *client = to_i2c_client(dev); - int res; - int index = attr->index; - - res = ads1015_read_adc(client, index); - if (res < 0) - return res; - - return sprintf(buf, "%d\n", ads1015_reg_to_mv(client, index, res)); -} - -static const struct sensor_device_attribute ads1015_in[] = { - SENSOR_ATTR_RO(in0_input, in, 0), - SENSOR_ATTR_RO(in1_input, in, 1), - SENSOR_ATTR_RO(in2_input, in, 2), - SENSOR_ATTR_RO(in3_input, in, 3), - SENSOR_ATTR_RO(in4_input, in, 4), - SENSOR_ATTR_RO(in5_input, in, 5), - SENSOR_ATTR_RO(in6_input, in, 6), - SENSOR_ATTR_RO(in7_input, in, 7), -}; - -/* - * Driver interface - */ - -static int ads1015_remove(struct i2c_client *client) -{ - struct ads1015_data *data = i2c_get_clientdata(client); - int k; - - hwmon_device_unregister(data->hwmon_dev); - for (k = 0; k < ADS1015_CHANNELS; ++k) - device_remove_file(&client->dev, &ads1015_in[k].dev_attr); - return 0; -} - -#ifdef CONFIG_OF -static int ads1015_get_channels_config_of(struct i2c_client *client) -{ - struct ads1015_data *data = i2c_get_clientdata(client); - struct device_node *node; - - if (!client->dev.of_node - || !of_get_next_child(client->dev.of_node, NULL)) - return -EINVAL; - - for_each_child_of_node(client->dev.of_node, node) { - u32 pval; - unsigned int channel; - unsigned int pga = ADS1015_DEFAULT_PGA; - unsigned int data_rate = ADS1015_DEFAULT_DATA_RATE; - - if (of_property_read_u32(node, "reg", &pval)) { - dev_err(&client->dev, "invalid reg on %pOF\n", node); - continue; - } - - channel = pval; - if (channel >= ADS1015_CHANNELS) { - dev_err(&client->dev, - "invalid channel index %d on %pOF\n", - channel, node); - continue; - } - - if (!of_property_read_u32(node, "ti,gain", &pval)) { - pga = pval; - if (pga > 6) { - dev_err(&client->dev, "invalid gain on %pOF\n", - node); - return -EINVAL; - } - } - - if (!of_property_read_u32(node, "ti,datarate", &pval)) { - data_rate = pval; - if (data_rate > 7) { - dev_err(&client->dev, - "invalid data_rate on %pOF\n", node); - return -EINVAL; - } - } - - data->channel_data[channel].enabled = true; - data->channel_data[channel].pga = pga; - data->channel_data[channel].data_rate = data_rate; - } - - return 0; -} -#endif - -static void ads1015_get_channels_config(struct i2c_client *client) -{ - unsigned int k; - struct ads1015_data *data = i2c_get_clientdata(client); - struct ads1015_platform_data *pdata = dev_get_platdata(&client->dev); - - /* prefer platform data */ - if (pdata) { - memcpy(data->channel_data, pdata->channel_data, - sizeof(data->channel_data)); - return; - } - -#ifdef CONFIG_OF - if (!ads1015_get_channels_config_of(client)) - return; -#endif - - /* fallback on default configuration */ - for (k = 0; k < ADS1015_CHANNELS; ++k) { - data->channel_data[k].enabled = true; - data->channel_data[k].pga = ADS1015_DEFAULT_PGA; - data->channel_data[k].data_rate = ADS1015_DEFAULT_DATA_RATE; - } -} - -static int ads1015_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct ads1015_data *data; - int err; - unsigned int k; - - data = devm_kzalloc(&client->dev, sizeof(struct ads1015_data), - GFP_KERNEL); - if (!data) - return -ENOMEM; - - if (client->dev.of_node) - data->id = (enum ads1015_chips) - of_device_get_match_data(&client->dev); - else - data->id = id->driver_data; - i2c_set_clientdata(client, data); - mutex_init(&data->update_lock); - - /* build sysfs attribute group */ - ads1015_get_channels_config(client); - for (k = 0; k < ADS1015_CHANNELS; ++k) { - if (!data->channel_data[k].enabled) - continue; - err = device_create_file(&client->dev, &ads1015_in[k].dev_attr); - if (err) - goto exit_remove; - } - - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } - - return 0; - -exit_remove: - for (k = 0; k < ADS1015_CHANNELS; ++k) - device_remove_file(&client->dev, &ads1015_in[k].dev_attr); - return err; -} - -static const struct i2c_device_id ads1015_id[] = { - { "ads1015", ads1015}, - { "ads1115", ads1115}, - { } -}; -MODULE_DEVICE_TABLE(i2c, ads1015_id); - -static const struct of_device_id __maybe_unused ads1015_of_match[] = { - { - .compatible = "ti,ads1015", - .data = (void *)ads1015 - }, - { - .compatible = "ti,ads1115", - .data = (void *)ads1115 - }, - { }, -}; -MODULE_DEVICE_TABLE(of, ads1015_of_match); - -static struct i2c_driver ads1015_driver = { - .driver = { - .name = "ads1015", - .of_match_table = of_match_ptr(ads1015_of_match), - }, - .probe = ads1015_probe, - .remove = ads1015_remove, - .id_table = ads1015_id, -}; - -module_i2c_driver(ads1015_driver); - -MODULE_AUTHOR("Dirk Eibach <eibach@gdsys.de>"); -MODULE_DESCRIPTION("ADS1015 driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index c3c6031a7285..6c64d50c9aae 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -187,7 +187,7 @@ static const struct of_device_id __maybe_unused adt7475_of_match[] = { MODULE_DEVICE_TABLE(of, adt7475_of_match); struct adt7475_data { - struct device *hwmon_dev; + struct i2c_client *client; struct mutex lock; unsigned long measure_updated; @@ -212,6 +212,7 @@ struct adt7475_data { u8 vid; u8 vrm; + const struct attribute_group *groups[9]; }; static struct i2c_driver adt7475_driver; @@ -346,8 +347,8 @@ static ssize_t voltage_store(struct device *dev, { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned char reg; long val; @@ -440,8 +441,8 @@ static ssize_t temp_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned char reg = 0; u8 out; int temp; @@ -542,8 +543,7 @@ static ssize_t temp_st_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); long val; switch (sattr->index) { @@ -570,8 +570,8 @@ static ssize_t temp_st_store(struct device *dev, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned char reg; int shift, idx; ulong val; @@ -647,8 +647,8 @@ static ssize_t point2_show(struct device *dev, struct device_attribute *attr, static ssize_t point2_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); int temp; long val; @@ -710,8 +710,8 @@ static ssize_t tach_store(struct device *dev, struct device_attribute *attr, { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned long val; if (kstrtoul(buf, 10, &val)) @@ -769,8 +769,8 @@ static ssize_t pwm_store(struct device *dev, struct device_attribute *attr, { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; unsigned char reg = 0; long val; @@ -818,8 +818,8 @@ static ssize_t stall_disable_show(struct device *dev, struct device_attribute *attr, char *buf) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + u8 mask = BIT(5 + sattr->index); return sprintf(buf, "%d\n", !!(data->enh_acoustics[0] & mask)); @@ -830,8 +830,8 @@ static ssize_t stall_disable_store(struct device *dev, const char *buf, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; long val; u8 mask = BIT(5 + sattr->index); @@ -914,8 +914,8 @@ static ssize_t pwmchan_store(struct device *dev, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int r; long val; @@ -938,8 +938,8 @@ static ssize_t pwmctrl_store(struct device *dev, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int r; long val; @@ -982,8 +982,8 @@ static ssize_t pwmfreq_store(struct device *dev, size_t count) { struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; int out; long val; @@ -1022,8 +1022,8 @@ static ssize_t pwm_use_point2_pwm_at_crit_store(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; long val; if (kstrtol(buf, 10, &val)) @@ -1342,26 +1342,6 @@ static int adt7475_detect(struct i2c_client *client, return 0; } -static void adt7475_remove_files(struct i2c_client *client, - struct adt7475_data *data) -{ - sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group); - if (data->has_fan4) - sysfs_remove_group(&client->dev.kobj, &fan4_attr_group); - if (data->has_pwm2) - sysfs_remove_group(&client->dev.kobj, &pwm2_attr_group); - if (data->has_voltage & (1 << 0)) - sysfs_remove_group(&client->dev.kobj, &in0_attr_group); - if (data->has_voltage & (1 << 3)) - sysfs_remove_group(&client->dev.kobj, &in3_attr_group); - if (data->has_voltage & (1 << 4)) - sysfs_remove_group(&client->dev.kobj, &in4_attr_group); - if (data->has_voltage & (1 << 5)) - sysfs_remove_group(&client->dev.kobj, &in5_attr_group); - if (data->has_vid) - sysfs_remove_group(&client->dev.kobj, &vid_attr_group); -} - static int adt7475_update_limits(struct i2c_client *client) { struct adt7475_data *data = i2c_get_clientdata(client); @@ -1489,7 +1469,8 @@ static int adt7475_probe(struct i2c_client *client, }; struct adt7475_data *data; - int i, ret = 0, revision; + struct device *hwmon_dev; + int i, ret = 0, revision, group_num = 0; u8 config2, config3; data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); @@ -1497,6 +1478,7 @@ static int adt7475_probe(struct i2c_client *client, return -ENOMEM; mutex_init(&data->lock); + data->client = client; i2c_set_clientdata(client, data); if (client->dev.of_node) @@ -1590,52 +1572,40 @@ static int adt7475_probe(struct i2c_client *client, break; } - ret = sysfs_create_group(&client->dev.kobj, &adt7475_attr_group); - if (ret) - return ret; + data->groups[group_num++] = &adt7475_attr_group; /* Features that can be disabled individually */ if (data->has_fan4) { - ret = sysfs_create_group(&client->dev.kobj, &fan4_attr_group); - if (ret) - goto eremove; + data->groups[group_num++] = &fan4_attr_group; } if (data->has_pwm2) { - ret = sysfs_create_group(&client->dev.kobj, &pwm2_attr_group); - if (ret) - goto eremove; + data->groups[group_num++] = &pwm2_attr_group; } if (data->has_voltage & (1 << 0)) { - ret = sysfs_create_group(&client->dev.kobj, &in0_attr_group); - if (ret) - goto eremove; + data->groups[group_num++] = &in0_attr_group; } if (data->has_voltage & (1 << 3)) { - ret = sysfs_create_group(&client->dev.kobj, &in3_attr_group); - if (ret) - goto eremove; + data->groups[group_num++] = &in3_attr_group; } if (data->has_voltage & (1 << 4)) { - ret = sysfs_create_group(&client->dev.kobj, &in4_attr_group); - if (ret) - goto eremove; + data->groups[group_num++] = &in4_attr_group; } if (data->has_voltage & (1 << 5)) { - ret = sysfs_create_group(&client->dev.kobj, &in5_attr_group); - if (ret) - goto eremove; + data->groups[group_num++] = &in5_attr_group; } if (data->has_vid) { data->vrm = vid_which_vrm(); - ret = sysfs_create_group(&client->dev.kobj, &vid_attr_group); - if (ret) - goto eremove; + data->groups[group_num] = &vid_attr_group; } - data->hwmon_dev = hwmon_device_register(&client->dev); - if (IS_ERR(data->hwmon_dev)) { - ret = PTR_ERR(data->hwmon_dev); - goto eremove; + /* register device with all the acquired attributes */ + hwmon_dev = devm_hwmon_device_register_with_groups(&client->dev, + client->name, data, + data->groups); + + if (IS_ERR(hwmon_dev)) { + ret = PTR_ERR(hwmon_dev); + return ret; } dev_info(&client->dev, "%s device, revision %d\n", @@ -1657,21 +1627,7 @@ static int adt7475_probe(struct i2c_client *client, /* Limits and settings, should never change update more than once */ ret = adt7475_update_limits(client); if (ret) - goto eremove; - - return 0; - -eremove: - adt7475_remove_files(client, data); - return ret; -} - -static int adt7475_remove(struct i2c_client *client) -{ - struct adt7475_data *data = i2c_get_clientdata(client); - - hwmon_device_unregister(data->hwmon_dev); - adt7475_remove_files(client, data); + return ret; return 0; } @@ -1683,7 +1639,6 @@ static struct i2c_driver adt7475_driver = { .of_match_table = of_match_ptr(adt7475_of_match), }, .probe = adt7475_probe, - .remove = adt7475_remove, .id_table = adt7475_id, .detect = adt7475_detect, .address_list = normal_i2c, @@ -1757,8 +1712,8 @@ static void adt7475_read_pwm(struct i2c_client *client, int index) static int adt7475_update_measure(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); + struct i2c_client *client = data->client; u16 ext; int i; int ret; @@ -1854,8 +1809,7 @@ static int adt7475_update_measure(struct device *dev) static struct adt7475_data *adt7475_update_device(struct device *dev) { - struct i2c_client *client = to_i2c_client(dev); - struct adt7475_data *data = i2c_get_clientdata(client); + struct adt7475_data *data = dev_get_drvdata(dev); int ret; mutex_lock(&data->lock); diff --git a/drivers/hwmon/as370-hwmon.c b/drivers/hwmon/as370-hwmon.c new file mode 100644 index 000000000000..464244ba8d58 --- /dev/null +++ b/drivers/hwmon/as370-hwmon.c @@ -0,0 +1,145 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Synaptics AS370 SoC Hardware Monitoring Driver + * + * Copyright (C) 2018 Synaptics Incorporated + * Author: Jisheng Zhang <jszhang@kernel.org> + */ + +#include <linux/bitops.h> +#include <linux/hwmon.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of_device.h> + +#define CTRL 0x0 +#define PD BIT(0) +#define EN BIT(1) +#define T_SEL BIT(2) +#define V_SEL BIT(3) +#define NMOS_SEL BIT(8) +#define PMOS_SEL BIT(9) +#define STS 0x4 +#define BN_MASK GENMASK(11, 0) +#define EOC BIT(12) + +struct as370_hwmon { + void __iomem *base; +}; + +static void init_pvt(struct as370_hwmon *hwmon) +{ + u32 val; + void __iomem *addr = hwmon->base + CTRL; + + val = PD; + writel_relaxed(val, addr); + val |= T_SEL; + writel_relaxed(val, addr); + val |= EN; + writel_relaxed(val, addr); + val &= ~PD; + writel_relaxed(val, addr); +} + +static int as370_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *temp) +{ + int val; + struct as370_hwmon *hwmon = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_temp_input: + val = readl_relaxed(hwmon->base + STS) & BN_MASK; + *temp = DIV_ROUND_CLOSEST(val * 251802, 4096) - 85525; + break; + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static umode_t +as370_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + if (type != hwmon_temp) + return 0; + + switch (attr) { + case hwmon_temp_input: + return 0444; + default: + return 0; + } +} + +static const u32 as370_hwmon_temp_config[] = { + HWMON_T_INPUT, + 0 +}; + +static const struct hwmon_channel_info as370_hwmon_temp = { + .type = hwmon_temp, + .config = as370_hwmon_temp_config, +}; + +static const struct hwmon_channel_info *as370_hwmon_info[] = { + &as370_hwmon_temp, + NULL +}; + +static const struct hwmon_ops as370_hwmon_ops = { + .is_visible = as370_hwmon_is_visible, + .read = as370_hwmon_read, +}; + +static const struct hwmon_chip_info as370_chip_info = { + .ops = &as370_hwmon_ops, + .info = as370_hwmon_info, +}; + +static int as370_hwmon_probe(struct platform_device *pdev) +{ + struct device *hwmon_dev; + struct as370_hwmon *hwmon; + struct device *dev = &pdev->dev; + + hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL); + if (!hwmon) + return -ENOMEM; + + hwmon->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(hwmon->base)) + return PTR_ERR(hwmon->base); + + init_pvt(hwmon); + + hwmon_dev = devm_hwmon_device_register_with_info(dev, + "as370", + hwmon, + &as370_chip_info, + NULL); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct of_device_id as370_hwmon_match[] = { + { .compatible = "syna,as370-hwmon" }, + {}, +}; +MODULE_DEVICE_TABLE(of, as370_hwmon_match); + +static struct platform_driver as370_hwmon_driver = { + .probe = as370_hwmon_probe, + .driver = { + .name = "as370-hwmon", + .of_match_table = as370_hwmon_match, + }, +}; +module_platform_driver(as370_hwmon_driver); + +MODULE_AUTHOR("Jisheng Zhang<jszhang@kernel.org>"); +MODULE_DESCRIPTION("Synaptics AS370 SoC hardware monitor"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index c9fa84b25678..4c609e23a4ef 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -706,21 +706,21 @@ static int asb100_detect_subclients(struct i2c_client *client) goto ERROR_SC_2; } - data->lm75[0] = i2c_new_dummy(adapter, sc_addr[0]); - if (!data->lm75[0]) { + data->lm75[0] = i2c_new_dummy_device(adapter, sc_addr[0]); + if (IS_ERR(data->lm75[0])) { dev_err(&client->dev, "subclient %d registration at address 0x%x failed.\n", 1, sc_addr[0]); - err = -ENOMEM; + err = PTR_ERR(data->lm75[0]); goto ERROR_SC_2; } - data->lm75[1] = i2c_new_dummy(adapter, sc_addr[1]); - if (!data->lm75[1]) { + data->lm75[1] = i2c_new_dummy_device(adapter, sc_addr[1]); + if (IS_ERR(data->lm75[1])) { dev_err(&client->dev, "subclient %d registration at address 0x%x failed.\n", 2, sc_addr[1]); - err = -ENOMEM; + err = PTR_ERR(data->lm75[1]); goto ERROR_SC_3; } diff --git a/drivers/hwmon/asus_atk0110.c b/drivers/hwmon/asus_atk0110.c index 8dd5b1b8db60..ff64a39d56de 100644 --- a/drivers/hwmon/asus_atk0110.c +++ b/drivers/hwmon/asus_atk0110.c @@ -789,33 +789,16 @@ static const struct file_operations atk_debugfs_ggrp_fops = { static void atk_debugfs_init(struct atk_data *data) { struct dentry *d; - struct dentry *f; data->debugfs.id = 0; d = debugfs_create_dir("asus_atk0110", NULL); - if (!d || IS_ERR(d)) - return; - f = debugfs_create_x32("id", 0600, d, &data->debugfs.id); - if (!f || IS_ERR(f)) - goto cleanup; - - f = debugfs_create_file_unsafe("gitm", 0400, d, data, - &atk_debugfs_gitm); - if (!f || IS_ERR(f)) - goto cleanup; - - f = debugfs_create_file("ggrp", 0400, d, data, - &atk_debugfs_ggrp_fops); - if (!f || IS_ERR(f)) - goto cleanup; + debugfs_create_x32("id", 0600, d, &data->debugfs.id); + debugfs_create_file_unsafe("gitm", 0400, d, data, &atk_debugfs_gitm); + debugfs_create_file("ggrp", 0400, d, data, &atk_debugfs_ggrp_fops); data->debugfs.root = d; - - return; -cleanup: - debugfs_remove_recursive(d); } static void atk_debugfs_cleanup(struct atk_data *data) diff --git a/drivers/hwmon/coretemp.c b/drivers/hwmon/coretemp.c index 4d0d6c86c12f..d855c78fb8be 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -96,10 +96,10 @@ struct platform_data { struct device_attribute name_attr; }; -/* Keep track of how many package pointers we allocated in init() */ -static int max_packages __read_mostly; -/* Array of package pointers. Serialized by cpu hotplug lock */ -static struct platform_device **pkg_devices; +/* Keep track of how many zone pointers we allocated in init() */ +static int max_zones __read_mostly; +/* Array of zone pointers. Serialized by cpu hotplug lock */ +static struct platform_device **zone_devices; static ssize_t show_label(struct device *dev, struct device_attribute *devattr, char *buf) @@ -422,10 +422,10 @@ static int chk_ucode_version(unsigned int cpu) static struct platform_device *coretemp_get_pdev(unsigned int cpu) { - int pkgid = topology_logical_package_id(cpu); + int id = topology_logical_die_id(cpu); - if (pkgid >= 0 && pkgid < max_packages) - return pkg_devices[pkgid]; + if (id >= 0 && id < max_zones) + return zone_devices[id]; return NULL; } @@ -531,7 +531,7 @@ static int coretemp_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct platform_data *pdata; - /* Initialize the per-package data structures */ + /* Initialize the per-zone data structures */ pdata = devm_kzalloc(dev, sizeof(struct platform_data), GFP_KERNEL); if (!pdata) return -ENOMEM; @@ -566,13 +566,13 @@ static struct platform_driver coretemp_driver = { static struct platform_device *coretemp_device_add(unsigned int cpu) { - int err, pkgid = topology_logical_package_id(cpu); + int err, zoneid = topology_logical_die_id(cpu); struct platform_device *pdev; - if (pkgid < 0) + if (zoneid < 0) return ERR_PTR(-ENOMEM); - pdev = platform_device_alloc(DRVNAME, pkgid); + pdev = platform_device_alloc(DRVNAME, zoneid); if (!pdev) return ERR_PTR(-ENOMEM); @@ -582,7 +582,7 @@ static struct platform_device *coretemp_device_add(unsigned int cpu) return ERR_PTR(err); } - pkg_devices[pkgid] = pdev; + zone_devices[zoneid] = pdev; return pdev; } @@ -690,7 +690,7 @@ static int coretemp_cpu_offline(unsigned int cpu) * the rest. */ if (cpumask_empty(&pd->cpumask)) { - pkg_devices[topology_logical_package_id(cpu)] = NULL; + zone_devices[topology_logical_die_id(cpu)] = NULL; platform_device_unregister(pdev); return 0; } @@ -728,15 +728,15 @@ static int __init coretemp_init(void) if (!x86_match_cpu(coretemp_ids)) return -ENODEV; - max_packages = topology_max_packages(); - pkg_devices = kcalloc(max_packages, sizeof(struct platform_device *), + max_zones = topology_max_packages() * topology_max_die_per_package(); + zone_devices = kcalloc(max_zones, sizeof(struct platform_device *), GFP_KERNEL); - if (!pkg_devices) + if (!zone_devices) return -ENOMEM; err = platform_driver_register(&coretemp_driver); if (err) - return err; + goto outzone; err = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "hwmon/coretemp:online", coretemp_cpu_online, coretemp_cpu_offline); @@ -747,7 +747,8 @@ static int __init coretemp_init(void) outdrv: platform_driver_unregister(&coretemp_driver); - kfree(pkg_devices); +outzone: + kfree(zone_devices); return err; } module_init(coretemp_init) @@ -756,7 +757,7 @@ static void __exit coretemp_exit(void) { cpuhp_remove_state(coretemp_hp_online); platform_driver_unregister(&coretemp_driver); - kfree(pkg_devices); + kfree(zone_devices); } module_exit(coretemp_exit) diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 84753680a4e8..3ea4021f267c 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -54,8 +54,8 @@ static void fan_alarm_notify(struct work_struct *ws) struct gpio_fan_data *fan_data = container_of(ws, struct gpio_fan_data, alarm_work); - sysfs_notify(&fan_data->dev->kobj, NULL, "fan1_alarm"); - kobject_uevent(&fan_data->dev->kobj, KOBJ_CHANGE); + sysfs_notify(&fan_data->hwmon_dev->kobj, NULL, "fan1_alarm"); + kobject_uevent(&fan_data->hwmon_dev->kobj, KOBJ_CHANGE); } static irqreturn_t fan_alarm_irq_handler(int irq, void *dev_id) @@ -510,13 +510,6 @@ static int gpio_fan_probe(struct platform_device *pdev) platform_set_drvdata(pdev, fan_data); mutex_init(&fan_data->lock); - /* Configure alarm GPIO if available. */ - if (fan_data->alarm_gpio) { - err = fan_alarm_init(fan_data); - if (err) - return err; - } - /* Configure control GPIOs if available. */ if (fan_data->gpios && fan_data->num_gpios > 0) { if (!fan_data->speed || fan_data->num_speed <= 1) @@ -524,7 +517,9 @@ static int gpio_fan_probe(struct platform_device *pdev) err = fan_ctrl_init(fan_data); if (err) return err; - devm_add_action_or_reset(dev, gpio_fan_stop, fan_data); + err = devm_add_action_or_reset(dev, gpio_fan_stop, fan_data); + if (err) + return err; } /* Make this driver part of hwmon class. */ @@ -535,6 +530,13 @@ static int gpio_fan_probe(struct platform_device *pdev) if (IS_ERR(fan_data->hwmon_dev)) return PTR_ERR(fan_data->hwmon_dev); + /* Configure alarm GPIO if available. */ + if (fan_data->alarm_gpio) { + err = fan_alarm_init(fan_data); + if (err) + return err; + } + /* Optional cooling device register for Device tree platforms */ fan_data->cdev = devm_thermal_of_cooling_device_register(dev, np, "gpio-fan", fan_data, &gpio_fan_cool_ops); diff --git a/drivers/hwmon/hwmon.c b/drivers/hwmon/hwmon.c index 05e120e01cb4..1f3b30b085b9 100644 --- a/drivers/hwmon/hwmon.c +++ b/drivers/hwmon/hwmon.c @@ -651,6 +651,12 @@ __hwmon_device_register(struct device *dev, const char *name, void *drvdata, hwdev, j); if (err) { device_unregister(hdev); + /* + * Don't worry about hwdev; + * hwmon_dev_release(), called + * from device_unregister(), + * will free it. + */ goto ida_remove; } } diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index f1c2d5faedf0..b85a125dd86f 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -44,12 +44,20 @@ static ssize_t iio_hwmon_read_val(struct device *dev, int ret; struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); struct iio_hwmon_state *state = dev_get_drvdata(dev); + struct iio_channel *chan = &state->channels[sattr->index]; + enum iio_chan_type type; + + ret = iio_read_channel_processed(chan, &result); + if (ret < 0) + return ret; - ret = iio_read_channel_processed(&state->channels[sattr->index], - &result); + ret = iio_get_channel_type(chan, &type); if (ret < 0) return ret; + if (type == IIO_POWER) + result *= 1000; /* mili-Watts to micro-Watts conversion */ + return sprintf(buf, "%d\n", result); } @@ -59,7 +67,7 @@ static int iio_hwmon_probe(struct platform_device *pdev) struct iio_hwmon_state *st; struct sensor_device_attribute *a; int ret, i; - int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1; + int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1, power_i = 1; enum iio_chan_type type; struct iio_channel *channels; struct device *hwmon_dev; @@ -114,6 +122,10 @@ static int iio_hwmon_probe(struct platform_device *pdev) n = curr_i++; prefix = "curr"; break; + case IIO_POWER: + n = power_i++; + prefix = "power"; + break; case IIO_HUMIDITYRELATIVE: n = humidity_i++; prefix = "humidity"; diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c index 55943b4dcc7b..0037e2bdacd6 100644 --- a/drivers/hwmon/ina3221.c +++ b/drivers/hwmon/ina3221.c @@ -713,8 +713,10 @@ static int ina3221_probe_from_dt(struct device *dev, struct ina3221_data *ina) for_each_child_of_node(np, child) { ret = ina3221_probe_child_from_dt(dev, child, ina); - if (ret) + if (ret) { + of_node_put(child); return ret; + } } return 0; diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index c77e89239dcd..5c1dddde193c 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -349,6 +349,7 @@ static const struct pci_device_id k10temp_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) }, { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) }, {} }; diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index 4994c90c8929..f73bd4eceb28 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -10,10 +10,8 @@ #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> -#include <linux/jiffies.h> #include <linux/pci.h> #include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> #include <linux/err.h> #include <linux/mutex.h> #include <asm/processor.h> @@ -24,108 +22,18 @@ #define SEL_CORE 0x04 struct k8temp_data { - struct device *hwmon_dev; struct mutex update_lock; - const char *name; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ /* registers values */ u8 sensorsp; /* sensor presence bits - SEL_CORE, SEL_PLACE */ - u32 temp[2][2]; /* core, place */ u8 swap_core_select; /* meaning of SEL_CORE is inverted */ u32 temp_offset; }; -static struct k8temp_data *k8temp_update_device(struct device *dev) -{ - struct k8temp_data *data = dev_get_drvdata(dev); - struct pci_dev *pdev = to_pci_dev(dev); - u8 tmp; - - mutex_lock(&data->update_lock); - - if (!data->valid - || time_after(jiffies, data->last_updated + HZ)) { - pci_read_config_byte(pdev, REG_TEMP, &tmp); - tmp &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */ - pci_write_config_byte(pdev, REG_TEMP, tmp); - pci_read_config_dword(pdev, REG_TEMP, &data->temp[0][0]); - - if (data->sensorsp & SEL_PLACE) { - tmp |= SEL_PLACE; /* Select sensor 1, core0 */ - pci_write_config_byte(pdev, REG_TEMP, tmp); - pci_read_config_dword(pdev, REG_TEMP, - &data->temp[0][1]); - } - - if (data->sensorsp & SEL_CORE) { - tmp &= ~SEL_PLACE; /* Select sensor 0, core1 */ - tmp |= SEL_CORE; - pci_write_config_byte(pdev, REG_TEMP, tmp); - pci_read_config_dword(pdev, REG_TEMP, - &data->temp[1][0]); - - if (data->sensorsp & SEL_PLACE) { - tmp |= SEL_PLACE; /* Select sensor 1, core1 */ - pci_write_config_byte(pdev, REG_TEMP, tmp); - pci_read_config_dword(pdev, REG_TEMP, - &data->temp[1][1]); - } - } - - data->last_updated = jiffies; - data->valid = 1; - } - - mutex_unlock(&data->update_lock); - return data; -} - -/* - * Sysfs stuff - */ - -static ssize_t name_show(struct device *dev, struct device_attribute - *devattr, char *buf) -{ - struct k8temp_data *data = dev_get_drvdata(dev); - - return sprintf(buf, "%s\n", data->name); -} - - -static ssize_t temp_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute_2 *attr = - to_sensor_dev_attr_2(devattr); - int core = attr->nr; - int place = attr->index; - int temp; - struct k8temp_data *data = k8temp_update_device(dev); - - if (data->swap_core_select && (data->sensorsp & SEL_CORE)) - core = core ? 0 : 1; - - temp = TEMP_FROM_REG(data->temp[core][place]) + data->temp_offset; - - return sprintf(buf, "%d\n", temp); -} - -/* core, place */ - -static SENSOR_DEVICE_ATTR_2_RO(temp1_input, temp, 0, 0); -static SENSOR_DEVICE_ATTR_2_RO(temp2_input, temp, 0, 1); -static SENSOR_DEVICE_ATTR_2_RO(temp3_input, temp, 1, 0); -static SENSOR_DEVICE_ATTR_2_RO(temp4_input, temp, 1, 1); -static DEVICE_ATTR_RO(name); - static const struct pci_device_id k8temp_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) }, { 0 }, }; - MODULE_DEVICE_TABLE(pci, k8temp_ids); static int is_rev_g_desktop(u8 model) @@ -159,14 +67,76 @@ static int is_rev_g_desktop(u8 model) return 1; } +static umode_t +k8temp_is_visible(const void *drvdata, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct k8temp_data *data = drvdata; + + if ((channel & 1) && !(data->sensorsp & SEL_PLACE)) + return 0; + + if ((channel & 2) && !(data->sensorsp & SEL_CORE)) + return 0; + + return 0444; +} + +static int +k8temp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct k8temp_data *data = dev_get_drvdata(dev); + struct pci_dev *pdev = to_pci_dev(dev->parent); + int core, place; + u32 temp; + u8 tmp; + + core = (channel >> 1) & 1; + place = channel & 1; + + core ^= data->swap_core_select; + + mutex_lock(&data->update_lock); + pci_read_config_byte(pdev, REG_TEMP, &tmp); + tmp &= ~(SEL_PLACE | SEL_CORE); + if (core) + tmp |= SEL_CORE; + if (place) + tmp |= SEL_PLACE; + pci_write_config_byte(pdev, REG_TEMP, tmp); + pci_read_config_dword(pdev, REG_TEMP, &temp); + mutex_unlock(&data->update_lock); + + *val = TEMP_FROM_REG(temp) + data->temp_offset; + + return 0; +} + +static const struct hwmon_ops k8temp_ops = { + .is_visible = k8temp_is_visible, + .read = k8temp_read, +}; + +static const struct hwmon_channel_info *k8temp_info[] = { + HWMON_CHANNEL_INFO(temp, + HWMON_T_INPUT, HWMON_T_INPUT, HWMON_T_INPUT, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_chip_info k8temp_chip_info = { + .ops = &k8temp_ops, + .info = k8temp_info, +}; + static int k8temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - int err; u8 scfg; u32 temp; u8 model, stepping; struct k8temp_data *data; + struct device *hwmon_dev; data = devm_kzalloc(&pdev->dev, sizeof(struct k8temp_data), GFP_KERNEL); if (!data) @@ -231,86 +201,21 @@ static int k8temp_probe(struct pci_dev *pdev, data->sensorsp &= ~SEL_CORE; } - data->name = "k8temp"; mutex_init(&data->update_lock); - pci_set_drvdata(pdev, data); - - /* Register sysfs hooks */ - err = device_create_file(&pdev->dev, - &sensor_dev_attr_temp1_input.dev_attr); - if (err) - goto exit_remove; - - /* sensor can be changed and reports something */ - if (data->sensorsp & SEL_PLACE) { - err = device_create_file(&pdev->dev, - &sensor_dev_attr_temp2_input.dev_attr); - if (err) - goto exit_remove; - } - - /* core can be changed and reports something */ - if (data->sensorsp & SEL_CORE) { - err = device_create_file(&pdev->dev, - &sensor_dev_attr_temp3_input.dev_attr); - if (err) - goto exit_remove; - if (data->sensorsp & SEL_PLACE) { - err = device_create_file(&pdev->dev, - &sensor_dev_attr_temp4_input. - dev_attr); - if (err) - goto exit_remove; - } - } - - err = device_create_file(&pdev->dev, &dev_attr_name); - if (err) - goto exit_remove; - data->hwmon_dev = hwmon_device_register(&pdev->dev); + hwmon_dev = devm_hwmon_device_register_with_info(&pdev->dev, + "k8temp", + data, + &k8temp_chip_info, + NULL); - if (IS_ERR(data->hwmon_dev)) { - err = PTR_ERR(data->hwmon_dev); - goto exit_remove; - } - - return 0; - -exit_remove: - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp1_input.dev_attr); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp2_input.dev_attr); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp3_input.dev_attr); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp4_input.dev_attr); - device_remove_file(&pdev->dev, &dev_attr_name); - return err; -} - -static void k8temp_remove(struct pci_dev *pdev) -{ - struct k8temp_data *data = pci_get_drvdata(pdev); - - hwmon_device_unregister(data->hwmon_dev); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp1_input.dev_attr); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp2_input.dev_attr); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp3_input.dev_attr); - device_remove_file(&pdev->dev, - &sensor_dev_attr_temp4_input.dev_attr); - device_remove_file(&pdev->dev, &dev_attr_name); + return PTR_ERR_OR_ZERO(hwmon_dev); } static struct pci_driver k8temp_driver = { .name = "k8temp", .id_table = k8temp_ids, .probe = k8temp_probe, - .remove = k8temp_remove, }; module_pci_driver(k8temp_driver); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 3fb9c0a2d6d0..5e6392294c03 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -16,9 +16,9 @@ #include <linux/of_device.h> #include <linux/of.h> #include <linux/regmap.h> +#include <linux/util_macros.h> #include "lm75.h" - /* * This driver handles the LM75 and compatible digital temperature sensors. */ @@ -36,6 +36,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ max6626, max31725, mcp980x, + pct2075, stds75, stlm75, tcn75, @@ -50,6 +51,41 @@ enum lm75_type { /* keep sorted in alphabetical order */ tmp75c, }; +/** + * struct lm75_params - lm75 configuration parameters. + * @set_mask: Bits to set in configuration register when configuring + * the chip. + * @clr_mask: Bits to clear in configuration register when configuring + * the chip. + * @default_resolution: Default number of bits to represent the temperature + * value. + * @resolution_limits: Limit register resolution. Optional. Should be set if + * the resolution of limit registers does not match the + * resolution of the temperature register. + * @resolutions: List of resolutions associated with sample times. + * Optional. Should be set if num_sample_times is larger + * than 1, and if the resolution changes with sample times. + * If set, number of entries must match num_sample_times. + * @default_sample_time:Sample time to be set by default. + * @num_sample_times: Number of possible sample times to be set. Optional. + * Should be set if the number of sample times is larger + * than one. + * @sample_times: All the possible sample times to be set. Mandatory if + * num_sample_times is larger than 1. If set, number of + * entries must match num_sample_times. + */ + +struct lm75_params { + u8 set_mask; + u8 clr_mask; + u8 default_resolution; + u8 resolution_limits; + const u8 *resolutions; + unsigned int default_sample_time; + u8 num_sample_times; + const unsigned int *sample_times; +}; + /* Addresses scanned */ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; @@ -59,24 +95,231 @@ static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, #define LM75_REG_CONF 0x01 #define LM75_REG_HYST 0x02 #define LM75_REG_MAX 0x03 +#define PCT2075_REG_IDLE 0x04 /* Each client has this additional data */ struct lm75_data { - struct i2c_client *client; - struct regmap *regmap; - u8 orig_conf; - u8 resolution; /* In bits, between 9 and 16 */ - u8 resolution_limits; - unsigned int sample_time; /* In ms */ + struct i2c_client *client; + struct regmap *regmap; + u8 orig_conf; + u8 current_conf; + u8 resolution; /* In bits, 9 to 16 */ + unsigned int sample_time; /* In ms */ + enum lm75_type kind; + const struct lm75_params *params; }; /*-----------------------------------------------------------------------*/ +static const u8 lm75_sample_set_masks[] = { 0 << 5, 1 << 5, 2 << 5, 3 << 5 }; + +#define LM75_SAMPLE_CLEAR_MASK (3 << 5) + +/* The structure below stores the configuration values of the supported devices. + * In case of being supported multiple configurations, the default one must + * always be the first element of the array + */ +static const struct lm75_params device_params[] = { + [adt75] = { + .clr_mask = 1 << 5, /* not one-shot mode */ + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [ds1775] = { + .clr_mask = 3 << 5, + .set_mask = 2 << 5, /* 11-bit mode */ + .default_resolution = 11, + .default_sample_time = 500, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 125, 250, 500, 1000 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [ds75] = { + .clr_mask = 3 << 5, + .set_mask = 2 << 5, /* 11-bit mode */ + .default_resolution = 11, + .default_sample_time = 600, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 150, 300, 600, 1200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [stds75] = { + .clr_mask = 3 << 5, + .set_mask = 2 << 5, /* 11-bit mode */ + .default_resolution = 11, + .default_sample_time = 600, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 150, 300, 600, 1200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [stlm75] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 6, + }, + [ds7505] = { + .set_mask = 3 << 5, /* 12-bit mode*/ + .default_resolution = 12, + .default_sample_time = 200, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 25, 50, 100, 200 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [g751] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [lm75] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [lm75a] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [lm75b] = { + .default_resolution = 11, + .default_sample_time = MSEC_PER_SEC / 10, + }, + [max6625] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 7, + }, + [max6626] = { + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 7, + .resolution_limits = 9, + }, + [max31725] = { + .default_resolution = 16, + .default_sample_time = MSEC_PER_SEC / 20, + }, + [tcn75] = { + .default_resolution = 9, + .default_sample_time = MSEC_PER_SEC / 18, + }, + [pct2075] = { + .default_resolution = 11, + .default_sample_time = MSEC_PER_SEC / 10, + .num_sample_times = 31, + .sample_times = (unsigned int []){ 100, 200, 300, 400, 500, 600, + 700, 800, 900, 1000, 1100, 1200, 1300, 1400, 1500, 1600, 1700, + 1800, 1900, 2000, 2100, 2200, 2300, 2400, 2500, 2600, 2700, + 2800, 2900, 3000, 3100 }, + }, + [mcp980x] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode */ + .default_resolution = 12, + .resolution_limits = 9, + .default_sample_time = 240, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 30, 60, 120, 240 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp100] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode */ + .default_resolution = 12, + .default_sample_time = 320, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 40, 80, 160, 320 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp101] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode */ + .default_resolution = 12, + .default_sample_time = 320, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 40, 80, 160, 320 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp105] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp112] = { + .set_mask = 3 << 5, /* 8 samples / second */ + .clr_mask = 1 << 7, /* no one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 125, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 125, 250, 1000, 4000 }, + }, + [tmp175] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp275] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp75] = { + .set_mask = 3 << 5, /* 12-bit mode */ + .clr_mask = 1 << 7, /* not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = 220, + .num_sample_times = 4, + .sample_times = (unsigned int []){ 28, 55, 110, 220 }, + .resolutions = (u8 []) {9, 10, 11, 12 }, + }, + [tmp75b] = { /* not one-shot mode, Conversion rate 37Hz */ + .clr_mask = 1 << 7 | 3 << 5, + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 37, + .sample_times = (unsigned int []){ MSEC_PER_SEC / 37, + MSEC_PER_SEC / 18, + MSEC_PER_SEC / 9, MSEC_PER_SEC / 4 }, + .num_sample_times = 4, + }, + [tmp75c] = { + .clr_mask = 1 << 5, /*not one-shot mode*/ + .default_resolution = 12, + .default_sample_time = MSEC_PER_SEC / 12, + } +}; + static inline long lm75_reg_to_mc(s16 temp, u8 resolution) { return ((temp >> (16 - resolution)) * 1000) >> (resolution - 8); } +static int lm75_write_config(struct lm75_data *data, u8 set_mask, + u8 clr_mask) +{ + u8 value; + + clr_mask |= LM75_SHUTDOWN; + value = data->current_conf & ~clr_mask; + value |= set_mask; + + if (data->current_conf != value) { + s32 err; + + err = i2c_smbus_write_byte_data(data->client, LM75_REG_CONF, + value); + if (err) + return err; + data->current_conf = value; + } + return 0; +} + static int lm75_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { @@ -120,16 +363,12 @@ static int lm75_read(struct device *dev, enum hwmon_sensor_types type, return 0; } -static int lm75_write(struct device *dev, enum hwmon_sensor_types type, - u32 attr, int channel, long temp) +static int lm75_write_temp(struct device *dev, u32 attr, long temp) { struct lm75_data *data = dev_get_drvdata(dev); u8 resolution; int reg; - if (type != hwmon_temp) - return -EINVAL; - switch (attr) { case hwmon_temp_max: reg = LM75_REG_MAX; @@ -145,8 +384,8 @@ static int lm75_write(struct device *dev, enum hwmon_sensor_types type, * Resolution of limit registers is assumed to be the same as the * temperature input register resolution unless given explicitly. */ - if (data->resolution_limits) - resolution = data->resolution_limits; + if (data->params->resolution_limits) + resolution = data->params->resolution_limits; else resolution = data->resolution; @@ -154,16 +393,88 @@ static int lm75_write(struct device *dev, enum hwmon_sensor_types type, temp = DIV_ROUND_CLOSEST(temp << (resolution - 8), 1000) << (16 - resolution); - return regmap_write(data->regmap, reg, temp); + return regmap_write(data->regmap, reg, (u16)temp); +} + +static int lm75_update_interval(struct device *dev, long val) +{ + struct lm75_data *data = dev_get_drvdata(dev); + unsigned int reg; + u8 index; + s32 err; + + index = find_closest(val, data->params->sample_times, + (int)data->params->num_sample_times); + + switch (data->kind) { + default: + err = lm75_write_config(data, lm75_sample_set_masks[index], + LM75_SAMPLE_CLEAR_MASK); + if (err) + return err; + + data->sample_time = data->params->sample_times[index]; + if (data->params->resolutions) + data->resolution = data->params->resolutions[index]; + break; + case tmp112: + err = regmap_read(data->regmap, LM75_REG_CONF, ®); + if (err < 0) + return err; + reg &= ~0x00c0; + reg |= (3 - index) << 6; + err = regmap_write(data->regmap, LM75_REG_CONF, reg); + if (err < 0) + return err; + data->sample_time = data->params->sample_times[index]; + break; + case pct2075: + err = i2c_smbus_write_byte_data(data->client, PCT2075_REG_IDLE, + index + 1); + if (err) + return err; + data->sample_time = data->params->sample_times[index]; + break; + } + return 0; +} + +static int lm75_write_chip(struct device *dev, u32 attr, long val) +{ + switch (attr) { + case hwmon_chip_update_interval: + return lm75_update_interval(dev, val); + default: + return -EINVAL; + } + return 0; +} + +static int lm75_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_chip: + return lm75_write_chip(dev, attr, val); + case hwmon_temp: + return lm75_write_temp(dev, attr, val); + default: + return -EINVAL; + } + return 0; } static umode_t lm75_is_visible(const void *data, enum hwmon_sensor_types type, u32 attr, int channel) { + const struct lm75_data *config_data = data; + switch (type) { case hwmon_chip: switch (attr) { case hwmon_chip_update_interval: + if (config_data->params->num_sample_times > 1) + return 0644; return 0444; } break; @@ -208,13 +519,13 @@ static bool lm75_is_writeable_reg(struct device *dev, unsigned int reg) static bool lm75_is_volatile_reg(struct device *dev, unsigned int reg) { - return reg == LM75_REG_TEMP; + return reg == LM75_REG_TEMP || reg == LM75_REG_CONF; } static const struct regmap_config lm75_regmap_config = { .reg_bits = 8, .val_bits = 16, - .max_register = LM75_REG_MAX, + .max_register = PCT2075_REG_IDLE, .writeable_reg = lm75_is_writeable_reg, .volatile_reg = lm75_is_volatile_reg, .val_format_endian = REGMAP_ENDIAN_BIG, @@ -238,8 +549,6 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) struct device *hwmon_dev; struct lm75_data *data; int status, err; - u8 set_mask, clr_mask; - int new; enum lm75_type kind; if (client->dev.of_node) @@ -256,6 +565,7 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) return -ENOMEM; data->client = client; + data->kind = kind; data->regmap = devm_regmap_init_i2c(client, &lm75_regmap_config); if (IS_ERR(data->regmap)) @@ -264,113 +574,30 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Set to LM75 resolution (9 bits, 1/2 degree C) and range. * Then tweak to be more precise when appropriate. */ - set_mask = 0; - clr_mask = LM75_SHUTDOWN; /* continuous conversions */ - - switch (kind) { - case adt75: - clr_mask |= 1 << 5; /* not one-shot mode */ - data->resolution = 12; - data->sample_time = MSEC_PER_SEC / 8; - break; - case ds1775: - case ds75: - case stds75: - clr_mask |= 3 << 5; - set_mask |= 2 << 5; /* 11-bit mode */ - data->resolution = 11; - data->sample_time = MSEC_PER_SEC; - break; - case stlm75: - data->resolution = 9; - data->sample_time = MSEC_PER_SEC / 5; - break; - case ds7505: - set_mask |= 3 << 5; /* 12-bit mode */ - data->resolution = 12; - data->sample_time = MSEC_PER_SEC / 4; - break; - case g751: - case lm75: - case lm75a: - data->resolution = 9; - data->sample_time = MSEC_PER_SEC / 2; - break; - case lm75b: - data->resolution = 11; - data->sample_time = MSEC_PER_SEC / 4; - break; - case max6625: - data->resolution = 9; - data->sample_time = MSEC_PER_SEC / 4; - break; - case max6626: - data->resolution = 12; - data->resolution_limits = 9; - data->sample_time = MSEC_PER_SEC / 4; - break; - case max31725: - data->resolution = 16; - data->sample_time = MSEC_PER_SEC / 8; - break; - case tcn75: - data->resolution = 9; - data->sample_time = MSEC_PER_SEC / 8; - break; - case mcp980x: - data->resolution_limits = 9; - /* fall through */ - case tmp100: - case tmp101: - set_mask |= 3 << 5; /* 12-bit mode */ - data->resolution = 12; - data->sample_time = MSEC_PER_SEC; - clr_mask |= 1 << 7; /* not one-shot mode */ - break; - case tmp112: - set_mask |= 3 << 5; /* 12-bit mode */ - clr_mask |= 1 << 7; /* not one-shot mode */ - data->resolution = 12; - data->sample_time = MSEC_PER_SEC / 4; - break; - case tmp105: - case tmp175: - case tmp275: - case tmp75: - set_mask |= 3 << 5; /* 12-bit mode */ - clr_mask |= 1 << 7; /* not one-shot mode */ - data->resolution = 12; - data->sample_time = MSEC_PER_SEC / 2; - break; - case tmp75b: /* not one-shot mode, Conversion rate 37Hz */ - clr_mask |= 1 << 15 | 0x3 << 13; - data->resolution = 12; - data->sample_time = MSEC_PER_SEC / 37; - break; - case tmp75c: - clr_mask |= 1 << 5; /* not one-shot mode */ - data->resolution = 12; - data->sample_time = MSEC_PER_SEC / 4; - break; - } - /* configure as specified */ + data->params = &device_params[data->kind]; + + /* Save default sample time and resolution*/ + data->sample_time = data->params->default_sample_time; + data->resolution = data->params->default_resolution; + + /* Cache original configuration */ status = i2c_smbus_read_byte_data(client, LM75_REG_CONF); if (status < 0) { dev_dbg(dev, "Can't read config? %d\n", status); return status; } data->orig_conf = status; - new = status & ~clr_mask; - new |= set_mask; - if (status != new) - i2c_smbus_write_byte_data(client, LM75_REG_CONF, new); + data->current_conf = status; - err = devm_add_action_or_reset(dev, lm75_remove, data); + err = lm75_write_config(data, data->params->set_mask, + data->params->clr_mask); if (err) return err; - dev_dbg(dev, "Config %02x\n", new); + err = devm_add_action_or_reset(dev, lm75_remove, data); + if (err) + return err; hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data, &lm75_chip_info, @@ -397,6 +624,7 @@ static const struct i2c_device_id lm75_ids[] = { { "max31725", max31725, }, { "max31726", max31725, }, { "mcp980x", mcp980x, }, + { "pct2075", pct2075, }, { "stds75", stds75, }, { "stlm75", stlm75, }, { "tcn75", tcn75, }, @@ -467,6 +695,10 @@ static const struct of_device_id __maybe_unused lm75_of_match[] = { .data = (void *)mcp980x }, { + .compatible = "nxp,pct2075", + .data = (void *)pct2075 + }, + { .compatible = "st,stds75", .data = (void *)stds75 }, diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index e562a578f20e..9b3c9f390ef8 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -174,6 +174,7 @@ enum chips { lm90, adm1032, lm99, lm86, max6657, max6659, adt7461, max6680, #define LM90_HAVE_EMERGENCY_ALARM (1 << 5)/* emergency alarm */ #define LM90_HAVE_TEMP3 (1 << 6) /* 3rd temperature sensor */ #define LM90_HAVE_BROKEN_ALERT (1 << 7) /* Broken alert */ +#define LM90_PAUSE_FOR_CONFIG (1 << 8) /* Pause conversion for config */ /* LM90 status */ #define LM90_STATUS_LTHRM (1 << 0) /* local THERM limit tripped */ @@ -367,6 +368,7 @@ static const struct lm90_params lm90_params[] = { .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, }, [max6657] = { + .flags = LM90_PAUSE_FOR_CONFIG, .alert_alarms = 0x7c, .max_convrate = 8, .reg_local_ext = MAX6657_REG_R_LOCAL_TEMPL, @@ -457,6 +459,7 @@ struct lm90_data { unsigned int update_interval; /* in milliseconds */ + u8 config; /* Current configuration register value */ u8 config_orig; /* Original configuration register value */ u8 convrate_orig; /* Original conversion rate register value */ u16 alert_alarms; /* Which alarm bits trigger ALERT# */ @@ -540,6 +543,21 @@ static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl) return (newh << 8) | l; } +static int lm90_update_confreg(struct lm90_data *data, u8 config) +{ + if (data->config != config) { + int err; + + err = i2c_smbus_write_byte_data(data->client, + LM90_REG_W_CONFIG1, + config); + if (err) + return err; + data->config = config; + } + return 0; +} + /* * client->update_lock must be held when calling this function (unless we are * in detection or initialization steps), and while a remote channel other @@ -548,23 +566,39 @@ static int lm90_read16(struct i2c_client *client, u8 regh, u8 regl) * various registers have different meanings as a result of selecting a * non-default remote channel. */ -static inline int lm90_select_remote_channel(struct i2c_client *client, - struct lm90_data *data, - int channel) +static int lm90_select_remote_channel(struct lm90_data *data, int channel) { - int config; + int err = 0; if (data->kind == max6696) { - config = lm90_read_reg(client, LM90_REG_R_CONFIG1); - if (config < 0) - return config; - config &= ~0x08; + u8 config = data->config & ~0x08; + if (channel) config |= 0x08; - i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, - config); + err = lm90_update_confreg(data, config); } - return 0; + return err; +} + +static int lm90_write_convrate(struct lm90_data *data, int val) +{ + u8 config = data->config; + int err; + + /* Save config and pause conversion */ + if (data->flags & LM90_PAUSE_FOR_CONFIG) { + err = lm90_update_confreg(data, config | 0x40); + if (err < 0) + return err; + } + + /* Set conv rate */ + err = i2c_smbus_write_byte_data(data->client, LM90_REG_W_CONVRATE, val); + + /* Revert change to config */ + lm90_update_confreg(data, config); + + return err; } /* @@ -587,7 +621,7 @@ static int lm90_set_convrate(struct i2c_client *client, struct lm90_data *data, if (interval >= update_interval * 3 / 4) break; - err = i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, i); + err = lm90_write_convrate(data, i); data->update_interval = DIV_ROUND_CLOSEST(update_interval, 64); return err; } @@ -658,7 +692,7 @@ static int lm90_update_limits(struct device *dev) } if (data->kind == max6696) { - val = lm90_select_remote_channel(client, data, 1); + val = lm90_select_remote_channel(data, 1); if (val < 0) return val; @@ -682,7 +716,7 @@ static int lm90_update_limits(struct device *dev) return val; data->temp11[REMOTE2_HIGH] = val << 8; - lm90_select_remote_channel(client, data, 0); + lm90_select_remote_channel(data, 0); } return 0; @@ -742,19 +776,19 @@ static int lm90_update_device(struct device *dev) data->alarms = val; /* lower 8 bit of alarms */ if (data->kind == max6696) { - val = lm90_select_remote_channel(client, data, 1); + val = lm90_select_remote_channel(data, 1); if (val < 0) return val; val = lm90_read16(client, LM90_REG_R_REMOTE_TEMPH, LM90_REG_R_REMOTE_TEMPL); if (val < 0) { - lm90_select_remote_channel(client, data, 0); + lm90_select_remote_channel(data, 0); return val; } data->temp11[REMOTE2_TEMP] = val; - lm90_select_remote_channel(client, data, 0); + lm90_select_remote_channel(data, 0); val = lm90_read_reg(client, MAX6696_REG_R_STATUS2); if (val < 0) @@ -768,15 +802,9 @@ static int lm90_update_device(struct device *dev) */ if (!(data->config_orig & 0x80) && !(data->alarms & data->alert_alarms)) { - val = lm90_read_reg(client, LM90_REG_R_CONFIG1); - if (val < 0) - return val; - - if (val & 0x80) { + if (data->config & 0x80) { dev_dbg(&client->dev, "Re-enabling ALERT#\n"); - i2c_smbus_write_byte_data(client, - LM90_REG_W_CONFIG1, - val & ~0x80); + lm90_update_confreg(data, data->config & ~0x80); } } @@ -994,7 +1022,7 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val) else data->temp11[index] = temp_to_s8(val) << 8; - lm90_select_remote_channel(client, data, index >= 3); + lm90_select_remote_channel(data, index >= 3); err = i2c_smbus_write_byte_data(client, regp->high, data->temp11[index] >> 8); if (err < 0) @@ -1003,7 +1031,7 @@ static int lm90_set_temp11(struct lm90_data *data, int index, long val) err = i2c_smbus_write_byte_data(client, regp->low, data->temp11[index] & 0xff); - lm90_select_remote_channel(client, data, 0); + lm90_select_remote_channel(data, 0); return err; } @@ -1052,9 +1080,9 @@ static int lm90_set_temp8(struct lm90_data *data, int index, long val) else data->temp8[index] = temp_to_s8(val); - lm90_select_remote_channel(client, data, index >= 6); + lm90_select_remote_channel(data, index >= 6); err = i2c_smbus_write_byte_data(client, reg[index], data->temp8[index]); - lm90_select_remote_channel(client, data, 0); + lm90_select_remote_channel(data, 0); return err; } @@ -1593,8 +1621,7 @@ static void lm90_restore_conf(void *_data) struct i2c_client *client = data->client; /* Restore initial configuration */ - i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, - data->convrate_orig); + lm90_write_convrate(data, data->convrate_orig); i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, data->config_orig); } @@ -1611,11 +1638,13 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) /* * Start the conversions. */ - lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */ config = lm90_read_reg(client, LM90_REG_R_CONFIG1); if (config < 0) return config; data->config_orig = config; + data->config = config; + + lm90_set_convrate(client, data, 500); /* 500ms; 2Hz conversion rate */ /* Check Temperature Range Select */ if (data->kind == adt7461 || data->kind == tmp451) { @@ -1638,8 +1667,7 @@ static int lm90_init_client(struct i2c_client *client, struct lm90_data *data) config &= ~0x08; config &= 0xBF; /* run */ - if (config != data->config_orig) /* Only write if changed */ - i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, config); + lm90_update_confreg(data, config); return devm_add_action_or_reset(&client->dev, lm90_restore_conf, data); } @@ -1718,7 +1746,7 @@ static int lm90_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct device *dev = &client->dev; - struct i2c_adapter *adapter = to_i2c_adapter(dev->parent); + struct i2c_adapter *adapter = client->adapter; struct hwmon_channel_info *info; struct regulator *regulator; struct device *hwmon_dev; @@ -1873,14 +1901,8 @@ static void lm90_alert(struct i2c_client *client, enum i2c_alert_protocol type, if ((data->flags & LM90_HAVE_BROKEN_ALERT) && (alarms & data->alert_alarms)) { - int config; - dev_dbg(&client->dev, "Disabling ALERT#\n"); - config = lm90_read_reg(client, LM90_REG_R_CONFIG1); - if (config >= 0) - i2c_smbus_write_byte_data(client, - LM90_REG_W_CONFIG1, - config | 0x80); + lm90_update_confreg(data, data->config | 0x80); } } else { dev_info(&client->dev, "Everything OK\n"); diff --git a/drivers/hwmon/ltc2990.c b/drivers/hwmon/ltc2990.c index f9431ad43ad3..53ff5051774c 100644 --- a/drivers/hwmon/ltc2990.c +++ b/drivers/hwmon/ltc2990.c @@ -13,7 +13,7 @@ #include <linux/i2c.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/of.h> +#include <linux/property.h> #define LTC2990_STATUS 0x00 #define LTC2990_CONTROL 0x01 @@ -206,7 +206,6 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c, int ret; struct device *hwmon_dev; struct ltc2990_data *data; - struct device_node *of_node = i2c->dev.of_node; if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) @@ -218,9 +217,10 @@ static int ltc2990_i2c_probe(struct i2c_client *i2c, data->i2c = i2c; - if (of_node) { - ret = of_property_read_u32_array(of_node, "lltc,meas-mode", - data->mode, 2); + if (dev_fwnode(&i2c->dev)) { + ret = device_property_read_u32_array(&i2c->dev, + "lltc,meas-mode", + data->mode, 2); if (ret < 0) return ret; diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 6b9056f9483f..3d9d371c35b5 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -92,7 +92,8 @@ module_param(clock, int, 0444); #define FAN_RPM_MIN 240 #define FAN_RPM_MAX 30000 -#define DIV_FROM_REG(reg) (1 << (reg & 7)) +#define DIV_FROM_REG(reg) (1 << ((reg) & 7)) +#define DAC_LIMIT(v12) ((v12) ? 180 : 76) /* * Client data (each client gets its own) @@ -100,11 +101,9 @@ module_param(clock, int, 0444); struct max6650_data { struct i2c_client *client; - const struct attribute_group *groups[3]; - struct thermal_cooling_device *cooling_dev; - struct mutex update_lock; + struct mutex update_lock; /* protect alarm register updates */ int nr_fans; - char valid; /* zero until following fields are valid */ + bool valid; /* false until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* register values */ @@ -114,6 +113,7 @@ struct max6650_data { u8 count; u8 dac; u8 alarm; + u8 alarm_en; unsigned long cooling_dev_state; }; @@ -137,41 +137,60 @@ static const struct of_device_id __maybe_unused max6650_dt_match[] = { }; MODULE_DEVICE_TABLE(of, max6650_dt_match); +static int dac_to_pwm(int dac, bool v12) +{ + /* + * Useful range for dac is 0-180 for 12V fans and 0-76 for 5V fans. + * Lower DAC values mean higher speeds. + */ + return clamp_val(255 - (255 * dac) / DAC_LIMIT(v12), 0, 255); +} + +static u8 pwm_to_dac(unsigned int pwm, bool v12) +{ + int limit = DAC_LIMIT(v12); + + return limit - (limit * pwm) / 255; +} + static struct max6650_data *max6650_update_device(struct device *dev) { struct max6650_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; + int reg, err = 0; int i; mutex_lock(&data->update_lock); if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - data->speed = i2c_smbus_read_byte_data(client, - MAX6650_REG_SPEED); - data->config = i2c_smbus_read_byte_data(client, - MAX6650_REG_CONFIG); for (i = 0; i < data->nr_fans; i++) { - data->tach[i] = i2c_smbus_read_byte_data(client, - tach_reg[i]); + reg = i2c_smbus_read_byte_data(client, tach_reg[i]); + if (reg < 0) { + err = reg; + goto error; + } + data->tach[i] = reg; } - data->count = i2c_smbus_read_byte_data(client, - MAX6650_REG_COUNT); - data->dac = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC); /* * Alarms are cleared on read in case the condition that * caused the alarm is removed. Keep the value latched here * for providing the register through different alarm files. */ - data->alarm |= i2c_smbus_read_byte_data(client, - MAX6650_REG_ALARM); - + reg = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM); + if (reg < 0) { + err = reg; + goto error; + } + data->alarm |= reg; data->last_updated = jiffies; - data->valid = 1; + data->valid = true; } +error: mutex_unlock(&data->update_lock); - + if (err) + data = ERR_PTR(err); return data; } @@ -199,26 +218,6 @@ static int max6650_set_operating_mode(struct max6650_data *data, u8 mode) return 0; } -static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct max6650_data *data = max6650_update_device(dev); - int rpm; - - /* - * Calculation details: - * - * Each tachometer counts over an interval given by the "count" - * register (0.25, 0.5, 1 or 2 seconds). This module assumes - * that the fans produce two pulses per revolution (this seems - * to be the most common). - */ - - rpm = ((data->tach[attr->index] * 120) / DIV_FROM_REG(data->count)); - return sprintf(buf, "%d\n", rpm); -} - /* * Set the fan speed to the specified RPM (or read back the RPM setting). * This works in closed loop mode only. Use pwm1 for open loop speed setting. @@ -260,26 +259,6 @@ static ssize_t fan_show(struct device *dev, struct device_attribute *devattr, * controlled. */ -static ssize_t fan1_target_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct max6650_data *data = max6650_update_device(dev); - int kscale, ktach, rpm; - - /* - * Use the datasheet equation: - * - * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)] - * - * then multiply by 60 to give rpm. - */ - - kscale = DIV_FROM_REG(data->config); - ktach = data->speed; - rpm = 60 * kscale * clock / (256 * (ktach + 1)); - return sprintf(buf, "%d\n", rpm); -} - static int max6650_set_target(struct max6650_data *data, unsigned long rpm) { int kscale, ktach; @@ -308,197 +287,8 @@ static int max6650_set_target(struct max6650_data *data, unsigned long rpm) data->speed); } -static ssize_t fan1_target_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct max6650_data *data = dev_get_drvdata(dev); - unsigned long rpm; - int err; - - err = kstrtoul(buf, 10, &rpm); - if (err) - return err; - - mutex_lock(&data->update_lock); - - err = max6650_set_target(data, rpm); - - mutex_unlock(&data->update_lock); - - if (err < 0) - return err; - - return count; -} - -/* - * Get/set the fan speed in open loop mode using pwm1 sysfs file. - * Speed is given as a relative value from 0 to 255, where 255 is maximum - * speed. Note that this is done by writing directly to the chip's DAC, - * it won't change the closed loop speed set by fan1_target. - * Also note that due to rounding errors it is possible that you don't read - * back exactly the value you have set. - */ - -static ssize_t pwm1_show(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - int pwm; - struct max6650_data *data = max6650_update_device(dev); - - /* - * Useful range for dac is 0-180 for 12V fans and 0-76 for 5V fans. - * Lower DAC values mean higher speeds. - */ - if (data->config & MAX6650_CFG_V12) - pwm = 255 - (255 * (int)data->dac)/180; - else - pwm = 255 - (255 * (int)data->dac)/76; - - if (pwm < 0) - pwm = 0; - - return sprintf(buf, "%d\n", pwm); -} - -static ssize_t pwm1_store(struct device *dev, - struct device_attribute *devattr, const char *buf, - size_t count) -{ - struct max6650_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - unsigned long pwm; - int err; - - err = kstrtoul(buf, 10, &pwm); - if (err) - return err; - - pwm = clamp_val(pwm, 0, 255); - - mutex_lock(&data->update_lock); - - if (data->config & MAX6650_CFG_V12) - data->dac = 180 - (180 * pwm)/255; - else - data->dac = 76 - (76 * pwm)/255; - err = i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac); - - mutex_unlock(&data->update_lock); - - return err < 0 ? err : count; -} - /* - * Get/Set controller mode: - * Possible values: - * 0 = Fan always on - * 1 = Open loop, Voltage is set according to speed, not regulated. - * 2 = Closed loop, RPM for all fans regulated by fan1 tachometer - * 3 = Fan off - */ -static ssize_t pwm1_enable_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct max6650_data *data = max6650_update_device(dev); - int mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4; - int sysfs_modes[4] = {0, 3, 2, 1}; - - return sprintf(buf, "%d\n", sysfs_modes[mode]); -} - -static ssize_t pwm1_enable_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct max6650_data *data = dev_get_drvdata(dev); - unsigned long mode; - int err; - const u8 max6650_modes[] = { - MAX6650_CFG_MODE_ON, - MAX6650_CFG_MODE_OPEN_LOOP, - MAX6650_CFG_MODE_CLOSED_LOOP, - MAX6650_CFG_MODE_OFF, - }; - - err = kstrtoul(buf, 10, &mode); - if (err) - return err; - - if (mode >= ARRAY_SIZE(max6650_modes)) - return -EINVAL; - - mutex_lock(&data->update_lock); - - max6650_set_operating_mode(data, max6650_modes[mode]); - - mutex_unlock(&data->update_lock); - - return count; -} - -/* - * Read/write functions for fan1_div sysfs file. The MAX6650 has no such - * divider. We handle this by converting between divider and counttime: - * - * (counttime == k) <==> (divider == 2^k), k = 0, 1, 2, or 3 - * - * Lower values of k allow to connect a faster fan without the risk of - * counter overflow. The price is lower resolution. You can also set counttime - * using the module parameter. Note that the module parameter "prescaler" also - * influences the behaviour. Unfortunately, there's no sysfs attribute - * defined for that. See the data sheet for details. - */ - -static ssize_t fan1_div_show(struct device *dev, - struct device_attribute *devattr, char *buf) -{ - struct max6650_data *data = max6650_update_device(dev); - - return sprintf(buf, "%d\n", DIV_FROM_REG(data->count)); -} - -static ssize_t fan1_div_store(struct device *dev, - struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct max6650_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - unsigned long div; - int err; - - err = kstrtoul(buf, 10, &div); - if (err) - return err; - - mutex_lock(&data->update_lock); - switch (div) { - case 1: - data->count = 0; - break; - case 2: - data->count = 1; - break; - case 4: - data->count = 2; - break; - case 8: - data->count = 3; - break; - default: - mutex_unlock(&data->update_lock); - return -EINVAL; - } - - i2c_smbus_write_byte_data(client, MAX6650_REG_COUNT, data->count); - mutex_unlock(&data->update_lock); - - return count; -} - -/* - * Get alarm stati: + * Get gpio alarm status: * Possible values: * 0 = no alarm * 1 = alarm @@ -509,42 +299,30 @@ static ssize_t alarm_show(struct device *dev, { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct max6650_data *data = max6650_update_device(dev); - struct i2c_client *client = data->client; - int alarm = 0; + bool alarm; - if (data->alarm & attr->index) { + if (IS_ERR(data)) + return PTR_ERR(data); + + alarm = data->alarm & attr->index; + if (alarm) { mutex_lock(&data->update_lock); - alarm = 1; data->alarm &= ~attr->index; - data->alarm |= i2c_smbus_read_byte_data(client, - MAX6650_REG_ALARM); + data->valid = false; mutex_unlock(&data->update_lock); } return sprintf(buf, "%d\n", alarm); } -static SENSOR_DEVICE_ATTR_RO(fan1_input, fan, 0); -static SENSOR_DEVICE_ATTR_RO(fan2_input, fan, 1); -static SENSOR_DEVICE_ATTR_RO(fan3_input, fan, 2); -static SENSOR_DEVICE_ATTR_RO(fan4_input, fan, 3); -static DEVICE_ATTR_RW(fan1_target); -static DEVICE_ATTR_RW(fan1_div); -static DEVICE_ATTR_RW(pwm1_enable); -static DEVICE_ATTR_RW(pwm1); -static SENSOR_DEVICE_ATTR_RO(fan1_max_alarm, alarm, MAX6650_ALRM_MAX); -static SENSOR_DEVICE_ATTR_RO(fan1_min_alarm, alarm, MAX6650_ALRM_MIN); -static SENSOR_DEVICE_ATTR_RO(fan1_fault, alarm, MAX6650_ALRM_TACH); static SENSOR_DEVICE_ATTR_RO(gpio1_alarm, alarm, MAX6650_ALRM_GPIO1); static SENSOR_DEVICE_ATTR_RO(gpio2_alarm, alarm, MAX6650_ALRM_GPIO2); static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a, - int n) + int n) { struct device *dev = container_of(kobj, struct device, kobj); struct max6650_data *data = dev_get_drvdata(dev); - struct i2c_client *client = data->client; - u8 alarm_en = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN); struct device_attribute *devattr; /* @@ -552,12 +330,9 @@ static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a, */ devattr = container_of(a, struct device_attribute, attr); - if (devattr == &sensor_dev_attr_fan1_max_alarm.dev_attr - || devattr == &sensor_dev_attr_fan1_min_alarm.dev_attr - || devattr == &sensor_dev_attr_fan1_fault.dev_attr - || devattr == &sensor_dev_attr_gpio1_alarm.dev_attr - || devattr == &sensor_dev_attr_gpio2_alarm.dev_attr) { - if (!(alarm_en & to_sensor_dev_attr(devattr)->index)) + if (devattr == &sensor_dev_attr_gpio1_alarm.dev_attr || + devattr == &sensor_dev_attr_gpio2_alarm.dev_attr) { + if (!(data->alarm_en & to_sensor_dev_attr(devattr)->index)) return 0; } @@ -565,14 +340,6 @@ static umode_t max6650_attrs_visible(struct kobject *kobj, struct attribute *a, } static struct attribute *max6650_attrs[] = { - &sensor_dev_attr_fan1_input.dev_attr.attr, - &dev_attr_fan1_target.attr, - &dev_attr_fan1_div.attr, - &dev_attr_pwm1_enable.attr, - &dev_attr_pwm1.attr, - &sensor_dev_attr_fan1_max_alarm.dev_attr.attr, - &sensor_dev_attr_fan1_min_alarm.dev_attr.attr, - &sensor_dev_attr_fan1_fault.dev_attr.attr, &sensor_dev_attr_gpio1_alarm.dev_attr.attr, &sensor_dev_attr_gpio2_alarm.dev_attr.attr, NULL @@ -583,27 +350,17 @@ static const struct attribute_group max6650_group = { .is_visible = max6650_attrs_visible, }; -static struct attribute *max6651_attrs[] = { - &sensor_dev_attr_fan2_input.dev_attr.attr, - &sensor_dev_attr_fan3_input.dev_attr.attr, - &sensor_dev_attr_fan4_input.dev_attr.attr, +static const struct attribute_group *max6650_groups[] = { + &max6650_group, NULL }; -static const struct attribute_group max6651_group = { - .attrs = max6651_attrs, -}; - -/* - * Real code - */ - static int max6650_init_client(struct max6650_data *data, struct i2c_client *client) { struct device *dev = &client->dev; - int config; - int err = -EIO; + int reg; + int err; u32 voltage; u32 prescale; u32 target_rpm; @@ -617,21 +374,20 @@ static int max6650_init_client(struct max6650_data *data, &prescale)) prescale = prescaler; - config = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG); - - if (config < 0) { - dev_err(dev, "Error reading config, aborting.\n"); - return err; + reg = i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG); + if (reg < 0) { + dev_err(dev, "Error reading config register, aborting.\n"); + return reg; } switch (voltage) { case 0: break; case 5: - config &= ~MAX6650_CFG_V12; + reg &= ~MAX6650_CFG_V12; break; case 12: - config |= MAX6650_CFG_V12; + reg |= MAX6650_CFG_V12; break; default: dev_err(dev, "illegal value for fan_voltage (%d)\n", voltage); @@ -641,22 +397,22 @@ static int max6650_init_client(struct max6650_data *data, case 0: break; case 1: - config &= ~MAX6650_CFG_PRESCALER_MASK; + reg &= ~MAX6650_CFG_PRESCALER_MASK; break; case 2: - config = (config & ~MAX6650_CFG_PRESCALER_MASK) + reg = (reg & ~MAX6650_CFG_PRESCALER_MASK) | MAX6650_CFG_PRESCALER_2; break; case 4: - config = (config & ~MAX6650_CFG_PRESCALER_MASK) + reg = (reg & ~MAX6650_CFG_PRESCALER_MASK) | MAX6650_CFG_PRESCALER_4; break; case 8: - config = (config & ~MAX6650_CFG_PRESCALER_MASK) + reg = (reg & ~MAX6650_CFG_PRESCALER_MASK) | MAX6650_CFG_PRESCALER_8; break; case 16: - config = (config & ~MAX6650_CFG_PRESCALER_MASK) + reg = (reg & ~MAX6650_CFG_PRESCALER_MASK) | MAX6650_CFG_PRESCALER_16; break; default: @@ -664,16 +420,43 @@ static int max6650_init_client(struct max6650_data *data, } dev_info(dev, "Fan voltage: %dV, prescaler: %d.\n", - (config & MAX6650_CFG_V12) ? 12 : 5, - 1 << (config & MAX6650_CFG_PRESCALER_MASK)); + (reg & MAX6650_CFG_V12) ? 12 : 5, + 1 << (reg & MAX6650_CFG_PRESCALER_MASK)); - if (i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, config)) { + err = i2c_smbus_write_byte_data(client, MAX6650_REG_CONFIG, reg); + if (err) { dev_err(dev, "Config write error, aborting.\n"); return err; } + data->config = reg; - data->config = config; - data->count = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT); + reg = i2c_smbus_read_byte_data(client, MAX6650_REG_SPEED); + if (reg < 0) { + dev_err(dev, "Failed to read speed register, aborting.\n"); + return reg; + } + data->speed = reg; + + reg = i2c_smbus_read_byte_data(client, MAX6650_REG_DAC); + if (reg < 0) { + dev_err(dev, "Failed to read DAC register, aborting.\n"); + return reg; + } + data->dac = reg; + + reg = i2c_smbus_read_byte_data(client, MAX6650_REG_COUNT); + if (reg < 0) { + dev_err(dev, "Failed to read count register, aborting.\n"); + return reg; + } + data->count = reg; + + reg = i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN); + if (reg < 0) { + dev_err(dev, "Failed to read alarm configuration, aborting.\n"); + return reg; + } + data->alarm_en = reg; if (!of_property_read_u32(client->dev.of_node, "maxim,fan-target-rpm", &target_rpm)) { @@ -684,8 +467,6 @@ static int max6650_init_client(struct max6650_data *data, return 0; } -#if IS_ENABLED(CONFIG_THERMAL) - static int max6650_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { @@ -715,23 +496,18 @@ static int max6650_set_cur_state(struct thermal_cooling_device *cdev, mutex_lock(&data->update_lock); - if (data->config & MAX6650_CFG_V12) - data->dac = 180 - (180 * state)/255; - else - data->dac = 76 - (76 * state)/255; - + data->dac = pwm_to_dac(state, data->config & MAX6650_CFG_V12); err = i2c_smbus_write_byte_data(client, MAX6650_REG_DAC, data->dac); - if (!err) { max6650_set_operating_mode(data, state ? - MAX6650_CFG_MODE_OPEN_LOOP : - MAX6650_CFG_MODE_OFF); + MAX6650_CFG_MODE_OPEN_LOOP : + MAX6650_CFG_MODE_OFF); data->cooling_dev_state = state; } mutex_unlock(&data->update_lock); - return err < 0 ? err : 0; + return err; } static const struct thermal_cooling_device_ops max6650_cooling_ops = { @@ -739,11 +515,252 @@ static const struct thermal_cooling_device_ops max6650_cooling_ops = { .get_cur_state = max6650_get_cur_state, .set_cur_state = max6650_set_cur_state, }; -#endif + +static int max6650_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct max6650_data *data = max6650_update_device(dev); + int mode; + + if (IS_ERR(data)) + return PTR_ERR(data); + + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + *val = dac_to_pwm(data->dac, + data->config & MAX6650_CFG_V12); + break; + case hwmon_pwm_enable: + /* + * Possible values: + * 0 = Fan always on + * 1 = Open loop, Voltage is set according to speed, + * not regulated. + * 2 = Closed loop, RPM for all fans regulated by fan1 + * tachometer + * 3 = Fan off + */ + mode = (data->config & MAX6650_CFG_MODE_MASK) >> 4; + *val = (4 - mode) & 3; /* {0 1 2 3} -> {0 3 2 1} */ + break; + default: + return -EOPNOTSUPP; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + /* + * Calculation details: + * + * Each tachometer counts over an interval given by the + * "count" register (0.25, 0.5, 1 or 2 seconds). + * The driver assumes that the fans produce two pulses + * per revolution (this seems to be the most common). + */ + *val = DIV_ROUND_CLOSEST(data->tach[channel] * 120, + DIV_FROM_REG(data->count)); + break; + case hwmon_fan_div: + *val = DIV_FROM_REG(data->count); + break; + case hwmon_fan_target: + /* + * Use the datasheet equation: + * FanSpeed = KSCALE x fCLK / [256 x (KTACH + 1)] + * then multiply by 60 to give rpm. + */ + *val = 60 * DIV_FROM_REG(data->config) * clock / + (256 * (data->speed + 1)); + break; + case hwmon_fan_min_alarm: + *val = !!(data->alarm & MAX6650_ALRM_MIN); + data->alarm &= ~MAX6650_ALRM_MIN; + data->valid = false; + break; + case hwmon_fan_max_alarm: + *val = !!(data->alarm & MAX6650_ALRM_MAX); + data->alarm &= ~MAX6650_ALRM_MAX; + data->valid = false; + break; + case hwmon_fan_fault: + *val = !!(data->alarm & MAX6650_ALRM_TACH); + data->alarm &= ~MAX6650_ALRM_TACH; + data->valid = false; + break; + default: + return -EOPNOTSUPP; + } + break; + default: + return -EOPNOTSUPP; + } + return 0; +} + +static const u8 max6650_pwm_modes[] = { + MAX6650_CFG_MODE_ON, + MAX6650_CFG_MODE_OPEN_LOOP, + MAX6650_CFG_MODE_CLOSED_LOOP, + MAX6650_CFG_MODE_OFF, +}; + +static int max6650_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + struct max6650_data *data = dev_get_drvdata(dev); + int ret = 0; + u8 reg; + + mutex_lock(&data->update_lock); + + switch (type) { + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + reg = pwm_to_dac(clamp_val(val, 0, 255), + data->config & MAX6650_CFG_V12); + ret = i2c_smbus_write_byte_data(data->client, + MAX6650_REG_DAC, reg); + if (ret) + break; + data->dac = reg; + break; + case hwmon_pwm_enable: + if (val < 0 || val >= ARRAY_SIZE(max6650_pwm_modes)) { + ret = -EINVAL; + break; + } + ret = max6650_set_operating_mode(data, + max6650_pwm_modes[val]); + break; + default: + ret = -EOPNOTSUPP; + break; + } + break; + case hwmon_fan: + switch (attr) { + case hwmon_fan_div: + switch (val) { + case 1: + reg = 0; + break; + case 2: + reg = 1; + break; + case 4: + reg = 2; + break; + case 8: + reg = 3; + break; + default: + ret = -EINVAL; + goto error; + } + ret = i2c_smbus_write_byte_data(data->client, + MAX6650_REG_COUNT, reg); + if (ret) + break; + data->count = reg; + break; + case hwmon_fan_target: + if (val < 0) { + ret = -EINVAL; + break; + } + ret = max6650_set_target(data, val); + break; + default: + ret = -EOPNOTSUPP; + break; + } + break; + default: + ret = -EOPNOTSUPP; + break; + } + +error: + mutex_unlock(&data->update_lock); + return ret; +} + +static umode_t max6650_is_visible(const void *_data, + enum hwmon_sensor_types type, u32 attr, + int channel) +{ + const struct max6650_data *data = _data; + + if (channel && (channel >= data->nr_fans || type != hwmon_fan)) + return 0; + + switch (type) { + case hwmon_fan: + switch (attr) { + case hwmon_fan_input: + return 0444; + case hwmon_fan_target: + case hwmon_fan_div: + return 0644; + case hwmon_fan_min_alarm: + if (data->alarm_en & MAX6650_ALRM_MIN) + return 0444; + break; + case hwmon_fan_max_alarm: + if (data->alarm_en & MAX6650_ALRM_MAX) + return 0444; + break; + case hwmon_fan_fault: + if (data->alarm_en & MAX6650_ALRM_TACH) + return 0444; + break; + default: + break; + } + break; + case hwmon_pwm: + switch (attr) { + case hwmon_pwm_input: + case hwmon_pwm_enable: + return 0644; + default: + break; + } + break; + default: + break; + } + return 0; +} + +static const struct hwmon_channel_info *max6650_info[] = { + HWMON_CHANNEL_INFO(fan, HWMON_F_INPUT | HWMON_F_TARGET | HWMON_F_DIV | + HWMON_F_MIN_ALARM | HWMON_F_MAX_ALARM | + HWMON_F_FAULT, + HWMON_F_INPUT, HWMON_F_INPUT, HWMON_F_INPUT), + HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), + NULL +}; + +static const struct hwmon_ops max6650_hwmon_ops = { + .read = max6650_read, + .write = max6650_write, + .is_visible = max6650_is_visible, +}; + +static const struct hwmon_chip_info max6650_chip_info = { + .ops = &max6650_hwmon_ops, + .info = max6650_info, +}; static int max6650_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct thermal_cooling_device *cooling_dev; struct device *dev = &client->dev; const struct of_device_id *of_id = of_match_device(of_match_ptr(max6650_dt_match), dev); @@ -767,37 +784,23 @@ static int max6650_probe(struct i2c_client *client, if (err) return err; - data->groups[0] = &max6650_group; - /* 3 additional fan inputs for the MAX6651 */ - if (data->nr_fans == 4) - data->groups[1] = &max6651_group; - - hwmon_dev = devm_hwmon_device_register_with_groups(dev, - client->name, data, - data->groups); + hwmon_dev = devm_hwmon_device_register_with_info(dev, + client->name, data, + &max6650_chip_info, + max6650_groups); err = PTR_ERR_OR_ZERO(hwmon_dev); if (err) return err; -#if IS_ENABLED(CONFIG_THERMAL) - data->cooling_dev = - thermal_of_cooling_device_register(client->dev.of_node, - client->name, data, - &max6650_cooling_ops); - if (IS_ERR(data->cooling_dev)) - dev_warn(&client->dev, - "thermal cooling device register failed: %ld\n", - PTR_ERR(data->cooling_dev)); -#endif - return 0; -} - -static int max6650_remove(struct i2c_client *client) -{ - struct max6650_data *data = i2c_get_clientdata(client); - - if (!IS_ERR(data->cooling_dev)) - thermal_cooling_device_unregister(data->cooling_dev); + if (IS_ENABLED(CONFIG_THERMAL)) { + cooling_dev = devm_thermal_of_cooling_device_register(dev, + dev->of_node, client->name, + data, &max6650_cooling_ops); + if (IS_ERR(cooling_dev)) { + dev_warn(dev, "thermal cooling device register failed: %ld\n", + PTR_ERR(cooling_dev)); + } + } return 0; } @@ -815,7 +818,6 @@ static struct i2c_driver max6650_driver = { .of_match_table = of_match_ptr(max6650_dt_match), }, .probe = max6650_probe, - .remove = max6650_remove, .id_table = max6650_id, }; diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index e7dff5febe16..7efa6bfef060 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -20,6 +20,7 @@ * * Chip #vin #fan #pwm #temp chip IDs man ID * nct6106d 9 3 3 6+3 0xc450 0xc1 0x5ca3 + * nct6116d 9 5 5 3+3 0xd280 0xc1 0x5ca3 * nct6775f 9 4 3 6+3 0xb470 0xc1 0x5ca3 * nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3 * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3 @@ -58,12 +59,13 @@ #define USE_ALTERNATE -enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792, nct6793, - nct6795, nct6796, nct6797, nct6798 }; +enum kinds { nct6106, nct6116, nct6775, nct6776, nct6779, nct6791, nct6792, + nct6793, nct6795, nct6796, nct6797, nct6798 }; /* used to set data->name = nct6775_device_names[data->sio_kind] */ static const char * const nct6775_device_names[] = { "nct6106", + "nct6116", "nct6775", "nct6776", "nct6779", @@ -78,6 +80,7 @@ static const char * const nct6775_device_names[] = { static const char * const nct6775_sio_names[] __initconst = { "NCT6106D", + "NCT6116D", "NCT6775F", "NCT6776D/F", "NCT6779D", @@ -115,6 +118,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ #define SIO_NCT6106_ID 0xc450 +#define SIO_NCT6116_ID 0xd280 #define SIO_NCT6775_ID 0xb470 #define SIO_NCT6776_ID 0xc330 #define SIO_NCT6779_ID 0xc560 @@ -825,10 +829,8 @@ static const u16 NCT6106_FAN_PULSE_SHIFT[] = { 0, 2, 4 }; static const u8 NCT6106_REG_PWM_MODE[] = { 0xf3, 0xf3, 0xf3 }; static const u8 NCT6106_PWM_MODE_MASK[] = { 0x01, 0x02, 0x04 }; -static const u16 NCT6106_REG_PWM[] = { 0x119, 0x129, 0x139 }; static const u16 NCT6106_REG_PWM_READ[] = { 0x4a, 0x4b, 0x4c }; static const u16 NCT6106_REG_FAN_MODE[] = { 0x113, 0x123, 0x133 }; -static const u16 NCT6106_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130 }; static const u16 NCT6106_REG_TEMP_SOURCE[] = { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5 }; @@ -852,7 +854,7 @@ static const u16 NCT6106_REG_TARGET[] = { 0x111, 0x121, 0x131 }; static const u16 NCT6106_REG_WEIGHT_TEMP_SEL[] = { 0x168, 0x178, 0x188 }; static const u16 NCT6106_REG_WEIGHT_TEMP_STEP[] = { 0x169, 0x179, 0x189 }; static const u16 NCT6106_REG_WEIGHT_TEMP_STEP_TOL[] = { 0x16a, 0x17a, 0x18a }; -static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x17c }; +static const u16 NCT6106_REG_WEIGHT_DUTY_STEP[] = { 0x16b, 0x17b, 0x18b }; static const u16 NCT6106_REG_WEIGHT_TEMP_BASE[] = { 0x16c, 0x17c, 0x18c }; static const u16 NCT6106_REG_WEIGHT_DUTY_BASE[] = { 0x16d, 0x17d, 0x18d }; @@ -896,6 +898,70 @@ static const u16 NCT6106_REG_TEMP_CRIT[32] = { [12] = 0x205, }; +/* NCT6112D/NCT6114D/NCT6116D specific data */ + +static const u16 NCT6116_REG_FAN[] = { 0x20, 0x22, 0x24, 0x26, 0x28 }; +static const u16 NCT6116_REG_FAN_MIN[] = { 0xe0, 0xe2, 0xe4, 0xe6, 0xe8 }; +static const u16 NCT6116_REG_FAN_PULSES[] = { 0xf6, 0xf6, 0xf6, 0xf6, 0xf5 }; +static const u16 NCT6116_FAN_PULSE_SHIFT[] = { 0, 2, 4, 6, 6 }; + +static const u16 NCT6116_REG_PWM[] = { 0x119, 0x129, 0x139, 0x199, 0x1a9 }; +static const u16 NCT6116_REG_FAN_MODE[] = { 0x113, 0x123, 0x133, 0x193, 0x1a3 }; +static const u16 NCT6116_REG_TEMP_SEL[] = { 0x110, 0x120, 0x130, 0x190, 0x1a0 }; +static const u16 NCT6116_REG_TEMP_SOURCE[] = { + 0xb0, 0xb1, 0xb2 }; + +static const u16 NCT6116_REG_CRITICAL_TEMP[] = { + 0x11a, 0x12a, 0x13a, 0x19a, 0x1aa }; +static const u16 NCT6116_REG_CRITICAL_TEMP_TOLERANCE[] = { + 0x11b, 0x12b, 0x13b, 0x19b, 0x1ab }; + +static const u16 NCT6116_REG_CRITICAL_PWM_ENABLE[] = { + 0x11c, 0x12c, 0x13c, 0x19c, 0x1ac }; +static const u16 NCT6116_REG_CRITICAL_PWM[] = { + 0x11d, 0x12d, 0x13d, 0x19d, 0x1ad }; + +static const u16 NCT6116_REG_FAN_STEP_UP_TIME[] = { + 0x114, 0x124, 0x134, 0x194, 0x1a4 }; +static const u16 NCT6116_REG_FAN_STEP_DOWN_TIME[] = { + 0x115, 0x125, 0x135, 0x195, 0x1a5 }; +static const u16 NCT6116_REG_FAN_STOP_OUTPUT[] = { + 0x116, 0x126, 0x136, 0x196, 0x1a6 }; +static const u16 NCT6116_REG_FAN_START_OUTPUT[] = { + 0x117, 0x127, 0x137, 0x197, 0x1a7 }; +static const u16 NCT6116_REG_FAN_STOP_TIME[] = { + 0x118, 0x128, 0x138, 0x198, 0x1a8 }; +static const u16 NCT6116_REG_TOLERANCE_H[] = { + 0x112, 0x122, 0x132, 0x192, 0x1a2 }; + +static const u16 NCT6116_REG_TARGET[] = { + 0x111, 0x121, 0x131, 0x191, 0x1a1 }; + +static const u16 NCT6116_REG_AUTO_TEMP[] = { + 0x160, 0x170, 0x180, 0x1d0, 0x1e0 }; +static const u16 NCT6116_REG_AUTO_PWM[] = { + 0x164, 0x174, 0x184, 0x1d4, 0x1e4 }; + +static const s8 NCT6116_ALARM_BITS[] = { + 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */ + 9, -1, -1, -1, -1, -1, -1, /* in8..in9 */ + -1, /* unused */ + 32, 33, 34, 35, 36, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 16, 17, 18, -1, -1, -1, /* temp1..temp6 */ + 48, -1 /* intrusion0, intrusion1 */ +}; + +static const s8 NCT6116_BEEP_BITS[] = { + 0, 1, 2, 3, 4, 5, 7, 8, /* in0.. in7 */ + 9, 10, 11, 12, -1, -1, -1, /* in8..in14 */ + 32, /* global beep enable */ + 24, 25, 26, 27, 28, /* fan1..fan5 */ + -1, -1, -1, /* unused */ + 16, 17, 18, -1, -1, -1, /* temp1..temp6 */ + 34, -1 /* intrusion0, intrusion1 */ +}; + static enum pwm_enable reg_to_pwm_enable(int pwm, int mode) { if (mode == 0 && pwm == 255) @@ -1294,6 +1360,11 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) return reg == 0x20 || reg == 0x22 || reg == 0x24 || reg == 0xe0 || reg == 0xe2 || reg == 0xe4 || reg == 0x111 || reg == 0x121 || reg == 0x131; + case nct6116: + return reg == 0x20 || reg == 0x22 || reg == 0x24 || + reg == 0x26 || reg == 0x28 || reg == 0xe0 || reg == 0xe2 || + reg == 0xe4 || reg == 0xe6 || reg == 0xe8 || reg == 0x111 || + reg == 0x121 || reg == 0x131 || reg == 0x191 || reg == 0x1a1; case nct6775: return (((reg & 0xff00) == 0x100 || (reg & 0xff00) == 0x200) && @@ -1673,6 +1744,7 @@ static void nct6775_update_pwm_limits(struct device *dev) data->auto_pwm[i][data->auto_pwm_num] = 0xff; break; case nct6106: + case nct6116: case nct6779: case nct6791: case nct6792: @@ -3109,6 +3181,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, case nct6776: break; /* always enabled, nothing to do */ case nct6106: + case nct6116: case nct6779: case nct6791: case nct6792: @@ -3535,6 +3608,23 @@ nct6775_check_fan_inputs(struct nct6775_data *data) fan3pin = !(cr24 & 0x80); pwm3pin = cr24 & 0x08; + } else if (data->kind == nct6116) { + int cr1a = superio_inb(sioreg, 0x1a); + int cr1b = superio_inb(sioreg, 0x1b); + int cr24 = superio_inb(sioreg, 0x24); + int cr2a = superio_inb(sioreg, 0x2a); + int cr2b = superio_inb(sioreg, 0x2b); + int cr2f = superio_inb(sioreg, 0x2f); + + fan3pin = !(cr2b & 0x10); + fan4pin = (cr2b & 0x80) || // pin 1(2) + (!(cr2f & 0x10) && (cr1a & 0x04)); // pin 65(66) + fan5pin = (cr2b & 0x80) || // pin 126(127) + (!(cr1b & 0x03) && (cr2a & 0x02)); // pin 94(96) + + pwm3pin = fan3pin && (cr24 & 0x08); + pwm4pin = fan4pin; + pwm5pin = fan5pin; } else { /* * NCT6779D, NCT6791D, NCT6792D, NCT6793D, NCT6795D, NCT6796D, @@ -3764,7 +3854,8 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_FAN_TIME[0] = NCT6106_REG_FAN_STOP_TIME; data->REG_FAN_TIME[1] = NCT6106_REG_FAN_STEP_UP_TIME; data->REG_FAN_TIME[2] = NCT6106_REG_FAN_STEP_DOWN_TIME; - data->REG_PWM[0] = NCT6106_REG_PWM; + data->REG_TOLERANCE_H = NCT6106_REG_TOLERANCE_H; + data->REG_PWM[0] = NCT6116_REG_PWM; data->REG_PWM[1] = NCT6106_REG_FAN_START_OUTPUT; data->REG_PWM[2] = NCT6106_REG_FAN_STOP_OUTPUT; data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP; @@ -3783,7 +3874,7 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_CRITICAL_PWM = NCT6106_REG_CRITICAL_PWM; data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; data->REG_TEMP_SOURCE = NCT6106_REG_TEMP_SOURCE; - data->REG_TEMP_SEL = NCT6106_REG_TEMP_SEL; + data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL; data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL; @@ -3806,6 +3897,79 @@ static int nct6775_probe(struct platform_device *pdev) reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H; break; + case nct6116: + data->in_num = 9; + data->pwm_num = 3; + data->auto_pwm_num = 4; + data->temp_fixed_num = 3; + data->num_temp_alarms = 3; + data->num_temp_beeps = 3; + + data->fan_from_reg = fan_from_reg13; + data->fan_from_reg_min = fan_from_reg13; + + data->temp_label = nct6776_temp_label; + data->temp_mask = NCT6776_TEMP_MASK; + data->virt_temp_mask = NCT6776_VIRT_TEMP_MASK; + + data->REG_VBAT = NCT6106_REG_VBAT; + data->REG_DIODE = NCT6106_REG_DIODE; + data->DIODE_MASK = NCT6106_DIODE_MASK; + data->REG_VIN = NCT6106_REG_IN; + data->REG_IN_MINMAX[0] = NCT6106_REG_IN_MIN; + data->REG_IN_MINMAX[1] = NCT6106_REG_IN_MAX; + data->REG_TARGET = NCT6116_REG_TARGET; + data->REG_FAN = NCT6116_REG_FAN; + data->REG_FAN_MODE = NCT6116_REG_FAN_MODE; + data->REG_FAN_MIN = NCT6116_REG_FAN_MIN; + data->REG_FAN_PULSES = NCT6116_REG_FAN_PULSES; + data->FAN_PULSE_SHIFT = NCT6116_FAN_PULSE_SHIFT; + data->REG_FAN_TIME[0] = NCT6116_REG_FAN_STOP_TIME; + data->REG_FAN_TIME[1] = NCT6116_REG_FAN_STEP_UP_TIME; + data->REG_FAN_TIME[2] = NCT6116_REG_FAN_STEP_DOWN_TIME; + data->REG_TOLERANCE_H = NCT6116_REG_TOLERANCE_H; + data->REG_PWM[0] = NCT6116_REG_PWM; + data->REG_PWM[1] = NCT6116_REG_FAN_START_OUTPUT; + data->REG_PWM[2] = NCT6116_REG_FAN_STOP_OUTPUT; + data->REG_PWM[5] = NCT6106_REG_WEIGHT_DUTY_STEP; + data->REG_PWM[6] = NCT6106_REG_WEIGHT_DUTY_BASE; + data->REG_PWM_READ = NCT6106_REG_PWM_READ; + data->REG_PWM_MODE = NCT6106_REG_PWM_MODE; + data->PWM_MODE_MASK = NCT6106_PWM_MODE_MASK; + data->REG_AUTO_TEMP = NCT6116_REG_AUTO_TEMP; + data->REG_AUTO_PWM = NCT6116_REG_AUTO_PWM; + data->REG_CRITICAL_TEMP = NCT6116_REG_CRITICAL_TEMP; + data->REG_CRITICAL_TEMP_TOLERANCE + = NCT6116_REG_CRITICAL_TEMP_TOLERANCE; + data->REG_CRITICAL_PWM_ENABLE = NCT6116_REG_CRITICAL_PWM_ENABLE; + data->CRITICAL_PWM_ENABLE_MASK + = NCT6106_CRITICAL_PWM_ENABLE_MASK; + data->REG_CRITICAL_PWM = NCT6116_REG_CRITICAL_PWM; + data->REG_TEMP_OFFSET = NCT6106_REG_TEMP_OFFSET; + data->REG_TEMP_SOURCE = NCT6116_REG_TEMP_SOURCE; + data->REG_TEMP_SEL = NCT6116_REG_TEMP_SEL; + data->REG_WEIGHT_TEMP_SEL = NCT6106_REG_WEIGHT_TEMP_SEL; + data->REG_WEIGHT_TEMP[0] = NCT6106_REG_WEIGHT_TEMP_STEP; + data->REG_WEIGHT_TEMP[1] = NCT6106_REG_WEIGHT_TEMP_STEP_TOL; + data->REG_WEIGHT_TEMP[2] = NCT6106_REG_WEIGHT_TEMP_BASE; + data->REG_ALARM = NCT6106_REG_ALARM; + data->ALARM_BITS = NCT6116_ALARM_BITS; + data->REG_BEEP = NCT6106_REG_BEEP; + data->BEEP_BITS = NCT6116_BEEP_BITS; + + reg_temp = NCT6106_REG_TEMP; + reg_temp_mon = NCT6106_REG_TEMP_MON; + num_reg_temp = ARRAY_SIZE(NCT6106_REG_TEMP); + num_reg_temp_mon = ARRAY_SIZE(NCT6106_REG_TEMP_MON); + reg_temp_over = NCT6106_REG_TEMP_OVER; + reg_temp_hyst = NCT6106_REG_TEMP_HYST; + reg_temp_config = NCT6106_REG_TEMP_CONFIG; + reg_temp_alternate = NCT6106_REG_TEMP_ALTERNATE; + reg_temp_crit = NCT6106_REG_TEMP_CRIT; + reg_temp_crit_l = NCT6106_REG_TEMP_CRIT_L; + reg_temp_crit_h = NCT6106_REG_TEMP_CRIT_H; + + break; case nct6775: data->in_num = 9; data->pwm_num = 3; @@ -4351,6 +4515,7 @@ static int nct6775_probe(struct platform_device *pdev) data->have_vid = (cr2a & 0x60) == 0x40; break; case nct6106: + case nct6116: case nct6779: case nct6791: case nct6792: @@ -4380,6 +4545,7 @@ static int nct6775_probe(struct platform_device *pdev) NCT6775_REG_CR_FAN_DEBOUNCE); switch (data->kind) { case nct6106: + case nct6116: tmp |= 0xe0; break; case nct6775: @@ -4575,6 +4741,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) case SIO_NCT6106_ID: sio_data->kind = nct6106; break; + case SIO_NCT6116_ID: + sio_data->kind = nct6116; + break; case SIO_NCT6775_ID: sio_data->kind = nct6775; break; diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c index ec7bcf8d7cd6..f3dd2a17bd42 100644 --- a/drivers/hwmon/nct7802.c +++ b/drivers/hwmon/nct7802.c @@ -704,7 +704,7 @@ static struct attribute *nct7802_in_attrs[] = { &sensor_dev_attr_in3_alarm.dev_attr.attr, &sensor_dev_attr_in3_beep.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, /* 17 */ + &sensor_dev_attr_in4_input.dev_attr.attr, /* 16 */ &sensor_dev_attr_in4_min.dev_attr.attr, &sensor_dev_attr_in4_max.dev_attr.attr, &sensor_dev_attr_in4_alarm.dev_attr.attr, @@ -730,9 +730,9 @@ static umode_t nct7802_in_is_visible(struct kobject *kobj, if (index >= 6 && index < 11 && (reg & 0x03) != 0x03) /* VSEN1 */ return 0; - if (index >= 11 && index < 17 && (reg & 0x0c) != 0x0c) /* VSEN2 */ + if (index >= 11 && index < 16 && (reg & 0x0c) != 0x0c) /* VSEN2 */ return 0; - if (index >= 17 && (reg & 0x30) != 0x30) /* VSEN3 */ + if (index >= 16 && (reg & 0x30) != 0x30) /* VSEN3 */ return 0; return attr->mode; diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c index 58a957445484..95b447cfa24c 100644 --- a/drivers/hwmon/nct7904.c +++ b/drivers/hwmon/nct7904.c @@ -4,6 +4,9 @@ * * Copyright (c) 2015 Kontron * Author: Vadim V. Vlasov <vvlasov@dev.rtsoft.ru> + * + * Copyright (c) 2019 Advantech + * Author: Amy.Shih <amy.shih@advantech.com.tw> */ #include <linux/module.h> @@ -43,16 +46,44 @@ #define DTS_T_CTRL1_REG 0x27 #define VT_ADC_MD_REG 0x2E +#define VSEN1_HV_LL_REG 0x02 /* Bank 1; 2 regs (HV/LV) per sensor */ +#define VSEN1_LV_LL_REG 0x03 /* Bank 1; 2 regs (HV/LV) per sensor */ +#define VSEN1_HV_HL_REG 0x00 /* Bank 1; 2 regs (HV/LV) per sensor */ +#define VSEN1_LV_HL_REG 0x01 /* Bank 1; 2 regs (HV/LV) per sensor */ +#define SMI_STS1_REG 0xC1 /* Bank 0; SMI Status Register */ +#define SMI_STS3_REG 0xC3 /* Bank 0; SMI Status Register */ +#define SMI_STS5_REG 0xC5 /* Bank 0; SMI Status Register */ +#define SMI_STS7_REG 0xC7 /* Bank 0; SMI Status Register */ +#define SMI_STS8_REG 0xC8 /* Bank 0; SMI Status Register */ + #define VSEN1_HV_REG 0x40 /* Bank 0; 2 regs (HV/LV) per sensor */ #define TEMP_CH1_HV_REG 0x42 /* Bank 0; same as VSEN2_HV */ #define LTD_HV_REG 0x62 /* Bank 0; 2 regs in VSEN range */ +#define LTD_HV_HL_REG 0x44 /* Bank 1; 1 reg for LTD */ +#define LTD_LV_HL_REG 0x45 /* Bank 1; 1 reg for LTD */ +#define LTD_HV_LL_REG 0x46 /* Bank 1; 1 reg for LTD */ +#define LTD_LV_LL_REG 0x47 /* Bank 1; 1 reg for LTD */ +#define TEMP_CH1_CH_REG 0x05 /* Bank 1; 1 reg for LTD */ +#define TEMP_CH1_W_REG 0x06 /* Bank 1; 1 reg for LTD */ +#define TEMP_CH1_WH_REG 0x07 /* Bank 1; 1 reg for LTD */ +#define TEMP_CH1_C_REG 0x04 /* Bank 1; 1 reg per sensor */ +#define DTS_T_CPU1_C_REG 0x90 /* Bank 1; 1 reg per sensor */ +#define DTS_T_CPU1_CH_REG 0x91 /* Bank 1; 1 reg per sensor */ +#define DTS_T_CPU1_W_REG 0x92 /* Bank 1; 1 reg per sensor */ +#define DTS_T_CPU1_WH_REG 0x93 /* Bank 1; 1 reg per sensor */ #define FANIN1_HV_REG 0x80 /* Bank 0; 2 regs (HV/LV) per sensor */ +#define FANIN1_HV_HL_REG 0x60 /* Bank 1; 2 regs (HV/LV) per sensor */ +#define FANIN1_LV_HL_REG 0x61 /* Bank 1; 2 regs (HV/LV) per sensor */ #define T_CPU1_HV_REG 0xA0 /* Bank 0; 2 regs (HV/LV) per sensor */ #define PRTS_REG 0x03 /* Bank 2 */ +#define PFE_REG 0x00 /* Bank 2; PECI Function Enable */ +#define TSI_CTRL_REG 0x50 /* Bank 2; TSI Control Register */ #define FANCTL1_FMR_REG 0x00 /* Bank 3; 1 reg per channel */ #define FANCTL1_OUT_REG 0x10 /* Bank 3; 1 reg per channel */ +#define ENABLE_TSI BIT(1) + static const unsigned short normal_i2c[] = { 0x2d, 0x2e, I2C_CLIENT_END }; @@ -65,6 +96,9 @@ struct nct7904_data { u32 vsen_mask; u32 tcpu_mask; u8 fan_mode[FANCTL_MAX]; + u8 enable_dts; + u8 has_dts; + u8 temp_mode; /* 0: TR mode, 1: TD mode */ }; /* Access functions */ @@ -163,6 +197,25 @@ static int nct7904_read_fan(struct device *dev, u32 attr, int channel, rpm = 1350000 / cnt; *val = rpm; return 0; + case hwmon_fan_min: + ret = nct7904_read_reg16(data, BANK_1, + FANIN1_HV_HL_REG + channel * 2); + if (ret < 0) + return ret; + cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f); + if (cnt == 0x1fff) + rpm = 0; + else + rpm = 1350000 / cnt; + *val = rpm; + return 0; + case hwmon_fan_alarm: + ret = nct7904_read_reg(data, BANK_0, + SMI_STS5_REG + (channel >> 3)); + if (ret < 0) + return ret; + *val = (ret >> (channel & 0x07)) & 1; + return 0; default: return -EOPNOTSUPP; } @@ -172,8 +225,20 @@ static umode_t nct7904_fan_is_visible(const void *_data, u32 attr, int channel) { const struct nct7904_data *data = _data; - if (attr == hwmon_fan_input && data->fanin_mask & (1 << channel)) - return 0444; + switch (attr) { + case hwmon_fan_input: + case hwmon_fan_alarm: + if (data->fanin_mask & (1 << channel)) + return 0444; + break; + case hwmon_fan_min: + if (data->fanin_mask & (1 << channel)) + return 0644; + break; + default: + break; + } + return 0; } @@ -204,6 +269,37 @@ static int nct7904_read_in(struct device *dev, u32 attr, int channel, volt *= 6; /* 0.006V scale */ *val = volt; return 0; + case hwmon_in_min: + ret = nct7904_read_reg16(data, BANK_1, + VSEN1_HV_LL_REG + index * 4); + if (ret < 0) + return ret; + volt = ((ret & 0xff00) >> 5) | (ret & 0x7); + if (index < 14) + volt *= 2; /* 0.002V scale */ + else + volt *= 6; /* 0.006V scale */ + *val = volt; + return 0; + case hwmon_in_max: + ret = nct7904_read_reg16(data, BANK_1, + VSEN1_HV_HL_REG + index * 4); + if (ret < 0) + return ret; + volt = ((ret & 0xff00) >> 5) | (ret & 0x7); + if (index < 14) + volt *= 2; /* 0.002V scale */ + else + volt *= 6; /* 0.006V scale */ + *val = volt; + return 0; + case hwmon_in_alarm: + ret = nct7904_read_reg(data, BANK_0, + SMI_STS1_REG + (index >> 3)); + if (ret < 0) + return ret; + *val = (ret >> (index & 0x07)) & 1; + return 0; default: return -EOPNOTSUPP; } @@ -214,9 +310,20 @@ static umode_t nct7904_in_is_visible(const void *_data, u32 attr, int channel) const struct nct7904_data *data = _data; int index = nct7904_chan_to_index[channel]; - if (channel > 0 && attr == hwmon_in_input && - (data->vsen_mask & BIT(index))) - return 0444; + switch (attr) { + case hwmon_in_input: + case hwmon_in_alarm: + if (channel > 0 && (data->vsen_mask & BIT(index))) + return 0444; + break; + case hwmon_in_min: + case hwmon_in_max: + if (channel > 0 && (data->vsen_mask & BIT(index))) + return 0644; + break; + default: + break; + } return 0; } @@ -226,36 +333,146 @@ static int nct7904_read_temp(struct device *dev, u32 attr, int channel, { struct nct7904_data *data = dev_get_drvdata(dev); int ret, temp; + unsigned int reg1, reg2, reg3; switch (attr) { case hwmon_temp_input: - if (channel == 0) + if (channel == 4) ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG); + else if (channel < 5) + ret = nct7904_read_reg16(data, BANK_0, + TEMP_CH1_HV_REG + channel * 4); else ret = nct7904_read_reg16(data, BANK_0, - T_CPU1_HV_REG + (channel - 1) * 2); + T_CPU1_HV_REG + (channel - 5) + * 2); if (ret < 0) return ret; temp = ((ret & 0xff00) >> 5) | (ret & 0x7); *val = sign_extend32(temp, 10) * 125; return 0; + case hwmon_temp_alarm: + if (channel == 4) { + ret = nct7904_read_reg(data, BANK_0, + SMI_STS3_REG); + if (ret < 0) + return ret; + *val = (ret >> 1) & 1; + } else if (channel < 4) { + ret = nct7904_read_reg(data, BANK_0, + SMI_STS1_REG); + if (ret < 0) + return ret; + *val = (ret >> (((channel * 2) + 1) & 0x07)) & 1; + } else { + if ((channel - 5) < 4) { + ret = nct7904_read_reg(data, BANK_0, + SMI_STS7_REG + + ((channel - 5) >> 3)); + if (ret < 0) + return ret; + *val = (ret >> ((channel - 5) & 0x07)) & 1; + } else { + ret = nct7904_read_reg(data, BANK_0, + SMI_STS8_REG + + ((channel - 5) >> 3)); + if (ret < 0) + return ret; + *val = (ret >> (((channel - 5) & 0x07) - 4)) + & 1; + } + } + return 0; + case hwmon_temp_type: + if (channel < 5) { + if ((data->tcpu_mask >> channel) & 0x01) { + if ((data->temp_mode >> channel) & 0x01) + *val = 3; /* TD */ + else + *val = 4; /* TR */ + } else { + *val = 0; + } + } else { + if ((data->has_dts >> (channel - 5)) & 0x01) { + if (data->enable_dts & ENABLE_TSI) + *val = 5; /* TSI */ + else + *val = 6; /* PECI */ + } else { + *val = 0; + } + } + return 0; + case hwmon_temp_max: + reg1 = LTD_HV_LL_REG; + reg2 = TEMP_CH1_W_REG; + reg3 = DTS_T_CPU1_W_REG; + break; + case hwmon_temp_max_hyst: + reg1 = LTD_LV_LL_REG; + reg2 = TEMP_CH1_WH_REG; + reg3 = DTS_T_CPU1_WH_REG; + break; + case hwmon_temp_crit: + reg1 = LTD_HV_HL_REG; + reg2 = TEMP_CH1_C_REG; + reg3 = DTS_T_CPU1_C_REG; + break; + case hwmon_temp_crit_hyst: + reg1 = LTD_LV_HL_REG; + reg2 = TEMP_CH1_CH_REG; + reg3 = DTS_T_CPU1_CH_REG; + break; default: return -EOPNOTSUPP; } + + if (channel == 4) + ret = nct7904_read_reg(data, BANK_1, reg1); + else if (channel < 5) + ret = nct7904_read_reg(data, BANK_1, + reg2 + channel * 8); + else + ret = nct7904_read_reg(data, BANK_1, + reg3 + (channel - 5) * 4); + + if (ret < 0) + return ret; + *val = ret * 1000; + return 0; } static umode_t nct7904_temp_is_visible(const void *_data, u32 attr, int channel) { const struct nct7904_data *data = _data; - if (attr == hwmon_temp_input) { - if (channel == 0) { - if (data->vsen_mask & BIT(17)) + switch (attr) { + case hwmon_temp_input: + case hwmon_temp_alarm: + case hwmon_temp_type: + if (channel < 5) { + if (data->tcpu_mask & BIT(channel)) return 0444; } else { - if (data->tcpu_mask & BIT(channel - 1)) + if (data->has_dts & BIT(channel - 5)) return 0444; } + break; + case hwmon_temp_max: + case hwmon_temp_max_hyst: + case hwmon_temp_crit: + case hwmon_temp_crit_hyst: + if (channel < 5) { + if (data->tcpu_mask & BIT(channel)) + return 0644; + } else { + if (data->has_dts & BIT(channel - 5)) + return 0644; + } + break; + default: + break; } return 0; @@ -286,6 +503,137 @@ static int nct7904_read_pwm(struct device *dev, u32 attr, int channel, } } +static int nct7904_write_temp(struct device *dev, u32 attr, int channel, + long val) +{ + struct nct7904_data *data = dev_get_drvdata(dev); + int ret; + unsigned int reg1, reg2, reg3; + + val = clamp_val(val / 1000, -128, 127); + + switch (attr) { + case hwmon_temp_max: + reg1 = LTD_HV_LL_REG; + reg2 = TEMP_CH1_W_REG; + reg3 = DTS_T_CPU1_W_REG; + break; + case hwmon_temp_max_hyst: + reg1 = LTD_LV_LL_REG; + reg2 = TEMP_CH1_WH_REG; + reg3 = DTS_T_CPU1_WH_REG; + break; + case hwmon_temp_crit: + reg1 = LTD_HV_HL_REG; + reg2 = TEMP_CH1_C_REG; + reg3 = DTS_T_CPU1_C_REG; + break; + case hwmon_temp_crit_hyst: + reg1 = LTD_LV_HL_REG; + reg2 = TEMP_CH1_CH_REG; + reg3 = DTS_T_CPU1_CH_REG; + break; + default: + return -EOPNOTSUPP; + } + if (channel == 4) + ret = nct7904_write_reg(data, BANK_1, reg1, val); + else if (channel < 5) + ret = nct7904_write_reg(data, BANK_1, + reg2 + channel * 8, val); + else + ret = nct7904_write_reg(data, BANK_1, + reg3 + (channel - 5) * 4, val); + + return ret; +} + +static int nct7904_write_fan(struct device *dev, u32 attr, int channel, + long val) +{ + struct nct7904_data *data = dev_get_drvdata(dev); + int ret; + u8 tmp; + + switch (attr) { + case hwmon_fan_min: + if (val <= 0) + return -EINVAL; + + val = clamp_val(DIV_ROUND_CLOSEST(1350000, val), 1, 0x1fff); + tmp = (val >> 5) & 0xff; + ret = nct7904_write_reg(data, BANK_1, + FANIN1_HV_HL_REG + channel * 2, tmp); + if (ret < 0) + return ret; + tmp = val & 0x1f; + ret = nct7904_write_reg(data, BANK_1, + FANIN1_LV_HL_REG + channel * 2, tmp); + return ret; + default: + return -EOPNOTSUPP; + } +} + +static int nct7904_write_in(struct device *dev, u32 attr, int channel, + long val) +{ + struct nct7904_data *data = dev_get_drvdata(dev); + int ret, index, tmp; + + index = nct7904_chan_to_index[channel]; + + if (index < 14) + val = val / 2; /* 0.002V scale */ + else + val = val / 6; /* 0.006V scale */ + + val = clamp_val(val, 0, 0x7ff); + + switch (attr) { + case hwmon_in_min: + tmp = nct7904_read_reg(data, BANK_1, + VSEN1_LV_LL_REG + index * 4); + if (tmp < 0) + return tmp; + tmp &= ~0x7; + tmp |= val & 0x7; + ret = nct7904_write_reg(data, BANK_1, + VSEN1_LV_LL_REG + index * 4, tmp); + if (ret < 0) + return ret; + tmp = nct7904_read_reg(data, BANK_1, + VSEN1_HV_LL_REG + index * 4); + if (tmp < 0) + return tmp; + tmp = (val >> 3) & 0xff; + ret = nct7904_write_reg(data, BANK_1, + VSEN1_HV_LL_REG + index * 4, tmp); + return ret; + case hwmon_in_max: + tmp = nct7904_read_reg(data, BANK_1, + VSEN1_LV_HL_REG + index * 4); + if (tmp < 0) + return tmp; + tmp &= ~0x7; + tmp |= val & 0x7; + ret = nct7904_write_reg(data, BANK_1, + VSEN1_LV_HL_REG + index * 4, tmp); + if (ret < 0) + return ret; + tmp = nct7904_read_reg(data, BANK_1, + VSEN1_HV_HL_REG + index * 4); + if (tmp < 0) + return tmp; + tmp = (val >> 3) & 0xff; + ret = nct7904_write_reg(data, BANK_1, + VSEN1_HV_HL_REG + index * 4, tmp); + return ret; + default: + return -EOPNOTSUPP; + } +} + static int nct7904_write_pwm(struct device *dev, u32 attr, int channel, long val) { @@ -343,8 +691,14 @@ static int nct7904_write(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long val) { switch (type) { + case hwmon_in: + return nct7904_write_in(dev, attr, channel, val); + case hwmon_fan: + return nct7904_write_fan(dev, attr, channel, val); case hwmon_pwm: return nct7904_write_pwm(dev, attr, channel, val); + case hwmon_temp: + return nct7904_write_temp(dev, attr, channel, val); default: return -EOPNOTSUPP; } @@ -393,51 +747,91 @@ static int nct7904_detect(struct i2c_client *client, static const struct hwmon_channel_info *nct7904_info[] = { HWMON_CHANNEL_INFO(in, - HWMON_I_INPUT, /* dummy, skipped in is_visible */ - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT, - HWMON_I_INPUT), + /* dummy, skipped in is_visible */ + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM, + HWMON_I_INPUT | HWMON_I_MIN | HWMON_I_MAX | + HWMON_I_ALARM), HWMON_CHANNEL_INFO(fan, - HWMON_F_INPUT, - HWMON_F_INPUT, - HWMON_F_INPUT, - HWMON_F_INPUT, - HWMON_F_INPUT, - HWMON_F_INPUT, - HWMON_F_INPUT, - HWMON_F_INPUT), + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM, + HWMON_F_INPUT | HWMON_F_MIN | HWMON_F_ALARM), HWMON_CHANNEL_INFO(pwm, HWMON_PWM_INPUT | HWMON_PWM_ENABLE, HWMON_PWM_INPUT | HWMON_PWM_ENABLE, HWMON_PWM_INPUT | HWMON_PWM_ENABLE, HWMON_PWM_INPUT | HWMON_PWM_ENABLE), HWMON_CHANNEL_INFO(temp, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT, - HWMON_T_INPUT), + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST, + HWMON_T_INPUT | HWMON_T_ALARM | HWMON_T_MAX | + HWMON_T_MAX_HYST | HWMON_T_TYPE | HWMON_T_CRIT | + HWMON_T_CRIT_HYST), NULL }; @@ -460,6 +854,7 @@ static int nct7904_probe(struct i2c_client *client, struct device *dev = &client->dev; int ret, i; u32 mask; + u8 val, bit; data = devm_kzalloc(dev, sizeof(struct nct7904_data), GFP_KERNEL); if (!data) @@ -493,10 +888,68 @@ static int nct7904_probe(struct i2c_client *client, data->vsen_mask = mask; /* CPU_TEMP attributes */ - ret = nct7904_read_reg16(data, BANK_0, DTS_T_CTRL0_REG); + ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL0_REG); + if (ret < 0) + return ret; + + if ((ret & 0x6) == 0x6) + data->tcpu_mask |= 1; /* TR1 */ + if ((ret & 0x18) == 0x18) + data->tcpu_mask |= 2; /* TR2 */ + if ((ret & 0x20) == 0x20) + data->tcpu_mask |= 4; /* TR3 */ + if ((ret & 0x80) == 0x80) + data->tcpu_mask |= 8; /* TR4 */ + + /* LTD */ + ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG); + if (ret < 0) + return ret; + if ((ret & 0x02) == 0x02) + data->tcpu_mask |= 0x10; + + /* Multi-Function detecting for Volt and TR/TD */ + ret = nct7904_read_reg(data, BANK_0, VT_ADC_MD_REG); + if (ret < 0) + return ret; + + data->temp_mode = 0; + for (i = 0; i < 4; i++) { + val = (ret & (0x03 << i)) >> (i * 2); + bit = (1 << i); + if (val == 0) + data->tcpu_mask &= ~bit; + else if (val == 0x1 || val == 0x2) + data->temp_mode |= bit; + } + + /* PECI */ + ret = nct7904_read_reg(data, BANK_2, PFE_REG); if (ret < 0) return ret; - data->tcpu_mask = ((ret >> 8) & 0xf) | ((ret & 0xf) << 4); + if (ret & 0x80) { + data->enable_dts = 1; /* Enable DTS & PECI */ + } else { + ret = nct7904_read_reg(data, BANK_2, TSI_CTRL_REG); + if (ret < 0) + return ret; + if (ret & 0x80) + data->enable_dts = 0x3; /* Enable DTS & TSI */ + } + + /* Check DTS enable status */ + if (data->enable_dts) { + ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL0_REG); + if (ret < 0) + return ret; + data->has_dts = ret & 0xF; + if (data->enable_dts & ENABLE_TSI) { + ret = nct7904_read_reg(data, BANK_0, DTS_T_CTRL1_REG); + if (ret < 0) + return ret; + data->has_dts |= (ret & 0xF) << 4; + } + } for (i = 0; i < FANCTL_MAX; i++) { ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + i); diff --git a/drivers/hwmon/npcm750-pwm-fan.c b/drivers/hwmon/npcm750-pwm-fan.c index 09aaefa6fdb8..11a28609da3c 100644 --- a/drivers/hwmon/npcm750-pwm-fan.c +++ b/drivers/hwmon/npcm750-pwm-fan.c @@ -967,10 +967,8 @@ static int npcm7xx_pwm_fan_probe(struct platform_device *pdev) spin_lock_init(&data->fan_lock[i]); data->fan_irq[i] = platform_get_irq(pdev, i); - if (data->fan_irq[i] < 0) { - dev_err(dev, "get IRQ fan%d failed\n", i); + if (data->fan_irq[i] < 0) return data->fan_irq[i]; - } sprintf(name, "NPCM7XX-FAN-MD%d", i); ret = devm_request_irq(dev, data->fan_irq[i], npcm7xx_fan_isr, diff --git a/drivers/hwmon/occ/common.c b/drivers/hwmon/occ/common.c index 13a6290c8d25..30e18eb60da7 100644 --- a/drivers/hwmon/occ/common.c +++ b/drivers/hwmon/occ/common.c @@ -124,12 +124,12 @@ struct extended_sensor { static int occ_poll(struct occ *occ) { int rc; - u16 checksum = occ->poll_cmd_data + 1; + u16 checksum = occ->poll_cmd_data + occ->seq_no + 1; u8 cmd[8]; struct occ_poll_response_header *header; /* big endian */ - cmd[0] = 0; /* sequence number */ + cmd[0] = occ->seq_no++; /* sequence number */ cmd[1] = 0; /* cmd type */ cmd[2] = 0; /* data length msb */ cmd[3] = 1; /* data length lsb */ @@ -241,6 +241,12 @@ static ssize_t occ_show_temp_1(struct device *dev, val = get_unaligned_be16(&temp->sensor_id); break; case 1: + /* + * If a sensor reading has expired and couldn't be refreshed, + * OCC returns 0xFFFF for that sensor. + */ + if (temp->value == 0xFFFF) + return -EREMOTEIO; val = get_unaligned_be16(&temp->value) * 1000; break; default: @@ -402,8 +408,10 @@ static ssize_t occ_show_power_1(struct device *dev, static u64 occ_get_powr_avg(u64 *accum, u32 *samples) { - return div64_u64(get_unaligned_be64(accum) * 1000000ULL, - get_unaligned_be32(samples)); + u64 divisor = get_unaligned_be32(samples); + + return (divisor == 0) ? 0 : + div64_u64(get_unaligned_be64(accum) * 1000000ULL, divisor); } static ssize_t occ_show_power_2(struct device *dev, diff --git a/drivers/hwmon/occ/common.h b/drivers/hwmon/occ/common.h index fc13f3c73c47..67e6968b8978 100644 --- a/drivers/hwmon/occ/common.h +++ b/drivers/hwmon/occ/common.h @@ -95,6 +95,7 @@ struct occ { struct occ_sensors sensors; int powr_sample_time_us; /* average power sample time */ + u8 seq_no; u8 poll_cmd_data; /* to perform OCC poll command */ int (*send_cmd)(struct occ *occ, u8 *cmd); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 30751eb9550a..d62d69bb7e49 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -46,6 +46,15 @@ config SENSORS_IBM_CFFPS This driver can also be built as a module. If so, the module will be called ibm-cffps. +config SENSORS_INSPUR_IPSPS + tristate "INSPUR Power System Power Supply" + help + If you say yes here you get hardware monitoring support for the INSPUR + Power System power supply. + + This driver can also be built as a module. If so, the module will + be called inspur-ipsps. + config SENSORS_IR35221 tristate "Infineon IR35221" help @@ -64,6 +73,15 @@ config SENSORS_IR38064 This driver can also be built as a module. If so, the module will be called ir38064. +config SENSORS_IRPS5401 + tristate "Infineon IRPS5401" + help + If you say yes here you get hardware monitoring support for the + Infineon IRPS5401 controller. + + This driver can also be built as a module. If so, the module will + be called irps5401. + config SENSORS_ISL68137 tristate "Intersil ISL68137" help @@ -154,6 +172,15 @@ config SENSORS_MAX8688 This driver can also be built as a module. If so, the module will be called max8688. +config SENSORS_PXE1610 + tristate "Infineon PXE1610" + help + If you say yes here you get hardware monitoring support for Infineon + PXE1610. + + This driver can also be built as a module. If so, the module will + be called pxe1610. + config SENSORS_TPS40422 tristate "TI TPS40422" help diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index 2219b9300316..03bacfcfd660 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -7,8 +7,10 @@ obj-$(CONFIG_PMBUS) += pmbus_core.o obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o obj-$(CONFIG_SENSORS_IBM_CFFPS) += ibm-cffps.o +obj-$(CONFIG_SENSORS_INSPUR_IPSPS) += inspur-ipsps.o obj-$(CONFIG_SENSORS_IR35221) += ir35221.o obj-$(CONFIG_SENSORS_IR38064) += ir38064.o +obj-$(CONFIG_SENSORS_IRPS5401) += irps5401.o obj-$(CONFIG_SENSORS_ISL68137) += isl68137.o obj-$(CONFIG_SENSORS_LM25066) += lm25066.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o @@ -18,6 +20,7 @@ obj-$(CONFIG_SENSORS_MAX20751) += max20751.o obj-$(CONFIG_SENSORS_MAX31785) += max31785.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o obj-$(CONFIG_SENSORS_MAX8688) += max8688.o +obj-$(CONFIG_SENSORS_PXE1610) += pxe1610.o obj-$(CONFIG_SENSORS_TPS40422) += tps40422.o obj-$(CONFIG_SENSORS_TPS53679) += tps53679.o obj-$(CONFIG_SENSORS_UCD9000) += ucd9000.o diff --git a/drivers/hwmon/pmbus/adm1275.c b/drivers/hwmon/pmbus/adm1275.c index 82052b6611c9..5caa37fbfc18 100644 --- a/drivers/hwmon/pmbus/adm1275.c +++ b/drivers/hwmon/pmbus/adm1275.c @@ -14,6 +14,8 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/bitops.h> +#include <linux/bitfield.h> +#include <linux/log2.h> #include "pmbus.h" enum chips { adm1075, adm1272, adm1275, adm1276, adm1278, adm1293, adm1294 }; @@ -69,6 +71,18 @@ enum chips { adm1075, adm1272, adm1275, adm1276, adm1278, adm1293, adm1294 }; #define ADM1075_VAUX_OV_WARN BIT(7) #define ADM1075_VAUX_UV_WARN BIT(6) +#define ADM1275_VI_AVG_SHIFT 0 +#define ADM1275_VI_AVG_MASK GENMASK(ADM1275_VI_AVG_SHIFT + 2, \ + ADM1275_VI_AVG_SHIFT) +#define ADM1275_SAMPLES_AVG_MAX 128 + +#define ADM1278_PWR_AVG_SHIFT 11 +#define ADM1278_PWR_AVG_MASK GENMASK(ADM1278_PWR_AVG_SHIFT + 2, \ + ADM1278_PWR_AVG_SHIFT) +#define ADM1278_VI_AVG_SHIFT 8 +#define ADM1278_VI_AVG_MASK GENMASK(ADM1278_VI_AVG_SHIFT + 2, \ + ADM1278_VI_AVG_SHIFT) + struct adm1275_data { int id; bool have_oc_fault; @@ -80,6 +94,7 @@ struct adm1275_data { bool have_pin_min; bool have_pin_max; bool have_temp_max; + bool have_power_sampling; struct pmbus_driver_info info; }; @@ -155,6 +170,62 @@ static const struct coefficients adm1293_coefficients[] = { [18] = { 7658, 0, -3 }, /* power, 21V, irange200 */ }; +static int adm1275_read_pmon_config(const struct adm1275_data *data, + struct i2c_client *client, bool is_power) +{ + int shift, ret; + u16 mask; + + /* + * The PMON configuration register is a 16-bit register only on chips + * supporting power average sampling. On other chips it is an 8-bit + * register. + */ + if (data->have_power_sampling) { + ret = i2c_smbus_read_word_data(client, ADM1275_PMON_CONFIG); + mask = is_power ? ADM1278_PWR_AVG_MASK : ADM1278_VI_AVG_MASK; + shift = is_power ? ADM1278_PWR_AVG_SHIFT : ADM1278_VI_AVG_SHIFT; + } else { + ret = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG); + mask = ADM1275_VI_AVG_MASK; + shift = ADM1275_VI_AVG_SHIFT; + } + if (ret < 0) + return ret; + + return (ret & mask) >> shift; +} + +static int adm1275_write_pmon_config(const struct adm1275_data *data, + struct i2c_client *client, + bool is_power, u16 word) +{ + int shift, ret; + u16 mask; + + if (data->have_power_sampling) { + ret = i2c_smbus_read_word_data(client, ADM1275_PMON_CONFIG); + mask = is_power ? ADM1278_PWR_AVG_MASK : ADM1278_VI_AVG_MASK; + shift = is_power ? ADM1278_PWR_AVG_SHIFT : ADM1278_VI_AVG_SHIFT; + } else { + ret = i2c_smbus_read_byte_data(client, ADM1275_PMON_CONFIG); + mask = ADM1275_VI_AVG_MASK; + shift = ADM1275_VI_AVG_SHIFT; + } + if (ret < 0) + return ret; + + word = (ret & ~mask) | ((word << shift) & mask); + if (data->have_power_sampling) + ret = i2c_smbus_write_word_data(client, ADM1275_PMON_CONFIG, + word); + else + ret = i2c_smbus_write_byte_data(client, ADM1275_PMON_CONFIG, + word); + + return ret; +} + static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) { const struct pmbus_driver_info *info = pmbus_get_driver_info(client); @@ -233,6 +304,21 @@ static int adm1275_read_word_data(struct i2c_client *client, int page, int reg) if (!data->have_temp_max) return -ENXIO; break; + case PMBUS_VIRT_POWER_SAMPLES: + if (!data->have_power_sampling) + return -ENXIO; + ret = adm1275_read_pmon_config(data, client, true); + if (ret < 0) + break; + ret = BIT(ret); + break; + case PMBUS_VIRT_IN_SAMPLES: + case PMBUS_VIRT_CURR_SAMPLES: + ret = adm1275_read_pmon_config(data, client, false); + if (ret < 0) + break; + ret = BIT(ret); + break; default: ret = -ENODATA; break; @@ -277,6 +363,19 @@ static int adm1275_write_word_data(struct i2c_client *client, int page, int reg, case PMBUS_VIRT_RESET_TEMP_HISTORY: ret = pmbus_write_word_data(client, 0, ADM1278_PEAK_TEMP, 0); break; + case PMBUS_VIRT_POWER_SAMPLES: + if (!data->have_power_sampling) + return -ENXIO; + word = clamp_val(word, 1, ADM1275_SAMPLES_AVG_MAX); + ret = adm1275_write_pmon_config(data, client, true, + ilog2(word)); + break; + case PMBUS_VIRT_IN_SAMPLES: + case PMBUS_VIRT_CURR_SAMPLES: + word = clamp_val(word, 1, ADM1275_SAMPLES_AVG_MAX); + ret = adm1275_write_pmon_config(data, client, false, + ilog2(word)); + break; default: ret = -ENODATA; break; @@ -430,7 +529,8 @@ static int adm1275_probe(struct i2c_client *client, info->format[PSC_CURRENT_OUT] = direct; info->format[PSC_POWER] = direct; info->format[PSC_TEMPERATURE] = direct; - info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT; + info->func[0] = PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_SAMPLES; info->read_word_data = adm1275_read_word_data; info->read_byte_data = adm1275_read_byte_data; @@ -471,6 +571,7 @@ static int adm1275_probe(struct i2c_client *client, data->have_vout = true; data->have_pin_max = true; data->have_temp_max = true; + data->have_power_sampling = true; coefficients = adm1272_coefficients; vindex = (config & ADM1275_VRANGE) ? 1 : 0; @@ -556,6 +657,7 @@ static int adm1275_probe(struct i2c_client *client, data->have_vout = true; data->have_pin_max = true; data->have_temp_max = true; + data->have_power_sampling = true; coefficients = adm1278_coefficients; vindex = 0; @@ -591,6 +693,7 @@ static int adm1275_probe(struct i2c_client *client, data->have_pin_min = true; data->have_pin_max = true; data->have_mfr_vaux_status = true; + data->have_power_sampling = true; coefficients = adm1293_coefficients; diff --git a/drivers/hwmon/pmbus/ibm-cffps.c b/drivers/hwmon/pmbus/ibm-cffps.c index ee2ee9e3ffd7..d44745e498e7 100644 --- a/drivers/hwmon/pmbus/ibm-cffps.c +++ b/drivers/hwmon/pmbus/ibm-cffps.c @@ -12,6 +12,7 @@ #include <linux/leds.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/of_device.h> #include <linux/pmbus.h> #include "pmbus.h" @@ -20,8 +21,9 @@ #define CFFPS_PN_CMD 0x9B #define CFFPS_SN_CMD 0x9E #define CFFPS_CCIN_CMD 0xBD -#define CFFPS_FW_CMD_START 0xFA -#define CFFPS_FW_NUM_BYTES 4 +#define CFFPS_FW_CMD 0xFA +#define CFFPS1_FW_NUM_BYTES 4 +#define CFFPS2_FW_NUM_WORDS 3 #define CFFPS_SYS_CONFIG_CMD 0xDA #define CFFPS_INPUT_HISTORY_CMD 0xD6 @@ -52,6 +54,8 @@ enum { CFFPS_DEBUGFS_NUM_ENTRIES }; +enum versions { cffps1, cffps2 }; + struct ibm_cffps_input_history { struct mutex update_lock; unsigned long last_update; @@ -61,6 +65,7 @@ struct ibm_cffps_input_history { }; struct ibm_cffps { + enum versions version; struct i2c_client *client; struct ibm_cffps_input_history input_history; @@ -132,6 +137,8 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf, struct ibm_cffps *psu = to_psu(idxp, idx); char data[I2C_SMBUS_BLOCK_MAX] = { 0 }; + pmbus_set_page(psu->client, 0); + switch (idx) { case CFFPS_DEBUGFS_INPUT_HISTORY: return ibm_cffps_read_input_history(psu, buf, count, ppos); @@ -152,16 +159,36 @@ static ssize_t ibm_cffps_debugfs_op(struct file *file, char __user *buf, rc = snprintf(data, 5, "%04X", rc); goto done; case CFFPS_DEBUGFS_FW: - for (i = 0; i < CFFPS_FW_NUM_BYTES; ++i) { - rc = i2c_smbus_read_byte_data(psu->client, - CFFPS_FW_CMD_START + i); - if (rc < 0) - return rc; + switch (psu->version) { + case cffps1: + for (i = 0; i < CFFPS1_FW_NUM_BYTES; ++i) { + rc = i2c_smbus_read_byte_data(psu->client, + CFFPS_FW_CMD + + i); + if (rc < 0) + return rc; + + snprintf(&data[i * 2], 3, "%02X", rc); + } - snprintf(&data[i * 2], 3, "%02X", rc); - } + rc = i * 2; + break; + case cffps2: + for (i = 0; i < CFFPS2_FW_NUM_WORDS; ++i) { + rc = i2c_smbus_read_word_data(psu->client, + CFFPS_FW_CMD + + i); + if (rc < 0) + return rc; + + snprintf(&data[i * 4], 5, "%04X", rc); + } - rc = i * 2; + rc = i * 4; + break; + default: + return -EOPNOTSUPP; + } goto done; default: return -EINVAL; @@ -279,6 +306,8 @@ static void ibm_cffps_led_brightness_set(struct led_classdev *led_cdev, psu->led_state = CFFPS_LED_ON; } + pmbus_set_page(psu->client, 0); + rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD, psu->led_state); if (rc < 0) @@ -299,6 +328,8 @@ static int ibm_cffps_led_blink_set(struct led_classdev *led_cdev, if (led_cdev->brightness == LED_OFF) return 0; + pmbus_set_page(psu->client, 0); + rc = i2c_smbus_write_byte_data(psu->client, CFFPS_SYS_CONFIG_CMD, CFFPS_LED_BLINK); if (rc < 0) @@ -328,15 +359,32 @@ static void ibm_cffps_create_led_class(struct ibm_cffps *psu) dev_warn(dev, "failed to register led class: %d\n", rc); } -static struct pmbus_driver_info ibm_cffps_info = { - .pages = 1, - .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | - PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | - PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT | - PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | - PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12, - .read_byte_data = ibm_cffps_read_byte_data, - .read_word_data = ibm_cffps_read_word_data, +static struct pmbus_driver_info ibm_cffps_info[] = { + [cffps1] = { + .pages = 1, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | + PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_STATUS_FAN12, + .read_byte_data = ibm_cffps_read_byte_data, + .read_word_data = ibm_cffps_read_word_data, + }, + [cffps2] = { + .pages = 2, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_PIN | PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | + PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT | + PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP | + PMBUS_HAVE_STATUS_FAN12, + .func[1] = PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | PMBUS_HAVE_TEMP3 | + PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT, + .read_byte_data = ibm_cffps_read_byte_data, + .read_word_data = ibm_cffps_read_word_data, + }, }; static struct pmbus_platform_data ibm_cffps_pdata = { @@ -347,12 +395,21 @@ static int ibm_cffps_probe(struct i2c_client *client, const struct i2c_device_id *id) { int i, rc; + enum versions vs; struct dentry *debugfs; struct dentry *ibm_cffps_dir; struct ibm_cffps *psu; + const void *md = of_device_get_match_data(&client->dev); + + if (md) + vs = (enum versions)md; + else if (id) + vs = (enum versions)id->driver_data; + else + vs = cffps1; client->dev.platform_data = &ibm_cffps_pdata; - rc = pmbus_do_probe(client, id, &ibm_cffps_info); + rc = pmbus_do_probe(client, id, &ibm_cffps_info[vs]); if (rc) return rc; @@ -364,6 +421,7 @@ static int ibm_cffps_probe(struct i2c_client *client, if (!psu) return 0; + psu->version = vs; psu->client = client; mutex_init(&psu->input_history.update_lock); psu->input_history.last_update = jiffies - HZ; @@ -405,13 +463,21 @@ static int ibm_cffps_probe(struct i2c_client *client, } static const struct i2c_device_id ibm_cffps_id[] = { - { "ibm_cffps1", 1 }, + { "ibm_cffps1", cffps1 }, + { "ibm_cffps2", cffps2 }, {} }; MODULE_DEVICE_TABLE(i2c, ibm_cffps_id); static const struct of_device_id ibm_cffps_of_match[] = { - { .compatible = "ibm,cffps1" }, + { + .compatible = "ibm,cffps1", + .data = (void *)cffps1 + }, + { + .compatible = "ibm,cffps2", + .data = (void *)cffps2 + }, {} }; MODULE_DEVICE_TABLE(of, ibm_cffps_of_match); diff --git a/drivers/hwmon/pmbus/inspur-ipsps.c b/drivers/hwmon/pmbus/inspur-ipsps.c new file mode 100644 index 000000000000..42e01549184a --- /dev/null +++ b/drivers/hwmon/pmbus/inspur-ipsps.c @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2019 Inspur Corp. + */ + +#include <linux/debugfs.h> +#include <linux/device.h> +#include <linux/fs.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/pmbus.h> +#include <linux/hwmon-sysfs.h> + +#include "pmbus.h" + +#define IPSPS_REG_VENDOR_ID 0x99 +#define IPSPS_REG_MODEL 0x9A +#define IPSPS_REG_FW_VERSION 0x9B +#define IPSPS_REG_PN 0x9C +#define IPSPS_REG_SN 0x9E +#define IPSPS_REG_HW_VERSION 0xB0 +#define IPSPS_REG_MODE 0xFC + +#define MODE_ACTIVE 0x55 +#define MODE_STANDBY 0x0E +#define MODE_REDUNDANCY 0x00 + +#define MODE_ACTIVE_STRING "active" +#define MODE_STANDBY_STRING "standby" +#define MODE_REDUNDANCY_STRING "redundancy" + +enum ipsps_index { + vendor, + model, + fw_version, + part_number, + serial_number, + hw_version, + mode, + num_regs, +}; + +static const u8 ipsps_regs[num_regs] = { + [vendor] = IPSPS_REG_VENDOR_ID, + [model] = IPSPS_REG_MODEL, + [fw_version] = IPSPS_REG_FW_VERSION, + [part_number] = IPSPS_REG_PN, + [serial_number] = IPSPS_REG_SN, + [hw_version] = IPSPS_REG_HW_VERSION, + [mode] = IPSPS_REG_MODE, +}; + +static ssize_t ipsps_string_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + u8 reg; + int rc; + char *p; + char data[I2C_SMBUS_BLOCK_MAX + 1]; + struct i2c_client *client = to_i2c_client(dev->parent); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + reg = ipsps_regs[attr->index]; + rc = i2c_smbus_read_block_data(client, reg, data); + if (rc < 0) + return rc; + + /* filled with printable characters, ending with # */ + p = memscan(data, '#', rc); + *p = '\0'; + + return snprintf(buf, PAGE_SIZE, "%s\n", data); +} + +static ssize_t ipsps_fw_version_show(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + u8 reg; + int rc; + u8 data[I2C_SMBUS_BLOCK_MAX] = { 0 }; + struct i2c_client *client = to_i2c_client(dev->parent); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + reg = ipsps_regs[attr->index]; + rc = i2c_smbus_read_block_data(client, reg, data); + if (rc < 0) + return rc; + + if (rc != 6) + return -EPROTO; + + return snprintf(buf, PAGE_SIZE, "%u.%02u%u-%u.%02u\n", + data[1], data[2]/* < 100 */, data[3]/*< 10*/, + data[4], data[5]/* < 100 */); +} + +static ssize_t ipsps_mode_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u8 reg; + int rc; + struct i2c_client *client = to_i2c_client(dev->parent); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + reg = ipsps_regs[attr->index]; + rc = i2c_smbus_read_byte_data(client, reg); + if (rc < 0) + return rc; + + switch (rc) { + case MODE_ACTIVE: + return snprintf(buf, PAGE_SIZE, "[%s] %s %s\n", + MODE_ACTIVE_STRING, + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); + case MODE_STANDBY: + return snprintf(buf, PAGE_SIZE, "%s [%s] %s\n", + MODE_ACTIVE_STRING, + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); + case MODE_REDUNDANCY: + return snprintf(buf, PAGE_SIZE, "%s %s [%s]\n", + MODE_ACTIVE_STRING, + MODE_STANDBY_STRING, MODE_REDUNDANCY_STRING); + default: + return snprintf(buf, PAGE_SIZE, "unspecified\n"); + } +} + +static ssize_t ipsps_mode_store(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + u8 reg; + int rc; + struct i2c_client *client = to_i2c_client(dev->parent); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + + reg = ipsps_regs[attr->index]; + if (sysfs_streq(MODE_STANDBY_STRING, buf)) { + rc = i2c_smbus_write_byte_data(client, reg, + MODE_STANDBY); + if (rc < 0) + return rc; + return count; + } else if (sysfs_streq(MODE_ACTIVE_STRING, buf)) { + rc = i2c_smbus_write_byte_data(client, reg, + MODE_ACTIVE); + if (rc < 0) + return rc; + return count; + } + + return -EINVAL; +} + +static SENSOR_DEVICE_ATTR_RO(vendor, ipsps_string, vendor); +static SENSOR_DEVICE_ATTR_RO(model, ipsps_string, model); +static SENSOR_DEVICE_ATTR_RO(part_number, ipsps_string, part_number); +static SENSOR_DEVICE_ATTR_RO(serial_number, ipsps_string, serial_number); +static SENSOR_DEVICE_ATTR_RO(hw_version, ipsps_string, hw_version); +static SENSOR_DEVICE_ATTR_RO(fw_version, ipsps_fw_version, fw_version); +static SENSOR_DEVICE_ATTR_RW(mode, ipsps_mode, mode); + +static struct attribute *ipsps_attrs[] = { + &sensor_dev_attr_vendor.dev_attr.attr, + &sensor_dev_attr_model.dev_attr.attr, + &sensor_dev_attr_part_number.dev_attr.attr, + &sensor_dev_attr_serial_number.dev_attr.attr, + &sensor_dev_attr_hw_version.dev_attr.attr, + &sensor_dev_attr_fw_version.dev_attr.attr, + &sensor_dev_attr_mode.dev_attr.attr, + NULL, +}; + +ATTRIBUTE_GROUPS(ipsps); + +static struct pmbus_driver_info ipsps_info = { + .pages = 1, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_VOUT | PMBUS_HAVE_IOUT | + PMBUS_HAVE_IIN | PMBUS_HAVE_POUT | PMBUS_HAVE_PIN | + PMBUS_HAVE_FAN12 | PMBUS_HAVE_TEMP | PMBUS_HAVE_TEMP2 | + PMBUS_HAVE_TEMP3 | PMBUS_HAVE_STATUS_VOUT | + PMBUS_HAVE_STATUS_IOUT | PMBUS_HAVE_STATUS_INPUT | + PMBUS_HAVE_STATUS_TEMP | PMBUS_HAVE_STATUS_FAN12, + .groups = ipsps_groups, +}; + +static struct pmbus_platform_data ipsps_pdata = { + .flags = PMBUS_SKIP_STATUS_CHECK, +}; + +static int ipsps_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + client->dev.platform_data = &ipsps_pdata; + return pmbus_do_probe(client, id, &ipsps_info); +} + +static const struct i2c_device_id ipsps_id[] = { + { "ipsps1", 0 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ipsps_id); + +#ifdef CONFIG_OF +static const struct of_device_id ipsps_of_match[] = { + { .compatible = "inspur,ipsps1" }, + {} +}; +MODULE_DEVICE_TABLE(of, ipsps_of_match); +#endif + +static struct i2c_driver ipsps_driver = { + .driver = { + .name = "inspur-ipsps", + .of_match_table = of_match_ptr(ipsps_of_match), + }, + .probe = ipsps_probe, + .remove = pmbus_do_remove, + .id_table = ipsps_id, +}; + +module_i2c_driver(ipsps_driver); + +MODULE_AUTHOR("John Wang"); +MODULE_DESCRIPTION("PMBus driver for Inspur Power System power supplies"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/irps5401.c b/drivers/hwmon/pmbus/irps5401.c new file mode 100644 index 000000000000..d37daa001fb3 --- /dev/null +++ b/drivers/hwmon/pmbus/irps5401.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for the Infineon IRPS5401M PMIC. + * + * Copyright (c) 2019 SED Systems, a division of Calian Ltd. + * + * The device supports VOUT_PEAK, IOUT_PEAK, and TEMPERATURE_PEAK, however + * this driver does not currently support them. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +#define IRPS5401_SW_FUNC (PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | \ + PMBUS_HAVE_STATUS_INPUT | \ + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | \ + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP) + +#define IRPS5401_LDO_FUNC (PMBUS_HAVE_VIN | \ + PMBUS_HAVE_STATUS_INPUT | \ + PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT | \ + PMBUS_HAVE_IOUT | PMBUS_HAVE_STATUS_IOUT | \ + PMBUS_HAVE_PIN | PMBUS_HAVE_POUT | \ + PMBUS_HAVE_TEMP | PMBUS_HAVE_STATUS_TEMP) + +static struct pmbus_driver_info irps5401_info = { + .pages = 5, + .func[0] = IRPS5401_SW_FUNC, + .func[1] = IRPS5401_SW_FUNC, + .func[2] = IRPS5401_SW_FUNC, + .func[3] = IRPS5401_SW_FUNC, + .func[4] = IRPS5401_LDO_FUNC, +}; + +static int irps5401_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return pmbus_do_probe(client, id, &irps5401_info); +} + +static const struct i2c_device_id irps5401_id[] = { + {"irps5401", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, irps5401_id); + +static struct i2c_driver irps5401_driver = { + .driver = { + .name = "irps5401", + }, + .probe = irps5401_probe, + .remove = pmbus_do_remove, + .id_table = irps5401_id, +}; + +module_i2c_driver(irps5401_driver); + +MODULE_AUTHOR("Robert Hancock"); +MODULE_DESCRIPTION("PMBus driver for Infineon IRPS5401"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/max31785.c b/drivers/hwmon/pmbus/max31785.c index 69d9029ea410..254b0f98c755 100644 --- a/drivers/hwmon/pmbus/max31785.c +++ b/drivers/hwmon/pmbus/max31785.c @@ -244,8 +244,6 @@ static int max31785_write_word_data(struct i2c_client *client, int page, #define MAX31785_VOUT_FUNCS \ (PMBUS_HAVE_VOUT | PMBUS_HAVE_STATUS_VOUT) -#define MAX37185_NUM_FAN_PAGES 6 - static const struct pmbus_driver_info max31785_info = { .pages = MAX31785_NR_PAGES, diff --git a/drivers/hwmon/pmbus/pxe1610.c b/drivers/hwmon/pmbus/pxe1610.c new file mode 100644 index 000000000000..ebe3f023f840 --- /dev/null +++ b/drivers/hwmon/pmbus/pxe1610.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Hardware monitoring driver for Infineon PXE1610 + * + * Copyright (c) 2019 Facebook Inc + * + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +#define PXE1610_NUM_PAGES 3 + +/* Identify chip parameters. */ +static int pxe1610_identify(struct i2c_client *client, + struct pmbus_driver_info *info) +{ + if (pmbus_check_byte_register(client, 0, PMBUS_VOUT_MODE)) { + u8 vout_mode; + int ret; + + /* Read the register with VOUT scaling value.*/ + ret = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); + if (ret < 0) + return ret; + + vout_mode = ret & GENMASK(4, 0); + + switch (vout_mode) { + case 1: + info->vrm_version = vr12; + break; + case 2: + info->vrm_version = vr13; + break; + default: + return -ENODEV; + } + } + + return 0; +} + +static struct pmbus_driver_info pxe1610_info = { + .pages = PXE1610_NUM_PAGES, + .format[PSC_VOLTAGE_IN] = linear, + .format[PSC_VOLTAGE_OUT] = vid, + .format[PSC_CURRENT_IN] = linear, + .format[PSC_CURRENT_OUT] = linear, + .format[PSC_TEMPERATURE] = linear, + .format[PSC_POWER] = linear, + .func[0] = PMBUS_HAVE_VIN + | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN + | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN + | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, + .func[1] = PMBUS_HAVE_VIN + | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN + | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN + | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, + .func[2] = PMBUS_HAVE_VIN + | PMBUS_HAVE_VOUT | PMBUS_HAVE_IIN + | PMBUS_HAVE_IOUT | PMBUS_HAVE_PIN + | PMBUS_HAVE_POUT | PMBUS_HAVE_TEMP + | PMBUS_HAVE_STATUS_VOUT | PMBUS_HAVE_STATUS_IOUT + | PMBUS_HAVE_STATUS_INPUT | PMBUS_HAVE_STATUS_TEMP, + .identify = pxe1610_identify, +}; + +static int pxe1610_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct pmbus_driver_info *info; + u8 buf[I2C_SMBUS_BLOCK_MAX]; + int ret; + + if (!i2c_check_functionality( + client->adapter, + I2C_FUNC_SMBUS_READ_BYTE_DATA + | I2C_FUNC_SMBUS_READ_WORD_DATA + | I2C_FUNC_SMBUS_READ_BLOCK_DATA)) + return -ENODEV; + + /* + * By default this device doesn't boot to page 0, so set page 0 + * to access all pmbus registers. + */ + i2c_smbus_write_byte_data(client, PMBUS_PAGE, 0); + + /* Read Manufacturer id */ + ret = i2c_smbus_read_block_data(client, PMBUS_MFR_ID, buf); + if (ret < 0) { + dev_err(&client->dev, "Failed to read PMBUS_MFR_ID\n"); + return ret; + } + if (ret != 2 || strncmp(buf, "XP", 2)) { + dev_err(&client->dev, "MFR_ID unrecognized\n"); + return -ENODEV; + } + + info = devm_kmemdup(&client->dev, &pxe1610_info, + sizeof(struct pmbus_driver_info), + GFP_KERNEL); + if (!info) + return -ENOMEM; + + return pmbus_do_probe(client, id, info); +} + +static const struct i2c_device_id pxe1610_id[] = { + {"pxe1610", 0}, + {"pxe1110", 0}, + {"pxm1310", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, pxe1610_id); + +static struct i2c_driver pxe1610_driver = { + .driver = { + .name = "pxe1610", + }, + .probe = pxe1610_probe, + .remove = pmbus_do_remove, + .id_table = pxe1610_id, +}; + +module_i2c_driver(pxe1610_driver); + +MODULE_AUTHOR("Vijay Khemka <vijaykhemka@fb.com>"); +MODULE_DESCRIPTION("PMBus driver for Infineon PXE1610, PXE1110 and PXM1310"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pmbus/ucd9000.c b/drivers/hwmon/pmbus/ucd9000.c index c846759bc1c0..a9229c6b0e84 100644 --- a/drivers/hwmon/pmbus/ucd9000.c +++ b/drivers/hwmon/pmbus/ucd9000.c @@ -15,7 +15,6 @@ #include <linux/slab.h> #include <linux/i2c.h> #include <linux/pmbus.h> -#include <linux/gpio.h> #include <linux/gpio/driver.h> #include "pmbus.h" diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 08c9b9f1c16e..54c0ff00d67f 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -320,8 +320,10 @@ static int pwm_fan_probe(struct platform_device *pdev) dev_err(dev, "Failed to enable fan supply: %d\n", ret); return ret; } - devm_add_action_or_reset(dev, pwm_fan_regulator_disable, - ctx->reg_en); + ret = devm_add_action_or_reset(dev, pwm_fan_regulator_disable, + ctx->reg_en); + if (ret) + return ret; } ctx->pwm_value = MAX_PWM; @@ -337,7 +339,9 @@ static int pwm_fan_probe(struct platform_device *pdev) return ret; } timer_setup(&ctx->rpm_timer, sample_timer, 0); - devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx); + ret = devm_add_action_or_reset(dev, pwm_fan_pwm_disable, ctx); + if (ret) + return ret; of_property_read_u32(dev->of_node, "pulses-per-revolution", &ppr); ctx->pulses_per_revolution = ppr; diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c index efe4bb1ff221..d3a64a35f7a9 100644 --- a/drivers/hwmon/raspberrypi-hwmon.c +++ b/drivers/hwmon/raspberrypi-hwmon.c @@ -146,7 +146,7 @@ static struct platform_driver rpi_hwmon_driver = { }; module_platform_driver(rpi_hwmon_driver); -MODULE_AUTHOR("Stefan Wahren <stefan.wahren@i2se.com>"); +MODULE_AUTHOR("Stefan Wahren <wahrenst@gmx.net>"); MODULE_DESCRIPTION("Raspberry Pi voltage sensor driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:raspberrypi-hwmon"); diff --git a/drivers/hwmon/scmi-hwmon.c b/drivers/hwmon/scmi-hwmon.c index a80183a488c5..8a7732c0bef3 100644 --- a/drivers/hwmon/scmi-hwmon.c +++ b/drivers/hwmon/scmi-hwmon.c @@ -18,6 +18,50 @@ struct scmi_sensors { const struct scmi_sensor_info **info[hwmon_max]; }; +static inline u64 __pow10(u8 x) +{ + u64 r = 1; + + while (x--) + r *= 10; + + return r; +} + +static int scmi_hwmon_scale(const struct scmi_sensor_info *sensor, u64 *value) +{ + s8 scale = sensor->scale; + u64 f; + + switch (sensor->type) { + case TEMPERATURE_C: + case VOLTAGE: + case CURRENT: + scale += 3; + break; + case POWER: + case ENERGY: + scale += 6; + break; + default: + break; + } + + if (scale == 0) + return 0; + + if (abs(scale) > 19) + return -E2BIG; + + f = __pow10(abs(scale)); + if (scale > 0) + *value *= f; + else + *value = div64_u64(*value, f); + + return 0; +} + static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { @@ -28,7 +72,11 @@ static int scmi_hwmon_read(struct device *dev, enum hwmon_sensor_types type, const struct scmi_handle *h = scmi_sensors->handle; sensor = *(scmi_sensors->info[type] + channel); - ret = h->sensor_ops->reading_get(h, sensor->id, false, &value); + ret = h->sensor_ops->reading_get(h, sensor->id, &value); + if (ret) + return ret; + + ret = scmi_hwmon_scale(sensor, &value); if (!ret) *val = value; diff --git a/drivers/hwmon/scpi-hwmon.c b/drivers/hwmon/scpi-hwmon.c index 9bfa228d0eb0..25aac40f2764 100644 --- a/drivers/hwmon/scpi-hwmon.c +++ b/drivers/hwmon/scpi-hwmon.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * System Control and Power Interface(SCPI) based hwmon sensor driver * * Copyright (C) 2015 ARM Ltd. * Punit Agrawal <punit.agrawal@arm.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/hwmon.h> diff --git a/drivers/hwmon/shtc1.c b/drivers/hwmon/shtc1.c index 83fe08185ac7..a0078ccede03 100644 --- a/drivers/hwmon/shtc1.c +++ b/drivers/hwmon/shtc1.c @@ -24,19 +24,33 @@ static const unsigned char shtc1_cmd_measure_blocking_lpm[] = { 0x64, 0x58 }; static const unsigned char shtc1_cmd_measure_nonblocking_lpm[] = { 0x60, 0x9c }; /* command for reading the ID register */ -static const unsigned char shtc1_cmd_read_id_reg[] = { 0xef, 0xc8 }; +static const unsigned char shtc1_cmd_read_id_reg[] = { 0xef, 0xc8 }; -/* constants for reading the ID register */ -#define SHTC1_ID 0x07 -#define SHTC1_ID_REG_MASK 0x1f +/* + * constants for reading the ID register + * SHTC1: 0x0007 with mask 0x003f + * SHTW1: 0x0007 with mask 0x003f + * SHTC3: 0x0807 with mask 0x083f + */ +#define SHTC3_ID 0x0807 +#define SHTC3_ID_MASK 0x083f +#define SHTC1_ID 0x0007 +#define SHTC1_ID_MASK 0x003f /* delays for non-blocking i2c commands, both in us */ #define SHTC1_NONBLOCKING_WAIT_TIME_HPM 14400 #define SHTC1_NONBLOCKING_WAIT_TIME_LPM 1000 +#define SHTC3_NONBLOCKING_WAIT_TIME_HPM 12100 +#define SHTC3_NONBLOCKING_WAIT_TIME_LPM 800 #define SHTC1_CMD_LENGTH 2 #define SHTC1_RESPONSE_LENGTH 6 +enum shtcx_chips { + shtc1, + shtc3, +}; + struct shtc1_data { struct i2c_client *client; struct mutex update_lock; @@ -47,6 +61,7 @@ struct shtc1_data { unsigned int nonblocking_wait_time; /* in us */ struct shtc1_platform_data setup; + enum shtcx_chips chip; int temperature; /* 1000 * temperature in dgr C */ int humidity; /* 1000 * relative humidity in %RH */ @@ -157,13 +172,16 @@ static void shtc1_select_command(struct shtc1_data *data) data->command = data->setup.blocking_io ? shtc1_cmd_measure_blocking_hpm : shtc1_cmd_measure_nonblocking_hpm; - data->nonblocking_wait_time = SHTC1_NONBLOCKING_WAIT_TIME_HPM; - + data->nonblocking_wait_time = (data->chip == shtc1) ? + SHTC1_NONBLOCKING_WAIT_TIME_HPM : + SHTC3_NONBLOCKING_WAIT_TIME_HPM; } else { data->command = data->setup.blocking_io ? shtc1_cmd_measure_blocking_lpm : shtc1_cmd_measure_nonblocking_lpm; - data->nonblocking_wait_time = SHTC1_NONBLOCKING_WAIT_TIME_LPM; + data->nonblocking_wait_time = (data->chip == shtc1) ? + SHTC1_NONBLOCKING_WAIT_TIME_LPM : + SHTC3_NONBLOCKING_WAIT_TIME_LPM; } } @@ -171,9 +189,11 @@ static int shtc1_probe(struct i2c_client *client, const struct i2c_device_id *id) { int ret; - char id_reg[2]; + u16 id_reg; + char id_reg_buf[2]; struct shtc1_data *data; struct device *hwmon_dev; + enum shtcx_chips chip = id->driver_data; struct i2c_adapter *adap = client->adapter; struct device *dev = &client->dev; @@ -187,13 +207,20 @@ static int shtc1_probe(struct i2c_client *client, dev_err(dev, "could not send read_id_reg command: %d\n", ret); return ret < 0 ? ret : -ENODEV; } - ret = i2c_master_recv(client, id_reg, sizeof(id_reg)); - if (ret != sizeof(id_reg)) { + ret = i2c_master_recv(client, id_reg_buf, sizeof(id_reg_buf)); + if (ret != sizeof(id_reg_buf)) { dev_err(dev, "could not read ID register: %d\n", ret); return -ENODEV; } - if ((id_reg[1] & SHTC1_ID_REG_MASK) != SHTC1_ID) { - dev_err(dev, "ID register doesn't match\n"); + + id_reg = be16_to_cpup((__be16 *)id_reg_buf); + if (chip == shtc3) { + if ((id_reg & SHTC3_ID_MASK) != SHTC3_ID) { + dev_err(dev, "SHTC3 ID register does not match\n"); + return -ENODEV; + } + } else if ((id_reg & SHTC1_ID_MASK) != SHTC1_ID) { + dev_err(dev, "SHTC1 ID register does not match\n"); return -ENODEV; } @@ -204,6 +231,7 @@ static int shtc1_probe(struct i2c_client *client, data->setup.blocking_io = false; data->setup.high_precision = true; data->client = client; + data->chip = chip; if (client->dev.platform_data) data->setup = *(struct shtc1_platform_data *)dev->platform_data; @@ -222,8 +250,9 @@ static int shtc1_probe(struct i2c_client *client, /* device ID table */ static const struct i2c_device_id shtc1_id[] = { - { "shtc1", 0 }, - { "shtw1", 0 }, + { "shtc1", shtc1 }, + { "shtw1", shtc1 }, + { "shtc3", shtc3 }, { } }; MODULE_DEVICE_TABLE(i2c, shtc1_id); diff --git a/drivers/hwmon/smm665.c b/drivers/hwmon/smm665.c index d8c91c2cb8cf..6eff14fe395d 100644 --- a/drivers/hwmon/smm665.c +++ b/drivers/hwmon/smm665.c @@ -586,10 +586,10 @@ static int smm665_probe(struct i2c_client *client, data->client = client; data->type = id->driver_data; - data->cmdreg = i2c_new_dummy(adapter, (client->addr & ~SMM665_REGMASK) + data->cmdreg = i2c_new_dummy_device(adapter, (client->addr & ~SMM665_REGMASK) | SMM665_CMDREG_BASE); - if (!data->cmdreg) - return -ENOMEM; + if (IS_ERR(data->cmdreg)) + return PTR_ERR(data->cmdreg); switch (data->type) { case smm465: diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c index cc6aca6e436c..b637836b58a1 100644 --- a/drivers/hwmon/smsc47m1.c +++ b/drivers/hwmon/smsc47m1.c @@ -351,6 +351,8 @@ static ssize_t fan_div_store(struct device *dev, tmp |= data->fan_div[2] << 4; smsc47m1_write_value(data, SMSC47M2_REG_FANDIV3, tmp); break; + default: + BUG(); } /* Preserve fan min */ diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index d2c04b6a3f2b..015f1ea31966 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -894,12 +894,12 @@ w83781d_detect_subclients(struct i2c_client *new_client) } for (i = 0; i < num_sc; i++) { - data->lm75[i] = i2c_new_dummy(adapter, sc_addr[i]); - if (!data->lm75[i]) { + data->lm75[i] = i2c_new_dummy_device(adapter, sc_addr[i]); + if (IS_ERR(data->lm75[i])) { dev_err(&new_client->dev, "Subclient %d registration at address 0x%x failed.\n", i, sc_addr[i]); - err = -ENOMEM; + err = PTR_ERR(data->lm75[i]); if (i == 1) goto ERROR_SC_3; goto ERROR_SC_2; diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 050ad4201691..aad8d4da5802 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -1260,7 +1260,7 @@ static int w83791d_detect_subclients(struct i2c_client *client) struct i2c_adapter *adapter = client->adapter; struct w83791d_data *data = i2c_get_clientdata(client); int address = client->addr; - int i, id, err; + int i, id; u8 val; id = i2c_adapter_id(adapter); @@ -1272,8 +1272,7 @@ static int w83791d_detect_subclients(struct i2c_client *client) "invalid subclient " "address %d; must be 0x48-0x4f\n", force_subclients[i]); - err = -ENODEV; - goto error_sc_0; + return -ENODEV; } } w83791d_write(client, W83791D_REG_I2C_SUBADDR, @@ -1283,29 +1282,22 @@ static int w83791d_detect_subclients(struct i2c_client *client) val = w83791d_read(client, W83791D_REG_I2C_SUBADDR); if (!(val & 0x08)) - data->lm75[0] = i2c_new_dummy(adapter, 0x48 + (val & 0x7)); + data->lm75[0] = devm_i2c_new_dummy_device(&client->dev, adapter, + 0x48 + (val & 0x7)); if (!(val & 0x80)) { - if ((data->lm75[0] != NULL) && + if (!IS_ERR(data->lm75[0]) && ((val & 0x7) == ((val >> 4) & 0x7))) { dev_err(&client->dev, "duplicate addresses 0x%x, " "use force_subclient\n", data->lm75[0]->addr); - err = -ENODEV; - goto error_sc_1; + return -ENODEV; } - data->lm75[1] = i2c_new_dummy(adapter, - 0x48 + ((val >> 4) & 0x7)); + data->lm75[1] = devm_i2c_new_dummy_device(&client->dev, adapter, + 0x48 + ((val >> 4) & 0x7)); } return 0; - -/* Undo inits in case of errors */ - -error_sc_1: - i2c_unregister_device(data->lm75[0]); -error_sc_0: - return err; } @@ -1394,7 +1386,7 @@ static int w83791d_probe(struct i2c_client *client, /* Register sysfs hooks */ err = sysfs_create_group(&client->dev.kobj, &w83791d_group); if (err) - goto error3; + return err; /* Check if pins of fan/pwm 4-5 are in use as GPIO */ has_fanpwm45 = w83791d_read(client, W83791D_REG_GPIO) & 0x10; @@ -1419,9 +1411,6 @@ error5: sysfs_remove_group(&client->dev.kobj, &w83791d_group_fanpwm45); error4: sysfs_remove_group(&client->dev.kobj, &w83791d_group); -error3: - i2c_unregister_device(data->lm75[0]); - i2c_unregister_device(data->lm75[1]); return err; } @@ -1432,9 +1421,6 @@ static int w83791d_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &w83791d_group); - i2c_unregister_device(data->lm75[0]); - i2c_unregister_device(data->lm75[1]); - return 0; } diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index da8a6d62aa23..7fc8a1160c8f 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -924,7 +924,7 @@ store_sf2_level(struct device *dev, struct device_attribute *attr, static int w83792d_detect_subclients(struct i2c_client *new_client) { - int i, id, err; + int i, id; int address = new_client->addr; u8 val; struct i2c_adapter *adapter = new_client->adapter; @@ -938,8 +938,7 @@ w83792d_detect_subclients(struct i2c_client *new_client) dev_err(&new_client->dev, "invalid subclient address %d; must be 0x48-0x4f\n", force_subclients[i]); - err = -ENODEV; - goto ERROR_SC_0; + return -ENODEV; } } w83792d_write_value(new_client, W83792D_REG_I2C_SUBADDR, @@ -949,28 +948,21 @@ w83792d_detect_subclients(struct i2c_client *new_client) val = w83792d_read_value(new_client, W83792D_REG_I2C_SUBADDR); if (!(val & 0x08)) - data->lm75[0] = i2c_new_dummy(adapter, 0x48 + (val & 0x7)); + data->lm75[0] = devm_i2c_new_dummy_device(&new_client->dev, adapter, + 0x48 + (val & 0x7)); if (!(val & 0x80)) { - if ((data->lm75[0] != NULL) && + if (!IS_ERR(data->lm75[0]) && ((val & 0x7) == ((val >> 4) & 0x7))) { dev_err(&new_client->dev, "duplicate addresses 0x%x, use force_subclient\n", data->lm75[0]->addr); - err = -ENODEV; - goto ERROR_SC_1; + return -ENODEV; } - data->lm75[1] = i2c_new_dummy(adapter, - 0x48 + ((val >> 4) & 0x7)); + data->lm75[1] = devm_i2c_new_dummy_device(&new_client->dev, adapter, + 0x48 + ((val >> 4) & 0x7)); } return 0; - -/* Undo inits in case of errors */ - -ERROR_SC_1: - i2c_unregister_device(data->lm75[0]); -ERROR_SC_0: - return err; } static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0); @@ -1396,7 +1388,7 @@ w83792d_probe(struct i2c_client *client, const struct i2c_device_id *id) /* Register sysfs hooks */ err = sysfs_create_group(&dev->kobj, &w83792d_group); if (err) - goto exit_i2c_unregister; + return err; /* * Read GPIO enable register to check if pins for fan 4,5 are used as @@ -1441,9 +1433,6 @@ exit_remove_files: sysfs_remove_group(&dev->kobj, &w83792d_group); for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++) sysfs_remove_group(&dev->kobj, &w83792d_group_fan[i]); -exit_i2c_unregister: - i2c_unregister_device(data->lm75[0]); - i2c_unregister_device(data->lm75[1]); return err; } @@ -1459,9 +1448,6 @@ w83792d_remove(struct i2c_client *client) sysfs_remove_group(&client->dev.kobj, &w83792d_group_fan[i]); - i2c_unregister_device(data->lm75[0]); - i2c_unregister_device(data->lm75[1]); - return 0; } diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 46f5dfec8d0a..9df48b70c70c 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1551,9 +1551,6 @@ static int w83793_remove(struct i2c_client *client) for (i = 0; i < ARRAY_SIZE(w83793_temp); i++) device_remove_file(dev, &w83793_temp[i].dev_attr); - i2c_unregister_device(data->lm75[0]); - i2c_unregister_device(data->lm75[1]); - /* Decrease data reference counter */ mutex_lock(&watchdog_data_mutex); kref_put(&data->kref, w83793_release_resources); @@ -1565,7 +1562,7 @@ static int w83793_remove(struct i2c_client *client) static int w83793_detect_subclients(struct i2c_client *client) { - int i, id, err; + int i, id; int address = client->addr; u8 tmp; struct i2c_adapter *adapter = client->adapter; @@ -1580,8 +1577,7 @@ w83793_detect_subclients(struct i2c_client *client) "invalid subclient " "address %d; must be 0x48-0x4f\n", force_subclients[i]); - err = -EINVAL; - goto ERROR_SC_0; + return -EINVAL; } } w83793_write_value(client, W83793_REG_I2C_SUBADDR, @@ -1591,28 +1587,21 @@ w83793_detect_subclients(struct i2c_client *client) tmp = w83793_read_value(client, W83793_REG_I2C_SUBADDR); if (!(tmp & 0x08)) - data->lm75[0] = i2c_new_dummy(adapter, 0x48 + (tmp & 0x7)); + data->lm75[0] = devm_i2c_new_dummy_device(&client->dev, adapter, + 0x48 + (tmp & 0x7)); if (!(tmp & 0x80)) { - if ((data->lm75[0] != NULL) + if (!IS_ERR(data->lm75[0]) && ((tmp & 0x7) == ((tmp >> 4) & 0x7))) { dev_err(&client->dev, "duplicate addresses 0x%x, " "use force_subclients\n", data->lm75[0]->addr); - err = -ENODEV; - goto ERROR_SC_1; + return -ENODEV; } - data->lm75[1] = i2c_new_dummy(adapter, - 0x48 + ((tmp >> 4) & 0x7)); + data->lm75[1] = devm_i2c_new_dummy_device(&client->dev, adapter, + 0x48 + ((tmp >> 4) & 0x7)); } return 0; - - /* Undo inits in case of errors */ - -ERROR_SC_1: - i2c_unregister_device(data->lm75[0]); -ERROR_SC_0: - return err; } /* Return 0 if detection is successful, -ENODEV otherwise */ @@ -1945,9 +1934,6 @@ exit_remove: for (i = 0; i < ARRAY_SIZE(w83793_temp); i++) device_remove_file(dev, &w83793_temp[i].dev_attr); - - i2c_unregister_device(data->lm75[0]); - i2c_unregister_device(data->lm75[1]); free_mem: kfree(data); exit: |