From 6d905e67cfd84b42c2171e252a3a0aca269da9f8 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Wed, 9 Dec 2009 20:35:45 +0100 Subject: hwmon: (s3c-hwmon) Fix build error The previous patch, commit be4c23c93c4828d36ac9e1a88410618a61676426 was from the wrong tree and thus broke the current build which had the channel configuration name changed. Fix the following build errors: drivers/hwmon/s3c-hwmon.c: In function 's3c_hwmon_probe': drivers/hwmon/s3c-hwmon.c:326: warning: initialization from incompatible pointer type drivers/hwmon/s3c-hwmon.c:331: error: dereferencing pointer to incomplete type drivers/hwmon/s3c-hwmon.c:336: error: dereferencing pointer to incomplete type Signed-off-by: Ben Dooks Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/s3c-hwmon.c b/drivers/hwmon/s3c-hwmon.c index 7183541..3f3f9a4 100644 --- a/drivers/hwmon/s3c-hwmon.c +++ b/drivers/hwmon/s3c-hwmon.c @@ -323,7 +323,7 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev) } for (i = 0; i < ARRAY_SIZE(pdata->in); i++) { - struct s3c24xx_adc_hwmon_incfg *cfg = pdata->in[i]; + struct s3c_hwmon_chcfg *cfg = pdata->in[i]; if (!cfg) continue; @@ -333,7 +333,7 @@ static int __devinit s3c_hwmon_probe(struct platform_device *dev) "channel %d multiplier too large\n", i); - if (cfg->divider == 0) { + if (cfg->div == 0) { dev_err(&dev->dev, "channel %d divider zero\n", i); continue; } -- cgit v0.10.2 From 047f4ec29453ad8a785ccab7d8098f28149828e2 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:46 +0100 Subject: MAINTAINERS: Add missing hwmon files Add missing documentation and header files to the hardware monitoring subsystem section. Signed-off-by: Jean Delvare diff --git a/MAINTAINERS b/MAINTAINERS index ea781c1..83398d8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2423,7 +2423,9 @@ HARDWARE MONITORING L: lm-sensors@lm-sensors.org W: http://www.lm-sensors.org/ S: Orphan +F: Documentation/hwmon/ F: drivers/hwmon/ +F: include/linux/hwmon*.h HARDWARE RANDOM NUMBER GENERATOR CORE M: Matt Mackall -- cgit v0.10.2 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 diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index 82def88..3de6b0b 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 v0.10.2 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 diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 659315d..f9ba96c 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 a3749cb..2f782e3 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 v0.10.2 From 591ec6509ed888723caf6ac8ced3f6f718625a1f Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:48 +0100 Subject: hwmon: (it87) Check for fan2 and fan3 availability The fan2 and fan3 input and output pins can be used as GPIOs. Check their function before exposing their sysfs attributes and accessing their registers. Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 2f782e3..0ffe84d 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -125,6 +125,7 @@ superio_exit(void) /* Logical device 7 registers (IT8712F and later) */ #define IT87_SIO_GPIO3_REG 0x27 +#define IT87_SIO_GPIO5_REG 0x29 #define IT87_SIO_PINX2_REG 0x2c /* Pin selection */ #define IT87_SIO_VID_REG 0xfc /* VID value */ @@ -245,8 +246,9 @@ struct it87_sio_data { /* Values read from Super-I/O config space */ u8 revision; u8 vid_value; + /* Features skipped based on config or DMI */ u8 skip_vid; - /* Values set based on DMI strings */ + u8 skip_fan; u8 skip_pwm; }; @@ -1044,6 +1046,19 @@ static int __init it87_find(unsigned short *address, sio_data->skip_vid = 1; } + /* Check if fan3 is there or not */ + if (reg & (1 << 6)) + sio_data->skip_pwm |= (1 << 2); + if (reg & (1 << 7)) + sio_data->skip_fan |= (1 << 2); + + /* Check if fan2 is there or not */ + reg = superio_inb(IT87_SIO_GPIO5_REG); + if (reg & (1 << 1)) + sio_data->skip_pwm |= (1 << 1); + if (reg & (1 << 2)) + sio_data->skip_fan |= (1 << 1); + if ((sio_data->type == it8718 || sio_data->type == it8720) && !(sio_data->skip_vid)) sio_data->vid_value = superio_inb(IT87_SIO_VID_REG); @@ -1367,8 +1382,10 @@ static int __devinit it87_check_pwm(struct device *dev) /* Called when we have found a new IT87. */ static void __devinit it87_init_device(struct platform_device *pdev) { + struct it87_sio_data *sio_data = pdev->dev.platform_data; struct it87_data *data = platform_get_drvdata(pdev); int tmp, i; + u8 mask; /* initialize to sane defaults: * - if the chip is in manual pwm mode, this will be overwritten with @@ -1414,10 +1431,11 @@ static void __devinit it87_init_device(struct platform_device *pdev) } /* Check if tachometers are reset manually or by some reason */ + mask = 0x70 & ~(sio_data->skip_fan << 4); data->fan_main_ctrl = it87_read_value(data, IT87_REG_FAN_MAIN_CTRL); - if ((data->fan_main_ctrl & 0x70) == 0) { + if ((data->fan_main_ctrl & mask) == 0) { /* Enable all fan tachometers */ - data->fan_main_ctrl |= 0x70; + data->fan_main_ctrl |= mask; it87_write_value(data, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); } data->has_fan = (data->fan_main_ctrl >> 4) & 0x07; @@ -1440,6 +1458,9 @@ static void __devinit it87_init_device(struct platform_device *pdev) } } + /* Fan input pins may be used for alternative functions */ + data->has_fan &= ~sio_data->skip_fan; + /* Set current fan mode registers and the default settings for the * other mode registers */ for (i = 0; i < 3; i++) { -- cgit v0.10.2 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 diff --git a/Documentation/hwmon/w83627hf b/Documentation/hwmon/w83627hf index 6ee36db..44dd2bc 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 2be28ac..d67407d 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 v0.10.2 From b72656dbc491484765776a16eeb55ef2e90efea6 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:49 +0100 Subject: hwmon: (w83627hf) Stop using globals for I/O port numbers Stop using global variables REG and VAL for I/O port numbers. This is ugly and unsafe. Signed-off-by: Jean Delvare Cc: Rodolfo Giometti diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c index d67407d..b257c72 100644 --- a/drivers/hwmon/w83627hf.c +++ b/drivers/hwmon/w83627hf.c @@ -59,6 +59,11 @@ static struct platform_device *pdev; #define DRVNAME "w83627hf" enum chips { w83627hf, w83627thf, w83697hf, w83637hf, w83687thf }; +struct w83627hf_sio_data { + enum chips type; + int sioaddr; +}; + static u8 force_i2c = 0x1f; module_param(force_i2c, byte, 0); MODULE_PARM_DESC(force_i2c, @@ -73,9 +78,7 @@ module_param(force_id, ushort, 0); MODULE_PARM_DESC(force_id, "Override the detected device ID"); /* modified from kernel/include/traps.c */ -static int REG; /* The register to read/write */ #define DEV 0x07 /* Register: Logical device select */ -static int VAL; /* The value to read/write */ /* logical device numbers for superio_select (below) */ #define W83627HF_LD_FDC 0x00 @@ -105,37 +108,37 @@ static int VAL; /* The value to read/write */ #define W83687THF_VID_DATA 0xF1 /* w83687thf only */ static inline void -superio_outb(int reg, int val) +superio_outb(struct w83627hf_sio_data *sio, int reg, int val) { - outb(reg, REG); - outb(val, VAL); + outb(reg, sio->sioaddr); + outb(val, sio->sioaddr + 1); } static inline int -superio_inb(int reg) +superio_inb(struct w83627hf_sio_data *sio, int reg) { - outb(reg, REG); - return inb(VAL); + outb(reg, sio->sioaddr); + return inb(sio->sioaddr + 1); } static inline void -superio_select(int ld) +superio_select(struct w83627hf_sio_data *sio, int ld) { - outb(DEV, REG); - outb(ld, VAL); + outb(DEV, sio->sioaddr); + outb(ld, sio->sioaddr + 1); } static inline void -superio_enter(void) +superio_enter(struct w83627hf_sio_data *sio) { - outb(0x87, REG); - outb(0x87, REG); + outb(0x87, sio->sioaddr); + outb(0x87, sio->sioaddr); } static inline void -superio_exit(void) +superio_exit(struct w83627hf_sio_data *sio) { - outb(0xAA, REG); + outb(0xAA, sio->sioaddr); } #define W627_DEVID 0x52 @@ -376,10 +379,6 @@ struct w83627hf_data { u8 vrm_ovt; /* Register value, 627THF/637HF/687THF only */ }; -struct w83627hf_sio_data { - enum chips type; -}; - static int w83627hf_probe(struct platform_device *pdev); static int __devexit w83627hf_remove(struct platform_device *pdev); @@ -1136,11 +1135,8 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr, "W83687THF", }; - REG = sioaddr; - VAL = sioaddr + 1; - - superio_enter(); - val = force_id ? force_id : superio_inb(DEVID); + superio_enter(sio_data); + val = force_id ? force_id : superio_inb(sio_data, DEVID); switch (val) { case W627_DEVID: sio_data->type = w83627hf; @@ -1164,9 +1160,9 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr, goto exit; } - superio_select(W83627HF_LD_HWM); - val = (superio_inb(WINB_BASE_REG) << 8) | - superio_inb(WINB_BASE_REG + 1); + superio_select(sio_data, W83627HF_LD_HWM); + val = (superio_inb(sio_data, WINB_BASE_REG) << 8) | + superio_inb(sio_data, WINB_BASE_REG + 1); *addr = val & WINB_ALIGNMENT; if (*addr == 0) { printk(KERN_WARNING DRVNAME ": Base address not set, " @@ -1174,18 +1170,19 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr, goto exit; } - val = superio_inb(WINB_ACT_REG); + val = superio_inb(sio_data, WINB_ACT_REG); if (!(val & 0x01)) { printk(KERN_WARNING DRVNAME ": Enabling HWM logical device\n"); - superio_outb(WINB_ACT_REG, val | 0x01); + superio_outb(sio_data, WINB_ACT_REG, val | 0x01); } err = 0; + sio_data->sioaddr = sioaddr; pr_info(DRVNAME ": Found %s chip at %#x\n", names[sio_data->type], *addr); exit: - superio_exit(); + superio_exit(sio_data); return err; } @@ -1500,20 +1497,21 @@ static int w83627hf_read_value(struct w83627hf_data *data, u16 reg) static int __devinit w83627thf_read_gpio5(struct platform_device *pdev) { + struct w83627hf_sio_data *sio_data = pdev->dev.platform_data; int res = 0xff, sel; - superio_enter(); - superio_select(W83627HF_LD_GPIO5); + superio_enter(sio_data); + superio_select(sio_data, W83627HF_LD_GPIO5); /* Make sure these GPIO pins are enabled */ - if (!(superio_inb(W83627THF_GPIO5_EN) & (1<<3))) { + if (!(superio_inb(sio_data, W83627THF_GPIO5_EN) & (1<<3))) { dev_dbg(&pdev->dev, "GPIO5 disabled, no VID function\n"); goto exit; } /* Make sure the pins are configured for input There must be at least five (VRM 9), and possibly 6 (VRM 10) */ - sel = superio_inb(W83627THF_GPIO5_IOSR) & 0x3f; + sel = superio_inb(sio_data, W83627THF_GPIO5_IOSR) & 0x3f; if ((sel & 0x1f) != 0x1f) { dev_dbg(&pdev->dev, "GPIO5 not configured for VID " "function\n"); @@ -1521,37 +1519,38 @@ static int __devinit w83627thf_read_gpio5(struct platform_device *pdev) } dev_info(&pdev->dev, "Reading VID from GPIO5\n"); - res = superio_inb(W83627THF_GPIO5_DR) & sel; + res = superio_inb(sio_data, W83627THF_GPIO5_DR) & sel; exit: - superio_exit(); + superio_exit(sio_data); return res; } static int __devinit w83687thf_read_vid(struct platform_device *pdev) { + struct w83627hf_sio_data *sio_data = pdev->dev.platform_data; int res = 0xff; - superio_enter(); - superio_select(W83627HF_LD_HWM); + superio_enter(sio_data); + superio_select(sio_data, W83627HF_LD_HWM); /* Make sure these GPIO pins are enabled */ - if (!(superio_inb(W83687THF_VID_EN) & (1 << 2))) { + if (!(superio_inb(sio_data, W83687THF_VID_EN) & (1 << 2))) { dev_dbg(&pdev->dev, "VID disabled, no VID function\n"); goto exit; } /* Make sure the pins are configured for input */ - if (!(superio_inb(W83687THF_VID_CFG) & (1 << 4))) { + if (!(superio_inb(sio_data, W83687THF_VID_CFG) & (1 << 4))) { dev_dbg(&pdev->dev, "VID configured as output, " "no VID function\n"); goto exit; } - res = superio_inb(W83687THF_VID_DATA) & 0x3f; + res = superio_inb(sio_data, W83687THF_VID_DATA) & 0x3f; exit: - superio_exit(); + superio_exit(sio_data); return res; } -- cgit v0.10.2 From 4e233cbed249ea94d989b8be08eac0414dbdc44b Mon Sep 17 00:00:00 2001 From: Adrien Demarez Date: Wed, 9 Dec 2009 20:35:50 +0100 Subject: hwmon: New driver for the National Semiconductor LM73 The National Semiconductor LM73 is a single temperature sensor, much like the famous LM75. Signed-off-by: Adrien Demarez Signed-off-by: Jean Delvare diff --git a/MAINTAINERS b/MAINTAINERS index 83398d8..1f6155c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3320,6 +3320,12 @@ S: Maintained F: Documentation/hwmon/lis3lv02d F: drivers/hwmon/lis3lv02d.* +LM73 HARDWARE MONITOR DRIVER +M: Guillaume Ligneul +L: lm-sensors@lm-sensors.org +S: Maintained +F: drivers/hwmon/lm73.c + LM83 HARDWARE MONITOR DRIVER M: Jean Delvare L: lm-sensors@lm-sensors.org diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 700e93a..87184b5 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -442,6 +442,15 @@ config SENSORS_LM70 This driver can also be built as a module. If so, the module will be called lm70. +config SENSORS_LM73 + tristate "National Semiconductor LM73" + depends on I2C + help + If you say yes here you get support for National Semiconductor LM73 + sensor chips. + This driver can also be built as a module. If so, the module + will be called lm73. + config SENSORS_LM75 tristate "National Semiconductor LM75 and compatibles" depends on I2C diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 9f46cb0..a24a52d 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o obj-$(CONFIG_SENSORS_LIS3_SPI) += lis3lv02d.o lis3lv02d_spi.o obj-$(CONFIG_SENSORS_LM63) += lm63.o obj-$(CONFIG_SENSORS_LM70) += lm70.o +obj-$(CONFIG_SENSORS_LM73) += lm73.o obj-$(CONFIG_SENSORS_LM75) += lm75.o obj-$(CONFIG_SENSORS_LM77) += lm77.o obj-$(CONFIG_SENSORS_LM78) += lm78.o diff --git a/drivers/hwmon/lm73.c b/drivers/hwmon/lm73.c new file mode 100644 index 0000000..0bf8b2a --- /dev/null +++ b/drivers/hwmon/lm73.c @@ -0,0 +1,205 @@ +/* + * LM73 Sensor driver + * Based on LM75 + * + * Copyright (C) 2007, CenoSYS (www.cenosys.com). + * Copyright (C) 2009, Bollore telecom (www.bolloretelecom.eu). + * + * Guillaume Ligneul + * Adrien Demarez + * Jeremy Laine + * + * This software program is licensed subject to the GNU General Public License + * (GPL).Version 2,June 1991, available at + * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html + */ + +#include +#include +#include +#include +#include +#include +#include + + +/* Addresses scanned */ +static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4c, + 0x4d, 0x4e, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(lm73); + +/* LM73 registers */ +#define LM73_REG_INPUT 0x00 +#define LM73_REG_CONF 0x01 +#define LM73_REG_MAX 0x02 +#define LM73_REG_MIN 0x03 +#define LM73_REG_CTRL 0x04 +#define LM73_REG_ID 0x07 + +#define LM73_ID 0x9001 /* or 0x190 after a swab16() */ +#define DRVNAME "lm73" +#define LM73_TEMP_MIN (-40) +#define LM73_TEMP_MAX 150 + +/*-----------------------------------------------------------------------*/ + + +static ssize_t set_temp(struct device *dev, struct device_attribute *da, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + long temp; + short value; + + int status = strict_strtol(buf, 10, &temp); + if (status < 0) + return status; + + /* Write value */ + value = (short) SENSORS_LIMIT(temp/250, (LM73_TEMP_MIN*4), + (LM73_TEMP_MAX*4)) << 5; + i2c_smbus_write_word_data(client, attr->index, swab16(value)); + return count; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *da, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); + struct i2c_client *client = to_i2c_client(dev); + /* use integer division instead of equivalent right shift to + guarantee arithmetic shift and preserve the sign */ + int temp = ((s16) (swab16(i2c_smbus_read_word_data(client, + attr->index)))*250) / 32; + return sprintf(buf, "%d\n", temp); +} + + +/*-----------------------------------------------------------------------*/ + +/* sysfs attributes for hwmon */ + +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, + show_temp, set_temp, LM73_REG_MAX); +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, + show_temp, set_temp, LM73_REG_MIN); +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, + show_temp, NULL, LM73_REG_INPUT); + + +static struct attribute *lm73_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp1_min.dev_attr.attr, + + NULL +}; + +static const struct attribute_group lm73_group = { + .attrs = lm73_attributes, +}; + +/*-----------------------------------------------------------------------*/ + +/* device probe and removal */ + +static int +lm73_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *hwmon_dev; + int status; + + /* Register sysfs hooks */ + status = sysfs_create_group(&client->dev.kobj, &lm73_group); + if (status) + return status; + + hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(hwmon_dev)) { + status = PTR_ERR(hwmon_dev); + goto exit_remove; + } + i2c_set_clientdata(client, hwmon_dev); + + dev_info(&client->dev, "%s: sensor '%s'\n", + dev_name(hwmon_dev), client->name); + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &lm73_group); + return status; +} + +static int lm73_remove(struct i2c_client *client) +{ + struct device *hwmon_dev = i2c_get_clientdata(client); + + hwmon_device_unregister(hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &lm73_group); + i2c_set_clientdata(client, NULL); + return 0; +} + +static const struct i2c_device_id lm73_ids[] = { + { "lm73", lm73 }, + { /* LIST END */ } +}; +MODULE_DEVICE_TABLE(i2c, lm73_ids); + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int lm73_detect(struct i2c_client *new_client, int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = new_client->adapter; + u16 id; + u8 ctrl; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + /* Check device ID */ + id = i2c_smbus_read_word_data(new_client, LM73_REG_ID); + ctrl = i2c_smbus_read_byte_data(new_client, LM73_REG_CTRL); + if ((id != LM73_ID) || (ctrl & 0x10)) + return -ENODEV; + + strlcpy(info->type, "lm73", I2C_NAME_SIZE); + + return 0; +} + +static struct i2c_driver lm73_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "lm73", + }, + .probe = lm73_probe, + .remove = lm73_remove, + .id_table = lm73_ids, + .detect = lm73_detect, + .address_data = &addr_data, +}; + +/* module glue */ + +static int __init sensors_lm73_init(void) +{ + return i2c_add_driver(&lm73_driver); +} + +static void __exit sensors_lm73_exit(void) +{ + i2c_del_driver(&lm73_driver); +} + +MODULE_AUTHOR("Guillaume Ligneul "); +MODULE_DESCRIPTION("LM73 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm73_init); +module_exit(sensors_lm73_exit); -- cgit v0.10.2 From 8007ea35ef88d0d621c6d192ce48ce7289109f39 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:51 +0100 Subject: hwmon: (adm1021) Clean up detect function As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c index afc5943..33acf29 100644 --- a/drivers/hwmon/adm1021.c +++ b/drivers/hwmon/adm1021.c @@ -288,9 +288,8 @@ static int adm1021_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - int i; - const char *type_name = ""; - int conv_rate, status, config; + const char *type_name; + int conv_rate, status, config, man_id, dev_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { pr_debug("adm1021: detect failed, " @@ -303,62 +302,37 @@ static int adm1021_detect(struct i2c_client *client, int kind, ADM1021_REG_CONV_RATE_R); config = i2c_smbus_read_byte_data(client, ADM1021_REG_CONFIG_R); - /* Now, we do the remaining detection. */ - if (kind < 0) { - if ((status & 0x03) != 0x00 || (config & 0x3F) != 0x00 - || (conv_rate & 0xF8) != 0x00) { - pr_debug("adm1021: detect failed, " - "chip not detected!\n"); - return -ENODEV; - } + /* Check unused bits */ + if ((status & 0x03) || (config & 0x3F) || (conv_rate & 0xF8)) { + pr_debug("adm1021: detect failed, chip not detected!\n"); + return -ENODEV; } /* Determine the chip type. */ - if (kind <= 0) { - i = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID); - if (i == 0x41) - if ((i2c_smbus_read_byte_data(client, - ADM1021_REG_DEV_ID) & 0xF0) == 0x30) - kind = adm1023; - else - kind = adm1021; - else if (i == 0x49) - kind = thmc10; - else if (i == 0x23) - kind = gl523sm; - else if ((i == 0x4d) && - (i2c_smbus_read_byte_data(client, - ADM1021_REG_DEV_ID) == 0x01)) - kind = max1617a; - else if (i == 0x54) - kind = mc1066; - /* LM84 Mfr ID in a different place, and it has more unused bits */ - else if (conv_rate == 0x00 - && (kind == 0 /* skip extra detection */ - || ((config & 0x7F) == 0x00 - && (status & 0xAB) == 0x00))) - kind = lm84; - else - kind = max1617; - } + man_id = i2c_smbus_read_byte_data(client, ADM1021_REG_MAN_ID); + dev_id = i2c_smbus_read_byte_data(client, ADM1021_REG_DEV_ID); - if (kind == max1617) { - type_name = "max1617"; - } else if (kind == max1617a) { + if (man_id == 0x4d && dev_id == 0x01) type_name = "max1617a"; - } else if (kind == adm1021) { - type_name = "adm1021"; - } else if (kind == adm1023) { - type_name = "adm1023"; - } else if (kind == thmc10) { + else if (man_id == 0x41) { + if ((dev_id & 0xF0) == 0x30) + type_name = "adm1023"; + else + type_name = "adm1021"; + } else if (man_id == 0x49) type_name = "thmc10"; - } else if (kind == lm84) { - type_name = "lm84"; - } else if (kind == gl523sm) { + else if (man_id == 0x23) type_name = "gl523sm"; - } else if (kind == mc1066) { + else if (man_id == 0x54) type_name = "mc1066"; - } + /* LM84 Mfr ID in a different place, and it has more unused bits */ + else if (conv_rate == 0x00 + && (config & 0x7F) == 0x00 + && (status & 0xAB) == 0x00) + type_name = "lm84"; + else + type_name = "max1617"; + pr_debug("adm1021: Detected chip %s at adapter %d, address 0x%02x.\n", type_name, i2c_adapter_id(adapter), client->addr); strlcpy(info->type, type_name, I2C_NAME_SIZE); -- cgit v0.10.2 From 9560672369098b021688421f45476ce0c8bc451b Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:51 +0100 Subject: hwmon: (adm1025) Clean up detect function As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c index 4db04d6..db6ac2b 100644 --- a/drivers/hwmon/adm1025.c +++ b/drivers/hwmon/adm1025.c @@ -2,7 +2,7 @@ * adm1025.c * * Copyright (C) 2000 Chen-Yuan Wu - * Copyright (C) 2003-2008 Jean Delvare + * Copyright (C) 2003-2009 Jean Delvare * * The ADM1025 is a sensor chip made by Analog Devices. It reports up to 6 * voltages (including its own power source) and up to two temperatures @@ -413,67 +413,34 @@ static int adm1025_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - const char *name = ""; - u8 config; + const char *name; + u8 man_id, chip_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip. A zero kind means that - * the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - */ - config = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG); - if (kind < 0) { /* detection */ - if ((config & 0x80) != 0x00 - || (i2c_smbus_read_byte_data(client, - ADM1025_REG_STATUS1) & 0xC0) != 0x00 - || (i2c_smbus_read_byte_data(client, - ADM1025_REG_STATUS2) & 0xBC) != 0x00) { - dev_dbg(&adapter->dev, - "ADM1025 detection failed at 0x%02x.\n", - client->addr); - return -ENODEV; - } + /* Check for unused bits */ + if ((i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG) & 0x80) + || (i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS1) & 0xC0) + || (i2c_smbus_read_byte_data(client, ADM1025_REG_STATUS2) & 0xBC)) { + dev_dbg(&adapter->dev, "ADM1025 detection failed at 0x%02x\n", + client->addr); + return -ENODEV; } - if (kind <= 0) { /* identification */ - u8 man_id, chip_id; - - man_id = i2c_smbus_read_byte_data(client, ADM1025_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(client, ADM1025_REG_CHIP_ID); - - if (man_id == 0x41) { /* Analog Devices */ - if ((chip_id & 0xF0) == 0x20) { /* ADM1025/ADM1025A */ - kind = adm1025; - } - } else - if (man_id == 0xA1) { /* Philips */ - if (client->addr != 0x2E - && (chip_id & 0xF0) == 0x20) { /* NE1619 */ - kind = ne1619; - } - } - - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X).\n", man_id, chip_id); - return -ENODEV; - } - } + /* Identification */ + chip_id = i2c_smbus_read_byte_data(client, ADM1025_REG_CHIP_ID); + if ((chip_id & 0xF0) != 0x20) + return -ENODEV; - if (kind == adm1025) { + man_id = i2c_smbus_read_byte_data(client, ADM1025_REG_MAN_ID); + if (man_id == 0x41) name = "adm1025"; - } else if (kind == ne1619) { + else if (man_id == 0xA1 && client->addr != 0x2E) name = "ne1619"; - } + else + return -ENODEV; + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; -- cgit v0.10.2 From 747d9bedc3d1e42900bf2bb1669f46e4fd0c8957 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:52 +0100 Subject: hwmon: (lm77) Clean up detect function As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c index 866b401..ac067fd 100644 --- a/drivers/hwmon/lm77.c +++ b/drivers/hwmon/lm77.c @@ -249,6 +249,7 @@ static int lm77_detect(struct i2c_client *new_client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; + int i, cur, conf, hyst, crit, min, max; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) @@ -265,51 +266,48 @@ static int lm77_detect(struct i2c_client *new_client, int kind, 4. registers cycling over 8-address boundaries Word-sized registers are high-byte first. */ - if (kind < 0) { - int i, cur, conf, hyst, crit, min, max; - - /* addresses cycling */ - cur = i2c_smbus_read_word_data(new_client, 0); - conf = i2c_smbus_read_byte_data(new_client, 1); - hyst = i2c_smbus_read_word_data(new_client, 2); - crit = i2c_smbus_read_word_data(new_client, 3); - min = i2c_smbus_read_word_data(new_client, 4); - max = i2c_smbus_read_word_data(new_client, 5); - for (i = 8; i <= 0xff; i += 8) - if (i2c_smbus_read_byte_data(new_client, i + 1) != conf - || i2c_smbus_read_word_data(new_client, i + 2) != hyst - || i2c_smbus_read_word_data(new_client, i + 3) != crit - || i2c_smbus_read_word_data(new_client, i + 4) != min - || i2c_smbus_read_word_data(new_client, i + 5) != max) - return -ENODEV; - - /* sign bits */ - if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0) - || ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0) - || ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0) - || ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0) - || ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0)) - return -ENODEV; - /* unused bits */ - if (conf & 0xe0) + /* addresses cycling */ + cur = i2c_smbus_read_word_data(new_client, 0); + conf = i2c_smbus_read_byte_data(new_client, 1); + hyst = i2c_smbus_read_word_data(new_client, 2); + crit = i2c_smbus_read_word_data(new_client, 3); + min = i2c_smbus_read_word_data(new_client, 4); + max = i2c_smbus_read_word_data(new_client, 5); + for (i = 8; i <= 0xff; i += 8) { + if (i2c_smbus_read_byte_data(new_client, i + 1) != conf + || i2c_smbus_read_word_data(new_client, i + 2) != hyst + || i2c_smbus_read_word_data(new_client, i + 3) != crit + || i2c_smbus_read_word_data(new_client, i + 4) != min + || i2c_smbus_read_word_data(new_client, i + 5) != max) return -ENODEV; + } - /* 0x06 and 0x07 return the last read value */ - cur = i2c_smbus_read_word_data(new_client, 0); - if (i2c_smbus_read_word_data(new_client, 6) != cur - || i2c_smbus_read_word_data(new_client, 7) != cur) - return -ENODEV; - hyst = i2c_smbus_read_word_data(new_client, 2); - if (i2c_smbus_read_word_data(new_client, 6) != hyst - || i2c_smbus_read_word_data(new_client, 7) != hyst) - return -ENODEV; - min = i2c_smbus_read_word_data(new_client, 4); - if (i2c_smbus_read_word_data(new_client, 6) != min - || i2c_smbus_read_word_data(new_client, 7) != min) - return -ENODEV; + /* sign bits */ + if (((cur & 0x00f0) != 0xf0 && (cur & 0x00f0) != 0x0) + || ((hyst & 0x00f0) != 0xf0 && (hyst & 0x00f0) != 0x0) + || ((crit & 0x00f0) != 0xf0 && (crit & 0x00f0) != 0x0) + || ((min & 0x00f0) != 0xf0 && (min & 0x00f0) != 0x0) + || ((max & 0x00f0) != 0xf0 && (max & 0x00f0) != 0x0)) + return -ENODEV; - } + /* unused bits */ + if (conf & 0xe0) + return -ENODEV; + + /* 0x06 and 0x07 return the last read value */ + cur = i2c_smbus_read_word_data(new_client, 0); + if (i2c_smbus_read_word_data(new_client, 6) != cur + || i2c_smbus_read_word_data(new_client, 7) != cur) + return -ENODEV; + hyst = i2c_smbus_read_word_data(new_client, 2); + if (i2c_smbus_read_word_data(new_client, 6) != hyst + || i2c_smbus_read_word_data(new_client, 7) != hyst) + return -ENODEV; + min = i2c_smbus_read_word_data(new_client, 4); + if (i2c_smbus_read_word_data(new_client, 6) != min + || i2c_smbus_read_word_data(new_client, 7) != min) + return -ENODEV; strlcpy(info->type, "lm77", I2C_NAME_SIZE); -- cgit v0.10.2 From b57dc3940a70a2c2460b628567a3cc76efe725f2 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:52 +0100 Subject: hwmon: (lm83) Clean up detect function As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c index e59e2d1..08b03e6 100644 --- a/drivers/hwmon/lm83.c +++ b/drivers/hwmon/lm83.c @@ -1,7 +1,7 @@ /* * lm83.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2008 Jean Delvare + * Copyright (C) 2003-2009 Jean Delvare * * Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is * a sensor chip made by National Semiconductor. It reports up to four @@ -295,69 +295,40 @@ static int lm83_detect(struct i2c_client *new_client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; - const char *name = ""; + const char *name; + u8 man_id, chip_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* Now we do the detection and identification. A negative kind - * means that the driver was loaded with no force parameter - * (default), so we must both detect and identify the chip - * (actually there is only one possible kind of chip for now, LM83). - * A zero kind means that the driver was loaded with the force - * parameter, the detection step shall be skipped. A positive kind - * means that the driver was loaded with the force parameter and a - * given kind of chip is requested, so both the detection and the - * identification steps are skipped. */ - - /* Default to an LM83 if forced */ - if (kind == 0) - kind = lm83; - - if (kind < 0) { /* detection */ - if (((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1) - & 0xA8) != 0x00) || - ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2) - & 0x48) != 0x00) || - ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG) - & 0x41) != 0x00)) { - dev_dbg(&adapter->dev, - "LM83 detection failed at 0x%02x.\n", - new_client->addr); - return -ENODEV; - } + /* Detection */ + if ((i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS1) & 0xA8) || + (i2c_smbus_read_byte_data(new_client, LM83_REG_R_STATUS2) & 0x48) || + (i2c_smbus_read_byte_data(new_client, LM83_REG_R_CONFIG) & 0x41)) { + dev_dbg(&adapter->dev, "LM83 detection failed at 0x%02x\n", + new_client->addr); + return -ENODEV; } - if (kind <= 0) { /* identification */ - u8 man_id, chip_id; - - man_id = i2c_smbus_read_byte_data(new_client, - LM83_REG_R_MAN_ID); - chip_id = i2c_smbus_read_byte_data(new_client, - LM83_REG_R_CHIP_ID); - - if (man_id == 0x01) { /* National Semiconductor */ - if (chip_id == 0x03) { - kind = lm83; - } else - if (chip_id == 0x01) { - kind = lm82; - } - } - - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X).\n", man_id, chip_id); - return -ENODEV; - } - } + /* Identification */ + man_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_MAN_ID); + if (man_id != 0x01) /* National Semiconductor */ + return -ENODEV; - if (kind == lm83) { + chip_id = i2c_smbus_read_byte_data(new_client, LM83_REG_R_CHIP_ID); + switch (chip_id) { + case 0x03: name = "lm83"; - } else - if (kind == lm82) { + break; + case 0x01: name = "lm82"; + break; + default: + /* identification failed */ + dev_info(&adapter->dev, + "Unsupported chip (man_id=0x%02X, chip_id=0x%02X)\n", + man_id, chip_id); + return -ENODEV; } strlcpy(info->type, name, I2C_NAME_SIZE); -- cgit v0.10.2 From d42a2eb5ad9766fac96f27af93b1634e4ffde220 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:53 +0100 Subject: hwmon: (lm85) Clean up detect function As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 6c53d98..d56da2e 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -5,7 +5,7 @@ Copyright (c) 2002, 2003 Philip Pokorny Copyright (c) 2003 Margit Schubert-While Copyright (c) 2004 Justin Thiessen - Copyright (C) 2007, 2008 Jean Delvare + Copyright (C) 2007--2009 Jean Delvare Chip details at @@ -1162,107 +1162,80 @@ static int lm85_detect(struct i2c_client *client, int kind, struct i2c_adapter *adapter = client->adapter; int address = client->addr; const char *type_name; + int company, verstep; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { /* We need to be able to do byte I/O */ return -ENODEV; } - /* If auto-detecting, determine the chip type */ - if (kind < 0) { - int company = lm85_read_value(client, LM85_REG_COMPANY); - int verstep = lm85_read_value(client, LM85_REG_VERSTEP); - - dev_dbg(&adapter->dev, "Detecting device at 0x%02x with " - "COMPANY: 0x%02x and VERSTEP: 0x%02x\n", - address, company, verstep); - - /* All supported chips have the version in common */ - if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC && - (verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC2) { - dev_dbg(&adapter->dev, "Autodetection failed: " - "unsupported version\n"); - return -ENODEV; - } - kind = any_chip; - - /* Now, refine the detection */ - if (company == LM85_COMPANY_NATIONAL) { - switch (verstep) { - case LM85_VERSTEP_LM85C: - kind = lm85c; - break; - case LM85_VERSTEP_LM85B: - kind = lm85b; - break; - case LM85_VERSTEP_LM96000_1: - case LM85_VERSTEP_LM96000_2: - /* Check for Winbond WPCD377I */ - if (lm85_is_fake(client)) { - dev_dbg(&adapter->dev, - "Found Winbond WPCD377I, " - "ignoring\n"); - return -ENODEV; - } - break; - } - } else if (company == LM85_COMPANY_ANALOG_DEV) { - switch (verstep) { - case LM85_VERSTEP_ADM1027: - kind = adm1027; - break; - case LM85_VERSTEP_ADT7463: - case LM85_VERSTEP_ADT7463C: - kind = adt7463; - break; - case LM85_VERSTEP_ADT7468_1: - case LM85_VERSTEP_ADT7468_2: - kind = adt7468; - break; - } - } else if (company == LM85_COMPANY_SMSC) { - switch (verstep) { - case LM85_VERSTEP_EMC6D100_A0: - case LM85_VERSTEP_EMC6D100_A1: - /* Note: we can't tell a '100 from a '101 */ - kind = emc6d100; - break; - case LM85_VERSTEP_EMC6D102: - kind = emc6d102; - break; + /* Determine the chip type */ + company = lm85_read_value(client, LM85_REG_COMPANY); + verstep = lm85_read_value(client, LM85_REG_VERSTEP); + + dev_dbg(&adapter->dev, "Detecting device at 0x%02x with " + "COMPANY: 0x%02x and VERSTEP: 0x%02x\n", + address, company, verstep); + + /* All supported chips have the version in common */ + if ((verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC && + (verstep & LM85_VERSTEP_VMASK) != LM85_VERSTEP_GENERIC2) { + dev_dbg(&adapter->dev, + "Autodetection failed: unsupported version\n"); + return -ENODEV; + } + type_name = "lm85"; + + /* Now, refine the detection */ + if (company == LM85_COMPANY_NATIONAL) { + switch (verstep) { + case LM85_VERSTEP_LM85C: + type_name = "lm85c"; + break; + case LM85_VERSTEP_LM85B: + type_name = "lm85b"; + break; + case LM85_VERSTEP_LM96000_1: + case LM85_VERSTEP_LM96000_2: + /* Check for Winbond WPCD377I */ + if (lm85_is_fake(client)) { + dev_dbg(&adapter->dev, + "Found Winbond WPCD377I, ignoring\n"); + return -ENODEV; } - } else { - dev_dbg(&adapter->dev, "Autodetection failed: " - "unknown vendor\n"); - return -ENODEV; + break; + } + } else if (company == LM85_COMPANY_ANALOG_DEV) { + switch (verstep) { + case LM85_VERSTEP_ADM1027: + type_name = "adm1027"; + break; + case LM85_VERSTEP_ADT7463: + case LM85_VERSTEP_ADT7463C: + type_name = "adt7463"; + break; + case LM85_VERSTEP_ADT7468_1: + case LM85_VERSTEP_ADT7468_2: + type_name = "adt7468"; + break; } + } else if (company == LM85_COMPANY_SMSC) { + switch (verstep) { + case LM85_VERSTEP_EMC6D100_A0: + case LM85_VERSTEP_EMC6D100_A1: + /* Note: we can't tell a '100 from a '101 */ + type_name = "emc6d100"; + break; + case LM85_VERSTEP_EMC6D102: + type_name = "emc6d102"; + break; + } + } else { + dev_dbg(&adapter->dev, + "Autodetection failed: unknown vendor\n"); + return -ENODEV; } - switch (kind) { - case lm85b: - type_name = "lm85b"; - break; - case lm85c: - type_name = "lm85c"; - break; - case adm1027: - type_name = "adm1027"; - break; - case adt7463: - type_name = "adt7463"; - break; - case adt7468: - type_name = "adt7468"; - break; - case emc6d100: - type_name = "emc6d100"; - break; - case emc6d102: - type_name = "emc6d102"; - break; - default: - type_name = "lm85"; - } strlcpy(info->type, type_name, I2C_NAME_SIZE); return 0; -- cgit v0.10.2 From 8f2fa77c53ba8c10696143c21b4111d449c85fb2 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:53 +0100 Subject: hwmon: (lm90) Clean up detect function As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c index 1aff757..b7c905f 100644 --- a/drivers/hwmon/lm90.c +++ b/drivers/hwmon/lm90.c @@ -1,7 +1,7 @@ /* * lm90.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2008 Jean Delvare + * Copyright (C) 2003-2009 Jean Delvare * * Based on the lm83 driver. The LM90 is a sensor chip made by National * Semiconductor. It reports up to two temperatures (its own plus up to @@ -661,154 +661,118 @@ static int lm90_detect(struct i2c_client *new_client, int kind, { struct i2c_adapter *adapter = new_client->adapter; int address = new_client->addr; - const char *name = ""; + const char *name = NULL; + int man_id, chip_id, reg_config1, reg_convrate; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip. A zero kind means that - * the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - */ - - /* Default to an LM90 if forced */ - if (kind == 0) - kind = lm90; - - if (kind < 0) { /* detection and identification */ - int man_id, chip_id, reg_config1, reg_convrate; - - if ((man_id = i2c_smbus_read_byte_data(new_client, + /* detection and identification */ + if ((man_id = i2c_smbus_read_byte_data(new_client, LM90_REG_R_MAN_ID)) < 0 - || (chip_id = i2c_smbus_read_byte_data(new_client, + || (chip_id = i2c_smbus_read_byte_data(new_client, LM90_REG_R_CHIP_ID)) < 0 - || (reg_config1 = i2c_smbus_read_byte_data(new_client, + || (reg_config1 = i2c_smbus_read_byte_data(new_client, LM90_REG_R_CONFIG1)) < 0 - || (reg_convrate = i2c_smbus_read_byte_data(new_client, + || (reg_convrate = i2c_smbus_read_byte_data(new_client, LM90_REG_R_CONVRATE)) < 0) + return -ENODEV; + + if ((address == 0x4C || address == 0x4D) + && man_id == 0x01) { /* National Semiconductor */ + int reg_config2; + + reg_config2 = i2c_smbus_read_byte_data(new_client, + LM90_REG_R_CONFIG2); + if (reg_config2 < 0) return -ENODEV; - - if ((address == 0x4C || address == 0x4D) - && man_id == 0x01) { /* National Semiconductor */ - int reg_config2; - - if ((reg_config2 = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CONFIG2)) < 0) - return -ENODEV; - - if ((reg_config1 & 0x2A) == 0x00 - && (reg_config2 & 0xF8) == 0x00 - && reg_convrate <= 0x09) { - if (address == 0x4C - && (chip_id & 0xF0) == 0x20) { /* LM90 */ - kind = lm90; - } else - if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */ - kind = lm99; - dev_info(&adapter->dev, - "Assuming LM99 chip at " - "0x%02x\n", address); - dev_info(&adapter->dev, - "If it is an LM89, pass " - "force_lm86=%d,0x%02x when " - "loading the lm90 driver\n", - i2c_adapter_id(adapter), - address); - } else - if (address == 0x4C - && (chip_id & 0xF0) == 0x10) { /* LM86 */ - kind = lm86; - } - } - } else - if ((address == 0x4C || address == 0x4D) - && man_id == 0x41) { /* Analog Devices */ - if ((chip_id & 0xF0) == 0x40 /* ADM1032 */ - && (reg_config1 & 0x3F) == 0x00 - && reg_convrate <= 0x0A) { - kind = adm1032; - } else - if (chip_id == 0x51 /* ADT7461 */ - && (reg_config1 & 0x1B) == 0x00 - && reg_convrate <= 0x0A) { - kind = adt7461; - } - } else - if (man_id == 0x4D) { /* Maxim */ - /* - * The MAX6657, MAX6658 and MAX6659 do NOT have a - * chip_id register. Reading from that address will - * return the last read value, which in our case is - * those of the man_id register. Likewise, the config1 - * register seems to lack a low nibble, so the value - * will be those of the previous read, so in our case - * those of the man_id register. - */ - if (chip_id == man_id - && (address == 0x4C || address == 0x4D) - && (reg_config1 & 0x1F) == (man_id & 0x0F) - && reg_convrate <= 0x09) { - kind = max6657; + + if ((reg_config1 & 0x2A) == 0x00 + && (reg_config2 & 0xF8) == 0x00 + && reg_convrate <= 0x09) { + if (address == 0x4C + && (chip_id & 0xF0) == 0x20) { /* LM90 */ + name = "lm90"; } else - /* The chip_id register of the MAX6680 and MAX6681 - * holds the revision of the chip. - * the lowest bit of the config1 register is unused - * and should return zero when read, so should the - * second to last bit of config1 (software reset) - */ - if (chip_id == 0x01 - && (reg_config1 & 0x03) == 0x00 - && reg_convrate <= 0x07) { - kind = max6680; + if ((chip_id & 0xF0) == 0x30) { /* LM89/LM99 */ + name = "lm99"; + dev_info(&adapter->dev, + "Assuming LM99 chip at 0x%02x\n", + address); + dev_info(&adapter->dev, + "If it is an LM89, instantiate it " + "with the new_device sysfs " + "interface\n"); } else - /* The chip_id register of the MAX6646/6647/6649 - * holds the revision of the chip. - * The lowest 6 bits of the config1 register are - * unused and should return zero when read. - */ - if (chip_id == 0x59 - && (reg_config1 & 0x3f) == 0x00 - && reg_convrate <= 0x07) { - kind = max6646; + if (address == 0x4C + && (chip_id & 0xF0) == 0x10) { /* LM86 */ + name = "lm86"; } } - - if (kind <= 0) { /* identification failed */ - dev_dbg(&adapter->dev, - "Unsupported chip at 0x%02x (man_id=0x%02X, " - "chip_id=0x%02X)\n", address, man_id, chip_id); - return -ENODEV; + } else + if ((address == 0x4C || address == 0x4D) + && man_id == 0x41) { /* Analog Devices */ + if ((chip_id & 0xF0) == 0x40 /* ADM1032 */ + && (reg_config1 & 0x3F) == 0x00 + && reg_convrate <= 0x0A) { + name = "adm1032"; + /* The ADM1032 supports PEC, but only if combined + transactions are not used. */ + if (i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE)) + info->flags |= I2C_CLIENT_PEC; + } else + if (chip_id == 0x51 /* ADT7461 */ + && (reg_config1 & 0x1B) == 0x00 + && reg_convrate <= 0x0A) { + name = "adt7461"; + } + } else + if (man_id == 0x4D) { /* Maxim */ + /* + * The MAX6657, MAX6658 and MAX6659 do NOT have a chip_id + * register. Reading from that address will return the last + * read value, which in our case is those of the man_id + * register. Likewise, the config1 register seems to lack a + * low nibble, so the value will be those of the previous + * read, so in our case those of the man_id register. + */ + if (chip_id == man_id + && (address == 0x4C || address == 0x4D) + && (reg_config1 & 0x1F) == (man_id & 0x0F) + && reg_convrate <= 0x09) { + name = "max6657"; + } else + /* + * The chip_id register of the MAX6680 and MAX6681 holds the + * revision of the chip. The lowest bit of the config1 register + * is unused and should return zero when read, so should the + * second to last bit of config1 (software reset). + */ + if (chip_id == 0x01 + && (reg_config1 & 0x03) == 0x00 + && reg_convrate <= 0x07) { + name = "max6680"; + } else + /* + * The chip_id register of the MAX6646/6647/6649 holds the + * revision of the chip. The lowest 6 bits of the config1 + * register are unused and should return zero when read. + */ + if (chip_id == 0x59 + && (reg_config1 & 0x3f) == 0x00 + && reg_convrate <= 0x07) { + name = "max6646"; } } - /* Fill the i2c board info */ - if (kind == lm90) { - name = "lm90"; - } else if (kind == adm1032) { - name = "adm1032"; - /* The ADM1032 supports PEC, but only if combined - transactions are not used. */ - if (i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE)) - info->flags |= I2C_CLIENT_PEC; - } else if (kind == lm99) { - name = "lm99"; - } else if (kind == lm86) { - name = "lm86"; - } else if (kind == max6657) { - name = "max6657"; - } else if (kind == max6680) { - name = "max6680"; - } else if (kind == adt7461) { - name = "adt7461"; - } else if (kind == max6646) { - name = "max6646"; + if (!name) { /* identification failed */ + dev_dbg(&adapter->dev, + "Unsupported chip at 0x%02x (man_id=0x%02X, " + "chip_id=0x%02X)\n", address, man_id, chip_id); + return -ENODEV; } + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; -- cgit v0.10.2 From dbe73c8f454a40de06039ec97bc0272f41f1d3c7 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:54 +0100 Subject: hwmon: (tmp401/tmp421) Clean up detect functions As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare Acked-by: Andre Prendel diff --git a/drivers/hwmon/tmp401.c b/drivers/hwmon/tmp401.c index 7b34f2c..ee96734 100644 --- a/drivers/hwmon/tmp401.c +++ b/drivers/hwmon/tmp401.c @@ -488,46 +488,43 @@ static void tmp401_init_client(struct i2c_client *client) i2c_smbus_write_byte_data(client, TMP401_CONFIG_WRITE, config); } -static int tmp401_detect(struct i2c_client *client, int kind, +static int tmp401_detect(struct i2c_client *client, int _kind, struct i2c_board_info *info) { + enum chips kind; struct i2c_adapter *adapter = client->adapter; + u8 reg; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; /* Detect and identify the chip */ - if (kind <= 0) { - u8 reg; - - reg = i2c_smbus_read_byte_data(client, - TMP401_MANUFACTURER_ID_REG); - if (reg != TMP401_MANUFACTURER_ID) - return -ENODEV; - - reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG); - - switch (reg) { - case TMP401_DEVICE_ID: - kind = tmp401; - break; - case TMP411_DEVICE_ID: - kind = tmp411; - break; - default: - return -ENODEV; - } + reg = i2c_smbus_read_byte_data(client, TMP401_MANUFACTURER_ID_REG); + if (reg != TMP401_MANUFACTURER_ID) + return -ENODEV; - reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); - if (reg & 0x1b) - return -ENODEV; + reg = i2c_smbus_read_byte_data(client, TMP401_DEVICE_ID_REG); - reg = i2c_smbus_read_byte_data(client, - TMP401_CONVERSION_RATE_READ); - /* Datasheet says: 0x1-0x6 */ - if (reg > 15) - return -ENODEV; + switch (reg) { + case TMP401_DEVICE_ID: + kind = tmp401; + break; + case TMP411_DEVICE_ID: + kind = tmp411; + break; + default: + return -ENODEV; } + + reg = i2c_smbus_read_byte_data(client, TMP401_CONFIG_READ); + if (reg & 0x1b) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, TMP401_CONVERSION_RATE_READ); + /* Datasheet says: 0x1-0x6 */ + if (reg > 15) + return -ENODEV; + strlcpy(info->type, tmp401_id[kind - 1].name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/tmp421.c b/drivers/hwmon/tmp421.c index 2092434..bb5464a 100644 --- a/drivers/hwmon/tmp421.c +++ b/drivers/hwmon/tmp421.c @@ -223,39 +223,36 @@ static int tmp421_init_client(struct i2c_client *client) return 0; } -static int tmp421_detect(struct i2c_client *client, int kind, +static int tmp421_detect(struct i2c_client *client, int _kind, struct i2c_board_info *info) { + enum chips kind; struct i2c_adapter *adapter = client->adapter; const char *names[] = { "TMP421", "TMP422", "TMP423" }; + u8 reg; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind <= 0) { - u8 reg; - - reg = i2c_smbus_read_byte_data(client, - TMP421_MANUFACTURER_ID_REG); - if (reg != TMP421_MANUFACTURER_ID) - return -ENODEV; - - reg = i2c_smbus_read_byte_data(client, - TMP421_DEVICE_ID_REG); - switch (reg) { - case TMP421_DEVICE_ID: - kind = tmp421; - break; - case TMP422_DEVICE_ID: - kind = tmp422; - break; - case TMP423_DEVICE_ID: - kind = tmp423; - break; - default: - return -ENODEV; - } + reg = i2c_smbus_read_byte_data(client, TMP421_MANUFACTURER_ID_REG); + if (reg != TMP421_MANUFACTURER_ID) + return -ENODEV; + + reg = i2c_smbus_read_byte_data(client, TMP421_DEVICE_ID_REG); + switch (reg) { + case TMP421_DEVICE_ID: + kind = tmp421; + break; + case TMP422_DEVICE_ID: + kind = tmp422; + break; + case TMP423_DEVICE_ID: + kind = tmp423; + break; + default: + return -ENODEV; } + strlcpy(info->type, tmp421_id[kind - 1].name, I2C_NAME_SIZE); dev_info(&adapter->dev, "Detected TI %s chip at 0x%02x\n", names[kind - 1], client->addr); -- cgit v0.10.2 From bab2bf44f80a5749fdf69f854247e912353142b2 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:54 +0100 Subject: hwmon: (w83781d) Clean up detect function As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c index d27ed1b..7ab7967 100644 --- a/drivers/hwmon/w83781d.c +++ b/drivers/hwmon/w83781d.c @@ -1054,11 +1054,11 @@ static int w83781d_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { - int val1 = 0, val2; + int val1, val2; struct w83781d_data *isa = w83781d_data_if_isa(); struct i2c_adapter *adapter = client->adapter; int address = client->addr; - const char *client_name = ""; + const char *client_name; enum vendor { winbond, asus } vendid; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) @@ -1070,98 +1070,73 @@ w83781d_detect(struct i2c_client *client, int kind, if (isa) mutex_lock(&isa->update_lock); - /* The w8378?d may be stuck in some other bank than bank 0. This may - make reading other information impossible. Specify a force=... or - force_*=... parameter, and the Winbond will be reset to the right - bank. */ - if (kind < 0) { - if (i2c_smbus_read_byte_data - (client, W83781D_REG_CONFIG) & 0x80) { - dev_dbg(&adapter->dev, "Detection of w83781d chip " - "failed at step 3\n"); - goto err_nodev; - } - val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK); - val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); - /* Check for Winbond or Asus ID if in bank 0 */ - if ((!(val1 & 0x07)) && - (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)) - || ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) { - dev_dbg(&adapter->dev, "Detection of w83781d chip " - "failed at step 4\n"); + if (i2c_smbus_read_byte_data(client, W83781D_REG_CONFIG) & 0x80) { + dev_dbg(&adapter->dev, + "Detection of w83781d chip failed at step 3\n"); + goto err_nodev; + } + + val1 = i2c_smbus_read_byte_data(client, W83781D_REG_BANK); + val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); + /* Check for Winbond or Asus ID if in bank 0 */ + if (!(val1 & 0x07) && + ((!(val1 & 0x80) && val2 != 0xa3 && val2 != 0xc3) || + ( (val1 & 0x80) && val2 != 0x5c && val2 != 0x12))) { + dev_dbg(&adapter->dev, + "Detection of w83781d chip failed at step 4\n"); + goto err_nodev; + } + /* If Winbond SMBus, check address at 0x48. + Asus doesn't support, except for as99127f rev.2 */ + if ((!(val1 & 0x80) && val2 == 0xa3) || + ( (val1 & 0x80) && val2 == 0x5c)) { + if (i2c_smbus_read_byte_data(client, W83781D_REG_I2C_ADDR) + != address) { + dev_dbg(&adapter->dev, + "Detection of w83781d chip failed at step 5\n"); goto err_nodev; } - /* If Winbond SMBus, check address at 0x48. - Asus doesn't support, except for as99127f rev.2 */ - if ((!(val1 & 0x80) && (val2 == 0xa3)) || - ((val1 & 0x80) && (val2 == 0x5c))) { - if (i2c_smbus_read_byte_data - (client, W83781D_REG_I2C_ADDR) != address) { - dev_dbg(&adapter->dev, "Detection of w83781d " - "chip failed at step 5\n"); - goto err_nodev; - } - } } - /* We have either had a force parameter, or we have already detected the - Winbond. Put it now into bank 0 and Vendor ID High Byte */ + /* Put it now into bank 0 and Vendor ID High Byte */ i2c_smbus_write_byte_data(client, W83781D_REG_BANK, (i2c_smbus_read_byte_data(client, W83781D_REG_BANK) & 0x78) | 0x80); - /* Determine the chip type. */ - if (kind <= 0) { - /* get vendor ID */ - val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); - if (val2 == 0x5c) - vendid = winbond; - else if (val2 == 0x12) - vendid = asus; - else { - dev_dbg(&adapter->dev, "w83781d chip vendor is " - "neither Winbond nor Asus\n"); - goto err_nodev; - } - - val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID); - if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond) - kind = w83781d; - else if (val1 == 0x30 && vendid == winbond) - kind = w83782d; - else if (val1 == 0x40 && vendid == winbond && address == 0x2d) - kind = w83783s; - else if (val1 == 0x31) - kind = as99127f; - else { - if (kind == 0) - dev_warn(&adapter->dev, "Ignoring 'force' " - "parameter for unknown chip at " - "address 0x%02x\n", address); - goto err_nodev; - } - - if ((kind == w83781d || kind == w83782d) - && w83781d_alias_detect(client, val1)) { - dev_dbg(&adapter->dev, "Device at 0x%02x appears to " - "be the same as ISA device\n", address); - goto err_nodev; - } + /* Get the vendor ID */ + val2 = i2c_smbus_read_byte_data(client, W83781D_REG_CHIPMAN); + if (val2 == 0x5c) + vendid = winbond; + else if (val2 == 0x12) + vendid = asus; + else { + dev_dbg(&adapter->dev, + "w83781d chip vendor is neither Winbond nor Asus\n"); + goto err_nodev; } - if (isa) - mutex_unlock(&isa->update_lock); - - if (kind == w83781d) { + /* Determine the chip type. */ + val1 = i2c_smbus_read_byte_data(client, W83781D_REG_WCHIPID); + if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond) client_name = "w83781d"; - } else if (kind == w83782d) { + else if (val1 == 0x30 && vendid == winbond) client_name = "w83782d"; - } else if (kind == w83783s) { + else if (val1 == 0x40 && vendid == winbond && address == 0x2d) client_name = "w83783s"; - } else if (kind == as99127f) { + else if (val1 == 0x31) client_name = "as99127f"; + else + goto err_nodev; + + if (val1 <= 0x30 && w83781d_alias_detect(client, val1)) { + dev_dbg(&adapter->dev, "Device at 0x%02x appears to " + "be the same as ISA device\n", address); + goto err_nodev; } + if (isa) + mutex_unlock(&isa->update_lock); + strlcpy(info->type, client_name, I2C_NAME_SIZE); return 0; -- cgit v0.10.2 From 3f8b845940c8344e88123bead66b5a871e6366df Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:55 +0100 Subject: hwmon: (w83791d) Clean up detect function As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare Acked-by: Marc Hulsman diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c index 97851c5..0410bf1 100644 --- a/drivers/hwmon/w83791d.c +++ b/drivers/hwmon/w83791d.c @@ -1270,56 +1270,32 @@ static int w83791d_detect(struct i2c_client *client, int kind, return -ENODEV; } - /* The w83791d may be stuck in some other bank than bank 0. This may - make reading other information impossible. Specify a force=... - parameter, and the Winbond will be reset to the right bank. */ - if (kind < 0) { - if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80) { - return -ENODEV; - } - val1 = w83791d_read(client, W83791D_REG_BANK); - val2 = w83791d_read(client, W83791D_REG_CHIPMAN); - /* Check for Winbond ID if in bank 0 */ - if (!(val1 & 0x07)) { - /* yes it is Bank0 */ - if (((!(val1 & 0x80)) && (val2 != 0xa3)) || - ((val1 & 0x80) && (val2 != 0x5c))) { - return -ENODEV; - } - } - /* If Winbond chip, address of chip and W83791D_REG_I2C_ADDR - should match */ - if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address) { + if (w83791d_read(client, W83791D_REG_CONFIG) & 0x80) + return -ENODEV; + + val1 = w83791d_read(client, W83791D_REG_BANK); + val2 = w83791d_read(client, W83791D_REG_CHIPMAN); + /* Check for Winbond ID if in bank 0 */ + if (!(val1 & 0x07)) { + if ((!(val1 & 0x80) && val2 != 0xa3) || + ( (val1 & 0x80) && val2 != 0x5c)) { return -ENODEV; } } + /* If Winbond chip, address of chip and W83791D_REG_I2C_ADDR + should match */ + if (w83791d_read(client, W83791D_REG_I2C_ADDR) != address) + return -ENODEV; - /* We either have a force parameter or we have reason to - believe it is a Winbond chip. Either way, we want bank 0 and - Vendor ID high byte */ + /* We want bank 0 and Vendor ID high byte */ val1 = w83791d_read(client, W83791D_REG_BANK) & 0x78; w83791d_write(client, W83791D_REG_BANK, val1 | 0x80); /* Verify it is a Winbond w83791d */ - if (kind <= 0) { - /* get vendor ID */ - val2 = w83791d_read(client, W83791D_REG_CHIPMAN); - if (val2 != 0x5c) { /* the vendor is NOT Winbond */ - return -ENODEV; - } - val1 = w83791d_read(client, W83791D_REG_WCHIPID); - if (val1 == 0x71) { - kind = w83791d; - } else { - if (kind == 0) - dev_warn(&adapter->dev, - "w83791d: Ignoring 'force' parameter " - "for unknown chip at adapter %d, " - "address 0x%02x\n", - i2c_adapter_id(adapter), address); - return -ENODEV; - } - } + val1 = w83791d_read(client, W83791D_REG_WCHIPID); + val2 = w83791d_read(client, W83791D_REG_CHIPMAN); + if (val1 != 0x71 || val2 != 0x5c) + return -ENODEV; strlcpy(info->type, "w83791d", I2C_NAME_SIZE); -- cgit v0.10.2 From 2de1875a5894794b42fce3833cddf4eac55831de Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:56 +0100 Subject: hwmon: (w83792d) Clean up detect function As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index 2be1619..3897885 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -1273,58 +1273,33 @@ w83792d_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) return -ENODEV; } - /* The w83792d may be stuck in some other bank than bank 0. This may - make reading other information impossible. Specify a force=... or - force_*=... parameter, and the Winbond will be reset to the right - bank. */ - if (kind < 0) { - if (w83792d_read_value(client, W83792D_REG_CONFIG) & 0x80) { - return -ENODEV; - } - val1 = w83792d_read_value(client, W83792D_REG_BANK); - val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN); - /* Check for Winbond ID if in bank 0 */ - if (!(val1 & 0x07)) { /* is Bank0 */ - if (((!(val1 & 0x80)) && (val2 != 0xa3)) || - ((val1 & 0x80) && (val2 != 0x5c))) { - return -ENODEV; - } - } - /* If Winbond chip, address of chip and W83792D_REG_I2C_ADDR - should match */ - if (w83792d_read_value(client, - W83792D_REG_I2C_ADDR) != address) { + if (w83792d_read_value(client, W83792D_REG_CONFIG) & 0x80) + return -ENODEV; + + val1 = w83792d_read_value(client, W83792D_REG_BANK); + val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN); + /* Check for Winbond ID if in bank 0 */ + if (!(val1 & 0x07)) { /* is Bank0 */ + if ((!(val1 & 0x80) && val2 != 0xa3) || + ( (val1 & 0x80) && val2 != 0x5c)) return -ENODEV; - } } + /* If Winbond chip, address of chip and W83792D_REG_I2C_ADDR + should match */ + if (w83792d_read_value(client, W83792D_REG_I2C_ADDR) != address) + return -ENODEV; - /* We have either had a force parameter, or we have already detected the - Winbond. Put it now into bank 0 and Vendor ID High Byte */ + /* Put it now into bank 0 and Vendor ID High Byte */ w83792d_write_value(client, W83792D_REG_BANK, (w83792d_read_value(client, W83792D_REG_BANK) & 0x78) | 0x80); /* Determine the chip type. */ - if (kind <= 0) { - /* get vendor ID */ - val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN); - if (val2 != 0x5c) { /* the vendor is NOT Winbond */ - return -ENODEV; - } - val1 = w83792d_read_value(client, W83792D_REG_WCHIPID); - if (val1 == 0x7a) { - kind = w83792d; - } else { - if (kind == 0) - dev_warn(&adapter->dev, - "w83792d: Ignoring 'force' parameter for" - " unknown chip at adapter %d, address" - " 0x%02x\n", i2c_adapter_id(adapter), - address); - return -ENODEV; - } - } + val1 = w83792d_read_value(client, W83792D_REG_WCHIPID); + val2 = w83792d_read_value(client, W83792D_REG_CHIPMAN); + if (val1 != 0x7a || val2 != 0x5c) + return -ENODEV; strlcpy(info->type, "w83792d", I2C_NAME_SIZE); -- cgit v0.10.2 From a1fa4cdcc4abd4c02a81ab7052c16a342d29f060 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:56 +0100 Subject: hwmon: (w83l785ts) Clean up detect function As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c index ea295b9..9b6c4c1 100644 --- a/drivers/hwmon/w83l785ts.c +++ b/drivers/hwmon/w83l785ts.c @@ -1,7 +1,7 @@ /* * w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware * monitoring - * Copyright (C) 2003-2004 Jean Delvare + * Copyright (C) 2003-2009 Jean Delvare * * Inspired from the lm83 driver. The W83L785TS-S is a sensor chip made * by Winbond. It reports a single external temperature with a 1 deg @@ -146,60 +146,36 @@ static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, 1); */ /* Return 0 if detection is successful, -ENODEV otherwise */ -static int w83l785ts_detect(struct i2c_client *new_client, int kind, +static int w83l785ts_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; + struct i2c_adapter *adapter = client->adapter; + u16 man_id; + u8 chip_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip (actually there is only - * one possible kind of chip for now, W83L785TS-S). A zero kind means - * that the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - */ - if (kind < 0) { /* detection */ - if (((w83l785ts_read_value(new_client, - W83L785TS_REG_CONFIG, 0) & 0x80) != 0x00) - || ((w83l785ts_read_value(new_client, - W83L785TS_REG_TYPE, 0) & 0xFC) != 0x00)) { - dev_dbg(&adapter->dev, - "W83L785TS-S detection failed at 0x%02x.\n", - new_client->addr); - return -ENODEV; - } + /* detection */ + if ((w83l785ts_read_value(client, W83L785TS_REG_CONFIG, 0) & 0x80) + || (w83l785ts_read_value(client, W83L785TS_REG_TYPE, 0) & 0xFC)) { + dev_dbg(&adapter->dev, + "W83L785TS-S detection failed at 0x%02x\n", + client->addr); + return -ENODEV; } - if (kind <= 0) { /* identification */ - u16 man_id; - u8 chip_id; - - man_id = (w83l785ts_read_value(new_client, - W83L785TS_REG_MAN_ID1, 0) << 8) + - w83l785ts_read_value(new_client, - W83L785TS_REG_MAN_ID2, 0); - chip_id = w83l785ts_read_value(new_client, - W83L785TS_REG_CHIP_ID, 0); - - if (man_id == 0x5CA3) { /* Winbond */ - if (chip_id == 0x70) { /* W83L785TS-S */ - kind = w83l785ts; - } - } - - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%04X, " - "chip_id=0x%02X).\n", man_id, chip_id); - return -ENODEV; - } + /* Identification */ + man_id = (w83l785ts_read_value(client, W83L785TS_REG_MAN_ID1, 0) << 8) + + w83l785ts_read_value(client, W83L785TS_REG_MAN_ID2, 0); + chip_id = w83l785ts_read_value(client, W83L785TS_REG_CHIP_ID, 0); + + if (man_id != 0x5CA3 /* Winbond */ + || chip_id != 0x70) { /* W83L785TS-S */ + dev_dbg(&adapter->dev, + "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n", + man_id, chip_id); + return -ENODEV; } strlcpy(info->type, "w83l785ts", I2C_NAME_SIZE); -- cgit v0.10.2 From 52df6440a29123eed912183fe785bbe174ef14b9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:57 +0100 Subject: hwmon: Clean up detect functions As kind is now hard-coded to -1, there is room for code clean-ups. Signed-off-by: Jean Delvare Acked-by: Corentin Labbe Cc: "Mark M. Hoffman" Cc: Juerg Haefliger Cc: Riku Voipio Acked-by: "Hans J. Koch" Cc: Rudolf Marek diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c index ff7de40..fb53639 100644 --- a/drivers/hwmon/adm1026.c +++ b/drivers/hwmon/adm1026.c @@ -1672,35 +1672,26 @@ static int adm1026_detect(struct i2c_client *client, int kind, i2c_adapter_id(client->adapter), client->addr, company, verstep); - /* If auto-detecting, Determine the chip type. */ - if (kind <= 0) { - dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x " - "...\n", i2c_adapter_id(adapter), address); - if (company == ADM1026_COMPANY_ANALOG_DEV - && verstep == ADM1026_VERSTEP_ADM1026) { - kind = adm1026; - } else if (company == ADM1026_COMPANY_ANALOG_DEV - && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Unrecognized stepping " - "0x%02x. Defaulting to ADM1026.\n", verstep); - kind = adm1026; - } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Found version/stepping " - "0x%02x. Assuming generic ADM1026.\n", - verstep); - kind = any_chip; - } else { - dev_dbg(&adapter->dev, "Autodetection failed\n"); - /* Not an ADM1026 ... */ - if (kind == 0) { /* User used force=x,y */ - dev_err(&adapter->dev, "Generic ADM1026 not " - "found at %d,0x%02x. Try " - "force_adm1026.\n", - i2c_adapter_id(adapter), address); - } - return -ENODEV; - } + /* Determine the chip type. */ + dev_dbg(&adapter->dev, "Autodetecting device at %d,0x%02x...\n", + i2c_adapter_id(adapter), address); + if (company == ADM1026_COMPANY_ANALOG_DEV + && verstep == ADM1026_VERSTEP_ADM1026) { + /* Analog Devices ADM1026 */ + } else if (company == ADM1026_COMPANY_ANALOG_DEV + && (verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { + dev_err(&adapter->dev, "Unrecognized stepping " + "0x%02x. Defaulting to ADM1026.\n", verstep); + } else if ((verstep & 0xf0) == ADM1026_VERSTEP_GENERIC) { + dev_err(&adapter->dev, "Found version/stepping " + "0x%02x. Assuming generic ADM1026.\n", + verstep); + } else { + dev_dbg(&adapter->dev, "Autodetection failed\n"); + /* Not an ADM1026... */ + return -ENODEV; } + strlcpy(info->type, "adm1026", I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/adm1029.c b/drivers/hwmon/adm1029.c index 3671815..4d7d843 100644 --- a/drivers/hwmon/adm1029.c +++ b/drivers/hwmon/adm1029.c @@ -301,59 +301,36 @@ static int adm1029_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + u8 man_id, chip_id, temp_devices_installed, nb_fan_support; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* Now we do the detection and identification. A negative kind - * means that the driver was loaded with no force parameter - * (default), so we must both detect and identify the chip - * (actually there is only one possible kind of chip for now, adm1029). - * A zero kind means that the driver was loaded with the force - * parameter, the detection step shall be skipped. A positive kind - * means that the driver was loaded with the force parameter and a - * given kind of chip is requested, so both the detection and the - * identification steps are skipped. */ - - /* Default to an adm1029 if forced */ - if (kind == 0) - kind = adm1029; - /* ADM1029 doesn't have CHIP ID, check just MAN ID * For better detection we check also ADM1029_TEMP_DEVICES_INSTALLED, * ADM1029_REG_NB_FAN_SUPPORT and compare it with possible values * documented */ - if (kind <= 0) { /* identification */ - u8 man_id, chip_id, temp_devices_installed, nb_fan_support; - - man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID); - temp_devices_installed = i2c_smbus_read_byte_data(client, + man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID); + temp_devices_installed = i2c_smbus_read_byte_data(client, ADM1029_REG_TEMP_DEVICES_INSTALLED); - nb_fan_support = i2c_smbus_read_byte_data(client, + nb_fan_support = i2c_smbus_read_byte_data(client, ADM1029_REG_NB_FAN_SUPPORT); - /* 0x41 is Analog Devices */ - if (man_id == 0x41 && (temp_devices_installed & 0xf9) == 0x01 - && nb_fan_support == 0x03) { - if ((chip_id & 0xF0) == 0x00) { - kind = adm1029; - } else { - /* There are no "official" CHIP ID, so actually - * we use Major/Minor revision for that */ - printk(KERN_INFO - "adm1029: Unknown major revision %x, " - "please let us know\n", chip_id); - } - } + /* 0x41 is Analog Devices */ + if (man_id != 0x41 || (temp_devices_installed & 0xf9) != 0x01 + || nb_fan_support != 0x03) + return -ENODEV; - if (kind <= 0) { /* identification failed */ - pr_debug("adm1029: Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X)\n", man_id, chip_id); - return -ENODEV; - } + if ((chip_id & 0xF0) != 0x00) { + /* There are no "official" CHIP ID, so actually + * we use Major/Minor revision for that */ + pr_info("adm1029: Unknown major revision %x, " + "please let us know\n", chip_id); + return -ENODEV; } + strlcpy(info->type, "adm1029", I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c index 5690595..0e72217 100644 --- a/drivers/hwmon/adm1031.c +++ b/drivers/hwmon/adm1031.c @@ -817,31 +817,19 @@ static int adm1031_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - const char *name = ""; + const char *name; + int id, co; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind < 0) { - int id, co; - id = i2c_smbus_read_byte_data(client, 0x3d); - co = i2c_smbus_read_byte_data(client, 0x3e); + id = i2c_smbus_read_byte_data(client, 0x3d); + co = i2c_smbus_read_byte_data(client, 0x3e); - if (!((id == 0x31 || id == 0x30) && co == 0x41)) - return -ENODEV; - kind = (id == 0x30) ? adm1030 : adm1031; - } - - if (kind <= 0) - kind = adm1031; + if (!((id == 0x31 || id == 0x30) && co == 0x41)) + return -ENODEV; + name = (id == 0x30) ? "adm1030" : "adm1031"; - /* Given the detected chip type, set the chip name and the - * auto fan control helper table. */ - if (kind == adm1030) { - name = "adm1030"; - } else if (kind == adm1031) { - name = "adm1031"; - } strlcpy(info->type, name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c index 2444b15..20e0481 100644 --- a/drivers/hwmon/adm9240.c +++ b/drivers/hwmon/adm9240.c @@ -556,51 +556,34 @@ static int adm9240_detect(struct i2c_client *new_client, int kind, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind == 0) { - kind = adm9240; - } - - if (kind < 0) { - - /* verify chip: reg address should match i2c address */ - if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR) - != address) { - dev_err(&adapter->dev, "detect fail: address match, " - "0x%02x\n", address); - return -ENODEV; - } - - /* check known chip manufacturer */ - man_id = i2c_smbus_read_byte_data(new_client, - ADM9240_REG_MAN_ID); - if (man_id == 0x23) { - kind = adm9240; - } else if (man_id == 0xda) { - kind = ds1780; - } else if (man_id == 0x01) { - kind = lm81; - } else { - dev_err(&adapter->dev, "detect fail: unknown manuf, " - "0x%02x\n", man_id); - return -ENODEV; - } - - /* successful detect, print chip info */ - die_rev = i2c_smbus_read_byte_data(new_client, - ADM9240_REG_DIE_REV); - dev_info(&adapter->dev, "found %s revision %u\n", - man_id == 0x23 ? "ADM9240" : - man_id == 0xda ? "DS1780" : "LM81", die_rev); + /* verify chip: reg address should match i2c address */ + if (i2c_smbus_read_byte_data(new_client, ADM9240_REG_I2C_ADDR) + != address) { + dev_err(&adapter->dev, "detect fail: address match, 0x%02x\n", + address); + return -ENODEV; } - /* either forced or detected chip kind */ - if (kind == adm9240) { + /* check known chip manufacturer */ + man_id = i2c_smbus_read_byte_data(new_client, ADM9240_REG_MAN_ID); + if (man_id == 0x23) { name = "adm9240"; - } else if (kind == ds1780) { + } else if (man_id == 0xda) { name = "ds1780"; - } else if (kind == lm81) { + } else if (man_id == 0x01) { name = "lm81"; + } else { + dev_err(&adapter->dev, "detect fail: unknown manuf, 0x%02x\n", + man_id); + return -ENODEV; } + + /* successful detect, print chip info */ + die_rev = i2c_smbus_read_byte_data(new_client, ADM9240_REG_DIE_REV); + dev_info(&adapter->dev, "found %s revision %u\n", + man_id == 0x23 ? "ADM9240" : + man_id == 0xda ? "DS1780" : "LM81", die_rev); + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/ads7828.c b/drivers/hwmon/ads7828.c index 5c39b4a..451977b 100644 --- a/drivers/hwmon/ads7828.c +++ b/drivers/hwmon/ads7828.c @@ -191,6 +191,7 @@ static int ads7828_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int ch; /* Check we have a valid client */ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_WORD_DATA)) @@ -202,20 +203,17 @@ static int ads7828_detect(struct i2c_client *client, int kind, - Read from the 8 channel addresses - Check the top 4 bits of each result are not set (12 data bits) */ - if (kind < 0) { - int ch; - for (ch = 0; ch < ADS7828_NCH; ch++) { - u16 in_data; - u8 cmd = channel_cmd_byte(ch); - in_data = ads7828_read_value(client, cmd); - if (in_data & 0xF000) { - printk(KERN_DEBUG - "%s : Doesn't look like an ads7828 device\n", - __func__); - return -ENODEV; - } + for (ch = 0; ch < ADS7828_NCH; ch++) { + u16 in_data; + u8 cmd = channel_cmd_byte(ch); + in_data = ads7828_read_value(client, cmd); + if (in_data & 0xF000) { + pr_debug("%s : Doesn't look like an ads7828 device\n", + __func__); + return -ENODEV; } } + strlcpy(info->type, "ads7828", I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c index 1852f27..f9c9562 100644 --- a/drivers/hwmon/adt7462.c +++ b/drivers/hwmon/adt7462.c @@ -1906,27 +1906,22 @@ static int adt7462_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int vendor, device, revision; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind <= 0) { - int vendor, device, revision; - - vendor = i2c_smbus_read_byte_data(client, ADT7462_REG_VENDOR); - if (vendor != ADT7462_VENDOR) - return -ENODEV; + vendor = i2c_smbus_read_byte_data(client, ADT7462_REG_VENDOR); + if (vendor != ADT7462_VENDOR) + return -ENODEV; - device = i2c_smbus_read_byte_data(client, ADT7462_REG_DEVICE); - if (device != ADT7462_DEVICE) - return -ENODEV; + device = i2c_smbus_read_byte_data(client, ADT7462_REG_DEVICE); + if (device != ADT7462_DEVICE) + return -ENODEV; - revision = i2c_smbus_read_byte_data(client, - ADT7462_REG_REVISION); - if (revision != ADT7462_REVISION) - return -ENODEV; - } else - dev_dbg(&adapter->dev, "detection forced\n"); + revision = i2c_smbus_read_byte_data(client, ADT7462_REG_REVISION); + if (revision != ADT7462_REVISION) + return -ENODEV; strlcpy(info->type, "adt7462", I2C_NAME_SIZE); diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index 633e1a1..32b1750 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -1229,27 +1229,22 @@ static int adt7470_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int vendor, device, revision; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind <= 0) { - int vendor, device, revision; - - vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR); - if (vendor != ADT7470_VENDOR) - return -ENODEV; + vendor = i2c_smbus_read_byte_data(client, ADT7470_REG_VENDOR); + if (vendor != ADT7470_VENDOR) + return -ENODEV; - device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE); - if (device != ADT7470_DEVICE) - return -ENODEV; + device = i2c_smbus_read_byte_data(client, ADT7470_REG_DEVICE); + if (device != ADT7470_DEVICE) + return -ENODEV; - revision = i2c_smbus_read_byte_data(client, - ADT7470_REG_REVISION); - if (revision != ADT7470_REVISION) - return -ENODEV; - } else - dev_dbg(&adapter->dev, "detection forced\n"); + revision = i2c_smbus_read_byte_data(client, ADT7470_REG_REVISION); + if (revision != ADT7470_REVISION) + return -ENODEV; strlcpy(info->type, "adt7470", I2C_NAME_SIZE); diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c index 0a6ce23..97ef508 100644 --- a/drivers/hwmon/adt7473.c +++ b/drivers/hwmon/adt7473.c @@ -1090,27 +1090,22 @@ static int adt7473_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int vendor, device, revision; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind <= 0) { - int vendor, device, revision; - - vendor = i2c_smbus_read_byte_data(client, ADT7473_REG_VENDOR); - if (vendor != ADT7473_VENDOR) - return -ENODEV; + vendor = i2c_smbus_read_byte_data(client, ADT7473_REG_VENDOR); + if (vendor != ADT7473_VENDOR) + return -ENODEV; - device = i2c_smbus_read_byte_data(client, ADT7473_REG_DEVICE); - if (device != ADT7473_DEVICE) - return -ENODEV; + device = i2c_smbus_read_byte_data(client, ADT7473_REG_DEVICE); + if (device != ADT7473_DEVICE) + return -ENODEV; - revision = i2c_smbus_read_byte_data(client, - ADT7473_REG_REVISION); - if (revision != ADT7473_REV_68 && revision != ADT7473_REV_69) - return -ENODEV; - } else - dev_dbg(&adapter->dev, "detection forced\n"); + revision = i2c_smbus_read_byte_data(client, ADT7473_REG_REVISION); + if (revision != ADT7473_REV_68 && revision != ADT7473_REV_69) + return -ENODEV; strlcpy(info->type, "adt7473", I2C_NAME_SIZE); diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index b5a9519..41d3e38 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -974,14 +974,12 @@ static int adt7475_detect(struct i2c_client *client, int kind, if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind <= 0) { - 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); - 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); + return -ENODEV; } strlcpy(info->type, adt7475_id[0].name, I2C_NAME_SIZE); diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c index 8acf829..480f80e 100644 --- a/drivers/hwmon/asb100.c +++ b/drivers/hwmon/asb100.c @@ -701,6 +701,7 @@ static int asb100_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int val1, val2; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { pr_debug("asb100.o: detect failed, " @@ -708,50 +709,30 @@ static int asb100_detect(struct i2c_client *client, int kind, return -ENODEV; } - /* The chip may be stuck in some other bank than bank 0. This may - make reading other information impossible. Specify a force=... or - force_*=... parameter, and the chip will be reset to the right - bank. */ - if (kind < 0) { - - int val1 = i2c_smbus_read_byte_data(client, ASB100_REG_BANK); - int val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN); - - /* If we're in bank 0 */ - if ((!(val1 & 0x07)) && - /* Check for ASB100 ID (low byte) */ - (((!(val1 & 0x80)) && (val2 != 0x94)) || - /* Check for ASB100 ID (high byte ) */ - ((val1 & 0x80) && (val2 != 0x06)))) { - pr_debug("asb100.o: detect failed, " - "bad chip id 0x%02x!\n", val2); - return -ENODEV; - } + val1 = i2c_smbus_read_byte_data(client, ASB100_REG_BANK); + val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN); - } /* kind < 0 */ + /* If we're in bank 0 */ + if ((!(val1 & 0x07)) && + /* Check for ASB100 ID (low byte) */ + (((!(val1 & 0x80)) && (val2 != 0x94)) || + /* Check for ASB100 ID (high byte ) */ + ((val1 & 0x80) && (val2 != 0x06)))) { + pr_debug("asb100: detect failed, bad chip id 0x%02x!\n", val2); + return -ENODEV; + } - /* We have either had a force parameter, or we have already detected - Winbond. Put it now into bank 0 and Vendor ID High Byte */ + /* Put it now into bank 0 and Vendor ID High Byte */ i2c_smbus_write_byte_data(client, ASB100_REG_BANK, (i2c_smbus_read_byte_data(client, ASB100_REG_BANK) & 0x78) | 0x80); /* Determine the chip type. */ - if (kind <= 0) { - int val1 = i2c_smbus_read_byte_data(client, ASB100_REG_WCHIPID); - int val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN); - - if ((val1 == 0x31) && (val2 == 0x06)) - kind = asb100; - else { - if (kind == 0) - dev_warn(&adapter->dev, "ignoring " - "'force' parameter for unknown chip " - "at adapter %d, address 0x%02x.\n", - i2c_adapter_id(adapter), client->addr); - return -ENODEV; - } - } + val1 = i2c_smbus_read_byte_data(client, ASB100_REG_WCHIPID); + val2 = i2c_smbus_read_byte_data(client, ASB100_REG_CHIPMAN); + + if (val1 != 0x31 || val2 != 0x06) + return -ENODEV; strlcpy(info->type, "asb100", I2C_NAME_SIZE); diff --git a/drivers/hwmon/dme1737.c b/drivers/hwmon/dme1737.c index 27d6257..4377bb0 100644 --- a/drivers/hwmon/dme1737.c +++ b/drivers/hwmon/dme1737.c @@ -2220,33 +2220,23 @@ static int dme1737_i2c_detect(struct i2c_client *client, int kind, return -ENODEV; } - /* A negative kind means that the driver was loaded with no force - * parameter (default), so we must identify the chip. */ - if (kind < 0) { - company = i2c_smbus_read_byte_data(client, DME1737_REG_COMPANY); - verstep = i2c_smbus_read_byte_data(client, DME1737_REG_VERSTEP); - - if (company == DME1737_COMPANY_SMSC && - (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) { - kind = dme1737; - } else if (company == DME1737_COMPANY_SMSC && - verstep == SCH5027_VERSTEP) { - kind = sch5027; - } else { - return -ENODEV; - } - } + company = i2c_smbus_read_byte_data(client, DME1737_REG_COMPANY); + verstep = i2c_smbus_read_byte_data(client, DME1737_REG_VERSTEP); - if (kind == sch5027) { + if (company == DME1737_COMPANY_SMSC && + verstep == SCH5027_VERSTEP) { name = "sch5027"; - } else { - kind = dme1737; + + } else if (company == DME1737_COMPANY_SMSC && + (verstep & DME1737_VERSTEP_MASK) == DME1737_VERSTEP) { name = "dme1737"; + } else { + return -ENODEV; } dev_info(dev, "Found a %s chip at 0x%02x (rev 0x%02x).\n", - kind == sch5027 ? "SCH5027" : "DME1737", client->addr, - verstep); + verstep == SCH5027_VERSTEP ? "SCH5027" : "DME1737", + client->addr, verstep); strlcpy(info->type, name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c index 53f88f5..2a4c6a0 100644 --- a/drivers/hwmon/ds1621.c +++ b/drivers/hwmon/ds1621.c @@ -237,20 +237,16 @@ static int ds1621_detect(struct i2c_client *client, int kind, return -ENODEV; /* Now, we do the remaining detection. It is lousy. */ - if (kind < 0) { - /* The NVB bit should be low if no EEPROM write has been - requested during the latest 10ms, which is highly - improbable in our case. */ - conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); - if (conf < 0 || conf & DS1621_REG_CONFIG_NVB) + /* The NVB bit should be low if no EEPROM write has been requested + during the latest 10ms, which is highly improbable in our case. */ + conf = i2c_smbus_read_byte_data(client, DS1621_REG_CONF); + if (conf < 0 || conf & DS1621_REG_CONFIG_NVB) + return -ENODEV; + /* The 7 lowest bits of a temperature should always be 0. */ + for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) { + temp = i2c_smbus_read_word_data(client, DS1621_REG_TEMP[i]); + if (temp < 0 || (temp & 0x7f00)) return -ENODEV; - /* The 7 lowest bits of a temperature should always be 0. */ - for (i = 0; i < ARRAY_SIZE(DS1621_REG_TEMP); i++) { - temp = i2c_smbus_read_word_data(client, - DS1621_REG_TEMP[i]); - if (temp < 0 || (temp & 0x7f00)) - return -ENODEV; - } } strlcpy(info->type, "ds1621", I2C_NAME_SIZE); diff --git a/drivers/hwmon/f75375s.c b/drivers/hwmon/f75375s.c index e2107e5..40dfbcd 100644 --- a/drivers/hwmon/f75375s.c +++ b/drivers/hwmon/f75375s.c @@ -681,30 +681,20 @@ static int f75375_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - u8 version = 0; - const char *name = ""; - - if (kind < 0) { - u16 vendid = f75375_read16(client, F75375_REG_VENDOR); - u16 chipid = f75375_read16(client, F75375_CHIP_ID); - version = f75375_read8(client, F75375_REG_VERSION); - if (chipid == 0x0306 && vendid == 0x1934) { - kind = f75375; - } else if (chipid == 0x0204 && vendid == 0x1934) { - kind = f75373; - } else { - dev_err(&adapter->dev, - "failed,%02X,%02X,%02X\n", - chipid, version, vendid); - return -ENODEV; - } - } + u16 vendid, chipid; + u8 version; + const char *name; - if (kind == f75375) { + vendid = f75375_read16(client, F75375_REG_VENDOR); + chipid = f75375_read16(client, F75375_CHIP_ID); + if (chipid == 0x0306 && vendid == 0x1934) name = "f75375"; - } else if (kind == f75373) { + else if (chipid == 0x0204 && vendid == 0x1934) name = "f75373"; - } + else + return -ENODEV; + + version = f75375_read8(client, F75375_REG_VERSION); dev_info(&adapter->dev, "found %s version: %02X\n", name, version); strlcpy(info->type, name, I2C_NAME_SIZE); diff --git a/drivers/hwmon/fschmd.c b/drivers/hwmon/fschmd.c index da1b1f9..281829c 100644 --- a/drivers/hwmon/fschmd.c +++ b/drivers/hwmon/fschmd.c @@ -1000,43 +1000,38 @@ static void fschmd_dmi_decode(const struct dmi_header *header, void *dummy) } } -static int fschmd_detect(struct i2c_client *client, int kind, +static int fschmd_detect(struct i2c_client *client, int _kind, struct i2c_board_info *info) { + enum chips kind; struct i2c_adapter *adapter = client->adapter; + char id[4]; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; /* Detect & Identify the chip */ - if (kind <= 0) { - char id[4]; - - id[0] = i2c_smbus_read_byte_data(client, - FSCHMD_REG_IDENT_0); - id[1] = i2c_smbus_read_byte_data(client, - FSCHMD_REG_IDENT_1); - id[2] = i2c_smbus_read_byte_data(client, - FSCHMD_REG_IDENT_2); - id[3] = '\0'; - - if (!strcmp(id, "PEG")) - kind = fscpos; - else if (!strcmp(id, "HER")) - kind = fscher; - else if (!strcmp(id, "SCY")) - kind = fscscy; - else if (!strcmp(id, "HRC")) - kind = fschrc; - else if (!strcmp(id, "HMD")) - kind = fschmd; - else if (!strcmp(id, "HDS")) - kind = fschds; - else if (!strcmp(id, "SYL")) - kind = fscsyl; - else - return -ENODEV; - } + id[0] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_0); + id[1] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_1); + id[2] = i2c_smbus_read_byte_data(client, FSCHMD_REG_IDENT_2); + id[3] = '\0'; + + if (!strcmp(id, "PEG")) + kind = fscpos; + else if (!strcmp(id, "HER")) + kind = fscher; + else if (!strcmp(id, "SCY")) + kind = fscscy; + else if (!strcmp(id, "HRC")) + kind = fschrc; + else if (!strcmp(id, "HMD")) + kind = fschmd; + else if (!strcmp(id, "HDS")) + kind = fschds; + else if (!strcmp(id, "SYL")) + kind = fscsyl; + else + return -ENODEV; strlcpy(info->type, fschmd_id[kind - 1].name, I2C_NAME_SIZE); diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c index 7820df4..1d69458a 100644 --- a/drivers/hwmon/gl518sm.c +++ b/drivers/hwmon/gl518sm.c @@ -488,36 +488,21 @@ static int gl518_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - int i; + int rev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; /* Now, we do the remaining detection. */ - - if (kind < 0) { - if ((gl518_read_value(client, GL518_REG_CHIP_ID) != 0x80) - || (gl518_read_value(client, GL518_REG_CONF) & 0x80)) - return -ENODEV; - } + if ((gl518_read_value(client, GL518_REG_CHIP_ID) != 0x80) + || (gl518_read_value(client, GL518_REG_CONF) & 0x80)) + return -ENODEV; /* Determine the chip type. */ - if (kind <= 0) { - i = gl518_read_value(client, GL518_REG_REVISION); - if (i == 0x00) { - kind = gl518sm_r00; - } else if (i == 0x80) { - kind = gl518sm_r80; - } else { - if (kind <= 0) - dev_info(&adapter->dev, - "Ignoring 'force' parameter for unknown " - "chip at adapter %d, address 0x%02x\n", - i2c_adapter_id(adapter), client->addr); - return -ENODEV; - } - } + rev = gl518_read_value(client, GL518_REG_REVISION); + if (rev != 0x00 && rev != 0x80) + return -ENODEV; strlcpy(info->type, "gl518sm", I2C_NAME_SIZE); diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c index 19616f2..92b5720 100644 --- a/drivers/hwmon/gl520sm.c +++ b/drivers/hwmon/gl520sm.c @@ -691,13 +691,11 @@ static int gl520_detect(struct i2c_client *client, int kind, return -ENODEV; /* Determine the chip type. */ - if (kind < 0) { - if ((gl520_read_value(client, GL520_REG_CHIP_ID) != 0x20) || - ((gl520_read_value(client, GL520_REG_REVISION) & 0x7f) != 0x00) || - ((gl520_read_value(client, GL520_REG_CONF) & 0x80) != 0x00)) { - dev_dbg(&client->dev, "Unknown chip type, skipping\n"); - return -ENODEV; - } + if ((gl520_read_value(client, GL520_REG_CHIP_ID) != 0x20) || + ((gl520_read_value(client, GL520_REG_REVISION) & 0x7f) != 0x00) || + ((gl520_read_value(client, GL520_REG_CONF) & 0x80) != 0x00)) { + dev_dbg(&client->dev, "Unknown chip type, skipping\n"); + return -ENODEV; } strlcpy(info->type, "gl520sm", I2C_NAME_SIZE); diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index 3195a26..5da66ab 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c @@ -427,40 +427,34 @@ static int lm63_detect(struct i2c_client *new_client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; + u8 man_id, chip_id, reg_config1, reg_config2; + u8 reg_alert_status, reg_alert_mask; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - if (kind < 0) { /* must identify */ - u8 man_id, chip_id, reg_config1, reg_config2; - u8 reg_alert_status, reg_alert_mask; - - man_id = i2c_smbus_read_byte_data(new_client, - LM63_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(new_client, - LM63_REG_CHIP_ID); - reg_config1 = i2c_smbus_read_byte_data(new_client, - LM63_REG_CONFIG1); - reg_config2 = i2c_smbus_read_byte_data(new_client, - LM63_REG_CONFIG2); - reg_alert_status = i2c_smbus_read_byte_data(new_client, - LM63_REG_ALERT_STATUS); - reg_alert_mask = i2c_smbus_read_byte_data(new_client, - LM63_REG_ALERT_MASK); - - if (man_id == 0x01 /* National Semiconductor */ - && chip_id == 0x41 /* LM63 */ - && (reg_config1 & 0x18) == 0x00 - && (reg_config2 & 0xF8) == 0x00 - && (reg_alert_status & 0x20) == 0x00 - && (reg_alert_mask & 0xA4) == 0xA4) { - kind = lm63; - } else { /* failed */ - dev_dbg(&adapter->dev, "Unsupported chip " - "(man_id=0x%02X, chip_id=0x%02X).\n", - man_id, chip_id); - return -ENODEV; - } + man_id = i2c_smbus_read_byte_data(new_client, LM63_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(new_client, LM63_REG_CHIP_ID); + + reg_config1 = i2c_smbus_read_byte_data(new_client, + LM63_REG_CONFIG1); + reg_config2 = i2c_smbus_read_byte_data(new_client, + LM63_REG_CONFIG2); + reg_alert_status = i2c_smbus_read_byte_data(new_client, + LM63_REG_ALERT_STATUS); + reg_alert_mask = i2c_smbus_read_byte_data(new_client, + LM63_REG_ALERT_MASK); + + if (man_id != 0x01 /* National Semiconductor */ + || chip_id != 0x41 /* LM63 */ + || (reg_config1 & 0x18) != 0x00 + || (reg_config2 & 0xF8) != 0x00 + || (reg_alert_status & 0x20) != 0x00 + || (reg_alert_mask & 0xA4) != 0xA4) { + dev_dbg(&adapter->dev, + "Unsupported chip (man_id=0x%02X, chip_id=0x%02X)\n", + man_id, chip_id); + return -ENODEV; } strlcpy(info->type, "lm63", I2C_NAME_SIZE); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c index 55bd87c..e392548 100644 --- a/drivers/hwmon/lm75.c +++ b/drivers/hwmon/lm75.c @@ -239,6 +239,7 @@ static int lm75_detect(struct i2c_client *new_client, int kind, { struct i2c_adapter *adapter = new_client->adapter; int i; + int cur, conf, hyst, os; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) @@ -251,40 +252,35 @@ static int lm75_detect(struct i2c_client *new_client, int kind, The cycling+unused addresses combination is not tested, since it would significantly slow the detection down and would hardly add any value. */ - if (kind < 0) { - int cur, conf, hyst, os; - - /* Unused addresses */ - cur = i2c_smbus_read_word_data(new_client, 0); - conf = i2c_smbus_read_byte_data(new_client, 1); - hyst = i2c_smbus_read_word_data(new_client, 2); - if (i2c_smbus_read_word_data(new_client, 4) != hyst - || i2c_smbus_read_word_data(new_client, 5) != hyst - || i2c_smbus_read_word_data(new_client, 6) != hyst - || i2c_smbus_read_word_data(new_client, 7) != hyst) - return -ENODEV; - os = i2c_smbus_read_word_data(new_client, 3); - if (i2c_smbus_read_word_data(new_client, 4) != os - || i2c_smbus_read_word_data(new_client, 5) != os - || i2c_smbus_read_word_data(new_client, 6) != os - || i2c_smbus_read_word_data(new_client, 7) != os) - return -ENODEV; - /* Unused bits */ - if (conf & 0xe0) - return -ENODEV; + /* Unused addresses */ + cur = i2c_smbus_read_word_data(new_client, 0); + conf = i2c_smbus_read_byte_data(new_client, 1); + hyst = i2c_smbus_read_word_data(new_client, 2); + if (i2c_smbus_read_word_data(new_client, 4) != hyst + || i2c_smbus_read_word_data(new_client, 5) != hyst + || i2c_smbus_read_word_data(new_client, 6) != hyst + || i2c_smbus_read_word_data(new_client, 7) != hyst) + return -ENODEV; + os = i2c_smbus_read_word_data(new_client, 3); + if (i2c_smbus_read_word_data(new_client, 4) != os + || i2c_smbus_read_word_data(new_client, 5) != os + || i2c_smbus_read_word_data(new_client, 6) != os + || i2c_smbus_read_word_data(new_client, 7) != os) + return -ENODEV; - /* Addresses cycling */ - for (i = 8; i < 0xff; i += 8) - if (i2c_smbus_read_byte_data(new_client, i + 1) != conf - || i2c_smbus_read_word_data(new_client, i + 2) != hyst - || i2c_smbus_read_word_data(new_client, i + 3) != os) - return -ENODEV; + /* Unused bits */ + if (conf & 0xe0) + return -ENODEV; + + /* Addresses cycling */ + for (i = 8; i < 0xff; i += 8) { + if (i2c_smbus_read_byte_data(new_client, i + 1) != conf + || i2c_smbus_read_word_data(new_client, i + 2) != hyst + || i2c_smbus_read_word_data(new_client, i + 3) != os) + return -ENODEV; } - /* NOTE: we treat "force=..." and "force_lm75=..." the same. - * Only new-style driver binding distinguishes chip types. - */ strlcpy(info->type, "lm75", I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c index f7e7016..5978291 100644 --- a/drivers/hwmon/lm78.c +++ b/drivers/hwmon/lm78.c @@ -576,52 +576,34 @@ static int lm78_i2c_detect(struct i2c_client *client, int kind, if (isa) mutex_lock(&isa->update_lock); - if (kind < 0) { - if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80) - || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR) - != address) - goto err_nodev; - - /* Explicitly prevent the misdetection of Winbond chips */ - i = i2c_smbus_read_byte_data(client, 0x4f); - if (i == 0xa3 || i == 0x5c) - goto err_nodev; - } + if ((i2c_smbus_read_byte_data(client, LM78_REG_CONFIG) & 0x80) + || i2c_smbus_read_byte_data(client, LM78_REG_I2C_ADDR) != address) + goto err_nodev; + + /* Explicitly prevent the misdetection of Winbond chips */ + i = i2c_smbus_read_byte_data(client, 0x4f); + if (i == 0xa3 || i == 0x5c) + goto err_nodev; /* Determine the chip type. */ - if (kind <= 0) { - i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID); - if (i == 0x00 || i == 0x20 /* LM78 */ - || i == 0x40) /* LM78-J */ - kind = lm78; - else if ((i & 0xfe) == 0xc0) - kind = lm79; - else { - if (kind == 0) - dev_warn(&adapter->dev, "Ignoring 'force' " - "parameter for unknown chip at " - "adapter %d, address 0x%02x\n", - i2c_adapter_id(adapter), address); - goto err_nodev; - } + i = i2c_smbus_read_byte_data(client, LM78_REG_CHIPID); + if (i == 0x00 || i == 0x20 /* LM78 */ + || i == 0x40) /* LM78-J */ + client_name = "lm78"; + else if ((i & 0xfe) == 0xc0) + client_name = "lm79"; + else + goto err_nodev; - if (lm78_alias_detect(client, i)) { - dev_dbg(&adapter->dev, "Device at 0x%02x appears to " - "be the same as ISA device\n", address); - goto err_nodev; - } + if (lm78_alias_detect(client, i)) { + dev_dbg(&adapter->dev, "Device at 0x%02x appears to " + "be the same as ISA device\n", address); + goto err_nodev; } if (isa) mutex_unlock(&isa->update_lock); - switch (kind) { - case lm79: - client_name = "lm79"; - break; - default: - client_name = "lm78"; - } strlcpy(info->type, client_name, I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c index 2e4a3ce..4929b18 100644 --- a/drivers/hwmon/lm87.c +++ b/drivers/hwmon/lm87.c @@ -666,37 +666,32 @@ static int lm87_detect(struct i2c_client *new_client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; - static const char *names[] = { "lm87", "adm1024" }; + const char *name; + u8 cid, rev; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* Default to an LM87 if forced */ - if (kind == 0) - kind = lm87; + if (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80) + return -ENODEV; /* Now, we do the remaining detection. */ - if (kind < 0) { - u8 cid = lm87_read_value(new_client, LM87_REG_COMPANY_ID); - u8 rev = lm87_read_value(new_client, LM87_REG_REVISION); - - if (cid == 0x02 /* National Semiconductor */ - && (rev >= 0x01 && rev <= 0x08)) - kind = lm87; - else if (cid == 0x41 /* Analog Devices */ - && (rev & 0xf0) == 0x10) - kind = adm1024; - - if (kind < 0 - || (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80)) { - dev_dbg(&adapter->dev, - "LM87 detection failed at 0x%02x.\n", - new_client->addr); - return -ENODEV; - } + cid = lm87_read_value(new_client, LM87_REG_COMPANY_ID); + rev = lm87_read_value(new_client, LM87_REG_REVISION); + + if (cid == 0x02 /* National Semiconductor */ + && (rev >= 0x01 && rev <= 0x08)) + name = "lm87"; + else if (cid == 0x41 /* Analog Devices */ + && (rev & 0xf0) == 0x10) + name = "adm1024"; + else { + dev_dbg(&adapter->dev, "LM87 detection failed at 0x%02x\n", + new_client->addr); + return -ENODEV; } - strlcpy(info->type, names[kind - 1], I2C_NAME_SIZE); + strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c index b2e00c5..47ac698 100644 --- a/drivers/hwmon/lm92.c +++ b/drivers/hwmon/lm92.c @@ -323,31 +323,22 @@ static int lm92_detect(struct i2c_client *new_client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = new_client->adapter; + u8 config; + u16 man_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) return -ENODEV; - /* A negative kind means that the driver was loaded with no force - parameter (default), so we must identify the chip. */ - if (kind < 0) { - u8 config = i2c_smbus_read_byte_data(new_client, - LM92_REG_CONFIG); - u16 man_id = i2c_smbus_read_word_data(new_client, - LM92_REG_MAN_ID); - - if ((config & 0xe0) == 0x00 - && man_id == 0x0180) { - pr_info("lm92: Found National Semiconductor LM92 chip\n"); - kind = lm92; - } else - if (max6635_check(new_client)) { - pr_info("lm92: Found Maxim MAX6635 chip\n"); - kind = lm92; /* No separate prefix */ - } - else - return -ENODEV; - } + config = i2c_smbus_read_byte_data(new_client, LM92_REG_CONFIG); + man_id = i2c_smbus_read_word_data(new_client, LM92_REG_MAN_ID); + + if ((config & 0xe0) == 0x00 && man_id == 0x0180) + pr_info("lm92: Found National Semiconductor LM92 chip\n"); + else if (max6635_check(new_client)) + pr_info("lm92: Found Maxim MAX6635 chip\n"); + else + return -ENODEV; strlcpy(info->type, "lm92", I2C_NAME_SIZE); diff --git a/drivers/hwmon/lm93.c b/drivers/hwmon/lm93.c index fc36cad..495e7ce 100644 --- a/drivers/hwmon/lm93.c +++ b/drivers/hwmon/lm93.c @@ -2505,34 +2505,24 @@ static int lm93_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + int mfr, ver; if (!i2c_check_functionality(adapter, LM93_SMBUS_FUNC_MIN)) return -ENODEV; /* detection */ - if (kind < 0) { - int mfr = lm93_read_byte(client, LM93_REG_MFR_ID); - - if (mfr != 0x01) { - dev_dbg(&adapter->dev,"detect failed, " - "bad manufacturer id 0x%02x!\n", mfr); - return -ENODEV; - } + mfr = lm93_read_byte(client, LM93_REG_MFR_ID); + if (mfr != 0x01) { + dev_dbg(&adapter->dev, + "detect failed, bad manufacturer id 0x%02x!\n", mfr); + return -ENODEV; } - if (kind <= 0) { - int ver = lm93_read_byte(client, LM93_REG_VER); - - if ((ver == LM93_MFR_ID) || (ver == LM93_MFR_ID_PROTOTYPE)) { - kind = lm93; - } else { - dev_dbg(&adapter->dev,"detect failed, " - "bad version id 0x%02x!\n", ver); - if (kind == 0) - dev_dbg(&adapter->dev, - "(ignored 'force' parameter)\n"); - return -ENODEV; - } + ver = lm93_read_byte(client, LM93_REG_VER); + if (ver != LM93_MFR_ID && ver != LM93_MFR_ID_PROTOTYPE) { + dev_dbg(&adapter->dev, + "detect failed, bad version id 0x%02x!\n", ver); + return -ENODEV; } strlcpy(info->type, "lm93", I2C_NAME_SIZE); diff --git a/drivers/hwmon/lm95241.c b/drivers/hwmon/lm95241.c index e34f9e4..906b896 100644 --- a/drivers/hwmon/lm95241.c +++ b/drivers/hwmon/lm95241.c @@ -315,51 +315,23 @@ static int lm95241_detect(struct i2c_client *new_client, int kind, { struct i2c_adapter *adapter = new_client->adapter; int address = new_client->addr; - const char *name = ""; + const char *name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip. A zero kind means that - * the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - */ - if (kind < 0) { /* detection */ - if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID) - != MANUFACTURER_ID) - || (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID) - < DEFAULT_REVISION)) { - dev_dbg(&adapter->dev, - "LM95241 detection failed at 0x%02x.\n", - address); - return -ENODEV; - } - } - - if (kind <= 0) { /* identification */ - if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID) - == MANUFACTURER_ID) - && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID) - >= DEFAULT_REVISION)) { - - kind = lm95241; - - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, "Unsupported chip\n"); - return -ENODEV; - } - } + if ((i2c_smbus_read_byte_data(new_client, LM95241_REG_R_MAN_ID) + == MANUFACTURER_ID) + && (i2c_smbus_read_byte_data(new_client, LM95241_REG_R_CHIP_ID) + >= DEFAULT_REVISION)) { + name = "lm95241"; + } else { + dev_dbg(&adapter->dev, "LM95241 detection failed at 0x%02x\n", + address); + return -ENODEV; } /* Fill the i2c board info */ - if (kind == lm95241) - name = "lm95241"; strlcpy(info->type, name, I2C_NAME_SIZE); return 0; } diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c index 7897754..7fcf5ff 100644 --- a/drivers/hwmon/max1619.c +++ b/drivers/hwmon/max1619.c @@ -226,58 +226,34 @@ static const struct attribute_group max1619_group = { */ /* Return 0 if detection is successful, -ENODEV otherwise */ -static int max1619_detect(struct i2c_client *new_client, int kind, +static int max1619_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { - struct i2c_adapter *adapter = new_client->adapter; - u8 reg_config=0, reg_convrate=0, reg_status=0; + struct i2c_adapter *adapter = client->adapter; + u8 reg_config, reg_convrate, reg_status, man_id, chip_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip. A zero kind means that - * the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - */ - if (kind < 0) { /* detection */ - reg_config = i2c_smbus_read_byte_data(new_client, - MAX1619_REG_R_CONFIG); - reg_convrate = i2c_smbus_read_byte_data(new_client, - MAX1619_REG_R_CONVRATE); - reg_status = i2c_smbus_read_byte_data(new_client, - MAX1619_REG_R_STATUS); - if ((reg_config & 0x03) != 0x00 - || reg_convrate > 0x07 || (reg_status & 0x61 ) !=0x00) { - dev_dbg(&adapter->dev, - "MAX1619 detection failed at 0x%02x.\n", - new_client->addr); - return -ENODEV; - } + /* detection */ + reg_config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); + reg_convrate = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONVRATE); + reg_status = i2c_smbus_read_byte_data(client, MAX1619_REG_R_STATUS); + if ((reg_config & 0x03) != 0x00 + || reg_convrate > 0x07 || (reg_status & 0x61) != 0x00) { + dev_dbg(&adapter->dev, "MAX1619 detection failed at 0x%02x\n", + client->addr); + return -ENODEV; } - if (kind <= 0) { /* identification */ - u8 man_id, chip_id; - - man_id = i2c_smbus_read_byte_data(new_client, - MAX1619_REG_R_MAN_ID); - chip_id = i2c_smbus_read_byte_data(new_client, - MAX1619_REG_R_CHIP_ID); - - if ((man_id == 0x4D) && (chip_id == 0x04)) - kind = max1619; - - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X).\n", man_id, chip_id); - return -ENODEV; - } + /* identification */ + man_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_MAN_ID); + chip_id = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CHIP_ID); + if (man_id != 0x4D || chip_id != 0x04) { + dev_info(&adapter->dev, + "Unsupported chip (man_id=0x%02X, chip_id=0x%02X).\n", + man_id, chip_id); + return -ENODEV; } strlcpy(info->type, "max1619", I2C_NAME_SIZE); diff --git a/drivers/hwmon/max6650.c b/drivers/hwmon/max6650.c index 58f66be..1da561e 100644 --- a/drivers/hwmon/max6650.c +++ b/drivers/hwmon/max6650.c @@ -534,7 +534,7 @@ static int max6650_detect(struct i2c_client *client, int kind, struct i2c_adapter *adapter = client->adapter; int address = client->addr; - dev_dbg(&adapter->dev, "max6650_detect called, kind = %d\n", kind); + dev_dbg(&adapter->dev, "max6650_detect called\n"); if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { dev_dbg(&adapter->dev, "max6650: I2C bus doesn't support " @@ -542,23 +542,7 @@ static int max6650_detect(struct i2c_client *client, int kind, return -ENODEV; } - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip (actually there is only - * one possible kind of chip for now, max6650). A zero kind means that - * the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - * - * Currently I can find no way to distinguish between a MAX6650 and - * a MAX6651. This driver has only been tried on the former. - */ - - if ((kind < 0) && - ( (i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG) & 0xC0) + if (((i2c_smbus_read_byte_data(client, MAX6650_REG_CONFIG) & 0xC0) ||(i2c_smbus_read_byte_data(client, MAX6650_REG_GPIO_STAT) & 0xE0) ||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM_EN) & 0xE0) ||(i2c_smbus_read_byte_data(client, MAX6650_REG_ALARM) & 0xE0) diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c index 8bb5cb5..4d88c04 100644 --- a/drivers/hwmon/smsc47m192.c +++ b/drivers/hwmon/smsc47m192.c @@ -491,24 +491,22 @@ static int smsc47m192_detect(struct i2c_client *client, int kind, return -ENODEV; /* Detection criteria from sensors_detect script */ - if (kind < 0) { - if (i2c_smbus_read_byte_data(client, + version = i2c_smbus_read_byte_data(client, SMSC47M192_REG_VERSION); + if (i2c_smbus_read_byte_data(client, SMSC47M192_REG_COMPANY_ID) == 0x55 - && ((version = i2c_smbus_read_byte_data(client, - SMSC47M192_REG_VERSION)) & 0xf0) == 0x20 - && (i2c_smbus_read_byte_data(client, + && (version & 0xf0) == 0x20 + && (i2c_smbus_read_byte_data(client, SMSC47M192_REG_VID) & 0x70) == 0x00 - && (i2c_smbus_read_byte_data(client, + && (i2c_smbus_read_byte_data(client, SMSC47M192_REG_VID4) & 0xfe) == 0x80) { - dev_info(&adapter->dev, - "found SMSC47M192 or compatible, " - "version 2, stepping A%d\n", version & 0x0f); - } else { - dev_dbg(&adapter->dev, - "SMSC47M192 detection failed at 0x%02x\n", - client->addr); - return -ENODEV; - } + dev_info(&adapter->dev, + "found SMSC47M192 or compatible, " + "version 2, stepping A%d\n", version & 0x0f); + } else { + dev_dbg(&adapter->dev, + "SMSC47M192 detection failed at 0x%02x\n", + client->addr); + return -ENODEV; } strlcpy(info->type, "smsc47m192", I2C_NAME_SIZE); diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c index 7d97431..1db9bc8 100644 --- a/drivers/hwmon/thmc50.c +++ b/drivers/hwmon/thmc50.c @@ -289,7 +289,6 @@ static int thmc50_detect(struct i2c_client *client, int kind, unsigned revision; unsigned config; struct i2c_adapter *adapter = client->adapter; - int err = 0; const char *type_name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { @@ -301,31 +300,13 @@ static int thmc50_detect(struct i2c_client *client, int kind, pr_debug("thmc50: Probing for THMC50 at 0x%2X on bus %d\n", client->addr, i2c_adapter_id(client->adapter)); - /* Now, we do the remaining detection. */ company = i2c_smbus_read_byte_data(client, THMC50_REG_COMPANY_ID); revision = i2c_smbus_read_byte_data(client, THMC50_REG_DIE_CODE); config = i2c_smbus_read_byte_data(client, THMC50_REG_CONF); + if (revision < 0xc0 || (config & 0x10)) + return -ENODEV; - if (kind == 0) - kind = thmc50; - else if (kind < 0) { - err = -ENODEV; - if (revision >= 0xc0 && ((config & 0x10) == 0)) { - if (company == 0x49) { - kind = thmc50; - err = 0; - } else if (company == 0x41) { - kind = adm1022; - err = 0; - } - } - } - if (err == -ENODEV) { - pr_debug("thmc50: Detection of THMC50/ADM1022 failed\n"); - return err; - } - - if (kind == adm1022) { + if (company == 0x41) { int id = i2c_adapter_id(client->adapter); int i; @@ -340,9 +321,13 @@ static int thmc50_detect(struct i2c_client *client, int kind, config); break; } - } else { + } else if (company == 0x49) { type_name = "thmc50"; + } else { + pr_debug("thmc50: Detection of THMC50/ADM1022 failed\n"); + return -ENODEV; } + pr_debug("thmc50: Detected %s (version %x, revision %x)\n", type_name, (revision >> 4) - 0xc, revision & 0xf); diff --git a/drivers/hwmon/w83793.c b/drivers/hwmon/w83793.c index 47dd398..80a2191 100644 --- a/drivers/hwmon/w83793.c +++ b/drivers/hwmon/w83793.c @@ -1164,7 +1164,7 @@ ERROR_SC_0: static int w83793_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { - u8 tmp, bank; + u8 tmp, bank, chip_id; struct i2c_adapter *adapter = client->adapter; unsigned short address = client->addr; @@ -1174,44 +1174,27 @@ static int w83793_detect(struct i2c_client *client, int kind, bank = i2c_smbus_read_byte_data(client, W83793_REG_BANKSEL); - if (kind < 0) { - tmp = bank & 0x80 ? 0x5c : 0xa3; - /* Check Winbond vendor ID */ - if (tmp != i2c_smbus_read_byte_data(client, - W83793_REG_VENDORID)) { - pr_debug("w83793: Detection failed at check " - "vendor id\n"); - return -ENODEV; - } - - /* If Winbond chip, address of chip and W83793_REG_I2C_ADDR - should match */ - if ((bank & 0x07) == 0 - && i2c_smbus_read_byte_data(client, W83793_REG_I2C_ADDR) != - (address << 1)) { - pr_debug("w83793: Detection failed at check " - "i2c addr\n"); - return -ENODEV; - } - + tmp = bank & 0x80 ? 0x5c : 0xa3; + /* Check Winbond vendor ID */ + if (tmp != i2c_smbus_read_byte_data(client, W83793_REG_VENDORID)) { + pr_debug("w83793: Detection failed at check vendor id\n"); + return -ENODEV; } - /* We have either had a force parameter, or we have already detected the - Winbond. Determine the chip type now */ - - if (kind <= 0) { - if (0x7b == i2c_smbus_read_byte_data(client, - W83793_REG_CHIPID)) { - kind = w83793; - } else { - if (kind == 0) - dev_warn(&adapter->dev, "w83793: Ignoring " - "'force' parameter for unknown chip " - "at address 0x%02x\n", address); - return -ENODEV; - } + /* If Winbond chip, address of chip and W83793_REG_I2C_ADDR + should match */ + if ((bank & 0x07) == 0 + && i2c_smbus_read_byte_data(client, W83793_REG_I2C_ADDR) != + (address << 1)) { + pr_debug("w83793: Detection failed at check i2c addr\n"); + return -ENODEV; } + /* Determine the chip type now */ + chip_id = i2c_smbus_read_byte_data(client, W83793_REG_CHIPID); + if (chip_id != 0x7b) + return -ENODEV; + strlcpy(info->type, "w83793", I2C_NAME_SIZE); return 0; diff --git a/drivers/hwmon/w83l786ng.c b/drivers/hwmon/w83l786ng.c index badca76..27da7d2 100644 --- a/drivers/hwmon/w83l786ng.c +++ b/drivers/hwmon/w83l786ng.c @@ -590,53 +590,31 @@ w83l786ng_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; + u16 man_id; + u8 chip_id; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { return -ENODEV; } - /* - * Now we do the remaining detection. A negative kind means that - * the driver was loaded with no force parameter (default), so we - * must both detect and identify the chip (actually there is only - * one possible kind of chip for now, W83L786NG). A zero kind means - * that the driver was loaded with the force parameter, the detection - * step shall be skipped. A positive kind means that the driver - * was loaded with the force parameter and a given kind of chip is - * requested, so both the detection and the identification steps - * are skipped. - */ - if (kind < 0) { /* detection */ - if (((w83l786ng_read_value(client, - W83L786NG_REG_CONFIG) & 0x80) != 0x00)) { - dev_dbg(&adapter->dev, - "W83L786NG detection failed at 0x%02x.\n", - client->addr); - return -ENODEV; - } + /* Detection */ + if ((w83l786ng_read_value(client, W83L786NG_REG_CONFIG) & 0x80)) { + dev_dbg(&adapter->dev, "W83L786NG detection failed at 0x%02x\n", + client->addr); + return -ENODEV; } - if (kind <= 0) { /* identification */ - u16 man_id; - u8 chip_id; - - man_id = (w83l786ng_read_value(client, - W83L786NG_REG_MAN_ID1) << 8) + - w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2); - chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID); - - if (man_id == 0x5CA3) { /* Winbond */ - if (chip_id == 0x80) { /* W83L786NG */ - kind = w83l786ng; - } - } + /* Identification */ + man_id = (w83l786ng_read_value(client, W83L786NG_REG_MAN_ID1) << 8) + + w83l786ng_read_value(client, W83L786NG_REG_MAN_ID2); + chip_id = w83l786ng_read_value(client, W83L786NG_REG_CHIP_ID); - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%04X, " - "chip_id=0x%02X).\n", man_id, chip_id); - return -ENODEV; - } + if (man_id != 0x5CA3 || /* Winbond */ + chip_id != 0x80) { /* W83L786NG */ + dev_dbg(&adapter->dev, + "Unsupported chip (man_id=0x%04X, chip_id=0x%02X)\n", + man_id, chip_id); + return -ENODEV; } strlcpy(info->type, "w83l786ng", I2C_NAME_SIZE); -- cgit v0.10.2 From d1ebd59a74d764ab74cda8d09589b7ab5f5529fd Mon Sep 17 00:00:00 2001 From: Marc Hulsman Date: Wed, 9 Dec 2009 20:35:58 +0100 Subject: hwmon: (w83791d) Remove experimental dependency The w83791d driver has been in the kernel for a while now, time to remove the EXPERIMENTAL dependency. Signed-off-by: Marc Hulsman Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 87184b5..6e7c30b 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -850,7 +850,7 @@ config SENSORS_W83781D config SENSORS_W83791D tristate "Winbond W83791D" - depends on I2C && EXPERIMENTAL + depends on I2C select HWMON_VID help If you say yes here you get support for the Winbond W83791D chip. -- cgit v0.10.2 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 diff --git a/Documentation/hwmon/mc13783-adc b/Documentation/hwmon/mc13783-adc new file mode 100644 index 0000000..044531a --- /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 6e7c30b..dd69393 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 a24a52d..33c2ee1 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 0000000..883fa81 --- /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 v0.10.2 From f95f0b4ce8cae533c3731e54a51892921db10ffe Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:35:59 +0100 Subject: hwmon: (thmc50) Stop using I2C_CLIENT_MODULE_PARM The thmc50 driver is the last user of I2C_CLIENT_MODULE_PARM, and I would like to get rid of that macro. Signed-off-by: Jean Delvare Tested-by: Krzysztof Helt diff --git a/drivers/hwmon/thmc50.c b/drivers/hwmon/thmc50.c index 1db9bc8..4b79384 100644 --- a/drivers/hwmon/thmc50.c +++ b/drivers/hwmon/thmc50.c @@ -36,7 +36,11 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; /* Insmod parameters */ I2C_CLIENT_INSMOD_2(thmc50, adm1022); -I2C_CLIENT_MODULE_PARM(adm1022_temp3, "List of adapter,address pairs " + +static unsigned short adm1022_temp3[16]; +static unsigned int adm1022_temp3_num; +module_param_array(adm1022_temp3, ushort, &adm1022_temp3_num, 0); +MODULE_PARM_DESC(adm1022_temp3, "List of adapter,address pairs " "to enable 3rd temperature (ADM1022 only)"); /* Many THMC50 constants specified below */ -- cgit v0.10.2 From 66344aa6adc4397f79f134ad122072dbe5abcb48 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Dec 2009 20:35:59 +0100 Subject: hwmon: (f71882fg) Cleanup sysfs attr creation 1/2 This patch makes a number of cleanups to the sysfs attr creation in the f71882fg driver, this is a preparation patch for adding f71889fg support: * Add some comments to explain why some models need separate sysfs attr arrays for in / temp / fan / pwm * Rename a number of sysfs attr arrays to make their function clearer * Move the pwm#_auto_channels_temp attribute from the common to all models fan attr array to the per model auto mode pwm attr arrays, so that all the auto mode pwm attr are grouped together, and thus can be left out on models where we don't support auto pwm mode * Put fan_beep attr in their own array, so that only auto mode pwm attr remain in the per model pwm sysfs attr arrays. * Put the 4th special fan input for the f8000 in its own array, so that only auto mode pwm attr remain in the per model pwm sysfs attr arrays. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 4146105..40dd6cf 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -258,7 +258,9 @@ static struct platform_driver f71882fg_driver = { static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); -/* Temp and in attr for the f71858fg */ +/* Temp and in attr for the f71858fg, the f71858fg is special as it + has its temperature indexes start at 0 (the others start at 1) and + it only has 3 voltage inputs */ static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = { SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), @@ -302,8 +304,8 @@ static struct sensor_device_attribute_2 f71858fg_in_temp_attr[] = { SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 2), }; -/* Temp and in attr common to both the f71862fg and f71882fg */ -static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = { +/* Temp and in attr common to the f71862fg, f71882fg and f71889fg */ +static struct sensor_device_attribute_2 fxxxx_in_temp_attr[] = { SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), SENSOR_ATTR_2(in1_input, S_IRUGO, show_in, NULL, 0, 1), SENSOR_ATTR_2(in2_input, S_IRUGO, show_in, NULL, 0, 2), @@ -371,8 +373,8 @@ static struct sensor_device_attribute_2 f718x2fg_in_temp_attr[] = { SENSOR_ATTR_2(temp3_fault, S_IRUGO, show_temp_fault, NULL, 0, 3), }; -/* Temp and in attr found only on the f71882fg */ -static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { +/* For models with in1 alarm capability */ +static struct sensor_device_attribute_2 fxxxx_in1_alarm_attr[] = { SENSOR_ATTR_2(in1_max, S_IRUGO|S_IWUSR, show_in_max, store_in_max, 0, 1), SENSOR_ATTR_2(in1_beep, S_IRUGO|S_IWUSR, show_in_beep, store_in_beep, @@ -383,6 +385,7 @@ static struct sensor_device_attribute_2 f71882fg_in_temp_attr[] = { /* Temp and in attr for the f8000 Note on the f8000 temp_ovt (crit) is used as max, and temp_high (max) is used as hysteresis value to clear alarms + Also like the f71858fg its temperature indexes start at 0 */ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = { SENSOR_ATTR_2(in0_input, S_IRUGO, show_in, NULL, 0, 0), @@ -435,39 +438,36 @@ static struct sensor_device_attribute_2 fxxxx_fan_attr[] = { store_pwm_enable, 0, 0), SENSOR_ATTR_2(pwm1_interpolate, S_IRUGO|S_IWUSR, show_pwm_interpolate, store_pwm_interpolate, 0, 0), - SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 0), SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 1), SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable, store_pwm_enable, 0, 1), SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR, show_pwm_interpolate, store_pwm_interpolate, 0, 1), - SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 1), SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2), SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, store_pwm_enable, 0, 2), SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR, show_pwm_interpolate, store_pwm_interpolate, 0, 2), - SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, - show_pwm_auto_point_channel, - store_pwm_auto_point_channel, 0, 2), }; -/* Fan / PWM attr for the f71862fg, less pwms and less zones per pwm than the - f71882fg */ -static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { +/* Attr for models which can beep on Fan alarm */ +static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = { SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, store_fan_beep, 0, 0), SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, store_fan_beep, 0, 1), SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, store_fan_beep, 0, 2), +}; +/* PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the + f71858fg / f71882fg / f71889fg */ +static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = { + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 1, 0), @@ -487,6 +487,9 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { SENSOR_ATTR_2(pwm1_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 1, 1), @@ -506,6 +509,9 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { SENSOR_ATTR_2(pwm2_auto_point2_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 1, 2), @@ -526,8 +532,11 @@ static struct sensor_device_attribute_2 f71862fg_fan_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 3, 2), }; -/* Fan / PWM attr common to both the f71882fg and f71858fg */ -static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = { +/* PWM attr common to the f71858fg, f71882fg and f71889fg */ +static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[] = { + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), SENSOR_ATTR_2(pwm1_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 0), @@ -566,6 +575,9 @@ static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = { SENSOR_ATTR_2(pwm1_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), SENSOR_ATTR_2(pwm2_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 1), @@ -604,6 +616,9 @@ static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = { SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), SENSOR_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 2), @@ -644,14 +659,7 @@ static struct sensor_device_attribute_2 f71882fg_f71858fg_fan_attr[] = { }; /* Fan / PWM attr found on the f71882fg but not on the f71858fg */ -static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { - SENSOR_ATTR_2(fan1_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 0), - SENSOR_ATTR_2(fan2_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 1), - SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 2), - +static struct sensor_device_attribute_2 f71882fg_auto_pwm_attr[] = { SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, @@ -707,12 +715,18 @@ static struct sensor_device_attribute_2 f71882fg_fan_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 3, 3), }; -/* Fan / PWM attr for the f8000, zones mapped to temp instead of to pwm! - Also the register block at offset A0 maps to TEMP1 (so our temp2, as the - F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */ +/* Fan attr specific to the f8000 (4th fan input can only measure speed) */ static struct sensor_device_attribute_2 f8000_fan_attr[] = { SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), +}; +/* PWM attr for the f8000, zones mapped to temp instead of to pwm! + Also the register block at offset A0 maps to TEMP1 (so our temp2, as the + F8000 starts counting temps at 0), B0 maps the TEMP2 and C0 maps to TEMP0 */ +static struct sensor_device_attribute_2 f8000_auto_pwm_attr[] = { + SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 0), SENSOR_ATTR_2(temp1_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 2), @@ -751,6 +765,9 @@ static struct sensor_device_attribute_2 f8000_fan_attr[] = { SENSOR_ATTR_2(temp1_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 2), + SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 1), SENSOR_ATTR_2(temp2_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 0), @@ -789,6 +806,9 @@ static struct sensor_device_attribute_2 f8000_fan_attr[] = { SENSOR_ATTR_2(temp2_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), + SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, + show_pwm_auto_point_channel, + store_pwm_auto_point_channel, 0, 2), SENSOR_ATTR_2(temp3_auto_point1_pwm, S_IRUGO|S_IWUSR, show_pwm_auto_point_pwm, store_pwm_auto_point_pwm, 0, 1), @@ -1847,15 +1867,15 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) break; case f71882fg: err = f71882fg_create_sysfs_files(pdev, - f71882fg_in_temp_attr, - ARRAY_SIZE(f71882fg_in_temp_attr)); + fxxxx_in1_alarm_attr, + ARRAY_SIZE(fxxxx_in1_alarm_attr)); if (err) goto exit_unregister_sysfs; /* fall through! */ case f71862fg: err = f71882fg_create_sysfs_files(pdev, - f718x2fg_in_temp_attr, - ARRAY_SIZE(f718x2fg_in_temp_attr)); + fxxxx_in_temp_attr, + ARRAY_SIZE(fxxxx_in_temp_attr)); break; case f8000: err = f71882fg_create_sysfs_files(pdev, @@ -1905,25 +1925,40 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) switch (data->type) { case f71862fg: err = f71882fg_create_sysfs_files(pdev, - f71862fg_fan_attr, - ARRAY_SIZE(f71862fg_fan_attr)); + fxxxx_fan_beep_attr, + ARRAY_SIZE(fxxxx_fan_beep_attr)); + if (err) + goto exit_unregister_sysfs; + err = f71882fg_create_sysfs_files(pdev, + f71862fg_auto_pwm_attr, + ARRAY_SIZE(f71862fg_auto_pwm_attr)); break; case f71882fg: err = f71882fg_create_sysfs_files(pdev, - f71882fg_fan_attr, - ARRAY_SIZE(f71882fg_fan_attr)); + fxxxx_fan_beep_attr, + ARRAY_SIZE(fxxxx_fan_beep_attr)); + if (err) + goto exit_unregister_sysfs; + err = f71882fg_create_sysfs_files(pdev, + f71882fg_auto_pwm_attr, + ARRAY_SIZE(f71882fg_auto_pwm_attr)); if (err) goto exit_unregister_sysfs; /* fall through! */ case f71858fg: err = f71882fg_create_sysfs_files(pdev, - f71882fg_f71858fg_fan_attr, - ARRAY_SIZE(f71882fg_f71858fg_fan_attr)); + fxxxx_auto_pwm_attr, + ARRAY_SIZE(fxxxx_auto_pwm_attr)); break; case f8000: err = f71882fg_create_sysfs_files(pdev, f8000_fan_attr, ARRAY_SIZE(f8000_fan_attr)); + if (err) + goto exit_unregister_sysfs; + err = f71882fg_create_sysfs_files(pdev, + f8000_auto_pwm_attr, + ARRAY_SIZE(f8000_auto_pwm_attr)); break; } if (err) @@ -1965,22 +2000,28 @@ static int f71882fg_remove(struct platform_device *pdev) below are supersets of the ones skipped. */ device_remove_file(&pdev->dev, &dev_attr_name); - for (i = 0; i < ARRAY_SIZE(f718x2fg_in_temp_attr); i++) + for (i = 0; i < ARRAY_SIZE(fxxxx_in_temp_attr); i++) device_remove_file(&pdev->dev, - &f718x2fg_in_temp_attr[i].dev_attr); + &fxxxx_in_temp_attr[i].dev_attr); - for (i = 0; i < ARRAY_SIZE(f71882fg_in_temp_attr); i++) + for (i = 0; i < ARRAY_SIZE(fxxxx_in1_alarm_attr); i++) device_remove_file(&pdev->dev, - &f71882fg_in_temp_attr[i].dev_attr); + &fxxxx_in1_alarm_attr[i].dev_attr); for (i = 0; i < ARRAY_SIZE(fxxxx_fan_attr); i++) device_remove_file(&pdev->dev, &fxxxx_fan_attr[i].dev_attr); - for (i = 0; i < ARRAY_SIZE(f71882fg_fan_attr); i++) - device_remove_file(&pdev->dev, &f71882fg_fan_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(fxxxx_fan_beep_attr); i++) + device_remove_file(&pdev->dev, + &fxxxx_fan_beep_attr[i].dev_attr); - for (i = 0; i < ARRAY_SIZE(f8000_fan_attr); i++) - device_remove_file(&pdev->dev, &f8000_fan_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(f71882fg_auto_pwm_attr); i++) + device_remove_file(&pdev->dev, + &f71882fg_auto_pwm_attr[i].dev_attr); + + for (i = 0; i < ARRAY_SIZE(f8000_auto_pwm_attr); i++) + device_remove_file(&pdev->dev, + &f8000_auto_pwm_attr[i].dev_attr); kfree(data); -- cgit v0.10.2 From b69b039922673dfabe0b5774f2e313f2a2297d01 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Dec 2009 20:36:00 +0100 Subject: hwmon: (f71882fg) Cleanup sysfs attr creation 2/2 This patch merges the f71882fg_auto_pwm_attr array into the fxxxx_fan_attr resp. fxxxx_auto_pwm_attr array, as the f71882fg_auto_pwm_attr array was merely extending these 2 with entries for a 4th fan, it also makes these 2 arrays 2 dimensional so that the rest of the code can choose to add attr for 3 or 4 fans without needing to know the nr of attr per fan. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 40dd6cf..210ed66 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -416,41 +416,51 @@ static struct sensor_device_attribute_2 f8000_in_temp_attr[] = { }; /* Fan / PWM attr common to all models */ -static struct sensor_device_attribute_2 fxxxx_fan_attr[] = { +static struct sensor_device_attribute_2 fxxxx_fan_attr[4][6] = { { SENSOR_ATTR_2(fan1_input, S_IRUGO, show_fan, NULL, 0, 0), SENSOR_ATTR_2(fan1_full_speed, S_IRUGO|S_IWUSR, show_fan_full_speed, store_fan_full_speed, 0, 0), SENSOR_ATTR_2(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 0), - SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1), - SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR, - show_fan_full_speed, - store_fan_full_speed, 0, 1), - SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1), - SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), - SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR, - show_fan_full_speed, - store_fan_full_speed, 0, 2), - SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), - SENSOR_ATTR_2(pwm1, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 0), SENSOR_ATTR_2(pwm1_enable, S_IRUGO|S_IWUSR, show_pwm_enable, store_pwm_enable, 0, 0), SENSOR_ATTR_2(pwm1_interpolate, S_IRUGO|S_IWUSR, show_pwm_interpolate, store_pwm_interpolate, 0, 0), - +}, { + SENSOR_ATTR_2(fan2_input, S_IRUGO, show_fan, NULL, 0, 1), + SENSOR_ATTR_2(fan2_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 1), + SENSOR_ATTR_2(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 1), SENSOR_ATTR_2(pwm2, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 1), SENSOR_ATTR_2(pwm2_enable, S_IRUGO|S_IWUSR, show_pwm_enable, store_pwm_enable, 0, 1), SENSOR_ATTR_2(pwm2_interpolate, S_IRUGO|S_IWUSR, show_pwm_interpolate, store_pwm_interpolate, 0, 1), - +}, { + SENSOR_ATTR_2(fan3_input, S_IRUGO, show_fan, NULL, 0, 2), + SENSOR_ATTR_2(fan3_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 2), + SENSOR_ATTR_2(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 2), SENSOR_ATTR_2(pwm3, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 2), SENSOR_ATTR_2(pwm3_enable, S_IRUGO|S_IWUSR, show_pwm_enable, store_pwm_enable, 0, 2), SENSOR_ATTR_2(pwm3_interpolate, S_IRUGO|S_IWUSR, show_pwm_interpolate, store_pwm_interpolate, 0, 2), -}; +}, { + SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), + SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, + show_fan_full_speed, + store_fan_full_speed, 0, 3), + SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3), + SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3), + SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable, + store_pwm_enable, 0, 3), + SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR, + show_pwm_interpolate, store_pwm_interpolate, 0, 3), +} }; /* Attr for models which can beep on Fan alarm */ static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = { @@ -460,6 +470,8 @@ static struct sensor_device_attribute_2 fxxxx_fan_beep_attr[] = { store_fan_beep, 0, 1), SENSOR_ATTR_2(fan3_beep, S_IRUGO|S_IWUSR, show_fan_beep, store_fan_beep, 0, 2), + SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, + store_fan_beep, 0, 3), }; /* PWM attr for the f71862fg, fewer pwms and fewer zones per pwm than the @@ -533,7 +545,7 @@ static struct sensor_device_attribute_2 f71862fg_auto_pwm_attr[] = { }; /* PWM attr common to the f71858fg, f71882fg and f71889fg */ -static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[] = { +static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[4][14] = { { SENSOR_ATTR_2(pwm1_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 0), @@ -574,7 +586,7 @@ static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 0), SENSOR_ATTR_2(pwm1_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 0), - +}, { SENSOR_ATTR_2(pwm2_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 1), @@ -615,7 +627,7 @@ static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 1), SENSOR_ATTR_2(pwm2_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 1), - +}, { SENSOR_ATTR_2(pwm3_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 2), @@ -656,23 +668,7 @@ static struct sensor_device_attribute_2 fxxxx_auto_pwm_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 2), SENSOR_ATTR_2(pwm3_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 2), -}; - -/* Fan / PWM attr found on the f71882fg but not on the f71858fg */ -static struct sensor_device_attribute_2 f71882fg_auto_pwm_attr[] = { - SENSOR_ATTR_2(fan4_input, S_IRUGO, show_fan, NULL, 0, 3), - SENSOR_ATTR_2(fan4_full_speed, S_IRUGO|S_IWUSR, - show_fan_full_speed, - store_fan_full_speed, 0, 3), - SENSOR_ATTR_2(fan4_beep, S_IRUGO|S_IWUSR, show_fan_beep, - store_fan_beep, 0, 3), - SENSOR_ATTR_2(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 0, 3), - - SENSOR_ATTR_2(pwm4, S_IRUGO|S_IWUSR, show_pwm, store_pwm, 0, 3), - SENSOR_ATTR_2(pwm4_enable, S_IRUGO|S_IWUSR, show_pwm_enable, - store_pwm_enable, 0, 3), - SENSOR_ATTR_2(pwm4_interpolate, S_IRUGO|S_IWUSR, - show_pwm_interpolate, store_pwm_interpolate, 0, 3), +}, { SENSOR_ATTR_2(pwm4_auto_channels_temp, S_IRUGO|S_IWUSR, show_pwm_auto_point_channel, store_pwm_auto_point_channel, 0, 3), @@ -713,7 +709,7 @@ static struct sensor_device_attribute_2 f71882fg_auto_pwm_attr[] = { show_pwm_auto_point_temp_hyst, NULL, 2, 3), SENSOR_ATTR_2(pwm4_auto_point4_temp_hyst, S_IRUGO, show_pwm_auto_point_temp_hyst, NULL, 3, 3), -}; +} }; /* Fan attr specific to the f8000 (4th fan input can only measure speed) */ static struct sensor_device_attribute_2 f8000_fan_attr[] = { @@ -1917,39 +1913,24 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) goto exit_unregister_sysfs; } - err = f71882fg_create_sysfs_files(pdev, fxxxx_fan_attr, - ARRAY_SIZE(fxxxx_fan_attr)); + err = f71882fg_create_sysfs_files(pdev, &fxxxx_fan_attr[0][0], + ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); if (err) goto exit_unregister_sysfs; - switch (data->type) { - case f71862fg: + if (data->type == f71862fg || data->type == f71882fg) { err = f71882fg_create_sysfs_files(pdev, - fxxxx_fan_beep_attr, - ARRAY_SIZE(fxxxx_fan_beep_attr)); + fxxxx_fan_beep_attr, nr_fans); if (err) goto exit_unregister_sysfs; + } + + switch (data->type) { + case f71862fg: err = f71882fg_create_sysfs_files(pdev, f71862fg_auto_pwm_attr, ARRAY_SIZE(f71862fg_auto_pwm_attr)); break; - case f71882fg: - err = f71882fg_create_sysfs_files(pdev, - fxxxx_fan_beep_attr, - ARRAY_SIZE(fxxxx_fan_beep_attr)); - if (err) - goto exit_unregister_sysfs; - err = f71882fg_create_sysfs_files(pdev, - f71882fg_auto_pwm_attr, - ARRAY_SIZE(f71882fg_auto_pwm_attr)); - if (err) - goto exit_unregister_sysfs; - /* fall through! */ - case f71858fg: - err = f71882fg_create_sysfs_files(pdev, - fxxxx_auto_pwm_attr, - ARRAY_SIZE(fxxxx_auto_pwm_attr)); - break; case f8000: err = f71882fg_create_sysfs_files(pdev, f8000_fan_attr, @@ -1960,6 +1941,10 @@ static int __devinit f71882fg_probe(struct platform_device *pdev) f8000_auto_pwm_attr, ARRAY_SIZE(f8000_auto_pwm_attr)); break; + default: /* f71858fg / f71882fg */ + err = f71882fg_create_sysfs_files(pdev, + &fxxxx_auto_pwm_attr[0][0], + ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); } if (err) goto exit_unregister_sysfs; @@ -1989,7 +1974,7 @@ exit_free: static int f71882fg_remove(struct platform_device *pdev) { - int i; + int i, j; struct f71882fg_data *data = platform_get_drvdata(pdev); platform_set_drvdata(pdev, NULL); @@ -2009,15 +1994,18 @@ static int f71882fg_remove(struct platform_device *pdev) &fxxxx_in1_alarm_attr[i].dev_attr); for (i = 0; i < ARRAY_SIZE(fxxxx_fan_attr); i++) - device_remove_file(&pdev->dev, &fxxxx_fan_attr[i].dev_attr); + for (j = 0; j < ARRAY_SIZE(fxxxx_fan_attr[0]); j++) + device_remove_file(&pdev->dev, + &fxxxx_fan_attr[i][j].dev_attr); for (i = 0; i < ARRAY_SIZE(fxxxx_fan_beep_attr); i++) device_remove_file(&pdev->dev, &fxxxx_fan_beep_attr[i].dev_attr); - for (i = 0; i < ARRAY_SIZE(f71882fg_auto_pwm_attr); i++) - device_remove_file(&pdev->dev, - &f71882fg_auto_pwm_attr[i].dev_attr); + for (i = 0; i < ARRAY_SIZE(fxxxx_auto_pwm_attr); i++) + for (j = 0; j < ARRAY_SIZE(fxxxx_auto_pwm_attr[0]); j++) + device_remove_file(&pdev->dev, + &fxxxx_auto_pwm_attr[i][j].dev_attr); for (i = 0; i < ARRAY_SIZE(f8000_auto_pwm_attr); i++) device_remove_file(&pdev->dev, -- cgit v0.10.2 From fc16c56e694d361388bae701894fd719dbc0f7eb Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 9 Dec 2009 20:36:01 +0100 Subject: hwmon: (f71882fg) Fix sysfs file removal There is a bug in the old sysfs file removal, as it uses fxxxx_in_temp_attr to remove the in and temp sysfs attributes, but fxxxx_in_temp_attr has temp#_alarm, where as f71858fg_in_temp_attr has temp#_max_alarm, so the temp#_max_alarm attributes for the f71858fg never get removed. This patch fixes this by doing the sysfs removal exactly the same way as the creation instead of being (too) clever, this will also avoid similar bugs in the future. Signed-off-by: Hans de Goede Signed-off-by: Jean Delvare diff --git a/drivers/hwmon/f71882fg.c b/drivers/hwmon/f71882fg.c index 210ed66..3a695f0 100644 --- a/drivers/hwmon/f71882fg.c +++ b/drivers/hwmon/f71882fg.c @@ -1810,6 +1810,15 @@ static int __devinit f71882fg_create_sysfs_files(struct platform_device *pdev, return 0; } +static void f71882fg_remove_sysfs_files(struct platform_device *pdev, + struct sensor_device_attribute_2 *attr, int count) +{ + int i; + + for (i = 0; i < count; i++) + device_remove_file(&pdev->dev, &attr[i].dev_attr); +} + static int __devinit f71882fg_probe(struct platform_device *pdev) { struct f71882fg_data *data; @@ -1974,42 +1983,74 @@ exit_free: static int f71882fg_remove(struct platform_device *pdev) { - int i, j; struct f71882fg_data *data = platform_get_drvdata(pdev); + int nr_fans = (data->type == f71882fg) ? 4 : 3; + u8 start_reg = f71882fg_read8(data, F71882FG_REG_START); platform_set_drvdata(pdev, NULL); if (data->hwmon_dev) hwmon_device_unregister(data->hwmon_dev); - /* Note we are not looping over all attr arrays we have as the ones - below are supersets of the ones skipped. */ device_remove_file(&pdev->dev, &dev_attr_name); - for (i = 0; i < ARRAY_SIZE(fxxxx_in_temp_attr); i++) - device_remove_file(&pdev->dev, - &fxxxx_in_temp_attr[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(fxxxx_in1_alarm_attr); i++) - device_remove_file(&pdev->dev, - &fxxxx_in1_alarm_attr[i].dev_attr); - - for (i = 0; i < ARRAY_SIZE(fxxxx_fan_attr); i++) - for (j = 0; j < ARRAY_SIZE(fxxxx_fan_attr[0]); j++) - device_remove_file(&pdev->dev, - &fxxxx_fan_attr[i][j].dev_attr); + if (start_reg & 0x01) { + switch (data->type) { + case f71858fg: + if (data->temp_config & 0x10) + f71882fg_remove_sysfs_files(pdev, + f8000_in_temp_attr, + ARRAY_SIZE(f8000_in_temp_attr)); + else + f71882fg_remove_sysfs_files(pdev, + f71858fg_in_temp_attr, + ARRAY_SIZE(f71858fg_in_temp_attr)); + break; + case f71882fg: + f71882fg_remove_sysfs_files(pdev, + fxxxx_in1_alarm_attr, + ARRAY_SIZE(fxxxx_in1_alarm_attr)); + /* fall through! */ + case f71862fg: + f71882fg_remove_sysfs_files(pdev, + fxxxx_in_temp_attr, + ARRAY_SIZE(fxxxx_in_temp_attr)); + break; + case f8000: + f71882fg_remove_sysfs_files(pdev, + f8000_in_temp_attr, + ARRAY_SIZE(f8000_in_temp_attr)); + break; + } + } - for (i = 0; i < ARRAY_SIZE(fxxxx_fan_beep_attr); i++) - device_remove_file(&pdev->dev, - &fxxxx_fan_beep_attr[i].dev_attr); + if (start_reg & 0x02) { + f71882fg_remove_sysfs_files(pdev, &fxxxx_fan_attr[0][0], + ARRAY_SIZE(fxxxx_fan_attr[0]) * nr_fans); - for (i = 0; i < ARRAY_SIZE(fxxxx_auto_pwm_attr); i++) - for (j = 0; j < ARRAY_SIZE(fxxxx_auto_pwm_attr[0]); j++) - device_remove_file(&pdev->dev, - &fxxxx_auto_pwm_attr[i][j].dev_attr); + if (data->type == f71862fg || data->type == f71882fg) + f71882fg_remove_sysfs_files(pdev, + fxxxx_fan_beep_attr, nr_fans); - for (i = 0; i < ARRAY_SIZE(f8000_auto_pwm_attr); i++) - device_remove_file(&pdev->dev, - &f8000_auto_pwm_attr[i].dev_attr); + switch (data->type) { + case f71862fg: + f71882fg_remove_sysfs_files(pdev, + f71862fg_auto_pwm_attr, + ARRAY_SIZE(f71862fg_auto_pwm_attr)); + break; + case f8000: + f71882fg_remove_sysfs_files(pdev, + f8000_fan_attr, + ARRAY_SIZE(f8000_fan_attr)); + f71882fg_remove_sysfs_files(pdev, + f8000_auto_pwm_attr, + ARRAY_SIZE(f8000_auto_pwm_attr)); + break; + default: /* f71858fg / f71882fg / f71889fg */ + f71882fg_remove_sysfs_files(pdev, + &fxxxx_auto_pwm_attr[0][0], + ARRAY_SIZE(fxxxx_auto_pwm_attr[0]) * nr_fans); + } + } kfree(data); -- cgit v0.10.2 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 diff --git a/Documentation/hwmon/f71882fg b/Documentation/hwmon/f71882fg index bee4c30..a7952c2 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 dd69393..edf8feb 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 3a695f0..a95fa42 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 v0.10.2 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" diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 591e944..2a4d779 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 1cbf671..446612b 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 edf8feb..5b2eaff 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 97ef508..aea244d 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 41d3e38..fba2f01 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 v0.10.2 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" diff --git a/Documentation/hwmon/adt7475 b/Documentation/hwmon/adt7475 index a2b1abe..ebfee55 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 v0.10.2 From f99318b2540da75e663603e1a0faef30a3bb0c92 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:03 +0100 Subject: hwmon: (adt7475) Implement pwm_use_point2_pwm_at_crit Implement the non-standard pwm_use_point2_pwm_at_crit sysfs attribute as the adt7473 driver did. Signed-off-by: Jean Delvare Cc: "Mark M. Hoffman" Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index fba2f01..188ae428 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -76,6 +76,9 @@ #define REG_EXTEND1 0x76 #define REG_EXTEND2 0x77 #define REG_CONFIG5 0x7C +#define REG_CONFIG4 0x7D + +#define CONFIG4_MAXDUTY 0x08 #define CONFIG5_TWOSCOMP 0x01 #define CONFIG5_TEMPOFFSET 0x02 @@ -132,6 +135,7 @@ struct adt7475_data { unsigned long limits_updated; char valid; + u8 config4; u8 config5; u16 alarms; u16 voltage[3][3]; @@ -779,6 +783,38 @@ static ssize_t set_pwmfreq(struct device *dev, struct device_attribute *attr, return count; } +static ssize_t show_pwm_at_crit(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + struct adt7475_data *data = adt7475_update_device(dev); + return sprintf(buf, "%d\n", !!(data->config4 & CONFIG4_MAXDUTY)); +} + +static ssize_t set_pwm_at_crit(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7475_data *data = i2c_get_clientdata(client); + long val; + + if (strict_strtol(buf, 10, &val)) + return -EINVAL; + if (val != 0 && val != 1) + return -EINVAL; + + mutex_lock(&data->lock); + data->config4 = i2c_smbus_read_byte_data(client, REG_CONFIG4); + if (val) + data->config4 |= CONFIG4_MAXDUTY; + else + data->config4 &= ~CONFIG4_MAXDUTY; + i2c_smbus_write_byte_data(client, REG_CONFIG4, data->config4); + mutex_unlock(&data->lock); + + return count; +} + static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_voltage, NULL, INPUT, 0); static SENSOR_DEVICE_ATTR_2(in1_max, S_IRUGO | S_IWUSR, show_voltage, set_voltage, MAX, 0); @@ -894,6 +930,10 @@ static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_pwm, S_IRUGO | S_IWUSR, show_pwm, static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_pwm, S_IRUGO | S_IWUSR, show_pwm, set_pwm, MAX, 2); +/* Non-standard name, might need revisiting */ +static DEVICE_ATTR(pwm_use_point2_pwm_at_crit, S_IWUSR | S_IRUGO, + show_pwm_at_crit, set_pwm_at_crit); + static struct attribute *adt7475_attrs[] = { &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_max.dev_attr.attr, @@ -962,6 +1002,7 @@ static struct attribute *adt7475_attrs[] = { &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, + &dev_attr_pwm_use_point2_pwm_at_crit.attr, NULL, }; @@ -1160,6 +1201,7 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) /* Limits and settings, should never change update every 60 seconds */ if (time_after(jiffies, data->limits_updated + HZ * 60) || !data->valid) { + data->config4 = adt7475_read(REG_CONFIG4); data->config5 = adt7475_read(REG_CONFIG5); for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) { -- cgit v0.10.2 From cffb9dd07fea8865093f6ccfb51d686487b89415 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:03 +0100 Subject: hwmon: (adt7475) Rework voltage inputs handling Rework the handling of voltage inputs to make it possible and easy to support more inputs. This will be needed for the upcoming ADT7490 support. Signed-off-by: Jean Delvare Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 188ae428..e9f69ee 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -39,7 +39,7 @@ /* 7475 Common Registers */ -#define REG_VOLTAGE_BASE 0x21 +#define REG_VOLTAGE_BASE 0x20 #define REG_TEMP_BASE 0x25 #define REG_TACH_BASE 0x28 #define REG_PWM_BASE 0x30 @@ -51,8 +51,8 @@ #define REG_STATUS1 0x41 #define REG_STATUS2 0x42 -#define REG_VOLTAGE_MIN_BASE 0x46 -#define REG_VOLTAGE_MAX_BASE 0x47 +#define REG_VOLTAGE_MIN_BASE 0x44 +#define REG_VOLTAGE_MAX_BASE 0x45 #define REG_TEMP_MIN_BASE 0x4E #define REG_TEMP_MAX_BASE 0x4F @@ -85,7 +85,7 @@ /* ADT7475 Settings */ -#define ADT7475_VOLTAGE_COUNT 2 +#define ADT7475_VOLTAGE_COUNT 5 /* Not counting Vtt */ #define ADT7475_TEMP_COUNT 3 #define ADT7475_TACH_COUNT 4 #define ADT7475_PWM_COUNT 3 @@ -137,8 +137,9 @@ struct adt7475_data { u8 config4; u8 config5; + u8 has_voltage; u16 alarms; - u16 voltage[3][3]; + u16 voltage[3][6]; u16 temp[7][3]; u16 tach[2][4]; u8 pwm[4][3]; @@ -201,26 +202,30 @@ static inline u16 rpm2tach(unsigned long rpm) return SENSORS_LIMIT((90000 * 60) / rpm, 1, 0xFFFF); } -static inline int reg2vcc(u16 reg) -{ - return (4296 * reg) / 1000; -} +/* Scaling factors for voltage inputs, taken from the ADT7490 datasheet */ +static const int adt7473_in_scaling[ADT7475_VOLTAGE_COUNT + 1][2] = { + { 45, 94 }, /* +2.5V */ + { 175, 525 }, /* Vccp */ + { 68, 71 }, /* Vcc */ + { 93, 47 }, /* +5V */ + { 120, 20 }, /* +12V */ + { 45, 45 }, /* Vtt */ +}; -static inline int reg2vccp(u16 reg) +static inline int reg2volt(int channel, u16 reg) { - return (2929 * reg) / 1000; -} + const int *r = adt7473_in_scaling[channel]; -static inline u16 vcc2reg(long vcc) -{ - vcc = SENSORS_LIMIT(vcc, 0, 4396); - return (vcc * 1000) / 4296; + return DIV_ROUND_CLOSEST(reg * (r[0] + r[1]) * 2250, r[1] * 1024); } -static inline u16 vccp2reg(long vcc) +static inline u16 volt2reg(int channel, long volt) { - vcc = SENSORS_LIMIT(vcc, 0, 2998); - return (vcc * 1000) / 2929; + const int *r = adt7473_in_scaling[channel]; + long reg; + + reg = (volt * r[1] * 1024) / ((r[0] + r[1]) * 2250); + return SENSORS_LIMIT(reg, 0, 1023) & (0xff << 2); } static u16 adt7475_read_word(struct i2c_client *client, int reg) @@ -276,12 +281,10 @@ static ssize_t show_voltage(struct device *dev, struct device_attribute *attr, switch (sattr->nr) { case ALARM: return sprintf(buf, "%d\n", - (data->alarms >> (sattr->index + 1)) & 1); + (data->alarms >> sattr->index) & 1); default: val = data->voltage[sattr->nr][sattr->index]; - return sprintf(buf, "%d\n", - sattr->index == - 0 ? reg2vccp(val) : reg2vcc(val)); + return sprintf(buf, "%d\n", reg2volt(sattr->index, val)); } } @@ -300,8 +303,7 @@ static ssize_t set_voltage(struct device *dev, struct device_attribute *attr, mutex_lock(&data->lock); - data->voltage[sattr->nr][sattr->index] = - sattr->index ? vcc2reg(val) : vccp2reg(val); + data->voltage[sattr->nr][sattr->index] = volt2reg(sattr->index, val); if (sattr->nr == MIN) reg = VOLTAGE_MIN_REG(sattr->index); @@ -815,18 +817,18 @@ static ssize_t set_pwm_at_crit(struct device *dev, return count; } -static SENSOR_DEVICE_ATTR_2(in1_input, S_IRUGO, show_voltage, NULL, INPUT, 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, 0); + set_voltage, MAX, 1); static SENSOR_DEVICE_ATTR_2(in1_min, S_IRUGO | S_IWUSR, show_voltage, - set_voltage, MIN, 0); -static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, show_voltage, NULL, ALARM, 0); -static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_voltage, NULL, INPUT, 1); + set_voltage, MIN, 1); +static SENSOR_DEVICE_ATTR_2(in1_alarm, S_IRUGO, show_voltage, NULL, ALARM, 1); +static SENSOR_DEVICE_ATTR_2(in2_input, S_IRUGO, show_voltage, NULL, INPUT, 2); static SENSOR_DEVICE_ATTR_2(in2_max, S_IRUGO | S_IWUSR, show_voltage, - set_voltage, MAX, 1); + set_voltage, MAX, 2); static SENSOR_DEVICE_ATTR_2(in2_min, S_IRUGO | S_IWUSR, show_voltage, - set_voltage, MIN, 1); -static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_voltage, NULL, ALARM, 1); + set_voltage, MIN, 2); +static SENSOR_DEVICE_ATTR_2(in2_alarm, S_IRUGO, show_voltage, NULL, ALARM, 2); 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); @@ -1050,6 +1052,12 @@ static int adt7475_probe(struct i2c_client *client, mutex_init(&data->lock); i2c_set_clientdata(client, data); + /* Initialize device-specific values */ + switch (id->driver_data) { + default: + data->has_voltage = 0x06; /* in1, in2 */ + } + /* Call adt7475_read_pwm for all pwm's as this will reprogram any pwm's which are disabled to manual mode with 0% duty cycle */ for (i = 0; i < ADT7475_PWM_COUNT; i++) @@ -1176,10 +1184,13 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) data->alarms |= adt7475_read(REG_STATUS1); ext = adt7475_read(REG_EXTEND1); - for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) + for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) { + if (!(data->has_voltage & (1 << i))) + continue; data->voltage[INPUT][i] = (adt7475_read(VOLTAGE_REG(i)) << 2) | - ((ext >> ((i + 1) * 2)) & 3); + ((ext >> (i * 2)) & 3); + } ext = adt7475_read(REG_EXTEND2); for (i = 0; i < ADT7475_TEMP_COUNT; i++) @@ -1205,6 +1216,8 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) data->config5 = adt7475_read(REG_CONFIG5); for (i = 0; i < ADT7475_VOLTAGE_COUNT; i++) { + if (!(data->has_voltage & (1 << i))) + continue; /* Adjust values so they match the input precision */ data->voltage[MIN][i] = adt7475_read(VOLTAGE_MIN_REG(i)) << 2; -- cgit v0.10.2 From 54ecb9e3c194687eebabe7ca45bae889ac1cd4de Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:03 +0100 Subject: hwmon: (adt7475) Add missing static marker adt7475_attr_group is used internally only and can thus be marked static. Signed-off-by: Jean Delvare Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index e9f69ee..20bab51 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -1008,7 +1008,7 @@ static struct attribute *adt7475_attrs[] = { NULL, }; -struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs }; +static struct attribute_group adt7475_attr_group = { .attrs = adt7475_attrs }; static int adt7475_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) -- cgit v0.10.2 From d656b6fde2531a13c4e68a3ce6b9f12bc19d96bb Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:04 +0100 Subject: hwmon: (adt7475) Improve device detection Check the value of register 0x3f as part of the device detection, to make it more robust. Signed-off-by: Jean Delvare Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 20bab51..520773b 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -47,6 +47,7 @@ #define REG_DEVID 0x3D #define REG_VENDID 0x3E +#define REG_DEVID2 0x3F #define REG_STATUS1 0x41 #define REG_STATUS2 0x42 @@ -1014,18 +1015,22 @@ static int adt7475_detect(struct i2c_client *client, int kind, struct i2c_board_info *info) { struct i2c_adapter *adapter = client->adapter; - int vendid, devid; + int vendid, devid, devid2; const char *name; if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) return -ENODEV; vendid = adt7475_read(REG_VENDID); - devid = adt7475_read(REG_DEVID); + devid2 = adt7475_read(REG_DEVID2); + if (vendid != 0x41 || /* Analog Devices */ + (devid2 & 0xf8) != 0x68) + return -ENODEV; - if (vendid == 0x41 && devid == 0x73) + devid = adt7475_read(REG_DEVID); + if (devid == 0x73) name = "adt7473"; - else if (vendid == 0x41 && devid == 0x75 && client->addr == 0x2e) + else if (devid == 0x75 && client->addr == 0x2e) name = "adt7475"; else { dev_dbg(&adapter->dev, -- cgit v0.10.2 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" diff --git a/Documentation/hwmon/adt7475 b/Documentation/hwmon/adt7475 index ebfee55..f08f287 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 5b2eaff..f9f4365 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 520773b..e495665 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 v0.10.2 From 0f14480b62235ef4fce1cd4755e1cde4c9be5f78 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:06 +0100 Subject: hwmon: (adt7475) Move sysfs files removal to a separate function Move sysfs files removal to a separate function. The code is common to the device probing error path and the standard device removal path. As it will grow with future driver development, this avoids code duplication. Signed-off-by: Jean Delvare Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index e495665..716dae9 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -1109,6 +1109,14 @@ static int adt7475_detect(struct i2c_client *client, int kind, return 0; } +static void adt7475_remove_files(struct i2c_client *client, + struct adt7475_data *data) +{ + sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group); + if (data->has_voltage & 0x39) + sysfs_remove_group(&client->dev.kobj, &adt7490_attr_group); +} + static int adt7475_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1156,9 +1164,7 @@ static int adt7475_probe(struct i2c_client *client, return 0; eremove: - sysfs_remove_group(&client->dev.kobj, &adt7475_attr_group); - if (data->has_voltage & 0x39) - sysfs_remove_group(&client->dev.kobj, &adt7490_attr_group); + adt7475_remove_files(client, data); efree: kfree(data); return ret; @@ -1169,9 +1175,7 @@ static int adt7475_remove(struct i2c_client *client) struct adt7475_data *data = i2c_get_clientdata(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); + adt7475_remove_files(client, data); kfree(data); return 0; -- cgit v0.10.2 From 378933c99402f26587ad80e97bff405265116f9e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:06 +0100 Subject: hwmon: (adt7475) Handle alternative pin functions The TACH4 pin can be used for other functions, so fan4 may not always be available. Likewise, the PWM2 pin can be used for ALERT output, in which case pwm2 is not available For the ADT7490, the +2.5 Vin pin may also be used for other functions, in which case in0 is not available. Signed-off-by: Jean Delvare Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 716dae9..d201307 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -80,6 +80,8 @@ #define REG_EXTEND1 0x76 #define REG_EXTEND2 0x77 + +#define REG_CONFIG3 0x78 #define REG_CONFIG5 0x7C #define REG_CONFIG4 0x7D @@ -88,6 +90,10 @@ #define REG_VTT_MIN 0x84 /* ADT7490 only */ #define REG_VTT_MAX 0x86 /* ADT7490 only */ +#define CONFIG3_SMBALERT 0x01 +#define CONFIG3_THERM 0x02 + +#define CONFIG4_PINFUNC 0x03 #define CONFIG4_MAXDUTY 0x08 #define CONFIG5_TWOSCOMP 0x01 @@ -149,6 +155,8 @@ struct adt7475_data { u8 config4; u8 config5; u8 has_voltage; + u8 has_pwm2:1; + u8 has_fan4:1; u32 alarms; u16 voltage[3][6]; u16 temp[7][3]; @@ -1025,21 +1033,12 @@ static struct attribute *adt7475_attrs[] = { &sensor_dev_attr_fan3_input.dev_attr.attr, &sensor_dev_attr_fan3_min.dev_attr.attr, &sensor_dev_attr_fan3_alarm.dev_attr.attr, - &sensor_dev_attr_fan4_input.dev_attr.attr, - &sensor_dev_attr_fan4_min.dev_attr.attr, - &sensor_dev_attr_fan4_alarm.dev_attr.attr, &sensor_dev_attr_pwm1.dev_attr.attr, &sensor_dev_attr_pwm1_freq.dev_attr.attr, &sensor_dev_attr_pwm1_enable.dev_attr.attr, &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, - &sensor_dev_attr_pwm2.dev_attr.attr, - &sensor_dev_attr_pwm2_freq.dev_attr.attr, - &sensor_dev_attr_pwm2_enable.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, - &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, &sensor_dev_attr_pwm3.dev_attr.attr, &sensor_dev_attr_pwm3_freq.dev_attr.attr, &sensor_dev_attr_pwm3_enable.dev_attr.attr, @@ -1050,12 +1049,33 @@ static struct attribute *adt7475_attrs[] = { NULL, }; +static struct attribute *fan4_attrs[] = { + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + &sensor_dev_attr_fan4_alarm.dev_attr.attr, + NULL +}; + +static struct attribute *pwm2_attrs[] = { + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm2_freq.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, + NULL +}; + /* Attributes specific to the ADT7490 */ -static struct attribute *adt7490_attrs[] = { +static struct attribute *in0_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, + NULL +}; + +static struct attribute *adt7490_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, @@ -1072,6 +1092,9 @@ static struct attribute *adt7490_attrs[] = { }; 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 int adt7475_detect(struct i2c_client *client, int kind, @@ -1115,13 +1138,20 @@ static void adt7475_remove_files(struct i2c_client *client, 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); } static int adt7475_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct adt7475_data *data; - int i, ret = 0; + int i, ret = 0, revision; + u8 config3; data = kzalloc(sizeof(*data), GFP_KERNEL); if (data == NULL) @@ -1133,10 +1163,36 @@ 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 */ + data->has_voltage = 0x3e; /* in1 to in5 */ + revision = adt7475_read(REG_DEVID2) & 0x03; break; default: data->has_voltage = 0x06; /* in1, in2 */ + revision = adt7475_read(REG_DEVID2) & 0x07; + } + + config3 = adt7475_read(REG_CONFIG3); + /* Pin PWM2 may alternatively be used for ALERT output */ + if (!(config3 & CONFIG3_SMBALERT)) + data->has_pwm2 = 1; + /* Meaning of this bit is inverted for the ADT7473-1 */ + if (id->driver_data == adt7473 && revision >= 1) + data->has_pwm2 = !data->has_pwm2; + + data->config4 = adt7475_read(REG_CONFIG4); + /* Pin TACH4 may alternatively be used for THERM */ + 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 */ + if (id->driver_data == adt7490) { + if ((data->config4 & CONFIG4_PINFUNC) == 0x1 && + !(config3 & CONFIG3_THERM)) + data->has_fan4 = 1; + if (!(config3 & CONFIG3_THERM) || + (data->config4 & CONFIG4_PINFUNC) == 0x1) + data->has_voltage |= (1 << 0); /* in0 */ } /* Call adt7475_read_pwm for all pwm's as this will reprogram any @@ -1155,6 +1211,23 @@ static int adt7475_probe(struct i2c_client *client, goto eremove; } + /* Features that can be disabled individually */ + if (data->has_fan4) { + ret = sysfs_create_group(&client->dev.kobj, &fan4_attr_group); + if (ret) + goto eremove; + } + if (data->has_pwm2) { + ret = sysfs_create_group(&client->dev.kobj, &pwm2_attr_group); + if (ret) + goto eremove; + } + if (data->has_voltage & (1 << 0)) { + ret = sysfs_create_group(&client->dev.kobj, &in0_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); @@ -1293,13 +1366,19 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) ((ext >> 4) & 3); } - for (i = 0; i < ADT7475_TACH_COUNT; i++) + for (i = 0; i < ADT7475_TACH_COUNT; i++) { + if (i == 3 && !data->has_fan4) + continue; data->tach[INPUT][i] = adt7475_read_word(client, TACH_REG(i)); + } /* Updated by hw when in auto mode */ - for (i = 0; i < ADT7475_PWM_COUNT; i++) + for (i = 0; i < ADT7475_PWM_COUNT; i++) { + if (i == 1 && !data->has_pwm2) + continue; data->pwm[INPUT][i] = adt7475_read(PWM_REG(i)); + } data->measure_updated = jiffies; } @@ -1340,11 +1419,16 @@ static struct adt7475_data *adt7475_update_device(struct device *dev) } adt7475_read_hystersis(client); - for (i = 0; i < ADT7475_TACH_COUNT; i++) + for (i = 0; i < ADT7475_TACH_COUNT; i++) { + if (i == 3 && !data->has_fan4) + continue; data->tach[MIN][i] = adt7475_read_word(client, TACH_MIN_REG(i)); + } for (i = 0; i < ADT7475_PWM_COUNT; i++) { + if (i == 1 && !data->has_pwm2) + continue; data->pwm[MAX][i] = adt7475_read(PWM_MAX_REG(i)); data->pwm[MIN][i] = adt7475_read(PWM_MIN_REG(i)); /* Set the channel and control information */ -- cgit v0.10.2 From d07ca4ad2f67ebb1bf48a2308a7a0cd81ef8f70a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:07 +0100 Subject: hwmon: (adt7475) Print device information on probe Print the device name and revision at probe time, as well as a list of all optional features which are available. Signed-off-by: Jean Delvare Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index d201307..688b0a1 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -40,6 +40,8 @@ /* 7475 Common Registers */ +#define REG_DEVREV2 0x12 /* ADT7490 only */ + #define REG_VTT 0x1E /* ADT7490 only */ #define REG_EXTEND3 0x1F /* ADT7490 only */ @@ -1149,6 +1151,12 @@ static void adt7475_remove_files(struct i2c_client *client, static int adt7475_probe(struct i2c_client *client, const struct i2c_device_id *id) { + static const char *names[] = { + [adt7473] = "ADT7473", + [adt7475] = "ADT7475", + [adt7490] = "ADT7490", + }; + struct adt7475_data *data; int i, ret = 0, revision; u8 config3; @@ -1165,6 +1173,8 @@ static int adt7475_probe(struct i2c_client *client, case adt7490: data->has_voltage = 0x3e; /* in1 to in5 */ revision = adt7475_read(REG_DEVID2) & 0x03; + if (revision == 0x03) + revision += adt7475_read(REG_DEVREV2); break; default: data->has_voltage = 0x06; /* in1, in2 */ @@ -1234,6 +1244,14 @@ static int adt7475_probe(struct i2c_client *client, goto eremove; } + 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", + (data->has_voltage & (1 << 0)) ? " in0" : "", + data->has_fan4 ? " fan4" : "", + data->has_pwm2 ? " pwm2" : ""); + return 0; eremove: -- cgit v0.10.2 From ebfaf1fbb6010204e973e329034d0004d6521503 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:07 +0100 Subject: hwmon: (adt7475) Voltage attenuators can be bypassed It is possible to bypass the voltage attenuators on the +2.5V, Vccp, +5V and +12V voltage monitoring inputs. This is useful to connect other voltage channels than the ones the monitoring chip was originally designed for. When this feature is enabled, we must not include the scaling factors in our computations. Signed-off-by: Jean Delvare Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" diff --git a/drivers/hwmon/adt7475.c b/drivers/hwmon/adt7475.c index 688b0a1..eac24c1 100644 --- a/drivers/hwmon/adt7475.c +++ b/drivers/hwmon/adt7475.c @@ -80,6 +80,8 @@ #define REG_TEMP_OFFSET_BASE 0x70 +#define REG_CONFIG2 0x73 + #define REG_EXTEND1 0x76 #define REG_EXTEND2 0x77 @@ -92,11 +94,15 @@ #define REG_VTT_MIN 0x84 /* ADT7490 only */ #define REG_VTT_MAX 0x86 /* ADT7490 only */ +#define CONFIG2_ATTN 0x20 + #define CONFIG3_SMBALERT 0x01 #define CONFIG3_THERM 0x02 #define CONFIG4_PINFUNC 0x03 #define CONFIG4_MAXDUTY 0x08 +#define CONFIG4_ATTN_IN10 0x30 +#define CONFIG4_ATTN_IN43 0xC0 #define CONFIG5_TWOSCOMP 0x01 #define CONFIG5_TEMPOFFSET 0x02 @@ -157,6 +163,7 @@ struct adt7475_data { u8 config4; u8 config5; u8 has_voltage; + u8 bypass_attn; /* Bypass voltage attenuator */ u8 has_pwm2:1; u8 has_fan4:1; u32 alarms; @@ -233,19 +240,24 @@ static const int adt7473_in_scaling[ADT7475_VOLTAGE_COUNT + 1][2] = { { 45, 45 }, /* Vtt */ }; -static inline int reg2volt(int channel, u16 reg) +static inline int reg2volt(int channel, u16 reg, u8 bypass_attn) { const int *r = adt7473_in_scaling[channel]; + if (bypass_attn & (1 << channel)) + return DIV_ROUND_CLOSEST(reg * 2250, 1024); return DIV_ROUND_CLOSEST(reg * (r[0] + r[1]) * 2250, r[1] * 1024); } -static inline u16 volt2reg(int channel, long volt) +static inline u16 volt2reg(int channel, long volt, u8 bypass_attn) { const int *r = adt7473_in_scaling[channel]; long reg; - reg = (volt * r[1] * 1024) / ((r[0] + r[1]) * 2250); + if (bypass_attn & (1 << channel)) + reg = (volt * 1024) / 2250; + else + reg = (volt * r[1] * 1024) / ((r[0] + r[1]) * 2250); return SENSORS_LIMIT(reg, 0, 1023) & (0xff << 2); } @@ -305,7 +317,8 @@ static ssize_t show_voltage(struct device *dev, struct device_attribute *attr, (data->alarms >> sattr->index) & 1); default: val = data->voltage[sattr->nr][sattr->index]; - return sprintf(buf, "%d\n", reg2volt(sattr->index, val)); + return sprintf(buf, "%d\n", + reg2volt(sattr->index, val, data->bypass_attn)); } } @@ -324,7 +337,8 @@ static ssize_t set_voltage(struct device *dev, struct device_attribute *attr, mutex_lock(&data->lock); - data->voltage[sattr->nr][sattr->index] = volt2reg(sattr->index, val); + data->voltage[sattr->nr][sattr->index] = + volt2reg(sattr->index, val, data->bypass_attn); if (sattr->index < ADT7475_VOLTAGE_COUNT) { if (sattr->nr == MIN) @@ -1159,7 +1173,7 @@ static int adt7475_probe(struct i2c_client *client, struct adt7475_data *data; int i, ret = 0, revision; - u8 config3; + u8 config2, config3; data = kzalloc(sizeof(*data), GFP_KERNEL); if (data == NULL) @@ -1205,6 +1219,16 @@ static int adt7475_probe(struct i2c_client *client, data->has_voltage |= (1 << 0); /* in0 */ } + /* Voltage attenuators can be bypassed, globally or individually */ + config2 = adt7475_read(REG_CONFIG2); + if (config2 & CONFIG2_ATTN) { + data->bypass_attn = (0x3 << 3) | 0x3; + } else { + data->bypass_attn = ((data->config4 & CONFIG4_ATTN_IN10) >> 4) | + ((data->config4 & CONFIG4_ATTN_IN43) >> 3); + } + data->bypass_attn &= data->has_voltage; + /* Call adt7475_read_pwm for all pwm's as this will reprogram any pwm's which are disabled to manual mode with 0% duty cycle */ for (i = 0; i < ADT7475_PWM_COUNT; i++) @@ -1251,6 +1275,12 @@ static int adt7475_probe(struct i2c_client *client, (data->has_voltage & (1 << 0)) ? " in0" : "", data->has_fan4 ? " fan4" : "", data->has_pwm2 ? " pwm2" : ""); + if (data->bypass_attn) + dev_info(&client->dev, "Bypassing attenuators on:%s%s%s%s\n", + (data->bypass_attn & (1 << 0)) ? " in0" : "", + (data->bypass_attn & (1 << 1)) ? " in1" : "", + (data->bypass_attn & (1 << 3)) ? " in3" : "", + (data->bypass_attn & (1 << 4)) ? " in4" : ""); return 0; -- cgit v0.10.2 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" diff --git a/Documentation/hwmon/adt7475 b/Documentation/hwmon/adt7475 index f08f287..03be1e6 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 f9f4365..957171c 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 eac24c1..72c3b75 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 v0.10.2 From b058b8596136d97b9469366f1f97fb35accf3d66 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 9 Dec 2009 20:36:08 +0100 Subject: hwmon: (adt7475) Add an entry in MAINTAINERS As I've just done a lot of changes to the adt7475 driver, I volunteer to maintain it for the year to come. Signed-off-by: Jean Delvare Cc: Hans de Goede Cc: Jordan Crouse Cc: "Darrick J. Wong" diff --git a/MAINTAINERS b/MAINTAINERS index 1f6155c..9cba1fe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -327,6 +327,13 @@ M: Colin Leroy S: Maintained F: drivers/macintosh/therm_adt746x.c +ADT7475 HARDWARE MONITOR DRIVER +M: Jean Delvare +L: lm-sensors@lm-sensors.org +S: Maintained +F: Documentation/hwmon/adt7475 +F: drivers/hwmon/adt7475.c + ADVANSYS SCSI DRIVER M: Matthew Wilcox L: linux-scsi@vger.kernel.org -- cgit v0.10.2 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" diff --git a/Documentation/hwmon/adt7475 b/Documentation/hwmon/adt7475 index 03be1e6..0502f2b 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 957171c..9e640c6 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 72c3b75..99abfdd 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 v0.10.2