From f18d2108159aeb6476973b566e10b3cdb8268472 Mon Sep 17 00:00:00 2001 From: Frodo Looijaard Date: Sat, 5 Dec 1998 00:47:22 +0000 Subject: [PATCH] PIIX4 update It compiles now for 2.0 kernels; I hope it still compiles for 2.1 kernels :-) Changes: * The non-BIOS PCI interface was introduced in kernel 2.1.54, so I changed all #ifdef statements to use that kernel as starting point * I integrated everything in piix4.h into piix4.c; as no other module will ever need to know about anything PIIX4-specific, we do not need a separate header file (same went for lm78.c and lm75.c) * In piix4_setup, both the 2.0 and 2.1 versions continue seeking for PCI devices until they have found 'function 3' of the PIIX4. This is the 'function' we need for SMBus access. * At some places in piix4_setup, even for 2.1 kernels the PCI BIOS code was used. This was seemingly needed, probably because the wrong 'function' was accessed. I have removed this; if there are troubles with the PIIX4 on kernels newer than 2.1.53, we need to introduce it again. * Renamed global things to start uniformly with piix4_ * Changed printk statements to start with piix4.o: * Pasted in missing part of piix4_access * No need to lock the adapter on access; this is already done by the smbus_access function in smbus.c. I corrected a locking bug there too, by the way. * Added a few include files needed by 2.0 at least * Replaced the call to schedule_timeout() in piix4_do_pause with the old code; schedule_timeout seems to be defined nowhere?!? Phil, was this your addition, and if so, what should it read? git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@34 7894878c-1315-0410-8ee3-d5d059ff63e0 --- kernel/busses/i2c-piix4.c | 282 ++++++++++++++++++++++---------------- kernel/compat.h | 26 ++++ kernel/include/compat.h | 26 ++++ kernel/include/smbus.h | 4 + kernel/smbus.c | 14 +- src/compat.h | 26 ++++ src/piix4.c | 282 ++++++++++++++++++++++---------------- src/piix4.h | 74 ---------- src/smbus.c | 14 +- src/smbus.h | 4 + 10 files changed, 430 insertions(+), 322 deletions(-) delete mode 100644 src/piix4.h diff --git a/kernel/busses/i2c-piix4.c b/kernel/busses/i2c-piix4.c index e8b4ab13..86162648 100644 --- a/kernel/busses/i2c-piix4.c +++ b/kernel/busses/i2c-piix4.c @@ -18,26 +18,57 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* Note: New PCI (non-BIOS) interface introduced in 2.1.54! */ +/* Note: we assume there can only be one PIIX4, with one SMBus interface */ +#include #include #include -#include #include #include +#include +#include #include "smbus.h" #include "version.h" #include "compat.h" -#include "piix4.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54)) +#include +#endif + + +/* 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 static int piix4_init(void); static int piix4_cleanup(void); static int piix4_setup(void); static s32 piix4_access(u8 addr, char read_write, u8 command, int size, union smbus_data * data); -/* Internal functions */ -static void do_pause( unsigned int amount ); -static int SMBus_PIIX4_Transaction(void); +static void piix4_do_pause( unsigned int amount ); +static int piix4_transaction(void); #ifdef MODULE extern int init_module(void); @@ -46,62 +77,70 @@ extern int cleanup_module(void); static struct smbus_adapter piix4_adapter; static int piix4_initialized; - -/* Global variables -- At least some of these may be modified/moved later */ -static unsigned short PIIX4_smba = 0; -static struct semaphore smbus_piix4_sem = MUTEX; +static unsigned short piix4_smba = 0; -/* Detect whether a PIIX4 can be found, and initialize it, where necessary. - Return -ENODEV if not found. */ +/* 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; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54)) struct pci_dev *PIIX4_dev; #else unsigned char PIIX4_bus, PIIX4_devfn; + int i,res; #endif + /* First check whether we can access PCI at all */ if (pci_present() == 0) { - printk("SMBus: Error: No PCI-bus found!\n"); + printk("piix4.o: Error: No PCI-bus found!\n"); error_return=-ENODEV; - } else { + goto END; + } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) + /* Look for the PIIX4, function 3 */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54)) + /* Note: we keep on searching until we have found 'function 3' */ + PIIX4_dev = NULL; + do PIIX4_dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82371AB_0, NULL); - if(PIIX4_dev == NULL) { -#else - if(pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, - 0, &PIIX4_bus, &PIIX4_devfn)) { -#endif - printk("SMBus: Error: Can't detect PIIX4!\n"); - error_return=-ENODEV; - } else { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) - /* For some reason, pci_read_config fails here sometimes! */ - pcibios_read_config_word(PIIX4_dev->bus->number, PIIX4_dev->devfn | 3, - SMBBA, &PIIX4_smba); -#else - pci_read_config_word_united(PIIX4_dev, PIIX4_bus ,PIIX4_devfn | 3, - SMBBA,&PIIX4_smba); -#endif - PIIX4_smba &= 0xfff0; - if (check_region(PIIX4_smba, 8)) { - printk("PIIX4_smb region 0x%x already in use!\n", - PIIX4_smba); - error_return=-ENODEV; - } else { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) - pcibios_read_config_byte(PIIX4_dev->bus->number, - PIIX4_dev->devfn | 3, SMBHSTCFG, &temp); -#else - pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn | 3, - SMBHSTCFG, &temp); -#endif + PCI_DEVICE_ID_INTEL_82371AB_0, PIIX4_dev); + while(PIIX4_dev && (PCI_FUNC(PIIX4_dev->devfn) != 3); + if(PIIX4_dev == NULL) { +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54) */ + for (i = 0; + ! (res = pcibios_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_0, + i,&PIIX4_bus, &PIIX4_devfn)) && + PCI_FUNC(PIIX4_devfn) != 3; + i++); + if (res) { +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54) */ + printk("piix4.o: Error: Can't detect PIIX4, function 3!\n"); + error_return=-ENODEV; + goto END; + } + +/* Determine the address of the SMBus areas */ + pci_read_config_word_united(PIIX4_dev, PIIX4_bus ,PIIX4_devfn, + SMBBA,&piix4_smba); + piix4_smba &= 0xfff0; + + if (check_region(piix4_smba, 8)) { + printk("piix4.o: PIIX4_smb region 0x%x already in use!\n", piix4_smba); + error_return=-ENODEV; + goto END; + } + + pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn, + SMBHSTCFG, &temp); + + #ifdef FORCE_PIIX4_ENABLE /* This should never need to be done, but has been noted that many Dell machines have the SMBus interface on the PIIX4 @@ -109,65 +148,57 @@ int piix4_setup(void) done by the Bios! Don't complain if your hardware does weird things after enabling this. :') Check for Bios updates before resorting to this. */ - if ((temp & 1) == 0) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) - pcibios_write_config_byte(PIIX4_dev->bus->number, PIIX4_dev->devfn | 3, - SMBHSTCFG, temp | 1); -#else - pcibios_write_config_byte(PIIX4_bus, PIIX4_devfn | 3, SMBHSTCFG, temp | 1); -#endif - printk("SMBus: WARNING: PIIX4 SMBus interface has been FORCEFULLY ENABLED!!\n"); - /* Update configuration value */ - pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn | 3, SMBHSTCFG, &temp); - /* Note: We test the bit again in the next 'if' just to be sure... */ - } -#endif - if ((temp & 1) == 0) { - printk("SMBUS: Error: Host SMBus controller not enabled!\n"); - piix4_initialized=0; - error_return=-ENODEV; - } else { - /* Everything is happy, let's grab the memory and set things up. */ - request_region(PIIX4_smba, 8, "SMBus"); - piix4_initialized=1; - } -#ifdef DEBUG - if ((temp & 0x0E) == 8) - printk("SMBUS: PIIX4 using Interrupt 9 for SMBus.\n"); - else if ((temp & 0x0E) == 0) - printk("SMBUS: PIIX4 using Interrupt SMI# for SMBus.\n"); - else - printk( "SMBUS: PIIX4: Illegal Interrupt configuration (or code out of date)!\n"); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) - pcibios_read_config_byte(PIIX4_dev->bus->number, - PIIX4_dev->devfn | 3, SMBREV, &temp); -#else - pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn | 3, - SMBREV, &temp); -#endif - printk("SMBUS: SMBREV = 0x%X\n",temp); -#endif - } - } - if (error_return) - printk("SMBus setup failed.\n"); -#ifdef DEBUG - else - printk("PIIX4_smba = 0x%X\n",PIIX4_smba); -#endif + if ((temp & 1) == 0) { + pcibios_write_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn, + SMBHSTCFG, temp | 1); + printk("piix4.0: WARNING: PIIX4 SMBus interface has been FORCEFULLY " + "ENABLED!!\n"); + /* Update configuration value */ + pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn, + SMBHSTCFG, &temp); + /* Note: We test the bit again in the next 'if' just to be sure... */ } +#endif /* FORCE_PIIX4_ENABLE */ + + if ((temp & 1) == 0) { + printk("SMBUS: Error: 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, "SMBus"); + +#ifdef DEBUG + if ((temp & 0x0E) == 8) + printk("piix4.o: PIIX4 using Interrupt 9 for SMBus.\n"); + else if ((temp & 0x0E) == 0) + printk("piix4.o: PIIX4 using Interrupt SMI# for SMBus.\n"); + else + printk("piix4.o: PIIX4: Illegal Interrupt configuration (or code out " + "of date)!\n"); + + pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn, SMBREV, + &temp); + printk("piix4.o: SMBREV = 0x%X\n",temp); + printk("piix4.o: PIIX4_smba = 0x%X\n",piix4_smba); +#endif /* DEBUG */ + +END: return error_return; } + /* Internally used pause function */ -void do_pause( unsigned int amount ) +void piix4_do_pause( unsigned int amount ) { current->state = TASK_INTERRUPTIBLE; - schedule_timeout(amount); + current->timeout = jiffies + amount; + schedule(); } /* Another internally used function */ -int SMBus_PIIX4_Transaction(void) +int piix4_transaction(void) { int temp; int result=0; @@ -176,17 +207,17 @@ int SMBus_PIIX4_Transaction(void) /* Make sure the SMBus host is ready to start transmitting */ if ((temp = inb_p(SMBHSTSTS)) != 0x00) { #ifdef DEBUG - printk("SMBus: SMBus_Read: SMBus busy (%02x). Resetting... ",temp); + printk("piix4.o: SMBus busy (%02x). Resetting... ",temp); #endif outb_p(temp, SMBHSTSTS); if ((temp = inb_p(SMBHSTSTS)) != 0x00) { #ifdef DEBUG - printk("Failed! (%02x)\n",temp); + printk("piix4.o: Failed! (%02x)\n",temp); #endif return -1; } else { #ifdef DEBUG - printk("Successfull!\n"); + printk("piix4.o: Successfull!\n"); #endif } } @@ -195,20 +226,20 @@ int SMBus_PIIX4_Transaction(void) outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); /* Wait for a fraction of a second! (See PIIX4 docs errata) */ - do_pause(1); + piix4_do_pause(1); /* Poll Host_busy bit */ temp=inb_p(SMBHSTSTS) & 0x01; while (temp & (timeout++ < MAX_TIMEOUT)) { /* Wait for a while and try again*/ - do_pause(1); + piix4_do_pause(1); temp = inb_p(SMBHSTSTS) & 0x01; } /* If the SMBus is still busy, we give up */ if (timeout >= MAX_TIMEOUT) { #ifdef DEBUG - printk("SMBus: SMBus_Read: SMBus Timeout!\n"); + printk("piix4.o: SMBus Timeout!\n"); result = -1; #endif } @@ -218,20 +249,21 @@ int SMBus_PIIX4_Transaction(void) if (temp & 0x10) { result = -1; #ifdef DEBUG - printk("SMBus error: Failed bus transaction\n"); + printk("piix4.o: Error: Failed bus transaction\n"); #endif } if (temp & 0x08) { result = -1; - printk("SMBus error: Bus collision! SMBus may be locked until next hard reset. (sorry!)\n"); + printk("piix4.o: Bus collision! SMBus may be locked until next hard + reset. (sorry!)\n"); /* Clock stops and slave is stuck in mid-transmission */ } if (temp & 0x04) { result = -1; #ifdef DEBUG - printk("SMBus error: no response!\n"); + printk("piix4.o: Error: no response!\n"); #endif } @@ -240,7 +272,7 @@ int SMBus_PIIX4_Transaction(void) if ((temp = inb_p(SMBHSTSTS)) != 0x00) { #ifdef DEBUG - printk("SMBus error: Failed reset at end of transaction (%02x)\n",temp); + printk("piix4.o: Failed reset at end of transaction (%02x)\n",temp); #endif } return result; @@ -252,8 +284,6 @@ s32 piix4_access(u8 addr, char read_write, { int i,len; - down(&smbus_piix4_sem); - outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); switch(size) { @@ -296,16 +326,36 @@ s32 piix4_access(u8 addr, char read_write, } } - if (SMBus_PIIX4_Transaction()) { /* Error in transaction */ - up(&smbus_piix4_sem); + if (piix4_transaction()) /* Error in transaction */ return -1; - } + - if ((read_write == SMBUS_WRITE) || (size == SMBUS_QUICK)) { - up(&smbus_piix4_sem); + if ((read_write == SMBUS_WRITE) || (size == SMBUS_QUICK)) return 0; + + + switch(size) { + case SMBUS_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 SMBUS_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case SMBUS_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case SMBUS_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; +} int piix4_init(void) @@ -352,10 +402,8 @@ int piix4_cleanup(void) piix4_initialized=0; } if (piix4_initialized >= 1) { - if (piix4_initialized) { - release_region(PIIX4_smba, 8); - piix4_initialized=0; - } + release_region(piix4_smba, 8); + piix4_initialized=0; } return 0; } diff --git a/kernel/compat.h b/kernel/compat.h index 8bae94d8..0c21b1de 100644 --- a/kernel/compat.h +++ b/kernel/compat.h @@ -49,5 +49,31 @@ #define get_user_data(to,from) get_user(to,from) #endif +/* If the new PCI interface is not present, fall back on the old PCI BIOS + interface. We also define some things to unite both interfaces. Not + very nice, but it works like a charm. + device is the 2.1 struct pci_dev, bus is the 2.0 bus number, dev is the + 2.0 device/function code, com is the PCI command, and res is the result. */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54)) +#define pci_present pcibios_present +#define pci_read_config_byte_united(device,bus,dev,com,res) \ + pcibios_read_config_byte(bus,dev,com,res); +#define pci_read_config_word_united(device,bus,dev,com,res) \ + pcibios_read_config_word(bus,dev,com,res); +#define pci_write_config_byte_united(device,bus,dev,com,res) \ + pcibios_write_config_byte(bus,dev,com,res); +#define pci_write_config_word_united(device,bus,dev,com,res) \ + pcibios_write_config_word(bus,dev,com,res); +#else +#define pci_read_config_byte_united(device,bus,dev,com,res) \ + pci_read_config_byte(device,com,res); +#define pci_read_config_word_united(device,bus,dev,com,res) \ + pci_read_config_word(device,com,res); +#define pci_write_config_byte_united(device,bus,dev,com,res) \ + pci_write_config_byte(device,com,res); +#define pci_write_config_word_united(device,bus,dev,com,res) \ + pci_write_config_byte(device,com,res); +#endif + #endif /* SENSORS_COMPAT_H */ diff --git a/kernel/include/compat.h b/kernel/include/compat.h index 8bae94d8..0c21b1de 100644 --- a/kernel/include/compat.h +++ b/kernel/include/compat.h @@ -49,5 +49,31 @@ #define get_user_data(to,from) get_user(to,from) #endif +/* If the new PCI interface is not present, fall back on the old PCI BIOS + interface. We also define some things to unite both interfaces. Not + very nice, but it works like a charm. + device is the 2.1 struct pci_dev, bus is the 2.0 bus number, dev is the + 2.0 device/function code, com is the PCI command, and res is the result. */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54)) +#define pci_present pcibios_present +#define pci_read_config_byte_united(device,bus,dev,com,res) \ + pcibios_read_config_byte(bus,dev,com,res); +#define pci_read_config_word_united(device,bus,dev,com,res) \ + pcibios_read_config_word(bus,dev,com,res); +#define pci_write_config_byte_united(device,bus,dev,com,res) \ + pcibios_write_config_byte(bus,dev,com,res); +#define pci_write_config_word_united(device,bus,dev,com,res) \ + pcibios_write_config_word(bus,dev,com,res); +#else +#define pci_read_config_byte_united(device,bus,dev,com,res) \ + pci_read_config_byte(device,com,res); +#define pci_read_config_word_united(device,bus,dev,com,res) \ + pci_read_config_word(device,com,res); +#define pci_write_config_byte_united(device,bus,dev,com,res) \ + pci_write_config_byte(device,com,res); +#define pci_write_config_word_united(device,bus,dev,com,res) \ + pci_write_config_byte(device,com,res); +#endif + #endif /* SENSORS_COMPAT_H */ diff --git a/kernel/include/smbus.h b/kernel/include/smbus.h index 4ecbf545..4a164113 100644 --- a/kernel/include/smbus.h +++ b/kernel/include/smbus.h @@ -20,6 +20,8 @@ #ifndef SENSORS_SMBUS_H #define SENSORS_SMBUS_H +#ifdef __KERNEL__ + /* This file must interface with Simon Vogl's i2c driver. Version 19981006 is OK, earlier versions are not; later versions will probably give problems too. @@ -285,5 +287,7 @@ extern inline int smbus_write_block_data(struct i2c_adapter * adapter, i2c_detach_client((struct i2c_client *) (clientptr)) +#endif /* def __KERNEL__ */ + #endif /* ndef SENSORS_SMBUS_H */ diff --git a/kernel/smbus.c b/kernel/smbus.c index 405104d2..f51ab649 100644 --- a/kernel/smbus.c +++ b/kernel/smbus.c @@ -74,21 +74,21 @@ s32 smbus_access (struct i2c_adapter * adapter, u8 addr, char read_write, u8 command, int size, union smbus_data * data) { int res; + if (adapter->id & ALGO_SMBUS) { #ifdef SPINLOCK - spin_lock_irqsave(&adapter->lock,adapter->lockflags); + spin_lock_irqsave(&adapter->lock,adapter->lockflags); #else - down(&adapter->lock); + down(&adapter->lock); #endif - if (adapter->id & ALGO_SMBUS) res = ((struct smbus_adapter *) adapter) -> smbus_access(addr,read_write,command,size,data); - else - res = smbus_access_i2c(adapter,addr,read_write,command,size,data); #ifdef SPINLOCK - spin_unlock_irqrestore(&adapter->lock,adapter->lockflags); + spin_unlock_irqrestore(&adapter->lock,adapter->lockflags); #else - up(&adapter->lock); + up(&adapter->lock); #endif + } else + res = smbus_access_i2c(adapter,addr,read_write,command,size,data); return res; } diff --git a/src/compat.h b/src/compat.h index 8bae94d8..0c21b1de 100644 --- a/src/compat.h +++ b/src/compat.h @@ -49,5 +49,31 @@ #define get_user_data(to,from) get_user(to,from) #endif +/* If the new PCI interface is not present, fall back on the old PCI BIOS + interface. We also define some things to unite both interfaces. Not + very nice, but it works like a charm. + device is the 2.1 struct pci_dev, bus is the 2.0 bus number, dev is the + 2.0 device/function code, com is the PCI command, and res is the result. */ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54)) +#define pci_present pcibios_present +#define pci_read_config_byte_united(device,bus,dev,com,res) \ + pcibios_read_config_byte(bus,dev,com,res); +#define pci_read_config_word_united(device,bus,dev,com,res) \ + pcibios_read_config_word(bus,dev,com,res); +#define pci_write_config_byte_united(device,bus,dev,com,res) \ + pcibios_write_config_byte(bus,dev,com,res); +#define pci_write_config_word_united(device,bus,dev,com,res) \ + pcibios_write_config_word(bus,dev,com,res); +#else +#define pci_read_config_byte_united(device,bus,dev,com,res) \ + pci_read_config_byte(device,com,res); +#define pci_read_config_word_united(device,bus,dev,com,res) \ + pci_read_config_word(device,com,res); +#define pci_write_config_byte_united(device,bus,dev,com,res) \ + pci_write_config_byte(device,com,res); +#define pci_write_config_word_united(device,bus,dev,com,res) \ + pci_write_config_byte(device,com,res); +#endif + #endif /* SENSORS_COMPAT_H */ diff --git a/src/piix4.c b/src/piix4.c index e8b4ab13..86162648 100644 --- a/src/piix4.c +++ b/src/piix4.c @@ -18,26 +18,57 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* Note: New PCI (non-BIOS) interface introduced in 2.1.54! */ +/* Note: we assume there can only be one PIIX4, with one SMBus interface */ +#include #include #include -#include #include #include +#include +#include #include "smbus.h" #include "version.h" #include "compat.h" -#include "piix4.h" + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54)) +#include +#endif + + +/* 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 static int piix4_init(void); static int piix4_cleanup(void); static int piix4_setup(void); static s32 piix4_access(u8 addr, char read_write, u8 command, int size, union smbus_data * data); -/* Internal functions */ -static void do_pause( unsigned int amount ); -static int SMBus_PIIX4_Transaction(void); +static void piix4_do_pause( unsigned int amount ); +static int piix4_transaction(void); #ifdef MODULE extern int init_module(void); @@ -46,62 +77,70 @@ extern int cleanup_module(void); static struct smbus_adapter piix4_adapter; static int piix4_initialized; - -/* Global variables -- At least some of these may be modified/moved later */ -static unsigned short PIIX4_smba = 0; -static struct semaphore smbus_piix4_sem = MUTEX; +static unsigned short piix4_smba = 0; -/* Detect whether a PIIX4 can be found, and initialize it, where necessary. - Return -ENODEV if not found. */ +/* 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; -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54)) struct pci_dev *PIIX4_dev; #else unsigned char PIIX4_bus, PIIX4_devfn; + int i,res; #endif + /* First check whether we can access PCI at all */ if (pci_present() == 0) { - printk("SMBus: Error: No PCI-bus found!\n"); + printk("piix4.o: Error: No PCI-bus found!\n"); error_return=-ENODEV; - } else { + goto END; + } -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) + /* Look for the PIIX4, function 3 */ +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54)) + /* Note: we keep on searching until we have found 'function 3' */ + PIIX4_dev = NULL; + do PIIX4_dev = pci_find_device(PCI_VENDOR_ID_INTEL, - PCI_DEVICE_ID_INTEL_82371AB_0, NULL); - if(PIIX4_dev == NULL) { -#else - if(pcibios_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, - 0, &PIIX4_bus, &PIIX4_devfn)) { -#endif - printk("SMBus: Error: Can't detect PIIX4!\n"); - error_return=-ENODEV; - } else { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) - /* For some reason, pci_read_config fails here sometimes! */ - pcibios_read_config_word(PIIX4_dev->bus->number, PIIX4_dev->devfn | 3, - SMBBA, &PIIX4_smba); -#else - pci_read_config_word_united(PIIX4_dev, PIIX4_bus ,PIIX4_devfn | 3, - SMBBA,&PIIX4_smba); -#endif - PIIX4_smba &= 0xfff0; - if (check_region(PIIX4_smba, 8)) { - printk("PIIX4_smb region 0x%x already in use!\n", - PIIX4_smba); - error_return=-ENODEV; - } else { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) - pcibios_read_config_byte(PIIX4_dev->bus->number, - PIIX4_dev->devfn | 3, SMBHSTCFG, &temp); -#else - pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn | 3, - SMBHSTCFG, &temp); -#endif + PCI_DEVICE_ID_INTEL_82371AB_0, PIIX4_dev); + while(PIIX4_dev && (PCI_FUNC(PIIX4_dev->devfn) != 3); + if(PIIX4_dev == NULL) { +#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2,1,54) */ + for (i = 0; + ! (res = pcibios_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_0, + i,&PIIX4_bus, &PIIX4_devfn)) && + PCI_FUNC(PIIX4_devfn) != 3; + i++); + if (res) { +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,54) */ + printk("piix4.o: Error: Can't detect PIIX4, function 3!\n"); + error_return=-ENODEV; + goto END; + } + +/* Determine the address of the SMBus areas */ + pci_read_config_word_united(PIIX4_dev, PIIX4_bus ,PIIX4_devfn, + SMBBA,&piix4_smba); + piix4_smba &= 0xfff0; + + if (check_region(piix4_smba, 8)) { + printk("piix4.o: PIIX4_smb region 0x%x already in use!\n", piix4_smba); + error_return=-ENODEV; + goto END; + } + + pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn, + SMBHSTCFG, &temp); + + #ifdef FORCE_PIIX4_ENABLE /* This should never need to be done, but has been noted that many Dell machines have the SMBus interface on the PIIX4 @@ -109,65 +148,57 @@ int piix4_setup(void) done by the Bios! Don't complain if your hardware does weird things after enabling this. :') Check for Bios updates before resorting to this. */ - if ((temp & 1) == 0) { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) - pcibios_write_config_byte(PIIX4_dev->bus->number, PIIX4_dev->devfn | 3, - SMBHSTCFG, temp | 1); -#else - pcibios_write_config_byte(PIIX4_bus, PIIX4_devfn | 3, SMBHSTCFG, temp | 1); -#endif - printk("SMBus: WARNING: PIIX4 SMBus interface has been FORCEFULLY ENABLED!!\n"); - /* Update configuration value */ - pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn | 3, SMBHSTCFG, &temp); - /* Note: We test the bit again in the next 'if' just to be sure... */ - } -#endif - if ((temp & 1) == 0) { - printk("SMBUS: Error: Host SMBus controller not enabled!\n"); - piix4_initialized=0; - error_return=-ENODEV; - } else { - /* Everything is happy, let's grab the memory and set things up. */ - request_region(PIIX4_smba, 8, "SMBus"); - piix4_initialized=1; - } -#ifdef DEBUG - if ((temp & 0x0E) == 8) - printk("SMBUS: PIIX4 using Interrupt 9 for SMBus.\n"); - else if ((temp & 0x0E) == 0) - printk("SMBUS: PIIX4 using Interrupt SMI# for SMBus.\n"); - else - printk( "SMBUS: PIIX4: Illegal Interrupt configuration (or code out of date)!\n"); -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)) - pcibios_read_config_byte(PIIX4_dev->bus->number, - PIIX4_dev->devfn | 3, SMBREV, &temp); -#else - pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn | 3, - SMBREV, &temp); -#endif - printk("SMBUS: SMBREV = 0x%X\n",temp); -#endif - } - } - if (error_return) - printk("SMBus setup failed.\n"); -#ifdef DEBUG - else - printk("PIIX4_smba = 0x%X\n",PIIX4_smba); -#endif + if ((temp & 1) == 0) { + pcibios_write_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn, + SMBHSTCFG, temp | 1); + printk("piix4.0: WARNING: PIIX4 SMBus interface has been FORCEFULLY " + "ENABLED!!\n"); + /* Update configuration value */ + pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn, + SMBHSTCFG, &temp); + /* Note: We test the bit again in the next 'if' just to be sure... */ } +#endif /* FORCE_PIIX4_ENABLE */ + + if ((temp & 1) == 0) { + printk("SMBUS: Error: 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, "SMBus"); + +#ifdef DEBUG + if ((temp & 0x0E) == 8) + printk("piix4.o: PIIX4 using Interrupt 9 for SMBus.\n"); + else if ((temp & 0x0E) == 0) + printk("piix4.o: PIIX4 using Interrupt SMI# for SMBus.\n"); + else + printk("piix4.o: PIIX4: Illegal Interrupt configuration (or code out " + "of date)!\n"); + + pci_read_config_byte_united(PIIX4_dev, PIIX4_bus, PIIX4_devfn, SMBREV, + &temp); + printk("piix4.o: SMBREV = 0x%X\n",temp); + printk("piix4.o: PIIX4_smba = 0x%X\n",piix4_smba); +#endif /* DEBUG */ + +END: return error_return; } + /* Internally used pause function */ -void do_pause( unsigned int amount ) +void piix4_do_pause( unsigned int amount ) { current->state = TASK_INTERRUPTIBLE; - schedule_timeout(amount); + current->timeout = jiffies + amount; + schedule(); } /* Another internally used function */ -int SMBus_PIIX4_Transaction(void) +int piix4_transaction(void) { int temp; int result=0; @@ -176,17 +207,17 @@ int SMBus_PIIX4_Transaction(void) /* Make sure the SMBus host is ready to start transmitting */ if ((temp = inb_p(SMBHSTSTS)) != 0x00) { #ifdef DEBUG - printk("SMBus: SMBus_Read: SMBus busy (%02x). Resetting... ",temp); + printk("piix4.o: SMBus busy (%02x). Resetting... ",temp); #endif outb_p(temp, SMBHSTSTS); if ((temp = inb_p(SMBHSTSTS)) != 0x00) { #ifdef DEBUG - printk("Failed! (%02x)\n",temp); + printk("piix4.o: Failed! (%02x)\n",temp); #endif return -1; } else { #ifdef DEBUG - printk("Successfull!\n"); + printk("piix4.o: Successfull!\n"); #endif } } @@ -195,20 +226,20 @@ int SMBus_PIIX4_Transaction(void) outb_p(inb(SMBHSTCNT) | 0x040, SMBHSTCNT); /* Wait for a fraction of a second! (See PIIX4 docs errata) */ - do_pause(1); + piix4_do_pause(1); /* Poll Host_busy bit */ temp=inb_p(SMBHSTSTS) & 0x01; while (temp & (timeout++ < MAX_TIMEOUT)) { /* Wait for a while and try again*/ - do_pause(1); + piix4_do_pause(1); temp = inb_p(SMBHSTSTS) & 0x01; } /* If the SMBus is still busy, we give up */ if (timeout >= MAX_TIMEOUT) { #ifdef DEBUG - printk("SMBus: SMBus_Read: SMBus Timeout!\n"); + printk("piix4.o: SMBus Timeout!\n"); result = -1; #endif } @@ -218,20 +249,21 @@ int SMBus_PIIX4_Transaction(void) if (temp & 0x10) { result = -1; #ifdef DEBUG - printk("SMBus error: Failed bus transaction\n"); + printk("piix4.o: Error: Failed bus transaction\n"); #endif } if (temp & 0x08) { result = -1; - printk("SMBus error: Bus collision! SMBus may be locked until next hard reset. (sorry!)\n"); + printk("piix4.o: Bus collision! SMBus may be locked until next hard + reset. (sorry!)\n"); /* Clock stops and slave is stuck in mid-transmission */ } if (temp & 0x04) { result = -1; #ifdef DEBUG - printk("SMBus error: no response!\n"); + printk("piix4.o: Error: no response!\n"); #endif } @@ -240,7 +272,7 @@ int SMBus_PIIX4_Transaction(void) if ((temp = inb_p(SMBHSTSTS)) != 0x00) { #ifdef DEBUG - printk("SMBus error: Failed reset at end of transaction (%02x)\n",temp); + printk("piix4.o: Failed reset at end of transaction (%02x)\n",temp); #endif } return result; @@ -252,8 +284,6 @@ s32 piix4_access(u8 addr, char read_write, { int i,len; - down(&smbus_piix4_sem); - outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); switch(size) { @@ -296,16 +326,36 @@ s32 piix4_access(u8 addr, char read_write, } } - if (SMBus_PIIX4_Transaction()) { /* Error in transaction */ - up(&smbus_piix4_sem); + if (piix4_transaction()) /* Error in transaction */ return -1; - } + - if ((read_write == SMBUS_WRITE) || (size == SMBUS_QUICK)) { - up(&smbus_piix4_sem); + if ((read_write == SMBUS_WRITE) || (size == SMBUS_QUICK)) return 0; + + + switch(size) { + case SMBUS_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 SMBUS_BYTE_DATA: + data->byte = inb_p(SMBHSTDAT0); + break; + case SMBUS_WORD_DATA: + data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + break; + case SMBUS_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; +} int piix4_init(void) @@ -352,10 +402,8 @@ int piix4_cleanup(void) piix4_initialized=0; } if (piix4_initialized >= 1) { - if (piix4_initialized) { - release_region(PIIX4_smba, 8); - piix4_initialized=0; - } + release_region(piix4_smba, 8); + piix4_initialized=0; } return 0; } diff --git a/src/piix4.h b/src/piix4.h deleted file mode 100644 index b9e8c91e..00000000 --- a/src/piix4.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - piix4.h - Part of a Linux module for reading sensor data. - Copyright (c) 1998 Alexander Larsson , - 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. -*/ - -/* This file was taken almost directly from the first (lm_sensors) project */ -/* Some things will probably need to change or will simply not be used. */ - - -#include -#include -/* - I/O Space Constants -*/ -#define SMBHSTSTS PIIX4_smba -#define SMBHSLVSTS PIIX4_smba + 1 -#define SMBHSTCNT PIIX4_smba + 2 -#define SMBHSTCMD PIIX4_smba + 3 -#define SMBHSTADD PIIX4_smba + 4 -#define SMBHSTDAT0 PIIX4_smba + 5 -#define SMBHSTDAT1 PIIX4_smba + 6 -#define SMBBLKDAT PIIX4_smba + 7 -#define SMBSLVCNT PIIX4_smba + 8 -#define SMBSHDWCMD PIIX4_smba + 9 -#define SMBSLVEVT PIIX4_smba + 0xA -#define SMBSLVDAT PIIX4_smba + 0xC - -/* - PCI Address Constants -*/ -#define SMBBA 0x090 -#define SMBHSTCFG 0x0D2 -#define SMBSLVC 0x0D3 -#define SMBSHDW1 0x0D4 -#define SMBSHDW2 0x0D5 -#define SMBREV 0x0D6 - -/* - Contants for other uses -*/ -#define MAX_TIMEOUT 500 -#define SMBUS_READ 1 -#define SMBUS_WRITE 0 -/* Set to 1 to enable (don't comment out completely) - This is known to freeze some machines (as Frodo knows ;') - in the current implemented state, so beware! */ -#define ENABLE_INT9 0 - -#define SMBUSTABLE_SIZE 64 - -/* - Prototypes -*/ -extern int SMBus_PIIX4_Init(void); -extern void SMBus_PIIX4_Cleanup(void); -extern int SMBus_PIIX4_Access(u8 addr, char read_write, u8 command, int size, - union SMBus_Data *data); - -extern int SMBus_PIIX4_Initialized; diff --git a/src/smbus.c b/src/smbus.c index 405104d2..f51ab649 100644 --- a/src/smbus.c +++ b/src/smbus.c @@ -74,21 +74,21 @@ s32 smbus_access (struct i2c_adapter * adapter, u8 addr, char read_write, u8 command, int size, union smbus_data * data) { int res; + if (adapter->id & ALGO_SMBUS) { #ifdef SPINLOCK - spin_lock_irqsave(&adapter->lock,adapter->lockflags); + spin_lock_irqsave(&adapter->lock,adapter->lockflags); #else - down(&adapter->lock); + down(&adapter->lock); #endif - if (adapter->id & ALGO_SMBUS) res = ((struct smbus_adapter *) adapter) -> smbus_access(addr,read_write,command,size,data); - else - res = smbus_access_i2c(adapter,addr,read_write,command,size,data); #ifdef SPINLOCK - spin_unlock_irqrestore(&adapter->lock,adapter->lockflags); + spin_unlock_irqrestore(&adapter->lock,adapter->lockflags); #else - up(&adapter->lock); + up(&adapter->lock); #endif + } else + res = smbus_access_i2c(adapter,addr,read_write,command,size,data); return res; } diff --git a/src/smbus.h b/src/smbus.h index 4ecbf545..4a164113 100644 --- a/src/smbus.h +++ b/src/smbus.h @@ -20,6 +20,8 @@ #ifndef SENSORS_SMBUS_H #define SENSORS_SMBUS_H +#ifdef __KERNEL__ + /* This file must interface with Simon Vogl's i2c driver. Version 19981006 is OK, earlier versions are not; later versions will probably give problems too. @@ -285,5 +287,7 @@ extern inline int smbus_write_block_data(struct i2c_adapter * adapter, i2c_detach_client((struct i2c_client *) (clientptr)) +#endif /* def __KERNEL__ */ + #endif /* ndef SENSORS_SMBUS_H */