From e9cea646014e25aa96eefa1b41d373a6ad95c189 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 12 Dec 2006 18:18:27 +0100 Subject: hwmon/f71805f: Document the fan control features Document the different fan control methods, list their options, and give some hints for best results. Signed-off-by: Jean Delvare --- Documentation/hwmon/f71805f | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/f71805f b/Documentation/hwmon/f71805f index 2ca69df669c3..bc571f99d0ee 100644 --- a/Documentation/hwmon/f71805f +++ b/Documentation/hwmon/f71805f @@ -13,8 +13,8 @@ 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 Kris Chen and Aaron Huang from Fintek for answering technical +questions and providing additional documentation. Thanks to Chris Lin from Jetway for providing wiring schematics and answering technical questions. @@ -28,8 +28,7 @@ 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. +three different modes (one manual, two automatic). The driver assumes that no more than one chip is present, which seems reasonable. @@ -103,3 +102,38 @@ 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. + + +Fan Control +----------- + +Both PWM (pulse-width modulation) and DC fan speed control methods are +supported. The right one to use depends on external circuitry on the +motherboard, so the driver assumes that the BIOS set the method +properly. The driver will report the method, but won't let you change +it. + +When the PWM method is used, you can select the operating frequency, +from 187.5 kHz (default) to 31 Hz. The best frequency depends on the +fan model. As a rule of thumb, lower frequencies seem to give better +control, but may generate annoying high-pitch noise. Fintek recommends +not going below 1 kHz, as the fan tachometers get confused by lower +frequencies as well. + +When the DC method is used, Fintek recommends not going below 5 V, which +corresponds to a pwm value of 106 for the driver. The driver doesn't +enforce this limit though. + +Three different fan control modes are supported: + +* Manual mode + You ask for a specific PWM duty cycle or DC voltage. + +* Fan speed mode + You ask for a specific fan speed. This mode assumes that pwm1 + corresponds to fan1, pwm2 to fan2 and pwm3 to fan3. + +* Temperature mode + You define 3 temperature/fan speed trip points, and the fan speed is + adjusted depending on the measured temperature, using interpolation. + This mode is not yet supported by the driver. -- cgit v1.2.3 From 8e9afcbbdef71aeeb510732f4f8d5ac3de863df0 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 12 Dec 2006 18:18:28 +0100 Subject: hwmon/it87: Remove the SMBus interface support This interface was useless as the LPC ISA-like interface is always available, is faster, and is more reliable. This cuts the driver size by some 20%. This change is also required to later convert the it87 driver to a platform driver, so that we can get rid of i2c-isa in a near future. Signed-off-by: Jean Delvare --- Documentation/feature-removal-schedule.txt | 9 -- Documentation/hwmon/it87 | 15 ++- drivers/hwmon/it87.c | 202 ++++++++--------------------- include/linux/i2c-id.h | 1 - 4 files changed, 64 insertions(+), 163 deletions(-) (limited to 'Documentation') diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 46f2a559b27c..e82c15de7d27 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -151,15 +151,6 @@ Who: Thomas Gleixner --------------------------- -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 - ---------------------------- - What: Unused EXPORT_SYMBOL/EXPORT_SYMBOL_GPL exports (temporary transition config option provided until then) The transition config option will also be removed at the same time. diff --git a/Documentation/hwmon/it87 b/Documentation/hwmon/it87 index e783fd62e308..74a80992d237 100644 --- a/Documentation/hwmon/it87 +++ b/Documentation/hwmon/it87 @@ -9,8 +9,7 @@ Supported chips: http://www.ite.com.tw/ * IT8712F Prefix: 'it8712' - Addresses scanned: I2C 0x2d - from Super I/O config space (8 I/O ports) + Addresses scanned: from Super I/O config space (8 I/O ports) Datasheet: Publicly available at the ITE website http://www.ite.com.tw/ * IT8716F @@ -53,6 +52,18 @@ Module Parameters misconfigured by BIOS - PWM values would be inverted. This option tries to fix this. Please contact your BIOS manufacturer and ask him for fix. + +Hardware Interfaces +------------------- + +All the chips suported by this driver are LPC Super-I/O chips, accessed +through the LPC bus (ISA-like I/O ports). The IT8712F additionally has an +SMBus interface to the hardware monitoring functions. This driver no +longer supports this interface though, as it is slower and less reliable +than the ISA access, and was only available on a small number of +motherboard models. + + Description ----------- diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 323ef06719c1..1ed8b7e2c35d 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c @@ -3,7 +3,7 @@ monitoring. Supports: IT8705F Super I/O chip w/LPC interface - IT8712F Super I/O chip w/LPC interface & SMBus + IT8712F Super I/O chip w/LPC interface IT8716F Super I/O chip w/LPC interface IT8718F Super I/O chip w/LPC interface Sis950 A clone of the IT8705F @@ -41,12 +41,8 @@ #include -/* Addresses to scan */ -static unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END }; static unsigned short isa_address; - -/* Insmod parameters */ -I2C_CLIENT_INSMOD_4(it87, it8712, it8716, it8718); +enum chips { it87, it8712, it8716, it8718 }; #define REG 0x2e /* The register to read/write */ #define DEV 0x07 /* Register: Logical device select */ @@ -162,8 +158,6 @@ static u8 vid_value; #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 @@ -242,33 +236,22 @@ struct it87_data { }; -static int it87_attach_adapter(struct i2c_adapter *adapter); -static int it87_isa_attach_adapter(struct i2c_adapter *adapter); -static int it87_detect(struct i2c_adapter *adapter, int address, int kind); +static int it87_detect(struct i2c_adapter *adapter); static int it87_detach_client(struct i2c_client *client); static int it87_read_value(struct i2c_client *client, u8 reg); -static int it87_write_value(struct i2c_client *client, u8 reg, u8 value); +static void it87_write_value(struct i2c_client *client, u8 reg, 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 = { - .driver = { - .name = "it87", - }, - .id = I2C_DRIVERID_IT87, - .attach_adapter = it87_attach_adapter, - .detach_client = it87_detach_client, -}; - static struct i2c_driver it87_isa_driver = { .driver = { .owner = THIS_MODULE, .name = "it87-isa", }, - .attach_adapter = it87_isa_attach_adapter, + .attach_adapter = it87_detect, .detach_client = it87_detach_client, }; @@ -850,22 +833,6 @@ static const struct attribute_group it87_group_opt = { .attrs = it87_attributes_opt, }; -/* 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_probe(adapter, &addr_data, it87_detect); -} - -static int it87_isa_attach_adapter(struct i2c_adapter *adapter) -{ - return it87_detect(adapter, isa_address, -1); -} - /* SuperIO detection - will change isa_address if a chip is found */ static int __init it87_find(unsigned short *address) { @@ -916,29 +883,20 @@ exit: } /* This function is called by i2c_probe */ -static int it87_detect(struct i2c_adapter *adapter, int address, int kind) +static int it87_detect(struct i2c_adapter *adapter) { - 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); + const char *name; 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_isa_driver.driver.name)) - goto ERROR0; - - /* For now, we presume we have a valid client. We create the - client structure, even though we cannot fill it completely yet. - But it allows us to access it87_{read,write}_value. */ + if (!request_region(isa_address, IT87_EXTENT, + it87_isa_driver.driver.name)){ + err = -EBUSY; + goto ERROR0; + } if (!(data = kzalloc(sizeof(struct it87_data), GFP_KERNEL))) { err = -ENOMEM; @@ -946,80 +904,46 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind) } new_client = &data->client; - if (is_isa) - mutex_init(&data->lock); + mutex_init(&data->lock); i2c_set_clientdata(new_client, data); - new_client->addr = address; + new_client->addr = isa_address; new_client->adapter = adapter; - new_client->driver = is_isa ? &it87_isa_driver : &it87_driver; - new_client->flags = 0; + new_client->driver = &it87_isa_driver; /* 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; - } + if ((it87_read_value(new_client, IT87_REG_CONFIG) & 0x80) + || it87_read_value(new_client, IT87_REG_CHIPID) != 0x90) { + 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) { - switch (chip_type) { - case IT8712F_DEVID: - kind = it8712; - break; - case IT8716F_DEVID: - kind = it8716; - break; - case IT8718F_DEVID: - kind = it8718; - break; - } - } - } - 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) { + switch (chip_type) { + case IT8712F_DEVID: + data->type = it8712; name = "it8712"; - } else if (kind == it8716) { + break; + case IT8716F_DEVID: + data->type = it8716; name = "it8716"; - } else if (kind == it8718) { + break; + case IT8718F_DEVID: + data->type = it8718; name = "it8718"; + break; + default: + data->type = it87; + name = "it87"; } /* 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; mutex_init(&data->update_lock); /* Tell the I2C layer a new client has arrived */ 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); @@ -1129,8 +1053,7 @@ ERROR3: ERROR2: kfree(data); ERROR1: - if (is_isa) - release_region(address, IT87_EXTENT); + release_region(isa_address, IT87_EXTENT); ERROR0: return err; } @@ -1147,50 +1070,39 @@ static int it87_detach_client(struct i2c_client *client) if ((err = i2c_detach_client(client))) return err; - if(i2c_is_isa_client(client)) - release_region(client->addr, IT87_EXTENT); + release_region(client->addr, IT87_EXTENT); kfree(data); 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. +/* ISA access must be locked explicitly! 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)) { - mutex_lock(&data->lock); - outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); - res = inb_p(client->addr + IT87_DATA_REG_OFFSET); - mutex_unlock(&data->lock); - return res; - } else - return i2c_smbus_read_byte_data(client, reg); + + mutex_lock(&data->lock); + outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); + res = inb_p(client->addr + IT87_DATA_REG_OFFSET); + mutex_unlock(&data->lock); + + return res; } -/* 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. +/* ISA access must be locked explicitly! 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) +static void 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)) { - mutex_lock(&data->lock); - outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); - outb_p(value, client->addr + IT87_DATA_REG_OFFSET); - mutex_unlock(&data->lock); - return 0; - } else - return i2c_smbus_write_byte_data(client, reg, value); + mutex_lock(&data->lock); + outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); + outb_p(value, client->addr + IT87_DATA_REG_OFFSET); + mutex_unlock(&data->lock); } /* Return 1 if and only if the PWM interface is safe to use */ @@ -1426,26 +1338,14 @@ static int __init sm_it87_init(void) { int res; - res = i2c_add_driver(&it87_driver); - if (res) + if ((res = it87_find(&isa_address))) return res; - - if (!it87_find(&isa_address)) { - res = i2c_isa_add_driver(&it87_isa_driver); - if (res) { - i2c_del_driver(&it87_driver); - return res; - } - } - - return 0; + return i2c_isa_add_driver(&it87_isa_driver); } static void __exit sm_it87_exit(void) { - if (isa_address) - i2c_isa_del_driver(&it87_isa_driver); - i2c_del_driver(&it87_driver); + i2c_isa_del_driver(&it87_isa_driver); } diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index 0a8f750cbede..9dc984b5f897 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -144,7 +144,6 @@ #define I2C_DRIVERID_MTP008 1023 #define I2C_DRIVERID_DS1621 1024 #define I2C_DRIVERID_ADM1024 1025 -#define I2C_DRIVERID_IT87 1026 #define I2C_DRIVERID_CH700X 1027 /* single driver for CH7003-7009 digital pc to tv encoders */ #define I2C_DRIVERID_FSCPOS 1028 #define I2C_DRIVERID_FSCSCY 1029 -- cgit v1.2.3 From ba224e2c4f0a706714ccb483b0c21d32f5994f67 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 12 Dec 2006 18:18:29 +0100 Subject: hwmon: New PC87427 hardware monitoring driver This is a new hardware monitoring driver for the National Semiconductor PC87427 Super-I/O chip. It only supports fan speed monitoring for now, while the chip can do much more. Thanks to Amir Habibi at Candelis for setting up a test system, and to Michael Kress for testing several iterations of this driver. Signed-off-by: Jean Delvare --- Documentation/hwmon/pc87427 | 38 +++ drivers/hwmon/Kconfig | 13 + drivers/hwmon/Makefile | 1 + drivers/hwmon/pc87427.c | 627 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 679 insertions(+) create mode 100644 Documentation/hwmon/pc87427 create mode 100644 drivers/hwmon/pc87427.c (limited to 'Documentation') diff --git a/Documentation/hwmon/pc87427 b/Documentation/hwmon/pc87427 new file mode 100644 index 000000000000..9a0708f9f49e --- /dev/null +++ b/Documentation/hwmon/pc87427 @@ -0,0 +1,38 @@ +Kernel driver pc87427 +===================== + +Supported chips: + * National Semiconductor PC87427 + Prefix: 'pc87427' + Addresses scanned: none, address read from Super I/O config space + Datasheet: http://www.winbond.com.tw/E-WINBONDHTM/partner/apc_007.html + +Author: Jean Delvare + +Thanks to Amir Habibi at Candelis for setting up a test system, and to +Michael Kress for testing several iterations of this driver. + + +Description +----------- + +The National Semiconductor Super I/O chip includes complete hardware +monitoring capabilities. It can monitor up to 18 voltages, 8 fans and +6 temperature sensors. Only the fans are supported at the moment. + +This chip also has fan controlling features, which are not yet supported +by this driver either. + +The driver assumes that no more than one chip is present, which seems +reasonable. + + +Fan Monitoring +-------------- + +Fan rotation speeds are reported as 14-bit values from a gated clock +signal. Speeds down to 83 RPM can be measured. + +An alarm is triggered if the rotation speed drops below a programmable +limit. Another alarm is triggered if the speed is too low to to be measured +(including stalled or missing fan). diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 39ab2fc128cf..e379ac41c09c 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -353,6 +353,19 @@ config SENSORS_PC87360 This driver can also be built as a module. If so, the module will be called pc87360. +config SENSORS_PC87427 + tristate "National Semiconductor PC87427" + depends on HWMON && EXPERIMENTAL + help + If you say yes here you get access to the hardware monitoring + functions of the National Semiconductor PC87427 Super-I/O chip. + The chip has two distinct logical devices, one for fan speed + monitoring and control, and one for voltage and temperature + monitoring. Only fan speed monitoring is supported right now. + + This driver can also be built as a module. If so, the module + will be called pc87427. + config SENSORS_SIS5595 tristate "Silicon Integrated Systems Corp. SiS5595" depends on HWMON && I2C && PCI && EXPERIMENTAL diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index af01cc64f7d2..95f3fc561186 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -41,6 +41,7 @@ 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_PC87427) += pc87427.o obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o diff --git a/drivers/hwmon/pc87427.c b/drivers/hwmon/pc87427.c new file mode 100644 index 000000000000..affa21a5ccfd --- /dev/null +++ b/drivers/hwmon/pc87427.c @@ -0,0 +1,627 @@ +/* + * pc87427.c - hardware monitoring driver for the + * National Semiconductor PC87427 Super-I/O chip + * Copyright (C) 2006 Jean Delvare + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + * + * Supports the following chips: + * + * Chip #vin #fan #pwm #temp devid + * PC87427 - 8 - - 0xF2 + * + * This driver assumes that no more than one chip is present. + * Only fan inputs are supported so far, although the chip can do much more. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct platform_device *pdev; + +#define DRVNAME "pc87427" + +/* The lock mutex protects both the I/O accesses (needed because the + device is using banked registers) and the register cache (needed to keep + the data in the registers and the cache in sync at any time). */ +struct pc87427_data { + struct class_device *class_dev; + struct mutex lock; + int address[2]; + const char *name; + + unsigned long last_updated; /* in jiffies */ + u8 fan_enabled; /* bit vector */ + u16 fan[8]; /* register values */ + u16 fan_min[8]; /* register values */ + u8 fan_status[8]; /* register values */ +}; + +/* + * Super-I/O registers and operations + */ + +#define SIOREG_LDSEL 0x07 /* Logical device select */ +#define SIOREG_DEVID 0x20 /* Device ID */ +#define SIOREG_ACT 0x30 /* Device activation */ +#define SIOREG_MAP 0x50 /* I/O or memory mapping */ +#define SIOREG_IOBASE 0x60 /* I/O base address */ + +static const u8 logdev[2] = { 0x09, 0x14 }; +static const char *logdev_str[2] = { DRVNAME " FMC", DRVNAME " HMC" }; +#define LD_FAN 0 +#define LD_IN 1 +#define LD_TEMP 1 + +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 REGION_LENGTH 32 +#define PC87427_REG_BANK 0x0f +#define BANK_FM(nr) (nr) +#define BANK_FT(nr) (0x08 + (nr)) +#define BANK_FC(nr) (0x10 + (nr) * 2) + +/* + * I/O access functions + */ + +/* ldi is the logical device index */ +static inline int pc87427_read8(struct pc87427_data *data, u8 ldi, u8 reg) +{ + return inb(data->address[ldi] + reg); +} + +/* Must be called with data->lock held, except during init */ +static inline int pc87427_read8_bank(struct pc87427_data *data, u8 ldi, + u8 bank, u8 reg) +{ + outb(bank, data->address[ldi] + PC87427_REG_BANK); + return inb(data->address[ldi] + reg); +} + +/* Must be called with data->lock held, except during init */ +static inline void pc87427_write8_bank(struct pc87427_data *data, u8 ldi, + u8 bank, u8 reg, u8 value) +{ + outb(bank, data->address[ldi] + PC87427_REG_BANK); + outb(value, data->address[ldi] + reg); +} + +/* + * Fan registers and conversions + */ + +/* fan data registers are 16-bit wide */ +#define PC87427_REG_FAN 0x12 +#define PC87427_REG_FAN_MIN 0x14 +#define PC87427_REG_FAN_STATUS 0x10 + +#define FAN_STATUS_STALL (1 << 3) +#define FAN_STATUS_LOSPD (1 << 1) +#define FAN_STATUS_MONEN (1 << 0) + +/* Dedicated function to read all registers related to a given fan input. + This saves us quite a few locks and bank selections. + Must be called with data->lock held. + nr is from 0 to 7 */ +static void pc87427_readall_fan(struct pc87427_data *data, u8 nr) +{ + int iobase = data->address[LD_FAN]; + + outb(BANK_FM(nr), iobase + PC87427_REG_BANK); + data->fan[nr] = inw(iobase + PC87427_REG_FAN); + data->fan_min[nr] = inw(iobase + PC87427_REG_FAN_MIN); + data->fan_status[nr] = inb(iobase + PC87427_REG_FAN_STATUS); + /* Clear fan alarm bits */ + outb(data->fan_status[nr], iobase + PC87427_REG_FAN_STATUS); +} + +/* The 2 LSB of fan speed registers are used for something different. + The actual 2 LSB of the measurements are not available. */ +static inline unsigned long fan_from_reg(u16 reg) +{ + reg &= 0xfffc; + if (reg == 0x0000 || reg == 0xfffc) + return 0; + return 5400000UL / reg; +} + +/* The 2 LSB of the fan speed limit registers are not significant. */ +static inline u16 fan_to_reg(unsigned long val) +{ + if (val < 83UL) + return 0xffff; + if (val >= 1350000UL) + return 0x0004; + return ((1350000UL + val / 2) / val) << 2; +} + +/* + * Data interface + */ + +static struct pc87427_data *pc87427_update_device(struct device *dev) +{ + struct pc87427_data *data = dev_get_drvdata(dev); + int i; + + mutex_lock(&data->lock); + if (!time_after(jiffies, data->last_updated + HZ) + && data->last_updated) + goto done; + + /* Fans */ + for (i = 0; i < 8; i++) { + if (!(data->fan_enabled & (1 << i))) + continue; + pc87427_readall_fan(data, i); + } + data->last_updated = jiffies; + +done: + mutex_unlock(&data->lock); + return data; +} + +static ssize_t show_fan_input(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87427_data *data = pc87427_update_device(dev); + int nr = attr->index; + + return sprintf(buf, "%lu\n", fan_from_reg(data->fan[nr])); +} + +static ssize_t show_fan_min(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87427_data *data = pc87427_update_device(dev); + int nr = attr->index; + + return sprintf(buf, "%lu\n", fan_from_reg(data->fan_min[nr])); +} + +static ssize_t show_fan_alarm(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87427_data *data = pc87427_update_device(dev); + int nr = attr->index; + + return sprintf(buf, "%d\n", !!(data->fan_status[nr] + & FAN_STATUS_LOSPD)); +} + +static ssize_t show_fan_fault(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct pc87427_data *data = pc87427_update_device(dev); + int nr = attr->index; + + return sprintf(buf, "%d\n", !!(data->fan_status[nr] + & FAN_STATUS_STALL)); +} + +static ssize_t set_fan_min(struct device *dev, struct device_attribute + *devattr, const char *buf, size_t count) +{ + struct pc87427_data *data = dev_get_drvdata(dev); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; + unsigned long val = simple_strtoul(buf, NULL, 10); + int iobase = data->address[LD_FAN]; + + mutex_lock(&data->lock); + outb(BANK_FM(nr), iobase + PC87427_REG_BANK); + /* The low speed limit registers are read-only while monitoring + is enabled, so we have to disable monitoring, then change the + limit, and finally enable monitoring again. */ + outb(0, iobase + PC87427_REG_FAN_STATUS); + data->fan_min[nr] = fan_to_reg(val); + outw(data->fan_min[nr], iobase + PC87427_REG_FAN_MIN); + outb(FAN_STATUS_MONEN, iobase + PC87427_REG_FAN_STATUS); + mutex_unlock(&data->lock); + + return count; +} + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan_input, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan_input, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan_input, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan_input, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan_input, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan_input, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan_input, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan_input, NULL, 7); + +static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, + show_fan_min, set_fan_min, 0); +static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, + show_fan_min, set_fan_min, 1); +static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, + show_fan_min, set_fan_min, 2); +static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO, + show_fan_min, set_fan_min, 3); +static SENSOR_DEVICE_ATTR(fan5_min, S_IWUSR | S_IRUGO, + show_fan_min, set_fan_min, 4); +static SENSOR_DEVICE_ATTR(fan6_min, S_IWUSR | S_IRUGO, + show_fan_min, set_fan_min, 5); +static SENSOR_DEVICE_ATTR(fan7_min, S_IWUSR | S_IRUGO, + show_fan_min, set_fan_min, 6); +static SENSOR_DEVICE_ATTR(fan8_min, S_IWUSR | S_IRUGO, + show_fan_min, set_fan_min, 7); + +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_fan_alarm, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_fan_alarm, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_fan_alarm, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_fan_alarm, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_fan_alarm, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_fan_alarm, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_alarm, S_IRUGO, show_fan_alarm, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_alarm, S_IRUGO, show_fan_alarm, NULL, 7); + +static SENSOR_DEVICE_ATTR(fan1_fault, S_IRUGO, show_fan_fault, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_fault, S_IRUGO, show_fan_fault, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_fault, S_IRUGO, show_fan_fault, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_fault, S_IRUGO, show_fan_fault, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_fault, S_IRUGO, show_fan_fault, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_fault, S_IRUGO, show_fan_fault, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_fault, S_IRUGO, show_fan_fault, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_fault, S_IRUGO, show_fan_fault, NULL, 7); + +static struct attribute *pc87427_attributes_fan[8][5] = { + { + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + &sensor_dev_attr_fan1_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + &sensor_dev_attr_fan2_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, + &sensor_dev_attr_fan3_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + &sensor_dev_attr_fan4_alarm.dev_attr.attr, + &sensor_dev_attr_fan4_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan5_min.dev_attr.attr, + &sensor_dev_attr_fan5_alarm.dev_attr.attr, + &sensor_dev_attr_fan5_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan6_min.dev_attr.attr, + &sensor_dev_attr_fan6_alarm.dev_attr.attr, + &sensor_dev_attr_fan6_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan7_min.dev_attr.attr, + &sensor_dev_attr_fan7_alarm.dev_attr.attr, + &sensor_dev_attr_fan7_fault.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_fan8_input.dev_attr.attr, + &sensor_dev_attr_fan8_min.dev_attr.attr, + &sensor_dev_attr_fan8_alarm.dev_attr.attr, + &sensor_dev_attr_fan8_fault.dev_attr.attr, + NULL + } +}; + +static const struct attribute_group pc87427_group_fan[8] = { + { .attrs = pc87427_attributes_fan[0] }, + { .attrs = pc87427_attributes_fan[1] }, + { .attrs = pc87427_attributes_fan[2] }, + { .attrs = pc87427_attributes_fan[3] }, + { .attrs = pc87427_attributes_fan[4] }, + { .attrs = pc87427_attributes_fan[5] }, + { .attrs = pc87427_attributes_fan[6] }, + { .attrs = pc87427_attributes_fan[7] }, +}; + +static ssize_t show_name(struct device *dev, struct device_attribute + *devattr, char *buf) +{ + struct pc87427_data *data = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", data->name); +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + + +/* + * Device detection, attach and detach + */ + +static void __devinit pc87427_init_device(struct device *dev) +{ + struct pc87427_data *data = dev_get_drvdata(dev); + int i; + u8 reg; + + /* The FMC module should be ready */ + reg = pc87427_read8(data, LD_FAN, PC87427_REG_BANK); + if (!(reg & 0x80)) + dev_warn(dev, "FMC module not ready!\n"); + + /* Check which fans are enabled */ + for (i = 0; i < 8; i++) { + reg = pc87427_read8_bank(data, LD_FAN, BANK_FM(i), + PC87427_REG_FAN_STATUS); + if (reg & FAN_STATUS_MONEN) + data->fan_enabled |= (1 << i); + } + + if (!data->fan_enabled) { + dev_dbg(dev, "Enabling all fan inputs\n"); + for (i = 0; i < 8; i++) + pc87427_write8_bank(data, LD_FAN, BANK_FM(i), + PC87427_REG_FAN_STATUS, + FAN_STATUS_MONEN); + data->fan_enabled = 0xff; + } +} + +static int __devinit pc87427_probe(struct platform_device *pdev) +{ + struct pc87427_data *data; + struct resource *res; + int i, err; + + if (!(data = kzalloc(sizeof(struct pc87427_data), GFP_KERNEL))) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Out of memory\n"); + goto exit; + } + + /* This will need to be revisited when we add support for + temperature and voltage monitoring. */ + res = platform_get_resource(pdev, IORESOURCE_IO, 0); + data->address[0] = res->start; + + mutex_init(&data->lock); + data->name = "pc87427"; + platform_set_drvdata(pdev, data); + pc87427_init_device(&pdev->dev); + + /* Register sysfs hooks */ + if ((err = device_create_file(&pdev->dev, &dev_attr_name))) + goto exit_kfree; + for (i = 0; i < 8; i++) { + if (!(data->fan_enabled & (1 << i))) + continue; + if ((err = sysfs_create_group(&pdev->dev.kobj, + &pc87427_group_fan[i]))) + goto exit_remove_files; + } + + 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_remove_files; + } + + return 0; + +exit_remove_files: + for (i = 0; i < 8; i++) { + if (!(data->fan_enabled & (1 << i))) + continue; + sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); + } +exit_kfree: + platform_set_drvdata(pdev, NULL); + kfree(data); +exit: + return err; +} + +static int __devexit pc87427_remove(struct platform_device *pdev) +{ + struct pc87427_data *data = platform_get_drvdata(pdev); + int i; + + platform_set_drvdata(pdev, NULL); + hwmon_device_unregister(data->class_dev); + device_remove_file(&pdev->dev, &dev_attr_name); + for (i = 0; i < 8; i++) { + if (!(data->fan_enabled & (1 << i))) + continue; + sysfs_remove_group(&pdev->dev.kobj, &pc87427_group_fan[i]); + } + kfree(data); + + return 0; +} + + +static struct platform_driver pc87427_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME, + }, + .probe = pc87427_probe, + .remove = __devexit_p(pc87427_remove), +}; + +static int __init pc87427_device_add(unsigned short address) +{ + struct resource res = { + .start = address, + .end = address + REGION_LENGTH - 1, + .name = logdev_str[0], + .flags = IORESOURCE_IO, + }; + int err; + + pdev = platform_device_alloc(DRVNAME, address); + if (!pdev) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Device allocation failed\n"); + goto exit; + } + + err = platform_device_add_resources(pdev, &res, 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 pc87427_find(int sioaddr, unsigned short *address) +{ + u16 val; + int i, err = 0; + + /* Identify device */ + val = superio_inb(sioaddr, SIOREG_DEVID); + if (val != 0xf2) { /* PC87427 */ + err = -ENODEV; + goto exit; + } + + for (i = 0; i < 2; i++) { + address[i] = 0; + /* Select logical device */ + superio_outb(sioaddr, SIOREG_LDSEL, logdev[i]); + + val = superio_inb(sioaddr, SIOREG_ACT); + if (!(val & 0x01)) { + printk(KERN_INFO DRVNAME ": Logical device 0x%02x " + "not activated\n", logdev[i]); + continue; + } + + val = superio_inb(sioaddr, SIOREG_MAP); + if (val & 0x01) { + printk(KERN_WARNING DRVNAME ": Logical device 0x%02x " + "is memory-mapped, can't use\n", logdev[i]); + continue; + } + + val = (superio_inb(sioaddr, SIOREG_IOBASE) << 8) + | superio_inb(sioaddr, SIOREG_IOBASE + 1); + if (!val) { + printk(KERN_INFO DRVNAME ": I/O base address not set " + "for logical device 0x%02x\n", logdev[i]); + continue; + } + address[i] = val; + } + +exit: + superio_exit(sioaddr); + return err; +} + +static int __init pc87427_init(void) +{ + int err; + unsigned short address[2]; + + if (pc87427_find(0x2e, address) + && pc87427_find(0x4e, address)) + return -ENODEV; + + /* For now the driver only handles fans so we only care about the + first address. */ + if (!address[0]) + return -ENODEV; + + err = platform_driver_register(&pc87427_driver); + if (err) + goto exit; + + /* Sets global pdev as a side effect */ + err = pc87427_device_add(address[0]); + if (err) + goto exit_driver; + + return 0; + +exit_driver: + platform_driver_unregister(&pc87427_driver); +exit: + return err; +} + +static void __exit pc87427_exit(void) +{ + platform_device_unregister(pdev); + platform_driver_unregister(&pc87427_driver); +} + +MODULE_AUTHOR("Jean Delvare "); +MODULE_DESCRIPTION("PC87427 hardware monitoring driver"); +MODULE_LICENSE("GPL"); + +module_init(pc87427_init); +module_exit(pc87427_exit); -- cgit v1.2.3 From 51c997d80e1f625aea3426a8a9087f5830ac6db3 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 12 Dec 2006 18:18:29 +0100 Subject: hwmon/f71805f: Add support for the Fintek F71872F/FG chip Add support for the Fintek F71872F/FG Super-I/O chip. It is basically the same as the Fintek F71805F/FG as far as hardware monitoring is concerned, with two additional internal voltages monitored (VSB and battery), and 6 VID inputs (not yet supported.) To make things a bit more confusing, two of the voltage input pins (in4 and in8) can be used for other functions. The driver reads the pin configuration from the Super-I/O configuration space to decide whether it must create interface files for these inputs or not. Many thanks to Nikolay Derkach for testing the early iterations of this code and reporting bugs. Signed-off-by: Jean Delvare --- Documentation/hwmon/f71805f | 14 ++- drivers/hwmon/Kconfig | 5 +- drivers/hwmon/f71805f.c | 230 +++++++++++++++++++++++++++++++++++--------- 3 files changed, 203 insertions(+), 46 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/f71805f b/Documentation/hwmon/f71805f index bc571f99d0ee..bfd0f154959c 100644 --- a/Documentation/hwmon/f71805f +++ b/Documentation/hwmon/f71805f @@ -6,6 +6,10 @@ Supported chips: Prefix: 'f71805f' Addresses scanned: none, address read from Super I/O config space Datasheet: Provided by Fintek on request + * Fintek F71872F/FG + Prefix: 'f71872f' + Addresses scanned: none, address read from Super I/O config space + Datasheet: Provided by Fintek on request Author: Jean Delvare @@ -30,6 +34,10 @@ 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 Fintek F71872F/FG Super I/O chip is almost the same, with two +additional internal voltages monitored (VSB and battery). It also features +6 VID inputs. The VID inputs are not yet supported by this driver. + The driver assumes that no more than one chip is present, which seems reasonable. @@ -41,7 +49,8 @@ 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. +factor 2. For the F71872F/FG, in9 (VSB) and in10 (battery) are also +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). @@ -60,9 +69,12 @@ 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 +in10 VSB VSB3.3V int. int. 2.00 1.65 V (3) +in9 VBAT VBATTERY int. int. 2.00 1.50 V (3) (1) Depends on your hardware setup. (2) Obviously not correct, swapping R1 and R2 would make more sense. +(3) F71872F/FG only. 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 diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e379ac41c09c..5a0763197e21 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -142,11 +142,12 @@ config SENSORS_DS1621 will be called ds1621. config SENSORS_F71805F - tristate "Fintek F71805F/FG" + tristate "Fintek F71805F/FG and F71872F/FG" depends on HWMON && EXPERIMENTAL help If you say yes here you get support for hardware monitoring - features of the Fintek F71805F/FG chips. + features of the Fintek F71805F/FG and F71872F/FG Super-I/O + chips. This driver can also be built as a module. If so, the module will be called f71805f. diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index 975c1cc2acf1..ba5e7b70f8cc 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c @@ -1,12 +1,15 @@ /* - * f71805f.c - driver for the Fintek F71805F/FG Super-I/O chip integrated - * hardware monitoring features + * f71805f.c - driver for the Fintek F71805F/FG and F71872F/FG Super-I/O + * chips integrated hardware monitoring features * Copyright (C) 2005-2006 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. * + * The F71872F/FG is almost the same, with two more voltages monitored, + * and 6 VID inputs. + * * 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 @@ -37,6 +40,7 @@ static struct platform_device *pdev; #define DRVNAME "f71805f" +enum kinds { f71805f, f71872f }; /* * Super-I/O constants and functions @@ -48,11 +52,13 @@ static struct platform_device *pdev; #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_FNSEL1 0x29 /* Multi Function Select 1 (F71872F) */ #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 +#define SIO_F71872F_ID 0x0341 static inline int superio_inb(int base, int reg) @@ -104,10 +110,10 @@ superio_exit(int base) * Registers */ -/* in nr from 0 to 8 (8-bit values) */ +/* in nr from 0 to 10 (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)) +#define F71805F_REG_IN_HIGH(nr) ((nr) < 10 ? 0x40 + 2 * (nr) : 0x2E) +#define F71805F_REG_IN_LOW(nr) ((nr) < 10 ? 0x41 + 2 * (nr) : 0x2F) /* 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)) @@ -150,9 +156,10 @@ struct f71805f_data { unsigned long last_limits; /* In jiffies */ /* Register values */ - u8 in[9]; - u8 in_high[9]; - u8 in_low[9]; + u8 in[11]; + u8 in_high[11]; + u8 in_low[11]; + u16 has_in; u16 fan[3]; u16 fan_low[3]; u16 fan_target[3]; @@ -166,6 +173,11 @@ struct f71805f_data { unsigned long alarms; }; +struct f71805f_sio_data { + enum kinds kind; + u8 fnsel1; +}; + static inline long in_from_reg(u8 reg) { return (reg * 8); @@ -316,7 +328,9 @@ static struct f71805f_data *f71805f_update_device(struct device *dev) /* 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++) { + for (nr = 0; nr < 11; nr++) { + if (!(data->has_in & (1 << nr))) + continue; data->in_high[nr] = f71805f_read8(data, F71805F_REG_IN_HIGH(nr)); data->in_low[nr] = f71805f_read8(data, @@ -346,7 +360,9 @@ static struct f71805f_data *f71805f_update_device(struct device *dev) /* Measurement registers cache is refreshed after 1 second */ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) { - for (nr = 0; nr < 9; nr++) { + for (nr = 0; nr < 11; nr++) { + if (!(data->has_in & (1 << nr))) + continue; data->in[nr] = f71805f_read8(data, F71805F_REG_IN(nr)); } @@ -385,35 +401,43 @@ static ssize_t show_in0(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", in0_from_reg(data->in[0])); + return sprintf(buf, "%ld\n", in0_from_reg(data->in[nr])); } static ssize_t show_in0_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", in0_from_reg(data->in_high[0])); + return sprintf(buf, "%ld\n", in0_from_reg(data->in_high[nr])); } static ssize_t show_in0_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", in0_from_reg(data->in_low[0])); + return sprintf(buf, "%ld\n", in0_from_reg(data->in_low[nr])); } 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); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); - data->in_high[0] = in0_to_reg(val); - f71805f_write8(data, F71805F_REG_IN_HIGH(0), data->in_high[0]); + data->in_high[nr] = in0_to_reg(val); + f71805f_write8(data, F71805F_REG_IN_HIGH(nr), data->in_high[nr]); mutex_unlock(&data->update_lock); return count; @@ -423,11 +447,13 @@ 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); + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + int nr = attr->index; long val = simple_strtol(buf, NULL, 10); mutex_lock(&data->update_lock); - data->in_low[0] = in0_to_reg(val); - f71805f_write8(data, F71805F_REG_IN_LOW(0), data->in_low[0]); + data->in_low[nr] = in0_to_reg(val); + f71805f_write8(data, F71805F_REG_IN_LOW(nr), data->in_low[nr]); mutex_unlock(&data->update_lock); return count; @@ -770,7 +796,7 @@ static ssize_t show_alarms_in(struct device *dev, struct device_attribute { struct f71805f_data *data = f71805f_update_device(dev); - return sprintf(buf, "%lu\n", data->alarms & 0x1ff); + return sprintf(buf, "%lu\n", data->alarms & 0x7ff); } static ssize_t show_alarms_fan(struct device *dev, struct device_attribute @@ -807,9 +833,11 @@ static ssize_t show_name(struct device *dev, struct device_attribute return sprintf(buf, "%s\n", data->name); } -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 SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in0, NULL, 0); +static SENSOR_DEVICE_ATTR(in0_max, S_IRUGO| S_IWUSR, + show_in0_max, set_in0_max, 0); +static SENSOR_DEVICE_ATTR(in0_min, S_IRUGO| S_IWUSR, + show_in0_min, set_in0_min, 0); static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1); static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR, show_in_max, set_in_max, 1); @@ -850,6 +878,16 @@ static SENSOR_DEVICE_ATTR(in8_max, S_IRUGO | S_IWUSR, show_in_max, set_in_max, 8); static SENSOR_DEVICE_ATTR(in8_min, S_IRUGO | S_IWUSR, show_in_min, set_in_min, 8); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_in0, NULL, 9); +static SENSOR_DEVICE_ATTR(in9_max, S_IRUGO | S_IWUSR, + show_in0_max, set_in0_max, 9); +static SENSOR_DEVICE_ATTR(in9_min, S_IRUGO | S_IWUSR, + show_in0_min, set_in0_min, 9); +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_in0, NULL, 10); +static SENSOR_DEVICE_ATTR(in10_max, S_IRUGO | S_IWUSR, + show_in0_max, set_in0_max, 10); +static SENSOR_DEVICE_ATTR(in10_min, S_IRUGO | S_IWUSR, + show_in0_min, set_in0_min, 10); static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR, @@ -916,6 +954,8 @@ static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5); static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6); static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7); static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8); +static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, 9); +static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, 10); static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 11); static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 12); static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13); @@ -929,9 +969,9 @@ static DEVICE_ATTR(alarms_temp, S_IRUGO, show_alarms_temp, NULL); static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static struct attribute *f71805f_attributes[] = { - &dev_attr_in0_input.attr, - &dev_attr_in0_max.attr, - &dev_attr_in0_min.attr, + &sensor_dev_attr_in0_input.dev_attr.attr, + &sensor_dev_attr_in0_max.dev_attr.attr, + &sensor_dev_attr_in0_min.dev_attr.attr, &sensor_dev_attr_in1_input.dev_attr.attr, &sensor_dev_attr_in1_max.dev_attr.attr, &sensor_dev_attr_in1_min.dev_attr.attr, @@ -941,9 +981,6 @@ static struct attribute *f71805f_attributes[] = { &sensor_dev_attr_in3_input.dev_attr.attr, &sensor_dev_attr_in3_max.dev_attr.attr, &sensor_dev_attr_in3_min.dev_attr.attr, - &sensor_dev_attr_in4_input.dev_attr.attr, - &sensor_dev_attr_in4_max.dev_attr.attr, - &sensor_dev_attr_in4_min.dev_attr.attr, &sensor_dev_attr_in5_input.dev_attr.attr, &sensor_dev_attr_in5_max.dev_attr.attr, &sensor_dev_attr_in5_min.dev_attr.attr, @@ -953,9 +990,6 @@ static struct attribute *f71805f_attributes[] = { &sensor_dev_attr_in7_input.dev_attr.attr, &sensor_dev_attr_in7_max.dev_attr.attr, &sensor_dev_attr_in7_min.dev_attr.attr, - &sensor_dev_attr_in8_input.dev_attr.attr, - &sensor_dev_attr_in8_max.dev_attr.attr, - &sensor_dev_attr_in8_min.dev_attr.attr, &sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, @@ -974,11 +1008,9 @@ static struct attribute *f71805f_attributes[] = { &sensor_dev_attr_in1_alarm.dev_attr.attr, &sensor_dev_attr_in2_alarm.dev_attr.attr, &sensor_dev_attr_in3_alarm.dev_attr.attr, - &sensor_dev_attr_in4_alarm.dev_attr.attr, &sensor_dev_attr_in5_alarm.dev_attr.attr, &sensor_dev_attr_in6_alarm.dev_attr.attr, &sensor_dev_attr_in7_alarm.dev_attr.attr, - &sensor_dev_attr_in8_alarm.dev_attr.attr, &dev_attr_alarms_in.attr, &sensor_dev_attr_temp1_alarm.dev_attr.attr, &sensor_dev_attr_temp2_alarm.dev_attr.attr, @@ -994,6 +1026,41 @@ static const struct attribute_group f71805f_group = { .attrs = f71805f_attributes, }; +static struct attribute *f71805f_attributes_optin[4][5] = { + { + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in8_max.dev_attr.attr, + &sensor_dev_attr_in8_min.dev_attr.attr, + &sensor_dev_attr_in8_alarm.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in9_max.dev_attr.attr, + &sensor_dev_attr_in9_min.dev_attr.attr, + &sensor_dev_attr_in9_alarm.dev_attr.attr, + NULL + }, { + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in10_max.dev_attr.attr, + &sensor_dev_attr_in10_min.dev_attr.attr, + &sensor_dev_attr_in10_alarm.dev_attr.attr, + NULL + } +}; + +static const struct attribute_group f71805f_group_optin[4] = { + { .attrs = f71805f_attributes_optin[0] }, + { .attrs = f71805f_attributes_optin[1] }, + { .attrs = f71805f_attributes_optin[2] }, + { .attrs = f71805f_attributes_optin[3] }, +}; + static struct attribute *f71805f_attributes_fan[3][8] = { { &sensor_dev_attr_fan1_input.dev_attr.attr, @@ -1084,10 +1151,16 @@ static void __devinit f71805f_init_device(struct f71805f_data *data) static int __devinit f71805f_probe(struct platform_device *pdev) { + struct f71805f_sio_data *sio_data = pdev->dev.platform_data; struct f71805f_data *data; struct resource *res; int i, err; + static const char *names[] = { + "f71805f", + "f71872f", + }; + if (!(data = kzalloc(sizeof(struct f71805f_data), GFP_KERNEL))) { err = -ENOMEM; printk(KERN_ERR DRVNAME ": Out of memory\n"); @@ -1097,17 +1170,51 @@ static int __devinit f71805f_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_IO, 0); data->addr = res->start; mutex_init(&data->lock); - data->name = "f71805f"; + data->name = names[sio_data->kind]; mutex_init(&data->update_lock); platform_set_drvdata(pdev, data); + /* Some voltage inputs depend on chip model and configuration */ + switch (sio_data->kind) { + case f71805f: + data->has_in = 0x1ff; + break; + case f71872f: + data->has_in = 0x6ef; + if (sio_data->fnsel1 & 0x01) + data->has_in |= (1 << 4); /* in4 */ + if (sio_data->fnsel1 & 0x02) + data->has_in |= (1 << 8); /* in8 */ + break; + } + /* Initialize the F71805F chip */ f71805f_init_device(data); /* Register sysfs interface files */ if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group))) goto exit_free; + if (data->has_in & (1 << 4)) { /* in4 */ + if ((err = sysfs_create_group(&pdev->dev.kobj, + &f71805f_group_optin[0]))) + goto exit_remove_files; + } + if (data->has_in & (1 << 8)) { /* in8 */ + if ((err = sysfs_create_group(&pdev->dev.kobj, + &f71805f_group_optin[1]))) + goto exit_remove_files; + } + if (data->has_in & (1 << 9)) { /* in9 (F71872F/FG only) */ + if ((err = sysfs_create_group(&pdev->dev.kobj, + &f71805f_group_optin[2]))) + goto exit_remove_files; + } + if (data->has_in & (1 << 10)) { /* in9 (F71872F/FG only) */ + if ((err = sysfs_create_group(&pdev->dev.kobj, + &f71805f_group_optin[3]))) + goto exit_remove_files; + } for (i = 0; i < 3; i++) { if (data->fan_ctrl[i] & FAN_CTRL_SKIP) continue; @@ -1143,6 +1250,8 @@ static int __devinit f71805f_probe(struct platform_device *pdev) exit_remove_files: sysfs_remove_group(&pdev->dev.kobj, &f71805f_group); + for (i = 0; i < 4; i++) + sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]); for (i = 0; i < 3; i++) sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq); @@ -1161,6 +1270,8 @@ static int __devexit f71805f_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); hwmon_device_unregister(data->class_dev); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group); + for (i = 0; i < 4; i++) + sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_optin[i]); for (i = 0; i < 3; i++) sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]); sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_pwm_freq); @@ -1178,7 +1289,8 @@ static struct platform_driver f71805f_driver = { .remove = __devexit_p(f71805f_remove), }; -static int __init f71805f_device_add(unsigned short address) +static int __init f71805f_device_add(unsigned short address, + const struct f71805f_sio_data *sio_data) { struct resource res = { .start = address, @@ -1202,26 +1314,45 @@ static int __init f71805f_device_add(unsigned short address) goto exit_device_put; } + pdev->dev.platform_data = kmalloc(sizeof(struct f71805f_sio_data), + GFP_KERNEL); + if (!pdev->dev.platform_data) { + err = -ENOMEM; + printk(KERN_ERR DRVNAME ": Platform data allocation failed\n"); + goto exit_device_put; + } + memcpy(pdev->dev.platform_data, sio_data, + sizeof(struct f71805f_sio_data)); + err = platform_device_add(pdev); if (err) { printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n", err); - goto exit_device_put; + goto exit_kfree_data; } return 0; +exit_kfree_data: + kfree(pdev->dev.platform_data); + pdev->dev.platform_data = NULL; exit_device_put: platform_device_put(pdev); exit: return err; } -static int __init f71805f_find(int sioaddr, unsigned short *address) +static int __init f71805f_find(int sioaddr, unsigned short *address, + struct f71805f_sio_data *sio_data) { int err = -ENODEV; u16 devid; + static const char *names[] = { + "F71805F/FG", + "F71872F/FG", + }; + superio_enter(sioaddr); devid = superio_inw(sioaddr, SIO_REG_MANID); @@ -1229,7 +1360,15 @@ static int __init f71805f_find(int sioaddr, unsigned short *address) goto exit; devid = superio_inw(sioaddr, SIO_REG_DEVID); - if (devid != SIO_F71805F_ID) { + switch (devid) { + case SIO_F71805F_ID: + sio_data->kind = f71805f; + break; + case SIO_F71872F_ID: + sio_data->kind = f71872f; + sio_data->fnsel1 = superio_inb(sioaddr, SIO_REG_FNSEL1); + break; + default: printk(KERN_INFO DRVNAME ": Unsupported Fintek device, " "skipping\n"); goto exit; @@ -1250,8 +1389,9 @@ static int __init f71805f_find(int sioaddr, unsigned short *address) } err = 0; - printk(KERN_INFO DRVNAME ": Found F71805F chip at %#x, revision %u\n", - *address, superio_inb(sioaddr, SIO_REG_DEVREV)); + printk(KERN_INFO DRVNAME ": Found %s chip at %#x, revision %u\n", + names[sio_data->kind], *address, + superio_inb(sioaddr, SIO_REG_DEVREV)); exit: superio_exit(sioaddr); @@ -1262,9 +1402,10 @@ static int __init f71805f_init(void) { int err; unsigned short address; + struct f71805f_sio_data sio_data; - if (f71805f_find(0x2e, &address) - && f71805f_find(0x4e, &address)) + if (f71805f_find(0x2e, &address, &sio_data) + && f71805f_find(0x4e, &address, &sio_data)) return -ENODEV; err = platform_driver_register(&f71805f_driver); @@ -1272,7 +1413,7 @@ static int __init f71805f_init(void) goto exit; /* Sets global pdev as a side effect */ - err = f71805f_device_add(address); + err = f71805f_device_add(address, &sio_data); if (err) goto exit_driver; @@ -1286,13 +1427,16 @@ exit: static void __exit f71805f_exit(void) { + kfree(pdev->dev.platform_data); + pdev->dev.platform_data = NULL; platform_device_unregister(pdev); + platform_driver_unregister(&f71805f_driver); } MODULE_AUTHOR("Jean Delvare "); MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("F71805F hardware monitoring driver"); +MODULE_DESCRIPTION("F71805F/F71872F hardware monitoring driver"); module_init(f71805f_init); module_exit(f71805f_exit); -- cgit v1.2.3 From 7188cc66b4facf749e4fc0a44165b06716e1b621 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 12 Dec 2006 18:18:30 +0100 Subject: hwmon: Update Rudolf Marek's e-mail address The Silicon Hill club is not what it used to be. Signed-off-by: Rudolf Marek Signed-off-by: Jean Delvare --- Documentation/hwmon/k8temp | 2 +- Documentation/hwmon/w83627ehf | 2 +- Documentation/hwmon/w83791d | 2 +- MAINTAINERS | 2 +- drivers/hwmon/hwmon-vid.c | 4 ++-- drivers/hwmon/k8temp.c | 4 ++-- drivers/hwmon/w83627ehf.c | 2 +- drivers/hwmon/w83792d.c | 2 +- drivers/i2c/busses/i2c-ali1563.c | 2 +- 9 files changed, 11 insertions(+), 11 deletions(-) (limited to 'Documentation') diff --git a/Documentation/hwmon/k8temp b/Documentation/hwmon/k8temp index 30d123b8d920..0005c7166146 100644 --- a/Documentation/hwmon/k8temp +++ b/Documentation/hwmon/k8temp @@ -8,7 +8,7 @@ Supported chips: Datasheet: http://www.amd.com/us-en/assets/content_type/white_papers_and_tech_docs/32559.pdf Author: Rudolf Marek -Contact: Rudolf Marek +Contact: Rudolf Marek Description ----------- diff --git a/Documentation/hwmon/w83627ehf b/Documentation/hwmon/w83627ehf index caa610a297e8..8a15a7408753 100644 --- a/Documentation/hwmon/w83627ehf +++ b/Documentation/hwmon/w83627ehf @@ -10,7 +10,7 @@ Supported chips: Authors: Jean Delvare Yuan Mu (Winbond) - Rudolf Marek + Rudolf Marek Description ----------- diff --git a/Documentation/hwmon/w83791d b/Documentation/hwmon/w83791d index 19b2ed739fa1..db9881df88a5 100644 --- a/Documentation/hwmon/w83791d +++ b/Documentation/hwmon/w83791d @@ -18,7 +18,7 @@ Credits: and Mark Studebaker w83792d.c: Chunhao Huang , - Rudolf Marek + Rudolf Marek Additional contributors: Sven Anders diff --git a/MAINTAINERS b/MAINTAINERS index dbf449ba240c..c2eea2918e53 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -277,7 +277,7 @@ S: Maintained ALI1563 I2C DRIVER P: Rudolf Marek -M: r.marek@sh.cvut.cz +M: r.marek@assembler.cz L: i2c@lm-sensors.org S: Maintained diff --git a/drivers/hwmon/hwmon-vid.c b/drivers/hwmon/hwmon-vid.c index 9d67320e6840..31c42002708f 100644 --- a/drivers/hwmon/hwmon-vid.c +++ b/drivers/hwmon/hwmon-vid.c @@ -1,7 +1,7 @@ /* hwmon-vid.c - VID/VRM/VRD voltage conversions - Copyright (c) 2004 Rudolf Marek + Copyright (c) 2004 Rudolf Marek Partly imported from i2c-vid.h of the lm_sensors project Copyright (c) 2002 Mark D. Studebaker @@ -232,7 +232,7 @@ u8 vid_which_vrm(void) EXPORT_SYMBOL(vid_from_reg); EXPORT_SYMBOL(vid_which_vrm); -MODULE_AUTHOR("Rudolf Marek "); +MODULE_AUTHOR("Rudolf Marek "); MODULE_DESCRIPTION("hwmon-vid driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c index f58b64ed09e3..5d8d0ca08fa9 100644 --- a/drivers/hwmon/k8temp.c +++ b/drivers/hwmon/k8temp.c @@ -1,7 +1,7 @@ /* * k8temp.c - Linux kernel module for hardware monitoring * - * Copyright (C) 2006 Rudolf Marek + * Copyright (C) 2006 Rudolf Marek * * Inspired from the w83785 and amd756 drivers. * @@ -286,7 +286,7 @@ static void __exit k8temp_exit(void) pci_unregister_driver(&k8temp_driver); } -MODULE_AUTHOR("Rudolf Marek "); +MODULE_AUTHOR("Rudolf Marek "); MODULE_DESCRIPTION("AMD K8 core temperature monitor"); MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c index 2257806d0102..212a1558c63b 100644 --- a/drivers/hwmon/w83627ehf.c +++ b/drivers/hwmon/w83627ehf.c @@ -3,7 +3,7 @@ the Winbond W83627EHF Super-I/O chip Copyright (C) 2005 Jean Delvare Copyright (C) 2006 Yuan Mu (Winbond), - Rudolf Marek + Rudolf Marek David Hubbard Shamelessly ripped from the w83627hf driver diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c index 4e108262576f..b0fa296740d1 100644 --- a/drivers/hwmon/w83792d.c +++ b/drivers/hwmon/w83792d.c @@ -3,7 +3,7 @@ monitoring Copyright (C) 2004, 2005 Winbond Electronics Corp. Chunhao Huang , - Rudolf Marek + Rudolf Marek 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 diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c index 33fbb47100a3..8e1e3f8e40a4 100644 --- a/drivers/i2c/busses/i2c-ali1563.c +++ b/drivers/i2c/busses/i2c-ali1563.c @@ -2,7 +2,7 @@ * i2c-ali1563.c - i2c driver for the ALi 1563 Southbridge * * Copyright (C) 2004 Patrick Mochel - * 2005 Rudolf Marek + * 2005 Rudolf Marek * * The 1563 southbridge is deceptively similar to the 1533, with a * few notable exceptions. One of those happens to be the fact they -- cgit v1.2.3 From 61db011d403388b2dd342489d7110cf13cb99025 Mon Sep 17 00:00:00 2001 From: Rudolf Marek Date: Tue, 12 Dec 2006 18:18:30 +0100 Subject: hwmon/w83793: Add documentation and maintainer Documentation for the new w83793 hardware monitoring driver, originally provided by Yuan My from Winbond. Also add myself as the maintainer of this driver. Signed-off-by: Rudolf Marek Signed-off-by: Jean Delvare --- Documentation/hwmon/sysfs-interface | 4 +- Documentation/hwmon/w83793 | 110 ++++++++++++++++++++++++++++++++++++ MAINTAINERS | 6 ++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 Documentation/hwmon/w83793 (limited to 'Documentation') diff --git a/Documentation/hwmon/sysfs-interface b/Documentation/hwmon/sysfs-interface index d1d390aaf620..efef3b962cd3 100644 --- a/Documentation/hwmon/sysfs-interface +++ b/Documentation/hwmon/sysfs-interface @@ -208,12 +208,14 @@ temp[1-*]_auto_point[1-*]_temp_hyst **************** temp[1-*]_type Sensor type selection. - Integers 1 to 4 or thermistor Beta value (typically 3435) + Integers 1 to 6 or thermistor Beta value (typically 3435) RW 1: PII/Celeron Diode 2: 3904 transistor 3: thermal diode 4: thermistor (default/unknown Beta) + 5: AMD AMDSI + 6: Intel PECI Not all types are supported by all chips temp[1-*]_max Temperature max value. diff --git a/Documentation/hwmon/w83793 b/Documentation/hwmon/w83793 new file mode 100644 index 000000000000..45e5408340e0 --- /dev/null +++ b/Documentation/hwmon/w83793 @@ -0,0 +1,110 @@ +Kernel driver w83793 +==================== + +Supported chips: + * Winbond W83793G/W83793R + Prefix: 'w83793' + Addresses scanned: I2C 0x2c - 0x2f + Datasheet: Still not published + +Authors: + Yuan Mu (Winbond Electronics) + Rudolf Marek + + +Module parameters +----------------- + +* reset int + (default 0) + This parameter is not recommended, it will lose motherboard specific + settings. Use 'reset=1' to reset the chip when loading this module. + +* force_subclients=bus,caddr,saddr1,saddr2 + This is used to force the i2c addresses for subclients of + a certain chip. Typical usage is `force_subclients=0,0x2f,0x4a,0x4b' + to force the subclients of chip 0x2f on bus 0 to i2c addresses + 0x4a and 0x4b. + + +Description +----------- + +This driver implements support for Winbond W83793G/W83793R chips. + +* Exported features + This driver exports 10 voltage sensors, up to 12 fan tachometer inputs, + 6 remote temperatures, up to 8 sets of PWM fan controls, SmartFan + (automatic fan speed control) on all temperature/PWM combinations, 2 + sets of 6-pin CPU VID input. + +* Sensor resolutions + If your motherboard maker used the reference design, the resolution of + voltage0-2 is 2mV, resolution of voltage3/4/5 is 16mV, 8mV for voltage6, + 24mV for voltage7/8. Temp1-4 have a 0.25 degree Celsius resolution, + temp5-6 have a 1 degree Celsiis resolution. + +* Temperature sensor types + Temp1-4 have 3 possible types. It can be read from (and written to) + temp[1-4]_type. + - If the value of 0, the related temperature channel stops + monitoring. + - If the value is 3, it starts monitoring using a remote termal diode + (default). + - If the value is 5, it starts monitoring using the temperature sensor + in AMD CPU and get result by AMDSI. + - If the value is 6, it starts monitoring using the temperature sensor + in Intel CPU and get result by PECI. + Temp5-6 can be connected to external thermistors (value of + temp[5-6]_type is 4). They can also be disabled (value is 0). + +* Alarm mechanism + For voltage sensors, an alarm triggers if the measured value is below + the low voltage limit or over the high voltage limit. + For temperature sensors, an alarm triggers if the measured value goes + above the high temperature limit, and wears off only after the measured + value drops below the hysteresis value. + For fan sensors, an alarm triggers if the measured value is below the + low speed limit. + +* SmartFan/PWM control + If you want to set a pwm fan to manual mode, you just need to make sure it + is not controlled by any temp channel, for example, you want to set fan1 + to manual mode, you need to check the value of temp[1-6]_fan_map, make + sure bit 0 is cleared in the 6 values. And then set the pwm1 value to + control the fan. + + Each temperature channel can control all the 8 PWM outputs (by setting the + corresponding bit in tempX_fan_map), you can set the temperature channel + mode using temp[1-6]_pwm_enable, 2 is Thermal Cruise mode and 3 + is the SmartFanII mode. Temperature channels will try to speed up or + slow down all controlled fans, this means one fan can receive different + PWM value requests from different temperature channels, but the chip + will always pick the safest (max) PWM value for each fan. + + In Thermal Cruise mode, the chip attempts to keep the temperature at a + predefined value, within a tolerance margin. So if tempX_input > + thermal_cruiseX + toleranceX, the chip will increase the PWM value, + if tempX_input < thermal_cruiseX - toleranceX, the chip will decrease + the PWM value. If the temperature is within the tolerance range, the PWM + value is left unchanged. + + SmartFanII works differently, you have to define up to 7 PWM, temperature + trip points, defining a PWM/temperature curve which the chip will follow. + While not fundamentally different from the Thermal Cruise mode, the + implementation is quite different, giving you a finer-grained control. + +* Chassis + If the case open alarm triggers, it will stay in this state unless cleared + by any write to the sysfs file "chassis". + +* VID and VRM + The VRM version is detected automatically, don't modify the it unless you + *do* know the cpu VRM version and it's not properly detected. + + +Notes +----- + + Only Fan1-5 and PWM1-3 are guaranteed to always exist, other fan inputs and + PWM outputs may or may not exist depending on the chip pin configuration. diff --git a/MAINTAINERS b/MAINTAINERS index c2eea2918e53..99654b5e9236 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3427,6 +3427,12 @@ M: bezaur@gmail.com L: lm-sensors@lm-sensors.org S: Maintained +W83793 HARDWARE MONITORING DRIVER +P: Rudolf Marek +M: r.marek@assembler.cz +L: lm-sensors@lm-sensors.org +S: Maintained + W83L51xD SD/MMC CARD INTERFACE DRIVER P: Pierre Ossman M: drzeus-wbsd@drzeus.cx -- cgit v1.2.3