diff -Nru a/MAINTAINERS b/MAINTAINERS --- a/MAINTAINERS Sun Feb 8 20:46:01 2004 +++ b/MAINTAINERS Sun Feb 8 20:46:01 2004 @@ -922,15 +922,13 @@ S: Maintained I2C AND SENSORS DRIVERS -P: Frodo Looijaard -M: frodol@dds.nl -P: Philip Edelbrock -M: phil@netroedge.com P: Greg Kroah-Hartman M: greg@kroah.com -L: sensors@stimpy.netroedge.com -W: http://www.lm-sensors.nu/ -S: Maintained +P: Philip Edelbrock +M: phil@netroedge.com +L: sensors@stimpy.netroedge.com +W: http://www.lm-sensors.nu/ +S: Maintained i386 BOOT CODE P: Riley H. Williams diff -Nru a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig --- a/drivers/i2c/busses/Kconfig Sun Feb 8 20:46:01 2004 +++ b/drivers/i2c/busses/Kconfig Sun Feb 8 20:46:01 2004 @@ -69,6 +69,18 @@ This support is also available as a module. If so, the module will be called i2c-elv. +config I2C_HYDRA + tristate "CHRP Apple Hydra Mac I/O I2C interface" + depends on I2C && PCI && PPC_CHRP && EXPERIMENTAL + select I2C_ALGOBIT + help + This supports the use of the I2C interface in the Apple Hydra Mac + I/O chip on some CHRP machines (e.g. the LongTrail). Say Y if you + have such a machine. + + This support is also available as a module. If so, the module + will be called i2c-hydra. + config I2C_I801 tristate "Intel 801" depends on I2C && PCI && EXPERIMENTAL diff -Nru a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile --- a/drivers/i2c/busses/Makefile Sun Feb 8 20:46:01 2004 +++ b/drivers/i2c/busses/Makefile Sun Feb 8 20:46:01 2004 @@ -8,6 +8,7 @@ obj-$(CONFIG_I2C_AMD8111) += i2c-amd8111.o obj-$(CONFIG_I2C_ELEKTOR) += i2c-elektor.o obj-$(CONFIG_I2C_ELV) += i2c-elv.o +obj-$(CONFIG_I2C_HYDRA) += i2c-hydra.o obj-$(CONFIG_I2C_I801) += i2c-i801.o obj-$(CONFIG_I2C_I810) += i2c-i810.o obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o diff -Nru a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/busses/i2c-hydra.c Sun Feb 8 20:46:01 2004 @@ -0,0 +1,186 @@ +/* + i2c-hydra.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + i2c Support for the Apple `Hydra' Mac I/O + + Copyright (c) 1999-2004 Geert Uytterhoeven + + Based on i2c Support for Via Technologies 82C586B South Bridge + Copyright (c) 1998, 1999 Kyösti Mälkki + + 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 + + +#define HYDRA_CPD_PD0 0x00000001 /* CachePD lines */ +#define HYDRA_CPD_PD1 0x00000002 +#define HYDRA_CPD_PD2 0x00000004 +#define HYDRA_CPD_PD3 0x00000008 + +#define HYDRA_SCLK HYDRA_CPD_PD0 +#define HYDRA_SDAT HYDRA_CPD_PD1 +#define HYDRA_SCLK_OE 0x00000010 +#define HYDRA_SDAT_OE 0x00000020 + +static inline void pdregw(void *data, u32 val) +{ + struct Hydra *hydra = (struct Hydra *)data; + writel(val, &hydra->CachePD); +} + +static inline u32 pdregr(void *data) +{ + struct Hydra *hydra = (struct Hydra *)data; + return readl(&hydra->CachePD); +} + +static void hydra_bit_setscl(void *data, int state) +{ + u32 val = pdregr(data); + if (state) + val &= ~HYDRA_SCLK_OE; + else { + val &= ~HYDRA_SCLK; + val |= HYDRA_SCLK_OE; + } + pdregw(data, val); +} + +static void hydra_bit_setsda(void *data, int state) +{ + u32 val = pdregr(data); + if (state) + val &= ~HYDRA_SDAT_OE; + else { + val &= ~HYDRA_SDAT; + val |= HYDRA_SDAT_OE; + } + pdregw(data, val); +} + +static int hydra_bit_getscl(void *data) +{ + return (pdregr(data) & HYDRA_SCLK) != 0; +} + +static int hydra_bit_getsda(void *data) +{ + return (pdregr(data) & HYDRA_SDAT) != 0; +} + +/* ------------------------------------------------------------------------ */ + +static struct i2c_algo_bit_data hydra_bit_data = { + .setsda = hydra_bit_setsda, + .setscl = hydra_bit_setscl, + .getsda = hydra_bit_getsda, + .getscl = hydra_bit_getscl, + .udelay = 5, + .mdelay = 5, + .timeout = HZ +}; + +static struct i2c_adapter hydra_adap = { + .owner = THIS_MODULE, + .name = "Hydra i2c", + .id = I2C_HW_B_HYDRA, + .algo_data = &hydra_bit_data, +}; + +static struct pci_device_id hydra_ids[] = { + { + .vendor = PCI_VENDOR_ID_APPLE, + .device = PCI_DEVICE_ID_APPLE_HYDRA, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, } +}; + +static int __devinit hydra_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + unsigned long base = pci_resource_start(dev, 0); + int res; + + if (!request_mem_region(base+offsetof(struct Hydra, CachePD), 4, + hydra_adap.name)) + return -EBUSY; + + hydra_bit_data.data = ioremap(base, pci_resource_len(dev, 0)); + if (hydra_bit_data.data == NULL) { + release_mem_region(base+offsetof(struct Hydra, CachePD), 4); + return -ENODEV; + } + + pdregw(hydra_bit_data.data, 0); /* clear SCLK_OE and SDAT_OE */ + hydra_adap.dev.parent = &dev->dev; + res = i2c_bit_add_bus(&hydra_adap); + if (res < 0) { + iounmap(hydra_bit_data.data); + release_mem_region(base+offsetof(struct Hydra, CachePD), 4); + return res; + } + return 0; +} + +static void __devexit hydra_remove(struct pci_dev *dev) +{ + pdregw(hydra_bit_data.data, 0); /* clear SCLK_OE and SDAT_OE */ + i2c_bit_del_bus(&hydra_adap); + iounmap(hydra_bit_data.data); + release_mem_region(pci_resource_start(dev, 0)+ + offsetof(struct Hydra, CachePD), 4); +} + + +static struct pci_driver hydra_driver = { + .name = "hydra smbus", + .id_table = hydra_ids, + .probe = hydra_probe, + .remove = __devexit_p(hydra_remove), +}; + +static int __init i2c_hydra_init(void) +{ + return pci_module_init(&hydra_driver); +} + + +static void __exit i2c_hydra_exit(void) +{ + pci_unregister_driver(&hydra_driver); +} + + + +MODULE_AUTHOR("Geert Uytterhoeven "); +MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O"); +MODULE_LICENSE("GPL"); + +module_init(i2c_hydra_init); +module_exit(i2c_hydra_exit); + diff -Nru a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig --- a/drivers/i2c/chips/Kconfig Sun Feb 8 20:46:01 2004 +++ b/drivers/i2c/chips/Kconfig Sun Feb 8 20:46:01 2004 @@ -45,6 +45,17 @@ This driver can also be built as a module. If so, the module will be called eeprom. +config SENSORS_GL518SM + tristate "Genesys Logic GL518SM" + depends on I2C && EXPERIMENTAL + select I2C_SENSOR + help + If you say yes here you get support for Genesys Logic GL518SM + sensor chips. + + This driver can also be built as a module. If so, the module + will be called gl518sm. + config SENSORS_IT87 tristate "ITE IT87xx and compatibles" depends on I2C && EXPERIMENTAL diff -Nru a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile --- a/drivers/i2c/chips/Makefile Sun Feb 8 20:46:01 2004 +++ b/drivers/i2c/chips/Makefile Sun Feb 8 20:46:01 2004 @@ -8,6 +8,7 @@ obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o +obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_LM75) += lm75.o obj-$(CONFIG_SENSORS_LM78) += lm78.o diff -Nru a/drivers/i2c/chips/gl518sm.c b/drivers/i2c/chips/gl518sm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/chips/gl518sm.c Sun Feb 8 20:46:01 2004 @@ -0,0 +1,612 @@ +/* + * gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware + * monitoring + * Copyright (C) 1998, 1999 Frodo Looijaard and + * Kyosti Malkki + * Copyright (C) 2004 Hong-Gunn Chew and + * Jean Delvare + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Ported to Linux 2.6 by Hong-Gunn Chew with the help of Jean Delvare + * and advice of Greg Kroah-Hartman. + * + * Notes about the port: + * Release 0x00 of the GL518SM chipset doesn't support reading of in0, + * in1 nor in2. The original driver had an ugly workaround to get them + * anyway (changing limits and watching alarms trigger and wear off). + * We did not keep that part of the original driver in the Linux 2.6 + * version, since it was making the driver significantly more complex + * with no real benefit. + * + * History: + * 2004-01-28 Original port. (Hong-Gunn Chew) + * 2004-01-31 Code review and approval. (Jean Delvare) + */ + +#include +#ifdef CONFIG_I2C_DEBUG_CHIP +#define DEBUG 1 +#endif + +#include +#include +#include +#include +#include + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END }; +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +static unsigned int normal_isa[] = { I2C_CLIENT_ISA_END }; +static unsigned int normal_isa_range[] = { I2C_CLIENT_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_2(gl518sm_r00, gl518sm_r80); + +/* Many GL518 constants specified below */ + +/* The GL518 registers */ +#define GL518_REG_CHIP_ID 0x00 +#define GL518_REG_REVISION 0x01 +#define GL518_REG_VENDOR_ID 0x02 +#define GL518_REG_CONF 0x03 +#define GL518_REG_TEMP_IN 0x04 +#define GL518_REG_TEMP_MAX 0x05 +#define GL518_REG_TEMP_HYST 0x06 +#define GL518_REG_FAN_COUNT 0x07 +#define GL518_REG_FAN_LIMIT 0x08 +#define GL518_REG_VIN1_LIMIT 0x09 +#define GL518_REG_VIN2_LIMIT 0x0a +#define GL518_REG_VIN3_LIMIT 0x0b +#define GL518_REG_VDD_LIMIT 0x0c +#define GL518_REG_VIN3 0x0d +#define GL518_REG_MISC 0x0f +#define GL518_REG_ALARM 0x10 +#define GL518_REG_MASK 0x11 +#define GL518_REG_INT 0x12 +#define GL518_REG_VIN2 0x13 +#define GL518_REG_VIN1 0x14 +#define GL518_REG_VDD 0x15 + + +/* + * Conversions. Rounding and limit checking is only done on the TO_REG + * variants. Note that you should be a bit careful with which arguments + * these macros are called: arguments may be evaluated more than once. + * Fixing this is just not worth it. + */ + +#define RAW_FROM_REG(val) val + +#define BOOL_FROM_REG(val) ((val)?0:1) +#define BOOL_TO_REG(val) ((val)?0:1) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0? \ + (val)-500:(val)+500)/1000)+119),0,255)) +#define TEMP_FROM_REG(val) (((val) - 119) * 1000) + +static inline u8 FAN_TO_REG(long rpm, int div) +{ + long rpmdiv; + if (rpm == 0) + return 0; + rpmdiv = SENSORS_LIMIT(rpm, 1, 1920000) * div; + return SENSORS_LIMIT((960000 + rpmdiv / 2) / rpmdiv, 1, 255); +} +#define FAN_FROM_REG(val,div) ((val)==0 ? 0 : (960000/((val)*(div)))) + +#define IN_TO_REG(val) (SENSORS_LIMIT((((val)+9)/19),0,255)) +#define IN_FROM_REG(val) ((val)*19) + +#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*4+47)/95),0,255)) +#define VDD_FROM_REG(val) (((val)*95+2)/4) + +#define DIV_TO_REG(val) ((val)==4?2:(val)==2?1:(val)==1?0:3) +#define DIV_FROM_REG(val) (1 << (val)) + +#define BEEP_MASK_TO_REG(val) ((val) & 0x7f & data->alarm_mask) +#define BEEP_MASK_FROM_REG(val) ((val) & 0x7f) + +/* Each client has this additional data */ +struct gl518_data { + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 voltage_in[4]; /* Register values; [0] = VDD */ + u8 voltage_min[4]; /* Register values; [0] = VDD */ + u8 voltage_max[4]; /* Register values; [0] = VDD */ + u8 iter_voltage_in[4]; /* Register values; [0] = VDD */ + u8 fan_in[2]; + u8 fan_min[2]; + u8 fan_div[2]; /* Register encoding, shifted right */ + u8 fan_auto1; /* Boolean */ + u8 temp_in; /* Register values */ + u8 temp_max; /* Register values */ + u8 temp_hyst; /* Register values */ + u8 alarms; /* Register value */ + u8 alarm_mask; /* Register value */ + u8 beep_mask; /* Register value */ + u8 beep_enable; /* Boolean */ +}; + +static int gl518_attach_adapter(struct i2c_adapter *adapter); +static int gl518_detect(struct i2c_adapter *adapter, int address, int kind); +static void gl518_init_client(struct i2c_client *client); +static int gl518_detach_client(struct i2c_client *client); +static int gl518_read_value(struct i2c_client *client, u8 reg); +static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value); +static void gl518_update_client(struct i2c_client *client); + +/* This is the driver that will be inserted */ +static struct i2c_driver gl518_driver = { + .owner = THIS_MODULE, + .name = "gl518sm", + .id = I2C_DRIVERID_GL518, + .flags = I2C_DF_NOTIFY, + .attach_adapter = gl518_attach_adapter, + .detach_client = gl518_detach_client, +}; + +/* + * Internal variables + */ + +static int gl518_id = 0; + +/* + * Sysfs stuff + */ + +#define show(type, suffix, value) \ +static ssize_t show_##suffix(struct device *dev, char *buf) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct gl518_data *data = i2c_get_clientdata(client); \ + gl518_update_client(client); \ + return sprintf(buf, "%d\n", type##_FROM_REG(data->value)); \ +} + +#define show_fan(suffix, value, index) \ +static ssize_t show_##suffix(struct device *dev, char *buf) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct gl518_data *data = i2c_get_clientdata(client); \ + gl518_update_client(client); \ + return sprintf(buf, "%d\n", FAN_FROM_REG(data->value[index], \ + DIV_FROM_REG(data->fan_div[index]))); \ +} + +show(TEMP, temp_input1, temp_in); +show(TEMP, temp_max1, temp_max); +show(TEMP, temp_hyst1, temp_hyst); +show(BOOL, fan_auto1, fan_auto1); +show_fan(fan_input1, fan_in, 0); +show_fan(fan_input2, fan_in, 1); +show_fan(fan_min1, fan_min, 0); +show_fan(fan_min2, fan_min, 1); +show(DIV, fan_div1, fan_div[0]); +show(DIV, fan_div2, fan_div[1]); +show(VDD, in_input0, voltage_in[0]); +show(IN, in_input1, voltage_in[1]); +show(IN, in_input2, voltage_in[2]); +show(IN, in_input3, voltage_in[3]); +show(VDD, in_min0, voltage_min[0]); +show(IN, in_min1, voltage_min[1]); +show(IN, in_min2, voltage_min[2]); +show(IN, in_min3, voltage_min[3]); +show(VDD, in_max0, voltage_max[0]); +show(IN, in_max1, voltage_max[1]); +show(IN, in_max2, voltage_max[2]); +show(IN, in_max3, voltage_max[3]); +show(RAW, alarms, alarms); +show(BOOL, beep_enable, beep_enable); +show(BEEP_MASK, beep_mask, beep_mask); + +#define set(type, suffix, value, reg) \ +static ssize_t set_##suffix(struct device *dev, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct gl518_data *data = i2c_get_clientdata(client); \ + data->value = type##_TO_REG(simple_strtol(buf, NULL, 10)); \ + gl518_write_value(client, reg, data->value); \ + return count; \ +} + +#define set_bits(type, suffix, value, reg, mask, shift) \ +static ssize_t set_##suffix(struct device *dev, const char *buf, \ + size_t count) \ +{ \ + struct i2c_client *client = to_i2c_client(dev); \ + struct gl518_data *data = i2c_get_clientdata(client); \ + int regvalue = gl518_read_value(client, reg); \ + data->value = type##_TO_REG(simple_strtoul(buf, NULL, 10)); \ + regvalue = (regvalue & ~mask) | (data->value << shift); \ + gl518_write_value(client, reg, regvalue); \ + return count; \ +} + +#define set_low(type, suffix, value, reg) \ + set_bits(type, suffix, value, reg, 0x00ff, 0) +#define set_high(type, suffix, value, reg) \ + set_bits(type, suffix, value, reg, 0xff00, 8) + +set(TEMP, temp_max1, temp_max, GL518_REG_TEMP_MAX); +set(TEMP, temp_hyst1, temp_hyst, GL518_REG_TEMP_HYST); +set_bits(BOOL, fan_auto1, fan_auto1, GL518_REG_MISC, 0x08, 3); +set_bits(DIV, fan_div1, fan_div[0], GL518_REG_MISC, 0xc0, 6); +set_bits(DIV, fan_div2, fan_div[1], GL518_REG_MISC, 0x30, 4); +set_low(VDD, in_min0, voltage_min[0], GL518_REG_VDD_LIMIT); +set_low(IN, in_min1, voltage_min[1], GL518_REG_VIN1_LIMIT); +set_low(IN, in_min2, voltage_min[2], GL518_REG_VIN2_LIMIT); +set_low(IN, in_min3, voltage_min[3], GL518_REG_VIN3_LIMIT); +set_high(VDD, in_max0, voltage_max[0], GL518_REG_VDD_LIMIT); +set_high(IN, in_max1, voltage_max[1], GL518_REG_VIN1_LIMIT); +set_high(IN, in_max2, voltage_max[2], GL518_REG_VIN2_LIMIT); +set_high(IN, in_max3, voltage_max[3], GL518_REG_VIN3_LIMIT); +set_bits(BOOL, beep_enable, beep_enable, GL518_REG_CONF, 0x04, 2); +set(BEEP_MASK, beep_mask, beep_mask, GL518_REG_ALARM); + +static ssize_t set_fan_min1(struct device *dev, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct gl518_data *data = i2c_get_clientdata(client); + int regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT); + + data->fan_min[0] = FAN_TO_REG(simple_strtoul(buf, NULL, 10), + DIV_FROM_REG(data->fan_div[0])); + regvalue = (regvalue & 0x00ff) | (data->fan_min[0] << 8); + gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue); + + data->beep_mask = gl518_read_value(client, GL518_REG_ALARM); + if (data->fan_min[0] == 0) + data->alarm_mask &= ~0x20; + else + data->alarm_mask |= 0x20; + data->beep_mask &= data->alarm_mask; + gl518_write_value(client, GL518_REG_ALARM, data->beep_mask); + + return count; +} + +static ssize_t set_fan_min2(struct device *dev, const char *buf, size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct gl518_data *data = i2c_get_clientdata(client); + int regvalue = gl518_read_value(client, GL518_REG_FAN_LIMIT); + + data->fan_min[1] = FAN_TO_REG(simple_strtoul(buf, NULL, 10), + DIV_FROM_REG(data->fan_div[1])); + regvalue = (regvalue & 0xff00) | data->fan_min[1]; + gl518_write_value(client, GL518_REG_FAN_LIMIT, regvalue); + + data->beep_mask = gl518_read_value(client, GL518_REG_ALARM); + if (data->fan_min[1] == 0) + data->alarm_mask &= ~0x40; + else + data->alarm_mask |= 0x40; + data->beep_mask &= data->alarm_mask; + gl518_write_value(client, GL518_REG_ALARM, data->beep_mask); + + return count; +} + +static DEVICE_ATTR(temp_input1, S_IRUGO, show_temp_input1, NULL); +static DEVICE_ATTR(temp_max1, S_IWUSR|S_IRUGO, show_temp_max1, set_temp_max1); +static DEVICE_ATTR(temp_hyst1, S_IWUSR|S_IRUGO, + show_temp_hyst1, set_temp_hyst1); +static DEVICE_ATTR(fan_auto1, S_IWUSR|S_IRUGO, show_fan_auto1, set_fan_auto1); +static DEVICE_ATTR(fan_input1, S_IRUGO, show_fan_input1, NULL); +static DEVICE_ATTR(fan_input2, S_IRUGO, show_fan_input2, NULL); +static DEVICE_ATTR(fan_min1, S_IWUSR|S_IRUGO, show_fan_min1, set_fan_min1); +static DEVICE_ATTR(fan_min2, S_IWUSR|S_IRUGO, show_fan_min2, set_fan_min2); +static DEVICE_ATTR(fan_div1, S_IWUSR|S_IRUGO, show_fan_div1, set_fan_div1); +static DEVICE_ATTR(fan_div2, S_IWUSR|S_IRUGO, show_fan_div2, set_fan_div2); +static DEVICE_ATTR(in_input0, S_IRUGO, show_in_input0, NULL); +static DEVICE_ATTR(in_input1, S_IRUGO, show_in_input1, NULL); +static DEVICE_ATTR(in_input2, S_IRUGO, show_in_input2, NULL); +static DEVICE_ATTR(in_input3, S_IRUGO, show_in_input3, NULL); +static DEVICE_ATTR(in_min0, S_IWUSR|S_IRUGO, show_in_min0, set_in_min0); +static DEVICE_ATTR(in_min1, S_IWUSR|S_IRUGO, show_in_min1, set_in_min1); +static DEVICE_ATTR(in_min2, S_IWUSR|S_IRUGO, show_in_min2, set_in_min2); +static DEVICE_ATTR(in_min3, S_IWUSR|S_IRUGO, show_in_min3, set_in_min3); +static DEVICE_ATTR(in_max0, S_IWUSR|S_IRUGO, show_in_max0, set_in_max0); +static DEVICE_ATTR(in_max1, S_IWUSR|S_IRUGO, show_in_max1, set_in_max1); +static DEVICE_ATTR(in_max2, S_IWUSR|S_IRUGO, show_in_max2, set_in_max2); +static DEVICE_ATTR(in_max3, S_IWUSR|S_IRUGO, show_in_max3, set_in_max3); +static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL); +static DEVICE_ATTR(beep_enable, S_IWUSR|S_IRUGO, + show_beep_enable, set_beep_enable); +static DEVICE_ATTR(beep_mask, S_IWUSR|S_IRUGO, + show_beep_mask, set_beep_mask); + +/* + * Real code + */ + +static int gl518_attach_adapter(struct i2c_adapter *adapter) +{ + if (!(adapter->class & I2C_ADAP_CLASS_SMBUS)) + return 0; + return i2c_detect(adapter, &addr_data, gl518_detect); +} + +static int gl518_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int i; + struct i2c_client *new_client; + struct gl518_data *data; + int err = 0; + const char *name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto exit; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access gl518_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct gl518_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto exit; + } + memset(new_client, 0x00, sizeof(struct i2c_client) + + sizeof(struct gl518_data)); + + data = (struct gl518_data *) (new_client + 1); + i2c_set_clientdata(new_client, data); + + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &gl518_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if ((gl518_read_value(new_client, GL518_REG_CHIP_ID) != 0x80) + || (gl518_read_value(new_client, GL518_REG_CONF) & 0x80)) + goto exit_free; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = gl518_read_value(new_client, GL518_REG_REVISION); + if (i == 0x00) { + kind = gl518sm_r00; + name = "gl518sm"; + } else if (i == 0x80) { + kind = gl518sm_r80; + name = "gl518sm"; + } else { + if (kind <= 0) + dev_info(&adapter->dev, + "Ignoring 'force' parameter for unknown " + "chip at adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto exit_free; + } + } + + /* Fill in the remaining client fields */ + strlcpy(new_client->name, name, I2C_NAME_SIZE); + new_client->id = gl518_id++; + data->type = kind; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto exit_free; + + /* Initialize the GL518SM chip */ + data->alarm_mask = 0xff; + data->voltage_in[0]=data->voltage_in[1]=data->voltage_in[2]=0; + gl518_init_client((struct i2c_client *) new_client); + + /* Register sysfs hooks */ + device_create_file(&new_client->dev, &dev_attr_in_input0); + device_create_file(&new_client->dev, &dev_attr_in_input1); + device_create_file(&new_client->dev, &dev_attr_in_input2); + device_create_file(&new_client->dev, &dev_attr_in_input3); + device_create_file(&new_client->dev, &dev_attr_in_min0); + device_create_file(&new_client->dev, &dev_attr_in_min1); + device_create_file(&new_client->dev, &dev_attr_in_min2); + device_create_file(&new_client->dev, &dev_attr_in_min3); + device_create_file(&new_client->dev, &dev_attr_in_max0); + device_create_file(&new_client->dev, &dev_attr_in_max1); + device_create_file(&new_client->dev, &dev_attr_in_max2); + device_create_file(&new_client->dev, &dev_attr_in_max3); + device_create_file(&new_client->dev, &dev_attr_fan_auto1); + device_create_file(&new_client->dev, &dev_attr_fan_input1); + device_create_file(&new_client->dev, &dev_attr_fan_input2); + device_create_file(&new_client->dev, &dev_attr_fan_min1); + device_create_file(&new_client->dev, &dev_attr_fan_min2); + device_create_file(&new_client->dev, &dev_attr_fan_div1); + device_create_file(&new_client->dev, &dev_attr_fan_div2); + device_create_file(&new_client->dev, &dev_attr_temp_input1); + device_create_file(&new_client->dev, &dev_attr_temp_max1); + device_create_file(&new_client->dev, &dev_attr_temp_hyst1); + device_create_file(&new_client->dev, &dev_attr_alarms); + device_create_file(&new_client->dev, &dev_attr_beep_enable); + device_create_file(&new_client->dev, &dev_attr_beep_mask); + + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + +exit_free: + kfree(new_client); +exit: + return err; +} + + +/* Called when we have found a new GL518SM. + Note that we preserve D4:NoFan2 and D2:beep_enable. */ +static void gl518_init_client(struct i2c_client *client) +{ + /* Make sure we leave D7:Reset untouched */ + u8 regvalue = gl518_read_value(client, GL518_REG_CONF) & 0x7f; + + /* Comparator mode (D3=0), standby mode (D6=0) */ + gl518_write_value(client, GL518_REG_CONF, (regvalue &= 0x37)); + + /* Never interrupts */ + gl518_write_value(client, GL518_REG_MASK, 0x00); + + /* Clear status register (D5=1), start (D6=1) */ + gl518_write_value(client, GL518_REG_CONF, 0x20 | regvalue); + gl518_write_value(client, GL518_REG_CONF, 0x40 | regvalue); +} + +static int gl518_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + +static inline u16 swap_bytes(u16 val) +{ + return (val >> 8) | (val << 8); +} + +/* Registers 0x07 to 0x0c are word-sized, others are byte-sized + GL518 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +static int gl518_read_value(struct i2c_client *client, u8 reg) +{ + if ((reg >= 0x07) && (reg <= 0x0c)) + return swap_bytes(i2c_smbus_read_word_data(client, reg)); + else + return i2c_smbus_read_byte_data(client, reg); +} + +/* Registers 0x07 to 0x0c are word-sized, others are byte-sized + GL518 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +static int gl518_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if ((reg >= 0x07) && (reg <= 0x0c)) + return i2c_smbus_write_word_data(client, reg, + swap_bytes(value)); + else + return i2c_smbus_write_byte_data(client, reg, value); +} + +static void gl518_update_client(struct i2c_client *client) +{ + struct gl518_data *data = i2c_get_clientdata(client); + int val; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + dev_dbg(&client->dev, "Starting gl518 update\n"); + + data->alarms = gl518_read_value(client, GL518_REG_INT); + data->beep_mask = gl518_read_value(client, GL518_REG_ALARM); + + val = gl518_read_value(client, GL518_REG_VDD_LIMIT); + data->voltage_min[0] = val & 0xff; + data->voltage_max[0] = (val >> 8) & 0xff; + val = gl518_read_value(client, GL518_REG_VIN1_LIMIT); + data->voltage_min[1] = val & 0xff; + data->voltage_max[1] = (val >> 8) & 0xff; + val = gl518_read_value(client, GL518_REG_VIN2_LIMIT); + data->voltage_min[2] = val & 0xff; + data->voltage_max[2] = (val >> 8) & 0xff; + val = gl518_read_value(client, GL518_REG_VIN3_LIMIT); + data->voltage_min[3] = val & 0xff; + data->voltage_max[3] = (val >> 8) & 0xff; + + val = gl518_read_value(client, GL518_REG_FAN_COUNT); + data->fan_in[0] = (val >> 8) & 0xff; + data->fan_in[1] = val & 0xff; + + val = gl518_read_value(client, GL518_REG_FAN_LIMIT); + data->fan_min[0] = (val >> 8) & 0xff; + data->fan_min[1] = val & 0xff; + + data->temp_in = gl518_read_value(client, GL518_REG_TEMP_IN); + data->temp_max = + gl518_read_value(client, GL518_REG_TEMP_MAX); + data->temp_hyst = + gl518_read_value(client, GL518_REG_TEMP_HYST); + + val = gl518_read_value(client, GL518_REG_MISC); + data->fan_div[0] = (val >> 6) & 0x03; + data->fan_div[1] = (val >> 4) & 0x03; + data->fan_auto1 = (val >> 3) & 0x01; + + data->alarms &= data->alarm_mask; + + val = gl518_read_value(client, GL518_REG_CONF); + data->beep_enable = (val >> 2) & 1; + + if (data->type != gl518sm_r00) { + data->voltage_in[0] = + gl518_read_value(client, GL518_REG_VDD); + data->voltage_in[1] = + gl518_read_value(client, GL518_REG_VIN1); + data->voltage_in[2] = + gl518_read_value(client, GL518_REG_VIN2); + } + data->voltage_in[3] = + gl518_read_value(client, GL518_REG_VIN3); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + +static int __init sensors_gl518sm_init(void) +{ + return i2c_add_driver(&gl518_driver); +} + +static void __exit sensors_gl518sm_exit(void) +{ + i2c_del_driver(&gl518_driver); +} + +MODULE_AUTHOR("Frodo Looijaard , " + "Kyosti Malkki and " + "Hong-Gunn Chew "); +MODULE_DESCRIPTION("GL518SM driver"); +MODULE_LICENSE("GPL"); + +module_init(sensors_gl518sm_init); +module_exit(sensors_gl518sm_exit); diff -Nru a/drivers/i2c/chips/lm85.c b/drivers/i2c/chips/lm85.c --- a/drivers/i2c/chips/lm85.c Sun Feb 8 20:46:01 2004 +++ b/drivers/i2c/chips/lm85.c Sun Feb 8 20:46:01 2004 @@ -816,7 +816,7 @@ kind = lm85b ; } else if( company == LM85_COMPANY_NATIONAL && (verstep & 0xf0) == LM85_VERSTEP_GENERIC ) { - dev_err(&adapter->dev, "Unrecgonized version/stepping 0x%02x" + dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" " Defaulting to LM85.\n", verstep); kind = any_chip ; } else if( company == LM85_COMPANY_ANALOG_DEV @@ -827,7 +827,7 @@ kind = adt7463 ; } else if( company == LM85_COMPANY_ANALOG_DEV && (verstep & 0xf0) == LM85_VERSTEP_GENERIC ) { - dev_err(&adapter->dev, "Unrecgonized version/stepping 0x%02x" + dev_err(&adapter->dev, "Unrecognized version/stepping 0x%02x" " Defaulting to ADM1027.\n", verstep); kind = adm1027 ; } else if( kind == 0 && (verstep & 0xf0) == 0x60) { @@ -1204,7 +1204,7 @@ /* Thanks to Richard Barrington for adding the LM85 to sensors-detect. * Thanks to Margit Schubert-While for help with - * post 2.7.0 CVS changes + * post 2.7.0 CVS changes. */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Philip Pokorny , Margit Schubert-While "); diff -Nru a/drivers/i2c/chips/w83l785ts.c b/drivers/i2c/chips/w83l785ts.c --- a/drivers/i2c/chips/w83l785ts.c Sun Feb 8 20:46:01 2004 +++ b/drivers/i2c/chips/w83l785ts.c Sun Feb 8 20:46:01 2004 @@ -38,6 +38,9 @@ #include #include +/* How many retries on register read error */ +#define MAX_RETRIES 5 + /* * Address to scan * Address is fully defined internally and cannot be changed. @@ -82,6 +85,7 @@ static int w83l785ts_detect(struct i2c_adapter *adapter, int address, int kind); static int w83l785ts_detach_client(struct i2c_client *client); +static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval); static void w83l785ts_update_client(struct i2c_client *client); /* @@ -137,8 +141,8 @@ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->temp_over)); } -static DEVICE_ATTR(temp_input, S_IRUGO, show_temp, NULL) -static DEVICE_ATTR(temp_max, S_IRUGO, show_temp_over, NULL) +static DEVICE_ATTR(temp_input1, S_IRUGO, show_temp, NULL) +static DEVICE_ATTR(temp_max1, S_IRUGO, show_temp_over, NULL) /* * Real code @@ -196,10 +200,10 @@ * are skipped. */ if (kind < 0) { /* detection */ - if (((i2c_smbus_read_byte_data(new_client, - W83L785TS_REG_CONFIG) & 0x80) != 0x00) - || ((i2c_smbus_read_byte_data(new_client, - W83L785TS_REG_TYPE) & 0xFC) != 0x00)) { + if (((w83l785ts_read_value(new_client, + W83L785TS_REG_CONFIG, 0) & 0x80) != 0x00) + || ((w83l785ts_read_value(new_client, + W83L785TS_REG_TYPE, 0) & 0xFC) != 0x00)) { dev_dbg(&adapter->dev, "W83L785TS-S detection failed at 0x%02x.\n", address); @@ -211,12 +215,12 @@ u16 man_id; u8 chip_id; - man_id = (i2c_smbus_read_byte_data(new_client, - W83L785TS_REG_MAN_ID1) << 8) + - i2c_smbus_read_byte_data(new_client, - W83L785TS_REG_MAN_ID2); - chip_id = i2c_smbus_read_byte_data(new_client, - W83L785TS_REG_CHIP_ID); + man_id = (w83l785ts_read_value(new_client, + W83L785TS_REG_MAN_ID1, 0) << 8) + + w83l785ts_read_value(new_client, + W83L785TS_REG_MAN_ID2, 0); + chip_id = w83l785ts_read_value(new_client, + W83L785TS_REG_CHIP_ID, 0); if (man_id == 0x5CA3) { /* Winbond */ if (chip_id == 0x70) { /* W83L785TS-S */ @@ -239,6 +243,9 @@ data->valid = 0; init_MUTEX(&data->update_lock); + /* Default values in case the first read fails (unlikely). */ + data->temp_over = data->temp = 0; + /* Tell the I2C layer a new client has arrived. */ if ((err = i2c_attach_client(new_client))) goto exit_free; @@ -249,8 +256,8 @@ */ /* Register sysfs hooks */ - device_create_file(&new_client->dev, &dev_attr_temp_input); - device_create_file(&new_client->dev, &dev_attr_temp_max); + device_create_file(&new_client->dev, &dev_attr_temp_input1); + device_create_file(&new_client->dev, &dev_attr_temp_max1); return 0; @@ -274,6 +281,26 @@ return 0; } +static u8 w83l785ts_read_value(struct i2c_client *client, u8 reg, u8 defval) +{ + int value, i; + + /* Frequent read errors have been reported on Asus boards, so we + * retry on read errors. If it still fails (unlikely), return the + * default value requested by the caller. */ + for (i = 1; i <= MAX_RETRIES; i++) { + value = i2c_smbus_read_byte_data(client, reg); + if (value >= 0) + return value; + dev_dbg(&client->dev, "Read failed, will retry in %d.\n", i); + i2c_delay(i); + } + + dev_err(&client->dev, "Couldn't read value from register. " + "Please report.\n"); + return defval; +} + static void w83l785ts_update_client(struct i2c_client *client) { struct w83l785ts_data *data = i2c_get_clientdata(client); @@ -284,10 +311,10 @@ || (jiffies - data->last_updated > HZ * 2) || (jiffies < data->last_updated)) { dev_dbg(&client->dev, "Updating w83l785ts data.\n"); - data->temp = i2c_smbus_read_byte_data(client, - W83L785TS_REG_TEMP); - data->temp_over = i2c_smbus_read_byte_data(client, - W83L785TS_REG_TEMP_OVER); + data->temp = w83l785ts_read_value(client, + W83L785TS_REG_TEMP, data->temp); + data->temp_over = w83l785ts_read_value(client, + W83L785TS_REG_TEMP_OVER, data->temp_over); data->last_updated = jiffies; data->valid = 1; diff -Nru a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c --- a/drivers/i2c/i2c-core.c Sun Feb 8 20:46:01 2004 +++ b/drivers/i2c/i2c-core.c Sun Feb 8 20:46:01 2004 @@ -598,7 +598,7 @@ ret = adap->algo->master_xfer(adap,&msg,1); up(&adap->bus_lock); - dev_dbg(&client->dev, "master_recv: return:%d (count:%d, addr:0x%02x)\n", + dev_dbg(&client->adapter->dev, "master_recv: return:%d (count:%d, addr:0x%02x)\n", ret, count, client->addr); /* if everything went ok (i.e. 1 msg transmitted), return #bytes