diff options
29 files changed, 487 insertions, 366 deletions
diff --git a/Documentation/i2c/busses/i2c-i801 b/Documentation/i2c/busses/i2c-i801 index 71f55bb..615142d 100644 --- a/Documentation/i2c/busses/i2c-i801 +++ b/Documentation/i2c/busses/i2c-i801 @@ -38,9 +38,10 @@ Module Parameters Disable selected features normally supported by the device. This makes it possible to work around possible driver or hardware bugs if the feature in question doesn't work as intended for whatever reason. Bit values: - 1 disable SMBus PEC - 2 disable the block buffer - 8 disable the I2C block read functionality + 0x01 disable SMBus PEC + 0x02 disable the block buffer + 0x08 disable the I2C block read functionality + 0x10 don't use interrupts Description @@ -86,6 +87,12 @@ SMBus 2.0 Support The 82801DB (ICH4) and later chips support several SMBus 2.0 features. +Interrupt Support +----------------- + +PCI interrupt support is supported on the 82801EB (ICH5) and later chips. + + Hidden ICH SMBus ---------------- diff --git a/Documentation/i2c/busses/i2c-piix4 b/Documentation/i2c/busses/i2c-piix4 index 475bb4a..1e6634f 100644 --- a/Documentation/i2c/busses/i2c-piix4 +++ b/Documentation/i2c/busses/i2c-piix4 @@ -8,6 +8,11 @@ Supported adapters: Datasheet: Only available via NDA from ServerWorks * ATI IXP200, IXP300, IXP400, SB600, SB700 and SB800 southbridges Datasheet: Not publicly available + SB700 register reference available at: + http://support.amd.com/us/Embedded_TechDocs/43009_sb7xx_rrg_pub_1.00.pdf + * AMD SP5100 (SB700 derivative found on some server mainboards) + Datasheet: Publicly available at the AMD website + http://support.amd.com/us/Embedded_TechDocs/44413.pdf * AMD Hudson-2 Datasheet: Not publicly available * Standard Microsystems (SMSC) SLC90E66 (Victory66) southbridge @@ -68,6 +73,10 @@ this driver on those mainboards. The ServerWorks Southbridges, the Intel 440MX, and the Victory66 are identical to the PIIX4 in I2C/SMBus support. +The AMD SB700 and SP5100 chipsets implement two PIIX4-compatible SMBus +controllers. If your BIOS initializes the secondary controller, it will +be detected by this driver as an "Auxiliary SMBus Host Controller". + If you own Force CPCI735 motherboard or other OSB4 based systems you may need to change the SMBus Interrupt Select register so the SMBus controller uses the SMI mode. diff --git a/Documentation/i2c/writing-clients b/Documentation/i2c/writing-clients index 5aa5337..3a94b0e 100644 --- a/Documentation/i2c/writing-clients +++ b/Documentation/i2c/writing-clients @@ -245,21 +245,17 @@ static int __init foo_init(void) { return i2c_add_driver(&foo_driver); } +module_init(foo_init); static void __exit foo_cleanup(void) { i2c_del_driver(&foo_driver); } +module_exit(foo_cleanup); -/* Substitute your own name and email address */ -MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>" -MODULE_DESCRIPTION("Driver for Barf Inc. Foo I2C devices"); - -/* a few non-GPL license types are also allowed */ -MODULE_LICENSE("GPL"); +The module_i2c_driver() macro can be used to reduce above code. -module_init(foo_init); -module_exit(foo_cleanup); +module_i2c_driver(foo_driver); Note that some functions are marked by `__init'. These functions can be removed after kernel booting (or module loading) is completed. @@ -267,6 +263,17 @@ Likewise, functions marked by `__exit' are dropped by the compiler when the code is built into the kernel, as they would never be called. +Driver Information +================== + +/* Substitute your own name and email address */ +MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>" +MODULE_DESCRIPTION("Driver for Barf Inc. Foo I2C devices"); + +/* a few non-GPL license types are also allowed */ +MODULE_LICENSE("GPL"); + + Power Management ================ diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 7244c8b..2e7530a 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -133,7 +133,7 @@ config I2C_PIIX4 ATI IXP300 ATI IXP400 ATI SB600 - ATI SB700 + ATI SB700/SP5100 ATI SB800 AMD Hudson-2 Serverworks OSB4 @@ -143,6 +143,10 @@ config I2C_PIIX4 Serverworks HT-1100 SMSC Victory66 + Some AMD chipsets contain two PIIX4-compatible SMBus + controllers. This driver will attempt to use both controllers + on the SB700/SP5100, if they have been initialized by the BIOS. + This driver can also be built as a module. If so, the module will be called i2c-piix4. diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c index e66d248..125cd8e 100644 --- a/drivers/i2c/busses/i2c-ali1535.c +++ b/drivers/i2c/busses/i2c-ali1535.c @@ -531,15 +531,7 @@ static struct pci_driver ali1535_driver = { .remove = __devexit_p(ali1535_remove), }; -static int __init i2c_ali1535_init(void) -{ - return pci_register_driver(&ali1535_driver); -} - -static void __exit i2c_ali1535_exit(void) -{ - pci_unregister_driver(&ali1535_driver); -} +module_pci_driver(ali1535_driver); MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, " "Philip Edelbrock <phil@netroedge.com>, " @@ -547,6 +539,3 @@ MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>, " "and Dan Eaton <dan.eaton@rocketlogix.com>"); MODULE_DESCRIPTION("ALI1535 SMBus driver"); MODULE_LICENSE("GPL"); - -module_init(i2c_ali1535_init); -module_exit(i2c_ali1535_exit); diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c index 47ae009..e02d9f8 100644 --- a/drivers/i2c/busses/i2c-ali1563.c +++ b/drivers/i2c/busses/i2c-ali1563.c @@ -431,18 +431,6 @@ static struct pci_driver ali1563_pci_driver = { .remove = __devexit_p(ali1563_remove), }; -static int __init ali1563_init(void) -{ - return pci_register_driver(&ali1563_pci_driver); -} - -module_init(ali1563_init); - -static void __exit ali1563_exit(void) -{ - pci_unregister_driver(&ali1563_pci_driver); -} - -module_exit(ali1563_exit); +module_pci_driver(ali1563_pci_driver); MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c index 087ea9c..ce8d26d 100644 --- a/drivers/i2c/busses/i2c-ali15x3.c +++ b/drivers/i2c/busses/i2c-ali15x3.c @@ -513,21 +513,10 @@ static struct pci_driver ali15x3_driver = { .remove = __devexit_p(ali15x3_remove), }; -static int __init i2c_ali15x3_init(void) -{ - return pci_register_driver(&ali15x3_driver); -} - -static void __exit i2c_ali15x3_exit(void) -{ - pci_unregister_driver(&ali15x3_driver); -} +module_pci_driver(ali15x3_driver); MODULE_AUTHOR ("Frodo Looijaard <frodol@dds.nl>, " "Philip Edelbrock <phil@netroedge.com>, " "and Mark D. Studebaker <mdsxyz123@yahoo.com>"); MODULE_DESCRIPTION("ALI15X3 SMBus driver"); MODULE_LICENSE("GPL"); - -module_init(i2c_ali15x3_init); -module_exit(i2c_ali15x3_exit); diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index eb778bf1..304aa03 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -410,21 +410,10 @@ static struct pci_driver amd756_driver = { .remove = __devexit_p(amd756_remove), }; -static int __init amd756_init(void) -{ - return pci_register_driver(&amd756_driver); -} - -static void __exit amd756_exit(void) -{ - pci_unregister_driver(&amd756_driver); -} +module_pci_driver(amd756_driver); MODULE_AUTHOR("Merlin Hughes <merlin@merlin.org>"); MODULE_DESCRIPTION("AMD756/766/768/8111 and nVidia nForce SMBus driver"); MODULE_LICENSE("GPL"); EXPORT_SYMBOL(amd756_smbus); - -module_init(amd756_init) -module_exit(amd756_exit) diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c index e5ac53b..0919ac1 100644 --- a/drivers/i2c/busses/i2c-amd8111.c +++ b/drivers/i2c/busses/i2c-amd8111.c @@ -491,15 +491,4 @@ static struct pci_driver amd8111_driver = { .remove = __devexit_p(amd8111_remove), }; -static int __init i2c_amd8111_init(void) -{ - return pci_register_driver(&amd8111_driver); -} - -static void __exit i2c_amd8111_exit(void) -{ - pci_unregister_driver(&amd8111_driver); -} - -module_init(i2c_amd8111_init); -module_exit(i2c_amd8111_exit); +module_pci_driver(amd8111_driver); diff --git a/drivers/i2c/busses/i2c-designware-pcidrv.c b/drivers/i2c/busses/i2c-designware-pcidrv.c index 00e8f21..92a1e2c 100644 --- a/drivers/i2c/busses/i2c-designware-pcidrv.c +++ b/drivers/i2c/busses/i2c-designware-pcidrv.c @@ -374,17 +374,7 @@ static struct pci_driver dw_i2c_driver = { }, }; -static int __init dw_i2c_init_driver(void) -{ - return pci_register_driver(&dw_i2c_driver); -} -module_init(dw_i2c_init_driver); - -static void __exit dw_i2c_exit_driver(void) -{ - pci_unregister_driver(&dw_i2c_driver); -} -module_exit(dw_i2c_exit_driver); +module_pci_driver(dw_i2c_driver); MODULE_AUTHOR("Baruch Siach <baruch@tkos.co.il>"); MODULE_DESCRIPTION("Synopsys DesignWare PCI I2C bus adapter"); diff --git a/drivers/i2c/busses/i2c-diolan-u2c.c b/drivers/i2c/busses/i2c-diolan-u2c.c index 7eb19a5..aedb94f 100644 --- a/drivers/i2c/busses/i2c-diolan-u2c.c +++ b/drivers/i2c/busses/i2c-diolan-u2c.c @@ -517,6 +517,6 @@ static struct usb_driver diolan_u2c_driver = { module_usb_driver(diolan_u2c_driver); -MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>"); +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION(DRIVER_NAME " driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-eg20t.c b/drivers/i2c/busses/i2c-eg20t.c index 2f74ae8..259f769 100644 --- a/drivers/i2c/busses/i2c-eg20t.c +++ b/drivers/i2c/busses/i2c-eg20t.c @@ -953,17 +953,7 @@ static struct pci_driver pch_pcidriver = { .resume = pch_i2c_resume }; -static int __init pch_pci_init(void) -{ - return pci_register_driver(&pch_pcidriver); -} -module_init(pch_pci_init); - -static void __exit pch_pci_exit(void) -{ - pci_unregister_driver(&pch_pcidriver); -} -module_exit(pch_pci_exit); +module_pci_driver(pch_pcidriver); MODULE_DESCRIPTION("Intel EG20T PCH/LAPIS Semico ML7213/ML7223/ML7831 IOH I2C"); MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c index c527de1..c9f95e1 100644 --- a/drivers/i2c/busses/i2c-hydra.c +++ b/drivers/i2c/busses/i2c-hydra.c @@ -156,23 +156,8 @@ static struct pci_driver hydra_driver = { .remove = __devexit_p(hydra_remove), }; -static int __init i2c_hydra_init(void) -{ - return pci_register_driver(&hydra_driver); -} - - -static void __exit i2c_hydra_exit(void) -{ - pci_unregister_driver(&hydra_driver); -} - - +module_pci_driver(hydra_driver); MODULE_AUTHOR("Geert Uytterhoeven <geert@linux-m68k.org>"); MODULE_DESCRIPTION("i2c for Apple Hydra Mac I/O"); MODULE_LICENSE("GPL"); - -module_init(i2c_hydra_init); -module_exit(i2c_hydra_exit); - diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index ae2945a..898dcf9 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -60,10 +60,12 @@ Block process call transaction no I2C block read transaction yes (doesn't use the block buffer) Slave mode no + Interrupt processing yes See the file Documentation/i2c/busses/i2c-i801 for details. */ +#include <linux/interrupt.h> #include <linux/module.h> #include <linux/pci.h> #include <linux/kernel.h> @@ -76,6 +78,7 @@ #include <linux/io.h> #include <linux/dmi.h> #include <linux/slab.h> +#include <linux/wait.h> /* I801 SMBus address offsets */ #define SMBHSTSTS(p) (0 + (p)->smba) @@ -91,8 +94,12 @@ /* PCI Address Constants */ #define SMBBAR 4 +#define SMBPCISTS 0x006 #define SMBHSTCFG 0x040 +/* Host status bits for SMBPCISTS */ +#define SMBPCISTS_INTS 0x08 + /* Host configuration bits for SMBHSTCFG */ #define SMBHSTCFG_HST_EN 1 #define SMBHSTCFG_SMB_SMI_EN 2 @@ -102,12 +109,8 @@ #define SMBAUXCTL_CRC 1 #define SMBAUXCTL_E32B 2 -/* kill bit for SMBHSTCNT */ -#define SMBHSTCNT_KILL 2 - /* Other settings */ #define MAX_RETRIES 400 -#define ENABLE_INT9 0 /* set to 0x01 to enable - untested */ /* I801 command constants */ #define I801_QUICK 0x00 @@ -117,10 +120,13 @@ #define I801_PROC_CALL 0x10 /* unimplemented */ #define I801_BLOCK_DATA 0x14 #define I801_I2C_BLOCK_DATA 0x18 /* ICH5 and later */ -#define I801_BLOCK_LAST 0x34 -#define I801_I2C_BLOCK_LAST 0x38 /* ICH5 and later */ -#define I801_START 0x40 -#define I801_PEC_EN 0x80 /* ICH3 and later */ + +/* I801 Host Control register bits */ +#define SMBHSTCNT_INTREN 0x01 +#define SMBHSTCNT_KILL 0x02 +#define SMBHSTCNT_LAST_BYTE 0x20 +#define SMBHSTCNT_START 0x40 +#define SMBHSTCNT_PEC_EN 0x80 /* ICH3 and later */ /* I801 Hosts Status register bits */ #define SMBHSTSTS_BYTE_DONE 0x80 @@ -132,9 +138,11 @@ #define SMBHSTSTS_INTR 0x02 #define SMBHSTSTS_HOST_BUSY 0x01 -#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_FAILED | \ - SMBHSTSTS_BUS_ERR | SMBHSTSTS_DEV_ERR | \ - SMBHSTSTS_INTR) +#define STATUS_ERROR_FLAGS (SMBHSTSTS_FAILED | SMBHSTSTS_BUS_ERR | \ + SMBHSTSTS_DEV_ERR) + +#define STATUS_FLAGS (SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR | \ + STATUS_ERROR_FLAGS) /* Older devices have their ID defined in <linux/pci_ids.h> */ #define PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS 0x1c22 @@ -154,6 +162,17 @@ struct i801_priv { unsigned char original_hstcfg; struct pci_dev *pci_dev; unsigned int features; + + /* isr processing */ + wait_queue_head_t waitq; + u8 status; + + /* Command state used by isr for byte-by-byte block transactions */ + u8 cmd; + bool is_read; + int count; + int len; + u8 *data; }; static struct pci_driver i801_driver; @@ -162,6 +181,7 @@ static struct pci_driver i801_driver; #define FEATURE_BLOCK_BUFFER (1 << 1) #define FEATURE_BLOCK_PROC (1 << 2) #define FEATURE_I2C_BLOCK_READ (1 << 3) +#define FEATURE_IRQ (1 << 4) /* Not really a feature, but it's convenient to handle it as such */ #define FEATURE_IDF (1 << 15) @@ -170,6 +190,7 @@ static const char *i801_feature_names[] = { "Block buffer", "Block process call", "I2C block read", + "Interrupt", }; static unsigned int disable_features; @@ -205,13 +226,22 @@ static int i801_check_pre(struct i801_priv *priv) return 0; } -/* Convert the status register to an error code, and clear it. */ -static int i801_check_post(struct i801_priv *priv, int status, int timeout) +/* + * Convert the status register to an error code, and clear it. + * Note that status only contains the bits we want to clear, not the + * actual register value. + */ +static int i801_check_post(struct i801_priv *priv, int status) { int result = 0; - /* If the SMBus is still busy, we give up */ - if (timeout) { + /* + * If the SMBus is still busy, we give up + * Note: This timeout condition only happens when using polling + * transactions. For interrupt operation, NAK/timeout is indicated by + * DEV_ERR. + */ + if (unlikely(status < 0)) { dev_err(&priv->pci_dev->dev, "Transaction timeout\n"); /* try to stop the current command */ dev_dbg(&priv->pci_dev->dev, "Terminating the current operation\n"); @@ -244,64 +274,76 @@ static int i801_check_post(struct i801_priv *priv, int status, int timeout) dev_dbg(&priv->pci_dev->dev, "Lost arbitration\n"); } - if (result) { - /* Clear error flags */ - outb_p(status & STATUS_FLAGS, SMBHSTSTS(priv)); - status = inb_p(SMBHSTSTS(priv)) & STATUS_FLAGS; - if (status) { - dev_warn(&priv->pci_dev->dev, "Failed clearing status " - "flags at end of transaction (%02x)\n", - status); - } - } + /* Clear status flags except BYTE_DONE, to be cleared by caller */ + outb_p(status, SMBHSTSTS(priv)); return result; } -static int i801_transaction(struct i801_priv *priv, int xact) +/* Wait for BUSY being cleared and either INTR or an error flag being set */ +static int i801_wait_intr(struct i801_priv *priv) { - int status; - int result; int timeout = 0; - - result = i801_check_pre(priv); - if (result < 0) - return result; - - /* the current contents of SMBHSTCNT can be overwritten, since PEC, - * INTREN, SMBSCMD are passed in xact */ - outb_p(xact | I801_START, SMBHSTCNT(priv)); + int status; /* We will always wait for a fraction of a second! */ do { usleep_range(250, 500); status = inb_p(SMBHSTSTS(priv)); - } while ((status & SMBHSTSTS_HOST_BUSY) && (timeout++ < MAX_RETRIES)); + } while (((status & SMBHSTSTS_HOST_BUSY) || + !(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR))) && + (timeout++ < MAX_RETRIES)); - result = i801_check_post(priv, status, timeout > MAX_RETRIES); - if (result < 0) - return result; - - outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv)); - return 0; + if (timeout > MAX_RETRIES) { + dev_dbg(&priv->pci_dev->dev, "INTR Timeout!\n"); + return -ETIMEDOUT; + } + return status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR); } -/* wait for INTR bit as advised by Intel */ -static void i801_wait_hwpec(struct i801_priv *priv) +/* Wait for either BYTE_DONE or an error flag being set */ +static int i801_wait_byte_done(struct i801_priv *priv) { int timeout = 0; int status; + /* We will always wait for a fraction of a second! */ do { usleep_range(250, 500); status = inb_p(SMBHSTSTS(priv)); - } while ((!(status & SMBHSTSTS_INTR)) - && (timeout++ < MAX_RETRIES)); + } while (!(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) && + (timeout++ < MAX_RETRIES)); - if (timeout > MAX_RETRIES) - dev_dbg(&priv->pci_dev->dev, "PEC Timeout!\n"); + if (timeout > MAX_RETRIES) { + dev_dbg(&priv->pci_dev->dev, "BYTE_DONE Timeout!\n"); + return -ETIMEDOUT; + } + return status & STATUS_ERROR_FLAGS; +} - outb_p(status, SMBHSTSTS(priv)); +static int i801_transaction(struct i801_priv *priv, int xact) +{ + int status; + int result; + + result = i801_check_pre(priv); + if (result < 0) + return result; + + if (priv->features & FEATURE_IRQ) { + outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START, + SMBHSTCNT(priv)); + wait_event(priv->waitq, (status = priv->status)); + priv->status = 0; + return i801_check_post(priv, status); + } + + /* the current contents of SMBHSTCNT can be overwritten, since PEC, + * SMBSCMD are passed in xact */ + outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv)); + + status = i801_wait_intr(priv); + return i801_check_post(priv, status); } static int i801_block_transaction_by_block(struct i801_priv *priv, @@ -321,8 +363,8 @@ static int i801_block_transaction_by_block(struct i801_priv *priv, outb_p(data->block[i+1], SMBBLKDAT(priv)); } - status = i801_transaction(priv, I801_BLOCK_DATA | ENABLE_INT9 | - I801_PEC_EN * hwpec); + status = i801_transaction(priv, I801_BLOCK_DATA | + (hwpec ? SMBHSTCNT_PEC_EN : 0)); if (status) return status; @@ -338,6 +380,98 @@ static int i801_block_transaction_by_block(struct i801_priv *priv, return 0; } +static void i801_isr_byte_done(struct i801_priv *priv) +{ + if (priv->is_read) { + /* For SMBus block reads, length is received with first byte */ + if (((priv->cmd & 0x1c) == I801_BLOCK_DATA) && + (priv->count == 0)) { + priv->len = inb_p(SMBHSTDAT0(priv)); + if (priv->len < 1 || priv->len > I2C_SMBUS_BLOCK_MAX) { + dev_err(&priv->pci_dev->dev, + "Illegal SMBus block read size %d\n", + priv->len); + /* FIXME: Recover */ + priv->len = I2C_SMBUS_BLOCK_MAX; + } else { + dev_dbg(&priv->pci_dev->dev, + "SMBus block read size is %d\n", + priv->len); + } + priv->data[-1] = priv->len; + } + + /* Read next byte */ + if (priv->count < priv->len) + priv->data[priv->count++] = inb(SMBBLKDAT(priv)); + else + dev_dbg(&priv->pci_dev->dev, + "Discarding extra byte on block read\n"); + + /* Set LAST_BYTE for last byte of read transaction */ + if (priv->count == priv->len - 1) + outb_p(priv->cmd | SMBHSTCNT_LAST_BYTE, + SMBHSTCNT(priv)); + } else if (priv->count < priv->len - 1) { + /* Write next byte, except for IRQ after last byte */ + outb_p(priv->data[++priv->count], SMBBLKDAT(priv)); + } + + /* Clear BYTE_DONE to continue with next byte */ + outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); +} + +/* + * There are two kinds of interrupts: + * + * 1) i801 signals transaction completion with one of these interrupts: + * INTR - Success + * DEV_ERR - Invalid command, NAK or communication timeout + * BUS_ERR - SMI# transaction collision + * FAILED - transaction was canceled due to a KILL request + * When any of these occur, update ->status and wake up the waitq. + * ->status must be cleared before kicking off the next transaction. + * + * 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt + * occurs for each byte of a byte-by-byte to prepare the next byte. + */ +static irqreturn_t i801_isr(int irq, void *dev_id) +{ + struct i801_priv *priv = dev_id; + u16 pcists; + u8 status; + + /* Confirm this is our interrupt */ + pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists); + if (!(pcists & SMBPCISTS_INTS)) + return IRQ_NONE; + + status = inb_p(SMBHSTSTS(priv)); + if (status != 0x42) + dev_dbg(&priv->pci_dev->dev, "irq: status = %02x\n", status); + + if (status & SMBHSTSTS_BYTE_DONE) + i801_isr_byte_done(priv); + + /* + * Clear irq sources and report transaction result. + * ->status must be cleared before the next transaction is started. + */ + status &= SMBHSTSTS_INTR | STATUS_ERROR_FLAGS; + if (status) { + outb_p(status, SMBHSTSTS(priv)); + priv->status |= status; + wake_up(&priv->waitq); + } + + return IRQ_HANDLED; +} + +/* + * For "byte-by-byte" block transactions: + * I2C write uses cmd=I801_BLOCK_DATA, I2C_EN=1 + * I2C read uses cmd=I801_I2C_BLOCK_DATA + */ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, union i2c_smbus_data *data, char read_write, int command, @@ -347,7 +481,6 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, int smbcmd; int status; int result; - int timeout; result = i801_check_pre(priv); if (result < 0) @@ -360,36 +493,39 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, outb_p(data->block[1], SMBBLKDAT(priv)); } + if (command == I2C_SMBUS_I2C_BLOCK_DATA && + read_write == I2C_SMBUS_READ) + smbcmd = I801_I2C_BLOCK_DATA; + else + smbcmd = I801_BLOCK_DATA; + + if (priv->features & FEATURE_IRQ) { + priv->is_read = (read_write == I2C_SMBUS_READ); + if (len == 1 && priv->is_read) + smbcmd |= SMBHSTCNT_LAST_BYTE; + priv->cmd = smbcmd | SMBHSTCNT_INTREN; + priv->len = len; + priv->count = 0; + priv->data = &data->block[1]; + + outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv)); + wait_event(priv->waitq, (status = priv->status)); + priv->status = 0; + return i801_check_post(priv, status); + } + for (i = 1; i <= len; i++) { - if (i == len && read_write == I2C_SMBUS_READ) { - if (command == I2C_SMBUS_I2C_BLOCK_DATA) - smbcmd = I801_I2C_BLOCK_LAST; - else - smbcmd = I801_BLOCK_LAST; - } else { - if (command == I2C_SMBUS_I2C_BLOCK_DATA - && read_write == I2C_SMBUS_READ) - smbcmd = I801_I2C_BLOCK_DATA; - else - smbcmd = I801_BLOCK_DATA; - } - outb_p(smbcmd | ENABLE_INT9, SMBHSTCNT(priv)); + if (i == len && read_write == I2C_SMBUS_READ) + smbcmd |= SMBHSTCNT_LAST_BYTE; + outb_p(smbcmd, SMBHSTCNT(priv)); if (i == 1) - outb_p(inb(SMBHSTCNT(priv)) | I801_START, + outb_p(inb(SMBHSTCNT(priv)) | SMBHSTCNT_START, SMBHSTCNT(priv)); - /* We will always wait for a fraction of a second! */ - timeout = 0; - do { - usleep_range(250, 500); - status = inb_p(SMBHSTSTS(priv)); - } while ((!(status & SMBHSTSTS_BYTE_DONE)) - && (timeout++ < MAX_RETRIES)); - - result = i801_check_post(priv, status, timeout > MAX_RETRIES); - if (result < 0) - return result; + status = i801_wait_byte_done(priv); + if (status) + goto exit; if (i == 1 && read_write == I2C_SMBUS_READ && command != I2C_SMBUS_I2C_BLOCK_DATA) { @@ -416,10 +552,12 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv, outb_p(data->block[i+1], SMBBLKDAT(priv)); /* signals SMBBLKDAT ready */ - outb_p(SMBHSTSTS_BYTE_DONE | SMBHSTSTS_INTR, SMBHSTSTS(priv)); + outb_p(SMBHSTSTS_BYTE_DONE, SMBHSTSTS(priv)); } - return 0; + status = i801_wait_intr(priv); +exit: + return i801_check_post(priv, status); } static int i801_set_block_buffer_mode(struct i801_priv *priv) @@ -474,9 +612,6 @@ static int i801_block_transaction(struct i801_priv *priv, read_write, command, hwpec); - if (result == 0 && hwpec) - i801_wait_hwpec(priv); - if (command == I2C_SMBUS_I2C_BLOCK_DATA && read_write == I2C_SMBUS_WRITE) { /* restore saved configuration register value */ @@ -564,7 +699,7 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr, ret = i801_block_transaction(priv, data, read_write, size, hwpec); else - ret = i801_transaction(priv, xact | ENABLE_INT9); + ret = i801_transaction(priv, xact); /* Some BIOSes don't like it when PEC is enabled at reboot or resume time, so we forcibly disable it after every transaction. Turn off @@ -799,6 +934,16 @@ static int __devinit i801_probe(struct pci_dev *dev, break; } + /* IRQ processing tested on CougarPoint PCH, ICH5, ICH7-M and ICH10 */ + if (dev->device == PCI_DEVICE_ID_INTEL_COUGARPOINT_SMBUS || + dev->device == PCI_DEVICE_ID_INTEL_82801EB_3 || + dev->device == PCI_DEVICE_ID_INTEL_ICH7_17 || + dev->device == PCI_DEVICE_ID_INTEL_ICH8_5 || + dev->device == PCI_DEVICE_ID_INTEL_ICH9_6 || + dev->device == PCI_DEVICE_ID_INTEL_ICH10_4 || + dev->device == PCI_DEVICE_ID_INTEL_ICH10_5) + priv->features |= FEATURE_IRQ; + /* Disable features on user request */ for (i = 0; i < ARRAY_SIZE(i801_feature_names); i++) { if (priv->features & disable_features & (1 << i)) @@ -846,16 +991,30 @@ static int __devinit i801_probe(struct pci_dev *dev, } pci_write_config_byte(priv->pci_dev, SMBHSTCFG, temp); - if (temp & SMBHSTCFG_SMB_SMI_EN) + if (temp & SMBHSTCFG_SMB_SMI_EN) { dev_dbg(&dev->dev, "SMBus using interrupt SMI#\n"); - else - dev_dbg(&dev->dev, "SMBus using PCI Interrupt\n"); + /* Disable SMBus interrupt feature if SMBus using SMI# */ + priv->features &= ~FEATURE_IRQ; + } /* Clear special mode bits */ if (priv->features & (FEATURE_SMBUS_PEC | FEATURE_BLOCK_BUFFER)) outb_p(inb_p(SMBAUXCTL(priv)) & ~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv)); + if (priv->features & FEATURE_IRQ) { + init_waitqueue_head(&priv->waitq); + + err = request_irq(dev->irq, i801_isr, IRQF_SHARED, + i801_driver.name, priv); + if (err) { + dev_err(&dev->dev, "Failed to allocate irq %d: %d\n", + dev->irq, err); + goto exit_release; + } + dev_info(&dev->dev, "SMBus using PCI Interrupt\n"); + } + /* set up the sysfs linkage to our parent device */ priv->adapter.dev.parent = &dev->dev; @@ -867,14 +1026,18 @@ static int __devinit i801_probe(struct pci_dev *dev, err = i2c_add_adapter(&priv->adapter); if (err) { dev_err(&dev->dev, "Failed to add SMBus adapter\n"); - goto exit_release; + goto exit_free_irq; } i801_probe_optional_slaves(priv); pci_set_drvdata(dev, priv); + return 0; +exit_free_irq: + if (priv->features & FEATURE_IRQ) + free_irq(dev->irq, priv); exit_release: pci_release_region(dev, SMBBAR); exit: @@ -888,7 +1051,11 @@ static void __devexit i801_remove(struct pci_dev *dev) i2c_del_adapter(&priv->adapter); pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg); + + if (priv->features & FEATURE_IRQ) + free_irq(dev->irq, priv); pci_release_region(dev, SMBBAR); + pci_set_drvdata(dev, NULL); kfree(priv); /* diff --git a/drivers/i2c/busses/i2c-intel-mid.c b/drivers/i2c/busses/i2c-intel-mid.c index 365bad5..7c28f10 100644 --- a/drivers/i2c/busses/i2c-intel-mid.c +++ b/drivers/i2c/busses/i2c-intel-mid.c @@ -1116,18 +1116,7 @@ static struct pci_driver intel_mid_i2c_driver = { .remove = __devexit_p(intel_mid_i2c_remove), }; -static int __init intel_mid_i2c_init(void) -{ - return pci_register_driver(&intel_mid_i2c_driver); -} - -static void __exit intel_mid_i2c_exit(void) -{ - pci_unregister_driver(&intel_mid_i2c_driver); -} - -module_init(intel_mid_i2c_init); -module_exit(intel_mid_i2c_exit); +module_pci_driver(intel_mid_i2c_driver); MODULE_AUTHOR("Ba Zheng <zheng.ba@intel.com>"); MODULE_DESCRIPTION("I2C driver for Moorestown Platform"); diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 43a96a1..392303b 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -453,16 +453,4 @@ static struct pci_driver nforce2_driver = { .remove = __devexit_p(nforce2_remove), }; -static int __init nforce2_init(void) -{ - return pci_register_driver(&nforce2_driver); -} - -static void __exit nforce2_exit(void) -{ - pci_unregister_driver(&nforce2_driver); -} - -module_init(nforce2_init); -module_exit(nforce2_exit); - +module_pci_driver(nforce2_driver); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index 801df60..c214833 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -545,6 +545,8 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap, if (dev->speed > 400) w |= OMAP_I2C_CON_OPMODE_HS; + if (msg->flags & I2C_M_STOP) + stop = 1; if (msg->flags & I2C_M_TEN) w |= OMAP_I2C_CON_XA; if (!(msg->flags & I2C_M_RD)) @@ -658,7 +660,8 @@ out: static u32 omap_i2c_func(struct i2c_adapter *adap) { - return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | + I2C_FUNC_PROTOCOL_MANGLING; } static inline void diff --git a/drivers/i2c/busses/i2c-pasemi.c b/drivers/i2c/busses/i2c-pasemi.c index eaaea73..12edefd 100644 --- a/drivers/i2c/busses/i2c-pasemi.c +++ b/drivers/i2c/busses/i2c-pasemi.c @@ -415,19 +415,8 @@ static struct pci_driver pasemi_smb_driver = { .remove = __devexit_p(pasemi_smb_remove), }; -static int __init pasemi_smb_init(void) -{ - return pci_register_driver(&pasemi_smb_driver); -} - -static void __exit pasemi_smb_exit(void) -{ - pci_unregister_driver(&pasemi_smb_driver); -} +module_pci_driver(pasemi_smb_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR ("Olof Johansson <olof@lixom.net>"); MODULE_DESCRIPTION("PA Semi PWRficient SMBus driver"); - -module_init(pasemi_smb_init); -module_exit(pasemi_smb_exit); diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index c14d48d..ef511df 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -21,11 +21,12 @@ Supports: Intel PIIX4, 440MX Serverworks OSB4, CSB5, CSB6, HT-1000, HT-1100 - ATI IXP200, IXP300, IXP400, SB600, SB700, SB800 + ATI IXP200, IXP300, IXP400, SB600, SB700/SP5100, SB800 AMD Hudson-2 SMSC Victory66 - Note: we assume there can only be one device, with one SMBus interface. + Note: we assume there can only be one device, with one or more + SMBus interfaces. */ #include <linux/module.h> @@ -94,10 +95,8 @@ MODULE_PARM_DESC(force_addr, "Forcibly enable the PIIX4 at the given address. " "EXTREMELY DANGEROUS!"); -static unsigned short piix4_smba; static int srvrworks_csb5_delay; static struct pci_driver piix4_driver; -static struct i2c_adapter piix4_adapter; static struct dmi_system_id __devinitdata piix4_dmi_blacklist[] = { { @@ -127,10 +126,15 @@ static struct dmi_system_id __devinitdata piix4_dmi_ibm[] = { { }, }; +struct i2c_piix4_adapdata { + unsigned short smba; +}; + static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, const struct pci_device_id *id) { unsigned char temp; + unsigned short piix4_smba; if ((PIIX4_dev->vendor == PCI_VENDOR_ID_SERVERWORKS) && (PIIX4_dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5)) @@ -206,7 +210,6 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, dev_err(&PIIX4_dev->dev, "Host SMBus controller not enabled!\n"); release_region(piix4_smba, SMBIOSIZE); - piix4_smba = 0; return -ENODEV; } } @@ -224,12 +227,13 @@ static int __devinit piix4_setup(struct pci_dev *PIIX4_dev, "SMBus Host Controller at 0x%x, revision %d\n", piix4_smba, temp); - return 0; + return piix4_smba; } static int __devinit piix4_setup_sb800(struct pci_dev *PIIX4_dev, const struct pci_device_id *id) { + unsigned short piix4_smba; unsigned short smba_idx = 0xcd6; u8 smba_en_lo, smba_en_hi, i2ccfg, i2ccfg_offset = 0x10, smb_en = 0x2c; @@ -273,7 +277,6 @@ static int __devinit piix4_setup_sb800(struct pci_dev *PIIX4_dev, dev_err(&PIIX4_dev->dev, "SMBus I2C bus config region " "0x%x already in use!\n", piix4_smba + i2ccfg_offset); release_region(piix4_smba, SMBIOSIZE); - piix4_smba = 0; return -EBUSY; } i2ccfg = inb_p(piix4_smba + i2ccfg_offset); @@ -288,30 +291,72 @@ static int __devinit piix4_setup_sb800(struct pci_dev *PIIX4_dev, "SMBus Host Controller at 0x%x, revision %d\n", piix4_smba, i2ccfg >> 4); - return 0; + return piix4_smba; +} + +static int __devinit piix4_setup_aux(struct pci_dev *PIIX4_dev, + const struct pci_device_id *id, + unsigned short base_reg_addr) +{ + /* Set up auxiliary SMBus controllers found on some + * AMD chipsets e.g. SP5100 (SB700 derivative) */ + + unsigned short piix4_smba; + + /* Read address of auxiliary SMBus controller */ + pci_read_config_word(PIIX4_dev, base_reg_addr, &piix4_smba); + if ((piix4_smba & 1) == 0) { + dev_dbg(&PIIX4_dev->dev, + "Auxiliary SMBus controller not enabled\n"); + return -ENODEV; + } + + piix4_smba &= 0xfff0; + if (piix4_smba == 0) { + dev_dbg(&PIIX4_dev->dev, + "Auxiliary SMBus base address uninitialized\n"); + return -ENODEV; + } + + if (acpi_check_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) + return -ENODEV; + + if (!request_region(piix4_smba, SMBIOSIZE, piix4_driver.name)) { + dev_err(&PIIX4_dev->dev, "Auxiliary SMBus region 0x%x " + "already in use!\n", piix4_smba); + return -EBUSY; + } + + dev_info(&PIIX4_dev->dev, + "Auxiliary SMBus Host Controller at 0x%x\n", + piix4_smba); + + return piix4_smba; } -static int piix4_transaction(void) +static int piix4_transaction(struct i2c_adapter *piix4_adapter) { + struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(piix4_adapter); + unsigned short piix4_smba = adapdata->smba; int temp; int result = 0; int timeout = 0; - dev_dbg(&piix4_adapter.dev, "Transaction (pre): CNT=%02x, CMD=%02x, " + dev_dbg(&piix4_adapter->dev, "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)); /* Make sure the SMBus host is ready to start transmitting */ if ((temp = inb_p(SMBHSTSTS)) != 0x00) { - dev_dbg(&piix4_adapter.dev, "SMBus busy (%02x). " + dev_dbg(&piix4_adapter->dev, "SMBus busy (%02x). " "Resetting...\n", temp); outb_p(temp, SMBHSTSTS); if ((temp = inb_p(SMBHSTSTS)) != 0x00) { - dev_err(&piix4_adapter.dev, "Failed! (%02x)\n", temp); + dev_err(&piix4_adapter->dev, "Failed! (%02x)\n", temp); return -EBUSY; } else { - dev_dbg(&piix4_adapter.dev, "Successful!\n"); + dev_dbg(&piix4_adapter->dev, "Successful!\n"); } } @@ -330,35 +375,35 @@ static int piix4_transaction(void) /* If the SMBus is still busy, we give up */ if (timeout == MAX_TIMEOUT) { - dev_err(&piix4_adapter.dev, "SMBus Timeout!\n"); + dev_err(&piix4_adapter->dev, "SMBus Timeout!\n"); result = -ETIMEDOUT; } if (temp & 0x10) { result = -EIO; - dev_err(&piix4_adapter.dev, "Error: Failed bus transaction\n"); + dev_err(&piix4_adapter->dev, "Error: Failed bus transaction\n"); } if (temp & 0x08) { result = -EIO; - dev_dbg(&piix4_adapter.dev, "Bus collision! SMBus may be " + dev_dbg(&piix4_adapter->dev, "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 = -ENXIO; - dev_dbg(&piix4_adapter.dev, "Error: no response!\n"); + dev_dbg(&piix4_adapter->dev, "Error: no response!\n"); } if (inb_p(SMBHSTSTS) != 0x00) outb_p(inb(SMBHSTSTS), SMBHSTSTS); if ((temp = inb_p(SMBHSTSTS)) != 0x00) { - dev_err(&piix4_adapter.dev, "Failed reset at end of " + dev_err(&piix4_adapter->dev, "Failed reset at end of " "transaction (%02x)\n", temp); } - dev_dbg(&piix4_adapter.dev, "Transaction (post): CNT=%02x, CMD=%02x, " + dev_dbg(&piix4_adapter->dev, "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)); @@ -370,6 +415,8 @@ 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) { + struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); + unsigned short piix4_smba = adapdata->smba; int i, len; int status; @@ -426,7 +473,7 @@ static s32 piix4_access(struct i2c_adapter * adap, u16 addr, outb_p((size & 0x1C) + (ENABLE_INT9 & 1), SMBHSTCNT); - status = piix4_transaction(); + status = piix4_transaction(adap); if (status) return status; @@ -466,12 +513,6 @@ static const struct i2c_algorithm smbus_algorithm = { .functionality = piix4_func, }; -static struct i2c_adapter piix4_adapter = { - .owner = THIS_MODULE, - .class = I2C_CLASS_HWMON | I2C_CLASS_SPD, - .algo = &smbus_algorithm, -}; - static DEFINE_PCI_DEVICE_TABLE(piix4_ids) = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_3) }, @@ -496,6 +537,57 @@ static DEFINE_PCI_DEVICE_TABLE(piix4_ids) = { MODULE_DEVICE_TABLE (pci, piix4_ids); +static struct i2c_adapter *piix4_main_adapter; +static struct i2c_adapter *piix4_aux_adapter; + +static int __devinit piix4_add_adapter(struct pci_dev *dev, + unsigned short smba, + struct i2c_adapter **padap) +{ + struct i2c_adapter *adap; + struct i2c_piix4_adapdata *adapdata; + int retval; + + adap = kzalloc(sizeof(*adap), GFP_KERNEL); + if (adap == NULL) { + release_region(smba, SMBIOSIZE); + return -ENOMEM; + } + + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; + adap->algo = &smbus_algorithm; + + adapdata = kzalloc(sizeof(*adapdata), GFP_KERNEL); + if (adapdata == NULL) { + kfree(adap); + release_region(smba, SMBIOSIZE); + return -ENOMEM; + } + + adapdata->smba = smba; + + /* set up the sysfs linkage to our parent device */ + adap->dev.parent = &dev->dev; + + snprintf(adap->name, sizeof(adap->name), + "SMBus PIIX4 adapter at %04x", smba); + + i2c_set_adapdata(adap, adapdata); + + retval = i2c_add_adapter(adap); + if (retval) { + dev_err(&dev->dev, "Couldn't register adapter!\n"); + kfree(adapdata); + kfree(adap); + release_region(smba, SMBIOSIZE); + return retval; + } + + *padap = adap; + return 0; +} + static int __devinit piix4_probe(struct pci_dev *dev, const struct pci_device_id *id) { @@ -510,30 +602,52 @@ static int __devinit piix4_probe(struct pci_dev *dev, else retval = piix4_setup(dev, id); - if (retval) + /* If no main SMBus found, give up */ + if (retval < 0) return retval; - /* set up the sysfs linkage to our parent device */ - piix4_adapter.dev.parent = &dev->dev; - - snprintf(piix4_adapter.name, sizeof(piix4_adapter.name), - "SMBus PIIX4 adapter at %04x", piix4_smba); + /* Try to register main SMBus adapter, give up if we can't */ + retval = piix4_add_adapter(dev, retval, &piix4_main_adapter); + if (retval < 0) + return retval; - if ((retval = i2c_add_adapter(&piix4_adapter))) { - dev_err(&dev->dev, "Couldn't register adapter!\n"); - release_region(piix4_smba, SMBIOSIZE); - piix4_smba = 0; + /* Check for auxiliary SMBus on some AMD chipsets */ + if (dev->vendor == PCI_VENDOR_ID_ATI && + dev->device == PCI_DEVICE_ID_ATI_SBX00_SMBUS && + dev->revision < 0x40) { + retval = piix4_setup_aux(dev, id, 0x58); + if (retval > 0) { + /* Try to add the aux adapter if it exists, + * piix4_add_adapter will clean up if this fails */ + piix4_add_adapter(dev, retval, &piix4_aux_adapter); + } } - return retval; + return 0; +} + +static void __devexit piix4_adap_remove(struct i2c_adapter *adap) +{ + struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); + + if (adapdata->smba) { + i2c_del_adapter(adap); + release_region(adapdata->smba, SMBIOSIZE); + kfree(adapdata); + kfree(adap); + } } static void __devexit piix4_remove(struct pci_dev *dev) { - if (piix4_smba) { - i2c_del_adapter(&piix4_adapter); - release_region(piix4_smba, SMBIOSIZE); - piix4_smba = 0; + if (piix4_main_adapter) { + piix4_adap_remove(piix4_main_adapter); + piix4_main_adapter = NULL; + } + + if (piix4_aux_adapter) { + piix4_adap_remove(piix4_aux_adapter); + piix4_aux_adapter = NULL; } } @@ -544,20 +658,9 @@ static struct pci_driver piix4_driver = { .remove = __devexit_p(piix4_remove), }; -static int __init i2c_piix4_init(void) -{ - return pci_register_driver(&piix4_driver); -} - -static void __exit i2c_piix4_exit(void) -{ - pci_unregister_driver(&piix4_driver); -} +module_pci_driver(piix4_driver); MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl> and " "Philip Edelbrock <phil@netroedge.com>"); MODULE_DESCRIPTION("PIIX4 SMBus driver"); MODULE_LICENSE("GPL"); - -module_init(i2c_piix4_init); -module_exit(i2c_piix4_exit); diff --git a/drivers/i2c/busses/i2c-pxa-pci.c b/drivers/i2c/busses/i2c-pxa-pci.c index a058179..4dc9bef 100644 --- a/drivers/i2c/busses/i2c-pxa-pci.c +++ b/drivers/i2c/busses/i2c-pxa-pci.c @@ -163,17 +163,7 @@ static struct pci_driver ce4100_i2c_driver = { .remove = __devexit_p(ce4100_i2c_remove), }; -static int __init ce4100_i2c_init(void) -{ - return pci_register_driver(&ce4100_i2c_driver); -} -module_init(ce4100_i2c_init); - -static void __exit ce4100_i2c_exit(void) -{ - pci_unregister_driver(&ce4100_i2c_driver); -} -module_exit(ce4100_i2c_exit); +module_pci_driver(ce4100_i2c_driver); MODULE_DESCRIPTION("CE4100 PCI-I2C glue code for PXA's driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c index 15cf78f..5d6723b 100644 --- a/drivers/i2c/busses/i2c-sis630.c +++ b/drivers/i2c/busses/i2c-sis630.c @@ -513,21 +513,8 @@ static struct pci_driver sis630_driver = { .remove = __devexit_p(sis630_remove), }; -static int __init i2c_sis630_init(void) -{ - return pci_register_driver(&sis630_driver); -} - - -static void __exit i2c_sis630_exit(void) -{ - pci_unregister_driver(&sis630_driver); -} - +module_pci_driver(sis630_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Alexander Malysh <amalysh@web.de>"); MODULE_DESCRIPTION("SIS630 SMBus driver"); - -module_init(i2c_sis630_init); -module_exit(i2c_sis630_exit); diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c index cc5d149..7b72614 100644 --- a/drivers/i2c/busses/i2c-sis96x.c +++ b/drivers/i2c/busses/i2c-sis96x.c @@ -324,21 +324,8 @@ static struct pci_driver sis96x_driver = { .remove = __devexit_p(sis96x_remove), }; -static int __init i2c_sis96x_init(void) -{ - return pci_register_driver(&sis96x_driver); -} - -static void __exit i2c_sis96x_exit(void) -{ - pci_unregister_driver(&sis96x_driver); -} +module_pci_driver(sis96x_driver); MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); MODULE_DESCRIPTION("SiS96x SMBus driver"); MODULE_LICENSE("GPL"); - -/* Register initialization functions using helper macros */ -module_init(i2c_sis96x_init); -module_exit(i2c_sis96x_exit); - diff --git a/drivers/i2c/busses/i2c-tiny-usb.c b/drivers/i2c/busses/i2c-tiny-usb.c index f07307f..0510636 100644 --- a/drivers/i2c/busses/i2c-tiny-usb.c +++ b/drivers/i2c/busses/i2c-tiny-usb.c @@ -143,6 +143,7 @@ static const struct i2c_algorithm usb_algorithm = { static const struct usb_device_id i2c_tiny_usb_table[] = { { USB_DEVICE(0x0403, 0xc631) }, /* FTDI */ { USB_DEVICE(0x1c40, 0x0534) }, /* EZPrototypes */ + { USB_DEVICE(0x1964, 0x0001) }, /* Robofuzz OSIF */ { } /* Terminating entry */ }; diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c index 713d31a..7ffee71 100644 --- a/drivers/i2c/busses/i2c-via.c +++ b/drivers/i2c/busses/i2c-via.c @@ -161,20 +161,8 @@ static struct pci_driver vt586b_driver = { .remove = __devexit_p(vt586b_remove), }; -static int __init i2c_vt586b_init(void) -{ - return pci_register_driver(&vt586b_driver); -} - -static void __exit i2c_vt586b_exit(void) -{ - pci_unregister_driver(&vt586b_driver); -} - +module_pci_driver(vt586b_driver); MODULE_AUTHOR("Kyösti Mälkki <kmalkki@cc.hut.fi>"); MODULE_DESCRIPTION("i2c for Via vt82c586b southbridge"); MODULE_LICENSE("GPL"); - -module_init(i2c_vt586b_init); -module_exit(i2c_vt586b_exit); diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index a6ad32b..26488aa 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -2122,7 +2122,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, int try; s32 res; - flags &= I2C_M_TEN | I2C_CLIENT_PEC; + flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; if (adapter->algo->smbus_xfer) { i2c_lock_adapter(adapter); @@ -2140,11 +2140,17 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, break; } i2c_unlock_adapter(adapter); - } else - res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, - command, protocol, data); - return res; + if (res != -EOPNOTSUPP || !adapter->algo->master_xfer) + return res; + /* + * Fall back to i2c_smbus_xfer_emulated if the adapter doesn't + * implement native support for the SMBus operation. + */ + } + + return i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, + command, protocol, data); } EXPORT_SYMBOL(i2c_smbus_xfer); diff --git a/drivers/i2c/i2c-smbus.c b/drivers/i2c/i2c-smbus.c index 9836d08..df3e0bf 100644 --- a/drivers/i2c/i2c-smbus.c +++ b/drivers/i2c/i2c-smbus.c @@ -245,18 +245,7 @@ int i2c_handle_smbus_alert(struct i2c_client *ara) } EXPORT_SYMBOL_GPL(i2c_handle_smbus_alert); -static int __init i2c_smbus_init(void) -{ - return i2c_add_driver(&smbalert_driver); -} - -static void __exit i2c_smbus_exit(void) -{ - i2c_del_driver(&smbalert_driver); -} - -module_init(i2c_smbus_init); -module_exit(i2c_smbus_exit); +module_i2c_driver(smbalert_driver); MODULE_AUTHOR("Jean Delvare <khali@linux-fr.org>"); MODULE_DESCRIPTION("SMBus protocol extensions support"); diff --git a/drivers/i2c/muxes/i2c-mux-pca9541.c b/drivers/i2c/muxes/i2c-mux-pca9541.c index 8aacde1..f8f72f3 100644 --- a/drivers/i2c/muxes/i2c-mux-pca9541.c +++ b/drivers/i2c/muxes/i2c-mux-pca9541.c @@ -396,6 +396,6 @@ static struct i2c_driver pca9541_driver = { module_i2c_driver(pca9541_driver); -MODULE_AUTHOR("Guenter Roeck <guenter.roeck@ericsson.com>"); +MODULE_AUTHOR("Guenter Roeck <linux@roeck-us.net>"); MODULE_DESCRIPTION("PCA9541 I2C master selector driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-i2c.h b/drivers/staging/media/go7007/wis-i2c.h index 3c2b9be..6d09c06 100644 --- a/drivers/staging/media/go7007/wis-i2c.h +++ b/drivers/staging/media/go7007/wis-i2c.h @@ -25,11 +25,6 @@ #define I2C_DRIVERID_WIS_TW2804 0xf0f6 #define I2C_DRIVERID_S2250 0xf0f7 -/* Flag to indicate that the client needs to be accessed with SCCB semantics */ -/* We re-use the I2C_M_TEN value so the flag passes through the masks in the - * core I2C code. Major kludge, but the I2C layer ain't exactly flexible. */ -#define I2C_CLIENT_SCCB 0x10 - /* Definitions for new video decoder commands */ struct video_decoder_resolution { diff --git a/include/linux/i2c.h b/include/linux/i2c.h index ddfa041..1d0fe48 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -425,6 +425,8 @@ void i2c_unlock_adapter(struct i2c_adapter *); #define I2C_CLIENT_TEN 0x10 /* we have a ten bit chip address */ /* Must equal I2C_M_TEN below */ #define I2C_CLIENT_WAKE 0x80 /* for board_info; true iff can wake */ +#define I2C_CLIENT_SCCB 0x9000 /* Use Omnivision SCCB protocol */ + /* Must match I2C_M_STOP|IGNORE_NAK */ /* i2c adapter classes (bitmask) */ #define I2C_CLASS_HWMON (1<<0) /* lm_sensors, ... */ @@ -541,6 +543,7 @@ struct i2c_msg { __u16 flags; #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */ #define I2C_M_RD 0x0001 /* read data, from slave to master */ +#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */ #define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */ #define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */ |