From 8254fc4afcc81e69428c453cc216aa612c80e98b Mon Sep 17 00:00:00 2001 From: Jason Gaston Date: Mon, 9 Jan 2006 10:58:08 -0800 Subject: [PATCH] i2c-i801: I2C patch for Intel ICH8 This patch adds the Intel ICH8 DID to the i2c-i801.c and Kconfig files for I2C support. Signed-off-by: Jason Gaston Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/Kconfig | 1 + drivers/i2c/busses/i2c-i801.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 08d5b8fed2d..ff92735c7c8 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -124,6 +124,7 @@ config I2C_I801 ICH6 ICH7 ESB2 + ICH8 This driver can also be built as a module. If so, the module will be called i2c-i801. diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 1c752ddc10e..8e0f3158215 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -32,6 +32,7 @@ ICH6 266A ICH7 27DA ESB2 269B + ICH8 283E This driver supports several versions of Intel's I/O Controller Hubs (ICH). For SMBus support, they are similar to the PIIX4 and are part of Intel's '810' and other chipsets. @@ -527,6 +528,7 @@ static struct pci_device_id i801_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_16) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_17) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_17) }, + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_5) }, { 0, } }; -- cgit v1.2.3 From 21bbd691827e3610ef975a88863859381ac8d8e0 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 9 Jan 2006 15:19:18 +1100 Subject: [PATCH] I2C: Resurrect i2c_smbus_write_i2c_block_data. Signed-off-by: Jean Delvare --- drivers/i2c/i2c-core.c | 15 +++++++++++++++ include/linux/i2c.h | 3 +++ 2 files changed, 18 insertions(+) diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index 0ce58b50604..1a2c9ab5d9e 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -946,6 +946,20 @@ s32 i2c_smbus_read_i2c_block_data(struct i2c_client *client, u8 command, u8 *val } } +s32 i2c_smbus_write_i2c_block_data(struct i2c_client *client, u8 command, + u8 length, u8 *values) +{ + union i2c_smbus_data data; + + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; + data.block[0] = length; + memcpy(data.block + 1, values, length); + return i2c_smbus_xfer(client->adapter, client->addr, client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_I2C_BLOCK_DATA, &data); +} + /* Simulate a SMBus command using the i2c protocol No checking of parameters is done! */ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr, @@ -1150,6 +1164,7 @@ EXPORT_SYMBOL(i2c_smbus_read_word_data); EXPORT_SYMBOL(i2c_smbus_write_word_data); EXPORT_SYMBOL(i2c_smbus_write_block_data); EXPORT_SYMBOL(i2c_smbus_read_i2c_block_data); +EXPORT_SYMBOL(i2c_smbus_write_i2c_block_data); MODULE_AUTHOR("Simon G. Vogl "); MODULE_DESCRIPTION("I2C-Bus main module"); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 7863a59bd59..63f1d63cc1d 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -100,6 +100,9 @@ extern s32 i2c_smbus_write_block_data(struct i2c_client * client, /* Returns the number of read bytes */ extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client, u8 command, u8 *values); +extern s32 i2c_smbus_write_i2c_block_data(struct i2c_client * client, + u8 command, u8 length, + u8 *values); /* * A driver is capable of handling one or more physical devices present on -- cgit v1.2.3 From 413b64515079a4063776d81067f69cc41bdb34ad Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 9 Jan 2006 22:43:08 +0100 Subject: [PATCH] hwmon: Fix negative temperature readings in lm77 driver Fix negative temperature readings in lm77 driver. Signed-off-by: Jean Delvare Acked-by: Michael Renzmann Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/lm77.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c index a2f420d01fb..df9e02aaa70 100644 --- a/drivers/hwmon/lm77.c +++ b/drivers/hwmon/lm77.c @@ -87,15 +87,15 @@ static struct i2c_driver lm77_driver = { /* 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) +static inline s16 LM77_TEMP_TO_REG(int temp) { int ntemp = SENSORS_LIMIT(temp, LM77_TEMP_MIN, LM77_TEMP_MAX); - return (u16)((ntemp / 500) * 8); + return (ntemp / 500) * 8; } -static inline int LM77_TEMP_FROM_REG(u16 reg) +static inline int LM77_TEMP_FROM_REG(s16 reg) { - return ((int)reg / 8) * 500; + return (reg / 8) * 500; } /* sysfs stuff */ -- cgit v1.2.3 From 806ba6409061ae49421319e3745026014abc49c2 Mon Sep 17 00:00:00 2001 From: Rudolf Marek Date: Mon, 9 Jan 2006 22:46:14 +0100 Subject: [PATCH] i2c: Rename i2c-sis96x documentation file This patch just renames the documentation file to correct file name. i2c-sis69x -> i2c-sis96x. Signed-off-by: Rudolf Marek Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- Documentation/i2c/busses/i2c-sis69x | 73 ------------------------------------- Documentation/i2c/busses/i2c-sis96x | 73 +++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 73 deletions(-) delete mode 100644 Documentation/i2c/busses/i2c-sis69x create mode 100644 Documentation/i2c/busses/i2c-sis96x diff --git a/Documentation/i2c/busses/i2c-sis69x b/Documentation/i2c/busses/i2c-sis69x deleted file mode 100644 index b88953dfd58..00000000000 --- a/Documentation/i2c/busses/i2c-sis69x +++ /dev/null @@ -1,73 +0,0 @@ -Kernel driver i2c-sis96x - -Replaces 2.4.x i2c-sis645 - -Supported adapters: - * Silicon Integrated Systems Corp (SiS) - Any combination of these host bridges: - 645, 645DX (aka 646), 648, 650, 651, 655, 735, 745, 746 - and these south bridges: - 961, 962, 963(L) - -Author: Mark M. Hoffman - -Description ------------ - -This SMBus only driver is known to work on motherboards with the above -named chipset combinations. The driver was developed without benefit of a -proper datasheet from SiS. The SMBus registers are assumed compatible with -those of the SiS630, although they are located in a completely different -place. Thanks to Alexander Malysh for providing the -SiS630 datasheet (and driver). - -The command "lspci" as root should produce something like these lines: - -00:00.0 Host bridge: Silicon Integrated Systems [SiS]: Unknown device 0645 -00:02.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513 -00:02.1 SMBus: Silicon Integrated Systems [SiS]: Unknown device 0016 - -or perhaps this... - -00:00.0 Host bridge: Silicon Integrated Systems [SiS]: Unknown device 0645 -00:02.0 ISA bridge: Silicon Integrated Systems [SiS]: Unknown device 0961 -00:02.1 SMBus: Silicon Integrated Systems [SiS]: Unknown device 0016 - -(kernel versions later than 2.4.18 may fill in the "Unknown"s) - -If you cant see it please look on quirk_sis_96x_smbus -(drivers/pci/quirks.c) (also if southbridge detection fails) - -I suspect that this driver could be made to work for the following SiS -chipsets as well: 635, and 635T. If anyone owns a board with those chips -AND is willing to risk crashing & burning an otherwise well-behaved kernel -in the name of progress... please contact me at or -via the project's mailing list: . Please -send bug reports and/or success stories as well. - - -TO DOs ------- - -* The driver does not support SMBus block reads/writes; I may add them if a -scenario is found where they're needed. - - -Thank You ---------- - -Mark D. Studebaker - - design hints and bug fixes -Alexander Maylsh - - ditto, plus an important datasheet... almost the one I really wanted -Hans-Günter Lütke Uphues - - patch for SiS735 -Robert Zwerus - - testing for SiS645DX -Kianusch Sayah Karadji - - patch for SiS645DX/962 -Ken Healy - - patch for SiS655 - -To anyone else who has written w/ feedback, thanks! - diff --git a/Documentation/i2c/busses/i2c-sis96x b/Documentation/i2c/busses/i2c-sis96x new file mode 100644 index 00000000000..00a009b977e --- /dev/null +++ b/Documentation/i2c/busses/i2c-sis96x @@ -0,0 +1,73 @@ +Kernel driver i2c-sis96x + +Replaces 2.4.x i2c-sis645 + +Supported adapters: + * Silicon Integrated Systems Corp (SiS) + Any combination of these host bridges: + 645, 645DX (aka 646), 648, 650, 651, 655, 735, 745, 746 + and these south bridges: + 961, 962, 963(L) + +Author: Mark M. Hoffman + +Description +----------- + +This SMBus only driver is known to work on motherboards with the above +named chipset combinations. The driver was developed without benefit of a +proper datasheet from SiS. The SMBus registers are assumed compatible with +those of the SiS630, although they are located in a completely different +place. Thanks to Alexander Malysh for providing the +SiS630 datasheet (and driver). + +The command "lspci" as root should produce something like these lines: + +00:00.0 Host bridge: Silicon Integrated Systems [SiS]: Unknown device 0645 +00:02.0 ISA bridge: Silicon Integrated Systems [SiS] 85C503/5513 +00:02.1 SMBus: Silicon Integrated Systems [SiS]: Unknown device 0016 + +or perhaps this... + +00:00.0 Host bridge: Silicon Integrated Systems [SiS]: Unknown device 0645 +00:02.0 ISA bridge: Silicon Integrated Systems [SiS]: Unknown device 0961 +00:02.1 SMBus: Silicon Integrated Systems [SiS]: Unknown device 0016 + +(kernel versions later than 2.4.18 may fill in the "Unknown"s) + +If you cant see it please look on quirk_sis_96x_smbus +(drivers/pci/quirks.c) (also if southbridge detection fails) + +I suspect that this driver could be made to work for the following SiS +chipsets as well: 635, and 635T. If anyone owns a board with those chips +AND is willing to risk crashing & burning an otherwise well-behaved kernel +in the name of progress... please contact me at or +via the project's mailing list: . Please +send bug reports and/or success stories as well. + + +TO DOs +------ + +* The driver does not support SMBus block reads/writes; I may add them if a +scenario is found where they're needed. + + +Thank You +--------- + +Mark D. Studebaker + - design hints and bug fixes +Alexander Maylsh + - ditto, plus an important datasheet... almost the one I really wanted +Hans-Günter Lütke Uphues + - patch for SiS735 +Robert Zwerus + - testing for SiS645DX +Kianusch Sayah Karadji + - patch for SiS645DX/962 +Ken Healy + - patch for SiS655 + +To anyone else who has written w/ feedback, thanks! + -- cgit v1.2.3 From 0d0ab7fe4c009c40dc485731f9ad98e1d336ddae Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 9 Jan 2006 23:07:05 +0100 Subject: [PATCH] hwmon: Inline w83792d register access functions Inline w83792d_{read,write}_value for better performance. Signed-off-by: Jean Delvare Acked-by: Yuan Mu Signed-off-by: Greg Kroah-Hartman --- drivers/hwmon/w83792d.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index b176bf0c4c7..a2f6bb67623 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -303,10 +303,6 @@ struct w83792d_data { static int w83792d_attach_adapter(struct i2c_adapter *adapter); static int w83792d_detect(struct i2c_adapter *adapter, int address, int kind); static int w83792d_detach_client(struct i2c_client *client); - -static int w83792d_read_value(struct i2c_client *client, u8 register); -static int w83792d_write_value(struct i2c_client *client, u8 register, - u8 value); static struct w83792d_data *w83792d_update_device(struct device *dev); #ifdef DEBUG @@ -329,6 +325,20 @@ static inline long in_count_from_reg(int nr, struct w83792d_data *data) return ((data->in[nr] << 2) | ((data->low_bits >> (2 * nr)) & 0x03)); } +/* The SMBus locks itself. The Winbond W83792D chip has a bank register, + but the driver only accesses registers in bank 0, so we don't have + to switch banks and lock access between switches. */ +static inline int w83792d_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +static inline int +w83792d_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + /* following are the sysfs callback functions */ static ssize_t show_in(struct device *dev, struct device_attribute *attr, char *buf) @@ -1386,19 +1396,6 @@ w83792d_detach_client(struct i2c_client *client) return 0; } -/* The SMBus locks itself. The Winbond W83792D chip has a bank register, - but the driver only accesses registers in bank 0, so we don't have - to switch banks and lock access between switches. */ -static int w83792d_read_value(struct i2c_client *client, u8 reg) -{ - return i2c_smbus_read_byte_data(client, reg); -} - -static int w83792d_write_value(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - static void w83792d_init_client(struct i2c_client *client) { -- cgit v1.2.3 From 8104a9a9c9ad8c849d931c46ef6841b23d1fc729 Mon Sep 17 00:00:00 2001 From: Eric Sesterhenn Date: Mon, 9 Jan 2006 23:09:57 +0100 Subject: [PATCH] i2c: Use module_param in i2c-algo-sibyte this patch changes MODULE_PARM usage to module_param in i2c-algo-sibyte.c Signed-off-by: Eric Sesterhenn Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/algos/i2c-algo-sibyte.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/algos/i2c-algo-sibyte.c b/drivers/i2c/algos/i2c-algo-sibyte.c index 938848ae162..3df3f09995c 100644 --- a/drivers/i2c/algos/i2c-algo-sibyte.c +++ b/drivers/i2c/algos/i2c-algo-sibyte.c @@ -202,7 +202,7 @@ EXPORT_SYMBOL(i2c_sibyte_del_bus); #ifdef MODULE MODULE_AUTHOR("Kip Walker, Broadcom Corp."); MODULE_DESCRIPTION("SiByte I2C-Bus algorithm"); -MODULE_PARM(bit_scan, "i"); +module_param(bit_scan, int, 0); MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus"); MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 7e3d7db52469f6bcfbfd2d3d00dd17da573facd9 Mon Sep 17 00:00:00 2001 From: Tobias Klauser Date: Mon, 9 Jan 2006 23:19:51 +0100 Subject: [PATCH] i2c: Use ARRAY_SIZE macro Use ARRAY_SIZE macro instead of sizeof(x)/sizeof(x[0]). Some trailing whitespaces are also removed. Signed-off-by: Tobias Klauser Signed-off-by: Jean Delvare Cc: Russell King Signed-off-by: Greg Kroah-Hartman --- drivers/i2c/busses/i2c-parport-light.c | 9 +++------ drivers/i2c/busses/i2c-parport.c | 7 ++----- drivers/i2c/busses/i2c-pxa.c | 2 +- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c index 3e5eba9fcac..c63025a4c86 100644 --- a/drivers/i2c/busses/i2c-parport-light.c +++ b/drivers/i2c/busses/i2c-parport-light.c @@ -121,14 +121,11 @@ static struct i2c_adapter parport_adapter = { static int __init i2c_parport_init(void) { - int type_count; - - type_count = sizeof(adapter_parm)/sizeof(struct adapter_parm); - if (type < 0 || type >= type_count) { + if (type < 0 || type >= ARRAY_SIZE(adapter_parm)) { printk(KERN_WARNING "i2c-parport: invalid type (%d)\n", type); type = 0; } - + if (base == 0) { printk(KERN_INFO "i2c-parport: using default base 0x%x\n", DEFAULT_BASE); base = DEFAULT_BASE; @@ -152,7 +149,7 @@ static int __init i2c_parport_init(void) release_region(base, 3); return -ENODEV; } - + return 0; } diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c index 2854d858fc9..7e2e8cd1c14 100644 --- a/drivers/i2c/busses/i2c-parport.c +++ b/drivers/i2c/busses/i2c-parport.c @@ -241,14 +241,11 @@ static struct parport_driver i2c_parport_driver = { static int __init i2c_parport_init(void) { - int type_count; - - type_count = sizeof(adapter_parm)/sizeof(struct adapter_parm); - if (type < 0 || type >= type_count) { + if (type < 0 || type >= ARRAY_SIZE(adapter_parm)) { printk(KERN_WARNING "i2c-parport: invalid type (%d)\n", type); type = 0; } - + return parport_register_driver(&i2c_parport_driver); } diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 86e2234faf8..7579f4b256a 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -861,7 +861,7 @@ static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id, struct pt_regs *r decode_ISR(isr); } - if (i2c->irqlogidx < sizeof(i2c->isrlog)/sizeof(u32)) + if (i2c->irqlogidx < ARRAY_SIZE(i2c->isrlog)) i2c->isrlog[i2c->irqlogidx++] = isr; show_state(i2c); -- cgit v1.2.3 From 5db3d3da93fb93875c719239892ac526cfd2d584 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 9 Jan 2006 23:32:57 +0100 Subject: [PATCH] hwmon: Add f71805f documentation Add some documentation for the new f71805f driver. This is almost the same help that was present in lm_sensors, with a few minor layout fixes. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- Documentation/hwmon/f71805f | 105 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 Documentation/hwmon/f71805f diff --git a/Documentation/hwmon/f71805f b/Documentation/hwmon/f71805f new file mode 100644 index 00000000000..28c5b7d1eb9 --- /dev/null +++ b/Documentation/hwmon/f71805f @@ -0,0 +1,105 @@ +Kernel driver f71805f +===================== + +Supported chips: + * Fintek F71805F/FG + Prefix: 'f71805f' + Addresses scanned: none, address read from Super I/O config space + Datasheet: Provided by Fintek on request + +Author: Jean Delvare + +Thanks to Denis Kieft from Barracuda Networks for the donation of a +test system (custom Jetway K8M8MS motherboard, with CPU and RAM) and +for providing initial documentation. + +Thanks to Kris Chen from Fintek for answering technical questions and +providing additional documentation. + +Thanks to Chris Lin from Jetway for providing wiring schematics and +anwsering technical questions. + + +Description +----------- + +The Fintek F71805F/FG Super I/O chip includes complete hardware monitoring +capabilities. It can monitor up to 9 voltages (counting its own power +source), 3 fans and 3 temperature sensors. + +This chip also has fan controlling features, using either DC or PWM, in +three different modes (one manual, two automatic). The driver doesn't +support these features yet. + +The driver assumes that no more than one chip is present, which seems +reasonable. + + +Voltage Monitoring +------------------ + +Voltages are sampled by an 8-bit ADC with a LSB of 8 mV. The supported +range is thus from 0 to 2.040 V. Voltage values outside of this range +need external resistors. An exception is in0, which is used to monitor +the chip's own power source (+3.3V), and is divided internally by a +factor 2. + +The two LSB of the voltage limit registers are not used (always 0), so +you can only set the limits in steps of 32 mV (before scaling). + +The wirings and resistor values suggested by Fintek are as follow: + + pin expected + name use R1 R2 divider raw val. + +in0 VCC VCC3.3V int. int. 2.00 1.65 V +in1 VIN1 VTT1.2V 10K - 1.00 1.20 V +in2 VIN2 VRAM 100K 100K 2.00 ~1.25 V (1) +in3 VIN3 VCHIPSET 47K 100K 1.47 2.24 V (2) +in4 VIN4 VCC5V 200K 47K 5.25 0.95 V +in5 VIN5 +12V 200K 20K 11.00 1.05 V +in6 VIN6 VCC1.5V 10K - 1.00 1.50 V +in7 VIN7 VCORE 10K - 1.00 ~1.40 V (1) +in8 VIN8 VSB5V 200K 47K 1.00 0.95 V + +(1) Depends on your hardware setup. +(2) Obviously not correct, swapping R1 and R2 would make more sense. + +These values can be used as hints at best, as motherboard manufacturers +are free to use a completely different setup. As a matter of fact, the +Jetway K8M8MS uses a significantly different setup. You will have to +find out documentation about your own motherboard, and edit sensors.conf +accordingly. + +Each voltage measured has associated low and high limits, each of which +triggers an alarm when crossed. + + +Fan Monitoring +-------------- + +Fan rotation speeds are reported as 12-bit values from a gated clock +signal. Speeds down to 366 RPM can be measured. There is no theoretical +high limit, but values over 6000 RPM seem to cause problem. The effective +resolution is much lower than you would expect, the step between different +register values being 10 rather than 1. + +The chip assumes 2 pulse-per-revolution fans. + +An alarm is triggered if the rotation speed drops below a programmable +limit or is too low to be measured. + + +Temperature Monitoring +---------------------- + +Temperatures are reported in degrees Celsius. Each temperature measured +has a high limit, those crossing triggers an alarm. There is an associated +hysteresis value, below which the temperature has to drop before the +alarm is cleared. + +All temperature channels are external, there is no embedded temperature +sensor. Each channel can be used for connecting either a thermal diode +or a thermistor. The driver reports the currently selected mode, but +doesn't allow changing it. In theory, the BIOS should have configured +everything properly. -- cgit v1.2.3 From e53004e20a58e9d28347e02adccb37a33e0d771a Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Mon, 9 Jan 2006 23:26:14 +0100 Subject: [PATCH] hwmon: New f71805f driver This is my f71805f hardware monitoring driver ported from lm_sensors to Linux 2.6. This new driver differs from the other hardware monitoring drivers in that it is implemented as a platform driver. This might not be optimal yet (we would probably need a generic infrastructure and bus type for Super-I/O logical devices) but it is certainly much better than the i2c-isa solution. Note that this driver requires lm_sensors CVS. I hope to get it released as 2.10.0 soon. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- Documentation/hwmon/sysfs-interface | 18 +- MAINTAINERS | 6 + drivers/hwmon/Kconfig | 10 + drivers/hwmon/Makefile | 1 + drivers/hwmon/f71805f.c | 908 ++++++++++++++++++++++++++++++++++++ 5 files changed, 942 insertions(+), 1 deletion(-) create mode 100644 drivers/hwmon/f71805f.c diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index 764cdc5480e..a0d0ab24288 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -179,11 +179,12 @@ temp[1-*]_auto_point[1-*]_temp_hyst **************** temp[1-3]_type Sensor type selection. - Integers 1, 2, 3 or thermistor Beta value (3435) + Integers 1 to 4 or thermistor Beta value (typically 3435) Read/Write. 1: PII/Celeron Diode 2: 3904 transistor 3: thermal diode + 4: thermistor (default/unknown Beta) Not all types are supported by all chips temp[1-4]_max Temperature max value. @@ -261,6 +262,21 @@ alarms Alarm bitmask. of individual bits. Bits are defined in kernel/include/sensors.h. +alarms_in Alarm bitmask relative to in (voltage) channels + Read only + A '1' bit means an alarm, LSB corresponds to in0 and so on + Prefered to 'alarms' for newer chips + +alarms_fan Alarm bitmask relative to fan channels + Read only + A '1' bit means an alarm, LSB corresponds to fan1 and so on + Prefered to 'alarms' for newer chips + +alarms_temp Alarm bitmask relative to temp (temperature) channels + Read only + A '1' bit means an alarm, LSB corresponds to temp1 and so on + Prefered to 'alarms' for newer chips + beep_enable Beep/interrupt enable 0 to disable. 1 to enable. diff --git a/MAINTAINERS b/MAINTAINERS index 11d44daa602..5b7a154d443 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -929,6 +929,12 @@ M: sct@redhat.com, akpm@osdl.org, adilger@clusterfs.com L: ext3-users@redhat.com S: Maintained +F71805F HARDWARE MONITORING DRIVER +P: Jean Delvare +M: khali@linux-fr.org +L: lm-sensors@lm-sensors.org +S: Maintained + FARSYNC SYNCHRONOUS DRIVER P: Kevin Curtis M: kevin.curtis@farsite.co.uk diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index c5829591436..7230d4e0819 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -113,6 +113,16 @@ config SENSORS_DS1621 This driver can also be built as a module. If so, the module will be called ds1621. +config SENSORS_F71805F + tristate "Fintek F71805F/FG" + depends on HWMON && EXPERIMENTAL + help + If you say yes here you get support for hardware monitoring + features of the Fintek F71805F/FG chips. + + This driver can also be built as a module. If so, the module + will be called f71805f. + config SENSORS_FSCHER tristate "FSC Hermes" depends on HWMON && I2C && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 06d4a1d1410..fbdb8d911a7 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -18,6 +18,7 @@ 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_F71805F) += f71805f.o obj-$(CONFIG_SENSORS_FSCHER) += fscher.o obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c new file mode 100644 index 00000000000..e029e0a94ec --- /dev/null +++ b/drivers/hwmon/f71805f.c @@ -0,0 +1,908 @@ +/* + * f71805f.c - driver for the Fintek F71805F/FG Super-I/O chip integrated + * hardware monitoring features + * Copyright (C) 2005 Jean Delvare + * + * The F71805F/FG is a LPC Super-I/O chip made by Fintek. It integrates + * complete hardware monitoring features: voltage, fan and temperature + * sensors, and manual and automatic fan speed control. + * + * 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 +#include + +static struct platform_device *pdev; + +#define DRVNAME "f71805f" + +/* + * Super-I/O constants and functions + */ + +#define F71805F_LD_HWM 0x04 + +#define SIO_REG_LDSEL 0x07 /* Logical device select */ +#define SIO_REG_DEVID 0x20 /* Device ID (2 bytes) */ +#define SIO_REG_DEVREV 0x22 /* Device revision */ +#define SIO_REG_MANID 0x23 /* Fintek ID (2 bytes) */ +#define SIO_REG_ENABLE 0x30 /* Logical device enable */ +#define SIO_REG_ADDR 0x60 /* Logical device address (2 bytes) */ + +#define SIO_FINTEK_ID 0x1934 +#define SIO_F71805F_ID 0x0406 + +static inline int +superio_inb(int base, int reg) +{ + outb(reg, base); + return inb(base + 1); +} + +static int +superio_inw(int base, int reg) +{ + int val; + outb(reg++, base); + val = inb(base + 1) << 8; + outb(reg, base); + val |= inb(base + 1); + return val; +} + +static inline void +superio_select(int base, int ld) +{ + outb(SIO_REG_LDSEL, base); + outb(ld, base + 1); +} + +static inline void +superio_enter(int base) +{ + outb(0x87, base); + outb(0x87, base); +} + +static inline void +superio_exit(int base) +{ + outb(0xaa, base); +} + +/* + * ISA constants + */ + +#define REGION_LENGTH 2 +#define ADDR_REG_OFFSET 0 +#define DATA_REG_OFFSET 1 + +static struct resource f71805f_resource __initdata = { + .flags = IORESOURCE_IO, +}; + +/* + * Registers + */ + +/* in nr from 0 to 8 (8-bit values) */ +#define F71805F_REG_IN(nr) (0x10 + (nr)) +#define F71805F_REG_IN_HIGH(nr) (0x40 + 2 * (nr)) +#define F71805F_REG_IN_LOW(nr) (0x41 + 2 * (nr)) +/* fan nr from 0 to 2 (12-bit values, two registers) */ +#define F71805F_REG_FAN(nr) (0x20 + 2 * (nr)) +#define F71805F_REG_FAN_LOW(nr) (0x28 + 2 * (nr)) +#define F71805F_REG_FAN_CTRL(nr) (0x60 + 16 * (nr)) +/* temp nr from 0 to 2 (8-bit values) */ +#define F71805F_REG_TEMP(nr) (0x1B + (nr)) +#define F71805F_REG_TEMP_HIGH(nr) (0x54 + 2 * (nr)) +#define F71805F_REG_TEMP_HYST(nr) (0x55 + 2 * (nr)) +#define F71805F_REG_TEMP_MODE 0x01 + +#define F71805F_REG_START 0x00 +/* status nr from 0 to 2 */ +#define F71805F_REG_STATUS(nr) (0x36 + (nr)) + +/* + * Data structures and manipulation thereof + */ + +struct f71805f_data { + unsigned short addr; + const char *name; + struct semaphore lock; + struct class_device *class_dev; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long last_limits; /* In jiffies */ + + /* Register values */ + u8 in[9]; + u8 in_high[9]; + u8 in_low[9]; + u16 fan[3]; + u16 fan_low[3]; + u8 fan_enabled; /* Read once at init time */ + u8 temp[3]; + u8 temp_high[3]; + u8 temp_hyst[3]; + u8 temp_mode; + u8 alarms[3]; +}; + +static inline long in_from_reg(u8 reg) +{ + return (reg * 8); +} + +/* The 2 least significant bits are not used */ +static inline u8 in_to_reg(long val) +{ + if (val <= 0) + return 0; + if (val >= 2016) + return 0xfc; + return (((val + 16) / 32) << 2); +} + +/* in0 is downscaled by a factor 2 internally */ +static inline long in0_from_reg(u8 reg) +{ + return (reg * 16); +} + +static inline u8 in0_to_reg(long val) +{ + if (val <= 0) + return 0; + if (val >= 4032) + return 0xfc; + return (((val + 32) / 64) << 2); +} + +/* The 4 most significant bits are not used */ +static inline long fan_from_reg(u16 reg) +{ + reg &= 0xfff; + if (!reg || reg == 0xfff) + return 0; + return (1500000 / reg); +} + +static inline u16 fan_to_reg(long rpm) +{ + /* If the low limit is set below what the chip can measure, + store the largest possible 12-bit value in the registers, + so that no alarm will ever trigger. */ + if (rpm < 367) + return 0xfff; + return (1500000 / rpm); +} + +static inline long temp_from_reg(u8 reg) +{ + return (reg * 1000); +} + +static inline u8 temp_to_reg(long val) +{ + if (val < 0) + val = 0; + else if (val > 1000 * 0xff) + val = 0xff; + return ((val + 500) / 1000); +} + +/* + * Device I/O access + */ + +static u8 f71805f_read8(struct f71805f_data *data, u8 reg) +{ + u8 val; + + down(&data->lock); + outb(reg, data->addr + ADDR_REG_OFFSET); + val = inb(data->addr + DATA_REG_OFFSET); + up(&data->lock); + + return val; +} + +static void f71805f_write8(struct f71805f_data *data, u8 reg, u8 val) +{ + down(&data->lock); + outb(reg, data->addr + ADDR_REG_OFFSET); + outb(val, data->addr + DATA_REG_OFFSET); + up(&data->lock); +} + +/* It is important to read the MSB first, because doing so latches the + value of the LSB, so we are sure both bytes belong to the same value. */ +static u16 f71805f_read16(struct f71805f_data *data, u8 reg) +{ + u16 val; + + down(&data->lock); + outb(reg, data->addr + ADDR_REG_OFFSET); + val = inb(data->addr + DATA_REG_OFFSET) << 8; + outb(++reg, data->addr + ADDR_REG_OFFSET); + val |= inb(data->addr + DATA_REG_OFFSET); + up(&data->lock); + + return val; +} + +static void f71805f_write16(struct f71805f_data *data, u8 reg, u16 val) +{ + down(&data->lock); + outb(reg, data->addr + ADDR_REG_OFFSET); + outb(val >> 8, data->addr + DATA_REG_OFFSET); + outb(++reg, data->addr + ADDR_REG_OFFSET); + outb(val & 0xff, data->addr + DATA_REG_OFFSET); + up(&data->lock); +} + +static struct f71805f_data *f71805f_update_device(struct device *dev) +{ + struct f71805f_data *data = dev_get_drvdata(dev); + int nr; + + down(&data->update_lock); + + /* Limit registers cache is refreshed after 60 seconds */ + if (time_after(jiffies, data->last_updated + 60 * HZ) + || !data->valid) { + for (nr = 0; nr < 9; nr++) { + data->in_high[nr] = f71805f_read8(data, + F71805F_REG_IN_HIGH(nr)); + data->in_low[nr] = f71805f_read8(data, + F71805F_REG_IN_LOW(nr)); + } + for (nr = 0; nr < 3; nr++) { + if (data->fan_enabled & (1 << nr)) + data->fan_low[nr] = f71805f_read16(data, + F71805F_REG_FAN_LOW(nr)); + } + for (nr = 0; nr < 3; nr++) { + data->temp_high[nr] = f71805f_read8(data, + F71805F_REG_TEMP_HIGH(nr)); + data->temp_hyst[nr] = f71805f_read8(data, + F71805F_REG_TEMP_HYST(nr)); + } + data->temp_mode = f71805f_read8(data, F71805F_REG_TEMP_MODE); + + data->last_limits = jiffies; + } + + /* Measurement registers cache is refreshed after 1 second */ + if (time_after(jiffies, data->last_updated + HZ) + || !data->valid) { + for (nr = 0; nr < 9; nr++) { + data->in[nr] = f71805f_read8(data, + F71805F_REG_IN(nr)); + } + for (nr = 0; nr < 3; nr++) { + if (data->fan_enabled & (1 << nr)) + data->fan[nr] = f71805f_read16(data, + F71805F_REG_FAN(nr)); + } + for (nr = 0; nr < 3; nr++) { + data->temp[nr] = f71805f_read8(data, + F71805F_REG_TEMP(nr)); + } + for (nr = 0; nr < 3; nr++) { + data->alarms[nr] = f71805f_read8(data, + F71805F_REG_STATUS(nr)); + } + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); + + return data; +} + +/* + * Sysfs interface + */ + +static ssize_t show_in0(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + + return sprintf(buf, "%ld\n", in0_from_reg(data->in[0])); +} + +static ssize_t show_in0_max(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + + return sprintf(buf, "%ld\n", in0_from_reg(data->in_high[0])); +} + +static ssize_t show_in0_min(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + + return sprintf(buf, "%ld\n", in0_from_reg(data->in_low[0])); +} + +static ssize_t set_in0_max(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71805f_data *data = dev_get_drvdata(dev); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->in_high[0] = in0_to_reg(val); + f71805f_write8(data, F71805F_REG_IN_HIGH(0), data->in_high[0]); + up(&data->update_lock); + + return count; +} + +static ssize_t set_in0_min(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71805f_data *data = dev_get_drvdata(dev); + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->in_low[0] = in0_to_reg(val); + f71805f_write8(data, F71805F_REG_IN_LOW(0), data->in_low[0]); + up(&data->update_lock); + + return count; +} + +static DEVICE_ATTR(in0_input, S_IRUGO, show_in0, NULL); +static DEVICE_ATTR(in0_max, S_IRUGO| S_IWUSR, show_in0_max, set_in0_max); +static DEVICE_ATTR(in0_min, S_IRUGO| S_IWUSR, show_in0_min, set_in0_min); + +static ssize_t show_in(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + + return sprintf(buf, "%ld\n", in_from_reg(data->in[nr])); +} + +static ssize_t show_in_max(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + + return sprintf(buf, "%ld\n", in_from_reg(data->in_high[nr])); +} + +static ssize_t show_in_min(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + + return sprintf(buf, "%ld\n", in_from_reg(data->in_low[nr])); +} + +static ssize_t set_in_max(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71805f_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->in_high[nr] = in_to_reg(val); + f71805f_write8(data, F71805F_REG_IN_HIGH(nr), data->in_high[nr]); + up(&data->update_lock); + + return count; +} + +static ssize_t set_in_min(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71805f_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->in_low[nr] = in_to_reg(val); + f71805f_write8(data, F71805F_REG_IN_LOW(nr), data->in_low[nr]); + up(&data->update_lock); + + return count; +} + +#define sysfs_in(offset) \ +static SENSOR_DEVICE_ATTR(in##offset##_input, S_IRUGO, \ + show_in, NULL, offset); \ +static SENSOR_DEVICE_ATTR(in##offset##_max, S_IRUGO | S_IWUSR, \ + show_in_max, set_in_max, offset); \ +static SENSOR_DEVICE_ATTR(in##offset##_min, S_IRUGO | S_IWUSR, \ + show_in_min, set_in_min, offset) + +sysfs_in(1); +sysfs_in(2); +sysfs_in(3); +sysfs_in(4); +sysfs_in(5); +sysfs_in(6); +sysfs_in(7); +sysfs_in(8); + +static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + + return sprintf(buf, "%ld\n", fan_from_reg(data->fan[nr])); +} + +static ssize_t show_fan_min(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + + return sprintf(buf, "%ld\n", fan_from_reg(data->fan_low[nr])); +} + +static ssize_t set_fan_min(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71805f_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->fan_low[nr] = fan_to_reg(val); + f71805f_write16(data, F71805F_REG_FAN_LOW(nr), data->fan_low[nr]); + up(&data->update_lock); + + return count; +} + +#define sysfs_fan(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) + +sysfs_fan(1); +sysfs_fan(2); +sysfs_fan(3); + +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + + return sprintf(buf, "%ld\n", temp_from_reg(data->temp[nr])); +} + +static ssize_t show_temp_max(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + + return sprintf(buf, "%ld\n", temp_from_reg(data->temp_high[nr])); +} + +static ssize_t show_temp_hyst(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + + return sprintf(buf, "%ld\n", temp_from_reg(data->temp_hyst[nr])); +} + +static ssize_t show_temp_type(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + + /* 3 is diode, 4 is thermistor */ + return sprintf(buf, "%u\n", (data->temp_mode & (1 << nr)) ? 3 : 4); +} + +static ssize_t set_temp_max(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71805f_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_high[nr] = temp_to_reg(val); + f71805f_write8(data, F71805F_REG_TEMP_HIGH(nr), data->temp_high[nr]); + up(&data->update_lock); + + return count; +} + +static ssize_t set_temp_hyst(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct f71805f_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + long val = simple_strtol(buf, NULL, 10); + + down(&data->update_lock); + data->temp_hyst[nr] = temp_to_reg(val); + f71805f_write8(data, F71805F_REG_TEMP_HYST(nr), data->temp_hyst[nr]); + up(&data->update_lock); + + return count; +} + +#define sysfs_temp(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##_max_hyst, S_IRUGO | S_IWUSR, \ + show_temp_hyst, set_temp_hyst, offset - 1); \ +static SENSOR_DEVICE_ATTR(temp##offset##_type, S_IRUGO, \ + show_temp_type, NULL, offset - 1) + +sysfs_temp(1); +sysfs_temp(2); +sysfs_temp(3); + +static ssize_t show_alarms_in(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + + return sprintf(buf, "%d\n", data->alarms[0] | + ((data->alarms[1] & 0x01) << 8)); +} + +static ssize_t show_alarms_fan(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + + return sprintf(buf, "%d\n", data->alarms[2] & 0x07); +} + +static ssize_t show_alarms_temp(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = f71805f_update_device(dev); + + return sprintf(buf, "%d\n", (data->alarms[1] >> 3) & 0x07); +} + +static DEVICE_ATTR(alarms_in, S_IRUGO, show_alarms_in, NULL); +static DEVICE_ATTR(alarms_fan, S_IRUGO, show_alarms_fan, NULL); +static DEVICE_ATTR(alarms_temp, S_IRUGO, show_alarms_temp, NULL); + +static ssize_t show_name(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct f71805f_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} + +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +/* + * Device registration and initialization + */ + +static void __devinit f71805f_init_device(struct f71805f_data *data) +{ + u8 reg; + int i; + + reg = f71805f_read8(data, F71805F_REG_START); + if ((reg & 0x41) != 0x01) { + printk(KERN_DEBUG DRVNAME ": Starting monitoring " + "operations\n"); + f71805f_write8(data, F71805F_REG_START, (reg | 0x01) & ~0x40); + } + + /* Fan monitoring can be disabled. If it is, we won't be polling + the register values, and won't create the related sysfs files. */ + for (i = 0; i < 3; i++) { + reg = f71805f_read8(data, F71805F_REG_FAN_CTRL(i)); + if (!(reg & 0x80)) + data->fan_enabled |= (1 << i); + } +} + +static int __devinit f71805f_probe(struct platform_device *pdev) +{ + struct f71805f_data *data; + struct resource *res; + int err; + + if (!(data = kzalloc(sizeof(struct f71805f_data), GFP_KERNEL))) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Out of memory\n"); + goto exit; + } + + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + data->addr = res->start; + init_MUTEX(&data->lock); + data->name = "f71805f"; + init_MUTEX(&data->update_lock); + + platform_set_drvdata(pdev, data); + + data->class_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(data->class_dev)) { + err = PTR_ERR(data->class_dev); + dev_err(&pdev->dev, "Class registration failed (%d)\n", err); + goto exit_free; + } + + /* Initialize the F71805F chip */ + f71805f_init_device(data); + + /* Register sysfs interface files */ + device_create_file(&pdev->dev, &dev_attr_in0_input); + device_create_file(&pdev->dev, &dev_attr_in0_max); + device_create_file(&pdev->dev, &dev_attr_in0_min); + device_create_file(&pdev->dev, &sensor_dev_attr_in1_input.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in2_input.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in3_input.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in4_input.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in5_input.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in6_input.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in7_input.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in8_input.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in1_max.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in2_max.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in3_max.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in4_max.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in5_max.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in6_max.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in7_max.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in8_max.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in1_min.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in2_min.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in3_min.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in4_min.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in5_min.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in6_min.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in7_min.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_in8_min.dev_attr); + if (data->fan_enabled & (1 << 0)) { + device_create_file(&pdev->dev, + &sensor_dev_attr_fan1_input.dev_attr); + device_create_file(&pdev->dev, + &sensor_dev_attr_fan1_min.dev_attr); + } + if (data->fan_enabled & (1 << 1)) { + device_create_file(&pdev->dev, + &sensor_dev_attr_fan2_input.dev_attr); + device_create_file(&pdev->dev, + &sensor_dev_attr_fan2_min.dev_attr); + } + if (data->fan_enabled & (1 << 2)) { + device_create_file(&pdev->dev, + &sensor_dev_attr_fan3_input.dev_attr); + device_create_file(&pdev->dev, + &sensor_dev_attr_fan3_min.dev_attr); + } + device_create_file(&pdev->dev, + &sensor_dev_attr_temp1_input.dev_attr); + device_create_file(&pdev->dev, + &sensor_dev_attr_temp2_input.dev_attr); + device_create_file(&pdev->dev, + &sensor_dev_attr_temp3_input.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_temp1_max.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_temp2_max.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_temp3_max.dev_attr); + device_create_file(&pdev->dev, + &sensor_dev_attr_temp1_max_hyst.dev_attr); + device_create_file(&pdev->dev, + &sensor_dev_attr_temp2_max_hyst.dev_attr); + device_create_file(&pdev->dev, + &sensor_dev_attr_temp3_max_hyst.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_temp1_type.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_temp2_type.dev_attr); + device_create_file(&pdev->dev, &sensor_dev_attr_temp3_type.dev_attr); + device_create_file(&pdev->dev, &dev_attr_alarms_in); + device_create_file(&pdev->dev, &dev_attr_alarms_fan); + device_create_file(&pdev->dev, &dev_attr_alarms_temp); + device_create_file(&pdev->dev, &dev_attr_name); + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static int __devexit f71805f_remove(struct platform_device *pdev) +{ + struct f71805f_data *data = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + hwmon_device_unregister(data->class_dev); + kfree(data); + + return 0; +} + +static struct platform_driver f71805f_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, + .probe = f71805f_probe, + .remove = __devexit_p(f71805f_remove), +}; + +static int __init f71805f_device_add(unsigned short address) +{ + int err; + + pdev = platform_device_alloc(DRVNAME, address); + if (!pdev) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + goto exit; + } + + f71805f_resource.start = address; + f71805f_resource.end = address + REGION_LENGTH - 1; + f71805f_resource.name = pdev->name; + err = platform_device_add_resources(pdev, &f71805f_resource, 1); + if (err) { + printk(KERN_ERR DRVNAME ": Device resource addition failed " + "(%d)\n", err); + goto exit_device_put; + } + + err = platform_device_add(pdev); + if (err) { + printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", + err); + goto exit_device_put; + } + + return 0; + +exit_device_put: + platform_device_put(pdev); +exit: + return err; +} + +static int __init f71805f_find(int sioaddr, unsigned short *address) +{ + int err = -ENODEV; + u16 devid; + + superio_enter(sioaddr); + + devid = superio_inw(sioaddr, SIO_REG_MANID); + if (devid != SIO_FINTEK_ID) + goto exit; + + devid = superio_inw(sioaddr, SIO_REG_DEVID); + if (devid != SIO_F71805F_ID) { + printk(KERN_INFO DRVNAME ": Unsupported Fintek device, " + "skipping\n"); + goto exit; + } + + superio_select(sioaddr, F71805F_LD_HWM); + if (!(superio_inb(sioaddr, SIO_REG_ENABLE) & 0x01)) { + printk(KERN_WARNING DRVNAME ": Device not activated, " + "skipping\n"); + goto exit; + } + + *address = superio_inw(sioaddr, SIO_REG_ADDR); + if (*address == 0) { + printk(KERN_WARNING DRVNAME ": Base address not set, " + "skipping\n"); + goto exit; + } + + err = 0; + printk(KERN_INFO DRVNAME ": Found F71805F chip at %#x, revision %u\n", + *address, superio_inb(sioaddr, SIO_REG_DEVREV)); + +exit: + superio_exit(sioaddr); + return err; +} + +static int __init f71805f_init(void) +{ + int err; + unsigned short address; + + if (f71805f_find(0x2e, &address) + && f71805f_find(0x4e, &address)) + return -ENODEV; + + err = platform_driver_register(&f71805f_driver); + if (err) + goto exit; + + /* Sets global pdev as a side effect */ + err = f71805f_device_add(address); + if (err) + goto exit_driver; + + return 0; + +exit_driver: + platform_driver_unregister(&f71805f_driver); +exit: + return err; +} + +static void __exit f71805f_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&f71805f_driver); +} + +MODULE_AUTHOR("Jean Delvare "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("F71805F hardware monitoring driver"); + +module_init(f71805f_init); +module_exit(f71805f_exit); -- cgit v1.2.3 From c5e3fbf22ccba0879b174fab7ec0e322b1266c2c Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Wed, 18 Jan 2006 22:39:48 +0100 Subject: [PATCH] hwmon: Fix reboot on it87 driver load Only scan I2C address 0x2d. This is the default address and no IT87xxF chip was ever seen on I2C at a different address. These chips are better accessed through their ISA interface anyway. This fixes bug #5889, although it doesn't address the whole class of problems. We'd need the ability to blacklist arbitrary I2C addresses on systems known to contain I2C devices which behave badly when probed. Plan the I2C interface for removal as well. If nobody complains within a year, it will confirm my impression that the I2C interface isn't actually needed by anyone. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- Documentation/feature-removal-schedule.txt | 9 +++++++++ Documentation/hwmon/it87 | 2 +- drivers/hwmon/it87.c | 8 ++++++-- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 4d4897c8ef9..b730d765b52 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -162,3 +162,12 @@ What: pci_module_init(driver) When: January 2007 Why: Is replaced by pci_register_driver(pci_driver). Who: Richard Knutsson and Greg Kroah-Hartman + +--------------------------- + +What: I2C interface of the it87 driver +When: January 2007 +Why: The ISA interface is faster and should be always available. The I2C + probing is also known to cause trouble in at least one case (see + bug #5889.) +Who: Jean Delvare diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index 7f42e441c64..9555be1ed99 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -9,7 +9,7 @@ Supported chips: http://www.ite.com.tw/ * IT8712F Prefix: 'it8712' - Addresses scanned: I2C 0x28 - 0x2f + Addresses scanned: I2C 0x2d from Super I/O config space (8 I/O ports) Datasheet: Publicly available at the ITE website http://www.ite.com.tw/ diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 0da7c9c508c..e87d52c5994 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -45,8 +45,7 @@ /* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, - 0x2e, 0x2f, I2C_CLIENT_END }; +static unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END }; static unsigned short isa_address; /* Insmod parameters */ @@ -830,6 +829,11 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind) if ((err = i2c_attach_client(new_client))) goto ERROR2; + if (!is_isa) + dev_info(&new_client->dev, "The I2C interface to IT87xxF " + "hardware monitoring chips is deprecated. Please " + "report if you still rely on it.\n"); + /* Check PWM configuration */ enable_pwm_interface = it87_check_pwm(new_client); -- cgit v1.2.3