summaryrefslogtreecommitdiff
path: root/drivers/i2c/busses/i2c-i801.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c/busses/i2c-i801.c')
-rw-r--r--drivers/i2c/busses/i2c-i801.c145
1 files changed, 56 insertions, 89 deletions
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 99d446763530..aa3f60e69230 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -88,6 +88,8 @@
* See the file Documentation/i2c/busses/i2c-i801.rst for details.
*/
+#define DRV_NAME "i801_smbus"
+
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/pci.h>
@@ -103,7 +105,7 @@
#include <linux/dmi.h>
#include <linux/slab.h>
#include <linux/string.h>
-#include <linux/wait.h>
+#include <linux/completion.h>
#include <linux/err.h>
#include <linux/platform_device.h>
#include <linux/platform_data/itco_wdt.h>
@@ -131,8 +133,6 @@
/* PCI Address Constants */
#define SMBBAR 4
-#define SMBPCICTL 0x004
-#define SMBPCISTS 0x006
#define SMBHSTCFG 0x040
#define TCOBASE 0x050
#define TCOCTL 0x054
@@ -141,12 +141,6 @@
#define SBREG_SMBCTRL 0xc6000c
#define SBREG_SMBCTRL_DNV 0xcf000c
-/* Host status bits for SMBPCISTS */
-#define SMBPCISTS_INTS BIT(3)
-
-/* Control bits for SMBPCICTL */
-#define SMBPCICTL_INTDIS BIT(10)
-
/* Host configuration bits for SMBHSTCFG */
#define SMBHSTCFG_HST_EN BIT(0)
#define SMBHSTCFG_SMB_SMI_EN BIT(1)
@@ -164,9 +158,6 @@
#define SMBAUXCTL_CRC BIT(0)
#define SMBAUXCTL_E32B BIT(1)
-/* Other settings */
-#define MAX_RETRIES 400
-
/* I801 command constants */
#define I801_QUICK 0x00
#define I801_BYTE 0x04
@@ -270,7 +261,7 @@ struct i801_priv {
unsigned int features;
/* isr processing */
- wait_queue_head_t waitq;
+ struct completion done;
u8 status;
/* Command state used by isr for byte-by-byte block transactions */
@@ -395,11 +386,9 @@ static int i801_check_post(struct i801_priv *priv, int status)
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");
- outb_p(inb_p(SMBHSTCNT(priv)) | SMBHSTCNT_KILL,
- SMBHSTCNT(priv));
+ outb_p(SMBHSTCNT_KILL, SMBHSTCNT(priv));
usleep_range(1000, 2000);
- outb_p(inb_p(SMBHSTCNT(priv)) & (~SMBHSTCNT_KILL),
- SMBHSTCNT(priv));
+ outb_p(0, SMBHSTCNT(priv));
/* Check if it worked */
status = inb_p(SMBHSTSTS(priv));
@@ -455,67 +444,53 @@ static int i801_check_post(struct i801_priv *priv, int status)
/* 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;
+ unsigned long timeout = jiffies + priv->adapter.timeout;
+ int status, busy;
- /* 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));
+ busy = status & SMBHSTSTS_HOST_BUSY;
+ status &= STATUS_ERROR_FLAGS | SMBHSTSTS_INTR;
+ if (!busy && status)
+ return status;
+ } while (time_is_after_eq_jiffies(timeout));
- if (timeout > MAX_RETRIES) {
- dev_dbg(&priv->pci_dev->dev, "INTR Timeout!\n");
- return -ETIMEDOUT;
- }
- return status & (STATUS_ERROR_FLAGS | SMBHSTSTS_INTR);
+ return -ETIMEDOUT;
}
/* Wait for either BYTE_DONE or an error flag being set */
static int i801_wait_byte_done(struct i801_priv *priv)
{
- int timeout = 0;
+ unsigned long timeout = jiffies + priv->adapter.timeout;
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 (status & (STATUS_ERROR_FLAGS | SMBHSTSTS_BYTE_DONE))
+ return status & STATUS_ERROR_FLAGS;
+ } while (time_is_after_eq_jiffies(timeout));
- if (timeout > MAX_RETRIES) {
- dev_dbg(&priv->pci_dev->dev, "BYTE_DONE Timeout!\n");
- return -ETIMEDOUT;
- }
- return status & STATUS_ERROR_FLAGS;
+ return -ETIMEDOUT;
}
static int i801_transaction(struct i801_priv *priv, int xact)
{
int status;
- int result;
+ unsigned long result;
const struct i2c_adapter *adap = &priv->adapter;
- result = i801_check_pre(priv);
- if (result < 0)
- return result;
+ status = i801_check_pre(priv);
+ if (status < 0)
+ return status;
if (priv->features & FEATURE_IRQ) {
+ reinit_completion(&priv->done);
outb_p(xact | SMBHSTCNT_INTREN | SMBHSTCNT_START,
SMBHSTCNT(priv));
- result = wait_event_timeout(priv->waitq,
- (status = priv->status),
- adap->timeout);
- if (!result) {
- status = -ETIMEDOUT;
- dev_warn(&priv->pci_dev->dev,
- "Timeout waiting for interrupt!\n");
- }
- priv->status = 0;
- return i801_check_post(priv, status);
+ result = wait_for_completion_timeout(&priv->done, adap->timeout);
+ return i801_check_post(priv, result ? priv->status : -ETIMEDOUT);
}
/* the current contents of SMBHSTCNT can be overwritten, since PEC,
@@ -640,7 +615,7 @@ static irqreturn_t i801_host_notify_isr(struct i801_priv *priv)
* 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.
+ * When any of these occur, update ->status and signal completion.
* ->status must be cleared before kicking off the next transaction.
*
* 2) For byte-by-byte (I2C read/write) transactions, one BYTE_DONE interrupt
@@ -655,8 +630,8 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
u8 status;
/* Confirm this is our interrupt */
- pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists);
- if (!(pcists & SMBPCISTS_INTS))
+ pci_read_config_word(priv->pci_dev, PCI_STATUS, &pcists);
+ if (!(pcists & PCI_STATUS_INTERRUPT))
return IRQ_NONE;
if (priv->features & FEATURE_HOST_NOTIFY) {
@@ -677,7 +652,7 @@ static irqreturn_t i801_isr(int irq, void *dev_id)
if (status) {
outb_p(status, SMBHSTSTS(priv));
priv->status = status;
- wake_up(&priv->waitq);
+ complete(&priv->done);
}
return IRQ_HANDLED;
@@ -696,15 +671,15 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
int i, len;
int smbcmd;
int status;
- int result;
+ unsigned long result;
const struct i2c_adapter *adap = &priv->adapter;
if (command == I2C_SMBUS_BLOCK_PROC_CALL)
return -EOPNOTSUPP;
- result = i801_check_pre(priv);
- if (result < 0)
- return result;
+ status = i801_check_pre(priv);
+ if (status < 0)
+ return status;
len = data->block[0];
@@ -728,17 +703,10 @@ static int i801_block_transaction_byte_by_byte(struct i801_priv *priv,
priv->count = 0;
priv->data = &data->block[1];
+ reinit_completion(&priv->done);
outb_p(priv->cmd | SMBHSTCNT_START, SMBHSTCNT(priv));
- result = wait_event_timeout(priv->waitq,
- (status = priv->status),
- adap->timeout);
- if (!result) {
- status = -ETIMEDOUT;
- dev_warn(&priv->pci_dev->dev,
- "Timeout waiting for interrupt!\n");
- }
- priv->status = 0;
- return i801_check_post(priv, status);
+ result = wait_for_completion_timeout(&priv->done, adap->timeout);
+ return i801_check_post(priv, result ? priv->status : -ETIMEDOUT);
}
for (i = 1; i <= len; i++) {
@@ -980,6 +948,9 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
}
out:
+ /* Unlock the SMBus device for use by BIOS/ACPI */
+ outb_p(SMBHSTSTS_INUSE_STS, SMBHSTSTS(priv));
+
pm_runtime_mark_last_busy(&priv->pci_dev->dev);
pm_runtime_put_autosuspend(&priv->pci_dev->dev);
mutex_unlock(&priv->acpi_lock);
@@ -1321,11 +1292,11 @@ static void i801_probe_optional_slaves(struct i801_priv *priv)
return;
if (apanel_addr) {
- struct i2c_board_info info;
+ struct i2c_board_info info = {
+ .addr = apanel_addr,
+ .type = "fujitsu_apanel",
+ };
- memset(&info, 0, sizeof(struct i2c_board_info));
- info.addr = apanel_addr;
- strlcpy(info.type, "fujitsu_apanel", I2C_NAME_SIZE);
i2c_new_client_device(&priv->adapter, &info);
}
@@ -1714,19 +1685,17 @@ static inline int i801_acpi_probe(struct i801_priv *priv) { return 0; }
static inline void i801_acpi_remove(struct i801_priv *priv) { }
#endif
-static unsigned char i801_setup_hstcfg(struct i801_priv *priv)
+static void i801_setup_hstcfg(struct i801_priv *priv)
{
unsigned char hstcfg = priv->original_hstcfg;
hstcfg &= ~SMBHSTCFG_I2C_EN; /* SMBus timing */
hstcfg |= SMBHSTCFG_HST_EN;
pci_write_config_byte(priv->pci_dev, SMBHSTCFG, hstcfg);
- return hstcfg;
}
static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
- unsigned char temp;
int err, i;
struct i801_priv *priv;
@@ -1837,8 +1806,7 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
if (i801_acpi_probe(priv))
return -ENODEV;
- err = pcim_iomap_regions(dev, 1 << SMBBAR,
- dev_driver_string(&dev->dev));
+ err = pcim_iomap_regions(dev, 1 << SMBBAR, DRV_NAME);
if (err) {
dev_err(&dev->dev,
"Failed to request SMBus region 0x%lx-0x%Lx\n",
@@ -1849,16 +1817,16 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
}
pci_read_config_byte(priv->pci_dev, SMBHSTCFG, &priv->original_hstcfg);
- temp = i801_setup_hstcfg(priv);
+ i801_setup_hstcfg(priv);
if (!(priv->original_hstcfg & SMBHSTCFG_HST_EN))
dev_info(&dev->dev, "Enabling SMBus device\n");
- if (temp & SMBHSTCFG_SMB_SMI_EN) {
+ if (priv->original_hstcfg & SMBHSTCFG_SMB_SMI_EN) {
dev_dbg(&dev->dev, "SMBus using interrupt SMI#\n");
/* Disable SMBus interrupt feature if SMBus using SMI# */
priv->features &= ~FEATURE_IRQ;
}
- if (temp & SMBHSTCFG_SPD_WD)
+ if (priv->original_hstcfg & SMBHSTCFG_SPD_WD)
dev_info(&dev->dev, "SPD Write Disable is set\n");
/* Clear special mode bits */
@@ -1880,24 +1848,23 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
u16 pcictl, pcists;
/* Complain if an interrupt is already pending */
- pci_read_config_word(priv->pci_dev, SMBPCISTS, &pcists);
- if (pcists & SMBPCISTS_INTS)
+ pci_read_config_word(priv->pci_dev, PCI_STATUS, &pcists);
+ if (pcists & PCI_STATUS_INTERRUPT)
dev_warn(&dev->dev, "An interrupt is pending!\n");
/* Check if interrupts have been disabled */
- pci_read_config_word(priv->pci_dev, SMBPCICTL, &pcictl);
- if (pcictl & SMBPCICTL_INTDIS) {
+ pci_read_config_word(priv->pci_dev, PCI_COMMAND, &pcictl);
+ if (pcictl & PCI_COMMAND_INTX_DISABLE) {
dev_info(&dev->dev, "Interrupts are disabled\n");
priv->features &= ~FEATURE_IRQ;
}
}
if (priv->features & FEATURE_IRQ) {
- init_waitqueue_head(&priv->waitq);
+ init_completion(&priv->done);
err = devm_request_irq(&dev->dev, dev->irq, i801_isr,
- IRQF_SHARED,
- dev_driver_string(&dev->dev), priv);
+ IRQF_SHARED, DRV_NAME, priv);
if (err) {
dev_err(&dev->dev, "Failed to allocate irq %d: %d\n",
dev->irq, err);
@@ -1987,7 +1954,7 @@ static int i801_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(i801_pm_ops, i801_suspend, i801_resume);
static struct pci_driver i801_driver = {
- .name = "i801_smbus",
+ .name = DRV_NAME,
.id_table = i801_ids,
.probe = i801_probe,
.remove = i801_remove,