diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-13 14:39:25 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-04-13 14:39:25 -0700 |
commit | 4b5ca74113a1e02f4a7ea80aea9b977705f3d961 (patch) | |
tree | 2c367930ad3dae93ef612d2cd8c772a436d648d4 /drivers | |
parent | cb0fc55dea8b498c286976bc4833481f6078c061 (diff) | |
parent | f354169e0f7dcd1b2c82cf1f98f6d976e85f74c3 (diff) | |
download | linux-4b5ca74113a1e02f4a7ea80aea9b977705f3d961.tar.bz2 |
Merge tag 'hwmon-for-linus-v4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging
Pull hwmon updates from Guenter Roeck:
"Support for new chips:
- add support for IT8620E, IT8781F, IT8786E, and IT8790E to it87
driver
- add driver for NCT7904
Functional improvements:
- support for new devicetree structure in ibmpowernv driver
- register pwm-fan and gpio-fan drivers as thermal cooling devices
- various minor cleanup and improvements to it87, nct6775, jc42,
ibmpex, and coretemp drivers"
* tag 'hwmon-for-linus-v4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging: (43 commits)
hwmon: (pwm-fan) Update the duty cycle inorder to control the pwm-fan
hwmon: (it87) Use feature macros on sio_data
hwmon: (ibmpowernv) Fix build error seen for some configurations
hwmon: (gpio-fan) Move the thermal registration after registration is complete
hwmon: (ibmpowernv) pretty print labels
hwmon: (ibmpowernv) add a label attribute
hwmon: (ibmpowernv) add support for the new device tree
hwmon: (ibmpowernv) add a helper routine create_hwmon_attr
hwmon: (it87) Add support for 6th fan of IT8620E
hwmon: (it87) Add support for IT8620E
hwmon: (it87) Add support for IT8790E
hwmon: (it87) Introduce feature flag to reflect internal in7 sensor
hwmon: (it87) Introduce configuration field for chip suffix
hwmon: (it87) Fix PWM frequency display for chips with newer PWM control
hwmon: (it87) Fix pwm sysfs attribute removal
hwmon: (ibmpowernv) do not use the OPAL index for hwmon attribute names
hwmon: (ibmpowernv) change create_hwmon_attr_name() prototype
hwmon: (ibmpowernv) add a convert_opal_attr_name() routine
hwmon: (ibmpowernv) add a get_sensor_type() routine
hwmon: (ibmpowernv) replace AMBIENT_TEMP by TEMP
...
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/hwmon/Kconfig | 18 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/coretemp.c | 11 | ||||
-rw-r--r-- | drivers/hwmon/gpio-fan.c | 130 | ||||
-rw-r--r-- | drivers/hwmon/ibmpex.c | 19 | ||||
-rw-r--r-- | drivers/hwmon/ibmpowernv.c | 267 | ||||
-rw-r--r-- | drivers/hwmon/it87.c | 298 | ||||
-rw-r--r-- | drivers/hwmon/jc42.c | 16 | ||||
-rw-r--r-- | drivers/hwmon/nct6775.c | 76 | ||||
-rw-r--r-- | drivers/hwmon/nct7904.c | 593 | ||||
-rw-r--r-- | drivers/hwmon/pwm-fan.c | 184 | ||||
-rw-r--r-- | drivers/hwmon/vexpress.c | 2 |
12 files changed, 1374 insertions, 241 deletions
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 110fade9cb74..25d9e72627e9 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -510,6 +510,7 @@ config SENSORS_G762 config SENSORS_GPIO_FAN tristate "GPIO fan" depends on GPIOLIB + depends on THERMAL || THERMAL=n help If you say yes here you get support for fans connected to GPIO lines. @@ -599,8 +600,8 @@ config SENSORS_IT87 help If you say yes here you get support for ITE IT8705F, IT8712F, IT8716F, IT8718F, IT8720F, IT8721F, IT8726F, IT8728F, IT8758E, - IT8771E, IT8772E, IT8782F, IT8783E/F and IT8603E sensor chips, - and the SiS950 clone. + IT8771E, IT8772E, IT8781F, IT8782F, IT8783E/F, IT8786E, IT8790E, + IT8603E, IT8620E, and IT8623E sensor chips, and the SiS950 clone. This driver can also be built as a module. If so, the module will be called it87. @@ -624,7 +625,7 @@ config SENSORS_JC42 mobile devices and servers. Support will include, but not be limited to, ADT7408, AT30TS00, CAT34TS02, CAT6095, MAX6604, MCP9804, MCP9805, MCP98242, MCP98243, MCP98244, MCP9843, SE97, SE98, STTS424(E), - STTS2002, STTS3000, TSE2002B3, TSE2002GB2, TS3000B3, and TS3000GB2. + STTS2002, STTS3000, TSE2002, TSE2004, TS3000, and TS3001. This driver can also be built as a module. If so, the module will be called jc42. @@ -1145,6 +1146,16 @@ config SENSORS_NCT7802 This driver can also be built as a module. If so, the module will be called nct7802. +config SENSORS_NCT7904 + tristate "Nuvoton NCT7904" + depends on I2C + help + If you say yes here you get support for the Nuvoton NCT7904 + hardware monitoring chip, including manual fan speed control. + + This driver can also be built as a module. If so, the module + will be called nct7904. + config SENSORS_PCF8591 tristate "Philips PCF8591 ADC/DAC" depends on I2C @@ -1164,6 +1175,7 @@ source drivers/hwmon/pmbus/Kconfig config SENSORS_PWM_FAN tristate "PWM fan" depends on (PWM && OF) || COMPILE_TEST + depends on THERMAL || THERMAL=n help If you say yes here you get support for fans connected to PWM lines. The driver uses the generic PWM interface, thus it will work on a diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 6c941472e707..b4a40f17e2aa 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -120,6 +120,7 @@ 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_NCT7904) += nct7904.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/coretemp.c b/drivers/hwmon/coretemp.c index 5b7fec824f10..ed303ba3a593 100644 --- a/drivers/hwmon/coretemp.c +++ b/drivers/hwmon/coretemp.c @@ -397,14 +397,13 @@ static int create_core_attrs(struct temp_data *tdata, struct device *dev, struct device_attribute *devattr, char *buf) = { show_label, show_crit_alarm, show_temp, show_tjmax, show_ttarget }; - static const char *const names[TOTAL_ATTRS] = { - "temp%d_label", "temp%d_crit_alarm", - "temp%d_input", "temp%d_crit", - "temp%d_max" }; + static const char *const suffixes[TOTAL_ATTRS] = { + "label", "crit_alarm", "input", "crit", "max" + }; for (i = 0; i < tdata->attr_size; i++) { - snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, names[i], - attr_no); + snprintf(tdata->attr_name[i], CORETEMP_NAME_LENGTH, + "temp%d_%s", attr_no, suffixes[i]); sysfs_attr_init(&tdata->sd_attrs[i].dev_attr.attr); tdata->sd_attrs[i].dev_attr.attr.name = tdata->attr_name[i]; tdata->sd_attrs[i].dev_attr.attr.mode = S_IRUGO; diff --git a/drivers/hwmon/gpio-fan.c b/drivers/hwmon/gpio-fan.c index 36abf814b8c7..a3dae6d0082a 100644 --- a/drivers/hwmon/gpio-fan.c +++ b/drivers/hwmon/gpio-fan.c @@ -34,10 +34,13 @@ #include <linux/of.h> #include <linux/of_platform.h> #include <linux/of_gpio.h> +#include <linux/thermal.h> struct gpio_fan_data { struct platform_device *pdev; struct device *hwmon_dev; + /* Cooling device if any */ + struct thermal_cooling_device *cdev; struct mutex lock; /* lock GPIOs operations. */ int num_ctrl; unsigned *ctrl; @@ -387,6 +390,53 @@ static int fan_ctrl_init(struct gpio_fan_data *fan_data, return 0; } +static int gpio_fan_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct gpio_fan_data *fan_data = cdev->devdata; + + if (!fan_data) + return -EINVAL; + + *state = fan_data->num_speed - 1; + return 0; +} + +static int gpio_fan_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct gpio_fan_data *fan_data = cdev->devdata; + int r; + + if (!fan_data) + return -EINVAL; + + r = get_fan_speed_index(fan_data); + if (r < 0) + return r; + + *state = r; + return 0; +} + +static int gpio_fan_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long state) +{ + struct gpio_fan_data *fan_data = cdev->devdata; + + if (!fan_data) + return -EINVAL; + + set_fan_speed(fan_data, state); + return 0; +} + +static const struct thermal_cooling_device_ops gpio_fan_cool_ops = { + .get_max_state = gpio_fan_get_max_state, + .get_cur_state = gpio_fan_get_cur_state, + .set_cur_state = gpio_fan_set_cur_state, +}; + #ifdef CONFIG_OF_GPIO /* * Translate OpenFirmware node properties into platform_data @@ -404,10 +454,32 @@ static int gpio_fan_get_of_pdata(struct device *dev, node = dev->of_node; + /* Alarm GPIO if one exists */ + if (of_gpio_named_count(node, "alarm-gpios") > 0) { + struct gpio_fan_alarm *alarm; + int val; + enum of_gpio_flags flags; + + alarm = devm_kzalloc(dev, sizeof(struct gpio_fan_alarm), + GFP_KERNEL); + if (!alarm) + return -ENOMEM; + + val = of_get_named_gpio_flags(node, "alarm-gpios", 0, &flags); + if (val < 0) + return val; + alarm->gpio = val; + alarm->active_low = flags & OF_GPIO_ACTIVE_LOW; + + pdata->alarm = alarm; + } + /* Fill GPIO pin array */ pdata->num_ctrl = of_gpio_count(node); if (pdata->num_ctrl <= 0) { - dev_err(dev, "gpios DT property empty / missing"); + if (pdata->alarm) + return 0; + dev_err(dev, "DT properties empty / missing"); return -ENODEV; } ctrl = devm_kzalloc(dev, pdata->num_ctrl * sizeof(unsigned), @@ -460,26 +532,6 @@ static int gpio_fan_get_of_pdata(struct device *dev, } pdata->speed = speed; - /* Alarm GPIO if one exists */ - if (of_gpio_named_count(node, "alarm-gpios") > 0) { - struct gpio_fan_alarm *alarm; - int val; - enum of_gpio_flags flags; - - alarm = devm_kzalloc(dev, sizeof(struct gpio_fan_alarm), - GFP_KERNEL); - if (!alarm) - return -ENOMEM; - - val = of_get_named_gpio_flags(node, "alarm-gpios", 0, &flags); - if (val < 0) - return val; - alarm->gpio = val; - alarm->active_low = flags & OF_GPIO_ACTIVE_LOW; - - pdata->alarm = alarm; - } - return 0; } @@ -495,6 +547,11 @@ static int gpio_fan_probe(struct platform_device *pdev) struct gpio_fan_data *fan_data; struct gpio_fan_platform_data *pdata = dev_get_platdata(&pdev->dev); + fan_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_fan_data), + GFP_KERNEL); + if (!fan_data) + return -ENOMEM; + #ifdef CONFIG_OF_GPIO if (!pdata) { pdata = devm_kzalloc(&pdev->dev, @@ -512,11 +569,6 @@ static int gpio_fan_probe(struct platform_device *pdev) return -EINVAL; #endif /* CONFIG_OF_GPIO */ - fan_data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_fan_data), - GFP_KERNEL); - if (!fan_data) - return -ENOMEM; - fan_data->pdev = pdev; platform_set_drvdata(pdev, fan_data); mutex_init(&fan_data->lock); @@ -544,18 +596,39 @@ static int gpio_fan_probe(struct platform_device *pdev) gpio_fan_groups); if (IS_ERR(fan_data->hwmon_dev)) return PTR_ERR(fan_data->hwmon_dev); +#ifdef CONFIG_OF_GPIO + /* Optional cooling device register for Device tree platforms */ + fan_data->cdev = thermal_of_cooling_device_register(pdev->dev.of_node, + "gpio-fan", + fan_data, + &gpio_fan_cool_ops); +#else /* CONFIG_OF_GPIO */ + /* Optional cooling device register for non Device tree platforms */ + fan_data->cdev = thermal_cooling_device_register("gpio-fan", fan_data, + &gpio_fan_cool_ops); +#endif /* CONFIG_OF_GPIO */ dev_info(&pdev->dev, "GPIO fan initialized\n"); return 0; } -static void gpio_fan_shutdown(struct platform_device *pdev) +static int gpio_fan_remove(struct platform_device *pdev) { - struct gpio_fan_data *fan_data = dev_get_drvdata(&pdev->dev); + struct gpio_fan_data *fan_data = platform_get_drvdata(pdev); + + if (!IS_ERR(fan_data->cdev)) + thermal_cooling_device_unregister(fan_data->cdev); if (fan_data->ctrl) set_fan_speed(fan_data, 0); + + return 0; +} + +static void gpio_fan_shutdown(struct platform_device *pdev) +{ + gpio_fan_remove(pdev); } #ifdef CONFIG_PM_SLEEP @@ -589,6 +662,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, + .remove = gpio_fan_remove, .shutdown = gpio_fan_shutdown, .driver = { .name = "gpio-fan", diff --git a/drivers/hwmon/ibmpex.c b/drivers/hwmon/ibmpex.c index 030e7ff589be..21b9c72f16bd 100644 --- a/drivers/hwmon/ibmpex.c +++ b/drivers/hwmon/ibmpex.c @@ -56,15 +56,10 @@ static u8 const temp_sensor_sig[] = {0x74, 0x65, 0x6D}; static u8 const watt_sensor_sig[] = {0x41, 0x43}; #define PEX_NUM_SENSOR_FUNCS 3 -static char const * const power_sensor_name_templates[] = { - "%s%d_average", - "%s%d_average_lowest", - "%s%d_average_highest" -}; -static char const * const temp_sensor_name_templates[] = { - "%s%d_input", - "%s%d_input_lowest", - "%s%d_input_highest" +static const char * const sensor_name_suffixes[] = { + "", + "_lowest", + "_highest" }; static void ibmpex_msg_handler(struct ipmi_recv_msg *msg, void *user_msg_data); @@ -355,9 +350,11 @@ static int create_sensor(struct ibmpex_bmc_data *data, int type, return -ENOMEM; if (type == TEMP_SENSOR) - sprintf(n, temp_sensor_name_templates[func], "temp", counter); + sprintf(n, "temp%d_input%s", + counter, sensor_name_suffixes[func]); else if (type == POWER_SENSOR) - sprintf(n, power_sensor_name_templates[func], "power", counter); + sprintf(n, "power%d_average%s", + counter, sensor_name_suffixes[func]); sysfs_attr_init(&data->sensors[sensor].attr[func].dev_attr.attr); data->sensors[sensor].attr[func].dev_attr.attr.name = n; diff --git a/drivers/hwmon/ibmpowernv.c b/drivers/hwmon/ibmpowernv.c index febe8175d36c..4255514b2c72 100644 --- a/drivers/hwmon/ibmpowernv.c +++ b/drivers/hwmon/ibmpowernv.c @@ -30,8 +30,11 @@ #include <linux/platform_device.h> #include <asm/opal.h> #include <linux/err.h> +#include <asm/cputhreads.h> +#include <asm/smp.h> #define MAX_ATTR_LEN 32 +#define MAX_LABEL_LEN 64 /* Sensor suffix name from DT */ #define DT_FAULT_ATTR_SUFFIX "faulted" @@ -44,17 +47,20 @@ */ enum sensors { FAN, - AMBIENT_TEMP, + TEMP, POWER_SUPPLY, POWER_INPUT, MAX_SENSOR_TYPE, }; +#define INVALID_INDEX (-1U) + static struct sensor_group { const char *name; const char *compatible; struct attribute_group group; u32 attr_count; + u32 hwmon_index; } sensor_groups[] = { {"fan", "ibm,opal-sensor-cooling-fan"}, {"temp", "ibm,opal-sensor-amb-temp"}, @@ -64,7 +70,10 @@ static struct sensor_group { struct sensor_data { u32 id; /* An opaque id of the firmware for each sensor */ + u32 hwmon_index; + u32 opal_index; enum sensors type; + char label[MAX_LABEL_LEN]; char name[MAX_ATTR_LEN]; struct device_attribute dev_attr; }; @@ -87,7 +96,7 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, return ret; /* Convert temperature to milli-degrees */ - if (sdata->type == AMBIENT_TEMP) + if (sdata->type == TEMP) x *= 1000; /* Convert power to micro-watts */ else if (sdata->type == POWER_INPUT) @@ -96,8 +105,65 @@ static ssize_t show_sensor(struct device *dev, struct device_attribute *devattr, return sprintf(buf, "%u\n", x); } -static int get_sensor_index_attr(const char *name, u32 *index, - char *attr) +static ssize_t show_label(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_data *sdata = container_of(devattr, struct sensor_data, + dev_attr); + + return sprintf(buf, "%s\n", sdata->label); +} + +static int __init get_logical_cpu(int hwcpu) +{ + int cpu; + + for_each_possible_cpu(cpu) + if (get_hard_smp_processor_id(cpu) == hwcpu) + return cpu; + + return -ENOENT; +} + +static void __init make_sensor_label(struct device_node *np, + struct sensor_data *sdata, + const char *label) +{ + u32 id; + size_t n; + + n = snprintf(sdata->label, sizeof(sdata->label), "%s", label); + + /* + * Core temp pretty print + */ + if (!of_property_read_u32(np, "ibm,pir", &id)) { + int cpuid = get_logical_cpu(id); + + if (cpuid >= 0) + /* + * The digital thermal sensors are associated + * with a core. Let's print out the range of + * cpu ids corresponding to the hardware + * threads of the core. + */ + n += snprintf(sdata->label + n, + sizeof(sdata->label) - n, " %d-%d", + cpuid, cpuid + threads_per_core - 1); + else + n += snprintf(sdata->label + n, + sizeof(sdata->label) - n, " phy%d", id); + } + + /* + * Membuffer pretty print + */ + if (!of_property_read_u32(np, "ibm,chip-id", &id)) + n += snprintf(sdata->label + n, sizeof(sdata->label) - n, + " %d", id & 0xffff); +} + +static int get_sensor_index_attr(const char *name, u32 *index, char *attr) { char *hash_pos = strchr(name, '#'); char buf[8] = { 0 }; @@ -127,46 +193,90 @@ static int get_sensor_index_attr(const char *name, u32 *index, return 0; } +static const char *convert_opal_attr_name(enum sensors type, + const char *opal_attr) +{ + const char *attr_name = NULL; + + if (!strcmp(opal_attr, DT_FAULT_ATTR_SUFFIX)) { + attr_name = "fault"; + } else if (!strcmp(opal_attr, DT_DATA_ATTR_SUFFIX)) { + attr_name = "input"; + } else if (!strcmp(opal_attr, DT_THRESHOLD_ATTR_SUFFIX)) { + if (type == TEMP) + attr_name = "max"; + else if (type == FAN) + attr_name = "min"; + } + + return attr_name; +} + /* * This function translates the DT node name into the 'hwmon' attribute name. * IBMPOWERNV device node appear like cooling-fan#2-data, amb-temp#1-thrs etc. * which need to be mapped as fan2_input, temp1_max respectively before * populating them inside hwmon device class. */ -static int create_hwmon_attr_name(struct device *dev, enum sensors type, - const char *node_name, - char *hwmon_attr_name) +static const char *parse_opal_node_name(const char *node_name, + enum sensors type, u32 *index) { char attr_suffix[MAX_ATTR_LEN]; - char *attr_name; - u32 index; + const char *attr_name; int err; - err = get_sensor_index_attr(node_name, &index, attr_suffix); - if (err) { - dev_err(dev, "Sensor device node name '%s' is invalid\n", - node_name); - return err; - } + err = get_sensor_index_attr(node_name, index, attr_suffix); + if (err) + return ERR_PTR(err); - if (!strcmp(attr_suffix, DT_FAULT_ATTR_SUFFIX)) { - attr_name = "fault"; - } else if (!strcmp(attr_suffix, DT_DATA_ATTR_SUFFIX)) { - attr_name = "input"; - } else if (!strcmp(attr_suffix, DT_THRESHOLD_ATTR_SUFFIX)) { - if (type == AMBIENT_TEMP) - attr_name = "max"; - else if (type == FAN) - attr_name = "min"; - else - return -ENOENT; - } else { - return -ENOENT; + attr_name = convert_opal_attr_name(type, attr_suffix); + if (!attr_name) + return ERR_PTR(-ENOENT); + + return attr_name; +} + +static int get_sensor_type(struct device_node *np) +{ + enum sensors type; + const char *str; + + for (type = 0; type < MAX_SENSOR_TYPE; type++) { + if (of_device_is_compatible(np, sensor_groups[type].compatible)) + return type; } - snprintf(hwmon_attr_name, MAX_ATTR_LEN, "%s%d_%s", - sensor_groups[type].name, index, attr_name); - return 0; + /* + * Let's check if we have a newer device tree + */ + if (!of_device_is_compatible(np, "ibm,opal-sensor")) + return MAX_SENSOR_TYPE; + + if (of_property_read_string(np, "sensor-type", &str)) + return MAX_SENSOR_TYPE; + + for (type = 0; type < MAX_SENSOR_TYPE; type++) + if (!strcmp(str, sensor_groups[type].name)) + return type; + + return MAX_SENSOR_TYPE; +} + +static u32 get_sensor_hwmon_index(struct sensor_data *sdata, + struct sensor_data *sdata_table, int count) +{ + int i; + + /* + * We don't use the OPAL index on newer device trees + */ + if (sdata->opal_index != INVALID_INDEX) { + for (i = 0; i < count; i++) + if (sdata_table[i].opal_index == sdata->opal_index && + sdata_table[i].type == sdata->type) + return sdata_table[i].hwmon_index; + } + return ++sensor_groups[sdata->type].hwmon_index; } static int populate_attr_groups(struct platform_device *pdev) @@ -178,15 +288,22 @@ static int populate_attr_groups(struct platform_device *pdev) opal = of_find_node_by_path("/ibm,opal/sensors"); for_each_child_of_node(opal, np) { + const char *label; + if (np->name == NULL) continue; - for (type = 0; type < MAX_SENSOR_TYPE; type++) - if (of_device_is_compatible(np, - sensor_groups[type].compatible)) { - sensor_groups[type].attr_count++; - break; - } + type = get_sensor_type(np); + if (type == MAX_SENSOR_TYPE) + continue; + + sensor_groups[type].attr_count++; + + /* + * add a new attribute for labels + */ + if (!of_property_read_string(np, "label", &label)) + sensor_groups[type].attr_count++; } of_node_put(opal); @@ -207,6 +324,21 @@ static int populate_attr_groups(struct platform_device *pdev) return 0; } +static void create_hwmon_attr(struct sensor_data *sdata, const char *attr_name, + ssize_t (*show)(struct device *dev, + struct device_attribute *attr, + char *buf)) +{ + snprintf(sdata->name, MAX_ATTR_LEN, "%s%d_%s", + sensor_groups[sdata->type].name, sdata->hwmon_index, + attr_name); + + sysfs_attr_init(&sdata->dev_attr.attr); + sdata->dev_attr.attr.name = sdata->name; + sdata->dev_attr.attr.mode = S_IRUGO; + sdata->dev_attr.show = show; +} + /* * Iterate through the device tree for each child of 'sensors' node, create * a sysfs attribute file, the file is named by translating the DT node name @@ -233,18 +365,23 @@ static int create_device_attrs(struct platform_device *pdev) } for_each_child_of_node(opal, np) { + const char *attr_name; + u32 opal_index; + const char *label; + if (np->name == NULL) continue; - for (type = 0; type < MAX_SENSOR_TYPE; type++) - if (of_device_is_compatible(np, - sensor_groups[type].compatible)) - break; - + type = get_sensor_type(np); if (type == MAX_SENSOR_TYPE) continue; - if (of_property_read_u32(np, "sensor-id", &sensor_id)) { + /* + * Newer device trees use a "sensor-data" property + * name for input. + */ + if (of_property_read_u32(np, "sensor-id", &sensor_id) && + of_property_read_u32(np, "sensor-data", &sensor_id)) { dev_info(&pdev->dev, "'sensor-id' missing in the node '%s'\n", np->name); @@ -253,18 +390,46 @@ static int create_device_attrs(struct platform_device *pdev) sdata[count].id = sensor_id; sdata[count].type = type; - err = create_hwmon_attr_name(&pdev->dev, type, np->name, - sdata[count].name); - if (err) - goto exit_put_node; - sysfs_attr_init(&sdata[count].dev_attr.attr); - sdata[count].dev_attr.attr.name = sdata[count].name; - sdata[count].dev_attr.attr.mode = S_IRUGO; - sdata[count].dev_attr.show = show_sensor; + /* + * If we can not parse the node name, it means we are + * running on a newer device tree. We can just forget + * about the OPAL index and use a defaut value for the + * hwmon attribute name + */ + attr_name = parse_opal_node_name(np->name, type, &opal_index); + if (IS_ERR(attr_name)) { + attr_name = "input"; + opal_index = INVALID_INDEX; + } + + sdata[count].opal_index = opal_index; + sdata[count].hwmon_index = + get_sensor_hwmon_index(&sdata[count], sdata, count); + + create_hwmon_attr(&sdata[count], attr_name, show_sensor); pgroups[type]->attrs[sensor_groups[type].attr_count++] = &sdata[count++].dev_attr.attr; + + if (!of_property_read_string(np, "label", &label)) { + /* + * For the label attribute, we can reuse the + * "properties" of the previous "input" + * attribute. They are related to the same + * sensor. + */ + sdata[count].type = type; + sdata[count].opal_index = sdata[count - 1].opal_index; + sdata[count].hwmon_index = sdata[count - 1].hwmon_index; + + make_sensor_label(np, &sdata[count], label); + + create_hwmon_attr(&sdata[count], "label", show_label); + + pgroups[type]->attrs[sensor_groups[type].attr_count++] = + &sdata[count++].dev_attr.attr; + } } exit_put_node: diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 409116c52cc5..d0ee556e8ce0 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -11,6 +11,7 @@ * similar parts. The other devices are supported by different drivers. * * Supports: IT8603E Super I/O chip w/LPC interface + * IT8620E Super I/O chip w/LPC interface * IT8623E Super I/O chip w/LPC interface * IT8705F Super I/O chip w/LPC interface * IT8712F Super I/O chip w/LPC interface @@ -23,8 +24,11 @@ * IT8758E Super I/O chip w/LPC interface * IT8771E Super I/O chip w/LPC interface * IT8772E Super I/O chip w/LPC interface + * IT8781F Super I/O chip w/LPC interface * IT8782F Super I/O chip w/LPC interface * IT8783E/F Super I/O chip w/LPC interface + * IT8786E Super I/O chip w/LPC interface + * IT8790E Super I/O chip w/LPC interface * Sis950 A clone of the IT8705F * * Copyright (C) 2001 Chris Gauthron @@ -66,7 +70,7 @@ #define DRVNAME "it87" enum chips { it87, it8712, it8716, it8718, it8720, it8721, it8728, it8771, - it8772, it8782, it8783, it8603 }; + it8772, it8781, it8782, it8783, it8786, it8790, it8603, it8620 }; static unsigned short force_id; module_param(force_id, ushort, 0); @@ -146,15 +150,20 @@ static inline void superio_exit(void) #define IT8728F_DEVID 0x8728 #define IT8771E_DEVID 0x8771 #define IT8772E_DEVID 0x8772 +#define IT8781F_DEVID 0x8781 #define IT8782F_DEVID 0x8782 #define IT8783E_DEVID 0x8783 +#define IT8786E_DEVID 0x8786 +#define IT8790E_DEVID 0x8790 #define IT8603E_DEVID 0x8603 +#define IT8620E_DEVID 0x8620 #define IT8623E_DEVID 0x8623 #define IT87_ACT_REG 0x30 #define IT87_BASE_REG 0x60 /* Logical device 7 registers (IT8712F and later) */ #define IT87_SIO_GPIO1_REG 0x25 +#define IT87_SIO_GPIO2_REG 0x26 #define IT87_SIO_GPIO3_REG 0x27 #define IT87_SIO_GPIO5_REG 0x29 #define IT87_SIO_PINX1_REG 0x2a /* Pin selection */ @@ -207,11 +216,11 @@ static bool fix_pwm_polarity; /* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */ -static const u8 IT87_REG_FAN[] = { 0x0d, 0x0e, 0x0f, 0x80, 0x82 }; -static const u8 IT87_REG_FAN_MIN[] = { 0x10, 0x11, 0x12, 0x84, 0x86 }; -static const u8 IT87_REG_FANX[] = { 0x18, 0x19, 0x1a, 0x81, 0x83 }; -static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 }; -static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 }; +static const u8 IT87_REG_FAN[] = { 0x0d, 0x0e, 0x0f, 0x80, 0x82, 0x4c }; +static const u8 IT87_REG_FAN_MIN[] = { 0x10, 0x11, 0x12, 0x84, 0x86, 0x4e }; +static const u8 IT87_REG_FANX[] = { 0x18, 0x19, 0x1a, 0x81, 0x83, 0x4d }; +static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87, 0x4f }; +static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 }; #define IT87_REG_FAN_MAIN_CTRL 0x13 #define IT87_REG_FAN_CTL 0x14 @@ -238,6 +247,7 @@ static const u8 IT87_REG_TEMP_OFFSET[] = { 0x56, 0x57, 0x59 }; struct it87_devices { const char *name; + const char * const suffix; u16 features; u8 peci_mask; u8 old_peci_mask; @@ -250,79 +260,131 @@ struct it87_devices { #define FEAT_TEMP_OFFSET (1 << 4) #define FEAT_TEMP_PECI (1 << 5) #define FEAT_TEMP_OLD_PECI (1 << 6) +#define FEAT_FAN16_CONFIG (1 << 7) /* Need to enable 16-bit fans */ +#define FEAT_FIVE_FANS (1 << 8) /* Supports five fans */ +#define FEAT_VID (1 << 9) /* Set if chip supports VID */ +#define FEAT_IN7_INTERNAL (1 << 10) /* Set if in7 is internal */ +#define FEAT_SIX_FANS (1 << 11) /* Supports six fans */ static const struct it87_devices it87_devices[] = { [it87] = { .name = "it87", + .suffix = "F", .features = FEAT_OLD_AUTOPWM, /* may need to overwrite */ }, [it8712] = { .name = "it8712", - .features = FEAT_OLD_AUTOPWM, /* may need to overwrite */ + .suffix = "F", + .features = FEAT_OLD_AUTOPWM | FEAT_VID, + /* may need to overwrite */ }, [it8716] = { .name = "it8716", - .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET, + .suffix = "F", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID + | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS, }, [it8718] = { .name = "it8718", - .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET - | FEAT_TEMP_OLD_PECI, + .suffix = "F", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS, .old_peci_mask = 0x4, }, [it8720] = { .name = "it8720", - .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET - | FEAT_TEMP_OLD_PECI, + .suffix = "F", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET | FEAT_VID + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS, .old_peci_mask = 0x4, }, [it8721] = { .name = "it8721", + .suffix = "F", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI, + | FEAT_TEMP_OFFSET | FEAT_TEMP_OLD_PECI | FEAT_TEMP_PECI + | FEAT_FAN16_CONFIG | FEAT_FIVE_FANS | FEAT_IN7_INTERNAL, .peci_mask = 0x05, .old_peci_mask = 0x02, /* Actually reports PCH */ }, [it8728] = { .name = "it8728", + .suffix = "F", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_FIVE_FANS + | FEAT_IN7_INTERNAL, .peci_mask = 0x07, }, [it8771] = { .name = "it8771", + .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, - /* PECI: guesswork */ - /* 12mV ADC (OHM) */ - /* 16 bit fans (OHM) */ + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + /* PECI: guesswork */ + /* 12mV ADC (OHM) */ + /* 16 bit fans (OHM) */ + /* three fans, always 16 bit (guesswork) */ .peci_mask = 0x07, }, [it8772] = { .name = "it8772", + .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, - /* PECI (coreboot) */ - /* 12mV ADC (HWSensors4, OHM) */ - /* 16 bit fans (HWSensors4, OHM) */ + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + /* PECI (coreboot) */ + /* 12mV ADC (HWSensors4, OHM) */ + /* 16 bit fans (HWSensors4, OHM) */ + /* three fans, always 16 bit (datasheet) */ .peci_mask = 0x07, }, + [it8781] = { + .name = "it8781", + .suffix = "F", + .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG, + .old_peci_mask = 0x4, + }, [it8782] = { .name = "it8782", + .suffix = "F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET - | FEAT_TEMP_OLD_PECI, + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG, .old_peci_mask = 0x4, }, [it8783] = { .name = "it8783", + .suffix = "E/F", .features = FEAT_16BIT_FANS | FEAT_TEMP_OFFSET - | FEAT_TEMP_OLD_PECI, + | FEAT_TEMP_OLD_PECI | FEAT_FAN16_CONFIG, .old_peci_mask = 0x4, }, + [it8786] = { + .name = "it8786", + .suffix = "E", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + .peci_mask = 0x07, + }, + [it8790] = { + .name = "it8790", + .suffix = "E", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + .peci_mask = 0x07, + }, [it8603] = { .name = "it8603", + .suffix = "E", .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS - | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI, + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_IN7_INTERNAL, + .peci_mask = 0x07, + }, + [it8620] = { + .name = "it8620", + .suffix = "E", + .features = FEAT_NEWER_AUTOPWM | FEAT_12MV_ADC | FEAT_16BIT_FANS + | FEAT_TEMP_OFFSET | FEAT_TEMP_PECI | FEAT_SIX_FANS + | FEAT_IN7_INTERNAL, .peci_mask = 0x07, }, }; @@ -337,6 +399,12 @@ static const struct it87_devices it87_devices[] = { #define has_temp_old_peci(data, nr) \ (((data)->features & FEAT_TEMP_OLD_PECI) && \ ((data)->old_peci_mask & (1 << nr))) +#define has_fan16_config(data) ((data)->features & FEAT_FAN16_CONFIG) +#define has_five_fans(data) ((data)->features & (FEAT_FIVE_FANS | \ + FEAT_SIX_FANS)) +#define has_vid(data) ((data)->features & FEAT_VID) +#define has_in7_internal(data) ((data)->features & FEAT_IN7_INTERNAL) +#define has_six_fans(data) ((data)->features & FEAT_SIX_FANS) struct it87_sio_data { enum chips type; @@ -373,7 +441,7 @@ struct it87_data { u16 in_scaled; /* Internal voltage sensors are scaled */ u8 in[10][3]; /* [nr][0]=in, [1]=min, [2]=max */ u8 has_fan; /* Bitfield, fans enabled */ - u16 fan[5][2]; /* Register values, [nr][0]=fan, [1]=min */ + u16 fan[6][2]; /* Register values, [nr][0]=fan, [1]=min */ u8 has_temp; /* Bitfield, temp sensors enabled */ s8 temp[3][4]; /* [nr][0]=temp, [1]=min, [2]=max, [3]=offset */ u8 sensor; /* Register value (IT87_REG_TEMP_ENABLE) */ @@ -475,15 +543,25 @@ static int DIV_TO_REG(int val) } #define DIV_FROM_REG(val) (1 << (val)) +/* + * PWM base frequencies. The frequency has to be divided by either 128 or 256, + * depending on the chip type, to calculate the actual PWM frequency. + * + * Some of the chip datasheets suggest a base frequency of 51 kHz instead + * of 750 kHz for the slowest base frequency, resulting in a PWM frequency + * of 200 Hz. Sometimes both PWM frequency select registers are affected, + * sometimes just one. It is unknown if this is a datasheet error or real, + * so this is ignored for now. + */ static const unsigned int pwm_freq[8] = { - 48000000 / 128, - 24000000 / 128, - 12000000 / 128, - 8000000 / 128, - 6000000 / 128, - 3000000 / 128, - 1500000 / 128, - 750000 / 128, + 48000000, + 24000000, + 12000000, + 8000000, + 6000000, + 3000000, + 1500000, + 750000, }; static int it87_probe(struct platform_device *pdev); @@ -801,8 +879,11 @@ static ssize_t show_pwm_freq(struct device *dev, struct device_attribute *attr, { struct it87_data *data = it87_update_device(dev); int index = (data->fan_ctl >> 4) & 0x07; + unsigned int freq; - return sprintf(buf, "%u\n", pwm_freq[index]); + freq = pwm_freq[index] / (has_newer_autopwm(data) ? 256 : 128); + + return sprintf(buf, "%u\n", freq); } static ssize_t set_fan(struct device *dev, struct device_attribute *attr, @@ -1024,6 +1105,9 @@ static ssize_t set_pwm_freq(struct device *dev, if (kstrtoul(buf, 10, &val) < 0) return -EINVAL; + val = clamp_val(val, 0, 1000000); + val *= has_newer_autopwm(data) ? 256 : 128; + /* Search for the nearest available frequency */ for (i = 0; i < 7; i++) { if (val > (pwm_freq[i] + pwm_freq[i+1]) / 2) @@ -1196,6 +1280,10 @@ static SENSOR_DEVICE_ATTR_2(fan5_input, S_IRUGO, show_fan, NULL, 4, 0); static SENSOR_DEVICE_ATTR_2(fan5_min, S_IRUGO | S_IWUSR, show_fan, set_fan, 4, 1); +static SENSOR_DEVICE_ATTR_2(fan6_input, S_IRUGO, show_fan, NULL, 5, 0); +static SENSOR_DEVICE_ATTR_2(fan6_min, S_IRUGO | S_IWUSR, show_fan, set_fan, + 5, 1); + static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, set_pwm_enable, 0); static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm, set_pwm, 0); @@ -1326,6 +1414,7 @@ static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 1); static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 2); static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, 3); static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_alarm, NULL, 7); static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 16); static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 17); static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 18); @@ -1376,6 +1465,7 @@ static SENSOR_DEVICE_ATTR(fan2_beep, S_IRUGO, show_beep, set_beep, 0); static SENSOR_DEVICE_ATTR(fan3_beep, S_IRUGO, show_beep, set_beep, 0); static SENSOR_DEVICE_ATTR(fan4_beep, S_IRUGO, show_beep, set_beep, 0); static SENSOR_DEVICE_ATTR(fan5_beep, S_IRUGO, show_beep, set_beep, 0); +static SENSOR_DEVICE_ATTR(fan6_beep, S_IRUGO, show_beep, set_beep, 0); static SENSOR_DEVICE_ATTR(temp1_beep, S_IRUGO | S_IWUSR, show_beep, set_beep, 2); static SENSOR_DEVICE_ATTR(temp2_beep, S_IRUGO, show_beep, NULL, 2); @@ -1579,7 +1669,7 @@ static struct attribute *it87_attributes_temp_beep[] = { &sensor_dev_attr_temp3_beep.dev_attr.attr, }; -static struct attribute *it87_attributes_fan[5][3+1] = { { +static struct attribute *it87_attributes_fan[6][3+1] = { { &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, &sensor_dev_attr_fan1_alarm.dev_attr.attr, @@ -1604,14 +1694,20 @@ static struct attribute *it87_attributes_fan[5][3+1] = { { &sensor_dev_attr_fan5_min.dev_attr.attr, &sensor_dev_attr_fan5_alarm.dev_attr.attr, NULL +}, { + &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan6_min.dev_attr.attr, + &sensor_dev_attr_fan6_alarm.dev_attr.attr, + NULL } }; -static const struct attribute_group it87_group_fan[5] = { +static const struct attribute_group it87_group_fan[6] = { { .attrs = it87_attributes_fan[0] }, { .attrs = it87_attributes_fan[1] }, { .attrs = it87_attributes_fan[2] }, { .attrs = it87_attributes_fan[3] }, { .attrs = it87_attributes_fan[4] }, + { .attrs = it87_attributes_fan[5] }, }; static const struct attribute *it87_attributes_fan_div[] = { @@ -1693,6 +1789,7 @@ static struct attribute *it87_attributes_fan_beep[] = { &sensor_dev_attr_fan3_beep.dev_attr.attr, &sensor_dev_attr_fan4_beep.dev_attr.attr, &sensor_dev_attr_fan5_beep.dev_attr.attr, + &sensor_dev_attr_fan6_beep.dev_attr.attr, }; static struct attribute *it87_attributes_vid[] = { @@ -1724,6 +1821,7 @@ static int __init it87_find(unsigned short *address, int err; u16 chip_type; const char *board_vendor, *board_name; + const struct it87_devices *config; err = superio_enter(); if (err) @@ -1761,16 +1859,28 @@ static int __init it87_find(unsigned short *address, case IT8772E_DEVID: sio_data->type = it8772; break; + case IT8781F_DEVID: + sio_data->type = it8781; + break; case IT8782F_DEVID: sio_data->type = it8782; break; case IT8783E_DEVID: sio_data->type = it8783; break; + case IT8786E_DEVID: + sio_data->type = it8786; + break; + case IT8790E_DEVID: + sio_data->type = it8790; + break; case IT8603E_DEVID: case IT8623E_DEVID: sio_data->type = it8603; break; + case IT8620E_DEVID: + sio_data->type = it8620; + break; case 0xffff: /* No device at all */ goto exit; default: @@ -1792,30 +1902,34 @@ static int __init it87_find(unsigned short *address, err = 0; sio_data->revision = superio_inb(DEVREV) & 0x0f; - pr_info("Found IT%04x%c chip at 0x%x, revision %d\n", chip_type, - chip_type == 0x8771 || chip_type == 0x8772 || - chip_type == 0x8603 ? 'E' : 'F', *address, - sio_data->revision); + pr_info("Found IT%04x%s chip at 0x%x, revision %d\n", chip_type, + it87_devices[sio_data->type].suffix, + *address, sio_data->revision); + + config = &it87_devices[sio_data->type]; + + /* in7 (VSB or VCCH5V) is always internal on some chips */ + if (has_in7_internal(config)) + sio_data->internal |= (1 << 1); /* in8 (Vbat) is always internal */ - sio_data->internal = (1 << 2); + sio_data->internal |= (1 << 2); + /* Only the IT8603E has in9 */ if (sio_data->type != it8603) sio_data->skip_in |= (1 << 9); - /* Read GPIO config and VID value from LDN 7 (GPIO) */ - if (sio_data->type == it87) { - /* The IT8705F doesn't have VID pins at all */ + if (!has_vid(config)) sio_data->skip_vid = 1; + /* Read GPIO config and VID value from LDN 7 (GPIO) */ + if (sio_data->type == it87) { /* The IT8705F has a different LD number for GPIO */ superio_select(5); sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } else if (sio_data->type == it8783) { int reg25, reg27, reg2a, reg2c, regef; - sio_data->skip_vid = 1; /* No VID */ - superio_select(GPIO); reg25 = superio_inb(IT87_SIO_GPIO1_REG); @@ -1881,7 +1995,6 @@ static int __init it87_find(unsigned short *address, } else if (sio_data->type == it8603) { int reg27, reg29; - sio_data->skip_vid = 1; /* No VID */ superio_select(GPIO); reg27 = superio_inb(IT87_SIO_GPIO3_REG); @@ -1902,14 +2015,36 @@ static int __init it87_find(unsigned short *address, sio_data->skip_in |= (1 << 5); /* No VIN5 */ sio_data->skip_in |= (1 << 6); /* No VIN6 */ - /* no fan4 */ - sio_data->skip_pwm |= (1 << 3); - sio_data->skip_fan |= (1 << 3); - - sio_data->internal |= (1 << 1); /* in7 is VSB */ sio_data->internal |= (1 << 3); /* in9 is AVCC */ sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; + } else if (sio_data->type == it8620) { + int reg; + + superio_select(GPIO); + + /* Check for fan4, fan5 */ + reg = superio_inb(IT87_SIO_GPIO2_REG); + if (!(reg & (1 << 5))) + sio_data->skip_fan |= (1 << 3); + if (!(reg & (1 << 4))) + sio_data->skip_fan |= (1 << 4); + + /* Check for pwm3, fan3 */ + reg = superio_inb(IT87_SIO_GPIO3_REG); + if (reg & (1 << 6)) + sio_data->skip_pwm |= (1 << 2); + if (reg & (1 << 7)) + sio_data->skip_fan |= (1 << 2); + + /* Check for pwm2, fan2 */ + reg = superio_inb(IT87_SIO_GPIO5_REG); + if (reg & (1 << 1)) + sio_data->skip_pwm |= (1 << 1); + if (reg & (1 << 2)) + sio_data->skip_fan |= (1 << 1); + + sio_data->beep_pin = superio_inb(IT87_SIO_BEEP_PIN_REG) & 0x3f; } else { int reg; bool uart6; @@ -1917,15 +2052,7 @@ static int __init it87_find(unsigned short *address, superio_select(GPIO); reg = superio_inb(IT87_SIO_GPIO3_REG); - if (sio_data->type == it8721 || sio_data->type == it8728 || - sio_data->type == it8771 || sio_data->type == it8772 || - sio_data->type == it8782) { - /* - * IT8721F/IT8758E, and IT8782F don't have VID pins - * at all, not sure about the IT8728F and compatibles. - */ - sio_data->skip_vid = 1; - } else { + if (!sio_data->skip_vid) { /* We need at least 4 VID pins */ if (reg & 0x0f) { pr_info("VID is disabled (pins used for GPIO)\n"); @@ -1975,10 +2102,7 @@ static int __init it87_find(unsigned short *address, } if (reg & (1 << 0)) sio_data->internal |= (1 << 0); - if ((reg & (1 << 1)) || sio_data->type == it8721 || - sio_data->type == it8728 || - sio_data->type == it8771 || - sio_data->type == it8772) + if (reg & (1 << 1)) sio_data->internal |= (1 << 1); /* @@ -2050,7 +2174,7 @@ static void it87_remove_files(struct device *dev) sysfs_remove_file(&dev->kobj, it87_attributes_temp_beep[i]); } - for (i = 0; i < 5; i++) { + for (i = 0; i < 6; i++) { if (!(data->has_fan & (1 << i))) continue; sysfs_remove_group(&dev->kobj, &it87_group_fan[i]); @@ -2062,7 +2186,7 @@ static void it87_remove_files(struct device *dev) it87_attributes_fan_div[i]); } for (i = 0; i < 3; i++) { - if (sio_data->skip_pwm & (1 << 0)) + if (sio_data->skip_pwm & (1 << i)) continue; sysfs_remove_group(&dev->kobj, &it87_group_pwm[i]); if (has_old_autopwm(data)) @@ -2112,13 +2236,14 @@ static int it87_probe(struct platform_device *pdev) case it87: if (sio_data->revision >= 0x03) { data->features &= ~FEAT_OLD_AUTOPWM; - data->features |= FEAT_16BIT_FANS; + data->features |= FEAT_FAN16_CONFIG | FEAT_16BIT_FANS; } break; case it8712: if (sio_data->revision >= 0x08) { data->features &= ~FEAT_OLD_AUTOPWM; - data->features |= FEAT_16BIT_FANS; + data->features |= FEAT_FAN16_CONFIG | FEAT_16BIT_FANS | + FEAT_FIVE_FANS; } break; default: @@ -2147,7 +2272,8 @@ static int it87_probe(struct platform_device *pdev) data->in_scaled |= (1 << 8); /* in8 is Vbat */ if (sio_data->internal & (1 << 3)) data->in_scaled |= (1 << 9); /* in9 is AVCC */ - } else if (sio_data->type == it8782 || sio_data->type == it8783) { + } else if (sio_data->type == it8781 || sio_data->type == it8782 || + sio_data->type == it8783) { if (sio_data->internal & (1 << 0)) data->in_scaled |= (1 << 3); /* in3 is VCC5V */ if (sio_data->internal & (1 << 1)) @@ -2205,7 +2331,7 @@ static int it87_probe(struct platform_device *pdev) /* Do not create fan files for disabled fans */ fan_beep_need_rw = 1; - for (i = 0; i < 5; i++) { + for (i = 0; i < 6; i++) { if (!(data->has_fan & (1 << i))) continue; err = sysfs_create_group(&dev->kobj, &it87_group_fan[i]); @@ -2450,24 +2576,26 @@ static void it87_init_device(struct platform_device *pdev) } data->has_fan = (data->fan_main_ctrl >> 4) & 0x07; - /* Set tachometers to 16-bit mode if needed, IT8603E (and IT8728F?) - * has it by default */ - if (has_16bit_fans(data) && data->type != it8603) { - tmp = it87_read_value(data, IT87_REG_FAN_16BIT); + tmp = it87_read_value(data, IT87_REG_FAN_16BIT); + + /* Set tachometers to 16-bit mode if needed */ + if (has_fan16_config(data)) { if (~tmp & 0x07 & data->has_fan) { dev_dbg(&pdev->dev, "Setting fan1-3 to 16-bit mode\n"); it87_write_value(data, IT87_REG_FAN_16BIT, tmp | 0x07); } - /* IT8705F, IT8782F, and IT8783E/F only support three fans. */ - if (data->type != it87 && data->type != it8782 && - data->type != it8783) { - if (tmp & (1 << 4)) - data->has_fan |= (1 << 3); /* fan4 enabled */ - if (tmp & (1 << 5)) - data->has_fan |= (1 << 4); /* fan5 enabled */ - } + } + + /* Check for additional fans */ + if (has_five_fans(data)) { + if (tmp & (1 << 4)) + data->has_fan |= (1 << 3); /* fan4 enabled */ + if (tmp & (1 << 5)) + data->has_fan |= (1 << 4); /* fan5 enabled */ + if (has_six_fans(data) && (tmp & (1 << 2))) + data->has_fan |= (1 << 5); /* fan6 enabled */ } /* Fan input pins may be used for alternative functions */ @@ -2535,7 +2663,7 @@ static struct it87_data *it87_update_device(struct device *dev) if (data->type == it8603) data->in[9][0] = it87_read_value(data, 0x2f); - for (i = 0; i < 5; i++) { + for (i = 0; i < 6; i++) { /* Skip disabled fans */ if (!(data->has_fan & (1 << i))) continue; diff --git a/drivers/hwmon/jc42.c b/drivers/hwmon/jc42.c index 996bdfd5cf25..9887d3224a86 100644 --- a/drivers/hwmon/jc42.c +++ b/drivers/hwmon/jc42.c @@ -87,11 +87,14 @@ static const unsigned short normal_i2c[] = { #define AT30TSE004_DEVID_MASK 0xffff /* IDT */ -#define TS3000B3_DEVID 0x2903 /* Also matches TSE2002B3 */ -#define TS3000B3_DEVID_MASK 0xffff +#define TSE2004_DEVID 0x2200 +#define TSE2004_DEVID_MASK 0xff00 -#define TS3000GB2_DEVID 0x2912 /* Also matches TSE2002GB2 */ -#define TS3000GB2_DEVID_MASK 0xffff +#define TS3000_DEVID 0x2900 /* Also matches TSE2002 */ +#define TS3000_DEVID_MASK 0xff00 + +#define TS3001_DEVID 0x3000 +#define TS3001_DEVID_MASK 0xff00 /* Maxim */ #define MAX6604_DEVID 0x3e00 @@ -152,8 +155,9 @@ static struct jc42_chips jc42_chips[] = { { ADT_MANID, ADT7408_DEVID, ADT7408_DEVID_MASK }, { ATMEL_MANID, AT30TS00_DEVID, AT30TS00_DEVID_MASK }, { ATMEL_MANID2, AT30TSE004_DEVID, AT30TSE004_DEVID_MASK }, - { IDT_MANID, TS3000B3_DEVID, TS3000B3_DEVID_MASK }, - { IDT_MANID, TS3000GB2_DEVID, TS3000GB2_DEVID_MASK }, + { IDT_MANID, TSE2004_DEVID, TSE2004_DEVID_MASK }, + { IDT_MANID, TS3000_DEVID, TS3000_DEVID_MASK }, + { IDT_MANID, TS3001_DEVID, TS3001_DEVID_MASK }, { MAX_MANID, MAX6604_DEVID, MAX6604_DEVID_MASK }, { MCP_MANID, MCP9804_DEVID, MCP9804_DEVID_MASK }, { MCP_MANID, MCP98242_DEVID, MCP98242_DEVID_MASK }, diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index 1be41177b620..4fcb48103299 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -57,6 +57,7 @@ #include <linux/err.h> #include <linux/mutex.h> #include <linux/acpi.h> +#include <linux/dmi.h> #include <linux/io.h> #include "lm75.h" @@ -880,12 +881,12 @@ struct nct6775_data { u16 have_temp; u16 have_temp_fixed; u16 have_in; -#ifdef CONFIG_PM + /* Remember extra register values over suspend/resume */ u8 vbat; u8 fandiv1; u8 fandiv2; -#endif + u8 sio_reg_enable; }; struct nct6775_sio_data { @@ -3178,6 +3179,10 @@ nct6775_check_fan_inputs(struct nct6775_data *data) int sioreg = data->sioreg; int regval; + /* Store SIO_REG_ENABLE for use during resume */ + superio_select(sioreg, NCT6775_LD_HWM); + data->sio_reg_enable = superio_inb(sioreg, SIO_REG_ENABLE); + /* fan4 and fan5 share some pins with the GPIO and serial flash */ if (data->kind == nct6775) { regval = superio_inb(sioreg, 0x2c); @@ -3195,21 +3200,38 @@ nct6775_check_fan_inputs(struct nct6775_data *data) pwm6pin = false; } else if (data->kind == nct6776) { bool gpok = superio_inb(sioreg, 0x27) & 0x80; + const char *board_vendor, *board_name; - superio_select(sioreg, NCT6775_LD_HWM); - regval = superio_inb(sioreg, SIO_REG_ENABLE); + board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); + board_name = dmi_get_system_info(DMI_BOARD_NAME); + + if (board_name && board_vendor && + !strcmp(board_vendor, "ASRock")) { + /* + * Auxiliary fan monitoring is not enabled on ASRock + * Z77 Pro4-M if booted in UEFI Ultra-FastBoot mode. + * Observed with BIOS version 2.00. + */ + if (!strcmp(board_name, "Z77 Pro4-M")) { + if ((data->sio_reg_enable & 0xe0) != 0xe0) { + data->sio_reg_enable |= 0xe0; + superio_outb(sioreg, SIO_REG_ENABLE, + data->sio_reg_enable); + } + } + } - if (regval & 0x80) + if (data->sio_reg_enable & 0x80) fan3pin = gpok; else fan3pin = !(superio_inb(sioreg, 0x24) & 0x40); - if (regval & 0x40) + if (data->sio_reg_enable & 0x40) fan4pin = gpok; else fan4pin = superio_inb(sioreg, 0x1C) & 0x01; - if (regval & 0x20) + if (data->sio_reg_enable & 0x20) fan5pin = gpok; else fan5pin = superio_inb(sioreg, 0x1C) & 0x02; @@ -3989,8 +4011,7 @@ static void nct6791_enable_io_mapping(int sioaddr) } } -#ifdef CONFIG_PM -static int nct6775_suspend(struct device *dev) +static int __maybe_unused nct6775_suspend(struct device *dev) { struct nct6775_data *data = nct6775_update_device(dev); @@ -4005,22 +4026,29 @@ static int nct6775_suspend(struct device *dev) return 0; } -static int nct6775_resume(struct device *dev) +static int __maybe_unused nct6775_resume(struct device *dev) { struct nct6775_data *data = dev_get_drvdata(dev); + int sioreg = data->sioreg; int i, j, err = 0; + u8 reg; mutex_lock(&data->update_lock); data->bank = 0xff; /* Force initial bank selection */ - if (data->kind == nct6791 || data->kind == nct6792) { - err = superio_enter(data->sioreg); - if (err) - goto abort; + err = superio_enter(sioreg); + if (err) + goto abort; - nct6791_enable_io_mapping(data->sioreg); - superio_exit(data->sioreg); - } + superio_select(sioreg, NCT6775_LD_HWM); + reg = superio_inb(sioreg, SIO_REG_ENABLE); + if (reg != data->sio_reg_enable) + superio_outb(sioreg, SIO_REG_ENABLE, data->sio_reg_enable); + + if (data->kind == nct6791 || data->kind == nct6792) + nct6791_enable_io_mapping(sioreg); + + superio_exit(sioreg); /* Restore limits */ for (i = 0; i < data->in_num; i++) { @@ -4066,22 +4094,12 @@ abort: return err; } -static const struct dev_pm_ops nct6775_dev_pm_ops = { - .suspend = nct6775_suspend, - .resume = nct6775_resume, - .freeze = nct6775_suspend, - .restore = nct6775_resume, -}; - -#define NCT6775_DEV_PM_OPS (&nct6775_dev_pm_ops) -#else -#define NCT6775_DEV_PM_OPS NULL -#endif /* CONFIG_PM */ +static SIMPLE_DEV_PM_OPS(nct6775_dev_pm_ops, nct6775_suspend, nct6775_resume); static struct platform_driver nct6775_driver = { .driver = { .name = DRVNAME, - .pm = NCT6775_DEV_PM_OPS, + .pm = &nct6775_dev_pm_ops, }, .probe = nct6775_probe, }; diff --git a/drivers/hwmon/nct7904.c b/drivers/hwmon/nct7904.c new file mode 100644 index 000000000000..b77b82f24480 --- /dev/null +++ b/drivers/hwmon/nct7904.c @@ -0,0 +1,593 @@ +/* + * nct7904.c - driver for Nuvoton NCT7904D. + * + * Copyright (c) 2015 Kontron + * Author: Vadim V. Vlasov <vvlasov@dev.rtsoft.ru> + * + * 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. + */ + +#include <linux/module.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> + +#define VENDOR_ID_REG 0x7A /* Any bank */ +#define NUVOTON_ID 0x50 +#define CHIP_ID_REG 0x7B /* Any bank */ +#define NCT7904_ID 0xC5 +#define DEVICE_ID_REG 0x7C /* Any bank */ + +#define BANK_SEL_REG 0xFF +#define BANK_0 0x00 +#define BANK_1 0x01 +#define BANK_2 0x02 +#define BANK_3 0x03 +#define BANK_4 0x04 +#define BANK_MAX 0x04 + +#define FANIN_MAX 12 /* Counted from 1 */ +#define VSEN_MAX 21 /* VSEN1..14, 3VDD, VBAT, V3VSB, + LTD (not a voltage), VSEN17..19 */ +#define FANCTL_MAX 4 /* Counted from 1 */ +#define TCPU_MAX 8 /* Counted from 1 */ +#define TEMP_MAX 4 /* Counted from 1 */ + +#define VT_ADC_CTRL0_REG 0x20 /* Bank 0 */ +#define VT_ADC_CTRL1_REG 0x21 /* Bank 0 */ +#define VT_ADC_CTRL2_REG 0x22 /* Bank 0 */ +#define FANIN_CTRL0_REG 0x24 +#define FANIN_CTRL1_REG 0x25 +#define DTS_T_CTRL0_REG 0x26 +#define DTS_T_CTRL1_REG 0x27 +#define VT_ADC_MD_REG 0x2E + +#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 FANIN1_HV_REG 0x80 /* Bank 0; 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 FANCTL1_FMR_REG 0x00 /* Bank 3; 1 reg per channel */ +#define FANCTL1_OUT_REG 0x10 /* Bank 3; 1 reg per channel */ + +static const unsigned short normal_i2c[] = { + 0x2d, 0x2e, I2C_CLIENT_END +}; + +struct nct7904_data { + struct i2c_client *client; + struct mutex bank_lock; + int bank_sel; + u32 fanin_mask; + u32 vsen_mask; + u32 tcpu_mask; + u8 fan_mode[FANCTL_MAX]; +}; + +/* Access functions */ +static int nct7904_bank_lock(struct nct7904_data *data, unsigned bank) +{ + int ret; + + mutex_lock(&data->bank_lock); + if (data->bank_sel == bank) + return 0; + ret = i2c_smbus_write_byte_data(data->client, BANK_SEL_REG, bank); + if (ret == 0) + data->bank_sel = bank; + else + data->bank_sel = -1; + return ret; +} + +static inline void nct7904_bank_release(struct nct7904_data *data) +{ + mutex_unlock(&data->bank_lock); +} + +/* Read 1-byte register. Returns unsigned reg or -ERRNO on error. */ +static int nct7904_read_reg(struct nct7904_data *data, + unsigned bank, unsigned reg) +{ + struct i2c_client *client = data->client; + int ret; + + ret = nct7904_bank_lock(data, bank); + if (ret == 0) + ret = i2c_smbus_read_byte_data(client, reg); + + nct7904_bank_release(data); + return ret; +} + +/* + * Read 2-byte register. Returns register in big-endian format or + * -ERRNO on error. + */ +static int nct7904_read_reg16(struct nct7904_data *data, + unsigned bank, unsigned reg) +{ + struct i2c_client *client = data->client; + int ret, hi; + + ret = nct7904_bank_lock(data, bank); + if (ret == 0) { + ret = i2c_smbus_read_byte_data(client, reg); + if (ret >= 0) { + hi = ret; + ret = i2c_smbus_read_byte_data(client, reg + 1); + if (ret >= 0) + ret |= hi << 8; + } + } + + nct7904_bank_release(data); + return ret; +} + +/* Write 1-byte register. Returns 0 or -ERRNO on error. */ +static int nct7904_write_reg(struct nct7904_data *data, + unsigned bank, unsigned reg, u8 val) +{ + struct i2c_client *client = data->client; + int ret; + + ret = nct7904_bank_lock(data, bank); + if (ret == 0) + ret = i2c_smbus_write_byte_data(client, reg, val); + + nct7904_bank_release(data); + return ret; +} + +/* FANIN ATTR */ +static ssize_t show_fan(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + int ret; + unsigned cnt, rpm; + + ret = nct7904_read_reg16(data, BANK_0, FANIN1_HV_REG + index * 2); + if (ret < 0) + return ret; + cnt = ((ret & 0xff00) >> 3) | (ret & 0x1f); + if (cnt == 0x1fff) + rpm = 0; + else + rpm = 1350000 / cnt; + return sprintf(buf, "%u\n", rpm); +} + +static umode_t nct7904_fanin_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7904_data *data = dev_get_drvdata(dev); + + if (data->fanin_mask & (1 << n)) + return a->mode; + return 0; +} + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan, NULL, 7); +static SENSOR_DEVICE_ATTR(fan9_input, S_IRUGO, show_fan, NULL, 8); +static SENSOR_DEVICE_ATTR(fan10_input, S_IRUGO, show_fan, NULL, 9); +static SENSOR_DEVICE_ATTR(fan11_input, S_IRUGO, show_fan, NULL, 10); +static SENSOR_DEVICE_ATTR(fan12_input, S_IRUGO, show_fan, NULL, 11); + +static struct attribute *nct7904_fanin_attrs[] = { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan8_input.dev_attr.attr, + &sensor_dev_attr_fan9_input.dev_attr.attr, + &sensor_dev_attr_fan10_input.dev_attr.attr, + &sensor_dev_attr_fan11_input.dev_attr.attr, + &sensor_dev_attr_fan12_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group nct7904_fanin_group = { + .attrs = nct7904_fanin_attrs, + .is_visible = nct7904_fanin_is_visible, +}; + +/* VSEN ATTR */ +static ssize_t show_voltage(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + int ret; + int volt; + + ret = nct7904_read_reg16(data, BANK_0, VSEN1_HV_REG + index * 2); + 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 */ + + return sprintf(buf, "%d\n", volt); +} + +static ssize_t show_ltemp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct nct7904_data *data = dev_get_drvdata(dev); + int ret; + int temp; + + ret = nct7904_read_reg16(data, BANK_0, LTD_HV_REG); + if (ret < 0) + return ret; + temp = ((ret & 0xff00) >> 5) | (ret & 0x7); + temp = sign_extend32(temp, 10) * 125; + + return sprintf(buf, "%d\n", temp); +} + +static umode_t nct7904_vsen_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7904_data *data = dev_get_drvdata(dev); + + if (data->vsen_mask & (1 << n)) + return a->mode; + return 0; +} + +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_voltage, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_voltage, NULL, 3); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 4); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 5); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 6); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_voltage, NULL, 7); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_voltage, NULL, 8); +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_voltage, NULL, 9); +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_voltage, NULL, 10); +static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, show_voltage, NULL, 11); +static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, show_voltage, NULL, 12); +static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, show_voltage, NULL, 13); +/* + * Next 3 voltage sensors have specific names in the Nuvoton doc + * (3VDD, VBAT, 3VSB) but we use vacant numbers for them. + */ +static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, show_voltage, NULL, 14); +static SENSOR_DEVICE_ATTR(in16_input, S_IRUGO, show_voltage, NULL, 15); +static SENSOR_DEVICE_ATTR(in20_input, S_IRUGO, show_voltage, NULL, 16); +/* This is not a voltage, but a local temperature sensor. */ +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_ltemp, NULL, 0); +static SENSOR_DEVICE_ATTR(in17_input, S_IRUGO, show_voltage, NULL, 18); +static SENSOR_DEVICE_ATTR(in18_input, S_IRUGO, show_voltage, NULL, 19); +static SENSOR_DEVICE_ATTR(in19_input, S_IRUGO, show_voltage, NULL, 20); + +static struct attribute *nct7904_vsen_attrs[] = { + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + &sensor_dev_attr_in12_input.dev_attr.attr, + &sensor_dev_attr_in13_input.dev_attr.attr, + &sensor_dev_attr_in14_input.dev_attr.attr, + &sensor_dev_attr_in15_input.dev_attr.attr, + &sensor_dev_attr_in16_input.dev_attr.attr, + &sensor_dev_attr_in20_input.dev_attr.attr, + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_in17_input.dev_attr.attr, + &sensor_dev_attr_in18_input.dev_attr.attr, + &sensor_dev_attr_in19_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group nct7904_vsen_group = { + .attrs = nct7904_vsen_attrs, + .is_visible = nct7904_vsen_is_visible, +}; + +/* CPU_TEMP ATTR */ +static ssize_t show_tcpu(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + int ret; + int temp; + + ret = nct7904_read_reg16(data, BANK_0, T_CPU1_HV_REG + index * 2); + if (ret < 0) + return ret; + + temp = ((ret & 0xff00) >> 5) | (ret & 0x7); + temp = sign_extend32(temp, 10) * 125; + return sprintf(buf, "%d\n", temp); +} + +static umode_t nct7904_tcpu_is_visible(struct kobject *kobj, + struct attribute *a, int n) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nct7904_data *data = dev_get_drvdata(dev); + + if (data->tcpu_mask & (1 << n)) + return a->mode; + return 0; +} + +/* "temp1_input" reserved for local temp */ +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_tcpu, NULL, 0); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_tcpu, NULL, 1); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_tcpu, NULL, 2); +static SENSOR_DEVICE_ATTR(temp5_input, S_IRUGO, show_tcpu, NULL, 3); +static SENSOR_DEVICE_ATTR(temp6_input, S_IRUGO, show_tcpu, NULL, 4); +static SENSOR_DEVICE_ATTR(temp7_input, S_IRUGO, show_tcpu, NULL, 5); +static SENSOR_DEVICE_ATTR(temp8_input, S_IRUGO, show_tcpu, NULL, 6); +static SENSOR_DEVICE_ATTR(temp9_input, S_IRUGO, show_tcpu, NULL, 7); + +static struct attribute *nct7904_tcpu_attrs[] = { + &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_temp6_input.dev_attr.attr, + &sensor_dev_attr_temp7_input.dev_attr.attr, + &sensor_dev_attr_temp8_input.dev_attr.attr, + &sensor_dev_attr_temp9_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group nct7904_tcpu_group = { + .attrs = nct7904_tcpu_attrs, + .is_visible = nct7904_tcpu_is_visible, +}; + +/* PWM ATTR */ +static ssize_t store_pwm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val > 255) + return -EINVAL; + + ret = nct7904_write_reg(data, BANK_3, FANCTL1_OUT_REG + index, val); + + return ret ? ret : count; +} + +static ssize_t show_pwm(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + int val; + + val = nct7904_read_reg(data, BANK_3, FANCTL1_OUT_REG + index); + if (val < 0) + return val; + + return sprintf(buf, "%d\n", val); +} + +static ssize_t store_mode(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + unsigned long val; + int ret; + + if (kstrtoul(buf, 10, &val) < 0) + return -EINVAL; + if (val > 1 || (val && !data->fan_mode[index])) + return -EINVAL; + + ret = nct7904_write_reg(data, BANK_3, FANCTL1_FMR_REG + index, + val ? data->fan_mode[index] : 0); + + return ret ? ret : count; +} + +/* Return 0 for manual mode or 1 for SmartFan mode */ +static ssize_t show_mode(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + int index = to_sensor_dev_attr(devattr)->index; + struct nct7904_data *data = dev_get_drvdata(dev); + int val; + + val = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + index); + if (val < 0) + return val; + + return sprintf(buf, "%d\n", val ? 1 : 0); +} + +/* 2 attributes per channel: pwm and mode */ +static SENSOR_DEVICE_ATTR(fan1_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 0); +static SENSOR_DEVICE_ATTR(fan1_mode, S_IRUGO | S_IWUSR, + show_mode, store_mode, 0); +static SENSOR_DEVICE_ATTR(fan2_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 1); +static SENSOR_DEVICE_ATTR(fan2_mode, S_IRUGO | S_IWUSR, + show_mode, store_mode, 1); +static SENSOR_DEVICE_ATTR(fan3_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 2); +static SENSOR_DEVICE_ATTR(fan3_mode, S_IRUGO | S_IWUSR, + show_mode, store_mode, 2); +static SENSOR_DEVICE_ATTR(fan4_pwm, S_IRUGO | S_IWUSR, + show_pwm, store_pwm, 3); +static SENSOR_DEVICE_ATTR(fan4_mode, S_IRUGO | S_IWUSR, + show_mode, store_mode, 3); + +static struct attribute *nct7904_fanctl_attrs[] = { + &sensor_dev_attr_fan1_pwm.dev_attr.attr, + &sensor_dev_attr_fan1_mode.dev_attr.attr, + &sensor_dev_attr_fan2_pwm.dev_attr.attr, + &sensor_dev_attr_fan2_mode.dev_attr.attr, + &sensor_dev_attr_fan3_pwm.dev_attr.attr, + &sensor_dev_attr_fan3_mode.dev_attr.attr, + &sensor_dev_attr_fan4_pwm.dev_attr.attr, + &sensor_dev_attr_fan4_mode.dev_attr.attr, + NULL +}; + +static const struct attribute_group nct7904_fanctl_group = { + .attrs = nct7904_fanctl_attrs, +}; + +static const struct attribute_group *nct7904_groups[] = { + &nct7904_fanin_group, + &nct7904_vsen_group, + &nct7904_tcpu_group, + &nct7904_fanctl_group, + NULL +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int nct7904_detect(struct i2c_client *client, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + return -ENODEV; + + /* Determine the chip type. */ + if (i2c_smbus_read_byte_data(client, VENDOR_ID_REG) != NUVOTON_ID || + i2c_smbus_read_byte_data(client, CHIP_ID_REG) != NCT7904_ID || + (i2c_smbus_read_byte_data(client, DEVICE_ID_REG) & 0xf0) != 0x50 || + (i2c_smbus_read_byte_data(client, BANK_SEL_REG) & 0xf8) != 0x00) + return -ENODEV; + + strlcpy(info->type, "nct7904", I2C_NAME_SIZE); + + return 0; +} + +static int nct7904_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct nct7904_data *data; + struct device *hwmon_dev; + struct device *dev = &client->dev; + int ret, i; + u32 mask; + + data = devm_kzalloc(dev, sizeof(struct nct7904_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->client = client; + mutex_init(&data->bank_lock); + data->bank_sel = -1; + + /* Setup sensor groups. */ + /* FANIN attributes */ + ret = nct7904_read_reg16(data, BANK_0, FANIN_CTRL0_REG); + if (ret < 0) + return ret; + data->fanin_mask = (ret >> 8) | ((ret & 0xff) << 8); + + /* + * VSEN attributes + * + * Note: voltage sensors overlap with external temperature + * sensors. So, if we ever decide to support the latter + * we will have to adjust 'vsen_mask' accordingly. + */ + mask = 0; + ret = nct7904_read_reg16(data, BANK_0, VT_ADC_CTRL0_REG); + if (ret >= 0) + mask = (ret >> 8) | ((ret & 0xff) << 8); + ret = nct7904_read_reg(data, BANK_0, VT_ADC_CTRL2_REG); + if (ret >= 0) + mask |= (ret << 16); + data->vsen_mask = mask; + + /* CPU_TEMP attributes */ + ret = nct7904_read_reg16(data, BANK_0, DTS_T_CTRL0_REG); + if (ret < 0) + return ret; + data->tcpu_mask = ((ret >> 8) & 0xf) | ((ret & 0xf) << 4); + + for (i = 0; i < FANCTL_MAX; i++) { + ret = nct7904_read_reg(data, BANK_3, FANCTL1_FMR_REG + i); + if (ret < 0) + return ret; + data->fan_mode[i] = ret; + } + + hwmon_dev = + devm_hwmon_device_register_with_groups(dev, client->name, data, + nct7904_groups); + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +static const struct i2c_device_id nct7904_id[] = { + {"nct7904", 0}, + {} +}; + +static struct i2c_driver nct7904_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "nct7904", + }, + .probe = nct7904_probe, + .id_table = nct7904_id, + .detect = nct7904_detect, + .address_list = normal_i2c, +}; + +module_i2c_driver(nct7904_driver); + +MODULE_AUTHOR("Vadim V. Vlasov <vvlasov@dev.rtsoft.ru>"); +MODULE_DESCRIPTION("Hwmon driver for NUVOTON NCT7904"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/pwm-fan.c b/drivers/hwmon/pwm-fan.c index 1991d9032c38..2d9a712699ff 100644 --- a/drivers/hwmon/pwm-fan.c +++ b/drivers/hwmon/pwm-fan.c @@ -24,55 +24,78 @@ #include <linux/platform_device.h> #include <linux/pwm.h> #include <linux/sysfs.h> +#include <linux/thermal.h> #define MAX_PWM 255 struct pwm_fan_ctx { struct mutex lock; struct pwm_device *pwm; - unsigned char pwm_value; + unsigned int pwm_value; + unsigned int pwm_fan_state; + unsigned int pwm_fan_max_state; + unsigned int *pwm_fan_cooling_levels; + struct thermal_cooling_device *cdev; }; -static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static int __set_pwm(struct pwm_fan_ctx *ctx, unsigned long pwm) { - struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); - unsigned long pwm, duty; - ssize_t ret; - - if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM) - return -EINVAL; + unsigned long duty; + int ret = 0; mutex_lock(&ctx->lock); - if (ctx->pwm_value == pwm) - goto exit_set_pwm_no_change; - - if (pwm == 0) { - pwm_disable(ctx->pwm); - goto exit_set_pwm; - } + goto exit_set_pwm_err; duty = DIV_ROUND_UP(pwm * (ctx->pwm->period - 1), MAX_PWM); ret = pwm_config(ctx->pwm, duty, ctx->pwm->period); if (ret) goto exit_set_pwm_err; + if (pwm == 0) + pwm_disable(ctx->pwm); + if (ctx->pwm_value == 0) { ret = pwm_enable(ctx->pwm); if (ret) goto exit_set_pwm_err; } -exit_set_pwm: ctx->pwm_value = pwm; -exit_set_pwm_no_change: - ret = count; exit_set_pwm_err: mutex_unlock(&ctx->lock); return ret; } +static void pwm_fan_update_state(struct pwm_fan_ctx *ctx, unsigned long pwm) +{ + int i; + + for (i = 0; i < ctx->pwm_fan_max_state; ++i) + if (pwm < ctx->pwm_fan_cooling_levels[i + 1]) + break; + + ctx->pwm_fan_state = i; +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pwm_fan_ctx *ctx = dev_get_drvdata(dev); + unsigned long pwm; + int ret; + + if (kstrtoul(buf, 10, &pwm) || pwm > MAX_PWM) + return -EINVAL; + + ret = __set_pwm(ctx, pwm); + if (ret) + return ret; + + pwm_fan_update_state(ctx, pwm); + return count; +} + static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, char *buf) { @@ -91,10 +114,108 @@ static struct attribute *pwm_fan_attrs[] = { ATTRIBUTE_GROUPS(pwm_fan); +/* thermal cooling device callbacks */ +static int pwm_fan_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct pwm_fan_ctx *ctx = cdev->devdata; + + if (!ctx) + return -EINVAL; + + *state = ctx->pwm_fan_max_state; + + return 0; +} + +static int pwm_fan_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct pwm_fan_ctx *ctx = cdev->devdata; + + if (!ctx) + return -EINVAL; + + *state = ctx->pwm_fan_state; + + return 0; +} + +static int +pwm_fan_set_cur_state(struct thermal_cooling_device *cdev, unsigned long state) +{ + struct pwm_fan_ctx *ctx = cdev->devdata; + int ret; + + if (!ctx || (state > ctx->pwm_fan_max_state)) + return -EINVAL; + + if (state == ctx->pwm_fan_state) + return 0; + + ret = __set_pwm(ctx, ctx->pwm_fan_cooling_levels[state]); + if (ret) { + dev_err(&cdev->device, "Cannot set pwm!\n"); + return ret; + } + + ctx->pwm_fan_state = state; + + return ret; +} + +static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = { + .get_max_state = pwm_fan_get_max_state, + .get_cur_state = pwm_fan_get_cur_state, + .set_cur_state = pwm_fan_set_cur_state, +}; + +static int pwm_fan_of_get_cooling_data(struct device *dev, + struct pwm_fan_ctx *ctx) +{ + struct device_node *np = dev->of_node; + int num, i, ret; + + if (!of_find_property(np, "cooling-levels", NULL)) + return 0; + + ret = of_property_count_u32_elems(np, "cooling-levels"); + if (ret <= 0) { + dev_err(dev, "Wrong data!\n"); + return ret ? : -EINVAL; + } + + num = ret; + ctx->pwm_fan_cooling_levels = devm_kzalloc(dev, num * sizeof(u32), + GFP_KERNEL); + if (!ctx->pwm_fan_cooling_levels) + return -ENOMEM; + + ret = of_property_read_u32_array(np, "cooling-levels", + ctx->pwm_fan_cooling_levels, num); + if (ret) { + dev_err(dev, "Property 'cooling-levels' cannot be read!\n"); + return ret; + } + + for (i = 0; i < num; i++) { + if (ctx->pwm_fan_cooling_levels[i] > MAX_PWM) { + dev_err(dev, "PWM fan state[%d]:%d > %d\n", i, + ctx->pwm_fan_cooling_levels[i], MAX_PWM); + return -EINVAL; + } + } + + ctx->pwm_fan_max_state = num - 1; + + return 0; +} + static int pwm_fan_probe(struct platform_device *pdev) { - struct device *hwmon; + struct thermal_cooling_device *cdev; struct pwm_fan_ctx *ctx; + struct device *hwmon; int duty_cycle; int ret; @@ -136,6 +257,26 @@ static int pwm_fan_probe(struct platform_device *pdev) pwm_disable(ctx->pwm); return PTR_ERR(hwmon); } + + ret = pwm_fan_of_get_cooling_data(&pdev->dev, ctx); + if (ret) + return ret; + + ctx->pwm_fan_state = ctx->pwm_fan_max_state; + if (IS_ENABLED(CONFIG_THERMAL)) { + cdev = thermal_of_cooling_device_register(pdev->dev.of_node, + "pwm-fan", ctx, + &pwm_fan_cooling_ops); + if (IS_ERR(cdev)) { + dev_err(&pdev->dev, + "Failed to register pwm-fan as cooling device"); + pwm_disable(ctx->pwm); + return PTR_ERR(cdev); + } + ctx->cdev = cdev; + thermal_cdev_update(cdev); + } + return 0; } @@ -143,6 +284,7 @@ static int pwm_fan_remove(struct platform_device *pdev) { struct pwm_fan_ctx *ctx = platform_get_drvdata(pdev); + thermal_cooling_device_unregister(ctx->cdev); if (ctx->pwm_value) pwm_disable(ctx->pwm); return 0; @@ -177,7 +319,7 @@ static int pwm_fan_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pwm_fan_pm, pwm_fan_suspend, pwm_fan_resume); -static struct of_device_id of_pwm_fan_match[] = { +static const struct of_device_id of_pwm_fan_match[] = { { .compatible = "pwm-fan", }, {}, }; diff --git a/drivers/hwmon/vexpress.c b/drivers/hwmon/vexpress.c index cf1848b8fb32..8ba419d343f8 100644 --- a/drivers/hwmon/vexpress.c +++ b/drivers/hwmon/vexpress.c @@ -193,7 +193,7 @@ static struct vexpress_hwmon_type vexpress_hwmon_energy = { }, }; -static struct of_device_id vexpress_hwmon_of_match[] = { +static const struct of_device_id vexpress_hwmon_of_match[] = { #if !defined(CONFIG_REGULATOR_VEXPRESS) { .compatible = "arm,vexpress-volt", |