diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-07-04 20:47:18 +0200 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-07-04 20:47:18 +0200 |
commit | 855ff900b8605df2b9ea309534cd2f02dc0c4cb8 (patch) | |
tree | c6f38bbb17d8702c7a692b383be94a59d451922b /drivers/i2c/busses/i2c-i801.c | |
parent | scsi: blkcg: Fix application ID config options (diff) | |
parent | i2c: ali1535: mention that the device should not be disabled (diff) | |
download | linux-855ff900b8605df2b9ea309534cd2f02dc0c4cb8.tar.xz linux-855ff900b8605df2b9ea309534cd2f02dc0c4cb8.zip |
Merge branch 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang:
- core supports now bus regulators controlling power for SCL/SDA
- quite some DT binding conversions to YAML
- added a seperate DT binding for the optional SMBus Alert feature
- documentation with examples how to deal with I2C sysfs files
- some bigger rework for the i801 driver
- and a few usual driver updates
* 'i2c/for-mergewindow' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (42 commits)
i2c: ali1535: mention that the device should not be disabled
i2c: mpc: Restore reread of I2C status register
i2c: core-smbus: Expose PEC calculate function for generic use
Documentation: i2c: Add doc for I2C sysfs
i2c: core: Disable client irq on reboot/shutdown
dt-bindings: i2c: update bindings for MT8195 SoC
i2c: imx: Fix some checkpatch warnings
i2c: davinci: Simplify with dev_err_probe()
i2c: cadence: Simplify with dev_err_probe()
i2c: xiic: Simplify with dev_err_probe()
i2c: cadence: Clear HOLD bit before xfer_size register rolls over
dt-bindings: i2c: ce4100: Replace "ti,pcf8575" by "nxp,pcf8575"
i2c: i801: Improve i801_setup_hstcfg
i2c: i801: Use driver name constant instead of function dev_driver_string
i2c: i801: Simplify initialization of i2c_board_info in i801_probe_optional_slaves
i2c: i801: Improve status polling
i2c: cht-wc: Replace of_node by NULL
i2c: riic: Add RZ/G2L support
dt-bindings: i2c: renesas,riic: Document RZ/G2L I2C controller
dt-bindings: i2c: renesas,iic: Convert to json-schema
...
Diffstat (limited to 'drivers/i2c/busses/i2c-i801.c')
-rw-r--r-- | drivers/i2c/busses/i2c-i801.c | 136 |
1 files changed, 51 insertions, 85 deletions
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 04a1e38f2a6f..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 */ @@ -453,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, @@ -638,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 @@ -653,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) { @@ -675,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; @@ -694,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]; @@ -726,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++) { @@ -1322,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); } @@ -1715,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; @@ -1838,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", @@ -1850,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 */ @@ -1881,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); @@ -1988,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, |