mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-24 17:59:32 +00:00
Merge branch 'i2c-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging
Pull i2c updates from Jean Delvare: "The most important changes here are a big cleanup of the i2c-piix4 driver, cleanups and interrupt support to the i2c-i801 driver, and support for the SCCB protocol." * 'i2c-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jdelvare/staging: i2c-omap: Add support for I2C_M_STOP message flag i2c: Fall back to emulated SMBus if the operation isn't supported natively i2c: Add SCCB support i2c-tiny-usb: Add support for the Robofuzz OSIF USB/I2C converter i2c-i801: Enable IRQ for byte_by_byte transactions i2c-i801: Enable interrupts on ICH5/7/8/9/10 i2c-i801: Enable IRQ for SMBus transactions i2c-i801: Consolidate polling i2c-i801: Drop ENABLE_INT9 i2c-i801: Rename some SMBHSTCNT bit constants i2c-i801: Check and return errors during byte-by-byte transfers i2c-i801: Clear only status bits in HST_STS i2c-i801: Refactor use of LAST_BYTE in i801_block_transaction_byte_by_byte i2c-smbus: Use module_i2c_driver() i2c/writing-clients: Mention module_i2c_driver() i2c-piix4: Support AMD auxiliary SMBus controller i2c-piix4: Separate registration and probing code i2c-piix4: Eliminate piix4_smba global variable i2c/busses: Use module_pci_driver i2c: Update Guenter Roeck's e-mail address
This commit is contained in:
commit
ddb0344827
29 changed files with 495 additions and 374 deletions
|
@ -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
|
||||
----------------
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -245,11 +245,26 @@ 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);
|
||||
|
||||
The module_i2c_driver() macro can be used to reduce above code.
|
||||
|
||||
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.
|
||||
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>"
|
||||
|
@ -258,14 +273,6 @@ MODULE_DESCRIPTION("Driver for Barf Inc. Foo I2C devices");
|
|||
/* a few non-GPL license types are also allowed */
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
module_init(foo_init);
|
||||
module_exit(foo_cleanup);
|
||||
|
||||
Note that some functions are marked by `__init'. These functions can
|
||||
be removed after kernel booting (or module loading) is completed.
|
||||
Likewise, functions marked by `__exit' are dropped by the compiler when
|
||||
the code is built into the kernel, as they would never be called.
|
||||
|
||||
|
||||
Power Management
|
||||
================
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/* Wait for BUSY being cleared and either INTR or an error flag being set */
|
||||
static int i801_wait_intr(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_HOST_BUSY) ||
|
||||
!(status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR))) &&
|
||||
(timeout++ < MAX_RETRIES));
|
||||
|
||||
if (timeout > MAX_RETRIES) {
|
||||
dev_dbg(&priv->pci_dev->dev, "INTR Timeout!\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR);
|
||||
}
|
||||
|
||||
/* 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 & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE)) &&
|
||||
(timeout++ < MAX_RETRIES));
|
||||
|
||||
if (timeout > MAX_RETRIES) {
|
||||
dev_dbg(&priv->pci_dev->dev, "BYTE_DONE Timeout!\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
return status & STATUS_ERROR_FLAGS;
|
||||
}
|
||||
|
||||
static int i801_transaction(struct i801_priv *priv, int xact)
|
||||
{
|
||||
int status;
|
||||
int result;
|
||||
int timeout = 0;
|
||||
|
||||
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,
|
||||
* INTREN, SMBSCMD are passed in xact */
|
||||
outb_p(xact | I801_START, SMBHSTCNT(priv));
|
||||
* SMBSCMD are passed in xact */
|
||||
outb_p(xact | SMBHSTCNT_START, SMBHSTCNT(priv));
|
||||
|
||||
/* 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));
|
||||
|
||||
result = i801_check_post(priv, status, timeout > MAX_RETRIES);
|
||||
if (result < 0)
|
||||
return result;
|
||||
|
||||
outb_p(SMBHSTSTS_INTR, SMBHSTSTS(priv));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* wait for INTR bit as advised by Intel */
|
||||
static void i801_wait_hwpec(struct i801_priv *priv)
|
||||
{
|
||||
int timeout = 0;
|
||||
int status;
|
||||
|
||||
do {
|
||||
usleep_range(250, 500);
|
||||
status = inb_p(SMBHSTSTS(priv));
|
||||
} while ((!(status & SMBHSTSTS_INTR))
|
||||
&& (timeout++ < MAX_RETRIES));
|
||||
|
||||
if (timeout > MAX_RETRIES)
|
||||
dev_dbg(&priv->pci_dev->dev, "PEC Timeout!\n");
|
||||
|
||||
outb_p(status, SMBHSTSTS(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);
|
||||
/*
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 @@ omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
|
|||
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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 piix4_transaction(void)
|
||||
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(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;
|
||||
/* 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;
|
||||
|
||||
snprintf(piix4_adapter.name, sizeof(piix4_adapter.name),
|
||||
"SMBus PIIX4 adapter at %04x", piix4_smba);
|
||||
|
||||
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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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 */
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 */
|
||||
|
|
Loading…
Reference in a new issue