From 6328c0e163abfce679b1beffb166f72900bf0a22 Mon Sep 17 00:00:00 2001 From: Denis Vlasenko Date: Wed, 22 Jun 2005 10:25:13 +0300 Subject: [PATCH] I2C: Coding style cleanups to via686a On Wednesday 22 June 2005 08:17, Greg KH wrote: > [PATCH] I2C: Coding style cleanups to via686a > > The via686a hardware monitoring driver has infamous coding style at the > moment. I'd like to clean up the mess before I start working on other > changes to this driver. Is the following patch acceptable? No code > change, only coding style (indentation, alignments, trailing white > space, a few parentheses and a typo). > > Signed-off-by: Jean Delvare > Signed-off-by: Greg Kroah-Hartman Nice. You missed some. This one is on top of your patch: Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/chips/via686a.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/chips/via686a.c b/drivers/i2c/chips/via686a.c index 137d9b7cacd4..164d47948390 100644 --- a/drivers/i2c/chips/via686a.c +++ b/drivers/i2c/chips/via686a.c @@ -1,9 +1,9 @@ /* via686a.c - Part of lm_sensors, Linux kernel modules - for hardware monitoring + for hardware monitoring Copyright (c) 1998 - 2002 Frodo Looijaard , - Kyösti Mälkki , + Kyösti Mälkki , Mark Studebaker , and Bob Dougherty (Some conversion-factor data were contributed by Jonathan Teh Soon Yew @@ -171,18 +171,18 @@ static inline u8 FAN_TO_REG(long rpm, int div) /******** TEMP CONVERSIONS (Bob Dougherty) *********/ /* linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew) if(temp<169) - return double(temp)*0.427-32.08; + return double(temp)*0.427-32.08; else if(temp>=169 && temp<=202) - return double(temp)*0.582-58.16; + return double(temp)*0.582-58.16; else - return double(temp)*0.924-127.33; + return double(temp)*0.924-127.33; A fifth-order polynomial fits the unofficial data (provided by Alex van Kaam ) a bit better. It also give more reasonable numbers on my machine (ie. they agree with what my BIOS tells me). Here's the fifth-order fit to the 8-bit data: temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 - - 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0. + 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0. (2000-10-25- RFD: thanks to Uwe Andersen for finding my typos in this formula!) -- cgit v1.2.3 From 65fc50e50ff9f8b82c3756eccd7e7db6a267ffe9 Mon Sep 17 00:00:00 2001 From: "david-b@pacbell.net" Date: Wed, 29 Jun 2005 07:13:00 -0700 Subject: [PATCH] I2C: minor TPS6501x cleanups This includes various small cleanups and fixes to the TPS 6501x driver that came mostly from review feedback by Jean Delvare; thanks Jean! Also some goofy whitespace gets fixed. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/chips/Kconfig | 1 - drivers/i2c/chips/tps65010.c | 59 +++++++++++++++++++++----------------------- 2 files changed, 28 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index a0982da09803..b28964b08006 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -509,7 +509,6 @@ config TPS65010 This driver can also be built as a module. If so, the module will be called tps65010. - config SENSORS_M41T00 tristate "ST M41T00 RTC chip" depends on I2C && PPC32 diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c index c0ac01b60039..280e9638c0f8 100644 --- a/drivers/i2c/chips/tps65010.c +++ b/drivers/i2c/chips/tps65010.c @@ -18,7 +18,6 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#undef DEBUG #include #include @@ -49,11 +48,7 @@ MODULE_DESCRIPTION("TPS6501x Power Management Driver"); MODULE_LICENSE("GPL"); -/* only two addresses possible */ -#define TPS_BASE 0x48 -static unsigned short normal_i2c[] = { - TPS_BASE, - I2C_CLIENT_END }; +static unsigned short normal_i2c[] = { 0x48, /* 0x49, */ I2C_CLIENT_END }; static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -102,7 +97,7 @@ struct tps65010 { u8 chgstatus, regstatus, chgconf; u8 nmask1, nmask2; - /* plus four GPIOs, probably used to switch power */ + /* not currently tracking GPIO state */ }; #define POWER_POLL_DELAY msecs_to_jiffies(800) @@ -135,7 +130,7 @@ static void dbg_regstat(char *buf, size_t len, u8 regstatus) (regstatus & TPS_REG_COVER) ? " uncover" : "", (regstatus & TPS_REG_UVLO) ? " UVLO" : "", (regstatus & TPS_REG_NO_CHG) ? " NO_CHG" : "", - (regstatus & TPS_REG_PG_LD02) ? " ld01_bad" : "", + (regstatus & TPS_REG_PG_LD02) ? " ld02_bad" : "", (regstatus & TPS_REG_PG_LD01) ? " ld01_bad" : "", (regstatus & TPS_REG_PG_MAIN) ? " main_bad" : "", (regstatus & TPS_REG_PG_CORE) ? " core_bad" : ""); @@ -143,7 +138,7 @@ static void dbg_regstat(char *buf, size_t len, u8 regstatus) static void dbg_chgconf(int por, char *buf, size_t len, u8 chgconfig) { - char *hibit; + const char *hibit; if (por) hibit = (chgconfig & TPS_CHARGE_POR) @@ -295,7 +290,7 @@ static int dbg_show(struct seq_file *s, void *_) seq_printf(s, "defgpio %02x mask3 %02x\n", value, v2); for (i = 0; i < 4; i++) { - if (value & (1 << (4 +i))) + if (value & (1 << (4 + i))) seq_printf(s, " gpio%d-out %s\n", i + 1, (value & (1 << i)) ? "low" : "hi "); else @@ -481,7 +476,7 @@ static int __exit tps65010_detach_client(struct i2c_client *client) debugfs_remove(tps->file); if (i2c_detach_client(client) == 0) kfree(tps); - the_tps = 0; + the_tps = NULL; return 0; } @@ -514,7 +509,6 @@ tps65010_probe(struct i2c_adapter *bus, int address, int kind) INIT_WORK(&tps->work, tps65010_work, tps); tps->irq = -1; tps->client.addr = address; - i2c_set_clientdata(&tps->client, tps); tps->client.adapter = bus; tps->client.driver = &tps65010_driver; strlcpy(tps->client.name, DRIVER_NAME, I2C_NAME_SIZE); @@ -523,9 +517,7 @@ tps65010_probe(struct i2c_adapter *bus, int address, int kind) if (status < 0) { dev_dbg(&bus->dev, "can't attach %s to device %d, err %d\n", DRIVER_NAME, address, status); -fail1: - kfree(tps); - return 0; + goto fail1; } #ifdef CONFIG_ARM @@ -535,7 +527,7 @@ fail1: tps->irq = OMAP_GPIO_IRQ(58); omap_request_gpio(58); omap_set_gpio_direction(58, 1); - omap_set_gpio_edge_ctrl(58, OMAP_GPIO_FALLING_EDGE); + set_irq_type(tps->irq, IRQT_FALLING); } if (machine_is_omap_osk()) { tps->model = TPS65010; @@ -543,7 +535,7 @@ fail1: tps->irq = OMAP_GPIO_IRQ(OMAP_MPUIO(1)); omap_request_gpio(OMAP_MPUIO(1)); omap_set_gpio_direction(OMAP_MPUIO(1), 1); - omap_set_gpio_edge_ctrl(OMAP_MPUIO(1), OMAP_GPIO_FALLING_EDGE); + set_irq_type(tps->irq, IRQT_FALLING); } if (machine_is_omap_h3()) { tps->model = TPS65013; @@ -633,6 +625,9 @@ fail1: tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL, tps, DEBUG_FOPS); return 0; +fail1: + kfree(tps); + return 0; } static int __init tps65010_scan_bus(struct i2c_adapter *bus) @@ -645,7 +640,6 @@ static int __init tps65010_scan_bus(struct i2c_adapter *bus) static struct i2c_driver tps65010_driver = { .owner = THIS_MODULE, .name = "tps65010", - .id = 888, /* FIXME assign "official" value */ .flags = I2C_DF_NOTIFY, .attach_adapter = tps65010_scan_bus, .detach_client = __exit_p(tps65010_detach_client), @@ -744,7 +738,7 @@ int tps65010_set_led(unsigned led, unsigned mode) if (!the_tps) return -ENODEV; - if(led == LED1) + if (led == LED1) offs = 0; else { offs = 2; @@ -753,11 +747,13 @@ int tps65010_set_led(unsigned led, unsigned mode) down(&the_tps->lock); - dev_dbg (&the_tps->client.dev, "led%i_on 0x%02x\n", led, - i2c_smbus_read_byte_data(&the_tps->client, TPS_LED1_ON + offs)); + pr_debug("%s: led%i_on 0x%02x\n", DRIVER_NAME, led, + i2c_smbus_read_byte_data(&the_tps->client, + TPS_LED1_ON + offs)); - dev_dbg (&the_tps->client.dev, "led%i_per 0x%02x\n", led, - i2c_smbus_read_byte_data(&the_tps->client, TPS_LED1_PER + offs)); + pr_debug("%s: led%i_per 0x%02x\n", DRIVER_NAME, led, + i2c_smbus_read_byte_data(&the_tps->client, + TPS_LED1_PER + offs)); switch (mode) { case OFF: @@ -773,7 +769,7 @@ int tps65010_set_led(unsigned led, unsigned mode) led_per = 0x08 | (1 << 7); break; default: - printk(KERN_ERR "%s: Wrong mode parameter for tps65010_set_led()\n", + printk(KERN_ERR "%s: Wrong mode parameter for set_led()\n", DRIVER_NAME); up(&the_tps->lock); return -EINVAL; @@ -789,7 +785,7 @@ int tps65010_set_led(unsigned led, unsigned mode) return status; } - dev_dbg (&the_tps->client.dev, "led%i_on 0x%02x\n", led, + pr_debug("%s: led%i_on 0x%02x\n", DRIVER_NAME, led, i2c_smbus_read_byte_data(&the_tps->client, TPS_LED1_ON + offs)); status = i2c_smbus_write_byte_data(&the_tps->client, @@ -802,8 +798,9 @@ int tps65010_set_led(unsigned led, unsigned mode) return status; } - dev_dbg (&the_tps->client.dev, "led%i_per 0x%02x\n", led, - i2c_smbus_read_byte_data(&the_tps->client, TPS_LED1_PER + offs)); + pr_debug("%s: led%i_per 0x%02x\n", DRIVER_NAME, led, + i2c_smbus_read_byte_data(&the_tps->client, + TPS_LED1_PER + offs)); up(&the_tps->lock); @@ -874,7 +871,7 @@ int tps65010_set_low_pwr(unsigned mode) if (status != 0) printk(KERN_ERR "%s: Failed to write vdcdc1 register\n", - DRIVER_NAME); + DRIVER_NAME); else pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME, i2c_smbus_read_byte_data(&the_tps->client, TPS_VDCDC1)); @@ -900,14 +897,14 @@ int tps65010_config_vregs1(unsigned value) down(&the_tps->lock); pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME, - i2c_smbus_read_byte_data(&the_tps->client, TPS_VREGS1)); + i2c_smbus_read_byte_data(&the_tps->client, TPS_VREGS1)); status = i2c_smbus_write_byte_data(&the_tps->client, TPS_VREGS1, value); if (status != 0) printk(KERN_ERR "%s: Failed to write vregs1 register\n", - DRIVER_NAME); + DRIVER_NAME); else pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME, i2c_smbus_read_byte_data(&the_tps->client, TPS_VREGS1)); @@ -1009,7 +1006,7 @@ static int __init tps_init(void) msleep(10); } -#if defined(CONFIG_ARM) +#ifdef CONFIG_ARM if (machine_is_omap_osk()) { // FIXME: More should be placed in the initialization code -- cgit v1.2.3 From 541e6a02768404efb06bd1ea5f33d614732f41fc Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 23 Jun 2005 22:18:08 +0200 Subject: [PATCH] I2C: Strip trailing whitespace from strings Here is a simple patch originally from Denis Vlasenko, which strips a useless trailing whitespace from 8 strings in 4 i2c drivers. Please apply, thanks. From: Denis Vlasenko Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/algos/i2c-algo-ite.c | 8 ++++---- drivers/i2c/busses/i2c-i801.c | 4 ++-- drivers/i2c/busses/i2c-piix4.c | 2 +- drivers/i2c/busses/i2c-sis5595.c | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/algos/i2c-algo-ite.c b/drivers/i2c/algos/i2c-algo-ite.c index 68e9e6832ca0..e6cae39f47aa 100644 --- a/drivers/i2c/algos/i2c-algo-ite.c +++ b/drivers/i2c/algos/i2c-algo-ite.c @@ -208,7 +208,7 @@ static int test_bus(struct i2c_algo_iic_data *adap, char *name) { goto bailout; } sdalo(adap); - printk("test_bus:1 scl: %d sda: %d \n",getscl(adap), + printk("test_bus:1 scl: %d sda: %d\n", getscl(adap), getsda(adap)); if ( 0 != getsda(adap) ) { printk("test_bus: %s SDA stuck high!\n",name); @@ -221,7 +221,7 @@ static int test_bus(struct i2c_algo_iic_data *adap, char *name) { goto bailout; } sdahi(adap); - printk("test_bus:2 scl: %d sda: %d \n",getscl(adap), + printk("test_bus:2 scl: %d sda: %d\n", getscl(adap), getsda(adap)); if ( 0 == getsda(adap) ) { printk("test_bus: %s SDA stuck low!\n",name); @@ -234,7 +234,7 @@ static int test_bus(struct i2c_algo_iic_data *adap, char *name) { goto bailout; } scllo(adap); - printk("test_bus:3 scl: %d sda: %d \n",getscl(adap), + printk("test_bus:3 scl: %d sda: %d\n", getscl(adap), getsda(adap)); if ( 0 != getscl(adap) ) { @@ -247,7 +247,7 @@ static int test_bus(struct i2c_algo_iic_data *adap, char *name) { goto bailout; } sclhi(adap); - printk("test_bus:4 scl: %d sda: %d \n",getscl(adap), + printk("test_bus:4 scl: %d sda: %d\n", getscl(adap), getsda(adap)); if ( 0 == getscl(adap) ) { printk("test_bus: %s SCL stuck low!\n",name); diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 45e6efb1dcd1..0ab7e37f5b00 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -194,7 +194,7 @@ static int i801_transaction(void) /* Make sure the SMBus host is ready to start transmitting */ /* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { - dev_dbg(&I801_dev->dev, "SMBus busy (%02x). Resetting... \n", + dev_dbg(&I801_dev->dev, "SMBus busy (%02x). Resetting...\n", temp); outb_p(temp, SMBHSTSTS); if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { @@ -315,7 +315,7 @@ static int i801_block_transaction(union i2c_smbus_data *data, char read_write, } if (temp & errmask) { dev_dbg(&I801_dev->dev, "SMBus busy (%02x). " - "Resetting... \n", temp); + "Resetting...\n", temp); outb_p(temp, SMBHSTSTS); if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) { dev_err(&I801_dev->dev, diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 1f80ba9da6f1..6d34ee381ce1 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -243,7 +243,7 @@ static int piix4_transaction(void) /* Make sure the SMBus host is ready to start transmitting */ if ((temp = inb_p(SMBHSTSTS)) != 0x00) { dev_dbg(&piix4_adapter.dev, "SMBus busy (%02x). " - "Resetting... \n", temp); + "Resetting...\n", temp); outb_p(temp, SMBHSTSTS); if ((temp = inb_p(SMBHSTSTS)) != 0x00) { dev_err(&piix4_adapter.dev, "Failed! (%02x)\n", temp); diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c index 2b5911cfb7b5..bbd5e4e52f09 100644 --- a/drivers/i2c/busses/i2c-sis5595.c +++ b/drivers/i2c/busses/i2c-sis5595.c @@ -228,7 +228,7 @@ static int sis5595_transaction(struct i2c_adapter *adap) /* Make sure the SMBus host is ready to start transmitting */ temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8); if (temp != 0x00) { - dev_dbg(&adap->dev, "SMBus busy (%04x). Resetting... \n", temp); + dev_dbg(&adap->dev, "SMBus busy (%04x). Resetting...\n", temp); sis5595_write(SMB_STS_LO, temp & 0xff); sis5595_write(SMB_STS_HI, temp >> 8); if ((temp = sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != 0x00) { -- cgit v1.2.3 From 9ab1ee2ab7d65979c0f14a60ee1f29f8988f5811 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 24 Jun 2005 21:14:16 +0200 Subject: [PATCH] I2C: New max6875 driver may corrupt EEPROMs After a careful code analysis on the new max6875 driver (drivers/i2c/chips/max6875.c), I have come to the conclusion that this driver may cause EEPROM corruptions if used on random systems. The EEPROM part of the MAX6875 chip is accessed using rather uncommon I2C sequences. What is seen by the MAX6875 as reads can be seen by a standard EEPROM (24C02) as writes. If you check the detection method used by the driver, you'll find that the first SMBus command it will send on the bus is i2c_smbus_write_byte_data(client, 0x80, 0x40). For the MAX6875 it makes an internal pointer point to a specific offset of the EEPROM waiting for a subsequent read command, so it's not an actual data write operation, but for a standard EEPROM, this instead means writing value 0x40 to offset 0x80. Blame Philips and Intel for the obscure protocol. Since the MAX6875 and the standard, common 24C02 EEPROMs share two I2C addresses (0x50 and 0x52), loading the max6875 driver on a system with standard EEPROMs at either address will trigger a write on these EEPROMs, which will lead to their corruption if they happen not to be write protected. This kind of EEPROMs can be found on memory modules (SPD), ethernet adapters (MAC address), laptops (proprietary data) and displays (EDID/DDC). Most of these are hopefully write-protected, but not all of them. For this reason, I would recommend that the max6875 driver be neutralized, in a way that nobody can corrupt his/her EEPROMs by just loading the driver. This means either deleting the driver completely, or not listing any default address for it. I'd like this to be done before 2.6.13-rc1 is released. Additionally, the max6875 driver lacks the 24RF08 corruption preventer present in the eeprom driver, which means that loading this driver in a system with such a chip would corrupt it as well. Here is a proposed quick patch addressing the issue, although I wouldn't mind a complete removal if it makes everyone feel safer. I think Ben has plans to replace this driver by a much simplified one anyway. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/chips/max6875.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/chips/max6875.c b/drivers/i2c/chips/max6875.c index fe6b150ec4c2..c4f14d9623c4 100644 --- a/drivers/i2c/chips/max6875.c +++ b/drivers/i2c/chips/max6875.c @@ -37,7 +37,8 @@ #include /* Addresses to scan */ -static unsigned short normal_i2c[] = {0x50, 0x52, I2C_CLIENT_END}; +/* No address scanned by default, as this could corrupt standard EEPROMS. */ +static unsigned short normal_i2c[] = {I2C_CLIENT_END}; static unsigned int normal_isa[] = {I2C_CLIENT_ISA_END}; /* Insmod parameters */ @@ -369,6 +370,9 @@ static int max6875_detect(struct i2c_adapter *adapter, int address, int kind) new_client->driver = &max6875_driver; new_client->flags = 0; + /* Prevent 24RF08 corruption */ + i2c_smbus_write_quick(new_client, 0); + /* Setup the user section */ data->blocks[max6875_eeprom_user].type = max6875_eeprom_user; data->blocks[max6875_eeprom_user].slices = USER_EEPROM_SLICES; -- cgit v1.2.3 From 2146fec20c38d926f0d88413977f941f42a14588 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 23 Jun 2005 23:41:39 +0200 Subject: [PATCH] I2C: max6875 Kconfig update Here is a proposed Kconfig update for the new max6875 i2c chip driver. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/chips/Kconfig | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index b28964b08006..5bd72c480c95 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -519,13 +519,16 @@ config SENSORS_M41T00 will be called m41t00. config SENSORS_MAX6875 - tristate "MAXIM MAX6875 Power supply supervisor" + tristate "Maxim MAX6875 Power supply supervisor" depends on I2C && EXPERIMENTAL help - If you say yes here you get support for the MAX6875 - EEPROM-Programmable, Hex/Quad, Power-Suppy Sequencers/Supervisors. + If you say yes here you get support for the Maxim MAX6875 + EEPROM-programmable, quad power-supply sequencer/supervisor. - This provides a interface to program the EEPROM and reset the chip. + This provides an interface to program the EEPROM and reset the chip. + + This driver also supports the Maxim MAX6874 hex power-supply + sequencer/supervisor if found at a compatible address. This driver can also be built as a module. If so, the module will be called max6875. -- cgit v1.2.3 From 5da69ba42aa42a479c0f5d8cb8351ebb6b51c12e Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 1 Jul 2005 14:28:15 +0200 Subject: [PATCH] I2C: m41t00: fix incorrect kfree Here is a simple path fixing an incorrect kfree in the m41t00 i2c chip driver. The current code happens to work by accident, but the freed pointer isn't the one which was allocated in the first place, which could cause problems later. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/chips/m41t00.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/i2c/chips/m41t00.c b/drivers/i2c/chips/m41t00.c index 5e463c47bfbc..778d7e12859d 100644 --- a/drivers/i2c/chips/m41t00.c +++ b/drivers/i2c/chips/m41t00.c @@ -207,7 +207,7 @@ m41t00_detach(struct i2c_client *client) int rc; if ((rc = i2c_detach_client(client)) == 0) { - kfree(i2c_get_clientdata(client)); + kfree(client); tasklet_kill(&m41t00_tasklet); } return rc; -- cgit v1.2.3 From a0920e10438e9fe8b22aba607083347c84458ed8 Mon Sep 17 00:00:00 2001 From: "Mark M. Hoffman" Date: Tue, 28 Jun 2005 00:21:30 -0400 Subject: [PATCH] i2c: make better use of IDR in i2c-core This patch uses the already existing IDR mechanism to simplify and improve the i2c_get_adapter function in i2c-core. Signed-off-by: Mark M. Hoffman Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/i2c-core.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 51ce268998cd..4fd4f52c8e9b 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -156,7 +156,7 @@ int i2c_add_adapter(struct i2c_adapter *adap) goto out_unlock; } - res = idr_get_new(&i2c_adapter_idr, NULL, &id); + res = idr_get_new(&i2c_adapter_idr, adap, &id); if (res < 0) { if (res == -EAGAIN) res = -ENOMEM; @@ -765,20 +765,15 @@ int i2c_adapter_id(struct i2c_adapter *adap) struct i2c_adapter* i2c_get_adapter(int id) { - struct list_head *item; struct i2c_adapter *adapter; down(&core_lists); - list_for_each(item,&adapters) { - adapter = list_entry(item, struct i2c_adapter, list); - if (id == adapter->nr && - try_module_get(adapter->owner)) { - up(&core_lists); - return adapter; - } - } + adapter = (struct i2c_adapter *)idr_find(&i2c_adapter_idr, id); + if (adapter && !try_module_get(adapter->owner)) + adapter = NULL; + up(&core_lists); - return NULL; + return adapter; } void i2c_put_adapter(struct i2c_adapter *adap) -- cgit v1.2.3 From 2db32767874fe53faff4f80de878ca19927efc1f Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 23 Jun 2005 23:43:00 +0200 Subject: [PATCH] I2C: drop bogus eeprom comment This simple patch drops an out-of-date comment in the eeprom i2c chip driver. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/chips/eeprom.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c index addf0adc24d4..6ea413f6d5e5 100644 --- a/drivers/i2c/chips/eeprom.c +++ b/drivers/i2c/chips/eeprom.c @@ -173,9 +173,6 @@ int eeprom_detect(struct i2c_adapter *adapter, int address, int kind) | I2C_FUNC_SMBUS_BYTE)) goto exit; - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access eeprom_{read,write}_value. */ if (!(data = kmalloc(sizeof(struct eeprom_data), GFP_KERNEL))) { err = -ENOMEM; goto exit; -- cgit v1.2.3 From 80efa8c72006a1c04004f8fb07b22073348e4bf2 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Fri, 1 Jul 2005 00:17:27 +0200 Subject: [PATCH] I2C: SENSORS_ATXP1 must select I2C_SENSOR On Thu, Jun 30, 2005 at 11:47:09PM +0200, Sebastian Pigulak wrote: > I've tried patching linux-2.6.13-RC1 with patch-2.6.13-rc1-git2 and > building atxp1(it allows Vcore voltage changing) into the kernel. > Unfortunately, the kernel compilation stops with: > > LD init/built-in.o > LD vmlinux > drivers/built-in.o(.text+0x92298): In function `atxp1_detect': > : undefined reference to `i2c_which_vrm' > drivers/built-in.o(.text+0x921ae): In function `atxp1_attach_adapter': > : undefined reference to `i2c_detect' > make: *** [vmlinux] B??d 1 > ==> ERROR: Build Failed. Aborting... > > Could someone have a look at the module and possibly fix it up? SENSORS_ATXP1 must select I2C_SENSOR. Signed-off-by: Adrian Bunk Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/chips/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 5bd72c480c95..fddfc11b297f 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -80,6 +80,7 @@ config SENSORS_ASB100 config SENSORS_ATXP1 tristate "Attansic ATXP1 VID controller" depends on I2C && EXPERIMENTAL + select I2C_SENSOR help If you say yes here you get support for the Attansic ATXP1 VID controller. -- cgit v1.2.3 From 0e65f82814e9828d3ff54988de9e7c0b36794daa Mon Sep 17 00:00:00 2001 From: Evgeniy Polyakov Date: Thu, 30 Jun 2005 22:52:38 +0400 Subject: [PATCH] w1: fix CRC calculation on bigendian platforms. In the 2.6.13-rc1 code the "rn" structure is in the wrong-endianness when passed to w1_attach_slave_device(). This causes problems like the family and crc being swapped around. Signed-off-by: Roger Blofeld Signed-off-by: Evgeniy Polyakov Signed-off-by: Greg Kroah-Hartman --- drivers/w1/w1.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 312cf3220f12..8a9c42822502 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -516,6 +516,7 @@ static void w1_slave_found(unsigned long data, u64 rn) struct w1_reg_num *tmp; int family_found = 0; struct w1_master *dev; + u64 rn_le = cpu_to_le64(rn); dev = w1_search_master(data); if (!dev) { @@ -544,10 +545,8 @@ static void w1_slave_found(unsigned long data, u64 rn) slave_count++; } - rn = cpu_to_le64(rn); - if (slave_count == dev->slave_count && - rn && ((le64_to_cpu(rn) >> 56) & 0xff) == w1_calc_crc8((u8 *)&rn, 7)) { + rn && ((rn >> 56) & 0xff) == w1_calc_crc8((u8 *)&rn_le, 7)) { w1_attach_slave_device(dev, tmp); } -- cgit v1.2.3 From ad2f931dcb41bcfae38cc77d78b7821dfef83cf2 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sat, 2 Jul 2005 18:15:49 +0200 Subject: [PATCH] I2C: Move hwmon drivers (1/3) Part 1: Configuration files and Makefiles. From: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- arch/arm/Kconfig | 2 + arch/h8300/Kconfig | 2 + arch/sparc64/Kconfig | 2 + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/hwmon/Kconfig | 420 +++++++++++++++++++++++++++++++++++++++++++++ drivers/hwmon/Makefile | 44 +++++ drivers/i2c/chips/Kconfig | 402 +------------------------------------------ drivers/i2c/chips/Makefile | 38 +--- 9 files changed, 476 insertions(+), 437 deletions(-) create mode 100644 drivers/hwmon/Kconfig create mode 100644 drivers/hwmon/Makefile (limited to 'drivers') diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 8752751f9985..0fb8a04c3115 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -744,6 +744,8 @@ source "drivers/char/Kconfig" source "drivers/i2c/Kconfig" +source "drivers/hwmon/Kconfig" + #source "drivers/l3/Kconfig" source "drivers/misc/Kconfig" diff --git a/arch/h8300/Kconfig b/arch/h8300/Kconfig index 62a89e812e3e..917286087c54 100644 --- a/arch/h8300/Kconfig +++ b/arch/h8300/Kconfig @@ -179,6 +179,8 @@ source "drivers/serial/Kconfig" source "drivers/i2c/Kconfig" +source "drivers/hwmon/Kconfig" + source "drivers/usb/Kconfig" endmenu diff --git a/arch/sparc64/Kconfig b/arch/sparc64/Kconfig index 6a4733683f0f..5e8591c427fb 100644 --- a/arch/sparc64/Kconfig +++ b/arch/sparc64/Kconfig @@ -647,6 +647,8 @@ source "drivers/input/Kconfig" source "drivers/i2c/Kconfig" +source "drivers/hwmon/Kconfig" + source "fs/Kconfig" source "drivers/media/Kconfig" diff --git a/drivers/Kconfig b/drivers/Kconfig index aed4a9b97c14..cf087de3b3a1 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -44,6 +44,8 @@ source "drivers/i2c/Kconfig" source "drivers/w1/Kconfig" +source "drivers/hwmon/Kconfig" + source "drivers/misc/Kconfig" source "drivers/media/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 3167be54fedd..126a851d5653 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -52,6 +52,7 @@ obj-$(CONFIG_INPUT) += input/ obj-$(CONFIG_I2O) += message/ obj-$(CONFIG_I2C) += i2c/ obj-$(CONFIG_W1) += w1/ +obj-$(CONFIG_HWMON) += hwmon/ obj-$(CONFIG_PHONE) += telephony/ obj-$(CONFIG_MD) += md/ obj-$(CONFIG_BT) += bluetooth/ diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig new file mode 100644 index 000000000000..140d5f851a5b --- /dev/null +++ b/drivers/hwmon/Kconfig @@ -0,0 +1,420 @@ +# +# I2C Sensor chip drivers configuration +# + +menu "Hardware Monitoring support" + +config HWMON + tristate "Hardware Monitoring support" + default y + help + Hardware monitoring devices let you monitor the hardware health + of a system. Most modern motherboards include such a device. It + can include temperature sensors, voltage sensors, fan speed + sensors and various additional features such as the ability to + control the speed of the fans. + +config SENSORS_ADM1021 + tristate "Analog Devices ADM1021 and compatibles" + depends on HWMON && I2C + select I2C_SENSOR + help + If you say yes here you get support for Analog Devices ADM1021 + and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, + Genesys Logic GL523SM, National Semiconductor LM84, TI THMC10, + and the XEON processor built-in sensor. + + This driver can also be built as a module. If so, the module + will be called adm1021. + +config SENSORS_ADM1025 + tristate "Analog Devices ADM1025 and compatibles" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for Analog Devices ADM1025 + and Philips NE1619 sensor chips. + + This driver can also be built as a module. If so, the module + will be called adm1025. + +config SENSORS_ADM1026 + tristate "Analog Devices ADM1026 and compatibles" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for Analog Devices ADM1026 + sensor chip. + + This driver can also be built as a module. If so, the module + will be called adm1026. + +config SENSORS_ADM1031 + tristate "Analog Devices ADM1031 and compatibles" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for Analog Devices ADM1031 + and ADM1030 sensor chips. + + This driver can also be built as a module. If so, the module + will be called adm1031. + +config SENSORS_ADM9240 + tristate "Analog Devices ADM9240 and compatibles" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for Analog Devices ADM9240, + Dallas DS1780, National Semiconductor LM81 sensor chips. + + This driver can also be built as a module. If so, the module + will be called adm9240. + +config SENSORS_ASB100 + tristate "Asus ASB100 Bach" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for the ASB100 Bach sensor + chip found on some Asus mainboards. + + This driver can also be built as a module. If so, the module + will be called asb100. + +config SENSORS_ATXP1 + tristate "Attansic ATXP1 VID controller" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for the Attansic ATXP1 VID + controller. + + If your board have such a chip, you are able to control your CPU + core and other voltages. + + This driver can also be built as a module. If so, the module + will be called atxp1. + +config SENSORS_DS1621 + tristate "Dallas Semiconductor DS1621 and DS1625" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for Dallas Semiconductor + DS1621 and DS1625 sensor chips. + + This driver can also be built as a module. If so, the module + will be called ds1621. + +config SENSORS_FSCHER + tristate "FSC Hermes" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for Fujitsu Siemens + Computers Hermes sensor chips. + + This driver can also be built as a module. If so, the module + will be called fscher. + +config SENSORS_FSCPOS + tristate "FSC Poseidon" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for Fujitsu Siemens + Computers Poseidon sensor chips. + + This driver can also be built as a module. If so, the module + will be called fscpos. + +config SENSORS_GL518SM + tristate "Genesys Logic GL518SM" + depends on HWMON && I2C + select I2C_SENSOR + help + If you say yes here you get support for Genesys Logic GL518SM + sensor chips. + + This driver can also be built as a module. If so, the module + will be called gl518sm. + +config SENSORS_GL520SM + tristate "Genesys Logic GL520SM" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for Genesys Logic GL520SM + sensor chips. + + This driver can also be built as a module. If so, the module + will be called gl520sm. + +config SENSORS_IT87 + tristate "ITE IT87xx and compatibles" + depends on HWMON && I2C + select I2C_SENSOR + help + If you say yes here you get support for ITE IT87xx sensor chips + and clones: SiS960. + + This driver can also be built as a module. If so, the module + will be called it87. + +config SENSORS_LM63 + tristate "National Semiconductor LM63" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for the National Semiconductor + LM63 remote diode digital temperature sensor with integrated fan + control. Such chips are found on the Tyan S4882 (Thunder K8QS Pro) + motherboard, among others. + + This driver can also be built as a module. If so, the module + will be called lm63. + +config SENSORS_LM75 + tristate "National Semiconductor LM75 and compatibles" + depends on HWMON && I2C + select I2C_SENSOR + help + If you say yes here you get support for National Semiconductor LM75 + sensor chips and clones: Dallas Semiconductor DS75 and DS1775 (in + 9-bit precision mode), and TelCom (now Microchip) TCN75. + + The DS75 and DS1775 in 10- to 12-bit precision modes will require + a force module parameter. The driver will not handle the extra + precision anyhow. + + This driver can also be built as a module. If so, the module + will be called lm75. + +config SENSORS_LM77 + tristate "National Semiconductor LM77" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for National Semiconductor LM77 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called lm77. + +config SENSORS_LM78 + tristate "National Semiconductor LM78 and compatibles" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for National Semiconductor LM78, + LM78-J and LM79. + + This driver can also be built as a module. If so, the module + will be called lm78. + +config SENSORS_LM80 + tristate "National Semiconductor LM80" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for National Semiconductor + LM80 sensor chips. + + This driver can also be built as a module. If so, the module + will be called lm80. + +config SENSORS_LM83 + tristate "National Semiconductor LM83" + depends on HWMON && I2C + select I2C_SENSOR + help + If you say yes here you get support for National Semiconductor + LM83 sensor chips. + + This driver can also be built as a module. If so, the module + will be called lm83. + +config SENSORS_LM85 + tristate "National Semiconductor LM85 and compatibles" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for National Semiconductor LM85 + sensor chips and clones: ADT7463, EMC6D100, EMC6D102 and ADM1027. + + This driver can also be built as a module. If so, the module + will be called lm85. + +config SENSORS_LM87 + tristate "National Semiconductor LM87" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for National Semiconductor LM87 + sensor chips. + + This driver can also be built as a module. If so, the module + will be called lm87. + +config SENSORS_LM90 + tristate "National Semiconductor LM90 and compatibles" + depends on HWMON && I2C + select I2C_SENSOR + help + If you say yes here you get support for National Semiconductor LM90, + LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657 and + MAX6658 sensor chips. + + The Analog Devices ADT7461 sensor chip is also supported, but only + if found in ADM1032 compatibility mode. + + This driver can also be built as a module. If so, the module + will be called lm90. + +config SENSORS_LM92 + tristate "National Semiconductor LM92 and compatibles" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for National Semiconductor LM92 + and Maxim MAX6635 sensor chips. + + This driver can also be built as a module. If so, the module + will be called lm92. + +config SENSORS_MAX1619 + tristate "Maxim MAX1619 sensor chip" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for MAX1619 sensor chip. + + This driver can also be built as a module. If so, the module + will be called max1619. + +config SENSORS_PC87360 + tristate "National Semiconductor PC87360 family" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + select I2C_ISA + help + If you say yes here you get access to the hardware monitoring + functions of the National Semiconductor PC8736x Super-I/O chips. + The PC87360, PC87363 and PC87364 only have fan monitoring and + control. The PC87365 and PC87366 additionally have voltage and + temperature monitoring. + + This driver can also be built as a module. If so, the module + will be called pc87360. + +config SENSORS_SIS5595 + tristate "Silicon Integrated Systems Corp. SiS5595" + depends on HWMON && I2C && PCI && EXPERIMENTAL + select I2C_SENSOR + select I2C_ISA + help + If you say yes here you get support for the integrated sensors in + SiS5595 South Bridges. + + This driver can also be built as a module. If so, the module + will be called sis5595. + +config SENSORS_SMSC47M1 + tristate "SMSC LPC47M10x and compatibles" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + select I2C_ISA + help + If you say yes here you get support for the integrated fan + monitoring and control capabilities of the SMSC LPC47B27x, + LPC47M10x, LPC47M13x, LPC47M14x, LPC47M15x and LPC47M192 chips. + + This driver can also be built as a module. If so, the module + will be called smsc47m1. + +config SENSORS_SMSC47B397 + tristate "SMSC LPC47B397-NC" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + select I2C_ISA + help + If you say yes here you get support for the SMSC LPC47B397-NC + sensor chip. + + This driver can also be built as a module. If so, the module + will be called smsc47b397. + +config SENSORS_VIA686A + tristate "VIA686A" + depends on HWMON && I2C && PCI + select I2C_SENSOR + select I2C_ISA + help + If you say yes here you get support for the integrated sensors in + Via 686A/B South Bridges. + + This driver can also be built as a module. If so, the module + will be called via686a. + +config SENSORS_W83781D + tristate "Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F" + depends on HWMON && I2C + select I2C_SENSOR + help + If you say yes here you get support for the Winbond W8378x series + of sensor chips: the W83781D, W83782D, W83783S and W83627HF, + and the similar Asus AS99127F. + + This driver can also be built as a module. If so, the module + will be called w83781d. + +config SENSORS_W83L785TS + tristate "Winbond W83L785TS-S" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for the Winbond W83L785TS-S + sensor chip, which is used on the Asus A7N8X, among other + motherboards. + + This driver can also be built as a module. If so, the module + will be called w83l785ts. + +config SENSORS_W83627HF + tristate "Winbond W83627HF, W83627THF, W83637HF, W83697HF" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + select I2C_ISA + help + If you say yes here you get support for the Winbond W836X7 series + of sensor chips: the W83627HF, W83627THF, W83637HF, and the W83697HF + + This driver can also be built as a module. If so, the module + will be called w83627hf. + +config SENSORS_W83627EHF + tristate "Winbond W83627EHF" + depends on HWMON && I2C && EXPERIMENTAL + select I2C_SENSOR + select I2C_ISA + help + If you say yes here you get preliminary support for the hardware + monitoring functionality of the Winbond W83627EHF Super-I/O chip. + Only fan and temperature inputs are supported at the moment, while + the chip does much more than that. + + This driver can also be built as a module. If so, the module + will be called w83627ehf. + +config HWMON_DEBUG_CHIP + bool "Hardware Monitoring Chip debugging messages" + depends on HWMON + default n + help + Say Y here if you want the I2C chip drivers to produce a bunch of + debug messages to the system log. Select this if you are having + a problem with I2C support and want to see more of what is going + on. + +endmenu diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile new file mode 100644 index 000000000000..2781403a0236 --- /dev/null +++ b/drivers/hwmon/Makefile @@ -0,0 +1,44 @@ +# +# Makefile for sensor chip drivers. +# + +# asb100, then w83781d go first, as they can override other drivers' addresses. +obj-$(CONFIG_SENSORS_ASB100) += asb100.o +obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o +obj-$(CONFIG_SENSORS_W83781D) += w83781d.o + +obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o +obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o +obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o +obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o +obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o +obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o +obj-$(CONFIG_SENSORS_DS1621) += ds1621.o +obj-$(CONFIG_SENSORS_FSCHER) += fscher.o +obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o +obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o +obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_IT87) += it87.o +obj-$(CONFIG_SENSORS_LM63) += lm63.o +obj-$(CONFIG_SENSORS_LM75) += lm75.o +obj-$(CONFIG_SENSORS_LM77) += lm77.o +obj-$(CONFIG_SENSORS_LM78) += lm78.o +obj-$(CONFIG_SENSORS_LM80) += lm80.o +obj-$(CONFIG_SENSORS_LM83) += lm83.o +obj-$(CONFIG_SENSORS_LM85) += lm85.o +obj-$(CONFIG_SENSORS_LM87) += lm87.o +obj-$(CONFIG_SENSORS_LM90) += lm90.o +obj-$(CONFIG_SENSORS_LM92) += lm92.o +obj-$(CONFIG_SENSORS_MAX1619) += max1619.o +obj-$(CONFIG_SENSORS_PC87360) += pc87360.o +obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o +obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o +obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o +obj-$(CONFIG_SENSORS_VIA686A) += via686a.o +obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o +obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o + +ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y) +EXTRA_CFLAGS += -DDEBUG +endif + diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index fddfc11b297f..43f70dbfc03f 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -1,410 +1,12 @@ # -# I2C Sensor and "other" chip configuration +# Miscellaneous I2C chip drivers configuration # -menu "Hardware Sensors Chip support" - depends on I2C - config I2C_SENSOR tristate default n -config SENSORS_ADM1021 - tristate "Analog Devices ADM1021 and compatibles" - depends on I2C - select I2C_SENSOR - help - If you say yes here you get support for Analog Devices ADM1021 - and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, - Genesys Logic GL523SM, National Semiconductor LM84, TI THMC10, - and the XEON processor built-in sensor. - - This driver can also be built as a module. If so, the module - will be called adm1021. - -config SENSORS_ADM1025 - tristate "Analog Devices ADM1025 and compatibles" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for Analog Devices ADM1025 - and Philips NE1619 sensor chips. - - This driver can also be built as a module. If so, the module - will be called adm1025. - -config SENSORS_ADM1026 - tristate "Analog Devices ADM1026 and compatibles" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for Analog Devices ADM1026 - sensor chip. - - This driver can also be built as a module. If so, the module - will be called adm1026. - -config SENSORS_ADM1031 - tristate "Analog Devices ADM1031 and compatibles" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for Analog Devices ADM1031 - and ADM1030 sensor chips. - - This driver can also be built as a module. If so, the module - will be called adm1031. - -config SENSORS_ADM9240 - tristate "Analog Devices ADM9240 and compatibles" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for Analog Devices ADM9240, - Dallas DS1780, National Semiconductor LM81 sensor chips. - - This driver can also be built as a module. If so, the module - will be called adm9240. - -config SENSORS_ASB100 - tristate "Asus ASB100 Bach" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for the ASB100 Bach sensor - chip found on some Asus mainboards. - - This driver can also be built as a module. If so, the module - will be called asb100. - -config SENSORS_ATXP1 - tristate "Attansic ATXP1 VID controller" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for the Attansic ATXP1 VID - controller. - - If your board have such a chip, you are able to control your CPU - core and other voltages. - - This driver can also be built as a module. If so, the module - will be called atxp1. - -config SENSORS_DS1621 - tristate "Dallas Semiconductor DS1621 and DS1625" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for Dallas Semiconductor - DS1621 and DS1625 sensor chips. - - This driver can also be built as a module. If so, the module - will be called ds1621. - -config SENSORS_FSCHER - tristate "FSC Hermes" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for Fujitsu Siemens - Computers Hermes sensor chips. - - This driver can also be built as a module. If so, the module - will be called fscher. - -config SENSORS_FSCPOS - tristate "FSC Poseidon" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for Fujitsu Siemens - Computers Poseidon sensor chips. - - This driver can also be built as a module. If so, the module - will be called fscpos. - -config SENSORS_GL518SM - tristate "Genesys Logic GL518SM" - depends on I2C - select I2C_SENSOR - help - If you say yes here you get support for Genesys Logic GL518SM - sensor chips. - - This driver can also be built as a module. If so, the module - will be called gl518sm. - -config SENSORS_GL520SM - tristate "Genesys Logic GL520SM" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for Genesys Logic GL520SM - sensor chips. - - This driver can also be built as a module. If so, the module - will be called gl520sm. - -config SENSORS_IT87 - tristate "ITE IT87xx and compatibles" - depends on I2C - select I2C_SENSOR - help - If you say yes here you get support for ITE IT87xx sensor chips - and clones: SiS960. - - This driver can also be built as a module. If so, the module - will be called it87. - -config SENSORS_LM63 - tristate "National Semiconductor LM63" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for the National Semiconductor - LM63 remote diode digital temperature sensor with integrated fan - control. Such chips are found on the Tyan S4882 (Thunder K8QS Pro) - motherboard, among others. - - This driver can also be built as a module. If so, the module - will be called lm63. - -config SENSORS_LM75 - tristate "National Semiconductor LM75 and compatibles" - depends on I2C - select I2C_SENSOR - help - If you say yes here you get support for National Semiconductor LM75 - sensor chips and clones: Dallas Semiconductor DS75 and DS1775 (in - 9-bit precision mode), and TelCom (now Microchip) TCN75. - - The DS75 and DS1775 in 10- to 12-bit precision modes will require - a force module parameter. The driver will not handle the extra - precision anyhow. - - This driver can also be built as a module. If so, the module - will be called lm75. - -config SENSORS_LM77 - tristate "National Semiconductor LM77" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for National Semiconductor LM77 - sensor chips. - - This driver can also be built as a module. If so, the module - will be called lm77. - -config SENSORS_LM78 - tristate "National Semiconductor LM78 and compatibles" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for National Semiconductor LM78, - LM78-J and LM79. - - This driver can also be built as a module. If so, the module - will be called lm78. - -config SENSORS_LM80 - tristate "National Semiconductor LM80" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for National Semiconductor - LM80 sensor chips. - - This driver can also be built as a module. If so, the module - will be called lm80. - -config SENSORS_LM83 - tristate "National Semiconductor LM83" - depends on I2C - select I2C_SENSOR - help - If you say yes here you get support for National Semiconductor - LM83 sensor chips. - - This driver can also be built as a module. If so, the module - will be called lm83. - -config SENSORS_LM85 - tristate "National Semiconductor LM85 and compatibles" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for National Semiconductor LM85 - sensor chips and clones: ADT7463, EMC6D100, EMC6D102 and ADM1027. - - This driver can also be built as a module. If so, the module - will be called lm85. - -config SENSORS_LM87 - tristate "National Semiconductor LM87" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for National Semiconductor LM87 - sensor chips. - - This driver can also be built as a module. If so, the module - will be called lm87. - -config SENSORS_LM90 - tristate "National Semiconductor LM90 and compatibles" - depends on I2C - select I2C_SENSOR - help - If you say yes here you get support for National Semiconductor LM90, - LM86, LM89 and LM99, Analog Devices ADM1032 and Maxim MAX6657 and - MAX6658 sensor chips. - - The Analog Devices ADT7461 sensor chip is also supported, but only - if found in ADM1032 compatibility mode. - - This driver can also be built as a module. If so, the module - will be called lm90. - -config SENSORS_LM92 - tristate "National Semiconductor LM92 and compatibles" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for National Semiconductor LM92 - and Maxim MAX6635 sensor chips. - - This driver can also be built as a module. If so, the module - will be called lm92. - -config SENSORS_MAX1619 - tristate "Maxim MAX1619 sensor chip" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for MAX1619 sensor chip. - - This driver can also be built as a module. If so, the module - will be called max1619. - -config SENSORS_PC87360 - tristate "National Semiconductor PC87360 family" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - select I2C_ISA - help - If you say yes here you get access to the hardware monitoring - functions of the National Semiconductor PC8736x Super-I/O chips. - The PC87360, PC87363 and PC87364 only have fan monitoring and - control. The PC87365 and PC87366 additionally have voltage and - temperature monitoring. - - This driver can also be built as a module. If so, the module - will be called pc87360. - -config SENSORS_SMSC47B397 - tristate "SMSC LPC47B397-NC" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - select I2C_ISA - help - If you say yes here you get support for the SMSC LPC47B397-NC - sensor chip. - - This driver can also be built as a module. If so, the module - will be called smsc47b397. - -config SENSORS_SIS5595 - tristate "Silicon Integrated Systems Corp. SiS5595" - depends on I2C && PCI && EXPERIMENTAL - select I2C_SENSOR - select I2C_ISA - help - If you say yes here you get support for the integrated sensors in - SiS5595 South Bridges. - - This driver can also be built as a module. If so, the module - will be called sis5595. - -config SENSORS_SMSC47M1 - tristate "SMSC LPC47M10x and compatibles" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - select I2C_ISA - help - If you say yes here you get support for the integrated fan - monitoring and control capabilities of the SMSC LPC47B27x, - LPC47M10x, LPC47M13x, LPC47M14x, LPC47M15x and LPC47M192 chips. - - This driver can also be built as a module. If so, the module - will be called smsc47m1. - -config SENSORS_VIA686A - tristate "VIA686A" - depends on I2C && PCI - select I2C_SENSOR - select I2C_ISA - help - If you say yes here you get support for the integrated sensors in - Via 686A/B South Bridges. - - This driver can also be built as a module. If so, the module - will be called via686a. - -config SENSORS_W83781D - tristate "Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F" - depends on I2C - select I2C_SENSOR - help - If you say yes here you get support for the Winbond W8378x series - of sensor chips: the W83781D, W83782D, W83783S and W83627HF, - and the similar Asus AS99127F. - - This driver can also be built as a module. If so, the module - will be called w83781d. - -config SENSORS_W83L785TS - tristate "Winbond W83L785TS-S" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - help - If you say yes here you get support for the Winbond W83L785TS-S - sensor chip, which is used on the Asus A7N8X, among other - motherboards. - - This driver can also be built as a module. If so, the module - will be called w83l785ts. - -config SENSORS_W83627HF - tristate "Winbond W83627HF, W83627THF, W83637HF, W83697HF" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - select I2C_ISA - help - If you say yes here you get support for the Winbond W836X7 series - of sensor chips: the W83627HF, W83627THF, W83637HF, and the W83697HF - - This driver can also be built as a module. If so, the module - will be called w83627hf. - -config SENSORS_W83627EHF - tristate "Winbond W83627EHF" - depends on I2C && EXPERIMENTAL - select I2C_SENSOR - select I2C_ISA - help - If you say yes here you get preliminary support for the hardware - monitoring functionality of the Winbond W83627EHF Super-I/O chip. - Only fan and temperature inputs are supported at the moment, while - the chip does much more than that. - - This driver can also be built as a module. If so, the module - will be called w83627ehf. - -endmenu - -menu "Other I2C Chip support" +menu "Miscellaneous I2C Chip support" depends on I2C config SENSORS_DS1337 diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index b5e6d2f84f97..a876dd42b860 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -1,52 +1,16 @@ # -# Makefile for sensor and "other" I2C chip drivers. +# Makefile for miscellaneous I2C chip drivers. # -# asb100, then w83781d go first, as they can override other drivers' addresses. -obj-$(CONFIG_SENSORS_ASB100) += asb100.o -obj-$(CONFIG_SENSORS_W83627HF) += w83627hf.o -obj-$(CONFIG_SENSORS_W83781D) += w83781d.o - -obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o -obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o -obj-$(CONFIG_SENSORS_ADM1026) += adm1026.o -obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o -obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o -obj-$(CONFIG_SENSORS_ATXP1) += atxp1.o obj-$(CONFIG_SENSORS_DS1337) += ds1337.o obj-$(CONFIG_SENSORS_DS1374) += ds1374.o -obj-$(CONFIG_SENSORS_DS1621) += ds1621.o obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o -obj-$(CONFIG_SENSORS_FSCHER) += fscher.o -obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o -obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o -obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o -obj-$(CONFIG_SENSORS_IT87) += it87.o -obj-$(CONFIG_SENSORS_LM63) += lm63.o -obj-$(CONFIG_SENSORS_LM75) += lm75.o -obj-$(CONFIG_SENSORS_LM77) += lm77.o -obj-$(CONFIG_SENSORS_LM78) += lm78.o -obj-$(CONFIG_SENSORS_LM80) += lm80.o -obj-$(CONFIG_SENSORS_LM83) += lm83.o -obj-$(CONFIG_SENSORS_LM85) += lm85.o -obj-$(CONFIG_SENSORS_LM87) += lm87.o -obj-$(CONFIG_SENSORS_LM90) += lm90.o -obj-$(CONFIG_SENSORS_LM92) += lm92.o -obj-$(CONFIG_SENSORS_MAX1619) += max1619.o obj-$(CONFIG_SENSORS_MAX6875) += max6875.o obj-$(CONFIG_SENSORS_M41T00) += m41t00.o -obj-$(CONFIG_SENSORS_PC87360) += pc87360.o obj-$(CONFIG_SENSORS_PCA9539) += pca9539.o obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o -obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o -obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o -obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o -obj-$(CONFIG_SENSORS_VIA686A) += via686a.o -obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o -obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o - obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TPS65010) += tps65010.o -- cgit v1.2.3 From 8d5d45fb14680326f833295f2316a4ec5e357220 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sat, 2 Jul 2005 18:20:26 +0200 Subject: [PATCH] I2C: Move hwmon drivers (2/3) Part 2: Move the driver files themselves. Note that the patch "adds trailing whitespace", because it does move the files as-is, and some files happen to have trailing whitespace. From: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/adm1021.c | 402 ++++++++++ drivers/hwmon/adm1025.c | 577 ++++++++++++++ drivers/hwmon/adm1026.c | 1714 ++++++++++++++++++++++++++++++++++++++++ drivers/hwmon/adm1031.c | 977 +++++++++++++++++++++++ drivers/hwmon/adm9240.c | 791 ++++++++++++++++++ drivers/hwmon/asb100.c | 1065 +++++++++++++++++++++++++ drivers/hwmon/atxp1.c | 361 +++++++++ drivers/hwmon/ds1621.c | 341 ++++++++ drivers/hwmon/fscher.c | 691 ++++++++++++++++ drivers/hwmon/fscpos.c | 641 +++++++++++++++ drivers/hwmon/gl518sm.c | 604 ++++++++++++++ drivers/hwmon/gl520sm.c | 769 ++++++++++++++++++ drivers/hwmon/it87.c | 1184 +++++++++++++++++++++++++++ drivers/hwmon/lm63.c | 597 ++++++++++++++ drivers/hwmon/lm75.c | 296 +++++++ drivers/hwmon/lm75.h | 49 ++ drivers/hwmon/lm77.c | 420 ++++++++++ drivers/hwmon/lm78.c | 795 +++++++++++++++++++ drivers/hwmon/lm80.c | 601 ++++++++++++++ drivers/hwmon/lm83.c | 408 ++++++++++ drivers/hwmon/lm85.c | 1575 ++++++++++++++++++++++++++++++++++++ drivers/hwmon/lm87.c | 828 +++++++++++++++++++ drivers/hwmon/lm90.c | 655 +++++++++++++++ drivers/hwmon/lm92.c | 429 ++++++++++ drivers/hwmon/max1619.c | 372 +++++++++ drivers/hwmon/pc87360.c | 1348 +++++++++++++++++++++++++++++++ drivers/hwmon/sis5595.c | 817 +++++++++++++++++++ drivers/hwmon/smsc47b397.c | 352 +++++++++ drivers/hwmon/smsc47m1.c | 593 ++++++++++++++ drivers/hwmon/via686a.c | 875 ++++++++++++++++++++ drivers/hwmon/w83627ehf.c | 846 ++++++++++++++++++++ drivers/hwmon/w83627hf.c | 1511 +++++++++++++++++++++++++++++++++++ drivers/hwmon/w83781d.c | 1632 ++++++++++++++++++++++++++++++++++++++ drivers/hwmon/w83l785ts.c | 328 ++++++++ drivers/i2c/chips/adm1021.c | 402 ---------- drivers/i2c/chips/adm1025.c | 577 -------------- drivers/i2c/chips/adm1026.c | 1714 ---------------------------------------- drivers/i2c/chips/adm1031.c | 977 ----------------------- drivers/i2c/chips/adm9240.c | 791 ------------------ drivers/i2c/chips/asb100.c | 1065 ------------------------- drivers/i2c/chips/atxp1.c | 361 --------- drivers/i2c/chips/ds1621.c | 341 -------- drivers/i2c/chips/fscher.c | 691 ---------------- drivers/i2c/chips/fscpos.c | 641 --------------- drivers/i2c/chips/gl518sm.c | 604 -------------- drivers/i2c/chips/gl520sm.c | 769 ------------------ drivers/i2c/chips/it87.c | 1184 --------------------------- drivers/i2c/chips/lm63.c | 597 -------------- drivers/i2c/chips/lm75.c | 296 ------- drivers/i2c/chips/lm75.h | 49 -- drivers/i2c/chips/lm77.c | 420 ---------- drivers/i2c/chips/lm78.c | 795 ------------------- drivers/i2c/chips/lm80.c | 601 -------------- drivers/i2c/chips/lm83.c | 408 ---------- drivers/i2c/chips/lm85.c | 1575 ------------------------------------ drivers/i2c/chips/lm87.c | 828 ------------------- drivers/i2c/chips/lm90.c | 655 --------------- drivers/i2c/chips/lm92.c | 429 ---------- drivers/i2c/chips/max1619.c | 372 --------- drivers/i2c/chips/pc87360.c | 1348 ------------------------------- drivers/i2c/chips/sis5595.c | 817 ------------------- drivers/i2c/chips/smsc47b397.c | 352 --------- drivers/i2c/chips/smsc47m1.c | 593 -------------- drivers/i2c/chips/via686a.c | 875 -------------------- drivers/i2c/chips/w83627ehf.c | 846 -------------------- drivers/i2c/chips/w83627hf.c | 1511 ----------------------------------- drivers/i2c/chips/w83781d.c | 1632 -------------------------------------- drivers/i2c/chips/w83l785ts.c | 328 -------- 68 files changed, 25444 insertions(+), 25444 deletions(-) create mode 100644 drivers/hwmon/adm1021.c create mode 100644 drivers/hwmon/adm1025.c create mode 100644 drivers/hwmon/adm1026.c create mode 100644 drivers/hwmon/adm1031.c create mode 100644 drivers/hwmon/adm9240.c create mode 100644 drivers/hwmon/asb100.c create mode 100644 drivers/hwmon/atxp1.c create mode 100644 drivers/hwmon/ds1621.c create mode 100644 drivers/hwmon/fscher.c create mode 100644 drivers/hwmon/fscpos.c create mode 100644 drivers/hwmon/gl518sm.c create mode 100644 drivers/hwmon/gl520sm.c create mode 100644 drivers/hwmon/it87.c create mode 100644 drivers/hwmon/lm63.c create mode 100644 drivers/hwmon/lm75.c create mode 100644 drivers/hwmon/lm75.h create mode 100644 drivers/hwmon/lm77.c create mode 100644 drivers/hwmon/lm78.c create mode 100644 drivers/hwmon/lm80.c create mode 100644 drivers/hwmon/lm83.c create mode 100644 drivers/hwmon/lm85.c create mode 100644 drivers/hwmon/lm87.c create mode 100644 drivers/hwmon/lm90.c create mode 100644 drivers/hwmon/lm92.c create mode 100644 drivers/hwmon/max1619.c create mode 100644 drivers/hwmon/pc87360.c create mode 100644 drivers/hwmon/sis5595.c create mode 100644 drivers/hwmon/smsc47b397.c create mode 100644 drivers/hwmon/smsc47m1.c create mode 100644 drivers/hwmon/via686a.c create mode 100644 drivers/hwmon/w83627ehf.c create mode 100644 drivers/hwmon/w83627hf.c create mode 100644 drivers/hwmon/w83781d.c create mode 100644 drivers/hwmon/w83l785ts.c delete mode 100644 drivers/i2c/chips/adm1021.c delete mode 100644 drivers/i2c/chips/adm1025.c delete mode 100644 drivers/i2c/chips/adm1026.c delete mode 100644 drivers/i2c/chips/adm1031.c delete mode 100644 drivers/i2c/chips/adm9240.c delete mode 100644 drivers/i2c/chips/asb100.c delete mode 100644 drivers/i2c/chips/atxp1.c delete mode 100644 drivers/i2c/chips/ds1621.c delete mode 100644 drivers/i2c/chips/fscher.c delete mode 100644 drivers/i2c/chips/fscpos.c delete mode 100644 drivers/i2c/chips/gl518sm.c delete mode 100644 drivers/i2c/chips/gl520sm.c delete mode 100644 drivers/i2c/chips/it87.c delete mode 100644 drivers/i2c/chips/lm63.c delete mode 100644 drivers/i2c/chips/lm75.c delete mode 100644 drivers/i2c/chips/lm75.h delete mode 100644 drivers/i2c/chips/lm77.c delete mode 100644 drivers/i2c/chips/lm78.c delete mode 100644 drivers/i2c/chips/lm80.c delete mode 100644 drivers/i2c/chips/lm83.c delete mode 100644 drivers/i2c/chips/lm85.c delete mode 100644 drivers/i2c/chips/lm87.c delete mode 100644 drivers/i2c/chips/lm90.c delete mode 100644 drivers/i2c/chips/lm92.c delete mode 100644 drivers/i2c/chips/max1619.c delete mode 100644 drivers/i2c/chips/pc87360.c delete mode 100644 drivers/i2c/chips/sis5595.c delete mode 100644 drivers/i2c/chips/smsc47b397.c delete mode 100644 drivers/i2c/chips/smsc47m1.c delete mode 100644 drivers/i2c/chips/via686a.c delete mode 100644 drivers/i2c/chips/w83627ehf.c delete mode 100644 drivers/i2c/chips/w83627hf.c delete mode 100644 drivers/i2c/chips/w83781d.c delete mode 100644 drivers/i2c/chips/w83l785ts.c (limited to 'drivers') diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c new file mode 100644 index 000000000000..d2c774c32f45 --- /dev/null +++ b/drivers/hwmon/adm1021.c @@ -0,0 +1,402 @@ +/* + adm1021.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include + + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, + 0x29, 0x2a, 0x2b, + 0x4c, 0x4d, 0x4e, + I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066); + +/* adm1021 constants specified below */ + +/* The adm1021 registers */ +/* Read-only */ +#define ADM1021_REG_TEMP 0x00 +#define ADM1021_REG_REMOTE_TEMP 0x01 +#define ADM1021_REG_STATUS 0x02 +#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/ +#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */ +#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */ +/* These use different addresses for reading/writing */ +#define ADM1021_REG_CONFIG_R 0x03 +#define ADM1021_REG_CONFIG_W 0x09 +#define ADM1021_REG_CONV_RATE_R 0x04 +#define ADM1021_REG_CONV_RATE_W 0x0A +/* These are for the ADM1023's additional precision on the remote temp sensor */ +#define ADM1021_REG_REM_TEMP_PREC 0x010 +#define ADM1021_REG_REM_OFFSET 0x011 +#define ADM1021_REG_REM_OFFSET_PREC 0x012 +#define ADM1021_REG_REM_TOS_PREC 0x013 +#define ADM1021_REG_REM_THYST_PREC 0x014 +/* limits */ +#define ADM1021_REG_TOS_R 0x05 +#define ADM1021_REG_TOS_W 0x0B +#define ADM1021_REG_REMOTE_TOS_R 0x07 +#define ADM1021_REG_REMOTE_TOS_W 0x0D +#define ADM1021_REG_THYST_R 0x06 +#define ADM1021_REG_THYST_W 0x0C +#define ADM1021_REG_REMOTE_THYST_R 0x08 +#define ADM1021_REG_REMOTE_THYST_W 0x0E +/* write-only */ +#define ADM1021_REG_ONESHOT 0x0F + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +/* Conversions note: 1021 uses normal integer signed-byte format*/ +#define TEMP_FROM_REG(val) (val > 127 ? (val-256)*1000 : val*1000) +#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? (val/1000)+256 : val/1000),0,255)) + +/* Initial values */ + +/* Note: Even though I left the low and high limits named os and hyst, +they don't quite work like a thermostat the way the LM75 does. I.e., +a lower temp than THYST actually triggers an alarm instead of +clearing it. Weird, ey? --Phil */ + +/* Each client has this additional data */ +struct adm1021_data { + struct i2c_client client; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 temp_max; /* Register values */ + u8 temp_hyst; + u8 temp_input; + u8 remote_temp_max; + u8 remote_temp_hyst; + u8 remote_temp_input; + u8 alarms; + /* Special values for ADM1023 only */ + u8 remote_temp_prec; + u8 remote_temp_os_prec; + u8 remote_temp_hyst_prec; + u8 remote_temp_offset; + u8 remote_temp_offset_prec; +}; + +static int adm1021_attach_adapter(struct i2c_adapter *adapter); +static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind); +static void adm1021_init_client(struct i2c_client *client); +static int adm1021_detach_client(struct i2c_client *client); +static int adm1021_read_value(struct i2c_client *client, u8 reg); +static int adm1021_write_value(struct i2c_client *client, u8 reg, + u16 value); +static struct adm1021_data *adm1021_update_device(struct device *dev); + +/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */ +static int read_only = 0; + + +/* This is the driver that will be inserted */ +static struct i2c_driver adm1021_driver = { + .owner = THIS_MODULE, + .name = "adm1021", + .id = I2C_DRIVERID_ADM1021, + .flags = I2C_DF_NOTIFY, + .attach_adapter = adm1021_attach_adapter, + .detach_client = adm1021_detach_client, +}; + +#define show(value) \ +static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct adm1021_data *data = adm1021_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ +} +show(temp_max); +show(temp_hyst); +show(temp_input); +show(remote_temp_max); +show(remote_temp_hyst); +show(remote_temp_input); + +#define show2(value) \ +static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct adm1021_data *data = adm1021_update_device(dev); \ + return sprintf(buf, "%d\n", data->value); \ +} +show2(alarms); + +#define set(value, reg) \ +static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct adm1021_data *data = i2c_get_clientdata(client); \ + int temp = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->value = TEMP_TO_REG(temp); \ + adm1021_write_value(client, reg, data->value); \ + up(&data->update_lock); \ + return count; \ +} +set(temp_max, ADM1021_REG_TOS_W); +set(temp_hyst, ADM1021_REG_THYST_W); +set(remote_temp_max, ADM1021_REG_REMOTE_TOS_W); +set(remote_temp_hyst, ADM1021_REG_REMOTE_THYST_W); + +static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max); +static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst); +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL); +static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_remote_temp_max, set_remote_temp_max); +static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_remote_temp_hyst, set_remote_temp_hyst); +static DEVICE_ATTR(temp2_input, S_IRUGO, show_remote_temp_input, NULL); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + + +static int adm1021_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, adm1021_detect); +} + +static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int i; + struct i2c_client *new_client; + struct adm1021_data *data; + int err = 0; + const char *type_name = ""; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + dev_dbg(&adapter->dev, "adm1021_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto error0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access adm1021_{read,write}_value. */ + + if (!(data = kmalloc(sizeof(struct adm1021_data), GFP_KERNEL))) { + err = -ENOMEM; + goto error0; + } + memset(data, 0, sizeof(struct adm1021_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &adm1021_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + if (kind < 0) { + if ((adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0x03) != 0x00 + || (adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x3F) != 0x00 + || (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) & 0xF8) != 0x00) { + err = -ENODEV; + goto error1; + } + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID); + if (i == 0x41) + if ((adm1021_read_value(new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030) + kind = adm1023; + else + kind = adm1021; + else if (i == 0x49) + kind = thmc10; + else if (i == 0x23) + kind = gl523sm; + else if ((i == 0x4d) && + (adm1021_read_value(new_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 (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) == 0x00 + && (kind == 0 /* skip extra detection */ + || ((adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x7F) == 0x00 + && (adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0xAB) == 0x00))) + kind = lm84; + else + kind = max1617; + } + + if (kind == max1617) { + type_name = "max1617"; + } else if (kind == max1617a) { + type_name = "max1617a"; + } else if (kind == adm1021) { + type_name = "adm1021"; + } else if (kind == adm1023) { + type_name = "adm1023"; + } else if (kind == thmc10) { + type_name = "thmc10"; + } else if (kind == lm84) { + type_name = "lm84"; + } else if (kind == gl523sm) { + type_name = "gl523sm"; + } else if (kind == mc1066) { + type_name = "mc1066"; + } + + /* Fill in the remaining client fields and put it into the global list */ + strlcpy(new_client->name, type_name, I2C_NAME_SIZE); + data->type = kind; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto error1; + + /* Initialize the ADM1021 chip */ + if (kind != lm84) + adm1021_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp1_min); + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp2_max); + device_create_file(&new_client->dev, &dev_attr_temp2_min); + device_create_file(&new_client->dev, &dev_attr_temp2_input); + device_create_file(&new_client->dev, &dev_attr_alarms); + + return 0; + +error1: + kfree(data); +error0: + return err; +} + +static void adm1021_init_client(struct i2c_client *client) +{ + /* Enable ADC and disable suspend mode */ + adm1021_write_value(client, ADM1021_REG_CONFIG_W, + adm1021_read_value(client, ADM1021_REG_CONFIG_R) & 0xBF); + /* Set Conversion rate to 1/sec (this can be tinkered with) */ + adm1021_write_value(client, ADM1021_REG_CONV_RATE_W, 0x04); +} + +static int adm1021_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +/* All registers are byte-sized */ +static int adm1021_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if (!read_only) + return i2c_smbus_write_byte_data(client, reg, value); + return 0; +} + +static struct adm1021_data *adm1021_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1021_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + dev_dbg(&client->dev, "Starting adm1021 update\n"); + + data->temp_input = adm1021_read_value(client, ADM1021_REG_TEMP); + data->temp_max = adm1021_read_value(client, ADM1021_REG_TOS_R); + data->temp_hyst = adm1021_read_value(client, ADM1021_REG_THYST_R); + data->remote_temp_input = adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP); + data->remote_temp_max = adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R); + data->remote_temp_hyst = adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R); + data->alarms = adm1021_read_value(client, ADM1021_REG_STATUS) & 0x7c; + if (data->type == adm1023) { + data->remote_temp_prec = adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC); + data->remote_temp_os_prec = adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC); + data->remote_temp_hyst_prec = adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC); + data->remote_temp_offset = adm1021_read_value(client, ADM1021_REG_REM_OFFSET); + data->remote_temp_offset_prec = adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC); + } + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_adm1021_init(void) +{ + return i2c_add_driver(&adm1021_driver); +} + +static void __exit sensors_adm1021_exit(void) +{ + i2c_del_driver(&adm1021_driver); +} + +MODULE_AUTHOR ("Frodo Looijaard and " + "Philip Edelbrock "); +MODULE_DESCRIPTION("adm1021 driver"); +MODULE_LICENSE("GPL"); + +module_param(read_only, bool, 0); +MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); + +module_init(sensors_adm1021_init) +module_exit(sensors_adm1021_exit) diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c new file mode 100644 index 000000000000..e452d0daf906 --- /dev/null +++ b/drivers/hwmon/adm1025.c @@ -0,0 +1,577 @@ +/* + * adm1025.c + * + * Copyright (C) 2000 Chen-Yuan Wu + * Copyright (C) 2003-2004 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 + * (its own plus up to one external one). Voltages are scaled internally + * (which is not the common way) with ratios such that the nominal value + * of each voltage correspond to a register value of 192 (which means a + * resolution of about 0.5% of the nominal value). Temperature values are + * reported with a 1 deg resolution and a 3 deg accuracy. Complete + * datasheet can be obtained from Analog's website at: + * http://www.analog.com/Analog_Root/productPage/productHome/0,2121,ADM1025,00.html + * + * This driver also supports the ADM1025A, which differs from the ADM1025 + * only in that it has "open-drain VID inputs while the ADM1025 has + * on-chip 100k pull-ups on the VID inputs". It doesn't make any + * difference for us. + * + * This driver also supports the NE1619, a sensor chip made by Philips. + * That chip is similar to the ADM1025A, with a few differences. The only + * difference that matters to us is that the NE1619 has only two possible + * addresses while the ADM1025A has a third one. Complete datasheet can be + * obtained from Philips's website at: + * http://www.semiconductors.philips.com/pip/NE1619DS.html + * + * Since the ADM1025 was the first chipset supported by this driver, most + * comments will refer to this chipset, but are actually general and + * concern all supported chipsets, unless mentioned otherwise. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Addresses to scan + * ADM1025 and ADM1025A have three possible addresses: 0x2c, 0x2d and 0x2e. + * NE1619 has two possible addresses: 0x2c and 0x2d. + */ + +static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* + * Insmod parameters + */ + +SENSORS_INSMOD_2(adm1025, ne1619); + +/* + * The ADM1025 registers + */ + +#define ADM1025_REG_MAN_ID 0x3E +#define ADM1025_REG_CHIP_ID 0x3F +#define ADM1025_REG_CONFIG 0x40 +#define ADM1025_REG_STATUS1 0x41 +#define ADM1025_REG_STATUS2 0x42 +#define ADM1025_REG_IN(nr) (0x20 + (nr)) +#define ADM1025_REG_IN_MAX(nr) (0x2B + (nr) * 2) +#define ADM1025_REG_IN_MIN(nr) (0x2C + (nr) * 2) +#define ADM1025_REG_TEMP(nr) (0x26 + (nr)) +#define ADM1025_REG_TEMP_HIGH(nr) (0x37 + (nr) * 2) +#define ADM1025_REG_TEMP_LOW(nr) (0x38 + (nr) * 2) +#define ADM1025_REG_VID 0x47 +#define ADM1025_REG_VID4 0x49 + +/* + * Conversions and various macros + * The ADM1025 uses signed 8-bit values for temperatures. + */ + +static int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 }; + +#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192) +#define IN_TO_REG(val,scale) ((val) <= 0 ? 0 : \ + (val) * 192 >= (scale) * 255 ? 255 : \ + ((val) * 192 + (scale)/2) / (scale)) + +#define TEMP_FROM_REG(reg) ((reg) * 1000) +#define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \ + (val) >= 126500 ? 127 : \ + (((val) < 0 ? (val)-500 : (val)+500) / 1000)) + +/* + * Functions declaration + */ + +static int adm1025_attach_adapter(struct i2c_adapter *adapter); +static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind); +static void adm1025_init_client(struct i2c_client *client); +static int adm1025_detach_client(struct i2c_client *client); +static struct adm1025_data *adm1025_update_device(struct device *dev); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver adm1025_driver = { + .owner = THIS_MODULE, + .name = "adm1025", + .id = I2C_DRIVERID_ADM1025, + .flags = I2C_DF_NOTIFY, + .attach_adapter = adm1025_attach_adapter, + .detach_client = adm1025_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct adm1025_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + u8 in[6]; /* register value */ + u8 in_max[6]; /* register value */ + u8 in_min[6]; /* register value */ + s8 temp[2]; /* register value */ + s8 temp_min[2]; /* register value */ + s8 temp_max[2]; /* register value */ + u16 alarms; /* register values, combined */ + u8 vid; /* register values, combined */ + u8 vrm; +}; + +/* + * Sysfs stuff + */ + +#define show_in(offset) \ +static ssize_t show_in##offset(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct adm1025_data *data = adm1025_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \ + in_scale[offset])); \ +} \ +static ssize_t show_in##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct adm1025_data *data = adm1025_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \ + in_scale[offset])); \ +} \ +static ssize_t show_in##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct adm1025_data *data = adm1025_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \ + in_scale[offset])); \ +} \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL); +show_in(0); +show_in(1); +show_in(2); +show_in(3); +show_in(4); +show_in(5); + +#define show_temp(offset) \ +static ssize_t show_temp##offset(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct adm1025_data *data = adm1025_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \ +} \ +static ssize_t show_temp##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct adm1025_data *data = adm1025_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[offset-1])); \ +} \ +static ssize_t show_temp##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct adm1025_data *data = adm1025_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[offset-1])); \ +}\ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp##offset, NULL); +show_temp(1); +show_temp(2); + +#define set_in(offset) \ +static ssize_t set_in##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct adm1025_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->in_min[offset] = IN_TO_REG(val, in_scale[offset]); \ + i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MIN(offset), \ + data->in_min[offset]); \ + up(&data->update_lock); \ + return count; \ +} \ +static ssize_t set_in##offset##_max(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct adm1025_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->in_max[offset] = IN_TO_REG(val, in_scale[offset]); \ + i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MAX(offset), \ + data->in_max[offset]); \ + up(&data->update_lock); \ + return count; \ +} \ +static DEVICE_ATTR(in##offset##_min, S_IWUSR | S_IRUGO, \ + show_in##offset##_min, set_in##offset##_min); \ +static DEVICE_ATTR(in##offset##_max, S_IWUSR | S_IRUGO, \ + show_in##offset##_max, set_in##offset##_max); +set_in(0); +set_in(1); +set_in(2); +set_in(3); +set_in(4); +set_in(5); + +#define set_temp(offset) \ +static ssize_t set_temp##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct adm1025_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->temp_min[offset-1] = TEMP_TO_REG(val); \ + i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_LOW(offset-1), \ + data->temp_min[offset-1]); \ + up(&data->update_lock); \ + return count; \ +} \ +static ssize_t set_temp##offset##_max(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct adm1025_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->temp_max[offset-1] = TEMP_TO_REG(val); \ + i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_HIGH(offset-1), \ + data->temp_max[offset-1]); \ + up(&data->update_lock); \ + return count; \ +} \ +static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \ + show_temp##offset##_min, set_temp##offset##_min); \ +static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \ + show_temp##offset##_max, set_temp##offset##_max); +set_temp(1); +set_temp(2); + +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1025_data *data = adm1025_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); +} +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1025_data *data = adm1025_update_device(dev); + return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm)); +} +/* in1_ref is deprecated in favour of cpu0_vid, remove after 2005-11-11 */ +static DEVICE_ATTR(in1_ref, S_IRUGO, show_vid, NULL); +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + +static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1025_data *data = adm1025_update_device(dev); + return sprintf(buf, "%u\n", data->vrm); +} +static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1025_data *data = i2c_get_clientdata(client); + data->vrm = simple_strtoul(buf, NULL, 10); + return count; +} +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); + +/* + * Real code + */ + +static int adm1025_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, adm1025_detect); +} + +/* + * The following function does more than just detection. If detection + * succeeds, it also registers the new chip. + */ +static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct adm1025_data *data; + int err = 0; + const char *name = ""; + u8 config; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct adm1025_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct adm1025_data)); + + /* The common I2C client data is placed right before the + ADM1025-specific data. */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &adm1025_driver; + new_client->flags = 0; + + /* + * 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(new_client, ADM1025_REG_CONFIG); + if (kind < 0) { /* detection */ + if ((config & 0x80) != 0x00 + || (i2c_smbus_read_byte_data(new_client, + ADM1025_REG_STATUS1) & 0xC0) != 0x00 + || (i2c_smbus_read_byte_data(new_client, + ADM1025_REG_STATUS2) & 0xBC) != 0x00) { + dev_dbg(&adapter->dev, + "ADM1025 detection failed at 0x%02x.\n", + address); + goto exit_free; + } + } + + if (kind <= 0) { /* identification */ + u8 man_id, chip_id; + + man_id = i2c_smbus_read_byte_data(new_client, + ADM1025_REG_MAN_ID); + chip_id = i2c_smbus_read_byte_data(new_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 (address != 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); + goto exit_free; + } + } + + if (kind == adm1025) { + name = "adm1025"; + } else if (kind == ne1619) { + name = "ne1619"; + } + + /* We can fill in the remaining client fields */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the ADM1025 chip */ + adm1025_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_in0_input); + device_create_file(&new_client->dev, &dev_attr_in1_input); + device_create_file(&new_client->dev, &dev_attr_in2_input); + device_create_file(&new_client->dev, &dev_attr_in3_input); + device_create_file(&new_client->dev, &dev_attr_in5_input); + device_create_file(&new_client->dev, &dev_attr_in0_min); + device_create_file(&new_client->dev, &dev_attr_in1_min); + device_create_file(&new_client->dev, &dev_attr_in2_min); + device_create_file(&new_client->dev, &dev_attr_in3_min); + device_create_file(&new_client->dev, &dev_attr_in5_min); + device_create_file(&new_client->dev, &dev_attr_in0_max); + device_create_file(&new_client->dev, &dev_attr_in1_max); + device_create_file(&new_client->dev, &dev_attr_in2_max); + device_create_file(&new_client->dev, &dev_attr_in3_max); + device_create_file(&new_client->dev, &dev_attr_in5_max); + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp2_input); + device_create_file(&new_client->dev, &dev_attr_temp1_min); + device_create_file(&new_client->dev, &dev_attr_temp2_min); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp2_max); + device_create_file(&new_client->dev, &dev_attr_alarms); + /* in1_ref is deprecated, remove after 2005-11-11 */ + device_create_file(&new_client->dev, &dev_attr_in1_ref); + device_create_file(&new_client->dev, &dev_attr_cpu0_vid); + device_create_file(&new_client->dev, &dev_attr_vrm); + + /* Pin 11 is either in4 (+12V) or VID4 */ + if (!(config & 0x20)) { + device_create_file(&new_client->dev, &dev_attr_in4_input); + device_create_file(&new_client->dev, &dev_attr_in4_min); + device_create_file(&new_client->dev, &dev_attr_in4_max); + } + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static void adm1025_init_client(struct i2c_client *client) +{ + u8 reg; + struct adm1025_data *data = i2c_get_clientdata(client); + int i; + + data->vrm = i2c_which_vrm(); + + /* + * Set high limits + * Usually we avoid setting limits on driver init, but it happens + * that the ADM1025 comes with stupid default limits (all registers + * set to 0). In case the chip has not gone through any limit + * setting yet, we better set the high limits to the max so that + * no alarm triggers. + */ + for (i=0; i<6; i++) { + reg = i2c_smbus_read_byte_data(client, + ADM1025_REG_IN_MAX(i)); + if (reg == 0) + i2c_smbus_write_byte_data(client, + ADM1025_REG_IN_MAX(i), + 0xFF); + } + for (i=0; i<2; i++) { + reg = i2c_smbus_read_byte_data(client, + ADM1025_REG_TEMP_HIGH(i)); + if (reg == 0) + i2c_smbus_write_byte_data(client, + ADM1025_REG_TEMP_HIGH(i), + 0x7F); + } + + /* + * Start the conversions + */ + reg = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG); + if (!(reg & 0x01)) + i2c_smbus_write_byte_data(client, ADM1025_REG_CONFIG, + (reg&0x7E)|0x01); +} + +static int adm1025_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static struct adm1025_data *adm1025_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1025_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { + int i; + + dev_dbg(&client->dev, "Updating data.\n"); + for (i=0; i<6; i++) { + data->in[i] = i2c_smbus_read_byte_data(client, + ADM1025_REG_IN(i)); + data->in_min[i] = i2c_smbus_read_byte_data(client, + ADM1025_REG_IN_MIN(i)); + data->in_max[i] = i2c_smbus_read_byte_data(client, + ADM1025_REG_IN_MAX(i)); + } + for (i=0; i<2; i++) { + data->temp[i] = i2c_smbus_read_byte_data(client, + ADM1025_REG_TEMP(i)); + data->temp_min[i] = i2c_smbus_read_byte_data(client, + ADM1025_REG_TEMP_LOW(i)); + data->temp_max[i] = i2c_smbus_read_byte_data(client, + ADM1025_REG_TEMP_HIGH(i)); + } + data->alarms = i2c_smbus_read_byte_data(client, + ADM1025_REG_STATUS1) + | (i2c_smbus_read_byte_data(client, + ADM1025_REG_STATUS2) << 8); + data->vid = (i2c_smbus_read_byte_data(client, + ADM1025_REG_VID) & 0x0f) + | ((i2c_smbus_read_byte_data(client, + ADM1025_REG_VID4) & 0x01) << 4); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_adm1025_init(void) +{ + return i2c_add_driver(&adm1025_driver); +} + +static void __exit sensors_adm1025_exit(void) +{ + i2c_del_driver(&adm1025_driver); +} + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("ADM1025 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_adm1025_init); +module_exit(sensors_adm1025_exit); diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c new file mode 100644 index 000000000000..3c85fe150cd7 --- /dev/null +++ b/drivers/hwmon/adm1026.c @@ -0,0 +1,1714 @@ +/* + adm1026.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (C) 2002, 2003 Philip Pokorny + Copyright (C) 2004 Justin Thiessen + + Chip details at: + + + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(adm1026); + +static int gpio_input[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; +static int gpio_output[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; +static int gpio_inverted[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; +static int gpio_normal[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; +static int gpio_fan[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; +module_param_array(gpio_input,int,NULL,0); +MODULE_PARM_DESC(gpio_input,"List of GPIO pins (0-16) to program as inputs"); +module_param_array(gpio_output,int,NULL,0); +MODULE_PARM_DESC(gpio_output,"List of GPIO pins (0-16) to program as " + "outputs"); +module_param_array(gpio_inverted,int,NULL,0); +MODULE_PARM_DESC(gpio_inverted,"List of GPIO pins (0-16) to program as " + "inverted"); +module_param_array(gpio_normal,int,NULL,0); +MODULE_PARM_DESC(gpio_normal,"List of GPIO pins (0-16) to program as " + "normal/non-inverted"); +module_param_array(gpio_fan,int,NULL,0); +MODULE_PARM_DESC(gpio_fan,"List of GPIO pins (0-7) to program as fan tachs"); + +/* Many ADM1026 constants specified below */ + +/* The ADM1026 registers */ +#define ADM1026_REG_CONFIG1 0x00 +#define CFG1_MONITOR 0x01 +#define CFG1_INT_ENABLE 0x02 +#define CFG1_INT_CLEAR 0x04 +#define CFG1_AIN8_9 0x08 +#define CFG1_THERM_HOT 0x10 +#define CFG1_DAC_AFC 0x20 +#define CFG1_PWM_AFC 0x40 +#define CFG1_RESET 0x80 +#define ADM1026_REG_CONFIG2 0x01 +/* CONFIG2 controls FAN0/GPIO0 through FAN7/GPIO7 */ +#define ADM1026_REG_CONFIG3 0x07 +#define CFG3_GPIO16_ENABLE 0x01 +#define CFG3_CI_CLEAR 0x02 +#define CFG3_VREF_250 0x04 +#define CFG3_GPIO16_DIR 0x40 +#define CFG3_GPIO16_POL 0x80 +#define ADM1026_REG_E2CONFIG 0x13 +#define E2CFG_READ 0x01 +#define E2CFG_WRITE 0x02 +#define E2CFG_ERASE 0x04 +#define E2CFG_ROM 0x08 +#define E2CFG_CLK_EXT 0x80 + +/* There are 10 general analog inputs and 7 dedicated inputs + * They are: + * 0 - 9 = AIN0 - AIN9 + * 10 = Vbat + * 11 = 3.3V Standby + * 12 = 3.3V Main + * 13 = +5V + * 14 = Vccp (CPU core voltage) + * 15 = +12V + * 16 = -12V + */ +static u16 ADM1026_REG_IN[] = { + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, + 0x36, 0x37, 0x27, 0x29, 0x26, 0x2a, + 0x2b, 0x2c, 0x2d, 0x2e, 0x2f + }; +static u16 ADM1026_REG_IN_MIN[] = { + 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, + 0x5e, 0x5f, 0x6d, 0x49, 0x6b, 0x4a, + 0x4b, 0x4c, 0x4d, 0x4e, 0x4f + }; +static u16 ADM1026_REG_IN_MAX[] = { + 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, + 0x56, 0x57, 0x6c, 0x41, 0x6a, 0x42, + 0x43, 0x44, 0x45, 0x46, 0x47 + }; + +/* Temperatures are: + * 0 - Internal + * 1 - External 1 + * 2 - External 2 + */ +static u16 ADM1026_REG_TEMP[] = { 0x1f, 0x28, 0x29 }; +static u16 ADM1026_REG_TEMP_MIN[] = { 0x69, 0x48, 0x49 }; +static u16 ADM1026_REG_TEMP_MAX[] = { 0x68, 0x40, 0x41 }; +static u16 ADM1026_REG_TEMP_TMIN[] = { 0x10, 0x11, 0x12 }; +static u16 ADM1026_REG_TEMP_THERM[] = { 0x0d, 0x0e, 0x0f }; +static u16 ADM1026_REG_TEMP_OFFSET[] = { 0x1e, 0x6e, 0x6f }; + +#define ADM1026_REG_FAN(nr) (0x38 + (nr)) +#define ADM1026_REG_FAN_MIN(nr) (0x60 + (nr)) +#define ADM1026_REG_FAN_DIV_0_3 0x02 +#define ADM1026_REG_FAN_DIV_4_7 0x03 + +#define ADM1026_REG_DAC 0x04 +#define ADM1026_REG_PWM 0x05 + +#define ADM1026_REG_GPIO_CFG_0_3 0x08 +#define ADM1026_REG_GPIO_CFG_4_7 0x09 +#define ADM1026_REG_GPIO_CFG_8_11 0x0a +#define ADM1026_REG_GPIO_CFG_12_15 0x0b +/* CFG_16 in REG_CFG3 */ +#define ADM1026_REG_GPIO_STATUS_0_7 0x24 +#define ADM1026_REG_GPIO_STATUS_8_15 0x25 +/* STATUS_16 in REG_STATUS4 */ +#define ADM1026_REG_GPIO_MASK_0_7 0x1c +#define ADM1026_REG_GPIO_MASK_8_15 0x1d +/* MASK_16 in REG_MASK4 */ + +#define ADM1026_REG_COMPANY 0x16 +#define ADM1026_REG_VERSTEP 0x17 +/* These are the recognized values for the above regs */ +#define ADM1026_COMPANY_ANALOG_DEV 0x41 +#define ADM1026_VERSTEP_GENERIC 0x40 +#define ADM1026_VERSTEP_ADM1026 0x44 + +#define ADM1026_REG_MASK1 0x18 +#define ADM1026_REG_MASK2 0x19 +#define ADM1026_REG_MASK3 0x1a +#define ADM1026_REG_MASK4 0x1b + +#define ADM1026_REG_STATUS1 0x20 +#define ADM1026_REG_STATUS2 0x21 +#define ADM1026_REG_STATUS3 0x22 +#define ADM1026_REG_STATUS4 0x23 + +#define ADM1026_FAN_ACTIVATION_TEMP_HYST -6 +#define ADM1026_FAN_CONTROL_TEMP_RANGE 20 +#define ADM1026_PWM_MAX 255 + +/* Conversions. Rounding and limit checking is only done on the TO_REG + * variants. Note that you should be a bit careful with which arguments + * these macros are called: arguments may be evaluated more than once. + */ + +/* IN are scaled acording to built-in resistors. These are the + * voltages corresponding to 3/4 of full scale (192 or 0xc0) + * NOTE: The -12V input needs an additional factor to account + * for the Vref pullup resistor. + * NEG12_OFFSET = SCALE * Vref / V-192 - Vref + * = 13875 * 2.50 / 1.875 - 2500 + * = 16000 + * + * The values in this table are based on Table II, page 15 of the + * datasheet. + */ +static int adm1026_scaling[] = { /* .001 Volts */ + 2250, 2250, 2250, 2250, 2250, 2250, + 1875, 1875, 1875, 1875, 3000, 3330, + 3330, 4995, 2250, 12000, 13875 + }; +#define NEG12_OFFSET 16000 +#define SCALE(val,from,to) (((val)*(to) + ((from)/2))/(from)) +#define INS_TO_REG(n,val) (SENSORS_LIMIT(SCALE(val,adm1026_scaling[n],192),\ + 0,255)) +#define INS_FROM_REG(n,val) (SCALE(val,192,adm1026_scaling[n])) + +/* FAN speed is measured using 22.5kHz clock and counts for 2 pulses + * and we assume a 2 pulse-per-rev fan tach signal + * 22500 kHz * 60 (sec/min) * 2 (pulse) / 2 (pulse/rev) == 1350000 + */ +#define FAN_TO_REG(val,div) ((val)<=0 ? 0xff : SENSORS_LIMIT(1350000/((val)*\ + (div)),1,254)) +#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==0xff ? 0 : 1350000/((val)*\ + (div))) +#define DIV_FROM_REG(val) (1<<(val)) +#define DIV_TO_REG(val) ((val)>=8 ? 3 : (val)>=4 ? 2 : (val)>=2 ? 1 : 0) + +/* Temperature is reported in 1 degC increments */ +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)+((val)<0 ? -500 : 500))/1000,\ + -127,127)) +#define TEMP_FROM_REG(val) ((val) * 1000) +#define OFFSET_TO_REG(val) (SENSORS_LIMIT(((val)+((val)<0 ? -500 : 500))/1000,\ + -127,127)) +#define OFFSET_FROM_REG(val) ((val) * 1000) + +#define PWM_TO_REG(val) (SENSORS_LIMIT(val,0,255)) +#define PWM_FROM_REG(val) (val) + +#define PWM_MIN_TO_REG(val) ((val) & 0xf0) +#define PWM_MIN_FROM_REG(val) (((val) & 0xf0) + ((val) >> 4)) + +/* Analog output is a voltage, and scaled to millivolts. The datasheet + * indicates that the DAC could be used to drive the fans, but in our + * example board (Arima HDAMA) it isn't connected to the fans at all. + */ +#define DAC_TO_REG(val) (SENSORS_LIMIT(((((val)*255)+500)/2500),0,255)) +#define DAC_FROM_REG(val) (((val)*2500)/255) + +/* Typically used with systems using a v9.1 VRM spec ? */ +#define ADM1026_INIT_VRM 91 + +/* Chip sampling rates + * + * Some sensors are not updated more frequently than once per second + * so it doesn't make sense to read them more often than that. + * We cache the results and return the saved data if the driver + * is called again before a second has elapsed. + * + * Also, there is significant configuration data for this chip + * So, we keep the config data up to date in the cache + * when it is written and only sample it once every 5 *minutes* + */ +#define ADM1026_DATA_INTERVAL (1 * HZ) +#define ADM1026_CONFIG_INTERVAL (5 * 60 * HZ) + +/* We allow for multiple chips in a single system. + * + * For each registered ADM1026, we need to keep state information + * at client->data. The adm1026_data structure is dynamically + * allocated, when a new client structure is allocated. */ + +struct pwm_data { + u8 pwm; + u8 enable; + u8 auto_pwm_min; +}; + +struct adm1026_data { + struct i2c_client client; + struct semaphore lock; + enum chips type; + + struct semaphore update_lock; + int valid; /* !=0 if following fields are valid */ + unsigned long last_reading; /* In jiffies */ + unsigned long last_config; /* In jiffies */ + + u8 in[17]; /* Register value */ + u8 in_max[17]; /* Register value */ + u8 in_min[17]; /* Register value */ + s8 temp[3]; /* Register value */ + s8 temp_min[3]; /* Register value */ + s8 temp_max[3]; /* Register value */ + s8 temp_tmin[3]; /* Register value */ + s8 temp_crit[3]; /* Register value */ + s8 temp_offset[3]; /* Register value */ + u8 fan[8]; /* Register value */ + u8 fan_min[8]; /* Register value */ + u8 fan_div[8]; /* Decoded value */ + struct pwm_data pwm1; /* Pwm control values */ + int vid; /* Decoded value */ + u8 vrm; /* VRM version */ + u8 analog_out; /* Register value (DAC) */ + long alarms; /* Register encoding, combined */ + long alarm_mask; /* Register encoding, combined */ + long gpio; /* Register encoding, combined */ + long gpio_mask; /* Register encoding, combined */ + u8 gpio_config[17]; /* Decoded value */ + u8 config1; /* Register value */ + u8 config2; /* Register value */ + u8 config3; /* Register value */ +}; + +static int adm1026_attach_adapter(struct i2c_adapter *adapter); +static int adm1026_detect(struct i2c_adapter *adapter, int address, + int kind); +static int adm1026_detach_client(struct i2c_client *client); +static int adm1026_read_value(struct i2c_client *client, u8 register); +static int adm1026_write_value(struct i2c_client *client, u8 register, + int value); +static void adm1026_print_gpio(struct i2c_client *client); +static void adm1026_fixup_gpio(struct i2c_client *client); +static struct adm1026_data *adm1026_update_device(struct device *dev); +static void adm1026_init_client(struct i2c_client *client); + + +static struct i2c_driver adm1026_driver = { + .owner = THIS_MODULE, + .name = "adm1026", + .flags = I2C_DF_NOTIFY, + .attach_adapter = adm1026_attach_adapter, + .detach_client = adm1026_detach_client, +}; + +int adm1026_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) { + return 0; + } + return i2c_detect(adapter, &addr_data, adm1026_detect); +} + +int adm1026_detach_client(struct i2c_client *client) +{ + i2c_detach_client(client); + kfree(client); + return 0; +} + +int adm1026_read_value(struct i2c_client *client, u8 reg) +{ + int res; + + if (reg < 0x80) { + /* "RAM" locations */ + res = i2c_smbus_read_byte_data(client, reg) & 0xff; + } else { + /* EEPROM, do nothing */ + res = 0; + } + return res; +} + +int adm1026_write_value(struct i2c_client *client, u8 reg, int value) +{ + int res; + + if (reg < 0x80) { + /* "RAM" locations */ + res = i2c_smbus_write_byte_data(client, reg, value); + } else { + /* EEPROM, do nothing */ + res = 0; + } + return res; +} + +void adm1026_init_client(struct i2c_client *client) +{ + int value, i; + struct adm1026_data *data = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "Initializing device\n"); + /* Read chip config */ + data->config1 = adm1026_read_value(client, ADM1026_REG_CONFIG1); + data->config2 = adm1026_read_value(client, ADM1026_REG_CONFIG2); + data->config3 = adm1026_read_value(client, ADM1026_REG_CONFIG3); + + /* Inform user of chip config */ + dev_dbg(&client->dev, "ADM1026_REG_CONFIG1 is: 0x%02x\n", + data->config1); + if ((data->config1 & CFG1_MONITOR) == 0) { + dev_dbg(&client->dev, "Monitoring not currently " + "enabled.\n"); + } + if (data->config1 & CFG1_INT_ENABLE) { + dev_dbg(&client->dev, "SMBALERT interrupts are " + "enabled.\n"); + } + if (data->config1 & CFG1_AIN8_9) { + dev_dbg(&client->dev, "in8 and in9 enabled. " + "temp3 disabled.\n"); + } else { + dev_dbg(&client->dev, "temp3 enabled. in8 and " + "in9 disabled.\n"); + } + if (data->config1 & CFG1_THERM_HOT) { + dev_dbg(&client->dev, "Automatic THERM, PWM, " + "and temp limits enabled.\n"); + } + + value = data->config3; + if (data->config3 & CFG3_GPIO16_ENABLE) { + dev_dbg(&client->dev, "GPIO16 enabled. THERM" + "pin disabled.\n"); + } else { + dev_dbg(&client->dev, "THERM pin enabled. " + "GPIO16 disabled.\n"); + } + if (data->config3 & CFG3_VREF_250) { + dev_dbg(&client->dev, "Vref is 2.50 Volts.\n"); + } else { + dev_dbg(&client->dev, "Vref is 1.82 Volts.\n"); + } + /* Read and pick apart the existing GPIO configuration */ + value = 0; + for (i = 0;i <= 15;++i) { + if ((i & 0x03) == 0) { + value = adm1026_read_value(client, + ADM1026_REG_GPIO_CFG_0_3 + i/4); + } + data->gpio_config[i] = value & 0x03; + value >>= 2; + } + data->gpio_config[16] = (data->config3 >> 6) & 0x03; + + /* ... and then print it */ + adm1026_print_gpio(client); + + /* If the user asks us to reprogram the GPIO config, then + * do it now. + */ + if (gpio_input[0] != -1 || gpio_output[0] != -1 + || gpio_inverted[0] != -1 || gpio_normal[0] != -1 + || gpio_fan[0] != -1) { + adm1026_fixup_gpio(client); + } + + /* WE INTENTIONALLY make no changes to the limits, + * offsets, pwms, fans and zones. If they were + * configured, we don't want to mess with them. + * If they weren't, the default is 100% PWM, no + * control and will suffice until 'sensors -s' + * can be run by the user. We DO set the default + * value for pwm1.auto_pwm_min to its maximum + * so that enabling automatic pwm fan control + * without first setting a value for pwm1.auto_pwm_min + * will not result in potentially dangerous fan speed decrease. + */ + data->pwm1.auto_pwm_min=255; + /* Start monitoring */ + value = adm1026_read_value(client, ADM1026_REG_CONFIG1); + /* Set MONITOR, clear interrupt acknowledge and s/w reset */ + value = (value | CFG1_MONITOR) & (~CFG1_INT_CLEAR & ~CFG1_RESET); + dev_dbg(&client->dev, "Setting CONFIG to: 0x%02x\n", value); + data->config1 = value; + adm1026_write_value(client, ADM1026_REG_CONFIG1, value); + + /* initialize fan_div[] to hardware defaults */ + value = adm1026_read_value(client, ADM1026_REG_FAN_DIV_0_3) | + (adm1026_read_value(client, ADM1026_REG_FAN_DIV_4_7) << 8); + for (i = 0;i <= 7;++i) { + data->fan_div[i] = DIV_FROM_REG(value & 0x03); + value >>= 2; + } +} + +void adm1026_print_gpio(struct i2c_client *client) +{ + struct adm1026_data *data = i2c_get_clientdata(client); + int i; + + dev_dbg(&client->dev, "GPIO config is:"); + for (i = 0;i <= 7;++i) { + if (data->config2 & (1 << i)) { + dev_dbg(&client->dev, "\t%sGP%s%d\n", + data->gpio_config[i] & 0x02 ? "" : "!", + data->gpio_config[i] & 0x01 ? "OUT" : "IN", + i); + } else { + dev_dbg(&client->dev, "\tFAN%d\n", i); + } + } + for (i = 8;i <= 15;++i) { + dev_dbg(&client->dev, "\t%sGP%s%d\n", + data->gpio_config[i] & 0x02 ? "" : "!", + data->gpio_config[i] & 0x01 ? "OUT" : "IN", + i); + } + if (data->config3 & CFG3_GPIO16_ENABLE) { + dev_dbg(&client->dev, "\t%sGP%s16\n", + data->gpio_config[16] & 0x02 ? "" : "!", + data->gpio_config[16] & 0x01 ? "OUT" : "IN"); + } else { + /* GPIO16 is THERM */ + dev_dbg(&client->dev, "\tTHERM\n"); + } +} + +void adm1026_fixup_gpio(struct i2c_client *client) +{ + struct adm1026_data *data = i2c_get_clientdata(client); + int i; + int value; + + /* Make the changes requested. */ + /* We may need to unlock/stop monitoring or soft-reset the + * chip before we can make changes. This hasn't been + * tested much. FIXME + */ + + /* Make outputs */ + for (i = 0;i <= 16;++i) { + if (gpio_output[i] >= 0 && gpio_output[i] <= 16) { + data->gpio_config[gpio_output[i]] |= 0x01; + } + /* if GPIO0-7 is output, it isn't a FAN tach */ + if (gpio_output[i] >= 0 && gpio_output[i] <= 7) { + data->config2 |= 1 << gpio_output[i]; + } + } + + /* Input overrides output */ + for (i = 0;i <= 16;++i) { + if (gpio_input[i] >= 0 && gpio_input[i] <= 16) { + data->gpio_config[gpio_input[i]] &= ~ 0x01; + } + /* if GPIO0-7 is input, it isn't a FAN tach */ + if (gpio_input[i] >= 0 && gpio_input[i] <= 7) { + data->config2 |= 1 << gpio_input[i]; + } + } + + /* Inverted */ + for (i = 0;i <= 16;++i) { + if (gpio_inverted[i] >= 0 && gpio_inverted[i] <= 16) { + data->gpio_config[gpio_inverted[i]] &= ~ 0x02; + } + } + + /* Normal overrides inverted */ + for (i = 0;i <= 16;++i) { + if (gpio_normal[i] >= 0 && gpio_normal[i] <= 16) { + data->gpio_config[gpio_normal[i]] |= 0x02; + } + } + + /* Fan overrides input and output */ + for (i = 0;i <= 7;++i) { + if (gpio_fan[i] >= 0 && gpio_fan[i] <= 7) { + data->config2 &= ~(1 << gpio_fan[i]); + } + } + + /* Write new configs to registers */ + adm1026_write_value(client, ADM1026_REG_CONFIG2, data->config2); + data->config3 = (data->config3 & 0x3f) + | ((data->gpio_config[16] & 0x03) << 6); + adm1026_write_value(client, ADM1026_REG_CONFIG3, data->config3); + for (i = 15, value = 0;i >= 0;--i) { + value <<= 2; + value |= data->gpio_config[i] & 0x03; + if ((i & 0x03) == 0) { + adm1026_write_value(client, + ADM1026_REG_GPIO_CFG_0_3 + i/4, + value); + value = 0; + } + } + + /* Print the new config */ + adm1026_print_gpio(client); +} + + +static struct adm1026_data *adm1026_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int i; + long value, alarms, gpio; + + down(&data->update_lock); + if (!data->valid + || time_after(jiffies, data->last_reading + ADM1026_DATA_INTERVAL)) { + /* Things that change quickly */ + dev_dbg(&client->dev,"Reading sensor values\n"); + for (i = 0;i <= 16;++i) { + data->in[i] = + adm1026_read_value(client, ADM1026_REG_IN[i]); + } + + for (i = 0;i <= 7;++i) { + data->fan[i] = + adm1026_read_value(client, ADM1026_REG_FAN(i)); + } + + for (i = 0;i <= 2;++i) { + /* NOTE: temp[] is s8 and we assume 2's complement + * "conversion" in the assignment */ + data->temp[i] = + adm1026_read_value(client, ADM1026_REG_TEMP[i]); + } + + data->pwm1.pwm = adm1026_read_value(client, + ADM1026_REG_PWM); + data->analog_out = adm1026_read_value(client, + ADM1026_REG_DAC); + /* GPIO16 is MSbit of alarms, move it to gpio */ + alarms = adm1026_read_value(client, ADM1026_REG_STATUS4); + gpio = alarms & 0x80 ? 0x0100 : 0; /* GPIO16 */ + alarms &= 0x7f; + alarms <<= 8; + alarms |= adm1026_read_value(client, ADM1026_REG_STATUS3); + alarms <<= 8; + alarms |= adm1026_read_value(client, ADM1026_REG_STATUS2); + alarms <<= 8; + alarms |= adm1026_read_value(client, ADM1026_REG_STATUS1); + data->alarms = alarms; + + /* Read the GPIO values */ + gpio |= adm1026_read_value(client, + ADM1026_REG_GPIO_STATUS_8_15); + gpio <<= 8; + gpio |= adm1026_read_value(client, + ADM1026_REG_GPIO_STATUS_0_7); + data->gpio = gpio; + + data->last_reading = jiffies; + }; /* last_reading */ + + if (!data->valid || + time_after(jiffies, data->last_config + ADM1026_CONFIG_INTERVAL)) { + /* Things that don't change often */ + dev_dbg(&client->dev, "Reading config values\n"); + for (i = 0;i <= 16;++i) { + data->in_min[i] = adm1026_read_value(client, + ADM1026_REG_IN_MIN[i]); + data->in_max[i] = adm1026_read_value(client, + ADM1026_REG_IN_MAX[i]); + } + + value = adm1026_read_value(client, ADM1026_REG_FAN_DIV_0_3) + | (adm1026_read_value(client, ADM1026_REG_FAN_DIV_4_7) + << 8); + for (i = 0;i <= 7;++i) { + data->fan_min[i] = adm1026_read_value(client, + ADM1026_REG_FAN_MIN(i)); + data->fan_div[i] = DIV_FROM_REG(value & 0x03); + value >>= 2; + } + + for (i = 0; i <= 2; ++i) { + /* NOTE: temp_xxx[] are s8 and we assume 2's + * complement "conversion" in the assignment + */ + data->temp_min[i] = adm1026_read_value(client, + ADM1026_REG_TEMP_MIN[i]); + data->temp_max[i] = adm1026_read_value(client, + ADM1026_REG_TEMP_MAX[i]); + data->temp_tmin[i] = adm1026_read_value(client, + ADM1026_REG_TEMP_TMIN[i]); + data->temp_crit[i] = adm1026_read_value(client, + ADM1026_REG_TEMP_THERM[i]); + data->temp_offset[i] = adm1026_read_value(client, + ADM1026_REG_TEMP_OFFSET[i]); + } + + /* Read the STATUS/alarm masks */ + alarms = adm1026_read_value(client, ADM1026_REG_MASK4); + gpio = alarms & 0x80 ? 0x0100 : 0; /* GPIO16 */ + alarms = (alarms & 0x7f) << 8; + alarms |= adm1026_read_value(client, ADM1026_REG_MASK3); + alarms <<= 8; + alarms |= adm1026_read_value(client, ADM1026_REG_MASK2); + alarms <<= 8; + alarms |= adm1026_read_value(client, ADM1026_REG_MASK1); + data->alarm_mask = alarms; + + /* Read the GPIO values */ + gpio |= adm1026_read_value(client, + ADM1026_REG_GPIO_MASK_8_15); + gpio <<= 8; + gpio |= adm1026_read_value(client, ADM1026_REG_GPIO_MASK_0_7); + data->gpio_mask = gpio; + + /* Read various values from CONFIG1 */ + data->config1 = adm1026_read_value(client, + ADM1026_REG_CONFIG1); + if (data->config1 & CFG1_PWM_AFC) { + data->pwm1.enable = 2; + data->pwm1.auto_pwm_min = + PWM_MIN_FROM_REG(data->pwm1.pwm); + } + /* Read the GPIO config */ + data->config2 = adm1026_read_value(client, + ADM1026_REG_CONFIG2); + data->config3 = adm1026_read_value(client, + ADM1026_REG_CONFIG3); + data->gpio_config[16] = (data->config3 >> 6) & 0x03; + + value = 0; + for (i = 0;i <= 15;++i) { + if ((i & 0x03) == 0) { + value = adm1026_read_value(client, + ADM1026_REG_GPIO_CFG_0_3 + i/4); + } + data->gpio_config[i] = value & 0x03; + value >>= 2; + } + + data->last_config = jiffies; + }; /* last_config */ + + dev_dbg(&client->dev, "Setting VID from GPIO11-15.\n"); + data->vid = (data->gpio >> 11) & 0x1f; + data->valid = 1; + up(&data->update_lock); + return data; +} + +static ssize_t show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in[nr])); +} +static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in_min[nr])); +} +static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->in_min[nr] = INS_TO_REG(nr, val); + adm1026_write_value(client, ADM1026_REG_IN_MIN[nr], data->in_min[nr]); + up(&data->update_lock); + return count; +} +static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in_max[nr])); +} +static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->in_max[nr] = INS_TO_REG(nr, val); + adm1026_write_value(client, ADM1026_REG_IN_MAX[nr], data->in_max[nr]); + up(&data->update_lock); + return count; +} + +#define in_reg(offset) \ +static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in, \ + NULL, offset); \ +static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in_min, set_in_min, offset); \ +static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in_max, set_in_max, offset); + + +in_reg(0); +in_reg(1); +in_reg(2); +in_reg(3); +in_reg(4); +in_reg(5); +in_reg(6); +in_reg(7); +in_reg(8); +in_reg(9); +in_reg(10); +in_reg(11); +in_reg(12); +in_reg(13); +in_reg(14); +in_reg(15); + +static ssize_t show_in16(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", INS_FROM_REG(16, data->in[16]) - + NEG12_OFFSET); +} +static ssize_t show_in16_min(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", INS_FROM_REG(16, data->in_min[16]) + - NEG12_OFFSET); +} +static ssize_t set_in16_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->in_min[16] = INS_TO_REG(16, val + NEG12_OFFSET); + adm1026_write_value(client, ADM1026_REG_IN_MIN[16], data->in_min[16]); + up(&data->update_lock); + return count; +} +static ssize_t show_in16_max(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", INS_FROM_REG(16, data->in_max[16]) + - NEG12_OFFSET); +} +static ssize_t set_in16_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->in_max[16] = INS_TO_REG(16, val+NEG12_OFFSET); + adm1026_write_value(client, ADM1026_REG_IN_MAX[16], data->in_max[16]); + up(&data->update_lock); + return count; +} + +static SENSOR_DEVICE_ATTR(in16_input, S_IRUGO, show_in16, NULL, 16); +static SENSOR_DEVICE_ATTR(in16_min, S_IRUGO | S_IWUSR, show_in16_min, set_in16_min, 16); +static SENSOR_DEVICE_ATTR(in16_max, S_IRUGO | S_IWUSR, show_in16_max, set_in16_max, 16); + + + + +/* Now add fan read/write functions */ + +static ssize_t show_fan(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr], + data->fan_div[nr])); +} +static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr], + data->fan_div[nr])); +} +static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->fan_min[nr] = FAN_TO_REG(val, data->fan_div[nr]); + adm1026_write_value(client, ADM1026_REG_FAN_MIN(nr), + data->fan_min[nr]); + up(&data->update_lock); + return count; +} + +#define fan_offset(offset) \ +static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan, NULL, \ + offset - 1); \ +static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan_min, set_fan_min, offset - 1); + +fan_offset(1); +fan_offset(2); +fan_offset(3); +fan_offset(4); +fan_offset(5); +fan_offset(6); +fan_offset(7); +fan_offset(8); + +/* Adjust fan_min to account for new fan divisor */ +static void fixup_fan_min(struct device *dev, int fan, int old_div) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int new_min; + int new_div = data->fan_div[fan]; + + /* 0 and 0xff are special. Don't adjust them */ + if (data->fan_min[fan] == 0 || data->fan_min[fan] == 0xff) { + return; + } + + new_min = data->fan_min[fan] * old_div / new_div; + new_min = SENSORS_LIMIT(new_min, 1, 254); + data->fan_min[fan] = new_min; + adm1026_write_value(client, ADM1026_REG_FAN_MIN(fan), new_min); +} + +/* Now add fan_div read/write functions */ +static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", data->fan_div[nr]); +} +static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val,orig_div,new_div,shift; + + val = simple_strtol(buf, NULL, 10); + new_div = DIV_TO_REG(val); + if (new_div == 0) { + return -EINVAL; + } + down(&data->update_lock); + orig_div = data->fan_div[nr]; + data->fan_div[nr] = DIV_FROM_REG(new_div); + + if (nr < 4) { /* 0 <= nr < 4 */ + shift = 2 * nr; + adm1026_write_value(client, ADM1026_REG_FAN_DIV_0_3, + ((DIV_TO_REG(orig_div) & (~(0x03 << shift))) | + (new_div << shift))); + } else { /* 3 < nr < 8 */ + shift = 2 * (nr - 4); + adm1026_write_value(client, ADM1026_REG_FAN_DIV_4_7, + ((DIV_TO_REG(orig_div) & (~(0x03 << (2 * shift)))) | + (new_div << shift))); + } + + if (data->fan_div[nr] != orig_div) { + fixup_fan_min(dev,nr,orig_div); + } + up(&data->update_lock); + return count; +} + +#define fan_offset_div(offset) \ +static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ + show_fan_div, set_fan_div, offset - 1); + +fan_offset_div(1); +fan_offset_div(2); +fan_offset_div(3); +fan_offset_div(4); +fan_offset_div(5); +fan_offset_div(6); +fan_offset_div(7); +fan_offset_div(8); + +/* Temps */ +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp[nr])); +} +static ssize_t show_temp_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_min[nr])); +} +static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_min[nr] = TEMP_TO_REG(val); + adm1026_write_value(client, ADM1026_REG_TEMP_MIN[nr], + data->temp_min[nr]); + up(&data->update_lock); + return count; +} +static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_max[nr])); +} +static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_max[nr] = TEMP_TO_REG(val); + adm1026_write_value(client, ADM1026_REG_TEMP_MAX[nr], + data->temp_max[nr]); + up(&data->update_lock); + return count; +} + +#define temp_reg(offset) \ +static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp, \ + NULL, offset - 1); \ +static SENSOR_DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ + show_temp_min, set_temp_min, offset - 1); \ +static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ + show_temp_max, set_temp_max, offset - 1); + + +temp_reg(1); +temp_reg(2); +temp_reg(3); + +static ssize_t show_temp_offset(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_offset[nr])); +} +static ssize_t set_temp_offset(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_offset[nr] = TEMP_TO_REG(val); + adm1026_write_value(client, ADM1026_REG_TEMP_OFFSET[nr], + data->temp_offset[nr]); + up(&data->update_lock); + return count; +} + +#define temp_offset_reg(offset) \ +static SENSOR_DEVICE_ATTR(temp##offset##_offset, S_IRUGO | S_IWUSR, \ + show_temp_offset, set_temp_offset, offset - 1); + +temp_offset_reg(1); +temp_offset_reg(2); +temp_offset_reg(3); + +static ssize_t show_temp_auto_point1_temp_hyst(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG( + ADM1026_FAN_ACTIVATION_TEMP_HYST + data->temp_tmin[nr])); +} +static ssize_t show_temp_auto_point2_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_tmin[nr] + + ADM1026_FAN_CONTROL_TEMP_RANGE)); +} +static ssize_t show_temp_auto_point1_temp(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_tmin[nr])); +} +static ssize_t set_temp_auto_point1_temp(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_tmin[nr] = TEMP_TO_REG(val); + adm1026_write_value(client, ADM1026_REG_TEMP_TMIN[nr], + data->temp_tmin[nr]); + up(&data->update_lock); + return count; +} + +#define temp_auto_point(offset) \ +static SENSOR_DEVICE_ATTR(temp##offset##_auto_point1_temp, S_IRUGO | S_IWUSR, \ + show_temp_auto_point1_temp, set_temp_auto_point1_temp, \ + offset - 1); \ +static SENSOR_DEVICE_ATTR(temp##offset##_auto_point1_temp_hyst, S_IRUGO, \ + show_temp_auto_point1_temp_hyst, NULL, offset - 1); \ +static SENSOR_DEVICE_ATTR(temp##offset##_auto_point2_temp, S_IRUGO, \ + show_temp_auto_point2_temp, NULL, offset - 1); + +temp_auto_point(1); +temp_auto_point(2); +temp_auto_point(3); + +static ssize_t show_temp_crit_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", (data->config1 & CFG1_THERM_HOT) >> 4); +} +static ssize_t set_temp_crit_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + if ((val == 1) || (val==0)) { + down(&data->update_lock); + data->config1 = (data->config1 & ~CFG1_THERM_HOT) | (val << 4); + adm1026_write_value(client, ADM1026_REG_CONFIG1, + data->config1); + up(&data->update_lock); + } + return count; +} + +#define temp_crit_enable(offset) \ +static DEVICE_ATTR(temp##offset##_crit_enable, S_IRUGO | S_IWUSR, \ + show_temp_crit_enable, set_temp_crit_enable); + +temp_crit_enable(1); +temp_crit_enable(2); +temp_crit_enable(3); + +static ssize_t show_temp_crit(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_crit[nr])); +} +static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_crit[nr] = TEMP_TO_REG(val); + adm1026_write_value(client, ADM1026_REG_TEMP_THERM[nr], + data->temp_crit[nr]); + up(&data->update_lock); + return count; +} + +#define temp_crit_reg(offset) \ +static SENSOR_DEVICE_ATTR(temp##offset##_crit, S_IRUGO | S_IWUSR, \ + show_temp_crit, set_temp_crit, offset - 1); + +temp_crit_reg(1); +temp_crit_reg(2); +temp_crit_reg(3); + +static ssize_t show_analog_out_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", DAC_FROM_REG(data->analog_out)); +} +static ssize_t set_analog_out_reg(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->analog_out = DAC_TO_REG(val); + adm1026_write_value(client, ADM1026_REG_DAC, data->analog_out); + up(&data->update_lock); + return count; +} + +static DEVICE_ATTR(analog_out, S_IRUGO | S_IWUSR, show_analog_out_reg, + set_analog_out_reg); + +static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", vid_from_reg(data->vid & 0x3f, data->vrm)); +} +/* vid deprecated in favour of cpu0_vid, remove after 2005-11-11 */ +static DEVICE_ATTR(vid, S_IRUGO, show_vid_reg, NULL); +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); + +static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", data->vrm); +} +static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + + data->vrm = simple_strtol(buf, NULL, 10); + return count; +} + +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); + +static ssize_t show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf, "%ld\n", (long) (data->alarms)); +} + +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); + +static ssize_t show_alarm_mask(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%ld\n", data->alarm_mask); +} +static ssize_t set_alarm_mask(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + unsigned long mask; + + down(&data->update_lock); + data->alarm_mask = val & 0x7fffffff; + mask = data->alarm_mask + | (data->gpio_mask & 0x10000 ? 0x80000000 : 0); + adm1026_write_value(client, ADM1026_REG_MASK1, + mask & 0xff); + mask >>= 8; + adm1026_write_value(client, ADM1026_REG_MASK2, + mask & 0xff); + mask >>= 8; + adm1026_write_value(client, ADM1026_REG_MASK3, + mask & 0xff); + mask >>= 8; + adm1026_write_value(client, ADM1026_REG_MASK4, + mask & 0xff); + up(&data->update_lock); + return count; +} + +static DEVICE_ATTR(alarm_mask, S_IRUGO | S_IWUSR, show_alarm_mask, + set_alarm_mask); + + +static ssize_t show_gpio(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%ld\n", data->gpio); +} +static ssize_t set_gpio(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + long gpio; + + down(&data->update_lock); + data->gpio = val & 0x1ffff; + gpio = data->gpio; + adm1026_write_value(client, ADM1026_REG_GPIO_STATUS_0_7,gpio & 0xff); + gpio >>= 8; + adm1026_write_value(client, ADM1026_REG_GPIO_STATUS_8_15,gpio & 0xff); + gpio = ((gpio >> 1) & 0x80) | (data->alarms >> 24 & 0x7f); + adm1026_write_value(client, ADM1026_REG_STATUS4,gpio & 0xff); + up(&data->update_lock); + return count; +} + +static DEVICE_ATTR(gpio, S_IRUGO | S_IWUSR, show_gpio, set_gpio); + + +static ssize_t show_gpio_mask(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%ld\n", data->gpio_mask); +} +static ssize_t set_gpio_mask(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + long mask; + + down(&data->update_lock); + data->gpio_mask = val & 0x1ffff; + mask = data->gpio_mask; + adm1026_write_value(client, ADM1026_REG_GPIO_MASK_0_7,mask & 0xff); + mask >>= 8; + adm1026_write_value(client, ADM1026_REG_GPIO_MASK_8_15,mask & 0xff); + mask = ((mask >> 1) & 0x80) | (data->alarm_mask >> 24 & 0x7f); + adm1026_write_value(client, ADM1026_REG_MASK1,mask & 0xff); + up(&data->update_lock); + return count; +} + +static DEVICE_ATTR(gpio_mask, S_IRUGO | S_IWUSR, show_gpio_mask, set_gpio_mask); + +static ssize_t show_pwm_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", PWM_FROM_REG(data->pwm1.pwm)); +} +static ssize_t set_pwm_reg(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + + if (data->pwm1.enable == 1) { + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->pwm1.pwm = PWM_TO_REG(val); + adm1026_write_value(client, ADM1026_REG_PWM, data->pwm1.pwm); + up(&data->update_lock); + } + return count; +} +static ssize_t show_auto_pwm_min(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", data->pwm1.auto_pwm_min); +} +static ssize_t set_auto_pwm_min(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->pwm1.auto_pwm_min = SENSORS_LIMIT(val,0,255); + if (data->pwm1.enable == 2) { /* apply immediately */ + data->pwm1.pwm = PWM_TO_REG((data->pwm1.pwm & 0x0f) | + PWM_MIN_TO_REG(data->pwm1.auto_pwm_min)); + adm1026_write_value(client, ADM1026_REG_PWM, data->pwm1.pwm); + } + up(&data->update_lock); + return count; +} +static ssize_t show_auto_pwm_max(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf,"%d\n", ADM1026_PWM_MAX); +} +static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1026_data *data = adm1026_update_device(dev); + return sprintf(buf,"%d\n", data->pwm1.enable); +} +static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1026_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + int old_enable; + + if ((val >= 0) && (val < 3)) { + down(&data->update_lock); + old_enable = data->pwm1.enable; + data->pwm1.enable = val; + data->config1 = (data->config1 & ~CFG1_PWM_AFC) + | ((val == 2) ? CFG1_PWM_AFC : 0); + adm1026_write_value(client, ADM1026_REG_CONFIG1, + data->config1); + if (val == 2) { /* apply pwm1_auto_pwm_min to pwm1 */ + data->pwm1.pwm = PWM_TO_REG((data->pwm1.pwm & 0x0f) | + PWM_MIN_TO_REG(data->pwm1.auto_pwm_min)); + adm1026_write_value(client, ADM1026_REG_PWM, + data->pwm1.pwm); + } else if (!((old_enable == 1) && (val == 1))) { + /* set pwm to safe value */ + data->pwm1.pwm = 255; + adm1026_write_value(client, ADM1026_REG_PWM, + data->pwm1.pwm); + } + up(&data->update_lock); + } + return count; +} + +/* enable PWM fan control */ +static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm_reg, set_pwm_reg); +static DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm_reg, set_pwm_reg); +static DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm_reg, set_pwm_reg); +static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, + set_pwm_enable); +static DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, show_pwm_enable, + set_pwm_enable); +static DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, show_pwm_enable, + set_pwm_enable); +static DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO | S_IWUSR, + show_auto_pwm_min, set_auto_pwm_min); +static DEVICE_ATTR(temp2_auto_point1_pwm, S_IRUGO | S_IWUSR, + show_auto_pwm_min, set_auto_pwm_min); +static DEVICE_ATTR(temp3_auto_point1_pwm, S_IRUGO | S_IWUSR, + show_auto_pwm_min, set_auto_pwm_min); + +static DEVICE_ATTR(temp1_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL); +static DEVICE_ATTR(temp2_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL); +static DEVICE_ATTR(temp3_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL); + +int adm1026_detect(struct i2c_adapter *adapter, int address, + int kind) +{ + int company, verstep; + struct i2c_client *new_client; + struct adm1026_data *data; + int err = 0; + const char *type_name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + /* We need to be able to do byte I/O */ + goto exit; + }; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access adm1026_{read,write}_value. */ + + if (!(data = kmalloc(sizeof(struct adm1026_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + memset(data, 0, sizeof(struct adm1026_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &adm1026_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + company = adm1026_read_value(new_client, ADM1026_REG_COMPANY); + verstep = adm1026_read_value(new_client, ADM1026_REG_VERSTEP); + + dev_dbg(&new_client->dev, "Detecting device at %d,0x%02x with" + " COMPANY: 0x%02x and VERSTEP: 0x%02x\n", + i2c_adapter_id(new_client->adapter), new_client->addr, + company, verstep); + + /* If auto-detecting, Determine the chip type. */ + if (kind <= 0) { + dev_dbg(&new_client->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(&new_client->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); + } + err = 0; + goto exitfree; + } + } + + /* Fill in the chip specific driver values */ + switch (kind) { + case any_chip : + type_name = "adm1026"; + break; + case adm1026 : + type_name = "adm1026"; + break; + default : + dev_err(&adapter->dev, ": Internal error, invalid " + "kind (%d)!", kind); + err = -EFAULT; + goto exitfree; + } + strlcpy(new_client->name, type_name, I2C_NAME_SIZE); + + /* Fill in the remaining client fields */ + data->type = kind; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exitfree; + + /* Set the VRM version */ + data->vrm = i2c_which_vrm(); + + /* Initialize the ADM1026 chip */ + adm1026_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &sensor_dev_attr_in0_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in0_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in0_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in1_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in1_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in1_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in2_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in2_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in2_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in3_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in3_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in3_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in4_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in4_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in4_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in5_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in5_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in5_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in6_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in6_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in6_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in7_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in7_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in7_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in8_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in8_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in8_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in9_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in9_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in9_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in10_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in10_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in10_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in11_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in11_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in11_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in12_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in12_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in12_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in13_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in13_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in13_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in14_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in14_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in14_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in15_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in15_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in15_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in16_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in16_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in16_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan1_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan1_div.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan1_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan2_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan2_div.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan2_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan3_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan3_div.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan3_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan4_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan4_div.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan4_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan5_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan5_div.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan5_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan6_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan6_div.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan6_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan7_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan7_div.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan7_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan8_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan8_div.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan8_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp2_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp2_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp2_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp3_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp3_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp3_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_offset.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp2_offset.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp3_offset.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_auto_point1_temp.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_auto_point1_temp.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp3_auto_point1_temp.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_auto_point1_temp_hyst.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp3_auto_point1_temp_hyst.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_auto_point2_temp.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_auto_point2_temp.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp3_auto_point2_temp.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_crit.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp2_crit.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp3_crit.dev_attr); + device_create_file(&new_client->dev, &dev_attr_temp1_crit_enable); + device_create_file(&new_client->dev, &dev_attr_temp2_crit_enable); + device_create_file(&new_client->dev, &dev_attr_temp3_crit_enable); + /* vid deprecated in favour of cpu0_vid, remove after 2005-11-11 */ + device_create_file(&new_client->dev, &dev_attr_vid); + device_create_file(&new_client->dev, &dev_attr_cpu0_vid); + device_create_file(&new_client->dev, &dev_attr_vrm); + device_create_file(&new_client->dev, &dev_attr_alarms); + device_create_file(&new_client->dev, &dev_attr_alarm_mask); + device_create_file(&new_client->dev, &dev_attr_gpio); + device_create_file(&new_client->dev, &dev_attr_gpio_mask); + device_create_file(&new_client->dev, &dev_attr_pwm1); + device_create_file(&new_client->dev, &dev_attr_pwm2); + device_create_file(&new_client->dev, &dev_attr_pwm3); + device_create_file(&new_client->dev, &dev_attr_pwm1_enable); + device_create_file(&new_client->dev, &dev_attr_pwm2_enable); + device_create_file(&new_client->dev, &dev_attr_pwm3_enable); + device_create_file(&new_client->dev, &dev_attr_temp1_auto_point1_pwm); + device_create_file(&new_client->dev, &dev_attr_temp2_auto_point1_pwm); + device_create_file(&new_client->dev, &dev_attr_temp3_auto_point1_pwm); + device_create_file(&new_client->dev, &dev_attr_temp1_auto_point2_pwm); + device_create_file(&new_client->dev, &dev_attr_temp2_auto_point2_pwm); + device_create_file(&new_client->dev, &dev_attr_temp3_auto_point2_pwm); + device_create_file(&new_client->dev, &dev_attr_analog_out); + return 0; + + /* Error out and cleanup code */ +exitfree: + kfree(new_client); +exit: + return err; +} +static int __init sm_adm1026_init(void) +{ + return i2c_add_driver(&adm1026_driver); +} + +static void __exit sm_adm1026_exit(void) +{ + i2c_del_driver(&adm1026_driver); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Philip Pokorny , " + "Justin Thiessen "); +MODULE_DESCRIPTION("ADM1026 driver"); + +module_init(sm_adm1026_init); +module_exit(sm_adm1026_exit); diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c new file mode 100644 index 000000000000..9168e983ca1d --- /dev/null +++ b/drivers/hwmon/adm1031.c @@ -0,0 +1,977 @@ +/* + adm1031.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Based on lm75.c and lm85.c + Supports adm1030 / adm1031 + Copyright (C) 2004 Alexandre d'Alton + Reworked by Jean Delvare + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include + +/* Following macros takes channel parameter starting from 0 to 2 */ +#define ADM1031_REG_FAN_SPEED(nr) (0x08 + (nr)) +#define ADM1031_REG_FAN_DIV(nr) (0x20 + (nr)) +#define ADM1031_REG_PWM (0x22) +#define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr)) + +#define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4*(nr)) +#define ADM1031_REG_TEMP_MIN(nr) (0x15 + 4*(nr)) +#define ADM1031_REG_TEMP_CRIT(nr) (0x16 + 4*(nr)) + +#define ADM1031_REG_TEMP(nr) (0xa + (nr)) +#define ADM1031_REG_AUTO_TEMP(nr) (0x24 + (nr)) + +#define ADM1031_REG_STATUS(nr) (0x2 + (nr)) + +#define ADM1031_REG_CONF1 0x0 +#define ADM1031_REG_CONF2 0x1 +#define ADM1031_REG_EXT_TEMP 0x6 + +#define ADM1031_CONF1_MONITOR_ENABLE 0x01 /* Monitoring enable */ +#define ADM1031_CONF1_PWM_INVERT 0x08 /* PWM Invert */ +#define ADM1031_CONF1_AUTO_MODE 0x80 /* Auto FAN */ + +#define ADM1031_CONF2_PWM1_ENABLE 0x01 +#define ADM1031_CONF2_PWM2_ENABLE 0x02 +#define ADM1031_CONF2_TACH1_ENABLE 0x04 +#define ADM1031_CONF2_TACH2_ENABLE 0x08 +#define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan)) + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_2(adm1030, adm1031); + +typedef u8 auto_chan_table_t[8][2]; + +/* Each client has this additional data */ +struct adm1031_data { + struct i2c_client client; + struct semaphore update_lock; + int chip_type; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + /* The chan_select_table contains the possible configurations for + * auto fan control. + */ + auto_chan_table_t *chan_select_table; + u16 alarm; + u8 conf1; + u8 conf2; + u8 fan[2]; + u8 fan_div[2]; + u8 fan_min[2]; + u8 pwm[2]; + u8 old_pwm[2]; + s8 temp[3]; + u8 ext_temp[3]; + u8 auto_temp[3]; + u8 auto_temp_min[3]; + u8 auto_temp_off[3]; + u8 auto_temp_max[3]; + s8 temp_min[3]; + s8 temp_max[3]; + s8 temp_crit[3]; +}; + +static int adm1031_attach_adapter(struct i2c_adapter *adapter); +static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind); +static void adm1031_init_client(struct i2c_client *client); +static int adm1031_detach_client(struct i2c_client *client); +static struct adm1031_data *adm1031_update_device(struct device *dev); + +/* This is the driver that will be inserted */ +static struct i2c_driver adm1031_driver = { + .owner = THIS_MODULE, + .name = "adm1031", + .flags = I2C_DF_NOTIFY, + .attach_adapter = adm1031_attach_adapter, + .detach_client = adm1031_detach_client, +}; + +static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static inline int +adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + + +#define TEMP_TO_REG(val) (((val) < 0 ? ((val - 500) / 1000) : \ + ((val + 500) / 1000))) + +#define TEMP_FROM_REG(val) ((val) * 1000) + +#define TEMP_FROM_REG_EXT(val, ext) (TEMP_FROM_REG(val) + (ext) * 125) + +#define FAN_FROM_REG(reg, div) ((reg) ? (11250 * 60) / ((reg) * (div)) : 0) + +static int FAN_TO_REG(int reg, int div) +{ + int tmp; + tmp = FAN_FROM_REG(SENSORS_LIMIT(reg, 0, 65535), div); + return tmp > 255 ? 255 : tmp; +} + +#define FAN_DIV_FROM_REG(reg) (1<<(((reg)&0xc0)>>6)) + +#define PWM_TO_REG(val) (SENSORS_LIMIT((val), 0, 255) >> 4) +#define PWM_FROM_REG(val) ((val) << 4) + +#define FAN_CHAN_FROM_REG(reg) (((reg) >> 5) & 7) +#define FAN_CHAN_TO_REG(val, reg) \ + (((reg) & 0x1F) | (((val) << 5) & 0xe0)) + +#define AUTO_TEMP_MIN_TO_REG(val, reg) \ + ((((val)/500) & 0xf8)|((reg) & 0x7)) +#define AUTO_TEMP_RANGE_FROM_REG(reg) (5000 * (1<< ((reg)&0x7))) +#define AUTO_TEMP_MIN_FROM_REG(reg) (1000 * ((((reg) >> 3) & 0x1f) << 2)) + +#define AUTO_TEMP_MIN_FROM_REG_DEG(reg) ((((reg) >> 3) & 0x1f) << 2) + +#define AUTO_TEMP_OFF_FROM_REG(reg) \ + (AUTO_TEMP_MIN_FROM_REG(reg) - 5000) + +#define AUTO_TEMP_MAX_FROM_REG(reg) \ + (AUTO_TEMP_RANGE_FROM_REG(reg) + \ + AUTO_TEMP_MIN_FROM_REG(reg)) + +static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm) +{ + int ret; + int range = val - AUTO_TEMP_MIN_FROM_REG(reg); + + range = ((val - AUTO_TEMP_MIN_FROM_REG(reg))*10)/(16 - pwm); + ret = ((reg & 0xf8) | + (range < 10000 ? 0 : + range < 20000 ? 1 : + range < 40000 ? 2 : range < 80000 ? 3 : 4)); + return ret; +} + +/* FAN auto control */ +#define GET_FAN_AUTO_BITFIELD(data, idx) \ + (*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx%2] + +/* The tables below contains the possible values for the auto fan + * control bitfields. the index in the table is the register value. + * MSb is the auto fan control enable bit, so the four first entries + * in the table disables auto fan control when both bitfields are zero. + */ +static auto_chan_table_t auto_channel_select_table_adm1031 = { + {0, 0}, {0, 0}, {0, 0}, {0, 0}, + {2 /*0b010 */ , 4 /*0b100 */ }, + {2 /*0b010 */ , 2 /*0b010 */ }, + {4 /*0b100 */ , 4 /*0b100 */ }, + {7 /*0b111 */ , 7 /*0b111 */ }, +}; + +static auto_chan_table_t auto_channel_select_table_adm1030 = { + {0, 0}, {0, 0}, {0, 0}, {0, 0}, + {2 /*0b10 */ , 0}, + {0xff /*invalid */ , 0}, + {0xff /*invalid */ , 0}, + {3 /*0b11 */ , 0}, +}; + +/* That function checks if a bitfield is valid and returns the other bitfield + * nearest match if no exact match where found. + */ +static int +get_fan_auto_nearest(struct adm1031_data *data, + int chan, u8 val, u8 reg, u8 * new_reg) +{ + int i; + int first_match = -1, exact_match = -1; + u8 other_reg_val = + (*data->chan_select_table)[FAN_CHAN_FROM_REG(reg)][chan ? 0 : 1]; + + if (val == 0) { + *new_reg = 0; + return 0; + } + + for (i = 0; i < 8; i++) { + if ((val == (*data->chan_select_table)[i][chan]) && + ((*data->chan_select_table)[i][chan ? 0 : 1] == + other_reg_val)) { + /* We found an exact match */ + exact_match = i; + break; + } else if (val == (*data->chan_select_table)[i][chan] && + first_match == -1) { + /* Save the first match in case of an exact match has not been + * found + */ + first_match = i; + } + } + + if (exact_match >= 0) { + *new_reg = exact_match; + } else if (first_match >= 0) { + *new_reg = first_match; + } else { + return -EINVAL; + } + return 0; +} + +static ssize_t show_fan_auto_channel(struct device *dev, char *buf, int nr) +{ + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", GET_FAN_AUTO_BITFIELD(data, nr)); +} + +static ssize_t +set_fan_auto_channel(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + u8 reg; + int ret; + u8 old_fan_mode; + + old_fan_mode = data->conf1; + + down(&data->update_lock); + + if ((ret = get_fan_auto_nearest(data, nr, val, data->conf1, ®))) { + up(&data->update_lock); + return ret; + } + if (((data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1)) & ADM1031_CONF1_AUTO_MODE) ^ + (old_fan_mode & ADM1031_CONF1_AUTO_MODE)) { + if (data->conf1 & ADM1031_CONF1_AUTO_MODE){ + /* Switch to Auto Fan Mode + * Save PWM registers + * Set PWM registers to 33% Both */ + data->old_pwm[0] = data->pwm[0]; + data->old_pwm[1] = data->pwm[1]; + adm1031_write_value(client, ADM1031_REG_PWM, 0x55); + } else { + /* Switch to Manual Mode */ + data->pwm[0] = data->old_pwm[0]; + data->pwm[1] = data->old_pwm[1]; + /* Restore PWM registers */ + adm1031_write_value(client, ADM1031_REG_PWM, + data->pwm[0] | (data->pwm[1] << 4)); + } + } + data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1); + adm1031_write_value(client, ADM1031_REG_CONF1, data->conf1); + up(&data->update_lock); + return count; +} + +#define fan_auto_channel_offset(offset) \ +static ssize_t show_fan_auto_channel_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_auto_channel(dev, buf, offset - 1); \ +} \ +static ssize_t set_fan_auto_channel_##offset (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_fan_auto_channel(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(auto_fan##offset##_channel, S_IRUGO | S_IWUSR, \ + show_fan_auto_channel_##offset, \ + set_fan_auto_channel_##offset) + +fan_auto_channel_offset(1); +fan_auto_channel_offset(2); + +/* Auto Temps */ +static ssize_t show_auto_temp_off(struct device *dev, char *buf, int nr) +{ + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", + AUTO_TEMP_OFF_FROM_REG(data->auto_temp[nr])); +} +static ssize_t show_auto_temp_min(struct device *dev, char *buf, int nr) +{ + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", + AUTO_TEMP_MIN_FROM_REG(data->auto_temp[nr])); +} +static ssize_t +set_auto_temp_min(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->auto_temp[nr] = AUTO_TEMP_MIN_TO_REG(val, data->auto_temp[nr]); + adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr), + data->auto_temp[nr]); + up(&data->update_lock); + return count; +} +static ssize_t show_auto_temp_max(struct device *dev, char *buf, int nr) +{ + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", + AUTO_TEMP_MAX_FROM_REG(data->auto_temp[nr])); +} +static ssize_t +set_auto_temp_max(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_max[nr] = AUTO_TEMP_MAX_TO_REG(val, data->auto_temp[nr], data->pwm[nr]); + adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr), + data->temp_max[nr]); + up(&data->update_lock); + return count; +} + +#define auto_temp_reg(offset) \ +static ssize_t show_auto_temp_##offset##_off (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_auto_temp_off(dev, buf, offset - 1); \ +} \ +static ssize_t show_auto_temp_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_auto_temp_min(dev, buf, offset - 1); \ +} \ +static ssize_t show_auto_temp_##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_auto_temp_max(dev, buf, offset - 1); \ +} \ +static ssize_t set_auto_temp_##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_auto_temp_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t set_auto_temp_##offset##_max (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_auto_temp_max(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(auto_temp##offset##_off, S_IRUGO, \ + show_auto_temp_##offset##_off, NULL); \ +static DEVICE_ATTR(auto_temp##offset##_min, S_IRUGO | S_IWUSR, \ + show_auto_temp_##offset##_min, set_auto_temp_##offset##_min);\ +static DEVICE_ATTR(auto_temp##offset##_max, S_IRUGO | S_IWUSR, \ + show_auto_temp_##offset##_max, set_auto_temp_##offset##_max) + +auto_temp_reg(1); +auto_temp_reg(2); +auto_temp_reg(3); + +/* pwm */ +static ssize_t show_pwm(struct device *dev, char *buf, int nr) +{ + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr])); +} +static ssize_t +set_pwm(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + int reg; + + down(&data->update_lock); + if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) && + (((val>>4) & 0xf) != 5)) { + /* In automatic mode, the only PWM accepted is 33% */ + up(&data->update_lock); + return -EINVAL; + } + data->pwm[nr] = PWM_TO_REG(val); + reg = adm1031_read_value(client, ADM1031_REG_PWM); + adm1031_write_value(client, ADM1031_REG_PWM, + nr ? ((data->pwm[nr] << 4) & 0xf0) | (reg & 0xf) + : (data->pwm[nr] & 0xf) | (reg & 0xf0)); + up(&data->update_lock); + return count; +} + +#define pwm_reg(offset) \ +static ssize_t show_pwm_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_pwm(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm_##offset (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_pwm(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ + show_pwm_##offset, set_pwm_##offset) + +pwm_reg(1); +pwm_reg(2); + +/* Fans */ + +/* + * That function checks the cases where the fan reading is not + * relevant. It is used to provide 0 as fan reading when the fan is + * not supposed to run + */ +static int trust_fan_readings(struct adm1031_data *data, int chan) +{ + int res = 0; + + if (data->conf1 & ADM1031_CONF1_AUTO_MODE) { + switch (data->conf1 & 0x60) { + case 0x00: /* remote temp1 controls fan1 remote temp2 controls fan2 */ + res = data->temp[chan+1] >= + AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[chan+1]); + break; + case 0x20: /* remote temp1 controls both fans */ + res = + data->temp[1] >= + AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1]); + break; + case 0x40: /* remote temp2 controls both fans */ + res = + data->temp[2] >= + AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]); + break; + case 0x60: /* max controls both fans */ + res = + data->temp[0] >= + AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[0]) + || data->temp[1] >= + AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1]) + || (data->chip_type == adm1031 + && data->temp[2] >= + AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2])); + break; + } + } else { + res = data->pwm[chan] > 0; + } + return res; +} + + +static ssize_t show_fan(struct device *dev, char *buf, int nr) +{ + struct adm1031_data *data = adm1031_update_device(dev); + int value; + + value = trust_fan_readings(data, nr) ? FAN_FROM_REG(data->fan[nr], + FAN_DIV_FROM_REG(data->fan_div[nr])) : 0; + return sprintf(buf, "%d\n", value); +} + +static ssize_t show_fan_div(struct device *dev, char *buf, int nr) +{ + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[nr])); +} +static ssize_t show_fan_min(struct device *dev, char *buf, int nr) +{ + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", + FAN_FROM_REG(data->fan_min[nr], + FAN_DIV_FROM_REG(data->fan_div[nr]))); +} +static ssize_t +set_fan_min(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + if (val) { + data->fan_min[nr] = + FAN_TO_REG(val, FAN_DIV_FROM_REG(data->fan_div[nr])); + } else { + data->fan_min[nr] = 0xff; + } + adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), data->fan_min[nr]); + up(&data->update_lock); + return count; +} +static ssize_t +set_fan_div(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + u8 tmp; + int old_div; + int new_min; + + tmp = val == 8 ? 0xc0 : + val == 4 ? 0x80 : + val == 2 ? 0x40 : + val == 1 ? 0x00 : + 0xff; + if (tmp == 0xff) + return -EINVAL; + + down(&data->update_lock); + old_div = FAN_DIV_FROM_REG(data->fan_div[nr]); + data->fan_div[nr] = (tmp & 0xC0) | (0x3f & data->fan_div[nr]); + new_min = data->fan_min[nr] * old_div / + FAN_DIV_FROM_REG(data->fan_div[nr]); + data->fan_min[nr] = new_min > 0xff ? 0xff : new_min; + data->fan[nr] = data->fan[nr] * old_div / + FAN_DIV_FROM_REG(data->fan_div[nr]); + + adm1031_write_value(client, ADM1031_REG_FAN_DIV(nr), + data->fan_div[nr]); + adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), + data->fan_min[nr]); + up(&data->update_lock); + return count; +} + +#define fan_offset(offset) \ +static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_min(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_div(dev, buf, offset - 1); \ +} \ +static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_fan_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t set_fan_##offset##_div (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_fan_div(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, \ + NULL); \ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan_##offset##_min, set_fan_##offset##_min); \ +static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ + show_fan_##offset##_div, set_fan_##offset##_div); \ +static DEVICE_ATTR(auto_fan##offset##_min_pwm, S_IRUGO | S_IWUSR, \ + show_pwm_##offset, set_pwm_##offset) + +fan_offset(1); +fan_offset(2); + + +/* Temps */ +static ssize_t show_temp(struct device *dev, char *buf, int nr) +{ + struct adm1031_data *data = adm1031_update_device(dev); + int ext; + ext = nr == 0 ? + ((data->ext_temp[nr] >> 6) & 0x3) * 2 : + (((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7)); + return sprintf(buf, "%d\n", TEMP_FROM_REG_EXT(data->temp[nr], ext)); +} +static ssize_t show_temp_min(struct device *dev, char *buf, int nr) +{ + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr])); +} +static ssize_t show_temp_max(struct device *dev, char *buf, int nr) +{ + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr])); +} +static ssize_t show_temp_crit(struct device *dev, char *buf, int nr) +{ + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[nr])); +} +static ssize_t +set_temp_min(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + int val; + + val = simple_strtol(buf, NULL, 10); + val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875); + down(&data->update_lock); + data->temp_min[nr] = TEMP_TO_REG(val); + adm1031_write_value(client, ADM1031_REG_TEMP_MIN(nr), + data->temp_min[nr]); + up(&data->update_lock); + return count; +} +static ssize_t +set_temp_max(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + int val; + + val = simple_strtol(buf, NULL, 10); + val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875); + down(&data->update_lock); + data->temp_max[nr] = TEMP_TO_REG(val); + adm1031_write_value(client, ADM1031_REG_TEMP_MAX(nr), + data->temp_max[nr]); + up(&data->update_lock); + return count; +} +static ssize_t +set_temp_crit(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + int val; + + val = simple_strtol(buf, NULL, 10); + val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875); + down(&data->update_lock); + data->temp_crit[nr] = TEMP_TO_REG(val); + adm1031_write_value(client, ADM1031_REG_TEMP_CRIT(nr), + data->temp_crit[nr]); + up(&data->update_lock); + return count; +} + +#define temp_reg(offset) \ +static ssize_t show_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp(dev, buf, offset - 1); \ +} \ +static ssize_t show_temp_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp_min(dev, buf, offset - 1); \ +} \ +static ssize_t show_temp_##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp_max(dev, buf, offset - 1); \ +} \ +static ssize_t show_temp_##offset##_crit (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp_crit(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp_##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_temp_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t set_temp_##offset##_max (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_temp_max(dev, buf, count, offset - 1); \ +} \ +static ssize_t set_temp_##offset##_crit (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_temp_crit(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, \ + NULL); \ +static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ + show_temp_##offset##_min, set_temp_##offset##_min); \ +static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ + show_temp_##offset##_max, set_temp_##offset##_max); \ +static DEVICE_ATTR(temp##offset##_crit, S_IRUGO | S_IWUSR, \ + show_temp_##offset##_crit, set_temp_##offset##_crit) + +temp_reg(1); +temp_reg(2); +temp_reg(3); + +/* Alarms */ +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm1031_data *data = adm1031_update_device(dev); + return sprintf(buf, "%d\n", data->alarm); +} + +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + + +static int adm1031_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, adm1031_detect); +} + +/* This function is called by i2c_detect */ +static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct adm1031_data *data; + int err = 0; + const char *name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct adm1031_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct adm1031_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &adm1031_driver; + new_client->flags = 0; + + if (kind < 0) { + int id, co; + id = i2c_smbus_read_byte_data(new_client, 0x3d); + co = i2c_smbus_read_byte_data(new_client, 0x3e); + + if (!((id == 0x31 || id == 0x30) && co == 0x41)) + goto exit_free; + kind = (id == 0x30) ? adm1030 : adm1031; + } + + if (kind <= 0) + kind = adm1031; + + /* Given the detected chip type, set the chip name and the + * auto fan control helper table. */ + if (kind == adm1030) { + name = "adm1030"; + data->chan_select_table = &auto_channel_select_table_adm1030; + } else if (kind == adm1031) { + name = "adm1031"; + data->chan_select_table = &auto_channel_select_table_adm1031; + } + data->chip_type = kind; + + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the ADM1031 chip */ + adm1031_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_fan1_input); + device_create_file(&new_client->dev, &dev_attr_fan1_div); + device_create_file(&new_client->dev, &dev_attr_fan1_min); + device_create_file(&new_client->dev, &dev_attr_pwm1); + device_create_file(&new_client->dev, &dev_attr_auto_fan1_channel); + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp1_min); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp1_crit); + device_create_file(&new_client->dev, &dev_attr_temp2_input); + device_create_file(&new_client->dev, &dev_attr_temp2_min); + device_create_file(&new_client->dev, &dev_attr_temp2_max); + device_create_file(&new_client->dev, &dev_attr_temp2_crit); + + device_create_file(&new_client->dev, &dev_attr_auto_temp1_off); + device_create_file(&new_client->dev, &dev_attr_auto_temp1_min); + device_create_file(&new_client->dev, &dev_attr_auto_temp1_max); + + device_create_file(&new_client->dev, &dev_attr_auto_temp2_off); + device_create_file(&new_client->dev, &dev_attr_auto_temp2_min); + device_create_file(&new_client->dev, &dev_attr_auto_temp2_max); + + device_create_file(&new_client->dev, &dev_attr_auto_fan1_min_pwm); + + device_create_file(&new_client->dev, &dev_attr_alarms); + + if (kind == adm1031) { + device_create_file(&new_client->dev, &dev_attr_fan2_input); + device_create_file(&new_client->dev, &dev_attr_fan2_div); + device_create_file(&new_client->dev, &dev_attr_fan2_min); + device_create_file(&new_client->dev, &dev_attr_pwm2); + device_create_file(&new_client->dev, + &dev_attr_auto_fan2_channel); + device_create_file(&new_client->dev, &dev_attr_temp3_input); + device_create_file(&new_client->dev, &dev_attr_temp3_min); + device_create_file(&new_client->dev, &dev_attr_temp3_max); + device_create_file(&new_client->dev, &dev_attr_temp3_crit); + device_create_file(&new_client->dev, &dev_attr_auto_temp3_off); + device_create_file(&new_client->dev, &dev_attr_auto_temp3_min); + device_create_file(&new_client->dev, &dev_attr_auto_temp3_max); + device_create_file(&new_client->dev, &dev_attr_auto_fan2_min_pwm); + } + + return 0; + +exit_free: + kfree(new_client); +exit: + return err; +} + +static int adm1031_detach_client(struct i2c_client *client) +{ + int ret; + if ((ret = i2c_detach_client(client)) != 0) { + return ret; + } + kfree(client); + return 0; +} + +static void adm1031_init_client(struct i2c_client *client) +{ + unsigned int read_val; + unsigned int mask; + struct adm1031_data *data = i2c_get_clientdata(client); + + mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE); + if (data->chip_type == adm1031) { + mask |= (ADM1031_CONF2_PWM2_ENABLE | + ADM1031_CONF2_TACH2_ENABLE); + } + /* Initialize the ADM1031 chip (enables fan speed reading ) */ + read_val = adm1031_read_value(client, ADM1031_REG_CONF2); + if ((read_val | mask) != read_val) { + adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask); + } + + read_val = adm1031_read_value(client, ADM1031_REG_CONF1); + if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) { + adm1031_write_value(client, ADM1031_REG_CONF1, read_val | + ADM1031_CONF1_MONITOR_ENABLE); + } + +} + +static struct adm1031_data *adm1031_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm1031_data *data = i2c_get_clientdata(client); + int chan; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + + dev_dbg(&client->dev, "Starting adm1031 update\n"); + for (chan = 0; + chan < ((data->chip_type == adm1031) ? 3 : 2); chan++) { + u8 oldh, newh; + + oldh = + adm1031_read_value(client, ADM1031_REG_TEMP(chan)); + data->ext_temp[chan] = + adm1031_read_value(client, ADM1031_REG_EXT_TEMP); + newh = + adm1031_read_value(client, ADM1031_REG_TEMP(chan)); + if (newh != oldh) { + data->ext_temp[chan] = + adm1031_read_value(client, + ADM1031_REG_EXT_TEMP); +#ifdef DEBUG + oldh = + adm1031_read_value(client, + ADM1031_REG_TEMP(chan)); + + /* oldh is actually newer */ + if (newh != oldh) + dev_warn(&client->dev, + "Remote temperature may be " + "wrong.\n"); +#endif + } + data->temp[chan] = newh; + + data->temp_min[chan] = + adm1031_read_value(client, + ADM1031_REG_TEMP_MIN(chan)); + data->temp_max[chan] = + adm1031_read_value(client, + ADM1031_REG_TEMP_MAX(chan)); + data->temp_crit[chan] = + adm1031_read_value(client, + ADM1031_REG_TEMP_CRIT(chan)); + data->auto_temp[chan] = + adm1031_read_value(client, + ADM1031_REG_AUTO_TEMP(chan)); + + } + + data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1); + data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2); + + data->alarm = adm1031_read_value(client, ADM1031_REG_STATUS(0)) + | (adm1031_read_value(client, ADM1031_REG_STATUS(1)) + << 8); + if (data->chip_type == adm1030) { + data->alarm &= 0xc0ff; + } + + for (chan=0; chan<(data->chip_type == adm1030 ? 1 : 2); chan++) { + data->fan_div[chan] = + adm1031_read_value(client, ADM1031_REG_FAN_DIV(chan)); + data->fan_min[chan] = + adm1031_read_value(client, ADM1031_REG_FAN_MIN(chan)); + data->fan[chan] = + adm1031_read_value(client, ADM1031_REG_FAN_SPEED(chan)); + data->pwm[chan] = + 0xf & (adm1031_read_value(client, ADM1031_REG_PWM) >> + (4*chan)); + } + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_adm1031_init(void) +{ + return i2c_add_driver(&adm1031_driver); +} + +static void __exit sensors_adm1031_exit(void) +{ + i2c_del_driver(&adm1031_driver); +} + +MODULE_AUTHOR("Alexandre d'Alton "); +MODULE_DESCRIPTION("ADM1031/ADM1030 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_adm1031_init); +module_exit(sensors_adm1031_exit); diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c new file mode 100644 index 000000000000..5c68e9c311aa --- /dev/null +++ b/drivers/hwmon/adm9240.c @@ -0,0 +1,791 @@ +/* + * adm9240.c Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * + * Copyright (C) 1999 Frodo Looijaard + * Philip Edelbrock + * Copyright (C) 2003 Michiel Rook + * Copyright (C) 2005 Grant Coady with valuable + * guidance from Jean Delvare + * + * Driver supports Analog Devices ADM9240 + * Dallas Semiconductor DS1780 + * National Semiconductor LM81 + * + * ADM9240 is the reference, DS1780 and LM81 are register compatibles + * + * Voltage Six inputs are scaled by chip, VID also reported + * Temperature Chip temperature to 0.5'C, maximum and max_hysteris + * Fans 2 fans, low speed alarm, automatic fan clock divider + * Alarms 16-bit map of active alarms + * Analog Out 0..1250 mV output + * + * Chassis Intrusion: clear CI latch with 'echo 1 > chassis_clear' + * + * Test hardware: Intel SE440BX-2 desktop motherboard --Grant + * + * LM81 extended temp reading not implemented + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, + I2C_CLIENT_END }; + +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_3(adm9240, ds1780, lm81); + +/* ADM9240 registers */ +#define ADM9240_REG_MAN_ID 0x3e +#define ADM9240_REG_DIE_REV 0x3f +#define ADM9240_REG_CONFIG 0x40 + +#define ADM9240_REG_IN(nr) (0x20 + (nr)) /* 0..5 */ +#define ADM9240_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define ADM9240_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define ADM9240_REG_FAN(nr) (0x28 + (nr)) /* 0..1 */ +#define ADM9240_REG_FAN_MIN(nr) (0x3b + (nr)) +#define ADM9240_REG_INT(nr) (0x41 + (nr)) +#define ADM9240_REG_INT_MASK(nr) (0x43 + (nr)) +#define ADM9240_REG_TEMP 0x27 +#define ADM9240_REG_TEMP_HIGH 0x39 +#define ADM9240_REG_TEMP_HYST 0x3a +#define ADM9240_REG_ANALOG_OUT 0x19 +#define ADM9240_REG_CHASSIS_CLEAR 0x46 +#define ADM9240_REG_VID_FAN_DIV 0x47 +#define ADM9240_REG_I2C_ADDR 0x48 +#define ADM9240_REG_VID4 0x49 +#define ADM9240_REG_TEMP_CONF 0x4b + +/* generalised scaling with integer rounding */ +static inline int SCALE(long val, int mul, int div) +{ + if (val < 0) + return (val * mul - div / 2) / div; + else + return (val * mul + div / 2) / div; +} + +/* adm9240 internally scales voltage measurements */ +static const u16 nom_mv[] = { 2500, 2700, 3300, 5000, 12000, 2700 }; + +static inline unsigned int IN_FROM_REG(u8 reg, int n) +{ + return SCALE(reg, nom_mv[n], 192); +} + +static inline u8 IN_TO_REG(unsigned long val, int n) +{ + return SENSORS_LIMIT(SCALE(val, 192, nom_mv[n]), 0, 255); +} + +/* temperature range: -40..125, 127 disables temperature alarm */ +static inline s8 TEMP_TO_REG(long val) +{ + return SENSORS_LIMIT(SCALE(val, 1, 1000), -40, 127); +} + +/* two fans, each with low fan speed limit */ +static inline unsigned int FAN_FROM_REG(u8 reg, u8 div) +{ + if (!reg) /* error */ + return -1; + + if (reg == 255) + return 0; + + return SCALE(1350000, 1, reg * div); +} + +/* analog out 0..1250mV */ +static inline u8 AOUT_TO_REG(unsigned long val) +{ + return SENSORS_LIMIT(SCALE(val, 255, 1250), 0, 255); +} + +static inline unsigned int AOUT_FROM_REG(u8 reg) +{ + return SCALE(reg, 1250, 255); +} + +static int adm9240_attach_adapter(struct i2c_adapter *adapter); +static int adm9240_detect(struct i2c_adapter *adapter, int address, int kind); +static void adm9240_init_client(struct i2c_client *client); +static int adm9240_detach_client(struct i2c_client *client); +static struct adm9240_data *adm9240_update_device(struct device *dev); + +/* driver data */ +static struct i2c_driver adm9240_driver = { + .owner = THIS_MODULE, + .name = "adm9240", + .id = I2C_DRIVERID_ADM9240, + .flags = I2C_DF_NOTIFY, + .attach_adapter = adm9240_attach_adapter, + .detach_client = adm9240_detach_client, +}; + +/* per client data */ +struct adm9240_data { + enum chips type; + struct i2c_client client; + struct semaphore update_lock; + char valid; + unsigned long last_updated_measure; + unsigned long last_updated_config; + + u8 in[6]; /* ro in0_input */ + u8 in_max[6]; /* rw in0_max */ + u8 in_min[6]; /* rw in0_min */ + u8 fan[2]; /* ro fan1_input */ + u8 fan_min[2]; /* rw fan1_min */ + u8 fan_div[2]; /* rw fan1_div, read-only accessor */ + s16 temp; /* ro temp1_input, 9-bit sign-extended */ + s8 temp_high; /* rw temp1_max */ + s8 temp_hyst; /* rw temp1_max_hyst */ + u16 alarms; /* ro alarms */ + u8 aout; /* rw aout_output */ + u8 vid; /* ro vid */ + u8 vrm; /* -- vrm set on startup, no accessor */ +}; + +/* i2c byte read/write interface */ +static int adm9240_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int adm9240_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/*** sysfs accessors ***/ + +/* temperature */ +#define show_temp(value, scale) \ +static ssize_t show_##value(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct adm9240_data *data = adm9240_update_device(dev); \ + return sprintf(buf, "%d\n", data->value * scale); \ +} +show_temp(temp_high, 1000); +show_temp(temp_hyst, 1000); +show_temp(temp, 500); /* 0.5'C per bit */ + +#define set_temp(value, reg) \ +static ssize_t set_##value(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct adm9240_data *data = adm9240_update_device(dev); \ + long temp = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->value = TEMP_TO_REG(temp); \ + adm9240_write_value(client, reg, data->value); \ + up(&data->update_lock); \ + return count; \ +} + +set_temp(temp_high, ADM9240_REG_TEMP_HIGH); +set_temp(temp_hyst, ADM9240_REG_TEMP_HYST); + +static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, + show_temp_high, set_temp_high); +static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, + show_temp_hyst, set_temp_hyst); +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); + +/* voltage */ +static ssize_t show_in(struct device *dev, char *buf, int nr) +{ + struct adm9240_data *data = adm9240_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr], nr)); +} + +static ssize_t show_in_min(struct device *dev, char *buf, int nr) +{ + struct adm9240_data *data = adm9240_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr], nr)); +} + +static ssize_t show_in_max(struct device *dev, char *buf, int nr) +{ + struct adm9240_data *data = adm9240_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr], nr)); +} + +static ssize_t set_in_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm9240_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->in_min[nr] = IN_TO_REG(val, nr); + adm9240_write_value(client, ADM9240_REG_IN_MIN(nr), data->in_min[nr]); + up(&data->update_lock); + return count; +} + +static ssize_t set_in_max(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm9240_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->in_max[nr] = IN_TO_REG(val, nr); + adm9240_write_value(client, ADM9240_REG_IN_MAX(nr), data->in_max[nr]); + up(&data->update_lock); + return count; +} + +#define show_in_offset(offset) \ +static ssize_t show_in##offset(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_in(dev, buf, offset); \ +} \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL); \ +static ssize_t show_in##offset##_min(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_in_min(dev, buf, offset); \ +} \ +static ssize_t show_in##offset##_max(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_in_max(dev, buf, offset); \ +} \ +static ssize_t \ +set_in##offset##_min(struct device *dev, \ + struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return set_in_min(dev, buf, count, offset); \ +} \ +static ssize_t \ +set_in##offset##_max(struct device *dev, \ + struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return set_in_max(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in##offset##_min, set_in##offset##_min); \ +static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in##offset##_max, set_in##offset##_max); + +show_in_offset(0); +show_in_offset(1); +show_in_offset(2); +show_in_offset(3); +show_in_offset(4); +show_in_offset(5); + +/* fans */ +static ssize_t show_fan(struct device *dev, char *buf, int nr) +{ + struct adm9240_data *data = adm9240_update_device(dev); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], + 1 << data->fan_div[nr])); +} + +static ssize_t show_fan_min(struct device *dev, char *buf, int nr) +{ + struct adm9240_data *data = adm9240_update_device(dev); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], + 1 << data->fan_div[nr])); +} + +static ssize_t show_fan_div(struct device *dev, char *buf, int nr) +{ + struct adm9240_data *data = adm9240_update_device(dev); + return sprintf(buf, "%d\n", 1 << data->fan_div[nr]); +} + +/* write new fan div, callers must hold data->update_lock */ +static void adm9240_write_fan_div(struct i2c_client *client, int nr, + u8 fan_div) +{ + u8 reg, old, shift = (nr + 2) * 2; + + reg = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV); + old = (reg >> shift) & 3; + reg &= ~(3 << shift); + reg |= (fan_div << shift); + adm9240_write_value(client, ADM9240_REG_VID_FAN_DIV, reg); + dev_dbg(&client->dev, "fan%d clock divider changed from %u " + "to %u\n", nr + 1, 1 << old, 1 << fan_div); +} + +/* + * set fan speed low limit: + * + * - value is zero: disable fan speed low limit alarm + * + * - value is below fan speed measurement range: enable fan speed low + * limit alarm to be asserted while fan speed too slow to measure + * + * - otherwise: select fan clock divider to suit fan speed low limit, + * measurement code may adjust registers to ensure fan speed reading + */ +static ssize_t set_fan_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm9240_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + u8 new_div; + + down(&data->update_lock); + + if (!val) { + data->fan_min[nr] = 255; + new_div = data->fan_div[nr]; + + dev_dbg(&client->dev, "fan%u low limit set disabled\n", + nr + 1); + + } else if (val < 1350000 / (8 * 254)) { + new_div = 3; + data->fan_min[nr] = 254; + + dev_dbg(&client->dev, "fan%u low limit set minimum %u\n", + nr + 1, FAN_FROM_REG(254, 1 << new_div)); + + } else { + unsigned int new_min = 1350000 / val; + + new_div = 0; + while (new_min > 192 && new_div < 3) { + new_div++; + new_min /= 2; + } + if (!new_min) /* keep > 0 */ + new_min++; + + data->fan_min[nr] = new_min; + + dev_dbg(&client->dev, "fan%u low limit set fan speed %u\n", + nr + 1, FAN_FROM_REG(new_min, 1 << new_div)); + } + + if (new_div != data->fan_div[nr]) { + data->fan_div[nr] = new_div; + adm9240_write_fan_div(client, nr, new_div); + } + adm9240_write_value(client, ADM9240_REG_FAN_MIN(nr), + data->fan_min[nr]); + + up(&data->update_lock); + return count; +} + +#define show_fan_offset(offset) \ +static ssize_t show_fan_##offset (struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ +return show_fan(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan_##offset##_div (struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ +return show_fan_div(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan_##offset##_min (struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ +return show_fan_min(dev, buf, offset - 1); \ +} \ +static ssize_t set_fan_##offset##_min (struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ +return set_fan_min(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ + show_fan_##offset, NULL); \ +static DEVICE_ATTR(fan##offset##_div, S_IRUGO, \ + show_fan_##offset##_div, NULL); \ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan_##offset##_min, set_fan_##offset##_min); + +show_fan_offset(1); +show_fan_offset(2); + +/* alarms */ +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm9240_data *data = adm9240_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); +} +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +/* vid */ +static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm9240_data *data = adm9240_update_device(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); +} +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + +/* analog output */ +static ssize_t show_aout(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct adm9240_data *data = adm9240_update_device(dev); + return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout)); +} + +static ssize_t set_aout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm9240_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->aout = AOUT_TO_REG(val); + adm9240_write_value(client, ADM9240_REG_ANALOG_OUT, data->aout); + up(&data->update_lock); + return count; +} +static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout); + +/* chassis_clear */ +static ssize_t chassis_clear(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + unsigned long val = simple_strtol(buf, NULL, 10); + + if (val == 1) { + adm9240_write_value(client, ADM9240_REG_CHASSIS_CLEAR, 0x80); + dev_dbg(&client->dev, "chassis intrusion latch cleared\n"); + } + return count; +} +static DEVICE_ATTR(chassis_clear, S_IWUSR, NULL, chassis_clear); + + +/*** sensor chip detect and driver install ***/ + +static int adm9240_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct adm9240_data *data; + int err = 0; + const char *name = ""; + u8 man_id, die_rev; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct adm9240_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct adm9240_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &adm9240_driver; + new_client->flags = 0; + + if (kind == 0) { + kind = adm9240; + } + + if (kind < 0) { + + /* verify chip: reg address should match i2c address */ + if (adm9240_read_value(new_client, ADM9240_REG_I2C_ADDR) + != address) { + dev_err(&adapter->dev, "detect fail: address match, " + "0x%02x\n", address); + goto exit_free; + } + + /* check known chip manufacturer */ + man_id = adm9240_read_value(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); + goto exit_free; + } + + /* successful detect, print chip info */ + die_rev = adm9240_read_value(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); + } + + /* either forced or detected chip kind */ + if (kind == adm9240) { + name = "adm9240"; + } else if (kind == ds1780) { + name = "ds1780"; + } else if (kind == lm81) { + name = "lm81"; + } + + /* fill in the remaining client fields and attach */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->type = kind; + init_MUTEX(&data->update_lock); + + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + adm9240_init_client(new_client); + + /* populate sysfs filesystem */ + device_create_file(&new_client->dev, &dev_attr_in0_input); + device_create_file(&new_client->dev, &dev_attr_in0_min); + device_create_file(&new_client->dev, &dev_attr_in0_max); + device_create_file(&new_client->dev, &dev_attr_in1_input); + device_create_file(&new_client->dev, &dev_attr_in1_min); + device_create_file(&new_client->dev, &dev_attr_in1_max); + device_create_file(&new_client->dev, &dev_attr_in2_input); + device_create_file(&new_client->dev, &dev_attr_in2_min); + device_create_file(&new_client->dev, &dev_attr_in2_max); + device_create_file(&new_client->dev, &dev_attr_in3_input); + device_create_file(&new_client->dev, &dev_attr_in3_min); + device_create_file(&new_client->dev, &dev_attr_in3_max); + device_create_file(&new_client->dev, &dev_attr_in4_input); + device_create_file(&new_client->dev, &dev_attr_in4_min); + device_create_file(&new_client->dev, &dev_attr_in4_max); + device_create_file(&new_client->dev, &dev_attr_in5_input); + device_create_file(&new_client->dev, &dev_attr_in5_min); + device_create_file(&new_client->dev, &dev_attr_in5_max); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_fan1_input); + device_create_file(&new_client->dev, &dev_attr_fan1_div); + device_create_file(&new_client->dev, &dev_attr_fan1_min); + device_create_file(&new_client->dev, &dev_attr_fan2_input); + device_create_file(&new_client->dev, &dev_attr_fan2_div); + device_create_file(&new_client->dev, &dev_attr_fan2_min); + device_create_file(&new_client->dev, &dev_attr_alarms); + device_create_file(&new_client->dev, &dev_attr_aout_output); + device_create_file(&new_client->dev, &dev_attr_chassis_clear); + device_create_file(&new_client->dev, &dev_attr_cpu0_vid); + + return 0; +exit_free: + kfree(new_client); +exit: + return err; +} + +static int adm9240_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, adm9240_detect); +} + +static int adm9240_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static void adm9240_init_client(struct i2c_client *client) +{ + struct adm9240_data *data = i2c_get_clientdata(client); + u8 conf = adm9240_read_value(client, ADM9240_REG_CONFIG); + u8 mode = adm9240_read_value(client, ADM9240_REG_TEMP_CONF) & 3; + + data->vrm = i2c_which_vrm(); /* need this to report vid as mV */ + + dev_info(&client->dev, "Using VRM: %d.%d\n", data->vrm / 10, + data->vrm % 10); + + if (conf & 1) { /* measurement cycle running: report state */ + + dev_info(&client->dev, "status: config 0x%02x mode %u\n", + conf, mode); + + } else { /* cold start: open limits before starting chip */ + int i; + + for (i = 0; i < 6; i++) + { + adm9240_write_value(client, + ADM9240_REG_IN_MIN(i), 0); + adm9240_write_value(client, + ADM9240_REG_IN_MAX(i), 255); + } + adm9240_write_value(client, ADM9240_REG_FAN_MIN(0), 255); + adm9240_write_value(client, ADM9240_REG_FAN_MIN(1), 255); + adm9240_write_value(client, ADM9240_REG_TEMP_HIGH, 127); + adm9240_write_value(client, ADM9240_REG_TEMP_HYST, 127); + + /* start measurement cycle */ + adm9240_write_value(client, ADM9240_REG_CONFIG, 1); + + dev_info(&client->dev, "cold start: config was 0x%02x " + "mode %u\n", conf, mode); + } +} + +static struct adm9240_data *adm9240_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adm9240_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + /* minimum measurement cycle: 1.75 seconds */ + if (time_after(jiffies, data->last_updated_measure + (HZ * 7 / 4)) + || !data->valid) { + + for (i = 0; i < 6; i++) /* read voltages */ + { + data->in[i] = adm9240_read_value(client, + ADM9240_REG_IN(i)); + } + data->alarms = adm9240_read_value(client, + ADM9240_REG_INT(0)) | + adm9240_read_value(client, + ADM9240_REG_INT(1)) << 8; + + /* read temperature: assume temperature changes less than + * 0.5'C per two measurement cycles thus ignore possible + * but unlikely aliasing error on lsb reading. --Grant */ + data->temp = ((adm9240_read_value(client, + ADM9240_REG_TEMP) << 8) | + adm9240_read_value(client, + ADM9240_REG_TEMP_CONF)) / 128; + + for (i = 0; i < 2; i++) /* read fans */ + { + data->fan[i] = adm9240_read_value(client, + ADM9240_REG_FAN(i)); + + /* adjust fan clock divider on overflow */ + if (data->valid && data->fan[i] == 255 && + data->fan_div[i] < 3) { + + adm9240_write_fan_div(client, i, + ++data->fan_div[i]); + + /* adjust fan_min if active, but not to 0 */ + if (data->fan_min[i] < 255 && + data->fan_min[i] >= 2) + data->fan_min[i] /= 2; + } + } + data->last_updated_measure = jiffies; + } + + /* minimum config reading cycle: 300 seconds */ + if (time_after(jiffies, data->last_updated_config + (HZ * 300)) + || !data->valid) { + + for (i = 0; i < 6; i++) + { + data->in_min[i] = adm9240_read_value(client, + ADM9240_REG_IN_MIN(i)); + data->in_max[i] = adm9240_read_value(client, + ADM9240_REG_IN_MAX(i)); + } + for (i = 0; i < 2; i++) + { + data->fan_min[i] = adm9240_read_value(client, + ADM9240_REG_FAN_MIN(i)); + } + data->temp_high = adm9240_read_value(client, + ADM9240_REG_TEMP_HIGH); + data->temp_hyst = adm9240_read_value(client, + ADM9240_REG_TEMP_HYST); + + /* read fan divs and 5-bit VID */ + i = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV); + data->fan_div[0] = (i >> 4) & 3; + data->fan_div[1] = (i >> 6) & 3; + data->vid = i & 0x0f; + data->vid |= (adm9240_read_value(client, + ADM9240_REG_VID4) & 1) << 4; + /* read analog out */ + data->aout = adm9240_read_value(client, + ADM9240_REG_ANALOG_OUT); + + data->last_updated_config = jiffies; + data->valid = 1; + } + up(&data->update_lock); + return data; +} + +static int __init sensors_adm9240_init(void) +{ + return i2c_add_driver(&adm9240_driver); +} + +static void __exit sensors_adm9240_exit(void) +{ + i2c_del_driver(&adm9240_driver); +} + +MODULE_AUTHOR("Michiel Rook , " + "Grant Coady and others"); +MODULE_DESCRIPTION("ADM9240/DS1780/LM81 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_adm9240_init); +module_exit(sensors_adm9240_exit); + diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c new file mode 100644 index 000000000000..70d996d6fe0a --- /dev/null +++ b/drivers/hwmon/asb100.c @@ -0,0 +1,1065 @@ +/* + asb100.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + + Copyright (C) 2004 Mark M. Hoffman + + (derived from w83781d.c) + + Copyright (C) 1998 - 2003 Frodo Looijaard , + Philip Edelbrock , and + Mark Studebaker + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This driver supports the hardware sensor chips: Asus ASB100 and + ASB100-A "BACH". + + ASB100-A supports pwm1, while plain ASB100 does not. There is no known + way for the driver to tell which one is there. + + Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + asb100 7 3 1 4 0x31 0x0694 yes no +*/ + +#include +#include +#include +#include +#include +#include +#include +#include "lm75.h" + +/* + HISTORY: + 2003-12-29 1.0.0 Ported from lm_sensors project for kernel 2.6 +*/ +#define ASB100_VERSION "1.0.0" + +/* I2C addresses to scan */ +static unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END }; + +/* ISA addresses to scan (none) */ +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(asb100); +I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + "{bus, clientaddr, subclientaddr1, subclientaddr2}"); + +/* Voltage IN registers 0-6 */ +#define ASB100_REG_IN(nr) (0x20 + (nr)) +#define ASB100_REG_IN_MAX(nr) (0x2b + (nr * 2)) +#define ASB100_REG_IN_MIN(nr) (0x2c + (nr * 2)) + +/* FAN IN registers 1-3 */ +#define ASB100_REG_FAN(nr) (0x28 + (nr)) +#define ASB100_REG_FAN_MIN(nr) (0x3b + (nr)) + +/* TEMPERATURE registers 1-4 */ +static const u16 asb100_reg_temp[] = {0, 0x27, 0x150, 0x250, 0x17}; +static const u16 asb100_reg_temp_max[] = {0, 0x39, 0x155, 0x255, 0x18}; +static const u16 asb100_reg_temp_hyst[] = {0, 0x3a, 0x153, 0x253, 0x19}; + +#define ASB100_REG_TEMP(nr) (asb100_reg_temp[nr]) +#define ASB100_REG_TEMP_MAX(nr) (asb100_reg_temp_max[nr]) +#define ASB100_REG_TEMP_HYST(nr) (asb100_reg_temp_hyst[nr]) + +#define ASB100_REG_TEMP2_CONFIG 0x0152 +#define ASB100_REG_TEMP3_CONFIG 0x0252 + + +#define ASB100_REG_CONFIG 0x40 +#define ASB100_REG_ALARM1 0x41 +#define ASB100_REG_ALARM2 0x42 +#define ASB100_REG_SMIM1 0x43 +#define ASB100_REG_SMIM2 0x44 +#define ASB100_REG_VID_FANDIV 0x47 +#define ASB100_REG_I2C_ADDR 0x48 +#define ASB100_REG_CHIPID 0x49 +#define ASB100_REG_I2C_SUBADDR 0x4a +#define ASB100_REG_PIN 0x4b +#define ASB100_REG_IRQ 0x4c +#define ASB100_REG_BANK 0x4e +#define ASB100_REG_CHIPMAN 0x4f + +#define ASB100_REG_WCHIPID 0x58 + +/* bit 7 -> enable, bits 0-3 -> duty cycle */ +#define ASB100_REG_PWM1 0x59 + +/* CONVERSIONS + Rounding and limit checking is only done on the TO_REG variants. */ + +/* These constants are a guess, consistent w/ w83781d */ +#define ASB100_IN_MIN ( 0) +#define ASB100_IN_MAX (4080) + +/* IN: 1/1000 V (0V to 4.08V) + REG: 16mV/bit */ +static u8 IN_TO_REG(unsigned val) +{ + unsigned nval = SENSORS_LIMIT(val, ASB100_IN_MIN, ASB100_IN_MAX); + return (nval + 8) / 16; +} + +static unsigned IN_FROM_REG(u8 reg) +{ + return reg * 16; +} + +static u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == -1) + return 0; + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); +} + +static int FAN_FROM_REG(u8 val, int div) +{ + return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div); +} + +/* These constants are a guess, consistent w/ w83781d */ +#define ASB100_TEMP_MIN (-128000) +#define ASB100_TEMP_MAX ( 127000) + +/* TEMP: 0.001C/bit (-128C to +127C) + REG: 1C/bit, two's complement */ +static u8 TEMP_TO_REG(int temp) +{ + int ntemp = SENSORS_LIMIT(temp, ASB100_TEMP_MIN, ASB100_TEMP_MAX); + ntemp += (ntemp<0 ? -500 : 500); + return (u8)(ntemp / 1000); +} + +static int TEMP_FROM_REG(u8 reg) +{ + return (s8)reg * 1000; +} + +/* PWM: 0 - 255 per sensors documentation + REG: (6.25% duty cycle per bit) */ +static u8 ASB100_PWM_TO_REG(int pwm) +{ + pwm = SENSORS_LIMIT(pwm, 0, 255); + return (u8)(pwm / 16); +} + +static int ASB100_PWM_FROM_REG(u8 reg) +{ + return reg * 16; +} + +#define DIV_FROM_REG(val) (1 << (val)) + +/* FAN DIV: 1, 2, 4, or 8 (defaults to 2) + REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */ +static u8 DIV_TO_REG(long val) +{ + return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1; +} + +/* For each registered client, we need to keep some data in memory. That + data is pointed to by client->data. The structure itself is + dynamically allocated, at the same time the client itself is allocated. */ +struct asb100_data { + struct i2c_client client; + struct semaphore lock; + enum chips type; + + struct semaphore update_lock; + unsigned long last_updated; /* In jiffies */ + + /* array of 2 pointers to subclients */ + struct i2c_client *lm75[2]; + + char valid; /* !=0 if following fields are valid */ + u8 in[7]; /* Register value */ + u8 in_max[7]; /* Register value */ + u8 in_min[7]; /* Register value */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u16 temp[4]; /* Register value (0 and 3 are u8 only) */ + u16 temp_max[4]; /* Register value (0 and 3 are u8 only) */ + u16 temp_hyst[4]; /* Register value (0 and 3 are u8 only) */ + u8 fan_div[3]; /* Register encoding, right justified */ + u8 pwm; /* Register encoding */ + u8 vid; /* Register encoding, combined */ + u32 alarms; /* Register encoding, combined */ + u8 vrm; +}; + +static int asb100_read_value(struct i2c_client *client, u16 reg); +static void asb100_write_value(struct i2c_client *client, u16 reg, u16 val); + +static int asb100_attach_adapter(struct i2c_adapter *adapter); +static int asb100_detect(struct i2c_adapter *adapter, int address, int kind); +static int asb100_detach_client(struct i2c_client *client); +static struct asb100_data *asb100_update_device(struct device *dev); +static void asb100_init_client(struct i2c_client *client); + +static struct i2c_driver asb100_driver = { + .owner = THIS_MODULE, + .name = "asb100", + .id = I2C_DRIVERID_ASB100, + .flags = I2C_DF_NOTIFY, + .attach_adapter = asb100_attach_adapter, + .detach_client = asb100_detach_client, +}; + +/* 7 Voltages */ +#define show_in_reg(reg) \ +static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ +{ \ + struct asb100_data *data = asb100_update_device(dev); \ + return sprintf(buf, "%d\n", IN_FROM_REG(data->reg[nr])); \ +} + +show_in_reg(in) +show_in_reg(in_min) +show_in_reg(in_max) + +#define set_in_reg(REG, reg) \ +static ssize_t set_in_##reg(struct device *dev, const char *buf, \ + size_t count, int nr) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct asb100_data *data = i2c_get_clientdata(client); \ + unsigned long val = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->in_##reg[nr] = IN_TO_REG(val); \ + asb100_write_value(client, ASB100_REG_IN_##REG(nr), \ + data->in_##reg[nr]); \ + up(&data->update_lock); \ + return count; \ +} + +set_in_reg(MIN, min) +set_in_reg(MAX, max) + +#define sysfs_in(offset) \ +static ssize_t \ + show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in(dev, buf, offset); \ +} \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ + show_in##offset, NULL); \ +static ssize_t \ + show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in_min(dev, buf, offset); \ +} \ +static ssize_t \ + show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in_max(dev, buf, offset); \ +} \ +static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_in_min(dev, buf, count, offset); \ +} \ +static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_in_max(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in##offset##_min, set_in##offset##_min); \ +static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in##offset##_max, set_in##offset##_max); + +sysfs_in(0); +sysfs_in(1); +sysfs_in(2); +sysfs_in(3); +sysfs_in(4); +sysfs_in(5); +sysfs_in(6); + +#define device_create_file_in(client, offset) do { \ + device_create_file(&client->dev, &dev_attr_in##offset##_input); \ + device_create_file(&client->dev, &dev_attr_in##offset##_min); \ + device_create_file(&client->dev, &dev_attr_in##offset##_max); \ +} while (0) + +/* 3 Fans */ +static ssize_t show_fan(struct device *dev, char *buf, int nr) +{ + struct asb100_data *data = asb100_update_device(dev); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr]))); +} + +static ssize_t show_fan_min(struct device *dev, char *buf, int nr) +{ + struct asb100_data *data = asb100_update_device(dev); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr]))); +} + +static ssize_t show_fan_div(struct device *dev, char *buf, int nr) +{ + struct asb100_data *data = asb100_update_device(dev); + return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); +} + +static ssize_t set_fan_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct asb100_data *data = i2c_get_clientdata(client); + u32 val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); + asb100_write_value(client, ASB100_REG_FAN_MIN(nr), data->fan_min[nr]); + up(&data->update_lock); + return count; +} + +/* Note: we save and restore the fan minimum here, because its value is + determined in part by the fan divisor. This follows the principle of + least suprise; the user doesn't expect the fan minimum to change just + because the divisor changed. */ +static ssize_t set_fan_div(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct asb100_data *data = i2c_get_clientdata(client); + unsigned long min; + unsigned long val = simple_strtoul(buf, NULL, 10); + int reg; + + down(&data->update_lock); + + min = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + data->fan_div[nr] = DIV_TO_REG(val); + + switch(nr) { + case 0: /* fan 1 */ + reg = asb100_read_value(client, ASB100_REG_VID_FANDIV); + reg = (reg & 0xcf) | (data->fan_div[0] << 4); + asb100_write_value(client, ASB100_REG_VID_FANDIV, reg); + break; + + case 1: /* fan 2 */ + reg = asb100_read_value(client, ASB100_REG_VID_FANDIV); + reg = (reg & 0x3f) | (data->fan_div[1] << 6); + asb100_write_value(client, ASB100_REG_VID_FANDIV, reg); + break; + + case 2: /* fan 3 */ + reg = asb100_read_value(client, ASB100_REG_PIN); + reg = (reg & 0x3f) | (data->fan_div[2] << 6); + asb100_write_value(client, ASB100_REG_PIN, reg); + break; + } + + data->fan_min[nr] = + FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); + asb100_write_value(client, ASB100_REG_FAN_MIN(nr), data->fan_min[nr]); + + up(&data->update_lock); + + return count; +} + +#define sysfs_fan(offset) \ +static ssize_t show_fan##offset(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_min(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan##offset##_div(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_div(dev, buf, offset - 1); \ +} \ +static ssize_t set_fan##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return set_fan_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t set_fan##offset##_div(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return set_fan_div(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ + show_fan##offset, NULL); \ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan##offset##_min, set_fan##offset##_min); \ +static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ + show_fan##offset##_div, set_fan##offset##_div); + +sysfs_fan(1); +sysfs_fan(2); +sysfs_fan(3); + +#define device_create_file_fan(client, offset) do { \ + device_create_file(&client->dev, &dev_attr_fan##offset##_input); \ + device_create_file(&client->dev, &dev_attr_fan##offset##_min); \ + device_create_file(&client->dev, &dev_attr_fan##offset##_div); \ +} while (0) + +/* 4 Temp. Sensors */ +static int sprintf_temp_from_reg(u16 reg, char *buf, int nr) +{ + int ret = 0; + + switch (nr) { + case 1: case 2: + ret = sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(reg)); + break; + case 0: case 3: default: + ret = sprintf(buf, "%d\n", TEMP_FROM_REG(reg)); + break; + } + return ret; +} + +#define show_temp_reg(reg) \ +static ssize_t show_##reg(struct device *dev, char *buf, int nr) \ +{ \ + struct asb100_data *data = asb100_update_device(dev); \ + return sprintf_temp_from_reg(data->reg[nr], buf, nr); \ +} + +show_temp_reg(temp); +show_temp_reg(temp_max); +show_temp_reg(temp_hyst); + +#define set_temp_reg(REG, reg) \ +static ssize_t set_##reg(struct device *dev, const char *buf, \ + size_t count, int nr) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct asb100_data *data = i2c_get_clientdata(client); \ + unsigned long val = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + switch (nr) { \ + case 1: case 2: \ + data->reg[nr] = LM75_TEMP_TO_REG(val); \ + break; \ + case 0: case 3: default: \ + data->reg[nr] = TEMP_TO_REG(val); \ + break; \ + } \ + asb100_write_value(client, ASB100_REG_TEMP_##REG(nr+1), \ + data->reg[nr]); \ + up(&data->update_lock); \ + return count; \ +} + +set_temp_reg(MAX, temp_max); +set_temp_reg(HYST, temp_hyst); + +#define sysfs_temp(num) \ +static ssize_t show_temp##num(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp(dev, buf, num-1); \ +} \ +static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL); \ +static ssize_t show_temp_max##num(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp_max(dev, buf, num-1); \ +} \ +static ssize_t set_temp_max##num(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return set_temp_max(dev, buf, count, num-1); \ +} \ +static DEVICE_ATTR(temp##num##_max, S_IRUGO | S_IWUSR, \ + show_temp_max##num, set_temp_max##num); \ +static ssize_t show_temp_hyst##num(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp_hyst(dev, buf, num-1); \ +} \ +static ssize_t set_temp_hyst##num(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return set_temp_hyst(dev, buf, count, num-1); \ +} \ +static DEVICE_ATTR(temp##num##_max_hyst, S_IRUGO | S_IWUSR, \ + show_temp_hyst##num, set_temp_hyst##num); + +sysfs_temp(1); +sysfs_temp(2); +sysfs_temp(3); +sysfs_temp(4); + +/* VID */ +#define device_create_file_temp(client, num) do { \ + device_create_file(&client->dev, &dev_attr_temp##num##_input); \ + device_create_file(&client->dev, &dev_attr_temp##num##_max); \ + device_create_file(&client->dev, &dev_attr_temp##num##_max_hyst); \ +} while (0) + +static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct asb100_data *data = asb100_update_device(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); +} + +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); +#define device_create_file_vid(client) \ +device_create_file(&client->dev, &dev_attr_cpu0_vid) + +/* VRM */ +static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct asb100_data *data = asb100_update_device(dev); + return sprintf(buf, "%d\n", data->vrm); +} + +static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct asb100_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + data->vrm = val; + return count; +} + +/* Alarms */ +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); +#define device_create_file_vrm(client) \ +device_create_file(&client->dev, &dev_attr_vrm); + +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct asb100_data *data = asb100_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); +} + +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +#define device_create_file_alarms(client) \ +device_create_file(&client->dev, &dev_attr_alarms) + +/* 1 PWM */ +static ssize_t show_pwm1(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct asb100_data *data = asb100_update_device(dev); + return sprintf(buf, "%d\n", ASB100_PWM_FROM_REG(data->pwm & 0x0f)); +} + +static ssize_t set_pwm1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct asb100_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->pwm &= 0x80; /* keep the enable bit */ + data->pwm |= (0x0f & ASB100_PWM_TO_REG(val)); + asb100_write_value(client, ASB100_REG_PWM1, data->pwm); + up(&data->update_lock); + return count; +} + +static ssize_t show_pwm_enable1(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct asb100_data *data = asb100_update_device(dev); + return sprintf(buf, "%d\n", (data->pwm & 0x80) ? 1 : 0); +} + +static ssize_t set_pwm_enable1(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct asb100_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->pwm &= 0x0f; /* keep the duty cycle bits */ + data->pwm |= (val ? 0x80 : 0x00); + asb100_write_value(client, ASB100_REG_PWM1, data->pwm); + up(&data->update_lock); + return count; +} + +static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm1, set_pwm1); +static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, + show_pwm_enable1, set_pwm_enable1); +#define device_create_file_pwm1(client) do { \ + device_create_file(&new_client->dev, &dev_attr_pwm1); \ + device_create_file(&new_client->dev, &dev_attr_pwm1_enable); \ +} while (0) + +/* This function is called when: + asb100_driver is inserted (when this module is loaded), for each + available adapter + when a new adapter is inserted (and asb100_driver is still present) + */ +static int asb100_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, asb100_detect); +} + +static int asb100_detect_subclients(struct i2c_adapter *adapter, int address, + int kind, struct i2c_client *new_client) +{ + int i, id, err; + struct asb100_data *data = i2c_get_clientdata(new_client); + + data->lm75[0] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!(data->lm75[0])) { + err = -ENOMEM; + goto ERROR_SC_0; + } + memset(data->lm75[0], 0x00, sizeof(struct i2c_client)); + + data->lm75[1] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!(data->lm75[1])) { + err = -ENOMEM; + goto ERROR_SC_1; + } + memset(data->lm75[1], 0x00, sizeof(struct i2c_client)); + + id = i2c_adapter_id(adapter); + + if (force_subclients[0] == id && force_subclients[1] == address) { + for (i = 2; i <= 3; i++) { + if (force_subclients[i] < 0x48 || + force_subclients[i] > 0x4f) { + dev_err(&new_client->dev, "invalid subclient " + "address %d; must be 0x48-0x4f\n", + force_subclients[i]); + err = -ENODEV; + goto ERROR_SC_2; + } + } + asb100_write_value(new_client, ASB100_REG_I2C_SUBADDR, + (force_subclients[2] & 0x07) | + ((force_subclients[3] & 0x07) <<4)); + data->lm75[0]->addr = force_subclients[2]; + data->lm75[1]->addr = force_subclients[3]; + } else { + int val = asb100_read_value(new_client, ASB100_REG_I2C_SUBADDR); + data->lm75[0]->addr = 0x48 + (val & 0x07); + data->lm75[1]->addr = 0x48 + ((val >> 4) & 0x07); + } + + if(data->lm75[0]->addr == data->lm75[1]->addr) { + dev_err(&new_client->dev, "duplicate addresses 0x%x " + "for subclients\n", data->lm75[0]->addr); + err = -ENODEV; + goto ERROR_SC_2; + } + + for (i = 0; i <= 1; i++) { + i2c_set_clientdata(data->lm75[i], NULL); + data->lm75[i]->adapter = adapter; + data->lm75[i]->driver = &asb100_driver; + data->lm75[i]->flags = 0; + strlcpy(data->lm75[i]->name, "asb100 subclient", I2C_NAME_SIZE); + } + + if ((err = i2c_attach_client(data->lm75[0]))) { + dev_err(&new_client->dev, "subclient %d registration " + "at address 0x%x failed.\n", i, data->lm75[0]->addr); + goto ERROR_SC_2; + } + + if ((err = i2c_attach_client(data->lm75[1]))) { + dev_err(&new_client->dev, "subclient %d registration " + "at address 0x%x failed.\n", i, data->lm75[1]->addr); + goto ERROR_SC_3; + } + + return 0; + +/* Undo inits in case of errors */ +ERROR_SC_3: + i2c_detach_client(data->lm75[0]); +ERROR_SC_2: + kfree(data->lm75[1]); +ERROR_SC_1: + kfree(data->lm75[0]); +ERROR_SC_0: + return err; +} + +static int asb100_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int err; + struct i2c_client *new_client; + struct asb100_data *data; + + /* asb100 is SMBus only */ + if (i2c_is_isa_adapter(adapter)) { + pr_debug("asb100.o: detect failed, " + "cannot attach to legacy adapter!\n"); + err = -ENODEV; + goto ERROR0; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + pr_debug("asb100.o: detect failed, " + "smbus byte data not supported!\n"); + err = -ENODEV; + goto ERROR0; + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access asb100_{read,write}_value. */ + + if (!(data = kmalloc(sizeof(struct asb100_data), GFP_KERNEL))) { + pr_debug("asb100.o: detect failed, kmalloc failed!\n"); + err = -ENOMEM; + goto ERROR0; + } + memset(data, 0, sizeof(struct asb100_data)); + + new_client = &data->client; + init_MUTEX(&data->lock); + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &asb100_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + /* 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 = asb100_read_value(new_client, ASB100_REG_BANK); + int val2 = asb100_read_value(new_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); + err = -ENODEV; + goto ERROR1; + } + + } /* kind < 0 */ + + /* We have either had a force parameter, or we have already detected + Winbond. Put it now into bank 0 and Vendor ID High Byte */ + asb100_write_value(new_client, ASB100_REG_BANK, + (asb100_read_value(new_client, ASB100_REG_BANK) & 0x78) | 0x80); + + /* Determine the chip type. */ + if (kind <= 0) { + int val1 = asb100_read_value(new_client, ASB100_REG_WCHIPID); + int val2 = asb100_read_value(new_client, ASB100_REG_CHIPMAN); + + if ((val1 == 0x31) && (val2 == 0x06)) + kind = asb100; + else { + if (kind == 0) + dev_warn(&new_client->dev, "ignoring " + "'force' parameter for unknown chip " + "at adapter %d, address 0x%02x.\n", + i2c_adapter_id(adapter), address); + err = -ENODEV; + goto ERROR1; + } + } + + /* Fill in remaining client fields and put it into the global list */ + strlcpy(new_client->name, "asb100", I2C_NAME_SIZE); + data->type = kind; + + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR1; + + /* Attach secondary lm75 clients */ + if ((err = asb100_detect_subclients(adapter, address, kind, + new_client))) + goto ERROR2; + + /* Initialize the chip */ + asb100_init_client(new_client); + + /* A few vars need to be filled upon startup */ + data->fan_min[0] = asb100_read_value(new_client, ASB100_REG_FAN_MIN(0)); + data->fan_min[1] = asb100_read_value(new_client, ASB100_REG_FAN_MIN(1)); + data->fan_min[2] = asb100_read_value(new_client, ASB100_REG_FAN_MIN(2)); + + /* Register sysfs hooks */ + device_create_file_in(new_client, 0); + device_create_file_in(new_client, 1); + device_create_file_in(new_client, 2); + device_create_file_in(new_client, 3); + device_create_file_in(new_client, 4); + device_create_file_in(new_client, 5); + device_create_file_in(new_client, 6); + + device_create_file_fan(new_client, 1); + device_create_file_fan(new_client, 2); + device_create_file_fan(new_client, 3); + + device_create_file_temp(new_client, 1); + device_create_file_temp(new_client, 2); + device_create_file_temp(new_client, 3); + device_create_file_temp(new_client, 4); + + device_create_file_vid(new_client); + device_create_file_vrm(new_client); + + device_create_file_alarms(new_client); + + device_create_file_pwm1(new_client); + + return 0; + +ERROR2: + i2c_detach_client(new_client); +ERROR1: + kfree(data); +ERROR0: + return err; +} + +static int asb100_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "client deregistration failed; " + "client not detached.\n"); + return err; + } + + if (i2c_get_clientdata(client)==NULL) { + /* subclients */ + kfree(client); + } else { + /* main client */ + kfree(i2c_get_clientdata(client)); + } + + return 0; +} + +/* The SMBus locks itself, usually, but nothing may access the chip between + bank switches. */ +static int asb100_read_value(struct i2c_client *client, u16 reg) +{ + struct asb100_data *data = i2c_get_clientdata(client); + struct i2c_client *cl; + int res, bank; + + down(&data->lock); + + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank); + + if (bank == 0 || bank > 2) { + res = i2c_smbus_read_byte_data(client, reg & 0xff); + } else { + /* switch to subclient */ + cl = data->lm75[bank - 1]; + + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x50: /* TEMP */ + res = swab16(i2c_smbus_read_word_data (cl, 0)); + break; + case 0x52: /* CONFIG */ + res = i2c_smbus_read_byte_data(cl, 1); + break; + case 0x53: /* HYST */ + res = swab16(i2c_smbus_read_word_data (cl, 2)); + break; + case 0x55: /* MAX */ + default: + res = swab16(i2c_smbus_read_word_data (cl, 3)); + break; + } + } + + if (bank > 2) + i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0); + + up(&data->lock); + + return res; +} + +static void asb100_write_value(struct i2c_client *client, u16 reg, u16 value) +{ + struct asb100_data *data = i2c_get_clientdata(client); + struct i2c_client *cl; + int bank; + + down(&data->lock); + + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank); + + if (bank == 0 || bank > 2) { + i2c_smbus_write_byte_data(client, reg & 0xff, value & 0xff); + } else { + /* switch to subclient */ + cl = data->lm75[bank - 1]; + + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x52: /* CONFIG */ + i2c_smbus_write_byte_data(cl, 1, value & 0xff); + break; + case 0x53: /* HYST */ + i2c_smbus_write_word_data(cl, 2, swab16(value)); + break; + case 0x55: /* MAX */ + i2c_smbus_write_word_data(cl, 3, swab16(value)); + break; + } + } + + if (bank > 2) + i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0); + + up(&data->lock); +} + +static void asb100_init_client(struct i2c_client *client) +{ + struct asb100_data *data = i2c_get_clientdata(client); + int vid = 0; + + vid = asb100_read_value(client, ASB100_REG_VID_FANDIV) & 0x0f; + vid |= (asb100_read_value(client, ASB100_REG_CHIPID) & 0x01) << 4; + data->vrm = i2c_which_vrm(); + vid = vid_from_reg(vid, data->vrm); + + /* Start monitoring */ + asb100_write_value(client, ASB100_REG_CONFIG, + (asb100_read_value(client, ASB100_REG_CONFIG) & 0xf7) | 0x01); +} + +static struct asb100_data *asb100_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct asb100_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + + dev_dbg(&client->dev, "starting device update...\n"); + + /* 7 voltage inputs */ + for (i = 0; i < 7; i++) { + data->in[i] = asb100_read_value(client, + ASB100_REG_IN(i)); + data->in_min[i] = asb100_read_value(client, + ASB100_REG_IN_MIN(i)); + data->in_max[i] = asb100_read_value(client, + ASB100_REG_IN_MAX(i)); + } + + /* 3 fan inputs */ + for (i = 0; i < 3; i++) { + data->fan[i] = asb100_read_value(client, + ASB100_REG_FAN(i)); + data->fan_min[i] = asb100_read_value(client, + ASB100_REG_FAN_MIN(i)); + } + + /* 4 temperature inputs */ + for (i = 1; i <= 4; i++) { + data->temp[i-1] = asb100_read_value(client, + ASB100_REG_TEMP(i)); + data->temp_max[i-1] = asb100_read_value(client, + ASB100_REG_TEMP_MAX(i)); + data->temp_hyst[i-1] = asb100_read_value(client, + ASB100_REG_TEMP_HYST(i)); + } + + /* VID and fan divisors */ + i = asb100_read_value(client, ASB100_REG_VID_FANDIV); + data->vid = i & 0x0f; + data->vid |= (asb100_read_value(client, + ASB100_REG_CHIPID) & 0x01) << 4; + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + data->fan_div[2] = (asb100_read_value(client, + ASB100_REG_PIN) >> 6) & 0x03; + + /* PWM */ + data->pwm = asb100_read_value(client, ASB100_REG_PWM1); + + /* alarms */ + data->alarms = asb100_read_value(client, ASB100_REG_ALARM1) + + (asb100_read_value(client, ASB100_REG_ALARM2) << 8); + + data->last_updated = jiffies; + data->valid = 1; + + dev_dbg(&client->dev, "... device update complete\n"); + } + + up(&data->update_lock); + + return data; +} + +static int __init asb100_init(void) +{ + return i2c_add_driver(&asb100_driver); +} + +static void __exit asb100_exit(void) +{ + i2c_del_driver(&asb100_driver); +} + +MODULE_AUTHOR("Mark M. Hoffman "); +MODULE_DESCRIPTION("ASB100 Bach driver"); +MODULE_LICENSE("GPL"); + +module_init(asb100_init); +module_exit(asb100_exit); + diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c new file mode 100644 index 000000000000..0bcf82b4c07b --- /dev/null +++ b/drivers/hwmon/atxp1.c @@ -0,0 +1,361 @@ +/* + atxp1.c - kernel module for setting CPU VID and general purpose + I/Os using the Attansic ATXP1 chip. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("System voltages control via Attansic ATXP1"); +MODULE_VERSION("0.6.2"); +MODULE_AUTHOR("Sebastian Witt "); + +#define ATXP1_VID 0x00 +#define ATXP1_CVID 0x01 +#define ATXP1_GPIO1 0x06 +#define ATXP1_GPIO2 0x0a +#define ATXP1_VIDENA 0x20 +#define ATXP1_VIDMASK 0x1f +#define ATXP1_GPIO1MASK 0x0f + +static unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +SENSORS_INSMOD_1(atxp1); + +static int atxp1_attach_adapter(struct i2c_adapter * adapter); +static int atxp1_detach_client(struct i2c_client * client); +static struct atxp1_data * atxp1_update_device(struct device *dev); +static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind); + +static struct i2c_driver atxp1_driver = { + .owner = THIS_MODULE, + .name = "atxp1", + .flags = I2C_DF_NOTIFY, + .attach_adapter = atxp1_attach_adapter, + .detach_client = atxp1_detach_client, +}; + +struct atxp1_data { + struct i2c_client client; + struct semaphore update_lock; + unsigned long last_updated; + u8 valid; + struct { + u8 vid; /* VID output register */ + u8 cpu_vid; /* VID input from CPU */ + u8 gpio1; /* General purpose I/O register 1 */ + u8 gpio2; /* General purpose I/O register 2 */ + } reg; + u8 vrm; /* Detected CPU VRM */ +}; + +static struct atxp1_data * atxp1_update_device(struct device *dev) +{ + struct i2c_client *client; + struct atxp1_data *data; + + client = to_i2c_client(dev); + data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ) || + (jiffies < data->last_updated) || + !data->valid) { + + /* Update local register data */ + data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID); + data->reg.cpu_vid = i2c_smbus_read_byte_data(client, ATXP1_CVID); + data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1); + data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2); + + data->valid = 1; + } + + up(&data->update_lock); + + return(data); +} + +/* sys file functions for cpu0_vid */ +static ssize_t atxp1_showvcore(struct device *dev, struct device_attribute *attr, char *buf) +{ + int size; + struct atxp1_data *data; + + data = atxp1_update_device(dev); + + size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, data->vrm)); + + return size; +} + +static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct atxp1_data *data; + struct i2c_client *client; + char vid; + char cvid; + unsigned int vcore; + + client = to_i2c_client(dev); + data = atxp1_update_device(dev); + + vcore = simple_strtoul(buf, NULL, 10); + vcore /= 25; + vcore *= 25; + + /* Calculate VID */ + vid = vid_to_reg(vcore, data->vrm); + + if (vid < 0) { + dev_err(dev, "VID calculation failed.\n"); + return -1; + } + + /* If output enabled, use control register value. Otherwise original CPU VID */ + if (data->reg.vid & ATXP1_VIDENA) + cvid = data->reg.vid & ATXP1_VIDMASK; + else + cvid = data->reg.cpu_vid; + + /* Nothing changed, aborting */ + if (vid == cvid) + return count; + + dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", vcore, vid); + + /* Write every 25 mV step to increase stability */ + if (cvid > vid) { + for (; cvid >= vid; cvid--) { + i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA); + } + } + else { + for (; cvid <= vid; cvid++) { + i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA); + } + } + + data->valid = 0; + + return count; +} + +/* CPU core reference voltage + unit: millivolt +*/ +static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, atxp1_storevcore); + +/* sys file functions for GPIO1 */ +static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr, char *buf) +{ + int size; + struct atxp1_data *data; + + data = atxp1_update_device(dev); + + size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK); + + return size; +} + +static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *attr, const char*buf, size_t count) +{ + struct atxp1_data *data; + struct i2c_client *client; + unsigned int value; + + client = to_i2c_client(dev); + data = atxp1_update_device(dev); + + value = simple_strtoul(buf, NULL, 16); + + value &= ATXP1_GPIO1MASK; + + if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) { + dev_info(dev, "Writing 0x%x to GPIO1.\n", value); + + i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value); + + data->valid = 0; + } + + return count; +} + +/* GPIO1 data register + unit: Four bit as hex (e.g. 0x0f) +*/ +static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1); + +/* sys file functions for GPIO2 */ +static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr, char *buf) +{ + int size; + struct atxp1_data *data; + + data = atxp1_update_device(dev); + + size = sprintf(buf, "0x%02x\n", data->reg.gpio2); + + return size; +} + +static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct atxp1_data *data; + struct i2c_client *client; + unsigned int value; + + client = to_i2c_client(dev); + data = atxp1_update_device(dev); + + value = simple_strtoul(buf, NULL, 16) & 0xff; + + if (value != data->reg.gpio2) { + dev_info(dev, "Writing 0x%x to GPIO1.\n", value); + + i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value); + + data->valid = 0; + } + + return count; +} + +/* GPIO2 data register + unit: Eight bit as hex (e.g. 0xff) +*/ +static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2); + + +static int atxp1_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, &atxp1_detect); +}; + +static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client * new_client; + struct atxp1_data * data; + int err = 0; + u8 temp; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct atxp1_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + + memset(data, 0, sizeof(struct atxp1_data)); + new_client = &data->client; + i2c_set_clientdata(new_client, data); + + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &atxp1_driver; + new_client->flags = 0; + + /* Detect ATXP1, checking if vendor ID registers are all zero */ + if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) && + (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) && + (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) && + (i2c_smbus_read_byte_data(new_client, 0xff) == 0) )) { + + /* No vendor ID, now checking if registers 0x10,0x11 (non-existent) + * showing the same as register 0x00 */ + temp = i2c_smbus_read_byte_data(new_client, 0x00); + + if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) && + (i2c_smbus_read_byte_data(new_client, 0x11) == temp) )) + goto exit_free; + } + + /* Get VRM */ + data->vrm = i2c_which_vrm(); + + if ((data->vrm != 90) && (data->vrm != 91)) { + dev_err(&new_client->dev, "Not supporting VRM %d.%d\n", + data->vrm / 10, data->vrm % 10); + goto exit_free; + } + + strncpy(new_client->name, "atxp1", I2C_NAME_SIZE); + + data->valid = 0; + + init_MUTEX(&data->update_lock); + + err = i2c_attach_client(new_client); + + if (err) + { + dev_err(&new_client->dev, "Attach client error.\n"); + goto exit_free; + } + + device_create_file(&new_client->dev, &dev_attr_gpio1); + device_create_file(&new_client->dev, &dev_attr_gpio2); + device_create_file(&new_client->dev, &dev_attr_cpu0_vid); + + dev_info(&new_client->dev, "Using VRM: %d.%d\n", + data->vrm / 10, data->vrm % 10); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +}; + +static int atxp1_detach_client(struct i2c_client * client) +{ + int err; + + err = i2c_detach_client(client); + + if (err) + dev_err(&client->dev, "Failed to detach client.\n"); + else + kfree(i2c_get_clientdata(client)); + + return err; +}; + +static int __init atxp1_init(void) +{ + return i2c_add_driver(&atxp1_driver); +}; + +static void __exit atxp1_exit(void) +{ + i2c_del_driver(&atxp1_driver); +}; + +module_init(atxp1_init); +module_exit(atxp1_exit); diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c new file mode 100644 index 000000000000..5360d58804f6 --- /dev/null +++ b/drivers/hwmon/ds1621.c @@ -0,0 +1,341 @@ +/* + ds1621.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Christian W. Zuckschwerdt 2000-11-23 + based on lm75.c by Frodo Looijaard + Ported to Linux 2.6 by Aurelien Jarno with + the help of Jean Delvare + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include "lm75.h" + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(ds1621); +static int polarity = -1; +module_param(polarity, int, 0); +MODULE_PARM_DESC(polarity, "Output's polarity: 0 = active high, 1 = active low"); + +/* Many DS1621 constants specified below */ +/* Config register used for detection */ +/* 7 6 5 4 3 2 1 0 */ +/* |Done|THF |TLF |NVB | X | X |POL |1SHOT| */ +#define DS1621_REG_CONFIG_NVB 0x10 +#define DS1621_REG_CONFIG_POLARITY 0x02 +#define DS1621_REG_CONFIG_1SHOT 0x01 +#define DS1621_REG_CONFIG_DONE 0x80 + +/* The DS1621 registers */ +#define DS1621_REG_TEMP 0xAA /* word, RO */ +#define DS1621_REG_TEMP_MIN 0xA1 /* word, RW */ +#define DS1621_REG_TEMP_MAX 0xA2 /* word, RW */ +#define DS1621_REG_CONF 0xAC /* byte, RW */ +#define DS1621_COM_START 0xEE /* no data */ +#define DS1621_COM_STOP 0x22 /* no data */ + +/* The DS1621 configuration register */ +#define DS1621_ALARM_TEMP_HIGH 0x40 +#define DS1621_ALARM_TEMP_LOW 0x20 + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define ALARMS_FROM_REG(val) ((val) & \ + (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW)) + +/* Each client has this additional data */ +struct ds1621_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u16 temp, temp_min, temp_max; /* Register values, word */ + u8 conf; /* Register encoding, combined */ +}; + +static int ds1621_attach_adapter(struct i2c_adapter *adapter); +static int ds1621_detect(struct i2c_adapter *adapter, int address, + int kind); +static void ds1621_init_client(struct i2c_client *client); +static int ds1621_detach_client(struct i2c_client *client); +static struct ds1621_data *ds1621_update_client(struct device *dev); + +/* This is the driver that will be inserted */ +static struct i2c_driver ds1621_driver = { + .owner = THIS_MODULE, + .name = "ds1621", + .id = I2C_DRIVERID_DS1621, + .flags = I2C_DF_NOTIFY, + .attach_adapter = ds1621_attach_adapter, + .detach_client = ds1621_detach_client, +}; + +/* All registers are word-sized, except for the configuration register. + DS1621 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +static int ds1621_read_value(struct i2c_client *client, u8 reg) +{ + if (reg == DS1621_REG_CONF) + return i2c_smbus_read_byte_data(client, reg); + else + return swab16(i2c_smbus_read_word_data(client, reg)); +} + +/* All registers are word-sized, except for the configuration register. + DS1621 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if (reg == DS1621_REG_CONF) + return i2c_smbus_write_byte_data(client, reg, value); + else + return i2c_smbus_write_word_data(client, reg, swab16(value)); +} + +static void ds1621_init_client(struct i2c_client *client) +{ + int reg = ds1621_read_value(client, DS1621_REG_CONF); + /* switch to continuous conversion mode */ + reg &= ~ DS1621_REG_CONFIG_1SHOT; + + /* setup output polarity */ + if (polarity == 0) + reg &= ~DS1621_REG_CONFIG_POLARITY; + else if (polarity == 1) + reg |= DS1621_REG_CONFIG_POLARITY; + + ds1621_write_value(client, DS1621_REG_CONF, reg); + + /* start conversion */ + i2c_smbus_write_byte(client, DS1621_COM_START); +} + +#define show(value) \ +static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct ds1621_data *data = ds1621_update_client(dev); \ + return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->value)); \ +} + +show(temp); +show(temp_min); +show(temp_max); + +#define set_temp(suffix, value, reg) \ +static ssize_t set_temp_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct ds1621_data *data = ds1621_update_client(dev); \ + u16 val = LM75_TEMP_TO_REG(simple_strtoul(buf, NULL, 10)); \ + \ + down(&data->update_lock); \ + data->value = val; \ + ds1621_write_value(client, reg, data->value); \ + up(&data->update_lock); \ + return count; \ +} + +set_temp(min, temp_min, DS1621_REG_TEMP_MIN); +set_temp(max, temp_max, DS1621_REG_TEMP_MAX); + +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ds1621_data *data = ds1621_update_client(dev); + return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->conf)); +} + +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static DEVICE_ATTR(temp1_input, S_IRUGO , show_temp, NULL); +static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO , show_temp_min, set_temp_min); +static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max); + + +static int ds1621_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, ds1621_detect); +} + +/* This function is called by i2c_detect */ +int ds1621_detect(struct i2c_adapter *adapter, int address, + int kind) +{ + int conf, temp; + struct i2c_client *new_client; + struct ds1621_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_WORD_DATA + | I2C_FUNC_SMBUS_WRITE_BYTE)) + goto exit; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access ds1621_{read,write}_value. */ + if (!(data = kmalloc(sizeof(struct ds1621_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct ds1621_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &ds1621_driver; + new_client->flags = 0; + + + /* 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 = ds1621_read_value(new_client, DS1621_REG_CONF); + if (conf & DS1621_REG_CONFIG_NVB) + goto exit_free; + /* The 7 lowest bits of a temperature should always be 0. */ + temp = ds1621_read_value(new_client, DS1621_REG_TEMP); + if (temp & 0x007f) + goto exit_free; + temp = ds1621_read_value(new_client, DS1621_REG_TEMP_MIN); + if (temp & 0x007f) + goto exit_free; + temp = ds1621_read_value(new_client, DS1621_REG_TEMP_MAX); + if (temp & 0x007f) + goto exit_free; + } + + /* Determine the chip type - only one kind supported! */ + if (kind <= 0) + kind = ds1621; + + /* Fill in remaining client fields and put it into the global list */ + strlcpy(new_client->name, "ds1621", I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the DS1621 chip */ + ds1621_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_alarms); + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp1_min); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + exit_free: + kfree(data); + exit: + return err; +} + +static int ds1621_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + + return 0; +} + + +static struct ds1621_data *ds1621_update_client(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ds1621_data *data = i2c_get_clientdata(client); + u8 new_conf; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + + dev_dbg(&client->dev, "Starting ds1621 update\n"); + + data->conf = ds1621_read_value(client, DS1621_REG_CONF); + + data->temp = ds1621_read_value(client, DS1621_REG_TEMP); + + data->temp_min = ds1621_read_value(client, + DS1621_REG_TEMP_MIN); + data->temp_max = ds1621_read_value(client, + DS1621_REG_TEMP_MAX); + + /* reset alarms if necessary */ + new_conf = data->conf; + if (data->temp < data->temp_min) + new_conf &= ~DS1621_ALARM_TEMP_LOW; + if (data->temp > data->temp_max) + new_conf &= ~DS1621_ALARM_TEMP_HIGH; + if (data->conf != new_conf) + ds1621_write_value(client, DS1621_REG_CONF, + new_conf); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init ds1621_init(void) +{ + return i2c_add_driver(&ds1621_driver); +} + +static void __exit ds1621_exit(void) +{ + i2c_del_driver(&ds1621_driver); +} + + +MODULE_AUTHOR("Christian W. Zuckschwerdt "); +MODULE_DESCRIPTION("DS1621 driver"); +MODULE_LICENSE("GPL"); + +module_init(ds1621_init); +module_exit(ds1621_exit); diff --git a/drivers/hwmon/fscher.c b/drivers/hwmon/fscher.c new file mode 100644 index 000000000000..da411741c2c5 --- /dev/null +++ b/drivers/hwmon/fscher.c @@ -0,0 +1,691 @@ +/* + * fscher.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2003, 2004 Reinhard Nissl + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * fujitsu siemens hermes chip, + * module based on fscpos.c + * Copyright (C) 2000 Hermann Jung + * Copyright (C) 1998, 1999 Frodo Looijaard + * and Philip Edelbrock + */ + +#include +#include +#include +#include +#include +#include + +/* + * Addresses to scan + */ + +static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* + * Insmod parameters + */ + +SENSORS_INSMOD_1(fscher); + +/* + * The FSCHER registers + */ + +/* chip identification */ +#define FSCHER_REG_IDENT_0 0x00 +#define FSCHER_REG_IDENT_1 0x01 +#define FSCHER_REG_IDENT_2 0x02 +#define FSCHER_REG_REVISION 0x03 + +/* global control and status */ +#define FSCHER_REG_EVENT_STATE 0x04 +#define FSCHER_REG_CONTROL 0x05 + +/* watchdog */ +#define FSCHER_REG_WDOG_PRESET 0x28 +#define FSCHER_REG_WDOG_STATE 0x23 +#define FSCHER_REG_WDOG_CONTROL 0x21 + +/* fan 0 */ +#define FSCHER_REG_FAN0_MIN 0x55 +#define FSCHER_REG_FAN0_ACT 0x0e +#define FSCHER_REG_FAN0_STATE 0x0d +#define FSCHER_REG_FAN0_RIPPLE 0x0f + +/* fan 1 */ +#define FSCHER_REG_FAN1_MIN 0x65 +#define FSCHER_REG_FAN1_ACT 0x6b +#define FSCHER_REG_FAN1_STATE 0x62 +#define FSCHER_REG_FAN1_RIPPLE 0x6f + +/* fan 2 */ +#define FSCHER_REG_FAN2_MIN 0xb5 +#define FSCHER_REG_FAN2_ACT 0xbb +#define FSCHER_REG_FAN2_STATE 0xb2 +#define FSCHER_REG_FAN2_RIPPLE 0xbf + +/* voltage supervision */ +#define FSCHER_REG_VOLT_12 0x45 +#define FSCHER_REG_VOLT_5 0x42 +#define FSCHER_REG_VOLT_BATT 0x48 + +/* temperature 0 */ +#define FSCHER_REG_TEMP0_ACT 0x64 +#define FSCHER_REG_TEMP0_STATE 0x71 + +/* temperature 1 */ +#define FSCHER_REG_TEMP1_ACT 0x32 +#define FSCHER_REG_TEMP1_STATE 0x81 + +/* temperature 2 */ +#define FSCHER_REG_TEMP2_ACT 0x35 +#define FSCHER_REG_TEMP2_STATE 0x91 + +/* + * Functions declaration + */ + +static int fscher_attach_adapter(struct i2c_adapter *adapter); +static int fscher_detect(struct i2c_adapter *adapter, int address, int kind); +static int fscher_detach_client(struct i2c_client *client); +static struct fscher_data *fscher_update_device(struct device *dev); +static void fscher_init_client(struct i2c_client *client); + +static int fscher_read_value(struct i2c_client *client, u8 reg); +static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver fscher_driver = { + .owner = THIS_MODULE, + .name = "fscher", + .id = I2C_DRIVERID_FSCHER, + .flags = I2C_DF_NOTIFY, + .attach_adapter = fscher_attach_adapter, + .detach_client = fscher_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct fscher_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* register values */ + u8 revision; /* revision of chip */ + u8 global_event; /* global event status */ + u8 global_control; /* global control register */ + u8 watchdog[3]; /* watchdog */ + u8 volt[3]; /* 12, 5, battery voltage */ + u8 temp_act[3]; /* temperature */ + u8 temp_status[3]; /* status of sensor */ + u8 fan_act[3]; /* fans revolutions per second */ + u8 fan_status[3]; /* fan status */ + u8 fan_min[3]; /* fan min value for rps */ + u8 fan_ripple[3]; /* divider for rps */ +}; + +/* + * Sysfs stuff + */ + +#define sysfs_r(kind, sub, offset, reg) \ +static ssize_t show_##kind##sub (struct fscher_data *, char *, int); \ +static ssize_t show_##kind##offset##sub (struct device *, struct device_attribute *attr, char *); \ +static ssize_t show_##kind##offset##sub (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct fscher_data *data = fscher_update_device(dev); \ + return show_##kind##sub(data, buf, (offset)); \ +} + +#define sysfs_w(kind, sub, offset, reg) \ +static ssize_t set_##kind##sub (struct i2c_client *, struct fscher_data *, const char *, size_t, int, int); \ +static ssize_t set_##kind##offset##sub (struct device *, struct device_attribute *attr, const char *, size_t); \ +static ssize_t set_##kind##offset##sub (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct fscher_data *data = i2c_get_clientdata(client); \ + return set_##kind##sub(client, data, buf, count, (offset), reg); \ +} + +#define sysfs_rw_n(kind, sub, offset, reg) \ +sysfs_r(kind, sub, offset, reg) \ +sysfs_w(kind, sub, offset, reg) \ +static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, show_##kind##offset##sub, set_##kind##offset##sub); + +#define sysfs_rw(kind, sub, reg) \ +sysfs_r(kind, sub, 0, reg) \ +sysfs_w(kind, sub, 0, reg) \ +static DEVICE_ATTR(kind##sub, S_IRUGO | S_IWUSR, show_##kind##0##sub, set_##kind##0##sub); + +#define sysfs_ro_n(kind, sub, offset, reg) \ +sysfs_r(kind, sub, offset, reg) \ +static DEVICE_ATTR(kind##offset##sub, S_IRUGO, show_##kind##offset##sub, NULL); + +#define sysfs_ro(kind, sub, reg) \ +sysfs_r(kind, sub, 0, reg) \ +static DEVICE_ATTR(kind, S_IRUGO, show_##kind##0##sub, NULL); + +#define sysfs_fan(offset, reg_status, reg_min, reg_ripple, reg_act) \ +sysfs_rw_n(pwm, , offset, reg_min) \ +sysfs_rw_n(fan, _status, offset, reg_status) \ +sysfs_rw_n(fan, _div , offset, reg_ripple) \ +sysfs_ro_n(fan, _input , offset, reg_act) + +#define sysfs_temp(offset, reg_status, reg_act) \ +sysfs_rw_n(temp, _status, offset, reg_status) \ +sysfs_ro_n(temp, _input , offset, reg_act) + +#define sysfs_in(offset, reg_act) \ +sysfs_ro_n(in, _input, offset, reg_act) + +#define sysfs_revision(reg_revision) \ +sysfs_ro(revision, , reg_revision) + +#define sysfs_alarms(reg_events) \ +sysfs_ro(alarms, , reg_events) + +#define sysfs_control(reg_control) \ +sysfs_rw(control, , reg_control) + +#define sysfs_watchdog(reg_control, reg_status, reg_preset) \ +sysfs_rw(watchdog, _control, reg_control) \ +sysfs_rw(watchdog, _status , reg_status) \ +sysfs_rw(watchdog, _preset , reg_preset) + +sysfs_fan(1, FSCHER_REG_FAN0_STATE, FSCHER_REG_FAN0_MIN, + FSCHER_REG_FAN0_RIPPLE, FSCHER_REG_FAN0_ACT) +sysfs_fan(2, FSCHER_REG_FAN1_STATE, FSCHER_REG_FAN1_MIN, + FSCHER_REG_FAN1_RIPPLE, FSCHER_REG_FAN1_ACT) +sysfs_fan(3, FSCHER_REG_FAN2_STATE, FSCHER_REG_FAN2_MIN, + FSCHER_REG_FAN2_RIPPLE, FSCHER_REG_FAN2_ACT) + +sysfs_temp(1, FSCHER_REG_TEMP0_STATE, FSCHER_REG_TEMP0_ACT) +sysfs_temp(2, FSCHER_REG_TEMP1_STATE, FSCHER_REG_TEMP1_ACT) +sysfs_temp(3, FSCHER_REG_TEMP2_STATE, FSCHER_REG_TEMP2_ACT) + +sysfs_in(0, FSCHER_REG_VOLT_12) +sysfs_in(1, FSCHER_REG_VOLT_5) +sysfs_in(2, FSCHER_REG_VOLT_BATT) + +sysfs_revision(FSCHER_REG_REVISION) +sysfs_alarms(FSCHER_REG_EVENTS) +sysfs_control(FSCHER_REG_CONTROL) +sysfs_watchdog(FSCHER_REG_WDOG_CONTROL, FSCHER_REG_WDOG_STATE, FSCHER_REG_WDOG_PRESET) + +#define device_create_file_fan(client, offset) \ +do { \ + device_create_file(&client->dev, &dev_attr_fan##offset##_status); \ + device_create_file(&client->dev, &dev_attr_pwm##offset); \ + device_create_file(&client->dev, &dev_attr_fan##offset##_div); \ + device_create_file(&client->dev, &dev_attr_fan##offset##_input); \ +} while (0) + +#define device_create_file_temp(client, offset) \ +do { \ + device_create_file(&client->dev, &dev_attr_temp##offset##_status); \ + device_create_file(&client->dev, &dev_attr_temp##offset##_input); \ +} while (0) + +#define device_create_file_in(client, offset) \ +do { \ + device_create_file(&client->dev, &dev_attr_in##offset##_input); \ +} while (0) + +#define device_create_file_revision(client) \ +do { \ + device_create_file(&client->dev, &dev_attr_revision); \ +} while (0) + +#define device_create_file_alarms(client) \ +do { \ + device_create_file(&client->dev, &dev_attr_alarms); \ +} while (0) + +#define device_create_file_control(client) \ +do { \ + device_create_file(&client->dev, &dev_attr_control); \ +} while (0) + +#define device_create_file_watchdog(client) \ +do { \ + device_create_file(&client->dev, &dev_attr_watchdog_status); \ + device_create_file(&client->dev, &dev_attr_watchdog_control); \ + device_create_file(&client->dev, &dev_attr_watchdog_preset); \ +} while (0) + +/* + * Real code + */ + +static int fscher_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, fscher_detect); +} + +static int fscher_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct fscher_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + /* OK. For now, we presume we have a valid client. We now create the + * client structure, even though we cannot fill it completely yet. + * But it allows us to access i2c_smbus_read_byte_data. */ + if (!(data = kmalloc(sizeof(struct fscher_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct fscher_data)); + + /* The common I2C client data is placed right before the + * Hermes-specific data. */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &fscher_driver; + new_client->flags = 0; + + /* Do the remaining detection unless force or force_fscher parameter */ + if (kind < 0) { + if ((i2c_smbus_read_byte_data(new_client, + FSCHER_REG_IDENT_0) != 0x48) /* 'H' */ + || (i2c_smbus_read_byte_data(new_client, + FSCHER_REG_IDENT_1) != 0x45) /* 'E' */ + || (i2c_smbus_read_byte_data(new_client, + FSCHER_REG_IDENT_2) != 0x52)) /* 'R' */ + goto exit_free; + } + + /* Fill in the remaining client fields and put it into the + * global list */ + strlcpy(new_client->name, "fscher", I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + fscher_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file_revision(new_client); + device_create_file_alarms(new_client); + device_create_file_control(new_client); + device_create_file_watchdog(new_client); + + device_create_file_in(new_client, 0); + device_create_file_in(new_client, 1); + device_create_file_in(new_client, 2); + + device_create_file_fan(new_client, 1); + device_create_file_fan(new_client, 2); + device_create_file_fan(new_client, 3); + + device_create_file_temp(new_client, 1); + device_create_file_temp(new_client, 2); + device_create_file_temp(new_client, 3); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static int fscher_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static int fscher_read_value(struct i2c_client *client, u8 reg) +{ + dev_dbg(&client->dev, "read reg 0x%02x\n", reg); + + return i2c_smbus_read_byte_data(client, reg); +} + +static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + dev_dbg(&client->dev, "write reg 0x%02x, val 0x%02x\n", + reg, value); + + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new FSC Hermes. */ +static void fscher_init_client(struct i2c_client *client) +{ + struct fscher_data *data = i2c_get_clientdata(client); + + /* Read revision from chip */ + data->revision = fscher_read_value(client, FSCHER_REG_REVISION); +} + +static struct fscher_data *fscher_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fscher_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { + + dev_dbg(&client->dev, "Starting fscher update\n"); + + data->temp_act[0] = fscher_read_value(client, FSCHER_REG_TEMP0_ACT); + data->temp_act[1] = fscher_read_value(client, FSCHER_REG_TEMP1_ACT); + data->temp_act[2] = fscher_read_value(client, FSCHER_REG_TEMP2_ACT); + data->temp_status[0] = fscher_read_value(client, FSCHER_REG_TEMP0_STATE); + data->temp_status[1] = fscher_read_value(client, FSCHER_REG_TEMP1_STATE); + data->temp_status[2] = fscher_read_value(client, FSCHER_REG_TEMP2_STATE); + + data->volt[0] = fscher_read_value(client, FSCHER_REG_VOLT_12); + data->volt[1] = fscher_read_value(client, FSCHER_REG_VOLT_5); + data->volt[2] = fscher_read_value(client, FSCHER_REG_VOLT_BATT); + + data->fan_act[0] = fscher_read_value(client, FSCHER_REG_FAN0_ACT); + data->fan_act[1] = fscher_read_value(client, FSCHER_REG_FAN1_ACT); + data->fan_act[2] = fscher_read_value(client, FSCHER_REG_FAN2_ACT); + data->fan_status[0] = fscher_read_value(client, FSCHER_REG_FAN0_STATE); + data->fan_status[1] = fscher_read_value(client, FSCHER_REG_FAN1_STATE); + data->fan_status[2] = fscher_read_value(client, FSCHER_REG_FAN2_STATE); + data->fan_min[0] = fscher_read_value(client, FSCHER_REG_FAN0_MIN); + data->fan_min[1] = fscher_read_value(client, FSCHER_REG_FAN1_MIN); + data->fan_min[2] = fscher_read_value(client, FSCHER_REG_FAN2_MIN); + data->fan_ripple[0] = fscher_read_value(client, FSCHER_REG_FAN0_RIPPLE); + data->fan_ripple[1] = fscher_read_value(client, FSCHER_REG_FAN1_RIPPLE); + data->fan_ripple[2] = fscher_read_value(client, FSCHER_REG_FAN2_RIPPLE); + + data->watchdog[0] = fscher_read_value(client, FSCHER_REG_WDOG_PRESET); + data->watchdog[1] = fscher_read_value(client, FSCHER_REG_WDOG_STATE); + data->watchdog[2] = fscher_read_value(client, FSCHER_REG_WDOG_CONTROL); + + data->global_event = fscher_read_value(client, FSCHER_REG_EVENT_STATE); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + + + +#define FAN_INDEX_FROM_NUM(nr) ((nr) - 1) + +static ssize_t set_fan_status(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* bits 0..1, 3..7 reserved => mask with 0x04 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x04; + + down(&data->update_lock); + data->fan_status[FAN_INDEX_FROM_NUM(nr)] &= ~v; + fscher_write_value(client, reg, v); + up(&data->update_lock); + return count; +} + +static ssize_t show_fan_status(struct fscher_data *data, char *buf, int nr) +{ + /* bits 0..1, 3..7 reserved => mask with 0x04 */ + return sprintf(buf, "%u\n", data->fan_status[FAN_INDEX_FROM_NUM(nr)] & 0x04); +} + +static ssize_t set_pwm(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + unsigned long v = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->fan_min[FAN_INDEX_FROM_NUM(nr)] = v > 0xff ? 0xff : v; + fscher_write_value(client, reg, data->fan_min[FAN_INDEX_FROM_NUM(nr)]); + up(&data->update_lock); + return count; +} + +static ssize_t show_pwm(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", data->fan_min[FAN_INDEX_FROM_NUM(nr)]); +} + +static ssize_t set_fan_div(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* supported values: 2, 4, 8 */ + unsigned long v = simple_strtoul(buf, NULL, 10); + + switch (v) { + case 2: v = 1; break; + case 4: v = 2; break; + case 8: v = 3; break; + default: + dev_err(&client->dev, "fan_div value %ld not " + "supported. Choose one of 2, 4 or 8!\n", v); + return -EINVAL; + } + + down(&data->update_lock); + + /* bits 2..7 reserved => mask with 0x03 */ + data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] &= ~0x03; + data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] |= v; + + fscher_write_value(client, reg, data->fan_ripple[FAN_INDEX_FROM_NUM(nr)]); + up(&data->update_lock); + return count; +} + +static ssize_t show_fan_div(struct fscher_data *data, char *buf, int nr) +{ + /* bits 2..7 reserved => mask with 0x03 */ + return sprintf(buf, "%u\n", 1 << (data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] & 0x03)); +} + +#define RPM_FROM_REG(val) (val*60) + +static ssize_t show_fan_input (struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[FAN_INDEX_FROM_NUM(nr)])); +} + + + +#define TEMP_INDEX_FROM_NUM(nr) ((nr) - 1) + +static ssize_t set_temp_status(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* bits 2..7 reserved, 0 read only => mask with 0x02 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; + + down(&data->update_lock); + data->temp_status[TEMP_INDEX_FROM_NUM(nr)] &= ~v; + fscher_write_value(client, reg, v); + up(&data->update_lock); + return count; +} + +static ssize_t show_temp_status(struct fscher_data *data, char *buf, int nr) +{ + /* bits 2..7 reserved => mask with 0x03 */ + return sprintf(buf, "%u\n", data->temp_status[TEMP_INDEX_FROM_NUM(nr)] & 0x03); +} + +#define TEMP_FROM_REG(val) (((val) - 128) * 1000) + +static ssize_t show_temp_input(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[TEMP_INDEX_FROM_NUM(nr)])); +} + +/* + * The final conversion is specified in sensors.conf, as it depends on + * mainboard specific values. We export the registers contents as + * pseudo-hundredths-of-Volts (range 0V - 2.55V). Not that it makes much + * sense per se, but it minimizes the conversions count and keeps the + * values within a usual range. + */ +#define VOLT_FROM_REG(val) ((val) * 10) + +static ssize_t show_in_input(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[nr])); +} + + + +static ssize_t show_revision(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", data->revision); +} + + + +static ssize_t show_alarms(struct fscher_data *data, char *buf, int nr) +{ + /* bits 2, 5..6 reserved => mask with 0x9b */ + return sprintf(buf, "%u\n", data->global_event & 0x9b); +} + + + +static ssize_t set_control(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* bits 1..7 reserved => mask with 0x01 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x01; + + down(&data->update_lock); + data->global_control &= ~v; + fscher_write_value(client, reg, v); + up(&data->update_lock); + return count; +} + +static ssize_t show_control(struct fscher_data *data, char *buf, int nr) +{ + /* bits 1..7 reserved => mask with 0x01 */ + return sprintf(buf, "%u\n", data->global_control & 0x01); +} + + + +static ssize_t set_watchdog_control(struct i2c_client *client, struct + fscher_data *data, const char *buf, size_t count, + int nr, int reg) +{ + /* bits 0..3 reserved => mask with 0xf0 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0; + + down(&data->update_lock); + data->watchdog[2] &= ~0xf0; + data->watchdog[2] |= v; + fscher_write_value(client, reg, data->watchdog[2]); + up(&data->update_lock); + return count; +} + +static ssize_t show_watchdog_control(struct fscher_data *data, char *buf, int nr) +{ + /* bits 0..3 reserved, bit 5 write only => mask with 0xd0 */ + return sprintf(buf, "%u\n", data->watchdog[2] & 0xd0); +} + +static ssize_t set_watchdog_status(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + /* bits 0, 2..7 reserved => mask with 0x02 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; + + down(&data->update_lock); + data->watchdog[1] &= ~v; + fscher_write_value(client, reg, v); + up(&data->update_lock); + return count; +} + +static ssize_t show_watchdog_status(struct fscher_data *data, char *buf, int nr) +{ + /* bits 0, 2..7 reserved => mask with 0x02 */ + return sprintf(buf, "%u\n", data->watchdog[1] & 0x02); +} + +static ssize_t set_watchdog_preset(struct i2c_client *client, struct fscher_data *data, + const char *buf, size_t count, int nr, int reg) +{ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff; + + down(&data->update_lock); + data->watchdog[0] = v; + fscher_write_value(client, reg, data->watchdog[0]); + up(&data->update_lock); + return count; +} + +static ssize_t show_watchdog_preset(struct fscher_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", data->watchdog[0]); +} + +static int __init sensors_fscher_init(void) +{ + return i2c_add_driver(&fscher_driver); +} + +static void __exit sensors_fscher_exit(void) +{ + i2c_del_driver(&fscher_driver); +} + +MODULE_AUTHOR("Reinhard Nissl "); +MODULE_DESCRIPTION("FSC Hermes driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_fscher_init); +module_exit(sensors_fscher_exit); diff --git a/drivers/hwmon/fscpos.c b/drivers/hwmon/fscpos.c new file mode 100644 index 000000000000..3beaa6191ef4 --- /dev/null +++ b/drivers/hwmon/fscpos.c @@ -0,0 +1,641 @@ +/* + fscpos.c - Kernel module for hardware monitoring with FSC Poseidon chips + Copyright (C) 2004, 2005 Stefan Ott + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + fujitsu siemens poseidon chip, + module based on the old fscpos module by Hermann Jung and + the fscher module by Reinhard Nissl + + original module based on lm80.c + Copyright (C) 1998, 1999 Frodo Looijaard + and Philip Edelbrock + + Thanks to Jean Delvare for reviewing my code and suggesting a lot of + improvements. +*/ + +#include +#include +#include +#include +#include + +/* + * Addresses to scan + */ +static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* + * Insmod parameters + */ +SENSORS_INSMOD_1(fscpos); + +/* + * The FSCPOS registers + */ + +/* chip identification */ +#define FSCPOS_REG_IDENT_0 0x00 +#define FSCPOS_REG_IDENT_1 0x01 +#define FSCPOS_REG_IDENT_2 0x02 +#define FSCPOS_REG_REVISION 0x03 + +/* global control and status */ +#define FSCPOS_REG_EVENT_STATE 0x04 +#define FSCPOS_REG_CONTROL 0x05 + +/* watchdog */ +#define FSCPOS_REG_WDOG_PRESET 0x28 +#define FSCPOS_REG_WDOG_STATE 0x23 +#define FSCPOS_REG_WDOG_CONTROL 0x21 + +/* voltages */ +#define FSCPOS_REG_VOLT_12 0x45 +#define FSCPOS_REG_VOLT_5 0x42 +#define FSCPOS_REG_VOLT_BATT 0x48 + +/* fans - the chip does not support minimum speed for fan2 */ +static u8 FSCPOS_REG_PWM[] = { 0x55, 0x65 }; +static u8 FSCPOS_REG_FAN_ACT[] = { 0x0e, 0x6b, 0xab }; +static u8 FSCPOS_REG_FAN_STATE[] = { 0x0d, 0x62, 0xa2 }; +static u8 FSCPOS_REG_FAN_RIPPLE[] = { 0x0f, 0x6f, 0xaf }; + +/* temperatures */ +static u8 FSCPOS_REG_TEMP_ACT[] = { 0x64, 0x32, 0x35 }; +static u8 FSCPOS_REG_TEMP_STATE[] = { 0x71, 0x81, 0x91 }; + +/* + * Functions declaration + */ +static int fscpos_attach_adapter(struct i2c_adapter *adapter); +static int fscpos_detect(struct i2c_adapter *adapter, int address, int kind); +static int fscpos_detach_client(struct i2c_client *client); + +static int fscpos_read_value(struct i2c_client *client, u8 register); +static int fscpos_write_value(struct i2c_client *client, u8 register, u8 value); +static struct fscpos_data *fscpos_update_device(struct device *dev); +static void fscpos_init_client(struct i2c_client *client); + +static void reset_fan_alarm(struct i2c_client *client, int nr); + +/* + * Driver data (common to all clients) + */ +static struct i2c_driver fscpos_driver = { + .owner = THIS_MODULE, + .name = "fscpos", + .id = I2C_DRIVERID_FSCPOS, + .flags = I2C_DF_NOTIFY, + .attach_adapter = fscpos_attach_adapter, + .detach_client = fscpos_detach_client, +}; + +/* + * Client data (each client gets its own) + */ +struct fscpos_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* 0 until following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + /* register values */ + u8 revision; /* revision of chip */ + u8 global_event; /* global event status */ + u8 global_control; /* global control register */ + u8 wdog_control; /* watchdog control */ + u8 wdog_state; /* watchdog status */ + u8 wdog_preset; /* watchdog preset */ + u8 volt[3]; /* 12, 5, battery current */ + u8 temp_act[3]; /* temperature */ + u8 temp_status[3]; /* status of sensor */ + u8 fan_act[3]; /* fans revolutions per second */ + u8 fan_status[3]; /* fan status */ + u8 pwm[2]; /* fan min value for rps */ + u8 fan_ripple[3]; /* divider for rps */ +}; + +/* Temperature */ +#define TEMP_FROM_REG(val) (((val) - 128) * 1000) + +static ssize_t show_temp_input(struct fscpos_data *data, char *buf, int nr) +{ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[nr - 1])); +} + +static ssize_t show_temp_status(struct fscpos_data *data, char *buf, int nr) +{ + /* bits 2..7 reserved => mask with 0x03 */ + return sprintf(buf, "%u\n", data->temp_status[nr - 1] & 0x03); +} + +static ssize_t show_temp_reset(struct fscpos_data *data, char *buf, int nr) +{ + return sprintf(buf, "1\n"); +} + +static ssize_t set_temp_reset(struct i2c_client *client, struct fscpos_data + *data, const char *buf, size_t count, int nr, int reg) +{ + unsigned long v = simple_strtoul(buf, NULL, 10); + if (v != 1) { + dev_err(&client->dev, "temp_reset value %ld not supported. " + "Use 1 to reset the alarm!\n", v); + return -EINVAL; + } + + dev_info(&client->dev, "You used the temp_reset feature which has not " + "been proplerly tested. Please report your " + "experience to the module author.\n"); + + /* Supported value: 2 (clears the status) */ + fscpos_write_value(client, FSCPOS_REG_TEMP_STATE[nr], 2); + return count; +} + +/* Fans */ +#define RPM_FROM_REG(val) ((val) * 60) + +static ssize_t show_fan_status(struct fscpos_data *data, char *buf, int nr) +{ + /* bits 0..1, 3..7 reserved => mask with 0x04 */ + return sprintf(buf, "%u\n", data->fan_status[nr - 1] & 0x04); +} + +static ssize_t show_fan_input(struct fscpos_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[nr - 1])); +} + +static ssize_t show_fan_ripple(struct fscpos_data *data, char *buf, int nr) +{ + /* bits 2..7 reserved => mask with 0x03 */ + return sprintf(buf, "%u\n", data->fan_ripple[nr - 1] & 0x03); +} + +static ssize_t set_fan_ripple(struct i2c_client *client, struct fscpos_data + *data, const char *buf, size_t count, int nr, int reg) +{ + /* supported values: 2, 4, 8 */ + unsigned long v = simple_strtoul(buf, NULL, 10); + + switch (v) { + case 2: v = 1; break; + case 4: v = 2; break; + case 8: v = 3; break; + default: + dev_err(&client->dev, "fan_ripple value %ld not supported. " + "Must be one of 2, 4 or 8!\n", v); + return -EINVAL; + } + + down(&data->update_lock); + /* bits 2..7 reserved => mask with 0x03 */ + data->fan_ripple[nr - 1] &= ~0x03; + data->fan_ripple[nr - 1] |= v; + + fscpos_write_value(client, reg, data->fan_ripple[nr - 1]); + up(&data->update_lock); + return count; +} + +static ssize_t show_pwm(struct fscpos_data *data, char *buf, int nr) +{ + return sprintf(buf, "%u\n", data->pwm[nr - 1]); +} + +static ssize_t set_pwm(struct i2c_client *client, struct fscpos_data *data, + const char *buf, size_t count, int nr, int reg) +{ + unsigned long v = simple_strtoul(buf, NULL, 10); + + /* Range: 0..255 */ + if (v < 0) v = 0; + if (v > 255) v = 255; + + down(&data->update_lock); + data->pwm[nr - 1] = v; + fscpos_write_value(client, reg, data->pwm[nr - 1]); + up(&data->update_lock); + return count; +} + +static void reset_fan_alarm(struct i2c_client *client, int nr) +{ + fscpos_write_value(client, FSCPOS_REG_FAN_STATE[nr], 4); +} + +/* Volts */ +#define VOLT_FROM_REG(val, mult) ((val) * (mult) / 255) + +static ssize_t show_volt_12(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fscpos_data *data = fscpos_update_device(dev); + return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[0], 14200)); +} + +static ssize_t show_volt_5(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fscpos_data *data = fscpos_update_device(dev); + return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[1], 6600)); +} + +static ssize_t show_volt_batt(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fscpos_data *data = fscpos_update_device(dev); + return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[2], 3300)); +} + +/* Watchdog */ +static ssize_t show_wdog_control(struct fscpos_data *data, char *buf) +{ + /* bits 0..3 reserved, bit 6 write only => mask with 0xb0 */ + return sprintf(buf, "%u\n", data->wdog_control & 0xb0); +} + +static ssize_t set_wdog_control(struct i2c_client *client, struct fscpos_data + *data, const char *buf, size_t count, int reg) +{ + /* bits 0..3 reserved => mask with 0xf0 */ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0; + + down(&data->update_lock); + data->wdog_control &= ~0xf0; + data->wdog_control |= v; + fscpos_write_value(client, reg, data->wdog_control); + up(&data->update_lock); + return count; +} + +static ssize_t show_wdog_state(struct fscpos_data *data, char *buf) +{ + /* bits 0, 2..7 reserved => mask with 0x02 */ + return sprintf(buf, "%u\n", data->wdog_state & 0x02); +} + +static ssize_t set_wdog_state(struct i2c_client *client, struct fscpos_data + *data, const char *buf, size_t count, int reg) +{ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; + + /* Valid values: 2 (clear) */ + if (v != 2) { + dev_err(&client->dev, "wdog_state value %ld not supported. " + "Must be 2 to clear the state!\n", v); + return -EINVAL; + } + + down(&data->update_lock); + data->wdog_state &= ~v; + fscpos_write_value(client, reg, v); + up(&data->update_lock); + return count; +} + +static ssize_t show_wdog_preset(struct fscpos_data *data, char *buf) +{ + return sprintf(buf, "%u\n", data->wdog_preset); +} + +static ssize_t set_wdog_preset(struct i2c_client *client, struct fscpos_data + *data, const char *buf, size_t count, int reg) +{ + unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff; + + down(&data->update_lock); + data->wdog_preset = v; + fscpos_write_value(client, reg, data->wdog_preset); + up(&data->update_lock); + return count; +} + +/* Event */ +static ssize_t show_event(struct device *dev, struct device_attribute *attr, char *buf) +{ + /* bits 5..7 reserved => mask with 0x1f */ + struct fscpos_data *data = fscpos_update_device(dev); + return sprintf(buf, "%u\n", data->global_event & 0x9b); +} + +/* + * Sysfs stuff + */ +#define create_getter(kind, sub) \ + static ssize_t sysfs_show_##kind##sub(struct device *dev, struct device_attribute *attr, char *buf) \ + { \ + struct fscpos_data *data = fscpos_update_device(dev); \ + return show_##kind##sub(data, buf); \ + } + +#define create_getter_n(kind, offset, sub) \ + static ssize_t sysfs_show_##kind##offset##sub(struct device *dev, struct device_attribute *attr, char\ + *buf) \ + { \ + struct fscpos_data *data = fscpos_update_device(dev); \ + return show_##kind##sub(data, buf, offset); \ + } + +#define create_setter(kind, sub, reg) \ + static ssize_t sysfs_set_##kind##sub (struct device *dev, struct device_attribute *attr, const char \ + *buf, size_t count) \ + { \ + struct i2c_client *client = to_i2c_client(dev); \ + struct fscpos_data *data = i2c_get_clientdata(client); \ + return set_##kind##sub(client, data, buf, count, reg); \ + } + +#define create_setter_n(kind, offset, sub, reg) \ + static ssize_t sysfs_set_##kind##offset##sub (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + struct i2c_client *client = to_i2c_client(dev); \ + struct fscpos_data *data = i2c_get_clientdata(client); \ + return set_##kind##sub(client, data, buf, count, offset, reg);\ + } + +#define create_sysfs_device_ro(kind, sub, offset) \ + static DEVICE_ATTR(kind##offset##sub, S_IRUGO, \ + sysfs_show_##kind##offset##sub, NULL); + +#define create_sysfs_device_rw(kind, sub, offset) \ + static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, \ + sysfs_show_##kind##offset##sub, sysfs_set_##kind##offset##sub); + +#define sysfs_ro_n(kind, sub, offset) \ + create_getter_n(kind, offset, sub); \ + create_sysfs_device_ro(kind, sub, offset); + +#define sysfs_rw_n(kind, sub, offset, reg) \ + create_getter_n(kind, offset, sub); \ + create_setter_n(kind, offset, sub, reg); \ + create_sysfs_device_rw(kind, sub, offset); + +#define sysfs_rw(kind, sub, reg) \ + create_getter(kind, sub); \ + create_setter(kind, sub, reg); \ + create_sysfs_device_rw(kind, sub,); + +#define sysfs_fan_with_min(offset, reg_status, reg_ripple, reg_min) \ + sysfs_fan(offset, reg_status, reg_ripple); \ + sysfs_rw_n(pwm,, offset, reg_min); + +#define sysfs_fan(offset, reg_status, reg_ripple) \ + sysfs_ro_n(fan, _input, offset); \ + sysfs_ro_n(fan, _status, offset); \ + sysfs_rw_n(fan, _ripple, offset, reg_ripple); + +#define sysfs_temp(offset, reg_status) \ + sysfs_ro_n(temp, _input, offset); \ + sysfs_ro_n(temp, _status, offset); \ + sysfs_rw_n(temp, _reset, offset, reg_status); + +#define sysfs_watchdog(reg_wdog_preset, reg_wdog_state, reg_wdog_control) \ + sysfs_rw(wdog, _control, reg_wdog_control); \ + sysfs_rw(wdog, _preset, reg_wdog_preset); \ + sysfs_rw(wdog, _state, reg_wdog_state); + +sysfs_fan_with_min(1, FSCPOS_REG_FAN_STATE[0], FSCPOS_REG_FAN_RIPPLE[0], + FSCPOS_REG_PWM[0]); +sysfs_fan_with_min(2, FSCPOS_REG_FAN_STATE[1], FSCPOS_REG_FAN_RIPPLE[1], + FSCPOS_REG_PWM[1]); +sysfs_fan(3, FSCPOS_REG_FAN_STATE[2], FSCPOS_REG_FAN_RIPPLE[2]); + +sysfs_temp(1, FSCPOS_REG_TEMP_STATE[0]); +sysfs_temp(2, FSCPOS_REG_TEMP_STATE[1]); +sysfs_temp(3, FSCPOS_REG_TEMP_STATE[2]); + +sysfs_watchdog(FSCPOS_REG_WDOG_PRESET, FSCPOS_REG_WDOG_STATE, + FSCPOS_REG_WDOG_CONTROL); + +static DEVICE_ATTR(event, S_IRUGO, show_event, NULL); +static DEVICE_ATTR(in0_input, S_IRUGO, show_volt_12, NULL); +static DEVICE_ATTR(in1_input, S_IRUGO, show_volt_5, NULL); +static DEVICE_ATTR(in2_input, S_IRUGO, show_volt_batt, NULL); + +static int fscpos_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, fscpos_detect); +} + +int fscpos_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct fscpos_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + /* + * OK. For now, we presume we have a valid client. We now create the + * client structure, even though we cannot fill it completely yet. + * But it allows us to access fscpos_{read,write}_value. + */ + + if (!(data = kmalloc(sizeof(struct fscpos_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct fscpos_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &fscpos_driver; + new_client->flags = 0; + + /* Do the remaining detection unless force or force_fscpos parameter */ + if (kind < 0) { + if ((fscpos_read_value(new_client, FSCPOS_REG_IDENT_0) + != 0x50) /* 'P' */ + || (fscpos_read_value(new_client, FSCPOS_REG_IDENT_1) + != 0x45) /* 'E' */ + || (fscpos_read_value(new_client, FSCPOS_REG_IDENT_2) + != 0x47))/* 'G' */ + { + dev_dbg(&new_client->dev, "fscpos detection failed\n"); + goto exit_free; + } + } + + /* Fill in the remaining client fields and put it in the global list */ + strlcpy(new_client->name, "fscpos", I2C_NAME_SIZE); + + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Inizialize the fscpos chip */ + fscpos_init_client(new_client); + + /* Announce that the chip was found */ + dev_info(&new_client->dev, "Found fscpos chip, rev %u\n", data->revision); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_event); + device_create_file(&new_client->dev, &dev_attr_in0_input); + device_create_file(&new_client->dev, &dev_attr_in1_input); + device_create_file(&new_client->dev, &dev_attr_in2_input); + device_create_file(&new_client->dev, &dev_attr_wdog_control); + device_create_file(&new_client->dev, &dev_attr_wdog_preset); + device_create_file(&new_client->dev, &dev_attr_wdog_state); + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp1_status); + device_create_file(&new_client->dev, &dev_attr_temp1_reset); + device_create_file(&new_client->dev, &dev_attr_temp2_input); + device_create_file(&new_client->dev, &dev_attr_temp2_status); + device_create_file(&new_client->dev, &dev_attr_temp2_reset); + device_create_file(&new_client->dev, &dev_attr_temp3_input); + device_create_file(&new_client->dev, &dev_attr_temp3_status); + device_create_file(&new_client->dev, &dev_attr_temp3_reset); + device_create_file(&new_client->dev, &dev_attr_fan1_input); + device_create_file(&new_client->dev, &dev_attr_fan1_status); + device_create_file(&new_client->dev, &dev_attr_fan1_ripple); + device_create_file(&new_client->dev, &dev_attr_pwm1); + device_create_file(&new_client->dev, &dev_attr_fan2_input); + device_create_file(&new_client->dev, &dev_attr_fan2_status); + device_create_file(&new_client->dev, &dev_attr_fan2_ripple); + device_create_file(&new_client->dev, &dev_attr_pwm2); + device_create_file(&new_client->dev, &dev_attr_fan3_input); + device_create_file(&new_client->dev, &dev_attr_fan3_status); + device_create_file(&new_client->dev, &dev_attr_fan3_ripple); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static int fscpos_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, client" + " not detached.\n"); + return err; + } + kfree(i2c_get_clientdata(client)); + return 0; +} + +static int fscpos_read_value(struct i2c_client *client, u8 reg) +{ + dev_dbg(&client->dev, "Read reg 0x%02x\n", reg); + return i2c_smbus_read_byte_data(client, reg); +} + +static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + dev_dbg(&client->dev, "Write reg 0x%02x, val 0x%02x\n", reg, value); + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new FSCPOS chip */ +static void fscpos_init_client(struct i2c_client *client) +{ + struct fscpos_data *data = i2c_get_clientdata(client); + + /* read revision from chip */ + data->revision = fscpos_read_value(client, FSCPOS_REG_REVISION); +} + +static struct fscpos_data *fscpos_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct fscpos_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 2 * HZ) || + (jiffies < data->last_updated) || !data->valid) { + int i; + + dev_dbg(&client->dev, "Starting fscpos update\n"); + + for (i = 0; i < 3; i++) { + data->temp_act[i] = fscpos_read_value(client, + FSCPOS_REG_TEMP_ACT[i]); + data->temp_status[i] = fscpos_read_value(client, + FSCPOS_REG_TEMP_STATE[i]); + data->fan_act[i] = fscpos_read_value(client, + FSCPOS_REG_FAN_ACT[i]); + data->fan_status[i] = fscpos_read_value(client, + FSCPOS_REG_FAN_STATE[i]); + data->fan_ripple[i] = fscpos_read_value(client, + FSCPOS_REG_FAN_RIPPLE[i]); + if (i < 2) { + /* fan2_min is not supported by the chip */ + data->pwm[i] = fscpos_read_value(client, + FSCPOS_REG_PWM[i]); + } + /* reset fan status if speed is back to > 0 */ + if (data->fan_status[i] != 0 && data->fan_act[i] > 0) { + reset_fan_alarm(client, i); + } + } + + data->volt[0] = fscpos_read_value(client, FSCPOS_REG_VOLT_12); + data->volt[1] = fscpos_read_value(client, FSCPOS_REG_VOLT_5); + data->volt[2] = fscpos_read_value(client, FSCPOS_REG_VOLT_BATT); + + data->wdog_preset = fscpos_read_value(client, + FSCPOS_REG_WDOG_PRESET); + data->wdog_state = fscpos_read_value(client, + FSCPOS_REG_WDOG_STATE); + data->wdog_control = fscpos_read_value(client, + FSCPOS_REG_WDOG_CONTROL); + + data->global_event = fscpos_read_value(client, + FSCPOS_REG_EVENT_STATE); + + data->last_updated = jiffies; + data->valid = 1; + } + up(&data->update_lock); + return data; +} + +static int __init sm_fscpos_init(void) +{ + return i2c_add_driver(&fscpos_driver); +} + +static void __exit sm_fscpos_exit(void) +{ + i2c_del_driver(&fscpos_driver); +} + +MODULE_AUTHOR("Stefan Ott based on work from Hermann Jung " + ", Frodo Looijaard " + " and Philip Edelbrock "); +MODULE_DESCRIPTION("fujitsu siemens poseidon chip driver"); +MODULE_LICENSE("GPL"); + +module_init(sm_fscpos_init); +module_exit(sm_fscpos_exit); diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c new file mode 100644 index 000000000000..6bedf729dcf5 --- /dev/null +++ b/drivers/hwmon/gl518sm.c @@ -0,0 +1,604 @@ +/* + * gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 1998, 1999 Frodo Looijaard and + * Kyosti Malkki + * Copyright (C) 2004 Hong-Gunn Chew and + * Jean Delvare + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Ported to Linux 2.6 by Hong-Gunn Chew with the help of Jean Delvare + * and advice of Greg Kroah-Hartman. + * + * Notes about the port: + * Release 0x00 of the GL518SM chipset doesn't support reading of in0, + * in1 nor in2. The original driver had an ugly workaround to get them + * anyway (changing limits and watching alarms trigger and wear off). + * We did not keep that part of the original driver in the Linux 2.6 + * version, since it was making the driver significantly more complex + * with no real benefit. + * + * History: + * 2004-01-28 Original port. (Hong-Gunn Chew) + * 2004-01-31 Code review and approval. (Jean Delvare) + */ + +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_2(gl518sm_r00, gl518sm_r80); + +/* Many GL518 constants specified below */ + +/* The GL518 registers */ +#define GL518_REG_CHIP_ID 0x00 +#define GL518_REG_REVISION 0x01 +#define GL518_REG_VENDOR_ID 0x02 +#define GL518_REG_CONF 0x03 +#define GL518_REG_TEMP_IN 0x04 +#define GL518_REG_TEMP_MAX 0x05 +#define GL518_REG_TEMP_HYST 0x06 +#define GL518_REG_FAN_COUNT 0x07 +#define GL518_REG_FAN_LIMIT 0x08 +#define GL518_REG_VIN1_LIMIT 0x09 +#define GL518_REG_VIN2_LIMIT 0x0a +#define GL518_REG_VIN3_LIMIT 0x0b +#define GL518_REG_VDD_LIMIT 0x0c +#define GL518_REG_VIN3 0x0d +#define GL518_REG_MISC 0x0f +#define GL518_REG_ALARM 0x10 +#define GL518_REG_MASK 0x11 +#define GL518_REG_INT 0x12 +#define GL518_REG_VIN2 0x13 +#define GL518_REG_VIN1 0x14 +#define GL518_REG_VDD 0x15 + + +/* + * Conversions. Rounding and limit checking is only done on the TO_REG + * variants. Note that you should be a bit careful with which arguments + * these macros are called: arguments may be evaluated more than once. + * Fixing this is just not worth it. + */ + +#define RAW_FROM_REG(val) val + +#define BOOL_FROM_REG(val) ((val)?0:1) +#define BOOL_TO_REG(val) ((val)?0:1) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0? \ + (val)-500:(val)+500)/1000)+119),0,255)) +#define TEMP_FROM_REG(val) (((val) - 119) * 1000) + +static inline u8 FAN_TO_REG(long rpm, int div) +{ + long rpmdiv; + if (rpm == 0) + return 0; + rpmdiv = SENSORS_LIMIT(rpm, 1, 1920000) * div; + return SENSORS_LIMIT((960000 + rpmdiv / 2) / rpmdiv, 1, 255); +} +#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (960000/((val)*(div)))) + +#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255)) +#define IN_FROM_REG(val) ((val)*19) + +#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255)) +#define VDD_FROM_REG(val) (((val)*95+2)/4) + +#define DIV_TO_REG(val) ((val)==4?2:(val)==2?1:(val)==1?0:3) +#define DIV_FROM_REG(val) (1 << (val)) + +#define BEEP_MASK_TO_REG(val) ((val) & 0x7f & data->alarm_mask) +#define BEEP_MASK_FROM_REG(val) ((val) & 0x7f) + +/* Each client has this additional data */ +struct gl518_data { + struct i2c_client client; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 voltage_in[4]; /* Register values; [0] = VDD */ + u8 voltage_min[4]; /* Register values; [0] = VDD */ + u8 voltage_max[4]; /* Register values; [0] = VDD */ + u8 iter_voltage_in[4]; /* Register values; [0] = VDD */ + u8 fan_in[2]; + u8 fan_min[2]; + u8 fan_div[2]; /* Register encoding, shifted right */ + u8 fan_auto1; /* Boolean */ + u8 temp_in; /* Register values */ + u8 temp_max; /* Register values */ + u8 temp_hyst; /* Register values */ + u8 alarms; /* Register value */ + u8 alarm_mask; /* Register value */ + u8 beep_mask; /* Register value */ + u8 beep_enable; /* Boolean */ +}; + +static int gl518_attach_adapter(struct i2c_adapter *adapter); +static int gl518_detect(struct i2c_adapter *adapter, int address, int kind); +static void gl518_init_client(struct i2c_client *client); +static int gl518_detach_client(struct i2c_client *client); +static int gl518_read_value(struct i2c_client *client, u8 reg); +static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value); +static struct gl518_data *gl518_update_device(struct device *dev); + +/* This is the driver that will be inserted */ +static struct i2c_driver gl518_driver = { + .owner = THIS_MODULE, + .name = "gl518sm", + .id = I2C_DRIVERID_GL518, + .flags = I2C_DF_NOTIFY, + .attach_adapter = gl518_attach_adapter, + .detach_client = gl518_detach_client, +}; + +/* + * Sysfs stuff + */ + +#define show(type, suffix, value) \ +static ssize_t show_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct gl518_data *data = gl518_update_device(dev); \ + return sprintf(buf, "%d\n", type##_FROM_REG(data->value)); \ +} + +#define show_fan(suffix, value, index) \ +static ssize_t show_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct gl518_data *data = gl518_update_device(dev); \ + return sprintf(buf, "%d\n", FAN_FROM_REG(data->value[index], \ + DIV_FROM_REG(data->fan_div[index]))); \ +} + +show(TEMP, temp_input1, temp_in); +show(TEMP, temp_max1, temp_max); +show(TEMP, temp_hyst1, temp_hyst); +show(BOOL, fan_auto1, fan_auto1); +show_fan(fan_input1, fan_in, 0); +show_fan(fan_input2, fan_in, 1); +show_fan(fan_min1, fan_min, 0); +show_fan(fan_min2, fan_min, 1); +show(DIV, fan_div1, fan_div[0]); +show(DIV, fan_div2, fan_div[1]); +show(VDD, in_input0, voltage_in[0]); +show(IN, in_input1, voltage_in[1]); +show(IN, in_input2, voltage_in[2]); +show(IN, in_input3, voltage_in[3]); +show(VDD, in_min0, voltage_min[0]); +show(IN, in_min1, voltage_min[1]); +show(IN, in_min2, voltage_min[2]); +show(IN, in_min3, voltage_min[3]); +show(VDD, in_max0, voltage_max[0]); +show(IN, in_max1, voltage_max[1]); +show(IN, in_max2, voltage_max[2]); +show(IN, in_max3, voltage_max[3]); +show(RAW, alarms, alarms); +show(BOOL, beep_enable, beep_enable); +show(BEEP_MASK, beep_mask, beep_mask); + +#define set(type, suffix, value, reg) \ +static ssize_t set_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct gl518_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->value = type##_TO_REG(val); \ + gl518_write_value(client, reg, data->value); \ + up(&data->update_lock); \ + return count; \ +} + +#define set_bits(type, suffix, value, reg, mask, shift) \ +static ssize_t set_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct gl518_data *data = i2c_get_clientdata(client); \ + int regvalue; \ + unsigned long val = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + regvalue = gl518_read_value(client, reg); \ + data->value = type##_TO_REG(val); \ + regvalue = (regvalue & ~mask) | (data->value << shift); \ + gl518_write_value(client, reg, regvalue); \ + up(&data->update_lock); \ + return count; \ +} + +#define set_low(type, suffix, value, reg) \ + set_bits(type, suffix, value, reg, 0x00ff, 0) +#define set_high(type, suffix, value, reg) \ + set_bits(type, suffix, value, reg, 0xff00, 8) + +set(TEMP, temp_max1, temp_max, GL518_REG_TEMP_MAX); +set(TEMP, temp_hyst1, temp_hyst, GL518_REG_TEMP_HYST); +set_bits(BOOL, fan_auto1, fan_auto1, GL518_REG_MISC, 0x08, 3); +set_bits(DIV, fan_div1, fan_div[0], GL518_REG_MISC, 0xc0, 6); +set_bits(DIV, fan_div2, fan_div[1], GL518_REG_MISC, 0x30, 4); +set_low(VDD, in_min0, voltage_min[0], GL518_REG_VDD_LIMIT); +set_low(IN, in_min1, voltage_min[1], GL518_REG_VIN1_LIMIT); +set_low(IN, in_min2, voltage_min[2], GL518_REG_VIN2_LIMIT); +set_low(IN, in_min3, voltage_min[3], GL518_REG_VIN3_LIMIT); +set_high(VDD, in_max0, voltage_max[0], GL518_REG_VDD_LIMIT); +set_high(IN, in_max1, voltage_max[1], GL518_REG_VIN1_LIMIT); +set_high(IN, in_max2, voltage_max[2], GL518_REG_VIN2_LIMIT); +set_high(IN, in_max3, voltage_max[3], GL518_REG_VIN3_LIMIT); +set_bits(BOOL, beep_enable, beep_enable, GL518_REG_CONF, 0x04, 2); +set(BEEP_MASK, beep_mask, beep_mask, GL518_REG_ALARM); + +static ssize_t set_fan_min1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct gl518_data *data = i2c_get_clientdata(client); + int regvalue; + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT); + data->fan_min[0] = FAN_TO_REG(val, + DIV_FROM_REG(data->fan_div[0])); + regvalue = (regvalue & 0x00ff) | (data->fan_min[0] << 8); + gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue); + + data->beep_mask = gl518_read_value(client, GL518_REG_ALARM); + if (data->fan_min[0] == 0) + data->alarm_mask &= ~0x20; + else + data->alarm_mask |= 0x20; + data->beep_mask &= data->alarm_mask; + gl518_write_value(client, GL518_REG_ALARM, data->beep_mask); + + up(&data->update_lock); + return count; +} + +static ssize_t set_fan_min2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct gl518_data *data = i2c_get_clientdata(client); + int regvalue; + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT); + data->fan_min[1] = FAN_TO_REG(val, + DIV_FROM_REG(data->fan_div[1])); + regvalue = (regvalue & 0xff00) | data->fan_min[1]; + gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue); + + data->beep_mask = gl518_read_value(client, GL518_REG_ALARM); + if (data->fan_min[1] == 0) + data->alarm_mask &= ~0x40; + else + data->alarm_mask |= 0x40; + data->beep_mask &= data->alarm_mask; + gl518_write_value(client, GL518_REG_ALARM, data->beep_mask); + + up(&data->update_lock); + return count; +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); +static DEVICE_ATTR(temp1_max, S_IWUSR|S_IRUGO, show_temp_max1, set_temp_max1); +static DEVICE_ATTR(temp1_max_hyst, S_IWUSR|S_IRUGO, + show_temp_hyst1, set_temp_hyst1); +static DEVICE_ATTR(fan1_auto, S_IWUSR|S_IRUGO, show_fan_auto1, set_fan_auto1); +static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input1, NULL); +static DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input2, NULL); +static DEVICE_ATTR(fan1_min, S_IWUSR|S_IRUGO, show_fan_min1, set_fan_min1); +static DEVICE_ATTR(fan2_min, S_IWUSR|S_IRUGO, show_fan_min2, set_fan_min2); +static DEVICE_ATTR(fan1_div, S_IWUSR|S_IRUGO, show_fan_div1, set_fan_div1); +static DEVICE_ATTR(fan2_div, S_IWUSR|S_IRUGO, show_fan_div2, set_fan_div2); +static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input0, NULL); +static DEVICE_ATTR(in1_input, S_IRUGO, show_in_input1, NULL); +static DEVICE_ATTR(in2_input, S_IRUGO, show_in_input2, NULL); +static DEVICE_ATTR(in3_input, S_IRUGO, show_in_input3, NULL); +static DEVICE_ATTR(in0_min, S_IWUSR|S_IRUGO, show_in_min0, set_in_min0); +static DEVICE_ATTR(in1_min, S_IWUSR|S_IRUGO, show_in_min1, set_in_min1); +static DEVICE_ATTR(in2_min, S_IWUSR|S_IRUGO, show_in_min2, set_in_min2); +static DEVICE_ATTR(in3_min, S_IWUSR|S_IRUGO, show_in_min3, set_in_min3); +static DEVICE_ATTR(in0_max, S_IWUSR|S_IRUGO, show_in_max0, set_in_max0); +static DEVICE_ATTR(in1_max, S_IWUSR|S_IRUGO, show_in_max1, set_in_max1); +static DEVICE_ATTR(in2_max, S_IWUSR|S_IRUGO, show_in_max2, set_in_max2); +static DEVICE_ATTR(in3_max, S_IWUSR|S_IRUGO, show_in_max3, set_in_max3); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static DEVICE_ATTR(beep_enable, S_IWUSR|S_IRUGO, + show_beep_enable, set_beep_enable); +static DEVICE_ATTR(beep_mask, S_IWUSR|S_IRUGO, + show_beep_mask, set_beep_mask); + +/* + * Real code + */ + +static int gl518_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, gl518_detect); +} + +static int gl518_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int i; + struct i2c_client *new_client; + struct gl518_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto exit; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access gl518_{read,write}_value. */ + + if (!(data = kmalloc(sizeof(struct gl518_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct gl518_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &gl518_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if ((gl518_read_value(new_client, GL518_REG_CHIP_ID) != 0x80) + || (gl518_read_value(new_client, GL518_REG_CONF) & 0x80)) + goto exit_free; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = gl518_read_value(new_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), address); + goto exit_free; + } + } + + /* Fill in the remaining client fields */ + strlcpy(new_client->name, "gl518sm", I2C_NAME_SIZE); + data->type = kind; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the GL518SM chip */ + data->alarm_mask = 0xff; + data->voltage_in[0]=data->voltage_in[1]=data->voltage_in[2]=0; + gl518_init_client((struct i2c_client *) new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_in0_input); + device_create_file(&new_client->dev, &dev_attr_in1_input); + device_create_file(&new_client->dev, &dev_attr_in2_input); + device_create_file(&new_client->dev, &dev_attr_in3_input); + device_create_file(&new_client->dev, &dev_attr_in0_min); + device_create_file(&new_client->dev, &dev_attr_in1_min); + device_create_file(&new_client->dev, &dev_attr_in2_min); + device_create_file(&new_client->dev, &dev_attr_in3_min); + device_create_file(&new_client->dev, &dev_attr_in0_max); + device_create_file(&new_client->dev, &dev_attr_in1_max); + device_create_file(&new_client->dev, &dev_attr_in2_max); + device_create_file(&new_client->dev, &dev_attr_in3_max); + device_create_file(&new_client->dev, &dev_attr_fan1_auto); + device_create_file(&new_client->dev, &dev_attr_fan1_input); + device_create_file(&new_client->dev, &dev_attr_fan2_input); + device_create_file(&new_client->dev, &dev_attr_fan1_min); + device_create_file(&new_client->dev, &dev_attr_fan2_min); + device_create_file(&new_client->dev, &dev_attr_fan1_div); + device_create_file(&new_client->dev, &dev_attr_fan2_div); + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); + device_create_file(&new_client->dev, &dev_attr_alarms); + device_create_file(&new_client->dev, &dev_attr_beep_enable); + device_create_file(&new_client->dev, &dev_attr_beep_mask); + + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + +exit_free: + kfree(data); +exit: + return err; +} + + +/* Called when we have found a new GL518SM. + Note that we preserve D4:NoFan2 and D2:beep_enable. */ +static void gl518_init_client(struct i2c_client *client) +{ + /* Make sure we leave D7:Reset untouched */ + u8 regvalue = gl518_read_value(client, GL518_REG_CONF) & 0x7f; + + /* Comparator mode (D3=0), standby mode (D6=0) */ + gl518_write_value(client, GL518_REG_CONF, (regvalue &= 0x37)); + + /* Never interrupts */ + gl518_write_value(client, GL518_REG_MASK, 0x00); + + /* Clear status register (D5=1), start (D6=1) */ + gl518_write_value(client, GL518_REG_CONF, 0x20 | regvalue); + gl518_write_value(client, GL518_REG_CONF, 0x40 | regvalue); +} + +static int gl518_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + + return 0; +} + +/* Registers 0x07 to 0x0c are word-sized, others are byte-sized + GL518 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +static int gl518_read_value(struct i2c_client *client, u8 reg) +{ + if ((reg >= 0x07) && (reg <= 0x0c)) + return swab16(i2c_smbus_read_word_data(client, reg)); + else + return i2c_smbus_read_byte_data(client, reg); +} + +/* Registers 0x07 to 0x0c are word-sized, others are byte-sized + GL518 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if ((reg >= 0x07) && (reg <= 0x0c)) + return i2c_smbus_write_word_data(client, reg, swab16(value)); + else + return i2c_smbus_write_byte_data(client, reg, value); +} + +static struct gl518_data *gl518_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct gl518_data *data = i2c_get_clientdata(client); + int val; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + dev_dbg(&client->dev, "Starting gl518 update\n"); + + data->alarms = gl518_read_value(client, GL518_REG_INT); + data->beep_mask = gl518_read_value(client, GL518_REG_ALARM); + + val = gl518_read_value(client, GL518_REG_VDD_LIMIT); + data->voltage_min[0] = val & 0xff; + data->voltage_max[0] = (val >> 8) & 0xff; + val = gl518_read_value(client, GL518_REG_VIN1_LIMIT); + data->voltage_min[1] = val & 0xff; + data->voltage_max[1] = (val >> 8) & 0xff; + val = gl518_read_value(client, GL518_REG_VIN2_LIMIT); + data->voltage_min[2] = val & 0xff; + data->voltage_max[2] = (val >> 8) & 0xff; + val = gl518_read_value(client, GL518_REG_VIN3_LIMIT); + data->voltage_min[3] = val & 0xff; + data->voltage_max[3] = (val >> 8) & 0xff; + + val = gl518_read_value(client, GL518_REG_FAN_COUNT); + data->fan_in[0] = (val >> 8) & 0xff; + data->fan_in[1] = val & 0xff; + + val = gl518_read_value(client, GL518_REG_FAN_LIMIT); + data->fan_min[0] = (val >> 8) & 0xff; + data->fan_min[1] = val & 0xff; + + data->temp_in = gl518_read_value(client, GL518_REG_TEMP_IN); + data->temp_max = + gl518_read_value(client, GL518_REG_TEMP_MAX); + data->temp_hyst = + gl518_read_value(client, GL518_REG_TEMP_HYST); + + val = gl518_read_value(client, GL518_REG_MISC); + data->fan_div[0] = (val >> 6) & 0x03; + data->fan_div[1] = (val >> 4) & 0x03; + data->fan_auto1 = (val >> 3) & 0x01; + + data->alarms &= data->alarm_mask; + + val = gl518_read_value(client, GL518_REG_CONF); + data->beep_enable = (val >> 2) & 1; + + if (data->type != gl518sm_r00) { + data->voltage_in[0] = + gl518_read_value(client, GL518_REG_VDD); + data->voltage_in[1] = + gl518_read_value(client, GL518_REG_VIN1); + data->voltage_in[2] = + gl518_read_value(client, GL518_REG_VIN2); + } + data->voltage_in[3] = + gl518_read_value(client, GL518_REG_VIN3); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_gl518sm_init(void) +{ + return i2c_add_driver(&gl518_driver); +} + +static void __exit sensors_gl518sm_exit(void) +{ + i2c_del_driver(&gl518_driver); +} + +MODULE_AUTHOR("Frodo Looijaard , " + "Kyosti Malkki and " + "Hong-Gunn Chew "); +MODULE_DESCRIPTION("GL518SM driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_gl518sm_init); +module_exit(sensors_gl518sm_exit); diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c new file mode 100644 index 000000000000..a13a504f5bfa --- /dev/null +++ b/drivers/hwmon/gl520sm.c @@ -0,0 +1,769 @@ +/* + gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard , + Kyösti Mälkki + Copyright (c) 2005 Maarten Deprez + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include +#include +#include +#include + +/* Type of the extra sensor */ +static unsigned short extra_sensor_type; +module_param(extra_sensor_type, ushort, 0); +MODULE_PARM_DESC(extra_sensor_type, "Type of extra sensor (0=autodetect, 1=temperature, 2=voltage)"); + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(gl520sm); + +/* Many GL520 constants specified below +One of the inputs can be configured as either temp or voltage. +That's why _TEMP2 and _IN4 access the same register +*/ + +/* The GL520 registers */ +#define GL520_REG_CHIP_ID 0x00 +#define GL520_REG_REVISION 0x01 +#define GL520_REG_CONF 0x03 +#define GL520_REG_MASK 0x11 + +#define GL520_REG_VID_INPUT 0x02 + +#define GL520_REG_IN0_INPUT 0x15 +#define GL520_REG_IN0_LIMIT 0x0c +#define GL520_REG_IN0_MIN GL520_REG_IN0_LIMIT +#define GL520_REG_IN0_MAX GL520_REG_IN0_LIMIT + +#define GL520_REG_IN1_INPUT 0x14 +#define GL520_REG_IN1_LIMIT 0x09 +#define GL520_REG_IN1_MIN GL520_REG_IN1_LIMIT +#define GL520_REG_IN1_MAX GL520_REG_IN1_LIMIT + +#define GL520_REG_IN2_INPUT 0x13 +#define GL520_REG_IN2_LIMIT 0x0a +#define GL520_REG_IN2_MIN GL520_REG_IN2_LIMIT +#define GL520_REG_IN2_MAX GL520_REG_IN2_LIMIT + +#define GL520_REG_IN3_INPUT 0x0d +#define GL520_REG_IN3_LIMIT 0x0b +#define GL520_REG_IN3_MIN GL520_REG_IN3_LIMIT +#define GL520_REG_IN3_MAX GL520_REG_IN3_LIMIT + +#define GL520_REG_IN4_INPUT 0x0e +#define GL520_REG_IN4_MAX 0x17 +#define GL520_REG_IN4_MIN 0x18 + +#define GL520_REG_TEMP1_INPUT 0x04 +#define GL520_REG_TEMP1_MAX 0x05 +#define GL520_REG_TEMP1_MAX_HYST 0x06 + +#define GL520_REG_TEMP2_INPUT 0x0e +#define GL520_REG_TEMP2_MAX 0x17 +#define GL520_REG_TEMP2_MAX_HYST 0x18 + +#define GL520_REG_FAN_INPUT 0x07 +#define GL520_REG_FAN_MIN 0x08 +#define GL520_REG_FAN_DIV 0x0f +#define GL520_REG_FAN_OFF GL520_REG_FAN_DIV + +#define GL520_REG_ALARMS 0x12 +#define GL520_REG_BEEP_MASK 0x10 +#define GL520_REG_BEEP_ENABLE GL520_REG_CONF + +/* + * Function declarations + */ + +static int gl520_attach_adapter(struct i2c_adapter *adapter); +static int gl520_detect(struct i2c_adapter *adapter, int address, int kind); +static void gl520_init_client(struct i2c_client *client); +static int gl520_detach_client(struct i2c_client *client); +static int gl520_read_value(struct i2c_client *client, u8 reg); +static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value); +static struct gl520_data *gl520_update_device(struct device *dev); + +/* Driver data */ +static struct i2c_driver gl520_driver = { + .owner = THIS_MODULE, + .name = "gl520sm", + .id = I2C_DRIVERID_GL520, + .flags = I2C_DF_NOTIFY, + .attach_adapter = gl520_attach_adapter, + .detach_client = gl520_detach_client, +}; + +/* Client data */ +struct gl520_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* zero until the following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + u8 vid; + u8 vrm; + u8 in_input[5]; /* [0] = VVD */ + u8 in_min[5]; /* [0] = VDD */ + u8 in_max[5]; /* [0] = VDD */ + u8 fan_input[2]; + u8 fan_min[2]; + u8 fan_div[2]; + u8 fan_off; + u8 temp_input[2]; + u8 temp_max[2]; + u8 temp_max_hyst[2]; + u8 alarms; + u8 beep_enable; + u8 beep_mask; + u8 alarm_mask; + u8 two_temps; +}; + +/* + * Sysfs stuff + */ + +#define sysfs_r(type, n, item, reg) \ +static ssize_t get_##type##item (struct gl520_data *, char *, int); \ +static ssize_t get_##type##n##item (struct device *, struct device_attribute *attr, char *); \ +static ssize_t get_##type##n##item (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct gl520_data *data = gl520_update_device(dev); \ + return get_##type##item(data, buf, (n)); \ +} + +#define sysfs_w(type, n, item, reg) \ +static ssize_t set_##type##item (struct i2c_client *, struct gl520_data *, const char *, size_t, int, int); \ +static ssize_t set_##type##n##item (struct device *, struct device_attribute *attr, const char *, size_t); \ +static ssize_t set_##type##n##item (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct gl520_data *data = i2c_get_clientdata(client); \ + return set_##type##item(client, data, buf, count, (n), reg); \ +} + +#define sysfs_rw_n(type, n, item, reg) \ +sysfs_r(type, n, item, reg) \ +sysfs_w(type, n, item, reg) \ +static DEVICE_ATTR(type##n##item, S_IRUGO | S_IWUSR, get_##type##n##item, set_##type##n##item); + +#define sysfs_ro_n(type, n, item, reg) \ +sysfs_r(type, n, item, reg) \ +static DEVICE_ATTR(type##n##item, S_IRUGO, get_##type##n##item, NULL); + +#define sysfs_rw(type, item, reg) \ +sysfs_r(type, 0, item, reg) \ +sysfs_w(type, 0, item, reg) \ +static DEVICE_ATTR(type##item, S_IRUGO | S_IWUSR, get_##type##0##item, set_##type##0##item); + +#define sysfs_ro(type, item, reg) \ +sysfs_r(type, 0, item, reg) \ +static DEVICE_ATTR(type##item, S_IRUGO, get_##type##0##item, NULL); + + +#define sysfs_vid(n) \ +sysfs_ro_n(cpu, n, _vid, GL520_REG_VID_INPUT) + +#define device_create_file_vid(client, n) \ +device_create_file(&client->dev, &dev_attr_cpu##n##_vid) + +#define sysfs_in(n) \ +sysfs_ro_n(in, n, _input, GL520_REG_IN##n##INPUT) \ +sysfs_rw_n(in, n, _min, GL520_REG_IN##n##_MIN) \ +sysfs_rw_n(in, n, _max, GL520_REG_IN##n##_MAX) \ + +#define device_create_file_in(client, n) \ +({device_create_file(&client->dev, &dev_attr_in##n##_input); \ +device_create_file(&client->dev, &dev_attr_in##n##_min); \ +device_create_file(&client->dev, &dev_attr_in##n##_max);}) + +#define sysfs_fan(n) \ +sysfs_ro_n(fan, n, _input, GL520_REG_FAN_INPUT) \ +sysfs_rw_n(fan, n, _min, GL520_REG_FAN_MIN) \ +sysfs_rw_n(fan, n, _div, GL520_REG_FAN_DIV) + +#define device_create_file_fan(client, n) \ +({device_create_file(&client->dev, &dev_attr_fan##n##_input); \ +device_create_file(&client->dev, &dev_attr_fan##n##_min); \ +device_create_file(&client->dev, &dev_attr_fan##n##_div);}) + +#define sysfs_fan_off(n) \ +sysfs_rw_n(fan, n, _off, GL520_REG_FAN_OFF) \ + +#define device_create_file_fan_off(client, n) \ +device_create_file(&client->dev, &dev_attr_fan##n##_off) + +#define sysfs_temp(n) \ +sysfs_ro_n(temp, n, _input, GL520_REG_TEMP##n##_INPUT) \ +sysfs_rw_n(temp, n, _max, GL520_REG_TEMP##n##_MAX) \ +sysfs_rw_n(temp, n, _max_hyst, GL520_REG_TEMP##n##_MAX_HYST) + +#define device_create_file_temp(client, n) \ +({device_create_file(&client->dev, &dev_attr_temp##n##_input); \ +device_create_file(&client->dev, &dev_attr_temp##n##_max); \ +device_create_file(&client->dev, &dev_attr_temp##n##_max_hyst);}) + +#define sysfs_alarms() \ +sysfs_ro(alarms, , GL520_REG_ALARMS) \ +sysfs_rw(beep_enable, , GL520_REG_BEEP_ENABLE) \ +sysfs_rw(beep_mask, , GL520_REG_BEEP_MASK) + +#define device_create_file_alarms(client) \ +({device_create_file(&client->dev, &dev_attr_alarms); \ +device_create_file(&client->dev, &dev_attr_beep_enable); \ +device_create_file(&client->dev, &dev_attr_beep_mask);}) + + +sysfs_vid(0) + +sysfs_in(0) +sysfs_in(1) +sysfs_in(2) +sysfs_in(3) +sysfs_in(4) + +sysfs_fan(1) +sysfs_fan(2) +sysfs_fan_off(1) + +sysfs_temp(1) +sysfs_temp(2) + +sysfs_alarms() + + +static ssize_t get_cpu_vid(struct gl520_data *data, char *buf, int n) +{ + return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm)); +} + +#define VDD_FROM_REG(val) (((val)*95+2)/4) +#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255)) + +#define IN_FROM_REG(val) ((val)*19) +#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255)) + +static ssize_t get_in_input(struct gl520_data *data, char *buf, int n) +{ + u8 r = data->in_input[n]; + + if (n == 0) + return sprintf(buf, "%d\n", VDD_FROM_REG(r)); + else + return sprintf(buf, "%d\n", IN_FROM_REG(r)); +} + +static ssize_t get_in_min(struct gl520_data *data, char *buf, int n) +{ + u8 r = data->in_min[n]; + + if (n == 0) + return sprintf(buf, "%d\n", VDD_FROM_REG(r)); + else + return sprintf(buf, "%d\n", IN_FROM_REG(r)); +} + +static ssize_t get_in_max(struct gl520_data *data, char *buf, int n) +{ + u8 r = data->in_max[n]; + + if (n == 0) + return sprintf(buf, "%d\n", VDD_FROM_REG(r)); + else + return sprintf(buf, "%d\n", IN_FROM_REG(r)); +} + +static ssize_t set_in_min(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) +{ + long v = simple_strtol(buf, NULL, 10); + u8 r; + + down(&data->update_lock); + + if (n == 0) + r = VDD_TO_REG(v); + else + r = IN_TO_REG(v); + + data->in_min[n] = r; + + if (n < 4) + gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff) | r); + else + gl520_write_value(client, reg, r); + + up(&data->update_lock); + return count; +} + +static ssize_t set_in_max(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) +{ + long v = simple_strtol(buf, NULL, 10); + u8 r; + + if (n == 0) + r = VDD_TO_REG(v); + else + r = IN_TO_REG(v); + + down(&data->update_lock); + + data->in_max[n] = r; + + if (n < 4) + gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff00) | (r << 8)); + else + gl520_write_value(client, reg, r); + + up(&data->update_lock); + return count; +} + +#define DIV_FROM_REG(val) (1 << (val)) +#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (480000/((val) << (div)))) +#define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255)); + +static ssize_t get_fan_input(struct gl520_data *data, char *buf, int n) +{ + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_input[n - 1], data->fan_div[n - 1])); +} + +static ssize_t get_fan_min(struct gl520_data *data, char *buf, int n) +{ + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[n - 1], data->fan_div[n - 1])); +} + +static ssize_t get_fan_div(struct gl520_data *data, char *buf, int n) +{ + return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[n - 1])); +} + +static ssize_t get_fan_off(struct gl520_data *data, char *buf, int n) +{ + return sprintf(buf, "%d\n", data->fan_off); +} + +static ssize_t set_fan_min(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) +{ + unsigned long v = simple_strtoul(buf, NULL, 10); + u8 r; + + down(&data->update_lock); + r = FAN_TO_REG(v, data->fan_div[n - 1]); + data->fan_min[n - 1] = r; + + if (n == 1) + gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff00) | (r << 8)); + else + gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff) | r); + + data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK); + if (data->fan_min[n - 1] == 0) + data->alarm_mask &= (n == 1) ? ~0x20 : ~0x40; + else + data->alarm_mask |= (n == 1) ? 0x20 : 0x40; + data->beep_mask &= data->alarm_mask; + gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask); + + up(&data->update_lock); + return count; +} + +static ssize_t set_fan_div(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) +{ + unsigned long v = simple_strtoul(buf, NULL, 10); + u8 r; + + switch (v) { + case 1: r = 0; break; + case 2: r = 1; break; + case 4: r = 2; break; + case 8: r = 3; break; + default: + dev_err(&client->dev, "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", v); + return -EINVAL; + } + + down(&data->update_lock); + data->fan_div[n - 1] = r; + + if (n == 1) + gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xc0) | (r << 6)); + else + gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x30) | (r << 4)); + + up(&data->update_lock); + return count; +} + +static ssize_t set_fan_off(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) +{ + u8 r = simple_strtoul(buf, NULL, 10)?1:0; + + down(&data->update_lock); + data->fan_off = r; + gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x0c) | (r << 2)); + up(&data->update_lock); + return count; +} + +#define TEMP_FROM_REG(val) (((val) - 130) * 1000) +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-500:(val)+500) / 1000)+130),0,255)) + +static ssize_t get_temp_input(struct gl520_data *data, char *buf, int n) +{ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_input[n - 1])); +} + +static ssize_t get_temp_max(struct gl520_data *data, char *buf, int n) +{ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[n - 1])); +} + +static ssize_t get_temp_max_hyst(struct gl520_data *data, char *buf, int n) +{ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max_hyst[n - 1])); +} + +static ssize_t set_temp_max(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) +{ + long v = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_max[n - 1] = TEMP_TO_REG(v);; + gl520_write_value(client, reg, data->temp_max[n - 1]); + up(&data->update_lock); + return count; +} + +static ssize_t set_temp_max_hyst(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) +{ + long v = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_max_hyst[n - 1] = TEMP_TO_REG(v); + gl520_write_value(client, reg, data->temp_max_hyst[n - 1]); + up(&data->update_lock); + return count; +} + +static ssize_t get_alarms(struct gl520_data *data, char *buf, int n) +{ + return sprintf(buf, "%d\n", data->alarms); +} + +static ssize_t get_beep_enable(struct gl520_data *data, char *buf, int n) +{ + return sprintf(buf, "%d\n", data->beep_enable); +} + +static ssize_t get_beep_mask(struct gl520_data *data, char *buf, int n) +{ + return sprintf(buf, "%d\n", data->beep_mask); +} + +static ssize_t set_beep_enable(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) +{ + u8 r = simple_strtoul(buf, NULL, 10)?0:1; + + down(&data->update_lock); + data->beep_enable = !r; + gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x04) | (r << 2)); + up(&data->update_lock); + return count; +} + +static ssize_t set_beep_mask(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) +{ + u8 r = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + r &= data->alarm_mask; + data->beep_mask = r; + gl520_write_value(client, reg, r); + up(&data->update_lock); + return count; +} + + +/* + * Real code + */ + +static int gl520_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, gl520_detect); +} + +static int gl520_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct gl520_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto exit; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access gl520_{read,write}_value. */ + + if (!(data = kmalloc(sizeof(struct gl520_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct gl520_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &gl520_driver; + new_client->flags = 0; + + /* Determine the chip type. */ + if (kind < 0) { + if ((gl520_read_value(new_client, GL520_REG_CHIP_ID) != 0x20) || + ((gl520_read_value(new_client, GL520_REG_REVISION) & 0x7f) != 0x00) || + ((gl520_read_value(new_client, GL520_REG_CONF) & 0x80) != 0x00)) { + dev_dbg(&new_client->dev, "Unknown chip type, skipping\n"); + goto exit_free; + } + } + + /* Fill in the remaining client fields */ + strlcpy(new_client->name, "gl520sm", I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the GL520SM chip */ + gl520_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file_vid(new_client, 0); + + device_create_file_in(new_client, 0); + device_create_file_in(new_client, 1); + device_create_file_in(new_client, 2); + device_create_file_in(new_client, 3); + if (!data->two_temps) + device_create_file_in(new_client, 4); + + device_create_file_fan(new_client, 1); + device_create_file_fan(new_client, 2); + device_create_file_fan_off(new_client, 1); + + device_create_file_temp(new_client, 1); + if (data->two_temps) + device_create_file_temp(new_client, 2); + + device_create_file_alarms(new_client); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + + +/* Called when we have found a new GL520SM. */ +static void gl520_init_client(struct i2c_client *client) +{ + struct gl520_data *data = i2c_get_clientdata(client); + u8 oldconf, conf; + + conf = oldconf = gl520_read_value(client, GL520_REG_CONF); + + data->alarm_mask = 0xff; + data->vrm = i2c_which_vrm(); + + if (extra_sensor_type == 1) + conf &= ~0x10; + else if (extra_sensor_type == 2) + conf |= 0x10; + data->two_temps = !(conf & 0x10); + + /* If IRQ# is disabled, we can safely force comparator mode */ + if (!(conf & 0x20)) + conf &= 0xf7; + + /* Enable monitoring if needed */ + conf |= 0x40; + + if (conf != oldconf) + gl520_write_value(client, GL520_REG_CONF, conf); + + gl520_update_device(&(client->dev)); + + if (data->fan_min[0] == 0) + data->alarm_mask &= ~0x20; + if (data->fan_min[1] == 0) + data->alarm_mask &= ~0x40; + + data->beep_mask &= data->alarm_mask; + gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask); +} + +static int gl520_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + + +/* Registers 0x07 to 0x0c are word-sized, others are byte-sized + GL520 uses a high-byte first convention */ +static int gl520_read_value(struct i2c_client *client, u8 reg) +{ + if ((reg >= 0x07) && (reg <= 0x0c)) + return swab16(i2c_smbus_read_word_data(client, reg)); + else + return i2c_smbus_read_byte_data(client, reg); +} + +static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if ((reg >= 0x07) && (reg <= 0x0c)) + return i2c_smbus_write_word_data(client, reg, swab16(value)); + else + return i2c_smbus_write_byte_data(client, reg, value); +} + + +static struct gl520_data *gl520_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct gl520_data *data = i2c_get_clientdata(client); + int val; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 2 * HZ) || + (jiffies < data->last_updated) || !data->valid) { + + dev_dbg(&client->dev, "Starting gl520sm update\n"); + + data->alarms = gl520_read_value(client, GL520_REG_ALARMS); + data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK); + data->vid = gl520_read_value(client, GL520_REG_VID_INPUT) & 0x1f; + + val = gl520_read_value(client, GL520_REG_IN0_LIMIT); + data->in_min[0] = val & 0xff; + data->in_max[0] = (val >> 8) & 0xff; + val = gl520_read_value(client, GL520_REG_IN1_LIMIT); + data->in_min[1] = val & 0xff; + data->in_max[1] = (val >> 8) & 0xff; + val = gl520_read_value(client, GL520_REG_IN2_LIMIT); + data->in_min[2] = val & 0xff; + data->in_max[2] = (val >> 8) & 0xff; + val = gl520_read_value(client, GL520_REG_IN3_LIMIT); + data->in_min[3] = val & 0xff; + data->in_max[3] = (val >> 8) & 0xff; + + val = gl520_read_value(client, GL520_REG_FAN_INPUT); + data->fan_input[0] = (val >> 8) & 0xff; + data->fan_input[1] = val & 0xff; + + val = gl520_read_value(client, GL520_REG_FAN_MIN); + data->fan_min[0] = (val >> 8) & 0xff; + data->fan_min[1] = val & 0xff; + + data->temp_input[0] = gl520_read_value(client, GL520_REG_TEMP1_INPUT); + data->temp_max[0] = gl520_read_value(client, GL520_REG_TEMP1_MAX); + data->temp_max_hyst[0] = gl520_read_value(client, GL520_REG_TEMP1_MAX_HYST); + + val = gl520_read_value(client, GL520_REG_FAN_DIV); + data->fan_div[0] = (val >> 6) & 0x03; + data->fan_div[1] = (val >> 4) & 0x03; + data->fan_off = (val >> 2) & 0x01; + + data->alarms &= data->alarm_mask; + + val = gl520_read_value(client, GL520_REG_CONF); + data->beep_enable = !((val >> 2) & 1); + + data->in_input[0] = gl520_read_value(client, GL520_REG_IN0_INPUT); + data->in_input[1] = gl520_read_value(client, GL520_REG_IN1_INPUT); + data->in_input[2] = gl520_read_value(client, GL520_REG_IN2_INPUT); + data->in_input[3] = gl520_read_value(client, GL520_REG_IN3_INPUT); + + /* Temp1 and Vin4 are the same input */ + if (data->two_temps) { + data->temp_input[1] = gl520_read_value(client, GL520_REG_TEMP2_INPUT); + data->temp_max[1] = gl520_read_value(client, GL520_REG_TEMP2_MAX); + data->temp_max_hyst[1] = gl520_read_value(client, GL520_REG_TEMP2_MAX_HYST); + } else { + data->in_input[4] = gl520_read_value(client, GL520_REG_IN4_INPUT); + data->in_min[4] = gl520_read_value(client, GL520_REG_IN4_MIN); + data->in_max[4] = gl520_read_value(client, GL520_REG_IN4_MAX); + } + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + + +static int __init sensors_gl520sm_init(void) +{ + return i2c_add_driver(&gl520_driver); +} + +static void __exit sensors_gl520sm_exit(void) +{ + i2c_del_driver(&gl520_driver); +} + + +MODULE_AUTHOR("Frodo Looijaard , " + "Kyösti Mälkki , " + "Maarten Deprez "); +MODULE_DESCRIPTION("GL520SM driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_gl520sm_init); +module_exit(sensors_gl520sm_exit); diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c new file mode 100644 index 000000000000..db20c9e47393 --- /dev/null +++ b/drivers/hwmon/it87.c @@ -0,0 +1,1184 @@ +/* + it87.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring. + + Supports: IT8705F Super I/O chip w/LPC interface & SMBus + IT8712F Super I/O chip w/LPC interface & SMBus + Sis950 A clone of the IT8705F + + Copyright (C) 2001 Chris Gauthron + Largely inspired by lm78.c of the same package + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + djg@pdp8.net David Gesswein 7/18/01 + Modified to fix bug with not all alarms enabled. + Added ability to read battery voltage and select temperature sensor + type at module load time. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, + 0x2e, 0x2f, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_2(it87, it8712); + +#define REG 0x2e /* The register to read/write */ +#define DEV 0x07 /* Register: Logical device select */ +#define VAL 0x2f /* The value to read/write */ +#define PME 0x04 /* The device with the fan registers in it */ +#define DEVID 0x20 /* Register: Device ID */ +#define DEVREV 0x22 /* Register: Device Revision */ + +static inline int +superio_inb(int reg) +{ + outb(reg, REG); + return inb(VAL); +} + +static int superio_inw(int reg) +{ + int val; + outb(reg++, REG); + val = inb(VAL) << 8; + outb(reg, REG); + val |= inb(VAL); + return val; +} + +static inline void +superio_select(void) +{ + outb(DEV, REG); + outb(PME, VAL); +} + +static inline void +superio_enter(void) +{ + outb(0x87, REG); + outb(0x01, REG); + outb(0x55, REG); + outb(0x55, REG); +} + +static inline void +superio_exit(void) +{ + outb(0x02, REG); + outb(0x02, VAL); +} + +#define IT8712F_DEVID 0x8712 +#define IT8705F_DEVID 0x8705 +#define IT87_ACT_REG 0x30 +#define IT87_BASE_REG 0x60 + +/* Update battery voltage after every reading if true */ +static int update_vbat; + +/* Not all BIOSes properly configure the PWM registers */ +static int fix_pwm_polarity; + +/* Chip Type */ + +static u16 chip_type; + +/* Many IT87 constants specified below */ + +/* Length of ISA address segment */ +#define IT87_EXTENT 8 + +/* Where are the ISA address/data registers relative to the base address */ +#define IT87_ADDR_REG_OFFSET 5 +#define IT87_DATA_REG_OFFSET 6 + +/*----- The IT87 registers -----*/ + +#define IT87_REG_CONFIG 0x00 + +#define IT87_REG_ALARM1 0x01 +#define IT87_REG_ALARM2 0x02 +#define IT87_REG_ALARM3 0x03 + +#define IT87_REG_VID 0x0a +#define IT87_REG_FAN_DIV 0x0b + +/* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */ + +#define IT87_REG_FAN(nr) (0x0d + (nr)) +#define IT87_REG_FAN_MIN(nr) (0x10 + (nr)) +#define IT87_REG_FAN_MAIN_CTRL 0x13 +#define IT87_REG_FAN_CTL 0x14 +#define IT87_REG_PWM(nr) (0x15 + (nr)) + +#define IT87_REG_VIN(nr) (0x20 + (nr)) +#define IT87_REG_TEMP(nr) (0x29 + (nr)) + +#define IT87_REG_VIN_MAX(nr) (0x30 + (nr) * 2) +#define IT87_REG_VIN_MIN(nr) (0x31 + (nr) * 2) +#define IT87_REG_TEMP_HIGH(nr) (0x40 + (nr) * 2) +#define IT87_REG_TEMP_LOW(nr) (0x41 + (nr) * 2) + +#define IT87_REG_I2C_ADDR 0x48 + +#define IT87_REG_VIN_ENABLE 0x50 +#define IT87_REG_TEMP_ENABLE 0x51 + +#define IT87_REG_CHIPID 0x58 + +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8)/16),0,255)) +#define IN_FROM_REG(val) ((val) * 16) + +static inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-500)/1000):\ + ((val)+500)/1000),-128,127)) +#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*1000) + +#define PWM_TO_REG(val) ((val) >> 1) +#define PWM_FROM_REG(val) (((val)&0x7f) << 1) + +static int DIV_TO_REG(int val) +{ + int answer = 0; + while ((val >>= 1) != 0) + answer++; + return answer; +} +#define DIV_FROM_REG(val) (1 << (val)) + + +/* For each registered IT87, we need to keep some data in memory. That + data is pointed to by it87_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new it87 client is + allocated. */ +struct it87_data { + struct i2c_client client; + struct semaphore lock; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[9]; /* Register value */ + u8 in_max[9]; /* Register value */ + u8 in_min[9]; /* Register value */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 temp[3]; /* Register value */ + u8 temp_high[3]; /* Register value */ + u8 temp_low[3]; /* Register value */ + u8 sensor; /* Register value */ + u8 fan_div[3]; /* Register encoding, shifted right */ + u8 vid; /* Register encoding, combined */ + int vrm; + u32 alarms; /* Register encoding, combined */ + u8 fan_main_ctrl; /* Register value */ + u8 manual_pwm_ctl[3]; /* manual PWM value set by user */ +}; + + +static int it87_attach_adapter(struct i2c_adapter *adapter); +static int it87_find(int *address); +static int it87_detect(struct i2c_adapter *adapter, int address, int kind); +static int it87_detach_client(struct i2c_client *client); + +static int it87_read_value(struct i2c_client *client, u8 register); +static int it87_write_value(struct i2c_client *client, u8 register, + u8 value); +static struct it87_data *it87_update_device(struct device *dev); +static int it87_check_pwm(struct i2c_client *client); +static void it87_init_client(struct i2c_client *client, struct it87_data *data); + + +static struct i2c_driver it87_driver = { + .owner = THIS_MODULE, + .name = "it87", + .id = I2C_DRIVERID_IT87, + .flags = I2C_DF_NOTIFY, + .attach_adapter = it87_attach_adapter, + .detach_client = it87_detach_client, +}; + +static ssize_t show_in(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); +} + +static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); +} + +static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); +} + +static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->in_min[nr] = IN_TO_REG(val); + it87_write_value(client, IT87_REG_VIN_MIN(nr), + data->in_min[nr]); + up(&data->update_lock); + return count; +} +static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->in_max[nr] = IN_TO_REG(val); + it87_write_value(client, IT87_REG_VIN_MAX(nr), + data->in_max[nr]); + up(&data->update_lock); + return count; +} + +#define show_in_offset(offset) \ +static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ + show_in, NULL, offset); + +#define limit_in_offset(offset) \ +static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in_min, set_in_min, offset); \ +static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in_max, set_in_max, offset); + +show_in_offset(0); +limit_in_offset(0); +show_in_offset(1); +limit_in_offset(1); +show_in_offset(2); +limit_in_offset(2); +show_in_offset(3); +limit_in_offset(3); +show_in_offset(4); +limit_in_offset(4); +show_in_offset(5); +limit_in_offset(5); +show_in_offset(6); +limit_in_offset(6); +show_in_offset(7); +limit_in_offset(7); +show_in_offset(8); + +/* 3 temperatures */ +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])); +} +static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr])); +} +static ssize_t show_temp_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[nr])); +} +static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_high[nr] = TEMP_TO_REG(val); + it87_write_value(client, IT87_REG_TEMP_HIGH(nr), data->temp_high[nr]); + up(&data->update_lock); + return count; +} +static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_low[nr] = TEMP_TO_REG(val); + it87_write_value(client, IT87_REG_TEMP_LOW(nr), data->temp_low[nr]); + up(&data->update_lock); + return count; +} +#define show_temp_offset(offset) \ +static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ + show_temp, NULL, offset - 1); \ +static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ + show_temp_max, set_temp_max, offset - 1); \ +static SENSOR_DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ + show_temp_min, set_temp_min, offset - 1); + +show_temp_offset(1); +show_temp_offset(2); +show_temp_offset(3); + +static ssize_t show_sensor(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = it87_update_device(dev); + u8 reg = data->sensor; /* In case the value is updated while we use it */ + + if (reg & (1 << nr)) + return sprintf(buf, "3\n"); /* thermal diode */ + if (reg & (8 << nr)) + return sprintf(buf, "2\n"); /* thermistor */ + return sprintf(buf, "0\n"); /* disabled */ +} +static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + + data->sensor &= ~(1 << nr); + data->sensor &= ~(8 << nr); + /* 3 = thermal diode; 2 = thermistor; 0 = disabled */ + if (val == 3) + data->sensor |= 1 << nr; + else if (val == 2) + data->sensor |= 8 << nr; + else if (val != 0) { + up(&data->update_lock); + return -EINVAL; + } + it87_write_value(client, IT87_REG_TEMP_ENABLE, data->sensor); + up(&data->update_lock); + return count; +} +#define show_sensor_offset(offset) \ +static SENSOR_DEVICE_ATTR(temp##offset##_type, S_IRUGO | S_IWUSR, \ + show_sensor, set_sensor, offset - 1); + +show_sensor_offset(1); +show_sensor_offset(2); +show_sensor_offset(3); + +/* 3 Fans */ +static ssize_t show_fan(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = it87_update_device(dev); + return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr]))); +} +static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = it87_update_device(dev); + return sprintf(buf,"%d\n", + FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]))); +} +static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); +} +static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = it87_update_device(dev); + return sprintf(buf,"%d\n", (data->fan_main_ctrl & (1 << nr)) ? 1 : 0); +} +static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct it87_data *data = it87_update_device(dev); + return sprintf(buf,"%d\n", data->manual_pwm_ctl[nr]); +} +static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); + it87_write_value(client, IT87_REG_FAN_MIN(nr), data->fan_min[nr]); + up(&data->update_lock); + return count; +} +static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + int i, min[3]; + u8 old; + + down(&data->update_lock); + old = it87_read_value(client, IT87_REG_FAN_DIV); + + for (i = 0; i < 3; i++) + min[i] = FAN_FROM_REG(data->fan_min[i], DIV_FROM_REG(data->fan_div[i])); + + switch (nr) { + case 0: + case 1: + data->fan_div[nr] = DIV_TO_REG(val); + break; + case 2: + if (val < 8) + data->fan_div[nr] = 1; + else + data->fan_div[nr] = 3; + } + val = old & 0x80; + val |= (data->fan_div[0] & 0x07); + val |= (data->fan_div[1] & 0x07) << 3; + if (data->fan_div[2] == 3) + val |= 0x1 << 6; + it87_write_value(client, IT87_REG_FAN_DIV, val); + + for (i = 0; i < 3; i++) { + data->fan_min[i]=FAN_TO_REG(min[i], DIV_FROM_REG(data->fan_div[i])); + it87_write_value(client, IT87_REG_FAN_MIN(i), data->fan_min[i]); + } + up(&data->update_lock); + return count; +} +static ssize_t set_pwm_enable(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + + if (val == 0) { + int tmp; + /* make sure the fan is on when in on/off mode */ + tmp = it87_read_value(client, IT87_REG_FAN_CTL); + it87_write_value(client, IT87_REG_FAN_CTL, tmp | (1 << nr)); + /* set on/off mode */ + data->fan_main_ctrl &= ~(1 << nr); + it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); + } else if (val == 1) { + /* set SmartGuardian mode */ + data->fan_main_ctrl |= (1 << nr); + it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); + /* set saved pwm value, clear FAN_CTLX PWM mode bit */ + it87_write_value(client, IT87_REG_PWM(nr), PWM_TO_REG(data->manual_pwm_ctl[nr])); + } else { + up(&data->update_lock); + return -EINVAL; + } + + up(&data->update_lock); + return count; +} +static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); + int nr = sensor_attr->index; + + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + if (val < 0 || val > 255) + return -EINVAL; + + down(&data->update_lock); + data->manual_pwm_ctl[nr] = val; + if (data->fan_main_ctrl & (1 << nr)) + it87_write_value(client, IT87_REG_PWM(nr), PWM_TO_REG(data->manual_pwm_ctl[nr])); + up(&data->update_lock); + return count; +} + +#define show_fan_offset(offset) \ +static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ + show_fan, NULL, offset - 1); \ +static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan_min, set_fan_min, offset - 1); \ +static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ + show_fan_div, set_fan_div, offset - 1); + +show_fan_offset(1); +show_fan_offset(2); +show_fan_offset(3); + +#define show_pwm_offset(offset) \ +static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ + show_pwm_enable, set_pwm_enable, offset - 1); \ +static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ + show_pwm, set_pwm, offset - 1); + +show_pwm_offset(1); +show_pwm_offset(2); +show_pwm_offset(3); + +/* Alarms */ +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); +} +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +static ssize_t +show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->vrm); +} +static ssize_t +store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + data->vrm = val; + + return count; +} +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); +#define device_create_file_vrm(client) \ +device_create_file(&client->dev, &dev_attr_vrm) + +static ssize_t +show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct it87_data *data = it87_update_device(dev); + return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); +} +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); +#define device_create_file_vid(client) \ +device_create_file(&client->dev, &dev_attr_cpu0_vid) + +/* This function is called when: + * it87_driver is inserted (when this module is loaded), for each + available adapter + * when a new adapter is inserted (and it87_driver is still present) */ +static int it87_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, it87_detect); +} + +/* SuperIO detection - will change normal_isa[0] if a chip is found */ +static int it87_find(int *address) +{ + int err = -ENODEV; + + superio_enter(); + chip_type = superio_inw(DEVID); + if (chip_type != IT8712F_DEVID + && chip_type != IT8705F_DEVID) + goto exit; + + superio_select(); + if (!(superio_inb(IT87_ACT_REG) & 0x01)) { + pr_info("it87: Device not activated, skipping\n"); + goto exit; + } + + *address = superio_inw(IT87_BASE_REG) & ~(IT87_EXTENT - 1); + if (*address == 0) { + pr_info("it87: Base address not set, skipping\n"); + goto exit; + } + + err = 0; + pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n", + chip_type, *address, superio_inb(DEVREV) & 0x0f); + +exit: + superio_exit(); + return err; +} + +/* This function is called by i2c_detect */ +int it87_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int i; + struct i2c_client *new_client; + struct it87_data *data; + int err = 0; + const char *name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + int enable_pwm_interface; + + if (!is_isa && + !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* Reserve the ISA region */ + if (is_isa) + if (!request_region(address, IT87_EXTENT, it87_driver.name)) + goto ERROR0; + + /* Probe whether there is anything available on this address. Already + done for SMBus and Super-I/O clients */ + if (kind < 0) { + if (is_isa && !chip_type) { +#define REALLY_SLOW_IO + /* We need the timeouts for at least some IT87-like chips. But only + if we read 'undefined' registers. */ + i = inb_p(address + 1); + if (inb_p(address + 2) != i + || inb_p(address + 3) != i + || inb_p(address + 7) != i) { + err = -ENODEV; + goto ERROR1; + } +#undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f, address + 5); + if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { + outb_p(i, address + 5); + err = -ENODEV; + goto ERROR1; + } + } + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access it87_{read,write}_value. */ + + if (!(data = kmalloc(sizeof(struct it87_data), GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR1; + } + memset(data, 0, sizeof(struct it87_data)); + + new_client = &data->client; + if (is_isa) + init_MUTEX(&data->lock); + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &it87_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if ((it87_read_value(new_client, IT87_REG_CONFIG) & 0x80) + || (!is_isa + && it87_read_value(new_client, IT87_REG_I2C_ADDR) != address)) { + err = -ENODEV; + goto ERROR2; + } + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = it87_read_value(new_client, IT87_REG_CHIPID); + if (i == 0x90) { + kind = it87; + if ((is_isa) && (chip_type == IT8712F_DEVID)) + kind = it8712; + } + 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), address); + err = -ENODEV; + goto ERROR2; + } + } + + if (kind == it87) { + name = "it87"; + } else if (kind == it8712) { + name = "it8712"; + } + + /* Fill in the remaining client fields and put it into the global list */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->type = kind; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR2; + + /* Check PWM configuration */ + enable_pwm_interface = it87_check_pwm(new_client); + + /* Initialize the IT87 chip */ + it87_init_client(new_client, data); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &sensor_dev_attr_in0_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in1_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in2_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in3_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in4_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in5_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in6_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in7_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in8_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in0_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in1_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in2_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in3_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in4_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in5_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in6_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in7_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in0_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in1_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in2_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in3_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in4_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in5_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in6_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_in7_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp2_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp3_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp2_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp3_max.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp2_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp3_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp1_type.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp2_type.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_temp3_type.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan1_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan2_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan3_input.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan1_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan2_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan3_min.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan1_div.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan2_div.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_fan3_div.dev_attr); + device_create_file(&new_client->dev, &dev_attr_alarms); + if (enable_pwm_interface) { + device_create_file(&new_client->dev, &sensor_dev_attr_pwm1_enable.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_pwm2_enable.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_pwm3_enable.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_pwm1.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_pwm2.dev_attr); + device_create_file(&new_client->dev, &sensor_dev_attr_pwm3.dev_attr); + } + + if (data->type == it8712) { + data->vrm = i2c_which_vrm(); + device_create_file_vrm(new_client); + device_create_file_vid(new_client); + } + + return 0; + +ERROR2: + kfree(data); +ERROR1: + if (is_isa) + release_region(address, IT87_EXTENT); +ERROR0: + return err; +} + +static int it87_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, + "Client deregistration failed, client not detached.\n"); + return err; + } + + if(i2c_is_isa_client(client)) + release_region(client->addr, IT87_EXTENT); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +/* The SMBus locks itself, but ISA access must be locked explicitly! + We don't want to lock the whole ISA bus, so we lock each client + separately. + We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, + would slow down the IT87 access and should not be necessary. */ +static int it87_read_value(struct i2c_client *client, u8 reg) +{ + struct it87_data *data = i2c_get_clientdata(client); + + int res; + if (i2c_is_isa_client(client)) { + down(&data->lock); + outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); + res = inb_p(client->addr + IT87_DATA_REG_OFFSET); + up(&data->lock); + return res; + } else + return i2c_smbus_read_byte_data(client, reg); +} + +/* The SMBus locks itself, but ISA access muse be locked explicitly! + We don't want to lock the whole ISA bus, so we lock each client + separately. + We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, + would slow down the IT87 access and should not be necessary. */ +static int it87_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + struct it87_data *data = i2c_get_clientdata(client); + + if (i2c_is_isa_client(client)) { + down(&data->lock); + outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); + outb_p(value, client->addr + IT87_DATA_REG_OFFSET); + up(&data->lock); + return 0; + } else + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Return 1 if and only if the PWM interface is safe to use */ +static int it87_check_pwm(struct i2c_client *client) +{ + /* Some BIOSes fail to correctly configure the IT87 fans. All fans off + * and polarity set to active low is sign that this is the case so we + * disable pwm control to protect the user. */ + int tmp = it87_read_value(client, IT87_REG_FAN_CTL); + if ((tmp & 0x87) == 0) { + if (fix_pwm_polarity) { + /* The user asks us to attempt a chip reconfiguration. + * This means switching to active high polarity and + * inverting all fan speed values. */ + int i; + u8 pwm[3]; + + for (i = 0; i < 3; i++) + pwm[i] = it87_read_value(client, + IT87_REG_PWM(i)); + + /* If any fan is in automatic pwm mode, the polarity + * might be correct, as suspicious as it seems, so we + * better don't change anything (but still disable the + * PWM interface). */ + if (!((pwm[0] | pwm[1] | pwm[2]) & 0x80)) { + dev_info(&client->dev, "Reconfiguring PWM to " + "active high polarity\n"); + it87_write_value(client, IT87_REG_FAN_CTL, + tmp | 0x87); + for (i = 0; i < 3; i++) + it87_write_value(client, + IT87_REG_PWM(i), + 0x7f & ~pwm[i]); + return 1; + } + + dev_info(&client->dev, "PWM configuration is " + "too broken to be fixed\n"); + } + + dev_info(&client->dev, "Detected broken BIOS " + "defaults, disabling PWM interface\n"); + return 0; + } else if (fix_pwm_polarity) { + dev_info(&client->dev, "PWM configuration looks " + "sane, won't touch\n"); + } + + return 1; +} + +/* Called when we have found a new IT87. */ +static void it87_init_client(struct i2c_client *client, struct it87_data *data) +{ + int tmp, i; + + /* initialize to sane defaults: + * - if the chip is in manual pwm mode, this will be overwritten with + * the actual settings on the chip (so in this case, initialization + * is not needed) + * - if in automatic or on/off mode, we could switch to manual mode, + * read the registers and set manual_pwm_ctl accordingly, but currently + * this is not implemented, so we initialize to something sane */ + for (i = 0; i < 3; i++) { + data->manual_pwm_ctl[i] = 0xff; + } + + /* Check if temperature channnels are reset manually or by some reason */ + tmp = it87_read_value(client, IT87_REG_TEMP_ENABLE); + if ((tmp & 0x3f) == 0) { + /* Temp1,Temp3=thermistor; Temp2=thermal diode */ + tmp = (tmp & 0xc0) | 0x2a; + it87_write_value(client, IT87_REG_TEMP_ENABLE, tmp); + } + data->sensor = tmp; + + /* Check if voltage monitors are reset manually or by some reason */ + tmp = it87_read_value(client, IT87_REG_VIN_ENABLE); + if ((tmp & 0xff) == 0) { + /* Enable all voltage monitors */ + it87_write_value(client, IT87_REG_VIN_ENABLE, 0xff); + } + + /* Check if tachometers are reset manually or by some reason */ + data->fan_main_ctrl = it87_read_value(client, IT87_REG_FAN_MAIN_CTRL); + if ((data->fan_main_ctrl & 0x70) == 0) { + /* Enable all fan tachometers */ + data->fan_main_ctrl |= 0x70; + it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); + } + + /* Set current fan mode registers and the default settings for the + * other mode registers */ + for (i = 0; i < 3; i++) { + if (data->fan_main_ctrl & (1 << i)) { + /* pwm mode */ + tmp = it87_read_value(client, IT87_REG_PWM(i)); + if (tmp & 0x80) { + /* automatic pwm - not yet implemented, but + * leave the settings made by the BIOS alone + * until a change is requested via the sysfs + * interface */ + } else { + /* manual pwm */ + data->manual_pwm_ctl[i] = PWM_FROM_REG(tmp); + } + } + } + + /* Start monitoring */ + it87_write_value(client, IT87_REG_CONFIG, + (it87_read_value(client, IT87_REG_CONFIG) & 0x36) + | (update_vbat ? 0x41 : 0x01)); +} + +static struct it87_data *it87_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct it87_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + + if (update_vbat) { + /* Cleared after each update, so reenable. Value + returned by this read will be previous value */ + it87_write_value(client, IT87_REG_CONFIG, + it87_read_value(client, IT87_REG_CONFIG) | 0x40); + } + for (i = 0; i <= 7; i++) { + data->in[i] = + it87_read_value(client, IT87_REG_VIN(i)); + data->in_min[i] = + it87_read_value(client, IT87_REG_VIN_MIN(i)); + data->in_max[i] = + it87_read_value(client, IT87_REG_VIN_MAX(i)); + } + data->in[8] = + it87_read_value(client, IT87_REG_VIN(8)); + /* Temperature sensor doesn't have limit registers, set + to min and max value */ + data->in_min[8] = 0; + data->in_max[8] = 255; + + for (i = 0; i < 3; i++) { + data->fan[i] = + it87_read_value(client, IT87_REG_FAN(i)); + data->fan_min[i] = + it87_read_value(client, IT87_REG_FAN_MIN(i)); + } + for (i = 0; i < 3; i++) { + data->temp[i] = + it87_read_value(client, IT87_REG_TEMP(i)); + data->temp_high[i] = + it87_read_value(client, IT87_REG_TEMP_HIGH(i)); + data->temp_low[i] = + it87_read_value(client, IT87_REG_TEMP_LOW(i)); + } + + i = it87_read_value(client, IT87_REG_FAN_DIV); + data->fan_div[0] = i & 0x07; + data->fan_div[1] = (i >> 3) & 0x07; + data->fan_div[2] = (i & 0x40) ? 3 : 1; + + data->alarms = + it87_read_value(client, IT87_REG_ALARM1) | + (it87_read_value(client, IT87_REG_ALARM2) << 8) | + (it87_read_value(client, IT87_REG_ALARM3) << 16); + data->fan_main_ctrl = it87_read_value(client, IT87_REG_FAN_MAIN_CTRL); + + data->sensor = it87_read_value(client, IT87_REG_TEMP_ENABLE); + /* The 8705 does not have VID capability */ + if (data->type == it8712) { + data->vid = it87_read_value(client, IT87_REG_VID); + data->vid &= 0x1f; + } + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sm_it87_init(void) +{ + int addr; + + if (!it87_find(&addr)) { + normal_isa[0] = addr; + } + return i2c_add_driver(&it87_driver); +} + +static void __exit sm_it87_exit(void) +{ + i2c_del_driver(&it87_driver); +} + + +MODULE_AUTHOR("Chris Gauthron "); +MODULE_DESCRIPTION("IT8705F, IT8712F, Sis950 driver"); +module_param(update_vbat, bool, 0); +MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); +module_param(fix_pwm_polarity, bool, 0); +MODULE_PARM_DESC(fix_pwm_polarity, "Force PWM polarity to active high (DANGEROUS)"); +MODULE_LICENSE("GPL"); + +module_init(sm_it87_init); +module_exit(sm_it87_exit); diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c new file mode 100644 index 000000000000..7c6f9ea5a254 --- /dev/null +++ b/drivers/hwmon/lm63.c @@ -0,0 +1,597 @@ +/* + * lm63.c - driver for the National Semiconductor LM63 temperature sensor + * with integrated fan control + * Copyright (C) 2004-2005 Jean Delvare + * Based on the lm90 driver. + * + * The LM63 is a sensor chip made by National Semiconductor. It measures + * two temperatures (its own and one external one) and the speed of one + * fan, those speed it can additionally control. Complete datasheet can be + * obtained from National's website at: + * http://www.national.com/pf/LM/LM63.html + * + * The LM63 is basically an LM86 with fan speed monitoring and control + * capabilities added. It misses some of the LM86 features though: + * - No low limit for local temperature. + * - No critical limit for local temperature. + * - Critical limit for remote temperature can be changed only once. We + * will consider that the critical limit is read-only. + * + * The datasheet isn't very clear about what the tachometer reading is. + * I had a explanation from National Semiconductor though. The two lower + * bits of the read value have to be masked out. The value is still 16 bit + * in width. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Addresses to scan + * Address is fully defined internally and cannot be changed. + */ + +static unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* + * Insmod parameters + */ + +SENSORS_INSMOD_1(lm63); + +/* + * The LM63 registers + */ + +#define LM63_REG_CONFIG1 0x03 +#define LM63_REG_CONFIG2 0xBF +#define LM63_REG_CONFIG_FAN 0x4A + +#define LM63_REG_TACH_COUNT_MSB 0x47 +#define LM63_REG_TACH_COUNT_LSB 0x46 +#define LM63_REG_TACH_LIMIT_MSB 0x49 +#define LM63_REG_TACH_LIMIT_LSB 0x48 + +#define LM63_REG_PWM_VALUE 0x4C +#define LM63_REG_PWM_FREQ 0x4D + +#define LM63_REG_LOCAL_TEMP 0x00 +#define LM63_REG_LOCAL_HIGH 0x05 + +#define LM63_REG_REMOTE_TEMP_MSB 0x01 +#define LM63_REG_REMOTE_TEMP_LSB 0x10 +#define LM63_REG_REMOTE_OFFSET_MSB 0x11 +#define LM63_REG_REMOTE_OFFSET_LSB 0x12 +#define LM63_REG_REMOTE_HIGH_MSB 0x07 +#define LM63_REG_REMOTE_HIGH_LSB 0x13 +#define LM63_REG_REMOTE_LOW_MSB 0x08 +#define LM63_REG_REMOTE_LOW_LSB 0x14 +#define LM63_REG_REMOTE_TCRIT 0x19 +#define LM63_REG_REMOTE_TCRIT_HYST 0x21 + +#define LM63_REG_ALERT_STATUS 0x02 +#define LM63_REG_ALERT_MASK 0x16 + +#define LM63_REG_MAN_ID 0xFE +#define LM63_REG_CHIP_ID 0xFF + +/* + * Conversions and various macros + * For tachometer counts, the LM63 uses 16-bit values. + * For local temperature and high limit, remote critical limit and hysteresis + * value, it uses signed 8-bit values with LSB = 1 degree Celsius. + * For remote temperature, low and high limits, it uses signed 11-bit values + * with LSB = 0.125 degree Celsius, left-justified in 16-bit registers. + */ + +#define FAN_FROM_REG(reg) ((reg) == 0xFFFC || (reg) == 0 ? 0 : \ + 5400000 / (reg)) +#define FAN_TO_REG(val) ((val) <= 82 ? 0xFFFC : \ + (5400000 / (val)) & 0xFFFC) +#define TEMP8_FROM_REG(reg) ((reg) * 1000) +#define TEMP8_TO_REG(val) ((val) <= -128000 ? -128 : \ + (val) >= 127000 ? 127 : \ + (val) < 0 ? ((val) - 500) / 1000 : \ + ((val) + 500) / 1000) +#define TEMP11_FROM_REG(reg) ((reg) / 32 * 125) +#define TEMP11_TO_REG(val) ((val) <= -128000 ? 0x8000 : \ + (val) >= 127875 ? 0x7FE0 : \ + (val) < 0 ? ((val) - 62) / 125 * 32 : \ + ((val) + 62) / 125 * 32) +#define HYST_TO_REG(val) ((val) <= 0 ? 0 : \ + (val) >= 127000 ? 127 : \ + ((val) + 500) / 1000) + +/* + * Functions declaration + */ + +static int lm63_attach_adapter(struct i2c_adapter *adapter); +static int lm63_detach_client(struct i2c_client *client); + +static struct lm63_data *lm63_update_device(struct device *dev); + +static int lm63_detect(struct i2c_adapter *adapter, int address, int kind); +static void lm63_init_client(struct i2c_client *client); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver lm63_driver = { + .owner = THIS_MODULE, + .name = "lm63", + .flags = I2C_DF_NOTIFY, + .attach_adapter = lm63_attach_adapter, + .detach_client = lm63_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct lm63_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* registers values */ + u8 config, config_fan; + u16 fan[2]; /* 0: input + 1: low limit */ + u8 pwm1_freq; + u8 pwm1_value; + s8 temp8[3]; /* 0: local input + 1: local high limit + 2: remote critical limit */ + s16 temp11[3]; /* 0: remote input + 1: remote low limit + 2: remote high limit */ + u8 temp2_crit_hyst; + u8 alarms; +}; + +/* + * Sysfs callback functions and files + */ + +static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm63_data *data = lm63_update_device(dev); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index])); +} + +static ssize_t set_fan(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->fan[1] = FAN_TO_REG(val); + i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_LSB, + data->fan[1] & 0xFF); + i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_MSB, + data->fan[1] >> 8); + up(&data->update_lock); + return count; +} + +static ssize_t show_pwm1(struct device *dev, struct device_attribute *dummy, + char *buf) +{ + struct lm63_data *data = lm63_update_device(dev); + return sprintf(buf, "%d\n", data->pwm1_value >= 2 * data->pwm1_freq ? + 255 : (data->pwm1_value * 255 + data->pwm1_freq) / + (2 * data->pwm1_freq)); +} + +static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + unsigned long val; + + if (!(data->config_fan & 0x20)) /* register is read-only */ + return -EPERM; + + val = simple_strtoul(buf, NULL, 10); + down(&data->update_lock); + data->pwm1_value = val <= 0 ? 0 : + val >= 255 ? 2 * data->pwm1_freq : + (val * data->pwm1_freq * 2 + 127) / 255; + i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1_value); + up(&data->update_lock); + return count; +} + +static ssize_t show_pwm1_enable(struct device *dev, struct device_attribute *dummy, + char *buf) +{ + struct lm63_data *data = lm63_update_device(dev); + return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2); +} + +static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm63_data *data = lm63_update_device(dev); + return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[attr->index])); +} + +static ssize_t set_temp8(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp8[1] = TEMP8_TO_REG(val); + i2c_smbus_write_byte_data(client, LM63_REG_LOCAL_HIGH, data->temp8[1]); + up(&data->update_lock); + return count; +} + +static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm63_data *data = lm63_update_device(dev); + return sprintf(buf, "%d\n", TEMP11_FROM_REG(data->temp11[attr->index])); +} + +static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + static const u8 reg[4] = { + LM63_REG_REMOTE_LOW_MSB, + LM63_REG_REMOTE_LOW_LSB, + LM63_REG_REMOTE_HIGH_MSB, + LM63_REG_REMOTE_HIGH_LSB, + }; + + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + int nr = attr->index; + + down(&data->update_lock); + data->temp11[nr] = TEMP11_TO_REG(val); + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], + data->temp11[nr] >> 8); + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], + data->temp11[nr] & 0xff); + up(&data->update_lock); + return count; +} + +/* Hysteresis register holds a relative value, while we want to present + an absolute to user-space */ +static ssize_t show_temp2_crit_hyst(struct device *dev, struct device_attribute *dummy, + char *buf) +{ + struct lm63_data *data = lm63_update_device(dev); + return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[2]) + - TEMP8_FROM_REG(data->temp2_crit_hyst)); +} + +/* And now the other way around, user-space provides an absolute + hysteresis value and we have to store a relative one */ +static ssize_t set_temp2_crit_hyst(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + long hyst; + + down(&data->update_lock); + hyst = TEMP8_FROM_REG(data->temp8[2]) - val; + i2c_smbus_write_byte_data(client, LM63_REG_REMOTE_TCRIT_HYST, + HYST_TO_REG(hyst)); + up(&data->update_lock); + return count; +} + +static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, + char *buf) +{ + struct lm63_data *data = lm63_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); +} + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, + set_fan, 1); + +static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1); +static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 1); + +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 1); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 2); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp8, NULL, 2); +static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst, + set_temp2_crit_hyst); + +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +/* + * Real code + */ + +static int lm63_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, lm63_detect); +} + +/* + * The following function does more than just detection. If detection + * succeeds, it also registers the new chip. + */ +static int lm63_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct lm63_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct lm63_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct lm63_data)); + + /* The common I2C client data is placed right before the + LM63-specific data. */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lm63_driver; + new_client->flags = 0; + + /* Default to an LM63 if forced */ + if (kind == 0) + kind = lm63; + + 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); + goto exit_free; + } + } + + strlcpy(new_client->name, "lm63", I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the LM63 chip */ + lm63_init_client(new_client); + + /* Register sysfs hooks */ + if (data->config & 0x04) { /* tachometer enabled */ + device_create_file(&new_client->dev, + &sensor_dev_attr_fan1_input.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_fan1_min.dev_attr); + } + device_create_file(&new_client->dev, &dev_attr_pwm1); + device_create_file(&new_client->dev, &dev_attr_pwm1_enable); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_input.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_input.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_min.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_max.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_max.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_crit.dev_attr); + device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst); + device_create_file(&new_client->dev, &dev_attr_alarms); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +/* Idealy we shouldn't have to initialize anything, since the BIOS + should have taken care of everything */ +static void lm63_init_client(struct i2c_client *client) +{ + struct lm63_data *data = i2c_get_clientdata(client); + + data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1); + data->config_fan = i2c_smbus_read_byte_data(client, + LM63_REG_CONFIG_FAN); + + /* Start converting if needed */ + if (data->config & 0x40) { /* standby */ + dev_dbg(&client->dev, "Switching to operational mode"); + data->config &= 0xA7; + i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1, + data->config); + } + + /* We may need pwm1_freq before ever updating the client data */ + data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ); + if (data->pwm1_freq == 0) + data->pwm1_freq = 1; + + /* Show some debug info about the LM63 configuration */ + dev_dbg(&client->dev, "Alert/tach pin configured for %s\n", + (data->config & 0x04) ? "tachometer input" : + "alert output"); + dev_dbg(&client->dev, "PWM clock %s kHz, output frequency %u Hz\n", + (data->config_fan & 0x08) ? "1.4" : "360", + ((data->config_fan & 0x08) ? 700 : 180000) / data->pwm1_freq); + dev_dbg(&client->dev, "PWM output active %s, %s mode\n", + (data->config_fan & 0x10) ? "low" : "high", + (data->config_fan & 0x20) ? "manual" : "auto"); +} + +static int lm63_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static struct lm63_data *lm63_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm63_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + if (data->config & 0x04) { /* tachometer enabled */ + /* order matters for fan1_input */ + data->fan[0] = i2c_smbus_read_byte_data(client, + LM63_REG_TACH_COUNT_LSB) & 0xFC; + data->fan[0] |= i2c_smbus_read_byte_data(client, + LM63_REG_TACH_COUNT_MSB) << 8; + data->fan[1] = (i2c_smbus_read_byte_data(client, + LM63_REG_TACH_LIMIT_LSB) & 0xFC) + | (i2c_smbus_read_byte_data(client, + LM63_REG_TACH_LIMIT_MSB) << 8); + } + + data->pwm1_freq = i2c_smbus_read_byte_data(client, + LM63_REG_PWM_FREQ); + if (data->pwm1_freq == 0) + data->pwm1_freq = 1; + data->pwm1_value = i2c_smbus_read_byte_data(client, + LM63_REG_PWM_VALUE); + + data->temp8[0] = i2c_smbus_read_byte_data(client, + LM63_REG_LOCAL_TEMP); + data->temp8[1] = i2c_smbus_read_byte_data(client, + LM63_REG_LOCAL_HIGH); + + /* order matters for temp2_input */ + data->temp11[0] = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TEMP_MSB) << 8; + data->temp11[0] |= i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TEMP_LSB); + data->temp11[1] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_LOW_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_LOW_LSB); + data->temp11[2] = (i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_HIGH_MSB) << 8) + | i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_HIGH_LSB); + data->temp8[2] = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TCRIT); + data->temp2_crit_hyst = i2c_smbus_read_byte_data(client, + LM63_REG_REMOTE_TCRIT_HYST); + + data->alarms = i2c_smbus_read_byte_data(client, + LM63_REG_ALERT_STATUS) & 0x7F; + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_lm63_init(void) +{ + return i2c_add_driver(&lm63_driver); +} + +static void __exit sensors_lm63_exit(void) +{ + i2c_del_driver(&lm63_driver); +} + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("LM63 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm63_init); +module_exit(sensors_lm63_exit); diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c new file mode 100644 index 000000000000..5be164ed278e --- /dev/null +++ b/drivers/hwmon/lm75.c @@ -0,0 +1,296 @@ +/* + lm75.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include "lm75.h" + + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, + 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(lm75); + +/* Many LM75 constants specified below */ + +/* The LM75 registers */ +#define LM75_REG_TEMP 0x00 +#define LM75_REG_CONF 0x01 +#define LM75_REG_TEMP_HYST 0x02 +#define LM75_REG_TEMP_OS 0x03 + +/* Each client has this additional data */ +struct lm75_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + u16 temp_input; /* Register values */ + u16 temp_max; + u16 temp_hyst; +}; + +static int lm75_attach_adapter(struct i2c_adapter *adapter); +static int lm75_detect(struct i2c_adapter *adapter, int address, int kind); +static void lm75_init_client(struct i2c_client *client); +static int lm75_detach_client(struct i2c_client *client); +static int lm75_read_value(struct i2c_client *client, u8 reg); +static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value); +static struct lm75_data *lm75_update_device(struct device *dev); + + +/* This is the driver that will be inserted */ +static struct i2c_driver lm75_driver = { + .owner = THIS_MODULE, + .name = "lm75", + .id = I2C_DRIVERID_LM75, + .flags = I2C_DF_NOTIFY, + .attach_adapter = lm75_attach_adapter, + .detach_client = lm75_detach_client, +}; + +#define show(value) \ +static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm75_data *data = lm75_update_device(dev); \ + return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->value)); \ +} +show(temp_max); +show(temp_hyst); +show(temp_input); + +#define set(value, reg) \ +static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm75_data *data = i2c_get_clientdata(client); \ + int temp = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->value = LM75_TEMP_TO_REG(temp); \ + lm75_write_value(client, reg, data->value); \ + up(&data->update_lock); \ + return count; \ +} +set(temp_max, LM75_REG_TEMP_OS); +set(temp_hyst, LM75_REG_TEMP_HYST); + +static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max); +static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst); +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL); + +static int lm75_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, lm75_detect); +} + +/* This function is called by i2c_detect */ +static int lm75_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int i; + struct i2c_client *new_client; + struct lm75_data *data; + int err = 0; + const char *name = ""; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + dev_dbg(&adapter->dev, + "lm75_detect called for an ISA bus adapter?!?\n"); + goto exit; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto exit; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access lm75_{read,write}_value. */ + if (!(data = kmalloc(sizeof(struct lm75_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct lm75_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lm75_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. There is no identification- + dedicated register so we have to rely on several tricks: + unused bits, registers cycling over 8-address boundaries, + addresses 0x04-0x07 returning the last read value. + 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) + goto exit_free; + 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) + goto exit_free; + + /* Unused bits */ + if (conf & 0xe0) + goto exit_free; + + /* 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) + goto exit_free; + } + + /* Determine the chip type - only one kind supported! */ + if (kind <= 0) + kind = lm75; + + if (kind == lm75) { + name = "lm75"; + } + + /* Fill in the remaining client fields and put it into the global list */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the LM75 chip */ + lm75_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); + device_create_file(&new_client->dev, &dev_attr_temp1_input); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static int lm75_detach_client(struct i2c_client *client) +{ + i2c_detach_client(client); + kfree(i2c_get_clientdata(client)); + return 0; +} + +/* All registers are word-sized, except for the configuration register. + LM75 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +static int lm75_read_value(struct i2c_client *client, u8 reg) +{ + if (reg == LM75_REG_CONF) + return i2c_smbus_read_byte_data(client, reg); + else + return swab16(i2c_smbus_read_word_data(client, reg)); +} + +/* All registers are word-sized, except for the configuration register. + LM75 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if (reg == LM75_REG_CONF) + return i2c_smbus_write_byte_data(client, reg, value); + else + return i2c_smbus_write_word_data(client, reg, swab16(value)); +} + +static void lm75_init_client(struct i2c_client *client) +{ + /* Initialize the LM75 chip */ + lm75_write_value(client, LM75_REG_CONF, 0); +} + +static struct lm75_data *lm75_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm75_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + dev_dbg(&client->dev, "Starting lm75 update\n"); + + data->temp_input = lm75_read_value(client, LM75_REG_TEMP); + data->temp_max = lm75_read_value(client, LM75_REG_TEMP_OS); + data->temp_hyst = lm75_read_value(client, LM75_REG_TEMP_HYST); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_lm75_init(void) +{ + return i2c_add_driver(&lm75_driver); +} + +static void __exit sensors_lm75_exit(void) +{ + i2c_del_driver(&lm75_driver); +} + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("LM75 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm75_init); +module_exit(sensors_lm75_exit); diff --git a/drivers/hwmon/lm75.h b/drivers/hwmon/lm75.h new file mode 100644 index 000000000000..63e3f2fb4c21 --- /dev/null +++ b/drivers/hwmon/lm75.h @@ -0,0 +1,49 @@ +/* + lm75.h - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2003 Mark M. Hoffman + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This file contains common code for encoding/decoding LM75 type + temperature readings, which are emulated by many of the chips + we support. As the user is unlikely to load more than one driver + which contains this code, we don't worry about the wasted space. +*/ + +#include + +/* straight from the datasheet */ +#define LM75_TEMP_MIN (-55000) +#define LM75_TEMP_MAX 125000 + +/* TEMP: 0.001C/bit (-55C to +125C) + REG: (0.5C/bit, two's complement) << 7 */ +static inline u16 LM75_TEMP_TO_REG(int temp) +{ + int ntemp = SENSORS_LIMIT(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); + ntemp += (ntemp<0 ? -250 : 250); + return (u16)((ntemp / 500) << 7); +} + +static inline int LM75_TEMP_FROM_REG(u16 reg) +{ + /* use integer division instead of equivalent right shift to + guarantee arithmetic shift and preserve the sign */ + return ((s16)reg / 128) * 500; +} + diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c new file mode 100644 index 000000000000..b98f44952997 --- /dev/null +++ b/drivers/hwmon/lm77.c @@ -0,0 +1,420 @@ +/* + lm77.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + + Copyright (c) 2004 Andras BALI + + Heavily based on lm75.c by Frodo Looijaard . The LM77 + is a temperature sensor and thermal window comparator with 0.5 deg + resolution made by National Semiconductor. Complete datasheet can be + obtained at their site: + http://www.national.com/pf/LM/LM77.html + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include + + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(lm77); + +/* The LM77 registers */ +#define LM77_REG_TEMP 0x00 +#define LM77_REG_CONF 0x01 +#define LM77_REG_TEMP_HYST 0x02 +#define LM77_REG_TEMP_CRIT 0x03 +#define LM77_REG_TEMP_MIN 0x04 +#define LM77_REG_TEMP_MAX 0x05 + +/* Each client has this additional data */ +struct lm77_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; + unsigned long last_updated; /* In jiffies */ + int temp_input; /* Temperatures */ + int temp_crit; + int temp_min; + int temp_max; + int temp_hyst; + u8 alarms; +}; + +static int lm77_attach_adapter(struct i2c_adapter *adapter); +static int lm77_detect(struct i2c_adapter *adapter, int address, int kind); +static void lm77_init_client(struct i2c_client *client); +static int lm77_detach_client(struct i2c_client *client); +static u16 lm77_read_value(struct i2c_client *client, u8 reg); +static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value); + +static struct lm77_data *lm77_update_device(struct device *dev); + + +/* This is the driver that will be inserted */ +static struct i2c_driver lm77_driver = { + .owner = THIS_MODULE, + .name = "lm77", + .flags = I2C_DF_NOTIFY, + .attach_adapter = lm77_attach_adapter, + .detach_client = lm77_detach_client, +}; + +/* straight from the datasheet */ +#define LM77_TEMP_MIN (-55000) +#define LM77_TEMP_MAX 125000 + +/* In the temperature registers, the low 3 bits are not part of the + temperature values; they are the status bits. */ +static inline u16 LM77_TEMP_TO_REG(int temp) +{ + int ntemp = SENSORS_LIMIT(temp, LM77_TEMP_MIN, LM77_TEMP_MAX); + return (u16)((ntemp / 500) * 8); +} + +static inline int LM77_TEMP_FROM_REG(u16 reg) +{ + return ((int)reg / 8) * 500; +} + +/* sysfs stuff */ + +/* read routines for temperature limits */ +#define show(value) \ +static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm77_data *data = lm77_update_device(dev); \ + return sprintf(buf, "%d\n", data->value); \ +} + +show(temp_input); +show(temp_crit); +show(temp_min); +show(temp_max); +show(alarms); + +/* read routines for hysteresis values */ +static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm77_data *data = lm77_update_device(dev); + return sprintf(buf, "%d\n", data->temp_crit - data->temp_hyst); +} +static ssize_t show_temp_min_hyst(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm77_data *data = lm77_update_device(dev); + return sprintf(buf, "%d\n", data->temp_min + data->temp_hyst); +} +static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm77_data *data = lm77_update_device(dev); + return sprintf(buf, "%d\n", data->temp_max - data->temp_hyst); +} + +/* write routines */ +#define set(value, reg) \ +static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm77_data *data = i2c_get_clientdata(client); \ + long val = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->value = val; \ + lm77_write_value(client, reg, LM77_TEMP_TO_REG(data->value)); \ + up(&data->update_lock); \ + return count; \ +} + +set(temp_min, LM77_REG_TEMP_MIN); +set(temp_max, LM77_REG_TEMP_MAX); + +/* hysteresis is stored as a relative value on the chip, so it has to be + converted first */ +static ssize_t set_temp_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm77_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->temp_hyst = data->temp_crit - val; + lm77_write_value(client, LM77_REG_TEMP_HYST, + LM77_TEMP_TO_REG(data->temp_hyst)); + up(&data->update_lock); + return count; +} + +/* preserve hysteresis when setting T_crit */ +static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm77_data *data = i2c_get_clientdata(client); + long val = simple_strtoul(buf, NULL, 10); + int oldcrithyst; + + down(&data->update_lock); + oldcrithyst = data->temp_crit - data->temp_hyst; + data->temp_crit = val; + data->temp_hyst = data->temp_crit - oldcrithyst; + lm77_write_value(client, LM77_REG_TEMP_CRIT, + LM77_TEMP_TO_REG(data->temp_crit)); + lm77_write_value(client, LM77_REG_TEMP_HYST, + LM77_TEMP_TO_REG(data->temp_hyst)); + up(&data->update_lock); + return count; +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, + show_temp_input, NULL); +static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, + show_temp_crit, set_temp_crit); +static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, + show_temp_min, set_temp_min); +static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, + show_temp_max, set_temp_max); + +static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, + show_temp_crit_hyst, set_temp_crit_hyst); +static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, + show_temp_min_hyst, NULL); +static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, + show_temp_max_hyst, NULL); + +static DEVICE_ATTR(alarms, S_IRUGO, + show_alarms, NULL); + +static int lm77_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, lm77_detect); +} + +/* This function is called by i2c_detect */ +static int lm77_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct lm77_data *data; + int err = 0; + const char *name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto exit; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access lm77_{read,write}_value. */ + if (!(data = kmalloc(sizeof(struct lm77_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct lm77_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lm77_driver; + new_client->flags = 0; + + /* Here comes the remaining detection. Since the LM77 has no + register dedicated to identification, we have to rely on the + following tricks: + + 1. the high 4 bits represent the sign and thus they should + always be the same + 2. the high 3 bits are unused in the configuration register + 3. addresses 0x06 and 0x07 return the last read value + 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) + goto exit_free; + + /* 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)) + goto exit_free; + + /* unused bits */ + if (conf & 0xe0) + goto exit_free; + + /* 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) + goto exit_free; + 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) + goto exit_free; + 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) + goto exit_free; + + } + + /* Determine the chip type - only one kind supported! */ + if (kind <= 0) + kind = lm77; + + if (kind == lm77) { + name = "lm77"; + } + + /* Fill in the remaining client fields and put it into the global list */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the LM77 chip */ + lm77_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp1_crit); + device_create_file(&new_client->dev, &dev_attr_temp1_min); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst); + device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst); + device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); + device_create_file(&new_client->dev, &dev_attr_alarms); + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static int lm77_detach_client(struct i2c_client *client) +{ + i2c_detach_client(client); + kfree(i2c_get_clientdata(client)); + return 0; +} + +/* All registers are word-sized, except for the configuration register. + The LM77 uses the high-byte first convention. */ +static u16 lm77_read_value(struct i2c_client *client, u8 reg) +{ + if (reg == LM77_REG_CONF) + return i2c_smbus_read_byte_data(client, reg); + else + return swab16(i2c_smbus_read_word_data(client, reg)); +} + +static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if (reg == LM77_REG_CONF) + return i2c_smbus_write_byte_data(client, reg, value); + else + return i2c_smbus_write_word_data(client, reg, swab16(value)); +} + +static void lm77_init_client(struct i2c_client *client) +{ + /* Initialize the LM77 chip - turn off shutdown mode */ + int conf = lm77_read_value(client, LM77_REG_CONF); + if (conf & 1) + lm77_write_value(client, LM77_REG_CONF, conf & 0xfe); +} + +static struct lm77_data *lm77_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm77_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + dev_dbg(&client->dev, "Starting lm77 update\n"); + data->temp_input = + LM77_TEMP_FROM_REG(lm77_read_value(client, + LM77_REG_TEMP)); + data->temp_hyst = + LM77_TEMP_FROM_REG(lm77_read_value(client, + LM77_REG_TEMP_HYST)); + data->temp_crit = + LM77_TEMP_FROM_REG(lm77_read_value(client, + LM77_REG_TEMP_CRIT)); + data->temp_min = + LM77_TEMP_FROM_REG(lm77_read_value(client, + LM77_REG_TEMP_MIN)); + data->temp_max = + LM77_TEMP_FROM_REG(lm77_read_value(client, + LM77_REG_TEMP_MAX)); + data->alarms = + lm77_read_value(client, LM77_REG_TEMP) & 0x0007; + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_lm77_init(void) +{ + return i2c_add_driver(&lm77_driver); +} + +static void __exit sensors_lm77_exit(void) +{ + i2c_del_driver(&lm77_driver); +} + +MODULE_AUTHOR("Andras BALI "); +MODULE_DESCRIPTION("LM77 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm77_init); +module_exit(sensors_lm77_exit); diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c new file mode 100644 index 000000000000..29241469dcba --- /dev/null +++ b/drivers/hwmon/lm78.c @@ -0,0 +1,795 @@ +/* + lm78.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, + 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, + 0x2f, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_3(lm78, lm78j, lm79); + +/* Many LM78 constants specified below */ + +/* Length of ISA address segment */ +#define LM78_EXTENT 8 + +/* Where are the ISA address/data registers relative to the base address */ +#define LM78_ADDR_REG_OFFSET 5 +#define LM78_DATA_REG_OFFSET 6 + +/* The LM78 registers */ +#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define LM78_REG_IN(nr) (0x20 + (nr)) + +#define LM78_REG_FAN_MIN(nr) (0x3b + (nr)) +#define LM78_REG_FAN(nr) (0x28 + (nr)) + +#define LM78_REG_TEMP 0x27 +#define LM78_REG_TEMP_OVER 0x39 +#define LM78_REG_TEMP_HYST 0x3a + +#define LM78_REG_ALARM1 0x41 +#define LM78_REG_ALARM2 0x42 + +#define LM78_REG_VID_FANDIV 0x47 + +#define LM78_REG_CONFIG 0x40 +#define LM78_REG_CHIPID 0x49 +#define LM78_REG_I2C_ADDR 0x48 + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. */ + +/* IN: mV, (0V to 4.08V) + REG: 16mV/bit */ +static inline u8 IN_TO_REG(unsigned long val) +{ + unsigned long nval = SENSORS_LIMIT(val, 0, 4080); + return (nval + 8) / 16; +} +#define IN_FROM_REG(val) ((val) * 16) + +static inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm <= 0) + return 255; + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); +} + +static inline int FAN_FROM_REG(u8 val, int div) +{ + return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div); +} + +/* TEMP: mC (-128C to +127C) + REG: 1C/bit, two's complement */ +static inline s8 TEMP_TO_REG(int val) +{ + int nval = SENSORS_LIMIT(val, -128000, 127000) ; + return nval<0 ? (nval-500)/1000 : (nval+500)/1000; +} + +static inline int TEMP_FROM_REG(s8 val) +{ + return val * 1000; +} + +/* VID: mV + REG: (see doc/vid) */ +static inline int VID_FROM_REG(u8 val) +{ + return val==0x1f ? 0 : val>=0x10 ? 5100-val*100 : 2050-val*50; +} + +#define DIV_FROM_REG(val) (1 << (val)) + +/* There are some complications in a module like this. First off, LM78 chips + may be both present on the SMBus and the ISA bus, and we have to handle + those cases separately at some places. Second, there might be several + LM78 chips available (well, actually, that is probably never done; but + it is a clean illustration of how to handle a case like that). Finally, + a specific chip may be attached to *both* ISA and SMBus, and we would + not like to detect it double. Fortunately, in the case of the LM78 at + least, a register tells us what SMBus address we are on, so that helps + a bit - except if there could be more than one SMBus. Groan. No solution + for this yet. */ + +/* This module may seem overly long and complicated. In fact, it is not so + bad. Quite a lot of bookkeeping is done. A real driver can often cut + some corners. */ + +/* For each registered LM78, we need to keep some data in memory. That + data is pointed to by lm78_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new lm78 client is + allocated. */ +struct lm78_data { + struct i2c_client client; + struct semaphore lock; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[7]; /* Register value */ + u8 in_max[7]; /* Register value */ + u8 in_min[7]; /* Register value */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + s8 temp; /* Register value */ + s8 temp_over; /* Register value */ + s8 temp_hyst; /* Register value */ + u8 fan_div[3]; /* Register encoding, shifted right */ + u8 vid; /* Register encoding, combined */ + u16 alarms; /* Register encoding, combined */ +}; + + +static int lm78_attach_adapter(struct i2c_adapter *adapter); +static int lm78_detect(struct i2c_adapter *adapter, int address, int kind); +static int lm78_detach_client(struct i2c_client *client); + +static int lm78_read_value(struct i2c_client *client, u8 register); +static int lm78_write_value(struct i2c_client *client, u8 register, u8 value); +static struct lm78_data *lm78_update_device(struct device *dev); +static void lm78_init_client(struct i2c_client *client); + + +static struct i2c_driver lm78_driver = { + .owner = THIS_MODULE, + .name = "lm78", + .id = I2C_DRIVERID_LM78, + .flags = I2C_DF_NOTIFY, + .attach_adapter = lm78_attach_adapter, + .detach_client = lm78_detach_client, +}; + +/* 7 Voltages */ +static ssize_t show_in(struct device *dev, char *buf, int nr) +{ + struct lm78_data *data = lm78_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); +} + +static ssize_t show_in_min(struct device *dev, char *buf, int nr) +{ + struct lm78_data *data = lm78_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); +} + +static ssize_t show_in_max(struct device *dev, char *buf, int nr) +{ + struct lm78_data *data = lm78_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); +} + +static ssize_t set_in_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm78_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->in_min[nr] = IN_TO_REG(val); + lm78_write_value(client, LM78_REG_IN_MIN(nr), data->in_min[nr]); + up(&data->update_lock); + return count; +} + +static ssize_t set_in_max(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm78_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->in_max[nr] = IN_TO_REG(val); + lm78_write_value(client, LM78_REG_IN_MAX(nr), data->in_max[nr]); + up(&data->update_lock); + return count; +} + +#define show_in_offset(offset) \ +static ssize_t \ + show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in(dev, buf, offset); \ +} \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ + show_in##offset, NULL); \ +static ssize_t \ + show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in_min(dev, buf, offset); \ +} \ +static ssize_t \ + show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in_max(dev, buf, offset); \ +} \ +static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_in_min(dev, buf, count, offset); \ +} \ +static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_in_max(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in##offset##_min, set_in##offset##_min); \ +static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in##offset##_max, set_in##offset##_max); + +show_in_offset(0); +show_in_offset(1); +show_in_offset(2); +show_in_offset(3); +show_in_offset(4); +show_in_offset(5); +show_in_offset(6); + +/* Temperature */ +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm78_data *data = lm78_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp)); +} + +static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm78_data *data = lm78_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over)); +} + +static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm78_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_over = TEMP_TO_REG(val); + lm78_write_value(client, LM78_REG_TEMP_OVER, data->temp_over); + up(&data->update_lock); + return count; +} + +static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm78_data *data = lm78_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst)); +} + +static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm78_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_hyst = TEMP_TO_REG(val); + lm78_write_value(client, LM78_REG_TEMP_HYST, data->temp_hyst); + up(&data->update_lock); + return count; +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); +static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, + show_temp_over, set_temp_over); +static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, + show_temp_hyst, set_temp_hyst); + +/* 3 Fans */ +static ssize_t show_fan(struct device *dev, char *buf, int nr) +{ + struct lm78_data *data = lm78_update_device(dev); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr])) ); +} + +static ssize_t show_fan_min(struct device *dev, char *buf, int nr) +{ + struct lm78_data *data = lm78_update_device(dev); + return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])) ); +} + +static ssize_t set_fan_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm78_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); + lm78_write_value(client, LM78_REG_FAN_MIN(nr), data->fan_min[nr]); + up(&data->update_lock); + return count; +} + +static ssize_t show_fan_div(struct device *dev, char *buf, int nr) +{ + struct lm78_data *data = lm78_update_device(dev); + return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) ); +} + +/* Note: we save and restore the fan minimum here, because its value is + determined in part by the fan divisor. This follows the principle of + least suprise; the user doesn't expect the fan minimum to change just + because the divisor changed. */ +static ssize_t set_fan_div(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm78_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + unsigned long min; + u8 reg; + + down(&data->update_lock); + min = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + + switch (val) { + case 1: data->fan_div[nr] = 0; break; + case 2: data->fan_div[nr] = 1; break; + case 4: data->fan_div[nr] = 2; break; + case 8: data->fan_div[nr] = 3; break; + default: + dev_err(&client->dev, "fan_div value %ld not " + "supported. Choose one of 1, 2, 4 or 8!\n", val); + up(&data->update_lock); + return -EINVAL; + } + + reg = lm78_read_value(client, LM78_REG_VID_FANDIV); + switch (nr) { + case 0: + reg = (reg & 0xcf) | (data->fan_div[nr] << 4); + break; + case 1: + reg = (reg & 0x3f) | (data->fan_div[nr] << 6); + break; + } + lm78_write_value(client, LM78_REG_VID_FANDIV, reg); + + data->fan_min[nr] = + FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); + lm78_write_value(client, LM78_REG_FAN_MIN(nr), data->fan_min[nr]); + up(&data->update_lock); + + return count; +} + +#define show_fan_offset(offset) \ +static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_min(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_div(dev, buf, offset - 1); \ +} \ +static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_fan_min(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan_##offset##_min, set_fan_##offset##_min); + +static ssize_t set_fan_1_div(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + return set_fan_div(dev, buf, count, 0) ; +} + +static ssize_t set_fan_2_div(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + return set_fan_div(dev, buf, count, 1) ; +} + +show_fan_offset(1); +show_fan_offset(2); +show_fan_offset(3); + +/* Fan 3 divisor is locked in H/W */ +static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, + show_fan_1_div, set_fan_1_div); +static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, + show_fan_2_div, set_fan_2_div); +static DEVICE_ATTR(fan3_div, S_IRUGO, show_fan_3_div, NULL); + +/* VID */ +static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm78_data *data = lm78_update_device(dev); + return sprintf(buf, "%d\n", VID_FROM_REG(data->vid)); +} +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + +/* Alarms */ +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm78_data *data = lm78_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); +} +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +/* This function is called when: + * lm78_driver is inserted (when this module is loaded), for each + available adapter + * when a new adapter is inserted (and lm78_driver is still present) */ +static int lm78_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, lm78_detect); +} + +/* This function is called by i2c_detect */ +int lm78_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int i, err; + struct i2c_client *new_client; + struct lm78_data *data; + const char *client_name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + + if (!is_isa && + !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + err = -ENODEV; + goto ERROR0; + } + + /* Reserve the ISA region */ + if (is_isa) + if (!request_region(address, LM78_EXTENT, lm78_driver.name)) { + err = -EBUSY; + goto ERROR0; + } + + /* Probe whether there is anything available on this address. Already + done for SMBus clients */ + if (kind < 0) { + if (is_isa) { + +#define REALLY_SLOW_IO + /* We need the timeouts for at least some LM78-like + chips. But only if we read 'undefined' registers. */ + i = inb_p(address + 1); + if (inb_p(address + 2) != i) { + err = -ENODEV; + goto ERROR1; + } + if (inb_p(address + 3) != i) { + err = -ENODEV; + goto ERROR1; + } + if (inb_p(address + 7) != i) { + err = -ENODEV; + goto ERROR1; + } +#undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f, address + 5); + if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { + outb_p(i, address + 5); + err = -ENODEV; + goto ERROR1; + } + } + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access lm78_{read,write}_value. */ + + if (!(data = kmalloc(sizeof(struct lm78_data), GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR1; + } + memset(data, 0, sizeof(struct lm78_data)); + + new_client = &data->client; + if (is_isa) + init_MUTEX(&data->lock); + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lm78_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + if (kind < 0) { + if (lm78_read_value(new_client, LM78_REG_CONFIG) & 0x80) { + err = -ENODEV; + goto ERROR2; + } + if (!is_isa && (lm78_read_value( + new_client, LM78_REG_I2C_ADDR) != address)) { + err = -ENODEV; + goto ERROR2; + } + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = lm78_read_value(new_client, LM78_REG_CHIPID); + if (i == 0x00 || i == 0x20) + kind = lm78; + else if (i == 0x40) + kind = lm78j; + 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); + err = -ENODEV; + goto ERROR2; + } + } + + if (kind == lm78) { + client_name = "lm78"; + } else if (kind == lm78j) { + client_name = "lm78-j"; + } else if (kind == lm79) { + client_name = "lm79"; + } + + /* Fill in the remaining client fields and put into the global list */ + strlcpy(new_client->name, client_name, I2C_NAME_SIZE); + data->type = kind; + + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR2; + + /* Initialize the LM78 chip */ + lm78_init_client(new_client); + + /* A few vars need to be filled upon startup */ + for (i = 0; i < 3; i++) { + data->fan_min[i] = lm78_read_value(new_client, + LM78_REG_FAN_MIN(i)); + } + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_in0_input); + device_create_file(&new_client->dev, &dev_attr_in0_min); + device_create_file(&new_client->dev, &dev_attr_in0_max); + device_create_file(&new_client->dev, &dev_attr_in1_input); + device_create_file(&new_client->dev, &dev_attr_in1_min); + device_create_file(&new_client->dev, &dev_attr_in1_max); + device_create_file(&new_client->dev, &dev_attr_in2_input); + device_create_file(&new_client->dev, &dev_attr_in2_min); + device_create_file(&new_client->dev, &dev_attr_in2_max); + device_create_file(&new_client->dev, &dev_attr_in3_input); + device_create_file(&new_client->dev, &dev_attr_in3_min); + device_create_file(&new_client->dev, &dev_attr_in3_max); + device_create_file(&new_client->dev, &dev_attr_in4_input); + device_create_file(&new_client->dev, &dev_attr_in4_min); + device_create_file(&new_client->dev, &dev_attr_in4_max); + device_create_file(&new_client->dev, &dev_attr_in5_input); + device_create_file(&new_client->dev, &dev_attr_in5_min); + device_create_file(&new_client->dev, &dev_attr_in5_max); + device_create_file(&new_client->dev, &dev_attr_in6_input); + device_create_file(&new_client->dev, &dev_attr_in6_min); + device_create_file(&new_client->dev, &dev_attr_in6_max); + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); + device_create_file(&new_client->dev, &dev_attr_fan1_input); + device_create_file(&new_client->dev, &dev_attr_fan1_min); + device_create_file(&new_client->dev, &dev_attr_fan1_div); + device_create_file(&new_client->dev, &dev_attr_fan2_input); + device_create_file(&new_client->dev, &dev_attr_fan2_min); + device_create_file(&new_client->dev, &dev_attr_fan2_div); + device_create_file(&new_client->dev, &dev_attr_fan3_input); + device_create_file(&new_client->dev, &dev_attr_fan3_min); + device_create_file(&new_client->dev, &dev_attr_fan3_div); + device_create_file(&new_client->dev, &dev_attr_alarms); + device_create_file(&new_client->dev, &dev_attr_cpu0_vid); + + return 0; + +ERROR2: + kfree(data); +ERROR1: + if (is_isa) + release_region(address, LM78_EXTENT); +ERROR0: + return err; +} + +static int lm78_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, + "Client deregistration failed, client not detached.\n"); + return err; + } + + if(i2c_is_isa_client(client)) + release_region(client->addr, LM78_EXTENT); + + kfree(i2c_get_clientdata(client)); + + return 0; +} + +/* The SMBus locks itself, but ISA access must be locked explicitly! + We don't want to lock the whole ISA bus, so we lock each client + separately. + We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, + would slow down the LM78 access and should not be necessary. */ +static int lm78_read_value(struct i2c_client *client, u8 reg) +{ + int res; + if (i2c_is_isa_client(client)) { + struct lm78_data *data = i2c_get_clientdata(client); + down(&data->lock); + outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); + res = inb_p(client->addr + LM78_DATA_REG_OFFSET); + up(&data->lock); + return res; + } else + return i2c_smbus_read_byte_data(client, reg); +} + +/* The SMBus locks itself, but ISA access muse be locked explicitly! + We don't want to lock the whole ISA bus, so we lock each client + separately. + We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, + would slow down the LM78 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +static int lm78_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + if (i2c_is_isa_client(client)) { + struct lm78_data *data = i2c_get_clientdata(client); + down(&data->lock); + outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); + outb_p(value, client->addr + LM78_DATA_REG_OFFSET); + up(&data->lock); + return 0; + } else + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new LM78. It should set limits, etc. */ +static void lm78_init_client(struct i2c_client *client) +{ + u8 config = lm78_read_value(client, LM78_REG_CONFIG); + + /* Start monitoring */ + if (!(config & 0x01)) + lm78_write_value(client, LM78_REG_CONFIG, + (config & 0xf7) | 0x01); +} + +static struct lm78_data *lm78_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm78_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + + dev_dbg(&client->dev, "Starting lm78 update\n"); + + for (i = 0; i <= 6; i++) { + data->in[i] = + lm78_read_value(client, LM78_REG_IN(i)); + data->in_min[i] = + lm78_read_value(client, LM78_REG_IN_MIN(i)); + data->in_max[i] = + lm78_read_value(client, LM78_REG_IN_MAX(i)); + } + for (i = 0; i < 3; i++) { + data->fan[i] = + lm78_read_value(client, LM78_REG_FAN(i)); + data->fan_min[i] = + lm78_read_value(client, LM78_REG_FAN_MIN(i)); + } + data->temp = lm78_read_value(client, LM78_REG_TEMP); + data->temp_over = + lm78_read_value(client, LM78_REG_TEMP_OVER); + data->temp_hyst = + lm78_read_value(client, LM78_REG_TEMP_HYST); + i = lm78_read_value(client, LM78_REG_VID_FANDIV); + data->vid = i & 0x0f; + if (data->type == lm79) + data->vid |= + (lm78_read_value(client, LM78_REG_CHIPID) & + 0x01) << 4; + else + data->vid |= 0x10; + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = lm78_read_value(client, LM78_REG_ALARM1) + + (lm78_read_value(client, LM78_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + + data->fan_div[2] = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sm_lm78_init(void) +{ + return i2c_add_driver(&lm78_driver); +} + +static void __exit sm_lm78_exit(void) +{ + i2c_del_driver(&lm78_driver); +} + + + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver"); +MODULE_LICENSE("GPL"); + +module_init(sm_lm78_init); +module_exit(sm_lm78_exit); diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c new file mode 100644 index 000000000000..8100595feb44 --- /dev/null +++ b/drivers/hwmon/lm80.c @@ -0,0 +1,601 @@ +/* + * lm80.c - From lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 1998, 1999 Frodo Looijaard + * and Philip Edelbrock + * + * Ported to Linux 2.6 by Tiago Sousa + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(lm80); + +/* Many LM80 constants specified below */ + +/* The LM80 registers */ +#define LM80_REG_IN_MAX(nr) (0x2a + (nr) * 2) +#define LM80_REG_IN_MIN(nr) (0x2b + (nr) * 2) +#define LM80_REG_IN(nr) (0x20 + (nr)) + +#define LM80_REG_FAN1 0x28 +#define LM80_REG_FAN2 0x29 +#define LM80_REG_FAN_MIN(nr) (0x3b + (nr)) + +#define LM80_REG_TEMP 0x27 +#define LM80_REG_TEMP_HOT_MAX 0x38 +#define LM80_REG_TEMP_HOT_HYST 0x39 +#define LM80_REG_TEMP_OS_MAX 0x3a +#define LM80_REG_TEMP_OS_HYST 0x3b + +#define LM80_REG_CONFIG 0x00 +#define LM80_REG_ALARM1 0x01 +#define LM80_REG_ALARM2 0x02 +#define LM80_REG_MASK1 0x03 +#define LM80_REG_MASK2 0x04 +#define LM80_REG_FANDIV 0x05 +#define LM80_REG_RES 0x06 + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + +#define IN_TO_REG(val) (SENSORS_LIMIT(((val)+5)/10,0,255)) +#define IN_FROM_REG(val) ((val)*10) + +static inline unsigned char FAN_TO_REG(unsigned rpm, unsigned div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm*div / 2) / (rpm*div), 1, 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:\ + (val)==255?0:1350000/((div)*(val))) + +static inline long TEMP_FROM_REG(u16 temp) +{ + long res; + + temp >>= 4; + if (temp < 0x0800) + res = 625 * (long) temp; + else + res = ((long) temp - 0x01000) * 625; + + return res / 10; +} + +#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*1000) + +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val)<0?\ + ((val)-500)/1000:((val)+500)/1000,0,255) + +#define DIV_FROM_REG(val) (1 << (val)) + +/* + * Client data (each client gets its own) + */ + +struct lm80_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[7]; /* Register value */ + u8 in_max[7]; /* Register value */ + u8 in_min[7]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u16 temp; /* Register values, shifted right */ + u8 temp_hot_max; /* Register value */ + u8 temp_hot_hyst; /* Register value */ + u8 temp_os_max; /* Register value */ + u8 temp_os_hyst; /* Register value */ + u16 alarms; /* Register encoding, combined */ +}; + +/* + * Functions declaration + */ + +static int lm80_attach_adapter(struct i2c_adapter *adapter); +static int lm80_detect(struct i2c_adapter *adapter, int address, int kind); +static void lm80_init_client(struct i2c_client *client); +static int lm80_detach_client(struct i2c_client *client); +static struct lm80_data *lm80_update_device(struct device *dev); +static int lm80_read_value(struct i2c_client *client, u8 reg); +static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver lm80_driver = { + .owner = THIS_MODULE, + .name = "lm80", + .id = I2C_DRIVERID_LM80, + .flags = I2C_DF_NOTIFY, + .attach_adapter = lm80_attach_adapter, + .detach_client = lm80_detach_client, +}; + +/* + * Sysfs stuff + */ + +#define show_in(suffix, value) \ +static ssize_t show_in_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm80_data *data = lm80_update_device(dev); \ + return sprintf(buf, "%d\n", IN_FROM_REG(data->value)); \ +} +show_in(min0, in_min[0]); +show_in(min1, in_min[1]); +show_in(min2, in_min[2]); +show_in(min3, in_min[3]); +show_in(min4, in_min[4]); +show_in(min5, in_min[5]); +show_in(min6, in_min[6]); +show_in(max0, in_max[0]); +show_in(max1, in_max[1]); +show_in(max2, in_max[2]); +show_in(max3, in_max[3]); +show_in(max4, in_max[4]); +show_in(max5, in_max[5]); +show_in(max6, in_max[6]); +show_in(input0, in[0]); +show_in(input1, in[1]); +show_in(input2, in[2]); +show_in(input3, in[3]); +show_in(input4, in[4]); +show_in(input5, in[5]); +show_in(input6, in[6]); + +#define set_in(suffix, value, reg) \ +static ssize_t set_in_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm80_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock);\ + data->value = IN_TO_REG(val); \ + lm80_write_value(client, reg, data->value); \ + up(&data->update_lock);\ + return count; \ +} +set_in(min0, in_min[0], LM80_REG_IN_MIN(0)); +set_in(min1, in_min[1], LM80_REG_IN_MIN(1)); +set_in(min2, in_min[2], LM80_REG_IN_MIN(2)); +set_in(min3, in_min[3], LM80_REG_IN_MIN(3)); +set_in(min4, in_min[4], LM80_REG_IN_MIN(4)); +set_in(min5, in_min[5], LM80_REG_IN_MIN(5)); +set_in(min6, in_min[6], LM80_REG_IN_MIN(6)); +set_in(max0, in_max[0], LM80_REG_IN_MAX(0)); +set_in(max1, in_max[1], LM80_REG_IN_MAX(1)); +set_in(max2, in_max[2], LM80_REG_IN_MAX(2)); +set_in(max3, in_max[3], LM80_REG_IN_MAX(3)); +set_in(max4, in_max[4], LM80_REG_IN_MAX(4)); +set_in(max5, in_max[5], LM80_REG_IN_MAX(5)); +set_in(max6, in_max[6], LM80_REG_IN_MAX(6)); + +#define show_fan(suffix, value, div) \ +static ssize_t show_fan_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm80_data *data = lm80_update_device(dev); \ + return sprintf(buf, "%d\n", FAN_FROM_REG(data->value, \ + DIV_FROM_REG(data->div))); \ +} +show_fan(min1, fan_min[0], fan_div[0]); +show_fan(min2, fan_min[1], fan_div[1]); +show_fan(input1, fan[0], fan_div[0]); +show_fan(input2, fan[1], fan_div[1]); + +#define show_fan_div(suffix, value) \ +static ssize_t show_fan_div##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm80_data *data = lm80_update_device(dev); \ + return sprintf(buf, "%d\n", DIV_FROM_REG(data->value)); \ +} +show_fan_div(1, fan_div[0]); +show_fan_div(2, fan_div[1]); + +#define set_fan(suffix, value, reg, div) \ +static ssize_t set_fan_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm80_data *data = i2c_get_clientdata(client); \ + long val = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock);\ + data->value = FAN_TO_REG(val, DIV_FROM_REG(data->div)); \ + lm80_write_value(client, reg, data->value); \ + up(&data->update_lock);\ + return count; \ +} +set_fan(min1, fan_min[0], LM80_REG_FAN_MIN(1), fan_div[0]); +set_fan(min2, fan_min[1], LM80_REG_FAN_MIN(2), fan_div[1]); + +/* Note: we save and restore the fan minimum here, because its value is + determined in part by the fan divisor. This follows the principle of + least suprise; the user doesn't expect the fan minimum to change just + because the divisor changed. */ +static ssize_t set_fan_div(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm80_data *data = i2c_get_clientdata(client); + unsigned long min, val = simple_strtoul(buf, NULL, 10); + u8 reg; + + /* Save fan_min */ + down(&data->update_lock); + min = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + + switch (val) { + case 1: data->fan_div[nr] = 0; break; + case 2: data->fan_div[nr] = 1; break; + case 4: data->fan_div[nr] = 2; break; + case 8: data->fan_div[nr] = 3; break; + default: + dev_err(&client->dev, "fan_div value %ld not " + "supported. Choose one of 1, 2, 4 or 8!\n", val); + up(&data->update_lock); + return -EINVAL; + } + + reg = (lm80_read_value(client, LM80_REG_FANDIV) & ~(3 << (2 * (nr + 1)))) + | (data->fan_div[nr] << (2 * (nr + 1))); + lm80_write_value(client, LM80_REG_FANDIV, reg); + + /* Restore fan_min */ + data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); + lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), data->fan_min[nr]); + up(&data->update_lock); + + return count; +} + +#define set_fan_div(number) \ +static ssize_t set_fan_div##number(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return set_fan_div(dev, buf, count, number - 1); \ +} +set_fan_div(1); +set_fan_div(2); + +static ssize_t show_temp_input1(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm80_data *data = lm80_update_device(dev); + return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp)); +} + +#define show_temp(suffix, value) \ +static ssize_t show_temp_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm80_data *data = lm80_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_LIMIT_FROM_REG(data->value)); \ +} +show_temp(hot_max, temp_hot_max); +show_temp(hot_hyst, temp_hot_hyst); +show_temp(os_max, temp_os_max); +show_temp(os_hyst, temp_os_hyst); + +#define set_temp(suffix, value, reg) \ +static ssize_t set_temp_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm80_data *data = i2c_get_clientdata(client); \ + long val = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->value = TEMP_LIMIT_TO_REG(val); \ + lm80_write_value(client, reg, data->value); \ + up(&data->update_lock); \ + return count; \ +} +set_temp(hot_max, temp_hot_max, LM80_REG_TEMP_HOT_MAX); +set_temp(hot_hyst, temp_hot_hyst, LM80_REG_TEMP_HOT_HYST); +set_temp(os_max, temp_os_max, LM80_REG_TEMP_OS_MAX); +set_temp(os_hyst, temp_os_hyst, LM80_REG_TEMP_OS_HYST); + +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm80_data *data = lm80_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); +} + +static DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min0, set_in_min0); +static DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min1, set_in_min1); +static DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min2, set_in_min2); +static DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min3, set_in_min3); +static DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min4, set_in_min4); +static DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min5, set_in_min5); +static DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min6, set_in_min6); +static DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max0, set_in_max0); +static DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max1, set_in_max1); +static DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max2, set_in_max2); +static DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max3, set_in_max3); +static DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max4, set_in_max4); +static DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max5, set_in_max5); +static DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max6, set_in_max6); +static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input0, NULL); +static DEVICE_ATTR(in1_input, S_IRUGO, show_in_input1, NULL); +static DEVICE_ATTR(in2_input, S_IRUGO, show_in_input2, NULL); +static DEVICE_ATTR(in3_input, S_IRUGO, show_in_input3, NULL); +static DEVICE_ATTR(in4_input, S_IRUGO, show_in_input4, NULL); +static DEVICE_ATTR(in5_input, S_IRUGO, show_in_input5, NULL); +static DEVICE_ATTR(in6_input, S_IRUGO, show_in_input6, NULL); +static DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min1, + set_fan_min1); +static DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min2, + set_fan_min2); +static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input1, NULL); +static DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input2, NULL); +static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div1, set_fan_div1); +static DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div2, set_fan_div2); +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); +static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_hot_max, + set_temp_hot_max); +static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_hot_hyst, + set_temp_hot_hyst); +static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_os_max, + set_temp_os_max); +static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_os_hyst, + set_temp_os_hyst); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +/* + * Real code + */ + +static int lm80_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, lm80_detect); +} + +int lm80_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int i, cur; + struct i2c_client *new_client; + struct lm80_data *data; + int err = 0; + const char *name; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access lm80_{read,write}_value. */ + if (!(data = kmalloc(sizeof(struct lm80_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct lm80_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lm80_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is lousy. */ + if (lm80_read_value(new_client, LM80_REG_ALARM2) & 0xc0) + goto error_free; + for (i = 0x2a; i <= 0x3d; i++) { + cur = i2c_smbus_read_byte_data(new_client, i); + if ((i2c_smbus_read_byte_data(new_client, i + 0x40) != cur) + || (i2c_smbus_read_byte_data(new_client, i + 0x80) != cur) + || (i2c_smbus_read_byte_data(new_client, i + 0xc0) != cur)) + goto error_free; + } + + /* Determine the chip type - only one kind supported! */ + kind = lm80; + name = "lm80"; + + /* Fill in the remaining client fields and put it into the global list */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto error_free; + + /* Initialize the LM80 chip */ + lm80_init_client(new_client); + + /* A few vars need to be filled upon startup */ + data->fan_min[0] = lm80_read_value(new_client, LM80_REG_FAN_MIN(1)); + data->fan_min[1] = lm80_read_value(new_client, LM80_REG_FAN_MIN(2)); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_in0_min); + device_create_file(&new_client->dev, &dev_attr_in1_min); + device_create_file(&new_client->dev, &dev_attr_in2_min); + device_create_file(&new_client->dev, &dev_attr_in3_min); + device_create_file(&new_client->dev, &dev_attr_in4_min); + device_create_file(&new_client->dev, &dev_attr_in5_min); + device_create_file(&new_client->dev, &dev_attr_in6_min); + device_create_file(&new_client->dev, &dev_attr_in0_max); + device_create_file(&new_client->dev, &dev_attr_in1_max); + device_create_file(&new_client->dev, &dev_attr_in2_max); + device_create_file(&new_client->dev, &dev_attr_in3_max); + device_create_file(&new_client->dev, &dev_attr_in4_max); + device_create_file(&new_client->dev, &dev_attr_in5_max); + device_create_file(&new_client->dev, &dev_attr_in6_max); + device_create_file(&new_client->dev, &dev_attr_in0_input); + device_create_file(&new_client->dev, &dev_attr_in1_input); + device_create_file(&new_client->dev, &dev_attr_in2_input); + device_create_file(&new_client->dev, &dev_attr_in3_input); + device_create_file(&new_client->dev, &dev_attr_in4_input); + device_create_file(&new_client->dev, &dev_attr_in5_input); + device_create_file(&new_client->dev, &dev_attr_in6_input); + device_create_file(&new_client->dev, &dev_attr_fan1_min); + device_create_file(&new_client->dev, &dev_attr_fan2_min); + device_create_file(&new_client->dev, &dev_attr_fan1_input); + device_create_file(&new_client->dev, &dev_attr_fan2_input); + device_create_file(&new_client->dev, &dev_attr_fan1_div); + device_create_file(&new_client->dev, &dev_attr_fan2_div); + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); + device_create_file(&new_client->dev, &dev_attr_temp1_crit); + device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst); + device_create_file(&new_client->dev, &dev_attr_alarms); + + return 0; + +error_free: + kfree(data); +exit: + return err; +} + +static int lm80_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static int lm80_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new LM80. */ +static void lm80_init_client(struct i2c_client *client) +{ + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others. This makes most other + initializations unnecessary */ + lm80_write_value(client, LM80_REG_CONFIG, 0x80); + /* Set 11-bit temperature resolution */ + lm80_write_value(client, LM80_REG_RES, 0x08); + + /* Start monitoring */ + lm80_write_value(client, LM80_REG_CONFIG, 0x01); +} + +static struct lm80_data *lm80_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm80_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { + dev_dbg(&client->dev, "Starting lm80 update\n"); + for (i = 0; i <= 6; i++) { + data->in[i] = + lm80_read_value(client, LM80_REG_IN(i)); + data->in_min[i] = + lm80_read_value(client, LM80_REG_IN_MIN(i)); + data->in_max[i] = + lm80_read_value(client, LM80_REG_IN_MAX(i)); + } + data->fan[0] = lm80_read_value(client, LM80_REG_FAN1); + data->fan_min[0] = + lm80_read_value(client, LM80_REG_FAN_MIN(1)); + data->fan[1] = lm80_read_value(client, LM80_REG_FAN2); + data->fan_min[1] = + lm80_read_value(client, LM80_REG_FAN_MIN(2)); + + data->temp = + (lm80_read_value(client, LM80_REG_TEMP) << 8) | + (lm80_read_value(client, LM80_REG_RES) & 0xf0); + data->temp_os_max = + lm80_read_value(client, LM80_REG_TEMP_OS_MAX); + data->temp_os_hyst = + lm80_read_value(client, LM80_REG_TEMP_OS_HYST); + data->temp_hot_max = + lm80_read_value(client, LM80_REG_TEMP_HOT_MAX); + data->temp_hot_hyst = + lm80_read_value(client, LM80_REG_TEMP_HOT_HYST); + + i = lm80_read_value(client, LM80_REG_FANDIV); + data->fan_div[0] = (i >> 2) & 0x03; + data->fan_div[1] = (i >> 4) & 0x03; + data->alarms = lm80_read_value(client, LM80_REG_ALARM1) + + (lm80_read_value(client, LM80_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_lm80_init(void) +{ + return i2c_add_driver(&lm80_driver); +} + +static void __exit sensors_lm80_exit(void) +{ + i2c_del_driver(&lm80_driver); +} + +MODULE_AUTHOR("Frodo Looijaard and " + "Philip Edelbrock "); +MODULE_DESCRIPTION("LM80 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm80_init); +module_exit(sensors_lm80_exit); diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c new file mode 100644 index 000000000000..a49008b444c8 --- /dev/null +++ b/drivers/hwmon/lm83.c @@ -0,0 +1,408 @@ +/* + * lm83.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2003-2005 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 + * temperatures (its own plus up to three external ones) with a 1 deg + * resolution and a 3-4 deg accuracy. Complete datasheet can be obtained + * from National's website at: + * http://www.national.com/pf/LM/LM83.html + * Since the datasheet omits to give the chip stepping code, I give it + * here: 0x03 (at register 0xff). + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Addresses to scan + * Address is selected using 2 three-level pins, resulting in 9 possible + * addresses. + */ + +static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, + 0x29, 0x2a, 0x2b, + 0x4c, 0x4d, 0x4e, + I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* + * Insmod parameters + */ + +SENSORS_INSMOD_1(lm83); + +/* + * The LM83 registers + * Manufacturer ID is 0x01 for National Semiconductor. + */ + +#define LM83_REG_R_MAN_ID 0xFE +#define LM83_REG_R_CHIP_ID 0xFF +#define LM83_REG_R_CONFIG 0x03 +#define LM83_REG_W_CONFIG 0x09 +#define LM83_REG_R_STATUS1 0x02 +#define LM83_REG_R_STATUS2 0x35 +#define LM83_REG_R_LOCAL_TEMP 0x00 +#define LM83_REG_R_LOCAL_HIGH 0x05 +#define LM83_REG_W_LOCAL_HIGH 0x0B +#define LM83_REG_R_REMOTE1_TEMP 0x30 +#define LM83_REG_R_REMOTE1_HIGH 0x38 +#define LM83_REG_W_REMOTE1_HIGH 0x50 +#define LM83_REG_R_REMOTE2_TEMP 0x01 +#define LM83_REG_R_REMOTE2_HIGH 0x07 +#define LM83_REG_W_REMOTE2_HIGH 0x0D +#define LM83_REG_R_REMOTE3_TEMP 0x31 +#define LM83_REG_R_REMOTE3_HIGH 0x3A +#define LM83_REG_W_REMOTE3_HIGH 0x52 +#define LM83_REG_R_TCRIT 0x42 +#define LM83_REG_W_TCRIT 0x5A + +/* + * Conversions and various macros + * The LM83 uses signed 8-bit values with LSB = 1 degree Celsius. + */ + +#define TEMP_FROM_REG(val) ((val) * 1000) +#define TEMP_TO_REG(val) ((val) <= -128000 ? -128 : \ + (val) >= 127000 ? 127 : \ + (val) < 0 ? ((val) - 500) / 1000 : \ + ((val) + 500) / 1000) + +static const u8 LM83_REG_R_TEMP[] = { + LM83_REG_R_LOCAL_TEMP, + LM83_REG_R_REMOTE1_TEMP, + LM83_REG_R_REMOTE2_TEMP, + LM83_REG_R_REMOTE3_TEMP, + LM83_REG_R_LOCAL_HIGH, + LM83_REG_R_REMOTE1_HIGH, + LM83_REG_R_REMOTE2_HIGH, + LM83_REG_R_REMOTE3_HIGH, + LM83_REG_R_TCRIT, +}; + +static const u8 LM83_REG_W_HIGH[] = { + LM83_REG_W_LOCAL_HIGH, + LM83_REG_W_REMOTE1_HIGH, + LM83_REG_W_REMOTE2_HIGH, + LM83_REG_W_REMOTE3_HIGH, + LM83_REG_W_TCRIT, +}; + +/* + * Functions declaration + */ + +static int lm83_attach_adapter(struct i2c_adapter *adapter); +static int lm83_detect(struct i2c_adapter *adapter, int address, int kind); +static int lm83_detach_client(struct i2c_client *client); +static struct lm83_data *lm83_update_device(struct device *dev); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver lm83_driver = { + .owner = THIS_MODULE, + .name = "lm83", + .id = I2C_DRIVERID_LM83, + .flags = I2C_DF_NOTIFY, + .attach_adapter = lm83_attach_adapter, + .detach_client = lm83_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct lm83_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* registers values */ + s8 temp[9]; /* 0..3: input 1-4, + 4..7: high limit 1-4, + 8 : critical limit */ + u16 alarms; /* bitvector, combined */ +}; + +/* + * Sysfs stuff + */ + +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm83_data *data = lm83_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); +} + +static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct lm83_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + int nr = attr->index; + + down(&data->update_lock); + data->temp[nr] = TEMP_TO_REG(val); + i2c_smbus_write_byte_data(client, LM83_REG_W_HIGH[nr - 4], + data->temp[nr]); + up(&data->update_lock); + return count; +} + +static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, + char *buf) +{ + struct lm83_data *data = lm83_update_device(dev); + return sprintf(buf, "%d\n", data->alarms); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, + set_temp, 4); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp, + set_temp, 5); +static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_temp, + set_temp, 6); +static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_temp, + set_temp, 7); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL, 8); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp, NULL, 8); +static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp, + set_temp, 8); +static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp, NULL, 8); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +/* + * Real code + */ + +static int lm83_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, lm83_detect); +} + +/* + * The following function does more than just detection. If detection + * succeeds, it also registers the new chip. + */ +static int lm83_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct lm83_data *data; + int err = 0; + const char *name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct lm83_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct lm83_data)); + + /* The common I2C client data is placed right after the + * LM83-specific data. */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lm83_driver; + new_client->flags = 0; + + /* 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", address); + goto exit_free; + } + } + + 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; + } + } + + if (kind <= 0) { /* identification failed */ + dev_info(&adapter->dev, + "Unsupported chip (man_id=0x%02X, " + "chip_id=0x%02X).\n", man_id, chip_id); + goto exit_free; + } + } + + if (kind == lm83) { + name = "lm83"; + } + + /* We can fill in the remaining client fields */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* + * Initialize the LM83 chip + * (Nothing to do for this one.) + */ + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_input.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_input.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp3_input.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp4_input.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_max.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_max.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp3_max.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp4_max.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_crit.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_crit.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp3_crit.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp4_crit.dev_attr); + device_create_file(&new_client->dev, &dev_attr_alarms); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static int lm83_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, + "Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static struct lm83_data *lm83_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm83_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { + int nr; + + dev_dbg(&client->dev, "Updating lm83 data.\n"); + for (nr = 0; nr < 9; nr++) { + data->temp[nr] = + i2c_smbus_read_byte_data(client, + LM83_REG_R_TEMP[nr]); + } + data->alarms = + i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1) + + (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2) + << 8); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_lm83_init(void) +{ + return i2c_add_driver(&lm83_driver); +} + +static void __exit sensors_lm83_exit(void) +{ + i2c_del_driver(&lm83_driver); +} + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("LM83 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm83_init); +module_exit(sensors_lm83_exit); diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c new file mode 100644 index 000000000000..b4d7fd418264 --- /dev/null +++ b/drivers/hwmon/lm85.c @@ -0,0 +1,1575 @@ +/* + lm85.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (c) 2002, 2003 Philip Pokorny + Copyright (c) 2003 Margit Schubert-While + Copyright (c) 2004 Justin Thiessen + + Chip details at + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102); + +/* The LM85 registers */ + +#define LM85_REG_IN(nr) (0x20 + (nr)) +#define LM85_REG_IN_MIN(nr) (0x44 + (nr) * 2) +#define LM85_REG_IN_MAX(nr) (0x45 + (nr) * 2) + +#define LM85_REG_TEMP(nr) (0x25 + (nr)) +#define LM85_REG_TEMP_MIN(nr) (0x4e + (nr) * 2) +#define LM85_REG_TEMP_MAX(nr) (0x4f + (nr) * 2) + +/* Fan speeds are LSB, MSB (2 bytes) */ +#define LM85_REG_FAN(nr) (0x28 + (nr) *2) +#define LM85_REG_FAN_MIN(nr) (0x54 + (nr) *2) + +#define LM85_REG_PWM(nr) (0x30 + (nr)) + +#define ADT7463_REG_OPPOINT(nr) (0x33 + (nr)) + +#define ADT7463_REG_TMIN_CTL1 0x36 +#define ADT7463_REG_TMIN_CTL2 0x37 + +#define LM85_REG_DEVICE 0x3d +#define LM85_REG_COMPANY 0x3e +#define LM85_REG_VERSTEP 0x3f +/* These are the recognized values for the above regs */ +#define LM85_DEVICE_ADX 0x27 +#define LM85_COMPANY_NATIONAL 0x01 +#define LM85_COMPANY_ANALOG_DEV 0x41 +#define LM85_COMPANY_SMSC 0x5c +#define LM85_VERSTEP_VMASK 0xf0 +#define LM85_VERSTEP_GENERIC 0x60 +#define LM85_VERSTEP_LM85C 0x60 +#define LM85_VERSTEP_LM85B 0x62 +#define LM85_VERSTEP_ADM1027 0x60 +#define LM85_VERSTEP_ADT7463 0x62 +#define LM85_VERSTEP_ADT7463C 0x6A +#define LM85_VERSTEP_EMC6D100_A0 0x60 +#define LM85_VERSTEP_EMC6D100_A1 0x61 +#define LM85_VERSTEP_EMC6D102 0x65 + +#define LM85_REG_CONFIG 0x40 + +#define LM85_REG_ALARM1 0x41 +#define LM85_REG_ALARM2 0x42 + +#define LM85_REG_VID 0x43 + +/* Automated FAN control */ +#define LM85_REG_AFAN_CONFIG(nr) (0x5c + (nr)) +#define LM85_REG_AFAN_RANGE(nr) (0x5f + (nr)) +#define LM85_REG_AFAN_SPIKE1 0x62 +#define LM85_REG_AFAN_SPIKE2 0x63 +#define LM85_REG_AFAN_MINPWM(nr) (0x64 + (nr)) +#define LM85_REG_AFAN_LIMIT(nr) (0x67 + (nr)) +#define LM85_REG_AFAN_CRITICAL(nr) (0x6a + (nr)) +#define LM85_REG_AFAN_HYST1 0x6d +#define LM85_REG_AFAN_HYST2 0x6e + +#define LM85_REG_TACH_MODE 0x74 +#define LM85_REG_SPINUP_CTL 0x75 + +#define ADM1027_REG_TEMP_OFFSET(nr) (0x70 + (nr)) +#define ADM1027_REG_CONFIG2 0x73 +#define ADM1027_REG_INTMASK1 0x74 +#define ADM1027_REG_INTMASK2 0x75 +#define ADM1027_REG_EXTEND_ADC1 0x76 +#define ADM1027_REG_EXTEND_ADC2 0x77 +#define ADM1027_REG_CONFIG3 0x78 +#define ADM1027_REG_FAN_PPR 0x7b + +#define ADT7463_REG_THERM 0x79 +#define ADT7463_REG_THERM_LIMIT 0x7A + +#define EMC6D100_REG_ALARM3 0x7d +/* IN5, IN6 and IN7 */ +#define EMC6D100_REG_IN(nr) (0x70 + ((nr)-5)) +#define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr)-5) * 2) +#define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr)-5) * 2) +#define EMC6D102_REG_EXTEND_ADC1 0x85 +#define EMC6D102_REG_EXTEND_ADC2 0x86 +#define EMC6D102_REG_EXTEND_ADC3 0x87 +#define EMC6D102_REG_EXTEND_ADC4 0x88 + +#define LM85_ALARM_IN0 0x0001 +#define LM85_ALARM_IN1 0x0002 +#define LM85_ALARM_IN2 0x0004 +#define LM85_ALARM_IN3 0x0008 +#define LM85_ALARM_TEMP1 0x0010 +#define LM85_ALARM_TEMP2 0x0020 +#define LM85_ALARM_TEMP3 0x0040 +#define LM85_ALARM_ALARM2 0x0080 +#define LM85_ALARM_IN4 0x0100 +#define LM85_ALARM_RESERVED 0x0200 +#define LM85_ALARM_FAN1 0x0400 +#define LM85_ALARM_FAN2 0x0800 +#define LM85_ALARM_FAN3 0x1000 +#define LM85_ALARM_FAN4 0x2000 +#define LM85_ALARM_TEMP1_FAULT 0x4000 +#define LM85_ALARM_TEMP3_FAULT 0x8000 + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + */ + +/* IN are scaled acording to built-in resistors */ +static int lm85_scaling[] = { /* .001 Volts */ + 2500, 2250, 3300, 5000, 12000, + 3300, 1500, 1800 /*EMC6D100*/ + }; +#define SCALE(val,from,to) (((val)*(to) + ((from)/2))/(from)) + +#define INS_TO_REG(n,val) \ + SENSORS_LIMIT(SCALE(val,lm85_scaling[n],192),0,255) + +#define INSEXT_FROM_REG(n,val,ext,scale) \ + SCALE((val)*(scale) + (ext),192*(scale),lm85_scaling[n]) + +#define INS_FROM_REG(n,val) INSEXT_FROM_REG(n,val,0,1) + +/* FAN speed is measured using 90kHz clock */ +#define FAN_TO_REG(val) (SENSORS_LIMIT( (val)<=0?0: 5400000/(val),0,65534)) +#define FAN_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:5400000/(val)) + +/* Temperature is reported in .001 degC increments */ +#define TEMP_TO_REG(val) \ + SENSORS_LIMIT(SCALE(val,1000,1),-127,127) +#define TEMPEXT_FROM_REG(val,ext,scale) \ + SCALE((val)*scale + (ext),scale,1000) +#define TEMP_FROM_REG(val) \ + TEMPEXT_FROM_REG(val,0,1) + +#define PWM_TO_REG(val) (SENSORS_LIMIT(val,0,255)) +#define PWM_FROM_REG(val) (val) + + +/* ZONEs have the following parameters: + * Limit (low) temp, 1. degC + * Hysteresis (below limit), 1. degC (0-15) + * Range of speed control, .1 degC (2-80) + * Critical (high) temp, 1. degC + * + * FAN PWMs have the following parameters: + * Reference Zone, 1, 2, 3, etc. + * Spinup time, .05 sec + * PWM value at limit/low temp, 1 count + * PWM Frequency, 1. Hz + * PWM is Min or OFF below limit, flag + * Invert PWM output, flag + * + * Some chips filter the temp, others the fan. + * Filter constant (or disabled) .1 seconds + */ + +/* These are the zone temperature range encodings in .001 degree C */ +static int lm85_range_map[] = { + 2000, 2500, 3300, 4000, 5000, 6600, + 8000, 10000, 13300, 16000, 20000, 26600, + 32000, 40000, 53300, 80000 + }; +static int RANGE_TO_REG( int range ) +{ + int i; + + if ( range < lm85_range_map[0] ) { + return 0 ; + } else if ( range > lm85_range_map[15] ) { + return 15 ; + } else { /* find closest match */ + for ( i = 14 ; i >= 0 ; --i ) { + if ( range > lm85_range_map[i] ) { /* range bracketed */ + if ((lm85_range_map[i+1] - range) < + (range - lm85_range_map[i])) { + i++; + break; + } + break; + } + } + } + return( i & 0x0f ); +} +#define RANGE_FROM_REG(val) (lm85_range_map[(val)&0x0f]) + +/* These are the Acoustic Enhancement, or Temperature smoothing encodings + * NOTE: The enable/disable bit is INCLUDED in these encodings as the + * MSB (bit 3, value 8). If the enable bit is 0, the encoded value + * is ignored, or set to 0. + */ +/* These are the PWM frequency encodings */ +static int lm85_freq_map[] = { /* .1 Hz */ + 100, 150, 230, 300, 380, 470, 620, 940 + }; +static int FREQ_TO_REG( int freq ) +{ + int i; + + if( freq >= lm85_freq_map[7] ) { return 7 ; } + for( i = 0 ; i < 7 ; ++i ) + if( freq <= lm85_freq_map[i] ) + break ; + return( i & 0x07 ); +} +#define FREQ_FROM_REG(val) (lm85_freq_map[(val)&0x07]) + +/* Since we can't use strings, I'm abusing these numbers + * to stand in for the following meanings: + * 1 -- PWM responds to Zone 1 + * 2 -- PWM responds to Zone 2 + * 3 -- PWM responds to Zone 3 + * 23 -- PWM responds to the higher temp of Zone 2 or 3 + * 123 -- PWM responds to highest of Zone 1, 2, or 3 + * 0 -- PWM is always at 0% (ie, off) + * -1 -- PWM is always at 100% + * -2 -- PWM responds to manual control + */ + +static int lm85_zone_map[] = { 1, 2, 3, -1, 0, 23, 123, -2 }; +#define ZONE_FROM_REG(val) (lm85_zone_map[((val)>>5)&0x07]) + +static int ZONE_TO_REG( int zone ) +{ + int i; + + for( i = 0 ; i <= 7 ; ++i ) + if( zone == lm85_zone_map[i] ) + break ; + if( i > 7 ) /* Not found. */ + i = 3; /* Always 100% */ + return( (i & 0x07)<<5 ); +} + +#define HYST_TO_REG(val) (SENSORS_LIMIT(((val)+500)/1000,0,15)) +#define HYST_FROM_REG(val) ((val)*1000) + +#define OFFSET_TO_REG(val) (SENSORS_LIMIT((val)/25,-127,127)) +#define OFFSET_FROM_REG(val) ((val)*25) + +#define PPR_MASK(fan) (0x03<<(fan *2)) +#define PPR_TO_REG(val,fan) (SENSORS_LIMIT((val)-1,0,3)<<(fan *2)) +#define PPR_FROM_REG(val,fan) ((((val)>>(fan * 2))&0x03)+1) + +/* i2c-vid.h defines vid_from_reg() */ +#define VID_FROM_REG(val,vrm) (vid_from_reg((val),(vrm))) + +/* Unlike some other drivers we DO NOT set initial limits. Use + * the config file to set limits. Some users have reported + * motherboards shutting down when we set limits in a previous + * version of the driver. + */ + +/* Chip sampling rates + * + * Some sensors are not updated more frequently than once per second + * so it doesn't make sense to read them more often than that. + * We cache the results and return the saved data if the driver + * is called again before a second has elapsed. + * + * Also, there is significant configuration data for this chip + * given the automatic PWM fan control that is possible. There + * are about 47 bytes of config data to only 22 bytes of actual + * readings. So, we keep the config data up to date in the cache + * when it is written and only sample it once every 1 *minute* + */ +#define LM85_DATA_INTERVAL (HZ + HZ / 2) +#define LM85_CONFIG_INTERVAL (1 * 60 * HZ) + +/* For each registered LM85, we need to keep some data in memory. That + data is pointed to by lm85_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new lm85 client is + allocated. */ + +/* LM85 can automatically adjust fan speeds based on temperature + * This structure encapsulates an entire Zone config. There are + * three zones (one for each temperature input) on the lm85 + */ +struct lm85_zone { + s8 limit; /* Low temp limit */ + u8 hyst; /* Low limit hysteresis. (0-15) */ + u8 range; /* Temp range, encoded */ + s8 critical; /* "All fans ON" temp limit */ + u8 off_desired; /* Actual "off" temperature specified. Preserved + * to prevent "drift" as other autofan control + * values change. + */ + u8 max_desired; /* Actual "max" temperature specified. Preserved + * to prevent "drift" as other autofan control + * values change. + */ +}; + +struct lm85_autofan { + u8 config; /* Register value */ + u8 freq; /* PWM frequency, encoded */ + u8 min_pwm; /* Minimum PWM value, encoded */ + u8 min_off; /* Min PWM or OFF below "limit", flag */ +}; + +struct lm85_data { + struct i2c_client client; + struct semaphore lock; + enum chips type; + + struct semaphore update_lock; + int valid; /* !=0 if following fields are valid */ + unsigned long last_reading; /* In jiffies */ + unsigned long last_config; /* In jiffies */ + + u8 in[8]; /* Register value */ + u8 in_max[8]; /* Register value */ + u8 in_min[8]; /* Register value */ + s8 temp[3]; /* Register value */ + s8 temp_min[3]; /* Register value */ + s8 temp_max[3]; /* Register value */ + s8 temp_offset[3]; /* Register value */ + u16 fan[4]; /* Register value */ + u16 fan_min[4]; /* Register value */ + u8 pwm[3]; /* Register value */ + u8 spinup_ctl; /* Register encoding, combined */ + u8 tach_mode; /* Register encoding, combined */ + u8 temp_ext[3]; /* Decoded values */ + u8 in_ext[8]; /* Decoded values */ + u8 adc_scale; /* ADC Extended bits scaling factor */ + u8 fan_ppr; /* Register value */ + u8 smooth[3]; /* Register encoding */ + u8 vid; /* Register value */ + u8 vrm; /* VRM version */ + u8 syncpwm3; /* Saved PWM3 for TACH 2,3,4 config */ + u8 oppoint[3]; /* Register value */ + u16 tmin_ctl; /* Register value */ + unsigned long therm_total; /* Cummulative therm count */ + u8 therm_limit; /* Register value */ + u32 alarms; /* Register encoding, combined */ + struct lm85_autofan autofan[3]; + struct lm85_zone zone[3]; +}; + +static int lm85_attach_adapter(struct i2c_adapter *adapter); +static int lm85_detect(struct i2c_adapter *adapter, int address, + int kind); +static int lm85_detach_client(struct i2c_client *client); + +static int lm85_read_value(struct i2c_client *client, u8 register); +static int lm85_write_value(struct i2c_client *client, u8 register, int value); +static struct lm85_data *lm85_update_device(struct device *dev); +static void lm85_init_client(struct i2c_client *client); + + +static struct i2c_driver lm85_driver = { + .owner = THIS_MODULE, + .name = "lm85", + .id = I2C_DRIVERID_LM85, + .flags = I2C_DF_NOTIFY, + .attach_adapter = lm85_attach_adapter, + .detach_client = lm85_detach_client, +}; + + +/* 4 Fans */ +static ssize_t show_fan(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr]) ); +} +static ssize_t show_fan_min(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr]) ); +} +static ssize_t set_fan_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->fan_min[nr] = FAN_TO_REG(val); + lm85_write_value(client, LM85_REG_FAN_MIN(nr), data->fan_min[nr]); + up(&data->update_lock); + return count; +} + +#define show_fan_offset(offset) \ +static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_min(dev, buf, offset - 1); \ +} \ +static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_fan_min(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, \ + NULL); \ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan_##offset##_min, set_fan_##offset##_min); + +show_fan_offset(1); +show_fan_offset(2); +show_fan_offset(3); +show_fan_offset(4); + +/* vid, vrm, alarms */ + +static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); +} + +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); + +static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->vrm); +} + +static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + data->vrm = val; + return count; +} + +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); + +static ssize_t show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); +} + +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); + +/* pwm */ + +static ssize_t show_pwm(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", PWM_FROM_REG(data->pwm[nr]) ); +} +static ssize_t set_pwm(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->pwm[nr] = PWM_TO_REG(val); + lm85_write_value(client, LM85_REG_PWM(nr), data->pwm[nr]); + up(&data->update_lock); + return count; +} +static ssize_t show_pwm_enable(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + int pwm_zone; + + pwm_zone = ZONE_FROM_REG(data->autofan[nr].config); + return sprintf(buf,"%d\n", (pwm_zone != 0 && pwm_zone != -1) ); +} + +#define show_pwm_reg(offset) \ +static ssize_t show_pwm_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_pwm(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm_##offset (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_pwm(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_pwm_enable##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_pwm_enable(dev, buf, offset - 1); \ +} \ +static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ + show_pwm_##offset, set_pwm_##offset); \ +static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO, \ + show_pwm_enable##offset, NULL); + +show_pwm_reg(1); +show_pwm_reg(2); +show_pwm_reg(3); + +/* Voltages */ + +static ssize_t show_in(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf( buf, "%d\n", INSEXT_FROM_REG(nr, + data->in[nr], + data->in_ext[nr], + data->adc_scale) ); +} +static ssize_t show_in_min(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in_min[nr]) ); +} +static ssize_t set_in_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->in_min[nr] = INS_TO_REG(nr, val); + lm85_write_value(client, LM85_REG_IN_MIN(nr), data->in_min[nr]); + up(&data->update_lock); + return count; +} +static ssize_t show_in_max(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in_max[nr]) ); +} +static ssize_t set_in_max(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->in_max[nr] = INS_TO_REG(nr, val); + lm85_write_value(client, LM85_REG_IN_MAX(nr), data->in_max[nr]); + up(&data->update_lock); + return count; +} +#define show_in_reg(offset) \ +static ssize_t show_in_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in(dev, buf, offset); \ +} \ +static ssize_t show_in_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in_min(dev, buf, offset); \ +} \ +static ssize_t show_in_##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in_max(dev, buf, offset); \ +} \ +static ssize_t set_in_##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_in_min(dev, buf, count, offset); \ +} \ +static ssize_t set_in_##offset##_max (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_in_max(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in_##offset, \ + NULL); \ +static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in_##offset##_min, set_in_##offset##_min); \ +static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in_##offset##_max, set_in_##offset##_max); + +show_in_reg(0); +show_in_reg(1); +show_in_reg(2); +show_in_reg(3); +show_in_reg(4); + +/* Temps */ + +static ssize_t show_temp(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", TEMPEXT_FROM_REG(data->temp[nr], + data->temp_ext[nr], + data->adc_scale) ); +} +static ssize_t show_temp_min(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_min[nr]) ); +} +static ssize_t set_temp_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_min[nr] = TEMP_TO_REG(val); + lm85_write_value(client, LM85_REG_TEMP_MIN(nr), data->temp_min[nr]); + up(&data->update_lock); + return count; +} +static ssize_t show_temp_max(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_max[nr]) ); +} +static ssize_t set_temp_max(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_max[nr] = TEMP_TO_REG(val); + lm85_write_value(client, LM85_REG_TEMP_MAX(nr), data->temp_max[nr]); + up(&data->update_lock); + return count; +} +#define show_temp_reg(offset) \ +static ssize_t show_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp(dev, buf, offset - 1); \ +} \ +static ssize_t show_temp_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp_min(dev, buf, offset - 1); \ +} \ +static ssize_t show_temp_##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp_max(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp_##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_temp_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t set_temp_##offset##_max (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_temp_max(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, \ + NULL); \ +static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ + show_temp_##offset##_min, set_temp_##offset##_min); \ +static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ + show_temp_##offset##_max, set_temp_##offset##_max); + +show_temp_reg(1); +show_temp_reg(2); +show_temp_reg(3); + + +/* Automatic PWM control */ + +static ssize_t show_pwm_auto_channels(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", ZONE_FROM_REG(data->autofan[nr].config)); +} +static ssize_t set_pwm_auto_channels(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->autofan[nr].config = (data->autofan[nr].config & (~0xe0)) + | ZONE_TO_REG(val) ; + lm85_write_value(client, LM85_REG_AFAN_CONFIG(nr), + data->autofan[nr].config); + up(&data->update_lock); + return count; +} +static ssize_t show_pwm_auto_pwm_min(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", PWM_FROM_REG(data->autofan[nr].min_pwm)); +} +static ssize_t set_pwm_auto_pwm_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->autofan[nr].min_pwm = PWM_TO_REG(val); + lm85_write_value(client, LM85_REG_AFAN_MINPWM(nr), + data->autofan[nr].min_pwm); + up(&data->update_lock); + return count; +} +static ssize_t show_pwm_auto_pwm_minctl(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", data->autofan[nr].min_off); +} +static ssize_t set_pwm_auto_pwm_minctl(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->autofan[nr].min_off = val; + lm85_write_value(client, LM85_REG_AFAN_SPIKE1, data->smooth[0] + | data->syncpwm3 + | (data->autofan[0].min_off ? 0x20 : 0) + | (data->autofan[1].min_off ? 0x40 : 0) + | (data->autofan[2].min_off ? 0x80 : 0) + ); + up(&data->update_lock); + return count; +} +static ssize_t show_pwm_auto_pwm_freq(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", FREQ_FROM_REG(data->autofan[nr].freq)); +} +static ssize_t set_pwm_auto_pwm_freq(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->autofan[nr].freq = FREQ_TO_REG(val); + lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), + (data->zone[nr].range << 4) + | data->autofan[nr].freq + ); + up(&data->update_lock); + return count; +} +#define pwm_auto(offset) \ +static ssize_t show_pwm##offset##_auto_channels (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_pwm_auto_channels(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_channels (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_channels(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_pwm##offset##_auto_pwm_min (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_pwm_auto_pwm_min(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_pwm_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_pwm_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_pwm##offset##_auto_pwm_minctl (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_pwm_auto_pwm_minctl(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_pwm_minctl (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_pwm_minctl(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_pwm##offset##_auto_pwm_freq (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_pwm_auto_pwm_freq(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_auto_pwm_freq(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_auto_pwm_freq(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(pwm##offset##_auto_channels, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_channels, \ + set_pwm##offset##_auto_channels); \ +static DEVICE_ATTR(pwm##offset##_auto_pwm_min, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_pwm_min, \ + set_pwm##offset##_auto_pwm_min); \ +static DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_pwm_minctl, \ + set_pwm##offset##_auto_pwm_minctl); \ +static DEVICE_ATTR(pwm##offset##_auto_pwm_freq, S_IRUGO | S_IWUSR, \ + show_pwm##offset##_auto_pwm_freq, \ + set_pwm##offset##_auto_pwm_freq); +pwm_auto(1); +pwm_auto(2); +pwm_auto(3); + +/* Temperature settings for automatic PWM control */ + +static ssize_t show_temp_auto_temp_off(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->zone[nr].limit) - + HYST_FROM_REG(data->zone[nr].hyst)); +} +static ssize_t set_temp_auto_temp_off(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + int min; + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + min = TEMP_FROM_REG(data->zone[nr].limit); + data->zone[nr].off_desired = TEMP_TO_REG(val); + data->zone[nr].hyst = HYST_TO_REG(min - val); + if ( nr == 0 || nr == 1 ) { + lm85_write_value(client, LM85_REG_AFAN_HYST1, + (data->zone[0].hyst << 4) + | data->zone[1].hyst + ); + } else { + lm85_write_value(client, LM85_REG_AFAN_HYST2, + (data->zone[2].hyst << 4) + ); + } + up(&data->update_lock); + return count; +} +static ssize_t show_temp_auto_temp_min(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->zone[nr].limit) ); +} +static ssize_t set_temp_auto_temp_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->zone[nr].limit = TEMP_TO_REG(val); + lm85_write_value(client, LM85_REG_AFAN_LIMIT(nr), + data->zone[nr].limit); + +/* Update temp_auto_max and temp_auto_range */ + data->zone[nr].range = RANGE_TO_REG( + TEMP_FROM_REG(data->zone[nr].max_desired) - + TEMP_FROM_REG(data->zone[nr].limit)); + lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), + ((data->zone[nr].range & 0x0f) << 4) + | (data->autofan[nr].freq & 0x07)); + +/* Update temp_auto_hyst and temp_auto_off */ + data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG( + data->zone[nr].limit) - TEMP_FROM_REG( + data->zone[nr].off_desired)); + if ( nr == 0 || nr == 1 ) { + lm85_write_value(client, LM85_REG_AFAN_HYST1, + (data->zone[0].hyst << 4) + | data->zone[1].hyst + ); + } else { + lm85_write_value(client, LM85_REG_AFAN_HYST2, + (data->zone[2].hyst << 4) + ); + } + up(&data->update_lock); + return count; +} +static ssize_t show_temp_auto_temp_max(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->zone[nr].limit) + + RANGE_FROM_REG(data->zone[nr].range)); +} +static ssize_t set_temp_auto_temp_max(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + int min; + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + min = TEMP_FROM_REG(data->zone[nr].limit); + data->zone[nr].max_desired = TEMP_TO_REG(val); + data->zone[nr].range = RANGE_TO_REG( + val - min); + lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), + ((data->zone[nr].range & 0x0f) << 4) + | (data->autofan[nr].freq & 0x07)); + up(&data->update_lock); + return count; +} +static ssize_t show_temp_auto_temp_crit(struct device *dev, char *buf, int nr) +{ + struct lm85_data *data = lm85_update_device(dev); + return sprintf(buf,"%d\n", TEMP_FROM_REG(data->zone[nr].critical)); +} +static ssize_t set_temp_auto_temp_crit(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->zone[nr].critical = TEMP_TO_REG(val); + lm85_write_value(client, LM85_REG_AFAN_CRITICAL(nr), + data->zone[nr].critical); + up(&data->update_lock); + return count; +} +#define temp_auto(offset) \ +static ssize_t show_temp##offset##_auto_temp_off (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_temp_auto_temp_off(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_off (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_off(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_temp##offset##_auto_temp_min (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_temp_auto_temp_min(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_temp##offset##_auto_temp_max (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_temp_auto_temp_max(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_max (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_max(dev, buf, count, offset - 1); \ +} \ +static ssize_t show_temp##offset##_auto_temp_crit (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_temp_auto_temp_crit(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp##offset##_auto_temp_crit (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_temp_auto_temp_crit(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(temp##offset##_auto_temp_off, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_off, \ + set_temp##offset##_auto_temp_off); \ +static DEVICE_ATTR(temp##offset##_auto_temp_min, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_min, \ + set_temp##offset##_auto_temp_min); \ +static DEVICE_ATTR(temp##offset##_auto_temp_max, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_max, \ + set_temp##offset##_auto_temp_max); \ +static DEVICE_ATTR(temp##offset##_auto_temp_crit, S_IRUGO | S_IWUSR, \ + show_temp##offset##_auto_temp_crit, \ + set_temp##offset##_auto_temp_crit); +temp_auto(1); +temp_auto(2); +temp_auto(3); + +int lm85_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, lm85_detect); +} + +int lm85_detect(struct i2c_adapter *adapter, int address, + int kind) +{ + int company, verstep ; + struct i2c_client *new_client = NULL; + struct lm85_data *data; + int err = 0; + const char *type_name = ""; + + if (i2c_is_isa_adapter(adapter)) { + /* This chip has no ISA interface */ + goto ERROR0 ; + }; + + if (!i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) { + /* We need to be able to do byte I/O */ + goto ERROR0 ; + }; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access lm85_{read,write}_value. */ + + if (!(data = kmalloc(sizeof(struct lm85_data), GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + memset(data, 0, sizeof(struct lm85_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lm85_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + company = lm85_read_value(new_client, LM85_REG_COMPANY); + verstep = lm85_read_value(new_client, LM85_REG_VERSTEP); + + dev_dbg(&adapter->dev, "Detecting device at %d,0x%02x with" + " COMPANY: 0x%02x and VERSTEP: 0x%02x\n", + i2c_adapter_id(new_client->adapter), new_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 == LM85_COMPANY_NATIONAL + && verstep == LM85_VERSTEP_LM85C ) { + kind = lm85c ; + } else if( company == LM85_COMPANY_NATIONAL + && verstep == LM85_VERSTEP_LM85B ) { + kind = lm85b ; + } else if( company == LM85_COMPANY_NATIONAL + && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC ) { + dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" + " Defaulting to LM85.\n", verstep); + kind = any_chip ; + } else if( company == LM85_COMPANY_ANALOG_DEV + && verstep == LM85_VERSTEP_ADM1027 ) { + kind = adm1027 ; + } else if( company == LM85_COMPANY_ANALOG_DEV + && (verstep == LM85_VERSTEP_ADT7463 + || verstep == LM85_VERSTEP_ADT7463C) ) { + kind = adt7463 ; + } else if( company == LM85_COMPANY_ANALOG_DEV + && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC ) { + dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" + " Defaulting to Generic LM85.\n", verstep ); + kind = any_chip ; + } else if( company == LM85_COMPANY_SMSC + && (verstep == LM85_VERSTEP_EMC6D100_A0 + || verstep == LM85_VERSTEP_EMC6D100_A1) ) { + /* Unfortunately, we can't tell a '100 from a '101 + * from the registers. Since a '101 is a '100 + * in a package with fewer pins and therefore no + * 3.3V, 1.5V or 1.8V inputs, perhaps if those + * inputs read 0, then it's a '101. + */ + kind = emc6d100 ; + } else if( company == LM85_COMPANY_SMSC + && verstep == LM85_VERSTEP_EMC6D102) { + kind = emc6d102 ; + } else if( company == LM85_COMPANY_SMSC + && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { + dev_err(&adapter->dev, "lm85: Detected SMSC chip\n"); + dev_err(&adapter->dev, "lm85: Unrecognized version/stepping 0x%02x" + " Defaulting to Generic LM85.\n", verstep ); + kind = any_chip ; + } else if( kind == any_chip + && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { + dev_err(&adapter->dev, "Generic LM85 Version 6 detected\n"); + /* Leave kind as "any_chip" */ + } else { + dev_dbg(&adapter->dev, "Autodetection failed\n"); + /* Not an LM85 ... */ + if( kind == any_chip ) { /* User used force=x,y */ + dev_err(&adapter->dev, "Generic LM85 Version 6 not" + " found at %d,0x%02x. Try force_lm85c.\n", + i2c_adapter_id(adapter), address ); + } + err = 0 ; + goto ERROR1; + } + } + + /* Fill in the chip specific driver values */ + if ( kind == any_chip ) { + type_name = "lm85"; + } else if ( kind == lm85b ) { + type_name = "lm85b"; + } else if ( kind == lm85c ) { + type_name = "lm85c"; + } else if ( kind == adm1027 ) { + type_name = "adm1027"; + } else if ( kind == adt7463 ) { + type_name = "adt7463"; + } else if ( kind == emc6d100){ + type_name = "emc6d100"; + } else if ( kind == emc6d102 ) { + type_name = "emc6d102"; + } + strlcpy(new_client->name, type_name, I2C_NAME_SIZE); + + /* Fill in the remaining client fields */ + data->type = kind; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR1; + + /* Set the VRM version */ + data->vrm = i2c_which_vrm(); + + /* Initialize the LM85 chip */ + lm85_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_fan1_input); + device_create_file(&new_client->dev, &dev_attr_fan2_input); + device_create_file(&new_client->dev, &dev_attr_fan3_input); + device_create_file(&new_client->dev, &dev_attr_fan4_input); + device_create_file(&new_client->dev, &dev_attr_fan1_min); + device_create_file(&new_client->dev, &dev_attr_fan2_min); + device_create_file(&new_client->dev, &dev_attr_fan3_min); + device_create_file(&new_client->dev, &dev_attr_fan4_min); + device_create_file(&new_client->dev, &dev_attr_pwm1); + device_create_file(&new_client->dev, &dev_attr_pwm2); + device_create_file(&new_client->dev, &dev_attr_pwm3); + device_create_file(&new_client->dev, &dev_attr_pwm1_enable); + device_create_file(&new_client->dev, &dev_attr_pwm2_enable); + device_create_file(&new_client->dev, &dev_attr_pwm3_enable); + device_create_file(&new_client->dev, &dev_attr_in0_input); + device_create_file(&new_client->dev, &dev_attr_in1_input); + device_create_file(&new_client->dev, &dev_attr_in2_input); + device_create_file(&new_client->dev, &dev_attr_in3_input); + device_create_file(&new_client->dev, &dev_attr_in4_input); + device_create_file(&new_client->dev, &dev_attr_in0_min); + device_create_file(&new_client->dev, &dev_attr_in1_min); + device_create_file(&new_client->dev, &dev_attr_in2_min); + device_create_file(&new_client->dev, &dev_attr_in3_min); + device_create_file(&new_client->dev, &dev_attr_in4_min); + device_create_file(&new_client->dev, &dev_attr_in0_max); + device_create_file(&new_client->dev, &dev_attr_in1_max); + device_create_file(&new_client->dev, &dev_attr_in2_max); + device_create_file(&new_client->dev, &dev_attr_in3_max); + device_create_file(&new_client->dev, &dev_attr_in4_max); + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp2_input); + device_create_file(&new_client->dev, &dev_attr_temp3_input); + device_create_file(&new_client->dev, &dev_attr_temp1_min); + device_create_file(&new_client->dev, &dev_attr_temp2_min); + device_create_file(&new_client->dev, &dev_attr_temp3_min); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp2_max); + device_create_file(&new_client->dev, &dev_attr_temp3_max); + device_create_file(&new_client->dev, &dev_attr_vrm); + device_create_file(&new_client->dev, &dev_attr_cpu0_vid); + device_create_file(&new_client->dev, &dev_attr_alarms); + device_create_file(&new_client->dev, &dev_attr_pwm1_auto_channels); + device_create_file(&new_client->dev, &dev_attr_pwm2_auto_channels); + device_create_file(&new_client->dev, &dev_attr_pwm3_auto_channels); + device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_min); + device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_min); + device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_min); + device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_minctl); + device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_minctl); + device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_minctl); + device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_freq); + device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_freq); + device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_freq); + device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_off); + device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_off); + device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_off); + device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_min); + device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_min); + device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_min); + device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_max); + device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_max); + device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_max); + device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_crit); + device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_crit); + device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_crit); + + return 0; + + /* Error out and cleanup code */ + ERROR1: + kfree(data); + ERROR0: + return err; +} + +int lm85_detach_client(struct i2c_client *client) +{ + i2c_detach_client(client); + kfree(i2c_get_clientdata(client)); + return 0; +} + + +int lm85_read_value(struct i2c_client *client, u8 reg) +{ + int res; + + /* What size location is it? */ + switch( reg ) { + case LM85_REG_FAN(0) : /* Read WORD data */ + case LM85_REG_FAN(1) : + case LM85_REG_FAN(2) : + case LM85_REG_FAN(3) : + case LM85_REG_FAN_MIN(0) : + case LM85_REG_FAN_MIN(1) : + case LM85_REG_FAN_MIN(2) : + case LM85_REG_FAN_MIN(3) : + case LM85_REG_ALARM1 : /* Read both bytes at once */ + res = i2c_smbus_read_byte_data(client, reg) & 0xff ; + res |= i2c_smbus_read_byte_data(client, reg+1) << 8 ; + break ; + case ADT7463_REG_TMIN_CTL1 : /* Read WORD MSB, LSB */ + res = i2c_smbus_read_byte_data(client, reg) << 8 ; + res |= i2c_smbus_read_byte_data(client, reg+1) & 0xff ; + break ; + default: /* Read BYTE data */ + res = i2c_smbus_read_byte_data(client, reg); + break ; + } + + return res ; +} + +int lm85_write_value(struct i2c_client *client, u8 reg, int value) +{ + int res ; + + switch( reg ) { + case LM85_REG_FAN(0) : /* Write WORD data */ + case LM85_REG_FAN(1) : + case LM85_REG_FAN(2) : + case LM85_REG_FAN(3) : + case LM85_REG_FAN_MIN(0) : + case LM85_REG_FAN_MIN(1) : + case LM85_REG_FAN_MIN(2) : + case LM85_REG_FAN_MIN(3) : + /* NOTE: ALARM is read only, so not included here */ + res = i2c_smbus_write_byte_data(client, reg, value & 0xff) ; + res |= i2c_smbus_write_byte_data(client, reg+1, (value>>8) & 0xff) ; + break ; + case ADT7463_REG_TMIN_CTL1 : /* Write WORD MSB, LSB */ + res = i2c_smbus_write_byte_data(client, reg, (value>>8) & 0xff); + res |= i2c_smbus_write_byte_data(client, reg+1, value & 0xff) ; + break ; + default: /* Write BYTE data */ + res = i2c_smbus_write_byte_data(client, reg, value); + break ; + } + + return res ; +} + +void lm85_init_client(struct i2c_client *client) +{ + int value; + struct lm85_data *data = i2c_get_clientdata(client); + + dev_dbg(&client->dev, "Initializing device\n"); + + /* Warn if part was not "READY" */ + value = lm85_read_value(client, LM85_REG_CONFIG); + dev_dbg(&client->dev, "LM85_REG_CONFIG is: 0x%02x\n", value); + if( value & 0x02 ) { + dev_err(&client->dev, "Client (%d,0x%02x) config is locked.\n", + i2c_adapter_id(client->adapter), client->addr ); + }; + if( ! (value & 0x04) ) { + dev_err(&client->dev, "Client (%d,0x%02x) is not ready.\n", + i2c_adapter_id(client->adapter), client->addr ); + }; + if( value & 0x10 + && ( data->type == adm1027 + || data->type == adt7463 ) ) { + dev_err(&client->dev, "Client (%d,0x%02x) VxI mode is set. " + "Please report this to the lm85 maintainer.\n", + i2c_adapter_id(client->adapter), client->addr ); + }; + + /* WE INTENTIONALLY make no changes to the limits, + * offsets, pwms, fans and zones. If they were + * configured, we don't want to mess with them. + * If they weren't, the default is 100% PWM, no + * control and will suffice until 'sensors -s' + * can be run by the user. + */ + + /* Start monitoring */ + value = lm85_read_value(client, LM85_REG_CONFIG); + /* Try to clear LOCK, Set START, save everything else */ + value = (value & ~ 0x02) | 0x01 ; + dev_dbg(&client->dev, "Setting CONFIG to: 0x%02x\n", value); + lm85_write_value(client, LM85_REG_CONFIG, value); +} + +static struct lm85_data *lm85_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm85_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if ( !data->valid || + time_after(jiffies, data->last_reading + LM85_DATA_INTERVAL) ) { + /* Things that change quickly */ + dev_dbg(&client->dev, "Reading sensor values\n"); + + /* Have to read extended bits first to "freeze" the + * more significant bits that are read later. + */ + if ( (data->type == adm1027) || (data->type == adt7463) ) { + int ext1 = lm85_read_value(client, + ADM1027_REG_EXTEND_ADC1); + int ext2 = lm85_read_value(client, + ADM1027_REG_EXTEND_ADC2); + int val = (ext1 << 8) + ext2; + + for(i = 0; i <= 4; i++) + data->in_ext[i] = (val>>(i * 2))&0x03; + + for(i = 0; i <= 2; i++) + data->temp_ext[i] = (val>>((i + 5) * 2))&0x03; + } + + /* adc_scale is 2^(number of LSBs). There are 4 extra bits in + the emc6d102 and 2 in the adt7463 and adm1027. In all + other chips ext is always 0 and the value of scale is + irrelevant. So it is left in 4*/ + data->adc_scale = (data->type == emc6d102 ) ? 16 : 4; + + for (i = 0; i <= 4; ++i) { + data->in[i] = + lm85_read_value(client, LM85_REG_IN(i)); + } + + for (i = 0; i <= 3; ++i) { + data->fan[i] = + lm85_read_value(client, LM85_REG_FAN(i)); + } + + for (i = 0; i <= 2; ++i) { + data->temp[i] = + lm85_read_value(client, LM85_REG_TEMP(i)); + } + + for (i = 0; i <= 2; ++i) { + data->pwm[i] = + lm85_read_value(client, LM85_REG_PWM(i)); + } + + data->alarms = lm85_read_value(client, LM85_REG_ALARM1); + + if ( data->type == adt7463 ) { + if( data->therm_total < ULONG_MAX - 256 ) { + data->therm_total += + lm85_read_value(client, ADT7463_REG_THERM ); + } + } else if ( data->type == emc6d100 ) { + /* Three more voltage sensors */ + for (i = 5; i <= 7; ++i) { + data->in[i] = + lm85_read_value(client, EMC6D100_REG_IN(i)); + } + /* More alarm bits */ + data->alarms |= + lm85_read_value(client, EMC6D100_REG_ALARM3) << 16; + } else if (data->type == emc6d102 ) { + /* Have to read LSB bits after the MSB ones because + the reading of the MSB bits has frozen the + LSBs (backward from the ADM1027). + */ + int ext1 = lm85_read_value(client, + EMC6D102_REG_EXTEND_ADC1); + int ext2 = lm85_read_value(client, + EMC6D102_REG_EXTEND_ADC2); + int ext3 = lm85_read_value(client, + EMC6D102_REG_EXTEND_ADC3); + int ext4 = lm85_read_value(client, + EMC6D102_REG_EXTEND_ADC4); + data->in_ext[0] = ext3 & 0x0f; + data->in_ext[1] = ext4 & 0x0f; + data->in_ext[2] = (ext4 >> 4) & 0x0f; + data->in_ext[3] = (ext3 >> 4) & 0x0f; + data->in_ext[4] = (ext2 >> 4) & 0x0f; + + data->temp_ext[0] = ext1 & 0x0f; + data->temp_ext[1] = ext2 & 0x0f; + data->temp_ext[2] = (ext1 >> 4) & 0x0f; + } + + data->last_reading = jiffies ; + }; /* last_reading */ + + if ( !data->valid || + time_after(jiffies, data->last_config + LM85_CONFIG_INTERVAL) ) { + /* Things that don't change often */ + dev_dbg(&client->dev, "Reading config values\n"); + + for (i = 0; i <= 4; ++i) { + data->in_min[i] = + lm85_read_value(client, LM85_REG_IN_MIN(i)); + data->in_max[i] = + lm85_read_value(client, LM85_REG_IN_MAX(i)); + } + + if ( data->type == emc6d100 ) { + for (i = 5; i <= 7; ++i) { + data->in_min[i] = + lm85_read_value(client, EMC6D100_REG_IN_MIN(i)); + data->in_max[i] = + lm85_read_value(client, EMC6D100_REG_IN_MAX(i)); + } + } + + for (i = 0; i <= 3; ++i) { + data->fan_min[i] = + lm85_read_value(client, LM85_REG_FAN_MIN(i)); + } + + for (i = 0; i <= 2; ++i) { + data->temp_min[i] = + lm85_read_value(client, LM85_REG_TEMP_MIN(i)); + data->temp_max[i] = + lm85_read_value(client, LM85_REG_TEMP_MAX(i)); + } + + data->vid = lm85_read_value(client, LM85_REG_VID); + + for (i = 0; i <= 2; ++i) { + int val ; + data->autofan[i].config = + lm85_read_value(client, LM85_REG_AFAN_CONFIG(i)); + val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i)); + data->autofan[i].freq = val & 0x07 ; + data->zone[i].range = (val >> 4) & 0x0f ; + data->autofan[i].min_pwm = + lm85_read_value(client, LM85_REG_AFAN_MINPWM(i)); + data->zone[i].limit = + lm85_read_value(client, LM85_REG_AFAN_LIMIT(i)); + data->zone[i].critical = + lm85_read_value(client, LM85_REG_AFAN_CRITICAL(i)); + } + + i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1); + data->smooth[0] = i & 0x0f ; + data->syncpwm3 = i & 0x10 ; /* Save PWM3 config */ + data->autofan[0].min_off = (i & 0x20) != 0 ; + data->autofan[1].min_off = (i & 0x40) != 0 ; + data->autofan[2].min_off = (i & 0x80) != 0 ; + i = lm85_read_value(client, LM85_REG_AFAN_SPIKE2); + data->smooth[1] = (i>>4) & 0x0f ; + data->smooth[2] = i & 0x0f ; + + i = lm85_read_value(client, LM85_REG_AFAN_HYST1); + data->zone[0].hyst = (i>>4) & 0x0f ; + data->zone[1].hyst = i & 0x0f ; + + i = lm85_read_value(client, LM85_REG_AFAN_HYST2); + data->zone[2].hyst = (i>>4) & 0x0f ; + + if ( (data->type == lm85b) || (data->type == lm85c) ) { + data->tach_mode = lm85_read_value(client, + LM85_REG_TACH_MODE ); + data->spinup_ctl = lm85_read_value(client, + LM85_REG_SPINUP_CTL ); + } else if ( (data->type == adt7463) || (data->type == adm1027) ) { + if ( data->type == adt7463 ) { + for (i = 0; i <= 2; ++i) { + data->oppoint[i] = lm85_read_value(client, + ADT7463_REG_OPPOINT(i) ); + } + data->tmin_ctl = lm85_read_value(client, + ADT7463_REG_TMIN_CTL1 ); + data->therm_limit = lm85_read_value(client, + ADT7463_REG_THERM_LIMIT ); + } + for (i = 0; i <= 2; ++i) { + data->temp_offset[i] = lm85_read_value(client, + ADM1027_REG_TEMP_OFFSET(i) ); + } + data->tach_mode = lm85_read_value(client, + ADM1027_REG_CONFIG3 ); + data->fan_ppr = lm85_read_value(client, + ADM1027_REG_FAN_PPR ); + } + + data->last_config = jiffies; + }; /* last_config */ + + data->valid = 1; + + up(&data->update_lock); + + return data; +} + + +static int __init sm_lm85_init(void) +{ + return i2c_add_driver(&lm85_driver); +} + +static void __exit sm_lm85_exit(void) +{ + i2c_del_driver(&lm85_driver); +} + +/* Thanks to Richard Barrington for adding the LM85 to sensors-detect. + * Thanks to Margit Schubert-While for help with + * post 2.7.0 CVS changes. + */ +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Philip Pokorny , Margit Schubert-While , Justin Thiessen + * Philip Edelbrock + * Stephen Rousset + * Dan Eaton + * Copyright (C) 2004 Jean Delvare + * + * Original port to Linux 2.6 by Jeff Oliver. + * + * The LM87 is a sensor chip made by National Semiconductor. It monitors up + * to 8 voltages (including its own power source), up to three temperatures + * (its own plus up to two external ones) and up to two fans. The default + * configuration is 6 voltages, two temperatures and two fans (see below). + * Voltages are scaled internally with ratios such that the nominal value of + * each voltage correspond to a register value of 192 (which means a + * resolution of about 0.5% of the nominal value). Temperature values are + * reported with a 1 deg resolution and a 3-4 deg accuracy. Complete + * datasheet can be obtained from National's website at: + * http://www.national.com/pf/LM/LM87.html + * + * Some functions share pins, so not all functions are available at the same + * time. Which are depends on the hardware setup. This driver assumes that + * the BIOS configured the chip correctly. In that respect, it differs from + * the original driver (from lm_sensors for Linux 2.4), which would force the + * LM87 to an arbitrary, compile-time chosen mode, regardless of the actual + * chipset wiring. + * For reference, here is the list of exclusive functions: + * - in0+in5 (default) or temp3 + * - fan1 (default) or in6 + * - fan2 (default) or in7 + * - VID lines (default) or IRQ lines (not handled by this driver) + * + * The LM87 additionally features an analog output, supposedly usable to + * control the speed of a fan. All new chips use pulse width modulation + * instead. The LM87 is the only hardware monitoring chipset I know of + * which uses amplitude modulation. Be careful when using this feature. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Addresses to scan + * LM87 has three possible addresses: 0x2c, 0x2d and 0x2e. + */ + +static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* + * Insmod parameters + */ + +SENSORS_INSMOD_1(lm87); + +/* + * The LM87 registers + */ + +/* nr in 0..5 */ +#define LM87_REG_IN(nr) (0x20 + (nr)) +#define LM87_REG_IN_MAX(nr) (0x2B + (nr) * 2) +#define LM87_REG_IN_MIN(nr) (0x2C + (nr) * 2) +/* nr in 0..1 */ +#define LM87_REG_AIN(nr) (0x28 + (nr)) +#define LM87_REG_AIN_MIN(nr) (0x1A + (nr)) +#define LM87_REG_AIN_MAX(nr) (0x3B + (nr)) + +static u8 LM87_REG_TEMP[3] = { 0x27, 0x26, 0x20 }; +static u8 LM87_REG_TEMP_HIGH[3] = { 0x39, 0x37, 0x2B }; +static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C }; + +#define LM87_REG_TEMP_HW_INT_LOCK 0x13 +#define LM87_REG_TEMP_HW_EXT_LOCK 0x14 +#define LM87_REG_TEMP_HW_INT 0x17 +#define LM87_REG_TEMP_HW_EXT 0x18 + +/* nr in 0..1 */ +#define LM87_REG_FAN(nr) (0x28 + (nr)) +#define LM87_REG_FAN_MIN(nr) (0x3B + (nr)) +#define LM87_REG_AOUT 0x19 + +#define LM87_REG_CONFIG 0x40 +#define LM87_REG_CHANNEL_MODE 0x16 +#define LM87_REG_VID_FAN_DIV 0x47 +#define LM87_REG_VID4 0x49 + +#define LM87_REG_ALARMS1 0x41 +#define LM87_REG_ALARMS2 0x42 + +#define LM87_REG_COMPANY_ID 0x3E +#define LM87_REG_REVISION 0x3F + +/* + * Conversions and various macros + * The LM87 uses signed 8-bit values for temperatures. + */ + +#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192) +#define IN_TO_REG(val,scale) ((val) <= 0 ? 0 : \ + (val) * 192 >= (scale) * 255 ? 255 : \ + ((val) * 192 + (scale)/2) / (scale)) + +#define TEMP_FROM_REG(reg) ((reg) * 1000) +#define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \ + (val) >= 126500 ? 127 : \ + (((val) < 0 ? (val)-500 : (val)+500) / 1000)) + +#define FAN_FROM_REG(reg,div) ((reg) == 255 || (reg) == 0 ? 0 : \ + 1350000 + (reg)*(div) / 2) / ((reg)*(div)) +#define FAN_TO_REG(val,div) ((val)*(div) * 255 <= 1350000 ? 255 : \ + (1350000 + (val)*(div) / 2) / ((val)*(div))) + +#define FAN_DIV_FROM_REG(reg) (1 << (reg)) + +/* analog out is 9.80mV/LSB */ +#define AOUT_FROM_REG(reg) (((reg) * 98 + 5) / 10) +#define AOUT_TO_REG(val) ((val) <= 0 ? 0 : \ + (val) >= 2500 ? 255 : \ + ((val) * 10 + 49) / 98) + +/* nr in 0..1 */ +#define CHAN_NO_FAN(nr) (1 << (nr)) +#define CHAN_TEMP3 (1 << 2) +#define CHAN_VCC_5V (1 << 3) +#define CHAN_NO_VID (1 << 8) + +/* + * Functions declaration + */ + +static int lm87_attach_adapter(struct i2c_adapter *adapter); +static int lm87_detect(struct i2c_adapter *adapter, int address, int kind); +static void lm87_init_client(struct i2c_client *client); +static int lm87_detach_client(struct i2c_client *client); +static struct lm87_data *lm87_update_device(struct device *dev); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver lm87_driver = { + .owner = THIS_MODULE, + .name = "lm87", + .id = I2C_DRIVERID_LM87, + .flags = I2C_DF_NOTIFY, + .attach_adapter = lm87_attach_adapter, + .detach_client = lm87_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct lm87_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 channel; /* register value */ + + u8 in[8]; /* register value */ + u8 in_max[8]; /* register value */ + u8 in_min[8]; /* register value */ + u16 in_scale[8]; + + s8 temp[3]; /* register value */ + s8 temp_high[3]; /* register value */ + s8 temp_low[3]; /* register value */ + s8 temp_crit_int; /* min of two register values */ + s8 temp_crit_ext; /* min of two register values */ + + u8 fan[2]; /* register value */ + u8 fan_min[2]; /* register value */ + u8 fan_div[2]; /* register value, shifted right */ + u8 aout; /* register value */ + + u16 alarms; /* register values, combined */ + u8 vid; /* register values, combined */ + u8 vrm; +}; + +/* + * Sysfs stuff + */ + +static inline int lm87_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +#define show_in(offset) \ +static ssize_t show_in##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \ + data->in_scale[offset])); \ +} \ +static ssize_t show_in##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \ + data->in_scale[offset])); \ +} \ +static ssize_t show_in##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \ + data->in_scale[offset])); \ +} \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ + show_in##offset##_input, NULL); +show_in(0); +show_in(1); +show_in(2); +show_in(3); +show_in(4); +show_in(5); +show_in(6); +show_in(7); + +static void set_in_min(struct device *dev, const char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->in_min[nr] = IN_TO_REG(val, data->in_scale[nr]); + lm87_write_value(client, nr<6 ? LM87_REG_IN_MIN(nr) : + LM87_REG_AIN_MIN(nr-6), data->in_min[nr]); + up(&data->update_lock); +} + +static void set_in_max(struct device *dev, const char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->in_max[nr] = IN_TO_REG(val, data->in_scale[nr]); + lm87_write_value(client, nr<6 ? LM87_REG_IN_MAX(nr) : + LM87_REG_AIN_MAX(nr-6), data->in_max[nr]); + up(&data->update_lock); +} + +#define set_in(offset) \ +static ssize_t set_in##offset##_min(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + set_in_min(dev, buf, offset); \ + return count; \ +} \ +static ssize_t set_in##offset##_max(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + set_in_max(dev, buf, offset); \ + return count; \ +} \ +static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in##offset##_min, set_in##offset##_min); \ +static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in##offset##_max, set_in##offset##_max); +set_in(0); +set_in(1); +set_in(2); +set_in(3); +set_in(4); +set_in(5); +set_in(6); +set_in(7); + +#define show_temp(offset) \ +static ssize_t show_temp##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \ +} \ +static ssize_t show_temp##offset##_low(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[offset-1])); \ +} \ +static ssize_t show_temp##offset##_high(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[offset-1])); \ +}\ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ + show_temp##offset##_input, NULL); +show_temp(1); +show_temp(2); +show_temp(3); + +static void set_temp_low(struct device *dev, const char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_low[nr] = TEMP_TO_REG(val); + lm87_write_value(client, LM87_REG_TEMP_LOW[nr], data->temp_low[nr]); + up(&data->update_lock); +} + +static void set_temp_high(struct device *dev, const char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_high[nr] = TEMP_TO_REG(val); + lm87_write_value(client, LM87_REG_TEMP_HIGH[nr], data->temp_high[nr]); + up(&data->update_lock); +} + +#define set_temp(offset) \ +static ssize_t set_temp##offset##_low(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + set_temp_low(dev, buf, offset-1); \ + return count; \ +} \ +static ssize_t set_temp##offset##_high(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + set_temp_high(dev, buf, offset-1); \ + return count; \ +} \ +static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ + show_temp##offset##_high, set_temp##offset##_high); \ +static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ + show_temp##offset##_low, set_temp##offset##_low); +set_temp(1); +set_temp(2); +set_temp(3); + +static ssize_t show_temp_crit_int(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_int)); +} + +static ssize_t show_temp_crit_ext(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_ext)); +} + +static DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit_int, NULL); +static DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit_ext, NULL); +static DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit_ext, NULL); + +#define show_fan(offset) \ +static ssize_t show_fan##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[offset-1], \ + FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \ +} \ +static ssize_t show_fan##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[offset-1], \ + FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \ +} \ +static ssize_t show_fan##offset##_div(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm87_data *data = lm87_update_device(dev); \ + return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[offset-1])); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ + show_fan##offset##_input, NULL); +show_fan(1); +show_fan(2); + +static void set_fan_min(struct device *dev, const char *buf, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->fan_min[nr] = FAN_TO_REG(val, + FAN_DIV_FROM_REG(data->fan_div[nr])); + lm87_write_value(client, LM87_REG_FAN_MIN(nr), data->fan_min[nr]); + up(&data->update_lock); +} + +/* Note: we save and restore the fan minimum here, because its value is + determined in part by the fan clock divider. This follows the principle + of least suprise; the user doesn't expect the fan minimum to change just + because the divider changed. */ +static ssize_t set_fan_div(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + unsigned long min; + u8 reg; + + down(&data->update_lock); + min = FAN_FROM_REG(data->fan_min[nr], + FAN_DIV_FROM_REG(data->fan_div[nr])); + + switch (val) { + case 1: data->fan_div[nr] = 0; break; + case 2: data->fan_div[nr] = 1; break; + case 4: data->fan_div[nr] = 2; break; + case 8: data->fan_div[nr] = 3; break; + default: + up(&data->update_lock); + return -EINVAL; + } + + reg = lm87_read_value(client, LM87_REG_VID_FAN_DIV); + switch (nr) { + case 0: + reg = (reg & 0xCF) | (data->fan_div[0] << 4); + break; + case 1: + reg = (reg & 0x3F) | (data->fan_div[1] << 6); + break; + } + lm87_write_value(client, LM87_REG_VID_FAN_DIV, reg); + + data->fan_min[nr] = FAN_TO_REG(min, val); + lm87_write_value(client, LM87_REG_FAN_MIN(nr), + data->fan_min[nr]); + up(&data->update_lock); + + return count; +} + +#define set_fan(offset) \ +static ssize_t set_fan##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + set_fan_min(dev, buf, offset-1); \ + return count; \ +} \ +static ssize_t set_fan##offset##_div(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return set_fan_div(dev, buf, count, offset-1); \ +} \ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan##offset##_min, set_fan##offset##_min); \ +static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ + show_fan##offset##_div, set_fan##offset##_div); +set_fan(1); +set_fan(2); + +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + return sprintf(buf, "%d\n", data->alarms); +} +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); +} +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + +static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + return sprintf(buf, "%d\n", data->vrm); +} +static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + data->vrm = simple_strtoul(buf, NULL, 10); + return count; +} +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); + +static ssize_t show_aout(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm87_data *data = lm87_update_device(dev); + return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout)); +} +static ssize_t set_aout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->aout = AOUT_TO_REG(val); + lm87_write_value(client, LM87_REG_AOUT, data->aout); + up(&data->update_lock); + return count; +} +static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout); + +/* + * Real code + */ + +static int lm87_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, lm87_detect); +} + +/* + * The following function does more than just detection. If detection + * succeeds, it also registers the new chip. + */ +static int lm87_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct lm87_data *data; + int err = 0; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct lm87_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct lm87_data)); + + /* The common I2C client data is placed right before the + LM87-specific data. */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lm87_driver; + new_client->flags = 0; + + /* Default to an LM87 if forced */ + if (kind == 0) + kind = lm87; + + /* Now, we do the remaining detection. */ + if (kind < 0) { + u8 rev = lm87_read_value(new_client, LM87_REG_REVISION); + + if (rev < 0x01 || rev > 0x08 + || (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80) + || lm87_read_value(new_client, LM87_REG_COMPANY_ID) != 0x02) { + dev_dbg(&adapter->dev, + "LM87 detection failed at 0x%02x.\n", + address); + goto exit_free; + } + } + + /* We can fill in the remaining client fields */ + strlcpy(new_client->name, "lm87", I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the LM87 chip */ + lm87_init_client(new_client); + + data->in_scale[0] = 2500; + data->in_scale[1] = 2700; + data->in_scale[2] = (data->channel & CHAN_VCC_5V) ? 5000 : 3300; + data->in_scale[3] = 5000; + data->in_scale[4] = 12000; + data->in_scale[5] = 2700; + data->in_scale[6] = 1875; + data->in_scale[7] = 1875; + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_in1_input); + device_create_file(&new_client->dev, &dev_attr_in1_min); + device_create_file(&new_client->dev, &dev_attr_in1_max); + device_create_file(&new_client->dev, &dev_attr_in2_input); + device_create_file(&new_client->dev, &dev_attr_in2_min); + device_create_file(&new_client->dev, &dev_attr_in2_max); + device_create_file(&new_client->dev, &dev_attr_in3_input); + device_create_file(&new_client->dev, &dev_attr_in3_min); + device_create_file(&new_client->dev, &dev_attr_in3_max); + device_create_file(&new_client->dev, &dev_attr_in4_input); + device_create_file(&new_client->dev, &dev_attr_in4_min); + device_create_file(&new_client->dev, &dev_attr_in4_max); + + if (data->channel & CHAN_NO_FAN(0)) { + device_create_file(&new_client->dev, &dev_attr_in6_input); + device_create_file(&new_client->dev, &dev_attr_in6_min); + device_create_file(&new_client->dev, &dev_attr_in6_max); + } else { + device_create_file(&new_client->dev, &dev_attr_fan1_input); + device_create_file(&new_client->dev, &dev_attr_fan1_min); + device_create_file(&new_client->dev, &dev_attr_fan1_div); + } + if (data->channel & CHAN_NO_FAN(1)) { + device_create_file(&new_client->dev, &dev_attr_in7_input); + device_create_file(&new_client->dev, &dev_attr_in7_min); + device_create_file(&new_client->dev, &dev_attr_in7_max); + } else { + device_create_file(&new_client->dev, &dev_attr_fan2_input); + device_create_file(&new_client->dev, &dev_attr_fan2_min); + device_create_file(&new_client->dev, &dev_attr_fan2_div); + } + + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp1_min); + device_create_file(&new_client->dev, &dev_attr_temp1_crit); + device_create_file(&new_client->dev, &dev_attr_temp2_input); + device_create_file(&new_client->dev, &dev_attr_temp2_max); + device_create_file(&new_client->dev, &dev_attr_temp2_min); + device_create_file(&new_client->dev, &dev_attr_temp2_crit); + + if (data->channel & CHAN_TEMP3) { + device_create_file(&new_client->dev, &dev_attr_temp3_input); + device_create_file(&new_client->dev, &dev_attr_temp3_max); + device_create_file(&new_client->dev, &dev_attr_temp3_min); + device_create_file(&new_client->dev, &dev_attr_temp3_crit); + } else { + device_create_file(&new_client->dev, &dev_attr_in0_input); + device_create_file(&new_client->dev, &dev_attr_in0_min); + device_create_file(&new_client->dev, &dev_attr_in0_max); + device_create_file(&new_client->dev, &dev_attr_in5_input); + device_create_file(&new_client->dev, &dev_attr_in5_min); + device_create_file(&new_client->dev, &dev_attr_in5_max); + } + + if (!(data->channel & CHAN_NO_VID)) { + device_create_file(&new_client->dev, &dev_attr_cpu0_vid); + device_create_file(&new_client->dev, &dev_attr_vrm); + } + + device_create_file(&new_client->dev, &dev_attr_alarms); + device_create_file(&new_client->dev, &dev_attr_aout_output); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static void lm87_init_client(struct i2c_client *client) +{ + struct lm87_data *data = i2c_get_clientdata(client); + u8 config; + + data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); + data->vrm = i2c_which_vrm(); + + config = lm87_read_value(client, LM87_REG_CONFIG); + if (!(config & 0x01)) { + int i; + + /* Limits are left uninitialized after power-up */ + for (i = 1; i < 6; i++) { + lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00); + lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF); + } + for (i = 0; i < 2; i++) { + lm87_write_value(client, LM87_REG_TEMP_HIGH[i], 0x7F); + lm87_write_value(client, LM87_REG_TEMP_LOW[i], 0x00); + lm87_write_value(client, LM87_REG_AIN_MIN(i), 0x00); + lm87_write_value(client, LM87_REG_AIN_MAX(i), 0xFF); + } + if (data->channel & CHAN_TEMP3) { + lm87_write_value(client, LM87_REG_TEMP_HIGH[2], 0x7F); + lm87_write_value(client, LM87_REG_TEMP_LOW[2], 0x00); + } else { + lm87_write_value(client, LM87_REG_IN_MIN(0), 0x00); + lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF); + } + } + if ((config & 0x81) != 0x01) { + /* Start monitoring */ + lm87_write_value(client, LM87_REG_CONFIG, + (config & 0xF7) | 0x01); + } +} + +static int lm87_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static struct lm87_data *lm87_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm87_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + int i, j; + + dev_dbg(&client->dev, "Updating data.\n"); + + i = (data->channel & CHAN_TEMP3) ? 1 : 0; + j = (data->channel & CHAN_TEMP3) ? 5 : 6; + for (; i < j; i++) { + data->in[i] = lm87_read_value(client, + LM87_REG_IN(i)); + data->in_min[i] = lm87_read_value(client, + LM87_REG_IN_MIN(i)); + data->in_max[i] = lm87_read_value(client, + LM87_REG_IN_MAX(i)); + } + + for (i = 0; i < 2; i++) { + if (data->channel & CHAN_NO_FAN(i)) { + data->in[6+i] = lm87_read_value(client, + LM87_REG_AIN(i)); + data->in_max[6+i] = lm87_read_value(client, + LM87_REG_AIN_MAX(i)); + data->in_min[6+i] = lm87_read_value(client, + LM87_REG_AIN_MIN(i)); + + } else { + data->fan[i] = lm87_read_value(client, + LM87_REG_FAN(i)); + data->fan_min[i] = lm87_read_value(client, + LM87_REG_FAN_MIN(i)); + } + } + + j = (data->channel & CHAN_TEMP3) ? 3 : 2; + for (i = 0 ; i < j; i++) { + data->temp[i] = lm87_read_value(client, + LM87_REG_TEMP[i]); + data->temp_high[i] = lm87_read_value(client, + LM87_REG_TEMP_HIGH[i]); + data->temp_low[i] = lm87_read_value(client, + LM87_REG_TEMP_LOW[i]); + } + + i = lm87_read_value(client, LM87_REG_TEMP_HW_INT_LOCK); + j = lm87_read_value(client, LM87_REG_TEMP_HW_INT); + data->temp_crit_int = min(i, j); + + i = lm87_read_value(client, LM87_REG_TEMP_HW_EXT_LOCK); + j = lm87_read_value(client, LM87_REG_TEMP_HW_EXT); + data->temp_crit_ext = min(i, j); + + i = lm87_read_value(client, LM87_REG_VID_FAN_DIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + data->vid = (i & 0x0F) + | (lm87_read_value(client, LM87_REG_VID4) & 0x01) + << 4; + + data->alarms = lm87_read_value(client, LM87_REG_ALARMS1) + | (lm87_read_value(client, LM87_REG_ALARMS2) + << 8); + data->aout = lm87_read_value(client, LM87_REG_AOUT); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_lm87_init(void) +{ + return i2c_add_driver(&lm87_driver); +} + +static void __exit sensors_lm87_exit(void) +{ + i2c_del_driver(&lm87_driver); +} + +MODULE_AUTHOR("Jean Delvare and others"); +MODULE_DESCRIPTION("LM87 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm87_init); +module_exit(sensors_lm87_exit); diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c new file mode 100644 index 000000000000..a67dcadf7cb0 --- /dev/null +++ b/drivers/hwmon/lm90.c @@ -0,0 +1,655 @@ +/* + * lm90.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2003-2005 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 + * one external one) with a 0.125 deg resolution (1 deg for local + * temperature) and a 3-4 deg accuracy. Complete datasheet can be + * obtained from National's website at: + * http://www.national.com/pf/LM/LM90.html + * + * This driver also supports the LM89 and LM99, two other sensor chips + * made by National Semiconductor. Both have an increased remote + * temperature measurement accuracy (1 degree), and the LM99 + * additionally shifts remote temperatures (measured and limits) by 16 + * degrees, which allows for higher temperatures measurement. The + * driver doesn't handle it since it can be done easily in user-space. + * Complete datasheets can be obtained from National's website at: + * http://www.national.com/pf/LM/LM89.html + * http://www.national.com/pf/LM/LM99.html + * Note that there is no way to differentiate between both chips. + * + * This driver also supports the LM86, another sensor chip made by + * National Semiconductor. It is exactly similar to the LM90 except it + * has a higher accuracy. + * Complete datasheet can be obtained from National's website at: + * http://www.national.com/pf/LM/LM86.html + * + * This driver also supports the ADM1032, a sensor chip made by Analog + * Devices. That chip is similar to the LM90, with a few differences + * that are not handled by this driver. Complete datasheet can be + * obtained from Analog's website at: + * http://products.analog.com/products/info.asp?product=ADM1032 + * Among others, it has a higher accuracy than the LM90, much like the + * LM86 does. + * + * This driver also supports the MAX6657, MAX6658 and MAX6659 sensor + * chips made by Maxim. These chips are similar to the LM86. Complete + * datasheet can be obtained at Maxim's website at: + * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 + * Note that there is no easy way to differentiate between the three + * variants. The extra address and features of the MAX6659 are not + * supported by this driver. + * + * This driver also supports the ADT7461 chip from Analog Devices but + * only in its "compatability mode". If an ADT7461 chip is found but + * is configured in non-compatible mode (where its temperature + * register values are decoded differently) it is ignored by this + * driver. Complete datasheet can be obtained from Analog's website + * at: + * http://products.analog.com/products/info.asp?product=ADT7461 + * + * Since the LM90 was the first chipset supported by this driver, most + * comments will refer to this chipset, but are actually general and + * concern all supported chipsets, unless mentioned otherwise. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Addresses to scan + * Address is fully defined internally and cannot be changed except for + * MAX6659. + * LM86, LM89, LM90, LM99, ADM1032, MAX6657 and MAX6658 have address 0x4c. + * LM89-1, and LM99-1 have address 0x4d. + * MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported). + * ADT7461 always has address 0x4c. + */ + +static unsigned short normal_i2c[] = { 0x4c, 0x4d, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* + * Insmod parameters + */ + +SENSORS_INSMOD_6(lm90, adm1032, lm99, lm86, max6657, adt7461); + +/* + * The LM90 registers + */ + +#define LM90_REG_R_MAN_ID 0xFE +#define LM90_REG_R_CHIP_ID 0xFF +#define LM90_REG_R_CONFIG1 0x03 +#define LM90_REG_W_CONFIG1 0x09 +#define LM90_REG_R_CONFIG2 0xBF +#define LM90_REG_W_CONFIG2 0xBF +#define LM90_REG_R_CONVRATE 0x04 +#define LM90_REG_W_CONVRATE 0x0A +#define LM90_REG_R_STATUS 0x02 +#define LM90_REG_R_LOCAL_TEMP 0x00 +#define LM90_REG_R_LOCAL_HIGH 0x05 +#define LM90_REG_W_LOCAL_HIGH 0x0B +#define LM90_REG_R_LOCAL_LOW 0x06 +#define LM90_REG_W_LOCAL_LOW 0x0C +#define LM90_REG_R_LOCAL_CRIT 0x20 +#define LM90_REG_W_LOCAL_CRIT 0x20 +#define LM90_REG_R_REMOTE_TEMPH 0x01 +#define LM90_REG_R_REMOTE_TEMPL 0x10 +#define LM90_REG_R_REMOTE_OFFSH 0x11 +#define LM90_REG_W_REMOTE_OFFSH 0x11 +#define LM90_REG_R_REMOTE_OFFSL 0x12 +#define LM90_REG_W_REMOTE_OFFSL 0x12 +#define LM90_REG_R_REMOTE_HIGHH 0x07 +#define LM90_REG_W_REMOTE_HIGHH 0x0D +#define LM90_REG_R_REMOTE_HIGHL 0x13 +#define LM90_REG_W_REMOTE_HIGHL 0x13 +#define LM90_REG_R_REMOTE_LOWH 0x08 +#define LM90_REG_W_REMOTE_LOWH 0x0E +#define LM90_REG_R_REMOTE_LOWL 0x14 +#define LM90_REG_W_REMOTE_LOWL 0x14 +#define LM90_REG_R_REMOTE_CRIT 0x19 +#define LM90_REG_W_REMOTE_CRIT 0x19 +#define LM90_REG_R_TCRIT_HYST 0x21 +#define LM90_REG_W_TCRIT_HYST 0x21 + +/* + * Conversions and various macros + * For local temperatures and limits, critical limits and the hysteresis + * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. + * For remote temperatures and limits, it uses signed 11-bit values with + * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. + */ + +#define TEMP1_FROM_REG(val) ((val) * 1000) +#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \ + (val) >= 127000 ? 127 : \ + (val) < 0 ? ((val) - 500) / 1000 : \ + ((val) + 500) / 1000) +#define TEMP2_FROM_REG(val) ((val) / 32 * 125) +#define TEMP2_TO_REG(val) ((val) <= -128000 ? 0x8000 : \ + (val) >= 127875 ? 0x7FE0 : \ + (val) < 0 ? ((val) - 62) / 125 * 32 : \ + ((val) + 62) / 125 * 32) +#define HYST_TO_REG(val) ((val) <= 0 ? 0 : (val) >= 30500 ? 31 : \ + ((val) + 500) / 1000) + +/* + * ADT7461 is almost identical to LM90 except that attempts to write + * values that are outside the range 0 < temp < 127 are treated as + * the boundary value. + */ + +#define TEMP1_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ + (val) >= 127000 ? 127 : \ + ((val) + 500) / 1000) +#define TEMP2_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ + (val) >= 127750 ? 0x7FC0 : \ + ((val) + 125) / 250 * 64) + +/* + * Functions declaration + */ + +static int lm90_attach_adapter(struct i2c_adapter *adapter); +static int lm90_detect(struct i2c_adapter *adapter, int address, + int kind); +static void lm90_init_client(struct i2c_client *client); +static int lm90_detach_client(struct i2c_client *client); +static struct lm90_data *lm90_update_device(struct device *dev); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver lm90_driver = { + .owner = THIS_MODULE, + .name = "lm90", + .id = I2C_DRIVERID_LM90, + .flags = I2C_DF_NOTIFY, + .attach_adapter = lm90_attach_adapter, + .detach_client = lm90_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct lm90_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + int kind; + + /* registers values */ + s8 temp8[5]; /* 0: local input + 1: local low limit + 2: local high limit + 3: local critical limit + 4: remote critical limit */ + s16 temp11[3]; /* 0: remote input + 1: remote low limit + 2: remote high limit */ + u8 temp_hyst; + u8 alarms; /* bitvector */ +}; + +/* + * Sysfs stuff + */ + +static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm90_data *data = lm90_update_device(dev); + return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index])); +} + +static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + static const u8 reg[4] = { + LM90_REG_W_LOCAL_LOW, + LM90_REG_W_LOCAL_HIGH, + LM90_REG_W_LOCAL_CRIT, + LM90_REG_W_REMOTE_CRIT, + }; + + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct lm90_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + int nr = attr->index; + + down(&data->update_lock); + if (data->kind == adt7461) + data->temp8[nr] = TEMP1_TO_REG_ADT7461(val); + else + data->temp8[nr] = TEMP1_TO_REG(val); + i2c_smbus_write_byte_data(client, reg[nr - 1], data->temp8[nr]); + up(&data->update_lock); + return count; +} + +static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm90_data *data = lm90_update_device(dev); + return sprintf(buf, "%d\n", TEMP2_FROM_REG(data->temp11[attr->index])); +} + +static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + static const u8 reg[4] = { + LM90_REG_W_REMOTE_LOWH, + LM90_REG_W_REMOTE_LOWL, + LM90_REG_W_REMOTE_HIGHH, + LM90_REG_W_REMOTE_HIGHL, + }; + + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct lm90_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + int nr = attr->index; + + down(&data->update_lock); + if (data->kind == adt7461) + data->temp11[nr] = TEMP2_TO_REG_ADT7461(val); + else + data->temp11[nr] = TEMP2_TO_REG(val); + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], + data->temp11[nr] >> 8); + i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], + data->temp11[nr] & 0xff); + up(&data->update_lock); + return count; +} + +static ssize_t show_temphyst(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct lm90_data *data = lm90_update_device(dev); + return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index]) + - TEMP1_FROM_REG(data->temp_hyst)); +} + +static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm90_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + long hyst; + + down(&data->update_lock); + hyst = TEMP1_FROM_REG(data->temp8[3]) - val; + i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, + HYST_TO_REG(hyst)); + up(&data->update_lock); + return count; +} + +static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, + char *buf) +{ + struct lm90_data *data = lm90_update_device(dev); + return sprintf(buf, "%d\n", data->alarms); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 1); +static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 1); +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 2); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, + set_temp11, 2); +static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 3); +static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, + set_temp8, 4); +static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, + set_temphyst, 3); +static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +/* + * Real code + */ + +static int lm90_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, lm90_detect); +} + +/* + * The following function does more than just detection. If detection + * succeeds, it also registers the new chip. + */ +static int lm90_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct lm90_data *data; + int err = 0; + const char *name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct lm90_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct lm90_data)); + + /* The common I2C client data is placed right before the + LM90-specific data. */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lm90_driver; + new_client->flags = 0; + + /* + * 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 */ + u8 man_id, chip_id, reg_config1, reg_convrate; + + man_id = i2c_smbus_read_byte_data(new_client, + LM90_REG_R_MAN_ID); + chip_id = i2c_smbus_read_byte_data(new_client, + LM90_REG_R_CHIP_ID); + reg_config1 = i2c_smbus_read_byte_data(new_client, + LM90_REG_R_CONFIG1); + reg_convrate = i2c_smbus_read_byte_data(new_client, + LM90_REG_R_CONVRATE); + + if (man_id == 0x01) { /* National Semiconductor */ + u8 reg_config2; + + reg_config2 = i2c_smbus_read_byte_data(new_client, + LM90_REG_R_CONFIG2); + + 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; + } else + if (address == 0x4C + && (chip_id & 0xF0) == 0x10) { /* LM86 */ + kind = lm86; + } + } + } else + if (man_id == 0x41) { /* Analog Devices */ + if (address == 0x4C + && (chip_id & 0xF0) == 0x40 /* ADM1032 */ + && (reg_config1 & 0x3F) == 0x00 + && reg_convrate <= 0x0A) { + kind = adm1032; + } else + if (address == 0x4c + && chip_id == 0x51 /* ADT7461 */ + && (reg_config1 & 0x1F) == 0x00 /* check compat mode */ + && reg_convrate <= 0x0A) { + kind = adt7461; + } + } else + if (man_id == 0x4D) { /* Maxim */ + /* + * The Maxim variants 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 + && (reg_config1 & 0x1F) == (man_id & 0x0F) + && reg_convrate <= 0x09) { + kind = max6657; + } + } + + if (kind <= 0) { /* identification failed */ + dev_info(&adapter->dev, + "Unsupported chip (man_id=0x%02X, " + "chip_id=0x%02X).\n", man_id, chip_id); + goto exit_free; + } + } + + if (kind == lm90) { + name = "lm90"; + } else if (kind == adm1032) { + name = "adm1032"; + } else if (kind == lm99) { + name = "lm99"; + } else if (kind == lm86) { + name = "lm86"; + } else if (kind == max6657) { + name = "max6657"; + } else if (kind == adt7461) { + name = "adt7461"; + } + + /* We can fill in the remaining client fields */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->valid = 0; + data->kind = kind; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the LM90 chip */ + lm90_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_input.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_input.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_min.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_min.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_max.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_max.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_crit.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_crit.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp1_crit_hyst.dev_attr); + device_create_file(&new_client->dev, + &sensor_dev_attr_temp2_crit_hyst.dev_attr); + device_create_file(&new_client->dev, &dev_attr_alarms); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static void lm90_init_client(struct i2c_client *client) +{ + u8 config; + + /* + * Start the conversions. + */ + i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, + 5); /* 2 Hz */ + config = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1); + if (config & 0x40) + i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, + config & 0xBF); /* run */ +} + +static int lm90_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static struct lm90_data *lm90_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm90_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { + u8 oldh, newh; + + dev_dbg(&client->dev, "Updating lm90 data.\n"); + data->temp8[0] = i2c_smbus_read_byte_data(client, + LM90_REG_R_LOCAL_TEMP); + data->temp8[1] = i2c_smbus_read_byte_data(client, + LM90_REG_R_LOCAL_LOW); + data->temp8[2] = i2c_smbus_read_byte_data(client, + LM90_REG_R_LOCAL_HIGH); + data->temp8[3] = i2c_smbus_read_byte_data(client, + LM90_REG_R_LOCAL_CRIT); + data->temp8[4] = i2c_smbus_read_byte_data(client, + LM90_REG_R_REMOTE_CRIT); + data->temp_hyst = i2c_smbus_read_byte_data(client, + LM90_REG_R_TCRIT_HYST); + + /* + * There is a trick here. We have to read two registers to + * have the remote sensor temperature, but we have to beware + * a conversion could occur inbetween the readings. The + * datasheet says we should either use the one-shot + * conversion register, which we don't want to do (disables + * hardware monitoring) or monitor the busy bit, which is + * impossible (we can't read the values and monitor that bit + * at the exact same time). So the solution used here is to + * read the high byte once, then the low byte, then the high + * byte again. If the new high byte matches the old one, + * then we have a valid reading. Else we have to read the low + * byte again, and now we believe we have a correct reading. + */ + oldh = i2c_smbus_read_byte_data(client, + LM90_REG_R_REMOTE_TEMPH); + data->temp11[0] = i2c_smbus_read_byte_data(client, + LM90_REG_R_REMOTE_TEMPL); + newh = i2c_smbus_read_byte_data(client, + LM90_REG_R_REMOTE_TEMPH); + if (newh != oldh) { + data->temp11[0] = i2c_smbus_read_byte_data(client, + LM90_REG_R_REMOTE_TEMPL); +#ifdef DEBUG + oldh = i2c_smbus_read_byte_data(client, + LM90_REG_R_REMOTE_TEMPH); + /* oldh is actually newer */ + if (newh != oldh) + dev_warn(&client->dev, "Remote temperature may be " + "wrong.\n"); +#endif + } + data->temp11[0] |= (newh << 8); + + data->temp11[1] = (i2c_smbus_read_byte_data(client, + LM90_REG_R_REMOTE_LOWH) << 8) + + i2c_smbus_read_byte_data(client, + LM90_REG_R_REMOTE_LOWL); + data->temp11[2] = (i2c_smbus_read_byte_data(client, + LM90_REG_R_REMOTE_HIGHH) << 8) + + i2c_smbus_read_byte_data(client, + LM90_REG_R_REMOTE_HIGHL); + data->alarms = i2c_smbus_read_byte_data(client, + LM90_REG_R_STATUS); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_lm90_init(void) +{ + return i2c_add_driver(&lm90_driver); +} + +static void __exit sensors_lm90_exit(void) +{ + i2c_del_driver(&lm90_driver); +} + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("LM90/ADM1032 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm90_init); +module_exit(sensors_lm90_exit); diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c new file mode 100644 index 000000000000..215c8e40ffdd --- /dev/null +++ b/drivers/hwmon/lm92.c @@ -0,0 +1,429 @@ +/* + * lm92 - Hardware monitoring driver + * Copyright (C) 2005 Jean Delvare + * + * Based on the lm90 driver, with some ideas taken from the lm_sensors + * lm92 driver as well. + * + * The LM92 is a sensor chip made by National Semiconductor. It reports + * its own temperature with a 0.0625 deg resolution and a 0.33 deg + * accuracy. Complete datasheet can be obtained from National's website + * at: + * http://www.national.com/pf/LM/LM92.html + * + * This driver also supports the MAX6635 sensor chip made by Maxim. + * This chip is compatible with the LM92, but has a lesser accuracy + * (1.0 deg). Complete datasheet can be obtained from Maxim's website + * at: + * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3074 + * + * Since the LM92 was the first chipset supported by this driver, most + * comments will refer to this chipset, but are actually general and + * concern all supported chipsets, unless mentioned otherwise. + * + * Support could easily be added for the National Semiconductor LM76 + * and Maxim MAX6633 and MAX6634 chips, which are mostly compatible + * with the LM92. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + + +/* The LM92 and MAX6635 have 2 two-state pins for address selection, + resulting in 4 possible addresses. */ +static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, + I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(lm92); + +/* The LM92 registers */ +#define LM92_REG_CONFIG 0x01 /* 8-bit, RW */ +#define LM92_REG_TEMP 0x00 /* 16-bit, RO */ +#define LM92_REG_TEMP_HYST 0x02 /* 16-bit, RW */ +#define LM92_REG_TEMP_CRIT 0x03 /* 16-bit, RW */ +#define LM92_REG_TEMP_LOW 0x04 /* 16-bit, RW */ +#define LM92_REG_TEMP_HIGH 0x05 /* 16-bit, RW */ +#define LM92_REG_MAN_ID 0x07 /* 16-bit, RO, LM92 only */ + +/* The LM92 uses signed 13-bit values with LSB = 0.0625 degree Celsius, + left-justified in 16-bit registers. No rounding is done, with such + a resolution it's just not worth it. Note that the MAX6635 doesn't + make use of the 4 lower bits for limits (i.e. effective resolution + for limits is 1 degree Celsius). */ +static inline int TEMP_FROM_REG(s16 reg) +{ + return reg / 8 * 625 / 10; +} + +static inline s16 TEMP_TO_REG(int val) +{ + if (val <= -60000) + return -60000 * 10 / 625 * 8; + if (val >= 160000) + return 160000 * 10 / 625 * 8; + return val * 10 / 625 * 8; +} + +/* Alarm flags are stored in the 3 LSB of the temperature register */ +static inline u8 ALARMS_FROM_REG(s16 reg) +{ + return reg & 0x0007; +} + +/* Driver data (common to all clients) */ +static struct i2c_driver lm92_driver; + +/* Client data (each client gets its own) */ +struct lm92_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* registers values */ + s16 temp1_input, temp1_crit, temp1_min, temp1_max, temp1_hyst; +}; + + +/* + * Sysfs attributes and callback functions + */ + +static struct lm92_data *lm92_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm92_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) + || !data->valid) { + dev_dbg(&client->dev, "Updating lm92 data\n"); + data->temp1_input = swab16(i2c_smbus_read_word_data(client, + LM92_REG_TEMP)); + data->temp1_hyst = swab16(i2c_smbus_read_word_data(client, + LM92_REG_TEMP_HYST)); + data->temp1_crit = swab16(i2c_smbus_read_word_data(client, + LM92_REG_TEMP_CRIT)); + data->temp1_min = swab16(i2c_smbus_read_word_data(client, + LM92_REG_TEMP_LOW)); + data->temp1_max = swab16(i2c_smbus_read_word_data(client, + LM92_REG_TEMP_HIGH)); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +#define show_temp(value) \ +static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct lm92_data *data = lm92_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ +} +show_temp(temp1_input); +show_temp(temp1_crit); +show_temp(temp1_min); +show_temp(temp1_max); + +#define set_temp(value, reg) \ +static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct lm92_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->value = TEMP_TO_REG(val); \ + i2c_smbus_write_word_data(client, reg, swab16(data->value)); \ + up(&data->update_lock); \ + return count; \ +} +set_temp(temp1_crit, LM92_REG_TEMP_CRIT); +set_temp(temp1_min, LM92_REG_TEMP_LOW); +set_temp(temp1_max, LM92_REG_TEMP_HIGH); + +static ssize_t show_temp1_crit_hyst(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm92_data *data = lm92_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_crit) + - TEMP_FROM_REG(data->temp1_hyst)); +} +static ssize_t show_temp1_max_hyst(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm92_data *data = lm92_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_max) + - TEMP_FROM_REG(data->temp1_hyst)); +} +static ssize_t show_temp1_min_hyst(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm92_data *data = lm92_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_min) + + TEMP_FROM_REG(data->temp1_hyst)); +} + +static ssize_t set_temp1_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct lm92_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp1_hyst = TEMP_FROM_REG(data->temp1_crit) - val; + i2c_smbus_write_word_data(client, LM92_REG_TEMP_HYST, + swab16(TEMP_TO_REG(data->temp1_hyst))); + up(&data->update_lock); + return count; +} + +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct lm92_data *data = lm92_update_device(dev); + return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp1_input)); +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1_input, NULL); +static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp1_crit, + set_temp1_crit); +static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp1_crit_hyst, + set_temp1_crit_hyst); +static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp1_min, + set_temp1_min); +static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp1_min_hyst, NULL); +static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp1_max, + set_temp1_max); +static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp1_max_hyst, NULL); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + + +/* + * Detection and registration + */ + +static void lm92_init_client(struct i2c_client *client) +{ + u8 config; + + /* Start the conversions if needed */ + config = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG); + if (config & 0x01) + i2c_smbus_write_byte_data(client, LM92_REG_CONFIG, + config & 0xFE); +} + +/* The MAX6635 has no identification register, so we have to use tricks + to identify it reliably. This is somewhat slow. + Note that we do NOT rely on the 2 MSB of the configuration register + always reading 0, as suggested by the datasheet, because it was once + reported not to be true. */ +static int max6635_check(struct i2c_client *client) +{ + u16 temp_low, temp_high, temp_hyst, temp_crit; + u8 conf; + int i; + + /* No manufacturer ID register, so a read from this address will + always return the last read value. */ + temp_low = i2c_smbus_read_word_data(client, LM92_REG_TEMP_LOW); + if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_low) + return 0; + temp_high = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HIGH); + if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_high) + return 0; + + /* Limits are stored as integer values (signed, 9-bit). */ + if ((temp_low & 0x7f00) || (temp_high & 0x7f00)) + return 0; + temp_hyst = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HYST); + temp_crit = i2c_smbus_read_word_data(client, LM92_REG_TEMP_CRIT); + if ((temp_hyst & 0x7f00) || (temp_crit & 0x7f00)) + return 0; + + /* Registers addresses were found to cycle over 16-byte boundaries. + We don't test all registers with all offsets so as to save some + reads and time, but this should still be sufficient to dismiss + non-MAX6635 chips. */ + conf = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG); + for (i=16; i<96; i*=2) { + if (temp_hyst != i2c_smbus_read_word_data(client, + LM92_REG_TEMP_HYST + i - 16) + || temp_crit != i2c_smbus_read_word_data(client, + LM92_REG_TEMP_CRIT + i) + || temp_low != i2c_smbus_read_word_data(client, + LM92_REG_TEMP_LOW + i + 16) + || temp_high != i2c_smbus_read_word_data(client, + LM92_REG_TEMP_HIGH + i + 32) + || conf != i2c_smbus_read_byte_data(client, + LM92_REG_CONFIG + i)) + return 0; + } + + return 1; +} + +/* The following function does more than just detection. If detection + succeeds, it also registers the new chip. */ +static int lm92_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct lm92_data *data; + int err = 0; + char *name; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA + | I2C_FUNC_SMBUS_WORD_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct lm92_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct lm92_data)); + + /* Fill in enough client fields so that we can read from the chip, + which is required for identication */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &lm92_driver; + new_client->flags = 0; + + /* 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 + goto exit_free; + } else + if (kind == 0) /* Default to an LM92 if forced */ + kind = lm92; + + /* Give it the proper name */ + if (kind == lm92) { + name = "lm92"; + } else { /* Supposedly cannot happen */ + dev_dbg(&new_client->dev, "Kind out of range?\n"); + goto exit_free; + } + + /* Fill in the remaining client fields */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the i2c subsystem a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the chipset */ + lm92_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp1_crit); + device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst); + device_create_file(&new_client->dev, &dev_attr_temp1_min); + device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); + device_create_file(&new_client->dev, &dev_attr_alarms); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static int lm92_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, lm92_detect); +} + +static int lm92_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + + +/* + * Module and driver stuff + */ + +static struct i2c_driver lm92_driver = { + .owner = THIS_MODULE, + .name = "lm92", + .id = I2C_DRIVERID_LM92, + .flags = I2C_DF_NOTIFY, + .attach_adapter = lm92_attach_adapter, + .detach_client = lm92_detach_client, +}; + +static int __init sensors_lm92_init(void) +{ + return i2c_add_driver(&lm92_driver); +} + +static void __exit sensors_lm92_exit(void) +{ + i2c_del_driver(&lm92_driver); +} + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("LM92/MAX6635 driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_lm92_init); +module_exit(sensors_lm92_exit); diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c new file mode 100644 index 000000000000..bf553dcd97d6 --- /dev/null +++ b/drivers/hwmon/max1619.c @@ -0,0 +1,372 @@ +/* + * max1619.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2003-2004 Alexey Fisher + * Jean Delvare + * + * Based on the lm90 driver. The MAX1619 is a sensor chip made by Maxim. + * It reports up to two temperatures (its own plus up to + * one external one). Complete datasheet can be + * obtained from Maxim's website at: + * http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +#include +#include +#include +#include +#include +#include + + +static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, + 0x29, 0x2a, 0x2b, + 0x4c, 0x4d, 0x4e, + I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* + * Insmod parameters + */ + +SENSORS_INSMOD_1(max1619); + +/* + * The MAX1619 registers + */ + +#define MAX1619_REG_R_MAN_ID 0xFE +#define MAX1619_REG_R_CHIP_ID 0xFF +#define MAX1619_REG_R_CONFIG 0x03 +#define MAX1619_REG_W_CONFIG 0x09 +#define MAX1619_REG_R_CONVRATE 0x04 +#define MAX1619_REG_W_CONVRATE 0x0A +#define MAX1619_REG_R_STATUS 0x02 +#define MAX1619_REG_R_LOCAL_TEMP 0x00 +#define MAX1619_REG_R_REMOTE_TEMP 0x01 +#define MAX1619_REG_R_REMOTE_HIGH 0x07 +#define MAX1619_REG_W_REMOTE_HIGH 0x0D +#define MAX1619_REG_R_REMOTE_LOW 0x08 +#define MAX1619_REG_W_REMOTE_LOW 0x0E +#define MAX1619_REG_R_REMOTE_CRIT 0x10 +#define MAX1619_REG_W_REMOTE_CRIT 0x12 +#define MAX1619_REG_R_TCRIT_HYST 0x11 +#define MAX1619_REG_W_TCRIT_HYST 0x13 + +/* + * Conversions and various macros + */ + +#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000) +#define TEMP_TO_REG(val) ((val < 0 ? val+0x100*1000 : val) / 1000) + +/* + * Functions declaration + */ + +static int max1619_attach_adapter(struct i2c_adapter *adapter); +static int max1619_detect(struct i2c_adapter *adapter, int address, + int kind); +static void max1619_init_client(struct i2c_client *client); +static int max1619_detach_client(struct i2c_client *client); +static struct max1619_data *max1619_update_device(struct device *dev); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver max1619_driver = { + .owner = THIS_MODULE, + .name = "max1619", + .flags = I2C_DF_NOTIFY, + .attach_adapter = max1619_attach_adapter, + .detach_client = max1619_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct max1619_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* registers values */ + u8 temp_input1; /* local */ + u8 temp_input2, temp_low2, temp_high2; /* remote */ + u8 temp_crit2; + u8 temp_hyst2; + u8 alarms; +}; + +/* + * Sysfs stuff + */ + +#define show_temp(value) \ +static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct max1619_data *data = max1619_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ +} +show_temp(temp_input1); +show_temp(temp_input2); +show_temp(temp_low2); +show_temp(temp_high2); +show_temp(temp_crit2); +show_temp(temp_hyst2); + +#define set_temp2(value, reg) \ +static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct max1619_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->value = TEMP_TO_REG(val); \ + i2c_smbus_write_byte_data(client, reg, data->value); \ + up(&data->update_lock); \ + return count; \ +} + +set_temp2(temp_low2, MAX1619_REG_W_REMOTE_LOW); +set_temp2(temp_high2, MAX1619_REG_W_REMOTE_HIGH); +set_temp2(temp_crit2, MAX1619_REG_W_REMOTE_CRIT); +set_temp2(temp_hyst2, MAX1619_REG_W_TCRIT_HYST); + +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct max1619_data *data = max1619_update_device(dev); + return sprintf(buf, "%d\n", data->alarms); +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); +static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL); +static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_low2, + set_temp_low2); +static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_high2, + set_temp_high2); +static DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit2, + set_temp_crit2); +static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst2, + set_temp_hyst2); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +/* + * Real code + */ + +static int max1619_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, max1619_detect); +} + +/* + * The following function does more than just detection. If detection + * succeeds, it also registers the new chip. + */ +static int max1619_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct max1619_data *data; + int err = 0; + const char *name = ""; + u8 reg_config=0, reg_convrate=0, reg_status=0; + u8 man_id, chip_id; + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct max1619_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct max1619_data)); + + /* The common I2C client data is placed right before the + MAX1619-specific data. */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &max1619_driver; + new_client->flags = 0; + + /* + * 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", + address); + goto exit_free; + } + } + + if (kind <= 0) { /* identification */ + + 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); + goto exit_free; + } + + + if (kind == max1619){ + name = "max1619"; + } + + /* We can fill in the remaining client fields */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the MAX1619 chip */ + max1619_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp2_input); + device_create_file(&new_client->dev, &dev_attr_temp2_min); + device_create_file(&new_client->dev, &dev_attr_temp2_max); + device_create_file(&new_client->dev, &dev_attr_temp2_crit); + device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst); + device_create_file(&new_client->dev, &dev_attr_alarms); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static void max1619_init_client(struct i2c_client *client) +{ + u8 config; + + /* + * Start the conversions. + */ + i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE, + 5); /* 2 Hz */ + config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); + if (config & 0x40) + i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG, + config & 0xBF); /* run */ +} + +static int max1619_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static struct max1619_data *max1619_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct max1619_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { + dev_dbg(&client->dev, "Updating max1619 data.\n"); + data->temp_input1 = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_LOCAL_TEMP); + data->temp_input2 = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_REMOTE_TEMP); + data->temp_high2 = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_REMOTE_HIGH); + data->temp_low2 = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_REMOTE_LOW); + data->temp_crit2 = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_REMOTE_CRIT); + data->temp_hyst2 = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_TCRIT_HYST); + data->alarms = i2c_smbus_read_byte_data(client, + MAX1619_REG_R_STATUS); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_max1619_init(void) +{ + return i2c_add_driver(&max1619_driver); +} + +static void __exit sensors_max1619_exit(void) +{ + i2c_del_driver(&max1619_driver); +} + +MODULE_AUTHOR("Alexey Fisher and" + "Jean Delvare "); +MODULE_DESCRIPTION("MAX1619 sensor driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_max1619_init); +module_exit(sensors_max1619_exit); diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c new file mode 100644 index 000000000000..876c68f3af31 --- /dev/null +++ b/drivers/hwmon/pc87360.c @@ -0,0 +1,1348 @@ +/* + * pc87360.c - Part of lm_sensors, Linux kernel modules + * for hardware monitoring + * Copyright (C) 2004 Jean Delvare + * + * Copied from smsc47m1.c: + * Copyright (C) 2002 Mark D. Studebaker + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Supports the following chips: + * + * Chip #vin #fan #pwm #temp devid + * PC87360 - 2 2 - 0xE1 + * PC87363 - 2 2 - 0xE8 + * PC87364 - 3 3 - 0xE4 + * PC87365 11 3 3 2 0xE5 + * PC87366 11 3 3 3-4 0xE9 + * + * This driver assumes that no more than one chip is present, and one of + * the standard Super-I/O addresses is used (0x2E/0x2F or 0x4E/0x4F). + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +static unsigned int normal_isa[] = { 0, I2C_CLIENT_ISA_END }; +static struct i2c_force_data forces[] = {{ NULL }}; +static u8 devid; +static unsigned int extra_isa[3]; +static u8 confreg[4]; + +enum chips { any_chip, pc87360, pc87363, pc87364, pc87365, pc87366 }; +static struct i2c_address_data addr_data = { + .normal_i2c = normal_i2c, + .normal_isa = normal_isa, + .forces = forces, +}; + +static int init = 1; +module_param(init, int, 0); +MODULE_PARM_DESC(init, + "Chip initialization level:\n" + " 0: None\n" + "*1: Forcibly enable internal voltage and temperature channels, except in9\n" + " 2: Forcibly enable all voltage and temperature channels, except in9\n" + " 3: Forcibly enable all voltage and temperature channels, including in9"); + +/* + * Super-I/O registers and operations + */ + +#define DEV 0x07 /* Register: Logical device select */ +#define DEVID 0x20 /* Register: Device ID */ +#define ACT 0x30 /* Register: Device activation */ +#define BASE 0x60 /* Register: Base address */ + +#define FSCM 0x09 /* Logical device: fans */ +#define VLM 0x0d /* Logical device: voltages */ +#define TMS 0x0e /* Logical device: temperatures */ +static const u8 logdev[3] = { FSCM, VLM, TMS }; + +#define LD_FAN 0 +#define LD_IN 1 +#define LD_TEMP 2 + +static inline void superio_outb(int sioaddr, int reg, int val) +{ + outb(reg, sioaddr); + outb(val, sioaddr+1); +} + +static inline int superio_inb(int sioaddr, int reg) +{ + outb(reg, sioaddr); + return inb(sioaddr+1); +} + +static inline void superio_exit(int sioaddr) +{ + outb(0x02, sioaddr); + outb(0x02, sioaddr+1); +} + +/* + * Logical devices + */ + +#define PC87360_EXTENT 0x10 +#define PC87365_REG_BANK 0x09 +#define NO_BANK 0xff + +/* + * Fan registers and conversions + */ + +/* nr has to be 0 or 1 (PC87360/87363) or 2 (PC87364/87365/87366) */ +#define PC87360_REG_PRESCALE(nr) (0x00 + 2 * (nr)) +#define PC87360_REG_PWM(nr) (0x01 + 2 * (nr)) +#define PC87360_REG_FAN_MIN(nr) (0x06 + 3 * (nr)) +#define PC87360_REG_FAN(nr) (0x07 + 3 * (nr)) +#define PC87360_REG_FAN_STATUS(nr) (0x08 + 3 * (nr)) + +#define FAN_FROM_REG(val,div) ((val) == 0 ? 0: \ + 480000 / ((val)*(div))) +#define FAN_TO_REG(val,div) ((val) <= 100 ? 0 : \ + 480000 / ((val)*(div))) +#define FAN_DIV_FROM_REG(val) (1 << ((val >> 5) & 0x03)) +#define FAN_STATUS_FROM_REG(val) ((val) & 0x07) + +#define FAN_CONFIG_MONITOR(val,nr) (((val) >> (2 + nr * 3)) & 1) +#define FAN_CONFIG_CONTROL(val,nr) (((val) >> (3 + nr * 3)) & 1) +#define FAN_CONFIG_INVERT(val,nr) (((val) >> (4 + nr * 3)) & 1) + +#define PWM_FROM_REG(val,inv) ((inv) ? 255 - (val) : (val)) +static inline u8 PWM_TO_REG(int val, int inv) +{ + if (inv) + val = 255 - val; + if (val < 0) + return 0; + if (val > 255) + return 255; + return val; +} + +/* + * Voltage registers and conversions + */ + +#define PC87365_REG_IN_CONVRATE 0x07 +#define PC87365_REG_IN_CONFIG 0x08 +#define PC87365_REG_IN 0x0B +#define PC87365_REG_IN_MIN 0x0D +#define PC87365_REG_IN_MAX 0x0C +#define PC87365_REG_IN_STATUS 0x0A +#define PC87365_REG_IN_ALARMS1 0x00 +#define PC87365_REG_IN_ALARMS2 0x01 +#define PC87365_REG_VID 0x06 + +#define IN_FROM_REG(val,ref) (((val) * (ref) + 128) / 256) +#define IN_TO_REG(val,ref) ((val) < 0 ? 0 : \ + (val)*256 >= (ref)*255 ? 255: \ + ((val) * 256 + (ref)/2) / (ref)) + +/* + * Temperature registers and conversions + */ + +#define PC87365_REG_TEMP_CONFIG 0x08 +#define PC87365_REG_TEMP 0x0B +#define PC87365_REG_TEMP_MIN 0x0D +#define PC87365_REG_TEMP_MAX 0x0C +#define PC87365_REG_TEMP_CRIT 0x0E +#define PC87365_REG_TEMP_STATUS 0x0A +#define PC87365_REG_TEMP_ALARMS 0x00 + +#define TEMP_FROM_REG(val) ((val) * 1000) +#define TEMP_TO_REG(val) ((val) < -55000 ? -55 : \ + (val) > 127000 ? 127 : \ + (val) < 0 ? ((val) - 500) / 1000 : \ + ((val) + 500) / 1000) + +/* + * Client data (each client gets its own) + */ + +struct pc87360_data { + struct i2c_client client; + struct semaphore lock; + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + int address[3]; + + u8 fannr, innr, tempnr; + + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 fan_status[3]; /* Register value */ + u8 pwm[3]; /* Register value */ + u16 fan_conf; /* Configuration register values, combined */ + + u16 in_vref; /* 1 mV/bit */ + u8 in[14]; /* Register value */ + u8 in_min[14]; /* Register value */ + u8 in_max[14]; /* Register value */ + u8 in_crit[3]; /* Register value */ + u8 in_status[14]; /* Register value */ + u16 in_alarms; /* Register values, combined, masked */ + u8 vid_conf; /* Configuration register value */ + u8 vrm; + u8 vid; /* Register value */ + + s8 temp[3]; /* Register value */ + s8 temp_min[3]; /* Register value */ + s8 temp_max[3]; /* Register value */ + s8 temp_crit[3]; /* Register value */ + u8 temp_status[3]; /* Register value */ + u8 temp_alarms; /* Register value, masked */ +}; + +/* + * Functions declaration + */ + +static int pc87360_attach_adapter(struct i2c_adapter *adapter); +static int pc87360_detect(struct i2c_adapter *adapter, int address, int kind); +static int pc87360_detach_client(struct i2c_client *client); + +static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, + u8 reg); +static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, + u8 reg, u8 value); +static void pc87360_init_client(struct i2c_client *client, int use_thermistors); +static struct pc87360_data *pc87360_update_device(struct device *dev); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver pc87360_driver = { + .owner = THIS_MODULE, + .name = "pc87360", + .flags = I2C_DF_NOTIFY, + .attach_adapter = pc87360_attach_adapter, + .detach_client = pc87360_detach_client, +}; + +/* + * Sysfs stuff + */ + +static ssize_t set_fan_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pc87360_data *data = i2c_get_clientdata(client); + long fan_min = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + fan_min = FAN_TO_REG(fan_min, FAN_DIV_FROM_REG(data->fan_status[nr])); + + /* If it wouldn't fit, change clock divisor */ + while (fan_min > 255 + && (data->fan_status[nr] & 0x60) != 0x60) { + fan_min >>= 1; + data->fan[nr] >>= 1; + data->fan_status[nr] += 0x20; + } + data->fan_min[nr] = fan_min > 255 ? 255 : fan_min; + pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_MIN(nr), + data->fan_min[nr]); + + /* Write new divider, preserve alarm bits */ + pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_STATUS(nr), + data->fan_status[nr] & 0xF9); + up(&data->update_lock); + + return count; +} + +#define show_and_set_fan(offset) \ +static ssize_t show_fan##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[offset-1], \ + FAN_DIV_FROM_REG(data->fan_status[offset-1]))); \ +} \ +static ssize_t show_fan##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[offset-1], \ + FAN_DIV_FROM_REG(data->fan_status[offset-1]))); \ +} \ +static ssize_t show_fan##offset##_div(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", \ + FAN_DIV_FROM_REG(data->fan_status[offset-1])); \ +} \ +static ssize_t show_fan##offset##_status(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", \ + FAN_STATUS_FROM_REG(data->fan_status[offset-1])); \ +} \ +static ssize_t set_fan##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + return set_fan_min(dev, buf, count, offset-1); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ + show_fan##offset##_input, NULL); \ +static DEVICE_ATTR(fan##offset##_min, S_IWUSR | S_IRUGO, \ + show_fan##offset##_min, set_fan##offset##_min); \ +static DEVICE_ATTR(fan##offset##_div, S_IRUGO, \ + show_fan##offset##_div, NULL); \ +static DEVICE_ATTR(fan##offset##_status, S_IRUGO, \ + show_fan##offset##_status, NULL); +show_and_set_fan(1) +show_and_set_fan(2) +show_and_set_fan(3) + +#define show_and_set_pwm(offset) \ +static ssize_t show_pwm##offset(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", \ + PWM_FROM_REG(data->pwm[offset-1], \ + FAN_CONFIG_INVERT(data->fan_conf, \ + offset-1))); \ +} \ +static ssize_t set_pwm##offset(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct pc87360_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->pwm[offset-1] = PWM_TO_REG(val, \ + FAN_CONFIG_INVERT(data->fan_conf, offset-1)); \ + pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(offset-1), \ + data->pwm[offset-1]); \ + up(&data->update_lock); \ + return count; \ +} \ +static DEVICE_ATTR(pwm##offset, S_IWUSR | S_IRUGO, \ + show_pwm##offset, set_pwm##offset); +show_and_set_pwm(1) +show_and_set_pwm(2) +show_and_set_pwm(3) + +#define show_and_set_in(offset) \ +static ssize_t show_in##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \ + data->in_vref)); \ +} \ +static ssize_t show_in##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \ + data->in_vref)); \ +} \ +static ssize_t show_in##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \ + data->in_vref)); \ +} \ +static ssize_t show_in##offset##_status(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", data->in_status[offset]); \ +} \ +static ssize_t set_in##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct pc87360_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->in_min[offset] = IN_TO_REG(val, data->in_vref); \ + pc87360_write_value(data, LD_IN, offset, PC87365_REG_IN_MIN, \ + data->in_min[offset]); \ + up(&data->update_lock); \ + return count; \ +} \ +static ssize_t set_in##offset##_max(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct pc87360_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->in_max[offset] = IN_TO_REG(val, \ + data->in_vref); \ + pc87360_write_value(data, LD_IN, offset, PC87365_REG_IN_MAX, \ + data->in_max[offset]); \ + up(&data->update_lock); \ + return count; \ +} \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ + show_in##offset##_input, NULL); \ +static DEVICE_ATTR(in##offset##_min, S_IWUSR | S_IRUGO, \ + show_in##offset##_min, set_in##offset##_min); \ +static DEVICE_ATTR(in##offset##_max, S_IWUSR | S_IRUGO, \ + show_in##offset##_max, set_in##offset##_max); \ +static DEVICE_ATTR(in##offset##_status, S_IRUGO, \ + show_in##offset##_status, NULL); +show_and_set_in(0) +show_and_set_in(1) +show_and_set_in(2) +show_and_set_in(3) +show_and_set_in(4) +show_and_set_in(5) +show_and_set_in(6) +show_and_set_in(7) +show_and_set_in(8) +show_and_set_in(9) +show_and_set_in(10) + +#define show_and_set_therm(offset) \ +static ssize_t show_temp##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset+7], \ + data->in_vref)); \ +} \ +static ssize_t show_temp##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset+7], \ + data->in_vref)); \ +} \ +static ssize_t show_temp##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset+7], \ + data->in_vref)); \ +} \ +static ssize_t show_temp##offset##_crit(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[offset-4], \ + data->in_vref)); \ +} \ +static ssize_t show_temp##offset##_status(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%u\n", data->in_status[offset+7]); \ +} \ +static ssize_t set_temp##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct pc87360_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->in_min[offset+7] = IN_TO_REG(val, data->in_vref); \ + pc87360_write_value(data, LD_IN, offset+7, PC87365_REG_TEMP_MIN, \ + data->in_min[offset+7]); \ + up(&data->update_lock); \ + return count; \ +} \ +static ssize_t set_temp##offset##_max(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct pc87360_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->in_max[offset+7] = IN_TO_REG(val, data->in_vref); \ + pc87360_write_value(data, LD_IN, offset+7, PC87365_REG_TEMP_MAX, \ + data->in_max[offset+7]); \ + up(&data->update_lock); \ + return count; \ +} \ +static ssize_t set_temp##offset##_crit(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct pc87360_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->in_crit[offset-4] = IN_TO_REG(val, data->in_vref); \ + pc87360_write_value(data, LD_IN, offset+7, PC87365_REG_TEMP_CRIT, \ + data->in_crit[offset-4]); \ + up(&data->update_lock); \ + return count; \ +} \ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ + show_temp##offset##_input, NULL); \ +static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \ + show_temp##offset##_min, set_temp##offset##_min); \ +static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \ + show_temp##offset##_max, set_temp##offset##_max); \ +static DEVICE_ATTR(temp##offset##_crit, S_IWUSR | S_IRUGO, \ + show_temp##offset##_crit, set_temp##offset##_crit); \ +static DEVICE_ATTR(temp##offset##_status, S_IRUGO, \ + show_temp##offset##_status, NULL); +show_and_set_therm(4) +show_and_set_therm(5) +show_and_set_therm(6) + +static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm)); +} +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); + +static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", data->vrm); +} +static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pc87360_data *data = i2c_get_clientdata(client); + data->vrm = simple_strtoul(buf, NULL, 10); + return count; +} +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); + +static ssize_t show_in_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", data->in_alarms); +} +static DEVICE_ATTR(alarms_in, S_IRUGO, show_in_alarms, NULL); + +#define show_and_set_temp(offset) \ +static ssize_t show_temp##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \ +} \ +static ssize_t show_temp##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[offset-1])); \ +} \ +static ssize_t show_temp##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[offset-1])); \ +}\ +static ssize_t show_temp##offset##_crit(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[offset-1])); \ +}\ +static ssize_t show_temp##offset##_status(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct pc87360_data *data = pc87360_update_device(dev); \ + return sprintf(buf, "%d\n", data->temp_status[offset-1]); \ +}\ +static ssize_t set_temp##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct pc87360_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->temp_min[offset-1] = TEMP_TO_REG(val); \ + pc87360_write_value(data, LD_TEMP, offset-1, PC87365_REG_TEMP_MIN, \ + data->temp_min[offset-1]); \ + up(&data->update_lock); \ + return count; \ +} \ +static ssize_t set_temp##offset##_max(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct pc87360_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->temp_max[offset-1] = TEMP_TO_REG(val); \ + pc87360_write_value(data, LD_TEMP, offset-1, PC87365_REG_TEMP_MAX, \ + data->temp_max[offset-1]); \ + up(&data->update_lock); \ + return count; \ +} \ +static ssize_t set_temp##offset##_crit(struct device *dev, struct device_attribute *attr, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct pc87360_data *data = i2c_get_clientdata(client); \ + long val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->temp_crit[offset-1] = TEMP_TO_REG(val); \ + pc87360_write_value(data, LD_TEMP, offset-1, PC87365_REG_TEMP_CRIT, \ + data->temp_crit[offset-1]); \ + up(&data->update_lock); \ + return count; \ +} \ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ + show_temp##offset##_input, NULL); \ +static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \ + show_temp##offset##_min, set_temp##offset##_min); \ +static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \ + show_temp##offset##_max, set_temp##offset##_max); \ +static DEVICE_ATTR(temp##offset##_crit, S_IWUSR | S_IRUGO, \ + show_temp##offset##_crit, set_temp##offset##_crit); \ +static DEVICE_ATTR(temp##offset##_status, S_IRUGO, \ + show_temp##offset##_status, NULL); +show_and_set_temp(1) +show_and_set_temp(2) +show_and_set_temp(3) + +static ssize_t show_temp_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct pc87360_data *data = pc87360_update_device(dev); + return sprintf(buf, "%u\n", data->temp_alarms); +} +static DEVICE_ATTR(alarms_temp, S_IRUGO, show_temp_alarms, NULL); + +/* + * Device detection, registration and update + */ + +static int pc87360_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, pc87360_detect); +} + +static int pc87360_find(int sioaddr, u8 *devid, int *address) +{ + u16 val; + int i; + int nrdev; /* logical device count */ + + /* No superio_enter */ + + /* Identify device */ + val = superio_inb(sioaddr, DEVID); + switch (val) { + case 0xE1: /* PC87360 */ + case 0xE8: /* PC87363 */ + case 0xE4: /* PC87364 */ + nrdev = 1; + break; + case 0xE5: /* PC87365 */ + case 0xE9: /* PC87366 */ + nrdev = 3; + break; + default: + superio_exit(sioaddr); + return -ENODEV; + } + /* Remember the device id */ + *devid = val; + + for (i = 0; i < nrdev; i++) { + /* select logical device */ + superio_outb(sioaddr, DEV, logdev[i]); + + val = superio_inb(sioaddr, ACT); + if (!(val & 0x01)) { + printk(KERN_INFO "pc87360: Device 0x%02x not " + "activated\n", logdev[i]); + continue; + } + + val = (superio_inb(sioaddr, BASE) << 8) + | superio_inb(sioaddr, BASE + 1); + if (!val) { + printk(KERN_INFO "pc87360: Base address not set for " + "device 0x%02x\n", logdev[i]); + continue; + } + + address[i] = val; + + if (i==0) { /* Fans */ + confreg[0] = superio_inb(sioaddr, 0xF0); + confreg[1] = superio_inb(sioaddr, 0xF1); + +#ifdef DEBUG + printk(KERN_DEBUG "pc87360: Fan 1: mon=%d " + "ctrl=%d inv=%d\n", (confreg[0]>>2)&1, + (confreg[0]>>3)&1, (confreg[0]>>4)&1); + printk(KERN_DEBUG "pc87360: Fan 2: mon=%d " + "ctrl=%d inv=%d\n", (confreg[0]>>5)&1, + (confreg[0]>>6)&1, (confreg[0]>>7)&1); + printk(KERN_DEBUG "pc87360: Fan 3: mon=%d " + "ctrl=%d inv=%d\n", confreg[1]&1, + (confreg[1]>>1)&1, (confreg[1]>>2)&1); +#endif + } else if (i==1) { /* Voltages */ + /* Are we using thermistors? */ + if (*devid == 0xE9) { /* PC87366 */ + /* These registers are not logical-device + specific, just that we won't need them if + we don't use the VLM device */ + confreg[2] = superio_inb(sioaddr, 0x2B); + confreg[3] = superio_inb(sioaddr, 0x25); + + if (confreg[2] & 0x40) { + printk(KERN_INFO "pc87360: Using " + "thermistors for temperature " + "monitoring\n"); + } + if (confreg[3] & 0xE0) { + printk(KERN_INFO "pc87360: VID " + "inputs routed (mode %u)\n", + confreg[3] >> 5); + } + } + } + } + + superio_exit(sioaddr); + return 0; +} + +/* We don't really care about the address. + Read from extra_isa instead. */ +int pc87360_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int i; + struct i2c_client *new_client; + struct pc87360_data *data; + int err = 0; + const char *name = "pc87360"; + int use_thermistors = 0; + + if (!i2c_is_isa_adapter(adapter)) + return -ENODEV; + + if (!(data = kmalloc(sizeof(struct pc87360_data), GFP_KERNEL))) + return -ENOMEM; + memset(data, 0x00, sizeof(struct pc87360_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + init_MUTEX(&data->lock); + new_client->adapter = adapter; + new_client->driver = &pc87360_driver; + new_client->flags = 0; + + data->fannr = 2; + data->innr = 0; + data->tempnr = 0; + + switch (devid) { + case 0xe8: + name = "pc87363"; + break; + case 0xe4: + name = "pc87364"; + data->fannr = 3; + break; + case 0xe5: + name = "pc87365"; + data->fannr = extra_isa[0] ? 3 : 0; + data->innr = extra_isa[1] ? 11 : 0; + data->tempnr = extra_isa[2] ? 2 : 0; + break; + case 0xe9: + name = "pc87366"; + data->fannr = extra_isa[0] ? 3 : 0; + data->innr = extra_isa[1] ? 14 : 0; + data->tempnr = extra_isa[2] ? 3 : 0; + break; + } + + strcpy(new_client->name, name); + data->valid = 0; + init_MUTEX(&data->update_lock); + + for (i = 0; i < 3; i++) { + if (((data->address[i] = extra_isa[i])) + && !request_region(extra_isa[i], PC87360_EXTENT, + pc87360_driver.name)) { + dev_err(&new_client->dev, "Region 0x%x-0x%x already " + "in use!\n", extra_isa[i], + extra_isa[i]+PC87360_EXTENT-1); + for (i--; i >= 0; i--) + release_region(extra_isa[i], PC87360_EXTENT); + err = -EBUSY; + goto ERROR1; + } + } + + /* Retrieve the fans configuration from Super-I/O space */ + if (data->fannr) + data->fan_conf = confreg[0] | (confreg[1] << 8); + + if ((err = i2c_attach_client(new_client))) + goto ERROR2; + + /* Use the correct reference voltage + Unless both the VLM and the TMS logical devices agree to + use an external Vref, the internal one is used. */ + if (data->innr) { + i = pc87360_read_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONFIG); + if (data->tempnr) { + i &= pc87360_read_value(data, LD_TEMP, NO_BANK, + PC87365_REG_TEMP_CONFIG); + } + data->in_vref = (i&0x02) ? 3025 : 2966; + dev_dbg(&new_client->dev, "Using %s reference voltage\n", + (i&0x02) ? "external" : "internal"); + + data->vid_conf = confreg[3]; + data->vrm = 90; + } + + /* Fan clock dividers may be needed before any data is read */ + for (i = 0; i < data->fannr; i++) { + if (FAN_CONFIG_MONITOR(data->fan_conf, i)) + data->fan_status[i] = pc87360_read_value(data, + LD_FAN, NO_BANK, + PC87360_REG_FAN_STATUS(i)); + } + + if (init > 0) { + if (devid == 0xe9 && data->address[1]) /* PC87366 */ + use_thermistors = confreg[2] & 0x40; + + pc87360_init_client(new_client, use_thermistors); + } + + /* Register sysfs hooks */ + if (data->innr) { + device_create_file(&new_client->dev, &dev_attr_in0_input); + device_create_file(&new_client->dev, &dev_attr_in1_input); + device_create_file(&new_client->dev, &dev_attr_in2_input); + device_create_file(&new_client->dev, &dev_attr_in3_input); + device_create_file(&new_client->dev, &dev_attr_in4_input); + device_create_file(&new_client->dev, &dev_attr_in5_input); + device_create_file(&new_client->dev, &dev_attr_in6_input); + device_create_file(&new_client->dev, &dev_attr_in7_input); + device_create_file(&new_client->dev, &dev_attr_in8_input); + device_create_file(&new_client->dev, &dev_attr_in9_input); + device_create_file(&new_client->dev, &dev_attr_in10_input); + device_create_file(&new_client->dev, &dev_attr_in0_min); + device_create_file(&new_client->dev, &dev_attr_in1_min); + device_create_file(&new_client->dev, &dev_attr_in2_min); + device_create_file(&new_client->dev, &dev_attr_in3_min); + device_create_file(&new_client->dev, &dev_attr_in4_min); + device_create_file(&new_client->dev, &dev_attr_in5_min); + device_create_file(&new_client->dev, &dev_attr_in6_min); + device_create_file(&new_client->dev, &dev_attr_in7_min); + device_create_file(&new_client->dev, &dev_attr_in8_min); + device_create_file(&new_client->dev, &dev_attr_in9_min); + device_create_file(&new_client->dev, &dev_attr_in10_min); + device_create_file(&new_client->dev, &dev_attr_in0_max); + device_create_file(&new_client->dev, &dev_attr_in1_max); + device_create_file(&new_client->dev, &dev_attr_in2_max); + device_create_file(&new_client->dev, &dev_attr_in3_max); + device_create_file(&new_client->dev, &dev_attr_in4_max); + device_create_file(&new_client->dev, &dev_attr_in5_max); + device_create_file(&new_client->dev, &dev_attr_in6_max); + device_create_file(&new_client->dev, &dev_attr_in7_max); + device_create_file(&new_client->dev, &dev_attr_in8_max); + device_create_file(&new_client->dev, &dev_attr_in9_max); + device_create_file(&new_client->dev, &dev_attr_in10_max); + device_create_file(&new_client->dev, &dev_attr_in0_status); + device_create_file(&new_client->dev, &dev_attr_in1_status); + device_create_file(&new_client->dev, &dev_attr_in2_status); + device_create_file(&new_client->dev, &dev_attr_in3_status); + device_create_file(&new_client->dev, &dev_attr_in4_status); + device_create_file(&new_client->dev, &dev_attr_in5_status); + device_create_file(&new_client->dev, &dev_attr_in6_status); + device_create_file(&new_client->dev, &dev_attr_in7_status); + device_create_file(&new_client->dev, &dev_attr_in8_status); + device_create_file(&new_client->dev, &dev_attr_in9_status); + device_create_file(&new_client->dev, &dev_attr_in10_status); + + device_create_file(&new_client->dev, &dev_attr_cpu0_vid); + device_create_file(&new_client->dev, &dev_attr_vrm); + device_create_file(&new_client->dev, &dev_attr_alarms_in); + } + + if (data->tempnr) { + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp2_input); + device_create_file(&new_client->dev, &dev_attr_temp1_min); + device_create_file(&new_client->dev, &dev_attr_temp2_min); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp2_max); + device_create_file(&new_client->dev, &dev_attr_temp1_crit); + device_create_file(&new_client->dev, &dev_attr_temp2_crit); + device_create_file(&new_client->dev, &dev_attr_temp1_status); + device_create_file(&new_client->dev, &dev_attr_temp2_status); + + device_create_file(&new_client->dev, &dev_attr_alarms_temp); + } + if (data->tempnr == 3) { + device_create_file(&new_client->dev, &dev_attr_temp3_input); + device_create_file(&new_client->dev, &dev_attr_temp3_min); + device_create_file(&new_client->dev, &dev_attr_temp3_max); + device_create_file(&new_client->dev, &dev_attr_temp3_crit); + device_create_file(&new_client->dev, &dev_attr_temp3_status); + } + if (data->innr == 14) { + device_create_file(&new_client->dev, &dev_attr_temp4_input); + device_create_file(&new_client->dev, &dev_attr_temp5_input); + device_create_file(&new_client->dev, &dev_attr_temp6_input); + device_create_file(&new_client->dev, &dev_attr_temp4_min); + device_create_file(&new_client->dev, &dev_attr_temp5_min); + device_create_file(&new_client->dev, &dev_attr_temp6_min); + device_create_file(&new_client->dev, &dev_attr_temp4_max); + device_create_file(&new_client->dev, &dev_attr_temp5_max); + device_create_file(&new_client->dev, &dev_attr_temp6_max); + device_create_file(&new_client->dev, &dev_attr_temp4_crit); + device_create_file(&new_client->dev, &dev_attr_temp5_crit); + device_create_file(&new_client->dev, &dev_attr_temp6_crit); + device_create_file(&new_client->dev, &dev_attr_temp4_status); + device_create_file(&new_client->dev, &dev_attr_temp5_status); + device_create_file(&new_client->dev, &dev_attr_temp6_status); + } + + if (data->fannr) { + if (FAN_CONFIG_MONITOR(data->fan_conf, 0)) { + device_create_file(&new_client->dev, + &dev_attr_fan1_input); + device_create_file(&new_client->dev, + &dev_attr_fan1_min); + device_create_file(&new_client->dev, + &dev_attr_fan1_div); + device_create_file(&new_client->dev, + &dev_attr_fan1_status); + } + + if (FAN_CONFIG_MONITOR(data->fan_conf, 1)) { + device_create_file(&new_client->dev, + &dev_attr_fan2_input); + device_create_file(&new_client->dev, + &dev_attr_fan2_min); + device_create_file(&new_client->dev, + &dev_attr_fan2_div); + device_create_file(&new_client->dev, + &dev_attr_fan2_status); + } + + if (FAN_CONFIG_CONTROL(data->fan_conf, 0)) + device_create_file(&new_client->dev, &dev_attr_pwm1); + if (FAN_CONFIG_CONTROL(data->fan_conf, 1)) + device_create_file(&new_client->dev, &dev_attr_pwm2); + } + if (data->fannr == 3) { + if (FAN_CONFIG_MONITOR(data->fan_conf, 2)) { + device_create_file(&new_client->dev, + &dev_attr_fan3_input); + device_create_file(&new_client->dev, + &dev_attr_fan3_min); + device_create_file(&new_client->dev, + &dev_attr_fan3_div); + device_create_file(&new_client->dev, + &dev_attr_fan3_status); + } + + if (FAN_CONFIG_CONTROL(data->fan_conf, 2)) + device_create_file(&new_client->dev, &dev_attr_pwm3); + } + + return 0; + +ERROR2: + for (i = 0; i < 3; i++) { + if (data->address[i]) { + release_region(data->address[i], PC87360_EXTENT); + } + } +ERROR1: + kfree(data); + return err; +} + +static int pc87360_detach_client(struct i2c_client *client) +{ + struct pc87360_data *data = i2c_get_clientdata(client); + int i; + + if ((i = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return i; + } + + for (i = 0; i < 3; i++) { + if (data->address[i]) { + release_region(data->address[i], PC87360_EXTENT); + } + } + kfree(data); + + return 0; +} + +/* ldi is the logical device index + bank is for voltages and temperatures only */ +static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, + u8 reg) +{ + int res; + + down(&(data->lock)); + if (bank != NO_BANK) + outb_p(bank, data->address[ldi] + PC87365_REG_BANK); + res = inb_p(data->address[ldi] + reg); + up(&(data->lock)); + + return res; +} + +static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, + u8 reg, u8 value) +{ + down(&(data->lock)); + if (bank != NO_BANK) + outb_p(bank, data->address[ldi] + PC87365_REG_BANK); + outb_p(value, data->address[ldi] + reg); + up(&(data->lock)); +} + +static void pc87360_init_client(struct i2c_client *client, int use_thermistors) +{ + struct pc87360_data *data = i2c_get_clientdata(client); + int i, nr; + const u8 init_in[14] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2 }; + const u8 init_temp[3] = { 2, 2, 1 }; + u8 reg; + + if (init >= 2 && data->innr) { + reg = pc87360_read_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONVRATE); + dev_info(&client->dev, "VLM conversion set to" + "1s period, 160us delay\n"); + pc87360_write_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONVRATE, + (reg & 0xC0) | 0x11); + } + + nr = data->innr < 11 ? data->innr : 11; + for (i=0; i= init_in[i]) { + /* Forcibly enable voltage channel */ + reg = pc87360_read_value(data, LD_IN, i, + PC87365_REG_IN_STATUS); + if (!(reg & 0x01)) { + dev_dbg(&client->dev, "Forcibly " + "enabling in%d\n", i); + pc87360_write_value(data, LD_IN, i, + PC87365_REG_IN_STATUS, + (reg & 0x68) | 0x87); + } + } + } + + /* We can't blindly trust the Super-I/O space configuration bit, + most BIOS won't set it properly */ + for (i=11; iinnr; i++) { + reg = pc87360_read_value(data, LD_IN, i, + PC87365_REG_TEMP_STATUS); + use_thermistors = use_thermistors || (reg & 0x01); + } + + i = use_thermistors ? 2 : 0; + for (; itempnr; i++) { + if (init >= init_temp[i]) { + /* Forcibly enable temperature channel */ + reg = pc87360_read_value(data, LD_TEMP, i, + PC87365_REG_TEMP_STATUS); + if (!(reg & 0x01)) { + dev_dbg(&client->dev, "Forcibly " + "enabling temp%d\n", i+1); + pc87360_write_value(data, LD_TEMP, i, + PC87365_REG_TEMP_STATUS, + 0xCF); + } + } + } + + if (use_thermistors) { + for (i=11; iinnr; i++) { + if (init >= init_in[i]) { + /* The pin may already be used by thermal + diodes */ + reg = pc87360_read_value(data, LD_TEMP, + (i-11)/2, PC87365_REG_TEMP_STATUS); + if (reg & 0x01) { + dev_dbg(&client->dev, "Skipping " + "temp%d, pin already in use " + "by temp%d\n", i-7, (i-11)/2); + continue; + } + + /* Forcibly enable thermistor channel */ + reg = pc87360_read_value(data, LD_IN, i, + PC87365_REG_IN_STATUS); + if (!(reg & 0x01)) { + dev_dbg(&client->dev, "Forcibly " + "enabling temp%d\n", i-7); + pc87360_write_value(data, LD_IN, i, + PC87365_REG_TEMP_STATUS, + (reg & 0x60) | 0x8F); + } + } + } + } + + if (data->innr) { + reg = pc87360_read_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONFIG); + if (reg & 0x01) { + dev_dbg(&client->dev, "Forcibly " + "enabling monitoring (VLM)\n"); + pc87360_write_value(data, LD_IN, NO_BANK, + PC87365_REG_IN_CONFIG, + reg & 0xFE); + } + } + + if (data->tempnr) { + reg = pc87360_read_value(data, LD_TEMP, NO_BANK, + PC87365_REG_TEMP_CONFIG); + if (reg & 0x01) { + dev_dbg(&client->dev, "Forcibly enabling " + "monitoring (TMS)\n"); + pc87360_write_value(data, LD_TEMP, NO_BANK, + PC87365_REG_TEMP_CONFIG, + reg & 0xFE); + } + + if (init >= 2) { + /* Chip config as documented by National Semi. */ + pc87360_write_value(data, LD_TEMP, 0xF, 0xA, 0x08); + /* We voluntarily omit the bank here, in case the + sequence itself matters. It shouldn't be a problem, + since nobody else is supposed to access the + device at that point. */ + pc87360_write_value(data, LD_TEMP, NO_BANK, 0xB, 0x04); + pc87360_write_value(data, LD_TEMP, NO_BANK, 0xC, 0x35); + pc87360_write_value(data, LD_TEMP, NO_BANK, 0xD, 0x05); + pc87360_write_value(data, LD_TEMP, NO_BANK, 0xE, 0x05); + } + } +} + +static void pc87360_autodiv(struct i2c_client *client, int nr) +{ + struct pc87360_data *data = i2c_get_clientdata(client); + u8 old_min = data->fan_min[nr]; + + /* Increase clock divider if needed and possible */ + if ((data->fan_status[nr] & 0x04) /* overflow flag */ + || (data->fan[nr] >= 224)) { /* next to overflow */ + if ((data->fan_status[nr] & 0x60) != 0x60) { + data->fan_status[nr] += 0x20; + data->fan_min[nr] >>= 1; + data->fan[nr] >>= 1; + dev_dbg(&client->dev, "Increasing " + "clock divider to %d for fan %d\n", + FAN_DIV_FROM_REG(data->fan_status[nr]), nr+1); + } + } else { + /* Decrease clock divider if possible */ + while (!(data->fan_min[nr] & 0x80) /* min "nails" divider */ + && data->fan[nr] < 85 /* bad accuracy */ + && (data->fan_status[nr] & 0x60) != 0x00) { + data->fan_status[nr] -= 0x20; + data->fan_min[nr] <<= 1; + data->fan[nr] <<= 1; + dev_dbg(&client->dev, "Decreasing " + "clock divider to %d for fan %d\n", + FAN_DIV_FROM_REG(data->fan_status[nr]), + nr+1); + } + } + + /* Write new fan min if it changed */ + if (old_min != data->fan_min[nr]) { + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_MIN(nr), + data->fan_min[nr]); + } +} + +static struct pc87360_data *pc87360_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct pc87360_data *data = i2c_get_clientdata(client); + u8 i; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { + dev_dbg(&client->dev, "Data update\n"); + + /* Fans */ + for (i = 0; i < data->fannr; i++) { + if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { + data->fan_status[i] = + pc87360_read_value(data, LD_FAN, + NO_BANK, PC87360_REG_FAN_STATUS(i)); + data->fan[i] = pc87360_read_value(data, LD_FAN, + NO_BANK, PC87360_REG_FAN(i)); + data->fan_min[i] = pc87360_read_value(data, + LD_FAN, NO_BANK, + PC87360_REG_FAN_MIN(i)); + /* Change clock divider if needed */ + pc87360_autodiv(client, i); + /* Clear bits and write new divider */ + pc87360_write_value(data, LD_FAN, NO_BANK, + PC87360_REG_FAN_STATUS(i), + data->fan_status[i]); + } + if (FAN_CONFIG_CONTROL(data->fan_conf, i)) + data->pwm[i] = pc87360_read_value(data, LD_FAN, + NO_BANK, PC87360_REG_PWM(i)); + } + + /* Voltages */ + for (i = 0; i < data->innr; i++) { + data->in_status[i] = pc87360_read_value(data, LD_IN, i, + PC87365_REG_IN_STATUS); + /* Clear bits */ + pc87360_write_value(data, LD_IN, i, + PC87365_REG_IN_STATUS, + data->in_status[i]); + if ((data->in_status[i] & 0x81) == 0x81) { + data->in[i] = pc87360_read_value(data, LD_IN, + i, PC87365_REG_IN); + } + if (data->in_status[i] & 0x01) { + data->in_min[i] = pc87360_read_value(data, + LD_IN, i, + PC87365_REG_IN_MIN); + data->in_max[i] = pc87360_read_value(data, + LD_IN, i, + PC87365_REG_IN_MAX); + if (i >= 11) + data->in_crit[i-11] = + pc87360_read_value(data, LD_IN, + i, PC87365_REG_TEMP_CRIT); + } + } + if (data->innr) { + data->in_alarms = pc87360_read_value(data, LD_IN, + NO_BANK, PC87365_REG_IN_ALARMS1) + | ((pc87360_read_value(data, LD_IN, + NO_BANK, PC87365_REG_IN_ALARMS2) + & 0x07) << 8); + data->vid = (data->vid_conf & 0xE0) ? + pc87360_read_value(data, LD_IN, + NO_BANK, PC87365_REG_VID) : 0x1F; + } + + /* Temperatures */ + for (i = 0; i < data->tempnr; i++) { + data->temp_status[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_STATUS); + /* Clear bits */ + pc87360_write_value(data, LD_TEMP, i, + PC87365_REG_TEMP_STATUS, + data->temp_status[i]); + if ((data->temp_status[i] & 0x81) == 0x81) { + data->temp[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP); + } + if (data->temp_status[i] & 0x01) { + data->temp_min[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_MIN); + data->temp_max[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_MAX); + data->temp_crit[i] = pc87360_read_value(data, + LD_TEMP, i, + PC87365_REG_TEMP_CRIT); + } + } + if (data->tempnr) { + data->temp_alarms = pc87360_read_value(data, LD_TEMP, + NO_BANK, PC87365_REG_TEMP_ALARMS) + & 0x3F; + } + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init pc87360_init(void) +{ + int i; + + if (pc87360_find(0x2e, &devid, extra_isa) + && pc87360_find(0x4e, &devid, extra_isa)) { + printk(KERN_WARNING "pc87360: PC8736x not detected, " + "module not inserted.\n"); + return -ENODEV; + } + + /* Arbitrarily pick one of the addresses */ + for (i = 0; i < 3; i++) { + if (extra_isa[i] != 0x0000) { + normal_isa[0] = extra_isa[i]; + break; + } + } + + if (normal_isa[0] == 0x0000) { + printk(KERN_WARNING "pc87360: No active logical device, " + "module not inserted.\n"); + return -ENODEV; + } + + return i2c_add_driver(&pc87360_driver); +} + +static void __exit pc87360_exit(void) +{ + i2c_del_driver(&pc87360_driver); +} + + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("PC8736x hardware monitor"); +MODULE_LICENSE("GPL"); + +module_init(pc87360_init); +module_exit(pc87360_exit); diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c new file mode 100644 index 000000000000..6bbfc8fb4f13 --- /dev/null +++ b/drivers/hwmon/sis5595.c @@ -0,0 +1,817 @@ +/* + sis5595.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + Copyright (C) 1998 - 2001 Frodo Looijaard , + Kyösti Mälkki , and + Mark D. Studebaker + Ported to Linux 2.6 by Aurelien Jarno with + the help of Jean Delvare + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + SiS southbridge has a LM78-like chip integrated on the same IC. + This driver is a customized copy of lm78.c + + Supports following revisions: + Version PCI ID PCI Revision + 1 1039/0008 AF or less + 2 1039/0008 B0 or greater + + Note: these chips contain a 0008 device which is incompatible with the + 5595. We recognize these by the presence of the listed + "blacklist" PCI ID and refuse to load. + + NOT SUPPORTED PCI ID BLACKLIST PCI ID + 540 0008 0540 + 550 0008 0550 + 5513 0008 5511 + 5581 0008 5597 + 5582 0008 5597 + 5597 0008 5597 + 5598 0008 5597/5598 + 630 0008 0630 + 645 0008 0645 + 730 0008 0730 + 735 0008 0735 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static u16 force_addr; +module_param(force_addr, ushort, 0); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the sensors"); + +/* Addresses to scan. + Note that we can't determine the ISA address until we have initialized + our module */ +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(sis5595); + +/* Many SIS5595 constants specified below */ + +/* Length of ISA address segment */ +#define SIS5595_EXTENT 8 +/* PCI Config Registers */ +#define SIS5595_REVISION_REG 0x08 +#define SIS5595_BASE_REG 0x68 +#define SIS5595_PIN_REG 0x7A +#define SIS5595_ENABLE_REG 0x7B + +/* Where are the ISA address/data registers relative to the base address */ +#define SIS5595_ADDR_REG_OFFSET 5 +#define SIS5595_DATA_REG_OFFSET 6 + +/* The SIS5595 registers */ +#define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define SIS5595_REG_IN(nr) (0x20 + (nr)) + +#define SIS5595_REG_FAN_MIN(nr) (0x3b + (nr)) +#define SIS5595_REG_FAN(nr) (0x28 + (nr)) + +/* On the first version of the chip, the temp registers are separate. + On the second version, + TEMP pin is shared with IN4, configured in PCI register 0x7A. + The registers are the same as well. + OVER and HYST are really MAX and MIN. */ + +#define REV2MIN 0xb0 +#define SIS5595_REG_TEMP (( data->revision) >= REV2MIN) ? \ + SIS5595_REG_IN(4) : 0x27 +#define SIS5595_REG_TEMP_OVER (( data->revision) >= REV2MIN) ? \ + SIS5595_REG_IN_MAX(4) : 0x39 +#define SIS5595_REG_TEMP_HYST (( data->revision) >= REV2MIN) ? \ + SIS5595_REG_IN_MIN(4) : 0x3a + +#define SIS5595_REG_CONFIG 0x40 +#define SIS5595_REG_ALARM1 0x41 +#define SIS5595_REG_ALARM2 0x42 +#define SIS5595_REG_FANDIV 0x47 + +/* Conversions. Limit checking is only done on the TO_REG + variants. */ + +/* IN: mV, (0V to 4.08V) + REG: 16mV/bit */ +static inline u8 IN_TO_REG(unsigned long val) +{ + unsigned long nval = SENSORS_LIMIT(val, 0, 4080); + return (nval + 8) / 16; +} +#define IN_FROM_REG(val) ((val) * 16) + +static inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm <= 0) + return 255; + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); +} + +static inline int FAN_FROM_REG(u8 val, int div) +{ + return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div); +} + +/* TEMP: mC (-54.12C to +157.53C) + REG: 0.83C/bit + 52.12, two's complement */ +static inline int TEMP_FROM_REG(s8 val) +{ + return val * 830 + 52120; +} +static inline s8 TEMP_TO_REG(int val) +{ + int nval = SENSORS_LIMIT(val, -54120, 157530) ; + return nval<0 ? (nval-5212-415)/830 : (nval-5212+415)/830; +} + +/* FAN DIV: 1, 2, 4, or 8 (defaults to 2) + REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */ +static inline u8 DIV_TO_REG(int val) +{ + return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1; +} +#define DIV_FROM_REG(val) (1 << (val)) + +/* For the SIS5595, we need to keep some data in memory. That + data is pointed to by sis5595_list[NR]->data. The structure itself is + dynamically allocated, at the time when the new sis5595 client is + allocated. */ +struct sis5595_data { + struct i2c_client client; + struct semaphore lock; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + char maxins; /* == 3 if temp enabled, otherwise == 4 */ + u8 revision; /* Reg. value */ + + u8 in[5]; /* Register value */ + u8 in_max[5]; /* Register value */ + u8 in_min[5]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + s8 temp; /* Register value */ + s8 temp_over; /* Register value */ + s8 temp_hyst; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u16 alarms; /* Register encoding, combined */ +}; + +static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */ + +static int sis5595_attach_adapter(struct i2c_adapter *adapter); +static int sis5595_detect(struct i2c_adapter *adapter, int address, int kind); +static int sis5595_detach_client(struct i2c_client *client); + +static int sis5595_read_value(struct i2c_client *client, u8 register); +static int sis5595_write_value(struct i2c_client *client, u8 register, u8 value); +static struct sis5595_data *sis5595_update_device(struct device *dev); +static void sis5595_init_client(struct i2c_client *client); + +static struct i2c_driver sis5595_driver = { + .owner = THIS_MODULE, + .name = "sis5595", + .id = I2C_DRIVERID_SIS5595, + .flags = I2C_DF_NOTIFY, + .attach_adapter = sis5595_attach_adapter, + .detach_client = sis5595_detach_client, +}; + +/* 4 Voltages */ +static ssize_t show_in(struct device *dev, char *buf, int nr) +{ + struct sis5595_data *data = sis5595_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); +} + +static ssize_t show_in_min(struct device *dev, char *buf, int nr) +{ + struct sis5595_data *data = sis5595_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); +} + +static ssize_t show_in_max(struct device *dev, char *buf, int nr) +{ + struct sis5595_data *data = sis5595_update_device(dev); + return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); +} + +static ssize_t set_in_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sis5595_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->in_min[nr] = IN_TO_REG(val); + sis5595_write_value(client, SIS5595_REG_IN_MIN(nr), data->in_min[nr]); + up(&data->update_lock); + return count; +} + +static ssize_t set_in_max(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sis5595_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->in_max[nr] = IN_TO_REG(val); + sis5595_write_value(client, SIS5595_REG_IN_MAX(nr), data->in_max[nr]); + up(&data->update_lock); + return count; +} + +#define show_in_offset(offset) \ +static ssize_t \ + show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in(dev, buf, offset); \ +} \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ + show_in##offset, NULL); \ +static ssize_t \ + show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in_min(dev, buf, offset); \ +} \ +static ssize_t \ + show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in_max(dev, buf, offset); \ +} \ +static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_in_min(dev, buf, count, offset); \ +} \ +static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_in_max(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in##offset##_min, set_in##offset##_min); \ +static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in##offset##_max, set_in##offset##_max); + +show_in_offset(0); +show_in_offset(1); +show_in_offset(2); +show_in_offset(3); +show_in_offset(4); + +/* Temperature */ +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sis5595_data *data = sis5595_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp)); +} + +static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sis5595_data *data = sis5595_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over)); +} + +static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sis5595_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_over = TEMP_TO_REG(val); + sis5595_write_value(client, SIS5595_REG_TEMP_OVER, data->temp_over); + up(&data->update_lock); + return count; +} + +static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sis5595_data *data = sis5595_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst)); +} + +static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sis5595_data *data = i2c_get_clientdata(client); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_hyst = TEMP_TO_REG(val); + sis5595_write_value(client, SIS5595_REG_TEMP_HYST, data->temp_hyst); + up(&data->update_lock); + return count; +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); +static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, + show_temp_over, set_temp_over); +static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, + show_temp_hyst, set_temp_hyst); + +/* 2 Fans */ +static ssize_t show_fan(struct device *dev, char *buf, int nr) +{ + struct sis5595_data *data = sis5595_update_device(dev); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr])) ); +} + +static ssize_t show_fan_min(struct device *dev, char *buf, int nr) +{ + struct sis5595_data *data = sis5595_update_device(dev); + return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])) ); +} + +static ssize_t set_fan_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sis5595_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); + sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]); + up(&data->update_lock); + return count; +} + +static ssize_t show_fan_div(struct device *dev, char *buf, int nr) +{ + struct sis5595_data *data = sis5595_update_device(dev); + return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) ); +} + +/* Note: we save and restore the fan minimum here, because its value is + determined in part by the fan divisor. This follows the principle of + least suprise; the user doesn't expect the fan minimum to change just + because the divisor changed. */ +static ssize_t set_fan_div(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sis5595_data *data = i2c_get_clientdata(client); + unsigned long min; + unsigned long val = simple_strtoul(buf, NULL, 10); + int reg; + + down(&data->update_lock); + min = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + reg = sis5595_read_value(client, SIS5595_REG_FANDIV); + + switch (val) { + case 1: data->fan_div[nr] = 0; break; + case 2: data->fan_div[nr] = 1; break; + case 4: data->fan_div[nr] = 2; break; + case 8: data->fan_div[nr] = 3; break; + default: + dev_err(&client->dev, "fan_div value %ld not " + "supported. Choose one of 1, 2, 4 or 8!\n", val); + up(&data->update_lock); + return -EINVAL; + } + + switch (nr) { + case 0: + reg = (reg & 0xcf) | (data->fan_div[nr] << 4); + break; + case 1: + reg = (reg & 0x3f) | (data->fan_div[nr] << 6); + break; + } + sis5595_write_value(client, SIS5595_REG_FANDIV, reg); + data->fan_min[nr] = + FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); + sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]); + up(&data->update_lock); + return count; +} + +#define show_fan_offset(offset) \ +static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_min(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_div(dev, buf, offset - 1); \ +} \ +static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_fan_min(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan_##offset##_min, set_fan_##offset##_min); + +show_fan_offset(1); +show_fan_offset(2); + +static ssize_t set_fan_1_div(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + return set_fan_div(dev, buf, count, 0) ; +} + +static ssize_t set_fan_2_div(struct device *dev, struct device_attribute *attr, const char *buf, + size_t count) +{ + return set_fan_div(dev, buf, count, 1) ; +} +static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, + show_fan_1_div, set_fan_1_div); +static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, + show_fan_2_div, set_fan_2_div); + +/* Alarms */ +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct sis5595_data *data = sis5595_update_device(dev); + return sprintf(buf, "%d\n", data->alarms); +} +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +/* This is called when the module is loaded */ +static int sis5595_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, sis5595_detect); +} + +int sis5595_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int err = 0; + int i; + struct i2c_client *new_client; + struct sis5595_data *data; + char val; + u16 a; + + /* Make sure we are probing the ISA bus!! */ + if (!i2c_is_isa_adapter(adapter)) + goto exit; + + if (force_addr) + address = force_addr & ~(SIS5595_EXTENT - 1); + /* Reserve the ISA region */ + if (!request_region(address, SIS5595_EXTENT, sis5595_driver.name)) { + err = -EBUSY; + goto exit; + } + if (force_addr) { + dev_warn(&adapter->dev, "forcing ISA address 0x%04X\n", address); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(s_bridge, SIS5595_BASE_REG, address)) + goto exit_release; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a)) + goto exit_release; + if ((a & ~(SIS5595_EXTENT - 1)) != address) + /* doesn't work for some chips? */ + goto exit_release; + } + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) { + goto exit_release; + } + if ((val & 0x80) == 0) { + if (PCIBIOS_SUCCESSFUL != + pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG, + val | 0x80)) + goto exit_release; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) + goto exit_release; + if ((val & 0x80) == 0) + /* doesn't work for some chips! */ + goto exit_release; + } + + if (!(data = kmalloc(sizeof(struct sis5595_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit_release; + } + memset(data, 0, sizeof(struct sis5595_data)); + + new_client = &data->client; + new_client->addr = address; + init_MUTEX(&data->lock); + i2c_set_clientdata(new_client, data); + new_client->adapter = adapter; + new_client->driver = &sis5595_driver; + new_client->flags = 0; + + /* Check revision and pin registers to determine whether 4 or 5 voltages */ + pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision)); + /* 4 voltages, 1 temp */ + data->maxins = 3; + if (data->revision >= REV2MIN) { + pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val); + if (!(val & 0x80)) + /* 5 voltages, no temps */ + data->maxins = 4; + } + + /* Fill in the remaining client fields and put it into the global list */ + strlcpy(new_client->name, "sis5595", I2C_NAME_SIZE); + + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the SIS5595 chip */ + sis5595_init_client(new_client); + + /* A few vars need to be filled upon startup */ + for (i = 0; i < 2; i++) { + data->fan_min[i] = sis5595_read_value(new_client, + SIS5595_REG_FAN_MIN(i)); + } + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_in0_input); + device_create_file(&new_client->dev, &dev_attr_in0_min); + device_create_file(&new_client->dev, &dev_attr_in0_max); + device_create_file(&new_client->dev, &dev_attr_in1_input); + device_create_file(&new_client->dev, &dev_attr_in1_min); + device_create_file(&new_client->dev, &dev_attr_in1_max); + device_create_file(&new_client->dev, &dev_attr_in2_input); + device_create_file(&new_client->dev, &dev_attr_in2_min); + device_create_file(&new_client->dev, &dev_attr_in2_max); + device_create_file(&new_client->dev, &dev_attr_in3_input); + device_create_file(&new_client->dev, &dev_attr_in3_min); + device_create_file(&new_client->dev, &dev_attr_in3_max); + if (data->maxins == 4) { + device_create_file(&new_client->dev, &dev_attr_in4_input); + device_create_file(&new_client->dev, &dev_attr_in4_min); + device_create_file(&new_client->dev, &dev_attr_in4_max); + } + device_create_file(&new_client->dev, &dev_attr_fan1_input); + device_create_file(&new_client->dev, &dev_attr_fan1_min); + device_create_file(&new_client->dev, &dev_attr_fan1_div); + device_create_file(&new_client->dev, &dev_attr_fan2_input); + device_create_file(&new_client->dev, &dev_attr_fan2_min); + device_create_file(&new_client->dev, &dev_attr_fan2_div); + device_create_file(&new_client->dev, &dev_attr_alarms); + if (data->maxins == 3) { + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); + } + return 0; + +exit_free: + kfree(data); +exit_release: + release_region(address, SIS5595_EXTENT); +exit: + return err; +} + +static int sis5595_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, + "Client deregistration failed, client not detached.\n"); + return err; + } + + if (i2c_is_isa_client(client)) + release_region(client->addr, SIS5595_EXTENT); + + kfree(i2c_get_clientdata(client)); + + return 0; +} + + +/* ISA access must be locked explicitly. */ +static int sis5595_read_value(struct i2c_client *client, u8 reg) +{ + int res; + + struct sis5595_data *data = i2c_get_clientdata(client); + down(&data->lock); + outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET); + res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET); + up(&data->lock); + return res; +} + +static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + struct sis5595_data *data = i2c_get_clientdata(client); + down(&data->lock); + outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET); + outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET); + up(&data->lock); + return 0; +} + +/* Called when we have found a new SIS5595. */ +static void sis5595_init_client(struct i2c_client *client) +{ + u8 config = sis5595_read_value(client, SIS5595_REG_CONFIG); + if (!(config & 0x01)) + sis5595_write_value(client, SIS5595_REG_CONFIG, + (config & 0xf7) | 0x01); +} + +static struct sis5595_data *sis5595_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct sis5595_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + + for (i = 0; i <= data->maxins; i++) { + data->in[i] = + sis5595_read_value(client, SIS5595_REG_IN(i)); + data->in_min[i] = + sis5595_read_value(client, + SIS5595_REG_IN_MIN(i)); + data->in_max[i] = + sis5595_read_value(client, + SIS5595_REG_IN_MAX(i)); + } + for (i = 0; i < 2; i++) { + data->fan[i] = + sis5595_read_value(client, SIS5595_REG_FAN(i)); + data->fan_min[i] = + sis5595_read_value(client, + SIS5595_REG_FAN_MIN(i)); + } + if (data->maxins == 3) { + data->temp = + sis5595_read_value(client, SIS5595_REG_TEMP); + data->temp_over = + sis5595_read_value(client, SIS5595_REG_TEMP_OVER); + data->temp_hyst = + sis5595_read_value(client, SIS5595_REG_TEMP_HYST); + } + i = sis5595_read_value(client, SIS5595_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = + sis5595_read_value(client, SIS5595_REG_ALARM1) | + (sis5595_read_value(client, SIS5595_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static struct pci_device_id sis5595_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, sis5595_pci_ids); + +static int blacklist[] __devinitdata = { + PCI_DEVICE_ID_SI_540, + PCI_DEVICE_ID_SI_550, + PCI_DEVICE_ID_SI_630, + PCI_DEVICE_ID_SI_645, + PCI_DEVICE_ID_SI_730, + PCI_DEVICE_ID_SI_735, + PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but + that ID shows up in other chips so we + use the 5511 ID for recognition */ + PCI_DEVICE_ID_SI_5597, + PCI_DEVICE_ID_SI_5598, + 0 }; + +static int __devinit sis5595_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + u16 val; + int *i; + int addr = 0; + + for (i = blacklist; *i != 0; i++) { + struct pci_dev *dev; + dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL); + if (dev) { + dev_err(&dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i); + pci_dev_put(dev); + return -ENODEV; + } + } + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(dev, SIS5595_BASE_REG, &val)) + return -ENODEV; + + addr = val & ~(SIS5595_EXTENT - 1); + if (addr == 0 && force_addr == 0) { + dev_err(&dev->dev, "Base address not set - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + if (force_addr) + addr = force_addr; /* so detect will get called */ + + if (!addr) { + dev_err(&dev->dev,"No SiS 5595 sensors found.\n"); + return -ENODEV; + } + normal_isa[0] = addr; + + s_bridge = pci_dev_get(dev); + if (i2c_add_driver(&sis5595_driver)) { + pci_dev_put(s_bridge); + s_bridge = NULL; + } + + /* Always return failure here. This is to allow other drivers to bind + * to this pci device. We don't really want to have control over the + * pci device, we only wanted to read as few register values from it. + */ + return -ENODEV; +} + +static struct pci_driver sis5595_pci_driver = { + .name = "sis5595", + .id_table = sis5595_pci_ids, + .probe = sis5595_pci_probe, +}; + +static int __init sm_sis5595_init(void) +{ + return pci_register_driver(&sis5595_pci_driver); +} + +static void __exit sm_sis5595_exit(void) +{ + pci_unregister_driver(&sis5595_pci_driver); + if (s_bridge != NULL) { + i2c_del_driver(&sis5595_driver); + pci_dev_put(s_bridge); + s_bridge = NULL; + } +} + +MODULE_AUTHOR("Aurelien Jarno "); +MODULE_DESCRIPTION("SiS 5595 Sensor device"); +MODULE_LICENSE("GPL"); + +module_init(sm_sis5595_init); +module_exit(sm_sis5595_exit); diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c new file mode 100644 index 000000000000..251ac2659554 --- /dev/null +++ b/drivers/hwmon/smsc47b397.c @@ -0,0 +1,352 @@ +/* + smsc47b397.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + Supports the SMSC LPC47B397-NC Super-I/O chip. + + Author/Maintainer: Mark M. Hoffman + Copyright (C) 2004 Utilitek Systems, Inc. + + derived in part from smsc47m1.c: + Copyright (C) 2002 Mark D. Studebaker + Copyright (C) 2004 Jean Delvare + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +/* Address is autodetected, there is no default value */ +static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; +static struct i2c_force_data forces[] = {{NULL}}; + +enum chips { any_chip, smsc47b397 }; +static struct i2c_address_data addr_data = { + .normal_i2c = normal_i2c, + .normal_isa = normal_isa, + .probe = normal_i2c, /* cheat */ + .ignore = normal_i2c, /* cheat */ + .forces = forces, +}; + +/* Super-I/0 registers and commands */ + +#define REG 0x2e /* The register to read/write */ +#define VAL 0x2f /* The value to read/write */ + +static inline void superio_outb(int reg, int val) +{ + outb(reg, REG); + outb(val, VAL); +} + +static inline int superio_inb(int reg) +{ + outb(reg, REG); + return inb(VAL); +} + +/* select superio logical device */ +static inline void superio_select(int ld) +{ + superio_outb(0x07, ld); +} + +static inline void superio_enter(void) +{ + outb(0x55, REG); +} + +static inline void superio_exit(void) +{ + outb(0xAA, REG); +} + +#define SUPERIO_REG_DEVID 0x20 +#define SUPERIO_REG_DEVREV 0x21 +#define SUPERIO_REG_BASE_MSB 0x60 +#define SUPERIO_REG_BASE_LSB 0x61 +#define SUPERIO_REG_LD8 0x08 + +#define SMSC_EXTENT 0x02 + +/* 0 <= nr <= 3 */ +static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80}; +#define SMSC47B397_REG_TEMP(nr) (smsc47b397_reg_temp[(nr)]) + +/* 0 <= nr <= 3 */ +#define SMSC47B397_REG_FAN_LSB(nr) (0x28 + 2 * (nr)) +#define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr)) + +struct smsc47b397_data { + struct i2c_client client; + struct semaphore lock; + + struct semaphore update_lock; + unsigned long last_updated; /* in jiffies */ + int valid; + + /* register values */ + u16 fan[4]; + u8 temp[4]; +}; + +static int smsc47b397_read_value(struct i2c_client *client, u8 reg) +{ + struct smsc47b397_data *data = i2c_get_clientdata(client); + int res; + + down(&data->lock); + outb(reg, client->addr); + res = inb_p(client->addr + 1); + up(&data->lock); + return res; +} + +static struct smsc47b397_data *smsc47b397_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47b397_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { + dev_dbg(&client->dev, "starting device update...\n"); + + /* 4 temperature inputs, 4 fan inputs */ + for (i = 0; i < 4; i++) { + data->temp[i] = smsc47b397_read_value(client, + SMSC47B397_REG_TEMP(i)); + + /* must read LSB first */ + data->fan[i] = smsc47b397_read_value(client, + SMSC47B397_REG_FAN_LSB(i)); + data->fan[i] |= smsc47b397_read_value(client, + SMSC47B397_REG_FAN_MSB(i)) << 8; + } + + data->last_updated = jiffies; + data->valid = 1; + + dev_dbg(&client->dev, "... device update complete\n"); + } + + up(&data->update_lock); + + return data; +} + +/* TEMP: 0.001C/bit (-128C to +127C) + REG: 1C/bit, two's complement */ +static int temp_from_reg(u8 reg) +{ + return (s8)reg * 1000; +} + +/* 0 <= nr <= 3 */ +static ssize_t show_temp(struct device *dev, char *buf, int nr) +{ + struct smsc47b397_data *data = smsc47b397_update_device(dev); + return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr])); +} + +#define sysfs_temp(num) \ +static ssize_t show_temp##num(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp(dev, buf, num-1); \ +} \ +static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL) + +sysfs_temp(1); +sysfs_temp(2); +sysfs_temp(3); +sysfs_temp(4); + +#define device_create_file_temp(client, num) \ + device_create_file(&client->dev, &dev_attr_temp##num##_input) + +/* FAN: 1 RPM/bit + REG: count of 90kHz pulses / revolution */ +static int fan_from_reg(u16 reg) +{ + return 90000 * 60 / reg; +} + +/* 0 <= nr <= 3 */ +static ssize_t show_fan(struct device *dev, char *buf, int nr) +{ + struct smsc47b397_data *data = smsc47b397_update_device(dev); + return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr])); +} + +#define sysfs_fan(num) \ +static ssize_t show_fan##num(struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan(dev, buf, num-1); \ +} \ +static DEVICE_ATTR(fan##num##_input, S_IRUGO, show_fan##num, NULL) + +sysfs_fan(1); +sysfs_fan(2); +sysfs_fan(3); +sysfs_fan(4); + +#define device_create_file_fan(client, num) \ + device_create_file(&client->dev, &dev_attr_fan##num##_input) + +static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind); + +static int smsc47b397_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, smsc47b397_detect); +} + +static int smsc47b397_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + release_region(client->addr, SMSC_EXTENT); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static struct i2c_driver smsc47b397_driver = { + .owner = THIS_MODULE, + .name = "smsc47b397", + .id = I2C_DRIVERID_SMSC47B397, + .flags = I2C_DF_NOTIFY, + .attach_adapter = smsc47b397_attach_adapter, + .detach_client = smsc47b397_detach_client, +}; + +static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind) +{ + struct i2c_client *new_client; + struct smsc47b397_data *data; + int err = 0; + + if (!i2c_is_isa_adapter(adapter)) { + return 0; + } + + if (!request_region(addr, SMSC_EXTENT, smsc47b397_driver.name)) { + dev_err(&adapter->dev, "Region 0x%x already in use!\n", addr); + return -EBUSY; + } + + if (!(data = kmalloc(sizeof(struct smsc47b397_data), GFP_KERNEL))) { + err = -ENOMEM; + goto error_release; + } + memset(data, 0x00, sizeof(struct smsc47b397_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = addr; + init_MUTEX(&data->lock); + new_client->adapter = adapter; + new_client->driver = &smsc47b397_driver; + new_client->flags = 0; + + strlcpy(new_client->name, "smsc47b397", I2C_NAME_SIZE); + + init_MUTEX(&data->update_lock); + + if ((err = i2c_attach_client(new_client))) + goto error_free; + + device_create_file_temp(new_client, 1); + device_create_file_temp(new_client, 2); + device_create_file_temp(new_client, 3); + device_create_file_temp(new_client, 4); + + device_create_file_fan(new_client, 1); + device_create_file_fan(new_client, 2); + device_create_file_fan(new_client, 3); + device_create_file_fan(new_client, 4); + + return 0; + +error_free: + kfree(new_client); +error_release: + release_region(addr, SMSC_EXTENT); + return err; +} + +static int __init smsc47b397_find(unsigned int *addr) +{ + u8 id, rev; + + superio_enter(); + id = superio_inb(SUPERIO_REG_DEVID); + + if (id != 0x6f) { + superio_exit(); + return -ENODEV; + } + + rev = superio_inb(SUPERIO_REG_DEVREV); + + superio_select(SUPERIO_REG_LD8); + *addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8) + | superio_inb(SUPERIO_REG_BASE_LSB); + + printk(KERN_INFO "smsc47b397: found SMSC LPC47B397-NC " + "(base address 0x%04x, revision %u)\n", *addr, rev); + + superio_exit(); + return 0; +} + +static int __init smsc47b397_init(void) +{ + int ret; + + if ((ret = smsc47b397_find(normal_isa))) + return ret; + + return i2c_add_driver(&smsc47b397_driver); +} + +static void __exit smsc47b397_exit(void) +{ + i2c_del_driver(&smsc47b397_driver); +} + +MODULE_AUTHOR("Mark M. Hoffman "); +MODULE_DESCRIPTION("SMSC LPC47B397 driver"); +MODULE_LICENSE("GPL"); + +module_init(smsc47b397_init); +module_exit(smsc47b397_exit); diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c new file mode 100644 index 000000000000..897117a7213f --- /dev/null +++ b/drivers/hwmon/smsc47m1.c @@ -0,0 +1,593 @@ +/* + smsc47m1.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + Supports the SMSC LPC47B27x, LPC47M10x, LPC47M13x and LPC47M14x + Super-I/O chips. + + Copyright (C) 2002 Mark D. Studebaker + Copyright (C) 2004 Jean Delvare + Ported to Linux 2.6 by Gabriele Gorla + and Jean Delvare + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +/* Address is autodetected, there is no default value */ +static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; +static struct i2c_force_data forces[] = {{NULL}}; + +enum chips { any_chip, smsc47m1 }; +static struct i2c_address_data addr_data = { + .normal_i2c = normal_i2c, + .normal_isa = normal_isa, + .forces = forces, +}; + +/* Super-I/0 registers and commands */ + +#define REG 0x2e /* The register to read/write */ +#define VAL 0x2f /* The value to read/write */ + +static inline void +superio_outb(int reg, int val) +{ + outb(reg, REG); + outb(val, VAL); +} + +static inline int +superio_inb(int reg) +{ + outb(reg, REG); + return inb(VAL); +} + +/* logical device for fans is 0x0A */ +#define superio_select() superio_outb(0x07, 0x0A) + +static inline void +superio_enter(void) +{ + outb(0x55, REG); +} + +static inline void +superio_exit(void) +{ + outb(0xAA, REG); +} + +#define SUPERIO_REG_ACT 0x30 +#define SUPERIO_REG_BASE 0x60 +#define SUPERIO_REG_DEVID 0x20 + +/* Logical device registers */ + +#define SMSC_EXTENT 0x80 + +/* nr is 0 or 1 in the macros below */ +#define SMSC47M1_REG_ALARM 0x04 +#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr)) +#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr)) +#define SMSC47M1_REG_PWM(nr) (0x56 + (nr)) +#define SMSC47M1_REG_FANDIV 0x58 +#define SMSC47M1_REG_FAN(nr) (0x59 + (nr)) +#define SMSC47M1_REG_FAN_PRELOAD(nr) (0x5B + (nr)) + +#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \ + 983040/((192-(reg))*(div))) +#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \ + 983040/(((reg)-(preload))*(div))) +#define DIV_FROM_REG(reg) (1 << (reg)) +#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1) +#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01) +#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E) + +struct smsc47m1_data { + struct i2c_client client; + struct semaphore lock; + + struct semaphore update_lock; + unsigned long last_updated; /* In jiffies */ + + u8 fan[2]; /* Register value */ + u8 fan_preload[2]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u8 alarms; /* Register encoding */ + u8 pwm[2]; /* Register value (bit 7 is enable) */ +}; + + +static int smsc47m1_attach_adapter(struct i2c_adapter *adapter); +static int smsc47m1_find(int *address); +static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind); +static int smsc47m1_detach_client(struct i2c_client *client); + +static int smsc47m1_read_value(struct i2c_client *client, u8 reg); +static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value); + +static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, + int init); + + +static struct i2c_driver smsc47m1_driver = { + .owner = THIS_MODULE, + .name = "smsc47m1", + .id = I2C_DRIVERID_SMSC47M1, + .flags = I2C_DF_NOTIFY, + .attach_adapter = smsc47m1_attach_adapter, + .detach_client = smsc47m1_detach_client, +}; + +/* nr is 0 or 1 in the callback functions below */ + +static ssize_t get_fan(struct device *dev, char *buf, int nr) +{ + struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); + /* This chip (stupidly) stops monitoring fan speed if PWM is + enabled and duty cycle is 0%. This is fine if the monitoring + and control concern the same fan, but troublesome if they are + not (which could as well happen). */ + int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 : + FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr]), + data->fan_preload[nr]); + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t get_fan_min(struct device *dev, char *buf, int nr) +{ + struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); + int rpm = MIN_FROM_REG(data->fan_preload[nr], + DIV_FROM_REG(data->fan_div[nr])); + return sprintf(buf, "%d\n", rpm); +} + +static ssize_t get_fan_div(struct device *dev, char *buf, int nr) +{ + struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); + return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); +} + +static ssize_t get_pwm(struct device *dev, char *buf, int nr) +{ + struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); + return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr])); +} + +static ssize_t get_pwm_en(struct device *dev, char *buf, int nr) +{ + struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); + return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[nr])); +} + +static ssize_t get_alarms(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); + return sprintf(buf, "%d\n", data->alarms); +} + +static ssize_t set_fan_min(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m1_data *data = i2c_get_clientdata(client); + long rpmdiv, val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]); + + if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) { + up(&data->update_lock); + return -EINVAL; + } + + data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv); + smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr), + data->fan_preload[nr]); + up(&data->update_lock); + + return count; +} + +/* Note: we save and restore the fan minimum here, because its value is + determined in part by the fan clock divider. This follows the principle + of least suprise; the user doesn't expect the fan minimum to change just + because the divider changed. */ +static ssize_t set_fan_div(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m1_data *data = i2c_get_clientdata(client); + + long new_div = simple_strtol(buf, NULL, 10), tmp; + u8 old_div = DIV_FROM_REG(data->fan_div[nr]); + + if (new_div == old_div) /* No change */ + return count; + + down(&data->update_lock); + switch (new_div) { + case 1: data->fan_div[nr] = 0; break; + case 2: data->fan_div[nr] = 1; break; + case 4: data->fan_div[nr] = 2; break; + case 8: data->fan_div[nr] = 3; break; + default: + up(&data->update_lock); + return -EINVAL; + } + + tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV) & 0x0F; + tmp |= (data->fan_div[0] << 4) | (data->fan_div[1] << 6); + smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp); + + /* Preserve fan min */ + tmp = 192 - (old_div * (192 - data->fan_preload[nr]) + + new_div / 2) / new_div; + data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191); + smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr), + data->fan_preload[nr]); + up(&data->update_lock); + + return count; +} + +static ssize_t set_pwm(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m1_data *data = i2c_get_clientdata(client); + + long val = simple_strtol(buf, NULL, 10); + + if (val < 0 || val > 255) + return -EINVAL; + + down(&data->update_lock); + data->pwm[nr] &= 0x81; /* Preserve additional bits */ + data->pwm[nr] |= PWM_TO_REG(val); + smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr), + data->pwm[nr]); + up(&data->update_lock); + + return count; +} + +static ssize_t set_pwm_en(struct device *dev, const char *buf, + size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m1_data *data = i2c_get_clientdata(client); + + long val = simple_strtol(buf, NULL, 10); + + if (val != 0 && val != 1) + return -EINVAL; + + down(&data->update_lock); + data->pwm[nr] &= 0xFE; /* preserve the other bits */ + data->pwm[nr] |= !val; + smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr), + data->pwm[nr]); + up(&data->update_lock); + + return count; +} + +#define fan_present(offset) \ +static ssize_t get_fan##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return get_fan(dev, buf, offset - 1); \ +} \ +static ssize_t get_fan##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return get_fan_min(dev, buf, offset - 1); \ +} \ +static ssize_t set_fan##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_fan_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t get_fan##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return get_fan_div(dev, buf, offset - 1); \ +} \ +static ssize_t set_fan##offset##_div (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_fan_div(dev, buf, count, offset - 1); \ +} \ +static ssize_t get_pwm##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return get_pwm(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_pwm(dev, buf, count, offset - 1); \ +} \ +static ssize_t get_pwm##offset##_en (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return get_pwm_en(dev, buf, offset - 1); \ +} \ +static ssize_t set_pwm##offset##_en (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_pwm_en(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan##offset, \ + NULL); \ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + get_fan##offset##_min, set_fan##offset##_min); \ +static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ + get_fan##offset##_div, set_fan##offset##_div); \ +static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ + get_pwm##offset, set_pwm##offset); \ +static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ + get_pwm##offset##_en, set_pwm##offset##_en); + +fan_present(1); +fan_present(2); + +static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL); + +static int smsc47m1_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, smsc47m1_detect); +} + +static int smsc47m1_find(int *address) +{ + u8 val; + + superio_enter(); + val = superio_inb(SUPERIO_REG_DEVID); + + /* + * SMSC LPC47M10x/LPC47M13x (device id 0x59), LPC47M14x (device id + * 0x5F) and LPC47B27x (device id 0x51) have fan control. + * The LPC47M15x and LPC47M192 chips "with hardware monitoring block" + * can do much more besides (device id 0x60). + */ + if (val == 0x51) + printk(KERN_INFO "smsc47m1: Found SMSC LPC47B27x\n"); + else if (val == 0x59) + printk(KERN_INFO "smsc47m1: Found SMSC LPC47M10x/LPC47M13x\n"); + else if (val == 0x5F) + printk(KERN_INFO "smsc47m1: Found SMSC LPC47M14x\n"); + else if (val == 0x60) + printk(KERN_INFO "smsc47m1: Found SMSC LPC47M15x/LPC47M192\n"); + else { + superio_exit(); + return -ENODEV; + } + + superio_select(); + *address = (superio_inb(SUPERIO_REG_BASE) << 8) + | superio_inb(SUPERIO_REG_BASE + 1); + val = superio_inb(SUPERIO_REG_ACT); + if (*address == 0 || (val & 0x01) == 0) { + printk(KERN_INFO "smsc47m1: Device is disabled, will not use\n"); + superio_exit(); + return -ENODEV; + } + + superio_exit(); + return 0; +} + +static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct smsc47m1_data *data; + int err = 0; + int fan1, fan2, pwm1, pwm2; + + if (!i2c_is_isa_adapter(adapter)) { + return 0; + } + + if (!request_region(address, SMSC_EXTENT, smsc47m1_driver.name)) { + dev_err(&adapter->dev, "Region 0x%x already in use!\n", address); + return -EBUSY; + } + + if (!(data = kmalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) { + err = -ENOMEM; + goto error_release; + } + memset(data, 0x00, sizeof(struct smsc47m1_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + init_MUTEX(&data->lock); + new_client->adapter = adapter; + new_client->driver = &smsc47m1_driver; + new_client->flags = 0; + + strlcpy(new_client->name, "smsc47m1", I2C_NAME_SIZE); + init_MUTEX(&data->update_lock); + + /* If no function is properly configured, there's no point in + actually registering the chip. */ + fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0)) & 0x05) + == 0x05; + fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1)) & 0x05) + == 0x05; + pwm1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05) + == 0x04; + pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05) + == 0x04; + if (!(fan1 || fan2 || pwm1 || pwm2)) { + dev_warn(&new_client->dev, "Device is not configured, will not use\n"); + err = -ENODEV; + goto error_free; + } + + if ((err = i2c_attach_client(new_client))) + goto error_free; + + /* Some values (fan min, clock dividers, pwm registers) may be + needed before any update is triggered, so we better read them + at least once here. We don't usually do it that way, but in + this particular case, manually reading 5 registers out of 8 + doesn't make much sense and we're better using the existing + function. */ + smsc47m1_update_device(&new_client->dev, 1); + + if (fan1) { + device_create_file(&new_client->dev, &dev_attr_fan1_input); + device_create_file(&new_client->dev, &dev_attr_fan1_min); + device_create_file(&new_client->dev, &dev_attr_fan1_div); + } else + dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, " + "skipping\n"); + + if (fan2) { + device_create_file(&new_client->dev, &dev_attr_fan2_input); + device_create_file(&new_client->dev, &dev_attr_fan2_min); + device_create_file(&new_client->dev, &dev_attr_fan2_div); + } else + dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, " + "skipping\n"); + + if (pwm1) { + device_create_file(&new_client->dev, &dev_attr_pwm1); + device_create_file(&new_client->dev, &dev_attr_pwm1_enable); + } else + dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, " + "skipping\n"); + if (pwm2) { + device_create_file(&new_client->dev, &dev_attr_pwm2); + device_create_file(&new_client->dev, &dev_attr_pwm2_enable); + } else + dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, " + "skipping\n"); + + device_create_file(&new_client->dev, &dev_attr_alarms); + + return 0; + +error_free: + kfree(new_client); +error_release: + release_region(address, SMSC_EXTENT); + return err; +} + +static int smsc47m1_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + release_region(client->addr, SMSC_EXTENT); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static int smsc47m1_read_value(struct i2c_client *client, u8 reg) +{ + int res; + + down(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock); + res = inb_p(client->addr + reg); + up(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock); + return res; +} + +static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + down(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock); + outb_p(value, client->addr + reg); + up(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock); +} + +static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, + int init) +{ + struct i2c_client *client = to_i2c_client(dev); + struct smsc47m1_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) { + int i; + + for (i = 0; i < 2; i++) { + data->fan[i] = smsc47m1_read_value(client, + SMSC47M1_REG_FAN(i)); + data->fan_preload[i] = smsc47m1_read_value(client, + SMSC47M1_REG_FAN_PRELOAD(i)); + data->pwm[i] = smsc47m1_read_value(client, + SMSC47M1_REG_PWM(i)); + } + + i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + + data->alarms = smsc47m1_read_value(client, + SMSC47M1_REG_ALARM) >> 6; + /* Clear alarms if needed */ + if (data->alarms) + smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0); + + data->last_updated = jiffies; + } + + up(&data->update_lock); + return data; +} + +static int __init sm_smsc47m1_init(void) +{ + if (smsc47m1_find(normal_isa)) { + return -ENODEV; + } + + return i2c_add_driver(&smsc47m1_driver); +} + +static void __exit sm_smsc47m1_exit(void) +{ + i2c_del_driver(&smsc47m1_driver); +} + +MODULE_AUTHOR("Mark D. Studebaker "); +MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver"); +MODULE_LICENSE("GPL"); + +module_init(sm_smsc47m1_init); +module_exit(sm_smsc47m1_exit); diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c new file mode 100644 index 000000000000..164d47948390 --- /dev/null +++ b/drivers/hwmon/via686a.c @@ -0,0 +1,875 @@ +/* + via686a.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + Copyright (c) 1998 - 2002 Frodo Looijaard , + Kyösti Mälkki , + Mark Studebaker , + and Bob Dougherty + (Some conversion-factor data were contributed by Jonathan Teh Soon Yew + and Alex van Kaam .) + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Supports the Via VT82C686A, VT82C686B south bridges. + Reports all as a 686A. + Warning - only supports a single device. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static unsigned short force_addr = 0; +module_param(force_addr, ushort, 0); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the sensors"); + +/* Addresses to scan. + Note that we can't determine the ISA address until we have initialized + our module */ +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(via686a); + +/* + The Via 686a southbridge has a LM78-like chip integrated on the same IC. + This driver is a customized copy of lm78.c +*/ + +/* Many VIA686A constants specified below */ + +/* Length of ISA address segment */ +#define VIA686A_EXTENT 0x80 +#define VIA686A_BASE_REG 0x70 +#define VIA686A_ENABLE_REG 0x74 + +/* The VIA686A registers */ +/* ins numbered 0-4 */ +#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2)) +#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2)) +#define VIA686A_REG_IN(nr) (0x22 + (nr)) + +/* fans numbered 1-2 */ +#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr)) +#define VIA686A_REG_FAN(nr) (0x28 + (nr)) + +/* temps numbered 1-3 */ +static const u8 VIA686A_REG_TEMP[] = { 0x20, 0x21, 0x1f }; +static const u8 VIA686A_REG_TEMP_OVER[] = { 0x39, 0x3d, 0x1d }; +static const u8 VIA686A_REG_TEMP_HYST[] = { 0x3a, 0x3e, 0x1e }; +/* bits 7-6 */ +#define VIA686A_REG_TEMP_LOW1 0x4b +/* 2 = bits 5-4, 3 = bits 7-6 */ +#define VIA686A_REG_TEMP_LOW23 0x49 + +#define VIA686A_REG_ALARM1 0x41 +#define VIA686A_REG_ALARM2 0x42 +#define VIA686A_REG_FANDIV 0x47 +#define VIA686A_REG_CONFIG 0x40 +/* The following register sets temp interrupt mode (bits 1-0 for temp1, + 3-2 for temp2, 5-4 for temp3). Modes are: + 00 interrupt stays as long as value is out-of-range + 01 interrupt is cleared once register is read (default) + 10 comparator mode- like 00, but ignores hysteresis + 11 same as 00 */ +#define VIA686A_REG_TEMP_MODE 0x4b +/* We'll just assume that you want to set all 3 simultaneously: */ +#define VIA686A_TEMP_MODE_MASK 0x3F +#define VIA686A_TEMP_MODE_CONTINUOUS 0x00 + +/* Conversions. Limit checking is only done on the TO_REG + variants. + +********* VOLTAGE CONVERSIONS (Bob Dougherty) ******** + From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew): + voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp + voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V + voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V + voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V + voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V + in[i]=(data[i+2]*25.0+133)*voltagefactor[i]; + That is: + volts = (25*regVal+133)*factor + regVal = (volts/factor-133)/25 + (These conversions were contributed by Jonathan Teh Soon Yew + ) */ +static inline u8 IN_TO_REG(long val, int inNum) +{ + /* To avoid floating point, we multiply constants by 10 (100 for +12V). + Rounding is done (120500 is actually 133000 - 12500). + Remember that val is expressed in 0.001V/bit, which is why we divide + by an additional 10000 (100000 for +12V): 1000 for val and 10 (100) + for the constants. */ + if (inNum <= 1) + return (u8) + SENSORS_LIMIT((val * 21024 - 1205000) / 250000, 0, 255); + else if (inNum == 2) + return (u8) + SENSORS_LIMIT((val * 15737 - 1205000) / 250000, 0, 255); + else if (inNum == 3) + return (u8) + SENSORS_LIMIT((val * 10108 - 1205000) / 250000, 0, 255); + else + return (u8) + SENSORS_LIMIT((val * 41714 - 12050000) / 2500000, 0, 255); +} + +static inline long IN_FROM_REG(u8 val, int inNum) +{ + /* To avoid floating point, we multiply constants by 10 (100 for +12V). + We also multiply them by 1000 because we want 0.001V/bit for the + output value. Rounding is done. */ + if (inNum <= 1) + return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024); + else if (inNum == 2) + return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737); + else if (inNum == 3) + return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108); + else + return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714); +} + +/********* FAN RPM CONVERSIONS ********/ +/* Higher register values = slower fans (the fan's strobe gates a counter). + But this chip saturates back at 0, not at 255 like all the other chips. + So, 0 means 0 RPM */ +static inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 0; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255); +} + +#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div))) + +/******** TEMP CONVERSIONS (Bob Dougherty) *********/ +/* linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew) + if(temp<169) + return double(temp)*0.427-32.08; + else if(temp>=169 && temp<=202) + return double(temp)*0.582-58.16; + else + return double(temp)*0.924-127.33; + + A fifth-order polynomial fits the unofficial data (provided by Alex van + Kaam ) a bit better. It also give more reasonable + numbers on my machine (ie. they agree with what my BIOS tells me). + Here's the fifth-order fit to the 8-bit data: + temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 - + 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0. + + (2000-10-25- RFD: thanks to Uwe Andersen for + finding my typos in this formula!) + + Alas, none of the elegant function-fit solutions will work because we + aren't allowed to use floating point in the kernel and doing it with + integers doesn't provide enough precision. So we'll do boring old + look-up table stuff. The unofficial data (see below) have effectively + 7-bit resolution (they are rounded to the nearest degree). I'm assuming + that the transfer function of the device is monotonic and smooth, so a + smooth function fit to the data will allow us to get better precision. + I used the 5th-order poly fit described above and solved for + VIA register values 0-255. I *10 before rounding, so we get tenth-degree + precision. (I could have done all 1024 values for our 10-bit readings, + but the function is very linear in the useful range (0-80 deg C), so + we'll just use linear interpolation for 10-bit readings.) So, tempLUT + is the temp at via register values 0-255: */ +static const long tempLUT[] = +{ -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519, + -503, -487, -471, -456, -442, -428, -414, -400, -387, -375, + -362, -350, -339, -327, -316, -305, -295, -285, -275, -265, + -255, -246, -237, -229, -220, -212, -204, -196, -188, -180, + -173, -166, -159, -152, -145, -139, -132, -126, -120, -114, + -108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49, + -44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16, + 20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84, + 88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138, + 142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189, + 193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241, + 245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294, + 299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348, + 353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404, + 409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464, + 469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532, + 538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614, + 621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718, + 728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856, + 870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044, + 1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252, + 1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462 +}; + +/* the original LUT values from Alex van Kaam + (for via register values 12-240): +{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31, +-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15, +-15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3, +-3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12, +12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22, +22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33, +33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45, +45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60, +61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84, +85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110}; + + + Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed + an extra term for a good fit to these inverse data!) and then + solving for each temp value from -50 to 110 (the useable range for + this chip). Here's the fit: + viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4 + - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01) + Note that n=161: */ +static const u8 viaLUT[] = +{ 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40, + 41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66, + 69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100, + 103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129, + 131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156, + 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, + 182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199, + 200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224, + 225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232, + 233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, + 239, 240 +}; + +/* Converting temps to (8-bit) hyst and over registers + No interpolation here. + The +50 is because the temps start at -50 */ +static inline u8 TEMP_TO_REG(long val) +{ + return viaLUT[val <= -50000 ? 0 : val >= 110000 ? 160 : + (val < 0 ? val - 500 : val + 500) / 1000 + 50]; +} + +/* for 8-bit temperature hyst and over registers */ +#define TEMP_FROM_REG(val) (tempLUT[(val)] * 100) + +/* for 10-bit temperature readings */ +static inline long TEMP_FROM_REG10(u16 val) +{ + u16 eightBits = val >> 2; + u16 twoBits = val & 3; + + /* no interpolation for these */ + if (twoBits == 0 || eightBits == 255) + return TEMP_FROM_REG(eightBits); + + /* do some linear interpolation */ + return (tempLUT[eightBits] * (4 - twoBits) + + tempLUT[eightBits + 1] * twoBits) * 25; +} + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) + +/* For the VIA686A, we need to keep some data in memory. + The structure is dynamically allocated, at the same time when a new + via686a client is allocated. */ +struct via686a_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[5]; /* Register value */ + u8 in_max[5]; /* Register value */ + u8 in_min[5]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u16 temp[3]; /* Register value 10 bit */ + u8 temp_over[3]; /* Register value */ + u8 temp_hyst[3]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u16 alarms; /* Register encoding, combined */ +}; + +static struct pci_dev *s_bridge; /* pointer to the (only) via686a */ + +static int via686a_attach_adapter(struct i2c_adapter *adapter); +static int via686a_detect(struct i2c_adapter *adapter, int address, int kind); +static int via686a_detach_client(struct i2c_client *client); + +static inline int via686a_read_value(struct i2c_client *client, u8 reg) +{ + return (inb_p(client->addr + reg)); +} + +static inline void via686a_write_value(struct i2c_client *client, u8 reg, + u8 value) +{ + outb_p(value, client->addr + reg); +} + +static struct via686a_data *via686a_update_device(struct device *dev); +static void via686a_init_client(struct i2c_client *client); + +/* following are the sysfs callback functions */ + +/* 7 voltage sensors */ +static ssize_t show_in(struct device *dev, char *buf, int nr) { + struct via686a_data *data = via686a_update_device(dev); + return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr)); +} + +static ssize_t show_in_min(struct device *dev, char *buf, int nr) { + struct via686a_data *data = via686a_update_device(dev); + return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr)); +} + +static ssize_t show_in_max(struct device *dev, char *buf, int nr) { + struct via686a_data *data = via686a_update_device(dev); + return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr)); +} + +static ssize_t set_in_min(struct device *dev, const char *buf, + size_t count, int nr) { + struct i2c_client *client = to_i2c_client(dev); + struct via686a_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->in_min[nr] = IN_TO_REG(val, nr); + via686a_write_value(client, VIA686A_REG_IN_MIN(nr), + data->in_min[nr]); + up(&data->update_lock); + return count; +} +static ssize_t set_in_max(struct device *dev, const char *buf, + size_t count, int nr) { + struct i2c_client *client = to_i2c_client(dev); + struct via686a_data *data = i2c_get_clientdata(client); + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->in_max[nr] = IN_TO_REG(val, nr); + via686a_write_value(client, VIA686A_REG_IN_MAX(nr), + data->in_max[nr]); + up(&data->update_lock); + return count; +} +#define show_in_offset(offset) \ +static ssize_t \ + show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in(dev, buf, offset); \ +} \ +static ssize_t \ + show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in_min(dev, buf, offset); \ +} \ +static ssize_t \ + show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in_max(dev, buf, offset); \ +} \ +static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_in_min(dev, buf, count, offset); \ +} \ +static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_in_max(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL);\ +static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in##offset##_min, set_in##offset##_min); \ +static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in##offset##_max, set_in##offset##_max); + +show_in_offset(0); +show_in_offset(1); +show_in_offset(2); +show_in_offset(3); +show_in_offset(4); + +/* 3 temperatures */ +static ssize_t show_temp(struct device *dev, char *buf, int nr) { + struct via686a_data *data = via686a_update_device(dev); + return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr])); +} +static ssize_t show_temp_over(struct device *dev, char *buf, int nr) { + struct via686a_data *data = via686a_update_device(dev); + return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr])); +} +static ssize_t show_temp_hyst(struct device *dev, char *buf, int nr) { + struct via686a_data *data = via686a_update_device(dev); + return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr])); +} +static ssize_t set_temp_over(struct device *dev, const char *buf, + size_t count, int nr) { + struct i2c_client *client = to_i2c_client(dev); + struct via686a_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_over[nr] = TEMP_TO_REG(val); + via686a_write_value(client, VIA686A_REG_TEMP_OVER[nr], + data->temp_over[nr]); + up(&data->update_lock); + return count; +} +static ssize_t set_temp_hyst(struct device *dev, const char *buf, + size_t count, int nr) { + struct i2c_client *client = to_i2c_client(dev); + struct via686a_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_hyst[nr] = TEMP_TO_REG(val); + via686a_write_value(client, VIA686A_REG_TEMP_HYST[nr], + data->temp_hyst[nr]); + up(&data->update_lock); + return count; +} +#define show_temp_offset(offset) \ +static ssize_t show_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp(dev, buf, offset - 1); \ +} \ +static ssize_t \ +show_temp_##offset##_over (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp_over(dev, buf, offset - 1); \ +} \ +static ssize_t \ +show_temp_##offset##_hyst (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp_hyst(dev, buf, offset - 1); \ +} \ +static ssize_t set_temp_##offset##_over (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_temp_over(dev, buf, count, offset - 1); \ +} \ +static ssize_t set_temp_##offset##_hyst (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_temp_hyst(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL);\ +static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ + show_temp_##offset##_over, set_temp_##offset##_over); \ +static DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \ + show_temp_##offset##_hyst, set_temp_##offset##_hyst); + +show_temp_offset(1); +show_temp_offset(2); +show_temp_offset(3); + +/* 2 Fans */ +static ssize_t show_fan(struct device *dev, char *buf, int nr) { + struct via686a_data *data = via686a_update_device(dev); + return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr])) ); +} +static ssize_t show_fan_min(struct device *dev, char *buf, int nr) { + struct via686a_data *data = via686a_update_device(dev); + return sprintf(buf, "%d\n", + FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])) ); +} +static ssize_t show_fan_div(struct device *dev, char *buf, int nr) { + struct via686a_data *data = via686a_update_device(dev); + return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) ); +} +static ssize_t set_fan_min(struct device *dev, const char *buf, + size_t count, int nr) { + struct i2c_client *client = to_i2c_client(dev); + struct via686a_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); + via686a_write_value(client, VIA686A_REG_FAN_MIN(nr+1), data->fan_min[nr]); + up(&data->update_lock); + return count; +} +static ssize_t set_fan_div(struct device *dev, const char *buf, + size_t count, int nr) { + struct i2c_client *client = to_i2c_client(dev); + struct via686a_data *data = i2c_get_clientdata(client); + int val = simple_strtol(buf, NULL, 10); + int old; + + down(&data->update_lock); + old = via686a_read_value(client, VIA686A_REG_FANDIV); + data->fan_div[nr] = DIV_TO_REG(val); + old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4); + via686a_write_value(client, VIA686A_REG_FANDIV, old); + up(&data->update_lock); + return count; +} + +#define show_fan_offset(offset) \ +static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_min(dev, buf, offset - 1); \ +} \ +static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_div(dev, buf, offset - 1); \ +} \ +static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_fan_min(dev, buf, count, offset - 1); \ +} \ +static ssize_t set_fan_##offset##_div (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return set_fan_div(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_fan_##offset##_min, set_fan_##offset##_min); \ +static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ + show_fan_##offset##_div, set_fan_##offset##_div); + +show_fan_offset(1); +show_fan_offset(2); + +/* Alarms */ +static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) { + struct via686a_data *data = via686a_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); +} +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); + +/* The driver. I choose to use type i2c_driver, as at is identical to both + smbus_driver and isa_driver, and clients could be of either kind */ +static struct i2c_driver via686a_driver = { + .owner = THIS_MODULE, + .name = "via686a", + .id = I2C_DRIVERID_VIA686A, + .flags = I2C_DF_NOTIFY, + .attach_adapter = via686a_attach_adapter, + .detach_client = via686a_detach_client, +}; + + +/* This is called when the module is loaded */ +static int via686a_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, via686a_detect); +} + +static int via686a_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct via686a_data *data; + int err = 0; + const char client_name[] = "via686a"; + u16 val; + + /* Make sure we are probing the ISA bus!! */ + if (!i2c_is_isa_adapter(adapter)) { + dev_err(&adapter->dev, + "via686a_detect called for an I2C bus adapter?!?\n"); + return 0; + } + + /* 8231 requires multiple of 256, we enforce that on 686 as well */ + if (force_addr) + address = force_addr & 0xFF00; + + if (force_addr) { + dev_warn(&adapter->dev, "forcing ISA address 0x%04X\n", + address); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(s_bridge, VIA686A_BASE_REG, address)) + return -ENODEV; + } + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val)) + return -ENODEV; + if (!(val & 0x0001)) { + dev_warn(&adapter->dev, "enabling sensors\n"); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(s_bridge, VIA686A_ENABLE_REG, + val | 0x0001)) + return -ENODEV; + } + + /* Reserve the ISA region */ + if (!request_region(address, VIA686A_EXTENT, via686a_driver.name)) { + dev_err(&adapter->dev, "region 0x%x already in use!\n", + address); + return -ENODEV; + } + + if (!(data = kmalloc(sizeof(struct via686a_data), GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + memset(data, 0, sizeof(struct via686a_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &via686a_driver; + new_client->flags = 0; + + /* Fill in the remaining client fields and put into the global list */ + strlcpy(new_client->name, client_name, I2C_NAME_SIZE); + + data->valid = 0; + init_MUTEX(&data->update_lock); + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Initialize the VIA686A chip */ + via686a_init_client(new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_in0_input); + device_create_file(&new_client->dev, &dev_attr_in1_input); + device_create_file(&new_client->dev, &dev_attr_in2_input); + device_create_file(&new_client->dev, &dev_attr_in3_input); + device_create_file(&new_client->dev, &dev_attr_in4_input); + device_create_file(&new_client->dev, &dev_attr_in0_min); + device_create_file(&new_client->dev, &dev_attr_in1_min); + device_create_file(&new_client->dev, &dev_attr_in2_min); + device_create_file(&new_client->dev, &dev_attr_in3_min); + device_create_file(&new_client->dev, &dev_attr_in4_min); + device_create_file(&new_client->dev, &dev_attr_in0_max); + device_create_file(&new_client->dev, &dev_attr_in1_max); + device_create_file(&new_client->dev, &dev_attr_in2_max); + device_create_file(&new_client->dev, &dev_attr_in3_max); + device_create_file(&new_client->dev, &dev_attr_in4_max); + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp2_input); + device_create_file(&new_client->dev, &dev_attr_temp3_input); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + device_create_file(&new_client->dev, &dev_attr_temp2_max); + device_create_file(&new_client->dev, &dev_attr_temp3_max); + device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); + device_create_file(&new_client->dev, &dev_attr_temp2_max_hyst); + device_create_file(&new_client->dev, &dev_attr_temp3_max_hyst); + device_create_file(&new_client->dev, &dev_attr_fan1_input); + device_create_file(&new_client->dev, &dev_attr_fan2_input); + device_create_file(&new_client->dev, &dev_attr_fan1_min); + device_create_file(&new_client->dev, &dev_attr_fan2_min); + device_create_file(&new_client->dev, &dev_attr_fan1_div); + device_create_file(&new_client->dev, &dev_attr_fan2_div); + device_create_file(&new_client->dev, &dev_attr_alarms); + + return 0; + +ERROR3: + kfree(data); +ERROR0: + release_region(address, VIA686A_EXTENT); + return err; +} + +static int via686a_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, + "Client deregistration failed, client not detached.\n"); + return err; + } + + release_region(client->addr, VIA686A_EXTENT); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +/* Called when we have found a new VIA686A. Set limits, etc. */ +static void via686a_init_client(struct i2c_client *client) +{ + u8 reg; + + /* Start monitoring */ + reg = via686a_read_value(client, VIA686A_REG_CONFIG); + via686a_write_value(client, VIA686A_REG_CONFIG, (reg|0x01)&0x7F); + + /* Configure temp interrupt mode for continuous-interrupt operation */ + via686a_write_value(client, VIA686A_REG_TEMP_MODE, + via686a_read_value(client, VIA686A_REG_TEMP_MODE) & + !(VIA686A_TEMP_MODE_MASK | VIA686A_TEMP_MODE_CONTINUOUS)); +} + +static struct via686a_data *via686a_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct via686a_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + for (i = 0; i <= 4; i++) { + data->in[i] = + via686a_read_value(client, VIA686A_REG_IN(i)); + data->in_min[i] = via686a_read_value(client, + VIA686A_REG_IN_MIN + (i)); + data->in_max[i] = + via686a_read_value(client, VIA686A_REG_IN_MAX(i)); + } + for (i = 1; i <= 2; i++) { + data->fan[i - 1] = + via686a_read_value(client, VIA686A_REG_FAN(i)); + data->fan_min[i - 1] = via686a_read_value(client, + VIA686A_REG_FAN_MIN(i)); + } + for (i = 0; i <= 2; i++) { + data->temp[i] = via686a_read_value(client, + VIA686A_REG_TEMP[i]) << 2; + data->temp_over[i] = + via686a_read_value(client, + VIA686A_REG_TEMP_OVER[i]); + data->temp_hyst[i] = + via686a_read_value(client, + VIA686A_REG_TEMP_HYST[i]); + } + /* add in lower 2 bits + temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1 + temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23 + temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23 + */ + data->temp[0] |= (via686a_read_value(client, + VIA686A_REG_TEMP_LOW1) + & 0xc0) >> 6; + data->temp[1] |= + (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) & + 0x30) >> 4; + data->temp[2] |= + (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) & + 0xc0) >> 6; + + i = via686a_read_value(client, VIA686A_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = + via686a_read_value(client, + VIA686A_REG_ALARM1) | + (via686a_read_value(client, VIA686A_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static struct pci_device_id via686a_pci_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, via686a_pci_ids); + +static int __devinit via686a_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + u16 val; + int addr = 0; + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(dev, VIA686A_BASE_REG, &val)) + return -ENODEV; + + addr = val & ~(VIA686A_EXTENT - 1); + if (addr == 0 && force_addr == 0) { + dev_err(&dev->dev, "base address not set - upgrade BIOS " + "or use force_addr=0xaddr\n"); + return -ENODEV; + } + if (force_addr) + addr = force_addr; /* so detect will get called */ + + if (!addr) { + dev_err(&dev->dev, "No Via 686A sensors found.\n"); + return -ENODEV; + } + normal_isa[0] = addr; + + s_bridge = pci_dev_get(dev); + if (i2c_add_driver(&via686a_driver)) { + pci_dev_put(s_bridge); + s_bridge = NULL; + } + + /* Always return failure here. This is to allow other drivers to bind + * to this pci device. We don't really want to have control over the + * pci device, we only wanted to read as few register values from it. + */ + return -ENODEV; +} + +static struct pci_driver via686a_pci_driver = { + .name = "via686a", + .id_table = via686a_pci_ids, + .probe = via686a_pci_probe, +}; + +static int __init sm_via686a_init(void) +{ + return pci_register_driver(&via686a_pci_driver); +} + +static void __exit sm_via686a_exit(void) +{ + pci_unregister_driver(&via686a_pci_driver); + if (s_bridge != NULL) { + i2c_del_driver(&via686a_driver); + pci_dev_put(s_bridge); + s_bridge = NULL; + } +} + +MODULE_AUTHOR("Kyösti Mälkki , " + "Mark Studebaker " + "and Bob Dougherty "); +MODULE_DESCRIPTION("VIA 686A Sensor device"); +MODULE_LICENSE("GPL"); + +module_init(sm_via686a_init); +module_exit(sm_via686a_exit); diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c new file mode 100644 index 000000000000..8a40b6976e1a --- /dev/null +++ b/drivers/hwmon/w83627ehf.c @@ -0,0 +1,846 @@ +/* + w83627ehf - Driver for the hardware monitoring functionality of + the Winbond W83627EHF Super-I/O chip + Copyright (C) 2005 Jean Delvare + + Shamelessly ripped from the w83627hf driver + Copyright (C) 2003 Mark Studebaker + + Thanks to Leon Moonen, Steve Cliffe and Grant Coady for their help + in testing and debugging this driver. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + + + Supports the following chips: + + Chip #vin #fan #pwm #temp chip_id man_id + w83627ehf - 5 - 3 0x88 0x5ca3 + + This is a preliminary version of the driver, only supporting the + fan and temperature inputs. The chip does much more than that. +*/ + +#include +#include +#include +#include +#include +#include +#include "lm75.h" + +/* Addresses to scan + The actual ISA address is read from Super-I/O configuration space */ +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +static unsigned int normal_isa[] = { 0, I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(w83627ehf); + +/* + * Super-I/O constants and functions + */ + +static int REG; /* The register to read/write */ +static int VAL; /* The value to read/write */ + +#define W83627EHF_LD_HWM 0x0b + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ +#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ + +#define SIO_W83627EHF_ID 0x8840 +#define SIO_ID_MASK 0xFFC0 + +static inline void +superio_outb(int reg, int val) +{ + outb(reg, REG); + outb(val, VAL); +} + +static inline int +superio_inb(int reg) +{ + outb(reg, REG); + return inb(VAL); +} + +static inline void +superio_select(int ld) +{ + outb(SIO_REG_LDSEL, REG); + outb(ld, VAL); +} + +static inline void +superio_enter(void) +{ + outb(0x87, REG); + outb(0x87, REG); +} + +static inline void +superio_exit(void) +{ + outb(0x02, REG); + outb(0x02, VAL); +} + +/* + * ISA constants + */ + +#define REGION_LENGTH 8 +#define ADDR_REG_OFFSET 5 +#define DATA_REG_OFFSET 6 + +#define W83627EHF_REG_BANK 0x4E +#define W83627EHF_REG_CONFIG 0x40 +#define W83627EHF_REG_CHIP_ID 0x49 +#define W83627EHF_REG_MAN_ID 0x4F + +static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 }; +static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c }; + +#define W83627EHF_REG_TEMP1 0x27 +#define W83627EHF_REG_TEMP1_HYST 0x3a +#define W83627EHF_REG_TEMP1_OVER 0x39 +static const u16 W83627EHF_REG_TEMP[] = { 0x150, 0x250 }; +static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x153, 0x253 }; +static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x155, 0x255 }; +static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 }; + +/* Fan clock dividers are spread over the following five registers */ +#define W83627EHF_REG_FANDIV1 0x47 +#define W83627EHF_REG_FANDIV2 0x4B +#define W83627EHF_REG_VBAT 0x5D +#define W83627EHF_REG_DIODE 0x59 +#define W83627EHF_REG_SMI_OVT 0x4C + +/* + * Conversions + */ + +static inline unsigned int +fan_from_reg(u8 reg, unsigned int div) +{ + if (reg == 0 || reg == 255) + return 0; + return 1350000U / (reg * div); +} + +static inline unsigned int +div_from_reg(u8 reg) +{ + return 1 << reg; +} + +static inline int +temp1_from_reg(s8 reg) +{ + return reg * 1000; +} + +static inline s8 +temp1_to_reg(int temp) +{ + if (temp <= -128000) + return -128; + if (temp >= 127000) + return 127; + if (temp < 0) + return (temp - 500) / 1000; + return (temp + 500) / 1000; +} + +/* + * Data structures and manipulation thereof + */ + +struct w83627ehf_data { + struct i2c_client client; + struct semaphore lock; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + /* Register values */ + u8 fan[5]; + u8 fan_min[5]; + u8 fan_div[5]; + u8 has_fan; /* some fan inputs can be disabled */ + s8 temp1; + s8 temp1_max; + s8 temp1_max_hyst; + s16 temp[2]; + s16 temp_max[2]; + s16 temp_max_hyst[2]; +}; + +static inline int is_word_sized(u16 reg) +{ + return (((reg & 0xff00) == 0x100 + || (reg & 0xff00) == 0x200) + && ((reg & 0x00ff) == 0x50 + || (reg & 0x00ff) == 0x53 + || (reg & 0x00ff) == 0x55)); +} + +/* We assume that the default bank is 0, thus the following two functions do + nothing for registers which live in bank 0. For others, they respectively + set the bank register to the correct value (before the register is + accessed), and back to 0 (afterwards). */ +static inline void w83627ehf_set_bank(struct i2c_client *client, u16 reg) +{ + if (reg & 0xff00) { + outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET); + outb_p(reg >> 8, client->addr + DATA_REG_OFFSET); + } +} + +static inline void w83627ehf_reset_bank(struct i2c_client *client, u16 reg) +{ + if (reg & 0xff00) { + outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET); + outb_p(0, client->addr + DATA_REG_OFFSET); + } +} + +static u16 w83627ehf_read_value(struct i2c_client *client, u16 reg) +{ + struct w83627ehf_data *data = i2c_get_clientdata(client); + int res, word_sized = is_word_sized(reg); + + down(&data->lock); + + w83627ehf_set_bank(client, reg); + outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET); + res = inb_p(client->addr + DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + client->addr + ADDR_REG_OFFSET); + res = (res << 8) + inb_p(client->addr + DATA_REG_OFFSET); + } + w83627ehf_reset_bank(client, reg); + + up(&data->lock); + + return res; +} + +static int w83627ehf_write_value(struct i2c_client *client, u16 reg, u16 value) +{ + struct w83627ehf_data *data = i2c_get_clientdata(client); + int word_sized = is_word_sized(reg); + + down(&data->lock); + + w83627ehf_set_bank(client, reg); + outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, client->addr + DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + client->addr + ADDR_REG_OFFSET); + } + outb_p(value & 0xff, client->addr + DATA_REG_OFFSET); + w83627ehf_reset_bank(client, reg); + + up(&data->lock); + return 0; +} + +/* This function assumes that the caller holds data->update_lock */ +static void w83627ehf_write_fan_div(struct i2c_client *client, int nr) +{ + struct w83627ehf_data *data = i2c_get_clientdata(client); + u8 reg; + + switch (nr) { + case 0: + reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0xcf) + | ((data->fan_div[0] & 0x03) << 4); + w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg); + reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xdf) + | ((data->fan_div[0] & 0x04) << 3); + w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg); + break; + case 1: + reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0x3f) + | ((data->fan_div[1] & 0x03) << 6); + w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg); + reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xbf) + | ((data->fan_div[1] & 0x04) << 4); + w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg); + break; + case 2: + reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV2) & 0x3f) + | ((data->fan_div[2] & 0x03) << 6); + w83627ehf_write_value(client, W83627EHF_REG_FANDIV2, reg); + reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0x7f) + | ((data->fan_div[2] & 0x04) << 5); + w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg); + break; + case 3: + reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0xfc) + | (data->fan_div[3] & 0x03); + w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg); + reg = (w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT) & 0x7f) + | ((data->fan_div[3] & 0x04) << 5); + w83627ehf_write_value(client, W83627EHF_REG_SMI_OVT, reg); + break; + case 4: + reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0x73) + | ((data->fan_div[4] & 0x03) << 3) + | ((data->fan_div[4] & 0x04) << 5); + w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg); + break; + } +} + +static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627ehf_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ) + || !data->valid) { + /* Fan clock dividers */ + i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV2); + data->fan_div[2] = (i >> 6) & 0x03; + i = w83627ehf_read_value(client, W83627EHF_REG_VBAT); + data->fan_div[0] |= (i >> 3) & 0x04; + data->fan_div[1] |= (i >> 4) & 0x04; + data->fan_div[2] |= (i >> 5) & 0x04; + if (data->has_fan & ((1 << 3) | (1 << 4))) { + i = w83627ehf_read_value(client, W83627EHF_REG_DIODE); + data->fan_div[3] = i & 0x03; + data->fan_div[4] = ((i >> 2) & 0x03) + | ((i >> 5) & 0x04); + } + if (data->has_fan & (1 << 3)) { + i = w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT); + data->fan_div[3] |= (i >> 5) & 0x04; + } + + /* Measured fan speeds and limits */ + for (i = 0; i < 5; i++) { + if (!(data->has_fan & (1 << i))) + continue; + + data->fan[i] = w83627ehf_read_value(client, + W83627EHF_REG_FAN[i]); + data->fan_min[i] = w83627ehf_read_value(client, + W83627EHF_REG_FAN_MIN[i]); + + /* If we failed to measure the fan speed and clock + divider can be increased, let's try that for next + time */ + if (data->fan[i] == 0xff + && data->fan_div[i] < 0x07) { + dev_dbg(&client->dev, "Increasing fan %d " + "clock divider from %u to %u\n", + i, div_from_reg(data->fan_div[i]), + div_from_reg(data->fan_div[i] + 1)); + data->fan_div[i]++; + w83627ehf_write_fan_div(client, i); + /* Preserve min limit if possible */ + if (data->fan_min[i] >= 2 + && data->fan_min[i] != 255) + w83627ehf_write_value(client, + W83627EHF_REG_FAN_MIN[i], + (data->fan_min[i] /= 2)); + } + } + + /* Measured temperatures and limits */ + data->temp1 = w83627ehf_read_value(client, + W83627EHF_REG_TEMP1); + data->temp1_max = w83627ehf_read_value(client, + W83627EHF_REG_TEMP1_OVER); + data->temp1_max_hyst = w83627ehf_read_value(client, + W83627EHF_REG_TEMP1_HYST); + for (i = 0; i < 2; i++) { + data->temp[i] = w83627ehf_read_value(client, + W83627EHF_REG_TEMP[i]); + data->temp_max[i] = w83627ehf_read_value(client, + W83627EHF_REG_TEMP_OVER[i]); + data->temp_max_hyst[i] = w83627ehf_read_value(client, + W83627EHF_REG_TEMP_HYST[i]); + } + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + return data; +} + +/* + * Sysfs callback functions + */ + +#define show_fan_reg(reg) \ +static ssize_t \ +show_##reg(struct device *dev, char *buf, int nr) \ +{ \ + struct w83627ehf_data *data = w83627ehf_update_device(dev); \ + return sprintf(buf, "%d\n", \ + fan_from_reg(data->reg[nr], \ + div_from_reg(data->fan_div[nr]))); \ +} +show_fan_reg(fan); +show_fan_reg(fan_min); + +static ssize_t +show_fan_div(struct device *dev, char *buf, int nr) +{ + struct w83627ehf_data *data = w83627ehf_update_device(dev); + return sprintf(buf, "%u\n", + div_from_reg(data->fan_div[nr])); +} + +static ssize_t +store_fan_min(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627ehf_data *data = i2c_get_clientdata(client); + unsigned int val = simple_strtoul(buf, NULL, 10); + unsigned int reg; + u8 new_div; + + down(&data->update_lock); + if (!val) { + /* No min limit, alarm disabled */ + data->fan_min[nr] = 255; + new_div = data->fan_div[nr]; /* No change */ + dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1); + } else if ((reg = 1350000U / val) >= 128 * 255) { + /* Speed below this value cannot possibly be represented, + even with the highest divider (128) */ + data->fan_min[nr] = 254; + new_div = 7; /* 128 == (1 << 7) */ + dev_warn(dev, "fan%u low limit %u below minimum %u, set to " + "minimum\n", nr + 1, val, fan_from_reg(254, 128)); + } else if (!reg) { + /* Speed above this value cannot possibly be represented, + even with the lowest divider (1) */ + data->fan_min[nr] = 1; + new_div = 0; /* 1 == (1 << 0) */ + dev_warn(dev, "fan%u low limit %u above maximum %u, set to " + "maximum\n", nr + 1, val, fan_from_reg(1, 1)); + } else { + /* Automatically pick the best divider, i.e. the one such + that the min limit will correspond to a register value + in the 96..192 range */ + new_div = 0; + while (reg > 192 && new_div < 7) { + reg >>= 1; + new_div++; + } + data->fan_min[nr] = reg; + } + + /* Write both the fan clock divider (if it changed) and the new + fan min (unconditionally) */ + if (new_div != data->fan_div[nr]) { + if (new_div > data->fan_div[nr]) + data->fan[nr] >>= (data->fan_div[nr] - new_div); + else + data->fan[nr] <<= (new_div - data->fan_div[nr]); + + dev_dbg(dev, "fan%u clock divider changed from %u to %u\n", + nr + 1, div_from_reg(data->fan_div[nr]), + div_from_reg(new_div)); + data->fan_div[nr] = new_div; + w83627ehf_write_fan_div(client, nr); + } + w83627ehf_write_value(client, W83627EHF_REG_FAN_MIN[nr], + data->fan_min[nr]); + up(&data->update_lock); + + return count; +} + +#define sysfs_fan_offset(offset) \ +static ssize_t \ +show_reg_fan_##offset(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_fan(dev, buf, offset-1); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ + show_reg_fan_##offset, NULL); + +#define sysfs_fan_min_offset(offset) \ +static ssize_t \ +show_reg_fan##offset##_min(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_fan_min(dev, buf, offset-1); \ +} \ +static ssize_t \ +store_reg_fan##offset##_min(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return store_fan_min(dev, buf, count, offset-1); \ +} \ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_reg_fan##offset##_min, \ + store_reg_fan##offset##_min); + +#define sysfs_fan_div_offset(offset) \ +static ssize_t \ +show_reg_fan##offset##_div(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_fan_div(dev, buf, offset - 1); \ +} \ +static DEVICE_ATTR(fan##offset##_div, S_IRUGO, \ + show_reg_fan##offset##_div, NULL); + +sysfs_fan_offset(1); +sysfs_fan_min_offset(1); +sysfs_fan_div_offset(1); +sysfs_fan_offset(2); +sysfs_fan_min_offset(2); +sysfs_fan_div_offset(2); +sysfs_fan_offset(3); +sysfs_fan_min_offset(3); +sysfs_fan_div_offset(3); +sysfs_fan_offset(4); +sysfs_fan_min_offset(4); +sysfs_fan_div_offset(4); +sysfs_fan_offset(5); +sysfs_fan_min_offset(5); +sysfs_fan_div_offset(5); + +#define show_temp1_reg(reg) \ +static ssize_t \ +show_##reg(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct w83627ehf_data *data = w83627ehf_update_device(dev); \ + return sprintf(buf, "%d\n", temp1_from_reg(data->reg)); \ +} +show_temp1_reg(temp1); +show_temp1_reg(temp1_max); +show_temp1_reg(temp1_max_hyst); + +#define store_temp1_reg(REG, reg) \ +static ssize_t \ +store_temp1_##reg(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83627ehf_data *data = i2c_get_clientdata(client); \ + u32 val = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->temp1_##reg = temp1_to_reg(val); \ + w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \ + data->temp1_##reg); \ + up(&data->update_lock); \ + return count; \ +} +store_temp1_reg(OVER, max); +store_temp1_reg(HYST, max_hyst); + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1, NULL); +static DEVICE_ATTR(temp1_max, S_IRUGO| S_IWUSR, + show_temp1_max, store_temp1_max); +static DEVICE_ATTR(temp1_max_hyst, S_IRUGO| S_IWUSR, + show_temp1_max_hyst, store_temp1_max_hyst); + +#define show_temp_reg(reg) \ +static ssize_t \ +show_##reg (struct device *dev, char *buf, int nr) \ +{ \ + struct w83627ehf_data *data = w83627ehf_update_device(dev); \ + return sprintf(buf, "%d\n", \ + LM75_TEMP_FROM_REG(data->reg[nr])); \ +} +show_temp_reg(temp); +show_temp_reg(temp_max); +show_temp_reg(temp_max_hyst); + +#define store_temp_reg(REG, reg) \ +static ssize_t \ +store_##reg (struct device *dev, const char *buf, size_t count, int nr) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83627ehf_data *data = i2c_get_clientdata(client); \ + u32 val = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->reg[nr] = LM75_TEMP_TO_REG(val); \ + w83627ehf_write_value(client, W83627EHF_REG_TEMP_##REG[nr], \ + data->reg[nr]); \ + up(&data->update_lock); \ + return count; \ +} +store_temp_reg(OVER, temp_max); +store_temp_reg(HYST, temp_max_hyst); + +#define sysfs_temp_offset(offset) \ +static ssize_t \ +show_reg_temp##offset (struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_temp(dev, buf, offset - 2); \ +} \ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ + show_reg_temp##offset, NULL); + +#define sysfs_temp_reg_offset(reg, offset) \ +static ssize_t \ +show_reg_temp##offset##_##reg(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + return show_temp_##reg(dev, buf, offset - 2); \ +} \ +static ssize_t \ +store_reg_temp##offset##_##reg(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return store_temp_##reg(dev, buf, count, offset - 2); \ +} \ +static DEVICE_ATTR(temp##offset##_##reg, S_IRUGO| S_IWUSR, \ + show_reg_temp##offset##_##reg, \ + store_reg_temp##offset##_##reg); + +sysfs_temp_offset(2); +sysfs_temp_reg_offset(max, 2); +sysfs_temp_reg_offset(max_hyst, 2); +sysfs_temp_offset(3); +sysfs_temp_reg_offset(max, 3); +sysfs_temp_reg_offset(max_hyst, 3); + +/* + * Driver and client management + */ + +static struct i2c_driver w83627ehf_driver; + +static void w83627ehf_init_client(struct i2c_client *client) +{ + int i; + u8 tmp; + + /* Start monitoring is needed */ + tmp = w83627ehf_read_value(client, W83627EHF_REG_CONFIG); + if (!(tmp & 0x01)) + w83627ehf_write_value(client, W83627EHF_REG_CONFIG, + tmp | 0x01); + + /* Enable temp2 and temp3 if needed */ + for (i = 0; i < 2; i++) { + tmp = w83627ehf_read_value(client, + W83627EHF_REG_TEMP_CONFIG[i]); + if (tmp & 0x01) + w83627ehf_write_value(client, + W83627EHF_REG_TEMP_CONFIG[i], + tmp & 0xfe); + } +} + +static int w83627ehf_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *client; + struct w83627ehf_data *data; + int i, err = 0; + + if (!i2c_is_isa_adapter(adapter)) + return 0; + + if (!request_region(address, REGION_LENGTH, w83627ehf_driver.name)) { + err = -EBUSY; + goto exit; + } + + if (!(data = kmalloc(sizeof(struct w83627ehf_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit_release; + } + memset(data, 0, sizeof(struct w83627ehf_data)); + + client = &data->client; + i2c_set_clientdata(client, data); + client->addr = address; + init_MUTEX(&data->lock); + client->adapter = adapter; + client->driver = &w83627ehf_driver; + client->flags = 0; + + strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the i2c layer a new client has arrived */ + if ((err = i2c_attach_client(client))) + goto exit_free; + + /* Initialize the chip */ + w83627ehf_init_client(client); + + /* A few vars need to be filled upon startup */ + for (i = 0; i < 5; i++) + data->fan_min[i] = w83627ehf_read_value(client, + W83627EHF_REG_FAN_MIN[i]); + + /* It looks like fan4 and fan5 pins can be alternatively used + as fan on/off switches */ + data->has_fan = 0x07; /* fan1, fan2 and fan3 */ + i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1); + if (i & (1 << 2)) + data->has_fan |= (1 << 3); + if (i & (1 << 0)) + data->has_fan |= (1 << 4); + + /* Register sysfs hooks */ + device_create_file(&client->dev, &dev_attr_fan1_input); + device_create_file(&client->dev, &dev_attr_fan1_min); + device_create_file(&client->dev, &dev_attr_fan1_div); + device_create_file(&client->dev, &dev_attr_fan2_input); + device_create_file(&client->dev, &dev_attr_fan2_min); + device_create_file(&client->dev, &dev_attr_fan2_div); + device_create_file(&client->dev, &dev_attr_fan3_input); + device_create_file(&client->dev, &dev_attr_fan3_min); + device_create_file(&client->dev, &dev_attr_fan3_div); + + if (data->has_fan & (1 << 3)) { + device_create_file(&client->dev, &dev_attr_fan4_input); + device_create_file(&client->dev, &dev_attr_fan4_min); + device_create_file(&client->dev, &dev_attr_fan4_div); + } + if (data->has_fan & (1 << 4)) { + device_create_file(&client->dev, &dev_attr_fan5_input); + device_create_file(&client->dev, &dev_attr_fan5_min); + device_create_file(&client->dev, &dev_attr_fan5_div); + } + + device_create_file(&client->dev, &dev_attr_temp1_input); + device_create_file(&client->dev, &dev_attr_temp1_max); + device_create_file(&client->dev, &dev_attr_temp1_max_hyst); + device_create_file(&client->dev, &dev_attr_temp2_input); + device_create_file(&client->dev, &dev_attr_temp2_max); + device_create_file(&client->dev, &dev_attr_temp2_max_hyst); + device_create_file(&client->dev, &dev_attr_temp3_input); + device_create_file(&client->dev, &dev_attr_temp3_max); + device_create_file(&client->dev, &dev_attr_temp3_max_hyst); + + return 0; + +exit_free: + kfree(data); +exit_release: + release_region(address, REGION_LENGTH); +exit: + return err; +} + +static int w83627ehf_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, w83627ehf_detect); +} + +static int w83627ehf_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + release_region(client->addr, REGION_LENGTH); + kfree(i2c_get_clientdata(client)); + + return 0; +} + +static struct i2c_driver w83627ehf_driver = { + .owner = THIS_MODULE, + .name = "w83627ehf", + .flags = I2C_DF_NOTIFY, + .attach_adapter = w83627ehf_attach_adapter, + .detach_client = w83627ehf_detach_client, +}; + +static int __init w83627ehf_find(int sioaddr, int *address) +{ + u16 val; + + REG = sioaddr; + VAL = sioaddr + 1; + superio_enter(); + + val = (superio_inb(SIO_REG_DEVID) << 8) + | superio_inb(SIO_REG_DEVID + 1); + if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) { + superio_exit(); + return -ENODEV; + } + + superio_select(W83627EHF_LD_HWM); + val = (superio_inb(SIO_REG_ADDR) << 8) + | superio_inb(SIO_REG_ADDR + 1); + *address = val & ~(REGION_LENGTH - 1); + if (*address == 0) { + superio_exit(); + return -ENODEV; + } + + /* Activate logical device if needed */ + val = superio_inb(SIO_REG_ENABLE); + if (!(val & 0x01)) + superio_outb(SIO_REG_ENABLE, val | 0x01); + + superio_exit(); + return 0; +} + +static int __init sensors_w83627ehf_init(void) +{ + if (w83627ehf_find(0x2e, &normal_isa[0]) + && w83627ehf_find(0x4e, &normal_isa[0])) + return -ENODEV; + + return i2c_add_driver(&w83627ehf_driver); +} + +static void __exit sensors_w83627ehf_exit(void) +{ + i2c_del_driver(&w83627ehf_driver); +} + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("W83627EHF driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_w83627ehf_init); +module_exit(sensors_w83627ehf_exit); diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c new file mode 100644 index 000000000000..bd87a42e068a --- /dev/null +++ b/drivers/hwmon/w83627hf.c @@ -0,0 +1,1511 @@ +/* + w83627hf.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2003 Frodo Looijaard , + Philip Edelbrock , + and Mark Studebaker + Ported to 2.6 by Bernhard C. Schrenk + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Supports following chips: + + Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + w83627hf 9 3 2 3 0x20 0x5ca3 no yes(LPC) + w83627thf 7 3 3 3 0x90 0x5ca3 no yes(LPC) + w83637hf 7 3 3 3 0x80 0x5ca3 no yes(LPC) + w83697hf 8 2 2 2 0x60 0x5ca3 no yes(LPC) + + For other winbond chips, and for i2c support in the above chips, + use w83781d.c. + + Note: automatic ("cruise") fan control for 697, 637 & 627thf not + supported yet. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "lm75.h" + +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, + "Initialize the i2c address of the sensors"); + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { I2C_CLIENT_END }; +static unsigned int normal_isa[] = { 0, I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_4(w83627hf, w83627thf, w83697hf, w83637hf); + +static int init = 1; +module_param(init, bool, 0); +MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); + +/* 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 +#define W83627HF_LD_PRT 0x01 +#define W83627HF_LD_UART1 0x02 +#define W83627HF_LD_UART2 0x03 +#define W83627HF_LD_KBC 0x05 +#define W83627HF_LD_CIR 0x06 /* w83627hf only */ +#define W83627HF_LD_GAME 0x07 +#define W83627HF_LD_MIDI 0x07 +#define W83627HF_LD_GPIO1 0x07 +#define W83627HF_LD_GPIO5 0x07 /* w83627thf only */ +#define W83627HF_LD_GPIO2 0x08 +#define W83627HF_LD_GPIO3 0x09 +#define W83627HF_LD_GPIO4 0x09 /* w83627thf only */ +#define W83627HF_LD_ACPI 0x0a +#define W83627HF_LD_HWM 0x0b + +#define DEVID 0x20 /* Register: Device ID */ + +#define W83627THF_GPIO5_EN 0x30 /* w83627thf only */ +#define W83627THF_GPIO5_IOSR 0xf3 /* w83627thf only */ +#define W83627THF_GPIO5_DR 0xf4 /* w83627thf only */ + +static inline void +superio_outb(int reg, int val) +{ + outb(reg, REG); + outb(val, VAL); +} + +static inline int +superio_inb(int reg) +{ + outb(reg, REG); + return inb(VAL); +} + +static inline void +superio_select(int ld) +{ + outb(DEV, REG); + outb(ld, VAL); +} + +static inline void +superio_enter(void) +{ + outb(0x87, REG); + outb(0x87, REG); +} + +static inline void +superio_exit(void) +{ + outb(0xAA, REG); +} + +#define W627_DEVID 0x52 +#define W627THF_DEVID 0x82 +#define W697_DEVID 0x60 +#define W637_DEVID 0x70 +#define WINB_ACT_REG 0x30 +#define WINB_BASE_REG 0x60 +/* Constants specified below */ + +/* Length of ISA address segment */ +#define WINB_EXTENT 8 + +/* Where are the ISA address/data registers relative to the base address */ +#define W83781D_ADDR_REG_OFFSET 5 +#define W83781D_DATA_REG_OFFSET 6 + +/* The W83781D registers */ +/* The W83782D registers for nr=7,8 are in bank 5 */ +#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ + (0x554 + (((nr) - 7) * 2))) +#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ + (0x555 + (((nr) - 7) * 2))) +#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ + (0x550 + (nr) - 7)) + +#define W83781D_REG_FAN_MIN(nr) (0x3a + (nr)) +#define W83781D_REG_FAN(nr) (0x27 + (nr)) + +#define W83781D_REG_TEMP2_CONFIG 0x152 +#define W83781D_REG_TEMP3_CONFIG 0x252 +#define W83781D_REG_TEMP(nr) ((nr == 3) ? (0x0250) : \ + ((nr == 2) ? (0x0150) : \ + (0x27))) +#define W83781D_REG_TEMP_HYST(nr) ((nr == 3) ? (0x253) : \ + ((nr == 2) ? (0x153) : \ + (0x3A))) +#define W83781D_REG_TEMP_OVER(nr) ((nr == 3) ? (0x255) : \ + ((nr == 2) ? (0x155) : \ + (0x39))) + +#define W83781D_REG_BANK 0x4E + +#define W83781D_REG_CONFIG 0x40 +#define W83781D_REG_ALARM1 0x41 +#define W83781D_REG_ALARM2 0x42 +#define W83781D_REG_ALARM3 0x450 + +#define W83781D_REG_IRQ 0x4C +#define W83781D_REG_BEEP_CONFIG 0x4D +#define W83781D_REG_BEEP_INTS1 0x56 +#define W83781D_REG_BEEP_INTS2 0x57 +#define W83781D_REG_BEEP_INTS3 0x453 + +#define W83781D_REG_VID_FANDIV 0x47 + +#define W83781D_REG_CHIPID 0x49 +#define W83781D_REG_WCHIPID 0x58 +#define W83781D_REG_CHIPMAN 0x4F +#define W83781D_REG_PIN 0x4B + +#define W83781D_REG_VBAT 0x5D + +#define W83627HF_REG_PWM1 0x5A +#define W83627HF_REG_PWM2 0x5B +#define W83627HF_REG_PWMCLK12 0x5C + +#define W83627THF_REG_PWM1 0x01 /* 697HF and 637HF too */ +#define W83627THF_REG_PWM2 0x03 /* 697HF and 637HF too */ +#define W83627THF_REG_PWM3 0x11 /* 637HF too */ + +#define W83627THF_REG_VRM_OVT_CFG 0x18 /* 637HF too */ + +static const u8 regpwm_627hf[] = { W83627HF_REG_PWM1, W83627HF_REG_PWM2 }; +static const u8 regpwm[] = { W83627THF_REG_PWM1, W83627THF_REG_PWM2, + W83627THF_REG_PWM3 }; +#define W836X7HF_REG_PWM(type, nr) (((type) == w83627hf) ? \ + regpwm_627hf[(nr) - 1] : regpwm[(nr) - 1]) + +#define W83781D_REG_I2C_ADDR 0x48 +#define W83781D_REG_I2C_SUBADDR 0x4A + +/* Sensor selection */ +#define W83781D_REG_SCFG1 0x5D +static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; +#define W83781D_REG_SCFG2 0x59 +static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; +#define W83781D_DEFAULT_BETA 3435 + +/* Conversions. Limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8)/16),0,255)) +#define IN_FROM_REG(val) ((val) * 16) + +static inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define TEMP_MIN (-128000) +#define TEMP_MAX ( 127000) + +/* TEMP: 0.001C/bit (-128C to +127C) + REG: 1C/bit, two's complement */ +static u8 TEMP_TO_REG(int temp) +{ + int ntemp = SENSORS_LIMIT(temp, TEMP_MIN, TEMP_MAX); + ntemp += (ntemp<0 ? -500 : 500); + return (u8)(ntemp / 1000); +} + +static int TEMP_FROM_REG(u8 reg) +{ + return (s8)reg * 1000; +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) + +#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255)) + +#define BEEP_MASK_FROM_REG(val) (val) +#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff) +#define BEEP_ENABLE_TO_REG(val) ((val)?1:0) +#define BEEP_ENABLE_FROM_REG(val) ((val)?1:0) + +#define DIV_FROM_REG(val) (1 << (val)) + +static inline u8 DIV_TO_REG(long val) +{ + int i; + val = SENSORS_LIMIT(val, 1, 128) >> 1; + for (i = 0; i < 7; i++) { + if (val == 0) + break; + val >>= 1; + } + return ((u8) i); +} + +/* For each registered chip, we need to keep some data in memory. That + data is pointed to by w83627hf_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new client is allocated. */ +struct w83627hf_data { + struct i2c_client client; + struct semaphore lock; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + struct i2c_client *lm75; /* for secondary I2C addresses */ + /* pointer to array of 2 subclients */ + + u8 in[9]; /* Register value */ + u8 in_max[9]; /* Register value */ + u8 in_min[9]; /* Register value */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 temp; + u8 temp_max; /* Register value */ + u8 temp_max_hyst; /* Register value */ + u16 temp_add[2]; /* Register value */ + u16 temp_max_add[2]; /* Register value */ + u16 temp_max_hyst_add[2]; /* Register value */ + u8 fan_div[3]; /* Register encoding, shifted right */ + u8 vid; /* Register encoding, combined */ + u32 alarms; /* Register encoding, combined */ + u32 beep_mask; /* Register encoding, combined */ + u8 beep_enable; /* Boolean */ + u8 pwm[3]; /* Register value */ + u16 sens[3]; /* 782D/783S only. + 1 = pentium diode; 2 = 3904 diode; + 3000-5000 = thermistor beta. + Default = 3435. + Other Betas unimplemented */ + u8 vrm; + u8 vrm_ovt; /* Register value, 627thf & 637hf only */ +}; + + +static int w83627hf_attach_adapter(struct i2c_adapter *adapter); +static int w83627hf_detect(struct i2c_adapter *adapter, int address, + int kind); +static int w83627hf_detach_client(struct i2c_client *client); + +static int w83627hf_read_value(struct i2c_client *client, u16 register); +static int w83627hf_write_value(struct i2c_client *client, u16 register, + u16 value); +static struct w83627hf_data *w83627hf_update_device(struct device *dev); +static void w83627hf_init_client(struct i2c_client *client); + +static struct i2c_driver w83627hf_driver = { + .owner = THIS_MODULE, + .name = "w83627hf", + .id = I2C_DRIVERID_W83627HF, + .flags = I2C_DF_NOTIFY, + .attach_adapter = w83627hf_attach_adapter, + .detach_client = w83627hf_detach_client, +}; + +/* following are the sysfs callback functions */ +#define show_in_reg(reg) \ +static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ +{ \ + struct w83627hf_data *data = w83627hf_update_device(dev); \ + return sprintf(buf,"%ld\n", (long)IN_FROM_REG(data->reg[nr])); \ +} +show_in_reg(in) +show_in_reg(in_min) +show_in_reg(in_max) + +#define store_in_reg(REG, reg) \ +static ssize_t \ +store_in_##reg (struct device *dev, const char *buf, size_t count, int nr) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83627hf_data *data = i2c_get_clientdata(client); \ + u32 val; \ + \ + val = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + data->in_##reg[nr] = IN_TO_REG(val); \ + w83627hf_write_value(client, W83781D_REG_IN_##REG(nr), \ + data->in_##reg[nr]); \ + \ + up(&data->update_lock); \ + return count; \ +} +store_in_reg(MIN, min) +store_in_reg(MAX, max) + +#define sysfs_in_offset(offset) \ +static ssize_t \ +show_regs_in_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in(dev, buf, offset); \ +} \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_regs_in_##offset, NULL); + +#define sysfs_in_reg_offset(reg, offset) \ +static ssize_t show_regs_in_##reg##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in_##reg (dev, buf, offset); \ +} \ +static ssize_t \ +store_regs_in_##reg##offset (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return store_in_##reg (dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(in##offset##_##reg, S_IRUGO| S_IWUSR, \ + show_regs_in_##reg##offset, store_regs_in_##reg##offset); + +#define sysfs_in_offsets(offset) \ +sysfs_in_offset(offset) \ +sysfs_in_reg_offset(min, offset) \ +sysfs_in_reg_offset(max, offset) + +sysfs_in_offsets(1); +sysfs_in_offsets(2); +sysfs_in_offsets(3); +sysfs_in_offsets(4); +sysfs_in_offsets(5); +sysfs_in_offsets(6); +sysfs_in_offsets(7); +sysfs_in_offsets(8); + +/* use a different set of functions for in0 */ +static ssize_t show_in_0(struct w83627hf_data *data, char *buf, u8 reg) +{ + long in0; + + if ((data->vrm_ovt & 0x01) && + (w83627thf == data->type || w83637hf == data->type)) + + /* use VRM9 calculation */ + in0 = (long)((reg * 488 + 70000 + 50) / 100); + else + /* use VRM8 (standard) calculation */ + in0 = (long)IN_FROM_REG(reg); + + return sprintf(buf,"%ld\n", in0); +} + +static ssize_t show_regs_in_0(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return show_in_0(data, buf, data->in[0]); +} + +static ssize_t show_regs_in_min0(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return show_in_0(data, buf, data->in_min[0]); +} + +static ssize_t show_regs_in_max0(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return show_in_0(data, buf, data->in_max[0]); +} + +static ssize_t store_regs_in_min0(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627hf_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + + if ((data->vrm_ovt & 0x01) && + (w83627thf == data->type || w83637hf == data->type)) + + /* use VRM9 calculation */ + data->in_min[0] = (u8)(((val * 100) - 70000 + 244) / 488); + else + /* use VRM8 (standard) calculation */ + data->in_min[0] = IN_TO_REG(val); + + w83627hf_write_value(client, W83781D_REG_IN_MIN(0), data->in_min[0]); + up(&data->update_lock); + return count; +} + +static ssize_t store_regs_in_max0(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627hf_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + + if ((data->vrm_ovt & 0x01) && + (w83627thf == data->type || w83637hf == data->type)) + + /* use VRM9 calculation */ + data->in_max[0] = (u8)(((val * 100) - 70000 + 244) / 488); + else + /* use VRM8 (standard) calculation */ + data->in_max[0] = IN_TO_REG(val); + + w83627hf_write_value(client, W83781D_REG_IN_MAX(0), data->in_max[0]); + up(&data->update_lock); + return count; +} + +static DEVICE_ATTR(in0_input, S_IRUGO, show_regs_in_0, NULL); +static DEVICE_ATTR(in0_min, S_IRUGO | S_IWUSR, + show_regs_in_min0, store_regs_in_min0); +static DEVICE_ATTR(in0_max, S_IRUGO | S_IWUSR, + show_regs_in_max0, store_regs_in_max0); + +#define device_create_file_in(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_in##offset##_input); \ +device_create_file(&client->dev, &dev_attr_in##offset##_min); \ +device_create_file(&client->dev, &dev_attr_in##offset##_max); \ +} while (0) + +#define show_fan_reg(reg) \ +static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ +{ \ + struct w83627hf_data *data = w83627hf_update_device(dev); \ + return sprintf(buf,"%ld\n", \ + FAN_FROM_REG(data->reg[nr-1], \ + (long)DIV_FROM_REG(data->fan_div[nr-1]))); \ +} +show_fan_reg(fan); +show_fan_reg(fan_min); + +static ssize_t +store_fan_min(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627hf_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->fan_min[nr - 1] = + FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr - 1])); + w83627hf_write_value(client, W83781D_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + + up(&data->update_lock); + return count; +} + +#define sysfs_fan_offset(offset) \ +static ssize_t show_regs_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan(dev, buf, offset); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_regs_fan_##offset, NULL); + +#define sysfs_fan_min_offset(offset) \ +static ssize_t show_regs_fan_min##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_min(dev, buf, offset); \ +} \ +static ssize_t \ +store_regs_fan_min##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return store_fan_min(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ + show_regs_fan_min##offset, store_regs_fan_min##offset); + +sysfs_fan_offset(1); +sysfs_fan_min_offset(1); +sysfs_fan_offset(2); +sysfs_fan_min_offset(2); +sysfs_fan_offset(3); +sysfs_fan_min_offset(3); + +#define device_create_file_fan(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_fan##offset##_input); \ +device_create_file(&client->dev, &dev_attr_fan##offset##_min); \ +} while (0) + +#define show_temp_reg(reg) \ +static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ +{ \ + struct w83627hf_data *data = w83627hf_update_device(dev); \ + if (nr >= 2) { /* TEMP2 and TEMP3 */ \ + return sprintf(buf,"%ld\n", \ + (long)LM75_TEMP_FROM_REG(data->reg##_add[nr-2])); \ + } else { /* TEMP1 */ \ + return sprintf(buf,"%ld\n", (long)TEMP_FROM_REG(data->reg)); \ + } \ +} +show_temp_reg(temp); +show_temp_reg(temp_max); +show_temp_reg(temp_max_hyst); + +#define store_temp_reg(REG, reg) \ +static ssize_t \ +store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83627hf_data *data = i2c_get_clientdata(client); \ + u32 val; \ + \ + val = simple_strtoul(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + \ + if (nr >= 2) { /* TEMP2 and TEMP3 */ \ + data->temp_##reg##_add[nr-2] = LM75_TEMP_TO_REG(val); \ + w83627hf_write_value(client, W83781D_REG_TEMP_##REG(nr), \ + data->temp_##reg##_add[nr-2]); \ + } else { /* TEMP1 */ \ + data->temp_##reg = TEMP_TO_REG(val); \ + w83627hf_write_value(client, W83781D_REG_TEMP_##REG(nr), \ + data->temp_##reg); \ + } \ + \ + up(&data->update_lock); \ + return count; \ +} +store_temp_reg(OVER, max); +store_temp_reg(HYST, max_hyst); + +#define sysfs_temp_offset(offset) \ +static ssize_t \ +show_regs_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp(dev, buf, offset); \ +} \ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_regs_temp_##offset, NULL); + +#define sysfs_temp_reg_offset(reg, offset) \ +static ssize_t show_regs_temp_##reg##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp_##reg (dev, buf, offset); \ +} \ +static ssize_t \ +store_regs_temp_##reg##offset (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return store_temp_##reg (dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(temp##offset##_##reg, S_IRUGO| S_IWUSR, \ + show_regs_temp_##reg##offset, store_regs_temp_##reg##offset); + +#define sysfs_temp_offsets(offset) \ +sysfs_temp_offset(offset) \ +sysfs_temp_reg_offset(max, offset) \ +sysfs_temp_reg_offset(max_hyst, offset) + +sysfs_temp_offsets(1); +sysfs_temp_offsets(2); +sysfs_temp_offsets(3); + +#define device_create_file_temp(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_temp##offset##_input); \ +device_create_file(&client->dev, &dev_attr_temp##offset##_max); \ +device_create_file(&client->dev, &dev_attr_temp##offset##_max_hyst); \ +} while (0) + +static ssize_t +show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); +} +static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); +#define device_create_file_vid(client) \ +device_create_file(&client->dev, &dev_attr_cpu0_vid) + +static ssize_t +show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->vrm); +} +static ssize_t +store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627hf_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + data->vrm = val; + + return count; +} +static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); +#define device_create_file_vrm(client) \ +device_create_file(&client->dev, &dev_attr_vrm) + +static ssize_t +show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->alarms); +} +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); +#define device_create_file_alarms(client) \ +device_create_file(&client->dev, &dev_attr_alarms) + +#define show_beep_reg(REG, reg) \ +static ssize_t show_beep_##reg (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + struct w83627hf_data *data = w83627hf_update_device(dev); \ + return sprintf(buf,"%ld\n", \ + (long)BEEP_##REG##_FROM_REG(data->beep_##reg)); \ +} +show_beep_reg(ENABLE, enable) +show_beep_reg(MASK, mask) + +#define BEEP_ENABLE 0 /* Store beep_enable */ +#define BEEP_MASK 1 /* Store beep_mask */ + +static ssize_t +store_beep_reg(struct device *dev, const char *buf, size_t count, + int update_mask) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627hf_data *data = i2c_get_clientdata(client); + u32 val, val2; + + val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + + if (update_mask == BEEP_MASK) { /* We are storing beep_mask */ + data->beep_mask = BEEP_MASK_TO_REG(val); + w83627hf_write_value(client, W83781D_REG_BEEP_INTS1, + data->beep_mask & 0xff); + w83627hf_write_value(client, W83781D_REG_BEEP_INTS3, + ((data->beep_mask) >> 16) & 0xff); + val2 = (data->beep_mask >> 8) & 0x7f; + } else { /* We are storing beep_enable */ + val2 = + w83627hf_read_value(client, W83781D_REG_BEEP_INTS2) & 0x7f; + data->beep_enable = BEEP_ENABLE_TO_REG(val); + } + + w83627hf_write_value(client, W83781D_REG_BEEP_INTS2, + val2 | data->beep_enable << 7); + + up(&data->update_lock); + return count; +} + +#define sysfs_beep(REG, reg) \ +static ssize_t show_regs_beep_##reg (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_beep_##reg(dev, attr, buf); \ +} \ +static ssize_t \ +store_regs_beep_##reg (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return store_beep_reg(dev, buf, count, BEEP_##REG); \ +} \ +static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, \ + show_regs_beep_##reg, store_regs_beep_##reg); + +sysfs_beep(ENABLE, enable); +sysfs_beep(MASK, mask); + +#define device_create_file_beep(client) \ +do { \ +device_create_file(&client->dev, &dev_attr_beep_enable); \ +device_create_file(&client->dev, &dev_attr_beep_mask); \ +} while (0) + +static ssize_t +show_fan_div_reg(struct device *dev, char *buf, int nr) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", + (long) DIV_FROM_REG(data->fan_div[nr - 1])); +} + +/* Note: we save and restore the fan minimum here, because its value is + determined in part by the fan divisor. This follows the principle of + least suprise; the user doesn't expect the fan minimum to change just + because the divisor changed. */ +static ssize_t +store_fan_div_reg(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627hf_data *data = i2c_get_clientdata(client); + unsigned long min; + u8 reg; + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + + /* Save fan_min */ + min = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + + data->fan_div[nr] = DIV_TO_REG(val); + + reg = (w83627hf_read_value(client, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV) + & (nr==0 ? 0xcf : 0x3f)) + | ((data->fan_div[nr] & 0x03) << (nr==0 ? 4 : 6)); + w83627hf_write_value(client, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg); + + reg = (w83627hf_read_value(client, W83781D_REG_VBAT) + & ~(1 << (5 + nr))) + | ((data->fan_div[nr] & 0x04) << (3 + nr)); + w83627hf_write_value(client, W83781D_REG_VBAT, reg); + + /* Restore fan_min */ + data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); + w83627hf_write_value(client, W83781D_REG_FAN_MIN(nr+1), data->fan_min[nr]); + + up(&data->update_lock); + return count; +} + +#define sysfs_fan_div(offset) \ +static ssize_t show_regs_fan_div_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_div_reg(dev, buf, offset); \ +} \ +static ssize_t \ +store_regs_fan_div_##offset (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return store_fan_div_reg(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ + show_regs_fan_div_##offset, store_regs_fan_div_##offset); + +sysfs_fan_div(1); +sysfs_fan_div(2); +sysfs_fan_div(3); + +#define device_create_file_fan_div(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_fan##offset##_div); \ +} while (0) + +static ssize_t +show_pwm_reg(struct device *dev, char *buf, int nr) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->pwm[nr - 1]); +} + +static ssize_t +store_pwm_reg(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627hf_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + + if (data->type == w83627thf) { + /* bits 0-3 are reserved in 627THF */ + data->pwm[nr - 1] = PWM_TO_REG(val) & 0xf0; + w83627hf_write_value(client, + W836X7HF_REG_PWM(data->type, nr), + data->pwm[nr - 1] | + (w83627hf_read_value(client, + W836X7HF_REG_PWM(data->type, nr)) & 0x0f)); + } else { + data->pwm[nr - 1] = PWM_TO_REG(val); + w83627hf_write_value(client, + W836X7HF_REG_PWM(data->type, nr), + data->pwm[nr - 1]); + } + + up(&data->update_lock); + return count; +} + +#define sysfs_pwm(offset) \ +static ssize_t show_regs_pwm_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_pwm_reg(dev, buf, offset); \ +} \ +static ssize_t \ +store_regs_pwm_##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return store_pwm_reg(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ + show_regs_pwm_##offset, store_regs_pwm_##offset); + +sysfs_pwm(1); +sysfs_pwm(2); +sysfs_pwm(3); + +#define device_create_file_pwm(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_pwm##offset); \ +} while (0) + +static ssize_t +show_sensor_reg(struct device *dev, char *buf, int nr) +{ + struct w83627hf_data *data = w83627hf_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->sens[nr - 1]); +} + +static ssize_t +store_sensor_reg(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627hf_data *data = i2c_get_clientdata(client); + u32 val, tmp; + + val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + + switch (val) { + case 1: /* PII/Celeron diode */ + tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); + w83627hf_write_value(client, W83781D_REG_SCFG1, + tmp | BIT_SCFG1[nr - 1]); + tmp = w83627hf_read_value(client, W83781D_REG_SCFG2); + w83627hf_write_value(client, W83781D_REG_SCFG2, + tmp | BIT_SCFG2[nr - 1]); + data->sens[nr - 1] = val; + break; + case 2: /* 3904 */ + tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); + w83627hf_write_value(client, W83781D_REG_SCFG1, + tmp | BIT_SCFG1[nr - 1]); + tmp = w83627hf_read_value(client, W83781D_REG_SCFG2); + w83627hf_write_value(client, W83781D_REG_SCFG2, + tmp & ~BIT_SCFG2[nr - 1]); + data->sens[nr - 1] = val; + break; + case W83781D_DEFAULT_BETA: /* thermistor */ + tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); + w83627hf_write_value(client, W83781D_REG_SCFG1, + tmp & ~BIT_SCFG1[nr - 1]); + data->sens[nr - 1] = val; + break; + default: + dev_err(&client->dev, + "Invalid sensor type %ld; must be 1, 2, or %d\n", + (long) val, W83781D_DEFAULT_BETA); + break; + } + + up(&data->update_lock); + return count; +} + +#define sysfs_sensor(offset) \ +static ssize_t show_regs_sensor_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_sensor_reg(dev, buf, offset); \ +} \ +static ssize_t \ +store_regs_sensor_##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return store_sensor_reg(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(temp##offset##_type, S_IRUGO | S_IWUSR, \ + show_regs_sensor_##offset, store_regs_sensor_##offset); + +sysfs_sensor(1); +sysfs_sensor(2); +sysfs_sensor(3); + +#define device_create_file_sensor(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_temp##offset##_type); \ +} while (0) + + +/* This function is called when: + * w83627hf_driver is inserted (when this module is loaded), for each + available adapter + * when a new adapter is inserted (and w83627hf_driver is still present) */ +static int w83627hf_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, w83627hf_detect); +} + +static int w83627hf_find(int sioaddr, int *address) +{ + u16 val; + + REG = sioaddr; + VAL = sioaddr + 1; + + superio_enter(); + val= superio_inb(DEVID); + if(val != W627_DEVID && + val != W627THF_DEVID && + val != W697_DEVID && + val != W637_DEVID) { + superio_exit(); + return -ENODEV; + } + + superio_select(W83627HF_LD_HWM); + val = (superio_inb(WINB_BASE_REG) << 8) | + superio_inb(WINB_BASE_REG + 1); + *address = val & ~(WINB_EXTENT - 1); + if (*address == 0 && force_addr == 0) { + superio_exit(); + return -ENODEV; + } + if (force_addr) + *address = force_addr; /* so detect will get called */ + + superio_exit(); + return 0; +} + +int w83627hf_detect(struct i2c_adapter *adapter, int address, + int kind) +{ + int val; + struct i2c_client *new_client; + struct w83627hf_data *data; + int err = 0; + const char *client_name = ""; + + if (!i2c_is_isa_adapter(adapter)) { + err = -ENODEV; + goto ERROR0; + } + + if(force_addr) + address = force_addr & ~(WINB_EXTENT - 1); + + if (!request_region(address, WINB_EXTENT, w83627hf_driver.name)) { + err = -EBUSY; + goto ERROR0; + } + + if(force_addr) { + printk("w83627hf.o: forcing ISA address 0x%04X\n", address); + superio_enter(); + superio_select(W83627HF_LD_HWM); + superio_outb(WINB_BASE_REG, address >> 8); + superio_outb(WINB_BASE_REG+1, address & 0xff); + superio_exit(); + } + + superio_enter(); + val= superio_inb(DEVID); + if(val == W627_DEVID) + kind = w83627hf; + else if(val == W697_DEVID) + kind = w83697hf; + else if(val == W627THF_DEVID) + kind = w83627thf; + else if(val == W637_DEVID) + kind = w83637hf; + else { + dev_info(&adapter->dev, + "Unsupported chip (dev_id=0x%02X).\n", val); + goto ERROR1; + } + + superio_select(W83627HF_LD_HWM); + if((val = 0x01 & superio_inb(WINB_ACT_REG)) == 0) + superio_outb(WINB_ACT_REG, 1); + superio_exit(); + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access w83627hf_{read,write}_value. */ + + if (!(data = kmalloc(sizeof(struct w83627hf_data), GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR1; + } + memset(data, 0, sizeof(struct w83627hf_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + init_MUTEX(&data->lock); + new_client->adapter = adapter; + new_client->driver = &w83627hf_driver; + new_client->flags = 0; + + + if (kind == w83627hf) { + client_name = "w83627hf"; + } else if (kind == w83627thf) { + client_name = "w83627thf"; + } else if (kind == w83697hf) { + client_name = "w83697hf"; + } else if (kind == w83637hf) { + client_name = "w83637hf"; + } + + /* Fill in the remaining client fields and put into the global list */ + strlcpy(new_client->name, client_name, I2C_NAME_SIZE); + data->type = kind; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR2; + + data->lm75 = NULL; + + /* Initialize the chip */ + w83627hf_init_client(new_client); + + /* A few vars need to be filled upon startup */ + data->fan_min[0] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(1)); + data->fan_min[1] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(2)); + data->fan_min[2] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(3)); + + /* Register sysfs hooks */ + device_create_file_in(new_client, 0); + if (kind != w83697hf) + device_create_file_in(new_client, 1); + device_create_file_in(new_client, 2); + device_create_file_in(new_client, 3); + device_create_file_in(new_client, 4); + if (kind != w83627thf && kind != w83637hf) { + device_create_file_in(new_client, 5); + device_create_file_in(new_client, 6); + } + device_create_file_in(new_client, 7); + device_create_file_in(new_client, 8); + + device_create_file_fan(new_client, 1); + device_create_file_fan(new_client, 2); + if (kind != w83697hf) + device_create_file_fan(new_client, 3); + + device_create_file_temp(new_client, 1); + device_create_file_temp(new_client, 2); + if (kind != w83697hf) + device_create_file_temp(new_client, 3); + + if (kind != w83697hf) + device_create_file_vid(new_client); + + if (kind != w83697hf) + device_create_file_vrm(new_client); + + device_create_file_fan_div(new_client, 1); + device_create_file_fan_div(new_client, 2); + if (kind != w83697hf) + device_create_file_fan_div(new_client, 3); + + device_create_file_alarms(new_client); + + device_create_file_beep(new_client); + + device_create_file_pwm(new_client, 1); + device_create_file_pwm(new_client, 2); + if (kind == w83627thf || kind == w83637hf) + device_create_file_pwm(new_client, 3); + + device_create_file_sensor(new_client, 1); + device_create_file_sensor(new_client, 2); + if (kind != w83697hf) + device_create_file_sensor(new_client, 3); + + return 0; + + ERROR2: + kfree(data); + ERROR1: + release_region(address, WINB_EXTENT); + ERROR0: + return err; +} + +static int w83627hf_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, + "Client deregistration failed, client not detached.\n"); + return err; + } + + release_region(client->addr, WINB_EXTENT); + kfree(i2c_get_clientdata(client)); + + return 0; +} + + +/* + ISA access must always be locked explicitly! + We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, + would slow down the W83781D access and should not be necessary. + There are some ugly typecasts here, but the good news is - they should + nowhere else be necessary! */ +static int w83627hf_read_value(struct i2c_client *client, u16 reg) +{ + struct w83627hf_data *data = i2c_get_clientdata(client); + int res, word_sized; + + down(&data->lock); + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x50) + || ((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + res = + (res << 8) + inb_p(client->addr + + W83781D_DATA_REG_OFFSET); + } + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } + up(&data->lock); + return res; +} + +static int w83627thf_read_gpio5(struct i2c_client *client) +{ + int res = 0xff, sel; + + superio_enter(); + superio_select(W83627HF_LD_GPIO5); + + /* Make sure these GPIO pins are enabled */ + if (!(superio_inb(W83627THF_GPIO5_EN) & (1<<3))) { + dev_dbg(&client->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); + if ((sel & 0x1f) != 0x1f) { + dev_dbg(&client->dev, "GPIO5 not configured for VID " + "function\n"); + goto exit; + } + + dev_info(&client->dev, "Reading VID from GPIO5\n"); + res = superio_inb(W83627THF_GPIO5_DR) & sel; + +exit: + superio_exit(); + return res; +} + +static int w83627hf_write_value(struct i2c_client *client, u16 reg, u16 value) +{ + struct w83627hf_data *data = i2c_get_clientdata(client); + int word_sized; + + down(&data->lock); + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + } + outb_p(value & 0xff, + client->addr + W83781D_DATA_REG_OFFSET); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } + up(&data->lock); + return 0; +} + +/* Called when we have found a new W83781D. It should set limits, etc. */ +static void w83627hf_init_client(struct i2c_client *client) +{ + struct w83627hf_data *data = i2c_get_clientdata(client); + int i; + int type = data->type; + u8 tmp; + + if(init) { + /* save this register */ + i = w83627hf_read_value(client, W83781D_REG_BEEP_CONFIG); + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others */ + w83627hf_write_value(client, W83781D_REG_CONFIG, 0x80); + /* Restore the register and disable power-on abnormal beep. + This saves FAN 1/2/3 input/output values set by BIOS. */ + w83627hf_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80); + /* Disable master beep-enable (reset turns it on). + Individual beeps should be reset to off but for some reason + disabling this bit helps some people not get beeped */ + w83627hf_write_value(client, W83781D_REG_BEEP_INTS2, 0); + } + + /* Minimize conflicts with other winbond i2c-only clients... */ + /* disable i2c subclients... how to disable main i2c client?? */ + /* force i2c address to relatively uncommon address */ + w83627hf_write_value(client, W83781D_REG_I2C_SUBADDR, 0x89); + w83627hf_write_value(client, W83781D_REG_I2C_ADDR, force_i2c); + + /* Read VID only once */ + if (w83627hf == data->type || w83637hf == data->type) { + int lo = w83627hf_read_value(client, W83781D_REG_VID_FANDIV); + int hi = w83627hf_read_value(client, W83781D_REG_CHIPID); + data->vid = (lo & 0x0f) | ((hi & 0x01) << 4); + } else if (w83627thf == data->type) { + data->vid = w83627thf_read_gpio5(client) & 0x3f; + } + + /* Read VRM & OVT Config only once */ + if (w83627thf == data->type || w83637hf == data->type) { + data->vrm_ovt = + w83627hf_read_value(client, W83627THF_REG_VRM_OVT_CFG); + data->vrm = (data->vrm_ovt & 0x01) ? 90 : 82; + } else { + /* Convert VID to voltage based on default VRM */ + data->vrm = i2c_which_vrm(); + } + + tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); + for (i = 1; i <= 3; i++) { + if (!(tmp & BIT_SCFG1[i - 1])) { + data->sens[i - 1] = W83781D_DEFAULT_BETA; + } else { + if (w83627hf_read_value + (client, + W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) + data->sens[i - 1] = 1; + else + data->sens[i - 1] = 2; + } + if ((type == w83697hf) && (i == 2)) + break; + } + + if(init) { + /* Enable temp2 */ + tmp = w83627hf_read_value(client, W83781D_REG_TEMP2_CONFIG); + if (tmp & 0x01) { + dev_warn(&client->dev, "Enabling temp2, readings " + "might not make sense\n"); + w83627hf_write_value(client, W83781D_REG_TEMP2_CONFIG, + tmp & 0xfe); + } + + /* Enable temp3 */ + if (type != w83697hf) { + tmp = w83627hf_read_value(client, + W83781D_REG_TEMP3_CONFIG); + if (tmp & 0x01) { + dev_warn(&client->dev, "Enabling temp3, " + "readings might not make sense\n"); + w83627hf_write_value(client, + W83781D_REG_TEMP3_CONFIG, tmp & 0xfe); + } + } + + if (type == w83627hf) { + /* enable PWM2 control (can't hurt since PWM reg + should have been reset to 0xff) */ + w83627hf_write_value(client, W83627HF_REG_PWMCLK12, + 0x19); + } + /* enable comparator mode for temp2 and temp3 so + alarm indication will work correctly */ + i = w83627hf_read_value(client, W83781D_REG_IRQ); + if (!(i & 0x40)) + w83627hf_write_value(client, W83781D_REG_IRQ, + i | 0x40); + } + + /* Start monitoring */ + w83627hf_write_value(client, W83781D_REG_CONFIG, + (w83627hf_read_value(client, + W83781D_REG_CONFIG) & 0xf7) + | 0x01); +} + +static struct w83627hf_data *w83627hf_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83627hf_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + for (i = 0; i <= 8; i++) { + /* skip missing sensors */ + if (((data->type == w83697hf) && (i == 1)) || + ((data->type == w83627thf || data->type == w83637hf) + && (i == 4 || i == 5))) + continue; + data->in[i] = + w83627hf_read_value(client, W83781D_REG_IN(i)); + data->in_min[i] = + w83627hf_read_value(client, + W83781D_REG_IN_MIN(i)); + data->in_max[i] = + w83627hf_read_value(client, + W83781D_REG_IN_MAX(i)); + } + for (i = 1; i <= 3; i++) { + data->fan[i - 1] = + w83627hf_read_value(client, W83781D_REG_FAN(i)); + data->fan_min[i - 1] = + w83627hf_read_value(client, + W83781D_REG_FAN_MIN(i)); + } + for (i = 1; i <= 3; i++) { + u8 tmp = w83627hf_read_value(client, + W836X7HF_REG_PWM(data->type, i)); + /* bits 0-3 are reserved in 627THF */ + if (data->type == w83627thf) + tmp &= 0xf0; + data->pwm[i - 1] = tmp; + if(i == 2 && + (data->type == w83627hf || data->type == w83697hf)) + break; + } + + data->temp = w83627hf_read_value(client, W83781D_REG_TEMP(1)); + data->temp_max = + w83627hf_read_value(client, W83781D_REG_TEMP_OVER(1)); + data->temp_max_hyst = + w83627hf_read_value(client, W83781D_REG_TEMP_HYST(1)); + data->temp_add[0] = + w83627hf_read_value(client, W83781D_REG_TEMP(2)); + data->temp_max_add[0] = + w83627hf_read_value(client, W83781D_REG_TEMP_OVER(2)); + data->temp_max_hyst_add[0] = + w83627hf_read_value(client, W83781D_REG_TEMP_HYST(2)); + if (data->type != w83697hf) { + data->temp_add[1] = + w83627hf_read_value(client, W83781D_REG_TEMP(3)); + data->temp_max_add[1] = + w83627hf_read_value(client, W83781D_REG_TEMP_OVER(3)); + data->temp_max_hyst_add[1] = + w83627hf_read_value(client, W83781D_REG_TEMP_HYST(3)); + } + + i = w83627hf_read_value(client, W83781D_REG_VID_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + if (data->type != w83697hf) { + data->fan_div[2] = (w83627hf_read_value(client, + W83781D_REG_PIN) >> 6) & 0x03; + } + i = w83627hf_read_value(client, W83781D_REG_VBAT); + data->fan_div[0] |= (i >> 3) & 0x04; + data->fan_div[1] |= (i >> 4) & 0x04; + if (data->type != w83697hf) + data->fan_div[2] |= (i >> 5) & 0x04; + data->alarms = + w83627hf_read_value(client, W83781D_REG_ALARM1) | + (w83627hf_read_value(client, W83781D_REG_ALARM2) << 8) | + (w83627hf_read_value(client, W83781D_REG_ALARM3) << 16); + i = w83627hf_read_value(client, W83781D_REG_BEEP_INTS2); + data->beep_enable = i >> 7; + data->beep_mask = ((i & 0x7f) << 8) | + w83627hf_read_value(client, W83781D_REG_BEEP_INTS1) | + w83627hf_read_value(client, W83781D_REG_BEEP_INTS3) << 16; + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_w83627hf_init(void) +{ + int addr; + + if (w83627hf_find(0x2e, &addr) + && w83627hf_find(0x4e, &addr)) { + return -ENODEV; + } + normal_isa[0] = addr; + + return i2c_add_driver(&w83627hf_driver); +} + +static void __exit sensors_w83627hf_exit(void) +{ + i2c_del_driver(&w83627hf_driver); +} + +MODULE_AUTHOR("Frodo Looijaard , " + "Philip Edelbrock , " + "and Mark Studebaker "); +MODULE_DESCRIPTION("W83627HF driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_w83627hf_init); +module_exit(sensors_w83627hf_exit); diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c new file mode 100644 index 000000000000..0bb131ce09eb --- /dev/null +++ b/drivers/hwmon/w83781d.c @@ -0,0 +1,1632 @@ +/* + w83781d.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2001 Frodo Looijaard , + Philip Edelbrock , + and Mark Studebaker + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Supports following chips: + + Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + as99127f 7 3 0 3 0x31 0x12c3 yes no + as99127f rev.2 (type_name = as99127f) 0x31 0x5ca3 yes no + w83781d 7 3 0 3 0x10-1 0x5ca3 yes yes + w83627hf 9 3 2 3 0x21 0x5ca3 yes yes(LPC) + w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes + w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "lm75.h" + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_5(w83781d, w83782d, w83783s, w83627hf, as99127f); +I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " + "{bus, clientaddr, subclientaddr1, subclientaddr2}"); + +static int init = 1; +module_param(init, bool, 0); +MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); + +/* Constants specified below */ + +/* Length of ISA address segment */ +#define W83781D_EXTENT 8 + +/* Where are the ISA address/data registers relative to the base address */ +#define W83781D_ADDR_REG_OFFSET 5 +#define W83781D_DATA_REG_OFFSET 6 + +/* The W83781D registers */ +/* The W83782D registers for nr=7,8 are in bank 5 */ +#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ + (0x554 + (((nr) - 7) * 2))) +#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ + (0x555 + (((nr) - 7) * 2))) +#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ + (0x550 + (nr) - 7)) + +#define W83781D_REG_FAN_MIN(nr) (0x3a + (nr)) +#define W83781D_REG_FAN(nr) (0x27 + (nr)) + +#define W83781D_REG_BANK 0x4E +#define W83781D_REG_TEMP2_CONFIG 0x152 +#define W83781D_REG_TEMP3_CONFIG 0x252 +#define W83781D_REG_TEMP(nr) ((nr == 3) ? (0x0250) : \ + ((nr == 2) ? (0x0150) : \ + (0x27))) +#define W83781D_REG_TEMP_HYST(nr) ((nr == 3) ? (0x253) : \ + ((nr == 2) ? (0x153) : \ + (0x3A))) +#define W83781D_REG_TEMP_OVER(nr) ((nr == 3) ? (0x255) : \ + ((nr == 2) ? (0x155) : \ + (0x39))) + +#define W83781D_REG_CONFIG 0x40 +#define W83781D_REG_ALARM1 0x41 +#define W83781D_REG_ALARM2 0x42 +#define W83781D_REG_ALARM3 0x450 /* not on W83781D */ + +#define W83781D_REG_IRQ 0x4C +#define W83781D_REG_BEEP_CONFIG 0x4D +#define W83781D_REG_BEEP_INTS1 0x56 +#define W83781D_REG_BEEP_INTS2 0x57 +#define W83781D_REG_BEEP_INTS3 0x453 /* not on W83781D */ + +#define W83781D_REG_VID_FANDIV 0x47 + +#define W83781D_REG_CHIPID 0x49 +#define W83781D_REG_WCHIPID 0x58 +#define W83781D_REG_CHIPMAN 0x4F +#define W83781D_REG_PIN 0x4B + +/* 782D/783S only */ +#define W83781D_REG_VBAT 0x5D + +/* PWM 782D (1-4) and 783S (1-2) only */ +#define W83781D_REG_PWM1 0x5B /* 782d and 783s/627hf datasheets disagree */ + /* on which is which; */ +#define W83781D_REG_PWM2 0x5A /* We follow the 782d convention here, */ + /* However 782d is probably wrong. */ +#define W83781D_REG_PWM3 0x5E +#define W83781D_REG_PWM4 0x5F +#define W83781D_REG_PWMCLK12 0x5C +#define W83781D_REG_PWMCLK34 0x45C +static const u8 regpwm[] = { W83781D_REG_PWM1, W83781D_REG_PWM2, + W83781D_REG_PWM3, W83781D_REG_PWM4 +}; + +#define W83781D_REG_PWM(nr) (regpwm[(nr) - 1]) + +#define W83781D_REG_I2C_ADDR 0x48 +#define W83781D_REG_I2C_SUBADDR 0x4A + +/* The following are undocumented in the data sheets however we + received the information in an email from Winbond tech support */ +/* Sensor selection - not on 781d */ +#define W83781D_REG_SCFG1 0x5D +static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; + +#define W83781D_REG_SCFG2 0x59 +static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; + +#define W83781D_DEFAULT_BETA 3435 + +/* RT Table registers */ +#define W83781D_REG_RT_IDX 0x50 +#define W83781D_REG_RT_VAL 0x51 + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) +#define IN_FROM_REG(val) (((val) * 16) / 10) + +static inline u8 +FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); +} + +#define FAN_FROM_REG(val,div) ((val) == 0 ? -1 : \ + ((val) == 255 ? 0 : \ + 1350000 / ((val) * (div)))) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val) < 0 ? (val)+0x100*1000 \ + : (val)) / 1000, 0, 0xff)) +#define TEMP_FROM_REG(val) (((val) & 0x80 ? (val)-0x100 : (val)) * 1000) + +#define PWM_FROM_REG(val) (val) +#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255)) +#define BEEP_MASK_FROM_REG(val,type) ((type) == as99127f ? \ + (val) ^ 0x7fff : (val)) +#define BEEP_MASK_TO_REG(val,type) ((type) == as99127f ? \ + (~(val)) & 0x7fff : (val) & 0xffffff) + +#define BEEP_ENABLE_TO_REG(val) ((val) ? 1 : 0) +#define BEEP_ENABLE_FROM_REG(val) ((val) ? 1 : 0) + +#define DIV_FROM_REG(val) (1 << (val)) + +static inline u8 +DIV_TO_REG(long val, enum chips type) +{ + int i; + val = SENSORS_LIMIT(val, 1, + ((type == w83781d + || type == as99127f) ? 8 : 128)) >> 1; + for (i = 0; i < 7; i++) { + if (val == 0) + break; + val >>= 1; + } + return ((u8) i); +} + +/* There are some complications in a module like this. First off, W83781D chips + may be both present on the SMBus and the ISA bus, and we have to handle + those cases separately at some places. Second, there might be several + W83781D chips available (well, actually, that is probably never done; but + it is a clean illustration of how to handle a case like that). Finally, + a specific chip may be attached to *both* ISA and SMBus, and we would + not like to detect it double. Fortunately, in the case of the W83781D at + least, a register tells us what SMBus address we are on, so that helps + a bit - except if there could be more than one SMBus. Groan. No solution + for this yet. */ + +/* This module may seem overly long and complicated. In fact, it is not so + bad. Quite a lot of bookkeeping is done. A real driver can often cut + some corners. */ + +/* For each registered W83781D, we need to keep some data in memory. That + data is pointed to by w83781d_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new w83781d client is + allocated. */ +struct w83781d_data { + struct i2c_client client; + struct semaphore lock; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + struct i2c_client *lm75[2]; /* for secondary I2C addresses */ + /* array of 2 pointers to subclients */ + + u8 in[9]; /* Register value - 8 & 9 for 782D only */ + u8 in_max[9]; /* Register value - 8 & 9 for 782D only */ + u8 in_min[9]; /* Register value - 8 & 9 for 782D only */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 temp; + u8 temp_max; /* Register value */ + u8 temp_max_hyst; /* Register value */ + u16 temp_add[2]; /* Register value */ + u16 temp_max_add[2]; /* Register value */ + u16 temp_max_hyst_add[2]; /* Register value */ + u8 fan_div[3]; /* Register encoding, shifted right */ + u8 vid; /* Register encoding, combined */ + u32 alarms; /* Register encoding, combined */ + u32 beep_mask; /* Register encoding, combined */ + u8 beep_enable; /* Boolean */ + u8 pwm[4]; /* Register value */ + u8 pwmenable[4]; /* Boolean */ + u16 sens[3]; /* 782D/783S only. + 1 = pentium diode; 2 = 3904 diode; + 3000-5000 = thermistor beta. + Default = 3435. + Other Betas unimplemented */ + u8 vrm; +}; + +static int w83781d_attach_adapter(struct i2c_adapter *adapter); +static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind); +static int w83781d_detach_client(struct i2c_client *client); + +static int w83781d_read_value(struct i2c_client *client, u16 register); +static int w83781d_write_value(struct i2c_client *client, u16 register, + u16 value); +static struct w83781d_data *w83781d_update_device(struct device *dev); +static void w83781d_init_client(struct i2c_client *client); + +static struct i2c_driver w83781d_driver = { + .owner = THIS_MODULE, + .name = "w83781d", + .id = I2C_DRIVERID_W83781D, + .flags = I2C_DF_NOTIFY, + .attach_adapter = w83781d_attach_adapter, + .detach_client = w83781d_detach_client, +}; + +/* following are the sysfs callback functions */ +#define show_in_reg(reg) \ +static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ +{ \ + struct w83781d_data *data = w83781d_update_device(dev); \ + return sprintf(buf,"%ld\n", (long)IN_FROM_REG(data->reg[nr] * 10)); \ +} +show_in_reg(in); +show_in_reg(in_min); +show_in_reg(in_max); + +#define store_in_reg(REG, reg) \ +static ssize_t store_in_##reg (struct device *dev, const char *buf, size_t count, int nr) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83781d_data *data = i2c_get_clientdata(client); \ + u32 val; \ + \ + val = simple_strtoul(buf, NULL, 10) / 10; \ + \ + down(&data->update_lock); \ + data->in_##reg[nr] = IN_TO_REG(val); \ + w83781d_write_value(client, W83781D_REG_IN_##REG(nr), data->in_##reg[nr]); \ + \ + up(&data->update_lock); \ + return count; \ +} +store_in_reg(MIN, min); +store_in_reg(MAX, max); + +#define sysfs_in_offset(offset) \ +static ssize_t \ +show_regs_in_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in(dev, buf, offset); \ +} \ +static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_regs_in_##offset, NULL); + +#define sysfs_in_reg_offset(reg, offset) \ +static ssize_t show_regs_in_##reg##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_in_##reg (dev, buf, offset); \ +} \ +static ssize_t store_regs_in_##reg##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return store_in_##reg (dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(in##offset##_##reg, S_IRUGO| S_IWUSR, show_regs_in_##reg##offset, store_regs_in_##reg##offset); + +#define sysfs_in_offsets(offset) \ +sysfs_in_offset(offset); \ +sysfs_in_reg_offset(min, offset); \ +sysfs_in_reg_offset(max, offset); + +sysfs_in_offsets(0); +sysfs_in_offsets(1); +sysfs_in_offsets(2); +sysfs_in_offsets(3); +sysfs_in_offsets(4); +sysfs_in_offsets(5); +sysfs_in_offsets(6); +sysfs_in_offsets(7); +sysfs_in_offsets(8); + +#define device_create_file_in(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_in##offset##_input); \ +device_create_file(&client->dev, &dev_attr_in##offset##_min); \ +device_create_file(&client->dev, &dev_attr_in##offset##_max); \ +} while (0) + +#define show_fan_reg(reg) \ +static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ +{ \ + struct w83781d_data *data = w83781d_update_device(dev); \ + return sprintf(buf,"%ld\n", \ + FAN_FROM_REG(data->reg[nr-1], (long)DIV_FROM_REG(data->fan_div[nr-1]))); \ +} +show_fan_reg(fan); +show_fan_reg(fan_min); + +static ssize_t +store_fan_min(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->fan_min[nr - 1] = + FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr - 1])); + w83781d_write_value(client, W83781D_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + + up(&data->update_lock); + return count; +} + +#define sysfs_fan_offset(offset) \ +static ssize_t show_regs_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan(dev, buf, offset); \ +} \ +static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_regs_fan_##offset, NULL); + +#define sysfs_fan_min_offset(offset) \ +static ssize_t show_regs_fan_min##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_min(dev, buf, offset); \ +} \ +static ssize_t store_regs_fan_min##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return store_fan_min(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, show_regs_fan_min##offset, store_regs_fan_min##offset); + +sysfs_fan_offset(1); +sysfs_fan_min_offset(1); +sysfs_fan_offset(2); +sysfs_fan_min_offset(2); +sysfs_fan_offset(3); +sysfs_fan_min_offset(3); + +#define device_create_file_fan(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_fan##offset##_input); \ +device_create_file(&client->dev, &dev_attr_fan##offset##_min); \ +} while (0) + +#define show_temp_reg(reg) \ +static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ +{ \ + struct w83781d_data *data = w83781d_update_device(dev); \ + if (nr >= 2) { /* TEMP2 and TEMP3 */ \ + return sprintf(buf,"%d\n", \ + LM75_TEMP_FROM_REG(data->reg##_add[nr-2])); \ + } else { /* TEMP1 */ \ + return sprintf(buf,"%ld\n", (long)TEMP_FROM_REG(data->reg)); \ + } \ +} +show_temp_reg(temp); +show_temp_reg(temp_max); +show_temp_reg(temp_max_hyst); + +#define store_temp_reg(REG, reg) \ +static ssize_t store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct w83781d_data *data = i2c_get_clientdata(client); \ + s32 val; \ + \ + val = simple_strtol(buf, NULL, 10); \ + \ + down(&data->update_lock); \ + \ + if (nr >= 2) { /* TEMP2 and TEMP3 */ \ + data->temp_##reg##_add[nr-2] = LM75_TEMP_TO_REG(val); \ + w83781d_write_value(client, W83781D_REG_TEMP_##REG(nr), \ + data->temp_##reg##_add[nr-2]); \ + } else { /* TEMP1 */ \ + data->temp_##reg = TEMP_TO_REG(val); \ + w83781d_write_value(client, W83781D_REG_TEMP_##REG(nr), \ + data->temp_##reg); \ + } \ + \ + up(&data->update_lock); \ + return count; \ +} +store_temp_reg(OVER, max); +store_temp_reg(HYST, max_hyst); + +#define sysfs_temp_offset(offset) \ +static ssize_t \ +show_regs_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp(dev, buf, offset); \ +} \ +static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_regs_temp_##offset, NULL); + +#define sysfs_temp_reg_offset(reg, offset) \ +static ssize_t show_regs_temp_##reg##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_temp_##reg (dev, buf, offset); \ +} \ +static ssize_t store_regs_temp_##reg##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return store_temp_##reg (dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(temp##offset##_##reg, S_IRUGO| S_IWUSR, show_regs_temp_##reg##offset, store_regs_temp_##reg##offset); + +#define sysfs_temp_offsets(offset) \ +sysfs_temp_offset(offset); \ +sysfs_temp_reg_offset(max, offset); \ +sysfs_temp_reg_offset(max_hyst, offset); + +sysfs_temp_offsets(1); +sysfs_temp_offsets(2); +sysfs_temp_offsets(3); + +#define device_create_file_temp(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_temp##offset##_input); \ +device_create_file(&client->dev, &dev_attr_temp##offset##_max); \ +device_create_file(&client->dev, &dev_attr_temp##offset##_max_hyst); \ +} while (0) + +static ssize_t +show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83781d_data *data = w83781d_update_device(dev); + return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); +} + +static +DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); +#define device_create_file_vid(client) \ +device_create_file(&client->dev, &dev_attr_cpu0_vid); +static ssize_t +show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83781d_data *data = w83781d_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->vrm); +} + +static ssize_t +store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + data->vrm = val; + + return count; +} + +static +DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); +#define device_create_file_vrm(client) \ +device_create_file(&client->dev, &dev_attr_vrm); +static ssize_t +show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83781d_data *data = w83781d_update_device(dev); + return sprintf(buf, "%u\n", data->alarms); +} + +static +DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); +#define device_create_file_alarms(client) \ +device_create_file(&client->dev, &dev_attr_alarms); +static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83781d_data *data = w83781d_update_device(dev); + return sprintf(buf, "%ld\n", + (long)BEEP_MASK_FROM_REG(data->beep_mask, data->type)); +} +static ssize_t show_beep_enable (struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83781d_data *data = w83781d_update_device(dev); + return sprintf(buf, "%ld\n", + (long)BEEP_ENABLE_FROM_REG(data->beep_enable)); +} + +#define BEEP_ENABLE 0 /* Store beep_enable */ +#define BEEP_MASK 1 /* Store beep_mask */ + +static ssize_t +store_beep_reg(struct device *dev, const char *buf, size_t count, + int update_mask) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val, val2; + + val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + + if (update_mask == BEEP_MASK) { /* We are storing beep_mask */ + data->beep_mask = BEEP_MASK_TO_REG(val, data->type); + w83781d_write_value(client, W83781D_REG_BEEP_INTS1, + data->beep_mask & 0xff); + + if ((data->type != w83781d) && (data->type != as99127f)) { + w83781d_write_value(client, W83781D_REG_BEEP_INTS3, + ((data->beep_mask) >> 16) & 0xff); + } + + val2 = (data->beep_mask >> 8) & 0x7f; + } else { /* We are storing beep_enable */ + val2 = w83781d_read_value(client, W83781D_REG_BEEP_INTS2) & 0x7f; + data->beep_enable = BEEP_ENABLE_TO_REG(val); + } + + w83781d_write_value(client, W83781D_REG_BEEP_INTS2, + val2 | data->beep_enable << 7); + + up(&data->update_lock); + return count; +} + +#define sysfs_beep(REG, reg) \ +static ssize_t show_regs_beep_##reg (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_beep_##reg(dev, attr, buf); \ +} \ +static ssize_t store_regs_beep_##reg (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return store_beep_reg(dev, buf, count, BEEP_##REG); \ +} \ +static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, show_regs_beep_##reg, store_regs_beep_##reg); + +sysfs_beep(ENABLE, enable); +sysfs_beep(MASK, mask); + +#define device_create_file_beep(client) \ +do { \ +device_create_file(&client->dev, &dev_attr_beep_enable); \ +device_create_file(&client->dev, &dev_attr_beep_mask); \ +} while (0) + +static ssize_t +show_fan_div_reg(struct device *dev, char *buf, int nr) +{ + struct w83781d_data *data = w83781d_update_device(dev); + return sprintf(buf, "%ld\n", + (long) DIV_FROM_REG(data->fan_div[nr - 1])); +} + +/* Note: we save and restore the fan minimum here, because its value is + determined in part by the fan divisor. This follows the principle of + least suprise; the user doesn't expect the fan minimum to change just + because the divisor changed. */ +static ssize_t +store_fan_div_reg(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + unsigned long min; + u8 reg; + unsigned long val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + + /* Save fan_min */ + min = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + + data->fan_div[nr] = DIV_TO_REG(val, data->type); + + reg = (w83781d_read_value(client, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV) + & (nr==0 ? 0xcf : 0x3f)) + | ((data->fan_div[nr] & 0x03) << (nr==0 ? 4 : 6)); + w83781d_write_value(client, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg); + + /* w83781d and as99127f don't have extended divisor bits */ + if (data->type != w83781d && data->type != as99127f) { + reg = (w83781d_read_value(client, W83781D_REG_VBAT) + & ~(1 << (5 + nr))) + | ((data->fan_div[nr] & 0x04) << (3 + nr)); + w83781d_write_value(client, W83781D_REG_VBAT, reg); + } + + /* Restore fan_min */ + data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); + w83781d_write_value(client, W83781D_REG_FAN_MIN(nr+1), data->fan_min[nr]); + + up(&data->update_lock); + return count; +} + +#define sysfs_fan_div(offset) \ +static ssize_t show_regs_fan_div_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_fan_div_reg(dev, buf, offset); \ +} \ +static ssize_t store_regs_fan_div_##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return store_fan_div_reg(dev, buf, count, offset - 1); \ +} \ +static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, show_regs_fan_div_##offset, store_regs_fan_div_##offset); + +sysfs_fan_div(1); +sysfs_fan_div(2); +sysfs_fan_div(3); + +#define device_create_file_fan_div(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_fan##offset##_div); \ +} while (0) + +static ssize_t +show_pwm_reg(struct device *dev, char *buf, int nr) +{ + struct w83781d_data *data = w83781d_update_device(dev); + return sprintf(buf, "%ld\n", (long) PWM_FROM_REG(data->pwm[nr - 1])); +} + +static ssize_t +show_pwmenable_reg(struct device *dev, char *buf, int nr) +{ + struct w83781d_data *data = w83781d_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->pwmenable[nr - 1]); +} + +static ssize_t +store_pwm_reg(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val; + + val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + data->pwm[nr - 1] = PWM_TO_REG(val); + w83781d_write_value(client, W83781D_REG_PWM(nr), data->pwm[nr - 1]); + up(&data->update_lock); + return count; +} + +static ssize_t +store_pwmenable_reg(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val, reg; + + val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + + switch (val) { + case 0: + case 1: + reg = w83781d_read_value(client, W83781D_REG_PWMCLK12); + w83781d_write_value(client, W83781D_REG_PWMCLK12, + (reg & 0xf7) | (val << 3)); + + reg = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG); + w83781d_write_value(client, W83781D_REG_BEEP_CONFIG, + (reg & 0xef) | (!val << 4)); + + data->pwmenable[nr - 1] = val; + break; + + default: + up(&data->update_lock); + return -EINVAL; + } + + up(&data->update_lock); + return count; +} + +#define sysfs_pwm(offset) \ +static ssize_t show_regs_pwm_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_pwm_reg(dev, buf, offset); \ +} \ +static ssize_t store_regs_pwm_##offset (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return store_pwm_reg(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ + show_regs_pwm_##offset, store_regs_pwm_##offset); + +#define sysfs_pwmenable(offset) \ +static ssize_t show_regs_pwmenable_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_pwmenable_reg(dev, buf, offset); \ +} \ +static ssize_t store_regs_pwmenable_##offset (struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + return store_pwmenable_reg(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ + show_regs_pwmenable_##offset, store_regs_pwmenable_##offset); + +sysfs_pwm(1); +sysfs_pwm(2); +sysfs_pwmenable(2); /* only PWM2 can be enabled/disabled */ +sysfs_pwm(3); +sysfs_pwm(4); + +#define device_create_file_pwm(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_pwm##offset); \ +} while (0) + +#define device_create_file_pwmenable(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_pwm##offset##_enable); \ +} while (0) + +static ssize_t +show_sensor_reg(struct device *dev, char *buf, int nr) +{ + struct w83781d_data *data = w83781d_update_device(dev); + return sprintf(buf, "%ld\n", (long) data->sens[nr - 1]); +} + +static ssize_t +store_sensor_reg(struct device *dev, const char *buf, size_t count, int nr) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + u32 val, tmp; + + val = simple_strtoul(buf, NULL, 10); + + down(&data->update_lock); + + switch (val) { + case 1: /* PII/Celeron diode */ + tmp = w83781d_read_value(client, W83781D_REG_SCFG1); + w83781d_write_value(client, W83781D_REG_SCFG1, + tmp | BIT_SCFG1[nr - 1]); + tmp = w83781d_read_value(client, W83781D_REG_SCFG2); + w83781d_write_value(client, W83781D_REG_SCFG2, + tmp | BIT_SCFG2[nr - 1]); + data->sens[nr - 1] = val; + break; + case 2: /* 3904 */ + tmp = w83781d_read_value(client, W83781D_REG_SCFG1); + w83781d_write_value(client, W83781D_REG_SCFG1, + tmp | BIT_SCFG1[nr - 1]); + tmp = w83781d_read_value(client, W83781D_REG_SCFG2); + w83781d_write_value(client, W83781D_REG_SCFG2, + tmp & ~BIT_SCFG2[nr - 1]); + data->sens[nr - 1] = val; + break; + case W83781D_DEFAULT_BETA: /* thermistor */ + tmp = w83781d_read_value(client, W83781D_REG_SCFG1); + w83781d_write_value(client, W83781D_REG_SCFG1, + tmp & ~BIT_SCFG1[nr - 1]); + data->sens[nr - 1] = val; + break; + default: + dev_err(dev, "Invalid sensor type %ld; must be 1, 2, or %d\n", + (long) val, W83781D_DEFAULT_BETA); + break; + } + + up(&data->update_lock); + return count; +} + +#define sysfs_sensor(offset) \ +static ssize_t show_regs_sensor_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ +{ \ + return show_sensor_reg(dev, buf, offset); \ +} \ +static ssize_t store_regs_sensor_##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ +{ \ + return store_sensor_reg(dev, buf, count, offset); \ +} \ +static DEVICE_ATTR(temp##offset##_type, S_IRUGO | S_IWUSR, show_regs_sensor_##offset, store_regs_sensor_##offset); + +sysfs_sensor(1); +sysfs_sensor(2); +sysfs_sensor(3); + +#define device_create_file_sensor(client, offset) \ +do { \ +device_create_file(&client->dev, &dev_attr_temp##offset##_type); \ +} while (0) + +/* This function is called when: + * w83781d_driver is inserted (when this module is loaded), for each + available adapter + * when a new adapter is inserted (and w83781d_driver is still present) */ +static int +w83781d_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, w83781d_detect); +} + +/* Assumes that adapter is of I2C, not ISA variety. + * OTHERWISE DON'T CALL THIS + */ +static int +w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, + struct i2c_client *new_client) +{ + int i, val1 = 0, id; + int err; + const char *client_name = ""; + struct w83781d_data *data = i2c_get_clientdata(new_client); + + data->lm75[0] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!(data->lm75[0])) { + err = -ENOMEM; + goto ERROR_SC_0; + } + memset(data->lm75[0], 0x00, sizeof (struct i2c_client)); + + id = i2c_adapter_id(adapter); + + if (force_subclients[0] == id && force_subclients[1] == address) { + for (i = 2; i <= 3; i++) { + if (force_subclients[i] < 0x48 || + force_subclients[i] > 0x4f) { + dev_err(&new_client->dev, "Invalid subclient " + "address %d; must be 0x48-0x4f\n", + force_subclients[i]); + err = -EINVAL; + goto ERROR_SC_1; + } + } + w83781d_write_value(new_client, W83781D_REG_I2C_SUBADDR, + (force_subclients[2] & 0x07) | + ((force_subclients[3] & 0x07) << 4)); + data->lm75[0]->addr = force_subclients[2]; + } else { + val1 = w83781d_read_value(new_client, W83781D_REG_I2C_SUBADDR); + data->lm75[0]->addr = 0x48 + (val1 & 0x07); + } + + if (kind != w83783s) { + + data->lm75[1] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!(data->lm75[1])) { + err = -ENOMEM; + goto ERROR_SC_1; + } + memset(data->lm75[1], 0x0, sizeof(struct i2c_client)); + + if (force_subclients[0] == id && + force_subclients[1] == address) { + data->lm75[1]->addr = force_subclients[3]; + } else { + data->lm75[1]->addr = 0x48 + ((val1 >> 4) & 0x07); + } + if (data->lm75[0]->addr == data->lm75[1]->addr) { + dev_err(&new_client->dev, + "Duplicate addresses 0x%x for subclients.\n", + data->lm75[0]->addr); + err = -EBUSY; + goto ERROR_SC_2; + } + } + + if (kind == w83781d) + client_name = "w83781d subclient"; + else if (kind == w83782d) + client_name = "w83782d subclient"; + else if (kind == w83783s) + client_name = "w83783s subclient"; + else if (kind == w83627hf) + client_name = "w83627hf subclient"; + else if (kind == as99127f) + client_name = "as99127f subclient"; + + for (i = 0; i <= 1; i++) { + /* store all data in w83781d */ + i2c_set_clientdata(data->lm75[i], NULL); + data->lm75[i]->adapter = adapter; + data->lm75[i]->driver = &w83781d_driver; + data->lm75[i]->flags = 0; + strlcpy(data->lm75[i]->name, client_name, + I2C_NAME_SIZE); + if ((err = i2c_attach_client(data->lm75[i]))) { + dev_err(&new_client->dev, "Subclient %d " + "registration at address 0x%x " + "failed.\n", i, data->lm75[i]->addr); + if (i == 1) + goto ERROR_SC_3; + goto ERROR_SC_2; + } + if (kind == w83783s) + break; + } + + return 0; + +/* Undo inits in case of errors */ +ERROR_SC_3: + i2c_detach_client(data->lm75[0]); +ERROR_SC_2: + if (NULL != data->lm75[1]) + kfree(data->lm75[1]); +ERROR_SC_1: + if (NULL != data->lm75[0]) + kfree(data->lm75[0]); +ERROR_SC_0: + return err; +} + +static int +w83781d_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int i = 0, val1 = 0, val2; + struct i2c_client *new_client; + struct w83781d_data *data; + int err; + const char *client_name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + enum vendor { winbond, asus } vendid; + + if (!is_isa + && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + err = -EINVAL; + goto ERROR0; + } + + /* Prevent users from forcing a kind for a bus it isn't supposed + to possibly be on */ + if (is_isa && (kind == as99127f || kind == w83783s)) { + dev_err(&adapter->dev, + "Cannot force I2C-only chip for ISA address 0x%02x.\n", + address); + err = -EINVAL; + goto ERROR0; + } + + if (is_isa) + if (!request_region(address, W83781D_EXTENT, + w83781d_driver.name)) { + dev_dbg(&adapter->dev, "Request of region " + "0x%x-0x%x for w83781d failed\n", address, + address + W83781D_EXTENT - 1); + err = -EBUSY; + goto ERROR0; + } + + /* Probe whether there is anything available on this address. Already + done for SMBus clients */ + if (kind < 0) { + if (is_isa) { + +#define REALLY_SLOW_IO + /* We need the timeouts for at least some LM78-like + chips. But only if we read 'undefined' registers. */ + i = inb_p(address + 1); + if (inb_p(address + 2) != i + || inb_p(address + 3) != i + || inb_p(address + 7) != i) { + dev_dbg(&adapter->dev, "Detection of w83781d " + "chip failed at step 1\n"); + err = -ENODEV; + goto ERROR1; + } +#undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f, address + 5); + val2 = inb_p(address + 5) & 0x7f; + if (val2 != (~i & 0x7f)) { + outb_p(i, address + 5); + dev_dbg(&adapter->dev, "Detection of w83781d " + "chip failed at step 2 (0x%x != " + "0x%x at 0x%x)\n", val2, ~i & 0x7f, + address + 5); + err = -ENODEV; + goto ERROR1; + } + } + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access w83781d_{read,write}_value. */ + + if (!(data = kmalloc(sizeof(struct w83781d_data), GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR1; + } + memset(data, 0, sizeof(struct w83781d_data)); + + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + init_MUTEX(&data->lock); + new_client->adapter = adapter; + new_client->driver = &w83781d_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + /* 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 (w83781d_read_value(new_client, W83781D_REG_CONFIG) & 0x80) { + dev_dbg(&new_client->dev, "Detection failed at step " + "3\n"); + err = -ENODEV; + goto ERROR2; + } + val1 = w83781d_read_value(new_client, W83781D_REG_BANK); + val2 = w83781d_read_value(new_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(&new_client->dev, "Detection failed at step " + "4\n"); + err = -ENODEV; + goto ERROR2; + } + /* If Winbond SMBus, check address at 0x48. + Asus doesn't support, except for as99127f rev.2 */ + if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) || + ((val1 & 0x80) && (val2 == 0x5c)))) { + if (w83781d_read_value + (new_client, W83781D_REG_I2C_ADDR) != address) { + dev_dbg(&new_client->dev, "Detection failed " + "at step 5\n"); + err = -ENODEV; + goto ERROR2; + } + } + } + + /* 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 */ + w83781d_write_value(new_client, W83781D_REG_BANK, + (w83781d_read_value(new_client, + W83781D_REG_BANK) & 0x78) | + 0x80); + + /* Determine the chip type. */ + if (kind <= 0) { + /* get vendor ID */ + val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN); + if (val2 == 0x5c) + vendid = winbond; + else if (val2 == 0x12) + vendid = asus; + else { + dev_dbg(&new_client->dev, "Chip was made by neither " + "Winbond nor Asus?\n"); + err = -ENODEV; + goto ERROR2; + } + + val1 = w83781d_read_value(new_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 && !is_isa + && address == 0x2d) + kind = w83783s; + else if (val1 == 0x21 && vendid == winbond) + kind = w83627hf; + else if (val1 == 0x31 && !is_isa && address >= 0x28) + kind = as99127f; + else { + if (kind == 0) + dev_warn(&new_client->dev, "Ignoring 'force' " + "parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + err = -EINVAL; + goto ERROR2; + } + } + + if (kind == w83781d) { + client_name = "w83781d"; + } else if (kind == w83782d) { + client_name = "w83782d"; + } else if (kind == w83783s) { + client_name = "w83783s"; + } else if (kind == w83627hf) { + client_name = "w83627hf"; + } else if (kind == as99127f) { + client_name = "as99127f"; + } + + /* Fill in the remaining client fields and put into the global list */ + strlcpy(new_client->name, client_name, I2C_NAME_SIZE); + data->type = kind; + + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR2; + + /* attach secondary i2c lm75-like clients */ + if (!is_isa) { + if ((err = w83781d_detect_subclients(adapter, address, + kind, new_client))) + goto ERROR3; + } else { + data->lm75[0] = NULL; + data->lm75[1] = NULL; + } + + /* Initialize the chip */ + w83781d_init_client(new_client); + + /* A few vars need to be filled upon startup */ + for (i = 1; i <= 3; i++) { + data->fan_min[i - 1] = w83781d_read_value(new_client, + W83781D_REG_FAN_MIN(i)); + } + if (kind != w83781d && kind != as99127f) + for (i = 0; i < 4; i++) + data->pwmenable[i] = 1; + + /* Register sysfs hooks */ + device_create_file_in(new_client, 0); + if (kind != w83783s) + device_create_file_in(new_client, 1); + device_create_file_in(new_client, 2); + device_create_file_in(new_client, 3); + device_create_file_in(new_client, 4); + device_create_file_in(new_client, 5); + device_create_file_in(new_client, 6); + if (kind != as99127f && kind != w83781d && kind != w83783s) { + device_create_file_in(new_client, 7); + device_create_file_in(new_client, 8); + } + + device_create_file_fan(new_client, 1); + device_create_file_fan(new_client, 2); + device_create_file_fan(new_client, 3); + + device_create_file_temp(new_client, 1); + device_create_file_temp(new_client, 2); + if (kind != w83783s) + device_create_file_temp(new_client, 3); + + device_create_file_vid(new_client); + device_create_file_vrm(new_client); + + device_create_file_fan_div(new_client, 1); + device_create_file_fan_div(new_client, 2); + device_create_file_fan_div(new_client, 3); + + device_create_file_alarms(new_client); + + device_create_file_beep(new_client); + + if (kind != w83781d && kind != as99127f) { + device_create_file_pwm(new_client, 1); + device_create_file_pwm(new_client, 2); + device_create_file_pwmenable(new_client, 2); + } + if (kind == w83782d && !is_isa) { + device_create_file_pwm(new_client, 3); + device_create_file_pwm(new_client, 4); + } + + if (kind != as99127f && kind != w83781d) { + device_create_file_sensor(new_client, 1); + device_create_file_sensor(new_client, 2); + if (kind != w83783s) + device_create_file_sensor(new_client, 3); + } + + return 0; + +ERROR3: + i2c_detach_client(new_client); +ERROR2: + kfree(data); +ERROR1: + if (is_isa) + release_region(address, W83781D_EXTENT); +ERROR0: + return err; +} + +static int +w83781d_detach_client(struct i2c_client *client) +{ + int err; + + if (i2c_is_isa_client(client)) + release_region(client->addr, W83781D_EXTENT); + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, + "Client deregistration failed, client not detached.\n"); + return err; + } + + if (i2c_get_clientdata(client)==NULL) { + /* subclients */ + kfree(client); + } else { + /* main client */ + kfree(i2c_get_clientdata(client)); + } + + return 0; +} + +/* The SMBus locks itself, usually, but nothing may access the Winbond between + bank switches. ISA access must always be locked explicitly! + We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, + would slow down the W83781D access and should not be necessary. + There are some ugly typecasts here, but the good news is - they should + nowhere else be necessary! */ +static int +w83781d_read_value(struct i2c_client *client, u16 reg) +{ + struct w83781d_data *data = i2c_get_clientdata(client); + int res, word_sized, bank; + struct i2c_client *cl; + + down(&data->lock); + if (i2c_is_isa_client(client)) { + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x50) + || ((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + res = + (res << 8) + inb_p(client->addr + + W83781D_DATA_REG_OFFSET); + } + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } + } else { + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + res = i2c_smbus_read_byte_data(client, reg & 0xff); + } else { + /* switch to subclient */ + cl = data->lm75[bank - 1]; + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x50: /* TEMP */ + res = swab16(i2c_smbus_read_word_data(cl, 0)); + break; + case 0x52: /* CONFIG */ + res = i2c_smbus_read_byte_data(cl, 1); + break; + case 0x53: /* HYST */ + res = swab16(i2c_smbus_read_word_data(cl, 2)); + break; + case 0x55: /* OVER */ + default: + res = swab16(i2c_smbus_read_word_data(cl, 3)); + break; + } + } + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); + } + up(&data->lock); + return res; +} + +static int +w83781d_write_value(struct i2c_client *client, u16 reg, u16 value) +{ + struct w83781d_data *data = i2c_get_clientdata(client); + int word_sized, bank; + struct i2c_client *cl; + + down(&data->lock); + if (i2c_is_isa_client(client)) { + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + } + outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } + } else { + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + i2c_smbus_write_byte_data(client, reg & 0xff, + value & 0xff); + } else { + /* switch to subclient */ + cl = data->lm75[bank - 1]; + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x52: /* CONFIG */ + i2c_smbus_write_byte_data(cl, 1, value & 0xff); + break; + case 0x53: /* HYST */ + i2c_smbus_write_word_data(cl, 2, swab16(value)); + break; + case 0x55: /* OVER */ + i2c_smbus_write_word_data(cl, 3, swab16(value)); + break; + } + } + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); + } + up(&data->lock); + return 0; +} + +/* Called when we have found a new W83781D. It should set limits, etc. */ +static void +w83781d_init_client(struct i2c_client *client) +{ + struct w83781d_data *data = i2c_get_clientdata(client); + int i, p; + int type = data->type; + u8 tmp; + + if (init && type != as99127f) { /* this resets registers we don't have + documentation for on the as99127f */ + /* save these registers */ + i = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG); + p = w83781d_read_value(client, W83781D_REG_PWMCLK12); + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others */ + w83781d_write_value(client, W83781D_REG_CONFIG, 0x80); + /* Restore the registers and disable power-on abnormal beep. + This saves FAN 1/2/3 input/output values set by BIOS. */ + w83781d_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80); + w83781d_write_value(client, W83781D_REG_PWMCLK12, p); + /* Disable master beep-enable (reset turns it on). + Individual beep_mask should be reset to off but for some reason + disabling this bit helps some people not get beeped */ + w83781d_write_value(client, W83781D_REG_BEEP_INTS2, 0); + } + + data->vrm = i2c_which_vrm(); + + if ((type != w83781d) && (type != as99127f)) { + tmp = w83781d_read_value(client, W83781D_REG_SCFG1); + for (i = 1; i <= 3; i++) { + if (!(tmp & BIT_SCFG1[i - 1])) { + data->sens[i - 1] = W83781D_DEFAULT_BETA; + } else { + if (w83781d_read_value + (client, + W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) + data->sens[i - 1] = 1; + else + data->sens[i - 1] = 2; + } + if (type == w83783s && i == 2) + break; + } + } + + if (init && type != as99127f) { + /* Enable temp2 */ + tmp = w83781d_read_value(client, W83781D_REG_TEMP2_CONFIG); + if (tmp & 0x01) { + dev_warn(&client->dev, "Enabling temp2, readings " + "might not make sense\n"); + w83781d_write_value(client, W83781D_REG_TEMP2_CONFIG, + tmp & 0xfe); + } + + /* Enable temp3 */ + if (type != w83783s) { + tmp = w83781d_read_value(client, + W83781D_REG_TEMP3_CONFIG); + if (tmp & 0x01) { + dev_warn(&client->dev, "Enabling temp3, " + "readings might not make sense\n"); + w83781d_write_value(client, + W83781D_REG_TEMP3_CONFIG, tmp & 0xfe); + } + } + + if (type != w83781d) { + /* enable comparator mode for temp2 and temp3 so + alarm indication will work correctly */ + i = w83781d_read_value(client, W83781D_REG_IRQ); + if (!(i & 0x40)) + w83781d_write_value(client, W83781D_REG_IRQ, + i | 0x40); + } + } + + /* Start monitoring */ + w83781d_write_value(client, W83781D_REG_CONFIG, + (w83781d_read_value(client, + W83781D_REG_CONFIG) & 0xf7) + | 0x01); +} + +static struct w83781d_data *w83781d_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83781d_data *data = i2c_get_clientdata(client); + int i; + + down(&data->update_lock); + + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) + || !data->valid) { + dev_dbg(dev, "Starting device update\n"); + + for (i = 0; i <= 8; i++) { + if (data->type == w83783s && i == 1) + continue; /* 783S has no in1 */ + data->in[i] = + w83781d_read_value(client, W83781D_REG_IN(i)); + data->in_min[i] = + w83781d_read_value(client, W83781D_REG_IN_MIN(i)); + data->in_max[i] = + w83781d_read_value(client, W83781D_REG_IN_MAX(i)); + if ((data->type != w83782d) + && (data->type != w83627hf) && (i == 6)) + break; + } + for (i = 1; i <= 3; i++) { + data->fan[i - 1] = + w83781d_read_value(client, W83781D_REG_FAN(i)); + data->fan_min[i - 1] = + w83781d_read_value(client, W83781D_REG_FAN_MIN(i)); + } + if (data->type != w83781d && data->type != as99127f) { + for (i = 1; i <= 4; i++) { + data->pwm[i - 1] = + w83781d_read_value(client, + W83781D_REG_PWM(i)); + if ((data->type != w83782d + || i2c_is_isa_client(client)) + && i == 2) + break; + } + /* Only PWM2 can be disabled */ + data->pwmenable[1] = (w83781d_read_value(client, + W83781D_REG_PWMCLK12) & 0x08) >> 3; + } + + data->temp = w83781d_read_value(client, W83781D_REG_TEMP(1)); + data->temp_max = + w83781d_read_value(client, W83781D_REG_TEMP_OVER(1)); + data->temp_max_hyst = + w83781d_read_value(client, W83781D_REG_TEMP_HYST(1)); + data->temp_add[0] = + w83781d_read_value(client, W83781D_REG_TEMP(2)); + data->temp_max_add[0] = + w83781d_read_value(client, W83781D_REG_TEMP_OVER(2)); + data->temp_max_hyst_add[0] = + w83781d_read_value(client, W83781D_REG_TEMP_HYST(2)); + if (data->type != w83783s) { + data->temp_add[1] = + w83781d_read_value(client, W83781D_REG_TEMP(3)); + data->temp_max_add[1] = + w83781d_read_value(client, + W83781D_REG_TEMP_OVER(3)); + data->temp_max_hyst_add[1] = + w83781d_read_value(client, + W83781D_REG_TEMP_HYST(3)); + } + i = w83781d_read_value(client, W83781D_REG_VID_FANDIV); + data->vid = i & 0x0f; + data->vid |= (w83781d_read_value(client, + W83781D_REG_CHIPID) & 0x01) << 4; + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + data->fan_div[2] = (w83781d_read_value(client, + W83781D_REG_PIN) >> 6) & 0x03; + if ((data->type != w83781d) && (data->type != as99127f)) { + i = w83781d_read_value(client, W83781D_REG_VBAT); + data->fan_div[0] |= (i >> 3) & 0x04; + data->fan_div[1] |= (i >> 4) & 0x04; + data->fan_div[2] |= (i >> 5) & 0x04; + } + data->alarms = + w83781d_read_value(client, + W83781D_REG_ALARM1) + + (w83781d_read_value(client, W83781D_REG_ALARM2) << 8); + if ((data->type == w83782d) || (data->type == w83627hf)) { + data->alarms |= + w83781d_read_value(client, + W83781D_REG_ALARM3) << 16; + } + i = w83781d_read_value(client, W83781D_REG_BEEP_INTS2); + data->beep_enable = i >> 7; + data->beep_mask = ((i & 0x7f) << 8) + + w83781d_read_value(client, W83781D_REG_BEEP_INTS1); + if ((data->type != w83781d) && (data->type != as99127f)) { + data->beep_mask |= + w83781d_read_value(client, + W83781D_REG_BEEP_INTS3) << 16; + } + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init +sensors_w83781d_init(void) +{ + return i2c_add_driver(&w83781d_driver); +} + +static void __exit +sensors_w83781d_exit(void) +{ + i2c_del_driver(&w83781d_driver); +} + +MODULE_AUTHOR("Frodo Looijaard , " + "Philip Edelbrock , " + "and Mark Studebaker "); +MODULE_DESCRIPTION("W83781D driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_w83781d_init); +module_exit(sensors_w83781d_exit); diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c new file mode 100644 index 000000000000..4469d52aba4c --- /dev/null +++ b/drivers/hwmon/w83l785ts.c @@ -0,0 +1,328 @@ +/* + * w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 2003-2004 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 + * resolution and a 3 deg accuracy. Datasheet can be obtained from + * Winbond's website at: + * http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf + * + * Ported to Linux 2.6 by Wolfgang Ziegler and Jean Delvare + * . + * + * Thanks to James Bolt for benchmarking the read + * error handling mechanism. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* How many retries on register read error */ +#define MAX_RETRIES 5 + +/* + * Address to scan + * Address is fully defined internally and cannot be changed. + */ + +static unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; + +/* + * Insmod parameters + */ + +SENSORS_INSMOD_1(w83l785ts); + +/* + * The W83L785TS-S registers + * Manufacturer ID is 0x5CA3 for Winbond. + */ + +#define W83L785TS_REG_MAN_ID1 0x4D +#define W83L785TS_REG_MAN_ID2 0x4C +#define W83L785TS_REG_CHIP_ID 0x4E +#define W83L785TS_REG_CONFIG 0x40 +#define W83L785TS_REG_TYPE 0x52 +#define W83L785TS_REG_TEMP 0x27 +#define W83L785TS_REG_TEMP_OVER 0x53 /* not sure about this one */ + +/* + * Conversions + * The W83L785TS-S uses signed 8-bit values. + */ + +#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000) + +/* + * Functions declaration + */ + +static int w83l785ts_attach_adapter(struct i2c_adapter *adapter); +static int w83l785ts_detect(struct i2c_adapter *adapter, int address, + int kind); +static int w83l785ts_detach_client(struct i2c_client *client); +static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval); +static struct w83l785ts_data *w83l785ts_update_device(struct device *dev); + +/* + * Driver data (common to all clients) + */ + +static struct i2c_driver w83l785ts_driver = { + .owner = THIS_MODULE, + .name = "w83l785ts", + .id = I2C_DRIVERID_W83L785TS, + .flags = I2C_DF_NOTIFY, + .attach_adapter = w83l785ts_attach_adapter, + .detach_client = w83l785ts_detach_client, +}; + +/* + * Client data (each client gets its own) + */ + +struct w83l785ts_data { + struct i2c_client client; + struct semaphore update_lock; + char valid; /* zero until following fields are valid */ + unsigned long last_updated; /* in jiffies */ + + /* registers values */ + u8 temp, temp_over; +}; + +/* + * Sysfs stuff + */ + +static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83l785ts_data *data = w83l785ts_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp)); +} + +static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct w83l785ts_data *data = w83l785ts_update_device(dev); + return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over)); +} + +static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); +static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_over, NULL); + +/* + * Real code + */ + +static int w83l785ts_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_CLASS_HWMON)) + return 0; + return i2c_detect(adapter, &addr_data, w83l785ts_detect); +} + +/* + * The following function does more than just detection. If detection + * succeeds, it also registers the new chip. + */ +static int w83l785ts_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *new_client; + struct w83l785ts_data *data; + int err = 0; + + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto exit; + + if (!(data = kmalloc(sizeof(struct w83l785ts_data), GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(data, 0, sizeof(struct w83l785ts_data)); + + + /* The common I2C client data is placed right before the + * W83L785TS-specific data. */ + new_client = &data->client; + i2c_set_clientdata(new_client, data); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &w83l785ts_driver; + new_client->flags = 0; + + /* + * 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", + address); + goto exit_free; + } + } + + 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); + goto exit_free; + } + } + + /* We can fill in the remaining client fields. */ + strlcpy(new_client->name, "w83l785ts", I2C_NAME_SIZE); + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Default values in case the first read fails (unlikely). */ + data->temp_over = data->temp = 0; + + /* Tell the I2C layer a new client has arrived. */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* + * Initialize the W83L785TS chip + * Nothing yet, assume it is already started. + */ + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_temp1_input); + device_create_file(&new_client->dev, &dev_attr_temp1_max); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static int w83l785ts_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval) +{ + int value, i; + + /* Frequent read errors have been reported on Asus boards, so we + * retry on read errors. If it still fails (unlikely), return the + * default value requested by the caller. */ + for (i = 1; i <= MAX_RETRIES; i++) { + value = i2c_smbus_read_byte_data(client, reg); + if (value >= 0) { + dev_dbg(&client->dev, "Read 0x%02x from register " + "0x%02x.\n", value, reg); + return value; + } + dev_dbg(&client->dev, "Read failed, will retry in %d.\n", i); + msleep(i); + } + + dev_err(&client->dev, "Couldn't read value from register 0x%02x. " + "Please report.\n", reg); + return defval; +} + +static struct w83l785ts_data *w83l785ts_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct w83l785ts_data *data = i2c_get_clientdata(client); + + down(&data->update_lock); + + if (!data->valid || time_after(jiffies, data->last_updated + HZ * 2)) { + dev_dbg(&client->dev, "Updating w83l785ts data.\n"); + data->temp = w83l785ts_read_value(client, + W83L785TS_REG_TEMP, data->temp); + data->temp_over = w83l785ts_read_value(client, + W83L785TS_REG_TEMP_OVER, data->temp_over); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +static int __init sensors_w83l785ts_init(void) +{ + return i2c_add_driver(&w83l785ts_driver); +} + +static void __exit sensors_w83l785ts_exit(void) +{ + i2c_del_driver(&w83l785ts_driver); +} + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("W83L785TS-S driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_w83l785ts_init); +module_exit(sensors_w83l785ts_exit); diff --git a/drivers/i2c/chips/adm1021.c b/drivers/i2c/chips/adm1021.c deleted file mode 100644 index d2c774c32f45..000000000000 --- a/drivers/i2c/chips/adm1021.c +++ /dev/null @@ -1,402 +0,0 @@ -/* - adm1021.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998, 1999 Frodo Looijaard and - Philip Edelbrock - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include - - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, - 0x29, 0x2a, 0x2b, - 0x4c, 0x4d, 0x4e, - I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066); - -/* adm1021 constants specified below */ - -/* The adm1021 registers */ -/* Read-only */ -#define ADM1021_REG_TEMP 0x00 -#define ADM1021_REG_REMOTE_TEMP 0x01 -#define ADM1021_REG_STATUS 0x02 -#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/ -#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */ -#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */ -/* These use different addresses for reading/writing */ -#define ADM1021_REG_CONFIG_R 0x03 -#define ADM1021_REG_CONFIG_W 0x09 -#define ADM1021_REG_CONV_RATE_R 0x04 -#define ADM1021_REG_CONV_RATE_W 0x0A -/* These are for the ADM1023's additional precision on the remote temp sensor */ -#define ADM1021_REG_REM_TEMP_PREC 0x010 -#define ADM1021_REG_REM_OFFSET 0x011 -#define ADM1021_REG_REM_OFFSET_PREC 0x012 -#define ADM1021_REG_REM_TOS_PREC 0x013 -#define ADM1021_REG_REM_THYST_PREC 0x014 -/* limits */ -#define ADM1021_REG_TOS_R 0x05 -#define ADM1021_REG_TOS_W 0x0B -#define ADM1021_REG_REMOTE_TOS_R 0x07 -#define ADM1021_REG_REMOTE_TOS_W 0x0D -#define ADM1021_REG_THYST_R 0x06 -#define ADM1021_REG_THYST_W 0x0C -#define ADM1021_REG_REMOTE_THYST_R 0x08 -#define ADM1021_REG_REMOTE_THYST_W 0x0E -/* write-only */ -#define ADM1021_REG_ONESHOT 0x0F - - -/* Conversions. Rounding and limit checking is only done on the TO_REG - variants. Note that you should be a bit careful with which arguments - these macros are called: arguments may be evaluated more than once. - Fixing this is just not worth it. */ -/* Conversions note: 1021 uses normal integer signed-byte format*/ -#define TEMP_FROM_REG(val) (val > 127 ? (val-256)*1000 : val*1000) -#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? (val/1000)+256 : val/1000),0,255)) - -/* Initial values */ - -/* Note: Even though I left the low and high limits named os and hyst, -they don't quite work like a thermostat the way the LM75 does. I.e., -a lower temp than THYST actually triggers an alarm instead of -clearing it. Weird, ey? --Phil */ - -/* Each client has this additional data */ -struct adm1021_data { - struct i2c_client client; - enum chips type; - - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - u8 temp_max; /* Register values */ - u8 temp_hyst; - u8 temp_input; - u8 remote_temp_max; - u8 remote_temp_hyst; - u8 remote_temp_input; - u8 alarms; - /* Special values for ADM1023 only */ - u8 remote_temp_prec; - u8 remote_temp_os_prec; - u8 remote_temp_hyst_prec; - u8 remote_temp_offset; - u8 remote_temp_offset_prec; -}; - -static int adm1021_attach_adapter(struct i2c_adapter *adapter); -static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind); -static void adm1021_init_client(struct i2c_client *client); -static int adm1021_detach_client(struct i2c_client *client); -static int adm1021_read_value(struct i2c_client *client, u8 reg); -static int adm1021_write_value(struct i2c_client *client, u8 reg, - u16 value); -static struct adm1021_data *adm1021_update_device(struct device *dev); - -/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */ -static int read_only = 0; - - -/* This is the driver that will be inserted */ -static struct i2c_driver adm1021_driver = { - .owner = THIS_MODULE, - .name = "adm1021", - .id = I2C_DRIVERID_ADM1021, - .flags = I2C_DF_NOTIFY, - .attach_adapter = adm1021_attach_adapter, - .detach_client = adm1021_detach_client, -}; - -#define show(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct adm1021_data *data = adm1021_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ -} -show(temp_max); -show(temp_hyst); -show(temp_input); -show(remote_temp_max); -show(remote_temp_hyst); -show(remote_temp_input); - -#define show2(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct adm1021_data *data = adm1021_update_device(dev); \ - return sprintf(buf, "%d\n", data->value); \ -} -show2(alarms); - -#define set(value, reg) \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct adm1021_data *data = i2c_get_clientdata(client); \ - int temp = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->value = TEMP_TO_REG(temp); \ - adm1021_write_value(client, reg, data->value); \ - up(&data->update_lock); \ - return count; \ -} -set(temp_max, ADM1021_REG_TOS_W); -set(temp_hyst, ADM1021_REG_THYST_W); -set(remote_temp_max, ADM1021_REG_REMOTE_TOS_W); -set(remote_temp_hyst, ADM1021_REG_REMOTE_THYST_W); - -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max); -static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst); -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL); -static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_remote_temp_max, set_remote_temp_max); -static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_remote_temp_hyst, set_remote_temp_hyst); -static DEVICE_ATTR(temp2_input, S_IRUGO, show_remote_temp_input, NULL); -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - - -static int adm1021_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, adm1021_detect); -} - -static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind) -{ - int i; - struct i2c_client *new_client; - struct adm1021_data *data; - int err = 0; - const char *type_name = ""; - - /* Make sure we aren't probing the ISA bus!! This is just a safety check - at this moment; i2c_detect really won't call us. */ -#ifdef DEBUG - if (i2c_is_isa_adapter(adapter)) { - dev_dbg(&adapter->dev, "adm1021_detect called for an ISA bus adapter?!?\n"); - return 0; - } -#endif - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto error0; - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access adm1021_{read,write}_value. */ - - if (!(data = kmalloc(sizeof(struct adm1021_data), GFP_KERNEL))) { - err = -ENOMEM; - goto error0; - } - memset(data, 0, sizeof(struct adm1021_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &adm1021_driver; - new_client->flags = 0; - - /* Now, we do the remaining detection. */ - if (kind < 0) { - if ((adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0x03) != 0x00 - || (adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x3F) != 0x00 - || (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) & 0xF8) != 0x00) { - err = -ENODEV; - goto error1; - } - } - - /* Determine the chip type. */ - if (kind <= 0) { - i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID); - if (i == 0x41) - if ((adm1021_read_value(new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030) - kind = adm1023; - else - kind = adm1021; - else if (i == 0x49) - kind = thmc10; - else if (i == 0x23) - kind = gl523sm; - else if ((i == 0x4d) && - (adm1021_read_value(new_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 (adm1021_read_value(new_client, ADM1021_REG_CONV_RATE_R) == 0x00 - && (kind == 0 /* skip extra detection */ - || ((adm1021_read_value(new_client, ADM1021_REG_CONFIG_R) & 0x7F) == 0x00 - && (adm1021_read_value(new_client, ADM1021_REG_STATUS) & 0xAB) == 0x00))) - kind = lm84; - else - kind = max1617; - } - - if (kind == max1617) { - type_name = "max1617"; - } else if (kind == max1617a) { - type_name = "max1617a"; - } else if (kind == adm1021) { - type_name = "adm1021"; - } else if (kind == adm1023) { - type_name = "adm1023"; - } else if (kind == thmc10) { - type_name = "thmc10"; - } else if (kind == lm84) { - type_name = "lm84"; - } else if (kind == gl523sm) { - type_name = "gl523sm"; - } else if (kind == mc1066) { - type_name = "mc1066"; - } - - /* Fill in the remaining client fields and put it into the global list */ - strlcpy(new_client->name, type_name, I2C_NAME_SIZE); - data->type = kind; - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto error1; - - /* Initialize the ADM1021 chip */ - if (kind != lm84) - adm1021_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_alarms); - - return 0; - -error1: - kfree(data); -error0: - return err; -} - -static void adm1021_init_client(struct i2c_client *client) -{ - /* Enable ADC and disable suspend mode */ - adm1021_write_value(client, ADM1021_REG_CONFIG_W, - adm1021_read_value(client, ADM1021_REG_CONFIG_R) & 0xBF); - /* Set Conversion rate to 1/sec (this can be tinkered with) */ - adm1021_write_value(client, ADM1021_REG_CONV_RATE_W, 0x04); -} - -static int adm1021_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - -/* All registers are byte-sized */ -static int adm1021_read_value(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value) -{ - if (!read_only) - return i2c_smbus_write_byte_data(client, reg, value); - return 0; -} - -static struct adm1021_data *adm1021_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1021_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - dev_dbg(&client->dev, "Starting adm1021 update\n"); - - data->temp_input = adm1021_read_value(client, ADM1021_REG_TEMP); - data->temp_max = adm1021_read_value(client, ADM1021_REG_TOS_R); - data->temp_hyst = adm1021_read_value(client, ADM1021_REG_THYST_R); - data->remote_temp_input = adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP); - data->remote_temp_max = adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R); - data->remote_temp_hyst = adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R); - data->alarms = adm1021_read_value(client, ADM1021_REG_STATUS) & 0x7c; - if (data->type == adm1023) { - data->remote_temp_prec = adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC); - data->remote_temp_os_prec = adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC); - data->remote_temp_hyst_prec = adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC); - data->remote_temp_offset = adm1021_read_value(client, ADM1021_REG_REM_OFFSET); - data->remote_temp_offset_prec = adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC); - } - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_adm1021_init(void) -{ - return i2c_add_driver(&adm1021_driver); -} - -static void __exit sensors_adm1021_exit(void) -{ - i2c_del_driver(&adm1021_driver); -} - -MODULE_AUTHOR ("Frodo Looijaard and " - "Philip Edelbrock "); -MODULE_DESCRIPTION("adm1021 driver"); -MODULE_LICENSE("GPL"); - -module_param(read_only, bool, 0); -MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); - -module_init(sensors_adm1021_init) -module_exit(sensors_adm1021_exit) diff --git a/drivers/i2c/chips/adm1025.c b/drivers/i2c/chips/adm1025.c deleted file mode 100644 index e452d0daf906..000000000000 --- a/drivers/i2c/chips/adm1025.c +++ /dev/null @@ -1,577 +0,0 @@ -/* - * adm1025.c - * - * Copyright (C) 2000 Chen-Yuan Wu - * Copyright (C) 2003-2004 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 - * (its own plus up to one external one). Voltages are scaled internally - * (which is not the common way) with ratios such that the nominal value - * of each voltage correspond to a register value of 192 (which means a - * resolution of about 0.5% of the nominal value). Temperature values are - * reported with a 1 deg resolution and a 3 deg accuracy. Complete - * datasheet can be obtained from Analog's website at: - * http://www.analog.com/Analog_Root/productPage/productHome/0,2121,ADM1025,00.html - * - * This driver also supports the ADM1025A, which differs from the ADM1025 - * only in that it has "open-drain VID inputs while the ADM1025 has - * on-chip 100k pull-ups on the VID inputs". It doesn't make any - * difference for us. - * - * This driver also supports the NE1619, a sensor chip made by Philips. - * That chip is similar to the ADM1025A, with a few differences. The only - * difference that matters to us is that the NE1619 has only two possible - * addresses while the ADM1025A has a third one. Complete datasheet can be - * obtained from Philips's website at: - * http://www.semiconductors.philips.com/pip/NE1619DS.html - * - * Since the ADM1025 was the first chipset supported by this driver, most - * comments will refer to this chipset, but are actually general and - * concern all supported chipsets, unless mentioned otherwise. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * Addresses to scan - * ADM1025 and ADM1025A have three possible addresses: 0x2c, 0x2d and 0x2e. - * NE1619 has two possible addresses: 0x2c and 0x2d. - */ - -static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* - * Insmod parameters - */ - -SENSORS_INSMOD_2(adm1025, ne1619); - -/* - * The ADM1025 registers - */ - -#define ADM1025_REG_MAN_ID 0x3E -#define ADM1025_REG_CHIP_ID 0x3F -#define ADM1025_REG_CONFIG 0x40 -#define ADM1025_REG_STATUS1 0x41 -#define ADM1025_REG_STATUS2 0x42 -#define ADM1025_REG_IN(nr) (0x20 + (nr)) -#define ADM1025_REG_IN_MAX(nr) (0x2B + (nr) * 2) -#define ADM1025_REG_IN_MIN(nr) (0x2C + (nr) * 2) -#define ADM1025_REG_TEMP(nr) (0x26 + (nr)) -#define ADM1025_REG_TEMP_HIGH(nr) (0x37 + (nr) * 2) -#define ADM1025_REG_TEMP_LOW(nr) (0x38 + (nr) * 2) -#define ADM1025_REG_VID 0x47 -#define ADM1025_REG_VID4 0x49 - -/* - * Conversions and various macros - * The ADM1025 uses signed 8-bit values for temperatures. - */ - -static int in_scale[6] = { 2500, 2250, 3300, 5000, 12000, 3300 }; - -#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192) -#define IN_TO_REG(val,scale) ((val) <= 0 ? 0 : \ - (val) * 192 >= (scale) * 255 ? 255 : \ - ((val) * 192 + (scale)/2) / (scale)) - -#define TEMP_FROM_REG(reg) ((reg) * 1000) -#define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \ - (val) >= 126500 ? 127 : \ - (((val) < 0 ? (val)-500 : (val)+500) / 1000)) - -/* - * Functions declaration - */ - -static int adm1025_attach_adapter(struct i2c_adapter *adapter); -static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind); -static void adm1025_init_client(struct i2c_client *client); -static int adm1025_detach_client(struct i2c_client *client); -static struct adm1025_data *adm1025_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static struct i2c_driver adm1025_driver = { - .owner = THIS_MODULE, - .name = "adm1025", - .id = I2C_DRIVERID_ADM1025, - .flags = I2C_DF_NOTIFY, - .attach_adapter = adm1025_attach_adapter, - .detach_client = adm1025_detach_client, -}; - -/* - * Client data (each client gets its own) - */ - -struct adm1025_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - u8 in[6]; /* register value */ - u8 in_max[6]; /* register value */ - u8 in_min[6]; /* register value */ - s8 temp[2]; /* register value */ - s8 temp_min[2]; /* register value */ - s8 temp_max[2]; /* register value */ - u16 alarms; /* register values, combined */ - u8 vid; /* register values, combined */ - u8 vrm; -}; - -/* - * Sysfs stuff - */ - -#define show_in(offset) \ -static ssize_t show_in##offset(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct adm1025_data *data = adm1025_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \ - in_scale[offset])); \ -} \ -static ssize_t show_in##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct adm1025_data *data = adm1025_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \ - in_scale[offset])); \ -} \ -static ssize_t show_in##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct adm1025_data *data = adm1025_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \ - in_scale[offset])); \ -} \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL); -show_in(0); -show_in(1); -show_in(2); -show_in(3); -show_in(4); -show_in(5); - -#define show_temp(offset) \ -static ssize_t show_temp##offset(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct adm1025_data *data = adm1025_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \ -} \ -static ssize_t show_temp##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct adm1025_data *data = adm1025_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[offset-1])); \ -} \ -static ssize_t show_temp##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct adm1025_data *data = adm1025_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[offset-1])); \ -}\ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp##offset, NULL); -show_temp(1); -show_temp(2); - -#define set_in(offset) \ -static ssize_t set_in##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct adm1025_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->in_min[offset] = IN_TO_REG(val, in_scale[offset]); \ - i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MIN(offset), \ - data->in_min[offset]); \ - up(&data->update_lock); \ - return count; \ -} \ -static ssize_t set_in##offset##_max(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct adm1025_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->in_max[offset] = IN_TO_REG(val, in_scale[offset]); \ - i2c_smbus_write_byte_data(client, ADM1025_REG_IN_MAX(offset), \ - data->in_max[offset]); \ - up(&data->update_lock); \ - return count; \ -} \ -static DEVICE_ATTR(in##offset##_min, S_IWUSR | S_IRUGO, \ - show_in##offset##_min, set_in##offset##_min); \ -static DEVICE_ATTR(in##offset##_max, S_IWUSR | S_IRUGO, \ - show_in##offset##_max, set_in##offset##_max); -set_in(0); -set_in(1); -set_in(2); -set_in(3); -set_in(4); -set_in(5); - -#define set_temp(offset) \ -static ssize_t set_temp##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct adm1025_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->temp_min[offset-1] = TEMP_TO_REG(val); \ - i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_LOW(offset-1), \ - data->temp_min[offset-1]); \ - up(&data->update_lock); \ - return count; \ -} \ -static ssize_t set_temp##offset##_max(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct adm1025_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->temp_max[offset-1] = TEMP_TO_REG(val); \ - i2c_smbus_write_byte_data(client, ADM1025_REG_TEMP_HIGH(offset-1), \ - data->temp_max[offset-1]); \ - up(&data->update_lock); \ - return count; \ -} \ -static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \ - show_temp##offset##_min, set_temp##offset##_min); \ -static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \ - show_temp##offset##_max, set_temp##offset##_max); -set_temp(1); -set_temp(2); - -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1025_data *data = adm1025_update_device(dev); - return sprintf(buf, "%u\n", data->alarms); -} -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1025_data *data = adm1025_update_device(dev); - return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm)); -} -/* in1_ref is deprecated in favour of cpu0_vid, remove after 2005-11-11 */ -static DEVICE_ATTR(in1_ref, S_IRUGO, show_vid, NULL); -static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); - -static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1025_data *data = adm1025_update_device(dev); - return sprintf(buf, "%u\n", data->vrm); -} -static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1025_data *data = i2c_get_clientdata(client); - data->vrm = simple_strtoul(buf, NULL, 10); - return count; -} -static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); - -/* - * Real code - */ - -static int adm1025_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, adm1025_detect); -} - -/* - * The following function does more than just detection. If detection - * succeeds, it also registers the new chip. - */ -static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct adm1025_data *data; - int err = 0; - const char *name = ""; - u8 config; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - if (!(data = kmalloc(sizeof(struct adm1025_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct adm1025_data)); - - /* The common I2C client data is placed right before the - ADM1025-specific data. */ - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &adm1025_driver; - new_client->flags = 0; - - /* - * 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(new_client, ADM1025_REG_CONFIG); - if (kind < 0) { /* detection */ - if ((config & 0x80) != 0x00 - || (i2c_smbus_read_byte_data(new_client, - ADM1025_REG_STATUS1) & 0xC0) != 0x00 - || (i2c_smbus_read_byte_data(new_client, - ADM1025_REG_STATUS2) & 0xBC) != 0x00) { - dev_dbg(&adapter->dev, - "ADM1025 detection failed at 0x%02x.\n", - address); - goto exit_free; - } - } - - if (kind <= 0) { /* identification */ - u8 man_id, chip_id; - - man_id = i2c_smbus_read_byte_data(new_client, - ADM1025_REG_MAN_ID); - chip_id = i2c_smbus_read_byte_data(new_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 (address != 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); - goto exit_free; - } - } - - if (kind == adm1025) { - name = "adm1025"; - } else if (kind == ne1619) { - name = "ne1619"; - } - - /* We can fill in the remaining client fields */ - strlcpy(new_client->name, name, I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the ADM1025 chip */ - adm1025_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in5_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in5_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in5_max); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_alarms); - /* in1_ref is deprecated, remove after 2005-11-11 */ - device_create_file(&new_client->dev, &dev_attr_in1_ref); - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - device_create_file(&new_client->dev, &dev_attr_vrm); - - /* Pin 11 is either in4 (+12V) or VID4 */ - if (!(config & 0x20)) { - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in4_max); - } - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static void adm1025_init_client(struct i2c_client *client) -{ - u8 reg; - struct adm1025_data *data = i2c_get_clientdata(client); - int i; - - data->vrm = i2c_which_vrm(); - - /* - * Set high limits - * Usually we avoid setting limits on driver init, but it happens - * that the ADM1025 comes with stupid default limits (all registers - * set to 0). In case the chip has not gone through any limit - * setting yet, we better set the high limits to the max so that - * no alarm triggers. - */ - for (i=0; i<6; i++) { - reg = i2c_smbus_read_byte_data(client, - ADM1025_REG_IN_MAX(i)); - if (reg == 0) - i2c_smbus_write_byte_data(client, - ADM1025_REG_IN_MAX(i), - 0xFF); - } - for (i=0; i<2; i++) { - reg = i2c_smbus_read_byte_data(client, - ADM1025_REG_TEMP_HIGH(i)); - if (reg == 0) - i2c_smbus_write_byte_data(client, - ADM1025_REG_TEMP_HIGH(i), - 0x7F); - } - - /* - * Start the conversions - */ - reg = i2c_smbus_read_byte_data(client, ADM1025_REG_CONFIG); - if (!(reg & 0x01)) - i2c_smbus_write_byte_data(client, ADM1025_REG_CONFIG, - (reg&0x7E)|0x01); -} - -static int adm1025_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - -static struct adm1025_data *adm1025_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1025_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - int i; - - dev_dbg(&client->dev, "Updating data.\n"); - for (i=0; i<6; i++) { - data->in[i] = i2c_smbus_read_byte_data(client, - ADM1025_REG_IN(i)); - data->in_min[i] = i2c_smbus_read_byte_data(client, - ADM1025_REG_IN_MIN(i)); - data->in_max[i] = i2c_smbus_read_byte_data(client, - ADM1025_REG_IN_MAX(i)); - } - for (i=0; i<2; i++) { - data->temp[i] = i2c_smbus_read_byte_data(client, - ADM1025_REG_TEMP(i)); - data->temp_min[i] = i2c_smbus_read_byte_data(client, - ADM1025_REG_TEMP_LOW(i)); - data->temp_max[i] = i2c_smbus_read_byte_data(client, - ADM1025_REG_TEMP_HIGH(i)); - } - data->alarms = i2c_smbus_read_byte_data(client, - ADM1025_REG_STATUS1) - | (i2c_smbus_read_byte_data(client, - ADM1025_REG_STATUS2) << 8); - data->vid = (i2c_smbus_read_byte_data(client, - ADM1025_REG_VID) & 0x0f) - | ((i2c_smbus_read_byte_data(client, - ADM1025_REG_VID4) & 0x01) << 4); - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_adm1025_init(void) -{ - return i2c_add_driver(&adm1025_driver); -} - -static void __exit sensors_adm1025_exit(void) -{ - i2c_del_driver(&adm1025_driver); -} - -MODULE_AUTHOR("Jean Delvare "); -MODULE_DESCRIPTION("ADM1025 driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_adm1025_init); -module_exit(sensors_adm1025_exit); diff --git a/drivers/i2c/chips/adm1026.c b/drivers/i2c/chips/adm1026.c deleted file mode 100644 index 3c85fe150cd7..000000000000 --- a/drivers/i2c/chips/adm1026.c +++ /dev/null @@ -1,1714 +0,0 @@ -/* - adm1026.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (C) 2002, 2003 Philip Pokorny - Copyright (C) 2004 Justin Thiessen - - Chip details at: - - - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_1(adm1026); - -static int gpio_input[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 }; -static int gpio_output[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 }; -static int gpio_inverted[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 }; -static int gpio_normal[17] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1 }; -static int gpio_fan[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; -module_param_array(gpio_input,int,NULL,0); -MODULE_PARM_DESC(gpio_input,"List of GPIO pins (0-16) to program as inputs"); -module_param_array(gpio_output,int,NULL,0); -MODULE_PARM_DESC(gpio_output,"List of GPIO pins (0-16) to program as " - "outputs"); -module_param_array(gpio_inverted,int,NULL,0); -MODULE_PARM_DESC(gpio_inverted,"List of GPIO pins (0-16) to program as " - "inverted"); -module_param_array(gpio_normal,int,NULL,0); -MODULE_PARM_DESC(gpio_normal,"List of GPIO pins (0-16) to program as " - "normal/non-inverted"); -module_param_array(gpio_fan,int,NULL,0); -MODULE_PARM_DESC(gpio_fan,"List of GPIO pins (0-7) to program as fan tachs"); - -/* Many ADM1026 constants specified below */ - -/* The ADM1026 registers */ -#define ADM1026_REG_CONFIG1 0x00 -#define CFG1_MONITOR 0x01 -#define CFG1_INT_ENABLE 0x02 -#define CFG1_INT_CLEAR 0x04 -#define CFG1_AIN8_9 0x08 -#define CFG1_THERM_HOT 0x10 -#define CFG1_DAC_AFC 0x20 -#define CFG1_PWM_AFC 0x40 -#define CFG1_RESET 0x80 -#define ADM1026_REG_CONFIG2 0x01 -/* CONFIG2 controls FAN0/GPIO0 through FAN7/GPIO7 */ -#define ADM1026_REG_CONFIG3 0x07 -#define CFG3_GPIO16_ENABLE 0x01 -#define CFG3_CI_CLEAR 0x02 -#define CFG3_VREF_250 0x04 -#define CFG3_GPIO16_DIR 0x40 -#define CFG3_GPIO16_POL 0x80 -#define ADM1026_REG_E2CONFIG 0x13 -#define E2CFG_READ 0x01 -#define E2CFG_WRITE 0x02 -#define E2CFG_ERASE 0x04 -#define E2CFG_ROM 0x08 -#define E2CFG_CLK_EXT 0x80 - -/* There are 10 general analog inputs and 7 dedicated inputs - * They are: - * 0 - 9 = AIN0 - AIN9 - * 10 = Vbat - * 11 = 3.3V Standby - * 12 = 3.3V Main - * 13 = +5V - * 14 = Vccp (CPU core voltage) - * 15 = +12V - * 16 = -12V - */ -static u16 ADM1026_REG_IN[] = { - 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, - 0x36, 0x37, 0x27, 0x29, 0x26, 0x2a, - 0x2b, 0x2c, 0x2d, 0x2e, 0x2f - }; -static u16 ADM1026_REG_IN_MIN[] = { - 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, - 0x5e, 0x5f, 0x6d, 0x49, 0x6b, 0x4a, - 0x4b, 0x4c, 0x4d, 0x4e, 0x4f - }; -static u16 ADM1026_REG_IN_MAX[] = { - 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, - 0x56, 0x57, 0x6c, 0x41, 0x6a, 0x42, - 0x43, 0x44, 0x45, 0x46, 0x47 - }; - -/* Temperatures are: - * 0 - Internal - * 1 - External 1 - * 2 - External 2 - */ -static u16 ADM1026_REG_TEMP[] = { 0x1f, 0x28, 0x29 }; -static u16 ADM1026_REG_TEMP_MIN[] = { 0x69, 0x48, 0x49 }; -static u16 ADM1026_REG_TEMP_MAX[] = { 0x68, 0x40, 0x41 }; -static u16 ADM1026_REG_TEMP_TMIN[] = { 0x10, 0x11, 0x12 }; -static u16 ADM1026_REG_TEMP_THERM[] = { 0x0d, 0x0e, 0x0f }; -static u16 ADM1026_REG_TEMP_OFFSET[] = { 0x1e, 0x6e, 0x6f }; - -#define ADM1026_REG_FAN(nr) (0x38 + (nr)) -#define ADM1026_REG_FAN_MIN(nr) (0x60 + (nr)) -#define ADM1026_REG_FAN_DIV_0_3 0x02 -#define ADM1026_REG_FAN_DIV_4_7 0x03 - -#define ADM1026_REG_DAC 0x04 -#define ADM1026_REG_PWM 0x05 - -#define ADM1026_REG_GPIO_CFG_0_3 0x08 -#define ADM1026_REG_GPIO_CFG_4_7 0x09 -#define ADM1026_REG_GPIO_CFG_8_11 0x0a -#define ADM1026_REG_GPIO_CFG_12_15 0x0b -/* CFG_16 in REG_CFG3 */ -#define ADM1026_REG_GPIO_STATUS_0_7 0x24 -#define ADM1026_REG_GPIO_STATUS_8_15 0x25 -/* STATUS_16 in REG_STATUS4 */ -#define ADM1026_REG_GPIO_MASK_0_7 0x1c -#define ADM1026_REG_GPIO_MASK_8_15 0x1d -/* MASK_16 in REG_MASK4 */ - -#define ADM1026_REG_COMPANY 0x16 -#define ADM1026_REG_VERSTEP 0x17 -/* These are the recognized values for the above regs */ -#define ADM1026_COMPANY_ANALOG_DEV 0x41 -#define ADM1026_VERSTEP_GENERIC 0x40 -#define ADM1026_VERSTEP_ADM1026 0x44 - -#define ADM1026_REG_MASK1 0x18 -#define ADM1026_REG_MASK2 0x19 -#define ADM1026_REG_MASK3 0x1a -#define ADM1026_REG_MASK4 0x1b - -#define ADM1026_REG_STATUS1 0x20 -#define ADM1026_REG_STATUS2 0x21 -#define ADM1026_REG_STATUS3 0x22 -#define ADM1026_REG_STATUS4 0x23 - -#define ADM1026_FAN_ACTIVATION_TEMP_HYST -6 -#define ADM1026_FAN_CONTROL_TEMP_RANGE 20 -#define ADM1026_PWM_MAX 255 - -/* Conversions. Rounding and limit checking is only done on the TO_REG - * variants. Note that you should be a bit careful with which arguments - * these macros are called: arguments may be evaluated more than once. - */ - -/* IN are scaled acording to built-in resistors. These are the - * voltages corresponding to 3/4 of full scale (192 or 0xc0) - * NOTE: The -12V input needs an additional factor to account - * for the Vref pullup resistor. - * NEG12_OFFSET = SCALE * Vref / V-192 - Vref - * = 13875 * 2.50 / 1.875 - 2500 - * = 16000 - * - * The values in this table are based on Table II, page 15 of the - * datasheet. - */ -static int adm1026_scaling[] = { /* .001 Volts */ - 2250, 2250, 2250, 2250, 2250, 2250, - 1875, 1875, 1875, 1875, 3000, 3330, - 3330, 4995, 2250, 12000, 13875 - }; -#define NEG12_OFFSET 16000 -#define SCALE(val,from,to) (((val)*(to) + ((from)/2))/(from)) -#define INS_TO_REG(n,val) (SENSORS_LIMIT(SCALE(val,adm1026_scaling[n],192),\ - 0,255)) -#define INS_FROM_REG(n,val) (SCALE(val,192,adm1026_scaling[n])) - -/* FAN speed is measured using 22.5kHz clock and counts for 2 pulses - * and we assume a 2 pulse-per-rev fan tach signal - * 22500 kHz * 60 (sec/min) * 2 (pulse) / 2 (pulse/rev) == 1350000 - */ -#define FAN_TO_REG(val,div) ((val)<=0 ? 0xff : SENSORS_LIMIT(1350000/((val)*\ - (div)),1,254)) -#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==0xff ? 0 : 1350000/((val)*\ - (div))) -#define DIV_FROM_REG(val) (1<<(val)) -#define DIV_TO_REG(val) ((val)>=8 ? 3 : (val)>=4 ? 2 : (val)>=2 ? 1 : 0) - -/* Temperature is reported in 1 degC increments */ -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)+((val)<0 ? -500 : 500))/1000,\ - -127,127)) -#define TEMP_FROM_REG(val) ((val) * 1000) -#define OFFSET_TO_REG(val) (SENSORS_LIMIT(((val)+((val)<0 ? -500 : 500))/1000,\ - -127,127)) -#define OFFSET_FROM_REG(val) ((val) * 1000) - -#define PWM_TO_REG(val) (SENSORS_LIMIT(val,0,255)) -#define PWM_FROM_REG(val) (val) - -#define PWM_MIN_TO_REG(val) ((val) & 0xf0) -#define PWM_MIN_FROM_REG(val) (((val) & 0xf0) + ((val) >> 4)) - -/* Analog output is a voltage, and scaled to millivolts. The datasheet - * indicates that the DAC could be used to drive the fans, but in our - * example board (Arima HDAMA) it isn't connected to the fans at all. - */ -#define DAC_TO_REG(val) (SENSORS_LIMIT(((((val)*255)+500)/2500),0,255)) -#define DAC_FROM_REG(val) (((val)*2500)/255) - -/* Typically used with systems using a v9.1 VRM spec ? */ -#define ADM1026_INIT_VRM 91 - -/* Chip sampling rates - * - * Some sensors are not updated more frequently than once per second - * so it doesn't make sense to read them more often than that. - * We cache the results and return the saved data if the driver - * is called again before a second has elapsed. - * - * Also, there is significant configuration data for this chip - * So, we keep the config data up to date in the cache - * when it is written and only sample it once every 5 *minutes* - */ -#define ADM1026_DATA_INTERVAL (1 * HZ) -#define ADM1026_CONFIG_INTERVAL (5 * 60 * HZ) - -/* We allow for multiple chips in a single system. - * - * For each registered ADM1026, we need to keep state information - * at client->data. The adm1026_data structure is dynamically - * allocated, when a new client structure is allocated. */ - -struct pwm_data { - u8 pwm; - u8 enable; - u8 auto_pwm_min; -}; - -struct adm1026_data { - struct i2c_client client; - struct semaphore lock; - enum chips type; - - struct semaphore update_lock; - int valid; /* !=0 if following fields are valid */ - unsigned long last_reading; /* In jiffies */ - unsigned long last_config; /* In jiffies */ - - u8 in[17]; /* Register value */ - u8 in_max[17]; /* Register value */ - u8 in_min[17]; /* Register value */ - s8 temp[3]; /* Register value */ - s8 temp_min[3]; /* Register value */ - s8 temp_max[3]; /* Register value */ - s8 temp_tmin[3]; /* Register value */ - s8 temp_crit[3]; /* Register value */ - s8 temp_offset[3]; /* Register value */ - u8 fan[8]; /* Register value */ - u8 fan_min[8]; /* Register value */ - u8 fan_div[8]; /* Decoded value */ - struct pwm_data pwm1; /* Pwm control values */ - int vid; /* Decoded value */ - u8 vrm; /* VRM version */ - u8 analog_out; /* Register value (DAC) */ - long alarms; /* Register encoding, combined */ - long alarm_mask; /* Register encoding, combined */ - long gpio; /* Register encoding, combined */ - long gpio_mask; /* Register encoding, combined */ - u8 gpio_config[17]; /* Decoded value */ - u8 config1; /* Register value */ - u8 config2; /* Register value */ - u8 config3; /* Register value */ -}; - -static int adm1026_attach_adapter(struct i2c_adapter *adapter); -static int adm1026_detect(struct i2c_adapter *adapter, int address, - int kind); -static int adm1026_detach_client(struct i2c_client *client); -static int adm1026_read_value(struct i2c_client *client, u8 register); -static int adm1026_write_value(struct i2c_client *client, u8 register, - int value); -static void adm1026_print_gpio(struct i2c_client *client); -static void adm1026_fixup_gpio(struct i2c_client *client); -static struct adm1026_data *adm1026_update_device(struct device *dev); -static void adm1026_init_client(struct i2c_client *client); - - -static struct i2c_driver adm1026_driver = { - .owner = THIS_MODULE, - .name = "adm1026", - .flags = I2C_DF_NOTIFY, - .attach_adapter = adm1026_attach_adapter, - .detach_client = adm1026_detach_client, -}; - -int adm1026_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) { - return 0; - } - return i2c_detect(adapter, &addr_data, adm1026_detect); -} - -int adm1026_detach_client(struct i2c_client *client) -{ - i2c_detach_client(client); - kfree(client); - return 0; -} - -int adm1026_read_value(struct i2c_client *client, u8 reg) -{ - int res; - - if (reg < 0x80) { - /* "RAM" locations */ - res = i2c_smbus_read_byte_data(client, reg) & 0xff; - } else { - /* EEPROM, do nothing */ - res = 0; - } - return res; -} - -int adm1026_write_value(struct i2c_client *client, u8 reg, int value) -{ - int res; - - if (reg < 0x80) { - /* "RAM" locations */ - res = i2c_smbus_write_byte_data(client, reg, value); - } else { - /* EEPROM, do nothing */ - res = 0; - } - return res; -} - -void adm1026_init_client(struct i2c_client *client) -{ - int value, i; - struct adm1026_data *data = i2c_get_clientdata(client); - - dev_dbg(&client->dev, "Initializing device\n"); - /* Read chip config */ - data->config1 = adm1026_read_value(client, ADM1026_REG_CONFIG1); - data->config2 = adm1026_read_value(client, ADM1026_REG_CONFIG2); - data->config3 = adm1026_read_value(client, ADM1026_REG_CONFIG3); - - /* Inform user of chip config */ - dev_dbg(&client->dev, "ADM1026_REG_CONFIG1 is: 0x%02x\n", - data->config1); - if ((data->config1 & CFG1_MONITOR) == 0) { - dev_dbg(&client->dev, "Monitoring not currently " - "enabled.\n"); - } - if (data->config1 & CFG1_INT_ENABLE) { - dev_dbg(&client->dev, "SMBALERT interrupts are " - "enabled.\n"); - } - if (data->config1 & CFG1_AIN8_9) { - dev_dbg(&client->dev, "in8 and in9 enabled. " - "temp3 disabled.\n"); - } else { - dev_dbg(&client->dev, "temp3 enabled. in8 and " - "in9 disabled.\n"); - } - if (data->config1 & CFG1_THERM_HOT) { - dev_dbg(&client->dev, "Automatic THERM, PWM, " - "and temp limits enabled.\n"); - } - - value = data->config3; - if (data->config3 & CFG3_GPIO16_ENABLE) { - dev_dbg(&client->dev, "GPIO16 enabled. THERM" - "pin disabled.\n"); - } else { - dev_dbg(&client->dev, "THERM pin enabled. " - "GPIO16 disabled.\n"); - } - if (data->config3 & CFG3_VREF_250) { - dev_dbg(&client->dev, "Vref is 2.50 Volts.\n"); - } else { - dev_dbg(&client->dev, "Vref is 1.82 Volts.\n"); - } - /* Read and pick apart the existing GPIO configuration */ - value = 0; - for (i = 0;i <= 15;++i) { - if ((i & 0x03) == 0) { - value = adm1026_read_value(client, - ADM1026_REG_GPIO_CFG_0_3 + i/4); - } - data->gpio_config[i] = value & 0x03; - value >>= 2; - } - data->gpio_config[16] = (data->config3 >> 6) & 0x03; - - /* ... and then print it */ - adm1026_print_gpio(client); - - /* If the user asks us to reprogram the GPIO config, then - * do it now. - */ - if (gpio_input[0] != -1 || gpio_output[0] != -1 - || gpio_inverted[0] != -1 || gpio_normal[0] != -1 - || gpio_fan[0] != -1) { - adm1026_fixup_gpio(client); - } - - /* WE INTENTIONALLY make no changes to the limits, - * offsets, pwms, fans and zones. If they were - * configured, we don't want to mess with them. - * If they weren't, the default is 100% PWM, no - * control and will suffice until 'sensors -s' - * can be run by the user. We DO set the default - * value for pwm1.auto_pwm_min to its maximum - * so that enabling automatic pwm fan control - * without first setting a value for pwm1.auto_pwm_min - * will not result in potentially dangerous fan speed decrease. - */ - data->pwm1.auto_pwm_min=255; - /* Start monitoring */ - value = adm1026_read_value(client, ADM1026_REG_CONFIG1); - /* Set MONITOR, clear interrupt acknowledge and s/w reset */ - value = (value | CFG1_MONITOR) & (~CFG1_INT_CLEAR & ~CFG1_RESET); - dev_dbg(&client->dev, "Setting CONFIG to: 0x%02x\n", value); - data->config1 = value; - adm1026_write_value(client, ADM1026_REG_CONFIG1, value); - - /* initialize fan_div[] to hardware defaults */ - value = adm1026_read_value(client, ADM1026_REG_FAN_DIV_0_3) | - (adm1026_read_value(client, ADM1026_REG_FAN_DIV_4_7) << 8); - for (i = 0;i <= 7;++i) { - data->fan_div[i] = DIV_FROM_REG(value & 0x03); - value >>= 2; - } -} - -void adm1026_print_gpio(struct i2c_client *client) -{ - struct adm1026_data *data = i2c_get_clientdata(client); - int i; - - dev_dbg(&client->dev, "GPIO config is:"); - for (i = 0;i <= 7;++i) { - if (data->config2 & (1 << i)) { - dev_dbg(&client->dev, "\t%sGP%s%d\n", - data->gpio_config[i] & 0x02 ? "" : "!", - data->gpio_config[i] & 0x01 ? "OUT" : "IN", - i); - } else { - dev_dbg(&client->dev, "\tFAN%d\n", i); - } - } - for (i = 8;i <= 15;++i) { - dev_dbg(&client->dev, "\t%sGP%s%d\n", - data->gpio_config[i] & 0x02 ? "" : "!", - data->gpio_config[i] & 0x01 ? "OUT" : "IN", - i); - } - if (data->config3 & CFG3_GPIO16_ENABLE) { - dev_dbg(&client->dev, "\t%sGP%s16\n", - data->gpio_config[16] & 0x02 ? "" : "!", - data->gpio_config[16] & 0x01 ? "OUT" : "IN"); - } else { - /* GPIO16 is THERM */ - dev_dbg(&client->dev, "\tTHERM\n"); - } -} - -void adm1026_fixup_gpio(struct i2c_client *client) -{ - struct adm1026_data *data = i2c_get_clientdata(client); - int i; - int value; - - /* Make the changes requested. */ - /* We may need to unlock/stop monitoring or soft-reset the - * chip before we can make changes. This hasn't been - * tested much. FIXME - */ - - /* Make outputs */ - for (i = 0;i <= 16;++i) { - if (gpio_output[i] >= 0 && gpio_output[i] <= 16) { - data->gpio_config[gpio_output[i]] |= 0x01; - } - /* if GPIO0-7 is output, it isn't a FAN tach */ - if (gpio_output[i] >= 0 && gpio_output[i] <= 7) { - data->config2 |= 1 << gpio_output[i]; - } - } - - /* Input overrides output */ - for (i = 0;i <= 16;++i) { - if (gpio_input[i] >= 0 && gpio_input[i] <= 16) { - data->gpio_config[gpio_input[i]] &= ~ 0x01; - } - /* if GPIO0-7 is input, it isn't a FAN tach */ - if (gpio_input[i] >= 0 && gpio_input[i] <= 7) { - data->config2 |= 1 << gpio_input[i]; - } - } - - /* Inverted */ - for (i = 0;i <= 16;++i) { - if (gpio_inverted[i] >= 0 && gpio_inverted[i] <= 16) { - data->gpio_config[gpio_inverted[i]] &= ~ 0x02; - } - } - - /* Normal overrides inverted */ - for (i = 0;i <= 16;++i) { - if (gpio_normal[i] >= 0 && gpio_normal[i] <= 16) { - data->gpio_config[gpio_normal[i]] |= 0x02; - } - } - - /* Fan overrides input and output */ - for (i = 0;i <= 7;++i) { - if (gpio_fan[i] >= 0 && gpio_fan[i] <= 7) { - data->config2 &= ~(1 << gpio_fan[i]); - } - } - - /* Write new configs to registers */ - adm1026_write_value(client, ADM1026_REG_CONFIG2, data->config2); - data->config3 = (data->config3 & 0x3f) - | ((data->gpio_config[16] & 0x03) << 6); - adm1026_write_value(client, ADM1026_REG_CONFIG3, data->config3); - for (i = 15, value = 0;i >= 0;--i) { - value <<= 2; - value |= data->gpio_config[i] & 0x03; - if ((i & 0x03) == 0) { - adm1026_write_value(client, - ADM1026_REG_GPIO_CFG_0_3 + i/4, - value); - value = 0; - } - } - - /* Print the new config */ - adm1026_print_gpio(client); -} - - -static struct adm1026_data *adm1026_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int i; - long value, alarms, gpio; - - down(&data->update_lock); - if (!data->valid - || time_after(jiffies, data->last_reading + ADM1026_DATA_INTERVAL)) { - /* Things that change quickly */ - dev_dbg(&client->dev,"Reading sensor values\n"); - for (i = 0;i <= 16;++i) { - data->in[i] = - adm1026_read_value(client, ADM1026_REG_IN[i]); - } - - for (i = 0;i <= 7;++i) { - data->fan[i] = - adm1026_read_value(client, ADM1026_REG_FAN(i)); - } - - for (i = 0;i <= 2;++i) { - /* NOTE: temp[] is s8 and we assume 2's complement - * "conversion" in the assignment */ - data->temp[i] = - adm1026_read_value(client, ADM1026_REG_TEMP[i]); - } - - data->pwm1.pwm = adm1026_read_value(client, - ADM1026_REG_PWM); - data->analog_out = adm1026_read_value(client, - ADM1026_REG_DAC); - /* GPIO16 is MSbit of alarms, move it to gpio */ - alarms = adm1026_read_value(client, ADM1026_REG_STATUS4); - gpio = alarms & 0x80 ? 0x0100 : 0; /* GPIO16 */ - alarms &= 0x7f; - alarms <<= 8; - alarms |= adm1026_read_value(client, ADM1026_REG_STATUS3); - alarms <<= 8; - alarms |= adm1026_read_value(client, ADM1026_REG_STATUS2); - alarms <<= 8; - alarms |= adm1026_read_value(client, ADM1026_REG_STATUS1); - data->alarms = alarms; - - /* Read the GPIO values */ - gpio |= adm1026_read_value(client, - ADM1026_REG_GPIO_STATUS_8_15); - gpio <<= 8; - gpio |= adm1026_read_value(client, - ADM1026_REG_GPIO_STATUS_0_7); - data->gpio = gpio; - - data->last_reading = jiffies; - }; /* last_reading */ - - if (!data->valid || - time_after(jiffies, data->last_config + ADM1026_CONFIG_INTERVAL)) { - /* Things that don't change often */ - dev_dbg(&client->dev, "Reading config values\n"); - for (i = 0;i <= 16;++i) { - data->in_min[i] = adm1026_read_value(client, - ADM1026_REG_IN_MIN[i]); - data->in_max[i] = adm1026_read_value(client, - ADM1026_REG_IN_MAX[i]); - } - - value = adm1026_read_value(client, ADM1026_REG_FAN_DIV_0_3) - | (adm1026_read_value(client, ADM1026_REG_FAN_DIV_4_7) - << 8); - for (i = 0;i <= 7;++i) { - data->fan_min[i] = adm1026_read_value(client, - ADM1026_REG_FAN_MIN(i)); - data->fan_div[i] = DIV_FROM_REG(value & 0x03); - value >>= 2; - } - - for (i = 0; i <= 2; ++i) { - /* NOTE: temp_xxx[] are s8 and we assume 2's - * complement "conversion" in the assignment - */ - data->temp_min[i] = adm1026_read_value(client, - ADM1026_REG_TEMP_MIN[i]); - data->temp_max[i] = adm1026_read_value(client, - ADM1026_REG_TEMP_MAX[i]); - data->temp_tmin[i] = adm1026_read_value(client, - ADM1026_REG_TEMP_TMIN[i]); - data->temp_crit[i] = adm1026_read_value(client, - ADM1026_REG_TEMP_THERM[i]); - data->temp_offset[i] = adm1026_read_value(client, - ADM1026_REG_TEMP_OFFSET[i]); - } - - /* Read the STATUS/alarm masks */ - alarms = adm1026_read_value(client, ADM1026_REG_MASK4); - gpio = alarms & 0x80 ? 0x0100 : 0; /* GPIO16 */ - alarms = (alarms & 0x7f) << 8; - alarms |= adm1026_read_value(client, ADM1026_REG_MASK3); - alarms <<= 8; - alarms |= adm1026_read_value(client, ADM1026_REG_MASK2); - alarms <<= 8; - alarms |= adm1026_read_value(client, ADM1026_REG_MASK1); - data->alarm_mask = alarms; - - /* Read the GPIO values */ - gpio |= adm1026_read_value(client, - ADM1026_REG_GPIO_MASK_8_15); - gpio <<= 8; - gpio |= adm1026_read_value(client, ADM1026_REG_GPIO_MASK_0_7); - data->gpio_mask = gpio; - - /* Read various values from CONFIG1 */ - data->config1 = adm1026_read_value(client, - ADM1026_REG_CONFIG1); - if (data->config1 & CFG1_PWM_AFC) { - data->pwm1.enable = 2; - data->pwm1.auto_pwm_min = - PWM_MIN_FROM_REG(data->pwm1.pwm); - } - /* Read the GPIO config */ - data->config2 = adm1026_read_value(client, - ADM1026_REG_CONFIG2); - data->config3 = adm1026_read_value(client, - ADM1026_REG_CONFIG3); - data->gpio_config[16] = (data->config3 >> 6) & 0x03; - - value = 0; - for (i = 0;i <= 15;++i) { - if ((i & 0x03) == 0) { - value = adm1026_read_value(client, - ADM1026_REG_GPIO_CFG_0_3 + i/4); - } - data->gpio_config[i] = value & 0x03; - value >>= 2; - } - - data->last_config = jiffies; - }; /* last_config */ - - dev_dbg(&client->dev, "Setting VID from GPIO11-15.\n"); - data->vid = (data->gpio >> 11) & 0x1f; - data->valid = 1; - up(&data->update_lock); - return data; -} - -static ssize_t show_in(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in[nr])); -} -static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in_min[nr])); -} -static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->in_min[nr] = INS_TO_REG(nr, val); - adm1026_write_value(client, ADM1026_REG_IN_MIN[nr], data->in_min[nr]); - up(&data->update_lock); - return count; -} -static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in_max[nr])); -} -static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->in_max[nr] = INS_TO_REG(nr, val); - adm1026_write_value(client, ADM1026_REG_IN_MAX[nr], data->in_max[nr]); - up(&data->update_lock); - return count; -} - -#define in_reg(offset) \ -static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in, \ - NULL, offset); \ -static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ - show_in_min, set_in_min, offset); \ -static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ - show_in_max, set_in_max, offset); - - -in_reg(0); -in_reg(1); -in_reg(2); -in_reg(3); -in_reg(4); -in_reg(5); -in_reg(6); -in_reg(7); -in_reg(8); -in_reg(9); -in_reg(10); -in_reg(11); -in_reg(12); -in_reg(13); -in_reg(14); -in_reg(15); - -static ssize_t show_in16(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", INS_FROM_REG(16, data->in[16]) - - NEG12_OFFSET); -} -static ssize_t show_in16_min(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", INS_FROM_REG(16, data->in_min[16]) - - NEG12_OFFSET); -} -static ssize_t set_in16_min(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->in_min[16] = INS_TO_REG(16, val + NEG12_OFFSET); - adm1026_write_value(client, ADM1026_REG_IN_MIN[16], data->in_min[16]); - up(&data->update_lock); - return count; -} -static ssize_t show_in16_max(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", INS_FROM_REG(16, data->in_max[16]) - - NEG12_OFFSET); -} -static ssize_t set_in16_max(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->in_max[16] = INS_TO_REG(16, val+NEG12_OFFSET); - adm1026_write_value(client, ADM1026_REG_IN_MAX[16], data->in_max[16]); - up(&data->update_lock); - return count; -} - -static SENSOR_DEVICE_ATTR(in16_input, S_IRUGO, show_in16, NULL, 16); -static SENSOR_DEVICE_ATTR(in16_min, S_IRUGO | S_IWUSR, show_in16_min, set_in16_min, 16); -static SENSOR_DEVICE_ATTR(in16_max, S_IRUGO | S_IWUSR, show_in16_max, set_in16_max, 16); - - - - -/* Now add fan read/write functions */ - -static ssize_t show_fan(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr], - data->fan_div[nr])); -} -static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr], - data->fan_div[nr])); -} -static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->fan_min[nr] = FAN_TO_REG(val, data->fan_div[nr]); - adm1026_write_value(client, ADM1026_REG_FAN_MIN(nr), - data->fan_min[nr]); - up(&data->update_lock); - return count; -} - -#define fan_offset(offset) \ -static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan, NULL, \ - offset - 1); \ -static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan_min, set_fan_min, offset - 1); - -fan_offset(1); -fan_offset(2); -fan_offset(3); -fan_offset(4); -fan_offset(5); -fan_offset(6); -fan_offset(7); -fan_offset(8); - -/* Adjust fan_min to account for new fan divisor */ -static void fixup_fan_min(struct device *dev, int fan, int old_div) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int new_min; - int new_div = data->fan_div[fan]; - - /* 0 and 0xff are special. Don't adjust them */ - if (data->fan_min[fan] == 0 || data->fan_min[fan] == 0xff) { - return; - } - - new_min = data->fan_min[fan] * old_div / new_div; - new_min = SENSORS_LIMIT(new_min, 1, 254); - data->fan_min[fan] = new_min; - adm1026_write_value(client, ADM1026_REG_FAN_MIN(fan), new_min); -} - -/* Now add fan_div read/write functions */ -static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", data->fan_div[nr]); -} -static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val,orig_div,new_div,shift; - - val = simple_strtol(buf, NULL, 10); - new_div = DIV_TO_REG(val); - if (new_div == 0) { - return -EINVAL; - } - down(&data->update_lock); - orig_div = data->fan_div[nr]; - data->fan_div[nr] = DIV_FROM_REG(new_div); - - if (nr < 4) { /* 0 <= nr < 4 */ - shift = 2 * nr; - adm1026_write_value(client, ADM1026_REG_FAN_DIV_0_3, - ((DIV_TO_REG(orig_div) & (~(0x03 << shift))) | - (new_div << shift))); - } else { /* 3 < nr < 8 */ - shift = 2 * (nr - 4); - adm1026_write_value(client, ADM1026_REG_FAN_DIV_4_7, - ((DIV_TO_REG(orig_div) & (~(0x03 << (2 * shift)))) | - (new_div << shift))); - } - - if (data->fan_div[nr] != orig_div) { - fixup_fan_min(dev,nr,orig_div); - } - up(&data->update_lock); - return count; -} - -#define fan_offset_div(offset) \ -static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ - show_fan_div, set_fan_div, offset - 1); - -fan_offset_div(1); -fan_offset_div(2); -fan_offset_div(3); -fan_offset_div(4); -fan_offset_div(5); -fan_offset_div(6); -fan_offset_div(7); -fan_offset_div(8); - -/* Temps */ -static ssize_t show_temp(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp[nr])); -} -static ssize_t show_temp_min(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_min[nr])); -} -static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_min[nr] = TEMP_TO_REG(val); - adm1026_write_value(client, ADM1026_REG_TEMP_MIN[nr], - data->temp_min[nr]); - up(&data->update_lock); - return count; -} -static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_max[nr])); -} -static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_max[nr] = TEMP_TO_REG(val); - adm1026_write_value(client, ADM1026_REG_TEMP_MAX[nr], - data->temp_max[nr]); - up(&data->update_lock); - return count; -} - -#define temp_reg(offset) \ -static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp, \ - NULL, offset - 1); \ -static SENSOR_DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ - show_temp_min, set_temp_min, offset - 1); \ -static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ - show_temp_max, set_temp_max, offset - 1); - - -temp_reg(1); -temp_reg(2); -temp_reg(3); - -static ssize_t show_temp_offset(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_offset[nr])); -} -static ssize_t set_temp_offset(struct device *dev, - struct device_attribute *attr, const char *buf, - size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_offset[nr] = TEMP_TO_REG(val); - adm1026_write_value(client, ADM1026_REG_TEMP_OFFSET[nr], - data->temp_offset[nr]); - up(&data->update_lock); - return count; -} - -#define temp_offset_reg(offset) \ -static SENSOR_DEVICE_ATTR(temp##offset##_offset, S_IRUGO | S_IWUSR, \ - show_temp_offset, set_temp_offset, offset - 1); - -temp_offset_reg(1); -temp_offset_reg(2); -temp_offset_reg(3); - -static ssize_t show_temp_auto_point1_temp_hyst(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG( - ADM1026_FAN_ACTIVATION_TEMP_HYST + data->temp_tmin[nr])); -} -static ssize_t show_temp_auto_point2_temp(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_tmin[nr] + - ADM1026_FAN_CONTROL_TEMP_RANGE)); -} -static ssize_t show_temp_auto_point1_temp(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_tmin[nr])); -} -static ssize_t set_temp_auto_point1_temp(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_tmin[nr] = TEMP_TO_REG(val); - adm1026_write_value(client, ADM1026_REG_TEMP_TMIN[nr], - data->temp_tmin[nr]); - up(&data->update_lock); - return count; -} - -#define temp_auto_point(offset) \ -static SENSOR_DEVICE_ATTR(temp##offset##_auto_point1_temp, S_IRUGO | S_IWUSR, \ - show_temp_auto_point1_temp, set_temp_auto_point1_temp, \ - offset - 1); \ -static SENSOR_DEVICE_ATTR(temp##offset##_auto_point1_temp_hyst, S_IRUGO, \ - show_temp_auto_point1_temp_hyst, NULL, offset - 1); \ -static SENSOR_DEVICE_ATTR(temp##offset##_auto_point2_temp, S_IRUGO, \ - show_temp_auto_point2_temp, NULL, offset - 1); - -temp_auto_point(1); -temp_auto_point(2); -temp_auto_point(3); - -static ssize_t show_temp_crit_enable(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", (data->config1 & CFG1_THERM_HOT) >> 4); -} -static ssize_t set_temp_crit_enable(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - if ((val == 1) || (val==0)) { - down(&data->update_lock); - data->config1 = (data->config1 & ~CFG1_THERM_HOT) | (val << 4); - adm1026_write_value(client, ADM1026_REG_CONFIG1, - data->config1); - up(&data->update_lock); - } - return count; -} - -#define temp_crit_enable(offset) \ -static DEVICE_ATTR(temp##offset##_crit_enable, S_IRUGO | S_IWUSR, \ - show_temp_crit_enable, set_temp_crit_enable); - -temp_crit_enable(1); -temp_crit_enable(2); -temp_crit_enable(3); - -static ssize_t show_temp_crit(struct device *dev, - struct device_attribute *attr, char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_crit[nr])); -} -static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_crit[nr] = TEMP_TO_REG(val); - adm1026_write_value(client, ADM1026_REG_TEMP_THERM[nr], - data->temp_crit[nr]); - up(&data->update_lock); - return count; -} - -#define temp_crit_reg(offset) \ -static SENSOR_DEVICE_ATTR(temp##offset##_crit, S_IRUGO | S_IWUSR, \ - show_temp_crit, set_temp_crit, offset - 1); - -temp_crit_reg(1); -temp_crit_reg(2); -temp_crit_reg(3); - -static ssize_t show_analog_out_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", DAC_FROM_REG(data->analog_out)); -} -static ssize_t set_analog_out_reg(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->analog_out = DAC_TO_REG(val); - adm1026_write_value(client, ADM1026_REG_DAC, data->analog_out); - up(&data->update_lock); - return count; -} - -static DEVICE_ATTR(analog_out, S_IRUGO | S_IWUSR, show_analog_out_reg, - set_analog_out_reg); - -static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", vid_from_reg(data->vid & 0x3f, data->vrm)); -} -/* vid deprecated in favour of cpu0_vid, remove after 2005-11-11 */ -static DEVICE_ATTR(vid, S_IRUGO, show_vid_reg, NULL); -static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); - -static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", data->vrm); -} -static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - - data->vrm = simple_strtol(buf, NULL, 10); - return count; -} - -static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); - -static ssize_t show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf, "%ld\n", (long) (data->alarms)); -} - -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); - -static ssize_t show_alarm_mask(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%ld\n", data->alarm_mask); -} -static ssize_t set_alarm_mask(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - unsigned long mask; - - down(&data->update_lock); - data->alarm_mask = val & 0x7fffffff; - mask = data->alarm_mask - | (data->gpio_mask & 0x10000 ? 0x80000000 : 0); - adm1026_write_value(client, ADM1026_REG_MASK1, - mask & 0xff); - mask >>= 8; - adm1026_write_value(client, ADM1026_REG_MASK2, - mask & 0xff); - mask >>= 8; - adm1026_write_value(client, ADM1026_REG_MASK3, - mask & 0xff); - mask >>= 8; - adm1026_write_value(client, ADM1026_REG_MASK4, - mask & 0xff); - up(&data->update_lock); - return count; -} - -static DEVICE_ATTR(alarm_mask, S_IRUGO | S_IWUSR, show_alarm_mask, - set_alarm_mask); - - -static ssize_t show_gpio(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%ld\n", data->gpio); -} -static ssize_t set_gpio(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - long gpio; - - down(&data->update_lock); - data->gpio = val & 0x1ffff; - gpio = data->gpio; - adm1026_write_value(client, ADM1026_REG_GPIO_STATUS_0_7,gpio & 0xff); - gpio >>= 8; - adm1026_write_value(client, ADM1026_REG_GPIO_STATUS_8_15,gpio & 0xff); - gpio = ((gpio >> 1) & 0x80) | (data->alarms >> 24 & 0x7f); - adm1026_write_value(client, ADM1026_REG_STATUS4,gpio & 0xff); - up(&data->update_lock); - return count; -} - -static DEVICE_ATTR(gpio, S_IRUGO | S_IWUSR, show_gpio, set_gpio); - - -static ssize_t show_gpio_mask(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%ld\n", data->gpio_mask); -} -static ssize_t set_gpio_mask(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - long mask; - - down(&data->update_lock); - data->gpio_mask = val & 0x1ffff; - mask = data->gpio_mask; - adm1026_write_value(client, ADM1026_REG_GPIO_MASK_0_7,mask & 0xff); - mask >>= 8; - adm1026_write_value(client, ADM1026_REG_GPIO_MASK_8_15,mask & 0xff); - mask = ((mask >> 1) & 0x80) | (data->alarm_mask >> 24 & 0x7f); - adm1026_write_value(client, ADM1026_REG_MASK1,mask & 0xff); - up(&data->update_lock); - return count; -} - -static DEVICE_ATTR(gpio_mask, S_IRUGO | S_IWUSR, show_gpio_mask, set_gpio_mask); - -static ssize_t show_pwm_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", PWM_FROM_REG(data->pwm1.pwm)); -} -static ssize_t set_pwm_reg(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - - if (data->pwm1.enable == 1) { - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->pwm1.pwm = PWM_TO_REG(val); - adm1026_write_value(client, ADM1026_REG_PWM, data->pwm1.pwm); - up(&data->update_lock); - } - return count; -} -static ssize_t show_auto_pwm_min(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", data->pwm1.auto_pwm_min); -} -static ssize_t set_auto_pwm_min(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->pwm1.auto_pwm_min = SENSORS_LIMIT(val,0,255); - if (data->pwm1.enable == 2) { /* apply immediately */ - data->pwm1.pwm = PWM_TO_REG((data->pwm1.pwm & 0x0f) | - PWM_MIN_TO_REG(data->pwm1.auto_pwm_min)); - adm1026_write_value(client, ADM1026_REG_PWM, data->pwm1.pwm); - } - up(&data->update_lock); - return count; -} -static ssize_t show_auto_pwm_max(struct device *dev, struct device_attribute *attr, char *buf) -{ - return sprintf(buf,"%d\n", ADM1026_PWM_MAX); -} -static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1026_data *data = adm1026_update_device(dev); - return sprintf(buf,"%d\n", data->pwm1.enable); -} -static ssize_t set_pwm_enable(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1026_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - int old_enable; - - if ((val >= 0) && (val < 3)) { - down(&data->update_lock); - old_enable = data->pwm1.enable; - data->pwm1.enable = val; - data->config1 = (data->config1 & ~CFG1_PWM_AFC) - | ((val == 2) ? CFG1_PWM_AFC : 0); - adm1026_write_value(client, ADM1026_REG_CONFIG1, - data->config1); - if (val == 2) { /* apply pwm1_auto_pwm_min to pwm1 */ - data->pwm1.pwm = PWM_TO_REG((data->pwm1.pwm & 0x0f) | - PWM_MIN_TO_REG(data->pwm1.auto_pwm_min)); - adm1026_write_value(client, ADM1026_REG_PWM, - data->pwm1.pwm); - } else if (!((old_enable == 1) && (val == 1))) { - /* set pwm to safe value */ - data->pwm1.pwm = 255; - adm1026_write_value(client, ADM1026_REG_PWM, - data->pwm1.pwm); - } - up(&data->update_lock); - } - return count; -} - -/* enable PWM fan control */ -static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm_reg, set_pwm_reg); -static DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, show_pwm_reg, set_pwm_reg); -static DEVICE_ATTR(pwm3, S_IRUGO | S_IWUSR, show_pwm_reg, set_pwm_reg); -static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, show_pwm_enable, - set_pwm_enable); -static DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, show_pwm_enable, - set_pwm_enable); -static DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, show_pwm_enable, - set_pwm_enable); -static DEVICE_ATTR(temp1_auto_point1_pwm, S_IRUGO | S_IWUSR, - show_auto_pwm_min, set_auto_pwm_min); -static DEVICE_ATTR(temp2_auto_point1_pwm, S_IRUGO | S_IWUSR, - show_auto_pwm_min, set_auto_pwm_min); -static DEVICE_ATTR(temp3_auto_point1_pwm, S_IRUGO | S_IWUSR, - show_auto_pwm_min, set_auto_pwm_min); - -static DEVICE_ATTR(temp1_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL); -static DEVICE_ATTR(temp2_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL); -static DEVICE_ATTR(temp3_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL); - -int adm1026_detect(struct i2c_adapter *adapter, int address, - int kind) -{ - int company, verstep; - struct i2c_client *new_client; - struct adm1026_data *data; - int err = 0; - const char *type_name = ""; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - /* We need to be able to do byte I/O */ - goto exit; - }; - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access adm1026_{read,write}_value. */ - - if (!(data = kmalloc(sizeof(struct adm1026_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - - memset(data, 0, sizeof(struct adm1026_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &adm1026_driver; - new_client->flags = 0; - - /* Now, we do the remaining detection. */ - - company = adm1026_read_value(new_client, ADM1026_REG_COMPANY); - verstep = adm1026_read_value(new_client, ADM1026_REG_VERSTEP); - - dev_dbg(&new_client->dev, "Detecting device at %d,0x%02x with" - " COMPANY: 0x%02x and VERSTEP: 0x%02x\n", - i2c_adapter_id(new_client->adapter), new_client->addr, - company, verstep); - - /* If auto-detecting, Determine the chip type. */ - if (kind <= 0) { - dev_dbg(&new_client->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(&new_client->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); - } - err = 0; - goto exitfree; - } - } - - /* Fill in the chip specific driver values */ - switch (kind) { - case any_chip : - type_name = "adm1026"; - break; - case adm1026 : - type_name = "adm1026"; - break; - default : - dev_err(&adapter->dev, ": Internal error, invalid " - "kind (%d)!", kind); - err = -EFAULT; - goto exitfree; - } - strlcpy(new_client->name, type_name, I2C_NAME_SIZE); - - /* Fill in the remaining client fields */ - data->type = kind; - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exitfree; - - /* Set the VRM version */ - data->vrm = i2c_which_vrm(); - - /* Initialize the ADM1026 chip */ - adm1026_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &sensor_dev_attr_in0_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in0_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in0_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in1_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in1_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in1_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in2_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in2_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in3_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in3_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in3_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in4_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in4_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in4_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in5_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in5_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in5_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in6_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in6_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in6_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in7_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in7_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in7_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in8_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in8_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in8_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in9_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in9_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in9_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in10_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in10_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in10_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in11_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in11_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in11_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in12_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in12_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in12_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in13_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in13_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in13_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in14_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in14_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in14_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in15_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in15_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in15_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in16_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in16_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in16_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan1_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan1_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan1_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan2_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan2_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan3_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan3_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan3_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan4_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan4_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan4_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan5_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan5_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan5_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan6_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan6_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan6_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan7_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan7_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan7_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan8_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan8_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan8_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_offset.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_offset.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_offset.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_auto_point1_temp.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_auto_point1_temp.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_auto_point1_temp.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_auto_point1_temp_hyst.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_auto_point1_temp_hyst.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_auto_point2_temp.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_auto_point2_temp.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_auto_point2_temp.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_crit.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_crit.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_crit.dev_attr); - device_create_file(&new_client->dev, &dev_attr_temp1_crit_enable); - device_create_file(&new_client->dev, &dev_attr_temp2_crit_enable); - device_create_file(&new_client->dev, &dev_attr_temp3_crit_enable); - /* vid deprecated in favour of cpu0_vid, remove after 2005-11-11 */ - device_create_file(&new_client->dev, &dev_attr_vid); - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - device_create_file(&new_client->dev, &dev_attr_vrm); - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_alarm_mask); - device_create_file(&new_client->dev, &dev_attr_gpio); - device_create_file(&new_client->dev, &dev_attr_gpio_mask); - device_create_file(&new_client->dev, &dev_attr_pwm1); - device_create_file(&new_client->dev, &dev_attr_pwm2); - device_create_file(&new_client->dev, &dev_attr_pwm3); - device_create_file(&new_client->dev, &dev_attr_pwm1_enable); - device_create_file(&new_client->dev, &dev_attr_pwm2_enable); - device_create_file(&new_client->dev, &dev_attr_pwm3_enable); - device_create_file(&new_client->dev, &dev_attr_temp1_auto_point1_pwm); - device_create_file(&new_client->dev, &dev_attr_temp2_auto_point1_pwm); - device_create_file(&new_client->dev, &dev_attr_temp3_auto_point1_pwm); - device_create_file(&new_client->dev, &dev_attr_temp1_auto_point2_pwm); - device_create_file(&new_client->dev, &dev_attr_temp2_auto_point2_pwm); - device_create_file(&new_client->dev, &dev_attr_temp3_auto_point2_pwm); - device_create_file(&new_client->dev, &dev_attr_analog_out); - return 0; - - /* Error out and cleanup code */ -exitfree: - kfree(new_client); -exit: - return err; -} -static int __init sm_adm1026_init(void) -{ - return i2c_add_driver(&adm1026_driver); -} - -static void __exit sm_adm1026_exit(void) -{ - i2c_del_driver(&adm1026_driver); -} - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Philip Pokorny , " - "Justin Thiessen "); -MODULE_DESCRIPTION("ADM1026 driver"); - -module_init(sm_adm1026_init); -module_exit(sm_adm1026_exit); diff --git a/drivers/i2c/chips/adm1031.c b/drivers/i2c/chips/adm1031.c deleted file mode 100644 index 9168e983ca1d..000000000000 --- a/drivers/i2c/chips/adm1031.c +++ /dev/null @@ -1,977 +0,0 @@ -/* - adm1031.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Based on lm75.c and lm85.c - Supports adm1030 / adm1031 - Copyright (C) 2004 Alexandre d'Alton - Reworked by Jean Delvare - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include - -/* Following macros takes channel parameter starting from 0 to 2 */ -#define ADM1031_REG_FAN_SPEED(nr) (0x08 + (nr)) -#define ADM1031_REG_FAN_DIV(nr) (0x20 + (nr)) -#define ADM1031_REG_PWM (0x22) -#define ADM1031_REG_FAN_MIN(nr) (0x10 + (nr)) - -#define ADM1031_REG_TEMP_MAX(nr) (0x14 + 4*(nr)) -#define ADM1031_REG_TEMP_MIN(nr) (0x15 + 4*(nr)) -#define ADM1031_REG_TEMP_CRIT(nr) (0x16 + 4*(nr)) - -#define ADM1031_REG_TEMP(nr) (0xa + (nr)) -#define ADM1031_REG_AUTO_TEMP(nr) (0x24 + (nr)) - -#define ADM1031_REG_STATUS(nr) (0x2 + (nr)) - -#define ADM1031_REG_CONF1 0x0 -#define ADM1031_REG_CONF2 0x1 -#define ADM1031_REG_EXT_TEMP 0x6 - -#define ADM1031_CONF1_MONITOR_ENABLE 0x01 /* Monitoring enable */ -#define ADM1031_CONF1_PWM_INVERT 0x08 /* PWM Invert */ -#define ADM1031_CONF1_AUTO_MODE 0x80 /* Auto FAN */ - -#define ADM1031_CONF2_PWM1_ENABLE 0x01 -#define ADM1031_CONF2_PWM2_ENABLE 0x02 -#define ADM1031_CONF2_TACH1_ENABLE 0x04 -#define ADM1031_CONF2_TACH2_ENABLE 0x08 -#define ADM1031_CONF2_TEMP_ENABLE(chan) (0x10 << (chan)) - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_2(adm1030, adm1031); - -typedef u8 auto_chan_table_t[8][2]; - -/* Each client has this additional data */ -struct adm1031_data { - struct i2c_client client; - struct semaphore update_lock; - int chip_type; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - /* The chan_select_table contains the possible configurations for - * auto fan control. - */ - auto_chan_table_t *chan_select_table; - u16 alarm; - u8 conf1; - u8 conf2; - u8 fan[2]; - u8 fan_div[2]; - u8 fan_min[2]; - u8 pwm[2]; - u8 old_pwm[2]; - s8 temp[3]; - u8 ext_temp[3]; - u8 auto_temp[3]; - u8 auto_temp_min[3]; - u8 auto_temp_off[3]; - u8 auto_temp_max[3]; - s8 temp_min[3]; - s8 temp_max[3]; - s8 temp_crit[3]; -}; - -static int adm1031_attach_adapter(struct i2c_adapter *adapter); -static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind); -static void adm1031_init_client(struct i2c_client *client); -static int adm1031_detach_client(struct i2c_client *client); -static struct adm1031_data *adm1031_update_device(struct device *dev); - -/* This is the driver that will be inserted */ -static struct i2c_driver adm1031_driver = { - .owner = THIS_MODULE, - .name = "adm1031", - .flags = I2C_DF_NOTIFY, - .attach_adapter = adm1031_attach_adapter, - .detach_client = adm1031_detach_client, -}; - -static inline u8 adm1031_read_value(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static inline int -adm1031_write_value(struct i2c_client *client, u8 reg, unsigned int value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - - -#define TEMP_TO_REG(val) (((val) < 0 ? ((val - 500) / 1000) : \ - ((val + 500) / 1000))) - -#define TEMP_FROM_REG(val) ((val) * 1000) - -#define TEMP_FROM_REG_EXT(val, ext) (TEMP_FROM_REG(val) + (ext) * 125) - -#define FAN_FROM_REG(reg, div) ((reg) ? (11250 * 60) / ((reg) * (div)) : 0) - -static int FAN_TO_REG(int reg, int div) -{ - int tmp; - tmp = FAN_FROM_REG(SENSORS_LIMIT(reg, 0, 65535), div); - return tmp > 255 ? 255 : tmp; -} - -#define FAN_DIV_FROM_REG(reg) (1<<(((reg)&0xc0)>>6)) - -#define PWM_TO_REG(val) (SENSORS_LIMIT((val), 0, 255) >> 4) -#define PWM_FROM_REG(val) ((val) << 4) - -#define FAN_CHAN_FROM_REG(reg) (((reg) >> 5) & 7) -#define FAN_CHAN_TO_REG(val, reg) \ - (((reg) & 0x1F) | (((val) << 5) & 0xe0)) - -#define AUTO_TEMP_MIN_TO_REG(val, reg) \ - ((((val)/500) & 0xf8)|((reg) & 0x7)) -#define AUTO_TEMP_RANGE_FROM_REG(reg) (5000 * (1<< ((reg)&0x7))) -#define AUTO_TEMP_MIN_FROM_REG(reg) (1000 * ((((reg) >> 3) & 0x1f) << 2)) - -#define AUTO_TEMP_MIN_FROM_REG_DEG(reg) ((((reg) >> 3) & 0x1f) << 2) - -#define AUTO_TEMP_OFF_FROM_REG(reg) \ - (AUTO_TEMP_MIN_FROM_REG(reg) - 5000) - -#define AUTO_TEMP_MAX_FROM_REG(reg) \ - (AUTO_TEMP_RANGE_FROM_REG(reg) + \ - AUTO_TEMP_MIN_FROM_REG(reg)) - -static int AUTO_TEMP_MAX_TO_REG(int val, int reg, int pwm) -{ - int ret; - int range = val - AUTO_TEMP_MIN_FROM_REG(reg); - - range = ((val - AUTO_TEMP_MIN_FROM_REG(reg))*10)/(16 - pwm); - ret = ((reg & 0xf8) | - (range < 10000 ? 0 : - range < 20000 ? 1 : - range < 40000 ? 2 : range < 80000 ? 3 : 4)); - return ret; -} - -/* FAN auto control */ -#define GET_FAN_AUTO_BITFIELD(data, idx) \ - (*(data)->chan_select_table)[FAN_CHAN_FROM_REG((data)->conf1)][idx%2] - -/* The tables below contains the possible values for the auto fan - * control bitfields. the index in the table is the register value. - * MSb is the auto fan control enable bit, so the four first entries - * in the table disables auto fan control when both bitfields are zero. - */ -static auto_chan_table_t auto_channel_select_table_adm1031 = { - {0, 0}, {0, 0}, {0, 0}, {0, 0}, - {2 /*0b010 */ , 4 /*0b100 */ }, - {2 /*0b010 */ , 2 /*0b010 */ }, - {4 /*0b100 */ , 4 /*0b100 */ }, - {7 /*0b111 */ , 7 /*0b111 */ }, -}; - -static auto_chan_table_t auto_channel_select_table_adm1030 = { - {0, 0}, {0, 0}, {0, 0}, {0, 0}, - {2 /*0b10 */ , 0}, - {0xff /*invalid */ , 0}, - {0xff /*invalid */ , 0}, - {3 /*0b11 */ , 0}, -}; - -/* That function checks if a bitfield is valid and returns the other bitfield - * nearest match if no exact match where found. - */ -static int -get_fan_auto_nearest(struct adm1031_data *data, - int chan, u8 val, u8 reg, u8 * new_reg) -{ - int i; - int first_match = -1, exact_match = -1; - u8 other_reg_val = - (*data->chan_select_table)[FAN_CHAN_FROM_REG(reg)][chan ? 0 : 1]; - - if (val == 0) { - *new_reg = 0; - return 0; - } - - for (i = 0; i < 8; i++) { - if ((val == (*data->chan_select_table)[i][chan]) && - ((*data->chan_select_table)[i][chan ? 0 : 1] == - other_reg_val)) { - /* We found an exact match */ - exact_match = i; - break; - } else if (val == (*data->chan_select_table)[i][chan] && - first_match == -1) { - /* Save the first match in case of an exact match has not been - * found - */ - first_match = i; - } - } - - if (exact_match >= 0) { - *new_reg = exact_match; - } else if (first_match >= 0) { - *new_reg = first_match; - } else { - return -EINVAL; - } - return 0; -} - -static ssize_t show_fan_auto_channel(struct device *dev, char *buf, int nr) -{ - struct adm1031_data *data = adm1031_update_device(dev); - return sprintf(buf, "%d\n", GET_FAN_AUTO_BITFIELD(data, nr)); -} - -static ssize_t -set_fan_auto_channel(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1031_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - u8 reg; - int ret; - u8 old_fan_mode; - - old_fan_mode = data->conf1; - - down(&data->update_lock); - - if ((ret = get_fan_auto_nearest(data, nr, val, data->conf1, ®))) { - up(&data->update_lock); - return ret; - } - if (((data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1)) & ADM1031_CONF1_AUTO_MODE) ^ - (old_fan_mode & ADM1031_CONF1_AUTO_MODE)) { - if (data->conf1 & ADM1031_CONF1_AUTO_MODE){ - /* Switch to Auto Fan Mode - * Save PWM registers - * Set PWM registers to 33% Both */ - data->old_pwm[0] = data->pwm[0]; - data->old_pwm[1] = data->pwm[1]; - adm1031_write_value(client, ADM1031_REG_PWM, 0x55); - } else { - /* Switch to Manual Mode */ - data->pwm[0] = data->old_pwm[0]; - data->pwm[1] = data->old_pwm[1]; - /* Restore PWM registers */ - adm1031_write_value(client, ADM1031_REG_PWM, - data->pwm[0] | (data->pwm[1] << 4)); - } - } - data->conf1 = FAN_CHAN_TO_REG(reg, data->conf1); - adm1031_write_value(client, ADM1031_REG_CONF1, data->conf1); - up(&data->update_lock); - return count; -} - -#define fan_auto_channel_offset(offset) \ -static ssize_t show_fan_auto_channel_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_auto_channel(dev, buf, offset - 1); \ -} \ -static ssize_t set_fan_auto_channel_##offset (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_fan_auto_channel(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(auto_fan##offset##_channel, S_IRUGO | S_IWUSR, \ - show_fan_auto_channel_##offset, \ - set_fan_auto_channel_##offset) - -fan_auto_channel_offset(1); -fan_auto_channel_offset(2); - -/* Auto Temps */ -static ssize_t show_auto_temp_off(struct device *dev, char *buf, int nr) -{ - struct adm1031_data *data = adm1031_update_device(dev); - return sprintf(buf, "%d\n", - AUTO_TEMP_OFF_FROM_REG(data->auto_temp[nr])); -} -static ssize_t show_auto_temp_min(struct device *dev, char *buf, int nr) -{ - struct adm1031_data *data = adm1031_update_device(dev); - return sprintf(buf, "%d\n", - AUTO_TEMP_MIN_FROM_REG(data->auto_temp[nr])); -} -static ssize_t -set_auto_temp_min(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1031_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->auto_temp[nr] = AUTO_TEMP_MIN_TO_REG(val, data->auto_temp[nr]); - adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr), - data->auto_temp[nr]); - up(&data->update_lock); - return count; -} -static ssize_t show_auto_temp_max(struct device *dev, char *buf, int nr) -{ - struct adm1031_data *data = adm1031_update_device(dev); - return sprintf(buf, "%d\n", - AUTO_TEMP_MAX_FROM_REG(data->auto_temp[nr])); -} -static ssize_t -set_auto_temp_max(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1031_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_max[nr] = AUTO_TEMP_MAX_TO_REG(val, data->auto_temp[nr], data->pwm[nr]); - adm1031_write_value(client, ADM1031_REG_AUTO_TEMP(nr), - data->temp_max[nr]); - up(&data->update_lock); - return count; -} - -#define auto_temp_reg(offset) \ -static ssize_t show_auto_temp_##offset##_off (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_auto_temp_off(dev, buf, offset - 1); \ -} \ -static ssize_t show_auto_temp_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_auto_temp_min(dev, buf, offset - 1); \ -} \ -static ssize_t show_auto_temp_##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_auto_temp_max(dev, buf, offset - 1); \ -} \ -static ssize_t set_auto_temp_##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_auto_temp_min(dev, buf, count, offset - 1); \ -} \ -static ssize_t set_auto_temp_##offset##_max (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_auto_temp_max(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(auto_temp##offset##_off, S_IRUGO, \ - show_auto_temp_##offset##_off, NULL); \ -static DEVICE_ATTR(auto_temp##offset##_min, S_IRUGO | S_IWUSR, \ - show_auto_temp_##offset##_min, set_auto_temp_##offset##_min);\ -static DEVICE_ATTR(auto_temp##offset##_max, S_IRUGO | S_IWUSR, \ - show_auto_temp_##offset##_max, set_auto_temp_##offset##_max) - -auto_temp_reg(1); -auto_temp_reg(2); -auto_temp_reg(3); - -/* pwm */ -static ssize_t show_pwm(struct device *dev, char *buf, int nr) -{ - struct adm1031_data *data = adm1031_update_device(dev); - return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr])); -} -static ssize_t -set_pwm(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1031_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - int reg; - - down(&data->update_lock); - if ((data->conf1 & ADM1031_CONF1_AUTO_MODE) && - (((val>>4) & 0xf) != 5)) { - /* In automatic mode, the only PWM accepted is 33% */ - up(&data->update_lock); - return -EINVAL; - } - data->pwm[nr] = PWM_TO_REG(val); - reg = adm1031_read_value(client, ADM1031_REG_PWM); - adm1031_write_value(client, ADM1031_REG_PWM, - nr ? ((data->pwm[nr] << 4) & 0xf0) | (reg & 0xf) - : (data->pwm[nr] & 0xf) | (reg & 0xf0)); - up(&data->update_lock); - return count; -} - -#define pwm_reg(offset) \ -static ssize_t show_pwm_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_pwm(dev, buf, offset - 1); \ -} \ -static ssize_t set_pwm_##offset (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_pwm(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ - show_pwm_##offset, set_pwm_##offset) - -pwm_reg(1); -pwm_reg(2); - -/* Fans */ - -/* - * That function checks the cases where the fan reading is not - * relevant. It is used to provide 0 as fan reading when the fan is - * not supposed to run - */ -static int trust_fan_readings(struct adm1031_data *data, int chan) -{ - int res = 0; - - if (data->conf1 & ADM1031_CONF1_AUTO_MODE) { - switch (data->conf1 & 0x60) { - case 0x00: /* remote temp1 controls fan1 remote temp2 controls fan2 */ - res = data->temp[chan+1] >= - AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[chan+1]); - break; - case 0x20: /* remote temp1 controls both fans */ - res = - data->temp[1] >= - AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1]); - break; - case 0x40: /* remote temp2 controls both fans */ - res = - data->temp[2] >= - AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2]); - break; - case 0x60: /* max controls both fans */ - res = - data->temp[0] >= - AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[0]) - || data->temp[1] >= - AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[1]) - || (data->chip_type == adm1031 - && data->temp[2] >= - AUTO_TEMP_MIN_FROM_REG_DEG(data->auto_temp[2])); - break; - } - } else { - res = data->pwm[chan] > 0; - } - return res; -} - - -static ssize_t show_fan(struct device *dev, char *buf, int nr) -{ - struct adm1031_data *data = adm1031_update_device(dev); - int value; - - value = trust_fan_readings(data, nr) ? FAN_FROM_REG(data->fan[nr], - FAN_DIV_FROM_REG(data->fan_div[nr])) : 0; - return sprintf(buf, "%d\n", value); -} - -static ssize_t show_fan_div(struct device *dev, char *buf, int nr) -{ - struct adm1031_data *data = adm1031_update_device(dev); - return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[nr])); -} -static ssize_t show_fan_min(struct device *dev, char *buf, int nr) -{ - struct adm1031_data *data = adm1031_update_device(dev); - return sprintf(buf, "%d\n", - FAN_FROM_REG(data->fan_min[nr], - FAN_DIV_FROM_REG(data->fan_div[nr]))); -} -static ssize_t -set_fan_min(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1031_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - if (val) { - data->fan_min[nr] = - FAN_TO_REG(val, FAN_DIV_FROM_REG(data->fan_div[nr])); - } else { - data->fan_min[nr] = 0xff; - } - adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), data->fan_min[nr]); - up(&data->update_lock); - return count; -} -static ssize_t -set_fan_div(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1031_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - u8 tmp; - int old_div; - int new_min; - - tmp = val == 8 ? 0xc0 : - val == 4 ? 0x80 : - val == 2 ? 0x40 : - val == 1 ? 0x00 : - 0xff; - if (tmp == 0xff) - return -EINVAL; - - down(&data->update_lock); - old_div = FAN_DIV_FROM_REG(data->fan_div[nr]); - data->fan_div[nr] = (tmp & 0xC0) | (0x3f & data->fan_div[nr]); - new_min = data->fan_min[nr] * old_div / - FAN_DIV_FROM_REG(data->fan_div[nr]); - data->fan_min[nr] = new_min > 0xff ? 0xff : new_min; - data->fan[nr] = data->fan[nr] * old_div / - FAN_DIV_FROM_REG(data->fan_div[nr]); - - adm1031_write_value(client, ADM1031_REG_FAN_DIV(nr), - data->fan_div[nr]); - adm1031_write_value(client, ADM1031_REG_FAN_MIN(nr), - data->fan_min[nr]); - up(&data->update_lock); - return count; -} - -#define fan_offset(offset) \ -static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_min(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_div(dev, buf, offset - 1); \ -} \ -static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_fan_min(dev, buf, count, offset - 1); \ -} \ -static ssize_t set_fan_##offset##_div (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_fan_div(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, \ - NULL); \ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan_##offset##_min, set_fan_##offset##_min); \ -static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ - show_fan_##offset##_div, set_fan_##offset##_div); \ -static DEVICE_ATTR(auto_fan##offset##_min_pwm, S_IRUGO | S_IWUSR, \ - show_pwm_##offset, set_pwm_##offset) - -fan_offset(1); -fan_offset(2); - - -/* Temps */ -static ssize_t show_temp(struct device *dev, char *buf, int nr) -{ - struct adm1031_data *data = adm1031_update_device(dev); - int ext; - ext = nr == 0 ? - ((data->ext_temp[nr] >> 6) & 0x3) * 2 : - (((data->ext_temp[nr] >> ((nr - 1) * 3)) & 7)); - return sprintf(buf, "%d\n", TEMP_FROM_REG_EXT(data->temp[nr], ext)); -} -static ssize_t show_temp_min(struct device *dev, char *buf, int nr) -{ - struct adm1031_data *data = adm1031_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[nr])); -} -static ssize_t show_temp_max(struct device *dev, char *buf, int nr) -{ - struct adm1031_data *data = adm1031_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[nr])); -} -static ssize_t show_temp_crit(struct device *dev, char *buf, int nr) -{ - struct adm1031_data *data = adm1031_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[nr])); -} -static ssize_t -set_temp_min(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1031_data *data = i2c_get_clientdata(client); - int val; - - val = simple_strtol(buf, NULL, 10); - val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875); - down(&data->update_lock); - data->temp_min[nr] = TEMP_TO_REG(val); - adm1031_write_value(client, ADM1031_REG_TEMP_MIN(nr), - data->temp_min[nr]); - up(&data->update_lock); - return count; -} -static ssize_t -set_temp_max(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1031_data *data = i2c_get_clientdata(client); - int val; - - val = simple_strtol(buf, NULL, 10); - val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875); - down(&data->update_lock); - data->temp_max[nr] = TEMP_TO_REG(val); - adm1031_write_value(client, ADM1031_REG_TEMP_MAX(nr), - data->temp_max[nr]); - up(&data->update_lock); - return count; -} -static ssize_t -set_temp_crit(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1031_data *data = i2c_get_clientdata(client); - int val; - - val = simple_strtol(buf, NULL, 10); - val = SENSORS_LIMIT(val, -55000, nr == 0 ? 127750 : 127875); - down(&data->update_lock); - data->temp_crit[nr] = TEMP_TO_REG(val); - adm1031_write_value(client, ADM1031_REG_TEMP_CRIT(nr), - data->temp_crit[nr]); - up(&data->update_lock); - return count; -} - -#define temp_reg(offset) \ -static ssize_t show_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp(dev, buf, offset - 1); \ -} \ -static ssize_t show_temp_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp_min(dev, buf, offset - 1); \ -} \ -static ssize_t show_temp_##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp_max(dev, buf, offset - 1); \ -} \ -static ssize_t show_temp_##offset##_crit (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp_crit(dev, buf, offset - 1); \ -} \ -static ssize_t set_temp_##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_temp_min(dev, buf, count, offset - 1); \ -} \ -static ssize_t set_temp_##offset##_max (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_temp_max(dev, buf, count, offset - 1); \ -} \ -static ssize_t set_temp_##offset##_crit (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_temp_crit(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, \ - NULL); \ -static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ - show_temp_##offset##_min, set_temp_##offset##_min); \ -static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ - show_temp_##offset##_max, set_temp_##offset##_max); \ -static DEVICE_ATTR(temp##offset##_crit, S_IRUGO | S_IWUSR, \ - show_temp_##offset##_crit, set_temp_##offset##_crit) - -temp_reg(1); -temp_reg(2); -temp_reg(3); - -/* Alarms */ -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm1031_data *data = adm1031_update_device(dev); - return sprintf(buf, "%d\n", data->alarm); -} - -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - - -static int adm1031_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, adm1031_detect); -} - -/* This function is called by i2c_detect */ -static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct adm1031_data *data; - int err = 0; - const char *name = ""; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - if (!(data = kmalloc(sizeof(struct adm1031_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct adm1031_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &adm1031_driver; - new_client->flags = 0; - - if (kind < 0) { - int id, co; - id = i2c_smbus_read_byte_data(new_client, 0x3d); - co = i2c_smbus_read_byte_data(new_client, 0x3e); - - if (!((id == 0x31 || id == 0x30) && co == 0x41)) - goto exit_free; - kind = (id == 0x30) ? adm1030 : adm1031; - } - - if (kind <= 0) - kind = adm1031; - - /* Given the detected chip type, set the chip name and the - * auto fan control helper table. */ - if (kind == adm1030) { - name = "adm1030"; - data->chan_select_table = &auto_channel_select_table_adm1030; - } else if (kind == adm1031) { - name = "adm1031"; - data->chan_select_table = &auto_channel_select_table_adm1031; - } - data->chip_type = kind; - - strlcpy(new_client->name, name, I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the ADM1031 chip */ - adm1031_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_pwm1); - device_create_file(&new_client->dev, &dev_attr_auto_fan1_channel); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_crit); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp2_crit); - - device_create_file(&new_client->dev, &dev_attr_auto_temp1_off); - device_create_file(&new_client->dev, &dev_attr_auto_temp1_min); - device_create_file(&new_client->dev, &dev_attr_auto_temp1_max); - - device_create_file(&new_client->dev, &dev_attr_auto_temp2_off); - device_create_file(&new_client->dev, &dev_attr_auto_temp2_min); - device_create_file(&new_client->dev, &dev_attr_auto_temp2_max); - - device_create_file(&new_client->dev, &dev_attr_auto_fan1_min_pwm); - - device_create_file(&new_client->dev, &dev_attr_alarms); - - if (kind == adm1031) { - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_pwm2); - device_create_file(&new_client->dev, - &dev_attr_auto_fan2_channel); - device_create_file(&new_client->dev, &dev_attr_temp3_input); - device_create_file(&new_client->dev, &dev_attr_temp3_min); - device_create_file(&new_client->dev, &dev_attr_temp3_max); - device_create_file(&new_client->dev, &dev_attr_temp3_crit); - device_create_file(&new_client->dev, &dev_attr_auto_temp3_off); - device_create_file(&new_client->dev, &dev_attr_auto_temp3_min); - device_create_file(&new_client->dev, &dev_attr_auto_temp3_max); - device_create_file(&new_client->dev, &dev_attr_auto_fan2_min_pwm); - } - - return 0; - -exit_free: - kfree(new_client); -exit: - return err; -} - -static int adm1031_detach_client(struct i2c_client *client) -{ - int ret; - if ((ret = i2c_detach_client(client)) != 0) { - return ret; - } - kfree(client); - return 0; -} - -static void adm1031_init_client(struct i2c_client *client) -{ - unsigned int read_val; - unsigned int mask; - struct adm1031_data *data = i2c_get_clientdata(client); - - mask = (ADM1031_CONF2_PWM1_ENABLE | ADM1031_CONF2_TACH1_ENABLE); - if (data->chip_type == adm1031) { - mask |= (ADM1031_CONF2_PWM2_ENABLE | - ADM1031_CONF2_TACH2_ENABLE); - } - /* Initialize the ADM1031 chip (enables fan speed reading ) */ - read_val = adm1031_read_value(client, ADM1031_REG_CONF2); - if ((read_val | mask) != read_val) { - adm1031_write_value(client, ADM1031_REG_CONF2, read_val | mask); - } - - read_val = adm1031_read_value(client, ADM1031_REG_CONF1); - if ((read_val | ADM1031_CONF1_MONITOR_ENABLE) != read_val) { - adm1031_write_value(client, ADM1031_REG_CONF1, read_val | - ADM1031_CONF1_MONITOR_ENABLE); - } - -} - -static struct adm1031_data *adm1031_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm1031_data *data = i2c_get_clientdata(client); - int chan; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - - dev_dbg(&client->dev, "Starting adm1031 update\n"); - for (chan = 0; - chan < ((data->chip_type == adm1031) ? 3 : 2); chan++) { - u8 oldh, newh; - - oldh = - adm1031_read_value(client, ADM1031_REG_TEMP(chan)); - data->ext_temp[chan] = - adm1031_read_value(client, ADM1031_REG_EXT_TEMP); - newh = - adm1031_read_value(client, ADM1031_REG_TEMP(chan)); - if (newh != oldh) { - data->ext_temp[chan] = - adm1031_read_value(client, - ADM1031_REG_EXT_TEMP); -#ifdef DEBUG - oldh = - adm1031_read_value(client, - ADM1031_REG_TEMP(chan)); - - /* oldh is actually newer */ - if (newh != oldh) - dev_warn(&client->dev, - "Remote temperature may be " - "wrong.\n"); -#endif - } - data->temp[chan] = newh; - - data->temp_min[chan] = - adm1031_read_value(client, - ADM1031_REG_TEMP_MIN(chan)); - data->temp_max[chan] = - adm1031_read_value(client, - ADM1031_REG_TEMP_MAX(chan)); - data->temp_crit[chan] = - adm1031_read_value(client, - ADM1031_REG_TEMP_CRIT(chan)); - data->auto_temp[chan] = - adm1031_read_value(client, - ADM1031_REG_AUTO_TEMP(chan)); - - } - - data->conf1 = adm1031_read_value(client, ADM1031_REG_CONF1); - data->conf2 = adm1031_read_value(client, ADM1031_REG_CONF2); - - data->alarm = adm1031_read_value(client, ADM1031_REG_STATUS(0)) - | (adm1031_read_value(client, ADM1031_REG_STATUS(1)) - << 8); - if (data->chip_type == adm1030) { - data->alarm &= 0xc0ff; - } - - for (chan=0; chan<(data->chip_type == adm1030 ? 1 : 2); chan++) { - data->fan_div[chan] = - adm1031_read_value(client, ADM1031_REG_FAN_DIV(chan)); - data->fan_min[chan] = - adm1031_read_value(client, ADM1031_REG_FAN_MIN(chan)); - data->fan[chan] = - adm1031_read_value(client, ADM1031_REG_FAN_SPEED(chan)); - data->pwm[chan] = - 0xf & (adm1031_read_value(client, ADM1031_REG_PWM) >> - (4*chan)); - } - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_adm1031_init(void) -{ - return i2c_add_driver(&adm1031_driver); -} - -static void __exit sensors_adm1031_exit(void) -{ - i2c_del_driver(&adm1031_driver); -} - -MODULE_AUTHOR("Alexandre d'Alton "); -MODULE_DESCRIPTION("ADM1031/ADM1030 driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_adm1031_init); -module_exit(sensors_adm1031_exit); diff --git a/drivers/i2c/chips/adm9240.c b/drivers/i2c/chips/adm9240.c deleted file mode 100644 index 5c68e9c311aa..000000000000 --- a/drivers/i2c/chips/adm9240.c +++ /dev/null @@ -1,791 +0,0 @@ -/* - * adm9240.c Part of lm_sensors, Linux kernel modules for hardware - * monitoring - * - * Copyright (C) 1999 Frodo Looijaard - * Philip Edelbrock - * Copyright (C) 2003 Michiel Rook - * Copyright (C) 2005 Grant Coady with valuable - * guidance from Jean Delvare - * - * Driver supports Analog Devices ADM9240 - * Dallas Semiconductor DS1780 - * National Semiconductor LM81 - * - * ADM9240 is the reference, DS1780 and LM81 are register compatibles - * - * Voltage Six inputs are scaled by chip, VID also reported - * Temperature Chip temperature to 0.5'C, maximum and max_hysteris - * Fans 2 fans, low speed alarm, automatic fan clock divider - * Alarms 16-bit map of active alarms - * Analog Out 0..1250 mV output - * - * Chassis Intrusion: clear CI latch with 'echo 1 > chassis_clear' - * - * Test hardware: Intel SE440BX-2 desktop motherboard --Grant - * - * LM81 extended temp reading not implemented - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, - I2C_CLIENT_END }; - -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_3(adm9240, ds1780, lm81); - -/* ADM9240 registers */ -#define ADM9240_REG_MAN_ID 0x3e -#define ADM9240_REG_DIE_REV 0x3f -#define ADM9240_REG_CONFIG 0x40 - -#define ADM9240_REG_IN(nr) (0x20 + (nr)) /* 0..5 */ -#define ADM9240_REG_IN_MAX(nr) (0x2b + (nr) * 2) -#define ADM9240_REG_IN_MIN(nr) (0x2c + (nr) * 2) -#define ADM9240_REG_FAN(nr) (0x28 + (nr)) /* 0..1 */ -#define ADM9240_REG_FAN_MIN(nr) (0x3b + (nr)) -#define ADM9240_REG_INT(nr) (0x41 + (nr)) -#define ADM9240_REG_INT_MASK(nr) (0x43 + (nr)) -#define ADM9240_REG_TEMP 0x27 -#define ADM9240_REG_TEMP_HIGH 0x39 -#define ADM9240_REG_TEMP_HYST 0x3a -#define ADM9240_REG_ANALOG_OUT 0x19 -#define ADM9240_REG_CHASSIS_CLEAR 0x46 -#define ADM9240_REG_VID_FAN_DIV 0x47 -#define ADM9240_REG_I2C_ADDR 0x48 -#define ADM9240_REG_VID4 0x49 -#define ADM9240_REG_TEMP_CONF 0x4b - -/* generalised scaling with integer rounding */ -static inline int SCALE(long val, int mul, int div) -{ - if (val < 0) - return (val * mul - div / 2) / div; - else - return (val * mul + div / 2) / div; -} - -/* adm9240 internally scales voltage measurements */ -static const u16 nom_mv[] = { 2500, 2700, 3300, 5000, 12000, 2700 }; - -static inline unsigned int IN_FROM_REG(u8 reg, int n) -{ - return SCALE(reg, nom_mv[n], 192); -} - -static inline u8 IN_TO_REG(unsigned long val, int n) -{ - return SENSORS_LIMIT(SCALE(val, 192, nom_mv[n]), 0, 255); -} - -/* temperature range: -40..125, 127 disables temperature alarm */ -static inline s8 TEMP_TO_REG(long val) -{ - return SENSORS_LIMIT(SCALE(val, 1, 1000), -40, 127); -} - -/* two fans, each with low fan speed limit */ -static inline unsigned int FAN_FROM_REG(u8 reg, u8 div) -{ - if (!reg) /* error */ - return -1; - - if (reg == 255) - return 0; - - return SCALE(1350000, 1, reg * div); -} - -/* analog out 0..1250mV */ -static inline u8 AOUT_TO_REG(unsigned long val) -{ - return SENSORS_LIMIT(SCALE(val, 255, 1250), 0, 255); -} - -static inline unsigned int AOUT_FROM_REG(u8 reg) -{ - return SCALE(reg, 1250, 255); -} - -static int adm9240_attach_adapter(struct i2c_adapter *adapter); -static int adm9240_detect(struct i2c_adapter *adapter, int address, int kind); -static void adm9240_init_client(struct i2c_client *client); -static int adm9240_detach_client(struct i2c_client *client); -static struct adm9240_data *adm9240_update_device(struct device *dev); - -/* driver data */ -static struct i2c_driver adm9240_driver = { - .owner = THIS_MODULE, - .name = "adm9240", - .id = I2C_DRIVERID_ADM9240, - .flags = I2C_DF_NOTIFY, - .attach_adapter = adm9240_attach_adapter, - .detach_client = adm9240_detach_client, -}; - -/* per client data */ -struct adm9240_data { - enum chips type; - struct i2c_client client; - struct semaphore update_lock; - char valid; - unsigned long last_updated_measure; - unsigned long last_updated_config; - - u8 in[6]; /* ro in0_input */ - u8 in_max[6]; /* rw in0_max */ - u8 in_min[6]; /* rw in0_min */ - u8 fan[2]; /* ro fan1_input */ - u8 fan_min[2]; /* rw fan1_min */ - u8 fan_div[2]; /* rw fan1_div, read-only accessor */ - s16 temp; /* ro temp1_input, 9-bit sign-extended */ - s8 temp_high; /* rw temp1_max */ - s8 temp_hyst; /* rw temp1_max_hyst */ - u16 alarms; /* ro alarms */ - u8 aout; /* rw aout_output */ - u8 vid; /* ro vid */ - u8 vrm; /* -- vrm set on startup, no accessor */ -}; - -/* i2c byte read/write interface */ -static int adm9240_read_value(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static int adm9240_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - -/*** sysfs accessors ***/ - -/* temperature */ -#define show_temp(value, scale) \ -static ssize_t show_##value(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - struct adm9240_data *data = adm9240_update_device(dev); \ - return sprintf(buf, "%d\n", data->value * scale); \ -} -show_temp(temp_high, 1000); -show_temp(temp_hyst, 1000); -show_temp(temp, 500); /* 0.5'C per bit */ - -#define set_temp(value, reg) \ -static ssize_t set_##value(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct adm9240_data *data = adm9240_update_device(dev); \ - long temp = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->value = TEMP_TO_REG(temp); \ - adm9240_write_value(client, reg, data->value); \ - up(&data->update_lock); \ - return count; \ -} - -set_temp(temp_high, ADM9240_REG_TEMP_HIGH); -set_temp(temp_hyst, ADM9240_REG_TEMP_HYST); - -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, - show_temp_high, set_temp_high); -static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, - show_temp_hyst, set_temp_hyst); -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); - -/* voltage */ -static ssize_t show_in(struct device *dev, char *buf, int nr) -{ - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr], nr)); -} - -static ssize_t show_in_min(struct device *dev, char *buf, int nr) -{ - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr], nr)); -} - -static ssize_t show_in_max(struct device *dev, char *buf, int nr) -{ - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr], nr)); -} - -static ssize_t set_in_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm9240_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val, nr); - adm9240_write_value(client, ADM9240_REG_IN_MIN(nr), data->in_min[nr]); - up(&data->update_lock); - return count; -} - -static ssize_t set_in_max(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm9240_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val, nr); - adm9240_write_value(client, ADM9240_REG_IN_MAX(nr), data->in_max[nr]); - up(&data->update_lock); - return count; -} - -#define show_in_offset(offset) \ -static ssize_t show_in##offset(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_in(dev, buf, offset); \ -} \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL); \ -static ssize_t show_in##offset##_min(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_in_min(dev, buf, offset); \ -} \ -static ssize_t show_in##offset##_max(struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_in_max(dev, buf, offset); \ -} \ -static ssize_t \ -set_in##offset##_min(struct device *dev, \ - struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - return set_in_min(dev, buf, count, offset); \ -} \ -static ssize_t \ -set_in##offset##_max(struct device *dev, \ - struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - return set_in_max(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ - show_in##offset##_min, set_in##offset##_min); \ -static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ - show_in##offset##_max, set_in##offset##_max); - -show_in_offset(0); -show_in_offset(1); -show_in_offset(2); -show_in_offset(3); -show_in_offset(4); -show_in_offset(5); - -/* fans */ -static ssize_t show_fan(struct device *dev, char *buf, int nr) -{ - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], - 1 << data->fan_div[nr])); -} - -static ssize_t show_fan_min(struct device *dev, char *buf, int nr) -{ - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], - 1 << data->fan_div[nr])); -} - -static ssize_t show_fan_div(struct device *dev, char *buf, int nr) -{ - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", 1 << data->fan_div[nr]); -} - -/* write new fan div, callers must hold data->update_lock */ -static void adm9240_write_fan_div(struct i2c_client *client, int nr, - u8 fan_div) -{ - u8 reg, old, shift = (nr + 2) * 2; - - reg = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV); - old = (reg >> shift) & 3; - reg &= ~(3 << shift); - reg |= (fan_div << shift); - adm9240_write_value(client, ADM9240_REG_VID_FAN_DIV, reg); - dev_dbg(&client->dev, "fan%d clock divider changed from %u " - "to %u\n", nr + 1, 1 << old, 1 << fan_div); -} - -/* - * set fan speed low limit: - * - * - value is zero: disable fan speed low limit alarm - * - * - value is below fan speed measurement range: enable fan speed low - * limit alarm to be asserted while fan speed too slow to measure - * - * - otherwise: select fan clock divider to suit fan speed low limit, - * measurement code may adjust registers to ensure fan speed reading - */ -static ssize_t set_fan_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm9240_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - u8 new_div; - - down(&data->update_lock); - - if (!val) { - data->fan_min[nr] = 255; - new_div = data->fan_div[nr]; - - dev_dbg(&client->dev, "fan%u low limit set disabled\n", - nr + 1); - - } else if (val < 1350000 / (8 * 254)) { - new_div = 3; - data->fan_min[nr] = 254; - - dev_dbg(&client->dev, "fan%u low limit set minimum %u\n", - nr + 1, FAN_FROM_REG(254, 1 << new_div)); - - } else { - unsigned int new_min = 1350000 / val; - - new_div = 0; - while (new_min > 192 && new_div < 3) { - new_div++; - new_min /= 2; - } - if (!new_min) /* keep > 0 */ - new_min++; - - data->fan_min[nr] = new_min; - - dev_dbg(&client->dev, "fan%u low limit set fan speed %u\n", - nr + 1, FAN_FROM_REG(new_min, 1 << new_div)); - } - - if (new_div != data->fan_div[nr]) { - data->fan_div[nr] = new_div; - adm9240_write_fan_div(client, nr, new_div); - } - adm9240_write_value(client, ADM9240_REG_FAN_MIN(nr), - data->fan_min[nr]); - - up(&data->update_lock); - return count; -} - -#define show_fan_offset(offset) \ -static ssize_t show_fan_##offset (struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ -return show_fan(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan_##offset##_div (struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ -return show_fan_div(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan_##offset##_min (struct device *dev, \ - struct device_attribute *attr, \ - char *buf) \ -{ \ -return show_fan_min(dev, buf, offset - 1); \ -} \ -static ssize_t set_fan_##offset##_min (struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ -return set_fan_min(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ - show_fan_##offset, NULL); \ -static DEVICE_ATTR(fan##offset##_div, S_IRUGO, \ - show_fan_##offset##_div, NULL); \ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan_##offset##_min, set_fan_##offset##_min); - -show_fan_offset(1); -show_fan_offset(2); - -/* alarms */ -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%u\n", data->alarms); -} -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -/* vid */ -static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); -} -static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); - -/* analog output */ -static ssize_t show_aout(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct adm9240_data *data = adm9240_update_device(dev); - return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout)); -} - -static ssize_t set_aout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm9240_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->aout = AOUT_TO_REG(val); - adm9240_write_value(client, ADM9240_REG_ANALOG_OUT, data->aout); - up(&data->update_lock); - return count; -} -static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout); - -/* chassis_clear */ -static ssize_t chassis_clear(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - unsigned long val = simple_strtol(buf, NULL, 10); - - if (val == 1) { - adm9240_write_value(client, ADM9240_REG_CHASSIS_CLEAR, 0x80); - dev_dbg(&client->dev, "chassis intrusion latch cleared\n"); - } - return count; -} -static DEVICE_ATTR(chassis_clear, S_IWUSR, NULL, chassis_clear); - - -/*** sensor chip detect and driver install ***/ - -static int adm9240_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct adm9240_data *data; - int err = 0; - const char *name = ""; - u8 man_id, die_rev; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - if (!(data = kmalloc(sizeof(struct adm9240_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct adm9240_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &adm9240_driver; - new_client->flags = 0; - - if (kind == 0) { - kind = adm9240; - } - - if (kind < 0) { - - /* verify chip: reg address should match i2c address */ - if (adm9240_read_value(new_client, ADM9240_REG_I2C_ADDR) - != address) { - dev_err(&adapter->dev, "detect fail: address match, " - "0x%02x\n", address); - goto exit_free; - } - - /* check known chip manufacturer */ - man_id = adm9240_read_value(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); - goto exit_free; - } - - /* successful detect, print chip info */ - die_rev = adm9240_read_value(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); - } - - /* either forced or detected chip kind */ - if (kind == adm9240) { - name = "adm9240"; - } else if (kind == ds1780) { - name = "ds1780"; - } else if (kind == lm81) { - name = "lm81"; - } - - /* fill in the remaining client fields and attach */ - strlcpy(new_client->name, name, I2C_NAME_SIZE); - data->type = kind; - init_MUTEX(&data->update_lock); - - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - adm9240_init_client(new_client); - - /* populate sysfs filesystem */ - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in4_max); - device_create_file(&new_client->dev, &dev_attr_in5_input); - device_create_file(&new_client->dev, &dev_attr_in5_min); - device_create_file(&new_client->dev, &dev_attr_in5_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_aout_output); - device_create_file(&new_client->dev, &dev_attr_chassis_clear); - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - - return 0; -exit_free: - kfree(new_client); -exit: - return err; -} - -static int adm9240_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, adm9240_detect); -} - -static int adm9240_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - -static void adm9240_init_client(struct i2c_client *client) -{ - struct adm9240_data *data = i2c_get_clientdata(client); - u8 conf = adm9240_read_value(client, ADM9240_REG_CONFIG); - u8 mode = adm9240_read_value(client, ADM9240_REG_TEMP_CONF) & 3; - - data->vrm = i2c_which_vrm(); /* need this to report vid as mV */ - - dev_info(&client->dev, "Using VRM: %d.%d\n", data->vrm / 10, - data->vrm % 10); - - if (conf & 1) { /* measurement cycle running: report state */ - - dev_info(&client->dev, "status: config 0x%02x mode %u\n", - conf, mode); - - } else { /* cold start: open limits before starting chip */ - int i; - - for (i = 0; i < 6; i++) - { - adm9240_write_value(client, - ADM9240_REG_IN_MIN(i), 0); - adm9240_write_value(client, - ADM9240_REG_IN_MAX(i), 255); - } - adm9240_write_value(client, ADM9240_REG_FAN_MIN(0), 255); - adm9240_write_value(client, ADM9240_REG_FAN_MIN(1), 255); - adm9240_write_value(client, ADM9240_REG_TEMP_HIGH, 127); - adm9240_write_value(client, ADM9240_REG_TEMP_HYST, 127); - - /* start measurement cycle */ - adm9240_write_value(client, ADM9240_REG_CONFIG, 1); - - dev_info(&client->dev, "cold start: config was 0x%02x " - "mode %u\n", conf, mode); - } -} - -static struct adm9240_data *adm9240_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct adm9240_data *data = i2c_get_clientdata(client); - int i; - - down(&data->update_lock); - - /* minimum measurement cycle: 1.75 seconds */ - if (time_after(jiffies, data->last_updated_measure + (HZ * 7 / 4)) - || !data->valid) { - - for (i = 0; i < 6; i++) /* read voltages */ - { - data->in[i] = adm9240_read_value(client, - ADM9240_REG_IN(i)); - } - data->alarms = adm9240_read_value(client, - ADM9240_REG_INT(0)) | - adm9240_read_value(client, - ADM9240_REG_INT(1)) << 8; - - /* read temperature: assume temperature changes less than - * 0.5'C per two measurement cycles thus ignore possible - * but unlikely aliasing error on lsb reading. --Grant */ - data->temp = ((adm9240_read_value(client, - ADM9240_REG_TEMP) << 8) | - adm9240_read_value(client, - ADM9240_REG_TEMP_CONF)) / 128; - - for (i = 0; i < 2; i++) /* read fans */ - { - data->fan[i] = adm9240_read_value(client, - ADM9240_REG_FAN(i)); - - /* adjust fan clock divider on overflow */ - if (data->valid && data->fan[i] == 255 && - data->fan_div[i] < 3) { - - adm9240_write_fan_div(client, i, - ++data->fan_div[i]); - - /* adjust fan_min if active, but not to 0 */ - if (data->fan_min[i] < 255 && - data->fan_min[i] >= 2) - data->fan_min[i] /= 2; - } - } - data->last_updated_measure = jiffies; - } - - /* minimum config reading cycle: 300 seconds */ - if (time_after(jiffies, data->last_updated_config + (HZ * 300)) - || !data->valid) { - - for (i = 0; i < 6; i++) - { - data->in_min[i] = adm9240_read_value(client, - ADM9240_REG_IN_MIN(i)); - data->in_max[i] = adm9240_read_value(client, - ADM9240_REG_IN_MAX(i)); - } - for (i = 0; i < 2; i++) - { - data->fan_min[i] = adm9240_read_value(client, - ADM9240_REG_FAN_MIN(i)); - } - data->temp_high = adm9240_read_value(client, - ADM9240_REG_TEMP_HIGH); - data->temp_hyst = adm9240_read_value(client, - ADM9240_REG_TEMP_HYST); - - /* read fan divs and 5-bit VID */ - i = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV); - data->fan_div[0] = (i >> 4) & 3; - data->fan_div[1] = (i >> 6) & 3; - data->vid = i & 0x0f; - data->vid |= (adm9240_read_value(client, - ADM9240_REG_VID4) & 1) << 4; - /* read analog out */ - data->aout = adm9240_read_value(client, - ADM9240_REG_ANALOG_OUT); - - data->last_updated_config = jiffies; - data->valid = 1; - } - up(&data->update_lock); - return data; -} - -static int __init sensors_adm9240_init(void) -{ - return i2c_add_driver(&adm9240_driver); -} - -static void __exit sensors_adm9240_exit(void) -{ - i2c_del_driver(&adm9240_driver); -} - -MODULE_AUTHOR("Michiel Rook , " - "Grant Coady and others"); -MODULE_DESCRIPTION("ADM9240/DS1780/LM81 driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_adm9240_init); -module_exit(sensors_adm9240_exit); - diff --git a/drivers/i2c/chips/asb100.c b/drivers/i2c/chips/asb100.c deleted file mode 100644 index 70d996d6fe0a..000000000000 --- a/drivers/i2c/chips/asb100.c +++ /dev/null @@ -1,1065 +0,0 @@ -/* - asb100.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - - Copyright (C) 2004 Mark M. Hoffman - - (derived from w83781d.c) - - Copyright (C) 1998 - 2003 Frodo Looijaard , - Philip Edelbrock , and - Mark Studebaker - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - This driver supports the hardware sensor chips: Asus ASB100 and - ASB100-A "BACH". - - ASB100-A supports pwm1, while plain ASB100 does not. There is no known - way for the driver to tell which one is there. - - Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA - asb100 7 3 1 4 0x31 0x0694 yes no -*/ - -#include -#include -#include -#include -#include -#include -#include -#include "lm75.h" - -/* - HISTORY: - 2003-12-29 1.0.0 Ported from lm_sensors project for kernel 2.6 -*/ -#define ASB100_VERSION "1.0.0" - -/* I2C addresses to scan */ -static unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END }; - -/* ISA addresses to scan (none) */ -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_1(asb100); -I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " - "{bus, clientaddr, subclientaddr1, subclientaddr2}"); - -/* Voltage IN registers 0-6 */ -#define ASB100_REG_IN(nr) (0x20 + (nr)) -#define ASB100_REG_IN_MAX(nr) (0x2b + (nr * 2)) -#define ASB100_REG_IN_MIN(nr) (0x2c + (nr * 2)) - -/* FAN IN registers 1-3 */ -#define ASB100_REG_FAN(nr) (0x28 + (nr)) -#define ASB100_REG_FAN_MIN(nr) (0x3b + (nr)) - -/* TEMPERATURE registers 1-4 */ -static const u16 asb100_reg_temp[] = {0, 0x27, 0x150, 0x250, 0x17}; -static const u16 asb100_reg_temp_max[] = {0, 0x39, 0x155, 0x255, 0x18}; -static const u16 asb100_reg_temp_hyst[] = {0, 0x3a, 0x153, 0x253, 0x19}; - -#define ASB100_REG_TEMP(nr) (asb100_reg_temp[nr]) -#define ASB100_REG_TEMP_MAX(nr) (asb100_reg_temp_max[nr]) -#define ASB100_REG_TEMP_HYST(nr) (asb100_reg_temp_hyst[nr]) - -#define ASB100_REG_TEMP2_CONFIG 0x0152 -#define ASB100_REG_TEMP3_CONFIG 0x0252 - - -#define ASB100_REG_CONFIG 0x40 -#define ASB100_REG_ALARM1 0x41 -#define ASB100_REG_ALARM2 0x42 -#define ASB100_REG_SMIM1 0x43 -#define ASB100_REG_SMIM2 0x44 -#define ASB100_REG_VID_FANDIV 0x47 -#define ASB100_REG_I2C_ADDR 0x48 -#define ASB100_REG_CHIPID 0x49 -#define ASB100_REG_I2C_SUBADDR 0x4a -#define ASB100_REG_PIN 0x4b -#define ASB100_REG_IRQ 0x4c -#define ASB100_REG_BANK 0x4e -#define ASB100_REG_CHIPMAN 0x4f - -#define ASB100_REG_WCHIPID 0x58 - -/* bit 7 -> enable, bits 0-3 -> duty cycle */ -#define ASB100_REG_PWM1 0x59 - -/* CONVERSIONS - Rounding and limit checking is only done on the TO_REG variants. */ - -/* These constants are a guess, consistent w/ w83781d */ -#define ASB100_IN_MIN ( 0) -#define ASB100_IN_MAX (4080) - -/* IN: 1/1000 V (0V to 4.08V) - REG: 16mV/bit */ -static u8 IN_TO_REG(unsigned val) -{ - unsigned nval = SENSORS_LIMIT(val, ASB100_IN_MIN, ASB100_IN_MAX); - return (nval + 8) / 16; -} - -static unsigned IN_FROM_REG(u8 reg) -{ - return reg * 16; -} - -static u8 FAN_TO_REG(long rpm, int div) -{ - if (rpm == -1) - return 0; - if (rpm == 0) - return 255; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); -} - -static int FAN_FROM_REG(u8 val, int div) -{ - return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div); -} - -/* These constants are a guess, consistent w/ w83781d */ -#define ASB100_TEMP_MIN (-128000) -#define ASB100_TEMP_MAX ( 127000) - -/* TEMP: 0.001C/bit (-128C to +127C) - REG: 1C/bit, two's complement */ -static u8 TEMP_TO_REG(int temp) -{ - int ntemp = SENSORS_LIMIT(temp, ASB100_TEMP_MIN, ASB100_TEMP_MAX); - ntemp += (ntemp<0 ? -500 : 500); - return (u8)(ntemp / 1000); -} - -static int TEMP_FROM_REG(u8 reg) -{ - return (s8)reg * 1000; -} - -/* PWM: 0 - 255 per sensors documentation - REG: (6.25% duty cycle per bit) */ -static u8 ASB100_PWM_TO_REG(int pwm) -{ - pwm = SENSORS_LIMIT(pwm, 0, 255); - return (u8)(pwm / 16); -} - -static int ASB100_PWM_FROM_REG(u8 reg) -{ - return reg * 16; -} - -#define DIV_FROM_REG(val) (1 << (val)) - -/* FAN DIV: 1, 2, 4, or 8 (defaults to 2) - REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */ -static u8 DIV_TO_REG(long val) -{ - return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1; -} - -/* For each registered client, we need to keep some data in memory. That - data is pointed to by client->data. The structure itself is - dynamically allocated, at the same time the client itself is allocated. */ -struct asb100_data { - struct i2c_client client; - struct semaphore lock; - enum chips type; - - struct semaphore update_lock; - unsigned long last_updated; /* In jiffies */ - - /* array of 2 pointers to subclients */ - struct i2c_client *lm75[2]; - - char valid; /* !=0 if following fields are valid */ - u8 in[7]; /* Register value */ - u8 in_max[7]; /* Register value */ - u8 in_min[7]; /* Register value */ - u8 fan[3]; /* Register value */ - u8 fan_min[3]; /* Register value */ - u16 temp[4]; /* Register value (0 and 3 are u8 only) */ - u16 temp_max[4]; /* Register value (0 and 3 are u8 only) */ - u16 temp_hyst[4]; /* Register value (0 and 3 are u8 only) */ - u8 fan_div[3]; /* Register encoding, right justified */ - u8 pwm; /* Register encoding */ - u8 vid; /* Register encoding, combined */ - u32 alarms; /* Register encoding, combined */ - u8 vrm; -}; - -static int asb100_read_value(struct i2c_client *client, u16 reg); -static void asb100_write_value(struct i2c_client *client, u16 reg, u16 val); - -static int asb100_attach_adapter(struct i2c_adapter *adapter); -static int asb100_detect(struct i2c_adapter *adapter, int address, int kind); -static int asb100_detach_client(struct i2c_client *client); -static struct asb100_data *asb100_update_device(struct device *dev); -static void asb100_init_client(struct i2c_client *client); - -static struct i2c_driver asb100_driver = { - .owner = THIS_MODULE, - .name = "asb100", - .id = I2C_DRIVERID_ASB100, - .flags = I2C_DF_NOTIFY, - .attach_adapter = asb100_attach_adapter, - .detach_client = asb100_detach_client, -}; - -/* 7 Voltages */ -#define show_in_reg(reg) \ -static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ -{ \ - struct asb100_data *data = asb100_update_device(dev); \ - return sprintf(buf, "%d\n", IN_FROM_REG(data->reg[nr])); \ -} - -show_in_reg(in) -show_in_reg(in_min) -show_in_reg(in_max) - -#define set_in_reg(REG, reg) \ -static ssize_t set_in_##reg(struct device *dev, const char *buf, \ - size_t count, int nr) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct asb100_data *data = i2c_get_clientdata(client); \ - unsigned long val = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->in_##reg[nr] = IN_TO_REG(val); \ - asb100_write_value(client, ASB100_REG_IN_##REG(nr), \ - data->in_##reg[nr]); \ - up(&data->update_lock); \ - return count; \ -} - -set_in_reg(MIN, min) -set_in_reg(MAX, max) - -#define sysfs_in(offset) \ -static ssize_t \ - show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in(dev, buf, offset); \ -} \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ - show_in##offset, NULL); \ -static ssize_t \ - show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in_min(dev, buf, offset); \ -} \ -static ssize_t \ - show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in_max(dev, buf, offset); \ -} \ -static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_in_min(dev, buf, count, offset); \ -} \ -static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_in_max(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ - show_in##offset##_min, set_in##offset##_min); \ -static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ - show_in##offset##_max, set_in##offset##_max); - -sysfs_in(0); -sysfs_in(1); -sysfs_in(2); -sysfs_in(3); -sysfs_in(4); -sysfs_in(5); -sysfs_in(6); - -#define device_create_file_in(client, offset) do { \ - device_create_file(&client->dev, &dev_attr_in##offset##_input); \ - device_create_file(&client->dev, &dev_attr_in##offset##_min); \ - device_create_file(&client->dev, &dev_attr_in##offset##_max); \ -} while (0) - -/* 3 Fans */ -static ssize_t show_fan(struct device *dev, char *buf, int nr) -{ - struct asb100_data *data = asb100_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], - DIV_FROM_REG(data->fan_div[nr]))); -} - -static ssize_t show_fan_min(struct device *dev, char *buf, int nr) -{ - struct asb100_data *data = asb100_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr]))); -} - -static ssize_t show_fan_div(struct device *dev, char *buf, int nr) -{ - struct asb100_data *data = asb100_update_device(dev); - return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); -} - -static ssize_t set_fan_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct asb100_data *data = i2c_get_clientdata(client); - u32 val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); - asb100_write_value(client, ASB100_REG_FAN_MIN(nr), data->fan_min[nr]); - up(&data->update_lock); - return count; -} - -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least suprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ -static ssize_t set_fan_div(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct asb100_data *data = i2c_get_clientdata(client); - unsigned long min; - unsigned long val = simple_strtoul(buf, NULL, 10); - int reg; - - down(&data->update_lock); - - min = FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr])); - data->fan_div[nr] = DIV_TO_REG(val); - - switch(nr) { - case 0: /* fan 1 */ - reg = asb100_read_value(client, ASB100_REG_VID_FANDIV); - reg = (reg & 0xcf) | (data->fan_div[0] << 4); - asb100_write_value(client, ASB100_REG_VID_FANDIV, reg); - break; - - case 1: /* fan 2 */ - reg = asb100_read_value(client, ASB100_REG_VID_FANDIV); - reg = (reg & 0x3f) | (data->fan_div[1] << 6); - asb100_write_value(client, ASB100_REG_VID_FANDIV, reg); - break; - - case 2: /* fan 3 */ - reg = asb100_read_value(client, ASB100_REG_PIN); - reg = (reg & 0x3f) | (data->fan_div[2] << 6); - asb100_write_value(client, ASB100_REG_PIN, reg); - break; - } - - data->fan_min[nr] = - FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); - asb100_write_value(client, ASB100_REG_FAN_MIN(nr), data->fan_min[nr]); - - up(&data->update_lock); - - return count; -} - -#define sysfs_fan(offset) \ -static ssize_t show_fan##offset(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_min(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan##offset##_div(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_div(dev, buf, offset - 1); \ -} \ -static ssize_t set_fan##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - return set_fan_min(dev, buf, count, offset - 1); \ -} \ -static ssize_t set_fan##offset##_div(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - return set_fan_div(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ - show_fan##offset, NULL); \ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan##offset##_min, set_fan##offset##_min); \ -static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ - show_fan##offset##_div, set_fan##offset##_div); - -sysfs_fan(1); -sysfs_fan(2); -sysfs_fan(3); - -#define device_create_file_fan(client, offset) do { \ - device_create_file(&client->dev, &dev_attr_fan##offset##_input); \ - device_create_file(&client->dev, &dev_attr_fan##offset##_min); \ - device_create_file(&client->dev, &dev_attr_fan##offset##_div); \ -} while (0) - -/* 4 Temp. Sensors */ -static int sprintf_temp_from_reg(u16 reg, char *buf, int nr) -{ - int ret = 0; - - switch (nr) { - case 1: case 2: - ret = sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(reg)); - break; - case 0: case 3: default: - ret = sprintf(buf, "%d\n", TEMP_FROM_REG(reg)); - break; - } - return ret; -} - -#define show_temp_reg(reg) \ -static ssize_t show_##reg(struct device *dev, char *buf, int nr) \ -{ \ - struct asb100_data *data = asb100_update_device(dev); \ - return sprintf_temp_from_reg(data->reg[nr], buf, nr); \ -} - -show_temp_reg(temp); -show_temp_reg(temp_max); -show_temp_reg(temp_hyst); - -#define set_temp_reg(REG, reg) \ -static ssize_t set_##reg(struct device *dev, const char *buf, \ - size_t count, int nr) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct asb100_data *data = i2c_get_clientdata(client); \ - unsigned long val = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - switch (nr) { \ - case 1: case 2: \ - data->reg[nr] = LM75_TEMP_TO_REG(val); \ - break; \ - case 0: case 3: default: \ - data->reg[nr] = TEMP_TO_REG(val); \ - break; \ - } \ - asb100_write_value(client, ASB100_REG_TEMP_##REG(nr+1), \ - data->reg[nr]); \ - up(&data->update_lock); \ - return count; \ -} - -set_temp_reg(MAX, temp_max); -set_temp_reg(HYST, temp_hyst); - -#define sysfs_temp(num) \ -static ssize_t show_temp##num(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp(dev, buf, num-1); \ -} \ -static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL); \ -static ssize_t show_temp_max##num(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp_max(dev, buf, num-1); \ -} \ -static ssize_t set_temp_max##num(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - return set_temp_max(dev, buf, count, num-1); \ -} \ -static DEVICE_ATTR(temp##num##_max, S_IRUGO | S_IWUSR, \ - show_temp_max##num, set_temp_max##num); \ -static ssize_t show_temp_hyst##num(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp_hyst(dev, buf, num-1); \ -} \ -static ssize_t set_temp_hyst##num(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - return set_temp_hyst(dev, buf, count, num-1); \ -} \ -static DEVICE_ATTR(temp##num##_max_hyst, S_IRUGO | S_IWUSR, \ - show_temp_hyst##num, set_temp_hyst##num); - -sysfs_temp(1); -sysfs_temp(2); -sysfs_temp(3); -sysfs_temp(4); - -/* VID */ -#define device_create_file_temp(client, num) do { \ - device_create_file(&client->dev, &dev_attr_temp##num##_input); \ - device_create_file(&client->dev, &dev_attr_temp##num##_max); \ - device_create_file(&client->dev, &dev_attr_temp##num##_max_hyst); \ -} while (0) - -static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct asb100_data *data = asb100_update_device(dev); - return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); -} - -static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); -#define device_create_file_vid(client) \ -device_create_file(&client->dev, &dev_attr_cpu0_vid) - -/* VRM */ -static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct asb100_data *data = asb100_update_device(dev); - return sprintf(buf, "%d\n", data->vrm); -} - -static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct asb100_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - data->vrm = val; - return count; -} - -/* Alarms */ -static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); -#define device_create_file_vrm(client) \ -device_create_file(&client->dev, &dev_attr_vrm); - -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct asb100_data *data = asb100_update_device(dev); - return sprintf(buf, "%u\n", data->alarms); -} - -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); -#define device_create_file_alarms(client) \ -device_create_file(&client->dev, &dev_attr_alarms) - -/* 1 PWM */ -static ssize_t show_pwm1(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct asb100_data *data = asb100_update_device(dev); - return sprintf(buf, "%d\n", ASB100_PWM_FROM_REG(data->pwm & 0x0f)); -} - -static ssize_t set_pwm1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct asb100_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->pwm &= 0x80; /* keep the enable bit */ - data->pwm |= (0x0f & ASB100_PWM_TO_REG(val)); - asb100_write_value(client, ASB100_REG_PWM1, data->pwm); - up(&data->update_lock); - return count; -} - -static ssize_t show_pwm_enable1(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct asb100_data *data = asb100_update_device(dev); - return sprintf(buf, "%d\n", (data->pwm & 0x80) ? 1 : 0); -} - -static ssize_t set_pwm_enable1(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct asb100_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->pwm &= 0x0f; /* keep the duty cycle bits */ - data->pwm |= (val ? 0x80 : 0x00); - asb100_write_value(client, ASB100_REG_PWM1, data->pwm); - up(&data->update_lock); - return count; -} - -static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm1, set_pwm1); -static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, - show_pwm_enable1, set_pwm_enable1); -#define device_create_file_pwm1(client) do { \ - device_create_file(&new_client->dev, &dev_attr_pwm1); \ - device_create_file(&new_client->dev, &dev_attr_pwm1_enable); \ -} while (0) - -/* This function is called when: - asb100_driver is inserted (when this module is loaded), for each - available adapter - when a new adapter is inserted (and asb100_driver is still present) - */ -static int asb100_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, asb100_detect); -} - -static int asb100_detect_subclients(struct i2c_adapter *adapter, int address, - int kind, struct i2c_client *new_client) -{ - int i, id, err; - struct asb100_data *data = i2c_get_clientdata(new_client); - - data->lm75[0] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!(data->lm75[0])) { - err = -ENOMEM; - goto ERROR_SC_0; - } - memset(data->lm75[0], 0x00, sizeof(struct i2c_client)); - - data->lm75[1] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!(data->lm75[1])) { - err = -ENOMEM; - goto ERROR_SC_1; - } - memset(data->lm75[1], 0x00, sizeof(struct i2c_client)); - - id = i2c_adapter_id(adapter); - - if (force_subclients[0] == id && force_subclients[1] == address) { - for (i = 2; i <= 3; i++) { - if (force_subclients[i] < 0x48 || - force_subclients[i] > 0x4f) { - dev_err(&new_client->dev, "invalid subclient " - "address %d; must be 0x48-0x4f\n", - force_subclients[i]); - err = -ENODEV; - goto ERROR_SC_2; - } - } - asb100_write_value(new_client, ASB100_REG_I2C_SUBADDR, - (force_subclients[2] & 0x07) | - ((force_subclients[3] & 0x07) <<4)); - data->lm75[0]->addr = force_subclients[2]; - data->lm75[1]->addr = force_subclients[3]; - } else { - int val = asb100_read_value(new_client, ASB100_REG_I2C_SUBADDR); - data->lm75[0]->addr = 0x48 + (val & 0x07); - data->lm75[1]->addr = 0x48 + ((val >> 4) & 0x07); - } - - if(data->lm75[0]->addr == data->lm75[1]->addr) { - dev_err(&new_client->dev, "duplicate addresses 0x%x " - "for subclients\n", data->lm75[0]->addr); - err = -ENODEV; - goto ERROR_SC_2; - } - - for (i = 0; i <= 1; i++) { - i2c_set_clientdata(data->lm75[i], NULL); - data->lm75[i]->adapter = adapter; - data->lm75[i]->driver = &asb100_driver; - data->lm75[i]->flags = 0; - strlcpy(data->lm75[i]->name, "asb100 subclient", I2C_NAME_SIZE); - } - - if ((err = i2c_attach_client(data->lm75[0]))) { - dev_err(&new_client->dev, "subclient %d registration " - "at address 0x%x failed.\n", i, data->lm75[0]->addr); - goto ERROR_SC_2; - } - - if ((err = i2c_attach_client(data->lm75[1]))) { - dev_err(&new_client->dev, "subclient %d registration " - "at address 0x%x failed.\n", i, data->lm75[1]->addr); - goto ERROR_SC_3; - } - - return 0; - -/* Undo inits in case of errors */ -ERROR_SC_3: - i2c_detach_client(data->lm75[0]); -ERROR_SC_2: - kfree(data->lm75[1]); -ERROR_SC_1: - kfree(data->lm75[0]); -ERROR_SC_0: - return err; -} - -static int asb100_detect(struct i2c_adapter *adapter, int address, int kind) -{ - int err; - struct i2c_client *new_client; - struct asb100_data *data; - - /* asb100 is SMBus only */ - if (i2c_is_isa_adapter(adapter)) { - pr_debug("asb100.o: detect failed, " - "cannot attach to legacy adapter!\n"); - err = -ENODEV; - goto ERROR0; - } - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - pr_debug("asb100.o: detect failed, " - "smbus byte data not supported!\n"); - err = -ENODEV; - goto ERROR0; - } - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access asb100_{read,write}_value. */ - - if (!(data = kmalloc(sizeof(struct asb100_data), GFP_KERNEL))) { - pr_debug("asb100.o: detect failed, kmalloc failed!\n"); - err = -ENOMEM; - goto ERROR0; - } - memset(data, 0, sizeof(struct asb100_data)); - - new_client = &data->client; - init_MUTEX(&data->lock); - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &asb100_driver; - new_client->flags = 0; - - /* Now, we do the remaining detection. */ - - /* 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 = asb100_read_value(new_client, ASB100_REG_BANK); - int val2 = asb100_read_value(new_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); - err = -ENODEV; - goto ERROR1; - } - - } /* kind < 0 */ - - /* We have either had a force parameter, or we have already detected - Winbond. Put it now into bank 0 and Vendor ID High Byte */ - asb100_write_value(new_client, ASB100_REG_BANK, - (asb100_read_value(new_client, ASB100_REG_BANK) & 0x78) | 0x80); - - /* Determine the chip type. */ - if (kind <= 0) { - int val1 = asb100_read_value(new_client, ASB100_REG_WCHIPID); - int val2 = asb100_read_value(new_client, ASB100_REG_CHIPMAN); - - if ((val1 == 0x31) && (val2 == 0x06)) - kind = asb100; - else { - if (kind == 0) - dev_warn(&new_client->dev, "ignoring " - "'force' parameter for unknown chip " - "at adapter %d, address 0x%02x.\n", - i2c_adapter_id(adapter), address); - err = -ENODEV; - goto ERROR1; - } - } - - /* Fill in remaining client fields and put it into the global list */ - strlcpy(new_client->name, "asb100", I2C_NAME_SIZE); - data->type = kind; - - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto ERROR1; - - /* Attach secondary lm75 clients */ - if ((err = asb100_detect_subclients(adapter, address, kind, - new_client))) - goto ERROR2; - - /* Initialize the chip */ - asb100_init_client(new_client); - - /* A few vars need to be filled upon startup */ - data->fan_min[0] = asb100_read_value(new_client, ASB100_REG_FAN_MIN(0)); - data->fan_min[1] = asb100_read_value(new_client, ASB100_REG_FAN_MIN(1)); - data->fan_min[2] = asb100_read_value(new_client, ASB100_REG_FAN_MIN(2)); - - /* Register sysfs hooks */ - device_create_file_in(new_client, 0); - device_create_file_in(new_client, 1); - device_create_file_in(new_client, 2); - device_create_file_in(new_client, 3); - device_create_file_in(new_client, 4); - device_create_file_in(new_client, 5); - device_create_file_in(new_client, 6); - - device_create_file_fan(new_client, 1); - device_create_file_fan(new_client, 2); - device_create_file_fan(new_client, 3); - - device_create_file_temp(new_client, 1); - device_create_file_temp(new_client, 2); - device_create_file_temp(new_client, 3); - device_create_file_temp(new_client, 4); - - device_create_file_vid(new_client); - device_create_file_vrm(new_client); - - device_create_file_alarms(new_client); - - device_create_file_pwm1(new_client); - - return 0; - -ERROR2: - i2c_detach_client(new_client); -ERROR1: - kfree(data); -ERROR0: - return err; -} - -static int asb100_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "client deregistration failed; " - "client not detached.\n"); - return err; - } - - if (i2c_get_clientdata(client)==NULL) { - /* subclients */ - kfree(client); - } else { - /* main client */ - kfree(i2c_get_clientdata(client)); - } - - return 0; -} - -/* The SMBus locks itself, usually, but nothing may access the chip between - bank switches. */ -static int asb100_read_value(struct i2c_client *client, u16 reg) -{ - struct asb100_data *data = i2c_get_clientdata(client); - struct i2c_client *cl; - int res, bank; - - down(&data->lock); - - bank = (reg >> 8) & 0x0f; - if (bank > 2) - /* switch banks */ - i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank); - - if (bank == 0 || bank > 2) { - res = i2c_smbus_read_byte_data(client, reg & 0xff); - } else { - /* switch to subclient */ - cl = data->lm75[bank - 1]; - - /* convert from ISA to LM75 I2C addresses */ - switch (reg & 0xff) { - case 0x50: /* TEMP */ - res = swab16(i2c_smbus_read_word_data (cl, 0)); - break; - case 0x52: /* CONFIG */ - res = i2c_smbus_read_byte_data(cl, 1); - break; - case 0x53: /* HYST */ - res = swab16(i2c_smbus_read_word_data (cl, 2)); - break; - case 0x55: /* MAX */ - default: - res = swab16(i2c_smbus_read_word_data (cl, 3)); - break; - } - } - - if (bank > 2) - i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0); - - up(&data->lock); - - return res; -} - -static void asb100_write_value(struct i2c_client *client, u16 reg, u16 value) -{ - struct asb100_data *data = i2c_get_clientdata(client); - struct i2c_client *cl; - int bank; - - down(&data->lock); - - bank = (reg >> 8) & 0x0f; - if (bank > 2) - /* switch banks */ - i2c_smbus_write_byte_data(client, ASB100_REG_BANK, bank); - - if (bank == 0 || bank > 2) { - i2c_smbus_write_byte_data(client, reg & 0xff, value & 0xff); - } else { - /* switch to subclient */ - cl = data->lm75[bank - 1]; - - /* convert from ISA to LM75 I2C addresses */ - switch (reg & 0xff) { - case 0x52: /* CONFIG */ - i2c_smbus_write_byte_data(cl, 1, value & 0xff); - break; - case 0x53: /* HYST */ - i2c_smbus_write_word_data(cl, 2, swab16(value)); - break; - case 0x55: /* MAX */ - i2c_smbus_write_word_data(cl, 3, swab16(value)); - break; - } - } - - if (bank > 2) - i2c_smbus_write_byte_data(client, ASB100_REG_BANK, 0); - - up(&data->lock); -} - -static void asb100_init_client(struct i2c_client *client) -{ - struct asb100_data *data = i2c_get_clientdata(client); - int vid = 0; - - vid = asb100_read_value(client, ASB100_REG_VID_FANDIV) & 0x0f; - vid |= (asb100_read_value(client, ASB100_REG_CHIPID) & 0x01) << 4; - data->vrm = i2c_which_vrm(); - vid = vid_from_reg(vid, data->vrm); - - /* Start monitoring */ - asb100_write_value(client, ASB100_REG_CONFIG, - (asb100_read_value(client, ASB100_REG_CONFIG) & 0xf7) | 0x01); -} - -static struct asb100_data *asb100_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct asb100_data *data = i2c_get_clientdata(client); - int i; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - - dev_dbg(&client->dev, "starting device update...\n"); - - /* 7 voltage inputs */ - for (i = 0; i < 7; i++) { - data->in[i] = asb100_read_value(client, - ASB100_REG_IN(i)); - data->in_min[i] = asb100_read_value(client, - ASB100_REG_IN_MIN(i)); - data->in_max[i] = asb100_read_value(client, - ASB100_REG_IN_MAX(i)); - } - - /* 3 fan inputs */ - for (i = 0; i < 3; i++) { - data->fan[i] = asb100_read_value(client, - ASB100_REG_FAN(i)); - data->fan_min[i] = asb100_read_value(client, - ASB100_REG_FAN_MIN(i)); - } - - /* 4 temperature inputs */ - for (i = 1; i <= 4; i++) { - data->temp[i-1] = asb100_read_value(client, - ASB100_REG_TEMP(i)); - data->temp_max[i-1] = asb100_read_value(client, - ASB100_REG_TEMP_MAX(i)); - data->temp_hyst[i-1] = asb100_read_value(client, - ASB100_REG_TEMP_HYST(i)); - } - - /* VID and fan divisors */ - i = asb100_read_value(client, ASB100_REG_VID_FANDIV); - data->vid = i & 0x0f; - data->vid |= (asb100_read_value(client, - ASB100_REG_CHIPID) & 0x01) << 4; - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = (i >> 6) & 0x03; - data->fan_div[2] = (asb100_read_value(client, - ASB100_REG_PIN) >> 6) & 0x03; - - /* PWM */ - data->pwm = asb100_read_value(client, ASB100_REG_PWM1); - - /* alarms */ - data->alarms = asb100_read_value(client, ASB100_REG_ALARM1) + - (asb100_read_value(client, ASB100_REG_ALARM2) << 8); - - data->last_updated = jiffies; - data->valid = 1; - - dev_dbg(&client->dev, "... device update complete\n"); - } - - up(&data->update_lock); - - return data; -} - -static int __init asb100_init(void) -{ - return i2c_add_driver(&asb100_driver); -} - -static void __exit asb100_exit(void) -{ - i2c_del_driver(&asb100_driver); -} - -MODULE_AUTHOR("Mark M. Hoffman "); -MODULE_DESCRIPTION("ASB100 Bach driver"); -MODULE_LICENSE("GPL"); - -module_init(asb100_init); -module_exit(asb100_exit); - diff --git a/drivers/i2c/chips/atxp1.c b/drivers/i2c/chips/atxp1.c deleted file mode 100644 index 0bcf82b4c07b..000000000000 --- a/drivers/i2c/chips/atxp1.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - atxp1.c - kernel module for setting CPU VID and general purpose - I/Os using the Attansic ATXP1 chip. - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include -#include -#include -#include -#include - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("System voltages control via Attansic ATXP1"); -MODULE_VERSION("0.6.2"); -MODULE_AUTHOR("Sebastian Witt "); - -#define ATXP1_VID 0x00 -#define ATXP1_CVID 0x01 -#define ATXP1_GPIO1 0x06 -#define ATXP1_GPIO2 0x0a -#define ATXP1_VIDENA 0x20 -#define ATXP1_VIDMASK 0x1f -#define ATXP1_GPIO1MASK 0x0f - -static unsigned short normal_i2c[] = { 0x37, 0x4e, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -SENSORS_INSMOD_1(atxp1); - -static int atxp1_attach_adapter(struct i2c_adapter * adapter); -static int atxp1_detach_client(struct i2c_client * client); -static struct atxp1_data * atxp1_update_device(struct device *dev); -static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind); - -static struct i2c_driver atxp1_driver = { - .owner = THIS_MODULE, - .name = "atxp1", - .flags = I2C_DF_NOTIFY, - .attach_adapter = atxp1_attach_adapter, - .detach_client = atxp1_detach_client, -}; - -struct atxp1_data { - struct i2c_client client; - struct semaphore update_lock; - unsigned long last_updated; - u8 valid; - struct { - u8 vid; /* VID output register */ - u8 cpu_vid; /* VID input from CPU */ - u8 gpio1; /* General purpose I/O register 1 */ - u8 gpio2; /* General purpose I/O register 2 */ - } reg; - u8 vrm; /* Detected CPU VRM */ -}; - -static struct atxp1_data * atxp1_update_device(struct device *dev) -{ - struct i2c_client *client; - struct atxp1_data *data; - - client = to_i2c_client(dev); - data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if ((jiffies - data->last_updated > HZ) || - (jiffies < data->last_updated) || - !data->valid) { - - /* Update local register data */ - data->reg.vid = i2c_smbus_read_byte_data(client, ATXP1_VID); - data->reg.cpu_vid = i2c_smbus_read_byte_data(client, ATXP1_CVID); - data->reg.gpio1 = i2c_smbus_read_byte_data(client, ATXP1_GPIO1); - data->reg.gpio2 = i2c_smbus_read_byte_data(client, ATXP1_GPIO2); - - data->valid = 1; - } - - up(&data->update_lock); - - return(data); -} - -/* sys file functions for cpu0_vid */ -static ssize_t atxp1_showvcore(struct device *dev, struct device_attribute *attr, char *buf) -{ - int size; - struct atxp1_data *data; - - data = atxp1_update_device(dev); - - size = sprintf(buf, "%d\n", vid_from_reg(data->reg.vid & ATXP1_VIDMASK, data->vrm)); - - return size; -} - -static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct atxp1_data *data; - struct i2c_client *client; - char vid; - char cvid; - unsigned int vcore; - - client = to_i2c_client(dev); - data = atxp1_update_device(dev); - - vcore = simple_strtoul(buf, NULL, 10); - vcore /= 25; - vcore *= 25; - - /* Calculate VID */ - vid = vid_to_reg(vcore, data->vrm); - - if (vid < 0) { - dev_err(dev, "VID calculation failed.\n"); - return -1; - } - - /* If output enabled, use control register value. Otherwise original CPU VID */ - if (data->reg.vid & ATXP1_VIDENA) - cvid = data->reg.vid & ATXP1_VIDMASK; - else - cvid = data->reg.cpu_vid; - - /* Nothing changed, aborting */ - if (vid == cvid) - return count; - - dev_dbg(dev, "Setting VCore to %d mV (0x%02x)\n", vcore, vid); - - /* Write every 25 mV step to increase stability */ - if (cvid > vid) { - for (; cvid >= vid; cvid--) { - i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA); - } - } - else { - for (; cvid <= vid; cvid++) { - i2c_smbus_write_byte_data(client, ATXP1_VID, cvid | ATXP1_VIDENA); - } - } - - data->valid = 0; - - return count; -} - -/* CPU core reference voltage - unit: millivolt -*/ -static DEVICE_ATTR(cpu0_vid, S_IRUGO | S_IWUSR, atxp1_showvcore, atxp1_storevcore); - -/* sys file functions for GPIO1 */ -static ssize_t atxp1_showgpio1(struct device *dev, struct device_attribute *attr, char *buf) -{ - int size; - struct atxp1_data *data; - - data = atxp1_update_device(dev); - - size = sprintf(buf, "0x%02x\n", data->reg.gpio1 & ATXP1_GPIO1MASK); - - return size; -} - -static ssize_t atxp1_storegpio1(struct device *dev, struct device_attribute *attr, const char*buf, size_t count) -{ - struct atxp1_data *data; - struct i2c_client *client; - unsigned int value; - - client = to_i2c_client(dev); - data = atxp1_update_device(dev); - - value = simple_strtoul(buf, NULL, 16); - - value &= ATXP1_GPIO1MASK; - - if (value != (data->reg.gpio1 & ATXP1_GPIO1MASK)) { - dev_info(dev, "Writing 0x%x to GPIO1.\n", value); - - i2c_smbus_write_byte_data(client, ATXP1_GPIO1, value); - - data->valid = 0; - } - - return count; -} - -/* GPIO1 data register - unit: Four bit as hex (e.g. 0x0f) -*/ -static DEVICE_ATTR(gpio1, S_IRUGO | S_IWUSR, atxp1_showgpio1, atxp1_storegpio1); - -/* sys file functions for GPIO2 */ -static ssize_t atxp1_showgpio2(struct device *dev, struct device_attribute *attr, char *buf) -{ - int size; - struct atxp1_data *data; - - data = atxp1_update_device(dev); - - size = sprintf(buf, "0x%02x\n", data->reg.gpio2); - - return size; -} - -static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct atxp1_data *data; - struct i2c_client *client; - unsigned int value; - - client = to_i2c_client(dev); - data = atxp1_update_device(dev); - - value = simple_strtoul(buf, NULL, 16) & 0xff; - - if (value != data->reg.gpio2) { - dev_info(dev, "Writing 0x%x to GPIO1.\n", value); - - i2c_smbus_write_byte_data(client, ATXP1_GPIO2, value); - - data->valid = 0; - } - - return count; -} - -/* GPIO2 data register - unit: Eight bit as hex (e.g. 0xff) -*/ -static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2); - - -static int atxp1_attach_adapter(struct i2c_adapter *adapter) -{ - return i2c_detect(adapter, &addr_data, &atxp1_detect); -}; - -static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client * new_client; - struct atxp1_data * data; - int err = 0; - u8 temp; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - if (!(data = kmalloc(sizeof(struct atxp1_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - - memset(data, 0, sizeof(struct atxp1_data)); - new_client = &data->client; - i2c_set_clientdata(new_client, data); - - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &atxp1_driver; - new_client->flags = 0; - - /* Detect ATXP1, checking if vendor ID registers are all zero */ - if (!((i2c_smbus_read_byte_data(new_client, 0x3e) == 0) && - (i2c_smbus_read_byte_data(new_client, 0x3f) == 0) && - (i2c_smbus_read_byte_data(new_client, 0xfe) == 0) && - (i2c_smbus_read_byte_data(new_client, 0xff) == 0) )) { - - /* No vendor ID, now checking if registers 0x10,0x11 (non-existent) - * showing the same as register 0x00 */ - temp = i2c_smbus_read_byte_data(new_client, 0x00); - - if (!((i2c_smbus_read_byte_data(new_client, 0x10) == temp) && - (i2c_smbus_read_byte_data(new_client, 0x11) == temp) )) - goto exit_free; - } - - /* Get VRM */ - data->vrm = i2c_which_vrm(); - - if ((data->vrm != 90) && (data->vrm != 91)) { - dev_err(&new_client->dev, "Not supporting VRM %d.%d\n", - data->vrm / 10, data->vrm % 10); - goto exit_free; - } - - strncpy(new_client->name, "atxp1", I2C_NAME_SIZE); - - data->valid = 0; - - init_MUTEX(&data->update_lock); - - err = i2c_attach_client(new_client); - - if (err) - { - dev_err(&new_client->dev, "Attach client error.\n"); - goto exit_free; - } - - device_create_file(&new_client->dev, &dev_attr_gpio1); - device_create_file(&new_client->dev, &dev_attr_gpio2); - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - - dev_info(&new_client->dev, "Using VRM: %d.%d\n", - data->vrm / 10, data->vrm % 10); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -}; - -static int atxp1_detach_client(struct i2c_client * client) -{ - int err; - - err = i2c_detach_client(client); - - if (err) - dev_err(&client->dev, "Failed to detach client.\n"); - else - kfree(i2c_get_clientdata(client)); - - return err; -}; - -static int __init atxp1_init(void) -{ - return i2c_add_driver(&atxp1_driver); -}; - -static void __exit atxp1_exit(void) -{ - i2c_del_driver(&atxp1_driver); -}; - -module_init(atxp1_init); -module_exit(atxp1_exit); diff --git a/drivers/i2c/chips/ds1621.c b/drivers/i2c/chips/ds1621.c deleted file mode 100644 index 5360d58804f6..000000000000 --- a/drivers/i2c/chips/ds1621.c +++ /dev/null @@ -1,341 +0,0 @@ -/* - ds1621.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Christian W. Zuckschwerdt 2000-11-23 - based on lm75.c by Frodo Looijaard - Ported to Linux 2.6 by Aurelien Jarno with - the help of Jean Delvare - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include "lm75.h" - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, - 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_1(ds1621); -static int polarity = -1; -module_param(polarity, int, 0); -MODULE_PARM_DESC(polarity, "Output's polarity: 0 = active high, 1 = active low"); - -/* Many DS1621 constants specified below */ -/* Config register used for detection */ -/* 7 6 5 4 3 2 1 0 */ -/* |Done|THF |TLF |NVB | X | X |POL |1SHOT| */ -#define DS1621_REG_CONFIG_NVB 0x10 -#define DS1621_REG_CONFIG_POLARITY 0x02 -#define DS1621_REG_CONFIG_1SHOT 0x01 -#define DS1621_REG_CONFIG_DONE 0x80 - -/* The DS1621 registers */ -#define DS1621_REG_TEMP 0xAA /* word, RO */ -#define DS1621_REG_TEMP_MIN 0xA1 /* word, RW */ -#define DS1621_REG_TEMP_MAX 0xA2 /* word, RW */ -#define DS1621_REG_CONF 0xAC /* byte, RW */ -#define DS1621_COM_START 0xEE /* no data */ -#define DS1621_COM_STOP 0x22 /* no data */ - -/* The DS1621 configuration register */ -#define DS1621_ALARM_TEMP_HIGH 0x40 -#define DS1621_ALARM_TEMP_LOW 0x20 - -/* Conversions. Rounding and limit checking is only done on the TO_REG - variants. Note that you should be a bit careful with which arguments - these macros are called: arguments may be evaluated more than once. - Fixing this is just not worth it. */ -#define ALARMS_FROM_REG(val) ((val) & \ - (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW)) - -/* Each client has this additional data */ -struct ds1621_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - u16 temp, temp_min, temp_max; /* Register values, word */ - u8 conf; /* Register encoding, combined */ -}; - -static int ds1621_attach_adapter(struct i2c_adapter *adapter); -static int ds1621_detect(struct i2c_adapter *adapter, int address, - int kind); -static void ds1621_init_client(struct i2c_client *client); -static int ds1621_detach_client(struct i2c_client *client); -static struct ds1621_data *ds1621_update_client(struct device *dev); - -/* This is the driver that will be inserted */ -static struct i2c_driver ds1621_driver = { - .owner = THIS_MODULE, - .name = "ds1621", - .id = I2C_DRIVERID_DS1621, - .flags = I2C_DF_NOTIFY, - .attach_adapter = ds1621_attach_adapter, - .detach_client = ds1621_detach_client, -}; - -/* All registers are word-sized, except for the configuration register. - DS1621 uses a high-byte first convention, which is exactly opposite to - the usual practice. */ -static int ds1621_read_value(struct i2c_client *client, u8 reg) -{ - if (reg == DS1621_REG_CONF) - return i2c_smbus_read_byte_data(client, reg); - else - return swab16(i2c_smbus_read_word_data(client, reg)); -} - -/* All registers are word-sized, except for the configuration register. - DS1621 uses a high-byte first convention, which is exactly opposite to - the usual practice. */ -static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value) -{ - if (reg == DS1621_REG_CONF) - return i2c_smbus_write_byte_data(client, reg, value); - else - return i2c_smbus_write_word_data(client, reg, swab16(value)); -} - -static void ds1621_init_client(struct i2c_client *client) -{ - int reg = ds1621_read_value(client, DS1621_REG_CONF); - /* switch to continuous conversion mode */ - reg &= ~ DS1621_REG_CONFIG_1SHOT; - - /* setup output polarity */ - if (polarity == 0) - reg &= ~DS1621_REG_CONFIG_POLARITY; - else if (polarity == 1) - reg |= DS1621_REG_CONFIG_POLARITY; - - ds1621_write_value(client, DS1621_REG_CONF, reg); - - /* start conversion */ - i2c_smbus_write_byte(client, DS1621_COM_START); -} - -#define show(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct ds1621_data *data = ds1621_update_client(dev); \ - return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->value)); \ -} - -show(temp); -show(temp_min); -show(temp_max); - -#define set_temp(suffix, value, reg) \ -static ssize_t set_temp_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct ds1621_data *data = ds1621_update_client(dev); \ - u16 val = LM75_TEMP_TO_REG(simple_strtoul(buf, NULL, 10)); \ - \ - down(&data->update_lock); \ - data->value = val; \ - ds1621_write_value(client, reg, data->value); \ - up(&data->update_lock); \ - return count; \ -} - -set_temp(min, temp_min, DS1621_REG_TEMP_MIN); -set_temp(max, temp_max, DS1621_REG_TEMP_MAX); - -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct ds1621_data *data = ds1621_update_client(dev); - return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->conf)); -} - -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); -static DEVICE_ATTR(temp1_input, S_IRUGO , show_temp, NULL); -static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO , show_temp_min, set_temp_min); -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max); - - -static int ds1621_attach_adapter(struct i2c_adapter *adapter) -{ - return i2c_detect(adapter, &addr_data, ds1621_detect); -} - -/* This function is called by i2c_detect */ -int ds1621_detect(struct i2c_adapter *adapter, int address, - int kind) -{ - int conf, temp; - struct i2c_client *new_client; - struct ds1621_data *data; - int err = 0; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA - | I2C_FUNC_SMBUS_WORD_DATA - | I2C_FUNC_SMBUS_WRITE_BYTE)) - goto exit; - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access ds1621_{read,write}_value. */ - if (!(data = kmalloc(sizeof(struct ds1621_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct ds1621_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &ds1621_driver; - new_client->flags = 0; - - - /* 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 = ds1621_read_value(new_client, DS1621_REG_CONF); - if (conf & DS1621_REG_CONFIG_NVB) - goto exit_free; - /* The 7 lowest bits of a temperature should always be 0. */ - temp = ds1621_read_value(new_client, DS1621_REG_TEMP); - if (temp & 0x007f) - goto exit_free; - temp = ds1621_read_value(new_client, DS1621_REG_TEMP_MIN); - if (temp & 0x007f) - goto exit_free; - temp = ds1621_read_value(new_client, DS1621_REG_TEMP_MAX); - if (temp & 0x007f) - goto exit_free; - } - - /* Determine the chip type - only one kind supported! */ - if (kind <= 0) - kind = ds1621; - - /* Fill in remaining client fields and put it into the global list */ - strlcpy(new_client->name, "ds1621", I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the DS1621 chip */ - ds1621_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - - return 0; - -/* OK, this is not exactly good programming practice, usually. But it is - very code-efficient in this case. */ - exit_free: - kfree(data); - exit: - return err; -} - -static int ds1621_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - - return 0; -} - - -static struct ds1621_data *ds1621_update_client(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct ds1621_data *data = i2c_get_clientdata(client); - u8 new_conf; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - - dev_dbg(&client->dev, "Starting ds1621 update\n"); - - data->conf = ds1621_read_value(client, DS1621_REG_CONF); - - data->temp = ds1621_read_value(client, DS1621_REG_TEMP); - - data->temp_min = ds1621_read_value(client, - DS1621_REG_TEMP_MIN); - data->temp_max = ds1621_read_value(client, - DS1621_REG_TEMP_MAX); - - /* reset alarms if necessary */ - new_conf = data->conf; - if (data->temp < data->temp_min) - new_conf &= ~DS1621_ALARM_TEMP_LOW; - if (data->temp > data->temp_max) - new_conf &= ~DS1621_ALARM_TEMP_HIGH; - if (data->conf != new_conf) - ds1621_write_value(client, DS1621_REG_CONF, - new_conf); - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init ds1621_init(void) -{ - return i2c_add_driver(&ds1621_driver); -} - -static void __exit ds1621_exit(void) -{ - i2c_del_driver(&ds1621_driver); -} - - -MODULE_AUTHOR("Christian W. Zuckschwerdt "); -MODULE_DESCRIPTION("DS1621 driver"); -MODULE_LICENSE("GPL"); - -module_init(ds1621_init); -module_exit(ds1621_exit); diff --git a/drivers/i2c/chips/fscher.c b/drivers/i2c/chips/fscher.c deleted file mode 100644 index da411741c2c5..000000000000 --- a/drivers/i2c/chips/fscher.c +++ /dev/null @@ -1,691 +0,0 @@ -/* - * fscher.c - Part of lm_sensors, Linux kernel modules for hardware - * monitoring - * Copyright (C) 2003, 2004 Reinhard Nissl - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* - * fujitsu siemens hermes chip, - * module based on fscpos.c - * Copyright (C) 2000 Hermann Jung - * Copyright (C) 1998, 1999 Frodo Looijaard - * and Philip Edelbrock - */ - -#include -#include -#include -#include -#include -#include - -/* - * Addresses to scan - */ - -static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* - * Insmod parameters - */ - -SENSORS_INSMOD_1(fscher); - -/* - * The FSCHER registers - */ - -/* chip identification */ -#define FSCHER_REG_IDENT_0 0x00 -#define FSCHER_REG_IDENT_1 0x01 -#define FSCHER_REG_IDENT_2 0x02 -#define FSCHER_REG_REVISION 0x03 - -/* global control and status */ -#define FSCHER_REG_EVENT_STATE 0x04 -#define FSCHER_REG_CONTROL 0x05 - -/* watchdog */ -#define FSCHER_REG_WDOG_PRESET 0x28 -#define FSCHER_REG_WDOG_STATE 0x23 -#define FSCHER_REG_WDOG_CONTROL 0x21 - -/* fan 0 */ -#define FSCHER_REG_FAN0_MIN 0x55 -#define FSCHER_REG_FAN0_ACT 0x0e -#define FSCHER_REG_FAN0_STATE 0x0d -#define FSCHER_REG_FAN0_RIPPLE 0x0f - -/* fan 1 */ -#define FSCHER_REG_FAN1_MIN 0x65 -#define FSCHER_REG_FAN1_ACT 0x6b -#define FSCHER_REG_FAN1_STATE 0x62 -#define FSCHER_REG_FAN1_RIPPLE 0x6f - -/* fan 2 */ -#define FSCHER_REG_FAN2_MIN 0xb5 -#define FSCHER_REG_FAN2_ACT 0xbb -#define FSCHER_REG_FAN2_STATE 0xb2 -#define FSCHER_REG_FAN2_RIPPLE 0xbf - -/* voltage supervision */ -#define FSCHER_REG_VOLT_12 0x45 -#define FSCHER_REG_VOLT_5 0x42 -#define FSCHER_REG_VOLT_BATT 0x48 - -/* temperature 0 */ -#define FSCHER_REG_TEMP0_ACT 0x64 -#define FSCHER_REG_TEMP0_STATE 0x71 - -/* temperature 1 */ -#define FSCHER_REG_TEMP1_ACT 0x32 -#define FSCHER_REG_TEMP1_STATE 0x81 - -/* temperature 2 */ -#define FSCHER_REG_TEMP2_ACT 0x35 -#define FSCHER_REG_TEMP2_STATE 0x91 - -/* - * Functions declaration - */ - -static int fscher_attach_adapter(struct i2c_adapter *adapter); -static int fscher_detect(struct i2c_adapter *adapter, int address, int kind); -static int fscher_detach_client(struct i2c_client *client); -static struct fscher_data *fscher_update_device(struct device *dev); -static void fscher_init_client(struct i2c_client *client); - -static int fscher_read_value(struct i2c_client *client, u8 reg); -static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value); - -/* - * Driver data (common to all clients) - */ - -static struct i2c_driver fscher_driver = { - .owner = THIS_MODULE, - .name = "fscher", - .id = I2C_DRIVERID_FSCHER, - .flags = I2C_DF_NOTIFY, - .attach_adapter = fscher_attach_adapter, - .detach_client = fscher_detach_client, -}; - -/* - * Client data (each client gets its own) - */ - -struct fscher_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* register values */ - u8 revision; /* revision of chip */ - u8 global_event; /* global event status */ - u8 global_control; /* global control register */ - u8 watchdog[3]; /* watchdog */ - u8 volt[3]; /* 12, 5, battery voltage */ - u8 temp_act[3]; /* temperature */ - u8 temp_status[3]; /* status of sensor */ - u8 fan_act[3]; /* fans revolutions per second */ - u8 fan_status[3]; /* fan status */ - u8 fan_min[3]; /* fan min value for rps */ - u8 fan_ripple[3]; /* divider for rps */ -}; - -/* - * Sysfs stuff - */ - -#define sysfs_r(kind, sub, offset, reg) \ -static ssize_t show_##kind##sub (struct fscher_data *, char *, int); \ -static ssize_t show_##kind##offset##sub (struct device *, struct device_attribute *attr, char *); \ -static ssize_t show_##kind##offset##sub (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct fscher_data *data = fscher_update_device(dev); \ - return show_##kind##sub(data, buf, (offset)); \ -} - -#define sysfs_w(kind, sub, offset, reg) \ -static ssize_t set_##kind##sub (struct i2c_client *, struct fscher_data *, const char *, size_t, int, int); \ -static ssize_t set_##kind##offset##sub (struct device *, struct device_attribute *attr, const char *, size_t); \ -static ssize_t set_##kind##offset##sub (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct fscher_data *data = i2c_get_clientdata(client); \ - return set_##kind##sub(client, data, buf, count, (offset), reg); \ -} - -#define sysfs_rw_n(kind, sub, offset, reg) \ -sysfs_r(kind, sub, offset, reg) \ -sysfs_w(kind, sub, offset, reg) \ -static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, show_##kind##offset##sub, set_##kind##offset##sub); - -#define sysfs_rw(kind, sub, reg) \ -sysfs_r(kind, sub, 0, reg) \ -sysfs_w(kind, sub, 0, reg) \ -static DEVICE_ATTR(kind##sub, S_IRUGO | S_IWUSR, show_##kind##0##sub, set_##kind##0##sub); - -#define sysfs_ro_n(kind, sub, offset, reg) \ -sysfs_r(kind, sub, offset, reg) \ -static DEVICE_ATTR(kind##offset##sub, S_IRUGO, show_##kind##offset##sub, NULL); - -#define sysfs_ro(kind, sub, reg) \ -sysfs_r(kind, sub, 0, reg) \ -static DEVICE_ATTR(kind, S_IRUGO, show_##kind##0##sub, NULL); - -#define sysfs_fan(offset, reg_status, reg_min, reg_ripple, reg_act) \ -sysfs_rw_n(pwm, , offset, reg_min) \ -sysfs_rw_n(fan, _status, offset, reg_status) \ -sysfs_rw_n(fan, _div , offset, reg_ripple) \ -sysfs_ro_n(fan, _input , offset, reg_act) - -#define sysfs_temp(offset, reg_status, reg_act) \ -sysfs_rw_n(temp, _status, offset, reg_status) \ -sysfs_ro_n(temp, _input , offset, reg_act) - -#define sysfs_in(offset, reg_act) \ -sysfs_ro_n(in, _input, offset, reg_act) - -#define sysfs_revision(reg_revision) \ -sysfs_ro(revision, , reg_revision) - -#define sysfs_alarms(reg_events) \ -sysfs_ro(alarms, , reg_events) - -#define sysfs_control(reg_control) \ -sysfs_rw(control, , reg_control) - -#define sysfs_watchdog(reg_control, reg_status, reg_preset) \ -sysfs_rw(watchdog, _control, reg_control) \ -sysfs_rw(watchdog, _status , reg_status) \ -sysfs_rw(watchdog, _preset , reg_preset) - -sysfs_fan(1, FSCHER_REG_FAN0_STATE, FSCHER_REG_FAN0_MIN, - FSCHER_REG_FAN0_RIPPLE, FSCHER_REG_FAN0_ACT) -sysfs_fan(2, FSCHER_REG_FAN1_STATE, FSCHER_REG_FAN1_MIN, - FSCHER_REG_FAN1_RIPPLE, FSCHER_REG_FAN1_ACT) -sysfs_fan(3, FSCHER_REG_FAN2_STATE, FSCHER_REG_FAN2_MIN, - FSCHER_REG_FAN2_RIPPLE, FSCHER_REG_FAN2_ACT) - -sysfs_temp(1, FSCHER_REG_TEMP0_STATE, FSCHER_REG_TEMP0_ACT) -sysfs_temp(2, FSCHER_REG_TEMP1_STATE, FSCHER_REG_TEMP1_ACT) -sysfs_temp(3, FSCHER_REG_TEMP2_STATE, FSCHER_REG_TEMP2_ACT) - -sysfs_in(0, FSCHER_REG_VOLT_12) -sysfs_in(1, FSCHER_REG_VOLT_5) -sysfs_in(2, FSCHER_REG_VOLT_BATT) - -sysfs_revision(FSCHER_REG_REVISION) -sysfs_alarms(FSCHER_REG_EVENTS) -sysfs_control(FSCHER_REG_CONTROL) -sysfs_watchdog(FSCHER_REG_WDOG_CONTROL, FSCHER_REG_WDOG_STATE, FSCHER_REG_WDOG_PRESET) - -#define device_create_file_fan(client, offset) \ -do { \ - device_create_file(&client->dev, &dev_attr_fan##offset##_status); \ - device_create_file(&client->dev, &dev_attr_pwm##offset); \ - device_create_file(&client->dev, &dev_attr_fan##offset##_div); \ - device_create_file(&client->dev, &dev_attr_fan##offset##_input); \ -} while (0) - -#define device_create_file_temp(client, offset) \ -do { \ - device_create_file(&client->dev, &dev_attr_temp##offset##_status); \ - device_create_file(&client->dev, &dev_attr_temp##offset##_input); \ -} while (0) - -#define device_create_file_in(client, offset) \ -do { \ - device_create_file(&client->dev, &dev_attr_in##offset##_input); \ -} while (0) - -#define device_create_file_revision(client) \ -do { \ - device_create_file(&client->dev, &dev_attr_revision); \ -} while (0) - -#define device_create_file_alarms(client) \ -do { \ - device_create_file(&client->dev, &dev_attr_alarms); \ -} while (0) - -#define device_create_file_control(client) \ -do { \ - device_create_file(&client->dev, &dev_attr_control); \ -} while (0) - -#define device_create_file_watchdog(client) \ -do { \ - device_create_file(&client->dev, &dev_attr_watchdog_status); \ - device_create_file(&client->dev, &dev_attr_watchdog_control); \ - device_create_file(&client->dev, &dev_attr_watchdog_preset); \ -} while (0) - -/* - * Real code - */ - -static int fscher_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, fscher_detect); -} - -static int fscher_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct fscher_data *data; - int err = 0; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - /* OK. For now, we presume we have a valid client. We now create the - * client structure, even though we cannot fill it completely yet. - * But it allows us to access i2c_smbus_read_byte_data. */ - if (!(data = kmalloc(sizeof(struct fscher_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct fscher_data)); - - /* The common I2C client data is placed right before the - * Hermes-specific data. */ - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &fscher_driver; - new_client->flags = 0; - - /* Do the remaining detection unless force or force_fscher parameter */ - if (kind < 0) { - if ((i2c_smbus_read_byte_data(new_client, - FSCHER_REG_IDENT_0) != 0x48) /* 'H' */ - || (i2c_smbus_read_byte_data(new_client, - FSCHER_REG_IDENT_1) != 0x45) /* 'E' */ - || (i2c_smbus_read_byte_data(new_client, - FSCHER_REG_IDENT_2) != 0x52)) /* 'R' */ - goto exit_free; - } - - /* Fill in the remaining client fields and put it into the - * global list */ - strlcpy(new_client->name, "fscher", I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - fscher_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file_revision(new_client); - device_create_file_alarms(new_client); - device_create_file_control(new_client); - device_create_file_watchdog(new_client); - - device_create_file_in(new_client, 0); - device_create_file_in(new_client, 1); - device_create_file_in(new_client, 2); - - device_create_file_fan(new_client, 1); - device_create_file_fan(new_client, 2); - device_create_file_fan(new_client, 3); - - device_create_file_temp(new_client, 1); - device_create_file_temp(new_client, 2); - device_create_file_temp(new_client, 3); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static int fscher_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - -static int fscher_read_value(struct i2c_client *client, u8 reg) -{ - dev_dbg(&client->dev, "read reg 0x%02x\n", reg); - - return i2c_smbus_read_byte_data(client, reg); -} - -static int fscher_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - dev_dbg(&client->dev, "write reg 0x%02x, val 0x%02x\n", - reg, value); - - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Called when we have found a new FSC Hermes. */ -static void fscher_init_client(struct i2c_client *client) -{ - struct fscher_data *data = i2c_get_clientdata(client); - - /* Read revision from chip */ - data->revision = fscher_read_value(client, FSCHER_REG_REVISION); -} - -static struct fscher_data *fscher_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct fscher_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { - - dev_dbg(&client->dev, "Starting fscher update\n"); - - data->temp_act[0] = fscher_read_value(client, FSCHER_REG_TEMP0_ACT); - data->temp_act[1] = fscher_read_value(client, FSCHER_REG_TEMP1_ACT); - data->temp_act[2] = fscher_read_value(client, FSCHER_REG_TEMP2_ACT); - data->temp_status[0] = fscher_read_value(client, FSCHER_REG_TEMP0_STATE); - data->temp_status[1] = fscher_read_value(client, FSCHER_REG_TEMP1_STATE); - data->temp_status[2] = fscher_read_value(client, FSCHER_REG_TEMP2_STATE); - - data->volt[0] = fscher_read_value(client, FSCHER_REG_VOLT_12); - data->volt[1] = fscher_read_value(client, FSCHER_REG_VOLT_5); - data->volt[2] = fscher_read_value(client, FSCHER_REG_VOLT_BATT); - - data->fan_act[0] = fscher_read_value(client, FSCHER_REG_FAN0_ACT); - data->fan_act[1] = fscher_read_value(client, FSCHER_REG_FAN1_ACT); - data->fan_act[2] = fscher_read_value(client, FSCHER_REG_FAN2_ACT); - data->fan_status[0] = fscher_read_value(client, FSCHER_REG_FAN0_STATE); - data->fan_status[1] = fscher_read_value(client, FSCHER_REG_FAN1_STATE); - data->fan_status[2] = fscher_read_value(client, FSCHER_REG_FAN2_STATE); - data->fan_min[0] = fscher_read_value(client, FSCHER_REG_FAN0_MIN); - data->fan_min[1] = fscher_read_value(client, FSCHER_REG_FAN1_MIN); - data->fan_min[2] = fscher_read_value(client, FSCHER_REG_FAN2_MIN); - data->fan_ripple[0] = fscher_read_value(client, FSCHER_REG_FAN0_RIPPLE); - data->fan_ripple[1] = fscher_read_value(client, FSCHER_REG_FAN1_RIPPLE); - data->fan_ripple[2] = fscher_read_value(client, FSCHER_REG_FAN2_RIPPLE); - - data->watchdog[0] = fscher_read_value(client, FSCHER_REG_WDOG_PRESET); - data->watchdog[1] = fscher_read_value(client, FSCHER_REG_WDOG_STATE); - data->watchdog[2] = fscher_read_value(client, FSCHER_REG_WDOG_CONTROL); - - data->global_event = fscher_read_value(client, FSCHER_REG_EVENT_STATE); - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - - - -#define FAN_INDEX_FROM_NUM(nr) ((nr) - 1) - -static ssize_t set_fan_status(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* bits 0..1, 3..7 reserved => mask with 0x04 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x04; - - down(&data->update_lock); - data->fan_status[FAN_INDEX_FROM_NUM(nr)] &= ~v; - fscher_write_value(client, reg, v); - up(&data->update_lock); - return count; -} - -static ssize_t show_fan_status(struct fscher_data *data, char *buf, int nr) -{ - /* bits 0..1, 3..7 reserved => mask with 0x04 */ - return sprintf(buf, "%u\n", data->fan_status[FAN_INDEX_FROM_NUM(nr)] & 0x04); -} - -static ssize_t set_pwm(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->fan_min[FAN_INDEX_FROM_NUM(nr)] = v > 0xff ? 0xff : v; - fscher_write_value(client, reg, data->fan_min[FAN_INDEX_FROM_NUM(nr)]); - up(&data->update_lock); - return count; -} - -static ssize_t show_pwm(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", data->fan_min[FAN_INDEX_FROM_NUM(nr)]); -} - -static ssize_t set_fan_div(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* supported values: 2, 4, 8 */ - unsigned long v = simple_strtoul(buf, NULL, 10); - - switch (v) { - case 2: v = 1; break; - case 4: v = 2; break; - case 8: v = 3; break; - default: - dev_err(&client->dev, "fan_div value %ld not " - "supported. Choose one of 2, 4 or 8!\n", v); - return -EINVAL; - } - - down(&data->update_lock); - - /* bits 2..7 reserved => mask with 0x03 */ - data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] &= ~0x03; - data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] |= v; - - fscher_write_value(client, reg, data->fan_ripple[FAN_INDEX_FROM_NUM(nr)]); - up(&data->update_lock); - return count; -} - -static ssize_t show_fan_div(struct fscher_data *data, char *buf, int nr) -{ - /* bits 2..7 reserved => mask with 0x03 */ - return sprintf(buf, "%u\n", 1 << (data->fan_ripple[FAN_INDEX_FROM_NUM(nr)] & 0x03)); -} - -#define RPM_FROM_REG(val) (val*60) - -static ssize_t show_fan_input (struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[FAN_INDEX_FROM_NUM(nr)])); -} - - - -#define TEMP_INDEX_FROM_NUM(nr) ((nr) - 1) - -static ssize_t set_temp_status(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* bits 2..7 reserved, 0 read only => mask with 0x02 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; - - down(&data->update_lock); - data->temp_status[TEMP_INDEX_FROM_NUM(nr)] &= ~v; - fscher_write_value(client, reg, v); - up(&data->update_lock); - return count; -} - -static ssize_t show_temp_status(struct fscher_data *data, char *buf, int nr) -{ - /* bits 2..7 reserved => mask with 0x03 */ - return sprintf(buf, "%u\n", data->temp_status[TEMP_INDEX_FROM_NUM(nr)] & 0x03); -} - -#define TEMP_FROM_REG(val) (((val) - 128) * 1000) - -static ssize_t show_temp_input(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[TEMP_INDEX_FROM_NUM(nr)])); -} - -/* - * The final conversion is specified in sensors.conf, as it depends on - * mainboard specific values. We export the registers contents as - * pseudo-hundredths-of-Volts (range 0V - 2.55V). Not that it makes much - * sense per se, but it minimizes the conversions count and keeps the - * values within a usual range. - */ -#define VOLT_FROM_REG(val) ((val) * 10) - -static ssize_t show_in_input(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[nr])); -} - - - -static ssize_t show_revision(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", data->revision); -} - - - -static ssize_t show_alarms(struct fscher_data *data, char *buf, int nr) -{ - /* bits 2, 5..6 reserved => mask with 0x9b */ - return sprintf(buf, "%u\n", data->global_event & 0x9b); -} - - - -static ssize_t set_control(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* bits 1..7 reserved => mask with 0x01 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x01; - - down(&data->update_lock); - data->global_control &= ~v; - fscher_write_value(client, reg, v); - up(&data->update_lock); - return count; -} - -static ssize_t show_control(struct fscher_data *data, char *buf, int nr) -{ - /* bits 1..7 reserved => mask with 0x01 */ - return sprintf(buf, "%u\n", data->global_control & 0x01); -} - - - -static ssize_t set_watchdog_control(struct i2c_client *client, struct - fscher_data *data, const char *buf, size_t count, - int nr, int reg) -{ - /* bits 0..3 reserved => mask with 0xf0 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0; - - down(&data->update_lock); - data->watchdog[2] &= ~0xf0; - data->watchdog[2] |= v; - fscher_write_value(client, reg, data->watchdog[2]); - up(&data->update_lock); - return count; -} - -static ssize_t show_watchdog_control(struct fscher_data *data, char *buf, int nr) -{ - /* bits 0..3 reserved, bit 5 write only => mask with 0xd0 */ - return sprintf(buf, "%u\n", data->watchdog[2] & 0xd0); -} - -static ssize_t set_watchdog_status(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - /* bits 0, 2..7 reserved => mask with 0x02 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; - - down(&data->update_lock); - data->watchdog[1] &= ~v; - fscher_write_value(client, reg, v); - up(&data->update_lock); - return count; -} - -static ssize_t show_watchdog_status(struct fscher_data *data, char *buf, int nr) -{ - /* bits 0, 2..7 reserved => mask with 0x02 */ - return sprintf(buf, "%u\n", data->watchdog[1] & 0x02); -} - -static ssize_t set_watchdog_preset(struct i2c_client *client, struct fscher_data *data, - const char *buf, size_t count, int nr, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff; - - down(&data->update_lock); - data->watchdog[0] = v; - fscher_write_value(client, reg, data->watchdog[0]); - up(&data->update_lock); - return count; -} - -static ssize_t show_watchdog_preset(struct fscher_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", data->watchdog[0]); -} - -static int __init sensors_fscher_init(void) -{ - return i2c_add_driver(&fscher_driver); -} - -static void __exit sensors_fscher_exit(void) -{ - i2c_del_driver(&fscher_driver); -} - -MODULE_AUTHOR("Reinhard Nissl "); -MODULE_DESCRIPTION("FSC Hermes driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_fscher_init); -module_exit(sensors_fscher_exit); diff --git a/drivers/i2c/chips/fscpos.c b/drivers/i2c/chips/fscpos.c deleted file mode 100644 index 3beaa6191ef4..000000000000 --- a/drivers/i2c/chips/fscpos.c +++ /dev/null @@ -1,641 +0,0 @@ -/* - fscpos.c - Kernel module for hardware monitoring with FSC Poseidon chips - Copyright (C) 2004, 2005 Stefan Ott - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - fujitsu siemens poseidon chip, - module based on the old fscpos module by Hermann Jung and - the fscher module by Reinhard Nissl - - original module based on lm80.c - Copyright (C) 1998, 1999 Frodo Looijaard - and Philip Edelbrock - - Thanks to Jean Delvare for reviewing my code and suggesting a lot of - improvements. -*/ - -#include -#include -#include -#include -#include - -/* - * Addresses to scan - */ -static unsigned short normal_i2c[] = { 0x73, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* - * Insmod parameters - */ -SENSORS_INSMOD_1(fscpos); - -/* - * The FSCPOS registers - */ - -/* chip identification */ -#define FSCPOS_REG_IDENT_0 0x00 -#define FSCPOS_REG_IDENT_1 0x01 -#define FSCPOS_REG_IDENT_2 0x02 -#define FSCPOS_REG_REVISION 0x03 - -/* global control and status */ -#define FSCPOS_REG_EVENT_STATE 0x04 -#define FSCPOS_REG_CONTROL 0x05 - -/* watchdog */ -#define FSCPOS_REG_WDOG_PRESET 0x28 -#define FSCPOS_REG_WDOG_STATE 0x23 -#define FSCPOS_REG_WDOG_CONTROL 0x21 - -/* voltages */ -#define FSCPOS_REG_VOLT_12 0x45 -#define FSCPOS_REG_VOLT_5 0x42 -#define FSCPOS_REG_VOLT_BATT 0x48 - -/* fans - the chip does not support minimum speed for fan2 */ -static u8 FSCPOS_REG_PWM[] = { 0x55, 0x65 }; -static u8 FSCPOS_REG_FAN_ACT[] = { 0x0e, 0x6b, 0xab }; -static u8 FSCPOS_REG_FAN_STATE[] = { 0x0d, 0x62, 0xa2 }; -static u8 FSCPOS_REG_FAN_RIPPLE[] = { 0x0f, 0x6f, 0xaf }; - -/* temperatures */ -static u8 FSCPOS_REG_TEMP_ACT[] = { 0x64, 0x32, 0x35 }; -static u8 FSCPOS_REG_TEMP_STATE[] = { 0x71, 0x81, 0x91 }; - -/* - * Functions declaration - */ -static int fscpos_attach_adapter(struct i2c_adapter *adapter); -static int fscpos_detect(struct i2c_adapter *adapter, int address, int kind); -static int fscpos_detach_client(struct i2c_client *client); - -static int fscpos_read_value(struct i2c_client *client, u8 register); -static int fscpos_write_value(struct i2c_client *client, u8 register, u8 value); -static struct fscpos_data *fscpos_update_device(struct device *dev); -static void fscpos_init_client(struct i2c_client *client); - -static void reset_fan_alarm(struct i2c_client *client, int nr); - -/* - * Driver data (common to all clients) - */ -static struct i2c_driver fscpos_driver = { - .owner = THIS_MODULE, - .name = "fscpos", - .id = I2C_DRIVERID_FSCPOS, - .flags = I2C_DF_NOTIFY, - .attach_adapter = fscpos_attach_adapter, - .detach_client = fscpos_detach_client, -}; - -/* - * Client data (each client gets its own) - */ -struct fscpos_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* 0 until following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - /* register values */ - u8 revision; /* revision of chip */ - u8 global_event; /* global event status */ - u8 global_control; /* global control register */ - u8 wdog_control; /* watchdog control */ - u8 wdog_state; /* watchdog status */ - u8 wdog_preset; /* watchdog preset */ - u8 volt[3]; /* 12, 5, battery current */ - u8 temp_act[3]; /* temperature */ - u8 temp_status[3]; /* status of sensor */ - u8 fan_act[3]; /* fans revolutions per second */ - u8 fan_status[3]; /* fan status */ - u8 pwm[2]; /* fan min value for rps */ - u8 fan_ripple[3]; /* divider for rps */ -}; - -/* Temperature */ -#define TEMP_FROM_REG(val) (((val) - 128) * 1000) - -static ssize_t show_temp_input(struct fscpos_data *data, char *buf, int nr) -{ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_act[nr - 1])); -} - -static ssize_t show_temp_status(struct fscpos_data *data, char *buf, int nr) -{ - /* bits 2..7 reserved => mask with 0x03 */ - return sprintf(buf, "%u\n", data->temp_status[nr - 1] & 0x03); -} - -static ssize_t show_temp_reset(struct fscpos_data *data, char *buf, int nr) -{ - return sprintf(buf, "1\n"); -} - -static ssize_t set_temp_reset(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int nr, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10); - if (v != 1) { - dev_err(&client->dev, "temp_reset value %ld not supported. " - "Use 1 to reset the alarm!\n", v); - return -EINVAL; - } - - dev_info(&client->dev, "You used the temp_reset feature which has not " - "been proplerly tested. Please report your " - "experience to the module author.\n"); - - /* Supported value: 2 (clears the status) */ - fscpos_write_value(client, FSCPOS_REG_TEMP_STATE[nr], 2); - return count; -} - -/* Fans */ -#define RPM_FROM_REG(val) ((val) * 60) - -static ssize_t show_fan_status(struct fscpos_data *data, char *buf, int nr) -{ - /* bits 0..1, 3..7 reserved => mask with 0x04 */ - return sprintf(buf, "%u\n", data->fan_status[nr - 1] & 0x04); -} - -static ssize_t show_fan_input(struct fscpos_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", RPM_FROM_REG(data->fan_act[nr - 1])); -} - -static ssize_t show_fan_ripple(struct fscpos_data *data, char *buf, int nr) -{ - /* bits 2..7 reserved => mask with 0x03 */ - return sprintf(buf, "%u\n", data->fan_ripple[nr - 1] & 0x03); -} - -static ssize_t set_fan_ripple(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int nr, int reg) -{ - /* supported values: 2, 4, 8 */ - unsigned long v = simple_strtoul(buf, NULL, 10); - - switch (v) { - case 2: v = 1; break; - case 4: v = 2; break; - case 8: v = 3; break; - default: - dev_err(&client->dev, "fan_ripple value %ld not supported. " - "Must be one of 2, 4 or 8!\n", v); - return -EINVAL; - } - - down(&data->update_lock); - /* bits 2..7 reserved => mask with 0x03 */ - data->fan_ripple[nr - 1] &= ~0x03; - data->fan_ripple[nr - 1] |= v; - - fscpos_write_value(client, reg, data->fan_ripple[nr - 1]); - up(&data->update_lock); - return count; -} - -static ssize_t show_pwm(struct fscpos_data *data, char *buf, int nr) -{ - return sprintf(buf, "%u\n", data->pwm[nr - 1]); -} - -static ssize_t set_pwm(struct i2c_client *client, struct fscpos_data *data, - const char *buf, size_t count, int nr, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10); - - /* Range: 0..255 */ - if (v < 0) v = 0; - if (v > 255) v = 255; - - down(&data->update_lock); - data->pwm[nr - 1] = v; - fscpos_write_value(client, reg, data->pwm[nr - 1]); - up(&data->update_lock); - return count; -} - -static void reset_fan_alarm(struct i2c_client *client, int nr) -{ - fscpos_write_value(client, FSCPOS_REG_FAN_STATE[nr], 4); -} - -/* Volts */ -#define VOLT_FROM_REG(val, mult) ((val) * (mult) / 255) - -static ssize_t show_volt_12(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct fscpos_data *data = fscpos_update_device(dev); - return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[0], 14200)); -} - -static ssize_t show_volt_5(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct fscpos_data *data = fscpos_update_device(dev); - return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[1], 6600)); -} - -static ssize_t show_volt_batt(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct fscpos_data *data = fscpos_update_device(dev); - return sprintf(buf, "%u\n", VOLT_FROM_REG(data->volt[2], 3300)); -} - -/* Watchdog */ -static ssize_t show_wdog_control(struct fscpos_data *data, char *buf) -{ - /* bits 0..3 reserved, bit 6 write only => mask with 0xb0 */ - return sprintf(buf, "%u\n", data->wdog_control & 0xb0); -} - -static ssize_t set_wdog_control(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int reg) -{ - /* bits 0..3 reserved => mask with 0xf0 */ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0xf0; - - down(&data->update_lock); - data->wdog_control &= ~0xf0; - data->wdog_control |= v; - fscpos_write_value(client, reg, data->wdog_control); - up(&data->update_lock); - return count; -} - -static ssize_t show_wdog_state(struct fscpos_data *data, char *buf) -{ - /* bits 0, 2..7 reserved => mask with 0x02 */ - return sprintf(buf, "%u\n", data->wdog_state & 0x02); -} - -static ssize_t set_wdog_state(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0x02; - - /* Valid values: 2 (clear) */ - if (v != 2) { - dev_err(&client->dev, "wdog_state value %ld not supported. " - "Must be 2 to clear the state!\n", v); - return -EINVAL; - } - - down(&data->update_lock); - data->wdog_state &= ~v; - fscpos_write_value(client, reg, v); - up(&data->update_lock); - return count; -} - -static ssize_t show_wdog_preset(struct fscpos_data *data, char *buf) -{ - return sprintf(buf, "%u\n", data->wdog_preset); -} - -static ssize_t set_wdog_preset(struct i2c_client *client, struct fscpos_data - *data, const char *buf, size_t count, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10) & 0xff; - - down(&data->update_lock); - data->wdog_preset = v; - fscpos_write_value(client, reg, data->wdog_preset); - up(&data->update_lock); - return count; -} - -/* Event */ -static ssize_t show_event(struct device *dev, struct device_attribute *attr, char *buf) -{ - /* bits 5..7 reserved => mask with 0x1f */ - struct fscpos_data *data = fscpos_update_device(dev); - return sprintf(buf, "%u\n", data->global_event & 0x9b); -} - -/* - * Sysfs stuff - */ -#define create_getter(kind, sub) \ - static ssize_t sysfs_show_##kind##sub(struct device *dev, struct device_attribute *attr, char *buf) \ - { \ - struct fscpos_data *data = fscpos_update_device(dev); \ - return show_##kind##sub(data, buf); \ - } - -#define create_getter_n(kind, offset, sub) \ - static ssize_t sysfs_show_##kind##offset##sub(struct device *dev, struct device_attribute *attr, char\ - *buf) \ - { \ - struct fscpos_data *data = fscpos_update_device(dev); \ - return show_##kind##sub(data, buf, offset); \ - } - -#define create_setter(kind, sub, reg) \ - static ssize_t sysfs_set_##kind##sub (struct device *dev, struct device_attribute *attr, const char \ - *buf, size_t count) \ - { \ - struct i2c_client *client = to_i2c_client(dev); \ - struct fscpos_data *data = i2c_get_clientdata(client); \ - return set_##kind##sub(client, data, buf, count, reg); \ - } - -#define create_setter_n(kind, offset, sub, reg) \ - static ssize_t sysfs_set_##kind##offset##sub (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ - { \ - struct i2c_client *client = to_i2c_client(dev); \ - struct fscpos_data *data = i2c_get_clientdata(client); \ - return set_##kind##sub(client, data, buf, count, offset, reg);\ - } - -#define create_sysfs_device_ro(kind, sub, offset) \ - static DEVICE_ATTR(kind##offset##sub, S_IRUGO, \ - sysfs_show_##kind##offset##sub, NULL); - -#define create_sysfs_device_rw(kind, sub, offset) \ - static DEVICE_ATTR(kind##offset##sub, S_IRUGO | S_IWUSR, \ - sysfs_show_##kind##offset##sub, sysfs_set_##kind##offset##sub); - -#define sysfs_ro_n(kind, sub, offset) \ - create_getter_n(kind, offset, sub); \ - create_sysfs_device_ro(kind, sub, offset); - -#define sysfs_rw_n(kind, sub, offset, reg) \ - create_getter_n(kind, offset, sub); \ - create_setter_n(kind, offset, sub, reg); \ - create_sysfs_device_rw(kind, sub, offset); - -#define sysfs_rw(kind, sub, reg) \ - create_getter(kind, sub); \ - create_setter(kind, sub, reg); \ - create_sysfs_device_rw(kind, sub,); - -#define sysfs_fan_with_min(offset, reg_status, reg_ripple, reg_min) \ - sysfs_fan(offset, reg_status, reg_ripple); \ - sysfs_rw_n(pwm,, offset, reg_min); - -#define sysfs_fan(offset, reg_status, reg_ripple) \ - sysfs_ro_n(fan, _input, offset); \ - sysfs_ro_n(fan, _status, offset); \ - sysfs_rw_n(fan, _ripple, offset, reg_ripple); - -#define sysfs_temp(offset, reg_status) \ - sysfs_ro_n(temp, _input, offset); \ - sysfs_ro_n(temp, _status, offset); \ - sysfs_rw_n(temp, _reset, offset, reg_status); - -#define sysfs_watchdog(reg_wdog_preset, reg_wdog_state, reg_wdog_control) \ - sysfs_rw(wdog, _control, reg_wdog_control); \ - sysfs_rw(wdog, _preset, reg_wdog_preset); \ - sysfs_rw(wdog, _state, reg_wdog_state); - -sysfs_fan_with_min(1, FSCPOS_REG_FAN_STATE[0], FSCPOS_REG_FAN_RIPPLE[0], - FSCPOS_REG_PWM[0]); -sysfs_fan_with_min(2, FSCPOS_REG_FAN_STATE[1], FSCPOS_REG_FAN_RIPPLE[1], - FSCPOS_REG_PWM[1]); -sysfs_fan(3, FSCPOS_REG_FAN_STATE[2], FSCPOS_REG_FAN_RIPPLE[2]); - -sysfs_temp(1, FSCPOS_REG_TEMP_STATE[0]); -sysfs_temp(2, FSCPOS_REG_TEMP_STATE[1]); -sysfs_temp(3, FSCPOS_REG_TEMP_STATE[2]); - -sysfs_watchdog(FSCPOS_REG_WDOG_PRESET, FSCPOS_REG_WDOG_STATE, - FSCPOS_REG_WDOG_CONTROL); - -static DEVICE_ATTR(event, S_IRUGO, show_event, NULL); -static DEVICE_ATTR(in0_input, S_IRUGO, show_volt_12, NULL); -static DEVICE_ATTR(in1_input, S_IRUGO, show_volt_5, NULL); -static DEVICE_ATTR(in2_input, S_IRUGO, show_volt_batt, NULL); - -static int fscpos_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, fscpos_detect); -} - -int fscpos_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct fscpos_data *data; - int err = 0; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - /* - * OK. For now, we presume we have a valid client. We now create the - * client structure, even though we cannot fill it completely yet. - * But it allows us to access fscpos_{read,write}_value. - */ - - if (!(data = kmalloc(sizeof(struct fscpos_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct fscpos_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &fscpos_driver; - new_client->flags = 0; - - /* Do the remaining detection unless force or force_fscpos parameter */ - if (kind < 0) { - if ((fscpos_read_value(new_client, FSCPOS_REG_IDENT_0) - != 0x50) /* 'P' */ - || (fscpos_read_value(new_client, FSCPOS_REG_IDENT_1) - != 0x45) /* 'E' */ - || (fscpos_read_value(new_client, FSCPOS_REG_IDENT_2) - != 0x47))/* 'G' */ - { - dev_dbg(&new_client->dev, "fscpos detection failed\n"); - goto exit_free; - } - } - - /* Fill in the remaining client fields and put it in the global list */ - strlcpy(new_client->name, "fscpos", I2C_NAME_SIZE); - - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Inizialize the fscpos chip */ - fscpos_init_client(new_client); - - /* Announce that the chip was found */ - dev_info(&new_client->dev, "Found fscpos chip, rev %u\n", data->revision); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_event); - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_wdog_control); - device_create_file(&new_client->dev, &dev_attr_wdog_preset); - device_create_file(&new_client->dev, &dev_attr_wdog_state); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_status); - device_create_file(&new_client->dev, &dev_attr_temp1_reset); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp2_status); - device_create_file(&new_client->dev, &dev_attr_temp2_reset); - device_create_file(&new_client->dev, &dev_attr_temp3_input); - device_create_file(&new_client->dev, &dev_attr_temp3_status); - device_create_file(&new_client->dev, &dev_attr_temp3_reset); - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_status); - device_create_file(&new_client->dev, &dev_attr_fan1_ripple); - device_create_file(&new_client->dev, &dev_attr_pwm1); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_status); - device_create_file(&new_client->dev, &dev_attr_fan2_ripple); - device_create_file(&new_client->dev, &dev_attr_pwm2); - device_create_file(&new_client->dev, &dev_attr_fan3_input); - device_create_file(&new_client->dev, &dev_attr_fan3_status); - device_create_file(&new_client->dev, &dev_attr_fan3_ripple); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static int fscpos_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, client" - " not detached.\n"); - return err; - } - kfree(i2c_get_clientdata(client)); - return 0; -} - -static int fscpos_read_value(struct i2c_client *client, u8 reg) -{ - dev_dbg(&client->dev, "Read reg 0x%02x\n", reg); - return i2c_smbus_read_byte_data(client, reg); -} - -static int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - dev_dbg(&client->dev, "Write reg 0x%02x, val 0x%02x\n", reg, value); - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Called when we have found a new FSCPOS chip */ -static void fscpos_init_client(struct i2c_client *client) -{ - struct fscpos_data *data = i2c_get_clientdata(client); - - /* read revision from chip */ - data->revision = fscpos_read_value(client, FSCPOS_REG_REVISION); -} - -static struct fscpos_data *fscpos_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct fscpos_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if ((jiffies - data->last_updated > 2 * HZ) || - (jiffies < data->last_updated) || !data->valid) { - int i; - - dev_dbg(&client->dev, "Starting fscpos update\n"); - - for (i = 0; i < 3; i++) { - data->temp_act[i] = fscpos_read_value(client, - FSCPOS_REG_TEMP_ACT[i]); - data->temp_status[i] = fscpos_read_value(client, - FSCPOS_REG_TEMP_STATE[i]); - data->fan_act[i] = fscpos_read_value(client, - FSCPOS_REG_FAN_ACT[i]); - data->fan_status[i] = fscpos_read_value(client, - FSCPOS_REG_FAN_STATE[i]); - data->fan_ripple[i] = fscpos_read_value(client, - FSCPOS_REG_FAN_RIPPLE[i]); - if (i < 2) { - /* fan2_min is not supported by the chip */ - data->pwm[i] = fscpos_read_value(client, - FSCPOS_REG_PWM[i]); - } - /* reset fan status if speed is back to > 0 */ - if (data->fan_status[i] != 0 && data->fan_act[i] > 0) { - reset_fan_alarm(client, i); - } - } - - data->volt[0] = fscpos_read_value(client, FSCPOS_REG_VOLT_12); - data->volt[1] = fscpos_read_value(client, FSCPOS_REG_VOLT_5); - data->volt[2] = fscpos_read_value(client, FSCPOS_REG_VOLT_BATT); - - data->wdog_preset = fscpos_read_value(client, - FSCPOS_REG_WDOG_PRESET); - data->wdog_state = fscpos_read_value(client, - FSCPOS_REG_WDOG_STATE); - data->wdog_control = fscpos_read_value(client, - FSCPOS_REG_WDOG_CONTROL); - - data->global_event = fscpos_read_value(client, - FSCPOS_REG_EVENT_STATE); - - data->last_updated = jiffies; - data->valid = 1; - } - up(&data->update_lock); - return data; -} - -static int __init sm_fscpos_init(void) -{ - return i2c_add_driver(&fscpos_driver); -} - -static void __exit sm_fscpos_exit(void) -{ - i2c_del_driver(&fscpos_driver); -} - -MODULE_AUTHOR("Stefan Ott based on work from Hermann Jung " - ", Frodo Looijaard " - " and Philip Edelbrock "); -MODULE_DESCRIPTION("fujitsu siemens poseidon chip driver"); -MODULE_LICENSE("GPL"); - -module_init(sm_fscpos_init); -module_exit(sm_fscpos_exit); diff --git a/drivers/i2c/chips/gl518sm.c b/drivers/i2c/chips/gl518sm.c deleted file mode 100644 index 6bedf729dcf5..000000000000 --- a/drivers/i2c/chips/gl518sm.c +++ /dev/null @@ -1,604 +0,0 @@ -/* - * gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware - * monitoring - * Copyright (C) 1998, 1999 Frodo Looijaard and - * Kyosti Malkki - * Copyright (C) 2004 Hong-Gunn Chew and - * Jean Delvare - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Ported to Linux 2.6 by Hong-Gunn Chew with the help of Jean Delvare - * and advice of Greg Kroah-Hartman. - * - * Notes about the port: - * Release 0x00 of the GL518SM chipset doesn't support reading of in0, - * in1 nor in2. The original driver had an ugly workaround to get them - * anyway (changing limits and watching alarms trigger and wear off). - * We did not keep that part of the original driver in the Linux 2.6 - * version, since it was making the driver significantly more complex - * with no real benefit. - * - * History: - * 2004-01-28 Original port. (Hong-Gunn Chew) - * 2004-01-31 Code review and approval. (Jean Delvare) - */ - -#include -#include -#include -#include -#include -#include - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_2(gl518sm_r00, gl518sm_r80); - -/* Many GL518 constants specified below */ - -/* The GL518 registers */ -#define GL518_REG_CHIP_ID 0x00 -#define GL518_REG_REVISION 0x01 -#define GL518_REG_VENDOR_ID 0x02 -#define GL518_REG_CONF 0x03 -#define GL518_REG_TEMP_IN 0x04 -#define GL518_REG_TEMP_MAX 0x05 -#define GL518_REG_TEMP_HYST 0x06 -#define GL518_REG_FAN_COUNT 0x07 -#define GL518_REG_FAN_LIMIT 0x08 -#define GL518_REG_VIN1_LIMIT 0x09 -#define GL518_REG_VIN2_LIMIT 0x0a -#define GL518_REG_VIN3_LIMIT 0x0b -#define GL518_REG_VDD_LIMIT 0x0c -#define GL518_REG_VIN3 0x0d -#define GL518_REG_MISC 0x0f -#define GL518_REG_ALARM 0x10 -#define GL518_REG_MASK 0x11 -#define GL518_REG_INT 0x12 -#define GL518_REG_VIN2 0x13 -#define GL518_REG_VIN1 0x14 -#define GL518_REG_VDD 0x15 - - -/* - * Conversions. Rounding and limit checking is only done on the TO_REG - * variants. Note that you should be a bit careful with which arguments - * these macros are called: arguments may be evaluated more than once. - * Fixing this is just not worth it. - */ - -#define RAW_FROM_REG(val) val - -#define BOOL_FROM_REG(val) ((val)?0:1) -#define BOOL_TO_REG(val) ((val)?0:1) - -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0? \ - (val)-500:(val)+500)/1000)+119),0,255)) -#define TEMP_FROM_REG(val) (((val) - 119) * 1000) - -static inline u8 FAN_TO_REG(long rpm, int div) -{ - long rpmdiv; - if (rpm == 0) - return 0; - rpmdiv = SENSORS_LIMIT(rpm, 1, 1920000) * div; - return SENSORS_LIMIT((960000 + rpmdiv / 2) / rpmdiv, 1, 255); -} -#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (960000/((val)*(div)))) - -#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255)) -#define IN_FROM_REG(val) ((val)*19) - -#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255)) -#define VDD_FROM_REG(val) (((val)*95+2)/4) - -#define DIV_TO_REG(val) ((val)==4?2:(val)==2?1:(val)==1?0:3) -#define DIV_FROM_REG(val) (1 << (val)) - -#define BEEP_MASK_TO_REG(val) ((val) & 0x7f & data->alarm_mask) -#define BEEP_MASK_FROM_REG(val) ((val) & 0x7f) - -/* Each client has this additional data */ -struct gl518_data { - struct i2c_client client; - enum chips type; - - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - u8 voltage_in[4]; /* Register values; [0] = VDD */ - u8 voltage_min[4]; /* Register values; [0] = VDD */ - u8 voltage_max[4]; /* Register values; [0] = VDD */ - u8 iter_voltage_in[4]; /* Register values; [0] = VDD */ - u8 fan_in[2]; - u8 fan_min[2]; - u8 fan_div[2]; /* Register encoding, shifted right */ - u8 fan_auto1; /* Boolean */ - u8 temp_in; /* Register values */ - u8 temp_max; /* Register values */ - u8 temp_hyst; /* Register values */ - u8 alarms; /* Register value */ - u8 alarm_mask; /* Register value */ - u8 beep_mask; /* Register value */ - u8 beep_enable; /* Boolean */ -}; - -static int gl518_attach_adapter(struct i2c_adapter *adapter); -static int gl518_detect(struct i2c_adapter *adapter, int address, int kind); -static void gl518_init_client(struct i2c_client *client); -static int gl518_detach_client(struct i2c_client *client); -static int gl518_read_value(struct i2c_client *client, u8 reg); -static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value); -static struct gl518_data *gl518_update_device(struct device *dev); - -/* This is the driver that will be inserted */ -static struct i2c_driver gl518_driver = { - .owner = THIS_MODULE, - .name = "gl518sm", - .id = I2C_DRIVERID_GL518, - .flags = I2C_DF_NOTIFY, - .attach_adapter = gl518_attach_adapter, - .detach_client = gl518_detach_client, -}; - -/* - * Sysfs stuff - */ - -#define show(type, suffix, value) \ -static ssize_t show_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct gl518_data *data = gl518_update_device(dev); \ - return sprintf(buf, "%d\n", type##_FROM_REG(data->value)); \ -} - -#define show_fan(suffix, value, index) \ -static ssize_t show_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct gl518_data *data = gl518_update_device(dev); \ - return sprintf(buf, "%d\n", FAN_FROM_REG(data->value[index], \ - DIV_FROM_REG(data->fan_div[index]))); \ -} - -show(TEMP, temp_input1, temp_in); -show(TEMP, temp_max1, temp_max); -show(TEMP, temp_hyst1, temp_hyst); -show(BOOL, fan_auto1, fan_auto1); -show_fan(fan_input1, fan_in, 0); -show_fan(fan_input2, fan_in, 1); -show_fan(fan_min1, fan_min, 0); -show_fan(fan_min2, fan_min, 1); -show(DIV, fan_div1, fan_div[0]); -show(DIV, fan_div2, fan_div[1]); -show(VDD, in_input0, voltage_in[0]); -show(IN, in_input1, voltage_in[1]); -show(IN, in_input2, voltage_in[2]); -show(IN, in_input3, voltage_in[3]); -show(VDD, in_min0, voltage_min[0]); -show(IN, in_min1, voltage_min[1]); -show(IN, in_min2, voltage_min[2]); -show(IN, in_min3, voltage_min[3]); -show(VDD, in_max0, voltage_max[0]); -show(IN, in_max1, voltage_max[1]); -show(IN, in_max2, voltage_max[2]); -show(IN, in_max3, voltage_max[3]); -show(RAW, alarms, alarms); -show(BOOL, beep_enable, beep_enable); -show(BEEP_MASK, beep_mask, beep_mask); - -#define set(type, suffix, value, reg) \ -static ssize_t set_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct gl518_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->value = type##_TO_REG(val); \ - gl518_write_value(client, reg, data->value); \ - up(&data->update_lock); \ - return count; \ -} - -#define set_bits(type, suffix, value, reg, mask, shift) \ -static ssize_t set_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct gl518_data *data = i2c_get_clientdata(client); \ - int regvalue; \ - unsigned long val = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - regvalue = gl518_read_value(client, reg); \ - data->value = type##_TO_REG(val); \ - regvalue = (regvalue & ~mask) | (data->value << shift); \ - gl518_write_value(client, reg, regvalue); \ - up(&data->update_lock); \ - return count; \ -} - -#define set_low(type, suffix, value, reg) \ - set_bits(type, suffix, value, reg, 0x00ff, 0) -#define set_high(type, suffix, value, reg) \ - set_bits(type, suffix, value, reg, 0xff00, 8) - -set(TEMP, temp_max1, temp_max, GL518_REG_TEMP_MAX); -set(TEMP, temp_hyst1, temp_hyst, GL518_REG_TEMP_HYST); -set_bits(BOOL, fan_auto1, fan_auto1, GL518_REG_MISC, 0x08, 3); -set_bits(DIV, fan_div1, fan_div[0], GL518_REG_MISC, 0xc0, 6); -set_bits(DIV, fan_div2, fan_div[1], GL518_REG_MISC, 0x30, 4); -set_low(VDD, in_min0, voltage_min[0], GL518_REG_VDD_LIMIT); -set_low(IN, in_min1, voltage_min[1], GL518_REG_VIN1_LIMIT); -set_low(IN, in_min2, voltage_min[2], GL518_REG_VIN2_LIMIT); -set_low(IN, in_min3, voltage_min[3], GL518_REG_VIN3_LIMIT); -set_high(VDD, in_max0, voltage_max[0], GL518_REG_VDD_LIMIT); -set_high(IN, in_max1, voltage_max[1], GL518_REG_VIN1_LIMIT); -set_high(IN, in_max2, voltage_max[2], GL518_REG_VIN2_LIMIT); -set_high(IN, in_max3, voltage_max[3], GL518_REG_VIN3_LIMIT); -set_bits(BOOL, beep_enable, beep_enable, GL518_REG_CONF, 0x04, 2); -set(BEEP_MASK, beep_mask, beep_mask, GL518_REG_ALARM); - -static ssize_t set_fan_min1(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct gl518_data *data = i2c_get_clientdata(client); - int regvalue; - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT); - data->fan_min[0] = FAN_TO_REG(val, - DIV_FROM_REG(data->fan_div[0])); - regvalue = (regvalue & 0x00ff) | (data->fan_min[0] << 8); - gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue); - - data->beep_mask = gl518_read_value(client, GL518_REG_ALARM); - if (data->fan_min[0] == 0) - data->alarm_mask &= ~0x20; - else - data->alarm_mask |= 0x20; - data->beep_mask &= data->alarm_mask; - gl518_write_value(client, GL518_REG_ALARM, data->beep_mask); - - up(&data->update_lock); - return count; -} - -static ssize_t set_fan_min2(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct gl518_data *data = i2c_get_clientdata(client); - int regvalue; - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT); - data->fan_min[1] = FAN_TO_REG(val, - DIV_FROM_REG(data->fan_div[1])); - regvalue = (regvalue & 0xff00) | data->fan_min[1]; - gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue); - - data->beep_mask = gl518_read_value(client, GL518_REG_ALARM); - if (data->fan_min[1] == 0) - data->alarm_mask &= ~0x40; - else - data->alarm_mask |= 0x40; - data->beep_mask &= data->alarm_mask; - gl518_write_value(client, GL518_REG_ALARM, data->beep_mask); - - up(&data->update_lock); - return count; -} - -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); -static DEVICE_ATTR(temp1_max, S_IWUSR|S_IRUGO, show_temp_max1, set_temp_max1); -static DEVICE_ATTR(temp1_max_hyst, S_IWUSR|S_IRUGO, - show_temp_hyst1, set_temp_hyst1); -static DEVICE_ATTR(fan1_auto, S_IWUSR|S_IRUGO, show_fan_auto1, set_fan_auto1); -static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input1, NULL); -static DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input2, NULL); -static DEVICE_ATTR(fan1_min, S_IWUSR|S_IRUGO, show_fan_min1, set_fan_min1); -static DEVICE_ATTR(fan2_min, S_IWUSR|S_IRUGO, show_fan_min2, set_fan_min2); -static DEVICE_ATTR(fan1_div, S_IWUSR|S_IRUGO, show_fan_div1, set_fan_div1); -static DEVICE_ATTR(fan2_div, S_IWUSR|S_IRUGO, show_fan_div2, set_fan_div2); -static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input0, NULL); -static DEVICE_ATTR(in1_input, S_IRUGO, show_in_input1, NULL); -static DEVICE_ATTR(in2_input, S_IRUGO, show_in_input2, NULL); -static DEVICE_ATTR(in3_input, S_IRUGO, show_in_input3, NULL); -static DEVICE_ATTR(in0_min, S_IWUSR|S_IRUGO, show_in_min0, set_in_min0); -static DEVICE_ATTR(in1_min, S_IWUSR|S_IRUGO, show_in_min1, set_in_min1); -static DEVICE_ATTR(in2_min, S_IWUSR|S_IRUGO, show_in_min2, set_in_min2); -static DEVICE_ATTR(in3_min, S_IWUSR|S_IRUGO, show_in_min3, set_in_min3); -static DEVICE_ATTR(in0_max, S_IWUSR|S_IRUGO, show_in_max0, set_in_max0); -static DEVICE_ATTR(in1_max, S_IWUSR|S_IRUGO, show_in_max1, set_in_max1); -static DEVICE_ATTR(in2_max, S_IWUSR|S_IRUGO, show_in_max2, set_in_max2); -static DEVICE_ATTR(in3_max, S_IWUSR|S_IRUGO, show_in_max3, set_in_max3); -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); -static DEVICE_ATTR(beep_enable, S_IWUSR|S_IRUGO, - show_beep_enable, set_beep_enable); -static DEVICE_ATTR(beep_mask, S_IWUSR|S_IRUGO, - show_beep_mask, set_beep_mask); - -/* - * Real code - */ - -static int gl518_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, gl518_detect); -} - -static int gl518_detect(struct i2c_adapter *adapter, int address, int kind) -{ - int i; - struct i2c_client *new_client; - struct gl518_data *data; - int err = 0; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WORD_DATA)) - goto exit; - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access gl518_{read,write}_value. */ - - if (!(data = kmalloc(sizeof(struct gl518_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct gl518_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &gl518_driver; - new_client->flags = 0; - - /* Now, we do the remaining detection. */ - - if (kind < 0) { - if ((gl518_read_value(new_client, GL518_REG_CHIP_ID) != 0x80) - || (gl518_read_value(new_client, GL518_REG_CONF) & 0x80)) - goto exit_free; - } - - /* Determine the chip type. */ - if (kind <= 0) { - i = gl518_read_value(new_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), address); - goto exit_free; - } - } - - /* Fill in the remaining client fields */ - strlcpy(new_client->name, "gl518sm", I2C_NAME_SIZE); - data->type = kind; - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the GL518SM chip */ - data->alarm_mask = 0xff; - data->voltage_in[0]=data->voltage_in[1]=data->voltage_in[2]=0; - gl518_init_client((struct i2c_client *) new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_fan1_auto); - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_beep_enable); - device_create_file(&new_client->dev, &dev_attr_beep_mask); - - return 0; - -/* OK, this is not exactly good programming practice, usually. But it is - very code-efficient in this case. */ - -exit_free: - kfree(data); -exit: - return err; -} - - -/* Called when we have found a new GL518SM. - Note that we preserve D4:NoFan2 and D2:beep_enable. */ -static void gl518_init_client(struct i2c_client *client) -{ - /* Make sure we leave D7:Reset untouched */ - u8 regvalue = gl518_read_value(client, GL518_REG_CONF) & 0x7f; - - /* Comparator mode (D3=0), standby mode (D6=0) */ - gl518_write_value(client, GL518_REG_CONF, (regvalue &= 0x37)); - - /* Never interrupts */ - gl518_write_value(client, GL518_REG_MASK, 0x00); - - /* Clear status register (D5=1), start (D6=1) */ - gl518_write_value(client, GL518_REG_CONF, 0x20 | regvalue); - gl518_write_value(client, GL518_REG_CONF, 0x40 | regvalue); -} - -static int gl518_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - - return 0; -} - -/* Registers 0x07 to 0x0c are word-sized, others are byte-sized - GL518 uses a high-byte first convention, which is exactly opposite to - the usual practice. */ -static int gl518_read_value(struct i2c_client *client, u8 reg) -{ - if ((reg >= 0x07) && (reg <= 0x0c)) - return swab16(i2c_smbus_read_word_data(client, reg)); - else - return i2c_smbus_read_byte_data(client, reg); -} - -/* Registers 0x07 to 0x0c are word-sized, others are byte-sized - GL518 uses a high-byte first convention, which is exactly opposite to - the usual practice. */ -static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value) -{ - if ((reg >= 0x07) && (reg <= 0x0c)) - return i2c_smbus_write_word_data(client, reg, swab16(value)); - else - return i2c_smbus_write_byte_data(client, reg, value); -} - -static struct gl518_data *gl518_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct gl518_data *data = i2c_get_clientdata(client); - int val; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - dev_dbg(&client->dev, "Starting gl518 update\n"); - - data->alarms = gl518_read_value(client, GL518_REG_INT); - data->beep_mask = gl518_read_value(client, GL518_REG_ALARM); - - val = gl518_read_value(client, GL518_REG_VDD_LIMIT); - data->voltage_min[0] = val & 0xff; - data->voltage_max[0] = (val >> 8) & 0xff; - val = gl518_read_value(client, GL518_REG_VIN1_LIMIT); - data->voltage_min[1] = val & 0xff; - data->voltage_max[1] = (val >> 8) & 0xff; - val = gl518_read_value(client, GL518_REG_VIN2_LIMIT); - data->voltage_min[2] = val & 0xff; - data->voltage_max[2] = (val >> 8) & 0xff; - val = gl518_read_value(client, GL518_REG_VIN3_LIMIT); - data->voltage_min[3] = val & 0xff; - data->voltage_max[3] = (val >> 8) & 0xff; - - val = gl518_read_value(client, GL518_REG_FAN_COUNT); - data->fan_in[0] = (val >> 8) & 0xff; - data->fan_in[1] = val & 0xff; - - val = gl518_read_value(client, GL518_REG_FAN_LIMIT); - data->fan_min[0] = (val >> 8) & 0xff; - data->fan_min[1] = val & 0xff; - - data->temp_in = gl518_read_value(client, GL518_REG_TEMP_IN); - data->temp_max = - gl518_read_value(client, GL518_REG_TEMP_MAX); - data->temp_hyst = - gl518_read_value(client, GL518_REG_TEMP_HYST); - - val = gl518_read_value(client, GL518_REG_MISC); - data->fan_div[0] = (val >> 6) & 0x03; - data->fan_div[1] = (val >> 4) & 0x03; - data->fan_auto1 = (val >> 3) & 0x01; - - data->alarms &= data->alarm_mask; - - val = gl518_read_value(client, GL518_REG_CONF); - data->beep_enable = (val >> 2) & 1; - - if (data->type != gl518sm_r00) { - data->voltage_in[0] = - gl518_read_value(client, GL518_REG_VDD); - data->voltage_in[1] = - gl518_read_value(client, GL518_REG_VIN1); - data->voltage_in[2] = - gl518_read_value(client, GL518_REG_VIN2); - } - data->voltage_in[3] = - gl518_read_value(client, GL518_REG_VIN3); - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_gl518sm_init(void) -{ - return i2c_add_driver(&gl518_driver); -} - -static void __exit sensors_gl518sm_exit(void) -{ - i2c_del_driver(&gl518_driver); -} - -MODULE_AUTHOR("Frodo Looijaard , " - "Kyosti Malkki and " - "Hong-Gunn Chew "); -MODULE_DESCRIPTION("GL518SM driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_gl518sm_init); -module_exit(sensors_gl518sm_exit); diff --git a/drivers/i2c/chips/gl520sm.c b/drivers/i2c/chips/gl520sm.c deleted file mode 100644 index a13a504f5bfa..000000000000 --- a/drivers/i2c/chips/gl520sm.c +++ /dev/null @@ -1,769 +0,0 @@ -/* - gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998, 1999 Frodo Looijaard , - Kyösti Mälkki - Copyright (c) 2005 Maarten Deprez - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - -*/ - -#include -#include -#include -#include -#include -#include - -/* Type of the extra sensor */ -static unsigned short extra_sensor_type; -module_param(extra_sensor_type, ushort, 0); -MODULE_PARM_DESC(extra_sensor_type, "Type of extra sensor (0=autodetect, 1=temperature, 2=voltage)"); - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_1(gl520sm); - -/* Many GL520 constants specified below -One of the inputs can be configured as either temp or voltage. -That's why _TEMP2 and _IN4 access the same register -*/ - -/* The GL520 registers */ -#define GL520_REG_CHIP_ID 0x00 -#define GL520_REG_REVISION 0x01 -#define GL520_REG_CONF 0x03 -#define GL520_REG_MASK 0x11 - -#define GL520_REG_VID_INPUT 0x02 - -#define GL520_REG_IN0_INPUT 0x15 -#define GL520_REG_IN0_LIMIT 0x0c -#define GL520_REG_IN0_MIN GL520_REG_IN0_LIMIT -#define GL520_REG_IN0_MAX GL520_REG_IN0_LIMIT - -#define GL520_REG_IN1_INPUT 0x14 -#define GL520_REG_IN1_LIMIT 0x09 -#define GL520_REG_IN1_MIN GL520_REG_IN1_LIMIT -#define GL520_REG_IN1_MAX GL520_REG_IN1_LIMIT - -#define GL520_REG_IN2_INPUT 0x13 -#define GL520_REG_IN2_LIMIT 0x0a -#define GL520_REG_IN2_MIN GL520_REG_IN2_LIMIT -#define GL520_REG_IN2_MAX GL520_REG_IN2_LIMIT - -#define GL520_REG_IN3_INPUT 0x0d -#define GL520_REG_IN3_LIMIT 0x0b -#define GL520_REG_IN3_MIN GL520_REG_IN3_LIMIT -#define GL520_REG_IN3_MAX GL520_REG_IN3_LIMIT - -#define GL520_REG_IN4_INPUT 0x0e -#define GL520_REG_IN4_MAX 0x17 -#define GL520_REG_IN4_MIN 0x18 - -#define GL520_REG_TEMP1_INPUT 0x04 -#define GL520_REG_TEMP1_MAX 0x05 -#define GL520_REG_TEMP1_MAX_HYST 0x06 - -#define GL520_REG_TEMP2_INPUT 0x0e -#define GL520_REG_TEMP2_MAX 0x17 -#define GL520_REG_TEMP2_MAX_HYST 0x18 - -#define GL520_REG_FAN_INPUT 0x07 -#define GL520_REG_FAN_MIN 0x08 -#define GL520_REG_FAN_DIV 0x0f -#define GL520_REG_FAN_OFF GL520_REG_FAN_DIV - -#define GL520_REG_ALARMS 0x12 -#define GL520_REG_BEEP_MASK 0x10 -#define GL520_REG_BEEP_ENABLE GL520_REG_CONF - -/* - * Function declarations - */ - -static int gl520_attach_adapter(struct i2c_adapter *adapter); -static int gl520_detect(struct i2c_adapter *adapter, int address, int kind); -static void gl520_init_client(struct i2c_client *client); -static int gl520_detach_client(struct i2c_client *client); -static int gl520_read_value(struct i2c_client *client, u8 reg); -static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value); -static struct gl520_data *gl520_update_device(struct device *dev); - -/* Driver data */ -static struct i2c_driver gl520_driver = { - .owner = THIS_MODULE, - .name = "gl520sm", - .id = I2C_DRIVERID_GL520, - .flags = I2C_DF_NOTIFY, - .attach_adapter = gl520_attach_adapter, - .detach_client = gl520_detach_client, -}; - -/* Client data */ -struct gl520_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* zero until the following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - u8 vid; - u8 vrm; - u8 in_input[5]; /* [0] = VVD */ - u8 in_min[5]; /* [0] = VDD */ - u8 in_max[5]; /* [0] = VDD */ - u8 fan_input[2]; - u8 fan_min[2]; - u8 fan_div[2]; - u8 fan_off; - u8 temp_input[2]; - u8 temp_max[2]; - u8 temp_max_hyst[2]; - u8 alarms; - u8 beep_enable; - u8 beep_mask; - u8 alarm_mask; - u8 two_temps; -}; - -/* - * Sysfs stuff - */ - -#define sysfs_r(type, n, item, reg) \ -static ssize_t get_##type##item (struct gl520_data *, char *, int); \ -static ssize_t get_##type##n##item (struct device *, struct device_attribute *attr, char *); \ -static ssize_t get_##type##n##item (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct gl520_data *data = gl520_update_device(dev); \ - return get_##type##item(data, buf, (n)); \ -} - -#define sysfs_w(type, n, item, reg) \ -static ssize_t set_##type##item (struct i2c_client *, struct gl520_data *, const char *, size_t, int, int); \ -static ssize_t set_##type##n##item (struct device *, struct device_attribute *attr, const char *, size_t); \ -static ssize_t set_##type##n##item (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct gl520_data *data = i2c_get_clientdata(client); \ - return set_##type##item(client, data, buf, count, (n), reg); \ -} - -#define sysfs_rw_n(type, n, item, reg) \ -sysfs_r(type, n, item, reg) \ -sysfs_w(type, n, item, reg) \ -static DEVICE_ATTR(type##n##item, S_IRUGO | S_IWUSR, get_##type##n##item, set_##type##n##item); - -#define sysfs_ro_n(type, n, item, reg) \ -sysfs_r(type, n, item, reg) \ -static DEVICE_ATTR(type##n##item, S_IRUGO, get_##type##n##item, NULL); - -#define sysfs_rw(type, item, reg) \ -sysfs_r(type, 0, item, reg) \ -sysfs_w(type, 0, item, reg) \ -static DEVICE_ATTR(type##item, S_IRUGO | S_IWUSR, get_##type##0##item, set_##type##0##item); - -#define sysfs_ro(type, item, reg) \ -sysfs_r(type, 0, item, reg) \ -static DEVICE_ATTR(type##item, S_IRUGO, get_##type##0##item, NULL); - - -#define sysfs_vid(n) \ -sysfs_ro_n(cpu, n, _vid, GL520_REG_VID_INPUT) - -#define device_create_file_vid(client, n) \ -device_create_file(&client->dev, &dev_attr_cpu##n##_vid) - -#define sysfs_in(n) \ -sysfs_ro_n(in, n, _input, GL520_REG_IN##n##INPUT) \ -sysfs_rw_n(in, n, _min, GL520_REG_IN##n##_MIN) \ -sysfs_rw_n(in, n, _max, GL520_REG_IN##n##_MAX) \ - -#define device_create_file_in(client, n) \ -({device_create_file(&client->dev, &dev_attr_in##n##_input); \ -device_create_file(&client->dev, &dev_attr_in##n##_min); \ -device_create_file(&client->dev, &dev_attr_in##n##_max);}) - -#define sysfs_fan(n) \ -sysfs_ro_n(fan, n, _input, GL520_REG_FAN_INPUT) \ -sysfs_rw_n(fan, n, _min, GL520_REG_FAN_MIN) \ -sysfs_rw_n(fan, n, _div, GL520_REG_FAN_DIV) - -#define device_create_file_fan(client, n) \ -({device_create_file(&client->dev, &dev_attr_fan##n##_input); \ -device_create_file(&client->dev, &dev_attr_fan##n##_min); \ -device_create_file(&client->dev, &dev_attr_fan##n##_div);}) - -#define sysfs_fan_off(n) \ -sysfs_rw_n(fan, n, _off, GL520_REG_FAN_OFF) \ - -#define device_create_file_fan_off(client, n) \ -device_create_file(&client->dev, &dev_attr_fan##n##_off) - -#define sysfs_temp(n) \ -sysfs_ro_n(temp, n, _input, GL520_REG_TEMP##n##_INPUT) \ -sysfs_rw_n(temp, n, _max, GL520_REG_TEMP##n##_MAX) \ -sysfs_rw_n(temp, n, _max_hyst, GL520_REG_TEMP##n##_MAX_HYST) - -#define device_create_file_temp(client, n) \ -({device_create_file(&client->dev, &dev_attr_temp##n##_input); \ -device_create_file(&client->dev, &dev_attr_temp##n##_max); \ -device_create_file(&client->dev, &dev_attr_temp##n##_max_hyst);}) - -#define sysfs_alarms() \ -sysfs_ro(alarms, , GL520_REG_ALARMS) \ -sysfs_rw(beep_enable, , GL520_REG_BEEP_ENABLE) \ -sysfs_rw(beep_mask, , GL520_REG_BEEP_MASK) - -#define device_create_file_alarms(client) \ -({device_create_file(&client->dev, &dev_attr_alarms); \ -device_create_file(&client->dev, &dev_attr_beep_enable); \ -device_create_file(&client->dev, &dev_attr_beep_mask);}) - - -sysfs_vid(0) - -sysfs_in(0) -sysfs_in(1) -sysfs_in(2) -sysfs_in(3) -sysfs_in(4) - -sysfs_fan(1) -sysfs_fan(2) -sysfs_fan_off(1) - -sysfs_temp(1) -sysfs_temp(2) - -sysfs_alarms() - - -static ssize_t get_cpu_vid(struct gl520_data *data, char *buf, int n) -{ - return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm)); -} - -#define VDD_FROM_REG(val) (((val)*95+2)/4) -#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255)) - -#define IN_FROM_REG(val) ((val)*19) -#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255)) - -static ssize_t get_in_input(struct gl520_data *data, char *buf, int n) -{ - u8 r = data->in_input[n]; - - if (n == 0) - return sprintf(buf, "%d\n", VDD_FROM_REG(r)); - else - return sprintf(buf, "%d\n", IN_FROM_REG(r)); -} - -static ssize_t get_in_min(struct gl520_data *data, char *buf, int n) -{ - u8 r = data->in_min[n]; - - if (n == 0) - return sprintf(buf, "%d\n", VDD_FROM_REG(r)); - else - return sprintf(buf, "%d\n", IN_FROM_REG(r)); -} - -static ssize_t get_in_max(struct gl520_data *data, char *buf, int n) -{ - u8 r = data->in_max[n]; - - if (n == 0) - return sprintf(buf, "%d\n", VDD_FROM_REG(r)); - else - return sprintf(buf, "%d\n", IN_FROM_REG(r)); -} - -static ssize_t set_in_min(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) -{ - long v = simple_strtol(buf, NULL, 10); - u8 r; - - down(&data->update_lock); - - if (n == 0) - r = VDD_TO_REG(v); - else - r = IN_TO_REG(v); - - data->in_min[n] = r; - - if (n < 4) - gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff) | r); - else - gl520_write_value(client, reg, r); - - up(&data->update_lock); - return count; -} - -static ssize_t set_in_max(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) -{ - long v = simple_strtol(buf, NULL, 10); - u8 r; - - if (n == 0) - r = VDD_TO_REG(v); - else - r = IN_TO_REG(v); - - down(&data->update_lock); - - data->in_max[n] = r; - - if (n < 4) - gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff00) | (r << 8)); - else - gl520_write_value(client, reg, r); - - up(&data->update_lock); - return count; -} - -#define DIV_FROM_REG(val) (1 << (val)) -#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (480000/((val) << (div)))) -#define FAN_TO_REG(val,div) ((val)<=0?0:SENSORS_LIMIT((480000 + ((val) << ((div)-1))) / ((val) << (div)), 1, 255)); - -static ssize_t get_fan_input(struct gl520_data *data, char *buf, int n) -{ - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_input[n - 1], data->fan_div[n - 1])); -} - -static ssize_t get_fan_min(struct gl520_data *data, char *buf, int n) -{ - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[n - 1], data->fan_div[n - 1])); -} - -static ssize_t get_fan_div(struct gl520_data *data, char *buf, int n) -{ - return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[n - 1])); -} - -static ssize_t get_fan_off(struct gl520_data *data, char *buf, int n) -{ - return sprintf(buf, "%d\n", data->fan_off); -} - -static ssize_t set_fan_min(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10); - u8 r; - - down(&data->update_lock); - r = FAN_TO_REG(v, data->fan_div[n - 1]); - data->fan_min[n - 1] = r; - - if (n == 1) - gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff00) | (r << 8)); - else - gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xff) | r); - - data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK); - if (data->fan_min[n - 1] == 0) - data->alarm_mask &= (n == 1) ? ~0x20 : ~0x40; - else - data->alarm_mask |= (n == 1) ? 0x20 : 0x40; - data->beep_mask &= data->alarm_mask; - gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask); - - up(&data->update_lock); - return count; -} - -static ssize_t set_fan_div(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) -{ - unsigned long v = simple_strtoul(buf, NULL, 10); - u8 r; - - switch (v) { - case 1: r = 0; break; - case 2: r = 1; break; - case 4: r = 2; break; - case 8: r = 3; break; - default: - dev_err(&client->dev, "fan_div value %ld not supported. Choose one of 1, 2, 4 or 8!\n", v); - return -EINVAL; - } - - down(&data->update_lock); - data->fan_div[n - 1] = r; - - if (n == 1) - gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0xc0) | (r << 6)); - else - gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x30) | (r << 4)); - - up(&data->update_lock); - return count; -} - -static ssize_t set_fan_off(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) -{ - u8 r = simple_strtoul(buf, NULL, 10)?1:0; - - down(&data->update_lock); - data->fan_off = r; - gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x0c) | (r << 2)); - up(&data->update_lock); - return count; -} - -#define TEMP_FROM_REG(val) (((val) - 130) * 1000) -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-500:(val)+500) / 1000)+130),0,255)) - -static ssize_t get_temp_input(struct gl520_data *data, char *buf, int n) -{ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_input[n - 1])); -} - -static ssize_t get_temp_max(struct gl520_data *data, char *buf, int n) -{ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[n - 1])); -} - -static ssize_t get_temp_max_hyst(struct gl520_data *data, char *buf, int n) -{ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max_hyst[n - 1])); -} - -static ssize_t set_temp_max(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) -{ - long v = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_max[n - 1] = TEMP_TO_REG(v);; - gl520_write_value(client, reg, data->temp_max[n - 1]); - up(&data->update_lock); - return count; -} - -static ssize_t set_temp_max_hyst(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) -{ - long v = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_max_hyst[n - 1] = TEMP_TO_REG(v); - gl520_write_value(client, reg, data->temp_max_hyst[n - 1]); - up(&data->update_lock); - return count; -} - -static ssize_t get_alarms(struct gl520_data *data, char *buf, int n) -{ - return sprintf(buf, "%d\n", data->alarms); -} - -static ssize_t get_beep_enable(struct gl520_data *data, char *buf, int n) -{ - return sprintf(buf, "%d\n", data->beep_enable); -} - -static ssize_t get_beep_mask(struct gl520_data *data, char *buf, int n) -{ - return sprintf(buf, "%d\n", data->beep_mask); -} - -static ssize_t set_beep_enable(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) -{ - u8 r = simple_strtoul(buf, NULL, 10)?0:1; - - down(&data->update_lock); - data->beep_enable = !r; - gl520_write_value(client, reg, (gl520_read_value(client, reg) & ~0x04) | (r << 2)); - up(&data->update_lock); - return count; -} - -static ssize_t set_beep_mask(struct i2c_client *client, struct gl520_data *data, const char *buf, size_t count, int n, int reg) -{ - u8 r = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - r &= data->alarm_mask; - data->beep_mask = r; - gl520_write_value(client, reg, r); - up(&data->update_lock); - return count; -} - - -/* - * Real code - */ - -static int gl520_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, gl520_detect); -} - -static int gl520_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct gl520_data *data; - int err = 0; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WORD_DATA)) - goto exit; - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access gl520_{read,write}_value. */ - - if (!(data = kmalloc(sizeof(struct gl520_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct gl520_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &gl520_driver; - new_client->flags = 0; - - /* Determine the chip type. */ - if (kind < 0) { - if ((gl520_read_value(new_client, GL520_REG_CHIP_ID) != 0x20) || - ((gl520_read_value(new_client, GL520_REG_REVISION) & 0x7f) != 0x00) || - ((gl520_read_value(new_client, GL520_REG_CONF) & 0x80) != 0x00)) { - dev_dbg(&new_client->dev, "Unknown chip type, skipping\n"); - goto exit_free; - } - } - - /* Fill in the remaining client fields */ - strlcpy(new_client->name, "gl520sm", I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the GL520SM chip */ - gl520_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file_vid(new_client, 0); - - device_create_file_in(new_client, 0); - device_create_file_in(new_client, 1); - device_create_file_in(new_client, 2); - device_create_file_in(new_client, 3); - if (!data->two_temps) - device_create_file_in(new_client, 4); - - device_create_file_fan(new_client, 1); - device_create_file_fan(new_client, 2); - device_create_file_fan_off(new_client, 1); - - device_create_file_temp(new_client, 1); - if (data->two_temps) - device_create_file_temp(new_client, 2); - - device_create_file_alarms(new_client); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - - -/* Called when we have found a new GL520SM. */ -static void gl520_init_client(struct i2c_client *client) -{ - struct gl520_data *data = i2c_get_clientdata(client); - u8 oldconf, conf; - - conf = oldconf = gl520_read_value(client, GL520_REG_CONF); - - data->alarm_mask = 0xff; - data->vrm = i2c_which_vrm(); - - if (extra_sensor_type == 1) - conf &= ~0x10; - else if (extra_sensor_type == 2) - conf |= 0x10; - data->two_temps = !(conf & 0x10); - - /* If IRQ# is disabled, we can safely force comparator mode */ - if (!(conf & 0x20)) - conf &= 0xf7; - - /* Enable monitoring if needed */ - conf |= 0x40; - - if (conf != oldconf) - gl520_write_value(client, GL520_REG_CONF, conf); - - gl520_update_device(&(client->dev)); - - if (data->fan_min[0] == 0) - data->alarm_mask &= ~0x20; - if (data->fan_min[1] == 0) - data->alarm_mask &= ~0x40; - - data->beep_mask &= data->alarm_mask; - gl520_write_value(client, GL520_REG_BEEP_MASK, data->beep_mask); -} - -static int gl520_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - - -/* Registers 0x07 to 0x0c are word-sized, others are byte-sized - GL520 uses a high-byte first convention */ -static int gl520_read_value(struct i2c_client *client, u8 reg) -{ - if ((reg >= 0x07) && (reg <= 0x0c)) - return swab16(i2c_smbus_read_word_data(client, reg)); - else - return i2c_smbus_read_byte_data(client, reg); -} - -static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value) -{ - if ((reg >= 0x07) && (reg <= 0x0c)) - return i2c_smbus_write_word_data(client, reg, swab16(value)); - else - return i2c_smbus_write_byte_data(client, reg, value); -} - - -static struct gl520_data *gl520_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct gl520_data *data = i2c_get_clientdata(client); - int val; - - down(&data->update_lock); - - if ((jiffies - data->last_updated > 2 * HZ) || - (jiffies < data->last_updated) || !data->valid) { - - dev_dbg(&client->dev, "Starting gl520sm update\n"); - - data->alarms = gl520_read_value(client, GL520_REG_ALARMS); - data->beep_mask = gl520_read_value(client, GL520_REG_BEEP_MASK); - data->vid = gl520_read_value(client, GL520_REG_VID_INPUT) & 0x1f; - - val = gl520_read_value(client, GL520_REG_IN0_LIMIT); - data->in_min[0] = val & 0xff; - data->in_max[0] = (val >> 8) & 0xff; - val = gl520_read_value(client, GL520_REG_IN1_LIMIT); - data->in_min[1] = val & 0xff; - data->in_max[1] = (val >> 8) & 0xff; - val = gl520_read_value(client, GL520_REG_IN2_LIMIT); - data->in_min[2] = val & 0xff; - data->in_max[2] = (val >> 8) & 0xff; - val = gl520_read_value(client, GL520_REG_IN3_LIMIT); - data->in_min[3] = val & 0xff; - data->in_max[3] = (val >> 8) & 0xff; - - val = gl520_read_value(client, GL520_REG_FAN_INPUT); - data->fan_input[0] = (val >> 8) & 0xff; - data->fan_input[1] = val & 0xff; - - val = gl520_read_value(client, GL520_REG_FAN_MIN); - data->fan_min[0] = (val >> 8) & 0xff; - data->fan_min[1] = val & 0xff; - - data->temp_input[0] = gl520_read_value(client, GL520_REG_TEMP1_INPUT); - data->temp_max[0] = gl520_read_value(client, GL520_REG_TEMP1_MAX); - data->temp_max_hyst[0] = gl520_read_value(client, GL520_REG_TEMP1_MAX_HYST); - - val = gl520_read_value(client, GL520_REG_FAN_DIV); - data->fan_div[0] = (val >> 6) & 0x03; - data->fan_div[1] = (val >> 4) & 0x03; - data->fan_off = (val >> 2) & 0x01; - - data->alarms &= data->alarm_mask; - - val = gl520_read_value(client, GL520_REG_CONF); - data->beep_enable = !((val >> 2) & 1); - - data->in_input[0] = gl520_read_value(client, GL520_REG_IN0_INPUT); - data->in_input[1] = gl520_read_value(client, GL520_REG_IN1_INPUT); - data->in_input[2] = gl520_read_value(client, GL520_REG_IN2_INPUT); - data->in_input[3] = gl520_read_value(client, GL520_REG_IN3_INPUT); - - /* Temp1 and Vin4 are the same input */ - if (data->two_temps) { - data->temp_input[1] = gl520_read_value(client, GL520_REG_TEMP2_INPUT); - data->temp_max[1] = gl520_read_value(client, GL520_REG_TEMP2_MAX); - data->temp_max_hyst[1] = gl520_read_value(client, GL520_REG_TEMP2_MAX_HYST); - } else { - data->in_input[4] = gl520_read_value(client, GL520_REG_IN4_INPUT); - data->in_min[4] = gl520_read_value(client, GL520_REG_IN4_MIN); - data->in_max[4] = gl520_read_value(client, GL520_REG_IN4_MAX); - } - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - - -static int __init sensors_gl520sm_init(void) -{ - return i2c_add_driver(&gl520_driver); -} - -static void __exit sensors_gl520sm_exit(void) -{ - i2c_del_driver(&gl520_driver); -} - - -MODULE_AUTHOR("Frodo Looijaard , " - "Kyösti Mälkki , " - "Maarten Deprez "); -MODULE_DESCRIPTION("GL520SM driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_gl520sm_init); -module_exit(sensors_gl520sm_exit); diff --git a/drivers/i2c/chips/it87.c b/drivers/i2c/chips/it87.c deleted file mode 100644 index db20c9e47393..000000000000 --- a/drivers/i2c/chips/it87.c +++ /dev/null @@ -1,1184 +0,0 @@ -/* - it87.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring. - - Supports: IT8705F Super I/O chip w/LPC interface & SMBus - IT8712F Super I/O chip w/LPC interface & SMBus - Sis950 A clone of the IT8705F - - Copyright (C) 2001 Chris Gauthron - Largely inspired by lm78.c of the same package - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - djg@pdp8.net David Gesswein 7/18/01 - Modified to fix bug with not all alarms enabled. - Added ability to read battery voltage and select temperature sensor - type at module load time. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, - 0x2e, 0x2f, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_2(it87, it8712); - -#define REG 0x2e /* The register to read/write */ -#define DEV 0x07 /* Register: Logical device select */ -#define VAL 0x2f /* The value to read/write */ -#define PME 0x04 /* The device with the fan registers in it */ -#define DEVID 0x20 /* Register: Device ID */ -#define DEVREV 0x22 /* Register: Device Revision */ - -static inline int -superio_inb(int reg) -{ - outb(reg, REG); - return inb(VAL); -} - -static int superio_inw(int reg) -{ - int val; - outb(reg++, REG); - val = inb(VAL) << 8; - outb(reg, REG); - val |= inb(VAL); - return val; -} - -static inline void -superio_select(void) -{ - outb(DEV, REG); - outb(PME, VAL); -} - -static inline void -superio_enter(void) -{ - outb(0x87, REG); - outb(0x01, REG); - outb(0x55, REG); - outb(0x55, REG); -} - -static inline void -superio_exit(void) -{ - outb(0x02, REG); - outb(0x02, VAL); -} - -#define IT8712F_DEVID 0x8712 -#define IT8705F_DEVID 0x8705 -#define IT87_ACT_REG 0x30 -#define IT87_BASE_REG 0x60 - -/* Update battery voltage after every reading if true */ -static int update_vbat; - -/* Not all BIOSes properly configure the PWM registers */ -static int fix_pwm_polarity; - -/* Chip Type */ - -static u16 chip_type; - -/* Many IT87 constants specified below */ - -/* Length of ISA address segment */ -#define IT87_EXTENT 8 - -/* Where are the ISA address/data registers relative to the base address */ -#define IT87_ADDR_REG_OFFSET 5 -#define IT87_DATA_REG_OFFSET 6 - -/*----- The IT87 registers -----*/ - -#define IT87_REG_CONFIG 0x00 - -#define IT87_REG_ALARM1 0x01 -#define IT87_REG_ALARM2 0x02 -#define IT87_REG_ALARM3 0x03 - -#define IT87_REG_VID 0x0a -#define IT87_REG_FAN_DIV 0x0b - -/* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */ - -#define IT87_REG_FAN(nr) (0x0d + (nr)) -#define IT87_REG_FAN_MIN(nr) (0x10 + (nr)) -#define IT87_REG_FAN_MAIN_CTRL 0x13 -#define IT87_REG_FAN_CTL 0x14 -#define IT87_REG_PWM(nr) (0x15 + (nr)) - -#define IT87_REG_VIN(nr) (0x20 + (nr)) -#define IT87_REG_TEMP(nr) (0x29 + (nr)) - -#define IT87_REG_VIN_MAX(nr) (0x30 + (nr) * 2) -#define IT87_REG_VIN_MIN(nr) (0x31 + (nr) * 2) -#define IT87_REG_TEMP_HIGH(nr) (0x40 + (nr) * 2) -#define IT87_REG_TEMP_LOW(nr) (0x41 + (nr) * 2) - -#define IT87_REG_I2C_ADDR 0x48 - -#define IT87_REG_VIN_ENABLE 0x50 -#define IT87_REG_TEMP_ENABLE 0x51 - -#define IT87_REG_CHIPID 0x58 - -#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8)/16),0,255)) -#define IN_FROM_REG(val) ((val) * 16) - -static inline u8 FAN_TO_REG(long rpm, int div) -{ - if (rpm == 0) - return 255; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, - 254); -} - -#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) - -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-500)/1000):\ - ((val)+500)/1000),-128,127)) -#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*1000) - -#define PWM_TO_REG(val) ((val) >> 1) -#define PWM_FROM_REG(val) (((val)&0x7f) << 1) - -static int DIV_TO_REG(int val) -{ - int answer = 0; - while ((val >>= 1) != 0) - answer++; - return answer; -} -#define DIV_FROM_REG(val) (1 << (val)) - - -/* For each registered IT87, we need to keep some data in memory. That - data is pointed to by it87_list[NR]->data. The structure itself is - dynamically allocated, at the same time when a new it87 client is - allocated. */ -struct it87_data { - struct i2c_client client; - struct semaphore lock; - enum chips type; - - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - u8 in[9]; /* Register value */ - u8 in_max[9]; /* Register value */ - u8 in_min[9]; /* Register value */ - u8 fan[3]; /* Register value */ - u8 fan_min[3]; /* Register value */ - u8 temp[3]; /* Register value */ - u8 temp_high[3]; /* Register value */ - u8 temp_low[3]; /* Register value */ - u8 sensor; /* Register value */ - u8 fan_div[3]; /* Register encoding, shifted right */ - u8 vid; /* Register encoding, combined */ - int vrm; - u32 alarms; /* Register encoding, combined */ - u8 fan_main_ctrl; /* Register value */ - u8 manual_pwm_ctl[3]; /* manual PWM value set by user */ -}; - - -static int it87_attach_adapter(struct i2c_adapter *adapter); -static int it87_find(int *address); -static int it87_detect(struct i2c_adapter *adapter, int address, int kind); -static int it87_detach_client(struct i2c_client *client); - -static int it87_read_value(struct i2c_client *client, u8 register); -static int it87_write_value(struct i2c_client *client, u8 register, - u8 value); -static struct it87_data *it87_update_device(struct device *dev); -static int it87_check_pwm(struct i2c_client *client); -static void it87_init_client(struct i2c_client *client, struct it87_data *data); - - -static struct i2c_driver it87_driver = { - .owner = THIS_MODULE, - .name = "it87", - .id = I2C_DRIVERID_IT87, - .flags = I2C_DF_NOTIFY, - .attach_adapter = it87_attach_adapter, - .detach_client = it87_detach_client, -}; - -static ssize_t show_in(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); -} - -static ssize_t show_in_min(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); -} - -static ssize_t show_in_max(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); -} - -static ssize_t set_in_min(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct i2c_client *client = to_i2c_client(dev); - struct it87_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val); - it87_write_value(client, IT87_REG_VIN_MIN(nr), - data->in_min[nr]); - up(&data->update_lock); - return count; -} -static ssize_t set_in_max(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct i2c_client *client = to_i2c_client(dev); - struct it87_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val); - it87_write_value(client, IT87_REG_VIN_MAX(nr), - data->in_max[nr]); - up(&data->update_lock); - return count; -} - -#define show_in_offset(offset) \ -static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ - show_in, NULL, offset); - -#define limit_in_offset(offset) \ -static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ - show_in_min, set_in_min, offset); \ -static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ - show_in_max, set_in_max, offset); - -show_in_offset(0); -limit_in_offset(0); -show_in_offset(1); -limit_in_offset(1); -show_in_offset(2); -limit_in_offset(2); -show_in_offset(3); -limit_in_offset(3); -show_in_offset(4); -limit_in_offset(4); -show_in_offset(5); -limit_in_offset(5); -show_in_offset(6); -limit_in_offset(6); -show_in_offset(7); -limit_in_offset(7); -show_in_offset(8); - -/* 3 temperatures */ -static ssize_t show_temp(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[nr])); -} -static ssize_t show_temp_max(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[nr])); -} -static ssize_t show_temp_min(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[nr])); -} -static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct i2c_client *client = to_i2c_client(dev); - struct it87_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_high[nr] = TEMP_TO_REG(val); - it87_write_value(client, IT87_REG_TEMP_HIGH(nr), data->temp_high[nr]); - up(&data->update_lock); - return count; -} -static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct i2c_client *client = to_i2c_client(dev); - struct it87_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_low[nr] = TEMP_TO_REG(val); - it87_write_value(client, IT87_REG_TEMP_LOW(nr), data->temp_low[nr]); - up(&data->update_lock); - return count; -} -#define show_temp_offset(offset) \ -static SENSOR_DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ - show_temp, NULL, offset - 1); \ -static SENSOR_DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ - show_temp_max, set_temp_max, offset - 1); \ -static SENSOR_DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ - show_temp_min, set_temp_min, offset - 1); - -show_temp_offset(1); -show_temp_offset(2); -show_temp_offset(3); - -static ssize_t show_sensor(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct it87_data *data = it87_update_device(dev); - u8 reg = data->sensor; /* In case the value is updated while we use it */ - - if (reg & (1 << nr)) - return sprintf(buf, "3\n"); /* thermal diode */ - if (reg & (8 << nr)) - return sprintf(buf, "2\n"); /* thermistor */ - return sprintf(buf, "0\n"); /* disabled */ -} -static ssize_t set_sensor(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct i2c_client *client = to_i2c_client(dev); - struct it87_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - - data->sensor &= ~(1 << nr); - data->sensor &= ~(8 << nr); - /* 3 = thermal diode; 2 = thermistor; 0 = disabled */ - if (val == 3) - data->sensor |= 1 << nr; - else if (val == 2) - data->sensor |= 8 << nr; - else if (val != 0) { - up(&data->update_lock); - return -EINVAL; - } - it87_write_value(client, IT87_REG_TEMP_ENABLE, data->sensor); - up(&data->update_lock); - return count; -} -#define show_sensor_offset(offset) \ -static SENSOR_DEVICE_ATTR(temp##offset##_type, S_IRUGO | S_IWUSR, \ - show_sensor, set_sensor, offset - 1); - -show_sensor_offset(1); -show_sensor_offset(2); -show_sensor_offset(3); - -/* 3 Fans */ -static ssize_t show_fan(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct it87_data *data = it87_update_device(dev); - return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr], - DIV_FROM_REG(data->fan_div[nr]))); -} -static ssize_t show_fan_min(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct it87_data *data = it87_update_device(dev); - return sprintf(buf,"%d\n", - FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]))); -} -static ssize_t show_fan_div(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); -} -static ssize_t show_pwm_enable(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct it87_data *data = it87_update_device(dev); - return sprintf(buf,"%d\n", (data->fan_main_ctrl & (1 << nr)) ? 1 : 0); -} -static ssize_t show_pwm(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct it87_data *data = it87_update_device(dev); - return sprintf(buf,"%d\n", data->manual_pwm_ctl[nr]); -} -static ssize_t set_fan_min(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct i2c_client *client = to_i2c_client(dev); - struct it87_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); - it87_write_value(client, IT87_REG_FAN_MIN(nr), data->fan_min[nr]); - up(&data->update_lock); - return count; -} -static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct i2c_client *client = to_i2c_client(dev); - struct it87_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - int i, min[3]; - u8 old; - - down(&data->update_lock); - old = it87_read_value(client, IT87_REG_FAN_DIV); - - for (i = 0; i < 3; i++) - min[i] = FAN_FROM_REG(data->fan_min[i], DIV_FROM_REG(data->fan_div[i])); - - switch (nr) { - case 0: - case 1: - data->fan_div[nr] = DIV_TO_REG(val); - break; - case 2: - if (val < 8) - data->fan_div[nr] = 1; - else - data->fan_div[nr] = 3; - } - val = old & 0x80; - val |= (data->fan_div[0] & 0x07); - val |= (data->fan_div[1] & 0x07) << 3; - if (data->fan_div[2] == 3) - val |= 0x1 << 6; - it87_write_value(client, IT87_REG_FAN_DIV, val); - - for (i = 0; i < 3; i++) { - data->fan_min[i]=FAN_TO_REG(min[i], DIV_FROM_REG(data->fan_div[i])); - it87_write_value(client, IT87_REG_FAN_MIN(i), data->fan_min[i]); - } - up(&data->update_lock); - return count; -} -static ssize_t set_pwm_enable(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct i2c_client *client = to_i2c_client(dev); - struct it87_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - - if (val == 0) { - int tmp; - /* make sure the fan is on when in on/off mode */ - tmp = it87_read_value(client, IT87_REG_FAN_CTL); - it87_write_value(client, IT87_REG_FAN_CTL, tmp | (1 << nr)); - /* set on/off mode */ - data->fan_main_ctrl &= ~(1 << nr); - it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); - } else if (val == 1) { - /* set SmartGuardian mode */ - data->fan_main_ctrl |= (1 << nr); - it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); - /* set saved pwm value, clear FAN_CTLX PWM mode bit */ - it87_write_value(client, IT87_REG_PWM(nr), PWM_TO_REG(data->manual_pwm_ctl[nr])); - } else { - up(&data->update_lock); - return -EINVAL; - } - - up(&data->update_lock); - return count; -} -static ssize_t set_pwm(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); - int nr = sensor_attr->index; - - struct i2c_client *client = to_i2c_client(dev); - struct it87_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - if (val < 0 || val > 255) - return -EINVAL; - - down(&data->update_lock); - data->manual_pwm_ctl[nr] = val; - if (data->fan_main_ctrl & (1 << nr)) - it87_write_value(client, IT87_REG_PWM(nr), PWM_TO_REG(data->manual_pwm_ctl[nr])); - up(&data->update_lock); - return count; -} - -#define show_fan_offset(offset) \ -static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ - show_fan, NULL, offset - 1); \ -static SENSOR_DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan_min, set_fan_min, offset - 1); \ -static SENSOR_DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ - show_fan_div, set_fan_div, offset - 1); - -show_fan_offset(1); -show_fan_offset(2); -show_fan_offset(3); - -#define show_pwm_offset(offset) \ -static SENSOR_DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ - show_pwm_enable, set_pwm_enable, offset - 1); \ -static SENSOR_DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ - show_pwm, set_pwm, offset - 1); - -show_pwm_offset(1); -show_pwm_offset(2); -show_pwm_offset(3); - -/* Alarms */ -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%u\n", data->alarms); -} -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -static ssize_t -show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->vrm); -} -static ssize_t -store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct it87_data *data = i2c_get_clientdata(client); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - data->vrm = val; - - return count; -} -static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); -#define device_create_file_vrm(client) \ -device_create_file(&client->dev, &dev_attr_vrm) - -static ssize_t -show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct it87_data *data = it87_update_device(dev); - return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); -} -static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); -#define device_create_file_vid(client) \ -device_create_file(&client->dev, &dev_attr_cpu0_vid) - -/* This function is called when: - * it87_driver is inserted (when this module is loaded), for each - available adapter - * when a new adapter is inserted (and it87_driver is still present) */ -static int it87_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, it87_detect); -} - -/* SuperIO detection - will change normal_isa[0] if a chip is found */ -static int it87_find(int *address) -{ - int err = -ENODEV; - - superio_enter(); - chip_type = superio_inw(DEVID); - if (chip_type != IT8712F_DEVID - && chip_type != IT8705F_DEVID) - goto exit; - - superio_select(); - if (!(superio_inb(IT87_ACT_REG) & 0x01)) { - pr_info("it87: Device not activated, skipping\n"); - goto exit; - } - - *address = superio_inw(IT87_BASE_REG) & ~(IT87_EXTENT - 1); - if (*address == 0) { - pr_info("it87: Base address not set, skipping\n"); - goto exit; - } - - err = 0; - pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n", - chip_type, *address, superio_inb(DEVREV) & 0x0f); - -exit: - superio_exit(); - return err; -} - -/* This function is called by i2c_detect */ -int it87_detect(struct i2c_adapter *adapter, int address, int kind) -{ - int i; - struct i2c_client *new_client; - struct it87_data *data; - int err = 0; - const char *name = ""; - int is_isa = i2c_is_isa_adapter(adapter); - int enable_pwm_interface; - - if (!is_isa && - !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto ERROR0; - - /* Reserve the ISA region */ - if (is_isa) - if (!request_region(address, IT87_EXTENT, it87_driver.name)) - goto ERROR0; - - /* Probe whether there is anything available on this address. Already - done for SMBus and Super-I/O clients */ - if (kind < 0) { - if (is_isa && !chip_type) { -#define REALLY_SLOW_IO - /* We need the timeouts for at least some IT87-like chips. But only - if we read 'undefined' registers. */ - i = inb_p(address + 1); - if (inb_p(address + 2) != i - || inb_p(address + 3) != i - || inb_p(address + 7) != i) { - err = -ENODEV; - goto ERROR1; - } -#undef REALLY_SLOW_IO - - /* Let's just hope nothing breaks here */ - i = inb_p(address + 5) & 0x7f; - outb_p(~i & 0x7f, address + 5); - if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { - outb_p(i, address + 5); - err = -ENODEV; - goto ERROR1; - } - } - } - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access it87_{read,write}_value. */ - - if (!(data = kmalloc(sizeof(struct it87_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR1; - } - memset(data, 0, sizeof(struct it87_data)); - - new_client = &data->client; - if (is_isa) - init_MUTEX(&data->lock); - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &it87_driver; - new_client->flags = 0; - - /* Now, we do the remaining detection. */ - - if (kind < 0) { - if ((it87_read_value(new_client, IT87_REG_CONFIG) & 0x80) - || (!is_isa - && it87_read_value(new_client, IT87_REG_I2C_ADDR) != address)) { - err = -ENODEV; - goto ERROR2; - } - } - - /* Determine the chip type. */ - if (kind <= 0) { - i = it87_read_value(new_client, IT87_REG_CHIPID); - if (i == 0x90) { - kind = it87; - if ((is_isa) && (chip_type == IT8712F_DEVID)) - kind = it8712; - } - 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), address); - err = -ENODEV; - goto ERROR2; - } - } - - if (kind == it87) { - name = "it87"; - } else if (kind == it8712) { - name = "it8712"; - } - - /* Fill in the remaining client fields and put it into the global list */ - strlcpy(new_client->name, name, I2C_NAME_SIZE); - data->type = kind; - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto ERROR2; - - /* Check PWM configuration */ - enable_pwm_interface = it87_check_pwm(new_client); - - /* Initialize the IT87 chip */ - it87_init_client(new_client, data); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &sensor_dev_attr_in0_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in1_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in3_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in4_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in5_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in6_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in7_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in8_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in0_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in1_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in2_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in3_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in4_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in5_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in6_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in7_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in0_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in1_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in2_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in3_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in4_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in5_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in6_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_in7_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_max.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp1_type.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp2_type.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_temp3_type.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan1_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan2_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan3_input.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan1_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan2_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan3_min.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan1_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan2_div.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_fan3_div.dev_attr); - device_create_file(&new_client->dev, &dev_attr_alarms); - if (enable_pwm_interface) { - device_create_file(&new_client->dev, &sensor_dev_attr_pwm1_enable.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_pwm2_enable.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_pwm3_enable.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_pwm1.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_pwm2.dev_attr); - device_create_file(&new_client->dev, &sensor_dev_attr_pwm3.dev_attr); - } - - if (data->type == it8712) { - data->vrm = i2c_which_vrm(); - device_create_file_vrm(new_client); - device_create_file_vid(new_client); - } - - return 0; - -ERROR2: - kfree(data); -ERROR1: - if (is_isa) - release_region(address, IT87_EXTENT); -ERROR0: - return err; -} - -static int it87_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, - "Client deregistration failed, client not detached.\n"); - return err; - } - - if(i2c_is_isa_client(client)) - release_region(client->addr, IT87_EXTENT); - kfree(i2c_get_clientdata(client)); - - return 0; -} - -/* The SMBus locks itself, but ISA access must be locked explicitly! - We don't want to lock the whole ISA bus, so we lock each client - separately. - We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, - would slow down the IT87 access and should not be necessary. */ -static int it87_read_value(struct i2c_client *client, u8 reg) -{ - struct it87_data *data = i2c_get_clientdata(client); - - int res; - if (i2c_is_isa_client(client)) { - down(&data->lock); - outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); - res = inb_p(client->addr + IT87_DATA_REG_OFFSET); - up(&data->lock); - return res; - } else - return i2c_smbus_read_byte_data(client, reg); -} - -/* The SMBus locks itself, but ISA access muse be locked explicitly! - We don't want to lock the whole ISA bus, so we lock each client - separately. - We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, - would slow down the IT87 access and should not be necessary. */ -static int it87_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - struct it87_data *data = i2c_get_clientdata(client); - - if (i2c_is_isa_client(client)) { - down(&data->lock); - outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); - outb_p(value, client->addr + IT87_DATA_REG_OFFSET); - up(&data->lock); - return 0; - } else - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Return 1 if and only if the PWM interface is safe to use */ -static int it87_check_pwm(struct i2c_client *client) -{ - /* Some BIOSes fail to correctly configure the IT87 fans. All fans off - * and polarity set to active low is sign that this is the case so we - * disable pwm control to protect the user. */ - int tmp = it87_read_value(client, IT87_REG_FAN_CTL); - if ((tmp & 0x87) == 0) { - if (fix_pwm_polarity) { - /* The user asks us to attempt a chip reconfiguration. - * This means switching to active high polarity and - * inverting all fan speed values. */ - int i; - u8 pwm[3]; - - for (i = 0; i < 3; i++) - pwm[i] = it87_read_value(client, - IT87_REG_PWM(i)); - - /* If any fan is in automatic pwm mode, the polarity - * might be correct, as suspicious as it seems, so we - * better don't change anything (but still disable the - * PWM interface). */ - if (!((pwm[0] | pwm[1] | pwm[2]) & 0x80)) { - dev_info(&client->dev, "Reconfiguring PWM to " - "active high polarity\n"); - it87_write_value(client, IT87_REG_FAN_CTL, - tmp | 0x87); - for (i = 0; i < 3; i++) - it87_write_value(client, - IT87_REG_PWM(i), - 0x7f & ~pwm[i]); - return 1; - } - - dev_info(&client->dev, "PWM configuration is " - "too broken to be fixed\n"); - } - - dev_info(&client->dev, "Detected broken BIOS " - "defaults, disabling PWM interface\n"); - return 0; - } else if (fix_pwm_polarity) { - dev_info(&client->dev, "PWM configuration looks " - "sane, won't touch\n"); - } - - return 1; -} - -/* Called when we have found a new IT87. */ -static void it87_init_client(struct i2c_client *client, struct it87_data *data) -{ - int tmp, i; - - /* initialize to sane defaults: - * - if the chip is in manual pwm mode, this will be overwritten with - * the actual settings on the chip (so in this case, initialization - * is not needed) - * - if in automatic or on/off mode, we could switch to manual mode, - * read the registers and set manual_pwm_ctl accordingly, but currently - * this is not implemented, so we initialize to something sane */ - for (i = 0; i < 3; i++) { - data->manual_pwm_ctl[i] = 0xff; - } - - /* Check if temperature channnels are reset manually or by some reason */ - tmp = it87_read_value(client, IT87_REG_TEMP_ENABLE); - if ((tmp & 0x3f) == 0) { - /* Temp1,Temp3=thermistor; Temp2=thermal diode */ - tmp = (tmp & 0xc0) | 0x2a; - it87_write_value(client, IT87_REG_TEMP_ENABLE, tmp); - } - data->sensor = tmp; - - /* Check if voltage monitors are reset manually or by some reason */ - tmp = it87_read_value(client, IT87_REG_VIN_ENABLE); - if ((tmp & 0xff) == 0) { - /* Enable all voltage monitors */ - it87_write_value(client, IT87_REG_VIN_ENABLE, 0xff); - } - - /* Check if tachometers are reset manually or by some reason */ - data->fan_main_ctrl = it87_read_value(client, IT87_REG_FAN_MAIN_CTRL); - if ((data->fan_main_ctrl & 0x70) == 0) { - /* Enable all fan tachometers */ - data->fan_main_ctrl |= 0x70; - it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl); - } - - /* Set current fan mode registers and the default settings for the - * other mode registers */ - for (i = 0; i < 3; i++) { - if (data->fan_main_ctrl & (1 << i)) { - /* pwm mode */ - tmp = it87_read_value(client, IT87_REG_PWM(i)); - if (tmp & 0x80) { - /* automatic pwm - not yet implemented, but - * leave the settings made by the BIOS alone - * until a change is requested via the sysfs - * interface */ - } else { - /* manual pwm */ - data->manual_pwm_ctl[i] = PWM_FROM_REG(tmp); - } - } - } - - /* Start monitoring */ - it87_write_value(client, IT87_REG_CONFIG, - (it87_read_value(client, IT87_REG_CONFIG) & 0x36) - | (update_vbat ? 0x41 : 0x01)); -} - -static struct it87_data *it87_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct it87_data *data = i2c_get_clientdata(client); - int i; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - - if (update_vbat) { - /* Cleared after each update, so reenable. Value - returned by this read will be previous value */ - it87_write_value(client, IT87_REG_CONFIG, - it87_read_value(client, IT87_REG_CONFIG) | 0x40); - } - for (i = 0; i <= 7; i++) { - data->in[i] = - it87_read_value(client, IT87_REG_VIN(i)); - data->in_min[i] = - it87_read_value(client, IT87_REG_VIN_MIN(i)); - data->in_max[i] = - it87_read_value(client, IT87_REG_VIN_MAX(i)); - } - data->in[8] = - it87_read_value(client, IT87_REG_VIN(8)); - /* Temperature sensor doesn't have limit registers, set - to min and max value */ - data->in_min[8] = 0; - data->in_max[8] = 255; - - for (i = 0; i < 3; i++) { - data->fan[i] = - it87_read_value(client, IT87_REG_FAN(i)); - data->fan_min[i] = - it87_read_value(client, IT87_REG_FAN_MIN(i)); - } - for (i = 0; i < 3; i++) { - data->temp[i] = - it87_read_value(client, IT87_REG_TEMP(i)); - data->temp_high[i] = - it87_read_value(client, IT87_REG_TEMP_HIGH(i)); - data->temp_low[i] = - it87_read_value(client, IT87_REG_TEMP_LOW(i)); - } - - i = it87_read_value(client, IT87_REG_FAN_DIV); - data->fan_div[0] = i & 0x07; - data->fan_div[1] = (i >> 3) & 0x07; - data->fan_div[2] = (i & 0x40) ? 3 : 1; - - data->alarms = - it87_read_value(client, IT87_REG_ALARM1) | - (it87_read_value(client, IT87_REG_ALARM2) << 8) | - (it87_read_value(client, IT87_REG_ALARM3) << 16); - data->fan_main_ctrl = it87_read_value(client, IT87_REG_FAN_MAIN_CTRL); - - data->sensor = it87_read_value(client, IT87_REG_TEMP_ENABLE); - /* The 8705 does not have VID capability */ - if (data->type == it8712) { - data->vid = it87_read_value(client, IT87_REG_VID); - data->vid &= 0x1f; - } - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sm_it87_init(void) -{ - int addr; - - if (!it87_find(&addr)) { - normal_isa[0] = addr; - } - return i2c_add_driver(&it87_driver); -} - -static void __exit sm_it87_exit(void) -{ - i2c_del_driver(&it87_driver); -} - - -MODULE_AUTHOR("Chris Gauthron "); -MODULE_DESCRIPTION("IT8705F, IT8712F, Sis950 driver"); -module_param(update_vbat, bool, 0); -MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); -module_param(fix_pwm_polarity, bool, 0); -MODULE_PARM_DESC(fix_pwm_polarity, "Force PWM polarity to active high (DANGEROUS)"); -MODULE_LICENSE("GPL"); - -module_init(sm_it87_init); -module_exit(sm_it87_exit); diff --git a/drivers/i2c/chips/lm63.c b/drivers/i2c/chips/lm63.c deleted file mode 100644 index 7c6f9ea5a254..000000000000 --- a/drivers/i2c/chips/lm63.c +++ /dev/null @@ -1,597 +0,0 @@ -/* - * lm63.c - driver for the National Semiconductor LM63 temperature sensor - * with integrated fan control - * Copyright (C) 2004-2005 Jean Delvare - * Based on the lm90 driver. - * - * The LM63 is a sensor chip made by National Semiconductor. It measures - * two temperatures (its own and one external one) and the speed of one - * fan, those speed it can additionally control. Complete datasheet can be - * obtained from National's website at: - * http://www.national.com/pf/LM/LM63.html - * - * The LM63 is basically an LM86 with fan speed monitoring and control - * capabilities added. It misses some of the LM86 features though: - * - No low limit for local temperature. - * - No critical limit for local temperature. - * - Critical limit for remote temperature can be changed only once. We - * will consider that the critical limit is read-only. - * - * The datasheet isn't very clear about what the tachometer reading is. - * I had a explanation from National Semiconductor though. The two lower - * bits of the read value have to be masked out. The value is still 16 bit - * in width. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * Addresses to scan - * Address is fully defined internally and cannot be changed. - */ - -static unsigned short normal_i2c[] = { 0x4c, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* - * Insmod parameters - */ - -SENSORS_INSMOD_1(lm63); - -/* - * The LM63 registers - */ - -#define LM63_REG_CONFIG1 0x03 -#define LM63_REG_CONFIG2 0xBF -#define LM63_REG_CONFIG_FAN 0x4A - -#define LM63_REG_TACH_COUNT_MSB 0x47 -#define LM63_REG_TACH_COUNT_LSB 0x46 -#define LM63_REG_TACH_LIMIT_MSB 0x49 -#define LM63_REG_TACH_LIMIT_LSB 0x48 - -#define LM63_REG_PWM_VALUE 0x4C -#define LM63_REG_PWM_FREQ 0x4D - -#define LM63_REG_LOCAL_TEMP 0x00 -#define LM63_REG_LOCAL_HIGH 0x05 - -#define LM63_REG_REMOTE_TEMP_MSB 0x01 -#define LM63_REG_REMOTE_TEMP_LSB 0x10 -#define LM63_REG_REMOTE_OFFSET_MSB 0x11 -#define LM63_REG_REMOTE_OFFSET_LSB 0x12 -#define LM63_REG_REMOTE_HIGH_MSB 0x07 -#define LM63_REG_REMOTE_HIGH_LSB 0x13 -#define LM63_REG_REMOTE_LOW_MSB 0x08 -#define LM63_REG_REMOTE_LOW_LSB 0x14 -#define LM63_REG_REMOTE_TCRIT 0x19 -#define LM63_REG_REMOTE_TCRIT_HYST 0x21 - -#define LM63_REG_ALERT_STATUS 0x02 -#define LM63_REG_ALERT_MASK 0x16 - -#define LM63_REG_MAN_ID 0xFE -#define LM63_REG_CHIP_ID 0xFF - -/* - * Conversions and various macros - * For tachometer counts, the LM63 uses 16-bit values. - * For local temperature and high limit, remote critical limit and hysteresis - * value, it uses signed 8-bit values with LSB = 1 degree Celsius. - * For remote temperature, low and high limits, it uses signed 11-bit values - * with LSB = 0.125 degree Celsius, left-justified in 16-bit registers. - */ - -#define FAN_FROM_REG(reg) ((reg) == 0xFFFC || (reg) == 0 ? 0 : \ - 5400000 / (reg)) -#define FAN_TO_REG(val) ((val) <= 82 ? 0xFFFC : \ - (5400000 / (val)) & 0xFFFC) -#define TEMP8_FROM_REG(reg) ((reg) * 1000) -#define TEMP8_TO_REG(val) ((val) <= -128000 ? -128 : \ - (val) >= 127000 ? 127 : \ - (val) < 0 ? ((val) - 500) / 1000 : \ - ((val) + 500) / 1000) -#define TEMP11_FROM_REG(reg) ((reg) / 32 * 125) -#define TEMP11_TO_REG(val) ((val) <= -128000 ? 0x8000 : \ - (val) >= 127875 ? 0x7FE0 : \ - (val) < 0 ? ((val) - 62) / 125 * 32 : \ - ((val) + 62) / 125 * 32) -#define HYST_TO_REG(val) ((val) <= 0 ? 0 : \ - (val) >= 127000 ? 127 : \ - ((val) + 500) / 1000) - -/* - * Functions declaration - */ - -static int lm63_attach_adapter(struct i2c_adapter *adapter); -static int lm63_detach_client(struct i2c_client *client); - -static struct lm63_data *lm63_update_device(struct device *dev); - -static int lm63_detect(struct i2c_adapter *adapter, int address, int kind); -static void lm63_init_client(struct i2c_client *client); - -/* - * Driver data (common to all clients) - */ - -static struct i2c_driver lm63_driver = { - .owner = THIS_MODULE, - .name = "lm63", - .flags = I2C_DF_NOTIFY, - .attach_adapter = lm63_attach_adapter, - .detach_client = lm63_detach_client, -}; - -/* - * Client data (each client gets its own) - */ - -struct lm63_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* registers values */ - u8 config, config_fan; - u16 fan[2]; /* 0: input - 1: low limit */ - u8 pwm1_freq; - u8 pwm1_value; - s8 temp8[3]; /* 0: local input - 1: local high limit - 2: remote critical limit */ - s16 temp11[3]; /* 0: remote input - 1: remote low limit - 2: remote high limit */ - u8 temp2_crit_hyst; - u8 alarms; -}; - -/* - * Sysfs callback functions and files - */ - -static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm63_data *data = lm63_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[attr->index])); -} - -static ssize_t set_fan(struct device *dev, struct device_attribute *dummy, - const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm63_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->fan[1] = FAN_TO_REG(val); - i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_LSB, - data->fan[1] & 0xFF); - i2c_smbus_write_byte_data(client, LM63_REG_TACH_LIMIT_MSB, - data->fan[1] >> 8); - up(&data->update_lock); - return count; -} - -static ssize_t show_pwm1(struct device *dev, struct device_attribute *dummy, - char *buf) -{ - struct lm63_data *data = lm63_update_device(dev); - return sprintf(buf, "%d\n", data->pwm1_value >= 2 * data->pwm1_freq ? - 255 : (data->pwm1_value * 255 + data->pwm1_freq) / - (2 * data->pwm1_freq)); -} - -static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy, - const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm63_data *data = i2c_get_clientdata(client); - unsigned long val; - - if (!(data->config_fan & 0x20)) /* register is read-only */ - return -EPERM; - - val = simple_strtoul(buf, NULL, 10); - down(&data->update_lock); - data->pwm1_value = val <= 0 ? 0 : - val >= 255 ? 2 * data->pwm1_freq : - (val * data->pwm1_freq * 2 + 127) / 255; - i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1_value); - up(&data->update_lock); - return count; -} - -static ssize_t show_pwm1_enable(struct device *dev, struct device_attribute *dummy, - char *buf) -{ - struct lm63_data *data = lm63_update_device(dev); - return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2); -} - -static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm63_data *data = lm63_update_device(dev); - return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[attr->index])); -} - -static ssize_t set_temp8(struct device *dev, struct device_attribute *dummy, - const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm63_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp8[1] = TEMP8_TO_REG(val); - i2c_smbus_write_byte_data(client, LM63_REG_LOCAL_HIGH, data->temp8[1]); - up(&data->update_lock); - return count; -} - -static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm63_data *data = lm63_update_device(dev); - return sprintf(buf, "%d\n", TEMP11_FROM_REG(data->temp11[attr->index])); -} - -static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - static const u8 reg[4] = { - LM63_REG_REMOTE_LOW_MSB, - LM63_REG_REMOTE_LOW_LSB, - LM63_REG_REMOTE_HIGH_MSB, - LM63_REG_REMOTE_HIGH_LSB, - }; - - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct lm63_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - int nr = attr->index; - - down(&data->update_lock); - data->temp11[nr] = TEMP11_TO_REG(val); - i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], - data->temp11[nr] >> 8); - i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], - data->temp11[nr] & 0xff); - up(&data->update_lock); - return count; -} - -/* Hysteresis register holds a relative value, while we want to present - an absolute to user-space */ -static ssize_t show_temp2_crit_hyst(struct device *dev, struct device_attribute *dummy, - char *buf) -{ - struct lm63_data *data = lm63_update_device(dev); - return sprintf(buf, "%d\n", TEMP8_FROM_REG(data->temp8[2]) - - TEMP8_FROM_REG(data->temp2_crit_hyst)); -} - -/* And now the other way around, user-space provides an absolute - hysteresis value and we have to store a relative one */ -static ssize_t set_temp2_crit_hyst(struct device *dev, struct device_attribute *dummy, - const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm63_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - long hyst; - - down(&data->update_lock); - hyst = TEMP8_FROM_REG(data->temp8[2]) - val; - i2c_smbus_write_byte_data(client, LM63_REG_REMOTE_TCRIT_HYST, - HYST_TO_REG(hyst)); - up(&data->update_lock); - return count; -} - -static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, - char *buf) -{ - struct lm63_data *data = lm63_update_device(dev); - return sprintf(buf, "%u\n", data->alarms); -} - -static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); -static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, - set_fan, 1); - -static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1); -static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL); - -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0); -static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 1); - -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); -static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 1); -static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 2); -static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp8, NULL, 2); -static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst, - set_temp2_crit_hyst); - -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -/* - * Real code - */ - -static int lm63_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, lm63_detect); -} - -/* - * The following function does more than just detection. If detection - * succeeds, it also registers the new chip. - */ -static int lm63_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct lm63_data *data; - int err = 0; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - if (!(data = kmalloc(sizeof(struct lm63_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct lm63_data)); - - /* The common I2C client data is placed right before the - LM63-specific data. */ - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm63_driver; - new_client->flags = 0; - - /* Default to an LM63 if forced */ - if (kind == 0) - kind = lm63; - - 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); - goto exit_free; - } - } - - strlcpy(new_client->name, "lm63", I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the LM63 chip */ - lm63_init_client(new_client); - - /* Register sysfs hooks */ - if (data->config & 0x04) { /* tachometer enabled */ - device_create_file(&new_client->dev, - &sensor_dev_attr_fan1_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_fan1_min.dev_attr); - } - device_create_file(&new_client->dev, &dev_attr_pwm1); - device_create_file(&new_client->dev, &dev_attr_pwm1_enable); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_crit.dev_attr); - device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst); - device_create_file(&new_client->dev, &dev_attr_alarms); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -/* Idealy we shouldn't have to initialize anything, since the BIOS - should have taken care of everything */ -static void lm63_init_client(struct i2c_client *client) -{ - struct lm63_data *data = i2c_get_clientdata(client); - - data->config = i2c_smbus_read_byte_data(client, LM63_REG_CONFIG1); - data->config_fan = i2c_smbus_read_byte_data(client, - LM63_REG_CONFIG_FAN); - - /* Start converting if needed */ - if (data->config & 0x40) { /* standby */ - dev_dbg(&client->dev, "Switching to operational mode"); - data->config &= 0xA7; - i2c_smbus_write_byte_data(client, LM63_REG_CONFIG1, - data->config); - } - - /* We may need pwm1_freq before ever updating the client data */ - data->pwm1_freq = i2c_smbus_read_byte_data(client, LM63_REG_PWM_FREQ); - if (data->pwm1_freq == 0) - data->pwm1_freq = 1; - - /* Show some debug info about the LM63 configuration */ - dev_dbg(&client->dev, "Alert/tach pin configured for %s\n", - (data->config & 0x04) ? "tachometer input" : - "alert output"); - dev_dbg(&client->dev, "PWM clock %s kHz, output frequency %u Hz\n", - (data->config_fan & 0x08) ? "1.4" : "360", - ((data->config_fan & 0x08) ? 700 : 180000) / data->pwm1_freq); - dev_dbg(&client->dev, "PWM output active %s, %s mode\n", - (data->config_fan & 0x10) ? "low" : "high", - (data->config_fan & 0x20) ? "manual" : "auto"); -} - -static int lm63_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - -static struct lm63_data *lm63_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm63_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - if (data->config & 0x04) { /* tachometer enabled */ - /* order matters for fan1_input */ - data->fan[0] = i2c_smbus_read_byte_data(client, - LM63_REG_TACH_COUNT_LSB) & 0xFC; - data->fan[0] |= i2c_smbus_read_byte_data(client, - LM63_REG_TACH_COUNT_MSB) << 8; - data->fan[1] = (i2c_smbus_read_byte_data(client, - LM63_REG_TACH_LIMIT_LSB) & 0xFC) - | (i2c_smbus_read_byte_data(client, - LM63_REG_TACH_LIMIT_MSB) << 8); - } - - data->pwm1_freq = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_FREQ); - if (data->pwm1_freq == 0) - data->pwm1_freq = 1; - data->pwm1_value = i2c_smbus_read_byte_data(client, - LM63_REG_PWM_VALUE); - - data->temp8[0] = i2c_smbus_read_byte_data(client, - LM63_REG_LOCAL_TEMP); - data->temp8[1] = i2c_smbus_read_byte_data(client, - LM63_REG_LOCAL_HIGH); - - /* order matters for temp2_input */ - data->temp11[0] = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TEMP_MSB) << 8; - data->temp11[0] |= i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TEMP_LSB); - data->temp11[1] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_LOW_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_LOW_LSB); - data->temp11[2] = (i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_HIGH_MSB) << 8) - | i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_HIGH_LSB); - data->temp8[2] = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TCRIT); - data->temp2_crit_hyst = i2c_smbus_read_byte_data(client, - LM63_REG_REMOTE_TCRIT_HYST); - - data->alarms = i2c_smbus_read_byte_data(client, - LM63_REG_ALERT_STATUS) & 0x7F; - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_lm63_init(void) -{ - return i2c_add_driver(&lm63_driver); -} - -static void __exit sensors_lm63_exit(void) -{ - i2c_del_driver(&lm63_driver); -} - -MODULE_AUTHOR("Jean Delvare "); -MODULE_DESCRIPTION("LM63 driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_lm63_init); -module_exit(sensors_lm63_exit); diff --git a/drivers/i2c/chips/lm75.c b/drivers/i2c/chips/lm75.c deleted file mode 100644 index 5be164ed278e..000000000000 --- a/drivers/i2c/chips/lm75.c +++ /dev/null @@ -1,296 +0,0 @@ -/* - lm75.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998, 1999 Frodo Looijaard - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include "lm75.h" - - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, - 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_1(lm75); - -/* Many LM75 constants specified below */ - -/* The LM75 registers */ -#define LM75_REG_TEMP 0x00 -#define LM75_REG_CONF 0x01 -#define LM75_REG_TEMP_HYST 0x02 -#define LM75_REG_TEMP_OS 0x03 - -/* Each client has this additional data */ -struct lm75_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - u16 temp_input; /* Register values */ - u16 temp_max; - u16 temp_hyst; -}; - -static int lm75_attach_adapter(struct i2c_adapter *adapter); -static int lm75_detect(struct i2c_adapter *adapter, int address, int kind); -static void lm75_init_client(struct i2c_client *client); -static int lm75_detach_client(struct i2c_client *client); -static int lm75_read_value(struct i2c_client *client, u8 reg); -static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value); -static struct lm75_data *lm75_update_device(struct device *dev); - - -/* This is the driver that will be inserted */ -static struct i2c_driver lm75_driver = { - .owner = THIS_MODULE, - .name = "lm75", - .id = I2C_DRIVERID_LM75, - .flags = I2C_DF_NOTIFY, - .attach_adapter = lm75_attach_adapter, - .detach_client = lm75_detach_client, -}; - -#define show(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm75_data *data = lm75_update_device(dev); \ - return sprintf(buf, "%d\n", LM75_TEMP_FROM_REG(data->value)); \ -} -show(temp_max); -show(temp_hyst); -show(temp_input); - -#define set(value, reg) \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm75_data *data = i2c_get_clientdata(client); \ - int temp = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->value = LM75_TEMP_TO_REG(temp); \ - lm75_write_value(client, reg, data->value); \ - up(&data->update_lock); \ - return count; \ -} -set(temp_max, LM75_REG_TEMP_OS); -set(temp_hyst, LM75_REG_TEMP_HYST); - -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max); -static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_hyst, set_temp_hyst); -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input, NULL); - -static int lm75_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, lm75_detect); -} - -/* This function is called by i2c_detect */ -static int lm75_detect(struct i2c_adapter *adapter, int address, int kind) -{ - int i; - struct i2c_client *new_client; - struct lm75_data *data; - int err = 0; - const char *name = ""; - - /* Make sure we aren't probing the ISA bus!! This is just a safety check - at this moment; i2c_detect really won't call us. */ -#ifdef DEBUG - if (i2c_is_isa_adapter(adapter)) { - dev_dbg(&adapter->dev, - "lm75_detect called for an ISA bus adapter?!?\n"); - goto exit; - } -#endif - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WORD_DATA)) - goto exit; - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access lm75_{read,write}_value. */ - if (!(data = kmalloc(sizeof(struct lm75_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct lm75_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm75_driver; - new_client->flags = 0; - - /* Now, we do the remaining detection. There is no identification- - dedicated register so we have to rely on several tricks: - unused bits, registers cycling over 8-address boundaries, - addresses 0x04-0x07 returning the last read value. - 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) - goto exit_free; - 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) - goto exit_free; - - /* Unused bits */ - if (conf & 0xe0) - goto exit_free; - - /* 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) - goto exit_free; - } - - /* Determine the chip type - only one kind supported! */ - if (kind <= 0) - kind = lm75; - - if (kind == lm75) { - name = "lm75"; - } - - /* Fill in the remaining client fields and put it into the global list */ - strlcpy(new_client->name, name, I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the LM75 chip */ - lm75_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static int lm75_detach_client(struct i2c_client *client) -{ - i2c_detach_client(client); - kfree(i2c_get_clientdata(client)); - return 0; -} - -/* All registers are word-sized, except for the configuration register. - LM75 uses a high-byte first convention, which is exactly opposite to - the usual practice. */ -static int lm75_read_value(struct i2c_client *client, u8 reg) -{ - if (reg == LM75_REG_CONF) - return i2c_smbus_read_byte_data(client, reg); - else - return swab16(i2c_smbus_read_word_data(client, reg)); -} - -/* All registers are word-sized, except for the configuration register. - LM75 uses a high-byte first convention, which is exactly opposite to - the usual practice. */ -static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value) -{ - if (reg == LM75_REG_CONF) - return i2c_smbus_write_byte_data(client, reg, value); - else - return i2c_smbus_write_word_data(client, reg, swab16(value)); -} - -static void lm75_init_client(struct i2c_client *client) -{ - /* Initialize the LM75 chip */ - lm75_write_value(client, LM75_REG_CONF, 0); -} - -static struct lm75_data *lm75_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm75_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - dev_dbg(&client->dev, "Starting lm75 update\n"); - - data->temp_input = lm75_read_value(client, LM75_REG_TEMP); - data->temp_max = lm75_read_value(client, LM75_REG_TEMP_OS); - data->temp_hyst = lm75_read_value(client, LM75_REG_TEMP_HYST); - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_lm75_init(void) -{ - return i2c_add_driver(&lm75_driver); -} - -static void __exit sensors_lm75_exit(void) -{ - i2c_del_driver(&lm75_driver); -} - -MODULE_AUTHOR("Frodo Looijaard "); -MODULE_DESCRIPTION("LM75 driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_lm75_init); -module_exit(sensors_lm75_exit); diff --git a/drivers/i2c/chips/lm75.h b/drivers/i2c/chips/lm75.h deleted file mode 100644 index 63e3f2fb4c21..000000000000 --- a/drivers/i2c/chips/lm75.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - lm75.h - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 2003 Mark M. Hoffman - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - This file contains common code for encoding/decoding LM75 type - temperature readings, which are emulated by many of the chips - we support. As the user is unlikely to load more than one driver - which contains this code, we don't worry about the wasted space. -*/ - -#include - -/* straight from the datasheet */ -#define LM75_TEMP_MIN (-55000) -#define LM75_TEMP_MAX 125000 - -/* TEMP: 0.001C/bit (-55C to +125C) - REG: (0.5C/bit, two's complement) << 7 */ -static inline u16 LM75_TEMP_TO_REG(int temp) -{ - int ntemp = SENSORS_LIMIT(temp, LM75_TEMP_MIN, LM75_TEMP_MAX); - ntemp += (ntemp<0 ? -250 : 250); - return (u16)((ntemp / 500) << 7); -} - -static inline int LM75_TEMP_FROM_REG(u16 reg) -{ - /* use integer division instead of equivalent right shift to - guarantee arithmetic shift and preserve the sign */ - return ((s16)reg / 128) * 500; -} - diff --git a/drivers/i2c/chips/lm77.c b/drivers/i2c/chips/lm77.c deleted file mode 100644 index b98f44952997..000000000000 --- a/drivers/i2c/chips/lm77.c +++ /dev/null @@ -1,420 +0,0 @@ -/* - lm77.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - - Copyright (c) 2004 Andras BALI - - Heavily based on lm75.c by Frodo Looijaard . The LM77 - is a temperature sensor and thermal window comparator with 0.5 deg - resolution made by National Semiconductor. Complete datasheet can be - obtained at their site: - http://www.national.com/pf/LM/LM77.html - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include - - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_1(lm77); - -/* The LM77 registers */ -#define LM77_REG_TEMP 0x00 -#define LM77_REG_CONF 0x01 -#define LM77_REG_TEMP_HYST 0x02 -#define LM77_REG_TEMP_CRIT 0x03 -#define LM77_REG_TEMP_MIN 0x04 -#define LM77_REG_TEMP_MAX 0x05 - -/* Each client has this additional data */ -struct lm77_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; - unsigned long last_updated; /* In jiffies */ - int temp_input; /* Temperatures */ - int temp_crit; - int temp_min; - int temp_max; - int temp_hyst; - u8 alarms; -}; - -static int lm77_attach_adapter(struct i2c_adapter *adapter); -static int lm77_detect(struct i2c_adapter *adapter, int address, int kind); -static void lm77_init_client(struct i2c_client *client); -static int lm77_detach_client(struct i2c_client *client); -static u16 lm77_read_value(struct i2c_client *client, u8 reg); -static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value); - -static struct lm77_data *lm77_update_device(struct device *dev); - - -/* This is the driver that will be inserted */ -static struct i2c_driver lm77_driver = { - .owner = THIS_MODULE, - .name = "lm77", - .flags = I2C_DF_NOTIFY, - .attach_adapter = lm77_attach_adapter, - .detach_client = lm77_detach_client, -}; - -/* straight from the datasheet */ -#define LM77_TEMP_MIN (-55000) -#define LM77_TEMP_MAX 125000 - -/* In the temperature registers, the low 3 bits are not part of the - temperature values; they are the status bits. */ -static inline u16 LM77_TEMP_TO_REG(int temp) -{ - int ntemp = SENSORS_LIMIT(temp, LM77_TEMP_MIN, LM77_TEMP_MAX); - return (u16)((ntemp / 500) * 8); -} - -static inline int LM77_TEMP_FROM_REG(u16 reg) -{ - return ((int)reg / 8) * 500; -} - -/* sysfs stuff */ - -/* read routines for temperature limits */ -#define show(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm77_data *data = lm77_update_device(dev); \ - return sprintf(buf, "%d\n", data->value); \ -} - -show(temp_input); -show(temp_crit); -show(temp_min); -show(temp_max); -show(alarms); - -/* read routines for hysteresis values */ -static ssize_t show_temp_crit_hyst(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm77_data *data = lm77_update_device(dev); - return sprintf(buf, "%d\n", data->temp_crit - data->temp_hyst); -} -static ssize_t show_temp_min_hyst(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm77_data *data = lm77_update_device(dev); - return sprintf(buf, "%d\n", data->temp_min + data->temp_hyst); -} -static ssize_t show_temp_max_hyst(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm77_data *data = lm77_update_device(dev); - return sprintf(buf, "%d\n", data->temp_max - data->temp_hyst); -} - -/* write routines */ -#define set(value, reg) \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm77_data *data = i2c_get_clientdata(client); \ - long val = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->value = val; \ - lm77_write_value(client, reg, LM77_TEMP_TO_REG(data->value)); \ - up(&data->update_lock); \ - return count; \ -} - -set(temp_min, LM77_REG_TEMP_MIN); -set(temp_max, LM77_REG_TEMP_MAX); - -/* hysteresis is stored as a relative value on the chip, so it has to be - converted first */ -static ssize_t set_temp_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm77_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->temp_hyst = data->temp_crit - val; - lm77_write_value(client, LM77_REG_TEMP_HYST, - LM77_TEMP_TO_REG(data->temp_hyst)); - up(&data->update_lock); - return count; -} - -/* preserve hysteresis when setting T_crit */ -static ssize_t set_temp_crit(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm77_data *data = i2c_get_clientdata(client); - long val = simple_strtoul(buf, NULL, 10); - int oldcrithyst; - - down(&data->update_lock); - oldcrithyst = data->temp_crit - data->temp_hyst; - data->temp_crit = val; - data->temp_hyst = data->temp_crit - oldcrithyst; - lm77_write_value(client, LM77_REG_TEMP_CRIT, - LM77_TEMP_TO_REG(data->temp_crit)); - lm77_write_value(client, LM77_REG_TEMP_HYST, - LM77_TEMP_TO_REG(data->temp_hyst)); - up(&data->update_lock); - return count; -} - -static DEVICE_ATTR(temp1_input, S_IRUGO, - show_temp_input, NULL); -static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, - show_temp_crit, set_temp_crit); -static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, - show_temp_min, set_temp_min); -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, - show_temp_max, set_temp_max); - -static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, - show_temp_crit_hyst, set_temp_crit_hyst); -static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, - show_temp_min_hyst, NULL); -static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, - show_temp_max_hyst, NULL); - -static DEVICE_ATTR(alarms, S_IRUGO, - show_alarms, NULL); - -static int lm77_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, lm77_detect); -} - -/* This function is called by i2c_detect */ -static int lm77_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct lm77_data *data; - int err = 0; - const char *name = ""; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WORD_DATA)) - goto exit; - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access lm77_{read,write}_value. */ - if (!(data = kmalloc(sizeof(struct lm77_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct lm77_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm77_driver; - new_client->flags = 0; - - /* Here comes the remaining detection. Since the LM77 has no - register dedicated to identification, we have to rely on the - following tricks: - - 1. the high 4 bits represent the sign and thus they should - always be the same - 2. the high 3 bits are unused in the configuration register - 3. addresses 0x06 and 0x07 return the last read value - 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) - goto exit_free; - - /* 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)) - goto exit_free; - - /* unused bits */ - if (conf & 0xe0) - goto exit_free; - - /* 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) - goto exit_free; - 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) - goto exit_free; - 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) - goto exit_free; - - } - - /* Determine the chip type - only one kind supported! */ - if (kind <= 0) - kind = lm77; - - if (kind == lm77) { - name = "lm77"; - } - - /* Fill in the remaining client fields and put it into the global list */ - strlcpy(new_client->name, name, I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the LM77 chip */ - lm77_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_crit); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_alarms); - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static int lm77_detach_client(struct i2c_client *client) -{ - i2c_detach_client(client); - kfree(i2c_get_clientdata(client)); - return 0; -} - -/* All registers are word-sized, except for the configuration register. - The LM77 uses the high-byte first convention. */ -static u16 lm77_read_value(struct i2c_client *client, u8 reg) -{ - if (reg == LM77_REG_CONF) - return i2c_smbus_read_byte_data(client, reg); - else - return swab16(i2c_smbus_read_word_data(client, reg)); -} - -static int lm77_write_value(struct i2c_client *client, u8 reg, u16 value) -{ - if (reg == LM77_REG_CONF) - return i2c_smbus_write_byte_data(client, reg, value); - else - return i2c_smbus_write_word_data(client, reg, swab16(value)); -} - -static void lm77_init_client(struct i2c_client *client) -{ - /* Initialize the LM77 chip - turn off shutdown mode */ - int conf = lm77_read_value(client, LM77_REG_CONF); - if (conf & 1) - lm77_write_value(client, LM77_REG_CONF, conf & 0xfe); -} - -static struct lm77_data *lm77_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm77_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - dev_dbg(&client->dev, "Starting lm77 update\n"); - data->temp_input = - LM77_TEMP_FROM_REG(lm77_read_value(client, - LM77_REG_TEMP)); - data->temp_hyst = - LM77_TEMP_FROM_REG(lm77_read_value(client, - LM77_REG_TEMP_HYST)); - data->temp_crit = - LM77_TEMP_FROM_REG(lm77_read_value(client, - LM77_REG_TEMP_CRIT)); - data->temp_min = - LM77_TEMP_FROM_REG(lm77_read_value(client, - LM77_REG_TEMP_MIN)); - data->temp_max = - LM77_TEMP_FROM_REG(lm77_read_value(client, - LM77_REG_TEMP_MAX)); - data->alarms = - lm77_read_value(client, LM77_REG_TEMP) & 0x0007; - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_lm77_init(void) -{ - return i2c_add_driver(&lm77_driver); -} - -static void __exit sensors_lm77_exit(void) -{ - i2c_del_driver(&lm77_driver); -} - -MODULE_AUTHOR("Andras BALI "); -MODULE_DESCRIPTION("LM77 driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_lm77_init); -module_exit(sensors_lm77_exit); diff --git a/drivers/i2c/chips/lm78.c b/drivers/i2c/chips/lm78.c deleted file mode 100644 index 29241469dcba..000000000000 --- a/drivers/i2c/chips/lm78.c +++ /dev/null @@ -1,795 +0,0 @@ -/* - lm78.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998, 1999 Frodo Looijaard - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24, - 0x25, 0x26, 0x27, 0x28, 0x29, - 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, - 0x2f, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_3(lm78, lm78j, lm79); - -/* Many LM78 constants specified below */ - -/* Length of ISA address segment */ -#define LM78_EXTENT 8 - -/* Where are the ISA address/data registers relative to the base address */ -#define LM78_ADDR_REG_OFFSET 5 -#define LM78_DATA_REG_OFFSET 6 - -/* The LM78 registers */ -#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2) -#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2) -#define LM78_REG_IN(nr) (0x20 + (nr)) - -#define LM78_REG_FAN_MIN(nr) (0x3b + (nr)) -#define LM78_REG_FAN(nr) (0x28 + (nr)) - -#define LM78_REG_TEMP 0x27 -#define LM78_REG_TEMP_OVER 0x39 -#define LM78_REG_TEMP_HYST 0x3a - -#define LM78_REG_ALARM1 0x41 -#define LM78_REG_ALARM2 0x42 - -#define LM78_REG_VID_FANDIV 0x47 - -#define LM78_REG_CONFIG 0x40 -#define LM78_REG_CHIPID 0x49 -#define LM78_REG_I2C_ADDR 0x48 - - -/* Conversions. Rounding and limit checking is only done on the TO_REG - variants. */ - -/* IN: mV, (0V to 4.08V) - REG: 16mV/bit */ -static inline u8 IN_TO_REG(unsigned long val) -{ - unsigned long nval = SENSORS_LIMIT(val, 0, 4080); - return (nval + 8) / 16; -} -#define IN_FROM_REG(val) ((val) * 16) - -static inline u8 FAN_TO_REG(long rpm, int div) -{ - if (rpm <= 0) - return 255; - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); -} - -static inline int FAN_FROM_REG(u8 val, int div) -{ - return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div); -} - -/* TEMP: mC (-128C to +127C) - REG: 1C/bit, two's complement */ -static inline s8 TEMP_TO_REG(int val) -{ - int nval = SENSORS_LIMIT(val, -128000, 127000) ; - return nval<0 ? (nval-500)/1000 : (nval+500)/1000; -} - -static inline int TEMP_FROM_REG(s8 val) -{ - return val * 1000; -} - -/* VID: mV - REG: (see doc/vid) */ -static inline int VID_FROM_REG(u8 val) -{ - return val==0x1f ? 0 : val>=0x10 ? 5100-val*100 : 2050-val*50; -} - -#define DIV_FROM_REG(val) (1 << (val)) - -/* There are some complications in a module like this. First off, LM78 chips - may be both present on the SMBus and the ISA bus, and we have to handle - those cases separately at some places. Second, there might be several - LM78 chips available (well, actually, that is probably never done; but - it is a clean illustration of how to handle a case like that). Finally, - a specific chip may be attached to *both* ISA and SMBus, and we would - not like to detect it double. Fortunately, in the case of the LM78 at - least, a register tells us what SMBus address we are on, so that helps - a bit - except if there could be more than one SMBus. Groan. No solution - for this yet. */ - -/* This module may seem overly long and complicated. In fact, it is not so - bad. Quite a lot of bookkeeping is done. A real driver can often cut - some corners. */ - -/* For each registered LM78, we need to keep some data in memory. That - data is pointed to by lm78_list[NR]->data. The structure itself is - dynamically allocated, at the same time when a new lm78 client is - allocated. */ -struct lm78_data { - struct i2c_client client; - struct semaphore lock; - enum chips type; - - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - u8 in[7]; /* Register value */ - u8 in_max[7]; /* Register value */ - u8 in_min[7]; /* Register value */ - u8 fan[3]; /* Register value */ - u8 fan_min[3]; /* Register value */ - s8 temp; /* Register value */ - s8 temp_over; /* Register value */ - s8 temp_hyst; /* Register value */ - u8 fan_div[3]; /* Register encoding, shifted right */ - u8 vid; /* Register encoding, combined */ - u16 alarms; /* Register encoding, combined */ -}; - - -static int lm78_attach_adapter(struct i2c_adapter *adapter); -static int lm78_detect(struct i2c_adapter *adapter, int address, int kind); -static int lm78_detach_client(struct i2c_client *client); - -static int lm78_read_value(struct i2c_client *client, u8 register); -static int lm78_write_value(struct i2c_client *client, u8 register, u8 value); -static struct lm78_data *lm78_update_device(struct device *dev); -static void lm78_init_client(struct i2c_client *client); - - -static struct i2c_driver lm78_driver = { - .owner = THIS_MODULE, - .name = "lm78", - .id = I2C_DRIVERID_LM78, - .flags = I2C_DF_NOTIFY, - .attach_adapter = lm78_attach_adapter, - .detach_client = lm78_detach_client, -}; - -/* 7 Voltages */ -static ssize_t show_in(struct device *dev, char *buf, int nr) -{ - struct lm78_data *data = lm78_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); -} - -static ssize_t show_in_min(struct device *dev, char *buf, int nr) -{ - struct lm78_data *data = lm78_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); -} - -static ssize_t show_in_max(struct device *dev, char *buf, int nr) -{ - struct lm78_data *data = lm78_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); -} - -static ssize_t set_in_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val); - lm78_write_value(client, LM78_REG_IN_MIN(nr), data->in_min[nr]); - up(&data->update_lock); - return count; -} - -static ssize_t set_in_max(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val); - lm78_write_value(client, LM78_REG_IN_MAX(nr), data->in_max[nr]); - up(&data->update_lock); - return count; -} - -#define show_in_offset(offset) \ -static ssize_t \ - show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in(dev, buf, offset); \ -} \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ - show_in##offset, NULL); \ -static ssize_t \ - show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in_min(dev, buf, offset); \ -} \ -static ssize_t \ - show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in_max(dev, buf, offset); \ -} \ -static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_in_min(dev, buf, count, offset); \ -} \ -static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_in_max(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ - show_in##offset##_min, set_in##offset##_min); \ -static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ - show_in##offset##_max, set_in##offset##_max); - -show_in_offset(0); -show_in_offset(1); -show_in_offset(2); -show_in_offset(3); -show_in_offset(4); -show_in_offset(5); -show_in_offset(6); - -/* Temperature */ -static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm78_data *data = lm78_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp)); -} - -static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm78_data *data = lm78_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over)); -} - -static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_over = TEMP_TO_REG(val); - lm78_write_value(client, LM78_REG_TEMP_OVER, data->temp_over); - up(&data->update_lock); - return count; -} - -static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm78_data *data = lm78_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst)); -} - -static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_hyst = TEMP_TO_REG(val); - lm78_write_value(client, LM78_REG_TEMP_HYST, data->temp_hyst); - up(&data->update_lock); - return count; -} - -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); -static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, - show_temp_over, set_temp_over); -static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, - show_temp_hyst, set_temp_hyst); - -/* 3 Fans */ -static ssize_t show_fan(struct device *dev, char *buf, int nr) -{ - struct lm78_data *data = lm78_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], - DIV_FROM_REG(data->fan_div[nr])) ); -} - -static ssize_t show_fan_min(struct device *dev, char *buf, int nr) -{ - struct lm78_data *data = lm78_update_device(dev); - return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr])) ); -} - -static ssize_t set_fan_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); - lm78_write_value(client, LM78_REG_FAN_MIN(nr), data->fan_min[nr]); - up(&data->update_lock); - return count; -} - -static ssize_t show_fan_div(struct device *dev, char *buf, int nr) -{ - struct lm78_data *data = lm78_update_device(dev); - return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) ); -} - -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least suprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ -static ssize_t set_fan_div(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - unsigned long min; - u8 reg; - - down(&data->update_lock); - min = FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr])); - - switch (val) { - case 1: data->fan_div[nr] = 0; break; - case 2: data->fan_div[nr] = 1; break; - case 4: data->fan_div[nr] = 2; break; - case 8: data->fan_div[nr] = 3; break; - default: - dev_err(&client->dev, "fan_div value %ld not " - "supported. Choose one of 1, 2, 4 or 8!\n", val); - up(&data->update_lock); - return -EINVAL; - } - - reg = lm78_read_value(client, LM78_REG_VID_FANDIV); - switch (nr) { - case 0: - reg = (reg & 0xcf) | (data->fan_div[nr] << 4); - break; - case 1: - reg = (reg & 0x3f) | (data->fan_div[nr] << 6); - break; - } - lm78_write_value(client, LM78_REG_VID_FANDIV, reg); - - data->fan_min[nr] = - FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); - lm78_write_value(client, LM78_REG_FAN_MIN(nr), data->fan_min[nr]); - up(&data->update_lock); - - return count; -} - -#define show_fan_offset(offset) \ -static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_min(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_div(dev, buf, offset - 1); \ -} \ -static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_fan_min(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan_##offset##_min, set_fan_##offset##_min); - -static ssize_t set_fan_1_div(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - return set_fan_div(dev, buf, count, 0) ; -} - -static ssize_t set_fan_2_div(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - return set_fan_div(dev, buf, count, 1) ; -} - -show_fan_offset(1); -show_fan_offset(2); -show_fan_offset(3); - -/* Fan 3 divisor is locked in H/W */ -static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, - show_fan_1_div, set_fan_1_div); -static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, - show_fan_2_div, set_fan_2_div); -static DEVICE_ATTR(fan3_div, S_IRUGO, show_fan_3_div, NULL); - -/* VID */ -static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm78_data *data = lm78_update_device(dev); - return sprintf(buf, "%d\n", VID_FROM_REG(data->vid)); -} -static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); - -/* Alarms */ -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm78_data *data = lm78_update_device(dev); - return sprintf(buf, "%u\n", data->alarms); -} -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -/* This function is called when: - * lm78_driver is inserted (when this module is loaded), for each - available adapter - * when a new adapter is inserted (and lm78_driver is still present) */ -static int lm78_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, lm78_detect); -} - -/* This function is called by i2c_detect */ -int lm78_detect(struct i2c_adapter *adapter, int address, int kind) -{ - int i, err; - struct i2c_client *new_client; - struct lm78_data *data; - const char *client_name = ""; - int is_isa = i2c_is_isa_adapter(adapter); - - if (!is_isa && - !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - err = -ENODEV; - goto ERROR0; - } - - /* Reserve the ISA region */ - if (is_isa) - if (!request_region(address, LM78_EXTENT, lm78_driver.name)) { - err = -EBUSY; - goto ERROR0; - } - - /* Probe whether there is anything available on this address. Already - done for SMBus clients */ - if (kind < 0) { - if (is_isa) { - -#define REALLY_SLOW_IO - /* We need the timeouts for at least some LM78-like - chips. But only if we read 'undefined' registers. */ - i = inb_p(address + 1); - if (inb_p(address + 2) != i) { - err = -ENODEV; - goto ERROR1; - } - if (inb_p(address + 3) != i) { - err = -ENODEV; - goto ERROR1; - } - if (inb_p(address + 7) != i) { - err = -ENODEV; - goto ERROR1; - } -#undef REALLY_SLOW_IO - - /* Let's just hope nothing breaks here */ - i = inb_p(address + 5) & 0x7f; - outb_p(~i & 0x7f, address + 5); - if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { - outb_p(i, address + 5); - err = -ENODEV; - goto ERROR1; - } - } - } - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access lm78_{read,write}_value. */ - - if (!(data = kmalloc(sizeof(struct lm78_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR1; - } - memset(data, 0, sizeof(struct lm78_data)); - - new_client = &data->client; - if (is_isa) - init_MUTEX(&data->lock); - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm78_driver; - new_client->flags = 0; - - /* Now, we do the remaining detection. */ - if (kind < 0) { - if (lm78_read_value(new_client, LM78_REG_CONFIG) & 0x80) { - err = -ENODEV; - goto ERROR2; - } - if (!is_isa && (lm78_read_value( - new_client, LM78_REG_I2C_ADDR) != address)) { - err = -ENODEV; - goto ERROR2; - } - } - - /* Determine the chip type. */ - if (kind <= 0) { - i = lm78_read_value(new_client, LM78_REG_CHIPID); - if (i == 0x00 || i == 0x20) - kind = lm78; - else if (i == 0x40) - kind = lm78j; - 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); - err = -ENODEV; - goto ERROR2; - } - } - - if (kind == lm78) { - client_name = "lm78"; - } else if (kind == lm78j) { - client_name = "lm78-j"; - } else if (kind == lm79) { - client_name = "lm79"; - } - - /* Fill in the remaining client fields and put into the global list */ - strlcpy(new_client->name, client_name, I2C_NAME_SIZE); - data->type = kind; - - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto ERROR2; - - /* Initialize the LM78 chip */ - lm78_init_client(new_client); - - /* A few vars need to be filled upon startup */ - for (i = 0; i < 3; i++) { - data->fan_min[i] = lm78_read_value(new_client, - LM78_REG_FAN_MIN(i)); - } - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in4_max); - device_create_file(&new_client->dev, &dev_attr_in5_input); - device_create_file(&new_client->dev, &dev_attr_in5_min); - device_create_file(&new_client->dev, &dev_attr_in5_max); - device_create_file(&new_client->dev, &dev_attr_in6_input); - device_create_file(&new_client->dev, &dev_attr_in6_min); - device_create_file(&new_client->dev, &dev_attr_in6_max); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_fan3_input); - device_create_file(&new_client->dev, &dev_attr_fan3_min); - device_create_file(&new_client->dev, &dev_attr_fan3_div); - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - - return 0; - -ERROR2: - kfree(data); -ERROR1: - if (is_isa) - release_region(address, LM78_EXTENT); -ERROR0: - return err; -} - -static int lm78_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, - "Client deregistration failed, client not detached.\n"); - return err; - } - - if(i2c_is_isa_client(client)) - release_region(client->addr, LM78_EXTENT); - - kfree(i2c_get_clientdata(client)); - - return 0; -} - -/* The SMBus locks itself, but ISA access must be locked explicitly! - We don't want to lock the whole ISA bus, so we lock each client - separately. - We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, - would slow down the LM78 access and should not be necessary. */ -static int lm78_read_value(struct i2c_client *client, u8 reg) -{ - int res; - if (i2c_is_isa_client(client)) { - struct lm78_data *data = i2c_get_clientdata(client); - down(&data->lock); - outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); - res = inb_p(client->addr + LM78_DATA_REG_OFFSET); - up(&data->lock); - return res; - } else - return i2c_smbus_read_byte_data(client, reg); -} - -/* The SMBus locks itself, but ISA access muse be locked explicitly! - We don't want to lock the whole ISA bus, so we lock each client - separately. - We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, - would slow down the LM78 access and should not be necessary. - There are some ugly typecasts here, but the good new is - they should - nowhere else be necessary! */ -static int lm78_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - if (i2c_is_isa_client(client)) { - struct lm78_data *data = i2c_get_clientdata(client); - down(&data->lock); - outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); - outb_p(value, client->addr + LM78_DATA_REG_OFFSET); - up(&data->lock); - return 0; - } else - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Called when we have found a new LM78. It should set limits, etc. */ -static void lm78_init_client(struct i2c_client *client) -{ - u8 config = lm78_read_value(client, LM78_REG_CONFIG); - - /* Start monitoring */ - if (!(config & 0x01)) - lm78_write_value(client, LM78_REG_CONFIG, - (config & 0xf7) | 0x01); -} - -static struct lm78_data *lm78_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm78_data *data = i2c_get_clientdata(client); - int i; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - - dev_dbg(&client->dev, "Starting lm78 update\n"); - - for (i = 0; i <= 6; i++) { - data->in[i] = - lm78_read_value(client, LM78_REG_IN(i)); - data->in_min[i] = - lm78_read_value(client, LM78_REG_IN_MIN(i)); - data->in_max[i] = - lm78_read_value(client, LM78_REG_IN_MAX(i)); - } - for (i = 0; i < 3; i++) { - data->fan[i] = - lm78_read_value(client, LM78_REG_FAN(i)); - data->fan_min[i] = - lm78_read_value(client, LM78_REG_FAN_MIN(i)); - } - data->temp = lm78_read_value(client, LM78_REG_TEMP); - data->temp_over = - lm78_read_value(client, LM78_REG_TEMP_OVER); - data->temp_hyst = - lm78_read_value(client, LM78_REG_TEMP_HYST); - i = lm78_read_value(client, LM78_REG_VID_FANDIV); - data->vid = i & 0x0f; - if (data->type == lm79) - data->vid |= - (lm78_read_value(client, LM78_REG_CHIPID) & - 0x01) << 4; - else - data->vid |= 0x10; - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = i >> 6; - data->alarms = lm78_read_value(client, LM78_REG_ALARM1) + - (lm78_read_value(client, LM78_REG_ALARM2) << 8); - data->last_updated = jiffies; - data->valid = 1; - - data->fan_div[2] = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sm_lm78_init(void) -{ - return i2c_add_driver(&lm78_driver); -} - -static void __exit sm_lm78_exit(void) -{ - i2c_del_driver(&lm78_driver); -} - - - -MODULE_AUTHOR("Frodo Looijaard "); -MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver"); -MODULE_LICENSE("GPL"); - -module_init(sm_lm78_init); -module_exit(sm_lm78_exit); diff --git a/drivers/i2c/chips/lm80.c b/drivers/i2c/chips/lm80.c deleted file mode 100644 index 8100595feb44..000000000000 --- a/drivers/i2c/chips/lm80.c +++ /dev/null @@ -1,601 +0,0 @@ -/* - * lm80.c - From lm_sensors, Linux kernel modules for hardware - * monitoring - * Copyright (C) 1998, 1999 Frodo Looijaard - * and Philip Edelbrock - * - * Ported to Linux 2.6 by Tiago Sousa - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, - 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_1(lm80); - -/* Many LM80 constants specified below */ - -/* The LM80 registers */ -#define LM80_REG_IN_MAX(nr) (0x2a + (nr) * 2) -#define LM80_REG_IN_MIN(nr) (0x2b + (nr) * 2) -#define LM80_REG_IN(nr) (0x20 + (nr)) - -#define LM80_REG_FAN1 0x28 -#define LM80_REG_FAN2 0x29 -#define LM80_REG_FAN_MIN(nr) (0x3b + (nr)) - -#define LM80_REG_TEMP 0x27 -#define LM80_REG_TEMP_HOT_MAX 0x38 -#define LM80_REG_TEMP_HOT_HYST 0x39 -#define LM80_REG_TEMP_OS_MAX 0x3a -#define LM80_REG_TEMP_OS_HYST 0x3b - -#define LM80_REG_CONFIG 0x00 -#define LM80_REG_ALARM1 0x01 -#define LM80_REG_ALARM2 0x02 -#define LM80_REG_MASK1 0x03 -#define LM80_REG_MASK2 0x04 -#define LM80_REG_FANDIV 0x05 -#define LM80_REG_RES 0x06 - - -/* Conversions. Rounding and limit checking is only done on the TO_REG - variants. Note that you should be a bit careful with which arguments - these macros are called: arguments may be evaluated more than once. - Fixing this is just not worth it. */ - -#define IN_TO_REG(val) (SENSORS_LIMIT(((val)+5)/10,0,255)) -#define IN_FROM_REG(val) ((val)*10) - -static inline unsigned char FAN_TO_REG(unsigned rpm, unsigned div) -{ - if (rpm == 0) - return 255; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm*div / 2) / (rpm*div), 1, 254); -} - -#define FAN_FROM_REG(val,div) ((val)==0?-1:\ - (val)==255?0:1350000/((div)*(val))) - -static inline long TEMP_FROM_REG(u16 temp) -{ - long res; - - temp >>= 4; - if (temp < 0x0800) - res = 625 * (long) temp; - else - res = ((long) temp - 0x01000) * 625; - - return res / 10; -} - -#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*1000) - -#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val)<0?\ - ((val)-500)/1000:((val)+500)/1000,0,255) - -#define DIV_FROM_REG(val) (1 << (val)) - -/* - * Client data (each client gets its own) - */ - -struct lm80_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - u8 in[7]; /* Register value */ - u8 in_max[7]; /* Register value */ - u8 in_min[7]; /* Register value */ - u8 fan[2]; /* Register value */ - u8 fan_min[2]; /* Register value */ - u8 fan_div[2]; /* Register encoding, shifted right */ - u16 temp; /* Register values, shifted right */ - u8 temp_hot_max; /* Register value */ - u8 temp_hot_hyst; /* Register value */ - u8 temp_os_max; /* Register value */ - u8 temp_os_hyst; /* Register value */ - u16 alarms; /* Register encoding, combined */ -}; - -/* - * Functions declaration - */ - -static int lm80_attach_adapter(struct i2c_adapter *adapter); -static int lm80_detect(struct i2c_adapter *adapter, int address, int kind); -static void lm80_init_client(struct i2c_client *client); -static int lm80_detach_client(struct i2c_client *client); -static struct lm80_data *lm80_update_device(struct device *dev); -static int lm80_read_value(struct i2c_client *client, u8 reg); -static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value); - -/* - * Driver data (common to all clients) - */ - -static struct i2c_driver lm80_driver = { - .owner = THIS_MODULE, - .name = "lm80", - .id = I2C_DRIVERID_LM80, - .flags = I2C_DF_NOTIFY, - .attach_adapter = lm80_attach_adapter, - .detach_client = lm80_detach_client, -}; - -/* - * Sysfs stuff - */ - -#define show_in(suffix, value) \ -static ssize_t show_in_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm80_data *data = lm80_update_device(dev); \ - return sprintf(buf, "%d\n", IN_FROM_REG(data->value)); \ -} -show_in(min0, in_min[0]); -show_in(min1, in_min[1]); -show_in(min2, in_min[2]); -show_in(min3, in_min[3]); -show_in(min4, in_min[4]); -show_in(min5, in_min[5]); -show_in(min6, in_min[6]); -show_in(max0, in_max[0]); -show_in(max1, in_max[1]); -show_in(max2, in_max[2]); -show_in(max3, in_max[3]); -show_in(max4, in_max[4]); -show_in(max5, in_max[5]); -show_in(max6, in_max[6]); -show_in(input0, in[0]); -show_in(input1, in[1]); -show_in(input2, in[2]); -show_in(input3, in[3]); -show_in(input4, in[4]); -show_in(input5, in[5]); -show_in(input6, in[6]); - -#define set_in(suffix, value, reg) \ -static ssize_t set_in_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm80_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock);\ - data->value = IN_TO_REG(val); \ - lm80_write_value(client, reg, data->value); \ - up(&data->update_lock);\ - return count; \ -} -set_in(min0, in_min[0], LM80_REG_IN_MIN(0)); -set_in(min1, in_min[1], LM80_REG_IN_MIN(1)); -set_in(min2, in_min[2], LM80_REG_IN_MIN(2)); -set_in(min3, in_min[3], LM80_REG_IN_MIN(3)); -set_in(min4, in_min[4], LM80_REG_IN_MIN(4)); -set_in(min5, in_min[5], LM80_REG_IN_MIN(5)); -set_in(min6, in_min[6], LM80_REG_IN_MIN(6)); -set_in(max0, in_max[0], LM80_REG_IN_MAX(0)); -set_in(max1, in_max[1], LM80_REG_IN_MAX(1)); -set_in(max2, in_max[2], LM80_REG_IN_MAX(2)); -set_in(max3, in_max[3], LM80_REG_IN_MAX(3)); -set_in(max4, in_max[4], LM80_REG_IN_MAX(4)); -set_in(max5, in_max[5], LM80_REG_IN_MAX(5)); -set_in(max6, in_max[6], LM80_REG_IN_MAX(6)); - -#define show_fan(suffix, value, div) \ -static ssize_t show_fan_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm80_data *data = lm80_update_device(dev); \ - return sprintf(buf, "%d\n", FAN_FROM_REG(data->value, \ - DIV_FROM_REG(data->div))); \ -} -show_fan(min1, fan_min[0], fan_div[0]); -show_fan(min2, fan_min[1], fan_div[1]); -show_fan(input1, fan[0], fan_div[0]); -show_fan(input2, fan[1], fan_div[1]); - -#define show_fan_div(suffix, value) \ -static ssize_t show_fan_div##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm80_data *data = lm80_update_device(dev); \ - return sprintf(buf, "%d\n", DIV_FROM_REG(data->value)); \ -} -show_fan_div(1, fan_div[0]); -show_fan_div(2, fan_div[1]); - -#define set_fan(suffix, value, reg, div) \ -static ssize_t set_fan_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm80_data *data = i2c_get_clientdata(client); \ - long val = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock);\ - data->value = FAN_TO_REG(val, DIV_FROM_REG(data->div)); \ - lm80_write_value(client, reg, data->value); \ - up(&data->update_lock);\ - return count; \ -} -set_fan(min1, fan_min[0], LM80_REG_FAN_MIN(1), fan_div[0]); -set_fan(min2, fan_min[1], LM80_REG_FAN_MIN(2), fan_div[1]); - -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least suprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ -static ssize_t set_fan_div(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm80_data *data = i2c_get_clientdata(client); - unsigned long min, val = simple_strtoul(buf, NULL, 10); - u8 reg; - - /* Save fan_min */ - down(&data->update_lock); - min = FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr])); - - switch (val) { - case 1: data->fan_div[nr] = 0; break; - case 2: data->fan_div[nr] = 1; break; - case 4: data->fan_div[nr] = 2; break; - case 8: data->fan_div[nr] = 3; break; - default: - dev_err(&client->dev, "fan_div value %ld not " - "supported. Choose one of 1, 2, 4 or 8!\n", val); - up(&data->update_lock); - return -EINVAL; - } - - reg = (lm80_read_value(client, LM80_REG_FANDIV) & ~(3 << (2 * (nr + 1)))) - | (data->fan_div[nr] << (2 * (nr + 1))); - lm80_write_value(client, LM80_REG_FANDIV, reg); - - /* Restore fan_min */ - data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); - lm80_write_value(client, LM80_REG_FAN_MIN(nr + 1), data->fan_min[nr]); - up(&data->update_lock); - - return count; -} - -#define set_fan_div(number) \ -static ssize_t set_fan_div##number(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - return set_fan_div(dev, buf, count, number - 1); \ -} -set_fan_div(1); -set_fan_div(2); - -static ssize_t show_temp_input1(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm80_data *data = lm80_update_device(dev); - return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp)); -} - -#define show_temp(suffix, value) \ -static ssize_t show_temp_##suffix(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm80_data *data = lm80_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_LIMIT_FROM_REG(data->value)); \ -} -show_temp(hot_max, temp_hot_max); -show_temp(hot_hyst, temp_hot_hyst); -show_temp(os_max, temp_os_max); -show_temp(os_hyst, temp_os_hyst); - -#define set_temp(suffix, value, reg) \ -static ssize_t set_temp_##suffix(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm80_data *data = i2c_get_clientdata(client); \ - long val = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->value = TEMP_LIMIT_TO_REG(val); \ - lm80_write_value(client, reg, data->value); \ - up(&data->update_lock); \ - return count; \ -} -set_temp(hot_max, temp_hot_max, LM80_REG_TEMP_HOT_MAX); -set_temp(hot_hyst, temp_hot_hyst, LM80_REG_TEMP_HOT_HYST); -set_temp(os_max, temp_os_max, LM80_REG_TEMP_OS_MAX); -set_temp(os_hyst, temp_os_hyst, LM80_REG_TEMP_OS_HYST); - -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm80_data *data = lm80_update_device(dev); - return sprintf(buf, "%u\n", data->alarms); -} - -static DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min0, set_in_min0); -static DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min1, set_in_min1); -static DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min2, set_in_min2); -static DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min3, set_in_min3); -static DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min4, set_in_min4); -static DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min5, set_in_min5); -static DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min6, set_in_min6); -static DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max0, set_in_max0); -static DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max1, set_in_max1); -static DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max2, set_in_max2); -static DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max3, set_in_max3); -static DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max4, set_in_max4); -static DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max5, set_in_max5); -static DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max6, set_in_max6); -static DEVICE_ATTR(in0_input, S_IRUGO, show_in_input0, NULL); -static DEVICE_ATTR(in1_input, S_IRUGO, show_in_input1, NULL); -static DEVICE_ATTR(in2_input, S_IRUGO, show_in_input2, NULL); -static DEVICE_ATTR(in3_input, S_IRUGO, show_in_input3, NULL); -static DEVICE_ATTR(in4_input, S_IRUGO, show_in_input4, NULL); -static DEVICE_ATTR(in5_input, S_IRUGO, show_in_input5, NULL); -static DEVICE_ATTR(in6_input, S_IRUGO, show_in_input6, NULL); -static DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min1, - set_fan_min1); -static DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min2, - set_fan_min2); -static DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input1, NULL); -static DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input2, NULL); -static DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div1, set_fan_div1); -static DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div2, set_fan_div2); -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_hot_max, - set_temp_hot_max); -static DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, show_temp_hot_hyst, - set_temp_hot_hyst); -static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp_os_max, - set_temp_os_max); -static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp_os_hyst, - set_temp_os_hyst); -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -/* - * Real code - */ - -static int lm80_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, lm80_detect); -} - -int lm80_detect(struct i2c_adapter *adapter, int address, int kind) -{ - int i, cur; - struct i2c_client *new_client; - struct lm80_data *data; - int err = 0; - const char *name; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access lm80_{read,write}_value. */ - if (!(data = kmalloc(sizeof(struct lm80_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct lm80_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm80_driver; - new_client->flags = 0; - - /* Now, we do the remaining detection. It is lousy. */ - if (lm80_read_value(new_client, LM80_REG_ALARM2) & 0xc0) - goto error_free; - for (i = 0x2a; i <= 0x3d; i++) { - cur = i2c_smbus_read_byte_data(new_client, i); - if ((i2c_smbus_read_byte_data(new_client, i + 0x40) != cur) - || (i2c_smbus_read_byte_data(new_client, i + 0x80) != cur) - || (i2c_smbus_read_byte_data(new_client, i + 0xc0) != cur)) - goto error_free; - } - - /* Determine the chip type - only one kind supported! */ - kind = lm80; - name = "lm80"; - - /* Fill in the remaining client fields and put it into the global list */ - strlcpy(new_client->name, name, I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto error_free; - - /* Initialize the LM80 chip */ - lm80_init_client(new_client); - - /* A few vars need to be filled upon startup */ - data->fan_min[0] = lm80_read_value(new_client, LM80_REG_FAN_MIN(1)); - data->fan_min[1] = lm80_read_value(new_client, LM80_REG_FAN_MIN(2)); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in5_min); - device_create_file(&new_client->dev, &dev_attr_in6_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in4_max); - device_create_file(&new_client->dev, &dev_attr_in5_max); - device_create_file(&new_client->dev, &dev_attr_in6_max); - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in5_input); - device_create_file(&new_client->dev, &dev_attr_in6_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_crit); - device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst); - device_create_file(&new_client->dev, &dev_attr_alarms); - - return 0; - -error_free: - kfree(data); -exit: - return err; -} - -static int lm80_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - -static int lm80_read_value(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static int lm80_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - -/* Called when we have found a new LM80. */ -static void lm80_init_client(struct i2c_client *client) -{ - /* Reset all except Watchdog values and last conversion values - This sets fan-divs to 2, among others. This makes most other - initializations unnecessary */ - lm80_write_value(client, LM80_REG_CONFIG, 0x80); - /* Set 11-bit temperature resolution */ - lm80_write_value(client, LM80_REG_RES, 0x08); - - /* Start monitoring */ - lm80_write_value(client, LM80_REG_CONFIG, 0x01); -} - -static struct lm80_data *lm80_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm80_data *data = i2c_get_clientdata(client); - int i; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + 2 * HZ) || !data->valid) { - dev_dbg(&client->dev, "Starting lm80 update\n"); - for (i = 0; i <= 6; i++) { - data->in[i] = - lm80_read_value(client, LM80_REG_IN(i)); - data->in_min[i] = - lm80_read_value(client, LM80_REG_IN_MIN(i)); - data->in_max[i] = - lm80_read_value(client, LM80_REG_IN_MAX(i)); - } - data->fan[0] = lm80_read_value(client, LM80_REG_FAN1); - data->fan_min[0] = - lm80_read_value(client, LM80_REG_FAN_MIN(1)); - data->fan[1] = lm80_read_value(client, LM80_REG_FAN2); - data->fan_min[1] = - lm80_read_value(client, LM80_REG_FAN_MIN(2)); - - data->temp = - (lm80_read_value(client, LM80_REG_TEMP) << 8) | - (lm80_read_value(client, LM80_REG_RES) & 0xf0); - data->temp_os_max = - lm80_read_value(client, LM80_REG_TEMP_OS_MAX); - data->temp_os_hyst = - lm80_read_value(client, LM80_REG_TEMP_OS_HYST); - data->temp_hot_max = - lm80_read_value(client, LM80_REG_TEMP_HOT_MAX); - data->temp_hot_hyst = - lm80_read_value(client, LM80_REG_TEMP_HOT_HYST); - - i = lm80_read_value(client, LM80_REG_FANDIV); - data->fan_div[0] = (i >> 2) & 0x03; - data->fan_div[1] = (i >> 4) & 0x03; - data->alarms = lm80_read_value(client, LM80_REG_ALARM1) + - (lm80_read_value(client, LM80_REG_ALARM2) << 8); - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_lm80_init(void) -{ - return i2c_add_driver(&lm80_driver); -} - -static void __exit sensors_lm80_exit(void) -{ - i2c_del_driver(&lm80_driver); -} - -MODULE_AUTHOR("Frodo Looijaard and " - "Philip Edelbrock "); -MODULE_DESCRIPTION("LM80 driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_lm80_init); -module_exit(sensors_lm80_exit); diff --git a/drivers/i2c/chips/lm83.c b/drivers/i2c/chips/lm83.c deleted file mode 100644 index a49008b444c8..000000000000 --- a/drivers/i2c/chips/lm83.c +++ /dev/null @@ -1,408 +0,0 @@ -/* - * lm83.c - Part of lm_sensors, Linux kernel modules for hardware - * monitoring - * Copyright (C) 2003-2005 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 - * temperatures (its own plus up to three external ones) with a 1 deg - * resolution and a 3-4 deg accuracy. Complete datasheet can be obtained - * from National's website at: - * http://www.national.com/pf/LM/LM83.html - * Since the datasheet omits to give the chip stepping code, I give it - * here: 0x03 (at register 0xff). - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * Addresses to scan - * Address is selected using 2 three-level pins, resulting in 9 possible - * addresses. - */ - -static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, - 0x29, 0x2a, 0x2b, - 0x4c, 0x4d, 0x4e, - I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* - * Insmod parameters - */ - -SENSORS_INSMOD_1(lm83); - -/* - * The LM83 registers - * Manufacturer ID is 0x01 for National Semiconductor. - */ - -#define LM83_REG_R_MAN_ID 0xFE -#define LM83_REG_R_CHIP_ID 0xFF -#define LM83_REG_R_CONFIG 0x03 -#define LM83_REG_W_CONFIG 0x09 -#define LM83_REG_R_STATUS1 0x02 -#define LM83_REG_R_STATUS2 0x35 -#define LM83_REG_R_LOCAL_TEMP 0x00 -#define LM83_REG_R_LOCAL_HIGH 0x05 -#define LM83_REG_W_LOCAL_HIGH 0x0B -#define LM83_REG_R_REMOTE1_TEMP 0x30 -#define LM83_REG_R_REMOTE1_HIGH 0x38 -#define LM83_REG_W_REMOTE1_HIGH 0x50 -#define LM83_REG_R_REMOTE2_TEMP 0x01 -#define LM83_REG_R_REMOTE2_HIGH 0x07 -#define LM83_REG_W_REMOTE2_HIGH 0x0D -#define LM83_REG_R_REMOTE3_TEMP 0x31 -#define LM83_REG_R_REMOTE3_HIGH 0x3A -#define LM83_REG_W_REMOTE3_HIGH 0x52 -#define LM83_REG_R_TCRIT 0x42 -#define LM83_REG_W_TCRIT 0x5A - -/* - * Conversions and various macros - * The LM83 uses signed 8-bit values with LSB = 1 degree Celsius. - */ - -#define TEMP_FROM_REG(val) ((val) * 1000) -#define TEMP_TO_REG(val) ((val) <= -128000 ? -128 : \ - (val) >= 127000 ? 127 : \ - (val) < 0 ? ((val) - 500) / 1000 : \ - ((val) + 500) / 1000) - -static const u8 LM83_REG_R_TEMP[] = { - LM83_REG_R_LOCAL_TEMP, - LM83_REG_R_REMOTE1_TEMP, - LM83_REG_R_REMOTE2_TEMP, - LM83_REG_R_REMOTE3_TEMP, - LM83_REG_R_LOCAL_HIGH, - LM83_REG_R_REMOTE1_HIGH, - LM83_REG_R_REMOTE2_HIGH, - LM83_REG_R_REMOTE3_HIGH, - LM83_REG_R_TCRIT, -}; - -static const u8 LM83_REG_W_HIGH[] = { - LM83_REG_W_LOCAL_HIGH, - LM83_REG_W_REMOTE1_HIGH, - LM83_REG_W_REMOTE2_HIGH, - LM83_REG_W_REMOTE3_HIGH, - LM83_REG_W_TCRIT, -}; - -/* - * Functions declaration - */ - -static int lm83_attach_adapter(struct i2c_adapter *adapter); -static int lm83_detect(struct i2c_adapter *adapter, int address, int kind); -static int lm83_detach_client(struct i2c_client *client); -static struct lm83_data *lm83_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static struct i2c_driver lm83_driver = { - .owner = THIS_MODULE, - .name = "lm83", - .id = I2C_DRIVERID_LM83, - .flags = I2C_DF_NOTIFY, - .attach_adapter = lm83_attach_adapter, - .detach_client = lm83_detach_client, -}; - -/* - * Client data (each client gets its own) - */ - -struct lm83_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* registers values */ - s8 temp[9]; /* 0..3: input 1-4, - 4..7: high limit 1-4, - 8 : critical limit */ - u16 alarms; /* bitvector, combined */ -}; - -/* - * Sysfs stuff - */ - -static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm83_data *data = lm83_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[attr->index])); -} - -static ssize_t set_temp(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct lm83_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - int nr = attr->index; - - down(&data->update_lock); - data->temp[nr] = TEMP_TO_REG(val); - i2c_smbus_write_byte_data(client, LM83_REG_W_HIGH[nr - 4], - data->temp[nr]); - up(&data->update_lock); - return count; -} - -static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, - char *buf) -{ - struct lm83_data *data = lm83_update_device(dev); - return sprintf(buf, "%d\n", data->alarms); -} - -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); -static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); -static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); -static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, - set_temp, 4); -static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp, - set_temp, 5); -static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_temp, - set_temp, 6); -static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_temp, - set_temp, 7); -static SENSOR_DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp, NULL, 8); -static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp, NULL, 8); -static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp, - set_temp, 8); -static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp, NULL, 8); -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -/* - * Real code - */ - -static int lm83_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, lm83_detect); -} - -/* - * The following function does more than just detection. If detection - * succeeds, it also registers the new chip. - */ -static int lm83_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct lm83_data *data; - int err = 0; - const char *name = ""; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - if (!(data = kmalloc(sizeof(struct lm83_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct lm83_data)); - - /* The common I2C client data is placed right after the - * LM83-specific data. */ - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm83_driver; - new_client->flags = 0; - - /* 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", address); - goto exit_free; - } - } - - 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; - } - } - - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X).\n", man_id, chip_id); - goto exit_free; - } - } - - if (kind == lm83) { - name = "lm83"; - } - - /* We can fill in the remaining client fields */ - strlcpy(new_client->name, name, I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* - * Initialize the LM83 chip - * (Nothing to do for this one.) - */ - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp4_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp4_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_crit.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_crit.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp3_crit.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp4_crit.dev_attr); - device_create_file(&new_client->dev, &dev_attr_alarms); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static int lm83_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, - "Client deregistration failed, client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - -static struct lm83_data *lm83_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm83_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - int nr; - - dev_dbg(&client->dev, "Updating lm83 data.\n"); - for (nr = 0; nr < 9; nr++) { - data->temp[nr] = - i2c_smbus_read_byte_data(client, - LM83_REG_R_TEMP[nr]); - } - data->alarms = - i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS1) - + (i2c_smbus_read_byte_data(client, LM83_REG_R_STATUS2) - << 8); - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_lm83_init(void) -{ - return i2c_add_driver(&lm83_driver); -} - -static void __exit sensors_lm83_exit(void) -{ - i2c_del_driver(&lm83_driver); -} - -MODULE_AUTHOR("Jean Delvare "); -MODULE_DESCRIPTION("LM83 driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_lm83_init); -module_exit(sensors_lm83_exit); diff --git a/drivers/i2c/chips/lm85.c b/drivers/i2c/chips/lm85.c deleted file mode 100644 index b4d7fd418264..000000000000 --- a/drivers/i2c/chips/lm85.c +++ /dev/null @@ -1,1575 +0,0 @@ -/* - lm85.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998, 1999 Frodo Looijaard - Copyright (c) 2002, 2003 Philip Pokorny - Copyright (c) 2003 Margit Schubert-While - Copyright (c) 2004 Justin Thiessen - - Chip details at - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102); - -/* The LM85 registers */ - -#define LM85_REG_IN(nr) (0x20 + (nr)) -#define LM85_REG_IN_MIN(nr) (0x44 + (nr) * 2) -#define LM85_REG_IN_MAX(nr) (0x45 + (nr) * 2) - -#define LM85_REG_TEMP(nr) (0x25 + (nr)) -#define LM85_REG_TEMP_MIN(nr) (0x4e + (nr) * 2) -#define LM85_REG_TEMP_MAX(nr) (0x4f + (nr) * 2) - -/* Fan speeds are LSB, MSB (2 bytes) */ -#define LM85_REG_FAN(nr) (0x28 + (nr) *2) -#define LM85_REG_FAN_MIN(nr) (0x54 + (nr) *2) - -#define LM85_REG_PWM(nr) (0x30 + (nr)) - -#define ADT7463_REG_OPPOINT(nr) (0x33 + (nr)) - -#define ADT7463_REG_TMIN_CTL1 0x36 -#define ADT7463_REG_TMIN_CTL2 0x37 - -#define LM85_REG_DEVICE 0x3d -#define LM85_REG_COMPANY 0x3e -#define LM85_REG_VERSTEP 0x3f -/* These are the recognized values for the above regs */ -#define LM85_DEVICE_ADX 0x27 -#define LM85_COMPANY_NATIONAL 0x01 -#define LM85_COMPANY_ANALOG_DEV 0x41 -#define LM85_COMPANY_SMSC 0x5c -#define LM85_VERSTEP_VMASK 0xf0 -#define LM85_VERSTEP_GENERIC 0x60 -#define LM85_VERSTEP_LM85C 0x60 -#define LM85_VERSTEP_LM85B 0x62 -#define LM85_VERSTEP_ADM1027 0x60 -#define LM85_VERSTEP_ADT7463 0x62 -#define LM85_VERSTEP_ADT7463C 0x6A -#define LM85_VERSTEP_EMC6D100_A0 0x60 -#define LM85_VERSTEP_EMC6D100_A1 0x61 -#define LM85_VERSTEP_EMC6D102 0x65 - -#define LM85_REG_CONFIG 0x40 - -#define LM85_REG_ALARM1 0x41 -#define LM85_REG_ALARM2 0x42 - -#define LM85_REG_VID 0x43 - -/* Automated FAN control */ -#define LM85_REG_AFAN_CONFIG(nr) (0x5c + (nr)) -#define LM85_REG_AFAN_RANGE(nr) (0x5f + (nr)) -#define LM85_REG_AFAN_SPIKE1 0x62 -#define LM85_REG_AFAN_SPIKE2 0x63 -#define LM85_REG_AFAN_MINPWM(nr) (0x64 + (nr)) -#define LM85_REG_AFAN_LIMIT(nr) (0x67 + (nr)) -#define LM85_REG_AFAN_CRITICAL(nr) (0x6a + (nr)) -#define LM85_REG_AFAN_HYST1 0x6d -#define LM85_REG_AFAN_HYST2 0x6e - -#define LM85_REG_TACH_MODE 0x74 -#define LM85_REG_SPINUP_CTL 0x75 - -#define ADM1027_REG_TEMP_OFFSET(nr) (0x70 + (nr)) -#define ADM1027_REG_CONFIG2 0x73 -#define ADM1027_REG_INTMASK1 0x74 -#define ADM1027_REG_INTMASK2 0x75 -#define ADM1027_REG_EXTEND_ADC1 0x76 -#define ADM1027_REG_EXTEND_ADC2 0x77 -#define ADM1027_REG_CONFIG3 0x78 -#define ADM1027_REG_FAN_PPR 0x7b - -#define ADT7463_REG_THERM 0x79 -#define ADT7463_REG_THERM_LIMIT 0x7A - -#define EMC6D100_REG_ALARM3 0x7d -/* IN5, IN6 and IN7 */ -#define EMC6D100_REG_IN(nr) (0x70 + ((nr)-5)) -#define EMC6D100_REG_IN_MIN(nr) (0x73 + ((nr)-5) * 2) -#define EMC6D100_REG_IN_MAX(nr) (0x74 + ((nr)-5) * 2) -#define EMC6D102_REG_EXTEND_ADC1 0x85 -#define EMC6D102_REG_EXTEND_ADC2 0x86 -#define EMC6D102_REG_EXTEND_ADC3 0x87 -#define EMC6D102_REG_EXTEND_ADC4 0x88 - -#define LM85_ALARM_IN0 0x0001 -#define LM85_ALARM_IN1 0x0002 -#define LM85_ALARM_IN2 0x0004 -#define LM85_ALARM_IN3 0x0008 -#define LM85_ALARM_TEMP1 0x0010 -#define LM85_ALARM_TEMP2 0x0020 -#define LM85_ALARM_TEMP3 0x0040 -#define LM85_ALARM_ALARM2 0x0080 -#define LM85_ALARM_IN4 0x0100 -#define LM85_ALARM_RESERVED 0x0200 -#define LM85_ALARM_FAN1 0x0400 -#define LM85_ALARM_FAN2 0x0800 -#define LM85_ALARM_FAN3 0x1000 -#define LM85_ALARM_FAN4 0x2000 -#define LM85_ALARM_TEMP1_FAULT 0x4000 -#define LM85_ALARM_TEMP3_FAULT 0x8000 - - -/* Conversions. Rounding and limit checking is only done on the TO_REG - variants. Note that you should be a bit careful with which arguments - these macros are called: arguments may be evaluated more than once. - */ - -/* IN are scaled acording to built-in resistors */ -static int lm85_scaling[] = { /* .001 Volts */ - 2500, 2250, 3300, 5000, 12000, - 3300, 1500, 1800 /*EMC6D100*/ - }; -#define SCALE(val,from,to) (((val)*(to) + ((from)/2))/(from)) - -#define INS_TO_REG(n,val) \ - SENSORS_LIMIT(SCALE(val,lm85_scaling[n],192),0,255) - -#define INSEXT_FROM_REG(n,val,ext,scale) \ - SCALE((val)*(scale) + (ext),192*(scale),lm85_scaling[n]) - -#define INS_FROM_REG(n,val) INSEXT_FROM_REG(n,val,0,1) - -/* FAN speed is measured using 90kHz clock */ -#define FAN_TO_REG(val) (SENSORS_LIMIT( (val)<=0?0: 5400000/(val),0,65534)) -#define FAN_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:5400000/(val)) - -/* Temperature is reported in .001 degC increments */ -#define TEMP_TO_REG(val) \ - SENSORS_LIMIT(SCALE(val,1000,1),-127,127) -#define TEMPEXT_FROM_REG(val,ext,scale) \ - SCALE((val)*scale + (ext),scale,1000) -#define TEMP_FROM_REG(val) \ - TEMPEXT_FROM_REG(val,0,1) - -#define PWM_TO_REG(val) (SENSORS_LIMIT(val,0,255)) -#define PWM_FROM_REG(val) (val) - - -/* ZONEs have the following parameters: - * Limit (low) temp, 1. degC - * Hysteresis (below limit), 1. degC (0-15) - * Range of speed control, .1 degC (2-80) - * Critical (high) temp, 1. degC - * - * FAN PWMs have the following parameters: - * Reference Zone, 1, 2, 3, etc. - * Spinup time, .05 sec - * PWM value at limit/low temp, 1 count - * PWM Frequency, 1. Hz - * PWM is Min or OFF below limit, flag - * Invert PWM output, flag - * - * Some chips filter the temp, others the fan. - * Filter constant (or disabled) .1 seconds - */ - -/* These are the zone temperature range encodings in .001 degree C */ -static int lm85_range_map[] = { - 2000, 2500, 3300, 4000, 5000, 6600, - 8000, 10000, 13300, 16000, 20000, 26600, - 32000, 40000, 53300, 80000 - }; -static int RANGE_TO_REG( int range ) -{ - int i; - - if ( range < lm85_range_map[0] ) { - return 0 ; - } else if ( range > lm85_range_map[15] ) { - return 15 ; - } else { /* find closest match */ - for ( i = 14 ; i >= 0 ; --i ) { - if ( range > lm85_range_map[i] ) { /* range bracketed */ - if ((lm85_range_map[i+1] - range) < - (range - lm85_range_map[i])) { - i++; - break; - } - break; - } - } - } - return( i & 0x0f ); -} -#define RANGE_FROM_REG(val) (lm85_range_map[(val)&0x0f]) - -/* These are the Acoustic Enhancement, or Temperature smoothing encodings - * NOTE: The enable/disable bit is INCLUDED in these encodings as the - * MSB (bit 3, value 8). If the enable bit is 0, the encoded value - * is ignored, or set to 0. - */ -/* These are the PWM frequency encodings */ -static int lm85_freq_map[] = { /* .1 Hz */ - 100, 150, 230, 300, 380, 470, 620, 940 - }; -static int FREQ_TO_REG( int freq ) -{ - int i; - - if( freq >= lm85_freq_map[7] ) { return 7 ; } - for( i = 0 ; i < 7 ; ++i ) - if( freq <= lm85_freq_map[i] ) - break ; - return( i & 0x07 ); -} -#define FREQ_FROM_REG(val) (lm85_freq_map[(val)&0x07]) - -/* Since we can't use strings, I'm abusing these numbers - * to stand in for the following meanings: - * 1 -- PWM responds to Zone 1 - * 2 -- PWM responds to Zone 2 - * 3 -- PWM responds to Zone 3 - * 23 -- PWM responds to the higher temp of Zone 2 or 3 - * 123 -- PWM responds to highest of Zone 1, 2, or 3 - * 0 -- PWM is always at 0% (ie, off) - * -1 -- PWM is always at 100% - * -2 -- PWM responds to manual control - */ - -static int lm85_zone_map[] = { 1, 2, 3, -1, 0, 23, 123, -2 }; -#define ZONE_FROM_REG(val) (lm85_zone_map[((val)>>5)&0x07]) - -static int ZONE_TO_REG( int zone ) -{ - int i; - - for( i = 0 ; i <= 7 ; ++i ) - if( zone == lm85_zone_map[i] ) - break ; - if( i > 7 ) /* Not found. */ - i = 3; /* Always 100% */ - return( (i & 0x07)<<5 ); -} - -#define HYST_TO_REG(val) (SENSORS_LIMIT(((val)+500)/1000,0,15)) -#define HYST_FROM_REG(val) ((val)*1000) - -#define OFFSET_TO_REG(val) (SENSORS_LIMIT((val)/25,-127,127)) -#define OFFSET_FROM_REG(val) ((val)*25) - -#define PPR_MASK(fan) (0x03<<(fan *2)) -#define PPR_TO_REG(val,fan) (SENSORS_LIMIT((val)-1,0,3)<<(fan *2)) -#define PPR_FROM_REG(val,fan) ((((val)>>(fan * 2))&0x03)+1) - -/* i2c-vid.h defines vid_from_reg() */ -#define VID_FROM_REG(val,vrm) (vid_from_reg((val),(vrm))) - -/* Unlike some other drivers we DO NOT set initial limits. Use - * the config file to set limits. Some users have reported - * motherboards shutting down when we set limits in a previous - * version of the driver. - */ - -/* Chip sampling rates - * - * Some sensors are not updated more frequently than once per second - * so it doesn't make sense to read them more often than that. - * We cache the results and return the saved data if the driver - * is called again before a second has elapsed. - * - * Also, there is significant configuration data for this chip - * given the automatic PWM fan control that is possible. There - * are about 47 bytes of config data to only 22 bytes of actual - * readings. So, we keep the config data up to date in the cache - * when it is written and only sample it once every 1 *minute* - */ -#define LM85_DATA_INTERVAL (HZ + HZ / 2) -#define LM85_CONFIG_INTERVAL (1 * 60 * HZ) - -/* For each registered LM85, we need to keep some data in memory. That - data is pointed to by lm85_list[NR]->data. The structure itself is - dynamically allocated, at the same time when a new lm85 client is - allocated. */ - -/* LM85 can automatically adjust fan speeds based on temperature - * This structure encapsulates an entire Zone config. There are - * three zones (one for each temperature input) on the lm85 - */ -struct lm85_zone { - s8 limit; /* Low temp limit */ - u8 hyst; /* Low limit hysteresis. (0-15) */ - u8 range; /* Temp range, encoded */ - s8 critical; /* "All fans ON" temp limit */ - u8 off_desired; /* Actual "off" temperature specified. Preserved - * to prevent "drift" as other autofan control - * values change. - */ - u8 max_desired; /* Actual "max" temperature specified. Preserved - * to prevent "drift" as other autofan control - * values change. - */ -}; - -struct lm85_autofan { - u8 config; /* Register value */ - u8 freq; /* PWM frequency, encoded */ - u8 min_pwm; /* Minimum PWM value, encoded */ - u8 min_off; /* Min PWM or OFF below "limit", flag */ -}; - -struct lm85_data { - struct i2c_client client; - struct semaphore lock; - enum chips type; - - struct semaphore update_lock; - int valid; /* !=0 if following fields are valid */ - unsigned long last_reading; /* In jiffies */ - unsigned long last_config; /* In jiffies */ - - u8 in[8]; /* Register value */ - u8 in_max[8]; /* Register value */ - u8 in_min[8]; /* Register value */ - s8 temp[3]; /* Register value */ - s8 temp_min[3]; /* Register value */ - s8 temp_max[3]; /* Register value */ - s8 temp_offset[3]; /* Register value */ - u16 fan[4]; /* Register value */ - u16 fan_min[4]; /* Register value */ - u8 pwm[3]; /* Register value */ - u8 spinup_ctl; /* Register encoding, combined */ - u8 tach_mode; /* Register encoding, combined */ - u8 temp_ext[3]; /* Decoded values */ - u8 in_ext[8]; /* Decoded values */ - u8 adc_scale; /* ADC Extended bits scaling factor */ - u8 fan_ppr; /* Register value */ - u8 smooth[3]; /* Register encoding */ - u8 vid; /* Register value */ - u8 vrm; /* VRM version */ - u8 syncpwm3; /* Saved PWM3 for TACH 2,3,4 config */ - u8 oppoint[3]; /* Register value */ - u16 tmin_ctl; /* Register value */ - unsigned long therm_total; /* Cummulative therm count */ - u8 therm_limit; /* Register value */ - u32 alarms; /* Register encoding, combined */ - struct lm85_autofan autofan[3]; - struct lm85_zone zone[3]; -}; - -static int lm85_attach_adapter(struct i2c_adapter *adapter); -static int lm85_detect(struct i2c_adapter *adapter, int address, - int kind); -static int lm85_detach_client(struct i2c_client *client); - -static int lm85_read_value(struct i2c_client *client, u8 register); -static int lm85_write_value(struct i2c_client *client, u8 register, int value); -static struct lm85_data *lm85_update_device(struct device *dev); -static void lm85_init_client(struct i2c_client *client); - - -static struct i2c_driver lm85_driver = { - .owner = THIS_MODULE, - .name = "lm85", - .id = I2C_DRIVERID_LM85, - .flags = I2C_DF_NOTIFY, - .attach_adapter = lm85_attach_adapter, - .detach_client = lm85_detach_client, -}; - - -/* 4 Fans */ -static ssize_t show_fan(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan[nr]) ); -} -static ssize_t show_fan_min(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr]) ); -} -static ssize_t set_fan_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->fan_min[nr] = FAN_TO_REG(val); - lm85_write_value(client, LM85_REG_FAN_MIN(nr), data->fan_min[nr]); - up(&data->update_lock); - return count; -} - -#define show_fan_offset(offset) \ -static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_min(dev, buf, offset - 1); \ -} \ -static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_fan_min(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, \ - NULL); \ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan_##offset##_min, set_fan_##offset##_min); - -show_fan_offset(1); -show_fan_offset(2); -show_fan_offset(3); -show_fan_offset(4); - -/* vid, vrm, alarms */ - -static ssize_t show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); -} - -static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); - -static ssize_t show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->vrm); -} - -static ssize_t store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - data->vrm = val; - return count; -} - -static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); - -static ssize_t show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf, "%u\n", data->alarms); -} - -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); - -/* pwm */ - -static ssize_t show_pwm(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", PWM_FROM_REG(data->pwm[nr]) ); -} -static ssize_t set_pwm(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->pwm[nr] = PWM_TO_REG(val); - lm85_write_value(client, LM85_REG_PWM(nr), data->pwm[nr]); - up(&data->update_lock); - return count; -} -static ssize_t show_pwm_enable(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - int pwm_zone; - - pwm_zone = ZONE_FROM_REG(data->autofan[nr].config); - return sprintf(buf,"%d\n", (pwm_zone != 0 && pwm_zone != -1) ); -} - -#define show_pwm_reg(offset) \ -static ssize_t show_pwm_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_pwm(dev, buf, offset - 1); \ -} \ -static ssize_t set_pwm_##offset (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_pwm(dev, buf, count, offset - 1); \ -} \ -static ssize_t show_pwm_enable##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_pwm_enable(dev, buf, offset - 1); \ -} \ -static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ - show_pwm_##offset, set_pwm_##offset); \ -static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO, \ - show_pwm_enable##offset, NULL); - -show_pwm_reg(1); -show_pwm_reg(2); -show_pwm_reg(3); - -/* Voltages */ - -static ssize_t show_in(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf( buf, "%d\n", INSEXT_FROM_REG(nr, - data->in[nr], - data->in_ext[nr], - data->adc_scale) ); -} -static ssize_t show_in_min(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in_min[nr]) ); -} -static ssize_t set_in_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->in_min[nr] = INS_TO_REG(nr, val); - lm85_write_value(client, LM85_REG_IN_MIN(nr), data->in_min[nr]); - up(&data->update_lock); - return count; -} -static ssize_t show_in_max(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", INS_FROM_REG(nr, data->in_max[nr]) ); -} -static ssize_t set_in_max(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->in_max[nr] = INS_TO_REG(nr, val); - lm85_write_value(client, LM85_REG_IN_MAX(nr), data->in_max[nr]); - up(&data->update_lock); - return count; -} -#define show_in_reg(offset) \ -static ssize_t show_in_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in(dev, buf, offset); \ -} \ -static ssize_t show_in_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in_min(dev, buf, offset); \ -} \ -static ssize_t show_in_##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in_max(dev, buf, offset); \ -} \ -static ssize_t set_in_##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_in_min(dev, buf, count, offset); \ -} \ -static ssize_t set_in_##offset##_max (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_in_max(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in_##offset, \ - NULL); \ -static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ - show_in_##offset##_min, set_in_##offset##_min); \ -static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ - show_in_##offset##_max, set_in_##offset##_max); - -show_in_reg(0); -show_in_reg(1); -show_in_reg(2); -show_in_reg(3); -show_in_reg(4); - -/* Temps */ - -static ssize_t show_temp(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", TEMPEXT_FROM_REG(data->temp[nr], - data->temp_ext[nr], - data->adc_scale) ); -} -static ssize_t show_temp_min(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_min[nr]) ); -} -static ssize_t set_temp_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_min[nr] = TEMP_TO_REG(val); - lm85_write_value(client, LM85_REG_TEMP_MIN(nr), data->temp_min[nr]); - up(&data->update_lock); - return count; -} -static ssize_t show_temp_max(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->temp_max[nr]) ); -} -static ssize_t set_temp_max(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_max[nr] = TEMP_TO_REG(val); - lm85_write_value(client, LM85_REG_TEMP_MAX(nr), data->temp_max[nr]); - up(&data->update_lock); - return count; -} -#define show_temp_reg(offset) \ -static ssize_t show_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp(dev, buf, offset - 1); \ -} \ -static ssize_t show_temp_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp_min(dev, buf, offset - 1); \ -} \ -static ssize_t show_temp_##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp_max(dev, buf, offset - 1); \ -} \ -static ssize_t set_temp_##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_temp_min(dev, buf, count, offset - 1); \ -} \ -static ssize_t set_temp_##offset##_max (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_temp_max(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, \ - NULL); \ -static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ - show_temp_##offset##_min, set_temp_##offset##_min); \ -static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ - show_temp_##offset##_max, set_temp_##offset##_max); - -show_temp_reg(1); -show_temp_reg(2); -show_temp_reg(3); - - -/* Automatic PWM control */ - -static ssize_t show_pwm_auto_channels(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", ZONE_FROM_REG(data->autofan[nr].config)); -} -static ssize_t set_pwm_auto_channels(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->autofan[nr].config = (data->autofan[nr].config & (~0xe0)) - | ZONE_TO_REG(val) ; - lm85_write_value(client, LM85_REG_AFAN_CONFIG(nr), - data->autofan[nr].config); - up(&data->update_lock); - return count; -} -static ssize_t show_pwm_auto_pwm_min(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", PWM_FROM_REG(data->autofan[nr].min_pwm)); -} -static ssize_t set_pwm_auto_pwm_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->autofan[nr].min_pwm = PWM_TO_REG(val); - lm85_write_value(client, LM85_REG_AFAN_MINPWM(nr), - data->autofan[nr].min_pwm); - up(&data->update_lock); - return count; -} -static ssize_t show_pwm_auto_pwm_minctl(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", data->autofan[nr].min_off); -} -static ssize_t set_pwm_auto_pwm_minctl(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->autofan[nr].min_off = val; - lm85_write_value(client, LM85_REG_AFAN_SPIKE1, data->smooth[0] - | data->syncpwm3 - | (data->autofan[0].min_off ? 0x20 : 0) - | (data->autofan[1].min_off ? 0x40 : 0) - | (data->autofan[2].min_off ? 0x80 : 0) - ); - up(&data->update_lock); - return count; -} -static ssize_t show_pwm_auto_pwm_freq(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", FREQ_FROM_REG(data->autofan[nr].freq)); -} -static ssize_t set_pwm_auto_pwm_freq(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->autofan[nr].freq = FREQ_TO_REG(val); - lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), - (data->zone[nr].range << 4) - | data->autofan[nr].freq - ); - up(&data->update_lock); - return count; -} -#define pwm_auto(offset) \ -static ssize_t show_pwm##offset##_auto_channels (struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_pwm_auto_channels(dev, buf, offset - 1); \ -} \ -static ssize_t set_pwm##offset##_auto_channels (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_pwm_auto_channels(dev, buf, count, offset - 1); \ -} \ -static ssize_t show_pwm##offset##_auto_pwm_min (struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_pwm_auto_pwm_min(dev, buf, offset - 1); \ -} \ -static ssize_t set_pwm##offset##_auto_pwm_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_pwm_auto_pwm_min(dev, buf, count, offset - 1); \ -} \ -static ssize_t show_pwm##offset##_auto_pwm_minctl (struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_pwm_auto_pwm_minctl(dev, buf, offset - 1); \ -} \ -static ssize_t set_pwm##offset##_auto_pwm_minctl (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_pwm_auto_pwm_minctl(dev, buf, count, offset - 1); \ -} \ -static ssize_t show_pwm##offset##_auto_pwm_freq (struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_pwm_auto_pwm_freq(dev, buf, offset - 1); \ -} \ -static ssize_t set_pwm##offset##_auto_pwm_freq(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_pwm_auto_pwm_freq(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(pwm##offset##_auto_channels, S_IRUGO | S_IWUSR, \ - show_pwm##offset##_auto_channels, \ - set_pwm##offset##_auto_channels); \ -static DEVICE_ATTR(pwm##offset##_auto_pwm_min, S_IRUGO | S_IWUSR, \ - show_pwm##offset##_auto_pwm_min, \ - set_pwm##offset##_auto_pwm_min); \ -static DEVICE_ATTR(pwm##offset##_auto_pwm_minctl, S_IRUGO | S_IWUSR, \ - show_pwm##offset##_auto_pwm_minctl, \ - set_pwm##offset##_auto_pwm_minctl); \ -static DEVICE_ATTR(pwm##offset##_auto_pwm_freq, S_IRUGO | S_IWUSR, \ - show_pwm##offset##_auto_pwm_freq, \ - set_pwm##offset##_auto_pwm_freq); -pwm_auto(1); -pwm_auto(2); -pwm_auto(3); - -/* Temperature settings for automatic PWM control */ - -static ssize_t show_temp_auto_temp_off(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->zone[nr].limit) - - HYST_FROM_REG(data->zone[nr].hyst)); -} -static ssize_t set_temp_auto_temp_off(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - int min; - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - min = TEMP_FROM_REG(data->zone[nr].limit); - data->zone[nr].off_desired = TEMP_TO_REG(val); - data->zone[nr].hyst = HYST_TO_REG(min - val); - if ( nr == 0 || nr == 1 ) { - lm85_write_value(client, LM85_REG_AFAN_HYST1, - (data->zone[0].hyst << 4) - | data->zone[1].hyst - ); - } else { - lm85_write_value(client, LM85_REG_AFAN_HYST2, - (data->zone[2].hyst << 4) - ); - } - up(&data->update_lock); - return count; -} -static ssize_t show_temp_auto_temp_min(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->zone[nr].limit) ); -} -static ssize_t set_temp_auto_temp_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->zone[nr].limit = TEMP_TO_REG(val); - lm85_write_value(client, LM85_REG_AFAN_LIMIT(nr), - data->zone[nr].limit); - -/* Update temp_auto_max and temp_auto_range */ - data->zone[nr].range = RANGE_TO_REG( - TEMP_FROM_REG(data->zone[nr].max_desired) - - TEMP_FROM_REG(data->zone[nr].limit)); - lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), - ((data->zone[nr].range & 0x0f) << 4) - | (data->autofan[nr].freq & 0x07)); - -/* Update temp_auto_hyst and temp_auto_off */ - data->zone[nr].hyst = HYST_TO_REG(TEMP_FROM_REG( - data->zone[nr].limit) - TEMP_FROM_REG( - data->zone[nr].off_desired)); - if ( nr == 0 || nr == 1 ) { - lm85_write_value(client, LM85_REG_AFAN_HYST1, - (data->zone[0].hyst << 4) - | data->zone[1].hyst - ); - } else { - lm85_write_value(client, LM85_REG_AFAN_HYST2, - (data->zone[2].hyst << 4) - ); - } - up(&data->update_lock); - return count; -} -static ssize_t show_temp_auto_temp_max(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->zone[nr].limit) + - RANGE_FROM_REG(data->zone[nr].range)); -} -static ssize_t set_temp_auto_temp_max(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - int min; - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - min = TEMP_FROM_REG(data->zone[nr].limit); - data->zone[nr].max_desired = TEMP_TO_REG(val); - data->zone[nr].range = RANGE_TO_REG( - val - min); - lm85_write_value(client, LM85_REG_AFAN_RANGE(nr), - ((data->zone[nr].range & 0x0f) << 4) - | (data->autofan[nr].freq & 0x07)); - up(&data->update_lock); - return count; -} -static ssize_t show_temp_auto_temp_crit(struct device *dev, char *buf, int nr) -{ - struct lm85_data *data = lm85_update_device(dev); - return sprintf(buf,"%d\n", TEMP_FROM_REG(data->zone[nr].critical)); -} -static ssize_t set_temp_auto_temp_crit(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->zone[nr].critical = TEMP_TO_REG(val); - lm85_write_value(client, LM85_REG_AFAN_CRITICAL(nr), - data->zone[nr].critical); - up(&data->update_lock); - return count; -} -#define temp_auto(offset) \ -static ssize_t show_temp##offset##_auto_temp_off (struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_temp_auto_temp_off(dev, buf, offset - 1); \ -} \ -static ssize_t set_temp##offset##_auto_temp_off (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_temp_auto_temp_off(dev, buf, count, offset - 1); \ -} \ -static ssize_t show_temp##offset##_auto_temp_min (struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_temp_auto_temp_min(dev, buf, offset - 1); \ -} \ -static ssize_t set_temp##offset##_auto_temp_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_temp_auto_temp_min(dev, buf, count, offset - 1); \ -} \ -static ssize_t show_temp##offset##_auto_temp_max (struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_temp_auto_temp_max(dev, buf, offset - 1); \ -} \ -static ssize_t set_temp##offset##_auto_temp_max (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_temp_auto_temp_max(dev, buf, count, offset - 1); \ -} \ -static ssize_t show_temp##offset##_auto_temp_crit (struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_temp_auto_temp_crit(dev, buf, offset - 1); \ -} \ -static ssize_t set_temp##offset##_auto_temp_crit (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_temp_auto_temp_crit(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(temp##offset##_auto_temp_off, S_IRUGO | S_IWUSR, \ - show_temp##offset##_auto_temp_off, \ - set_temp##offset##_auto_temp_off); \ -static DEVICE_ATTR(temp##offset##_auto_temp_min, S_IRUGO | S_IWUSR, \ - show_temp##offset##_auto_temp_min, \ - set_temp##offset##_auto_temp_min); \ -static DEVICE_ATTR(temp##offset##_auto_temp_max, S_IRUGO | S_IWUSR, \ - show_temp##offset##_auto_temp_max, \ - set_temp##offset##_auto_temp_max); \ -static DEVICE_ATTR(temp##offset##_auto_temp_crit, S_IRUGO | S_IWUSR, \ - show_temp##offset##_auto_temp_crit, \ - set_temp##offset##_auto_temp_crit); -temp_auto(1); -temp_auto(2); -temp_auto(3); - -int lm85_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, lm85_detect); -} - -int lm85_detect(struct i2c_adapter *adapter, int address, - int kind) -{ - int company, verstep ; - struct i2c_client *new_client = NULL; - struct lm85_data *data; - int err = 0; - const char *type_name = ""; - - if (i2c_is_isa_adapter(adapter)) { - /* This chip has no ISA interface */ - goto ERROR0 ; - }; - - if (!i2c_check_functionality(adapter, - I2C_FUNC_SMBUS_BYTE_DATA)) { - /* We need to be able to do byte I/O */ - goto ERROR0 ; - }; - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access lm85_{read,write}_value. */ - - if (!(data = kmalloc(sizeof(struct lm85_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR0; - } - memset(data, 0, sizeof(struct lm85_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm85_driver; - new_client->flags = 0; - - /* Now, we do the remaining detection. */ - - company = lm85_read_value(new_client, LM85_REG_COMPANY); - verstep = lm85_read_value(new_client, LM85_REG_VERSTEP); - - dev_dbg(&adapter->dev, "Detecting device at %d,0x%02x with" - " COMPANY: 0x%02x and VERSTEP: 0x%02x\n", - i2c_adapter_id(new_client->adapter), new_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 == LM85_COMPANY_NATIONAL - && verstep == LM85_VERSTEP_LM85C ) { - kind = lm85c ; - } else if( company == LM85_COMPANY_NATIONAL - && verstep == LM85_VERSTEP_LM85B ) { - kind = lm85b ; - } else if( company == LM85_COMPANY_NATIONAL - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC ) { - dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" - " Defaulting to LM85.\n", verstep); - kind = any_chip ; - } else if( company == LM85_COMPANY_ANALOG_DEV - && verstep == LM85_VERSTEP_ADM1027 ) { - kind = adm1027 ; - } else if( company == LM85_COMPANY_ANALOG_DEV - && (verstep == LM85_VERSTEP_ADT7463 - || verstep == LM85_VERSTEP_ADT7463C) ) { - kind = adt7463 ; - } else if( company == LM85_COMPANY_ANALOG_DEV - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC ) { - dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" - " Defaulting to Generic LM85.\n", verstep ); - kind = any_chip ; - } else if( company == LM85_COMPANY_SMSC - && (verstep == LM85_VERSTEP_EMC6D100_A0 - || verstep == LM85_VERSTEP_EMC6D100_A1) ) { - /* Unfortunately, we can't tell a '100 from a '101 - * from the registers. Since a '101 is a '100 - * in a package with fewer pins and therefore no - * 3.3V, 1.5V or 1.8V inputs, perhaps if those - * inputs read 0, then it's a '101. - */ - kind = emc6d100 ; - } else if( company == LM85_COMPANY_SMSC - && verstep == LM85_VERSTEP_EMC6D102) { - kind = emc6d102 ; - } else if( company == LM85_COMPANY_SMSC - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "lm85: Detected SMSC chip\n"); - dev_err(&adapter->dev, "lm85: Unrecognized version/stepping 0x%02x" - " Defaulting to Generic LM85.\n", verstep ); - kind = any_chip ; - } else if( kind == any_chip - && (verstep & LM85_VERSTEP_VMASK) == LM85_VERSTEP_GENERIC) { - dev_err(&adapter->dev, "Generic LM85 Version 6 detected\n"); - /* Leave kind as "any_chip" */ - } else { - dev_dbg(&adapter->dev, "Autodetection failed\n"); - /* Not an LM85 ... */ - if( kind == any_chip ) { /* User used force=x,y */ - dev_err(&adapter->dev, "Generic LM85 Version 6 not" - " found at %d,0x%02x. Try force_lm85c.\n", - i2c_adapter_id(adapter), address ); - } - err = 0 ; - goto ERROR1; - } - } - - /* Fill in the chip specific driver values */ - if ( kind == any_chip ) { - type_name = "lm85"; - } else if ( kind == lm85b ) { - type_name = "lm85b"; - } else if ( kind == lm85c ) { - type_name = "lm85c"; - } else if ( kind == adm1027 ) { - type_name = "adm1027"; - } else if ( kind == adt7463 ) { - type_name = "adt7463"; - } else if ( kind == emc6d100){ - type_name = "emc6d100"; - } else if ( kind == emc6d102 ) { - type_name = "emc6d102"; - } - strlcpy(new_client->name, type_name, I2C_NAME_SIZE); - - /* Fill in the remaining client fields */ - data->type = kind; - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto ERROR1; - - /* Set the VRM version */ - data->vrm = i2c_which_vrm(); - - /* Initialize the LM85 chip */ - lm85_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan3_input); - device_create_file(&new_client->dev, &dev_attr_fan4_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan3_min); - device_create_file(&new_client->dev, &dev_attr_fan4_min); - device_create_file(&new_client->dev, &dev_attr_pwm1); - device_create_file(&new_client->dev, &dev_attr_pwm2); - device_create_file(&new_client->dev, &dev_attr_pwm3); - device_create_file(&new_client->dev, &dev_attr_pwm1_enable); - device_create_file(&new_client->dev, &dev_attr_pwm2_enable); - device_create_file(&new_client->dev, &dev_attr_pwm3_enable); - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in4_max); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp3_input); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp3_min); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp3_max); - device_create_file(&new_client->dev, &dev_attr_vrm); - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_pwm1_auto_channels); - device_create_file(&new_client->dev, &dev_attr_pwm2_auto_channels); - device_create_file(&new_client->dev, &dev_attr_pwm3_auto_channels); - device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_min); - device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_min); - device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_min); - device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_minctl); - device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_minctl); - device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_minctl); - device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_freq); - device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_freq); - device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_freq); - device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_off); - device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_off); - device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_off); - device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_min); - device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_min); - device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_min); - device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_max); - device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_max); - device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_max); - device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_crit); - device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_crit); - device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_crit); - - return 0; - - /* Error out and cleanup code */ - ERROR1: - kfree(data); - ERROR0: - return err; -} - -int lm85_detach_client(struct i2c_client *client) -{ - i2c_detach_client(client); - kfree(i2c_get_clientdata(client)); - return 0; -} - - -int lm85_read_value(struct i2c_client *client, u8 reg) -{ - int res; - - /* What size location is it? */ - switch( reg ) { - case LM85_REG_FAN(0) : /* Read WORD data */ - case LM85_REG_FAN(1) : - case LM85_REG_FAN(2) : - case LM85_REG_FAN(3) : - case LM85_REG_FAN_MIN(0) : - case LM85_REG_FAN_MIN(1) : - case LM85_REG_FAN_MIN(2) : - case LM85_REG_FAN_MIN(3) : - case LM85_REG_ALARM1 : /* Read both bytes at once */ - res = i2c_smbus_read_byte_data(client, reg) & 0xff ; - res |= i2c_smbus_read_byte_data(client, reg+1) << 8 ; - break ; - case ADT7463_REG_TMIN_CTL1 : /* Read WORD MSB, LSB */ - res = i2c_smbus_read_byte_data(client, reg) << 8 ; - res |= i2c_smbus_read_byte_data(client, reg+1) & 0xff ; - break ; - default: /* Read BYTE data */ - res = i2c_smbus_read_byte_data(client, reg); - break ; - } - - return res ; -} - -int lm85_write_value(struct i2c_client *client, u8 reg, int value) -{ - int res ; - - switch( reg ) { - case LM85_REG_FAN(0) : /* Write WORD data */ - case LM85_REG_FAN(1) : - case LM85_REG_FAN(2) : - case LM85_REG_FAN(3) : - case LM85_REG_FAN_MIN(0) : - case LM85_REG_FAN_MIN(1) : - case LM85_REG_FAN_MIN(2) : - case LM85_REG_FAN_MIN(3) : - /* NOTE: ALARM is read only, so not included here */ - res = i2c_smbus_write_byte_data(client, reg, value & 0xff) ; - res |= i2c_smbus_write_byte_data(client, reg+1, (value>>8) & 0xff) ; - break ; - case ADT7463_REG_TMIN_CTL1 : /* Write WORD MSB, LSB */ - res = i2c_smbus_write_byte_data(client, reg, (value>>8) & 0xff); - res |= i2c_smbus_write_byte_data(client, reg+1, value & 0xff) ; - break ; - default: /* Write BYTE data */ - res = i2c_smbus_write_byte_data(client, reg, value); - break ; - } - - return res ; -} - -void lm85_init_client(struct i2c_client *client) -{ - int value; - struct lm85_data *data = i2c_get_clientdata(client); - - dev_dbg(&client->dev, "Initializing device\n"); - - /* Warn if part was not "READY" */ - value = lm85_read_value(client, LM85_REG_CONFIG); - dev_dbg(&client->dev, "LM85_REG_CONFIG is: 0x%02x\n", value); - if( value & 0x02 ) { - dev_err(&client->dev, "Client (%d,0x%02x) config is locked.\n", - i2c_adapter_id(client->adapter), client->addr ); - }; - if( ! (value & 0x04) ) { - dev_err(&client->dev, "Client (%d,0x%02x) is not ready.\n", - i2c_adapter_id(client->adapter), client->addr ); - }; - if( value & 0x10 - && ( data->type == adm1027 - || data->type == adt7463 ) ) { - dev_err(&client->dev, "Client (%d,0x%02x) VxI mode is set. " - "Please report this to the lm85 maintainer.\n", - i2c_adapter_id(client->adapter), client->addr ); - }; - - /* WE INTENTIONALLY make no changes to the limits, - * offsets, pwms, fans and zones. If they were - * configured, we don't want to mess with them. - * If they weren't, the default is 100% PWM, no - * control and will suffice until 'sensors -s' - * can be run by the user. - */ - - /* Start monitoring */ - value = lm85_read_value(client, LM85_REG_CONFIG); - /* Try to clear LOCK, Set START, save everything else */ - value = (value & ~ 0x02) | 0x01 ; - dev_dbg(&client->dev, "Setting CONFIG to: 0x%02x\n", value); - lm85_write_value(client, LM85_REG_CONFIG, value); -} - -static struct lm85_data *lm85_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm85_data *data = i2c_get_clientdata(client); - int i; - - down(&data->update_lock); - - if ( !data->valid || - time_after(jiffies, data->last_reading + LM85_DATA_INTERVAL) ) { - /* Things that change quickly */ - dev_dbg(&client->dev, "Reading sensor values\n"); - - /* Have to read extended bits first to "freeze" the - * more significant bits that are read later. - */ - if ( (data->type == adm1027) || (data->type == adt7463) ) { - int ext1 = lm85_read_value(client, - ADM1027_REG_EXTEND_ADC1); - int ext2 = lm85_read_value(client, - ADM1027_REG_EXTEND_ADC2); - int val = (ext1 << 8) + ext2; - - for(i = 0; i <= 4; i++) - data->in_ext[i] = (val>>(i * 2))&0x03; - - for(i = 0; i <= 2; i++) - data->temp_ext[i] = (val>>((i + 5) * 2))&0x03; - } - - /* adc_scale is 2^(number of LSBs). There are 4 extra bits in - the emc6d102 and 2 in the adt7463 and adm1027. In all - other chips ext is always 0 and the value of scale is - irrelevant. So it is left in 4*/ - data->adc_scale = (data->type == emc6d102 ) ? 16 : 4; - - for (i = 0; i <= 4; ++i) { - data->in[i] = - lm85_read_value(client, LM85_REG_IN(i)); - } - - for (i = 0; i <= 3; ++i) { - data->fan[i] = - lm85_read_value(client, LM85_REG_FAN(i)); - } - - for (i = 0; i <= 2; ++i) { - data->temp[i] = - lm85_read_value(client, LM85_REG_TEMP(i)); - } - - for (i = 0; i <= 2; ++i) { - data->pwm[i] = - lm85_read_value(client, LM85_REG_PWM(i)); - } - - data->alarms = lm85_read_value(client, LM85_REG_ALARM1); - - if ( data->type == adt7463 ) { - if( data->therm_total < ULONG_MAX - 256 ) { - data->therm_total += - lm85_read_value(client, ADT7463_REG_THERM ); - } - } else if ( data->type == emc6d100 ) { - /* Three more voltage sensors */ - for (i = 5; i <= 7; ++i) { - data->in[i] = - lm85_read_value(client, EMC6D100_REG_IN(i)); - } - /* More alarm bits */ - data->alarms |= - lm85_read_value(client, EMC6D100_REG_ALARM3) << 16; - } else if (data->type == emc6d102 ) { - /* Have to read LSB bits after the MSB ones because - the reading of the MSB bits has frozen the - LSBs (backward from the ADM1027). - */ - int ext1 = lm85_read_value(client, - EMC6D102_REG_EXTEND_ADC1); - int ext2 = lm85_read_value(client, - EMC6D102_REG_EXTEND_ADC2); - int ext3 = lm85_read_value(client, - EMC6D102_REG_EXTEND_ADC3); - int ext4 = lm85_read_value(client, - EMC6D102_REG_EXTEND_ADC4); - data->in_ext[0] = ext3 & 0x0f; - data->in_ext[1] = ext4 & 0x0f; - data->in_ext[2] = (ext4 >> 4) & 0x0f; - data->in_ext[3] = (ext3 >> 4) & 0x0f; - data->in_ext[4] = (ext2 >> 4) & 0x0f; - - data->temp_ext[0] = ext1 & 0x0f; - data->temp_ext[1] = ext2 & 0x0f; - data->temp_ext[2] = (ext1 >> 4) & 0x0f; - } - - data->last_reading = jiffies ; - }; /* last_reading */ - - if ( !data->valid || - time_after(jiffies, data->last_config + LM85_CONFIG_INTERVAL) ) { - /* Things that don't change often */ - dev_dbg(&client->dev, "Reading config values\n"); - - for (i = 0; i <= 4; ++i) { - data->in_min[i] = - lm85_read_value(client, LM85_REG_IN_MIN(i)); - data->in_max[i] = - lm85_read_value(client, LM85_REG_IN_MAX(i)); - } - - if ( data->type == emc6d100 ) { - for (i = 5; i <= 7; ++i) { - data->in_min[i] = - lm85_read_value(client, EMC6D100_REG_IN_MIN(i)); - data->in_max[i] = - lm85_read_value(client, EMC6D100_REG_IN_MAX(i)); - } - } - - for (i = 0; i <= 3; ++i) { - data->fan_min[i] = - lm85_read_value(client, LM85_REG_FAN_MIN(i)); - } - - for (i = 0; i <= 2; ++i) { - data->temp_min[i] = - lm85_read_value(client, LM85_REG_TEMP_MIN(i)); - data->temp_max[i] = - lm85_read_value(client, LM85_REG_TEMP_MAX(i)); - } - - data->vid = lm85_read_value(client, LM85_REG_VID); - - for (i = 0; i <= 2; ++i) { - int val ; - data->autofan[i].config = - lm85_read_value(client, LM85_REG_AFAN_CONFIG(i)); - val = lm85_read_value(client, LM85_REG_AFAN_RANGE(i)); - data->autofan[i].freq = val & 0x07 ; - data->zone[i].range = (val >> 4) & 0x0f ; - data->autofan[i].min_pwm = - lm85_read_value(client, LM85_REG_AFAN_MINPWM(i)); - data->zone[i].limit = - lm85_read_value(client, LM85_REG_AFAN_LIMIT(i)); - data->zone[i].critical = - lm85_read_value(client, LM85_REG_AFAN_CRITICAL(i)); - } - - i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1); - data->smooth[0] = i & 0x0f ; - data->syncpwm3 = i & 0x10 ; /* Save PWM3 config */ - data->autofan[0].min_off = (i & 0x20) != 0 ; - data->autofan[1].min_off = (i & 0x40) != 0 ; - data->autofan[2].min_off = (i & 0x80) != 0 ; - i = lm85_read_value(client, LM85_REG_AFAN_SPIKE2); - data->smooth[1] = (i>>4) & 0x0f ; - data->smooth[2] = i & 0x0f ; - - i = lm85_read_value(client, LM85_REG_AFAN_HYST1); - data->zone[0].hyst = (i>>4) & 0x0f ; - data->zone[1].hyst = i & 0x0f ; - - i = lm85_read_value(client, LM85_REG_AFAN_HYST2); - data->zone[2].hyst = (i>>4) & 0x0f ; - - if ( (data->type == lm85b) || (data->type == lm85c) ) { - data->tach_mode = lm85_read_value(client, - LM85_REG_TACH_MODE ); - data->spinup_ctl = lm85_read_value(client, - LM85_REG_SPINUP_CTL ); - } else if ( (data->type == adt7463) || (data->type == adm1027) ) { - if ( data->type == adt7463 ) { - for (i = 0; i <= 2; ++i) { - data->oppoint[i] = lm85_read_value(client, - ADT7463_REG_OPPOINT(i) ); - } - data->tmin_ctl = lm85_read_value(client, - ADT7463_REG_TMIN_CTL1 ); - data->therm_limit = lm85_read_value(client, - ADT7463_REG_THERM_LIMIT ); - } - for (i = 0; i <= 2; ++i) { - data->temp_offset[i] = lm85_read_value(client, - ADM1027_REG_TEMP_OFFSET(i) ); - } - data->tach_mode = lm85_read_value(client, - ADM1027_REG_CONFIG3 ); - data->fan_ppr = lm85_read_value(client, - ADM1027_REG_FAN_PPR ); - } - - data->last_config = jiffies; - }; /* last_config */ - - data->valid = 1; - - up(&data->update_lock); - - return data; -} - - -static int __init sm_lm85_init(void) -{ - return i2c_add_driver(&lm85_driver); -} - -static void __exit sm_lm85_exit(void) -{ - i2c_del_driver(&lm85_driver); -} - -/* Thanks to Richard Barrington for adding the LM85 to sensors-detect. - * Thanks to Margit Schubert-While for help with - * post 2.7.0 CVS changes. - */ -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Philip Pokorny , Margit Schubert-While , Justin Thiessen - * Philip Edelbrock - * Stephen Rousset - * Dan Eaton - * Copyright (C) 2004 Jean Delvare - * - * Original port to Linux 2.6 by Jeff Oliver. - * - * The LM87 is a sensor chip made by National Semiconductor. It monitors up - * to 8 voltages (including its own power source), up to three temperatures - * (its own plus up to two external ones) and up to two fans. The default - * configuration is 6 voltages, two temperatures and two fans (see below). - * Voltages are scaled internally with ratios such that the nominal value of - * each voltage correspond to a register value of 192 (which means a - * resolution of about 0.5% of the nominal value). Temperature values are - * reported with a 1 deg resolution and a 3-4 deg accuracy. Complete - * datasheet can be obtained from National's website at: - * http://www.national.com/pf/LM/LM87.html - * - * Some functions share pins, so not all functions are available at the same - * time. Which are depends on the hardware setup. This driver assumes that - * the BIOS configured the chip correctly. In that respect, it differs from - * the original driver (from lm_sensors for Linux 2.4), which would force the - * LM87 to an arbitrary, compile-time chosen mode, regardless of the actual - * chipset wiring. - * For reference, here is the list of exclusive functions: - * - in0+in5 (default) or temp3 - * - fan1 (default) or in6 - * - fan2 (default) or in7 - * - VID lines (default) or IRQ lines (not handled by this driver) - * - * The LM87 additionally features an analog output, supposedly usable to - * control the speed of a fan. All new chips use pulse width modulation - * instead. The LM87 is the only hardware monitoring chipset I know of - * which uses amplitude modulation. Be careful when using this feature. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * Addresses to scan - * LM87 has three possible addresses: 0x2c, 0x2d and 0x2e. - */ - -static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* - * Insmod parameters - */ - -SENSORS_INSMOD_1(lm87); - -/* - * The LM87 registers - */ - -/* nr in 0..5 */ -#define LM87_REG_IN(nr) (0x20 + (nr)) -#define LM87_REG_IN_MAX(nr) (0x2B + (nr) * 2) -#define LM87_REG_IN_MIN(nr) (0x2C + (nr) * 2) -/* nr in 0..1 */ -#define LM87_REG_AIN(nr) (0x28 + (nr)) -#define LM87_REG_AIN_MIN(nr) (0x1A + (nr)) -#define LM87_REG_AIN_MAX(nr) (0x3B + (nr)) - -static u8 LM87_REG_TEMP[3] = { 0x27, 0x26, 0x20 }; -static u8 LM87_REG_TEMP_HIGH[3] = { 0x39, 0x37, 0x2B }; -static u8 LM87_REG_TEMP_LOW[3] = { 0x3A, 0x38, 0x2C }; - -#define LM87_REG_TEMP_HW_INT_LOCK 0x13 -#define LM87_REG_TEMP_HW_EXT_LOCK 0x14 -#define LM87_REG_TEMP_HW_INT 0x17 -#define LM87_REG_TEMP_HW_EXT 0x18 - -/* nr in 0..1 */ -#define LM87_REG_FAN(nr) (0x28 + (nr)) -#define LM87_REG_FAN_MIN(nr) (0x3B + (nr)) -#define LM87_REG_AOUT 0x19 - -#define LM87_REG_CONFIG 0x40 -#define LM87_REG_CHANNEL_MODE 0x16 -#define LM87_REG_VID_FAN_DIV 0x47 -#define LM87_REG_VID4 0x49 - -#define LM87_REG_ALARMS1 0x41 -#define LM87_REG_ALARMS2 0x42 - -#define LM87_REG_COMPANY_ID 0x3E -#define LM87_REG_REVISION 0x3F - -/* - * Conversions and various macros - * The LM87 uses signed 8-bit values for temperatures. - */ - -#define IN_FROM_REG(reg,scale) (((reg) * (scale) + 96) / 192) -#define IN_TO_REG(val,scale) ((val) <= 0 ? 0 : \ - (val) * 192 >= (scale) * 255 ? 255 : \ - ((val) * 192 + (scale)/2) / (scale)) - -#define TEMP_FROM_REG(reg) ((reg) * 1000) -#define TEMP_TO_REG(val) ((val) <= -127500 ? -128 : \ - (val) >= 126500 ? 127 : \ - (((val) < 0 ? (val)-500 : (val)+500) / 1000)) - -#define FAN_FROM_REG(reg,div) ((reg) == 255 || (reg) == 0 ? 0 : \ - 1350000 + (reg)*(div) / 2) / ((reg)*(div)) -#define FAN_TO_REG(val,div) ((val)*(div) * 255 <= 1350000 ? 255 : \ - (1350000 + (val)*(div) / 2) / ((val)*(div))) - -#define FAN_DIV_FROM_REG(reg) (1 << (reg)) - -/* analog out is 9.80mV/LSB */ -#define AOUT_FROM_REG(reg) (((reg) * 98 + 5) / 10) -#define AOUT_TO_REG(val) ((val) <= 0 ? 0 : \ - (val) >= 2500 ? 255 : \ - ((val) * 10 + 49) / 98) - -/* nr in 0..1 */ -#define CHAN_NO_FAN(nr) (1 << (nr)) -#define CHAN_TEMP3 (1 << 2) -#define CHAN_VCC_5V (1 << 3) -#define CHAN_NO_VID (1 << 8) - -/* - * Functions declaration - */ - -static int lm87_attach_adapter(struct i2c_adapter *adapter); -static int lm87_detect(struct i2c_adapter *adapter, int address, int kind); -static void lm87_init_client(struct i2c_client *client); -static int lm87_detach_client(struct i2c_client *client); -static struct lm87_data *lm87_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static struct i2c_driver lm87_driver = { - .owner = THIS_MODULE, - .name = "lm87", - .id = I2C_DRIVERID_LM87, - .flags = I2C_DF_NOTIFY, - .attach_adapter = lm87_attach_adapter, - .detach_client = lm87_detach_client, -}; - -/* - * Client data (each client gets its own) - */ - -struct lm87_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - u8 channel; /* register value */ - - u8 in[8]; /* register value */ - u8 in_max[8]; /* register value */ - u8 in_min[8]; /* register value */ - u16 in_scale[8]; - - s8 temp[3]; /* register value */ - s8 temp_high[3]; /* register value */ - s8 temp_low[3]; /* register value */ - s8 temp_crit_int; /* min of two register values */ - s8 temp_crit_ext; /* min of two register values */ - - u8 fan[2]; /* register value */ - u8 fan_min[2]; /* register value */ - u8 fan_div[2]; /* register value, shifted right */ - u8 aout; /* register value */ - - u16 alarms; /* register values, combined */ - u8 vid; /* register values, combined */ - u8 vrm; -}; - -/* - * Sysfs stuff - */ - -static inline int lm87_read_value(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static inline int lm87_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - -#define show_in(offset) \ -static ssize_t show_in##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \ - data->in_scale[offset])); \ -} \ -static ssize_t show_in##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \ - data->in_scale[offset])); \ -} \ -static ssize_t show_in##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \ - data->in_scale[offset])); \ -} \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ - show_in##offset##_input, NULL); -show_in(0); -show_in(1); -show_in(2); -show_in(3); -show_in(4); -show_in(5); -show_in(6); -show_in(7); - -static void set_in_min(struct device *dev, const char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val, data->in_scale[nr]); - lm87_write_value(client, nr<6 ? LM87_REG_IN_MIN(nr) : - LM87_REG_AIN_MIN(nr-6), data->in_min[nr]); - up(&data->update_lock); -} - -static void set_in_max(struct device *dev, const char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val, data->in_scale[nr]); - lm87_write_value(client, nr<6 ? LM87_REG_IN_MAX(nr) : - LM87_REG_AIN_MAX(nr-6), data->in_max[nr]); - up(&data->update_lock); -} - -#define set_in(offset) \ -static ssize_t set_in##offset##_min(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - set_in_min(dev, buf, offset); \ - return count; \ -} \ -static ssize_t set_in##offset##_max(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - set_in_max(dev, buf, offset); \ - return count; \ -} \ -static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ - show_in##offset##_min, set_in##offset##_min); \ -static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ - show_in##offset##_max, set_in##offset##_max); -set_in(0); -set_in(1); -set_in(2); -set_in(3); -set_in(4); -set_in(5); -set_in(6); -set_in(7); - -#define show_temp(offset) \ -static ssize_t show_temp##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \ -} \ -static ssize_t show_temp##offset##_low(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_low[offset-1])); \ -} \ -static ssize_t show_temp##offset##_high(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_high[offset-1])); \ -}\ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ - show_temp##offset##_input, NULL); -show_temp(1); -show_temp(2); -show_temp(3); - -static void set_temp_low(struct device *dev, const char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_low[nr] = TEMP_TO_REG(val); - lm87_write_value(client, LM87_REG_TEMP_LOW[nr], data->temp_low[nr]); - up(&data->update_lock); -} - -static void set_temp_high(struct device *dev, const char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_high[nr] = TEMP_TO_REG(val); - lm87_write_value(client, LM87_REG_TEMP_HIGH[nr], data->temp_high[nr]); - up(&data->update_lock); -} - -#define set_temp(offset) \ -static ssize_t set_temp##offset##_low(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - set_temp_low(dev, buf, offset-1); \ - return count; \ -} \ -static ssize_t set_temp##offset##_high(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - set_temp_high(dev, buf, offset-1); \ - return count; \ -} \ -static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ - show_temp##offset##_high, set_temp##offset##_high); \ -static DEVICE_ATTR(temp##offset##_min, S_IRUGO | S_IWUSR, \ - show_temp##offset##_low, set_temp##offset##_low); -set_temp(1); -set_temp(2); -set_temp(3); - -static ssize_t show_temp_crit_int(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm87_data *data = lm87_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_int)); -} - -static ssize_t show_temp_crit_ext(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm87_data *data = lm87_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit_ext)); -} - -static DEVICE_ATTR(temp1_crit, S_IRUGO, show_temp_crit_int, NULL); -static DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp_crit_ext, NULL); -static DEVICE_ATTR(temp3_crit, S_IRUGO, show_temp_crit_ext, NULL); - -#define show_fan(offset) \ -static ssize_t show_fan##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[offset-1], \ - FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \ -} \ -static ssize_t show_fan##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan_min[offset-1], \ - FAN_DIV_FROM_REG(data->fan_div[offset-1]))); \ -} \ -static ssize_t show_fan##offset##_div(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm87_data *data = lm87_update_device(dev); \ - return sprintf(buf, "%d\n", FAN_DIV_FROM_REG(data->fan_div[offset-1])); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ - show_fan##offset##_input, NULL); -show_fan(1); -show_fan(2); - -static void set_fan_min(struct device *dev, const char *buf, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->fan_min[nr] = FAN_TO_REG(val, - FAN_DIV_FROM_REG(data->fan_div[nr])); - lm87_write_value(client, LM87_REG_FAN_MIN(nr), data->fan_min[nr]); - up(&data->update_lock); -} - -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan clock divider. This follows the principle - of least suprise; the user doesn't expect the fan minimum to change just - because the divider changed. */ -static ssize_t set_fan_div(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - unsigned long min; - u8 reg; - - down(&data->update_lock); - min = FAN_FROM_REG(data->fan_min[nr], - FAN_DIV_FROM_REG(data->fan_div[nr])); - - switch (val) { - case 1: data->fan_div[nr] = 0; break; - case 2: data->fan_div[nr] = 1; break; - case 4: data->fan_div[nr] = 2; break; - case 8: data->fan_div[nr] = 3; break; - default: - up(&data->update_lock); - return -EINVAL; - } - - reg = lm87_read_value(client, LM87_REG_VID_FAN_DIV); - switch (nr) { - case 0: - reg = (reg & 0xCF) | (data->fan_div[0] << 4); - break; - case 1: - reg = (reg & 0x3F) | (data->fan_div[1] << 6); - break; - } - lm87_write_value(client, LM87_REG_VID_FAN_DIV, reg); - - data->fan_min[nr] = FAN_TO_REG(min, val); - lm87_write_value(client, LM87_REG_FAN_MIN(nr), - data->fan_min[nr]); - up(&data->update_lock); - - return count; -} - -#define set_fan(offset) \ -static ssize_t set_fan##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - set_fan_min(dev, buf, offset-1); \ - return count; \ -} \ -static ssize_t set_fan##offset##_div(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - return set_fan_div(dev, buf, count, offset-1); \ -} \ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan##offset##_min, set_fan##offset##_min); \ -static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ - show_fan##offset##_div, set_fan##offset##_div); -set_fan(1); -set_fan(2); - -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm87_data *data = lm87_update_device(dev); - return sprintf(buf, "%d\n", data->alarms); -} -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm87_data *data = lm87_update_device(dev); - return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm)); -} -static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); - -static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm87_data *data = lm87_update_device(dev); - return sprintf(buf, "%d\n", data->vrm); -} -static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm87_data *data = i2c_get_clientdata(client); - data->vrm = simple_strtoul(buf, NULL, 10); - return count; -} -static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); - -static ssize_t show_aout(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm87_data *data = lm87_update_device(dev); - return sprintf(buf, "%d\n", AOUT_FROM_REG(data->aout)); -} -static ssize_t set_aout(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm87_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->aout = AOUT_TO_REG(val); - lm87_write_value(client, LM87_REG_AOUT, data->aout); - up(&data->update_lock); - return count; -} -static DEVICE_ATTR(aout_output, S_IRUGO | S_IWUSR, show_aout, set_aout); - -/* - * Real code - */ - -static int lm87_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, lm87_detect); -} - -/* - * The following function does more than just detection. If detection - * succeeds, it also registers the new chip. - */ -static int lm87_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct lm87_data *data; - int err = 0; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - if (!(data = kmalloc(sizeof(struct lm87_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct lm87_data)); - - /* The common I2C client data is placed right before the - LM87-specific data. */ - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm87_driver; - new_client->flags = 0; - - /* Default to an LM87 if forced */ - if (kind == 0) - kind = lm87; - - /* Now, we do the remaining detection. */ - if (kind < 0) { - u8 rev = lm87_read_value(new_client, LM87_REG_REVISION); - - if (rev < 0x01 || rev > 0x08 - || (lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80) - || lm87_read_value(new_client, LM87_REG_COMPANY_ID) != 0x02) { - dev_dbg(&adapter->dev, - "LM87 detection failed at 0x%02x.\n", - address); - goto exit_free; - } - } - - /* We can fill in the remaining client fields */ - strlcpy(new_client->name, "lm87", I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the LM87 chip */ - lm87_init_client(new_client); - - data->in_scale[0] = 2500; - data->in_scale[1] = 2700; - data->in_scale[2] = (data->channel & CHAN_VCC_5V) ? 5000 : 3300; - data->in_scale[3] = 5000; - data->in_scale[4] = 12000; - data->in_scale[5] = 2700; - data->in_scale[6] = 1875; - data->in_scale[7] = 1875; - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in4_max); - - if (data->channel & CHAN_NO_FAN(0)) { - device_create_file(&new_client->dev, &dev_attr_in6_input); - device_create_file(&new_client->dev, &dev_attr_in6_min); - device_create_file(&new_client->dev, &dev_attr_in6_max); - } else { - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - } - if (data->channel & CHAN_NO_FAN(1)) { - device_create_file(&new_client->dev, &dev_attr_in7_input); - device_create_file(&new_client->dev, &dev_attr_in7_min); - device_create_file(&new_client->dev, &dev_attr_in7_max); - } else { - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - } - - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp1_crit); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp2_crit); - - if (data->channel & CHAN_TEMP3) { - device_create_file(&new_client->dev, &dev_attr_temp3_input); - device_create_file(&new_client->dev, &dev_attr_temp3_max); - device_create_file(&new_client->dev, &dev_attr_temp3_min); - device_create_file(&new_client->dev, &dev_attr_temp3_crit); - } else { - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in5_input); - device_create_file(&new_client->dev, &dev_attr_in5_min); - device_create_file(&new_client->dev, &dev_attr_in5_max); - } - - if (!(data->channel & CHAN_NO_VID)) { - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - device_create_file(&new_client->dev, &dev_attr_vrm); - } - - device_create_file(&new_client->dev, &dev_attr_alarms); - device_create_file(&new_client->dev, &dev_attr_aout_output); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static void lm87_init_client(struct i2c_client *client) -{ - struct lm87_data *data = i2c_get_clientdata(client); - u8 config; - - data->channel = lm87_read_value(client, LM87_REG_CHANNEL_MODE); - data->vrm = i2c_which_vrm(); - - config = lm87_read_value(client, LM87_REG_CONFIG); - if (!(config & 0x01)) { - int i; - - /* Limits are left uninitialized after power-up */ - for (i = 1; i < 6; i++) { - lm87_write_value(client, LM87_REG_IN_MIN(i), 0x00); - lm87_write_value(client, LM87_REG_IN_MAX(i), 0xFF); - } - for (i = 0; i < 2; i++) { - lm87_write_value(client, LM87_REG_TEMP_HIGH[i], 0x7F); - lm87_write_value(client, LM87_REG_TEMP_LOW[i], 0x00); - lm87_write_value(client, LM87_REG_AIN_MIN(i), 0x00); - lm87_write_value(client, LM87_REG_AIN_MAX(i), 0xFF); - } - if (data->channel & CHAN_TEMP3) { - lm87_write_value(client, LM87_REG_TEMP_HIGH[2], 0x7F); - lm87_write_value(client, LM87_REG_TEMP_LOW[2], 0x00); - } else { - lm87_write_value(client, LM87_REG_IN_MIN(0), 0x00); - lm87_write_value(client, LM87_REG_IN_MAX(0), 0xFF); - } - } - if ((config & 0x81) != 0x01) { - /* Start monitoring */ - lm87_write_value(client, LM87_REG_CONFIG, - (config & 0xF7) | 0x01); - } -} - -static int lm87_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - -static struct lm87_data *lm87_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm87_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - int i, j; - - dev_dbg(&client->dev, "Updating data.\n"); - - i = (data->channel & CHAN_TEMP3) ? 1 : 0; - j = (data->channel & CHAN_TEMP3) ? 5 : 6; - for (; i < j; i++) { - data->in[i] = lm87_read_value(client, - LM87_REG_IN(i)); - data->in_min[i] = lm87_read_value(client, - LM87_REG_IN_MIN(i)); - data->in_max[i] = lm87_read_value(client, - LM87_REG_IN_MAX(i)); - } - - for (i = 0; i < 2; i++) { - if (data->channel & CHAN_NO_FAN(i)) { - data->in[6+i] = lm87_read_value(client, - LM87_REG_AIN(i)); - data->in_max[6+i] = lm87_read_value(client, - LM87_REG_AIN_MAX(i)); - data->in_min[6+i] = lm87_read_value(client, - LM87_REG_AIN_MIN(i)); - - } else { - data->fan[i] = lm87_read_value(client, - LM87_REG_FAN(i)); - data->fan_min[i] = lm87_read_value(client, - LM87_REG_FAN_MIN(i)); - } - } - - j = (data->channel & CHAN_TEMP3) ? 3 : 2; - for (i = 0 ; i < j; i++) { - data->temp[i] = lm87_read_value(client, - LM87_REG_TEMP[i]); - data->temp_high[i] = lm87_read_value(client, - LM87_REG_TEMP_HIGH[i]); - data->temp_low[i] = lm87_read_value(client, - LM87_REG_TEMP_LOW[i]); - } - - i = lm87_read_value(client, LM87_REG_TEMP_HW_INT_LOCK); - j = lm87_read_value(client, LM87_REG_TEMP_HW_INT); - data->temp_crit_int = min(i, j); - - i = lm87_read_value(client, LM87_REG_TEMP_HW_EXT_LOCK); - j = lm87_read_value(client, LM87_REG_TEMP_HW_EXT); - data->temp_crit_ext = min(i, j); - - i = lm87_read_value(client, LM87_REG_VID_FAN_DIV); - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = (i >> 6) & 0x03; - data->vid = (i & 0x0F) - | (lm87_read_value(client, LM87_REG_VID4) & 0x01) - << 4; - - data->alarms = lm87_read_value(client, LM87_REG_ALARMS1) - | (lm87_read_value(client, LM87_REG_ALARMS2) - << 8); - data->aout = lm87_read_value(client, LM87_REG_AOUT); - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_lm87_init(void) -{ - return i2c_add_driver(&lm87_driver); -} - -static void __exit sensors_lm87_exit(void) -{ - i2c_del_driver(&lm87_driver); -} - -MODULE_AUTHOR("Jean Delvare and others"); -MODULE_DESCRIPTION("LM87 driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_lm87_init); -module_exit(sensors_lm87_exit); diff --git a/drivers/i2c/chips/lm90.c b/drivers/i2c/chips/lm90.c deleted file mode 100644 index a67dcadf7cb0..000000000000 --- a/drivers/i2c/chips/lm90.c +++ /dev/null @@ -1,655 +0,0 @@ -/* - * lm90.c - Part of lm_sensors, Linux kernel modules for hardware - * monitoring - * Copyright (C) 2003-2005 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 - * one external one) with a 0.125 deg resolution (1 deg for local - * temperature) and a 3-4 deg accuracy. Complete datasheet can be - * obtained from National's website at: - * http://www.national.com/pf/LM/LM90.html - * - * This driver also supports the LM89 and LM99, two other sensor chips - * made by National Semiconductor. Both have an increased remote - * temperature measurement accuracy (1 degree), and the LM99 - * additionally shifts remote temperatures (measured and limits) by 16 - * degrees, which allows for higher temperatures measurement. The - * driver doesn't handle it since it can be done easily in user-space. - * Complete datasheets can be obtained from National's website at: - * http://www.national.com/pf/LM/LM89.html - * http://www.national.com/pf/LM/LM99.html - * Note that there is no way to differentiate between both chips. - * - * This driver also supports the LM86, another sensor chip made by - * National Semiconductor. It is exactly similar to the LM90 except it - * has a higher accuracy. - * Complete datasheet can be obtained from National's website at: - * http://www.national.com/pf/LM/LM86.html - * - * This driver also supports the ADM1032, a sensor chip made by Analog - * Devices. That chip is similar to the LM90, with a few differences - * that are not handled by this driver. Complete datasheet can be - * obtained from Analog's website at: - * http://products.analog.com/products/info.asp?product=ADM1032 - * Among others, it has a higher accuracy than the LM90, much like the - * LM86 does. - * - * This driver also supports the MAX6657, MAX6658 and MAX6659 sensor - * chips made by Maxim. These chips are similar to the LM86. Complete - * datasheet can be obtained at Maxim's website at: - * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 - * Note that there is no easy way to differentiate between the three - * variants. The extra address and features of the MAX6659 are not - * supported by this driver. - * - * This driver also supports the ADT7461 chip from Analog Devices but - * only in its "compatability mode". If an ADT7461 chip is found but - * is configured in non-compatible mode (where its temperature - * register values are decoded differently) it is ignored by this - * driver. Complete datasheet can be obtained from Analog's website - * at: - * http://products.analog.com/products/info.asp?product=ADT7461 - * - * Since the LM90 was the first chipset supported by this driver, most - * comments will refer to this chipset, but are actually general and - * concern all supported chipsets, unless mentioned otherwise. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -/* - * Addresses to scan - * Address is fully defined internally and cannot be changed except for - * MAX6659. - * LM86, LM89, LM90, LM99, ADM1032, MAX6657 and MAX6658 have address 0x4c. - * LM89-1, and LM99-1 have address 0x4d. - * MAX6659 can have address 0x4c, 0x4d or 0x4e (unsupported). - * ADT7461 always has address 0x4c. - */ - -static unsigned short normal_i2c[] = { 0x4c, 0x4d, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* - * Insmod parameters - */ - -SENSORS_INSMOD_6(lm90, adm1032, lm99, lm86, max6657, adt7461); - -/* - * The LM90 registers - */ - -#define LM90_REG_R_MAN_ID 0xFE -#define LM90_REG_R_CHIP_ID 0xFF -#define LM90_REG_R_CONFIG1 0x03 -#define LM90_REG_W_CONFIG1 0x09 -#define LM90_REG_R_CONFIG2 0xBF -#define LM90_REG_W_CONFIG2 0xBF -#define LM90_REG_R_CONVRATE 0x04 -#define LM90_REG_W_CONVRATE 0x0A -#define LM90_REG_R_STATUS 0x02 -#define LM90_REG_R_LOCAL_TEMP 0x00 -#define LM90_REG_R_LOCAL_HIGH 0x05 -#define LM90_REG_W_LOCAL_HIGH 0x0B -#define LM90_REG_R_LOCAL_LOW 0x06 -#define LM90_REG_W_LOCAL_LOW 0x0C -#define LM90_REG_R_LOCAL_CRIT 0x20 -#define LM90_REG_W_LOCAL_CRIT 0x20 -#define LM90_REG_R_REMOTE_TEMPH 0x01 -#define LM90_REG_R_REMOTE_TEMPL 0x10 -#define LM90_REG_R_REMOTE_OFFSH 0x11 -#define LM90_REG_W_REMOTE_OFFSH 0x11 -#define LM90_REG_R_REMOTE_OFFSL 0x12 -#define LM90_REG_W_REMOTE_OFFSL 0x12 -#define LM90_REG_R_REMOTE_HIGHH 0x07 -#define LM90_REG_W_REMOTE_HIGHH 0x0D -#define LM90_REG_R_REMOTE_HIGHL 0x13 -#define LM90_REG_W_REMOTE_HIGHL 0x13 -#define LM90_REG_R_REMOTE_LOWH 0x08 -#define LM90_REG_W_REMOTE_LOWH 0x0E -#define LM90_REG_R_REMOTE_LOWL 0x14 -#define LM90_REG_W_REMOTE_LOWL 0x14 -#define LM90_REG_R_REMOTE_CRIT 0x19 -#define LM90_REG_W_REMOTE_CRIT 0x19 -#define LM90_REG_R_TCRIT_HYST 0x21 -#define LM90_REG_W_TCRIT_HYST 0x21 - -/* - * Conversions and various macros - * For local temperatures and limits, critical limits and the hysteresis - * value, the LM90 uses signed 8-bit values with LSB = 1 degree Celsius. - * For remote temperatures and limits, it uses signed 11-bit values with - * LSB = 0.125 degree Celsius, left-justified in 16-bit registers. - */ - -#define TEMP1_FROM_REG(val) ((val) * 1000) -#define TEMP1_TO_REG(val) ((val) <= -128000 ? -128 : \ - (val) >= 127000 ? 127 : \ - (val) < 0 ? ((val) - 500) / 1000 : \ - ((val) + 500) / 1000) -#define TEMP2_FROM_REG(val) ((val) / 32 * 125) -#define TEMP2_TO_REG(val) ((val) <= -128000 ? 0x8000 : \ - (val) >= 127875 ? 0x7FE0 : \ - (val) < 0 ? ((val) - 62) / 125 * 32 : \ - ((val) + 62) / 125 * 32) -#define HYST_TO_REG(val) ((val) <= 0 ? 0 : (val) >= 30500 ? 31 : \ - ((val) + 500) / 1000) - -/* - * ADT7461 is almost identical to LM90 except that attempts to write - * values that are outside the range 0 < temp < 127 are treated as - * the boundary value. - */ - -#define TEMP1_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ - (val) >= 127000 ? 127 : \ - ((val) + 500) / 1000) -#define TEMP2_TO_REG_ADT7461(val) ((val) <= 0 ? 0 : \ - (val) >= 127750 ? 0x7FC0 : \ - ((val) + 125) / 250 * 64) - -/* - * Functions declaration - */ - -static int lm90_attach_adapter(struct i2c_adapter *adapter); -static int lm90_detect(struct i2c_adapter *adapter, int address, - int kind); -static void lm90_init_client(struct i2c_client *client); -static int lm90_detach_client(struct i2c_client *client); -static struct lm90_data *lm90_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static struct i2c_driver lm90_driver = { - .owner = THIS_MODULE, - .name = "lm90", - .id = I2C_DRIVERID_LM90, - .flags = I2C_DF_NOTIFY, - .attach_adapter = lm90_attach_adapter, - .detach_client = lm90_detach_client, -}; - -/* - * Client data (each client gets its own) - */ - -struct lm90_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - int kind; - - /* registers values */ - s8 temp8[5]; /* 0: local input - 1: local low limit - 2: local high limit - 3: local critical limit - 4: remote critical limit */ - s16 temp11[3]; /* 0: remote input - 1: remote low limit - 2: remote high limit */ - u8 temp_hyst; - u8 alarms; /* bitvector */ -}; - -/* - * Sysfs stuff - */ - -static ssize_t show_temp8(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index])); -} - -static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - static const u8 reg[4] = { - LM90_REG_W_LOCAL_LOW, - LM90_REG_W_LOCAL_HIGH, - LM90_REG_W_LOCAL_CRIT, - LM90_REG_W_REMOTE_CRIT, - }; - - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct lm90_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - int nr = attr->index; - - down(&data->update_lock); - if (data->kind == adt7461) - data->temp8[nr] = TEMP1_TO_REG_ADT7461(val); - else - data->temp8[nr] = TEMP1_TO_REG(val); - i2c_smbus_write_byte_data(client, reg[nr - 1], data->temp8[nr]); - up(&data->update_lock); - return count; -} - -static ssize_t show_temp11(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", TEMP2_FROM_REG(data->temp11[attr->index])); -} - -static ssize_t set_temp11(struct device *dev, struct device_attribute *devattr, - const char *buf, size_t count) -{ - static const u8 reg[4] = { - LM90_REG_W_REMOTE_LOWH, - LM90_REG_W_REMOTE_LOWL, - LM90_REG_W_REMOTE_HIGHH, - LM90_REG_W_REMOTE_HIGHL, - }; - - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct i2c_client *client = to_i2c_client(dev); - struct lm90_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - int nr = attr->index; - - down(&data->update_lock); - if (data->kind == adt7461) - data->temp11[nr] = TEMP2_TO_REG_ADT7461(val); - else - data->temp11[nr] = TEMP2_TO_REG(val); - i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2], - data->temp11[nr] >> 8); - i2c_smbus_write_byte_data(client, reg[(nr - 1) * 2 + 1], - data->temp11[nr] & 0xff); - up(&data->update_lock); - return count; -} - -static ssize_t show_temphyst(struct device *dev, struct device_attribute *devattr, - char *buf) -{ - struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); - struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp8[attr->index]) - - TEMP1_FROM_REG(data->temp_hyst)); -} - -static ssize_t set_temphyst(struct device *dev, struct device_attribute *dummy, - const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm90_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - long hyst; - - down(&data->update_lock); - hyst = TEMP1_FROM_REG(data->temp8[3]) - val; - i2c_smbus_write_byte_data(client, LM90_REG_W_TCRIT_HYST, - HYST_TO_REG(hyst)); - up(&data->update_lock); - return count; -} - -static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy, - char *buf) -{ - struct lm90_data *data = lm90_update_device(dev); - return sprintf(buf, "%d\n", data->alarms); -} - -static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0); -static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0); -static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 1); -static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 1); -static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 2); -static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp11, - set_temp11, 2); -static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 3); -static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8, - set_temp8, 4); -static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst, - set_temphyst, 3); -static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4); -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -/* - * Real code - */ - -static int lm90_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, lm90_detect); -} - -/* - * The following function does more than just detection. If detection - * succeeds, it also registers the new chip. - */ -static int lm90_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct lm90_data *data; - int err = 0; - const char *name = ""; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - if (!(data = kmalloc(sizeof(struct lm90_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct lm90_data)); - - /* The common I2C client data is placed right before the - LM90-specific data. */ - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm90_driver; - new_client->flags = 0; - - /* - * 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 */ - u8 man_id, chip_id, reg_config1, reg_convrate; - - man_id = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_MAN_ID); - chip_id = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CHIP_ID); - reg_config1 = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CONFIG1); - reg_convrate = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CONVRATE); - - if (man_id == 0x01) { /* National Semiconductor */ - u8 reg_config2; - - reg_config2 = i2c_smbus_read_byte_data(new_client, - LM90_REG_R_CONFIG2); - - 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; - } else - if (address == 0x4C - && (chip_id & 0xF0) == 0x10) { /* LM86 */ - kind = lm86; - } - } - } else - if (man_id == 0x41) { /* Analog Devices */ - if (address == 0x4C - && (chip_id & 0xF0) == 0x40 /* ADM1032 */ - && (reg_config1 & 0x3F) == 0x00 - && reg_convrate <= 0x0A) { - kind = adm1032; - } else - if (address == 0x4c - && chip_id == 0x51 /* ADT7461 */ - && (reg_config1 & 0x1F) == 0x00 /* check compat mode */ - && reg_convrate <= 0x0A) { - kind = adt7461; - } - } else - if (man_id == 0x4D) { /* Maxim */ - /* - * The Maxim variants 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 - && (reg_config1 & 0x1F) == (man_id & 0x0F) - && reg_convrate <= 0x09) { - kind = max6657; - } - } - - if (kind <= 0) { /* identification failed */ - dev_info(&adapter->dev, - "Unsupported chip (man_id=0x%02X, " - "chip_id=0x%02X).\n", man_id, chip_id); - goto exit_free; - } - } - - if (kind == lm90) { - name = "lm90"; - } else if (kind == adm1032) { - name = "adm1032"; - } else if (kind == lm99) { - name = "lm99"; - } else if (kind == lm86) { - name = "lm86"; - } else if (kind == max6657) { - name = "max6657"; - } else if (kind == adt7461) { - name = "adt7461"; - } - - /* We can fill in the remaining client fields */ - strlcpy(new_client->name, name, I2C_NAME_SIZE); - data->valid = 0; - data->kind = kind; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the LM90 chip */ - lm90_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_input.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_min.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_max.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_crit.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_crit.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp1_crit_hyst.dev_attr); - device_create_file(&new_client->dev, - &sensor_dev_attr_temp2_crit_hyst.dev_attr); - device_create_file(&new_client->dev, &dev_attr_alarms); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static void lm90_init_client(struct i2c_client *client) -{ - u8 config; - - /* - * Start the conversions. - */ - i2c_smbus_write_byte_data(client, LM90_REG_W_CONVRATE, - 5); /* 2 Hz */ - config = i2c_smbus_read_byte_data(client, LM90_REG_R_CONFIG1); - if (config & 0x40) - i2c_smbus_write_byte_data(client, LM90_REG_W_CONFIG1, - config & 0xBF); /* run */ -} - -static int lm90_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - -static struct lm90_data *lm90_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm90_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - u8 oldh, newh; - - dev_dbg(&client->dev, "Updating lm90 data.\n"); - data->temp8[0] = i2c_smbus_read_byte_data(client, - LM90_REG_R_LOCAL_TEMP); - data->temp8[1] = i2c_smbus_read_byte_data(client, - LM90_REG_R_LOCAL_LOW); - data->temp8[2] = i2c_smbus_read_byte_data(client, - LM90_REG_R_LOCAL_HIGH); - data->temp8[3] = i2c_smbus_read_byte_data(client, - LM90_REG_R_LOCAL_CRIT); - data->temp8[4] = i2c_smbus_read_byte_data(client, - LM90_REG_R_REMOTE_CRIT); - data->temp_hyst = i2c_smbus_read_byte_data(client, - LM90_REG_R_TCRIT_HYST); - - /* - * There is a trick here. We have to read two registers to - * have the remote sensor temperature, but we have to beware - * a conversion could occur inbetween the readings. The - * datasheet says we should either use the one-shot - * conversion register, which we don't want to do (disables - * hardware monitoring) or monitor the busy bit, which is - * impossible (we can't read the values and monitor that bit - * at the exact same time). So the solution used here is to - * read the high byte once, then the low byte, then the high - * byte again. If the new high byte matches the old one, - * then we have a valid reading. Else we have to read the low - * byte again, and now we believe we have a correct reading. - */ - oldh = i2c_smbus_read_byte_data(client, - LM90_REG_R_REMOTE_TEMPH); - data->temp11[0] = i2c_smbus_read_byte_data(client, - LM90_REG_R_REMOTE_TEMPL); - newh = i2c_smbus_read_byte_data(client, - LM90_REG_R_REMOTE_TEMPH); - if (newh != oldh) { - data->temp11[0] = i2c_smbus_read_byte_data(client, - LM90_REG_R_REMOTE_TEMPL); -#ifdef DEBUG - oldh = i2c_smbus_read_byte_data(client, - LM90_REG_R_REMOTE_TEMPH); - /* oldh is actually newer */ - if (newh != oldh) - dev_warn(&client->dev, "Remote temperature may be " - "wrong.\n"); -#endif - } - data->temp11[0] |= (newh << 8); - - data->temp11[1] = (i2c_smbus_read_byte_data(client, - LM90_REG_R_REMOTE_LOWH) << 8) + - i2c_smbus_read_byte_data(client, - LM90_REG_R_REMOTE_LOWL); - data->temp11[2] = (i2c_smbus_read_byte_data(client, - LM90_REG_R_REMOTE_HIGHH) << 8) + - i2c_smbus_read_byte_data(client, - LM90_REG_R_REMOTE_HIGHL); - data->alarms = i2c_smbus_read_byte_data(client, - LM90_REG_R_STATUS); - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_lm90_init(void) -{ - return i2c_add_driver(&lm90_driver); -} - -static void __exit sensors_lm90_exit(void) -{ - i2c_del_driver(&lm90_driver); -} - -MODULE_AUTHOR("Jean Delvare "); -MODULE_DESCRIPTION("LM90/ADM1032 driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_lm90_init); -module_exit(sensors_lm90_exit); diff --git a/drivers/i2c/chips/lm92.c b/drivers/i2c/chips/lm92.c deleted file mode 100644 index 215c8e40ffdd..000000000000 --- a/drivers/i2c/chips/lm92.c +++ /dev/null @@ -1,429 +0,0 @@ -/* - * lm92 - Hardware monitoring driver - * Copyright (C) 2005 Jean Delvare - * - * Based on the lm90 driver, with some ideas taken from the lm_sensors - * lm92 driver as well. - * - * The LM92 is a sensor chip made by National Semiconductor. It reports - * its own temperature with a 0.0625 deg resolution and a 0.33 deg - * accuracy. Complete datasheet can be obtained from National's website - * at: - * http://www.national.com/pf/LM/LM92.html - * - * This driver also supports the MAX6635 sensor chip made by Maxim. - * This chip is compatible with the LM92, but has a lesser accuracy - * (1.0 deg). Complete datasheet can be obtained from Maxim's website - * at: - * http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3074 - * - * Since the LM92 was the first chipset supported by this driver, most - * comments will refer to this chipset, but are actually general and - * concern all supported chipsets, unless mentioned otherwise. - * - * Support could easily be added for the National Semiconductor LM76 - * and Maxim MAX6633 and MAX6634 chips, which are mostly compatible - * with the LM92. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include - - -/* The LM92 and MAX6635 have 2 two-state pins for address selection, - resulting in 4 possible addresses. */ -static unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, - I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_1(lm92); - -/* The LM92 registers */ -#define LM92_REG_CONFIG 0x01 /* 8-bit, RW */ -#define LM92_REG_TEMP 0x00 /* 16-bit, RO */ -#define LM92_REG_TEMP_HYST 0x02 /* 16-bit, RW */ -#define LM92_REG_TEMP_CRIT 0x03 /* 16-bit, RW */ -#define LM92_REG_TEMP_LOW 0x04 /* 16-bit, RW */ -#define LM92_REG_TEMP_HIGH 0x05 /* 16-bit, RW */ -#define LM92_REG_MAN_ID 0x07 /* 16-bit, RO, LM92 only */ - -/* The LM92 uses signed 13-bit values with LSB = 0.0625 degree Celsius, - left-justified in 16-bit registers. No rounding is done, with such - a resolution it's just not worth it. Note that the MAX6635 doesn't - make use of the 4 lower bits for limits (i.e. effective resolution - for limits is 1 degree Celsius). */ -static inline int TEMP_FROM_REG(s16 reg) -{ - return reg / 8 * 625 / 10; -} - -static inline s16 TEMP_TO_REG(int val) -{ - if (val <= -60000) - return -60000 * 10 / 625 * 8; - if (val >= 160000) - return 160000 * 10 / 625 * 8; - return val * 10 / 625 * 8; -} - -/* Alarm flags are stored in the 3 LSB of the temperature register */ -static inline u8 ALARMS_FROM_REG(s16 reg) -{ - return reg & 0x0007; -} - -/* Driver data (common to all clients) */ -static struct i2c_driver lm92_driver; - -/* Client data (each client gets its own) */ -struct lm92_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* registers values */ - s16 temp1_input, temp1_crit, temp1_min, temp1_max, temp1_hyst; -}; - - -/* - * Sysfs attributes and callback functions - */ - -static struct lm92_data *lm92_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm92_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ) - || !data->valid) { - dev_dbg(&client->dev, "Updating lm92 data\n"); - data->temp1_input = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP)); - data->temp1_hyst = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP_HYST)); - data->temp1_crit = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP_CRIT)); - data->temp1_min = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP_LOW)); - data->temp1_max = swab16(i2c_smbus_read_word_data(client, - LM92_REG_TEMP_HIGH)); - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -#define show_temp(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct lm92_data *data = lm92_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ -} -show_temp(temp1_input); -show_temp(temp1_crit); -show_temp(temp1_min); -show_temp(temp1_max); - -#define set_temp(value, reg) \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct lm92_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->value = TEMP_TO_REG(val); \ - i2c_smbus_write_word_data(client, reg, swab16(data->value)); \ - up(&data->update_lock); \ - return count; \ -} -set_temp(temp1_crit, LM92_REG_TEMP_CRIT); -set_temp(temp1_min, LM92_REG_TEMP_LOW); -set_temp(temp1_max, LM92_REG_TEMP_HIGH); - -static ssize_t show_temp1_crit_hyst(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm92_data *data = lm92_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_crit) - - TEMP_FROM_REG(data->temp1_hyst)); -} -static ssize_t show_temp1_max_hyst(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm92_data *data = lm92_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_max) - - TEMP_FROM_REG(data->temp1_hyst)); -} -static ssize_t show_temp1_min_hyst(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm92_data *data = lm92_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp1_min) - + TEMP_FROM_REG(data->temp1_hyst)); -} - -static ssize_t set_temp1_crit_hyst(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct lm92_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp1_hyst = TEMP_FROM_REG(data->temp1_crit) - val; - i2c_smbus_write_word_data(client, LM92_REG_TEMP_HYST, - swab16(TEMP_TO_REG(data->temp1_hyst))); - up(&data->update_lock); - return count; -} - -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct lm92_data *data = lm92_update_device(dev); - return sprintf(buf, "%d\n", ALARMS_FROM_REG(data->temp1_input)); -} - -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1_input, NULL); -static DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, show_temp1_crit, - set_temp1_crit); -static DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temp1_crit_hyst, - set_temp1_crit_hyst); -static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp1_min, - set_temp1_min); -static DEVICE_ATTR(temp1_min_hyst, S_IRUGO, show_temp1_min_hyst, NULL); -static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp1_max, - set_temp1_max); -static DEVICE_ATTR(temp1_max_hyst, S_IRUGO, show_temp1_max_hyst, NULL); -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - - -/* - * Detection and registration - */ - -static void lm92_init_client(struct i2c_client *client) -{ - u8 config; - - /* Start the conversions if needed */ - config = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG); - if (config & 0x01) - i2c_smbus_write_byte_data(client, LM92_REG_CONFIG, - config & 0xFE); -} - -/* The MAX6635 has no identification register, so we have to use tricks - to identify it reliably. This is somewhat slow. - Note that we do NOT rely on the 2 MSB of the configuration register - always reading 0, as suggested by the datasheet, because it was once - reported not to be true. */ -static int max6635_check(struct i2c_client *client) -{ - u16 temp_low, temp_high, temp_hyst, temp_crit; - u8 conf; - int i; - - /* No manufacturer ID register, so a read from this address will - always return the last read value. */ - temp_low = i2c_smbus_read_word_data(client, LM92_REG_TEMP_LOW); - if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_low) - return 0; - temp_high = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HIGH); - if (i2c_smbus_read_word_data(client, LM92_REG_MAN_ID) != temp_high) - return 0; - - /* Limits are stored as integer values (signed, 9-bit). */ - if ((temp_low & 0x7f00) || (temp_high & 0x7f00)) - return 0; - temp_hyst = i2c_smbus_read_word_data(client, LM92_REG_TEMP_HYST); - temp_crit = i2c_smbus_read_word_data(client, LM92_REG_TEMP_CRIT); - if ((temp_hyst & 0x7f00) || (temp_crit & 0x7f00)) - return 0; - - /* Registers addresses were found to cycle over 16-byte boundaries. - We don't test all registers with all offsets so as to save some - reads and time, but this should still be sufficient to dismiss - non-MAX6635 chips. */ - conf = i2c_smbus_read_byte_data(client, LM92_REG_CONFIG); - for (i=16; i<96; i*=2) { - if (temp_hyst != i2c_smbus_read_word_data(client, - LM92_REG_TEMP_HYST + i - 16) - || temp_crit != i2c_smbus_read_word_data(client, - LM92_REG_TEMP_CRIT + i) - || temp_low != i2c_smbus_read_word_data(client, - LM92_REG_TEMP_LOW + i + 16) - || temp_high != i2c_smbus_read_word_data(client, - LM92_REG_TEMP_HIGH + i + 32) - || conf != i2c_smbus_read_byte_data(client, - LM92_REG_CONFIG + i)) - return 0; - } - - return 1; -} - -/* The following function does more than just detection. If detection - succeeds, it also registers the new chip. */ -static int lm92_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct lm92_data *data; - int err = 0; - char *name; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA - | I2C_FUNC_SMBUS_WORD_DATA)) - goto exit; - - if (!(data = kmalloc(sizeof(struct lm92_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct lm92_data)); - - /* Fill in enough client fields so that we can read from the chip, - which is required for identication */ - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &lm92_driver; - new_client->flags = 0; - - /* 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 - goto exit_free; - } else - if (kind == 0) /* Default to an LM92 if forced */ - kind = lm92; - - /* Give it the proper name */ - if (kind == lm92) { - name = "lm92"; - } else { /* Supposedly cannot happen */ - dev_dbg(&new_client->dev, "Kind out of range?\n"); - goto exit_free; - } - - /* Fill in the remaining client fields */ - strlcpy(new_client->name, name, I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the i2c subsystem a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the chipset */ - lm92_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_crit); - device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_alarms); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static int lm92_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, lm92_detect); -} - -static int lm92_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - - -/* - * Module and driver stuff - */ - -static struct i2c_driver lm92_driver = { - .owner = THIS_MODULE, - .name = "lm92", - .id = I2C_DRIVERID_LM92, - .flags = I2C_DF_NOTIFY, - .attach_adapter = lm92_attach_adapter, - .detach_client = lm92_detach_client, -}; - -static int __init sensors_lm92_init(void) -{ - return i2c_add_driver(&lm92_driver); -} - -static void __exit sensors_lm92_exit(void) -{ - i2c_del_driver(&lm92_driver); -} - -MODULE_AUTHOR("Jean Delvare "); -MODULE_DESCRIPTION("LM92/MAX6635 driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_lm92_init); -module_exit(sensors_lm92_exit); diff --git a/drivers/i2c/chips/max1619.c b/drivers/i2c/chips/max1619.c deleted file mode 100644 index bf553dcd97d6..000000000000 --- a/drivers/i2c/chips/max1619.c +++ /dev/null @@ -1,372 +0,0 @@ -/* - * max1619.c - Part of lm_sensors, Linux kernel modules for hardware - * monitoring - * Copyright (C) 2003-2004 Alexey Fisher - * Jean Delvare - * - * Based on the lm90 driver. The MAX1619 is a sensor chip made by Maxim. - * It reports up to two temperatures (its own plus up to - * one external one). Complete datasheet can be - * obtained from Maxim's website at: - * http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - - -#include -#include -#include -#include -#include -#include - - -static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a, - 0x29, 0x2a, 0x2b, - 0x4c, 0x4d, 0x4e, - I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* - * Insmod parameters - */ - -SENSORS_INSMOD_1(max1619); - -/* - * The MAX1619 registers - */ - -#define MAX1619_REG_R_MAN_ID 0xFE -#define MAX1619_REG_R_CHIP_ID 0xFF -#define MAX1619_REG_R_CONFIG 0x03 -#define MAX1619_REG_W_CONFIG 0x09 -#define MAX1619_REG_R_CONVRATE 0x04 -#define MAX1619_REG_W_CONVRATE 0x0A -#define MAX1619_REG_R_STATUS 0x02 -#define MAX1619_REG_R_LOCAL_TEMP 0x00 -#define MAX1619_REG_R_REMOTE_TEMP 0x01 -#define MAX1619_REG_R_REMOTE_HIGH 0x07 -#define MAX1619_REG_W_REMOTE_HIGH 0x0D -#define MAX1619_REG_R_REMOTE_LOW 0x08 -#define MAX1619_REG_W_REMOTE_LOW 0x0E -#define MAX1619_REG_R_REMOTE_CRIT 0x10 -#define MAX1619_REG_W_REMOTE_CRIT 0x12 -#define MAX1619_REG_R_TCRIT_HYST 0x11 -#define MAX1619_REG_W_TCRIT_HYST 0x13 - -/* - * Conversions and various macros - */ - -#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000) -#define TEMP_TO_REG(val) ((val < 0 ? val+0x100*1000 : val) / 1000) - -/* - * Functions declaration - */ - -static int max1619_attach_adapter(struct i2c_adapter *adapter); -static int max1619_detect(struct i2c_adapter *adapter, int address, - int kind); -static void max1619_init_client(struct i2c_client *client); -static int max1619_detach_client(struct i2c_client *client); -static struct max1619_data *max1619_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static struct i2c_driver max1619_driver = { - .owner = THIS_MODULE, - .name = "max1619", - .flags = I2C_DF_NOTIFY, - .attach_adapter = max1619_attach_adapter, - .detach_client = max1619_detach_client, -}; - -/* - * Client data (each client gets its own) - */ - -struct max1619_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* registers values */ - u8 temp_input1; /* local */ - u8 temp_input2, temp_low2, temp_high2; /* remote */ - u8 temp_crit2; - u8 temp_hyst2; - u8 alarms; -}; - -/* - * Sysfs stuff - */ - -#define show_temp(value) \ -static ssize_t show_##value(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct max1619_data *data = max1619_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->value)); \ -} -show_temp(temp_input1); -show_temp(temp_input2); -show_temp(temp_low2); -show_temp(temp_high2); -show_temp(temp_crit2); -show_temp(temp_hyst2); - -#define set_temp2(value, reg) \ -static ssize_t set_##value(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct max1619_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->value = TEMP_TO_REG(val); \ - i2c_smbus_write_byte_data(client, reg, data->value); \ - up(&data->update_lock); \ - return count; \ -} - -set_temp2(temp_low2, MAX1619_REG_W_REMOTE_LOW); -set_temp2(temp_high2, MAX1619_REG_W_REMOTE_HIGH); -set_temp2(temp_crit2, MAX1619_REG_W_REMOTE_CRIT); -set_temp2(temp_hyst2, MAX1619_REG_W_TCRIT_HYST); - -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct max1619_data *data = max1619_update_device(dev); - return sprintf(buf, "%d\n", data->alarms); -} - -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp_input1, NULL); -static DEVICE_ATTR(temp2_input, S_IRUGO, show_temp_input2, NULL); -static DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_low2, - set_temp_low2); -static DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_high2, - set_temp_high2); -static DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp_crit2, - set_temp_crit2); -static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst2, - set_temp_hyst2); -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -/* - * Real code - */ - -static int max1619_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, max1619_detect); -} - -/* - * The following function does more than just detection. If detection - * succeeds, it also registers the new chip. - */ -static int max1619_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct max1619_data *data; - int err = 0; - const char *name = ""; - u8 reg_config=0, reg_convrate=0, reg_status=0; - u8 man_id, chip_id; - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - if (!(data = kmalloc(sizeof(struct max1619_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct max1619_data)); - - /* The common I2C client data is placed right before the - MAX1619-specific data. */ - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &max1619_driver; - new_client->flags = 0; - - /* - * 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", - address); - goto exit_free; - } - } - - if (kind <= 0) { /* identification */ - - 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); - goto exit_free; - } - - - if (kind == max1619){ - name = "max1619"; - } - - /* We can fill in the remaining client fields */ - strlcpy(new_client->name, name, I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the MAX1619 chip */ - max1619_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp2_crit); - device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst); - device_create_file(&new_client->dev, &dev_attr_alarms); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static void max1619_init_client(struct i2c_client *client) -{ - u8 config; - - /* - * Start the conversions. - */ - i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONVRATE, - 5); /* 2 Hz */ - config = i2c_smbus_read_byte_data(client, MAX1619_REG_R_CONFIG); - if (config & 0x40) - i2c_smbus_write_byte_data(client, MAX1619_REG_W_CONFIG, - config & 0xBF); /* run */ -} - -static int max1619_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - -static struct max1619_data *max1619_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct max1619_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - dev_dbg(&client->dev, "Updating max1619 data.\n"); - data->temp_input1 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_LOCAL_TEMP); - data->temp_input2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_REMOTE_TEMP); - data->temp_high2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_REMOTE_HIGH); - data->temp_low2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_REMOTE_LOW); - data->temp_crit2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_REMOTE_CRIT); - data->temp_hyst2 = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_TCRIT_HYST); - data->alarms = i2c_smbus_read_byte_data(client, - MAX1619_REG_R_STATUS); - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_max1619_init(void) -{ - return i2c_add_driver(&max1619_driver); -} - -static void __exit sensors_max1619_exit(void) -{ - i2c_del_driver(&max1619_driver); -} - -MODULE_AUTHOR("Alexey Fisher and" - "Jean Delvare "); -MODULE_DESCRIPTION("MAX1619 sensor driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_max1619_init); -module_exit(sensors_max1619_exit); diff --git a/drivers/i2c/chips/pc87360.c b/drivers/i2c/chips/pc87360.c deleted file mode 100644 index 876c68f3af31..000000000000 --- a/drivers/i2c/chips/pc87360.c +++ /dev/null @@ -1,1348 +0,0 @@ -/* - * pc87360.c - Part of lm_sensors, Linux kernel modules - * for hardware monitoring - * Copyright (C) 2004 Jean Delvare - * - * Copied from smsc47m1.c: - * Copyright (C) 2002 Mark D. Studebaker - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * Supports the following chips: - * - * Chip #vin #fan #pwm #temp devid - * PC87360 - 2 2 - 0xE1 - * PC87363 - 2 2 - 0xE8 - * PC87364 - 3 3 - 0xE4 - * PC87365 11 3 3 2 0xE5 - * PC87366 11 3 3 3-4 0xE9 - * - * This driver assumes that no more than one chip is present, and one of - * the standard Super-I/O addresses is used (0x2E/0x2F or 0x4E/0x4F). - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; -static unsigned int normal_isa[] = { 0, I2C_CLIENT_ISA_END }; -static struct i2c_force_data forces[] = {{ NULL }}; -static u8 devid; -static unsigned int extra_isa[3]; -static u8 confreg[4]; - -enum chips { any_chip, pc87360, pc87363, pc87364, pc87365, pc87366 }; -static struct i2c_address_data addr_data = { - .normal_i2c = normal_i2c, - .normal_isa = normal_isa, - .forces = forces, -}; - -static int init = 1; -module_param(init, int, 0); -MODULE_PARM_DESC(init, - "Chip initialization level:\n" - " 0: None\n" - "*1: Forcibly enable internal voltage and temperature channels, except in9\n" - " 2: Forcibly enable all voltage and temperature channels, except in9\n" - " 3: Forcibly enable all voltage and temperature channels, including in9"); - -/* - * Super-I/O registers and operations - */ - -#define DEV 0x07 /* Register: Logical device select */ -#define DEVID 0x20 /* Register: Device ID */ -#define ACT 0x30 /* Register: Device activation */ -#define BASE 0x60 /* Register: Base address */ - -#define FSCM 0x09 /* Logical device: fans */ -#define VLM 0x0d /* Logical device: voltages */ -#define TMS 0x0e /* Logical device: temperatures */ -static const u8 logdev[3] = { FSCM, VLM, TMS }; - -#define LD_FAN 0 -#define LD_IN 1 -#define LD_TEMP 2 - -static inline void superio_outb(int sioaddr, int reg, int val) -{ - outb(reg, sioaddr); - outb(val, sioaddr+1); -} - -static inline int superio_inb(int sioaddr, int reg) -{ - outb(reg, sioaddr); - return inb(sioaddr+1); -} - -static inline void superio_exit(int sioaddr) -{ - outb(0x02, sioaddr); - outb(0x02, sioaddr+1); -} - -/* - * Logical devices - */ - -#define PC87360_EXTENT 0x10 -#define PC87365_REG_BANK 0x09 -#define NO_BANK 0xff - -/* - * Fan registers and conversions - */ - -/* nr has to be 0 or 1 (PC87360/87363) or 2 (PC87364/87365/87366) */ -#define PC87360_REG_PRESCALE(nr) (0x00 + 2 * (nr)) -#define PC87360_REG_PWM(nr) (0x01 + 2 * (nr)) -#define PC87360_REG_FAN_MIN(nr) (0x06 + 3 * (nr)) -#define PC87360_REG_FAN(nr) (0x07 + 3 * (nr)) -#define PC87360_REG_FAN_STATUS(nr) (0x08 + 3 * (nr)) - -#define FAN_FROM_REG(val,div) ((val) == 0 ? 0: \ - 480000 / ((val)*(div))) -#define FAN_TO_REG(val,div) ((val) <= 100 ? 0 : \ - 480000 / ((val)*(div))) -#define FAN_DIV_FROM_REG(val) (1 << ((val >> 5) & 0x03)) -#define FAN_STATUS_FROM_REG(val) ((val) & 0x07) - -#define FAN_CONFIG_MONITOR(val,nr) (((val) >> (2 + nr * 3)) & 1) -#define FAN_CONFIG_CONTROL(val,nr) (((val) >> (3 + nr * 3)) & 1) -#define FAN_CONFIG_INVERT(val,nr) (((val) >> (4 + nr * 3)) & 1) - -#define PWM_FROM_REG(val,inv) ((inv) ? 255 - (val) : (val)) -static inline u8 PWM_TO_REG(int val, int inv) -{ - if (inv) - val = 255 - val; - if (val < 0) - return 0; - if (val > 255) - return 255; - return val; -} - -/* - * Voltage registers and conversions - */ - -#define PC87365_REG_IN_CONVRATE 0x07 -#define PC87365_REG_IN_CONFIG 0x08 -#define PC87365_REG_IN 0x0B -#define PC87365_REG_IN_MIN 0x0D -#define PC87365_REG_IN_MAX 0x0C -#define PC87365_REG_IN_STATUS 0x0A -#define PC87365_REG_IN_ALARMS1 0x00 -#define PC87365_REG_IN_ALARMS2 0x01 -#define PC87365_REG_VID 0x06 - -#define IN_FROM_REG(val,ref) (((val) * (ref) + 128) / 256) -#define IN_TO_REG(val,ref) ((val) < 0 ? 0 : \ - (val)*256 >= (ref)*255 ? 255: \ - ((val) * 256 + (ref)/2) / (ref)) - -/* - * Temperature registers and conversions - */ - -#define PC87365_REG_TEMP_CONFIG 0x08 -#define PC87365_REG_TEMP 0x0B -#define PC87365_REG_TEMP_MIN 0x0D -#define PC87365_REG_TEMP_MAX 0x0C -#define PC87365_REG_TEMP_CRIT 0x0E -#define PC87365_REG_TEMP_STATUS 0x0A -#define PC87365_REG_TEMP_ALARMS 0x00 - -#define TEMP_FROM_REG(val) ((val) * 1000) -#define TEMP_TO_REG(val) ((val) < -55000 ? -55 : \ - (val) > 127000 ? 127 : \ - (val) < 0 ? ((val) - 500) / 1000 : \ - ((val) + 500) / 1000) - -/* - * Client data (each client gets its own) - */ - -struct pc87360_data { - struct i2c_client client; - struct semaphore lock; - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - int address[3]; - - u8 fannr, innr, tempnr; - - u8 fan[3]; /* Register value */ - u8 fan_min[3]; /* Register value */ - u8 fan_status[3]; /* Register value */ - u8 pwm[3]; /* Register value */ - u16 fan_conf; /* Configuration register values, combined */ - - u16 in_vref; /* 1 mV/bit */ - u8 in[14]; /* Register value */ - u8 in_min[14]; /* Register value */ - u8 in_max[14]; /* Register value */ - u8 in_crit[3]; /* Register value */ - u8 in_status[14]; /* Register value */ - u16 in_alarms; /* Register values, combined, masked */ - u8 vid_conf; /* Configuration register value */ - u8 vrm; - u8 vid; /* Register value */ - - s8 temp[3]; /* Register value */ - s8 temp_min[3]; /* Register value */ - s8 temp_max[3]; /* Register value */ - s8 temp_crit[3]; /* Register value */ - u8 temp_status[3]; /* Register value */ - u8 temp_alarms; /* Register value, masked */ -}; - -/* - * Functions declaration - */ - -static int pc87360_attach_adapter(struct i2c_adapter *adapter); -static int pc87360_detect(struct i2c_adapter *adapter, int address, int kind); -static int pc87360_detach_client(struct i2c_client *client); - -static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg); -static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg, u8 value); -static void pc87360_init_client(struct i2c_client *client, int use_thermistors); -static struct pc87360_data *pc87360_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static struct i2c_driver pc87360_driver = { - .owner = THIS_MODULE, - .name = "pc87360", - .flags = I2C_DF_NOTIFY, - .attach_adapter = pc87360_attach_adapter, - .detach_client = pc87360_detach_client, -}; - -/* - * Sysfs stuff - */ - -static ssize_t set_fan_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pc87360_data *data = i2c_get_clientdata(client); - long fan_min = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - fan_min = FAN_TO_REG(fan_min, FAN_DIV_FROM_REG(data->fan_status[nr])); - - /* If it wouldn't fit, change clock divisor */ - while (fan_min > 255 - && (data->fan_status[nr] & 0x60) != 0x60) { - fan_min >>= 1; - data->fan[nr] >>= 1; - data->fan_status[nr] += 0x20; - } - data->fan_min[nr] = fan_min > 255 ? 255 : fan_min; - pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_MIN(nr), - data->fan_min[nr]); - - /* Write new divider, preserve alarm bits */ - pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_FAN_STATUS(nr), - data->fan_status[nr] & 0xF9); - up(&data->update_lock); - - return count; -} - -#define show_and_set_fan(offset) \ -static ssize_t show_fan##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan[offset-1], \ - FAN_DIV_FROM_REG(data->fan_status[offset-1]))); \ -} \ -static ssize_t show_fan##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", FAN_FROM_REG(data->fan_min[offset-1], \ - FAN_DIV_FROM_REG(data->fan_status[offset-1]))); \ -} \ -static ssize_t show_fan##offset##_div(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", \ - FAN_DIV_FROM_REG(data->fan_status[offset-1])); \ -} \ -static ssize_t show_fan##offset##_status(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", \ - FAN_STATUS_FROM_REG(data->fan_status[offset-1])); \ -} \ -static ssize_t set_fan##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - return set_fan_min(dev, buf, count, offset-1); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ - show_fan##offset##_input, NULL); \ -static DEVICE_ATTR(fan##offset##_min, S_IWUSR | S_IRUGO, \ - show_fan##offset##_min, set_fan##offset##_min); \ -static DEVICE_ATTR(fan##offset##_div, S_IRUGO, \ - show_fan##offset##_div, NULL); \ -static DEVICE_ATTR(fan##offset##_status, S_IRUGO, \ - show_fan##offset##_status, NULL); -show_and_set_fan(1) -show_and_set_fan(2) -show_and_set_fan(3) - -#define show_and_set_pwm(offset) \ -static ssize_t show_pwm##offset(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", \ - PWM_FROM_REG(data->pwm[offset-1], \ - FAN_CONFIG_INVERT(data->fan_conf, \ - offset-1))); \ -} \ -static ssize_t set_pwm##offset(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct pc87360_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->pwm[offset-1] = PWM_TO_REG(val, \ - FAN_CONFIG_INVERT(data->fan_conf, offset-1)); \ - pc87360_write_value(data, LD_FAN, NO_BANK, PC87360_REG_PWM(offset-1), \ - data->pwm[offset-1]); \ - up(&data->update_lock); \ - return count; \ -} \ -static DEVICE_ATTR(pwm##offset, S_IWUSR | S_IRUGO, \ - show_pwm##offset, set_pwm##offset); -show_and_set_pwm(1) -show_and_set_pwm(2) -show_and_set_pwm(3) - -#define show_and_set_in(offset) \ -static ssize_t show_in##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset], \ - data->in_vref)); \ -} \ -static ssize_t show_in##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset], \ - data->in_vref)); \ -} \ -static ssize_t show_in##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset], \ - data->in_vref)); \ -} \ -static ssize_t show_in##offset##_status(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", data->in_status[offset]); \ -} \ -static ssize_t set_in##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct pc87360_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->in_min[offset] = IN_TO_REG(val, data->in_vref); \ - pc87360_write_value(data, LD_IN, offset, PC87365_REG_IN_MIN, \ - data->in_min[offset]); \ - up(&data->update_lock); \ - return count; \ -} \ -static ssize_t set_in##offset##_max(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct pc87360_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->in_max[offset] = IN_TO_REG(val, \ - data->in_vref); \ - pc87360_write_value(data, LD_IN, offset, PC87365_REG_IN_MAX, \ - data->in_max[offset]); \ - up(&data->update_lock); \ - return count; \ -} \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ - show_in##offset##_input, NULL); \ -static DEVICE_ATTR(in##offset##_min, S_IWUSR | S_IRUGO, \ - show_in##offset##_min, set_in##offset##_min); \ -static DEVICE_ATTR(in##offset##_max, S_IWUSR | S_IRUGO, \ - show_in##offset##_max, set_in##offset##_max); \ -static DEVICE_ATTR(in##offset##_status, S_IRUGO, \ - show_in##offset##_status, NULL); -show_and_set_in(0) -show_and_set_in(1) -show_and_set_in(2) -show_and_set_in(3) -show_and_set_in(4) -show_and_set_in(5) -show_and_set_in(6) -show_and_set_in(7) -show_and_set_in(8) -show_and_set_in(9) -show_and_set_in(10) - -#define show_and_set_therm(offset) \ -static ssize_t show_temp##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in[offset+7], \ - data->in_vref)); \ -} \ -static ssize_t show_temp##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_min[offset+7], \ - data->in_vref)); \ -} \ -static ssize_t show_temp##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_max[offset+7], \ - data->in_vref)); \ -} \ -static ssize_t show_temp##offset##_crit(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", IN_FROM_REG(data->in_crit[offset-4], \ - data->in_vref)); \ -} \ -static ssize_t show_temp##offset##_status(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%u\n", data->in_status[offset+7]); \ -} \ -static ssize_t set_temp##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct pc87360_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->in_min[offset+7] = IN_TO_REG(val, data->in_vref); \ - pc87360_write_value(data, LD_IN, offset+7, PC87365_REG_TEMP_MIN, \ - data->in_min[offset+7]); \ - up(&data->update_lock); \ - return count; \ -} \ -static ssize_t set_temp##offset##_max(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct pc87360_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->in_max[offset+7] = IN_TO_REG(val, data->in_vref); \ - pc87360_write_value(data, LD_IN, offset+7, PC87365_REG_TEMP_MAX, \ - data->in_max[offset+7]); \ - up(&data->update_lock); \ - return count; \ -} \ -static ssize_t set_temp##offset##_crit(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct pc87360_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->in_crit[offset-4] = IN_TO_REG(val, data->in_vref); \ - pc87360_write_value(data, LD_IN, offset+7, PC87365_REG_TEMP_CRIT, \ - data->in_crit[offset-4]); \ - up(&data->update_lock); \ - return count; \ -} \ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ - show_temp##offset##_input, NULL); \ -static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \ - show_temp##offset##_min, set_temp##offset##_min); \ -static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \ - show_temp##offset##_max, set_temp##offset##_max); \ -static DEVICE_ATTR(temp##offset##_crit, S_IWUSR | S_IRUGO, \ - show_temp##offset##_crit, set_temp##offset##_crit); \ -static DEVICE_ATTR(temp##offset##_status, S_IRUGO, \ - show_temp##offset##_status, NULL); -show_and_set_therm(4) -show_and_set_therm(5) -show_and_set_therm(6) - -static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm)); -} -static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL); - -static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->vrm); -} -static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pc87360_data *data = i2c_get_clientdata(client); - data->vrm = simple_strtoul(buf, NULL, 10); - return count; -} -static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm); - -static ssize_t show_in_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->in_alarms); -} -static DEVICE_ATTR(alarms_in, S_IRUGO, show_in_alarms, NULL); - -#define show_and_set_temp(offset) \ -static ssize_t show_temp##offset##_input(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp[offset-1])); \ -} \ -static ssize_t show_temp##offset##_min(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_min[offset-1])); \ -} \ -static ssize_t show_temp##offset##_max(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_max[offset-1])); \ -}\ -static ssize_t show_temp##offset##_crit(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_crit[offset-1])); \ -}\ -static ssize_t show_temp##offset##_status(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct pc87360_data *data = pc87360_update_device(dev); \ - return sprintf(buf, "%d\n", data->temp_status[offset-1]); \ -}\ -static ssize_t set_temp##offset##_min(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct pc87360_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->temp_min[offset-1] = TEMP_TO_REG(val); \ - pc87360_write_value(data, LD_TEMP, offset-1, PC87365_REG_TEMP_MIN, \ - data->temp_min[offset-1]); \ - up(&data->update_lock); \ - return count; \ -} \ -static ssize_t set_temp##offset##_max(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct pc87360_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->temp_max[offset-1] = TEMP_TO_REG(val); \ - pc87360_write_value(data, LD_TEMP, offset-1, PC87365_REG_TEMP_MAX, \ - data->temp_max[offset-1]); \ - up(&data->update_lock); \ - return count; \ -} \ -static ssize_t set_temp##offset##_crit(struct device *dev, struct device_attribute *attr, const char *buf, \ - size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct pc87360_data *data = i2c_get_clientdata(client); \ - long val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->temp_crit[offset-1] = TEMP_TO_REG(val); \ - pc87360_write_value(data, LD_TEMP, offset-1, PC87365_REG_TEMP_CRIT, \ - data->temp_crit[offset-1]); \ - up(&data->update_lock); \ - return count; \ -} \ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ - show_temp##offset##_input, NULL); \ -static DEVICE_ATTR(temp##offset##_min, S_IWUSR | S_IRUGO, \ - show_temp##offset##_min, set_temp##offset##_min); \ -static DEVICE_ATTR(temp##offset##_max, S_IWUSR | S_IRUGO, \ - show_temp##offset##_max, set_temp##offset##_max); \ -static DEVICE_ATTR(temp##offset##_crit, S_IWUSR | S_IRUGO, \ - show_temp##offset##_crit, set_temp##offset##_crit); \ -static DEVICE_ATTR(temp##offset##_status, S_IRUGO, \ - show_temp##offset##_status, NULL); -show_and_set_temp(1) -show_and_set_temp(2) -show_and_set_temp(3) - -static ssize_t show_temp_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct pc87360_data *data = pc87360_update_device(dev); - return sprintf(buf, "%u\n", data->temp_alarms); -} -static DEVICE_ATTR(alarms_temp, S_IRUGO, show_temp_alarms, NULL); - -/* - * Device detection, registration and update - */ - -static int pc87360_attach_adapter(struct i2c_adapter *adapter) -{ - return i2c_detect(adapter, &addr_data, pc87360_detect); -} - -static int pc87360_find(int sioaddr, u8 *devid, int *address) -{ - u16 val; - int i; - int nrdev; /* logical device count */ - - /* No superio_enter */ - - /* Identify device */ - val = superio_inb(sioaddr, DEVID); - switch (val) { - case 0xE1: /* PC87360 */ - case 0xE8: /* PC87363 */ - case 0xE4: /* PC87364 */ - nrdev = 1; - break; - case 0xE5: /* PC87365 */ - case 0xE9: /* PC87366 */ - nrdev = 3; - break; - default: - superio_exit(sioaddr); - return -ENODEV; - } - /* Remember the device id */ - *devid = val; - - for (i = 0; i < nrdev; i++) { - /* select logical device */ - superio_outb(sioaddr, DEV, logdev[i]); - - val = superio_inb(sioaddr, ACT); - if (!(val & 0x01)) { - printk(KERN_INFO "pc87360: Device 0x%02x not " - "activated\n", logdev[i]); - continue; - } - - val = (superio_inb(sioaddr, BASE) << 8) - | superio_inb(sioaddr, BASE + 1); - if (!val) { - printk(KERN_INFO "pc87360: Base address not set for " - "device 0x%02x\n", logdev[i]); - continue; - } - - address[i] = val; - - if (i==0) { /* Fans */ - confreg[0] = superio_inb(sioaddr, 0xF0); - confreg[1] = superio_inb(sioaddr, 0xF1); - -#ifdef DEBUG - printk(KERN_DEBUG "pc87360: Fan 1: mon=%d " - "ctrl=%d inv=%d\n", (confreg[0]>>2)&1, - (confreg[0]>>3)&1, (confreg[0]>>4)&1); - printk(KERN_DEBUG "pc87360: Fan 2: mon=%d " - "ctrl=%d inv=%d\n", (confreg[0]>>5)&1, - (confreg[0]>>6)&1, (confreg[0]>>7)&1); - printk(KERN_DEBUG "pc87360: Fan 3: mon=%d " - "ctrl=%d inv=%d\n", confreg[1]&1, - (confreg[1]>>1)&1, (confreg[1]>>2)&1); -#endif - } else if (i==1) { /* Voltages */ - /* Are we using thermistors? */ - if (*devid == 0xE9) { /* PC87366 */ - /* These registers are not logical-device - specific, just that we won't need them if - we don't use the VLM device */ - confreg[2] = superio_inb(sioaddr, 0x2B); - confreg[3] = superio_inb(sioaddr, 0x25); - - if (confreg[2] & 0x40) { - printk(KERN_INFO "pc87360: Using " - "thermistors for temperature " - "monitoring\n"); - } - if (confreg[3] & 0xE0) { - printk(KERN_INFO "pc87360: VID " - "inputs routed (mode %u)\n", - confreg[3] >> 5); - } - } - } - } - - superio_exit(sioaddr); - return 0; -} - -/* We don't really care about the address. - Read from extra_isa instead. */ -int pc87360_detect(struct i2c_adapter *adapter, int address, int kind) -{ - int i; - struct i2c_client *new_client; - struct pc87360_data *data; - int err = 0; - const char *name = "pc87360"; - int use_thermistors = 0; - - if (!i2c_is_isa_adapter(adapter)) - return -ENODEV; - - if (!(data = kmalloc(sizeof(struct pc87360_data), GFP_KERNEL))) - return -ENOMEM; - memset(data, 0x00, sizeof(struct pc87360_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - init_MUTEX(&data->lock); - new_client->adapter = adapter; - new_client->driver = &pc87360_driver; - new_client->flags = 0; - - data->fannr = 2; - data->innr = 0; - data->tempnr = 0; - - switch (devid) { - case 0xe8: - name = "pc87363"; - break; - case 0xe4: - name = "pc87364"; - data->fannr = 3; - break; - case 0xe5: - name = "pc87365"; - data->fannr = extra_isa[0] ? 3 : 0; - data->innr = extra_isa[1] ? 11 : 0; - data->tempnr = extra_isa[2] ? 2 : 0; - break; - case 0xe9: - name = "pc87366"; - data->fannr = extra_isa[0] ? 3 : 0; - data->innr = extra_isa[1] ? 14 : 0; - data->tempnr = extra_isa[2] ? 3 : 0; - break; - } - - strcpy(new_client->name, name); - data->valid = 0; - init_MUTEX(&data->update_lock); - - for (i = 0; i < 3; i++) { - if (((data->address[i] = extra_isa[i])) - && !request_region(extra_isa[i], PC87360_EXTENT, - pc87360_driver.name)) { - dev_err(&new_client->dev, "Region 0x%x-0x%x already " - "in use!\n", extra_isa[i], - extra_isa[i]+PC87360_EXTENT-1); - for (i--; i >= 0; i--) - release_region(extra_isa[i], PC87360_EXTENT); - err = -EBUSY; - goto ERROR1; - } - } - - /* Retrieve the fans configuration from Super-I/O space */ - if (data->fannr) - data->fan_conf = confreg[0] | (confreg[1] << 8); - - if ((err = i2c_attach_client(new_client))) - goto ERROR2; - - /* Use the correct reference voltage - Unless both the VLM and the TMS logical devices agree to - use an external Vref, the internal one is used. */ - if (data->innr) { - i = pc87360_read_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONFIG); - if (data->tempnr) { - i &= pc87360_read_value(data, LD_TEMP, NO_BANK, - PC87365_REG_TEMP_CONFIG); - } - data->in_vref = (i&0x02) ? 3025 : 2966; - dev_dbg(&new_client->dev, "Using %s reference voltage\n", - (i&0x02) ? "external" : "internal"); - - data->vid_conf = confreg[3]; - data->vrm = 90; - } - - /* Fan clock dividers may be needed before any data is read */ - for (i = 0; i < data->fannr; i++) { - if (FAN_CONFIG_MONITOR(data->fan_conf, i)) - data->fan_status[i] = pc87360_read_value(data, - LD_FAN, NO_BANK, - PC87360_REG_FAN_STATUS(i)); - } - - if (init > 0) { - if (devid == 0xe9 && data->address[1]) /* PC87366 */ - use_thermistors = confreg[2] & 0x40; - - pc87360_init_client(new_client, use_thermistors); - } - - /* Register sysfs hooks */ - if (data->innr) { - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in5_input); - device_create_file(&new_client->dev, &dev_attr_in6_input); - device_create_file(&new_client->dev, &dev_attr_in7_input); - device_create_file(&new_client->dev, &dev_attr_in8_input); - device_create_file(&new_client->dev, &dev_attr_in9_input); - device_create_file(&new_client->dev, &dev_attr_in10_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in5_min); - device_create_file(&new_client->dev, &dev_attr_in6_min); - device_create_file(&new_client->dev, &dev_attr_in7_min); - device_create_file(&new_client->dev, &dev_attr_in8_min); - device_create_file(&new_client->dev, &dev_attr_in9_min); - device_create_file(&new_client->dev, &dev_attr_in10_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in4_max); - device_create_file(&new_client->dev, &dev_attr_in5_max); - device_create_file(&new_client->dev, &dev_attr_in6_max); - device_create_file(&new_client->dev, &dev_attr_in7_max); - device_create_file(&new_client->dev, &dev_attr_in8_max); - device_create_file(&new_client->dev, &dev_attr_in9_max); - device_create_file(&new_client->dev, &dev_attr_in10_max); - device_create_file(&new_client->dev, &dev_attr_in0_status); - device_create_file(&new_client->dev, &dev_attr_in1_status); - device_create_file(&new_client->dev, &dev_attr_in2_status); - device_create_file(&new_client->dev, &dev_attr_in3_status); - device_create_file(&new_client->dev, &dev_attr_in4_status); - device_create_file(&new_client->dev, &dev_attr_in5_status); - device_create_file(&new_client->dev, &dev_attr_in6_status); - device_create_file(&new_client->dev, &dev_attr_in7_status); - device_create_file(&new_client->dev, &dev_attr_in8_status); - device_create_file(&new_client->dev, &dev_attr_in9_status); - device_create_file(&new_client->dev, &dev_attr_in10_status); - - device_create_file(&new_client->dev, &dev_attr_cpu0_vid); - device_create_file(&new_client->dev, &dev_attr_vrm); - device_create_file(&new_client->dev, &dev_attr_alarms_in); - } - - if (data->tempnr) { - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp1_min); - device_create_file(&new_client->dev, &dev_attr_temp2_min); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp1_crit); - device_create_file(&new_client->dev, &dev_attr_temp2_crit); - device_create_file(&new_client->dev, &dev_attr_temp1_status); - device_create_file(&new_client->dev, &dev_attr_temp2_status); - - device_create_file(&new_client->dev, &dev_attr_alarms_temp); - } - if (data->tempnr == 3) { - device_create_file(&new_client->dev, &dev_attr_temp3_input); - device_create_file(&new_client->dev, &dev_attr_temp3_min); - device_create_file(&new_client->dev, &dev_attr_temp3_max); - device_create_file(&new_client->dev, &dev_attr_temp3_crit); - device_create_file(&new_client->dev, &dev_attr_temp3_status); - } - if (data->innr == 14) { - device_create_file(&new_client->dev, &dev_attr_temp4_input); - device_create_file(&new_client->dev, &dev_attr_temp5_input); - device_create_file(&new_client->dev, &dev_attr_temp6_input); - device_create_file(&new_client->dev, &dev_attr_temp4_min); - device_create_file(&new_client->dev, &dev_attr_temp5_min); - device_create_file(&new_client->dev, &dev_attr_temp6_min); - device_create_file(&new_client->dev, &dev_attr_temp4_max); - device_create_file(&new_client->dev, &dev_attr_temp5_max); - device_create_file(&new_client->dev, &dev_attr_temp6_max); - device_create_file(&new_client->dev, &dev_attr_temp4_crit); - device_create_file(&new_client->dev, &dev_attr_temp5_crit); - device_create_file(&new_client->dev, &dev_attr_temp6_crit); - device_create_file(&new_client->dev, &dev_attr_temp4_status); - device_create_file(&new_client->dev, &dev_attr_temp5_status); - device_create_file(&new_client->dev, &dev_attr_temp6_status); - } - - if (data->fannr) { - if (FAN_CONFIG_MONITOR(data->fan_conf, 0)) { - device_create_file(&new_client->dev, - &dev_attr_fan1_input); - device_create_file(&new_client->dev, - &dev_attr_fan1_min); - device_create_file(&new_client->dev, - &dev_attr_fan1_div); - device_create_file(&new_client->dev, - &dev_attr_fan1_status); - } - - if (FAN_CONFIG_MONITOR(data->fan_conf, 1)) { - device_create_file(&new_client->dev, - &dev_attr_fan2_input); - device_create_file(&new_client->dev, - &dev_attr_fan2_min); - device_create_file(&new_client->dev, - &dev_attr_fan2_div); - device_create_file(&new_client->dev, - &dev_attr_fan2_status); - } - - if (FAN_CONFIG_CONTROL(data->fan_conf, 0)) - device_create_file(&new_client->dev, &dev_attr_pwm1); - if (FAN_CONFIG_CONTROL(data->fan_conf, 1)) - device_create_file(&new_client->dev, &dev_attr_pwm2); - } - if (data->fannr == 3) { - if (FAN_CONFIG_MONITOR(data->fan_conf, 2)) { - device_create_file(&new_client->dev, - &dev_attr_fan3_input); - device_create_file(&new_client->dev, - &dev_attr_fan3_min); - device_create_file(&new_client->dev, - &dev_attr_fan3_div); - device_create_file(&new_client->dev, - &dev_attr_fan3_status); - } - - if (FAN_CONFIG_CONTROL(data->fan_conf, 2)) - device_create_file(&new_client->dev, &dev_attr_pwm3); - } - - return 0; - -ERROR2: - for (i = 0; i < 3; i++) { - if (data->address[i]) { - release_region(data->address[i], PC87360_EXTENT); - } - } -ERROR1: - kfree(data); - return err; -} - -static int pc87360_detach_client(struct i2c_client *client) -{ - struct pc87360_data *data = i2c_get_clientdata(client); - int i; - - if ((i = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return i; - } - - for (i = 0; i < 3; i++) { - if (data->address[i]) { - release_region(data->address[i], PC87360_EXTENT); - } - } - kfree(data); - - return 0; -} - -/* ldi is the logical device index - bank is for voltages and temperatures only */ -static int pc87360_read_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg) -{ - int res; - - down(&(data->lock)); - if (bank != NO_BANK) - outb_p(bank, data->address[ldi] + PC87365_REG_BANK); - res = inb_p(data->address[ldi] + reg); - up(&(data->lock)); - - return res; -} - -static void pc87360_write_value(struct pc87360_data *data, u8 ldi, u8 bank, - u8 reg, u8 value) -{ - down(&(data->lock)); - if (bank != NO_BANK) - outb_p(bank, data->address[ldi] + PC87365_REG_BANK); - outb_p(value, data->address[ldi] + reg); - up(&(data->lock)); -} - -static void pc87360_init_client(struct i2c_client *client, int use_thermistors) -{ - struct pc87360_data *data = i2c_get_clientdata(client); - int i, nr; - const u8 init_in[14] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 3, 1, 2, 2, 2 }; - const u8 init_temp[3] = { 2, 2, 1 }; - u8 reg; - - if (init >= 2 && data->innr) { - reg = pc87360_read_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONVRATE); - dev_info(&client->dev, "VLM conversion set to" - "1s period, 160us delay\n"); - pc87360_write_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONVRATE, - (reg & 0xC0) | 0x11); - } - - nr = data->innr < 11 ? data->innr : 11; - for (i=0; i= init_in[i]) { - /* Forcibly enable voltage channel */ - reg = pc87360_read_value(data, LD_IN, i, - PC87365_REG_IN_STATUS); - if (!(reg & 0x01)) { - dev_dbg(&client->dev, "Forcibly " - "enabling in%d\n", i); - pc87360_write_value(data, LD_IN, i, - PC87365_REG_IN_STATUS, - (reg & 0x68) | 0x87); - } - } - } - - /* We can't blindly trust the Super-I/O space configuration bit, - most BIOS won't set it properly */ - for (i=11; iinnr; i++) { - reg = pc87360_read_value(data, LD_IN, i, - PC87365_REG_TEMP_STATUS); - use_thermistors = use_thermistors || (reg & 0x01); - } - - i = use_thermistors ? 2 : 0; - for (; itempnr; i++) { - if (init >= init_temp[i]) { - /* Forcibly enable temperature channel */ - reg = pc87360_read_value(data, LD_TEMP, i, - PC87365_REG_TEMP_STATUS); - if (!(reg & 0x01)) { - dev_dbg(&client->dev, "Forcibly " - "enabling temp%d\n", i+1); - pc87360_write_value(data, LD_TEMP, i, - PC87365_REG_TEMP_STATUS, - 0xCF); - } - } - } - - if (use_thermistors) { - for (i=11; iinnr; i++) { - if (init >= init_in[i]) { - /* The pin may already be used by thermal - diodes */ - reg = pc87360_read_value(data, LD_TEMP, - (i-11)/2, PC87365_REG_TEMP_STATUS); - if (reg & 0x01) { - dev_dbg(&client->dev, "Skipping " - "temp%d, pin already in use " - "by temp%d\n", i-7, (i-11)/2); - continue; - } - - /* Forcibly enable thermistor channel */ - reg = pc87360_read_value(data, LD_IN, i, - PC87365_REG_IN_STATUS); - if (!(reg & 0x01)) { - dev_dbg(&client->dev, "Forcibly " - "enabling temp%d\n", i-7); - pc87360_write_value(data, LD_IN, i, - PC87365_REG_TEMP_STATUS, - (reg & 0x60) | 0x8F); - } - } - } - } - - if (data->innr) { - reg = pc87360_read_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONFIG); - if (reg & 0x01) { - dev_dbg(&client->dev, "Forcibly " - "enabling monitoring (VLM)\n"); - pc87360_write_value(data, LD_IN, NO_BANK, - PC87365_REG_IN_CONFIG, - reg & 0xFE); - } - } - - if (data->tempnr) { - reg = pc87360_read_value(data, LD_TEMP, NO_BANK, - PC87365_REG_TEMP_CONFIG); - if (reg & 0x01) { - dev_dbg(&client->dev, "Forcibly enabling " - "monitoring (TMS)\n"); - pc87360_write_value(data, LD_TEMP, NO_BANK, - PC87365_REG_TEMP_CONFIG, - reg & 0xFE); - } - - if (init >= 2) { - /* Chip config as documented by National Semi. */ - pc87360_write_value(data, LD_TEMP, 0xF, 0xA, 0x08); - /* We voluntarily omit the bank here, in case the - sequence itself matters. It shouldn't be a problem, - since nobody else is supposed to access the - device at that point. */ - pc87360_write_value(data, LD_TEMP, NO_BANK, 0xB, 0x04); - pc87360_write_value(data, LD_TEMP, NO_BANK, 0xC, 0x35); - pc87360_write_value(data, LD_TEMP, NO_BANK, 0xD, 0x05); - pc87360_write_value(data, LD_TEMP, NO_BANK, 0xE, 0x05); - } - } -} - -static void pc87360_autodiv(struct i2c_client *client, int nr) -{ - struct pc87360_data *data = i2c_get_clientdata(client); - u8 old_min = data->fan_min[nr]; - - /* Increase clock divider if needed and possible */ - if ((data->fan_status[nr] & 0x04) /* overflow flag */ - || (data->fan[nr] >= 224)) { /* next to overflow */ - if ((data->fan_status[nr] & 0x60) != 0x60) { - data->fan_status[nr] += 0x20; - data->fan_min[nr] >>= 1; - data->fan[nr] >>= 1; - dev_dbg(&client->dev, "Increasing " - "clock divider to %d for fan %d\n", - FAN_DIV_FROM_REG(data->fan_status[nr]), nr+1); - } - } else { - /* Decrease clock divider if possible */ - while (!(data->fan_min[nr] & 0x80) /* min "nails" divider */ - && data->fan[nr] < 85 /* bad accuracy */ - && (data->fan_status[nr] & 0x60) != 0x00) { - data->fan_status[nr] -= 0x20; - data->fan_min[nr] <<= 1; - data->fan[nr] <<= 1; - dev_dbg(&client->dev, "Decreasing " - "clock divider to %d for fan %d\n", - FAN_DIV_FROM_REG(data->fan_status[nr]), - nr+1); - } - } - - /* Write new fan min if it changed */ - if (old_min != data->fan_min[nr]) { - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_MIN(nr), - data->fan_min[nr]); - } -} - -static struct pc87360_data *pc87360_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct pc87360_data *data = i2c_get_clientdata(client); - u8 i; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { - dev_dbg(&client->dev, "Data update\n"); - - /* Fans */ - for (i = 0; i < data->fannr; i++) { - if (FAN_CONFIG_MONITOR(data->fan_conf, i)) { - data->fan_status[i] = - pc87360_read_value(data, LD_FAN, - NO_BANK, PC87360_REG_FAN_STATUS(i)); - data->fan[i] = pc87360_read_value(data, LD_FAN, - NO_BANK, PC87360_REG_FAN(i)); - data->fan_min[i] = pc87360_read_value(data, - LD_FAN, NO_BANK, - PC87360_REG_FAN_MIN(i)); - /* Change clock divider if needed */ - pc87360_autodiv(client, i); - /* Clear bits and write new divider */ - pc87360_write_value(data, LD_FAN, NO_BANK, - PC87360_REG_FAN_STATUS(i), - data->fan_status[i]); - } - if (FAN_CONFIG_CONTROL(data->fan_conf, i)) - data->pwm[i] = pc87360_read_value(data, LD_FAN, - NO_BANK, PC87360_REG_PWM(i)); - } - - /* Voltages */ - for (i = 0; i < data->innr; i++) { - data->in_status[i] = pc87360_read_value(data, LD_IN, i, - PC87365_REG_IN_STATUS); - /* Clear bits */ - pc87360_write_value(data, LD_IN, i, - PC87365_REG_IN_STATUS, - data->in_status[i]); - if ((data->in_status[i] & 0x81) == 0x81) { - data->in[i] = pc87360_read_value(data, LD_IN, - i, PC87365_REG_IN); - } - if (data->in_status[i] & 0x01) { - data->in_min[i] = pc87360_read_value(data, - LD_IN, i, - PC87365_REG_IN_MIN); - data->in_max[i] = pc87360_read_value(data, - LD_IN, i, - PC87365_REG_IN_MAX); - if (i >= 11) - data->in_crit[i-11] = - pc87360_read_value(data, LD_IN, - i, PC87365_REG_TEMP_CRIT); - } - } - if (data->innr) { - data->in_alarms = pc87360_read_value(data, LD_IN, - NO_BANK, PC87365_REG_IN_ALARMS1) - | ((pc87360_read_value(data, LD_IN, - NO_BANK, PC87365_REG_IN_ALARMS2) - & 0x07) << 8); - data->vid = (data->vid_conf & 0xE0) ? - pc87360_read_value(data, LD_IN, - NO_BANK, PC87365_REG_VID) : 0x1F; - } - - /* Temperatures */ - for (i = 0; i < data->tempnr; i++) { - data->temp_status[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_STATUS); - /* Clear bits */ - pc87360_write_value(data, LD_TEMP, i, - PC87365_REG_TEMP_STATUS, - data->temp_status[i]); - if ((data->temp_status[i] & 0x81) == 0x81) { - data->temp[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP); - } - if (data->temp_status[i] & 0x01) { - data->temp_min[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_MIN); - data->temp_max[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_MAX); - data->temp_crit[i] = pc87360_read_value(data, - LD_TEMP, i, - PC87365_REG_TEMP_CRIT); - } - } - if (data->tempnr) { - data->temp_alarms = pc87360_read_value(data, LD_TEMP, - NO_BANK, PC87365_REG_TEMP_ALARMS) - & 0x3F; - } - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init pc87360_init(void) -{ - int i; - - if (pc87360_find(0x2e, &devid, extra_isa) - && pc87360_find(0x4e, &devid, extra_isa)) { - printk(KERN_WARNING "pc87360: PC8736x not detected, " - "module not inserted.\n"); - return -ENODEV; - } - - /* Arbitrarily pick one of the addresses */ - for (i = 0; i < 3; i++) { - if (extra_isa[i] != 0x0000) { - normal_isa[0] = extra_isa[i]; - break; - } - } - - if (normal_isa[0] == 0x0000) { - printk(KERN_WARNING "pc87360: No active logical device, " - "module not inserted.\n"); - return -ENODEV; - } - - return i2c_add_driver(&pc87360_driver); -} - -static void __exit pc87360_exit(void) -{ - i2c_del_driver(&pc87360_driver); -} - - -MODULE_AUTHOR("Jean Delvare "); -MODULE_DESCRIPTION("PC8736x hardware monitor"); -MODULE_LICENSE("GPL"); - -module_init(pc87360_init); -module_exit(pc87360_exit); diff --git a/drivers/i2c/chips/sis5595.c b/drivers/i2c/chips/sis5595.c deleted file mode 100644 index 6bbfc8fb4f13..000000000000 --- a/drivers/i2c/chips/sis5595.c +++ /dev/null @@ -1,817 +0,0 @@ -/* - sis5595.c - Part of lm_sensors, Linux kernel modules - for hardware monitoring - - Copyright (C) 1998 - 2001 Frodo Looijaard , - Kyösti Mälkki , and - Mark D. Studebaker - Ported to Linux 2.6 by Aurelien Jarno with - the help of Jean Delvare - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - SiS southbridge has a LM78-like chip integrated on the same IC. - This driver is a customized copy of lm78.c - - Supports following revisions: - Version PCI ID PCI Revision - 1 1039/0008 AF or less - 2 1039/0008 B0 or greater - - Note: these chips contain a 0008 device which is incompatible with the - 5595. We recognize these by the presence of the listed - "blacklist" PCI ID and refuse to load. - - NOT SUPPORTED PCI ID BLACKLIST PCI ID - 540 0008 0540 - 550 0008 0550 - 5513 0008 5511 - 5581 0008 5597 - 5582 0008 5597 - 5597 0008 5597 - 5598 0008 5597/5598 - 630 0008 0630 - 645 0008 0645 - 730 0008 0730 - 735 0008 0735 -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* If force_addr is set to anything different from 0, we forcibly enable - the device at the given address. */ -static u16 force_addr; -module_param(force_addr, ushort, 0); -MODULE_PARM_DESC(force_addr, - "Initialize the base address of the sensors"); - -/* Addresses to scan. - Note that we can't determine the ISA address until we have initialized - our module */ -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; -static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_1(sis5595); - -/* Many SIS5595 constants specified below */ - -/* Length of ISA address segment */ -#define SIS5595_EXTENT 8 -/* PCI Config Registers */ -#define SIS5595_REVISION_REG 0x08 -#define SIS5595_BASE_REG 0x68 -#define SIS5595_PIN_REG 0x7A -#define SIS5595_ENABLE_REG 0x7B - -/* Where are the ISA address/data registers relative to the base address */ -#define SIS5595_ADDR_REG_OFFSET 5 -#define SIS5595_DATA_REG_OFFSET 6 - -/* The SIS5595 registers */ -#define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2) -#define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2) -#define SIS5595_REG_IN(nr) (0x20 + (nr)) - -#define SIS5595_REG_FAN_MIN(nr) (0x3b + (nr)) -#define SIS5595_REG_FAN(nr) (0x28 + (nr)) - -/* On the first version of the chip, the temp registers are separate. - On the second version, - TEMP pin is shared with IN4, configured in PCI register 0x7A. - The registers are the same as well. - OVER and HYST are really MAX and MIN. */ - -#define REV2MIN 0xb0 -#define SIS5595_REG_TEMP (( data->revision) >= REV2MIN) ? \ - SIS5595_REG_IN(4) : 0x27 -#define SIS5595_REG_TEMP_OVER (( data->revision) >= REV2MIN) ? \ - SIS5595_REG_IN_MAX(4) : 0x39 -#define SIS5595_REG_TEMP_HYST (( data->revision) >= REV2MIN) ? \ - SIS5595_REG_IN_MIN(4) : 0x3a - -#define SIS5595_REG_CONFIG 0x40 -#define SIS5595_REG_ALARM1 0x41 -#define SIS5595_REG_ALARM2 0x42 -#define SIS5595_REG_FANDIV 0x47 - -/* Conversions. Limit checking is only done on the TO_REG - variants. */ - -/* IN: mV, (0V to 4.08V) - REG: 16mV/bit */ -static inline u8 IN_TO_REG(unsigned long val) -{ - unsigned long nval = SENSORS_LIMIT(val, 0, 4080); - return (nval + 8) / 16; -} -#define IN_FROM_REG(val) ((val) * 16) - -static inline u8 FAN_TO_REG(long rpm, int div) -{ - if (rpm <= 0) - return 255; - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); -} - -static inline int FAN_FROM_REG(u8 val, int div) -{ - return val==0 ? -1 : val==255 ? 0 : 1350000/(val*div); -} - -/* TEMP: mC (-54.12C to +157.53C) - REG: 0.83C/bit + 52.12, two's complement */ -static inline int TEMP_FROM_REG(s8 val) -{ - return val * 830 + 52120; -} -static inline s8 TEMP_TO_REG(int val) -{ - int nval = SENSORS_LIMIT(val, -54120, 157530) ; - return nval<0 ? (nval-5212-415)/830 : (nval-5212+415)/830; -} - -/* FAN DIV: 1, 2, 4, or 8 (defaults to 2) - REG: 0, 1, 2, or 3 (respectively) (defaults to 1) */ -static inline u8 DIV_TO_REG(int val) -{ - return val==8 ? 3 : val==4 ? 2 : val==1 ? 0 : 1; -} -#define DIV_FROM_REG(val) (1 << (val)) - -/* For the SIS5595, we need to keep some data in memory. That - data is pointed to by sis5595_list[NR]->data. The structure itself is - dynamically allocated, at the time when the new sis5595 client is - allocated. */ -struct sis5595_data { - struct i2c_client client; - struct semaphore lock; - - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - char maxins; /* == 3 if temp enabled, otherwise == 4 */ - u8 revision; /* Reg. value */ - - u8 in[5]; /* Register value */ - u8 in_max[5]; /* Register value */ - u8 in_min[5]; /* Register value */ - u8 fan[2]; /* Register value */ - u8 fan_min[2]; /* Register value */ - s8 temp; /* Register value */ - s8 temp_over; /* Register value */ - s8 temp_hyst; /* Register value */ - u8 fan_div[2]; /* Register encoding, shifted right */ - u16 alarms; /* Register encoding, combined */ -}; - -static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */ - -static int sis5595_attach_adapter(struct i2c_adapter *adapter); -static int sis5595_detect(struct i2c_adapter *adapter, int address, int kind); -static int sis5595_detach_client(struct i2c_client *client); - -static int sis5595_read_value(struct i2c_client *client, u8 register); -static int sis5595_write_value(struct i2c_client *client, u8 register, u8 value); -static struct sis5595_data *sis5595_update_device(struct device *dev); -static void sis5595_init_client(struct i2c_client *client); - -static struct i2c_driver sis5595_driver = { - .owner = THIS_MODULE, - .name = "sis5595", - .id = I2C_DRIVERID_SIS5595, - .flags = I2C_DF_NOTIFY, - .attach_adapter = sis5595_attach_adapter, - .detach_client = sis5595_detach_client, -}; - -/* 4 Voltages */ -static ssize_t show_in(struct device *dev, char *buf, int nr) -{ - struct sis5595_data *data = sis5595_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in[nr])); -} - -static ssize_t show_in_min(struct device *dev, char *buf, int nr) -{ - struct sis5595_data *data = sis5595_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_min[nr])); -} - -static ssize_t show_in_max(struct device *dev, char *buf, int nr) -{ - struct sis5595_data *data = sis5595_update_device(dev); - return sprintf(buf, "%d\n", IN_FROM_REG(data->in_max[nr])); -} - -static ssize_t set_in_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct sis5595_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val); - sis5595_write_value(client, SIS5595_REG_IN_MIN(nr), data->in_min[nr]); - up(&data->update_lock); - return count; -} - -static ssize_t set_in_max(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct sis5595_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val); - sis5595_write_value(client, SIS5595_REG_IN_MAX(nr), data->in_max[nr]); - up(&data->update_lock); - return count; -} - -#define show_in_offset(offset) \ -static ssize_t \ - show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in(dev, buf, offset); \ -} \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, \ - show_in##offset, NULL); \ -static ssize_t \ - show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in_min(dev, buf, offset); \ -} \ -static ssize_t \ - show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in_max(dev, buf, offset); \ -} \ -static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_in_min(dev, buf, count, offset); \ -} \ -static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_in_max(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ - show_in##offset##_min, set_in##offset##_min); \ -static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ - show_in##offset##_max, set_in##offset##_max); - -show_in_offset(0); -show_in_offset(1); -show_in_offset(2); -show_in_offset(3); -show_in_offset(4); - -/* Temperature */ -static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct sis5595_data *data = sis5595_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp)); -} - -static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct sis5595_data *data = sis5595_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over)); -} - -static ssize_t set_temp_over(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct sis5595_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_over = TEMP_TO_REG(val); - sis5595_write_value(client, SIS5595_REG_TEMP_OVER, data->temp_over); - up(&data->update_lock); - return count; -} - -static ssize_t show_temp_hyst(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct sis5595_data *data = sis5595_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_hyst)); -} - -static ssize_t set_temp_hyst(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct sis5595_data *data = i2c_get_clientdata(client); - long val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_hyst = TEMP_TO_REG(val); - sis5595_write_value(client, SIS5595_REG_TEMP_HYST, data->temp_hyst); - up(&data->update_lock); - return count; -} - -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); -static DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR, - show_temp_over, set_temp_over); -static DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR, - show_temp_hyst, set_temp_hyst); - -/* 2 Fans */ -static ssize_t show_fan(struct device *dev, char *buf, int nr) -{ - struct sis5595_data *data = sis5595_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], - DIV_FROM_REG(data->fan_div[nr])) ); -} - -static ssize_t show_fan_min(struct device *dev, char *buf, int nr) -{ - struct sis5595_data *data = sis5595_update_device(dev); - return sprintf(buf,"%d\n", FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr])) ); -} - -static ssize_t set_fan_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct sis5595_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); - sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]); - up(&data->update_lock); - return count; -} - -static ssize_t show_fan_div(struct device *dev, char *buf, int nr) -{ - struct sis5595_data *data = sis5595_update_device(dev); - return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) ); -} - -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least suprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ -static ssize_t set_fan_div(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct sis5595_data *data = i2c_get_clientdata(client); - unsigned long min; - unsigned long val = simple_strtoul(buf, NULL, 10); - int reg; - - down(&data->update_lock); - min = FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr])); - reg = sis5595_read_value(client, SIS5595_REG_FANDIV); - - switch (val) { - case 1: data->fan_div[nr] = 0; break; - case 2: data->fan_div[nr] = 1; break; - case 4: data->fan_div[nr] = 2; break; - case 8: data->fan_div[nr] = 3; break; - default: - dev_err(&client->dev, "fan_div value %ld not " - "supported. Choose one of 1, 2, 4 or 8!\n", val); - up(&data->update_lock); - return -EINVAL; - } - - switch (nr) { - case 0: - reg = (reg & 0xcf) | (data->fan_div[nr] << 4); - break; - case 1: - reg = (reg & 0x3f) | (data->fan_div[nr] << 6); - break; - } - sis5595_write_value(client, SIS5595_REG_FANDIV, reg); - data->fan_min[nr] = - FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); - sis5595_write_value(client, SIS5595_REG_FAN_MIN(nr), data->fan_min[nr]); - up(&data->update_lock); - return count; -} - -#define show_fan_offset(offset) \ -static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_min(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_div(dev, buf, offset - 1); \ -} \ -static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_fan_min(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan_##offset##_min, set_fan_##offset##_min); - -show_fan_offset(1); -show_fan_offset(2); - -static ssize_t set_fan_1_div(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - return set_fan_div(dev, buf, count, 0) ; -} - -static ssize_t set_fan_2_div(struct device *dev, struct device_attribute *attr, const char *buf, - size_t count) -{ - return set_fan_div(dev, buf, count, 1) ; -} -static DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, - show_fan_1_div, set_fan_1_div); -static DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, - show_fan_2_div, set_fan_2_div); - -/* Alarms */ -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct sis5595_data *data = sis5595_update_device(dev); - return sprintf(buf, "%d\n", data->alarms); -} -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -/* This is called when the module is loaded */ -static int sis5595_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, sis5595_detect); -} - -int sis5595_detect(struct i2c_adapter *adapter, int address, int kind) -{ - int err = 0; - int i; - struct i2c_client *new_client; - struct sis5595_data *data; - char val; - u16 a; - - /* Make sure we are probing the ISA bus!! */ - if (!i2c_is_isa_adapter(adapter)) - goto exit; - - if (force_addr) - address = force_addr & ~(SIS5595_EXTENT - 1); - /* Reserve the ISA region */ - if (!request_region(address, SIS5595_EXTENT, sis5595_driver.name)) { - err = -EBUSY; - goto exit; - } - if (force_addr) { - dev_warn(&adapter->dev, "forcing ISA address 0x%04X\n", address); - if (PCIBIOS_SUCCESSFUL != - pci_write_config_word(s_bridge, SIS5595_BASE_REG, address)) - goto exit_release; - if (PCIBIOS_SUCCESSFUL != - pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a)) - goto exit_release; - if ((a & ~(SIS5595_EXTENT - 1)) != address) - /* doesn't work for some chips? */ - goto exit_release; - } - - if (PCIBIOS_SUCCESSFUL != - pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) { - goto exit_release; - } - if ((val & 0x80) == 0) { - if (PCIBIOS_SUCCESSFUL != - pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG, - val | 0x80)) - goto exit_release; - if (PCIBIOS_SUCCESSFUL != - pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) - goto exit_release; - if ((val & 0x80) == 0) - /* doesn't work for some chips! */ - goto exit_release; - } - - if (!(data = kmalloc(sizeof(struct sis5595_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit_release; - } - memset(data, 0, sizeof(struct sis5595_data)); - - new_client = &data->client; - new_client->addr = address; - init_MUTEX(&data->lock); - i2c_set_clientdata(new_client, data); - new_client->adapter = adapter; - new_client->driver = &sis5595_driver; - new_client->flags = 0; - - /* Check revision and pin registers to determine whether 4 or 5 voltages */ - pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision)); - /* 4 voltages, 1 temp */ - data->maxins = 3; - if (data->revision >= REV2MIN) { - pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val); - if (!(val & 0x80)) - /* 5 voltages, no temps */ - data->maxins = 4; - } - - /* Fill in the remaining client fields and put it into the global list */ - strlcpy(new_client->name, "sis5595", I2C_NAME_SIZE); - - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* Initialize the SIS5595 chip */ - sis5595_init_client(new_client); - - /* A few vars need to be filled upon startup */ - for (i = 0; i < 2; i++) { - data->fan_min[i] = sis5595_read_value(new_client, - SIS5595_REG_FAN_MIN(i)); - } - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in3_max); - if (data->maxins == 4) { - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in4_max); - } - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_alarms); - if (data->maxins == 3) { - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - } - return 0; - -exit_free: - kfree(data); -exit_release: - release_region(address, SIS5595_EXTENT); -exit: - return err; -} - -static int sis5595_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, - "Client deregistration failed, client not detached.\n"); - return err; - } - - if (i2c_is_isa_client(client)) - release_region(client->addr, SIS5595_EXTENT); - - kfree(i2c_get_clientdata(client)); - - return 0; -} - - -/* ISA access must be locked explicitly. */ -static int sis5595_read_value(struct i2c_client *client, u8 reg) -{ - int res; - - struct sis5595_data *data = i2c_get_clientdata(client); - down(&data->lock); - outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET); - res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET); - up(&data->lock); - return res; -} - -static int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - struct sis5595_data *data = i2c_get_clientdata(client); - down(&data->lock); - outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET); - outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET); - up(&data->lock); - return 0; -} - -/* Called when we have found a new SIS5595. */ -static void sis5595_init_client(struct i2c_client *client) -{ - u8 config = sis5595_read_value(client, SIS5595_REG_CONFIG); - if (!(config & 0x01)) - sis5595_write_value(client, SIS5595_REG_CONFIG, - (config & 0xf7) | 0x01); -} - -static struct sis5595_data *sis5595_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct sis5595_data *data = i2c_get_clientdata(client); - int i; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - - for (i = 0; i <= data->maxins; i++) { - data->in[i] = - sis5595_read_value(client, SIS5595_REG_IN(i)); - data->in_min[i] = - sis5595_read_value(client, - SIS5595_REG_IN_MIN(i)); - data->in_max[i] = - sis5595_read_value(client, - SIS5595_REG_IN_MAX(i)); - } - for (i = 0; i < 2; i++) { - data->fan[i] = - sis5595_read_value(client, SIS5595_REG_FAN(i)); - data->fan_min[i] = - sis5595_read_value(client, - SIS5595_REG_FAN_MIN(i)); - } - if (data->maxins == 3) { - data->temp = - sis5595_read_value(client, SIS5595_REG_TEMP); - data->temp_over = - sis5595_read_value(client, SIS5595_REG_TEMP_OVER); - data->temp_hyst = - sis5595_read_value(client, SIS5595_REG_TEMP_HYST); - } - i = sis5595_read_value(client, SIS5595_REG_FANDIV); - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = i >> 6; - data->alarms = - sis5595_read_value(client, SIS5595_REG_ALARM1) | - (sis5595_read_value(client, SIS5595_REG_ALARM2) << 8); - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static struct pci_device_id sis5595_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503) }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, sis5595_pci_ids); - -static int blacklist[] __devinitdata = { - PCI_DEVICE_ID_SI_540, - PCI_DEVICE_ID_SI_550, - PCI_DEVICE_ID_SI_630, - PCI_DEVICE_ID_SI_645, - PCI_DEVICE_ID_SI_730, - PCI_DEVICE_ID_SI_735, - PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but - that ID shows up in other chips so we - use the 5511 ID for recognition */ - PCI_DEVICE_ID_SI_5597, - PCI_DEVICE_ID_SI_5598, - 0 }; - -static int __devinit sis5595_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id) -{ - u16 val; - int *i; - int addr = 0; - - for (i = blacklist; *i != 0; i++) { - struct pci_dev *dev; - dev = pci_get_device(PCI_VENDOR_ID_SI, *i, NULL); - if (dev) { - dev_err(&dev->dev, "Looked for SIS5595 but found unsupported device %.4x\n", *i); - pci_dev_put(dev); - return -ENODEV; - } - } - - if (PCIBIOS_SUCCESSFUL != - pci_read_config_word(dev, SIS5595_BASE_REG, &val)) - return -ENODEV; - - addr = val & ~(SIS5595_EXTENT - 1); - if (addr == 0 && force_addr == 0) { - dev_err(&dev->dev, "Base address not set - upgrade BIOS or use force_addr=0xaddr\n"); - return -ENODEV; - } - if (force_addr) - addr = force_addr; /* so detect will get called */ - - if (!addr) { - dev_err(&dev->dev,"No SiS 5595 sensors found.\n"); - return -ENODEV; - } - normal_isa[0] = addr; - - s_bridge = pci_dev_get(dev); - if (i2c_add_driver(&sis5595_driver)) { - pci_dev_put(s_bridge); - s_bridge = NULL; - } - - /* Always return failure here. This is to allow other drivers to bind - * to this pci device. We don't really want to have control over the - * pci device, we only wanted to read as few register values from it. - */ - return -ENODEV; -} - -static struct pci_driver sis5595_pci_driver = { - .name = "sis5595", - .id_table = sis5595_pci_ids, - .probe = sis5595_pci_probe, -}; - -static int __init sm_sis5595_init(void) -{ - return pci_register_driver(&sis5595_pci_driver); -} - -static void __exit sm_sis5595_exit(void) -{ - pci_unregister_driver(&sis5595_pci_driver); - if (s_bridge != NULL) { - i2c_del_driver(&sis5595_driver); - pci_dev_put(s_bridge); - s_bridge = NULL; - } -} - -MODULE_AUTHOR("Aurelien Jarno "); -MODULE_DESCRIPTION("SiS 5595 Sensor device"); -MODULE_LICENSE("GPL"); - -module_init(sm_sis5595_init); -module_exit(sm_sis5595_exit); diff --git a/drivers/i2c/chips/smsc47b397.c b/drivers/i2c/chips/smsc47b397.c deleted file mode 100644 index 251ac2659554..000000000000 --- a/drivers/i2c/chips/smsc47b397.c +++ /dev/null @@ -1,352 +0,0 @@ -/* - smsc47b397.c - Part of lm_sensors, Linux kernel modules - for hardware monitoring - - Supports the SMSC LPC47B397-NC Super-I/O chip. - - Author/Maintainer: Mark M. Hoffman - Copyright (C) 2004 Utilitek Systems, Inc. - - derived in part from smsc47m1.c: - Copyright (C) 2002 Mark D. Studebaker - Copyright (C) 2004 Jean Delvare - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; -/* Address is autodetected, there is no default value */ -static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; -static struct i2c_force_data forces[] = {{NULL}}; - -enum chips { any_chip, smsc47b397 }; -static struct i2c_address_data addr_data = { - .normal_i2c = normal_i2c, - .normal_isa = normal_isa, - .probe = normal_i2c, /* cheat */ - .ignore = normal_i2c, /* cheat */ - .forces = forces, -}; - -/* Super-I/0 registers and commands */ - -#define REG 0x2e /* The register to read/write */ -#define VAL 0x2f /* The value to read/write */ - -static inline void superio_outb(int reg, int val) -{ - outb(reg, REG); - outb(val, VAL); -} - -static inline int superio_inb(int reg) -{ - outb(reg, REG); - return inb(VAL); -} - -/* select superio logical device */ -static inline void superio_select(int ld) -{ - superio_outb(0x07, ld); -} - -static inline void superio_enter(void) -{ - outb(0x55, REG); -} - -static inline void superio_exit(void) -{ - outb(0xAA, REG); -} - -#define SUPERIO_REG_DEVID 0x20 -#define SUPERIO_REG_DEVREV 0x21 -#define SUPERIO_REG_BASE_MSB 0x60 -#define SUPERIO_REG_BASE_LSB 0x61 -#define SUPERIO_REG_LD8 0x08 - -#define SMSC_EXTENT 0x02 - -/* 0 <= nr <= 3 */ -static u8 smsc47b397_reg_temp[] = {0x25, 0x26, 0x27, 0x80}; -#define SMSC47B397_REG_TEMP(nr) (smsc47b397_reg_temp[(nr)]) - -/* 0 <= nr <= 3 */ -#define SMSC47B397_REG_FAN_LSB(nr) (0x28 + 2 * (nr)) -#define SMSC47B397_REG_FAN_MSB(nr) (0x29 + 2 * (nr)) - -struct smsc47b397_data { - struct i2c_client client; - struct semaphore lock; - - struct semaphore update_lock; - unsigned long last_updated; /* in jiffies */ - int valid; - - /* register values */ - u16 fan[4]; - u8 temp[4]; -}; - -static int smsc47b397_read_value(struct i2c_client *client, u8 reg) -{ - struct smsc47b397_data *data = i2c_get_clientdata(client); - int res; - - down(&data->lock); - outb(reg, client->addr); - res = inb_p(client->addr + 1); - up(&data->lock); - return res; -} - -static struct smsc47b397_data *smsc47b397_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct smsc47b397_data *data = i2c_get_clientdata(client); - int i; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - dev_dbg(&client->dev, "starting device update...\n"); - - /* 4 temperature inputs, 4 fan inputs */ - for (i = 0; i < 4; i++) { - data->temp[i] = smsc47b397_read_value(client, - SMSC47B397_REG_TEMP(i)); - - /* must read LSB first */ - data->fan[i] = smsc47b397_read_value(client, - SMSC47B397_REG_FAN_LSB(i)); - data->fan[i] |= smsc47b397_read_value(client, - SMSC47B397_REG_FAN_MSB(i)) << 8; - } - - data->last_updated = jiffies; - data->valid = 1; - - dev_dbg(&client->dev, "... device update complete\n"); - } - - up(&data->update_lock); - - return data; -} - -/* TEMP: 0.001C/bit (-128C to +127C) - REG: 1C/bit, two's complement */ -static int temp_from_reg(u8 reg) -{ - return (s8)reg * 1000; -} - -/* 0 <= nr <= 3 */ -static ssize_t show_temp(struct device *dev, char *buf, int nr) -{ - struct smsc47b397_data *data = smsc47b397_update_device(dev); - return sprintf(buf, "%d\n", temp_from_reg(data->temp[nr])); -} - -#define sysfs_temp(num) \ -static ssize_t show_temp##num(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp(dev, buf, num-1); \ -} \ -static DEVICE_ATTR(temp##num##_input, S_IRUGO, show_temp##num, NULL) - -sysfs_temp(1); -sysfs_temp(2); -sysfs_temp(3); -sysfs_temp(4); - -#define device_create_file_temp(client, num) \ - device_create_file(&client->dev, &dev_attr_temp##num##_input) - -/* FAN: 1 RPM/bit - REG: count of 90kHz pulses / revolution */ -static int fan_from_reg(u16 reg) -{ - return 90000 * 60 / reg; -} - -/* 0 <= nr <= 3 */ -static ssize_t show_fan(struct device *dev, char *buf, int nr) -{ - struct smsc47b397_data *data = smsc47b397_update_device(dev); - return sprintf(buf, "%d\n", fan_from_reg(data->fan[nr])); -} - -#define sysfs_fan(num) \ -static ssize_t show_fan##num(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan(dev, buf, num-1); \ -} \ -static DEVICE_ATTR(fan##num##_input, S_IRUGO, show_fan##num, NULL) - -sysfs_fan(1); -sysfs_fan(2); -sysfs_fan(3); -sysfs_fan(4); - -#define device_create_file_fan(client, num) \ - device_create_file(&client->dev, &dev_attr_fan##num##_input) - -static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind); - -static int smsc47b397_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, smsc47b397_detect); -} - -static int smsc47b397_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - release_region(client->addr, SMSC_EXTENT); - kfree(i2c_get_clientdata(client)); - - return 0; -} - -static struct i2c_driver smsc47b397_driver = { - .owner = THIS_MODULE, - .name = "smsc47b397", - .id = I2C_DRIVERID_SMSC47B397, - .flags = I2C_DF_NOTIFY, - .attach_adapter = smsc47b397_attach_adapter, - .detach_client = smsc47b397_detach_client, -}; - -static int smsc47b397_detect(struct i2c_adapter *adapter, int addr, int kind) -{ - struct i2c_client *new_client; - struct smsc47b397_data *data; - int err = 0; - - if (!i2c_is_isa_adapter(adapter)) { - return 0; - } - - if (!request_region(addr, SMSC_EXTENT, smsc47b397_driver.name)) { - dev_err(&adapter->dev, "Region 0x%x already in use!\n", addr); - return -EBUSY; - } - - if (!(data = kmalloc(sizeof(struct smsc47b397_data), GFP_KERNEL))) { - err = -ENOMEM; - goto error_release; - } - memset(data, 0x00, sizeof(struct smsc47b397_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = addr; - init_MUTEX(&data->lock); - new_client->adapter = adapter; - new_client->driver = &smsc47b397_driver; - new_client->flags = 0; - - strlcpy(new_client->name, "smsc47b397", I2C_NAME_SIZE); - - init_MUTEX(&data->update_lock); - - if ((err = i2c_attach_client(new_client))) - goto error_free; - - device_create_file_temp(new_client, 1); - device_create_file_temp(new_client, 2); - device_create_file_temp(new_client, 3); - device_create_file_temp(new_client, 4); - - device_create_file_fan(new_client, 1); - device_create_file_fan(new_client, 2); - device_create_file_fan(new_client, 3); - device_create_file_fan(new_client, 4); - - return 0; - -error_free: - kfree(new_client); -error_release: - release_region(addr, SMSC_EXTENT); - return err; -} - -static int __init smsc47b397_find(unsigned int *addr) -{ - u8 id, rev; - - superio_enter(); - id = superio_inb(SUPERIO_REG_DEVID); - - if (id != 0x6f) { - superio_exit(); - return -ENODEV; - } - - rev = superio_inb(SUPERIO_REG_DEVREV); - - superio_select(SUPERIO_REG_LD8); - *addr = (superio_inb(SUPERIO_REG_BASE_MSB) << 8) - | superio_inb(SUPERIO_REG_BASE_LSB); - - printk(KERN_INFO "smsc47b397: found SMSC LPC47B397-NC " - "(base address 0x%04x, revision %u)\n", *addr, rev); - - superio_exit(); - return 0; -} - -static int __init smsc47b397_init(void) -{ - int ret; - - if ((ret = smsc47b397_find(normal_isa))) - return ret; - - return i2c_add_driver(&smsc47b397_driver); -} - -static void __exit smsc47b397_exit(void) -{ - i2c_del_driver(&smsc47b397_driver); -} - -MODULE_AUTHOR("Mark M. Hoffman "); -MODULE_DESCRIPTION("SMSC LPC47B397 driver"); -MODULE_LICENSE("GPL"); - -module_init(smsc47b397_init); -module_exit(smsc47b397_exit); diff --git a/drivers/i2c/chips/smsc47m1.c b/drivers/i2c/chips/smsc47m1.c deleted file mode 100644 index 897117a7213f..000000000000 --- a/drivers/i2c/chips/smsc47m1.c +++ /dev/null @@ -1,593 +0,0 @@ -/* - smsc47m1.c - Part of lm_sensors, Linux kernel modules - for hardware monitoring - - Supports the SMSC LPC47B27x, LPC47M10x, LPC47M13x and LPC47M14x - Super-I/O chips. - - Copyright (C) 2002 Mark D. Studebaker - Copyright (C) 2004 Jean Delvare - Ported to Linux 2.6 by Gabriele Gorla - and Jean Delvare - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; -/* Address is autodetected, there is no default value */ -static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; -static struct i2c_force_data forces[] = {{NULL}}; - -enum chips { any_chip, smsc47m1 }; -static struct i2c_address_data addr_data = { - .normal_i2c = normal_i2c, - .normal_isa = normal_isa, - .forces = forces, -}; - -/* Super-I/0 registers and commands */ - -#define REG 0x2e /* The register to read/write */ -#define VAL 0x2f /* The value to read/write */ - -static inline void -superio_outb(int reg, int val) -{ - outb(reg, REG); - outb(val, VAL); -} - -static inline int -superio_inb(int reg) -{ - outb(reg, REG); - return inb(VAL); -} - -/* logical device for fans is 0x0A */ -#define superio_select() superio_outb(0x07, 0x0A) - -static inline void -superio_enter(void) -{ - outb(0x55, REG); -} - -static inline void -superio_exit(void) -{ - outb(0xAA, REG); -} - -#define SUPERIO_REG_ACT 0x30 -#define SUPERIO_REG_BASE 0x60 -#define SUPERIO_REG_DEVID 0x20 - -/* Logical device registers */ - -#define SMSC_EXTENT 0x80 - -/* nr is 0 or 1 in the macros below */ -#define SMSC47M1_REG_ALARM 0x04 -#define SMSC47M1_REG_TPIN(nr) (0x34 - (nr)) -#define SMSC47M1_REG_PPIN(nr) (0x36 - (nr)) -#define SMSC47M1_REG_PWM(nr) (0x56 + (nr)) -#define SMSC47M1_REG_FANDIV 0x58 -#define SMSC47M1_REG_FAN(nr) (0x59 + (nr)) -#define SMSC47M1_REG_FAN_PRELOAD(nr) (0x5B + (nr)) - -#define MIN_FROM_REG(reg,div) ((reg)>=192 ? 0 : \ - 983040/((192-(reg))*(div))) -#define FAN_FROM_REG(reg,div,preload) ((reg)<=(preload) || (reg)==255 ? 0 : \ - 983040/(((reg)-(preload))*(div))) -#define DIV_FROM_REG(reg) (1 << (reg)) -#define PWM_FROM_REG(reg) (((reg) & 0x7E) << 1) -#define PWM_EN_FROM_REG(reg) ((~(reg)) & 0x01) -#define PWM_TO_REG(reg) (((reg) >> 1) & 0x7E) - -struct smsc47m1_data { - struct i2c_client client; - struct semaphore lock; - - struct semaphore update_lock; - unsigned long last_updated; /* In jiffies */ - - u8 fan[2]; /* Register value */ - u8 fan_preload[2]; /* Register value */ - u8 fan_div[2]; /* Register encoding, shifted right */ - u8 alarms; /* Register encoding */ - u8 pwm[2]; /* Register value (bit 7 is enable) */ -}; - - -static int smsc47m1_attach_adapter(struct i2c_adapter *adapter); -static int smsc47m1_find(int *address); -static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind); -static int smsc47m1_detach_client(struct i2c_client *client); - -static int smsc47m1_read_value(struct i2c_client *client, u8 reg); -static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value); - -static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, - int init); - - -static struct i2c_driver smsc47m1_driver = { - .owner = THIS_MODULE, - .name = "smsc47m1", - .id = I2C_DRIVERID_SMSC47M1, - .flags = I2C_DF_NOTIFY, - .attach_adapter = smsc47m1_attach_adapter, - .detach_client = smsc47m1_detach_client, -}; - -/* nr is 0 or 1 in the callback functions below */ - -static ssize_t get_fan(struct device *dev, char *buf, int nr) -{ - struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); - /* This chip (stupidly) stops monitoring fan speed if PWM is - enabled and duty cycle is 0%. This is fine if the monitoring - and control concern the same fan, but troublesome if they are - not (which could as well happen). */ - int rpm = (data->pwm[nr] & 0x7F) == 0x00 ? 0 : - FAN_FROM_REG(data->fan[nr], - DIV_FROM_REG(data->fan_div[nr]), - data->fan_preload[nr]); - return sprintf(buf, "%d\n", rpm); -} - -static ssize_t get_fan_min(struct device *dev, char *buf, int nr) -{ - struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); - int rpm = MIN_FROM_REG(data->fan_preload[nr], - DIV_FROM_REG(data->fan_div[nr])); - return sprintf(buf, "%d\n", rpm); -} - -static ssize_t get_fan_div(struct device *dev, char *buf, int nr) -{ - struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); - return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr])); -} - -static ssize_t get_pwm(struct device *dev, char *buf, int nr) -{ - struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); - return sprintf(buf, "%d\n", PWM_FROM_REG(data->pwm[nr])); -} - -static ssize_t get_pwm_en(struct device *dev, char *buf, int nr) -{ - struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); - return sprintf(buf, "%d\n", PWM_EN_FROM_REG(data->pwm[nr])); -} - -static ssize_t get_alarms(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct smsc47m1_data *data = smsc47m1_update_device(dev, 0); - return sprintf(buf, "%d\n", data->alarms); -} - -static ssize_t set_fan_min(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct smsc47m1_data *data = i2c_get_clientdata(client); - long rpmdiv, val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - rpmdiv = val * DIV_FROM_REG(data->fan_div[nr]); - - if (983040 > 192 * rpmdiv || 2 * rpmdiv > 983040) { - up(&data->update_lock); - return -EINVAL; - } - - data->fan_preload[nr] = 192 - ((983040 + rpmdiv / 2) / rpmdiv); - smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr), - data->fan_preload[nr]); - up(&data->update_lock); - - return count; -} - -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan clock divider. This follows the principle - of least suprise; the user doesn't expect the fan minimum to change just - because the divider changed. */ -static ssize_t set_fan_div(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct smsc47m1_data *data = i2c_get_clientdata(client); - - long new_div = simple_strtol(buf, NULL, 10), tmp; - u8 old_div = DIV_FROM_REG(data->fan_div[nr]); - - if (new_div == old_div) /* No change */ - return count; - - down(&data->update_lock); - switch (new_div) { - case 1: data->fan_div[nr] = 0; break; - case 2: data->fan_div[nr] = 1; break; - case 4: data->fan_div[nr] = 2; break; - case 8: data->fan_div[nr] = 3; break; - default: - up(&data->update_lock); - return -EINVAL; - } - - tmp = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV) & 0x0F; - tmp |= (data->fan_div[0] << 4) | (data->fan_div[1] << 6); - smsc47m1_write_value(client, SMSC47M1_REG_FANDIV, tmp); - - /* Preserve fan min */ - tmp = 192 - (old_div * (192 - data->fan_preload[nr]) - + new_div / 2) / new_div; - data->fan_preload[nr] = SENSORS_LIMIT(tmp, 0, 191); - smsc47m1_write_value(client, SMSC47M1_REG_FAN_PRELOAD(nr), - data->fan_preload[nr]); - up(&data->update_lock); - - return count; -} - -static ssize_t set_pwm(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct smsc47m1_data *data = i2c_get_clientdata(client); - - long val = simple_strtol(buf, NULL, 10); - - if (val < 0 || val > 255) - return -EINVAL; - - down(&data->update_lock); - data->pwm[nr] &= 0x81; /* Preserve additional bits */ - data->pwm[nr] |= PWM_TO_REG(val); - smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr), - data->pwm[nr]); - up(&data->update_lock); - - return count; -} - -static ssize_t set_pwm_en(struct device *dev, const char *buf, - size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct smsc47m1_data *data = i2c_get_clientdata(client); - - long val = simple_strtol(buf, NULL, 10); - - if (val != 0 && val != 1) - return -EINVAL; - - down(&data->update_lock); - data->pwm[nr] &= 0xFE; /* preserve the other bits */ - data->pwm[nr] |= !val; - smsc47m1_write_value(client, SMSC47M1_REG_PWM(nr), - data->pwm[nr]); - up(&data->update_lock); - - return count; -} - -#define fan_present(offset) \ -static ssize_t get_fan##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return get_fan(dev, buf, offset - 1); \ -} \ -static ssize_t get_fan##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return get_fan_min(dev, buf, offset - 1); \ -} \ -static ssize_t set_fan##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_fan_min(dev, buf, count, offset - 1); \ -} \ -static ssize_t get_fan##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return get_fan_div(dev, buf, offset - 1); \ -} \ -static ssize_t set_fan##offset##_div (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_fan_div(dev, buf, count, offset - 1); \ -} \ -static ssize_t get_pwm##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return get_pwm(dev, buf, offset - 1); \ -} \ -static ssize_t set_pwm##offset (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_pwm(dev, buf, count, offset - 1); \ -} \ -static ssize_t get_pwm##offset##_en (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return get_pwm_en(dev, buf, offset - 1); \ -} \ -static ssize_t set_pwm##offset##_en (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_pwm_en(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, get_fan##offset, \ - NULL); \ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - get_fan##offset##_min, set_fan##offset##_min); \ -static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ - get_fan##offset##_div, set_fan##offset##_div); \ -static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ - get_pwm##offset, set_pwm##offset); \ -static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ - get_pwm##offset##_en, set_pwm##offset##_en); - -fan_present(1); -fan_present(2); - -static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL); - -static int smsc47m1_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, smsc47m1_detect); -} - -static int smsc47m1_find(int *address) -{ - u8 val; - - superio_enter(); - val = superio_inb(SUPERIO_REG_DEVID); - - /* - * SMSC LPC47M10x/LPC47M13x (device id 0x59), LPC47M14x (device id - * 0x5F) and LPC47B27x (device id 0x51) have fan control. - * The LPC47M15x and LPC47M192 chips "with hardware monitoring block" - * can do much more besides (device id 0x60). - */ - if (val == 0x51) - printk(KERN_INFO "smsc47m1: Found SMSC LPC47B27x\n"); - else if (val == 0x59) - printk(KERN_INFO "smsc47m1: Found SMSC LPC47M10x/LPC47M13x\n"); - else if (val == 0x5F) - printk(KERN_INFO "smsc47m1: Found SMSC LPC47M14x\n"); - else if (val == 0x60) - printk(KERN_INFO "smsc47m1: Found SMSC LPC47M15x/LPC47M192\n"); - else { - superio_exit(); - return -ENODEV; - } - - superio_select(); - *address = (superio_inb(SUPERIO_REG_BASE) << 8) - | superio_inb(SUPERIO_REG_BASE + 1); - val = superio_inb(SUPERIO_REG_ACT); - if (*address == 0 || (val & 0x01) == 0) { - printk(KERN_INFO "smsc47m1: Device is disabled, will not use\n"); - superio_exit(); - return -ENODEV; - } - - superio_exit(); - return 0; -} - -static int smsc47m1_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct smsc47m1_data *data; - int err = 0; - int fan1, fan2, pwm1, pwm2; - - if (!i2c_is_isa_adapter(adapter)) { - return 0; - } - - if (!request_region(address, SMSC_EXTENT, smsc47m1_driver.name)) { - dev_err(&adapter->dev, "Region 0x%x already in use!\n", address); - return -EBUSY; - } - - if (!(data = kmalloc(sizeof(struct smsc47m1_data), GFP_KERNEL))) { - err = -ENOMEM; - goto error_release; - } - memset(data, 0x00, sizeof(struct smsc47m1_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - init_MUTEX(&data->lock); - new_client->adapter = adapter; - new_client->driver = &smsc47m1_driver; - new_client->flags = 0; - - strlcpy(new_client->name, "smsc47m1", I2C_NAME_SIZE); - init_MUTEX(&data->update_lock); - - /* If no function is properly configured, there's no point in - actually registering the chip. */ - fan1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(0)) & 0x05) - == 0x05; - fan2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_TPIN(1)) & 0x05) - == 0x05; - pwm1 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(0)) & 0x05) - == 0x04; - pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05) - == 0x04; - if (!(fan1 || fan2 || pwm1 || pwm2)) { - dev_warn(&new_client->dev, "Device is not configured, will not use\n"); - err = -ENODEV; - goto error_free; - } - - if ((err = i2c_attach_client(new_client))) - goto error_free; - - /* Some values (fan min, clock dividers, pwm registers) may be - needed before any update is triggered, so we better read them - at least once here. We don't usually do it that way, but in - this particular case, manually reading 5 registers out of 8 - doesn't make much sense and we're better using the existing - function. */ - smsc47m1_update_device(&new_client->dev, 1); - - if (fan1) { - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - } else - dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, " - "skipping\n"); - - if (fan2) { - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - } else - dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, " - "skipping\n"); - - if (pwm1) { - device_create_file(&new_client->dev, &dev_attr_pwm1); - device_create_file(&new_client->dev, &dev_attr_pwm1_enable); - } else - dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, " - "skipping\n"); - if (pwm2) { - device_create_file(&new_client->dev, &dev_attr_pwm2); - device_create_file(&new_client->dev, &dev_attr_pwm2_enable); - } else - dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, " - "skipping\n"); - - device_create_file(&new_client->dev, &dev_attr_alarms); - - return 0; - -error_free: - kfree(new_client); -error_release: - release_region(address, SMSC_EXTENT); - return err; -} - -static int smsc47m1_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - release_region(client->addr, SMSC_EXTENT); - kfree(i2c_get_clientdata(client)); - - return 0; -} - -static int smsc47m1_read_value(struct i2c_client *client, u8 reg) -{ - int res; - - down(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock); - res = inb_p(client->addr + reg); - up(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock); - return res; -} - -static void smsc47m1_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - down(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock); - outb_p(value, client->addr + reg); - up(&((struct smsc47m1_data *) i2c_get_clientdata(client))->lock); -} - -static struct smsc47m1_data *smsc47m1_update_device(struct device *dev, - int init) -{ - struct i2c_client *client = to_i2c_client(dev); - struct smsc47m1_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) || init) { - int i; - - for (i = 0; i < 2; i++) { - data->fan[i] = smsc47m1_read_value(client, - SMSC47M1_REG_FAN(i)); - data->fan_preload[i] = smsc47m1_read_value(client, - SMSC47M1_REG_FAN_PRELOAD(i)); - data->pwm[i] = smsc47m1_read_value(client, - SMSC47M1_REG_PWM(i)); - } - - i = smsc47m1_read_value(client, SMSC47M1_REG_FANDIV); - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = i >> 6; - - data->alarms = smsc47m1_read_value(client, - SMSC47M1_REG_ALARM) >> 6; - /* Clear alarms if needed */ - if (data->alarms) - smsc47m1_write_value(client, SMSC47M1_REG_ALARM, 0xC0); - - data->last_updated = jiffies; - } - - up(&data->update_lock); - return data; -} - -static int __init sm_smsc47m1_init(void) -{ - if (smsc47m1_find(normal_isa)) { - return -ENODEV; - } - - return i2c_add_driver(&smsc47m1_driver); -} - -static void __exit sm_smsc47m1_exit(void) -{ - i2c_del_driver(&smsc47m1_driver); -} - -MODULE_AUTHOR("Mark D. Studebaker "); -MODULE_DESCRIPTION("SMSC LPC47M1xx fan sensors driver"); -MODULE_LICENSE("GPL"); - -module_init(sm_smsc47m1_init); -module_exit(sm_smsc47m1_exit); diff --git a/drivers/i2c/chips/via686a.c b/drivers/i2c/chips/via686a.c deleted file mode 100644 index 164d47948390..000000000000 --- a/drivers/i2c/chips/via686a.c +++ /dev/null @@ -1,875 +0,0 @@ -/* - via686a.c - Part of lm_sensors, Linux kernel modules - for hardware monitoring - - Copyright (c) 1998 - 2002 Frodo Looijaard , - Kyösti Mälkki , - Mark Studebaker , - and Bob Dougherty - (Some conversion-factor data were contributed by Jonathan Teh Soon Yew - and Alex van Kaam .) - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - Supports the Via VT82C686A, VT82C686B south bridges. - Reports all as a 686A. - Warning - only supports a single device. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include - - -/* If force_addr is set to anything different from 0, we forcibly enable - the device at the given address. */ -static unsigned short force_addr = 0; -module_param(force_addr, ushort, 0); -MODULE_PARM_DESC(force_addr, - "Initialize the base address of the sensors"); - -/* Addresses to scan. - Note that we can't determine the ISA address until we have initialized - our module */ -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; -static unsigned int normal_isa[] = { 0x0000, I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_1(via686a); - -/* - The Via 686a southbridge has a LM78-like chip integrated on the same IC. - This driver is a customized copy of lm78.c -*/ - -/* Many VIA686A constants specified below */ - -/* Length of ISA address segment */ -#define VIA686A_EXTENT 0x80 -#define VIA686A_BASE_REG 0x70 -#define VIA686A_ENABLE_REG 0x74 - -/* The VIA686A registers */ -/* ins numbered 0-4 */ -#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2)) -#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2)) -#define VIA686A_REG_IN(nr) (0x22 + (nr)) - -/* fans numbered 1-2 */ -#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr)) -#define VIA686A_REG_FAN(nr) (0x28 + (nr)) - -/* temps numbered 1-3 */ -static const u8 VIA686A_REG_TEMP[] = { 0x20, 0x21, 0x1f }; -static const u8 VIA686A_REG_TEMP_OVER[] = { 0x39, 0x3d, 0x1d }; -static const u8 VIA686A_REG_TEMP_HYST[] = { 0x3a, 0x3e, 0x1e }; -/* bits 7-6 */ -#define VIA686A_REG_TEMP_LOW1 0x4b -/* 2 = bits 5-4, 3 = bits 7-6 */ -#define VIA686A_REG_TEMP_LOW23 0x49 - -#define VIA686A_REG_ALARM1 0x41 -#define VIA686A_REG_ALARM2 0x42 -#define VIA686A_REG_FANDIV 0x47 -#define VIA686A_REG_CONFIG 0x40 -/* The following register sets temp interrupt mode (bits 1-0 for temp1, - 3-2 for temp2, 5-4 for temp3). Modes are: - 00 interrupt stays as long as value is out-of-range - 01 interrupt is cleared once register is read (default) - 10 comparator mode- like 00, but ignores hysteresis - 11 same as 00 */ -#define VIA686A_REG_TEMP_MODE 0x4b -/* We'll just assume that you want to set all 3 simultaneously: */ -#define VIA686A_TEMP_MODE_MASK 0x3F -#define VIA686A_TEMP_MODE_CONTINUOUS 0x00 - -/* Conversions. Limit checking is only done on the TO_REG - variants. - -********* VOLTAGE CONVERSIONS (Bob Dougherty) ******** - From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew): - voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp - voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V - voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V - voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V - voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V - in[i]=(data[i+2]*25.0+133)*voltagefactor[i]; - That is: - volts = (25*regVal+133)*factor - regVal = (volts/factor-133)/25 - (These conversions were contributed by Jonathan Teh Soon Yew - ) */ -static inline u8 IN_TO_REG(long val, int inNum) -{ - /* To avoid floating point, we multiply constants by 10 (100 for +12V). - Rounding is done (120500 is actually 133000 - 12500). - Remember that val is expressed in 0.001V/bit, which is why we divide - by an additional 10000 (100000 for +12V): 1000 for val and 10 (100) - for the constants. */ - if (inNum <= 1) - return (u8) - SENSORS_LIMIT((val * 21024 - 1205000) / 250000, 0, 255); - else if (inNum == 2) - return (u8) - SENSORS_LIMIT((val * 15737 - 1205000) / 250000, 0, 255); - else if (inNum == 3) - return (u8) - SENSORS_LIMIT((val * 10108 - 1205000) / 250000, 0, 255); - else - return (u8) - SENSORS_LIMIT((val * 41714 - 12050000) / 2500000, 0, 255); -} - -static inline long IN_FROM_REG(u8 val, int inNum) -{ - /* To avoid floating point, we multiply constants by 10 (100 for +12V). - We also multiply them by 1000 because we want 0.001V/bit for the - output value. Rounding is done. */ - if (inNum <= 1) - return (long) ((250000 * val + 1330000 + 21024 / 2) / 21024); - else if (inNum == 2) - return (long) ((250000 * val + 1330000 + 15737 / 2) / 15737); - else if (inNum == 3) - return (long) ((250000 * val + 1330000 + 10108 / 2) / 10108); - else - return (long) ((2500000 * val + 13300000 + 41714 / 2) / 41714); -} - -/********* FAN RPM CONVERSIONS ********/ -/* Higher register values = slower fans (the fan's strobe gates a counter). - But this chip saturates back at 0, not at 255 like all the other chips. - So, 0 means 0 RPM */ -static inline u8 FAN_TO_REG(long rpm, int div) -{ - if (rpm == 0) - return 0; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255); -} - -#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div))) - -/******** TEMP CONVERSIONS (Bob Dougherty) *********/ -/* linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew) - if(temp<169) - return double(temp)*0.427-32.08; - else if(temp>=169 && temp<=202) - return double(temp)*0.582-58.16; - else - return double(temp)*0.924-127.33; - - A fifth-order polynomial fits the unofficial data (provided by Alex van - Kaam ) a bit better. It also give more reasonable - numbers on my machine (ie. they agree with what my BIOS tells me). - Here's the fifth-order fit to the 8-bit data: - temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 - - 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0. - - (2000-10-25- RFD: thanks to Uwe Andersen for - finding my typos in this formula!) - - Alas, none of the elegant function-fit solutions will work because we - aren't allowed to use floating point in the kernel and doing it with - integers doesn't provide enough precision. So we'll do boring old - look-up table stuff. The unofficial data (see below) have effectively - 7-bit resolution (they are rounded to the nearest degree). I'm assuming - that the transfer function of the device is monotonic and smooth, so a - smooth function fit to the data will allow us to get better precision. - I used the 5th-order poly fit described above and solved for - VIA register values 0-255. I *10 before rounding, so we get tenth-degree - precision. (I could have done all 1024 values for our 10-bit readings, - but the function is very linear in the useful range (0-80 deg C), so - we'll just use linear interpolation for 10-bit readings.) So, tempLUT - is the temp at via register values 0-255: */ -static const long tempLUT[] = -{ -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519, - -503, -487, -471, -456, -442, -428, -414, -400, -387, -375, - -362, -350, -339, -327, -316, -305, -295, -285, -275, -265, - -255, -246, -237, -229, -220, -212, -204, -196, -188, -180, - -173, -166, -159, -152, -145, -139, -132, -126, -120, -114, - -108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49, - -44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16, - 20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84, - 88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138, - 142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189, - 193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241, - 245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294, - 299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348, - 353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404, - 409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464, - 469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532, - 538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614, - 621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718, - 728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856, - 870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044, - 1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252, - 1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462 -}; - -/* the original LUT values from Alex van Kaam - (for via register values 12-240): -{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31, --30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15, --15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3, --3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12, -12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22, -22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33, -33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45, -45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60, -61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84, -85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110}; - - - Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed - an extra term for a good fit to these inverse data!) and then - solving for each temp value from -50 to 110 (the useable range for - this chip). Here's the fit: - viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4 - - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01) - Note that n=161: */ -static const u8 viaLUT[] = -{ 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23, - 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40, - 41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66, - 69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100, - 103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129, - 131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156, - 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, - 182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199, - 200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213, - 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224, - 225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232, - 233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, - 239, 240 -}; - -/* Converting temps to (8-bit) hyst and over registers - No interpolation here. - The +50 is because the temps start at -50 */ -static inline u8 TEMP_TO_REG(long val) -{ - return viaLUT[val <= -50000 ? 0 : val >= 110000 ? 160 : - (val < 0 ? val - 500 : val + 500) / 1000 + 50]; -} - -/* for 8-bit temperature hyst and over registers */ -#define TEMP_FROM_REG(val) (tempLUT[(val)] * 100) - -/* for 10-bit temperature readings */ -static inline long TEMP_FROM_REG10(u16 val) -{ - u16 eightBits = val >> 2; - u16 twoBits = val & 3; - - /* no interpolation for these */ - if (twoBits == 0 || eightBits == 255) - return TEMP_FROM_REG(eightBits); - - /* do some linear interpolation */ - return (tempLUT[eightBits] * (4 - twoBits) + - tempLUT[eightBits + 1] * twoBits) * 25; -} - -#define DIV_FROM_REG(val) (1 << (val)) -#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) - -/* For the VIA686A, we need to keep some data in memory. - The structure is dynamically allocated, at the same time when a new - via686a client is allocated. */ -struct via686a_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - u8 in[5]; /* Register value */ - u8 in_max[5]; /* Register value */ - u8 in_min[5]; /* Register value */ - u8 fan[2]; /* Register value */ - u8 fan_min[2]; /* Register value */ - u16 temp[3]; /* Register value 10 bit */ - u8 temp_over[3]; /* Register value */ - u8 temp_hyst[3]; /* Register value */ - u8 fan_div[2]; /* Register encoding, shifted right */ - u16 alarms; /* Register encoding, combined */ -}; - -static struct pci_dev *s_bridge; /* pointer to the (only) via686a */ - -static int via686a_attach_adapter(struct i2c_adapter *adapter); -static int via686a_detect(struct i2c_adapter *adapter, int address, int kind); -static int via686a_detach_client(struct i2c_client *client); - -static inline int via686a_read_value(struct i2c_client *client, u8 reg) -{ - return (inb_p(client->addr + reg)); -} - -static inline void via686a_write_value(struct i2c_client *client, u8 reg, - u8 value) -{ - outb_p(value, client->addr + reg); -} - -static struct via686a_data *via686a_update_device(struct device *dev); -static void via686a_init_client(struct i2c_client *client); - -/* following are the sysfs callback functions */ - -/* 7 voltage sensors */ -static ssize_t show_in(struct device *dev, char *buf, int nr) { - struct via686a_data *data = via686a_update_device(dev); - return sprintf(buf, "%ld\n", IN_FROM_REG(data->in[nr], nr)); -} - -static ssize_t show_in_min(struct device *dev, char *buf, int nr) { - struct via686a_data *data = via686a_update_device(dev); - return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_min[nr], nr)); -} - -static ssize_t show_in_max(struct device *dev, char *buf, int nr) { - struct via686a_data *data = via686a_update_device(dev); - return sprintf(buf, "%ld\n", IN_FROM_REG(data->in_max[nr], nr)); -} - -static ssize_t set_in_min(struct device *dev, const char *buf, - size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct via686a_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->in_min[nr] = IN_TO_REG(val, nr); - via686a_write_value(client, VIA686A_REG_IN_MIN(nr), - data->in_min[nr]); - up(&data->update_lock); - return count; -} -static ssize_t set_in_max(struct device *dev, const char *buf, - size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct via686a_data *data = i2c_get_clientdata(client); - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->in_max[nr] = IN_TO_REG(val, nr); - via686a_write_value(client, VIA686A_REG_IN_MAX(nr), - data->in_max[nr]); - up(&data->update_lock); - return count; -} -#define show_in_offset(offset) \ -static ssize_t \ - show_in##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in(dev, buf, offset); \ -} \ -static ssize_t \ - show_in##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in_min(dev, buf, offset); \ -} \ -static ssize_t \ - show_in##offset##_max (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in_max(dev, buf, offset); \ -} \ -static ssize_t set_in##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_in_min(dev, buf, count, offset); \ -} \ -static ssize_t set_in##offset##_max (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_in_max(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_in##offset, NULL);\ -static DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ - show_in##offset##_min, set_in##offset##_min); \ -static DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ - show_in##offset##_max, set_in##offset##_max); - -show_in_offset(0); -show_in_offset(1); -show_in_offset(2); -show_in_offset(3); -show_in_offset(4); - -/* 3 temperatures */ -static ssize_t show_temp(struct device *dev, char *buf, int nr) { - struct via686a_data *data = via686a_update_device(dev); - return sprintf(buf, "%ld\n", TEMP_FROM_REG10(data->temp[nr])); -} -static ssize_t show_temp_over(struct device *dev, char *buf, int nr) { - struct via686a_data *data = via686a_update_device(dev); - return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_over[nr])); -} -static ssize_t show_temp_hyst(struct device *dev, char *buf, int nr) { - struct via686a_data *data = via686a_update_device(dev); - return sprintf(buf, "%ld\n", TEMP_FROM_REG(data->temp_hyst[nr])); -} -static ssize_t set_temp_over(struct device *dev, const char *buf, - size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct via686a_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_over[nr] = TEMP_TO_REG(val); - via686a_write_value(client, VIA686A_REG_TEMP_OVER[nr], - data->temp_over[nr]); - up(&data->update_lock); - return count; -} -static ssize_t set_temp_hyst(struct device *dev, const char *buf, - size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct via686a_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->temp_hyst[nr] = TEMP_TO_REG(val); - via686a_write_value(client, VIA686A_REG_TEMP_HYST[nr], - data->temp_hyst[nr]); - up(&data->update_lock); - return count; -} -#define show_temp_offset(offset) \ -static ssize_t show_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp(dev, buf, offset - 1); \ -} \ -static ssize_t \ -show_temp_##offset##_over (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp_over(dev, buf, offset - 1); \ -} \ -static ssize_t \ -show_temp_##offset##_hyst (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp_hyst(dev, buf, offset - 1); \ -} \ -static ssize_t set_temp_##offset##_over (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_temp_over(dev, buf, count, offset - 1); \ -} \ -static ssize_t set_temp_##offset##_hyst (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_temp_hyst(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_temp_##offset, NULL);\ -static DEVICE_ATTR(temp##offset##_max, S_IRUGO | S_IWUSR, \ - show_temp_##offset##_over, set_temp_##offset##_over); \ -static DEVICE_ATTR(temp##offset##_max_hyst, S_IRUGO | S_IWUSR, \ - show_temp_##offset##_hyst, set_temp_##offset##_hyst); - -show_temp_offset(1); -show_temp_offset(2); -show_temp_offset(3); - -/* 2 Fans */ -static ssize_t show_fan(struct device *dev, char *buf, int nr) { - struct via686a_data *data = via686a_update_device(dev); - return sprintf(buf, "%d\n", FAN_FROM_REG(data->fan[nr], - DIV_FROM_REG(data->fan_div[nr])) ); -} -static ssize_t show_fan_min(struct device *dev, char *buf, int nr) { - struct via686a_data *data = via686a_update_device(dev); - return sprintf(buf, "%d\n", - FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr])) ); -} -static ssize_t show_fan_div(struct device *dev, char *buf, int nr) { - struct via686a_data *data = via686a_update_device(dev); - return sprintf(buf, "%d\n", DIV_FROM_REG(data->fan_div[nr]) ); -} -static ssize_t set_fan_min(struct device *dev, const char *buf, - size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct via686a_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - - down(&data->update_lock); - data->fan_min[nr] = FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr])); - via686a_write_value(client, VIA686A_REG_FAN_MIN(nr+1), data->fan_min[nr]); - up(&data->update_lock); - return count; -} -static ssize_t set_fan_div(struct device *dev, const char *buf, - size_t count, int nr) { - struct i2c_client *client = to_i2c_client(dev); - struct via686a_data *data = i2c_get_clientdata(client); - int val = simple_strtol(buf, NULL, 10); - int old; - - down(&data->update_lock); - old = via686a_read_value(client, VIA686A_REG_FANDIV); - data->fan_div[nr] = DIV_TO_REG(val); - old = (old & 0x0f) | (data->fan_div[1] << 6) | (data->fan_div[0] << 4); - via686a_write_value(client, VIA686A_REG_FANDIV, old); - up(&data->update_lock); - return count; -} - -#define show_fan_offset(offset) \ -static ssize_t show_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan_##offset##_min (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_min(dev, buf, offset - 1); \ -} \ -static ssize_t show_fan_##offset##_div (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_div(dev, buf, offset - 1); \ -} \ -static ssize_t set_fan_##offset##_min (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_fan_min(dev, buf, count, offset - 1); \ -} \ -static ssize_t set_fan_##offset##_div (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return set_fan_div(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_fan_##offset, NULL);\ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_fan_##offset##_min, set_fan_##offset##_min); \ -static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ - show_fan_##offset##_div, set_fan_##offset##_div); - -show_fan_offset(1); -show_fan_offset(2); - -/* Alarms */ -static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf) { - struct via686a_data *data = via686a_update_device(dev); - return sprintf(buf, "%u\n", data->alarms); -} -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); - -/* The driver. I choose to use type i2c_driver, as at is identical to both - smbus_driver and isa_driver, and clients could be of either kind */ -static struct i2c_driver via686a_driver = { - .owner = THIS_MODULE, - .name = "via686a", - .id = I2C_DRIVERID_VIA686A, - .flags = I2C_DF_NOTIFY, - .attach_adapter = via686a_attach_adapter, - .detach_client = via686a_detach_client, -}; - - -/* This is called when the module is loaded */ -static int via686a_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, via686a_detect); -} - -static int via686a_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct via686a_data *data; - int err = 0; - const char client_name[] = "via686a"; - u16 val; - - /* Make sure we are probing the ISA bus!! */ - if (!i2c_is_isa_adapter(adapter)) { - dev_err(&adapter->dev, - "via686a_detect called for an I2C bus adapter?!?\n"); - return 0; - } - - /* 8231 requires multiple of 256, we enforce that on 686 as well */ - if (force_addr) - address = force_addr & 0xFF00; - - if (force_addr) { - dev_warn(&adapter->dev, "forcing ISA address 0x%04X\n", - address); - if (PCIBIOS_SUCCESSFUL != - pci_write_config_word(s_bridge, VIA686A_BASE_REG, address)) - return -ENODEV; - } - if (PCIBIOS_SUCCESSFUL != - pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val)) - return -ENODEV; - if (!(val & 0x0001)) { - dev_warn(&adapter->dev, "enabling sensors\n"); - if (PCIBIOS_SUCCESSFUL != - pci_write_config_word(s_bridge, VIA686A_ENABLE_REG, - val | 0x0001)) - return -ENODEV; - } - - /* Reserve the ISA region */ - if (!request_region(address, VIA686A_EXTENT, via686a_driver.name)) { - dev_err(&adapter->dev, "region 0x%x already in use!\n", - address); - return -ENODEV; - } - - if (!(data = kmalloc(sizeof(struct via686a_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR0; - } - memset(data, 0, sizeof(struct via686a_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &via686a_driver; - new_client->flags = 0; - - /* Fill in the remaining client fields and put into the global list */ - strlcpy(new_client->name, client_name, I2C_NAME_SIZE); - - data->valid = 0; - init_MUTEX(&data->update_lock); - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto ERROR3; - - /* Initialize the VIA686A chip */ - via686a_init_client(new_client); - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_in0_input); - device_create_file(&new_client->dev, &dev_attr_in1_input); - device_create_file(&new_client->dev, &dev_attr_in2_input); - device_create_file(&new_client->dev, &dev_attr_in3_input); - device_create_file(&new_client->dev, &dev_attr_in4_input); - device_create_file(&new_client->dev, &dev_attr_in0_min); - device_create_file(&new_client->dev, &dev_attr_in1_min); - device_create_file(&new_client->dev, &dev_attr_in2_min); - device_create_file(&new_client->dev, &dev_attr_in3_min); - device_create_file(&new_client->dev, &dev_attr_in4_min); - device_create_file(&new_client->dev, &dev_attr_in0_max); - device_create_file(&new_client->dev, &dev_attr_in1_max); - device_create_file(&new_client->dev, &dev_attr_in2_max); - device_create_file(&new_client->dev, &dev_attr_in3_max); - device_create_file(&new_client->dev, &dev_attr_in4_max); - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp2_input); - device_create_file(&new_client->dev, &dev_attr_temp3_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - device_create_file(&new_client->dev, &dev_attr_temp2_max); - device_create_file(&new_client->dev, &dev_attr_temp3_max); - device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&new_client->dev, &dev_attr_temp2_max_hyst); - device_create_file(&new_client->dev, &dev_attr_temp3_max_hyst); - device_create_file(&new_client->dev, &dev_attr_fan1_input); - device_create_file(&new_client->dev, &dev_attr_fan2_input); - device_create_file(&new_client->dev, &dev_attr_fan1_min); - device_create_file(&new_client->dev, &dev_attr_fan2_min); - device_create_file(&new_client->dev, &dev_attr_fan1_div); - device_create_file(&new_client->dev, &dev_attr_fan2_div); - device_create_file(&new_client->dev, &dev_attr_alarms); - - return 0; - -ERROR3: - kfree(data); -ERROR0: - release_region(address, VIA686A_EXTENT); - return err; -} - -static int via686a_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, - "Client deregistration failed, client not detached.\n"); - return err; - } - - release_region(client->addr, VIA686A_EXTENT); - kfree(i2c_get_clientdata(client)); - - return 0; -} - -/* Called when we have found a new VIA686A. Set limits, etc. */ -static void via686a_init_client(struct i2c_client *client) -{ - u8 reg; - - /* Start monitoring */ - reg = via686a_read_value(client, VIA686A_REG_CONFIG); - via686a_write_value(client, VIA686A_REG_CONFIG, (reg|0x01)&0x7F); - - /* Configure temp interrupt mode for continuous-interrupt operation */ - via686a_write_value(client, VIA686A_REG_TEMP_MODE, - via686a_read_value(client, VIA686A_REG_TEMP_MODE) & - !(VIA686A_TEMP_MODE_MASK | VIA686A_TEMP_MODE_CONTINUOUS)); -} - -static struct via686a_data *via686a_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct via686a_data *data = i2c_get_clientdata(client); - int i; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - for (i = 0; i <= 4; i++) { - data->in[i] = - via686a_read_value(client, VIA686A_REG_IN(i)); - data->in_min[i] = via686a_read_value(client, - VIA686A_REG_IN_MIN - (i)); - data->in_max[i] = - via686a_read_value(client, VIA686A_REG_IN_MAX(i)); - } - for (i = 1; i <= 2; i++) { - data->fan[i - 1] = - via686a_read_value(client, VIA686A_REG_FAN(i)); - data->fan_min[i - 1] = via686a_read_value(client, - VIA686A_REG_FAN_MIN(i)); - } - for (i = 0; i <= 2; i++) { - data->temp[i] = via686a_read_value(client, - VIA686A_REG_TEMP[i]) << 2; - data->temp_over[i] = - via686a_read_value(client, - VIA686A_REG_TEMP_OVER[i]); - data->temp_hyst[i] = - via686a_read_value(client, - VIA686A_REG_TEMP_HYST[i]); - } - /* add in lower 2 bits - temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1 - temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23 - temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23 - */ - data->temp[0] |= (via686a_read_value(client, - VIA686A_REG_TEMP_LOW1) - & 0xc0) >> 6; - data->temp[1] |= - (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) & - 0x30) >> 4; - data->temp[2] |= - (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) & - 0xc0) >> 6; - - i = via686a_read_value(client, VIA686A_REG_FANDIV); - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = i >> 6; - data->alarms = - via686a_read_value(client, - VIA686A_REG_ALARM1) | - (via686a_read_value(client, VIA686A_REG_ALARM2) << 8); - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static struct pci_device_id via686a_pci_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C686_4) }, - { 0, } -}; - -MODULE_DEVICE_TABLE(pci, via686a_pci_ids); - -static int __devinit via686a_pci_probe(struct pci_dev *dev, - const struct pci_device_id *id) -{ - u16 val; - int addr = 0; - - if (PCIBIOS_SUCCESSFUL != - pci_read_config_word(dev, VIA686A_BASE_REG, &val)) - return -ENODEV; - - addr = val & ~(VIA686A_EXTENT - 1); - if (addr == 0 && force_addr == 0) { - dev_err(&dev->dev, "base address not set - upgrade BIOS " - "or use force_addr=0xaddr\n"); - return -ENODEV; - } - if (force_addr) - addr = force_addr; /* so detect will get called */ - - if (!addr) { - dev_err(&dev->dev, "No Via 686A sensors found.\n"); - return -ENODEV; - } - normal_isa[0] = addr; - - s_bridge = pci_dev_get(dev); - if (i2c_add_driver(&via686a_driver)) { - pci_dev_put(s_bridge); - s_bridge = NULL; - } - - /* Always return failure here. This is to allow other drivers to bind - * to this pci device. We don't really want to have control over the - * pci device, we only wanted to read as few register values from it. - */ - return -ENODEV; -} - -static struct pci_driver via686a_pci_driver = { - .name = "via686a", - .id_table = via686a_pci_ids, - .probe = via686a_pci_probe, -}; - -static int __init sm_via686a_init(void) -{ - return pci_register_driver(&via686a_pci_driver); -} - -static void __exit sm_via686a_exit(void) -{ - pci_unregister_driver(&via686a_pci_driver); - if (s_bridge != NULL) { - i2c_del_driver(&via686a_driver); - pci_dev_put(s_bridge); - s_bridge = NULL; - } -} - -MODULE_AUTHOR("Kyösti Mälkki , " - "Mark Studebaker " - "and Bob Dougherty "); -MODULE_DESCRIPTION("VIA 686A Sensor device"); -MODULE_LICENSE("GPL"); - -module_init(sm_via686a_init); -module_exit(sm_via686a_exit); diff --git a/drivers/i2c/chips/w83627ehf.c b/drivers/i2c/chips/w83627ehf.c deleted file mode 100644 index 8a40b6976e1a..000000000000 --- a/drivers/i2c/chips/w83627ehf.c +++ /dev/null @@ -1,846 +0,0 @@ -/* - w83627ehf - Driver for the hardware monitoring functionality of - the Winbond W83627EHF Super-I/O chip - Copyright (C) 2005 Jean Delvare - - Shamelessly ripped from the w83627hf driver - Copyright (C) 2003 Mark Studebaker - - Thanks to Leon Moonen, Steve Cliffe and Grant Coady for their help - in testing and debugging this driver. - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. - - - Supports the following chips: - - Chip #vin #fan #pwm #temp chip_id man_id - w83627ehf - 5 - 3 0x88 0x5ca3 - - This is a preliminary version of the driver, only supporting the - fan and temperature inputs. The chip does much more than that. -*/ - -#include -#include -#include -#include -#include -#include -#include "lm75.h" - -/* Addresses to scan - The actual ISA address is read from Super-I/O configuration space */ -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; -static unsigned int normal_isa[] = { 0, I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_1(w83627ehf); - -/* - * Super-I/O constants and functions - */ - -static int REG; /* The register to read/write */ -static int VAL; /* The value to read/write */ - -#define W83627EHF_LD_HWM 0x0b - -#define SIO_REG_LDSEL 0x07 /* Logical device select */ -#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ -#define SIO_REG_ENABLE 0x30 /* Logical device enable */ -#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ - -#define SIO_W83627EHF_ID 0x8840 -#define SIO_ID_MASK 0xFFC0 - -static inline void -superio_outb(int reg, int val) -{ - outb(reg, REG); - outb(val, VAL); -} - -static inline int -superio_inb(int reg) -{ - outb(reg, REG); - return inb(VAL); -} - -static inline void -superio_select(int ld) -{ - outb(SIO_REG_LDSEL, REG); - outb(ld, VAL); -} - -static inline void -superio_enter(void) -{ - outb(0x87, REG); - outb(0x87, REG); -} - -static inline void -superio_exit(void) -{ - outb(0x02, REG); - outb(0x02, VAL); -} - -/* - * ISA constants - */ - -#define REGION_LENGTH 8 -#define ADDR_REG_OFFSET 5 -#define DATA_REG_OFFSET 6 - -#define W83627EHF_REG_BANK 0x4E -#define W83627EHF_REG_CONFIG 0x40 -#define W83627EHF_REG_CHIP_ID 0x49 -#define W83627EHF_REG_MAN_ID 0x4F - -static const u16 W83627EHF_REG_FAN[] = { 0x28, 0x29, 0x2a, 0x3f, 0x553 }; -static const u16 W83627EHF_REG_FAN_MIN[] = { 0x3b, 0x3c, 0x3d, 0x3e, 0x55c }; - -#define W83627EHF_REG_TEMP1 0x27 -#define W83627EHF_REG_TEMP1_HYST 0x3a -#define W83627EHF_REG_TEMP1_OVER 0x39 -static const u16 W83627EHF_REG_TEMP[] = { 0x150, 0x250 }; -static const u16 W83627EHF_REG_TEMP_HYST[] = { 0x153, 0x253 }; -static const u16 W83627EHF_REG_TEMP_OVER[] = { 0x155, 0x255 }; -static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 }; - -/* Fan clock dividers are spread over the following five registers */ -#define W83627EHF_REG_FANDIV1 0x47 -#define W83627EHF_REG_FANDIV2 0x4B -#define W83627EHF_REG_VBAT 0x5D -#define W83627EHF_REG_DIODE 0x59 -#define W83627EHF_REG_SMI_OVT 0x4C - -/* - * Conversions - */ - -static inline unsigned int -fan_from_reg(u8 reg, unsigned int div) -{ - if (reg == 0 || reg == 255) - return 0; - return 1350000U / (reg * div); -} - -static inline unsigned int -div_from_reg(u8 reg) -{ - return 1 << reg; -} - -static inline int -temp1_from_reg(s8 reg) -{ - return reg * 1000; -} - -static inline s8 -temp1_to_reg(int temp) -{ - if (temp <= -128000) - return -128; - if (temp >= 127000) - return 127; - if (temp < 0) - return (temp - 500) / 1000; - return (temp + 500) / 1000; -} - -/* - * Data structures and manipulation thereof - */ - -struct w83627ehf_data { - struct i2c_client client; - struct semaphore lock; - - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - /* Register values */ - u8 fan[5]; - u8 fan_min[5]; - u8 fan_div[5]; - u8 has_fan; /* some fan inputs can be disabled */ - s8 temp1; - s8 temp1_max; - s8 temp1_max_hyst; - s16 temp[2]; - s16 temp_max[2]; - s16 temp_max_hyst[2]; -}; - -static inline int is_word_sized(u16 reg) -{ - return (((reg & 0xff00) == 0x100 - || (reg & 0xff00) == 0x200) - && ((reg & 0x00ff) == 0x50 - || (reg & 0x00ff) == 0x53 - || (reg & 0x00ff) == 0x55)); -} - -/* We assume that the default bank is 0, thus the following two functions do - nothing for registers which live in bank 0. For others, they respectively - set the bank register to the correct value (before the register is - accessed), and back to 0 (afterwards). */ -static inline void w83627ehf_set_bank(struct i2c_client *client, u16 reg) -{ - if (reg & 0xff00) { - outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET); - outb_p(reg >> 8, client->addr + DATA_REG_OFFSET); - } -} - -static inline void w83627ehf_reset_bank(struct i2c_client *client, u16 reg) -{ - if (reg & 0xff00) { - outb_p(W83627EHF_REG_BANK, client->addr + ADDR_REG_OFFSET); - outb_p(0, client->addr + DATA_REG_OFFSET); - } -} - -static u16 w83627ehf_read_value(struct i2c_client *client, u16 reg) -{ - struct w83627ehf_data *data = i2c_get_clientdata(client); - int res, word_sized = is_word_sized(reg); - - down(&data->lock); - - w83627ehf_set_bank(client, reg); - outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET); - res = inb_p(client->addr + DATA_REG_OFFSET); - if (word_sized) { - outb_p((reg & 0xff) + 1, - client->addr + ADDR_REG_OFFSET); - res = (res << 8) + inb_p(client->addr + DATA_REG_OFFSET); - } - w83627ehf_reset_bank(client, reg); - - up(&data->lock); - - return res; -} - -static int w83627ehf_write_value(struct i2c_client *client, u16 reg, u16 value) -{ - struct w83627ehf_data *data = i2c_get_clientdata(client); - int word_sized = is_word_sized(reg); - - down(&data->lock); - - w83627ehf_set_bank(client, reg); - outb_p(reg & 0xff, client->addr + ADDR_REG_OFFSET); - if (word_sized) { - outb_p(value >> 8, client->addr + DATA_REG_OFFSET); - outb_p((reg & 0xff) + 1, - client->addr + ADDR_REG_OFFSET); - } - outb_p(value & 0xff, client->addr + DATA_REG_OFFSET); - w83627ehf_reset_bank(client, reg); - - up(&data->lock); - return 0; -} - -/* This function assumes that the caller holds data->update_lock */ -static void w83627ehf_write_fan_div(struct i2c_client *client, int nr) -{ - struct w83627ehf_data *data = i2c_get_clientdata(client); - u8 reg; - - switch (nr) { - case 0: - reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0xcf) - | ((data->fan_div[0] & 0x03) << 4); - w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg); - reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xdf) - | ((data->fan_div[0] & 0x04) << 3); - w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg); - break; - case 1: - reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV1) & 0x3f) - | ((data->fan_div[1] & 0x03) << 6); - w83627ehf_write_value(client, W83627EHF_REG_FANDIV1, reg); - reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0xbf) - | ((data->fan_div[1] & 0x04) << 4); - w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg); - break; - case 2: - reg = (w83627ehf_read_value(client, W83627EHF_REG_FANDIV2) & 0x3f) - | ((data->fan_div[2] & 0x03) << 6); - w83627ehf_write_value(client, W83627EHF_REG_FANDIV2, reg); - reg = (w83627ehf_read_value(client, W83627EHF_REG_VBAT) & 0x7f) - | ((data->fan_div[2] & 0x04) << 5); - w83627ehf_write_value(client, W83627EHF_REG_VBAT, reg); - break; - case 3: - reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0xfc) - | (data->fan_div[3] & 0x03); - w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg); - reg = (w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT) & 0x7f) - | ((data->fan_div[3] & 0x04) << 5); - w83627ehf_write_value(client, W83627EHF_REG_SMI_OVT, reg); - break; - case 4: - reg = (w83627ehf_read_value(client, W83627EHF_REG_DIODE) & 0x73) - | ((data->fan_div[4] & 0x03) << 3) - | ((data->fan_div[4] & 0x04) << 5); - w83627ehf_write_value(client, W83627EHF_REG_DIODE, reg); - break; - } -} - -static struct w83627ehf_data *w83627ehf_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83627ehf_data *data = i2c_get_clientdata(client); - int i; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ) - || !data->valid) { - /* Fan clock dividers */ - i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1); - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = (i >> 6) & 0x03; - i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV2); - data->fan_div[2] = (i >> 6) & 0x03; - i = w83627ehf_read_value(client, W83627EHF_REG_VBAT); - data->fan_div[0] |= (i >> 3) & 0x04; - data->fan_div[1] |= (i >> 4) & 0x04; - data->fan_div[2] |= (i >> 5) & 0x04; - if (data->has_fan & ((1 << 3) | (1 << 4))) { - i = w83627ehf_read_value(client, W83627EHF_REG_DIODE); - data->fan_div[3] = i & 0x03; - data->fan_div[4] = ((i >> 2) & 0x03) - | ((i >> 5) & 0x04); - } - if (data->has_fan & (1 << 3)) { - i = w83627ehf_read_value(client, W83627EHF_REG_SMI_OVT); - data->fan_div[3] |= (i >> 5) & 0x04; - } - - /* Measured fan speeds and limits */ - for (i = 0; i < 5; i++) { - if (!(data->has_fan & (1 << i))) - continue; - - data->fan[i] = w83627ehf_read_value(client, - W83627EHF_REG_FAN[i]); - data->fan_min[i] = w83627ehf_read_value(client, - W83627EHF_REG_FAN_MIN[i]); - - /* If we failed to measure the fan speed and clock - divider can be increased, let's try that for next - time */ - if (data->fan[i] == 0xff - && data->fan_div[i] < 0x07) { - dev_dbg(&client->dev, "Increasing fan %d " - "clock divider from %u to %u\n", - i, div_from_reg(data->fan_div[i]), - div_from_reg(data->fan_div[i] + 1)); - data->fan_div[i]++; - w83627ehf_write_fan_div(client, i); - /* Preserve min limit if possible */ - if (data->fan_min[i] >= 2 - && data->fan_min[i] != 255) - w83627ehf_write_value(client, - W83627EHF_REG_FAN_MIN[i], - (data->fan_min[i] /= 2)); - } - } - - /* Measured temperatures and limits */ - data->temp1 = w83627ehf_read_value(client, - W83627EHF_REG_TEMP1); - data->temp1_max = w83627ehf_read_value(client, - W83627EHF_REG_TEMP1_OVER); - data->temp1_max_hyst = w83627ehf_read_value(client, - W83627EHF_REG_TEMP1_HYST); - for (i = 0; i < 2; i++) { - data->temp[i] = w83627ehf_read_value(client, - W83627EHF_REG_TEMP[i]); - data->temp_max[i] = w83627ehf_read_value(client, - W83627EHF_REG_TEMP_OVER[i]); - data->temp_max_hyst[i] = w83627ehf_read_value(client, - W83627EHF_REG_TEMP_HYST[i]); - } - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - return data; -} - -/* - * Sysfs callback functions - */ - -#define show_fan_reg(reg) \ -static ssize_t \ -show_##reg(struct device *dev, char *buf, int nr) \ -{ \ - struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - return sprintf(buf, "%d\n", \ - fan_from_reg(data->reg[nr], \ - div_from_reg(data->fan_div[nr]))); \ -} -show_fan_reg(fan); -show_fan_reg(fan_min); - -static ssize_t -show_fan_div(struct device *dev, char *buf, int nr) -{ - struct w83627ehf_data *data = w83627ehf_update_device(dev); - return sprintf(buf, "%u\n", - div_from_reg(data->fan_div[nr])); -} - -static ssize_t -store_fan_min(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83627ehf_data *data = i2c_get_clientdata(client); - unsigned int val = simple_strtoul(buf, NULL, 10); - unsigned int reg; - u8 new_div; - - down(&data->update_lock); - if (!val) { - /* No min limit, alarm disabled */ - data->fan_min[nr] = 255; - new_div = data->fan_div[nr]; /* No change */ - dev_info(dev, "fan%u low limit and alarm disabled\n", nr + 1); - } else if ((reg = 1350000U / val) >= 128 * 255) { - /* Speed below this value cannot possibly be represented, - even with the highest divider (128) */ - data->fan_min[nr] = 254; - new_div = 7; /* 128 == (1 << 7) */ - dev_warn(dev, "fan%u low limit %u below minimum %u, set to " - "minimum\n", nr + 1, val, fan_from_reg(254, 128)); - } else if (!reg) { - /* Speed above this value cannot possibly be represented, - even with the lowest divider (1) */ - data->fan_min[nr] = 1; - new_div = 0; /* 1 == (1 << 0) */ - dev_warn(dev, "fan%u low limit %u above maximum %u, set to " - "maximum\n", nr + 1, val, fan_from_reg(1, 1)); - } else { - /* Automatically pick the best divider, i.e. the one such - that the min limit will correspond to a register value - in the 96..192 range */ - new_div = 0; - while (reg > 192 && new_div < 7) { - reg >>= 1; - new_div++; - } - data->fan_min[nr] = reg; - } - - /* Write both the fan clock divider (if it changed) and the new - fan min (unconditionally) */ - if (new_div != data->fan_div[nr]) { - if (new_div > data->fan_div[nr]) - data->fan[nr] >>= (data->fan_div[nr] - new_div); - else - data->fan[nr] <<= (new_div - data->fan_div[nr]); - - dev_dbg(dev, "fan%u clock divider changed from %u to %u\n", - nr + 1, div_from_reg(data->fan_div[nr]), - div_from_reg(new_div)); - data->fan_div[nr] = new_div; - w83627ehf_write_fan_div(client, nr); - } - w83627ehf_write_value(client, W83627EHF_REG_FAN_MIN[nr], - data->fan_min[nr]); - up(&data->update_lock); - - return count; -} - -#define sysfs_fan_offset(offset) \ -static ssize_t \ -show_reg_fan_##offset(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_fan(dev, buf, offset-1); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ - show_reg_fan_##offset, NULL); - -#define sysfs_fan_min_offset(offset) \ -static ssize_t \ -show_reg_fan##offset##_min(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_fan_min(dev, buf, offset-1); \ -} \ -static ssize_t \ -store_reg_fan##offset##_min(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return store_fan_min(dev, buf, count, offset-1); \ -} \ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_reg_fan##offset##_min, \ - store_reg_fan##offset##_min); - -#define sysfs_fan_div_offset(offset) \ -static ssize_t \ -show_reg_fan##offset##_div(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_fan_div(dev, buf, offset - 1); \ -} \ -static DEVICE_ATTR(fan##offset##_div, S_IRUGO, \ - show_reg_fan##offset##_div, NULL); - -sysfs_fan_offset(1); -sysfs_fan_min_offset(1); -sysfs_fan_div_offset(1); -sysfs_fan_offset(2); -sysfs_fan_min_offset(2); -sysfs_fan_div_offset(2); -sysfs_fan_offset(3); -sysfs_fan_min_offset(3); -sysfs_fan_div_offset(3); -sysfs_fan_offset(4); -sysfs_fan_min_offset(4); -sysfs_fan_div_offset(4); -sysfs_fan_offset(5); -sysfs_fan_min_offset(5); -sysfs_fan_div_offset(5); - -#define show_temp1_reg(reg) \ -static ssize_t \ -show_##reg(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - return sprintf(buf, "%d\n", temp1_from_reg(data->reg)); \ -} -show_temp1_reg(temp1); -show_temp1_reg(temp1_max); -show_temp1_reg(temp1_max_hyst); - -#define store_temp1_reg(REG, reg) \ -static ssize_t \ -store_temp1_##reg(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct w83627ehf_data *data = i2c_get_clientdata(client); \ - u32 val = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->temp1_##reg = temp1_to_reg(val); \ - w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \ - data->temp1_##reg); \ - up(&data->update_lock); \ - return count; \ -} -store_temp1_reg(OVER, max); -store_temp1_reg(HYST, max_hyst); - -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp1, NULL); -static DEVICE_ATTR(temp1_max, S_IRUGO| S_IWUSR, - show_temp1_max, store_temp1_max); -static DEVICE_ATTR(temp1_max_hyst, S_IRUGO| S_IWUSR, - show_temp1_max_hyst, store_temp1_max_hyst); - -#define show_temp_reg(reg) \ -static ssize_t \ -show_##reg (struct device *dev, char *buf, int nr) \ -{ \ - struct w83627ehf_data *data = w83627ehf_update_device(dev); \ - return sprintf(buf, "%d\n", \ - LM75_TEMP_FROM_REG(data->reg[nr])); \ -} -show_temp_reg(temp); -show_temp_reg(temp_max); -show_temp_reg(temp_max_hyst); - -#define store_temp_reg(REG, reg) \ -static ssize_t \ -store_##reg (struct device *dev, const char *buf, size_t count, int nr) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct w83627ehf_data *data = i2c_get_clientdata(client); \ - u32 val = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->reg[nr] = LM75_TEMP_TO_REG(val); \ - w83627ehf_write_value(client, W83627EHF_REG_TEMP_##REG[nr], \ - data->reg[nr]); \ - up(&data->update_lock); \ - return count; \ -} -store_temp_reg(OVER, temp_max); -store_temp_reg(HYST, temp_max_hyst); - -#define sysfs_temp_offset(offset) \ -static ssize_t \ -show_reg_temp##offset (struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_temp(dev, buf, offset - 2); \ -} \ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, \ - show_reg_temp##offset, NULL); - -#define sysfs_temp_reg_offset(reg, offset) \ -static ssize_t \ -show_reg_temp##offset##_##reg(struct device *dev, struct device_attribute *attr, \ - char *buf) \ -{ \ - return show_temp_##reg(dev, buf, offset - 2); \ -} \ -static ssize_t \ -store_reg_temp##offset##_##reg(struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return store_temp_##reg(dev, buf, count, offset - 2); \ -} \ -static DEVICE_ATTR(temp##offset##_##reg, S_IRUGO| S_IWUSR, \ - show_reg_temp##offset##_##reg, \ - store_reg_temp##offset##_##reg); - -sysfs_temp_offset(2); -sysfs_temp_reg_offset(max, 2); -sysfs_temp_reg_offset(max_hyst, 2); -sysfs_temp_offset(3); -sysfs_temp_reg_offset(max, 3); -sysfs_temp_reg_offset(max_hyst, 3); - -/* - * Driver and client management - */ - -static struct i2c_driver w83627ehf_driver; - -static void w83627ehf_init_client(struct i2c_client *client) -{ - int i; - u8 tmp; - - /* Start monitoring is needed */ - tmp = w83627ehf_read_value(client, W83627EHF_REG_CONFIG); - if (!(tmp & 0x01)) - w83627ehf_write_value(client, W83627EHF_REG_CONFIG, - tmp | 0x01); - - /* Enable temp2 and temp3 if needed */ - for (i = 0; i < 2; i++) { - tmp = w83627ehf_read_value(client, - W83627EHF_REG_TEMP_CONFIG[i]); - if (tmp & 0x01) - w83627ehf_write_value(client, - W83627EHF_REG_TEMP_CONFIG[i], - tmp & 0xfe); - } -} - -static int w83627ehf_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *client; - struct w83627ehf_data *data; - int i, err = 0; - - if (!i2c_is_isa_adapter(adapter)) - return 0; - - if (!request_region(address, REGION_LENGTH, w83627ehf_driver.name)) { - err = -EBUSY; - goto exit; - } - - if (!(data = kmalloc(sizeof(struct w83627ehf_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit_release; - } - memset(data, 0, sizeof(struct w83627ehf_data)); - - client = &data->client; - i2c_set_clientdata(client, data); - client->addr = address; - init_MUTEX(&data->lock); - client->adapter = adapter; - client->driver = &w83627ehf_driver; - client->flags = 0; - - strlcpy(client->name, "w83627ehf", I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the i2c layer a new client has arrived */ - if ((err = i2c_attach_client(client))) - goto exit_free; - - /* Initialize the chip */ - w83627ehf_init_client(client); - - /* A few vars need to be filled upon startup */ - for (i = 0; i < 5; i++) - data->fan_min[i] = w83627ehf_read_value(client, - W83627EHF_REG_FAN_MIN[i]); - - /* It looks like fan4 and fan5 pins can be alternatively used - as fan on/off switches */ - data->has_fan = 0x07; /* fan1, fan2 and fan3 */ - i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1); - if (i & (1 << 2)) - data->has_fan |= (1 << 3); - if (i & (1 << 0)) - data->has_fan |= (1 << 4); - - /* Register sysfs hooks */ - device_create_file(&client->dev, &dev_attr_fan1_input); - device_create_file(&client->dev, &dev_attr_fan1_min); - device_create_file(&client->dev, &dev_attr_fan1_div); - device_create_file(&client->dev, &dev_attr_fan2_input); - device_create_file(&client->dev, &dev_attr_fan2_min); - device_create_file(&client->dev, &dev_attr_fan2_div); - device_create_file(&client->dev, &dev_attr_fan3_input); - device_create_file(&client->dev, &dev_attr_fan3_min); - device_create_file(&client->dev, &dev_attr_fan3_div); - - if (data->has_fan & (1 << 3)) { - device_create_file(&client->dev, &dev_attr_fan4_input); - device_create_file(&client->dev, &dev_attr_fan4_min); - device_create_file(&client->dev, &dev_attr_fan4_div); - } - if (data->has_fan & (1 << 4)) { - device_create_file(&client->dev, &dev_attr_fan5_input); - device_create_file(&client->dev, &dev_attr_fan5_min); - device_create_file(&client->dev, &dev_attr_fan5_div); - } - - device_create_file(&client->dev, &dev_attr_temp1_input); - device_create_file(&client->dev, &dev_attr_temp1_max); - device_create_file(&client->dev, &dev_attr_temp1_max_hyst); - device_create_file(&client->dev, &dev_attr_temp2_input); - device_create_file(&client->dev, &dev_attr_temp2_max); - device_create_file(&client->dev, &dev_attr_temp2_max_hyst); - device_create_file(&client->dev, &dev_attr_temp3_input); - device_create_file(&client->dev, &dev_attr_temp3_max); - device_create_file(&client->dev, &dev_attr_temp3_max_hyst); - - return 0; - -exit_free: - kfree(data); -exit_release: - release_region(address, REGION_LENGTH); -exit: - return err; -} - -static int w83627ehf_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, w83627ehf_detect); -} - -static int w83627ehf_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - release_region(client->addr, REGION_LENGTH); - kfree(i2c_get_clientdata(client)); - - return 0; -} - -static struct i2c_driver w83627ehf_driver = { - .owner = THIS_MODULE, - .name = "w83627ehf", - .flags = I2C_DF_NOTIFY, - .attach_adapter = w83627ehf_attach_adapter, - .detach_client = w83627ehf_detach_client, -}; - -static int __init w83627ehf_find(int sioaddr, int *address) -{ - u16 val; - - REG = sioaddr; - VAL = sioaddr + 1; - superio_enter(); - - val = (superio_inb(SIO_REG_DEVID) << 8) - | superio_inb(SIO_REG_DEVID + 1); - if ((val & SIO_ID_MASK) != SIO_W83627EHF_ID) { - superio_exit(); - return -ENODEV; - } - - superio_select(W83627EHF_LD_HWM); - val = (superio_inb(SIO_REG_ADDR) << 8) - | superio_inb(SIO_REG_ADDR + 1); - *address = val & ~(REGION_LENGTH - 1); - if (*address == 0) { - superio_exit(); - return -ENODEV; - } - - /* Activate logical device if needed */ - val = superio_inb(SIO_REG_ENABLE); - if (!(val & 0x01)) - superio_outb(SIO_REG_ENABLE, val | 0x01); - - superio_exit(); - return 0; -} - -static int __init sensors_w83627ehf_init(void) -{ - if (w83627ehf_find(0x2e, &normal_isa[0]) - && w83627ehf_find(0x4e, &normal_isa[0])) - return -ENODEV; - - return i2c_add_driver(&w83627ehf_driver); -} - -static void __exit sensors_w83627ehf_exit(void) -{ - i2c_del_driver(&w83627ehf_driver); -} - -MODULE_AUTHOR("Jean Delvare "); -MODULE_DESCRIPTION("W83627EHF driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_w83627ehf_init); -module_exit(sensors_w83627ehf_exit); diff --git a/drivers/i2c/chips/w83627hf.c b/drivers/i2c/chips/w83627hf.c deleted file mode 100644 index bd87a42e068a..000000000000 --- a/drivers/i2c/chips/w83627hf.c +++ /dev/null @@ -1,1511 +0,0 @@ -/* - w83627hf.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998 - 2003 Frodo Looijaard , - Philip Edelbrock , - and Mark Studebaker - Ported to 2.6 by Bernhard C. Schrenk - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - Supports following chips: - - Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA - w83627hf 9 3 2 3 0x20 0x5ca3 no yes(LPC) - w83627thf 7 3 3 3 0x90 0x5ca3 no yes(LPC) - w83637hf 7 3 3 3 0x80 0x5ca3 no yes(LPC) - w83697hf 8 2 2 2 0x60 0x5ca3 no yes(LPC) - - For other winbond chips, and for i2c support in the above chips, - use w83781d.c. - - Note: automatic ("cruise") fan control for 697, 637 & 627thf not - supported yet. -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "lm75.h" - -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, - "Initialize the i2c address of the sensors"); - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { I2C_CLIENT_END }; -static unsigned int normal_isa[] = { 0, I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_4(w83627hf, w83627thf, w83697hf, w83637hf); - -static int init = 1; -module_param(init, bool, 0); -MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); - -/* 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 -#define W83627HF_LD_PRT 0x01 -#define W83627HF_LD_UART1 0x02 -#define W83627HF_LD_UART2 0x03 -#define W83627HF_LD_KBC 0x05 -#define W83627HF_LD_CIR 0x06 /* w83627hf only */ -#define W83627HF_LD_GAME 0x07 -#define W83627HF_LD_MIDI 0x07 -#define W83627HF_LD_GPIO1 0x07 -#define W83627HF_LD_GPIO5 0x07 /* w83627thf only */ -#define W83627HF_LD_GPIO2 0x08 -#define W83627HF_LD_GPIO3 0x09 -#define W83627HF_LD_GPIO4 0x09 /* w83627thf only */ -#define W83627HF_LD_ACPI 0x0a -#define W83627HF_LD_HWM 0x0b - -#define DEVID 0x20 /* Register: Device ID */ - -#define W83627THF_GPIO5_EN 0x30 /* w83627thf only */ -#define W83627THF_GPIO5_IOSR 0xf3 /* w83627thf only */ -#define W83627THF_GPIO5_DR 0xf4 /* w83627thf only */ - -static inline void -superio_outb(int reg, int val) -{ - outb(reg, REG); - outb(val, VAL); -} - -static inline int -superio_inb(int reg) -{ - outb(reg, REG); - return inb(VAL); -} - -static inline void -superio_select(int ld) -{ - outb(DEV, REG); - outb(ld, VAL); -} - -static inline void -superio_enter(void) -{ - outb(0x87, REG); - outb(0x87, REG); -} - -static inline void -superio_exit(void) -{ - outb(0xAA, REG); -} - -#define W627_DEVID 0x52 -#define W627THF_DEVID 0x82 -#define W697_DEVID 0x60 -#define W637_DEVID 0x70 -#define WINB_ACT_REG 0x30 -#define WINB_BASE_REG 0x60 -/* Constants specified below */ - -/* Length of ISA address segment */ -#define WINB_EXTENT 8 - -/* Where are the ISA address/data registers relative to the base address */ -#define W83781D_ADDR_REG_OFFSET 5 -#define W83781D_DATA_REG_OFFSET 6 - -/* The W83781D registers */ -/* The W83782D registers for nr=7,8 are in bank 5 */ -#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ - (0x554 + (((nr) - 7) * 2))) -#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ - (0x555 + (((nr) - 7) * 2))) -#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ - (0x550 + (nr) - 7)) - -#define W83781D_REG_FAN_MIN(nr) (0x3a + (nr)) -#define W83781D_REG_FAN(nr) (0x27 + (nr)) - -#define W83781D_REG_TEMP2_CONFIG 0x152 -#define W83781D_REG_TEMP3_CONFIG 0x252 -#define W83781D_REG_TEMP(nr) ((nr == 3) ? (0x0250) : \ - ((nr == 2) ? (0x0150) : \ - (0x27))) -#define W83781D_REG_TEMP_HYST(nr) ((nr == 3) ? (0x253) : \ - ((nr == 2) ? (0x153) : \ - (0x3A))) -#define W83781D_REG_TEMP_OVER(nr) ((nr == 3) ? (0x255) : \ - ((nr == 2) ? (0x155) : \ - (0x39))) - -#define W83781D_REG_BANK 0x4E - -#define W83781D_REG_CONFIG 0x40 -#define W83781D_REG_ALARM1 0x41 -#define W83781D_REG_ALARM2 0x42 -#define W83781D_REG_ALARM3 0x450 - -#define W83781D_REG_IRQ 0x4C -#define W83781D_REG_BEEP_CONFIG 0x4D -#define W83781D_REG_BEEP_INTS1 0x56 -#define W83781D_REG_BEEP_INTS2 0x57 -#define W83781D_REG_BEEP_INTS3 0x453 - -#define W83781D_REG_VID_FANDIV 0x47 - -#define W83781D_REG_CHIPID 0x49 -#define W83781D_REG_WCHIPID 0x58 -#define W83781D_REG_CHIPMAN 0x4F -#define W83781D_REG_PIN 0x4B - -#define W83781D_REG_VBAT 0x5D - -#define W83627HF_REG_PWM1 0x5A -#define W83627HF_REG_PWM2 0x5B -#define W83627HF_REG_PWMCLK12 0x5C - -#define W83627THF_REG_PWM1 0x01 /* 697HF and 637HF too */ -#define W83627THF_REG_PWM2 0x03 /* 697HF and 637HF too */ -#define W83627THF_REG_PWM3 0x11 /* 637HF too */ - -#define W83627THF_REG_VRM_OVT_CFG 0x18 /* 637HF too */ - -static const u8 regpwm_627hf[] = { W83627HF_REG_PWM1, W83627HF_REG_PWM2 }; -static const u8 regpwm[] = { W83627THF_REG_PWM1, W83627THF_REG_PWM2, - W83627THF_REG_PWM3 }; -#define W836X7HF_REG_PWM(type, nr) (((type) == w83627hf) ? \ - regpwm_627hf[(nr) - 1] : regpwm[(nr) - 1]) - -#define W83781D_REG_I2C_ADDR 0x48 -#define W83781D_REG_I2C_SUBADDR 0x4A - -/* Sensor selection */ -#define W83781D_REG_SCFG1 0x5D -static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; -#define W83781D_REG_SCFG2 0x59 -static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; -#define W83781D_DEFAULT_BETA 3435 - -/* Conversions. Limit checking is only done on the TO_REG - variants. Note that you should be a bit careful with which arguments - these macros are called: arguments may be evaluated more than once. - Fixing this is just not worth it. */ -#define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8)/16),0,255)) -#define IN_FROM_REG(val) ((val) * 16) - -static inline u8 FAN_TO_REG(long rpm, int div) -{ - if (rpm == 0) - return 255; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, - 254); -} - -#define TEMP_MIN (-128000) -#define TEMP_MAX ( 127000) - -/* TEMP: 0.001C/bit (-128C to +127C) - REG: 1C/bit, two's complement */ -static u8 TEMP_TO_REG(int temp) -{ - int ntemp = SENSORS_LIMIT(temp, TEMP_MIN, TEMP_MAX); - ntemp += (ntemp<0 ? -500 : 500); - return (u8)(ntemp / 1000); -} - -static int TEMP_FROM_REG(u8 reg) -{ - return (s8)reg * 1000; -} - -#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) - -#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255)) - -#define BEEP_MASK_FROM_REG(val) (val) -#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff) -#define BEEP_ENABLE_TO_REG(val) ((val)?1:0) -#define BEEP_ENABLE_FROM_REG(val) ((val)?1:0) - -#define DIV_FROM_REG(val) (1 << (val)) - -static inline u8 DIV_TO_REG(long val) -{ - int i; - val = SENSORS_LIMIT(val, 1, 128) >> 1; - for (i = 0; i < 7; i++) { - if (val == 0) - break; - val >>= 1; - } - return ((u8) i); -} - -/* For each registered chip, we need to keep some data in memory. That - data is pointed to by w83627hf_list[NR]->data. The structure itself is - dynamically allocated, at the same time when a new client is allocated. */ -struct w83627hf_data { - struct i2c_client client; - struct semaphore lock; - enum chips type; - - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - struct i2c_client *lm75; /* for secondary I2C addresses */ - /* pointer to array of 2 subclients */ - - u8 in[9]; /* Register value */ - u8 in_max[9]; /* Register value */ - u8 in_min[9]; /* Register value */ - u8 fan[3]; /* Register value */ - u8 fan_min[3]; /* Register value */ - u8 temp; - u8 temp_max; /* Register value */ - u8 temp_max_hyst; /* Register value */ - u16 temp_add[2]; /* Register value */ - u16 temp_max_add[2]; /* Register value */ - u16 temp_max_hyst_add[2]; /* Register value */ - u8 fan_div[3]; /* Register encoding, shifted right */ - u8 vid; /* Register encoding, combined */ - u32 alarms; /* Register encoding, combined */ - u32 beep_mask; /* Register encoding, combined */ - u8 beep_enable; /* Boolean */ - u8 pwm[3]; /* Register value */ - u16 sens[3]; /* 782D/783S only. - 1 = pentium diode; 2 = 3904 diode; - 3000-5000 = thermistor beta. - Default = 3435. - Other Betas unimplemented */ - u8 vrm; - u8 vrm_ovt; /* Register value, 627thf & 637hf only */ -}; - - -static int w83627hf_attach_adapter(struct i2c_adapter *adapter); -static int w83627hf_detect(struct i2c_adapter *adapter, int address, - int kind); -static int w83627hf_detach_client(struct i2c_client *client); - -static int w83627hf_read_value(struct i2c_client *client, u16 register); -static int w83627hf_write_value(struct i2c_client *client, u16 register, - u16 value); -static struct w83627hf_data *w83627hf_update_device(struct device *dev); -static void w83627hf_init_client(struct i2c_client *client); - -static struct i2c_driver w83627hf_driver = { - .owner = THIS_MODULE, - .name = "w83627hf", - .id = I2C_DRIVERID_W83627HF, - .flags = I2C_DF_NOTIFY, - .attach_adapter = w83627hf_attach_adapter, - .detach_client = w83627hf_detach_client, -}; - -/* following are the sysfs callback functions */ -#define show_in_reg(reg) \ -static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ -{ \ - struct w83627hf_data *data = w83627hf_update_device(dev); \ - return sprintf(buf,"%ld\n", (long)IN_FROM_REG(data->reg[nr])); \ -} -show_in_reg(in) -show_in_reg(in_min) -show_in_reg(in_max) - -#define store_in_reg(REG, reg) \ -static ssize_t \ -store_in_##reg (struct device *dev, const char *buf, size_t count, int nr) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct w83627hf_data *data = i2c_get_clientdata(client); \ - u32 val; \ - \ - val = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - data->in_##reg[nr] = IN_TO_REG(val); \ - w83627hf_write_value(client, W83781D_REG_IN_##REG(nr), \ - data->in_##reg[nr]); \ - \ - up(&data->update_lock); \ - return count; \ -} -store_in_reg(MIN, min) -store_in_reg(MAX, max) - -#define sysfs_in_offset(offset) \ -static ssize_t \ -show_regs_in_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in(dev, buf, offset); \ -} \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_regs_in_##offset, NULL); - -#define sysfs_in_reg_offset(reg, offset) \ -static ssize_t show_regs_in_##reg##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in_##reg (dev, buf, offset); \ -} \ -static ssize_t \ -store_regs_in_##reg##offset (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return store_in_##reg (dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(in##offset##_##reg, S_IRUGO| S_IWUSR, \ - show_regs_in_##reg##offset, store_regs_in_##reg##offset); - -#define sysfs_in_offsets(offset) \ -sysfs_in_offset(offset) \ -sysfs_in_reg_offset(min, offset) \ -sysfs_in_reg_offset(max, offset) - -sysfs_in_offsets(1); -sysfs_in_offsets(2); -sysfs_in_offsets(3); -sysfs_in_offsets(4); -sysfs_in_offsets(5); -sysfs_in_offsets(6); -sysfs_in_offsets(7); -sysfs_in_offsets(8); - -/* use a different set of functions for in0 */ -static ssize_t show_in_0(struct w83627hf_data *data, char *buf, u8 reg) -{ - long in0; - - if ((data->vrm_ovt & 0x01) && - (w83627thf == data->type || w83637hf == data->type)) - - /* use VRM9 calculation */ - in0 = (long)((reg * 488 + 70000 + 50) / 100); - else - /* use VRM8 (standard) calculation */ - in0 = (long)IN_FROM_REG(reg); - - return sprintf(buf,"%ld\n", in0); -} - -static ssize_t show_regs_in_0(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return show_in_0(data, buf, data->in[0]); -} - -static ssize_t show_regs_in_min0(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return show_in_0(data, buf, data->in_min[0]); -} - -static ssize_t show_regs_in_max0(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return show_in_0(data, buf, data->in_max[0]); -} - -static ssize_t store_regs_in_min0(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - - if ((data->vrm_ovt & 0x01) && - (w83627thf == data->type || w83637hf == data->type)) - - /* use VRM9 calculation */ - data->in_min[0] = (u8)(((val * 100) - 70000 + 244) / 488); - else - /* use VRM8 (standard) calculation */ - data->in_min[0] = IN_TO_REG(val); - - w83627hf_write_value(client, W83781D_REG_IN_MIN(0), data->in_min[0]); - up(&data->update_lock); - return count; -} - -static ssize_t store_regs_in_max0(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - - if ((data->vrm_ovt & 0x01) && - (w83627thf == data->type || w83637hf == data->type)) - - /* use VRM9 calculation */ - data->in_max[0] = (u8)(((val * 100) - 70000 + 244) / 488); - else - /* use VRM8 (standard) calculation */ - data->in_max[0] = IN_TO_REG(val); - - w83627hf_write_value(client, W83781D_REG_IN_MAX(0), data->in_max[0]); - up(&data->update_lock); - return count; -} - -static DEVICE_ATTR(in0_input, S_IRUGO, show_regs_in_0, NULL); -static DEVICE_ATTR(in0_min, S_IRUGO | S_IWUSR, - show_regs_in_min0, store_regs_in_min0); -static DEVICE_ATTR(in0_max, S_IRUGO | S_IWUSR, - show_regs_in_max0, store_regs_in_max0); - -#define device_create_file_in(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_in##offset##_input); \ -device_create_file(&client->dev, &dev_attr_in##offset##_min); \ -device_create_file(&client->dev, &dev_attr_in##offset##_max); \ -} while (0) - -#define show_fan_reg(reg) \ -static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ -{ \ - struct w83627hf_data *data = w83627hf_update_device(dev); \ - return sprintf(buf,"%ld\n", \ - FAN_FROM_REG(data->reg[nr-1], \ - (long)DIV_FROM_REG(data->fan_div[nr-1]))); \ -} -show_fan_reg(fan); -show_fan_reg(fan_min); - -static ssize_t -store_fan_min(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->fan_min[nr - 1] = - FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr - 1])); - w83627hf_write_value(client, W83781D_REG_FAN_MIN(nr), - data->fan_min[nr - 1]); - - up(&data->update_lock); - return count; -} - -#define sysfs_fan_offset(offset) \ -static ssize_t show_regs_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan(dev, buf, offset); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_regs_fan_##offset, NULL); - -#define sysfs_fan_min_offset(offset) \ -static ssize_t show_regs_fan_min##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_min(dev, buf, offset); \ -} \ -static ssize_t \ -store_regs_fan_min##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - return store_fan_min(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \ - show_regs_fan_min##offset, store_regs_fan_min##offset); - -sysfs_fan_offset(1); -sysfs_fan_min_offset(1); -sysfs_fan_offset(2); -sysfs_fan_min_offset(2); -sysfs_fan_offset(3); -sysfs_fan_min_offset(3); - -#define device_create_file_fan(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_fan##offset##_input); \ -device_create_file(&client->dev, &dev_attr_fan##offset##_min); \ -} while (0) - -#define show_temp_reg(reg) \ -static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ -{ \ - struct w83627hf_data *data = w83627hf_update_device(dev); \ - if (nr >= 2) { /* TEMP2 and TEMP3 */ \ - return sprintf(buf,"%ld\n", \ - (long)LM75_TEMP_FROM_REG(data->reg##_add[nr-2])); \ - } else { /* TEMP1 */ \ - return sprintf(buf,"%ld\n", (long)TEMP_FROM_REG(data->reg)); \ - } \ -} -show_temp_reg(temp); -show_temp_reg(temp_max); -show_temp_reg(temp_max_hyst); - -#define store_temp_reg(REG, reg) \ -static ssize_t \ -store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct w83627hf_data *data = i2c_get_clientdata(client); \ - u32 val; \ - \ - val = simple_strtoul(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - \ - if (nr >= 2) { /* TEMP2 and TEMP3 */ \ - data->temp_##reg##_add[nr-2] = LM75_TEMP_TO_REG(val); \ - w83627hf_write_value(client, W83781D_REG_TEMP_##REG(nr), \ - data->temp_##reg##_add[nr-2]); \ - } else { /* TEMP1 */ \ - data->temp_##reg = TEMP_TO_REG(val); \ - w83627hf_write_value(client, W83781D_REG_TEMP_##REG(nr), \ - data->temp_##reg); \ - } \ - \ - up(&data->update_lock); \ - return count; \ -} -store_temp_reg(OVER, max); -store_temp_reg(HYST, max_hyst); - -#define sysfs_temp_offset(offset) \ -static ssize_t \ -show_regs_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp(dev, buf, offset); \ -} \ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_regs_temp_##offset, NULL); - -#define sysfs_temp_reg_offset(reg, offset) \ -static ssize_t show_regs_temp_##reg##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp_##reg (dev, buf, offset); \ -} \ -static ssize_t \ -store_regs_temp_##reg##offset (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return store_temp_##reg (dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(temp##offset##_##reg, S_IRUGO| S_IWUSR, \ - show_regs_temp_##reg##offset, store_regs_temp_##reg##offset); - -#define sysfs_temp_offsets(offset) \ -sysfs_temp_offset(offset) \ -sysfs_temp_reg_offset(max, offset) \ -sysfs_temp_reg_offset(max_hyst, offset) - -sysfs_temp_offsets(1); -sysfs_temp_offsets(2); -sysfs_temp_offsets(3); - -#define device_create_file_temp(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_temp##offset##_input); \ -device_create_file(&client->dev, &dev_attr_temp##offset##_max); \ -device_create_file(&client->dev, &dev_attr_temp##offset##_max_hyst); \ -} while (0) - -static ssize_t -show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); -} -static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); -#define device_create_file_vid(client) \ -device_create_file(&client->dev, &dev_attr_cpu0_vid) - -static ssize_t -show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->vrm); -} -static ssize_t -store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - data->vrm = val; - - return count; -} -static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); -#define device_create_file_vrm(client) \ -device_create_file(&client->dev, &dev_attr_vrm) - -static ssize_t -show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->alarms); -} -static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); -#define device_create_file_alarms(client) \ -device_create_file(&client->dev, &dev_attr_alarms) - -#define show_beep_reg(REG, reg) \ -static ssize_t show_beep_##reg (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - struct w83627hf_data *data = w83627hf_update_device(dev); \ - return sprintf(buf,"%ld\n", \ - (long)BEEP_##REG##_FROM_REG(data->beep_##reg)); \ -} -show_beep_reg(ENABLE, enable) -show_beep_reg(MASK, mask) - -#define BEEP_ENABLE 0 /* Store beep_enable */ -#define BEEP_MASK 1 /* Store beep_mask */ - -static ssize_t -store_beep_reg(struct device *dev, const char *buf, size_t count, - int update_mask) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); - u32 val, val2; - - val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - - if (update_mask == BEEP_MASK) { /* We are storing beep_mask */ - data->beep_mask = BEEP_MASK_TO_REG(val); - w83627hf_write_value(client, W83781D_REG_BEEP_INTS1, - data->beep_mask & 0xff); - w83627hf_write_value(client, W83781D_REG_BEEP_INTS3, - ((data->beep_mask) >> 16) & 0xff); - val2 = (data->beep_mask >> 8) & 0x7f; - } else { /* We are storing beep_enable */ - val2 = - w83627hf_read_value(client, W83781D_REG_BEEP_INTS2) & 0x7f; - data->beep_enable = BEEP_ENABLE_TO_REG(val); - } - - w83627hf_write_value(client, W83781D_REG_BEEP_INTS2, - val2 | data->beep_enable << 7); - - up(&data->update_lock); - return count; -} - -#define sysfs_beep(REG, reg) \ -static ssize_t show_regs_beep_##reg (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_beep_##reg(dev, attr, buf); \ -} \ -static ssize_t \ -store_regs_beep_##reg (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - return store_beep_reg(dev, buf, count, BEEP_##REG); \ -} \ -static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, \ - show_regs_beep_##reg, store_regs_beep_##reg); - -sysfs_beep(ENABLE, enable); -sysfs_beep(MASK, mask); - -#define device_create_file_beep(client) \ -do { \ -device_create_file(&client->dev, &dev_attr_beep_enable); \ -device_create_file(&client->dev, &dev_attr_beep_mask); \ -} while (0) - -static ssize_t -show_fan_div_reg(struct device *dev, char *buf, int nr) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", - (long) DIV_FROM_REG(data->fan_div[nr - 1])); -} - -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least suprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ -static ssize_t -store_fan_div_reg(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); - unsigned long min; - u8 reg; - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - - /* Save fan_min */ - min = FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr])); - - data->fan_div[nr] = DIV_TO_REG(val); - - reg = (w83627hf_read_value(client, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV) - & (nr==0 ? 0xcf : 0x3f)) - | ((data->fan_div[nr] & 0x03) << (nr==0 ? 4 : 6)); - w83627hf_write_value(client, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg); - - reg = (w83627hf_read_value(client, W83781D_REG_VBAT) - & ~(1 << (5 + nr))) - | ((data->fan_div[nr] & 0x04) << (3 + nr)); - w83627hf_write_value(client, W83781D_REG_VBAT, reg); - - /* Restore fan_min */ - data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); - w83627hf_write_value(client, W83781D_REG_FAN_MIN(nr+1), data->fan_min[nr]); - - up(&data->update_lock); - return count; -} - -#define sysfs_fan_div(offset) \ -static ssize_t show_regs_fan_div_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_div_reg(dev, buf, offset); \ -} \ -static ssize_t \ -store_regs_fan_div_##offset (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return store_fan_div_reg(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, \ - show_regs_fan_div_##offset, store_regs_fan_div_##offset); - -sysfs_fan_div(1); -sysfs_fan_div(2); -sysfs_fan_div(3); - -#define device_create_file_fan_div(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_fan##offset##_div); \ -} while (0) - -static ssize_t -show_pwm_reg(struct device *dev, char *buf, int nr) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->pwm[nr - 1]); -} - -static ssize_t -store_pwm_reg(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - - if (data->type == w83627thf) { - /* bits 0-3 are reserved in 627THF */ - data->pwm[nr - 1] = PWM_TO_REG(val) & 0xf0; - w83627hf_write_value(client, - W836X7HF_REG_PWM(data->type, nr), - data->pwm[nr - 1] | - (w83627hf_read_value(client, - W836X7HF_REG_PWM(data->type, nr)) & 0x0f)); - } else { - data->pwm[nr - 1] = PWM_TO_REG(val); - w83627hf_write_value(client, - W836X7HF_REG_PWM(data->type, nr), - data->pwm[nr - 1]); - } - - up(&data->update_lock); - return count; -} - -#define sysfs_pwm(offset) \ -static ssize_t show_regs_pwm_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_pwm_reg(dev, buf, offset); \ -} \ -static ssize_t \ -store_regs_pwm_##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - return store_pwm_reg(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ - show_regs_pwm_##offset, store_regs_pwm_##offset); - -sysfs_pwm(1); -sysfs_pwm(2); -sysfs_pwm(3); - -#define device_create_file_pwm(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_pwm##offset); \ -} while (0) - -static ssize_t -show_sensor_reg(struct device *dev, char *buf, int nr) -{ - struct w83627hf_data *data = w83627hf_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->sens[nr - 1]); -} - -static ssize_t -store_sensor_reg(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); - u32 val, tmp; - - val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - - switch (val) { - case 1: /* PII/Celeron diode */ - tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); - w83627hf_write_value(client, W83781D_REG_SCFG1, - tmp | BIT_SCFG1[nr - 1]); - tmp = w83627hf_read_value(client, W83781D_REG_SCFG2); - w83627hf_write_value(client, W83781D_REG_SCFG2, - tmp | BIT_SCFG2[nr - 1]); - data->sens[nr - 1] = val; - break; - case 2: /* 3904 */ - tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); - w83627hf_write_value(client, W83781D_REG_SCFG1, - tmp | BIT_SCFG1[nr - 1]); - tmp = w83627hf_read_value(client, W83781D_REG_SCFG2); - w83627hf_write_value(client, W83781D_REG_SCFG2, - tmp & ~BIT_SCFG2[nr - 1]); - data->sens[nr - 1] = val; - break; - case W83781D_DEFAULT_BETA: /* thermistor */ - tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); - w83627hf_write_value(client, W83781D_REG_SCFG1, - tmp & ~BIT_SCFG1[nr - 1]); - data->sens[nr - 1] = val; - break; - default: - dev_err(&client->dev, - "Invalid sensor type %ld; must be 1, 2, or %d\n", - (long) val, W83781D_DEFAULT_BETA); - break; - } - - up(&data->update_lock); - return count; -} - -#define sysfs_sensor(offset) \ -static ssize_t show_regs_sensor_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_sensor_reg(dev, buf, offset); \ -} \ -static ssize_t \ -store_regs_sensor_##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - return store_sensor_reg(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(temp##offset##_type, S_IRUGO | S_IWUSR, \ - show_regs_sensor_##offset, store_regs_sensor_##offset); - -sysfs_sensor(1); -sysfs_sensor(2); -sysfs_sensor(3); - -#define device_create_file_sensor(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_temp##offset##_type); \ -} while (0) - - -/* This function is called when: - * w83627hf_driver is inserted (when this module is loaded), for each - available adapter - * when a new adapter is inserted (and w83627hf_driver is still present) */ -static int w83627hf_attach_adapter(struct i2c_adapter *adapter) -{ - return i2c_detect(adapter, &addr_data, w83627hf_detect); -} - -static int w83627hf_find(int sioaddr, int *address) -{ - u16 val; - - REG = sioaddr; - VAL = sioaddr + 1; - - superio_enter(); - val= superio_inb(DEVID); - if(val != W627_DEVID && - val != W627THF_DEVID && - val != W697_DEVID && - val != W637_DEVID) { - superio_exit(); - return -ENODEV; - } - - superio_select(W83627HF_LD_HWM); - val = (superio_inb(WINB_BASE_REG) << 8) | - superio_inb(WINB_BASE_REG + 1); - *address = val & ~(WINB_EXTENT - 1); - if (*address == 0 && force_addr == 0) { - superio_exit(); - return -ENODEV; - } - if (force_addr) - *address = force_addr; /* so detect will get called */ - - superio_exit(); - return 0; -} - -int w83627hf_detect(struct i2c_adapter *adapter, int address, - int kind) -{ - int val; - struct i2c_client *new_client; - struct w83627hf_data *data; - int err = 0; - const char *client_name = ""; - - if (!i2c_is_isa_adapter(adapter)) { - err = -ENODEV; - goto ERROR0; - } - - if(force_addr) - address = force_addr & ~(WINB_EXTENT - 1); - - if (!request_region(address, WINB_EXTENT, w83627hf_driver.name)) { - err = -EBUSY; - goto ERROR0; - } - - if(force_addr) { - printk("w83627hf.o: forcing ISA address 0x%04X\n", address); - superio_enter(); - superio_select(W83627HF_LD_HWM); - superio_outb(WINB_BASE_REG, address >> 8); - superio_outb(WINB_BASE_REG+1, address & 0xff); - superio_exit(); - } - - superio_enter(); - val= superio_inb(DEVID); - if(val == W627_DEVID) - kind = w83627hf; - else if(val == W697_DEVID) - kind = w83697hf; - else if(val == W627THF_DEVID) - kind = w83627thf; - else if(val == W637_DEVID) - kind = w83637hf; - else { - dev_info(&adapter->dev, - "Unsupported chip (dev_id=0x%02X).\n", val); - goto ERROR1; - } - - superio_select(W83627HF_LD_HWM); - if((val = 0x01 & superio_inb(WINB_ACT_REG)) == 0) - superio_outb(WINB_ACT_REG, 1); - superio_exit(); - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access w83627hf_{read,write}_value. */ - - if (!(data = kmalloc(sizeof(struct w83627hf_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR1; - } - memset(data, 0, sizeof(struct w83627hf_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - init_MUTEX(&data->lock); - new_client->adapter = adapter; - new_client->driver = &w83627hf_driver; - new_client->flags = 0; - - - if (kind == w83627hf) { - client_name = "w83627hf"; - } else if (kind == w83627thf) { - client_name = "w83627thf"; - } else if (kind == w83697hf) { - client_name = "w83697hf"; - } else if (kind == w83637hf) { - client_name = "w83637hf"; - } - - /* Fill in the remaining client fields and put into the global list */ - strlcpy(new_client->name, client_name, I2C_NAME_SIZE); - data->type = kind; - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto ERROR2; - - data->lm75 = NULL; - - /* Initialize the chip */ - w83627hf_init_client(new_client); - - /* A few vars need to be filled upon startup */ - data->fan_min[0] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(1)); - data->fan_min[1] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(2)); - data->fan_min[2] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(3)); - - /* Register sysfs hooks */ - device_create_file_in(new_client, 0); - if (kind != w83697hf) - device_create_file_in(new_client, 1); - device_create_file_in(new_client, 2); - device_create_file_in(new_client, 3); - device_create_file_in(new_client, 4); - if (kind != w83627thf && kind != w83637hf) { - device_create_file_in(new_client, 5); - device_create_file_in(new_client, 6); - } - device_create_file_in(new_client, 7); - device_create_file_in(new_client, 8); - - device_create_file_fan(new_client, 1); - device_create_file_fan(new_client, 2); - if (kind != w83697hf) - device_create_file_fan(new_client, 3); - - device_create_file_temp(new_client, 1); - device_create_file_temp(new_client, 2); - if (kind != w83697hf) - device_create_file_temp(new_client, 3); - - if (kind != w83697hf) - device_create_file_vid(new_client); - - if (kind != w83697hf) - device_create_file_vrm(new_client); - - device_create_file_fan_div(new_client, 1); - device_create_file_fan_div(new_client, 2); - if (kind != w83697hf) - device_create_file_fan_div(new_client, 3); - - device_create_file_alarms(new_client); - - device_create_file_beep(new_client); - - device_create_file_pwm(new_client, 1); - device_create_file_pwm(new_client, 2); - if (kind == w83627thf || kind == w83637hf) - device_create_file_pwm(new_client, 3); - - device_create_file_sensor(new_client, 1); - device_create_file_sensor(new_client, 2); - if (kind != w83697hf) - device_create_file_sensor(new_client, 3); - - return 0; - - ERROR2: - kfree(data); - ERROR1: - release_region(address, WINB_EXTENT); - ERROR0: - return err; -} - -static int w83627hf_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, - "Client deregistration failed, client not detached.\n"); - return err; - } - - release_region(client->addr, WINB_EXTENT); - kfree(i2c_get_clientdata(client)); - - return 0; -} - - -/* - ISA access must always be locked explicitly! - We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, - would slow down the W83781D access and should not be necessary. - There are some ugly typecasts here, but the good news is - they should - nowhere else be necessary! */ -static int w83627hf_read_value(struct i2c_client *client, u16 reg) -{ - struct w83627hf_data *data = i2c_get_clientdata(client); - int res, word_sized; - - down(&data->lock); - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x50) - || ((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); - if (word_sized) { - outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); - res = - (res << 8) + inb_p(client->addr + - W83781D_DATA_REG_OFFSET); - } - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); - } - up(&data->lock); - return res; -} - -static int w83627thf_read_gpio5(struct i2c_client *client) -{ - int res = 0xff, sel; - - superio_enter(); - superio_select(W83627HF_LD_GPIO5); - - /* Make sure these GPIO pins are enabled */ - if (!(superio_inb(W83627THF_GPIO5_EN) & (1<<3))) { - dev_dbg(&client->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); - if ((sel & 0x1f) != 0x1f) { - dev_dbg(&client->dev, "GPIO5 not configured for VID " - "function\n"); - goto exit; - } - - dev_info(&client->dev, "Reading VID from GPIO5\n"); - res = superio_inb(W83627THF_GPIO5_DR) & sel; - -exit: - superio_exit(); - return res; -} - -static int w83627hf_write_value(struct i2c_client *client, u16 reg, u16 value) -{ - struct w83627hf_data *data = i2c_get_clientdata(client); - int word_sized; - - down(&data->lock); - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - if (word_sized) { - outb_p(value >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); - } - outb_p(value & 0xff, - client->addr + W83781D_DATA_REG_OFFSET); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); - } - up(&data->lock); - return 0; -} - -/* Called when we have found a new W83781D. It should set limits, etc. */ -static void w83627hf_init_client(struct i2c_client *client) -{ - struct w83627hf_data *data = i2c_get_clientdata(client); - int i; - int type = data->type; - u8 tmp; - - if(init) { - /* save this register */ - i = w83627hf_read_value(client, W83781D_REG_BEEP_CONFIG); - /* Reset all except Watchdog values and last conversion values - This sets fan-divs to 2, among others */ - w83627hf_write_value(client, W83781D_REG_CONFIG, 0x80); - /* Restore the register and disable power-on abnormal beep. - This saves FAN 1/2/3 input/output values set by BIOS. */ - w83627hf_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80); - /* Disable master beep-enable (reset turns it on). - Individual beeps should be reset to off but for some reason - disabling this bit helps some people not get beeped */ - w83627hf_write_value(client, W83781D_REG_BEEP_INTS2, 0); - } - - /* Minimize conflicts with other winbond i2c-only clients... */ - /* disable i2c subclients... how to disable main i2c client?? */ - /* force i2c address to relatively uncommon address */ - w83627hf_write_value(client, W83781D_REG_I2C_SUBADDR, 0x89); - w83627hf_write_value(client, W83781D_REG_I2C_ADDR, force_i2c); - - /* Read VID only once */ - if (w83627hf == data->type || w83637hf == data->type) { - int lo = w83627hf_read_value(client, W83781D_REG_VID_FANDIV); - int hi = w83627hf_read_value(client, W83781D_REG_CHIPID); - data->vid = (lo & 0x0f) | ((hi & 0x01) << 4); - } else if (w83627thf == data->type) { - data->vid = w83627thf_read_gpio5(client) & 0x3f; - } - - /* Read VRM & OVT Config only once */ - if (w83627thf == data->type || w83637hf == data->type) { - data->vrm_ovt = - w83627hf_read_value(client, W83627THF_REG_VRM_OVT_CFG); - data->vrm = (data->vrm_ovt & 0x01) ? 90 : 82; - } else { - /* Convert VID to voltage based on default VRM */ - data->vrm = i2c_which_vrm(); - } - - tmp = w83627hf_read_value(client, W83781D_REG_SCFG1); - for (i = 1; i <= 3; i++) { - if (!(tmp & BIT_SCFG1[i - 1])) { - data->sens[i - 1] = W83781D_DEFAULT_BETA; - } else { - if (w83627hf_read_value - (client, - W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) - data->sens[i - 1] = 1; - else - data->sens[i - 1] = 2; - } - if ((type == w83697hf) && (i == 2)) - break; - } - - if(init) { - /* Enable temp2 */ - tmp = w83627hf_read_value(client, W83781D_REG_TEMP2_CONFIG); - if (tmp & 0x01) { - dev_warn(&client->dev, "Enabling temp2, readings " - "might not make sense\n"); - w83627hf_write_value(client, W83781D_REG_TEMP2_CONFIG, - tmp & 0xfe); - } - - /* Enable temp3 */ - if (type != w83697hf) { - tmp = w83627hf_read_value(client, - W83781D_REG_TEMP3_CONFIG); - if (tmp & 0x01) { - dev_warn(&client->dev, "Enabling temp3, " - "readings might not make sense\n"); - w83627hf_write_value(client, - W83781D_REG_TEMP3_CONFIG, tmp & 0xfe); - } - } - - if (type == w83627hf) { - /* enable PWM2 control (can't hurt since PWM reg - should have been reset to 0xff) */ - w83627hf_write_value(client, W83627HF_REG_PWMCLK12, - 0x19); - } - /* enable comparator mode for temp2 and temp3 so - alarm indication will work correctly */ - i = w83627hf_read_value(client, W83781D_REG_IRQ); - if (!(i & 0x40)) - w83627hf_write_value(client, W83781D_REG_IRQ, - i | 0x40); - } - - /* Start monitoring */ - w83627hf_write_value(client, W83781D_REG_CONFIG, - (w83627hf_read_value(client, - W83781D_REG_CONFIG) & 0xf7) - | 0x01); -} - -static struct w83627hf_data *w83627hf_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83627hf_data *data = i2c_get_clientdata(client); - int i; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - for (i = 0; i <= 8; i++) { - /* skip missing sensors */ - if (((data->type == w83697hf) && (i == 1)) || - ((data->type == w83627thf || data->type == w83637hf) - && (i == 4 || i == 5))) - continue; - data->in[i] = - w83627hf_read_value(client, W83781D_REG_IN(i)); - data->in_min[i] = - w83627hf_read_value(client, - W83781D_REG_IN_MIN(i)); - data->in_max[i] = - w83627hf_read_value(client, - W83781D_REG_IN_MAX(i)); - } - for (i = 1; i <= 3; i++) { - data->fan[i - 1] = - w83627hf_read_value(client, W83781D_REG_FAN(i)); - data->fan_min[i - 1] = - w83627hf_read_value(client, - W83781D_REG_FAN_MIN(i)); - } - for (i = 1; i <= 3; i++) { - u8 tmp = w83627hf_read_value(client, - W836X7HF_REG_PWM(data->type, i)); - /* bits 0-3 are reserved in 627THF */ - if (data->type == w83627thf) - tmp &= 0xf0; - data->pwm[i - 1] = tmp; - if(i == 2 && - (data->type == w83627hf || data->type == w83697hf)) - break; - } - - data->temp = w83627hf_read_value(client, W83781D_REG_TEMP(1)); - data->temp_max = - w83627hf_read_value(client, W83781D_REG_TEMP_OVER(1)); - data->temp_max_hyst = - w83627hf_read_value(client, W83781D_REG_TEMP_HYST(1)); - data->temp_add[0] = - w83627hf_read_value(client, W83781D_REG_TEMP(2)); - data->temp_max_add[0] = - w83627hf_read_value(client, W83781D_REG_TEMP_OVER(2)); - data->temp_max_hyst_add[0] = - w83627hf_read_value(client, W83781D_REG_TEMP_HYST(2)); - if (data->type != w83697hf) { - data->temp_add[1] = - w83627hf_read_value(client, W83781D_REG_TEMP(3)); - data->temp_max_add[1] = - w83627hf_read_value(client, W83781D_REG_TEMP_OVER(3)); - data->temp_max_hyst_add[1] = - w83627hf_read_value(client, W83781D_REG_TEMP_HYST(3)); - } - - i = w83627hf_read_value(client, W83781D_REG_VID_FANDIV); - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = (i >> 6) & 0x03; - if (data->type != w83697hf) { - data->fan_div[2] = (w83627hf_read_value(client, - W83781D_REG_PIN) >> 6) & 0x03; - } - i = w83627hf_read_value(client, W83781D_REG_VBAT); - data->fan_div[0] |= (i >> 3) & 0x04; - data->fan_div[1] |= (i >> 4) & 0x04; - if (data->type != w83697hf) - data->fan_div[2] |= (i >> 5) & 0x04; - data->alarms = - w83627hf_read_value(client, W83781D_REG_ALARM1) | - (w83627hf_read_value(client, W83781D_REG_ALARM2) << 8) | - (w83627hf_read_value(client, W83781D_REG_ALARM3) << 16); - i = w83627hf_read_value(client, W83781D_REG_BEEP_INTS2); - data->beep_enable = i >> 7; - data->beep_mask = ((i & 0x7f) << 8) | - w83627hf_read_value(client, W83781D_REG_BEEP_INTS1) | - w83627hf_read_value(client, W83781D_REG_BEEP_INTS3) << 16; - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_w83627hf_init(void) -{ - int addr; - - if (w83627hf_find(0x2e, &addr) - && w83627hf_find(0x4e, &addr)) { - return -ENODEV; - } - normal_isa[0] = addr; - - return i2c_add_driver(&w83627hf_driver); -} - -static void __exit sensors_w83627hf_exit(void) -{ - i2c_del_driver(&w83627hf_driver); -} - -MODULE_AUTHOR("Frodo Looijaard , " - "Philip Edelbrock , " - "and Mark Studebaker "); -MODULE_DESCRIPTION("W83627HF driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_w83627hf_init); -module_exit(sensors_w83627hf_exit); diff --git a/drivers/i2c/chips/w83781d.c b/drivers/i2c/chips/w83781d.c deleted file mode 100644 index 0bb131ce09eb..000000000000 --- a/drivers/i2c/chips/w83781d.c +++ /dev/null @@ -1,1632 +0,0 @@ -/* - w83781d.c - Part of lm_sensors, Linux kernel modules for hardware - monitoring - Copyright (c) 1998 - 2001 Frodo Looijaard , - Philip Edelbrock , - and Mark Studebaker - - 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., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - -/* - Supports following chips: - - Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA - as99127f 7 3 0 3 0x31 0x12c3 yes no - as99127f rev.2 (type_name = as99127f) 0x31 0x5ca3 yes no - w83781d 7 3 0 3 0x10-1 0x5ca3 yes yes - w83627hf 9 3 2 3 0x21 0x5ca3 yes yes(LPC) - w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes - w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include "lm75.h" - -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, - 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { 0x0290, I2C_CLIENT_ISA_END }; - -/* Insmod parameters */ -SENSORS_INSMOD_5(w83781d, w83782d, w83783s, w83627hf, as99127f); -I2C_CLIENT_MODULE_PARM(force_subclients, "List of subclient addresses: " - "{bus, clientaddr, subclientaddr1, subclientaddr2}"); - -static int init = 1; -module_param(init, bool, 0); -MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); - -/* Constants specified below */ - -/* Length of ISA address segment */ -#define W83781D_EXTENT 8 - -/* Where are the ISA address/data registers relative to the base address */ -#define W83781D_ADDR_REG_OFFSET 5 -#define W83781D_DATA_REG_OFFSET 6 - -/* The W83781D registers */ -/* The W83782D registers for nr=7,8 are in bank 5 */ -#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ - (0x554 + (((nr) - 7) * 2))) -#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ - (0x555 + (((nr) - 7) * 2))) -#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ - (0x550 + (nr) - 7)) - -#define W83781D_REG_FAN_MIN(nr) (0x3a + (nr)) -#define W83781D_REG_FAN(nr) (0x27 + (nr)) - -#define W83781D_REG_BANK 0x4E -#define W83781D_REG_TEMP2_CONFIG 0x152 -#define W83781D_REG_TEMP3_CONFIG 0x252 -#define W83781D_REG_TEMP(nr) ((nr == 3) ? (0x0250) : \ - ((nr == 2) ? (0x0150) : \ - (0x27))) -#define W83781D_REG_TEMP_HYST(nr) ((nr == 3) ? (0x253) : \ - ((nr == 2) ? (0x153) : \ - (0x3A))) -#define W83781D_REG_TEMP_OVER(nr) ((nr == 3) ? (0x255) : \ - ((nr == 2) ? (0x155) : \ - (0x39))) - -#define W83781D_REG_CONFIG 0x40 -#define W83781D_REG_ALARM1 0x41 -#define W83781D_REG_ALARM2 0x42 -#define W83781D_REG_ALARM3 0x450 /* not on W83781D */ - -#define W83781D_REG_IRQ 0x4C -#define W83781D_REG_BEEP_CONFIG 0x4D -#define W83781D_REG_BEEP_INTS1 0x56 -#define W83781D_REG_BEEP_INTS2 0x57 -#define W83781D_REG_BEEP_INTS3 0x453 /* not on W83781D */ - -#define W83781D_REG_VID_FANDIV 0x47 - -#define W83781D_REG_CHIPID 0x49 -#define W83781D_REG_WCHIPID 0x58 -#define W83781D_REG_CHIPMAN 0x4F -#define W83781D_REG_PIN 0x4B - -/* 782D/783S only */ -#define W83781D_REG_VBAT 0x5D - -/* PWM 782D (1-4) and 783S (1-2) only */ -#define W83781D_REG_PWM1 0x5B /* 782d and 783s/627hf datasheets disagree */ - /* on which is which; */ -#define W83781D_REG_PWM2 0x5A /* We follow the 782d convention here, */ - /* However 782d is probably wrong. */ -#define W83781D_REG_PWM3 0x5E -#define W83781D_REG_PWM4 0x5F -#define W83781D_REG_PWMCLK12 0x5C -#define W83781D_REG_PWMCLK34 0x45C -static const u8 regpwm[] = { W83781D_REG_PWM1, W83781D_REG_PWM2, - W83781D_REG_PWM3, W83781D_REG_PWM4 -}; - -#define W83781D_REG_PWM(nr) (regpwm[(nr) - 1]) - -#define W83781D_REG_I2C_ADDR 0x48 -#define W83781D_REG_I2C_SUBADDR 0x4A - -/* The following are undocumented in the data sheets however we - received the information in an email from Winbond tech support */ -/* Sensor selection - not on 781d */ -#define W83781D_REG_SCFG1 0x5D -static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; - -#define W83781D_REG_SCFG2 0x59 -static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; - -#define W83781D_DEFAULT_BETA 3435 - -/* RT Table registers */ -#define W83781D_REG_RT_IDX 0x50 -#define W83781D_REG_RT_VAL 0x51 - -/* Conversions. Rounding and limit checking is only done on the TO_REG - variants. Note that you should be a bit careful with which arguments - these macros are called: arguments may be evaluated more than once. - Fixing this is just not worth it. */ -#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) -#define IN_FROM_REG(val) (((val) * 16) / 10) - -static inline u8 -FAN_TO_REG(long rpm, int div) -{ - if (rpm == 0) - return 255; - rpm = SENSORS_LIMIT(rpm, 1, 1000000); - return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 254); -} - -#define FAN_FROM_REG(val,div) ((val) == 0 ? -1 : \ - ((val) == 255 ? 0 : \ - 1350000 / ((val) * (div)))) - -#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val) < 0 ? (val)+0x100*1000 \ - : (val)) / 1000, 0, 0xff)) -#define TEMP_FROM_REG(val) (((val) & 0x80 ? (val)-0x100 : (val)) * 1000) - -#define PWM_FROM_REG(val) (val) -#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255)) -#define BEEP_MASK_FROM_REG(val,type) ((type) == as99127f ? \ - (val) ^ 0x7fff : (val)) -#define BEEP_MASK_TO_REG(val,type) ((type) == as99127f ? \ - (~(val)) & 0x7fff : (val) & 0xffffff) - -#define BEEP_ENABLE_TO_REG(val) ((val) ? 1 : 0) -#define BEEP_ENABLE_FROM_REG(val) ((val) ? 1 : 0) - -#define DIV_FROM_REG(val) (1 << (val)) - -static inline u8 -DIV_TO_REG(long val, enum chips type) -{ - int i; - val = SENSORS_LIMIT(val, 1, - ((type == w83781d - || type == as99127f) ? 8 : 128)) >> 1; - for (i = 0; i < 7; i++) { - if (val == 0) - break; - val >>= 1; - } - return ((u8) i); -} - -/* There are some complications in a module like this. First off, W83781D chips - may be both present on the SMBus and the ISA bus, and we have to handle - those cases separately at some places. Second, there might be several - W83781D chips available (well, actually, that is probably never done; but - it is a clean illustration of how to handle a case like that). Finally, - a specific chip may be attached to *both* ISA and SMBus, and we would - not like to detect it double. Fortunately, in the case of the W83781D at - least, a register tells us what SMBus address we are on, so that helps - a bit - except if there could be more than one SMBus. Groan. No solution - for this yet. */ - -/* This module may seem overly long and complicated. In fact, it is not so - bad. Quite a lot of bookkeeping is done. A real driver can often cut - some corners. */ - -/* For each registered W83781D, we need to keep some data in memory. That - data is pointed to by w83781d_list[NR]->data. The structure itself is - dynamically allocated, at the same time when a new w83781d client is - allocated. */ -struct w83781d_data { - struct i2c_client client; - struct semaphore lock; - enum chips type; - - struct semaphore update_lock; - char valid; /* !=0 if following fields are valid */ - unsigned long last_updated; /* In jiffies */ - - struct i2c_client *lm75[2]; /* for secondary I2C addresses */ - /* array of 2 pointers to subclients */ - - u8 in[9]; /* Register value - 8 & 9 for 782D only */ - u8 in_max[9]; /* Register value - 8 & 9 for 782D only */ - u8 in_min[9]; /* Register value - 8 & 9 for 782D only */ - u8 fan[3]; /* Register value */ - u8 fan_min[3]; /* Register value */ - u8 temp; - u8 temp_max; /* Register value */ - u8 temp_max_hyst; /* Register value */ - u16 temp_add[2]; /* Register value */ - u16 temp_max_add[2]; /* Register value */ - u16 temp_max_hyst_add[2]; /* Register value */ - u8 fan_div[3]; /* Register encoding, shifted right */ - u8 vid; /* Register encoding, combined */ - u32 alarms; /* Register encoding, combined */ - u32 beep_mask; /* Register encoding, combined */ - u8 beep_enable; /* Boolean */ - u8 pwm[4]; /* Register value */ - u8 pwmenable[4]; /* Boolean */ - u16 sens[3]; /* 782D/783S only. - 1 = pentium diode; 2 = 3904 diode; - 3000-5000 = thermistor beta. - Default = 3435. - Other Betas unimplemented */ - u8 vrm; -}; - -static int w83781d_attach_adapter(struct i2c_adapter *adapter); -static int w83781d_detect(struct i2c_adapter *adapter, int address, int kind); -static int w83781d_detach_client(struct i2c_client *client); - -static int w83781d_read_value(struct i2c_client *client, u16 register); -static int w83781d_write_value(struct i2c_client *client, u16 register, - u16 value); -static struct w83781d_data *w83781d_update_device(struct device *dev); -static void w83781d_init_client(struct i2c_client *client); - -static struct i2c_driver w83781d_driver = { - .owner = THIS_MODULE, - .name = "w83781d", - .id = I2C_DRIVERID_W83781D, - .flags = I2C_DF_NOTIFY, - .attach_adapter = w83781d_attach_adapter, - .detach_client = w83781d_detach_client, -}; - -/* following are the sysfs callback functions */ -#define show_in_reg(reg) \ -static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ -{ \ - struct w83781d_data *data = w83781d_update_device(dev); \ - return sprintf(buf,"%ld\n", (long)IN_FROM_REG(data->reg[nr] * 10)); \ -} -show_in_reg(in); -show_in_reg(in_min); -show_in_reg(in_max); - -#define store_in_reg(REG, reg) \ -static ssize_t store_in_##reg (struct device *dev, const char *buf, size_t count, int nr) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct w83781d_data *data = i2c_get_clientdata(client); \ - u32 val; \ - \ - val = simple_strtoul(buf, NULL, 10) / 10; \ - \ - down(&data->update_lock); \ - data->in_##reg[nr] = IN_TO_REG(val); \ - w83781d_write_value(client, W83781D_REG_IN_##REG(nr), data->in_##reg[nr]); \ - \ - up(&data->update_lock); \ - return count; \ -} -store_in_reg(MIN, min); -store_in_reg(MAX, max); - -#define sysfs_in_offset(offset) \ -static ssize_t \ -show_regs_in_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in(dev, buf, offset); \ -} \ -static DEVICE_ATTR(in##offset##_input, S_IRUGO, show_regs_in_##offset, NULL); - -#define sysfs_in_reg_offset(reg, offset) \ -static ssize_t show_regs_in_##reg##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_in_##reg (dev, buf, offset); \ -} \ -static ssize_t store_regs_in_##reg##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - return store_in_##reg (dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(in##offset##_##reg, S_IRUGO| S_IWUSR, show_regs_in_##reg##offset, store_regs_in_##reg##offset); - -#define sysfs_in_offsets(offset) \ -sysfs_in_offset(offset); \ -sysfs_in_reg_offset(min, offset); \ -sysfs_in_reg_offset(max, offset); - -sysfs_in_offsets(0); -sysfs_in_offsets(1); -sysfs_in_offsets(2); -sysfs_in_offsets(3); -sysfs_in_offsets(4); -sysfs_in_offsets(5); -sysfs_in_offsets(6); -sysfs_in_offsets(7); -sysfs_in_offsets(8); - -#define device_create_file_in(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_in##offset##_input); \ -device_create_file(&client->dev, &dev_attr_in##offset##_min); \ -device_create_file(&client->dev, &dev_attr_in##offset##_max); \ -} while (0) - -#define show_fan_reg(reg) \ -static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ -{ \ - struct w83781d_data *data = w83781d_update_device(dev); \ - return sprintf(buf,"%ld\n", \ - FAN_FROM_REG(data->reg[nr-1], (long)DIV_FROM_REG(data->fan_div[nr-1]))); \ -} -show_fan_reg(fan); -show_fan_reg(fan_min); - -static ssize_t -store_fan_min(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->fan_min[nr - 1] = - FAN_TO_REG(val, DIV_FROM_REG(data->fan_div[nr - 1])); - w83781d_write_value(client, W83781D_REG_FAN_MIN(nr), - data->fan_min[nr - 1]); - - up(&data->update_lock); - return count; -} - -#define sysfs_fan_offset(offset) \ -static ssize_t show_regs_fan_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan(dev, buf, offset); \ -} \ -static DEVICE_ATTR(fan##offset##_input, S_IRUGO, show_regs_fan_##offset, NULL); - -#define sysfs_fan_min_offset(offset) \ -static ssize_t show_regs_fan_min##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_min(dev, buf, offset); \ -} \ -static ssize_t store_regs_fan_min##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - return store_fan_min(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, show_regs_fan_min##offset, store_regs_fan_min##offset); - -sysfs_fan_offset(1); -sysfs_fan_min_offset(1); -sysfs_fan_offset(2); -sysfs_fan_min_offset(2); -sysfs_fan_offset(3); -sysfs_fan_min_offset(3); - -#define device_create_file_fan(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_fan##offset##_input); \ -device_create_file(&client->dev, &dev_attr_fan##offset##_min); \ -} while (0) - -#define show_temp_reg(reg) \ -static ssize_t show_##reg (struct device *dev, char *buf, int nr) \ -{ \ - struct w83781d_data *data = w83781d_update_device(dev); \ - if (nr >= 2) { /* TEMP2 and TEMP3 */ \ - return sprintf(buf,"%d\n", \ - LM75_TEMP_FROM_REG(data->reg##_add[nr-2])); \ - } else { /* TEMP1 */ \ - return sprintf(buf,"%ld\n", (long)TEMP_FROM_REG(data->reg)); \ - } \ -} -show_temp_reg(temp); -show_temp_reg(temp_max); -show_temp_reg(temp_max_hyst); - -#define store_temp_reg(REG, reg) \ -static ssize_t store_temp_##reg (struct device *dev, const char *buf, size_t count, int nr) \ -{ \ - struct i2c_client *client = to_i2c_client(dev); \ - struct w83781d_data *data = i2c_get_clientdata(client); \ - s32 val; \ - \ - val = simple_strtol(buf, NULL, 10); \ - \ - down(&data->update_lock); \ - \ - if (nr >= 2) { /* TEMP2 and TEMP3 */ \ - data->temp_##reg##_add[nr-2] = LM75_TEMP_TO_REG(val); \ - w83781d_write_value(client, W83781D_REG_TEMP_##REG(nr), \ - data->temp_##reg##_add[nr-2]); \ - } else { /* TEMP1 */ \ - data->temp_##reg = TEMP_TO_REG(val); \ - w83781d_write_value(client, W83781D_REG_TEMP_##REG(nr), \ - data->temp_##reg); \ - } \ - \ - up(&data->update_lock); \ - return count; \ -} -store_temp_reg(OVER, max); -store_temp_reg(HYST, max_hyst); - -#define sysfs_temp_offset(offset) \ -static ssize_t \ -show_regs_temp_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp(dev, buf, offset); \ -} \ -static DEVICE_ATTR(temp##offset##_input, S_IRUGO, show_regs_temp_##offset, NULL); - -#define sysfs_temp_reg_offset(reg, offset) \ -static ssize_t show_regs_temp_##reg##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_temp_##reg (dev, buf, offset); \ -} \ -static ssize_t store_regs_temp_##reg##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - return store_temp_##reg (dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(temp##offset##_##reg, S_IRUGO| S_IWUSR, show_regs_temp_##reg##offset, store_regs_temp_##reg##offset); - -#define sysfs_temp_offsets(offset) \ -sysfs_temp_offset(offset); \ -sysfs_temp_reg_offset(max, offset); \ -sysfs_temp_reg_offset(max_hyst, offset); - -sysfs_temp_offsets(1); -sysfs_temp_offsets(2); -sysfs_temp_offsets(3); - -#define device_create_file_temp(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_temp##offset##_input); \ -device_create_file(&client->dev, &dev_attr_temp##offset##_max); \ -device_create_file(&client->dev, &dev_attr_temp##offset##_max_hyst); \ -} while (0) - -static ssize_t -show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83781d_data *data = w83781d_update_device(dev); - return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm)); -} - -static -DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL); -#define device_create_file_vid(client) \ -device_create_file(&client->dev, &dev_attr_cpu0_vid); -static ssize_t -show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83781d_data *data = w83781d_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->vrm); -} - -static ssize_t -store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - data->vrm = val; - - return count; -} - -static -DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg); -#define device_create_file_vrm(client) \ -device_create_file(&client->dev, &dev_attr_vrm); -static ssize_t -show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83781d_data *data = w83781d_update_device(dev); - return sprintf(buf, "%u\n", data->alarms); -} - -static -DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL); -#define device_create_file_alarms(client) \ -device_create_file(&client->dev, &dev_attr_alarms); -static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83781d_data *data = w83781d_update_device(dev); - return sprintf(buf, "%ld\n", - (long)BEEP_MASK_FROM_REG(data->beep_mask, data->type)); -} -static ssize_t show_beep_enable (struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83781d_data *data = w83781d_update_device(dev); - return sprintf(buf, "%ld\n", - (long)BEEP_ENABLE_FROM_REG(data->beep_enable)); -} - -#define BEEP_ENABLE 0 /* Store beep_enable */ -#define BEEP_MASK 1 /* Store beep_mask */ - -static ssize_t -store_beep_reg(struct device *dev, const char *buf, size_t count, - int update_mask) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); - u32 val, val2; - - val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - - if (update_mask == BEEP_MASK) { /* We are storing beep_mask */ - data->beep_mask = BEEP_MASK_TO_REG(val, data->type); - w83781d_write_value(client, W83781D_REG_BEEP_INTS1, - data->beep_mask & 0xff); - - if ((data->type != w83781d) && (data->type != as99127f)) { - w83781d_write_value(client, W83781D_REG_BEEP_INTS3, - ((data->beep_mask) >> 16) & 0xff); - } - - val2 = (data->beep_mask >> 8) & 0x7f; - } else { /* We are storing beep_enable */ - val2 = w83781d_read_value(client, W83781D_REG_BEEP_INTS2) & 0x7f; - data->beep_enable = BEEP_ENABLE_TO_REG(val); - } - - w83781d_write_value(client, W83781D_REG_BEEP_INTS2, - val2 | data->beep_enable << 7); - - up(&data->update_lock); - return count; -} - -#define sysfs_beep(REG, reg) \ -static ssize_t show_regs_beep_##reg (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_beep_##reg(dev, attr, buf); \ -} \ -static ssize_t store_regs_beep_##reg (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - return store_beep_reg(dev, buf, count, BEEP_##REG); \ -} \ -static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, show_regs_beep_##reg, store_regs_beep_##reg); - -sysfs_beep(ENABLE, enable); -sysfs_beep(MASK, mask); - -#define device_create_file_beep(client) \ -do { \ -device_create_file(&client->dev, &dev_attr_beep_enable); \ -device_create_file(&client->dev, &dev_attr_beep_mask); \ -} while (0) - -static ssize_t -show_fan_div_reg(struct device *dev, char *buf, int nr) -{ - struct w83781d_data *data = w83781d_update_device(dev); - return sprintf(buf, "%ld\n", - (long) DIV_FROM_REG(data->fan_div[nr - 1])); -} - -/* Note: we save and restore the fan minimum here, because its value is - determined in part by the fan divisor. This follows the principle of - least suprise; the user doesn't expect the fan minimum to change just - because the divisor changed. */ -static ssize_t -store_fan_div_reg(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); - unsigned long min; - u8 reg; - unsigned long val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - - /* Save fan_min */ - min = FAN_FROM_REG(data->fan_min[nr], - DIV_FROM_REG(data->fan_div[nr])); - - data->fan_div[nr] = DIV_TO_REG(val, data->type); - - reg = (w83781d_read_value(client, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV) - & (nr==0 ? 0xcf : 0x3f)) - | ((data->fan_div[nr] & 0x03) << (nr==0 ? 4 : 6)); - w83781d_write_value(client, nr==2 ? W83781D_REG_PIN : W83781D_REG_VID_FANDIV, reg); - - /* w83781d and as99127f don't have extended divisor bits */ - if (data->type != w83781d && data->type != as99127f) { - reg = (w83781d_read_value(client, W83781D_REG_VBAT) - & ~(1 << (5 + nr))) - | ((data->fan_div[nr] & 0x04) << (3 + nr)); - w83781d_write_value(client, W83781D_REG_VBAT, reg); - } - - /* Restore fan_min */ - data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr])); - w83781d_write_value(client, W83781D_REG_FAN_MIN(nr+1), data->fan_min[nr]); - - up(&data->update_lock); - return count; -} - -#define sysfs_fan_div(offset) \ -static ssize_t show_regs_fan_div_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_fan_div_reg(dev, buf, offset); \ -} \ -static ssize_t store_regs_fan_div_##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - return store_fan_div_reg(dev, buf, count, offset - 1); \ -} \ -static DEVICE_ATTR(fan##offset##_div, S_IRUGO | S_IWUSR, show_regs_fan_div_##offset, store_regs_fan_div_##offset); - -sysfs_fan_div(1); -sysfs_fan_div(2); -sysfs_fan_div(3); - -#define device_create_file_fan_div(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_fan##offset##_div); \ -} while (0) - -static ssize_t -show_pwm_reg(struct device *dev, char *buf, int nr) -{ - struct w83781d_data *data = w83781d_update_device(dev); - return sprintf(buf, "%ld\n", (long) PWM_FROM_REG(data->pwm[nr - 1])); -} - -static ssize_t -show_pwmenable_reg(struct device *dev, char *buf, int nr) -{ - struct w83781d_data *data = w83781d_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->pwmenable[nr - 1]); -} - -static ssize_t -store_pwm_reg(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); - u32 val; - - val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - data->pwm[nr - 1] = PWM_TO_REG(val); - w83781d_write_value(client, W83781D_REG_PWM(nr), data->pwm[nr - 1]); - up(&data->update_lock); - return count; -} - -static ssize_t -store_pwmenable_reg(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); - u32 val, reg; - - val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - - switch (val) { - case 0: - case 1: - reg = w83781d_read_value(client, W83781D_REG_PWMCLK12); - w83781d_write_value(client, W83781D_REG_PWMCLK12, - (reg & 0xf7) | (val << 3)); - - reg = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG); - w83781d_write_value(client, W83781D_REG_BEEP_CONFIG, - (reg & 0xef) | (!val << 4)); - - data->pwmenable[nr - 1] = val; - break; - - default: - up(&data->update_lock); - return -EINVAL; - } - - up(&data->update_lock); - return count; -} - -#define sysfs_pwm(offset) \ -static ssize_t show_regs_pwm_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_pwm_reg(dev, buf, offset); \ -} \ -static ssize_t store_regs_pwm_##offset (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return store_pwm_reg(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(pwm##offset, S_IRUGO | S_IWUSR, \ - show_regs_pwm_##offset, store_regs_pwm_##offset); - -#define sysfs_pwmenable(offset) \ -static ssize_t show_regs_pwmenable_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_pwmenable_reg(dev, buf, offset); \ -} \ -static ssize_t store_regs_pwmenable_##offset (struct device *dev, struct device_attribute *attr, \ - const char *buf, size_t count) \ -{ \ - return store_pwmenable_reg(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(pwm##offset##_enable, S_IRUGO | S_IWUSR, \ - show_regs_pwmenable_##offset, store_regs_pwmenable_##offset); - -sysfs_pwm(1); -sysfs_pwm(2); -sysfs_pwmenable(2); /* only PWM2 can be enabled/disabled */ -sysfs_pwm(3); -sysfs_pwm(4); - -#define device_create_file_pwm(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_pwm##offset); \ -} while (0) - -#define device_create_file_pwmenable(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_pwm##offset##_enable); \ -} while (0) - -static ssize_t -show_sensor_reg(struct device *dev, char *buf, int nr) -{ - struct w83781d_data *data = w83781d_update_device(dev); - return sprintf(buf, "%ld\n", (long) data->sens[nr - 1]); -} - -static ssize_t -store_sensor_reg(struct device *dev, const char *buf, size_t count, int nr) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); - u32 val, tmp; - - val = simple_strtoul(buf, NULL, 10); - - down(&data->update_lock); - - switch (val) { - case 1: /* PII/Celeron diode */ - tmp = w83781d_read_value(client, W83781D_REG_SCFG1); - w83781d_write_value(client, W83781D_REG_SCFG1, - tmp | BIT_SCFG1[nr - 1]); - tmp = w83781d_read_value(client, W83781D_REG_SCFG2); - w83781d_write_value(client, W83781D_REG_SCFG2, - tmp | BIT_SCFG2[nr - 1]); - data->sens[nr - 1] = val; - break; - case 2: /* 3904 */ - tmp = w83781d_read_value(client, W83781D_REG_SCFG1); - w83781d_write_value(client, W83781D_REG_SCFG1, - tmp | BIT_SCFG1[nr - 1]); - tmp = w83781d_read_value(client, W83781D_REG_SCFG2); - w83781d_write_value(client, W83781D_REG_SCFG2, - tmp & ~BIT_SCFG2[nr - 1]); - data->sens[nr - 1] = val; - break; - case W83781D_DEFAULT_BETA: /* thermistor */ - tmp = w83781d_read_value(client, W83781D_REG_SCFG1); - w83781d_write_value(client, W83781D_REG_SCFG1, - tmp & ~BIT_SCFG1[nr - 1]); - data->sens[nr - 1] = val; - break; - default: - dev_err(dev, "Invalid sensor type %ld; must be 1, 2, or %d\n", - (long) val, W83781D_DEFAULT_BETA); - break; - } - - up(&data->update_lock); - return count; -} - -#define sysfs_sensor(offset) \ -static ssize_t show_regs_sensor_##offset (struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return show_sensor_reg(dev, buf, offset); \ -} \ -static ssize_t store_regs_sensor_##offset (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \ -{ \ - return store_sensor_reg(dev, buf, count, offset); \ -} \ -static DEVICE_ATTR(temp##offset##_type, S_IRUGO | S_IWUSR, show_regs_sensor_##offset, store_regs_sensor_##offset); - -sysfs_sensor(1); -sysfs_sensor(2); -sysfs_sensor(3); - -#define device_create_file_sensor(client, offset) \ -do { \ -device_create_file(&client->dev, &dev_attr_temp##offset##_type); \ -} while (0) - -/* This function is called when: - * w83781d_driver is inserted (when this module is loaded), for each - available adapter - * when a new adapter is inserted (and w83781d_driver is still present) */ -static int -w83781d_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, w83781d_detect); -} - -/* Assumes that adapter is of I2C, not ISA variety. - * OTHERWISE DON'T CALL THIS - */ -static int -w83781d_detect_subclients(struct i2c_adapter *adapter, int address, int kind, - struct i2c_client *new_client) -{ - int i, val1 = 0, id; - int err; - const char *client_name = ""; - struct w83781d_data *data = i2c_get_clientdata(new_client); - - data->lm75[0] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!(data->lm75[0])) { - err = -ENOMEM; - goto ERROR_SC_0; - } - memset(data->lm75[0], 0x00, sizeof (struct i2c_client)); - - id = i2c_adapter_id(adapter); - - if (force_subclients[0] == id && force_subclients[1] == address) { - for (i = 2; i <= 3; i++) { - if (force_subclients[i] < 0x48 || - force_subclients[i] > 0x4f) { - dev_err(&new_client->dev, "Invalid subclient " - "address %d; must be 0x48-0x4f\n", - force_subclients[i]); - err = -EINVAL; - goto ERROR_SC_1; - } - } - w83781d_write_value(new_client, W83781D_REG_I2C_SUBADDR, - (force_subclients[2] & 0x07) | - ((force_subclients[3] & 0x07) << 4)); - data->lm75[0]->addr = force_subclients[2]; - } else { - val1 = w83781d_read_value(new_client, W83781D_REG_I2C_SUBADDR); - data->lm75[0]->addr = 0x48 + (val1 & 0x07); - } - - if (kind != w83783s) { - - data->lm75[1] = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); - if (!(data->lm75[1])) { - err = -ENOMEM; - goto ERROR_SC_1; - } - memset(data->lm75[1], 0x0, sizeof(struct i2c_client)); - - if (force_subclients[0] == id && - force_subclients[1] == address) { - data->lm75[1]->addr = force_subclients[3]; - } else { - data->lm75[1]->addr = 0x48 + ((val1 >> 4) & 0x07); - } - if (data->lm75[0]->addr == data->lm75[1]->addr) { - dev_err(&new_client->dev, - "Duplicate addresses 0x%x for subclients.\n", - data->lm75[0]->addr); - err = -EBUSY; - goto ERROR_SC_2; - } - } - - if (kind == w83781d) - client_name = "w83781d subclient"; - else if (kind == w83782d) - client_name = "w83782d subclient"; - else if (kind == w83783s) - client_name = "w83783s subclient"; - else if (kind == w83627hf) - client_name = "w83627hf subclient"; - else if (kind == as99127f) - client_name = "as99127f subclient"; - - for (i = 0; i <= 1; i++) { - /* store all data in w83781d */ - i2c_set_clientdata(data->lm75[i], NULL); - data->lm75[i]->adapter = adapter; - data->lm75[i]->driver = &w83781d_driver; - data->lm75[i]->flags = 0; - strlcpy(data->lm75[i]->name, client_name, - I2C_NAME_SIZE); - if ((err = i2c_attach_client(data->lm75[i]))) { - dev_err(&new_client->dev, "Subclient %d " - "registration at address 0x%x " - "failed.\n", i, data->lm75[i]->addr); - if (i == 1) - goto ERROR_SC_3; - goto ERROR_SC_2; - } - if (kind == w83783s) - break; - } - - return 0; - -/* Undo inits in case of errors */ -ERROR_SC_3: - i2c_detach_client(data->lm75[0]); -ERROR_SC_2: - if (NULL != data->lm75[1]) - kfree(data->lm75[1]); -ERROR_SC_1: - if (NULL != data->lm75[0]) - kfree(data->lm75[0]); -ERROR_SC_0: - return err; -} - -static int -w83781d_detect(struct i2c_adapter *adapter, int address, int kind) -{ - int i = 0, val1 = 0, val2; - struct i2c_client *new_client; - struct w83781d_data *data; - int err; - const char *client_name = ""; - int is_isa = i2c_is_isa_adapter(adapter); - enum vendor { winbond, asus } vendid; - - if (!is_isa - && !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { - err = -EINVAL; - goto ERROR0; - } - - /* Prevent users from forcing a kind for a bus it isn't supposed - to possibly be on */ - if (is_isa && (kind == as99127f || kind == w83783s)) { - dev_err(&adapter->dev, - "Cannot force I2C-only chip for ISA address 0x%02x.\n", - address); - err = -EINVAL; - goto ERROR0; - } - - if (is_isa) - if (!request_region(address, W83781D_EXTENT, - w83781d_driver.name)) { - dev_dbg(&adapter->dev, "Request of region " - "0x%x-0x%x for w83781d failed\n", address, - address + W83781D_EXTENT - 1); - err = -EBUSY; - goto ERROR0; - } - - /* Probe whether there is anything available on this address. Already - done for SMBus clients */ - if (kind < 0) { - if (is_isa) { - -#define REALLY_SLOW_IO - /* We need the timeouts for at least some LM78-like - chips. But only if we read 'undefined' registers. */ - i = inb_p(address + 1); - if (inb_p(address + 2) != i - || inb_p(address + 3) != i - || inb_p(address + 7) != i) { - dev_dbg(&adapter->dev, "Detection of w83781d " - "chip failed at step 1\n"); - err = -ENODEV; - goto ERROR1; - } -#undef REALLY_SLOW_IO - - /* Let's just hope nothing breaks here */ - i = inb_p(address + 5) & 0x7f; - outb_p(~i & 0x7f, address + 5); - val2 = inb_p(address + 5) & 0x7f; - if (val2 != (~i & 0x7f)) { - outb_p(i, address + 5); - dev_dbg(&adapter->dev, "Detection of w83781d " - "chip failed at step 2 (0x%x != " - "0x%x at 0x%x)\n", val2, ~i & 0x7f, - address + 5); - err = -ENODEV; - goto ERROR1; - } - } - } - - /* OK. For now, we presume we have a valid client. We now create the - client structure, even though we cannot fill it completely yet. - But it allows us to access w83781d_{read,write}_value. */ - - if (!(data = kmalloc(sizeof(struct w83781d_data), GFP_KERNEL))) { - err = -ENOMEM; - goto ERROR1; - } - memset(data, 0, sizeof(struct w83781d_data)); - - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - init_MUTEX(&data->lock); - new_client->adapter = adapter; - new_client->driver = &w83781d_driver; - new_client->flags = 0; - - /* Now, we do the remaining detection. */ - - /* 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 (w83781d_read_value(new_client, W83781D_REG_CONFIG) & 0x80) { - dev_dbg(&new_client->dev, "Detection failed at step " - "3\n"); - err = -ENODEV; - goto ERROR2; - } - val1 = w83781d_read_value(new_client, W83781D_REG_BANK); - val2 = w83781d_read_value(new_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(&new_client->dev, "Detection failed at step " - "4\n"); - err = -ENODEV; - goto ERROR2; - } - /* If Winbond SMBus, check address at 0x48. - Asus doesn't support, except for as99127f rev.2 */ - if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) || - ((val1 & 0x80) && (val2 == 0x5c)))) { - if (w83781d_read_value - (new_client, W83781D_REG_I2C_ADDR) != address) { - dev_dbg(&new_client->dev, "Detection failed " - "at step 5\n"); - err = -ENODEV; - goto ERROR2; - } - } - } - - /* 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 */ - w83781d_write_value(new_client, W83781D_REG_BANK, - (w83781d_read_value(new_client, - W83781D_REG_BANK) & 0x78) | - 0x80); - - /* Determine the chip type. */ - if (kind <= 0) { - /* get vendor ID */ - val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN); - if (val2 == 0x5c) - vendid = winbond; - else if (val2 == 0x12) - vendid = asus; - else { - dev_dbg(&new_client->dev, "Chip was made by neither " - "Winbond nor Asus?\n"); - err = -ENODEV; - goto ERROR2; - } - - val1 = w83781d_read_value(new_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 && !is_isa - && address == 0x2d) - kind = w83783s; - else if (val1 == 0x21 && vendid == winbond) - kind = w83627hf; - else if (val1 == 0x31 && !is_isa && address >= 0x28) - kind = as99127f; - else { - if (kind == 0) - dev_warn(&new_client->dev, "Ignoring 'force' " - "parameter for unknown chip at " - "adapter %d, address 0x%02x\n", - i2c_adapter_id(adapter), address); - err = -EINVAL; - goto ERROR2; - } - } - - if (kind == w83781d) { - client_name = "w83781d"; - } else if (kind == w83782d) { - client_name = "w83782d"; - } else if (kind == w83783s) { - client_name = "w83783s"; - } else if (kind == w83627hf) { - client_name = "w83627hf"; - } else if (kind == as99127f) { - client_name = "as99127f"; - } - - /* Fill in the remaining client fields and put into the global list */ - strlcpy(new_client->name, client_name, I2C_NAME_SIZE); - data->type = kind; - - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Tell the I2C layer a new client has arrived */ - if ((err = i2c_attach_client(new_client))) - goto ERROR2; - - /* attach secondary i2c lm75-like clients */ - if (!is_isa) { - if ((err = w83781d_detect_subclients(adapter, address, - kind, new_client))) - goto ERROR3; - } else { - data->lm75[0] = NULL; - data->lm75[1] = NULL; - } - - /* Initialize the chip */ - w83781d_init_client(new_client); - - /* A few vars need to be filled upon startup */ - for (i = 1; i <= 3; i++) { - data->fan_min[i - 1] = w83781d_read_value(new_client, - W83781D_REG_FAN_MIN(i)); - } - if (kind != w83781d && kind != as99127f) - for (i = 0; i < 4; i++) - data->pwmenable[i] = 1; - - /* Register sysfs hooks */ - device_create_file_in(new_client, 0); - if (kind != w83783s) - device_create_file_in(new_client, 1); - device_create_file_in(new_client, 2); - device_create_file_in(new_client, 3); - device_create_file_in(new_client, 4); - device_create_file_in(new_client, 5); - device_create_file_in(new_client, 6); - if (kind != as99127f && kind != w83781d && kind != w83783s) { - device_create_file_in(new_client, 7); - device_create_file_in(new_client, 8); - } - - device_create_file_fan(new_client, 1); - device_create_file_fan(new_client, 2); - device_create_file_fan(new_client, 3); - - device_create_file_temp(new_client, 1); - device_create_file_temp(new_client, 2); - if (kind != w83783s) - device_create_file_temp(new_client, 3); - - device_create_file_vid(new_client); - device_create_file_vrm(new_client); - - device_create_file_fan_div(new_client, 1); - device_create_file_fan_div(new_client, 2); - device_create_file_fan_div(new_client, 3); - - device_create_file_alarms(new_client); - - device_create_file_beep(new_client); - - if (kind != w83781d && kind != as99127f) { - device_create_file_pwm(new_client, 1); - device_create_file_pwm(new_client, 2); - device_create_file_pwmenable(new_client, 2); - } - if (kind == w83782d && !is_isa) { - device_create_file_pwm(new_client, 3); - device_create_file_pwm(new_client, 4); - } - - if (kind != as99127f && kind != w83781d) { - device_create_file_sensor(new_client, 1); - device_create_file_sensor(new_client, 2); - if (kind != w83783s) - device_create_file_sensor(new_client, 3); - } - - return 0; - -ERROR3: - i2c_detach_client(new_client); -ERROR2: - kfree(data); -ERROR1: - if (is_isa) - release_region(address, W83781D_EXTENT); -ERROR0: - return err; -} - -static int -w83781d_detach_client(struct i2c_client *client) -{ - int err; - - if (i2c_is_isa_client(client)) - release_region(client->addr, W83781D_EXTENT); - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, - "Client deregistration failed, client not detached.\n"); - return err; - } - - if (i2c_get_clientdata(client)==NULL) { - /* subclients */ - kfree(client); - } else { - /* main client */ - kfree(i2c_get_clientdata(client)); - } - - return 0; -} - -/* The SMBus locks itself, usually, but nothing may access the Winbond between - bank switches. ISA access must always be locked explicitly! - We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, - would slow down the W83781D access and should not be necessary. - There are some ugly typecasts here, but the good news is - they should - nowhere else be necessary! */ -static int -w83781d_read_value(struct i2c_client *client, u16 reg) -{ - struct w83781d_data *data = i2c_get_clientdata(client); - int res, word_sized, bank; - struct i2c_client *cl; - - down(&data->lock); - if (i2c_is_isa_client(client)) { - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x50) - || ((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); - if (word_sized) { - outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); - res = - (res << 8) + inb_p(client->addr + - W83781D_DATA_REG_OFFSET); - } - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); - } - } else { - bank = (reg >> 8) & 0x0f; - if (bank > 2) - /* switch banks */ - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, - bank); - if (bank == 0 || bank > 2) { - res = i2c_smbus_read_byte_data(client, reg & 0xff); - } else { - /* switch to subclient */ - cl = data->lm75[bank - 1]; - /* convert from ISA to LM75 I2C addresses */ - switch (reg & 0xff) { - case 0x50: /* TEMP */ - res = swab16(i2c_smbus_read_word_data(cl, 0)); - break; - case 0x52: /* CONFIG */ - res = i2c_smbus_read_byte_data(cl, 1); - break; - case 0x53: /* HYST */ - res = swab16(i2c_smbus_read_word_data(cl, 2)); - break; - case 0x55: /* OVER */ - default: - res = swab16(i2c_smbus_read_word_data(cl, 3)); - break; - } - } - if (bank > 2) - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); - } - up(&data->lock); - return res; -} - -static int -w83781d_write_value(struct i2c_client *client, u16 reg, u16 value) -{ - struct w83781d_data *data = i2c_get_clientdata(client); - int word_sized, bank; - struct i2c_client *cl; - - down(&data->lock); - if (i2c_is_isa_client(client)) { - word_sized = (((reg & 0xff00) == 0x100) - || ((reg & 0xff00) == 0x200)) - && (((reg & 0x00ff) == 0x53) - || ((reg & 0x00ff) == 0x55)); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(reg >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - } - outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); - if (word_sized) { - outb_p(value >> 8, - client->addr + W83781D_DATA_REG_OFFSET); - outb_p((reg & 0xff) + 1, - client->addr + W83781D_ADDR_REG_OFFSET); - } - outb_p(value & 0xff, client->addr + W83781D_DATA_REG_OFFSET); - if (reg & 0xff00) { - outb_p(W83781D_REG_BANK, - client->addr + W83781D_ADDR_REG_OFFSET); - outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); - } - } else { - bank = (reg >> 8) & 0x0f; - if (bank > 2) - /* switch banks */ - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, - bank); - if (bank == 0 || bank > 2) { - i2c_smbus_write_byte_data(client, reg & 0xff, - value & 0xff); - } else { - /* switch to subclient */ - cl = data->lm75[bank - 1]; - /* convert from ISA to LM75 I2C addresses */ - switch (reg & 0xff) { - case 0x52: /* CONFIG */ - i2c_smbus_write_byte_data(cl, 1, value & 0xff); - break; - case 0x53: /* HYST */ - i2c_smbus_write_word_data(cl, 2, swab16(value)); - break; - case 0x55: /* OVER */ - i2c_smbus_write_word_data(cl, 3, swab16(value)); - break; - } - } - if (bank > 2) - i2c_smbus_write_byte_data(client, W83781D_REG_BANK, 0); - } - up(&data->lock); - return 0; -} - -/* Called when we have found a new W83781D. It should set limits, etc. */ -static void -w83781d_init_client(struct i2c_client *client) -{ - struct w83781d_data *data = i2c_get_clientdata(client); - int i, p; - int type = data->type; - u8 tmp; - - if (init && type != as99127f) { /* this resets registers we don't have - documentation for on the as99127f */ - /* save these registers */ - i = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG); - p = w83781d_read_value(client, W83781D_REG_PWMCLK12); - /* Reset all except Watchdog values and last conversion values - This sets fan-divs to 2, among others */ - w83781d_write_value(client, W83781D_REG_CONFIG, 0x80); - /* Restore the registers and disable power-on abnormal beep. - This saves FAN 1/2/3 input/output values set by BIOS. */ - w83781d_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80); - w83781d_write_value(client, W83781D_REG_PWMCLK12, p); - /* Disable master beep-enable (reset turns it on). - Individual beep_mask should be reset to off but for some reason - disabling this bit helps some people not get beeped */ - w83781d_write_value(client, W83781D_REG_BEEP_INTS2, 0); - } - - data->vrm = i2c_which_vrm(); - - if ((type != w83781d) && (type != as99127f)) { - tmp = w83781d_read_value(client, W83781D_REG_SCFG1); - for (i = 1; i <= 3; i++) { - if (!(tmp & BIT_SCFG1[i - 1])) { - data->sens[i - 1] = W83781D_DEFAULT_BETA; - } else { - if (w83781d_read_value - (client, - W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) - data->sens[i - 1] = 1; - else - data->sens[i - 1] = 2; - } - if (type == w83783s && i == 2) - break; - } - } - - if (init && type != as99127f) { - /* Enable temp2 */ - tmp = w83781d_read_value(client, W83781D_REG_TEMP2_CONFIG); - if (tmp & 0x01) { - dev_warn(&client->dev, "Enabling temp2, readings " - "might not make sense\n"); - w83781d_write_value(client, W83781D_REG_TEMP2_CONFIG, - tmp & 0xfe); - } - - /* Enable temp3 */ - if (type != w83783s) { - tmp = w83781d_read_value(client, - W83781D_REG_TEMP3_CONFIG); - if (tmp & 0x01) { - dev_warn(&client->dev, "Enabling temp3, " - "readings might not make sense\n"); - w83781d_write_value(client, - W83781D_REG_TEMP3_CONFIG, tmp & 0xfe); - } - } - - if (type != w83781d) { - /* enable comparator mode for temp2 and temp3 so - alarm indication will work correctly */ - i = w83781d_read_value(client, W83781D_REG_IRQ); - if (!(i & 0x40)) - w83781d_write_value(client, W83781D_REG_IRQ, - i | 0x40); - } - } - - /* Start monitoring */ - w83781d_write_value(client, W83781D_REG_CONFIG, - (w83781d_read_value(client, - W83781D_REG_CONFIG) & 0xf7) - | 0x01); -} - -static struct w83781d_data *w83781d_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83781d_data *data = i2c_get_clientdata(client); - int i; - - down(&data->update_lock); - - if (time_after(jiffies, data->last_updated + HZ + HZ / 2) - || !data->valid) { - dev_dbg(dev, "Starting device update\n"); - - for (i = 0; i <= 8; i++) { - if (data->type == w83783s && i == 1) - continue; /* 783S has no in1 */ - data->in[i] = - w83781d_read_value(client, W83781D_REG_IN(i)); - data->in_min[i] = - w83781d_read_value(client, W83781D_REG_IN_MIN(i)); - data->in_max[i] = - w83781d_read_value(client, W83781D_REG_IN_MAX(i)); - if ((data->type != w83782d) - && (data->type != w83627hf) && (i == 6)) - break; - } - for (i = 1; i <= 3; i++) { - data->fan[i - 1] = - w83781d_read_value(client, W83781D_REG_FAN(i)); - data->fan_min[i - 1] = - w83781d_read_value(client, W83781D_REG_FAN_MIN(i)); - } - if (data->type != w83781d && data->type != as99127f) { - for (i = 1; i <= 4; i++) { - data->pwm[i - 1] = - w83781d_read_value(client, - W83781D_REG_PWM(i)); - if ((data->type != w83782d - || i2c_is_isa_client(client)) - && i == 2) - break; - } - /* Only PWM2 can be disabled */ - data->pwmenable[1] = (w83781d_read_value(client, - W83781D_REG_PWMCLK12) & 0x08) >> 3; - } - - data->temp = w83781d_read_value(client, W83781D_REG_TEMP(1)); - data->temp_max = - w83781d_read_value(client, W83781D_REG_TEMP_OVER(1)); - data->temp_max_hyst = - w83781d_read_value(client, W83781D_REG_TEMP_HYST(1)); - data->temp_add[0] = - w83781d_read_value(client, W83781D_REG_TEMP(2)); - data->temp_max_add[0] = - w83781d_read_value(client, W83781D_REG_TEMP_OVER(2)); - data->temp_max_hyst_add[0] = - w83781d_read_value(client, W83781D_REG_TEMP_HYST(2)); - if (data->type != w83783s) { - data->temp_add[1] = - w83781d_read_value(client, W83781D_REG_TEMP(3)); - data->temp_max_add[1] = - w83781d_read_value(client, - W83781D_REG_TEMP_OVER(3)); - data->temp_max_hyst_add[1] = - w83781d_read_value(client, - W83781D_REG_TEMP_HYST(3)); - } - i = w83781d_read_value(client, W83781D_REG_VID_FANDIV); - data->vid = i & 0x0f; - data->vid |= (w83781d_read_value(client, - W83781D_REG_CHIPID) & 0x01) << 4; - data->fan_div[0] = (i >> 4) & 0x03; - data->fan_div[1] = (i >> 6) & 0x03; - data->fan_div[2] = (w83781d_read_value(client, - W83781D_REG_PIN) >> 6) & 0x03; - if ((data->type != w83781d) && (data->type != as99127f)) { - i = w83781d_read_value(client, W83781D_REG_VBAT); - data->fan_div[0] |= (i >> 3) & 0x04; - data->fan_div[1] |= (i >> 4) & 0x04; - data->fan_div[2] |= (i >> 5) & 0x04; - } - data->alarms = - w83781d_read_value(client, - W83781D_REG_ALARM1) + - (w83781d_read_value(client, W83781D_REG_ALARM2) << 8); - if ((data->type == w83782d) || (data->type == w83627hf)) { - data->alarms |= - w83781d_read_value(client, - W83781D_REG_ALARM3) << 16; - } - i = w83781d_read_value(client, W83781D_REG_BEEP_INTS2); - data->beep_enable = i >> 7; - data->beep_mask = ((i & 0x7f) << 8) + - w83781d_read_value(client, W83781D_REG_BEEP_INTS1); - if ((data->type != w83781d) && (data->type != as99127f)) { - data->beep_mask |= - w83781d_read_value(client, - W83781D_REG_BEEP_INTS3) << 16; - } - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init -sensors_w83781d_init(void) -{ - return i2c_add_driver(&w83781d_driver); -} - -static void __exit -sensors_w83781d_exit(void) -{ - i2c_del_driver(&w83781d_driver); -} - -MODULE_AUTHOR("Frodo Looijaard , " - "Philip Edelbrock , " - "and Mark Studebaker "); -MODULE_DESCRIPTION("W83781D driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_w83781d_init); -module_exit(sensors_w83781d_exit); diff --git a/drivers/i2c/chips/w83l785ts.c b/drivers/i2c/chips/w83l785ts.c deleted file mode 100644 index 4469d52aba4c..000000000000 --- a/drivers/i2c/chips/w83l785ts.c +++ /dev/null @@ -1,328 +0,0 @@ -/* - * w83l785ts.c - Part of lm_sensors, Linux kernel modules for hardware - * monitoring - * Copyright (C) 2003-2004 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 - * resolution and a 3 deg accuracy. Datasheet can be obtained from - * Winbond's website at: - * http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf - * - * Ported to Linux 2.6 by Wolfgang Ziegler and Jean Delvare - * . - * - * Thanks to James Bolt for benchmarking the read - * error handling mechanism. - * - * 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., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include - -/* How many retries on register read error */ -#define MAX_RETRIES 5 - -/* - * Address to scan - * Address is fully defined internally and cannot be changed. - */ - -static unsigned short normal_i2c[] = { 0x2e, I2C_CLIENT_END }; -static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; - -/* - * Insmod parameters - */ - -SENSORS_INSMOD_1(w83l785ts); - -/* - * The W83L785TS-S registers - * Manufacturer ID is 0x5CA3 for Winbond. - */ - -#define W83L785TS_REG_MAN_ID1 0x4D -#define W83L785TS_REG_MAN_ID2 0x4C -#define W83L785TS_REG_CHIP_ID 0x4E -#define W83L785TS_REG_CONFIG 0x40 -#define W83L785TS_REG_TYPE 0x52 -#define W83L785TS_REG_TEMP 0x27 -#define W83L785TS_REG_TEMP_OVER 0x53 /* not sure about this one */ - -/* - * Conversions - * The W83L785TS-S uses signed 8-bit values. - */ - -#define TEMP_FROM_REG(val) ((val & 0x80 ? val-0x100 : val) * 1000) - -/* - * Functions declaration - */ - -static int w83l785ts_attach_adapter(struct i2c_adapter *adapter); -static int w83l785ts_detect(struct i2c_adapter *adapter, int address, - int kind); -static int w83l785ts_detach_client(struct i2c_client *client); -static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval); -static struct w83l785ts_data *w83l785ts_update_device(struct device *dev); - -/* - * Driver data (common to all clients) - */ - -static struct i2c_driver w83l785ts_driver = { - .owner = THIS_MODULE, - .name = "w83l785ts", - .id = I2C_DRIVERID_W83L785TS, - .flags = I2C_DF_NOTIFY, - .attach_adapter = w83l785ts_attach_adapter, - .detach_client = w83l785ts_detach_client, -}; - -/* - * Client data (each client gets its own) - */ - -struct w83l785ts_data { - struct i2c_client client; - struct semaphore update_lock; - char valid; /* zero until following fields are valid */ - unsigned long last_updated; /* in jiffies */ - - /* registers values */ - u8 temp, temp_over; -}; - -/* - * Sysfs stuff - */ - -static ssize_t show_temp(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83l785ts_data *data = w83l785ts_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp)); -} - -static ssize_t show_temp_over(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct w83l785ts_data *data = w83l785ts_update_device(dev); - return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over)); -} - -static DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL); -static DEVICE_ATTR(temp1_max, S_IRUGO, show_temp_over, NULL); - -/* - * Real code - */ - -static int w83l785ts_attach_adapter(struct i2c_adapter *adapter) -{ - if (!(adapter->class & I2C_CLASS_HWMON)) - return 0; - return i2c_detect(adapter, &addr_data, w83l785ts_detect); -} - -/* - * The following function does more than just detection. If detection - * succeeds, it also registers the new chip. - */ -static int w83l785ts_detect(struct i2c_adapter *adapter, int address, int kind) -{ - struct i2c_client *new_client; - struct w83l785ts_data *data; - int err = 0; - - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - goto exit; - - if (!(data = kmalloc(sizeof(struct w83l785ts_data), GFP_KERNEL))) { - err = -ENOMEM; - goto exit; - } - memset(data, 0, sizeof(struct w83l785ts_data)); - - - /* The common I2C client data is placed right before the - * W83L785TS-specific data. */ - new_client = &data->client; - i2c_set_clientdata(new_client, data); - new_client->addr = address; - new_client->adapter = adapter; - new_client->driver = &w83l785ts_driver; - new_client->flags = 0; - - /* - * 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", - address); - goto exit_free; - } - } - - 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); - goto exit_free; - } - } - - /* We can fill in the remaining client fields. */ - strlcpy(new_client->name, "w83l785ts", I2C_NAME_SIZE); - data->valid = 0; - init_MUTEX(&data->update_lock); - - /* Default values in case the first read fails (unlikely). */ - data->temp_over = data->temp = 0; - - /* Tell the I2C layer a new client has arrived. */ - if ((err = i2c_attach_client(new_client))) - goto exit_free; - - /* - * Initialize the W83L785TS chip - * Nothing yet, assume it is already started. - */ - - /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_temp1_input); - device_create_file(&new_client->dev, &dev_attr_temp1_max); - - return 0; - -exit_free: - kfree(data); -exit: - return err; -} - -static int w83l785ts_detach_client(struct i2c_client *client) -{ - int err; - - if ((err = i2c_detach_client(client))) { - dev_err(&client->dev, "Client deregistration failed, " - "client not detached.\n"); - return err; - } - - kfree(i2c_get_clientdata(client)); - return 0; -} - -static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval) -{ - int value, i; - - /* Frequent read errors have been reported on Asus boards, so we - * retry on read errors. If it still fails (unlikely), return the - * default value requested by the caller. */ - for (i = 1; i <= MAX_RETRIES; i++) { - value = i2c_smbus_read_byte_data(client, reg); - if (value >= 0) { - dev_dbg(&client->dev, "Read 0x%02x from register " - "0x%02x.\n", value, reg); - return value; - } - dev_dbg(&client->dev, "Read failed, will retry in %d.\n", i); - msleep(i); - } - - dev_err(&client->dev, "Couldn't read value from register 0x%02x. " - "Please report.\n", reg); - return defval; -} - -static struct w83l785ts_data *w83l785ts_update_device(struct device *dev) -{ - struct i2c_client *client = to_i2c_client(dev); - struct w83l785ts_data *data = i2c_get_clientdata(client); - - down(&data->update_lock); - - if (!data->valid || time_after(jiffies, data->last_updated + HZ * 2)) { - dev_dbg(&client->dev, "Updating w83l785ts data.\n"); - data->temp = w83l785ts_read_value(client, - W83L785TS_REG_TEMP, data->temp); - data->temp_over = w83l785ts_read_value(client, - W83L785TS_REG_TEMP_OVER, data->temp_over); - - data->last_updated = jiffies; - data->valid = 1; - } - - up(&data->update_lock); - - return data; -} - -static int __init sensors_w83l785ts_init(void) -{ - return i2c_add_driver(&w83l785ts_driver); -} - -static void __exit sensors_w83l785ts_exit(void) -{ - i2c_del_driver(&w83l785ts_driver); -} - -MODULE_AUTHOR("Jean Delvare "); -MODULE_DESCRIPTION("W83L785TS-S driver"); -MODULE_LICENSE("GPL"); - -module_init(sensors_w83l785ts_init); -module_exit(sensors_w83l785ts_exit); -- cgit v1.2.3