diff options
-rw-r--r-- | CREDITS | 8 | ||||
-rw-r--r-- | Documentation/hwmon/htu21 | 46 | ||||
-rw-r--r-- | Documentation/hwmon/ltc3815 | 61 | ||||
-rw-r--r-- | MAINTAINERS | 4 | ||||
-rw-r--r-- | drivers/hwmon/Kconfig | 10 | ||||
-rw-r--r-- | drivers/hwmon/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/fam15h_power.c | 7 | ||||
-rw-r--r-- | drivers/hwmon/htu21.c | 174 | ||||
-rw-r--r-- | drivers/hwmon/ibmaem.c | 12 | ||||
-rw-r--r-- | drivers/hwmon/nct6683.c | 89 | ||||
-rw-r--r-- | drivers/hwmon/nct6775.c | 11 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/Kconfig | 10 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/Makefile | 1 | ||||
-rw-r--r-- | drivers/hwmon/pmbus/ltc3815.c | 215 |
14 files changed, 381 insertions, 268 deletions
@@ -1507,6 +1507,14 @@ S: 312/107 Canberra Avenue S: Griffith, ACT 2603 S: Australia +N: Andreas Herrmann +E: herrmann.der.user@gmail.com +E: herrmann.der.user@googlemail.com +D: Key developer of x86/AMD64 +D: Author of AMD family 15h processor power monitoring driver +D: Maintainer of AMD Athlon 64 and Opteron processor frequency driver +S: Germany + N: Sebastian Hetze E: she@lunetix.de D: German Linux Documentation, diff --git a/Documentation/hwmon/htu21 b/Documentation/hwmon/htu21 deleted file mode 100644 index f39a215fb6ae..000000000000 --- a/Documentation/hwmon/htu21 +++ /dev/null @@ -1,46 +0,0 @@ -Kernel driver htu21 -=================== - -Supported chips: - * Measurement Specialties HTU21D - Prefix: 'htu21' - Addresses scanned: none - Datasheet: Publicly available at the Measurement Specialties website - http://www.meas-spec.com/downloads/HTU21D.pdf - - -Author: - William Markezana <william.markezana@meas-spec.com> - -Description ------------ - -The HTU21D is a humidity and temperature sensor in a DFN package of -only 3 x 3 mm footprint and 0.9 mm height. - -The devices communicate with the I2C protocol. All sensors are set to the -same I2C address 0x40, so an entry with I2C_BOARD_INFO("htu21", 0x40) can -be used in the board setup code. - -This driver does not auto-detect devices. You will have to instantiate the -devices explicitly. Please see Documentation/i2c/instantiating-devices -for details. - -sysfs-Interface ---------------- - -temp1_input - temperature input -humidity1_input - humidity input - -Notes ------ - -The driver uses the default resolution settings of 12 bit for humidity and 14 -bit for temperature, which results in typical measurement times of 11 ms for -humidity and 44 ms for temperature. To keep self heating below 0.1 degree -Celsius, the device should not be active for more than 10% of the time. For -this reason, the driver performs no more than two measurements per second and -reports cached information if polled more frequently. - -Different resolutions, the on-chip heater, using the CRC checksum and reading -the serial number are not supported yet. diff --git a/Documentation/hwmon/ltc3815 b/Documentation/hwmon/ltc3815 new file mode 100644 index 000000000000..eb7db2d13587 --- /dev/null +++ b/Documentation/hwmon/ltc3815 @@ -0,0 +1,61 @@ +Kernel driver ltc3815 +===================== + +Supported chips: + * Linear Technology LTC3815 + Prefix: 'ltc3815' + Addresses scanned: - + Datasheet: http://www.linear.com/product/ltc3815 + +Author: Guenter Roeck <linux@roeck-us.net> + + +Description +----------- + +LTC3815 is a Monolithic Synchronous DC/DC Step-Down Converter. + + +Usage Notes +----------- + +This driver does not probe for PMBus devices. You will have to instantiate +devices explicitly. + +Example: the following commands will load the driver for an LTC3815 +at address 0x20 on I2C bus #1: + +# modprobe ltc3815 +# echo ltc3815 0x20 > /sys/bus/i2c/devices/i2c-1/new_device + + +Sysfs attributes +---------------- + +in1_label "vin" +in1_input Measured input voltage. +in1_alarm Input voltage alarm. +in1_highest Highest input voltage. +in1_reset_history Reset input voltage history. + +in2_label "vout1". +in2_input Measured output voltage. +in2_alarm Output voltage alarm. +in2_highest Highest output voltage. +in2_reset_history Reset output voltage history. + +temp1_input Measured chip temperature. +temp1_alarm Temperature alarm. +temp1_highest Highest measured temperature. +temp1_reset_history Reset temperature history. + +curr1_label "iin". +curr1_input Measured input current. +curr1_highest Highest input current. +curr1_reset_history Reset input current history. + +curr2_label "iout1". +curr2_input Measured output current. +curr2_alarm Output current alarm. +curr2_highest Highest output current. +curr2_reset_history Reset output current history. diff --git a/MAINTAINERS b/MAINTAINERS index a394704a9003..cbfb81b5ced5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -614,9 +614,9 @@ F: drivers/crypto/ccp/ F: include/linux/ccp.h AMD FAM15H PROCESSOR POWER MONITORING DRIVER -M: Andreas Herrmann <herrmann.der.user@googlemail.com> +M: Huang Rui <ray.huang@amd.com> L: lm-sensors@lm-sensors.org -S: Maintained +S: Supported F: Documentation/hwmon/fam15h_power F: drivers/hwmon/fam15h_power.c diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 80a73bfc1a65..60fb80bd353d 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -859,16 +859,6 @@ config SENSORS_MAX31790 This driver can also be built as a module. If so, the module will be called max31790. -config SENSORS_HTU21 - tristate "Measurement Specialties HTU21D humidity/temperature sensors" - depends on I2C - help - If you say yes here you get support for the Measurement Specialties - HTU21D humidity and temperature sensors. - - This driver can also be built as a module. If so, the module - will be called htu21. - config SENSORS_MCP3021 tristate "Microchip MCP3021 and compatibles" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 12a32398fdcc..30c94df31465 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -68,7 +68,6 @@ obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o -obj-$(CONFIG_SENSORS_HTU21) += htu21.o obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o obj-$(CONFIG_SENSORS_I5500) += i5500_temp.o obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o diff --git a/drivers/hwmon/fam15h_power.c b/drivers/hwmon/fam15h_power.c index 5f7067d7b625..f77eb971ce95 100644 --- a/drivers/hwmon/fam15h_power.c +++ b/drivers/hwmon/fam15h_power.c @@ -47,6 +47,8 @@ MODULE_LICENSE("GPL"); #define MSR_F15H_CU_MAX_PWR_ACCUMULATOR 0xc001007b +#define PCI_DEVICE_ID_AMD_15H_M70H_NB_F4 0x15b4 + struct fam15h_power_data { struct pci_dev *pdev; unsigned int tdp_to_watts; @@ -124,7 +126,7 @@ static int fam15h_power_init_attrs(struct pci_dev *pdev, if (c->x86 == 0x15 && (c->x86_model <= 0xf || - (c->x86_model >= 0x60 && c->x86_model <= 0x6f))) + (c->x86_model >= 0x60 && c->x86_model <= 0x7f))) n += 1; fam15h_power_attrs = devm_kcalloc(&pdev->dev, n, @@ -138,7 +140,7 @@ static int fam15h_power_init_attrs(struct pci_dev *pdev, fam15h_power_attrs[n++] = &dev_attr_power1_crit.attr; if (c->x86 == 0x15 && (c->x86_model <= 0xf || - (c->x86_model >= 0x60 && c->x86_model <= 0x6f))) + (c->x86_model >= 0x60 && c->x86_model <= 0x7f))) fam15h_power_attrs[n++] = &dev_attr_power1_input.attr; data->group.attrs = fam15h_power_attrs; @@ -296,6 +298,7 @@ static const struct pci_device_id fam15h_power_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F4) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_15H_M70H_NB_F4) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) }, { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) }, {} diff --git a/drivers/hwmon/htu21.c b/drivers/hwmon/htu21.c deleted file mode 100644 index 4c3bbb72f82a..000000000000 --- a/drivers/hwmon/htu21.c +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Measurement Specialties HTU21D humidity and temperature sensor driver - * - * Copyright (C) 2013 William Markezana <william.markezana@meas-spec.com> - * - * 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/init.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/hwmon.h> -#include <linux/hwmon-sysfs.h> -#include <linux/err.h> -#include <linux/mutex.h> -#include <linux/device.h> -#include <linux/jiffies.h> - -/* HTU21 Commands */ -#define HTU21_T_MEASUREMENT_HM 0xE3 -#define HTU21_RH_MEASUREMENT_HM 0xE5 - -struct htu21 { - struct i2c_client *client; - struct mutex lock; - bool valid; - unsigned long last_update; - int temperature; - int humidity; -}; - -static inline int htu21_temp_ticks_to_millicelsius(int ticks) -{ - ticks &= ~0x0003; /* clear status bits */ - /* - * Formula T = -46.85 + 175.72 * ST / 2^16 from datasheet p14, - * optimized for integer fixed point (3 digits) arithmetic - */ - return ((21965 * ticks) >> 13) - 46850; -} - -static inline int htu21_rh_ticks_to_per_cent_mille(int ticks) -{ - ticks &= ~0x0003; /* clear status bits */ - /* - * Formula RH = -6 + 125 * SRH / 2^16 from datasheet p14, - * optimized for integer fixed point (3 digits) arithmetic - */ - return ((15625 * ticks) >> 13) - 6000; -} - -static int htu21_update_measurements(struct device *dev) -{ - struct htu21 *htu21 = dev_get_drvdata(dev); - struct i2c_client *client = htu21->client; - int ret = 0; - - mutex_lock(&htu21->lock); - - if (time_after(jiffies, htu21->last_update + HZ / 2) || - !htu21->valid) { - ret = i2c_smbus_read_word_swapped(client, - HTU21_T_MEASUREMENT_HM); - if (ret < 0) - goto out; - htu21->temperature = htu21_temp_ticks_to_millicelsius(ret); - ret = i2c_smbus_read_word_swapped(client, - HTU21_RH_MEASUREMENT_HM); - if (ret < 0) - goto out; - htu21->humidity = htu21_rh_ticks_to_per_cent_mille(ret); - htu21->last_update = jiffies; - htu21->valid = true; - } -out: - mutex_unlock(&htu21->lock); - - return ret >= 0 ? 0 : ret; -} - -static ssize_t htu21_show_temperature(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct htu21 *htu21 = dev_get_drvdata(dev); - int ret; - - ret = htu21_update_measurements(dev); - if (ret < 0) - return ret; - return sprintf(buf, "%d\n", htu21->temperature); -} - -static ssize_t htu21_show_humidity(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct htu21 *htu21 = dev_get_drvdata(dev); - int ret; - - ret = htu21_update_measurements(dev); - if (ret < 0) - return ret; - return sprintf(buf, "%d\n", htu21->humidity); -} - -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, - htu21_show_temperature, NULL, 0); -static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, - htu21_show_humidity, NULL, 0); - -static struct attribute *htu21_attrs[] = { - &sensor_dev_attr_temp1_input.dev_attr.attr, - &sensor_dev_attr_humidity1_input.dev_attr.attr, - NULL -}; - -ATTRIBUTE_GROUPS(htu21); - -static int htu21_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct device *dev = &client->dev; - struct htu21 *htu21; - struct device *hwmon_dev; - - if (!i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_READ_WORD_DATA)) { - dev_err(&client->dev, - "adapter does not support SMBus word transactions\n"); - return -ENODEV; - } - - htu21 = devm_kzalloc(dev, sizeof(*htu21), GFP_KERNEL); - if (!htu21) - return -ENOMEM; - - htu21->client = client; - mutex_init(&htu21->lock); - - hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, - htu21, - htu21_groups); - return PTR_ERR_OR_ZERO(hwmon_dev); -} - -static const struct i2c_device_id htu21_id[] = { - { "htu21", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, htu21_id); - -static struct i2c_driver htu21_driver = { - .class = I2C_CLASS_HWMON, - .driver = { - .name = "htu21", - }, - .probe = htu21_probe, - .id_table = htu21_id, -}; - -module_i2c_driver(htu21_driver); - -MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>"); -MODULE_DESCRIPTION("MEAS HTU21D humidity and temperature sensor driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index 7a8a6fbf11ff..1f643782ce04 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -920,8 +920,8 @@ static ssize_t aem_set_power_period(struct device *dev, /* Discover sensors on an AEM device */ static int aem_register_sensors(struct aem_data *data, - struct aem_ro_sensor_template *ro, - struct aem_rw_sensor_template *rw) + const struct aem_ro_sensor_template *ro, + const struct aem_rw_sensor_template *rw) { struct device *dev = &data->pdev->dev; struct sensor_device_attribute *sensors = data->sensors; @@ -1020,19 +1020,19 @@ static void aem_remove_sensors(struct aem_data *data) /* Sensor probe functions */ /* Description of AEM1 sensors */ -static struct aem_ro_sensor_template aem1_ro_sensors[] = { +static const struct aem_ro_sensor_template aem1_ro_sensors[] = { {"energy1_input", aem_show_energy, 0}, {"power1_average", aem_show_power, 0}, {NULL, NULL, 0}, }; -static struct aem_rw_sensor_template aem1_rw_sensors[] = { +static const struct aem_rw_sensor_template aem1_rw_sensors[] = { {"power1_average_interval", aem_show_power_period, aem_set_power_period, 0}, {NULL, NULL, NULL, 0}, }; /* Description of AEM2 sensors */ -static struct aem_ro_sensor_template aem2_ro_sensors[] = { +static const struct aem_ro_sensor_template aem2_ro_sensors[] = { {"energy1_input", aem_show_energy, 0}, {"energy2_input", aem_show_energy, 1}, {"power1_average", aem_show_power, 0}, @@ -1050,7 +1050,7 @@ static struct aem_ro_sensor_template aem2_ro_sensors[] = { {NULL, NULL, 0}, }; -static struct aem_rw_sensor_template aem2_rw_sensors[] = { +static const struct aem_rw_sensor_template aem2_rw_sensors[] = { {"power1_average_interval", aem_show_power_period, aem_set_power_period, 0}, {"power2_average_interval", aem_show_power_period, aem_set_power_period, 1}, {NULL, NULL, NULL, 0}, diff --git a/drivers/hwmon/nct6683.c b/drivers/hwmon/nct6683.c index 37f01702d081..559c596b24f9 100644 --- a/drivers/hwmon/nct6683.c +++ b/drivers/hwmon/nct6683.c @@ -29,7 +29,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/acpi.h> -#include <linux/dmi.h> +#include <linux/delay.h> #include <linux/err.h> #include <linux/init.h> #include <linux/io.h> @@ -45,7 +45,7 @@ enum kinds { nct6683 }; static bool force; module_param(force, bool, 0); -MODULE_PARM_DESC(force, "Set to one to enable detection on non-Intel boards"); +MODULE_PARM_DESC(force, "Set to one to enable support for unknown vendors"); static const char * const nct6683_device_names[] = { "nct6683", @@ -141,6 +141,7 @@ superio_exit(int ioreg) #define NCT6683_REG_MON(x) (0x100 + (x) * 2) #define NCT6683_REG_FAN_RPM(x) (0x140 + (x) * 2) #define NCT6683_REG_PWM(x) (0x160 + (x)) +#define NCT6683_REG_PWM_WRITE(x) (0xa28 + (x)) #define NCT6683_REG_MON_STS(x) (0x174 + (x)) #define NCT6683_REG_IDLE(x) (0x178 + (x)) @@ -165,8 +166,13 @@ superio_exit(int ioreg) #define NCT6683_REG_FAN_MIN(x) (0x3b8 + (x) * 2) /* 16 bit */ +#define NCT6683_REG_FAN_CFG_CTRL 0xa01 +#define NCT6683_FAN_CFG_REQ 0x80 +#define NCT6683_FAN_CFG_DONE 0x40 + #define NCT6683_REG_CUSTOMER_ID 0x602 #define NCT6683_CUSTOMER_ID_INTEL 0x805 +#define NCT6683_CUSTOMER_ID_MITAC 0xa0e #define NCT6683_REG_BUILD_YEAR 0x604 #define NCT6683_REG_BUILD_MONTH 0x605 @@ -394,7 +400,8 @@ struct sensor_template_group { }; static struct attribute_group * -nct6683_create_attr_group(struct device *dev, struct sensor_template_group *tg, +nct6683_create_attr_group(struct device *dev, + const struct sensor_template_group *tg, int repeat) { struct sensor_device_attribute_2 *a2; @@ -559,6 +566,7 @@ static int get_temp_reg(struct nct6683_data *data, int nr, int index) break; } break; + case NCT6683_CUSTOMER_ID_MITAC: default: switch (nr) { default: @@ -703,7 +711,7 @@ static struct sensor_device_template *nct6683_attributes_in_template[] = { NULL }; -static struct sensor_template_group nct6683_in_template_group = { +static const struct sensor_template_group nct6683_in_template_group = { .templates = nct6683_attributes_in_template, .is_visible = nct6683_in_is_visible, }; @@ -774,7 +782,7 @@ static struct sensor_device_template *nct6683_attributes_fan_template[] = { NULL }; -static struct sensor_template_group nct6683_fan_template_group = { +static const struct sensor_template_group nct6683_fan_template_group = { .templates = nct6683_attributes_fan_template, .is_visible = nct6683_fan_is_visible, .base = 1, @@ -902,7 +910,7 @@ static struct sensor_device_template *nct6683_attributes_temp_template[] = { NULL }; -static struct sensor_template_group nct6683_temp_template_group = { +static const struct sensor_template_group nct6683_temp_template_group = { .templates = nct6683_attributes_temp_template, .is_visible = nct6683_temp_is_visible, .base = 1, @@ -918,7 +926,29 @@ show_pwm(struct device *dev, struct device_attribute *attr, char *buf) return sprintf(buf, "%d\n", data->pwm[index]); } -SENSOR_TEMPLATE(pwm, "pwm%d", S_IRUGO, show_pwm, NULL, 0); +static ssize_t +store_pwm(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 nct6683_data *data = dev_get_drvdata(dev); + int index = sattr->index; + unsigned long val; + + if (kstrtoul(buf, 10, &val) || val > 255) + return -EINVAL; + + mutex_lock(&data->update_lock); + nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_REQ); + usleep_range(1000, 2000); + nct6683_write(data, NCT6683_REG_PWM_WRITE(index), val); + nct6683_write(data, NCT6683_REG_FAN_CFG_CTRL, NCT6683_FAN_CFG_DONE); + mutex_unlock(&data->update_lock); + + return count; +} + +SENSOR_TEMPLATE(pwm, "pwm%d", S_IRUGO, show_pwm, store_pwm, 0); static umode_t nct6683_pwm_is_visible(struct kobject *kobj, struct attribute *attr, int index) @@ -930,6 +960,10 @@ static umode_t nct6683_pwm_is_visible(struct kobject *kobj, if (!(data->have_pwm & (1 << pwm))) return 0; + /* Only update pwm values for Mitac boards */ + if (data->customer_id == NCT6683_CUSTOMER_ID_MITAC) + return attr->mode | S_IWUSR; + return attr->mode; } @@ -938,7 +972,7 @@ static struct sensor_device_template *nct6683_attributes_pwm_template[] = { NULL }; -static struct sensor_template_group nct6683_pwm_template_group = { +static const struct sensor_template_group nct6683_pwm_template_group = { .templates = nct6683_attributes_pwm_template, .is_visible = nct6683_pwm_is_visible, .base = 1, @@ -1170,6 +1204,7 @@ static int nct6683_probe(struct platform_device *pdev) struct device *hwmon_dev; struct resource *res; int groups = 0; + char build[16]; res = platform_get_resource(pdev, IORESOURCE_IO, 0); if (!devm_request_region(dev, res->start, IOREGION_LENGTH, DRVNAME)) @@ -1187,6 +1222,17 @@ static int nct6683_probe(struct platform_device *pdev) data->customer_id = nct6683_read16(data, NCT6683_REG_CUSTOMER_ID); + /* By default only instantiate driver if the customer ID is known */ + switch (data->customer_id) { + case NCT6683_CUSTOMER_ID_INTEL: + break; + case NCT6683_CUSTOMER_ID_MITAC: + break; + default: + if (!force) + return -ENODEV; + } + nct6683_init_device(data); nct6683_setup_fans(data); nct6683_setup_sensors(data); @@ -1230,13 +1276,22 @@ static int nct6683_probe(struct platform_device *pdev) } data->groups[groups++] = &nct6683_group_other; - dev_info(dev, "%s EC firmware version %d.%d build %02x/%02x/%02x\n", + if (data->customer_id == NCT6683_CUSTOMER_ID_INTEL) + scnprintf(build, sizeof(build), "%02x/%02x/%02x", + nct6683_read(data, NCT6683_REG_BUILD_MONTH), + nct6683_read(data, NCT6683_REG_BUILD_DAY), + nct6683_read(data, NCT6683_REG_BUILD_YEAR)); + else + scnprintf(build, sizeof(build), "%02d/%02d/%02d", + nct6683_read(data, NCT6683_REG_BUILD_MONTH), + nct6683_read(data, NCT6683_REG_BUILD_DAY), + nct6683_read(data, NCT6683_REG_BUILD_YEAR)); + + dev_info(dev, "%s EC firmware version %d.%d build %s\n", nct6683_chip_names[data->kind], nct6683_read(data, NCT6683_REG_VERSION_HI), nct6683_read(data, NCT6683_REG_VERSION_LO), - nct6683_read(data, NCT6683_REG_BUILD_MONTH), - nct6683_read(data, NCT6683_REG_BUILD_DAY), - nct6683_read(data, NCT6683_REG_BUILD_YEAR)); + build); hwmon_dev = devm_hwmon_device_register_with_groups(dev, nct6683_device_names[data->kind], data, data->groups); @@ -1292,20 +1347,10 @@ static struct platform_driver nct6683_driver = { static int __init nct6683_find(int sioaddr, struct nct6683_sio_data *sio_data) { - const char *board_vendor; int addr; u16 val; int err; - /* - * Only run on Intel boards unless the 'force' module parameter is set - */ - if (!force) { - board_vendor = dmi_get_system_info(DMI_BOARD_VENDOR); - if (!board_vendor || strcmp(board_vendor, "Intel Corporation")) - return -ENODEV; - } - err = superio_enter(sioaddr); if (err) return err; diff --git a/drivers/hwmon/nct6775.c b/drivers/hwmon/nct6775.c index d7ebdf8651f5..d087a8e00cf5 100644 --- a/drivers/hwmon/nct6775.c +++ b/drivers/hwmon/nct6775.c @@ -1045,7 +1045,8 @@ struct sensor_template_group { }; static struct attribute_group * -nct6775_create_attr_group(struct device *dev, struct sensor_template_group *tg, +nct6775_create_attr_group(struct device *dev, + const struct sensor_template_group *tg, int repeat) { struct attribute_group *group; @@ -1827,7 +1828,7 @@ static struct sensor_device_template *nct6775_attributes_in_template[] = { NULL }; -static struct sensor_template_group nct6775_in_template_group = { +static const struct sensor_template_group nct6775_in_template_group = { .templates = nct6775_attributes_in_template, .is_visible = nct6775_in_is_visible, }; @@ -2046,7 +2047,7 @@ static struct sensor_device_template *nct6775_attributes_fan_template[] = { NULL }; -static struct sensor_template_group nct6775_fan_template_group = { +static const struct sensor_template_group nct6775_fan_template_group = { .templates = nct6775_attributes_fan_template, .is_visible = nct6775_fan_is_visible, .base = 1, @@ -2255,7 +2256,7 @@ static struct sensor_device_template *nct6775_attributes_temp_template[] = { NULL }; -static struct sensor_template_group nct6775_temp_template_group = { +static const struct sensor_template_group nct6775_temp_template_group = { .templates = nct6775_attributes_temp_template, .is_visible = nct6775_temp_is_visible, .base = 1, @@ -3117,7 +3118,7 @@ static struct sensor_device_template *nct6775_attributes_pwm_template[] = { NULL }; -static struct sensor_template_group nct6775_pwm_template_group = { +static const struct sensor_template_group nct6775_pwm_template_group = { .templates = nct6775_attributes_pwm_template, .is_visible = nct6775_pwm_is_visible, .base = 1, diff --git a/drivers/hwmon/pmbus/Kconfig b/drivers/hwmon/pmbus/Kconfig index df6ebb2b8f0f..7e5cc3d025ef 100644 --- a/drivers/hwmon/pmbus/Kconfig +++ b/drivers/hwmon/pmbus/Kconfig @@ -65,6 +65,16 @@ config SENSORS_LTC2978_REGULATOR If you say yes here you get regulator support for Linear Technology LTC2974, LTC2977, LTC2978, LTC3880, LTC3883, and LTM4676. +config SENSORS_LTC3815 + tristate "Linear Technologies LTC3815" + default n + help + If you say yes here you get hardware monitoring support for Linear + Technology LTC3815. + + This driver can also be built as a module. If so, the module will + be called ltc3815. + config SENSORS_MAX16064 tristate "Maxim MAX16064" default n diff --git a/drivers/hwmon/pmbus/Makefile b/drivers/hwmon/pmbus/Makefile index bce046d37f02..562132054aaf 100644 --- a/drivers/hwmon/pmbus/Makefile +++ b/drivers/hwmon/pmbus/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_SENSORS_PMBUS) += pmbus.o obj-$(CONFIG_SENSORS_ADM1275) += adm1275.o obj-$(CONFIG_SENSORS_LM25066) += lm25066.o obj-$(CONFIG_SENSORS_LTC2978) += ltc2978.o +obj-$(CONFIG_SENSORS_LTC3815) += ltc3815.o obj-$(CONFIG_SENSORS_MAX16064) += max16064.o obj-$(CONFIG_SENSORS_MAX20751) += max20751.o obj-$(CONFIG_SENSORS_MAX34440) += max34440.o diff --git a/drivers/hwmon/pmbus/ltc3815.c b/drivers/hwmon/pmbus/ltc3815.c new file mode 100644 index 000000000000..bb32e6276622 --- /dev/null +++ b/drivers/hwmon/pmbus/ltc3815.c @@ -0,0 +1,215 @@ +/* + * Hardware monitoring driver for LTC3815 + * + * Copyright (c) 2015 Linear Technology + * Copyright (c) 2015 Guenter Roeck + * + * 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/err.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include "pmbus.h" + +#define LTC3815_MFR_IOUT_PEAK 0xd7 +#define LTC3815_MFR_VOUT_PEAK 0xdd +#define LTC3815_MFR_VIN_PEAK 0xde +#define LTC3815_MFR_TEMP_PEAK 0xdf +#define LTC3815_MFR_IIN_PEAK 0xe1 +#define LTC3815_MFR_SPECIAL_ID 0xe7 + +#define LTC3815_ID 0x8000 +#define LTC3815_ID_MASK 0xff00 + +static int ltc3815_read_byte_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VOUT_MODE: + /* + * The chip returns 0x3e, suggesting VID mode with manufacturer + * specific VID codes. Since the output voltage is reported + * with a LSB of 0.5mV, override and report direct mode with + * appropriate coefficients. + */ + ret = 0x40; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int ltc3815_write_byte(struct i2c_client *client, int page, u8 reg) +{ + int ret; + + switch (reg) { + case PMBUS_CLEAR_FAULTS: + /* + * LTC3815 does not support the CLEAR_FAULTS command. + * Emulate it by clearing the status register. + */ + ret = pmbus_read_word_data(client, 0, PMBUS_STATUS_WORD); + if (ret > 0) { + pmbus_write_word_data(client, 0, PMBUS_STATUS_WORD, + ret); + ret = 0; + } + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int ltc3815_read_word_data(struct i2c_client *client, int page, int reg) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_READ_VIN_MAX: + ret = pmbus_read_word_data(client, page, LTC3815_MFR_VIN_PEAK); + break; + case PMBUS_VIRT_READ_VOUT_MAX: + ret = pmbus_read_word_data(client, page, LTC3815_MFR_VOUT_PEAK); + break; + case PMBUS_VIRT_READ_TEMP_MAX: + ret = pmbus_read_word_data(client, page, LTC3815_MFR_TEMP_PEAK); + break; + case PMBUS_VIRT_READ_IOUT_MAX: + ret = pmbus_read_word_data(client, page, LTC3815_MFR_IOUT_PEAK); + break; + case PMBUS_VIRT_READ_IIN_MAX: + ret = pmbus_read_word_data(client, page, LTC3815_MFR_IIN_PEAK); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + case PMBUS_VIRT_RESET_VIN_HISTORY: + case PMBUS_VIRT_RESET_TEMP_HISTORY: + case PMBUS_VIRT_RESET_IOUT_HISTORY: + case PMBUS_VIRT_RESET_IIN_HISTORY: + ret = 0; + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static int ltc3815_write_word_data(struct i2c_client *client, int page, + int reg, u16 word) +{ + int ret; + + switch (reg) { + case PMBUS_VIRT_RESET_IIN_HISTORY: + ret = pmbus_write_word_data(client, page, + LTC3815_MFR_IIN_PEAK, 0); + break; + case PMBUS_VIRT_RESET_IOUT_HISTORY: + ret = pmbus_write_word_data(client, page, + LTC3815_MFR_IOUT_PEAK, 0); + break; + case PMBUS_VIRT_RESET_VOUT_HISTORY: + ret = pmbus_write_word_data(client, page, + LTC3815_MFR_VOUT_PEAK, 0); + break; + case PMBUS_VIRT_RESET_VIN_HISTORY: + ret = pmbus_write_word_data(client, page, + LTC3815_MFR_VIN_PEAK, 0); + break; + case PMBUS_VIRT_RESET_TEMP_HISTORY: + ret = pmbus_write_word_data(client, page, + LTC3815_MFR_TEMP_PEAK, 0); + break; + default: + ret = -ENODATA; + break; + } + return ret; +} + +static const struct i2c_device_id ltc3815_id[] = { + {"ltc3815", 0}, + { } +}; +MODULE_DEVICE_TABLE(i2c, ltc3815_id); + +static struct pmbus_driver_info ltc3815_info = { + .pages = 1, + .format[PSC_VOLTAGE_IN] = direct, + .format[PSC_VOLTAGE_OUT] = direct, + .format[PSC_CURRENT_IN] = direct, + .format[PSC_CURRENT_OUT] = direct, + .format[PSC_TEMPERATURE] = direct, + .m[PSC_VOLTAGE_IN] = 250, + .b[PSC_VOLTAGE_IN] = 0, + .R[PSC_VOLTAGE_IN] = 0, + .m[PSC_VOLTAGE_OUT] = 2, + .b[PSC_VOLTAGE_OUT] = 0, + .R[PSC_VOLTAGE_OUT] = 3, + .m[PSC_CURRENT_IN] = 1, + .b[PSC_CURRENT_IN] = 0, + .R[PSC_CURRENT_IN] = 2, + .m[PSC_CURRENT_OUT] = 1, + .b[PSC_CURRENT_OUT] = 0, + .R[PSC_CURRENT_OUT] = 2, + .m[PSC_TEMPERATURE] = 1, + .b[PSC_TEMPERATURE] = 0, + .R[PSC_TEMPERATURE] = 0, + .func[0] = PMBUS_HAVE_VIN | PMBUS_HAVE_IIN | PMBUS_HAVE_VOUT | + PMBUS_HAVE_IOUT | PMBUS_HAVE_TEMP, + .read_byte_data = ltc3815_read_byte_data, + .read_word_data = ltc3815_read_word_data, + .write_byte = ltc3815_write_byte, + .write_word_data = ltc3815_write_word_data, +}; + +static int ltc3815_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int chip_id; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_READ_WORD_DATA)) + return -ENODEV; + + chip_id = i2c_smbus_read_word_data(client, LTC3815_MFR_SPECIAL_ID); + if (chip_id < 0) + return chip_id; + if ((chip_id & LTC3815_ID_MASK) != LTC3815_ID) + return -ENODEV; + + return pmbus_do_probe(client, id, <c3815_info); +} + +static struct i2c_driver ltc3815_driver = { + .driver = { + .name = "ltc3815", + }, + .probe = ltc3815_probe, + .remove = pmbus_do_remove, + .id_table = ltc3815_id, +}; + +module_i2c_driver(ltc3815_driver); + +MODULE_AUTHOR("Guenter Roeck"); +MODULE_DESCRIPTION("PMBus driver for LTC3815"); +MODULE_LICENSE("GPL"); |