Hi, Ok for a bit more detail. My changes aside from the cosmetic stuff are minimal at this point as alot of the work I did on this driver was duplicated on your part. I renamed all the defines to make sense to me as I was going through the datasheet. I realize this makes for clutered patch but I think the names are a little more intuitive now. I cleaned up device initialization a bit. Streamlined by removing the check_region call. I may have removed some other unneeded checks and cleaned up others. Of significant difference I change the SMBus IDLE timer to be slightly different than the default as was previously set. I got better results, i.e. less false collisions or unresponsive errors. The major overhaul occured in do_transaction and access. First I split the SMBus error handling into its own funtion ali1535_reset(). This allowed for a more streamlined approach to processing each request from access. Overall affect of this change can be seen in do_transaction, transactions succeed or fail much faster now then before. For me this change significantly sped up all smbus transactions while reducing the number of failed transactions by about 90%. On a busy smbus, e.g. when something like a smart battery takes over, colisions will be high but they are recovered more efficiently. The final section of changes occurs in ali1535_access. I removed some pre-conditional error checking, the for-loop. It doesn't matter the condition of the bus at this time anyway, if the bus isn't happy when the transaction actually occurs, reset() will take care of it. Secondly there was a pretty foul bug in the handling of BLOCK DATA that would cause the chip to go device error instantly. Things went much smoother without this line. outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); /* Reset SMBBLKDAT */ Attached is a better cvs diff, I missed a couple things in my last diff. Also fixed in a bug where device intialization would never fail given the conditional check in ali1535_probe and the return of ali1535_setup. One of the most confusing aspects of this chip is its name vs the PCI ID. It would seem alot of ALi1535 chips all have ID 1533. Was there actually ever a 1533? Who knows. What might be of use to your ali15x3 driver and this one is a merger and complete name change to ALI M7101 or something since this is where the SMBus controller lives. FWIW untill I had it figured out I was using a chopped up version of i2c-ali15x3 that would just skip the init checks on my chip. It worked quite nicely. If you have any other specific questions I am happy to try to answer them. Thanks, Matt fee at users.sourceforge.net On Sat, Jul 19, 2003 at 09:20:24PM -0400, Mark D. Studebaker wrote: > thanks for the patch, especially since it's against CVS. > > Our users have had some trouble with this driver and perhaps this will help. > Unfortunately the patch is huge due to your renaming of most of the > #defines, > and changing almost every printk, > so it's a little difficult to see what really changed. > > Since I can't test this perhaps you could give us some more info > on the changes. > > Also you imply in the comments that you fixed a lot of problems in 1535 that > still exist in 15x3 (which I wrote). If true, could you elaborate on the > fixes that may be appropriate for 15x3 as well. > > mds > -------------- next part -------------- Index: kernel/busses/i2c-ali1535.c =================================================================== RCS file: /home/cvs/lm_sensors2/kernel/busses/i2c-ali1535.c,v retrieving revision 1.15 diff -u -2 -r1.15 i2c-ali1535.c --- kernel/busses/i2c-ali1535.c 23 Jun 2003 01:21:04 -0000 1.15 +++ kernel/busses/i2c-ali1535.c 22 Jul 2003 01:03:53 -0000 @@ -2,5 +2,7 @@ i2c-ali1535.c - Part of lm_sensors, Linux kernel modules for hardware monitoring - Copyright (c) 2000 Frodo Looijaard <frodol at dds.nl>, + Copyright (c) 2000, 2003 + Matt Mercer <fee at users.sourceforge.net> + Frodo Looijaard <frodol at dds.nl>, Philip Edelbrock <phil at netroedge.com>, Mark D. Studebaker <mdsxyz123 at yahoo.com>, @@ -24,33 +26,35 @@ /* - This is the driver for the SMB Host controller on - Acer Labs Inc. (ALI) M1535 South Bridge. + This is the driver for the SMB Host controller on Acer Labs Inc. (ALi) M7101 + PMU component of the M1535 South Bridge. The SMB controller is part of the + M7101 device, which is an ACPI compliant Power Management Unit (PMU). + + The M1535 is a South bridge for portable systems. It is very similar to + the M15x3 South bridges also produced by ALi. There are 4 primary verions + of this chip, they are labeled 1535 (notebook), 1535D (desktop), 1535+ and + 1535D+. According to the datasheet and the pci config data these chips + report a PCI ID of 1533 although they are actually M1535. This is atleast + the case for the M1535 B1 and probably many others. + + Some of the differences between the 1533 and 1535, registers within the + M7101 component 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.ali.com.tw/ - 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. + Both the ACPI and SMBus I/O spaces must be enabled. We make sure that the + SMBus is enabled. We leave the ACPI alone. - This driver controls the SMB Host only. - - This driver does not use interrupts. + 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 */ +/* + * Note: we assume there can only be one ALI1535/7101, with one SMBus interface + */ + #include <linux/module.h> @@ -66,86 +70,101 @@ #include "version.h" +#undef ALI_DEBUG -/* 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 */ +/* PCI Address Constants of the M7101 PMU */ +#define M7101_COM 0x04 /* M7101 COM, command register */ +#define M7101_RID 0x08 /* M7101 RID, revision ID register */ +#define M7101_CFG 0xD1 /* M7101 CFG_SET, configuration setting */ +#define M7101_SMBBASE 0xE2 /* M7101 SMB_BASE, SMB I/O base addres setting */ +#define M7101_SMBHST 0xF0 /* M7101 SMB_HOST, SMBus Host Configuration */ +#define M7101_SMBIDLE 0xF2 /* M7101 SMB_IDLE, SMBus Idle Time */ + +/* Default M7101 base address */ +#define SMB_SMB_DEFAULTBASE 0x8040 /* Toshiba and HP implement this elsewhere */ + +/* M7101 SMB IO register address offsets */ +#define SMB_STATUS (0 + ali1535_smba) /* SMB host controller status register */ +#define SMB_TYPE (1 + ali1535_smba) /* SMB command type register */ +#define SMB_SPORT (2 + ali1535_smba) /* Start Cycle Port & Alert Addr */ +#define SMB_ADD (3 + ali1535_smba) /* Dev addr register & count for block */ +#define SMB_DATA0 (4 + ali1535_smba) /* Dev DATA 0 register */ +#define SMB_DATA1 (5 + ali1535_smba) /* Dev DATA 1 register */ +#define SMB_BLKDAT (6 + ali1535_smba) /* Dev BLOCK DATA register */ +#define SMB_CMD (7 + ali1535_smba) /* SMB command register */ + +/* M7101 SMB_STATUS register */ +#define SMB_STS_ALERT 0x01 /* Alert-Response-Address cmd issued */ +#define SMB_STS_A_10_BITS 0x02 /* set when ALERT */ +#define SMB_STS_IDLE 0x04 /* host idle */ +#define SMB_STS_BUSY 0x08 /* host busy */ +#define SMB_STS_DONE 0x10 /* transaction complete */ +#define SMB_STS_DEV 0x20 /* device error */ +#define SMB_STS_BUSERR 0x40 /* bus error/collision */ +#define SMB_STS_FAIL 0x80 /* failed bus transaction */ +#define SMB_STS_ERR 0xe0 /* all the bad error bits */ +#define SMB_STS_RST 0xff /* all the bad error bits */ + +/* M7101 SMB_TYP command constants */ +#define SMB_TYP_KILL 0x04 /* Kill Command (write) */ +#define SMB_TYP_T_OUT_CMD 0x08 /* Time-out Command (write) */ +#define SMB_TYP_QUICK 0x00 /* Quick command */ +#define SMB_TYP_BYTE 0x10 /* Send/Rcv byte */ +#define SMB_TYP_BYTE_DATA 0x20 /* Write/Read byte */ +#define SMB_TYP_WORD_DATA 0x30 /* Write/Read word */ +#define SMB_TYP_BLOCK_DATA 0x40 /* Write/Read block */ +#define SMB_TYP_PROC_CALL 0x50 /* I2C Process call */ + + +/* M7101 SMB_TYPE read data */ +#define SMB_A_HIGH_BIT8 0x04 /* Bit 8 of 10-bit address in */ + /* Alert-Response-Address */ + /* (read) */ +#define SMB_A_HIGH_BIT9 0x08 /* Bit 9 of 10-bit address in */ + /* Alert-Response-Address */ + /* (read) */ +#define SMB_D_HI_MASK 0x03 /* Mask for isolating bits 9-8 */ + /* of 10-bit address in I2C */ + /* Read Command */ +#define SMB_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 */ +#define SMB_BLOCK_CLR 0x04 /* reset block data index */ /* ALI1535 device address register bits */ -#define ALI1535_RD_ADDR 0x01 /* Read/Write Bit in Device */ +#define SMB_RD_ADDR 0x01 /* Read/Write Bit in Device */ /* Address field */ /* -> Write = 0 */ /* -> Read = 1 */ -#define ALI1535_SMBIO_EN 0x04 /* SMB I/O Space enable */ +#define SMB_SMBIO_EN 0x04 /* SMB I/O Space enable */ + +/* Other settings */ +#define MAX_TIMEOUT 100 /* times 1/100 sec */ +#define SMB_IDLE_TIMEOUT 0x50 /* bus transaction idle timer */ + /* bits 7-5 base clock + * 000: 149K + * 001: 74K (default) + * 010: 31K *works best* + * 100: 223K + * 101: 111K + * 110: 55K + * bits 4-3 delay timer + * 00: base clock x 4 (default) + * 01: base clock x 2 + * 10: base clock x 8 *works best* + * 11: reserved + * bits 2-0 reserved + */ + +#define SMB_SMB_IOSIZE 32 static void ali1535_do_pause(unsigned int amount); static int ali1535_transaction(void); +static int ali1535_reset(void); 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. */ +/* Detect whether a M7101 can be found, and initialize it. */ int ali1535_setup(struct pci_dev *ALI1535_dev) { @@ -153,72 +172,50 @@ unsigned char temp; -/* 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)); + /* Determine the address of the SMBus area */ + pci_read_config_word(ALI1535_dev, M7101_SMBBASE, &ali1535_smba); + ali1535_smba &= (0xffff & ~(SMB_SMB_IOSIZE - 1)); if (ali1535_smba == 0) { - printk - ("i2c-ali1535.o: ALI1535_smb region uninitialized - upgrade BIOS?\n"); + printk("i2c-ali1535: M7101 SMBus region uninitialized\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; } + printk("i2c-ali1535: M7101 SMBus region at 0x%04x\n", ali1535_smba); - 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"); + /* + * Check the following things: + * - SMB I/O address is initialized + * - Device is enabled + * - We can use the addresses + */ + + /* SMB IO space enabled? */ + pci_read_config_byte(ALI1535_dev, M7101_CFG, &temp); + if (!(temp & SMB_SMBIO_EN)) { + printk("i2c-ali1535: M7101 SMBus device not enabled\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"); + /* SMB Host controller interface enabled? */ + pci_read_config_byte(ALI1535_dev, M7101_SMBHST, &temp); + if (!(temp & 1)) { + printk("i2c-ali1535: M7101 SMBus controller not enabled(0x%02x)\n", + temp); error_return = -ENODEV; goto END; } -/* set SMB clock to 74KHz as recommended in data sheet */ - pci_write_config_byte(ALI1535_dev, SMBCLK, 0x20); + /* set SMB idle timer */ + pci_write_config_byte(ALI1535_dev, M7101_SMBIDLE, SMB_IDLE_TIMEOUT); /* 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: + if(NULL == request_region(ali1535_smba, SMB_SMB_IOSIZE, "ali1535_smb")) { + printk("i2c-ali1535: failed to get region 0x%04x\n", ali1535_smba); + error_return = -ENODEV; + goto END; + } +END: return error_return; -} +} /* end ali1535_setup */ @@ -231,227 +228,201 @@ /* Another internally used function */ -int ali1535_transaction(void) +int ali1535_reset(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 +/* + * 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 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) + * 2. Reset the controller with an KILL command. + * (this doesn't seem to clear the controller if an external device + * is hung) + * Worst case, nothing seems to work except power reset. + */ /* get status */ - temp = inb_p(SMBHSTSTS); - - /* Make sure the SMBus host is ready to start transmitting */ + int status = inb(SMB_STATUS); + /* 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); + if (status & SMB_STS_BUSY) { + printk("i2c-ali1535: Issuing T_OUT to clear busy condition 0x%02x\n", status); + /* Abort/KILL - reset the host controller */ + outb(SMB_TYP_T_OUT_CMD, SMB_TYPE); + outb(SMB_STS_RST, SMB_STATUS); /* reset status */ + status = inb(SMB_STATUS); +#ifdef ALI_DEBUG + printk("i2c-ali1535: T_OUT results condition 0x%02x\n", status); #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); + /* + * Try resetting entire SMB bus, including other devices, this may not + * work either - it clears the BUSY bit but the BUSY bit may come back + * on when you try and use the chip again. + */ + if (status & SMB_STS_ERR) { + printk("i2c-ali1535: Issuing KILL to clear condition 0x%02x\n", status); + outb(SMB_TYP_KILL, SMB_TYPE); + outb(SMB_STS_RST, SMB_STATUS); /* reset status */ + status = inb(SMB_STATUS); +#ifdef ALI_DEBUG + printk("i2c-ali1535: KILL results condition 0x%02x\n", status); +#endif } -/* - } -*/ - /* 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); - } + /* + * 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. + */ + if (status & SMB_STS_ERR) { + printk ("i2c-ali1535: SMBus reset failed, controller or device " + "on bus is probably hung 0x%02x\n", status); + return -1; } + else return 0; +} + + +int ali1535_transaction(void) +{ +#ifdef ALI_DEBUG + printk("i2c-ali1535: transaction (pre): STS=%02x, TYP=%02x, PORT=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x, CMD=%02x\n", + inb(SMB_STATUS), inb(SMB_TYPE), inb(SMB_SPORT), inb(SMB_ADD), + inb(SMB_DATA0), inb(SMB_DATA1), inb(SMB_CMD)); +#endif + + int status, result, timeout = 0; + + status = inb(SMB_STATUS); /* start the transaction by writing anything to the start register */ - outb_p(0xFF, SMBHSTPORT); + if (status & SMB_STS_IDLE) + outb(0x01, SMB_SPORT); + else return -1; /* 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)); + status = inb(SMB_STATUS); +#ifdef ALI_DEBUG + printk("i2c-ali1535: transaction (DO): STS=%02x, TYP=%02x, PORT=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x, CMD=%02x\n", + inb(SMB_STATUS), inb(SMB_TYPE), inb(SMB_SPORT), inb(SMB_ADD), + inb(SMB_DATA0), inb(SMB_DATA1), inb(SMB_CMD)); +#endif + } while (((status & SMB_STS_BUSY) || !(status & SMB_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"); + printk("i2c-ali1535: SMBus Timeout! 0x%02x\n", status); + return -1; } - if (temp & ALI1535_STS_FAIL) { + if (status & SMB_STS_DONE) + outb(SMB_STS_RST, SMB_STATUS); + else if (status & SMB_STS_FAIL) + return -1; + /* + * 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. + */ + else if (status & SMB_STS_BUSERR) { + outb(SMB_STS_RST, SMB_STATUS); result = -1; -#ifdef DEBUG - printk("i2c-ali1535.o: Error: Failed bus transaction\n"); +#ifdef ALI_DEBUG + printk("i2c-ali1535: Error: no response or bus collision STS=%02x" + " ADD=%02x\n", inb(SMB_STATUS),inb(SMB_ADD)); #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) { + else if (status & SMB_STS_DEV) { + printk("i2c-ali1535: Error: device error STS=%02x\n", status); 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) { + else { + printk("i2c-ali1535: Error: command never completed, STS=%02x\n", status); 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)); +#ifdef ALI_DEBUG + printk("i2c-ali1535: transaction (post): STS=%02x, TYP=%02x, PORT=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x, CMD=%02x\n", + inb(SMB_STATUS), inb(SMB_TYPE), inb(SMB_SPORT), inb(SMB_ADD), + inb(SMB_DATA0), inb(SMB_DATA1), inb(SMB_CMD)); #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; -} +} /* end ali1535_transaction() */ -/* Return -1 on error. */ -s32 ali1535_access(struct i2c_adapter * adap, u16 addr, +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 size, union i2c_smbus_data* data) { - int i, len; - int temp; - int timeout; + int i, len, temp, timeout = 0; + u8 status = 0; 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); +#ifdef ALI_DEBUG + printk("i2c-ali1535: Access (pre): STS=%02x, TYP=%02x, PORT=%02x, " + "ADD=%02x, DAT0=%02x, DAT1=%02x, CMD=%02x\n", + inb(SMB_STATUS), inb(SMB_TYPE), inb(SMB_SPORT), inb(SMB_ADD), + inb(SMB_DATA0), inb(SMB_DATA1), inb(SMB_CMD)); +#endif + + /* clear status register */ + outb(SMB_STS_RST, SMB_STATUS); switch (size) { + case I2C_SMBUS_PROC_CALL: + printk("i2c-ali1535: 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; + //printk("i2c-ali1535: access: I2C_SMBUS_QUCK\n"); + outb(((addr & 0x7f) << 1) | (read_write & 0x01), SMB_ADD); + size = SMB_TYP_QUICK; + outb(size, SMB_TYPE); /* 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 */ + //printk("i2c-ali1535: access: I2C_SMBUS_BYTE\n"); + outb(((addr & 0x7f) << 1) | (read_write & 0x01), SMB_ADD); + size = SMB_TYP_BYTE; + outb(size, SMB_TYPE); /* output command */ if (read_write == I2C_SMBUS_WRITE) - outb_p(command, SMBHSTCMD); + outb(command, SMB_CMD); 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); + //printk("i2c-ali1535: access: I2C_SMBUS_BYTE_DATA\n"); + outb(((addr & 0x7f) << 1) | (read_write & 0x01), SMB_ADD); + size = SMB_TYP_BYTE_DATA; + outb(size, SMB_TYPE); /* output command */ + outb(command, SMB_CMD); if (read_write == I2C_SMBUS_WRITE) - outb_p(data->byte, SMBHSTDAT0); + outb(data->byte, SMB_DATA0); 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); + //printk("i2c-ali1535: access: I2C_SMBUS_WORD_DATA\n"); + outb(((addr & 0x7f) << 1) | (read_write & 0x01), SMB_ADD); + size = SMB_TYP_WORD_DATA; + outb(size, SMB_TYPE); /* output command */ + outb(command, SMB_CMD); if (read_write == I2C_SMBUS_WRITE) { - outb_p(data->word & 0xff, SMBHSTDAT0); - outb_p((data->word & 0xff00) >> 8, SMBHSTDAT1); + outb(data->word & 0xff, SMB_DATA0); + outb((data->word & 0xff00) >> 8, SMB_DATA1); } 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); + //printk("i2c-ali1535: access: I2C_SMBUS_BLOCK_DATA\n"); + outb(((addr & 0x7f) << 1) | (read_write & 0x01), SMB_ADD); + size = SMB_TYP_BLOCK_DATA; + outb(size, SMB_TYPE); /* output command */ + outb(command, SMB_CMD); if (read_write == I2C_SMBUS_WRITE) { len = data->block[0]; @@ -464,104 +435,105 @@ data->block[0] = len; } - outb_p(len, SMBHSTDAT0); - outb_p(inb_p(SMBHSTTYP) | ALI1535_BLOCK_CLR, SMBHSTTYP); /* Reset SMBBLKDAT */ + outb(len, SMB_DATA0); + //outb(inb(SMB_TYPE) | SMB_BLOCK_CLR, SMB_TYPE); /* Reset SMB_BLKDAT */ + outb(0x04, SMB_STATUS); /* Reset SMB_BLKDAT */ for (i = 1; i <= len; i++) - outb_p(data->block[i], SMBBLKDAT); + outb(data->block[i], SMB_BLKDAT); } break; - default: - printk - (KERN_WARNING "i2c-ali1535.o: Unsupported transaction %d\n", size); - result = -1; - goto EXIT; - } + } /* end switch */ - if (ali1535_transaction()) /* Error in transaction */ - { +#ifdef ALI_DEBUG + printk("i2c-ali1535: Access (pre-trans): STS=%02x, TYP=%02x, PORT=%02x," + " ADD=%02x, DAT0=%02x, DAT1=%02x, CMD=%02x\n", + inb(SMB_STATUS), inb(SMB_TYPE), inb(SMB_SPORT), inb(SMB_ADD), + inb(SMB_DATA0), inb(SMB_DATA1), inb(SMB_CMD)); +#endif + + if (ali1535_transaction() < 0) { /* Error in transaction */ + ali1535_reset(); result = -1; goto EXIT; - } + } - if ((read_write == I2C_SMBUS_WRITE) || (size == ALI1535_QUICK)) - { + if ((read_write == I2C_SMBUS_WRITE) || (size == SMB_TYP_QUICK)) { result = 0; goto EXIT; - } + } + +#ifdef ALI_DEBUG + printk("i2c-ali1535: Access (pre read): STS=%02x, TYP=%02x, PORT=%02x," + " ADD=%02x, DAT0=%02x, DAT1=%02x, CMD=%02x\n", + inb(SMB_STATUS), inb(SMB_TYPE), inb(SMB_SPORT), inb(SMB_ADD), + inb(SMB_DATA0), inb(SMB_DATA1), inb(SMB_CMD)); +#endif switch (size) { - case ALI1535_BYTE: /* Result put in SMBHSTDAT0 */ - data->byte = inb_p(SMBHSTDAT0); + case SMB_TYP_BYTE: /* Result put in SMB_DATA0 */ + data->byte = inb(SMB_DATA0); break; - case ALI1535_BYTE_DATA: - data->byte = inb_p(SMBHSTDAT0); + case SMB_TYP_BYTE_DATA: + data->byte = inb(SMB_DATA0); break; - case ALI1535_WORD_DATA: - data->word = inb_p(SMBHSTDAT0) + (inb_p(SMBHSTDAT1) << 8); + case SMB_TYP_WORD_DATA: + data->word = inb(SMB_DATA0) | (inb(SMB_DATA1) << 8); break; - case ALI1535_BLOCK_DATA: - len = inb_p(SMBHSTDAT0); + case SMB_TYP_BLOCK_DATA: + len = inb(SMB_DATA0); 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 */ - } + outb(0x04, SMB_STATUS); /* Reset SMB_BLKDAT */ + for (i = 1; i <= data->block[0]; i++) + data->block[i] = inb(SMB_BLKDAT); break; - } + } /* end switch */ +#ifdef ALI_DEBUG + printk("i2c-ali1535: Access (post read): STS=%02x, TYP=%02x, PORT=%02x," + " ADD=%02x, DAT0=%02x, DAT1=%02x, CMD=%02x\n", + inb(SMB_STATUS), inb(SMB_TYPE), inb(SMB_SPORT), inb(SMB_ADD), + inb(SMB_DATA0), inb(SMB_DATA1), inb(SMB_CMD)); +#endif + EXIT: + if(SMB_TYP_BLOCK_DATA == size || + I2C_SMBUS_BLOCK_DATA == size) + outb(0x04, SMB_STATUS); /* Reset SMB_BLKDAT */ up(&i2c_ali1535_sem); return result; } - 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; + I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_BLOCK_DATA; } static struct i2c_algorithm smbus_algorithm = { - .name = "Non-i2c SMBus adapter", - .id = I2C_ALGO_SMBUS, - .smbus_xfer = ali1535_access, - .functionality = ali1535_func, + .name = "Non-i2c SMBus adapter", + .id = I2C_ALGO_SMBUS, + .smbus_xfer = ali1535_access, + .functionality = ali1535_func, }; static struct i2c_adapter ali1535_adapter = { - .owner = THIS_MODULE, - .name = "unset", - .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI1535, - .algo = &smbus_algorithm, -}; - - -static struct pci_device_id ali1535_ids[] __devinitdata = { - { - .vendor = PCI_VENDOR_ID_AL, - .device = PCI_DEVICE_ID_AL_M7101, - .subvendor = PCI_ANY_ID, - .subdevice = PCI_ANY_ID, - }, - { 0, } + .owner = THIS_MODULE, + .name = "unset", + .id = I2C_ALGO_SMBUS | I2C_HW_SMBUS_ALI1535, + .algo = &smbus_algorithm, }; static int __devinit ali1535_probe(struct pci_dev *dev, const struct pci_device_id *id) { - if (ali1535_setup(dev)) { - printk - ("i2c-ali1535.o: ALI1535 not detected, module not inserted.\n"); - return -ENODEV; - } - - sprintf(ali1535_adapter.name, "SMBus ALI1535 adapter at %04x", - ali1535_smba); - return i2c_add_adapter(&ali1535_adapter); + if (0 != ali1535_setup(dev)) { + printk + ("i2c-ali1535: ALi7101 not detected, module not inserted.\n"); + return -ENODEV; + } + + sprintf(ali1535_adapter.name, "ALi7101 SMBus adapter at 0x%04x", + ali1535_smba); + return i2c_add_adapter(&ali1535_adapter); } @@ -571,7 +543,16 @@ } +static struct pci_device_id ali1535_ids[] __devinitdata = { + { + .vendor = PCI_VENDOR_ID_AL, + .device = PCI_DEVICE_ID_AL_M7101, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + }, + { 0, } +}; static struct pci_driver ali1535_driver = { - .name = "ali1535 smbus", + .name = "ALi7101 SMBus", .id_table = ali1535_ids, .probe = ali1535_probe, @@ -581,13 +562,12 @@ static int __init i2c_ali1535_init(void) { - printk("i2c-ali1535.o version %s (%s)\n", LM_VERSION, LM_DATE); + printk("i2c-ali1535: version %s (%s)\n", LM_VERSION, LM_DATE); return pci_module_init(&ali1535_driver); } - static void __exit i2c_ali1535_exit(void) { + release_region(ali1535_smba, SMB_SMB_IOSIZE); pci_unregister_driver(&ali1535_driver); - release_region(ali1535_smba, ALI1535_SMB_IOSIZE); } @@ -601,5 +581,5 @@ ("Frodo Looijaard <frodol at dds.nl>, Philip Edelbrock <phil at netroedge.com>, " "Mark D. Studebaker <mdsxyz123 at yahoo.com> and Dan Eaton <dan.eaton at rocketlogix.com>"); -MODULE_DESCRIPTION("ALI1535 SMBus driver"); +MODULE_DESCRIPTION("ALI1535/M7101 SMBus driver"); MODULE_LICENSE("GPL");