--- linux-old/drivers/i2c/dmi_scan.c Tue Jun 17 15:50:26 CEST 2003 +++ linux/drivers/i2c/dmi_scan.c Tue Jun 17 15:50:26 CEST 2003 @@ -0,0 +1,250 @@ +/* + Taken from arch/i386/kernel/dmi_scan.c. + Changes dmi_ident to be non-static so we can access. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20021208" +#define LM_VERSION "2.7.0" +#include + +/* for 2.2 kernels */ +#ifndef isa_memcpy_fromio +#define isa_memcpy_fromio(a,b,c) memcpy_fromio((a),(b),(c)) +#endif + +struct dmi_header +{ + u8 type; + u8 length; + u16 handle; +}; + +#define dmi_printk(x) +//#define dmi_printk(x) printk x + +static char * __init dmi_string(struct dmi_header *dm, u8 s) +{ + u8 *bp=(u8 *)dm; + bp+=dm->length; + if(!s) + return ""; + s--; + while(s>0 && *bp) + { + bp+=strlen(bp); + bp++; + s--; + } + return bp; +} + +/* + * We have to be cautious here. We have seen BIOSes with DMI pointers + * pointing to completely the wrong place for example + */ + +static int __init dmi_table(u32 base, int len, int num, void (*decode)(struct dmi_header *)) +{ + u8 *buf; + struct dmi_header *dm; + u8 *data; + int i=0; + + buf = ioremap(base, len); + if(buf==NULL) + return -1; + + data = buf; + + /* + * Stop when we see all the items the table claimed to have + * OR we run off the end of the table (also happens) + */ + + while(ilength; + while(data-buf>4, buf[14]&0x0F)); + else + dmi_printk((KERN_INFO "DMI present.\n")); + dmi_printk((KERN_INFO "%d structures occupying %d bytes.\n", + num, len)); + dmi_printk((KERN_INFO "DMI table at 0x%08X.\n", + base)); + if(dmi_table(base,len, num, decode)==0) + return 0; + } + fp+=16; + } + return -1; +} + + +char *dmi_ident[DMI_STRING_MAX]; + +/* + * Save a DMI string + */ + +static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string) +{ + char *d = (char*)dm; + char *p = dmi_string(dm, d[string]); + if(p==NULL || *p == 0) + return; + if (dmi_ident[slot]) + return; + dmi_ident[slot] = kmalloc(strlen(p)+1, GFP_KERNEL); + if(dmi_ident[slot]) + strcpy(dmi_ident[slot], p); + else + printk(KERN_ERR "dmi_save_ident: out of memory.\n"); +} + +/* + * Process a DMI table entry. Right now all we care about are the BIOS + * and machine entries. For 2.5 we should pull the smbus controller info + * out of here. + */ + +static void __init dmi_decode(struct dmi_header *dm) +{ + u8 *data = (u8 *)dm; + + switch(dm->type) + { + case 0: + dmi_printk(("BIOS Vendor: %s\n", + dmi_string(dm, data[4]))); + dmi_save_ident(dm, DMI_BIOS_VENDOR, 4); + dmi_printk(("BIOS Version: %s\n", + dmi_string(dm, data[5]))); + dmi_save_ident(dm, DMI_BIOS_VERSION, 5); + dmi_printk(("BIOS Release: %s\n", + dmi_string(dm, data[8]))); + dmi_save_ident(dm, DMI_BIOS_DATE, 8); + break; + case 1: + dmi_printk(("System Vendor: %s\n", + dmi_string(dm, data[4]))); + dmi_save_ident(dm, DMI_SYS_VENDOR, 4); + dmi_printk(("Product Name: %s\n", + dmi_string(dm, data[5]))); + dmi_save_ident(dm, DMI_PRODUCT_NAME, 5); + dmi_printk(("Version: %s\n", + dmi_string(dm, data[6]))); + dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6); + dmi_printk(("Serial Number: %s\n", + dmi_string(dm, data[7]))); + break; + case 2: + dmi_printk(("Board Vendor: %s\n", + dmi_string(dm, data[4]))); + dmi_save_ident(dm, DMI_BOARD_VENDOR, 4); + dmi_printk(("Board Name: %s\n", + dmi_string(dm, data[5]))); + dmi_save_ident(dm, DMI_BOARD_NAME, 5); + dmi_printk(("Board Version: %s\n", + dmi_string(dm, data[6]))); + dmi_save_ident(dm, DMI_BOARD_VERSION, 6); + break; + } +} + +void __init dmi_scan_mach(void) +{ + int err; + printk("dmi_scan.o version %s (%s)\n", LM_VERSION, LM_DATE); + err = dmi_iterate(dmi_decode); + if(err) + printk("dmi_scan.o: SM BIOS not found\n"); + else + printk("dmi_scan.o: SM BIOS found\n"); +} + +#ifdef MODULE +MODULE_DESCRIPTION("SM BIOS DMI Scanner"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +EXPORT_SYMBOL(dmi_ident); +EXPORT_SYMBOL(dmi_scan_mach); +int init_module(void) +{ + return 0; +} + +int cleanup_module(void) +{ + return 0; +} + +#endif --- linux-old/include/linux/dmi_scan.h Tue Jun 17 15:50:26 CEST 2003 +++ linux/include/linux/dmi_scan.h Tue Jun 17 15:50:26 CEST 2003 @@ -0,0 +1,15 @@ +enum +{ + DMI_BIOS_VENDOR, + DMI_BIOS_VERSION, + DMI_BIOS_DATE, + DMI_SYS_VENDOR, + DMI_PRODUCT_NAME, + DMI_PRODUCT_VERSION, + DMI_BOARD_VENDOR, + DMI_BOARD_NAME, + DMI_BOARD_VERSION, + DMI_STRING_MAX +}; + +extern char *dmi_ident[DMI_STRING_MAX]; --- linux-old/drivers/i2c/i2c-ali1535.c Tue Jun 17 15:50:26 CEST 2003 +++ linux/drivers/i2c/i2c-ali1535.c Tue Jun 17 15:50:26 CEST 2003 @@ -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 "20021208" +#define LM_VERSION "2.7.0" +#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. */ +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 Tue Jun 17 15:50:26 CEST 2003 +++ linux/drivers/i2c/i2c-ali15x3.c Tue Jun 17 15:50:26 CEST 2003 @@ -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 "20021208" +#define LM_VERSION "2.7.0" +#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. */ +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 Tue Jun 17 15:50:26 CEST 2003 +++ linux/drivers/i2c/i2c-amd756.c Tue Jun 17 15:50:26 CEST 2003 @@ -0,0 +1,556 @@ +/* + amd756.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + + Copyright (c) 1999-2002 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. +*/ + +/* + 2002-04-08: Added nForce support. (Csaba Halasz) + 2002-10-03: Fixed nForce PnP I/O port. (Michael Steil) +*/ + +/* + Supports AMD756, AMD766, AMD768 and nVidia nForce + 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 "20021208" +#define LM_VERSION "2.7.0" +#include + +#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 +#ifndef PCI_DEVICE_ID_AMD_768_SMBUS +#define PCI_DEVICE_ID_AMD_768_SMBUS 0x7443 +#endif +#ifndef PCI_DEVICE_ID_AMD_8111_SMBUS +#define PCI_DEVICE_ID_AMD_8111_SMBUS 0x746B +#endif +#ifndef PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS +#define PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS 0x01B4 +#endif + +struct sd { + const unsigned short vendor; + const unsigned short device; + const unsigned short function; + const char* name; + int amdsetup:1; +}; + +static struct sd supported[] = { + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_756, 3, "AMD756", 1}, + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_766, 3, "AMD766", 1}, + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_768_SMBUS, 3, "AMD768", 1}, + {PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS, 3, "AMD8111", 1}, + {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_SMBUS, 1, "nVidia nForce", 0}, + {0, 0, 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 */ +#define SMBBANFORCE 0x014 + +/* general configuration */ +#define SMBGCFG 0x041 /* mh */ + +/* silicon revision code */ +#define SMBREV 0x008 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* 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 void amd756_abort(void); +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 struct sd *amd756_sd = NULL; +static unsigned short amd756_smba = 0; + +int amd756_setup(void) +{ + unsigned char temp; + struct sd *currdev; + struct pci_dev *AMD756_dev = NULL; + + if (pci_present() == 0) { + return -ENODEV; + } + + /* Look for a supported chip */ + for(currdev = supported; currdev->vendor; ) { + AMD756_dev = pci_find_device(currdev->vendor, + currdev->device, AMD756_dev); + if (AMD756_dev != NULL) { + if (PCI_FUNC(AMD756_dev->devfn) == currdev->function) + break; + } else { + currdev++; + } + } + + if (AMD756_dev == NULL) { + printk + ("i2c-amd756.o: Error: No AMD756 or compatible device detected!\n"); + return -ENODEV; + } + printk(KERN_INFO "i2c-amd756.o: Found %s SMBus controller.\n", currdev->name); + + if (currdev->amdsetup) + { + 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; + } else { + pci_read_config_word(AMD756_dev, SMBBANFORCE, &amd756_smba); + amd756_smba &= 0xfffc; + } + if(amd756_smba == 0) { + printk(KERN_ERR "i2c-amd756.o: Error: SMB base address uninitialized\n"); + return -ENODEV; + } + 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 */ + + /* store struct sd * for future reference */ + amd756_sd = currdev; + + 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) +#define GE_ABORT (1 << 5) + +void amd756_abort(void) +{ + printk("i2c-amd756.o: Sending abort.\n"); + outw_p(inw(SMB_GLOBAL_ENABLE) | GE_ABORT, SMB_GLOBAL_ENABLE); + amd756_do_pause(100); + outw_p(GS_CLEAR_STS, SMB_GLOBAL_STATUS); +} + +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); + amd756_abort(); + 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"); + amd756_abort (); + 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"); + } + + 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. */ +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) & 0x3f; + if(data->block[0] > 32) + data->block[0] = 32; + /* 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 or compatible device not detected, module not inserted.\n"); + amd756_cleanup(); + return res; + } + amd756_initialized++; + sprintf(amd756_adapter.name, "SMBus %s adapter at %04x", + amd756_sd->name, 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: %s bus detected and initialized\n", + amd756_sd->name); + 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/768/nVidia nForce SMBus driver"); + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +int init_module(void) +{ + return i2c_amd756_init(); +} + +int cleanup_module(void) +{ + return amd756_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/i2c/i2c-amd8111.c Tue Jun 17 15:50:27 CEST 2003 +++ linux/drivers/i2c/i2c-amd8111.c Tue Jun 17 15:50:27 CEST 2003 @@ -0,0 +1,521 @@ +/* + * SMBus 2.0 driver for AMD-8111 IO-Hub. + * + * Copyright (c) 2002 Vojtech Pavlik + * + * 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 version 2. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "/usr/local/include/linux/i2c.h" +#include +#define LM_DATE "20021208" +#define LM_VERSION "2.7.0" + +#ifndef I2C_HW_SMBUS_AMD8111 +#error Your i2c is too old - i2c-2.7.0 or greater required! +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) +#include +#define MAX_PCI_DEVS 8 +static struct pci_dev *amd8111_devs[MAX_PCI_DEVS] = { NULL, /* ... */ }; +static void *amd8111_drvdata[MAX_PCI_DEVS]; +static int amd8111_devcnt = 0; + +#define min_t(t, x, y) (((x)<(y))?(x):(y)) +#define __devinit +#define __devexit +#define __devinitdata +#define __devexit_p(x) x +struct pci_device_id { + unsigned int vendor, device; + unsigned int subvendor, subdevice; + unsigned int class, class_mask; + unsigned long driver_data; +}; +struct pci_driver { + struct list_head node; + char *name; + const struct pci_device_id *id_table; + int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); + void (*remove)(struct pci_dev *dev); +}; +#define PCI_ANY_ID 0xffff + +static void *pci_get_drvdata(struct pci_dev *dev) +{ + int i; + for (i = 0; i < amd8111_devcnt; i++) + if (amd8111_devs[i] == dev) + return amd8111_drvdata[i]; + return NULL; +} + +static void pci_set_drvdata(struct pci_dev *dev, void *driver_data) +{ + int i; + for (i = 0; i < amd8111_devcnt; i++) + if (amd8111_devs[i] == dev) + amd8111_drvdata[i] = driver_data; +} + +#endif + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_AUTHOR ("Vojtech Pavlik "); +MODULE_DESCRIPTION("AMD8111 SMBus 2.0 driver"); + +struct amd_smbus { + struct pci_dev *dev; + struct i2c_adapter adapter; + int base; + int size; +}; + +/* + * AMD PCI control registers definitions. + */ + +#define AMD_PCI_MISC 0x48 + +#define AMD_PCI_MISC_SCI 0x04 /* deliver SCI */ +#define AMD_PCI_MISC_INT 0x02 /* deliver PCI IRQ */ +#define AMD_PCI_MISC_SPEEDUP 0x01 /* 16x clock speedup */ + +/* + * ACPI 2.0 chapter 13 PCI interface definitions. + */ + +#define AMD_EC_DATA 0x00 /* data register */ +#define AMD_EC_SC 0x04 /* status of controller */ +#define AMD_EC_CMD 0x04 /* command register */ +#define AMD_EC_ICR 0x08 /* interrupt control register */ + +#define AMD_EC_SC_SMI 0x04 /* smi event pending */ +#define AMD_EC_SC_SCI 0x02 /* sci event pending */ +#define AMD_EC_SC_BURST 0x01 /* burst mode enabled */ +#define AMD_EC_SC_CMD 0x08 /* byte in data reg is command */ +#define AMD_EC_SC_IBF 0x02 /* data ready for embedded controller */ +#define AMD_EC_SC_OBF 0x01 /* data ready for host */ + +#define AMD_EC_CMD_RD 0x80 /* read EC */ +#define AMD_EC_CMD_WR 0x81 /* write EC */ +#define AMD_EC_CMD_BE 0x82 /* enable burst mode */ +#define AMD_EC_CMD_BD 0x83 /* disable burst mode */ +#define AMD_EC_CMD_QR 0x84 /* query EC */ + +/* + * ACPI 2.0 chapter 13 access of registers of the EC + */ + +unsigned int amd_ec_wait_write(struct amd_smbus *smbus) +{ + int timeout = 500; + + while (timeout-- && (inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_IBF)) + udelay(1); + + if (!timeout) { + printk(KERN_WARNING "i2c-amd8111.c: Timeout while waiting for IBF to clear\n"); + return -1; + } + + return 0; +} + +unsigned int amd_ec_wait_read(struct amd_smbus *smbus) +{ + int timeout = 500; + + while (timeout-- && (~inb(smbus->base + AMD_EC_SC) & AMD_EC_SC_OBF)) + udelay(1); + + if (!timeout) { + printk(KERN_WARNING "i2c-amd8111.c: Timeout while waiting for OBF to set\n"); + return -1; + } + + return 0; +} + +unsigned int amd_ec_read(struct amd_smbus *smbus, unsigned char address, unsigned char *data) +{ + if (amd_ec_wait_write(smbus)) + return -1; + outb(AMD_EC_CMD_RD, smbus->base + AMD_EC_CMD); + + if (amd_ec_wait_write(smbus)) + return -1; + outb(address, smbus->base + AMD_EC_DATA); + + if (amd_ec_wait_read(smbus)) + return -1; + *data = inb(smbus->base + AMD_EC_DATA); + + return 0; +} + +unsigned int amd_ec_write(struct amd_smbus *smbus, unsigned char address, unsigned char data) +{ + if (amd_ec_wait_write(smbus)) + return -1; + outb(AMD_EC_CMD_WR, smbus->base + AMD_EC_CMD); + + if (amd_ec_wait_write(smbus)) + return -1; + outb(address, smbus->base + AMD_EC_DATA); + + if (amd_ec_wait_write(smbus)) + return -1; + outb(data, smbus->base + AMD_EC_DATA); + + return 0; +} + +/* + * ACPI 2.0 chapter 13 SMBus 2.0 EC register model + */ + +#define AMD_SMB_PRTCL 0x00 /* protocol, PEC */ +#define AMD_SMB_STS 0x01 /* status */ +#define AMD_SMB_ADDR 0x02 /* address */ +#define AMD_SMB_CMD 0x03 /* command */ +#define AMD_SMB_DATA 0x04 /* 32 data registers */ +#define AMD_SMB_BCNT 0x24 /* number of data bytes */ +#define AMD_SMB_ALRM_A 0x25 /* alarm address */ +#define AMD_SMB_ALRM_D 0x26 /* 2 bytes alarm data */ + +#define AMD_SMB_STS_DONE 0x80 +#define AMD_SMB_STS_ALRM 0x40 +#define AMD_SMB_STS_RES 0x20 +#define AMD_SMB_STS_STATUS 0x1f + +#define AMD_SMB_STATUS_OK 0x00 +#define AMD_SMB_STATUS_FAIL 0x07 +#define AMD_SMB_STATUS_DNAK 0x10 +#define AMD_SMB_STATUS_DERR 0x11 +#define AMD_SMB_STATUS_CMD_DENY 0x12 +#define AMD_SMB_STATUS_UNKNOWN 0x13 +#define AMD_SMB_STATUS_ACC_DENY 0x17 +#define AMD_SMB_STATUS_TIMEOUT 0x18 +#define AMD_SMB_STATUS_NOTSUP 0x19 +#define AMD_SMB_STATUS_BUSY 0x1A +#define AMD_SMB_STATUS_PEC 0x1F + +#define AMD_SMB_PRTCL_WRITE 0x00 +#define AMD_SMB_PRTCL_READ 0x01 +#define AMD_SMB_PRTCL_QUICK 0x02 +#define AMD_SMB_PRTCL_BYTE 0x04 +#define AMD_SMB_PRTCL_BYTE_DATA 0x06 +#define AMD_SMB_PRTCL_WORD_DATA 0x08 +#define AMD_SMB_PRTCL_BLOCK_DATA 0x0a +#define AMD_SMB_PRTCL_PROC_CALL 0x0c +#define AMD_SMB_PRTCL_BLOCK_PROC_CALL 0x0d +#define AMD_SMB_PRTCL_I2C_BLOCK_DATA 0x4a +#define AMD_SMB_PRTCL_PEC 0x80 + + +s32 amd8111_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, union i2c_smbus_data * data) +{ + struct amd_smbus *smbus = adap->algo_data; + unsigned char protocol, len, pec, temp[2]; + int i; + + protocol = (read_write == I2C_SMBUS_READ) ? AMD_SMB_PRTCL_READ : AMD_SMB_PRTCL_WRITE; + pec = (flags & I2C_CLIENT_PEC) ? AMD_SMB_PRTCL_PEC : 0; + + switch (size) { + + case I2C_SMBUS_QUICK: + protocol |= AMD_SMB_PRTCL_QUICK; + read_write = I2C_SMBUS_WRITE; + break; + + case I2C_SMBUS_BYTE: + if (read_write == I2C_SMBUS_WRITE) + amd_ec_write(smbus, AMD_SMB_DATA, data->byte); + protocol |= AMD_SMB_PRTCL_BYTE; + break; + + case I2C_SMBUS_BYTE_DATA: + amd_ec_write(smbus, AMD_SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + amd_ec_write(smbus, AMD_SMB_DATA, data->byte); + protocol |= AMD_SMB_PRTCL_BYTE_DATA; + break; + + case I2C_SMBUS_WORD_DATA: + amd_ec_write(smbus, AMD_SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + amd_ec_write(smbus, AMD_SMB_DATA, data->word); + amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8); + } + protocol |= AMD_SMB_PRTCL_WORD_DATA | pec; + break; + + case I2C_SMBUS_BLOCK_DATA: + amd_ec_write(smbus, AMD_SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + len = min_t(u8, data->block[0], 32); + amd_ec_write(smbus, AMD_SMB_BCNT, len); + for (i = 0; i < len; i++) + amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]); + } + protocol |= AMD_SMB_PRTCL_BLOCK_DATA | pec; + break; + + case I2C_SMBUS_I2C_BLOCK_DATA: + len = min_t(u8, data->block[0], 32); + amd_ec_write(smbus, AMD_SMB_CMD, command); + amd_ec_write(smbus, AMD_SMB_BCNT, len); + if (read_write == I2C_SMBUS_WRITE) + for (i = 0; i < len; i++) + amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]); + protocol |= AMD_SMB_PRTCL_I2C_BLOCK_DATA; + break; + + case I2C_SMBUS_PROC_CALL: + amd_ec_write(smbus, AMD_SMB_CMD, command); + amd_ec_write(smbus, AMD_SMB_DATA, data->word); + amd_ec_write(smbus, AMD_SMB_DATA + 1, data->word >> 8); + protocol = AMD_SMB_PRTCL_PROC_CALL | pec; + read_write = I2C_SMBUS_READ; + break; + + case I2C_SMBUS_BLOCK_PROC_CALL: + protocol |= pec; + len = min_t(u8, data->block[0], 31); + amd_ec_write(smbus, AMD_SMB_CMD, command); + amd_ec_write(smbus, AMD_SMB_BCNT, len); + for (i = 0; i < len; i++) + amd_ec_write(smbus, AMD_SMB_DATA + i, data->block[i + 1]); + protocol = AMD_SMB_PRTCL_BLOCK_PROC_CALL | pec; + read_write = I2C_SMBUS_READ; + break; + + case I2C_SMBUS_WORD_DATA_PEC: + case I2C_SMBUS_BLOCK_DATA_PEC: + case I2C_SMBUS_PROC_CALL_PEC: + case I2C_SMBUS_BLOCK_PROC_CALL_PEC: + printk(KERN_WARNING "i2c-amd8111.c: Unexpected software PEC transaction %d\n.", size); + return -1; + + default: + printk(KERN_WARNING "i2c-amd8111.c: Unsupported transaction %d\n", size); + return -1; + } + + amd_ec_write(smbus, AMD_SMB_ADDR, addr << 1); + amd_ec_write(smbus, AMD_SMB_PRTCL, protocol); + + amd_ec_read(smbus, AMD_SMB_STS, temp + 0); + + if (~temp[0] & AMD_SMB_STS_DONE) { + udelay(500); + amd_ec_read(smbus, AMD_SMB_STS, temp + 0); + } + + if (~temp[0] & AMD_SMB_STS_DONE) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ/100); + amd_ec_read(smbus, AMD_SMB_STS, temp + 0); + } + + if ((~temp[0] & AMD_SMB_STS_DONE) || (temp[0] & AMD_SMB_STS_STATUS)) + return -1; + + if (read_write == I2C_SMBUS_WRITE) + return 0; + + switch (size) { + + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: + amd_ec_read(smbus, AMD_SMB_DATA, &data->byte); + break; + + case I2C_SMBUS_WORD_DATA: + case I2C_SMBUS_PROC_CALL: + amd_ec_read(smbus, AMD_SMB_DATA, temp + 0); + amd_ec_read(smbus, AMD_SMB_DATA + 1, temp + 1); + data->word = (temp[1] << 8) | temp[0]; + break; + + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_PROC_CALL: + amd_ec_read(smbus, AMD_SMB_BCNT, &len); + len = min_t(u8, len, 32); + case I2C_SMBUS_I2C_BLOCK_DATA: + for (i = 0; i < len; i++) + amd_ec_read(smbus, AMD_SMB_DATA + i, data->block + i + 1); + data->block[0] = len; + break; + } + + return 0; +} + +void amd8111_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void amd8111_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +u32 amd8111_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 | I2C_FUNC_SMBUS_BLOCK_PROC_CALL | + I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC; +} + +static struct i2c_algorithm smbus_algorithm = { + .name = "Non-I2C SMBus 2.0 adapter", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = amd8111_access, + .functionality = amd8111_func, +}; + +static int __devinit amd8111_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct amd_smbus *smbus; + + if (~pci_resource_flags(dev, 0) & IORESOURCE_IO) + return -1; + + if (!(smbus = kmalloc(sizeof(struct amd_smbus), GFP_KERNEL))) + return -1; + memset(smbus, 0, sizeof(struct amd_smbus)); + + pci_set_drvdata(dev, smbus); + smbus->dev = dev; + smbus->base = pci_resource_start(dev, 0); + smbus->size = pci_resource_len(dev, 0); + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,14) + if (!request_region(smbus->base, smbus->size, "amd8111 SMBus 2.0")) { + kfree(smbus); + return -1; + } +#else + if (check_region(smbus->base, smbus->size) < 0) { + kfree(smbus); + return -1; + } + request_region(smbus->base, smbus->size, "amd8111 SMBus 2.0"); +#endif + + sprintf(smbus->adapter.name, "SMBus2 AMD8111 adapter at %04x", smbus->base); + smbus->adapter.id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_AMD8111; + smbus->adapter.algo = &smbus_algorithm; + smbus->adapter.algo_data = smbus; + smbus->adapter.inc_use = amd8111_inc; + smbus->adapter.dec_use = amd8111_dec; + + if (i2c_add_adapter(&smbus->adapter)) { + printk(KERN_WARNING "i2c-amd8111.c: Failed to register adapter.\n"); + release_region(smbus->base, smbus->size); + kfree(smbus); + return -1; + } + + pci_write_config_dword(smbus->dev, AMD_PCI_MISC, 0); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) + printk(KERN_INFO "i2c-amd8111.c: AMD8111 SMBus 2.0 adapter at %#x\n", smbus->base); +#else + printk(KERN_INFO "i2c-amd8111.c: AMD8111 SMBus 2.0 adapter at pci%s\n", dev->slot_name); +#endif + return 0; +} + +static void __devexit amd8111_remove(struct pci_dev *dev) +{ + struct amd_smbus *smbus = pci_get_drvdata(dev); + if (i2c_del_adapter(&smbus->adapter)) { + printk(KERN_WARNING "i2c-amd8111.c: Failed to unregister adapter.\n"); + return; + } + release_region(smbus->base, smbus->size); + kfree(smbus); +} + +static struct pci_device_id amd8111_id_table[] __devinitdata = +{{ 0x1022, 0x746a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 }, + { 0 }}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) + +int i2c_amd8111_init(void) +{ + struct pci_dev *pci; + struct pci_device_id *id; + int found = 0; + + for (id = amd8111_id_table; id->vendor; id++) { + pci = NULL; + while ((pci = pci_find_device(id->vendor, id->device, pci))) + if (amd8111_devcnt < 8 && !amd8111_probe(pci, id)) + amd8111_devs[amd8111_devcnt++] = pci; + } + + return found ? 0 : -ENODEV; +} + +int __init amd8111_init(void) +{ + return i2c_amd8111_init(); +} + +void __exit amd8111_exit(void) +{ + int i; + for (i = 0; i < amd8111_devcnt; i++) + amd8111_remove(amd8111_devs[i]); +} + +#else + +static struct pci_driver amd8111_driver = { + .name = "amd8111 smbus 2.0", + .id_table = amd8111_id_table, + .probe = amd8111_probe, + .remove = __devexit_p(amd8111_remove), +}; + +int __init amd8111_init(void) +{ + return pci_module_init(&amd8111_driver); +} + +void __exit amd8111_exit(void) +{ + pci_unregister_driver(&amd8111_driver); +} + + +MODULE_DEVICE_TABLE(pci, amd8111_id_table); + +#endif + +module_init(amd8111_init); +module_exit(amd8111_exit); + --- linux-old/drivers/i2c/i2c-hydra.c Tue Jun 17 15:50:27 CEST 2003 +++ linux/drivers/i2c/i2c-hydra.c Tue Jun 17 15:50:27 CEST 2003 @@ -0,0 +1,208 @@ +/* + 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); + pdregr(); /* flush posted write */ +} + +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); + pdregr(); /* flush posted write */ +} + +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 Tue Jun 17 15:50:27 CEST 2003 +++ linux/drivers/i2c/i2c-i801.c Tue Jun 17 15:50:27 CEST 2003 @@ -0,0 +1,766 @@ +/* + i801.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2002 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 + 82801DB 24C3 (HW PEC supported, 32 byte buffer not supported) + + 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. + I2C Block Read and Process Call are not supported. +*/ + +/* Note: we assume there can only be one I801, with one SMBus interface */ + +/* #define DEBUG 1 */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20021208" +#define LM_VERSION "2.7.0" + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifdef I2C_FUNC_SMBUS_BLOCK_DATA_PEC +#define HAVE_PEC +#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 +#define PCI_DEVICE_ID_INTEL_82801DB_SMBUS 0x24C3 + +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, + PCI_DEVICE_ID_INTEL_82801DB_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) +#define SMBPEC (8 + i801_smba) /* ICH4 only */ +#define SMBAUXSTS (12 + i801_smba) /* ICH4 only */ +#define SMBAUXCTL (13 + i801_smba) /* ICH4 only */ + +/* 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 100 +#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */ + +/* I801 command constants */ +#define I801_QUICK 0x00 +#define I801_BYTE 0x04 +#define I801_BYTE_DATA 0x08 +#define I801_WORD_DATA 0x0C +#define I801_PROC_CALL 0x10 /* later chips only, unimplemented */ +#define I801_BLOCK_DATA 0x14 +#define I801_I2C_BLOCK_DATA 0x18 /* unimplemented */ +#define I801_BLOCK_LAST 0x34 +#define I801_I2C_BLOCK_LAST 0x38 /* unimplemented */ +#define I801_START 0x40 +#define I801_PEC_EN 0x80 /* ICH4 only */ + +/* insmod parameters */ + +/* 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 command); +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; +static int isich4 = 0; + +/* 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(KERN_WARNING "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 + (KERN_WARNING "i2c-i801.o: Error: Can't detect I801, function 3!\n"); + error_return = -ENODEV; + goto END; + } + isich4 = *num == PCI_DEVICE_ID_INTEL_82801DB_SMBUS; + +/* Determine the address of the SMBus areas */ + if (force_addr) { + i801_smba = force_addr & 0xfff0; + } else { + pci_read_config_word(I801_dev, SMBBA, &i801_smba); + i801_smba &= 0xfff0; + if(i801_smba == 0) { + printk(KERN_ERR "i2c-i801.o: SMB base address uninitialized - upgrade BIOS or use force_addr=0xaddr\n"); + return -ENODEV; + } + } + + if (check_region(i801_smba, (isich4 ? 16 : 8))) { + printk + (KERN_ERR "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); + temp &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */ + pci_write_config_byte(I801_dev, SMBHSTCFG, temp); +/* If force_addr is set, we program the new address here. Just to make + sure, we disable the device 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 + (KERN_WARNING "i2c-i801.o: WARNING: I801 SMBus interface set to new " + "address %04x!\n", i801_smba); + } else if ((temp & 1) == 0) { + pci_write_config_byte(I801_dev, SMBHSTCFG, temp | 1); + printk(KERN_WARNING "i2c-i801.o: enabling SMBus device\n"); + } + + request_region(i801_smba, (isich4 ? 16 : 8), "i801-smbus"); + +#ifdef DEBUG + if (temp & 0x02) + printk + (KERN_DEBUG "i2c-i801.o: I801 using Interrupt SMI# for SMBus.\n"); + else + printk + (KERN_DEBUG "i2c-i801.o: I801 using PCI Interrupt for SMBus.\n"); + + pci_read_config_byte(I801_dev, SMBREV, &temp); + printk(KERN_DEBUG "i2c-i801.o: SMBREV = 0x%X\n", temp); + printk(KERN_DEBUG "i2c-i801.o: I801_smba = 0x%X\n", i801_smba); +#endif /* DEBUG */ + + END: + return error_return; +} + + +void i801_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +int i801_transaction(void) +{ + int temp; + int result = 0; + int timeout = 0; + +#ifdef DEBUG + printk + (KERN_DEBUG "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(KERN_DEBUG "i2c-i801.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if ((temp = (0x1f & inb_p(SMBHSTSTS))) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Failed! (%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Successfull!\n"); +#endif + } + } + + outb_p(inb(SMBHSTCNT) | I801_START, 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(KERN_DEBUG "i2c-i801.o: SMBus Timeout!\n"); + result = -1; +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: Error: Failed bus transaction\n"); +#endif + } + + if (temp & 0x08) { + result = -1; + printk + (KERN_ERR "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(KERN_DEBUG "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 + (KERN_DEBUG "i2c-i801.o: Failed reset at end of transaction (%02x)\n", + temp); +#endif + } +#ifdef DEBUG + printk + (KERN_DEBUG "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 command) +{ + int i, len; + int smbcmd; + int temp; + int result = 0; + int timeout; + unsigned char hostc, errmask; + + if (command == I2C_SMBUS_I2C_BLOCK_DATA) { + 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 */ + } + + if(isich4 && command != I2C_SMBUS_I2C_BLOCK_DATA) { + /* set 32 byte buffer */ + } + + for (i = 1; i <= len; i++) { + if (i == len && read_write == I2C_SMBUS_READ) + smbcmd = I801_BLOCK_LAST; + else + smbcmd = I801_BLOCK_DATA; +#if 0 /* now using HW PEC */ + if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) + smbcmd |= I801_PEC_EN; +#endif + outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT); + +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Block (pre %d): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, BLKDAT=%02x\n", i, 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 + (KERN_DEBUG "i2c-i801.o: SMBus busy (%02x). Resetting... \n", + temp); +#endif + outb_p(temp, SMBHSTSTS); + if (((temp = inb_p(SMBHSTSTS)) & errmask) != 0x00) { + printk + (KERN_ERR "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; + } + } + + if (i == 1) { +#if 0 /* #ifdef HAVE_PEC (now using HW PEC) */ + if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) { + if(read_write == I2C_SMBUS_WRITE) + outb_p(data->block[len + 1], SMBPEC); + } +#endif + outb_p(inb(SMBHSTCNT) | I801_START, SMBHSTCNT); + } + + /* We will always wait for a fraction of a second! */ + timeout = 0; + do { + temp = inb_p(SMBHSTSTS); + i801_do_pause(1); + } + while ((!(temp & 0x80)) + && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-i801.o: SMBus Timeout!\n"); +#endif + } + + if (temp & 0x10) { + result = -1; +#ifdef DEBUG + printk + (KERN_DEBUG "i2c-i801.o: Error: Failed bus transaction\n"); +#endif + } else if (temp & 0x08) { + result = -1; + printk(KERN_ERR "i2c-i801.o: Bus collision!\n"); + } else if (temp & 0x04) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "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 */ + +#ifdef DEBUG + if ((temp = (0x1e & inb_p(SMBHSTSTS))) != 0x00) { + printk + (KERN_DEBUG "i2c-i801.o: Bad status (%02x) at end of transaction\n", + temp); + } + printk + (KERN_DEBUG "i2c-i801.o: Block (post %d): CNT=%02x, CMD=%02x, ADD=%02x, " + "DAT0=%02x, BLKDAT=%02x\n", i, inb_p(SMBHSTCNT), + inb_p(SMBHSTCMD), inb_p(SMBHSTADD), inb_p(SMBHSTDAT0), + inb_p(SMBBLKDAT)); +#endif + + if (result < 0) + goto END; + } + +#ifdef HAVE_PEC + if(isich4 && command == I2C_SMBUS_BLOCK_DATA_PEC) { + /* wait for INTR bit as advised by Intel */ + timeout = 0; + do { + temp = inb_p(SMBHSTSTS); + i801_do_pause(1); + } while ((!(temp & 0x02)) + && (timeout++ < MAX_TIMEOUT)); + + if (timeout >= MAX_TIMEOUT) { + printk(KERN_DEBUG "i2c-i801.o: PEC Timeout!\n"); + } +#if 0 /* now using HW PEC */ + if(read_write == I2C_SMBUS_READ) { + data->block[len + 1] = inb_p(SMBPEC); + } +#endif + outb_p(temp, SMBHSTSTS); + } +#endif + result = 0; +END: + if (command == I2C_SMBUS_I2C_BLOCK_DATA) { + /* restore saved configuration register value */ + pci_write_config_byte(I801_dev, SMBHSTCFG, hostc); + } + return result; +} + +/* Return -1 on error. */ +s32 i801_access(struct i2c_adapter * adap, u16 addr, unsigned short flags, + char read_write, u8 command, int size, + union i2c_smbus_data * data) +{ + int hwpec = 0; + int block = 0; + int ret, xact = 0; + +#ifdef HAVE_PEC + if(isich4) + hwpec = (flags & I2C_CLIENT_PEC) != 0; +#endif + + switch (size) { + case I2C_SMBUS_QUICK: + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + xact = 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); + xact = 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); + xact = 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); + } + xact = I801_WORD_DATA; + break; + case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_I2C_BLOCK_DATA: +#ifdef HAVE_PEC + case I2C_SMBUS_BLOCK_DATA_PEC: + if(hwpec && size == I2C_SMBUS_BLOCK_DATA) + size = I2C_SMBUS_BLOCK_DATA_PEC; +#endif + outb_p(((addr & 0x7f) << 1) | (read_write & 0x01), + SMBHSTADD); + outb_p(command, SMBHSTCMD); + block = 1; + break; + case I2C_SMBUS_PROC_CALL: + default: + printk(KERN_ERR "i2c-i801.o: Unsupported transaction %d\n", size); + return -1; + } + +#ifdef HAVE_PEC + if(isich4 && hwpec) { + if(size != I2C_SMBUS_QUICK && + size != I2C_SMBUS_I2C_BLOCK_DATA) + outb_p(1, SMBAUXCTL); /* enable HW PEC */ + } +#endif + if(block) + ret = i801_block_transaction(data, read_write, size); + else { + outb_p(xact | ENABLE_INT9, SMBHSTCNT); + ret = i801_transaction(); + } + +#ifdef HAVE_PEC + if(isich4 && hwpec) { + if(size != I2C_SMBUS_QUICK && + size != I2C_SMBUS_I2C_BLOCK_DATA) + outb_p(0, SMBAUXCTL); + } +#endif + + if(block) + return ret; + if(ret) + return -1; + if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK)) + return 0; + + switch (xact & 0x7f) { + case I801_BYTE: /* Result put in SMBHSTDAT0 */ + 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 +#ifdef HAVE_PEC + | (isich4 ? I2C_FUNC_SMBUS_BLOCK_DATA_PEC | + I2C_FUNC_SMBUS_HWPEC_CALC + : 0) +#endif + ; +} + +int __init i2c_i801_init(void) +{ + int res; + printk(KERN_INFO "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 + (KERN_DEBUG "i2c-i801.o: Oops, i801_init called a second time!\n"); + return -EBUSY; + } +#endif + i801_initialized = 0; + if ((res = i801_setup())) { + printk + (KERN_WARNING "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 + (KERN_ERR "i2c-i801.o: Adapter registration failed, module not inserted.\n"); + i801_cleanup(); + return res; + } + i801_initialized++; + printk(KERN_INFO "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 + (KERN_ERR "i2c-i801.o: i2c_del_adapter failed, module not removed\n"); + return res; + } else + i801_initialized--; + } + if (i801_initialized >= 1) { + release_region(i801_smba, (isich4 ? 16 : 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 Tue Jun 17 15:50:27 CEST 2003 +++ linux/drivers/i2c/i2c-i810.c Tue Jun 17 15:50:27 CEST 2003 @@ -0,0 +1,359 @@ +/* + 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 "20021208" +#define LM_VERSION "2.7.0" +#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, + 0x2562, + 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 (HZ / 2) + +#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); + readlong(I810_GPIOB); /* flush posted write */ +} + +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); + readlong(I810_GPIOB); /* flush posted write */ +} + +/* 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); + readlong(I810_GPIOA); /* flush posted write */ +} + +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); + readlong(I810_GPIOA); /* flush posted write */ +} + +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); + if(mem) { + 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); + if(!mem) + return -ENOMEM; + 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 Tue Jun 17 15:50:27 CEST 2003 +++ linux/drivers/i2c/i2c-isa.c Tue Jun 17 15:50:27 CEST 2003 @@ -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 "20021208" +#define LM_VERSION "2.7.0" + +#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 Tue Jun 17 15:50:27 CEST 2003 +++ linux/drivers/i2c/i2c-piix4.c Tue Jun 17 15:50:27 CEST 2003 @@ -0,0 +1,610 @@ +/* + piix4.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2002 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 +#include +#define LM_DATE "20021208" +#define LM_VERSION "2.7.0" +#include +#include +#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; + +#ifdef CONFIG_X86 +/* + * Get DMI information. + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,34) +void dmi_scan_mach(void); +#endif +static int __init ibm_dmi_probe(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,34) + extern int is_unsafe_smbus; + return is_unsafe_smbus; +#else +#define IBM_SIGNATURE "IBM" + dmi_scan_mach(); + if(dmi_ident[DMI_SYS_VENDOR] == NULL) + return 0; + if(strncmp(dmi_ident[DMI_SYS_VENDOR], IBM_SIGNATURE, + strlen(IBM_SIGNATURE)) == 0) + return 1; + return 0; +#endif +} +#endif + +/* 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); + +#ifdef CONFIG_X86 + if(ibm_dmi_probe()) { + printk + (KERN_ERR "i2c-piix4.o: IBM Laptop detected; this module may corrupt\n"); + printk + (KERN_ERR " your serial eeprom! Refusing to load module!\n"); + error_return = -EPERM; + goto END; + } +#endif + +/* 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. */ +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: Module insertion failed.\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-savage4.c Tue Jun 17 15:50:27 CEST 2003 +++ linux/drivers/i2c/i2c-savage4.c Tue Jun 17 15:50:27 CEST 2003 @@ -0,0 +1,376 @@ +/* + i2c-savage4.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 Savage4 to gain access to + the BT869 and possibly other I2C devices. The DDC bus is not + yet supported because its register is not memory-mapped. + However we leave the DDC code here, commented out, to make + it easier to add later. +*/ + +#include +#include +#include +#include +#include +#include +#define LM_DATE "20021208" +#define LM_VERSION "2.7.0" +#include + +/* 3DFX defines */ +/* #define PCI_VENDOR_ID_S3 0x5333 */ +#define PCI_CHIP_SAVAGE3D 0x8A20 +#define PCI_CHIP_SAVAGE3D_MV 0x8A21 +#define PCI_CHIP_SAVAGE4 0x8A22 +#define PCI_CHIP_SAVAGE2000 0x9102 +#define PCI_CHIP_PROSAVAGE_PM 0x8A25 +#define PCI_CHIP_PROSAVAGE_KM 0x8A26 +#define PCI_CHIP_SAVAGE_MX_MV 0x8c10 +#define PCI_CHIP_SAVAGE_MX 0x8c11 +#define PCI_CHIP_SAVAGE_IX_MV 0x8c12 +#define PCI_CHIP_SAVAGE_IX 0x8c13 + +#define REG 0xff20 /* Serial Port 1 Register */ + +/* 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 0x00000020 +#define I2C_SCL_OUT 0x00000001 +#define I2C_SDA_OUT 0x00000002 +#define I2C_SCL_IN 0x00000008 +#define I2C_SDA_IN 0x00000010 + +/* initialization states */ +#define INIT2 0x20 +/* #define INIT3 0x4 */ + +/* delays */ +#define CYCLE_DELAY 10 +#define TIMEOUT (HZ / 2) + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_savage4_init(void); +static int __init savage4_cleanup(void); +static int savage4_setup(void); +static void config_s4(struct pci_dev *dev); +static void savage4_inc(struct i2c_adapter *adapter); +static void savage4_dec(struct i2c_adapter *adapter); + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + + +static int __initdata savage4_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 sav GPIO registers don't have individual masks for each bit + so we always have to read before writing. */ + +static void bit_savi2c_setscl(void *data, int val) +{ + unsigned int r; + r = readlong(); + if(val) + r |= I2C_SCL_OUT; + else + r &= ~I2C_SCL_OUT; + outlong(r); + readlong(); /* flush posted write */ +} + +static void bit_savi2c_setsda(void *data, int val) +{ + unsigned int r; + r = readlong(); + if(val) + r |= I2C_SDA_OUT; + else + r &= ~I2C_SDA_OUT; + outlong(r); + readlong(); /* flush posted write */ +} + +/* 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_savi2c_getscl(void *data) +{ + return (0 != (readlong() & I2C_SCL_IN)); +} + +static int bit_savi2c_getsda(void *data) +{ + return (0 != (readlong() & I2C_SDA_IN)); +} + +/*static void bit_savddc_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_savddc_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_savddc_getscl(void *data) +{ + return (0 != (readlong() & DDC_SCL_IN)); +} + +static int bit_savddc_getsda(void *data) +{ + return (0 != (readlong() & DDC_SDA_IN)); +} +*/ +static struct i2c_algo_bit_data sav_i2c_bit_data = { + NULL, + bit_savi2c_setsda, + bit_savi2c_setscl, + bit_savi2c_getsda, + bit_savi2c_getscl, + CYCLE_DELAY, CYCLE_DELAY, TIMEOUT +}; + +static struct i2c_adapter savage4_i2c_adapter = { + "I2C Savage4 adapter", + I2C_HW_B_SAVG, + NULL, + &sav_i2c_bit_data, + savage4_inc, + savage4_dec, + NULL, + NULL, +}; +/* +static struct i2c_algo_bit_data sav_ddc_bit_data = { + NULL, + bit_savddc_setsda, + bit_savddc_setscl, + bit_savddc_getsda, + bit_savddc_getscl, + CYCLE_DELAY, CYCLE_DELAY, TIMEOUT +}; + +static struct i2c_adapter savage4_ddc_adapter = { + "DDC Voodoo3/Banshee adapter", + I2C_HW_B_VOO, + NULL, + &sav_ddc_bit_data, + savage4_inc, + savage4_dec, + NULL, + NULL, +}; +*/ +/* Configures the chip */ + +void config_s4(struct pci_dev *dev) +{ + unsigned int cadr; + + /* map 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, 0x0080000); + if(mem) { +// *((unsigned int *) (mem + REG2)) = 0x8160; + *((unsigned int *) (mem + REG)) = 0x00000020; + printk("i2c-savage4: Using Savage4 at 0x%p\n", mem); + } +} + +/* Detect chip and initialize it. */ +static int savage4_setup(void) +{ + struct pci_dev *dev; + int s4_num; + + s4_num = 0; + + dev = NULL; + do { + if ((dev = pci_find_device(PCI_VENDOR_ID_S3, + PCI_CHIP_SAVAGE4, + dev))) { + if (!s4_num) + config_s4(dev); + s4_num++; + } + } while (dev); + + dev = NULL; + do { + if ((dev = pci_find_device(PCI_VENDOR_ID_S3, + PCI_CHIP_SAVAGE2000, + dev))) { + if (!s4_num) + config_s4(dev); + s4_num++; + } + } while (dev); + + if (s4_num > 0) { + if(!mem) + return -ENOMEM; + printk("i2c-savage4: %d Savage4 found.\n", s4_num); + if (s4_num > 1) + printk("i2c-savage4: warning: only 1 supported.\n"); + return 0; + } else { + printk("i2c-savage4: No Savage4 found.\n"); + return -ENODEV; + } +} + +void savage4_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +void savage4_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +int __init i2c_savage4_init(void) +{ + int res; + printk("i2c-savage4.o version %s (%s)\n", LM_VERSION, LM_DATE); + savage4_initialized = 0; + if ((res = savage4_setup())) { + printk + ("i2c-savage4.o: Savage4 not detected, module not inserted.\n"); + savage4_cleanup(); + return res; + } + if ((res = i2c_bit_add_bus(&savage4_i2c_adapter))) { + printk("i2c-savage4.o: I2C adapter registration failed\n"); + } else { + printk("i2c-savage4.o: I2C bus initialized\n"); + savage4_initialized |= INIT2; + } +/* + if ((res = i2c_bit_add_bus(&savage4_ddc_adapter))) { + printk("i2c-savage4.o: DDC adapter registration failed\n"); + } else { + printk("i2c-savage4.o: DDC bus initialized\n"); + savage4_initialized |= INIT3; + } +*/ + if(!(savage4_initialized & (INIT2 /* | INIT3 */ ))) { + printk("i2c-savage4.o: Both registrations failed, module not inserted\n"); + savage4_cleanup(); + return res; + } + return 0; +} + +int __init savage4_cleanup(void) +{ + int res; + + iounmap(mem); +/* + if (savage4_initialized & INIT3) { + if ((res = i2c_bit_del_bus(&savage4_ddc_adapter))) { + printk + ("i2c-savage4.o: i2c_bit_del_bus failed, module not removed\n"); + return res; + } + } +*/ + if (savage4_initialized & INIT2) { + if ((res = i2c_bit_del_bus(&savage4_i2c_adapter))) { + printk + ("i2c-savage4.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("Savage4 I2C/SMBus driver"); + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +int init_module(void) +{ + return i2c_savage4_init(); +} + +int cleanup_module(void) +{ + return savage4_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/i2c/i2c-sis5595.c Tue Jun 17 15:50:27 CEST 2003 +++ linux/drivers/i2c/i2c-sis5595.c Tue Jun 17 15:50:27 CEST 2003 @@ -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 "20021208" +#define LM_VERSION "2.7.0" +#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. */ +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-sis630.c Tue Jun 17 15:50:28 CEST 2003 +++ linux/drivers/i2c/i2c-sis630.c Tue Jun 17 15:50:28 CEST 2003 @@ -0,0 +1,540 @@ +/* + sis630.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + + Copyright (c) 2002 Alexander Malysh + + 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. +*/ + +/* + Changes: + 19.11.2002 + Fixed logic by restoring of Host Master Clock + 24.09.2002 + Fixed typo in sis630_access + Fixed logical error by restoring of Host Master Clock + 21.09.2002 + Added high_clock module option.If this option is set + used Host Master Clock 56KHz (default 14KHz).For now we are save old Host + Master Clock and after transaction completed restore (otherwise + it's confuse BIOS and hung Machine). + 18.09.2002 + Added SIS730 as supported + 24.08.2002 + Fixed the typo in sis630_access (Thanks to Mark M. Hoffman) + Changed sis630_transaction. Now it's 2x faster (Thanks to Mark M. Hoffman) +*/ + +/* + Status: beta + + Supports: + SIS 630 + SIS 730 + + Note: we assume there can only be one device, with one SMBus interface. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "/usr/local/include/linux/i2c.h" +#define LM_DATE "20021208" +#define LM_VERSION "2.7.0" +#include + +#ifndef PCI_VENDOR_ID_SI +#define PCI_VENDOR_ID_SI 0x1039 +#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_503 +#define PCI_DEVICE_ID_SI_503 0x0008 +#endif + +struct sd { + const unsigned short mfr; + const unsigned short dev; + const unsigned char fn; + const char *name; +}; + +/* supported chips */ +static struct sd supported[] = { + {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_630, 0, "SIS630"}, + {PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_730, 0, "SIS730"}, + {0, 0, 0, NULL} +}; + + +/* SIS630 SMBus registers */ +#define SMB_STS 0x80 /* status */ +#define SMB_EN 0x81 /* status enable */ +#define SMB_CNT 0x82 +#define SMBHOST_CNT 0x83 +#define SMB_ADDR 0x84 +#define SMB_CMD 0x85 +#define SMB_PCOUNT 0x86 /* processed count */ +#define SMB_COUNT 0x87 +#define SMB_BYTE 0x88 /* ~0x8F data byte field */ +#define SMBDEV_ADDR 0x90 +#define SMB_DB0 0x91 +#define SMB_DB1 0x92 +#define SMB_SAA 0x93 + +/* register count for request_region */ +#define SIS630_SMB_IOREGION 20 + +/* PCI address constants */ +/* acpi base address register */ +#define SIS630_ACPI_BASE_REG 0x74 +/* bios control register */ +#define SIS630_BIOS_CTL_REG 0x40 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* SIS630 constants */ +#define SIS630_QUICK 0x00 +#define SIS630_BYTE 0x01 +#define SIS630_BYTE_DATA 0x02 +#define SIS630_WORD_DATA 0x03 +#define SIS630_PCALL 0x04 +#define SIS630_BLOCK_DATA 0x05 + +/* insmod parameters */ + +/* If force is set to anything different from 0, we forcibly enable the + SIS630. DANGEROUS! */ +static int force = 0; +static int high_clock = 0; +MODULE_PARM(force, "i"); +MODULE_PARM_DESC(force, "Forcibly enable the SIS630. DANGEROUS!"); +MODULE_PARM(high_clock, "i"); +MODULE_PARM_DESC(high_clock, "Set Host Master Clock to 56KHz (default 14KHz)."); + + +#ifdef MODULE +static +#else +extern +#endif +int __init i2c_sis630_init(void); +static int __init i2c_sis630_cleanup(void); +static int sis630_setup(void); +static s32 sis630_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data); +static void sis630_do_pause(unsigned int amount); +static int sis630_transaction(int size); +static void sis630_inc(struct i2c_adapter *adapter); +static void sis630_dec(struct i2c_adapter *adapter); +static u32 sis630_func(struct i2c_adapter *adapter); +static u8 sis630_read(u8 reg); +static void sis630_write(u8 reg, u8 data); + + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ sis630_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ sis630_func, +}; + +static struct i2c_adapter sis630_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS630, + &smbus_algorithm, + NULL, + sis630_inc, + sis630_dec, + NULL, + NULL, +}; + +static unsigned short acpi_base = 0; +static int __initdata sis630_initialized; + +u8 sis630_read(u8 reg) { + return inb(acpi_base + reg); +} + +void sis630_write(u8 reg, u8 data) { + outb(data, acpi_base + reg); +} + +/* Internally used pause function */ +void sis630_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +int sis630_transaction(int size) { + int temp; + int result = 0; + int timeout = 0; + u8 oldclock; + + /* + Make sure the SMBus host is ready to start transmitting. + */ + if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-sis630.o: SMBus busy (%02x). " + "Resetting...\n",temp); +#endif + /* kill smbus transaction */ + sis630_write(SMBHOST_CNT, 0x20); + + if ((temp = sis630_read(SMB_CNT) & 0x03) != 0x00) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-sis630.o: Failed! (%02x)\n", + temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-sis630.o: Successfull!\n"); +#endif + } + } + + /* save old clock, so we can prevent machine to hung */ + oldclock = sis630_read(SMB_CNT); + +#ifdef DEBUG + printk(KERN_DEBUG "i2c-sis630.o: saved clock 0x%02x\n", oldclock); +#endif + + /* disable timeout interrupt , set Host Master Clock to 56KHz if requested */ + if (high_clock > 0) + sis630_write(SMB_CNT, 0x20); + else + sis630_write(SMB_CNT, (oldclock & ~0x40)); + + /* clear all sticky bits */ + temp = sis630_read(SMB_STS); + sis630_write(SMB_STS, temp & 0x1e); + + /* start the transaction by setting bit 4 and size */ + sis630_write(SMBHOST_CNT,0x10 | (size & 0x07)); + + /* We will always wait for a fraction of a second! */ + do { + sis630_do_pause(1); + temp = sis630_read(SMB_STS); + } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { +#ifdef DEBUG + printk(KERN_DEBUG "i2c-sis630.o: SMBus Timeout!\n"); +#endif + result = -1; + } + + if (temp & 0x02) { + result = -1; +#ifdef DEBUG + printk(KERN_DEBUG "i2c-sis630.o: Error: Failed bus " + "transaction\n"); +#endif + } + + if (temp & 0x04) { + result = -1; + printk(KERN_ERR "i2c-sis630.o: Bus collision! " + "SMBus may be locked until next hard reset (or not...)\n"); + /* + TBD: Datasheet say: + the software should clear this bit and restart SMBUS operation + */ + } + + /* clear all status "sticky" bits */ + sis630_write(SMB_STS, temp); + +#ifdef DEBUG + printk(KERN_DEBUG "i2c-sis630.o: SMB_CNT before clock restore 0x%02x\n", sis630_read(SMB_CNT)); +#endif + + /* + * restore old Host Master Clock if high_clock is set + * and oldclock was not 56KHz + */ + if (high_clock > 0 && !(oldclock & 0x20)) + sis630_write(SMB_CNT,(sis630_read(SMB_CNT) & ~0x20)); + +#ifdef DEBUG + printk(KERN_DEBUG "i2c-sis630.o: SMB_CNT after clock restore 0x%02x\n", sis630_read(SMB_CNT)); +#endif + + return result; +} + +/* Return -1 on error. */ +s32 sis630_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: + sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS630_QUICK; + break; + case I2C_SMBUS_BYTE: + sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) + sis630_write(SMB_CMD, command); + size = SIS630_BYTE; + break; + case I2C_SMBUS_BYTE_DATA: + sis630_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis630_write(SMB_BYTE, data->byte); + size = SIS630_BYTE_DATA; + break; + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis630_write(SMB_BYTE, data->word & 0xff); + sis630_write(SMB_BYTE + 1,(data->word & 0xff00) >> 8); + } + size = (size == I2C_SMBUS_PROC_CALL ? SIS630_PCALL : SIS630_WORD_DATA); + break; + case I2C_SMBUS_BLOCK_DATA: +/* it's not implemented yet, but comming soon */ +#if 0 + sis630_write(SMB_ADDR,((addr & 0x7f) << 1) | (read_write & 0x01)); + sis630_write(SMB_CMD, command); + size = SIS630_BLOCK_DATA; +#endif + default: + printk("Unsupported I2C size\n"); + return -1; + break; + } + + + if (sis630_transaction(size)) + return -1; + + if ((size != SIS630_PCALL) && + ((read_write == I2C_SMBUS_WRITE) || (size == SIS630_QUICK))) { + return 0; + } + + switch(size) { + case SIS630_BYTE: + case SIS630_BYTE_DATA: + data->byte = sis630_read(SMB_BYTE); + break; + case SIS630_PCALL: + case SIS630_WORD_DATA: + data->word = sis630_read(SMB_BYTE) + (sis630_read(SMB_BYTE + 1) << 8); + break; + default: + return -1; + break; + } + + return 0; +} + +void sis630_inc(struct i2c_adapter *adapter) { + MOD_INC_USE_COUNT; +} + +void sis630_dec(struct i2c_adapter *adapter) { + MOD_DEC_USE_COUNT; +} + +u32 sis630_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 sis630_setup(void) { + unsigned char b; + struct pci_dev *sis630_dev = NULL,*tmp = NULL; + struct sd *en = supported; + + /* First check whether we can access PCI at all */ + if (pci_present() == 0) { + printk("i2c-sis630.o: Error: No PCI-bus found!\n"); + return -ENODEV; + } + + /* Look for the SIS630 and compatible */ + if (!(sis630_dev = pci_find_device(PCI_VENDOR_ID_SI, + PCI_DEVICE_ID_SI_503, + sis630_dev))) { + printk(KERN_ERR "i2c-sis630.o: Error: Can't detect 85C503/5513 ISA bridge!\n"); + return -ENODEV; + } + do { + if ((tmp = pci_find_device(en->mfr,en->dev,NULL))) { + if (PCI_FUNC(tmp->devfn) != en->fn) + continue; + + break; + } + en++; + } + while(en->mfr); + if (tmp == NULL && force == 0 ) { + printk(KERN_ERR "i2c-sis630.o: Error: Can't detect SIS630 compatible device!\n"); + return -ENODEV; + } + else if (tmp == NULL && force > 0) { + printk(KERN_NOTICE "i2c-sis630.o: WARNING: Can't detect SIS630 compatible device, but " + "loading because of force option enabled\n"); + } + + /* + Enable ACPI first , so we can accsess reg 74-75 + in acpi io space and read acpi base addr + */ + if (PCIBIOS_SUCCESSFUL != + pci_read_config_byte(sis630_dev, SIS630_BIOS_CTL_REG,&b)) { + printk(KERN_ERR "i2c-sis630.o: Error: Can't read bios ctl reg\n"); + return -ENODEV; + } + /* if ACPI already anbled , do nothing */ + if (!(b & 0x80) && + PCIBIOS_SUCCESSFUL != + pci_write_config_byte(sis630_dev,SIS630_BIOS_CTL_REG,b|0x80)) { + printk(KERN_ERR "i2c-sis630.o: Error: Can't enable ACPI\n"); + return -ENODEV; + } + /* Determine the ACPI base address */ + if (PCIBIOS_SUCCESSFUL != + pci_read_config_word(sis630_dev,SIS630_ACPI_BASE_REG,&acpi_base)) { + printk(KERN_ERR "i2c-sis630.o: Error: Can't determine ACPI base address\n"); + return -ENODEV; + } + +#ifdef DEBUG + printk(KERN_DEBUG "i2c-sis630.o: ACPI base at 0x%04x\n", acpi_base); +#endif + + /* Everything is happy, let's grab the memory and set things up. */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,14) + if (!request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, "sis630-smbus")){ + printk(KERN_ERR "i2c-sis630.o: SMBus registers 0x%04x-0x%04x " + "already in use!\n",acpi_base + SMB_STS, acpi_base + SMB_SAA); + return -ENODEV; + } +#else + if (check_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION) < 0){ + printk(KERN_ERR "i2c-sis630.o: SMBus registers 0x%04x-0x%04x " + "already in use!\n",acpi_base + SMB_STS, acpi_base + SMB_SAA); + return -ENODEV; + } + request_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION, "sis630-smbus"); +#endif + + return 0; +} + +int __init i2c_sis630_init(void) { + int res; + printk("i2c-sis630.o version %s (%s)\n", LM_VERSION, LM_DATE); + + if (sis630_initialized) { + printk(KERN_ERR "i2c-sis630.o: Oops, sis630_init called a second time!\n"); + return -EBUSY; + } + + sis630_initialized = 0; + if ((res = sis630_setup())) { + printk(KERN_ERR "i2c-sis630.o: SIS630 comp. bus not detected, module not inserted.\n"); + i2c_sis630_cleanup(); + return res; + } + sis630_initialized++; + sprintf(sis630_adapter.name, "SMBus SIS630 adapter at %04x", + acpi_base + SMB_STS); + if ((res = i2c_add_adapter(&sis630_adapter))) { + printk(KERN_ERR "i2c-sis630.o: Adapter registration failed, " + "module not inserted.\n"); + i2c_sis630_cleanup(); + return res; + } + sis630_initialized++; + printk(KERN_INFO "i2c-sis630.o: SIS630 comp. bus detected and initialized\n"); + + return 0; +} + +int __init i2c_sis630_cleanup(void) { + int res; + if (sis630_initialized >= 2) { + if ((res = i2c_del_adapter(&sis630_adapter))) { + printk(KERN_ERR "i2c-sis630.o: i2c_del_adapter failed, module not" + "removed\n"); + return res; + } else + sis630_initialized--; + } + if (sis630_initialized >= 1) { + release_region(acpi_base + SMB_STS, SIS630_SMB_IOREGION); + sis630_initialized--; + } + return 0; +} + + +EXPORT_NO_SYMBOLS; + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +#ifdef MODULE +MODULE_AUTHOR("Alexander Malysh "); +MODULE_DESCRIPTION("SIS630 SMBus driver"); + +int init_module(void) { + return i2c_sis630_init(); +} + +int cleanup_module(void) { + return i2c_sis630_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/i2c/i2c-sis645.c Tue Jun 17 15:50:28 CEST 2003 +++ linux/drivers/i2c/i2c-sis645.c Tue Jun 17 15:50:28 CEST 2003 @@ -0,0 +1,597 @@ +/* + sis645.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + + Copyright (c) 2002 Mark M. Hoffman + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +/* + This module must be considered BETA unless and until + the chipset manufacturer releases a datasheet. + + The register definitions are based on the SiS630. + The method for *finding* the registers is based on trial and error. + + A history of changes to this file is available by anonymous CVS: + http://www2.lm-sensors.nu/~lm78/download.html +*/ + +/* + Note: we assume there can only be one SiS645 with one SMBus interface +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#define LM_DATE "20021208" +#define LM_VERSION "2.7.0" +#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + +/* PCI identifiers */ + +/* SiS645 north bridge */ +#ifndef PCI_DEVICE_ID_SI_645 +#define PCI_DEVICE_ID_SI_645 0x0645 +#endif + +/* SiS645DX north bridge */ +#ifndef PCI_DEVICE_ID_SI_646 +#define PCI_DEVICE_ID_SI_646 0x0646 +#endif + +/* SiS650 north bridge */ +#ifndef PCI_DEVICE_ID_SI_650 +#define PCI_DEVICE_ID_SI_650 0x0650 +#endif + +/* SiS735 combo chipset */ +#ifndef PCI_DEVICE_ID_SI_735 +#define PCI_DEVICE_ID_SI_735 0x0735 +#endif + +/* SiS961 south bridge */ +#ifndef PCI_DEVICE_ID_SI_961 +#define PCI_DEVICE_ID_SI_961 0x0961 +#endif + +#define PCI_DEVICE_ID_SI_SMBUS 0x16 + +/* base address register in PCI config space */ +#define BASE_IO_REG 0x04 + +/* SiS645 SMBus registers */ +#define SMB_STS 0x00 +#define SMB_EN 0x01 +#define SMB_CNT 0x02 +#define SMB_HOST_CNT 0x03 +#define SMB_ADDR 0x04 +#define SMB_CMD 0x05 +#define SMB_PCOUNT 0x06 +#define SMB_COUNT 0x07 +#define SMB_BYTE 0x08 +#define SMB_DEV_ADDR 0x10 +#define SMB_DB0 0x11 +#define SMB_DB1 0x12 +#define SMB_SAA 0x13 + +/* register count for request_region */ +#define SIS645_SMB_IOREGION 0x20 + +/* Other settings */ +#define MAX_TIMEOUT 500 + +/* SiS645 SMBus constants */ +#define SIS645_QUICK 0x00 +#define SIS645_BYTE 0x01 +#define SIS645_BYTE_DATA 0x02 +#define SIS645_WORD_DATA 0x03 +#define SIS645_PROC_CALL 0x04 +#define SIS645_BLOCK_DATA 0x05 + + +static int __init sis645_init(void); +static void sis645_cleanup(void); + +static int sis645_enable_smbus(struct pci_dev *dev); +static int sis645_build_dev(struct pci_dev **smbus_dev, + struct pci_dev *bridge_dev); +static int sis645_setup(void); +static s32 sis645_access(struct i2c_adapter *adap, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, + union i2c_smbus_data *data); +static void sis645_do_pause(unsigned int amount); +static int sis645_transaction(int size); +static void sis645_inc(struct i2c_adapter *adapter); +static void sis645_dec(struct i2c_adapter *adapter); +static u32 sis645_func(struct i2c_adapter *adapter); + +static struct i2c_algorithm smbus_algorithm = { + /* name */ "Non-I2C SMBus adapter", + /* id */ I2C_ALGO_SMBUS, + /* master_xfer */ NULL, + /* smbus_access */ sis645_access, + /* slave_send */ NULL, + /* slave_rcv */ NULL, + /* algo_control */ NULL, + /* functionality */ sis645_func, +}; + +static struct i2c_adapter sis645_adapter = { + "unset", + I2C_ALGO_SMBUS | I2C_HW_SMBUS_SIS645, + &smbus_algorithm, + NULL, + sis645_inc, + sis645_dec, + NULL, + NULL, +}; + +static int sis645_initialized; +static unsigned short sis645_smbus_base = 0; + +static u8 sis645_read(u8 reg) +{ + return inb(sis645_smbus_base + reg) ; +} + +static void sis645_write(u8 reg, u8 data) +{ + outb(data, sis645_smbus_base + reg) ; +} + +#ifdef CONFIG_HOTPLUG + +/* Turns on SMBus device if it is not; return 0 iff successful + */ +static int sis645_enable_smbus(struct pci_dev *dev) +{ + u8 val = 0; + + pci_read_config_byte(dev, 0x77, &val); + +#ifdef DEBUG + printk("i2c-sis645.o: Config byte was 0x%02x.\n", val); +#endif + + pci_write_config_byte(dev, 0x77, val & ~0x10); + + pci_read_config_byte(dev, 0x77, &val); + + if (val & 0x10) { +#ifdef DEBUG + printk("i2c-sis645.o: Error: Config byte stuck!\n"); +#endif + return -1; + } + + return 0; +} + +/* Builds the basic pci_dev for SiS645 SMBus + */ +static int sis645_build_dev(struct pci_dev **smbus_dev, + struct pci_dev *bridge_dev) +{ + struct pci_dev temp_dev; + u16 vid = 0, did = 0; + int ret; + + /* fill in the device structure for search */ + memset(&temp_dev, 0, sizeof(temp_dev)); + temp_dev.bus = bridge_dev->bus; + temp_dev.sysdata = bridge_dev->bus->sysdata; + temp_dev.hdr_type = PCI_HEADER_TYPE_NORMAL; + + /* the SMBus device is function 1 on the same unit as the ISA bridge */ + temp_dev.devfn = bridge_dev->devfn + 1; + + /* query to make sure */ + ret = pci_read_config_word(&temp_dev, PCI_VENDOR_ID, &vid); + if (ret || PCI_VENDOR_ID_SI != vid) { + printk("i2c-sis645.o: Couldn't find SMBus device!\n"); + return ret; + } + temp_dev.vendor = vid; + + ret = pci_read_config_word(&temp_dev, PCI_DEVICE_ID, &did); + if (ret || PCI_DEVICE_ID_SI_SMBUS != did) { + printk("i2c-sis645.o: Couldn't find SMBus device!\n"); + return ret; + } + temp_dev.device = did; + + /* ok, we've got it... request some memory and finish it off */ + *smbus_dev = kmalloc(sizeof(**smbus_dev), GFP_ATOMIC); + if (NULL == *smbus_dev) { + printk("i2c-sis645.o: Error: Out of memory!\n"); + return -ENOMEM; + } + + **smbus_dev = temp_dev; + + ret = pci_setup_device(*smbus_dev); + if (ret) { + printk("i2c-sis645.o: pci_setup_device failed (0x%08x)\n",ret); + } + return ret; +} + +#endif /* CONFIG_HOTPLUG */ + +/* Detect whether a SiS645 can be found, and initialize it, where necessary. + */ +static int sis645_setup(void) +{ + struct pci_dev *SIS645_ISA_dev; + struct pci_dev *SIS645_SMBUS_dev; + int ret; + u16 ww = 0; + + if (pci_present() == 0) { + printk("i2c-sis645.o: Error: No PCI-bus found!\n"); + return -ENODEV; + } + + if (SIS645_ISA_dev = pci_find_device(PCI_VENDOR_ID_SI, + PCI_DEVICE_ID_SI_961, NULL)) { + printk("i2c-sis645.o: Found SiS961 south bridge.\n"); + } + + else if (SIS645_ISA_dev = pci_find_device(PCI_VENDOR_ID_SI, + PCI_DEVICE_ID_SI_503, NULL)) { + printk("i2c-sis645.o: Found SiS south bridge in compatability mode(?)\n"); + + /* look for known compatible north bridges */ + if ((NULL == pci_find_device(PCI_VENDOR_ID_SI, + PCI_DEVICE_ID_SI_645, NULL)) + && (NULL == pci_find_device(PCI_VENDOR_ID_SI, + PCI_DEVICE_ID_SI_646, NULL)) + && (NULL == pci_find_device(PCI_VENDOR_ID_SI, + PCI_DEVICE_ID_SI_650, NULL)) + && (NULL == pci_find_device(PCI_VENDOR_ID_SI, + PCI_DEVICE_ID_SI_735, NULL))) { + printk("i2c-sis645.o: Error: Can't find suitable host bridge!\n"); + return -ENODEV; + } + } + + else { + printk("i2c-sis645.o: Error: Can't find suitable south bridge!\n"); + return -ENODEV; + } + + if (!(SIS645_SMBUS_dev = pci_find_device(PCI_VENDOR_ID_SI, + PCI_DEVICE_ID_SI_SMBUS, NULL))) { + + printk("i2c-sis645.o: " + "Attempting to enable SiS645 SMBus device\n"); + +#ifndef CONFIG_HOTPLUG + printk("i2c-sis645.o: " + "Requires kernel >= 2.4 with CONFIG_HOTPLUG, sorry!\n"); + return -ENODEV; + +#else /* CONFIG_HOTPLUG */ + if (ret = sis645_enable_smbus(SIS645_ISA_dev)) { + return ret; + } + + if (ret = sis645_build_dev(&SIS645_SMBUS_dev, SIS645_ISA_dev)) { + return ret; + } + + if (ret = pci_enable_device(SIS645_SMBUS_dev)) { + printk("i2c-sis645.o: Can't pci_enable SMBus device!" + " (0x%08x)\n", ret); + return ret; + } + + pci_insert_device(SIS645_SMBUS_dev, SIS645_SMBUS_dev->bus); + +#endif /* CONFIG_HOTPLUG */ + + } + + pci_read_config_word(SIS645_SMBUS_dev, PCI_CLASS_DEVICE, &ww); + if (PCI_CLASS_SERIAL_SMBUS != ww) { + printk("i2c-sis645.o: Error: Unsupported device class 0x%04x!\n", ww); + return -ENODEV; + } + + /* get the IO base address */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,13) + sis645_smbus_base = SIS645_SMBUS_dev->resource[BASE_IO_REG].start; +#else + sis645_smbus_base = SIS645_SMBUS_dev->base_address[BASE_IO_REG]; +#endif + if (!sis645_smbus_base) { + printk("i2c-sis645.o: SiS645 SMBus base address not initialized!\n"); + return -EINVAL; + } + printk("i2c-sis645.o: SiS645 SMBus base address: 0x%04x\n", sis645_smbus_base); + + /* Everything is happy, let's grab the memory and set things up. */ +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,3,14) + if (!request_region(sis645_smbus_base, SIS645_SMB_IOREGION, "sis645-smbus")) { + printk + ("i2c-sis645.o: SMBus registers 0x%04x-0x%04x already in use!\n", + sis645_smbus_base, sis645_smbus_base + SIS645_SMB_IOREGION - 1); + return -EINVAL; + } +#else + if (check_region(sis645_smbus_base, SIS645_SMB_IOREGION) < 0) { + printk + ("i2c-sis645.o: SMBus registers 0x%04x-0x%04x already in use!\n", + sis645_smbus_base, sis645_smbus_base + SIS645_SMB_IOREGION - 1); + return -EINVAL; + } + request_region(sis645_smbus_base, SIS645_SMB_IOREGION, "sis645-smbus"); +#endif + + return(0); +} + + +/* Internally used pause function */ +static void sis645_do_pause(unsigned int amount) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(amount); +} + +/* Execute a SMBus transaction. + int size is from SIS645_QUICK to SIS645_BLOCK_DATA + */ +static int sis645_transaction(int size) +{ + int temp; + int result = 0; + int timeout = 0; + + /* Make sure the SMBus host is ready to start transmitting */ + if (((temp = sis645_read(SMB_CNT)) & 0x03) != 0x00) { +#ifdef DEBUG + printk("i2c-sis645.o: SMBus busy (0x%02x). Resetting...\n", + temp); +#endif + + /* kill the transaction */ + sis645_write(SMB_HOST_CNT, 0x20); + + /* check it again */ + if (((temp = sis645_read(SMB_CNT)) & 0x03) != 0x00) { +#ifdef DEBUG + printk("i2c-sis645.o: Failed! (0x%02x)\n", temp); +#endif + return -1; + } else { +#ifdef DEBUG + printk("i2c-sis645.o: Successful!\n"); +#endif + } + } + + /* Turn off timeout interrupts, set fast host clock */ + sis645_write(SMB_CNT, 0x20); + + /* clear all (sticky) status flags */ + temp = sis645_read(SMB_STS); + sis645_write(SMB_STS, temp & 0x1e); + + /* start the transaction by setting bit 4 and size bits */ + sis645_write(SMB_HOST_CNT, 0x10 | (size & 0x07)); + + /* We will always wait for a fraction of a second! */ + do { + sis645_do_pause(1); + temp = sis645_read(SMB_STS); + } while (!(temp & 0x0e) && (timeout++ < MAX_TIMEOUT)); + + /* If the SMBus is still busy, we give up */ + if (timeout >= MAX_TIMEOUT) { + printk("i2c-sis645.o: SMBus Timeout! (0x%02x)\n",temp); + result = -1; + } + + /* device error - probably missing ACK */ + if (temp & 0x02) { +#ifdef DEBUG + printk("i2c-sis645.o: Error: Failed bus transaction!\n"); +#endif + result = -1; + } + + /* bus collision */ + if (temp & 0x04) { +#ifdef DEBUG + printk("i2c-sis645.o: Error: Bus collision!\n"); +#endif + result = -1; + } + + /* Finish up by resetting the bus */ + sis645_write(SMB_STS, temp); + if (temp = sis645_read(SMB_STS)) { +#ifdef DEBUG + printk("i2c-sis645.o: Failed reset at end of transaction!" + " (0x%02x)\n", temp); +#endif + } + + return result; +} + +/* Return -1 on error. */ +s32 sis645_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: + sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + size = SIS645_QUICK; + break; + + case I2C_SMBUS_BYTE: + sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + if (read_write == I2C_SMBUS_WRITE) + sis645_write(SMB_CMD, command); + size = SIS645_BYTE; + break; + + case I2C_SMBUS_BYTE_DATA: + sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis645_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) + sis645_write(SMB_BYTE, data->byte); + size = SIS645_BYTE_DATA; + break; + + case I2C_SMBUS_PROC_CALL: + case I2C_SMBUS_WORD_DATA: + sis645_write(SMB_ADDR, ((addr & 0x7f) << 1) | (read_write & 0x01)); + sis645_write(SMB_CMD, command); + if (read_write == I2C_SMBUS_WRITE) { + sis645_write(SMB_BYTE, data->word & 0xff); + sis645_write(SMB_BYTE + 1, (data->word & 0xff00) >> 8); + } + size = (size == I2C_SMBUS_PROC_CALL ? SIS645_PROC_CALL : SIS645_WORD_DATA); + break; + + case I2C_SMBUS_BLOCK_DATA: + /* TO DO: */ + printk("sis645.o: SMBus block not implemented!\n"); + return -1; + break; + + default: + printk("sis645.o: unsupported I2C size\n"); + return -1; + break; + } + + if (sis645_transaction(size)) + return -1; + + if ((size != SIS645_PROC_CALL) && + ((read_write == I2C_SMBUS_WRITE) || (size == SIS645_QUICK))) + return 0; + + switch (size) { + case SIS645_BYTE: + case SIS645_BYTE_DATA: + data->byte = sis645_read(SMB_BYTE); + break; + + case SIS645_WORD_DATA: + case SIS645_PROC_CALL: + data->word = sis645_read(SMB_BYTE) + + (sis645_read(SMB_BYTE + 1) << 8); + break; + } + return 0; +} + +static void sis645_inc(struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +static void sis645_dec(struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +static u32 sis645_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; +} + +static int __init sis645_init(void) +{ + int res; + printk("i2c-sis645.o: version %s (%s)\n", LM_VERSION, LM_DATE); + + if (sis645_initialized) { + printk("i2c-sis645.o: Oops, sis645_init called a second time!\n"); + return -EBUSY; + } + + sis645_initialized = 0; + if ((res = sis645_setup())) { + printk ("i2c-sis645.o: SiS645 not detected, module not inserted.\n"); + sis645_cleanup(); + return res; + } + + sis645_initialized++; + sprintf(sis645_adapter.name, "SMBus SiS645 adapter at 0x%04x", sis645_smbus_base); + if ((res = i2c_add_adapter(&sis645_adapter))) { + printk ("i2c-sis645.o: Adapter registration failed, module not inserted.\n"); + sis645_cleanup(); + return res; + } + + sis645_initialized++; + printk("i2c-sis645.o: SiS645 bus detected and initialized.\n"); + return 0; +} + +static void sis645_cleanup(void) +{ + int res; + if (sis645_initialized >= 2) { + if ((res = i2c_del_adapter(&sis645_adapter))) { + printk("i2c-sis645.o: i2c_del_adapter failed, module not removed\n"); + return; + } else + sis645_initialized--; + } + if (sis645_initialized >= 1) { + release_region(sis645_smbus_base, SIS645_SMB_IOREGION); + sis645_initialized--; + } + return; +} + +EXPORT_NO_SYMBOLS; + +/* Register initialization functions using helper macros */ +module_init(sis645_init); +module_exit(sis645_cleanup); + +#ifdef MODULE + +MODULE_AUTHOR("Mark M. Hoffman "); +MODULE_DESCRIPTION("SiS645 SMBus driver"); + +#endif --- linux-old/drivers/i2c/i2c-tsunami.c Tue Jun 17 15:50:28 CEST 2003 +++ linux/drivers/i2c/i2c-tsunami.c Tue Jun 17 15:50:28 CEST 2003 @@ -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 "20021208" +#define LM_VERSION "2.7.0" +#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, HZ/2 /* 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 Tue Jun 17 15:50:28 CEST 2003 +++ linux/drivers/i2c/i2c-via.c Tue Jun 17 15:50:28 CEST 2003 @@ -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 "20021208" +#define LM_VERSION "2.7.0" +#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, HZ, /*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 Tue Jun 17 15:50:28 CEST 2003 +++ linux/drivers/i2c/i2c-viapro.c Tue Jun 17 15:50:28 CEST 2003 @@ -0,0 +1,576 @@ +/* + i2c-viapro.c - Part of lm_sensors, Linux kernel modules for hardware + monitoring + Copyright (c) 1998 - 2002 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 + 8231 + 8233 + 8233A (0x3147 and 0x3177) + 8235 + 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 "20021208" +#define LM_VERSION "2.7.0" +#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"}, + {0x3147, SMBBA3, 0xD2, "VT8233A"}, + {0x3177, SMBBA3, 0xD2, "VT8233A/8235"}, + {0x8235, SMBBA1, 0xD2, "VT8231"}, + {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. */ +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 Tue Jun 17 15:50:28 CEST 2003 +++ linux/drivers/i2c/i2c-voodoo3.c Tue Jun 17 15:50:28 CEST 2003 @@ -0,0 +1,369 @@ +/* + 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 "20021208" +#define LM_VERSION "2.7.0" +#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 (HZ / 2) + +#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); + readlong(); /* flush posted write */ +} + +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); + readlong(); /* flush posted write */ +} + +/* 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); + readlong(); /* flush posted write */ +} + +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); + readlong(); /* flush posted write */ +} + +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); + if(mem) { + *((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) { + if(!mem) + return -ENOMEM; + 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 Tue Jun 17 15:50:28 CEST 2003 +++ linux/drivers/sensors/adm1021.c Tue Jun 17 15:50:28 CEST 2003 @@ -0,0 +1,669 @@ +/* + 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 "20021208" +#define LM_VERSION "2.7.0" +#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_8(adm1021, adm1023, max1617, max1617a, thmc10, lm84, gl523sm, mc1066); + +/* adm1021 constants specified below */ + +/* The adm1021 registers */ +/* Read-only */ +#define ADM1021_REG_TEMP 0x00 +#define ADM1021_REG_REMOTE_TEMP 0x01 +#define ADM1021_REG_STATUS 0x02 +#define ADM1021_REG_MAN_ID 0x0FE /* 0x41 = AMD, 0x49 = TI, 0x4D = Maxim, 0x23 = Genesys , 0x54 = Onsemi*/ +#define ADM1021_REG_DEV_ID 0x0FF /* ADM1021 = 0x0X, ADM1023 = 0x3X */ +#define ADM1021_REG_DIE_CODE 0x0FF /* MAX1617A */ +/* These use different addresses for reading/writing */ +#define ADM1021_REG_CONFIG_R 0x03 +#define ADM1021_REG_CONFIG_W 0x09 +#define ADM1021_REG_CONV_RATE_R 0x04 +#define ADM1021_REG_CONV_RATE_W 0x0A +/* These are for the ADM1023's additional precision on the remote temp sensor */ +#define ADM1021_REG_REM_TEMP_PREC 0x010 +#define ADM1021_REG_REM_OFFSET 0x011 +#define ADM1021_REG_REM_OFFSET_PREC 0x012 +#define ADM1021_REG_REM_TOS_PREC 0x013 +#define ADM1021_REG_REM_THYST_PREC 0x014 +/* limits */ +#define ADM1021_REG_TOS_R 0x05 +#define ADM1021_REG_TOS_W 0x0B +#define ADM1021_REG_REMOTE_TOS_R 0x07 +#define ADM1021_REG_REMOTE_TOS_W 0x0D +#define ADM1021_REG_THYST_R 0x06 +#define ADM1021_REG_THYST_W 0x0C +#define ADM1021_REG_REMOTE_THYST_R 0x08 +#define ADM1021_REG_REMOTE_THYST_W 0x0E +/* write-only */ +#define ADM1021_REG_ONESHOT 0x0F + + +/* Conversions. Rounding and limit checking is only done on the TO_REG + variants. Note that you should be a bit careful with which arguments + these macros are called: arguments may be evaluated more than once. + Fixing this is just not worth it. */ +/* Conversions note: 1021 uses normal integer signed-byte format*/ +#define TEMP_FROM_REG(val) (val > 127 ? val-256 : 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); + +/* (amalysh) read only mode, otherwise any limit's writing confuse BIOS */ +static int read_only = 0; + + +/* This is the driver that will be inserted */ +static struct i2c_driver adm1021_driver = { + /* 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 if (i == 0x54) + kind = mc1066; + 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 if (kind == mc1066) { + type_name = "mc1066"; + client_name = "MC1066 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) +{ + if (read_only > 0) + return 0; + + 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"); + +MODULE_PARM(read_only, "i"); +MODULE_PARM_DESC(read_only, "Don't set any values, read only mode"); + +int init_module(void) +{ + return sensors_adm1021_init(); +} + +int cleanup_module(void) +{ + return adm1021_cleanup(); +} + +#endif /* MODULE */ --- linux-old/drivers/sensors/adm1024.c Tue Jun 17 15:50:29 CEST 2003 +++ linux/drivers/sensors/adm1024.c Tue Jun 17 15:50:29 CEST 2003 @@ -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 "20021208" +#define LM_VERSION "2.7.0" +#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 Tue Jun 17 15:50:29 CEST 2003 +++ linux/drivers/sensors/adm1025.c Tue Jun 17 15:50:29 CEST 2003 @@ -0,0 +1,777 @@ +/* + 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 "20021208" +#define LM_VERSION "2.7.0" +#include +#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) + +/* 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 */ + u8 vrm; +}; + + +#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); +static void adm1025_vrm(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}, + {ADM1025_SYSCTL_VRM, "vrm", NULL, 0, 0644, NULL, &i2c_proc_real, + &i2c_sysctl_real, NULL, &adm1025_vrm}, + {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) +{ + struct adm1025_data *data = client->data; + + data->vrm = DEFAULT_VRM; + /* 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 = 3; + else if (operation == SENSORS_PROC_REAL_READ) { + adm1025_update_client(client); + results[0] = vid_from_reg(data->vid, data->vrm); + *nrels_mag = 1; + } +} + +void adm1025_vrm(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) { + results[0] = data->vrm; + *nrels_mag = 1; + } else if (operation == SENSORS_PROC_REAL_WRITE) { + if (*nrels_mag >= 1) + data->vrm = results[0]; + } +} + +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 Tue Jun 17 15:50:29 CEST 2003 +++ linux/drivers/sensors/adm9240.c Tue Jun 17 15:50:29 CEST 2003 @@ -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 "20021208" +#define LM_VERSION "2.7.0" +#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 Tue Jun 17 15:50:29 CEST 2003 +++ linux/drivers/sensors/bt869.c Tue Jun 17 15:50:29 CEST 2003 @@ -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 "20021208" +#define LM_VERSION "2.7.0" +#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 Tue Jun 17 15:50:30 CEST 2003 +++ linux/drivers/sensors/ddcmon.c Tue Jun 17 15:50:30 CEST 2003 @@ -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 "20021208" +#define LM_VERSION "2.7.0" +#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 Tue Jun 17 15:50:30 CEST 2003 +++ linux/drivers/sensors/ds1621.c Tue Jun 17 15:50:30 CEST 2003 @@ -0,0 +1,626 @@ +/* + 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 "20021208" +#define LM_VERSION "2.7.0" +#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_COM_START) || (reg == DS1621_COM_STOP) ) + return i2c_smbus_write_byte(client, reg); + else + 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) +{ + int reg; + + /* 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)); + reg = ds1621_read_value(client, DS1621_REG_CONF); + /* start the continous conversion */ + if(reg & 0x01) + ds1621_write_value(client, DS1621_REG_CONF, reg & 0xfe); +} + +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_write_value(client, DS1621_COM_START, 0); + } + + /* 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_write_value(client, DS1621_COM_START, 0); + data->enable=1; + } else { + ds1621_write_value(client, DS1621_COM_STOP, 0); + data->enable=0; + } + } else { + ds1621_write_value(client, DS1621_COM_START, 0); + 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 Tue Jun 17 15:50:30 CEST 2003 +++ linux/drivers/sensors/eeprom.c Tue Jun 17 15:50:30 CEST 2003 @@ -0,0 +1,494 @@ +/* + 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 "20021208" +#define LM_VERSION "2.7.0" +#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) { + /* prevent 24RF08 corruption */ + i2c_smbus_write_quick(new_client, 0); + 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 Tue Jun 17 15:50:30 CEST 2003 +++ linux/drivers/sensors/fscpos.c Tue Jun 17 15:50:30 CEST 2003 @@ -0,0 +1,772 @@ +/* + 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 "20021208" +#define LM_VERSION "2.7.0" +#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[] = { 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 Tue Jun 17 15:50:30 CEST 2003 +++ linux/drivers/sensors/fscscy.c Tue Jun 17 15:50:30 CEST 2003 @@ -0,0 +1,987 @@ +/* + 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 "20021208" +#define LM_VERSION "2.7.0" +#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[] = { 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 Tue Jun 17 15:50:31 CEST 2003 +++ linux/drivers/sensors/gl518sm.c Tue Jun 17 15:50:31 CEST 2003 @@ -0,0 +1,1128 @@ +/* + 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 "20021208" +#define LM_VERSION "2.7.0" +#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; + } + } + + type_name = "gl518sm"; + if (kind == gl518sm_r00) { + client_name = "GL518SM Revision 0x00 chip"; + } else if (kind == gl518sm_r80) { + 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 */ + if (kind == gl518sm_r00) + data->iterate = 0; + else + data->iterate = 3; + 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 && + data->type == gl518sm_r00 ) { + 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 Tue Jun 17 15:50:31 CEST 2003 +++ linux/drivers/sensors/gl520sm.c Tue Jun 17 15:50:31 CEST 2003 @@ -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 "20021208" +#define LM_VERSION "2.7.0" +#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->