diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-09 07:30:25 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-12-09 07:30:25 +0300 |
commit | 177808cd28ac793d654bb1ae5ae1f778e7b3864f (patch) | |
tree | e54e877b3aecd09d91d473da36981ed1702cfa87 /drivers | |
parent | 0160928e792eff243c84b39a46cddb2fb89da0cb (diff) | |
parent | 907a6d5824599d09e986105a5a880d119a996c4b (diff) | |
download | linux-177808cd28ac793d654bb1ae5ae1f778e7b3864f.tar.xz |
Merge tag 'hwmon-for-linus-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
"Notable changes:
- new driver for NCT7802Y
- support for TMP435, LM95233, LM95235, NCT6792D, and NXP LM75B
- regulator support for PMBus chips, specifically LTX2978
- support for humidity sensors to iio-hwmon bridge driver
* tag 'hwmon-for-linus-3.19' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (21 commits)
hwmon: (tmp401) Detect TMP435 on all addresses it supports
hwmon: (lm75) Strengthen detect function
hwmon: (gpio-fan) Add a shutdown handler to poweroff the fans
hwmon: (gpio-fan) Allow usage of gpio operations that may sleep
hwmon: (tmp401) Bail out from tmp401_probe() in case of write errors
hwmon: (tmp401) Add support for TI TMP435
hwmon: (lm95234) Add support for LM95233
hwmon: (lm95245) Add support for LM95235
hwmon: (ina2xx) bail-out from ina2xx_probe() in case of configuration errors
hwmon: (nct6775) Add blank lines after declarations
hwmon: (nct6775) Add support for NCT6792D
hwmon: (nct6775) Documentation updates
hwmon: (lm75) Add support for the NXP LM75B
hwmon: Driver for Nuvoton NCT7802Y
hwmon: (ibmpowernv) Convert to module_platform_driver
hwmon: (ibmpowernv) Use platform 'id_table' to probe the device
hwmon: (iio_hwmon) Add support for humidity sensors
hwmon: (ltc2978) Add regulator support
hwmon: (pmbus) Add regulator support
hwmon: (pmbus) add helpers for byte write and read modify write
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/Kconfig | 28 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/gpio-fan.c | 18 | ||||
-rw-r--r-- | drivers/hwmon/ibmpowernv.c | 78 | ||||
-rw-r--r-- | drivers/hwmon/iio_hwmon.c | 7 | ||||
-rw-r--r-- | drivers/hwmon/ina2xx.c | 26 | ||||
-rw-r--r-- | drivers/hwmon/lm75.c | 12 | ||||
-rw-r--r-- | drivers/hwmon/lm95234.c | 91 | ||||
-rw-r--r-- | drivers/hwmon/lm95245.c | 41 | ||||
-rw-r--r-- | drivers/hwmon/nct6775.c | 77 | ||||
-rw-r--r-- | drivers/hwmon/nct7802.c | 860 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/Kconfig | 11 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/ltc2978.c | 39 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/pmbus.h | 30 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/pmbus_core.c | 118 | ||||
-rw-r--r-- | drivers/hwmon/tmp401.c | 42 |
16 files changed, 1327 insertions, 152 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5286d7ce1f9e..6529c09c46f0 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1028,11 +1028,11 @@ config SENSORS_LM93 will be called lm93. config SENSORS_LM95234 - tristate "National Semiconductor LM95234" + tristate "National Semiconductor LM95234 and compatibles" depends on I2C help - If you say yes here you get support for the LM95234 temperature - sensor. + If you say yes here you get support for the LM95233 and LM95234 + temperature sensor chips. This driver can also be built as a module. If so, the module will be called lm95234. @@ -1048,10 +1048,11 @@ config SENSORS_LM95241 will be called lm95241. config SENSORS_LM95245 - tristate "National Semiconductor LM95245 sensor chip" + tristate "National Semiconductor LM95245 and compatibles" depends on I2C help - If you say yes here you get support for LM95245 sensor chip. + If you say yes here you get support for LM95235 and LM95245 + temperature sensor chips. This driver can also be built as a module. If so, the module will be called lm95245. @@ -1117,12 +1118,23 @@ config SENSORS_NCT6775 help If you say yes here you get support for the hardware monitoring functionality of the Nuvoton NCT6106D, NCT6775F, NCT6776F, NCT6779D, - NCT6791D and compatible Super-I/O chips. This driver replaces the - w83627ehf driver for NCT6775F and NCT6776F. + NCT6791D, NCT6792D and compatible Super-I/O chips. This driver + replaces the w83627ehf driver for NCT6775F and NCT6776F. This driver can also be built as a module. If so, the module will be called nct6775. +config SENSORS_NCT7802 + tristate "Nuvoton NCT7802Y" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the Nuvoton NCT7802Y + hardware monitoring chip. + + This driver can also be built as a module. If so, the module + will be called nct7802. + config SENSORS_PCF8591 tristate "Philips PCF8591 ADC/DAC" depends on I2C @@ -1454,7 +1466,7 @@ config SENSORS_TMP401 depends on I2C help If you say yes here you get support for Texas Instruments TMP401, - TMP411, TMP431, and TMP432 temperature sensor chips. + TMP411, TMP431, TMP432 and TMP435 temperature sensor chips. This driver can also be built as a module. If so, the module will be called tmp401. diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index c90a7611efaa..67280643bcf0 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -118,6 +118,7 @@ obj-$(CONFIG_SENSORS_MCP3021) += mcp3021.o obj-$(CONFIG_SENSORS_MENF21BMC_HWMON) += menf21bmc_hwmon.o obj-$(CONFIG_SENSORS_NCT6683) += nct6683.o obj-$(CONFIG_SENSORS_NCT6775) += nct6775.o +obj-$(CONFIG_SENSORS_NCT7802) += nct7802.o obj-$(CONFIG_SENSORS_NTC_THERMISTOR) += ntc_thermistor.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 4efa1734bdad..36abf814b8c7 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -79,7 +79,7 @@ static ssize_t show_fan_alarm(struct device *dev, { struct gpio_fan_data *fan_data = dev_get_drvdata(dev); struct gpio_fan_alarm *alarm = fan_data->alarm; - int value = gpio_get_value(alarm->gpio); + int value = gpio_get_value_cansleep(alarm->gpio); if (alarm->active_low) value = !value; @@ -131,7 +131,7 @@ static void __set_fan_ctrl(struct gpio_fan_data *fan_data, int ctrl_val) int i; for (i = 0; i < fan_data->num_ctrl; i++) - gpio_set_value(fan_data->ctrl[i], (ctrl_val >> i) & 1); + gpio_set_value_cansleep(fan_data->ctrl[i], (ctrl_val >> i) & 1); } static int __get_fan_ctrl(struct gpio_fan_data *fan_data) @@ -142,7 +142,7 @@ static int __get_fan_ctrl(struct gpio_fan_data *fan_data) for (i = 0; i < fan_data->num_ctrl; i++) { int value; - value = gpio_get_value(fan_data->ctrl[i]); + value = gpio_get_value_cansleep(fan_data->ctrl[i]); ctrl_val |= (value << i); } return ctrl_val; @@ -369,7 +369,8 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data, if (err) return err; - err = gpio_direction_output(ctrl[i], gpio_get_value(ctrl[i])); + err = gpio_direction_output(ctrl[i], + gpio_get_value_cansleep(ctrl[i])); if (err) return err; } @@ -549,6 +550,14 @@ static int gpio_fan_probe(struct platform_device *pdev) return 0; } +static void gpio_fan_shutdown(struct platform_device *pdev) +{ + struct gpio_fan_data *fan_data = dev_get_drvdata(&pdev->dev); + + if (fan_data->ctrl) + set_fan_speed(fan_data, 0); +} + #ifdef CONFIG_PM_SLEEP static int gpio_fan_suspend(struct device *dev) { @@ -580,6 +589,7 @@ static SIMPLE_DEV_PM_OPS(gpio_fan_pm, gpio_fan_suspend, gpio_fan_resume); static struct platform_driver gpio_fan_driver = { .probe = gpio_fan_probe, + .shutdown = gpio_fan_shutdown, .driver = { .name = "gpio-fan", .pm = GPIO_FAN_PM, diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index 6a30eeea94be..7c2c7be182f2 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -74,9 +74,6 @@ struct platform_data { u32 sensors_count; /* Total count of sensors from each group */ }; -/* Platform device representing all the ibmpowernv sensors */ -static struct platform_device *pdevice; - static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, char *buf) { @@ -99,7 +96,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%u\n", x); } -static int __init get_sensor_index_attr(const char *name, u32 *index, +static int get_sensor_index_attr(const char *name, u32 *index, char *attr) { char *hash_pos = strchr(name, '#'); @@ -136,7 +133,7 @@ static int __init get_sensor_index_attr(const char *name, u32 *index, * which need to be mapped as fan2_input, temp1_max respectively before * populating them inside hwmon device class. */ -static int __init create_hwmon_attr_name(struct device *dev, enum sensors type, +static int create_hwmon_attr_name(struct device *dev, enum sensors type, const char *node_name, char *hwmon_attr_name) { @@ -172,7 +169,7 @@ static int __init create_hwmon_attr_name(struct device *dev, enum sensors type, return 0; } -static int __init populate_attr_groups(struct platform_device *pdev) +static int populate_attr_groups(struct platform_device *pdev) { struct platform_data *pdata = platform_get_drvdata(pdev); const struct attribute_group **pgroups = pdata->attr_groups; @@ -180,11 +177,6 @@ static int __init populate_attr_groups(struct platform_device *pdev) enum sensors type; opal = of_find_node_by_path("/ibm,opal/sensors"); - if (!opal) { - dev_dbg(&pdev->dev, "Opal node 'sensors' not found\n"); - return -ENODEV; - } - for_each_child_of_node(opal, np) { if (np->name == NULL) continue; @@ -221,7 +213,7 @@ static int __init populate_attr_groups(struct platform_device *pdev) * to the name required by the higher 'hwmon' driver like fan1_input, temp1_max * etc.. */ -static int __init create_device_attrs(struct platform_device *pdev) +static int create_device_attrs(struct platform_device *pdev) { struct platform_data *pdata = platform_get_drvdata(pdev); const struct attribute_group **pgroups = pdata->attr_groups; @@ -280,7 +272,7 @@ exit_put_node: return err; } -static int __init ibmpowernv_probe(struct platform_device *pdev) +static int ibmpowernv_probe(struct platform_device *pdev) { struct platform_data *pdata; struct device *hwmon_dev; @@ -309,57 +301,25 @@ static int __init ibmpowernv_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(hwmon_dev); } -static struct platform_driver ibmpowernv_driver = { - .driver = { - .owner = THIS_MODULE, - .name = DRVNAME, +static const struct platform_device_id opal_sensor_driver_ids[] = { + { + .name = "opal-sensor", }, + { } }; +MODULE_DEVICE_TABLE(platform, opal_sensor_driver_ids); -static int __init ibmpowernv_init(void) -{ - int err; - - pdevice = platform_device_alloc(DRVNAME, 0); - if (!pdevice) { - pr_err("Device allocation failed\n"); - err = -ENOMEM; - goto exit; - } - - err = platform_device_add(pdevice); - if (err) { - pr_err("Device addition failed (%d)\n", err); - goto exit_device_put; - } - - err = platform_driver_probe(&ibmpowernv_driver, ibmpowernv_probe); - if (err) { - if (err != -ENODEV) - pr_err("Platform driver probe failed (%d)\n", err); - - goto exit_device_del; - } - - return 0; - -exit_device_del: - platform_device_del(pdevice); -exit_device_put: - platform_device_put(pdevice); -exit: - return err; -} +static struct platform_driver ibmpowernv_driver = { + .probe = ibmpowernv_probe, + .id_table = opal_sensor_driver_ids, + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, +}; -static void __exit ibmpowernv_exit(void) -{ - platform_driver_unregister(&ibmpowernv_driver); - platform_device_unregister(pdevice); -} +module_platform_driver(ibmpowernv_driver); MODULE_AUTHOR("Neelesh Gupta <neelegup@linux.vnet.ibm.com>"); MODULE_DESCRIPTION("IBM POWERNV platform sensors"); MODULE_LICENSE("GPL"); - -module_init(ibmpowernv_init); -module_exit(ibmpowernv_exit); diff --git a/drivers/hwmon/iio_hwmon.c b/drivers/hwmon/iio_hwmon.c index 14c82daab019..980175628563 100644 --- a/drivers/hwmon/iio_hwmon.c +++ b/drivers/hwmon/iio_hwmon.c @@ -63,7 +63,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; + int in_i = 1, temp_i = 1, curr_i = 1, humidity_i = 1; enum iio_chan_type type; struct iio_channel *channels; const char *name = "iio_hwmon"; @@ -123,6 +123,11 @@ static int iio_hwmon_probe(struct platform_device *pdev) "curr%d_input", curr_i++); break; + case IIO_HUMIDITYRELATIVE: + a->dev_attr.attr.name = kasprintf(GFP_KERNEL, + "humidity%d_input", + humidity_i++); + break; default: ret = -EINVAL; goto error_release_channels; diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c index bfd3f3eeabcd..e01feba909c3 100644 --- a/drivers/hwmon/ina2xx.c +++ b/drivers/hwmon/ina2xx.c @@ -223,6 +223,7 @@ static int ina2xx_probe(struct i2c_client *client, struct device *hwmon_dev; long shunt = 10000; /* default shunt value 10mOhms */ u32 val; + int ret; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; @@ -247,12 +248,25 @@ static int ina2xx_probe(struct i2c_client *client, data->config = &ina2xx_config[data->kind]; /* device configuration */ - i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, - data->config->config_default); - /* set current LSB to 1mA, shunt is in uOhms */ - /* (equation 13 in datasheet) */ - i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, - data->config->calibration_factor / shunt); + ret = i2c_smbus_write_word_swapped(client, INA2XX_CONFIG, + data->config->config_default); + if (ret < 0) { + dev_err(dev, + "error writing to the config register: %d", ret); + return -ENODEV; + } + + /* + * Set current LSB to 1mA, shunt is in uOhms + * (equation 13 in datasheet). + */ + ret = i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION, + data->config->calibration_factor / shunt); + if (ret < 0) { + dev_err(dev, + "error writing to the calibration register: %d", ret); + return -ENODEV; + } data->client = client; mutex_init(&data->update_lock); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index d16dbb33a531..6753fd940c76 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -44,6 +44,7 @@ enum lm75_type { /* keep sorted in alphabetical order */ g751, lm75, lm75a, + lm75b, max6625, max6626, mcp980x, @@ -233,6 +234,10 @@ lm75_probe(struct i2c_client *client, const struct i2c_device_id *id) data->resolution = 9; data->sample_time = HZ / 2; break; + case lm75b: + data->resolution = 11; + data->sample_time = HZ / 4; + break; case max6625: data->resolution = 9; data->sample_time = HZ / 4; @@ -322,6 +327,7 @@ static const struct i2c_device_id lm75_ids[] = { { "g751", g751, }, { "lm75", lm75, }, { "lm75a", lm75a, }, + { "lm75b", lm75b, }, { "max6625", max6625, }, { "max6626", max6626, }, { "mcp980x", mcp980x, }, @@ -409,6 +415,12 @@ static int lm75_detect(struct i2c_client *new_client, || i2c_smbus_read_byte_data(new_client, 7) != os) return -ENODEV; } + /* + * It is very unlikely that this is a LM75 if both + * hysteresis and temperature limit registers are 0. + */ + if (hyst == 0 && os == 0) + return -ENODEV; /* Addresses cycling */ for (i = 8; i <= 248; i += 40) { diff --git a/drivers/hwmon/lm95234.c b/drivers/hwmon/lm95234.c index 411202bdaf6b..8796de39ff9b 100644 --- a/drivers/hwmon/lm95234.c +++ b/drivers/hwmon/lm95234.c @@ -1,7 +1,7 @@ /* * Driver for Texas Instruments / National Semiconductor LM95234 * - * Copyright (c) 2013 Guenter Roeck <linux@roeck-us.net> + * Copyright (c) 2013, 2014 Guenter Roeck <linux@roeck-us.net> * * Derived from lm95241.c * Copyright (C) 2008, 2010 Davide Rizzo <elpa.rizzo@gmail.com> @@ -30,7 +30,10 @@ #define DRVNAME "lm95234" -static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END }; +enum chips { lm95233, lm95234 }; + +static const unsigned short normal_i2c[] = { + 0x18, 0x2a, 0x2b, 0x4d, 0x4e, I2C_CLIENT_END }; /* LM95234 registers */ #define LM95234_REG_MAN_ID 0xFE @@ -53,11 +56,13 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4d, 0x4e, I2C_CLIENT_END }; #define LM95234_REG_TCRIT_HYST 0x5a #define NATSEMI_MAN_ID 0x01 +#define LM95233_CHIP_ID 0x89 #define LM95234_CHIP_ID 0x79 /* Client data (each client gets its own) */ struct lm95234_data { struct i2c_client *client; + const struct attribute_group *groups[3]; struct mutex update_lock; unsigned long last_updated, interval; /* in jiffies */ bool valid; /* false until following fields are valid */ @@ -564,35 +569,23 @@ static SENSOR_DEVICE_ATTR(temp5_offset, S_IWUSR | S_IRUGO, show_offset, static DEVICE_ATTR(update_interval, S_IWUSR | S_IRUGO, show_interval, set_interval); -static struct attribute *lm95234_attrs[] = { +static struct attribute *lm95234_common_attrs[] = { &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, - &sensor_dev_attr_temp4_input.dev_attr.attr, - &sensor_dev_attr_temp5_input.dev_attr.attr, &sensor_dev_attr_temp2_fault.dev_attr.attr, &sensor_dev_attr_temp3_fault.dev_attr.attr, - &sensor_dev_attr_temp4_fault.dev_attr.attr, - &sensor_dev_attr_temp5_fault.dev_attr.attr, &sensor_dev_attr_temp2_type.dev_attr.attr, &sensor_dev_attr_temp3_type.dev_attr.attr, - &sensor_dev_attr_temp4_type.dev_attr.attr, - &sensor_dev_attr_temp5_type.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, - &sensor_dev_attr_temp4_max.dev_attr.attr, - &sensor_dev_attr_temp5_max.dev_attr.attr, &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, &sensor_dev_attr_temp2_max_hyst.dev_attr.attr, &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, - &sensor_dev_attr_temp5_max_hyst.dev_attr.attr, &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, - &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, &sensor_dev_attr_temp2_crit.dev_attr.attr, &sensor_dev_attr_temp3_crit.dev_attr.attr, &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr, @@ -601,18 +594,44 @@ static struct attribute *lm95234_attrs[] = { &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, &sensor_dev_attr_temp2_offset.dev_attr.attr, &sensor_dev_attr_temp3_offset.dev_attr.attr, + &dev_attr_update_interval.attr, + NULL +}; + +static const struct attribute_group lm95234_common_group = { + .attrs = lm95234_common_attrs, +}; + +static struct attribute *lm95234_attrs[] = { + &sensor_dev_attr_temp4_input.dev_attr.attr, + &sensor_dev_attr_temp5_input.dev_attr.attr, + &sensor_dev_attr_temp4_fault.dev_attr.attr, + &sensor_dev_attr_temp5_fault.dev_attr.attr, + &sensor_dev_attr_temp4_type.dev_attr.attr, + &sensor_dev_attr_temp5_type.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp4_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp5_max_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, &sensor_dev_attr_temp4_offset.dev_attr.attr, &sensor_dev_attr_temp5_offset.dev_attr.attr, - &dev_attr_update_interval.attr, NULL }; -ATTRIBUTE_GROUPS(lm95234); + +static const struct attribute_group lm95234_group = { + .attrs = lm95234_attrs, +}; static int lm95234_detect(struct i2c_client *client, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int address = client->addr; + u8 config_mask, model_mask; int mfg_id, chip_id, val; + const char *name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; @@ -622,15 +641,31 @@ static int lm95234_detect(struct i2c_client *client, return -ENODEV; chip_id = i2c_smbus_read_byte_data(client, LM95234_REG_CHIP_ID); - if (chip_id != LM95234_CHIP_ID) + switch (chip_id) { + case LM95233_CHIP_ID: + if (address != 0x18 && address != 0x2a && address != 0x2b) + return -ENODEV; + config_mask = 0xbf; + model_mask = 0xf9; + name = "lm95233"; + break; + case LM95234_CHIP_ID: + if (address != 0x18 && address != 0x4d && address != 0x4e) + return -ENODEV; + config_mask = 0xbc; + model_mask = 0xe1; + name = "lm95234"; + break; + default: return -ENODEV; + } val = i2c_smbus_read_byte_data(client, LM95234_REG_STATUS); if (val & 0x30) return -ENODEV; val = i2c_smbus_read_byte_data(client, LM95234_REG_CONFIG); - if (val & 0xbc) + if (val & config_mask) return -ENODEV; val = i2c_smbus_read_byte_data(client, LM95234_REG_CONVRATE); @@ -638,14 +673,14 @@ static int lm95234_detect(struct i2c_client *client, return -ENODEV; val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL); - if (val & 0xe1) + if (val & model_mask) return -ENODEV; val = i2c_smbus_read_byte_data(client, LM95234_REG_REM_MODEL_STS); - if (val & 0xe1) + if (val & model_mask) return -ENODEV; - strlcpy(info->type, "lm95234", I2C_NAME_SIZE); + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -698,15 +733,19 @@ static int lm95234_probe(struct i2c_client *client, if (err < 0) return err; + data->groups[0] = &lm95234_common_group; + if (id->driver_data == lm95234) + data->groups[1] = &lm95234_group; + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - data, - lm95234_groups); + data, data->groups); return PTR_ERR_OR_ZERO(hwmon_dev); } /* Driver data (common to all clients) */ static const struct i2c_device_id lm95234_id[] = { - { "lm95234", 0 }, + { "lm95233", lm95233 }, + { "lm95234", lm95234 }, { } }; MODULE_DEVICE_TABLE(i2c, lm95234_id); @@ -725,5 +764,5 @@ static struct i2c_driver lm95234_driver = { module_i2c_driver(lm95234_driver); MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); -MODULE_DESCRIPTION("LM95234 sensor driver"); +MODULE_DESCRIPTION("LM95233/LM95234 sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/lm95245.c b/drivers/hwmon/lm95245.c index 0ae0dfdafdff..e7aef4561c83 100644 --- a/drivers/hwmon/lm95245.c +++ b/drivers/hwmon/lm95245.c @@ -1,10 +1,8 @@ /* * Copyright (C) 2011 Alexander Stein <alexander.stein@systec-electronic.com> * - * The LM95245 is a sensor chip made by National Semiconductors. + * The LM95245 is a sensor chip made by TI / National Semiconductor. * It reports up to two temperatures (its own plus an external one). - * Complete datasheet can be obtained from National's website at: - * http://www.national.com/ds.cgi/LM/LM95245.pdf * * This driver is based on lm95241.c * @@ -34,8 +32,6 @@ #include <linux/mutex.h> #include <linux/sysfs.h> -#define DEVNAME "lm95245" - static const unsigned short normal_i2c[] = { 0x18, 0x19, 0x29, 0x4c, 0x4d, I2C_CLIENT_END }; @@ -98,7 +94,8 @@ static const unsigned short normal_i2c[] = { #define STATUS1_LOC 0x01 #define MANUFACTURER_ID 0x01 -#define DEFAULT_REVISION 0xB3 +#define LM95235_REVISION 0xB1 +#define LM95245_REVISION 0xB3 static const u8 lm95245_reg_address[] = { LM95245_REG_R_LOCAL_TEMPH_S, @@ -427,17 +424,32 @@ static int lm95245_detect(struct i2c_client *new_client, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; + int address = new_client->addr; + const char *name; + int rev, id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID) - != MANUFACTURER_ID - || i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID) - != DEFAULT_REVISION) + id = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_MAN_ID); + if (id != MANUFACTURER_ID) return -ENODEV; - strlcpy(info->type, DEVNAME, I2C_NAME_SIZE); + rev = i2c_smbus_read_byte_data(new_client, LM95245_REG_R_CHIP_ID); + switch (rev) { + case LM95235_REVISION: + if (address != 0x18 && address != 0x29 && address != 0x4c) + return -ENODEV; + name = "lm95235"; + break; + case LM95245_REVISION: + name = "lm95245"; + break; + default: + return -ENODEV; + } + + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } @@ -484,7 +496,8 @@ static int lm95245_probe(struct i2c_client *client, /* Driver data (common to all clients) */ static const struct i2c_device_id lm95245_id[] = { - { DEVNAME, 0 }, + { "lm95235", 0 }, + { "lm95245", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, lm95245_id); @@ -492,7 +505,7 @@ MODULE_DEVICE_TABLE(i2c, lm95245_id); static struct i2c_driver lm95245_driver = { .class = I2C_CLASS_HWMON, .driver = { - .name = DEVNAME, + .name = "lm95245", }, .probe = lm95245_probe, .id_table = lm95245_id, @@ -503,5 +516,5 @@ static struct i2c_driver lm95245_driver = { module_i2c_driver(lm95245_driver); MODULE_AUTHOR("Alexander Stein <alexander.stein@systec-electronic.com>"); -MODULE_DESCRIPTION("LM95245 sensor driver"); +MODULE_DESCRIPTION("LM95235/LM95245 sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 504cbddbdd90..dc0df57200cd 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -38,6 +38,7 @@ * nct6776f 9 5 3 6+3 0xc330 0xc1 0x5ca3 * nct6779d 15 5 5 2+6 0xc560 0xc1 0x5ca3 * nct6791d 15 6 6 2+6 0xc800 0xc1 0x5ca3 + * nct6792d 15 6 6 2+6 0xc910 0xc1 0x5ca3 * * #temp lists the number of monitored temperature sources (first value) plus * the number of directly connectable temperature sensors (second value). @@ -61,7 +62,7 @@ #define USE_ALTERNATE -enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791 }; +enum kinds { nct6106, nct6775, nct6776, nct6779, nct6791, nct6792 }; /* used to set data->name = nct6775_device_names[data->sio_kind] */ static const char * const nct6775_device_names[] = { @@ -70,6 +71,7 @@ static const char * const nct6775_device_names[] = { "nct6776", "nct6779", "nct6791", + "nct6792", }; static unsigned short force_id; @@ -100,6 +102,7 @@ MODULE_PARM_DESC(fan_debounce, "Enable debouncing for fan RPM signal"); #define SIO_NCT6776_ID 0xc330 #define SIO_NCT6779_ID 0xc560 #define SIO_NCT6791_ID 0xc800 +#define SIO_NCT6792_ID 0xc910 #define SIO_ID_MASK 0xFFF0 enum pwm_enable { off, manual, thermal_cruise, speed_cruise, sf3, sf4 }; @@ -529,6 +532,12 @@ static const s8 NCT6791_ALARM_BITS[] = { 4, 5, 13, -1, -1, -1, /* temp1..temp6 */ 12, 9 }; /* intrusion0, intrusion1 */ +/* NCT6792 specific data */ + +static const u16 NCT6792_REG_TEMP_MON[] = { + 0x73, 0x75, 0x77, 0x79, 0x7b, 0x7d }; +static const u16 NCT6792_REG_BEEP[NUM_REG_BEEP] = { + 0xb2, 0xb3, 0xb4, 0xb5, 0xbf }; /* NCT6102D/NCT6106D specific data */ @@ -1043,13 +1052,14 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) reg == 0x73 || reg == 0x75 || reg == 0x77; case nct6779: case nct6791: + case nct6792: return reg == 0x150 || reg == 0x153 || reg == 0x155 || ((reg & 0xfff0) == 0x4b0 && (reg & 0x000f) < 0x0b) || reg == 0x402 || reg == 0x63a || reg == 0x63c || reg == 0x63e || reg == 0x640 || reg == 0x642 || reg == 0x73 || reg == 0x75 || reg == 0x77 || reg == 0x79 || - reg == 0x7b; + reg == 0x7b || reg == 0x7d; } return false; } @@ -1063,6 +1073,7 @@ static bool is_word_sized(struct nct6775_data *data, u16 reg) static inline void nct6775_set_bank(struct nct6775_data *data, u16 reg) { u8 bank = reg >> 8; + if (data->bank != bank) { outb_p(NCT6775_REG_BANK, data->addr + ADDR_REG_OFFSET); outb_p(bank, data->addr + DATA_REG_OFFSET); @@ -1300,6 +1311,7 @@ static void nct6775_update_pwm(struct device *dev) if (!data->target_speed_tolerance[i] || data->pwm_enable[i] == speed_cruise) { u8 t = fanmodecfg & 0x0f; + if (data->REG_TOLERANCE_H) { t |= (nct6775_read_value(data, data->REG_TOLERANCE_H[i]) & 0x70) >> 1; @@ -1391,6 +1403,7 @@ static void nct6775_update_pwm_limits(struct device *dev) case nct6106: case nct6779: case nct6791: + case nct6792: reg = nct6775_read_value(data, data->REG_CRITICAL_PWM_ENABLE[i]); if (reg & data->CRITICAL_PWM_ENABLE_MASK) @@ -1473,6 +1486,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) data->alarms = 0; for (i = 0; i < NUM_REG_ALARM; i++) { u8 alarm; + if (!data->REG_ALARM[i]) continue; alarm = nct6775_read_value(data, data->REG_ALARM[i]); @@ -1482,6 +1496,7 @@ static struct nct6775_data *nct6775_update_device(struct device *dev) data->beeps = 0; for (i = 0; i < NUM_REG_BEEP; i++) { u8 beep; + if (!data->REG_BEEP[i]) continue; beep = nct6775_read_value(data, data->REG_BEEP[i]); @@ -1504,8 +1519,9 @@ show_in_reg(struct device *dev, struct device_attribute *attr, char *buf) { struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - int nr = sattr->nr; int index = sattr->index; + int nr = sattr->nr; + return sprintf(buf, "%ld\n", in_from_reg(data->in[nr][index], nr)); } @@ -1515,10 +1531,12 @@ store_in_reg(struct device *dev, struct device_attribute *attr, const char *buf, { struct nct6775_data *data = dev_get_drvdata(dev); struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); - int nr = sattr->nr; int index = sattr->index; + int nr = sattr->nr; unsigned long val; - int err = kstrtoul(buf, 10, &val); + int err; + + err = kstrtoul(buf, 10, &val); if (err < 0) return err; mutex_lock(&data->update_lock); @@ -1535,6 +1553,7 @@ show_alarm(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = data->ALARM_BITS[sattr->index]; + return sprintf(buf, "%u\n", (unsigned int)((data->alarms >> nr) & 0x01)); } @@ -1570,6 +1589,7 @@ show_temp_alarm(struct device *dev, struct device_attribute *attr, char *buf) nr = find_temp_source(data, sattr->index, data->num_temp_alarms); if (nr >= 0) { int bit = data->ALARM_BITS[nr + TEMP_ALARM_BASE]; + alarm = (data->alarms >> bit) & 0x01; } return sprintf(buf, "%u\n", alarm); @@ -1595,8 +1615,9 @@ store_beep(struct device *dev, struct device_attribute *attr, const char *buf, int nr = data->BEEP_BITS[sattr->index]; int regindex = nr >> 3; unsigned long val; + int err; - int err = kstrtoul(buf, 10, &val); + err = kstrtoul(buf, 10, &val); if (err < 0) return err; if (val > 1) @@ -1629,6 +1650,7 @@ show_temp_beep(struct device *dev, struct device_attribute *attr, char *buf) nr = find_temp_source(data, sattr->index, data->num_temp_beeps); if (nr >= 0) { int bit = data->BEEP_BITS[nr + TEMP_ALARM_BASE]; + beep = (data->beeps >> bit) & 0x01; } return sprintf(buf, "%u\n", beep); @@ -1642,8 +1664,9 @@ store_temp_beep(struct device *dev, struct device_attribute *attr, struct nct6775_data *data = dev_get_drvdata(dev); int nr, bit, regindex; unsigned long val; + int err; - int err = kstrtoul(buf, 10, &val); + err = kstrtoul(buf, 10, &val); if (err < 0) return err; if (val > 1) @@ -1715,6 +1738,7 @@ show_fan(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%d\n", data->rpm[nr]); } @@ -1724,6 +1748,7 @@ show_fan_min(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%d\n", data->fan_from_reg_min(data->fan_min[nr], data->fan_div[nr])); @@ -1735,6 +1760,7 @@ show_fan_div(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%u\n", div_from_reg(data->fan_div[nr])); } @@ -1746,9 +1772,9 @@ store_fan_min(struct device *dev, struct device_attribute *attr, struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; unsigned long val; - int err; unsigned int reg; u8 new_div; + int err; err = kstrtoul(buf, 10, &val); if (err < 0) @@ -1932,6 +1958,7 @@ show_temp_label(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%s\n", data->temp_label[data->temp_src[nr]]); } @@ -2008,6 +2035,7 @@ show_temp_type(struct device *dev, struct device_attribute *attr, char *buf) struct nct6775_data *data = nct6775_update_device(dev); struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); int nr = sattr->index; + return sprintf(buf, "%d\n", (int)data->temp_type[nr]); } @@ -2790,6 +2818,7 @@ store_auto_pwm(struct device *dev, struct device_attribute *attr, case nct6106: case nct6779: case nct6791: + case nct6792: nct6775_write_value(data, data->REG_CRITICAL_PWM[nr], val); reg = nct6775_read_value(data, @@ -2997,6 +3026,7 @@ static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) { struct nct6775_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); } @@ -3202,7 +3232,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm4pin = false; pwm5pin = false; pwm6pin = false; - } else { /* NCT6779D or NCT6791D */ + } else { /* NCT6779D, NCT6791D, or NCT6792D */ regval = superio_inb(sioreg, 0x1c); fan3pin = !(regval & (1 << 5)); @@ -3215,7 +3245,7 @@ nct6775_check_fan_inputs(struct nct6775_data *data) fan4min = fan4pin; - if (data->kind == nct6791) { + if (data->kind == nct6791 || data->kind == nct6792) { regval = superio_inb(sioreg, 0x2d); fan6pin = (regval & (1 << 1)); pwm6pin = (regval & (1 << 0)); @@ -3588,6 +3618,7 @@ static int nct6775_probe(struct platform_device *pdev) break; case nct6791: + case nct6792: data->in_num = 15; data->pwm_num = 6; data->auto_pwm_num = 4; @@ -3650,12 +3681,20 @@ static int nct6775_probe(struct platform_device *pdev) data->REG_WEIGHT_TEMP[1] = NCT6791_REG_WEIGHT_TEMP_STEP_TOL; data->REG_WEIGHT_TEMP[2] = NCT6791_REG_WEIGHT_TEMP_BASE; data->REG_ALARM = NCT6791_REG_ALARM; - data->REG_BEEP = NCT6776_REG_BEEP; + if (data->kind == nct6791) + data->REG_BEEP = NCT6776_REG_BEEP; + else + data->REG_BEEP = NCT6792_REG_BEEP; reg_temp = NCT6779_REG_TEMP; - reg_temp_mon = NCT6779_REG_TEMP_MON; num_reg_temp = ARRAY_SIZE(NCT6779_REG_TEMP); - num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON); + if (data->kind == nct6791) { + reg_temp_mon = NCT6779_REG_TEMP_MON; + num_reg_temp_mon = ARRAY_SIZE(NCT6779_REG_TEMP_MON); + } else { + reg_temp_mon = NCT6792_REG_TEMP_MON; + num_reg_temp_mon = ARRAY_SIZE(NCT6792_REG_TEMP_MON); + } reg_temp_over = NCT6779_REG_TEMP_OVER; reg_temp_hyst = NCT6779_REG_TEMP_HYST; reg_temp_config = NCT6779_REG_TEMP_CONFIG; @@ -3854,6 +3893,7 @@ static int nct6775_probe(struct platform_device *pdev) case nct6106: case nct6779: case nct6791: + case nct6792: break; } @@ -3885,6 +3925,7 @@ static int nct6775_probe(struct platform_device *pdev) tmp |= 0x3e; break; case nct6791: + case nct6792: tmp |= 0x7e; break; } @@ -3972,7 +4013,7 @@ static int nct6775_resume(struct device *dev) mutex_lock(&data->update_lock); data->bank = 0xff; /* Force initial bank selection */ - if (data->kind == nct6791) { + if (data->kind == nct6791 || data->kind == nct6792) { err = superio_enter(data->sioreg); if (err) goto abort; @@ -4052,6 +4093,7 @@ static const char * const nct6775_sio_names[] __initconst = { "NCT6776D/F", "NCT6779D", "NCT6791D", + "NCT6792D", }; /* nct6775_find() looks for a '627 in the Super-I/O config space */ @@ -4086,6 +4128,9 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) case SIO_NCT6791_ID: sio_data->kind = nct6791; break; + case SIO_NCT6792_ID: + sio_data->kind = nct6792; + break; default: if (val != 0xffff) pr_debug("unsupported chip ID: 0x%04x\n", val); @@ -4111,7 +4156,7 @@ static int __init nct6775_find(int sioaddr, struct nct6775_sio_data *sio_data) superio_outb(sioaddr, SIO_REG_ENABLE, val | 0x01); } - if (sio_data->kind == nct6791) + if (sio_data->kind == nct6791 || sio_data->kind == nct6792) nct6791_enable_io_mapping(sioaddr); superio_exit(sioaddr); @@ -4221,7 +4266,7 @@ static void __exit sensors_nct6775_exit(void) } MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); -MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D driver"); +MODULE_DESCRIPTION("NCT6106D/NCT6775F/NCT6776F/NCT6779D/NCT6791D/NCT6792D driver"); MODULE_LICENSE("GPL"); module_init(sensors_nct6775_init); diff --git a/drivers/hwmon/nct7802.c b/drivers/hwmon/nct7802.c new file mode 100644 index 000000000000..ec5678289e4a --- /dev/null +++ b/drivers/hwmon/nct7802.c @@ -0,0 +1,860 @@ +/* + * nct7802 - Driver for Nuvoton NCT7802Y + * + * Copyright (C) 2014 Guenter Roeck <linux@roeck-us.net> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/jiffies.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +#define DRVNAME "nct7802" + +static const u8 REG_VOLTAGE[5] = { 0x09, 0x0a, 0x0c, 0x0d, 0x0e }; + +static const u8 REG_VOLTAGE_LIMIT_LSB[2][5] = { + { 0x40, 0x00, 0x42, 0x44, 0x46 }, + { 0x3f, 0x00, 0x41, 0x43, 0x45 }, +}; + +static const u8 REG_VOLTAGE_LIMIT_MSB[5] = { 0x48, 0x00, 0x47, 0x47, 0x48 }; + +static const u8 REG_VOLTAGE_LIMIT_MSB_SHIFT[2][5] = { + { 0, 0, 4, 0, 4 }, + { 2, 0, 6, 2, 6 }, +}; + +#define REG_BANK 0x00 +#define REG_TEMP_LSB 0x05 +#define REG_TEMP_PECI_LSB 0x08 +#define REG_VOLTAGE_LOW 0x0f +#define REG_FANCOUNT_LOW 0x13 +#define REG_START 0x21 +#define REG_MODE 0x22 +#define REG_PECI_ENABLE 0x23 +#define REG_FAN_ENABLE 0x24 +#define REG_VMON_ENABLE 0x25 +#define REG_VENDOR_ID 0xfd +#define REG_CHIP_ID 0xfe +#define REG_VERSION_ID 0xff + +/* + * Data structures and manipulation thereof + */ + +struct nct7802_data { + struct regmap *regmap; + struct mutex access_lock; /* for multi-byte read and write operations */ +}; + +static int nct7802_read_temp(struct nct7802_data *data, + u8 reg_temp, u8 reg_temp_low, int *temp) +{ + unsigned int t1, t2 = 0; + int err; + + *temp = 0; + + mutex_lock(&data->access_lock); + err = regmap_read(data->regmap, reg_temp, &t1); + if (err < 0) + goto abort; + t1 <<= 8; + if (reg_temp_low) { /* 11 bit data */ + err = regmap_read(data->regmap, reg_temp_low, &t2); + if (err < 0) + goto abort; + } + t1 |= t2 & 0xe0; + *temp = (s16)t1 / 32 * 125; +abort: + mutex_unlock(&data->access_lock); + return err; +} + +static int nct7802_read_fan(struct nct7802_data *data, u8 reg_fan) +{ + unsigned int f1, f2; + int ret; + + mutex_lock(&data->access_lock); + ret = regmap_read(data->regmap, reg_fan, &f1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, REG_FANCOUNT_LOW, &f2); + if (ret < 0) + goto abort; + ret = (f1 << 5) | (f2 >> 3); + /* convert fan count to rpm */ + if (ret == 0x1fff) /* maximum value, assume fan is stopped */ + ret = 0; + else if (ret) + ret = DIV_ROUND_CLOSEST(1350000U, ret); +abort: + mutex_unlock(&data->access_lock); + return ret; +} + +static int nct7802_read_fan_min(struct nct7802_data *data, u8 reg_fan_low, + u8 reg_fan_high) +{ + unsigned int f1, f2; + int ret; + + mutex_lock(&data->access_lock); + ret = regmap_read(data->regmap, reg_fan_low, &f1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, reg_fan_high, &f2); + if (ret < 0) + goto abort; + ret = f1 | ((f2 & 0xf8) << 5); + /* convert fan count to rpm */ + if (ret == 0x1fff) /* maximum value, assume no limit */ + ret = 0; + else if (ret) + ret = DIV_ROUND_CLOSEST(1350000U, ret); +abort: + mutex_unlock(&data->access_lock); + return ret; +} + +static int nct7802_write_fan_min(struct nct7802_data *data, u8 reg_fan_low, + u8 reg_fan_high, unsigned int limit) +{ + int err; + + if (limit) + limit = DIV_ROUND_CLOSEST(1350000U, limit); + else + limit = 0x1fff; + limit = clamp_val(limit, 0, 0x1fff); + + mutex_lock(&data->access_lock); + err = regmap_write(data->regmap, reg_fan_low, limit & 0xff); + if (err < 0) + goto abort; + + err = regmap_write(data->regmap, reg_fan_high, (limit & 0x1f00) >> 5); +abort: + mutex_unlock(&data->access_lock); + return err; +} + +static u8 nct7802_vmul[] = { 4, 2, 2, 2, 2 }; + +static int nct7802_read_voltage(struct nct7802_data *data, int nr, int index) +{ + unsigned int v1, v2; + int ret; + + mutex_lock(&data->access_lock); + if (index == 0) { /* voltage */ + ret = regmap_read(data->regmap, REG_VOLTAGE[nr], &v1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, REG_VOLTAGE_LOW, &v2); + if (ret < 0) + goto abort; + ret = ((v1 << 2) | (v2 >> 6)) * nct7802_vmul[nr]; + } else { /* limit */ + int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; + + ret = regmap_read(data->regmap, + REG_VOLTAGE_LIMIT_LSB[index - 1][nr], &v1); + if (ret < 0) + goto abort; + ret = regmap_read(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr], + &v2); + if (ret < 0) + goto abort; + ret = (v1 | ((v2 << shift) & 0x300)) * nct7802_vmul[nr]; + } +abort: + mutex_unlock(&data->access_lock); + return ret; +} + +static int nct7802_write_voltage(struct nct7802_data *data, int nr, int index, + unsigned int voltage) +{ + int shift = 8 - REG_VOLTAGE_LIMIT_MSB_SHIFT[index - 1][nr]; + int err; + + voltage = DIV_ROUND_CLOSEST(voltage, nct7802_vmul[nr]); + voltage = clamp_val(voltage, 0, 0x3ff); + + mutex_lock(&data->access_lock); + err = regmap_write(data->regmap, + REG_VOLTAGE_LIMIT_LSB[index - 1][nr], + voltage & 0xff); + if (err < 0) + goto abort; + + err = regmap_update_bits(data->regmap, REG_VOLTAGE_LIMIT_MSB[nr], + 0x0300 >> shift, (voltage & 0x0300) >> shift); +abort: + mutex_unlock(&data->access_lock); + return err; +} + +static ssize_t show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int voltage; + + voltage = nct7802_read_voltage(data, sattr->nr, sattr->index); + if (voltage < 0) + return voltage; + + return sprintf(buf, "%d\n", voltage); +} + +static ssize_t store_in(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 nct7802_data *data = dev_get_drvdata(dev); + int index = sattr->index; + int nr = sattr->nr; + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + err = nct7802_write_voltage(data, nr, index, val); + return err ? : count; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int err, temp; + + err = nct7802_read_temp(data, sattr->nr, sattr->index, &temp); + if (err < 0) + return err; + + return sprintf(buf, "%d\n", temp); +} + +static ssize_t store_temp(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 nct7802_data *data = dev_get_drvdata(dev); + int nr = sattr->nr; + long val; + int err; + + err = kstrtol(buf, 10, &val); + if (err < 0) + return err; + + val = clamp_val(DIV_ROUND_CLOSEST(val, 1000), -128, 127); + + err = regmap_write(data->regmap, nr, val & 0xff); + return err ? : count; +} + +static ssize_t show_fan(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sattr = to_sensor_dev_attr(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int speed; + + speed = nct7802_read_fan(data, sattr->index); + if (speed < 0) + return speed; + + return sprintf(buf, "%d\n", speed); +} + +static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + int speed; + + speed = nct7802_read_fan_min(data, sattr->nr, sattr->index); + if (speed < 0) + return speed; + + return sprintf(buf, "%d\n", speed); +} + +static ssize_t store_fan_min(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 nct7802_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + + err = nct7802_write_fan_min(data, sattr->nr, sattr->index, val); + return err ? : count; +} + +static ssize_t show_alarm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct nct7802_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + int bit = sattr->index; + unsigned int val; + int ret; + + ret = regmap_read(data->regmap, sattr->nr, &val); + if (ret < 0) + return ret; + + return sprintf(buf, "%u\n", !!(val & (1 << bit))); +} + +static ssize_t +show_beep(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute_2 *sattr = to_sensor_dev_attr_2(attr); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int regval; + int err; + + err = regmap_read(data->regmap, sattr->nr, ®val); + if (err) + return err; + + return sprintf(buf, "%u\n", !!(regval & (1 << sattr->index))); +} + +static ssize_t +store_beep(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 nct7802_data *data = dev_get_drvdata(dev); + unsigned long val; + int err; + + err = kstrtoul(buf, 10, &val); + if (err < 0) + return err; + if (val > 1) + return -EINVAL; + + err = regmap_update_bits(data->regmap, sattr->nr, 1 << sattr->index, + val ? 1 << sattr->index : 0); + return err ? : count; +} + +static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0x01, + REG_TEMP_LSB); +static SENSOR_DEVICE_ATTR_2(temp1_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x31, 0); +static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x30, 0); +static SENSOR_DEVICE_ATTR_2(temp1_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3a, 0); + +static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0x02, + REG_TEMP_LSB); +static SENSOR_DEVICE_ATTR_2(temp2_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x33, 0); +static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x32, 0); +static SENSOR_DEVICE_ATTR_2(temp2_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3b, 0); + +static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 0x03, + REG_TEMP_LSB); +static SENSOR_DEVICE_ATTR_2(temp3_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x35, 0); +static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x34, 0); +static SENSOR_DEVICE_ATTR_2(temp3_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3c, 0); + +static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 0x04, 0); +static SENSOR_DEVICE_ATTR_2(temp4_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x37, 0); +static SENSOR_DEVICE_ATTR_2(temp4_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x36, 0); +static SENSOR_DEVICE_ATTR_2(temp4_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3d, 0); + +static SENSOR_DEVICE_ATTR_2(temp5_input, S_IRUGO, show_temp, NULL, 0x06, + REG_TEMP_PECI_LSB); +static SENSOR_DEVICE_ATTR_2(temp5_min, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x39, 0); +static SENSOR_DEVICE_ATTR_2(temp5_max, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x38, 0); +static SENSOR_DEVICE_ATTR_2(temp5_crit, S_IRUGO | S_IWUSR, show_temp, + store_temp, 0x3e, 0); + +static SENSOR_DEVICE_ATTR_2(temp6_input, S_IRUGO, show_temp, NULL, 0x07, + REG_TEMP_PECI_LSB); + +static SENSOR_DEVICE_ATTR_2(temp1_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 0); +static SENSOR_DEVICE_ATTR_2(temp2_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 1); +static SENSOR_DEVICE_ATTR_2(temp3_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 2); +static SENSOR_DEVICE_ATTR_2(temp4_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 3); +static SENSOR_DEVICE_ATTR_2(temp5_min_alarm, S_IRUGO, show_alarm, NULL, + 0x18, 4); + +static SENSOR_DEVICE_ATTR_2(temp1_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 0); +static SENSOR_DEVICE_ATTR_2(temp2_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 1); +static SENSOR_DEVICE_ATTR_2(temp3_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 2); +static SENSOR_DEVICE_ATTR_2(temp4_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 3); +static SENSOR_DEVICE_ATTR_2(temp5_max_alarm, S_IRUGO, show_alarm, NULL, + 0x19, 4); + +static SENSOR_DEVICE_ATTR_2(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 0); +static SENSOR_DEVICE_ATTR_2(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 1); +static SENSOR_DEVICE_ATTR_2(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 2); +static SENSOR_DEVICE_ATTR_2(temp4_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 3); +static SENSOR_DEVICE_ATTR_2(temp5_crit_alarm, S_IRUGO, show_alarm, NULL, + 0x1b, 4); + +static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, show_alarm, NULL, 0x17, 0); +static SENSOR_DEVICE_ATTR_2(temp2_fault, S_IRUGO, show_alarm, NULL, 0x17, 1); +static SENSOR_DEVICE_ATTR_2(temp3_fault, S_IRUGO, show_alarm, NULL, 0x17, 2); + +static SENSOR_DEVICE_ATTR_2(temp1_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 0); +static SENSOR_DEVICE_ATTR_2(temp2_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 1); +static SENSOR_DEVICE_ATTR_2(temp3_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 2); +static SENSOR_DEVICE_ATTR_2(temp4_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 3); +static SENSOR_DEVICE_ATTR_2(temp5_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 4); +static SENSOR_DEVICE_ATTR_2(temp6_beep, S_IRUGO | S_IWUSR, show_beep, + store_beep, 0x5c, 5); + +static struct attribute *nct7802_temp_attrs[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_crit.dev_attr.attr, + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp1_fault.dev_attr.attr, + &sensor_dev_attr_temp1_beep.dev_attr.attr, + + &sensor_dev_attr_temp2_input.dev_attr.attr, /* 9 */ + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp2_crit.dev_attr.attr, + &sensor_dev_attr_temp2_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_fault.dev_attr.attr, + &sensor_dev_attr_temp2_beep.dev_attr.attr, + + &sensor_dev_attr_temp3_input.dev_attr.attr, /* 18 */ + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp3_crit.dev_attr.attr, + &sensor_dev_attr_temp3_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_fault.dev_attr.attr, + &sensor_dev_attr_temp3_beep.dev_attr.attr, + + &sensor_dev_attr_temp4_input.dev_attr.attr, /* 27 */ + &sensor_dev_attr_temp4_min.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + &sensor_dev_attr_temp4_crit.dev_attr.attr, + &sensor_dev_attr_temp4_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_beep.dev_attr.attr, + + &sensor_dev_attr_temp5_input.dev_attr.attr, /* 35 */ + &sensor_dev_attr_temp5_min.dev_attr.attr, + &sensor_dev_attr_temp5_max.dev_attr.attr, + &sensor_dev_attr_temp5_crit.dev_attr.attr, + &sensor_dev_attr_temp5_min_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_max_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_crit_alarm.dev_attr.attr, + &sensor_dev_attr_temp5_beep.dev_attr.attr, + + &sensor_dev_attr_temp6_input.dev_attr.attr, /* 43 */ + &sensor_dev_attr_temp6_beep.dev_attr.attr, + + NULL +}; + +static umode_t nct7802_temp_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int reg; + int err; + + err = regmap_read(data->regmap, REG_MODE, ®); + if (err < 0) + return 0; + + if (index < 9 && + (reg & 03) != 0x01 && (reg & 0x03) != 0x02) /* RD1 */ + return 0; + if (index >= 9 && index < 18 && + (reg & 0x0c) != 0x04 && (reg & 0x0c) != 0x08) /* RD2 */ + return 0; + if (index >= 18 && index < 27 && (reg & 0x30) != 0x10) /* RD3 */ + return 0; + if (index >= 27 && index < 35) /* local */ + return attr->mode; + + err = regmap_read(data->regmap, REG_PECI_ENABLE, ®); + if (err < 0) + return 0; + + if (index >= 35 && index < 43 && !(reg & 0x01)) /* PECI 0 */ + return 0; + + if (index >= 0x43 && (!(reg & 0x02))) /* PECI 1 */ + return 0; + + return attr->mode; +} + +static struct attribute_group nct7802_temp_group = { + .attrs = nct7802_temp_attrs, + .is_visible = nct7802_temp_is_visible, +}; + +static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0); +static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_in, store_in, + 0, 1); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_in, store_in, + 0, 2); +static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 3); +static SENSOR_DEVICE_ATTR_2(in0_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5a, 3); + +static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 1, 0); + +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 2, 0); +static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_in, store_in, + 2, 1); +static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_in, store_in, + 2, 2); +static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 0); +static SENSOR_DEVICE_ATTR_2(in2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5a, 0); + +static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_in, NULL, 3, 0); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_in, store_in, + 3, 1); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_in, store_in, + 3, 2); +static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 1); +static SENSOR_DEVICE_ATTR_2(in3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5a, 1); + +static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_in, NULL, 4, 0); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_in, store_in, + 4, 1); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_in, store_in, + 4, 2); +static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, show_alarm, NULL, 0x1e, 2); +static SENSOR_DEVICE_ATTR_2(in4_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5a, 2); + +static struct attribute *nct7802_in_attrs[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + &sensor_dev_attr_in0_beep.dev_attr.attr, + + &sensor_dev_attr_in1_input.dev_attr.attr, /* 5 */ + + &sensor_dev_attr_in2_input.dev_attr.attr, /* 6 */ + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in2_beep.dev_attr.attr, + + &sensor_dev_attr_in3_input.dev_attr.attr, /* 11 */ + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &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_min.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + &sensor_dev_attr_in4_beep.dev_attr.attr, + + NULL, +}; + +static umode_t nct7802_in_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7802_data *data = dev_get_drvdata(dev); + unsigned int reg; + int err; + + if (index < 6) /* VCC, VCORE */ + return attr->mode; + + err = regmap_read(data->regmap, REG_MODE, ®); + if (err < 0) + return 0; + + if (index >= 6 && index < 11 && (reg & 0x03) != 0x03) /* VSEN1 */ + return 0; + if (index >= 11 && index < 17 && (reg & 0x0c) != 0x0c) /* VSEN2 */ + return 0; + if (index >= 17 && (reg & 0x30) != 0x30) /* VSEN3 */ + return 0; + + return attr->mode; +} + +static struct attribute_group nct7802_in_group = { + .attrs = nct7802_in_attrs, + .is_visible = nct7802_in_is_visible, +}; + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0x10); +static SENSOR_DEVICE_ATTR_2(fan1_min, S_IRUGO | S_IWUSR, show_fan_min, + store_fan_min, 0x49, 0x4c); +static SENSOR_DEVICE_ATTR_2(fan1_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 0); +static SENSOR_DEVICE_ATTR_2(fan1_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5b, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 0x11); +static SENSOR_DEVICE_ATTR_2(fan2_min, S_IRUGO | S_IWUSR, show_fan_min, + store_fan_min, 0x4a, 0x4d); +static SENSOR_DEVICE_ATTR_2(fan2_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 1); +static SENSOR_DEVICE_ATTR_2(fan2_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5b, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 0x12); +static SENSOR_DEVICE_ATTR_2(fan3_min, S_IRUGO | S_IWUSR, show_fan_min, + store_fan_min, 0x4b, 0x4e); +static SENSOR_DEVICE_ATTR_2(fan3_alarm, S_IRUGO, show_alarm, NULL, 0x1a, 2); +static SENSOR_DEVICE_ATTR_2(fan3_beep, S_IRUGO | S_IWUSR, show_beep, store_beep, + 0x5b, 2); + +static struct attribute *nct7802_fan_attrs[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + &sensor_dev_attr_fan1_beep.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + &sensor_dev_attr_fan2_beep.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, + &sensor_dev_attr_fan3_beep.dev_attr.attr, + + NULL +}; + +static umode_t nct7802_fan_is_visible(struct kobject *kobj, + struct attribute *attr, int index) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7802_data *data = dev_get_drvdata(dev); + int fan = index / 4; /* 4 attributes per fan */ + unsigned int reg; + int err; + + err = regmap_read(data->regmap, REG_FAN_ENABLE, ®); + if (err < 0 || !(reg & (1 << fan))) + return 0; + + return attr->mode; +} + +static struct attribute_group nct7802_fan_group = { + .attrs = nct7802_fan_attrs, + .is_visible = nct7802_fan_is_visible, +}; + +static const struct attribute_group *nct7802_groups[] = { + &nct7802_temp_group, + &nct7802_in_group, + &nct7802_fan_group, + NULL +}; + +static int nct7802_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + int reg; + + /* + * Chip identification registers are only available in bank 0, + * so only attempt chip detection if bank 0 is selected + */ + reg = i2c_smbus_read_byte_data(client, REG_BANK); + if (reg != 0x00) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_VENDOR_ID); + if (reg != 0x50) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_CHIP_ID); + if (reg != 0xc3) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_VERSION_ID); + if (reg < 0 || (reg & 0xf0) != 0x20) + return -ENODEV; + + /* Also validate lower bits of voltage and temperature registers */ + reg = i2c_smbus_read_byte_data(client, REG_TEMP_LSB); + if (reg < 0 || (reg & 0x1f)) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_TEMP_PECI_LSB); + if (reg < 0 || (reg & 0x3f)) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, REG_VOLTAGE_LOW); + if (reg < 0 || (reg & 0x3f)) + return -ENODEV; + + strlcpy(info->type, "nct7802", I2C_NAME_SIZE); + return 0; +} + +static bool nct7802_regmap_is_volatile(struct device *dev, unsigned int reg) +{ + return reg != REG_BANK && reg <= 0x20; +} + +static struct regmap_config nct7802_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = nct7802_regmap_is_volatile, +}; + +static int nct7802_init_chip(struct nct7802_data *data) +{ + int err; + + /* Enable ADC */ + err = regmap_update_bits(data->regmap, REG_START, 0x01, 0x01); + if (err) + return err; + + /* Enable local temperature sensor */ + err = regmap_update_bits(data->regmap, REG_MODE, 0x40, 0x40); + if (err) + return err; + + /* Enable Vcore and VCC voltage monitoring */ + return regmap_update_bits(data->regmap, REG_VMON_ENABLE, 0x03, 0x03); +} + +static int nct7802_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct nct7802_data *data; + struct device *hwmon_dev; + int ret; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + data->regmap = devm_regmap_init_i2c(client, &nct7802_regmap_config); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + mutex_init(&data->access_lock); + + ret = nct7802_init_chip(data); + if (ret < 0) + return ret; + + hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, + data, + nct7802_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const unsigned short nct7802_address_list[] = { + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END +}; + +static const struct i2c_device_id nct7802_idtable[] = { + { "nct7802", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nct7802_idtable); + +static struct i2c_driver nct7802_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = DRVNAME, + }, + .detect = nct7802_detect, + .probe = nct7802_probe, + .id_table = nct7802_idtable, + .address_list = nct7802_address_list, +}; + +module_i2c_driver(nct7802_driver); + +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); +MODULE_DESCRIPTION("NCT7802Y Hardware Monitoring Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index 6e1e4935fc62..a674cd83a4e2 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -47,15 +47,22 @@ config SENSORS_LM25066 be called lm25066. config SENSORS_LTC2978 - tristate "Linear Technologies LTC2974, LTC2978, LTC3880, and LTC3883" + tristate "Linear Technologies LTC2978 and compatibles" default n help If you say yes here you get hardware monitoring support for Linear - Technology LTC2974, LTC2978, LTC3880, and LTC3883. + Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676. This driver can also be built as a module. If so, the module will be called ltc2978. +config SENSORS_LTC2978_REGULATOR + boolean "Regulator support for LTC2978 and compatibles" + depends on SENSORS_LTC2978 && REGULATOR + help + If you say yes here you get regulator support for Linear + Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676. + config SENSORS_MAX16064 tristate "Maxim MAX16064" default n diff --git a/drivers/hwmon/pmbus/ltc2978.c b/drivers/hwmon/pmbus/ltc2978.c index e24ed521051a..0835050ec245 100644 --- a/drivers/hwmon/pmbus/ltc2978.c +++ b/drivers/hwmon/pmbus/ltc2978.c @@ -22,6 +22,7 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/i2c.h> +#include <linux/regulator/driver.h> #include "pmbus.h" enum chips { ltc2974, ltc2977, ltc2978, ltc3880, ltc3883, ltm4676 }; @@ -374,6 +375,19 @@ static const struct i2c_device_id ltc2978_id[] = { }; MODULE_DEVICE_TABLE(i2c, ltc2978_id); +#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR) +static const struct regulator_desc ltc2978_reg_desc[] = { + PMBUS_REGULATOR("vout", 0), + PMBUS_REGULATOR("vout", 1), + PMBUS_REGULATOR("vout", 2), + PMBUS_REGULATOR("vout", 3), + PMBUS_REGULATOR("vout", 4), + PMBUS_REGULATOR("vout", 5), + PMBUS_REGULATOR("vout", 6), + PMBUS_REGULATOR("vout", 7), +}; +#endif /* CONFIG_SENSORS_LTC2978_REGULATOR */ + static int ltc2978_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -487,13 +501,36 @@ static int ltc2978_probe(struct i2c_client *client, default: return -ENODEV; } + +#if IS_ENABLED(CONFIG_SENSORS_LTC2978_REGULATOR) + info->num_regulators = info->pages; + info->reg_desc = ltc2978_reg_desc; + if (info->num_regulators > ARRAY_SIZE(ltc2978_reg_desc)) { + dev_err(&client->dev, "num_regulators too large!"); + info->num_regulators = ARRAY_SIZE(ltc2978_reg_desc); + } +#endif + return pmbus_do_probe(client, id, info); } -/* This is the driver that will be inserted */ +#ifdef CONFIG_OF +static const struct of_device_id ltc2978_of_match[] = { + { .compatible = "lltc,ltc2974" }, + { .compatible = "lltc,ltc2977" }, + { .compatible = "lltc,ltc2978" }, + { .compatible = "lltc,ltc3880" }, + { .compatible = "lltc,ltc3883" }, + { .compatible = "lltc,ltm4676" }, + { } +}; +MODULE_DEVICE_TABLE(of, ltc2978_of_match); +#endif + static struct i2c_driver ltc2978_driver = { .driver = { .name = "ltc2978", + .of_match_table = of_match_ptr(ltc2978_of_match), }, .probe = ltc2978_probe, .remove = pmbus_do_remove, diff --git a/drivers/hwmon/pmbus/pmbus.h b/drivers/hwmon/pmbus/pmbus.h index fa9beb3eb60c..89a23ff836e7 100644 --- a/drivers/hwmon/pmbus/pmbus.h +++ b/drivers/hwmon/pmbus/pmbus.h @@ -19,6 +19,8 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/regulator/driver.h> + #ifndef PMBUS_H #define PMBUS_H @@ -186,6 +188,11 @@ #define PMBUS_VIRT_STATUS_VMON (PMBUS_VIRT_BASE + 35) /* + * OPERATION + */ +#define PB_OPERATION_CONTROL_ON (1<<7) + +/* * CAPABILITY */ #define PB_CAPABILITY_SMBALERT (1<<4) @@ -365,8 +372,27 @@ struct pmbus_driver_info { */ int (*identify)(struct i2c_client *client, struct pmbus_driver_info *info); + + /* Regulator functionality, if supported by this chip driver. */ + int num_regulators; + const struct regulator_desc *reg_desc; }; +/* Regulator ops */ + +extern struct regulator_ops pmbus_regulator_ops; + +/* Macro for filling in array of struct regulator_desc */ +#define PMBUS_REGULATOR(_name, _id) \ + [_id] = { \ + .name = (_name # _id), \ + .id = (_id), \ + .of_match = of_match_ptr(_name # _id), \ + .regulators_node = of_match_ptr("regulators"), \ + .ops = &pmbus_regulator_ops, \ + .owner = THIS_MODULE, \ + } + /* Function declarations */ void pmbus_clear_cache(struct i2c_client *client); @@ -375,6 +401,10 @@ int pmbus_read_word_data(struct i2c_client *client, u8 page, u8 reg); int pmbus_write_word_data(struct i2c_client *client, u8 page, u8 reg, u16 word); int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg); int pmbus_write_byte(struct i2c_client *client, int page, u8 value); +int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, + u8 value); +int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + u8 mask, u8 value); void pmbus_clear_faults(struct i2c_client *client); bool pmbus_check_byte_register(struct i2c_client *client, int page, int reg); bool pmbus_check_word_register(struct i2c_client *client, int page, int reg); diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index 291d11fe93e7..f2e47c7dd808 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -29,6 +29,8 @@ #include <linux/hwmon-sysfs.h> #include <linux/jiffies.h> #include <linux/i2c/pmbus.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> #include "pmbus.h" /* @@ -253,6 +255,37 @@ int pmbus_read_byte_data(struct i2c_client *client, int page, u8 reg) } EXPORT_SYMBOL_GPL(pmbus_read_byte_data); +int pmbus_write_byte_data(struct i2c_client *client, int page, u8 reg, u8 value) +{ + int rv; + + rv = pmbus_set_page(client, page); + if (rv < 0) + return rv; + + return i2c_smbus_write_byte_data(client, reg, value); +} +EXPORT_SYMBOL_GPL(pmbus_write_byte_data); + +int pmbus_update_byte_data(struct i2c_client *client, int page, u8 reg, + u8 mask, u8 value) +{ + unsigned int tmp; + int rv; + + rv = pmbus_read_byte_data(client, page, reg); + if (rv < 0) + return rv; + + tmp = (rv & ~mask) | (value & mask); + + if (tmp != rv) + rv = pmbus_write_byte_data(client, page, reg, tmp); + + return rv; +} +EXPORT_SYMBOL_GPL(pmbus_update_byte_data); + /* * _pmbus_read_byte_data() is similar to pmbus_read_byte_data(), but checks if * a device specific mapping function exists and calls it if necessary. @@ -1727,6 +1760,84 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, return 0; } +#if IS_ENABLED(CONFIG_REGULATOR) +static int pmbus_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + u8 page = rdev_get_id(rdev); + int ret; + + ret = pmbus_read_byte_data(client, page, PMBUS_OPERATION); + if (ret < 0) + return ret; + + return !!(ret & PB_OPERATION_CONTROL_ON); +} + +static int _pmbus_regulator_on_off(struct regulator_dev *rdev, bool enable) +{ + struct device *dev = rdev_get_dev(rdev); + struct i2c_client *client = to_i2c_client(dev->parent); + u8 page = rdev_get_id(rdev); + + return pmbus_update_byte_data(client, page, PMBUS_OPERATION, + PB_OPERATION_CONTROL_ON, + enable ? PB_OPERATION_CONTROL_ON : 0); +} + +static int pmbus_regulator_enable(struct regulator_dev *rdev) +{ + return _pmbus_regulator_on_off(rdev, 1); +} + +static int pmbus_regulator_disable(struct regulator_dev *rdev) +{ + return _pmbus_regulator_on_off(rdev, 0); +} + +struct regulator_ops pmbus_regulator_ops = { + .enable = pmbus_regulator_enable, + .disable = pmbus_regulator_disable, + .is_enabled = pmbus_regulator_is_enabled, +}; +EXPORT_SYMBOL_GPL(pmbus_regulator_ops); + +static int pmbus_regulator_register(struct pmbus_data *data) +{ + struct device *dev = data->dev; + const struct pmbus_driver_info *info = data->info; + const struct pmbus_platform_data *pdata = dev_get_platdata(dev); + struct regulator_dev *rdev; + int i; + + for (i = 0; i < info->num_regulators; i++) { + struct regulator_config config = { }; + + config.dev = dev; + config.driver_data = data; + + if (pdata && pdata->reg_init_data) + config.init_data = &pdata->reg_init_data[i]; + + rdev = devm_regulator_register(dev, &info->reg_desc[i], + &config); + if (IS_ERR(rdev)) { + dev_err(dev, "Failed to register %s regulator\n", + info->reg_desc[i].name); + return PTR_ERR(rdev); + } + } + + return 0; +} +#else +static int pmbus_regulator_register(struct pmbus_data *data) +{ + return 0; +} +#endif + int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, struct pmbus_driver_info *info) { @@ -1781,8 +1892,15 @@ int pmbus_do_probe(struct i2c_client *client, const struct i2c_device_id *id, dev_err(dev, "Failed to register hwmon device\n"); goto out_kfree; } + + ret = pmbus_regulator_register(data); + if (ret) + goto out_unregister; + return 0; +out_unregister: + hwmon_device_unregister(data->hwmon_dev); out_kfree: kfree(data->group.attrs); return ret; diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index 7fa6e7d0b9b6..99664ebc738d 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -44,9 +44,10 @@ #include <linux/sysfs.h> /* Addresses to scan */ -static const unsigned short normal_i2c[] = { 0x4c, 0x4d, 0x4e, I2C_CLIENT_END }; +static const unsigned short normal_i2c[] = { 0x37, 0x48, 0x49, 0x4a, 0x4c, 0x4d, + 0x4e, 0x4f, I2C_CLIENT_END }; -enum chips { tmp401, tmp411, tmp431, tmp432 }; +enum chips { tmp401, tmp411, tmp431, tmp432, tmp435 }; /* * The TMP401 registers, note some registers have different addresses for @@ -136,6 +137,7 @@ static const u8 TMP432_STATUS_REG[] = { #define TMP411C_DEVICE_ID 0x10 #define TMP431_DEVICE_ID 0x31 #define TMP432_DEVICE_ID 0x32 +#define TMP435_DEVICE_ID 0x35 /* * Driver data (common to all clients) @@ -146,6 +148,7 @@ static const struct i2c_device_id tmp401_id[] = { { "tmp411", tmp411 }, { "tmp431", tmp431 }, { "tmp432", tmp432 }, + { "tmp435", tmp435 }, { } }; MODULE_DEVICE_TABLE(i2c, tmp401_id); @@ -613,10 +616,10 @@ static const struct attribute_group tmp432_group = { * Begin non sysfs callback code (aka Real code) */ -static void tmp401_init_client(struct tmp401_data *data, - struct i2c_client *client) +static int tmp401_init_client(struct tmp401_data *data, + struct i2c_client *client) { - int config, config_orig; + int config, config_orig, status = 0; /* Set the conversion rate to 2 Hz */ i2c_smbus_write_byte_data(client, TMP401_CONVERSION_RATE_WRITE, 5); @@ -624,16 +627,18 @@ static void tmp401_init_client(struct tmp401_data *data, /* Start conversions (disable shutdown if necessary) */ config = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); - if (config < 0) { - dev_warn(&client->dev, "Initialization failed!\n"); - return; - } + if (config < 0) + return config; config_orig = config; config &= ~TMP401_CONFIG_SHUTDOWN; if (config != config_orig) - i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config); + status = i2c_smbus_write_byte_data(client, + TMP401_CONFIG_WRITE, + config); + + return status; } static int tmp401_detect(struct i2c_client *client, @@ -675,15 +680,18 @@ static int tmp401_detect(struct i2c_client *client, kind = tmp411; break; case TMP431_DEVICE_ID: - if (client->addr == 0x4e) + if (client->addr != 0x4c && client->addr != 0x4d) return -ENODEV; kind = tmp431; break; case TMP432_DEVICE_ID: - if (client->addr == 0x4e) + if (client->addr != 0x4c && client->addr != 0x4d) return -ENODEV; kind = tmp432; break; + case TMP435_DEVICE_ID: + kind = tmp435; + break; default: return -ENODEV; } @@ -705,11 +713,13 @@ static int tmp401_detect(struct i2c_client *client, static int tmp401_probe(struct i2c_client *client, const struct i2c_device_id *id) { - const char *names[] = { "TMP401", "TMP411", "TMP431", "TMP432" }; + static const char * const names[] = { + "TMP401", "TMP411", "TMP431", "TMP432", "TMP435" + }; struct device *dev = &client->dev; struct device *hwmon_dev; struct tmp401_data *data; - int groups = 0; + int groups = 0, status; data = devm_kzalloc(dev, sizeof(struct tmp401_data), GFP_KERNEL); if (!data) @@ -720,7 +730,9 @@ static int tmp401_probe(struct i2c_client *client, data->kind = id->driver_data; /* Initialize the TMP401 chip */ - tmp401_init_client(data, client); + status = tmp401_init_client(data, client); + if (status < 0) + return status; /* Register sysfs hooks */ data->groups[groups++] = &tmp401_group; |