diff options
author | Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com> | 2017-01-11 04:11:44 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2017-01-19 14:18:04 -0500 |
commit | 64e236812197178a0ffae4ac76394730c392ccd9 (patch) | |
tree | 15065ca2c11a849c583b2f6259722af90470b653 /drivers/i2c | |
parent | ce31072b43426e3bf45bc490e93504f5746cb9b5 (diff) |
i2c: piix4: Avoid race conditions with IMC
commit 701dc207bf551d9fe6defa36e84a911e880398c3 upstream.
On AMD's SB800 and upwards, the SMBus is shared with the Integrated
Micro Controller (IMC).
The platform provides a hardware semaphore to avoid race conditions
among them. (Check page 288 of the SB800-Series Southbridges Register
Reference Guide http://support.amd.com/TechDocs/45482.pdf)
Without this patch, many access to the SMBus end with an invalid
transaction or even with the bus stalled.
Reported-by: Alexandre Desnoyers <alex@qtec.com>
Signed-off-by: Ricardo Ribalda Delgado <ricardo.ribalda@gmail.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>:
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-piix4.c | 22 |
1 files changed, 22 insertions, 0 deletions
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index c2268cdf38e8..e34d82e79b98 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c | |||
@@ -585,10 +585,29 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, | |||
585 | u8 command, int size, union i2c_smbus_data *data) | 585 | u8 command, int size, union i2c_smbus_data *data) |
586 | { | 586 | { |
587 | struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); | 587 | struct i2c_piix4_adapdata *adapdata = i2c_get_adapdata(adap); |
588 | unsigned short piix4_smba = adapdata->smba; | ||
589 | int retries = MAX_TIMEOUT; | ||
590 | int smbslvcnt; | ||
588 | u8 smba_en_lo; | 591 | u8 smba_en_lo; |
589 | u8 port; | 592 | u8 port; |
590 | int retval; | 593 | int retval; |
591 | 594 | ||
595 | /* Request the SMBUS semaphore, avoid conflicts with the IMC */ | ||
596 | smbslvcnt = inb_p(SMBSLVCNT); | ||
597 | do { | ||
598 | outb_p(smbslvcnt | 0x10, SMBSLVCNT); | ||
599 | |||
600 | /* Check the semaphore status */ | ||
601 | smbslvcnt = inb_p(SMBSLVCNT); | ||
602 | if (smbslvcnt & 0x10) | ||
603 | break; | ||
604 | |||
605 | usleep_range(1000, 2000); | ||
606 | } while (--retries); | ||
607 | /* SMBus is still owned by the IMC, we give up */ | ||
608 | if (!retries) | ||
609 | return -EBUSY; | ||
610 | |||
592 | mutex_lock(&piix4_mutex_sb800); | 611 | mutex_lock(&piix4_mutex_sb800); |
593 | 612 | ||
594 | outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX); | 613 | outb_p(piix4_port_sel_sb800, SB800_PIIX4_SMB_IDX); |
@@ -606,6 +625,9 @@ static s32 piix4_access_sb800(struct i2c_adapter *adap, u16 addr, | |||
606 | 625 | ||
607 | mutex_unlock(&piix4_mutex_sb800); | 626 | mutex_unlock(&piix4_mutex_sb800); |
608 | 627 | ||
628 | /* Release the semaphore */ | ||
629 | outb_p(smbslvcnt | 0x20, SMBSLVCNT); | ||
630 | |||
609 | return retval; | 631 | return retval; |
610 | } | 632 | } |
611 | 633 | ||