--- linux-old/drivers/i2c/i2c-ali1535.c Mon Mar 25 15:28:35 CET 2002 +++ linux/drivers/i2c/i2c-ali1535.c Mon Mar 25 15:28:35 CET 2002 @@ -0,0 +1,691 @@ +/* + i2c-ali1535.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2000 Frodo Looijaard , + Philip Edelbrock , + Mark D. Studebaker , + Dan Eaton and + Stephen Rousset + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This is the driver for the SMB Host controller on + Acer Labs Inc. (ALI) M1535 South Bridge. + + The M1535 is a South bridge for portable systems. + It is very similar to the M15x3 South bridges also produced + by Acer Labs Inc. Some of the registers within the part + have moved and some have been redefined slightly. Additionally, + the sequencing of the SMBus transactions has been modified + to be more consistent with the sequencing recommended by + the manufacturer and observed through testing. These + changes are reflected in this driver and can be identified + by comparing this driver to the i2c-ali15x3 driver. + For an overview of these chips see http://www.acerlabs.com + + The SMB controller is part of the 7101 device, which is an + ACPI-compliant Power Management Unit (PMU). + + The whole 7101 device has to be enabled for the SMB to work. + You can't just enable the SMB alone. + The SMB and the ACPI have separate I/O spaces. + We make sure that the SMB is enabled. We leave the ACPI alone. + + This driver controls the SMB Host only. + + This driver does not use interrupts. +*/ + + +/* Note: we assume there can only be one ALI1535, with one SMBus interface */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef DECLARE_MUTEX +#define DECLARE_MUTEX(name) struct semaphore name = MUTEX +#endif /* def DECLARE_MUTEX */ + +/* ALI1535 SMBus address offsets */ +#define SMBHSTSTS (0 + ali1535_smba) +#define SMBHSTTYP (1 + ali1535_smba) +#define SMBHSTPORT (2 + ali1535_smba) +#define SMBHSTCMD (7 + ali1535_smba) +#define SMBHSTADD (3 + ali1535_smba) +#define SMBHSTDAT0 (4 + ali1535_smba) +#define SMBHSTDAT1 (5 + ali1535_smba) +#define SMBBLKDAT (6 + ali1535_smba) + +/* PCI Address Constants */ +#define SMBCOM 0x004 +#define SMBREV 0x008 +#define SMBCFG 0x0D1 +#define SMBBA 0x0E2 +#define SMBHSTCFG 0x0F0 +#define SMBCLK 0x0F2 + +/* Other settings */ +#define MAX_TIMEOUT 500 /* times 1/100 sec */ +#define ALI1535_SMB_IOSIZE 32 + +/* +*/ +#define ALI1535_SMB_DEFAULTBASE 0x8040 + +/* ALI1535 address lock bits */ +#define ALI1535_LOCK 0x06 < dwe > + +/* ALI1535 command constants */ +#define ALI1535_QUICK 0x00 +#define ALI1535_BYTE 0x10 +#define ALI1535_BYTE_DATA 0x20 +#define ALI1535_WORD_DATA 0x30 +#define ALI1535_BLOCK_DATA 0x40 +#define ALI1535_I2C_READ 0x60 + +#define ALI1535_DEV10B_EN 0x80 /* Enable 10-bit addressing in */ + /* I2C read */ +#define ALI1535_T_OUT 0x08 /* Time-out Command (write) */ +#define ALI1535_A_HIGH_BIT9 0x08 /* Bit 9 of 10-bit address in */ + /* Alert-Response-Address */ + /* (read) */ +#define ALI1535_KILL 0x04 /* Kill Command (write) */ +#define ALI1535_A_HIGH_BIT8 0x04 /* Bit 8 of 10-bit address in */ + /* Alert-Response-Address */ + /* (read) */ + +#define ALI1535_D_HI_MASK 0x03 /* Mask for isolating bits 9-8 */ + /* of 10-bit address in I2C */ + /* Read Command */ + +/* ALI1535 status register bits */ +#define ALI1535_STS_IDLE 0x04 +#define ALI1535_STS_BUSY 0x08 /* host busy */ +#define ALI1535_STS_DONE 0x10 /* transaction complete */ +#define ALI1535_STS_DEV 0x20 /* device error */ +#define ALI1535_STS_BUSERR 0x40 /* bus error */ +#define ALI1535_STS_FAIL 0x80 /* failed bus transaction */ +#define ALI1535_STS_ERR 0xE0 /* all the bad error bits */ + +#define ALI1535_BLOCK_CLR 0x04 /* reset block data index */ + +/* ALI1535 device address register bits */ +#define ALI1535_RD_ADDR 0x01 /* Read/Write Bit in Device */ + /* Address field */ + /* -> Write = 0 */ + /* -> Read = 1 */ +#define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable */ + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_ali1535_init(void); +static int __init ali1535_cleanup(void); +static int ali1535_setup(void); +static s32 ali1535_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data); +static void ali1535_do_pause(unsigned int amount); +static int ali1535_transaction(void); +static void ali1535_inc(struct i2c_adapter *adapter); +static void ali1535_dec(struct i2c_adapter *adapter); +static u32 ali1535_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-i2c SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ ali1535_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ ali1535_func, +}; + +static struct i2c_adapter ali1535_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI1535, + &smbus_algorithm, + NULL, + ali1535_inc, + ali1535_dec, + NULL, + NULL, +}; + +static int __initdata ali1535_initialized; +static unsigned short ali1535_smba = 0; +DECLARE_MUTEX(i2c_ali1535_sem); + + +/* Detect whether a ALI1535 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int ali1535_setup(void) +{ + int error_return = 0; + unsigned char temp; + + struct pci_dev *ALI1535_dev; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) { + printk("i2c-ali1535.o: Error: No PCI-bus found!\n"); + error_return = -ENODEV; + goto END; + } + + /* Look for the ALI1535, M7101 device */ + ALI1535_dev = NULL; + ALI1535_dev = pci_find_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M7101, + ALI1535_dev); + + if (ALI1535_dev == NULL) { + printk("i2c-ali1535.o: Error: Can't detect ali1535!\n"); + error_return = -ENODEV; + goto END; + } + +/* Check the following things: + - SMB I/O address is initialized + - Device is enabled + - We can use the addresses +*/ + +/* Determine the address of the SMBus area */ + pci_read_config_word(ALI1535_dev, SMBBA, &ali1535_smba); + ali1535_smba &= (0xffff & ~(ALI1535_SMB_IOSIZE - 1)); + if (ali1535_smba == 0) { + printk + ("i2c-ali1535.o: ALI1535_smb region uninitialized - upgrade BIOS?\n"); + error_return = -ENODEV; + } + + if (error_return == -ENODEV) + goto END; + + if (check_region(ali1535_smba, ALI1535_SMB_IOSIZE)) { + printk + ("i2c-ali1535.o: ALI1535_smb region 0x%x already in use!\n", + ali1535_smba); + error_return = -ENODEV; + } + + if (error_return == -ENODEV) + goto END; + + /* check if whole device is enabled */ + pci_read_config_byte(ALI1535_dev, SMBCFG, &temp); + if ((temp & ALI1535_SMBIO_EN) == 0) { + printk + ("i2c-ali1535.o: SMB device not enabled - upgrade BIOS?\n"); + error_return = -ENODEV; + goto END; + } + +/* Is SMB Host controller enabled? */ + pci_read_config_byte(ALI1535_dev, SMBHSTCFG, &temp); + if ((temp & 1) == 0) { + printk + ("i2c-ali1535.o: SMBus controller not enabled - upgrade BIOS?\n"); + error_return = -ENODEV; + goto END; + } + +/* set SMB clock to 74KHz as recommended in data sheet */ + pci_write_config_byte(ALI1535_dev, SMBCLK, 0x20); + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(ali1535_smba, ALI1535_SMB_IOSIZE, "ali1535-smb"); + +#ifdef DEBUG +/* + The interrupt routing for SMB is set up in register 0x77 in the + 1533 ISA Bridge device, NOT in the 7101 device. + Don't bother with finding the 1533 device and reading the register. + if ((....... & 0x0F) == 1) + printk("i2c-ali1535.o: ALI1535 using Interrupt 9 for SMBus.\n"); +*/ + pci_read_config_byte(ALI1535_dev, SMBREV, &temp); + printk("i2c-ali1535.o: SMBREV = 0x%X\n", temp); + printk("i2c-ali1535.o: ALI1535_smba = 0x%X\n", ali1535_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +/* Internally used pause function */ +void ali1535_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int ali1535_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-ali1535.o: Transaction (pre): STS=%02x, TYP=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + + /* get status */ + temp = inb_p(SMBHSTSTS); + + /* Make sure the SMBus host is ready to start transmitting */ + /* Check the busy bit first */ + if (temp & ALI1535_STS_BUSY) { +/* + If the host controller is still busy, it may have timed out in the previous transaction, + resulting in a "SMBus Timeout" printk. + I've tried the following to reset a stuck busy bit. + 1. Reset the controller with an KILL command. + (this doesn't seem to clear the controller if an external device is hung) + 2. Reset the controller and the other SMBus devices with a T_OUT command. + (this clears the host busy bit if an external device is hung, + but it comes back upon a new access to a device) + 3. Disable and reenable the controller in SMBHSTCFG + Worst case, nothing seems to work except power reset. +*/ +/* Abort - reset the host controller */ +/* +#ifdef DEBUG + printk("i2c-ali1535.o: Resetting host controller to clear busy condition\n",temp); +#endif + outb_p(ALI1535_KILL, SMBHSTTYP); + temp = inb_p(SMBHSTSTS); + if (temp & ALI1535_STS_BUSY) { +*/ + +/* + Try resetting entire SMB bus, including other devices - + This may not work either - it clears the BUSY bit but + then the BUSY bit may come back on when you try and use the chip again. + If that's the case you are stuck. +*/ + printk + ("i2c-ali1535.o: Resetting entire SMB Bus to clear busy condition (%02x)\n", + temp); + outb_p(ALI1535_T_OUT, SMBHSTTYP); + temp = inb_p(SMBHSTSTS); + } +/* + } +*/ + + /* now check the error bits and the busy bit */ + if (temp & (ALI1535_STS_ERR | ALI1535_STS_BUSY)) { + /* do a clear-on-write */ + outb_p(0xFF, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & + (ALI1535_STS_ERR | ALI1535_STS_BUSY)) { + /* this is probably going to be correctable only by a power reset + as one of the bits now appears to be stuck */ + /* This may be a bus or device with electrical problems. */ + printk + ("i2c-ali1535.o: SMBus reset failed! (0x%02x) - controller or device on bus is probably hung\n", + temp); + return -1; + } + } else { + /* check and clear done bit */ + if (temp & ALI1535_STS_DONE) { + outb_p(temp, SMBHSTSTS); + } + } + + /* start the transaction by writing anything to the start register */ + outb_p(0xFF, SMBHSTPORT); + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + ali1535_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while (((temp & ALI1535_STS_BUSY) && !(temp & ALI1535_STS_IDLE)) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; + printk("i2c-ali1535.o: SMBus Timeout!\n"); + } + + if (temp & ALI1535_STS_FAIL) { + result = -1; +#ifdef DEBUG + printk("i2c-ali1535.o: Error: Failed bus transaction\n"); +#endif + } + +/* + Unfortunately the ALI SMB controller maps "no response" and "bus collision" + into a single bit. No reponse is the usual case so don't do a printk. + This means that bus collisions go unreported. +*/ + if (temp & ALI1535_STS_BUSERR) { + result = -1; +#ifdef DEBUG + printk + ("i2c-ali1535.o: Error: no response or bus collision ADD=%02x\n", + inb_p(SMBHSTADD)); +#endif + } + +/* haven't ever seen this */ + if (temp & ALI1535_STS_DEV) { + result = -1; + printk("i2c-ali1535.o: Error: device error\n"); + } + +/* + check to see if the "command complete" indication is set + */ + if (!(temp & ALI1535_STS_DONE)) { + result = -1; + printk("i2c-ali1535.o: Error: command never completed\n"); + } +#ifdef DEBUG + printk + ("i2c-ali1535.o: Transaction (post): STS=%02x, TYP=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTTYP), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + +/* + take consequent actions for error conditions + */ + if (!(temp & ALI1535_STS_DONE)) { + /* issue "kill" to reset host controller */ + outb_p(ALI1535_KILL,SMBHSTTYP); + outb_p(0xFF,SMBHSTSTS); + } + else if (temp & ALI1535_STS_ERR) { + /* issue "timeout" to reset all devices on bus */ + outb_p(ALI1535_T_OUT,SMBHSTTYP); + outb_p(0xFF,SMBHSTSTS); + } + + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 ali1535_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data * data) +{ + int i, len; + int temp; + int timeout; + s32 result = 0; + + down(&i2c_ali1535_sem); +/* make sure SMBus is idle */ + temp = inb_p(SMBHSTSTS); + for (timeout = 0; + (timeout < MAX_TIMEOUT) && !(temp & ALI1535_STS_IDLE); + timeout++) { + ali1535_do_pause(1); + temp = inb_p(SMBHSTSTS); + } + if (timeout >= MAX_TIMEOUT) { + printk("i2c-ali1535.o: Idle wait Timeout! STS=0x%02x\n", + temp); + } + +/* clear status register (clear-on-write) */ + outb_p(0xFF, SMBHSTSTS); + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + ("i2c-ali1535.o: I2C_SMBUS_PROC_CALL not supported!\n"); + result = -1; + goto EXIT; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_QUICK; + outb_p(size, SMBHSTTYP); /* output command */ + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_BYTE; + outb_p(size, SMBHSTTYP); /* output command */ + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_BYTE_DATA; + outb_p(size, SMBHSTTYP); /* output command */ + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_WORD_DATA; + outb_p(size, SMBHSTTYP); /* output command */ + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI1535_BLOCK_DATA; + outb_p(size, SMBHSTTYP); /* output command */ + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) { + len = 0; + data->block[0] = len; + } + if (len > 32) { + len = 32; + data->block[0] = len; + } + outb_p(len, SMBHSTDAT0); + outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + break; + } + + if (ali1535_transaction()) /* Error in transaction */ + { + result = -1; + goto EXIT; + } + + if ((read_write == I2C_SMBUS_WRITE) || (size == ALI1535_QUICK)) + { + result = 0; + goto EXIT; + } + + switch (size) { + case ALI1535_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI1535_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI1535_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case ALI1535_BLOCK_DATA: + len = inb_p(SMBHSTDAT0); + if (len > 32) + len = 32; + data->block[0] = len; + outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) { + data->block[i] = inb_p(SMBBLKDAT); +#ifdef DEBUG + printk + ("i2c-ali1535.o: Blk: len=%d, i=%d, data=%02x\n", + len, i, data->block[i]); +#endif /* DEBUG */ + } + break; + } +EXIT: + up(&i2c_ali1535_sem); + return result; +} + +void ali1535_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void ali1535_dec(struct i2c_adapter *adapter) +{ + + MOD_DEC_USE_COUNT; +} + +u32 ali1535_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +int __init i2c_ali1535_init(void) +{ + int res; + printk("i2c-ali1535.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG +/* PE- It might be good to make this a permanent part of the code! */ + if (ali1535_initialized) { + printk + ("i2c-ali1535.o: Oops, ali1535_init called a second time!\n"); + return -EBUSY; + } +#endif + ali1535_initialized = 0; + if ((res = ali1535_setup())) { + printk + ("i2c-ali1535.o: ALI1535 not detected, module not inserted.\n"); + ali1535_cleanup(); + return res; + } + ali1535_initialized++; + sprintf(ali1535_adapter.name, "SMBus ALI1535 adapter at %04x", + ali1535_smba); + if ((res = i2c_add_adapter(&ali1535_adapter))) { + printk + ("i2c-ali1535.o: Adapter registration failed, module not inserted.\n"); + ali1535_cleanup(); + return res; + } + ali1535_initialized++; + printk + ("i2c-ali1535.o: ALI1535 SMBus Controller detected and initialized\n"); + return 0; +} + +int __init ali1535_cleanup(void) +{ + int res; + if (ali1535_initialized >= 2) { + if ((res = i2c_del_adapter(&ali1535_adapter))) { + printk + ("i2c-ali1535.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + ali1535_initialized--; + } + if (ali1535_initialized >= 1) { + release_region(ali1535_smba, ALI1535_SMB_IOSIZE); + ali1535_initialized--; + } + return 0; +} + +#ifdef RLX +EXPORT_SYMBOL(ali1535_smba); +EXPORT_SYMBOL(ali1535_access); +EXPORT_SYMBOL(i2c_ali1535_sem); +#else +EXPORT_NO_SYMBOLS; +#endif + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , + Mark D. Studebaker and Dan Eaton "); +MODULE_DESCRIPTION("ALI1535 SMBus driver"); + +int init_module(void) +{ + return i2c_ali1535_init(); +} + +int cleanup_module(void) +{ + return ali1535_cleanup(); +} + +#endif /* MODULE */ + --- linux-old/drivers/i2c/i2c-ali15x3.c Mon Mar 25 15:28:35 CET 2002 +++ linux/drivers/i2c/i2c-ali15x3.c Mon Mar 25 15:28:35 CET 2002 @@ -0,0 +1,650 @@ +/* + ali15x3.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1999 Frodo Looijaard and + Philip Edelbrock and + Mark D. Studebaker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This is the driver for the SMB Host controller on + Acer Labs Inc. (ALI) M1541 and M1543C South Bridges. + + The M1543C is a South bridge for desktop systems. + The M1533 is a South bridge for portable systems. + They are part of the following ALI chipsets: + "Aladdin Pro 2": Includes the M1621 Slot 1 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin V": Includes the M1541 Socket 7 North bridge + with AGP and 100MHz CPU Front Side bus + "Aladdin IV": Includes the M1541 Socket 7 North bridge + with host bus up to 83.3 MHz. + For an overview of these chips see http://www.acerlabs.com + + The M1533/M1543C devices appear as FOUR separate devices + on the PCI bus. An output of lspci will show something similar + to the following: + + 00:02.0 USB Controller: Acer Laboratories Inc. M5237 + 00:03.0 Bridge: Acer Laboratories Inc. M7101 + 00:07.0 ISA bridge: Acer Laboratories Inc. M1533 + 00:0f.0 IDE interface: Acer Laboratories Inc. M5229 + + The SMB controller is part of the 7101 device, which is an + ACPI-compliant Power Management Unit (PMU). + + The whole 7101 device has to be enabled for the SMB to work. + You can't just enable the SMB alone. + The SMB and the ACPI have separate I/O spaces. + We make sure that the SMB is enabled. We leave the ACPI alone. + + This driver controls the SMB Host only. + The SMB Slave controller on the M15X3 is not enabled. + + This driver does not use interrupts. +*/ + +/* Note: we assume there can only be one ALI15X3, with one SMBus interface */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* ALI15X3 SMBus address offsets */ +#define SMBHSTSTS (0 + ali15x3_smba) +#define SMBHSTCNT (1 + ali15x3_smba) +#define SMBHSTSTART (2 + ali15x3_smba) +#define SMBHSTCMD (7 + ali15x3_smba) +#define SMBHSTADD (3 + ali15x3_smba) +#define SMBHSTDAT0 (4 + ali15x3_smba) +#define SMBHSTDAT1 (5 + ali15x3_smba) +#define SMBBLKDAT (6 + ali15x3_smba) + +/* PCI Address Constants */ +#define SMBCOM 0x004 +#define SMBBA 0x014 +#define SMBATPC 0x05B /* used to unlock xxxBA registers */ +#define SMBHSTCFG 0x0E0 +#define SMBSLVC 0x0E1 +#define SMBCLK 0x0E2 +#define SMBREV 0x008 + +/* Other settings */ +#define MAX_TIMEOUT 200 /* times 1/100 sec */ +#define ALI15X3_SMB_IOSIZE 32 + +/* this is what the Award 1004 BIOS sets them to on a ASUS P5A MB. + We don't use these here. If the bases aren't set to some value we + tell user to upgrade BIOS and we fail. +*/ +#define ALI15X3_SMB_DEFAULTBASE 0xE800 + +/* ALI15X3 address lock bits */ +#define ALI15X3_LOCK 0x06 + +/* ALI15X3 command constants */ +#define ALI15X3_ABORT 0x02 +#define ALI15X3_T_OUT 0x04 +#define ALI15X3_QUICK 0x00 +#define ALI15X3_BYTE 0x10 +#define ALI15X3_BYTE_DATA 0x20 +#define ALI15X3_WORD_DATA 0x30 +#define ALI15X3_BLOCK_DATA 0x40 +#define ALI15X3_BLOCK_CLR 0x80 + +/* ALI15X3 status register bits */ +#define ALI15X3_STS_IDLE 0x04 +#define ALI15X3_STS_BUSY 0x08 +#define ALI15X3_STS_DONE 0x10 +#define ALI15X3_STS_DEV 0x20 /* device error */ +#define ALI15X3_STS_COLL 0x40 /* collision or no response */ +#define ALI15X3_STS_TERM 0x80 /* terminated by abort */ +#define ALI15X3_STS_ERR 0xE0 /* all the bad error bits */ + + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the i2c controller"); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_ali15x3_init(void); +static int __init ali15x3_cleanup(void); +static int ali15x3_setup(void); +static s32 ali15x3_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data); +static void ali15x3_do_pause(unsigned int amount); +static int ali15x3_transaction(void); +static void ali15x3_inc(struct i2c_adapter *adapter); +static void ali15x3_dec(struct i2c_adapter *adapter); +static u32 ali15x3_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ ali15x3_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ ali15x3_func, +}; + +static struct i2c_adapter ali15x3_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI15X3, + &smbus_algorithm, + NULL, + ali15x3_inc, + ali15x3_dec, + NULL, + NULL, +}; + +static int __initdata ali15x3_initialized; +static unsigned short ali15x3_smba = 0; +static int locked=0; + +/* Detect whether a ALI15X3 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int ali15x3_setup(void) +{ + u16 a; + unsigned char temp; + + struct pci_dev *ALI15X3_dev; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) { + printk("i2c-ali15x3.o: Error: No PCI-bus found!\n"); + return -ENODEV; + } + + /* Look for the ALI15X3, M7101 device */ + ALI15X3_dev = NULL; + ALI15X3_dev = pci_find_device(PCI_VENDOR_ID_AL, + PCI_DEVICE_ID_AL_M7101, ALI15X3_dev); + if (ALI15X3_dev == NULL) { + printk("i2c-ali15x3.o: Error: Can't detect ali15x3!\n"); + return -ENODEV; + } + +/* Check the following things: + - SMB I/O address is initialized + - Device is enabled + - We can use the addresses +*/ + +/* Unlock the register. + The data sheet says that the address registers are read-only + if the lock bits are 1, but in fact the address registers + are zero unless you clear the lock bits. +*/ + pci_read_config_byte(ALI15X3_dev, SMBATPC, &temp); + if (temp & ALI15X3_LOCK) { + temp &= ~ALI15X3_LOCK; + pci_write_config_byte(ALI15X3_dev, SMBATPC, temp); + } + +/* Determine the address of the SMBus area */ + pci_read_config_word(ALI15X3_dev, SMBBA, &ali15x3_smba); + ali15x3_smba &= (0xffff & ~(ALI15X3_SMB_IOSIZE - 1)); + if (ali15x3_smba == 0 && force_addr == 0) { + printk + ("i2c-ali15x3.o: ALI15X3_smb region uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + + if(force_addr) + ali15x3_smba = force_addr & ~(ALI15X3_SMB_IOSIZE - 1); + + if (check_region(ali15x3_smba, ALI15X3_SMB_IOSIZE)) { + printk + ("i2c-ali15x3.o: ALI15X3_smb region 0x%x already in use!\n", + ali15x3_smba); + return -ENODEV; + } + + if(force_addr) { + printk("i2c-ali15x3.o: forcing ISA address 0x%04X\n", ali15x3_smba); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(ALI15X3_dev, SMBBA, ali15x3_smba)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(ALI15X3_dev, SMBBA, &a)) + return -ENODEV; + if ((a & ~(ALI15X3_SMB_IOSIZE - 1)) != ali15x3_smba) { + /* make sure it works */ + printk("i2c-ali15x3.o: force address failed - not supported?\n"); + return -ENODEV; + } + } +/* check if whole device is enabled */ + pci_read_config_byte(ALI15X3_dev, SMBCOM, &temp); + if ((temp & 1) == 0) { + printk("i2c-ali15x3: enabling SMBus device\n"); + pci_write_config_byte(ALI15X3_dev, SMBCOM, temp | 0x01); + } + +/* Is SMB Host controller enabled? */ + pci_read_config_byte(ALI15X3_dev, SMBHSTCFG, &temp); + if ((temp & 1) == 0) { + printk("i2c-ali15x3: enabling SMBus controller\n"); + pci_write_config_byte(ALI15X3_dev, SMBHSTCFG, temp | 0x01); + } + +/* set SMB clock to 74KHz as recommended in data sheet */ + pci_write_config_byte(ALI15X3_dev, SMBCLK, 0x20); + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(ali15x3_smba, ALI15X3_SMB_IOSIZE, "ali15x3-smb"); + +#ifdef DEBUG +/* + The interrupt routing for SMB is set up in register 0x77 in the + 1533 ISA Bridge device, NOT in the 7101 device. + Don't bother with finding the 1533 device and reading the register. + if ((....... & 0x0F) == 1) + printk("i2c-ali15x3.o: ALI15X3 using Interrupt 9 for SMBus.\n"); +*/ + pci_read_config_byte(ALI15X3_dev, SMBREV, &temp); + printk("i2c-ali15x3.o: SMBREV = 0x%X\n", temp); + printk("i2c-ali15x3.o: ALI15X3_smba = 0x%X\n", ali15x3_smba); +#endif /* DEBUG */ + + return 0; +} + + +/* Internally used pause function */ +void ali15x3_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int ali15x3_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Transaction (pre): STS=%02x, CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + + /* get status */ + temp = inb_p(SMBHSTSTS); + + /* Make sure the SMBus host is ready to start transmitting */ + /* Check the busy bit first */ + if (temp & ALI15X3_STS_BUSY) { +/* + If the host controller is still busy, it may have timed out in the previous transaction, + resulting in a "SMBus Timeout" printk. + I've tried the following to reset a stuck busy bit. + 1. Reset the controller with an ABORT command. + (this doesn't seem to clear the controller if an external device is hung) + 2. Reset the controller and the other SMBus devices with a T_OUT command. + (this clears the host busy bit if an external device is hung, + but it comes back upon a new access to a device) + 3. Disable and reenable the controller in SMBHSTCFG + Worst case, nothing seems to work except power reset. +*/ +/* Abort - reset the host controller */ +/* +#ifdef DEBUG + printk("i2c-ali15x3.o: Resetting host controller to clear busy condition\n",temp); +#endif + outb_p(ALI15X3_ABORT, SMBHSTCNT); + temp = inb_p(SMBHSTSTS); + if (temp & ALI15X3_STS_BUSY) { +*/ + +/* + Try resetting entire SMB bus, including other devices - + This may not work either - it clears the BUSY bit but + then the BUSY bit may come back on when you try and use the chip again. + If that's the case you are stuck. +*/ + printk + ("i2c-ali15x3.o: Resetting entire SMB Bus to clear busy condition (%02x)\n", + temp); + outb_p(ALI15X3_T_OUT, SMBHSTCNT); + temp = inb_p(SMBHSTSTS); + } +/* + } +*/ + + /* now check the error bits and the busy bit */ + if (temp & (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* do a clear-on-write */ + outb_p(0xFF, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) & + (ALI15X3_STS_ERR | ALI15X3_STS_BUSY)) { + /* this is probably going to be correctable only by a power reset + as one of the bits now appears to be stuck */ + /* This may be a bus or device with electrical problems. */ + printk + ("i2c-ali15x3.o: SMBus reset failed! (0x%02x) - controller or device on bus is probably hung\n", + temp); + return -1; + } + } else { + /* check and clear done bit */ + if (temp & ALI15X3_STS_DONE) { + outb_p(temp, SMBHSTSTS); + } + } + + /* start the transaction by writing anything to the start register */ + outb_p(0xFF, SMBHSTSTART); + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + ali15x3_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((!(temp & (ALI15X3_STS_ERR | ALI15X3_STS_DONE))) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; + printk("i2c-ali15x3.o: SMBus Timeout!\n"); + } + + if (temp & ALI15X3_STS_TERM) { + result = -1; +#ifdef DEBUG + printk("i2c-ali15x3.o: Error: Failed bus transaction\n"); +#endif + } + +/* + Unfortunately the ALI SMB controller maps "no response" and "bus collision" + into a single bit. No reponse is the usual case so don't + do a printk. + This means that bus collisions go unreported. +*/ + if (temp & ALI15X3_STS_COLL) { + result = -1; +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Error: no response or bus collision ADD=%02x\n", + inb_p(SMBHSTADD)); +#endif + } + +/* haven't ever seen this */ + if (temp & ALI15X3_STS_DEV) { + result = -1; + printk("i2c-ali15x3.o: Error: device error\n"); + } +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Transaction (post): STS=%02x, CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTSTS), inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 ali15x3_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, u8 command, + int size, union i2c_smbus_data * data) +{ + int i, len; + int temp; + int timeout; + +/* clear all the bits (clear-on-write) */ + outb_p(0xFF, SMBHSTSTS); +/* make sure SMBus is idle */ + temp = inb_p(SMBHSTSTS); + for (timeout = 0; + (timeout < MAX_TIMEOUT) && !(temp & ALI15X3_STS_IDLE); + timeout++) { + ali15x3_do_pause(1); + temp = inb_p(SMBHSTSTS); + } + if (timeout >= MAX_TIMEOUT) { + printk("i2c-ali15x3.o: Idle wait Timeout! STS=0x%02x\n", + temp); + } + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + ("i2c-ali15x3.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = ALI15X3_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = ALI15X3_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = ALI15X3_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = ALI15X3_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) { + len = 0; + data->block[0] = len; + } + if (len > 32) { + len = 32; + data->block[0] = len; + } + outb_p(len, SMBHSTDAT0); + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = ALI15X3_BLOCK_DATA; + break; + } + + outb_p(size, SMBHSTCNT); /* output command */ + + if (ali15x3_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == ALI15X3_QUICK)) + return 0; + + + switch (size) { + case ALI15X3_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case ALI15X3_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case ALI15X3_BLOCK_DATA: + len = inb_p(SMBHSTDAT0); + if (len > 32) + len = 32; + data->block[0] = len; + outb_p(inb_p(SMBHSTCNT) | ALI15X3_BLOCK_CLR, SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) { + data->block[i] = inb_p(SMBBLKDAT); +#ifdef DEBUG + printk + ("i2c-ali15x3.o: Blk: len=%d, i=%d, data=%02x\n", + len, i, data->block[i]); +#endif /* DEBUG */ + } + break; + } + return 0; +} + +void ali15x3_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void ali15x3_dec(struct i2c_adapter *adapter) +{ + + MOD_DEC_USE_COUNT; +} + +u32 ali15x3_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +int __init i2c_ali15x3_init(void) +{ + int res; + printk("i2c-ali15x3.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG +/* PE- It might be good to make this a permanent part of the code! */ + if (ali15x3_initialized) { + printk + ("i2c-ali15x3.o: Oops, ali15x3_init called a second time!\n"); + return -EBUSY; + } +#endif + ali15x3_initialized = 0; + if ((res = ali15x3_setup())) { + printk + ("i2c-ali15x3.o: ALI15X3 not detected, module not inserted.\n"); + ali15x3_cleanup(); + return res; + } + ali15x3_initialized++; + sprintf(ali15x3_adapter.name, "SMBus ALI15X3 adapter at %04x", + ali15x3_smba); + if ((res = i2c_add_adapter(&ali15x3_adapter))) { + printk + ("i2c-ali15x3.o: Adapter registration failed, module not inserted.\n"); + ali15x3_cleanup(); + return res; + } + ali15x3_initialized++; + printk + ("i2c-ali15x3.o: ALI15X3 SMBus Controller detected and initialized\n"); + return 0; +} + +int __init ali15x3_cleanup(void) +{ + int res; + if (ali15x3_initialized >= 2) { + if ((res = i2c_del_adapter(&ali15x3_adapter))) { + printk + ("i2c-ali15x3.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + ali15x3_initialized--; + } + if (ali15x3_initialized >= 1) { + release_region(ali15x3_smba, ALI15X3_SMB_IOSIZE); + ali15x3_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , and Mark D. Studebaker "); +MODULE_DESCRIPTION("ALI15X3 SMBus driver"); + +int init_module(void) +{ + return i2c_ali15x3_init(); +} + +int cleanup_module(void) +{ + return ali15x3_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/i2c/i2c-amd756.c Mon Mar 25 15:28:35 CET 2002 +++ linux/drivers/i2c/i2c-amd756.c Mon Mar 25 15:28:35 CET 2002 @@ -0,0 +1,509 @@ +/* + amd756.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + + Copyright (c) 1999 Merlin Hughes + + Shamelessly ripped from i2c-piix4.c: + + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Supports AMD756, AMD766, and AMD768. + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef PCI_DEVICE_ID_AMD_756 +#define PCI_DEVICE_ID_AMD_756 0x740B +#endif +#ifndef PCI_DEVICE_ID_AMD_766 +#define PCI_DEVICE_ID_AMD_766 0x7413 +#endif + +static int supported[] = {PCI_DEVICE_ID_AMD_756, + PCI_DEVICE_ID_AMD_766, + 0x7443, /* AMD768 */ + 0 }; + +/* AMD756 SMBus address offsets */ +#define SMB_ADDR_OFFSET 0xE0 +#define SMB_IOSIZE 16 +#define SMB_GLOBAL_STATUS (0x0 + amd756_smba) +#define SMB_GLOBAL_ENABLE (0x2 + amd756_smba) +#define SMB_HOST_ADDRESS (0x4 + amd756_smba) +#define SMB_HOST_DATA (0x6 + amd756_smba) +#define SMB_HOST_COMMAND (0x8 + amd756_smba) +#define SMB_HOST_BLOCK_DATA (0x9 + amd756_smba) +#define SMB_HAS_DATA (0xA + amd756_smba) +#define SMB_HAS_DEVICE_ADDRESS (0xC + amd756_smba) +#define SMB_HAS_HOST_ADDRESS (0xE + amd756_smba) +#define SMB_SNOOP_ADDRESS (0xF + amd756_smba) + +/* PCI Address Constants */ + +/* address of I/O space */ +#define SMBBA 0x058 /* mh */ + +/* general configuration */ +#define SMBGCFG 0x041 /* mh */ + +/* silicon revision code */ +#define SMBREV 0x008 + +/* Other settings */ +#define MAX_TIMEOUT 100 + +/* AMD756 constants */ +#define AMD756_QUICK 0x00 +#define AMD756_BYTE 0x01 +#define AMD756_BYTE_DATA 0x02 +#define AMD756_WORD_DATA 0x03 +#define AMD756_PROCESS_CALL 0x04 +#define AMD756_BLOCK_DATA 0x05 + +/* insmod parameters */ + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_amd756_init(void); +static int __init amd756_cleanup(void); +static int amd756_setup(void); +static s32 amd756_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); +static void amd756_do_pause(unsigned int amount); +static int amd756_transaction(void); +static void amd756_inc(struct i2c_adapter *adapter); +static void amd756_dec(struct i2c_adapter *adapter); +static u32 amd756_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ amd756_access, + /* slave;_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ amd756_func, +}; + +static struct i2c_adapter amd756_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD756, + &smbus_algorithm, + NULL, + amd756_inc, + amd756_dec, + NULL, + NULL, +}; + +static int __initdata amd756_initialized; +static unsigned short amd756_smba = 0; + +/* Detect whether a AMD756 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int amd756_setup(void) +{ + unsigned char temp; + int *num = supported; + struct pci_dev *AMD756_dev = NULL; + + if (pci_present() == 0) { + printk("i2c-amd756.o: Error: No PCI-bus found!\n"); + return(-ENODEV); + } + + /* Look for the AMD756, function 3 */ + /* Note: we keep on searching until we have found 'function 3' */ + do { + if((AMD756_dev = pci_find_device(PCI_VENDOR_ID_AMD, + *num, AMD756_dev))) { + if(PCI_FUNC(AMD756_dev->devfn) != 3) + continue; + break; + } + num++; + } while (*num != 0); + + if (AMD756_dev == NULL) { + printk + ("i2c-amd756.o: Error: Can't detect AMD756, function 3!\n"); + return(-ENODEV); + } + + + pci_read_config_byte(AMD756_dev, SMBGCFG, &temp); + if ((temp & 128) == 0) { + printk + ("i2c-amd756.o: Error: SMBus controller I/O not enabled!\n"); + return(-ENODEV); + } + + /* Determine the address of the SMBus areas */ + /* Technically it is a dword but... */ + pci_read_config_word(AMD756_dev, SMBBA, &amd756_smba); + amd756_smba &= 0xff00; + amd756_smba += SMB_ADDR_OFFSET; + + if (check_region(amd756_smba, SMB_IOSIZE)) { + printk + ("i2c-amd756.o: SMB region 0x%x already in use!\n", + amd756_smba); + return(-ENODEV); + } + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(amd756_smba, SMB_IOSIZE, "amd756-smbus"); + +#ifdef DEBUG + pci_read_config_byte(AMD756_dev, SMBREV, &temp); + printk("i2c-amd756.o: SMBREV = 0x%X\n", temp); + printk("i2c-amd756.o: AMD756_smba = 0x%X\n", amd756_smba); +#endif /* DEBUG */ + + return 0; +} + +/* + SMBUS event = I/O 28-29 bit 11 + see E0 for the status bits and enabled in E2 + +*/ + +/* Internally used pause function */ +void amd756_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +#define GS_ABRT_STS (1 << 0) +#define GS_COL_STS (1 << 1) +#define GS_PRERR_STS (1 << 2) +#define GS_HST_STS (1 << 3) +#define GS_HCYC_STS (1 << 4) +#define GS_TO_STS (1 << 5) +#define GS_SMB_STS (1 << 11) + +#define GS_CLEAR_STS (GS_ABRT_STS | GS_COL_STS | GS_PRERR_STS | \ + GS_HCYC_STS | GS_TO_STS ) + +#define GE_CYC_TYPE_MASK (7) +#define GE_HOST_STC (1 << 3) + +int amd756_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-amd756.o: Transaction (pre): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n", + inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE), + inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inw_p(SMB_GLOBAL_STATUS)) & (GS_HST_STS | GS_SMB_STS)) { +#ifdef DEBUG + printk + ("i2c-amd756.o: SMBus busy (%04x). Waiting... \n", temp); +#endif + do { + amd756_do_pause(1); + temp = inw_p(SMB_GLOBAL_STATUS); + } while ((temp & (GS_HST_STS | GS_SMB_STS)) && + (timeout++ < MAX_TIMEOUT)); + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + printk("i2c-amd756.o: Busy wait timeout! (%04x)\n", + temp); + return(-1); + } + timeout = 0; + } + + /* start the transaction by setting the start bit */ + outw_p(inw(SMB_GLOBAL_ENABLE) | GE_HOST_STC, SMB_GLOBAL_ENABLE); + + /* We will always wait for a fraction of a second! */ + do { + amd756_do_pause(1); + temp = inw_p(SMB_GLOBAL_STATUS); + } while ((temp & GS_HST_STS) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + printk("i2c-amd756.o: Completion timeout!\n"); + return(-1); + } + + if (temp & GS_PRERR_STS) { + result = -1; +#ifdef DEBUG + printk("i2c-amd756.o: SMBus Protocol error (no response)!\n"); +#endif + } + + if (temp & GS_COL_STS) { + result = -1; + printk("i2c-amd756.o: SMBus collision!\n"); + /* TODO: Clear Collision Status with a 1 */ + } + + if (temp & GS_TO_STS) { + result = -1; +#ifdef DEBUG + printk("i2c-amd756.o: SMBus protocol timeout!\n"); +#endif + } +#ifdef DEBUG + if (temp & GS_HCYC_STS) { + printk("i2c-amd756.o: SMBus protocol success!\n"); + } +#endif + + outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); + +#ifdef DEBUG + if (((temp = inw_p(SMB_GLOBAL_STATUS)) & GS_CLEAR_STS) != 0x00) { + printk + ("i2c-amd756.o: Failed reset at end of transaction (%04x)\n", + temp); + } + printk + ("i2c-amd756.o: Transaction (post): GS=%04x, GE=%04x, ADD=%04x, DAT=%04x\n", + inw_p(SMB_GLOBAL_STATUS), inw_p(SMB_GLOBAL_ENABLE), + inw_p(SMB_HOST_ADDRESS), inb_p(SMB_HOST_DATA)); +#endif + + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 amd756_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int i, len; + + /** TODO: Should I supporte the 10-bit transfers? */ + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + ("i2c-amd756.o: I2C_SMBUS_PROC_CALL not supported!\n"); + /* TODO: Well... It is supported, I'm just not sure what to do here... */ + return -1; + case I2C_SMBUS_QUICK: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + size = AMD756_QUICK; + break; + case I2C_SMBUS_BYTE: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + /* TODO: Why only during write? */ + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMB_HOST_COMMAND); + size = AMD756_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + outb_p(command, SMB_HOST_COMMAND); + if (read_write == I2C_SMBUS_WRITE) + outw_p(data->byte, SMB_HOST_DATA); + size = AMD756_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + outb_p(command, SMB_HOST_COMMAND); + if (read_write == I2C_SMBUS_WRITE) + outw_p(data->word, SMB_HOST_DATA); /* TODO: endian???? */ + size = AMD756_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outw_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMB_HOST_ADDRESS); + outb_p(command, SMB_HOST_COMMAND); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + if (len > 32) + len = 32; + outw_p(len, SMB_HOST_DATA); + /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], + SMB_HOST_BLOCK_DATA); + } + size = AMD756_BLOCK_DATA; + break; + } + + /* How about enabling interrupts... */ + outw_p(size & GE_CYC_TYPE_MASK, SMB_GLOBAL_ENABLE); + + if (amd756_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == AMD756_QUICK)) + return 0; + + + switch (size) { + case AMD756_BYTE: + data->byte = inw_p(SMB_HOST_DATA); + break; + case AMD756_BYTE_DATA: + data->byte = inw_p(SMB_HOST_DATA); + break; + case AMD756_WORD_DATA: + data->word = inw_p(SMB_HOST_DATA); /* TODO: endian???? */ + break; + case AMD756_BLOCK_DATA: + data->block[0] = inw_p(SMB_HOST_DATA & 63); + /* i = inw_p(SMBHSTCNT); Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMB_HOST_BLOCK_DATA); + break; + } + + return 0; +} + +void amd756_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void amd756_dec(struct i2c_adapter *adapter) +{ + + MOD_DEC_USE_COUNT; +} + +u32 amd756_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL; +} + +int __init i2c_amd756_init(void) +{ + int res; + printk("i2c-amd756.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG +/* PE- It might be good to make this a permanent part of the code! */ + if (amd756_initialized) { + printk + ("i2c-amd756.o: Oops, amd756_init called a second time!\n"); + return -EBUSY; + } +#endif + amd756_initialized = 0; + if ((res = amd756_setup())) { + printk + ("i2c-amd756.o: AMD756/766 not detected, module not inserted.\n"); + amd756_cleanup(); + return res; + } + amd756_initialized++; + sprintf(amd756_adapter.name, "SMBus AMD7X6 adapter at %04x", + amd756_smba); + if ((res = i2c_add_adapter(&amd756_adapter))) { + printk + ("i2c-amd756.o: Adapter registration failed, module not inserted.\n"); + amd756_cleanup(); + return res; + } + amd756_initialized++; + printk("i2c-amd756.o: AMD756/766 bus detected and initialized\n"); + return 0; +} + +int __init amd756_cleanup(void) +{ + int res; + if (amd756_initialized >= 2) { + if ((res = i2c_del_adapter(&amd756_adapter))) { + printk + ("i2c-amd756.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + amd756_initialized--; + } + if (amd756_initialized >= 1) { + release_region(amd756_smba, SMB_IOSIZE); + amd756_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Merlin Hughes "); +MODULE_DESCRIPTION("AMD756/766 SMBus driver"); + +int init_module(void) +{ + return i2c_amd756_init(); +} + +int cleanup_module(void) +{ + return amd756_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/i2c/i2c-hydra.c Mon Mar 25 15:28:35 CET 2002 +++ linux/drivers/i2c/i2c-hydra.c Mon Mar 25 15:28:35 CET 2002 @@ -0,0 +1,206 @@ +/* + 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 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 +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* PCI device */ +#define VENDOR PCI_VENDOR_ID_APPLE +#define DEVICE PCI_DEVICE_ID_APPLE_HYDRA + +#define HYDRA_CACHE_PD 0x00000030 + +#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 unsigned long hydra_base; + +static inline void pdregw(u32 val) +{ + writel(val, hydra_base + HYDRA_CACHE_PD); +} + +static inline u32 pdregr(void) +{ + u32 val = readl(hydra_base + HYDRA_CACHE_PD); + return val; +} + +static void bit_hydra_setscl(void *data, int state) +{ + u32 val = pdregr(); + if (state) + val &= ~HYDRA_SCLK_OE; + else { + val &= ~HYDRA_SCLK; + val |= HYDRA_SCLK_OE; + } + pdregw(val); +} + +static void bit_hydra_setsda(void *data, int state) +{ + u32 val = pdregr(); + if (state) + val &= ~HYDRA_SDAT_OE; + else { + val &= ~HYDRA_SDAT; + val |= HYDRA_SDAT_OE; + } + pdregw(val); +} + +static int bit_hydra_getscl(void *data) +{ + return (pdregr() & HYDRA_SCLK) != 0; +} + +static int bit_hydra_getsda(void *data) +{ + return (pdregr() & HYDRA_SDAT) != 0; +} + +static void bit_hydra_inc(struct i2c_adapter *adap) +{ + MOD_INC_USE_COUNT; +} + +static void bit_hydra_dec(struct i2c_adapter *adap) +{ + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------------------ */ + +static struct i2c_algo_bit_data bit_hydra_data = { + NULL, + bit_hydra_setsda, + bit_hydra_setscl, + bit_hydra_getsda, + bit_hydra_getscl, + 5, 5, 100, /*waits, timeout */ +}; + +static struct i2c_adapter bit_hydra_ops = { + "Hydra i2c", + I2C_HW_B_HYDRA, + NULL, + &bit_hydra_data, + bit_hydra_inc, + bit_hydra_dec, + NULL, + NULL, +}; + + +static int find_hydra(void) +{ + struct pci_dev *dev; + unsigned int base_addr; + + if (!pci_present()) + return -ENODEV; + + dev = pci_find_device(VENDOR, DEVICE, NULL); + if (!dev) { + printk("Hydra not found\n"); + return -ENODEV; + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13) + base_addr = dev->resource[0].start; +#else + base_addr = dev->base_address[0]; +#endif + hydra_base = (unsigned long) ioremap(base_addr, 0x100); + + return 0; +} + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_hydra_init(void) +{ + if (find_hydra() < 0) { + printk("Error while reading PCI configuration\n"); + return -ENODEV; + } + + pdregw(0); /* clear SCLK_OE and SDAT_OE */ + + if (i2c_bit_add_bus(&bit_hydra_ops) == 0) { + printk("Hydra i2c: Module succesfully loaded\n"); + return 0; + } else { + iounmap((void *) hydra_base); + printk + ("Hydra i2c: Algo-bit error, couldn't register bus\n"); + return -ENODEV; + } +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Geert Uytterhoeven "); +MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O"); + +int init_module(void) +{ + return i2c_hydra_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_hydra_ops); + if (hydra_base) { + pdregw(0); /* clear SCLK_OE and SDAT_OE */ + iounmap((void *) hydra_base); + } +} +#endif --- linux-old/drivers/i2c/i2c-i801.c Mon Mar 25 15:28:35 CET 2002 +++ linux/drivers/i2c/i2c-i801.c Mon Mar 25 15:28:35 CET 2002 @@ -0,0 +1,710 @@ +/* + i801.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2001 Frodo Looijaard , + Philip Edelbrock , and Mark D. Studebaker + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + SUPPORTED DEVICES PCI ID + 82801AA 2413 + 82801AB 2423 + 82801BA 2443 + 82801CA/CAM 2483 + + This driver supports several versions of Intel's I/O Controller Hubs (ICH). + For SMBus support, they are similar to the PIIX4 and are part + of Intel's '810' and other chipsets. + See the doc/busses/i2c-i801 file for details. +*/ + +/* Note: we assume there can only be one I801, with one SMBus interface */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef PCI_DEVICE_ID_INTEL_82801AA_3 +#define PCI_DEVICE_ID_INTEL_82801AA_3 0x2413 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82801AB_3 +#define PCI_DEVICE_ID_INTEL_82801AB_3 0x2423 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82801BA_2 +#define PCI_DEVICE_ID_INTEL_82801BA_2 0x2443 +#endif +#define PCI_DEVICE_ID_INTEL_82801CA_SMBUS 0x2483 + +static int supported[] = {PCI_DEVICE_ID_INTEL_82801AA_3, + PCI_DEVICE_ID_INTEL_82801AB_3, + PCI_DEVICE_ID_INTEL_82801BA_2, + PCI_DEVICE_ID_INTEL_82801CA_SMBUS, + 0 }; + +/* I801 SMBus address offsets */ +#define SMBHSTSTS (0 + i801_smba) +#define SMBHSTCNT (2 + i801_smba) +#define SMBHSTCMD (3 + i801_smba) +#define SMBHSTADD (4 + i801_smba) +#define SMBHSTDAT0 (5 + i801_smba) +#define SMBHSTDAT1 (6 + i801_smba) +#define SMBBLKDAT (7 + i801_smba) + +/* PCI Address Constants */ +#define SMBBA 0x020 +#define SMBHSTCFG 0x040 +#define SMBREV 0x008 + +/* Host configuration bits for SMBHSTCFG */ +#define SMBHSTCFG_HST_EN 1 +#define SMBHSTCFG_SMB_SMI_EN 2 +#define SMBHSTCFG_I2C_EN 4 + +/* Other settings */ +#define MAX_TIMEOUT 500 +#define ENABLE_INT9 0 + +/* I801 command constants */ +#define I801_QUICK 0x00 +#define I801_BYTE 0x04 +#define I801_BYTE_DATA 0x08 +#define I801_WORD_DATA 0x0C +#define I801_BLOCK_DATA 0x14 +#define I801_I2C_BLOCK_DATA 0x18 /* unimplemented */ +#define I801_BLOCK_LAST 0x34 +#define I801_I2C_BLOCK_LAST 0x38 /* unimplemented */ + +/* insmod parameters */ + +/* If force is set to anything different from 0, we forcibly enable the + I801. DANGEROUS! */ +static int force = 0; +MODULE_PARM(force, "i"); +MODULE_PARM_DESC(force, "Forcibly enable the I801. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the I801 at the given address. VERY DANGEROUS! */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the I801 at the given address. " + "EXTREMELY DANGEROUS!"); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_i801_init(void); +static int __init i801_cleanup(void); +static int i801_setup(void); +static s32 i801_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); +static void i801_do_pause(unsigned int amount); +static int i801_transaction(void); +static int i801_block_transaction(union i2c_smbus_data *data, + char read_write, int i2c_enable); +static void i801_inc(struct i2c_adapter *adapter); +static void i801_dec(struct i2c_adapter *adapter); +static u32 i801_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_xfer */ i801_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ i801_func, +}; + +static struct i2c_adapter i801_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_I801, + &smbus_algorithm, + NULL, + i801_inc, + i801_dec, + NULL, + NULL, +}; + +static int __initdata i801_initialized; +static unsigned short i801_smba = 0; +static struct pci_dev *I801_dev = NULL; + + +/* Detect whether a I801 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int i801_setup(void) +{ + int error_return = 0; + int *num = supported; + unsigned char temp; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) { + printk("i2c-i801.o: Error: No PCI-bus found!\n"); + error_return = -ENODEV; + goto END; + } + + /* Look for each chip */ + /* Note: we keep on searching until we have found 'function 3' */ + I801_dev = NULL; + do { + if((I801_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + *num, I801_dev))) { + if(PCI_FUNC(I801_dev->devfn) != 3) + continue; + break; + } + num++; + } while (*num != 0); + + if (I801_dev == NULL) { + printk + ("i2c-i801.o: Error: Can't detect I801, function 3!\n"); + error_return = -ENODEV; + goto END; + } + +/* Determine the address of the SMBus areas */ + if (force_addr) { + i801_smba = force_addr & 0xfff0; + force = 0; + } else { + pci_read_config_word(I801_dev, SMBBA, &i801_smba); + i801_smba &= 0xfff0; + } + + if (check_region(i801_smba, 8)) { + printk + ("i2c-i801.o: I801_smb region 0x%x already in use!\n", + i801_smba); + error_return = -ENODEV; + goto END; + } + + pci_read_config_byte(I801_dev, SMBHSTCFG, &temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the I801 first. */ + if (force_addr) { + pci_write_config_byte(I801_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(I801_dev, SMBBA, i801_smba); + pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 0x01); + printk + ("i2c-i801.o: WARNING: I801 SMBus interface set to new " + "address %04x!\n", i801_smba); + } else if ((temp & 1) == 0) { + if (force) { +/* This should never need to be done, but has been noted that + many Dell machines have the SMBus interface on the PIIX4 + disabled!? NOTE: This assumes I/O space and other allocations WERE + done by the Bios! Don't complain if your hardware does weird + things after enabling this. :') Check for Bios updates before + resorting to this. */ + pci_write_config_byte(I801_dev, SMBHSTCFG, + temp | 1); + printk + ("i2c-i801.o: WARNING: I801 SMBus interface has been FORCEFULLY " + "ENABLED!\n"); + } else { + printk + ("SMBUS: Error: Host SMBus controller not enabled!\n"); + error_return = -ENODEV; + goto END; + } + } + + /* note: we assumed that the BIOS picked SMBus or I2C Bus timing + appropriately (bit 2 in SMBHSTCFG) */ + /* Everything is happy, let's grab the memory and set things up. */ + request_region(i801_smba, 8, "i801-smbus"); + +#ifdef DEBUG + if (temp & 0x02) + printk + ("i2c-i801.o: I801 using Interrupt SMI# for SMBus.\n"); + else + printk + ("i2c-i801.o: I801 using PCI Interrupt for SMBus.\n"); + + pci_read_config_byte(I801_dev, SMBREV, &temp); + printk("i2c-i801.o: SMBREV = 0x%X\n", temp); + printk("i2c-i801.o: I801_smba = 0x%X\n", i801_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +/* Internally used pause function */ +void i801_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int i801_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-i801.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + /* 0x1f = Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk("i2c-i801.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk("i2c-i801.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk("i2c-i801.o: Successfull!\n"); +#endif + } + } + + /* start the transaction by setting bit 6 */ + outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! */ + do { + i801_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { +#ifdef DEBUG + printk("i2c-i801.o: SMBus Timeout!\n"); + result = -1; +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk("i2c-i801.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + ("i2c-i801.o: Bus collision! SMBus may be locked until next hard\n" + "reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk("i2c-i801.o: Error: no response!\n"); +#endif + } + + if ((inb_p(SMBHSTSTS) & 0x1f) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk + ("i2c-i801.o: Failed reset at end of transaction (%02x)\n", + temp); +#endif + } +#ifdef DEBUG + printk + ("i2c-i801.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* All-inclusive block transaction function */ +int i801_block_transaction(union i2c_smbus_data *data, char read_write, + int i2c_enable) +{ + int i, len; + int smbcmd; + int temp; + int result = 0; + int timeout = 0; + unsigned char hostc, errmask; + + if (i2c_enable) { + if (read_write == I2C_SMBUS_WRITE) { + /* set I2C_EN bit in configuration register */ + pci_read_config_byte(I801_dev, SMBHSTCFG, &hostc); + pci_write_config_byte(I801_dev, SMBHSTCFG, + hostc | SMBHSTCFG_I2C_EN); + } else { + printk("i2c-i801.o: " + "I2C_SMBUS_I2C_BLOCK_READ not supported!\n"); + return -1; + } + } + + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 1) + len = 1; + if (len > 32) + len = 32; + outb_p(len, SMBHSTDAT0); + outb_p(data->block[1], SMBBLKDAT); + } else { + len = 32; /* max for reads */ + } + + for (i = 1; i <= len; i++) { + if (i == len && read_write == I2C_SMBUS_READ) + smbcmd = I801_BLOCK_LAST; + else + smbcmd = I801_BLOCK_DATA; + + outb_p((smbcmd & 0x3C) + (ENABLE_INT9 & 1), SMBHSTCNT); + +#ifdef DEBUG + printk + ("i2c-i801.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, BLKDAT=%02x\n", inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBBLKDAT)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + temp = inb_p(SMBHSTSTS); + if (i == 1) { + /* Erronenous conditions before transaction: + * Byte_Done, Failed, Bus_Err, Dev_Err, Intr, Host_Busy */ + errmask=0x9f; + } else { + /* Erronenous conditions during transaction: + * Failed, Bus_Err, Dev_Err, Intr */ + errmask=0x1e; + } + if (temp & errmask) { +#ifdef DEBUG + printk + ("i2c-i801.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) { + printk + ("i2c-i801.o: Reset failed! (%02x)\n", + temp); + result = -1; + goto END; + } + if (i != 1) { + result = -1; /* if die in middle of block transaction, fail */ + goto END; + } + } + + /* start the transaction by setting bit 6 */ + if (i==1) outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! */ + do { + temp = inb_p(SMBHSTSTS); + i801_do_pause(1); + } + while ( + (((i >= len) && (temp & 0x01)) + || ((i < len) && !(temp & 0x80))) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; +#ifdef DEBUG + printk("i2c-i801.o: SMBus Timeout!\n"); +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk + ("i2c-i801.o: Error: Failed bus transaction\n"); +#endif + } else if (temp & 0x08) { + result = -1; + printk + ("i2c-i801.o: Bus collision! SMBus may be locked until next hard" + " reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } else if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk("i2c-i801.o: Error: no response!\n"); +#endif + } + + if (i == 1 && read_write == I2C_SMBUS_READ) { + len = inb_p(SMBHSTDAT0); + if (len < 1) + len = 1; + if (len > 32) + len = 32; + data->block[0] = len; + } + + /* Retrieve/store value in SMBBLKDAT */ + if (read_write == I2C_SMBUS_READ) + data->block[i] = inb_p(SMBBLKDAT); + if (read_write == I2C_SMBUS_WRITE && i+1 <= len) + outb_p(data->block[i+1], SMBBLKDAT); + if ((temp & 0x9e) != 0x00) + outb_p(temp, SMBHSTSTS); /* signals SMBBLKDAT ready */ + + temp = inb_p(SMBHSTSTS); + if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk + ("i2c-i801.o: Failed reset at end of transaction (%02x)\n", + temp); +#endif + } +#ifdef DEBUG + printk + ("i2c-i801.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, BLKDAT=%02x\n", inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBBLKDAT)); +#endif + + if (result < 0) { + goto END; + } + } + result = 0; +END: + if (i2c_enable) { + /* restore saved configuration register value */ + pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); + } + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 i801_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk("i2c-i801.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = I801_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = I801_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = I801_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = I801_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + /* Block transactions are very different from piix4 block + and from the other i801 transactions. Handle in the + i801_block_transaction() routine. */ + return i801_block_transaction(data, read_write, + size==I2C_SMBUS_I2C_BLOCK_DATA); + } + + /* 'size' is really the transaction type */ + outb_p((size & 0x3C) + (ENABLE_INT9 & 1), SMBHSTCNT); + + if (i801_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == I801_QUICK)) + return 0; + + + switch (size) { + case I801_BYTE: /* Result put in SMBHSTDAT0 */ + data->byte = inb_p(SMBHSTDAT0); + break; + case I801_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case I801_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + } + return 0; +} + +void i801_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void i801_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +u32 i801_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK; +} + +int __init i2c_i801_init(void) +{ + int res; + printk("i2c-i801.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG +/* PE- It might be good to make this a permanent part of the code! */ + if (i801_initialized) { + printk + ("i2c-i801.o: Oops, i801_init called a second time!\n"); + return -EBUSY; + } +#endif + i801_initialized = 0; + if ((res = i801_setup())) { + printk + ("i2c-i801.o: I801 not detected, module not inserted.\n"); + i801_cleanup(); + return res; + } + i801_initialized++; + sprintf(i801_adapter.name, "SMBus I801 adapter at %04x", + i801_smba); + if ((res = i2c_add_adapter(&i801_adapter))) { + printk + ("i2c-i801.o: Adapter registration failed, module not inserted.\n"); + i801_cleanup(); + return res; + } + i801_initialized++; + printk("i2c-i801.o: I801 bus detected and initialized\n"); + return 0; +} + +int __init i801_cleanup(void) +{ + int res; + if (i801_initialized >= 2) { + if ((res = i2c_del_adapter(&i801_adapter))) { + printk + ("i2c-i801.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + i801_initialized--; + } + if (i801_initialized >= 1) { + release_region(i801_smba, 8); + i801_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , and Mark D. Studebaker "); +MODULE_DESCRIPTION("I801 SMBus driver"); + +int init_module(void) +{ + return i2c_i801_init(); +} + +int cleanup_module(void) +{ + return i801_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/i2c/i2c-i810.c Mon Mar 25 15:28:36 CET 2002 +++ linux/drivers/i2c/i2c-i810.c Mon Mar 25 15:28:36 CET 2002 @@ -0,0 +1,350 @@ +/* + i2c-i810.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999, 2000 Frodo Looijaard , + Philip Edelbrock , + Ralph Metzler , and + Mark D. Studebaker + + Based on code written by Ralph Metzler and + Simon Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ +/* + This interfaces to the I810/I815 to provide access to + the DDC Bus and the I2C Bus. + + SUPPORTED DEVICES PCI ID + i810AA 7121 + i810AB 7123 + i810E 7125 + i815 1132 +*/ + + +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* PCI defines */ +#ifndef PCI_DEVICE_ID_INTEL_82810_IG1 +#define PCI_DEVICE_ID_INTEL_82810_IG1 0x7121 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82810_IG3 +#define PCI_DEVICE_ID_INTEL_82810_IG3 0x7123 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82815_2 +#define PCI_DEVICE_ID_INTEL_82815_2 0x1132 +#endif + +static int i810_supported[] = {PCI_DEVICE_ID_INTEL_82810_IG1, + PCI_DEVICE_ID_INTEL_82810_IG3, + 0x7125, + PCI_DEVICE_ID_INTEL_82815_2, + 0 }; + +/* GPIO register locations */ +#define I810_IOCONTROL_OFFSET 0x5000 +#define I810_HVSYNC 0x00 /* not used */ +#define I810_GPIOA 0x10 +#define I810_GPIOB 0x14 + +/* bit locations in the registers */ +#define SCL_DIR_MASK 0x0001 +#define SCL_DIR 0x0002 +#define SCL_VAL_MASK 0x0004 +#define SCL_VAL_OUT 0x0008 +#define SCL_VAL_IN 0x0010 +#define SDA_DIR_MASK 0x0100 +#define SDA_DIR 0x0200 +#define SDA_VAL_MASK 0x0400 +#define SDA_VAL_OUT 0x0800 +#define SDA_VAL_IN 0x1000 + +/* initialization states */ +#define INIT1 0x1 +#define INIT2 0x2 +#define INIT3 0x4 + +/* delays */ +#define CYCLE_DELAY 10 +#define TIMEOUT 50 + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_i810_init(void); +static int __init i810i2c_cleanup(void); +static int i810i2c_setup(void); +static void config_i810(struct pci_dev *dev); +static void i810_inc(struct i2c_adapter *adapter); +static void i810_dec(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static int __initdata i810i2c_initialized; +static unsigned char *mem; + +static inline void outlong(unsigned int dat, int off) +{ + *((unsigned int *) (mem + off)) = dat; +} + +static inline unsigned int readlong(int off) +{ + return *((unsigned int *) (mem + off)); +} + +/* The i810 GPIO registers have individual masks for each bit + so we never have to read before writing. Nice. */ + +static void bit_i810i2c_setscl(void *data, int val) +{ + outlong((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK, + I810_GPIOB); +} + +static void bit_i810i2c_setsda(void *data, int val) +{ + outlong((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK, + I810_GPIOB); +} + +/* The GPIO pins are open drain, so the pins always remain outputs. + We rely on the i2c-algo-bit routines to set the pins high before + reading the input from other chips. Following guidance in the 815 + prog. ref. guide, we do a "dummy write" of 0 to the register before + reading which forces the input value to be latched. We presume this + applies to the 810 as well. This is necessary to get + i2c_algo_bit bit_test=1 to pass. */ + +static int bit_i810i2c_getscl(void *data) +{ + outlong(0, I810_GPIOB); + return (0 != (readlong(I810_GPIOB) & SCL_VAL_IN)); +} + +static int bit_i810i2c_getsda(void *data) +{ + outlong(0, I810_GPIOB); + return (0 != (readlong(I810_GPIOB) & SDA_VAL_IN)); +} + +static void bit_i810ddc_setscl(void *data, int val) +{ + outlong((val ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK, + I810_GPIOA); +} + +static void bit_i810ddc_setsda(void *data, int val) +{ + outlong((val ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK, + I810_GPIOA); +} + +static int bit_i810ddc_getscl(void *data) +{ + outlong(0, I810_GPIOA); + return (0 != (readlong(I810_GPIOA) & SCL_VAL_IN)); +} + +static int bit_i810ddc_getsda(void *data) +{ + outlong(0, I810_GPIOA); + return (0 != (readlong(I810_GPIOA) & SDA_VAL_IN)); +} + +static struct i2c_algo_bit_data i810_i2c_bit_data = { + NULL, + bit_i810i2c_setsda, + bit_i810i2c_setscl, + bit_i810i2c_getsda, + bit_i810i2c_getscl, + CYCLE_DELAY, CYCLE_DELAY, TIMEOUT +}; + +static struct i2c_adapter i810_i2c_adapter = { + "I810/I815 I2C Adapter", + I2C_HW_B_I810, + NULL, + &i810_i2c_bit_data, + i810_inc, + i810_dec, + NULL, + NULL, +}; + +static struct i2c_algo_bit_data i810_ddc_bit_data = { + NULL, + bit_i810ddc_setsda, + bit_i810ddc_setscl, + bit_i810ddc_getsda, + bit_i810ddc_getscl, + CYCLE_DELAY, CYCLE_DELAY, TIMEOUT +}; + +static struct i2c_adapter i810_ddc_adapter = { + "I810/I815 DDC Adapter", + I2C_HW_B_I810, + NULL, + &i810_ddc_bit_data, + i810_inc, + i810_dec, + NULL, + NULL, +}; + + +/* Configures the chip */ +void config_i810(struct pci_dev *dev) +{ + unsigned long cadr; + + /* map I810 memory */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13) + cadr = dev->resource[1].start; +#else + cadr = dev->base_address[1]; +#endif + cadr += I810_IOCONTROL_OFFSET; + cadr &= PCI_BASE_ADDRESS_MEM_MASK; + mem = ioremap_nocache(cadr, 0x1000); + bit_i810i2c_setscl(NULL, 1); + bit_i810i2c_setsda(NULL, 1); + bit_i810ddc_setscl(NULL, 1); + bit_i810ddc_setsda(NULL, 1); +} + +/* Detect whether a supported device can be found, + and initialize it */ +static int i810i2c_setup(void) +{ + struct pci_dev *dev = NULL; + int *num = i810_supported; + + do { + if ((dev = pci_find_device(PCI_VENDOR_ID_INTEL, + *num++, dev))) { + config_i810(dev); + printk("i2c-i810.o: i810/i815 found.\n"); + return 0; + } + } while (*num != 0); + + return -ENODEV; +} + + +void i810_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void i810_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +int __init i2c_i810_init(void) +{ + int res; + printk("i2c-i810.o version %s (%s)\n", LM_VERSION, LM_DATE); + + i810i2c_initialized = 0; + if ((res = i810i2c_setup())) { + printk + ("i2c-i810.o: i810/i815 not detected, module not inserted.\n"); + i810i2c_cleanup(); + return res; + } + if ((res = i2c_bit_add_bus(&i810_i2c_adapter))) { + printk("i2c-i810.o: I2C adapter registration failed\n"); + } else { + printk("i2c-i810.o: I810/I815 I2C bus initialized\n"); + i810i2c_initialized |= INIT2; + } + if ((res = i2c_bit_add_bus(&i810_ddc_adapter))) { + printk("i2c-i810.o: DDC adapter registration failed\n"); + } else { + printk("i2c-i810.o: I810/I815 DDC bus initialized\n"); + i810i2c_initialized |= INIT3; + } + if(!(i810i2c_initialized & (INIT2 | INIT3))) { + printk("i2c-i810.o: Both registrations failed, module not inserted\n"); + i810i2c_cleanup(); + return res; + } + return 0; +} + +int __init i810i2c_cleanup(void) +{ + int res; + + iounmap(mem); + if (i810i2c_initialized & INIT3) { + if ((res = i2c_bit_del_bus(&i810_ddc_adapter))) { + printk + ("i2c-i810.o: i2c_del_adapter failed, module not removed\n"); + return res; + } + } + if (i810i2c_initialized & INIT2) { + if ((res = i2c_bit_del_bus(&i810_i2c_adapter))) { + printk + ("i2c-i810.o: i2c_del_adapter failed, module not removed\n"); + return res; + } + } + i810i2c_initialized = 0; + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , Ralph Metzler , and Mark D. Studebaker "); +MODULE_DESCRIPTION("I810/I815 I2C/DDC driver"); + + +int init_module(void) +{ + return i2c_i810_init(); +} + +int cleanup_module(void) +{ + return i810i2c_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/i2c/i2c-isa.c Mon Mar 25 15:28:36 CET 2002 +++ linux/drivers/i2c/i2c-isa.c Mon Mar 25 15:28:36 CET 2002 @@ -0,0 +1,157 @@ +/* + i2c-isa.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* This implements an i2c algorithm/adapter for ISA bus. Not that this is + on first sight very useful; almost no functionality is preserved. + Except that it makes writing drivers for chips which can be on both + the SMBus and the ISA bus very much easier. See lm78.c for an example + of this. */ + +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +static void isa_inc_use(struct i2c_adapter *adapter); +static void isa_dec_use(struct i2c_adapter *adapter); +static u32 isa_func(struct i2c_adapter *adapter); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_isa_init(void); +static int __init isa_cleanup(void); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* This is the actual algorithm we define */ +static struct i2c_algorithm isa_algorithm = { + /* name */ "ISA bus algorithm", + /* id */ I2C_ALGO_ISA, + /* master_xfer */ NULL, + /* smbus_access */ NULL, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ &isa_func, +}; + +/* There can only be one... */ +static struct i2c_adapter isa_adapter = { + /* name */ "ISA main adapter", + /* id */ I2C_ALGO_ISA | I2C_HW_ISA, + /* algorithm */ &isa_algorithm, + /* algo_data */ NULL, + /* inc_use */ &isa_inc_use, + /* dec_use */ &isa_dec_use, + /* data */ NULL, + /* Other fields not initialized */ +}; + +/* Used in isa_init/cleanup */ +static int __initdata isa_initialized; + +void isa_inc_use(struct i2c_adapter *adapter) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void isa_dec_use(struct i2c_adapter *adapter) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* We can't do a thing... */ +static u32 isa_func(struct i2c_adapter *adapter) +{ + return 0; +} + +int __init i2c_isa_init(void) +{ + int res; + printk("i2c-isa.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG + if (isa_initialized) { + printk + ("i2c-isa.o: Oops, isa_init called a second time!\n"); + return -EBUSY; + } +#endif + isa_initialized = 0; + if ((res = i2c_add_adapter(&isa_adapter))) { + printk("i2c-isa.o: Adapter registration failed, " + "module i2c-isa.o is not inserted\n."); + isa_cleanup(); + return res; + } + isa_initialized++; + printk("i2c-isa.o: ISA bus access for i2c modules initialized.\n"); + return 0; +} + +int __init isa_cleanup(void) +{ + int res; + if (isa_initialized >= 1) { + if ((res = i2c_del_adapter(&isa_adapter))) { + printk + ("i2c-isa.o: Adapter deregistration failed, module not removed.\n"); + return res; + } else + isa_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("ISA bus access through i2c"); + +int init_module(void) +{ + return i2c_isa_init(); +} + +int cleanup_module(void) +{ + return isa_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/i2c/i2c-piix4.c Mon Mar 25 15:28:36 CET 2002 +++ linux/drivers/i2c/i2c-piix4.c Mon Mar 25 15:28:36 CET 2002 @@ -0,0 +1,573 @@ +/* + piix4.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Supports: + Intel PIIX4, 440MX + Serverworks OSB4, CSB5 + SMSC Victory66 + + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +/* Note: We assume all devices are identical + to the Intel PIIX4; we only mention it during detection. */ + +#ifndef PCI_DEVICE_ID_SERVERWORKS_OSB4 +#define PCI_DEVICE_ID_SERVERWORKS_OSB4 0x0200 +#endif + +#ifndef PCI_DEVICE_ID_SERVERWORKS_CSB5 +#define PCI_DEVICE_ID_SERVERWORKS_CSB5 0x0201 +#endif + +#ifndef PCI_VENDOR_ID_SERVERWORKS +#define PCI_VENDOR_ID_SERVERWORKS 0x01166 +#endif + +#ifndef PCI_DEVICE_ID_INTEL_82443MX_3 +#define PCI_DEVICE_ID_INTEL_82443MX_3 0x719b +#endif + +#ifndef PCI_VENDOR_ID_EFAR +#define PCI_VENDOR_ID_EFAR 0x1055 +#endif + +#ifndef PCI_DEVICE_ID_EFAR_SLC90E66_3 +#define PCI_DEVICE_ID_EFAR_SLC90E66_3 0x9463 +#endif + +struct sd { + const unsigned short mfr; + const unsigned short dev; + const unsigned char fn; + const char *name; +}; + +static struct sd supported[] = { + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, 3, "PIIX4"}, + {PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4, 0, "OSB4"}, + {PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5, 0, "CSB5"}, + {PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3, 3, "440MX"}, + {PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_3, 0, "Victory66"}, + {0, 0, 0, NULL} +}; + +/* PIIX4 SMBus address offsets */ +#define SMBHSTSTS (0 + piix4_smba) +#define SMBHSLVSTS (1 + piix4_smba) +#define SMBHSTCNT (2 + piix4_smba) +#define SMBHSTCMD (3 + piix4_smba) +#define SMBHSTADD (4 + piix4_smba) +#define SMBHSTDAT0 (5 + piix4_smba) +#define SMBHSTDAT1 (6 + piix4_smba) +#define SMBBLKDAT (7 + piix4_smba) +#define SMBSLVCNT (8 + piix4_smba) +#define SMBSHDWCMD (9 + piix4_smba) +#define SMBSLVEVT (0xA + piix4_smba) +#define SMBSLVDAT (0xC + piix4_smba) + +/* PCI Address Constants */ +#define SMBBA 0x090 +#define SMBHSTCFG 0x0D2 +#define SMBSLVC 0x0D3 +#define SMBSHDW1 0x0D4 +#define SMBSHDW2 0x0D5 +#define SMBREV 0x0D6 + +/* Other settings */ +#define MAX_TIMEOUT 500 +#define ENABLE_INT9 0 + +/* PIIX4 constants */ +#define PIIX4_QUICK 0x00 +#define PIIX4_BYTE 0x04 +#define PIIX4_BYTE_DATA 0x08 +#define PIIX4_WORD_DATA 0x0C +#define PIIX4_BLOCK_DATA 0x14 + +/* insmod parameters */ + +/* If force is set to anything different from 0, we forcibly enable the + PIIX4. DANGEROUS! */ +static int force = 0; +MODULE_PARM(force, "i"); +MODULE_PARM_DESC(force, "Forcibly enable the PIIX4. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the PIIX4 at the given address. VERY DANGEROUS! */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the PIIX4 at the given address. " + "EXTREMELY DANGEROUS!"); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_piix4_init(void); +static int __init piix4_cleanup(void); +static int piix4_setup(void); +static s32 piix4_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); +static void piix4_do_pause(unsigned int amount); +static int piix4_transaction(void); +static void piix4_inc(struct i2c_adapter *adapter); +static void piix4_dec(struct i2c_adapter *adapter); +static u32 piix4_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ piix4_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ piix4_func, +}; + +static struct i2c_adapter piix4_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_PIIX4, + &smbus_algorithm, + NULL, + piix4_inc, + piix4_dec, + NULL, + NULL, +}; + +static int __initdata piix4_initialized; +static unsigned short piix4_smba = 0; + +/* Detect whether a PIIX4 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int piix4_setup(void) +{ + int error_return = 0; + unsigned char temp; + struct sd *num = supported; + struct pci_dev *PIIX4_dev = NULL; + + if (pci_present() == 0) { + error_return = -ENODEV; + goto END; + } + + /* Look for a supported device/function */ + do { + if((PIIX4_dev = pci_find_device(num->mfr, num->dev, + PIIX4_dev))) { + if(PCI_FUNC(PIIX4_dev->devfn) != num->fn) + continue; + break; + } + PIIX4_dev = NULL; + num++; + } while (num->mfr); + + if (PIIX4_dev == NULL) { + printk + (KERN_ERR "i2c-piix4.o: Error: Can't detect PIIX4 or compatible device!\n"); + error_return = -ENODEV; + goto END; + } + printk(KERN_INFO "i2c-piix4.o: Found %s device\n", num->name); + + +/* Determine the address of the SMBus areas */ + if (force_addr) { + piix4_smba = force_addr & 0xfff0; + force = 0; + } else { + pci_read_config_word(PIIX4_dev, SMBBA, &piix4_smba); + piix4_smba &= 0xfff0; + if(piix4_smba == 0) { + printk(KERN_ERR "i2c-piix4.o: SMB base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + } + + if (check_region(piix4_smba, 8)) { + printk + (KERN_ERR "i2c-piix4.o: SMB region 0x%x already in use!\n", + piix4_smba); + error_return = -ENODEV; + goto END; + } + + pci_read_config_byte(PIIX4_dev, SMBHSTCFG, &temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the PIIX4 first. */ + if (force_addr) { + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(PIIX4_dev, SMBBA, piix4_smba); + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, temp | 0x01); + printk + (KERN_INFO "i2c-piix4.o: WARNING: SMBus interface set to new " + "address %04x!\n", piix4_smba); + } else if ((temp & 1) == 0) { + if (force) { +/* This should never need to be done, but has been noted that + many Dell machines have the SMBus interface on the PIIX4 + disabled!? NOTE: This assumes I/O space and other allocations WERE + done by the Bios! Don't complain if your hardware does weird + things after enabling this. :') Check for Bios updates before + resorting to this. */ + pci_write_config_byte(PIIX4_dev, SMBHSTCFG, + temp | 1); + printk + (KERN_NOTICE "i2c-piix4.o: WARNING: SMBus interface has been FORCEFULLY " + "ENABLED!\n"); + } else { + printk + (KERN_ERR "i2c-piix4.o: Host SMBus controller not enabled!\n"); + error_return = -ENODEV; + goto END; + } + } + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(piix4_smba, 8, "piix4-smbus"); + +#ifdef DEBUG + if ((temp & 0x0E) == 8) + printk + (KERN_DEBUG "i2c-piix4.o: Using Interrupt 9 for SMBus.\n"); + else if ((temp & 0x0E) == 0) + printk + (KERN_DEBUG "i2c-piix4.o: Using Interrupt SMI# for SMBus.\n"); + else + printk + (KERN_ERR "i2c-piix4.o: Illegal Interrupt configuration (or code out " + "of date)!\n"); + + pci_read_config_byte(PIIX4_dev, SMBREV, &temp); + printk(KERN_DEBUG "i2c-piix4.o: SMBREV = 0x%X\n", temp); + printk(KERN_DEBUG "i2c-piix4.o: SMBA = 0x%X\n", piix4_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +/* Internally used pause function */ +void piix4_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int piix4_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-piix4.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-piix4.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk(KERN_ERR "i2c-piix4.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-piix4.o: Successfull!\n"); +#endif + } + } + + /* start the transaction by setting bit 6 */ + outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! (See PIIX4 docs errata) */ + do { + piix4_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + +#ifdef DEBUG + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + printk(KERN_ERR "i2c-piix4.o: SMBus Timeout!\n"); + result = -1; + } +#endif + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk(KERN_ERR "i2c-piix4.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + (KERN_ERR "i2c-piix4.o: Bus collision! SMBus may be locked until next hard\n" + "reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk(KERN_ERR "i2c-piix4.o: Error: no response!\n"); +#endif + } + + if (inb_p(SMBHSTSTS) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + +#ifdef DEBUG + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { + printk + (KERN_ERR "i2c-piix4.o: Failed reset at end of transaction (%02x)\n", + temp); + } + printk + (KERN_DEBUG "i2c-piix4.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 piix4_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int i, len; + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + (KERN_ERR "i2c-piix4.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = PIIX4_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = PIIX4_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = PIIX4_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = PIIX4_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + if (len > 32) + len = 32; + outb_p(len, SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = PIIX4_BLOCK_DATA; + break; + } + + outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); + + if (piix4_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == PIIX4_QUICK)) + return 0; + + + switch (size) { + case PIIX4_BYTE: /* Where is the result put? I assume here it is in + SMBHSTDAT0 but it might just as well be in the + SMBHSTCMD. No clue in the docs */ + + data->byte = inb_p(SMBHSTDAT0); + break; + case PIIX4_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case PIIX4_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case PIIX4_BLOCK_DATA: + data->block[0] = inb_p(SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMBBLKDAT); + break; + } + return 0; +} + +void piix4_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void piix4_dec(struct i2c_adapter *adapter) +{ + + MOD_DEC_USE_COUNT; +} + +u32 piix4_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +int __init i2c_piix4_init(void) +{ + int res; + printk("i2c-piix4.o version %s (%s)\n", LM_VERSION, LM_DATE); + if (piix4_initialized) { + printk + (KERN_ERR "i2c-piix4.o: Oops, piix4_init called a second time!\n"); + return -EBUSY; + } + piix4_initialized = 0; + if ((res = piix4_setup())) { + printk + (KERN_ERR "i2c-piix4.o: Device not detected, module not inserted.\n"); + piix4_cleanup(); + return res; + } + piix4_initialized++; + sprintf(piix4_adapter.name, "SMBus PIIX4 adapter at %04x", + piix4_smba); + if ((res = i2c_add_adapter(&piix4_adapter))) { + printk + (KERN_ERR "i2c-piix4.o: Adapter registration failed, module not inserted.\n"); + piix4_cleanup(); + return res; + } + piix4_initialized++; + printk(KERN_ERR "i2c-piix4.o: SMBus detected and initialized\n"); + return 0; +} + +int __init piix4_cleanup(void) +{ + int res; + if (piix4_initialized >= 2) { + if ((res = i2c_del_adapter(&piix4_adapter))) { + printk + (KERN_ERR "i2c-piix4.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + piix4_initialized--; + } + if (piix4_initialized >= 1) { + release_region(piix4_smba, 8); + piix4_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("PIIX4 SMBus driver"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +int init_module(void) +{ + return i2c_piix4_init(); +} + +int cleanup_module(void) +{ + return piix4_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/i2c/i2c-sis5595.c Mon Mar 25 15:28:36 CET 2002 +++ linux/drivers/i2c/i2c-sis5595.c Mon Mar 25 15:28:36 CET 2002 @@ -0,0 +1,552 @@ +/* + sis5595.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Note: we assume there can only be one SIS5595 with one SMBus interface */ + +/* + Note: all have mfr. ID 0x1039. + SUPPORTED PCI ID + 5595 0008 + + Note: these chips contain a 0008 device which is incompatible with the + 5595. We recognize these by the presence of the listed + "blacklist" PCI ID and refuse to load. + + NOT SUPPORTED PCI ID BLACKLIST PCI ID + 540 0008 0540 + 550 0008 0550 + 5513 0008 5511 + 5581 0008 5597 + 5582 0008 5597 + 5597 0008 5597 + 5598 0008 5597/5598 + 630 0008 0630 + 645 0008 0645 + 730 0008 0730 + 735 0008 0735 +*/ + +/* TO DO: + * Add Block Transfers (ugly, but supported by the adapter) + * Add adapter resets + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef PCI_DEVICE_ID_SI_540 +#define PCI_DEVICE_ID_SI_540 0x0540 +#endif +#ifndef PCI_DEVICE_ID_SI_550 +#define PCI_DEVICE_ID_SI_550 0x0550 +#endif +#ifndef PCI_DEVICE_ID_SI_630 +#define PCI_DEVICE_ID_SI_630 0x0630 +#endif +#ifndef PCI_DEVICE_ID_SI_730 +#define PCI_DEVICE_ID_SI_730 0x0730 +#endif +#ifndef PCI_DEVICE_ID_SI_5598 +#define PCI_DEVICE_ID_SI_5598 0x5598 +#endif + +static int blacklist[] = { + PCI_DEVICE_ID_SI_540, + PCI_DEVICE_ID_SI_550, + PCI_DEVICE_ID_SI_630, + PCI_DEVICE_ID_SI_730, + PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but + that ID shows up in other chips so we + use the 5511 ID for recognition */ + PCI_DEVICE_ID_SI_5597, + PCI_DEVICE_ID_SI_5598, + 0x645, + 0x735, + 0 }; + +/* Length of ISA address segment */ +#define SIS5595_EXTENT 8 +/* SIS5595 SMBus registers */ +#define SMB_STS_LO 0x00 +#define SMB_STS_HI 0x01 +#define SMB_CTL_LO 0x02 +#define SMB_CTL_HI 0x03 +#define SMB_ADDR 0x04 +#define SMB_CMD 0x05 +#define SMB_PCNT 0x06 +#define SMB_CNT 0x07 +#define SMB_BYTE 0x08 +#define SMB_DEV 0x10 +#define SMB_DB0 0x11 +#define SMB_DB1 0x12 +#define SMB_HAA 0x13 + +/* PCI Address Constants */ +#define SMB_INDEX 0x38 +#define SMB_DAT 0x39 +#define SIS5595_ENABLE_REG 0x40 +#define ACPI_BASE 0x90 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* SIS5595 constants */ +#define SIS5595_QUICK 0x00 +#define SIS5595_BYTE 0x02 +#define SIS5595_BYTE_DATA 0x04 +#define SIS5595_WORD_DATA 0x06 +#define SIS5595_PROC_CALL 0x08 +#define SIS5595_BLOCK_DATA 0x0A + +/* insmod parameters */ + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the i2c controller"); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_sis5595_init(void); +static int __init sis5595_cleanup(void); +static int sis5595_setup(void); +static s32 sis5595_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data); +static void sis5595_do_pause(unsigned int amount); +static int sis5595_transaction(void); +static void sis5595_inc(struct i2c_adapter *adapter); +static void sis5595_dec(struct i2c_adapter *adapter); +static u32 sis5595_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ sis5595_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ sis5595_func, +}; + +static struct i2c_adapter sis5595_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS5595, + &smbus_algorithm, + NULL, + sis5595_inc, + sis5595_dec, + NULL, + NULL, +}; + +static int __initdata sis5595_initialized; +static unsigned short sis5595_base = 0; + +static u8 sis5595_read(u8 reg) +{ + outb(reg, sis5595_base + SMB_INDEX); + return inb(sis5595_base + SMB_DAT); +} + +static void sis5595_write(u8 reg, u8 data) +{ + outb(reg, sis5595_base + SMB_INDEX); + outb(data, sis5595_base + SMB_DAT); +} + + +/* Detect whether a SIS5595 can be found, and initialize it, where necessary. + Note the differences between kernels with the old PCI BIOS interface and + newer kernels with the real PCI interface. In compat.h some things are + defined to make the transition easier. */ +int sis5595_setup(void) +{ + u16 a; + u8 val; + struct pci_dev *SIS5595_dev; + int *i; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) { + printk("i2c-sis5595.o: Error: No PCI-bus found!\n"); + return -ENODEV; + } + + /* Look for the SIS5595 */ + SIS5595_dev = NULL; + if (!(SIS5595_dev = pci_find_device(PCI_VENDOR_ID_SI, + PCI_DEVICE_ID_SI_503, + SIS5595_dev))) { + printk("i2c-sis5595.o: Error: Can't detect SIS5595!\n"); + return -ENODEV; + } + + /* Look for imposters */ + for(i = blacklist; *i != 0; i++) { + if (pci_find_device(PCI_VENDOR_ID_SI, *i, NULL)) { + printk("i2c-sis5595.o: Error: Looked for SIS5595 but found unsupported device %.4X\n", *i); + return -ENODEV; + } + } + +/* Determine the address of the SMBus areas */ + pci_read_config_word(SIS5595_dev, ACPI_BASE, &sis5595_base); + if(sis5595_base == 0 && force_addr == 0) { + printk("i2c-sis5595.o: ACPI base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + + if(force_addr) + sis5595_base = force_addr & ~(SIS5595_EXTENT - 1); +#ifdef DEBUG + printk("ACPI Base address: %04x\n", sis5595_base); +#endif + /* NB: We grab just the two SMBus registers here, but this may still + * interfere with ACPI :-( */ + if (check_region(sis5595_base + SMB_INDEX, 2)) { + printk + ("i2c-sis5595.o: SMBus registers 0x%04x-0x%04x already in use!\n", + sis5595_base + SMB_INDEX, + sis5595_base + SMB_INDEX + 1); + return -ENODEV; + } + + if(force_addr) { + printk("i2c-sis5595.o: forcing ISA address 0x%04X\n", sis5595_base); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(SIS5595_dev, ACPI_BASE, sis5595_base)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(SIS5595_dev, ACPI_BASE, &a)) + return -ENODEV; + if ((a & ~(SIS5595_EXTENT - 1)) != sis5595_base) { + /* doesn't work for some chips! */ + printk("i2c-sis5595.o: force address failed - not supported?\n"); + return -ENODEV; + } + } + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)) + return -ENODEV; + if((val & 0x80) == 0) { + printk("sis5595.o: enabling ACPI\n"); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, + val | 0x80)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(SIS5595_dev, SIS5595_ENABLE_REG, &val)) + return -ENODEV; + if((val & 0x80) == 0) { /* doesn't work for some chips? */ + printk("sis5595.o: ACPI enable failed - not supported?\n"); + return -ENODEV; + } + } + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(sis5595_base + SMB_INDEX, 2, "sis5595-smbus"); + return(0); +} + + +/* Internally used pause function */ +void sis5595_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int sis5595_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + + /* Make sure the SMBus host is ready to start transmitting */ + if ( + (temp = + sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != + 0x00) { +#ifdef DEBUG + printk("i2c-sis5595.o: SMBus busy (%04x). Resetting... \n", + temp); +#endif + sis5595_write(SMB_STS_LO, temp & 0xff); + sis5595_write(SMB_STS_HI, temp >> 8); + if ( + (temp = + sis5595_read(SMB_STS_LO) + + (sis5595_read(SMB_STS_HI) << 8)) != 0x00) { +#ifdef DEBUG + printk("i2c-sis5595.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk("i2c-sis5595.o: Successfull!\n"); +#endif + } + } + + /* start the transaction by setting bit 4 */ + sis5595_write(SMB_CTL_LO, sis5595_read(SMB_CTL_LO) | 0x10); + + /* We will always wait for a fraction of a second! */ + do { + sis5595_do_pause(1); + temp = sis5595_read(SMB_STS_LO); + } while (!(temp & 0x40) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { +#ifdef DEBUG + printk("i2c-sis5595.o: SMBus Timeout!\n"); +#endif + result = -1; + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk("i2c-sis5595.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x20) { + result = -1; + printk + ("i2c-sis5595.o: Bus collision! SMBus may be locked until next hard\n" + "reset (or not...)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if ( + (temp = + sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != + 0x00) { + sis5595_write(SMB_STS_LO, temp & 0xff); + sis5595_write(SMB_STS_HI, temp >> 8); + } + + if ( + (temp = + sis5595_read(SMB_STS_LO) + (sis5595_read(SMB_STS_HI) << 8)) != + 0x00) { + +#ifdef DEBUG + printk + ("i2c-sis5595.o: Failed reset at end of transaction (%02x)\n", + temp); +#endif + } + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 sis5595_access(struct i2c_adapter * adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + switch (size) { + case I2C_SMBUS_QUICK: + sis5595_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS5595_QUICK; + break; + case I2C_SMBUS_BYTE: + sis5595_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) + sis5595_write(SMB_CMD, command); + size = SIS5595_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + sis5595_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis5595_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis5595_write(SMB_BYTE, data->byte); + size = SIS5595_BYTE_DATA; + break; + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis5595_write(SMB_ADDR, + ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis5595_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis5595_write(SMB_BYTE, data->word & 0xff); + sis5595_write(SMB_BYTE + 1, + (data->word & 0xff00) >> 8); + } + size = + (size == + I2C_SMBUS_PROC_CALL) ? SIS5595_PROC_CALL : + SIS5595_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + printk("sis5595.o: Block data not yet implemented!\n"); + return -1; + break; + } + + sis5595_write(SMB_CTL_LO, ((size & 0x0E))); + + if (sis5595_transaction()) /* Error in transaction */ + return -1; + + if ((size != SIS5595_PROC_CALL) && + ((read_write == I2C_SMBUS_WRITE) || (size == SIS5595_QUICK))) + return 0; + + + switch (size) { + case SIS5595_BYTE: /* Where is the result put? I assume here it is in + SMB_DATA but it might just as well be in the + SMB_CMD. No clue in the docs */ + case SIS5595_BYTE_DATA: + data->byte = sis5595_read(SMB_BYTE); + break; + case SIS5595_WORD_DATA: + case SIS5595_PROC_CALL: + data->word = + sis5595_read(SMB_BYTE) + + (sis5595_read(SMB_BYTE + 1) << 8); + break; + } + return 0; +} + +void sis5595_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void sis5595_dec(struct i2c_adapter *adapter) +{ + + MOD_DEC_USE_COUNT; +} + +u32 sis5595_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_PROC_CALL; +} + +int __init i2c_sis5595_init(void) +{ + int res; + printk("i2c-sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG +/* PE- It might be good to make this a permanent part of the code! */ + if (sis5595_initialized) { + printk + ("i2c-sis5595.o: Oops, sis5595_init called a second time!\n"); + return -EBUSY; + } +#endif + sis5595_initialized = 0; + if ((res = sis5595_setup())) { + printk + ("i2c-sis5595.o: SIS5595 not detected, module not inserted.\n"); + sis5595_cleanup(); + return res; + } + sis5595_initialized++; + sprintf(sis5595_adapter.name, "SMBus SIS5595 adapter at %04x", + sis5595_base + SMB_INDEX); + if ((res = i2c_add_adapter(&sis5595_adapter))) { + printk + ("i2c-sis5595.o: Adapter registration failed, module not inserted.\n"); + sis5595_cleanup(); + return res; + } + sis5595_initialized++; + printk("i2c-sis5595.o: SIS5595 bus detected and initialized\n"); + return 0; +} + +int __init sis5595_cleanup(void) +{ + int res; + if (sis5595_initialized >= 2) { + if ((res = i2c_del_adapter(&sis5595_adapter))) { + printk + ("i2c-sis5595.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + sis5595_initialized--; + } + if (sis5595_initialized >= 1) { + release_region(sis5595_base + SMB_INDEX, 2); + sis5595_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("SIS5595 SMBus driver"); + +int init_module(void) +{ + return i2c_sis5595_init(); +} + +int cleanup_module(void) +{ + return sis5595_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/i2c/i2c-tsunami.c Mon Mar 25 15:28:36 CET 2002 +++ linux/drivers/i2c/i2c-tsunami.c Mon Mar 25 15:28:36 CET 2002 @@ -0,0 +1,201 @@ +/* + i2c-tsunami.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2001 Oleg Vdovikin + + Based on code written by Ralph Metzler and + Simon Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* This interfaces to the I2C bus of the Tsunami/Typhoon 21272 chipsets + to gain access to the on-board I2C devices. + + For more information refer to Compaq's + "Tsunami/Typhoon 21272 Chipset Hardware Reference Manual" + Order Number: DS-0025-TE +*/ + +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* Memory Presence Detect Register (MPD-RW) bits + with except of reserved RAZ bits */ + +#define MPD_DR 0x8 /* Data receive - RO */ +#define MPD_CKR 0x4 /* Clock receive - RO */ +#define MPD_DS 0x2 /* Data send - Must be a 1 to receive - WO */ +#define MPD_CKS 0x1 /* Clock send - WO */ + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_tsunami_init(void); +static int __init i2c_tsunami_cleanup(void); +static void i2c_tsunami_inc(struct i2c_adapter *adapter); +static void i2c_tsunami_dec(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +extern inline void writempd(unsigned long value) +{ + TSUNAMI_cchip->mpd.csr = value; + mb(); +} + +extern inline unsigned long readmpd(void) +{ + return TSUNAMI_cchip->mpd.csr; +} + +static void bit_tsunami_setscl(void *data, int val) +{ + /* read currently setted bits to modify them */ + unsigned long bits = readmpd() >> 2; /* assume output == input */ + + if (val) + bits |= MPD_CKS; + else + bits &= ~MPD_CKS; + + writempd(bits); +} + +static void bit_tsunami_setsda(void *data, int val) +{ + /* read currently setted bits to modify them */ + unsigned long bits = readmpd() >> 2; /* assume output == input */ + + if (val) + bits |= MPD_DS; + else + bits &= ~MPD_DS; + + writempd(bits); +} + +/* The MPD pins are open drain, so the pins always remain outputs. + We rely on the i2c-algo-bit routines to set the pins high before + reading the input from other chips. */ + +static int bit_tsunami_getscl(void *data) +{ + return (0 != (readmpd() & MPD_CKR)); +} + +static int bit_tsunami_getsda(void *data) +{ + return (0 != (readmpd() & MPD_DR)); +} + +static struct i2c_algo_bit_data tsunami_i2c_bit_data = { + NULL, + bit_tsunami_setsda, + bit_tsunami_setscl, + bit_tsunami_getsda, + bit_tsunami_getscl, + 10, 10, 50 /* delays/timeout */ +}; + +static struct i2c_adapter tsunami_i2c_adapter = { + "I2C Tsunami/Typhoon adapter", + I2C_HW_B_TSUNA, + NULL, + &tsunami_i2c_bit_data, + i2c_tsunami_inc, + i2c_tsunami_dec, + NULL, + NULL, +}; + +void i2c_tsunami_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void i2c_tsunami_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +int __init i2c_tsunami_init(void) +{ + int res; + printk("i2c-tsunami.o version %s (%s)\n", LM_VERSION, LM_DATE); + + if (hwrpb->sys_type != ST_DEC_TSUNAMI) { + printk("i2c-tsunami.o: not Tsunami based system (%d), module not inserted.\n", hwrpb->sys_type); + return -ENXIO; + } else { + printk("i2c-tsunami.o: using Cchip MPD at 0x%lx.\n", &TSUNAMI_cchip->mpd); + } + + if ((res = i2c_bit_add_bus(&tsunami_i2c_adapter))) { + printk("i2c-tsunami.o: I2C adapter registration failed\n"); + } else { + printk("i2c-tsunami.o: I2C bus initialized\n"); + } + + return res; +} + +int __init i2c_tsunami_cleanup(void) +{ + int res; + + if ((res = i2c_bit_del_bus(&tsunami_i2c_adapter))) { + printk("i2c-tsunami.o: i2c_bit_del_bus failed, module not removed\n"); + return res; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Oleg I. Vdovikin "); +MODULE_DESCRIPTION("Tsunami I2C/SMBus driver"); + +int init_module(void) +{ + return i2c_tsunami_init(); +} + +int cleanup_module(void) +{ + return i2c_tsunami_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/i2c/i2c-via.c Mon Mar 25 15:28:36 CET 2002 +++ linux/drivers/i2c/i2c-via.c Mon Mar 25 15:28:36 CET 2002 @@ -0,0 +1,226 @@ +/* + i2c-via.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + 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 LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* PCI device */ +#define VENDOR PCI_VENDOR_ID_VIA +#define DEVICE PCI_DEVICE_ID_VIA_82C586_3 + +/* Power management registers */ + +#define PM_CFG_REVID 0x08 /* silicon revision code */ +#define PM_CFG_IOBASE0 0x20 +#define PM_CFG_IOBASE1 0x48 + +#define I2C_DIR (pm_io_base+0x40) +#define I2C_OUT (pm_io_base+0x42) +#define I2C_IN (pm_io_base+0x44) +#define I2C_SCL 0x02 /* clock bit in DIR/OUT/IN register */ +#define I2C_SDA 0x04 + +/* io-region reservation */ +#define IOSPACE 0x06 +#define IOTEXT "via-i2c" + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) x /* silicon revision, io addresses */ +#define DEB2(x) x /* line status */ +#define DEBE(x) /* */ + +/* ----- local functions ---------------------------------------------- */ + +static u16 pm_io_base; + +/* + It does not appear from the datasheet that the GPIO pins are + open drain. So a we set a low value by setting the direction to + output and a high value by setting the direction to input and + relying on the required I2C pullup. The data value is initialized + to 0 in i2c_via_init() and never changed. +*/ + +static void bit_via_setscl(void *data, int state) +{ + outb(state ? inb(I2C_DIR) & ~I2C_SCL : inb(I2C_DIR) | I2C_SCL, + I2C_DIR); +} + +static void bit_via_setsda(void *data, int state) +{ + outb(state ? inb(I2C_DIR) & ~I2C_SDA : inb(I2C_DIR) | I2C_SDA, + I2C_DIR); +} + +static int bit_via_getscl(void *data) +{ + return (0 != (inb(I2C_IN) & I2C_SCL)); +} + +static int bit_via_getsda(void *data) +{ + return (0 != (inb(I2C_IN) & I2C_SDA)); +} + +static void bit_via_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +static void bit_via_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +/* ------------------------------------------------------------------------ */ + +static struct i2c_algo_bit_data bit_data = { + NULL, + bit_via_setsda, + bit_via_setscl, + bit_via_getsda, + bit_via_getscl, + 5, 5, 100, /*waits, timeout */ +}; + +static struct i2c_adapter bit_via_ops = { + "VIA i2c", + I2C_HW_B_VIA, + NULL, + &bit_data, + bit_via_inc, + bit_via_dec, + NULL, + NULL, +}; + + +/* When exactly was the new pci interface introduced? */ +static int find_via(void) +{ + struct pci_dev *s_bridge; + u16 base; + u8 rev; + + if (!pci_present()) + return -ENODEV; + + s_bridge = pci_find_device(VENDOR, DEVICE, NULL); + + if (!s_bridge) { + printk("i2c-via.o: vt82c586b not found\n"); + return -ENODEV; + } + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(s_bridge, PM_CFG_REVID, &rev)) + return -ENODEV; + + switch (rev) { + case 0x00: + base = PM_CFG_IOBASE0; + break; + case 0x01: + case 0x10: + base = PM_CFG_IOBASE1; + break; + + default: + base = PM_CFG_IOBASE1; + /* later revision */ + } + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, base, &pm_io_base)) + return -ENODEV; + + pm_io_base &= (0xff << 8); + return 0; +} + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_via_init(void) +{ + printk("i2c-via.o version %s (%s)\n", LM_VERSION, LM_DATE); + if (find_via() < 0) { + printk("i2c-via.o: Error while reading PCI configuration\n"); + return -ENODEV; + } + + if (check_region(I2C_DIR, IOSPACE) < 0) { + printk("i2c-via.o: IO 0x%x-0x%x already in use\n", + I2C_DIR, I2C_DIR + IOSPACE); + return -EBUSY; + } else { + request_region(I2C_DIR, IOSPACE, IOTEXT); + outb(inb(I2C_DIR) & ~(I2C_SDA | I2C_SCL), I2C_DIR); + outb(inb(I2C_OUT) & ~(I2C_SDA | I2C_SCL), I2C_OUT); + } + + if (i2c_bit_add_bus(&bit_via_ops) == 0) { + printk("i2c-via.o: Module succesfully loaded\n"); + return 0; + } else { + release_region(I2C_DIR, IOSPACE); + printk + ("i2c-via.o: Algo-bit error, couldn't register bus\n"); + return -ENODEV; + } +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +MODULE_AUTHOR("Kyösti Mälkki "); +MODULE_DESCRIPTION("i2c for Via vt82c586b southbridge"); + +int init_module(void) +{ + return i2c_via_init(); +} + +void cleanup_module(void) +{ + i2c_bit_del_bus(&bit_via_ops); + release_region(I2C_DIR, IOSPACE); +} +#endif --- linux-old/drivers/i2c/i2c-viapro.c Mon Mar 25 15:28:36 CET 2002 +++ linux/drivers/i2c/i2c-viapro.c Mon Mar 25 15:28:36 CET 2002 @@ -0,0 +1,570 @@ +/* + i2c-viapro.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2001 Frodo Looijaard , + Philip Edelbrock , Kyösti Mälkki , + Mark D. Studebaker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Supports Via devices: + 82C596A/B (0x3050) + 82C596B (0x3051) + 82C686A/B + 8233 + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifndef PCI_DEVICE_ID_VIA_82C596_3 +#define PCI_DEVICE_ID_VIA_82C596_3 0x3050 +#endif +#ifndef PCI_DEVICE_ID_VIA_82C596B_3 +#define PCI_DEVICE_ID_VIA_82C596B_3 0x3051 +#endif +#ifndef PCI_DEVICE_ID_VIA_82C686_4 +#define PCI_DEVICE_ID_VIA_82C686_4 0x3057 +#endif +#ifndef PCI_DEVICE_ID_VIA_8233_0 +#define PCI_DEVICE_ID_VIA_8233_0 0x3074 +#endif + +#define SMBBA1 0x90 +#define SMBBA2 0x80 +#define SMBBA3 0xD0 + +struct sd { + const unsigned short dev; + const unsigned char base; + const unsigned char hstcfg; + const char *name; +}; + +static struct sd supported[] = { + {PCI_DEVICE_ID_VIA_82C596_3, SMBBA1, 0xD2, "VT82C596A/B"}, + {PCI_DEVICE_ID_VIA_82C596B_3, SMBBA1, 0xD2, "VT82C596B"}, + {PCI_DEVICE_ID_VIA_82C686_4, SMBBA1, 0xD2, "VT82C686A/B"}, + {PCI_DEVICE_ID_VIA_8233_0, SMBBA3, 0xD2, "VT8233"}, + {0, 0, 0, NULL} +}; + +static struct sd *num = supported; + +/* SMBus address offsets */ +#define SMBHSTSTS (0 + vt596_smba) +#define SMBHSLVSTS (1 + vt596_smba) +#define SMBHSTCNT (2 + vt596_smba) +#define SMBHSTCMD (3 + vt596_smba) +#define SMBHSTADD (4 + vt596_smba) +#define SMBHSTDAT0 (5 + vt596_smba) +#define SMBHSTDAT1 (6 + vt596_smba) +#define SMBBLKDAT (7 + vt596_smba) +#define SMBSLVCNT (8 + vt596_smba) +#define SMBSHDWCMD (9 + vt596_smba) +#define SMBSLVEVT (0xA + vt596_smba) +#define SMBSLVDAT (0xC + vt596_smba) + +/* PCI Address Constants */ + +/* SMBus data in configuration space can be found in two places, + We try to select the better one*/ + +static unsigned short smb_cf_hstcfg; + +#define SMBHSTCFG (smb_cf_hstcfg) +#define SMBSLVC (SMBHSTCFG+1) +#define SMBSHDW1 (SMBHSTCFG+2) +#define SMBSHDW2 (SMBHSTCFG+3) +#define SMBREV (SMBHSTCFG+4) + +/* Other settings */ +#define MAX_TIMEOUT 500 +#define ENABLE_INT9 0 + +/* VT82C596 constants */ +#define VT596_QUICK 0x00 +#define VT596_BYTE 0x04 +#define VT596_BYTE_DATA 0x08 +#define VT596_WORD_DATA 0x0C +#define VT596_BLOCK_DATA 0x14 + +/* insmod parameters */ + +/* If force is set to anything different from 0, we forcibly enable the + VT596. DANGEROUS! */ +static int force = 0; +MODULE_PARM(force, "i"); +MODULE_PARM_DESC(force, "Forcibly enable the SMBus. DANGEROUS!"); + +/* If force_addr is set to anything different from 0, we forcibly enable + the VT596 at the given address. VERY DANGEROUS! */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Forcibly enable the SMBus at the given address. " + "EXTREMELY DANGEROUS!"); + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_vt596_init(void); +static int __init vt596_cleanup(void); +static int vt596_setup(void); +static s32 vt596_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data); +static void vt596_do_pause(unsigned int amount); +static int vt596_transaction(void); +static void vt596_inc(struct i2c_adapter *adapter); +static void vt596_dec(struct i2c_adapter *adapter); +static u32 vt596_func(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ vt596_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ vt596_func, +}; + +static struct i2c_adapter vt596_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_VIA2, + &smbus_algorithm, + NULL, + vt596_inc, + vt596_dec, + NULL, + NULL, +}; + +static int __initdata vt596_initialized; +static unsigned short vt596_smba = 0; + + +/* Detect whether a compatible device can be found, and initialize it. */ +int vt596_setup(void) +{ + unsigned char temp; + + struct pci_dev *VT596_dev = NULL; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) + return(-ENODEV); + + /* Look for a supported device/function */ + do { + if((VT596_dev = pci_find_device(PCI_VENDOR_ID_VIA, num->dev, + VT596_dev))) + break; + } while ((++num)->dev); + + if (VT596_dev == NULL) + return(-ENODEV); + printk("i2c-viapro.o: Found Via %s device\n", num->name); + +/* Determine the address of the SMBus areas */ + smb_cf_hstcfg = num->hstcfg; + if (force_addr) { + vt596_smba = force_addr & 0xfff0; + force = 0; + } else { + if ((pci_read_config_word(VT596_dev, num->base, &vt596_smba)) + || !(vt596_smba & 0x1)) { + /* try 2nd address and config reg. for 596 */ + if((num->dev == PCI_DEVICE_ID_VIA_82C596_3) && + (!pci_read_config_word(VT596_dev, SMBBA2, &vt596_smba)) && + (vt596_smba & 0x1)) { + smb_cf_hstcfg = 0x84; + } else { + /* no matches at all */ + printk("i2c-viapro.o: Cannot configure SMBus " + "I/O Base address\n"); + return(-ENODEV); + } + } + vt596_smba &= 0xfff0; + if(vt596_smba == 0) { + printk(KERN_ERR "i2c-viapro.o: SMBus base address" + "uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + } + + if (check_region(vt596_smba, 8)) { + printk("i2c-viapro.o: SMBus region 0x%x already in use!\n", + vt596_smba); + return(-ENODEV); + } + + pci_read_config_byte(VT596_dev, SMBHSTCFG, &temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the VT596 first. */ + if (force_addr) { + pci_write_config_byte(VT596_dev, SMBHSTCFG, temp & 0xfe); + pci_write_config_word(VT596_dev, num->base, vt596_smba); + pci_write_config_byte(VT596_dev, SMBHSTCFG, temp | 0x01); + printk + ("i2c-viapro.o: WARNING: SMBus interface set to new " + "address 0x%04x!\n", vt596_smba); + } else if ((temp & 1) == 0) { + if (force) { +/* NOTE: This assumes I/O space and other allocations WERE + done by the Bios! Don't complain if your hardware does weird + things after enabling this. :') Check for Bios updates before + resorting to this. */ + pci_write_config_byte(VT596_dev, SMBHSTCFG, + temp | 1); + printk + ("i2c-viapro.o: enabling SMBus device\n"); + } else { + printk + ("SMBUS: Error: Host SMBus controller not enabled! - " + "upgrade BIOS or use force=1\n"); + return(-ENODEV); + } + } + + /* Everything is happy, let's grab the memory and set things up. */ + request_region(vt596_smba, 8, "viapro-smbus"); + +#ifdef DEBUG + if ((temp & 0x0E) == 8) + printk("i2c-viapro.o: using Interrupt 9 for SMBus.\n"); + else if ((temp & 0x0E) == 0) + printk("i2c-viapro.o: using Interrupt SMI# for SMBus.\n"); + else + printk + ("i2c-viapro.o: Illegal Interrupt configuration (or code out " + "of date)!\n"); + + pci_read_config_byte(VT596_dev, SMBREV, &temp); + printk("i2c-viapro.o: SMBREV = 0x%X\n", temp); + printk("i2c-viapro.o: VT596_smba = 0x%X\n", vt596_smba); +#endif /* DEBUG */ + + return(0); +} + + +/* Internally used pause function */ +void vt596_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Another internally used function */ +int vt596_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + ("i2c-viapro.o: Transaction (pre): CNT=%02x, CMD=%02x, ADD=%02x, DAT0=%02x, " + "DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + + /* Make sure the SMBus host is ready to start transmitting */ + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk("i2c-viapro.o: SMBus busy (0x%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk("i2c-viapro.o: Failed! (0x%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk("i2c-viapro.o: Successfull!\n"); +#endif + } + } + + /* start the transaction by setting bit 6 */ + outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); + + /* We will always wait for a fraction of a second! + I don't know if VIA needs this, Intel did */ + do { + vt596_do_pause(1); + temp = inb_p(SMBHSTSTS); + } while ((temp & 0x01) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { +#ifdef DEBUG + printk("i2c-viapro.o: SMBus Timeout!\n"); + result = -1; +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk("i2c-viapro.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + ("i2c-viapro.o: Bus collision! SMBus may be locked until next hard\n" + "reset. (sorry!)\n"); + /* Clock stops and slave is stuck in mid-transmission */ + } + + if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk("i2c-viapro.o: Error: no response!\n"); +#endif + } + + if (inb_p(SMBHSTSTS) != 0x00) + outb_p(inb(SMBHSTSTS), SMBHSTSTS); + + if ((temp = inb_p(SMBHSTSTS)) != 0x00) { +#ifdef DEBUG + printk + ("i2c-viapro.o: Failed reset at end of transaction (%02x)\n", + temp); +#endif + } +#ifdef DEBUG + printk + ("i2c-viapro.o: Transaction (post): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, DAT1=%02x\n", inb_p(SMBHSTCNT), inb_p(SMBHSTCMD), + inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), inb_p(SMBHSTDAT1)); +#endif + return result; +} + +/* Return -1 on error. See smbus.h for more information */ +s32 vt596_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, + char read_write, + u8 command, int size, union i2c_smbus_data * data) +{ + int i, len; + + switch (size) { + case I2C_SMBUS_PROC_CALL: + printk + ("i2c-viapro.o: I2C_SMBUS_PROC_CALL not supported!\n"); + return -1; + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + size = VT596_QUICK; + break; + case I2C_SMBUS_BYTE: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(command, SMBHSTCMD); + size = VT596_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) + outb_p(data->byte, SMBHSTDAT0); + size = VT596_BYTE_DATA; + break; + case I2C_SMBUS_WORD_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + outb_p(data->word & 0xff, SMBHSTDAT0); + outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + } + size = VT596_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + if (read_write == I2C_SMBUS_WRITE) { + len = data->block[0]; + if (len < 0) + len = 0; + if (len > 32) + len = 32; + outb_p(len, SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= len; i++) + outb_p(data->block[i], SMBBLKDAT); + } + size = VT596_BLOCK_DATA; + break; + } + + outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); + + if (vt596_transaction()) /* Error in transaction */ + return -1; + + if ((read_write == I2C_SMBUS_WRITE) || (size == VT596_QUICK)) + return 0; + + + switch (size) { + case VT596_BYTE: /* Where is the result put? I assume here it is in + SMBHSTDAT0 but it might just as well be in the + SMBHSTCMD. No clue in the docs */ + + data->byte = inb_p(SMBHSTDAT0); + break; + case VT596_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case VT596_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case VT596_BLOCK_DATA: + data->block[0] = inb_p(SMBHSTDAT0); + i = inb_p(SMBHSTCNT); /* Reset SMBBLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb_p(SMBBLKDAT); + break; + } + return 0; +} + +void vt596_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void vt596_dec(struct i2c_adapter *adapter) +{ + + MOD_DEC_USE_COUNT; +} + +u32 vt596_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; +} + +int __init i2c_vt596_init(void) +{ + int res; + printk("i2c-viapro.o version %s (%s)\n", LM_VERSION, LM_DATE); +#ifdef DEBUG +/* PE- It might be good to make this a permanent part of the code! */ + if (vt596_initialized) { + printk + ("i2c-viapro.o: Oops, vt596_init called a second time!\n"); + return -EBUSY; + } +#endif + vt596_initialized = 0; + if ((res = vt596_setup())) { + printk + ("i2c-viapro.o: Can't detect vt82c596 or compatible device, module not inserted.\n"); + vt596_cleanup(); + return res; + } + vt596_initialized++; + sprintf(vt596_adapter.name, "SMBus Via Pro adapter at %04x", + vt596_smba); + if ((res = i2c_add_adapter(&vt596_adapter))) { + printk + ("i2c-viapro.o: Adapter registration failed, module not inserted.\n"); + vt596_cleanup(); + return res; + } + vt596_initialized++; + printk("i2c-viapro.o: Via Pro SMBus detected and initialized\n"); + return 0; +} + +int __init vt596_cleanup(void) +{ + int res; + if (vt596_initialized >= 2) { + if ((res = i2c_del_adapter(&vt596_adapter))) { + printk + ("i2c-viapro.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + vt596_initialized--; + } + if (vt596_initialized >= 1) { + release_region(vt596_smba, 8); + vt596_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("vt82c596 SMBus driver"); + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +int init_module(void) +{ + return i2c_vt596_init(); +} + +int cleanup_module(void) +{ + return vt596_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/i2c/i2c-voodoo3.c Mon Mar 25 15:28:36 CET 2002 +++ linux/drivers/i2c/i2c-voodoo3.c Mon Mar 25 15:28:36 CET 2002 @@ -0,0 +1,362 @@ +/* + voodoo3.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard , + Philip Edelbrock , + Ralph Metzler , and + Mark D. Studebaker + + Based on code written by Ralph Metzler and + Simon Vogl + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* This interfaces to the I2C bus of the Voodoo3 to gain access to + the BT869 and possibly other I2C devices. */ + +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* 3DFX defines */ +#ifndef PCI_DEVICE_ID_3DFX_VOODOO3 +#define PCI_DEVICE_ID_3DFX_VOODOO3 0x05 +#endif +#ifndef PCI_DEVICE_ID_3DFX_BANSHEE +#define PCI_DEVICE_ID_3DFX_BANSHEE 0x03 +#endif + +/* the only registers we use */ +#define REG 0x78 +#define REG2 0x70 + +/* bit locations in the register */ +#define DDC_ENAB 0x00040000 +#define DDC_SCL_OUT 0x00080000 +#define DDC_SDA_OUT 0x00100000 +#define DDC_SCL_IN 0x00200000 +#define DDC_SDA_IN 0x00400000 +#define I2C_ENAB 0x00800000 +#define I2C_SCL_OUT 0x01000000 +#define I2C_SDA_OUT 0x02000000 +#define I2C_SCL_IN 0x04000000 +#define I2C_SDA_IN 0x08000000 + +/* initialization states */ +#define INIT2 0x2 +#define INIT3 0x4 + +/* delays */ +#define CYCLE_DELAY 10 +#define TIMEOUT 50 + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_voodoo3_init(void); +static int __init voodoo3_cleanup(void); +static int voodoo3_setup(void); +static void config_v3(struct pci_dev *dev); +static void voodoo3_inc(struct i2c_adapter *adapter); +static void voodoo3_dec(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + + +static int __initdata voodoo3_initialized; +static unsigned char *mem; + +extern inline void outlong(unsigned int dat) +{ + *((unsigned int *) (mem + REG)) = dat; +} + +extern inline unsigned int readlong(void) +{ + return *((unsigned int *) (mem + REG)); +} + +/* The voo GPIO registers don't have individual masks for each bit + so we always have to read before writing. */ + +static void bit_vooi2c_setscl(void *data, int val) +{ + unsigned int r; + r = readlong(); + if(val) + r |= I2C_SCL_OUT; + else + r &= ~I2C_SCL_OUT; + outlong(r); +} + +static void bit_vooi2c_setsda(void *data, int val) +{ + unsigned int r; + r = readlong(); + if(val) + r |= I2C_SDA_OUT; + else + r &= ~I2C_SDA_OUT; + outlong(r); +} + +/* The GPIO pins are open drain, so the pins always remain outputs. + We rely on the i2c-algo-bit routines to set the pins high before + reading the input from other chips. */ + +static int bit_vooi2c_getscl(void *data) +{ + return (0 != (readlong() & I2C_SCL_IN)); +} + +static int bit_vooi2c_getsda(void *data) +{ + return (0 != (readlong() & I2C_SDA_IN)); +} + +static void bit_vooddc_setscl(void *data, int val) +{ + unsigned int r; + r = readlong(); + if(val) + r |= DDC_SCL_OUT; + else + r &= ~DDC_SCL_OUT; + outlong(r); +} + +static void bit_vooddc_setsda(void *data, int val) +{ + unsigned int r; + r = readlong(); + if(val) + r |= DDC_SDA_OUT; + else + r &= ~DDC_SDA_OUT; + outlong(r); +} + +static int bit_vooddc_getscl(void *data) +{ + return (0 != (readlong() & DDC_SCL_IN)); +} + +static int bit_vooddc_getsda(void *data) +{ + return (0 != (readlong() & DDC_SDA_IN)); +} + +static struct i2c_algo_bit_data voo_i2c_bit_data = { + NULL, + bit_vooi2c_setsda, + bit_vooi2c_setscl, + bit_vooi2c_getsda, + bit_vooi2c_getscl, + CYCLE_DELAY, CYCLE_DELAY, TIMEOUT +}; + +static struct i2c_adapter voodoo3_i2c_adapter = { + "I2C Voodoo3/Banshee adapter", + I2C_HW_B_VOO, + NULL, + &voo_i2c_bit_data, + voodoo3_inc, + voodoo3_dec, + NULL, + NULL, +}; + +static struct i2c_algo_bit_data voo_ddc_bit_data = { + NULL, + bit_vooddc_setsda, + bit_vooddc_setscl, + bit_vooddc_getsda, + bit_vooddc_getscl, + CYCLE_DELAY, CYCLE_DELAY, TIMEOUT +}; + +static struct i2c_adapter voodoo3_ddc_adapter = { + "DDC Voodoo3/Banshee adapter", + I2C_HW_B_VOO, + NULL, + &voo_ddc_bit_data, + voodoo3_inc, + voodoo3_dec, + NULL, + NULL, +}; + +/* Configures the chip */ + +void config_v3(struct pci_dev *dev) +{ + unsigned int cadr; + + /* map Voodoo3 memory */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13) + cadr = dev->resource[0].start; +#else + cadr = dev->base_address[0]; +#endif + cadr &= PCI_BASE_ADDRESS_MEM_MASK; + mem = ioremap_nocache(cadr, 0x1000); + + *((unsigned int *) (mem + REG2)) = 0x8160; + *((unsigned int *) (mem + REG)) = 0xcffc0020; + printk("i2c-voodoo3: Using Banshee/Voodoo3 at 0x%p\n", mem); +} + +/* Detect whether a Voodoo3 or a Banshee can be found, + and initialize it. */ +static int voodoo3_setup(void) +{ + struct pci_dev *dev; + int v3_num; + + v3_num = 0; + + dev = NULL; + do { + if ((dev = pci_find_device(PCI_VENDOR_ID_3DFX, + PCI_DEVICE_ID_3DFX_VOODOO3, + dev))) { + if (!v3_num) + config_v3(dev); + v3_num++; + } + } while (dev); + + dev = NULL; + do { + if ((dev = pci_find_device(PCI_VENDOR_ID_3DFX, + PCI_DEVICE_ID_3DFX_BANSHEE, + dev))) { + if (!v3_num) + config_v3(dev); + v3_num++; + } + } while (dev); + + if (v3_num > 0) { + printk("i2c-voodoo3: %d Banshee/Voodoo3 found.\n", v3_num); + if (v3_num > 1) + printk("i2c-voodoo3: warning: only 1 supported.\n"); + return 0; + } else { + printk("i2c-voodoo3: No Voodoo3 found.\n"); + return -ENODEV; + } +} + +void voodoo3_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void voodoo3_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +int __init i2c_voodoo3_init(void) +{ + int res; + printk("i2c-voodoo3.o version %s (%s)\n", LM_VERSION, LM_DATE); + voodoo3_initialized = 0; + if ((res = voodoo3_setup())) { + printk + ("i2c-voodoo3.o: Voodoo3 not detected, module not inserted.\n"); + voodoo3_cleanup(); + return res; + } + if ((res = i2c_bit_add_bus(&voodoo3_i2c_adapter))) { + printk("i2c-voodoo3.o: I2C adapter registration failed\n"); + } else { + printk("i2c-voodoo3.o: I2C bus initialized\n"); + voodoo3_initialized |= INIT2; + } + if ((res = i2c_bit_add_bus(&voodoo3_ddc_adapter))) { + printk("i2c-voodoo3.o: DDC adapter registration failed\n"); + } else { + printk("i2c-voodoo3.o: DDC bus initialized\n"); + voodoo3_initialized |= INIT3; + } + if(!(voodoo3_initialized & (INIT2 | INIT3))) { + printk("i2c-voodoo3.o: Both registrations failed, module not inserted\n"); + voodoo3_cleanup(); + return res; + } + return 0; +} + +int __init voodoo3_cleanup(void) +{ + int res; + + iounmap(mem); + if (voodoo3_initialized & INIT3) { + if ((res = i2c_bit_del_bus(&voodoo3_ddc_adapter))) { + printk + ("i2c-voodoo3.o: i2c_bit_del_bus failed, module not removed\n"); + return res; + } + } + if (voodoo3_initialized & INIT2) { + if ((res = i2c_bit_del_bus(&voodoo3_i2c_adapter))) { + printk + ("i2c-voodoo3.o: i2c_bit_del_bus failed, module not removed\n"); + return res; + } + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , Ralph Metzler , and Mark D. Studebaker "); +MODULE_DESCRIPTION("Voodoo3 I2C/SMBus driver"); + + +int init_module(void) +{ + return i2c_voodoo3_init(); +} + +int cleanup_module(void) +{ + return voodoo3_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/adm1021.c Mon Mar 25 15:28:36 CET 2002 +++ linux/drivers/sensors/adm1021.c Mon Mar 25 15:28:36 CET 2002 @@ -0,0 +1,655 @@ +/* + adm1021.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x18, 0x1a, 0x29, 0x2b, + 0x4c, 0x4e, SENSORS_I2C_END +}; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_7(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm); + +/* adm1021 constants specified below */ + +/* The adm1021 registers */ +/* Read-only */ +#define ADM1021_REG_TEMP 0x00 +#define ADM1021_REG_REMOTE_TEMP 0x01 +#define ADM1021_REG_STATUS 0x02 +#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys */ +#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */ +#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */ +/* These use different addresses for reading/writing */ +#define ADM1021_REG_CONFIG_R 0x03 +#define ADM1021_REG_CONFIG_W 0x09 +#define ADM1021_REG_CONV_RATE_R 0x04 +#define ADM1021_REG_CONV_RATE_W 0x0A +/* These are for the ADM1023's additional precision on the remote temp sensor */ +#define ADM1021_REG_REM_TEMP_PREC 0x010 +#define ADM1021_REG_REM_OFFSET 0x011 +#define ADM1021_REG_REM_OFFSET_PREC 0x012 +#define ADM1021_REG_REM_TOS_PREC 0x013 +#define ADM1021_REG_REM_THYST_PREC 0x014 +/* limits */ +#define ADM1021_REG_TOS_R 0x05 +#define ADM1021_REG_TOS_W 0x0B +#define ADM1021_REG_REMOTE_TOS_R 0x07 +#define ADM1021_REG_REMOTE_TOS_W 0x0D +#define ADM1021_REG_THYST_R 0x06 +#define ADM1021_REG_THYST_W 0x0C +#define ADM1021_REG_REMOTE_THYST_R 0x08 +#define ADM1021_REG_REMOTE_THYST_W 0x0E +/* write-only */ +#define ADM1021_REG_ONESHOT 0x0F + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +/* Conversions note: 1021 uses normal integer signed-byte format*/ +#define TEMP_FROM_REG(val) (val > 127 ? val-256 : val) +#define TEMP_TO_REG(val) (SENSORS_LIMIT((val < 0 ? val+256 : val),0,255)) + +/* Initial values */ + +/* Note: Eventhough I left the low and high limits named os and hyst, +they don't quite work like a thermostat the way the LM75 does. I.e., +a lower temp than THYST actuall triggers an alarm instead of +clearing it. Weird, ey? --Phil */ +#define adm1021_INIT_TOS 60 +#define adm1021_INIT_THYST 20 +#define adm1021_INIT_REMOTE_TOS 60 +#define adm1021_INIT_REMOTE_THYST 20 + +/* Each client has this additional data */ +struct adm1021_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 temp, temp_os, temp_hyst; /* Register values */ + u8 remote_temp, remote_temp_os, remote_temp_hyst, alarms, die_code; + /* Special values for ADM1023 only */ + u8 remote_temp_prec, remote_temp_os_prec, remote_temp_hyst_prec, + remote_temp_offset, remote_temp_offset_prec; +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_adm1021_init(void); +static int __init adm1021_cleanup(void); +static int adm1021_attach_adapter(struct i2c_adapter *adapter); +static int adm1021_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void adm1021_init_client(struct i2c_client *client); +static int adm1021_detach_client(struct i2c_client *client); +static int adm1021_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void adm1021_inc_use(struct i2c_client *client); +static void adm1021_dec_use(struct i2c_client *client); +static int adm1021_read_value(struct i2c_client *client, u8 reg); +static int adm1021_write_value(struct i2c_client *client, u8 reg, + u16 value); +static void adm1021_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1021_remote_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results); +static void adm1021_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1021_die_code(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1021_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver adm1021_driver = { + /* name */ "ADM1021, MAX1617 sensor driver", + /* id */ I2C_DRIVERID_ADM1021, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &adm1021_attach_adapter, + /* detach_client */ &adm1021_detach_client, + /* command */ &adm1021_command, + /* inc_use */ &adm1021_inc_use, + /* dec_use */ &adm1021_dec_use +}; + +/* These files are created for each detected adm1021. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table adm1021_dir_table_template[] = { + {ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_temp}, + {ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_remote_temp}, + {ADM1021_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_die_code}, + {ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_alarms}, + {0} +}; + +static ctl_table adm1021_max_dir_table_template[] = { + {ADM1021_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_temp}, + {ADM1021_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_remote_temp}, + {ADM1021_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1021_alarms}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata adm1021_initialized = 0; + +/* I choose here for semi-static allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +static int adm1021_id = 0; + +int adm1021_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, adm1021_detect); +} + +static int adm1021_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct adm1021_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("adm1021.o: adm1021_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access adm1021_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct adm1021_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct adm1021_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &adm1021_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if ( + (adm1021_read_value(new_client, ADM1021_REG_STATUS) & + 0x03) != 0x00) + goto ERROR1; + } + + /* Determine the chip type. */ + + if (kind <= 0) { + i = adm1021_read_value(new_client, ADM1021_REG_MAN_ID); + if (i == 0x41) + if ((adm1021_read_value (new_client, ADM1021_REG_DEV_ID) & 0x0F0) == 0x030) + kind = adm1023; + else + kind = adm1021; + else if (i == 0x49) + kind = thmc10; + else if (i == 0x23) + kind = gl523sm; + else if ((i == 0x4d) && + (adm1021_read_value + (new_client, ADM1021_REG_DEV_ID) == 0x01)) + kind = max1617a; + /* LM84 Mfr ID in a different place */ + else + if (adm1021_read_value + (new_client, ADM1021_REG_CONV_RATE_R) == 0x00) + kind = lm84; + else + kind = max1617; + } + + if (kind == max1617) { + type_name = "max1617"; + client_name = "MAX1617 chip"; + } else if (kind == max1617a) { + type_name = "max1617a"; + client_name = "MAX1617A chip"; + } else if (kind == adm1021) { + type_name = "adm1021"; + client_name = "ADM1021 chip"; + } else if (kind == adm1023) { + type_name = "adm1023"; + client_name = "ADM1023 chip"; + } else if (kind == thmc10) { + type_name = "thmc10"; + client_name = "THMC10 chip"; + } else if (kind == lm84) { + type_name = "lm84"; + client_name = "LM84 chip"; + } else if (kind == gl523sm) { + type_name = "gl523sm"; + client_name = "GL523SM chip"; + } else { +#ifdef DEBUG + printk("adm1021.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = adm1021_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + data->type == + adm1021 ? + adm1021_dir_table_template : + adm1021_max_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the ADM1021 chip */ + adm1021_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +void adm1021_init_client(struct i2c_client *client) +{ + /* Initialize the adm1021 chip */ + adm1021_write_value(client, ADM1021_REG_TOS_W, + TEMP_TO_REG(adm1021_INIT_TOS)); + adm1021_write_value(client, ADM1021_REG_THYST_W, + TEMP_TO_REG(adm1021_INIT_THYST)); + adm1021_write_value(client, ADM1021_REG_REMOTE_TOS_W, + TEMP_TO_REG(adm1021_INIT_REMOTE_TOS)); + adm1021_write_value(client, ADM1021_REG_REMOTE_THYST_W, + TEMP_TO_REG(adm1021_INIT_REMOTE_THYST)); + /* Enable ADC and disable suspend mode */ + adm1021_write_value(client, ADM1021_REG_CONFIG_W, 0); + /* Set Conversion rate to 1/sec (this can be tinkered with) */ + adm1021_write_value(client, ADM1021_REG_CONV_RATE_W, 0x04); +} + +int adm1021_detach_client(struct i2c_client *client) +{ + + int err; + + i2c_deregister_entry(((struct adm1021_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("adm1021.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; + +} + + +/* No commands defined yet */ +int adm1021_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void adm1021_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void adm1021_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* All registers are byte-sized */ +int adm1021_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +int adm1021_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +void adm1021_update_client(struct i2c_client *client) +{ + struct adm1021_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting adm1021 update\n"); +#endif + + data->temp = adm1021_read_value(client, ADM1021_REG_TEMP); + data->temp_os = + adm1021_read_value(client, ADM1021_REG_TOS_R); + data->temp_hyst = + adm1021_read_value(client, ADM1021_REG_THYST_R); + data->remote_temp = + adm1021_read_value(client, ADM1021_REG_REMOTE_TEMP); + data->remote_temp_os = + adm1021_read_value(client, ADM1021_REG_REMOTE_TOS_R); + data->remote_temp_hyst = + adm1021_read_value(client, ADM1021_REG_REMOTE_THYST_R); + data->alarms = + adm1021_read_value(client, ADM1021_REG_STATUS) & 0xec; + if (data->type == adm1021) + data->die_code = + adm1021_read_value(client, + ADM1021_REG_DIE_CODE); + if (data->type == adm1023) { + data->remote_temp_prec = + adm1021_read_value(client, ADM1021_REG_REM_TEMP_PREC); + data->remote_temp_os_prec = + adm1021_read_value(client, ADM1021_REG_REM_TOS_PREC); + data->remote_temp_hyst_prec = + adm1021_read_value(client, ADM1021_REG_REM_THYST_PREC); + data->remote_temp_offset = + adm1021_read_value(client, ADM1021_REG_REM_OFFSET); + data->remote_temp_offset_prec = + adm1021_read_value(client, ADM1021_REG_REM_OFFSET_PREC); + } + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void adm1021_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1021_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1021_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_os); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_os = TEMP_TO_REG(results[0]); + adm1021_write_value(client, ADM1021_REG_TOS_W, + data->temp_os); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + adm1021_write_value(client, ADM1021_REG_THYST_W, + data->temp_hyst); + } + } +} + +void adm1021_remote_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ +int prec=0; + struct adm1021_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + if (data->type == adm1023) { *nrels_mag = 3; } + else { *nrels_mag = 0; } + else if (operation == SENSORS_PROC_REAL_READ) { + adm1021_update_client(client); + results[0] = TEMP_FROM_REG(data->remote_temp_os); + results[1] = TEMP_FROM_REG(data->remote_temp_hyst); + results[2] = TEMP_FROM_REG(data->remote_temp); + if (data->type == adm1023) { + results[0]=results[0]*1000 + + ((data->remote_temp_os_prec >> 5) * 125); + results[1]=results[1]*1000 + + ((data->remote_temp_hyst_prec >> 5) * 125); + results[2]=(TEMP_FROM_REG(data->remote_temp_offset)*1000) + + ((data->remote_temp_offset_prec >> 5) * 125); + results[3]=TEMP_FROM_REG(data->remote_temp)*1000 + + ((data->remote_temp_prec >> 5) * 125); + *nrels_mag = 4; + } else { + *nrels_mag = 3; + } + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + if (data->type == adm1023) { + prec=((results[0]-((results[0]/1000)*1000))/125)<<5; + adm1021_write_value(client, + ADM1021_REG_REM_TOS_PREC, + prec); + results[0]=results[0]/1000; + data->remote_temp_os_prec=prec; + } + data->remote_temp_os = TEMP_TO_REG(results[0]); + adm1021_write_value(client, + ADM1021_REG_REMOTE_TOS_W, + data->remote_temp_os); + } + if (*nrels_mag >= 2) { + if (data->type == adm1023) { + prec=((results[1]-((results[1]/1000)*1000))/125)<<5; + adm1021_write_value(client, + ADM1021_REG_REM_THYST_PREC, + prec); + results[1]=results[1]/1000; + data->remote_temp_hyst_prec=prec; + } + data->remote_temp_hyst = TEMP_TO_REG(results[1]); + adm1021_write_value(client, + ADM1021_REG_REMOTE_THYST_W, + data->remote_temp_hyst); + } + if (*nrels_mag >= 3) { + if (data->type == adm1023) { + prec=((results[2]-((results[2]/1000)*1000))/125)<<5; + adm1021_write_value(client, + ADM1021_REG_REM_OFFSET_PREC, + prec); + results[2]=results[2]/1000; + data->remote_temp_offset_prec=prec; + data->remote_temp_offset=results[2]; + adm1021_write_value(client, + ADM1021_REG_REM_OFFSET, + data->remote_temp_offset); + } + } + } +} + +void adm1021_die_code(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct adm1021_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1021_update_client(client); + results[0] = data->die_code; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + /* Can't write to it */ + } +} + +void adm1021_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1021_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1021_update_client(client); + results[0] = data->alarms; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + /* Can't write to it */ + } +} + +int __init sensors_adm1021_init(void) +{ + int res; + + printk("adm1021.o version %s (%s)\n", LM_VERSION, LM_DATE); + adm1021_initialized = 0; + if ((res = i2c_add_driver(&adm1021_driver))) { + printk + ("adm1021.o: Driver registration failed, module not inserted.\n"); + adm1021_cleanup(); + return res; + } + adm1021_initialized++; + return 0; +} + +int __init adm1021_cleanup(void) +{ + int res; + + if (adm1021_initialized >= 1) { + if ((res = i2c_del_driver(&adm1021_driver))) { + printk + ("adm1021.o: Driver deregistration failed, module not removed.\n"); + return res; + } + adm1021_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("adm1021 driver"); + +int init_module(void) +{ + return sensors_adm1021_init(); +} + +int cleanup_module(void) +{ + return adm1021_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/adm1024.c Mon Mar 25 15:28:37 CET 2002 +++ linux/drivers/sensors/adm1024.c Mon Mar 25 15:28:37 CET 2002 @@ -0,0 +1,937 @@ +/* + adm1024.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Add by Ken Bowley from the adm1025.c written by + Gordon Wu and from adm9240.c written by + Copyright (c) 1999 Frodo Looijaard + and Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Supports the Analog Devices ADM1024. See doc/chips/adm1024 for details */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(adm1024); + +/* Many ADM1024 constants specified below */ + +#define ADM1024_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define ADM1024_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define ADM1024_REG_IN(nr) (0x20 + (nr)) + +/* The ADM1024 registers */ +#define ADM1024_REG_INT_TEMP_TRIP_SET 0x13 +#define ADM1024_REG_EXT_TEMP_TRIP_SET 0x14 +#define ADM1024_REG_TEST 0x15 +#define ADM1024_REG_CHANNEL_MODE 0x16 +#define ADM1024_REG_INT_TEMP_TRIP 0x17 /* read only */ +#define ADM1024_REG_EXT_TEMP_TRIP 0x18 /* read only */ +#define ADM1024_REG_ANALOG_OUT 0x19 +#define ADM1024_REG_AIN1_LOW_LIMIT 0x1A +#define ADM1024_REG_AIN2_LOW_LIMIT 0x1B +/* These are all read-only */ +#define ADM1024_REG_2_5V 0x20 /* 2.5V Measured Value/EXT Temp 2 */ +#define ADM1024_REG_VCCP1 0x21 +#define ADM1024_REG_3_3V 0x22 /* VCC Measured Value */ +#define ADM1024_REG_5V 0x23 +#define ADM1024_REG_12V 0x24 +#define ADM1024_REG_VCCP2 0x25 +#define ADM1024_REG_EXT_TEMP1 0x26 +#define ADM1024_REG_TEMP 0x27 +#define ADM1024_REG_FAN1 0x28 /* FAN1/AIN1 Value */ +#define ADM1024_REG_FAN2 0x29 /* FAN2/AIN2 Value */ +#define ADM1024_REG_COMPANY_ID 0x3E /* 0x41 for ADM1024 */ +#define ADM1024_REG_DIE_REV 0x3F +/* These are read/write */ +#define ADM1024_REG_2_5V_HIGH 0x2B /* 2.5V/Ext Temp2 High Limit */ +#define ADM1024_REG_2_5V_LOW 0x2C /* 2.5V/Ext Temp2 Low Limit */ +#define ADM1024_REG_VCCP1_HIGH 0x2D +#define ADM1024_REG_VCCP1_LOW 0x2E +#define ADM1024_REG_3_3V_HIGH 0x2F /* VCC High Limit */ +#define ADM1024_REG_3_3V_LOW 0x30 /* VCC Low Limit */ +#define ADM1024_REG_5V_HIGH 0x31 +#define ADM1024_REG_5V_LOW 0x32 +#define ADM1024_REG_12V_HIGH 0x33 +#define ADM1024_REG_12V_LOW 0x34 +#define ADM1024_REG_VCCP2_HIGH 0x35 +#define ADM1024_REG_VCCP2_LOW 0x36 +#define ADM1024_REG_EXT_TEMP1_HIGH 0x37 +#define ADM1024_REG_EXT_TEMP1_LOW 0x38 +#define ADM1024_REG_TOS 0x39 +#define ADM1024_REG_THYST 0x3A +#define ADM1024_REG_FAN1_MIN 0x3B +#define ADM1024_REG_FAN2_MIN 0x3C + +#define ADM1024_REG_CONFIG 0x40 +#define ADM1024_REG_INT1_STAT 0x41 +#define ADM1024_REG_INT2_STAT 0x42 +#define ADM1024_REG_INT1_MASK 0x43 +#define ADM1024_REG_INT2_MASK 0x44 + +#define ADM1024_REG_CHASSIS_CLEAR 0x46 +#define ADM1024_REG_VID_FAN_DIV 0x47 +#define ADM1024_REG_I2C_ADDR 0x48 +#define ADM1024_REG_VID4 0x49 +#define ADM1024_REG_CONFIG2 0x4A +#define ADM1024_REG_TEMP_CONFIG 0x4B +#define ADM1024_REG_EXTMODE1 0x4C /* Interupt Status Register Mirror No. 1 */ +#define ADM1024_REG_EXTMODE2 0x4D /* Interupt Status Register Mirror No. 2 */ + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255)) +#define IN_FROM_REG(val,nr) (val) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:\ + (val)==255?0:1350000/((div)*(val))) + +#define TEMP_FROM_REG(temp) \ + ((temp)<256?((((temp)&0x1fe) >> 1) * 10) + ((temp) & 1) * 5: \ + ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5) \ + +#define EXT_TEMP_FROM_REG(temp) (((temp)>0x80?(temp)-0x100:(temp))*10) + + +#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) + +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10), \ + 0,255) + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1))) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) + +/* Initial limits */ +#define ADM1024_INIT_IN_0 190 +#define ADM1024_INIT_IN_1 190 +#define ADM1024_INIT_IN_2 190 +#define ADM1024_INIT_IN_3 190 +#define ADM1024_INIT_IN_4 190 +#define ADM1024_INIT_IN_5 190 + +#define ADM1024_INIT_IN_PERCENTAGE 10 + +#define ADM1024_INIT_IN_MIN_0 \ + (ADM1024_INIT_IN_0 - ADM1024_INIT_IN_0 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MAX_0 \ + (ADM1024_INIT_IN_0 + ADM1024_INIT_IN_0 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MIN_1 \ + (ADM1024_INIT_IN_1 - ADM1024_INIT_IN_1 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MAX_1 \ + (ADM1024_INIT_IN_1 + ADM1024_INIT_IN_1 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MIN_2 \ + (ADM1024_INIT_IN_2 - ADM1024_INIT_IN_2 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MAX_2 \ + (ADM1024_INIT_IN_2 + ADM1024_INIT_IN_2 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MIN_3 \ + (ADM1024_INIT_IN_3 - ADM1024_INIT_IN_3 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MAX_3 \ + (ADM1024_INIT_IN_3 + ADM1024_INIT_IN_3 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MIN_4 \ + (ADM1024_INIT_IN_4 - ADM1024_INIT_IN_4 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MAX_4 \ + (ADM1024_INIT_IN_4 + ADM1024_INIT_IN_4 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MIN_5 \ + (ADM1024_INIT_IN_5 - ADM1024_INIT_IN_5 * ADM1024_INIT_IN_PERCENTAGE / 100) +#define ADM1024_INIT_IN_MAX_5 \ + (ADM1024_INIT_IN_5 + ADM1024_INIT_IN_5 * ADM1024_INIT_IN_PERCENTAGE / 100) + +#define ADM1024_INIT_FAN_MIN_1 3000 +#define ADM1024_INIT_FAN_MIN_2 3000 + +#define ADM1024_INIT_TEMP_OS_MAX 600 +#define ADM1024_INIT_TEMP_OS_HYST 500 +#define ADM1024_INIT_TEMP_HOT_MAX 700 +#define ADM1024_INIT_TEMP_HOT_HYST 600 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered ADM1024, we need to keep some data in memory. That + data is pointed to by adm1024_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new adm1024 client is + allocated. */ +struct adm1024_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[6]; /* Register value */ + u8 in_max[6]; /* Register value */ + u8 in_min[6]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + int temp; /* Temp, shifted right */ + u8 temp_os_max; /* Register value */ + u8 temp_os_hyst; /* Register value */ + int temp1; /* Ext Temp 1 */ + u8 temp1_os_max; + u8 temp1_os_hyst; + int temp2; /* Ext Temp 2 */ + u8 temp2_os_max; + u8 temp2_os_hyst; + u16 alarms; /* Register encoding, combined */ + u8 analog_out; /* Register value */ + u8 vid; /* Register value combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_adm1024_init(void); +static int __init adm1024_cleanup(void); + +static int adm1024_attach_adapter(struct i2c_adapter *adapter); +static int adm1024_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int adm1024_detach_client(struct i2c_client *client); +static int adm1024_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void adm1024_inc_use(struct i2c_client *client); +static void adm1024_dec_use(struct i2c_client *client); + +static int adm1024_read_value(struct i2c_client *client, u8 register); +static int adm1024_write_value(struct i2c_client *client, u8 register, + u8 value); +static void adm1024_update_client(struct i2c_client *client); +static void adm1024_init_client(struct i2c_client *client); + + +static void adm1024_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_temp1(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_temp2(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1024_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results); +static void adm1024_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* I choose here for semi-static ADM1024 allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +static int adm1024_id = 0; + +static struct i2c_driver adm1024_driver = { + /* name */ "ADM1024 sensor driver", + /* id */ I2C_DRIVERID_ADM1024, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &adm1024_attach_adapter, + /* detach_client */ &adm1024_detach_client, + /* command */ &adm1024_command, + /* inc_use */ &adm1024_inc_use, + /* dec_use */ &adm1024_dec_use +}; + +/* Used by adm1024_init/cleanup */ +static int __initdata adm1024_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected ADM1024. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table adm1024_dir_table_template[] = { + {ADM1024_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_in}, + {ADM1024_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_in}, + {ADM1024_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_in}, + {ADM1024_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_in}, + {ADM1024_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_in}, + {ADM1024_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_in}, + {ADM1024_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_fan}, + {ADM1024_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_fan}, + {ADM1024_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_temp}, + {ADM1024_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_temp1}, + {ADM1024_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_temp2}, + {ADM1024_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_fan_div}, + {ADM1024_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_alarms}, + {ADM1024_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_analog_out}, + {ADM1024_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1024_vid}, + {0} +}; + +int adm1024_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, adm1024_detect); +} + +static int adm1024_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct adm1024_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("adm1024.o: adm1024_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access adm1024_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct adm1024_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct adm1024_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &adm1024_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if((adm1024_read_value(new_client, ADM1024_REG_CONFIG) & 0x80) != 0x00) + goto ERROR1; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = adm1024_read_value(new_client, ADM1024_REG_COMPANY_ID); + if (i == 0x41) + kind = adm1024; + else { + if (kind == 0) + printk + ("adm1024.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == adm1024) { + type_name = "adm1024"; + client_name = "ADM1024 chip"; + } else { +#ifdef DEBUG + printk("adm1024.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = adm1024_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + adm1024_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the ADM1024 chip */ + adm1024_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int adm1024_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct adm1024_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("adm1024.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; + +} + +/* No commands defined yet */ +int adm1024_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void adm1024_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void adm1024_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +int adm1024_read_value(struct i2c_client *client, u8 reg) +{ + return 0xFF & i2c_smbus_read_byte_data(client, reg); +} + +int adm1024_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new ADM1024. It should set limits, etc. */ +void adm1024_init_client(struct i2c_client *client) +{ + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others. This makes most other + initializations unnecessary */ + adm1024_write_value(client, ADM1024_REG_CONFIG, 0x80); + + adm1024_write_value(client, ADM1024_REG_IN_MIN(0), + IN_TO_REG(ADM1024_INIT_IN_MIN_0, 0)); + adm1024_write_value(client, ADM1024_REG_IN_MAX(0), + IN_TO_REG(ADM1024_INIT_IN_MAX_0, 0)); + adm1024_write_value(client, ADM1024_REG_IN_MIN(1), + IN_TO_REG(ADM1024_INIT_IN_MIN_1, 1)); + adm1024_write_value(client, ADM1024_REG_IN_MAX(1), + IN_TO_REG(ADM1024_INIT_IN_MAX_1, 1)); + adm1024_write_value(client, ADM1024_REG_IN_MIN(2), + IN_TO_REG(ADM1024_INIT_IN_MIN_2, 2)); + adm1024_write_value(client, ADM1024_REG_IN_MAX(2), + IN_TO_REG(ADM1024_INIT_IN_MAX_2, 2)); + adm1024_write_value(client, ADM1024_REG_IN_MIN(3), + IN_TO_REG(ADM1024_INIT_IN_MIN_3, 3)); + adm1024_write_value(client, ADM1024_REG_IN_MAX(3), + IN_TO_REG(ADM1024_INIT_IN_MAX_3, 3)); + adm1024_write_value(client, ADM1024_REG_IN_MIN(4), + IN_TO_REG(ADM1024_INIT_IN_MIN_4, 4)); + adm1024_write_value(client, ADM1024_REG_IN_MAX(4), + IN_TO_REG(ADM1024_INIT_IN_MAX_4, 4)); + adm1024_write_value(client, ADM1024_REG_IN_MIN(5), + IN_TO_REG(ADM1024_INIT_IN_MIN_5, 5)); + adm1024_write_value(client, ADM1024_REG_IN_MAX(5), + IN_TO_REG(ADM1024_INIT_IN_MAX_5, 5)); + adm1024_write_value(client, ADM1024_REG_FAN1_MIN, + FAN_TO_REG(ADM1024_INIT_FAN_MIN_1, 2)); + adm1024_write_value(client, ADM1024_REG_FAN2_MIN, + FAN_TO_REG(ADM1024_INIT_FAN_MIN_2, 2)); + adm1024_write_value(client, ADM1024_REG_TOS, + TEMP_LIMIT_TO_REG(ADM1024_INIT_TEMP_OS_MAX)); + adm1024_write_value(client, ADM1024_REG_THYST, + TEMP_LIMIT_TO_REG(ADM1024_INIT_TEMP_OS_HYST)); + adm1024_write_value(client, ADM1024_REG_EXT_TEMP1_HIGH, + TEMP_LIMIT_TO_REG(ADM1024_INIT_TEMP_OS_MAX)); + adm1024_write_value(client, ADM1024_REG_EXT_TEMP1_LOW, + TEMP_LIMIT_TO_REG(ADM1024_INIT_TEMP_OS_HYST)); + adm1024_write_value(client, ADM1024_REG_2_5V_HIGH, + TEMP_LIMIT_TO_REG(ADM1024_INIT_TEMP_OS_MAX)); + adm1024_write_value(client, ADM1024_REG_2_5V_LOW, + TEMP_LIMIT_TO_REG(ADM1024_INIT_TEMP_OS_HYST)); + adm1024_write_value(client, ADM1024_REG_TEMP_CONFIG, 0x00); + + /* Enable temperature channel 2 */ + adm1024_write_value(client, ADM1024_REG_CHANNEL_MODE, adm1024_read_value(client, ADM1024_REG_CHANNEL_MODE) | 0x04); + + /* Start monitoring */ + adm1024_write_value(client, ADM1024_REG_CONFIG, 0x07); +} + +void adm1024_update_client(struct i2c_client *client) +{ + struct adm1024_data *data = client->data; + u8 i; + + down(&data->update_lock); + + if ( + (jiffies - data->last_updated > + (data->type == adm1024 ? HZ / 2 : HZ * 2)) + || (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting adm1024 update\n"); +#endif + for (i = 0; i <= 5; i++) { + data->in[i] = + adm1024_read_value(client, ADM1024_REG_IN(i)); + data->in_min[i] = + adm1024_read_value(client, + ADM1024_REG_IN_MIN(i)); + data->in_max[i] = + adm1024_read_value(client, + ADM1024_REG_IN_MAX(i)); + } + data->fan[0] = + adm1024_read_value(client, ADM1024_REG_FAN1); + data->fan_min[0] = + adm1024_read_value(client, ADM1024_REG_FAN1_MIN); + data->fan[1] = + adm1024_read_value(client, ADM1024_REG_FAN2); + data->fan_min[1] = + adm1024_read_value(client, ADM1024_REG_FAN2_MIN); + data->temp = + (adm1024_read_value(client, ADM1024_REG_TEMP) << 1) + + ((adm1024_read_value + (client, ADM1024_REG_TEMP_CONFIG) & 0x80) >> 7); + data->temp_os_max = + adm1024_read_value(client, ADM1024_REG_TOS); + data->temp_os_hyst = + adm1024_read_value(client, ADM1024_REG_THYST); + data->temp1 = + adm1024_read_value(client, ADM1024_REG_EXT_TEMP1); + data->temp1_os_max = + adm1024_read_value(client, ADM1024_REG_EXT_TEMP1_HIGH); + data->temp1_os_hyst = + adm1024_read_value(client, ADM1024_REG_EXT_TEMP1_LOW); + data->temp2 = + adm1024_read_value(client, ADM1024_REG_2_5V); + data->temp2_os_max = + adm1024_read_value(client, ADM1024_REG_2_5V_HIGH); + data->temp2_os_hyst = + adm1024_read_value(client, ADM1024_REG_2_5V_LOW); + + i = adm1024_read_value(client, ADM1024_REG_VID_FAN_DIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + data->vid = i & 0x0f; + data->vid |= + (adm1024_read_value(client, ADM1024_REG_VID4) & 0x01) + << 4; + + data->alarms = + adm1024_read_value(client, + ADM1024_REG_INT1_STAT) + + (adm1024_read_value(client, ADM1024_REG_INT2_STAT) << + 8); + data->analog_out = + adm1024_read_value(client, ADM1024_REG_ANALOG_OUT); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void adm1024_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + + int scales[6] = { 250, 225, 330, 500, 1200, 270 }; + + struct adm1024_data *data = client->data; + int nr = ctl_name - ADM1024_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = + IN_FROM_REG(data->in_min[nr], nr) * scales[nr] / 192; + results[1] = + IN_FROM_REG(data->in_max[nr], nr) * scales[nr] / 192; + results[2] = + IN_FROM_REG(data->in[nr], nr) * scales[nr] / 192; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = + IN_TO_REG((results[0] * 192) / scales[nr], nr); + adm1024_write_value(client, ADM1024_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = + IN_TO_REG((results[1] * 192) / scales[nr], nr); + adm1024_write_value(client, ADM1024_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void adm1024_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + int nr = ctl_name - ADM1024_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data-> + fan_div[nr - 1])); + results[1] = + FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG + (data-> + fan_div[nr - + 1])); + adm1024_write_value(client, + nr == + 1 ? ADM1024_REG_FAN1_MIN : + ADM1024_REG_FAN2_MIN, + data->fan_min[nr - 1]); + } + } +} + + +void adm1024_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->temp_os_max); + results[1] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_os_max = TEMP_LIMIT_TO_REG(results[0]); + adm1024_write_value(client, ADM1024_REG_TOS, + data->temp_os_max); + } + if (*nrels_mag >= 2) { + data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[1]); + adm1024_write_value(client, ADM1024_REG_THYST, + data->temp_os_hyst); + } + } +} + +void adm1024_temp1(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->temp1_os_max); + results[1] = TEMP_LIMIT_FROM_REG(data->temp1_os_hyst); + results[2] = EXT_TEMP_FROM_REG(data->temp1); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp1_os_max = TEMP_LIMIT_TO_REG(results[0]); + adm1024_write_value(client, ADM1024_REG_EXT_TEMP1_HIGH, + data->temp1_os_max); + } + if (*nrels_mag >= 2) { + data->temp1_os_hyst = TEMP_LIMIT_TO_REG(results[1]); + adm1024_write_value(client, ADM1024_REG_EXT_TEMP1_LOW, + data->temp1_os_hyst); + } + } +} + +void adm1024_temp2(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->temp2_os_max); + results[1] = TEMP_LIMIT_FROM_REG(data->temp2_os_hyst); + results[2] = EXT_TEMP_FROM_REG(data->temp2); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp2_os_max = TEMP_LIMIT_TO_REG(results[0]); + adm1024_write_value(client, ADM1024_REG_2_5V_HIGH, + data->temp2_os_max); + } + if (*nrels_mag >= 2) { + data->temp2_os_hyst = TEMP_LIMIT_TO_REG(results[1]); + adm1024_write_value(client, ADM1024_REG_2_5V_LOW, + data->temp2_os_hyst); + } + } +} + +void adm1024_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void adm1024_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = adm1024_read_value(client, ADM1024_REG_VID_FAN_DIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan_div[1] << 6); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div[0] << 4); + adm1024_write_value(client, + ADM1024_REG_VID_FAN_DIV, old); + } + } +} + +void adm1024_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = data->analog_out; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->analog_out = results[0]; + adm1024_write_value(client, ADM1024_REG_ANALOG_OUT, + data->analog_out); + } + } +} + +void adm1024_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1024_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1024_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +int __init sensors_adm1024_init(void) +{ + int res; + + printk("adm1024.o version %s (%s)\n", LM_VERSION, LM_DATE); + adm1024_initialized = 0; + + if ((res = i2c_add_driver(&adm1024_driver))) { + printk + ("adm1024.o: Driver registration failed, module not inserted.\n"); + adm1024_cleanup(); + return res; + } + adm1024_initialized++; + return 0; +} + +int __init adm1024_cleanup(void) +{ + int res; + + if (adm1024_initialized >= 1) { + if ((res = i2c_del_driver(&adm1024_driver))) { + printk + ("adm1024.o: Driver deregistration failed, module not removed.\n"); + return res; + } + adm1024_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("ADM1024 driver"); + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +int init_module(void) +{ + return sensors_adm1024_init(); +} + +int cleanup_module(void) +{ + return adm1024_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/adm1025.c Mon Mar 25 15:28:37 CET 2002 +++ linux/drivers/sensors/adm1025.c Mon Mar 25 15:28:37 CET 2002 @@ -0,0 +1,756 @@ +/* + adm1025.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Add by Gordon Wu according to the adm9240.c written by + Frodo Looijaard + and Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Supports the Analog Devices ADM1025. See doc/chips/adm1025 for details */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(adm1025); + +/* Many ADM1025 constants specified below */ + +#define ADM1025_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define ADM1025_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define ADM1025_REG_IN(nr) (0x20 + (nr)) + +/* The ADM1025 registers */ +#define ADM1025_REG_TEST 0x15 +/* These are all read-only */ +#define ADM1025_REG_2_5V 0x20 +#define ADM1025_REG_VCCP1 0x21 +#define ADM1025_REG_3_3V 0x22 +#define ADM1025_REG_5V 0x23 +#define ADM1025_REG_12V 0x24 +#define ADM1025_REG_VCC 0x25 +#define ADM1025_REG_RTEMP 0x26 +#define ADM1025_REG_TEMP 0x27 +#define ADM1025_REG_COMPANY_ID 0x3E /* 0x41 for ADM1025 */ +#define ADM1025_REG_DIE_REV 0x3F +/* These are read/write */ +#define ADM1025_REG_2_5V_HIGH 0x2B +#define ADM1025_REG_2_5V_LOW 0x2C +#define ADM1025_REG_VCCP1_HIGH 0x2D +#define ADM1025_REG_VCCP1_LOW 0x2E +#define ADM1025_REG_3_3V_HIGH 0x2F +#define ADM1025_REG_3_3V_LOW 0x30 +#define ADM1025_REG_5V_HIGH 0x31 +#define ADM1025_REG_5V_LOW 0x32 +#define ADM1025_REG_12V_HIGH 0x33 +#define ADM1025_REG_12V_LOW 0x34 +#define ADM1025_REG_VCC_HIGH 0x35 +#define ADM1025_REG_VCC_LOW 0x36 +#define ADM1025_REG_RTEMP_HIGH 0x37 +#define ADM1025_REG_RTEMP_LOW 0x38 +#define ADM1025_REG_TEMP_HIGH 0x39 +#define ADM1025_REG_TEMP_LOW 0x3A + +#define ADM1025_REG_CONFIG 0x40 +#define ADM1025_REG_INT1_STAT 0x41 +#define ADM1025_REG_INT2_STAT 0x42 + +#define ADM1025_REG_VID 0x47 +#define ADM1025_REG_VID4 0x49 + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255)) +#define IN_FROM_REG(val,nr) (val) + +#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) +#define TEMP_LIMIT_FROM_REG(val) TEMP_FROM_REG(val) +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10), 0, 255) + +#define ALARMS_FROM_REG(val) (val) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) + +/* Initial limits */ +#define ADM1025_INIT_IN_0 190 +#define ADM1025_INIT_IN_1 190 +#define ADM1025_INIT_IN_2 190 +#define ADM1025_INIT_IN_3 190 +#define ADM1025_INIT_IN_4 190 +#define ADM1025_INIT_IN_5 190 + +#define ADM1025_INIT_IN_PERCENTAGE 10 + +#define ADM1025_INIT_IN_MIN_0 \ + (ADM1025_INIT_IN_0 - ADM1025_INIT_IN_0 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MAX_0 \ + (ADM1025_INIT_IN_0 + ADM1025_INIT_IN_0 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MIN_1 \ + (ADM1025_INIT_IN_1 - ADM1025_INIT_IN_1 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MAX_1 \ + (ADM1025_INIT_IN_1 + ADM1025_INIT_IN_1 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MIN_2 \ + (ADM1025_INIT_IN_2 - ADM1025_INIT_IN_2 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MAX_2 \ + (ADM1025_INIT_IN_2 + ADM1025_INIT_IN_2 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MIN_3 \ + (ADM1025_INIT_IN_3 - ADM1025_INIT_IN_3 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MAX_3 \ + (ADM1025_INIT_IN_3 + ADM1025_INIT_IN_3 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MIN_4 \ + (ADM1025_INIT_IN_4 - ADM1025_INIT_IN_4 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MAX_4 \ + (ADM1025_INIT_IN_4 + ADM1025_INIT_IN_4 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MIN_5 \ + (ADM1025_INIT_IN_5 - ADM1025_INIT_IN_5 * ADM1025_INIT_IN_PERCENTAGE / 100) +#define ADM1025_INIT_IN_MAX_5 \ + (ADM1025_INIT_IN_5 + ADM1025_INIT_IN_5 * ADM1025_INIT_IN_PERCENTAGE / 100) + +#define ADM1025_INIT_RTEMP_MAX 600 +#define ADM1025_INIT_RTEMP_MIN 0 +#define ADM1025_INIT_TEMP_MAX 600 +#define ADM1025_INIT_TEMP_MIN 0 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered ADM1025, we need to keep some data in memory. That + data is pointed to by adm1025_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new adm1025 client is + allocated. */ +struct adm1025_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[6]; /* Register value */ + u8 in_max[6]; /* Register value */ + u8 in_min[6]; /* Register value */ + u8 rtemp; /* Register value */ + u8 rtemp_max; /* Register value */ + u8 rtemp_min; /* Register value */ + u8 temp; /* Register value */ + u8 temp_max; /* Register value */ + u8 temp_min; /* Register value */ + u16 alarms; /* Register encoding, combined */ + u8 analog_out; /* Register value */ + u8 vid; /* Register value combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_adm1025_init(void); +static int __init adm1025_cleanup(void); + +static int adm1025_attach_adapter(struct i2c_adapter *adapter); +static int adm1025_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int adm1025_detach_client(struct i2c_client *client); +static int adm1025_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void adm1025_inc_use(struct i2c_client *client); +static void adm1025_dec_use(struct i2c_client *client); + +static int adm1025_read_value(struct i2c_client *client, u8 register); +static int adm1025_write_value(struct i2c_client *client, u8 register, + u8 value); +static void adm1025_update_client(struct i2c_client *client); +static void adm1025_init_client(struct i2c_client *client); + + +static void adm1025_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1025_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1025_rm_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm1025_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +/*static void adm1025_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results);*/ +static void adm1025_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* I choose here for semi-static ADM1025 allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +static int adm1025_id = 0; + +static struct i2c_driver adm1025_driver = { + /* name */ "ADM1025 sensor driver", + /* id */ I2C_DRIVERID_ADM1025, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &adm1025_attach_adapter, + /* detach_client */ &adm1025_detach_client, + /* command */ &adm1025_command, + /* inc_use */ &adm1025_inc_use, + /* dec_use */ &adm1025_dec_use +}; + +/* Used by adm1025_init/cleanup */ +static int __initdata adm1025_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected ADM1025. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table adm1025_dir_table_template[] = { + {ADM1025_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_in}, + {ADM1025_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_in}, + {ADM1025_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_in}, + {ADM1025_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_in}, + {ADM1025_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_in}, + {ADM1025_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_in}, + {ADM1025_SYSCTL_RTEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_rm_temp}, + {ADM1025_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_temp}, + {ADM1025_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_alarms}, +/* {ADM1025_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_analog_out},*/ + {ADM1025_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_vid}, + {0} +}; + +int adm1025_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, adm1025_detect); +} + +static int adm1025_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct adm1025_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("adm1025.o: adm1025_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access adm1025_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct adm1025_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct adm1025_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &adm1025_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if((adm1025_read_value(new_client,ADM1025_REG_CONFIG) & 0x80) != 0x00) + goto ERROR1; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = adm1025_read_value(new_client, ADM1025_REG_COMPANY_ID); + if (i == 0x41) + kind = adm1025; + else { + if (kind == 0) + printk + ("adm1025.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == adm1025) { + type_name = "adm1025"; + client_name = "ADM1025 chip"; + } else { +#ifdef DEBUG + printk("adm1025.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = adm1025_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + adm1025_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the ADM1025 chip */ + adm1025_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int adm1025_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct adm1025_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("adm1025.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; + +} + +/* No commands defined yet */ +int adm1025_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void adm1025_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void adm1025_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +int adm1025_read_value(struct i2c_client *client, u8 reg) +{ + return 0xFF & i2c_smbus_read_byte_data(client, reg); +} + +int adm1025_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new ADM1025. It should set limits, etc. */ +void adm1025_init_client(struct i2c_client *client) +{ + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others. This makes most other + initializations unnecessary */ + adm1025_write_value(client, ADM1025_REG_CONFIG, 0x80); + + adm1025_write_value(client, ADM1025_REG_IN_MIN(0), + IN_TO_REG(ADM1025_INIT_IN_MIN_0, 0)); + adm1025_write_value(client, ADM1025_REG_IN_MAX(0), + IN_TO_REG(ADM1025_INIT_IN_MAX_0, 0)); + adm1025_write_value(client, ADM1025_REG_IN_MIN(1), + IN_TO_REG(ADM1025_INIT_IN_MIN_1, 1)); + adm1025_write_value(client, ADM1025_REG_IN_MAX(1), + IN_TO_REG(ADM1025_INIT_IN_MAX_1, 1)); + adm1025_write_value(client, ADM1025_REG_IN_MIN(2), + IN_TO_REG(ADM1025_INIT_IN_MIN_2, 2)); + adm1025_write_value(client, ADM1025_REG_IN_MAX(2), + IN_TO_REG(ADM1025_INIT_IN_MAX_2, 2)); + adm1025_write_value(client, ADM1025_REG_IN_MIN(3), + IN_TO_REG(ADM1025_INIT_IN_MIN_3, 3)); + adm1025_write_value(client, ADM1025_REG_IN_MAX(3), + IN_TO_REG(ADM1025_INIT_IN_MAX_3, 3)); + adm1025_write_value(client, ADM1025_REG_IN_MIN(4), + IN_TO_REG(ADM1025_INIT_IN_MIN_4, 4)); + adm1025_write_value(client, ADM1025_REG_IN_MAX(4), + IN_TO_REG(ADM1025_INIT_IN_MAX_4, 4)); + adm1025_write_value(client, ADM1025_REG_IN_MIN(5), + IN_TO_REG(ADM1025_INIT_IN_MIN_5, 5)); + adm1025_write_value(client, ADM1025_REG_IN_MAX(5), + IN_TO_REG(ADM1025_INIT_IN_MAX_5, 5)); + + adm1025_write_value(client, ADM1025_REG_RTEMP_HIGH, + TEMP_LIMIT_TO_REG(ADM1025_INIT_RTEMP_MAX)); + adm1025_write_value(client, ADM1025_REG_RTEMP_LOW, + TEMP_LIMIT_TO_REG(ADM1025_INIT_RTEMP_MIN)); + adm1025_write_value(client, ADM1025_REG_TEMP_HIGH, + TEMP_LIMIT_TO_REG(ADM1025_INIT_TEMP_MAX)); + adm1025_write_value(client, ADM1025_REG_TEMP_LOW, + TEMP_LIMIT_TO_REG(ADM1025_INIT_TEMP_MIN)); + + /* Start monitoring */ + adm1025_write_value(client, ADM1025_REG_CONFIG, 0x01); +} + +void adm1025_update_client(struct i2c_client *client) +{ + struct adm1025_data *data = client->data; + u8 i; + + down(&data->update_lock); + + if ( + (jiffies - data->last_updated > + (data->type == adm1025 ? HZ / 2 : HZ * 2)) + || (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting adm1025 update\n"); +#endif + for (i = 0; i <= 5; i++) { + data->in[i] = + adm1025_read_value(client, ADM1025_REG_IN(i)); + data->in_min[i] = + adm1025_read_value(client, + ADM1025_REG_IN_MIN(i)); + data->in_max[i] = + adm1025_read_value(client, + ADM1025_REG_IN_MAX(i)); + } + data->temp = + adm1025_read_value(client, ADM1025_REG_TEMP); + data->rtemp = + adm1025_read_value(client, ADM1025_REG_RTEMP); +#ifdef DEBUG + printk("The temp is %2x\n",data->temp); +#endif + data->temp_max = + adm1025_read_value(client, ADM1025_REG_TEMP_HIGH); + data->temp_min = + adm1025_read_value(client, ADM1025_REG_TEMP_LOW); + data->rtemp_max = + adm1025_read_value(client, ADM1025_REG_RTEMP_HIGH); + data->rtemp_min = + adm1025_read_value(client, ADM1025_REG_RTEMP_LOW); + + i = adm1025_read_value(client, ADM1025_REG_VID); + data->vid = i & 0x0f; + data->vid |= + (adm1025_read_value(client, ADM1025_REG_VID4) & 0x01) + << 4; + + data->alarms = + adm1025_read_value(client, + ADM1025_REG_INT1_STAT) + + (adm1025_read_value(client, ADM1025_REG_INT2_STAT) << + 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void adm1025_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + + int scales[6] = { 250, 225, 330, 500, 1200, 330 }; + + struct adm1025_data *data = client->data; + int nr = ctl_name - ADM1025_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = + IN_FROM_REG(data->in_min[nr], nr) * scales[nr] / 192; + results[1] = + IN_FROM_REG(data->in_max[nr], nr) * scales[nr] / 192; + results[2] = + IN_FROM_REG(data->in[nr], nr) * scales[nr] / 192; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = + IN_TO_REG((results[0] * 192) / scales[nr], nr); + adm1025_write_value(client, ADM1025_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = + IN_TO_REG((results[1] * 192) / scales[nr], nr); + adm1025_write_value(client, ADM1025_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + + +void adm1025_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1025_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->temp_max); + results[1] = TEMP_LIMIT_FROM_REG(data->temp_min); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_max = TEMP_LIMIT_TO_REG(results[0]); + adm1025_write_value(client, ADM1025_REG_TEMP_HIGH, + data->temp_max); + } + if (*nrels_mag >= 2) { + data->temp_min = TEMP_LIMIT_TO_REG(results[1]); + adm1025_write_value(client, ADM1025_REG_TEMP_LOW, + data->temp_min); + } + } +} + +void adm1025_rm_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1025_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->rtemp_max); + results[1] = TEMP_LIMIT_FROM_REG(data->rtemp_min); + results[2] = TEMP_FROM_REG(data->rtemp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->rtemp_max = TEMP_LIMIT_TO_REG(results[0]); + adm1025_write_value(client, ADM1025_REG_RTEMP_HIGH, + data->rtemp_max); + } + if (*nrels_mag >= 2) { + data->rtemp_min = TEMP_LIMIT_TO_REG(results[1]); + adm1025_write_value(client, ADM1025_REG_RTEMP_LOW, + data->rtemp_min); + } + } +} + +void adm1025_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1025_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} +/* +void adm1025_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct adm1025_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = data->analog_out; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->analog_out = results[0]; + adm1025_write_value(client, ADM1025_REG_ANALOG_OUT, + data->analog_out); + } + } +} +*/ + +void adm1025_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm1025_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +int __init sensors_adm1025_init(void) +{ + int res; + + printk("adm1025.o version %s (%s)\n", LM_VERSION, LM_DATE); + adm1025_initialized = 0; + + if ((res = i2c_add_driver(&adm1025_driver))) { + printk + ("adm1025.o: Driver registration failed, module not inserted.\n"); + adm1025_cleanup(); + return res; + } + adm1025_initialized++; + return 0; +} + +int __init adm1025_cleanup(void) +{ + int res; + + if (adm1025_initialized >= 1) { + if ((res = i2c_del_driver(&adm1025_driver))) { + printk + ("adm1025.o: Driver deregistration failed, module not removed.\n"); + return res; + } + adm1025_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("ADM1025 driver"); + +int init_module(void) +{ + return sensors_adm1025_init(); +} + +int cleanup_module(void) +{ + return adm1025_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/adm9240.c Mon Mar 25 15:28:37 CET 2002 +++ linux/drivers/sensors/adm9240.c Mon Mar 25 15:28:37 CET 2002 @@ -0,0 +1,878 @@ +/* + adm9240.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1999 Frodo Looijaard + and Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Supports ADM9240, DS1780, and LM81. See doc/chips/adm9240 for details */ + +/* + A couple notes about the ADM9240: + +* It claims to be 'LM7x' register compatible. This must be in reference + to only the LM78, because it is missing stuff to emulate LM75's as well. + (like the Winbond W83781 does) + +* This driver was written from rev. 0 of the PDF, but it seems well + written and complete (unlike the W83781 which is horrible and has + supposidly gone through a few revisions.. rev 0 of that one must + have been in crayon on construction paper...) + +* All analog inputs can range from 0 to 2.5, eventhough some inputs are + marked as being 5V, 12V, etc. I don't have any real voltages going + into my prototype, so I'm not sure that things are computed right, + but at least the limits seem to be working OK. + +* Another curiousity is that the fan_div seems to be read-only. I.e., + any written value to it doesn't seem to make any difference. The + fan_div seems to be 'stuck' at 2 (which isn't a bad value in most cases). + + + --Phil + +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x2c, 0x2f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_3(adm9240, ds1780, lm81); + +/* Many ADM9240 constants specified below */ + +#define ADM9240_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define ADM9240_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define ADM9240_REG_IN(nr) (0x20 + (nr)) + +/* The ADM9240 registers */ +#define ADM9240_REG_TEST 0x15 +#define ADM9240_REG_ANALOG_OUT 0x19 +/* These are all read-only */ +#define ADM9240_REG_2_5V 0x20 +#define ADM9240_REG_VCCP1 0x21 +#define ADM9240_REG_3_3V 0x22 +#define ADM9240_REG_5V 0x23 +#define ADM9240_REG_12V 0x24 +#define ADM9240_REG_VCCP2 0x25 +#define ADM9240_REG_TEMP 0x27 +#define ADM9240_REG_FAN1 0x28 +#define ADM9240_REG_FAN2 0x29 +#define ADM9240_REG_COMPANY_ID 0x3E /* 0x23 for ADM9240; 0xDA for DS1780 */ + /* 0x01 for LM81 */ +#define ADM9240_REG_DIE_REV 0x3F +/* These are read/write */ +#define ADM9240_REG_2_5V_HIGH 0x2B +#define ADM9240_REG_2_5V_LOW 0x2C +#define ADM9240_REG_VCCP1_HIGH 0x2D +#define ADM9240_REG_VCCP1_LOW 0x2E +#define ADM9240_REG_3_3V_HIGH 0x2F +#define ADM9240_REG_3_3V_LOW 0x30 +#define ADM9240_REG_5V_HIGH 0x31 +#define ADM9240_REG_5V_LOW 0x32 +#define ADM9240_REG_12V_HIGH 0x33 +#define ADM9240_REG_12V_LOW 0x34 +#define ADM9240_REG_VCCP2_HIGH 0x35 +#define ADM9240_REG_VCCP2_LOW 0x36 +#define ADM9240_REG_TCRIT_LIMIT 0x37 /* LM81 only - not supported */ +#define ADM9240_REG_LOW_LIMIT 0x38 /* LM81 only - not supported */ +#define ADM9240_REG_TOS 0x39 +#define ADM9240_REG_THYST 0x3A +#define ADM9240_REG_FAN1_MIN 0x3B +#define ADM9240_REG_FAN2_MIN 0x3C + +#define ADM9240_REG_CONFIG 0x40 +#define ADM9240_REG_INT1_STAT 0x41 +#define ADM9240_REG_INT2_STAT 0x42 +#define ADM9240_REG_INT1_MASK 0x43 +#define ADM9240_REG_INT2_MASK 0x44 + +#define ADM9240_REG_COMPAT 0x45 /* dummy compat. register for other drivers? */ +#define ADM9240_REG_CHASSIS_CLEAR 0x46 +#define ADM9240_REG_VID_FAN_DIV 0x47 +#define ADM9240_REG_I2C_ADDR 0x48 +#define ADM9240_REG_VID4 0x49 +#define ADM9240_REG_TEMP_CONFIG 0x4B +#define ADM9240_REG_EXTMODE1 0x4C /* LM81 only - not supported */ +#define ADM9240_REG_EXTMODE2 0x4D /* LM81 only - not supported */ + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255)) +#define IN_FROM_REG(val,nr) (val) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:\ + (val)==255?0:1350000/((div)*(val))) + +#define TEMP_FROM_REG(temp) \ + ((temp)<256?((((temp)&0x1fe) >> 1) * 10) + ((temp) & 1) * 5: \ + ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5) \ + +#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) + +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10), \ + 0,255) + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1))) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) + +/* Initial limits */ +#define ADM9240_INIT_IN_0 190 +#define ADM9240_INIT_IN_1 190 +#define ADM9240_INIT_IN_2 190 +#define ADM9240_INIT_IN_3 190 +#define ADM9240_INIT_IN_4 190 +#define ADM9240_INIT_IN_5 190 + +#define ADM9240_INIT_IN_PERCENTAGE 10 + +#define ADM9240_INIT_IN_MIN_0 \ + (ADM9240_INIT_IN_0 - ADM9240_INIT_IN_0 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MAX_0 \ + (ADM9240_INIT_IN_0 + ADM9240_INIT_IN_0 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MIN_1 \ + (ADM9240_INIT_IN_1 - ADM9240_INIT_IN_1 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MAX_1 \ + (ADM9240_INIT_IN_1 + ADM9240_INIT_IN_1 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MIN_2 \ + (ADM9240_INIT_IN_2 - ADM9240_INIT_IN_2 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MAX_2 \ + (ADM9240_INIT_IN_2 + ADM9240_INIT_IN_2 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MIN_3 \ + (ADM9240_INIT_IN_3 - ADM9240_INIT_IN_3 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MAX_3 \ + (ADM9240_INIT_IN_3 + ADM9240_INIT_IN_3 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MIN_4 \ + (ADM9240_INIT_IN_4 - ADM9240_INIT_IN_4 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MAX_4 \ + (ADM9240_INIT_IN_4 + ADM9240_INIT_IN_4 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MIN_5 \ + (ADM9240_INIT_IN_5 - ADM9240_INIT_IN_5 * ADM9240_INIT_IN_PERCENTAGE / 100) +#define ADM9240_INIT_IN_MAX_5 \ + (ADM9240_INIT_IN_5 + ADM9240_INIT_IN_5 * ADM9240_INIT_IN_PERCENTAGE / 100) + +#define ADM9240_INIT_FAN_MIN_1 3000 +#define ADM9240_INIT_FAN_MIN_2 3000 + +#define ADM9240_INIT_TEMP_OS_MAX 600 +#define ADM9240_INIT_TEMP_OS_HYST 500 +#define ADM9240_INIT_TEMP_HOT_MAX 700 +#define ADM9240_INIT_TEMP_HOT_HYST 600 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered ADM9240, we need to keep some data in memory. That + data is pointed to by adm9240_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new adm9240 client is + allocated. */ +struct adm9240_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[6]; /* Register value */ + u8 in_max[6]; /* Register value */ + u8 in_min[6]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + int temp; /* Temp, shifted right */ + u8 temp_os_max; /* Register value */ + u8 temp_os_hyst; /* Register value */ + u16 alarms; /* Register encoding, combined */ + u8 analog_out; /* Register value */ + u8 vid; /* Register value combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_adm9240_init(void); +static int __init adm9240_cleanup(void); + +static int adm9240_attach_adapter(struct i2c_adapter *adapter); +static int adm9240_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int adm9240_detach_client(struct i2c_client *client); +static int adm9240_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void adm9240_inc_use(struct i2c_client *client); +static void adm9240_dec_use(struct i2c_client *client); + +static int adm9240_read_value(struct i2c_client *client, u8 register); +static int adm9240_write_value(struct i2c_client *client, u8 register, + u8 value); +static void adm9240_update_client(struct i2c_client *client); +static void adm9240_init_client(struct i2c_client *client); + + +static void adm9240_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm9240_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm9240_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm9240_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm9240_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void adm9240_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results); +static void adm9240_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* I choose here for semi-static ADM9240 allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +static int adm9240_id = 0; + +static struct i2c_driver adm9240_driver = { + /* name */ "ADM9240 sensor driver", + /* id */ I2C_DRIVERID_ADM9240, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &adm9240_attach_adapter, + /* detach_client */ &adm9240_detach_client, + /* command */ &adm9240_command, + /* inc_use */ &adm9240_inc_use, + /* dec_use */ &adm9240_dec_use +}; + +/* Used by adm9240_init/cleanup */ +static int __initdata adm9240_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected ADM9240. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table adm9240_dir_table_template[] = { + {ADM9240_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_in}, + {ADM9240_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_in}, + {ADM9240_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_in}, + {ADM9240_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_in}, + {ADM9240_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_in}, + {ADM9240_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_in}, + {ADM9240_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_fan}, + {ADM9240_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_fan}, + {ADM9240_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_temp}, + {ADM9240_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_fan_div}, + {ADM9240_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_alarms}, + {ADM9240_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_analog_out}, + {ADM9240_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm9240_vid}, + {0} +}; + +int adm9240_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, adm9240_detect); +} + +static int adm9240_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct adm9240_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("adm9240.o: adm9240_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access adm9240_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct adm9240_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct adm9240_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &adm9240_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if ( + ((adm9240_read_value + (new_client, ADM9240_REG_CONFIG) & 0x80) != 0x00) + || + (adm9240_read_value(new_client, ADM9240_REG_I2C_ADDR) + != address)) + goto ERROR1; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = adm9240_read_value(new_client, ADM9240_REG_COMPANY_ID); + if (i == 0x23) + kind = adm9240; + else if (i == 0xda) + kind = ds1780; + else if (i == 0x01) + kind = lm81; + else { + if (kind == 0) + printk + ("adm9240.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == adm9240) { + type_name = "adm9240"; + client_name = "ADM9240 chip"; + } else if (kind == ds1780) { + type_name = "ds1780"; + client_name = "DS1780 chip"; + } else if (kind == lm81) { + type_name = "lm81"; + client_name = "LM81 chip"; + } else { +#ifdef DEBUG + printk("adm9240.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = adm9240_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + adm9240_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the ADM9240 chip */ + adm9240_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int adm9240_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct adm9240_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("adm9240.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; + +} + +/* No commands defined yet */ +int adm9240_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void adm9240_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void adm9240_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +int adm9240_read_value(struct i2c_client *client, u8 reg) +{ + return 0xFF & i2c_smbus_read_byte_data(client, reg); +} + +int adm9240_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new ADM9240. It should set limits, etc. */ +void adm9240_init_client(struct i2c_client *client) +{ + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others. This makes most other + initializations unnecessary */ + adm9240_write_value(client, ADM9240_REG_CONFIG, 0x80); + + adm9240_write_value(client, ADM9240_REG_IN_MIN(0), + IN_TO_REG(ADM9240_INIT_IN_MIN_0, 0)); + adm9240_write_value(client, ADM9240_REG_IN_MAX(0), + IN_TO_REG(ADM9240_INIT_IN_MAX_0, 0)); + adm9240_write_value(client, ADM9240_REG_IN_MIN(1), + IN_TO_REG(ADM9240_INIT_IN_MIN_1, 1)); + adm9240_write_value(client, ADM9240_REG_IN_MAX(1), + IN_TO_REG(ADM9240_INIT_IN_MAX_1, 1)); + adm9240_write_value(client, ADM9240_REG_IN_MIN(2), + IN_TO_REG(ADM9240_INIT_IN_MIN_2, 2)); + adm9240_write_value(client, ADM9240_REG_IN_MAX(2), + IN_TO_REG(ADM9240_INIT_IN_MAX_2, 2)); + adm9240_write_value(client, ADM9240_REG_IN_MIN(3), + IN_TO_REG(ADM9240_INIT_IN_MIN_3, 3)); + adm9240_write_value(client, ADM9240_REG_IN_MAX(3), + IN_TO_REG(ADM9240_INIT_IN_MAX_3, 3)); + adm9240_write_value(client, ADM9240_REG_IN_MIN(4), + IN_TO_REG(ADM9240_INIT_IN_MIN_4, 4)); + adm9240_write_value(client, ADM9240_REG_IN_MAX(4), + IN_TO_REG(ADM9240_INIT_IN_MAX_4, 4)); + adm9240_write_value(client, ADM9240_REG_IN_MIN(5), + IN_TO_REG(ADM9240_INIT_IN_MIN_5, 5)); + adm9240_write_value(client, ADM9240_REG_IN_MAX(5), + IN_TO_REG(ADM9240_INIT_IN_MAX_5, 5)); + adm9240_write_value(client, ADM9240_REG_FAN1_MIN, + FAN_TO_REG(ADM9240_INIT_FAN_MIN_1, 2)); + adm9240_write_value(client, ADM9240_REG_FAN2_MIN, + FAN_TO_REG(ADM9240_INIT_FAN_MIN_2, 2)); + adm9240_write_value(client, ADM9240_REG_TOS, + TEMP_LIMIT_TO_REG(ADM9240_INIT_TEMP_OS_MAX)); + adm9240_write_value(client, ADM9240_REG_THYST, + TEMP_LIMIT_TO_REG(ADM9240_INIT_TEMP_OS_HYST)); + adm9240_write_value(client, ADM9240_REG_TEMP_CONFIG, 0x00); + + /* Start monitoring */ + adm9240_write_value(client, ADM9240_REG_CONFIG, 0x01); +} + +void adm9240_update_client(struct i2c_client *client) +{ + struct adm9240_data *data = client->data; + u8 i; + + down(&data->update_lock); + + if ( + (jiffies - data->last_updated > + (data->type == adm9240 ? HZ / 2 : HZ * 2)) + || (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting adm9240 update\n"); +#endif + for (i = 0; i <= 5; i++) { + data->in[i] = + adm9240_read_value(client, ADM9240_REG_IN(i)); + data->in_min[i] = + adm9240_read_value(client, + ADM9240_REG_IN_MIN(i)); + data->in_max[i] = + adm9240_read_value(client, + ADM9240_REG_IN_MAX(i)); + } + data->fan[0] = + adm9240_read_value(client, ADM9240_REG_FAN1); + data->fan_min[0] = + adm9240_read_value(client, ADM9240_REG_FAN1_MIN); + data->fan[1] = + adm9240_read_value(client, ADM9240_REG_FAN2); + data->fan_min[1] = + adm9240_read_value(client, ADM9240_REG_FAN2_MIN); + data->temp = + (adm9240_read_value(client, ADM9240_REG_TEMP) << 1) + + ((adm9240_read_value + (client, ADM9240_REG_TEMP_CONFIG) & 0x80) >> 7); + data->temp_os_max = + adm9240_read_value(client, ADM9240_REG_TOS); + data->temp_os_hyst = + adm9240_read_value(client, ADM9240_REG_THYST); + + i = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + data->vid = i & 0x0f; + data->vid |= + (adm9240_read_value(client, ADM9240_REG_VID4) & 0x01) + << 4; + + data->alarms = + adm9240_read_value(client, + ADM9240_REG_INT1_STAT) + + (adm9240_read_value(client, ADM9240_REG_INT2_STAT) << + 8); + data->analog_out = + adm9240_read_value(client, ADM9240_REG_ANALOG_OUT); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void adm9240_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + + int scales[6] = { 250, 270, 330, 500, 1200, 270 }; + + struct adm9240_data *data = client->data; + int nr = ctl_name - ADM9240_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = + IN_FROM_REG(data->in_min[nr], nr) * scales[nr] / 192; + results[1] = + IN_FROM_REG(data->in_max[nr], nr) * scales[nr] / 192; + results[2] = + IN_FROM_REG(data->in[nr], nr) * scales[nr] / 192; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = + IN_TO_REG((results[0] * 192) / scales[nr], nr); + adm9240_write_value(client, ADM9240_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = + IN_TO_REG((results[1] * 192) / scales[nr], nr); + adm9240_write_value(client, ADM9240_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void adm9240_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm9240_data *data = client->data; + int nr = ctl_name - ADM9240_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data-> + fan_div[nr - 1])); + results[1] = + FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG + (data-> + fan_div[nr - + 1])); + adm9240_write_value(client, + nr == + 1 ? ADM9240_REG_FAN1_MIN : + ADM9240_REG_FAN2_MIN, + data->fan_min[nr - 1]); + } + } +} + + +void adm9240_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm9240_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->temp_os_max); + results[1] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_os_max = TEMP_LIMIT_TO_REG(results[0]); + adm9240_write_value(client, ADM9240_REG_TOS, + data->temp_os_max); + } + if (*nrels_mag >= 2) { + data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[1]); + adm9240_write_value(client, ADM9240_REG_THYST, + data->temp_os_hyst); + } + } +} + +void adm9240_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm9240_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void adm9240_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct adm9240_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = adm9240_read_value(client, ADM9240_REG_VID_FAN_DIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan_div[1] << 6); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div[0] << 4); + adm9240_write_value(client, + ADM9240_REG_VID_FAN_DIV, old); + } + } +} + +void adm9240_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct adm9240_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = data->analog_out; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->analog_out = results[0]; + adm9240_write_value(client, ADM9240_REG_ANALOG_OUT, + data->analog_out); + } + } +} + +void adm9240_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct adm9240_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + adm9240_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +int __init sensors_adm9240_init(void) +{ + int res; + + printk("adm9240.o version %s (%s)\n", LM_VERSION, LM_DATE); + adm9240_initialized = 0; + + if ((res = i2c_add_driver(&adm9240_driver))) { + printk + ("adm9240.o: Driver registration failed, module not inserted.\n"); + adm9240_cleanup(); + return res; + } + adm9240_initialized++; + return 0; +} + +int __init adm9240_cleanup(void) +{ + int res; + + if (adm9240_initialized >= 1) { + if ((res = i2c_del_driver(&adm9240_driver))) { + printk + ("adm9240.o: Driver deregistration failed, module not removed.\n"); + return res; + } + adm9240_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("ADM9240 driver"); + +int init_module(void) +{ + return sensors_adm9240_init(); +} + +int cleanup_module(void) +{ + return adm9240_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/bt869.c Mon Mar 25 15:28:37 CET 2002 +++ linux/drivers/sensors/bt869.c Mon Mar 25 15:28:37 CET 2002 @@ -0,0 +1,971 @@ +/* + bt869.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + + Copyright (c) 1998, 1999 Frodo Looijaard + Copyright (c) 2001, 2002 Stephen Davies + + 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. +*/ + + +#define DEBUG 1 + +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; + +/* found only at 0x44 or 0x45 */ +static unsigned short normal_i2c_range[] = { 0x44, 0x45, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(bt869); + +/* Many bt869 constants specified below */ + +/* The bt869 registers */ +/* Coming soon: Many, many registers */ + +/* 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. */ + + /*none */ + +/* Initial values */ +/*none*/ + +/* Each client has this additional data */ +struct bt869_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 status[3]; /* Register values */ + u16 res[2]; /* Resolution XxY */ + u8 ntsc; /* 1=NTSC, 0=PAL */ + u8 half; /* go half res */ + u8 depth; /* screen depth */ + u8 colorbars; /* turn on/off colorbar calibration screen */ + u8 svideo; /* output format: (2=RGB) 1=SVIDEO, 0=Composite */ +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_bt869_init(void); +static int __init bt869_cleanup(void); +static int bt869_attach_adapter(struct i2c_adapter *adapter); +static int bt869_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void bt869_init_client(struct i2c_client *client); +static int bt869_detach_client(struct i2c_client *client); +static int bt869_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void bt869_inc_use(struct i2c_client *client); +static void bt869_dec_use(struct i2c_client *client); +static int bt869_read_value(struct i2c_client *client, u8 reg); +static int bt869_write_value(struct i2c_client *client, u8 reg, u16 value); +static void bt869_write_values(struct i2c_client *client, u16 *values); +static void bt869_status(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_ntsc(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_res(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_half(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_colorbars(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_svideo(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_depth(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void bt869_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver bt869_driver = { + /* name */ "BT869 video-output chip driver", + /* id */ I2C_DRIVERID_BT869, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &bt869_attach_adapter, + /* detach_client */ &bt869_detach_client, + /* command */ &bt869_command, + /* inc_use */ &bt869_inc_use, + /* dec_use */ &bt869_dec_use +}; + +/* These files are created for each detected bt869. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table bt869_dir_table_template[] = { + {BT869_SYSCTL_STATUS, "status", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_status}, + {BT869_SYSCTL_NTSC, "ntsc", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_ntsc}, + {BT869_SYSCTL_RES, "res", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_res}, + {BT869_SYSCTL_HALF, "half", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_half}, + {BT869_SYSCTL_COLORBARS, "colorbars", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_colorbars}, + {BT869_SYSCTL_DEPTH, "depth", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_depth}, + {BT869_SYSCTL_SVIDEO, "svideo", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &bt869_svideo}, + {0} +}; + +/* ****************** + +720x576, 27.5MHz, PAL, no overscan compensation. + +This mode should be use for digital video, DVD playback etc. + +NOTE: This mode for PAL, see 720x480 for an equivalent NTSC mode +NOTE: -- Steve Davies + + +Compatible X modeline: + + Mode "720x576-BT869" + DotClock 27.5 + HTimings 720 744 800 880 + VTimings 576 581 583 625 + EndMode + + +625LINE=1 625 line output format +BST_AMP[7:0]=x57 87 Burst ampl. multiplication factor (PAL std??) +BY_PLL=0 Use the PLL +CATTENUATE[2:0]=0 No chroma attenuation +CCF1B1[7:0]=0 close caption stuff +CCF1B2[7:0]=0 close caption stuff +CCF2B1[7:0]=0 close caption stuff +CCF2B2[7:0]=0 close caption stuff +CCORING[2:0]=0 Bypass chroma coring +CCR_START[8:0]=0 [CCR_START[8]=0; CCR_START[7:0]=0] Close-caption clock runin start from hsync +CC_ADD[11:0]=xD2 210 [CC_ADD[11:8]=0; CC_ADD[7:0]=xD2] Close-caption DTO increment +CHECK_STAT=0 Don't check monitor status +CLPF[1:0]=0 Hoz chroma lowpass filter=Bypass +DACDISA=1 Disable DACA +DACDISB=0 Don't disable DACB +DACDISC=0 Don't disable DACC +DACOFF=0 Don't disable the DACs +DATDLY = 0 normal +DATSWP=0 normal +DCHROMA=0 Don't blank chroma +DIS_FFILT=1 Disable flickerfilter +DIS_GMSHC=1 Disable chroma psuedo-gamma removal +DIS_GMSHY=1 Disable luma pseudo gamma removal +DIS_GMUSHC=1 Disable chroma anti-pseudo gamma removal +DIS_GMUSHY=1 Disable luma anti-pseudo gamma removal +DIS_SCRESET=0 Normal subcarrier phase resets +DIS_YFLPF=0 Disable Luma initial hoz low pass filter +DIV2=0 Input pixel rate not divided by 2 +ECBAR=0 No colour bars +ECCF1=0 Disable closed caption +ECCF2=0 Disable closed caption +ECCGATE=0 Normal close caption encoding +ECLIP=0 0=disable clipping +EN_ASYNC=0 set to 0 for normal operation +EN_BLANKO=0 BLANK is an input +EN_DOT=0 Disables dot clock sync on BLANK pin +EN_OUT=1 Allows outputs to be enabled +EN_XCLK=1 Use CLKI pin as clock source +ESTATUS[1:0]=0 Used to select readback register +FIELDI=0 Logical 1 on FIELD indicates even field +F_SELC[2:0]=0 5 line chroma flickerfilter +F_SELY[2:0]=0 5 line luma flickerfilter +HBURST_BEGIN[7:0]=x98 152 Chroma burst start point in clocks +HBURST_END[7:0]=x58 88 Chroma burst end point in clocks - 128 +HSYNCI=0 Active low HSYNC +HSYNC_WIDTH[7:0]=x80 128 Analogue sync width in clocks +HSYNOFFSET[9:0]=0 [HSYNOFFSET[9:8]=0; HSYNOFFSET[7:0]=0] hsync in "standard position" +HSYNWIDTH[5:0]=2 2 pixel hsync width +H_ACTIVE[9:0]=x2D0 720 [H_ACTIVE[9:8]=2; H_ACTIVE[7:0]=xD0] Active pixels per line +H_BLANKI[8:0]=x84 132 [H_BLANKI[8]=0; H_BLANKI[7:0]=x84] End of blanking of input video +H_BLANKO[9:0]=x120 288 [H_BLANKO[9:8]=1; H_BLANKO[7:0]=x20] End of blanking from hoz sync leading edge +H_CLKI[10:0]=x378 888 [H_CLKI[10:8]=3; H_CLKI[7:0]=x78] Input line length total in clocks +H_CLKO[11:0]=x6e0 1760 [H_CLKO[11:8]=6; H_CLKO[7:0]=xe0] Output clocks per line +H_FRACT[7:0]=0 0 fractional input clocks per line +IN_MODE[2:0]=0 24Bit RGB muxed +LUMADLY[1:0]=0 0 pixel delay on Y_DLY luma +MCB[7:0]=x49 73 Mult factor for CB prior to subcarrier mod. +MCR[7:0]=x82 130 Mult factor for CR prior to subcarrier mod. +MODE2X=0 Don't divide clock input by 2 +MSC[31:0]=x2945E0B4 692445365 [MSC[31:24]=x29; MSC[23:16]=x45; MSC[15:8]=xE0; MSC[7:0]=xB4] Subcarrier incr. +MY[7:0]=x8C 140 Mult factor for Y +NI_OUT=0 Normal interlaced output +OUT_MODE[1:0]=0 video0-3 is CVBS, Y, C, Y_DLY +OUT_MUXA[1:0]=0 Don't care as DACA is disabled +OUT_MUXB[1:0]=1 Output video[1] (Y) on DACB +OUT_MUXC[1:0]=2 Output video[2] (C) on DACC +PAL_MD=1 Video output in PAL mode +PHASE_OFF[7:0]=0 Subcarrier phase offset +PLL_FRACT[15:0]=x30 48 [PLL_FRACT[15:8]=0x0; PLL_FRACT[7:0]=x30] frac portion of pll multiplier +PLL_INT[5:0]=0x0C 12 Int portion of pll multiplier +SETUP=0 7.5-IRE setup disabled +SLAVER=1 +SRESET=0 Don't do a software reset +SYNC_AMP[7:0]=xF0 240 Sync amp mult. factor (PAL std???) +VBLANKDLY=0 Extra line of blanking in 2nd field? +VSYNCI=0 Active low VSYNC +VSYNC_DUR=0 2.5line VSYNC duration on output +VSYNCOFFSET[10:0]=0 [VSYNOFFSET[10:8]=0; VSYNOFFSET[7:0]=0] VSYNC in standard position +VSYNWIDTH[2:0]=1 1 line of vsync width +V_ACTIVEI[9:0]=x240 576 [V_ACTIVEI[9:0]=2; V_ACTIVEI[7:0]=x40] Active input lines +V_ACTIVEO[8:0]=x122 290 [V_ACTIVE0[8]=1; V_ACTIVEO[7:0]=x22] +V_BLANKI[7:0]=x2A 42 Input lines from vsync to first active line +V_BLANKO[7:0]=x16 22 +V_LINESI[9:0]=x271 625 [V_LINESI[9:8]=2; V_LINESI[7:0]=x71] Number of input lines +V_SCALE[13:0]=x1000 4096 [V_SCALE[13:8]=x10; V_SCALE[7:0]=0] Vert scale coefficient="none"? +YATTENUATE[2:0]=0 no luma attenuation +YCORING[2:0]=0 Luma-coring bypass +YLPF[1:0]=0 Luma hoz low pass filter=bypass + +***************** */ + +static u16 registers_720_576[] = + { + 0x6e, 0x00, /* HSYNOFFSET[7:0]=0 */ + 0x70, 0x02, /* HSYNOFFSET[9:8]=0; HSYNWIDTH[5:0]=2 */ + 0x72, 0x00, /* VSYNOFFSET[7:0]=0 */ + 0x74, 0x01, /* DATDLY = 0; DATSWP=0; VSYNOFFSET[10:8]=0; VSYNWIDTH[2:0]=1 */ + 0x76, 0xe0, /* H_CLKO[7:0]=xe0 */ + 0x78, 0xd0, /* H_ACTIVE[7:0]=xD0 */ + 0x7a, 0x80, /* HSYNC_WIDTH[7:0]=x80 */ + 0x7c, 0x98, /* HBURST_BEGIN[7:0]=x98 */ + 0x7e, 0x58, /* HBURST_END[7:0]=x58 */ + 0x80, 0x20, /* H_BLANKO[7:0]=x20 */ + 0x82, 0x16, /* V_BLANKO[7:0]=x16 */ + 0x84, 0x22, /* V_ACTIVEO[7:0]=x22 */ + 0x86, 0xa6, /* V_ACTIVE0[8]=1; H_ACTIVE[9:8]=2; H_CLKO[11:8]=6 */ + 0x88, 0x00, /* H_FRACT[7:0]=0 */ + 0x8a, 0x78, /* H_CLKI[7:0]=x78 */ + 0x8c, 0x80, /* H_BLANKI[7:0]=x84 */ + 0x8e, 0x03, /* VBLANKDLY=0; H_BLANKI[8]=0; H_CLKI[10:8]=3 */ + 0x90, 0x71, /* V_LINESI[7:0]=x71 */ + 0x92, 0x2a, /* V_BLANKI[7:0]=x2A */ + 0x94, 0x40, /* V_ACTIVEI[7:0]=x40 */ + 0x96, 0x0a, /* CLPF[1:0]=0; YLPF[1:0]=0; V_ACTIVEI[9:0]=2; V_LINESI[9:8]=2 */ + 0x98, 0x00, /* V_SCALE[7:0]=0 */ + 0x9a, 0x50, /* H_BLANKO[9:8]=1; V_SCALE[13:8]=x10 */ + 0x9c, 0x30, /* PLL_FRACT[7:0]=x30 */ + 0x9e, 0x0, /* PLL_FRACT[15:8]=0x0 */ + 0xa0, 0x8c, /* EN_XCLK=1; BY_PLL=0; PLL_INT[5:0]=0x0C */ + 0xa2, 0x24, /* ECLIP=0; PAL_MD=1; DIS_SCRESET=0; VSYNC_DUR=0; 625LINE=1; SETUP=0; NI_OUT=0 */ + 0xa4, 0xf0, /* SYNC_AMP[7:0]=xF0 */ + 0xa6, 0x57, /* BST_AMP[7:0]=x57 */ + 0xa8, 0x82, /* MCR[7:0]=x82 */ + 0xaa, 0x49, /* MCB[7:0]=x49 */ + 0xac, 0x8c, /* MY[7:0]=x8C */ + 0xae, 0xb4, /* MSC[7:0]=xb4 */ + 0xb0, 0xe0, /* MSC[15:8]=xe0 */ + 0xb2, 0x45, /* MSC[23:16]=x45 */ + 0xb4, 0x29, /* MSC[31:24]=x29 */ + 0xb6, 0x00, /* PHASE_OFF[7:0]=0 */ + //0xba, 0x21, /* SRESET=0; CHECK_STAT=0; SLAVER=1; DACOFF=0; DACDISC=0; DACDISB=0; DACDISA=1 */ + 0xc4, 0x01, /* ESTATUS[1:0]=0; ECCF2=0; ECCF1=0; ECCGATE=0; ECBAR=0; DCHROMA=0; EN_OUT=1 */ + 0xc6, 0x00, /* EN_BLANKO=0; EN_DOT=0; FIELDI=0; VSYNCI=0; HSYNCI=0; IN_MODE[2:0]=0(24bRGB) */ + 0xc8, 0x40, /* DIS_YFLPF=0; DIS_FFILT=1; F_SELC[2:0]=0; F_SELY[2:0]=0 */ + 0xca, 0xc0, /* DIS_GMUSHY=1; DIS_GMSHY=1; YCORING[2:0]=0; YATTENUATE[2:0]=0 */ + 0xcc, 0xc0, /* DIS_GMUSHC=1; DIS_GMSHC=1; CCORING[2:0]=0; CATTENUATE[2:0]=0 */ + //0xce, 0x24, /* OUT_MUXC=2 [C]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/ + //0xce, 0x04, /* OUT_MUXC=0 [CVBS]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/ + 0xd6, 0x00, /* OUT_MODE[1:0]=0; LUMADLY[1:0]=0 */ + 0, 0 + }; + + +/* ****************** + +720x480, 27.5MHz, NTSC no overscan compensation. + +This mode should be use for digital video, DVD playback etc. + +NOTE: This mode for NTSC, see 720x576 for an equivalent PAL mode +NOTE: -- Steve Davies + +Compatible X modeline: + + Mode "720x480-BT869" + DotClock 27.5 + HTimings 720 744 800 872 + VTimings 480 483 485 525 + EndMode + + +625LINE=0 not 625 line output format +BST_AMP[7:0]=x74 116 Burst ampl. multiplication factor (NTSC std??) +BY_PLL=0 Use the PLL +CATTENUATE[2:0]=0 No chroma attenuation +CCF1B1[7:0]=0 close caption stuff +CCF1B2[7:0]=0 close caption stuff +CCF2B1[7:0]=0 close caption stuff +CCF2B2[7:0]=0 close caption stuff +CCORING[2:0]=0 Bypass chroma coring +CCR_START[8:0]=0 [CCR_START[8]=0; CCR_START[7:0]=0] Close-caption clock runin start from hsync +CC_ADD[11:0]=xD2 210 [CC_ADD[11:8]=0; CC_ADD[7:0]=xD2] Close-caption DTO increment +CHECK_STAT=0 Don't check monitor status +CLPF[1:0]=0 Hoz chroma lowpass filter=Bypass +DACDISA=1 Disable DACA +DACDISB=0 Don't disable DACB +DACDISC=0 Don't disable DACC +DACOFF=0 Don't disable the DACs +DATDLY = 0 normal +DATSWP=0 normal +DCHROMA=0 Don't blank chroma +DIS_FFILT=1 Disable flickerfilter +DIS_GMSHC=1 Disable chroma psuedo-gamma removal +DIS_GMSHY=1 Disable luma pseudo gamma removal +DIS_GMUSHC=1 Disable chroma anti-pseudo gamma removal +DIS_GMUSHY=1 Disable luma anti-pseudo gamma removal +DIS_SCRESET=0 Normal subcarrier phase resets +DIS_YFLPF=0 Disable Luma initial hoz low pass filter +DIV2=0 Input pixel rate not divided by 2 +ECBAR=0 No colour bars +ECCF1=0 Disable closed caption +ECCF2=0 Disable closed caption +ECCGATE=0 Normal close caption encoding +ECLIP=0 0=disable clipping +EN_ASYNC=0 set to 0 for normal operation +EN_BLANKO=0 BLANK is an input +EN_DOT=0 Disables dot clock sync on BLANK pin +EN_OUT=1 Allows outputs to be enabled +EN_XCLK=1 Use CLKI pin as clock source +ESTATUS[1:0]=0 Used to select readback register +FIELDI=0 Logical 1 on FIELD indicates even field +F_SELC[2:0]=0 5 line chroma flickerfilter +F_SELY[2:0]=0 5 line luma flickerfilter +HBURST_BEGIN[7:0]=x92 146 Chroma burst start point in clocks +HBURST_END[7:0]=x57 87 Chroma burst end point in clocks - 128 +HSYNCI=0 Active low HSYNC +HSYNC_WIDTH[7:0]=x80 128 Analogue sync width in clocks +HSYNOFFSET[9:0]=0 [HSYNOFFSET[9:8]=0; HSYNOFFSET[7:0]=0] hsync in "standard position" +HSYNWIDTH[5:0]=2 2 pixel hsync width +H_ACTIVE[9:0]=x2D0 720 [H_ACTIVE[9:8]=2; H_ACTIVE[7:0]=xD0] Active pixels per line +H_BLANKI[8:0]=x80 128 [H_BLANKI[8]=0; H_BLANKI[7:0]=x80] End of blanking of input video +H_BLANKO[9:0]=x102 258 [H_BLANKO[9:8]=1; H_BLANKO[7:0]=x2] End of blanking from hoz sync leading edge +H_CLKI[10:0]=x368 872 [H_CLKI[10:8]=3; H_CLKI[7:0]=x68] Input line length total in clocks +H_CLKO[11:0]=x6d0 1744 [H_CLKO[11:8]=6; H_CLKO[7:0]=xD0] Output clocks per line +H_FRACT[7:0]=0 0 fractional input clocks per line +IN_MODE[2:0]=0 24Bit RGB muxed +LUMADLY[1:0]=0 0 pixel delay on Y_DLY luma +MCB[7:0]=x43 67 Mult factor for CB prior to subcarrier mod. +MCR[7:0]=x77 119 Mult factor for CR prior to subcarrier mod. +MODE2X=0 Don't divide clock input by 2 +MSC[31:0]=x215282E5 559055589 [MSC[31:24]=x21; MSC[23:16]=x52; MSC[15:8]=x82; MSC[7:0]=xE5] Subcarrier incr. +MY[7:0]=x85 133 Mult factor for Y +NI_OUT=0 Normal interlaced output +OUT_MODE[1:0]=0 video0-3 is CVBS, Y, C, Y_DLY +OUT_MUXA[1:0]=0 Don't care as DACA is disabled +OUT_MUXB[1:0]=1 Output video[1] (Y) on DACB +OUT_MUXC[1:0]=2 Output video[2] (C) on DACC +PAL_MD=0 Video output in PAL mode? No. +PHASE_OFF[7:0]=0 Subcarrier phase offset +PLL_FRACT[15:0]=x30 48 [PLL_FRACT[15:8]=0x0; PLL_FRACT[7:0]=x30] frac portion of pll multiplier +PLL_INT[5:0]=0x0C 12 Int portion of pll multiplier +SETUP=1 7.5-IRE enabled for NTSC +SLAVER=1 +SRESET=0 Don't do a software reset +SYNC_AMP[7:0]=xE5 229 Sync amp mult. factor (PAL std???) +VBLANKDLY=0 Extra line of blanking in 2nd field? +VSYNCI=0 Active low VSYNC +VSYNC_DUR=1 2.5line VSYNC duration on output (Yes for NTSC) +VSYNCOFFSET[10:0]=0 [VSYNOFFSET[10:8]=0; VSYNOFFSET[7:0]=0] VSYNC in standard position +VSYNWIDTH[2:0]=1 1 line of vsync width +V_ACTIVEI[9:0]=x1E0 480 [V_ACTIVEI[9:0]=1; V_ACTIVEI[7:0]=xE0] Active input lines +V_ACTIVEO[8:0]=xF0 240 [V_ACTIVE0[8]=0; V_ACTIVEO[7:0]=xF0] +V_BLANKI[7:0]=x2A 42 Input lines from vsync to first active line +V_BLANKO[7:0]=x16 22 +V_LINESI[9:0]=x20D 525 [V_LINESI[9:8]=2; V_LINESI[7:0]=x0D] Number of input lines +V_SCALE[13:0]=x1000 4096 [V_SCALE[13:8]=x10; V_SCALE[7:0]=0] Vert scale coefficient="none"? +YATTENUATE[2:0]=0 no luma attenuation +YCORING[2:0]=0 Luma-coring bypass +YLPF[1:0]=0 Luma hoz low pass filter=bypass + +***************** */ + +static u16 registers_720_480[] = + { + 0x6e, 0x00, /* HSYNOFFSET[7:0]=0 */ + 0x70, 0x02, /* HSYNOFFSET[9:8]=0; HSYNWIDTH[5:0]=2 */ + 0x72, 0x00, /* VSYNOFFSET[7:0]=0 */ + 0x74, 0x01, /* DATDLY = 0; DATSWP=0; VSYNOFFSET[10:8]=0; VSYNWIDTH[2:0]=1 */ + 0x76, 0xD0, /* H_CLKO[7:0]=xD0 */ + 0x78, 0xD0, /* H_ACTIVE[7:0]=xD0 */ + 0x7a, 0x80, /* HSYNC_WIDTH[7:0]=x80 */ + 0x7c, 0x92, /* HBURST_BEGIN[7:0]=x92 */ + 0x7e, 0x57, /* HBURST_END[7:0]=x57 */ + 0x80, 0x02, /* H_BLANKO[7:0]=x2 */ + 0x82, 0x16, /* V_BLANKO[7:0]=x16 */ + 0x84, 0xF0, /* V_ACTIVEO[7:0]=xF0 */ + 0x86, 0x26, /* V_ACTIVE0[8]=0; H_ACTIVE[9:8]=2; H_CLKO[11:8]=6 */ + 0x88, 0x00, /* H_FRACT[7:0]=0 */ + 0x8a, 0xD0, /* H_CLKI[7:0]=xD0 */ + 0x8c, 0x80, /* H_BLANKI[7:0]=x80 */ + 0x8e, 0x03, /* VBLANKDLY=0; H_BLANKI[8]=0; H_CLKI[10:8]=3 */ + 0x90, 0x0D, /* V_LINESI[7:0]=x0D */ + 0x92, 0x2A, /* V_BLANKI[7:0]=x2A */ + 0x94, 0xE0, /* V_ACTIVEI[7:0]=xE0 */ + 0x96, 0x06, /* CLPF[1:0]=0; YLPF[1:0]=0; V_ACTIVEI[9:8]=1; V_LINESI[9:8]=2 */ + 0x98, 0x00, /* V_SCALE[7:0]=0 */ + 0x9a, 0x50, /* H_BLANKO[9:8]=1; V_SCALE[13:8]=x10 */ + 0x9c, 0x30, /* PLL_FRACT[7:0]=x30 */ + 0x9e, 0x0, /* PLL_FRACT[15:8]=0x0 */ + 0xa0, 0x8c, /* EN_XCLK=1; BY_PLL=0; PLL_INT[5:0]=0x0C */ + 0xa2, 0x0A, /* ECLIP=0; PAL_MD=0; DIS_SCRESET=0; VSYNC_DUR=1; 625LINE=0; SETUP=1; NI_OUT=0 */ + 0xa4, 0xE5, /* SYNC_AMP[7:0]=xE5 */ + 0xa6, 0x74, /* BST_AMP[7:0]=x74 */ + 0xa8, 0x77, /* MCR[7:0]=x77 */ + 0xaa, 0x43, /* MCB[7:0]=x43 */ + 0xac, 0x85, /* MY[7:0]=x85 */ + 0xae, 0xE5, /* MSC[7:0]=xE5 */ + 0xb0, 0x82, /* MSC[15:8]=x82 */ + 0xb2, 0x52, /* MSC[23:16]=x52 */ + 0xb4, 0x21, /* MSC[31:24]=x21 */ + 0xb6, 0x00, /* PHASE_OFF[7:0]=0 */ + //0xba, 0x21, /* SRESET=0; CHECK_STAT=0; SLAVER=1; DACOFF=0; DACDISC=0; DACDISB=0; DACDISA=1 */ + 0xc4, 0x01, /* ESTATUS[1:0]=0; ECCF2=0; ECCF1=0; ECCGATE=0; ECBAR=0; DCHROMA=0; EN_OUT=1 */ + 0xc6, 0x00, /* EN_BLANKO=0; EN_DOT=0; FIELDI=0; VSYNCI=0; HSYNCI=0; IN_MODE[2:0]=0(24bRGB) */ + 0xc8, 0x40, /* DIS_YFLPF=0; DIS_FFILT=1; F_SELC[2:0]=0; F_SELY[2:0]=0 */ + 0xca, 0xc0, /* DIS_GMUSHY=1; DIS_GMSHY=1; YCORING[2:0]=0; YATTENUATE[2:0]=0 */ + 0xcc, 0xc0, /* DIS_GMUSHC=1; DIS_GMSHC=1; CCORING[2:0]=0; CATTENUATE[2:0]=0 */ + //0xce, 0x24, /* OUT_MUXC=2 [C]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/ + //0xce, 0x04, /* OUT_MUXC=0 [CVBS]; OUT_MUXB=1 [Y]; OUT_MUXA=0 [CVBS, but disabled]*/ + 0xd6, 0x00, /* OUT_MODE[1:0]=0; LUMADLY[1:0]=0 */ + 0, 0 + }; + + +/* Used by init/cleanup */ +static int __initdata bt869_initialized = 0; + +int bt869_id = 0; + +int bt869_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, bt869_detect); +} + +/* This function is called by i2c_detect */ +int bt869_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, cur; + struct i2c_client *new_client; + struct bt869_data *data; + int err = 0; + const char *type_name, *client_name; + + + printk("bt869.o: probing address %d .\n", address); + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("bt869.o: bt869_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_READ_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access bt869_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct bt869_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = + (struct bt869_data *) (((struct i2c_client *) new_client) + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &bt869_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is lousy. */ + i2c_smbus_write_byte_data(new_client, 0xC4, 0); /* set status bank 0 */ + cur = i2c_smbus_read_byte(new_client); + printk("bt869.o: address 0x%X testing-->0x%X\n", address, cur); + if ((cur & 0xE0) != 0x20) + goto ERROR1; + + /* Determine the chip type */ + kind = ((cur & 0x20) >> 5); + + if (kind) { + type_name = "bt869"; + client_name = "bt869 chip"; + printk("bt869.o: BT869 detected\n"); + } else { + type_name = "bt868"; + client_name = "bt868 chip"; + printk("bt869.o: BT868 detected\n"); + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = bt869_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + bt869_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + bt869_init_client((struct i2c_client *) new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int bt869_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct bt869_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("bt869.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int bt869_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void bt869_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void bt869_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* All registers are byte-sized. + bt869 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int bt869_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte(client); +} + +/* All registers are byte-sized. + bt869 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int bt869_write_value(struct i2c_client *client, u8 reg, u16 value) +{ +#ifdef DEBUG + printk("bt869.o: write_value(0x%X, 0x%X)\n", reg, value); +#endif + return i2c_smbus_write_byte_data(client, reg, value); +} + +void bt869_write_values(struct i2c_client *client, u16 *values) +{ + /* writes set of registers from array. 0,0 marks end of table */ + while (*values) { + bt869_write_value(client, values[0], values[1]); + values += 2; + } +} + +void bt869_init_client(struct i2c_client *client) +{ + struct bt869_data *data = client->data; + + /* Initialize the bt869 chip */ + bt869_write_value(client, 0x0ba, 0x80); + // bt869_write_value(client,0x0D6, 0x00); + /* Be a slave to the clock on the Voodoo3 */ + bt869_write_value(client, 0xa0, 0x80); + bt869_write_value(client, 0xba, 0x20); + /* depth =16bpp */ + bt869_write_value(client, 0x0C6, 0x001); + bt869_write_value(client, 0xC4, 1); + /* Flicker free enable and config */ + bt869_write_value(client, 0xC8, 0); + data->res[0] = 640; + data->res[1] = 480; + data->ntsc = 1; + data->half = 0; + data->colorbars = 0; + data->svideo = 0; + data->depth = 16; + +} + +void bt869_update_client(struct i2c_client *client) +{ + struct bt869_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { +#ifdef DEBUG + printk("Starting bt869 update\n"); +#endif + if ((data->res[0] == 800) && (data->res[1] == 600)) { + /* 800x600 built-in mode */ + bt869_write_value(client, 0xB8, + (2 + (!data->ntsc))); + bt869_write_value(client, 0xa0, 0x80 + 0x11); + printk("bt869.o: writing into config -->0x%X\n", + (2 + (!data->ntsc))); + } + else if ((data->res[0] == 720) && (data->res[1] == 576)) { + /* 720x576 no-overscan-compensation mode suitable for PAL DVD playback */ + data->ntsc = 0; /* This mode always PAL */ + bt869_write_values(client, registers_720_576); + } + else if ((data->res[0] == 720) && (data->res[1] == 480)) { + /* 720x480 no-overscan-compensation mode suitable for NTSC DVD playback */ + data->ntsc = 1; /* This mode always NTSC */ + bt869_write_values(client, registers_720_480); + } + else { + /* 640x480 built-in mode */ + bt869_write_value(client, 0xB8, (!data->ntsc)); + bt869_write_value(client, 0xa0, 0x80 + 0x0C); + printk("bt869.o: writing into config -->0x%X\n", + (0 + (!data->ntsc))); + if ((data->res[0] != 640) || (data->res[1] != 480)) { + printk + ("bt869.o: Warning: arbitrary resolutions not supported yet. Using 640x480.\n"); + data->res[0] = 640; + data->res[1] = 480; + } + } + /* Set colour depth */ + if ((data->depth != 24) && (data->depth != 16)) + data->depth = 16; + if (data->depth == 16) + bt869_write_value(client, 0x0C6, 0x001); + if (data->depth == 24) + bt869_write_value(client, 0x0C6, 0x000); + /* set "half" resolution mode */ + bt869_write_value(client, 0xd4, data->half << 6); + /* Set composite/svideo mode, also enable the right dacs */ + switch (data->svideo) { + case 2: /* RGB */ + /* requires hardware mod on Voodoo3 to get all outputs, + untested in practice... Feedback to steve@daviesfam.org please */ + bt869_write_value(client, 0xd6, 0x0c); + bt869_write_value(client, 0xce, 0x24); + bt869_write_value(client, 0xba, 0x20); + break; + case 1: /* Svideo*/ + bt869_write_value(client, 0xce, 0x24); + bt869_write_value(client, 0xba, 0x21); + break; + default: /* Composite */ + bt869_write_value(client, 0xce, 0x0); + bt869_write_value(client, 0xba, 0x21); + break; + } + /* Enable outputs */ + bt869_write_value(client, 0xC4, 1); + /* Issue timing reset */ + bt869_write_value(client, 0x6c, 0x80); + +/* Read back status registers */ + bt869_write_value(client, 0xC4, + 1 | (data->colorbars << 2)); + data->status[0] = bt869_read_value(client, 1); + bt869_write_value(client, 0xC4, + 0x41 | (data->colorbars << 2)); + data->status[1] = bt869_read_value(client, 1); + bt869_write_value(client, 0xC4, + 0x81 | (data->colorbars << 2)); + data->status[2] = bt869_read_value(client, 1); + bt869_write_value(client, 0xC4, + 0x0C1 | (data->colorbars << 2)); + data->last_updated = jiffies; + data->valid = 1; + } + up(&data->update_lock); +} + + +void bt869_status(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->status[0]; + results[1] = data->status[1]; + results[2] = data->status[2]; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + printk + ("bt869.o: Warning: write was requested on read-only proc file: status\n"); + } +} + + +void bt869_ntsc(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->ntsc; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->ntsc = (results[0] > 0); + } + bt869_update_client(client); + } +} + + +void bt869_svideo(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->svideo; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->svideo = results[0]; + } + bt869_update_client(client); + } +} + + +void bt869_res(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->res[0]; + results[1] = data->res[1]; + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->res[0] = results[0]; + } + if (*nrels_mag >= 2) { + data->res[1] = results[1]; + } + bt869_update_client(client); + } +} + + +void bt869_half(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->half; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->half = (results[0] > 0); + bt869_update_client(client); + } + } +} + +void bt869_colorbars(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->colorbars; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->colorbars = (results[0] > 0); + bt869_update_client(client); + } + } +} + +void bt869_depth(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct bt869_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + bt869_update_client(client); + results[0] = data->depth; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->depth = results[0]; + bt869_update_client(client); + } + } +} + +int __init sensors_bt869_init(void) +{ + int res; + + printk("bt869.o version %s (%s)\n", LM_VERSION, LM_DATE); + bt869_initialized = 0; + if ((res = i2c_add_driver(&bt869_driver))) { + printk + ("bt869.o: Driver registration failed, module not inserted.\n"); + bt869_cleanup(); + return res; + } + bt869_initialized++; + return 0; +} + +int __init bt869_cleanup(void) +{ + int res; + + if (bt869_initialized >= 1) { + if ((res = i2c_del_driver(&bt869_driver))) { + printk + ("bt869.o: Driver deregistration failed, module not removed.\n"); + return res; + } + bt869_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard , Philip Edelbrock , Stephen Davies "); +MODULE_DESCRIPTION("bt869 driver"); + +int init_module(void) +{ + return sensors_bt869_init(); +} + +int cleanup_module(void) +{ + return bt869_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/ddcmon.c Mon Mar 25 15:28:37 CET 2002 +++ linux/drivers/sensors/ddcmon.c Mon Mar 25 15:28:37 CET 2002 @@ -0,0 +1,443 @@ +/* + ddcmon.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999, 2000 Frodo Looijaard , + Philip Edelbrock , + and Mark Studebaker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x50, SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(ddcmon); + +/* Many constants specified below */ + +/* DDCMON registers */ +#define DDCMON_REG_ID 0x08 +#define DDCMON_REG_SERIAL 0x0C +#define DDCMON_REG_HORSIZE 0x15 +#define DDCMON_REG_VERSIZE 0x16 +#define DDCMON_REG_TIMINGS 0x23 +#define DDCMON_REG_TIMBASE 0x36 +#define DDCMON_REG_TIMINCR 18 +#define DDCMON_REG_TIMNUM 4 +#define DDCMON_REG_TIMOFFSET 5 +#define DDCMON_REG_CHECKSUM 0x7f + +/* Size of DDCMON in bytes */ +#define DDCMON_SIZE 128 + +/* Each client has this additional data */ +struct ddcmon_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 data[DDCMON_SIZE]; /* Register values */ + int memtype; +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_ddcmon_init(void); +static int __init ddcmon_cleanup(void); + +static int ddcmon_attach_adapter(struct i2c_adapter *adapter); +static int ddcmon_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int ddcmon_detach_client(struct i2c_client *client); +static int ddcmon_command(struct i2c_client *client, unsigned int cmd, + void *arg); + +static void ddcmon_inc_use(struct i2c_client *client); +static void ddcmon_dec_use(struct i2c_client *client); + +static void ddcmon_idcall(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ddcmon_size(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ddcmon_sync(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ddcmon_timings(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ddcmon_serial(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ddcmon_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver ddcmon_driver = { + /* name */ "DDCMON READER", + /* id */ I2C_DRIVERID_DDCMON, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &ddcmon_attach_adapter, + /* detach_client */ &ddcmon_detach_client, + /* command */ &ddcmon_command, + /* inc_use */ &ddcmon_inc_use, + /* dec_use */ &ddcmon_dec_use +}; + +/* These files are created for each detected DDCMON. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table ddcmon_dir_table_template[] = { + {DDCMON_SYSCTL_ID, "ID", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &ddcmon_idcall}, + {DDCMON_SYSCTL_SIZE, "size", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &ddcmon_size}, + {DDCMON_SYSCTL_SYNC, "sync", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_sync}, + {DDCMON_SYSCTL_TIMINGS, "timings", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_timings}, + {DDCMON_SYSCTL_SERIAL, "serial", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ddcmon_serial}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata ddcmon_initialized = 0; + +static int ddcmon_id = 0; + +int ddcmon_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, ddcmon_detect); +} + +/* This function is called by i2c_detect */ +int ddcmon_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, cs; + struct i2c_client *new_client; + struct ddcmon_data *data; + int err = 0; + const char *type_name, *client_name; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access ddcmon_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct ddcmon_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct ddcmon_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &ddcmon_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + /* Verify the first 8 locations 0x00FFFFFFFFFFFF00 */ + /* Allow force and force_ddcmon arguments */ + if(kind < 0) + { + for(i = 0; i < 8; i++) { + cs = i2c_smbus_read_byte_data(new_client, i); + if(i == 0 || i == 7) { + if(cs != 0) + goto ERROR1; + } else if(cs != 0xff) + goto ERROR1; + } + } + + type_name = "ddcmon"; + client_name = "DDC Monitor"; + + /* Fill in the remaining client fields and put it in the global list */ + strcpy(new_client->name, client_name); + + new_client->id = ddcmon_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + ddcmon_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + return 0; + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int ddcmon_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct ddcmon_data *) (client->data))-> + sysctl_id); + if ((err = i2c_detach_client(client))) { + printk + ("ddcmon.o: Client deregistration failed, client not detached.\n"); + return err; + } + kfree(client); + return 0; +} + +/* No commands defined yet */ +int ddcmon_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void ddcmon_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void ddcmon_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +void ddcmon_update_client(struct i2c_client *client) +{ + struct ddcmon_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 300 * HZ) || + (jiffies < data->last_updated) || !data->valid) { + if (i2c_smbus_write_byte(client, 0)) { +#ifdef DEBUG + printk("ddcmon read start has failed!\n"); +#endif + } + for (i = 0; i < DDCMON_SIZE; i++) { + data->data[i] = (u8) i2c_smbus_read_byte(client); + } + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void ddcmon_idcall(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct ddcmon_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ddcmon_update_client(client); + results[0] = data->data[DDCMON_REG_ID + 1] | + (data->data[DDCMON_REG_ID] << 8) | + (data->data[DDCMON_REG_ID + 3] << 16) | + (data->data[DDCMON_REG_ID + 2] << 24); + *nrels_mag = 1; + } +} + +void ddcmon_size(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct ddcmon_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ddcmon_update_client(client); + results[0] = data->data[DDCMON_REG_VERSIZE]; + results[1] = data->data[DDCMON_REG_HORSIZE]; + *nrels_mag = 2; + } +} + +void ddcmon_sync(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + int i, j; + struct ddcmon_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ddcmon_update_client(client); + *nrels_mag = 4; + /* look for sync entry */ + for(i = DDCMON_REG_TIMBASE; + i < DDCMON_REG_TIMBASE + + (DDCMON_REG_TIMNUM * DDCMON_REG_TIMINCR); + i += DDCMON_REG_TIMINCR) { + if(data->data[i] == 0) { + for(j = 0; j < 4; j++) + results[j] = data->data[i + j + + DDCMON_REG_TIMOFFSET]; + return; + } + } + for(j = 0; j < 4; j++) + results[j] = 0; + } +} + +void ddcmon_timings(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct ddcmon_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ddcmon_update_client(client); + results[0] = data->data[DDCMON_REG_TIMINGS] | + (data->data[DDCMON_REG_TIMINGS + 1] << 8) | + (data->data[DDCMON_REG_TIMINGS + 2] << 16); + *nrels_mag = 1; + } +} + +void ddcmon_serial(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct ddcmon_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ddcmon_update_client(client); + results[0] = data->data[DDCMON_REG_SERIAL] | + (data->data[DDCMON_REG_SERIAL + 1] << 8) | + (data->data[DDCMON_REG_SERIAL + 2] << 16) | + (data->data[DDCMON_REG_SERIAL + 3] << 24); + *nrels_mag = 1; + } +} + +int __init sensors_ddcmon_init(void) +{ + int res; + + printk("ddcmon.o version %s (%s)\n", LM_VERSION, LM_DATE); + ddcmon_initialized = 0; + if ((res = i2c_add_driver(&ddcmon_driver))) { + printk + ("ddcmon.o: Driver registration failed, module not inserted.\n"); + ddcmon_cleanup(); + return res; + } + ddcmon_initialized++; + return 0; +} + +int __init ddcmon_cleanup(void) +{ + int res; + + if (ddcmon_initialized >= 1) { + if ((res = i2c_del_driver(&ddcmon_driver))) { + printk + ("ddcmon.o: Driver deregistration failed, module not removed.\n"); + return res; + } + } else + ddcmon_initialized--; + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard , " + "Philip Edelbrock , " + "and Mark Studebaker "); +MODULE_DESCRIPTION("DDCMON driver"); + +int init_module(void) +{ + return sensors_ddcmon_init(); +} + +int cleanup_module(void) +{ + return ddcmon_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/ds1621.c Mon Mar 25 15:28:38 CET 2002 +++ linux/drivers/sensors/ds1621.c Mon Mar 25 15:28:38 CET 2002 @@ -0,0 +1,621 @@ +/* + ds1621.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Christian W. Zuckschwerdt 2000-11-23 + based on lm75.c by Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Supports DS1621. See doc/chips/ds1621 for details */ + +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(ds1621); + +/* Many DS1621 constants specified below */ + +/* Config register used for detection */ +/* 7 6 5 4 3 2 1 0 */ +/* |Done|THF |TLF |NVB | 1 | 0 |POL |1SHOT| */ +#define DS1621_REG_CONFIG_MASK 0x0C +#define DS1621_REG_CONFIG_VAL 0x08 +#define DS1621_REG_CONFIG_POLARITY 0x02 +#define DS1621_REG_CONFIG_1SHOT 0x01 +#define DS1621_REG_CONFIG_DONE 0x80 + +/* Note: the done bit is always unset if continuous conversion is in progress. + We need to stop the continuous conversion or switch to single shot + before this bit becomes available! + */ + +/* The DS1621 registers */ +#define DS1621_REG_TEMP 0xAA /* word, RO */ +#define DS1621_REG_TEMP_OVER 0xA1 /* word, RW */ +#define DS1621_REG_TEMP_HYST 0xA2 /* word, RW -- it's a low temp trigger */ +#define DS1621_REG_CONF 0xAC /* byte, RW */ +#define DS1621_REG_TEMP_COUNTER 0xA8 /* byte, RO */ +#define DS1621_REG_TEMP_SLOPE 0xA9 /* byte, RO */ +#define DS1621_COM_START 0xEE /* no data */ +#define DS1621_COM_STOP 0x22 /* no data */ + +/* 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 TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | \ + ((val & 0x8000)?-256:0)) +#define TEMP_TO_REG(val) (SENSORS_LIMIT((val<0 ? (0x200+((val)/5))<<7 : \ + (((val) + 2) / 5) << 7),0,0xffff)) +#define ALARMS_FROM_REG(val) ((val) & \ + (DS1621_ALARM_TEMP_HIGH | DS1621_ALARM_TEMP_LOW)) +#define ITEMP_FROM_REG(val) ((((val & 0x7fff) >> 8)) | \ + ((val & 0x8000)?-256:0)) + +/* Initial values */ +#define DS1621_INIT_TEMP_OVER 600 +#define DS1621_INIT_TEMP_HYST 0 /* 500 would cause an alarm at room temp. */ + +/* Each client has this additional data */ +struct ds1621_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u16 temp, temp_over, temp_hyst; /* Register values, word */ + u8 conf; /* Register encoding, combined */ + + char enable; /* !=0 if we're expected to restart the conversion */ + u8 temp_int, temp_counter, temp_slope; /* Register values, byte */ +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_ds1621_init(void); +static int __init ds1621_cleanup(void); +static int ds1621_attach_adapter(struct i2c_adapter *adapter); +static int ds1621_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void ds1621_init_client(struct i2c_client *client); +static int ds1621_detach_client(struct i2c_client *client); +static int ds1621_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void ds1621_inc_use(struct i2c_client *client); +static void ds1621_dec_use(struct i2c_client *client); +static u16 swap_bytes(u16 val); +static int ds1621_read_value(struct i2c_client *client, u8 reg); +static int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value); +static void ds1621_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ds1621_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ds1621_enable(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ds1621_continuous(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ds1621_polarity(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void ds1621_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver ds1621_driver = { + /* name */ "DS1621 sensor driver", + /* id */ I2C_DRIVERID_DS1621, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &ds1621_attach_adapter, + /* detach_client */ &ds1621_detach_client, + /* command */ &ds1621_command, + /* inc_use */ &ds1621_inc_use, + /* dec_use */ &ds1621_dec_use +}; + +/* These files are created for each detected DS1621. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table ds1621_dir_table_template[] = { + {DS1621_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_temp}, + {DS1621_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_alarms}, + {DS1621_SYSCTL_ENABLE, "enable", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_enable}, + {DS1621_SYSCTL_CONTINUOUS, "continuous", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_continuous}, + {DS1621_SYSCTL_POLARITY, "polarity", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &ds1621_polarity}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata ds1621_initialized = 0; + +static int ds1621_id = 0; + +int ds1621_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, ds1621_detect); +} + +/* This function is called by i2c_detect */ +int ds1621_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, conf; + struct i2c_client *new_client; + struct ds1621_data *data; + int err = 0; + const char *type_name, *client_name; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("ds1621.o: ds1621_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access ds1621_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct ds1621_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct ds1621_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &ds1621_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is lousy. */ + if (kind < 0) { + conf = i2c_smbus_read_byte_data(new_client, + DS1621_REG_CONF); + if ((conf & DS1621_REG_CONFIG_MASK) + != DS1621_REG_CONFIG_VAL) + goto ERROR1; + } + + /* Determine the chip type - only one kind supported! */ + if (kind <= 0) + kind = ds1621; + + if (kind == ds1621) { + type_name = "ds1621"; + client_name = "DS1621 chip"; + } else { +#ifdef DEBUG + printk("ds1621.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = ds1621_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + ds1621_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + ds1621_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int ds1621_detach_client(struct i2c_client *client) +{ + int err; + +#ifdef MODULE + if (MOD_IN_USE) + return -EBUSY; +#endif + + + i2c_deregister_entry(((struct ds1621_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("ds1621.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int ds1621_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void ds1621_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void ds1621_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +u16 swap_bytes(u16 val) +{ + return (val >> 8) | (val << 8); +} + +/* All registers are word-sized, except for the configuration register. + DS1621 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int ds1621_read_value(struct i2c_client *client, u8 reg) +{ + if ((reg == DS1621_REG_CONF) || (reg == DS1621_REG_TEMP_COUNTER) + || (reg == DS1621_REG_TEMP_SLOPE)) + return i2c_smbus_read_byte_data(client, reg); + else + return swap_bytes(i2c_smbus_read_word_data(client, reg)); +} + +/* All registers are word-sized, except for the configuration register. + DS1621 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int ds1621_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if ((reg == DS1621_REG_CONF) || (reg == DS1621_REG_TEMP_COUNTER) + || (reg == DS1621_REG_TEMP_SLOPE)) + return i2c_smbus_write_byte_data(client, reg, value); + else + return i2c_smbus_write_word_data(client, reg, + swap_bytes(value)); +} + +void ds1621_init_client(struct i2c_client *client) +{ + /* Initialize the DS1621 chip */ + ds1621_write_value(client, DS1621_REG_TEMP_OVER, + TEMP_TO_REG(DS1621_INIT_TEMP_OVER)); + ds1621_write_value(client, DS1621_REG_TEMP_HYST, + TEMP_TO_REG(DS1621_INIT_TEMP_HYST)); + ds1621_write_value(client, DS1621_REG_CONF, 0); + + /* perhaps we should start the continous conversion? For now */ + /* you got to do that yourself using the "enable" in proc */ +} + +void ds1621_update_client(struct i2c_client *client) +{ + struct ds1621_data *data = client->data; + u8 new_conf; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting ds1621 update\n"); +#endif + + data->conf = ds1621_read_value(client, DS1621_REG_CONF); + + data->temp = ds1621_read_value(client, + DS1621_REG_TEMP); + data->temp_over = ds1621_read_value(client, + DS1621_REG_TEMP_OVER); + data->temp_hyst = ds1621_read_value(client, + DS1621_REG_TEMP_HYST); + + /* wait for the DONE bit before reading extended values */ + + if (data->conf & DS1621_REG_CONFIG_DONE) { + data->temp_counter = ds1621_read_value(client, + DS1621_REG_TEMP_COUNTER); + data->temp_slope = ds1621_read_value(client, + DS1621_REG_TEMP_SLOPE); + data->temp_int = ITEMP_FROM_REG(data->temp); + /* restart the conversion */ + if (data->enable) + ds1621_read_value(client, DS1621_COM_START); + } + + /* reset alarms if neccessary */ + new_conf = data->conf; + if (data->temp < data->temp_over) + new_conf &= ~DS1621_ALARM_TEMP_HIGH; + if (data->temp > data->temp_hyst) + new_conf &= ~DS1621_ALARM_TEMP_LOW; + if (data->conf != new_conf) + ds1621_write_value(client, DS1621_REG_CONF, + new_conf); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void ds1621_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct ds1621_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + if (!(data->conf & DS1621_REG_CONFIG_DONE) || + (data->temp_counter > data->temp_slope) || + (data->temp_slope == 0)) { + *nrels_mag = 1; + } else { + *nrels_mag = 2; + } + else if (operation == SENSORS_PROC_REAL_READ) { + ds1621_update_client(client); + /* decide wether to calculate more precise temp */ + if (!(data->conf & DS1621_REG_CONFIG_DONE) || + (data->temp_counter > data->temp_slope) || + (data->temp_slope == 0)) { + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + } else { + results[0] = TEMP_FROM_REG(data->temp_over)*10; + results[1] = TEMP_FROM_REG(data->temp_hyst)*10; + results[2] = data->temp_int * 100 - 25 + + ((data->temp_slope - data->temp_counter) * + 100 / data->temp_slope); + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over = TEMP_TO_REG(results[0]); + ds1621_write_value(client, DS1621_REG_TEMP_OVER, + data->temp_over); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + ds1621_write_value(client, DS1621_REG_TEMP_HYST, + data->temp_hyst); + } + } +} + +void ds1621_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct ds1621_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ds1621_update_client(client); + results[0] = ALARMS_FROM_REG(data->conf); + *nrels_mag = 1; + } +} + +void ds1621_enable(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + /* If you really screw up your chip (like I did) this is */ + /* sometimes needed to (re)start the continous conversion */ + /* there is no data to read so this might hang your SMBus! */ + + struct ds1621_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ds1621_update_client(client); + results[0] = !(data->conf & DS1621_REG_CONFIG_DONE); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + if (results[0]) { + ds1621_read_value(client, DS1621_COM_START); + data->enable=1; + } else { + ds1621_read_value(client, DS1621_COM_STOP); + data->enable=0; + } + } else { + ds1621_read_value(client, DS1621_COM_START); + data->enable=1; + } + } +} + +void ds1621_continuous(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct ds1621_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ds1621_update_client(client); + results[0] = !(data->conf & DS1621_REG_CONFIG_1SHOT); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + ds1621_update_client(client); + if (*nrels_mag >= 1) { + if (results[0]) { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf & ~DS1621_REG_CONFIG_1SHOT); + } else { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf | DS1621_REG_CONFIG_1SHOT); + } + } else { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf & ~DS1621_REG_CONFIG_1SHOT); + } + } +} + +void ds1621_polarity(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct ds1621_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + ds1621_update_client(client); + results[0] = !(!(data->conf & DS1621_REG_CONFIG_POLARITY)); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + ds1621_update_client(client); + if (*nrels_mag >= 1) { + if (results[0]) { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf | DS1621_REG_CONFIG_POLARITY); + } else { + ds1621_write_value(client, DS1621_REG_CONF, + data->conf & ~DS1621_REG_CONFIG_POLARITY); + } + } + } +} + +int __init sensors_ds1621_init(void) +{ + int res; + + printk("ds1621.o version %s (%s)\n", LM_VERSION, LM_DATE); + ds1621_initialized = 0; + if ((res = i2c_add_driver(&ds1621_driver))) { + printk + ("ds1621.o: Driver registration failed, module not inserted.\n"); + ds1621_cleanup(); + return res; + } + ds1621_initialized++; + return 0; +} + +int __init ds1621_cleanup(void) +{ + int res; + + if (ds1621_initialized >= 1) { + if ((res = i2c_del_driver(&ds1621_driver))) { + printk + ("ds1621.o: Driver deregistration failed, module not removed.\n"); + return res; + } + ds1621_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Christian W. Zuckschwerdt "); +MODULE_DESCRIPTION("DS1621 driver"); + +int init_module(void) +{ + return sensors_ds1621_init(); +} + +int cleanup_module(void) +{ + return ds1621_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/eeprom.c Mon Mar 25 15:28:38 CET 2002 +++ linux/drivers/sensors/eeprom.c Mon Mar 25 15:28:38 CET 2002 @@ -0,0 +1,492 @@ +/* + eeprom.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x50, 0x57, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(eeprom); + +static int checksum = 0; +MODULE_PARM(checksum, "i"); +MODULE_PARM_DESC(checksum, + "Only accept eeproms whose checksum is correct"); + + +/* Many constants specified below */ + +/* EEPROM registers */ +#define EEPROM_REG_CHECKSUM 0x3f + +/* EEPROM memory types: */ +#define ONE_K 1 +#define TWO_K 2 +#define FOUR_K 3 +#define EIGHT_K 4 +#define SIXTEEN_K 5 + +/* Conversions */ +/* Size of EEPROM in bytes */ +#define EEPROM_SIZE 256 + +/* Each client has this additional data */ +struct eeprom_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 data[EEPROM_SIZE]; /* Register values */ +#if 0 + int memtype; +#endif +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_eeprom_init(void); +static int __init eeprom_cleanup(void); + +static int eeprom_attach_adapter(struct i2c_adapter *adapter); +static int eeprom_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int eeprom_detach_client(struct i2c_client *client); +static int eeprom_command(struct i2c_client *client, unsigned int cmd, + void *arg); + +static void eeprom_inc_use(struct i2c_client *client); +static void eeprom_dec_use(struct i2c_client *client); + +#if 0 +static int eeprom_write_value(struct i2c_client *client, u8 reg, + u8 value); +#endif + +static void eeprom_contents(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void eeprom_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver eeprom_driver = { + /* name */ "EEPROM READER", + /* id */ I2C_DRIVERID_EEPROM, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &eeprom_attach_adapter, + /* detach_client */ &eeprom_detach_client, + /* command */ &eeprom_command, + /* inc_use */ &eeprom_inc_use, + /* dec_use */ &eeprom_dec_use +}; + +/* These files are created for each detected EEPROM. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table eeprom_dir_table_template[] = { + {EEPROM_SYSCTL1, "00", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL2, "10", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL3, "20", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL4, "30", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL5, "40", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL6, "50", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL7, "60", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL8, "70", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL9, "80", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL10, "90", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL11, "a0", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL12, "b0", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL13, "c0", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL14, "d0", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL15, "e0", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {EEPROM_SYSCTL16, "f0", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &eeprom_contents}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata eeprom_initialized = 0; + +static int eeprom_id = 0; + +int eeprom_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, eeprom_detect); +} + +/* This function is called by i2c_detect */ +int eeprom_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, cs; + struct i2c_client *new_client; + struct eeprom_data *data; + int err = 0; + const char *type_name, *client_name; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("eeprom.o: eeprom_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access eeprom_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct eeprom_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct eeprom_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &eeprom_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is not there, unless you force + the checksum to work out. */ + if (checksum) { + cs = 0; + for (i = 0; i <= 0x3e; i++) + cs += i2c_smbus_read_byte_data(new_client, i); + cs &= 0xff; + if (i2c_smbus_read_byte_data + (new_client, EEPROM_REG_CHECKSUM) != cs) + goto ERROR1; + } + + /* Determine the chip type - only one kind supported! */ + if (kind <= 0) + kind = eeprom; + + if (kind == eeprom) { + type_name = "eeprom"; + client_name = "EEPROM chip"; + } else { +#ifdef DEBUG + printk("eeprom.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = eeprom_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + eeprom_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int eeprom_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct eeprom_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("eeprom.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int eeprom_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void eeprom_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void eeprom_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +#if 0 +/* No writes yet (PAE) */ +int eeprom_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} +#endif + +void eeprom_update_client(struct i2c_client *client) +{ + struct eeprom_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 300 * HZ) | + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting eeprom update\n"); +#endif + + if (i2c_smbus_write_byte(client, 0)) { +#ifdef DEBUG + printk("eeprom read start has failed!\n"); +#endif + } + for (i = 0; i < EEPROM_SIZE; i++) { + data->data[i] = (u8) i2c_smbus_read_byte(client); + } + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void eeprom_contents(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + int i; + int base = 0; + struct eeprom_data *data = client->data; + + switch (ctl_name) { + case EEPROM_SYSCTL2: + base = 16; + break; + case EEPROM_SYSCTL3: + base = 32; + break; + case EEPROM_SYSCTL4: + base = 48; + break; + case EEPROM_SYSCTL5: + base = 64; + break; + case EEPROM_SYSCTL6: + base = 80; + break; + case EEPROM_SYSCTL7: + base = 96; + break; + case EEPROM_SYSCTL8: + base = 112; + break; + case EEPROM_SYSCTL9: + base = 128; + break; + case EEPROM_SYSCTL10: + base = 144; + break; + case EEPROM_SYSCTL11: + base = 160; + break; + case EEPROM_SYSCTL12: + base = 176; + break; + case EEPROM_SYSCTL13: + base = 192; + break; + case EEPROM_SYSCTL14: + base = 208; + break; + case EEPROM_SYSCTL15: + base = 224; + break; + case EEPROM_SYSCTL16: + base = 240; + break; + } + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + eeprom_update_client(client); + for (i = 0; i < 16; i++) { + results[i] = data->data[i + base]; + } +#ifdef DEBUG + printk("eeprom.o: 0x%X EEPROM Contents (base %d): ", + client->addr, base); + for (i = 0; i < 16; i++) { + printk(" 0x%X", data->data[i + base]); + } + printk(" .\n"); +#endif + *nrels_mag = 16; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + +/* No writes to the EEPROM (yet, anyway) (PAE) */ + printk("eeprom.o: No writes to EEPROMs supported!\n"); + } +} + +int __init sensors_eeprom_init(void) +{ + int res; + + printk("eeprom.o version %s (%s)\n", LM_VERSION, LM_DATE); + eeprom_initialized = 0; + if ((res = i2c_add_driver(&eeprom_driver))) { + printk + ("eeprom.o: Driver registration failed, module not inserted.\n"); + eeprom_cleanup(); + return res; + } + eeprom_initialized++; + return 0; +} + +int __init eeprom_cleanup(void) +{ + int res; + + if (eeprom_initialized >= 1) { + if ((res = i2c_del_driver(&eeprom_driver))) { + printk + ("eeprom.o: Driver deregistration failed, module not removed.\n"); + return res; + } + } else + eeprom_initialized--; + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("EEPROM driver"); + +int init_module(void) +{ + return sensors_eeprom_init(); +} + +int cleanup_module(void) +{ + return eeprom_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/fscpos.c Mon Mar 25 15:28:38 CET 2002 +++ linux/drivers/sensors/fscpos.c Mon Mar 25 15:28:38 CET 2002 @@ -0,0 +1,777 @@ +/* + fscpos.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2001 Hermann Jung + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + fujitsu siemens poseidon chip, + module based on lm80.c + Copyright (c) 1998, 1999 Frodo Looijaard + and Philip Edelbrock +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +/* temp. because wasn't in kernel 2.4.13 patch */ +#ifndef I2C_DRIVERID_FSCPOS +#define I2C_DRIVERID_FSCPOS 1028 +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x73, SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(fscpos); + +/* The FSCPOS registers */ + +/* chip identification */ +#define FSCPOS_REG_IDENT_0 0x00 +#define FSCPOS_REG_IDENT_1 0x01 +#define FSCPOS_REG_IDENT_2 0x02 +#define FSCPOS_REG_REVISION 0x03 + +/* global control and status */ +#define FSCPOS_REG_EVENT_STATE 0x04 +#define FSCPOS_REG_CONTROL 0x05 + +/* watchdog */ +#define FSCPOS_REG_WDOG_PRESET 0x28 +#define FSCPOS_REG_WDOG_STATE 0x23 +#define FSCPOS_REG_WDOG_CONTROL 0x21 + +/* fan 0 */ +#define FSCPOS_REG_FAN0_MIN 0x55 +#define FSCPOS_REG_FAN0_ACT 0x0e +#define FSCPOS_REG_FAN0_STATE 0x0d +#define FSCPOS_REG_FAN0_RIPPLE 0x0f + +/* fan 1 */ +#define FSCPOS_REG_FAN1_MIN 0x65 +#define FSCPOS_REG_FAN1_ACT 0x6b +#define FSCPOS_REG_FAN1_STATE 0x62 +#define FSCPOS_REG_FAN1_RIPPLE 0x6f + +/* fan 2 */ +/* min speed fan2 not supported */ +#define FSCPOS_REG_FAN2_ACT 0xab +#define FSCPOS_REG_FAN2_STATE 0xa2 +#define FSCPOS_REG_FAN2_RIPPLE 0x0af + +/* voltage supervision */ +#define FSCPOS_REG_VOLT_12 0x45 +#define FSCPOS_REG_VOLT_5 0x42 +#define FSCPOS_REG_VOLT_BATT 0x48 + +/* temperatures */ +/* sensor 0 */ +#define FSCPOS_REG_TEMP0_ACT 0x64 +#define FSCPOS_REG_TEMP0_STATE 0x71 + +/* sensor 1 */ +#define FSCPOS_REG_TEMP1_ACT 0x32 +#define FSCPOS_REG_TEMP1_STATE 0x81 + +/* sensor 2 */ +#define FSCPOS_REG_TEMP2_ACT 0x35 +#define FSCPOS_REG_TEMP2_STATE 0x91 + + + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + +#define IN_TO_REG(val,nr) (SENSORS_LIMIT((val),0,255)) +#define IN_FROM_REG(val,nr) (val) + +/* Initial limits */ + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered FSCPOS, we need to keep some data in memory. That + data is pointed to by fscpos_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new fscpos client is + allocated. */ +struct fscpos_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 revision; /* revision of chip */ + u8 global_event; /* global event status */ + u8 global_control; /* global control register */ + u8 watchdog[3]; /* watchdog */ + u8 volt[3]; /* 12, 5, battery current */ + u8 temp_act[3]; /* temperature */ + u8 temp_status[3]; /* status of sensor */ + u8 fan_act[3]; /* fans revolutions per second */ + u8 fan_status[3]; /* fan status */ + u8 fan_min[3]; /* fan min value for rps */ + u8 fan_ripple[3]; /* divider for rps */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_fscpos_init(void); +static int __init fscpos_cleanup(void); + +static int fscpos_attach_adapter(struct i2c_adapter *adapter); +static int fscpos_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int fscpos_detach_client(struct i2c_client *client); +static int fscpos_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void fscpos_inc_use(struct i2c_client *client); +static void fscpos_dec_use(struct i2c_client *client); + +static int fscpos_read_value(struct i2c_client *client, u8 register); +static int fscpos_write_value(struct i2c_client *client, u8 register, + u8 value); +static void fscpos_update_client(struct i2c_client *client); +static void fscpos_init_client(struct i2c_client *client); + + +static void fscpos_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void fscpos_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void fscpos_fan_internal(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results, + int nr, int reg_state, int reg_min, int res_ripple); +static void fscpos_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void fscpos_volt(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void fscpos_wdog(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static int fscpos_id = 0; + +static struct i2c_driver fscpos_driver = { + /* name */ "FSCPOS sensor driver", + /* id */ I2C_DRIVERID_FSCPOS, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &fscpos_attach_adapter, + /* detach_client */ &fscpos_detach_client, + /* command */ &fscpos_command, + /* inc_use */ &fscpos_inc_use, + /* dec_use */ &fscpos_dec_use +}; + +/* Used by fscpos_init/cleanup */ +static int __initdata fscpos_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected FSCPOS. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table fscpos_dir_table_template[] = { + {FSCPOS_SYSCTL_REV, "rev", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_in}, + {FSCPOS_SYSCTL_EVENT, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_in}, + {FSCPOS_SYSCTL_CONTROL, "control", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_in}, + {FSCPOS_SYSCTL_TEMP0, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_temp}, + {FSCPOS_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_temp}, + {FSCPOS_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_temp}, + {FSCPOS_SYSCTL_VOLT0, "in0", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_volt}, + {FSCPOS_SYSCTL_VOLT1, "in1", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_volt}, + {FSCPOS_SYSCTL_VOLT2, "in2", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_volt}, + {FSCPOS_SYSCTL_FAN0, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_fan}, + {FSCPOS_SYSCTL_FAN1, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_fan}, + {FSCPOS_SYSCTL_FAN2, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_fan}, + {FSCPOS_SYSCTL_WDOG, "wdog", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscpos_wdog}, + {0} +}; + +int fscpos_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, fscpos_detect); +} + +int fscpos_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct fscpos_data *data; + int err = 0; + const char *type_name, *client_name; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("fscpos.o: fscpos_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access fscpos_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct fscpos_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct fscpos_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &fscpos_driver; + new_client->flags = 0; + + /* Do the remaining detection unless force or force_fscpos parameter */ + if (kind < 0) { + if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_0) != 0x50) + goto ERROR1; + if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_1) != 0x45) + goto ERROR1; + if (fscpos_read_value(new_client, FSCPOS_REG_IDENT_2) != 0x47) + goto ERROR1; + } + + kind = fscpos; + + type_name = "fscpos"; + client_name = "fsc poseidon chip"; + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = fscpos_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + fscpos_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + fscpos_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int fscpos_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct fscpos_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("fscpos.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int fscpos_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void fscpos_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void fscpos_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +int fscpos_read_value(struct i2c_client *client, u8 reg) +{ +#ifdef DEBUG + printk("fscpos: read reg 0x%02x\n",reg); +#endif + return i2c_smbus_read_byte_data(client, reg); +} + +int fscpos_write_value(struct i2c_client *client, u8 reg, u8 value) +{ +#ifdef DEBUG + printk("fscpos: write reg 0x%02x, val 0x%02x\n",reg, value); +#endif + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new FSCPOS. It should set limits, etc. */ +void fscpos_init_client(struct i2c_client *client) +{ + struct fscpos_data *data = client->data; + + /* read revision from chip */ + data->revision = fscpos_read_value(client,FSCPOS_REG_REVISION); + /* setup missing fan2_min value */ + data->fan_min[2] = 0xff; +} + +void fscpos_update_client(struct i2c_client *client) +{ + struct fscpos_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 2 * HZ) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting fscpos update\n"); +#endif + data->temp_act[0] = fscpos_read_value(client, FSCPOS_REG_TEMP0_ACT); + data->temp_act[1] = fscpos_read_value(client, FSCPOS_REG_TEMP1_ACT); + data->temp_act[2] = fscpos_read_value(client, FSCPOS_REG_TEMP2_ACT); + data->temp_status[0] = fscpos_read_value(client, FSCPOS_REG_TEMP0_STATE); + data->temp_status[1] = fscpos_read_value(client, FSCPOS_REG_TEMP1_STATE); + data->temp_status[2] = fscpos_read_value(client, FSCPOS_REG_TEMP2_STATE); + + data->volt[0] = fscpos_read_value(client, FSCPOS_REG_VOLT_12); + data->volt[1] = fscpos_read_value(client, FSCPOS_REG_VOLT_5); + data->volt[2] = fscpos_read_value(client, FSCPOS_REG_VOLT_BATT); + + data->fan_act[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_ACT); + data->fan_act[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_ACT); + data->fan_act[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_ACT); + data->fan_status[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_STATE); + data->fan_status[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_STATE); + data->fan_status[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_STATE); + data->fan_min[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_MIN); + data->fan_min[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_MIN); + /* fan2_min is not supported */ + data->fan_ripple[0] = fscpos_read_value(client, FSCPOS_REG_FAN0_RIPPLE); + data->fan_ripple[1] = fscpos_read_value(client, FSCPOS_REG_FAN1_RIPPLE); + data->fan_ripple[2] = fscpos_read_value(client, FSCPOS_REG_FAN2_RIPPLE); + + data->watchdog[0] = fscpos_read_value(client, FSCPOS_REG_WDOG_PRESET); + data->watchdog[1] = fscpos_read_value(client, FSCPOS_REG_WDOG_STATE); + data->watchdog[2] = fscpos_read_value(client, FSCPOS_REG_WDOG_CONTROL); + + data->global_event = fscpos_read_value(client, FSCPOS_REG_EVENT_STATE); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void fscpos_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscpos_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscpos_update_client(client); + switch(ctl_name) { + case FSCPOS_SYSCTL_REV: + results[0] = data->revision ; + break; + case FSCPOS_SYSCTL_EVENT: + results[0] = data->global_event & 0x1f; + break; + case FSCPOS_SYSCTL_CONTROL: + results[0] = data->global_control & 0x01; + break; + default: + printk("fscpos: ctl_name %d not supported\n", + ctl_name); + *nrels_mag = 0; + return; + } + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if((ctl_name == FSCPOS_SYSCTL_CONTROL) && (*nrels_mag >= 1)) { + data->global_control = (results[0] & 0x01); + printk("fscpos: writing 0x%02x to global_control\n", + data->global_control); + fscpos_write_value(client,FSCPOS_REG_CONTROL, + data->global_control); + } + else + printk("fscpos: writing to chip not supported\n"); + } +} + +#define TEMP_FROM_REG(val) (val-128) + + +void fscpos_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscpos_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscpos_update_client(client); + switch(ctl_name) { + case FSCPOS_SYSCTL_TEMP0: + results[0] = data->temp_status[0] & 0x03; + results[1] = TEMP_FROM_REG(data->temp_act[0]); + break; + case FSCPOS_SYSCTL_TEMP1: + results[0] = data->temp_status[1] & 0x03; + results[1] = TEMP_FROM_REG(data->temp_act[1]); + break; + case FSCPOS_SYSCTL_TEMP2: + results[0] = data->temp_status[2] & 0x03; + results[1] = TEMP_FROM_REG(data->temp_act[2]); + break; + default: + printk("fscpos: ctl_name %d not supported\n", + ctl_name); + *nrels_mag = 0; + return; + } + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if(*nrels_mag >= 1) { + switch(ctl_name) { + case FSCPOS_SYSCTL_TEMP0: + data->temp_status[0] = + (data->temp_status[0] & ~0x02) + | (results[0] & 0x02); + printk("fscpos: writing value 0x%02x " + "to temp0_status\n", + data->temp_status[0]); + fscpos_write_value(client, + FSCPOS_REG_TEMP0_STATE, + data->temp_status[0] & 0x02); + break; + case FSCPOS_SYSCTL_TEMP1: + data->temp_status[1] = (data->temp_status[1] & ~0x02) | (results[0] & 0x02); + printk("fscpos: writing value 0x%02x to temp1_status\n", data->temp_status[1]); + fscpos_write_value(client,FSCPOS_REG_TEMP1_STATE, + data->temp_status[1] & 0x02); + break; + case FSCPOS_SYSCTL_TEMP2: + data->temp_status[2] = (data->temp_status[2] & ~0x02) | (results[0] & 0x02); + printk("fscpos: writing value 0x%02x to temp2_status\n", data->temp_status[2]); + fscpos_write_value(client,FSCPOS_REG_TEMP2_STATE, + data->temp_status[2] & 0x02); + break; + default: + printk("fscpos: ctl_name %d not supported\n",ctl_name); + } + } + else + printk("fscpos: writing to chip not supported\n"); + } +} + +#define VOLT_FROM_REG(val,mult) (val*mult/255) + +void fscpos_volt(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscpos_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + fscpos_update_client(client); + switch(ctl_name) { + case FSCPOS_SYSCTL_VOLT0: + results[0] = VOLT_FROM_REG(data->volt[0],1420); + break; + case FSCPOS_SYSCTL_VOLT1: + results[0] = VOLT_FROM_REG(data->volt[1],660); + break; + case FSCPOS_SYSCTL_VOLT2: + results[0] = VOLT_FROM_REG(data->volt[2],330); + break; + default: + printk("fscpos: ctl_name %d not supported\n", + ctl_name); + *nrels_mag = 0; + return; + } + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + printk("fscpos: writing to chip not supported\n"); + } +} + +void fscpos_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + + switch(ctl_name) { + case FSCPOS_SYSCTL_FAN0: + fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results, + 0,FSCPOS_REG_FAN0_STATE,FSCPOS_REG_FAN0_MIN, + FSCPOS_REG_FAN0_RIPPLE); + break; + case FSCPOS_SYSCTL_FAN1: + fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results, + 1,FSCPOS_REG_FAN1_STATE,FSCPOS_REG_FAN1_MIN, + FSCPOS_REG_FAN1_RIPPLE); + break; + case FSCPOS_SYSCTL_FAN2: + fscpos_fan_internal(client,operation,ctl_name,nrels_mag,results, + 2,FSCPOS_REG_FAN2_STATE,0xff, + FSCPOS_REG_FAN2_RIPPLE); + break; + default: + printk("fscpos: illegal fan nr %d\n",ctl_name); + } +} + +#define RPM_FROM_REG(val) (val*60) + +void fscpos_fan_internal(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results, int nr, + int reg_state, int reg_min, int reg_ripple ) +{ + struct fscpos_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscpos_update_client(client); + results[0] = data->fan_status[nr] & 0x04; + results[1] = data->fan_min[nr]; + results[2] = data->fan_ripple[nr] & 0x03; + results[3] = RPM_FROM_REG(data->fan_act[nr]); + *nrels_mag = 4; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if(*nrels_mag >= 1) { + data->fan_status[nr] = results[0] & 0x04; + printk("fscpos: writing value 0x%02x to fan%d_status\n", + data->fan_status[nr],nr); + fscpos_write_value(client,reg_state, + data->fan_status[nr]); + } + if((*nrels_mag >= 2) && (nr < 2)) { + /* minimal speed for fan2 not supported */ + data->fan_min[nr] = results[1]; + printk("fscpos: writing value 0x%02x to fan%d_min\n", + data->fan_min[nr],nr); + fscpos_write_value(client,reg_min, + data->fan_min[nr]); + } + if(*nrels_mag >= 3) { + if((results[2] & 0x03) == 0) { + printk("fscpos: fan%d ripple 0 not allowed\n",nr); + return; + } + data->fan_ripple[nr] = results[2] & 0x03; + printk("fscpos: writing value 0x%02x to fan%d_ripple\n", + data->fan_ripple[nr],nr); + fscpos_write_value(client,reg_ripple, + data->fan_ripple[nr]); + } + } +} + +void fscpos_wdog(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscpos_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscpos_update_client(client); + results[0] = data->watchdog[0] ; + results[1] = data->watchdog[1] & 0x02; + results[2] = data->watchdog[2] & 0xb0; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->watchdog[0] = results[0] & 0xff; + printk("fscpos: writing value 0x%02x to wdog_preset\n", + data->watchdog[0]); + fscpos_write_value(client,FSCPOS_REG_WDOG_PRESET, + data->watchdog[0]); + } + if (*nrels_mag >= 2) { + data->watchdog[1] = results[1] & 0x02; + printk("fscpos: writing value 0x%02x to wdog_state\n", + data->watchdog[1]); + fscpos_write_value(client,FSCPOS_REG_WDOG_STATE, + data->watchdog[1]); + } + if (*nrels_mag >= 3) { + data->watchdog[2] = results[2] & 0xb0; + printk("fscpos: writing value 0x%02x to wdog_control\n", + data->watchdog[2]); + fscpos_write_value(client,FSCPOS_REG_WDOG_CONTROL, + data->watchdog[2]); + } + } +} + +int __init sensors_fscpos_init(void) +{ + int res; + + printk("fscpos.o version %s (%s)\n", LM_VERSION, LM_DATE); + fscpos_initialized = 0; + + if ((res = i2c_add_driver(&fscpos_driver))) { + printk + ("fscpos.o: Driver registration failed, module not inserted.\n"); + fscpos_cleanup(); + return res; + } + fscpos_initialized++; + return 0; +} + +int __init fscpos_cleanup(void) +{ + int res; + + if (fscpos_initialized >= 1) { + if ((res = i2c_del_driver(&fscpos_driver))) { + printk + ("fscpos.o: Driver deregistration failed, module not removed.\n"); + return res; + } + fscpos_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Hermann Jung based on work from Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("fujitsu siemens poseidon chip driver"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + + +int init_module(void) +{ + return sensors_fscpos_init(); +} + +int cleanup_module(void) +{ + return fscpos_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/fscscy.c Mon Mar 25 15:28:38 CET 2002 +++ linux/drivers/sensors/fscscy.c Mon Mar 25 15:28:38 CET 2002 @@ -0,0 +1,992 @@ +/* + fscscy.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2001 Martin Knoblauch + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + fujitsu siemens scylla chip, + module based on lm80.c, fscpos.c + Copyright (c) 1998, 1999 Frodo Looijaard + and Philip Edelbrock +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +/* temp. because wasn't in kernel 2.4.13 patch */ +#ifndef I2C_DRIVERID_FSCSCY +#define I2C_DRIVERID_FSCSCY 1029 +#endif + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x73, SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(fscscy); + +/* The FSCSCY registers */ + +/* chip identification */ +#define FSCSCY_REG_IDENT_0 0x00 +#define FSCSCY_REG_IDENT_1 0x01 +#define FSCSCY_REG_IDENT_2 0x02 +#define FSCSCY_REG_REVISION 0x03 + +/* global control and status */ +#define FSCSCY_REG_EVENT_STATE 0x04 +#define FSCSCY_REG_CONTROL 0x05 + +/* watchdog */ +#define FSCSCY_REG_WDOG_PRESET 0x28 +#define FSCSCY_REG_WDOG_STATE 0x23 +#define FSCSCY_REG_WDOG_CONTROL 0x21 + +/* +** Fan definitions +** +** _RPMMIN: Minimum speed. Can be set via interface, but only for three of the fans +** FAN1_RPMMIN is wired to Fan 0 (CPU Fans) +** FAN4_RPMMIN is wired to Fan 2 (PS Fans ??) +** FAN5_RPMMIN is wired to Fan 3 (AUX Fans ??) +** _ACT: Actual Fan Speed +** _STATE: Fan status register +** _RIPPLE: Fan speed multiplier +*/ + +/* fan 0 */ +#define FSCSCY_REG_FAN0_RPMMIN 0x65 +#define FSCSCY_REG_FAN0_ACT 0x6b +#define FSCSCY_REG_FAN0_STATE 0x62 +#define FSCSCY_REG_FAN0_RIPPLE 0x6f + +/* fan 1 */ +#define FSCSCY_REG_FAN1_RPMMIN FSCSCY_REG_FAN0_RPMMIN +#define FSCSCY_REG_FAN1_ACT 0x6c +#define FSCSCY_REG_FAN1_STATE 0x61 +#define FSCSCY_REG_FAN1_RIPPLE 0x6f + +/* fan 2 */ +#define FSCSCY_REG_FAN2_RPMMIN 0x55 +#define FSCSCY_REG_FAN2_ACT 0x0e +#define FSCSCY_REG_FAN2_STATE 0x0d +#define FSCSCY_REG_FAN2_RIPPLE 0x0f + +/* fan 3 */ +#define FSCSCY_REG_FAN3_RPMMIN 0xa5 +#define FSCSCY_REG_FAN3_ACT 0xab +#define FSCSCY_REG_FAN3_STATE 0xa2 +#define FSCSCY_REG_FAN3_RIPPLE 0xaf + +/* fan 4 */ +#define FSCSCY_REG_FAN4_RPMMIN FSCSCY_REG_FAN2_RPMMIN +#define FSCSCY_REG_FAN4_ACT 0x5c +#define FSCSCY_REG_FAN4_STATE 0x52 +#define FSCSCY_REG_FAN4_RIPPLE 0x0f + +/* fan 5 */ +#define FSCSCY_REG_FAN5_RPMMIN FSCSCY_REG_FAN3_RPMMIN +#define FSCSCY_REG_FAN5_ACT 0xbb +#define FSCSCY_REG_FAN5_STATE 0xb2 +#define FSCSCY_REG_FAN5_RIPPLE 0xbf + +/* voltage supervision */ +#define FSCSCY_REG_VOLT_12 0x45 +#define FSCSCY_REG_VOLT_5 0x42 +#define FSCSCY_REG_VOLT_BATT 0x48 + +/* temperatures */ +/* sensor 0 */ +#define FSCSCY_REG_TEMP0_ACT 0x64 +#define FSCSCY_REG_TEMP0_STATE 0x71 +#define FSCSCY_REG_TEMP0_LIM 0x76 + +/* sensor 1 */ +#define FSCSCY_REG_TEMP1_ACT 0xD0 +#define FSCSCY_REG_TEMP1_STATE 0xD1 +#define FSCSCY_REG_TEMP1_LIM 0xD6 + +/* sensor 2 */ +#define FSCSCY_REG_TEMP2_ACT 0x32 +#define FSCSCY_REG_TEMP2_STATE 0x81 +#define FSCSCY_REG_TEMP2_LIM 0x86 + +/* sensor3 */ +#define FSCSCY_REG_TEMP3_ACT 0x35 +#define FSCSCY_REG_TEMP3_STATE 0x91 +#define FSCSCY_REG_TEMP3_LIM 0x96 + +/* PCI Load */ +#define FSCSCY_REG_PCILOAD 0x1a + +/* Intrusion Sensor */ +#define FSCSCY_REG_INTR_STATE 0x13 +#define FSCSCY_REG_INTR_CTRL 0x12 + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + +#define IN_TO_REG(val,nr) (SENSORS_LIMIT((val),0,255)) +#define IN_FROM_REG(val,nr) (val) + +/* Initial limits */ + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered FSCSCY, we need to keep some data in memory. That + data is pointed to by fscscy_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new fscscy client is + allocated. */ +struct fscscy_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 revision; /* revision of chip */ + u8 global_event; /* global event status */ + u8 global_control; /* global control register */ + u8 watchdog[3]; /* watchdog */ + u8 volt[3]; /* 12, 5, battery current */ + u8 volt_min[3]; /* minimum voltages over module "lifetime" */ + u8 volt_max[3]; /* maximum voltages over module "lifetime" */ + u8 temp_act[4]; /* temperature */ + u8 temp_status[4]; /* status of temp. sensor */ + u8 temp_lim[4]; /* limit temperature of temp. sensor */ + u8 temp_min[4]; /* minimum of temp. sensor, this is just calculated by the module */ + u8 temp_max[4]; /* maximum of temp. sensor, this is just calculsted by the module */ + u8 fan_act[6]; /* fans revolutions per second */ + u8 fan_status[6]; /* fan status */ + u8 fan_rpmmin[6]; /* fan min value for rps */ + u8 fan_ripple[6]; /* divider for rps */ + u8 fan_min[6]; /* minimum RPM over module "lifetime" */ + u8 fan_max[6]; /* maximum RPM over module "lifetime" */ + u8 pciload; /* PCILoad value */ + u8 intr_status; /* Intrusion Status */ + u8 intr_control; /* Intrusion Control */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_fscscy_init(void); +static int __init fscscy_cleanup(void); + +static int fscscy_attach_adapter(struct i2c_adapter *adapter); +static int fscscy_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int fscscy_detach_client(struct i2c_client *client); +static int fscscy_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void fscscy_inc_use(struct i2c_client *client); +static void fscscy_dec_use(struct i2c_client *client); + +static int fscscy_read_value(struct i2c_client *client, u8 register); +static int fscscy_write_value(struct i2c_client *client, u8 register, + u8 value); +static void fscscy_update_client(struct i2c_client *client); +static void fscscy_init_client(struct i2c_client *client); + + +static void fscscy_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void fscscy_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void fscscy_fan_internal(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results, + int nr, int reg_state, int reg_min, int res_ripple); +static void fscscy_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void fscscy_volt(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void fscscy_wdog(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void fscscy_pciload(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void fscscy_intrusion(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static int fscscy_id = 0; + +static struct i2c_driver fscscy_driver = { + /* name */ "FSCSCY sensor driver", + /* id */ I2C_DRIVERID_FSCSCY, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &fscscy_attach_adapter, + /* detach_client */ &fscscy_detach_client, + /* command */ &fscscy_command, + /* inc_use */ &fscscy_inc_use, + /* dec_use */ &fscscy_dec_use +}; + +/* Used by fscscy_init/cleanup */ +static int __initdata fscscy_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected FSCSCY. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table fscscy_dir_table_template[] = { + {FSCSCY_SYSCTL_REV, "rev", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_in}, + {FSCSCY_SYSCTL_EVENT, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_in}, + {FSCSCY_SYSCTL_CONTROL, "control", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_in}, + {FSCSCY_SYSCTL_TEMP0, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_temp}, + {FSCSCY_SYSCTL_TEMP1, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_temp}, + {FSCSCY_SYSCTL_TEMP2, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_temp}, + {FSCSCY_SYSCTL_TEMP3, "temp4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_temp}, + {FSCSCY_SYSCTL_VOLT0, "in0", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_volt}, + {FSCSCY_SYSCTL_VOLT1, "in1", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_volt}, + {FSCSCY_SYSCTL_VOLT2, "in2", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_volt}, + {FSCSCY_SYSCTL_FAN0, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_fan}, + {FSCSCY_SYSCTL_FAN1, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_fan}, + {FSCSCY_SYSCTL_FAN2, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_fan}, + {FSCSCY_SYSCTL_FAN3, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_fan}, + {FSCSCY_SYSCTL_FAN4, "fan5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_fan}, + {FSCSCY_SYSCTL_FAN5, "fan6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_fan}, + {FSCSCY_SYSCTL_WDOG, "wdog", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_wdog}, + {FSCSCY_SYSCTL_PCILOAD, "pciload", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_pciload}, + {FSCSCY_SYSCTL_INTRUSION, "intrusion", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &fscscy_intrusion}, + {0} +}; + +int fscscy_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, fscscy_detect); +} + +int fscscy_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct fscscy_data *data; + int err = 0; + const char *type_name, *client_name; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("fscscy.o: fscscy_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access fscscy_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct fscscy_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct fscscy_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &fscscy_driver; + new_client->flags = 0; + + /* Do the remaining detection unless force or force_fscscy parameter */ + if (kind < 0) { + if (fscscy_read_value(new_client, FSCSCY_REG_IDENT_0) != 0x53) + goto ERROR1; + if (fscscy_read_value(new_client, FSCSCY_REG_IDENT_1) != 0x43) + goto ERROR1; + if (fscscy_read_value(new_client, FSCSCY_REG_IDENT_2) != 0x59) + goto ERROR1; + } + + kind = fscscy; + + type_name = "fscscy"; + client_name = "fsc scylla chip"; + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = fscscy_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + fscscy_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + fscscy_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int fscscy_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct fscscy_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("fscscy.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int fscscy_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void fscscy_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void fscscy_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +int fscscy_read_value(struct i2c_client *client, u8 reg) +{ +#ifdef DEBUG + printk("fscscy: read reg 0x%02x\n",reg); +#endif + return i2c_smbus_read_byte_data(client, reg); +} + +int fscscy_write_value(struct i2c_client *client, u8 reg, u8 value) +{ +#ifdef DEBUG + printk("fscscy: write reg 0x%02x, val 0x%02x\n",reg, value); +#endif + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new FSCSCY. It should set limits, etc. */ +void fscscy_init_client(struct i2c_client *client) +{ + struct fscscy_data *data = client->data; + + /* read revision from chip */ + data->revision = fscscy_read_value(client,FSCSCY_REG_REVISION); + + /* Initialize min/max values from chip */ + data->fan_min[0] = data->fan_max[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_ACT); + data->fan_min[1] = data->fan_max[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_ACT); + data->fan_min[2] = data->fan_max[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_ACT); + data->fan_min[3] = data->fan_max[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_ACT); + data->fan_min[4] = data->fan_max[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_ACT); + data->fan_min[4] = data->fan_max[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_ACT); + data->temp_min[0] = data->temp_max[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_ACT); + data->temp_min[1] = data->temp_max[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_ACT); + data->temp_min[2] = data->temp_max[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_ACT); + data->temp_min[3] = data->temp_max[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_ACT); + data->volt_min[0] = data->volt_max[0] = fscscy_read_value(client, FSCSCY_REG_VOLT_12); + data->volt_min[1] = data->volt_max[1] = fscscy_read_value(client, FSCSCY_REG_VOLT_5); + data->volt_min[2] = data->volt_max[2] = fscscy_read_value(client, FSCSCY_REG_VOLT_BATT); +} + +void fscscy_update_client(struct i2c_client *client) +{ + struct fscscy_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 2 * HZ) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting fscscy update\n"); +#endif + data->temp_act[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_ACT); + if (data->temp_min[0] > data->temp_act[0]) data->temp_min[0] = data->temp_act[0]; + if (data->temp_max[0] < data->temp_act[0]) data->temp_max[0] = data->temp_act[0]; + data->temp_act[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_ACT); + if (data->temp_min[1] > data->temp_act[1]) data->temp_min[1] = data->temp_act[1]; + if (data->temp_max[1] < data->temp_act[1]) data->temp_max[1] = data->temp_act[1]; + data->temp_act[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_ACT); + if (data->temp_min[2] > data->temp_act[2]) data->temp_min[2] = data->temp_act[2]; + if (data->temp_max[2] < data->temp_act[2]) data->temp_max[2] = data->temp_act[2]; + data->temp_act[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_ACT); + if (data->temp_min[3] > data->temp_act[3]) data->temp_min[3] = data->temp_act[3]; + if (data->temp_max[3] < data->temp_act[3]) data->temp_max[3] = data->temp_act[3]; + data->temp_status[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_STATE); + data->temp_status[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_STATE); + data->temp_status[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_STATE); + data->temp_status[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_STATE); + data->temp_lim[0] = fscscy_read_value(client, FSCSCY_REG_TEMP0_LIM); + data->temp_lim[1] = fscscy_read_value(client, FSCSCY_REG_TEMP1_LIM); + data->temp_lim[2] = fscscy_read_value(client, FSCSCY_REG_TEMP2_LIM); + data->temp_lim[3] = fscscy_read_value(client, FSCSCY_REG_TEMP3_LIM); + + data->volt[0] = fscscy_read_value(client, FSCSCY_REG_VOLT_12); + if (data->volt_min[0] > data->volt[0]) data->volt_min[0] = data->volt[0]; + if (data->volt_max[0] < data->volt[0]) data->volt_max[0] = data->volt[0]; + data->volt[1] = fscscy_read_value(client, FSCSCY_REG_VOLT_5); + if (data->volt_min[1] > data->volt[1]) data->volt_min[1] = data->volt[1]; + if (data->volt_max[1] < data->volt[1]) data->volt_max[1] = data->volt[1]; + data->volt[2] = fscscy_read_value(client, FSCSCY_REG_VOLT_BATT); + if (data->volt_min[2] > data->volt[2]) data->volt_min[2] = data->volt[2]; + if (data->volt_max[2] < data->volt[2]) data->volt_max[2] = data->volt[2]; + + data->fan_act[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_ACT); + if (data->fan_min[0] > data->fan_act[0]) data->fan_min[0] = data->fan_act[0]; + if (data->fan_max[0] < data->fan_act[0]) data->fan_max[0] = data->fan_act[0]; + data->fan_act[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_ACT); + if (data->fan_min[1] > data->fan_act[1]) data->fan_min[1] = data->fan_act[1]; + if (data->fan_max[1] < data->fan_act[1]) data->fan_max[1] = data->fan_act[1]; + data->fan_act[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_ACT); + if (data->fan_min[2] > data->fan_act[2]) data->fan_min[2] = data->fan_act[2]; + if (data->fan_max[2] < data->fan_act[2]) data->fan_max[2] = data->fan_act[2]; + data->fan_act[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_ACT); + if (data->fan_min[3] > data->fan_act[3]) data->fan_min[3] = data->fan_act[3]; + if (data->fan_max[3] < data->fan_act[3]) data->fan_max[3] = data->fan_act[3]; + data->fan_act[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_ACT); + if (data->fan_min[4] > data->fan_act[4]) data->fan_min[4] = data->fan_act[4]; + if (data->fan_max[4] < data->fan_act[4]) data->fan_max[4] = data->fan_act[4]; + data->fan_act[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_ACT); + if (data->fan_min[5] > data->fan_act[5]) data->fan_min[5] = data->fan_act[5]; + if (data->fan_max[5] < data->fan_act[5]) data->fan_max[5] = data->fan_act[5]; + data->fan_status[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_STATE); + data->fan_status[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_STATE); + data->fan_status[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_STATE); + data->fan_status[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_STATE); + data->fan_status[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_STATE); + data->fan_status[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_STATE); + data->fan_rpmmin[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_RPMMIN); + data->fan_rpmmin[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_RPMMIN); + data->fan_rpmmin[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_RPMMIN); + data->fan_rpmmin[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_RPMMIN); + data->fan_rpmmin[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_RPMMIN); + data->fan_rpmmin[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_RPMMIN); + data->fan_ripple[0] = fscscy_read_value(client, FSCSCY_REG_FAN0_RIPPLE); + data->fan_ripple[1] = fscscy_read_value(client, FSCSCY_REG_FAN1_RIPPLE); + data->fan_ripple[2] = fscscy_read_value(client, FSCSCY_REG_FAN2_RIPPLE); + data->fan_ripple[3] = fscscy_read_value(client, FSCSCY_REG_FAN3_RIPPLE); + data->fan_ripple[4] = fscscy_read_value(client, FSCSCY_REG_FAN4_RIPPLE); + data->fan_ripple[5] = fscscy_read_value(client, FSCSCY_REG_FAN5_RIPPLE); + + data->watchdog[0] = fscscy_read_value(client, FSCSCY_REG_WDOG_PRESET); + data->watchdog[1] = fscscy_read_value(client, FSCSCY_REG_WDOG_STATE); + data->watchdog[2] = fscscy_read_value(client, FSCSCY_REG_WDOG_CONTROL); + + data->global_event = fscscy_read_value(client, FSCSCY_REG_EVENT_STATE); + data->global_control = fscscy_read_value(client, FSCSCY_REG_CONTROL); + data->pciload = fscscy_read_value(client, FSCSCY_REG_PCILOAD); + data->intr_status = fscscy_read_value(client, FSCSCY_REG_INTR_STATE); + data->intr_control = fscscy_read_value(client, FSCSCY_REG_INTR_CTRL); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void fscscy_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscscy_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscscy_update_client(client); + switch(ctl_name) { + case FSCSCY_SYSCTL_REV: + results[0] = data->revision ; + break; + case FSCSCY_SYSCTL_EVENT: + results[0] = data->global_event & 0x9f; /* MKN */ + break; + case FSCSCY_SYSCTL_CONTROL: + results[0] = data->global_control & 0x19; /* MKN */ + break; + default: + printk("fscscy: ctl_name %d not supported\n", + ctl_name); + *nrels_mag = 0; + return; + } + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if((ctl_name == FSCSCY_SYSCTL_CONTROL) && (*nrels_mag >= 1)) { + data->global_control = (data->global_control & 0x18) | (results[0] & 0x01); /* MKN */ + printk("fscscy: writing 0x%02x to global_control\n", + data->global_control); + fscscy_write_value(client,FSCSCY_REG_CONTROL, + data->global_control); + } + else + printk("fscscy: writing to chip not supported\n"); + } +} + +#define TEMP_FROM_REG(val) (val-128) + + +void fscscy_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscscy_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscscy_update_client(client); + switch(ctl_name) { + case FSCSCY_SYSCTL_TEMP0: + results[0] = data->temp_status[0] & 0x03; + results[1] = TEMP_FROM_REG(data->temp_act[0]); + results[2] = TEMP_FROM_REG(data->temp_lim[0]); + results[3] = TEMP_FROM_REG(data->temp_min[0]); + results[4] = TEMP_FROM_REG(data->temp_max[0]); + break; + case FSCSCY_SYSCTL_TEMP1: + results[0] = data->temp_status[1] & 0x03; + results[1] = TEMP_FROM_REG(data->temp_act[1]); + results[2] = TEMP_FROM_REG(data->temp_lim[1]); + results[3] = TEMP_FROM_REG(data->temp_min[1]); + results[4] = TEMP_FROM_REG(data->temp_max[1]); + break; + case FSCSCY_SYSCTL_TEMP2: + results[0] = data->temp_status[2] & 0x03; + results[1] = TEMP_FROM_REG(data->temp_act[2]); + results[2] = TEMP_FROM_REG(data->temp_lim[2]); + results[3] = TEMP_FROM_REG(data->temp_min[2]); + results[4] = TEMP_FROM_REG(data->temp_max[2]); + break; + case FSCSCY_SYSCTL_TEMP3: + results[0] = data->temp_status[3] & 0x03; + results[1] = TEMP_FROM_REG(data->temp_act[3]); + results[2] = TEMP_FROM_REG(data->temp_lim[3]); + results[3] = TEMP_FROM_REG(data->temp_min[3]); + results[4] = TEMP_FROM_REG(data->temp_max[3]); + break; + default: + printk("fscscy: ctl_name %d not supported\n", + ctl_name); + *nrels_mag = 0; + return; + } + *nrels_mag = 5; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if(*nrels_mag >= 1) { + switch(ctl_name) { + case FSCSCY_SYSCTL_TEMP0: + data->temp_status[0] = + (data->temp_status[0] & ~0x02) + | (results[0] & 0x02); + printk("fscscy: writing value 0x%02x " + "to temp0_status\n", + data->temp_status[0]); + fscscy_write_value(client, + FSCSCY_REG_TEMP0_STATE, + data->temp_status[0] & 0x02); + break; + case FSCSCY_SYSCTL_TEMP1: + data->temp_status[1] = (data->temp_status[1] & ~0x02) | (results[0] & 0x02); + printk("fscscy: writing value 0x%02x to temp1_status\n", data->temp_status[1]); + fscscy_write_value(client,FSCSCY_REG_TEMP1_STATE, + data->temp_status[1] & 0x02); + break; + case FSCSCY_SYSCTL_TEMP2: + data->temp_status[2] = (data->temp_status[2] & ~0x02) | (results[0] & 0x02); + printk("fscscy: writing value 0x%02x to temp2_status\n", data->temp_status[2]); + fscscy_write_value(client,FSCSCY_REG_TEMP2_STATE, + data->temp_status[2] & 0x02); + break; + case FSCSCY_SYSCTL_TEMP3: + data->temp_status[3] = (data->temp_status[3] & ~0x02) | (results[0] & 0x02); + printk("fscscy: writing value 0x%02x to temp3_status\n", data->temp_status[3]); + fscscy_write_value(client,FSCSCY_REG_TEMP3_STATE, + data->temp_status[3] & 0x02); + break; + default: + printk("fscscy: ctl_name %d not supported\n",ctl_name); + } + } + else + printk("fscscy: writing to chip not supported\n"); + } +} + +#define VOLT_FROM_REG(val,mult) (val*mult/255) + +void fscscy_volt(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscscy_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + fscscy_update_client(client); + switch(ctl_name) { + case FSCSCY_SYSCTL_VOLT0: + results[0] = VOLT_FROM_REG(data->volt[0],1420); + results[1] = VOLT_FROM_REG(data->volt_min[0],1420); + results[2] = VOLT_FROM_REG(data->volt_max[0],1420); + break; + case FSCSCY_SYSCTL_VOLT1: + results[0] = VOLT_FROM_REG(data->volt[1],660); + results[1] = VOLT_FROM_REG(data->volt_min[1],660); + results[2] = VOLT_FROM_REG(data->volt_max[1],660); + break; + case FSCSCY_SYSCTL_VOLT2: + results[0] = VOLT_FROM_REG(data->volt[2],330); + results[1] = VOLT_FROM_REG(data->volt_min[2],330); + results[2] = VOLT_FROM_REG(data->volt_max[2],330); + break; + default: + printk("fscscy: ctl_name %d not supported\n", + ctl_name); + *nrels_mag = 0; + return; + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + printk("fscscy: writing to chip not supported\n"); + } +} + +void fscscy_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + + switch(ctl_name) { + case FSCSCY_SYSCTL_FAN0: + fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results, + 0,FSCSCY_REG_FAN0_STATE,FSCSCY_REG_FAN0_RPMMIN, + FSCSCY_REG_FAN0_RIPPLE); + break; + case FSCSCY_SYSCTL_FAN1: + fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results, + 1,FSCSCY_REG_FAN1_STATE,FSCSCY_REG_FAN1_RPMMIN, + FSCSCY_REG_FAN1_RIPPLE); + break; + case FSCSCY_SYSCTL_FAN2: + fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results, + 2,FSCSCY_REG_FAN2_STATE,FSCSCY_REG_FAN2_RPMMIN, + FSCSCY_REG_FAN2_RIPPLE); + break; + case FSCSCY_SYSCTL_FAN3: + fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results, + 3,FSCSCY_REG_FAN3_STATE,FSCSCY_REG_FAN3_RPMMIN, + FSCSCY_REG_FAN3_RIPPLE); + break; + case FSCSCY_SYSCTL_FAN4: + fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results, + 4,FSCSCY_REG_FAN4_STATE,FSCSCY_REG_FAN4_RPMMIN, + FSCSCY_REG_FAN4_RIPPLE); + break; + case FSCSCY_SYSCTL_FAN5: + fscscy_fan_internal(client,operation,ctl_name,nrels_mag,results, + 5,FSCSCY_REG_FAN5_STATE,FSCSCY_REG_FAN5_RPMMIN, + FSCSCY_REG_FAN5_RIPPLE); + break; + default: + printk("fscscy: illegal fan nr %d\n",ctl_name); + } +} + +#define RPM_FROM_REG(val) (val*60) + +void fscscy_fan_internal(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results, int nr, + int reg_state, int reg_min, int reg_ripple ) +{ + struct fscscy_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscscy_update_client(client); + results[0] = data->fan_status[nr] & 0x0f; /* MKN */ + results[1] = data->fan_rpmmin[nr]; + results[2] = data->fan_ripple[nr] & 0x03; + results[3] = RPM_FROM_REG(data->fan_act[nr]); + results[4] = RPM_FROM_REG(data->fan_min[nr]); + results[5] = RPM_FROM_REG(data->fan_max[nr]); + *nrels_mag = 6; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if(*nrels_mag >= 1) { + data->fan_status[nr] = (data->fan_status[nr] & 0x0b) | (results[0] & 0x04); /* MKN */ + printk("fscscy: writing value 0x%02x to fan%d_status\n", + data->fan_status[nr],nr); + fscscy_write_value(client,reg_state, + data->fan_status[nr]); + } + if(*nrels_mag >= 2) { + if((results[1] & 0xff) == 0) { + printk("fscscy: fan%d rpmmin 0 not allowed for safety reasons\n",nr); + return; + } + data->fan_rpmmin[nr] = results[1]; + printk("fscscy: writing value 0x%02x to fan%d_min\n", + data->fan_rpmmin[nr],nr); + fscscy_write_value(client,reg_min, + data->fan_rpmmin[nr]); + } + if(*nrels_mag >= 3) { + if((results[2] & 0x03) == 0) { + printk("fscscy: fan%d ripple 0 is nonsense/not allowed\n",nr); + return; + } + data->fan_ripple[nr] = results[2] & 0x03; + printk("fscscy: writing value 0x%02x to fan%d_ripple\n", + data->fan_ripple[nr],nr); + fscscy_write_value(client,reg_ripple, + data->fan_ripple[nr]); + } + } +} + +void fscscy_wdog(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscscy_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscscy_update_client(client); + results[0] = data->watchdog[0] ; + results[1] = data->watchdog[1] & 0x02; + results[2] = data->watchdog[2] & 0xb0; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->watchdog[0] = results[0] & 0xff; + printk("fscscy: writing value 0x%02x to wdog_preset\n", + data->watchdog[0]); + fscscy_write_value(client,FSCSCY_REG_WDOG_PRESET, + data->watchdog[0]); + } + if (*nrels_mag >= 2) { + data->watchdog[1] = results[1] & 0x02; + printk("fscscy: writing value 0x%02x to wdog_state\n", + data->watchdog[1]); + fscscy_write_value(client,FSCSCY_REG_WDOG_STATE, + data->watchdog[1]); + } + if (*nrels_mag >= 3) { + data->watchdog[2] = results[2] & 0xb0; + printk("fscscy: writing value 0x%02x to wdog_control\n", + data->watchdog[2]); + fscscy_write_value(client,FSCSCY_REG_WDOG_CONTROL, + data->watchdog[2]); + } + } +} + +void fscscy_pciload(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscscy_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscscy_update_client(client); + results[0] = data->pciload; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + printk("fscscy: writing PCILOAD to chip not supported\n"); + } +} + +void fscscy_intrusion(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct fscscy_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + fscscy_update_client(client); + results[0] = data->intr_control & 0x80; + results[1] = data->intr_status & 0xc0; + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->intr_control = results[0] & 0x80; + printk("fscscy: writing value 0x%02x to intr_control\n", + data->intr_control); + fscscy_write_value(client,FSCSCY_REG_INTR_CTRL, + data->intr_control); + } + } +} + +int __init sensors_fscscy_init(void) +{ + int res; + + printk("fscscy.o version %s (%s)\n", LM_VERSION, LM_DATE); + fscscy_initialized = 0; + + if ((res = i2c_add_driver(&fscscy_driver))) { + printk + ("fscscy.o: Driver registration failed, module not inserted.\n"); + fscscy_cleanup(); + return res; + } + fscscy_initialized++; + return 0; +} + +int __init fscscy_cleanup(void) +{ + int res; + + if (fscscy_initialized >= 1) { + if ((res = i2c_del_driver(&fscscy_driver))) { + printk + ("fscscy.o: Driver deregistration failed, module not removed.\n"); + return res; + } + fscscy_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Martin Knoblauch based on work (fscpos) from Hermann Jung "); +MODULE_DESCRIPTION("fujitsu siemens scylla chip driver"); + +int init_module(void) +{ + return sensors_fscscy_init(); +} + +int cleanup_module(void) +{ + return fscscy_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/gl518sm.c Mon Mar 25 15:28:38 CET 2002 +++ linux/drivers/sensors/gl518sm.c Mon Mar 25 15:28:38 CET 2002 @@ -0,0 +1,1125 @@ +/* + gl518sm.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard , + 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 +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#ifdef __SMP__ +#include +#endif + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_2(gl518sm_r00, gl518sm_r80); + +/* Defining this will enable debug messages for the voltage iteration + code used with rev 0 ICs */ +#undef DEBUG_VIN + +/* 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 0x04 +#define GL518_REG_TEMP_OVER 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 TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-5:(val)+5) / 10)+119),\ + 0,255)) +#define TEMP_FROM_REG(val) (((val) - 119) * 10) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((960000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) \ + ( (val)==0 ? 0 : (val)==255 ? 0 : (960000/((val)*(div))) ) + +#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*10+8)/19),0,255)) +#define IN_FROM_REG(val) (((val)*19)/10) + +#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*10+11)/23),0,255)) +#define VDD_FROM_REG(val) (((val)*23)/10) + +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) +#define DIV_FROM_REG(val) (1 << (val)) + +#define ALARMS_FROM_REG(val) val + +#define BEEP_ENABLE_TO_REG(val) ((val)?0:1) +#define BEEP_ENABLE_FROM_REG(val) ((val)?0:1) + +#define BEEPS_TO_REG(val) ((val) & 0x7f) +#define BEEPS_FROM_REG(val) ((val) & 0x7f) + +/* Initial values */ +#define GL518_INIT_TEMP_OVER 600 +#define GL518_INIT_TEMP_HYST 500 +#define GL518_INIT_FAN_MIN_1 3000 +#define GL518_INIT_FAN_MIN_2 3000 + +/* These are somewhat sane */ +#define GL518_INIT_VIN_1 330 /* 3.3 V */ +#define GL518_INIT_VIN_2 286 /* 12 V */ +#define GL518_INIT_VIN_3 260 /* Vcore */ +#define GL518_INIT_VDD 500 /* 5 V */ + +#define GL518_INIT_PERCENTAGE 10 + +#define GL518_INIT_VIN_MIN_1 \ + (GL518_INIT_VIN_1 - GL518_INIT_VIN_1 * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VIN_MAX_1 \ + (GL518_INIT_VIN_1 + GL518_INIT_VIN_1 * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VIN_MIN_2 \ + (GL518_INIT_VIN_2 - GL518_INIT_VIN_2 * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VIN_MAX_2 \ + (GL518_INIT_VIN_2 + GL518_INIT_VIN_2 * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VIN_MIN_3 \ + (GL518_INIT_VIN_3 - GL518_INIT_VIN_3 * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VIN_MAX_3 \ + (GL518_INIT_VIN_3 + GL518_INIT_VIN_3 * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VDD_MIN \ + (GL518_INIT_VDD - GL518_INIT_VDD * GL518_INIT_PERCENTAGE / 100) +#define GL518_INIT_VDD_MAX \ + (GL518_INIT_VDD + GL518_INIT_VDD * GL518_INIT_PERCENTAGE / 100) + + +/* Each client has this additional data */ +struct gl518_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + + int iterate_lock; + int quit_thread; + struct task_struct *thread; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) + wait_queue_head_t wq; +#else + struct wait_queue *wq; +#endif + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long last_updated_v00; + /* In jiffies (used only by rev00 chips) */ + + u8 voltage[4]; /* Register values; [0] = VDD */ + u8 voltage_min[4]; /* Register values; [0] = VDD */ + u8 voltage_max[4]; /* Register values; [0] = VDD */ + u8 iter_voltage[4]; /* Register values; [0] = VDD */ + u8 fan[2]; + u8 fan_min[2]; + u8 temp; /* Register values */ + u8 temp_over; /* Register values */ + u8 temp_hyst; /* Register values */ + u8 alarms, beeps; /* Register value */ + u8 alarm_mask; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u8 beep_enable; /* Boolean */ + u8 iterate; /* Voltage iteration mode */ +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_gl518sm_init(void); +static int __init gl518_cleanup(void); +static int gl518_attach_adapter(struct i2c_adapter *adapter); +static int gl518_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void gl518_init_client(struct i2c_client *client); +static int gl518_detach_client(struct i2c_client *client); +static int gl518_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void gl518_inc_use(struct i2c_client *client); +static void gl518_dec_use(struct i2c_client *client); +static u16 swap_bytes(u16 val); +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); + +static void gl518_update_client_rev00(struct i2c_client *client); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,68) +static int gl518_update_thread(void *data); +#endif +static void gl518_update_iterate(struct i2c_client *client); + +static void gl518_vin(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_beep(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_fan1off(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl518_iterate(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* This is the driver that will be inserted */ +static struct i2c_driver gl518_driver = { + /* name */ "GL518SM sensor chip driver", + /* id */ I2C_DRIVERID_GL518, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &gl518_attach_adapter, + /* detach_client */ &gl518_detach_client, + /* command */ &gl518_command, + /* inc_use */ &gl518_inc_use, + /* dec_use */ &gl518_dec_use +}; + +/* These files are created for each detected GL518. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table gl518_dir_table_template[] = { + {GL518_SYSCTL_VIN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_vin}, + {GL518_SYSCTL_VIN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_vin}, + {GL518_SYSCTL_VIN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_vin}, + {GL518_SYSCTL_VDD, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_vin}, + {GL518_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_fan}, + {GL518_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_fan}, + {GL518_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_temp}, + {GL518_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_fan_div}, + {GL518_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_alarms}, + {GL518_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_beep}, + {GL518_SYSCTL_FAN1OFF, "fan1off", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_fan1off}, + {GL518_SYSCTL_ITERATE, "iterate", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl518_iterate}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata gl518_initialized = 0; + +/* I choose here for semi-static GL518SM allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +#define MAX_GL518_NR 4 +static struct i2c_client *gl518_list[MAX_GL518_NR]; + +int gl518_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, gl518_detect); +} + +static int gl518_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct gl518_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("gl518sm.o: gl518_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access gl518_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct gl518_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct gl518_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + 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 ERROR1; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = gl518_read_value(new_client, GL518_REG_REVISION); + if (i == 0x00) + kind = gl518sm_r00; + else if (i == 0x80) + kind = gl518sm_r80; + else { + if (kind == 0) + printk + ("gl518sm.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == gl518sm_r00) { + type_name = "gl518sm"; + client_name = "GL518SM Revision 0x00 chip"; + } else if (kind == gl518sm_r80) { + type_name = "gl518sm"; + client_name = "GL518SM Revision 0x80 chip"; + } else { +#ifdef DEBUG + printk("gl518sm.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + for (i = 0; i < MAX_GL518_NR; i++) + if (!gl518_list[i]) + break; + if (i == MAX_GL518_NR) { + printk + ("gl518sm.o: No empty slots left, recompile and heighten " + "MAX_GL518_NR!\n"); + err = -ENOMEM; + goto ERROR2; + } + gl518_list[i] = new_client; + new_client->id = i; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry((struct i2c_client *) new_client, + type_name, + gl518_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the GL518SM chip */ + data->iterate = 0; + data->iterate_lock = 0; + data->quit_thread = 0; + data->thread = NULL; + data->alarm_mask = 0xff; + data->voltage[0]=data->voltage[1]=data->voltage[2]=0; + gl518_init_client((struct i2c_client *) new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + for (i = 0; i < MAX_GL518_NR; i++) + if (new_client == gl518_list[i]) + gl518_list[i] = NULL; + ERROR2: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + + +/* Called when we have found a new GL518SM. It should set limits, etc. */ +void gl518_init_client(struct i2c_client *client) +{ + /* Power-on defaults (bit 7=1) */ + gl518_write_value(client, GL518_REG_CONF, 0x80); + + /* No noisy output (bit 2=1), Comparator mode (bit 3=0), two fans (bit4=0), + standby mode (bit6=0) */ + gl518_write_value(client, GL518_REG_CONF, 0x04); + + /* Never interrupts */ + gl518_write_value(client, GL518_REG_MASK, 0x00); + + gl518_write_value(client, GL518_REG_TEMP_HYST, + TEMP_TO_REG(GL518_INIT_TEMP_HYST)); + gl518_write_value(client, GL518_REG_TEMP_OVER, + TEMP_TO_REG(GL518_INIT_TEMP_OVER)); + gl518_write_value(client, GL518_REG_MISC, (DIV_TO_REG(2) << 6) | + (DIV_TO_REG(2) << 4)); + gl518_write_value(client, GL518_REG_FAN_LIMIT, + (FAN_TO_REG(GL518_INIT_FAN_MIN_1, 2) << 8) | + FAN_TO_REG(GL518_INIT_FAN_MIN_2, 2)); + gl518_write_value(client, GL518_REG_VIN1_LIMIT, + (IN_TO_REG(GL518_INIT_VIN_MAX_1) << 8) | + IN_TO_REG(GL518_INIT_VIN_MIN_1)); + gl518_write_value(client, GL518_REG_VIN2_LIMIT, + (IN_TO_REG(GL518_INIT_VIN_MAX_2) << 8) | + IN_TO_REG(GL518_INIT_VIN_MIN_2)); + gl518_write_value(client, GL518_REG_VIN3_LIMIT, + (IN_TO_REG(GL518_INIT_VIN_MAX_3) << 8) | + IN_TO_REG(GL518_INIT_VIN_MIN_3)); + gl518_write_value(client, GL518_REG_VDD_LIMIT, + (VDD_TO_REG(GL518_INIT_VDD_MAX) << 8) | + VDD_TO_REG(GL518_INIT_VDD_MIN)); + + /* Clear status register (bit 5=1), start (bit6=1) */ + gl518_write_value(client, GL518_REG_CONF, 0x24); + gl518_write_value(client, GL518_REG_CONF, 0x44); +} + +int gl518_detach_client(struct i2c_client *client) +{ + int err, i; + struct gl518_data *data = client->data; + + i2c_deregister_entry(((struct gl518_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("gl518sm.o: Client deregistration failed, client not detached.\n"); + return err; + } + + for (i = 0; i < MAX_GL518_NR; i++) + if (client == gl518_list[i]) + break; + if ((i == MAX_GL518_NR)) { + printk("gl518sm.o: Client to detach not found.\n"); + return -ENOENT; + } + gl518_list[i] = NULL; + + if (data->thread) { + data->quit_thread = 1; + wake_up_interruptible(&data->wq); + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int gl518_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void gl518_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void gl518_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +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. */ +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. */ +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); +} + +void gl518_update_client(struct i2c_client *client) +{ + struct gl518_data *data = client->data; + int val; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting gl518 update\n"); +#endif + + data->alarms = gl518_read_value(client, GL518_REG_INT); + data->beeps = 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[0] = (val >> 8) & 0xff; + data->fan[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 = gl518_read_value(client, GL518_REG_TEMP); + data->temp_over = + gl518_read_value(client, GL518_REG_TEMP_OVER); + 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->alarms &= data->alarm_mask; + + val = gl518_read_value(client, GL518_REG_CONF); + data->beep_enable = (val >> 2) & 1; + +#ifndef DEBUG_VIN + if (data->type != gl518sm_r00) { + data->voltage[0] = + gl518_read_value(client, GL518_REG_VDD); + data->voltage[1] = + gl518_read_value(client, GL518_REG_VIN1); + data->voltage[2] = + gl518_read_value(client, GL518_REG_VIN2); + data->voltage[3] = + gl518_read_value(client, GL518_REG_VIN3); + } else + gl518_update_client_rev00(client); +#else + gl518_update_client_rev00(client); +#endif + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + +/* Here we decide how to run the iteration code. + When called, we trigger the iteration and report the last + measured voltage. No delay for user apps */ +void gl518_update_client_rev00(struct i2c_client *client) +{ + struct gl518_data *data = client->data; + int i; + + if (data->iterate == 1) { /* 10 sec delay */ + /* as that update is slow, we consider the data valid for 30 seconds */ + if ( + ((jiffies - data->last_updated_v00 > 30 * HZ) + || (data->alarms & 7) + || (!data->valid)) && (!data->iterate_lock)) { + data->iterate_lock = 1; + gl518_update_iterate(client); + data->iterate_lock = 0; + } + for (i = 0; i < 4; i++) + data->voltage[i] = data->iter_voltage[i]; + } else if (data->iterate == 2) { /* show results of last iteration */ + for (i = 0; i < 4; i++) + data->voltage[i] = data->iter_voltage[i]; + wake_up_interruptible(&data->wq); + } else { /* no iteration */ + data->voltage[3] = + gl518_read_value(client, GL518_REG_VIN3); + } +} + +int gl518_update_thread(void *c) +{ + struct i2c_client *client = c; + struct gl518_data *data = client->data; + +#ifdef __SMP__ + lock_kernel(); +#endif + exit_mm(current); + current->session = 1; + current->pgrp = 1; + sigfillset(¤t->blocked); + current->fs->umask = 0; + strcpy(current->comm, "gl518sm"); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) + init_waitqueue_head(&(data->wq)); +#else + data->wq = NULL; +#endif + data->thread = current; + +#ifdef __SMP__ + unlock_kernel(); +#endif + + for (;;) { + if (!data->iterate_lock) { + data->iterate_lock = 1; + gl518_update_iterate(client); + data->iterate_lock = 0; + } + + if ((data->quit_thread) || signal_pending(current)) + break; + interruptible_sleep_on(&data->wq); + } + + data->thread = NULL; + data->quit_thread = 0; + return 0; +} + +/* This updates vdd, vin1, vin2 values by doing slow and multiple + comparisons for the GL518SM rev 00 that lacks support for direct + reading of these values. Values are kept in iter_voltage */ + +void gl518_update_iterate(struct i2c_client *client) +{ + struct gl518_data *data = client->data; + int i, j, loop_more = 1, min[3], max[3], delta[3]; + int alarm, beeps, irqs; + +#define VIN_REG(c) c==0?GL518_REG_VDD_LIMIT:\ + c==1?GL518_REG_VIN1_LIMIT:\ + GL518_REG_VIN2_LIMIT + + /* disable beeps & irqs for vin0-2 */ + beeps = gl518_read_value(client, GL518_REG_ALARM); + irqs = gl518_read_value(client, GL518_REG_MASK); + gl518_write_value(client, GL518_REG_ALARM, beeps & ~0x7); + gl518_write_value(client, GL518_REG_MASK, irqs & ~0x7); + + alarm = data->alarms; + + for (i = 0; i < 3; i++) { + if (alarm & (1 << i)) { + min[i] = 0; + max[i] = 127; + } else { + min[i] = data->voltage_min[i]; + max[i] = + (data->voltage_max[i] + + data->voltage_min[i]) / 2; + } + delta[i] = (max[i] - min[i]) / 2; + } + + for (j = 0; (j < 10 && loop_more); j++) { + + for (i = 0; i < 3; i++) + gl518_write_value(client, VIN_REG(i), + max[i] << 8 | min[i]); + + if ((data->thread) && + ((data->quit_thread) || signal_pending(current))) + goto finish; + + /* we wait now 1.5 seconds before comparing */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ + HZ / 2); + + alarm = gl518_read_value(client, GL518_REG_INT); + +#ifdef DEBUG_VIN + printk("gl518sm: iteration %2d: %4d%c %4d%c %4d%c\n", j, + max[0], (alarm & 1) ? '!' : ' ', + max[1], (alarm & 2) ? '!' : ' ', + max[2], (alarm & 4) ? '!' : ' '); +#endif + + for (loop_more = 0, i = 0; i < 3; i++) { + if (alarm & (1 << i)) + max[i] += delta[i]; + else + max[i] -= delta[i]; + + if (delta[i]) + loop_more++; + delta[i] >>= 1; + } + + } + + for (i = 0; i < 3; i++) + if (alarm & (1 << i)) + max[i]++; + +#ifdef DEBUG_VIN + printk("gl518sm: final :%5d %5d %5d\n", max[0], max[1], + max[2]); + printk("gl518sm: meter :%5d %5d %5d\n", data->voltage[0], + data->voltage[1], data->voltage[2]); +#endif + + /* update values, including vin3 */ + for (i = 0; i < 3; i++) { + data->iter_voltage[i] = max[i]; + } + data->iter_voltage[3] = gl518_read_value(client, GL518_REG_VIN3); + data->last_updated_v00 = jiffies; + + finish: + + /* reset values */ + for (i = 0; i < 3; i++) { + gl518_write_value(client, VIN_REG(i), + data->voltage_max[i] << 8 | data-> + voltage_min[i]); + } + + gl518_write_value(client, GL518_REG_ALARM, beeps); + gl518_write_value(client, GL518_REG_MASK, irqs); + +#undef VIN_REG +} + +void gl518_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + gl518_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over = TEMP_TO_REG(results[0]); + gl518_write_value(client, GL518_REG_TEMP_OVER, + data->temp_over); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + gl518_write_value(client, GL518_REG_TEMP_HYST, + data->temp_hyst); + } + } +} + +void gl518_vin(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + int nr = ctl_name - GL518_SYSCTL_VDD; + int regnr, old = 0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + gl518_update_client(client); + results[0] = nr ? IN_FROM_REG(data->voltage_min[nr]) : + VDD_FROM_REG(data->voltage_min[nr]); + results[1] = nr ? IN_FROM_REG(data->voltage_max[nr]) : + VDD_FROM_REG(data->voltage_max[nr]); + results[2] = nr ? IN_FROM_REG(data->voltage[nr]) : + VDD_FROM_REG(data->voltage[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + regnr = + nr == 0 ? GL518_REG_VDD_LIMIT : nr == + 1 ? GL518_REG_VIN1_LIMIT : nr == + 2 ? GL518_REG_VIN2_LIMIT : GL518_REG_VIN3_LIMIT; + if (*nrels_mag == 1) + old = gl518_read_value(client, regnr) & 0xff00; + if (*nrels_mag >= 2) { + data->voltage_max[nr] = + nr ? IN_TO_REG(results[1]) : + VDD_TO_REG(results[1]); + old = data->voltage_max[nr] << 8; + } + if (*nrels_mag >= 1) { + data->voltage_min[nr] = + nr ? IN_TO_REG(results[0]) : + VDD_TO_REG(results[0]); + old |= data->voltage_min[nr]; + gl518_write_value(client, regnr, old); + } + } +} + + +void gl518_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + int nr = ctl_name - GL518_SYSCTL_FAN1; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl518_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + results[1] = + FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr] = FAN_TO_REG(results[0], + DIV_FROM_REG(data-> + fan_div + [nr])); + old = + gl518_read_value(client, GL518_REG_FAN_LIMIT); + + if (nr == 0) { + old = + (old & 0x00ff) | (data-> + fan_min[0] << 8); + if (results[0] == 0) + data->alarm_mask &= ~0x20; + else + data->alarm_mask |= 0x20; + } else { + old = (old & 0xff00) | data->fan_min[1]; + if (results[0] == 0) + data->alarm_mask &= ~0x40; + else + data->alarm_mask |= 0x40; + } + gl518_write_value(client, GL518_REG_FAN_LIMIT, + old); + } + } +} + + +void gl518_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl518_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void gl518_beep(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl518_update_client(client); + results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable); + results[1] = BEEPS_FROM_REG(data->beeps); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->beep_enable = BEEP_ENABLE_TO_REG(results[0]); + gl518_write_value(client, GL518_REG_CONF, + (gl518_read_value(client, + GL518_REG_CONF) + & 0xfb) | (data-> + beep_enable << 2)); + } + if (*nrels_mag >= 2) { + data->beeps = + BEEPS_TO_REG(results[1]) & data->alarm_mask; + gl518_write_value(client, GL518_REG_ALARM, + data->beeps); + } + } +} + + +void gl518_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + int old; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl518_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = gl518_read_value(client, GL518_REG_MISC); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0xcf) | (data->fan_div[1] << 4); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0x3f) | (data->fan_div[0] << 6); + } + gl518_write_value(client, GL518_REG_MISC, old); + } +} + +void gl518_fan1off(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int old; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = + ((gl518_read_value(client, GL518_REG_MISC) & 0x08) != + 0); + results[1] = + ((gl518_read_value(client, GL518_REG_CONF) & 0x10) != + 0); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + old = + gl518_read_value(client, + GL518_REG_MISC) & 0xf7; + if (results[0]) + old |= 0x08; + gl518_write_value(client, GL518_REG_MISC, old); + } + if (*nrels_mag >= 2) { + old = + gl518_read_value(client, + GL518_REG_CONF) & 0xef; + if (results[1]) + old |= 0x10; + gl518_write_value(client, GL518_REG_CONF, old); + } + } +} + +void gl518_iterate(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl518_data *data = client->data; + int i; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = data->iterate; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if ((*nrels_mag >= 1) && (data->iterate != results[0])) { + data->iterate = results[0]; + for (i = 0; i < 4; i++) { + data->voltage[i] = 0; + data->iter_voltage[i] = 0; + } + data->valid = 0; + + if ((data->iterate != 2) && (data->thread)) { + data->quit_thread = 1; + wake_up_interruptible(&data->wq); + } else if ((data->iterate == 2) && (!data->thread)) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,1) + init_waitqueue_head(&(data->wq)); +#else + data->wq = NULL; +#endif + kernel_thread(gl518_update_thread, + (void *) client, 0); + } + } + } +} + +int __init sensors_gl518sm_init(void) +{ + int res; + + printk("gl518sm.o version %s (%s)\n", LM_VERSION, LM_DATE); + gl518_initialized = 0; + if ((res = i2c_add_driver(&gl518_driver))) { + printk + ("gl518sm.o: Driver registration failed, module not inserted.\n"); + gl518_cleanup(); + return res; + } + gl518_initialized++; + return 0; +} + +int __init gl518_cleanup(void) +{ + int res; + + if (gl518_initialized >= 1) { + if ((res = i2c_del_driver(&gl518_driver))) { + printk + ("gl518.o: Driver deregistration failed, module not removed.\n"); + return res; + } + gl518_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Kyösti Mälkki "); +MODULE_DESCRIPTION("GL518SM driver"); + +int init_module(void) +{ + return sensors_gl518sm_init(); +} + +int cleanup_module(void) +{ + return gl518_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/gl520sm.c Mon Mar 25 15:28:39 CET 2002 +++ linux/drivers/sensors/gl520sm.c Mon Mar 25 15:28:39 CET 2002 @@ -0,0 +1,928 @@ +/* + gl520sm.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard , + 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 +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2c, 0x2d, SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(gl520sm); + +/* Many GL520 constants specified below +One of the inputs can be configured as either temp or voltage. +That's why _TEMP2 and _VIN4 access the same register +*/ + +/* The GL520 registers */ +#define GL520_REG_CHIP_ID 0x00 +#define GL520_REG_REVISION 0x01 +#define GL520_REG_VID 0x02 +#define GL520_REG_CONF 0x03 +#define GL520_REG_TEMP1 0x04 +#define GL520_REG_TEMP1_OVER 0x05 +#define GL520_REG_TEMP1_HYST 0x06 +#define GL520_REG_FAN_COUNT 0x07 +#define GL520_REG_FAN_LIMIT 0x08 +#define GL520_REG_VIN1_LIMIT 0x09 +#define GL520_REG_VIN2_LIMIT 0x0a +#define GL520_REG_VIN3_LIMIT 0x0b +#define GL520_REG_VDD_LIMIT 0x0c +#define GL520_REG_VIN3 0x0d +#define GL520_REG_VIN4 0x0e +#define GL520_REG_TEMP2 0x0e +#define GL520_REG_MISC 0x0f +#define GL520_REG_ALARM 0x10 +#define GL520_REG_MASK 0x11 +#define GL520_REG_INT 0x12 +#define GL520_REG_VIN2 0x13 +#define GL520_REG_VIN1 0x14 +#define GL520_REG_VDD 0x15 +#define GL520_REG_TEMP2_OVER 0x17 +#define GL520_REG_VIN4_MAX 0x17 +#define GL520_REG_TEMP2_HYST 0x18 +#define GL520_REG_VIN4_MIN 0x18 + + +/* 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 TEMP_TO_REG(val) (SENSORS_LIMIT(((((val)<0?(val)-5:(val)+5) / 10)+130),\ + 0,255)) +#define TEMP_FROM_REG(val) (((val) - 130) * 10) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((960000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) \ + ( (val)==0 ? 0 : (val)==255 ? 0 : (960000/((val)*(div))) ) + +#define IN_TO_REG(val) (SENSORS_LIMIT((((val)*10+8)/19),0,255)) +#define IN_FROM_REG(val) (((val)*19)/10) + +#define VDD_TO_REG(val) (SENSORS_LIMIT((((val)*10+11)/23),0,255)) +#define VDD_FROM_REG(val) (((val)*23)/10) + +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) +#define DIV_FROM_REG(val) (1 << (val)) + +#define ALARMS_FROM_REG(val) val + +#define BEEP_ENABLE_TO_REG(val) ((val)?0:1) +#define BEEP_ENABLE_FROM_REG(val) ((val)?0:1) + +#define BEEPS_TO_REG(val) (val) +#define BEEPS_FROM_REG(val) (val) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) + +/* Initial values */ +#define GL520_INIT_TEMP_OVER 600 +#define GL520_INIT_TEMP_HYST 500 +#define GL520_INIT_FAN_MIN_1 3000 +#define GL520_INIT_FAN_MIN_2 3000 + +/* These are somewhat sane */ +#define GL520_INIT_VIN_1 330 /* 3.3 V */ +#define GL520_INIT_VIN_2 286 /* 12 V */ +#define GL520_INIT_VIN_3 260 /* Vcore */ +#define GL520_INIT_VIN_4 160 /* -12 V */ +#define GL520_INIT_VDD 500 /* 5 V */ + +#define GL520_INIT_PERCENTAGE 10 + +#define GL520_INIT_VIN_MIN_1 \ + (GL520_INIT_VIN_1 - GL520_INIT_VIN_1 * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VIN_MAX_1 \ + (GL520_INIT_VIN_1 + GL520_INIT_VIN_1 * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VIN_MIN_2 \ + (GL520_INIT_VIN_2 - GL520_INIT_VIN_2 * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VIN_MAX_2 \ + (GL520_INIT_VIN_2 + GL520_INIT_VIN_2 * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VIN_MIN_3 \ + (GL520_INIT_VIN_3 - GL520_INIT_VIN_3 * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VIN_MAX_3 \ + (GL520_INIT_VIN_3 + GL520_INIT_VIN_3 * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VDD_MIN \ + (GL520_INIT_VDD - GL520_INIT_VDD * GL520_INIT_PERCENTAGE / 100) +#define GL520_INIT_VDD_MAX \ + (GL520_INIT_VDD + GL520_INIT_VDD * GL520_INIT_PERCENTAGE / 100) + + +/* Each client has this additional data */ +struct gl520_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + unsigned long last_updated_v00; + /* In jiffies (used only by rev00 chips) */ + + u8 voltage[5]; /* Register values; [0] = VDD */ + u8 voltage_min[5]; /* Register values; [0] = VDD */ + u8 voltage_max[5]; /* Register values; [0] = VDD */ + u8 fan[2]; + u8 fan_min[2]; + u8 temp[2]; /* Register values */ + u8 temp_over[2]; /* Register values */ + u8 temp_hyst[2]; /* Register values */ + u8 alarms, beeps, vid; /* Register value */ + u8 alarm_mask; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u8 beep_enable; /* Boolean */ + u8 two_temps; /* Boolean */ +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_gl520_init(void); +static int __init gl520_cleanup(void); +static int gl520_attach_adapter(struct i2c_adapter *adapter); +static int gl520_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void gl520_init_client(struct i2c_client *client); +static int gl520_detach_client(struct i2c_client *client); +static int gl520_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void gl520_inc_use(struct i2c_client *client); +static void gl520_dec_use(struct i2c_client *client); +static u16 swap_bytes(u16 val); +static int gl520_read_value(struct i2c_client *client, u8 reg); +static int gl520_write_value(struct i2c_client *client, u8 reg, u16 value); +static void gl520_update_client(struct i2c_client *client); + +static void gl520_vin(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_beep(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_fan1off(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void gl520_config(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* This is the driver that will be inserted */ +static struct i2c_driver gl520_driver = { + /* name */ "GL520SM sensor chip driver", + /* id */ I2C_DRIVERID_GL520, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &gl520_attach_adapter, + /* detach_client */ &gl520_detach_client, + /* command */ &gl520_command, + /* inc_use */ &gl520_inc_use, + /* dec_use */ &gl520_dec_use +}; + +/* These files are created for each detected GL520. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table gl520_dir_table_template[] = { + {GL520_SYSCTL_VIN1, "vin1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_vin}, + {GL520_SYSCTL_VIN2, "vin2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_vin}, + {GL520_SYSCTL_VIN3, "vin3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_vin}, + {GL520_SYSCTL_VIN4, "vin4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_vin}, + {GL520_SYSCTL_VDD, "vdd", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_vin}, + {GL520_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_vid}, + {GL520_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_fan}, + {GL520_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_fan}, + {GL520_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_temp}, + {GL520_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_temp}, + {GL520_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_fan_div}, + {GL520_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_alarms}, + {GL520_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_beep}, + {GL520_SYSCTL_FAN1OFF, "fan1off", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_fan1off}, + {GL520_SYSCTL_CONFIG, "config", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &gl520_config}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata gl520_initialized = 0; + +/* I choose here for semi-static GL520SM allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +static int gl520_id = 0; + +int gl520_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, gl520_detect); +} + +static int gl520_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct gl520_data *data; + int err = 0; + const char *type_name = ""; + char client_name[32]; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("gl520sm.o: gl520_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access gl520_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct gl520_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct gl520_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &gl520_driver; + new_client->flags = 0; + + /* Determine the chip type. */ + + if (gl520_read_value(new_client, GL520_REG_CHIP_ID) != 0x20) { + printk + ("gl520sm.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } else { + kind = gl520sm; + } + + i = gl520_read_value(new_client, GL520_REG_REVISION); + if (kind == gl520sm) { + type_name = "gl520sm"; + sprintf(client_name, "GL520SM Revision %02x chip", i); + } else { +#ifdef DEBUG + printk("gl520sm.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = gl520_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + gl520_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the GL520SM chip */ + data->two_temps = 1; + data->alarm_mask = 0xff; + gl520_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + + +/* Called when we have found a new GL520SM. It should set limits, etc. */ +void gl520_init_client(struct i2c_client *client) +{ + /* Power-on defaults (bit 7=1) */ + gl520_write_value(client, GL520_REG_CONF, 0x80); + + /* No noisy output (bit 2=1), Comparator mode (bit 3=0), two fans (bit4=0), + standby mode (bit6=0) */ + gl520_write_value(client, GL520_REG_CONF, 0x04); + + /* Never interrupts */ + gl520_write_value(client, GL520_REG_MASK, 0x00); + + gl520_write_value(client, GL520_REG_TEMP1_HYST, + TEMP_TO_REG(GL520_INIT_TEMP_HYST)); + gl520_write_value(client, GL520_REG_TEMP1_OVER, + TEMP_TO_REG(GL520_INIT_TEMP_OVER)); + + /* We set Temp2, but not Vin4. */ + gl520_write_value(client, GL520_REG_TEMP2_HYST, + TEMP_TO_REG(GL520_INIT_TEMP_HYST)); + gl520_write_value(client, GL520_REG_TEMP2_OVER, + TEMP_TO_REG(GL520_INIT_TEMP_OVER)); + + gl520_write_value(client, GL520_REG_MISC, (DIV_TO_REG(2) << 6) | + (DIV_TO_REG(2) << 4)); + gl520_write_value(client, GL520_REG_FAN_LIMIT, + (FAN_TO_REG(GL520_INIT_FAN_MIN_1, 2) << 8) | + FAN_TO_REG(GL520_INIT_FAN_MIN_2, 2)); + + gl520_write_value(client, GL520_REG_VIN1_LIMIT, + (IN_TO_REG(GL520_INIT_VIN_MAX_1) << 8) | + IN_TO_REG(GL520_INIT_VIN_MIN_1)); + gl520_write_value(client, GL520_REG_VIN2_LIMIT, + (IN_TO_REG(GL520_INIT_VIN_MAX_2) << 8) | + IN_TO_REG(GL520_INIT_VIN_MIN_2)); + gl520_write_value(client, GL520_REG_VIN3_LIMIT, + (IN_TO_REG(GL520_INIT_VIN_MAX_3) << 8) | + IN_TO_REG(GL520_INIT_VIN_MIN_3)); + gl520_write_value(client, GL520_REG_VDD_LIMIT, + (VDD_TO_REG(GL520_INIT_VDD_MAX) << 8) | + VDD_TO_REG(GL520_INIT_VDD_MIN)); + + /* Clear status register (bit 5=1), start (bit6=1) */ + gl520_write_value(client, GL520_REG_CONF, 0x24); + gl520_write_value(client, GL520_REG_CONF, 0x44); +} + +int gl520_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct gl520_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("gl520sm.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int gl520_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void gl520_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void gl520_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +u16 swap_bytes(u16 val) +{ + return (val >> 8) | (val << 8); +} + +/* Registers 0x07 to 0x0c are word-sized, others are byte-sized + GL520 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int gl520_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 + GL520 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int gl520_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if ((reg >= 0x07) && (reg <= 0x0c)) + return i2c_smbus_write_word_data(client, reg, + swap_bytes(value)); + else + return i2c_smbus_write_byte_data(client, reg, value); +} + +void gl520_update_client(struct i2c_client *client) +{ + struct gl520_data *data = client->data; + int val; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting gl520 update\n"); +#endif + + data->alarms = gl520_read_value(client, GL520_REG_INT); + data->beeps = gl520_read_value(client, GL520_REG_ALARM); + data->vid = gl520_read_value(client, GL520_REG_VID) & 0x1f; + + val = gl520_read_value(client, GL520_REG_VDD_LIMIT); + data->voltage_min[0] = val & 0xff; + data->voltage_max[0] = (val >> 8) & 0xff; + val = gl520_read_value(client, GL520_REG_VIN1_LIMIT); + data->voltage_min[1] = val & 0xff; + data->voltage_max[1] = (val >> 8) & 0xff; + val = gl520_read_value(client, GL520_REG_VIN2_LIMIT); + data->voltage_min[2] = val & 0xff; + data->voltage_max[2] = (val >> 8) & 0xff; + val = gl520_read_value(client, GL520_REG_VIN3_LIMIT); + data->voltage_min[3] = val & 0xff; + data->voltage_max[3] = (val >> 8) & 0xff; + + val = gl520_read_value(client, GL520_REG_FAN_COUNT); + data->fan[0] = (val >> 8) & 0xff; + data->fan[1] = val & 0xff; + + val = gl520_read_value(client, GL520_REG_FAN_LIMIT); + data->fan_min[0] = (val >> 8) & 0xff; + data->fan_min[1] = val & 0xff; + + data->temp[0] = gl520_read_value(client, GL520_REG_TEMP1); + data->temp_over[0] = + gl520_read_value(client, GL520_REG_TEMP1_OVER); + data->temp_hyst[0] = + gl520_read_value(client, GL520_REG_TEMP1_HYST); + + val = gl520_read_value(client, GL520_REG_MISC); + data->fan_div[0] = (val >> 6) & 0x03; + data->fan_div[1] = (val >> 4) & 0x03; + + data->alarms &= data->alarm_mask; + + val = gl520_read_value(client, GL520_REG_CONF); + data->beep_enable = (val >> 2) & 1; + + data->voltage[0] = gl520_read_value(client, GL520_REG_VDD); + data->voltage[1] = + gl520_read_value(client, GL520_REG_VIN1); + data->voltage[2] = + gl520_read_value(client, GL520_REG_VIN2); + data->voltage[3] = + gl520_read_value(client, GL520_REG_VIN3); + + /* Temp1 and Vin4 are the same input */ + data->temp[1] = gl520_read_value(client, GL520_REG_TEMP2); + data->temp_over[1] = + gl520_read_value(client, GL520_REG_TEMP2_OVER); + data->temp_hyst[1] = + gl520_read_value(client, GL520_REG_TEMP2_HYST); + + data->voltage[4] = + gl520_read_value(client, GL520_REG_VIN4); + data->voltage_min[4] = + gl520_read_value(client, GL520_REG_VIN4_MIN); + data->voltage_max[4] = + gl520_read_value(client, GL520_REG_VIN4_MAX); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + +void gl520_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + int nr = ctl_name - GL520_SYSCTL_TEMP1; + int regnr; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over[nr]); + results[1] = TEMP_FROM_REG(data->temp_hyst[nr]); + results[2] = TEMP_FROM_REG(data->temp[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if ((nr == 0) && (!data->two_temps)) + return; + regnr = + nr == 0 ? GL520_REG_TEMP1_OVER : GL520_REG_TEMP2_OVER; + if (*nrels_mag >= 1) { + data->temp_over[nr] = TEMP_TO_REG(results[nr]); + gl520_write_value(client, regnr, + data->temp_over[nr]); + } + regnr = + nr == 0 ? GL520_REG_TEMP1_HYST : GL520_REG_TEMP2_HYST; + if (*nrels_mag >= 2) { + data->temp_hyst[nr] = TEMP_TO_REG(results[nr]); + gl520_write_value(client, regnr, + data->temp_hyst[nr]); + } + } +} + +void gl520_vin(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + int nr = ctl_name - GL520_SYSCTL_VDD; + int regnr, old = 0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = nr ? IN_FROM_REG(data->voltage_min[nr]) : + VDD_FROM_REG(data->voltage_min[nr]); + results[1] = nr ? IN_FROM_REG(data->voltage_max[nr]) : + VDD_FROM_REG(data->voltage_max[nr]); + results[2] = nr ? IN_FROM_REG(data->voltage[nr]) : + VDD_FROM_REG(data->voltage[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (nr != 4) { + regnr = + nr == 0 ? GL520_REG_VDD_LIMIT : nr == + 1 ? GL520_REG_VIN1_LIMIT : nr == + 2 ? GL520_REG_VIN2_LIMIT : + GL520_REG_VIN3_LIMIT; + if (*nrels_mag == 1) + old = + gl520_read_value(client, + regnr) & 0xff00; + if (*nrels_mag >= 2) { + data->voltage_max[nr] = + nr ? IN_TO_REG(results[1]) : + VDD_TO_REG(results[1]); + old = data->voltage_max[nr] << 8; + } + if (*nrels_mag >= 1) { + data->voltage_min[nr] = + nr ? IN_TO_REG(results[0]) : + VDD_TO_REG(results[0]); + old |= data->voltage_min[nr]; + gl520_write_value(client, regnr, old); + } + } else if (!data->two_temps) { + if (*nrels_mag == 1) + gl520_write_value(client, + GL520_REG_VIN4_MIN, + IN_TO_REG(results[0])); + if (*nrels_mag >= 2) + gl520_write_value(client, + GL520_REG_VIN4_MAX, + IN_TO_REG(results[1])); + } + } +} + + +void gl520_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + int nr = ctl_name - GL520_SYSCTL_FAN1; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + results[1] = + FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr] = FAN_TO_REG(results[0], + DIV_FROM_REG(data-> + fan_div + [nr])); + old = + gl520_read_value(client, GL520_REG_FAN_LIMIT); + + if (nr == 0) { + old = + (old & 0x00ff) | (data-> + fan_min[nr] << 8); + if (results[0] == 0) + data->alarm_mask &= ~0x20; + else + data->alarm_mask |= 0x20; + } else { + old = (old & 0xff00) | data->fan_min[nr]; + if (results[0] == 0) + data->alarm_mask &= ~0x40; + else + data->alarm_mask |= 0x40; + } + gl520_write_value(client, GL520_REG_FAN_LIMIT, + old); + } + } +} + + +void gl520_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void gl520_beep(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable); + results[1] = BEEPS_FROM_REG(data->beeps); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->beep_enable = BEEP_ENABLE_TO_REG(results[0]); + gl520_write_value(client, GL520_REG_CONF, + (gl520_read_value(client, + GL520_REG_CONF) + & 0xfb) | (data-> + beep_enable << 2)); + } + if (*nrels_mag >= 2) { + data->beeps = + BEEPS_TO_REG(results[1]) & data->alarm_mask; + gl520_write_value(client, GL520_REG_ALARM, + data->beeps); + } + } +} + + +void gl520_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + int old; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = gl520_read_value(client, GL520_REG_MISC); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0xcf) | (data->fan_div[1] << 4); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0x3f) | (data->fan_div[0] << 6); + } + gl520_write_value(client, GL520_REG_MISC, old); + } +} + +void gl520_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + gl520_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +void gl520_fan1off(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int old; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = + ((gl520_read_value(client, GL520_REG_MISC) & 0x04) != + 0); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + old = + gl520_read_value(client, + GL520_REG_MISC) & 0xfb; + if (results[0]) + old |= 0x04; + gl520_write_value(client, GL520_REG_MISC, old); + } + } +} + +void gl520_config(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct gl520_data *data = client->data; + int old; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = + ((gl520_read_value(client, GL520_REG_CONF) & 0x10) == + 0); + data->two_temps = results[0]; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + old = + gl520_read_value(client, + GL520_REG_CONF) & 0xef; + if (!results[1]) { + old |= 0x10; + data->two_temps = 0; + } else { + data->two_temps = 1; + } + gl520_write_value(client, GL520_REG_CONF, old); + } + } +} + +int __init sensors_gl520_init(void) +{ + int res; + + printk("gl520sm.o version %s (%s)\n", LM_VERSION, LM_DATE); + gl520_initialized = 0; + if ((res = i2c_add_driver(&gl520_driver))) { + printk + ("gl520sm.o: Driver registration failed, module not inserted.\n"); + gl520_cleanup(); + return res; + } + gl520_initialized++; + return 0; +} + +int __init gl520_cleanup(void) +{ + int res; + + if (gl520_initialized >= 1) { + if ((res = i2c_del_driver(&gl520_driver))) { + printk + ("gl520.o: Driver deregistration failed, module not removed.\n"); + return res; + } + gl520_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Kyösti Mälkki "); +MODULE_DESCRIPTION("GL520SM driver"); + +int init_module(void) +{ + return sensors_gl520_init(); +} + +int cleanup_module(void) +{ + return gl520_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/it87.c Mon Mar 25 15:28:39 CET 2002 +++ linux/drivers/sensors/it87.c Mon Mar 25 15:28:39 CET 2002 @@ -0,0 +1,941 @@ +/* + it87.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring. + + Supports: IT8705F Super I/O chip w/LPC interface + IT8712F Super I/O chup w/LPC interface & SMbus + Sis950 A clone of the IT8705F + + Copyright (c) 2001 Chris Gauthron + Largely inspired by lm78.c of the same package + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + djg@pdp8.net David Gesswein 7/18/01 + Modified to fix bug with not all alarms enabled. + Added ability to read battery voltage and select temperature sensor + type at module load time. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_4(it87, it8705, it8712, sis950); + + +/* Update battery voltage after every reading if true */ +static int update_vbat = 0; + + +/* Enable Temp1 as thermal resistor */ +/* Enable Temp2 as thermal diode */ +/* Enable Temp3 as thermal resistor */ +static int temp_type = 0x2a; + +/* Many IT87 constants specified below */ + +/* Length of ISA address segment */ +#define IT87_EXTENT 8 + +/* Where are the ISA address/data registers relative to the base address */ +#define IT87_ADDR_REG_OFFSET 5 +#define IT87_DATA_REG_OFFSET 6 + +/*----- The IT87 registers -----*/ + +#define IT87_REG_CONFIG 0x00 + +#define IT87_REG_ALARM1 0x01 +#define IT87_REG_ALARM2 0x02 +#define IT87_REG_ALARM3 0x03 + +#define IT87_REG_VID 0x0a +#define IT87_REG_FAN_DIV 0x0b + +/* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */ + +#define IT87_REG_FAN(nr) (0x0c + (nr)) +#define IT87_REG_FAN_MIN(nr) (0x0f + (nr)) +#define IT87_REG_FAN_CTRL 0x13 + +#define IT87_REG_VIN(nr) (0x20 + (nr)) +#define IT87_REG_TEMP(nr) (0x28 + (nr)) + +#define IT87_REG_VIN_MAX(nr) (0x30 + (nr) * 2) +#define IT87_REG_VIN_MIN(nr) (0x31 + (nr) * 2) +#define IT87_REG_TEMP_HIGH(nr) (0x3e + (nr) * 2) +#define IT87_REG_TEMP_LOW(nr) (0x3f + (nr) * 2) + +#define IT87_REG_I2C_ADDR 0x48 + +#define IT87_REG_VIN_ENABLE 0x50 +#define IT87_REG_TEMP_ENABLE 0x51 + +#define IT87_REG_CHIPID 0x58 + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) +#define IN_FROM_REG(val) (((val) * 16) / 10) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10),0,255)) +#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) +#define ALARMS_FROM_REG(val) (val) + +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) +#define DIV_FROM_REG(val) (1 << (val)) + +/* Initial limits. Use the config file to set better limits. */ +#define IT87_INIT_IN_0 170 +#define IT87_INIT_IN_1 250 +#define IT87_INIT_IN_2 (330 / 2) +#define IT87_INIT_IN_3 (((500) * 100)/168) +#define IT87_INIT_IN_4 (((1200) * 10)/38) +#define IT87_INIT_IN_5 (((1200) * 10)/72) +#define IT87_INIT_IN_6 (((500) * 10)/56) +#define IT87_INIT_IN_7 (((500) * 100)/168) + +#define IT87_INIT_IN_PERCENTAGE 10 + +#define IT87_INIT_IN_MIN_0 \ + (IT87_INIT_IN_0 - IT87_INIT_IN_0 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_0 \ + (IT87_INIT_IN_0 + IT87_INIT_IN_0 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_1 \ + (IT87_INIT_IN_1 - IT87_INIT_IN_1 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_1 \ + (IT87_INIT_IN_1 + IT87_INIT_IN_1 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_2 \ + (IT87_INIT_IN_2 - IT87_INIT_IN_2 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_2 \ + (IT87_INIT_IN_2 + IT87_INIT_IN_2 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_3 \ + (IT87_INIT_IN_3 - IT87_INIT_IN_3 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_3 \ + (IT87_INIT_IN_3 + IT87_INIT_IN_3 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_4 \ + (IT87_INIT_IN_4 - IT87_INIT_IN_4 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_4 \ + (IT87_INIT_IN_4 + IT87_INIT_IN_4 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_5 \ + (IT87_INIT_IN_5 - IT87_INIT_IN_5 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_5 \ + (IT87_INIT_IN_5 + IT87_INIT_IN_5 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_6 \ + (IT87_INIT_IN_6 - IT87_INIT_IN_6 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_6 \ + (IT87_INIT_IN_6 + IT87_INIT_IN_6 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MIN_7 \ + (IT87_INIT_IN_7 - IT87_INIT_IN_7 * IT87_INIT_IN_PERCENTAGE / 100) +#define IT87_INIT_IN_MAX_7 \ + (IT87_INIT_IN_7 + IT87_INIT_IN_7 * IT87_INIT_IN_PERCENTAGE / 100) + +#define IT87_INIT_FAN_MIN_1 3000 +#define IT87_INIT_FAN_MIN_2 3000 +#define IT87_INIT_FAN_MIN_3 3000 + +#define IT87_INIT_TEMP_HIGH_1 600 +#define IT87_INIT_TEMP_LOW_1 200 +#define IT87_INIT_TEMP_HIGH_2 600 +#define IT87_INIT_TEMP_LOW_2 200 +#define IT87_INIT_TEMP_HIGH_3 600 +#define IT87_INIT_TEMP_LOW_3 200 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered IT87, we need to keep some data in memory. That + data is pointed to by it87_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new it87 client is + allocated. */ +struct it87_data { + struct semaphore lock; + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[9]; /* Register value */ + u8 in_max[9]; /* Register value */ + u8 in_min[9]; /* Register value */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 temp[3]; /* Register value */ + u8 temp_high[3]; /* Register value */ + u8 temp_low[3]; /* Register value */ + u8 fan_div[3]; /* Register encoding, shifted right */ + u8 vid; /* Register encoding, combined */ + u32 alarms; /* Register encoding, combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_it87_init(void); +static int __init it87_cleanup(void); + +static int it87_attach_adapter(struct i2c_adapter *adapter); +static int it87_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int it87_detach_client(struct i2c_client *client); +static int it87_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void it87_inc_use(struct i2c_client *client); +static void it87_dec_use(struct i2c_client *client); + +static int it87_read_value(struct i2c_client *client, u8 register); +static int it87_write_value(struct i2c_client *client, u8 register, + u8 value); +static void it87_update_client(struct i2c_client *client); +static void it87_init_client(struct i2c_client *client); + + +static void it87_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void it87_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void it87_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void it87_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void it87_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void it87_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static struct i2c_driver it87_driver = { + /* name */ "IT87xx sensor driver", + /* id */ I2C_DRIVERID_IT87, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &it87_attach_adapter, + /* detach_client */ &it87_detach_client, + /* command */ &it87_command, + /* inc_use */ &it87_inc_use, + /* dec_use */ &it87_dec_use +}; + +/* Used by it87_init/cleanup */ +static int __initdata it87_initialized = 0; + +static int it87_id = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected IT87. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table it87_dir_table_template[] = { + {IT87_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_in}, + {IT87_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_fan}, + {IT87_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_fan}, + {IT87_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_fan}, + {IT87_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_temp}, + {IT87_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_temp}, + {IT87_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_temp}, + {IT87_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_vid}, + {IT87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_fan_div}, + {IT87_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &it87_alarms}, + {0} +}; + + +/* 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) */ +int it87_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, it87_detect); +} + +/* This function is called by i2c_detect */ +int it87_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct it87_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + + if (!is_isa + && !i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) goto + ERROR0; + + if (is_isa) { + if (check_region(address, IT87_EXTENT)) + goto ERROR0; + } + + /* Probe whether there is anything available on this address. Already + done for SMBus clients */ + if (kind < 0) { + if (is_isa) { + +#define REALLY_SLOW_IO + /* We need the timeouts for at least some IT87-like chips. But only + if we read 'undefined' registers. */ + i = inb_p(address + 1); + if (inb_p(address + 2) != i) + goto ERROR0; + if (inb_p(address + 3) != i) + goto ERROR0; + if (inb_p(address + 7) != i) + goto ERROR0; +#undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f, address + 5); + if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { + outb_p(i, address + 5); + return 0; + } + } + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access it87_{read,write}_value. */ + + if (!(new_client = kmalloc((sizeof(struct i2c_client)) + + sizeof(struct it87_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct it87_data *) (new_client + 1); + if (is_isa) + init_MUTEX(&data->lock); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &it87_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if (it87_read_value(new_client, IT87_REG_CONFIG) & 0x80) + goto ERROR1; + if (!is_isa + && (it87_read_value(new_client, IT87_REG_I2C_ADDR) != + address)) goto ERROR1; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = it87_read_value(new_client, IT87_REG_CHIPID); + if (i == 0x90) { + kind = it87; + } + else { + if (kind == 0) + printk + ("it87.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == it87) { + type_name = "it87"; + client_name = "IT87 chip"; + } /* else if (kind == it8712) { + type_name = "it8712"; + client_name = "IT87-J chip"; + } */ else { +#ifdef DEBUG + printk("it87.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Reserve the ISA region */ + if (is_isa) + request_region(address, IT87_EXTENT, type_name); + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = it87_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + it87_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the IT87 chip */ + it87_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + if (is_isa) + release_region(address, IT87_EXTENT); + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int it87_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct it87_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("it87.o: Client deregistration failed, client not detached.\n"); + return err; + } + + if(i2c_is_isa_client(client)) + release_region(client->addr, IT87_EXTENT); + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int it87_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void it87_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void it87_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* The SMBus locks itself, but ISA access must be locked explicitely! + We don't want to lock the whole ISA bus, so we lock each client + separately. + We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, + would slow down the IT87 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +int it87_read_value(struct i2c_client *client, u8 reg) +{ + int res; + if (i2c_is_isa_client(client)) { + down(&(((struct it87_data *) (client->data))->lock)); + outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); + res = inb_p(client->addr + IT87_DATA_REG_OFFSET); + up(&(((struct it87_data *) (client->data))->lock)); + return res; + } else + return i2c_smbus_read_byte_data(client, reg); +} + +/* The SMBus locks itself, but ISA access muse be locked explicitely! + We don't want to lock the whole ISA bus, so we lock each client + separately. + We ignore the IT87 BUSY flag at this moment - it could lead to deadlocks, + would slow down the IT87 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +int it87_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + if (i2c_is_isa_client(client)) { + down(&(((struct it87_data *) (client->data))->lock)); + outb_p(reg, client->addr + IT87_ADDR_REG_OFFSET); + outb_p(value, client->addr + IT87_DATA_REG_OFFSET); + up(&(((struct it87_data *) (client->data))->lock)); + return 0; + } else + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new IT87. It should set limits, etc. */ +void it87_init_client(struct i2c_client *client) +{ + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others */ + it87_write_value(client, IT87_REG_CONFIG, 0x80); + it87_write_value(client, IT87_REG_VIN_MIN(0), + IN_TO_REG(IT87_INIT_IN_MIN_0)); + it87_write_value(client, IT87_REG_VIN_MAX(0), + IN_TO_REG(IT87_INIT_IN_MAX_0)); + it87_write_value(client, IT87_REG_VIN_MIN(1), + IN_TO_REG(IT87_INIT_IN_MIN_1)); + it87_write_value(client, IT87_REG_VIN_MAX(1), + IN_TO_REG(IT87_INIT_IN_MAX_1)); + it87_write_value(client, IT87_REG_VIN_MIN(2), + IN_TO_REG(IT87_INIT_IN_MIN_2)); + it87_write_value(client, IT87_REG_VIN_MAX(2), + IN_TO_REG(IT87_INIT_IN_MAX_2)); + it87_write_value(client, IT87_REG_VIN_MIN(3), + IN_TO_REG(IT87_INIT_IN_MIN_3)); + it87_write_value(client, IT87_REG_VIN_MAX(3), + IN_TO_REG(IT87_INIT_IN_MAX_3)); + it87_write_value(client, IT87_REG_VIN_MIN(4), + IN_TO_REG(IT87_INIT_IN_MIN_4)); + it87_write_value(client, IT87_REG_VIN_MAX(4), + IN_TO_REG(IT87_INIT_IN_MAX_4)); + it87_write_value(client, IT87_REG_VIN_MIN(5), + IN_TO_REG(IT87_INIT_IN_MIN_5)); + it87_write_value(client, IT87_REG_VIN_MAX(5), + IN_TO_REG(IT87_INIT_IN_MAX_5)); + it87_write_value(client, IT87_REG_VIN_MIN(6), + IN_TO_REG(IT87_INIT_IN_MIN_6)); + it87_write_value(client, IT87_REG_VIN_MAX(6), + IN_TO_REG(IT87_INIT_IN_MAX_6)); + it87_write_value(client, IT87_REG_VIN_MIN(7), + IN_TO_REG(IT87_INIT_IN_MIN_7)); + it87_write_value(client, IT87_REG_VIN_MAX(7), + IN_TO_REG(IT87_INIT_IN_MAX_7)); + /* Note: Battery voltage does not have limit registers */ + it87_write_value(client, IT87_REG_FAN_MIN(1), + FAN_TO_REG(IT87_INIT_FAN_MIN_1, 2)); + it87_write_value(client, IT87_REG_FAN_MIN(2), + FAN_TO_REG(IT87_INIT_FAN_MIN_2, 2)); + it87_write_value(client, IT87_REG_FAN_MIN(3), + FAN_TO_REG(IT87_INIT_FAN_MIN_3, 2)); + it87_write_value(client, IT87_REG_TEMP_HIGH(1), + TEMP_TO_REG(IT87_INIT_TEMP_HIGH_1)); + it87_write_value(client, IT87_REG_TEMP_LOW(1), + TEMP_TO_REG(IT87_INIT_TEMP_LOW_1)); + it87_write_value(client, IT87_REG_TEMP_HIGH(2), + TEMP_TO_REG(IT87_INIT_TEMP_HIGH_2)); + it87_write_value(client, IT87_REG_TEMP_LOW(2), + TEMP_TO_REG(IT87_INIT_TEMP_LOW_2)); + it87_write_value(client, IT87_REG_TEMP_HIGH(3), + TEMP_TO_REG(IT87_INIT_TEMP_HIGH_3)); + it87_write_value(client, IT87_REG_TEMP_LOW(3), + TEMP_TO_REG(IT87_INIT_TEMP_LOW_3)); + + /* Enable voltage monitors */ + it87_write_value(client, IT87_REG_VIN_ENABLE, 0xff); + + /* Enable Temp1-Temp3 */ + it87_write_value(client, IT87_REG_TEMP_ENABLE, + (it87_read_value(client, IT87_REG_TEMP_ENABLE) & 0xc0) + | (temp_type & 0x3f)); + + /* Enable fans */ + it87_write_value(client, IT87_REG_FAN_CTRL, + (it87_read_value(client, IT87_REG_FAN_CTRL) & 0x8f) + | 0x70); + + /* Start monitoring */ + it87_write_value(client, IT87_REG_CONFIG, + (it87_read_value(client, IT87_REG_CONFIG) & 0xb7) + | (update_vbat ? 0x41 : 0x01)); +} + +void it87_update_client(struct i2c_client *client) +{ + struct it87_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + + if (update_vbat) { + /* Cleared after each update, so reenable. Value + returned by this read will be previous value */ + it87_write_value(client, IT87_REG_CONFIG, + it87_read_value(client, IT87_REG_CONFIG) | 0x40); + } + for (i = 0; i <= 7; i++) { + data->in[i] = + it87_read_value(client, IT87_REG_VIN(i)); + data->in_min[i] = + it87_read_value(client, IT87_REG_VIN_MIN(i)); + data->in_max[i] = + it87_read_value(client, IT87_REG_VIN_MAX(i)); + } + data->in[8] = + it87_read_value(client, IT87_REG_VIN(8)); + /* Temperature sensor doesn't have limit registers, set + to min and max value */ + data->in_min[8] = 0; + data->in_max[8] = 255; + + for (i = 1; i <= 3; i++) { + data->fan[i - 1] = + it87_read_value(client, IT87_REG_FAN(i)); + data->fan_min[i - 1] = + it87_read_value(client, IT87_REG_FAN_MIN(i)); + } + for (i = 1; i <= 3; i++) { + data->temp[i - 1] = + it87_read_value(client, IT87_REG_TEMP(i)); + data->temp_high[i - 1] = + it87_read_value(client, IT87_REG_TEMP_HIGH(i)); + data->temp_low[i - 1] = + it87_read_value(client, IT87_REG_TEMP_LOW(i)); + } + + /* The 8705 does not have VID capability */ + /*if (data->type == it8712) { + data->vid = it87_read_value(client, IT87_REG_VID); + data->vid &= 0x1f; + } + else */ { + data->vid = 0x1f; + } + + i = it87_read_value(client, IT87_REG_FAN_DIV); + data->fan_div[0] = i & 0x07; + data->fan_div[1] = (i >> 3) & 0x07; + data->fan_div[2] = 1; + + data->alarms = + it87_read_value(client, IT87_REG_ALARM1) | + (it87_read_value(client, IT87_REG_ALARM2) << 8) | + (it87_read_value(client, IT87_REG_ALARM3) << 16); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + - Each function must return the magnitude (power of 10 to divide the + data with) if it is called with operation==SENSORS_PROC_REAL_INFO. + - It must put a maximum of *nrels elements in results reflecting the + data of this file, and set *nrels to the number it actually put + in it, if operation==SENSORS_PROC_REAL_READ. + - Finally, it must get upto *nrels elements from results and write them + to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void it87_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct it87_data *data = client->data; + int nr = ctl_name - IT87_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + it87_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr]); + results[1] = IN_FROM_REG(data->in_max[nr]); + results[2] = IN_FROM_REG(data->in[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0]); + it87_write_value(client, IT87_REG_VIN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1]); + it87_write_value(client, IT87_REG_VIN_MAX(nr), + data->in_max[nr]); + } + } +} + +void it87_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct it87_data *data = client->data; + int nr = ctl_name - IT87_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + it87_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data-> + fan_div[nr - 1])); + results[1] = + FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG + (data-> + fan_div[nr - + 1])); + it87_write_value(client, IT87_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } + } +} + + +void it87_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct it87_data *data = client->data; + int nr = ctl_name - IT87_SYSCTL_TEMP1 + 1; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + it87_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_high[nr - 1]); + results[1] = TEMP_FROM_REG(data->temp_low[nr - 1]); + results[2] = TEMP_FROM_REG(data->temp[nr - 1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_high[nr - 1] = TEMP_TO_REG(results[0]); + it87_write_value(client, IT87_REG_TEMP_HIGH(nr), + data->temp_high[nr - 1]); + } + if (*nrels_mag >= 2) { + data->temp_low[nr - 1] = TEMP_TO_REG(results[1]); + it87_write_value(client, IT87_REG_TEMP_LOW(nr), + data->temp_low[nr - 1]); + } + } +} + +void it87_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct it87_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + it87_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +void it87_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct it87_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + it87_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void it87_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct it87_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + it87_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + results[2] = 2; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = it87_read_value(client, IT87_REG_FAN_DIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0xc3) | (data->fan_div[1] << 3); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xf8) | data->fan_div[0]; + it87_write_value(client, IT87_REG_FAN_DIV, old); + } + } +} + +int __init sensors_it87_init(void) +{ + int res; + + printk("it87.o version %s (%s)\n", LM_VERSION, LM_DATE); + it87_initialized = 0; + + if ((res = i2c_add_driver(&it87_driver))) { + printk + ("it87.o: Driver registration failed, module not inserted.\n"); + it87_cleanup(); + return res; + } + it87_initialized++; + return 0; +} + +int __init it87_cleanup(void) +{ + int res; + + if (it87_initialized >= 1) { + if ((res = i2c_del_driver(&it87_driver))) { + printk + ("it87.o: Driver deregistration failed, module not removed.\n"); + return res; + } + it87_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Chris Gauthron "); +MODULE_DESCRIPTION("IT8705F, IT8712F, Sis950 driver"); +MODULE_PARM(update_vbat, "i"); +MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value"); +MODULE_PARM(temp_type, "i"); +MODULE_PARM_DESC(temp_type, "Temperature sensor type, normally leave unset"); + +int init_module(void) +{ + return sensors_it87_init(); +} + +int cleanup_module(void) +{ + return it87_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/lm75.c Mon Mar 25 15:28:39 CET 2002 +++ linux/drivers/sensors/lm75.c Mon Mar 25 15:28:39 CET 2002 @@ -0,0 +1,439 @@ +/* + lm75.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x48, 0x4f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(lm75); + +/* Many LM75 constants specified below */ + +/* The LM75 registers */ +#define LM75_REG_TEMP 0x00 +#define LM75_REG_CONF 0x01 +#define LM75_REG_TEMP_HYST 0x02 +#define LM75_REG_TEMP_OS 0x03 + +/* 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 TEMP_FROM_REG(val) ((((val & 0x7fff) >> 7) * 5) | ((val & 0x8000)?-256:0)) +#define TEMP_TO_REG(val) (SENSORS_LIMIT((val<0?(0x200+((val)/5))<<7:(((val) + 2) / 5) << 7),0,0xffff)) + +/* Initial values */ +#define LM75_INIT_TEMP_OS 600 +#define LM75_INIT_TEMP_HYST 500 + +/* Each client has this additional data */ +struct lm75_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u16 temp, temp_os, temp_hyst; /* Register values */ +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_lm75_init(void); +static int __init lm75_cleanup(void); +static int lm75_attach_adapter(struct i2c_adapter *adapter); +static int lm75_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void lm75_init_client(struct i2c_client *client); +static int lm75_detach_client(struct i2c_client *client); +static int lm75_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void lm75_inc_use(struct i2c_client *client); +static void lm75_dec_use(struct i2c_client *client); +static u16 swap_bytes(u16 val); +static int lm75_read_value(struct i2c_client *client, u8 reg); +static int lm75_write_value(struct i2c_client *client, u8 reg, u16 value); +static void lm75_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm75_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver lm75_driver = { + /* name */ "LM75 sensor chip driver", + /* id */ I2C_DRIVERID_LM75, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &lm75_attach_adapter, + /* detach_client */ &lm75_detach_client, + /* command */ &lm75_command, + /* inc_use */ &lm75_inc_use, + /* dec_use */ &lm75_dec_use +}; + +/* These files are created for each detected LM75. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table lm75_dir_table_template[] = { + {LM75_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm75_temp}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata lm75_initialized = 0; + +static int lm75_id = 0; + +int lm75_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, lm75_detect); +} + +/* This function is called by i2c_detect */ +int lm75_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, cur, conf, hyst, os; + struct i2c_client *new_client; + struct lm75_data *data; + int err = 0; + const char *type_name, *client_name; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("lm75.o: lm75_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WORD_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access lm75_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct lm75_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct lm75_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &lm75_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is lousy. */ + if (kind < 0) { + cur = i2c_smbus_read_word_data(new_client, 0); + conf = i2c_smbus_read_byte_data(new_client, 1); + hyst = i2c_smbus_read_word_data(new_client, 2); + os = i2c_smbus_read_word_data(new_client, 3); + for (i = 0; i <= 0x1f; i++) + if ( + (i2c_smbus_read_byte_data + (new_client, i * 8 + 1) != conf) + || + (i2c_smbus_read_word_data + (new_client, i * 8 + 2) != hyst) + || + (i2c_smbus_read_word_data + (new_client, i * 8 + 3) != os)) + goto ERROR1; + } + + /* Determine the chip type - only one kind supported! */ + if (kind <= 0) + kind = lm75; + + if (kind == lm75) { + type_name = "lm75"; + client_name = "LM75 chip"; + } else { +#ifdef DEBUG + printk("lm75.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = lm75_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + lm75_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + lm75_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int lm75_detach_client(struct i2c_client *client) +{ + int err; + +#ifdef MODULE + if (MOD_IN_USE) + return -EBUSY; +#endif + + + i2c_deregister_entry(((struct lm75_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("lm75.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int lm75_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void lm75_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void lm75_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +u16 swap_bytes(u16 val) +{ + return (val >> 8) | (val << 8); +} + +/* All registers are word-sized, except for the configuration register. + LM75 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int lm75_read_value(struct i2c_client *client, u8 reg) +{ + if (reg == LM75_REG_CONF) + return i2c_smbus_read_byte_data(client, reg); + else + return swap_bytes(i2c_smbus_read_word_data(client, reg)); +} + +/* All registers are word-sized, except for the configuration register. + LM75 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int lm75_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if (reg == LM75_REG_CONF) + return i2c_smbus_write_byte_data(client, reg, value); + else + return i2c_smbus_write_word_data(client, reg, + swap_bytes(value)); +} + +void lm75_init_client(struct i2c_client *client) +{ + /* Initialize the LM75 chip */ + lm75_write_value(client, LM75_REG_TEMP_OS, + TEMP_TO_REG(LM75_INIT_TEMP_OS)); + lm75_write_value(client, LM75_REG_TEMP_HYST, + TEMP_TO_REG(LM75_INIT_TEMP_HYST)); + lm75_write_value(client, LM75_REG_CONF, 0); +} + +void lm75_update_client(struct i2c_client *client) +{ + struct lm75_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting lm75 update\n"); +#endif + + data->temp = lm75_read_value(client, LM75_REG_TEMP); + data->temp_os = lm75_read_value(client, LM75_REG_TEMP_OS); + data->temp_hyst = + lm75_read_value(client, LM75_REG_TEMP_HYST); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void lm75_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm75_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + lm75_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_os); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_os = TEMP_TO_REG(results[0]); + lm75_write_value(client, LM75_REG_TEMP_OS, + data->temp_os); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + lm75_write_value(client, LM75_REG_TEMP_HYST, + data->temp_hyst); + } + } +} + +int __init sensors_lm75_init(void) +{ + int res; + + printk("lm75.o version %s (%s)\n", LM_VERSION, LM_DATE); + lm75_initialized = 0; + if ((res = i2c_add_driver(&lm75_driver))) { + printk + ("lm75.o: Driver registration failed, module not inserted.\n"); + lm75_cleanup(); + return res; + } + lm75_initialized++; + return 0; +} + +int __init lm75_cleanup(void) +{ + int res; + + if (lm75_initialized >= 1) { + if ((res = i2c_del_driver(&lm75_driver))) { + printk + ("lm75.o: Driver deregistration failed, module not removed.\n"); + return res; + } + lm75_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("LM75 driver"); + +int init_module(void) +{ + return sensors_lm75_init(); +} + +int cleanup_module(void) +{ + return lm75_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/lm78.c Mon Mar 25 15:28:39 CET 2002 +++ linux/drivers/sensors/lm78.c Mon Mar 25 15:28:39 CET 2002 @@ -0,0 +1,873 @@ +/* + lm78.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_3(lm78, lm78j, lm79); + +/* Many LM78 constants specified below */ + +/* Length of ISA address segment */ +#define LM78_EXTENT 8 + +/* Where are the ISA address/data registers relative to the base address */ +#define LM78_ADDR_REG_OFFSET 5 +#define LM78_DATA_REG_OFFSET 6 + +/* The LM78 registers */ +#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define LM78_REG_IN(nr) (0x20 + (nr)) + +#define LM78_REG_FAN_MIN(nr) (0x3a + (nr)) +#define LM78_REG_FAN(nr) (0x27 + (nr)) + +#define LM78_REG_TEMP 0x27 +#define LM78_REG_TEMP_OVER 0x39 +#define LM78_REG_TEMP_HYST 0x3a + +#define LM78_REG_ALARM1 0x41 +#define LM78_REG_ALARM2 0x42 + +#define LM78_REG_VID_FANDIV 0x47 + +#define LM78_REG_CONFIG 0x40 +#define LM78_REG_CHIPID 0x49 +#define LM78_REG_I2C_ADDR 0x48 + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) +#define IN_FROM_REG(val) (((val) * 16) / 10) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10),0,255)) +#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) +#define ALARMS_FROM_REG(val) (val) + +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) +#define DIV_FROM_REG(val) (1 << (val)) + +/* Initial limits. To keep them sane, we use the 'standard' translation as + specified in the LM78 sheet. Use the config file to set better limits. */ +#define LM78_INIT_IN_0 (vid==350?280:vid) +#define LM78_INIT_IN_1 (vid==350?280:vid) +#define LM78_INIT_IN_2 330 +#define LM78_INIT_IN_3 (((500) * 100)/168) +#define LM78_INIT_IN_4 (((1200) * 10)/38) +#define LM78_INIT_IN_5 (((-1200) * -604)/2100) +#define LM78_INIT_IN_6 (((-500) * -604)/909) + +#define LM78_INIT_IN_PERCENTAGE 10 + +#define LM78_INIT_IN_MIN_0 \ + (LM78_INIT_IN_0 - LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_0 \ + (LM78_INIT_IN_0 + LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MIN_1 \ + (LM78_INIT_IN_1 - LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_1 \ + (LM78_INIT_IN_1 + LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MIN_2 \ + (LM78_INIT_IN_2 - LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_2 \ + (LM78_INIT_IN_2 + LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MIN_3 \ + (LM78_INIT_IN_3 - LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_3 \ + (LM78_INIT_IN_3 + LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MIN_4 \ + (LM78_INIT_IN_4 - LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_4 \ + (LM78_INIT_IN_4 + LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MIN_5 \ + (LM78_INIT_IN_5 - LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_5 \ + (LM78_INIT_IN_5 + LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MIN_6 \ + (LM78_INIT_IN_6 - LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100) +#define LM78_INIT_IN_MAX_6 \ + (LM78_INIT_IN_6 + LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100) + +#define LM78_INIT_FAN_MIN_1 3000 +#define LM78_INIT_FAN_MIN_2 3000 +#define LM78_INIT_FAN_MIN_3 3000 + +#define LM78_INIT_TEMP_OVER 600 +#define LM78_INIT_TEMP_HYST 500 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* There are some complications in a module like this. First off, LM78 chips + may be both present on the SMBus and the ISA bus, and we have to handle + those cases separately at some places. Second, there might be several + LM78 chips available (well, actually, that is probably never done; but + it is a clean illustration of how to handle a case like that). Finally, + a specific chip may be attached to *both* ISA and SMBus, and we would + not like to detect it double. Fortunately, in the case of the LM78 at + least, a register tells us what SMBus address we are on, so that helps + a bit - except if there could be more than one SMBus. Groan. No solution + for this yet. */ + +/* This module may seem overly long and complicated. In fact, it is not so + bad. Quite a lot of bookkeeping is done. A real driver can often cut + some corners. */ + +/* For each registered LM78, we need to keep some data in memory. That + data is pointed to by lm78_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new lm78 client is + allocated. */ +struct lm78_data { + struct semaphore lock; + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[7]; /* Register value */ + u8 in_max[7]; /* Register value */ + u8 in_min[7]; /* Register value */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 temp; /* Register value */ + u8 temp_over; /* Register value */ + u8 temp_hyst; /* Register value */ + u8 fan_div[3]; /* Register encoding, shifted right */ + u8 vid; /* Register encoding, combined */ + u16 alarms; /* Register encoding, combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_lm78_init(void); +static int __init lm78_cleanup(void); + +static int lm78_attach_adapter(struct i2c_adapter *adapter); +static int lm78_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int lm78_detach_client(struct i2c_client *client); +static int lm78_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void lm78_inc_use(struct i2c_client *client); +static void lm78_dec_use(struct i2c_client *client); + +static int lm78_read_value(struct i2c_client *client, u8 register); +static int lm78_write_value(struct i2c_client *client, u8 register, + u8 value); +static void lm78_update_client(struct i2c_client *client); +static void lm78_init_client(struct i2c_client *client); + + +static void lm78_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void lm78_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm78_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm78_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm78_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm78_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static struct i2c_driver lm78_driver = { + /* name */ "LM78(-J) and LM79 sensor driver", + /* id */ I2C_DRIVERID_LM78, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &lm78_attach_adapter, + /* detach_client */ &lm78_detach_client, + /* command */ &lm78_command, + /* inc_use */ &lm78_inc_use, + /* dec_use */ &lm78_dec_use +}; + +/* Used by lm78_init/cleanup */ +static int __initdata lm78_initialized = 0; + +static int lm78_id = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected LM78. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table lm78_dir_table_template[] = { + {LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_in}, + {LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_fan}, + {LM78_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_fan}, + {LM78_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_fan}, + {LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_temp}, + {LM78_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_vid}, + {LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_fan_div}, + {LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm78_alarms}, + {0} +}; + + +/* This function is called when: + * lm78_driver is inserted (when this module is loaded), for each + available adapter + * when a new adapter is inserted (and lm78_driver is still present) */ +int lm78_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, lm78_detect); +} + +/* This function is called by i2c_detect */ +int lm78_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct lm78_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + + if (!is_isa + && !i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) goto + ERROR0; + + if (is_isa) { + if (check_region(address, LM78_EXTENT)) + goto ERROR0; + } + + /* Probe whether there is anything available on this address. Already + done for SMBus clients */ + if (kind < 0) { + if (is_isa) { + +#define REALLY_SLOW_IO + /* We need the timeouts for at least some LM78-like chips. But only + if we read 'undefined' registers. */ + i = inb_p(address + 1); + if (inb_p(address + 2) != i) + goto ERROR0; + if (inb_p(address + 3) != i) + goto ERROR0; + if (inb_p(address + 7) != i) + goto ERROR0; +#undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f, address + 5); + if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { + outb_p(i, address + 5); + return 0; + } + } + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access lm78_{read,write}_value. */ + + if (!(new_client = kmalloc((sizeof(struct i2c_client)) + + sizeof(struct lm78_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct lm78_data *) (new_client + 1); + if (is_isa) + init_MUTEX(&data->lock); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &lm78_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if (lm78_read_value(new_client, LM78_REG_CONFIG) & 0x80) + goto ERROR1; + if (!is_isa + && (lm78_read_value(new_client, LM78_REG_I2C_ADDR) != + address)) goto ERROR1; + } + + /* Determine the chip type. */ + if (kind <= 0) { + i = lm78_read_value(new_client, LM78_REG_CHIPID); + if (i == 0x00 || i == 0x20) + kind = lm78; + else if (i == 0x40) + kind = lm78j; + else if ((i & 0xfe) == 0xc0) + kind = lm79; + else { + if (kind == 0) + printk + ("lm78.o: Ignoring 'force' parameter for unknown chip at " + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == lm78) { + type_name = "lm78"; + client_name = "LM78 chip"; + } else if (kind == lm78j) { + type_name = "lm78-j"; + client_name = "LM78-J chip"; + } else if (kind == lm79) { + type_name = "lm79"; + client_name = "LM79 chip"; + } else { +#ifdef DEBUG + printk("lm78.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Reserve the ISA region */ + if (is_isa) + request_region(address, LM78_EXTENT, type_name); + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = lm78_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + lm78_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the LM78 chip */ + lm78_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + if (is_isa) + release_region(address, LM78_EXTENT); + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int lm78_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct lm78_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("lm78.o: Client deregistration failed, client not detached.\n"); + return err; + } + + if(i2c_is_isa_client(client)) + release_region(client->addr, LM78_EXTENT); + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int lm78_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void lm78_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void lm78_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* The SMBus locks itself, but ISA access must be locked explicitely! + We don't want to lock the whole ISA bus, so we lock each client + separately. + We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, + would slow down the LM78 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +int lm78_read_value(struct i2c_client *client, u8 reg) +{ + int res; + if (i2c_is_isa_client(client)) { + down(&(((struct lm78_data *) (client->data))->lock)); + outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); + res = inb_p(client->addr + LM78_DATA_REG_OFFSET); + up(&(((struct lm78_data *) (client->data))->lock)); + return res; + } else + return i2c_smbus_read_byte_data(client, reg); +} + +/* The SMBus locks itself, but ISA access muse be locked explicitely! + We don't want to lock the whole ISA bus, so we lock each client + separately. + We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, + would slow down the LM78 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +int lm78_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + if (i2c_is_isa_client(client)) { + down(&(((struct lm78_data *) (client->data))->lock)); + outb_p(reg, client->addr + LM78_ADDR_REG_OFFSET); + outb_p(value, client->addr + LM78_DATA_REG_OFFSET); + up(&(((struct lm78_data *) (client->data))->lock)); + return 0; + } else + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new LM78. It should set limits, etc. */ +void lm78_init_client(struct i2c_client *client) +{ + int vid; + + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others */ + lm78_write_value(client, LM78_REG_CONFIG, 0x80); + + vid = lm78_read_value(client, LM78_REG_VID_FANDIV) & 0x0f; + if (((struct lm78_data *) (client->data))->type == lm79) + vid |= + (lm78_read_value(client, LM78_REG_CHIPID) & 0x01) << 4; + else + vid |= 0x10; + vid = VID_FROM_REG(vid); + + lm78_write_value(client, LM78_REG_IN_MIN(0), + IN_TO_REG(LM78_INIT_IN_MIN_0)); + lm78_write_value(client, LM78_REG_IN_MAX(0), + IN_TO_REG(LM78_INIT_IN_MAX_0)); + lm78_write_value(client, LM78_REG_IN_MIN(1), + IN_TO_REG(LM78_INIT_IN_MIN_1)); + lm78_write_value(client, LM78_REG_IN_MAX(1), + IN_TO_REG(LM78_INIT_IN_MAX_1)); + lm78_write_value(client, LM78_REG_IN_MIN(2), + IN_TO_REG(LM78_INIT_IN_MIN_2)); + lm78_write_value(client, LM78_REG_IN_MAX(2), + IN_TO_REG(LM78_INIT_IN_MAX_2)); + lm78_write_value(client, LM78_REG_IN_MIN(3), + IN_TO_REG(LM78_INIT_IN_MIN_3)); + lm78_write_value(client, LM78_REG_IN_MAX(3), + IN_TO_REG(LM78_INIT_IN_MAX_3)); + lm78_write_value(client, LM78_REG_IN_MIN(4), + IN_TO_REG(LM78_INIT_IN_MIN_4)); + lm78_write_value(client, LM78_REG_IN_MAX(4), + IN_TO_REG(LM78_INIT_IN_MAX_4)); + lm78_write_value(client, LM78_REG_IN_MIN(5), + IN_TO_REG(LM78_INIT_IN_MIN_5)); + lm78_write_value(client, LM78_REG_IN_MAX(5), + IN_TO_REG(LM78_INIT_IN_MAX_5)); + lm78_write_value(client, LM78_REG_IN_MIN(6), + IN_TO_REG(LM78_INIT_IN_MIN_6)); + lm78_write_value(client, LM78_REG_IN_MAX(6), + IN_TO_REG(LM78_INIT_IN_MAX_6)); + lm78_write_value(client, LM78_REG_FAN_MIN(1), + FAN_TO_REG(LM78_INIT_FAN_MIN_1, 2)); + lm78_write_value(client, LM78_REG_FAN_MIN(2), + FAN_TO_REG(LM78_INIT_FAN_MIN_2, 2)); + lm78_write_value(client, LM78_REG_FAN_MIN(3), + FAN_TO_REG(LM78_INIT_FAN_MIN_3, 2)); + lm78_write_value(client, LM78_REG_TEMP_OVER, + TEMP_TO_REG(LM78_INIT_TEMP_OVER)); + lm78_write_value(client, LM78_REG_TEMP_HYST, + TEMP_TO_REG(LM78_INIT_TEMP_HYST)); + + /* Start monitoring */ + lm78_write_value(client, LM78_REG_CONFIG, + (lm78_read_value(client, LM78_REG_CONFIG) & 0xf7) + | 0x01); + +} + +void lm78_update_client(struct i2c_client *client) +{ + struct lm78_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting lm78 update\n"); +#endif + for (i = 0; i <= 6; i++) { + data->in[i] = + lm78_read_value(client, LM78_REG_IN(i)); + data->in_min[i] = + lm78_read_value(client, LM78_REG_IN_MIN(i)); + data->in_max[i] = + lm78_read_value(client, LM78_REG_IN_MAX(i)); + } + for (i = 1; i <= 3; i++) { + data->fan[i - 1] = + lm78_read_value(client, LM78_REG_FAN(i)); + data->fan_min[i - 1] = + lm78_read_value(client, LM78_REG_FAN_MIN(i)); + } + data->temp = lm78_read_value(client, LM78_REG_TEMP); + data->temp_over = + lm78_read_value(client, LM78_REG_TEMP_OVER); + data->temp_hyst = + lm78_read_value(client, LM78_REG_TEMP_HYST); + i = lm78_read_value(client, LM78_REG_VID_FANDIV); + data->vid = i & 0x0f; + if (data->type == lm79) + data->vid |= + (lm78_read_value(client, LM78_REG_CHIPID) & + 0x01) << 4; + else + data->vid |= 0x10; + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = lm78_read_value(client, LM78_REG_ALARM1) + + (lm78_read_value(client, LM78_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + + data->fan_div[2] = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void lm78_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm78_data *data = client->data; + int nr = ctl_name - LM78_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr]); + results[1] = IN_FROM_REG(data->in_max[nr]); + results[2] = IN_FROM_REG(data->in[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0]); + lm78_write_value(client, LM78_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1]); + lm78_write_value(client, LM78_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void lm78_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm78_data *data = client->data; + int nr = ctl_name - LM78_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data-> + fan_div[nr - 1])); + results[1] = + FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG + (data-> + fan_div[nr - + 1])); + lm78_write_value(client, LM78_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } + } +} + + +void lm78_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm78_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over = TEMP_TO_REG(results[0]); + lm78_write_value(client, LM78_REG_TEMP_OVER, + data->temp_over); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + lm78_write_value(client, LM78_REG_TEMP_HYST, + data->temp_hyst); + } + } +} + +void lm78_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm78_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +void lm78_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm78_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void lm78_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm78_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm78_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + results[2] = 2; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = lm78_read_value(client, LM78_REG_VID_FANDIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan_div[1] << 6); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div[0] << 4); + lm78_write_value(client, LM78_REG_VID_FANDIV, old); + } + } +} + +int __init sensors_lm78_init(void) +{ + int res; + + printk("lm78.o version %s (%s)\n", LM_VERSION, LM_DATE); + lm78_initialized = 0; + + if ((res = i2c_add_driver(&lm78_driver))) { + printk + ("lm78.o: Driver registration failed, module not inserted.\n"); + lm78_cleanup(); + return res; + } + lm78_initialized++; + return 0; +} + +int __init lm78_cleanup(void) +{ + int res; + + if (lm78_initialized >= 1) { + if ((res = i2c_del_driver(&lm78_driver))) { + printk + ("lm78.o: Driver deregistration failed, module not removed.\n"); + return res; + } + lm78_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("LM78, LM78-J and LM79 driver"); + +int init_module(void) +{ + return sensors_lm78_init(); +} + +int cleanup_module(void) +{ + return lm78_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/lm80.c Mon Mar 25 15:28:39 CET 2002 +++ linux/drivers/sensors/lm80.c Mon Mar 25 15:28:39 CET 2002 @@ -0,0 +1,761 @@ +/* + lm80.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + and Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(lm80); + +/* Many LM80 constants specified below */ + +/* The LM80 registers */ +#define LM80_REG_IN_MAX(nr) (0x2a + (nr) * 2) +#define LM80_REG_IN_MIN(nr) (0x2b + (nr) * 2) +#define LM80_REG_IN(nr) (0x20 + (nr)) + +#define LM80_REG_FAN1_MIN 0x3c +#define LM80_REG_FAN2_MIN 0x3d +#define LM80_REG_FAN1 0x28 +#define LM80_REG_FAN2 0x29 + +#define LM80_REG_TEMP 0x27 +#define LM80_REG_TEMP_HOT_MAX 0x38 +#define LM80_REG_TEMP_HOT_HYST 0x39 +#define LM80_REG_TEMP_OS_MAX 0x3a +#define LM80_REG_TEMP_OS_HYST 0x3b + +#define LM80_REG_CONFIG 0x00 +#define LM80_REG_ALARM1 0x01 +#define LM80_REG_ALARM2 0x02 +#define LM80_REG_MASK1 0x03 +#define LM80_REG_MASK2 0x04 +#define LM80_REG_FANDIV 0x05 +#define LM80_REG_RES 0x06 + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + +#define IN_TO_REG(val,nr) (SENSORS_LIMIT((val),0,255)) +#define IN_FROM_REG(val,nr) (val) + +extern inline unsigned char FAN_TO_REG(unsigned rpm, unsigned div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:\ + (val)==255?0:1350000/((div)*(val))) + +extern inline long TEMP_FROM_REG(u16 temp) +{ + long res; + + temp = temp >> 4; + if (temp < 0x0800) { + res = (625 * (long) temp); + } else { + res = ((long) temp - 0x01000) * 625; + } + return res / 100; +} + +#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*100) + +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-50)/100):\ + ((val)+50)/100), \ + 0,255) + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) + +/* Initial limits */ +#define LM80_INIT_IN_0 190 +#define LM80_INIT_IN_1 190 +#define LM80_INIT_IN_2 190 +#define LM80_INIT_IN_3 190 +#define LM80_INIT_IN_4 190 +#define LM80_INIT_IN_5 190 +#define LM80_INIT_IN_6 190 + +#define LM80_INIT_IN_PERCENTAGE 10 + +#define LM80_INIT_IN_MIN_0 \ + (LM80_INIT_IN_0 - LM80_INIT_IN_0 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_0 \ + (LM80_INIT_IN_0 + LM80_INIT_IN_0 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MIN_1 \ + (LM80_INIT_IN_1 - LM80_INIT_IN_1 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_1 \ + (LM80_INIT_IN_1 + LM80_INIT_IN_1 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MIN_2 \ + (LM80_INIT_IN_2 - LM80_INIT_IN_2 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_2 \ + (LM80_INIT_IN_2 + LM80_INIT_IN_2 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MIN_3 \ + (LM80_INIT_IN_3 - LM80_INIT_IN_3 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_3 \ + (LM80_INIT_IN_3 + LM80_INIT_IN_3 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MIN_4 \ + (LM80_INIT_IN_4 - LM80_INIT_IN_4 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_4 \ + (LM80_INIT_IN_4 + LM80_INIT_IN_4 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MIN_5 \ + (LM80_INIT_IN_5 - LM80_INIT_IN_5 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_5 \ + (LM80_INIT_IN_5 + LM80_INIT_IN_5 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MIN_6 \ + (LM80_INIT_IN_6 - LM80_INIT_IN_6 * LM80_INIT_IN_PERCENTAGE / 100) +#define LM80_INIT_IN_MAX_6 \ + (LM80_INIT_IN_6 + LM80_INIT_IN_6 * LM80_INIT_IN_PERCENTAGE / 100) + +#define LM80_INIT_FAN_MIN_1 3000 +#define LM80_INIT_FAN_MIN_2 3000 + +#define LM80_INIT_TEMP_OS_MAX 600 +#define LM80_INIT_TEMP_OS_HYST 500 +#define LM80_INIT_TEMP_HOT_MAX 700 +#define LM80_INIT_TEMP_HOT_HYST 600 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered LM80, we need to keep some data in memory. That + data is pointed to by lm80_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new lm80 client is + allocated. */ +struct lm80_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[7]; /* Register value */ + u8 in_max[7]; /* Register value */ + u8 in_min[7]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u16 temp; /* Register values, shifted right */ + u8 temp_hot_max; /* Register value */ + u8 temp_hot_hyst; /* Register value */ + u8 temp_os_max; /* Register value */ + u8 temp_os_hyst; /* Register value */ + u16 alarms; /* Register encoding, combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_lm80_init(void); +static int __init lm80_cleanup(void); + +static int lm80_attach_adapter(struct i2c_adapter *adapter); +static int lm80_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int lm80_detach_client(struct i2c_client *client); +static int lm80_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void lm80_inc_use(struct i2c_client *client); +static void lm80_dec_use(struct i2c_client *client); + +static int lm80_read_value(struct i2c_client *client, u8 register); +static int lm80_write_value(struct i2c_client *client, u8 register, + u8 value); +static void lm80_update_client(struct i2c_client *client); +static void lm80_init_client(struct i2c_client *client); + + +static void lm80_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results); +static void lm80_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm80_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm80_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm80_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static int lm80_id = 0; + +static struct i2c_driver lm80_driver = { + /* name */ "LM80 sensor driver", + /* id */ I2C_DRIVERID_LM80, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &lm80_attach_adapter, + /* detach_client */ &lm80_detach_client, + /* command */ &lm80_command, + /* inc_use */ &lm80_inc_use, + /* dec_use */ &lm80_dec_use +}; + +/* Used by lm80_init/cleanup */ +static int __initdata lm80_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected LM80. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table lm80_dir_table_template[] = { + {LM80_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_in}, + {LM80_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_fan}, + {LM80_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_fan}, + {LM80_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_temp}, + {LM80_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_fan_div}, + {LM80_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm80_alarms}, + {0} +}; + +int lm80_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, lm80_detect); +} + +int lm80_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, cur; + struct i2c_client *new_client; + struct lm80_data *data; + int err = 0; + const char *type_name, *client_name; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("lm80.o: lm80_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access lm80_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct lm80_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct lm80_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &lm80_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is lousy. */ + if (lm80_read_value(new_client, LM80_REG_ALARM2) & 0xc0) + goto ERROR1; + for (i = 0x2a; i <= 0x3d; i++) { + cur = i2c_smbus_read_byte_data(new_client, i); + if ((i2c_smbus_read_byte_data(new_client, i + 0x40) != cur) + || (i2c_smbus_read_byte_data(new_client, i + 0x80) != + cur) + || (i2c_smbus_read_byte_data(new_client, i + 0xc0) != + cur)) goto ERROR1; + } + + /* Determine the chip type - only one kind supported! */ + if (kind <= 0) + kind = lm80; + + if (kind == lm80) { + type_name = "lm80"; + client_name = "LM80 chip"; + } else { +#ifdef DEBUG + printk("lm80.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = lm80_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + lm80_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + lm80_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int lm80_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct lm80_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("lm80.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int lm80_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void lm80_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void lm80_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +int lm80_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +int lm80_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new LM80. It should set limits, etc. */ +void lm80_init_client(struct i2c_client *client) +{ + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others. This makes most other + initializations unnecessary */ + lm80_write_value(client, LM80_REG_CONFIG, 0x80); + /* Set 11-bit temperature resolution */ + lm80_write_value(client, LM80_REG_RES, 0x08); + + lm80_write_value(client, LM80_REG_IN_MIN(0), + IN_TO_REG(LM80_INIT_IN_MIN_0, 0)); + lm80_write_value(client, LM80_REG_IN_MAX(0), + IN_TO_REG(LM80_INIT_IN_MAX_0, 0)); + lm80_write_value(client, LM80_REG_IN_MIN(1), + IN_TO_REG(LM80_INIT_IN_MIN_1, 1)); + lm80_write_value(client, LM80_REG_IN_MAX(1), + IN_TO_REG(LM80_INIT_IN_MAX_1, 1)); + lm80_write_value(client, LM80_REG_IN_MIN(2), + IN_TO_REG(LM80_INIT_IN_MIN_2, 2)); + lm80_write_value(client, LM80_REG_IN_MAX(2), + IN_TO_REG(LM80_INIT_IN_MAX_2, 2)); + lm80_write_value(client, LM80_REG_IN_MIN(3), + IN_TO_REG(LM80_INIT_IN_MIN_3, 3)); + lm80_write_value(client, LM80_REG_IN_MAX(3), + IN_TO_REG(LM80_INIT_IN_MAX_3, 3)); + lm80_write_value(client, LM80_REG_IN_MIN(4), + IN_TO_REG(LM80_INIT_IN_MIN_4, 4)); + lm80_write_value(client, LM80_REG_IN_MAX(4), + IN_TO_REG(LM80_INIT_IN_MAX_4, 4)); + lm80_write_value(client, LM80_REG_IN_MIN(5), + IN_TO_REG(LM80_INIT_IN_MIN_5, 5)); + lm80_write_value(client, LM80_REG_IN_MAX(5), + IN_TO_REG(LM80_INIT_IN_MAX_5, 5)); + lm80_write_value(client, LM80_REG_IN_MIN(6), + IN_TO_REG(LM80_INIT_IN_MIN_6, 6)); + lm80_write_value(client, LM80_REG_IN_MAX(6), + IN_TO_REG(LM80_INIT_IN_MAX_6, 6)); + lm80_write_value(client, LM80_REG_FAN1_MIN, + FAN_TO_REG(LM80_INIT_FAN_MIN_1, 2)); + lm80_write_value(client, LM80_REG_FAN2_MIN, + FAN_TO_REG(LM80_INIT_FAN_MIN_2, 2)); + lm80_write_value(client, LM80_REG_TEMP_HOT_MAX, + TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_MAX)); + lm80_write_value(client, LM80_REG_TEMP_HOT_HYST, + TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_HYST)); + lm80_write_value(client, LM80_REG_TEMP_OS_MAX, + TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_MAX)); + lm80_write_value(client, LM80_REG_TEMP_OS_HYST, + TEMP_LIMIT_TO_REG(LM80_INIT_TEMP_OS_HYST)); + + /* Start monitoring */ + lm80_write_value(client, LM80_REG_CONFIG, 0x01); +} + +void lm80_update_client(struct i2c_client *client) +{ + struct lm80_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > 2 * HZ) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting lm80 update\n"); +#endif + for (i = 0; i <= 6; i++) { + data->in[i] = + lm80_read_value(client, LM80_REG_IN(i)); + data->in_min[i] = + lm80_read_value(client, LM80_REG_IN_MIN(i)); + data->in_max[i] = + lm80_read_value(client, LM80_REG_IN_MAX(i)); + } + data->fan[0] = lm80_read_value(client, LM80_REG_FAN1); + data->fan_min[0] = + lm80_read_value(client, LM80_REG_FAN1_MIN); + data->fan[1] = lm80_read_value(client, LM80_REG_FAN2); + data->fan_min[1] = + lm80_read_value(client, LM80_REG_FAN2_MIN); + + data->temp = + (lm80_read_value(client, LM80_REG_TEMP) << 8) | + (lm80_read_value(client, LM80_REG_RES) & 0xf0); + data->temp_os_max = + lm80_read_value(client, LM80_REG_TEMP_OS_MAX); + data->temp_os_hyst = + lm80_read_value(client, LM80_REG_TEMP_OS_HYST); + data->temp_hot_max = + lm80_read_value(client, LM80_REG_TEMP_HOT_MAX); + data->temp_hot_hyst = + lm80_read_value(client, LM80_REG_TEMP_HOT_HYST); + + i = lm80_read_value(client, LM80_REG_FANDIV); + data->fan_div[0] = (i >> 2) & 0x03; + data->fan_div[1] = (i >> 4) & 0x03; + data->alarms = lm80_read_value(client, LM80_REG_ALARM1) + + (lm80_read_value(client, LM80_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void lm80_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm80_data *data = client->data; + int nr = ctl_name - LM80_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm80_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr], nr); + results[1] = IN_FROM_REG(data->in_max[nr], nr); + results[2] = IN_FROM_REG(data->in[nr], nr); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0], nr); + lm80_write_value(client, LM80_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1], nr); + lm80_write_value(client, LM80_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void lm80_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm80_data *data = client->data; + int nr = ctl_name - LM80_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm80_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data-> + fan_div[nr - 1])); + results[1] = + FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG + (data-> + fan_div[nr - + 1])); + lm80_write_value(client, + nr == + 1 ? LM80_REG_FAN1_MIN : + LM80_REG_FAN2_MIN, + data->fan_min[nr - 1]); + } + } +} + + +void lm80_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm80_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm80_update_client(client); + results[0] = TEMP_LIMIT_FROM_REG(data->temp_hot_max); + results[1] = TEMP_LIMIT_FROM_REG(data->temp_hot_hyst); + results[2] = TEMP_LIMIT_FROM_REG(data->temp_os_max); + results[3] = TEMP_LIMIT_FROM_REG(data->temp_os_hyst); + results[4] = TEMP_FROM_REG(data->temp); + *nrels_mag = 5; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_hot_max = TEMP_LIMIT_TO_REG(results[0]); + lm80_write_value(client, LM80_REG_TEMP_HOT_MAX, + data->temp_hot_max); + } + if (*nrels_mag >= 2) { + data->temp_hot_hyst = + TEMP_LIMIT_TO_REG(results[1]); + lm80_write_value(client, LM80_REG_TEMP_HOT_HYST, + data->temp_hot_hyst); + } + if (*nrels_mag >= 3) { + data->temp_os_max = TEMP_LIMIT_TO_REG(results[2]); + lm80_write_value(client, LM80_REG_TEMP_OS_MAX, + data->temp_os_max); + } + if (*nrels_mag >= 4) { + data->temp_os_hyst = TEMP_LIMIT_TO_REG(results[3]); + lm80_write_value(client, LM80_REG_TEMP_OS_HYST, + data->temp_os_hyst); + } + } +} + +void lm80_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm80_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm80_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void lm80_fan_div(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm80_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm80_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + results[2] = 2; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = lm80_read_value(client, LM80_REG_FANDIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0xcf) | (data->fan_div[1] << 4); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xf3) | (data->fan_div[0] << 2); + lm80_write_value(client, LM80_REG_FANDIV, old); + } + } +} + +int __init sensors_lm80_init(void) +{ + int res; + + printk("lm80.o version %s (%s)\n", LM_VERSION, LM_DATE); + lm80_initialized = 0; + + if ((res = i2c_add_driver(&lm80_driver))) { + printk + ("lm80.o: Driver registration failed, module not inserted.\n"); + lm80_cleanup(); + return res; + } + lm80_initialized++; + return 0; +} + +int __init lm80_cleanup(void) +{ + int res; + + if (lm80_initialized >= 1) { + if ((res = i2c_del_driver(&lm80_driver))) { + printk + ("lm80.o: Driver deregistration failed, module not removed.\n"); + return res; + } + lm80_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("LM80 driver"); + +int init_module(void) +{ + return sensors_lm80_init(); +} + +int cleanup_module(void) +{ + return lm80_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/lm87.c Mon Mar 25 15:28:40 CET 2002 +++ linux/drivers/sensors/lm87.c Mon Mar 25 15:28:40 CET 2002 @@ -0,0 +1,1081 @@ +/* + LM87.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2000 Frodo Looijaard + Philip Edelbrock + Stephen Rousset + Dan Eaton + + 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 +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +/* Chip configuration settings. These should be set to reflect the +HARDWARE configuration of your chip. By default (read: when all of +these are left commented out), this driver assumes that the +configuration is the same as National's defaults for the Channel Mode +register. + +Set to '1' the appropriate defines, as nessesary: + + - External temp sensors 2 (possible second CPU temp) + This will disable the 2.5V and Vccp2 readings. + Ironically, National decided that you can read the + temperature of a second CPU or it's core voltage, + but not both! Comment out if FAULT is reported. */ + +/* #define LM87_EXT2 1 */ + +/* Aux analog input. When enabled, the Fan 1 reading + will be disabled */ + +/* #define LM87_AIN1 1 */ + +/* Aux analog input 2. When enabled, the Fan 2 reading + will be disabled */ + +/* #define LM87_AIN2 1 */ + +/* Internal Vcc is 5V instead of 3.3V */ + +/* #define LM87_5V_VCC 1 */ + +/* That's the end of the hardware config defines. I would have made + them insmod params, but it would be too much work. ;') */ + + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x2c, 0x2e, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(lm87); + +/* The following is the calculation for the register offset + * for the monitored items minimum and maximum locations. + */ +#define LM87_REG_IN_MAX(nr) (0x2b + ((nr) * 2)) +#define LM87_REG_IN_MIN(nr) (0x2c + ((nr) * 2)) +#define LM87_REG_IN(nr) (0x20 + (nr)) + +/* Initial limits */ + +/* + * LM87 register definition + * + */ + + /* The LM87 registers */ +#define LM87_INT_TEMP_HI_LIMIT_LOCKABLE 0x13 +#define LM87_EXT_TEMP_HI_LIMIT_LOCKABLE 0x14 +#define LM87_REG_TEST 0x15 +#define LM87_REG_CHANNEL_MODE 0x16 +#define LM87_REG_INT_TEMP_HI_LIMIT 0x17 +#define LM87_REG_EXT_TEMP_HI_LIMIT 0x18 +#define LM87_REG_ANALOG_OUT 0x19 + + /* These are all read-only */ +#define LM87_REG_2_5V_EXT_TEMP_2 0x20 +#define LM87_REG_VCCP1 0x21 +#define LM87_REG_3_3V 0x22 +#define LM87_REG_5V 0x23 +#define LM87_REG_12V 0x24 +#define LM87_REG_VCCP2 0x25 +#define LM87_REG_EXT_TEMP_1 0x26 +#define LM87_REG_INT_TEMP 0x27 /* LM87 temp. */ +#define LM87_REG_FAN1_AIN1 0x28 +#define LM87_REG_FAN2_AIN2 0x29 + +/* These are read/write */ +#define LM87_REG_AIN1_LOW 0x1A +#define LM87_REG_AIN2_LOW 0x1B +#define LM87_REG_2_5V_EXT_TEMP_2_HIGH 0x2B +#define LM87_REG_2_5V_EXT_TEMP_2_LOW 0x2C +#define LM87_REG_VCCP1_HIGH 0x2D +#define LM87_REG_VCCP1_LOW 0x2E +#define LM87_REG_3_3V_HIGH 0x2F +#define LM87_REG_3_3V_LOW 0x30 +#define LM87_REG_5V_HIGH 0x31 +#define LM87_REG_5V_LOW 0x32 +#define LM87_REG_12V_HIGH 0x33 +#define LM87_REG_12V_LOW 0x34 +#define LM87_REG_VCCP2_HIGH 0x35 +#define LM87_REG_VCCP2_LOW 0x36 +#define LM87_REG_EXT_TEMP_1_HIGH 0x37 +#define LM87_REG_EXT_TEMP_1_LOW 0x38 +#define LM87_REG_INT_TEMP_HIGH 0x39 +#define LM87_REG_INT_TEMP_LOW 0x3A +#define LM87_REG_FAN1_AIN1_LIMIT 0x3B +#define LM87_REG_FAN2_AIN2_LIMIT 0x3C +#define LM87_REG_COMPANY_ID 0x3E +#define LM87_REG_DIE_REV 0x3F + +#define LM87_REG_CONFIG 0x40 +#define LM87_REG_INT1_STAT 0x41 +#define LM87_REG_INT2_STAT 0x42 +#define LM87_REG_INT1_MASK 0x43 +#define LM87_REG_INT2_MASK 0x44 +#define LM87_REG_CHASSIS_CLEAR 0x46 +#define LM87_REG_VID_FAN_DIV 0x47 +#define LM87_REG_VID4 0x49 +#define LM87_REG_CONFIG_2 0x4A +#define LM87_REG_INTRPT_STATUS_1_MIRROR 0x4C +#define LM87_REG_INTRPT_STATUS_2_MIRROR 0x4D +#define LM87_REG_SMBALERT_NUM_ENABLE 0x80 + + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + +#define IN_TO_REG(val,nr) (SENSORS_LIMIT(((val) & 0xff),0,255)) +#define IN_FROM_REG(val,nr) (val) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:\ + (val)==255?0:1350000/((div)*(val))) + +#define TEMP_FROM_REG(temp) (temp * 10) + +#define TEMP_LIMIT_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) + +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10),0,255) +#if 0 +#define TEMP_FROM_REG(temp) \ + ((temp)<256?((((temp)&0x1fe) >> 1) * 10) + ((temp) & 1) * 5: \ + ((((temp)&0x1fe) >> 1) -255) * 10 - ((temp) & 1) * 5) \ + +#define TEMP_LIMIT_FROM_REG(val) (val) + +#define TEMP_LIMIT_TO_REG(val) SENSORS_LIMIT((val),0,255) +#endif + + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==1?0:((val)==8?3:((val)==4?2:1))) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) + +#define LM87_INIT_FAN_MIN 3000 + +#define LM87_INIT_EXT_TEMP_MAX 600 +#define LM87_INIT_EXT_TEMP_MIN 100 +#define LM87_INIT_INT_TEMP_MAX 600 +#define LM87_INIT_INT_TEMP_MIN 100 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For each registered LM87, we need to keep some data in memory. That + data is pointed to by LM87_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new LM87 client is + allocated. */ +struct lm87_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[6]; /* Scaled Register value */ + u8 in_max[6]; /* Scaled Register value */ + u8 in_min[6]; /* Scaled Register value */ + u8 ain1; /* Register value */ + u8 ain1_min; /* Register value */ + u8 ain1_max; /* Register value */ + u8 ain2; /* Register value */ + u8 ain2_min; /* Register value */ + u8 ain2_max; /* Register value */ + u8 fan; /* Register value */ + u8 fan_min; /* Register value */ + u8 fan_div; /* Register encoding, shifted right */ + u8 fan2; /* Register value */ + u8 fan2_min; /* Register value */ + u8 fan2_div; /* Register encoding, shifted right */ + int ext2_temp; /* Temp, shifted right */ + int ext_temp; /* Temp, shifted right */ + int int_temp; /* Temp, shifted right */ + u8 ext_temp_max; /* Register value */ + u8 ext_temp_min; /* Register value */ + u8 ext2_temp_max; /* Register value */ + u8 ext2_temp_min; /* Register value */ + u8 int_temp_max; /* Register value */ + u8 int_temp_min; /* Register value */ + u16 alarms; /* Register encoding, combined */ + u8 analog_out; /* Register value */ + u8 vid; /* Register value combined */ +}; + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_lm87_init(void); +static int __init lm87_cleanup(void); + +static int lm87_attach_adapter(struct i2c_adapter *adapter); +static int lm87_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int lm87_detach_client(struct i2c_client *client); +static int lm87_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void lm87_inc_use(struct i2c_client *client); +static void lm87_dec_use(struct i2c_client *client); + +static int lm87_read_value(struct i2c_client *client, u8 register); +static int lm87_write_value(struct i2c_client *client, u8 register, + u8 value); +static void lm87_update_client(struct i2c_client *client); +static void lm87_init_client(struct i2c_client *client); + + +static void lm87_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +#if defined (LM87_AIN1) || defined (LM87_AIN2) +static void lm87_ain(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +#endif +static void lm87_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm87_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm87_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm87_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void lm87_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results); +static void lm87_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* I choose here for semi-static LM87 allocation. Complete dynamic + allocation could also be used; the code needed for this would probably + take more memory than the datastructure takes now. */ +static int lm87_id = 0; + +static struct i2c_driver LM87_driver = { + /* name */ "LM87 sensor driver", + /* id */ I2C_DRIVERID_LM87, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &lm87_attach_adapter, + /* detach_client */ &lm87_detach_client, + /* command */ &lm87_command, + /* inc_use */ &lm87_inc_use, + /* dec_use */ &lm87_dec_use +}; + +/* Used by LM87_init/cleanup */ +static int __initdata lm87_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected LM87. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ + +static ctl_table LM87_dir_table_template[] = { +#ifdef LM87_AIN1 + {LM87_SYSCTL_AIN1, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_ain}, +#endif +#ifdef LM87_AIN2 + {LM87_SYSCTL_AIN2, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_ain}, +#endif +#ifndef LM87_EXT2 + {LM87_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_in}, + {LM87_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_in}, +#endif + {LM87_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_in}, + {LM87_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_in}, + {LM87_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_in}, + {LM87_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_in}, +#ifndef LM87_AIN1 + {LM87_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_fan}, + {LM87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_fan_div}, +#define LM87_FANDIV_FLAG +#endif +#ifndef LM87_AIN2 + {LM87_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_fan}, +#ifndef LM87_FANDIV_FLAG + {LM87_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_fan_div}, +#endif /* LM87_FANDIV_FLAG */ +#endif /* LM87_AIN2 */ +#ifdef LM87_EXT2 + {LM87_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_temp}, +#endif + {LM87_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_temp}, + {LM87_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_temp}, + {LM87_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_alarms}, + {LM87_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_analog_out}, + {LM87_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &lm87_vid}, + {0} +}; + +int lm87_attach_adapter(struct i2c_adapter *adapter) +{ + int error; + struct i2c_client_address_data lm87_client_data; + + lm87_client_data.normal_i2c = addr_data.normal_i2c; + lm87_client_data.normal_i2c_range = addr_data.normal_i2c_range; + lm87_client_data.probe = addr_data.probe; + lm87_client_data.probe_range = addr_data.probe_range; + lm87_client_data.ignore = addr_data.ignore; + lm87_client_data.ignore_range = addr_data.ignore_range; + lm87_client_data.force = addr_data.forces->force; + + error = i2c_probe(adapter, &lm87_client_data, lm87_detect); + i2c_detect(adapter, &addr_data, lm87_detect); + + return error; +} + +static int lm87_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct lm87_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access LM87_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct lm87_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct lm87_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &LM87_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + if (kind < 0) { + if (((lm87_read_value(new_client, LM87_REG_CONFIG) & 0x80) + != 0x00) || + (lm87_read_value(new_client, LM87_REG_COMPANY_ID) != 0x02)) + goto ERROR1; + } + + /* Fill in the remaining client fields and put into the global list */ + type_name = "lm87"; + client_name = "LM87 chip"; + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = lm87_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + LM87_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the LM87 chip */ + lm87_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int lm87_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct lm87_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("lm87.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; + +} + +/* No commands defined yet */ +int lm87_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void lm87_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void lm87_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +int lm87_read_value(struct i2c_client *client, u8 reg) +{ + return 0xFF & i2c_smbus_read_byte_data(client, reg); +} + +int lm87_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new LM87. It should set limits, etc. */ +void lm87_init_client(struct i2c_client *client) +{ + int vid; + u8 v; + + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others. This makes most other + initializations unnecessary */ + lm87_write_value(client, LM87_REG_CONFIG, 0x80); + + /* Setup Channel Mode register for configuration of monitoring + * Default is 00000000b + * bit 0 - Configures Fan 1/AIN 1 input (1 = AIN) + * bit 1 - Configures Fan 2/AIN 2 input (1 = AIN) + * bit 2 - Configures 2.5V&Vccp2/D2 input (1 = 2nd Therm.) + * bit 3 - Configures Vcc for 5V/3.3V reading (0 = 3.3V) + * bit 4 - Configures IRQ0 Enable if = 1 + * bit 5 - Configures IRQ1 Enable if = 1 + * bit 6 - Configures IRQ2 Enable if = 1 + * bit 7 - Configures VID/IRQ input as interrupts if = 1 + */ + +/* I know, not clean, but it works. :'p */ + lm87_write_value(client, LM87_REG_CHANNEL_MODE, +#ifdef LM87_AIN1 + 0x01 +#else +0 +#endif + | +#ifdef LM87_AIN2 + 0x02 +#else +0 +#endif + | +#ifdef LM87_EXT2 + 0x04 +#else +0 +#endif + | +#ifdef LM87_5V_VCC +0x08 +#else +0 +#endif + ); + + /* Set IN (voltage) initial limits to sane values +/- 5% */ + lm87_write_value(client, LM87_REG_IN_MIN(0),182); + lm87_write_value(client, LM87_REG_IN_MAX(0),202); + lm87_write_value(client, LM87_REG_IN_MIN(2),182); + lm87_write_value(client, LM87_REG_IN_MAX(2),202); + lm87_write_value(client, LM87_REG_IN_MIN(3),182); + lm87_write_value(client, LM87_REG_IN_MAX(3),202); + lm87_write_value(client, LM87_REG_IN_MIN(4),182); + lm87_write_value(client, LM87_REG_IN_MAX(4),202); + + /* Set CPU core voltage limits relative to vid readings +/- 5% */ + v = (lm87_read_value(client, LM87_REG_VID_FAN_DIV) & 0x0f) + | ((lm87_read_value(client, LM87_REG_VID4) & 0x01) + << 4 ); + vid = VID_FROM_REG(v); + v = vid * 95 * 192 / 27000; + lm87_write_value(client, LM87_REG_IN_MIN(1), v); + lm87_write_value(client, LM87_REG_IN_MIN(5), v); + v = vid * 105 * 192 / 27000; + lm87_write_value(client, LM87_REG_IN_MAX(1), v); + lm87_write_value(client, LM87_REG_IN_MAX(5), v); + + /* Set Temp initial limits to sane values */ + lm87_write_value(client, LM87_REG_EXT_TEMP_1_HIGH, + TEMP_LIMIT_TO_REG(LM87_INIT_EXT_TEMP_MAX)); + lm87_write_value(client, LM87_REG_EXT_TEMP_1_LOW, + TEMP_LIMIT_TO_REG(LM87_INIT_EXT_TEMP_MIN)); +#ifdef LM87_EXT2 + lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH, + TEMP_LIMIT_TO_REG(LM87_INIT_EXT_TEMP_MAX)); + lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW, + TEMP_LIMIT_TO_REG(LM87_INIT_EXT_TEMP_MIN)); +#endif + lm87_write_value(client, LM87_REG_INT_TEMP_HIGH, + TEMP_LIMIT_TO_REG(LM87_INIT_INT_TEMP_MAX)); + lm87_write_value(client, LM87_REG_INT_TEMP_LOW, + TEMP_LIMIT_TO_REG(LM87_INIT_INT_TEMP_MIN)); + +#ifndef LM87_AIN1 + lm87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT, + FAN_TO_REG(LM87_INIT_FAN_MIN, 2)); +#endif +#ifndef LM87_AIN2 + lm87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT, + FAN_TO_REG(LM87_INIT_FAN_MIN, 2)); +#endif + + /* Start monitoring */ + lm87_write_value(client, LM87_REG_CONFIG, 0x01); +} + +void lm87_update_client(struct i2c_client *client) +{ + struct lm87_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ) || /* 1 sec cache */ + (jiffies < data->last_updated) || + !data->valid) { + for (i = 0; i <= 5; i++) { + data->in[i] = + lm87_read_value(client,LM87_REG_IN(i)); + data->in_min[i] = + lm87_read_value(client,LM87_REG_IN_MIN(i)); + data->in_max[i] = + lm87_read_value(client,LM87_REG_IN_MAX(i)); + } + data->ain1 = + lm87_read_value(client,LM87_REG_FAN1_AIN1); + data->ain1_min = + lm87_read_value(client,LM87_REG_AIN1_LOW); + data->ain1_max = + lm87_read_value(client,LM87_REG_FAN1_AIN1_LIMIT); + data->ain2 = + lm87_read_value(client,LM87_REG_FAN2_AIN2); + data->ain2_min = + lm87_read_value(client,LM87_REG_AIN2_LOW); + data->ain2_max = + lm87_read_value(client,LM87_REG_FAN2_AIN2_LIMIT); + + data->fan = + lm87_read_value(client, LM87_REG_FAN1_AIN1); + data->fan_min = + lm87_read_value(client, LM87_REG_FAN1_AIN1_LIMIT); + data->fan2 = + lm87_read_value(client, LM87_REG_FAN2_AIN2); + data->fan2_min = + lm87_read_value(client, LM87_REG_FAN2_AIN2_LIMIT); + + data->ext2_temp = + lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2); + data->ext_temp = + lm87_read_value(client, LM87_REG_EXT_TEMP_1); + data->int_temp = + lm87_read_value(client, LM87_REG_INT_TEMP); + + data->ext2_temp_max = + lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH); + data->ext2_temp_min = + lm87_read_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW); + + data->ext_temp_max = + lm87_read_value(client, LM87_REG_EXT_TEMP_1_HIGH); + data->ext_temp_min = + lm87_read_value(client, LM87_REG_EXT_TEMP_1_LOW); + + data->int_temp_max = + lm87_read_value(client, LM87_REG_INT_TEMP_HIGH); + data->int_temp_min = + lm87_read_value(client, LM87_REG_INT_TEMP_LOW); + + i = lm87_read_value(client, LM87_REG_VID_FAN_DIV); + data->fan_div = (i >> 4) & 0x03; + data->fan2_div = (i >> 6) & 0x03; + data->vid = i & 0x0f; + data->vid |= + (lm87_read_value(client, LM87_REG_VID4) & 0x01) + << 4; + data->alarms = + lm87_read_value(client, LM87_REG_INT1_STAT) + + (lm87_read_value(client, LM87_REG_INT2_STAT) << 8); + data->analog_out = + lm87_read_value(client, LM87_REG_ANALOG_OUT); + data->last_updated = jiffies; + data->valid = 1; + } + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void lm87_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + long scales[6] = { 250, 270, +#ifdef LM87_5V_VCC +500, +#else +330, +#endif + 500, 1200, 270 }; + + struct lm87_data *data = client->data; + int nr = ctl_name - LM87_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); + results[0] = + ((long)data->in_min[nr] * scales[nr]) / 192; + results[1] = + ((long)data->in_max[nr] * scales[nr]) / 192; + results[2] = + ((long)data->in[nr] * scales[nr]) / 192; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = + (results[0] * 192) / scales[nr]; + lm87_write_value(client, LM87_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = + (results[1] * 192) / scales[nr]; + lm87_write_value(client, LM87_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +#if defined (LM87_AIN1) || defined (LM87_AIN2) +void lm87_ain(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm87_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); + if (ctl_name == LM87_SYSCTL_AIN1) { + results[0] = data->ain1_min; + results[1] = data->ain1_max; + results[2] = data->ain1; + } else { + results[0] = data->ain2_min; + results[1] = data->ain2_max; + results[2] = data->ain2; + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + if (ctl_name == LM87_SYSCTL_AIN1) { + data->ain1_min = results[0]; + lm87_write_value(client, LM87_REG_AIN1_LOW, + data->ain1_min); + } else { + data->ain2_min = results[0]; + lm87_write_value(client, LM87_REG_AIN2_LOW, + data->ain2_min); + } + } + if (*nrels_mag >= 2) { + if (ctl_name == LM87_SYSCTL_AIN1) { + data->ain1_max = results[1]; + lm87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT, + data->ain1_max); + } else { + data->ain2_max = results[1]; + lm87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT, + data->ain2_max); + } + } + } +} +#endif + +void lm87_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm87_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); + if (ctl_name == LM87_SYSCTL_FAN1) { + results[0] = FAN_FROM_REG(data->fan_min, + DIV_FROM_REG(data->fan_div)); + results[1] = FAN_FROM_REG(data->fan, + DIV_FROM_REG(data->fan_div)); + } else { + results[0] = FAN_FROM_REG(data->fan2_min, + DIV_FROM_REG(data->fan2_div)); + results[1] = FAN_FROM_REG(data->fan2, + DIV_FROM_REG(data->fan2_div)); + } + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 0) { + if (ctl_name == LM87_SYSCTL_FAN1) { + data->fan_min = FAN_TO_REG(results[0], + DIV_FROM_REG + (data->fan_div)); + lm87_write_value(client, LM87_REG_FAN1_AIN1_LIMIT, + data->fan_min); + } else { + data->fan2_min = FAN_TO_REG(results[0], + DIV_FROM_REG + (data->fan2_div)); + lm87_write_value(client, LM87_REG_FAN2_AIN2_LIMIT, + data->fan2_min); + } + } + } +} + + +void lm87_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm87_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) + { + lm87_update_client(client); + + /* find out which temp. is being requested */ + if (ctl_name == LM87_SYSCTL_TEMP3) + { + results[0] = TEMP_LIMIT_FROM_REG(data->ext2_temp_max); + results[1] = TEMP_LIMIT_FROM_REG(data->ext2_temp_min); + results[2] = TEMP_FROM_REG(data->ext2_temp); + } + else if(ctl_name == LM87_SYSCTL_TEMP2) + { + results[0] = TEMP_LIMIT_FROM_REG(data->ext_temp_max); + results[1] = TEMP_LIMIT_FROM_REG(data->ext_temp_min); + results[2] = TEMP_FROM_REG(data->ext_temp); + } + else if(ctl_name == LM87_SYSCTL_TEMP1) + { + results[0] = TEMP_LIMIT_FROM_REG(data->int_temp_max); + results[1] = TEMP_LIMIT_FROM_REG(data->int_temp_min); + results[2] = TEMP_FROM_REG(data->int_temp); + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + if (ctl_name == LM87_SYSCTL_TEMP3) { + data->ext2_temp_max = TEMP_LIMIT_TO_REG(results[0]); + lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_HIGH, + data->ext2_temp_max); + } + if (ctl_name == LM87_SYSCTL_TEMP2) { + data->ext_temp_max = TEMP_LIMIT_TO_REG(results[0]); + lm87_write_value(client, LM87_REG_EXT_TEMP_1_HIGH, + data->ext_temp_max); + } + if (ctl_name == LM87_SYSCTL_TEMP1) { + data->int_temp_max = TEMP_LIMIT_TO_REG(results[0]); + lm87_write_value(client, LM87_REG_INT_TEMP_HIGH, + data->int_temp_max); + } + } + if (*nrels_mag >= 2) { + if (ctl_name == LM87_SYSCTL_TEMP3) { + data->ext2_temp_min = TEMP_LIMIT_TO_REG(results[1]); + lm87_write_value(client, LM87_REG_2_5V_EXT_TEMP_2_LOW, + data->ext2_temp_min); + } + if (ctl_name == LM87_SYSCTL_TEMP2) { + data->ext_temp_min = TEMP_LIMIT_TO_REG(results[1]); + lm87_write_value(client, LM87_REG_EXT_TEMP_1_LOW, + data->ext_temp_min); + } + if (ctl_name == LM87_SYSCTL_TEMP1) { + data->int_temp_min = TEMP_LIMIT_TO_REG(results[1]); + lm87_write_value(client, LM87_REG_INT_TEMP_LOW, + data->int_temp_min); + } + } + } +} + +void lm87_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm87_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void lm87_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ +/* This gets a little hairy depending on the hardware config */ + + struct lm87_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); +#ifndef LM87_AIN1 + results[0] = DIV_FROM_REG(data->fan_div); +# ifndef LM87_AIN2 + results[1] = DIV_FROM_REG(data->fan2_div); + *nrels_mag = 2; +# else + *nrels_mag = 1; +# endif +#else /* Must be referring to fan 2 */ + results[0] = DIV_FROM_REG(data->fan2_div); + *nrels_mag = 1; +#endif + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = lm87_read_value(client, LM87_REG_VID_FAN_DIV); +/* Note: it's OK to change fan2 div even if fan2 isn't enabled */ +#ifndef LM87_AIN1 + if (*nrels_mag >= 2) { + data->fan2_div = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan2_div << 6); + } + if (*nrels_mag >= 1) { + data->fan_div = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div << 4); + lm87_write_value(client, LM87_REG_VID_FAN_DIV, old); + } +#else /* Must be referring to fan 2 */ + if (*nrels_mag >= 1) { + data->fan2_div = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan2_div << 6); + lm87_write_value(client, LM87_REG_VID_FAN_DIV, old); + } +#endif + } +} + +void lm87_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct lm87_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); + results[0] = data->analog_out; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->analog_out = results[0]; + lm87_write_value(client, LM87_REG_ANALOG_OUT, + data->analog_out); + } + } +} + +void lm87_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct lm87_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + lm87_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +int __init sensors_lm87_init(void) +{ + int res; + + printk("lm87.o version %s (%s)\n", LM_VERSION, LM_DATE); + lm87_initialized = 0; + + if ((res = i2c_add_driver(&LM87_driver))) { + printk + ("lm87.o: Driver registration failed, module not inserted.\n"); + lm87_cleanup(); + return res; + } + lm87_initialized++; + return 0; +} + +int __init lm87_cleanup(void) +{ + int res; + + if (lm87_initialized >= 1) { + if ((res = i2c_del_driver(&LM87_driver))) { + printk + ("lm87.o: Driver deregistration failed, module not removed.\n"); + return res; + } + lm87_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +MODULE_AUTHOR + ("Frodo Looijaard , + Philip Edelbrock , + Mark Studebaker , + and Stephen Rousset "); + +MODULE_DESCRIPTION("LM87 driver"); + +int init_module(void) +{ + return sensors_lm87_init(); +} + +int cleanup_module(void) +{ + return lm87_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/matorb.c Mon Mar 25 15:28:40 CET 2002 +++ linux/drivers/sensors/matorb.c Mon Mar 25 15:28:40 CET 2002 @@ -0,0 +1,371 @@ +/* + matorb.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + and Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#define DEBUG 1 + +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { 0x2E, SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(matorb); + +/* Many MATORB constants specified below */ + + +/* Each client has this additional data */ +struct matorb_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_matorb_init(void); +static int __init matorb_cleanup(void); +static int matorb_attach_adapter(struct i2c_adapter *adapter); +static int matorb_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void matorb_init_client(struct i2c_client *client); +static int matorb_detach_client(struct i2c_client *client); +static int matorb_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void matorb_inc_use(struct i2c_client *client); +static void matorb_dec_use(struct i2c_client *client); +static int matorb_write_value(struct i2c_client *client, u8 reg, + u16 value); +static void matorb_disp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void matorb_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver matorb_driver = { + /* name */ "Matrix Orbital LCD driver", + /* id */ I2C_DRIVERID_MATORB, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &matorb_attach_adapter, + /* detach_client */ &matorb_detach_client, + /* command */ &matorb_command, + /* inc_use */ &matorb_inc_use, + /* dec_use */ &matorb_dec_use +}; + +/* These files are created for each detected MATORB. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table matorb_dir_table_template[] = { + {MATORB_SYSCTL_DISP, "disp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &matorb_disp}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata matorb_initialized = 0; + +static int matorb_id = 0; + +int matorb_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, matorb_detect); +} + +/* This function is called by i2c_detect */ +int matorb_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, cur; + struct i2c_client *new_client; + struct matorb_data *data; + int err = 0; + const char *type_name = "matorb"; + const char *client_name = "matorb"; + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("matorb.o: matorb_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_WRITE_BYTE_DATA)) + goto ERROR0; + + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access matorb_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct matorb_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct matorb_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &matorb_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. It is lousy. */ + cur = i2c_smbus_write_byte_data(new_client, 0x0FE, 0x58); /* clear screen */ + + printk("matorb.o: debug detect 0x%X\n", cur); + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = matorb_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + matorb_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + matorb_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + kfree(new_client); + ERROR0: + return err; +} + +int matorb_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct matorb_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("matorb.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int matorb_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void matorb_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void matorb_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +#if 0 +/* All registers are word-sized, except for the configuration register. + MATORB uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int matorb_read_value(struct i2c_client *client, u8 reg) +{ + return -1; /* Doesn't support reads */ +} +#endif + +/* All registers are word-sized, except for the configuration register. + MATORB uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int matorb_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + if (reg == 0) { + return i2c_smbus_write_byte(client, value); + } else { + return i2c_smbus_write_byte_data(client, reg, value); + } +} + +void matorb_init_client(struct i2c_client *client) +{ + /* Initialize the MATORB chip */ +} + +void matorb_update_client(struct i2c_client *client) +{ + struct matorb_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting matorb update\n"); +#endif + +/* nothing yet */ + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void matorb_disp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int i; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + matorb_update_client(client); + results[0] = 0; + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + for (i = 1; i <= *nrels_mag; i++) { + matorb_write_value(client, 0, results[i - 1]); + } + } +} + +int __init sensors_matorb_init(void) +{ + int res; + + printk("matorb.o version %s (%s)\n", LM_VERSION, LM_DATE); + matorb_initialized = 0; + if ((res = i2c_add_driver(&matorb_driver))) { + printk + ("matorb.o: Driver registration failed, module not inserted.\n"); + matorb_cleanup(); + return res; + } + matorb_initialized++; + return 0; +} + +int __init matorb_cleanup(void) +{ + int res; + + if (matorb_initialized >= 1) { + if ((res = i2c_del_driver(&matorb_driver))) { + printk + ("matorb.o: Driver deregistration failed, module not removed.\n"); + return res; + } + matorb_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("MATORB driver"); + +int init_module(void) +{ + return sensors_matorb_init(); +} + +int cleanup_module(void) +{ + return matorb_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/maxilife.c Mon Mar 25 15:28:40 CET 2002 +++ linux/drivers/sensors/maxilife.c Mon Mar 25 15:28:40 CET 2002 @@ -0,0 +1,1442 @@ +/* + maxilife.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1999-2000 Fons Rademakers + + 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. +*/ + +/* The is the driver for the HP MaxiLife Health monitoring system + as used in the line of HP Kayak Workstation PC's. + + The driver supports the following MaxiLife firmware versions: + + 0) HP KAYAK XU/XAs (Dual Pentium II Slot 1, Deschutes/Klamath) + 1) HP KAYAK XU (Dual Xeon [Slot 2] 400/450 Mhz) + 2) HP KAYAK XA (Pentium II Slot 1, monoprocessor) + + Currently firmware auto detection is not implemented. To use the + driver load it with the correct option for you Kayak. For example: + + insmod maxilife.o maxi_version=0 | 1 | 2 + + maxi_version=0 is the default + + This version of MaxiLife is called MaxiLife'98 and has been + succeeded by MaxiLife'99, see below. + + The new version of the driver also supports MaxiLife NBA (New BIOS + Architecture). This new MaxiLife controller provides a much cleaner + machine independent abstraction layer to the MaxiLife controller. + Instead of accessing directly registers (different for each revision) + one now accesses the sensors via unique mailbox tokens that do not + change between revisions. Also the quantities are already in physical + units (degrees, rpms, voltages, etc.) and don't need special conversion + formulas. This new MaxiLife is available on the new 2000 machines, + like the Kayak XU800 and XM600. This hardware is also autodetected. +*/ + +static const char *version_str = "2.00 29/2/2000 Fons Rademakers"; + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + + +#undef AUTODETECT /* try to autodetect MaxiLife version */ +/*#define AUTODETECT*/ +#define NOWRITE /* don't allow writing to MaxiLife registers */ + +#ifdef AUTODETECT +#include +#include +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x10, 0x14, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(maxilife); + +/* Macro definitions */ +#define LOW(MyWord) ((u8) (MyWord)) +#define HIGH(MyWord) ((u8) (((u16)(MyWord) >> 8) & 0xFF)) + +/*----------------- MaxiLife'98 registers and conversion formulas ------------*/ +#define MAXI_REG_TEMP(nr) (0x60 + (nr)) + +#define MAXI_REG_FAN(nr) (0x65 + (nr)) +#define MAXI_REG_FAN_MIN(nr) ((nr)==0 ? 0xb3 : (nr)==1 ? 0xb3 : 0xab) +#define MAXI_REG_FAN_MINAS(nr) ((nr)==0 ? 0xb3 : (nr)==1 ? 0xab : 0xb3) +#define MAXI_REG_FAN_SPEED(nr) ((nr)==0 ? 0xe4 : (nr)==1 ? 0xe5 : 0xe9) + +#define MAXI_REG_PLL 0xb9 +#define MAXI_REG_PLL_MIN 0xba +#define MAXI_REG_PLL_MAX 0xbb + +#define MAXI_REG_VID(nr) ((nr)==0 ? 0xd1 : (nr)==1 ? 0xd9 : \ + (nr)==2 ? 0xd4 : 0xc5) +#define MAXI_REG_VID_MIN(nr) MAXI_REG_VID(nr)+1 +#define MAXI_REG_VID_MAX(nr) MAXI_REG_VID(nr)+2 + +#define MAXI_REG_DIAG_RT1 0x2c +#define MAXI_REG_DIAG_RT2 0x2d + +#define MAXI_REG_BIOS_CTRL 0x2a + +/* 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. */ + + /* 0xfe: fan off, 0xff: stopped (alarm) */ + /* 19531 / val * 60 == 1171860 / val */ +#define FAN_FROM_REG(val) ((val)==0xfe ? 0 : (val)==0xff ? -1 : \ + (val)==0x00 ? -1 : (1171860 / (val))) + +extern inline u8 FAN_TO_REG(long rpm) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1171860 + rpm / 2) / (rpm), 1, 254); +} + +#define TEMP_FROM_REG(val) ((val) * 5) +#define TEMP_TO_REG(val) (SENSORS_LIMIT((val+2) / 5),0,0xff) +#define PLL_FROM_REG(val) (((val) * 1000) / 32) +#define PLL_TO_REG(val) (SENSORS_LIMIT((((val) * 32 + 500) / 1000),\ + 0,0xff)) +#define VID_FROM_REG(val) ((val) ? (((val) * 27390) / 256) + 3208 : 0) +#define VID_TO_REG(val) (SENSORS_LIMIT((((val) - 3208) * 256) / 27390, \ + 0,255)) +#define ALARMS_FROM_REG(val) (val) + +/*----------------- MaxiLife'99 mailbox and token definitions ----------------*/ +/* MaxiLife mailbox data register map */ +#define MAXI_REG_MBX_STATUS 0x5a +#define MAXI_REG_MBX_CMD 0x5b +#define MAXI_REG_MBX_TOKEN_H 0x5c +#define MAXI_REG_MBX_TOKEN_L 0x5d +#define MAXI_REG_MBX_DATA 0x60 + +/* Mailbox status register definition */ +#define MAXI_STAT_IDLE 0xff +#define MAXI_STAT_OK 0x00 +#define MAXI_STAT_BUSY 0x0b +/* other values not used */ + +/* Mailbox command register opcodes */ +#define MAXI_CMD_READ 0x02 +#define MAXI_CMD_WRITE 0x03 +/* other values not used */ + +/* MaxiLife NBA Hardware monitoring tokens */ + +/* Alarm tokens (0x1xxx) */ +#define MAXI_TOK_ALARM(nr) (0x1000 + (nr)) +#define MAXI_TOK_ALARM_EVENT 0x1000 +#define MAXI_TOK_ALARM_FAN 0x1001 +#define MAXI_TOK_ALARM_TEMP 0x1002 +#define MAXI_TOK_ALARM_VID 0x1003 /* voltages */ +#define MAXI_TOK_ALARM_AVID 0x1004 /* additional voltages */ +#define MAXI_TOK_ALARM_PWR 0x1101 /* power supply glitch */ + +/* Fan status tokens (0x20xx) */ +#define MAXI_TOK_FAN(nr) (0x2000 + (nr)) +#define MAXI_TOK_FAN_CPU 0x2000 +#define MAXI_TOK_FAN_PCI 0x2001 +#define MAXI_TOK_FAN_HDD 0x2002 /* hard disk bay fan */ +#define MAXI_TOK_FAN_SINK 0x2003 /* heatsink */ + +/* Temperature status tokens (0x21xx) */ +#define MAXI_TOK_TEMP(nr) (0x2100 + (nr)) +#define MAXI_TOK_TEMP_CPU1 0x2100 +#define MAXI_TOK_TEMP_CPU2 0x2101 +#define MAXI_TOK_TEMP_PCI 0x2102 /* PCI/ambient temp */ +#define MAXI_TOK_TEMP_HDD 0x2103 /* hard disk bay temp */ +#define MAXI_TOK_TEMP_MEM 0x2104 /* mother board temp */ +#define MAXI_TOK_TEMP_CPU 0x2105 /* CPU reference temp */ + +/* Voltage status tokens (0x22xx) */ +#define MAXI_TOK_VID(nr) (0x2200 + (nr)) +#define MAXI_TOK_VID_12 0x2200 /* +12 volt */ +#define MAXI_TOK_VID_CPU1 0x2201 /* cpu 1 voltage */ +#define MAXI_TOK_VID_CPU2 0x2202 /* cpu 2 voltage */ +#define MAXI_TOK_VID_L2 0x2203 /* level 2 cache voltage */ +#define MAXI_TOK_VID_M12 0x2204 /* -12 volt */ + +/* Additive voltage status tokens (0x23xx) */ +#define MAXI_TOK_AVID(nr) (0x2300 + (nr)) +#define MAXI_TOK_AVID_15 0x2300 /* 1.5 volt */ +#define MAXI_TOK_AVID_18 0x2301 /* 1.8 volt */ +#define MAXI_TOK_AVID_25 0x2302 /* 2.5 volt */ +#define MAXI_TOK_AVID_33 0x2303 /* 3.3 volt */ +#define MAXI_TOK_AVID_5 0x2304 /* 5 volt */ +#define MAXI_TOK_AVID_M5 0x2305 /* -5 volt */ +#define MAXI_TOK_AVID_BAT 0x2306 /* battery voltage */ + +/* Threshold tokens (0x3xxx) */ +#define MAXI_TOK_MIN(token) ((token) + 0x1000) +#define MAXI_TOK_MAX(token) ((token) + 0x1800) + +/* LCD Panel (0x4xxx) */ +#define MAXI_TOK_LCD(nr) (0x4000 + (nr)) +#define MAXI_TOK_LCD_LINE1 0x4000 +#define MAXI_TOK_LCD_LINE2 0x4001 +#define MAXI_TOK_LCD_LINE3 0x4002 +#define MAXI_TOK_LCD_LINE4 0x4003 + + /* 0xfe: fan off, 0xff: stopped (alarm) */ + /* or not available */ +#define FAN99_FROM_REG(val) ((val)==0xfe ? 0 : (val)==0xff ? -1 : ((val)*39)) + + /* when no CPU2 temp is 127 (0x7f) */ +#define TEMP99_FROM_REG(val) ((val)==0x7f ? -1 : (val)==0xff ? -1 : (val)) + +#define VID99_FROM_REG(nr,val) ((val)==0xff ? 0 : \ + (nr)==1 ? ((val) * 608) : \ + (nr)==2 ? ((val) * 160) : \ + (nr)==3 ? ((val) * 160) : \ + (nr)==4 ? (val) /* no formula spcified */ : \ + (nr)==5 ? ((val) * 823 - 149140) : 0) + + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* The following product codenames apply: + Cristal/Geronimo: HP KAYAK XU/XAs + (Dual Pentium II Slot 1, Deschutes/Klamath) + Cognac: HP KAYAK XU (Dual Xeon [Slot 2] 400/450 Mhz) + Ashaki: HP KAYAK XA (Pentium II Slot 1, monoprocessor) + NBA: New BIOS Architecture, Kayak XU800, XM600, ... */ + +enum maxi_type { cristal, cognac, ashaki, nba }; +enum sensor_type { fan, temp, vid, pll, lcd, alarm }; + +/* For each registered MaxiLife controller, we need to keep some data in + memory. That data is pointed to by maxi_list[NR]->data. The structure + itself is dynamically allocated, at the same time when a new MaxiLife + client is allocated. We assume MaxiLife will only be present on the + SMBus and not on the ISA bus. */ +struct maxi_data { + struct semaphore lock; + int sysctl_id; + enum maxi_type type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 fan[4]; /* Register value */ + u8 fan_min[4]; /* Register value */ + u8 fan_speed[4]; /* Register value */ + u8 fan_div[4]; /* Static value */ + u8 temp[6]; /* Register value */ + u8 temp_max[6]; /* Static value */ + u8 temp_hyst[6]; /* Static value */ + u8 pll; /* Register value */ + u8 pll_min; /* Register value */ + u8 pll_max; /* register value */ + u8 vid[5]; /* Register value */ + u8 vid_min[5]; /* Register value */ + u8 vid_max[5]; /* Register value */ + u8 lcd[4][17]; /* Four LCD lines */ + u16 alarms; /* Register encoding, combined */ +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_maxi_init(void); +static int __init maxi_cleanup(void); + +static int maxi_attach_adapter(struct i2c_adapter *adapter); +static int maxi_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int maxi_detach_client(struct i2c_client *client); +static int maxi_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void maxi_inc_use(struct i2c_client *client); +static void maxi_dec_use(struct i2c_client *client); + +static int maxi_read_value(struct i2c_client *client, u8 register); +static int maxi_read_token(struct i2c_client *client, u16 token); +#ifndef NOWRITE +static int maxi_write_value(struct i2c_client *client, u8 register, + u8 value); +#endif +static int maxi_write_token_loop(struct i2c_client *client, u16 token, + u8 len, u8 * values); + +static void maxi_update_client(struct i2c_client *client); +static void maxi99_update_client(struct i2c_client *client, + enum sensor_type sensor, int which); +static void maxi_init_client(struct i2c_client *client); + +static void maxi_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi99_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi99_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi_pll(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi99_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi_lcd(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void maxi_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +/* The driver. I choose to use type i2c_driver, as at is identical to + the smbus_driver. */ +static struct i2c_driver maxi_driver = { + /* name */ "HP MaxiLife driver", + /* id */ I2C_DRIVERID_MAXILIFE, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &maxi_attach_adapter, + /* detach_client */ &maxi_detach_client, + /* command */ &maxi_command, + /* inc_use */ &maxi_inc_use, + /* dec_use */ &maxi_dec_use +}; + +/* Used by maxi_init/cleanup */ +static int __initdata maxi_initialized = 0; + +static int maxi_id = 0; + +/* Default firmware version. Use module option "maxi_version" + to set desired version. Auto detect is not yet working */ +static int maxi_version = cristal; + +/* The /proc/sys entries */ +/* These files are created for each detected MaxiLife processor. + This is just a template; though at first sight, you might think we + could use a statically allocated list, we need some way to get back + to the parent - which is done through one of the 'extra' fields + which are initialized when a new copy is allocated. */ +static ctl_table maxi_dir_table_template[] = { + {MAXI_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_fan}, + {MAXI_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_fan}, + {MAXI_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_fan}, + {MAXI_SYSCTL_FAN4, "fan4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_fan}, + {MAXI_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_temp}, + {MAXI_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_temp}, + {MAXI_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_temp}, + {MAXI_SYSCTL_TEMP4, "temp4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_temp}, + {MAXI_SYSCTL_TEMP5, "temp5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_temp}, + {MAXI_SYSCTL_TEMP6, "temp6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_temp}, + {MAXI_SYSCTL_PLL, "pll", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_pll}, + {MAXI_SYSCTL_VID1, "vid1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_vid}, + {MAXI_SYSCTL_VID2, "vid2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_vid}, + {MAXI_SYSCTL_VID3, "vid3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_vid}, + {MAXI_SYSCTL_VID4, "vid4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_vid}, + {MAXI_SYSCTL_VID5, "vid5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_vid}, + {MAXI_SYSCTL_LCD1, "lcd1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_lcd}, + {MAXI_SYSCTL_LCD2, "lcd2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_lcd}, + {MAXI_SYSCTL_LCD3, "lcd3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_lcd}, + {MAXI_SYSCTL_LCD4, "lcd4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_lcd}, + {MAXI_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &maxi_alarms}, + {0} +}; + +/* This function is called when: + - maxi_driver is inserted (when this module is loaded), for each + available adapter + - when a new adapter is inserted (and maxi_driver is still present) */ +int maxi_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, maxi_detect); +} + +/* This function is called by i2c_detect */ +int maxi_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + struct i2c_client *new_client; + struct maxi_data *data; + enum maxi_type type; + int i, j, err = 0; + const char *type_name, *client_name; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access maxi_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct maxi_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + /* Fill the new client structure with data */ + data = (struct maxi_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &maxi_driver; + new_client->flags = 0; + + /* Now we do the remaining detection. */ + if (kind < 0) { + if (i2c_smbus_read_byte_data + (new_client, MAXI_REG_MBX_STATUS) < 0) + goto ERROR2; + } + + /* Determine the chip type - only one kind supported */ + if (kind <= 0) + kind = maxilife; + + if (kind == maxilife) { + /* Detect if the machine has a MaxiLife NBA controller. + The right way to perform this check is to do a read/modify/write + on register MbxStatus (5A): + - Read 5A (value 0 for non-NBA firmware, FF (MbxIdle on NBA-firmware) + - Write 55 on 5A, then read back 5A + Non-NBA firmware: value is 55 (reg 5A is a standard writable reg) + NBA firmaware: value is FF (write-protect on MbxStatus active) */ + int stat; + i2c_smbus_write_byte_data(new_client, MAXI_REG_MBX_STATUS, + 0x55); + stat = + i2c_smbus_read_byte_data(new_client, + MAXI_REG_MBX_STATUS); + + /*if (stat == MAXI_STAT_IDLE || stat == MAXI_STAT_OK) */ + if (stat != 0x55) + maxi_version = nba; +#ifdef AUTODETECT + else { + /* The right way to get the platform info is to read the firmware + revision from serial EEPROM (addr=0x54), at offset 0x0045. + This is a string as: + "CG 00.04" -> Cristal [XU] / Geronimo [XAs] + "CO 00.03" -> Cognac [XU] + "AS 00.01" -> Ashaki [XA] */ +#if 0 + int biosctl; + biosctl = + i2c_smbus_read_byte_data(new_client, + MAXI_REG_BIOS_CTRL); + i2c_smbus_write_byte_data(new_client, + MAXI_REG_BIOS_CTRL, + biosctl | 4); + err = eeprom_read_byte_data(adapter, 0x54, 0x45); + i2c_smbus_write_byte_data(new_client, + MAXI_REG_BIOS_CTRL, + biosctl); +#endif + int i; + char *biosmem, *bm; + bm = biosmem = ioremap(0xe0000, 0x20000); + if (biosmem) { + printk("begin of bios search\n"); + for (i = 0; i < 0x20000; i++) { + if (*bm == 'C') { + char *s = bm; + while (s && isprint(*s)) { + printk("%c", *s); + s++; + } + printk("\n"); + if (!strncmp + (bm, "CG 00.04", 8)) { + maxi_version = + cristal; + printk + ("maxilife: found MaxiLife Rev CG 00.04\n"); + break; + } + if (!strncmp + (bm, "CO 00.03", 8)) { + maxi_version = + cognac; + printk + ("maxilife: found MaxiLife Rev CO 00.03\n"); + break; + } + } + if (*bm == 'A' && *(bm + 1) == 'S') { + char *s = bm; + while (s && isprint(*s)) { + printk("%c", *s); + s++; + } + printk("\n"); + if (!strncmp + (bm, "AS 00.01", 8)) { + maxi_version = + ashaki; + printk + ("maxilife: found MaxiLife Rev AS 00.01\n"); + break; + } + } + bm++; + } + printk("end of bios search\n"); + } else + printk("could not map bios memory\n"); + } +#endif + + if (maxi_version == cristal) { + type = cristal; + type_name = "maxilife-cg"; + client_name = "HP MaxiLife Rev CG 00.04"; + printk + ("maxilife: HP KAYAK XU/XAs (Dual Pentium II Slot 1)\n"); + } else if (maxi_version == cognac) { + type = cognac; + type_name = "maxilife-co"; + client_name = "HP MaxiLife Rev CO 00.03"; + printk + ("maxilife: HP KAYAK XU (Dual Xeon Slot 2 400/450 Mhz)\n"); + } else if (maxi_version == ashaki) { + type = ashaki; + type_name = "maxilife-as"; + client_name = "HP MaxiLife Rev AS 00.01"; + printk + ("maxilife: HP KAYAK XA (Pentium II Slot 1, monoprocessor)\n"); + } else if (maxi_version == nba) { + type = nba; + type_name = "maxilife-nba"; + client_name = "HP MaxiLife NBA"; + printk("maxilife: HP KAYAK XU800/XM600\n"); + } else { +#ifdef AUTODETECT + printk + ("maxilife: Warning: probed non-maxilife chip?!? (%x)\n", + err); +#else + printk + ("maxilife: Error: specified wrong maxi_version (%d)\n", + maxi_version); +#endif + goto ERROR2; + } + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + ((struct maxi_data *) (new_client->data))->type = type; + + for (i = 0; i < 4; i++) + for (j = 0; j < 17; j++) + ((struct maxi_data *) (new_client->data))-> + lcd[i][j] = (u8) 0; + + new_client->id = maxi_id++; + + data->valid = 0; + init_MUTEX(&data->lock); + init_MUTEX(&data->update_lock); + + /* Tell i2c-core that a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR2; + + /* Register a new directory entry with module sensors */ + if ((err = i2c_register_entry(new_client, type_name, + maxi_dir_table_template, + THIS_MODULE)) < 0) + goto ERROR4; + data->sysctl_id = err; + + /* Initialize the MaxiLife chip */ + maxi_init_client(new_client); + return 0; + + /* OK, this is not exactly good programming practice, usually. + But it is very code-efficient in this case. */ + ERROR4: + i2c_detach_client(new_client); + ERROR2: + kfree(new_client); + ERROR0: + return err; +} + +/* This function is called whenever a client should be removed: + - maxi_driver is removed (when this module is unloaded) + - when an adapter is removed which has a maxi client (and maxi_driver + is still present). */ +int maxi_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct maxi_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("maxilife: Client deregistration failed, client not detached.\n"); + return err; + } + kfree(client); + return 0; +} + +/* No commands defined yet */ +int maxi_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +/* Nothing here yet */ +void maxi_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +/* Nothing here yet */ +void maxi_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* Read byte from specified register (-1 in case of error, value otherwise). */ +int maxi_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/* Read the byte value for a MaxiLife token (-1 in case of error, value otherwise */ +int maxi_read_token(struct i2c_client *client, u16 token) +{ + u8 lowToken, highToken; + int error, value; + + lowToken = LOW(token); + highToken = HIGH(token); + + /* Set mailbox status register to idle state. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS, + MAXI_STAT_IDLE); + if (error < 0) + return error; + + /* Check for mailbox idle state. */ + error = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS); + if (error != MAXI_STAT_IDLE) + return -1; + + /* Write the most significant byte of the token we want to read. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_H, + highToken); + if (error < 0) + return error; + + /* Write the least significant byte of the token we want to read. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_L, + lowToken); + if (error < 0) + return error; + + /* Write the read token opcode to the mailbox. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_CMD, + MAXI_CMD_READ); + if (error < 0) + return error; + + /* Check for transaction completion */ + do { + error = + i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS); + } while (error == MAXI_STAT_BUSY); + if (error != MAXI_STAT_OK) + return -1; + + /* Read the value of the token. */ + value = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_DATA); + if (value == -1) + return -1; + + /* set mailbox status to idle to complete transaction. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS, + MAXI_STAT_IDLE); + if (error < 0) + return error; + + return value; +} + +#ifndef NOWRITE +/* Write byte to specified register (-1 in case of error, 0 otherwise). */ +int maxi_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} +#endif + +/* Write a set of len byte values to MaxiLife token (-1 in case of error, 0 otherwise). */ +int maxi_write_token_loop(struct i2c_client *client, u16 token, u8 len, + u8 * values) +{ + u8 lowToken, highToken, bCounter; + int error; + + lowToken = LOW(token); + highToken = HIGH(token); + + /* Set mailbox status register to idle state. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS, + MAXI_STAT_IDLE); + if (error < 0) + return error; + + /* Check for mailbox idle state. */ + error = i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS); + if (error != MAXI_STAT_IDLE) + return -1; + + for (bCounter = 0; (bCounter < len && bCounter < 32); bCounter++) { + error = + i2c_smbus_write_byte_data(client, + (u8) (MAXI_REG_MBX_DATA + + bCounter), + values[bCounter]); + if (error < 0) + return error; + } + + /* Write the most significant byte of the token we want to read. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_H, + highToken); + if (error < 0) + return error; + + /* Write the least significant byte of the token we want to read. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_TOKEN_L, + lowToken); + if (error < 0) + return error; + + /* Write the write token opcode to the mailbox. */ + error = + i2c_smbus_write_byte_data(client, MAXI_REG_MBX_CMD, + MAXI_CMD_WRITE); + if (error < 0) + return error; + + /* Check for transaction completion */ + do { + error = + i2c_smbus_read_byte_data(client, MAXI_REG_MBX_STATUS); + } while (error == MAXI_STAT_BUSY); + if (error != MAXI_STAT_OK) + return -1; + + /* set mailbox status to idle to complete transaction. */ + return i2c_smbus_write_byte_data(client, MAXI_REG_MBX_STATUS, + MAXI_STAT_IDLE); +} + +/* Called when we have found a new MaxiLife. It should set limits, etc. */ +void maxi_init_client(struct i2c_client *client) +{ + struct maxi_data *data = client->data; + + if (data->type == nba) { + strcpy(data->lcd[2], " Linux MaxiLife"); + maxi_write_token_loop(client, MAXI_TOK_LCD(2), + strlen(data->lcd[2]) + 1, + data->lcd[2]); + } +} + +void maxi_update_client(struct i2c_client *client) +{ + struct maxi_data *data = client->data; + int i; + + if (data->type == nba) { + printk + ("maxi_update_client should never be called by nba\n"); + return; + } + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("maxilife: Starting MaxiLife update\n"); +#endif + for (i = 0; i < 5; i++) + data->temp[i] = + maxi_read_value(client, MAXI_REG_TEMP(i)); + switch (data->type) { + case cristal: + data->temp[0] = 0; /* not valid */ + data->temp_max[0] = 0; + data->temp_hyst[0] = 0; + data->temp_max[1] = 110; /* max PCI slot temp */ + data->temp_hyst[1] = 100; + data->temp_max[2] = 120; /* max BX chipset temp */ + data->temp_hyst[2] = 110; + data->temp_max[3] = 100; /* max HDD temp */ + data->temp_hyst[3] = 90; + data->temp_max[4] = 120; /* max CPU temp */ + data->temp_hyst[4] = 110; + break; + + case cognac: + data->temp_max[0] = 120; /* max CPU1 temp */ + data->temp_hyst[0] = 110; + data->temp_max[1] = 110; /* max PCI slot temp */ + data->temp_hyst[1] = 100; + data->temp_max[2] = 120; /* max CPU2 temp */ + data->temp_hyst[2] = 110; + data->temp_max[3] = 100; /* max HDD temp */ + data->temp_hyst[3] = 90; + data->temp_max[4] = 120; /* max reference CPU temp */ + data->temp_hyst[4] = 110; + break; + + case ashaki: + data->temp[0] = 0; /* not valid */ + data->temp_max[0] = 0; + data->temp_hyst[0] = 0; + data->temp_max[1] = 110; /* max PCI slot temp */ + data->temp_hyst[1] = 100; + data->temp[2] = 0; /* not valid */ + data->temp_max[2] = 0; + data->temp_hyst[2] = 0; + data->temp_max[3] = 100; /* max HDD temp */ + data->temp_hyst[3] = 90; + data->temp_max[4] = 120; /* max CPU temp */ + data->temp_hyst[4] = 110; + break; + + default: + printk("maxilife: Unknown MaxiLife chip\n"); + } + data->temp[5] = 0; /* only used by MaxiLife'99 */ + data->temp_max[5] = 0; + data->temp_hyst[5] = 0; + + for (i = 0; i < 3; i++) { + data->fan[i] = + maxi_read_value(client, MAXI_REG_FAN(i)); + data->fan_speed[i] = + maxi_read_value(client, MAXI_REG_FAN_SPEED(i)); + data->fan_div[i] = 4; + if (data->type == ashaki) + data->fan_min[i] = + maxi_read_value(client, + MAXI_REG_FAN_MINAS(i)); + else + data->fan_min[i] = + maxi_read_value(client, + MAXI_REG_FAN_MIN(i)); + } + data->fan[3] = 0xff; /* only used by MaxiLife'99 */ + data->fan_speed[3] = 0; + data->fan_div[3] = 4; /* avoid possible /0 */ + data->fan_min[3] = 0; + + data->pll = maxi_read_value(client, MAXI_REG_PLL); + data->pll_min = maxi_read_value(client, MAXI_REG_PLL_MIN); + data->pll_max = maxi_read_value(client, MAXI_REG_PLL_MAX); + + for (i = 0; i < 4; i++) { + data->vid[i] = + maxi_read_value(client, MAXI_REG_VID(i)); + data->vid_min[i] = + maxi_read_value(client, MAXI_REG_VID_MIN(i)); + data->vid_max[i] = + maxi_read_value(client, MAXI_REG_VID_MAX(i)); + } + switch (data->type) { + case cristal: + data->vid[3] = 0; /* no voltage cache L2 */ + data->vid_min[3] = 0; + data->vid_max[3] = 0; + break; + + case cognac: + break; + + case ashaki: + data->vid[1] = 0; /* no voltage CPU 2 */ + data->vid_min[1] = 0; + data->vid_max[1] = 0; + data->vid[3] = 0; /* no voltage cache L2 */ + data->vid_min[3] = 0; + data->vid_max[3] = 0; + break; + + default: + printk("maxilife: Unknown MaxiLife chip\n"); + } + data->vid[4] = 0; /* only used by MaxliLife'99 */ + data->vid_min[4] = 0; + data->vid_max[4] = 0; + + data->alarms = maxi_read_value(client, MAXI_REG_DIAG_RT1) + + (maxi_read_value(client, MAXI_REG_DIAG_RT2) << 8); + + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + +void maxi99_update_client(struct i2c_client *client, + enum sensor_type sensor, int which) +{ + static unsigned long last_updated[6][6]; /* sensor, which */ + struct maxi_data *data = client->data; + + down(&data->update_lock); + + /*maxi_write_token_loop(client, MAXI_TOK_LCD_LINE3, 13, "Linux 2.2.13"); */ + + if ((jiffies - last_updated[sensor][which] > 2 * HZ) || + (jiffies < last_updated[sensor][which] + || !last_updated[sensor][which])) { + + int tmp, i; + + switch (sensor) { + case fan: + for (i = 0; i < 4; i++) { + if (i == which) { + tmp = + maxi_read_token(client, + MAXI_TOK_FAN + (i)); + data->fan[i] = + maxi_read_token(client, + MAXI_TOK_FAN + (i)); + data->fan_speed[i] = + maxi_read_token(client, + MAXI_TOK_MAX + (MAXI_TOK_FAN + (i))); + data->fan_div[i] = 1; + data->fan_min[i] = 0; + } + } + break; + + case temp: + for (i = 0; i < 6; i++) { + if (i == which) { + data->temp[i] = + maxi_read_token(client, + MAXI_TOK_TEMP + (i)); + data->temp_max[i] = + maxi_read_token(client, + MAXI_TOK_MAX + (MAXI_TOK_TEMP + (i))); + data->temp_hyst[i] = + data->temp_max[i] - 5; + } + } + break; + + case vid: + for (i = 0; i < 5; i++) { + if (i == which) { + data->vid[i] = + maxi_read_token(client, + MAXI_TOK_VID + (i)); + data->vid_min[i] = + maxi_read_token(client, + MAXI_TOK_MIN + (MAXI_TOK_VID + (i))); + data->vid_max[i] = + maxi_read_token(client, + MAXI_TOK_MAX + (MAXI_TOK_VID + (i))); + } + } + break; + + case pll: + data->pll = 0; + data->pll_min = 0; + data->pll_max = 0; + break; + + case alarm: + data->alarms = + (maxi_read_token(client, MAXI_TOK_ALARM_EVENT) + << 8); + if (data->alarms) + data->alarms += + data->alarms == + (1 << 8) ? maxi_read_token(client, + MAXI_TOK_ALARM_FAN) + : data->alarms == + (2 << 8) ? maxi_read_token(client, + MAXI_TOK_ALARM_VID) + : data->alarms == + (4 << 8) ? maxi_read_token(client, + MAXI_TOK_ALARM_TEMP) + : data->alarms == + (8 << 8) ? maxi_read_token(client, + MAXI_TOK_ALARM_FAN) + : 0; + break; + + default: + printk("maxilife: Unknown sensor type\n"); + } + + last_updated[sensor][which] = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the data + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void maxi_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr; + + if (data->type == nba) { + maxi99_fan(client, operation, ctl_name, nrels_mag, + results); + return; + } + + nr = ctl_name - MAXI_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1]); + results[1] = data->fan_div[nr - 1]; + results[2] = FAN_FROM_REG(data->fan[nr - 1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } +#endif + } +} + +void maxi99_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr; + + nr = ctl_name - MAXI_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi99_update_client(client, fan, nr - 1); + results[0] = FAN99_FROM_REG(data->fan_min[nr - 1]); /* min rpm */ + results[1] = data->fan_div[nr - 1]; /* divisor */ + results[2] = FAN99_FROM_REG(data->fan[nr - 1]); /* rpm */ + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + /* still to do */ + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } +#endif + } +} + +void maxi_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr; + + if (data->type == nba) { + maxi99_temp(client, operation, ctl_name, nrels_mag, + results); + return; + } + + nr = ctl_name - MAXI_SYSCTL_TEMP1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_max[nr - 1]); + results[1] = TEMP_FROM_REG(data->temp_hyst[nr - 1]); + results[2] = TEMP_FROM_REG(data->temp[nr - 1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + /* temperature range can not be changed */ + } +} + +void maxi99_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr; + + nr = ctl_name - MAXI_SYSCTL_TEMP1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi99_update_client(client, temp, nr - 1); + results[0] = TEMP99_FROM_REG(data->temp_max[nr - 1]); + results[1] = TEMP99_FROM_REG(data->temp_hyst[nr - 1]); + results[2] = TEMP99_FROM_REG(data->temp[nr - 1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + /* temperature range can not be changed */ + } +} + +void maxi_pll(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + if (data->type == nba) + maxi99_update_client(client, pll, 0); + else + maxi_update_client(client); + results[0] = PLL_FROM_REG(data->pll_min); + results[1] = PLL_FROM_REG(data->pll_max); + results[2] = PLL_FROM_REG(data->pll); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + if (*nrels_mag >= 1) { + data->pll_min = PLL_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_PLL_MIN, + data->pll_min); + } + if (*nrels_mag >= 2) { + data->pll_max = PLL_TO_REG(results[1]); + maxi_write_value(client, MAXI_REG_PLL_MAX, + data->pll_max); + } +#endif + } +} + +void maxi_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr; + + if (data->type == nba) { + maxi99_vid(client, operation, ctl_name, nrels_mag, + results); + return; + } + + nr = ctl_name - MAXI_SYSCTL_VID1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 4; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi_update_client(client); + results[0] = VID_FROM_REG(data->vid_min[nr - 1]); + results[1] = VID_FROM_REG(data->vid_max[nr - 1]); + results[2] = VID_FROM_REG(data->vid[nr - 1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + if (*nrels_mag >= 1) { + data->vid_min[nr - 1] = VID_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_VID_MIN(nr), + data->vid_min[nr - 1]); + } + if (*nrels_mag >= 2) { + data->vid_max[nr - 1] = VID_TO_REG(results[1]); + maxi_write_value(client, MAXI_REG_VID_MAX(nr), + data->vid_max[nr - 1]); + } +#endif + } +} + +void maxi99_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + int nr = ctl_name - MAXI_SYSCTL_VID1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 4; + else if (operation == SENSORS_PROC_REAL_READ) { + maxi99_update_client(client, vid, nr - 1); + results[0] = VID99_FROM_REG(nr, data->vid_min[nr - 1]); + results[1] = VID99_FROM_REG(nr, data->vid_max[nr - 1]); + results[2] = VID99_FROM_REG(nr, data->vid[nr - 1]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { +#ifndef NOWRITE + /* still to do */ + if (*nrels_mag >= 1) { + data->vid_min[nr - 1] = VID_TO_REG(results[0]); + maxi_write_value(client, MAXI_REG_VID_MIN(nr), + data->vid_min[nr - 1]); + } + if (*nrels_mag >= 2) { + data->vid_max[nr - 1] = VID_TO_REG(results[1]); + maxi_write_value(client, MAXI_REG_VID_MAX(nr), + data->vid_max[nr - 1]); + } +#endif + } +} + +void maxi_lcd(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + /* Allows writing and reading from LCD display */ + + struct maxi_data *data = client->data; + int nr; + + if (data->type != nba) + return; + + nr = ctl_name - MAXI_SYSCTL_LCD1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = *((long *) &data->lcd[nr - 1][0]); + results[1] = *((long *) &data->lcd[nr - 1][4]); + results[2] = *((long *) &data->lcd[nr - 1][8]); + results[3] = *((long *) &data->lcd[nr - 1][12]); + *nrels_mag = 4; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + /* + Writing a string to line 3 of the LCD can be done like: + echo -n "Linux MaxiLife" | od -A n -l > \ + /proc/sys/dev/sensors/maxilife-nba-i2c-0-14/lcd3 + */ + if (*nrels_mag >= 1) + *((long *) &data->lcd[nr - 1][0]) = results[0]; + if (*nrels_mag >= 2) + *((long *) &data->lcd[nr - 1][4]) = results[1]; + if (*nrels_mag >= 3) + *((long *) &data->lcd[nr - 1][8]) = results[2]; + if (*nrels_mag >= 4) + *((long *) &data->lcd[nr - 1][12]) = results[3]; + maxi_write_token_loop(client, MAXI_TOK_LCD(nr - 1), + strlen(data->lcd[nr - 1]) + 1, + data->lcd[nr - 1]); +#if 0 + if (*nrels_mag >= 1) + printk("nr=%d, result[0] = %.4s\n", nr, + (char *) &results[0]); + if (*nrels_mag >= 2) + printk("nr=%d, result[1] = %.4s\n", nr, + (char *) &results[1]); + if (*nrels_mag >= 3) + printk("nr=%d, result[2] = %.4s\n", nr, + (char *) &results[2]); + if (*nrels_mag >= 4) + printk("nr=%d, result[3] = %.4s\n", nr, + (char *) &results[3]); +#endif + } + +} + +void maxi_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct maxi_data *data = client->data; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + if (data->type == nba) + maxi99_update_client(client, alarm, 0); + else + maxi_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +int __init sensors_maxi_init(void) +{ + int res; + + printk("maxilife: Version %s (lm_sensors %s (%s))\n", version_str, + LM_VERSION, LM_DATE); + maxi_initialized = 0; + + if ((res = i2c_add_driver(&maxi_driver))) { + printk + ("maxilife: Driver registration failed, module not inserted.\n"); + maxi_cleanup(); + return res; + } + maxi_initialized++; + return 0; +} + +int __init maxi_cleanup(void) +{ + int res; + + if (maxi_initialized >= 1) { + if ((res = i2c_del_driver(&maxi_driver))) { + printk + ("maxilife: Driver deregistration failed, module not removed.\n"); + return res; + } + maxi_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Fons Rademakers "); +MODULE_DESCRIPTION("HP MaxiLife driver"); +MODULE_PARM(maxi_version, "i"); +MODULE_PARM_DESC(maxi_version, "MaxiLife firmware version"); + +int init_module(void) +{ + return sensors_maxi_init(); +} + +int cleanup_module(void) +{ + return maxi_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/mtp008.c Mon Mar 25 15:28:40 CET 2002 +++ linux/drivers/sensors/mtp008.c Mon Mar 25 15:28:40 CET 2002 @@ -0,0 +1,1248 @@ +/* + mtp008.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 2001 Kris Van Hees + + 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 +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = {SENSORS_I2C_END}; +static unsigned short normal_i2c_range[] = {0x2c, 0x2e, SENSORS_I2C_END}; +static unsigned int normal_isa[] = {SENSORS_ISA_END}; +static unsigned int normal_isa_range[] = {SENSORS_ISA_END}; + +/* Insmod parameters */ +SENSORS_INSMOD_1(mtp008); + +/* The MTP008 registers */ +/* in0 .. in6 */ +#define MTP008_REG_IN(nr) (0x20 + (nr)) +#define MTP008_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define MTP008_REG_IN_MIN(nr) (0x2c + (nr) * 2) + +/* temp1 */ +#define MTP008_REG_TEMP 0x27 +#define MTP008_REG_TEMP_MAX 0x39 +#define MTP008_REG_TEMP_MIN 0x3a + +/* fan1 .. fan3 */ +#define MTP008_REG_FAN(nr) (0x27 + (nr)) +#define MTP008_REG_FAN_MIN(nr) (0x3a + (nr)) + +#define MTP008_REG_CONFIG 0x40 +#define MTP008_REG_INT_STAT1 0x41 +#define MTP008_REG_INT_STAT2 0x42 + +#define MTP008_REG_SMI_MASK1 0x43 +#define MTP008_REG_SMI_MASK2 0x44 + +#define MTP008_REG_NMI_MASK1 0x45 +#define MTP008_REG_NMI_MASK2 0x46 + +#define MTP008_REG_VID_FANDIV 0x47 + +#define MTP008_REG_I2C_ADDR 0x48 + +#define MTP008_REG_RESET_VID4 0x49 + +#define MTP008_REG_OVT_PROP 0x50 + +#define MTP008_REG_BEEP_CTRL1 0x51 +#define MTP008_REG_BEEP_CTRL2 0x52 + +/* pwm1 .. pwm3 nr range 1-3 */ +#define MTP008_REG_PWM_CTRL(nr) (0x52 + (nr)) + +#define MTP008_REG_PIN_CTRL1 0x56 +#define MTP008_REG_PIN_CTRL2 0x57 + +#define MTP008_REG_CHIPID 0x58 + +/* + * Pin control register configuration constants. + */ +#define MTP008_CFG_VT1_PII 0x08 +#define MTP008_CFG_VT2_AIN 0x00 +#define MTP008_CFG_VT2_VT 0x03 +#define MTP008_CFG_VT2_PII 0x04 +#define MTP008_CFG_VT2_MASK 0x06 +#define MTP008_CFG_VT3_VT 0x01 + +/* sensor pin types */ +#define VOLTAGE 1 +#define THERMISTOR 2 +#define PIIDIODE 3 + +/* + * Conversion routines and macros. Rounding and limit checking is only done on + * the TO_REG variants. + * + * Note that IN values are expressed as 100 times the actual voltage to avoid + * having to use floating point values. As such, IN values are between 0 and + * 409 (0V to 4.096V). + */ +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8) / 16), 0, 255)) +#define IN_FROM_REG(val) (((val) * 16) / 10) + +/* + * The fan cotation count (as stored in the register) is calculated using the + * following formula: + * count = (22.5K * 60) / (rpm * div) = 1350000 / (rpm * div) + * and the rpm is therefore: + * rpm = 1350000 / (count * div) + */ +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + + return SENSORS_LIMIT( + (1350000 + rpm * div / 2) / (rpm * div), + 1, 254 + ); +} + +#define FAN_FROM_REG(val, div) ((val) == 0 ? -1 \ + : (val) == 255 ? 0 \ + : 1350000 / \ + ((val) * (div)) \ + ) + +/* + * Temperatures are stored as two's complement values of the Celsius value. It + * actually uses 10 times the Celsius value to avoid using floating point + * values. + */ +#define TEMP_TO_REG(val) ( \ + (val) < 0 \ + ? SENSORS_LIMIT(((val) - 5) / 10, 0, 255) \ + : SENSORS_LIMIT(((val) + 5) / 10, 0, 255) \ + ) +#define TEMP_FROM_REG(val) ( \ + ( \ + (val) > 0x80 ? (val) - 0x100 \ + : (val) \ + ) * 10 \ + ) + +/* + * VCORE voltage: + * 0x00 to 0x0f = 2.05 to 1.30 (0.05 per unit) + * 0x10 to 0x1e = 3.50 to 2.10 (0.10 per unit) + * 0x1f = No CPU + */ +#define VID_FROM_REG(val) ((val) == 0x1f \ + ? 0 \ + : (val) < 0x10 ? 205 - (val) * 5 \ + : 510 - (val) * 10) + +/* + * Fan divider. + */ +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val) == 8 ? 3 \ + : (val) == 4 ? 2 \ + : (val) == 2 ? 1 \ + : 0) + +/* + * Alarms (interrupt status). + */ +#define ALARMS_FROM_REG(val) (val) + +/* + * Beep controls. + */ +#define BEEPS_FROM_REG(val) (val) +#define BEEPS_TO_REG(val) (val) + +/* + * PWM control. nr range 1 to 3 + */ +#define PWM_FROM_REG(val) (val) +#define PWM_TO_REG(val) (val) +#define PWMENABLE_FROM_REG(nr, val) (((val) >> ((nr) + 3)) & 1) + +/* Initial limits */ +#define MTP008_INIT_IN_0 (vid) /* VCore 1 */ +#define MTP008_INIT_IN_1 330 /* +3.3V */ +#define MTP008_INIT_IN_2 (1200 * 10 / 38) /* +12V */ +#define MTP008_INIT_IN_3 (vid) /* VCore 2 */ +#define MTP008_INIT_IN_5 ((11861 + 7 * (-1200)) / 36) /* -12V */ +#define MTP008_INIT_IN_6 150 /* Vtt */ + +#define MTP008_INIT_IN_PCT 10 + +#define MTP008_INIT_IN_MIN_0 (MTP008_INIT_IN_0 - \ + MTP008_INIT_IN_0 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MAX_0 (MTP008_INIT_IN_0 + \ + MTP008_INIT_IN_0 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MIN_1 (MTP008_INIT_IN_1 - \ + MTP008_INIT_IN_1 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MAX_1 (MTP008_INIT_IN_1 + \ + MTP008_INIT_IN_1 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MIN_2 (MTP008_INIT_IN_2 - \ + MTP008_INIT_IN_2 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MAX_2 (MTP008_INIT_IN_2 + \ + MTP008_INIT_IN_2 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MIN_3 (MTP008_INIT_IN_3 - \ + MTP008_INIT_IN_3 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MAX_3 (MTP008_INIT_IN_3 + \ + MTP008_INIT_IN_3 * MTP008_INIT_IN_PCT / 100) + +#define MTP008_INIT_IN_MIN_5 (MTP008_INIT_IN_5 - \ + MTP008_INIT_IN_5 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MAX_5 (MTP008_INIT_IN_5 + \ + MTP008_INIT_IN_5 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MIN_6 (MTP008_INIT_IN_6 - \ + MTP008_INIT_IN_6 * MTP008_INIT_IN_PCT / 100) +#define MTP008_INIT_IN_MAX_6 (MTP008_INIT_IN_6 + \ + MTP008_INIT_IN_6 * MTP008_INIT_IN_PCT / 100) + +#define MTP008_INIT_FAN_MIN_1 3000 +#define MTP008_INIT_FAN_MIN_2 3000 +#define MTP008_INIT_FAN_MIN_3 3000 + +#define MTP008_INIT_TEMP_OVER 700 /* 70 Celsius */ +#define MTP008_INIT_TEMP_HYST 500 /* 50 Celsius */ +#define MTP008_INIT_TEMP2_OVER 700 /* 70 Celsius */ +#define MTP008_INIT_TEMP2_HYST 500 /* 50 Celsius */ + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* + * For each registered MTP008, we need to keep some data in memory. The + * structure itself is dynamically allocated, at the same time when a new + * mtp008 client is allocated. + */ +struct mtp008_data { + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[7]; /* Register value */ + u8 in_max[7]; /* Register value */ + u8 in_min[7]; /* Register value */ + u8 temp; /* Register value */ + u8 temp_max; /* Register value */ + u8 temp_min; /* Register value */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 vid; /* Register encoding */ + u8 fan_div[3]; /* Register encoding */ + u16 alarms; /* Register encoding */ + u16 beeps; /* Register encoding */ + u8 pwm[4]; /* Register value */ + u8 sens[3]; /* 1 = Analog input, + 2 = Thermistor, + 3 = PII/Celeron diode */ + u8 pwmenable; /* Register 0x57 value */ +}; + +#ifdef MODULE +static int __init sensors_mtp008_init(void); +#else +extern int __init sensors_mtp008_init(void); +#endif +static int __init mtp008_cleanup(void); + +static int mtp008_attach_adapter(struct i2c_adapter *adapter); +static int mtp008_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int mtp008_detach_client(struct i2c_client *client); +static int mtp008_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void mtp008_inc_use(struct i2c_client *client); +static void mtp008_dec_use(struct i2c_client *client); + +static int mtp008_read_value(struct i2c_client *client, u8 register); +static int mtp008_write_value(struct i2c_client *client, u8 register, u8 value); +static void mtp008_update_client(struct i2c_client *client); +static void mtp008_init_client(struct i2c_client *client); + +static void mtp008_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_temp_add(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_beep(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_pwm(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_sens(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void mtp008_getsensortype(struct mtp008_data *data, u8 inp); + +static int mtp008_id = 0; + +static struct i2c_driver mtp008_driver = +{ + /* name */ "MTP008 sensor driver", + /* id */ I2C_DRIVERID_MTP008, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &mtp008_attach_adapter, + /* detach_client */ &mtp008_detach_client, + /* command */ &mtp008_command, + /* inc_use */ &mtp008_inc_use, + /* dec_use */ &mtp008_dec_use +}; + +/* Used by mtp008_init/cleanup */ +static int __initdata mtp008_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected chip. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ + +static ctl_table mtp008_dir_table_template[] = +{ + {MTP008_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_in}, + {MTP008_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan}, + {MTP008_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan}, + {MTP008_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan}, + {MTP008_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp}, + {MTP008_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp_add}, + {MTP008_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_temp_add}, + {MTP008_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_vid}, + {MTP008_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_fan_div}, + {MTP008_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_alarms}, + {MTP008_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_beep}, + {MTP008_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm}, + {MTP008_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm}, + {MTP008_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_pwm}, + {MTP008_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens}, + {MTP008_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens}, + {MTP008_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &mtp008_sens}, + {0} +}; + +/* This function is called when: + * mtp008_driver is inserted (when this module is loaded), for each available + * adapter when a new adapter is inserted (and mtp008_driver is still present) + */ +int mtp008_attach_adapter(struct i2c_adapter *adapter) +{ + struct i2c_client_address_data mtp008_addr_data; + + mtp008_addr_data.normal_i2c = addr_data.normal_i2c; + mtp008_addr_data.normal_i2c_range = addr_data.normal_i2c_range; + mtp008_addr_data.probe = addr_data.probe; + mtp008_addr_data.probe_range = addr_data.probe_range; + mtp008_addr_data.ignore = addr_data.ignore; + mtp008_addr_data.ignore_range = addr_data.ignore_range; + mtp008_addr_data.force = addr_data.forces->force; + + return i2c_probe(adapter, &mtp008_addr_data, mtp008_detect); +} + +int mtp008_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + const char *type_name = ""; + const char *client_name = ""; + int is_isa, err, sysid; + struct i2c_client *new_client; + struct mtp008_data *data; + + err = 0; + + is_isa = i2c_is_isa_adapter(adapter); + if (is_isa || + !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* + * 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 use mtp008_(read|write)_value(). + */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct mtp008_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + data = (struct mtp008_data *) (new_client + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &mtp008_driver; + new_client->flags = 0; + + /* + * Remaining detection. + */ + if (kind < 0) { + if (mtp008_read_value(new_client, MTP008_REG_CHIPID) != 0xac) + goto ERROR1; + } + /* + * Fill in the remaining client fields and put it into the global list. + */ + type_name = "mtp008"; + client_name = "MTP008 chip"; + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = mtp008_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* + * Tell the I2C layer that a new client has arrived. + */ + if ((err = i2c_attach_client(new_client))) + goto ERROR1; + + /* + * Register a new directory entry with the sensors module. + */ + if ((sysid = i2c_register_entry(new_client, type_name, + mtp008_dir_table_template, + THIS_MODULE)) < 0) { + err = sysid; + goto ERROR2; + } + data->sysctl_id = sysid; + + /* + * Initialize the MTP008 chip. + */ + mtp008_init_client(new_client); + + return 0; + + /* + * Error handling. Bad programming practise but very code efficient. + */ + ERROR2: + i2c_detach_client(new_client); + ERROR1: + kfree(new_client); + + ERROR0: + return err; +} + +int mtp008_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry( + ((struct mtp008_data *) (client->data))->sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk("mtp008.o: Deregistration failed, " + "client not detached.\n"); + return err; + } + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int mtp008_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void mtp008_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void mtp008_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +int mtp008_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg) & 0xff; +} + +int mtp008_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +/* Called when we have found a new MTP008. It should set limits, etc. */ +void mtp008_init_client(struct i2c_client *client) +{ + int vid; + u8 save1, save2; + struct mtp008_data *data; + + data = client->data; + + /* + * Initialize the Myson MTP008 hardware monitoring chip. + * Save the pin settings that the BIOS hopefully set. + */ + save1 = mtp008_read_value(client, MTP008_REG_PIN_CTRL1); + save2 = mtp008_read_value(client, MTP008_REG_PIN_CTRL2); + mtp008_write_value(client, MTP008_REG_CONFIG, + (mtp008_read_value(client, MTP008_REG_CONFIG) & 0x7f) | 0x80); + mtp008_write_value(client, MTP008_REG_PIN_CTRL1, save1); + mtp008_write_value(client, MTP008_REG_PIN_CTRL2, save2); + + mtp008_getsensortype(data, save2); + + /* + * Retrieve the VID setting (needed for the default limits). + */ + vid = mtp008_read_value(client, MTP008_REG_VID_FANDIV) & 0x0f; + vid |= (mtp008_read_value(client, MTP008_REG_RESET_VID4) & 0x01) << 4; + vid = VID_FROM_REG(vid); + + /* + * Set the default limits. + * + * Setting temp sensors is done as follows: + * + * Register 0x57: 0 0 0 0 x x x x + * | \ / +-- AIN5/VT3 + * | +----- AIN4/VT2/PII + * +-------- VT1/PII + */ + + mtp008_write_value(client, MTP008_REG_IN_MAX(0), + IN_TO_REG(MTP008_INIT_IN_MAX_0)); + mtp008_write_value(client, MTP008_REG_IN_MIN(0), + IN_TO_REG(MTP008_INIT_IN_MIN_0)); + mtp008_write_value(client, MTP008_REG_IN_MAX(1), + IN_TO_REG(MTP008_INIT_IN_MAX_1)); + mtp008_write_value(client, MTP008_REG_IN_MIN(1), + IN_TO_REG(MTP008_INIT_IN_MIN_1)); + mtp008_write_value(client, MTP008_REG_IN_MAX(2), + IN_TO_REG(MTP008_INIT_IN_MAX_2)); + mtp008_write_value(client, MTP008_REG_IN_MIN(2), + IN_TO_REG(MTP008_INIT_IN_MIN_2)); + mtp008_write_value(client, MTP008_REG_IN_MAX(3), + IN_TO_REG(MTP008_INIT_IN_MAX_3)); + mtp008_write_value(client, MTP008_REG_IN_MIN(3), + IN_TO_REG(MTP008_INIT_IN_MIN_3)); + + mtp008_write_value(client, MTP008_REG_IN_MAX(5), + IN_TO_REG(MTP008_INIT_IN_MAX_5)); + mtp008_write_value(client, MTP008_REG_IN_MIN(5), + IN_TO_REG(MTP008_INIT_IN_MIN_5)); + mtp008_write_value(client, MTP008_REG_IN_MAX(6), + IN_TO_REG(MTP008_INIT_IN_MAX_6)); + mtp008_write_value(client, MTP008_REG_IN_MIN(6), + IN_TO_REG(MTP008_INIT_IN_MIN_6)); + + mtp008_write_value(client, MTP008_REG_TEMP_MAX, + TEMP_TO_REG(MTP008_INIT_TEMP_OVER)); + mtp008_write_value(client, MTP008_REG_TEMP_MIN, + TEMP_TO_REG(MTP008_INIT_TEMP_HYST)); + mtp008_write_value(client, MTP008_REG_IN_MAX(4), + TEMP_TO_REG(MTP008_INIT_TEMP2_OVER)); + mtp008_write_value(client, MTP008_REG_IN_MIN(4), + TEMP_TO_REG(MTP008_INIT_TEMP2_HYST)); + + mtp008_write_value(client, MTP008_REG_FAN_MIN(1), + FAN_TO_REG(MTP008_INIT_FAN_MIN_1, 2)); + mtp008_write_value(client, MTP008_REG_FAN_MIN(2), + FAN_TO_REG(MTP008_INIT_FAN_MIN_2, 2)); + mtp008_write_value(client, MTP008_REG_FAN_MIN(3), + FAN_TO_REG(MTP008_INIT_FAN_MIN_3, 2)); + + /* + * Start monitoring. + */ + mtp008_write_value( + client, MTP008_REG_CONFIG, + (mtp008_read_value(client, MTP008_REG_CONFIG) & 0xf7) | 0x01 + ); +} + +void mtp008_update_client(struct i2c_client *client) +{ + int i; + u8 inp; + struct mtp008_data *data; + + data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { +#ifdef DEBUG + printk("Starting MTP008 update\n"); +#endif + + /* + * Read in the analog inputs. We're reading AIN4 and AIN5 as + * regular analog inputs, even though they may have been + * configured as temperature readings instead. Interpretation + * of these values is done elsewhere. + */ + for (i = 0; i < 7; i++) { + data->in[i] = + mtp008_read_value(client, MTP008_REG_IN(i)); + data->in_max[i] = + mtp008_read_value(client, MTP008_REG_IN_MAX(i)); + data->in_min[i] = + mtp008_read_value(client, MTP008_REG_IN_MIN(i)); + } + + /* + * Read the temperature sensor. + */ + data->temp = mtp008_read_value(client, MTP008_REG_TEMP); + data->temp_max = mtp008_read_value(client, MTP008_REG_TEMP_MAX); + data->temp_min = mtp008_read_value(client, MTP008_REG_TEMP_MIN); + + /* + * Read the first 2 fan dividers and the VID setting. Read the + * third fan divider from a different register. + */ + inp = mtp008_read_value(client, MTP008_REG_VID_FANDIV); + data->vid = inp & 0x0f; + data->vid |= (mtp008_read_value(client, + MTP008_REG_RESET_VID4) & 0x01) << 4; + + data->fan_div[0] = (inp >> 4) & 0x03; + data->fan_div[1] = inp >> 6; + data->fan_div[2] = + mtp008_read_value(client, MTP008_REG_PIN_CTRL1) >> 6; + + /* + * Read the interrupt status registers. + */ + data->alarms = + (mtp008_read_value(client, + MTP008_REG_INT_STAT1) & 0xdf) | + (mtp008_read_value(client, + MTP008_REG_INT_STAT2) & 0x0f) << 8; + + /* + * Read the beep control registers. + */ + data->beeps = + (mtp008_read_value(client, + MTP008_REG_BEEP_CTRL1) & 0xdf) | + (mtp008_read_value(client, + MTP008_REG_BEEP_CTRL2) & 0x8f) << 8; + + /* + * Read the sensor configuration. + */ + inp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2); + mtp008_getsensortype(data, inp); + + /* + * Read the PWM registers if enabled. + */ + for (i = 1; i <= 3; i++) + { + if(PWMENABLE_FROM_REG(i, inp)) + data->pwm[i-1] = mtp008_read_value(client, + MTP008_REG_PWM_CTRL(i)); + else + data->pwm[i-1] = 255; + } + + /* + * Read the fan sensors. Skip 3 if PWM3 enabled. + */ + for (i = 1; i <= 3; i++) { + if(i == 3 && PWMENABLE_FROM_REG(3, inp)) { + data->fan[2] = 0; + data->fan_min[2] = 0; + } else { + data->fan[i-1] = mtp008_read_value(client, + MTP008_REG_FAN(i)); + data->fan_min[i-1] = mtp008_read_value(client, + MTP008_REG_FAN_MIN(i)); + } + } + + data->last_updated = jiffies; + data->valid = 1; + } + up(&data->update_lock); +} + +void mtp008_getsensortype(struct mtp008_data *data, u8 inp) +{ + inp &= 0x0f; + data->sens[0] = (inp >> 3) + 2; /* 2 or 3 */ + data->sens[1] = ((inp >> 1) & 0x03) + 1; /* 1, 2 or 3 */ + data->sens[2] = (inp & 0x01) + 1; /* 1 or 2 */ +} + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void mtp008_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int nr; + struct mtp008_data *data; + + nr = ctl_name - MTP008_SYSCTL_IN0; + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 2; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + if((nr != 4 && nr != 5) || data->sens[nr - 3] == VOLTAGE) { + results[0] = IN_FROM_REG(data->in_min[nr]); + results[1] = IN_FROM_REG(data->in_max[nr]); + results[2] = IN_FROM_REG(data->in[nr]); + } else { + results[0] = 0; + results[1] = 0; + results[2] = 0; + } + + *nrels_mag = 3; + + break; + case SENSORS_PROC_REAL_WRITE: + if((nr != 4 && nr != 5) || data->sens[nr - 3] == VOLTAGE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0]); + mtp008_write_value(client, MTP008_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1]); + mtp008_write_value(client, MTP008_REG_IN_MAX(nr), + data->in_max[nr]); + } + } + } +} + +void mtp008_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int nr; + struct mtp008_data *data; + + nr = ctl_name - MTP008_SYSCTL_FAN1; + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 0; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = FAN_FROM_REG(data->fan_min[nr], + DIV_FROM_REG(data->fan_div[nr])); + results[1] = FAN_FROM_REG(data->fan[nr], + DIV_FROM_REG(data->fan_div[nr])); + + *nrels_mag = 2; + + break; + case SENSORS_PROC_REAL_WRITE: + if (*nrels_mag >= 1) { + data->fan_min[nr] = + FAN_TO_REG(results[0], + DIV_FROM_REG(data->fan_div[nr])); + mtp008_write_value(client, MTP008_REG_FAN_MIN(nr + 1), + data->fan_min[nr]); + } + } +} + +void mtp008_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct mtp008_data *data; + + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 1; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = TEMP_FROM_REG(data->temp_max); + results[1] = TEMP_FROM_REG(data->temp_min); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + + break; + case SENSORS_PROC_REAL_WRITE: + if (*nrels_mag >= 1) { + data->temp_max = TEMP_TO_REG(results[0]); + mtp008_write_value(client, MTP008_REG_TEMP_MAX, + data->temp_max); + } + if (*nrels_mag >= 2) { + data->temp_min = TEMP_TO_REG(results[1]); + mtp008_write_value(client, MTP008_REG_TEMP_MIN, + data->temp_min); + } + } +} + +void mtp008_temp_add(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int nr; + struct mtp008_data *data; + + nr = 3 + ctl_name - MTP008_SYSCTL_TEMP1; /* AIN4 or AIN5 */ + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 1; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + if(data->sens[nr - 3] != VOLTAGE) { + results[0] = TEMP_FROM_REG(data->in_max[nr]); + results[1] = TEMP_FROM_REG(data->in_min[nr]); + results[2] = TEMP_FROM_REG(data->in[nr]); + } else { + results[0] = 0; + results[1] = 0; + results[2] = 0; + } + *nrels_mag = 3; + + break; + case SENSORS_PROC_REAL_WRITE: + if(data->sens[nr - 3] != VOLTAGE) { + if (*nrels_mag >= 1) { + data->in_max[nr] = TEMP_TO_REG(results[0]); + mtp008_write_value(client, MTP008_REG_TEMP_MAX, + data->in_max[nr]); + } + if (*nrels_mag >= 2) { + data->in_min[nr] = TEMP_TO_REG(results[1]); + mtp008_write_value(client, MTP008_REG_TEMP_MIN, + data->in_min[nr]); + } + } + } +} + +void mtp008_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct mtp008_data *data; + + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 2; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = VID_FROM_REG(data->vid); + + *nrels_mag = 1; + } +} + +void mtp008_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct mtp008_data *data; + u8 val; + + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 0; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + results[2] = DIV_FROM_REG(data->fan_div[2]); + + *nrels_mag = 3; + + break; + case SENSORS_PROC_REAL_WRITE: + if (*nrels_mag >= 3) { + data->fan_div[2] = DIV_TO_REG(results[2]); + val = mtp008_read_value(client, MTP008_REG_PIN_CTRL1); + val = (val & 0x3f) | (data->fan_div[2] & 0x03) << 6; + mtp008_write_value(client, MTP008_REG_PIN_CTRL1, val); + } + if (*nrels_mag >= 1) { + val = mtp008_read_value(client, MTP008_REG_VID_FANDIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + val = (val & 0x3f) | + (data->fan_div[1] & 0x03) << 6; + } + data->fan_div[0] = DIV_TO_REG(results[0]); + val = (val & 0xcf) | (data->fan_div[0] & 0x03) << 4; + mtp008_write_value(client, MTP008_REG_VID_FANDIV, val); + } + } +} + +void mtp008_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct mtp008_data *data; + + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 0; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = ALARMS_FROM_REG(data->alarms); + + *nrels_mag = 1; + } +} + +void mtp008_beep(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct mtp008_data *data; + + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 0; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = BEEPS_FROM_REG(data->beeps); + + *nrels_mag = 1; + + break; + case SENSORS_PROC_REAL_WRITE: + if (*nrels_mag >= 1) { + data->beeps = BEEPS_TO_REG(results[0]) & 0xdf8f; + + mtp008_write_value(client, MTP008_REG_BEEP_CTRL1, + data->beeps & 0xff); + mtp008_write_value(client, MTP008_REG_BEEP_CTRL2, + data->beeps >> 8); + } + } +} + +void mtp008_pwm(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + int nr; + struct mtp008_data *data; + + nr = ctl_name - MTP008_SYSCTL_PWM1; + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 0; + + break; + case SENSORS_PROC_REAL_READ: + mtp008_update_client(client); + + results[0] = PWM_FROM_REG(data->pwm[nr]); + + *nrels_mag = 1; + + break; + case SENSORS_PROC_REAL_WRITE: + if (*nrels_mag >= 1) { + data->pwm[nr] = PWM_TO_REG(results[0]); + mtp008_write_value(client, MTP008_REG_PWM_CTRL(nr), + data->pwm[nr]); + } + } +} + +void mtp008_sens(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + const char *opts = ""; + int nr; + u8 tmp; + struct mtp008_data *data; + + nr = 1 + ctl_name - MTP008_SYSCTL_SENS1; + data = client->data; + + switch (operation) { + case SENSORS_PROC_REAL_INFO: + *nrels_mag = 0; + + break; + case SENSORS_PROC_REAL_READ: + results[0] = data->sens[nr - 1]; + + *nrels_mag = 1; + + break; + case SENSORS_PROC_REAL_WRITE: + if (*nrels_mag >= 1) { + tmp = mtp008_read_value(client, MTP008_REG_PIN_CTRL2); + + switch (nr) { + case 1: /* VT or PII */ + opts = "2 or 3"; + + switch (results[0]) { + case THERMISTOR: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp & ~MTP008_CFG_VT1_PII); + data->sens[0] = 2; + return; + case PIIDIODE: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp | MTP008_CFG_VT1_PII); + data->sens[0] = 3; + return; + } + + break; + case 2: /* AIN, VT or PII */ + tmp &= ~MTP008_CFG_VT2_MASK; + opts = "1, 2 or 3"; + + switch (results[0]) { + case VOLTAGE: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp | MTP008_CFG_VT2_AIN); + data->sens[1] = 1; + return; + case THERMISTOR: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp | MTP008_CFG_VT2_VT); + data->sens[1] = 2; + return; + case PIIDIODE: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp | MTP008_CFG_VT2_PII); + data->sens[1] = 3; + return; + } + + break; + case 3: /* AIN or VT */ + opts = "1 or 2"; + + switch (results[0]) { + case VOLTAGE: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp & ~MTP008_CFG_VT3_VT); + data->sens[2] = 1; + return; + case THERMISTOR: + mtp008_write_value( + client, MTP008_REG_PIN_CTRL2, + tmp | MTP008_CFG_VT3_VT); + data->sens[2] = 2; + return; + } + + break; + } + + printk("mtp008.o: Invalid sensor type %ld " + "for sensor %d; must be %s.\n", + results[0], nr, opts); + } + } +} + +int __init sensors_mtp008_init(void) +{ + int res; + + printk("mtp008.o version %s (%s)\n", LM_VERSION, LM_DATE); + mtp008_initialized = 0; + + if ((res = i2c_add_driver(&mtp008_driver))) { + printk("mtp008.o: Driver registration failed, " + "module not inserted.\n"); + mtp008_cleanup(); + return res; + } + mtp008_initialized++; + + return 0; +} + +int __init mtp008_cleanup(void) +{ + int res; + + if (mtp008_initialized >= 1) { + if ((res = i2c_del_driver(&mtp008_driver))) { + printk("mtp008.o: Driver deregistration failed, " + "module not removed.\n"); + return res; + } + mtp008_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard , " + "Philip Edelbrock , " + "and Kris Van Hees "); +MODULE_DESCRIPTION("MTP008 driver"); + +int init_module(void) +{ + return sensors_mtp008_init(); +} + +int cleanup_module(void) +{ + return mtp008_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/sis5595.c Mon Mar 25 15:28:40 CET 2002 +++ linux/drivers/sensors/sis5595.c Mon Mar 25 15:28:40 CET 2002 @@ -0,0 +1,890 @@ +/* + sis5595.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + Copyright (c) 1998 - 2001 Frodo Looijaard , + Kyösti Mälkki , and + Mark D. Studebaker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Supports following revisions: + Version PCI ID PCI Revision + 1 1039/0008 AF or less + 2 1039/0008 B0 or greater + + Note: these chips contain a 0008 device which is incompatible with the + 5595. We recognize these by the presence of the listed + "blacklist" PCI ID and refuse to load. + + NOT SUPPORTED PCI ID BLACKLIST PCI ID + 540 0008 0540 + 550 0008 0550 + 5513 0008 5511 + 5581 0008 5597 + 5582 0008 5597 + 5597 0008 5597 + 5598 0008 5597/5598 + 630 0008 0630 + 645 0008 0645 + 730 0008 0730 + 735 0008 0735 +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the sensors"); + +/* Addresses to scan. + Note that we can't determine the ISA address until we have initialized + our module */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(sis5595); + +#ifndef PCI_DEVICE_ID_SI_540 +#define PCI_DEVICE_ID_SI_540 0x0540 +#endif +#ifndef PCI_DEVICE_ID_SI_550 +#define PCI_DEVICE_ID_SI_550 0x0550 +#endif +#ifndef PCI_DEVICE_ID_SI_630 +#define PCI_DEVICE_ID_SI_630 0x0630 +#endif +#ifndef PCI_DEVICE_ID_SI_730 +#define PCI_DEVICE_ID_SI_730 0x0730 +#endif +#ifndef PCI_DEVICE_ID_SI_5598 +#define PCI_DEVICE_ID_SI_5598 0x5598 +#endif + +static int blacklist[] = { + PCI_DEVICE_ID_SI_540, + PCI_DEVICE_ID_SI_550, + PCI_DEVICE_ID_SI_630, + PCI_DEVICE_ID_SI_730, + PCI_DEVICE_ID_SI_5511, /* 5513 chip has the 0008 device but + that ID shows up in other chips so we + use the 5511 ID for recognition */ + PCI_DEVICE_ID_SI_5597, + PCI_DEVICE_ID_SI_5598, + 0x645, + 0x735, + 0 }; +/* + SiS southbridge has a LM78-like chip integrated on the same IC. + This driver is a customized copy of lm78.c +*/ + +/* Many SIS5595 constants specified below */ + +/* Length of ISA address segment */ +#define SIS5595_EXTENT 8 +/* PCI Config Registers */ +#define SIS5595_REVISION_REG 0x08 +#define SIS5595_BASE_REG 0x68 +#define SIS5595_PIN_REG 0x7A +#define SIS5595_ENABLE_REG 0x7B + +/* Where are the ISA address/data registers relative to the base address */ +#define SIS5595_ADDR_REG_OFFSET 5 +#define SIS5595_DATA_REG_OFFSET 6 + +/* The SIS5595 registers */ +#define SIS5595_REG_IN_MAX(nr) (0x2b + (nr) * 2) +#define SIS5595_REG_IN_MIN(nr) (0x2c + (nr) * 2) +#define SIS5595_REG_IN(nr) (0x20 + (nr)) + +#define SIS5595_REG_FAN_MIN(nr) (0x3a + (nr)) +#define SIS5595_REG_FAN(nr) (0x27 + (nr)) + +/* On the first version of the chip, the temp registers are separate. + On the second version, + TEMP pin is shared with IN4, configured in PCI register 0x7A. + The registers are the same as well. + OVER and HYST are really MAX and MIN. */ + +#define REV2MIN 0xb0 +#define SIS5595_REG_TEMP (( data->revision) >= REV2MIN) ? \ + SIS5595_REG_IN(4) : 0x27 +#define SIS5595_REG_TEMP_OVER (( data->revision) >= REV2MIN) ? \ + SIS5595_REG_IN_MAX(4) : 0x39 +#define SIS5595_REG_TEMP_HYST (( data->revision) >= REV2MIN) ? \ + SIS5595_REG_IN_MIN(4) : 0x3a + +#define SIS5595_REG_CONFIG 0x40 +#define SIS5595_REG_ALARM1 0x41 +#define SIS5595_REG_ALARM2 0x42 +#define SIS5595_REG_FANDIV 0x47 + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ + +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) +#define IN_FROM_REG(val) (((val) * 16) / 10) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) + +/* Version 1 datasheet temp=.83*reg + 52.12 */ +#define TEMP_FROM_REG(val) (((((val)>=0x80?(val)-0x100:(val))*83)+5212)/10) +/* inverse 1.20*val - 62.77 */ +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?\ + ((((val)*12)-6327)/100):\ + ((((val)*12)-6227)/100)),0,255)) + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) + +/* Initial limits. To keep them sane, we use the 'standard' translation as + specified in the SIS5595 sheet. Use the config file to set better limits. */ +#define SIS5595_INIT_IN_0 (((1200) * 10)/38) +#define SIS5595_INIT_IN_1 (((500) * 100)/168) +#define SIS5595_INIT_IN_2 330 +#define SIS5595_INIT_IN_3 250 +#define SIS5595_INIT_IN_4 250 + +#define SIS5595_INIT_IN_PERCENTAGE 10 + +#define SIS5595_INIT_IN_MIN_0 \ + (SIS5595_INIT_IN_0 - SIS5595_INIT_IN_0 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MAX_0 \ + (SIS5595_INIT_IN_0 + SIS5595_INIT_IN_0 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MIN_1 \ + (SIS5595_INIT_IN_1 - SIS5595_INIT_IN_1 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MAX_1 \ + (SIS5595_INIT_IN_1 + SIS5595_INIT_IN_1 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MIN_2 \ + (SIS5595_INIT_IN_2 - SIS5595_INIT_IN_2 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MAX_2 \ + (SIS5595_INIT_IN_2 + SIS5595_INIT_IN_2 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MIN_3 \ + (SIS5595_INIT_IN_3 - SIS5595_INIT_IN_3 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MAX_3 \ + (SIS5595_INIT_IN_3 + SIS5595_INIT_IN_3 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MIN_4 \ + (SIS5595_INIT_IN_4 - SIS5595_INIT_IN_4 * SIS5595_INIT_IN_PERCENTAGE / 100) +#define SIS5595_INIT_IN_MAX_4 \ + (SIS5595_INIT_IN_4 + SIS5595_INIT_IN_4 * SIS5595_INIT_IN_PERCENTAGE / 100) + +#define SIS5595_INIT_FAN_MIN_1 3000 +#define SIS5595_INIT_FAN_MIN_2 3000 + +#define SIS5595_INIT_TEMP_OVER 600 +#define SIS5595_INIT_TEMP_HYST 100 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For the SIS5595, we need to keep some data in memory. That + data is pointed to by sis5595_list[NR]->data. The structure itself is + dynamically allocated, at the time when the new sis5595 client is + allocated. */ +struct sis5595_data { + struct semaphore lock; + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + char maxins; /* == 3 if temp enabled, otherwise == 4 */ + u8 revision; /* Reg. value */ + + u8 in[5]; /* Register value */ + u8 in_max[5]; /* Register value */ + u8 in_min[5]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u8 temp; /* Register value */ + u8 temp_over; /* Register value - really max */ + u8 temp_hyst; /* Register value - really min */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u16 alarms; /* Register encoding, combined */ +}; + +static struct pci_dev *s_bridge; /* pointer to the (only) sis5595 */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_sis5595_init(void); +static int __init sis5595_cleanup(void); + +static int sis5595_attach_adapter(struct i2c_adapter *adapter); +static int sis5595_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int sis5595_detach_client(struct i2c_client *client); +static int sis5595_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void sis5595_inc_use(struct i2c_client *client); +static void sis5595_dec_use(struct i2c_client *client); + +static int sis5595_read_value(struct i2c_client *client, u8 register); +static int sis5595_write_value(struct i2c_client *client, u8 register, + u8 value); +static void sis5595_update_client(struct i2c_client *client); +static void sis5595_init_client(struct i2c_client *client); +static int sis5595_find_sis(int *address); + + +static void sis5595_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void sis5595_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void sis5595_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void sis5595_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void sis5595_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static int sis5595_id = 0; + +/* The driver. I choose to use type i2c_driver, as at is identical to both + smbus_driver and isa_driver, and clients could be of either kind */ +static struct i2c_driver sis5595_driver = { + /* name */ "SiS 5595", + /* id */ I2C_DRIVERID_SIS5595, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &sis5595_attach_adapter, + /* detach_client */ &sis5595_detach_client, + /* command */ &sis5595_command, + /* inc_use */ &sis5595_inc_use, + /* dec_use */ &sis5595_dec_use +}; + +/* Used by sis5595_init/cleanup */ +static int __initdata sis5595_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected SIS5595. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table sis5595_dir_table_template[] = { + {SIS5595_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_in}, + {SIS5595_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_in}, + {SIS5595_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_in}, + {SIS5595_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_in}, + {SIS5595_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_in}, + {SIS5595_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_fan}, + {SIS5595_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_fan}, + {SIS5595_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_temp}, + {SIS5595_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_fan_div}, + {SIS5595_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &sis5595_alarms}, + {0} +}; + +/* This is called when the module is loaded */ +int sis5595_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, sis5595_detect); +} + +/* Locate SiS bridge and correct base address for SIS5595 */ +int sis5595_find_sis(int *address) +{ + u16 val; + int *i; + + if (!pci_present()) + return -ENODEV; + + if (!(s_bridge = + pci_find_device(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_503, + NULL))) + return -ENODEV; + + /* Look for imposters */ + for(i = blacklist; *i != 0; i++) { + if (pci_find_device(PCI_VENDOR_ID_SI, *i, NULL)) { + printk("sis5595.o: Error: Looked for SIS5595 but found unsupported device %.4X\n", *i); + return -ENODEV; + } + } + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, SIS5595_BASE_REG, &val)) + return -ENODEV; + + *address = val & ~(SIS5595_EXTENT - 1); + if (*address == 0 && force_addr == 0) { + printk("sis5595.o: base address not set - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + if (force_addr) + *address = force_addr; /* so detect will get called */ + + return 0; +} + +int sis5595_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct sis5595_data *data; + int err = 0; + const char *type_name = "sis5595"; + const char *client_name = "SIS5595 chip"; + char val; + u16 a; + + /* Make sure we are probing the ISA bus!! */ + if (!i2c_is_isa_adapter(adapter)) { + printk + ("sis5595.o: sis5595_detect called for an I2C bus adapter?!?\n"); + return 0; + } + + if(force_addr) + address = force_addr & ~(SIS5595_EXTENT - 1); + if (check_region(address, SIS5595_EXTENT)) { + printk("sis5595.o: region 0x%x already in use!\n", address); + return -ENODEV; + } + if(force_addr) { + printk("sis5595.o: forcing ISA address 0x%04X\n", address); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(s_bridge, SIS5595_BASE_REG, address)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, SIS5595_BASE_REG, &a)) + return -ENODEV; + if ((a & ~(SIS5595_EXTENT - 1)) != address) { + /* doesn't work for some chips? */ + printk("sis5595.o: force address failed\n"); + return -ENODEV; + } + } + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) + return -ENODEV; + if((val & 0x80) == 0) { + printk("sis5595.o: enabling sensors\n"); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_byte(s_bridge, SIS5595_ENABLE_REG, + val | 0x80)) + return -ENODEV; + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(s_bridge, SIS5595_ENABLE_REG, &val)) + return -ENODEV; + if((val & 0x80) == 0) { /* doesn't work for some chips! */ + printk("sis5595.o: sensors enable failed - not supported?\n"); + return -ENODEV; + } + } + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct sis5595_data), + GFP_KERNEL))) { + return -ENOMEM; + } + + data = (struct sis5595_data *) (new_client + 1); + new_client->addr = address; + init_MUTEX(&data->lock); + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &sis5595_driver; + new_client->flags = 0; + + /* Reserve the ISA region */ + request_region(address, SIS5595_EXTENT, type_name); + + /* Check revision and pin registers to determine whether 3 or 4 voltages */ + pci_read_config_byte(s_bridge, SIS5595_REVISION_REG, &(data->revision)); + if(data->revision < REV2MIN) { + data->maxins = 3; + } else { + pci_read_config_byte(s_bridge, SIS5595_PIN_REG, &val); + if(val & 0x80) + /* 3 voltages, 1 temp */ + data->maxins = 3; + else + /* 4 voltages, no temps */ + data->maxins = 4; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = sis5595_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry((struct i2c_client *) new_client, + type_name, + sis5595_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the SIS5595 chip */ + sis5595_init_client(new_client); + return 0; + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + release_region(address, SIS5595_EXTENT); + kfree(new_client); + return err; +} + +int sis5595_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct sis5595_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("sis5595.o: Client deregistration failed, client not detached.\n"); + return err; + } + + release_region(client->addr, SIS5595_EXTENT); + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int sis5595_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void sis5595_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void sis5595_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + + +/* ISA access must be locked explicitly. + There are some ugly typecasts here, but the good news is - they should + nowhere else be necessary! */ +int sis5595_read_value(struct i2c_client *client, u8 reg) +{ + int res; + + down(&(((struct sis5595_data *) (client->data))->lock)); + outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET); + res = inb_p(client->addr + SIS5595_DATA_REG_OFFSET); + up(&(((struct sis5595_data *) (client->data))->lock)); + return res; +} + +int sis5595_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + down(&(((struct sis5595_data *) (client->data))->lock)); + outb_p(reg, client->addr + SIS5595_ADDR_REG_OFFSET); + outb_p(value, client->addr + SIS5595_DATA_REG_OFFSET); + up(&(((struct sis5595_data *) (client->data))->lock)); + return 0; +} + +/* Called when we have found a new SIS5595. It should set limits, etc. */ +void sis5595_init_client(struct i2c_client *client) +{ + struct sis5595_data *data = client->data; + + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others */ + sis5595_write_value(client, SIS5595_REG_CONFIG, 0x80); + + sis5595_write_value(client, SIS5595_REG_IN_MIN(0), + IN_TO_REG(SIS5595_INIT_IN_MIN_0)); + sis5595_write_value(client, SIS5595_REG_IN_MAX(0), + IN_TO_REG(SIS5595_INIT_IN_MAX_0)); + sis5595_write_value(client, SIS5595_REG_IN_MIN(1), + IN_TO_REG(SIS5595_INIT_IN_MIN_1)); + sis5595_write_value(client, SIS5595_REG_IN_MAX(1), + IN_TO_REG(SIS5595_INIT_IN_MAX_1)); + sis5595_write_value(client, SIS5595_REG_IN_MIN(2), + IN_TO_REG(SIS5595_INIT_IN_MIN_2)); + sis5595_write_value(client, SIS5595_REG_IN_MAX(2), + IN_TO_REG(SIS5595_INIT_IN_MAX_2)); + sis5595_write_value(client, SIS5595_REG_IN_MIN(3), + IN_TO_REG(SIS5595_INIT_IN_MIN_3)); + sis5595_write_value(client, SIS5595_REG_IN_MAX(3), + IN_TO_REG(SIS5595_INIT_IN_MAX_3)); + sis5595_write_value(client, SIS5595_REG_FAN_MIN(1), + FAN_TO_REG(SIS5595_INIT_FAN_MIN_1, 2)); + sis5595_write_value(client, SIS5595_REG_FAN_MIN(2), + FAN_TO_REG(SIS5595_INIT_FAN_MIN_2, 2)); + if(data->maxins == 4) { + sis5595_write_value(client, SIS5595_REG_IN_MIN(4), + IN_TO_REG(SIS5595_INIT_IN_MIN_4)); + sis5595_write_value(client, SIS5595_REG_IN_MAX(4), + IN_TO_REG(SIS5595_INIT_IN_MAX_4)); + } else { + sis5595_write_value(client, SIS5595_REG_TEMP_OVER, + TEMP_TO_REG(SIS5595_INIT_TEMP_OVER)); + sis5595_write_value(client, SIS5595_REG_TEMP_HYST, + TEMP_TO_REG(SIS5595_INIT_TEMP_HYST)); + } + + /* Start monitoring */ + sis5595_write_value(client, SIS5595_REG_CONFIG, + (sis5595_read_value(client, SIS5595_REG_CONFIG) + & 0xf7) | 0x01); + +} + +void sis5595_update_client(struct i2c_client *client) +{ + struct sis5595_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + + for (i = 0; i <= data->maxins; i++) { + data->in[i] = + sis5595_read_value(client, SIS5595_REG_IN(i)); + data->in_min[i] = + sis5595_read_value(client, + SIS5595_REG_IN_MIN(i)); + data->in_max[i] = + sis5595_read_value(client, + SIS5595_REG_IN_MAX(i)); + } + for (i = 1; i <= 2; i++) { + data->fan[i - 1] = + sis5595_read_value(client, SIS5595_REG_FAN(i)); + data->fan_min[i - 1] = + sis5595_read_value(client, + SIS5595_REG_FAN_MIN(i)); + } + if(data->maxins == 3) { + data->temp = + sis5595_read_value(client, SIS5595_REG_TEMP); + data->temp_over = + sis5595_read_value(client, SIS5595_REG_TEMP_OVER); + data->temp_hyst = + sis5595_read_value(client, SIS5595_REG_TEMP_HYST); + } + i = sis5595_read_value(client, SIS5595_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = + sis5595_read_value(client, SIS5595_REG_ALARM1) | + (sis5595_read_value(client, SIS5595_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ + +/* Return 0 for in4 and disallow writes if pin used for temp */ +void sis5595_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct sis5595_data *data = client->data; + int nr = ctl_name - SIS5595_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + if(nr <= 3 || data->maxins == 4) { + sis5595_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr]); + results[1] = IN_FROM_REG(data->in_max[nr]); + results[2] = IN_FROM_REG(data->in[nr]); + } else { + results[0] = 0; + results[1] = 0; + results[2] = 0; + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if(nr <= 3 || data->maxins == 4) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0]); + sis5595_write_value(client, + SIS5595_REG_IN_MIN(nr), data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1]); + sis5595_write_value(client, + SIS5595_REG_IN_MAX(nr), data->in_max[nr]); + } + } + } +} + +void sis5595_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct sis5595_data *data = client->data; + int nr = ctl_name - SIS5595_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + sis5595_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + results[1] = FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG + (data-> + fan_div[nr-1])); + sis5595_write_value(client, + SIS5595_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } + } +} + + +/* Return 0 for temp and disallow writes if pin used for in4 */ +void sis5595_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct sis5595_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + if(data->maxins == 3) { + sis5595_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + } else { + results[0] = 0; + results[1] = 0; + results[2] = 0; + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if(data->maxins == 3) { + if (*nrels_mag >= 1) { + data->temp_over = TEMP_TO_REG(results[0]); + sis5595_write_value(client, + SIS5595_REG_TEMP_OVER, data->temp_over); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + sis5595_write_value(client, + SIS5595_REG_TEMP_HYST, data->temp_hyst); + } + } + } +} + +void sis5595_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct sis5595_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + sis5595_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void sis5595_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct sis5595_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + sis5595_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = sis5595_read_value(client, SIS5595_REG_FANDIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan_div[1] << 6); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div[0] << 4); + sis5595_write_value(client, SIS5595_REG_FANDIV, old); + } + } +} + +int __init sensors_sis5595_init(void) +{ + int res, addr; + + printk("sis5595.o version %s (%s)\n", LM_VERSION, LM_DATE); + sis5595_initialized = 0; + + if (sis5595_find_sis(&addr)) { + printk("sis5595.o: SIS5595 not detected, module not inserted.\n"); + return -ENODEV; + } + normal_isa[0] = addr; + + if ((res = i2c_add_driver(&sis5595_driver))) { + printk + ("sis5595.o: Driver registration failed, module not inserted.\n"); + sis5595_cleanup(); + return res; + } + sis5595_initialized++; + return 0; +} + +int __init sis5595_cleanup(void) +{ + int res; + + if (sis5595_initialized >= 1) { + if ((res = i2c_del_driver(&sis5595_driver))) { + printk + ("sis5595.o: Driver deregistration failed, module not removed.\n"); + return res; + } + sis5595_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Kyösti Mälkki "); +MODULE_DESCRIPTION("SiS 5595 Sensor device"); + +int init_module(void) +{ + return sensors_sis5595_init(); +} + +int cleanup_module(void) +{ + return sis5595_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/thmc50.c Mon Mar 25 15:28:41 CET 2002 +++ linux/drivers/sensors/thmc50.c Mon Mar 25 15:28:41 CET 2002 @@ -0,0 +1,580 @@ +/* + thmc50.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard and + Philip Edelbrock + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#define DEBUG 1 + +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x2D, 0x2F, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(thmc50); + +/* Many THMC50 constants specified below */ + +/* The THMC50 registers */ +#define THMC50_REG_TEMP 0x27 +#define THMC50_REG_CONF 0x40 +#define THMC50_REG_TEMP_HYST 0x3A +#define THMC50_REG_TEMP_OS 0x39 + +#define THMC50_REG_TEMP_TRIP 0x13 +#define THMC50_REG_TEMP_REMOTE_TRIP 0x14 +#define THMC50_REG_TEMP_DEFAULT_TRIP 0x17 +#define THMC50_REG_TEMP_REMOTE_DEFAULT_TRIP 0x18 +#define THMC50_REG_ANALOG_OUT 0x19 +#define THMC50_REG_REMOTE_TEMP 0x26 +#define THMC50_REG_REMOTE_TEMP_HYST 0x38 +#define THMC50_REG_REMOTE_TEMP_OS 0x37 + +#define THMC50_REG_INTER 0x41 +#define THMC50_REG_INTER_MIRROR 0x4C +#define THMC50_REG_INTER_MASK 0x43 + +#define THMC50_REG_COMPANY_ID 0x3E +#define THMC50_REG_DIE_CODE 0x3F + + +/* 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 TEMP_FROM_REG(val) ((val>127)?val - 0x0100:val) +#define TEMP_TO_REG(val) ((val<0)?0x0100+val:val) + +/* Initial values */ +#define THMC50_INIT_TEMP_OS 60 +#define THMC50_INIT_TEMP_HYST 50 + +/* Each client has this additional data */ +struct thmc50_data { + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u16 temp, temp_os, temp_hyst, + remote_temp, remote_temp_os, remote_temp_hyst, + inter, inter_mask, die_code, analog_out; /* Register values */ +}; + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_thmc50_init(void); +static int __init thmc50_cleanup(void); +static int thmc50_attach_adapter(struct i2c_adapter *adapter); +static int thmc50_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static void thmc50_init_client(struct i2c_client *client); +static int thmc50_detach_client(struct i2c_client *client); +static int thmc50_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void thmc50_inc_use(struct i2c_client *client); +static void thmc50_dec_use(struct i2c_client *client); +static int thmc50_read_value(struct i2c_client *client, u8 reg); +static int thmc50_write_value(struct i2c_client *client, u8 reg, + u16 value); +static void thmc50_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void thmc50_remote_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, + long *results); +static void thmc50_inter(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void thmc50_inter_mask(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void thmc50_die_code(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void thmc50_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void thmc50_update_client(struct i2c_client *client); + + +/* This is the driver that will be inserted */ +static struct i2c_driver thmc50_driver = { + /* name */ "THMC50 sensor chip driver", + /* id */ I2C_DRIVERID_THMC50, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &thmc50_attach_adapter, + /* detach_client */ &thmc50_detach_client, + /* command */ &thmc50_command, + /* inc_use */ &thmc50_inc_use, + /* dec_use */ &thmc50_dec_use +}; + +/* These files are created for each detected THMC50. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table thmc50_dir_table_template[] = { + {THMC50_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &thmc50_temp}, + {THMC50_SYSCTL_REMOTE_TEMP, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &thmc50_remote_temp}, + {THMC50_SYSCTL_INTER, "inter", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &thmc50_inter}, + {THMC50_SYSCTL_INTER_MASK, "inter_mask", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &thmc50_inter_mask}, + {THMC50_SYSCTL_DIE_CODE, "die_code", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &thmc50_die_code}, + {THMC50_SYSCTL_ANALOG_OUT, "analog_out", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &thmc50_analog_out}, + {0} +}; + +/* Used by init/cleanup */ +static int __initdata thmc50_initialized = 0; + +static int thmc50_id = 0; + +int thmc50_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, thmc50_detect); +} + +/* This function is called by i2c_detect */ +int thmc50_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int company, i; + struct i2c_client *new_client; + struct thmc50_data *data; + int err = 0; + const char *type_name, *client_name; + +#ifdef DEBUG + printk("thmc50.o: Probing for THMC50 at 0x%2X on bus %d\n", + address, adapter->id); +#endif + + /* Make sure we aren't probing the ISA bus!! This is just a safety check + at this moment; i2c_detect really won't call us. */ +#ifdef DEBUG + if (i2c_is_isa_adapter(adapter)) { + printk + ("thmc50.o: thmc50_detect called for an ISA bus adapter?!?\n"); + return 0; + } +#endif + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + goto ERROR0; + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access thmc50_{read,write}_value. */ + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct thmc50_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = + (struct thmc50_data *) (((struct i2c_client *) new_client) + + 1); + new_client->addr = address; + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &thmc50_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + company = + i2c_smbus_read_byte_data(new_client, THMC50_REG_COMPANY_ID); + + if (company != 0x49) { +#ifdef DEBUG + printk + ("thmc50.o: Detect of THMC50 failed (reg 3E: 0x%X)\n", + company); +#endif + goto ERROR1; + } + + /* Determine the chip type - only one kind supported! */ + kind = thmc50; + + if (kind == thmc50) { + type_name = "thmc50"; + client_name = "THMC50 chip"; + } else { +#ifdef DEBUG + printk("thmc50.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + + new_client->id = thmc50_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, type_name, + thmc50_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + thmc50_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int thmc50_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct thmc50_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("thmc50.o: Client deregistration failed, client not detached.\n"); + return err; + } + + kfree(client); + + return 0; +} + + +/* No commands defined yet */ +int thmc50_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void thmc50_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void thmc50_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* All registers are word-sized, except for the configuration register. + THMC50 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int thmc50_read_value(struct i2c_client *client, u8 reg) +{ + return i2c_smbus_read_byte_data(client, reg); +} + +/* All registers are word-sized, except for the configuration register. + THMC50 uses a high-byte first convention, which is exactly opposite to + the usual practice. */ +int thmc50_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +void thmc50_init_client(struct i2c_client *client) +{ + /* Initialize the THMC50 chip */ + thmc50_write_value(client, THMC50_REG_TEMP_OS, + TEMP_TO_REG(THMC50_INIT_TEMP_OS)); + thmc50_write_value(client, THMC50_REG_TEMP_HYST, + TEMP_TO_REG(THMC50_INIT_TEMP_HYST)); + thmc50_write_value(client, THMC50_REG_CONF, 1); +} + +void thmc50_update_client(struct i2c_client *client) +{ + struct thmc50_data *data = client->data; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk("Starting thmc50 update\n"); +#endif + + data->temp = thmc50_read_value(client, THMC50_REG_TEMP); + data->temp_os = + thmc50_read_value(client, THMC50_REG_TEMP_OS); + data->temp_hyst = + thmc50_read_value(client, THMC50_REG_TEMP_HYST); + data->remote_temp = + thmc50_read_value(client, THMC50_REG_REMOTE_TEMP); + data->remote_temp_os = + thmc50_read_value(client, THMC50_REG_REMOTE_TEMP_OS); + data->remote_temp_hyst = + thmc50_read_value(client, THMC50_REG_REMOTE_TEMP_HYST); + data->inter = thmc50_read_value(client, THMC50_REG_INTER); + data->inter_mask = + thmc50_read_value(client, THMC50_REG_INTER_MASK); + data->die_code = + thmc50_read_value(client, THMC50_REG_DIE_CODE); + data->analog_out = + thmc50_read_value(client, THMC50_REG_ANALOG_OUT); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +void thmc50_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct thmc50_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + thmc50_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_os); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_os = TEMP_TO_REG(results[0]); + thmc50_write_value(client, THMC50_REG_TEMP_OS, + data->temp_os); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + thmc50_write_value(client, THMC50_REG_TEMP_HYST, + data->temp_hyst); + } + } +} + + +void thmc50_remote_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct thmc50_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + thmc50_update_client(client); + results[0] = TEMP_FROM_REG(data->remote_temp_os); + results[1] = TEMP_FROM_REG(data->remote_temp_hyst); + results[2] = TEMP_FROM_REG(data->remote_temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->remote_temp_os = TEMP_TO_REG(results[0]); + thmc50_write_value(client, + THMC50_REG_REMOTE_TEMP_OS, + data->remote_temp_os); + } + if (*nrels_mag >= 2) { + data->remote_temp_hyst = TEMP_TO_REG(results[1]); + thmc50_write_value(client, + THMC50_REG_REMOTE_TEMP_HYST, + data->remote_temp_hyst); + } + } +} + + +void thmc50_inter(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct thmc50_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + thmc50_update_client(client); + results[0] = data->inter; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + printk("thmc50.o: No writes to Interrupt register!\n"); + } +} + + +void thmc50_inter_mask(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct thmc50_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + thmc50_update_client(client); + results[0] = data->inter_mask; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->inter_mask = results[0]; + thmc50_write_value(client, THMC50_REG_INTER_MASK, + data->inter_mask); + } + } +} + + +void thmc50_die_code(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct thmc50_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + thmc50_update_client(client); + results[0] = data->die_code; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + printk("thmc50.o: No writes to Die-Code register!\n"); + } +} + + +void thmc50_analog_out(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct thmc50_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + thmc50_update_client(client); + results[0] = data->analog_out; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->analog_out = results[0]; + thmc50_write_value(client, THMC50_REG_ANALOG_OUT, + data->analog_out); + } + } +} + + + + +int __init sensors_thmc50_init(void) +{ + int res; + + printk("thmc50.o version %s (%s)\n", LM_VERSION, LM_DATE); + thmc50_initialized = 0; + if ((res = i2c_add_driver(&thmc50_driver))) { + printk + ("thmc50.o: Driver registration failed, module not inserted.\n"); + thmc50_cleanup(); + return res; + } + thmc50_initialized++; + return 0; +} + +int __init thmc50_cleanup(void) +{ + int res; + + if (thmc50_initialized >= 1) { + if ((res = i2c_del_driver(&thmc50_driver))) { + printk + ("thmc50.o: Driver deregistration failed, module not removed.\n"); + return res; + } + thmc50_initialized--; + } + + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Frodo Looijaard and Philip Edelbrock "); +MODULE_DESCRIPTION("THMC50 driver"); + +int init_module(void) +{ + return sensors_thmc50_init(); +} + +int cleanup_module(void) +{ + return thmc50_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/via686a.c Mon Mar 25 15:28:41 CET 2002 +++ linux/drivers/sensors/via686a.c Mon Mar 25 15:28:41 CET 2002 @@ -0,0 +1,997 @@ +/* + via686a.c - Part of lm_sensors, Linux kernel modules + for hardware monitoring + + Copyright (c) 1998 - 2001 Frodo Looijaard , + Kyösti Mälkki , + Mark Studebaker , + and Bob Dougherty + (Some conversion-factor data were contributed by Jonathan Teh Soon Yew + and Alex van Kaam .) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Supports the Via VT82C686A and VT82C686B south bridges. + Reports either as a 686A. + See doc/chips/via686a for details. + Warning - only supports a single device. +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifndef PCI_DEVICE_ID_VIA_82C686_4 +#define PCI_DEVICE_ID_VIA_82C686_4 0x3057 +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* If force_addr is set to anything different from 0, we forcibly enable + the device at the given address. */ +static int force_addr = 0; +MODULE_PARM(force_addr, "i"); +MODULE_PARM_DESC(force_addr, + "Initialize the base address of the sensors"); + +/* Addresses to scan. + Note that we can't determine the ISA address until we have initialized + our module */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0x0000, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_1(via686a); + +/* + The Via 686a southbridge has a LM78-like chip integrated on the same IC. + This driver is a customized copy of lm78.c +*/ + +/* Many VIA686A constants specified below */ + +/* Length of ISA address segment */ +#define VIA686A_EXTENT 0x80 +#define VIA686A_BASE_REG 0x70 +#define VIA686A_ENABLE_REG 0x74 + +/* The VIA686A registers */ +/* ins numbered 0-4 */ +#define VIA686A_REG_IN_MAX(nr) (0x2b + ((nr) * 2)) +#define VIA686A_REG_IN_MIN(nr) (0x2c + ((nr) * 2)) +#define VIA686A_REG_IN(nr) (0x22 + (nr)) + +/* fans numbered 1-2 */ +#define VIA686A_REG_FAN_MIN(nr) (0x3a + (nr)) +#define VIA686A_REG_FAN(nr) (0x28 + (nr)) + +// the following values are as speced by VIA: +static const u8 regtemp[] = { 0x20, 0x21, 0x1f }; +static const u8 regover[] = { 0x39, 0x3d, 0x1d }; +static const u8 reghyst[] = { 0x3a, 0x3e, 0x1e }; + +/* temps numbered 1-3 */ +#define VIA686A_REG_TEMP(nr) (regtemp[(nr) - 1]) +#define VIA686A_REG_TEMP_OVER(nr) (regover[(nr) - 1]) +#define VIA686A_REG_TEMP_HYST(nr) (reghyst[(nr) - 1]) +#define VIA686A_REG_TEMP_LOW1 0x4b // bits 7-6 +#define VIA686A_REG_TEMP_LOW23 0x49 // 2 = bits 5-4, 3 = bits 7-6 + +#define VIA686A_REG_ALARM1 0x41 +#define VIA686A_REG_ALARM2 0x42 +#define VIA686A_REG_FANDIV 0x47 +#define VIA686A_REG_CONFIG 0x40 +// The following register sets temp interrupt mode (bits 1-0 for temp1, +// 3-2 for temp2, 5-4 for temp3). Modes are: +// 00 interrupt stays as long as value is out-of-range +// 01 interrupt is cleared once register is read (default) +// 10 comparator mode- like 00, but ignores hysteresis +// 11 same as 00 +#define VIA686A_REG_TEMP_MODE 0x4b +// We'll just assume that you want to set all 3 simulataneously: +#define VIA686A_TEMP_MODE_MASK 0x3F +#define VIA686A_TEMP_MODE_CONTINUOUS (0x00) + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. */ + +/********* VOLTAGE CONVERSIONS (Bob Dougherty) ********/ +// From HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew): +// voltagefactor[0]=1.25/2628; (2628/1.25=2102.4) // Vccp +// voltagefactor[1]=1.25/2628; (2628/1.25=2102.4) // +2.5V +// voltagefactor[2]=1.67/2628; (2628/1.67=1573.7) // +3.3V +// voltagefactor[3]=2.6/2628; (2628/2.60=1010.8) // +5V +// voltagefactor[4]=6.3/2628; (2628/6.30=417.14) // +12V +// in[i]=(data[i+2]*25.0+133)*voltagefactor[i]; +// That is: +// volts = (25*regVal+133)*factor +// regVal = (volts/factor-133)/25 +// (These conversions were contributed by Jonathan Teh Soon Yew +// ) +// +// These get us close, but they don't completely agree with what my BIOS +// says- they are all a bit low. But, it all we have to go on... +extern inline u8 IN_TO_REG(long val, int inNum) +{ + // to avoid floating point, we multiply everything by 100. + // val is guaranteed to be positive, so we can achieve the effect of + // rounding by (...*10+5)/10. Note that the *10 is hidden in the + // /250 (which should really be /2500). + // At the end, we need to /100 because we *100 everything and we need + // to /10 because of the rounding thing, so we /1000. + if (inNum <= 1) + return (u8) + SENSORS_LIMIT(((val * 210240 - 13300) / 250 + 5) / 1000, + 0, 255); + else if (inNum == 2) + return (u8) + SENSORS_LIMIT(((val * 157370 - 13300) / 250 + 5) / 1000, + 0, 255); + else if (inNum == 3) + return (u8) + SENSORS_LIMIT(((val * 101080 - 13300) / 250 + 5) / 1000, + 0, 255); + else + return (u8) SENSORS_LIMIT(((val * 41714 - 13300) / 250 + 5) + / 1000, 0, 255); +} + +extern inline long IN_FROM_REG(u8 val, int inNum) +{ + // to avoid floating point, we multiply everything by 100. + // val is guaranteed to be positive, so we can achieve the effect of + // rounding by adding 0.5. Or, to avoid fp math, we do (...*10+5)/10. + // We need to scale with *100 anyway, so no need to /100 at the end. + if (inNum <= 1) + return (long) (((250000 * val + 13300) / 210240 * 10 + 5) /10); + else if (inNum == 2) + return (long) (((250000 * val + 13300) / 157370 * 10 + 5) /10); + else if (inNum == 3) + return (long) (((250000 * val + 13300) / 101080 * 10 + 5) /10); + else + return (long) (((250000 * val + 13300) / 41714 * 10 + 5) /10); +} + +/********* FAN RPM CONVERSIONS ********/ +// Higher register values = slower fans (the fan's strobe gates a counter). +// But this chip saturates back at 0, not at 255 like all the other chips. +// So, 0 means 0 RPM +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 0; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, 255); +} + +#define FAN_FROM_REG(val,div) ((val)==0?0:(val)==255?0:1350000/((val)*(div))) + +/******** TEMP CONVERSIONS (Bob Dougherty) *********/ +// linear fits from HWMon.cpp (Copyright 1998-2000 Jonathan Teh Soon Yew) +// if(temp<169) +// return double(temp)*0.427-32.08; +// else if(temp>=169 && temp<=202) +// return double(temp)*0.582-58.16; +// else +// return double(temp)*0.924-127.33; +// +// A fifth-order polynomial fits the unofficial data (provided by Alex van +// Kaam ) a bit better. It also give more reasonable +// numbers on my machine (ie. they agree with what my BIOS tells me). +// Here's the fifth-order fit to the 8-bit data: +// temp = 1.625093e-10*val^5 - 1.001632e-07*val^4 + 2.457653e-05*val^3 - +// 2.967619e-03*val^2 + 2.175144e-01*val - 7.090067e+0. +// +// (2000-10-25- RFD: thanks to Uwe Andersen for +// finding my typos in this formula!) +// +// Alas, none of the elegant function-fit solutions will work because we +// aren't allowed to use floating point in the kernel and doing it with +// integers doesn't rpovide enough precision. So we'll do boring old +// look-up table stuff. The unofficial data (see below) have effectively +// 7-bit resolution (they are rounded to the nearest degree). I'm assuming +// that the transfer function of the device is monotonic and smooth, so a +// smooth function fit to the data will allow us to get better precision. +// I used the 5th-order poly fit described above and solved for +// VIA register values 0-255. I *10 before rounding, so we get tenth-degree +// precision. (I could have done all 1024 values for our 10-bit readings, +// but the function is very linear in the useful range (0-80 deg C), so +// we'll just use linear interpolation for 10-bit readings.) So, tempLUT +// is the temp at via register values 0-255: +static const long tempLUT[] = + { -709, -688, -667, -646, -627, -607, -589, -570, -553, -536, -519, + -503, -487, -471, -456, -442, -428, -414, -400, -387, -375, + -362, -350, -339, -327, -316, -305, -295, -285, -275, -265, + -255, -246, -237, -229, -220, -212, -204, -196, -188, -180, + -173, -166, -159, -152, -145, -139, -132, -126, -120, -114, + -108, -102, -96, -91, -85, -80, -74, -69, -64, -59, -54, -49, + -44, -39, -34, -29, -25, -20, -15, -11, -6, -2, 3, 7, 12, 16, + 20, 25, 29, 33, 37, 42, 46, 50, 54, 59, 63, 67, 71, 75, 79, 84, + 88, 92, 96, 100, 104, 109, 113, 117, 121, 125, 130, 134, 138, + 142, 146, 151, 155, 159, 163, 168, 172, 176, 181, 185, 189, + 193, 198, 202, 206, 211, 215, 219, 224, 228, 232, 237, 241, + 245, 250, 254, 259, 263, 267, 272, 276, 281, 285, 290, 294, + 299, 303, 307, 312, 316, 321, 325, 330, 334, 339, 344, 348, + 353, 357, 362, 366, 371, 376, 380, 385, 390, 395, 399, 404, + 409, 414, 419, 423, 428, 433, 438, 443, 449, 454, 459, 464, + 469, 475, 480, 486, 491, 497, 502, 508, 514, 520, 526, 532, + 538, 544, 551, 557, 564, 571, 578, 584, 592, 599, 606, 614, + 621, 629, 637, 645, 654, 662, 671, 680, 689, 698, 708, 718, + 728, 738, 749, 759, 770, 782, 793, 805, 818, 830, 843, 856, + 870, 883, 898, 912, 927, 943, 958, 975, 991, 1008, 1026, 1044, + 1062, 1081, 1101, 1121, 1141, 1162, 1184, 1206, 1229, 1252, + 1276, 1301, 1326, 1352, 1378, 1406, 1434, 1462 +}; + +/* the original LUT values from Alex van Kaam + (for via register values 12-240): +{-50,-49,-47,-45,-43,-41,-39,-38,-37,-35,-34,-33,-32,-31, +-30,-29,-28,-27,-26,-25,-24,-24,-23,-22,-21,-20,-20,-19,-18,-17,-17,-16,-15, +-15,-14,-14,-13,-12,-12,-11,-11,-10,-9,-9,-8,-8,-7,-7,-6,-6,-5,-5,-4,-4,-3, +-3,-2,-2,-1,-1,0,0,1,1,1,3,3,3,4,4,4,5,5,5,6,6,7,7,8,8,9,9,9,10,10,11,11,12, +12,12,13,13,13,14,14,15,15,16,16,16,17,17,18,18,19,19,20,20,21,21,21,22,22, +22,23,23,24,24,25,25,26,26,26,27,27,27,28,28,29,29,30,30,30,31,31,32,32,33, +33,34,34,35,35,35,36,36,37,37,38,38,39,39,40,40,41,41,42,42,43,43,44,44,45, +45,46,46,47,48,48,49,49,50,51,51,52,52,53,53,54,55,55,56,57,57,58,59,59,60, +61,62,62,63,64,65,66,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,83,84, +85,86,88,89,91,92,94,96,97,99,101,103,105,107,109,110}; +*/ + +// Here's the reverse LUT. I got it by doing a 6-th order poly fit (needed +// an extra term for a good fit to these inverse data!) and then +// solving for each temp value from -50 to 110 (the useable range for +// this chip). Here's the fit: +// viaRegVal = -1.160370e-10*val^6 +3.193693e-08*val^5 - 1.464447e-06*val^4 +// - 2.525453e-04*val^3 + 1.424593e-02*val^2 + 2.148941e+00*val +7.275808e+01) +// Note that n=161: +static const u8 viaLUT[] = + { 12, 12, 13, 14, 14, 15, 16, 16, 17, 18, 18, 19, 20, 20, 21, 22, 23, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 36, 37, 39, 40, + 41, 43, 45, 46, 48, 49, 51, 53, 55, 57, 59, 60, 62, 64, 66, + 69, 71, 73, 75, 77, 79, 82, 84, 86, 88, 91, 93, 95, 98, 100, + 103, 105, 107, 110, 112, 115, 117, 119, 122, 124, 126, 129, + 131, 134, 136, 138, 140, 143, 145, 147, 150, 152, 154, 156, + 158, 160, 162, 164, 166, 168, 170, 172, 174, 176, 178, 180, + 182, 183, 185, 187, 188, 190, 192, 193, 195, 196, 198, 199, + 200, 202, 203, 205, 206, 207, 208, 209, 210, 211, 212, 213, + 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224, + 225, 226, 226, 227, 228, 228, 229, 230, 230, 231, 232, 232, + 233, 233, 234, 235, 235, 236, 236, 237, 237, 238, 238, 239, + 239, 240 +}; + +/* Converting temps to (8-bit) hyst and over registers */ +// No interpolation here. Just check the limits and go. +// The +5 effectively rounds off properly and the +50 is because +// the temps start at -50 +extern inline u8 TEMP_TO_REG(long val) +{ + return (u8) + SENSORS_LIMIT(viaLUT[((val <= -500) ? 0 : (val >= 1100) ? 160 : + ((val + 5) / 10 + 50))], 0, 255); +} + +/* for 8-bit temperature hyst and over registers */ +// The temp values are already *10, so we don't need to do that. +// But we _will_ round these off to the nearest degree with (...*10+5)/10 +#define TEMP_FROM_REG(val) ((tempLUT[(val)]*10+5)/10) + +/* for 10-bit temperature readings */ +// You might _think_ this is too long to inline, but's it's really only +// called once... +extern inline long TEMP_FROM_REG10(u16 val) +{ + // the temp values are already *10, so we don't need to do that. + long temp; + u16 eightBits = val >> 2; + u16 twoBits = val & 3; + + // handle the extremes first (they won't interpolate well! ;-) + if (val == 0) + return (long) tempLUT[0]; + if (val == 1023) + return (long) tempLUT[255]; + + if (twoBits == 0) + return (long) tempLUT[eightBits]; + else { + // do some interpolation by multipying the lower and upper + // bounds by 25, 50 or 75, then /100. + temp = ((25 * (4 - twoBits)) * tempLUT[eightBits] + + (25 * twoBits) * tempLUT[eightBits + 1]); + // increase the magnitude by 50 to achieve rounding. + if (temp > 0) + temp += 50; + else + temp -= 50; + return (temp / 100); + } +} + +#define ALARMS_FROM_REG(val) (val) + +#define DIV_FROM_REG(val) (1 << (val)) +#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?0:1) + +/* Initial limits */ +#define VIA686A_INIT_IN_0 200 +#define VIA686A_INIT_IN_1 250 +#define VIA686A_INIT_IN_2 330 +#define VIA686A_INIT_IN_3 500 +#define VIA686A_INIT_IN_4 1200 + +#define VIA686A_INIT_IN_PERCENTAGE 10 + +#define VIA686A_INIT_IN_MIN_0 (VIA686A_INIT_IN_0 - VIA686A_INIT_IN_0 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_0 (VIA686A_INIT_IN_0 + VIA686A_INIT_IN_0 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MIN_1 (VIA686A_INIT_IN_1 - VIA686A_INIT_IN_1 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_1 (VIA686A_INIT_IN_1 + VIA686A_INIT_IN_1 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MIN_2 (VIA686A_INIT_IN_2 - VIA686A_INIT_IN_2 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_2 (VIA686A_INIT_IN_2 + VIA686A_INIT_IN_2 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MIN_3 (VIA686A_INIT_IN_3 - VIA686A_INIT_IN_3 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_3 (VIA686A_INIT_IN_3 + VIA686A_INIT_IN_3 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MIN_4 (VIA686A_INIT_IN_4 - VIA686A_INIT_IN_4 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) +#define VIA686A_INIT_IN_MAX_4 (VIA686A_INIT_IN_4 + VIA686A_INIT_IN_4 \ + * VIA686A_INIT_IN_PERCENTAGE / 100) + +#define VIA686A_INIT_FAN_MIN 3000 + +#define VIA686A_INIT_TEMP_OVER 600 +#define VIA686A_INIT_TEMP_HYST 500 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* For the VIA686A, we need to keep some data in memory. That + data is pointed to by via686a_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new via686a client is + allocated. */ +struct via686a_data { + struct semaphore lock; + int sysctl_id; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + u8 in[5]; /* Register value */ + u8 in_max[5]; /* Register value */ + u8 in_min[5]; /* Register value */ + u8 fan[2]; /* Register value */ + u8 fan_min[2]; /* Register value */ + u16 temp[3]; /* Register value 10 bit */ + u8 temp_over[3]; /* Register value */ + u8 temp_hyst[3]; /* Register value */ + u8 fan_div[2]; /* Register encoding, shifted right */ + u16 alarms; /* Register encoding, combined */ +}; + +static struct pci_dev *s_bridge; /* pointer to the (only) via686a */ + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_via686a_init(void); +static int __init via686a_cleanup(void); + +static int via686a_attach_adapter(struct i2c_adapter *adapter); +static int via686a_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int via686a_detach_client(struct i2c_client *client); +static int via686a_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void via686a_inc_use(struct i2c_client *client); +static void via686a_dec_use(struct i2c_client *client); + +static int via686a_read_value(struct i2c_client *client, u8 register); +static void via686a_write_value(struct i2c_client *client, u8 register, + u8 value); +static void via686a_update_client(struct i2c_client *client); +static void via686a_init_client(struct i2c_client *client); +static int via686a_find(int *address); + + +static void via686a_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void via686a_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void via686a_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void via686a_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void via686a_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); + +static int via686a_id = 0; + +/* The driver. I choose to use type i2c_driver, as at is identical to both + smbus_driver and isa_driver, and clients could be of either kind */ +static struct i2c_driver via686a_driver = { + /* name */ "VIA 686A", + /* id */ I2C_DRIVERID_VIA686A, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &via686a_attach_adapter, + /* detach_client */ &via686a_detach_client, + /* command */ &via686a_command, + /* inc_use */ &via686a_inc_use, + /* dec_use */ &via686a_dec_use +}; + +/* Used by via686a_init/cleanup */ +static int __initdata via686a_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected VIA686A. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ +static ctl_table via686a_dir_table_template[] = { + {VIA686A_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_in}, + {VIA686A_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_fan}, + {VIA686A_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_fan}, + {VIA686A_SYSCTL_TEMP, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &via686a_temp}, + {VIA686A_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp}, + {VIA686A_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_temp}, + {VIA686A_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_fan_div}, + {VIA686A_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, + &i2c_proc_real, &i2c_sysctl_real, NULL, &via686a_alarms}, + {0} +}; + +static inline int via686a_read_value(struct i2c_client *client, u8 reg) +{ + return (inb_p(client->addr + reg)); +} + +static inline void via686a_write_value(struct i2c_client *client, u8 reg, + u8 value) +{ + outb_p(value, client->addr + reg); +} + +/* This is called when the module is loaded */ +int via686a_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, via686a_detect); +} + +/* Locate chip and get correct base address */ +int via686a_find(int *address) +{ + u16 val; + + if (!pci_present()) + return -ENODEV; + + if (!(s_bridge = pci_find_device(PCI_VENDOR_ID_VIA, + PCI_DEVICE_ID_VIA_82C686_4, + NULL))) return -ENODEV; + + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, VIA686A_BASE_REG, &val)) + return -ENODEV; + *address = val & ~(VIA686A_EXTENT - 1); + if (*address == 0 && force_addr == 0) { + printk("via686a.o: base address not set - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + if (force_addr) + *address = force_addr; /* so detect will get called */ + + return 0; +} + +int via686a_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i; + struct i2c_client *new_client; + struct via686a_data *data; + int err = 0; + const char *type_name = "via686a"; + u16 val; + + /* Make sure we are probing the ISA bus!! */ + if (!i2c_is_isa_adapter(adapter)) { + printk + ("via686a.o: via686a_detect called for an I2C bus adapter?!?\n"); + return 0; + } + + if(force_addr) + address = force_addr & ~(VIA686A_EXTENT - 1); + if (check_region(address, VIA686A_EXTENT)) { + printk("via686a.o: region 0x%x already in use!\n", + address); + return -ENODEV; + } + + if(force_addr) { + printk("via686a.o: forcing ISA address 0x%04X\n", address); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(s_bridge, VIA686A_BASE_REG, address)) + return -ENODEV; + } + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(s_bridge, VIA686A_ENABLE_REG, &val)) + return -ENODEV; + if (!(val & 0x0001)) { + printk("via686a.o: enabling sensors\n"); + if (PCIBIOS_SUCCESSFUL != + pci_write_config_word(s_bridge, VIA686A_ENABLE_REG, + val | 0x0001)) + return -ENODEV; + } + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct via686a_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct via686a_data *) (new_client + 1); + new_client->addr = address; + init_MUTEX(&data->lock); + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &via686a_driver; + new_client->flags = 0; + + /* Reserve the ISA region */ + request_region(address, VIA686A_EXTENT, "via686a-sensors"); + + /* Fill in the remaining client fields and put into the global list */ + strcpy(new_client->name, "Via 686A Integrated Sensors"); + + new_client->id = via686a_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry((struct i2c_client *) new_client, + type_name, + via686a_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR4; + } + data->sysctl_id = i; + + /* Initialize the VIA686A chip */ + via686a_init_client(new_client); + return 0; + + ERROR4: + i2c_detach_client(new_client); + ERROR3: + release_region(address, VIA686A_EXTENT); + kfree(new_client); + ERROR0: + return err; +} + +int via686a_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct via686a_data *) + (client->data))->sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + ("via686a.o: Client deregistration failed, client not detached.\n"); + return err; + } + + release_region(client->addr, VIA686A_EXTENT); + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int via686a_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void via686a_inc_use(struct i2c_client *client) +{ + MOD_INC_USE_COUNT; +} + +void via686a_dec_use(struct i2c_client *client) +{ + MOD_DEC_USE_COUNT; +} + +/* Called when we have found a new VIA686A. Set limits, etc. */ +void via686a_init_client(struct i2c_client *client) +{ + int i; + + /* Reset the device */ + via686a_write_value(client, VIA686A_REG_CONFIG, 0x80); + + /* Have to wait for reset to complete or else the following + initializations won't work reliably. The delay was arrived at + empirically, the datasheet doesn't tell you. + Waiting for the reset bit to clear doesn't work, it + clears in about 2-4 udelays and that isn't nearly enough. */ + udelay(50); + + via686a_write_value(client, VIA686A_REG_IN_MIN(0), + IN_TO_REG(VIA686A_INIT_IN_MIN_0, 0)); + via686a_write_value(client, VIA686A_REG_IN_MAX(0), + IN_TO_REG(VIA686A_INIT_IN_MAX_0, 0)); + via686a_write_value(client, VIA686A_REG_IN_MIN(1), + IN_TO_REG(VIA686A_INIT_IN_MIN_1, 1)); + via686a_write_value(client, VIA686A_REG_IN_MAX(1), + IN_TO_REG(VIA686A_INIT_IN_MAX_1, 1)); + via686a_write_value(client, VIA686A_REG_IN_MIN(2), + IN_TO_REG(VIA686A_INIT_IN_MIN_2, 2)); + via686a_write_value(client, VIA686A_REG_IN_MAX(2), + IN_TO_REG(VIA686A_INIT_IN_MAX_2, 2)); + via686a_write_value(client, VIA686A_REG_IN_MIN(3), + IN_TO_REG(VIA686A_INIT_IN_MIN_3, 3)); + via686a_write_value(client, VIA686A_REG_IN_MAX(3), + IN_TO_REG(VIA686A_INIT_IN_MAX_3, 3)); + via686a_write_value(client, VIA686A_REG_IN_MIN(4), + IN_TO_REG(VIA686A_INIT_IN_MIN_4, 4)); + via686a_write_value(client, VIA686A_REG_IN_MAX(4), + IN_TO_REG(VIA686A_INIT_IN_MAX_4, 4)); + via686a_write_value(client, VIA686A_REG_FAN_MIN(1), + FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2)); + via686a_write_value(client, VIA686A_REG_FAN_MIN(2), + FAN_TO_REG(VIA686A_INIT_FAN_MIN, 2)); + for (i = 1; i <= 3; i++) { + via686a_write_value(client, VIA686A_REG_TEMP_OVER(i), + TEMP_TO_REG(VIA686A_INIT_TEMP_OVER)); + via686a_write_value(client, VIA686A_REG_TEMP_HYST(i), + TEMP_TO_REG(VIA686A_INIT_TEMP_HYST)); + } + + /* Start monitoring */ + via686a_write_value(client, VIA686A_REG_CONFIG, 0x01); + + /* Cofigure temp interrupt mode for continuous-interrupt operation */ + via686a_write_value(client, VIA686A_REG_TEMP_MODE, + via686a_read_value(client, VIA686A_REG_TEMP_MODE) & + !(VIA686A_TEMP_MODE_MASK | VIA686A_TEMP_MODE_CONTINUOUS)); +} + +void via686a_update_client(struct i2c_client *client) +{ + struct via686a_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + + for (i = 0; i <= 4; i++) { + data->in[i] = + via686a_read_value(client, VIA686A_REG_IN(i)); + data->in_min[i] = via686a_read_value(client, + VIA686A_REG_IN_MIN + (i)); + data->in_max[i] = + via686a_read_value(client, VIA686A_REG_IN_MAX(i)); + } + for (i = 1; i <= 2; i++) { + data->fan[i - 1] = + via686a_read_value(client, VIA686A_REG_FAN(i)); + data->fan_min[i - 1] = via686a_read_value(client, + VIA686A_REG_FAN_MIN(i)); + } + for (i = 1; i <= 3; i++) { + data->temp[i - 1] = via686a_read_value(client, + VIA686A_REG_TEMP(i)) << 2; + data->temp_over[i - 1] = + via686a_read_value(client, + VIA686A_REG_TEMP_OVER(i)); + data->temp_hyst[i - 1] = + via686a_read_value(client, + VIA686A_REG_TEMP_HYST(i)); + } + /* add in lower 2 bits + temp1 uses bits 7-6 of VIA686A_REG_TEMP_LOW1 + temp2 uses bits 5-4 of VIA686A_REG_TEMP_LOW23 + temp3 uses bits 7-6 of VIA686A_REG_TEMP_LOW23 + */ + data->temp[0] |= (via686a_read_value(client, + VIA686A_REG_TEMP_LOW1) + & 0xc0) >> 6; + data->temp[1] |= + (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) & + 0x30) >> 4; + data->temp[2] |= + (via686a_read_value(client, VIA686A_REG_TEMP_LOW23) & + 0xc0) >> 6; + + i = via686a_read_value(client, VIA686A_REG_FANDIV); + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = i >> 6; + data->alarms = + via686a_read_value(client, + VIA686A_REG_ALARM1) | + (via686a_read_value(client, VIA686A_REG_ALARM2) << 8); + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void via686a_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct via686a_data *data = client->data; + int nr = ctl_name - VIA686A_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr], nr); + results[1] = IN_FROM_REG(data->in_max[nr], nr); + results[2] = IN_FROM_REG(data->in[nr], nr); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0], nr); + via686a_write_value(client, VIA686A_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1], nr); + via686a_write_value(client, VIA686A_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void via686a_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct via686a_data *data = client->data; + int nr = ctl_name - VIA686A_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data->fan_div + [nr - 1])); + results[1] = + FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = FAN_TO_REG(results[0], + DIV_FROM_REG(data-> + fan_div[nr -1])); + via686a_write_value(client, + VIA686A_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } + } +} + +void via686a_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct via686a_data *data = client->data; + int nr = ctl_name - VIA686A_SYSCTL_TEMP; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over[nr]); + results[1] = TEMP_FROM_REG(data->temp_hyst[nr]); + results[2] = TEMP_FROM_REG10(data->temp[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over[nr] = TEMP_TO_REG(results[0]); + via686a_write_value(client, + VIA686A_REG_TEMP_OVER(nr + 1), + data->temp_over[nr]); + } + if (*nrels_mag >= 2) { + data->temp_hyst[nr] = TEMP_TO_REG(results[1]); + via686a_write_value(client, + VIA686A_REG_TEMP_HYST(nr + 1), + data->temp_hyst[nr]); + } + } +} + +void via686a_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct via686a_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void via686a_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct via686a_data *data = client->data; + int old; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + via686a_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = via686a_read_value(client, VIA686A_REG_FANDIV); + if (*nrels_mag >= 2) { + data->fan_div[1] = DIV_TO_REG(results[1]); + old = (old & 0x3f) | (data->fan_div[1] << 6); + } + if (*nrels_mag >= 1) { + data->fan_div[0] = DIV_TO_REG(results[0]); + old = (old & 0xcf) | (data->fan_div[0] << 4); + via686a_write_value(client, VIA686A_REG_FANDIV, + old); + } + } +} + +int __init sensors_via686a_init(void) +{ + int res, addr; + + printk("via686a.o version %s (%s)\n", LM_VERSION, LM_DATE); + via686a_initialized = 0; + + if (via686a_find(&addr)) { + printk("via686a.o: No Via 686A sensors found.\n"); + return -ENODEV; + } + normal_isa[0] = addr; + + if ((res = i2c_add_driver(&via686a_driver))) { + printk("via686a.o: Driver registration failed.\n"); + via686a_cleanup(); + return res; + } + via686a_initialized++; + return 0; +} + +int __init via686a_cleanup(void) +{ + int res; + + if (via686a_initialized >= 1) { + if ((res = i2c_del_driver(&via686a_driver))) { + printk + ("via686a.o: Driver deregistration failed.\n"); + return res; + } + via686a_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR + ("Kyösti Mälkki , Mark Studebaker , Bob Dougherty "); +MODULE_DESCRIPTION("VIA 686A Sensor device"); + +int init_module(void) +{ + return sensors_via686a_init(); +} + +int cleanup_module(void) +{ + return via686a_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/w83781d.c Mon Mar 25 15:28:41 CET 2002 +++ linux/drivers/sensors/w83781d.c Mon Mar 25 15:28:41 CET 2002 @@ -0,0 +1,2033 @@ +/* + w83781d.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2001 Frodo Looijaard , + Philip Edelbrock , + and Mark Studebaker + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + Supports following chips: + + Chip #vin #fanin #pwm #temp wchipid vendid i2c ISA + as99127f 7 3 1? 3 0x20 0x12c3 yes no + w83781d 7 3 0 3 0x10 0x5ca3 yes yes + w83627hf 9 3 2 3 0x20 0x5ca3 yes yes(LPC) + w83782d 9 3 2-4 3 0x30 0x5ca3 yes yes + w83783s 5-6 3 2 1-2 0x40 0x5ca3 yes no + w83697hf 8 2 2 2 0x60 0x5ca3 no yes(LPC) + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20020322" +#define LM_VERSION "2.6.3" +#include +#include + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)) || \ + (LINUX_VERSION_CODE == KERNEL_VERSION(2,3,0)) +#define init_MUTEX(s) do { *(s) = MUTEX; } while(0) +#endif + +#ifndef THIS_MODULE +#define THIS_MODULE NULL +#endif + +/* RT Table support #defined so we can take it out if it gets bothersome */ +#define W83781D_RT 1 + +/* Addresses to scan */ +static unsigned short normal_i2c[] = { SENSORS_I2C_END }; +static unsigned short normal_i2c_range[] = { 0x20, 0x2f, SENSORS_I2C_END }; +static unsigned int normal_isa[] = { 0x0290, SENSORS_ISA_END }; +static unsigned int normal_isa_range[] = { SENSORS_ISA_END }; + +/* Insmod parameters */ +SENSORS_INSMOD_6(w83781d, w83782d, w83783s, w83627hf, as99127f, w83697hf); +SENSORS_MODULE_PARM(force_subclients, "List of subclient addresses: " \ + "{bus, clientaddr, subclientaddr1, subclientaddr2}"); + +static int init = 1; +MODULE_PARM(init, "i"); +MODULE_PARM_DESC(init, "Set to zero to bypass chip initialization"); + +/* Constants specified below */ + +/* Length of ISA address segment */ +#define W83781D_EXTENT 8 + +/* Where are the ISA address/data registers relative to the base address */ +#define W83781D_ADDR_REG_OFFSET 5 +#define W83781D_DATA_REG_OFFSET 6 + +/* The W83781D registers */ +/* The W83782D registers for nr=7,8 are in bank 5 */ +#define W83781D_REG_IN_MAX(nr) ((nr < 7) ? (0x2b + (nr) * 2) : \ + (0x554 + (((nr) - 7) * 2))) +#define W83781D_REG_IN_MIN(nr) ((nr < 7) ? (0x2c + (nr) * 2) : \ + (0x555 + (((nr) - 7) * 2))) +#define W83781D_REG_IN(nr) ((nr < 7) ? (0x20 + (nr)) : \ + (0x550 + (nr) - 7)) + +#define W83781D_REG_FAN_MIN(nr) (0x3a + (nr)) +#define W83781D_REG_FAN(nr) (0x27 + (nr)) + +#define W83781D_REG_TEMP2 0x0150 +#define W83781D_REG_TEMP3 0x0250 +#define W83781D_REG_TEMP2_HYST 0x153 +#define W83781D_REG_TEMP3_HYST 0x253 +#define W83781D_REG_TEMP2_CONFIG 0x152 +#define W83781D_REG_TEMP3_CONFIG 0x252 +#define W83781D_REG_TEMP2_OVER 0x155 +#define W83781D_REG_TEMP3_OVER 0x255 + +#define W83781D_REG_TEMP 0x27 +#define W83781D_REG_TEMP_OVER 0x39 +#define W83781D_REG_TEMP_HYST 0x3A +#define W83781D_REG_BANK 0x4E + +#define W83781D_REG_CONFIG 0x40 +#define W83781D_REG_ALARM1 0x41 +#define W83781D_REG_ALARM2 0x42 +#define W83781D_REG_ALARM3 0x450 /* not on W83781D */ + +#define W83781D_REG_BEEP_CONFIG 0x4D +#define W83781D_REG_BEEP_INTS1 0x56 +#define W83781D_REG_BEEP_INTS2 0x57 +#define W83781D_REG_BEEP_INTS3 0x453 /* not on W83781D */ + +#define W83781D_REG_VID_FANDIV 0x47 + +#define W83781D_REG_CHIPID 0x49 +#define W83781D_REG_WCHIPID 0x58 +#define W83781D_REG_CHIPMAN 0x4F +#define W83781D_REG_PIN 0x4B + +/* 782D/783S only */ +#define W83781D_REG_VBAT 0x5D + +/* PWM 782D (1-4) and 783S (1-2) only */ +#define W83781D_REG_PWM1 0x5B /* 782d and 783s/627hf datasheets disagree */ + /* on which is which; */ +#define W83781D_REG_PWM2 0x5A /* We follow the 782d convention here, */ + /* However 782d is probably wrong. */ +#define W83781D_REG_PWM3 0x5E +#define W83781D_REG_PWM4 0x5F +#define W83781D_REG_PWMCLK12 0x5C +#define W83781D_REG_PWMCLK34 0x45C +static const u8 regpwm[] = { W83781D_REG_PWM1, W83781D_REG_PWM2, + W83781D_REG_PWM3, W83781D_REG_PWM4 +}; +#define W83781D_REG_PWM(nr) (regpwm[(nr) - 1]) + +#define W83781D_REG_I2C_ADDR 0x48 +#define W83781D_REG_I2C_SUBADDR 0x4A + +/* The following are undocumented in the data sheets however we + received the information in an email from Winbond tech support */ +/* Sensor selection - not on 781d */ +#define W83781D_REG_SCFG1 0x5D +static const u8 BIT_SCFG1[] = { 0x02, 0x04, 0x08 }; +#define W83781D_REG_SCFG2 0x59 +static const u8 BIT_SCFG2[] = { 0x10, 0x20, 0x40 }; +#define W83781D_DEFAULT_BETA 3435 + +/* RT Table registers */ +#define W83781D_REG_RT_IDX 0x50 +#define W83781D_REG_RT_VAL 0x51 + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +#define IN_TO_REG(val) (SENSORS_LIMIT((((val) * 10 + 8)/16),0,255)) +#define IN_FROM_REG(val) (((val) * 16) / 10) + +extern inline u8 FAN_TO_REG(long rpm, int div) +{ + if (rpm == 0) + return 255; + rpm = SENSORS_LIMIT(rpm, 1, 1000000); + return SENSORS_LIMIT((1350000 + rpm * div / 2) / (rpm * div), 1, + 254); +} + +#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div))) + +#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-5)/10):\ + ((val)+5)/10),0,255)) +#define TEMP_FROM_REG(val) (((val)>0x80?(val)-0x100:(val))*10) + +#define TEMP_ADD_TO_REG(val) (SENSORS_LIMIT(((((val) + 2) / 5) << 7),\ + 0,0xffff)) +#define TEMP_ADD_FROM_REG(val) (((val) >> 7) * 5) + +#define AS99127_TEMP_ADD_TO_REG(val) (SENSORS_LIMIT((((((val) + 2)*4)/10) \ + << 7),0,0xffff)) +#define AS99127_TEMP_ADD_FROM_REG(val) ((((val) >> 7) * 10) / 4) + +#define VID_FROM_REG(val) ((val)==0x1f?0:(val)>=0x10?510-(val)*10:\ + 205-(val)*5) +#define ALARMS_FROM_REG(val) (val) +#define PWM_FROM_REG(val) (val) +#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255)) +#define BEEPS_FROM_REG(val) (val) +#define BEEPS_TO_REG(val) ((val) & 0xffffff) + +#define BEEP_ENABLE_TO_REG(val) ((val)?1:0) +#define BEEP_ENABLE_FROM_REG(val) ((val)?1:0) + +#define DIV_FROM_REG(val) (1 << (val)) + +extern inline u8 DIV_TO_REG(long val, enum chips type) +{ + int i; + val = SENSORS_LIMIT(val, 1, + ((type == w83781d || type == as99127f) ? 8 : 128)) >> 1; + for (i = 0; i < 6; i++) { + if (val == 0) + break; + val >>= 1; + } + return ((u8) i); +} + +/* Initial limits */ +#define W83781D_INIT_IN_0 (vid==350?280:vid) +#define W83781D_INIT_IN_1 (vid==350?280:vid) +#define W83781D_INIT_IN_2 330 +#define W83781D_INIT_IN_3 (((500) * 100)/168) +#define W83781D_INIT_IN_4 (((1200) * 10)/38) +#define W83781D_INIT_IN_5 (((-1200) * -604)/2100) +#define W83781D_INIT_IN_6 (((-500) * -604)/909) +#define W83781D_INIT_IN_7 (((500) * 100)/168) +#define W83781D_INIT_IN_8 300 +/* Initial limits for 782d/783s negative voltages */ +/* Note level shift. Change min/max below if you change these. */ +#define W83782D_INIT_IN_5 ((((-1200) + 1491) * 100)/514) +#define W83782D_INIT_IN_6 ((( (-500) + 771) * 100)/314) + +#define W83781D_INIT_IN_PERCENTAGE 10 + +#define W83781D_INIT_IN_MIN_0 \ + (W83781D_INIT_IN_0 - W83781D_INIT_IN_0 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_0 \ + (W83781D_INIT_IN_0 + W83781D_INIT_IN_0 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_1 \ + (W83781D_INIT_IN_1 - W83781D_INIT_IN_1 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_1 \ + (W83781D_INIT_IN_1 + W83781D_INIT_IN_1 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_2 \ + (W83781D_INIT_IN_2 - W83781D_INIT_IN_2 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_2 \ + (W83781D_INIT_IN_2 + W83781D_INIT_IN_2 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_3 \ + (W83781D_INIT_IN_3 - W83781D_INIT_IN_3 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_3 \ + (W83781D_INIT_IN_3 + W83781D_INIT_IN_3 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_4 \ + (W83781D_INIT_IN_4 - W83781D_INIT_IN_4 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_4 \ + (W83781D_INIT_IN_4 + W83781D_INIT_IN_4 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_5 \ + (W83781D_INIT_IN_5 - W83781D_INIT_IN_5 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_5 \ + (W83781D_INIT_IN_5 + W83781D_INIT_IN_5 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_6 \ + (W83781D_INIT_IN_6 - W83781D_INIT_IN_6 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_6 \ + (W83781D_INIT_IN_6 + W83781D_INIT_IN_6 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_7 \ + (W83781D_INIT_IN_7 - W83781D_INIT_IN_7 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_7 \ + (W83781D_INIT_IN_7 + W83781D_INIT_IN_7 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MIN_8 \ + (W83781D_INIT_IN_8 - W83781D_INIT_IN_8 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +#define W83781D_INIT_IN_MAX_8 \ + (W83781D_INIT_IN_8 + W83781D_INIT_IN_8 * W83781D_INIT_IN_PERCENTAGE \ + / 100) +/* Initial limits for 782d/783s negative voltages */ +/* These aren't direct multiples because of level shift */ +/* Beware going negative - check */ +#define W83782D_INIT_IN_MIN_5_TMP \ + (((-1200 * (100 + W83781D_INIT_IN_PERCENTAGE)) + (1491 * 100))/514) +#define W83782D_INIT_IN_MIN_5 \ + ((W83782D_INIT_IN_MIN_5_TMP > 0) ? W83782D_INIT_IN_MIN_5_TMP : 0) +#define W83782D_INIT_IN_MAX_5 \ + (((-1200 * (100 - W83781D_INIT_IN_PERCENTAGE)) + (1491 * 100))/514) +#define W83782D_INIT_IN_MIN_6_TMP \ + ((( -500 * (100 + W83781D_INIT_IN_PERCENTAGE)) + (771 * 100))/314) +#define W83782D_INIT_IN_MIN_6 \ + ((W83782D_INIT_IN_MIN_6_TMP > 0) ? W83782D_INIT_IN_MIN_6_TMP : 0) +#define W83782D_INIT_IN_MAX_6 \ + ((( -500 * (100 - W83781D_INIT_IN_PERCENTAGE)) + (771 * 100))/314) + +#define W83781D_INIT_FAN_MIN_1 3000 +#define W83781D_INIT_FAN_MIN_2 3000 +#define W83781D_INIT_FAN_MIN_3 3000 + +#define W83781D_INIT_TEMP_OVER 600 +#define W83781D_INIT_TEMP_HYST 500 +#define W83781D_INIT_TEMP2_OVER 600 +#define W83781D_INIT_TEMP2_HYST 500 +#define W83781D_INIT_TEMP3_OVER 600 +#define W83781D_INIT_TEMP3_HYST 500 + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* There are some complications in a module like this. First off, W83781D chips + may be both present on the SMBus and the ISA bus, and we have to handle + those cases separately at some places. Second, there might be several + W83781D chips available (well, actually, that is probably never done; but + it is a clean illustration of how to handle a case like that). Finally, + a specific chip may be attached to *both* ISA and SMBus, and we would + not like to detect it double. Fortunately, in the case of the W83781D at + least, a register tells us what SMBus address we are on, so that helps + a bit - except if there could be more than one SMBus. Groan. No solution + for this yet. */ + +/* This module may seem overly long and complicated. In fact, it is not so + bad. Quite a lot of bookkeeping is done. A real driver can often cut + some corners. */ + +/* For each registered W83781D, we need to keep some data in memory. That + data is pointed to by w83781d_list[NR]->data. The structure itself is + dynamically allocated, at the same time when a new w83781d client is + allocated. */ +struct w83781d_data { + struct semaphore lock; + int sysctl_id; + enum chips type; + + struct semaphore update_lock; + char valid; /* !=0 if following fields are valid */ + unsigned long last_updated; /* In jiffies */ + + struct i2c_client *lm75; /* for secondary I2C addresses */ + /* pointer to array of 2 subclients */ + + u8 in[9]; /* Register value - 8 & 9 for 782D only */ + u8 in_max[9]; /* Register value - 8 & 9 for 782D only */ + u8 in_min[9]; /* Register value - 8 & 9 for 782D only */ + u8 fan[3]; /* Register value */ + u8 fan_min[3]; /* Register value */ + u8 temp; + u8 temp_over; /* Register value */ + u8 temp_hyst; /* Register value */ + u16 temp_add[2]; /* Register value */ + u16 temp_add_over[2]; /* Register value */ + u16 temp_add_hyst[2]; /* Register value */ + u8 fan_div[3]; /* Register encoding, shifted right */ + u8 vid; /* Register encoding, combined */ + u32 alarms; /* Register encoding, combined */ + u32 beeps; /* Register encoding, combined */ + u8 beep_enable; /* Boolean */ + u8 pwm[4]; /* Register value */ + u16 sens[3]; /* 782D/783S only. + 1 = pentium diode; 2 = 3904 diode; + 3000-5000 = thermistor beta. + Default = 3435. + Other Betas unimplemented */ +#ifdef W83781D_RT + u8 rt[3][32]; /* Register value */ +#endif +}; + + +#ifdef MODULE +static +#else +extern +#endif +int __init sensors_w83781d_init(void); +static int __init w83781d_cleanup(void); + +static int w83781d_attach_adapter(struct i2c_adapter *adapter); +static int w83781d_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind); +static int w83781d_detach_client(struct i2c_client *client); +static int w83781d_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void w83781d_inc_use(struct i2c_client *client); +static void w83781d_dec_use(struct i2c_client *client); + +static int w83781d_read_value(struct i2c_client *client, u16 register); +static int w83781d_write_value(struct i2c_client *client, u16 register, + u16 value); +static void w83781d_update_client(struct i2c_client *client); +static void w83781d_init_client(struct i2c_client *client); + + +static void w83781d_in(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_fan(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_temp(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_temp_add(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_vid(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_alarms(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_beep(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_pwm(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +static void w83781d_sens(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +#ifdef W83781D_RT +static void w83781d_rt(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results); +#endif +static u16 swap_bytes(u16 val); + +static int w83781d_id = 0; + +static struct i2c_driver w83781d_driver = { + /* name */ "W83781D sensor driver", + /* id */ I2C_DRIVERID_W83781D, + /* flags */ I2C_DF_NOTIFY, + /* attach_adapter */ &w83781d_attach_adapter, + /* detach_client */ &w83781d_detach_client, + /* command */ &w83781d_command, + /* inc_use */ &w83781d_inc_use, + /* dec_use */ &w83781d_dec_use +}; + +/* Used by w83781d_init/cleanup */ +static int __initdata w83781d_initialized = 0; + +/* The /proc/sys entries */ +/* These files are created for each detected chip. This is just a template; + though at first sight, you might think we could use a statically + allocated list, we need some way to get back to the parent - which + is done through one of the 'extra' fields which are initialized + when a new copy is allocated. */ + +/* just a guess - no datasheet */ +static ctl_table as99127f_dir_table_template[] = { + {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp}, + {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_vid}, + {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan_div}, + {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_alarms}, + {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_beep}, + {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {0} +}; + +static ctl_table w83781d_dir_table_template[] = { + {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp}, + {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_vid}, + {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan_div}, + {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_alarms}, + {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_beep}, +#ifdef W83781D_RT + {W83781D_SYSCTL_RT1, "rt1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_rt}, + {W83781D_SYSCTL_RT2, "rt2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_rt}, + {W83781D_SYSCTL_RT3, "rt3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_rt}, +#endif + {0} +}; + +/* without pwm3-4 */ +static ctl_table w83782d_isa_dir_table_template[] = { + {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp}, + {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_vid}, + {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan_div}, + {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_alarms}, + {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_beep}, + {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {0} +}; + +/* with pwm3-4 */ +static ctl_table w83782d_i2c_dir_table_template[] = { + {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp}, + {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_TEMP3, "temp3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_vid}, + {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan_div}, + {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_alarms}, + {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_beep}, + {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM3, "pwm3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM4, "pwm4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {W83781D_SYSCTL_SENS3, "sensor3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {0} +}; + +static ctl_table w83783s_dir_table_template[] = { + {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + /* no in1 to maintain compatibility with 781d and 782d. */ + {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN3, "fan3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp}, + {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_VID, "vid", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_vid}, + {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan_div}, + {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_alarms}, + {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_beep}, + {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {0} +}; + +/* similar to w83782d but no fan3, no vid */ +static ctl_table w83697hf_dir_table_template[] = { + {W83781D_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + /* no in1 to maintain compatibility with 781d and 782d. */ + {W83781D_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN7, "in7", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_IN8, "in8", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_in}, + {W83781D_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_FAN2, "fan2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan}, + {W83781D_SYSCTL_TEMP1, "temp1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp}, + {W83781D_SYSCTL_TEMP2, "temp2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_temp_add}, + {W83781D_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_fan_div}, + {W83781D_SYSCTL_ALARMS, "alarms", NULL, 0, 0444, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_alarms}, + {W83781D_SYSCTL_BEEP, "beep", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_beep}, + {W83781D_SYSCTL_PWM1, "pwm1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_PWM2, "pwm2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_pwm}, + {W83781D_SYSCTL_SENS1, "sensor1", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {W83781D_SYSCTL_SENS2, "sensor2", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &w83781d_sens}, + {0} +}; + + +/* This function is called when: + * w83781d_driver is inserted (when this module is loaded), for each + available adapter + * when a new adapter is inserted (and w83781d_driver is still present) */ +int w83781d_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_detect(adapter, &addr_data, w83781d_detect); +} + +int w83781d_detect(struct i2c_adapter *adapter, int address, + unsigned short flags, int kind) +{ + int i, val1, val2, id; + struct i2c_client *new_client; + struct w83781d_data *data; + int err = 0; + const char *type_name = ""; + const char *client_name = ""; + int is_isa = i2c_is_isa_adapter(adapter); + enum vendor { winbond, asus } vendid; + + if (!is_isa + && !i2c_check_functionality(adapter, + I2C_FUNC_SMBUS_BYTE_DATA)) goto + ERROR0; + + if (is_isa) { + if (check_region(address, W83781D_EXTENT)) + goto ERROR0; + } + + /* Probe whether there is anything available on this address. Already + done for SMBus clients */ + if (kind < 0) { + if (is_isa) { + +#define REALLY_SLOW_IO + /* We need the timeouts for at least some LM78-like chips. But only + if we read 'undefined' registers. */ + i = inb_p(address + 1); + if (inb_p(address + 2) != i) + goto ERROR0; + if (inb_p(address + 3) != i) + goto ERROR0; + if (inb_p(address + 7) != i) + goto ERROR0; +#undef REALLY_SLOW_IO + + /* Let's just hope nothing breaks here */ + i = inb_p(address + 5) & 0x7f; + outb_p(~i & 0x7f, address + 5); + if ((inb_p(address + 5) & 0x7f) != (~i & 0x7f)) { + outb_p(i, address + 5); + return 0; + } + } + } + + /* OK. For now, we presume we have a valid client. We now create the + client structure, even though we cannot fill it completely yet. + But it allows us to access w83781d_{read,write}_value. */ + + if (!(new_client = kmalloc(sizeof(struct i2c_client) + + sizeof(struct w83781d_data), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR0; + } + + data = (struct w83781d_data *) (new_client + 1); + new_client->addr = address; + init_MUTEX(&data->lock); + new_client->data = data; + new_client->adapter = adapter; + new_client->driver = &w83781d_driver; + new_client->flags = 0; + + /* Now, we do the remaining detection. */ + + /* The w8378?d may be stuck in some other bank than bank 0. This may + make reading other information impossible. Specify a force=... or + force_*=... parameter, and the Winbond will be reset to the right + bank. */ + if (kind < 0) { + if (w83781d_read_value(new_client, W83781D_REG_CONFIG) & + 0x80) goto ERROR1; + val1 = w83781d_read_value(new_client, W83781D_REG_BANK); + val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN); + /* Check for Winbond or Asus ID if in bank 0 */ + if ((!(val1 & 0x07)) && + (((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3)) + || ((val1 & 0x80) && (val2 != 0x5c) + && (val2 != 0x12)))) goto ERROR1; + /* If Winbond SMBus, check address at 0x48. Asus doesn't support */ + if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) || + ((val1 & 0x80) && (val2 == 0x5c)))) { + if (w83781d_read_value + (new_client, W83781D_REG_I2C_ADDR) != address) + goto ERROR1; + } + } + + /* We have either had a force parameter, or we have already detected the + Winbond. Put it now into bank 0 and Vendor ID High Byte */ + w83781d_write_value(new_client, W83781D_REG_BANK, + (w83781d_read_value(new_client, + W83781D_REG_BANK) & 0x78) | + 0x80); + + /* Determine the chip type. */ + if (kind <= 0) { + /* get vendor ID */ + val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN); + if (val2 == 0x5c) + vendid = winbond; + else if (val2 == 0x12) + vendid = asus; + else + goto ERROR1; + /* mask off lower bit, not reliable */ + val1 = + w83781d_read_value(new_client, + W83781D_REG_WCHIPID) & 0xfe; + if (val1 == 0x10 && vendid == winbond) + kind = w83781d; + else if (val1 == 0x30 && vendid == winbond) + kind = w83782d; + else if (val1 == 0x40 && vendid == winbond && !is_isa) + kind = w83783s; + else if (val1 == 0x20 && vendid == winbond) + kind = w83627hf; + else if (val1 == 0x30 && vendid == asus && !is_isa) + kind = as99127f; + else if (val1 == 0x60 && vendid == winbond && is_isa) + kind = w83697hf; + else { + if (kind == 0) + printk + (KERN_WARNING "w83781d.o: Ignoring 'force' parameter for unknown chip at" + "adapter %d, address 0x%02x\n", + i2c_adapter_id(adapter), address); + goto ERROR1; + } + } + + if (kind == w83781d) { + type_name = "w83781d"; + client_name = "W83781D chip"; + } else if (kind == w83782d) { + type_name = "w83782d"; + client_name = "W83782D chip"; + } else if (kind == w83783s) { + type_name = "w83783s"; + client_name = "W83783S chip"; + } else if (kind == w83627hf) { + type_name = "w83627hf"; + client_name = "W83627HF chip"; + } else if (kind == as99127f) { + type_name = "as99127f"; + client_name = "AS99127F chip"; + } else if (kind == w83697hf) { + type_name = "w83697hf"; + client_name = "W83697HF chip"; + } else { +#ifdef DEBUG + printk(KERN_ERR "w83781d.o: Internal error: unknown kind (%d)?!?", + kind); +#endif + goto ERROR1; + } + + /* Reserve the ISA region */ + if (is_isa) + request_region(address, W83781D_EXTENT, type_name); + + /* Fill in the remaining client fields and put it into the global list */ + strcpy(new_client->name, client_name); + data->type = kind; + + new_client->id = w83781d_id++; + data->valid = 0; + init_MUTEX(&data->update_lock); + + /* Tell the I2C layer a new client has arrived */ + if ((err = i2c_attach_client(new_client))) + goto ERROR3; + + /* attach secondary i2c lm75-like clients */ + if (!is_isa) { + if (!(data->lm75 = kmalloc(2 * sizeof(struct i2c_client), + GFP_KERNEL))) { + err = -ENOMEM; + goto ERROR4; + } + id = i2c_adapter_id(adapter); + if(force_subclients[0] == id && force_subclients[1] == address) { + for(i = 2; i <= 3; i++) { + if(force_subclients[i] < 0x48 || + force_subclients[i] > 0x4f) { + printk(KERN_ERR "w83781d.o: Invalid subclient address %d; must be 0x48-0x4f\n", + force_subclients[i]); + goto ERROR5; + } + } + w83781d_write_value(new_client, + W83781D_REG_I2C_SUBADDR, + (force_subclients[2] & 0x07) | + ((force_subclients[3] & 0x07) <<4)); + data->lm75[0].addr = force_subclients[2]; + } else { + val1 = w83781d_read_value(new_client, + W83781D_REG_I2C_SUBADDR); + data->lm75[0].addr = 0x48 + (val1 & 0x07); + } + if (kind != w83783s) { + if(force_subclients[0] == id && + force_subclients[1] == address) { + data->lm75[1].addr = force_subclients[3]; + } else { + data->lm75[1].addr = 0x48 + ((val1 >> 4) & 0x07); + } + if(data->lm75[0].addr == data->lm75[1].addr) { + printk(KERN_ERR "w83781d.o: Duplicate addresses 0x%x for subclients.\n", + data->lm75[0].addr); + goto ERROR5; + } + } + if (kind == w83781d) + client_name = "W83781D subclient"; + else if (kind == w83782d) + client_name = "W83782D subclient"; + else if (kind == w83783s) + client_name = "W83783S subclient"; + else if (kind == w83627hf) + client_name = "W83627HF subclient"; + else if (kind == as99127f) + client_name = "AS99127F subclient"; + + for (i = 0; i <= 1; i++) { + data->lm75[i].data = NULL; /* store all data in w83781d */ + data->lm75[i].adapter = adapter; + data->lm75[i].driver = &w83781d_driver; + data->lm75[i].flags = 0; + strcpy(data->lm75[i].name, client_name); + data->lm75[i].id = w83781d_id++; + if ((err = i2c_attach_client(&(data->lm75[i])))) { + printk(KERN_ERR "w83781d.o: Subclient %d registration at address 0x%x failed.\n", + i, data->lm75[i].addr); + if (i == 1) + goto ERROR6; + goto ERROR5; + } + if (kind == w83783s) + break; + } + } else { + data->lm75 = NULL; + } + + /* Register a new directory entry with module sensors */ + if ((i = i2c_register_entry(new_client, + type_name, + (kind == as99127f) ? + as99127f_dir_table_template : + (kind == w83781d) ? + w83781d_dir_table_template : + (kind == w83783s) ? + w83783s_dir_table_template : + (kind == w83697hf) ? + w83697hf_dir_table_template : + (is_isa || kind == w83627hf) ? + w83782d_isa_dir_table_template : + w83782d_i2c_dir_table_template, + THIS_MODULE)) < 0) { + err = i; + goto ERROR7; + } + data->sysctl_id = i; + + /* Initialize the chip */ + w83781d_init_client(new_client); + return 0; + +/* OK, this is not exactly good programming practice, usually. But it is + very code-efficient in this case. */ + + ERROR7: + if (!is_isa) + i2c_detach_client(& + (((struct + w83781d_data *) (new_client->data))-> + lm75[1])); + ERROR6: + if (!is_isa) + i2c_detach_client(& + (((struct + w83781d_data *) (new_client->data))-> + lm75[0])); + ERROR5: + if (!is_isa) + kfree(((struct w83781d_data *) (new_client->data))->lm75); + ERROR4: + i2c_detach_client(new_client); + ERROR3: + if (is_isa) + release_region(address, W83781D_EXTENT); + ERROR1: + kfree(new_client); + ERROR0: + return err; +} + +int w83781d_detach_client(struct i2c_client *client) +{ + int err; + + i2c_deregister_entry(((struct w83781d_data *) (client->data))-> + sysctl_id); + + if ((err = i2c_detach_client(client))) { + printk + (KERN_ERR "w83781d.o: Client deregistration failed, client not detached.\n"); + return err; + } + + if(i2c_is_isa_client(client)) { + release_region(client->addr, W83781D_EXTENT); + } else { + i2c_detach_client(& + (((struct + w83781d_data *) (client->data))-> + lm75[0])); + if((((struct w83781d_data *) (client->data))->type) != w83783s) + i2c_detach_client(& + (((struct + w83781d_data *) (client->data))-> + lm75[1])); + kfree(((struct w83781d_data *) (client->data))->lm75); + } + kfree(client); + + return 0; +} + +/* No commands defined yet */ +int w83781d_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + return 0; +} + +void w83781d_inc_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void w83781d_dec_use(struct i2c_client *client) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +u16 swap_bytes(u16 val) +{ + return (val >> 8) | (val << 8); +} + +/* The SMBus locks itself, usually, but nothing may access the Winbond between + bank switches. ISA access must always be locked explicitly! + We ignore the W83781D BUSY flag at this moment - it could lead to deadlocks, + would slow down the W83781D access and should not be necessary. + There are some ugly typecasts here, but the good news is - they should + nowhere else be necessary! */ +int w83781d_read_value(struct i2c_client *client, u16 reg) +{ + int res, word_sized, bank; + struct i2c_client *cl; + + down(&(((struct w83781d_data *) (client->data))->lock)); + if (i2c_is_isa_client(client)) { + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x50) + || ((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + res = inb_p(client->addr + W83781D_DATA_REG_OFFSET); + if (word_sized) { + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + res = + (res << 8) + inb_p(client->addr + + W83781D_DATA_REG_OFFSET); + } + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } + } else { + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + res = i2c_smbus_read_byte_data(client, reg & 0xff); + } else { + /* switch to subclient */ + cl = + &(((struct w83781d_data *) (client->data))-> + lm75[bank - 1]); + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x50: /* TEMP */ + res = + swap_bytes(i2c_smbus_read_word_data + (cl, 0)); + break; + case 0x52: /* CONFIG */ + res = i2c_smbus_read_byte_data(cl, 1); + break; + case 0x53: /* HYST */ + res = + swap_bytes(i2c_smbus_read_word_data + (cl, 2)); + break; + case 0x55: /* OVER */ + default: + res = + swap_bytes(i2c_smbus_read_word_data + (cl, 3)); + break; + } + } + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + 0); + } + up(&(((struct w83781d_data *) (client->data))->lock)); + return res; +} + +int w83781d_write_value(struct i2c_client *client, u16 reg, u16 value) +{ + int word_sized, bank; + struct i2c_client *cl; + + down(&(((struct w83781d_data *) (client->data))->lock)); + if (i2c_is_isa_client(client)) { + word_sized = (((reg & 0xff00) == 0x100) + || ((reg & 0xff00) == 0x200)) + && (((reg & 0x00ff) == 0x53) + || ((reg & 0x00ff) == 0x55)); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(reg >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + } + outb_p(reg & 0xff, client->addr + W83781D_ADDR_REG_OFFSET); + if (word_sized) { + outb_p(value >> 8, + client->addr + W83781D_DATA_REG_OFFSET); + outb_p((reg & 0xff) + 1, + client->addr + W83781D_ADDR_REG_OFFSET); + } + outb_p(value & 0xff, + client->addr + W83781D_DATA_REG_OFFSET); + if (reg & 0xff00) { + outb_p(W83781D_REG_BANK, + client->addr + W83781D_ADDR_REG_OFFSET); + outb_p(0, client->addr + W83781D_DATA_REG_OFFSET); + } + } else { + bank = (reg >> 8) & 0x0f; + if (bank > 2) + /* switch banks */ + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + bank); + if (bank == 0 || bank > 2) { + i2c_smbus_write_byte_data(client, reg & 0xff, + value & 0xff); + } else { + /* switch to subclient */ + cl = &(((struct w83781d_data *) (client->data))-> + lm75[bank - 1]); + /* convert from ISA to LM75 I2C addresses */ + switch (reg & 0xff) { + case 0x52: /* CONFIG */ + i2c_smbus_write_byte_data(cl, 1, + value & 0xff); + break; + case 0x53: /* HYST */ + i2c_smbus_write_word_data(cl, 2, + swap_bytes(value)); + break; + case 0x55: /* OVER */ + i2c_smbus_write_word_data(cl, 3, + swap_bytes(value)); + break; + } + } + if (bank > 2) + i2c_smbus_write_byte_data(client, W83781D_REG_BANK, + 0); + } + up(&(((struct w83781d_data *) (client->data))->lock)); + return 0; +} + +/* Called when we have found a new W83781D. It should set limits, etc. */ +void w83781d_init_client(struct i2c_client *client) +{ + struct w83781d_data *data = client->data; + int vid, i; + int type = data->type; + u8 tmp; + + if(init && type != as99127f) { /* this resets registers we don't have + documentation for on the as99127f */ + /* save this register */ + i = w83781d_read_value(client, W83781D_REG_BEEP_CONFIG); + /* Reset all except Watchdog values and last conversion values + This sets fan-divs to 2, among others */ + w83781d_write_value(client, W83781D_REG_CONFIG, 0x80); + /* Restore the register and disable power-on abnormal beep. + This saves FAN 1/2/3 input/output values set by BIOS. */ + w83781d_write_value(client, W83781D_REG_BEEP_CONFIG, i | 0x80); + /* Disable master beep-enable (reset turns it on). + Individual beeps should be reset to off but for some reason + disabling this bit helps some people not get beeped */ + w83781d_write_value(client, W83781D_REG_BEEP_INTS2, 0); + } + + if (type != w83697hf) { + vid = w83781d_read_value(client, W83781D_REG_VID_FANDIV) & 0x0f; + vid |= + (w83781d_read_value(client, W83781D_REG_CHIPID) & 0x01) << 4; + vid = VID_FROM_REG(vid); + } + + if ((type != w83781d) && (type != as99127f)) { + tmp = w83781d_read_value(client, W83781D_REG_SCFG1); + for (i = 1; i <= 3; i++) { + if (!(tmp & BIT_SCFG1[i - 1])) { + data->sens[i - 1] = W83781D_DEFAULT_BETA; + } else { + if (w83781d_read_value + (client, + W83781D_REG_SCFG2) & BIT_SCFG2[i - 1]) + data->sens[i - 1] = 1; + else + data->sens[i - 1] = 2; + } + if ((type == w83783s || type == w83697hf) && (i == 2)) + break; + } + } +#ifdef W83781D_RT +/* + Fill up the RT Tables. + We assume that they are 32 bytes long, in order for temp 1-3. + Data sheet documentation is sparse. + We also assume that it is only for the 781D although I suspect + that the others support it as well.... +*/ + + if (init && type == w83781d) { + u16 k = 0; +/* + Auto-indexing doesn't seem to work... + w83781d_write_value(client,W83781D_REG_RT_IDX,0); +*/ + for (i = 0; i < 3; i++) { + int j; + for (j = 0; j < 32; j++) { + w83781d_write_value(client, + W83781D_REG_RT_IDX, + k++); + data->rt[i][j] = + w83781d_read_value(client, + W83781D_REG_RT_VAL); + } + } + } +#endif /* W83781D_RT */ + + if(init) { + w83781d_write_value(client, W83781D_REG_IN_MIN(0), + IN_TO_REG(W83781D_INIT_IN_MIN_0)); + w83781d_write_value(client, W83781D_REG_IN_MAX(0), + IN_TO_REG(W83781D_INIT_IN_MAX_0)); + if (type != w83783s && type != w83697hf) { + w83781d_write_value(client, W83781D_REG_IN_MIN(1), + IN_TO_REG(W83781D_INIT_IN_MIN_1)); + w83781d_write_value(client, W83781D_REG_IN_MAX(1), + IN_TO_REG(W83781D_INIT_IN_MAX_1)); + } + + w83781d_write_value(client, W83781D_REG_IN_MIN(2), + IN_TO_REG(W83781D_INIT_IN_MIN_2)); + w83781d_write_value(client, W83781D_REG_IN_MAX(2), + IN_TO_REG(W83781D_INIT_IN_MAX_2)); + w83781d_write_value(client, W83781D_REG_IN_MIN(3), + IN_TO_REG(W83781D_INIT_IN_MIN_3)); + w83781d_write_value(client, W83781D_REG_IN_MAX(3), + IN_TO_REG(W83781D_INIT_IN_MAX_3)); + w83781d_write_value(client, W83781D_REG_IN_MIN(4), + IN_TO_REG(W83781D_INIT_IN_MIN_4)); + w83781d_write_value(client, W83781D_REG_IN_MAX(4), + IN_TO_REG(W83781D_INIT_IN_MAX_4)); + if (type == w83781d || type == as99127f) { + w83781d_write_value(client, W83781D_REG_IN_MIN(5), + IN_TO_REG(W83781D_INIT_IN_MIN_5)); + w83781d_write_value(client, W83781D_REG_IN_MAX(5), + IN_TO_REG(W83781D_INIT_IN_MAX_5)); + } else { + w83781d_write_value(client, W83781D_REG_IN_MIN(5), + IN_TO_REG(W83782D_INIT_IN_MIN_5)); + w83781d_write_value(client, W83781D_REG_IN_MAX(5), + IN_TO_REG(W83782D_INIT_IN_MAX_5)); + } + if (type == w83781d || type == as99127f) { + w83781d_write_value(client, W83781D_REG_IN_MIN(6), + IN_TO_REG(W83781D_INIT_IN_MIN_6)); + w83781d_write_value(client, W83781D_REG_IN_MAX(6), + IN_TO_REG(W83781D_INIT_IN_MAX_6)); + } else { + w83781d_write_value(client, W83781D_REG_IN_MIN(6), + IN_TO_REG(W83782D_INIT_IN_MIN_6)); + w83781d_write_value(client, W83781D_REG_IN_MAX(6), + IN_TO_REG(W83782D_INIT_IN_MAX_6)); + } + if ((type == w83782d) || (type == w83627hf) || + (type == w83697hf)) { + w83781d_write_value(client, W83781D_REG_IN_MIN(7), + IN_TO_REG(W83781D_INIT_IN_MIN_7)); + w83781d_write_value(client, W83781D_REG_IN_MAX(7), + IN_TO_REG(W83781D_INIT_IN_MAX_7)); + w83781d_write_value(client, W83781D_REG_IN_MIN(8), + IN_TO_REG(W83781D_INIT_IN_MIN_8)); + w83781d_write_value(client, W83781D_REG_IN_MAX(8), + IN_TO_REG(W83781D_INIT_IN_MAX_8)); + w83781d_write_value(client, W83781D_REG_VBAT, + (w83781d_read_value(client, W83781D_REG_VBAT) | 0x01)); + } + w83781d_write_value(client, W83781D_REG_FAN_MIN(1), + FAN_TO_REG(W83781D_INIT_FAN_MIN_1, 2)); + w83781d_write_value(client, W83781D_REG_FAN_MIN(2), + FAN_TO_REG(W83781D_INIT_FAN_MIN_2, 2)); + if (type != w83697hf) { + w83781d_write_value(client, W83781D_REG_FAN_MIN(3), + FAN_TO_REG(W83781D_INIT_FAN_MIN_3, 2)); + } + + w83781d_write_value(client, W83781D_REG_TEMP_OVER, + TEMP_TO_REG(W83781D_INIT_TEMP_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP_HYST, + TEMP_TO_REG(W83781D_INIT_TEMP_HYST)); + + if (type == as99127f) { + w83781d_write_value(client, W83781D_REG_TEMP2_OVER, + AS99127_TEMP_ADD_TO_REG + (W83781D_INIT_TEMP2_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP2_HYST, + AS99127_TEMP_ADD_TO_REG + (W83781D_INIT_TEMP2_HYST)); + } else { + w83781d_write_value(client, W83781D_REG_TEMP2_OVER, + TEMP_ADD_TO_REG + (W83781D_INIT_TEMP2_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP2_HYST, + TEMP_ADD_TO_REG + (W83781D_INIT_TEMP2_HYST)); + } + w83781d_write_value(client, W83781D_REG_TEMP2_CONFIG, 0x00); + + if (type == as99127f) { + w83781d_write_value(client, W83781D_REG_TEMP3_OVER, + AS99127_TEMP_ADD_TO_REG + (W83781D_INIT_TEMP3_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP3_HYST, + AS99127_TEMP_ADD_TO_REG + (W83781D_INIT_TEMP3_HYST)); + } else if (type != w83783s && type != w83697hf) { + w83781d_write_value(client, W83781D_REG_TEMP3_OVER, + TEMP_ADD_TO_REG + (W83781D_INIT_TEMP3_OVER)); + w83781d_write_value(client, W83781D_REG_TEMP3_HYST, + TEMP_ADD_TO_REG + (W83781D_INIT_TEMP3_HYST)); + } + if (type != w83783s && type != w83697hf) { + w83781d_write_value(client, W83781D_REG_TEMP3_CONFIG, + 0x00); + } + /* enable PWM2 control (can't hurt since PWM reg should have + been reset to 0xff) */ + if (type != w83781d) { + w83781d_write_value(client, W83781D_REG_PWMCLK12, 0x19); + } + } + + /* Start monitoring */ + w83781d_write_value(client, W83781D_REG_CONFIG, + (w83781d_read_value(client, + W83781D_REG_CONFIG) & 0xf7) + | 0x01); +} + +void w83781d_update_client(struct i2c_client *client) +{ + struct w83781d_data *data = client->data; + int i; + + down(&data->update_lock); + + if ((jiffies - data->last_updated > HZ + HZ / 2) || + (jiffies < data->last_updated) || !data->valid) { + +#ifdef DEBUG + printk(KERN_DEBUG "Starting device update\n"); +#endif + for (i = 0; i <= 8; i++) { + if ((data->type == w83783s || data->type == w83697hf) + && (i == 1)) + continue; /* 783S has no in1 */ + data->in[i] = + w83781d_read_value(client, W83781D_REG_IN(i)); + data->in_min[i] = + w83781d_read_value(client, + W83781D_REG_IN_MIN(i)); + data->in_max[i] = + w83781d_read_value(client, + W83781D_REG_IN_MAX(i)); + if ((data->type != w83782d) && (data->type != w83697hf) + && (data->type != w83627hf) && (i == 6)) + break; + } + for (i = 1; i <= 3; i++) { + data->fan[i - 1] = + w83781d_read_value(client, W83781D_REG_FAN(i)); + data->fan_min[i - 1] = + w83781d_read_value(client, + W83781D_REG_FAN_MIN(i)); + } + if (data->type != w83781d) { + for (i = 1; i <= 4; i++) { + data->pwm[i - 1] = + w83781d_read_value(client, + W83781D_REG_PWM(i)); + if (((data->type == w83783s) + || (data->type == w83627hf) + || (data->type == as99127f) + || (data->type == w83697hf) + || ((data->type == w83782d) + && i2c_is_isa_client(client))) + && i == 2) + break; + } + } + + data->temp = w83781d_read_value(client, W83781D_REG_TEMP); + data->temp_over = + w83781d_read_value(client, W83781D_REG_TEMP_OVER); + data->temp_hyst = + w83781d_read_value(client, W83781D_REG_TEMP_HYST); + data->temp_add[0] = + w83781d_read_value(client, W83781D_REG_TEMP2); + data->temp_add_over[0] = + w83781d_read_value(client, W83781D_REG_TEMP2_OVER); + data->temp_add_hyst[0] = + w83781d_read_value(client, W83781D_REG_TEMP2_HYST); + if (data->type != w83783s && data->type != w83697hf) { + data->temp_add[1] = + w83781d_read_value(client, W83781D_REG_TEMP3); + data->temp_add_over[1] = + w83781d_read_value(client, W83781D_REG_TEMP3_OVER); + data->temp_add_hyst[1] = + w83781d_read_value(client, W83781D_REG_TEMP3_HYST); + } + i = w83781d_read_value(client, W83781D_REG_VID_FANDIV); + if (data->type != w83697hf) { + data->vid = i & 0x0f; + data->vid |= + (w83781d_read_value(client, W83781D_REG_CHIPID) & 0x01) + << 4; + } + data->fan_div[0] = (i >> 4) & 0x03; + data->fan_div[1] = (i >> 6) & 0x03; + if (data->type != w83697hf) { + data->fan_div[2] = (w83781d_read_value(client, + W83781D_REG_PIN) >> 6) & 0x03; + } + if ((data->type != w83781d) && (data->type != as99127f)) { + i = w83781d_read_value(client, W83781D_REG_VBAT); + data->fan_div[0] |= (i >> 3) & 0x04; + data->fan_div[1] |= (i >> 4) & 0x04; + if (data->type != w83697hf) + data->fan_div[2] |= (i >> 5) & 0x04; + } + data->alarms = + w83781d_read_value(client, + W83781D_REG_ALARM1) + + (w83781d_read_value(client, W83781D_REG_ALARM2) << 8); + if ((data->type == w83782d) || (data->type == w83627hf)) { + data->alarms |= + w83781d_read_value(client, + W83781D_REG_ALARM3) << 16; + } + i = w83781d_read_value(client, W83781D_REG_BEEP_INTS2); + data->beep_enable = i >> 7; + data->beeps = ((i & 0x7f) << 8) + + w83781d_read_value(client, W83781D_REG_BEEP_INTS1); + if ((data->type != w83781d) && (data->type != as99127f)) { + data->beeps |= + w83781d_read_value(client, + W83781D_REG_BEEP_INTS3) << 16; + } + data->last_updated = jiffies; + data->valid = 1; + } + + up(&data->update_lock); +} + + +/* The next few functions are the call-back functions of the /proc/sys and + sysctl files. Which function is used is defined in the ctl_table in + the extra1 field. + Each function must return the magnitude (power of 10 to divide the date + with) if it is called with operation==SENSORS_PROC_REAL_INFO. It must + put a maximum of *nrels elements in results reflecting the data of this + file, and set *nrels to the number it actually put in it, if operation== + SENSORS_PROC_REAL_READ. Finally, it must get upto *nrels elements from + results and write them to the chip, if operations==SENSORS_PROC_REAL_WRITE. + Note that on SENSORS_PROC_REAL_READ, I do not check whether results is + large enough (by checking the incoming value of *nrels). This is not very + good practice, but as long as you put less than about 5 values in results, + you can assume it is large enough. */ +void w83781d_in(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int nr = ctl_name - W83781D_SYSCTL_IN0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = IN_FROM_REG(data->in_min[nr]); + results[1] = IN_FROM_REG(data->in_max[nr]); + results[2] = IN_FROM_REG(data->in[nr]); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->in_min[nr] = IN_TO_REG(results[0]); + w83781d_write_value(client, W83781D_REG_IN_MIN(nr), + data->in_min[nr]); + } + if (*nrels_mag >= 2) { + data->in_max[nr] = IN_TO_REG(results[1]); + w83781d_write_value(client, W83781D_REG_IN_MAX(nr), + data->in_max[nr]); + } + } +} + +void w83781d_fan(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int nr = ctl_name - W83781D_SYSCTL_FAN1 + 1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = FAN_FROM_REG(data->fan_min[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + results[1] = FAN_FROM_REG(data->fan[nr - 1], + DIV_FROM_REG(data->fan_div[nr - 1])); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->fan_min[nr - 1] = + FAN_TO_REG(results[0], + DIV_FROM_REG(data->fan_div[nr-1])); + w83781d_write_value(client, + W83781D_REG_FAN_MIN(nr), + data->fan_min[nr - 1]); + } + } +} + +void w83781d_temp(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = TEMP_FROM_REG(data->temp_over); + results[1] = TEMP_FROM_REG(data->temp_hyst); + results[2] = TEMP_FROM_REG(data->temp); + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->temp_over = TEMP_TO_REG(results[0]); + w83781d_write_value(client, W83781D_REG_TEMP_OVER, + data->temp_over); + } + if (*nrels_mag >= 2) { + data->temp_hyst = TEMP_TO_REG(results[1]); + w83781d_write_value(client, W83781D_REG_TEMP_HYST, + data->temp_hyst); + } + } +} + +void w83781d_temp_add(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int nr = ctl_name - W83781D_SYSCTL_TEMP2; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 1; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + if (data->type == as99127f) { + results[0] = + AS99127_TEMP_ADD_FROM_REG(data-> + temp_add_over[nr]); + results[1] = + AS99127_TEMP_ADD_FROM_REG(data-> + temp_add_hyst[nr]); + results[2] = + AS99127_TEMP_ADD_FROM_REG(data->temp_add[nr]); + } else { + results[0] = + TEMP_ADD_FROM_REG(data->temp_add_over[nr]); + results[1] = + TEMP_ADD_FROM_REG(data->temp_add_hyst[nr]); + results[2] = TEMP_ADD_FROM_REG(data->temp_add[nr]); + } + *nrels_mag = 3; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + if (data->type == as99127f) + data->temp_add_over[nr] = + AS99127_TEMP_ADD_TO_REG(results[0]); + else + data->temp_add_over[nr] = + TEMP_ADD_TO_REG(results[0]); + w83781d_write_value(client, + nr ? W83781D_REG_TEMP3_OVER : + W83781D_REG_TEMP2_OVER, + data->temp_add_over[nr]); + } + if (*nrels_mag >= 2) { + if (data->type == as99127f) + data->temp_add_hyst[nr] = + AS99127_TEMP_ADD_TO_REG(results[1]); + else + data->temp_add_hyst[nr] = + TEMP_ADD_TO_REG(results[1]); + w83781d_write_value(client, + nr ? W83781D_REG_TEMP3_HYST : + W83781D_REG_TEMP2_HYST, + data->temp_add_hyst[nr]); + } + } +} + + +void w83781d_vid(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 2; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = VID_FROM_REG(data->vid); + *nrels_mag = 1; + } +} + +void w83781d_alarms(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = ALARMS_FROM_REG(data->alarms); + *nrels_mag = 1; + } +} + +void w83781d_beep(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int val; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = BEEP_ENABLE_FROM_REG(data->beep_enable); + results[1] = BEEPS_FROM_REG(data->beeps); + *nrels_mag = 2; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 2) { + data->beeps = BEEPS_TO_REG(results[1]); + w83781d_write_value(client, W83781D_REG_BEEP_INTS1, + data->beeps & 0xff); + if ((data->type != w83781d) && + (data->type != as99127f)) { + w83781d_write_value(client, + W83781D_REG_BEEP_INTS3, + ((data-> beeps) >> 16) & + 0xff); + } + val = (data->beeps >> 8) & 0x7f; + } else if (*nrels_mag >= 1) + val = + w83781d_read_value(client, + W83781D_REG_BEEP_INTS2) & + 0x7f; + if (*nrels_mag >= 1) { + data->beep_enable = BEEP_ENABLE_TO_REG(results[0]); + w83781d_write_value(client, W83781D_REG_BEEP_INTS2, + val | data->beep_enable << 7); + } + } +} + +/* w83697hf only has two fans */ +void w83781d_fan_div(struct i2c_client *client, int operation, + int ctl_name, int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int old, old2, old3 = 0; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = DIV_FROM_REG(data->fan_div[0]); + results[1] = DIV_FROM_REG(data->fan_div[1]); + if (data->type == w83697hf) { + *nrels_mag = 2; + } else { + results[2] = DIV_FROM_REG(data->fan_div[2]); + *nrels_mag = 3; + } + } else if (operation == SENSORS_PROC_REAL_WRITE) { + old = w83781d_read_value(client, W83781D_REG_VID_FANDIV); + /* w83781d and as99127f don't have extended divisor bits */ + if ((data->type != w83781d) && data->type != as99127f) { + old3 = + w83781d_read_value(client, W83781D_REG_VBAT); + } + if (*nrels_mag >= 3 && data->type != w83697hf) { + data->fan_div[2] = + DIV_TO_REG(results[2], data->type); + old2 = w83781d_read_value(client, W83781D_REG_PIN); + old2 = + (old2 & 0x3f) | ((data->fan_div[2] & 0x03) << 6); + w83781d_write_value(client, W83781D_REG_PIN, old2); + if ((data->type != w83781d) && + (data->type != as99127f)) { + old3 = + (old3 & 0x7f) | + ((data->fan_div[2] & 0x04) << 5); + } + } + if (*nrels_mag >= 2) { + data->fan_div[1] = + DIV_TO_REG(results[1], data->type); + old = + (old & 0x3f) | ((data->fan_div[1] & 0x03) << 6); + if ((data->type != w83781d) && + (data->type != as99127f)) { + old3 = + (old3 & 0xbf) | + ((data->fan_div[1] & 0x04) << 4); + } + } + if (*nrels_mag >= 1) { + data->fan_div[0] = + DIV_TO_REG(results[0], data->type); + old = + (old & 0xcf) | ((data->fan_div[0] & 0x03) << 4); + w83781d_write_value(client, W83781D_REG_VID_FANDIV, + old); + if ((data->type != w83781d) && + (data->type != as99127f)) { + old3 = + (old3 & 0xdf) | + ((data->fan_div[0] & 0x04) << 3); + w83781d_write_value(client, + W83781D_REG_VBAT, + old3); + } + } + } +} + +void w83781d_pwm(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int nr = 1 + ctl_name - W83781D_SYSCTL_PWM1; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + w83781d_update_client(client); + results[0] = PWM_FROM_REG(data->pwm[nr - 1]); + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + data->pwm[nr - 1] = PWM_TO_REG(results[0]); + w83781d_write_value(client, W83781D_REG_PWM(nr), + data->pwm[nr - 1]); + } + } +} + +void w83781d_sens(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int nr = 1 + ctl_name - W83781D_SYSCTL_SENS1; + u8 tmp; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + results[0] = data->sens[nr - 1]; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) { + switch (results[0]) { + case 1: /* PII/Celeron diode */ + tmp = w83781d_read_value(client, + W83781D_REG_SCFG1); + w83781d_write_value(client, + W83781D_REG_SCFG1, + tmp | BIT_SCFG1[nr - + 1]); + tmp = w83781d_read_value(client, + W83781D_REG_SCFG2); + w83781d_write_value(client, + W83781D_REG_SCFG2, + tmp | BIT_SCFG2[nr - + 1]); + data->sens[nr - 1] = results[0]; + break; + case 2: /* 3904 */ + tmp = w83781d_read_value(client, + W83781D_REG_SCFG1); + w83781d_write_value(client, + W83781D_REG_SCFG1, + tmp | BIT_SCFG1[nr - + 1]); + tmp = w83781d_read_value(client, + W83781D_REG_SCFG2); + w83781d_write_value(client, + W83781D_REG_SCFG2, + tmp & ~BIT_SCFG2[nr - + 1]); + data->sens[nr - 1] = results[0]; + break; + case W83781D_DEFAULT_BETA: /* thermistor */ + tmp = w83781d_read_value(client, + W83781D_REG_SCFG1); + w83781d_write_value(client, + W83781D_REG_SCFG1, + tmp & ~BIT_SCFG1[nr - + 1]); + data->sens[nr - 1] = results[0]; + break; + default: + printk + (KERN_ERR "w83781d.o: Invalid sensor type %ld; must be 1, 2, or %d\n", + results[0], W83781D_DEFAULT_BETA); + break; + } + } + } +} + +#ifdef W83781D_RT +void w83781d_rt(struct i2c_client *client, int operation, int ctl_name, + int *nrels_mag, long *results) +{ + struct w83781d_data *data = client->data; + int nr = 1 + ctl_name - W83781D_SYSCTL_RT1; + int i; + + if (operation == SENSORS_PROC_REAL_INFO) + *nrels_mag = 0; + else if (operation == SENSORS_PROC_REAL_READ) { + for (i = 0; i < 32; i++) { + results[i] = data->rt[nr - 1][i]; + } + *nrels_mag = 32; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag > 32) + *nrels_mag = 32; + for (i = 0; i < *nrels_mag; i++) { + /* fixme: no bounds checking 0-255 */ + data->rt[nr - 1][i] = results[i]; + w83781d_write_value(client, W83781D_REG_RT_IDX, i); + w83781d_write_value(client, W83781D_REG_RT_VAL, + data->rt[nr - 1][i]); + } + } +} +#endif + +int __init sensors_w83781d_init(void) +{ + int res; + + printk(KERN_INFO "w83781d.o version %s (%s)\n", LM_VERSION, LM_DATE); + w83781d_initialized = 0; + + if ((res = i2c_add_driver(&w83781d_driver))) { + printk + (KERN_ERR "w83781d.o: Driver registration failed, module not inserted.\n"); + w83781d_cleanup(); + return res; + } + w83781d_initialized++; + return 0; +} + +int __init w83781d_cleanup(void) +{ + int res; + + if (w83781d_initialized >= 1) { + if ((res = i2c_del_driver(&w83781d_driver))) { + printk + (KERN_ERR "w83781d.o: Driver deregistration failed, module not removed.\n"); + return res; + } + w83781d_initialized--; + } + return 0; +} + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard , " + "Philip Edelbrock , " + "and Mark Studebaker "); +MODULE_DESCRIPTION("W83781D driver"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + + +int init_module(void) +{ + return sensors_w83781d_init(); +} + +int cleanup_module(void) +{ + return w83781d_cleanup(); +} + +#endif /* MODULE */ --- linux-old/include/linux/sensors.h Mon Mar 25 15:28:41 CET 2002 +++ linux/include/linux/sensors.h Mon Mar 25 15:28:41 CET 2002 @@ -0,0 +1,559 @@ +/* + sensors.h - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef SENSORS_NSENSORS_H +#define SENSORS_NSENSORS_H + +#include + +#define LM78_SYSCTL_IN0 1000 /* Volts * 100 */ +#define LM78_SYSCTL_IN1 1001 +#define LM78_SYSCTL_IN2 1002 +#define LM78_SYSCTL_IN3 1003 +#define LM78_SYSCTL_IN4 1004 +#define LM78_SYSCTL_IN5 1005 +#define LM78_SYSCTL_IN6 1006 +#define LM78_SYSCTL_FAN1 1101 /* Rotations/min */ +#define LM78_SYSCTL_FAN2 1102 +#define LM78_SYSCTL_FAN3 1103 +#define LM78_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */ +#define LM78_SYSCTL_VID 1300 /* Volts * 100 */ +#define LM78_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ +#define LM78_SYSCTL_ALARMS 2001 /* bitvector */ + +#define LM78_ALARM_IN0 0x0001 +#define LM78_ALARM_IN1 0x0002 +#define LM78_ALARM_IN2 0x0004 +#define LM78_ALARM_IN3 0x0008 +#define LM78_ALARM_IN4 0x0100 +#define LM78_ALARM_IN5 0x0200 +#define LM78_ALARM_IN6 0x0400 +#define LM78_ALARM_FAN1 0x0040 +#define LM78_ALARM_FAN2 0x0080 +#define LM78_ALARM_FAN3 0x0800 +#define LM78_ALARM_TEMP 0x0010 +#define LM78_ALARM_BTI 0x0020 +#define LM78_ALARM_CHAS 0x1000 +#define LM78_ALARM_FIFO 0x2000 +#define LM78_ALARM_SMI_IN 0x4000 + +#define W83781D_SYSCTL_IN0 1000 /* Volts * 100 */ +#define W83781D_SYSCTL_IN1 1001 +#define W83781D_SYSCTL_IN2 1002 +#define W83781D_SYSCTL_IN3 1003 +#define W83781D_SYSCTL_IN4 1004 +#define W83781D_SYSCTL_IN5 1005 +#define W83781D_SYSCTL_IN6 1006 +#define W83781D_SYSCTL_IN7 1007 +#define W83781D_SYSCTL_IN8 1008 +#define W83781D_SYSCTL_FAN1 1101 /* Rotations/min */ +#define W83781D_SYSCTL_FAN2 1102 +#define W83781D_SYSCTL_FAN3 1103 +#define W83781D_SYSCTL_TEMP1 1200 /* Degrees Celcius * 10 */ +#define W83781D_SYSCTL_TEMP2 1201 /* Degrees Celcius * 10 */ +#define W83781D_SYSCTL_TEMP3 1202 /* Degrees Celcius * 10 */ +#define W83781D_SYSCTL_VID 1300 /* Volts * 100 */ +#define W83781D_SYSCTL_PWM1 1401 +#define W83781D_SYSCTL_PWM2 1402 +#define W83781D_SYSCTL_PWM3 1403 +#define W83781D_SYSCTL_PWM4 1404 +#define W83781D_SYSCTL_SENS1 1501 /* 1, 2, or Beta (3000-5000) */ +#define W83781D_SYSCTL_SENS2 1502 +#define W83781D_SYSCTL_SENS3 1503 +#define W83781D_SYSCTL_RT1 1601 /* 32-entry table */ +#define W83781D_SYSCTL_RT2 1602 /* 32-entry table */ +#define W83781D_SYSCTL_RT3 1603 /* 32-entry table */ +#define W83781D_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ +#define W83781D_SYSCTL_ALARMS 2001 /* bitvector */ +#define W83781D_SYSCTL_BEEP 2002 /* bitvector */ + +#define W83781D_ALARM_IN0 0x0001 +#define W83781D_ALARM_IN1 0x0002 +#define W83781D_ALARM_IN2 0x0004 +#define W83781D_ALARM_IN3 0x0008 +#define W83781D_ALARM_IN4 0x0100 +#define W83781D_ALARM_IN5 0x0200 +#define W83781D_ALARM_IN6 0x0400 +#define W83782D_ALARM_IN7 0x10000 +#define W83782D_ALARM_IN8 0x20000 +#define W83781D_ALARM_FAN1 0x0040 +#define W83781D_ALARM_FAN2 0x0080 +#define W83781D_ALARM_FAN3 0x0800 +#define W83781D_ALARM_TEMP1 0x0010 +#define W83781D_ALARM_TEMP23 0x0020 /* 781D only */ +#define W83781D_ALARM_TEMP2 0x0020 /* 782D/783S */ +#define W83781D_ALARM_TEMP3 0x2000 /* 782D only */ +#define W83781D_ALARM_CHAS 0x1000 + +#define LM75_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */ + +#define ADM1021_SYSCTL_TEMP 1200 +#define ADM1021_SYSCTL_REMOTE_TEMP 1201 +#define ADM1021_SYSCTL_DIE_CODE 1202 +#define ADM1021_SYSCTL_ALARMS 1203 + +#define ADM1021_ALARM_TEMP_HIGH 0x40 +#define ADM1021_ALARM_TEMP_LOW 0x20 +#define ADM1021_ALARM_RTEMP_HIGH 0x10 +#define ADM1021_ALARM_RTEMP_LOW 0x08 +#define ADM1021_ALARM_RTEMP_NA 0x04 + +#define GL518_SYSCTL_VDD 1000 /* Volts * 100 */ +#define GL518_SYSCTL_VIN1 1001 +#define GL518_SYSCTL_VIN2 1002 +#define GL518_SYSCTL_VIN3 1003 +#define GL518_SYSCTL_FAN1 1101 /* RPM */ +#define GL518_SYSCTL_FAN2 1102 +#define GL518_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */ +#define GL518_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ +#define GL518_SYSCTL_ALARMS 2001 /* bitvector */ +#define GL518_SYSCTL_BEEP 2002 /* bitvector */ +#define GL518_SYSCTL_FAN1OFF 2003 +#define GL518_SYSCTL_ITERATE 2004 + +#define GL518_ALARM_VDD 0x01 +#define GL518_ALARM_VIN1 0x02 +#define GL518_ALARM_VIN2 0x04 +#define GL518_ALARM_VIN3 0x08 +#define GL518_ALARM_TEMP 0x10 +#define GL518_ALARM_FAN1 0x20 +#define GL518_ALARM_FAN2 0x40 + +#define GL520_SYSCTL_VDD 1000 /* Volts * 100 */ +#define GL520_SYSCTL_VIN1 1001 +#define GL520_SYSCTL_VIN2 1002 +#define GL520_SYSCTL_VIN3 1003 +#define GL520_SYSCTL_VIN4 1004 +#define GL520_SYSCTL_FAN1 1101 /* RPM */ +#define GL520_SYSCTL_FAN2 1102 +#define GL520_SYSCTL_TEMP1 1200 /* Degrees Celcius * 10 */ +#define GL520_SYSCTL_TEMP2 1201 /* Degrees Celcius * 10 */ +#define GL520_SYSCTL_VID 1300 +#define GL520_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ +#define GL520_SYSCTL_ALARMS 2001 /* bitvector */ +#define GL520_SYSCTL_BEEP 2002 /* bitvector */ +#define GL520_SYSCTL_FAN1OFF 2003 +#define GL520_SYSCTL_CONFIG 2004 + +#define GL520_ALARM_VDD 0x01 +#define GL520_ALARM_VIN1 0x02 +#define GL520_ALARM_VIN2 0x04 +#define GL520_ALARM_VIN3 0x08 +#define GL520_ALARM_TEMP1 0x10 +#define GL520_ALARM_FAN1 0x20 +#define GL520_ALARM_FAN2 0x40 +#define GL520_ALARM_TEMP2 0x80 +#define GL520_ALARM_VIN4 0x80 + +#define EEPROM_SYSCTL1 1000 +#define EEPROM_SYSCTL2 1001 +#define EEPROM_SYSCTL3 1002 +#define EEPROM_SYSCTL4 1003 +#define EEPROM_SYSCTL5 1004 +#define EEPROM_SYSCTL6 1005 +#define EEPROM_SYSCTL7 1006 +#define EEPROM_SYSCTL8 1007 +#define EEPROM_SYSCTL9 1008 +#define EEPROM_SYSCTL10 1009 +#define EEPROM_SYSCTL11 1010 +#define EEPROM_SYSCTL12 1011 +#define EEPROM_SYSCTL13 1012 +#define EEPROM_SYSCTL14 1013 +#define EEPROM_SYSCTL15 1014 +#define EEPROM_SYSCTL16 1015 + +#define LM80_SYSCTL_IN0 1000 /* Volts * 100 */ +#define LM80_SYSCTL_IN1 1001 +#define LM80_SYSCTL_IN2 1002 +#define LM80_SYSCTL_IN3 1003 +#define LM80_SYSCTL_IN4 1004 +#define LM80_SYSCTL_IN5 1005 +#define LM80_SYSCTL_IN6 1006 +#define LM80_SYSCTL_FAN1 1101 /* Rotations/min */ +#define LM80_SYSCTL_FAN2 1102 +#define LM80_SYSCTL_TEMP 1250 /* Degrees Celcius * 100 */ +#define LM80_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ +#define LM80_SYSCTL_ALARMS 2001 /* bitvector */ + +#define ADM9240_SYSCTL_IN0 1000 /* Volts * 100 */ +#define ADM9240_SYSCTL_IN1 1001 +#define ADM9240_SYSCTL_IN2 1002 +#define ADM9240_SYSCTL_IN3 1003 +#define ADM9240_SYSCTL_IN4 1004 +#define ADM9240_SYSCTL_IN5 1005 +#define ADM9240_SYSCTL_FAN1 1101 /* Rotations/min */ +#define ADM9240_SYSCTL_FAN2 1102 +#define ADM9240_SYSCTL_TEMP 1250 /* Degrees Celcius * 100 */ +#define ADM9240_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ +#define ADM9240_SYSCTL_ALARMS 2001 /* bitvector */ +#define ADM9240_SYSCTL_ANALOG_OUT 2002 +#define ADM9240_SYSCTL_VID 2003 + +#define ADM9240_ALARM_IN0 0x0001 +#define ADM9240_ALARM_IN1 0x0002 +#define ADM9240_ALARM_IN2 0x0004 +#define ADM9240_ALARM_IN3 0x0008 +#define ADM9240_ALARM_IN4 0x0100 +#define ADM9240_ALARM_IN5 0x0200 +#define ADM9240_ALARM_FAN1 0x0040 +#define ADM9240_ALARM_FAN2 0x0080 +#define ADM9240_ALARM_TEMP 0x0010 +#define ADM9240_ALARM_CHAS 0x1000 + +#define ADM1024_SYSCTL_IN0 1000 /* Volts * 100 */ +#define ADM1024_SYSCTL_IN1 1001 +#define ADM1024_SYSCTL_IN2 1002 +#define ADM1024_SYSCTL_IN3 1003 +#define ADM1024_SYSCTL_IN4 1004 +#define ADM1024_SYSCTL_IN5 1005 +#define ADM1024_SYSCTL_FAN1 1101 /* Rotations/min */ +#define ADM1024_SYSCTL_FAN2 1102 +#define ADM1024_SYSCTL_TEMP 1250 /* Degrees Celcius * 100 */ +#define ADM1024_SYSCTL_TEMP1 1290 /* Degrees Celcius */ +#define ADM1024_SYSCTL_TEMP2 1295 /* Degrees Celcius */ +#define ADM1024_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ +#define ADM1024_SYSCTL_ALARMS 2001 /* bitvector */ +#define ADM1024_SYSCTL_ANALOG_OUT 2002 +#define ADM1024_SYSCTL_VID 2003 + +#define ADM1024_ALARM_IN0 0x0001 +#define ADM1024_ALARM_IN1 0x0002 +#define ADM1024_ALARM_IN2 0x0004 +#define ADM1024_ALARM_IN3 0x0008 +#define ADM1024_ALARM_IN4 0x0100 +#define ADM1024_ALARM_IN5 0x0200 +#define ADM1024_ALARM_FAN1 0x0040 +#define ADM1024_ALARM_FAN2 0x0080 +#define ADM1024_ALARM_TEMP 0x0010 +#define ADM1024_ALARM_TEMP1 0x0020 +#define ADM1024_ALARM_TEMP2 0x0001 +#define ADM1024_ALARM_CHAS 0x1000 + +#define ADM1025_SYSCTL_IN0 1000 /* Volts * 100 */ +#define ADM1025_SYSCTL_IN1 1001 +#define ADM1025_SYSCTL_IN2 1002 +#define ADM1025_SYSCTL_IN3 1003 +#define ADM1025_SYSCTL_IN4 1004 +#define ADM1025_SYSCTL_IN5 1005 +#define ADM1025_SYSCTL_RTEMP 1251 +#define ADM1025_SYSCTL_TEMP 1250 /* Degrees Celcius * 100 */ +#define ADM1025_SYSCTL_ALARMS 2001 /* bitvector */ +#define ADM1025_SYSCTL_ANALOG_OUT 2002 +#define ADM1025_SYSCTL_VID 2003 + +#define ADM1025_ALARM_IN0 0x0001 +#define ADM1025_ALARM_IN1 0x0002 +#define ADM1025_ALARM_IN2 0x0004 +#define ADM1025_ALARM_IN3 0x0008 +#define ADM1025_ALARM_IN4 0x0100 +#define ADM1025_ALARM_IN5 0x0200 +#define ADM1025_ALARM_RTEMP 0x0020 +#define ADM1025_ALARM_TEMP 0x0010 + +#define LTC1710_SYSCTL_SWITCH_1 1000 +#define LTC1710_SYSCTL_SWITCH_2 1001 + +#define LM80_ALARM_IN0 0x0001 +#define LM80_ALARM_IN1 0x0002 +#define LM80_ALARM_IN2 0x0004 +#define LM80_ALARM_IN3 0x0008 +#define LM80_ALARM_IN4 0x0010 +#define LM80_ALARM_IN5 0x0020 +#define LM80_ALARM_IN6 0x0040 +#define LM80_ALARM_FAN1 0x0400 +#define LM80_ALARM_FAN2 0x0800 +#define LM80_ALARM_TEMP_HOT 0x0100 +#define LM80_ALARM_TEMP_OS 0x2000 +#define LM80_ALARM_CHAS 0x1000 +#define LM80_ALARM_BTI 0x0200 +#define LM80_ALARM_INT_IN 0x0080 + +#define MAXI_SYSCTL_FAN1 1101 /* Rotations/min */ +#define MAXI_SYSCTL_FAN2 1102 /* Rotations/min */ +#define MAXI_SYSCTL_FAN3 1103 /* Rotations/min */ +#define MAXI_SYSCTL_FAN4 1104 /* Rotations/min */ +#define MAXI_SYSCTL_TEMP1 1201 /* Degrees Celcius */ +#define MAXI_SYSCTL_TEMP2 1202 /* Degrees Celcius */ +#define MAXI_SYSCTL_TEMP3 1203 /* Degrees Celcius */ +#define MAXI_SYSCTL_TEMP4 1204 /* Degrees Celcius */ +#define MAXI_SYSCTL_TEMP5 1205 /* Degrees Celcius */ +#define MAXI_SYSCTL_TEMP6 1206 /* Degrees Celcius */ +#define MAXI_SYSCTL_PLL 1301 /* MHz */ +#define MAXI_SYSCTL_VID1 1401 /* Volts / 6.337, for nba just Volts */ +#define MAXI_SYSCTL_VID2 1402 /* Volts */ +#define MAXI_SYSCTL_VID3 1403 /* Volts */ +#define MAXI_SYSCTL_VID4 1404 /* Volts */ +#define MAXI_SYSCTL_VID5 1405 /* Volts */ +#define MAXI_SYSCTL_LCD1 1501 /* Line 1 of LCD */ +#define MAXI_SYSCTL_LCD2 1502 /* Line 2 of LCD */ +#define MAXI_SYSCTL_LCD3 1503 /* Line 3 of LCD */ +#define MAXI_SYSCTL_LCD4 1504 /* Line 4 of LCD */ +#define MAXI_SYSCTL_ALARMS 2001 /* Bitvector (see below) */ + +#define MAXI_ALARM_VID4 0x0001 +#define MAXI_ALARM_TEMP2 0x0002 +#define MAXI_ALARM_VID1 0x0004 +#define MAXI_ALARM_VID2 0x0008 +#define MAXI_ALARM_VID3 0x0010 +#define MAXI_ALARM_PLL 0x0080 +#define MAXI_ALARM_TEMP4 0x0100 +#define MAXI_ALARM_TEMP5 0x0200 +#define MAXI_ALARM_FAN1 0x1000 +#define MAXI_ALARM_FAN2 0x2000 +#define MAXI_ALARM_FAN3 0x4000 + +#define MAXI_ALARM_FAN 0x0100 /* To be used with MaxiLife'99 */ +#define MAXI_ALARM_VID 0x0200 /* The MSB specifies which sensor */ +#define MAXI_ALARM_TEMP 0x0400 /* in the alarm group failed, i.e.: */ +#define MAXI_ALARM_VADD 0x0800 /* 0x0402 = TEMP2 failed = CPU2 temp */ + +#define SIS5595_SYSCTL_IN0 1000 /* Volts * 100 */ +#define SIS5595_SYSCTL_IN1 1001 +#define SIS5595_SYSCTL_IN2 1002 +#define SIS5595_SYSCTL_IN3 1003 +#define SIS5595_SYSCTL_IN4 1004 +#define SIS5595_SYSCTL_FAN1 1101 /* Rotations/min */ +#define SIS5595_SYSCTL_FAN2 1102 +#define SIS5595_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */ +#define SIS5595_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ +#define SIS5595_SYSCTL_ALARMS 2001 /* bitvector */ + +#define SIS5595_ALARM_IN0 0x01 +#define SIS5595_ALARM_IN1 0x02 +#define SIS5595_ALARM_IN2 0x04 +#define SIS5595_ALARM_IN3 0x08 +#define SIS5595_ALARM_BTI 0x20 +#define SIS5595_ALARM_FAN1 0x40 +#define SIS5595_ALARM_FAN2 0x80 +#define SIS5595_ALARM_IN4 0x8000 +#define SIS5595_ALARM_TEMP 0x8000 + +#define VIA686A_SYSCTL_IN0 1000 +#define VIA686A_SYSCTL_IN1 1001 +#define VIA686A_SYSCTL_IN2 1002 +#define VIA686A_SYSCTL_IN3 1003 +#define VIA686A_SYSCTL_IN4 1004 +#define VIA686A_SYSCTL_FAN1 1101 +#define VIA686A_SYSCTL_FAN2 1102 +#define VIA686A_SYSCTL_TEMP 1200 +#define VIA686A_SYSCTL_TEMP2 1201 +#define VIA686A_SYSCTL_TEMP3 1202 +#define VIA686A_SYSCTL_FAN_DIV 2000 +#define VIA686A_SYSCTL_ALARMS 2001 + +#define VIA686A_ALARM_IN0 0x01 +#define VIA686A_ALARM_IN1 0x02 +#define VIA686A_ALARM_IN2 0x04 +#define VIA686A_ALARM_IN3 0x08 +#define VIA686A_ALARM_TEMP 0x10 +#define VIA686A_ALARM_FAN1 0x40 +#define VIA686A_ALARM_FAN2 0x80 +#define VIA686A_ALARM_IN4 0x100 +#define VIA686A_ALARM_TEMP2 0x800 +#define VIA686A_ALARM_CHAS 0x1000 +#define VIA686A_ALARM_TEMP3 0x8000 + +#define ICSPLL_SYSCTL1 1000 + +#define BT869_SYSCTL_STATUS 1000 +#define BT869_SYSCTL_NTSC 1001 +#define BT869_SYSCTL_HALF 1002 +#define BT869_SYSCTL_RES 1003 +#define BT869_SYSCTL_COLORBARS 1004 +#define BT869_SYSCTL_DEPTH 1005 +#define BT869_SYSCTL_SVIDEO 1006 + +#define MATORB_SYSCTL_DISP 1000 + +#define THMC50_SYSCTL_TEMP 1200 /* Degrees Celcius */ +#define THMC50_SYSCTL_REMOTE_TEMP 1201 /* Degrees Celcius */ +#define THMC50_SYSCTL_INTER 1202 +#define THMC50_SYSCTL_INTER_MASK 1203 +#define THMC50_SYSCTL_DIE_CODE 1204 +#define THMC50_SYSCTL_ANALOG_OUT 1205 + +#define DDCMON_SYSCTL_ID 1010 +#define DDCMON_SYSCTL_SIZE 1011 +#define DDCMON_SYSCTL_SYNC 1012 +#define DDCMON_SYSCTL_TIMINGS 1013 +#define DDCMON_SYSCTL_SERIAL 1014 + +#define LM87_SYSCTL_IN0 1000 /* Volts * 100 */ +#define LM87_SYSCTL_IN1 1001 +#define LM87_SYSCTL_IN2 1002 +#define LM87_SYSCTL_IN3 1003 +#define LM87_SYSCTL_IN4 1004 +#define LM87_SYSCTL_IN5 1005 +#define LM87_SYSCTL_AIN1 1006 +#define LM87_SYSCTL_AIN2 1007 +#define LM87_SYSCTL_FAN1 1102 +#define LM87_SYSCTL_FAN2 1103 +#define LM87_SYSCTL_TEMP1 1250 /* Degrees Celcius * 100 */ +#define LM87_SYSCTL_TEMP2 1251 /* Degrees Celcius * 100 */ +#define LM87_SYSCTL_TEMP3 1252 /* Degrees Celcius * 100 */ +#define LM87_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ +#define LM87_SYSCTL_ALARMS 2001 /* bitvector */ +#define LM87_SYSCTL_ANALOG_OUT 2002 +#define LM87_SYSCTL_VID 2003 + +#define LM87_ALARM_IN0 0x0001 +#define LM87_ALARM_IN1 0x0002 +#define LM87_ALARM_IN2 0x0004 +#define LM87_ALARM_IN3 0x0008 +#define LM87_ALARM_TEMP1 0x0010 +#define LM87_ALARM_TEMP2 0x0020 +#define LM87_ALARM_TEMP3 0x0020 /* same?? */ +#define LM87_ALARM_FAN1 0x0040 +#define LM87_ALARM_FAN2 0x0080 +#define LM87_ALARM_IN4 0x0100 +#define LM87_ALARM_IN5 0x0200 +#define LM87_ALARM_RESERVED1 0x0400 +#define LM87_ALARM_RESERVED2 0x0800 +#define LM87_ALARM_CHAS 0x1000 +#define LM87_ALARM_THERM_SIG 0x2000 +#define LM87_ALARM_TEMP2_FAULT 0x4000 +#define LM87_ALARM_TEMP3_FAULT 0x08000 + +#define PCF8574_SYSCTL_READ 1000 +#define PCF8574_SYSCTL_WRITE 1001 + +#define MTP008_SYSCTL_IN0 1000 /* Volts * 100 */ +#define MTP008_SYSCTL_IN1 1001 +#define MTP008_SYSCTL_IN2 1002 +#define MTP008_SYSCTL_IN3 1003 +#define MTP008_SYSCTL_IN4 1004 +#define MTP008_SYSCTL_IN5 1005 +#define MTP008_SYSCTL_IN6 1006 +#define MTP008_SYSCTL_FAN1 1101 /* Rotations/min */ +#define MTP008_SYSCTL_FAN2 1102 +#define MTP008_SYSCTL_FAN3 1103 +#define MTP008_SYSCTL_TEMP1 1200 /* Degrees Celcius * 10 */ +#define MTP008_SYSCTL_TEMP2 1201 /* Degrees Celcius * 10 */ +#define MTP008_SYSCTL_TEMP3 1202 /* Degrees Celcius * 10 */ +#define MTP008_SYSCTL_VID 1300 /* Volts * 100 */ +#define MTP008_SYSCTL_PWM1 1401 +#define MTP008_SYSCTL_PWM2 1402 +#define MTP008_SYSCTL_PWM3 1403 +#define MTP008_SYSCTL_SENS1 1501 /* 1, 2, or Beta (3000-5000) */ +#define MTP008_SYSCTL_SENS2 1502 +#define MTP008_SYSCTL_SENS3 1503 +#define MTP008_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ +#define MTP008_SYSCTL_ALARMS 2001 /* bitvector */ +#define MTP008_SYSCTL_BEEP 2002 /* bitvector */ + +#define MTP008_ALARM_IN0 0x0001 +#define MTP008_ALARM_IN1 0x0002 +#define MTP008_ALARM_IN2 0x0004 +#define MTP008_ALARM_IN3 0x0008 +#define MTP008_ALARM_IN4 0x0100 +#define MTP008_ALARM_IN5 0x0200 +#define MTP008_ALARM_IN6 0x0400 +#define MTP008_ALARM_FAN1 0x0040 +#define MTP008_ALARM_FAN2 0x0080 +#define MTP008_ALARM_FAN3 0x0800 +#define MTP008_ALARM_TEMP1 0x0010 +#define MTP008_ALARM_TEMP2 0x0100 +#define MTP008_ALARM_TEMP3 0x0200 + +#define DS1621_SYSCTL_TEMP 1200 /* Degrees Celcius * 10 */ +#define DS1621_SYSCTL_ALARMS 2001 /* bitvector */ +#define DS1621_ALARM_TEMP_HIGH 0x40 +#define DS1621_ALARM_TEMP_LOW 0x20 +#define DS1621_SYSCTL_ENABLE 2002 +#define DS1621_SYSCTL_CONTINUOUS 2003 +#define DS1621_SYSCTL_POLARITY 2004 + +#define IT87_SYSCTL_IN0 1000 /* Volts * 100 */ +#define IT87_SYSCTL_IN1 1001 +#define IT87_SYSCTL_IN2 1002 +#define IT87_SYSCTL_IN3 1003 +#define IT87_SYSCTL_IN4 1004 +#define IT87_SYSCTL_IN5 1005 +#define IT87_SYSCTL_IN6 1006 +#define IT87_SYSCTL_IN7 1007 +#define IT87_SYSCTL_IN8 1008 +#define IT87_SYSCTL_FAN1 1101 /* Rotations/min */ +#define IT87_SYSCTL_FAN2 1102 +#define IT87_SYSCTL_FAN3 1103 +#define IT87_SYSCTL_TEMP1 1200 /* Degrees Celcius * 10 */ +#define IT87_SYSCTL_TEMP2 1201 /* Degrees Celcius * 10 */ +#define IT87_SYSCTL_TEMP3 1202 /* Degrees Celcius * 10 */ +#define IT87_SYSCTL_VID 1300 /* Volts * 100 */ +#define IT87_SYSCTL_FAN_DIV 2000 /* 1, 2, 4 or 8 */ +#define IT87_SYSCTL_ALARMS 2004 /* bitvector */ + +#define IT87_ALARM_IN0 0x000100 +#define IT87_ALARM_IN1 0x000200 +#define IT87_ALARM_IN2 0x000400 +#define IT87_ALARM_IN3 0x000800 +#define IT87_ALARM_IN4 0x001000 +#define IT87_ALARM_IN5 0x002000 +#define IT87_ALARM_IN6 0x004000 +#define IT87_ALARM_IN7 0x008000 +#define IT87_ALARM_FAN1 0x0001 +#define IT87_ALARM_FAN2 0x0002 +#define IT87_ALARM_FAN3 0x0004 +#define IT87_ALARM_TEMP1 0x00010000 +#define IT87_ALARM_TEMP2 0x00020000 +#define IT87_ALARM_TEMP3 0x00040000 + +#define FSCPOS_SYSCTL_VOLT0 1000 /* 12 volt supply */ +#define FSCPOS_SYSCTL_VOLT1 1001 /* 5 volt supply */ +#define FSCPOS_SYSCTL_VOLT2 1002 /* batterie voltage*/ +#define FSCPOS_SYSCTL_FAN0 1101 /* state, min, ripple, actual value fan 0 */ +#define FSCPOS_SYSCTL_FAN1 1102 /* state, min, ripple, actual value fan 1 */ +#define FSCPOS_SYSCTL_FAN2 1103 /* state, min, ripple, actual value fan 2 */ +#define FSCPOS_SYSCTL_TEMP0 1201 /* state and value of sensor 0, cpu die */ +#define FSCPOS_SYSCTL_TEMP1 1202 /* state and value of sensor 1, motherboard */ +#define FSCPOS_SYSCTL_TEMP2 1203 /* state and value of sensor 2, chassis */ +#define FSCPOS_SYSCTL_REV 2000 /* Revision */ +#define FSCPOS_SYSCTL_EVENT 2001 /* global event status */ +#define FSCPOS_SYSCTL_CONTROL 2002 /* global control byte */ +#define FSCPOS_SYSCTL_WDOG 2003 /* state, min, ripple, actual value fan 2 */ + +#define FSCSCY_SYSCTL_VOLT0 1000 /* 12 volt supply */ +#define FSCSCY_SYSCTL_VOLT1 1001 /* 5 volt supply */ +#define FSCSCY_SYSCTL_VOLT2 1002 /* batterie voltage*/ +#define FSCSCY_SYSCTL_FAN0 1101 /* state, min, ripple, actual value fan 0 */ +#define FSCSCY_SYSCTL_FAN1 1102 /* state, min, ripple, actual value fan 1 */ +#define FSCSCY_SYSCTL_FAN2 1103 /* state, min, ripple, actual value fan 2 */ +#define FSCSCY_SYSCTL_FAN3 1104 /* state, min, ripple, actual value fan 3 */ +#define FSCSCY_SYSCTL_FAN4 1105 /* state, min, ripple, actual value fan 4 */ +#define FSCSCY_SYSCTL_FAN5 1106 /* state, min, ripple, actual value fan 5 */ +#define FSCSCY_SYSCTL_TEMP0 1201 /* state and value of sensor 0, cpu die */ +#define FSCSCY_SYSCTL_TEMP1 1202 /* state and value of sensor 1, motherboard */ +#define FSCSCY_SYSCTL_TEMP2 1203 /* state and value of sensor 2, chassis */ +#define FSCSCY_SYSCTL_TEMP3 1204 /* state and value of sensor 3, chassis */ +#define FSCSCY_SYSCTL_REV 2000 /* Revision */ +#define FSCSCY_SYSCTL_EVENT 2001 /* global event status */ +#define FSCSCY_SYSCTL_CONTROL 2002 /* global control byte */ +#define FSCSCY_SYSCTL_WDOG 2003 /* state, min, ripple, actual value fan 2 */ +#define FSCSCY_SYSCTL_PCILOAD 2004 /* PCILoad value */ +#define FSCSCY_SYSCTL_INTRUSION 2005 /* state, control for intrusion sensor */ + +#define PCF8591_SYSCTL_AIN_CONF 1000 /* Analog input configuration */ +#define PCF8591_SYSCTL_CH0 1001 /* Input channel 1 */ +#define PCF8591_SYSCTL_CH1 1002 /* Input channel 2 */ +#define PCF8591_SYSCTL_CH2 1003 /* Input channel 3 */ +#define PCF8591_SYSCTL_CH3 1004 /* Input channel 4 */ +#define PCF8591_SYSCTL_AOUT_ENABLE 1005 /* Analog output enable flag */ +#define PCF8591_SYSCTL_AOUT 1006 /* Analog output */ + +#endif /* def SENSORS_SENSORS_H */ --- linux-old/drivers/sensors/sensors.c Mon Mar 25 15:28:41 CET 2002 +++ linux/drivers/sensors/sensors.c Mon Mar 25 15:28:41 CET 2002 @@ -0,0 +1,149 @@ +/* + sensors.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998, 1999 Frodo Looijaard + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* Not configurable as a module */ + +#include + +#ifdef CONFIG_SENSORS_ADM1021 +extern int sensors_adm1021_init(void); +#endif +#ifdef CONFIG_SENSORS_ADM1025 +extern int sensors_adm1025_init(void); +#endif +#ifdef CONFIG_SENSORS_ADM9240 +extern int sensors_adm9240_init(void); +#endif +#ifdef CONFIG_SENSORS_BT869 +extern int sensors_bt869_init(void); +#endif +#ifdef CONFIG_SENSORS_DDCMON +extern int sensors_ddcmon_init(void); +#endif +#ifdef CONFIG_SENSORS_DS1621 +extern int sensors_ds1621_init(void); +#endif +#ifdef CONFIG_SENSORS_GL518SM +extern int sensors_gl518sm_init(void); +#endif +#ifdef CONFIG_SENSORS_GL520SM +extern int sensors_gl520_init(void); +#endif +#ifdef CONFIG_SENSORS_LM75 +extern int sensors_lm75_init(void); +#endif +#ifdef CONFIG_SENSORS_LM78 +extern int sensors_lm78_init(void); +#endif +#ifdef CONFIG_SENSORS_LM80 +extern int sensors_lm80_init(void); +#endif +#ifdef CONFIG_SENSORS_LM87 +extern int sensors_lm87_init(void); +#endif +#ifdef CONFIG_SENSORS_MTP008 +extern int sensors_mtp008_init(void); +#endif +#ifdef CONFIG_SENSORS_SIS5595 +extern int sensors_sis5595_init(void); +#endif +#ifdef CONFIG_SENSORS_THMC50 +extern int sensors_thmc50_init(void); +#endif +#ifdef CONFIG_SENSORS_VIA686A +extern int sensors_via686a_init(void); +#endif +#ifdef CONFIG_SENSORS_W83781D +extern int sensors_w83781d_init(void); +#endif +#ifdef CONFIG_SENSORS_EEPROM +extern int sensors_eeprom_init(void); +#endif +#ifdef CONFIG_SENSORS_LTC1710 +extern int sensors_ltc1710_init(void); +#endif +#ifdef CONFIG_SENSORS_IT87 +extern int sensors_it87_init(void); +#endif + +int __init sensors_init_all(void) +{ +#ifdef CONFIG_SENSORS_ADM1021 + sensors_adm1021_init(); +#endif +#ifdef CONFIG_SENSORS_ADM1025 + sensors_adm1025_init(); +#endif +#ifdef CONFIG_SENSORS_ADM9240 + sensors_adm9240_init(); +#endif +#ifdef CONFIG_SENSORS_BT869 + sensors_bt869_init(); +#endif +#ifdef CONFIG_SENSORS_DDCMON + sensors_ddcmon_init(); +#endif +#ifdef CONFIG_SENSORS_DS1621 + sensors_ds1621_init(); +#endif +#ifdef CONFIG_SENSORS_GL518SM + sensors_gl518sm_init(); +#endif +#ifdef CONFIG_SENSORS_GL520SM + sensors_gl520_init(); +#endif +#ifdef CONFIG_SENSORS_LM75 + sensors_lm75_init(); +#endif +#ifdef CONFIG_SENSORS_LM78 + sensors_lm78_init(); +#endif +#ifdef CONFIG_SENSORS_LM80 + sensors_lm80_init(); +#endif +#ifdef CONFIG_SENSORS_LM87 + sensors_lm87_init(); +#endif +#ifdef CONFIG_SENSORS_MTP008 + sensors_mtp008_init(); +#endif +#ifdef CONFIG_SENSORS_SIS5595 + sensors_sis5595_init(); +#endif +#ifdef CONFIG_SENSORS_THMC50 + sensors_thmc50_init(); +#endif +#ifdef CONFIG_SENSORS_VIA686A + sensors_via686a_init(); +#endif +#ifdef CONFIG_SENSORS_W83781D + sensors_w83781d_init(); +#endif +#ifdef CONFIG_SENSORS_EEPROM + sensors_eeprom_init(); +#endif +#ifdef CONFIG_SENSORS_LTC1710 + sensors_ltc1710_init(); +#endif +#ifdef CONFIG_SENSORS_IT87 + sensors_it87_init(); +#endif + return 0; +} --- linux-old/drivers/sensors/Config.in Mon Mar 25 15:28:42 CET 2002 +++ linux/drivers/sensors/Config.in Mon Mar 25 15:28:42 CET 2002 @@ -0,0 +1,46 @@ +# +# Sensor device configuration +# All depend on CONFIG_I2C_PROC. +# ISA-only devices depend on CONFIG_I2C_ISA also. +# + +if [ "$CONFIG_I2C" = "m" -o "$CONFIG_I2C" = "y" ] ; then +if [ "$CONFIG_I2C_PROC" = "m" -o "$CONFIG_I2C_PROC" = "y" ] ; then + mainmenu_option next_comment + comment 'Hardware sensors support' + + dep_mbool 'Hardware sensors support' CONFIG_SENSORS $CONFIG_I2C $CONFIG_I2C_PROC + + if [ "$CONFIG_SENSORS" != "n" ]; then + dep_tristate ' Analog Devices ADM1021 and compatibles' CONFIG_SENSORS_ADM1021 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Analog Devices ADM1024' CONFIG_SENSORS_ADM1024 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Analog Devices ADM1025' CONFIG_SENSORS_ADM1025 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Analog Devices ADM9240 and compatibles' CONFIG_SENSORS_ADM9240 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Dallas DS1621 and DS1625' CONFIG_SENSORS_DS1621 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Fujitsu-Siemens Poseidon' CONFIG_SENSORS_FSCPOS $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Fujitsu-Siemens Scylla' CONFIG_SENSORS_FSCSCY $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Genesys Logic GL518SM' CONFIG_SENSORS_GL518SM $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Genesys Logic GL520SM' CONFIG_SENSORS_GL520SM $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' HP Maxilife' CONFIG_SENSORS_MAXILIFE $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' ITE 8705/8712, SiS950' CONFIG_SENSORS_IT87 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Myson MTP008' CONFIG_SENSORS_MTP008 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' National Semiconductors LM75 and compatibles' CONFIG_SENSORS_LM75 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' National Semiconductors LM78' CONFIG_SENSORS_LM78 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' National Semiconductors LM80' CONFIG_SENSORS_LM80 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' National Semiconductors LM87' CONFIG_SENSORS_LM87 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Silicon Integrated Systems Corp. SiS5595' CONFIG_SENSORS_SIS5595 $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA + dep_tristate ' Texas Instruments THMC50 and compatibles' CONFIG_SENSORS_THMC50 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' VIA 686a Integrated Hardware Monitor' CONFIG_SENSORS_VIA686A $CONFIG_I2C $CONFIG_I2C_PROC $CONFIG_I2C_ISA + dep_tristate ' Winbond W83781D, W83782D, W83783S, W83627HF, Asus AS99127F' CONFIG_SENSORS_W83781D $CONFIG_I2C $CONFIG_I2C_PROC + bool 'Other I2C devices' CONFIG_SENSORS_OTHER + if [ "$CONFIG_SENSORS_OTHER" = "y" ] ; then + dep_tristate ' Brooktree BT869 Video Modulator' CONFIG_SENSORS_BT869 $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' DDC Monitor EDID EEPROM' CONFIG_SENSORS_DDCMON $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' EEprom (DIMM) reader ' CONFIG_SENSORS_EEPROM $CONFIG_I2C $CONFIG_I2C_PROC + dep_tristate ' Matrix-Orbital LCD Displays' CONFIG_SENSORS_MATORB $CONFIG_I2C $CONFIG_I2C_PROC + fi + fi + endmenu +fi +fi + --- linux-old/Makefile Mon Mar 25 15:28:42 CET 2002 +++ linux/Makefile Mon Mar 25 15:28:42 CET 2002 @@ -187,4 +187,5 @@ DRIVERS-$(CONFIG_HOTPLUG_PCI) += drivers/hotplug/vmlinux-obj.o +DRIVERS-$(CONFIG_SENSORS) += drivers/sensors/sensor.o DRIVERS := $(DRIVERS-y) --- linux-old/drivers/Makefile Mon Mar 25 15:28:42 CET 2002 +++ linux/drivers/Makefile Mon Mar 25 15:28:42 CET 2002 @@ -9,5 +9,5 @@ mod-subdirs := dio mtd sbus video macintosh usb input telephony sgi ide \ message/i2o message/fusion scsi md ieee1394 pnp isdn atm \ - fc4 net/hamradio i2c acpi bluetooth + fc4 net/hamradio i2c acpi bluetooth sensors subdir-y := parport char block net sound misc media cdrom hotplug @@ -44,4 +44,5 @@ subdir-$(CONFIG_HAMRADIO) += net/hamradio subdir-$(CONFIG_I2C) += i2c +subdir-$(CONFIG_SENSORS) += sensors subdir-$(CONFIG_ACPI) += acpi --- linux-old/drivers/sensors/Makefile Mon Mar 25 15:28:42 CET 2002 +++ linux/drivers/sensors/Makefile Mon Mar 25 15:28:42 CET 2002 @@ -0,0 +1,36 @@ +# +# Makefile for the kernel hardware sensors drivers. +# + +MOD_LIST_NAME := SENSORS_MODULES +O_TARGET := sensor.o + +export-objs := sensors.o + +obj-$(CONFIG_SENSORS) += sensors.o +obj-$(CONFIG_SENSORS_ADM1021) += adm1021.o +obj-$(CONFIG_SENSORS_ADM1024) += adm1024.o +obj-$(CONFIG_SENSORS_ADM1025) += adm1025.o +obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o +obj-$(CONFIG_SENSORS_BT869) += bt869.o +obj-$(CONFIG_SENSORS_DDCMON) += ddcmon.o +obj-$(CONFIG_SENSORS_DS1621) += ds1621.o +obj-$(CONFIG_SENSORS_EEPROM) += eeprom.o +obj-$(CONFIG_SENSORS_FSCPOS) += fscpos.o +obj-$(CONFIG_SENSORS_FSCSCY) += fscscy.o +obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o +obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o +obj-$(CONFIG_SENSORS_IT87) += it87.o +obj-$(CONFIG_SENSORS_LM75) += lm75.o +obj-$(CONFIG_SENSORS_LM78) += lm78.o +obj-$(CONFIG_SENSORS_LM80) += lm80.o +obj-$(CONFIG_SENSORS_LM87) += lm87.o +obj-$(CONFIG_SENSORS_MAXILIFE) += maxilife.o +obj-$(CONFIG_SENSORS_MTP008) += mtp008.o +obj-$(CONFIG_SENSORS_SIS5595) += sis5595.o +obj-$(CONFIG_SENSORS_THMC50) += thmc50.o +obj-$(CONFIG_SENSORS_VIA686A) += via686a.o +obj-$(CONFIG_SENSORS_W83781D) += w83781d.o + +include $(TOPDIR)/Rules.make + --- linux-old/drivers/char/Config.in Mon Mar 25 15:28:42 CET 2002 +++ linux/drivers/char/Config.in Mon Mar 25 15:28:42 CET 2002 @@ -108,4 +108,6 @@ source drivers/i2c/Config.in +source drivers/sensors/Config.in + mainmenu_option next_comment comment 'Mice' --- linux-old/drivers/char/mem.c Mon Mar 25 15:28:42 CET 2002 +++ linux/drivers/char/mem.c Mon Mar 25 15:28:42 CET 2002 @@ -27,4 +27,7 @@ #include +#ifdef CONFIG_SENSORS +extern void sensors_init_all(void); +#endif #ifdef CONFIG_I2C extern int i2c_init_all(void); @@ -673,4 +676,8 @@ tapechar_init(); #endif +#ifdef CONFIG_SENSORS + sensors_init_all(); +#endif + return 0; } --- linux-old/drivers/i2c/Config.in Mon Mar 25 15:28:42 CET 2002 +++ linux/drivers/i2c/Config.in Mon Mar 25 15:28:42 CET 2002 @@ -36,4 +36,21 @@ # This is needed for automatic patch generation: sensors code starts here + bool 'I2C mainboard interfaces' CONFIG_I2C_MAINBOARD + if [ "$CONFIG_I2C_MAINBOARD" = "y" ]; then + tristate ' Acer Labs ALI 1535' CONFIG_I2C_ALI1535 + tristate ' Acer Labs ALI 1533 and 1543C' CONFIG_I2C_ALI15X3 + dep_tristate ' Apple Hydra Mac I/O' CONFIG_I2C_HYDRA $CONFIG_I2C_ALGOBIT + tristate ' AMD 756/766/768' CONFIG_I2C_AMD756 + dep_tristate ' DEC Tsunami I2C interface' CONFIG_I2C_TSUNAMI $CONFIG_I2C_ALGOBIT + tristate ' Intel 82801AA, 82801AB and 82801BA' CONFIG_I2C_I801 + dep_tristate ' Intel i810AA/AB/E and i815' CONFIG_I2C_I810 $CONFIG_I2C_ALGOBIT + tristate ' Intel 82371AB PIIX4(E), 443MX, ServerWorks OSB4/CSB5, SMSC Victory66' CONFIG_I2C_PIIX4 + tristate ' SiS 5595' CONFIG_I2C_SIS5595 + dep_tristate ' VIA Technologies, Inc. VT82C586B' CONFIG_I2C_VIA $CONFIG_I2C_ALGOBIT + tristate ' VIA Technologies, Inc. VT596A/B, 686A/B, 8233' CONFIG_I2C_VIAPRO + dep_tristate ' Voodoo3 I2C interface' CONFIG_I2C_VOODOO3 $CONFIG_I2C_ALGOBIT + tristate ' Pseudo ISA adapter (for some hardware sensors)' CONFIG_I2C_ISA + fi + # This is needed for automatic patch generation: sensors code ends here --- linux-old/drivers/i2c/Makefile Mon Mar 25 15:28:42 CET 2002 +++ linux/drivers/i2c/Makefile Mon Mar 25 15:28:42 CET 2002 @@ -18,4 +18,17 @@ # This is needed for automatic patch generation: sensors code starts here +obj-$(CONFIG_I2C_ALI1535) += i2c-ali1535.o +obj-$(CONFIG_I2C_ALI15X3) += i2c-ali15x3.o +obj-$(CONFIG_I2C_AMD756) += i2c-amd756.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_ISA) += i2c-isa.o +obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o +obj-$(CONFIG_I2C_SIS5595) += i2c-sis5595.o +obj-$(CONFIG_I2C_TSUNAMI) += i2c-tsunami.o +obj-$(CONFIG_I2C_VIA) += i2c-via.o +obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o +obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o # This is needed for automatic patch generation: sensors code ends here --- linux-old/drivers/i2c/i2c-core.c Mon Mar 25 15:28:42 CET 2002 +++ linux/drivers/i2c/i2c-core.c Mon Mar 25 15:28:42 CET 2002 @@ -1351,4 +1351,43 @@ /* This is needed for automatic patch generation: sensors code starts here */ +#ifdef CONFIG_I2C_ALI1535 + extern int i2c_ali1535_init(void); +#endif +#ifdef CONFIG_I2C_ALI15X3 + extern int i2c_ali15x3_init(void); +#endif +#ifdef CONFIG_I2C_AMD756 + extern int i2c_amd756_init(void); +#endif +#ifdef CONFIG_I2C_HYDRA + extern int i2c_hydra_init(void); +#endif +#ifdef CONFIG_I2C_I801 + extern int i2c_i801_init(void); +#endif +#ifdef CONFIG_I2C_I810 + extern int i2c_i810_init(void); +#endif +#ifdef CONFIG_I2C_ISA + extern int i2c_isa_init(void); +#endif +#ifdef CONFIG_I2C_PIIX4 + extern int i2c_piix4_init(void); +#endif +#ifdef CONFIG_I2C_SIS5595 + extern int i2c_sis5595_init(void); +#endif +#ifdef CONFIG_I2C_TSUNAMI + extern int i2c_tsunami_init(void); +#endif +#ifdef CONFIG_I2C_VIA + extern int i2c_via_init(void); +#endif +#ifdef CONFIG_I2C_VIAPRO + extern int i2c_vt596_init(void); +#endif +#ifdef CONFIG_I2C_VOODOO3 + extern int i2c_voodoo3_init(void); +#endif /* This is needed for automatic patch generation: sensors code ends here */ @@ -1396,4 +1435,43 @@ #endif /* This is needed for automatic patch generation: sensors code starts here */ +#ifdef CONFIG_I2C_ALI1535 + i2c_ali1535_init(); +#endif +#ifdef CONFIG_I2C_ALI15X3 + i2c_ali15x3_init(); +#endif +#ifdef CONFIG_I2C_AMD756 + i2c_amd756_init(); +#endif +#ifdef CONFIG_I2C_HYDRA + i2c_hydra_init(); +#endif +#ifdef CONFIG_I2C_I801 + i2c_i801_init(); +#endif +#ifdef CONFIG_I2C_I810 + i2c_i810_init(); +#endif +#ifdef CONFIG_I2C_PIIX4 + i2c_piix4_init(); +#endif +#ifdef CONFIG_I2C_SIS5595 + i2c_sis5595_init(); +#endif +#ifdef CONFIG_I2C_TSUNAMI + i2c_tsunami_init(); +#endif +#ifdef CONFIG_I2C_VIA + i2c_via_init(); +#endif +#ifdef CONFIG_I2C_VIAPRO + i2c_vt596_init(); +#endif +#ifdef CONFIG_I2C_VOODOO3 + i2c_voodoo3_init(); +#endif +#ifdef CONFIG_I2C_ISA + i2c_isa_init(); +#endif /* This is needed for automatic patch generation: sensors code ends here */ --- linux-old/Documentation/Configure.help Mon Mar 25 15:28:42 CET 2002 +++ linux/Documentation/Configure.help Mon Mar 25 15:28:42 CET 2002 @@ -24706,3 +24706,336 @@ # adaptive-fill:nil # fill-column:70 +I2C mainboard interfaces +CONFIG_I2C_MAINBOARD + Many modern mainboards have some kind of I2C interface integrated. This + is often in the form of a SMBus, or System Management Bus, which is + basically the same as I2C but which uses only a subset of the I2C + protocol. + + You will also want the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Acer Labs ALI 1535 +CONFIG_I2C_ALI1535 + If you say yes to this option, support will be included for the Acer + Labs ALI 1535 mainboard I2C interface. This can also be + built as a module. + +Acer Labs ALI 1533 and 1543C +CONFIG_I2C_ALI15X3 + If you say yes to this option, support will be included for the Acer + Labs ALI 1533 and 1543C mainboard I2C interfaces. This can also be + built as a module which can be inserted and removed while the kernel + is running. + +AMD 756/766/768 +CONFIG_I2C_AMD756 + If you say yes to this option, support will be included for the AMD + 756/766/768 mainboard I2C interfaces. This can also be + built as a module which can be inserted and removed while the kernel + is running. + +Apple Hydra Mac I/O +CONFIG_I2C_HYDRA + If you say yes to this option, support will be included for the + Hydra mainboard I2C interface. This can also be built as a module + which can be inserted and removed while the kernel is running. + +Intel I801 +CONFIG_I2C_I801 + If you say yes to this option, support will be included for the + Intel I801 mainboard I2C interfaces. "I810" mainboard sensor chips are + generally located on the I801's I2C bus. This can also be + built as a module which can be inserted and removed while the kernel + is running. + +Intel I810/I815 based Mainboard +CONFIG_I2C_I810 + If you say yes to this option, support will be included for the + Intel I810/I815 mainboard I2C interfaces. The I2C busses these chips + are generally used only for video devices. For "810" mainboard sensor + chips, use the I801 I2C driver instead. This can also be + built as a module which can be inserted and removed while the kernel + is running. + +Intel 82371AB PIIX4(E) / ServerWorks OSB4 and CSB5 +CONFIG_I2C_PIIX4 + If you say yes to this option, support will be included for the + Intel PIIX4, PIIX4E, and 443MX, Serverworks OSB4/CSB5, + and SMSC Victory66 mainboard + I2C interfaces. This can also be + built as a module which can be inserted and removed while the kernel + is running. + +Silicon Integrated Systems Corp. SiS5595 based Mainboard +CONFIG_I2C_SIS5595 + If you say yes to this option, support will be included for the + SiS5595 mainboard I2C interfaces. For integrated sensors on the + Sis5595, use CONFIG_SENSORS_SIS5595. This can also be + built as a module which can be inserted and removed while the kernel + is running. + +VIA Technologies, Inc. VT82C586B +CONFIG_I2C_VIA + If you say yes to this option, support will be included for the VIA + Technologies I2C adapter found on some motherboards. This can also + be built as a module which can be inserted and removed while the + kernel is running. + +VIA Technologies, Inc. VT82C596, 596B, 686A/B, 8233 +CONFIG_I2C_VIAPRO + If you say yes to this option, support will be included for the VIA + Technologies I2C adapter on these chips. For integrated sensors on the + Via 686A/B, use CONFIG_SENSORS_VIA686A. This can also be + be built as a module which can be inserted and removed while the + kernel is running. + +3DFX Banshee / Voodoo3 +CONFIG_I2C_VOODOO3 + If you say yes to this option, support will be included for the + 3DFX Banshee and Voodoo3 I2C interfaces. The I2C busses on the these + chips are generally used only for video devices. + This can also be + built as a module which can be inserted and removed while the kernel + is running. + +DEC Tsunami 21272 +CONFIG_I2C_TSUNAMI + If you say yes to this option, support will be included for the DEC + Tsunami chipset I2C adapter. Requires the Alpha architecture; + do not enable otherwise. This can also be built as a module which + can be inserted and removed while the kernel is running. + +Pseudo ISA adapter (for hardware sensors modules) +CONFIG_I2C_ISA + This provides support for accessing some hardware sensor chips over + the ISA bus rather than the I2C or SMBus. If you want to do this, + say yes here. This feature can also be built as a module which can + be inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Analog Devices ADM1021 and compatibles +CONFIG_SENSORS_ADM1021 + If you say yes here you get support for Analog Devices ADM1021 + and ADM1023 sensor chips and clones: Maxim MAX1617 and MAX1617A, + Genesys Logic GL523SM, National Semi LM84, TI THMC10, + and the XEON processor built-in sensor. This can also + be built as a module which can be inserted and removed while the + kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Analog Devices ADM1024 +CONFIG_SENSORS_ADM1024 + If you say yes here you get support for Analog Devices ADM1024 sensor + chips. This can also be built as a module. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Analog Devices ADM1025 +CONFIG_SENSORS_ADM1025 + If you say yes here you get support for Analog Devices ADM1025 sensor + chips. This can also be built as a module which can be inserted and + removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Analog Devices ADM9240 and compatibles +CONFIG_SENSORS_ADM9240 + If you say yes here you get support for Analog Devices ADM9240 + sensor chips and clones: the Dallas Semiconductor DS1780 and + the National Semiconductor LM81. This can also be built as a + module which can be inserted and removed while the kernel is + running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Dallas DS1621 and DS1625 +CONFIG_SENSORS_DS1621 + If you say yes here you get support for the Dallas DS1621 and DS1625x + sensor chips. This can also be built as a module. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Fujitsu-Siemens Poseidon +CONFIG_SENSORS_FSCPOS + If you say yes here you get support for the Fujitsu-Siemens Poseidon + sensor chip. This can also be built as a module. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Fujitsu-Siemens Scylla +CONFIG_SENSORS_FSCSCY + If you say yes here you get support for the Fujitsu-Siemens Scylla + sensor chip. This can also be built as a module. This driver may/should + also work with the following Fujitsu-Siemens chips: "Poseidon", + "Poseidon II" and "Hydra". You may have to force loading of the module + for motherboards in these cases. Be careful - those motherboards have + not been tested with this driver. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Genesys Logic GL518SM +CONFIG_SENSORS_GL518SM + If you say yes here you get support for Genesys Logic GL518SM sensor + chips. This can also be built as a module which can be inserted and + removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Genesys Logic GL520SM +CONFIG_SENSORS_GL520SM + If you say yes here you get support for Genesys Logic GL518SM sensor + chips. This can also be built as a module which can be inserted and + removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +HP Maxilife +CONFIG_SENSORS_MAXILIFE + If you say yes here you get support for the HP Maxilife + sensor chip. This can also be built as a module. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +ITE 8705, 8712, Sis950 +CONFIG_SENSORS_IT87 + If you say yes here you get support for the ITE 8705 and 8712 and + SiS950 sensor chips. This can also be built as a module. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Myson MTP008 +CONFIG_SENSORS_MTP008 + If you say yes here you get support for the Myson MTP008 + sensor chip. This can also be built as a module. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +National Semiconductors LM75 and compatibles +CONFIG_SENSORS_LM75 + If you say yes here you get support for National Semiconductor LM75 + sensor chips and clones: Dallas Semi DS75 and DS1775, TelCon + TCN75, and National Semi LM77. This can also be built as a module which + can be inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +National Semiconductors LM78 +CONFIG_SENSORS_LM78 + If you say yes here you get support for National Semiconductor LM78 + sensor chips family: the LM78-J and LM79. Many clone chips will + also work at least somewhat with this driver. This can also be built + as a module which can be inserted and removed while the kernel is + running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +National Semiconductors LM80 +CONFIG_SENSORS_LM80 + If you say yes here you get support for National Semiconductor LM80 + sensor chips. This can also be built as a module which can be + inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +National Semiconductors LM87 +CONFIG_SENSORS_LM87 + If you say yes here you get support for National Semiconductor LM87 + sensor chips. This can also be built as a module which can be + inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Silicon Integrated Systems Corp. SiS5595 Sensor +CONFIG_SENSORS_SIS5595 + If you say yes here you get support for the integrated sensors in + SiS5595 South Bridges. This can also be built as a module + which can be inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Texas Instruments THMC50 / Analog Devices ADM1022 +CONFIG_SENSORS_THMC50 + If you say yes here you get support for Texas Instruments THMC50 + sensor chips and clones: the Analog Devices ADM1022. + This can also be built as a module which + can be inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Via VT82C686A/B +CONFIG_SENSORS_VIA686A + If you say yes here you get support for the integrated sensors in + Via 686A/B South Bridges. This can also be built as a module + which can be inserted and removed while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +Winbond W83781D, W83782D, W83783S, W83627HF, AS99127F +CONFIG_SENSORS_W83781D + If you say yes here you get support for the Winbond W8378x series + of sensor chips: the W83781D, W83782D, W83783S and W83682HF, + and the similar Asus AS99127F. This + can also be built as a module which can be inserted and removed + while the kernel is running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + +EEprom (DIMM) reader +CONFIG_SENSORS_EEPROM + If you say yes here you get read-only access to the EEPROM data + available on modern memory DIMMs, and which could theoretically + also be available on other devices. This can also be built as a + module which can be inserted and removed while the kernel is + running. + + You will also need the latest user-space utilties: you can find them + in the lm_sensors package, which you can download at + http://www.lm-sensors.nu + # End: --- linux-old/MAINTAINERS Mon Mar 25 15:28:42 CET 2002 +++ linux/MAINTAINERS Mon Mar 25 15:28:42 CET 2002 @@ -1356,4 +1356,13 @@ S: Maintained +SENSORS DRIVERS +P: Frodo Looijaard +M: frodol@dds.nl +P: Philip Edelbrock +M: phil@netroedge.com +L: sensors@stimpy.netroedge.com +W: http://www.lm-sensors.nu/ +S: Maintained + SGI VISUAL WORKSTATION 320 AND 540 P: Bent Hagemark