From f7290e24e3ce8a0b4b39a74800fd341b6b7a6f47 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:47 +0100 Subject: hwmon: Clarify autopwm trip points documentation Document the case of hybrid automatic fan speed control implementations, where trip points are associated to both PWM output channels and temperature input channels. Signed-off-by: Jean Delvare --- Documentation/hwmon/sysfs-interface | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'Documentation/hwmon') diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index 82def883361..3de6b0bcb14 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -225,8 +225,6 @@ pwm[1-*]_auto_point[1-*]_temp_hyst to PWM output channels. RW -OR - temp[1-*]_auto_point[1-*]_pwm temp[1-*]_auto_point[1-*]_temp temp[1-*]_auto_point[1-*]_temp_hyst @@ -235,6 +233,15 @@ temp[1-*]_auto_point[1-*]_temp_hyst to temperature channels. RW +There is a third case where trip points are associated to both PWM output +channels and temperature channels: the PWM values are associated to PWM +output channels while the temperature values are associated to temperature +channels. In that case, the result is determined by the mapping between +temperature inputs and PWM outputs. When several temperature inputs are +mapped to a given PWM output, this leads to several candidate PWM values. +The actual result is up to the chip, but in general the highest candidate +value (fastest fan speed) wins. + **************** * Temperatures * -- cgit v1.2.3-70-g09d2 From 895ff267686663afa894314b749d23ac2867434a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:47 +0100 Subject: hwmon: (it87) Verify the VID pin usage The VID input pins can alternatively be used as GPIOs. Make sure we have at least 4 pins used for VID, otherwise don't bother reading and exposing VID. Signed-off-by: Jean Delvare Tested-by: Adam Nielsen --- Documentation/hwmon/it87 | 1 - drivers/hwmon/it87.c | 20 ++++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) (limited to 'Documentation/hwmon') diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 659315d98e0..f9ba96c0ac4 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -86,7 +86,6 @@ The IT8712F and IT8716F additionally feature VID inputs, used to report the Vcore voltage of the processor. The early IT8712F have 5 VID pins, the IT8716F and late IT8712F have 6. They are shared with other functions though, so the functionality may not be available on a given system. -The driver dumbly assume it is there. The IT8718F and IT8720F also features VID inputs (up to 8 pins) but the value is stored in the Super-I/O configuration space. Due to technical limitations, diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index a3749cb0f18..2f782e3f9a2 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -124,6 +124,7 @@ superio_exit(void) #define IT87_BASE_REG 0x60 /* Logical device 7 registers (IT8712F and later) */ +#define IT87_SIO_GPIO3_REG 0x27 #define IT87_SIO_PINX2_REG 0x2c /* Pin selection */ #define IT87_SIO_VID_REG 0xfc /* VID value */ @@ -244,6 +245,7 @@ struct it87_sio_data { /* Values read from Super-I/O config space */ u8 revision; u8 vid_value; + u8 skip_vid; /* Values set based on DMI strings */ u8 skip_pwm; }; @@ -1028,11 +1030,22 @@ static int __init it87_find(unsigned short *address, chip_type, *address, sio_data->revision); /* Read GPIO config and VID value from LDN 7 (GPIO) */ - if (sio_data->type != it87) { + if (sio_data->type == it87) { + /* The IT8705F doesn't have VID pins at all */ + sio_data->skip_vid = 1; + } else { int reg; superio_select(GPIO); - if (sio_data->type == it8718 || sio_data->type == it8720) + /* We need at least 4 VID pins */ + reg = superio_inb(IT87_SIO_GPIO3_REG); + if (reg & 0x0f) { + pr_info("it87: VID is disabled (pins used for GPIO)\n"); + sio_data->skip_vid = 1; + } + + if ((sio_data->type == it8718 || sio_data->type == it8720) + && !(sio_data->skip_vid)) sio_data->vid_value = superio_inb(IT87_SIO_VID_REG); reg = superio_inb(IT87_SIO_PINX2_REG); @@ -1236,8 +1249,7 @@ static int __devinit it87_probe(struct platform_device *pdev) } } - if (data->type == it8712 || data->type == it8716 - || data->type == it8718 || data->type == it8720) { + if (!sio_data->skip_vid) { data->vrm = vid_which_vrm(); /* VID reading from Super-I/O config space if available */ data->vid = sio_data->vid_value; -- cgit v1.2.3-70-g09d2 From 8918023d40ebb2c086e77368810763975761cb1b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:48 +0100 Subject: hwmon: (w83627hf) Drop the force_addr module parameter This module parameter is there to workaround broken BIOS. I'm not even sure if it was used in the past 5 years, and it gets in the way of converting the driver to the MFD infrastructure. So tell the users how they can do the same from user-space. Signed-off-by: Jean Delvare Cc: Rodolfo Giometti --- Documentation/hwmon/w83627hf | 29 +++++++++++++++++++++++++++-- drivers/hwmon/w83627hf.c | 11 ----------- 2 files changed, 27 insertions(+), 13 deletions(-) (limited to 'Documentation/hwmon') diff --git a/Documentation/hwmon/w83627hf b/Documentation/hwmon/w83627hf index 6ee36dbafd6..44dd2bcc72b 100644 --- a/Documentation/hwmon/w83627hf +++ b/Documentation/hwmon/w83627hf @@ -32,8 +32,6 @@ Authors: Module Parameters ----------------- -* force_addr: int - Initialize the ISA address of the sensors * force_i2c: int Initialize the I2C address of the sensors * init: int @@ -70,3 +68,30 @@ doesn't help, you may just ignore the bogus VID reading with no harm done. For further information on this driver see the w83781d driver documentation. [1] http://www.lm-sensors.org/browser/lm-sensors/trunk/doc/vid + +Forcing the address +------------------- + +The driver used to have a module parameter named force_addr, which could +be used to force the base I/O address of the hardware monitoring block. +This was meant as a workaround for mainboards with a broken BIOS. This +module parameter is gone for technical reasons. If you need this feature, +you can obtain the same result by using the isaset tool (part of +lm-sensors) before loading the driver: + +# Enter the Super I/O config space +isaset -y -f 0x2e 0x87 +isaset -y -f 0x2e 0x87 + +# Select the hwmon logical device +isaset -y 0x2e 0x2f 0x07 0x0b + +# Set the base I/O address (to 0x290 in this example) +isaset -y 0x2e 0x2f 0x60 0x02 +isaset -y 0x2e 0x2f 0x61 0x90 + +# Exit the Super-I/O config space +isaset -y -f 0x2e 0xaa + +The above sequence assumes a Super-I/O config space at 0x2e/0x2f, but +0x4e/0x4f is also possible. diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index 2be28ac4ede..d67407d045b 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -59,10 +59,6 @@ static struct platform_device *pdev; #define DRVNAME "w83627hf" enum chips { w83627hf, w83627thf, w83697hf, w83637hf, w83687thf }; -static u16 force_addr; -module_param(force_addr, ushort, 0); -MODULE_PARM_DESC(force_addr, - "Initialize the base address of the sensors"); static u8 force_i2c = 0x1f; module_param(force_i2c, byte, 0); MODULE_PARM_DESC(force_i2c, @@ -1169,13 +1165,6 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr, } superio_select(W83627HF_LD_HWM); - force_addr &= WINB_ALIGNMENT; - if (force_addr) { - printk(KERN_WARNING DRVNAME ": Forcing address 0x%x\n", - force_addr); - superio_outb(WINB_BASE_REG, force_addr >> 8); - superio_outb(WINB_BASE_REG + 1, force_addr & 0xff); - } val = (superio_inb(WINB_BASE_REG) << 8) | superio_inb(WINB_BASE_REG + 1); *addr = val & WINB_ALIGNMENT; -- cgit v1.2.3-70-g09d2 From 93ee0a75f6e4b2c7ec20fd8f4ace87f88ba785b9 Mon Sep 17 00:00:00 2001 From: Luotao Fu Date: Wed, 9 Dec 2009 20:35:58 +0100 Subject: hwmon: Add Freescale MC13783 ADC driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This driver provides support for the ADC integrated into the Freescale MC13783 PMIC. Signed-off-by: Luotao Fu Signed-off-by: Sascha Hauer Signed-off-by: Uwe Kleine-König Reviewed-by: Mark Brown Acked-by: Hans de Goede Cc: Eric Piel Signed-off-by: Jean Delvare --- Documentation/hwmon/mc13783-adc | 50 +++++++++ drivers/hwmon/Kconfig | 6 + drivers/hwmon/Makefile | 1 + drivers/hwmon/mc13783-adc.c | 236 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 293 insertions(+) create mode 100644 Documentation/hwmon/mc13783-adc create mode 100644 drivers/hwmon/mc13783-adc.c (limited to 'Documentation/hwmon') diff --git a/Documentation/hwmon/mc13783-adc b/Documentation/hwmon/mc13783-adc new file mode 100644 index 00000000000..044531a8640 --- /dev/null +++ b/Documentation/hwmon/mc13783-adc @@ -0,0 +1,50 @@ +Kernel driver mc13783-adc +========================= + +Supported chips: + * Freescale Atlas MC13783 + Prefix: 'mc13783_adc' + Datasheet: http://www.freescale.com/files/rf_if/doc/data_sheet/MC13783.pdf?fsrch=1 + +Authors: + Sascha Hauer + Luotao Fu + +Description +----------- + +The Freescale MC13783 is a Power Management and Audio Circuit. Among +other things it contains a 10-bit A/D converter. The converter has 16 +channels which can be used in different modes. +The A/D converter has a resolution of 2.25mV. Channels 0-4 have +a dedicated meaning with chip internal scaling applied. Channels 5-7 +can be used as general purpose inputs or alternatively in a dedicated +mode. Channels 12-15 are occupied by the touchscreen if it's active. + +Currently the driver only supports channels 2 and 5-15 with no alternative +modes for channels 5-7. + +See this table for the meaning of the different channels and their chip +internal scaling: + +Channel Signal Input Range Scaling +------------------------------------------------------------------------------- +0 Battery Voltage (BATT) 2.50 - 4.65V -2.40V +1 Battery Current (BATT - BATTISNS) -50 - 50 mV x20 +2 Application Supply (BP) 2.50 - 4.65V -2.40V +3 Charger Voltage (CHRGRAW) 0 - 10V / /5 + 0 - 20V /10 +4 Charger Current (CHRGISNSP-CHRGISNSN) -0.25V - 0.25V x4 +5 General Purpose ADIN5 / Battery Pack Thermistor 0 - 2.30V No +6 General Purpose ADIN6 / Backup Voltage (LICELL) 0 - 2.30V / No / + 1.50 - 3.50V -1.20V +7 General Purpose ADIN7 / UID / Die Temperature 0 - 2.30V / No / + 0 - 2.55V / x0.9 / No +8 General Purpose ADIN8 0 - 2.30V No +9 General Purpose ADIN9 0 - 2.30V No +10 General Purpose ADIN10 0 - 2.30V No +11 General Purpose ADIN11 0 - 2.30V No +12 General Purpose TSX1 / Touchscreen X-plate 1 0 - 2.30V No +13 General Purpose TSX2 / Touchscreen X-plate 2 0 - 2.30V No +14 General Purpose TSY1 / Touchscreen Y-plate 1 0 - 2.30V No +15 General Purpose TSY2 / Touchscreen Y-plate 2 0 - 2.30V No diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6e7c30b8caa..dd6939370f7 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1017,6 +1017,12 @@ config SENSORS_APPLESMC Say Y here if you have an applicable laptop and want to experience the awesome power of applesmc. +config SENSORS_MC13783_ADC + tristate "Freescale MC13783 ADC" + depends on MFD_MC13783 + help + Support for the A/D converter on MC13783 PMIC. + if ACPI comment "ACPI drivers" diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index a24a52d12dc..33c2ee10528 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -74,6 +74,7 @@ obj-$(CONFIG_SENSORS_LTC4245) += ltc4245.o obj-$(CONFIG_SENSORS_MAX1111) += max1111.o obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6650) += max6650.o +obj-$(CONFIG_SENSORS_MC13783_ADC)+= mc13783-adc.o obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c new file mode 100644 index 00000000000..883fa8197da --- /dev/null +++ b/drivers/hwmon/mc13783-adc.c @@ -0,0 +1,236 @@ +/* + * Driver for the Freescale Semiconductor MC13783 adc. + * + * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2009 Sascha Hauer, Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MC13783_ADC_NAME "mc13783-adc" + +struct mc13783_adc_priv { + struct mc13783 *mc13783; + struct device *hwmon_dev; +}; + +static ssize_t mc13783_adc_show_name(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + return sprintf(buf, "mc13783_adc\n"); +} + +static int mc13783_adc_read(struct device *dev, + struct device_attribute *devattr, unsigned int *val) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + unsigned int channel = attr->index; + unsigned int sample[4]; + int ret; + + ret = mc13783_adc_do_conversion(priv->mc13783, + MC13783_ADC_MODE_MULT_CHAN, + channel, sample); + if (ret) + return ret; + + channel &= 0x7; + + *val = (sample[channel % 4] >> (channel > 3 ? 14 : 2)) & 0x3ff; + + return 0; +} + +static ssize_t mc13783_adc_read_bp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + unsigned val; + int ret = mc13783_adc_read(dev, devattr, &val); + + if (ret) + return ret; + + /* + * BP (channel 2) reports with offset 2.4V to the actual value to fit + * the input range of the ADC. unit = 2.25mV = 9/4 mV. + */ + val = DIV_ROUND_CLOSEST(val * 9, 4) + 2400; + + return sprintf(buf, "%u\n", val); +} + +static ssize_t mc13783_adc_read_gp(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + unsigned val; + int ret = mc13783_adc_read(dev, devattr, &val); + + if (ret) + return ret; + + /* + * input range is [0, 2.3V], val has 10 bits, so each bit + * is worth 9/4 mV. + */ + val = DIV_ROUND_CLOSEST(val * 9, 4); + + return sprintf(buf, "%u\n", val); +} + +static DEVICE_ATTR(name, S_IRUGO, mc13783_adc_show_name, NULL); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, mc13783_adc_read_bp, NULL, 2); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, mc13783_adc_read_gp, NULL, 5); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, mc13783_adc_read_gp, NULL, 6); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, mc13783_adc_read_gp, NULL, 7); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, mc13783_adc_read_gp, NULL, 8); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, mc13783_adc_read_gp, NULL, 9); +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, mc13783_adc_read_gp, NULL, 10); +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, mc13783_adc_read_gp, NULL, 11); +static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, mc13783_adc_read_gp, NULL, 12); +static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, mc13783_adc_read_gp, NULL, 13); +static SENSOR_DEVICE_ATTR(in14_input, S_IRUGO, mc13783_adc_read_gp, NULL, 14); +static SENSOR_DEVICE_ATTR(in15_input, S_IRUGO, mc13783_adc_read_gp, NULL, 15); + +static struct attribute *mc13783_attr[] = { + &dev_attr_name.attr, + &sensor_dev_attr_in2_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, + NULL +}; + +static const struct attribute_group mc13783_group = { + .attrs = mc13783_attr, +}; + +/* last four channels may be occupied by the touchscreen */ +static struct attribute *mc13783_attr_ts[] = { + &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, + NULL +}; + +static const struct attribute_group mc13783_group_ts = { + .attrs = mc13783_attr_ts, +}; + +static int __init mc13783_adc_probe(struct platform_device *pdev) +{ + struct mc13783_adc_priv *priv; + int ret; + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->mc13783 = dev_get_drvdata(pdev->dev.parent); + + platform_set_drvdata(pdev, priv); + + /* Register sysfs hooks */ + ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group); + if (ret) + goto out_err_create1; + + if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN)) + ret = sysfs_create_group(&pdev->dev.kobj, &mc13783_group_ts); + if (ret) + goto out_err_create2; + + priv->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(priv->hwmon_dev)) { + ret = PTR_ERR(priv->hwmon_dev); + dev_err(&pdev->dev, + "hwmon_device_register failed with %d.\n", ret); + goto out_err_register; + } + + + return 0; + +out_err_register: + + if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN)) + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts); +out_err_create2: + + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group); +out_err_create1: + + platform_set_drvdata(pdev, NULL); + kfree(priv); + + return ret; +} + +static int __devexit mc13783_adc_remove(struct platform_device *pdev) +{ + struct mc13783_adc_priv *priv = platform_get_drvdata(pdev); + + hwmon_device_unregister(priv->hwmon_dev); + + if (!(priv->mc13783->flags & MC13783_USE_TOUCHSCREEN)) + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group_ts); + + sysfs_remove_group(&pdev->dev.kobj, &mc13783_group); + + platform_set_drvdata(pdev, NULL); + kfree(priv); + + return 0; +} + +static struct platform_driver mc13783_adc_driver = { + .remove = __devexit_p(mc13783_adc_remove), + .driver = { + .owner = THIS_MODULE, + .name = MC13783_ADC_NAME, + }, +}; + +static int __init mc13783_adc_init(void) +{ + return platform_driver_probe(&mc13783_adc_driver, mc13783_adc_probe); +} + +static void __exit mc13783_adc_exit(void) +{ + platform_driver_unregister(&mc13783_adc_driver); +} + +module_init(mc13783_adc_init); +module_exit(mc13783_adc_exit); + +MODULE_DESCRIPTION("MC13783 ADC driver"); +MODULE_AUTHOR("Luotao Fu "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" MC13783_ADC_NAME); -- cgit v1.2.3-70-g09d2 From 7669896f499e1bce5cfb38f2685ff583ecdb24dd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Dec 2009 20:36:01 +0100 Subject: hwmon: (f71882fg) Add support for the f71889fg (version 2) This adds support for the Fintek f71889fg to the f71882fg driver, many thanks to Gerd v. Egidy for providing (remote) access to a machine which such an ic. Note that this bit of the patch: - val = SENSORS_LIMIT(val, 0, 255); + + if (data->type == f71889fg) + val = SENSORS_LIMIT(val, -128, 127); + else + val = SENSORS_LIMIT(val, 0, 127); Changes behaviour for already supported models, the new behaviour is correct as the already supported models have bit 7 of the involved registers fixed at 0, so the previous behaviour which allowed setting temp zone limits > 127 was not correct. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare --- Documentation/hwmon/f71882fg | 10 ++++++ drivers/hwmon/Kconfig | 6 ++-- drivers/hwmon/f71882fg.c | 86 ++++++++++++++++++++++++++++++++++---------- 3 files changed, 81 insertions(+), 21 deletions(-) (limited to 'Documentation/hwmon') diff --git a/Documentation/hwmon/f71882fg b/Documentation/hwmon/f71882fg index bee4c30bc1e..a7952c2bd95 100644 --- a/Documentation/hwmon/f71882fg +++ b/Documentation/hwmon/f71882fg @@ -14,6 +14,10 @@ Supported chips: Prefix: 'f71882fg' Addresses scanned: none, address read from Super I/O config space Datasheet: Available from the Fintek website + * Fintek F71889FG + Prefix: 'f71889fg' + Addresses scanned: none, address read from Super I/O config space + Datasheet: Should become available on the Fintek website soon * Fintek F8000 Prefix: 'f8000' Addresses scanned: none, address read from Super I/O config space @@ -51,6 +55,12 @@ supported. The right one to use depends on external circuitry on the motherboard, so the driver assumes that the BIOS set the method properly. +Note that the lowest numbered temperature zone trip point corresponds to +to the border between the highest and one but highest temperature zones, and +vica versa. So the temperature zone trip points 1-4 (or 1-2) go from high temp +to low temp! This is how things are implemented in the IC, and the driver +mimicks this. + There are 2 modes to specify the speed of the fan, PWM duty cycle (or DC voltage) mode, where 0-100% duty cycle (0-100% of 12V) is specified. And RPM mode where the actual RPM of the fan (as measured) is controlled and the speed diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index dd6939370f7..edf8febb5bc 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -305,12 +305,12 @@ config SENSORS_F71805F will be called f71805f. config SENSORS_F71882FG - tristate "Fintek F71858FG, F71862FG, F71882FG and F8000" + tristate "Fintek F71858FG, F71862FG, F71882FG, F71889FG and F8000" depends on EXPERIMENTAL help If you say yes here you get support for hardware monitoring - features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG - and F8000 Super-I/O chips. + features of the Fintek F71858FG, F71862FG/71863FG, F71882FG/F71883FG, + F71889FG and F8000 Super-I/O chips. This driver can also be built as a module. If so, the module will be called f71882fg. diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 3a695f06905..a95fa4256ca 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -48,6 +48,7 @@ #define SIO_F71858_ID 0x0507 /* Chipset ID */ #define SIO_F71862_ID 0x0601 /* Chipset ID */ #define SIO_F71882_ID 0x0541 /* Chipset ID */ +#define SIO_F71889_ID 0x0723 /* Chipset ID */ #define SIO_F8000_ID 0x0581 /* Chipset ID */ #define REGION_LENGTH 8 @@ -95,12 +96,13 @@ static unsigned short force_id; module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); -enum chips { f71858fg, f71862fg, f71882fg, f8000 }; +enum chips { f71858fg, f71862fg, f71882fg, f71889fg, f8000 }; static const char *f71882fg_names[] = { "f71858fg", "f71862fg", "f71882fg", + "f71889fg", "f8000", }; @@ -155,7 +157,7 @@ struct f71882fg_data { u8 pwm_auto_point_hyst[2]; u8 pwm_auto_point_mapping[4]; u8 pwm_auto_point_pwm[4][5]; - u8 pwm_auto_point_temp[4][4]; + s8 pwm_auto_point_temp[4][4]; }; /* Sysfs in */ @@ -945,7 +947,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) /* Update once every 60 seconds */ if ( time_after(jiffies, data->last_limits + 60 * HZ ) || !data->valid) { - if (data->type == f71882fg) { + if (data->type == f71882fg || data->type == f71889fg) { data->in1_max = f71882fg_read8(data, F71882FG_REG_IN1_HIGH); data->in_beep = @@ -967,7 +969,8 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) F71882FG_REG_TEMP_HYST(1)); } - if (data->type == f71862fg || data->type == f71882fg) { + if (data->type == f71862fg || data->type == f71882fg || + data->type == f71889fg) { data->fan_beep = f71882fg_read8(data, F71882FG_REG_FAN_BEEP); data->temp_beep = f71882fg_read8(data, @@ -977,15 +980,33 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) data->temp_type[2] = (reg & 0x04) ? 2 : 4; data->temp_type[3] = (reg & 0x08) ? 2 : 4; } - reg2 = f71882fg_read8(data, F71882FG_REG_PECI); - if ((reg2 & 0x03) == 0x01) - data->temp_type[1] = 6 /* PECI */; - else if ((reg2 & 0x03) == 0x02) - data->temp_type[1] = 5 /* AMDSI */; - else if (data->type == f71862fg || data->type == f71882fg) - data->temp_type[1] = (reg & 0x02) ? 2 : 4; - else - data->temp_type[1] = 2; /* Only supports BJT */ + /* Determine temp index 1 sensor type */ + if (data->type == f71889fg) { + reg2 = f71882fg_read8(data, F71882FG_REG_START); + switch ((reg2 & 0x60) >> 5) { + case 0x00: /* BJT / Thermistor */ + data->temp_type[1] = (reg & 0x02) ? 2 : 4; + break; + case 0x01: /* AMDSI */ + data->temp_type[1] = 5; + break; + case 0x02: /* PECI */ + case 0x03: /* Ibex Peak ?? Report as PECI for now */ + data->temp_type[1] = 6; + break; + } + } else { + reg2 = f71882fg_read8(data, F71882FG_REG_PECI); + if ((reg2 & 0x03) == 0x01) + data->temp_type[1] = 6; /* PECI */ + else if ((reg2 & 0x03) == 0x02) + data->temp_type[1] = 5; /* AMDSI */ + else if (data->type == f71862fg || + data->type == f71882fg) + data->temp_type[1] = (reg & 0x02) ? 2 : 4; + else /* f71858fg and f8000 only support BJT */ + data->temp_type[1] = 2; + } data->pwm_enable = f71882fg_read8(data, F71882FG_REG_PWM_ENABLE); @@ -1062,7 +1083,7 @@ static struct f71882fg_data *f71882fg_update_device(struct device *dev) if (data->type == f8000) data->fan[3] = f71882fg_read16(data, F71882FG_REG_FAN(3)); - if (data->type == f71882fg) + if (data->type == f71882fg || data->type == f71889fg) data->in_status = f71882fg_read8(data, F71882FG_REG_IN_STATUS); for (nr = 0; nr < nr_ins; nr++) @@ -1780,7 +1801,11 @@ static ssize_t store_pwm_auto_point_temp(struct device *dev, int pwm = to_sensor_dev_attr_2(devattr)->index; int point = to_sensor_dev_attr_2(devattr)->nr; long val = simple_strtol(buf, NULL, 10) / 1000; - val = SENSORS_LIMIT(val, 0, 255); + + if (data->type == f71889fg) + val = SENSORS_LIMIT(val, -128, 127); + else + val = SENSORS_LIMIT(val, 0, 127); mutex_lock(&data->update_lock); f71882fg_write8(data, F71882FG_REG_POINT_TEMP(pwm, point), val); @@ -1871,6 +1896,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) ARRAY_SIZE(f71858fg_in_temp_attr)); break; case f71882fg: + case f71889fg: err = f71882fg_create_sysfs_files(pdev, fxxxx_in1_alarm_attr, ARRAY_SIZE(fxxxx_in1_alarm_attr)); @@ -1908,6 +1934,7 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) err = (data->pwm_enable & 0x15) != 0x15; break; case f71882fg: + case f71889fg: err = 0; break; case f8000: @@ -1927,7 +1954,8 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) if (err) goto exit_unregister_sysfs; - if (data->type == f71862fg || data->type == f71882fg) { + if (data->type == f71862fg || data->type == f71882fg || + data->type == f71889fg) { err = f71882fg_create_sysfs_files(pdev, fxxxx_fan_beep_attr, nr_fans); if (err) @@ -1950,6 +1978,22 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) f8000_auto_pwm_attr, ARRAY_SIZE(f8000_auto_pwm_attr)); break; + case f71889fg: + for (i = 0; i < nr_fans; i++) { + data->pwm_auto_point_mapping[i] = + f71882fg_read8(data, + F71882FG_REG_POINT_MAPPING(i)); + if (data->pwm_auto_point_mapping[i] & 0x80) + break; + } + if (i != nr_fans) { + dev_warn(&pdev->dev, + "Auto pwm controlled by raw digital " + "data, disabling pwm auto_point " + "sysfs attributes\n"); + break; + } + /* fall through */ default: /* f71858fg / f71882fg */ err = f71882fg_create_sysfs_files(pdev, &fxxxx_auto_pwm_attr[0][0], @@ -2006,6 +2050,7 @@ static int f71882fg_remove(struct platform_device *pdev) ARRAY_SIZE(f71858fg_in_temp_attr)); break; case f71882fg: + case f71889fg: f71882fg_remove_sysfs_files(pdev, fxxxx_in1_alarm_attr, ARRAY_SIZE(fxxxx_in1_alarm_attr)); @@ -2027,7 +2072,8 @@ static int f71882fg_remove(struct platform_device *pdev) f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0], ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); - if (data->type == f71862fg || data->type == f71882fg) + if (data->type == f71862fg || data->type == f71882fg || + data->type == f71889fg) f71882fg_remove_sysfs_files(pdev, fxxxx_fan_beep_attr, nr_fans); @@ -2082,11 +2128,15 @@ static int __init f71882fg_find(int sioaddr, unsigned short *address, case SIO_F71882_ID: sio_data->type = f71882fg; break; + case SIO_F71889_ID: + sio_data->type = f71889fg; + break; case SIO_F8000_ID: sio_data->type = f8000; break; default: - printk(KERN_INFO DRVNAME ": Unsupported Fintek device\n"); + printk(KERN_INFO DRVNAME ": Unsupported Fintek device: %04x\n", + (unsigned int)devid); goto exit; } -- cgit v1.2.3-70-g09d2 From b180d0508475c5c55085839d22f454c69379eacc Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:02 +0100 Subject: hwmon: (adt7475) Add support for the ADT7473 Add support for the ADT7473 to the adt7475 driver, and mark the adt7473 driver for removal. The ADT7473 and ADT7475 chips are almost the same chip and essentially compatible, so there's no point in having separate drivers for them. Signed-off-by: Jean Delvare Cc: "Mark M. Hoffman" Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" --- Documentation/feature-removal-schedule.txt | 7 +++++++ Documentation/hwmon/adt7473 | 2 ++ drivers/hwmon/Kconfig | 10 +++++++--- drivers/hwmon/adt7473.c | 3 ++- drivers/hwmon/adt7475.c | 25 +++++++++++++++++-------- 5 files changed, 35 insertions(+), 12 deletions(-) (limited to 'Documentation/hwmon') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 591e94448e6..2a4d77946c7 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -483,3 +483,10 @@ Why: With the recent innovations in CPU hardware acceleration technologies Who: Alok N Kataria ---------------------------- + +What: adt7473 hardware monitoring driver +When: February 2010 +Why: Obsoleted by the adt7475 driver. +Who: Jean Delvare + +--------------------------- diff --git a/Documentation/hwmon/adt7473 b/Documentation/hwmon/adt7473 index 1cbf671822e..446612bd1fb 100644 --- a/Documentation/hwmon/adt7473 +++ b/Documentation/hwmon/adt7473 @@ -9,6 +9,8 @@ Supported chips: Author: Darrick J. Wong +This driver is depreacted, please use the adt7475 driver instead. + Description ----------- diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index edf8febb5bc..5b2eaff4790 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -191,21 +191,25 @@ config SENSORS_ADT7470 will be called adt7470. config SENSORS_ADT7473 - tristate "Analog Devices ADT7473" + tristate "Analog Devices ADT7473 (DEPRECATED)" depends on I2C && EXPERIMENTAL + select SENSORS_ADT7475 help If you say yes here you get support for the Analog Devices ADT7473 temperature monitoring chips. + This driver is deprecated, you should use the adt7475 driver + instead. + This driver can also be built as a module. If so, the module will be called adt7473. config SENSORS_ADT7475 - tristate "Analog Devices ADT7475" + tristate "Analog Devices ADT7473 and ADT7475" depends on I2C && EXPERIMENTAL help If you say yes here you get support for the Analog Devices - ADT7475 hardware monitoring chips. + ADT7473 and ADT7475 hardware monitoring chips. This driver can also be build as a module. If so, the module will be called adt7475. diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c index 97ef50833f6..aea244db974 100644 --- a/drivers/hwmon/adt7473.c +++ b/drivers/hwmon/adt7473.c @@ -174,7 +174,6 @@ static const struct i2c_device_id adt7473_id[] = { { "adt7473", adt7473 }, { } }; -MODULE_DEVICE_TABLE(i2c, adt7473_id); static struct i2c_driver adt7473_driver = { .class = I2C_CLASS_HWMON, @@ -1166,6 +1165,8 @@ static int adt7473_remove(struct i2c_client *client) static int __init adt7473_init(void) { + pr_notice("The adt7473 driver is deprecated, please use the adt7475 " + "driver instead\n"); return i2c_add_driver(&adt7473_driver); } diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 41d3e38f9ce..fba2f016e4d 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -113,11 +113,12 @@ #define TEMP_OFFSET_REG(idx) (REG_TEMP_OFFSET_BASE + (idx)) #define TEMP_TRANGE_REG(idx) (REG_TEMP_TRANGE_BASE + (idx)) -static unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END }; +static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; -I2C_CLIENT_INSMOD_1(adt7475); +I2C_CLIENT_INSMOD_2(adt7473, adt7475); static const struct i2c_device_id adt7475_id[] = { + { "adt7473", adt7473 }, { "adt7475", adt7475 }, { } }; @@ -970,19 +971,27 @@ static int adt7475_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int vendid, devid; + const char *name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (adt7475_read(REG_VENDID) != 0x41 || - adt7475_read(REG_DEVID) != 0x75) { - dev_err(&adapter->dev, - "Couldn't detect a adt7475 part at 0x%02x\n", - (unsigned int)client->addr); + vendid = adt7475_read(REG_VENDID); + devid = adt7475_read(REG_DEVID); + + if (vendid == 0x41 && devid == 0x73) + name = "adt7473"; + else if (vendid == 0x41 && devid == 0x75 && client->addr == 0x2e) + name = "adt7475"; + else { + dev_dbg(&adapter->dev, + "Couldn't detect an ADT7473 or ADT7475 part at " + "0x%02x\n", (unsigned int)client->addr); return -ENODEV; } - strlcpy(info->type, adt7475_id[0].name, I2C_NAME_SIZE); + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } -- cgit v1.2.3-70-g09d2 From f890c6a3b6c3ed06719e696fed9267cc6b40aabd Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:02 +0100 Subject: hwmon: (adt7475) New documentation New documentation for the adt7475 driver, based on the adt7473 driver documentation. It is IMHO much more useful that the previous documentation which was essentially redundant with sysfs-interface. Signed-off-by: Jean Delvare Cc: "Mark M. Hoffman" Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" --- Documentation/hwmon/adt7475 | 170 +++++++++++++++++++++----------------------- 1 file changed, 83 insertions(+), 87 deletions(-) (limited to 'Documentation/hwmon') diff --git a/Documentation/hwmon/adt7475 b/Documentation/hwmon/adt7475 index a2b1abec850..ebfee552402 100644 --- a/Documentation/hwmon/adt7475 +++ b/Documentation/hwmon/adt7475 @@ -1,87 +1,83 @@ -This describes the interface for the ADT7475 driver: - -(there are 4 fans, numbered fan1 to fan4): - -fanX_input Read the current speed of the fan (in RPMs) -fanX_min Read/write the minimum speed of the fan. Dropping - below this sets an alarm. - -(there are three PWMs, numbered pwm1 to pwm3): - -pwmX Read/write the current duty cycle of the PWM. Writes - only have effect when auto mode is turned off (see - below). Range is 0 - 255. - -pwmX_enable Fan speed control method: - - 0 - No control (fan at full speed) - 1 - Manual fan speed control (using pwm[1-*]) - 2 - Automatic fan speed control - -pwmX_auto_channels_temp Select which channels affect this PWM - - 1 - TEMP1 controls PWM - 2 - TEMP2 controls PWM - 4 - TEMP3 controls PWM - 6 - TEMP2 and TEMP3 control PWM - 7 - All three inputs control PWM - -pwmX_freq Read/write the PWM frequency in Hz. The number - should be one of the following: - - 11 Hz - 14 Hz - 22 Hz - 29 Hz - 35 Hz - 44 Hz - 58 Hz - 88 Hz - -pwmX_auto_point1_pwm Read/write the minimum PWM duty cycle in automatic mode - -pwmX_auto_point2_pwm Read/write the maximum PWM duty cycle in automatic mode - -(there are three temperature settings numbered temp1 to temp3): - -tempX_input Read the current temperature. The value is in milli - degrees of Celsius. - -tempX_max Read/write the upper temperature limit - exceeding this - will cause an alarm. - -tempX_min Read/write the lower temperature limit - exceeding this - will cause an alarm. - -tempX_offset Read/write the temperature adjustment offset - -tempX_crit Read/write the THERM limit for remote1. - -tempX_crit_hyst Set the temperature value below crit where the - fans will stay on - this helps drive the temperature - low enough so it doesn't stay near the edge and - cause THERM to keep tripping. - -tempX_auto_point1_temp Read/write the minimum temperature where the fans will - turn on in automatic mode. - -tempX_auto_point2_temp Read/write the maximum temperature over which the fans - will run in automatic mode. tempX_auto_point1_temp - and tempX_auto_point2_temp together define the - range of automatic control. - -tempX_alarm Read a 1 if the max/min alarm is set -tempX_fault Read a 1 if either temp1 or temp3 diode has a fault - -(There are two voltage settings, in1 and in2): - -inX_input Read the current voltage on VCC. Value is in - millivolts. - -inX_min read/write the minimum voltage limit. - Dropping below this causes an alarm. - -inX_max read/write the maximum voltage limit. - Exceeding this causes an alarm. - -inX_alarm Read a 1 if the max/min alarm is set. +Kernel driver adt7475 +===================== + +Supported chips: + * Analog Devices ADT7473 + Prefix: 'adt7473' + Addresses scanned: I2C 0x2C, 0x2D, 0x2E + Datasheet: Publicly available at the On Semiconductors website + * Analog Devices ADT7475 + Prefix: 'adt7475' + Addresses scanned: I2C 0x2E + Datasheet: Publicly available at the On Semiconductors website + +Authors: + Jordan Crouse + Hans de Goede + Darrick J. Wong (documentation) + + +Description +----------- + +This driver implements support for the Analog Devices ADT7473 and ADT7475 +chip family. Both chips differ only in minor details. They will be +collectively designed by the name "ADT747x" in the rest of this document. + +The ADT747x uses the 2-wire interface compatible with the SMBus 2.0 +specification. Using an analog to digital converter it measures three (3) +temperatures and two (2) voltages. It has four (4) 16-bit counters for +measuring fan speed. There are three (3) PWM outputs that can be used +to control fan speed. + +A sophisticated control system for the PWM outputs is designed into the +ADT747x that allows fan speed to be adjusted automatically based on any of the +three temperature sensors. Each PWM output is individually adjustable and +programmable. Once configured, the ADT747x will adjust the PWM outputs in +response to the measured temperatures without further host intervention. +This feature can also be disabled for manual control of the PWM's. + +Each of the measured inputs (voltage, temperature, fan speed) has +corresponding high/low limit values. The ADT747x will signal an ALARM if +any measured value exceeds either limit. + +The ADT747x samples all inputs continuously. The driver will not read +the registers more often than once every other second. Further, +configuration data is only read once per minute. + +Special Features +---------------- + +The ADT747x has a 10-bit ADC and can therefore measure temperatures +with a resolution of 0.25 degree Celsius. Temperature readings can be +configured either for two's complement format or "Offset 64" format, +wherein 64 is subtracted from the raw value to get the temperature value. + +The datasheet is very detailed and describes a procedure for determining +an optimal configuration for the automatic PWM control. + +Fan Speed Control +----------------- + +The driver exposes two trip points per PWM channel. + +point1: Set the PWM speed at the lower temperature bound +point2: Set the PWM speed at the higher temperature bound + +The ADT747x will scale the PWM linearly between the lower and higher PWM +speed when the temperature is between the two temperature boundaries. +Temperature boundaries are associated to temperature channels rather than +PWM outputs, and a given PWM output can be controlled by several temperature +channels. As a result, the ADT747x may compute more than one PWM value +for a channel at a given time, in which case the maximum value (fastest +fan speed) is applied. PWM values range from 0 (off) to 255 (full speed). + +Fan speed may be set to maximum when the temperature sensor associated with +the PWM control exceeds temp#_max. + +Notes +----- + +The nVidia binary driver presents an ADT7473 chip via an on-card i2c bus. +Unfortunately, they fail to set the i2c adapter class, so this driver may +fail to find the chip until the nvidia driver is patched. -- cgit v1.2.3-70-g09d2 From 3d849981711741e76e501e4b9e3e7b792f2b0fd5 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:05 +0100 Subject: hwmon: (adt7475) Add support for the ADT7490 Add support for the Analog Devices ADT7490 chip. This chip is largely compatible with the ADT7473 and ADT7475, with additional features. In particular, it has 6 voltage inputs instead of 2. Signed-off-by: Jean Delvare Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" --- Documentation/hwmon/adt7475 | 34 +++++++++++-- drivers/hwmon/Kconfig | 4 +- drivers/hwmon/adt7475.c | 115 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 134 insertions(+), 19 deletions(-) (limited to 'Documentation/hwmon') diff --git a/Documentation/hwmon/adt7475 b/Documentation/hwmon/adt7475 index ebfee552402..f08f28715b8 100644 --- a/Documentation/hwmon/adt7475 +++ b/Documentation/hwmon/adt7475 @@ -10,24 +10,31 @@ Supported chips: Prefix: 'adt7475' Addresses scanned: I2C 0x2E Datasheet: Publicly available at the On Semiconductors website + * Analog Devices ADT7490 + Prefix: 'adt7490' + Addresses scanned: I2C 0x2C, 0x2D, 0x2E + Datasheet: Publicly available at the On Semiconductors website Authors: Jordan Crouse Hans de Goede Darrick J. Wong (documentation) + Jean Delvare Description ----------- -This driver implements support for the Analog Devices ADT7473 and ADT7475 -chip family. Both chips differ only in minor details. They will be -collectively designed by the name "ADT747x" in the rest of this document. +This driver implements support for the Analog Devices ADT7473, ADT7475 and +ADT7490 chip family. The ADT7473 and ADT7475 differ only in minor details. +The ADT7490 has additional features, including extra voltage measurement +inputs and PECI support. All the supported chips will be collectively +designed by the name "ADT747x" in the rest of this document. The ADT747x uses the 2-wire interface compatible with the SMBus 2.0 specification. Using an analog to digital converter it measures three (3) -temperatures and two (2) voltages. It has four (4) 16-bit counters for -measuring fan speed. There are three (3) PWM outputs that can be used +temperatures and two (2) or more voltages. It has four (4) 16-bit counters +for measuring fan speed. There are three (3) PWM outputs that can be used to control fan speed. A sophisticated control system for the PWM outputs is designed into the @@ -45,6 +52,23 @@ The ADT747x samples all inputs continuously. The driver will not read the registers more often than once every other second. Further, configuration data is only read once per minute. +Chip Differences Summary +------------------------ + +ADT7473: + * 2 voltage inputs + * system acoustics optimizations (not implemented) + +ADT7475: + * 2 voltage inputs + +ADT7490: + * 6 voltage inputs + * 1 Imon input (not implemented) + * PECI support (not implemented) + * 2 GPIO pins (not implemented) + * system acoustics optimizations (not implemented) + Special Features ---------------- diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 5b2eaff4790..f9f4365349b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -205,11 +205,11 @@ config SENSORS_ADT7473 will be called adt7473. config SENSORS_ADT7475 - tristate "Analog Devices ADT7473 and ADT7475" + tristate "Analog Devices ADT7473, ADT7475 and ADT7490" depends on I2C && EXPERIMENTAL help If you say yes here you get support for the Analog Devices - ADT7473 and ADT7475 hardware monitoring chips. + ADT7473, ADT7475 and ADT7490 hardware monitoring chips. This driver can also be build as a module. If so, the module will be called adt7475. diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 520773b1654..e495665569e 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -3,7 +3,8 @@ * Copyright (C) 2007-2008, Advanced Micro Devices, Inc. * Copyright (C) 2008 Jordan Crouse * Copyright (C) 2008 Hans de Goede - + * Copyright (C) 2009 Jean Delvare + * * Derived from the lm83 driver by Jean Delvare * * This program is free software; you can redistribute it and/or modify @@ -39,6 +40,9 @@ /* 7475 Common Registers */ +#define REG_VTT 0x1E /* ADT7490 only */ +#define REG_EXTEND3 0x1F /* ADT7490 only */ + #define REG_VOLTAGE_BASE 0x20 #define REG_TEMP_BASE 0x25 #define REG_TACH_BASE 0x28 @@ -79,6 +83,11 @@ #define REG_CONFIG5 0x7C #define REG_CONFIG4 0x7D +#define REG_STATUS4 0x81 /* ADT7490 only */ + +#define REG_VTT_MIN 0x84 /* ADT7490 only */ +#define REG_VTT_MAX 0x86 /* ADT7490 only */ + #define CONFIG4_MAXDUTY 0x08 #define CONFIG5_TWOSCOMP 0x01 @@ -119,11 +128,12 @@ static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; -I2C_CLIENT_INSMOD_2(adt7473, adt7475); +I2C_CLIENT_INSMOD_3(adt7473, adt7475, adt7490); static const struct i2c_device_id adt7475_id[] = { { "adt7473", adt7473 }, { "adt7475", adt7475 }, + { "adt7490", adt7490 }, { } }; MODULE_DEVICE_TABLE(i2c, adt7475_id); @@ -139,7 +149,7 @@ struct adt7475_data { u8 config4; u8 config5; u8 has_voltage; - u16 alarms; + u32 alarms; u16 voltage[3][6]; u16 temp[7][3]; u16 tach[2][4]; @@ -306,10 +316,17 @@ static ssize_t set_voltage(struct device *dev, struct device_attribute *attr, data->voltage[sattr->nr][sattr->index] = volt2reg(sattr->index, val); - if (sattr->nr == MIN) - reg = VOLTAGE_MIN_REG(sattr->index); - else - reg = VOLTAGE_MAX_REG(sattr->index); + if (sattr->index < ADT7475_VOLTAGE_COUNT) { + if (sattr->nr == MIN) + reg = VOLTAGE_MIN_REG(sattr->index); + else + reg = VOLTAGE_MAX_REG(sattr->index); + } else { + if (sattr->nr == MIN) + reg = REG_VTT_MIN; + else + reg = REG_VTT_MAX; + } i2c_smbus_write_byte_data(client, reg, data->voltage[sattr->nr][sattr->index] >> 2); @@ -818,6 +835,12 @@ static ssize_t set_pwm_at_crit(struct device *dev, return count; } +static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_voltage, NULL, INPUT, 0); +static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MAX, 0); +static SENSOR_DEVICE_ATTR_2(in0_min, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MIN, 0); +static SENSOR_DEVICE_ATTR_2(in0_alarm, S_IRUGO, show_voltage, NULL, ALARM, 0); static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_voltage, NULL, INPUT, 1); static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_voltage, set_voltage, MAX, 1); @@ -830,6 +853,24 @@ static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_voltage, static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_voltage, set_voltage, MIN, 2); static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_voltage, NULL, ALARM, 2); +static SENSOR_DEVICE_ATTR_2(in3_input, S_IRUGO, show_voltage, NULL, INPUT, 3); +static SENSOR_DEVICE_ATTR_2(in3_max, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MAX, 3); +static SENSOR_DEVICE_ATTR_2(in3_min, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MIN, 3); +static SENSOR_DEVICE_ATTR_2(in3_alarm, S_IRUGO, show_voltage, NULL, ALARM, 3); +static SENSOR_DEVICE_ATTR_2(in4_input, S_IRUGO, show_voltage, NULL, INPUT, 4); +static SENSOR_DEVICE_ATTR_2(in4_max, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MAX, 4); +static SENSOR_DEVICE_ATTR_2(in4_min, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MIN, 4); +static SENSOR_DEVICE_ATTR_2(in4_alarm, S_IRUGO, show_voltage, NULL, ALARM, 8); +static SENSOR_DEVICE_ATTR_2(in5_input, S_IRUGO, show_voltage, NULL, INPUT, 5); +static SENSOR_DEVICE_ATTR_2(in5_max, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MAX, 5); +static SENSOR_DEVICE_ATTR_2(in5_min, S_IRUGO | S_IWUSR, show_voltage, + set_voltage, MIN, 5); +static SENSOR_DEVICE_ATTR_2(in5_alarm, S_IRUGO, show_voltage, NULL, ALARM, 31); static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, INPUT, 0); static SENSOR_DEVICE_ATTR_2(temp1_alarm, S_IRUGO, show_temp, NULL, ALARM, 0); static SENSOR_DEVICE_ATTR_2(temp1_fault, S_IRUGO, show_temp, NULL, FAULT, 0); @@ -1009,7 +1050,29 @@ static struct attribute *adt7475_attrs[] = { NULL, }; +/* Attributes specific to the ADT7490 */ +static struct attribute *adt7490_attrs[] = { + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, + &sensor_dev_attr_in0_alarm.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + NULL +}; + static struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs }; +static struct attribute_group adt7490_attr_group = { .attrs = adt7490_attrs }; static int adt7475_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) @@ -1032,9 +1095,11 @@ static int adt7475_detect(struct i2c_client *client, int kind, name = "adt7473"; else if (devid == 0x75 && client->addr == 0x2e) name = "adt7475"; + else if ((devid2 & 0xfc) == 0x6c) + name = "adt7490"; else { dev_dbg(&adapter->dev, - "Couldn't detect an ADT7473 or ADT7475 part at " + "Couldn't detect an ADT7473/75/90 part at " "0x%02x\n", (unsigned int)client->addr); return -ENODEV; } @@ -1059,6 +1124,9 @@ static int adt7475_probe(struct i2c_client *client, /* Initialize device-specific values */ switch (id->driver_data) { + case adt7490: + data->has_voltage = 0x3f; /* in0 to in5 */ + break; default: data->has_voltage = 0x06; /* in1, in2 */ } @@ -1072,6 +1140,13 @@ static int adt7475_probe(struct i2c_client *client, if (ret) goto efree; + if (id->driver_data == adt7490) { + ret = sysfs_create_group(&client->dev.kobj, + &adt7490_attr_group); + if (ret) + goto eremove; + } + data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { ret = PTR_ERR(data->hwmon_dev); @@ -1082,6 +1157,8 @@ static int adt7475_probe(struct i2c_client *client, eremove: sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group); + if (data->has_voltage & 0x39) + sysfs_remove_group(&client->dev.kobj, &adt7490_attr_group); efree: kfree(data); return ret; @@ -1093,6 +1170,8 @@ static int adt7475_remove(struct i2c_client *client) hwmon_device_unregister(data->hwmon_dev); sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group); + if (data->has_voltage & 0x39) + sysfs_remove_group(&client->dev.kobj, &adt7490_attr_group); kfree(data); return 0; @@ -1177,7 +1256,7 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); struct adt7475_data *data = i2c_get_clientdata(client); - u8 ext; + u16 ext; int i; mutex_lock(&data->lock); @@ -1188,7 +1267,8 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) data->alarms = adt7475_read(REG_STATUS2) << 8; data->alarms |= adt7475_read(REG_STATUS1); - ext = adt7475_read(REG_EXTEND1); + ext = (adt7475_read(REG_EXTEND2) << 8) | + adt7475_read(REG_EXTEND1); for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) { if (!(data->has_voltage & (1 << i))) continue; @@ -1197,11 +1277,17 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) ((ext >> (i * 2)) & 3); } - ext = adt7475_read(REG_EXTEND2); for (i = 0; i < ADT7475_TEMP_COUNT; i++) data->temp[INPUT][i] = (adt7475_read(TEMP_REG(i)) << 2) | - ((ext >> ((i + 1) * 2)) & 3); + ((ext >> ((i + 5) * 2)) & 3); + + if (data->has_voltage & (1 << 5)) { + data->alarms |= adt7475_read(REG_STATUS4) << 24; + ext = adt7475_read(REG_EXTEND3); + data->voltage[INPUT][5] = adt7475_read(REG_VTT) << 2 | + ((ext >> 4) & 3); + } for (i = 0; i < ADT7475_TACH_COUNT; i++) data->tach[INPUT][i] = @@ -1230,6 +1316,11 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) adt7475_read(VOLTAGE_MAX_REG(i)) << 2; } + if (data->has_voltage & (1 << 5)) { + data->voltage[MIN][5] = adt7475_read(REG_VTT_MIN) << 2; + data->voltage[MAX][5] = adt7475_read(REG_VTT_MAX) << 2; + } + for (i = 0; i < ADT7475_TEMP_COUNT; i++) { /* Adjust values so they match the input precision */ data->temp[MIN][i] = -- cgit v1.2.3-70-g09d2 From d8d2ee073274ab666282d1942d08f1bb454d715b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:08 +0100 Subject: hwmon: (adt7475) Add support for the ADT7476 Add support for the Analog Devices ADT7476 chip. This chip is largely compatible with the ADT7473 and ADT7475, with additional features. In particular, it has 5 voltage inputs instead of 2, and VID input pins. Signed-off-by: Jean Delvare Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" --- Documentation/hwmon/adt7475 | 20 +++++++++--- drivers/hwmon/Kconfig | 5 +-- drivers/hwmon/adt7475.c | 80 +++++++++++++++++++++++++++++++++++---------- 3 files changed, 80 insertions(+), 25 deletions(-) (limited to 'Documentation/hwmon') diff --git a/Documentation/hwmon/adt7475 b/Documentation/hwmon/adt7475 index f08f28715b8..03be1e6f69c 100644 --- a/Documentation/hwmon/adt7475 +++ b/Documentation/hwmon/adt7475 @@ -10,6 +10,10 @@ Supported chips: Prefix: 'adt7475' Addresses scanned: I2C 0x2E Datasheet: Publicly available at the On Semiconductors website + * Analog Devices ADT7476 + Prefix: 'adt7476' + Addresses scanned: I2C 0x2C, 0x2D, 0x2E + Datasheet: Publicly available at the On Semiconductors website * Analog Devices ADT7490 Prefix: 'adt7490' Addresses scanned: I2C 0x2C, 0x2D, 0x2E @@ -25,11 +29,13 @@ Authors: Description ----------- -This driver implements support for the Analog Devices ADT7473, ADT7475 and -ADT7490 chip family. The ADT7473 and ADT7475 differ only in minor details. -The ADT7490 has additional features, including extra voltage measurement -inputs and PECI support. All the supported chips will be collectively -designed by the name "ADT747x" in the rest of this document. +This driver implements support for the Analog Devices ADT7473, ADT7475, +ADT7476 and ADT7490 chip family. The ADT7473 and ADT7475 differ only in +minor details. The ADT7476 has additional features, including extra voltage +measurement inputs and VID support. The ADT7490 also has additional +features, including extra voltage measurement inputs and PECI support. All +the supported chips will be collectively designed by the name "ADT747x" in +the rest of this document. The ADT747x uses the 2-wire interface compatible with the SMBus 2.0 specification. Using an analog to digital converter it measures three (3) @@ -62,6 +68,10 @@ ADT7473: ADT7475: * 2 voltage inputs +ADT7476: + * 5 voltage inputs + * VID support (not implemented) + ADT7490: * 6 voltage inputs * 1 Imon input (not implemented) diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index f9f4365349b..957171c6232 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -205,11 +205,12 @@ config SENSORS_ADT7473 will be called adt7473. config SENSORS_ADT7475 - tristate "Analog Devices ADT7473, ADT7475 and ADT7490" + tristate "Analog Devices ADT7473, ADT7475, ADT7476 and ADT7490" depends on I2C && EXPERIMENTAL help If you say yes here you get support for the Analog Devices - ADT7473, ADT7475 and ADT7490 hardware monitoring chips. + ADT7473, ADT7475, ADT7476 and ADT7490 hardware monitoring + chips. This driver can also be build as a module. If so, the module will be called adt7475. diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index eac24c1361b..72c3b754e7b 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -58,6 +58,8 @@ #define REG_STATUS1 0x41 #define REG_STATUS2 0x42 +#define REG_VID 0x43 /* ADT7476 only */ + #define REG_VOLTAGE_MIN_BASE 0x44 #define REG_VOLTAGE_MAX_BASE 0x45 @@ -94,6 +96,8 @@ #define REG_VTT_MIN 0x84 /* ADT7490 only */ #define REG_VTT_MAX 0x86 /* ADT7490 only */ +#define VID_VIDSEL 0x80 /* ADT7476 only */ + #define CONFIG2_ATTN 0x20 #define CONFIG3_SMBALERT 0x01 @@ -142,11 +146,12 @@ static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; -I2C_CLIENT_INSMOD_3(adt7473, adt7475, adt7490); +I2C_CLIENT_INSMOD_4(adt7473, adt7475, adt7476, adt7490); static const struct i2c_device_id adt7475_id[] = { { "adt7473", adt7473 }, { "adt7475", adt7475 }, + { "adt7476", adt7476 }, { "adt7490", adt7490 }, { } }; @@ -1082,7 +1087,6 @@ static struct attribute *pwm2_attrs[] = { NULL }; -/* Attributes specific to the ADT7490 */ static struct attribute *in0_attrs[] = { &sensor_dev_attr_in0_input.dev_attr.attr, &sensor_dev_attr_in0_max.dev_attr.attr, @@ -1091,15 +1095,23 @@ static struct attribute *in0_attrs[] = { NULL }; -static struct attribute *adt7490_attrs[] = { +static struct attribute *in3_attrs[] = { &sensor_dev_attr_in3_input.dev_attr.attr, &sensor_dev_attr_in3_max.dev_attr.attr, &sensor_dev_attr_in3_min.dev_attr.attr, &sensor_dev_attr_in3_alarm.dev_attr.attr, + NULL +}; + +static struct attribute *in4_attrs[] = { &sensor_dev_attr_in4_input.dev_attr.attr, &sensor_dev_attr_in4_max.dev_attr.attr, &sensor_dev_attr_in4_min.dev_attr.attr, &sensor_dev_attr_in4_alarm.dev_attr.attr, + NULL +}; + +static struct attribute *in5_attrs[] = { &sensor_dev_attr_in5_input.dev_attr.attr, &sensor_dev_attr_in5_max.dev_attr.attr, &sensor_dev_attr_in5_min.dev_attr.attr, @@ -1111,7 +1123,9 @@ static struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs }; static struct attribute_group fan4_attr_group = { .attrs = fan4_attrs }; static struct attribute_group pwm2_attr_group = { .attrs = pwm2_attrs }; static struct attribute_group in0_attr_group = { .attrs = in0_attrs }; -static struct attribute_group adt7490_attr_group = { .attrs = adt7490_attrs }; +static struct attribute_group in3_attr_group = { .attrs = in3_attrs }; +static struct attribute_group in4_attr_group = { .attrs = in4_attrs }; +static struct attribute_group in5_attr_group = { .attrs = in5_attrs }; static int adt7475_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) @@ -1134,11 +1148,13 @@ static int adt7475_detect(struct i2c_client *client, int kind, name = "adt7473"; else if (devid == 0x75 && client->addr == 0x2e) name = "adt7475"; + else if (devid == 0x76) + name = "adt7476"; else if ((devid2 & 0xfc) == 0x6c) name = "adt7490"; else { dev_dbg(&adapter->dev, - "Couldn't detect an ADT7473/75/90 part at " + "Couldn't detect an ADT7473/75/76/90 part at " "0x%02x\n", (unsigned int)client->addr); return -ENODEV; } @@ -1152,14 +1168,18 @@ static void adt7475_remove_files(struct i2c_client *client, struct adt7475_data *data) { sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group); - if (data->has_voltage & 0x39) - sysfs_remove_group(&client->dev.kobj, &adt7490_attr_group); if (data->has_fan4) sysfs_remove_group(&client->dev.kobj, &fan4_attr_group); if (data->has_pwm2) sysfs_remove_group(&client->dev.kobj, &pwm2_attr_group); if (data->has_voltage & (1 << 0)) sysfs_remove_group(&client->dev.kobj, &in0_attr_group); + if (data->has_voltage & (1 << 3)) + sysfs_remove_group(&client->dev.kobj, &in3_attr_group); + if (data->has_voltage & (1 << 4)) + sysfs_remove_group(&client->dev.kobj, &in4_attr_group); + if (data->has_voltage & (1 << 5)) + sysfs_remove_group(&client->dev.kobj, &in5_attr_group); } static int adt7475_probe(struct i2c_client *client, @@ -1168,6 +1188,7 @@ static int adt7475_probe(struct i2c_client *client, static const char *names[] = { [adt7473] = "ADT7473", [adt7475] = "ADT7475", + [adt7476] = "ADT7476", [adt7490] = "ADT7490", }; @@ -1184,6 +1205,10 @@ static int adt7475_probe(struct i2c_client *client, /* Initialize device-specific values */ switch (id->driver_data) { + case adt7476: + data->has_voltage = 0x0e; /* in1 to in3 */ + revision = adt7475_read(REG_DEVID2) & 0x07; + break; case adt7490: data->has_voltage = 0x3e; /* in1 to in5 */ revision = adt7475_read(REG_DEVID2) & 0x03; @@ -1208,17 +1233,27 @@ static int adt7475_probe(struct i2c_client *client, if ((data->config4 & CONFIG4_PINFUNC) == 0x0) data->has_fan4 = 1; - /* THERM configuration is more complex on the ADT7490, because 2 - different pins (TACH4 and +2.5 Vin) can be used for this function */ + /* THERM configuration is more complex on the ADT7476 and ADT7490, + because 2 different pins (TACH4 and +2.5 Vin) can be used for + this function */ if (id->driver_data == adt7490) { if ((data->config4 & CONFIG4_PINFUNC) == 0x1 && !(config3 & CONFIG3_THERM)) data->has_fan4 = 1; + } + if (id->driver_data == adt7476 || id->driver_data == adt7490) { if (!(config3 & CONFIG3_THERM) || (data->config4 & CONFIG4_PINFUNC) == 0x1) data->has_voltage |= (1 << 0); /* in0 */ } + /* On the ADT7476, the +12V input pin may instead be used as VID5 */ + if (id->driver_data == adt7476) { + u8 vid = adt7475_read(REG_VID); + if (!(vid & VID_VIDSEL)) + data->has_voltage |= (1 << 4); /* in4 */ + } + /* Voltage attenuators can be bypassed, globally or individually */ config2 = adt7475_read(REG_CONFIG2); if (config2 & CONFIG2_ATTN) { @@ -1238,13 +1273,6 @@ static int adt7475_probe(struct i2c_client *client, if (ret) goto efree; - if (id->driver_data == adt7490) { - ret = sysfs_create_group(&client->dev.kobj, - &adt7490_attr_group); - if (ret) - goto eremove; - } - /* Features that can be disabled individually */ if (data->has_fan4) { ret = sysfs_create_group(&client->dev.kobj, &fan4_attr_group); @@ -1261,6 +1289,21 @@ static int adt7475_probe(struct i2c_client *client, if (ret) goto eremove; } + if (data->has_voltage & (1 << 3)) { + ret = sysfs_create_group(&client->dev.kobj, &in3_attr_group); + if (ret) + goto eremove; + } + if (data->has_voltage & (1 << 4)) { + ret = sysfs_create_group(&client->dev.kobj, &in4_attr_group); + if (ret) + goto eremove; + } + if (data->has_voltage & (1 << 5)) { + ret = sysfs_create_group(&client->dev.kobj, &in5_attr_group); + if (ret) + goto eremove; + } data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -1270,9 +1313,10 @@ static int adt7475_probe(struct i2c_client *client, dev_info(&client->dev, "%s device, revision %d\n", names[id->driver_data], revision); - if ((data->has_voltage & (1 << 0)) || data->has_fan4 || data->has_pwm2) - dev_info(&client->dev, "Optional features:%s%s%s\n", + if ((data->has_voltage & 0x11) || data->has_fan4 || data->has_pwm2) + dev_info(&client->dev, "Optional features:%s%s%s%s\n", (data->has_voltage & (1 << 0)) ? " in0" : "", + (data->has_voltage & (1 << 4)) ? " in4" : "", data->has_fan4 ? " fan4" : "", data->has_pwm2 ? " pwm2" : ""); if (data->bypass_attn) -- cgit v1.2.3-70-g09d2 From 54fe4671aa5853ca88da72d67e969a3d8de6dcf6 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:08 +0100 Subject: hwmon: (adt7475) Add VID support for the ADT7476 The ADT7476 has 5 dedicated pins for VID input, and the +12V input can optionally be used as a 6th VID pin. Add support for VID input. Signed-off-by: Jean Delvare Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" --- Documentation/hwmon/adt7475 | 2 +- drivers/hwmon/Kconfig | 1 + drivers/hwmon/adt7475.c | 66 ++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 65 insertions(+), 4 deletions(-) (limited to 'Documentation/hwmon') diff --git a/Documentation/hwmon/adt7475 b/Documentation/hwmon/adt7475 index 03be1e6f69c..0502f2b464e 100644 --- a/Documentation/hwmon/adt7475 +++ b/Documentation/hwmon/adt7475 @@ -70,7 +70,7 @@ ADT7475: ADT7476: * 5 voltage inputs - * VID support (not implemented) + * VID support ADT7490: * 6 voltage inputs diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 957171c6232..9e640c62ebd 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -207,6 +207,7 @@ config SENSORS_ADT7473 config SENSORS_ADT7475 tristate "Analog Devices ADT7473, ADT7475, ADT7476 and ADT7490" depends on I2C && EXPERIMENTAL + select HWMON_VID help If you say yes here you get support for the Analog Devices ADT7473, ADT7475, ADT7476 and ADT7490 hardware monitoring diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 72c3b754e7b..99abfddedbc 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -18,6 +18,7 @@ #include #include #include +#include #include /* Indexes for the sysfs hooks */ @@ -110,6 +111,7 @@ #define CONFIG5_TWOSCOMP 0x01 #define CONFIG5_TEMPOFFSET 0x02 +#define CONFIG5_VIDGPIO 0x10 /* ADT7476 only */ /* ADT7475 Settings */ @@ -171,6 +173,7 @@ struct adt7475_data { u8 bypass_attn; /* Bypass voltage attenuator */ u8 has_pwm2:1; u8 has_fan4:1; + u8 has_vid:1; u32 alarms; u16 voltage[3][6]; u16 temp[7][3]; @@ -179,6 +182,9 @@ struct adt7475_data { u8 range[3]; u8 pwmctl[3]; u8 pwmchan[3]; + + u8 vid; + u8 vrm; }; static struct i2c_driver adt7475_driver; @@ -864,6 +870,35 @@ static ssize_t set_pwm_at_crit(struct device *dev, return count; } +static ssize_t show_vrm(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct adt7475_data *data = dev_get_drvdata(dev); + return sprintf(buf, "%d\n", (int)data->vrm); +} + +static ssize_t set_vrm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct adt7475_data *data = dev_get_drvdata(dev); + long val; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + if (val < 0 || val > 255) + return -EINVAL; + data->vrm = val; + + return count; +} + +static ssize_t show_vid(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct adt7475_data *data = adt7475_update_device(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); +} + static SENSOR_DEVICE_ATTR_2(in0_input, S_IRUGO, show_voltage, NULL, INPUT, 0); static SENSOR_DEVICE_ATTR_2(in0_max, S_IRUGO | S_IWUSR, show_voltage, set_voltage, MAX, 0); @@ -1007,6 +1042,9 @@ static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO | S_IWUSR, show_pwm, static DEVICE_ATTR(pwm_use_point2_pwm_at_crit, S_IWUSR | S_IRUGO, show_pwm_at_crit, set_pwm_at_crit); +static DEVICE_ATTR(vrm, S_IWUSR | S_IRUGO, show_vrm, set_vrm); +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + static struct attribute *adt7475_attrs[] = { &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_max.dev_attr.attr, @@ -1119,6 +1157,12 @@ static struct attribute *in5_attrs[] = { NULL }; +static struct attribute *vid_attrs[] = { + &dev_attr_cpu0_vid.attr, + &dev_attr_vrm.attr, + NULL +}; + static struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs }; static struct attribute_group fan4_attr_group = { .attrs = fan4_attrs }; static struct attribute_group pwm2_attr_group = { .attrs = pwm2_attrs }; @@ -1126,6 +1170,7 @@ static struct attribute_group in0_attr_group = { .attrs = in0_attrs }; static struct attribute_group in3_attr_group = { .attrs = in3_attrs }; static struct attribute_group in4_attr_group = { .attrs = in4_attrs }; static struct attribute_group in5_attr_group = { .attrs = in5_attrs }; +static struct attribute_group vid_attr_group = { .attrs = vid_attrs }; static int adt7475_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) @@ -1180,6 +1225,8 @@ static void adt7475_remove_files(struct i2c_client *client, sysfs_remove_group(&client->dev.kobj, &in4_attr_group); if (data->has_voltage & (1 << 5)) sysfs_remove_group(&client->dev.kobj, &in5_attr_group); + if (data->has_vid) + sysfs_remove_group(&client->dev.kobj, &vid_attr_group); } static int adt7475_probe(struct i2c_client *client, @@ -1247,11 +1294,14 @@ static int adt7475_probe(struct i2c_client *client, data->has_voltage |= (1 << 0); /* in0 */ } - /* On the ADT7476, the +12V input pin may instead be used as VID5 */ + /* On the ADT7476, the +12V input pin may instead be used as VID5, + and VID pins may alternatively be used as GPIO */ if (id->driver_data == adt7476) { u8 vid = adt7475_read(REG_VID); if (!(vid & VID_VIDSEL)) data->has_voltage |= (1 << 4); /* in4 */ + + data->has_vid = !(adt7475_read(REG_CONFIG5) & CONFIG5_VIDGPIO); } /* Voltage attenuators can be bypassed, globally or individually */ @@ -1304,6 +1354,12 @@ static int adt7475_probe(struct i2c_client *client, if (ret) goto eremove; } + if (data->has_vid) { + data->vrm = vid_which_vrm(); + ret = sysfs_create_group(&client->dev.kobj, &vid_attr_group); + if (ret) + goto eremove; + } data->hwmon_dev = hwmon_device_register(&client->dev); if (IS_ERR(data->hwmon_dev)) { @@ -1314,11 +1370,12 @@ static int adt7475_probe(struct i2c_client *client, dev_info(&client->dev, "%s device, revision %d\n", names[id->driver_data], revision); if ((data->has_voltage & 0x11) || data->has_fan4 || data->has_pwm2) - dev_info(&client->dev, "Optional features:%s%s%s%s\n", + dev_info(&client->dev, "Optional features:%s%s%s%s%s\n", (data->has_voltage & (1 << 0)) ? " in0" : "", (data->has_voltage & (1 << 4)) ? " in4" : "", data->has_fan4 ? " fan4" : "", - data->has_pwm2 ? " pwm2" : ""); + data->has_pwm2 ? " pwm2" : "", + data->has_vid ? " vid" : ""); if (data->bypass_attn) dev_info(&client->dev, "Bypassing attenuators on:%s%s%s%s\n", (data->bypass_attn & (1 << 0)) ? " in0" : "", @@ -1472,6 +1529,9 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) data->pwm[INPUT][i] = adt7475_read(PWM_REG(i)); } + if (data->has_vid) + data->vid = adt7475_read(REG_VID) & 0x3f; + data->measure_updated = jiffies; } -- cgit v1.2.3-70-g09d2