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 diff --git a/drivers/i2c/chips/via686a.c b/drivers/i2c/chips/via686a.c index 137d9b7..164d479 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 v0.10.2 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 diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index a0982da..b28964b 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 c0ac01b..280e963 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 v0.10.2 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 diff --git a/drivers/i2c/algos/i2c-algo-ite.c b/drivers/i2c/algos/i2c-algo-ite.c index 68e9e68..e6cae39 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 45e6efb1..0ab7e37 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 1f80ba9..6d34ee3 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 2b5911c..bbd5e4e 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 v0.10.2 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 diff --git a/drivers/i2c/chips/max6875.c b/drivers/i2c/chips/max6875.c index fe6b150..c4f14d9 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 v0.10.2 From 089bd86632769051f15cd7387eebe126d18f151f Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Thu, 23 Jun 2005 23:37:53 +0200 Subject: [PATCH] I2C: max6875 documentation update Here is a proposed documentation update for the new max6875 i2c chip driver. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/i2c/chips/max6875 b/Documentation/i2c/chips/max6875 index b4fb49b..b020028 100644 --- a/Documentation/i2c/chips/max6875 +++ b/Documentation/i2c/chips/max6875 @@ -2,10 +2,10 @@ Kernel driver max6875 ===================== Supported chips: - * Maxim max6874, max6875 - Prefixes: 'max6875' + * Maxim MAX6874, MAX6875 + Prefix: 'max6875' Addresses scanned: 0x50, 0x52 - Datasheets: + Datasheet: http://pdfserv.maxim-ic.com/en/ds/MAX6874-MAX6875.pdf Author: Ben Gardner @@ -23,14 +23,26 @@ Module Parameters Description ----------- -The MAXIM max6875 is a EEPROM-programmable power-supply sequencer/supervisor. +The Maxim MAX6875 is an EEPROM-programmable power-supply sequencer/supervisor. It provides timed outputs that can be used as a watchdog, if properly wired. It also provides 512 bytes of user EEPROM. -At reset, the max6875 reads the configuration eeprom into its configuration +At reset, the MAX6875 reads the configuration EEPROM into its configuration registers. The chip then begins to operate according to the values in the registers. +The Maxim MAX6874 is a similar, mostly compatible device, with more intputs +and outputs: + + vin gpi vout +MAX6874 6 4 8 +MAX6875 4 3 5 + +MAX6874 chips can have four different addresses (as opposed to only two for +the MAX6875). The additional addresses (0x54 and 0x56) are not probed by +this driver by default, but the probe module parameter can be used if +needed. + See the datasheet for details on how to program the EEPROM. -- cgit v0.10.2 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 diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index b28964b..5bd72c48 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 v0.10.2 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 diff --git a/drivers/i2c/chips/m41t00.c b/drivers/i2c/chips/m41t00.c index 5e463c47..778d7e1 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 v0.10.2 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 diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 51ce268..4fd4f52 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 v0.10.2 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 diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c index addf0ad..6ea413f 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 v0.10.2 From 61f5809d3ebce9d5433b8696048e91405b681023 Mon Sep 17 00:00:00 2001 From: "david-b@pacbell.net" Date: Wed, 29 Jun 2005 07:14:06 -0700 Subject: [PATCH] I2C: minor I2C doc cleanups The I2C stack has long had "id" fields, of rather dubious utility, in many data structures. This removes mention of one of them from the documentation about how to write an I2C driver, so that only drivers that really need to use them (probably old/legacy code) will have any reason to use this field. Signed-off-by: David Brownell Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/i2c/writing-clients b/Documentation/i2c/writing-clients index f482dae..91664be 100644 --- a/Documentation/i2c/writing-clients +++ b/Documentation/i2c/writing-clients @@ -27,7 +27,6 @@ address. static struct i2c_driver foo_driver = { .owner = THIS_MODULE, .name = "Foo version 2.3 driver", - .id = I2C_DRIVERID_FOO, /* from i2c-id.h, optional */ .flags = I2C_DF_NOTIFY, .attach_adapter = &foo_attach_adapter, .detach_client = &foo_detach_client, @@ -37,12 +36,6 @@ static struct i2c_driver foo_driver = { The name can be chosen freely, and may be upto 40 characters long. Please use something descriptive here. -If used, the id should be a unique ID. The range 0xf000 to 0xffff is -reserved for local use, and you can use one of those until you start -distributing the driver, at which time you should contact the i2c authors -to get your own ID(s). Note that most of the time you don't need an ID -at all so you can just omit it. - Don't worry about the flags field; just put I2C_DF_NOTIFY into it. This means that your driver will be notified when new adapters are found. This is almost always what you want. -- cgit v0.10.2 From a68e2f4895070f3a449bfe5ae1174b73cc900642 Mon Sep 17 00:00:00 2001 From: Jan Veldeman Date: Fri, 1 Jul 2005 16:20:24 +0200 Subject: [PATCH] I2C: Documentation fix Fix documentation to match code in include/linux/i2c-dev.h Signed-off-by: Jan Veldeman Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/i2c/dev-interface b/Documentation/i2c/dev-interface index 09d6cda..a78ea62 100644 --- a/Documentation/i2c/dev-interface +++ b/Documentation/i2c/dev-interface @@ -97,10 +97,10 @@ ioctl(file,I2C_PEC,long select) ioctl(file,I2C_FUNCS,unsigned long *funcs) Gets the adapter functionality and puts it in *funcs. -ioctl(file,I2C_RDWR,struct i2c_ioctl_rdwr_data *msgset) +ioctl(file,I2C_RDWR,struct i2c_rdwr_ioctl_data *msgset) Do combined read/write transaction without stop in between. - The argument is a pointer to a struct i2c_ioctl_rdwr_data { + The argument is a pointer to a struct i2c_rdwr_ioctl_data { struct i2c_msg *msgs; /* ptr to array of simple messages */ int nmsgs; /* number of messages to exchange */ -- cgit v0.10.2 From 1d772e2587da3c8b0fb8610fcc1c91fd82f87e52 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sat, 25 Jun 2005 11:37:40 +0200 Subject: [PATCH] I2C: Clarify the usage of i2c-dev.h Upon suggestion by Nils Roeder, here is an update to the i2c documentation to clarify which header files user-space applications relying on the i2c-dev interface should include. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/i2c/dev-interface b/Documentation/i2c/dev-interface index a78ea62..b849ad6 100644 --- a/Documentation/i2c/dev-interface +++ b/Documentation/i2c/dev-interface @@ -14,9 +14,12 @@ C example ========= So let's say you want to access an i2c adapter from a C program. The -first thing to do is `#include " and "#include . -Yes, I know, you should never include kernel header files, but until glibc -knows about i2c, there is not much choice. +first thing to do is "#include ". Please note that +there are two files named "i2c-dev.h" out there, one is distributed +with the Linux kernel and is meant to be included from kernel +driver code, the other one is distributed with lm_sensors and is +meant to be included from user-space programs. You obviously want +the second one here. Now, you have to decide which adapter you want to access. You should inspect /sys/class/i2c-dev/ to decide this. Adapter numbers are assigned @@ -78,7 +81,7 @@ Full interface description ========================== The following IOCTLs are defined and fully supported -(see also i2c-dev.h and i2c.h): +(see also i2c-dev.h): ioctl(file,I2C_SLAVE,long addr) Change slave address. The address is passed in the 7 lower bits of the -- cgit v0.10.2 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 diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index 5bd72c48..fddfc11 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 v0.10.2 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 diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 312cf32..8a9c428 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 v0.10.2 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 diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 8752751..0fb8a04 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 62a89e8..9172860 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 6a47336..5e8591c4 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 aed4a9b..cf087de 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 3167be5..126a851 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 0000000..140d5f8 --- /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 0000000..2781403 --- /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 fddfc11..43f70db 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 b5e6d2f..a876dd4 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 v0.10.2 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 diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c new file mode 100644 index 0000000..d2c774c --- /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 0000000..e452d0d --- /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 0000000..3c85fe1 --- /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 0000000..9168e98 --- /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 0000000..5c68e9c --- /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 0000000..70d996d --- /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 0000000..0bcf82b --- /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 0000000..5360d58 --- /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 0000000..da41174 --- /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 0000000..3beaa61 --- /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 0000000..6bedf72 --- /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 0000000..a13a504 --- /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 0000000..db20c9e --- /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 0000000..7c6f9ea --- /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 0000000..5be164e --- /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 0000000..63e3f2f --- /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 0000000..b98f449 --- /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 0000000..2924146 --- /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 0000000..8100595 --- /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 0000000..a49008b --- /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 0000000..b4d7fd4 --- /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 0000000..a67dcad --- /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 0000000..215c8e4 --- /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 0000000..bf553dc --- /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 0000000..876c68f --- /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 0000000..6bbfc8f --- /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 0000000..251ac26 --- /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 0000000..897117a --- /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 0000000..164d479 --- /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 0000000..8a40b69 --- /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 0000000..bd87a42 --- /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 0000000..0bb131c --- /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 0000000..4469d52 --- /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 d2c774c..0000000 --- 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 e452d0d..0000000 --- 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 3c85fe1..0000000 --- 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 9168e98..0000000 --- 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 5c68e9c..0000000 --- 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 70d996d..0000000 --- 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 0bcf82b..0000000 --- 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 5360d58..0000000 --- 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 da41174..0000000 --- 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 3beaa61..0000000 --- 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 6bedf72..0000000 --- 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 a13a504..0000000 --- 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 db20c9e..0000000 --- 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 7c6f9ea..0000000 --- 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 5be164e..0000000 --- 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 63e3f2f..0000000 --- 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 b98f449..0000000 --- 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 2924146..0000000 --- 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 8100595..0000000 --- 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 a49008b..0000000 --- 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 b4d7fd4..0000000 --- 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 a67dcad..0000000 --- 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 215c8e4..0000000 --- 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 bf553dc..0000000 --- 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 876c68f..0000000 --- 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 6bbfc8f..0000000 --- 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 251ac26..0000000 --- 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 897117a..0000000 --- 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 164d479..0000000 --- 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 8a40b69..0000000 --- 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 bd87a42..0000000 --- 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 0bb131c..0000000 --- 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 4469d52..0000000 --- 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 v0.10.2 From ede7fbdf526c314850c9f32dd8da1753bf8d0ad5 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Sat, 2 Jul 2005 18:52:48 +0200 Subject: [PATCH] I2C: Move hwmon drivers (3/3) Part 3: Move the drivers documentation, plus two general documentation files. Note that the patch "adds trailing whitespace", because it does move the files as-is, and some files happen to have trailing whitespace. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman diff --git a/Documentation/hwmon/adm1021 b/Documentation/hwmon/adm1021 new file mode 100644 index 0000000..03d02bf --- /dev/null +++ b/Documentation/hwmon/adm1021 @@ -0,0 +1,111 @@ +Kernel driver adm1021 +===================== + +Supported chips: + * Analog Devices ADM1021 + Prefix: 'adm1021' + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + Datasheet: Publicly available at the Analog Devices website + * Analog Devices ADM1021A/ADM1023 + Prefix: 'adm1023' + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + Datasheet: Publicly available at the Analog Devices website + * Genesys Logic GL523SM + Prefix: 'gl523sm' + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + Datasheet: + * Intel Xeon Processor + Prefix: - any other - may require 'force_adm1021' parameter + Addresses scanned: none + Datasheet: Publicly available at Intel website + * Maxim MAX1617 + Prefix: 'max1617' + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + Datasheet: Publicly available at the Maxim website + * Maxim MAX1617A + Prefix: 'max1617a' + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + Datasheet: Publicly available at the Maxim website + * National Semiconductor LM84 + Prefix: 'lm84' + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + Datasheet: Publicly available at the National Semiconductor website + * Philips NE1617 + Prefix: 'max1617' (probably detected as a max1617) + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + Datasheet: Publicly available at the Philips website + * Philips NE1617A + Prefix: 'max1617' (probably detected as a max1617) + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + Datasheet: Publicly available at the Philips website + * TI THMC10 + Prefix: 'thmc10' + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + Datasheet: Publicly available at the TI website + * Onsemi MC1066 + Prefix: 'mc1066' + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + Datasheet: Publicly available at the Onsemi website + + +Authors: + Frodo Looijaard , + Philip Edelbrock + +Module Parameters +----------------- + +* read_only: int + Don't set any values, read only mode + + +Description +----------- + +The chips supported by this driver are very similar. The Maxim MAX1617 is +the oldest; it has the problem that it is not very well detectable. The +MAX1617A solves that. The ADM1021 is a straight clone of the MAX1617A. +Ditto for the THMC10. From here on, we will refer to all these chips as +ADM1021-clones. + +The ADM1021 and MAX1617A reports a die code, which is a sort of revision +code. This can help us pinpoint problems; it is not very useful +otherwise. + +ADM1021-clones implement two temperature sensors. One of them is internal, +and measures the temperature of the chip itself; the other is external and +is realised in the form of a transistor-like device. A special alarm +indicates whether the remote sensor is connected. + +Each sensor has its own low and high limits. When they are crossed, the +corresponding alarm is set and remains on as long as the temperature stays +out of range. Temperatures are measured in degrees Celsius. Measurements +are possible between -65 and +127 degrees, with a resolution of one degree. + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once. This means that the cause for the alarm may already +have disappeared! + +This driver only updates its values each 1.5 seconds; reading it more often +will do no harm, but will return 'old' values. It is possible to make +ADM1021-clones do faster measurements, but there is really no good reason +for that. + +Xeon support +------------ + +Some Xeon processors have real max1617, adm1021, or compatible chips +within them, with two temperature sensors. + +Other Xeons have chips with only one sensor. + +If you have a Xeon, and the adm1021 module loads, and both temperatures +appear valid, then things are good. + +If the adm1021 module doesn't load, you should try this: + modprobe adm1021 force_adm1021=BUS,ADDRESS + ADDRESS can only be 0x18, 0x1a, 0x29, 0x2b, 0x4c, or 0x4e. + +If you have dual Xeons you may have appear to have two separate +adm1021-compatible chips, or two single-temperature sensors, at distinct +addresses. diff --git a/Documentation/hwmon/adm1025 b/Documentation/hwmon/adm1025 new file mode 100644 index 0000000..39d2b78 --- /dev/null +++ b/Documentation/hwmon/adm1025 @@ -0,0 +1,51 @@ +Kernel driver adm1025 +===================== + +Supported chips: + * Analog Devices ADM1025, ADM1025A + Prefix: 'adm1025' + Addresses scanned: I2C 0x2c - 0x2e + Datasheet: Publicly available at the Analog Devices website + * Philips NE1619 + Prefix: 'ne1619' + Addresses scanned: I2C 0x2c - 0x2d + Datasheet: Publicly available at the Philips website + +The NE1619 presents some differences with the original ADM1025: + * Only two possible addresses (0x2c - 0x2d). + * No temperature offset register, but we don't use it anyway. + * No INT mode for pin 16. We don't play with it anyway. + +Authors: + Chen-Yuan Wu , + Jean Delvare + +Description +----------- + +(This is from Analog Devices.) The ADM1025 is a complete system hardware +monitor for microprocessor-based systems, providing measurement and limit +comparison of various system parameters. Five voltage measurement inputs +are provided, for monitoring +2.5V, +3.3V, +5V and +12V power supplies and +the processor core voltage. The ADM1025 can monitor a sixth power-supply +voltage by measuring its own VCC. One input (two pins) is dedicated to a +remote temperature-sensing diode and an on-chip temperature sensor allows +ambient temperature to be monitored. + +One specificity of this chip is that the pin 11 can be hardwired in two +different manners. It can act as the +12V power-supply voltage analog +input, or as the a fifth digital entry for the VID reading (bit 4). It's +kind of strange since both are useful, and the reason for designing the +chip that way is obscure at least to me. The bit 5 of the configuration +register can be used to define how the chip is hardwired. Please note that +it is not a choice you have to make as the user. The choice was already +made by your motherboard's maker. If the configuration bit isn't set +properly, you'll have a wrong +12V reading or a wrong VID reading. The way +the driver handles that is to preserve this bit through the initialization +process, assuming that the BIOS set it up properly beforehand. If it turns +out not to be true in some cases, we'll provide a module parameter to force +modes. + +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. diff --git a/Documentation/hwmon/adm1026 b/Documentation/hwmon/adm1026 new file mode 100644 index 0000000..473c689 --- /dev/null +++ b/Documentation/hwmon/adm1026 @@ -0,0 +1,93 @@ +Kernel driver adm1026 +===================== + +Supported chips: + * Analog Devices ADM1026 + Prefix: 'adm1026' + Addresses scanned: I2C 0x2c, 0x2d, 0x2e + Datasheet: Publicly available at the Analog Devices website + http://www.analog.com/en/prod/0,,766_825_ADM1026,00.html + +Authors: + Philip Pokorny for Penguin Computing + Justin Thiessen + +Module Parameters +----------------- + +* gpio_input: int array (min = 1, max = 17) + List of GPIO pins (0-16) to program as inputs +* gpio_output: int array (min = 1, max = 17) + List of GPIO pins (0-16) to program as outputs +* gpio_inverted: int array (min = 1, max = 17) + List of GPIO pins (0-16) to program as inverted +* gpio_normal: int array (min = 1, max = 17) + List of GPIO pins (0-16) to program as normal/non-inverted +* gpio_fan: int array (min = 1, max = 8) + List of GPIO pins (0-7) to program as fan tachs + + +Description +----------- + +This driver implements support for the Analog Devices ADM1026. Analog +Devices calls it a "complete thermal system management controller." + +The ADM1026 implements three (3) temperature sensors, 17 voltage sensors, +16 general purpose digital I/O lines, eight (8) fan speed sensors (8-bit), +an analog output and a PWM output along with limit, alarm and mask bits for +all of the above. There is even 8k bytes of EEPROM memory on chip. + +Temperatures are measured in degrees Celsius. There are two external +sensor inputs and one internal sensor. Each sensor has a high and low +limit. If the limit is exceeded, an interrupt (#SMBALERT) can be +generated. The interrupts can be masked. In addition, there are over-temp +limits for each sensor. If this limit is exceeded, the #THERM output will +be asserted. The current temperature and limits have a resolution of 1 +degree. + +Fan rotation speeds are reported in RPM (rotations per minute) but measured +in counts of a 22.5kHz internal clock. Each fan has a high limit which +corresponds to a minimum fan speed. If the limit is exceeded, an interrupt +can be generated. Each fan can be programmed to divide the reference clock +by 1, 2, 4 or 8. Not all RPM values can accurately be represented, so some +rounding is done. With a divider of 8, the slowest measurable speed of a +two pulse per revolution fan is 661 RPM. + +There are 17 voltage sensors. An alarm is triggered if the voltage has +crossed a programmable minimum or maximum limit. Note that minimum in this +case always means 'closest to zero'; this is important for negative voltage +measurements. Several inputs have integrated attenuators so they can measure +higher voltages directly. 3.3V, 5V, 12V, -12V and battery voltage all have +dedicated inputs. There are several inputs scaled to 0-3V full-scale range +for SCSI terminator power. The remaining inputs are not scaled and have +a 0-2.5V full-scale range. A 2.5V or 1.82V reference voltage is provided +for negative voltage measurements. + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once. This means that the cause for the alarm may already +have disappeared! Note that in the current implementation, all hardware +registers are read whenever any data is read (unless it is less than 2.0 +seconds since the last update). This means that you can easily miss +once-only alarms. + +The ADM1026 measures continuously. Analog inputs are measured about 4 +times a second. Fan speed measurement time depends on fan speed and +divisor. It can take as long as 1.5 seconds to measure all fan speeds. + +The ADM1026 has the ability to automatically control fan speed based on the +temperature sensor inputs. Both the PWM output and the DAC output can be +used to control fan speed. Usually only one of these two outputs will be +used. Write the minimum PWM or DAC value to the appropriate control +register. Then set the low temperature limit in the tmin values for each +temperature sensor. The range of control is fixed at 20 °C, and the +largest difference between current and tmin of the temperature sensors sets +the control output. See the datasheet for several example circuits for +controlling fan speed with the PWM and DAC outputs. The fan speed sensors +do not have PWM compensation, so it is probably best to control the fan +voltage from the power lead rather than on the ground lead. + +The datasheet shows an example application with VID signals attached to +GPIO lines. Unfortunately, the chip may not be connected to the VID lines +in this way. The driver assumes that the chips *is* connected this way to +get a VID voltage. diff --git a/Documentation/hwmon/adm1031 b/Documentation/hwmon/adm1031 new file mode 100644 index 0000000..130a383 --- /dev/null +++ b/Documentation/hwmon/adm1031 @@ -0,0 +1,35 @@ +Kernel driver adm1031 +===================== + +Supported chips: + * Analog Devices ADM1030 + Prefix: 'adm1030' + Addresses scanned: I2C 0x2c to 0x2e + Datasheet: Publicly available at the Analog Devices website + http://products.analog.com/products/info.asp?product=ADM1030 + + * Analog Devices ADM1031 + Prefix: 'adm1031' + Addresses scanned: I2C 0x2c to 0x2e + Datasheet: Publicly available at the Analog Devices website + http://products.analog.com/products/info.asp?product=ADM1031 + +Authors: + Alexandre d'Alton + Jean Delvare + +Description +----------- + +The ADM1030 and ADM1031 are digital temperature sensors and fan controllers. +They sense their own temperature as well as the temperature of up to one +(ADM1030) or two (ADM1031) external diodes. + +All temperature values are given in degrees Celsius. Resolution is 0.5 +degree for the local temperature, 0.125 degree for the remote temperatures. + +Each temperature channel has its own high and low limits, plus a critical +limit. + +The ADM1030 monitors a single fan speed, while the ADM1031 monitors up to +two. Each fan channel has its own low speed limit. diff --git a/Documentation/hwmon/adm9240 b/Documentation/hwmon/adm9240 new file mode 100644 index 0000000..35f618f --- /dev/null +++ b/Documentation/hwmon/adm9240 @@ -0,0 +1,177 @@ +Kernel driver adm9240 +===================== + +Supported chips: + * Analog Devices ADM9240 + Prefix: 'adm9240' + Addresses scanned: I2C 0x2c - 0x2f + Datasheet: Publicly available at the Analog Devices website + http://www.analog.com/UploadedFiles/Data_Sheets/79857778ADM9240_0.pdf + + * Dallas Semiconductor DS1780 + Prefix: 'ds1780' + Addresses scanned: I2C 0x2c - 0x2f + Datasheet: Publicly available at the Dallas Semiconductor (Maxim) website + http://pdfserv.maxim-ic.com/en/ds/DS1780.pdf + + * National Semiconductor LM81 + Prefix: 'lm81' + Addresses scanned: I2C 0x2c - 0x2f + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/ds.cgi/LM/LM81.pdf + +Authors: + Frodo Looijaard , + Philip Edelbrock , + Michiel Rook , + Grant Coady with guidance + from Jean Delvare + +Interface +--------- +The I2C addresses listed above assume BIOS has not changed the +chip MSB 5-bit address. Each chip reports a unique manufacturer +identification code as well as the chip revision/stepping level. + +Description +----------- +[From ADM9240] The ADM9240 is a complete system hardware monitor for +microprocessor-based systems, providing measurement and limit comparison +of up to four power supplies and two processor core voltages, plus +temperature, two fan speeds and chassis intrusion. Measured values can +be read out via an I2C-compatible serial System Management Bus, and values +for limit comparisons can be programmed in over the same serial bus. The +high speed successive approximation ADC allows frequent sampling of all +analog channels to ensure a fast interrupt response to any out-of-limit +measurement. + +The ADM9240, DS1780 and LM81 are register compatible, the following +details are common to the three chips. Chip differences are described +after this section. + + +Measurements +------------ +The measurement cycle + +The adm9240 driver will take a measurement reading no faster than once +each two seconds. User-space may read sysfs interface faster than the +measurement update rate and will receive cached data from the most +recent measurement. + +ADM9240 has a very fast 320us temperature and voltage measurement cycle +with independent fan speed measurement cycles counting alternating rising +edges of the fan tacho inputs. + +DS1780 measurement cycle is about once per second including fan speed. + +LM81 measurement cycle is about once per 400ms including fan speed. +The LM81 12-bit extended temperature measurement mode is not supported. + +Temperature +----------- +On chip temperature is reported as degrees Celsius as 9-bit signed data +with resolution of 0.5 degrees Celsius. High and low temperature limits +are 8-bit signed data with resolution of one degree Celsius. + +Temperature alarm is asserted once the temperature exceeds the high limit, +and is cleared when the temperature falls below the temp1_max_hyst value. + +Fan Speed +--------- +Two fan tacho inputs are provided, the ADM9240 gates an internal 22.5kHz +clock via a divider to an 8-bit counter. Fan speed (rpm) is calculated by: + +rpm = (22500 * 60) / (count * divider) + +Automatic fan clock divider + + * User sets 0 to fan_min limit + - low speed alarm is disabled + - fan clock divider not changed + - auto fan clock adjuster enabled for valid fan speed reading + + * User sets fan_min limit too low + - low speed alarm is enabled + - fan clock divider set to max + - fan_min set to register value 254 which corresponds + to 664 rpm on adm9240 + - low speed alarm will be asserted if fan speed is + less than minimum measurable speed + - auto fan clock adjuster disabled + + * User sets reasonable fan speed + - low speed alarm is enabled + - fan clock divider set to suit fan_min + - auto fan clock adjuster enabled: adjusts fan_min + + * User sets unreasonably high low fan speed limit + - resolution of the low speed limit may be reduced + - alarm will be asserted + - auto fan clock adjuster enabled: adjusts fan_min + + * fan speed may be displayed as zero until the auto fan clock divider + adjuster brings fan speed clock divider back into chip measurement + range, this will occur within a few measurement cycles. + +Analog Output +------------- +An analog output provides a 0 to 1.25 volt signal intended for an external +fan speed amplifier circuit. The analog output is set to maximum value on +power up or reset. This doesn't do much on the test Intel SE440BX-2. + +Voltage Monitor + +Voltage (IN) measurement is internally scaled: + + nr label nominal maximum resolution + mV mV mV + 0 +2.5V 2500 3320 13.0 + 1 Vccp1 2700 3600 14.1 + 2 +3.3V 3300 4380 17.2 + 3 +5V 5000 6640 26.0 + 4 +12V 12000 15940 62.5 + 5 Vccp2 2700 3600 14.1 + +The reading is an unsigned 8-bit value, nominal voltage measurement is +represented by a reading of 192, being 3/4 of the measurement range. + +An alarm is asserted for any voltage going below or above the set limits. + +The driver reports and accepts voltage limits scaled to the above table. + +VID Monitor +----------- +The chip has five inputs to read the 5-bit VID and reports the mV value +based on detected CPU type. + +Chassis Intrusion +----------------- +An alarm is asserted when the CI pin goes active high. The ADM9240 +Datasheet has an example of an external temperature sensor driving +this pin. On an Intel SE440BX-2 the Chassis Intrusion header is +connected to a normally open switch. + +The ADM9240 provides an internal open drain on this line, and may output +a 20 ms active low pulse to reset an external Chassis Intrusion latch. + +Clear the CI latch by writing value 1 to the sysfs chassis_clear file. + +Alarm flags reported as 16-bit word + + bit label comment + --- ------------- -------------------------- + 0 +2.5 V_Error high or low limit exceeded + 1 VCCP_Error high or low limit exceeded + 2 +3.3 V_Error high or low limit exceeded + 3 +5 V_Error high or low limit exceeded + 4 Temp_Error temperature error + 6 FAN1_Error fan low limit exceeded + 7 FAN2_Error fan low limit exceeded + 8 +12 V_Error high or low limit exceeded + 9 VCCP2_Error high or low limit exceeded + 12 Chassis_Error CI pin went high + +Remaining bits are reserved and thus undefined. It is important to note +that alarm bits may be cleared on read, user-space may latch alarms and +provide the end-user with a method to clear alarm memory. diff --git a/Documentation/hwmon/asb100 b/Documentation/hwmon/asb100 new file mode 100644 index 0000000..ab7365e --- /dev/null +++ b/Documentation/hwmon/asb100 @@ -0,0 +1,72 @@ +Kernel driver asb100 +==================== + +Supported Chips: + * Asus ASB100 and ASB100-A "Bach" + Prefix: 'asb100' + Addresses scanned: I2C 0x2d + Datasheet: none released + +Author: Mark M. Hoffman + +Description +----------- + +This driver implements support for the Asus ASB100 and ASB100-A "Bach". +These are custom ASICs available only on Asus mainboards. Asus refuses to +supply a datasheet for these chips. Thanks go to many people who helped +investigate their hardware, including: + +Vitaly V. Bursov +Alexander van Kaam (author of MBM for Windows) +Bertrik Sikken + +The ASB100 implements seven voltage sensors, three fan rotation speed +sensors, four temperature sensors, VID lines and alarms. In addition to +these, the ASB100-A also implements a single PWM controller for fans 2 and +3 (i.e. one setting controls both.) If you have a plain ASB100, the PWM +controller will simply not work (or maybe it will for you... it doesn't for +me). + +Temperatures are measured and reported in degrees Celsius. + +Fan speeds are reported in RPM (rotations per minute). An alarm is +triggered if the rotation speed has dropped below a programmable limit. + +Voltage sensors (also known as IN sensors) report values in volts. + +The VID lines encode the core voltage value: the voltage level your +processor should work with. This is hardcoded by the mainboard and/or +processor itself. It is a value in volts. + +Alarms: (TODO question marks indicate may or may not work) + +0x0001 => in0 (?) +0x0002 => in1 (?) +0x0004 => in2 +0x0008 => in3 +0x0010 => temp1 (1) +0x0020 => temp2 +0x0040 => fan1 +0x0080 => fan2 +0x0100 => in4 +0x0200 => in5 (?) (2) +0x0400 => in6 (?) (2) +0x0800 => fan3 +0x1000 => chassis switch +0x2000 => temp3 + +Alarm Notes: + +(1) This alarm will only trigger if the hysteresis value is 127C. +I.e. it behaves the same as w83781d. + +(2) The min and max registers for these values appear to +be read-only or otherwise stuck at 0x00. + +TODO: +* Experiment with fan divisors > 8. +* Experiment with temp. sensor types. +* Are there really 13 voltage inputs? Probably not... +* Cleanups, no doubt... + diff --git a/Documentation/hwmon/ds1621 b/Documentation/hwmon/ds1621 new file mode 100644 index 0000000..1fee6f1 --- /dev/null +++ b/Documentation/hwmon/ds1621 @@ -0,0 +1,108 @@ +Kernel driver ds1621 +==================== + +Supported chips: + * Dallas Semiconductor DS1621 + Prefix: 'ds1621' + Addresses scanned: I2C 0x48 - 0x4f + Datasheet: Publicly available at the Dallas Semiconductor website + http://www.dalsemi.com/ + * Dallas Semiconductor DS1625 + Prefix: 'ds1621' + Addresses scanned: I2C 0x48 - 0x4f + Datasheet: Publicly available at the Dallas Semiconductor website + http://www.dalsemi.com/ + +Authors: + Christian W. Zuckschwerdt + valuable contributions by Jan M. Sendler + ported to 2.6 by Aurelien Jarno + with the help of Jean Delvare + +Module Parameters +------------------ + +* polarity int + Output's polarity: 0 = active high, 1 = active low + +Description +----------- + +The DS1621 is a (one instance) digital thermometer and thermostat. It has +both high and low temperature limits which can be user defined (i.e. +programmed into non-volatile on-chip registers). Temperature range is -55 +degree Celsius to +125 in 0.5 increments. You may convert this into a +Fahrenheit range of -67 to +257 degrees with 0.9 steps. If polarity +parameter is not provided, original value is used. + +As for the thermostat, behavior can also be programmed using the polarity +toggle. On the one hand ("heater"), the thermostat output of the chip, +Tout, will trigger when the low limit temperature is met or underrun and +stays high until the high limit is met or exceeded. On the other hand +("cooler"), vice versa. That way "heater" equals "active low", whereas +"conditioner" equals "active high". Please note that the DS1621 data sheet +is somewhat misleading in this point since setting the polarity bit does +not simply invert Tout. + +A second thing is that, during extensive testing, Tout showed a tolerance +of up to +/- 0.5 degrees even when compared against precise temperature +readings. Be sure to have a high vs. low temperature limit gap of al least +1.0 degree Celsius to avoid Tout "bouncing", though! + +As for alarms, you can read the alarm status of the DS1621 via the 'alarms' +/sys file interface. The result consists mainly of bit 6 and 5 of the +configuration register of the chip; bit 6 (0x40 or 64) is the high alarm +bit and bit 5 (0x20 or 32) the low one. These bits are set when the high or +low limits are met or exceeded and are reset by the module as soon as the +respective temperature ranges are left. + +The alarm registers are in no way suitable to find out about the actual +status of Tout. They will only tell you about its history, whether or not +any of the limits have ever been met or exceeded since last power-up or +reset. Be aware: When testing, it showed that the status of Tout can change +with neither of the alarms set. + +Temperature conversion of the DS1621 takes up to 1000ms; internal access to +non-volatile registers may last for 10ms or below. + +High Accuracy Temperature Reading +--------------------------------- + +As said before, the temperature issued via the 9-bit i2c-bus data is +somewhat arbitrary. Internally, the temperature conversion is of a +different kind that is explained (not so...) well in the DS1621 data sheet. +To cut the long story short: Inside the DS1621 there are two oscillators, +both of them biassed by a temperature coefficient. + +Higher resolution of the temperature reading can be achieved using the +internal projection, which means taking account of REG_COUNT and REG_SLOPE +(the driver manages them): + +Taken from Dallas Semiconductors App Note 068: 'Increasing Temperature +Resolution on the DS1620' and App Note 105: 'High Resolution Temperature +Measurement with Dallas Direct-to-Digital Temperature Sensors' + +- Read the 9-bit temperature and strip the LSB (Truncate the .5 degs) +- The resulting value is TEMP_READ. +- Then, read REG_COUNT. +- And then, REG_SLOPE. + + TEMP = TEMP_READ - 0.25 + ((REG_SLOPE - REG_COUNT) / REG_SLOPE) + +Note that this is what the DONE bit in the DS1621 configuration register is +good for: Internally, one temperature conversion takes up to 1000ms. Before +that conversion is complete you will not be able to read valid things out +of REG_COUNT and REG_SLOPE. The DONE bit, as you may have guessed by now, +tells you whether the conversion is complete ("done", in plain English) and +thus, whether the values you read are good or not. + +The DS1621 has two modes of operation: "Continuous" conversion, which can +be understood as the default stand-alone mode where the chip gets the +temperature and controls external devices via its Tout pin or tells other +i2c's about it if they care. The other mode is called "1SHOT", that means +that it only figures out about the temperature when it is explicitly told +to do so; this can be seen as power saving mode. + +Now if you want to read REG_COUNT and REG_SLOPE, you have to either stop +the continuous conversions until the contents of these registers are valid, +or, in 1SHOT mode, you have to have one conversion made. diff --git a/Documentation/hwmon/fscher b/Documentation/hwmon/fscher new file mode 100644 index 0000000..6403165 --- /dev/null +++ b/Documentation/hwmon/fscher @@ -0,0 +1,169 @@ +Kernel driver fscher +==================== + +Supported chips: + * Fujitsu-Siemens Hermes chip + Prefix: 'fscher' + Addresses scanned: I2C 0x73 + +Authors: + Reinhard Nissl based on work + from Hermann Jung , + Frodo Looijaard , + Philip Edelbrock + +Description +----------- + +This driver implements support for the Fujitsu-Siemens Hermes chip. It is +described in the 'Register Set Specification BMC Hermes based Systemboard' +from Fujitsu-Siemens. + +The Hermes chip implements a hardware-based system management, e.g. for +controlling fan speed and core voltage. There is also a watchdog counter on +the chip which can trigger an alarm and even shut the system down. + +The chip provides three temperature values (CPU, motherboard and +auxiliary), three voltage values (+12V, +5V and battery) and three fans +(power supply, CPU and auxiliary). + +Temperatures are measured in degrees Celsius. The resolution is 1 degree. + +Fan rotation speeds are reported in RPM (rotations per minute). The value +can be divided by a programmable divider (1, 2 or 4) which is stored on +the chip. + +Voltage sensors (also known as "in" sensors) report their values in volts. + +All values are reported as final values from the driver. There is no need +for further calculations. + + +Detailed description +-------------------- + +Below you'll find a single line description of all the bit values. With +this information, you're able to decode e. g. alarms, wdog, etc. To make +use of the watchdog, you'll need to set the watchdog time and enable the +watchdog. After that it is necessary to restart the watchdog time within +the specified period of time, or a system reset will occur. + +* revision + READING & 0xff = 0x??: HERMES revision identification + +* alarms + READING & 0x80 = 0x80: CPU throttling active + READING & 0x80 = 0x00: CPU running at full speed + + READING & 0x10 = 0x10: software event (see control:1) + READING & 0x10 = 0x00: no software event + + READING & 0x08 = 0x08: watchdog event (see wdog:2) + READING & 0x08 = 0x00: no watchdog event + + READING & 0x02 = 0x02: thermal event (see temp*:1) + READING & 0x02 = 0x00: no thermal event + + READING & 0x01 = 0x01: fan event (see fan*:1) + READING & 0x01 = 0x00: no fan event + + READING & 0x13 ! 0x00: ALERT LED is flashing + +* control + READING & 0x01 = 0x01: software event + READING & 0x01 = 0x00: no software event + + WRITING & 0x01 = 0x01: set software event + WRITING & 0x01 = 0x00: clear software event + +* watchdog_control + READING & 0x80 = 0x80: power off on watchdog event while thermal event + READING & 0x80 = 0x00: watchdog power off disabled (just system reset enabled) + + READING & 0x40 = 0x40: watchdog timebase 60 seconds (see also wdog:1) + READING & 0x40 = 0x00: watchdog timebase 2 seconds + + READING & 0x10 = 0x10: watchdog enabled + READING & 0x10 = 0x00: watchdog disabled + + WRITING & 0x80 = 0x80: enable "power off on watchdog event while thermal event" + WRITING & 0x80 = 0x00: disable "power off on watchdog event while thermal event" + + WRITING & 0x40 = 0x40: set watchdog timebase to 60 seconds + WRITING & 0x40 = 0x00: set watchdog timebase to 2 seconds + + WRITING & 0x20 = 0x20: disable watchdog + + WRITING & 0x10 = 0x10: enable watchdog / restart watchdog time + +* watchdog_state + READING & 0x02 = 0x02: watchdog system reset occurred + READING & 0x02 = 0x00: no watchdog system reset occurred + + WRITING & 0x02 = 0x02: clear watchdog event + +* watchdog_preset + READING & 0xff = 0x??: configured watch dog time in units (see wdog:3 0x40) + + WRITING & 0xff = 0x??: configure watch dog time in units + +* in* (0: +5V, 1: +12V, 2: onboard 3V battery) + READING: actual voltage value + +* temp*_status (1: CPU sensor, 2: onboard sensor, 3: auxiliary sensor) + READING & 0x02 = 0x02: thermal event (overtemperature) + READING & 0x02 = 0x00: no thermal event + + READING & 0x01 = 0x01: sensor is working + READING & 0x01 = 0x00: sensor is faulty + + WRITING & 0x02 = 0x02: clear thermal event + +* temp*_input (1: CPU sensor, 2: onboard sensor, 3: auxiliary sensor) + READING: actual temperature value + +* fan*_status (1: power supply fan, 2: CPU fan, 3: auxiliary fan) + READING & 0x04 = 0x04: fan event (fan fault) + READING & 0x04 = 0x00: no fan event + + WRITING & 0x04 = 0x04: clear fan event + +* fan*_div (1: power supply fan, 2: CPU fan, 3: auxiliary fan) + Divisors 2,4 and 8 are supported, both for reading and writing + +* fan*_pwm (1: power supply fan, 2: CPU fan, 3: auxiliary fan) + READING & 0xff = 0x00: fan may be switched off + READING & 0xff = 0x01: fan must run at least at minimum speed (supply: 6V) + READING & 0xff = 0xff: fan must run at maximum speed (supply: 12V) + READING & 0xff = 0x??: fan must run at least at given speed (supply: 6V..12V) + + WRITING & 0xff = 0x00: fan may be switched off + WRITING & 0xff = 0x01: fan must run at least at minimum speed (supply: 6V) + WRITING & 0xff = 0xff: fan must run at maximum speed (supply: 12V) + WRITING & 0xff = 0x??: fan must run at least at given speed (supply: 6V..12V) + +* fan*_input (1: power supply fan, 2: CPU fan, 3: auxiliary fan) + READING: actual RPM value + + +Limitations +----------- + +* Measuring fan speed +It seems that the chip counts "ripples" (typical fans produce 2 ripples per +rotation while VERAX fans produce 18) in a 9-bit register. This register is +read out every second, then the ripple prescaler (2, 4 or 8) is applied and +the result is stored in the 8 bit output register. Due to the limitation of +the counting register to 9 bits, it is impossible to measure a VERAX fan +properly (even with a prescaler of 8). At its maximum speed of 3500 RPM the +fan produces 1080 ripples per second which causes the counting register to +overflow twice, leading to only 186 RPM. + +* Measuring input voltages +in2 ("battery") reports the voltage of the onboard lithium battery and not ++3.3V from the power supply. + +* Undocumented features +Fujitsu-Siemens Computers has not documented all features of the chip so +far. Their software, System Guard, shows that there are a still some +features which cannot be controlled by this implementation. diff --git a/Documentation/hwmon/gl518sm b/Documentation/hwmon/gl518sm new file mode 100644 index 0000000..ce08818 --- /dev/null +++ b/Documentation/hwmon/gl518sm @@ -0,0 +1,74 @@ +Kernel driver gl518sm +===================== + +Supported chips: + * Genesys Logic GL518SM release 0x00 + Prefix: 'gl518sm' + Addresses scanned: I2C 0x2c and 0x2d + Datasheet: http://www.genesyslogic.com/pdf + * Genesys Logic GL518SM release 0x80 + Prefix: 'gl518sm' + Addresses scanned: I2C 0x2c and 0x2d + Datasheet: http://www.genesyslogic.com/pdf + +Authors: + Frodo Looijaard , + Kyösti Mälkki + Hong-Gunn Chew + Jean Delvare + +Description +----------- + +IMPORTANT: + +For the revision 0x00 chip, the in0, in1, and in2 values (+5V, +3V, +and +12V) CANNOT be read. This is a limitation of the chip, not the driver. + +This driver supports the Genesys Logic GL518SM chip. There are at least +two revision of this chip, which we call revision 0x00 and 0x80. Revision +0x80 chips support the reading of all voltages and revision 0x00 only +for VIN3. + +The GL518SM implements one temperature sensor, two fan rotation speed +sensors, and four voltage sensors. It can report alarms through the +computer speakers. + +Temperatures are measured in degrees Celsius. An alarm goes off while the +temperature is above the over temperature limit, and has not yet dropped +below the hysteresis limit. The alarm always reflects the current +situation. Measurements are guaranteed between -10 degrees and +110 +degrees, with a accuracy of +/-3 degrees. + +Rotation speeds are reported in RPM (rotations per minute). An alarm is +triggered if the rotation speed has dropped below a programmable limit. In +case when you have selected to turn fan1 off, no fan1 alarm is triggered. + +Fan readings can be divided by a programmable divider (1, 2, 4 or 8) to +give the readings more range or accuracy. Not all RPM values can +accurately be represented, so some rounding is done. With a divider +of 2, the lowest representable value is around 1900 RPM. + +Voltage sensors (also known as VIN sensors) report their values in volts. +An alarm is triggered if the voltage has crossed a programmable minimum or +maximum limit. Note that minimum in this case always means 'closest to +zero'; this is important for negative voltage measurements. The VDD input +measures voltages between 0.000 and 5.865 volt, with a resolution of 0.023 +volt. The other inputs measure voltages between 0.000 and 4.845 volt, with +a resolution of 0.019 volt. Note that revision 0x00 chips do not support +reading the current voltage of any input except for VIN3; limit setting and +alarms work fine, though. + +When an alarm is triggered, you can be warned by a beeping signal through your +computer speaker. It is possible to enable all beeping globally, or only the +beeping for some alarms. + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once (except for temperature alarms). This means that the +cause for the alarm may already have disappeared! Note that in the current +implementation, all hardware registers are read whenever any data is read +(unless it is less than 1.5 seconds since the last update). This means that +you can easily miss once-only alarms. + +The GL518SM only updates its values each 1.5 seconds; reading it more often +will do no harm, but will return 'old' values. diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 new file mode 100644 index 0000000..0d01950 --- /dev/null +++ b/Documentation/hwmon/it87 @@ -0,0 +1,96 @@ +Kernel driver it87 +================== + +Supported chips: + * IT8705F + Prefix: 'it87' + Addresses scanned: from Super I/O config space, or default ISA 0x290 (8 I/O ports) + Datasheet: Publicly available at the ITE website + http://www.ite.com.tw/ + * IT8712F + Prefix: 'it8712' + Addresses scanned: I2C 0x28 - 0x2f + from Super I/O config space, or default ISA 0x290 (8 I/O ports) + Datasheet: Publicly available at the ITE website + http://www.ite.com.tw/ + * SiS950 [clone of IT8705F] + Prefix: 'sis950' + Addresses scanned: from Super I/O config space, or default ISA 0x290 (8 I/O ports) + Datasheet: No longer be available + +Author: Christophe Gauthron + + +Module Parameters +----------------- + +* update_vbat: int + + 0 if vbat should report power on value, 1 if vbat should be updated after + each read. Default is 0. On some boards the battery voltage is provided + by either the battery or the onboard power supply. Only the first reading + at power on will be the actual battery voltage (which the chip does + automatically). On other boards the battery voltage is always fed to + the chip so can be read at any time. Excessive reading may decrease + battery life but no information is given in the datasheet. + +* fix_pwm_polarity int + + Force PWM polarity to active high (DANGEROUS). Some chips are + misconfigured by BIOS - PWM values would be inverted. This option tries + to fix this. Please contact your BIOS manufacturer and ask him for fix. + +Description +----------- + +This driver implements support for the IT8705F, IT8712F and SiS950 chips. + +This driver also supports IT8712F, which adds SMBus access, and a VID +input, used to report the Vcore voltage of the Pentium processor. +The IT8712F additionally features VID inputs. + +These chips are 'Super I/O chips', supporting floppy disks, infrared ports, +joysticks and other miscellaneous stuff. For hardware monitoring, they +include an 'environment controller' with 3 temperature sensors, 3 fan +rotation speed sensors, 8 voltage sensors, and associated alarms. + +Temperatures are measured in degrees Celsius. An alarm is triggered once +when the Overtemperature Shutdown limit is crossed. + +Fan rotation speeds are reported in RPM (rotations per minute). An alarm is +triggered if the rotation speed has dropped below a programmable limit. Fan +readings can be divided by a programmable divider (1, 2, 4 or 8) to give the +readings more range or accuracy. Not all RPM values can accurately be +represented, so some rounding is done. With a divider of 2, the lowest +representable value is around 2600 RPM. + +Voltage sensors (also known as IN sensors) report their values in volts. An +alarm is triggered if the voltage has crossed a programmable minimum or +maximum limit. Note that minimum in this case always means 'closest to +zero'; this is important for negative voltage measurements. All voltage +inputs can measure voltages between 0 and 4.08 volts, with a resolution of +0.016 volt. The battery voltage in8 does not have limit registers. + +The VID lines (IT8712F only) encode the core voltage value: the voltage +level your processor should work with. This is hardcoded by the mainboard +and/or processor itself. It is a value in volts. + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once. This means that the cause for the alarm may already +have disappeared! Note that in the current implementation, all hardware +registers are read whenever any data is read (unless it is less than 1.5 +seconds since the last update). This means that you can easily miss +once-only alarms. + +The IT87xx only updates its values each 1.5 seconds; reading it more often +will do no harm, but will return 'old' values. + +To change sensor N to a thermistor, 'echo 2 > tempN_type' where N is 1, 2, +or 3. To change sensor N to a thermal diode, 'echo 3 > tempN_type'. +Give 0 for unused sensor. Any other value is invalid. To configure this at +startup, consult lm_sensors's /etc/sensors.conf. (2 = thermistor; +3 = thermal diode) + +The fan speed control features are limited to manual PWM mode. Automatic +"Smart Guardian" mode control handling is not implemented. However +if you want to go for "manual mode" just write 1 to pwmN_enable. diff --git a/Documentation/hwmon/lm63 b/Documentation/hwmon/lm63 new file mode 100644 index 0000000..31660bf --- /dev/null +++ b/Documentation/hwmon/lm63 @@ -0,0 +1,57 @@ +Kernel driver lm63 +================== + +Supported chips: + * National Semiconductor LM63 + Prefix: 'lm63' + Addresses scanned: I2C 0x4c + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/pf/LM/LM63.html + +Author: Jean Delvare + +Thanks go to Tyan and especially Alex Buckingham for setting up a remote +access to their S4882 test platform for this driver. + http://www.tyan.com/ + +Description +----------- + +The LM63 is a digital temperature sensor with integrated fan monitoring +and control. + +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. + +An explanation from National Semiconductor: The two lower bits of the read +value have to be masked out. The value is still 16 bit in width. + +All temperature values are given in degrees Celsius. Resolution is 1.0 +degree for the local temperature, 0.125 degree for the remote temperature. + +The fan speed is measured using a tachometer. Contrary to most chips which +store the value in an 8-bit register and have a selectable clock divider +to make sure that the result will fit in the register, the LM63 uses 16-bit +value for measuring the speed of the fan. It can measure fan speeds down to +83 RPM, at least in theory. + +Note that the pin used for fan monitoring is shared with an alert out +function. Depending on how the board designer wanted to use the chip, fan +speed monitoring will or will not be possible. The proper chip configuration +is left to the BIOS, and the driver will blindly trust it. + +A PWM output can be used to control the speed of the fan. The LM63 has two +PWM modes: manual and automatic. Automatic mode is not fully implemented yet +(you cannot define your custom PWM/temperature curve), and mode change isn't +supported either. + +The lm63 driver will not update its values more frequently than every +second; reading them more often will do no harm, but will return 'old' +values. + diff --git a/Documentation/hwmon/lm75 b/Documentation/hwmon/lm75 new file mode 100644 index 0000000..8e6356f --- /dev/null +++ b/Documentation/hwmon/lm75 @@ -0,0 +1,65 @@ +Kernel driver lm75 +================== + +Supported chips: + * National Semiconductor LM75 + Prefix: 'lm75' + Addresses scanned: I2C 0x48 - 0x4f + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/ + * Dallas Semiconductor DS75 + Prefix: 'lm75' + Addresses scanned: I2C 0x48 - 0x4f + Datasheet: Publicly available at the Dallas Semiconductor website + http://www.maxim-ic.com/ + * Dallas Semiconductor DS1775 + Prefix: 'lm75' + Addresses scanned: I2C 0x48 - 0x4f + Datasheet: Publicly available at the Dallas Semiconductor website + http://www.maxim-ic.com/ + * Maxim MAX6625, MAX6626 + Prefix: 'lm75' + Addresses scanned: I2C 0x48 - 0x4b + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/ + * Microchip (TelCom) TCN75 + Prefix: 'lm75' + Addresses scanned: I2C 0x48 - 0x4f + Datasheet: Publicly available at the Microchip website + http://www.microchip.com/ + +Author: Frodo Looijaard + +Description +----------- + +The LM75 implements one temperature sensor. Limits can be set through the +Overtemperature Shutdown register and Hysteresis register. Each value can be +set and read to half-degree accuracy. +An alarm is issued (usually to a connected LM78) when the temperature +gets higher then the Overtemperature Shutdown value; it stays on until +the temperature falls below the Hysteresis value. +All temperatures are in degrees Celsius, and are guaranteed within a +range of -55 to +125 degrees. + +The LM75 only updates its values each 1.5 seconds; reading it more often +will do no harm, but will return 'old' values. + +The LM75 is usually used in combination with LM78-like chips, to measure +the temperature of the processor(s). + +The DS75, DS1775, MAX6625, and MAX6626 are supported as well. +They are not distinguished from an LM75. While most of these chips +have three additional bits of accuracy (12 vs. 9 for the LM75), +the additional bits are not supported. Not only that, but these chips will +not be detected if not in 9-bit precision mode (use the force parameter if +needed). + +The TCN75 is supported as well, and is not distinguished from an LM75. + +The LM75 is essentially an industry standard; there may be other +LM75 clones not listed here, with or without various enhancements, +that are supported. + +The LM77 is not supported, contrary to what we pretended for a long time. +Both chips are simply not compatible, value encoding differs. diff --git a/Documentation/hwmon/lm77 b/Documentation/hwmon/lm77 new file mode 100644 index 0000000..57c3a46 --- /dev/null +++ b/Documentation/hwmon/lm77 @@ -0,0 +1,22 @@ +Kernel driver lm77 +================== + +Supported chips: + * National Semiconductor LM77 + Prefix: 'lm77' + Addresses scanned: I2C 0x48 - 0x4b + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/ + +Author: Andras BALI + +Description +----------- + +The LM77 implements one temperature sensor. The temperature +sensor incorporates a band-gap type temperature sensor, +10-bit ADC, and a digital comparator with user-programmable upper +and lower limit values. + +Limits can be set through the Overtemperature Shutdown register and +Hysteresis register. diff --git a/Documentation/hwmon/lm78 b/Documentation/hwmon/lm78 new file mode 100644 index 0000000..357086e --- /dev/null +++ b/Documentation/hwmon/lm78 @@ -0,0 +1,82 @@ +Kernel driver lm78 +================== + +Supported chips: + * National Semiconductor LM78 + Prefix: 'lm78' + Addresses scanned: I2C 0x20 - 0x2f, ISA 0x290 (8 I/O ports) + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/ + * National Semiconductor LM78-J + Prefix: 'lm78-j' + Addresses scanned: I2C 0x20 - 0x2f, ISA 0x290 (8 I/O ports) + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/ + * National Semiconductor LM79 + Prefix: 'lm79' + Addresses scanned: I2C 0x20 - 0x2f, ISA 0x290 (8 I/O ports) + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/ + +Author: Frodo Looijaard + +Description +----------- + +This driver implements support for the National Semiconductor LM78, LM78-J +and LM79. They are described as 'Microprocessor System Hardware Monitors'. + +There is almost no difference between the three supported chips. Functionally, +the LM78 and LM78-J are exactly identical. The LM79 has one more VID line, +which is used to report the lower voltages newer Pentium processors use. +From here on, LM7* means either of these three types. + +The LM7* implements one temperature sensor, three fan rotation speed sensors, +seven voltage sensors, VID lines, alarms, and some miscellaneous stuff. + +Temperatures are measured in degrees Celsius. An alarm is triggered once +when the Overtemperature Shutdown limit is crossed; it is triggered again +as soon as it drops below the Hysteresis value. A more useful behavior +can be found by setting the Hysteresis value to +127 degrees Celsius; in +this case, alarms are issued during all the time when the actual temperature +is above the Overtemperature Shutdown value. Measurements are guaranteed +between -55 and +125 degrees, with a resolution of 1 degree. + +Fan rotation speeds are reported in RPM (rotations per minute). An alarm is +triggered if the rotation speed has dropped below a programmable limit. Fan +readings can be divided by a programmable divider (1, 2, 4 or 8) to give +the readings more range or accuracy. Not all RPM values can accurately be +represented, so some rounding is done. With a divider of 2, the lowest +representable value is around 2600 RPM. + +Voltage sensors (also known as IN sensors) report their values in volts. +An alarm is triggered if the voltage has crossed a programmable minimum +or maximum limit. Note that minimum in this case always means 'closest to +zero'; this is important for negative voltage measurements. All voltage +inputs can measure voltages between 0 and 4.08 volts, with a resolution +of 0.016 volt. + +The VID lines encode the core voltage value: the voltage level your processor +should work with. This is hardcoded by the mainboard and/or processor itself. +It is a value in volts. When it is unconnected, you will often find the +value 3.50 V here. + +In addition to the alarms described above, there are a couple of additional +ones. There is a BTI alarm, which gets triggered when an external chip has +crossed its limits. Usually, this is connected to all LM75 chips; if at +least one crosses its limits, this bit gets set. The CHAS alarm triggers +if your computer case is open. The FIFO alarms should never trigger; it +indicates an internal error. The SMI_IN alarm indicates some other chip +has triggered an SMI interrupt. As we do not use SMI interrupts at all, +this condition usually indicates there is a problem with some other +device. + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once. This means that the cause for the alarm may +already have disappeared! Note that in the current implementation, all +hardware registers are read whenever any data is read (unless it is less +than 1.5 seconds since the last update). This means that you can easily +miss once-only alarms. + +The LM7* only updates its values each 1.5 seconds; reading it more often +will do no harm, but will return 'old' values. diff --git a/Documentation/hwmon/lm80 b/Documentation/hwmon/lm80 new file mode 100644 index 0000000..cb5b407 --- /dev/null +++ b/Documentation/hwmon/lm80 @@ -0,0 +1,56 @@ +Kernel driver lm80 +================== + +Supported chips: + * National Semiconductor LM80 + Prefix: 'lm80' + Addresses scanned: I2C 0x28 - 0x2f + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/ + +Authors: + Frodo Looijaard , + Philip Edelbrock + +Description +----------- + +This driver implements support for the National Semiconductor LM80. +It is described as a 'Serial Interface ACPI-Compatible Microprocessor +System Hardware Monitor'. + +The LM80 implements one temperature sensor, two fan rotation speed sensors, +seven voltage sensors, alarms, and some miscellaneous stuff. + +Temperatures are measured in degrees Celsius. There are two sets of limits +which operate independently. When the HOT Temperature Limit is crossed, +this will cause an alarm that will be reasserted until the temperature +drops below the HOT Hysteresis. The Overtemperature Shutdown (OS) limits +should work in the same way (but this must be checked; the datasheet +is unclear about this). Measurements are guaranteed between -55 and ++125 degrees. The current temperature measurement has a resolution of +0.0625 degrees; the limits have a resolution of 1 degree. + +Fan rotation speeds are reported in RPM (rotations per minute). An alarm is +triggered if the rotation speed has dropped below a programmable limit. Fan +readings can be divided by a programmable divider (1, 2, 4 or 8) to give +the readings more range or accuracy. Not all RPM values can accurately be +represented, so some rounding is done. With a divider of 2, the lowest +representable value is around 2600 RPM. + +Voltage sensors (also known as IN sensors) report their values in volts. +An alarm is triggered if the voltage has crossed a programmable minimum +or maximum limit. Note that minimum in this case always means 'closest to +zero'; this is important for negative voltage measurements. All voltage +inputs can measure voltages between 0 and 2.55 volts, with a resolution +of 0.01 volt. + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once. This means that the cause for the alarm may +already have disappeared! Note that in the current implementation, all +hardware registers are read whenever any data is read (unless it is less +than 2.0 seconds since the last update). This means that you can easily +miss once-only alarms. + +The LM80 only updates its values each 1.5 seconds; reading it more often +will do no harm, but will return 'old' values. diff --git a/Documentation/hwmon/lm83 b/Documentation/hwmon/lm83 new file mode 100644 index 0000000..061d9ed --- /dev/null +++ b/Documentation/hwmon/lm83 @@ -0,0 +1,76 @@ +Kernel driver lm83 +================== + +Supported chips: + * National Semiconductor LM83 + Prefix: 'lm83' + Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/pf/LM/LM83.html + + +Author: Jean Delvare + +Description +----------- + +The LM83 is a digital temperature sensor. It senses its own temperature as +well as the temperature of up to three external diodes. It is compatible +with many other devices such as the LM84 and all other ADM1021 clones. +The main difference between the LM83 and the LM84 in that the later can +only sense the temperature of one external diode. + +Using the adm1021 driver for a LM83 should work, but only two temperatures +will be reported instead of four. + +The LM83 is only found on a handful of motherboards. Both a confirmed +list and an unconfirmed list follow. If you can confirm or infirm the +fact that any of these motherboards do actually have an LM83, please +contact us. Note that the LM90 can easily be misdetected as a LM83. + +Confirmed motherboards: + SBS P014 + +Unconfirmed motherboards: + Gigabyte GA-8IK1100 + Iwill MPX2 + Soltek SL-75DRV5 + +The driver has been successfully tested by Magnus Forsström, who I'd +like to thank here. More testers will be of course welcome. + +The fact that the LM83 is only scarcely used can be easily explained. +Most motherboards come with more than just temperature sensors for +health monitoring. They also have voltage and fan rotation speed +sensors. This means that temperature-only chips are usually used as +secondary chips coupled with another chip such as an IT8705F or similar +chip, which provides more features. Since systems usually need three +temperature sensors (motherboard, processor, power supply) and primary +chips provide some temperature sensors, the secondary chip, if needed, +won't have to handle more than two temperatures. Thus, ADM1021 clones +are sufficient, and there is no need for a four temperatures sensor +chip such as the LM83. The only case where using an LM83 would make +sense is on SMP systems, such as the above-mentioned Iwill MPX2, +because you want an additional temperature sensor for each additional +CPU. + +On the SBS P014, this is different, since the LM83 is the only hardware +monitoring chipset. One temperature sensor is used for the motherboard +(actually measuring the LM83's own temperature), one is used for the +CPU. The two other sensors must be used to measure the temperature of +two other points of the motherboard. We suspect these points to be the +north and south bridges, but this couldn't be confirmed. + +All temperature values are given in degrees Celsius. Local temperature +is given within a range of 0 to +85 degrees. Remote temperatures are +given within a range of 0 to +125 degrees. Resolution is 1.0 degree, +accuracy is guaranteed to 3.0 degrees (see the datasheet for more +details). + +Each sensor has its own high limit, but the critical limit is common to +all four sensors. There is no hysteresis mechanism as found on most +recent temperature sensors. + +The lm83 driver will not update its values more frequently than every +other second; reading them more often will do no harm, but will return +'old' values. diff --git a/Documentation/hwmon/lm85 b/Documentation/hwmon/lm85 new file mode 100644 index 0000000..9549237 --- /dev/null +++ b/Documentation/hwmon/lm85 @@ -0,0 +1,221 @@ +Kernel driver lm85 +================== + +Supported chips: + * National Semiconductor LM85 (B and C versions) + Prefix: 'lm85' + Addresses scanned: I2C 0x2c, 0x2d, 0x2e + Datasheet: http://www.national.com/pf/LM/LM85.html + * Analog Devices ADM1027 + Prefix: 'adm1027' + Addresses scanned: I2C 0x2c, 0x2d, 0x2e + Datasheet: http://www.analog.com/en/prod/0,,766_825_ADM1027,00.html + * Analog Devices ADT7463 + Prefix: 'adt7463' + Addresses scanned: I2C 0x2c, 0x2d, 0x2e + Datasheet: http://www.analog.com/en/prod/0,,766_825_ADT7463,00.html + * SMSC EMC6D100, SMSC EMC6D101 + Prefix: 'emc6d100' + Addresses scanned: I2C 0x2c, 0x2d, 0x2e + Datasheet: http://www.smsc.com/main/tools/discontinued/6d100.pdf + * SMSC EMC6D102 + Prefix: 'emc6d102' + Addresses scanned: I2C 0x2c, 0x2d, 0x2e + Datasheet: http://www.smsc.com/main/catalog/emc6d102.html + +Authors: + Philip Pokorny , + Frodo Looijaard , + Richard Barrington , + Margit Schubert-While , + Justin Thiessen + +Description +----------- + +This driver implements support for the National Semiconductor LM85 and +compatible chips including the Analog Devices ADM1027, ADT7463 and +SMSC EMC6D10x chips family. + +The LM85 uses the 2-wire interface compatible with the SMBUS 2.0 +specification. Using an analog to digital converter it measures three (3) +temperatures and five (5) voltages. It has four (4) 16-bit counters for +measuring fan speed. Five (5) digital inputs are provided for sampling the +VID signals from the processor to the VRM. Lastly, there are three (3) PWM +outputs that can be used to control fan speed. + +The voltage inputs have internal scaling resistors so that the following +voltage can be measured without external resistors: + + 2.5V, 3.3V, 5V, 12V, and CPU core voltage (2.25V) + +The temperatures measured are one internal diode, and two remote diodes. +Remote 1 is generally the CPU temperature. These inputs are designed to +measure a thermal diode like the one in a Pentium 4 processor in a socket +423 or socket 478 package. They can also measure temperature using a +transistor like the 2N3904. + +A sophisticated control system for the PWM outputs is designed into the +LM85 that allows fan speed to be adjusted automatically based on any of the +three temperature sensors. Each PWM output is individually adjustable and +programmable. Once configured, the LM85 will adjust the PWM outputs in +response to the measured temperatures without further host intervention. +This feature can also be disabled for manual control of the PWM's. + +Each of the measured inputs (voltage, temperature, fan speed) has +corresponding high/low limit values. The LM85 will signal an ALARM if any +measured value exceeds either limit. + +The LM85 samples all inputs continuously. The lm85 driver will not read +the registers more often than once a second. Further, configuration data is +only read once each 5 minutes. There is twice as much config data as +measurements, so this would seem to be a worthwhile optimization. + +Special Features +---------------- + +The LM85 has four fan speed monitoring modes. The ADM1027 has only two. +Both have special circuitry to compensate for PWM interactions with the +TACH signal from the fans. The ADM1027 can be configured to measure the +speed of a two wire fan, but the input conditioning circuitry is different +for 3-wire and 2-wire mode. For this reason, the 2-wire fan modes are not +exposed to user control. The BIOS should initialize them to the correct +mode. If you've designed your own ADM1027, you'll have to modify the +init_client function and add an insmod parameter to set this up. + +To smooth the response of fans to changes in temperature, the LM85 has an +optional filter for smoothing temperatures. The ADM1027 has the same +config option but uses it to rate limit the changes to fan speed instead. + +The ADM1027 and ADT7463 have a 10-bit ADC and can therefore measure +temperatures with 0.25 degC resolution. They also provide an offset to the +temperature readings that is automatically applied during measurement. +This offset can be used to zero out any errors due to traces and placement. +The documentation says that the offset is in 0.25 degC steps, but in +initial testing of the ADM1027 it was 1.00 degC steps. Analog Devices has +confirmed this "bug". The ADT7463 is reported to work as described in the +documentation. The current lm85 driver does not show the offset register. + +The ADT7463 has a THERM asserted counter. This counter has a 22.76ms +resolution and a range of 5.8 seconds. The driver implements a 32-bit +accumulator of the counter value to extend the range to over a year. The +counter will stay at it's max value until read. + +See the vendor datasheets for more information. There is application note +from National (AN-1260) with some additional information about the LM85. +The Analog Devices datasheet is very detailed and describes a procedure for +determining an optimal configuration for the automatic PWM control. + +The SMSC EMC6D100 & EMC6D101 monitor external voltages, temperatures, and +fan speeds. They use this monitoring capability to alert the system to out +of limit conditions and can automatically control the speeds of multiple +fans in a PC or embedded system. The EMC6D101, available in a 24-pin SSOP +package, and the EMC6D100, available in a 28-pin SSOP package, are designed +to be register compatible. The EMC6D100 offers all the features of the +EMC6D101 plus additional voltage monitoring and system control features. +Unfortunately it is not possible to distinguish between the package +versions on register level so these additional voltage inputs may read +zero. The EMC6D102 features addtional ADC bits thus extending precision +of voltage and temperature channels. + + +Hardware Configurations +----------------------- + +The LM85 can be jumpered for 3 different SMBus addresses. There are +no other hardware configuration options for the LM85. + +The lm85 driver detects both LM85B and LM85C revisions of the chip. See the +datasheet for a complete description of the differences. Other than +identifying the chip, the driver behaves no differently with regard to +these two chips. The LM85B is recommended for new designs. + +The ADM1027 and ADT7463 chips have an optional SMBALERT output that can be +used to signal the chipset in case a limit is exceeded or the temperature +sensors fail. Individual sensor interrupts can be masked so they won't +trigger SMBALERT. The SMBALERT output if configured replaces one of the other +functions (PWM2 or IN0). This functionality is not implemented in current +driver. + +The ADT7463 also has an optional THERM output/input which can be connected +to the processor PROC_HOT output. If available, the autofan control +dynamic Tmin feature can be enabled to keep the system temperature within +spec (just?!) with the least possible fan noise. + +Configuration Notes +------------------- + +Besides standard interfaces driver adds following: + +* Temperatures and Zones + +Each temperature sensor is associated with a Zone. There are three +sensors and therefore three zones (# 1, 2 and 3). Each zone has the following +temperature configuration points: + +* temp#_auto_temp_off - temperature below which fans should be off or spinning very low. +* temp#_auto_temp_min - temperature over which fans start to spin. +* temp#_auto_temp_max - temperature when fans spin at full speed. +* temp#_auto_temp_crit - temperature when all fans will run full speed. + +* PWM Control + +There are three PWM outputs. The LM85 datasheet suggests that the +pwm3 output control both fan3 and fan4. Each PWM can be individually +configured and assigned to a zone for it's control value. Each PWM can be +configured individually according to the following options. + +* pwm#_auto_pwm_min - this specifies the PWM value for temp#_auto_temp_off + temperature. (PWM value from 0 to 255) + +* pwm#_auto_pwm_freq - select base frequency of PWM output. You can select + in range of 10.0 to 94.0 Hz in .1 Hz units. + (Values 100 to 940). + +The pwm#_auto_pwm_freq can be set to one of the following 8 values. Setting the +frequency to a value not on this list, will result in the next higher frequency +being selected. The actual device frequency may vary slightly from this +specification as designed by the manufacturer. Consult the datasheet for more +details. (PWM Frequency values: 100, 150, 230, 300, 380, 470, 620, 940) + +* pwm#_auto_pwm_minctl - this flags selects for temp#_auto_temp_off temperature + the bahaviour of fans. Write 1 to let fans spinning at + pwm#_auto_pwm_min or write 0 to let them off. + +NOTE: It has been reported that there is a bug in the LM85 that causes the flag +to be associated with the zones not the PWMs. This contradicts all the +published documentation. Setting pwm#_min_ctl in this case actually affects all +PWMs controlled by zone '#'. + +* PWM Controlling Zone selection + +* pwm#_auto_channels - controls zone that is associated with PWM + +Configuration choices: + + Value Meaning + ------ ------------------------------------------------ + 1 Controlled by Zone 1 + 2 Controlled by Zone 2 + 3 Controlled by Zone 3 + 23 Controlled by higher temp of Zone 2 or 3 + 123 Controlled by highest temp of Zone 1, 2 or 3 + 0 PWM always 0% (off) + -1 PWM always 100% (full on) + -2 Manual control (write to 'pwm#' to set) + +The National LM85's have two vendor specific configuration +features. Tach. mode and Spinup Control. For more details on these, +see the LM85 datasheet or Application Note AN-1260. + +The Analog Devices ADM1027 has several vendor specific enhancements. +The number of pulses-per-rev of the fans can be set, Tach monitoring +can be optimized for PWM operation, and an offset can be applied to +the temperatures to compensate for systemic errors in the +measurements. + +In addition to the ADM1027 features, the ADT7463 also has Tmin control +and THERM asserted counts. Automatic Tmin control acts to adjust the +Tmin value to maintain the measured temperature sensor at a specified +temperature. There isn't much documentation on this feature in the +ADT7463 data sheet. This is not supported by current driver. diff --git a/Documentation/hwmon/lm87 b/Documentation/hwmon/lm87 new file mode 100644 index 0000000..c952c57 --- /dev/null +++ b/Documentation/hwmon/lm87 @@ -0,0 +1,73 @@ +Kernel driver lm87 +================== + +Supported chips: + * National Semiconductor LM87 + Prefix: 'lm87' + Addresses scanned: I2C 0x2c - 0x2f + Datasheet: http://www.national.com/pf/LM/LM87.html + +Authors: + Frodo Looijaard , + Philip Edelbrock , + Mark Studebaker , + Stephen Rousset , + Dan Eaton , + Jean Delvare , + Original 2.6 port Jeff Oliver + +Description +----------- + +This driver implements support for the National Semiconductor LM87. + +The LM87 implements up to three temperature sensors, up to two fan +rotation speed sensors, up to seven voltage sensors, alarms, and some +miscellaneous stuff. + +Temperatures are measured in degrees Celsius. Each input has a high +and low alarm settings. A high limit produces an alarm when the value +goes above it, and an alarm is also produced when the value goes below +the low limit. + +Fan rotation speeds are reported in RPM (rotations per minute). An alarm is +triggered if the rotation speed has dropped below a programmable limit. Fan +readings can be divided by a programmable divider (1, 2, 4 or 8) to give +the readings more range or accuracy. Not all RPM values can accurately be +represented, so some rounding is done. With a divider of 2, the lowest +representable value is around 2600 RPM. + +Voltage sensors (also known as IN sensors) report their values in +volts. An alarm is triggered if the voltage has crossed a programmable +minimum or maximum limit. Note that minimum in this case always means +'closest to zero'; this is important for negative voltage measurements. + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once. This means that the cause for the alarm may +already have disappeared! Note that in the current implementation, all +hardware registers are read whenever any data is read (unless it is less +than 1.0 seconds since the last update). This means that you can easily +miss once-only alarms. + +The lm87 driver only updates its values each 1.0 seconds; reading it more +often will do no harm, but will return 'old' values. + + +Hardware Configurations +----------------------- + +The LM87 has four pins which can serve one of two possible functions, +depending on the hardware configuration. + +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) diff --git a/Documentation/hwmon/lm90 b/Documentation/hwmon/lm90 new file mode 100644 index 0000000..2c4cf39 --- /dev/null +++ b/Documentation/hwmon/lm90 @@ -0,0 +1,121 @@ +Kernel driver lm90 +================== + +Supported chips: + * National Semiconductor LM90 + Prefix: 'lm90' + Addresses scanned: I2C 0x4c + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/pf/LM/LM90.html + * National Semiconductor LM89 + Prefix: 'lm99' + Addresses scanned: I2C 0x4c and 0x4d + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/pf/LM/LM89.html + * National Semiconductor LM99 + Prefix: 'lm99' + Addresses scanned: I2C 0x4c and 0x4d + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/pf/LM/LM99.html + * National Semiconductor LM86 + Prefix: 'lm86' + Addresses scanned: I2C 0x4c + Datasheet: Publicly available at the National Semiconductor website + http://www.national.com/pf/LM/LM86.html + * Analog Devices ADM1032 + Prefix: 'adm1032' + Addresses scanned: I2C 0x4c + Datasheet: Publicly available at the Analog Devices website + http://products.analog.com/products/info.asp?product=ADM1032 + * Analog Devices ADT7461 + Prefix: 'adt7461' + Addresses scanned: I2C 0x4c + Datasheet: Publicly available at the Analog Devices website + http://products.analog.com/products/info.asp?product=ADT7461 + Note: Only if in ADM1032 compatibility mode + * Maxim MAX6657 + Prefix: 'max6657' + Addresses scanned: I2C 0x4c + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 + * Maxim MAX6658 + Prefix: 'max6657' + Addresses scanned: I2C 0x4c + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 + * Maxim MAX6659 + Prefix: 'max6657' + Addresses scanned: I2C 0x4c, 0x4d (unsupported 0x4e) + Datasheet: Publicly available at the Maxim website + http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 + + +Author: Jean Delvare + + +Description +----------- + +The LM90 is a digital temperature sensor. It senses its own temperature as +well as the temperature of up to one external diode. It is compatible +with many other devices such as the LM86, the LM89, the LM99, the ADM1032, +the MAX6657, MAX6658 and the MAX6659 all of which are supported by this driver. +Note that there is no easy way to differentiate between the last three +variants. The extra address and features of the MAX6659 are not supported by +this driver. Additionally, the ADT7461 is supported if found in ADM1032 +compatibility mode. + +The specificity of this family of chipsets over the ADM1021/LM84 +family is that it features critical limits with hysteresis, and an +increased resolution of the remote temperature measurement. + +The different chipsets of the family are not strictly identical, although +very similar. This driver doesn't handle any specific feature for now, +but could if there ever was a need for it. For reference, here comes a +non-exhaustive list of specific features: + +LM90: + * Filter and alert configuration register at 0xBF. + * ALERT is triggered by temperatures over critical limits. + +LM86 and LM89: + * Same as LM90 + * Better external channel accuracy + +LM99: + * Same as LM89 + * External temperature shifted by 16 degrees down + +ADM1032: + * Consecutive alert register at 0x22. + * Conversion averaging. + * Up to 64 conversions/s. + * ALERT is triggered by open remote sensor. + +ADT7461 + * Extended temperature range (breaks compatibility) + * Lower resolution for remote temperature + +MAX6657 and MAX6658: + * Remote sensor type selection + +MAX6659 + * Selectable address + * Second critical temperature limit + * Remote sensor type selection + +All temperature values are given in degrees Celsius. Resolution +is 1.0 degree for the local temperature, 0.125 degree for the remote +temperature. + +Each sensor has its own high and low limits, plus a critical limit. +Additionally, there is a relative hysteresis value common to both critical +values. To make life easier to user-space applications, two absolute values +are exported, one for each channel, but these values are of course linked. +Only the local hysteresis can be set from user-space, and the same delta +applies to the remote hysteresis. + +The lm90 driver will not update its values more frequently than every +other second; reading them more often will do no harm, but will return +'old' values. + diff --git a/Documentation/hwmon/lm92 b/Documentation/hwmon/lm92 new file mode 100644 index 0000000..7705bfa --- /dev/null +++ b/Documentation/hwmon/lm92 @@ -0,0 +1,37 @@ +Kernel driver lm92 +================== + +Supported chips: + * National Semiconductor LM92 + Prefix: 'lm92' + Addresses scanned: I2C 0x48 - 0x4b + Datasheet: http://www.national.com/pf/LM/LM92.html + * National Semiconductor LM76 + Prefix: 'lm92' + Addresses scanned: none, force parameter needed + Datasheet: http://www.national.com/pf/LM/LM76.html + * Maxim MAX6633/MAX6634/MAX6635 + Prefix: 'lm92' + Addresses scanned: I2C 0x48 - 0x4b + MAX6633 with address in 0x40 - 0x47, 0x4c - 0x4f needs force parameter + and MAX6634 with address in 0x4c - 0x4f needs force parameter + Datasheet: http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3074 + +Authors: + Abraham van der Merwe + Jean Delvare + + +Description +----------- + +This driver implements support for the National Semiconductor LM92 +temperature sensor. + +Each LM92 temperature sensor supports a single temperature sensor. There are +alarms for high, low, and critical thresholds. There's also an hysteresis to +control the thresholds for resetting alarms. + +Support was added later for the LM76 and Maxim MAX6633/MAX6634/MAX6635, +which are mostly compatible. They have not all been tested, so you +may need to use the force parameter. diff --git a/Documentation/hwmon/max1619 b/Documentation/hwmon/max1619 new file mode 100644 index 0000000..d6f8d9c --- /dev/null +++ b/Documentation/hwmon/max1619 @@ -0,0 +1,29 @@ +Kernel driver max1619 +===================== + +Supported chips: + * Maxim MAX1619 + Prefix: 'max1619' + Addresses scanned: I2C 0x18-0x1a, 0x29-0x2b, 0x4c-0x4e + Datasheet: Publicly available at the Maxim website + http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf + +Authors: + Alexey Fisher , + Jean Delvare + +Description +----------- + +The MAX1619 is a digital temperature sensor. It senses its own temperature as +well as the temperature of up to one external diode. + +All temperature values are given in degrees Celsius. Resolution +is 1.0 degree for the local temperature and for the remote temperature. + +Only the external sensor has high and low limits. + +The max1619 driver will not update its values more frequently than every +other second; reading them more often will do no harm, but will return +'old' values. + diff --git a/Documentation/hwmon/pc87360 b/Documentation/hwmon/pc87360 new file mode 100644 index 0000000..89a8fcf --- /dev/null +++ b/Documentation/hwmon/pc87360 @@ -0,0 +1,189 @@ +Kernel driver pc87360 +===================== + +Supported chips: + * National Semiconductor PC87360, PC87363, PC87364, PC87365 and PC87366 + Prefixes: 'pc87360', 'pc87363', 'pc87364', 'pc87365', 'pc87366' + Addresses scanned: none, address read from Super I/O config space + Datasheets: + http://www.national.com/pf/PC/PC87360.html + http://www.national.com/pf/PC/PC87363.html + http://www.national.com/pf/PC/PC87364.html + http://www.national.com/pf/PC/PC87365.html + http://www.national.com/pf/PC/PC87366.html + +Authors: Jean Delvare + +Thanks to Sandeep Mehta, Tonko de Rooy and Daniel Ceregatti for testing. +Thanks to Rudolf Marek for helping me investigate conversion issues. + + +Module Parameters +----------------- + +* init int + Chip initialization level: + 0: None + *1: Forcibly enable internal voltage and temperature channels, except in9 + 2: Forcibly enable all voltage and temperature channels, except in9 + 3: Forcibly enable all voltage and temperature channels, including in9 + +Note that this parameter has no effect for the PC87360, PC87363 and PC87364 +chips. + +Also note that for the PC87366, initialization levels 2 and 3 don't enable +all temperature channels, because some of them share pins with each other, +so they can't be used at the same time. + + +Description +----------- + +The National Semiconductor PC87360 Super I/O chip contains monitoring and +PWM control circuitry for two fans. The PC87363 chip is similar, and the +PC87364 chip has monitoring and PWM control for a third fan. + +The National Semiconductor PC87365 and PC87366 Super I/O chips are complete +hardware monitoring chipsets, not only controlling and monitoring three fans, +but also monitoring eleven voltage inputs and two (PC87365) or up to four +(PC87366) temperatures. + + 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 + +The 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) + +Fan Monitoring +-------------- + +Fan rotation speeds are reported in RPM (revolutions per minute). An alarm +is triggered if the rotation speed has dropped below a programmable limit. +A different alarm is triggered if the fan speed is too low to be measured. + +Fan readings are affected by a programmable clock divider, giving the +readings more range or accuracy. Usually, users have to learn how it works, +but this driver implements dynamic clock divider selection, so you don't +have to care no more. + +For reference, here are a few values about clock dividers: + + slowest accuracy highest + measurable around 3000 accurate + divider speed (RPM) RPM (RPM) speed (RPM) + 1 1882 18 6928 + 2 941 37 4898 + 4 470 74 3464 + 8 235 150 2449 + +For the curious, here is how the values above were computed: + * slowest measurable speed: clock/(255*divider) + * accuracy around 3000 RPM: 3000^2/clock + * highest accurate speed: sqrt(clock*100) +The clock speed for the PC87360 family is 480 kHz. I arbitrarily chose 100 +RPM as the lowest acceptable accuracy. + +As mentioned above, you don't have to care about this no more. + +Note that not all RPM values can be represented, even when the best clock +divider is selected. This is not only true for the measured speeds, but +also for the programmable low limits, so don't be surprised if you try to +set, say, fan1_min to 2900 and it finally reads 2909. + + +Fan Control +----------- + +PWM (pulse width modulation) values range from 0 to 255, with 0 meaning +that the fan is stopped, and 255 meaning that the fan goes at full speed. + +Be extremely careful when changing PWM values. Low PWM values, even +non-zero, can stop the fan, which may cause irreversible damage to your +hardware if temperature increases too much. When changing PWM values, go +step by step and keep an eye on temperatures. + +One user reported problems with PWM. Changing PWM values would break fan +speed readings. No explanation nor fix could be found. + + +Temperature Monitoring +---------------------- + +Temperatures are reported in degrees Celsius. Each temperature measured has +associated low, high and overtemperature limits, each of which triggers an +alarm when crossed. + +The first two temperature channels are external. The third one (PC87366 +only) is internal. + +The PC87366 has three additional temperature channels, based on +thermistors (as opposed to thermal diodes for the first three temperature +channels). For technical reasons, these channels are held by the VLM +(voltage level monitor) logical device, not the TMS (temperature +measurement) one. As a consequence, these temperatures are exported as +voltages, and converted into temperatures in user-space. + +Note that these three additional channels share their pins with the +external thermal diode channels, so you (physically) can't use them all at +the same time. Although it should be possible to mix the two sensor types, +the documents from National Semiconductor suggest that motherboard +manufacturers should choose one type and stick to it. So you will more +likely have either channels 1 to 3 (thermal diodes) or 3 to 6 (internal +thermal diode, and thermistors). + + +Voltage Monitoring +------------------ + +Voltages are reported relatively to a reference voltage, either internal or +external. Some of them (in7:Vsb, in8:Vdd and in10:AVdd) are divided by two +internally, you will have to compensate in sensors.conf. Others (in0 to in6) +are likely to be divided externally. The meaning of each of these inputs as +well as the values of the resistors used for division is left to the +motherboard manufacturers, so you will have to document yourself and edit +sensors.conf accordingly. National Semiconductor has a document with +recommended resistor values for some voltages, but this still leaves much +room for per motherboard specificities, unfortunately. Even worse, +motherboard manufacturers don't seem to care about National Semiconductor's +recommendations. + +Each voltage measured has associated low and high limits, each of which +triggers an alarm when crossed. + +When available, VID inputs are used to provide the nominal CPU Core voltage. +The driver will default to VRM 9.0, but this can be changed from user-space. +The chipsets can handle two sets of VID inputs (on dual-CPU systems), but +the driver will only export one for now. This may change later if there is +a need. + + +General Remarks +--------------- + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once. This means that the cause for the alarm may already +have disappeared! Note that all hardware registers are read whenever any +data is read (unless it is less than 2 seconds since the last update, in +which case cached values are returned instead). As a consequence, when +a once-only alarm triggers, it may take 2 seconds for it to show, and 2 +more seconds for it to disappear. + +Monitoring of in9 isn't enabled at lower init levels (<3) because that +channel measures the battery voltage (Vbat). It is a known fact that +repeatedly sampling the battery voltage reduces its lifetime. National +Semiconductor smartly designed their chipset so that in9 is sampled only +once every 1024 sampling cycles (that is every 34 minutes at the default +sampling rate), so the effect is attenuated, but still present. + + +Limitations +----------- + +The datasheets suggests that some values (fan mins, fan dividers) +shouldn't be changed once the monitoring has started, but we ignore that +recommendation. We'll reconsider if it actually causes trouble. diff --git a/Documentation/hwmon/sis5595 b/Documentation/hwmon/sis5595 new file mode 100644 index 0000000..b7ae36b --- /dev/null +++ b/Documentation/hwmon/sis5595 @@ -0,0 +1,106 @@ +Kernel driver sis5595 +===================== + +Supported chips: + * Silicon Integrated Systems Corp. SiS5595 Southbridge Hardware Monitor + Prefix: 'sis5595' + Addresses scanned: ISA in PCI-space encoded address + Datasheet: Publicly available at the Silicon Integrated Systems Corp. site. + +Authors: + Kyösti Mälkki , + Mark D. Studebaker , + Aurelien Jarno 2.6 port + + 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 + 630 0008 0630 + 645 0008 0645 + 730 0008 0730 + 735 0008 0735 + + +Module Parameters +----------------- +force_addr=0xaddr Set the I/O base address. Useful for boards + that don't set the address in the BIOS. Does not do a + PCI force; the device must still be present in lspci. + Don't use this unless the driver complains that the + base address is not set. + Example: 'modprobe sis5595 force_addr=0x290' + + +Description +----------- + +The SiS5595 southbridge has integrated hardware monitor functions. It also +has an I2C bus, but this driver only supports the hardware monitor. For the +I2C bus driver see i2c-sis5595. + +The SiS5595 implements zero or one temperature sensor, two fan speed +sensors, four or five voltage sensors, and alarms. + +On the first version of the chip, there are four voltage sensors and one +temperature sensor. + +On the second version of the chip, the temperature sensor (temp) and the +fifth voltage sensor (in4) share a pin which is configurable, but not +through the driver. Sorry. The driver senses the configuration of the pin, +which was hopefully set by the BIOS. + +Temperatures are measured in degrees Celsius. An alarm is triggered once +when the max is crossed; it is also triggered when it drops below the min +value. Measurements are guaranteed between -55 and +125 degrees, with a +resolution of 1 degree. + +Fan rotation speeds are reported in RPM (rotations per minute). An alarm is +triggered if the rotation speed has dropped below a programmable limit. Fan +readings can be divided by a programmable divider (1, 2, 4 or 8) to give +the readings more range or accuracy. Not all RPM values can accurately be +represented, so some rounding is done. With a divider of 2, the lowest +representable value is around 2600 RPM. + +Voltage sensors (also known as IN sensors) report their values in volts. An +alarm is triggered if the voltage has crossed a programmable minimum or +maximum limit. Note that minimum in this case always means 'closest to +zero'; this is important for negative voltage measurements. All voltage +inputs can measure voltages between 0 and 4.08 volts, with a resolution of +0.016 volt. + +In addition to the alarms described above, there is a BTI alarm, which gets +triggered when an external chip has crossed its limits. Usually, this is +connected to some LM75-like chip; if at least one crosses its limits, this +bit gets set. + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once. This means that the cause for the alarm may already +have disappeared! Note that in the current implementation, all hardware +registers are read whenever any data is read (unless it is less than 1.5 +seconds since the last update). This means that you can easily miss +once-only alarms. + +The SiS5595 only updates its values each 1.5 seconds; reading it more often +will do no harm, but will return 'old' values. + +Problems +-------- +Some chips refuse to be enabled. We don't know why. +The driver will recognize this and print a message in dmesg. + diff --git a/Documentation/hwmon/smsc47b397 b/Documentation/hwmon/smsc47b397 new file mode 100644 index 0000000..da9d80c --- /dev/null +++ b/Documentation/hwmon/smsc47b397 @@ -0,0 +1,158 @@ +Kernel driver smsc47b397 +======================== + +Supported chips: + * SMSC LPC47B397-NC + Prefix: 'smsc47b397' + Addresses scanned: none, address read from Super I/O config space + Datasheet: In this file + +Authors: Mark M. Hoffman + Utilitek Systems, Inc. + +November 23, 2004 + +The following specification describes the SMSC LPC47B397-NC sensor chip +(for which there is no public datasheet available). This document was +provided by Craig Kelly (In-Store Broadcast Network) and edited/corrected +by Mark M. Hoffman . + +* * * * * + +Methods for detecting the HP SIO and reading the thermal data on a dc7100. + +The thermal information on the dc7100 is contained in the SIO Hardware Monitor +(HWM). The information is accessed through an index/data pair. The index/data +pair is located at the HWM Base Address + 0 and the HWM Base Address + 1. The +HWM Base address can be obtained from Logical Device 8, registers 0x60 (MSB) +and 0x61 (LSB). Currently we are using 0x480 for the HWM Base Address and +0x480 and 0x481 for the index/data pair. + +Reading temperature information. +The temperature information is located in the following registers: +Temp1 0x25 (Currently, this reflects the CPU temp on all systems). +Temp2 0x26 +Temp3 0x27 +Temp4 0x80 + +Programming Example +The following is an example of how to read the HWM temperature registers: +MOV DX,480H +MOV AX,25H +OUT DX,AL +MOV DX,481H +IN AL,DX + +AL contains the data in hex, the temperature in Celsius is the decimal +equivalent. + +Ex: If AL contains 0x2A, the temperature is 42 degrees C. + +Reading tach information. +The fan speed information is located in the following registers: + LSB MSB +Tach1 0x28 0x29 (Currently, this reflects the CPU + fan speed on all systems). +Tach2 0x2A 0x2B +Tach3 0x2C 0x2D +Tach4 0x2E 0x2F + +Important!!! +Reading the tach LSB locks the tach MSB. +The LSB Must be read first. + +How to convert the tach reading to RPM. +The tach reading (TCount) is given by: (Tach MSB * 256) + (Tach LSB) +The SIO counts the number of 90kHz (11.111us) pulses per revolution. +RPM = 60/(TCount * 11.111us) + +Example: +Reg 0x28 = 0x9B +Reg 0x29 = 0x08 + +TCount = 0x89B = 2203 + +RPM = 60 / (2203 * 11.11111 E-6) = 2451 RPM + +Obtaining the SIO version. + +CONFIGURATION SEQUENCE +To program the configuration registers, the following sequence must be followed: +1. Enter Configuration Mode +2. Configure the Configuration Registers +3. Exit Configuration Mode. + +Enter Configuration Mode +To place the chip into the Configuration State The config key (0x55) is written +to the CONFIG PORT (0x2E). + +Configuration Mode +In configuration mode, the INDEX PORT is located at the CONFIG PORT address and +the DATA PORT is at INDEX PORT address + 1. + +The desired configuration registers are accessed in two steps: +a. Write the index of the Logical Device Number Configuration Register + (i.e., 0x07) to the INDEX PORT and then write the number of the + desired logical device to the DATA PORT. + +b. Write the address of the desired configuration register within the + logical device to the INDEX PORT and then write or read the config- + uration register through the DATA PORT. + +Note: If accessing the Global Configuration Registers, step (a) is not required. + +Exit Configuration Mode +To exit the Configuration State the write 0xAA to the CONFIG PORT (0x2E). +The chip returns to the RUN State. (This is important). + +Programming Example +The following is an example of how to read the SIO Device ID located at 0x20 + +; ENTER CONFIGURATION MODE +MOV DX,02EH +MOV AX,055H +OUT DX,AL +; GLOBAL CONFIGURATION REGISTER +MOV DX,02EH +MOV AL,20H +OUT DX,AL +; READ THE DATA +MOV DX,02FH +IN AL,DX +; EXIT CONFIGURATION MODE +MOV DX,02EH +MOV AX,0AAH +OUT DX,AL + +The registers of interest for identifying the SIO on the dc7100 are Device ID +(0x20) and Device Rev (0x21). + +The Device ID will read 0X6F +The Device Rev currently reads 0x01 + +Obtaining the HWM Base Address. +The following is an example of how to read the HWM Base Address located in +Logical Device 8. + +; ENTER CONFIGURATION MODE +MOV DX,02EH +MOV AX,055H +OUT DX,AL +; CONFIGURE REGISTER CRE0, +; LOGICAL DEVICE 8 +MOV DX,02EH +MOV AL,07H +OUT DX,AL ;Point to LD# Config Reg +MOV DX,02FH +MOV AL, 08H +OUT DX,AL;Point to Logical Device 8 +; +MOV DX,02EH +MOV AL,60H +OUT DX,AL ; Point to HWM Base Addr MSB +MOV DX,02FH +IN AL,DX ; Get MSB of HWM Base Addr +; EXIT CONFIGURATION MODE +MOV DX,02EH +MOV AX,0AAH +OUT DX,AL diff --git a/Documentation/hwmon/smsc47m1 b/Documentation/hwmon/smsc47m1 new file mode 100644 index 0000000..34e6478 --- /dev/null +++ b/Documentation/hwmon/smsc47m1 @@ -0,0 +1,52 @@ +Kernel driver smsc47m1 +====================== + +Supported chips: + * SMSC LPC47B27x, LPC47M10x, LPC47M13x, LPC47M14x, LPC47M15x and LPC47M192 + Addresses scanned: none, address read from Super I/O config space + Prefix: 'smsc47m1' + Datasheets: + http://www.smsc.com/main/datasheets/47b27x.pdf + http://www.smsc.com/main/datasheets/47m10x.pdf + http://www.smsc.com/main/tools/discontinued/47m13x.pdf + http://www.smsc.com/main/datasheets/47m14x.pdf + http://www.smsc.com/main/tools/discontinued/47m15x.pdf + http://www.smsc.com/main/datasheets/47m192.pdf + +Authors: + Mark D. Studebaker , + With assistance from Bruce Allen , and his + fan.c program: http://www.lsc-group.phys.uwm.edu/%7Eballen/driver/ + Gabriele Gorla , + Jean Delvare + +Description +----------- + +The Standard Microsystems Corporation (SMSC) 47M1xx Super I/O chips +contain monitoring and PWM control circuitry for two fans. + +The 47M15x and 47M192 chips contain a full 'hardware monitoring block' +in addition to the fan monitoring and control. The hardware monitoring +block is not supported by the driver. + +Fan rotation speeds are reported in RPM (rotations per minute). An alarm is +triggered if the rotation speed has dropped below a programmable limit. Fan +readings can be divided by a programmable divider (1, 2, 4 or 8) to give +the readings more range or accuracy. Not all RPM values can accurately be +represented, so some rounding is done. With a divider of 2, the lowest +representable value is around 2600 RPM. + +PWM values are from 0 to 255. + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once. This means that the cause for the alarm may +already have disappeared! Note that in the current implementation, all +hardware registers are read whenever any data is read (unless it is less +than 1.5 seconds since the last update). This means that you can easily +miss once-only alarms. + + +********************** +The lm_sensors project gratefully acknowledges the support of +Intel in the development of this driver. diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface new file mode 100644 index 0000000..3464005 --- /dev/null +++ b/Documentation/hwmon/sysfs-interface @@ -0,0 +1,274 @@ +Naming and data format standards for sysfs files +------------------------------------------------ + +The libsensors library offers an interface to the raw sensors data +through the sysfs interface. See libsensors documentation and source for +more further information. As of writing this document, libsensors +(from lm_sensors 2.8.3) is heavily chip-dependant. Adding or updating +support for any given chip requires modifying the library's code. +This is because libsensors was written for the procfs interface +older kernel modules were using, which wasn't standardized enough. +Recent versions of libsensors (from lm_sensors 2.8.2 and later) have +support for the sysfs interface, though. + +The new sysfs interface was designed to be as chip-independant as +possible. + +Note that motherboards vary widely in the connections to sensor chips. +There is no standard that ensures, for example, that the second +temperature sensor is connected to the CPU, or that the second fan is on +the CPU. Also, some values reported by the chips need some computation +before they make full sense. For example, most chips can only measure +voltages between 0 and +4V. Other voltages are scaled back into that +range using external resistors. Since the values of these resistors +can change from motherboard to motherboard, the conversions cannot be +hard coded into the driver and have to be done in user space. + +For this reason, even if we aim at a chip-independant libsensors, it will +still require a configuration file (e.g. /etc/sensors.conf) for proper +values conversion, labeling of inputs and hiding of unused inputs. + +An alternative method that some programs use is to access the sysfs +files directly. This document briefly describes the standards that the +drivers follow, so that an application program can scan for entries and +access this data in a simple and consistent way. That said, such programs +will have to implement conversion, labeling and hiding of inputs. For +this reason, it is still not recommended to bypass the library. + +If you are developing a userspace application please send us feedback on +this standard. + +Note that this standard isn't completely established yet, so it is subject +to changes, even important ones. One more reason to use the library instead +of accessing sysfs files directly. + +Each chip gets its own directory in the sysfs /sys/devices tree. To +find all sensor chips, it is easier to follow the symlinks from +/sys/i2c/devices/ + +All sysfs values are fixed point numbers. To get the true value of some +of the values, you should divide by the specified value. + +There is only one value per file, unlike the older /proc specification. +The common scheme for files naming is: _. Usual +types for sensor chips are "in" (voltage), "temp" (temperature) and +"fan" (fan). Usual items are "input" (measured value), "max" (high +threshold, "min" (low threshold). Numbering usually starts from 1, +except for voltages which start from 0 (because most data sheets use +this). A number is always used for elements that can be present more +than once, even if there is a single element of the given type on the +specific chip. Other files do not refer to a specific element, so +they have a simple name, and no number. + +Alarms are direct indications read from the chips. The drivers do NOT +make comparisons of readings to thresholds. This allows violations +between readings to be caught and alarmed. The exact definition of an +alarm (for example, whether a threshold must be met or must be exceeded +to cause an alarm) is chip-dependent. + + +------------------------------------------------------------------------- + +************ +* Voltages * +************ + +in[0-8]_min Voltage min value. + Unit: millivolt + Read/Write + +in[0-8]_max Voltage max value. + Unit: millivolt + Read/Write + +in[0-8]_input Voltage input value. + Unit: millivolt + Read only + Actual voltage depends on the scaling resistors on the + motherboard, as recommended in the chip datasheet. + This varies by chip and by motherboard. + Because of this variation, values are generally NOT scaled + by the chip driver, and must be done by the application. + However, some drivers (notably lm87 and via686a) + do scale, with various degrees of success. + These drivers will output the actual voltage. + + Typical usage: + in0_* CPU #1 voltage (not scaled) + in1_* CPU #2 voltage (not scaled) + in2_* 3.3V nominal (not scaled) + in3_* 5.0V nominal (scaled) + in4_* 12.0V nominal (scaled) + in5_* -12.0V nominal (scaled) + in6_* -5.0V nominal (scaled) + in7_* varies + in8_* varies + +cpu[0-1]_vid CPU core reference voltage. + Unit: millivolt + Read only. + Not always correct. + +vrm Voltage Regulator Module version number. + Read only. + Two digit number, first is major version, second is + minor version. + Affects the way the driver calculates the CPU core reference + voltage from the vid pins. + + +******** +* Fans * +******** + +fan[1-3]_min Fan minimum value + Unit: revolution/min (RPM) + Read/Write. + +fan[1-3]_input Fan input value. + Unit: revolution/min (RPM) + Read only. + +fan[1-3]_div Fan divisor. + Integer value in powers of two (1, 2, 4, 8, 16, 32, 64, 128). + Some chips only support values 1, 2, 4 and 8. + Note that this is actually an internal clock divisor, which + affects the measurable speed range, not the read value. + +******* +* PWM * +******* + +pwm[1-3] Pulse width modulation fan control. + Integer value in the range 0 to 255 + Read/Write + 255 is max or 100%. + +pwm[1-3]_enable + Switch PWM on and off. + Not always present even if fan*_pwm is. + 0 to turn off + 1 to turn on in manual mode + 2 to turn on in automatic mode + Read/Write + +pwm[1-*]_auto_channels_temp + Select which temperature channels affect this PWM output in + auto mode. Bitfield, 1 is temp1, 2 is temp2, 4 is temp3 etc... + Which values are possible depend on the chip used. + +pwm[1-*]_auto_point[1-*]_pwm +pwm[1-*]_auto_point[1-*]_temp +pwm[1-*]_auto_point[1-*]_temp_hyst + Define the PWM vs temperature curve. Number of trip points is + chip-dependent. Use this for chips which associate trip points + to PWM output channels. + +OR + +temp[1-*]_auto_point[1-*]_pwm +temp[1-*]_auto_point[1-*]_temp +temp[1-*]_auto_point[1-*]_temp_hyst + Define the PWM vs temperature curve. Number of trip points is + chip-dependent. Use this for chips which associate trip points + to temperature channels. + + +**************** +* Temperatures * +**************** + +temp[1-3]_type Sensor type selection. + Integers 1, 2, 3 or thermistor Beta value (3435) + Read/Write. + 1: PII/Celeron Diode + 2: 3904 transistor + 3: thermal diode + Not all types are supported by all chips + +temp[1-4]_max Temperature max value. + Unit: millidegree Celcius + Read/Write value. + +temp[1-3]_min Temperature min value. + Unit: millidegree Celcius + Read/Write value. + +temp[1-3]_max_hyst + Temperature hysteresis value for max limit. + Unit: millidegree Celcius + Must be reported as an absolute temperature, NOT a delta + from the max value. + Read/Write value. + +temp[1-4]_input Temperature input value. + Unit: millidegree Celcius + Read only value. + +temp[1-4]_crit Temperature critical value, typically greater than + corresponding temp_max values. + Unit: millidegree Celcius + Read/Write value. + +temp[1-2]_crit_hyst + Temperature hysteresis value for critical limit. + Unit: millidegree Celcius + Must be reported as an absolute temperature, NOT a delta + from the critical value. + Read/Write value. + + If there are multiple temperature sensors, temp1_* is + generally the sensor inside the chip itself, + reported as "motherboard temperature". temp2_* to + temp4_* are generally sensors external to the chip + itself, for example the thermal diode inside the CPU or + a thermistor nearby. + + +************ +* Currents * +************ + +Note that no known chip provides current measurements as of writing, +so this part is theoretical, so to say. + +curr[1-n]_max Current max value + Unit: milliampere + Read/Write. + +curr[1-n]_min Current min value. + Unit: milliampere + Read/Write. + +curr[1-n]_input Current input value + Unit: milliampere + Read only. + + +********* +* Other * +********* + +alarms Alarm bitmask. + Read only. + Integer representation of one to four bytes. + A '1' bit means an alarm. + Chips should be programmed for 'comparator' mode so that + the alarm will 'come back' after you read the register + if it is still valid. + Generally a direct representation of a chip's internal + alarm registers; there is no standard for the position + of individual bits. + Bits are defined in kernel/include/sensors.h. + +beep_enable Beep/interrupt enable + 0 to disable. + 1 to enable. + Read/Write + +beep_mask Bitmask for beep. + Same format as 'alarms' with the same bit locations. + Read/Write + +eeprom Raw EEPROM data in binary form. + Read only. diff --git a/Documentation/hwmon/userspace-tools b/Documentation/hwmon/userspace-tools new file mode 100644 index 0000000..2622aac --- /dev/null +++ b/Documentation/hwmon/userspace-tools @@ -0,0 +1,39 @@ +Introduction +------------ + +Most mainboards have sensor chips to monitor system health (like temperatures, +voltages, fans speed). They are often connected through an I2C bus, but some +are also connected directly through the ISA bus. + +The kernel drivers make the data from the sensor chips available in the /sys +virtual filesystem. Userspace tools are then used to display or set or the +data in a more friendly manner. + +Lm-sensors +---------- + +Core set of utilites that will allow you to obtain health information, +setup monitoring limits etc. You can get them on their homepage +http://www.lm-sensors.nu/ or as a package from your Linux distribution. + +If from website: +Get lmsensors from project web site. Please note, you need only userspace +part, so compile with "make user_install" target. + +General hints to get things working: + +0) get lm-sensors userspace utils +1) compile all drivers in I2C section as modules in your kernel +2) run sensors-detect script, it will tell you what modules you need to load. +3) load them and run "sensors" command, you should see some results. +4) fix sensors.conf, labels, limits, fan divisors +5) if any more problems consult FAQ, or documentation + +Other utilites +-------------- + +If you want some graphical indicators of system health look for applications +like: gkrellm, ksensors, xsensors, wmtemp, wmsensors, wmgtemp, ksysguardd, +hardware-monitor + +If you are server administrator you can try snmpd or mrtgutils. diff --git a/Documentation/hwmon/via686a b/Documentation/hwmon/via686a new file mode 100644 index 0000000..b82014c --- /dev/null +++ b/Documentation/hwmon/via686a @@ -0,0 +1,65 @@ +Kernel driver via686a +===================== + +Supported chips: + * Via VT82C686A, VT82C686B Southbridge Integrated Hardware Monitor + Prefix: 'via686a' + Addresses scanned: ISA in PCI-space encoded address + Datasheet: On request through web form (http://www.via.com.tw/en/support/datasheets/) + +Authors: + Kyösti Mälkki , + Mark D. Studebaker + Bob Dougherty + (Some conversion-factor data were contributed by + Jonathan Teh Soon Yew + and Alex van Kaam .) + +Module Parameters +----------------- + +force_addr=0xaddr Set the I/O base address. Useful for Asus A7V boards + that don't set the address in the BIOS. Does not do a + PCI force; the via686a must still be present in lspci. + Don't use this unless the driver complains that the + base address is not set. + Example: 'modprobe via686a force_addr=0x6000' + +Description +----------- + +The driver does not distinguish between the chips and reports +all as a 686A. + +The Via 686a southbridge has integrated hardware monitor functionality. +It also has an I2C bus, but this driver only supports the hardware monitor. +For the I2C bus driver, see + +The Via 686a implements three temperature sensors, two fan rotation speed +sensors, five voltage sensors and alarms. + +Temperatures are measured in degrees Celsius. An alarm is triggered once +when the Overtemperature Shutdown limit is crossed; it is triggered again +as soon as it drops below the hysteresis value. + +Fan rotation speeds are reported in RPM (rotations per minute). An alarm is +triggered if the rotation speed has dropped below a programmable limit. Fan +readings can be divided by a programmable divider (1, 2, 4 or 8) to give +the readings more range or accuracy. Not all RPM values can accurately be +represented, so some rounding is done. With a divider of 2, the lowest +representable value is around 2600 RPM. + +Voltage sensors (also known as IN sensors) report their values in volts. +An alarm is triggered if the voltage has crossed a programmable minimum +or maximum limit. Voltages are internally scalled, so each voltage channel +has a different resolution and range. + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once. This means that the cause for the alarm may +already have disappeared! Note that in the current implementation, all +hardware registers are read whenever any data is read (unless it is less +than 1.5 seconds since the last update). This means that you can easily +miss once-only alarms. + +The driver only updates its values each 1.5 seconds; reading it more often +will do no harm, but will return 'old' values. diff --git a/Documentation/hwmon/w83627hf b/Documentation/hwmon/w83627hf new file mode 100644 index 0000000..78f37c2 --- /dev/null +++ b/Documentation/hwmon/w83627hf @@ -0,0 +1,66 @@ +Kernel driver w83627hf +====================== + +Supported chips: + * Winbond W83627HF (ISA accesses ONLY) + Prefix: 'w83627hf' + Addresses scanned: ISA address retrieved from Super I/O registers + Datasheet: http://www.winbond.com/PDF/sheet/w83627hf.pdf + * Winbond W83627THF + Prefix: 'w83627thf' + Addresses scanned: ISA address retrieved from Super I/O registers + Datasheet: http://www.winbond.com/PDF/sheet/w83627thf.pdf + * Winbond W83697HF + Prefix: 'w83697hf' + Addresses scanned: ISA address retrieved from Super I/O registers + Datasheet: http://www.winbond.com/PDF/sheet/697hf.pdf + * Winbond W83637HF + Prefix: 'w83637hf' + Addresses scanned: ISA address retrieved from Super I/O registers + Datasheet: http://www.winbond.com/PDF/sheet/w83637hf.pdf + +Authors: + Frodo Looijaard , + Philip Edelbrock , + Mark Studebaker , + Bernhard C. Schrenk + +Module Parameters +----------------- + +* force_addr: int + Initialize the ISA address of the sensors +* force_i2c: int + Initialize the I2C address of the sensors +* init: int + (default is 1) + Use 'init=0' to bypass initializing the chip. + Try this if your computer crashes when you load the module. + +Description +----------- + +This driver implements support for ISA accesses *only* for +the Winbond W83627HF, W83627THF, W83697HF and W83637HF Super I/O chips. +We will refer to them collectively as Winbond chips. + +This driver supports ISA accesses, which should be more reliable +than i2c accesses. Also, for Tyan boards which contain both a +Super I/O chip and a second i2c-only Winbond chip (often a W83782D), +using this driver will avoid i2c address conflicts and complex +initialization that were required in the w83781d driver. + +If you really want i2c accesses for these Super I/O chips, +use the w83781d driver. However this is not the preferred method +now that this ISA driver has been developed. + +Technically, the w83627thf does not support a VID reading. However, it's +possible or even likely that your mainboard maker has routed these signals +to a specific set of general purpose IO pins (the Asus P4C800-E is one such +board). The w83627thf driver now interprets these as VID. If the VID on +your board doesn't work, first see doc/vid in the lm_sensors package. If +that still doesn't help, email us at lm-sensors@lm-sensors.org. + +For further information on this driver see the w83781d driver +documentation. + diff --git a/Documentation/hwmon/w83781d b/Documentation/hwmon/w83781d new file mode 100644 index 0000000..e545933 --- /dev/null +++ b/Documentation/hwmon/w83781d @@ -0,0 +1,402 @@ +Kernel driver w83781d +===================== + +Supported chips: + * Winbond W83781D + Prefix: 'w83781d' + Addresses scanned: I2C 0x20 - 0x2f, ISA 0x290 (8 I/O ports) + Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/w83781d.pdf + * Winbond W83782D + Prefix: 'w83782d' + Addresses scanned: I2C 0x20 - 0x2f, ISA 0x290 (8 I/O ports) + Datasheet: http://www.winbond.com/PDF/sheet/w83782d.pdf + * Winbond W83783S + Prefix: 'w83783s' + Addresses scanned: I2C 0x2d + Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/w83783s.pdf + * Winbond W83627HF + Prefix: 'w83627hf' + Addresses scanned: I2C 0x20 - 0x2f, ISA 0x290 (8 I/O ports) + Datasheet: http://www.winbond.com/PDF/sheet/w83627hf.pdf + * Asus AS99127F + Prefix: 'as99127f' + Addresses scanned: I2C 0x28 - 0x2f + Datasheet: Unavailable from Asus + +Authors: + Frodo Looijaard , + Philip Edelbrock , + Mark Studebaker + +Module parameters +----------------- + +* init int + (default 1) + Use 'init=0' to bypass initializing the chip. + Try this if your computer crashes when you load the module. + +force_subclients=bus,caddr,saddr,saddr + This is used to force the i2c addresses for subclients of + a certain chip. Typical usage is `force_subclients=0,0x2d,0x4a,0x4b' + to force the subclients of chip 0x2d on bus 0 to i2c addresses + 0x4a and 0x4b. This parameter is useful for certain Tyan boards. + +Description +----------- + +This driver implements support for the Winbond W83781D, W83782D, W83783S, +W83627HF chips, and the Asus AS99127F chips. We will refer to them +collectively as W8378* chips. + +There is quite some difference between these chips, but they are similar +enough that it was sensible to put them together in one driver. +The W83627HF chip is assumed to be identical to the ISA W83782D. +The Asus chips are similar to an I2C-only W83782D. + +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 + +Detection of these chips can sometimes be foiled because they can be in +an internal state that allows no clean access. If you know the address +of the chip, use a 'force' parameter; this will put them into a more +well-behaved state first. + +The W8378* implements temperature sensors (three on the W83781D and W83782D, +two on the W83783S), three fan rotation speed sensors, voltage sensors +(seven on the W83781D, nine on the W83782D and six on the W83783S), VID +lines, alarms with beep warnings, and some miscellaneous stuff. + +Temperatures are measured in degrees Celsius. There is always one main +temperature sensor, and one (W83783S) or two (W83781D and W83782D) other +sensors. An alarm is triggered for the main sensor once when the +Overtemperature Shutdown limit is crossed; it is triggered again as soon as +it drops below the Hysteresis value. A more useful behavior +can be found by setting the Hysteresis value to +127 degrees Celsius; in +this case, alarms are issued during all the time when the actual temperature +is above the Overtemperature Shutdown value. The driver sets the +hysteresis value for temp1 to 127 at initialization. + +For the other temperature sensor(s), an alarm is triggered when the +temperature gets higher then the Overtemperature Shutdown value; it stays +on until the temperature falls below the Hysteresis value. But on the +W83781D, there is only one alarm that functions for both other sensors! +Temperatures are guaranteed within a range of -55 to +125 degrees. The +main temperature sensors has a resolution of 1 degree; the other sensor(s) +of 0.5 degree. + +Fan rotation speeds are reported in RPM (rotations per minute). An alarm is +triggered if the rotation speed has dropped below a programmable limit. Fan +readings can be divided by a programmable divider (1, 2, 4 or 8 for the +W83781D; 1, 2, 4, 8, 16, 32, 64 or 128 for the others) to give +the readings more range or accuracy. Not all RPM values can accurately +be represented, so some rounding is done. With a divider of 2, the lowest +representable value is around 2600 RPM. + +Voltage sensors (also known as IN sensors) report their values in volts. +An alarm is triggered if the voltage has crossed a programmable minimum +or maximum limit. Note that minimum in this case always means 'closest to +zero'; this is important for negative voltage measurements. All voltage +inputs can measure voltages between 0 and 4.08 volts, with a resolution +of 0.016 volt. + +The VID lines encode the core voltage value: the voltage level your processor +should work with. This is hardcoded by the mainboard and/or processor itself. +It is a value in volts. When it is unconnected, you will often find the +value 3.50 V here. + +The W83782D and W83783S temperature conversion machine understands about +several kinds of temperature probes. You can program the so-called +beta value in the sensor files. '1' is the PII/Celeron diode, '2' is the +TN3904 transistor, and 3435 the default thermistor value. Other values +are (not yet) supported. + +In addition to the alarms described above, there is a CHAS alarm on the +chips which triggers if your computer case is open. + +When an alarm goes off, you can be warned by a beeping signal through +your computer speaker. It is possible to enable all beeping globally, +or only the beeping for some alarms. + +If an alarm triggers, it will remain triggered until the hardware register +is read at least once. This means that the cause for the alarm may +already have disappeared! Note that in the current implementation, all +hardware registers are read whenever any data is read (unless it is less +than 1.5 seconds since the last update). This means that you can easily +miss once-only alarms. + +The chips only update values each 1.5 seconds; reading them more often +will do no harm, but will return 'old' values. + +AS99127F PROBLEMS +----------------- +The as99127f support was developed without the benefit of a datasheet. +In most cases it is treated as a w83781d (although revision 2 of the +AS99127F looks more like a w83782d). +This support will be BETA until a datasheet is released. +One user has reported problems with fans stopping +occasionally. + +Note that the individual beep bits are inverted from the other chips. +The driver now takes care of this so that user-space applications +don't have to know about it. + +Known problems: + - Problems with diode/thermistor settings (supported?) + - One user reports fans stopping under high server load. + - Revision 2 seems to have 2 PWM registers but we don't know + how to handle them. More details below. + +These will not be fixed unless we get a datasheet. +If you have problems, please lobby Asus to release a datasheet. +Unfortunately several others have without success. +Please do not send mail to us asking for better as99127f support. +We have done the best we can without a datasheet. +Please do not send mail to the author or the sensors group asking for +a datasheet or ideas on how to convince Asus. We can't help. + + +NOTES: +----- + 783s has no in1 so that in[2-6] are compatible with the 781d/782d. + + 783s pin is programmable for -5V or temp1; defaults to -5V, + no control in driver so temp1 doesn't work. + + 782d and 783s datasheets differ on which is pwm1 and which is pwm2. + We chose to follow 782d. + + 782d and 783s pin is programmable for fan3 input or pwm2 output; + defaults to fan3 input. + If pwm2 is enabled (with echo 255 1 > pwm2), then + fan3 will report 0. + + 782d has pwm1-2 for ISA, pwm1-4 for i2c. (pwm3-4 share pins with + the ISA pins) + +Data sheet updates: +------------------ + - PWM clock registers: + + 000: master / 512 + 001: master / 1024 + 010: master / 2048 + 011: master / 4096 + 100: master / 8192 + + +Answers from Winbond tech support +--------------------------------- +> +> 1) In the W83781D data sheet section 7.2 last paragraph, it talks about +> reprogramming the R-T table if the Beta of the thermistor is not +> 3435K. The R-T table is described briefly in section 8.20. +> What formulas do I use to program a new R-T table for a given Beta? +> + We are sorry that the calculation for R-T table value is +confidential. If you have another Beta value of thermistor, we can help +to calculate the R-T table for you. But you should give us real R-T +Table which can be gotten by thermistor vendor. Therefore we will calculate +them and obtain 32-byte data, and you can fill the 32-byte data to the +register in Bank0.CR51 of W83781D. + + +> 2) In the W83782D data sheet, it mentions that pins 38, 39, and 40 are +> programmable to be either thermistor or Pentium II diode inputs. +> How do I program them for diode inputs? I can't find any register +> to program these to be diode inputs. + --> You may program Bank0 CR[5Dh] and CR[59h] registers. + + CR[5Dh] bit 1(VTIN1) bit 2(VTIN2) bit 3(VTIN3) + + thermistor 0 0 0 + diode 1 1 1 + + +(error) CR[59h] bit 4(VTIN1) bit 2(VTIN2) bit 3(VTIN3) +(right) CR[59h] bit 4(VTIN1) bit 5(VTIN2) bit 6(VTIN3) + + PII thermal diode 1 1 1 + 2N3904 diode 0 0 0 + + +Asus Clones +----------- + +We have no datasheets for the Asus clones (AS99127F and ASB100 Bach). +Here are some very useful information that were given to us by Alex Van +Kaam about how to detect these chips, and how to read their values. He +also gives advice for another Asus chipset, the Mozart-2 (which we +don't support yet). Thanks Alex! +I reworded some parts and added personal comments. + +# Detection: + +AS99127F rev.1, AS99127F rev.2 and ASB100: +- I2C address range: 0x29 - 0x2F +- If register 0x58 holds 0x31 then we have an Asus (either ASB100 or + AS99127F) +- Which one depends on register 0x4F (manufacturer ID): + 0x06 or 0x94: ASB100 + 0x12 or 0xC3: AS99127F rev.1 + 0x5C or 0xA3: AS99127F rev.2 + Note that 0x5CA3 is Winbond's ID (WEC), which let us think Asus get their + AS99127F rev.2 direct from Winbond. The other codes mean ATT and DVC, + respectively. ATT could stand for Asustek something (although it would be + very badly chosen IMHO), I don't know what DVC could stand for. Maybe + these codes simply aren't meant to be decoded that way. + +Mozart-2: +- I2C address: 0x77 +- If register 0x58 holds 0x56 or 0x10 then we have a Mozart-2 +- Of the Mozart there are 3 types: + 0x58=0x56, 0x4E=0x94, 0x4F=0x36: Asus ASM58 Mozart-2 + 0x58=0x56, 0x4E=0x94, 0x4F=0x06: Asus AS2K129R Mozart-2 + 0x58=0x10, 0x4E=0x5C, 0x4F=0xA3: Asus ??? Mozart-2 + You can handle all 3 the exact same way :) + +# Temperature sensors: + +ASB100: +- sensor 1: register 0x27 +- sensor 2 & 3 are the 2 LM75's on the SMBus +- sensor 4: register 0x17 +Remark: I noticed that on Intel boards sensor 2 is used for the CPU + and 4 is ignored/stuck, on AMD boards sensor 4 is the CPU and sensor 2 is + either ignored or a socket temperature. + +AS99127F (rev.1 and 2 alike): +- sensor 1: register 0x27 +- sensor 2 & 3 are the 2 LM75's on the SMBus +Remark: Register 0x5b is suspected to be temperature type selector. Bit 1 + would control temp1, bit 3 temp2 and bit 5 temp3. + +Mozart-2: +- sensor 1: register 0x27 +- sensor 2: register 0x13 + +# Fan sensors: + +ASB100, AS99127F (rev.1 and 2 alike): +- 3 fans, identical to the W83781D + +Mozart-2: +- 2 fans only, 1350000/RPM/div +- fan 1: register 0x28, divisor on register 0xA1 (bits 4-5) +- fan 2: register 0x29, divisor on register 0xA1 (bits 6-7) + +# Voltages: + +This is where there is a difference between AS99127F rev.1 and 2. +Remark: The difference is similar to the difference between + W83781D and W83782D. + +ASB100: +in0=r(0x20)*0.016 +in1=r(0x21)*0.016 +in2=r(0x22)*0.016 +in3=r(0x23)*0.016*1.68 +in4=r(0x24)*0.016*3.8 +in5=r(0x25)*(-0.016)*3.97 +in6=r(0x26)*(-0.016)*1.666 + +AS99127F rev.1: +in0=r(0x20)*0.016 +in1=r(0x21)*0.016 +in2=r(0x22)*0.016 +in3=r(0x23)*0.016*1.68 +in4=r(0x24)*0.016*3.8 +in5=r(0x25)*(-0.016)*3.97 +in6=r(0x26)*(-0.016)*1.503 + +AS99127F rev.2: +in0=r(0x20)*0.016 +in1=r(0x21)*0.016 +in2=r(0x22)*0.016 +in3=r(0x23)*0.016*1.68 +in4=r(0x24)*0.016*3.8 +in5=(r(0x25)*0.016-3.6)*5.14+3.6 +in6=(r(0x26)*0.016-3.6)*3.14+3.6 + +Mozart-2: +in0=r(0x20)*0.016 +in1=255 +in2=r(0x22)*0.016 +in3=r(0x23)*0.016*1.68 +in4=r(0x24)*0.016*4 +in5=255 +in6=255 + + +# PWM + +Additional info about PWM on the AS99127F (may apply to other Asus +chips as well) by Jean Delvare as of 2004-04-09: + +AS99127F revision 2 seems to have two PWM registers at 0x59 and 0x5A, +and a temperature sensor type selector at 0x5B (which basically means +that they swapped registers 0x59 and 0x5B when you compare with Winbond +chips). +Revision 1 of the chip also has the temperature sensor type selector at +0x5B, but PWM registers have no effect. + +We don't know exactly how the temperature sensor type selection works. +Looks like bits 1-0 are for temp1, bits 3-2 for temp2 and bits 5-4 for +temp3, although it is possible that only the most significant bit matters +each time. So far, values other than 0 always broke the readings. + +PWM registers seem to be split in two parts: bit 7 is a mode selector, +while the other bits seem to define a value or threshold. + +When bit 7 is clear, bits 6-0 seem to hold a threshold value. If the value +is below a given limit, the fan runs at low speed. If the value is above +the limit, the fan runs at full speed. We have no clue as to what the limit +represents. Note that there seem to be some inertia in this mode, speed +changes may need some time to trigger. Also, an hysteresis mechanism is +suspected since walking through all the values increasingly and then +decreasingly led to slightly different limits. + +When bit 7 is set, bits 3-0 seem to hold a threshold value, while bits 6-4 +would not be significant. If the value is below a given limit, the fan runs +at full speed, while if it is above the limit it runs at low speed (so this +is the contrary of the other mode, in a way). Here again, we don't know +what the limit is supposed to represent. + +One remarkable thing is that the fans would only have two or three +different speeds (transitional states left apart), not a whole range as +you usually get with PWM. + +As a conclusion, you can write 0x00 or 0x8F to the PWM registers to make +fans run at low speed, and 0x7F or 0x80 to make them run at full speed. + +Please contact us if you can figure out how it is supposed to work. As +long as we don't know more, the w83781d driver doesn't handle PWM on +AS99127F chips at all. + +Additional info about PWM on the AS99127F rev.1 by Hector Martin: + +I've been fiddling around with the (in)famous 0x59 register and +found out the following values do work as a form of coarse pwm: + +0x80 - seems to turn fans off after some time(1-2 minutes)... might be +some form of auto-fan-control based on temp? hmm (Qfan? this mobo is an +old ASUS, it isn't marketed as Qfan. Maybe some beta pre-attemp at Qfan +that was dropped at the BIOS) +0x81 - off +0x82 - slightly "on-ner" than off, but my fans do not get to move. I can +hear the high-pitched PWM sound that motors give off at too-low-pwm. +0x83 - now they do move. Estimate about 70% speed or so. +0x84-0x8f - full on + +Changing the high nibble doesn't seem to do much except the high bit +(0x80) must be set for PWM to work, else the current pwm doesn't seem to +change. + +My mobo is an ASUS A7V266-E. This behavior is similar to what I got +with speedfan under Windows, where 0-15% would be off, 15-2x% (can't +remember the exact value) would be 70% and higher would be full on. diff --git a/Documentation/hwmon/w83l785ts b/Documentation/hwmon/w83l785ts new file mode 100644 index 0000000..1841ced --- /dev/null +++ b/Documentation/hwmon/w83l785ts @@ -0,0 +1,39 @@ +Kernel driver w83l785ts +======================= + +Supported chips: + * Winbond W83L785TS-S + Prefix: 'w83l785ts' + Addresses scanned: I2C 0x2e + Datasheet: Publicly available at the Winbond USA website + http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf + +Authors: + Jean Delvare + +Description +----------- + +The W83L785TS-S is a digital temperature sensor. It senses the +temperature of a single external diode. The high limit is +theoretically defined as 85 or 100 degrees C through a combination +of external resistors, so the user cannot change it. Values seen so +far suggest that the two possible limits are actually 95 and 110 +degrees C. The datasheet is rather poor and obviously inaccurate +on several points including this one. + +All temperature values are given in degrees Celsius. Resolution +is 1.0 degree. See the datasheet for details. + +The w83l785ts driver will not update its values more frequently than +every other second; reading them more often will do no harm, but will +return 'old' values. + +Known Issues +------------ + +On some systems (Asus), the BIOS is known to interfere with the driver +and cause read errors. The driver will retry a given number of times +(5 by default) and then give up, returning the old value (or 0 if +there is no old value). It seems to work well enough so that you should +not notice anything. Thanks to James Bolt for helping test this feature. diff --git a/Documentation/i2c/chips/adm1021 b/Documentation/i2c/chips/adm1021 deleted file mode 100644 index 03d02bf..0000000 --- a/Documentation/i2c/chips/adm1021 +++ /dev/null @@ -1,111 +0,0 @@ -Kernel driver adm1021 -===================== - -Supported chips: - * Analog Devices ADM1021 - Prefix: 'adm1021' - Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the Analog Devices website - * Analog Devices ADM1021A/ADM1023 - Prefix: 'adm1023' - Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the Analog Devices website - * Genesys Logic GL523SM - Prefix: 'gl523sm' - Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: - * Intel Xeon Processor - Prefix: - any other - may require 'force_adm1021' parameter - Addresses scanned: none - Datasheet: Publicly available at Intel website - * Maxim MAX1617 - Prefix: 'max1617' - Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the Maxim website - * Maxim MAX1617A - Prefix: 'max1617a' - Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the Maxim website - * National Semiconductor LM84 - Prefix: 'lm84' - Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the National Semiconductor website - * Philips NE1617 - Prefix: 'max1617' (probably detected as a max1617) - Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the Philips website - * Philips NE1617A - Prefix: 'max1617' (probably detected as a max1617) - Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the Philips website - * TI THMC10 - Prefix: 'thmc10' - Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the TI website - * Onsemi MC1066 - Prefix: 'mc1066' - Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the Onsemi website - - -Authors: - Frodo Looijaard , - Philip Edelbrock - -Module Parameters ------------------ - -* read_only: int - Don't set any values, read only mode - - -Description ------------ - -The chips supported by this driver are very similar. The Maxim MAX1617 is -the oldest; it has the problem that it is not very well detectable. The -MAX1617A solves that. The ADM1021 is a straight clone of the MAX1617A. -Ditto for the THMC10. From here on, we will refer to all these chips as -ADM1021-clones. - -The ADM1021 and MAX1617A reports a die code, which is a sort of revision -code. This can help us pinpoint problems; it is not very useful -otherwise. - -ADM1021-clones implement two temperature sensors. One of them is internal, -and measures the temperature of the chip itself; the other is external and -is realised in the form of a transistor-like device. A special alarm -indicates whether the remote sensor is connected. - -Each sensor has its own low and high limits. When they are crossed, the -corresponding alarm is set and remains on as long as the temperature stays -out of range. Temperatures are measured in degrees Celsius. Measurements -are possible between -65 and +127 degrees, with a resolution of one degree. - -If an alarm triggers, it will remain triggered until the hardware register -is read at least once. This means that the cause for the alarm may already -have disappeared! - -This driver only updates its values each 1.5 seconds; reading it more often -will do no harm, but will return 'old' values. It is possible to make -ADM1021-clones do faster measurements, but there is really no good reason -for that. - -Xeon support ------------- - -Some Xeon processors have real max1617, adm1021, or compatible chips -within them, with two temperature sensors. - -Other Xeons have chips with only one sensor. - -If you have a Xeon, and the adm1021 module loads, and both temperatures -appear valid, then things are good. - -If the adm1021 module doesn't load, you should try this: - modprobe adm1021 force_adm1021=BUS,ADDRESS - ADDRESS can only be 0x18, 0x1a, 0x29, 0x2b, 0x4c, or 0x4e. - -If you have dual Xeons you may have appear to have two separate -adm1021-compatible chips, or two single-temperature sensors, at distinct -addresses. diff --git a/Documentation/i2c/chips/adm1025 b/Documentation/i2c/chips/adm1025 deleted file mode 100644 index 39d2b78..0000000 --- a/Documentation/i2c/chips/adm1025 +++ /dev/null @@ -1,51 +0,0 @@ -Kernel driver adm1025 -===================== - -Supported chips: - * Analog Devices ADM1025, ADM1025A - Prefix: 'adm1025' - Addresses scanned: I2C 0x2c - 0x2e - Datasheet: Publicly available at the Analog Devices website - * Philips NE1619 - Prefix: 'ne1619' - Addresses scanned: I2C 0x2c - 0x2d - Datasheet: Publicly available at the Philips website - -The NE1619 presents some differences with the original ADM1025: - * Only two possible addresses (0x2c - 0x2d). - * No temperature offset register, but we don't use it anyway. - * No INT mode for pin 16. We don't play with it anyway. - -Authors: - Chen-Yuan Wu , - Jean Delvare - -Description ------------ - -(This is from Analog Devices.) The ADM1025 is a complete system hardware -monitor for microprocessor-based systems, providing measurement and limit -comparison of various system parameters. Five voltage measurement inputs -are provided, for monitoring +2.5V, +3.3V, +5V and +12V power supplies and -the processor core voltage. The ADM1025 can monitor a sixth power-supply -voltage by measuring its own VCC. One input (two pins) is dedicated to a -remote temperature-sensing diode and an on-chip temperature sensor allows -ambient temperature to be monitored. - -One specificity of this chip is that the pin 11 can be hardwired in two -different manners. It can act as the +12V power-supply voltage analog -input, or as the a fifth digital entry for the VID reading (bit 4). It's -kind of strange since both are useful, and the reason for designing the -chip that way is obscure at least to me. The bit 5 of the configuration -register can be used to define how the chip is hardwired. Please note that -it is not a choice you have to make as the user. The choice was already -made by your motherboard's maker. If the configuration bit isn't set -properly, you'll have a wrong +12V reading or a wrong VID reading. The way -the driver handles that is to preserve this bit through the initialization -process, assuming that the BIOS set it up properly beforehand. If it turns -out not to be true in some cases, we'll provide a module parameter to force -modes. - -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. diff --git a/Documentation/i2c/chips/adm1026 b/Documentation/i2c/chips/adm1026 deleted file mode 100644 index 473c689..0000000 --- a/Documentation/i2c/chips/adm1026 +++ /dev/null @@ -1,93 +0,0 @@ -Kernel driver adm1026 -===================== - -Supported chips: - * Analog Devices ADM1026 - Prefix: 'adm1026' - Addresses scanned: I2C 0x2c, 0x2d, 0x2e - Datasheet: Publicly available at the Analog Devices website - http://www.analog.com/en/prod/0,,766_825_ADM1026,00.html - -Authors: - Philip Pokorny for Penguin Computing - Justin Thiessen - -Module Parameters ------------------ - -* gpio_input: int array (min = 1, max = 17) - List of GPIO pins (0-16) to program as inputs -* gpio_output: int array (min = 1, max = 17) - List of GPIO pins (0-16) to program as outputs -* gpio_inverted: int array (min = 1, max = 17) - List of GPIO pins (0-16) to program as inverted -* gpio_normal: int array (min = 1, max = 17) - List of GPIO pins (0-16) to program as normal/non-inverted -* gpio_fan: int array (min = 1, max = 8) - List of GPIO pins (0-7) to program as fan tachs - - -Description ------------ - -This driver implements support for the Analog Devices ADM1026. Analog -Devices calls it a "complete thermal system management controller." - -The ADM1026 implements three (3) temperature sensors, 17 voltage sensors, -16 general purpose digital I/O lines, eight (8) fan speed sensors (8-bit), -an analog output and a PWM output along with limit, alarm and mask bits for -all of the above. There is even 8k bytes of EEPROM memory on chip. - -Temperatures are measured in degrees Celsius. There are two external -sensor inputs and one internal sensor. Each sensor has a high and low -limit. If the limit is exceeded, an interrupt (#SMBALERT) can be -generated. The interrupts can be masked. In addition, there are over-temp -limits for each sensor. If this limit is exceeded, the #THERM output will -be asserted. The current temperature and limits have a resolution of 1 -degree. - -Fan rotation speeds are reported in RPM (rotations per minute) but measured -in counts of a 22.5kHz internal clock. Each fan has a high limit which -corresponds to a minimum fan speed. If the limit is exceeded, an interrupt -can be generated. Each fan can be programmed to divide the reference clock -by 1, 2, 4 or 8. Not all RPM values can accurately be represented, so some -rounding is done. With a divider of 8, the slowest measurable speed of a -two pulse per revolution fan is 661 RPM. - -There are 17 voltage sensors. An alarm is triggered if the voltage has -crossed a programmable minimum or maximum limit. Note that minimum in this -case always means 'closest to zero'; this is important for negative voltage -measurements. Several inputs have integrated attenuators so they can measure -higher voltages directly. 3.3V, 5V, 12V, -12V and battery voltage all have -dedicated inputs. There are several inputs scaled to 0-3V full-scale range -for SCSI terminator power. The remaining inputs are not scaled and have -a 0-2.5V full-scale range. A 2.5V or 1.82V reference voltage is provided -for negative voltage measurements. - -If an alarm triggers, it will remain triggered until the hardware register -is read at least once. This means that the cause for the alarm may already -have disappeared! Note that in the current implementation, all hardware -registers are read whenever any data is read (unless it is less than 2.0 -seconds since the last update). This means that you can easily miss -once-only alarms. - -The ADM1026 measures continuously. Analog inputs are measured about 4 -times a second. Fan speed measurement time depends on fan speed and -divisor. It can take as long as 1.5 seconds to measure all fan speeds. - -The ADM1026 has the ability to automatically control fan speed based on the -temperature sensor inputs. Both the PWM output and the DAC output can be -used to control fan speed. Usually only one of these two outputs will be -used. Write the minimum PWM or DAC value to the appropriate control -register. Then set the low temperature limit in the tmin values for each -temperature sensor. The range of control is fixed at 20 °C, and the -largest difference between current and tmin of the temperature sensors sets -the control output. See the datasheet for several example circuits for -controlling fan speed with the PWM and DAC outputs. The fan speed sensors -do not have PWM compensation, so it is probably best to control the fan -voltage from the power lead rather than on the ground lead. - -The datasheet shows an example application with VID signals attached to -GPIO lines. Unfortunately, the chip may not be connected to the VID lines -in this way. The driver assumes that the chips *is* connected this way to -get a VID voltage. diff --git a/Documentation/i2c/chips/adm1031 b/Documentation/i2c/chips/adm1031 deleted file mode 100644 index 130a383..0000000 --- a/Documentation/i2c/chips/adm1031 +++ /dev/null @@ -1,35 +0,0 @@ -Kernel driver adm1031 -===================== - -Supported chips: - * Analog Devices ADM1030 - Prefix: 'adm1030' - Addresses scanned: I2C 0x2c to 0x2e - Datasheet: Publicly available at the Analog Devices website - http://products.analog.com/products/info.asp?product=ADM1030 - - * Analog Devices ADM1031 - Prefix: 'adm1031' - Addresses scanned: I2C 0x2c to 0x2e - Datasheet: Publicly available at the Analog Devices website - http://products.analog.com/products/info.asp?product=ADM1031 - -Authors: - Alexandre d'Alton - Jean Delvare - -Description ------------ - -The ADM1030 and ADM1031 are digital temperature sensors and fan controllers. -They sense their own temperature as well as the temperature of up to one -(ADM1030) or two (ADM1031) external diodes. - -All temperature values are given in degrees Celsius. Resolution is 0.5 -degree for the local temperature, 0.125 degree for the remote temperatures. - -Each temperature channel has its own high and low limits, plus a critical -limit. - -The ADM1030 monitors a single fan speed, while the ADM1031 monitors up to -two. Each fan channel has its own low speed limit. diff --git a/Documentation/i2c/chips/adm9240 b/Documentation/i2c/chips/adm9240 deleted file mode 100644 index 35f618f..0000000 --- a/Documentation/i2c/chips/adm9240 +++ /dev/null @@ -1,177 +0,0 @@ -Kernel driver adm9240 -===================== - -Supported chips: - * Analog Devices ADM9240 - Prefix: 'adm9240' - Addresses scanned: I2C 0x2c - 0x2f - Datasheet: Publicly available at the Analog Devices website - http://www.analog.com/UploadedFiles/Data_Sheets/79857778ADM9240_0.pdf - - * Dallas Semiconductor DS1780 - Prefix: 'ds1780' - Addresses scanned: I2C 0x2c - 0x2f - Datasheet: Publicly available at the Dallas Semiconductor (Maxim) website - http://pdfserv.maxim-ic.com/en/ds/DS1780.pdf - - * National Semiconductor LM81 - Prefix: 'lm81' - Addresses scanned: I2C 0x2c - 0x2f - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/ds.cgi/LM/LM81.pdf - -Authors: - Frodo Looijaard , - Philip Edelbrock , - Michiel Rook , - Grant Coady with guidance - from Jean Delvare - -Interface ---------- -The I2C addresses listed above assume BIOS has not changed the -chip MSB 5-bit address. Each chip reports a unique manufacturer -identification code as well as the chip revision/stepping level. - -Description ------------ -[From ADM9240] The ADM9240 is a complete system hardware monitor for -microprocessor-based systems, providing measurement and limit comparison -of up to four power supplies and two processor core voltages, plus -temperature, two fan speeds and chassis intrusion. Measured values can -be read out via an I2C-compatible serial System Management Bus, and values -for limit comparisons can be programmed in over the same serial bus. The -high speed successive approximation ADC allows frequent sampling of all -analog channels to ensure a fast interrupt response to any out-of-limit -measurement. - -The ADM9240, DS1780 and LM81 are register compatible, the following -details are common to the three chips. Chip differences are described -after this section. - - -Measurements ------------- -The measurement cycle - -The adm9240 driver will take a measurement reading no faster than once -each two seconds. User-space may read sysfs interface faster than the -measurement update rate and will receive cached data from the most -recent measurement. - -ADM9240 has a very fast 320us temperature and voltage measurement cycle -with independent fan speed measurement cycles counting alternating rising -edges of the fan tacho inputs. - -DS1780 measurement cycle is about once per second including fan speed. - -LM81 measurement cycle is about once per 400ms including fan speed. -The LM81 12-bit extended temperature measurement mode is not supported. - -Temperature ------------ -On chip temperature is reported as degrees Celsius as 9-bit signed data -with resolution of 0.5 degrees Celsius. High and low temperature limits -are 8-bit signed data with resolution of one degree Celsius. - -Temperature alarm is asserted once the temperature exceeds the high limit, -and is cleared when the temperature falls below the temp1_max_hyst value. - -Fan Speed ---------- -Two fan tacho inputs are provided, the ADM9240 gates an internal 22.5kHz -clock via a divider to an 8-bit counter. Fan speed (rpm) is calculated by: - -rpm = (22500 * 60) / (count * divider) - -Automatic fan clock divider - - * User sets 0 to fan_min limit - - low speed alarm is disabled - - fan clock divider not changed - - auto fan clock adjuster enabled for valid fan speed reading - - * User sets fan_min limit too low - - low speed alarm is enabled - - fan clock divider set to max - - fan_min set to register value 254 which corresponds - to 664 rpm on adm9240 - - low speed alarm will be asserted if fan speed is - less than minimum measurable speed - - auto fan clock adjuster disabled - - * User sets reasonable fan speed - - low speed alarm is enabled - - fan clock divider set to suit fan_min - - auto fan clock adjuster enabled: adjusts fan_min - - * User sets unreasonably high low fan speed limit - - resolution of the low speed limit may be reduced - - alarm will be asserted - - auto fan clock adjuster enabled: adjusts fan_min - - * fan speed may be displayed as zero until the auto fan clock divider - adjuster brings fan speed clock divider back into chip measurement - range, this will occur within a few measurement cycles. - -Analog Output -------------- -An analog output provides a 0 to 1.25 volt signal intended for an external -fan speed amplifier circuit. The analog output is set to maximum value on -power up or reset. This doesn't do much on the test Intel SE440BX-2. - -Voltage Monitor - -Voltage (IN) measurement is internally scaled: - - nr label nominal maximum resolution - mV mV mV - 0 +2.5V 2500 3320 13.0 - 1 Vccp1 2700 3600 14.1 - 2 +3.3V 3300 4380 17.2 - 3 +5V 5000 6640 26.0 - 4 +12V 12000 15940 62.5 - 5 Vccp2 2700 3600 14.1 - -The reading is an unsigned 8-bit value, nominal voltage measurement is -represented by a reading of 192, being 3/4 of the measurement range. - -An alarm is asserted for any voltage going below or above the set limits. - -The driver reports and accepts voltage limits scaled to the above table. - -VID Monitor ------------ -The chip has five inputs to read the 5-bit VID and reports the mV value -based on detected CPU type. - -Chassis Intrusion ------------------ -An alarm is asserted when the CI pin goes active high. The ADM9240 -Datasheet has an example of an external temperature sensor driving -this pin. On an Intel SE440BX-2 the Chassis Intrusion header is -connected to a normally open switch. - -The ADM9240 provides an internal open drain on this line, and may output -a 20 ms active low pulse to reset an external Chassis Intrusion latch. - -Clear the CI latch by writing value 1 to the sysfs chassis_clear file. - -Alarm flags reported as 16-bit word - - bit label comment - --- ------------- -------------------------- - 0 +2.5 V_Error high or low limit exceeded - 1 VCCP_Error high or low limit exceeded - 2 +3.3 V_Error high or low limit exceeded - 3 +5 V_Error high or low limit exceeded - 4 Temp_Error temperature error - 6 FAN1_Error fan low limit exceeded - 7 FAN2_Error fan low limit exceeded - 8 +12 V_Error high or low limit exceeded - 9 VCCP2_Error high or low limit exceeded - 12 Chassis_Error CI pin went high - -Remaining bits are reserved and thus undefined. It is important to note -that alarm bits may be cleared on read, user-space may latch alarms and -provide the end-user with a method to clear alarm memory. diff --git a/Documentation/i2c/chips/asb100 b/Documentation/i2c/chips/asb100 deleted file mode 100644 index ab7365e..0000000 --- a/Documentation/i2c/chips/asb100 +++ /dev/null @@ -1,72 +0,0 @@ -Kernel driver asb100 -==================== - -Supported Chips: - * Asus ASB100 and ASB100-A "Bach" - Prefix: 'asb100' - Addresses scanned: I2C 0x2d - Datasheet: none released - -Author: Mark M. Hoffman - -Description ------------ - -This driver implements support for the Asus ASB100 and ASB100-A "Bach". -These are custom ASICs available only on Asus mainboards. Asus refuses to -supply a datasheet for these chips. Thanks go to many people who helped -investigate their hardware, including: - -Vitaly V. Bursov -Alexander van Kaam (author of MBM for Windows) -Bertrik Sikken - -The ASB100 implements seven voltage sensors, three fan rotation speed -sensors, four temperature sensors, VID lines and alarms. In addition to -these, the ASB100-A also implements a single PWM controller for fans 2 and -3 (i.e. one setting controls both.) If you have a plain ASB100, the PWM -controller will simply not work (or maybe it will for you... it doesn't for -me). - -Temperatures are measured and reported in degrees Celsius. - -Fan speeds are reported in RPM (rotations per minute). An alarm is -triggered if the rotation speed has dropped below a programmable limit. - -Voltage sensors (also known as IN sensors) report values in volts. - -The VID lines encode the core voltage value: the voltage level your -processor should work with. This is hardcoded by the mainboard and/or -processor itself. It is a value in volts. - -Alarms: (TODO question marks indicate may or may not work) - -0x0001 => in0 (?) -0x0002 => in1 (?) -0x0004 => in2 -0x0008 => in3 -0x0010 => temp1 (1) -0x0020 => temp2 -0x0040 => fan1 -0x0080 => fan2 -0x0100 => in4 -0x0200 => in5 (?) (2) -0x0400 => in6 (?) (2) -0x0800 => fan3 -0x1000 => chassis switch -0x2000 => temp3 - -Alarm Notes: - -(1) This alarm will only trigger if the hysteresis value is 127C. -I.e. it behaves the same as w83781d. - -(2) The min and max registers for these values appear to -be read-only or otherwise stuck at 0x00. - -TODO: -* Experiment with fan divisors > 8. -* Experiment with temp. sensor types. -* Are there really 13 voltage inputs? Probably not... -* Cleanups, no doubt... - diff --git a/Documentation/i2c/chips/ds1621 b/Documentation/i2c/chips/ds1621 deleted file mode 100644 index 1fee6f1..0000000 --- a/Documentation/i2c/chips/ds1621 +++ /dev/null @@ -1,108 +0,0 @@ -Kernel driver ds1621 -==================== - -Supported chips: - * Dallas Semiconductor DS1621 - Prefix: 'ds1621' - Addresses scanned: I2C 0x48 - 0x4f - Datasheet: Publicly available at the Dallas Semiconductor website - http://www.dalsemi.com/ - * Dallas Semiconductor DS1625 - Prefix: 'ds1621' - Addresses scanned: I2C 0x48 - 0x4f - Datasheet: Publicly available at the Dallas Semiconductor website - http://www.dalsemi.com/ - -Authors: - Christian W. Zuckschwerdt - valuable contributions by Jan M. Sendler - ported to 2.6 by Aurelien Jarno - with the help of Jean Delvare - -Module Parameters ------------------- - -* polarity int - Output's polarity: 0 = active high, 1 = active low - -Description ------------ - -The DS1621 is a (one instance) digital thermometer and thermostat. It has -both high and low temperature limits which can be user defined (i.e. -programmed into non-volatile on-chip registers). Temperature range is -55 -degree Celsius to +125 in 0.5 increments. You may convert this into a -Fahrenheit range of -67 to +257 degrees with 0.9 steps. If polarity -parameter is not provided, original value is used. - -As for the thermostat, behavior can also be programmed using the polarity -toggle. On the one hand ("heater"), the thermostat output of the chip, -Tout, will trigger when the low limit temperature is met or underrun and -stays high until the high limit is met or exceeded. On the other hand -("cooler"), vice versa. That way "heater" equals "active low", whereas -"conditioner" equals "active high". Please note that the DS1621 data sheet -is somewhat misleading in this point since setting the polarity bit does -not simply invert Tout. - -A second thing is that, during extensive testing, Tout showed a tolerance -of up to +/- 0.5 degrees even when compared against precise temperature -readings. Be sure to have a high vs. low temperature limit gap of al least -1.0 degree Celsius to avoid Tout "bouncing", though! - -As for alarms, you can read the alarm status of the DS1621 via the 'alarms' -/sys file interface. The result consists mainly of bit 6 and 5 of the -configuration register of the chip; bit 6 (0x40 or 64) is the high alarm -bit and bit 5 (0x20 or 32) the low one. These bits are set when the high or -low limits are met or exceeded and are reset by the module as soon as the -respective temperature ranges are left. - -The alarm registers are in no way suitable to find out about the actual -status of Tout. They will only tell you about its history, whether or not -any of the limits have ever been met or exceeded since last power-up or -reset. Be aware: When testing, it showed that the status of Tout can change -with neither of the alarms set. - -Temperature conversion of the DS1621 takes up to 1000ms; internal access to -non-volatile registers may last for 10ms or below. - -High Accuracy Temperature Reading ---------------------------------- - -As said before, the temperature issued via the 9-bit i2c-bus data is -somewhat arbitrary. Internally, the temperature conversion is of a -different kind that is explained (not so...) well in the DS1621 data sheet. -To cut the long story short: Inside the DS1621 there are two oscillators, -both of them biassed by a temperature coefficient. - -Higher resolution of the temperature reading can be achieved using the -internal projection, which means taking account of REG_COUNT and REG_SLOPE -(the driver manages them): - -Taken from Dallas Semiconductors App Note 068: 'Increasing Temperature -Resolution on the DS1620' and App Note 105: 'High Resolution Temperature -Measurement with Dallas Direct-to-Digital Temperature Sensors' - -- Read the 9-bit temperature and strip the LSB (Truncate the .5 degs) -- The resulting value is TEMP_READ. -- Then, read REG_COUNT. -- And then, REG_SLOPE. - - TEMP = TEMP_READ - 0.25 + ((REG_SLOPE - REG_COUNT) / REG_SLOPE) - -Note that this is what the DONE bit in the DS1621 configuration register is -good for: Internally, one temperature conversion takes up to 1000ms. Before -that conversion is complete you will not be able to read valid things out -of REG_COUNT and REG_SLOPE. The DONE bit, as you may have guessed by now, -tells you whether the conversion is complete ("done", in plain English) and -thus, whether the values you read are good or not. - -The DS1621 has two modes of operation: "Continuous" conversion, which can -be understood as the default stand-alone mode where the chip gets the -temperature and controls external devices via its Tout pin or tells other -i2c's about it if they care. The other mode is called "1SHOT", that means -that it only figures out about the temperature when it is explicitly told -to do so; this can be seen as power saving mode. - -Now if you want to read REG_COUNT and REG_SLOPE, you have to either stop -the continuous conversions until the contents of these registers are valid, -or, in 1SHOT mode, you have to have one conversion made. diff --git a/Documentation/i2c/chips/fscher b/Documentation/i2c/chips/fscher deleted file mode 100644 index 6403165..0000000 --- a/Documentation/i2c/chips/fscher +++ /dev/null @@ -1,169 +0,0 @@ -Kernel driver fscher -==================== - -Supported chips: - * Fujitsu-Siemens Hermes chip - Prefix: 'fscher' - Addresses scanned: I2C 0x73 - -Authors: - Reinhard Nissl based on work - from Hermann Jung , - Frodo Looijaard , - Philip Edelbrock - -Description ------------ - -This driver implements support for the Fujitsu-Siemens Hermes chip. It is -described in the 'Register Set Specification BMC Hermes based Systemboard' -from Fujitsu-Siemens. - -The Hermes chip implements a hardware-based system management, e.g. for -controlling fan speed and core voltage. There is also a watchdog counter on -the chip which can trigger an alarm and even shut the system down. - -The chip provides three temperature values (CPU, motherboard and -auxiliary), three voltage values (+12V, +5V and battery) and three fans -(power supply, CPU and auxiliary). - -Temperatures are measured in degrees Celsius. The resolution is 1 degree. - -Fan rotation speeds are reported in RPM (rotations per minute). The value -can be divided by a programmable divider (1, 2 or 4) which is stored on -the chip. - -Voltage sensors (also known as "in" sensors) report their values in volts. - -All values are reported as final values from the driver. There is no need -for further calculations. - - -Detailed description --------------------- - -Below you'll find a single line description of all the bit values. With -this information, you're able to decode e. g. alarms, wdog, etc. To make -use of the watchdog, you'll need to set the watchdog time and enable the -watchdog. After that it is necessary to restart the watchdog time within -the specified period of time, or a system reset will occur. - -* revision - READING & 0xff = 0x??: HERMES revision identification - -* alarms - READING & 0x80 = 0x80: CPU throttling active - READING & 0x80 = 0x00: CPU running at full speed - - READING & 0x10 = 0x10: software event (see control:1) - READING & 0x10 = 0x00: no software event - - READING & 0x08 = 0x08: watchdog event (see wdog:2) - READING & 0x08 = 0x00: no watchdog event - - READING & 0x02 = 0x02: thermal event (see temp*:1) - READING & 0x02 = 0x00: no thermal event - - READING & 0x01 = 0x01: fan event (see fan*:1) - READING & 0x01 = 0x00: no fan event - - READING & 0x13 ! 0x00: ALERT LED is flashing - -* control - READING & 0x01 = 0x01: software event - READING & 0x01 = 0x00: no software event - - WRITING & 0x01 = 0x01: set software event - WRITING & 0x01 = 0x00: clear software event - -* watchdog_control - READING & 0x80 = 0x80: power off on watchdog event while thermal event - READING & 0x80 = 0x00: watchdog power off disabled (just system reset enabled) - - READING & 0x40 = 0x40: watchdog timebase 60 seconds (see also wdog:1) - READING & 0x40 = 0x00: watchdog timebase 2 seconds - - READING & 0x10 = 0x10: watchdog enabled - READING & 0x10 = 0x00: watchdog disabled - - WRITING & 0x80 = 0x80: enable "power off on watchdog event while thermal event" - WRITING & 0x80 = 0x00: disable "power off on watchdog event while thermal event" - - WRITING & 0x40 = 0x40: set watchdog timebase to 60 seconds - WRITING & 0x40 = 0x00: set watchdog timebase to 2 seconds - - WRITING & 0x20 = 0x20: disable watchdog - - WRITING & 0x10 = 0x10: enable watchdog / restart watchdog time - -* watchdog_state - READING & 0x02 = 0x02: watchdog system reset occurred - READING & 0x02 = 0x00: no watchdog system reset occurred - - WRITING & 0x02 = 0x02: clear watchdog event - -* watchdog_preset - READING & 0xff = 0x??: configured watch dog time in units (see wdog:3 0x40) - - WRITING & 0xff = 0x??: configure watch dog time in units - -* in* (0: +5V, 1: +12V, 2: onboard 3V battery) - READING: actual voltage value - -* temp*_status (1: CPU sensor, 2: onboard sensor, 3: auxiliary sensor) - READING & 0x02 = 0x02: thermal event (overtemperature) - READING & 0x02 = 0x00: no thermal event - - READING & 0x01 = 0x01: sensor is working - READING & 0x01 = 0x00: sensor is faulty - - WRITING & 0x02 = 0x02: clear thermal event - -* temp*_input (1: CPU sensor, 2: onboard sensor, 3: auxiliary sensor) - READING: actual temperature value - -* fan*_status (1: power supply fan, 2: CPU fan, 3: auxiliary fan) - READING & 0x04 = 0x04: fan event (fan fault) - READING & 0x04 = 0x00: no fan event - - WRITING & 0x04 = 0x04: clear fan event - -* fan*_div (1: power supply fan, 2: CPU fan, 3: auxiliary fan) - Divisors 2,4 and 8 are supported, both for reading and writing - -* fan*_pwm (1: power supply fan, 2: CPU fan, 3: auxiliary fan) - READING & 0xff = 0x00: fan may be switched off - READING & 0xff = 0x01: fan must run at least at minimum speed (supply: 6V) - READING & 0xff = 0xff: fan must run at maximum speed (supply: 12V) - READING & 0xff = 0x??: fan must run at least at given speed (supply: 6V..12V) - - WRITING & 0xff = 0x00: fan may be switched off - WRITING & 0xff = 0x01: fan must run at least at minimum speed (supply: 6V) - WRITING & 0xff = 0xff: fan must run at maximum speed (supply: 12V) - WRITING & 0xff = 0x??: fan must run at least at given speed (supply: 6V..12V) - -* fan*_input (1: power supply fan, 2: CPU fan, 3: auxiliary fan) - READING: actual RPM value - - -Limitations ------------ - -* Measuring fan speed -It seems that the chip counts "ripples" (typical fans produce 2 ripples per -rotation while VERAX fans produce 18) in a 9-bit register. This register is -read out every second, then the ripple prescaler (2, 4 or 8) is applied and -the result is stored in the 8 bit output register. Due to the limitation of -the counting register to 9 bits, it is impossible to measure a VERAX fan -properly (even with a prescaler of 8). At its maximum speed of 3500 RPM the -fan produces 1080 ripples per second which causes the counting register to -overflow twice, leading to only 186 RPM. - -* Measuring input voltages -in2 ("battery") reports the voltage of the onboard lithium battery and not -+3.3V from the power supply. - -* Undocumented features -Fujitsu-Siemens Computers has not documented all features of the chip so -far. Their software, System Guard, shows that there are a still some -features which cannot be controlled by this implementation. diff --git a/Documentation/i2c/chips/gl518sm b/Documentation/i2c/chips/gl518sm deleted file mode 100644 index ce08818..0000000 --- a/Documentation/i2c/chips/gl518sm +++ /dev/null @@ -1,74 +0,0 @@ -Kernel driver gl518sm -===================== - -Supported chips: - * Genesys Logic GL518SM release 0x00 - Prefix: 'gl518sm' - Addresses scanned: I2C 0x2c and 0x2d - Datasheet: http://www.genesyslogic.com/pdf - * Genesys Logic GL518SM release 0x80 - Prefix: 'gl518sm' - Addresses scanned: I2C 0x2c and 0x2d - Datasheet: http://www.genesyslogic.com/pdf - -Authors: - Frodo Looijaard , - Kyösti Mälkki - Hong-Gunn Chew - Jean Delvare - -Description ------------ - -IMPORTANT: - -For the revision 0x00 chip, the in0, in1, and in2 values (+5V, +3V, -and +12V) CANNOT be read. This is a limitation of the chip, not the driver. - -This driver supports the Genesys Logic GL518SM chip. There are at least -two revision of this chip, which we call revision 0x00 and 0x80. Revision -0x80 chips support the reading of all voltages and revision 0x00 only -for VIN3. - -The GL518SM implements one temperature sensor, two fan rotation speed -sensors, and four voltage sensors. It can report alarms through the -computer speakers. - -Temperatures are measured in degrees Celsius. An alarm goes off while the -temperature is above the over temperature limit, and has not yet dropped -below the hysteresis limit. The alarm always reflects the current -situation. Measurements are guaranteed between -10 degrees and +110 -degrees, with a accuracy of +/-3 degrees. - -Rotation speeds are reported in RPM (rotations per minute). An alarm is -triggered if the rotation speed has dropped below a programmable limit. In -case when you have selected to turn fan1 off, no fan1 alarm is triggered. - -Fan readings can be divided by a programmable divider (1, 2, 4 or 8) to -give the readings more range or accuracy. Not all RPM values can -accurately be represented, so some rounding is done. With a divider -of 2, the lowest representable value is around 1900 RPM. - -Voltage sensors (also known as VIN sensors) report their values in volts. -An alarm is triggered if the voltage has crossed a programmable minimum or -maximum limit. Note that minimum in this case always means 'closest to -zero'; this is important for negative voltage measurements. The VDD input -measures voltages between 0.000 and 5.865 volt, with a resolution of 0.023 -volt. The other inputs measure voltages between 0.000 and 4.845 volt, with -a resolution of 0.019 volt. Note that revision 0x00 chips do not support -reading the current voltage of any input except for VIN3; limit setting and -alarms work fine, though. - -When an alarm is triggered, you can be warned by a beeping signal through your -computer speaker. It is possible to enable all beeping globally, or only the -beeping for some alarms. - -If an alarm triggers, it will remain triggered until the hardware register -is read at least once (except for temperature alarms). This means that the -cause for the alarm may already have disappeared! Note that in the current -implementation, all hardware registers are read whenever any data is read -(unless it is less than 1.5 seconds since the last update). This means that -you can easily miss once-only alarms. - -The GL518SM only updates its values each 1.5 seconds; reading it more often -will do no harm, but will return 'old' values. diff --git a/Documentation/i2c/chips/it87 b/Documentation/i2c/chips/it87 deleted file mode 100644 index 0d01950..0000000 --- a/Documentation/i2c/chips/it87 +++ /dev/null @@ -1,96 +0,0 @@ -Kernel driver it87 -================== - -Supported chips: - * IT8705F - Prefix: 'it87' - Addresses scanned: from Super I/O config space, or default ISA 0x290 (8 I/O ports) - Datasheet: Publicly available at the ITE website - http://www.ite.com.tw/ - * IT8712F - Prefix: 'it8712' - Addresses scanned: I2C 0x28 - 0x2f - from Super I/O config space, or default ISA 0x290 (8 I/O ports) - Datasheet: Publicly available at the ITE website - http://www.ite.com.tw/ - * SiS950 [clone of IT8705F] - Prefix: 'sis950' - Addresses scanned: from Super I/O config space, or default ISA 0x290 (8 I/O ports) - Datasheet: No longer be available - -Author: Christophe Gauthron - - -Module Parameters ------------------ - -* update_vbat: int - - 0 if vbat should report power on value, 1 if vbat should be updated after - each read. Default is 0. On some boards the battery voltage is provided - by either the battery or the onboard power supply. Only the first reading - at power on will be the actual battery voltage (which the chip does - automatically). On other boards the battery voltage is always fed to - the chip so can be read at any time. Excessive reading may decrease - battery life but no information is given in the datasheet. - -* fix_pwm_polarity int - - Force PWM polarity to active high (DANGEROUS). Some chips are - misconfigured by BIOS - PWM values would be inverted. This option tries - to fix this. Please contact your BIOS manufacturer and ask him for fix. - -Description ------------ - -This driver implements support for the IT8705F, IT8712F and SiS950 chips. - -This driver also supports IT8712F, which adds SMBus access, and a VID -input, used to report the Vcore voltage of the Pentium processor. -The IT8712F additionally features VID inputs. - -These chips are 'Super I/O chips', supporting floppy disks, infrared ports, -joysticks and other miscellaneous stuff. For hardware monitoring, they -include an 'environment controller' with 3 temperature sensors, 3 fan -rotation speed sensors, 8 voltage sensors, and associated alarms. - -Temperatures are measured in degrees Celsius. An alarm is triggered once -when the Overtemperature Shutdown limit is crossed. - -Fan rotation speeds are reported in RPM (rotations per minute). An alarm is -triggered if the rotation speed has dropped below a programmable limit. Fan -readings can be divided by a programmable divider (1, 2, 4 or 8) to give the -readings more range or accuracy. Not all RPM values can accurately be -represented, so some rounding is done. With a divider of 2, the lowest -representable value is around 2600 RPM. - -Voltage sensors (also known as IN sensors) report their values in volts. An -alarm is triggered if the voltage has crossed a programmable minimum or -maximum limit. Note that minimum in this case always means 'closest to -zero'; this is important for negative voltage measurements. All voltage -inputs can measure voltages between 0 and 4.08 volts, with a resolution of -0.016 volt. The battery voltage in8 does not have limit registers. - -The VID lines (IT8712F only) encode the core voltage value: the voltage -level your processor should work with. This is hardcoded by the mainboard -and/or processor itself. It is a value in volts. - -If an alarm triggers, it will remain triggered until the hardware register -is read at least once. This means that the cause for the alarm may already -have disappeared! Note that in the current implementation, all hardware -registers are read whenever any data is read (unless it is less than 1.5 -seconds since the last update). This means that you can easily miss -once-only alarms. - -The IT87xx only updates its values each 1.5 seconds; reading it more often -will do no harm, but will return 'old' values. - -To change sensor N to a thermistor, 'echo 2 > tempN_type' where N is 1, 2, -or 3. To change sensor N to a thermal diode, 'echo 3 > tempN_type'. -Give 0 for unused sensor. Any other value is invalid. To configure this at -startup, consult lm_sensors's /etc/sensors.conf. (2 = thermistor; -3 = thermal diode) - -The fan speed control features are limited to manual PWM mode. Automatic -"Smart Guardian" mode control handling is not implemented. However -if you want to go for "manual mode" just write 1 to pwmN_enable. diff --git a/Documentation/i2c/chips/lm63 b/Documentation/i2c/chips/lm63 deleted file mode 100644 index 31660bf..0000000 --- a/Documentation/i2c/chips/lm63 +++ /dev/null @@ -1,57 +0,0 @@ -Kernel driver lm63 -================== - -Supported chips: - * National Semiconductor LM63 - Prefix: 'lm63' - Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/pf/LM/LM63.html - -Author: Jean Delvare - -Thanks go to Tyan and especially Alex Buckingham for setting up a remote -access to their S4882 test platform for this driver. - http://www.tyan.com/ - -Description ------------ - -The LM63 is a digital temperature sensor with integrated fan monitoring -and control. - -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. - -An explanation from National Semiconductor: The two lower bits of the read -value have to be masked out. The value is still 16 bit in width. - -All temperature values are given in degrees Celsius. Resolution is 1.0 -degree for the local temperature, 0.125 degree for the remote temperature. - -The fan speed is measured using a tachometer. Contrary to most chips which -store the value in an 8-bit register and have a selectable clock divider -to make sure that the result will fit in the register, the LM63 uses 16-bit -value for measuring the speed of the fan. It can measure fan speeds down to -83 RPM, at least in theory. - -Note that the pin used for fan monitoring is shared with an alert out -function. Depending on how the board designer wanted to use the chip, fan -speed monitoring will or will not be possible. The proper chip configuration -is left to the BIOS, and the driver will blindly trust it. - -A PWM output can be used to control the speed of the fan. The LM63 has two -PWM modes: manual and automatic. Automatic mode is not fully implemented yet -(you cannot define your custom PWM/temperature curve), and mode change isn't -supported either. - -The lm63 driver will not update its values more frequently than every -second; reading them more often will do no harm, but will return 'old' -values. - diff --git a/Documentation/i2c/chips/lm75 b/Documentation/i2c/chips/lm75 deleted file mode 100644 index 8e6356f..0000000 --- a/Documentation/i2c/chips/lm75 +++ /dev/null @@ -1,65 +0,0 @@ -Kernel driver lm75 -================== - -Supported chips: - * National Semiconductor LM75 - Prefix: 'lm75' - Addresses scanned: I2C 0x48 - 0x4f - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/ - * Dallas Semiconductor DS75 - Prefix: 'lm75' - Addresses scanned: I2C 0x48 - 0x4f - Datasheet: Publicly available at the Dallas Semiconductor website - http://www.maxim-ic.com/ - * Dallas Semiconductor DS1775 - Prefix: 'lm75' - Addresses scanned: I2C 0x48 - 0x4f - Datasheet: Publicly available at the Dallas Semiconductor website - http://www.maxim-ic.com/ - * Maxim MAX6625, MAX6626 - Prefix: 'lm75' - Addresses scanned: I2C 0x48 - 0x4b - Datasheet: Publicly available at the Maxim website - http://www.maxim-ic.com/ - * Microchip (TelCom) TCN75 - Prefix: 'lm75' - Addresses scanned: I2C 0x48 - 0x4f - Datasheet: Publicly available at the Microchip website - http://www.microchip.com/ - -Author: Frodo Looijaard - -Description ------------ - -The LM75 implements one temperature sensor. Limits can be set through the -Overtemperature Shutdown register and Hysteresis register. Each value can be -set and read to half-degree accuracy. -An alarm is issued (usually to a connected LM78) when the temperature -gets higher then the Overtemperature Shutdown value; it stays on until -the temperature falls below the Hysteresis value. -All temperatures are in degrees Celsius, and are guaranteed within a -range of -55 to +125 degrees. - -The LM75 only updates its values each 1.5 seconds; reading it more often -will do no harm, but will return 'old' values. - -The LM75 is usually used in combination with LM78-like chips, to measure -the temperature of the processor(s). - -The DS75, DS1775, MAX6625, and MAX6626 are supported as well. -They are not distinguished from an LM75. While most of these chips -have three additional bits of accuracy (12 vs. 9 for the LM75), -the additional bits are not supported. Not only that, but these chips will -not be detected if not in 9-bit precision mode (use the force parameter if -needed). - -The TCN75 is supported as well, and is not distinguished from an LM75. - -The LM75 is essentially an industry standard; there may be other -LM75 clones not listed here, with or without various enhancements, -that are supported. - -The LM77 is not supported, contrary to what we pretended for a long time. -Both chips are simply not compatible, value encoding differs. diff --git a/Documentation/i2c/chips/lm77 b/Documentation/i2c/chips/lm77 deleted file mode 100644 index 57c3a46..0000000 --- a/Documentation/i2c/chips/lm77 +++ /dev/null @@ -1,22 +0,0 @@ -Kernel driver lm77 -================== - -Supported chips: - * National Semiconductor LM77 - Prefix: 'lm77' - Addresses scanned: I2C 0x48 - 0x4b - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/ - -Author: Andras BALI - -Description ------------ - -The LM77 implements one temperature sensor. The temperature -sensor incorporates a band-gap type temperature sensor, -10-bit ADC, and a digital comparator with user-programmable upper -and lower limit values. - -Limits can be set through the Overtemperature Shutdown register and -Hysteresis register. diff --git a/Documentation/i2c/chips/lm78 b/Documentation/i2c/chips/lm78 deleted file mode 100644 index 357086e..0000000 --- a/Documentation/i2c/chips/lm78 +++ /dev/null @@ -1,82 +0,0 @@ -Kernel driver lm78 -================== - -Supported chips: - * National Semiconductor LM78 - Prefix: 'lm78' - Addresses scanned: I2C 0x20 - 0x2f, ISA 0x290 (8 I/O ports) - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/ - * National Semiconductor LM78-J - Prefix: 'lm78-j' - Addresses scanned: I2C 0x20 - 0x2f, ISA 0x290 (8 I/O ports) - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/ - * National Semiconductor LM79 - Prefix: 'lm79' - Addresses scanned: I2C 0x20 - 0x2f, ISA 0x290 (8 I/O ports) - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/ - -Author: Frodo Looijaard - -Description ------------ - -This driver implements support for the National Semiconductor LM78, LM78-J -and LM79. They are described as 'Microprocessor System Hardware Monitors'. - -There is almost no difference between the three supported chips. Functionally, -the LM78 and LM78-J are exactly identical. The LM79 has one more VID line, -which is used to report the lower voltages newer Pentium processors use. -From here on, LM7* means either of these three types. - -The LM7* implements one temperature sensor, three fan rotation speed sensors, -seven voltage sensors, VID lines, alarms, and some miscellaneous stuff. - -Temperatures are measured in degrees Celsius. An alarm is triggered once -when the Overtemperature Shutdown limit is crossed; it is triggered again -as soon as it drops below the Hysteresis value. A more useful behavior -can be found by setting the Hysteresis value to +127 degrees Celsius; in -this case, alarms are issued during all the time when the actual temperature -is above the Overtemperature Shutdown value. Measurements are guaranteed -between -55 and +125 degrees, with a resolution of 1 degree. - -Fan rotation speeds are reported in RPM (rotations per minute). An alarm is -triggered if the rotation speed has dropped below a programmable limit. Fan -readings can be divided by a programmable divider (1, 2, 4 or 8) to give -the readings more range or accuracy. Not all RPM values can accurately be -represented, so some rounding is done. With a divider of 2, the lowest -representable value is around 2600 RPM. - -Voltage sensors (also known as IN sensors) report their values in volts. -An alarm is triggered if the voltage has crossed a programmable minimum -or maximum limit. Note that minimum in this case always means 'closest to -zero'; this is important for negative voltage measurements. All voltage -inputs can measure voltages between 0 and 4.08 volts, with a resolution -of 0.016 volt. - -The VID lines encode the core voltage value: the voltage level your processor -should work with. This is hardcoded by the mainboard and/or processor itself. -It is a value in volts. When it is unconnected, you will often find the -value 3.50 V here. - -In addition to the alarms described above, there are a couple of additional -ones. There is a BTI alarm, which gets triggered when an external chip has -crossed its limits. Usually, this is connected to all LM75 chips; if at -least one crosses its limits, this bit gets set. The CHAS alarm triggers -if your computer case is open. The FIFO alarms should never trigger; it -indicates an internal error. The SMI_IN alarm indicates some other chip -has triggered an SMI interrupt. As we do not use SMI interrupts at all, -this condition usually indicates there is a problem with some other -device. - -If an alarm triggers, it will remain triggered until the hardware register -is read at least once. This means that the cause for the alarm may -already have disappeared! Note that in the current implementation, all -hardware registers are read whenever any data is read (unless it is less -than 1.5 seconds since the last update). This means that you can easily -miss once-only alarms. - -The LM7* only updates its values each 1.5 seconds; reading it more often -will do no harm, but will return 'old' values. diff --git a/Documentation/i2c/chips/lm80 b/Documentation/i2c/chips/lm80 deleted file mode 100644 index cb5b407..0000000 --- a/Documentation/i2c/chips/lm80 +++ /dev/null @@ -1,56 +0,0 @@ -Kernel driver lm80 -================== - -Supported chips: - * National Semiconductor LM80 - Prefix: 'lm80' - Addresses scanned: I2C 0x28 - 0x2f - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/ - -Authors: - Frodo Looijaard , - Philip Edelbrock - -Description ------------ - -This driver implements support for the National Semiconductor LM80. -It is described as a 'Serial Interface ACPI-Compatible Microprocessor -System Hardware Monitor'. - -The LM80 implements one temperature sensor, two fan rotation speed sensors, -seven voltage sensors, alarms, and some miscellaneous stuff. - -Temperatures are measured in degrees Celsius. There are two sets of limits -which operate independently. When the HOT Temperature Limit is crossed, -this will cause an alarm that will be reasserted until the temperature -drops below the HOT Hysteresis. The Overtemperature Shutdown (OS) limits -should work in the same way (but this must be checked; the datasheet -is unclear about this). Measurements are guaranteed between -55 and -+125 degrees. The current temperature measurement has a resolution of -0.0625 degrees; the limits have a resolution of 1 degree. - -Fan rotation speeds are reported in RPM (rotations per minute). An alarm is -triggered if the rotation speed has dropped below a programmable limit. Fan -readings can be divided by a programmable divider (1, 2, 4 or 8) to give -the readings more range or accuracy. Not all RPM values can accurately be -represented, so some rounding is done. With a divider of 2, the lowest -representable value is around 2600 RPM. - -Voltage sensors (also known as IN sensors) report their values in volts. -An alarm is triggered if the voltage has crossed a programmable minimum -or maximum limit. Note that minimum in this case always means 'closest to -zero'; this is important for negative voltage measurements. All voltage -inputs can measure voltages between 0 and 2.55 volts, with a resolution -of 0.01 volt. - -If an alarm triggers, it will remain triggered until the hardware register -is read at least once. This means that the cause for the alarm may -already have disappeared! Note that in the current implementation, all -hardware registers are read whenever any data is read (unless it is less -than 2.0 seconds since the last update). This means that you can easily -miss once-only alarms. - -The LM80 only updates its values each 1.5 seconds; reading it more often -will do no harm, but will return 'old' values. diff --git a/Documentation/i2c/chips/lm83 b/Documentation/i2c/chips/lm83 deleted file mode 100644 index 061d9ed..0000000 --- a/Documentation/i2c/chips/lm83 +++ /dev/null @@ -1,76 +0,0 @@ -Kernel driver lm83 -================== - -Supported chips: - * National Semiconductor LM83 - Prefix: 'lm83' - Addresses scanned: I2C 0x18 - 0x1a, 0x29 - 0x2b, 0x4c - 0x4e - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/pf/LM/LM83.html - - -Author: Jean Delvare - -Description ------------ - -The LM83 is a digital temperature sensor. It senses its own temperature as -well as the temperature of up to three external diodes. It is compatible -with many other devices such as the LM84 and all other ADM1021 clones. -The main difference between the LM83 and the LM84 in that the later can -only sense the temperature of one external diode. - -Using the adm1021 driver for a LM83 should work, but only two temperatures -will be reported instead of four. - -The LM83 is only found on a handful of motherboards. Both a confirmed -list and an unconfirmed list follow. If you can confirm or infirm the -fact that any of these motherboards do actually have an LM83, please -contact us. Note that the LM90 can easily be misdetected as a LM83. - -Confirmed motherboards: - SBS P014 - -Unconfirmed motherboards: - Gigabyte GA-8IK1100 - Iwill MPX2 - Soltek SL-75DRV5 - -The driver has been successfully tested by Magnus Forsström, who I'd -like to thank here. More testers will be of course welcome. - -The fact that the LM83 is only scarcely used can be easily explained. -Most motherboards come with more than just temperature sensors for -health monitoring. They also have voltage and fan rotation speed -sensors. This means that temperature-only chips are usually used as -secondary chips coupled with another chip such as an IT8705F or similar -chip, which provides more features. Since systems usually need three -temperature sensors (motherboard, processor, power supply) and primary -chips provide some temperature sensors, the secondary chip, if needed, -won't have to handle more than two temperatures. Thus, ADM1021 clones -are sufficient, and there is no need for a four temperatures sensor -chip such as the LM83. The only case where using an LM83 would make -sense is on SMP systems, such as the above-mentioned Iwill MPX2, -because you want an additional temperature sensor for each additional -CPU. - -On the SBS P014, this is different, since the LM83 is the only hardware -monitoring chipset. One temperature sensor is used for the motherboard -(actually measuring the LM83's own temperature), one is used for the -CPU. The two other sensors must be used to measure the temperature of -two other points of the motherboard. We suspect these points to be the -north and south bridges, but this couldn't be confirmed. - -All temperature values are given in degrees Celsius. Local temperature -is given within a range of 0 to +85 degrees. Remote temperatures are -given within a range of 0 to +125 degrees. Resolution is 1.0 degree, -accuracy is guaranteed to 3.0 degrees (see the datasheet for more -details). - -Each sensor has its own high limit, but the critical limit is common to -all four sensors. There is no hysteresis mechanism as found on most -recent temperature sensors. - -The lm83 driver will not update its values more frequently than every -other second; reading them more often will do no harm, but will return -'old' values. diff --git a/Documentation/i2c/chips/lm85 b/Documentation/i2c/chips/lm85 deleted file mode 100644 index 9549237..0000000 --- a/Documentation/i2c/chips/lm85 +++ /dev/null @@ -1,221 +0,0 @@ -Kernel driver lm85 -================== - -Supported chips: - * National Semiconductor LM85 (B and C versions) - Prefix: 'lm85' - Addresses scanned: I2C 0x2c, 0x2d, 0x2e - Datasheet: http://www.national.com/pf/LM/LM85.html - * Analog Devices ADM1027 - Prefix: 'adm1027' - Addresses scanned: I2C 0x2c, 0x2d, 0x2e - Datasheet: http://www.analog.com/en/prod/0,,766_825_ADM1027,00.html - * Analog Devices ADT7463 - Prefix: 'adt7463' - Addresses scanned: I2C 0x2c, 0x2d, 0x2e - Datasheet: http://www.analog.com/en/prod/0,,766_825_ADT7463,00.html - * SMSC EMC6D100, SMSC EMC6D101 - Prefix: 'emc6d100' - Addresses scanned: I2C 0x2c, 0x2d, 0x2e - Datasheet: http://www.smsc.com/main/tools/discontinued/6d100.pdf - * SMSC EMC6D102 - Prefix: 'emc6d102' - Addresses scanned: I2C 0x2c, 0x2d, 0x2e - Datasheet: http://www.smsc.com/main/catalog/emc6d102.html - -Authors: - Philip Pokorny , - Frodo Looijaard , - Richard Barrington , - Margit Schubert-While , - Justin Thiessen - -Description ------------ - -This driver implements support for the National Semiconductor LM85 and -compatible chips including the Analog Devices ADM1027, ADT7463 and -SMSC EMC6D10x chips family. - -The LM85 uses the 2-wire interface compatible with the SMBUS 2.0 -specification. Using an analog to digital converter it measures three (3) -temperatures and five (5) voltages. It has four (4) 16-bit counters for -measuring fan speed. Five (5) digital inputs are provided for sampling the -VID signals from the processor to the VRM. Lastly, there are three (3) PWM -outputs that can be used to control fan speed. - -The voltage inputs have internal scaling resistors so that the following -voltage can be measured without external resistors: - - 2.5V, 3.3V, 5V, 12V, and CPU core voltage (2.25V) - -The temperatures measured are one internal diode, and two remote diodes. -Remote 1 is generally the CPU temperature. These inputs are designed to -measure a thermal diode like the one in a Pentium 4 processor in a socket -423 or socket 478 package. They can also measure temperature using a -transistor like the 2N3904. - -A sophisticated control system for the PWM outputs is designed into the -LM85 that allows fan speed to be adjusted automatically based on any of the -three temperature sensors. Each PWM output is individually adjustable and -programmable. Once configured, the LM85 will adjust the PWM outputs in -response to the measured temperatures without further host intervention. -This feature can also be disabled for manual control of the PWM's. - -Each of the measured inputs (voltage, temperature, fan speed) has -corresponding high/low limit values. The LM85 will signal an ALARM if any -measured value exceeds either limit. - -The LM85 samples all inputs continuously. The lm85 driver will not read -the registers more often than once a second. Further, configuration data is -only read once each 5 minutes. There is twice as much config data as -measurements, so this would seem to be a worthwhile optimization. - -Special Features ----------------- - -The LM85 has four fan speed monitoring modes. The ADM1027 has only two. -Both have special circuitry to compensate for PWM interactions with the -TACH signal from the fans. The ADM1027 can be configured to measure the -speed of a two wire fan, but the input conditioning circuitry is different -for 3-wire and 2-wire mode. For this reason, the 2-wire fan modes are not -exposed to user control. The BIOS should initialize them to the correct -mode. If you've designed your own ADM1027, you'll have to modify the -init_client function and add an insmod parameter to set this up. - -To smooth the response of fans to changes in temperature, the LM85 has an -optional filter for smoothing temperatures. The ADM1027 has the same -config option but uses it to rate limit the changes to fan speed instead. - -The ADM1027 and ADT7463 have a 10-bit ADC and can therefore measure -temperatures with 0.25 degC resolution. They also provide an offset to the -temperature readings that is automatically applied during measurement. -This offset can be used to zero out any errors due to traces and placement. -The documentation says that the offset is in 0.25 degC steps, but in -initial testing of the ADM1027 it was 1.00 degC steps. Analog Devices has -confirmed this "bug". The ADT7463 is reported to work as described in the -documentation. The current lm85 driver does not show the offset register. - -The ADT7463 has a THERM asserted counter. This counter has a 22.76ms -resolution and a range of 5.8 seconds. The driver implements a 32-bit -accumulator of the counter value to extend the range to over a year. The -counter will stay at it's max value until read. - -See the vendor datasheets for more information. There is application note -from National (AN-1260) with some additional information about the LM85. -The Analog Devices datasheet is very detailed and describes a procedure for -determining an optimal configuration for the automatic PWM control. - -The SMSC EMC6D100 & EMC6D101 monitor external voltages, temperatures, and -fan speeds. They use this monitoring capability to alert the system to out -of limit conditions and can automatically control the speeds of multiple -fans in a PC or embedded system. The EMC6D101, available in a 24-pin SSOP -package, and the EMC6D100, available in a 28-pin SSOP package, are designed -to be register compatible. The EMC6D100 offers all the features of the -EMC6D101 plus additional voltage monitoring and system control features. -Unfortunately it is not possible to distinguish between the package -versions on register level so these additional voltage inputs may read -zero. The EMC6D102 features addtional ADC bits thus extending precision -of voltage and temperature channels. - - -Hardware Configurations ------------------------ - -The LM85 can be jumpered for 3 different SMBus addresses. There are -no other hardware configuration options for the LM85. - -The lm85 driver detects both LM85B and LM85C revisions of the chip. See the -datasheet for a complete description of the differences. Other than -identifying the chip, the driver behaves no differently with regard to -these two chips. The LM85B is recommended for new designs. - -The ADM1027 and ADT7463 chips have an optional SMBALERT output that can be -used to signal the chipset in case a limit is exceeded or the temperature -sensors fail. Individual sensor interrupts can be masked so they won't -trigger SMBALERT. The SMBALERT output if configured replaces one of the other -functions (PWM2 or IN0). This functionality is not implemented in current -driver. - -The ADT7463 also has an optional THERM output/input which can be connected -to the processor PROC_HOT output. If available, the autofan control -dynamic Tmin feature can be enabled to keep the system temperature within -spec (just?!) with the least possible fan noise. - -Configuration Notes -------------------- - -Besides standard interfaces driver adds following: - -* Temperatures and Zones - -Each temperature sensor is associated with a Zone. There are three -sensors and therefore three zones (# 1, 2 and 3). Each zone has the following -temperature configuration points: - -* temp#_auto_temp_off - temperature below which fans should be off or spinning very low. -* temp#_auto_temp_min - temperature over which fans start to spin. -* temp#_auto_temp_max - temperature when fans spin at full speed. -* temp#_auto_temp_crit - temperature when all fans will run full speed. - -* PWM Control - -There are three PWM outputs. The LM85 datasheet suggests that the -pwm3 output control both fan3 and fan4. Each PWM can be individually -configured and assigned to a zone for it's control value. Each PWM can be -configured individually according to the following options. - -* pwm#_auto_pwm_min - this specifies the PWM value for temp#_auto_temp_off - temperature. (PWM value from 0 to 255) - -* pwm#_auto_pwm_freq - select base frequency of PWM output. You can select - in range of 10.0 to 94.0 Hz in .1 Hz units. - (Values 100 to 940). - -The pwm#_auto_pwm_freq can be set to one of the following 8 values. Setting the -frequency to a value not on this list, will result in the next higher frequency -being selected. The actual device frequency may vary slightly from this -specification as designed by the manufacturer. Consult the datasheet for more -details. (PWM Frequency values: 100, 150, 230, 300, 380, 470, 620, 940) - -* pwm#_auto_pwm_minctl - this flags selects for temp#_auto_temp_off temperature - the bahaviour of fans. Write 1 to let fans spinning at - pwm#_auto_pwm_min or write 0 to let them off. - -NOTE: It has been reported that there is a bug in the LM85 that causes the flag -to be associated with the zones not the PWMs. This contradicts all the -published documentation. Setting pwm#_min_ctl in this case actually affects all -PWMs controlled by zone '#'. - -* PWM Controlling Zone selection - -* pwm#_auto_channels - controls zone that is associated with PWM - -Configuration choices: - - Value Meaning - ------ ------------------------------------------------ - 1 Controlled by Zone 1 - 2 Controlled by Zone 2 - 3 Controlled by Zone 3 - 23 Controlled by higher temp of Zone 2 or 3 - 123 Controlled by highest temp of Zone 1, 2 or 3 - 0 PWM always 0% (off) - -1 PWM always 100% (full on) - -2 Manual control (write to 'pwm#' to set) - -The National LM85's have two vendor specific configuration -features. Tach. mode and Spinup Control. For more details on these, -see the LM85 datasheet or Application Note AN-1260. - -The Analog Devices ADM1027 has several vendor specific enhancements. -The number of pulses-per-rev of the fans can be set, Tach monitoring -can be optimized for PWM operation, and an offset can be applied to -the temperatures to compensate for systemic errors in the -measurements. - -In addition to the ADM1027 features, the ADT7463 also has Tmin control -and THERM asserted counts. Automatic Tmin control acts to adjust the -Tmin value to maintain the measured temperature sensor at a specified -temperature. There isn't much documentation on this feature in the -ADT7463 data sheet. This is not supported by current driver. diff --git a/Documentation/i2c/chips/lm87 b/Documentation/i2c/chips/lm87 deleted file mode 100644 index c952c57..0000000 --- a/Documentation/i2c/chips/lm87 +++ /dev/null @@ -1,73 +0,0 @@ -Kernel driver lm87 -================== - -Supported chips: - * National Semiconductor LM87 - Prefix: 'lm87' - Addresses scanned: I2C 0x2c - 0x2f - Datasheet: http://www.national.com/pf/LM/LM87.html - -Authors: - Frodo Looijaard , - Philip Edelbrock , - Mark Studebaker , - Stephen Rousset , - Dan Eaton , - Jean Delvare , - Original 2.6 port Jeff Oliver - -Description ------------ - -This driver implements support for the National Semiconductor LM87. - -The LM87 implements up to three temperature sensors, up to two fan -rotation speed sensors, up to seven voltage sensors, alarms, and some -miscellaneous stuff. - -Temperatures are measured in degrees Celsius. Each input has a high -and low alarm settings. A high limit produces an alarm when the value -goes above it, and an alarm is also produced when the value goes below -the low limit. - -Fan rotation speeds are reported in RPM (rotations per minute). An alarm is -triggered if the rotation speed has dropped below a programmable limit. Fan -readings can be divided by a programmable divider (1, 2, 4 or 8) to give -the readings more range or accuracy. Not all RPM values can accurately be -represented, so some rounding is done. With a divider of 2, the lowest -representable value is around 2600 RPM. - -Voltage sensors (also known as IN sensors) report their values in -volts. An alarm is triggered if the voltage has crossed a programmable -minimum or maximum limit. Note that minimum in this case always means -'closest to zero'; this is important for negative voltage measurements. - -If an alarm triggers, it will remain triggered until the hardware register -is read at least once. This means that the cause for the alarm may -already have disappeared! Note that in the current implementation, all -hardware registers are read whenever any data is read (unless it is less -than 1.0 seconds since the last update). This means that you can easily -miss once-only alarms. - -The lm87 driver only updates its values each 1.0 seconds; reading it more -often will do no harm, but will return 'old' values. - - -Hardware Configurations ------------------------ - -The LM87 has four pins which can serve one of two possible functions, -depending on the hardware configuration. - -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) diff --git a/Documentation/i2c/chips/lm90 b/Documentation/i2c/chips/lm90 deleted file mode 100644 index 2c4cf39..0000000 --- a/Documentation/i2c/chips/lm90 +++ /dev/null @@ -1,121 +0,0 @@ -Kernel driver lm90 -================== - -Supported chips: - * National Semiconductor LM90 - Prefix: 'lm90' - Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/pf/LM/LM90.html - * National Semiconductor LM89 - Prefix: 'lm99' - Addresses scanned: I2C 0x4c and 0x4d - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/pf/LM/LM89.html - * National Semiconductor LM99 - Prefix: 'lm99' - Addresses scanned: I2C 0x4c and 0x4d - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/pf/LM/LM99.html - * National Semiconductor LM86 - Prefix: 'lm86' - Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the National Semiconductor website - http://www.national.com/pf/LM/LM86.html - * Analog Devices ADM1032 - Prefix: 'adm1032' - Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the Analog Devices website - http://products.analog.com/products/info.asp?product=ADM1032 - * Analog Devices ADT7461 - Prefix: 'adt7461' - Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the Analog Devices website - http://products.analog.com/products/info.asp?product=ADT7461 - Note: Only if in ADM1032 compatibility mode - * Maxim MAX6657 - Prefix: 'max6657' - Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the Maxim website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 - * Maxim MAX6658 - Prefix: 'max6657' - Addresses scanned: I2C 0x4c - Datasheet: Publicly available at the Maxim website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 - * Maxim MAX6659 - Prefix: 'max6657' - Addresses scanned: I2C 0x4c, 0x4d (unsupported 0x4e) - Datasheet: Publicly available at the Maxim website - http://www.maxim-ic.com/quick_view2.cfm/qv_pk/2578 - - -Author: Jean Delvare - - -Description ------------ - -The LM90 is a digital temperature sensor. It senses its own temperature as -well as the temperature of up to one external diode. It is compatible -with many other devices such as the LM86, the LM89, the LM99, the ADM1032, -the MAX6657, MAX6658 and the MAX6659 all of which are supported by this driver. -Note that there is no easy way to differentiate between the last three -variants. The extra address and features of the MAX6659 are not supported by -this driver. Additionally, the ADT7461 is supported if found in ADM1032 -compatibility mode. - -The specificity of this family of chipsets over the ADM1021/LM84 -family is that it features critical limits with hysteresis, and an -increased resolution of the remote temperature measurement. - -The different chipsets of the family are not strictly identical, although -very similar. This driver doesn't handle any specific feature for now, -but could if there ever was a need for it. For reference, here comes a -non-exhaustive list of specific features: - -LM90: - * Filter and alert configuration register at 0xBF. - * ALERT is triggered by temperatures over critical limits. - -LM86 and LM89: - * Same as LM90 - * Better external channel accuracy - -LM99: - * Same as LM89 - * External temperature shifted by 16 degrees down - -ADM1032: - * Consecutive alert register at 0x22. - * Conversion averaging. - * Up to 64 conversions/s. - * ALERT is triggered by open remote sensor. - -ADT7461 - * Extended temperature range (breaks compatibility) - * Lower resolution for remote temperature - -MAX6657 and MAX6658: - * Remote sensor type selection - -MAX6659 - * Selectable address - * Second critical temperature limit - * Remote sensor type selection - -All temperature values are given in degrees Celsius. Resolution -is 1.0 degree for the local temperature, 0.125 degree for the remote -temperature. - -Each sensor has its own high and low limits, plus a critical limit. -Additionally, there is a relative hysteresis value common to both critical -values. To make life easier to user-space applications, two absolute values -are exported, one for each channel, but these values are of course linked. -Only the local hysteresis can be set from user-space, and the same delta -applies to the remote hysteresis. - -The lm90 driver will not update its values more frequently than every -other second; reading them more often will do no harm, but will return -'old' values. - diff --git a/Documentation/i2c/chips/lm92 b/Documentation/i2c/chips/lm92 deleted file mode 100644 index 7705bfa..0000000 --- a/Documentation/i2c/chips/lm92 +++ /dev/null @@ -1,37 +0,0 @@ -Kernel driver lm92 -================== - -Supported chips: - * National Semiconductor LM92 - Prefix: 'lm92' - Addresses scanned: I2C 0x48 - 0x4b - Datasheet: http://www.national.com/pf/LM/LM92.html - * National Semiconductor LM76 - Prefix: 'lm92' - Addresses scanned: none, force parameter needed - Datasheet: http://www.national.com/pf/LM/LM76.html - * Maxim MAX6633/MAX6634/MAX6635 - Prefix: 'lm92' - Addresses scanned: I2C 0x48 - 0x4b - MAX6633 with address in 0x40 - 0x47, 0x4c - 0x4f needs force parameter - and MAX6634 with address in 0x4c - 0x4f needs force parameter - Datasheet: http://www.maxim-ic.com/quick_view2.cfm/qv_pk/3074 - -Authors: - Abraham van der Merwe - Jean Delvare - - -Description ------------ - -This driver implements support for the National Semiconductor LM92 -temperature sensor. - -Each LM92 temperature sensor supports a single temperature sensor. There are -alarms for high, low, and critical thresholds. There's also an hysteresis to -control the thresholds for resetting alarms. - -Support was added later for the LM76 and Maxim MAX6633/MAX6634/MAX6635, -which are mostly compatible. They have not all been tested, so you -may need to use the force parameter. diff --git a/Documentation/i2c/chips/max1619 b/Documentation/i2c/chips/max1619 deleted file mode 100644 index d6f8d9c..0000000 --- a/Documentation/i2c/chips/max1619 +++ /dev/null @@ -1,29 +0,0 @@ -Kernel driver max1619 -===================== - -Supported chips: - * Maxim MAX1619 - Prefix: 'max1619' - Addresses scanned: I2C 0x18-0x1a, 0x29-0x2b, 0x4c-0x4e - Datasheet: Publicly available at the Maxim website - http://pdfserv.maxim-ic.com/en/ds/MAX1619.pdf - -Authors: - Alexey Fisher , - Jean Delvare - -Description ------------ - -The MAX1619 is a digital temperature sensor. It senses its own temperature as -well as the temperature of up to one external diode. - -All temperature values are given in degrees Celsius. Resolution -is 1.0 degree for the local temperature and for the remote temperature. - -Only the external sensor has high and low limits. - -The max1619 driver will not update its values more frequently than every -other second; reading them more often will do no harm, but will return -'old' values. - diff --git a/Documentation/i2c/chips/pc87360 b/Documentation/i2c/chips/pc87360 deleted file mode 100644 index 89a8fcf..0000000 --- a/Documentation/i2c/chips/pc87360 +++ /dev/null @@ -1,189 +0,0 @@ -Kernel driver pc87360 -===================== - -Supported chips: - * National Semiconductor PC87360, PC87363, PC87364, PC87365 and PC87366 - Prefixes: 'pc87360', 'pc87363', 'pc87364', 'pc87365', 'pc87366' - Addresses scanned: none, address read from Super I/O config space - Datasheets: - http://www.national.com/pf/PC/PC87360.html - http://www.national.com/pf/PC/PC87363.html - http://www.national.com/pf/PC/PC87364.html - http://www.national.com/pf/PC/PC87365.html - http://www.national.com/pf/PC/PC87366.html - -Authors: Jean Delvare - -Thanks to Sandeep Mehta, Tonko de Rooy and Daniel Ceregatti for testing. -Thanks to Rudolf Marek for helping me investigate conversion issues. - - -Module Parameters ------------------ - -* init int - Chip initialization level: - 0: None - *1: Forcibly enable internal voltage and temperature channels, except in9 - 2: Forcibly enable all voltage and temperature channels, except in9 - 3: Forcibly enable all voltage and temperature channels, including in9 - -Note that this parameter has no effect for the PC87360, PC87363 and PC87364 -chips. - -Also note that for the PC87366, initialization levels 2 and 3 don't enable -all temperature channels, because some of them share pins with each other, -so they can't be used at the same time. - - -Description ------------ - -The National Semiconductor PC87360 Super I/O chip contains monitoring and -PWM control circuitry for two fans. The PC87363 chip is similar, and the -PC87364 chip has monitoring and PWM control for a third fan. - -The National Semiconductor PC87365 and PC87366 Super I/O chips are complete -hardware monitoring chipsets, not only controlling and monitoring three fans, -but also monitoring eleven voltage inputs and two (PC87365) or up to four -(PC87366) temperatures. - - 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 - -The 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) - -Fan Monitoring --------------- - -Fan rotation speeds are reported in RPM (revolutions per minute). An alarm -is triggered if the rotation speed has dropped below a programmable limit. -A different alarm is triggered if the fan speed is too low to be measured. - -Fan readings are affected by a programmable clock divider, giving the -readings more range or accuracy. Usually, users have to learn how it works, -but this driver implements dynamic clock divider selection, so you don't -have to care no more. - -For reference, here are a few values about clock dividers: - - slowest accuracy highest - measurable around 3000 accurate - divider speed (RPM) RPM (RPM) speed (RPM) - 1 1882 18 6928 - 2 941 37 4898 - 4 470 74 3464 - 8 235 150 2449 - -For the curious, here is how the values above were computed: - * slowest measurable speed: clock/(255*divider) - * accuracy around 3000 RPM: 3000^2/clock - * highest accurate speed: sqrt(clock*100) -The clock speed for the PC87360 family is 480 kHz. I arbitrarily chose 100 -RPM as the lowest acceptable accuracy. - -As mentioned above, you don't have to care about this no more. - -Note that not all RPM values can be represented, even when the best clock -divider is selected. This is not only true for the measured speeds, but -also for the programmable low limits, so don't be surprised if you try to -set, say, fan1_min to 2900 and it finally reads 2909. - - -Fan Control ------------ - -PWM (pulse width modulation) values range from 0 to 255, with 0 meaning -that the fan is stopped, and 255 meaning that the fan goes at full speed. - -Be extremely careful when changing PWM values. Low PWM values, even -non-zero, can stop the fan, which may cause irreversible damage to your -hardware if temperature increases too much. When changing PWM values, go -step by step and keep an eye on temperatures. - -One user reported problems with PWM. Changing PWM values would break fan -speed readings. No explanation nor fix could be found. - - -Temperature Monitoring ----------------------- - -Temperatures are reported in degrees Celsius. Each temperature measured has -associated low, high and overtemperature limits, each of which triggers an -alarm when crossed. - -The first two temperature channels are external. The third one (PC87366 -only) is internal. - -The PC87366 has three additional temperature channels, based on -thermistors (as opposed to thermal diodes for the first three temperature -channels). For technical reasons, these channels are held by the VLM -(voltage level monitor) logical device, not the TMS (temperature -measurement) one. As a consequence, these temperatures are exported as -voltages, and converted into temperatures in user-space. - -Note that these three additional channels share their pins with the -external thermal diode channels, so you (physically) can't use them all at -the same time. Although it should be possible to mix the two sensor types, -the documents from National Semiconductor suggest that motherboard -manufacturers should choose one type and stick to it. So you will more -likely have either channels 1 to 3 (thermal diodes) or 3 to 6 (internal -thermal diode, and thermistors). - - -Voltage Monitoring ------------------- - -Voltages are reported relatively to a reference voltage, either internal or -external. Some of them (in7:Vsb, in8:Vdd and in10:AVdd) are divided by two -internally, you will have to compensate in sensors.conf. Others (in0 to in6) -are likely to be divided externally. The meaning of each of these inputs as -well as the values of the resistors used for division is left to the -motherboard manufacturers, so you will have to document yourself and edit -sensors.conf accordingly. National Semiconductor has a document with -recommended resistor values for some voltages, but this still leaves much -room for per motherboard specificities, unfortunately. Even worse, -motherboard manufacturers don't seem to care about National Semiconductor's -recommendations. - -Each voltage measured has associated low and high limits, each of which -triggers an alarm when crossed. - -When available, VID inputs are used to provide the nominal CPU Core voltage. -The driver will default to VRM 9.0, but this can be changed from user-space. -The chipsets can handle two sets of VID inputs (on dual-CPU systems), but -the driver will only export one for now. This may change later if there is -a need. - - -General Remarks ---------------- - -If an alarm triggers, it will remain triggered until the hardware register -is read at least once. This means that the cause for the alarm may already -have disappeared! Note that all hardware registers are read whenever any -data is read (unless it is less than 2 seconds since the last update, in -which case cached values are returned instead). As a consequence, when -a once-only alarm triggers, it may take 2 seconds for it to show, and 2 -more seconds for it to disappear. - -Monitoring of in9 isn't enabled at lower init levels (<3) because that -channel measures the battery voltage (Vbat). It is a known fact that -repeatedly sampling the battery voltage reduces its lifetime. National -Semiconductor smartly designed their chipset so that in9 is sampled only -once every 1024 sampling cycles (that is every 34 minutes at the default -sampling rate), so the effect is attenuated, but still present. - - -Limitations ------------ - -The datasheets suggests that some values (fan mins, fan dividers) -shouldn't be changed once the monitoring has started, but we ignore that -recommendation. We'll reconsider if it actually causes trouble. diff --git a/Documentation/i2c/chips/sis5595 b/Documentation/i2c/chips/sis5595 deleted file mode 100644 index b7ae36b..0000000 --- a/Documentation/i2c/chips/sis5595 +++ /dev/null @@ -1,106 +0,0 @@ -Kernel driver sis5595 -===================== - -Supported chips: - * Silicon Integrated Systems Corp. SiS5595 Southbridge Hardware Monitor - Prefix: 'sis5595' - Addresses scanned: ISA in PCI-space encoded address - Datasheet: Publicly available at the Silicon Integrated Systems Corp. site. - -Authors: - Kyösti Mälkki , - Mark D. Studebaker , - Aurelien Jarno 2.6 port - - 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 - 630 0008 0630 - 645 0008 0645 - 730 0008 0730 - 735 0008 0735 - - -Module Parameters ------------------ -force_addr=0xaddr Set the I/O base address. Useful for boards - that don't set the address in the BIOS. Does not do a - PCI force; the device must still be present in lspci. - Don't use this unless the driver complains that the - base address is not set. - Example: 'modprobe sis5595 force_addr=0x290' - - -Description ------------ - -The SiS5595 southbridge has integrated hardware monitor functions. It also -has an I2C bus, but this driver only supports the hardware monitor. For the -I2C bus driver see i2c-sis5595. - -The SiS5595 implements zero or one temperature sensor, two fan speed -sensors, four or five voltage sensors, and alarms. - -On the first version of the chip, there are four voltage sensors and one -temperature sensor. - -On the second version of the chip, the temperature sensor (temp) and the -fifth voltage sensor (in4) share a pin which is configurable, but not -through the driver. Sorry. The driver senses the configuration of the pin, -which was hopefully set by the BIOS. - -Temperatures are measured in degrees Celsius. An alarm is triggered once -when the max is crossed; it is also triggered when it drops below the min -value. Measurements are guaranteed between -55 and +125 degrees, with a -resolution of 1 degree. - -Fan rotation speeds are reported in RPM (rotations per minute). An alarm is -triggered if the rotation speed has dropped below a programmable limit. Fan -readings can be divided by a programmable divider (1, 2, 4 or 8) to give -the readings more range or accuracy. Not all RPM values can accurately be -represented, so some rounding is done. With a divider of 2, the lowest -representable value is around 2600 RPM. - -Voltage sensors (also known as IN sensors) report their values in volts. An -alarm is triggered if the voltage has crossed a programmable minimum or -maximum limit. Note that minimum in this case always means 'closest to -zero'; this is important for negative voltage measurements. All voltage -inputs can measure voltages between 0 and 4.08 volts, with a resolution of -0.016 volt. - -In addition to the alarms described above, there is a BTI alarm, which gets -triggered when an external chip has crossed its limits. Usually, this is -connected to some LM75-like chip; if at least one crosses its limits, this -bit gets set. - -If an alarm triggers, it will remain triggered until the hardware register -is read at least once. This means that the cause for the alarm may already -have disappeared! Note that in the current implementation, all hardware -registers are read whenever any data is read (unless it is less than 1.5 -seconds since the last update). This means that you can easily miss -once-only alarms. - -The SiS5595 only updates its values each 1.5 seconds; reading it more often -will do no harm, but will return 'old' values. - -Problems --------- -Some chips refuse to be enabled. We don't know why. -The driver will recognize this and print a message in dmesg. - diff --git a/Documentation/i2c/chips/smsc47b397 b/Documentation/i2c/chips/smsc47b397 deleted file mode 100644 index da9d80c..0000000 --- a/Documentation/i2c/chips/smsc47b397 +++ /dev/null @@ -1,158 +0,0 @@ -Kernel driver smsc47b397 -======================== - -Supported chips: - * SMSC LPC47B397-NC - Prefix: 'smsc47b397' - Addresses scanned: none, address read from Super I/O config space - Datasheet: In this file - -Authors: Mark M. Hoffman - Utilitek Systems, Inc. - -November 23, 2004 - -The following specification describes the SMSC LPC47B397-NC sensor chip -(for which there is no public datasheet available). This document was -provided by Craig Kelly (In-Store Broadcast Network) and edited/corrected -by Mark M. Hoffman . - -* * * * * - -Methods for detecting the HP SIO and reading the thermal data on a dc7100. - -The thermal information on the dc7100 is contained in the SIO Hardware Monitor -(HWM). The information is accessed through an index/data pair. The index/data -pair is located at the HWM Base Address + 0 and the HWM Base Address + 1. The -HWM Base address can be obtained from Logical Device 8, registers 0x60 (MSB) -and 0x61 (LSB). Currently we are using 0x480 for the HWM Base Address and -0x480 and 0x481 for the index/data pair. - -Reading temperature information. -The temperature information is located in the following registers: -Temp1 0x25 (Currently, this reflects the CPU temp on all systems). -Temp2 0x26 -Temp3 0x27 -Temp4 0x80 - -Programming Example -The following is an example of how to read the HWM temperature registers: -MOV DX,480H -MOV AX,25H -OUT DX,AL -MOV DX,481H -IN AL,DX - -AL contains the data in hex, the temperature in Celsius is the decimal -equivalent. - -Ex: If AL contains 0x2A, the temperature is 42 degrees C. - -Reading tach information. -The fan speed information is located in the following registers: - LSB MSB -Tach1 0x28 0x29 (Currently, this reflects the CPU - fan speed on all systems). -Tach2 0x2A 0x2B -Tach3 0x2C 0x2D -Tach4 0x2E 0x2F - -Important!!! -Reading the tach LSB locks the tach MSB. -The LSB Must be read first. - -How to convert the tach reading to RPM. -The tach reading (TCount) is given by: (Tach MSB * 256) + (Tach LSB) -The SIO counts the number of 90kHz (11.111us) pulses per revolution. -RPM = 60/(TCount * 11.111us) - -Example: -Reg 0x28 = 0x9B -Reg 0x29 = 0x08 - -TCount = 0x89B = 2203 - -RPM = 60 / (2203 * 11.11111 E-6) = 2451 RPM - -Obtaining the SIO version. - -CONFIGURATION SEQUENCE -To program the configuration registers, the following sequence must be followed: -1. Enter Configuration Mode -2. Configure the Configuration Registers -3. Exit Configuration Mode. - -Enter Configuration Mode -To place the chip into the Configuration State The config key (0x55) is written -to the CONFIG PORT (0x2E). - -Configuration Mode -In configuration mode, the INDEX PORT is located at the CONFIG PORT address and -the DATA PORT is at INDEX PORT address + 1. - -The desired configuration registers are accessed in two steps: -a. Write the index of the Logical Device Number Configuration Register - (i.e., 0x07) to the INDEX PORT and then write the number of the - desired logical device to the DATA PORT. - -b. Write the address of the desired configuration register within the - logical device to the INDEX PORT and then write or read the config- - uration register through the DATA PORT. - -Note: If accessing the Global Configuration Registers, step (a) is not required. - -Exit Configuration Mode -To exit the Configuration State the write 0xAA to the CONFIG PORT (0x2E). -The chip returns to the RUN State. (This is important). - -Programming Example -The following is an example of how to read the SIO Device ID located at 0x20 - -; ENTER CONFIGURATION MODE -MOV DX,02EH -MOV AX,055H -OUT DX,AL -; GLOBAL CONFIGURATION REGISTER -MOV DX,02EH -MOV AL,20H -OUT DX,AL -; READ THE DATA -MOV DX,02FH -IN AL,DX -; EXIT CONFIGURATION MODE -MOV DX,02EH -MOV AX,0AAH -OUT DX,AL - -The registers of interest for identifying the SIO on the dc7100 are Device ID -(0x20) and Device Rev (0x21). - -The Device ID will read 0X6F -The Device Rev currently reads 0x01 - -Obtaining the HWM Base Address. -The following is an example of how to read the HWM Base Address located in -Logical Device 8. - -; ENTER CONFIGURATION MODE -MOV DX,02EH -MOV AX,055H -OUT DX,AL -; CONFIGURE REGISTER CRE0, -; LOGICAL DEVICE 8 -MOV DX,02EH -MOV AL,07H -OUT DX,AL ;Point to LD# Config Reg -MOV DX,02FH -MOV AL, 08H -OUT DX,AL;Point to Logical Device 8 -; -MOV DX,02EH -MOV AL,60H -OUT DX,AL ; Point to HWM Base Addr MSB -MOV DX,02FH -IN AL,DX ; Get MSB of HWM Base Addr -; EXIT CONFIGURATION MODE -MOV DX,02EH -MOV AX,0AAH -OUT DX,AL diff --git a/Documentation/i2c/chips/smsc47m1 b/Documentation/i2c/chips/smsc47m1 deleted file mode 100644 index 34e6478..0000000 --- a/Documentation/i2c/chips/smsc47m1 +++ /dev/null @@ -1,52 +0,0 @@ -Kernel driver smsc47m1 -====================== - -Supported chips: - * SMSC LPC47B27x, LPC47M10x, LPC47M13x, LPC47M14x, LPC47M15x and LPC47M192 - Addresses scanned: none, address read from Super I/O config space - Prefix: 'smsc47m1' - Datasheets: - http://www.smsc.com/main/datasheets/47b27x.pdf - http://www.smsc.com/main/datasheets/47m10x.pdf - http://www.smsc.com/main/tools/discontinued/47m13x.pdf - http://www.smsc.com/main/datasheets/47m14x.pdf - http://www.smsc.com/main/tools/discontinued/47m15x.pdf - http://www.smsc.com/main/datasheets/47m192.pdf - -Authors: - Mark D. Studebaker , - With assistance from Bruce Allen , and his - fan.c program: http://www.lsc-group.phys.uwm.edu/%7Eballen/driver/ - Gabriele Gorla , - Jean Delvare - -Description ------------ - -The Standard Microsystems Corporation (SMSC) 47M1xx Super I/O chips -contain monitoring and PWM control circuitry for two fans. - -The 47M15x and 47M192 chips contain a full 'hardware monitoring block' -in addition to the fan monitoring and control. The hardware monitoring -block is not supported by the driver. - -Fan rotation speeds are reported in RPM (rotations per minute). An alarm is -triggered if the rotation speed has dropped below a programmable limit. Fan -readings can be divided by a programmable divider (1, 2, 4 or 8) to give -the readings more range or accuracy. Not all RPM values can accurately be -represented, so some rounding is done. With a divider of 2, the lowest -representable value is around 2600 RPM. - -PWM values are from 0 to 255. - -If an alarm triggers, it will remain triggered until the hardware register -is read at least once. This means that the cause for the alarm may -already have disappeared! Note that in the current implementation, all -hardware registers are read whenever any data is read (unless it is less -than 1.5 seconds since the last update). This means that you can easily -miss once-only alarms. - - -********************** -The lm_sensors project gratefully acknowledges the support of -Intel in the development of this driver. diff --git a/Documentation/i2c/chips/via686a b/Documentation/i2c/chips/via686a deleted file mode 100644 index b82014c..0000000 --- a/Documentation/i2c/chips/via686a +++ /dev/null @@ -1,65 +0,0 @@ -Kernel driver via686a -===================== - -Supported chips: - * Via VT82C686A, VT82C686B Southbridge Integrated Hardware Monitor - Prefix: 'via686a' - Addresses scanned: ISA in PCI-space encoded address - Datasheet: On request through web form (http://www.via.com.tw/en/support/datasheets/) - -Authors: - Kyösti Mälkki , - Mark D. Studebaker - Bob Dougherty - (Some conversion-factor data were contributed by - Jonathan Teh Soon Yew - and Alex van Kaam .) - -Module Parameters ------------------ - -force_addr=0xaddr Set the I/O base address. Useful for Asus A7V boards - that don't set the address in the BIOS. Does not do a - PCI force; the via686a must still be present in lspci. - Don't use this unless the driver complains that the - base address is not set. - Example: 'modprobe via686a force_addr=0x6000' - -Description ------------ - -The driver does not distinguish between the chips and reports -all as a 686A. - -The Via 686a southbridge has integrated hardware monitor functionality. -It also has an I2C bus, but this driver only supports the hardware monitor. -For the I2C bus driver, see - -The Via 686a implements three temperature sensors, two fan rotation speed -sensors, five voltage sensors and alarms. - -Temperatures are measured in degrees Celsius. An alarm is triggered once -when the Overtemperature Shutdown limit is crossed; it is triggered again -as soon as it drops below the hysteresis value. - -Fan rotation speeds are reported in RPM (rotations per minute). An alarm is -triggered if the rotation speed has dropped below a programmable limit. Fan -readings can be divided by a programmable divider (1, 2, 4 or 8) to give -the readings more range or accuracy. Not all RPM values can accurately be -represented, so some rounding is done. With a divider of 2, the lowest -representable value is around 2600 RPM. - -Voltage sensors (also known as IN sensors) report their values in volts. -An alarm is triggered if the voltage has crossed a programmable minimum -or maximum limit. Voltages are internally scalled, so each voltage channel -has a different resolution and range. - -If an alarm triggers, it will remain triggered until the hardware register -is read at least once. This means that the cause for the alarm may -already have disappeared! Note that in the current implementation, all -hardware registers are read whenever any data is read (unless it is less -than 1.5 seconds since the last update). This means that you can easily -miss once-only alarms. - -The driver only updates its values each 1.5 seconds; reading it more often -will do no harm, but will return 'old' values. diff --git a/Documentation/i2c/chips/w83627hf b/Documentation/i2c/chips/w83627hf deleted file mode 100644 index 78f37c2..0000000 --- a/Documentation/i2c/chips/w83627hf +++ /dev/null @@ -1,66 +0,0 @@ -Kernel driver w83627hf -====================== - -Supported chips: - * Winbond W83627HF (ISA accesses ONLY) - Prefix: 'w83627hf' - Addresses scanned: ISA address retrieved from Super I/O registers - Datasheet: http://www.winbond.com/PDF/sheet/w83627hf.pdf - * Winbond W83627THF - Prefix: 'w83627thf' - Addresses scanned: ISA address retrieved from Super I/O registers - Datasheet: http://www.winbond.com/PDF/sheet/w83627thf.pdf - * Winbond W83697HF - Prefix: 'w83697hf' - Addresses scanned: ISA address retrieved from Super I/O registers - Datasheet: http://www.winbond.com/PDF/sheet/697hf.pdf - * Winbond W83637HF - Prefix: 'w83637hf' - Addresses scanned: ISA address retrieved from Super I/O registers - Datasheet: http://www.winbond.com/PDF/sheet/w83637hf.pdf - -Authors: - Frodo Looijaard , - Philip Edelbrock , - Mark Studebaker , - Bernhard C. Schrenk - -Module Parameters ------------------ - -* force_addr: int - Initialize the ISA address of the sensors -* force_i2c: int - Initialize the I2C address of the sensors -* init: int - (default is 1) - Use 'init=0' to bypass initializing the chip. - Try this if your computer crashes when you load the module. - -Description ------------ - -This driver implements support for ISA accesses *only* for -the Winbond W83627HF, W83627THF, W83697HF and W83637HF Super I/O chips. -We will refer to them collectively as Winbond chips. - -This driver supports ISA accesses, which should be more reliable -than i2c accesses. Also, for Tyan boards which contain both a -Super I/O chip and a second i2c-only Winbond chip (often a W83782D), -using this driver will avoid i2c address conflicts and complex -initialization that were required in the w83781d driver. - -If you really want i2c accesses for these Super I/O chips, -use the w83781d driver. However this is not the preferred method -now that this ISA driver has been developed. - -Technically, the w83627thf does not support a VID reading. However, it's -possible or even likely that your mainboard maker has routed these signals -to a specific set of general purpose IO pins (the Asus P4C800-E is one such -board). The w83627thf driver now interprets these as VID. If the VID on -your board doesn't work, first see doc/vid in the lm_sensors package. If -that still doesn't help, email us at lm-sensors@lm-sensors.org. - -For further information on this driver see the w83781d driver -documentation. - diff --git a/Documentation/i2c/chips/w83781d b/Documentation/i2c/chips/w83781d deleted file mode 100644 index e545933..0000000 --- a/Documentation/i2c/chips/w83781d +++ /dev/null @@ -1,402 +0,0 @@ -Kernel driver w83781d -===================== - -Supported chips: - * Winbond W83781D - Prefix: 'w83781d' - Addresses scanned: I2C 0x20 - 0x2f, ISA 0x290 (8 I/O ports) - Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/w83781d.pdf - * Winbond W83782D - Prefix: 'w83782d' - Addresses scanned: I2C 0x20 - 0x2f, ISA 0x290 (8 I/O ports) - Datasheet: http://www.winbond.com/PDF/sheet/w83782d.pdf - * Winbond W83783S - Prefix: 'w83783s' - Addresses scanned: I2C 0x2d - Datasheet: http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/w83783s.pdf - * Winbond W83627HF - Prefix: 'w83627hf' - Addresses scanned: I2C 0x20 - 0x2f, ISA 0x290 (8 I/O ports) - Datasheet: http://www.winbond.com/PDF/sheet/w83627hf.pdf - * Asus AS99127F - Prefix: 'as99127f' - Addresses scanned: I2C 0x28 - 0x2f - Datasheet: Unavailable from Asus - -Authors: - Frodo Looijaard , - Philip Edelbrock , - Mark Studebaker - -Module parameters ------------------ - -* init int - (default 1) - Use 'init=0' to bypass initializing the chip. - Try this if your computer crashes when you load the module. - -force_subclients=bus,caddr,saddr,saddr - This is used to force the i2c addresses for subclients of - a certain chip. Typical usage is `force_subclients=0,0x2d,0x4a,0x4b' - to force the subclients of chip 0x2d on bus 0 to i2c addresses - 0x4a and 0x4b. This parameter is useful for certain Tyan boards. - -Description ------------ - -This driver implements support for the Winbond W83781D, W83782D, W83783S, -W83627HF chips, and the Asus AS99127F chips. We will refer to them -collectively as W8378* chips. - -There is quite some difference between these chips, but they are similar -enough that it was sensible to put them together in one driver. -The W83627HF chip is assumed to be identical to the ISA W83782D. -The Asus chips are similar to an I2C-only W83782D. - -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 - -Detection of these chips can sometimes be foiled because they can be in -an internal state that allows no clean access. If you know the address -of the chip, use a 'force' parameter; this will put them into a more -well-behaved state first. - -The W8378* implements temperature sensors (three on the W83781D and W83782D, -two on the W83783S), three fan rotation speed sensors, voltage sensors -(seven on the W83781D, nine on the W83782D and six on the W83783S), VID -lines, alarms with beep warnings, and some miscellaneous stuff. - -Temperatures are measured in degrees Celsius. There is always one main -temperature sensor, and one (W83783S) or two (W83781D and W83782D) other -sensors. An alarm is triggered for the main sensor once when the -Overtemperature Shutdown limit is crossed; it is triggered again as soon as -it drops below the Hysteresis value. A more useful behavior -can be found by setting the Hysteresis value to +127 degrees Celsius; in -this case, alarms are issued during all the time when the actual temperature -is above the Overtemperature Shutdown value. The driver sets the -hysteresis value for temp1 to 127 at initialization. - -For the other temperature sensor(s), an alarm is triggered when the -temperature gets higher then the Overtemperature Shutdown value; it stays -on until the temperature falls below the Hysteresis value. But on the -W83781D, there is only one alarm that functions for both other sensors! -Temperatures are guaranteed within a range of -55 to +125 degrees. The -main temperature sensors has a resolution of 1 degree; the other sensor(s) -of 0.5 degree. - -Fan rotation speeds are reported in RPM (rotations per minute). An alarm is -triggered if the rotation speed has dropped below a programmable limit. Fan -readings can be divided by a programmable divider (1, 2, 4 or 8 for the -W83781D; 1, 2, 4, 8, 16, 32, 64 or 128 for the others) to give -the readings more range or accuracy. Not all RPM values can accurately -be represented, so some rounding is done. With a divider of 2, the lowest -representable value is around 2600 RPM. - -Voltage sensors (also known as IN sensors) report their values in volts. -An alarm is triggered if the voltage has crossed a programmable minimum -or maximum limit. Note that minimum in this case always means 'closest to -zero'; this is important for negative voltage measurements. All voltage -inputs can measure voltages between 0 and 4.08 volts, with a resolution -of 0.016 volt. - -The VID lines encode the core voltage value: the voltage level your processor -should work with. This is hardcoded by the mainboard and/or processor itself. -It is a value in volts. When it is unconnected, you will often find the -value 3.50 V here. - -The W83782D and W83783S temperature conversion machine understands about -several kinds of temperature probes. You can program the so-called -beta value in the sensor files. '1' is the PII/Celeron diode, '2' is the -TN3904 transistor, and 3435 the default thermistor value. Other values -are (not yet) supported. - -In addition to the alarms described above, there is a CHAS alarm on the -chips which triggers if your computer case is open. - -When an alarm goes off, you can be warned by a beeping signal through -your computer speaker. It is possible to enable all beeping globally, -or only the beeping for some alarms. - -If an alarm triggers, it will remain triggered until the hardware register -is read at least once. This means that the cause for the alarm may -already have disappeared! Note that in the current implementation, all -hardware registers are read whenever any data is read (unless it is less -than 1.5 seconds since the last update). This means that you can easily -miss once-only alarms. - -The chips only update values each 1.5 seconds; reading them more often -will do no harm, but will return 'old' values. - -AS99127F PROBLEMS ------------------ -The as99127f support was developed without the benefit of a datasheet. -In most cases it is treated as a w83781d (although revision 2 of the -AS99127F looks more like a w83782d). -This support will be BETA until a datasheet is released. -One user has reported problems with fans stopping -occasionally. - -Note that the individual beep bits are inverted from the other chips. -The driver now takes care of this so that user-space applications -don't have to know about it. - -Known problems: - - Problems with diode/thermistor settings (supported?) - - One user reports fans stopping under high server load. - - Revision 2 seems to have 2 PWM registers but we don't know - how to handle them. More details below. - -These will not be fixed unless we get a datasheet. -If you have problems, please lobby Asus to release a datasheet. -Unfortunately several others have without success. -Please do not send mail to us asking for better as99127f support. -We have done the best we can without a datasheet. -Please do not send mail to the author or the sensors group asking for -a datasheet or ideas on how to convince Asus. We can't help. - - -NOTES: ------ - 783s has no in1 so that in[2-6] are compatible with the 781d/782d. - - 783s pin is programmable for -5V or temp1; defaults to -5V, - no control in driver so temp1 doesn't work. - - 782d and 783s datasheets differ on which is pwm1 and which is pwm2. - We chose to follow 782d. - - 782d and 783s pin is programmable for fan3 input or pwm2 output; - defaults to fan3 input. - If pwm2 is enabled (with echo 255 1 > pwm2), then - fan3 will report 0. - - 782d has pwm1-2 for ISA, pwm1-4 for i2c. (pwm3-4 share pins with - the ISA pins) - -Data sheet updates: ------------------- - - PWM clock registers: - - 000: master / 512 - 001: master / 1024 - 010: master / 2048 - 011: master / 4096 - 100: master / 8192 - - -Answers from Winbond tech support ---------------------------------- -> -> 1) In the W83781D data sheet section 7.2 last paragraph, it talks about -> reprogramming the R-T table if the Beta of the thermistor is not -> 3435K. The R-T table is described briefly in section 8.20. -> What formulas do I use to program a new R-T table for a given Beta? -> - We are sorry that the calculation for R-T table value is -confidential. If you have another Beta value of thermistor, we can help -to calculate the R-T table for you. But you should give us real R-T -Table which can be gotten by thermistor vendor. Therefore we will calculate -them and obtain 32-byte data, and you can fill the 32-byte data to the -register in Bank0.CR51 of W83781D. - - -> 2) In the W83782D data sheet, it mentions that pins 38, 39, and 40 are -> programmable to be either thermistor or Pentium II diode inputs. -> How do I program them for diode inputs? I can't find any register -> to program these to be diode inputs. - --> You may program Bank0 CR[5Dh] and CR[59h] registers. - - CR[5Dh] bit 1(VTIN1) bit 2(VTIN2) bit 3(VTIN3) - - thermistor 0 0 0 - diode 1 1 1 - - -(error) CR[59h] bit 4(VTIN1) bit 2(VTIN2) bit 3(VTIN3) -(right) CR[59h] bit 4(VTIN1) bit 5(VTIN2) bit 6(VTIN3) - - PII thermal diode 1 1 1 - 2N3904 diode 0 0 0 - - -Asus Clones ------------ - -We have no datasheets for the Asus clones (AS99127F and ASB100 Bach). -Here are some very useful information that were given to us by Alex Van -Kaam about how to detect these chips, and how to read their values. He -also gives advice for another Asus chipset, the Mozart-2 (which we -don't support yet). Thanks Alex! -I reworded some parts and added personal comments. - -# Detection: - -AS99127F rev.1, AS99127F rev.2 and ASB100: -- I2C address range: 0x29 - 0x2F -- If register 0x58 holds 0x31 then we have an Asus (either ASB100 or - AS99127F) -- Which one depends on register 0x4F (manufacturer ID): - 0x06 or 0x94: ASB100 - 0x12 or 0xC3: AS99127F rev.1 - 0x5C or 0xA3: AS99127F rev.2 - Note that 0x5CA3 is Winbond's ID (WEC), which let us think Asus get their - AS99127F rev.2 direct from Winbond. The other codes mean ATT and DVC, - respectively. ATT could stand for Asustek something (although it would be - very badly chosen IMHO), I don't know what DVC could stand for. Maybe - these codes simply aren't meant to be decoded that way. - -Mozart-2: -- I2C address: 0x77 -- If register 0x58 holds 0x56 or 0x10 then we have a Mozart-2 -- Of the Mozart there are 3 types: - 0x58=0x56, 0x4E=0x94, 0x4F=0x36: Asus ASM58 Mozart-2 - 0x58=0x56, 0x4E=0x94, 0x4F=0x06: Asus AS2K129R Mozart-2 - 0x58=0x10, 0x4E=0x5C, 0x4F=0xA3: Asus ??? Mozart-2 - You can handle all 3 the exact same way :) - -# Temperature sensors: - -ASB100: -- sensor 1: register 0x27 -- sensor 2 & 3 are the 2 LM75's on the SMBus -- sensor 4: register 0x17 -Remark: I noticed that on Intel boards sensor 2 is used for the CPU - and 4 is ignored/stuck, on AMD boards sensor 4 is the CPU and sensor 2 is - either ignored or a socket temperature. - -AS99127F (rev.1 and 2 alike): -- sensor 1: register 0x27 -- sensor 2 & 3 are the 2 LM75's on the SMBus -Remark: Register 0x5b is suspected to be temperature type selector. Bit 1 - would control temp1, bit 3 temp2 and bit 5 temp3. - -Mozart-2: -- sensor 1: register 0x27 -- sensor 2: register 0x13 - -# Fan sensors: - -ASB100, AS99127F (rev.1 and 2 alike): -- 3 fans, identical to the W83781D - -Mozart-2: -- 2 fans only, 1350000/RPM/div -- fan 1: register 0x28, divisor on register 0xA1 (bits 4-5) -- fan 2: register 0x29, divisor on register 0xA1 (bits 6-7) - -# Voltages: - -This is where there is a difference between AS99127F rev.1 and 2. -Remark: The difference is similar to the difference between - W83781D and W83782D. - -ASB100: -in0=r(0x20)*0.016 -in1=r(0x21)*0.016 -in2=r(0x22)*0.016 -in3=r(0x23)*0.016*1.68 -in4=r(0x24)*0.016*3.8 -in5=r(0x25)*(-0.016)*3.97 -in6=r(0x26)*(-0.016)*1.666 - -AS99127F rev.1: -in0=r(0x20)*0.016 -in1=r(0x21)*0.016 -in2=r(0x22)*0.016 -in3=r(0x23)*0.016*1.68 -in4=r(0x24)*0.016*3.8 -in5=r(0x25)*(-0.016)*3.97 -in6=r(0x26)*(-0.016)*1.503 - -AS99127F rev.2: -in0=r(0x20)*0.016 -in1=r(0x21)*0.016 -in2=r(0x22)*0.016 -in3=r(0x23)*0.016*1.68 -in4=r(0x24)*0.016*3.8 -in5=(r(0x25)*0.016-3.6)*5.14+3.6 -in6=(r(0x26)*0.016-3.6)*3.14+3.6 - -Mozart-2: -in0=r(0x20)*0.016 -in1=255 -in2=r(0x22)*0.016 -in3=r(0x23)*0.016*1.68 -in4=r(0x24)*0.016*4 -in5=255 -in6=255 - - -# PWM - -Additional info about PWM on the AS99127F (may apply to other Asus -chips as well) by Jean Delvare as of 2004-04-09: - -AS99127F revision 2 seems to have two PWM registers at 0x59 and 0x5A, -and a temperature sensor type selector at 0x5B (which basically means -that they swapped registers 0x59 and 0x5B when you compare with Winbond -chips). -Revision 1 of the chip also has the temperature sensor type selector at -0x5B, but PWM registers have no effect. - -We don't know exactly how the temperature sensor type selection works. -Looks like bits 1-0 are for temp1, bits 3-2 for temp2 and bits 5-4 for -temp3, although it is possible that only the most significant bit matters -each time. So far, values other than 0 always broke the readings. - -PWM registers seem to be split in two parts: bit 7 is a mode selector, -while the other bits seem to define a value or threshold. - -When bit 7 is clear, bits 6-0 seem to hold a threshold value. If the value -is below a given limit, the fan runs at low speed. If the value is above -the limit, the fan runs at full speed. We have no clue as to what the limit -represents. Note that there seem to be some inertia in this mode, speed -changes may need some time to trigger. Also, an hysteresis mechanism is -suspected since walking through all the values increasingly and then -decreasingly led to slightly different limits. - -When bit 7 is set, bits 3-0 seem to hold a threshold value, while bits 6-4 -would not be significant. If the value is below a given limit, the fan runs -at full speed, while if it is above the limit it runs at low speed (so this -is the contrary of the other mode, in a way). Here again, we don't know -what the limit is supposed to represent. - -One remarkable thing is that the fans would only have two or three -different speeds (transitional states left apart), not a whole range as -you usually get with PWM. - -As a conclusion, you can write 0x00 or 0x8F to the PWM registers to make -fans run at low speed, and 0x7F or 0x80 to make them run at full speed. - -Please contact us if you can figure out how it is supposed to work. As -long as we don't know more, the w83781d driver doesn't handle PWM on -AS99127F chips at all. - -Additional info about PWM on the AS99127F rev.1 by Hector Martin: - -I've been fiddling around with the (in)famous 0x59 register and -found out the following values do work as a form of coarse pwm: - -0x80 - seems to turn fans off after some time(1-2 minutes)... might be -some form of auto-fan-control based on temp? hmm (Qfan? this mobo is an -old ASUS, it isn't marketed as Qfan. Maybe some beta pre-attemp at Qfan -that was dropped at the BIOS) -0x81 - off -0x82 - slightly "on-ner" than off, but my fans do not get to move. I can -hear the high-pitched PWM sound that motors give off at too-low-pwm. -0x83 - now they do move. Estimate about 70% speed or so. -0x84-0x8f - full on - -Changing the high nibble doesn't seem to do much except the high bit -(0x80) must be set for PWM to work, else the current pwm doesn't seem to -change. - -My mobo is an ASUS A7V266-E. This behavior is similar to what I got -with speedfan under Windows, where 0-15% would be off, 15-2x% (can't -remember the exact value) would be 70% and higher would be full on. diff --git a/Documentation/i2c/chips/w83l785ts b/Documentation/i2c/chips/w83l785ts deleted file mode 100644 index 1841ced..0000000 --- a/Documentation/i2c/chips/w83l785ts +++ /dev/null @@ -1,39 +0,0 @@ -Kernel driver w83l785ts -======================= - -Supported chips: - * Winbond W83L785TS-S - Prefix: 'w83l785ts' - Addresses scanned: I2C 0x2e - Datasheet: Publicly available at the Winbond USA website - http://www.winbond-usa.com/products/winbond_products/pdfs/PCIC/W83L785TS-S.pdf - -Authors: - Jean Delvare - -Description ------------ - -The W83L785TS-S is a digital temperature sensor. It senses the -temperature of a single external diode. The high limit is -theoretically defined as 85 or 100 degrees C through a combination -of external resistors, so the user cannot change it. Values seen so -far suggest that the two possible limits are actually 95 and 110 -degrees C. The datasheet is rather poor and obviously inaccurate -on several points including this one. - -All temperature values are given in degrees Celsius. Resolution -is 1.0 degree. See the datasheet for details. - -The w83l785ts driver will not update its values more frequently than -every other second; reading them more often will do no harm, but will -return 'old' values. - -Known Issues ------------- - -On some systems (Asus), the BIOS is known to interfere with the driver -and cause read errors. The driver will retry a given number of times -(5 by default) and then give up, returning the old value (or 0 if -there is no old value). It seems to work well enough so that you should -not notice anything. Thanks to James Bolt for helping test this feature. diff --git a/Documentation/i2c/sysfs-interface b/Documentation/i2c/sysfs-interface deleted file mode 100644 index 3464005..0000000 --- a/Documentation/i2c/sysfs-interface +++ /dev/null @@ -1,274 +0,0 @@ -Naming and data format standards for sysfs files ------------------------------------------------- - -The libsensors library offers an interface to the raw sensors data -through the sysfs interface. See libsensors documentation and source for -more further information. As of writing this document, libsensors -(from lm_sensors 2.8.3) is heavily chip-dependant. Adding or updating -support for any given chip requires modifying the library's code. -This is because libsensors was written for the procfs interface -older kernel modules were using, which wasn't standardized enough. -Recent versions of libsensors (from lm_sensors 2.8.2 and later) have -support for the sysfs interface, though. - -The new sysfs interface was designed to be as chip-independant as -possible. - -Note that motherboards vary widely in the connections to sensor chips. -There is no standard that ensures, for example, that the second -temperature sensor is connected to the CPU, or that the second fan is on -the CPU. Also, some values reported by the chips need some computation -before they make full sense. For example, most chips can only measure -voltages between 0 and +4V. Other voltages are scaled back into that -range using external resistors. Since the values of these resistors -can change from motherboard to motherboard, the conversions cannot be -hard coded into the driver and have to be done in user space. - -For this reason, even if we aim at a chip-independant libsensors, it will -still require a configuration file (e.g. /etc/sensors.conf) for proper -values conversion, labeling of inputs and hiding of unused inputs. - -An alternative method that some programs use is to access the sysfs -files directly. This document briefly describes the standards that the -drivers follow, so that an application program can scan for entries and -access this data in a simple and consistent way. That said, such programs -will have to implement conversion, labeling and hiding of inputs. For -this reason, it is still not recommended to bypass the library. - -If you are developing a userspace application please send us feedback on -this standard. - -Note that this standard isn't completely established yet, so it is subject -to changes, even important ones. One more reason to use the library instead -of accessing sysfs files directly. - -Each chip gets its own directory in the sysfs /sys/devices tree. To -find all sensor chips, it is easier to follow the symlinks from -/sys/i2c/devices/ - -All sysfs values are fixed point numbers. To get the true value of some -of the values, you should divide by the specified value. - -There is only one value per file, unlike the older /proc specification. -The common scheme for files naming is: _. Usual -types for sensor chips are "in" (voltage), "temp" (temperature) and -"fan" (fan). Usual items are "input" (measured value), "max" (high -threshold, "min" (low threshold). Numbering usually starts from 1, -except for voltages which start from 0 (because most data sheets use -this). A number is always used for elements that can be present more -than once, even if there is a single element of the given type on the -specific chip. Other files do not refer to a specific element, so -they have a simple name, and no number. - -Alarms are direct indications read from the chips. The drivers do NOT -make comparisons of readings to thresholds. This allows violations -between readings to be caught and alarmed. The exact definition of an -alarm (for example, whether a threshold must be met or must be exceeded -to cause an alarm) is chip-dependent. - - -------------------------------------------------------------------------- - -************ -* Voltages * -************ - -in[0-8]_min Voltage min value. - Unit: millivolt - Read/Write - -in[0-8]_max Voltage max value. - Unit: millivolt - Read/Write - -in[0-8]_input Voltage input value. - Unit: millivolt - Read only - Actual voltage depends on the scaling resistors on the - motherboard, as recommended in the chip datasheet. - This varies by chip and by motherboard. - Because of this variation, values are generally NOT scaled - by the chip driver, and must be done by the application. - However, some drivers (notably lm87 and via686a) - do scale, with various degrees of success. - These drivers will output the actual voltage. - - Typical usage: - in0_* CPU #1 voltage (not scaled) - in1_* CPU #2 voltage (not scaled) - in2_* 3.3V nominal (not scaled) - in3_* 5.0V nominal (scaled) - in4_* 12.0V nominal (scaled) - in5_* -12.0V nominal (scaled) - in6_* -5.0V nominal (scaled) - in7_* varies - in8_* varies - -cpu[0-1]_vid CPU core reference voltage. - Unit: millivolt - Read only. - Not always correct. - -vrm Voltage Regulator Module version number. - Read only. - Two digit number, first is major version, second is - minor version. - Affects the way the driver calculates the CPU core reference - voltage from the vid pins. - - -******** -* Fans * -******** - -fan[1-3]_min Fan minimum value - Unit: revolution/min (RPM) - Read/Write. - -fan[1-3]_input Fan input value. - Unit: revolution/min (RPM) - Read only. - -fan[1-3]_div Fan divisor. - Integer value in powers of two (1, 2, 4, 8, 16, 32, 64, 128). - Some chips only support values 1, 2, 4 and 8. - Note that this is actually an internal clock divisor, which - affects the measurable speed range, not the read value. - -******* -* PWM * -******* - -pwm[1-3] Pulse width modulation fan control. - Integer value in the range 0 to 255 - Read/Write - 255 is max or 100%. - -pwm[1-3]_enable - Switch PWM on and off. - Not always present even if fan*_pwm is. - 0 to turn off - 1 to turn on in manual mode - 2 to turn on in automatic mode - Read/Write - -pwm[1-*]_auto_channels_temp - Select which temperature channels affect this PWM output in - auto mode. Bitfield, 1 is temp1, 2 is temp2, 4 is temp3 etc... - Which values are possible depend on the chip used. - -pwm[1-*]_auto_point[1-*]_pwm -pwm[1-*]_auto_point[1-*]_temp -pwm[1-*]_auto_point[1-*]_temp_hyst - Define the PWM vs temperature curve. Number of trip points is - chip-dependent. Use this for chips which associate trip points - to PWM output channels. - -OR - -temp[1-*]_auto_point[1-*]_pwm -temp[1-*]_auto_point[1-*]_temp -temp[1-*]_auto_point[1-*]_temp_hyst - Define the PWM vs temperature curve. Number of trip points is - chip-dependent. Use this for chips which associate trip points - to temperature channels. - - -**************** -* Temperatures * -**************** - -temp[1-3]_type Sensor type selection. - Integers 1, 2, 3 or thermistor Beta value (3435) - Read/Write. - 1: PII/Celeron Diode - 2: 3904 transistor - 3: thermal diode - Not all types are supported by all chips - -temp[1-4]_max Temperature max value. - Unit: millidegree Celcius - Read/Write value. - -temp[1-3]_min Temperature min value. - Unit: millidegree Celcius - Read/Write value. - -temp[1-3]_max_hyst - Temperature hysteresis value for max limit. - Unit: millidegree Celcius - Must be reported as an absolute temperature, NOT a delta - from the max value. - Read/Write value. - -temp[1-4]_input Temperature input value. - Unit: millidegree Celcius - Read only value. - -temp[1-4]_crit Temperature critical value, typically greater than - corresponding temp_max values. - Unit: millidegree Celcius - Read/Write value. - -temp[1-2]_crit_hyst - Temperature hysteresis value for critical limit. - Unit: millidegree Celcius - Must be reported as an absolute temperature, NOT a delta - from the critical value. - Read/Write value. - - If there are multiple temperature sensors, temp1_* is - generally the sensor inside the chip itself, - reported as "motherboard temperature". temp2_* to - temp4_* are generally sensors external to the chip - itself, for example the thermal diode inside the CPU or - a thermistor nearby. - - -************ -* Currents * -************ - -Note that no known chip provides current measurements as of writing, -so this part is theoretical, so to say. - -curr[1-n]_max Current max value - Unit: milliampere - Read/Write. - -curr[1-n]_min Current min value. - Unit: milliampere - Read/Write. - -curr[1-n]_input Current input value - Unit: milliampere - Read only. - - -********* -* Other * -********* - -alarms Alarm bitmask. - Read only. - Integer representation of one to four bytes. - A '1' bit means an alarm. - Chips should be programmed for 'comparator' mode so that - the alarm will 'come back' after you read the register - if it is still valid. - Generally a direct representation of a chip's internal - alarm registers; there is no standard for the position - of individual bits. - Bits are defined in kernel/include/sensors.h. - -beep_enable Beep/interrupt enable - 0 to disable. - 1 to enable. - Read/Write - -beep_mask Bitmask for beep. - Same format as 'alarms' with the same bit locations. - Read/Write - -eeprom Raw EEPROM data in binary form. - Read only. diff --git a/Documentation/i2c/userspace-tools b/Documentation/i2c/userspace-tools deleted file mode 100644 index 2622aac..0000000 --- a/Documentation/i2c/userspace-tools +++ /dev/null @@ -1,39 +0,0 @@ -Introduction ------------- - -Most mainboards have sensor chips to monitor system health (like temperatures, -voltages, fans speed). They are often connected through an I2C bus, but some -are also connected directly through the ISA bus. - -The kernel drivers make the data from the sensor chips available in the /sys -virtual filesystem. Userspace tools are then used to display or set or the -data in a more friendly manner. - -Lm-sensors ----------- - -Core set of utilites that will allow you to obtain health information, -setup monitoring limits etc. You can get them on their homepage -http://www.lm-sensors.nu/ or as a package from your Linux distribution. - -If from website: -Get lmsensors from project web site. Please note, you need only userspace -part, so compile with "make user_install" target. - -General hints to get things working: - -0) get lm-sensors userspace utils -1) compile all drivers in I2C section as modules in your kernel -2) run sensors-detect script, it will tell you what modules you need to load. -3) load them and run "sensors" command, you should see some results. -4) fix sensors.conf, labels, limits, fan divisors -5) if any more problems consult FAQ, or documentation - -Other utilites --------------- - -If you want some graphical indicators of system health look for applications -like: gkrellm, ksensors, xsensors, wmtemp, wmsensors, wmgtemp, ksysguardd, -hardware-monitor - -If you are server administrator you can try snmpd or mrtgutils. -- cgit v0.10.2