diff options
68 files changed, 1464 insertions, 1020 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 353bfe6bbaca..c58ee8c0b782 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2878,6 +2878,14 @@ S: Maintained F: drivers/iio/light/cm* F: Documentation/devicetree/bindings/i2c/trivial-devices.txt +CAVIUM I2C DRIVER +M: Jan Glauber <jglauber@cavium.com> +M: David Daney <david.daney@cavium.com> +W: http://www.cavium.com +S: Supported +F: drivers/i2c/busses/i2c-octeon* +F: drivers/i2c/busses/i2c-thunderx* + CAVIUM LIQUIDIO NETWORK DRIVER M: Derek Chickles <derek.chickles@caviumnetworks.com> M: Satanand Burla <satananda.burla@caviumnetworks.com> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 5c3993b26129..1f3239ead037 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -956,6 +956,17 @@ config I2C_OCTEON This driver can also be built as a module. If so, the module will be called i2c-octeon. +config I2C_THUNDERX + tristate "Cavium ThunderX I2C bus support" + depends on 64BIT && PCI && (ARM64 || COMPILE_TEST) + select I2C_SMBUS + help + Say yes if you want to support the I2C serial bus on Cavium + ThunderX SOC. + + This driver can also be built as a module. If so, the module + will be called i2c-thunderx. + config I2C_XILINX tristate "Xilinx I2C Controller" depends on HAS_IOMEM diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 37f2819b4560..29764cc20a44 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -91,7 +91,10 @@ obj-$(CONFIG_I2C_UNIPHIER) += i2c-uniphier.o obj-$(CONFIG_I2C_UNIPHIER_F) += i2c-uniphier-f.o obj-$(CONFIG_I2C_VERSATILE) += i2c-versatile.o obj-$(CONFIG_I2C_WMT) += i2c-wmt.o +i2c-octeon-objs := i2c-octeon-core.o i2c-octeon-platdrv.o obj-$(CONFIG_I2C_OCTEON) += i2c-octeon.o +i2c-thunderx-objs := i2c-octeon-core.o i2c-thunderx-pcidrv.o +obj-$(CONFIG_I2C_THUNDERX) += i2c-thunderx.o obj-$(CONFIG_I2C_XILINX) += i2c-xiic.o obj-$(CONFIG_I2C_XLR) += i2c-xlr.o obj-$(CONFIG_I2C_XLP9XX) += i2c-xlp9xx.o diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c index 6c7113d990f8..274908cd1fde 100644 --- a/drivers/i2c/busses/i2c-amd756.c +++ b/drivers/i2c/busses/i2c-amd756.c @@ -378,11 +378,8 @@ static int amd756_probe(struct pci_dev *pdev, const struct pci_device_id *id) amd756_ioport); error = i2c_add_adapter(&amd756_smbus); - if (error) { - dev_err(&pdev->dev, - "Adapter registration failed, module not inserted\n"); + if (error) goto out_err; - } return 0; diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 1bb97f658b47..0b86c6173e07 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -1122,8 +1122,6 @@ static int at91_twi_probe(struct platform_device *pdev) rc = i2c_add_numbered_adapter(&dev->adapter); if (rc) { - dev_err(dev->dev, "Adapter %s registration failed\n", - dev->adapter.name); clk_disable_unprepare(dev->clk); pm_runtime_disable(dev->dev); diff --git a/drivers/i2c/busses/i2c-axxia.c b/drivers/i2c/busses/i2c-axxia.c index c335cc7852f9..d3bcaf4ab095 100644 --- a/drivers/i2c/busses/i2c-axxia.c +++ b/drivers/i2c/busses/i2c-axxia.c @@ -558,13 +558,7 @@ static int axxia_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, idev); - ret = i2c_add_adapter(&idev->adapter); - if (ret) { - dev_err(&pdev->dev, "failed to add adapter\n"); - return ret; - } - - return 0; + return i2c_add_adapter(&idev->adapter); } static int axxia_i2c_remove(struct platform_device *pdev) diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index 95f7cac76f89..326b3db02c48 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -488,13 +488,7 @@ static int bcm_iproc_i2c_probe(struct platform_device *pdev) adap->dev.parent = &pdev->dev; adap->dev.of_node = pdev->dev.of_node; - ret = i2c_add_adapter(adap); - if (ret) { - dev_err(iproc_i2c->device, "failed to add adapter\n"); - return ret; - } - - return 0; + return i2c_add_adapter(adap); } static int bcm_iproc_i2c_remove(struct platform_device *pdev) diff --git a/drivers/i2c/busses/i2c-bcm-kona.c b/drivers/i2c/busses/i2c-bcm-kona.c index f98743277e3c..d8494f8c9bd4 100644 --- a/drivers/i2c/busses/i2c-bcm-kona.c +++ b/drivers/i2c/busses/i2c-bcm-kona.c @@ -858,10 +858,8 @@ static int bcm_kona_i2c_probe(struct platform_device *pdev) adap->dev.of_node = pdev->dev.of_node; rc = i2c_add_adapter(adap); - if (rc) { - dev_err(dev->device, "failed to add adapter\n"); + if (rc) return rc; - } dev_info(dev->device, "device registered successfully\n"); diff --git a/drivers/i2c/busses/i2c-bfin-twi.c b/drivers/i2c/busses/i2c-bfin-twi.c index 025686d41640..29d00c4f7824 100644 --- a/drivers/i2c/busses/i2c-bfin-twi.c +++ b/drivers/i2c/busses/i2c-bfin-twi.c @@ -685,10 +685,8 @@ static int i2c_bfin_twi_probe(struct platform_device *pdev) write_CONTROL(iface, read_CONTROL(iface) | TWI_ENA); rc = i2c_add_numbered_adapter(p_adap); - if (rc < 0) { - dev_err(&pdev->dev, "Can't add i2c adapter!\n"); + if (rc < 0) goto out_error; - } platform_set_drvdata(pdev, iface); diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c index 385b57bfcb38..0652281662a8 100644 --- a/drivers/i2c/busses/i2c-brcmstb.c +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -648,10 +648,8 @@ static int brcmstb_i2c_probe(struct platform_device *pdev) adap->dev.parent = &pdev->dev; adap->dev.of_node = pdev->dev.of_node; rc = i2c_add_adapter(adap); - if (rc) { - dev_err(dev->device, "failed to add adapter\n"); + if (rc) goto probe_errorout; - } dev_info(dev->device, "%s@%dhz registered in %s mode\n", int_name ? int_name : " ", dev->clk_freq_hz, diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 90bbd9f9dd8f..1cc1e79e958d 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -963,10 +963,8 @@ static int cdns_i2c_probe(struct platform_device *pdev) } ret = i2c_add_adapter(&id->adap); - if (ret < 0) { - dev_err(&pdev->dev, "reg adap failed: %d\n", ret); + if (ret < 0) goto err_clk_dis; - } /* * Cadence I2C controller has a bug wherein it generates diff --git a/drivers/i2c/busses/i2c-cpm.c b/drivers/i2c/busses/i2c-cpm.c index ee57c1e865e2..d89bde2c5da2 100644 --- a/drivers/i2c/busses/i2c-cpm.c +++ b/drivers/i2c/busses/i2c-cpm.c @@ -665,10 +665,8 @@ static int cpm_i2c_probe(struct platform_device *ofdev) cpm->adap.nr = (data && len == 4) ? be32_to_cpup(data) : -1; result = i2c_add_numbered_adapter(&cpm->adap); - if (result < 0) { - dev_err(&ofdev->dev, "Unable to register with I2C\n"); + if (result < 0) goto out_shut; - } dev_dbg(&ofdev->dev, "hw routines for %s registered.\n", cpm->adap.name); diff --git a/drivers/i2c/busses/i2c-cros-ec-tunnel.c b/drivers/i2c/busses/i2c-cros-ec-tunnel.c index 2d5ff86398d0..9b36a7b3befd 100644 --- a/drivers/i2c/busses/i2c-cros-ec-tunnel.c +++ b/drivers/i2c/busses/i2c-cros-ec-tunnel.c @@ -281,10 +281,8 @@ static int ec_i2c_probe(struct platform_device *pdev) bus->adap.retries = I2C_MAX_RETRIES; err = i2c_add_adapter(&bus->adap); - if (err) { - dev_err(dev, "cannot register i2c adapter\n"); + if (err) return err; - } platform_set_drvdata(pdev, bus); return err; diff --git a/drivers/i2c/busses/i2c-davinci.c b/drivers/i2c/busses/i2c-davinci.c index a8bdcb5292f5..9e7ef5cf5d49 100644 --- a/drivers/i2c/busses/i2c-davinci.c +++ b/drivers/i2c/busses/i2c-davinci.c @@ -846,10 +846,8 @@ static int davinci_i2c_probe(struct platform_device *pdev) adap->nr = pdev->id; r = i2c_add_numbered_adapter(adap); - if (r) { - dev_err(&pdev->dev, "failure adding adapter\n"); + if (r) goto err_unuse_clocks; - } return 0; diff --git a/drivers/i2c/busses/i2c-designware-core.c b/drivers/i2c/busses/i2c-designware-core.c index c6922b806fb7..52a603a01ddd 100644 --- a/drivers/i2c/busses/i2c-designware-core.c +++ b/drivers/i2c/busses/i2c-designware-core.c @@ -42,6 +42,8 @@ #define DW_IC_SS_SCL_LCNT 0x18 #define DW_IC_FS_SCL_HCNT 0x1c #define DW_IC_FS_SCL_LCNT 0x20 +#define DW_IC_HS_SCL_HCNT 0x24 +#define DW_IC_HS_SCL_LCNT 0x28 #define DW_IC_INTR_STAT 0x2c #define DW_IC_INTR_MASK 0x30 #define DW_IC_RAW_INTR_STAT 0x34 @@ -89,12 +91,17 @@ DW_IC_INTR_TX_ABRT | \ DW_IC_INTR_STOP_DET) -#define DW_IC_STATUS_ACTIVITY 0x1 +#define DW_IC_STATUS_ACTIVITY 0x1 +#define DW_IC_STATUS_TFE BIT(2) +#define DW_IC_STATUS_MST_ACTIVITY BIT(5) #define DW_IC_ERR_TX_ABRT 0x1 #define DW_IC_TAR_10BITADDR_MASTER BIT(12) +#define DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH (BIT(2) | BIT(3)) +#define DW_IC_COMP_PARAM_1_SPEED_MODE_MASK GENMASK(3, 2) + /* * status codes */ @@ -252,10 +259,15 @@ static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) static void __i2c_dw_enable(struct dw_i2c_dev *dev, bool enable) { + dw_writel(dev, enable, DW_IC_ENABLE); +} + +static void __i2c_dw_enable_and_wait(struct dw_i2c_dev *dev, bool enable) +{ int timeout = 100; do { - dw_writel(dev, enable, DW_IC_ENABLE); + __i2c_dw_enable(dev, enable); if ((dw_readl(dev, DW_IC_ENABLE_STATUS) & 1) == enable) return; @@ -282,6 +294,28 @@ static unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev) return dev->get_clk_rate_khz(dev); } +static int i2c_dw_acquire_lock(struct dw_i2c_dev *dev) +{ + int ret; + + if (!dev->acquire_lock) + return 0; + + ret = dev->acquire_lock(dev); + if (!ret) + return 0; + + dev_err(dev->dev, "couldn't acquire bus ownership\n"); + + return ret; +} + +static void i2c_dw_release_lock(struct dw_i2c_dev *dev) +{ + if (dev->release_lock) + dev->release_lock(dev); +} + /** * i2c_dw_init() - initialize the designware i2c master hardware * @dev: device private data @@ -293,17 +327,13 @@ static unsigned long i2c_dw_clk_rate(struct dw_i2c_dev *dev) int i2c_dw_init(struct dw_i2c_dev *dev) { u32 hcnt, lcnt; - u32 reg; + u32 reg, comp_param1; u32 sda_falling_time, scl_falling_time; int ret; - if (dev->acquire_lock) { - ret = dev->acquire_lock(dev); - if (ret) { - dev_err(dev->dev, "couldn't acquire bus ownership\n"); - return ret; - } - } + ret = i2c_dw_acquire_lock(dev); + if (ret) + return ret; reg = dw_readl(dev, DW_IC_COMP_TYPE); if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) { @@ -315,13 +345,14 @@ int i2c_dw_init(struct dw_i2c_dev *dev) } else if (reg != DW_IC_COMP_TYPE_VALUE) { dev_err(dev->dev, "Unknown Synopsys component type: " "0x%08x\n", reg); - if (dev->release_lock) - dev->release_lock(dev); + i2c_dw_release_lock(dev); return -ENODEV; } + comp_param1 = dw_readl(dev, DW_IC_COMP_PARAM_1); + /* Disable the adapter */ - __i2c_dw_enable(dev, false); + __i2c_dw_enable_and_wait(dev, false); /* set standard and fast speed deviders for high/low periods */ @@ -347,8 +378,11 @@ int i2c_dw_init(struct dw_i2c_dev *dev) dw_writel(dev, lcnt, DW_IC_SS_SCL_LCNT); dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); - /* Set SCL timing parameters for fast-mode */ - if (dev->fs_hcnt && dev->fs_lcnt) { + /* Set SCL timing parameters for fast-mode or fast-mode plus */ + if ((dev->clk_freq == 1000000) && dev->fp_hcnt && dev->fp_lcnt) { + hcnt = dev->fp_hcnt; + lcnt = dev->fp_lcnt; + } else if (dev->fs_hcnt && dev->fs_lcnt) { hcnt = dev->fs_hcnt; lcnt = dev->fs_lcnt; } else { @@ -366,6 +400,23 @@ int i2c_dw_init(struct dw_i2c_dev *dev) dw_writel(dev, lcnt, DW_IC_FS_SCL_LCNT); dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); + if ((dev->master_cfg & DW_IC_CON_SPEED_MASK) == + DW_IC_CON_SPEED_HIGH) { + if ((comp_param1 & DW_IC_COMP_PARAM_1_SPEED_MODE_MASK) + != DW_IC_COMP_PARAM_1_SPEED_MODE_HIGH) { + dev_err(dev->dev, "High Speed not supported!\n"); + dev->master_cfg &= ~DW_IC_CON_SPEED_MASK; + dev->master_cfg |= DW_IC_CON_SPEED_FAST; + } else if (dev->hs_hcnt && dev->hs_lcnt) { + hcnt = dev->hs_hcnt; + lcnt = dev->hs_lcnt; + dw_writel(dev, hcnt, DW_IC_HS_SCL_HCNT); + dw_writel(dev, lcnt, DW_IC_HS_SCL_LCNT); + dev_dbg(dev->dev, "HighSpeed-mode HCNT:LCNT = %d:%d\n", + hcnt, lcnt); + } + } + /* Configure SDA Hold Time if required */ if (dev->sda_hold_time) { reg = dw_readl(dev, DW_IC_COMP_VERSION); @@ -383,8 +434,8 @@ int i2c_dw_init(struct dw_i2c_dev *dev) /* configure the i2c master */ dw_writel(dev, dev->master_cfg , DW_IC_CON); - if (dev->release_lock) - dev->release_lock(dev); + i2c_dw_release_lock(dev); + return 0; } EXPORT_SYMBOL_GPL(i2c_dw_init); @@ -411,27 +462,45 @@ static int i2c_dw_wait_bus_not_busy(struct dw_i2c_dev *dev) static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) { struct i2c_msg *msgs = dev->msgs; - u32 ic_con, ic_tar = 0; + u32 ic_tar = 0; + bool enabled; - /* Disable the adapter */ - __i2c_dw_enable(dev, false); + enabled = dw_readl(dev, DW_IC_ENABLE_STATUS) & 1; + + if (enabled) { + u32 ic_status; + + /* + * Only disable adapter if ic_tar and ic_con can't be + * dynamically updated + */ + ic_status = dw_readl(dev, DW_IC_STATUS); + if (!dev->dynamic_tar_update_enabled || + (ic_status & DW_IC_STATUS_MST_ACTIVITY) || + !(ic_status & DW_IC_STATUS_TFE)) { + __i2c_dw_enable_and_wait(dev, false); + enabled = false; + } + } /* if the slave address is ten bit address, enable 10BITADDR */ - ic_con = dw_readl(dev, DW_IC_CON); - if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) { - ic_con |= DW_IC_CON_10BITADDR_MASTER; + if (dev->dynamic_tar_update_enabled) { /* * If I2C_DYNAMIC_TAR_UPDATE is set, the 10-bit addressing - * mode has to be enabled via bit 12 of IC_TAR register. - * We set it always as I2C_DYNAMIC_TAR_UPDATE can't be - * detected from registers. + * mode has to be enabled via bit 12 of IC_TAR register, + * otherwise bit 4 of IC_CON is used. */ - ic_tar = DW_IC_TAR_10BITADDR_MASTER; + if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) + ic_tar = DW_IC_TAR_10BITADDR_MASTER; } else { - ic_con &= ~DW_IC_CON_10BITADDR_MASTER; - } + u32 ic_con = dw_readl(dev, DW_IC_CON); - dw_writel(dev, ic_con, DW_IC_CON); + if (msgs[dev->msg_write_idx].flags & I2C_M_TEN) + ic_con |= DW_IC_CON_10BITADDR_MASTER; + else + ic_con &= ~DW_IC_CON_10BITADDR_MASTER; + dw_writel(dev, ic_con, DW_IC_CON); + } /* * Set the slave (target) address and enable 10-bit addressing mode @@ -442,8 +511,8 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev) /* enforce disabled interrupts (due to HW issues) */ i2c_dw_disable_int(dev); - /* Enable the adapter */ - __i2c_dw_enable(dev, true); + if (!enabled) + __i2c_dw_enable(dev, true); /* Clear and enable interrupts */ dw_readl(dev, DW_IC_CLR_INTR); @@ -624,7 +693,8 @@ static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev) } /* - * Prepare controller for a transaction and call i2c_dw_xfer_msg + * Prepare controller for a transaction and start transfer by calling + * i2c_dw_xfer_init() */ static int i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) @@ -647,13 +717,9 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) dev->abort_source = 0; dev->rx_outstanding = 0; - if (dev->acquire_lock) { - ret = dev->acquire_lock(dev); - if (ret) { - dev_err(dev->dev, "couldn't acquire bus ownership\n"); - goto done_nolock; - } - } + ret = i2c_dw_acquire_lock(dev); + if (ret) + goto done_nolock; ret = i2c_dw_wait_bus_not_busy(dev); if (ret < 0) @@ -671,16 +737,6 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) goto done; } - /* - * We must disable the adapter before returning and signaling the end - * of the current transfer. Otherwise the hardware might continue - * generating interrupts which in turn causes a race condition with - * the following transfer. Needs some more investigation if the - * additional interrupts are a hardware bug or this driver doesn't - * handle them correctly yet. - */ - __i2c_dw_enable(dev, false); - if (dev->msg_err) { ret = dev->msg_err; goto done; @@ -700,8 +756,7 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) ret = -EIO; done: - if (dev->release_lock) - dev->release_lock(dev); + i2c_dw_release_lock(dev); done_nolock: pm_runtime_mark_last_busy(dev->dev); @@ -818,9 +873,19 @@ static irqreturn_t i2c_dw_isr(int this_irq, void *dev_id) */ tx_aborted: - if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err) + if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) + || dev->msg_err) { + /* + * We must disable interruts before returning and signaling + * the end of the current transfer. Otherwise the hardware + * might continue generating interrupts for non-existent + * transfers. + */ + i2c_dw_disable_int(dev); + dw_readl(dev, DW_IC_CLR_INTR); + complete(&dev->cmd_complete); - else if (unlikely(dev->accessor_flags & ACCESS_INTR_MASK)) { + } else if (unlikely(dev->accessor_flags & ACCESS_INTR_MASK)) { /* workaround to trigger pending interrupt */ stat = dw_readl(dev, DW_IC_INTR_MASK); i2c_dw_disable_int(dev); @@ -833,7 +898,7 @@ tx_aborted: void i2c_dw_disable(struct dw_i2c_dev *dev) { /* Disable controller */ - __i2c_dw_enable(dev, false); + __i2c_dw_enable_and_wait(dev, false); /* Disable all interupts */ dw_writel(dev, 0, DW_IC_INTR_MASK); @@ -857,6 +922,7 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) { struct i2c_adapter *adap = &dev->adapter; int r; + u32 reg; init_completion(&dev->cmd_complete); @@ -864,6 +930,26 @@ int i2c_dw_probe(struct dw_i2c_dev *dev) if (r) return r; + r = i2c_dw_acquire_lock(dev); + if (r) + return r; + + /* + * Test if dynamic TAR update is enabled in this controller by writing + * to IC_10BITADDR_MASTER field in IC_CON: when it is enabled this + * field is read-only so it should not succeed + */ + reg = dw_readl(dev, DW_IC_CON); + dw_writel(dev, reg ^ DW_IC_CON_10BITADDR_MASTER, DW_IC_CON); + + if ((dw_readl(dev, DW_IC_CON) & DW_IC_CON_10BITADDR_MASTER) == + (reg & DW_IC_CON_10BITADDR_MASTER)) { + dev->dynamic_tar_update_enabled = true; + dev_dbg(dev->dev, "Dynamic TAR update enabled"); + } + + i2c_dw_release_lock(dev); + snprintf(adap->name, sizeof(adap->name), "Synopsys DesignWare I2C adapter"); adap->retries = 3; diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h index 38493a7142ad..0d44d2ae7d4c 100644 --- a/drivers/i2c/busses/i2c-designware-core.h +++ b/drivers/i2c/busses/i2c-designware-core.h @@ -26,6 +26,7 @@ #define DW_IC_CON_MASTER 0x1 #define DW_IC_CON_SPEED_STD 0x2 #define DW_IC_CON_SPEED_FAST 0x4 +#define DW_IC_CON_SPEED_HIGH 0x6 #define DW_IC_CON_SPEED_MASK 0x6 #define DW_IC_CON_10BITADDR_MASTER 0x10 #define DW_IC_CON_RESTART_EN 0x20 @@ -57,10 +58,15 @@ * @tx_fifo_depth: depth of the hardware tx fifo * @rx_fifo_depth: depth of the hardware rx fifo * @rx_outstanding: current master-rx elements in tx fifo + * @clk_freq: bus clock frequency * @ss_hcnt: standard speed HCNT value * @ss_lcnt: standard speed LCNT value * @fs_hcnt: fast speed HCNT value * @fs_lcnt: fast speed LCNT value + * @fp_hcnt: fast plus HCNT value + * @fp_lcnt: fast plus LCNT value + * @hs_hcnt: high speed HCNT value + * @hs_lcnt: high speed LCNT value * @acquire_lock: function to acquire a hardware lock on the bus * @release_lock: function to release a hardware lock on the bus * @pm_runtime_disabled: true if pm runtime is disabled @@ -96,6 +102,7 @@ struct dw_i2c_dev { unsigned int tx_fifo_depth; unsigned int rx_fifo_depth; int rx_outstanding; + u32 clk_freq; u32 sda_hold_time; u32 sda_falling_time; u32 scl_falling_time; @@ -103,9 +110,14 @@ struct dw_i2c_dev { u16 ss_lcnt; u16 fs_hcnt; u16 fs_lcnt; + u16 fp_hcnt; + u16 fp_lcnt; + u16 hs_hcnt; + u16 hs_lcnt; int (*acquire_lock)(struct dw_i2c_dev *dev); void (*release_lock)(struct dw_i2c_dev *dev); bool pm_runtime_disabled; + bool dynamic_tar_update_enabled; }; #define ACCESS_SWAP 0x00000001 diff --git a/drivers/i2c/busses/i2c-designware-platdrv.c b/drivers/i2c/busses/i2c-designware-platdrv.c index d656657b805c..0b42a12171f3 100644 --- a/drivers/i2c/busses/i2c-designware-platdrv.c +++ b/drivers/i2c/busses/i2c-designware-platdrv.c @@ -107,6 +107,8 @@ static int dw_i2c_acpi_configure(struct platform_device *pdev) dw_i2c_acpi_params(pdev, "SSCN", &dev->ss_hcnt, &dev->ss_lcnt, NULL); dw_i2c_acpi_params(pdev, "FMCN", &dev->fs_hcnt, &dev->fs_lcnt, &dev->sda_hold_time); + dw_i2c_acpi_params(pdev, "FPCN", &dev->fp_hcnt, &dev->fp_lcnt, NULL); + dw_i2c_acpi_params(pdev, "HSCN", &dev->hs_hcnt, &dev->hs_lcnt, NULL); id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev); if (id && id->driver_data) @@ -155,7 +157,7 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) struct i2c_adapter *adap; struct resource *mem; int irq, r; - u32 clk_freq, ht = 0; + u32 acpi_speed, ht = 0; irq = platform_get_irq(pdev, 0); if (irq < 0) @@ -175,10 +177,10 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dev); /* fast mode by default because of legacy reasons */ - clk_freq = 400000; + dev->clk_freq = 400000; if (pdata) { - clk_freq = pdata->i2c_scl_freq; + dev->clk_freq = pdata->i2c_scl_freq; } else { device_property_read_u32(&pdev->dev, "i2c-sda-hold-time-ns", &ht); @@ -187,17 +189,24 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) device_property_read_u32(&pdev->dev, "i2c-scl-falling-time-ns", &dev->scl_falling_time); device_property_read_u32(&pdev->dev, "clock-frequency", - &clk_freq); + &dev->clk_freq); } + acpi_speed = i2c_acpi_find_bus_speed(&pdev->dev); + if (acpi_speed) + dev->clk_freq = acpi_speed; + if (has_acpi_companion(&pdev->dev)) dw_i2c_acpi_configure(pdev); /* - * Only standard mode at 100kHz and fast mode at 400kHz are supported. + * Only standard mode at 100kHz, fast mode at 400kHz, + * fast mode plus at 1MHz and high speed mode at 3.4MHz are supported. */ - if (clk_freq != 100000 && clk_freq != 400000) { - dev_err(&pdev->dev, "Only 100kHz and 400kHz supported"); + if (dev->clk_freq != 100000 && dev->clk_freq != 400000 + && dev->clk_freq != 1000000 && dev->clk_freq != 3400000) { + dev_err(&pdev->dev, + "Only 100kHz, 400kHz, 1MHz and 3.4MHz supported"); return -EINVAL; } @@ -212,12 +221,20 @@ static int dw_i2c_plat_probe(struct platform_device *pdev) I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_I2C_BLOCK; - if (clk_freq == 100000) - dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | - DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_STD; - else - dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | - DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST; + + dev->master_cfg = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | + DW_IC_CON_RESTART_EN; + + switch (dev->clk_freq) { + case 100000: + dev->master_cfg |= DW_IC_CON_SPEED_STD; + break; + case 3400000: + dev->master_cfg |= DW_IC_CON_SPEED_HIGH; + break; + default: + dev->master_cfg |= DW_IC_CON_SPEED_FAST; + } dev->clk = devm_clk_get(&pdev->dev, NULL); if (!i2c_dw_plat_prepare_clk(dev, true)) { diff --git a/drivers/i2c/busses/i2c-diolan-u2c.c b/drivers/i2c/busses/i2c-diolan-u2c.c index b19a310bf9b3..f718ee4e3332 100644 --- a/drivers/i2c/busses/i2c-diolan-u2c.c +++ b/drivers/i2c/busses/i2c-diolan-u2c.c @@ -487,10 +487,8 @@ static int diolan_u2c_probe(struct usb_interface *interface, /* and finally attach to i2c layer */ ret = i2c_add_adapter(&dev->adapter); - if (ret < 0) { - dev_err(&interface->dev, "failed to add I2C adapter\n"); + if (ret < 0) goto error_free; - } dev_dbg(&interface->dev, "connected " DRIVER_NAME "\n"); diff --git a/drivers/i2c/busses/i2c-dln2.c b/drivers/i2c/busses/i2c-dln2.c index f2eb4f76591f..8acda2aa1558 100644 --- a/drivers/i2c/busses/i2c-dln2.c +++ b/drivers/i2c/busses/i2c-dln2.c @@ -228,10 +228,8 @@ static int dln2_i2c_probe(struct platform_device *pdev) /* and finally attach to i2c layer */ ret = i2c_add_adapter(&dln2->adapter); - if (ret < 0) { - dev_err(dev, "failed to add I2C adapter: %d\n", ret); + if (ret < 0) goto out_disable; - } return 0; diff --git a/drivers/i2c/busses/i2c-efm32.c b/drivers/i2c/busses/i2c-efm32.c index e253598d764c..aa336ba89aa3 100644 --- a/drivers/i2c/busses/i2c-efm32.c +++ b/drivers/i2c/busses/i2c-efm32.c @@ -438,7 +438,6 @@ static int efm32_i2c_probe(struct platform_device *pdev) ret = i2c_add_adapter(&ddata->adapter); if (ret) { - dev_err(&pdev->dev, "failed to add i2c adapter (%d)\n", ret); free_irq(ddata->irq, ddata); err_disable_clk: diff --git a/drivers/i2c/busses/i2c-exynos5.c b/drivers/i2c/busses/i2c-exynos5.c index c0e3ada02876..bea607149972 100644 --- a/drivers/i2c/busses/i2c-exynos5.c +++ b/drivers/i2c/busses/i2c-exynos5.c @@ -796,10 +796,8 @@ static int exynos5_i2c_probe(struct platform_device *pdev) exynos5_i2c_reset(i2c); ret = i2c_add_adapter(&i2c->adap); - if (ret < 0) { - dev_err(&pdev->dev, "failed to add bus to i2c core\n"); + if (ret < 0) goto err_clk; - } platform_set_drvdata(pdev, i2c); diff --git a/drivers/i2c/busses/i2c-hix5hd2.c b/drivers/i2c/busses/i2c-hix5hd2.c index 7c6966434ee7..ae7f3180f7e8 100644 --- a/drivers/i2c/busses/i2c-hix5hd2.c +++ b/drivers/i2c/busses/i2c-hix5hd2.c @@ -478,10 +478,8 @@ static int hix5hd2_i2c_probe(struct platform_device *pdev) pm_runtime_enable(priv->dev); ret = i2c_add_adapter(&priv->adap); - if (ret < 0) { - dev_err(&pdev->dev, "failed to add bus to i2c core\n"); + if (ret < 0) goto err_runtime; - } return ret; diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 5ef9b733d153..22a0ed4de80d 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -1613,7 +1613,6 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id) "SMBus I801 adapter at %04lx", priv->smba); err = i2c_add_adapter(&priv->adapter); if (err) { - dev_err(&dev->dev, "Failed to add SMBus adapter\n"); i801_acpi_remove(priv); return err; } diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c index cdaa7be2cd1b..412b91d255ad 100644 --- a/drivers/i2c/busses/i2c-ibm_iic.c +++ b/drivers/i2c/busses/i2c-ibm_iic.c @@ -751,10 +751,8 @@ static int iic_probe(struct platform_device *ofdev) adap->timeout = HZ; ret = i2c_add_adapter(adap); - if (ret < 0) { - dev_err(&ofdev->dev, "failed to register i2c adapter\n"); + if (ret < 0) goto error_cleanup; - } dev_info(&ofdev->dev, "using %s mode\n", dev->fast_mode ? "fast (400 kHz)" : "standard (100 kHz)"); diff --git a/drivers/i2c/busses/i2c-img-scb.c b/drivers/i2c/busses/i2c-img-scb.c index ea20425b6972..db8e8b40569d 100644 --- a/drivers/i2c/busses/i2c-img-scb.c +++ b/drivers/i2c/busses/i2c-img-scb.c @@ -1394,10 +1394,8 @@ static int img_i2c_probe(struct platform_device *pdev) goto disable_clk; ret = i2c_add_numbered_adapter(&i2c->adap); - if (ret < 0) { - dev_err(&pdev->dev, "failed to add adapter\n"); + if (ret < 0) goto disable_clk; - } return 0; diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 1844bc9f7cd5..cb11eee95673 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1129,10 +1129,8 @@ static int i2c_imx_probe(struct platform_device *pdev) /* Add I2C adapter */ ret = i2c_add_numbered_adapter(&i2c_imx->adapter); - if (ret < 0) { - dev_err(&pdev->dev, "registration failed\n"); + if (ret < 0) goto rpm_disable; - } pm_runtime_mark_last_busy(&pdev->dev); pm_runtime_put_autosuspend(&pdev->dev); diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c index c2f25f19d76f..0cf1379f4e80 100644 --- a/drivers/i2c/busses/i2c-isch.c +++ b/drivers/i2c/busses/i2c-isch.c @@ -288,10 +288,8 @@ static int smbus_sch_probe(struct platform_device *dev) "SMBus SCH adapter at %04x", sch_smba); retval = i2c_add_adapter(&sch_adapter); - if (retval) { - dev_err(&dev->dev, "Couldn't register adapter!\n"); + if (retval) sch_smba = 0; - } return retval; } diff --git a/drivers/i2c/busses/i2c-ismt.c b/drivers/i2c/busses/i2c-ismt.c index 1c8707710098..f573448d2132 100644 --- a/drivers/i2c/busses/i2c-ismt.c +++ b/drivers/i2c/busses/i2c-ismt.c @@ -922,10 +922,8 @@ ismt_probe(struct pci_dev *pdev, const struct pci_device_id *id) return err; err = i2c_add_adapter(&priv->adapter); - if (err) { - dev_err(&pdev->dev, "Failed to add SMBus iSMT adapter\n"); + if (err) return -ENODEV; - } return 0; } diff --git a/drivers/i2c/busses/i2c-jz4780.c b/drivers/i2c/busses/i2c-jz4780.c index cd9872594fe2..b8ea62105f42 100644 --- a/drivers/i2c/busses/i2c-jz4780.c +++ b/drivers/i2c/busses/i2c-jz4780.c @@ -798,10 +798,8 @@ static int jz4780_i2c_probe(struct platform_device *pdev) goto err; ret = i2c_add_adapter(&i2c->adap); - if (ret < 0) { - dev_err(&pdev->dev, "Failed to add bus\n"); + if (ret < 0) goto err; - } return 0; diff --git a/drivers/i2c/busses/i2c-lpc2k.c b/drivers/i2c/busses/i2c-lpc2k.c index 586a15205e61..9b1fef455a89 100644 --- a/drivers/i2c/busses/i2c-lpc2k.c +++ b/drivers/i2c/busses/i2c-lpc2k.c @@ -432,10 +432,8 @@ static int i2c_lpc2k_probe(struct platform_device *pdev) i2c->adap.dev.of_node = pdev->dev.of_node; ret = i2c_add_adapter(&i2c->adap); - if (ret < 0) { - dev_err(&pdev->dev, "failed to add adapter!\n"); + if (ret < 0) goto fail_clk; - } dev_info(&pdev->dev, "LPC2K I2C adapter\n"); diff --git a/drivers/i2c/busses/i2c-meson.c b/drivers/i2c/busses/i2c-meson.c index 76e28980904f..8570e6dbd0db 100644 --- a/drivers/i2c/busses/i2c-meson.c +++ b/drivers/i2c/busses/i2c-meson.c @@ -453,7 +453,6 @@ static int meson_i2c_probe(struct platform_device *pdev) ret = i2c_add_adapter(&i2c->adap); if (ret < 0) { - dev_err(&pdev->dev, "can't register adapter\n"); clk_unprepare(i2c->clk); return ret; } diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index 48ecffecc0ed..565a49a0c564 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -737,10 +737,8 @@ static int fsl_i2c_probe(struct platform_device *op) i2c->adap.dev.of_node = of_node_get(op->dev.of_node); result = i2c_add_adapter(&i2c->adap); - if (result < 0) { - dev_err(i2c->dev, "failed to add adapter\n"); + if (result < 0) goto fail_add; - } return result; diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index d9373e60be8a..4a7d9bc2142b 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -786,10 +786,8 @@ static int mtk_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(&i2c->adap, i2c); ret = i2c_add_adapter(&i2c->adap); - if (ret) { - dev_err(&pdev->dev, "Failed to add i2c bus to i2c core\n"); + if (ret) return ret; - } platform_set_drvdata(pdev, i2c); diff --git a/drivers/i2c/busses/i2c-mxs.c b/drivers/i2c/busses/i2c-mxs.c index 033846cdf266..5738556b6aac 100644 --- a/drivers/i2c/busses/i2c-mxs.c +++ b/drivers/i2c/busses/i2c-mxs.c @@ -868,7 +868,6 @@ static int mxs_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(adap, i2c); err = i2c_add_numbered_adapter(adap); if (err) { - dev_err(dev, "Failed to add adapter (%d)\n", err); writel(MXS_I2C_CTRL0_SFTRST, i2c->regs + MXS_I2C_CTRL0_SET); return err; diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c index 42fcc9458432..374b35e7e450 100644 --- a/drivers/i2c/busses/i2c-nforce2.c +++ b/drivers/i2c/busses/i2c-nforce2.c @@ -366,7 +366,6 @@ static int nforce2_probe_smb(struct pci_dev *dev, int bar, int alt_reg, error = i2c_add_adapter(&smbus->adapter); if (error) { - dev_err(&smbus->adapter.dev, "Failed to register adapter.\n"); release_region(smbus->base, smbus->size); return error; } diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index bcd17e8cbcb4..da6609d62848 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -1046,10 +1046,8 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) adap->name, dev->virtbase); ret = i2c_add_adapter(adap); - if (ret) { - dev_err(&adev->dev, "failed to add adapter\n"); + if (ret) goto err_no_adap; - } pm_runtime_put(&adev->dev); diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c index ac88a524143e..34f1889a4073 100644 --- a/drivers/i2c/busses/i2c-ocores.c +++ b/drivers/i2c/busses/i2c-ocores.c @@ -494,10 +494,8 @@ static int ocores_i2c_probe(struct platform_device *pdev) /* add i2c adapter to i2c tree */ ret = i2c_add_adapter(&i2c->adap); - if (ret) { - dev_err(&pdev->dev, "Failed to add adapter\n"); + if (ret) goto err_clk; - } /* add in known devices to the bus */ if (pdata) { diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon-core.c index 30ae35146723..f45ea5eef513 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon-core.c @@ -4,280 +4,127 @@ * * Portions Copyright (C) 2010 - 2016 Cavium, Inc. * - * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors. + * This file contains the shared part of the driver for the i2c adapter in + * Cavium Networks' OCTEON processors and ThunderX SOCs. * * This file is licensed under the terms of the GNU General Public * License version 2. This program is licensed "as is" without any * warranty of any kind, whether express or implied. */ -#include <linux/atomic.h> -#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/delay.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/i2c.h> -#include <linux/io.h> -#include <linux/of.h> - -#include <asm/octeon/octeon.h> - -#define DRV_NAME "i2c-octeon" - -/* Register offsets */ -#define SW_TWSI 0x00 -#define TWSI_INT 0x10 -#define SW_TWSI_EXT 0x18 - -/* Controller command patterns */ -#define SW_TWSI_V BIT_ULL(63) /* Valid bit */ -#define SW_TWSI_EIA BIT_ULL(61) /* Extended internal address */ -#define SW_TWSI_R BIT_ULL(56) /* Result or read bit */ -#define SW_TWSI_SOVR BIT_ULL(55) /* Size override */ -#define SW_TWSI_SIZE_SHIFT 52 -#define SW_TWSI_ADDR_SHIFT 40 -#define SW_TWSI_IA_SHIFT 32 /* Internal address */ - -/* Controller opcode word (bits 60:57) */ -#define SW_TWSI_OP_SHIFT 57 -#define SW_TWSI_OP_7 (0ULL << SW_TWSI_OP_SHIFT) -#define SW_TWSI_OP_7_IA (1ULL << SW_TWSI_OP_SHIFT) -#define SW_TWSI_OP_10 (2ULL << SW_TWSI_OP_SHIFT) -#define SW_TWSI_OP_10_IA (3ULL << SW_TWSI_OP_SHIFT) -#define SW_TWSI_OP_TWSI_CLK (4ULL << SW_TWSI_OP_SHIFT) -#define SW_TWSI_OP_EOP (6ULL << SW_TWSI_OP_SHIFT) /* Extended opcode */ - -/* Controller extended opcode word (bits 34:32) */ -#define SW_TWSI_EOP_SHIFT 32 -#define SW_TWSI_EOP_TWSI_DATA (SW_TWSI_OP_EOP | 1ULL << SW_TWSI_EOP_SHIFT) -#define SW_TWSI_EOP_TWSI_CTL (SW_TWSI_OP_EOP | 2ULL << SW_TWSI_EOP_SHIFT) -#define SW_TWSI_EOP_TWSI_CLKCTL (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) -#define SW_TWSI_EOP_TWSI_STAT (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) -#define SW_TWSI_EOP_TWSI_RST (SW_TWSI_OP_EOP | 7ULL << SW_TWSI_EOP_SHIFT) - -/* Controller command and status bits */ -#define TWSI_CTL_CE 0x80 /* High level controller enable */ -#define TWSI_CTL_ENAB 0x40 /* Bus enable */ -#define TWSI_CTL_STA 0x20 /* Master-mode start, HW clears when done */ -#define TWSI_CTL_STP 0x10 /* Master-mode stop, HW clears when done */ -#define TWSI_CTL_IFLG 0x08 /* HW event, SW writes 0 to ACK */ -#define TWSI_CTL_AAK 0x04 /* Assert ACK */ - -/* Status values */ -#define STAT_ERROR 0x00 -#define STAT_START 0x08 -#define STAT_REP_START 0x10 -#define STAT_TXADDR_ACK 0x18 -#define STAT_TXADDR_NAK 0x20 -#define STAT_TXDATA_ACK 0x28 -#define STAT_TXDATA_NAK 0x30 -#define STAT_LOST_ARB_38 0x38 -#define STAT_RXADDR_ACK 0x40 -#define STAT_RXADDR_NAK 0x48 -#define STAT_RXDATA_ACK 0x50 -#define STAT_RXDATA_NAK 0x58 -#define STAT_SLAVE_60 0x60 -#define STAT_LOST_ARB_68 0x68 -#define STAT_SLAVE_70 0x70 -#define STAT_LOST_ARB_78 0x78 -#define STAT_SLAVE_80 0x80 -#define STAT_SLAVE_88 0x88 -#define STAT_GENDATA_ACK 0x90 -#define STAT_GENDATA_NAK 0x98 -#define STAT_SLAVE_A0 0xA0 -#define STAT_SLAVE_A8 0xA8 -#define STAT_LOST_ARB_B0 0xB0 -#define STAT_SLAVE_LOST 0xB8 -#define STAT_SLAVE_NAK 0xC0 -#define STAT_SLAVE_ACK 0xC8 -#define STAT_AD2W_ACK 0xD0 -#define STAT_AD2W_NAK 0xD8 -#define STAT_IDLE 0xF8 - -/* TWSI_INT values */ -#define TWSI_INT_ST_INT BIT_ULL(0) -#define TWSI_INT_TS_INT BIT_ULL(1) -#define TWSI_INT_CORE_INT BIT_ULL(2) -#define TWSI_INT_ST_EN BIT_ULL(4) -#define TWSI_INT_TS_EN BIT_ULL(5) -#define TWSI_INT_CORE_EN BIT_ULL(6) -#define TWSI_INT_SDA_OVR BIT_ULL(8) -#define TWSI_INT_SCL_OVR BIT_ULL(9) -#define TWSI_INT_SDA BIT_ULL(10) -#define TWSI_INT_SCL BIT_ULL(11) - -#define I2C_OCTEON_EVENT_WAIT 80 /* microseconds */ - -struct octeon_i2c { - wait_queue_head_t queue; - struct i2c_adapter adap; - int irq; - int hlc_irq; /* For cn7890 only */ - u32 twsi_freq; - int sys_freq; - void __iomem *twsi_base; - struct device *dev; - bool hlc_enabled; - bool broken_irq_mode; - bool broken_irq_check; - void (*int_enable)(struct octeon_i2c *); - void (*int_disable)(struct octeon_i2c *); - void (*hlc_int_enable)(struct octeon_i2c *); - void (*hlc_int_disable)(struct octeon_i2c *); - atomic_t int_enable_cnt; - atomic_t hlc_int_enable_cnt; -}; - -static void octeon_i2c_writeq_flush(u64 val, void __iomem *addr) -{ - __raw_writeq(val, addr); - __raw_readq(addr); /* wait for write to land */ -} - -/** - * octeon_i2c_reg_write - write an I2C core register - * @i2c: The struct octeon_i2c - * @eop_reg: Register selector - * @data: Value to be written - * - * The I2C core registers are accessed indirectly via the SW_TWSI CSR. - */ -static void octeon_i2c_reg_write(struct octeon_i2c *i2c, u64 eop_reg, u8 data) -{ - u64 tmp; - __raw_writeq(SW_TWSI_V | eop_reg | data, i2c->twsi_base + SW_TWSI); - do { - tmp = __raw_readq(i2c->twsi_base + SW_TWSI); - } while ((tmp & SW_TWSI_V) != 0); -} - -#define octeon_i2c_ctl_write(i2c, val) \ - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, val) -#define octeon_i2c_data_write(i2c, val) \ - octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_DATA, val) +#include "i2c-octeon-core.h" -/** - * octeon_i2c_reg_read - read lower bits of an I2C core register - * @i2c: The struct octeon_i2c - * @eop_reg: Register selector - * - * Returns the data. - * - * The I2C core registers are accessed indirectly via the SW_TWSI CSR. - */ -static u8 octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg) +/* interrupt service routine */ +irqreturn_t octeon_i2c_isr(int irq, void *dev_id) { - u64 tmp; + struct octeon_i2c *i2c = dev_id; - __raw_writeq(SW_TWSI_V | eop_reg | SW_TWSI_R, i2c->twsi_base + SW_TWSI); - do { - tmp = __raw_readq(i2c->twsi_base + SW_TWSI); - } while ((tmp & SW_TWSI_V) != 0); + i2c->int_disable(i2c); + wake_up(&i2c->queue); - return tmp & 0xFF; + return IRQ_HANDLED; } -#define octeon_i2c_ctl_read(i2c) \ - octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_CTL) -#define octeon_i2c_data_read(i2c) \ - octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_DATA) -#define octeon_i2c_stat_read(i2c) \ - octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT) - -/** - * octeon_i2c_read_int - read the TWSI_INT register - * @i2c: The struct octeon_i2c - * - * Returns the value of the register. - */ -static u64 octeon_i2c_read_int(struct octeon_i2c *i2c) +static bool octeon_i2c_test_iflg(struct octeon_i2c *i2c) { - return __raw_readq(i2c->twsi_base + TWSI_INT); + return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG); } -/** - * octeon_i2c_write_int - write the TWSI_INT register - * @i2c: The struct octeon_i2c - * @data: Value to be written - */ -static void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) +static bool octeon_i2c_test_ready(struct octeon_i2c *i2c, bool *first) { - octeon_i2c_writeq_flush(data, i2c->twsi_base + TWSI_INT); -} + if (octeon_i2c_test_iflg(i2c)) + return true; -/** - * octeon_i2c_int_enable - enable the CORE interrupt - * @i2c: The struct octeon_i2c - * - * The interrupt will be asserted when there is non-STAT_IDLE state in - * the SW_TWSI_EOP_TWSI_STAT register. - */ -static void octeon_i2c_int_enable(struct octeon_i2c *i2c) -{ - octeon_i2c_write_int(i2c, TWSI_INT_CORE_EN); -} + if (*first) { + *first = false; + return false; + } -/* disable the CORE interrupt */ -static void octeon_i2c_int_disable(struct octeon_i2c *i2c) -{ - /* clear TS/ST/IFLG events */ - octeon_i2c_write_int(i2c, 0); + /* + * IRQ has signaled an event but IFLG hasn't changed. + * Sleep and retry once. + */ + usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT); + return octeon_i2c_test_iflg(i2c); } /** - * octeon_i2c_int_enable78 - enable the CORE interrupt + * octeon_i2c_wait - wait for the IFLG to be set * @i2c: The struct octeon_i2c * - * The interrupt will be asserted when there is non-STAT_IDLE state in the - * SW_TWSI_EOP_TWSI_STAT register. + * Returns 0 on success, otherwise a negative errno. */ -static void octeon_i2c_int_enable78(struct octeon_i2c *i2c) -{ - atomic_inc_return(&i2c->int_enable_cnt); - enable_irq(i2c->irq); -} - -static void __octeon_i2c_irq_disable(atomic_t *cnt, int irq) +static int octeon_i2c_wait(struct octeon_i2c *i2c) { - int count; + long time_left; + bool first = true; /* - * The interrupt can be disabled in two places, but we only - * want to make the disable_irq_nosync() call once, so keep - * track with the atomic variable. + * Some chip revisions don't assert the irq in the interrupt + * controller. So we must poll for the IFLG change. */ - count = atomic_dec_if_positive(cnt); - if (count >= 0) - disable_irq_nosync(irq); + if (i2c->broken_irq_mode) { + u64 end = get_jiffies_64() + i2c->adap.timeout; + + while (!octeon_i2c_test_iflg(i2c) && + time_before64(get_jiffies_64(), end)) + usleep_range(I2C_OCTEON_EVENT_WAIT / 2, I2C_OCTEON_EVENT_WAIT); + + return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; + } + + i2c->int_enable(i2c); + time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_ready(i2c, &first), + i2c->adap.timeout); + i2c->int_disable(i2c); + + if (i2c->broken_irq_check && !time_left && + octeon_i2c_test_iflg(i2c)) { + dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); + i2c->broken_irq_mode = true; + return 0; + } + + if (!time_left) + return -ETIMEDOUT; + + return 0; } -/* disable the CORE interrupt */ -static void octeon_i2c_int_disable78(struct octeon_i2c *i2c) +static bool octeon_i2c_hlc_test_valid(struct octeon_i2c *i2c) { - __octeon_i2c_irq_disable(&i2c->int_enable_cnt, i2c->irq); + return (__raw_readq(i2c->twsi_base + SW_TWSI(i2c)) & SW_TWSI_V) == 0; } -/** - * octeon_i2c_hlc_int_enable78 - enable the ST interrupt - * @i2c: The struct octeon_i2c - * - * The interrupt will be asserted when there is non-STAT_IDLE state in - * the SW_TWSI_EOP_TWSI_STAT register. - */ -static void octeon_i2c_hlc_int_enable78(struct octeon_i2c *i2c) +static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c, bool *first) { - atomic_inc_return(&i2c->hlc_int_enable_cnt); - enable_irq(i2c->hlc_irq); + /* check if valid bit is cleared */ + if (octeon_i2c_hlc_test_valid(i2c)) + return true; + + if (*first) { + *first = false; + return false; + } + + /* + * IRQ has signaled an event but valid bit isn't cleared. + * Sleep and retry once. + */ + usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT); + return octeon_i2c_hlc_test_valid(i2c); } -/* disable the ST interrupt */ -static void octeon_i2c_hlc_int_disable78(struct octeon_i2c *i2c) +static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c) { - __octeon_i2c_irq_disable(&i2c->hlc_int_enable_cnt, i2c->hlc_irq); + /* clear ST/TS events, listen for neither */ + octeon_i2c_write_int(i2c, TWSI_INT_ST_INT | TWSI_INT_TS_INT); } /* @@ -321,83 +168,41 @@ static void octeon_i2c_hlc_disable(struct octeon_i2c *i2c) octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); } -/* interrupt service routine */ -static irqreturn_t octeon_i2c_isr(int irq, void *dev_id) -{ - struct octeon_i2c *i2c = dev_id; - - i2c->int_disable(i2c); - wake_up(&i2c->queue); - - return IRQ_HANDLED; -} - -/* HLC interrupt service routine */ -static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id) -{ - struct octeon_i2c *i2c = dev_id; - - i2c->hlc_int_disable(i2c); - wake_up(&i2c->queue); - - return IRQ_HANDLED; -} - -static bool octeon_i2c_test_iflg(struct octeon_i2c *i2c) -{ - return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG); -} - -static bool octeon_i2c_test_ready(struct octeon_i2c *i2c, bool *first) -{ - if (octeon_i2c_test_iflg(i2c)) - return true; - - if (*first) { - *first = false; - return false; - } - - /* - * IRQ has signaled an event but IFLG hasn't changed. - * Sleep and retry once. - */ - usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT); - return octeon_i2c_test_iflg(i2c); -} - /** - * octeon_i2c_wait - wait for the IFLG to be set + * octeon_i2c_hlc_wait - wait for an HLC operation to complete * @i2c: The struct octeon_i2c * - * Returns 0 on success, otherwise a negative errno. + * Returns 0 on success, otherwise -ETIMEDOUT. */ -static int octeon_i2c_wait(struct octeon_i2c *i2c) +static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { - long time_left; - bool first = 1; + bool first = true; + int time_left; /* - * Some chip revisions don't assert the irq in the interrupt - * controller. So we must poll for the IFLG change. + * Some cn38xx boards don't assert the irq in the interrupt + * controller. So we must poll for the valid bit change. */ if (i2c->broken_irq_mode) { u64 end = get_jiffies_64() + i2c->adap.timeout; - while (!octeon_i2c_test_iflg(i2c) && + while (!octeon_i2c_hlc_test_valid(i2c) && time_before64(get_jiffies_64(), end)) usleep_range(I2C_OCTEON_EVENT_WAIT / 2, I2C_OCTEON_EVENT_WAIT); - return octeon_i2c_test_iflg(i2c) ? 0 : -ETIMEDOUT; + return octeon_i2c_hlc_test_valid(i2c) ? 0 : -ETIMEDOUT; } - i2c->int_enable(i2c); - time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_ready(i2c, &first), + i2c->hlc_int_enable(i2c); + time_left = wait_event_timeout(i2c->queue, + octeon_i2c_hlc_test_ready(i2c, &first), i2c->adap.timeout); - i2c->int_disable(i2c); + i2c->hlc_int_disable(i2c); + if (!time_left) + octeon_i2c_hlc_int_clear(i2c); if (i2c->broken_irq_check && !time_left && - octeon_i2c_test_iflg(i2c)) { + octeon_i2c_hlc_test_valid(i2c)) { dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); i2c->broken_irq_mode = true; return 0; @@ -405,7 +210,6 @@ static int octeon_i2c_wait(struct octeon_i2c *i2c) if (!time_left) return -ETIMEDOUT; - return 0; } @@ -470,83 +274,155 @@ static int octeon_i2c_check_status(struct octeon_i2c *i2c, int final_read) } } -static bool octeon_i2c_hlc_test_valid(struct octeon_i2c *i2c) +static int octeon_i2c_recovery(struct octeon_i2c *i2c) { - return (__raw_readq(i2c->twsi_base + SW_TWSI) & SW_TWSI_V) == 0; + int ret; + + ret = i2c_recover_bus(&i2c->adap); + if (ret) + /* recover failed, try hardware re-init */ + ret = octeon_i2c_init_lowlevel(i2c); + return ret; } -static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c, bool *first) +/** + * octeon_i2c_start - send START to the bus + * @i2c: The struct octeon_i2c + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_start(struct octeon_i2c *i2c) { - /* check if valid bit is cleared */ - if (octeon_i2c_hlc_test_valid(i2c)) - return true; + int ret; + u8 stat; - if (*first) { - *first = false; - return false; - } + octeon_i2c_hlc_disable(i2c); - /* - * IRQ has signaled an event but valid bit isn't cleared. - * Sleep and retry once. - */ - usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT); - return octeon_i2c_hlc_test_valid(i2c); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); + ret = octeon_i2c_wait(i2c); + if (ret) + goto error; + + stat = octeon_i2c_stat_read(i2c); + if (stat == STAT_START || stat == STAT_REP_START) + /* START successful, bail out */ + return 0; + +error: + /* START failed, try to recover */ + ret = octeon_i2c_recovery(i2c); + return (ret) ? ret : -EAGAIN; } -static void octeon_i2c_hlc_int_enable(struct octeon_i2c *i2c) +/* send STOP to the bus */ +static void octeon_i2c_stop(struct octeon_i2c *i2c) { - octeon_i2c_write_int(i2c, TWSI_INT_ST_EN); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STP); } -static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c) +/** + * octeon_i2c_read - receive data from the bus via low-level controller + * @i2c: The struct octeon_i2c + * @target: Target address + * @data: Pointer to the location to store the data + * @rlength: Length of the data + * @recv_len: flag for length byte + * + * The address is sent over the bus, then the data is read. + * + * Returns 0 on success, otherwise a negative errno. + */ +static int octeon_i2c_read(struct octeon_i2c *i2c, int target, + u8 *data, u16 *rlength, bool recv_len) { - /* clear ST/TS events, listen for neither */ - octeon_i2c_write_int(i2c, TWSI_INT_ST_INT | TWSI_INT_TS_INT); + int i, result, length = *rlength; + bool final_read = false; + + octeon_i2c_data_write(i2c, (target << 1) | 1); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + /* address OK ? */ + result = octeon_i2c_check_status(i2c, false); + if (result) + return result; + + for (i = 0; i < length; i++) { + /* + * For the last byte to receive TWSI_CTL_AAK must not be set. + * + * A special case is I2C_M_RECV_LEN where we don't know the + * additional length yet. If recv_len is set we assume we're + * not reading the final byte and therefore need to set + * TWSI_CTL_AAK. + */ + if ((i + 1 == length) && !(recv_len && i == 0)) + final_read = true; + + /* clear iflg to allow next event */ + if (final_read) + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); + else + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_AAK); + + result = octeon_i2c_wait(i2c); + if (result) + return result; + + data[i] = octeon_i2c_data_read(i2c); + if (recv_len && i == 0) { + if (data[i] > I2C_SMBUS_BLOCK_MAX + 1) + return -EPROTO; + length += data[i]; + } + + result = octeon_i2c_check_status(i2c, final_read); + if (result) + return result; + } + *rlength = length; + return 0; } /** - * octeon_i2c_hlc_wait - wait for an HLC operation to complete + * octeon_i2c_write - send data to the bus via low-level controller * @i2c: The struct octeon_i2c + * @target: Target address + * @data: Pointer to the data to be sent + * @length: Length of the data * - * Returns 0 on success, otherwise -ETIMEDOUT. + * The address is sent over the bus, then the data. + * + * Returns 0 on success, otherwise a negative errno. */ -static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) +static int octeon_i2c_write(struct octeon_i2c *i2c, int target, + const u8 *data, int length) { - bool first = 1; - int time_left; + int i, result; - /* - * Some cn38xx boards don't assert the irq in the interrupt - * controller. So we must poll for the valid bit change. - */ - if (i2c->broken_irq_mode) { - u64 end = get_jiffies_64() + i2c->adap.timeout; + octeon_i2c_data_write(i2c, target << 1); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); - while (!octeon_i2c_hlc_test_valid(i2c) && - time_before64(get_jiffies_64(), end)) - usleep_range(I2C_OCTEON_EVENT_WAIT / 2, I2C_OCTEON_EVENT_WAIT); + result = octeon_i2c_wait(i2c); + if (result) + return result; - return octeon_i2c_hlc_test_valid(i2c) ? 0 : -ETIMEDOUT; - } + for (i = 0; i < length; i++) { + result = octeon_i2c_check_status(i2c, false); + if (result) + return result; - i2c->hlc_int_enable(i2c); - time_left = wait_event_timeout(i2c->queue, - octeon_i2c_hlc_test_ready(i2c, &first), - i2c->adap.timeout); - i2c->hlc_int_disable(i2c); - if (!time_left) - octeon_i2c_hlc_int_clear(i2c); + octeon_i2c_data_write(i2c, data[i]); + octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); - if (i2c->broken_irq_check && !time_left && - octeon_i2c_hlc_test_valid(i2c)) { - dev_err(i2c->dev, "broken irq connection detected, switching to polling mode.\n"); - i2c->broken_irq_mode = true; - return 0; + result = octeon_i2c_wait(i2c); + if (result) + return result; } - if (!time_left) - return -ETIMEDOUT; return 0; } @@ -570,12 +446,12 @@ static int octeon_i2c_hlc_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) else cmd |= SW_TWSI_OP_7; - octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI); + octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI(i2c)); ret = octeon_i2c_hlc_wait(i2c); if (ret) goto err; - cmd = __raw_readq(i2c->twsi_base + SW_TWSI); + cmd = __raw_readq(i2c->twsi_base + SW_TWSI(i2c)); if ((cmd & SW_TWSI_R) == 0) return -EAGAIN; @@ -583,7 +459,7 @@ static int octeon_i2c_hlc_read(struct octeon_i2c *i2c, struct i2c_msg *msgs) msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff; if (msgs[0].len > 4) { - cmd = __raw_readq(i2c->twsi_base + SW_TWSI_EXT); + cmd = __raw_readq(i2c->twsi_base + SW_TWSI_EXT(i2c)); for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--) msgs[0].buf[j] = (cmd >> (8 * i)) & 0xff; } @@ -620,15 +496,15 @@ static int octeon_i2c_hlc_write(struct octeon_i2c *i2c, struct i2c_msg *msgs) for (i = 0; i < msgs[0].len - 4 && i < 4; i++, j--) ext |= (u64)msgs[0].buf[j] << (8 * i); - octeon_i2c_writeq_flush(ext, i2c->twsi_base + SW_TWSI_EXT); + octeon_i2c_writeq_flush(ext, i2c->twsi_base + SW_TWSI_EXT(i2c)); } - octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI); + octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI(i2c)); ret = octeon_i2c_hlc_wait(i2c); if (ret) goto err; - cmd = __raw_readq(i2c->twsi_base + SW_TWSI); + cmd = __raw_readq(i2c->twsi_base + SW_TWSI(i2c)); if ((cmd & SW_TWSI_R) == 0) return -EAGAIN; @@ -663,19 +539,19 @@ static int octeon_i2c_hlc_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs cmd |= SW_TWSI_EIA; ext = (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT; cmd |= (u64)msgs[0].buf[1] << SW_TWSI_IA_SHIFT; - octeon_i2c_writeq_flush(ext, i2c->twsi_base + SW_TWSI_EXT); + octeon_i2c_writeq_flush(ext, i2c->twsi_base + SW_TWSI_EXT(i2c)); } else { cmd |= (u64)msgs[0].buf[0] << SW_TWSI_IA_SHIFT; } octeon_i2c_hlc_int_clear(i2c); - octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI); + octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI(i2c)); ret = octeon_i2c_hlc_wait(i2c); if (ret) goto err; - cmd = __raw_readq(i2c->twsi_base + SW_TWSI); + cmd = __raw_readq(i2c->twsi_base + SW_TWSI(i2c)); if ((cmd & SW_TWSI_R) == 0) return -EAGAIN; @@ -683,7 +559,7 @@ static int octeon_i2c_hlc_comp_read(struct octeon_i2c *i2c, struct i2c_msg *msgs msgs[1].buf[j] = (cmd >> (8 * i)) & 0xff; if (msgs[1].len > 4) { - cmd = __raw_readq(i2c->twsi_base + SW_TWSI_EXT); + cmd = __raw_readq(i2c->twsi_base + SW_TWSI_EXT(i2c)); for (i = 0; i < msgs[1].len - 4 && i < 4; i++, j--) msgs[1].buf[j] = (cmd >> (8 * i)) & 0xff; } @@ -730,16 +606,16 @@ static int octeon_i2c_hlc_comp_write(struct octeon_i2c *i2c, struct i2c_msg *msg set_ext = true; } if (set_ext) - octeon_i2c_writeq_flush(ext, i2c->twsi_base + SW_TWSI_EXT); + octeon_i2c_writeq_flush(ext, i2c->twsi_base + SW_TWSI_EXT(i2c)); octeon_i2c_hlc_int_clear(i2c); - octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI); + octeon_i2c_writeq_flush(cmd, i2c->twsi_base + SW_TWSI(i2c)); ret = octeon_i2c_hlc_wait(i2c); if (ret) goto err; - cmd = __raw_readq(i2c->twsi_base + SW_TWSI); + cmd = __raw_readq(i2c->twsi_base + SW_TWSI(i2c)); if ((cmd & SW_TWSI_R) == 0) return -EAGAIN; @@ -749,8 +625,68 @@ err: return ret; } +/** + * octeon_i2c_xfer - The driver's master_xfer function + * @adap: Pointer to the i2c_adapter structure + * @msgs: Pointer to the messages to be processed + * @num: Length of the MSGS array + * + * Returns the number of messages processed, or a negative errno on failure. + */ +int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) +{ + struct octeon_i2c *i2c = i2c_get_adapdata(adap); + int i, ret = 0; + + if (num == 1) { + if (msgs[0].len > 0 && msgs[0].len <= 8) { + if (msgs[0].flags & I2C_M_RD) + ret = octeon_i2c_hlc_read(i2c, msgs); + else + ret = octeon_i2c_hlc_write(i2c, msgs); + goto out; + } + } else if (num == 2) { + if ((msgs[0].flags & I2C_M_RD) == 0 && + (msgs[1].flags & I2C_M_RECV_LEN) == 0 && + msgs[0].len > 0 && msgs[0].len <= 2 && + msgs[1].len > 0 && msgs[1].len <= 8 && + msgs[0].addr == msgs[1].addr) { + if (msgs[1].flags & I2C_M_RD) + ret = octeon_i2c_hlc_comp_read(i2c, msgs); + else + ret = octeon_i2c_hlc_comp_write(i2c, msgs); + goto out; + } + } + + for (i = 0; ret == 0 && i < num; i++) { + struct i2c_msg *pmsg = &msgs[i]; + + /* zero-length messages are not supported */ + if (!pmsg->len) { + ret = -EOPNOTSUPP; + break; + } + + ret = octeon_i2c_start(i2c); + if (ret) + return ret; + + if (pmsg->flags & I2C_M_RD) + ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf, + &pmsg->len, pmsg->flags & I2C_M_RECV_LEN); + else + ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf, + pmsg->len); + } + octeon_i2c_stop(i2c); +out: + return (ret != 0) ? ret : num; +} + /* calculate and set clock divisors */ -static void octeon_i2c_set_clock(struct octeon_i2c *i2c) +void octeon_i2c_set_clock(struct octeon_i2c *i2c) { int tclk, thp_base, inc, thp_idx, mdiv_idx, ndiv_idx, foscl, diff; int thp = 0x18, mdiv = 2, ndiv = 0, delta_hz = 1000000; @@ -791,7 +727,7 @@ static void octeon_i2c_set_clock(struct octeon_i2c *i2c) octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CLKCTL, (mdiv << 3) | ndiv); } -static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) +int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) { u8 status = 0; int tries; @@ -818,219 +754,6 @@ static int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c) return 0; } -static int octeon_i2c_recovery(struct octeon_i2c *i2c) -{ - int ret; - - ret = i2c_recover_bus(&i2c->adap); - if (ret) - /* recover failed, try hardware re-init */ - ret = octeon_i2c_init_lowlevel(i2c); - return ret; -} - -/** - * octeon_i2c_start - send START to the bus - * @i2c: The struct octeon_i2c - * - * Returns 0 on success, otherwise a negative errno. - */ -static int octeon_i2c_start(struct octeon_i2c *i2c) -{ - int ret; - u8 stat; - - octeon_i2c_hlc_disable(i2c); - - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STA); - ret = octeon_i2c_wait(i2c); - if (ret) - goto error; - - stat = octeon_i2c_stat_read(i2c); - if (stat == STAT_START || stat == STAT_REP_START) - /* START successful, bail out */ - return 0; - -error: - /* START failed, try to recover */ - ret = octeon_i2c_recovery(i2c); - return (ret) ? ret : -EAGAIN; -} - -/* send STOP to the bus */ -static void octeon_i2c_stop(struct octeon_i2c *i2c) -{ - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_STP); -} - -/** - * octeon_i2c_write - send data to the bus via low-level controller - * @i2c: The struct octeon_i2c - * @target: Target address - * @data: Pointer to the data to be sent - * @length: Length of the data - * - * The address is sent over the bus, then the data. - * - * Returns 0 on success, otherwise a negative errno. - */ -static int octeon_i2c_write(struct octeon_i2c *i2c, int target, - const u8 *data, int length) -{ - int i, result; - - octeon_i2c_data_write(i2c, target << 1); - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); - - result = octeon_i2c_wait(i2c); - if (result) - return result; - - for (i = 0; i < length; i++) { - result = octeon_i2c_check_status(i2c, false); - if (result) - return result; - - octeon_i2c_data_write(i2c, data[i]); - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); - - result = octeon_i2c_wait(i2c); - if (result) - return result; - } - - return 0; -} - -/** - * octeon_i2c_read - receive data from the bus via low-level controller - * @i2c: The struct octeon_i2c - * @target: Target address - * @data: Pointer to the location to store the data - * @rlength: Length of the data - * @recv_len: flag for length byte - * - * The address is sent over the bus, then the data is read. - * - * Returns 0 on success, otherwise a negative errno. - */ -static int octeon_i2c_read(struct octeon_i2c *i2c, int target, - u8 *data, u16 *rlength, bool recv_len) -{ - int i, result, length = *rlength; - bool final_read = false; - - octeon_i2c_data_write(i2c, (target << 1) | 1); - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); - - result = octeon_i2c_wait(i2c); - if (result) - return result; - - /* address OK ? */ - result = octeon_i2c_check_status(i2c, false); - if (result) - return result; - - for (i = 0; i < length; i++) { - /* - * For the last byte to receive TWSI_CTL_AAK must not be set. - * - * A special case is I2C_M_RECV_LEN where we don't know the - * additional length yet. If recv_len is set we assume we're - * not reading the final byte and therefore need to set - * TWSI_CTL_AAK. - */ - if ((i + 1 == length) && !(recv_len && i == 0)) - final_read = true; - - /* clear iflg to allow next event */ - if (final_read) - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB); - else - octeon_i2c_ctl_write(i2c, TWSI_CTL_ENAB | TWSI_CTL_AAK); - - result = octeon_i2c_wait(i2c); - if (result) - return result; - - data[i] = octeon_i2c_data_read(i2c); - if (recv_len && i == 0) { - if (data[i] > I2C_SMBUS_BLOCK_MAX + 1) - return -EPROTO; - length += data[i]; - } - - result = octeon_i2c_check_status(i2c, final_read); - if (result) - return result; - } - *rlength = length; - return 0; -} - -/** - * octeon_i2c_xfer - The driver's master_xfer function - * @adap: Pointer to the i2c_adapter structure - * @msgs: Pointer to the messages to be processed - * @num: Length of the MSGS array - * - * Returns the number of messages processed, or a negative errno on failure. - */ -static int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, - int num) -{ - struct octeon_i2c *i2c = i2c_get_adapdata(adap); - int i, ret = 0; - - if (num == 1) { - if (msgs[0].len > 0 && msgs[0].len <= 8) { - if (msgs[0].flags & I2C_M_RD) - ret = octeon_i2c_hlc_read(i2c, msgs); - else - ret = octeon_i2c_hlc_write(i2c, msgs); - goto out; - } - } else if (num == 2) { - if ((msgs[0].flags & I2C_M_RD) == 0 && - (msgs[1].flags & I2C_M_RECV_LEN) == 0 && - msgs[0].len > 0 && msgs[0].len <= 2 && - msgs[1].len > 0 && msgs[1].len <= 8 && - msgs[0].addr == msgs[1].addr) { - if (msgs[1].flags & I2C_M_RD) - ret = octeon_i2c_hlc_comp_read(i2c, msgs); - else - ret = octeon_i2c_hlc_comp_write(i2c, msgs); - goto out; - } - } - - for (i = 0; ret == 0 && i < num; i++) { - struct i2c_msg *pmsg = &msgs[i]; - - /* zero-length messages are not supported */ - if (!pmsg->len) { - ret = -EOPNOTSUPP; - break; - } - - ret = octeon_i2c_start(i2c); - if (ret) - return ret; - - if (pmsg->flags & I2C_M_RD) - ret = octeon_i2c_read(i2c, pmsg->addr, pmsg->buf, - &pmsg->len, pmsg->flags & I2C_M_RECV_LEN); - else - ret = octeon_i2c_write(i2c, pmsg->addr, pmsg->buf, - pmsg->len); - } - octeon_i2c_stop(i2c); -out: - return (ret != 0) ? ret : num; -} - static int octeon_i2c_get_scl(struct i2c_adapter *adap) { struct octeon_i2c *i2c = i2c_get_adapdata(adap); @@ -1077,7 +800,7 @@ static void octeon_i2c_unprepare_recovery(struct i2c_adapter *adap) octeon_i2c_write_int(i2c, 0); } -static struct i2c_bus_recovery_info octeon_i2c_recovery_info = { +struct i2c_bus_recovery_info octeon_i2c_recovery_info = { .recover_bus = i2c_generic_scl_recovery, .get_scl = octeon_i2c_get_scl, .set_scl = octeon_i2c_set_scl, @@ -1085,171 +808,3 @@ static struct i2c_bus_recovery_info octeon_i2c_recovery_info = { .prepare_recovery = octeon_i2c_prepare_recovery, .unprepare_recovery = octeon_i2c_unprepare_recovery, }; - -static u32 octeon_i2c_functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | - I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_SMBUS_BLOCK_PROC_CALL; -} - -static const struct i2c_algorithm octeon_i2c_algo = { - .master_xfer = octeon_i2c_xfer, - .functionality = octeon_i2c_functionality, -}; - -static struct i2c_adapter octeon_i2c_ops = { - .owner = THIS_MODULE, - .name = "OCTEON adapter", - .algo = &octeon_i2c_algo, -}; - -static int octeon_i2c_probe(struct platform_device *pdev) -{ - struct device_node *node = pdev->dev.of_node; - int irq, result = 0, hlc_irq = 0; - struct resource *res_mem; - struct octeon_i2c *i2c; - bool cn78xx_style; - - cn78xx_style = of_device_is_compatible(node, "cavium,octeon-7890-twsi"); - if (cn78xx_style) { - hlc_irq = platform_get_irq(pdev, 0); - if (hlc_irq < 0) - return hlc_irq; - - irq = platform_get_irq(pdev, 2); - if (irq < 0) - return irq; - } else { - /* All adaptors have an irq. */ - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return irq; - } - - i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); - if (!i2c) { - result = -ENOMEM; - goto out; - } - i2c->dev = &pdev->dev; - - res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); - i2c->twsi_base = devm_ioremap_resource(&pdev->dev, res_mem); - if (IS_ERR(i2c->twsi_base)) { - result = PTR_ERR(i2c->twsi_base); - goto out; - } - - /* - * "clock-rate" is a legacy binding, the official binding is - * "clock-frequency". Try the official one first and then - * fall back if it doesn't exist. - */ - if (of_property_read_u32(node, "clock-frequency", &i2c->twsi_freq) && - of_property_read_u32(node, "clock-rate", &i2c->twsi_freq)) { - dev_err(i2c->dev, - "no I2C 'clock-rate' or 'clock-frequency' property\n"); - result = -ENXIO; - goto out; - } - - i2c->sys_freq = octeon_get_io_clock_rate(); - - init_waitqueue_head(&i2c->queue); - - i2c->irq = irq; - - if (cn78xx_style) { - i2c->hlc_irq = hlc_irq; - - i2c->int_enable = octeon_i2c_int_enable78; - i2c->int_disable = octeon_i2c_int_disable78; - i2c->hlc_int_enable = octeon_i2c_hlc_int_enable78; - i2c->hlc_int_disable = octeon_i2c_hlc_int_disable78; - - irq_set_status_flags(i2c->irq, IRQ_NOAUTOEN); - irq_set_status_flags(i2c->hlc_irq, IRQ_NOAUTOEN); - - result = devm_request_irq(&pdev->dev, i2c->hlc_irq, - octeon_i2c_hlc_isr78, 0, - DRV_NAME, i2c); - if (result < 0) { - dev_err(i2c->dev, "failed to attach interrupt\n"); - goto out; - } - } else { - i2c->int_enable = octeon_i2c_int_enable; - i2c->int_disable = octeon_i2c_int_disable; - i2c->hlc_int_enable = octeon_i2c_hlc_int_enable; - i2c->hlc_int_disable = octeon_i2c_int_disable; - } - - result = devm_request_irq(&pdev->dev, i2c->irq, - octeon_i2c_isr, 0, DRV_NAME, i2c); - if (result < 0) { - dev_err(i2c->dev, "failed to attach interrupt\n"); - goto out; - } - - if (OCTEON_IS_MODEL(OCTEON_CN38XX)) - i2c->broken_irq_check = true; - - result = octeon_i2c_init_lowlevel(i2c); - if (result) { - dev_err(i2c->dev, "init low level failed\n"); - goto out; - } - - octeon_i2c_set_clock(i2c); - - i2c->adap = octeon_i2c_ops; - i2c->adap.timeout = msecs_to_jiffies(2); - i2c->adap.retries = 5; - i2c->adap.bus_recovery_info = &octeon_i2c_recovery_info; - i2c->adap.dev.parent = &pdev->dev; - i2c->adap.dev.of_node = node; - i2c_set_adapdata(&i2c->adap, i2c); - platform_set_drvdata(pdev, i2c); - - result = i2c_add_adapter(&i2c->adap); - if (result < 0) { - dev_err(i2c->dev, "failed to add adapter\n"); - goto out; - } - dev_info(i2c->dev, "probed\n"); - return 0; - -out: - return result; -}; - -static int octeon_i2c_remove(struct platform_device *pdev) -{ - struct octeon_i2c *i2c = platform_get_drvdata(pdev); - - i2c_del_adapter(&i2c->adap); - return 0; -}; - -static const struct of_device_id octeon_i2c_match[] = { - { .compatible = "cavium,octeon-3860-twsi", }, - { .compatible = "cavium,octeon-7890-twsi", }, - {}, -}; -MODULE_DEVICE_TABLE(of, octeon_i2c_match); - -static struct platform_driver octeon_i2c_driver = { - .probe = octeon_i2c_probe, - .remove = octeon_i2c_remove, - .driver = { - .name = DRV_NAME, - .of_match_table = octeon_i2c_match, - }, -}; - -module_platform_driver(octeon_i2c_driver); - -MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>"); -MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors"); -MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-octeon-core.h b/drivers/i2c/busses/i2c-octeon-core.h new file mode 100644 index 000000000000..87151ea74acd --- /dev/null +++ b/drivers/i2c/busses/i2c-octeon-core.h @@ -0,0 +1,211 @@ +#include <linux/atomic.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/i2c-smbus.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/pci.h> + +/* Controller command patterns */ +#define SW_TWSI_V BIT_ULL(63) /* Valid bit */ +#define SW_TWSI_EIA BIT_ULL(61) /* Extended internal address */ +#define SW_TWSI_R BIT_ULL(56) /* Result or read bit */ +#define SW_TWSI_SOVR BIT_ULL(55) /* Size override */ +#define SW_TWSI_SIZE_SHIFT 52 +#define SW_TWSI_ADDR_SHIFT 40 +#define SW_TWSI_IA_SHIFT 32 /* Internal address */ + +/* Controller opcode word (bits 60:57) */ +#define SW_TWSI_OP_SHIFT 57 +#define SW_TWSI_OP_7 (0ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_7_IA (1ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10 (2ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_10_IA (3ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_TWSI_CLK (4ULL << SW_TWSI_OP_SHIFT) +#define SW_TWSI_OP_EOP (6ULL << SW_TWSI_OP_SHIFT) /* Extended opcode */ + +/* Controller extended opcode word (bits 34:32) */ +#define SW_TWSI_EOP_SHIFT 32 +#define SW_TWSI_EOP_TWSI_DATA (SW_TWSI_OP_EOP | 1ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_CTL (SW_TWSI_OP_EOP | 2ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_CLKCTL (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_STAT (SW_TWSI_OP_EOP | 3ULL << SW_TWSI_EOP_SHIFT) +#define SW_TWSI_EOP_TWSI_RST (SW_TWSI_OP_EOP | 7ULL << SW_TWSI_EOP_SHIFT) + +/* Controller command and status bits */ +#define TWSI_CTL_CE 0x80 /* High level controller enable */ +#define TWSI_CTL_ENAB 0x40 /* Bus enable */ +#define TWSI_CTL_STA 0x20 /* Master-mode start, HW clears when done */ +#define TWSI_CTL_STP 0x10 /* Master-mode stop, HW clears when done */ +#define TWSI_CTL_IFLG 0x08 /* HW event, SW writes 0 to ACK */ +#define TWSI_CTL_AAK 0x04 /* Assert ACK */ + +/* Status values */ +#define STAT_ERROR 0x00 +#define STAT_START 0x08 +#define STAT_REP_START 0x10 +#define STAT_TXADDR_ACK 0x18 +#define STAT_TXADDR_NAK 0x20 +#define STAT_TXDATA_ACK 0x28 +#define STAT_TXDATA_NAK 0x30 +#define STAT_LOST_ARB_38 0x38 +#define STAT_RXADDR_ACK 0x40 +#define STAT_RXADDR_NAK 0x48 +#define STAT_RXDATA_ACK 0x50 +#define STAT_RXDATA_NAK 0x58 +#define STAT_SLAVE_60 0x60 +#define STAT_LOST_ARB_68 0x68 +#define STAT_SLAVE_70 0x70 +#define STAT_LOST_ARB_78 0x78 +#define STAT_SLAVE_80 0x80 +#define STAT_SLAVE_88 0x88 +#define STAT_GENDATA_ACK 0x90 +#define STAT_GENDATA_NAK 0x98 +#define STAT_SLAVE_A0 0xA0 +#define STAT_SLAVE_A8 0xA8 +#define STAT_LOST_ARB_B0 0xB0 +#define STAT_SLAVE_LOST 0xB8 +#define STAT_SLAVE_NAK 0xC0 +#define STAT_SLAVE_ACK 0xC8 +#define STAT_AD2W_ACK 0xD0 +#define STAT_AD2W_NAK 0xD8 +#define STAT_IDLE 0xF8 + +/* TWSI_INT values */ +#define TWSI_INT_ST_INT BIT_ULL(0) +#define TWSI_INT_TS_INT BIT_ULL(1) +#define TWSI_INT_CORE_INT BIT_ULL(2) +#define TWSI_INT_ST_EN BIT_ULL(4) +#define TWSI_INT_TS_EN BIT_ULL(5) +#define TWSI_INT_CORE_EN BIT_ULL(6) +#define TWSI_INT_SDA_OVR BIT_ULL(8) +#define TWSI_INT_SCL_OVR BIT_ULL(9) +#define TWSI_INT_SDA BIT_ULL(10) +#define TWSI_INT_SCL BIT_ULL(11) + +#define I2C_OCTEON_EVENT_WAIT 80 /* microseconds */ + +/* Register offsets */ +struct octeon_i2c_reg_offset { + unsigned int sw_twsi; + unsigned int twsi_int; + unsigned int sw_twsi_ext; +}; + +#define SW_TWSI(x) (x->roff.sw_twsi) +#define TWSI_INT(x) (x->roff.twsi_int) +#define SW_TWSI_EXT(x) (x->roff.sw_twsi_ext) + +struct octeon_i2c { + wait_queue_head_t queue; + struct i2c_adapter adap; + struct octeon_i2c_reg_offset roff; + struct clk *clk; + int irq; + int hlc_irq; /* For cn7890 only */ + u32 twsi_freq; + int sys_freq; + void __iomem *twsi_base; + struct device *dev; + bool hlc_enabled; + bool broken_irq_mode; + bool broken_irq_check; + void (*int_enable)(struct octeon_i2c *); + void (*int_disable)(struct octeon_i2c *); + void (*hlc_int_enable)(struct octeon_i2c *); + void (*hlc_int_disable)(struct octeon_i2c *); + atomic_t int_enable_cnt; + atomic_t hlc_int_enable_cnt; +#if IS_ENABLED(CONFIG_I2C_THUNDERX) + struct msix_entry i2c_msix; +#endif + struct i2c_smbus_alert_setup alert_data; + struct i2c_client *ara; +}; + +static inline void octeon_i2c_writeq_flush(u64 val, void __iomem *addr) +{ + __raw_writeq(val, addr); + __raw_readq(addr); /* wait for write to land */ +} + +/** + * octeon_i2c_reg_write - write an I2C core register + * @i2c: The struct octeon_i2c + * @eop_reg: Register selector + * @data: Value to be written + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static inline void octeon_i2c_reg_write(struct octeon_i2c *i2c, u64 eop_reg, u8 data) +{ + u64 tmp; + + __raw_writeq(SW_TWSI_V | eop_reg | data, i2c->twsi_base + SW_TWSI(i2c)); + do { + tmp = __raw_readq(i2c->twsi_base + SW_TWSI(i2c)); + } while ((tmp & SW_TWSI_V) != 0); +} + +#define octeon_i2c_ctl_write(i2c, val) \ + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_CTL, val) +#define octeon_i2c_data_write(i2c, val) \ + octeon_i2c_reg_write(i2c, SW_TWSI_EOP_TWSI_DATA, val) + +/** + * octeon_i2c_reg_read - read lower bits of an I2C core register + * @i2c: The struct octeon_i2c + * @eop_reg: Register selector + * + * Returns the data. + * + * The I2C core registers are accessed indirectly via the SW_TWSI CSR. + */ +static inline u8 octeon_i2c_reg_read(struct octeon_i2c *i2c, u64 eop_reg) +{ + u64 tmp; + + __raw_writeq(SW_TWSI_V | eop_reg | SW_TWSI_R, i2c->twsi_base + SW_TWSI(i2c)); + do { + tmp = __raw_readq(i2c->twsi_base + SW_TWSI(i2c)); + } while ((tmp & SW_TWSI_V) != 0); + + return tmp & 0xFF; +} + +#define octeon_i2c_ctl_read(i2c) \ + octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_CTL) +#define octeon_i2c_data_read(i2c) \ + octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_DATA) +#define octeon_i2c_stat_read(i2c) \ + octeon_i2c_reg_read(i2c, SW_TWSI_EOP_TWSI_STAT) + +/** + * octeon_i2c_read_int - read the TWSI_INT register + * @i2c: The struct octeon_i2c + * + * Returns the value of the register. + */ +static inline u64 octeon_i2c_read_int(struct octeon_i2c *i2c) +{ + return __raw_readq(i2c->twsi_base + TWSI_INT(i2c)); +} + +/** + * octeon_i2c_write_int - write the TWSI_INT register + * @i2c: The struct octeon_i2c + * @data: Value to be written + */ +static inline void octeon_i2c_write_int(struct octeon_i2c *i2c, u64 data) +{ + octeon_i2c_writeq_flush(data, i2c->twsi_base + TWSI_INT(i2c)); +} + +/* Prototypes */ +irqreturn_t octeon_i2c_isr(int irq, void *dev_id); +int octeon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num); +int octeon_i2c_init_lowlevel(struct octeon_i2c *i2c); +void octeon_i2c_set_clock(struct octeon_i2c *i2c); +extern struct i2c_bus_recovery_info octeon_i2c_recovery_info; diff --git a/drivers/i2c/busses/i2c-octeon-platdrv.c b/drivers/i2c/busses/i2c-octeon-platdrv.c new file mode 100644 index 000000000000..917524ce6890 --- /dev/null +++ b/drivers/i2c/busses/i2c-octeon-platdrv.c @@ -0,0 +1,286 @@ +/* + * (C) Copyright 2009-2010 + * Nokia Siemens Networks, michael.lawnick.ext@nsn.com + * + * Portions Copyright (C) 2010 - 2016 Cavium, Inc. + * + * This is a driver for the i2c adapter in Cavium Networks' OCTEON processors. + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/atomic.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/slab.h> + +#include <asm/octeon/octeon.h> +#include "i2c-octeon-core.h" + +#define DRV_NAME "i2c-octeon" + +/** + * octeon_i2c_int_enable - enable the CORE interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in + * the SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_write_int(i2c, TWSI_INT_CORE_EN); +} + +/* disable the CORE interrupt */ +static void octeon_i2c_int_disable(struct octeon_i2c *i2c) +{ + /* clear TS/ST/IFLG events */ + octeon_i2c_write_int(i2c, 0); +} + +/** + * octeon_i2c_int_enable78 - enable the CORE interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in the + * SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_int_enable78(struct octeon_i2c *i2c) +{ + atomic_inc_return(&i2c->int_enable_cnt); + enable_irq(i2c->irq); +} + +static void __octeon_i2c_irq_disable(atomic_t *cnt, int irq) +{ + int count; + + /* + * The interrupt can be disabled in two places, but we only + * want to make the disable_irq_nosync() call once, so keep + * track with the atomic variable. + */ + count = atomic_dec_if_positive(cnt); + if (count >= 0) + disable_irq_nosync(irq); +} + +/* disable the CORE interrupt */ +static void octeon_i2c_int_disable78(struct octeon_i2c *i2c) +{ + __octeon_i2c_irq_disable(&i2c->int_enable_cnt, i2c->irq); +} + +/** + * octeon_i2c_hlc_int_enable78 - enable the ST interrupt + * @i2c: The struct octeon_i2c + * + * The interrupt will be asserted when there is non-STAT_IDLE state in + * the SW_TWSI_EOP_TWSI_STAT register. + */ +static void octeon_i2c_hlc_int_enable78(struct octeon_i2c *i2c) +{ + atomic_inc_return(&i2c->hlc_int_enable_cnt); + enable_irq(i2c->hlc_irq); +} + +/* disable the ST interrupt */ +static void octeon_i2c_hlc_int_disable78(struct octeon_i2c *i2c) +{ + __octeon_i2c_irq_disable(&i2c->hlc_int_enable_cnt, i2c->hlc_irq); +} + +/* HLC interrupt service routine */ +static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id) +{ + struct octeon_i2c *i2c = dev_id; + + i2c->hlc_int_disable(i2c); + wake_up(&i2c->queue); + + return IRQ_HANDLED; +} + +static void octeon_i2c_hlc_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_write_int(i2c, TWSI_INT_ST_EN); +} + +static u32 octeon_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_SMBUS_BLOCK_PROC_CALL; +} + +static const struct i2c_algorithm octeon_i2c_algo = { + .master_xfer = octeon_i2c_xfer, + .functionality = octeon_i2c_functionality, +}; + +static struct i2c_adapter octeon_i2c_ops = { + .owner = THIS_MODULE, + .name = "OCTEON adapter", + .algo = &octeon_i2c_algo, +}; + +static int octeon_i2c_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + int irq, result = 0, hlc_irq = 0; + struct resource *res_mem; + struct octeon_i2c *i2c; + bool cn78xx_style; + + cn78xx_style = of_device_is_compatible(node, "cavium,octeon-7890-twsi"); + if (cn78xx_style) { + hlc_irq = platform_get_irq(pdev, 0); + if (hlc_irq < 0) + return hlc_irq; + + irq = platform_get_irq(pdev, 2); + if (irq < 0) + return irq; + } else { + /* All adaptors have an irq. */ + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + } + + i2c = devm_kzalloc(&pdev->dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) { + result = -ENOMEM; + goto out; + } + i2c->dev = &pdev->dev; + + i2c->roff.sw_twsi = 0x00; + i2c->roff.twsi_int = 0x10; + i2c->roff.sw_twsi_ext = 0x18; + + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + i2c->twsi_base = devm_ioremap_resource(&pdev->dev, res_mem); + if (IS_ERR(i2c->twsi_base)) { + result = PTR_ERR(i2c->twsi_base); + goto out; + } + + /* + * "clock-rate" is a legacy binding, the official binding is + * "clock-frequency". Try the official one first and then + * fall back if it doesn't exist. + */ + if (of_property_read_u32(node, "clock-frequency", &i2c->twsi_freq) && + of_property_read_u32(node, "clock-rate", &i2c->twsi_freq)) { + dev_err(i2c->dev, + "no I2C 'clock-rate' or 'clock-frequency' property\n"); + result = -ENXIO; + goto out; + } + + i2c->sys_freq = octeon_get_io_clock_rate(); + + init_waitqueue_head(&i2c->queue); + + i2c->irq = irq; + + if (cn78xx_style) { + i2c->hlc_irq = hlc_irq; + + i2c->int_enable = octeon_i2c_int_enable78; + i2c->int_disable = octeon_i2c_int_disable78; + i2c->hlc_int_enable = octeon_i2c_hlc_int_enable78; + i2c->hlc_int_disable = octeon_i2c_hlc_int_disable78; + + irq_set_status_flags(i2c->irq, IRQ_NOAUTOEN); + irq_set_status_flags(i2c->hlc_irq, IRQ_NOAUTOEN); + + result = devm_request_irq(&pdev->dev, i2c->hlc_irq, + octeon_i2c_hlc_isr78, 0, + DRV_NAME, i2c); + if (result < 0) { + dev_err(i2c->dev, "failed to attach interrupt\n"); + goto out; + } + } else { + i2c->int_enable = octeon_i2c_int_enable; + i2c->int_disable = octeon_i2c_int_disable; + i2c->hlc_int_enable = octeon_i2c_hlc_int_enable; + i2c->hlc_int_disable = octeon_i2c_int_disable; + } + + result = devm_request_irq(&pdev->dev, i2c->irq, + octeon_i2c_isr, 0, DRV_NAME, i2c); + if (result < 0) { + dev_err(i2c->dev, "failed to attach interrupt\n"); + goto out; + } + + if (OCTEON_IS_MODEL(OCTEON_CN38XX)) + i2c->broken_irq_check = true; + + result = octeon_i2c_init_lowlevel(i2c); + if (result) { + dev_err(i2c->dev, "init low level failed\n"); + goto out; + } + + octeon_i2c_set_clock(i2c); + + i2c->adap = octeon_i2c_ops; + i2c->adap.timeout = msecs_to_jiffies(2); + i2c->adap.retries = 5; + i2c->adap.bus_recovery_info = &octeon_i2c_recovery_info; + i2c->adap.dev.parent = &pdev->dev; + i2c->adap.dev.of_node = node; + i2c_set_adapdata(&i2c->adap, i2c); + platform_set_drvdata(pdev, i2c); + + result = i2c_add_adapter(&i2c->adap); + if (result < 0) + goto out; + dev_info(i2c->dev, "probed\n"); + return 0; + +out: + return result; +}; + +static int octeon_i2c_remove(struct platform_device *pdev) +{ + struct octeon_i2c *i2c = platform_get_drvdata(pdev); + + i2c_del_adapter(&i2c->adap); + return 0; +}; + +static const struct of_device_id octeon_i2c_match[] = { + { .compatible = "cavium,octeon-3860-twsi", }, + { .compatible = "cavium,octeon-7890-twsi", }, + {}, +}; +MODULE_DEVICE_TABLE(of, octeon_i2c_match); + +static struct platform_driver octeon_i2c_driver = { + .probe = octeon_i2c_probe, + .remove = octeon_i2c_remove, + .driver = { + .name = DRV_NAME, + .of_match_table = octeon_i2c_match, + }, +}; + +module_platform_driver(octeon_i2c_driver); + +MODULE_AUTHOR("Michael Lawnick <michael.lawnick.ext@nsn.com>"); +MODULE_DESCRIPTION("I2C-Bus adapter for Cavium OCTEON processors"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c index ab1279b8e240..c7da0c42baee 100644 --- a/drivers/i2c/busses/i2c-omap.c +++ b/drivers/i2c/busses/i2c-omap.c @@ -1425,10 +1425,8 @@ omap_i2c_probe(struct platform_device *pdev) /* i2c device drivers may be active on return from add_adapter() */ adap->nr = pdev->id; r = i2c_add_numbered_adapter(adap); - if (r) { - dev_err(omap->dev, "failure adding adapter\n"); + if (r) goto err_unuse_clocks; - } dev_info(omap->dev, "bus %d rev%d.%d at %d kHz\n", adap->nr, major, minor, omap->speed); diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c index 23d1c167b5d7..c2268cdf38e8 100644 --- a/drivers/i2c/busses/i2c-piix4.c +++ b/drivers/i2c/busses/i2c-piix4.c @@ -694,7 +694,6 @@ static int piix4_add_adapter(struct pci_dev *dev, unsigned short smba, retval = i2c_add_adapter(adap); if (retval) { - dev_err(&dev->dev, "Couldn't register adapter!\n"); kfree(adapdata); kfree(adap); release_region(smba, SMBIOSIZE); diff --git a/drivers/i2c/busses/i2c-pmcmsp.c b/drivers/i2c/busses/i2c-pmcmsp.c index 2c40edbf6224..217c78711d65 100644 --- a/drivers/i2c/busses/i2c-pmcmsp.c +++ b/drivers/i2c/busses/i2c-pmcmsp.c @@ -329,10 +329,8 @@ static int pmcmsptwi_probe(struct platform_device *pldev) i2c_set_adapdata(&pmcmsptwi_adapter, &pmcmsptwi_data); rc = i2c_add_adapter(&pmcmsptwi_adapter); - if (rc) { - dev_err(&pldev->dev, "Unable to register I2C adapter\n"); + if (rc) goto ret_unmap; - } return 0; diff --git a/drivers/i2c/busses/i2c-pnx.c b/drivers/i2c/busses/i2c-pnx.c index 7ea67aa46fb7..fd5f9d2bf6d9 100644 --- a/drivers/i2c/busses/i2c-pnx.c +++ b/drivers/i2c/busses/i2c-pnx.c @@ -714,10 +714,8 @@ static int i2c_pnx_probe(struct platform_device *pdev) /* Register this adapter with the I2C subsystem */ ret = i2c_add_numbered_adapter(&alg_data->adapter); - if (ret < 0) { - dev_err(&pdev->dev, "I2C: Failed to add bus\n"); + if (ret < 0) goto out_clock; - } dev_dbg(&pdev->dev, "%s: Master at %#8x, irq %d.\n", alg_data->adapter.name, res->start, alg_data->irq); diff --git a/drivers/i2c/busses/i2c-puv3.c b/drivers/i2c/busses/i2c-puv3.c index 82b6f02544da..0c8b1571886d 100644 --- a/drivers/i2c/busses/i2c-puv3.c +++ b/drivers/i2c/busses/i2c-puv3.c @@ -212,11 +212,8 @@ static int puv3_i2c_probe(struct platform_device *pdev) adapter->nr = pdev->id; rc = i2c_add_numbered_adapter(adapter); - if (rc) { - dev_err(&pdev->dev, "Adapter '%s' registration failed\n", - adapter->name); + if (rc) goto fail_add_adapter; - } dev_info(&pdev->dev, "PKUnity v3 i2c bus adapter.\n"); return 0; diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c index 0d351954db02..e28b825b0433 100644 --- a/drivers/i2c/busses/i2c-pxa.c +++ b/drivers/i2c/busses/i2c-pxa.c @@ -1292,10 +1292,8 @@ static int i2c_pxa_probe(struct platform_device *dev) #endif ret = i2c_add_numbered_adapter(&i2c->adap); - if (ret < 0) { - dev_err(&dev->dev, "failed to add bus: %d\n", ret); + if (ret < 0) goto ereqirq; - } platform_set_drvdata(dev, i2c); diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c index 52407f3c9e1c..400e344704d2 100644 --- a/drivers/i2c/busses/i2c-rcar.c +++ b/drivers/i2c/busses/i2c-rcar.c @@ -875,10 +875,8 @@ static int rcar_i2c_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); ret = i2c_add_numbered_adapter(adap); - if (ret < 0) { - dev_err(dev, "reg adap failed: %d\n", ret); + if (ret < 0) goto out_pm_disable; - } dev_info(dev, "probed\n"); diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c index d7e3af671543..6263ea82d6ac 100644 --- a/drivers/i2c/busses/i2c-riic.c +++ b/drivers/i2c/busses/i2c-riic.c @@ -383,10 +383,8 @@ static int riic_i2c_probe(struct platform_device *pdev) ret = i2c_add_adapter(adap); - if (ret) { - dev_err(&pdev->dev, "failed to add adapter\n"); + if (ret) return ret; - } platform_set_drvdata(pdev, riic); diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 2bc8b01153d6..3b87afe82f63 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -1303,10 +1303,8 @@ static int rk3x_i2c_probe(struct platform_device *pdev) rk3x_i2c_adapt_div(i2c, clk_rate); ret = i2c_add_adapter(&i2c->adap); - if (ret < 0) { - dev_err(&pdev->dev, "Could not register adapter\n"); + if (ret < 0) goto err_clk_notifier; - } dev_info(&pdev->dev, "Initialized RK3xxx I2C bus at %p\n", i2c->regs); diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c index 38dc1cacfd8b..499af26e736e 100644 --- a/drivers/i2c/busses/i2c-s3c2410.c +++ b/drivers/i2c/busses/i2c-s3c2410.c @@ -1215,7 +1215,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev) ret = i2c_add_numbered_adapter(&i2c->adap); if (ret < 0) { - dev_err(&pdev->dev, "failed to add bus to i2c core\n"); pm_runtime_disable(&pdev->dev); s3c24xx_i2c_deregister_cpufreq(i2c); clk_unprepare(i2c->clk); diff --git a/drivers/i2c/busses/i2c-sh7760.c b/drivers/i2c/busses/i2c-sh7760.c index 24968384b401..c2005c789d2b 100644 --- a/drivers/i2c/busses/i2c-sh7760.c +++ b/drivers/i2c/busses/i2c-sh7760.c @@ -510,10 +510,8 @@ static int sh7760_i2c_probe(struct platform_device *pdev) } ret = i2c_add_numbered_adapter(&id->adap); - if (ret < 0) { - dev_err(&pdev->dev, "reg adap failed: %d\n", ret); + if (ret < 0) goto out4; - } platform_set_drvdata(pdev, id); diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 6fb3e2645992..50f276e8d179 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -981,7 +981,6 @@ static int sh_mobile_i2c_probe(struct platform_device *dev) ret = i2c_add_numbered_adapter(adap); if (ret < 0) { sh_mobile_i2c_release_dma(pd); - dev_err(&dev->dev, "cannot add numbered adapter\n"); return ret; } diff --git a/drivers/i2c/busses/i2c-sirf.c b/drivers/i2c/busses/i2c-sirf.c index 792a42bdd335..95e81d0f72b4 100644 --- a/drivers/i2c/busses/i2c-sirf.c +++ b/drivers/i2c/busses/i2c-sirf.c @@ -387,10 +387,8 @@ static int i2c_sirfsoc_probe(struct platform_device *pdev) writel(regval, siic->base + SIRFSOC_I2C_SDA_DELAY); err = i2c_add_numbered_adapter(adap); - if (err < 0) { - dev_err(&pdev->dev, "Can't add new i2c adapter\n"); + if (err < 0) goto out; - } clk_disable(clk); diff --git a/drivers/i2c/busses/i2c-st.c b/drivers/i2c/busses/i2c-st.c index 944ec4205084..1371547ce1a3 100644 --- a/drivers/i2c/busses/i2c-st.c +++ b/drivers/i2c/busses/i2c-st.c @@ -874,10 +874,8 @@ static int st_i2c_probe(struct platform_device *pdev) init_completion(&i2c_dev->complete); ret = i2c_add_adapter(adap); - if (ret) { - dev_err(&pdev->dev, "Failed to add adapter\n"); + if (ret) return ret; - } platform_set_drvdata(pdev, i2c_dev); diff --git a/drivers/i2c/busses/i2c-stu300.c b/drivers/i2c/busses/i2c-stu300.c index 460c134832ac..dc63236b45b2 100644 --- a/drivers/i2c/busses/i2c-stu300.c +++ b/drivers/i2c/busses/i2c-stu300.c @@ -920,11 +920,8 @@ static int stu300_probe(struct platform_device *pdev) /* i2c device drivers may be active on return from add_adapter() */ ret = i2c_add_numbered_adapter(adap); - if (ret) { - dev_err(&pdev->dev, "failure adding ST Micro DDC " - "I2C adapter\n"); + if (ret) return ret; - } platform_set_drvdata(pdev, dev); dev_info(&pdev->dev, "ST DDC I2C @ %p, irq %d\n", diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c index b126dbaa47e3..d9979da11485 100644 --- a/drivers/i2c/busses/i2c-tegra.c +++ b/drivers/i2c/busses/i2c-tegra.c @@ -932,10 +932,8 @@ static int tegra_i2c_probe(struct platform_device *pdev) i2c_dev->adapter.dev.of_node = pdev->dev.of_node; ret = i2c_add_numbered_adapter(&i2c_dev->adapter); - if (ret) { - dev_err(&pdev->dev, "Failed to add I2C adapter\n"); + if (ret) goto disable_div_clk; - } return 0; diff --git a/drivers/i2c/busses/i2c-thunderx-pcidrv.c b/drivers/i2c/busses/i2c-thunderx-pcidrv.c new file mode 100644 index 000000000000..bba5b429f69c --- /dev/null +++ b/drivers/i2c/busses/i2c-thunderx-pcidrv.c @@ -0,0 +1,259 @@ +/* + * Cavium ThunderX i2c driver. + * + * Copyright (C) 2015,2016 Cavium Inc. + * Authors: Fred Martin <fmartin@caviumnetworks.com> + * Jan Glauber <jglauber@cavium.com> + * + * This file is licensed under the terms of the GNU General Public + * License version 2. This program is licensed "as is" without any + * warranty of any kind, whether express or implied. + */ + +#include <linux/acpi.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/i2c-smbus.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/pci.h> + +#include "i2c-octeon-core.h" + +#define DRV_NAME "i2c-thunderx" + +#define PCI_DEVICE_ID_THUNDER_TWSI 0xa012 + +#define SYS_FREQ_DEFAULT 700000000 + +#define TWSI_INT_ENA_W1C 0x1028 +#define TWSI_INT_ENA_W1S 0x1030 + +/* + * Enable the CORE interrupt. + * The interrupt will be asserted when there is non-STAT_IDLE state in the + * SW_TWSI_EOP_TWSI_STAT register. + */ +static void thunder_i2c_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_writeq_flush(TWSI_INT_CORE_INT, + i2c->twsi_base + TWSI_INT_ENA_W1S); +} + +/* + * Disable the CORE interrupt. + */ +static void thunder_i2c_int_disable(struct octeon_i2c *i2c) +{ + octeon_i2c_writeq_flush(TWSI_INT_CORE_INT, + i2c->twsi_base + TWSI_INT_ENA_W1C); +} + +static void thunder_i2c_hlc_int_enable(struct octeon_i2c *i2c) +{ + octeon_i2c_writeq_flush(TWSI_INT_ST_INT | TWSI_INT_TS_INT, + i2c->twsi_base + TWSI_INT_ENA_W1S); +} + +static void thunder_i2c_hlc_int_disable(struct octeon_i2c *i2c) +{ + octeon_i2c_writeq_flush(TWSI_INT_ST_INT | TWSI_INT_TS_INT, + i2c->twsi_base + TWSI_INT_ENA_W1C); +} + +static u32 thunderx_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK) | + I2C_FUNC_SMBUS_READ_BLOCK_DATA | I2C_SMBUS_BLOCK_PROC_CALL; +} + +static const struct i2c_algorithm thunderx_i2c_algo = { + .master_xfer = octeon_i2c_xfer, + .functionality = thunderx_i2c_functionality, +}; + +static struct i2c_adapter thunderx_i2c_ops = { + .owner = THIS_MODULE, + .name = "ThunderX adapter", + .algo = &thunderx_i2c_algo, +}; + +static void thunder_i2c_clock_enable(struct device *dev, struct octeon_i2c *i2c) +{ + int ret; + + i2c->clk = clk_get(dev, NULL); + if (IS_ERR(i2c->clk)) { + i2c->clk = NULL; + goto skip; + } + + ret = clk_prepare_enable(i2c->clk); + if (ret) + goto skip; + i2c->sys_freq = clk_get_rate(i2c->clk); + +skip: + if (!i2c->sys_freq) + i2c->sys_freq = SYS_FREQ_DEFAULT; +} + +static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk) +{ + if (!clk) + return; + clk_disable_unprepare(clk); + clk_put(clk); +} + +static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c, + struct device_node *node) +{ + u32 type; + + if (!node) + return -EINVAL; + + i2c->alert_data.irq = irq_of_parse_and_map(node, 0); + if (!i2c->alert_data.irq) + return -EINVAL; + + type = irqd_get_trigger_type(irq_get_irq_data(i2c->alert_data.irq)); + i2c->alert_data.alert_edge_triggered = + (type & IRQ_TYPE_LEVEL_MASK) ? 1 : 0; + + i2c->ara = i2c_setup_smbus_alert(&i2c->adap, &i2c->alert_data); + if (!i2c->ara) + return -ENODEV; + return 0; +} + +static int thunder_i2c_smbus_setup(struct octeon_i2c *i2c, + struct device_node *node) +{ + /* TODO: ACPI support */ + if (!acpi_disabled) + return -EOPNOTSUPP; + + return thunder_i2c_smbus_setup_of(i2c, node); +} + +static void thunder_i2c_smbus_remove(struct octeon_i2c *i2c) +{ + if (i2c->ara) + i2c_unregister_device(i2c->ara); +} + +static int thunder_i2c_probe_pci(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct device *dev = &pdev->dev; + struct octeon_i2c *i2c; + int ret; + + i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL); + if (!i2c) + return -ENOMEM; + + i2c->roff.sw_twsi = 0x1000; + i2c->roff.twsi_int = 0x1010; + i2c->roff.sw_twsi_ext = 0x1018; + + i2c->dev = dev; + pci_set_drvdata(pdev, i2c); + ret = pcim_enable_device(pdev); + if (ret) + return ret; + + ret = pci_request_regions(pdev, DRV_NAME); + if (ret) + return ret; + + i2c->twsi_base = pcim_iomap(pdev, 0, pci_resource_len(pdev, 0)); + if (!i2c->twsi_base) + return -EINVAL; + + thunder_i2c_clock_enable(dev, i2c); + ret = device_property_read_u32(dev, "clock-frequency", &i2c->twsi_freq); + if (ret) + i2c->twsi_freq = 100000; + + init_waitqueue_head(&i2c->queue); + + i2c->int_enable = thunder_i2c_int_enable; + i2c->int_disable = thunder_i2c_int_disable; + i2c->hlc_int_enable = thunder_i2c_hlc_int_enable; + i2c->hlc_int_disable = thunder_i2c_hlc_int_disable; + + ret = pci_enable_msix(pdev, &i2c->i2c_msix, 1); + if (ret) + goto error; + + ret = devm_request_irq(dev, i2c->i2c_msix.vector, octeon_i2c_isr, 0, + DRV_NAME, i2c); + if (ret) + goto error; + + ret = octeon_i2c_init_lowlevel(i2c); + if (ret) + goto error; + + octeon_i2c_set_clock(i2c); + + i2c->adap = thunderx_i2c_ops; + i2c->adap.retries = 5; + i2c->adap.bus_recovery_info = &octeon_i2c_recovery_info; + i2c->adap.dev.parent = dev; + i2c->adap.dev.of_node = pdev->dev.of_node; + snprintf(i2c->adap.name, sizeof(i2c->adap.name), + "Cavium ThunderX i2c adapter at %s", dev_name(dev)); + i2c_set_adapdata(&i2c->adap, i2c); + + ret = i2c_add_adapter(&i2c->adap); + if (ret) + goto error; + + dev_info(i2c->dev, "Probed. Set system clock to %u\n", i2c->sys_freq); + + ret = thunder_i2c_smbus_setup(i2c, pdev->dev.of_node); + if (ret) + dev_info(dev, "SMBUS alert not active on this bus\n"); + + return 0; + +error: + thunder_i2c_clock_disable(dev, i2c->clk); + return ret; +} + +static void thunder_i2c_remove_pci(struct pci_dev *pdev) +{ + struct octeon_i2c *i2c = pci_get_drvdata(pdev); + + thunder_i2c_smbus_remove(i2c); + thunder_i2c_clock_disable(&pdev->dev, i2c->clk); + i2c_del_adapter(&i2c->adap); +} + +static const struct pci_device_id thunder_i2c_pci_id_table[] = { + { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, PCI_DEVICE_ID_THUNDER_TWSI) }, + { 0, } +}; + +MODULE_DEVICE_TABLE(pci, thunder_i2c_pci_id_table); + +static struct pci_driver thunder_i2c_pci_driver = { + .name = DRV_NAME, + .id_table = thunder_i2c_pci_id_table, + .probe = thunder_i2c_probe_pci, + .remove = thunder_i2c_remove_pci, +}; + +module_pci_driver(thunder_i2c_pci_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Fred Martin <fmartin@caviumnetworks.com>"); +MODULE_DESCRIPTION("I2C-Bus adapter for Cavium ThunderX SOC"); diff --git a/drivers/i2c/busses/i2c-uniphier-f.c b/drivers/i2c/busses/i2c-uniphier-f.c index aeead0d27d10..35608531fe07 100644 --- a/drivers/i2c/busses/i2c-uniphier-f.c +++ b/drivers/i2c/busses/i2c-uniphier-f.c @@ -550,11 +550,6 @@ static int uniphier_fi2c_probe(struct platform_device *pdev) } ret = i2c_add_adapter(&priv->adap); - if (ret) { - dev_err(dev, "failed to add I2C adapter\n"); - goto err; - } - err: if (ret) clk_disable_unprepare(priv->clk); diff --git a/drivers/i2c/busses/i2c-uniphier.c b/drivers/i2c/busses/i2c-uniphier.c index 475a5eb514e2..d6e612a0e02a 100644 --- a/drivers/i2c/busses/i2c-uniphier.c +++ b/drivers/i2c/busses/i2c-uniphier.c @@ -407,11 +407,6 @@ static int uniphier_i2c_probe(struct platform_device *pdev) } ret = i2c_add_adapter(&priv->adap); - if (ret) { - dev_err(dev, "failed to add I2C adapter\n"); - goto err; - } - err: if (ret) clk_disable_unprepare(priv->clk); diff --git a/drivers/i2c/busses/i2c-wmt.c b/drivers/i2c/busses/i2c-wmt.c index e1e3a85596c5..fbd0fd59f312 100644 --- a/drivers/i2c/busses/i2c-wmt.c +++ b/drivers/i2c/busses/i2c-wmt.c @@ -432,10 +432,8 @@ static int wmt_i2c_probe(struct platform_device *pdev) } err = i2c_add_adapter(adap); - if (err) { - dev_err(&pdev->dev, "failed to add adapter\n"); + if (err) return err; - } platform_set_drvdata(pdev, i2c_dev); diff --git a/drivers/i2c/busses/i2c-xgene-slimpro.c b/drivers/i2c/busses/i2c-xgene-slimpro.c index 4233f5695352..263685c7a512 100644 --- a/drivers/i2c/busses/i2c-xgene-slimpro.c +++ b/drivers/i2c/busses/i2c-xgene-slimpro.c @@ -418,7 +418,6 @@ static int xgene_slimpro_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(adapter, ctx); rc = i2c_add_adapter(adapter); if (rc) { - dev_err(&pdev->dev, "Adapter registeration failed\n"); mbox_free_channel(ctx->mbox_chan); return rc; } diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index 74f54f2f471f..66bce3b311a1 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -804,7 +804,6 @@ static int xiic_i2c_probe(struct platform_device *pdev) /* add i2c adapter to i2c tree */ ret = i2c_add_adapter(&i2c->adap); if (ret) { - dev_err(&pdev->dev, "Failed to add adapter\n"); xiic_deinit(i2c); goto err_clk_dis; } diff --git a/drivers/i2c/busses/i2c-xlp9xx.c b/drivers/i2c/busses/i2c-xlp9xx.c index 55a7bef1b2e1..2a972ed7aa0d 100644 --- a/drivers/i2c/busses/i2c-xlp9xx.c +++ b/drivers/i2c/busses/i2c-xlp9xx.c @@ -400,10 +400,8 @@ static int xlp9xx_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(&priv->adapter, priv); err = i2c_add_adapter(&priv->adapter); - if (err) { - dev_err(&pdev->dev, "failed to add I2C adapter!\n"); + if (err) return err; - } platform_set_drvdata(pdev, priv); dev_dbg(&pdev->dev, "I2C bus:%d added\n", priv->adapter.nr); diff --git a/drivers/i2c/busses/i2c-xlr.c b/drivers/i2c/busses/i2c-xlr.c index 613c3a4f2c51..0968f59b6df5 100644 --- a/drivers/i2c/busses/i2c-xlr.c +++ b/drivers/i2c/busses/i2c-xlr.c @@ -432,10 +432,8 @@ static int xlr_i2c_probe(struct platform_device *pdev) i2c_set_adapdata(&priv->adap, priv); ret = i2c_add_numbered_adapter(&priv->adap); - if (ret < 0) { - dev_err(&priv->adap.dev, "Failed to add i2c bus.\n"); + if (ret < 0) return ret; - } platform_set_drvdata(pdev, priv); dev_info(&priv->adap.dev, "Added I2C Bus.\n"); diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c index da3a02ef4a31..05794423e01f 100644 --- a/drivers/i2c/i2c-core.c +++ b/drivers/i2c/i2c-core.c @@ -88,7 +88,7 @@ void i2c_transfer_trace_unreg(void) } #if defined(CONFIG_ACPI) -struct acpi_i2c_handler_data { +struct i2c_acpi_handler_data { struct acpi_connection_info info; struct i2c_adapter *adapter; }; @@ -103,15 +103,18 @@ struct gsb_buffer { }; } __packed; -struct acpi_i2c_lookup { +struct i2c_acpi_lookup { struct i2c_board_info *info; acpi_handle adapter_handle; acpi_handle device_handle; + acpi_handle search_handle; + u32 speed; + u32 min_speed; }; -static int acpi_i2c_fill_info(struct acpi_resource *ares, void *data) +static int i2c_acpi_fill_info(struct acpi_resource *ares, void *data) { - struct acpi_i2c_lookup *lookup = data; + struct i2c_acpi_lookup *lookup = data; struct i2c_board_info *info = lookup->info; struct acpi_resource_i2c_serialbus *sb; acpi_status status; @@ -130,19 +133,18 @@ static int acpi_i2c_fill_info(struct acpi_resource *ares, void *data) return 1; info->addr = sb->slave_address; + lookup->speed = sb->connection_speed; if (sb->access_mode == ACPI_I2C_10BIT_MODE) info->flags |= I2C_CLIENT_TEN; return 1; } -static int acpi_i2c_get_info(struct acpi_device *adev, - struct i2c_board_info *info, - acpi_handle *adapter_handle) +static int i2c_acpi_do_lookup(struct acpi_device *adev, + struct i2c_acpi_lookup *lookup) { + struct i2c_board_info *info = lookup->info; struct list_head resource_list; - struct resource_entry *entry; - struct acpi_i2c_lookup lookup; int ret; if (acpi_bus_get_status(adev) || !adev->status.present || @@ -150,24 +152,41 @@ static int acpi_i2c_get_info(struct acpi_device *adev, return -EINVAL; memset(info, 0, sizeof(*info)); - info->fwnode = acpi_fwnode_handle(adev); - - memset(&lookup, 0, sizeof(lookup)); - lookup.device_handle = acpi_device_handle(adev); - lookup.info = info; + lookup->device_handle = acpi_device_handle(adev); /* Look up for I2cSerialBus resource */ INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, - acpi_i2c_fill_info, &lookup); + i2c_acpi_fill_info, lookup); acpi_dev_free_resource_list(&resource_list); if (ret < 0 || !info->addr) return -EINVAL; + return 0; +} + +static int i2c_acpi_get_info(struct acpi_device *adev, + struct i2c_board_info *info, + acpi_handle *adapter_handle) +{ + struct list_head resource_list; + struct resource_entry *entry; + struct i2c_acpi_lookup lookup; + int ret; + + memset(&lookup, 0, sizeof(lookup)); + lookup.info = info; + + ret = i2c_acpi_do_lookup(adev, &lookup); + if (ret) + return ret; + + info->fwnode = acpi_fwnode_handle(adev); *adapter_handle = lookup.adapter_handle; /* Then fill IRQ number if any */ + INIT_LIST_HEAD(&resource_list); ret = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); if (ret < 0) return -EINVAL; @@ -186,7 +205,7 @@ static int acpi_i2c_get_info(struct acpi_device *adev, return 0; } -static void acpi_i2c_register_device(struct i2c_adapter *adapter, +static void i2c_acpi_register_device(struct i2c_adapter *adapter, struct acpi_device *adev, struct i2c_board_info *info) { @@ -201,7 +220,7 @@ static void acpi_i2c_register_device(struct i2c_adapter *adapter, } } -static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, +static acpi_status i2c_acpi_add_device(acpi_handle handle, u32 level, void *data, void **return_value) { struct i2c_adapter *adapter = data; @@ -212,28 +231,28 @@ static acpi_status acpi_i2c_add_device(acpi_handle handle, u32 level, if (acpi_bus_get_device(handle, &adev)) return AE_OK; - if (acpi_i2c_get_info(adev, &info, &adapter_handle)) + if (i2c_acpi_get_info(adev, &info, &adapter_handle)) return AE_OK; if (adapter_handle != ACPI_HANDLE(&adapter->dev)) return AE_OK; - acpi_i2c_register_device(adapter, adev, &info); + i2c_acpi_register_device(adapter, adev, &info); return AE_OK; } -#define ACPI_I2C_MAX_SCAN_DEPTH 32 +#define I2C_ACPI_MAX_SCAN_DEPTH 32 /** - * acpi_i2c_register_devices - enumerate I2C slave devices behind adapter + * i2c_acpi_register_devices - enumerate I2C slave devices behind adapter * @adap: pointer to adapter * * Enumerate all I2C slave devices behind this adapter by walking the ACPI * namespace. When a device is found it will be added to the Linux device * model and bound to the corresponding ACPI handle. */ -static void acpi_i2c_register_devices(struct i2c_adapter *adap) +static void i2c_acpi_register_devices(struct i2c_adapter *adap) { acpi_status status; @@ -241,14 +260,72 @@ static void acpi_i2c_register_devices(struct i2c_adapter *adap) return; status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, - ACPI_I2C_MAX_SCAN_DEPTH, - acpi_i2c_add_device, NULL, + I2C_ACPI_MAX_SCAN_DEPTH, + i2c_acpi_add_device, NULL, adap, NULL); if (ACPI_FAILURE(status)) dev_warn(&adap->dev, "failed to enumerate I2C slaves\n"); } -static int acpi_i2c_match_adapter(struct device *dev, void *data) +static acpi_status i2c_acpi_lookup_speed(acpi_handle handle, u32 level, + void *data, void **return_value) +{ + struct i2c_acpi_lookup *lookup = data; + struct acpi_device *adev; + + if (acpi_bus_get_device(handle, &adev)) + return AE_OK; + + if (i2c_acpi_do_lookup(adev, lookup)) + return AE_OK; + + if (lookup->search_handle != lookup->adapter_handle) + return AE_OK; + + if (lookup->speed <= lookup->min_speed) + lookup->min_speed = lookup->speed; + + return AE_OK; +} + +/** + * i2c_acpi_find_bus_speed - find I2C bus speed from ACPI + * @dev: The device owning the bus + * + * Find the I2C bus speed by walking the ACPI namespace for all I2C slaves + * devices connected to this bus and use the speed of slowest device. + * + * Returns the speed in Hz or zero + */ +u32 i2c_acpi_find_bus_speed(struct device *dev) +{ + struct i2c_acpi_lookup lookup; + struct i2c_board_info dummy; + acpi_status status; + + if (!has_acpi_companion(dev)) + return 0; + + memset(&lookup, 0, sizeof(lookup)); + lookup.search_handle = ACPI_HANDLE(dev); + lookup.min_speed = UINT_MAX; + lookup.info = &dummy; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + I2C_ACPI_MAX_SCAN_DEPTH, + i2c_acpi_lookup_speed, NULL, + &lookup, NULL); + + if (ACPI_FAILURE(status)) { + dev_warn(dev, "unable to find I2C bus speed from ACPI\n"); + return 0; + } + + return lookup.min_speed != UINT_MAX ? lookup.min_speed : 0; +} +EXPORT_SYMBOL_GPL(i2c_acpi_find_bus_speed); + +static int i2c_acpi_match_adapter(struct device *dev, void *data) { struct i2c_adapter *adapter = i2c_verify_adapter(dev); @@ -258,29 +335,29 @@ static int acpi_i2c_match_adapter(struct device *dev, void *data) return ACPI_HANDLE(dev) == (acpi_handle)data; } -static int acpi_i2c_match_device(struct device *dev, void *data) +static int i2c_acpi_match_device(struct device *dev, void *data) { return ACPI_COMPANION(dev) == data; } -static struct i2c_adapter *acpi_i2c_find_adapter_by_handle(acpi_handle handle) +static struct i2c_adapter *i2c_acpi_find_adapter_by_handle(acpi_handle handle) { struct device *dev; dev = bus_find_device(&i2c_bus_type, NULL, handle, - acpi_i2c_match_adapter); + i2c_acpi_match_adapter); return dev ? i2c_verify_adapter(dev) : NULL; } -static struct i2c_client *acpi_i2c_find_client_by_adev(struct acpi_device *adev) +static struct i2c_client *i2c_acpi_find_client_by_adev(struct acpi_device *adev) { struct device *dev; - dev = bus_find_device(&i2c_bus_type, NULL, adev, acpi_i2c_match_device); + dev = bus_find_device(&i2c_bus_type, NULL, adev, i2c_acpi_match_device); return dev ? i2c_verify_client(dev) : NULL; } -static int acpi_i2c_notify(struct notifier_block *nb, unsigned long value, +static int i2c_acpi_notify(struct notifier_block *nb, unsigned long value, void *arg) { struct acpi_device *adev = arg; @@ -291,20 +368,20 @@ static int acpi_i2c_notify(struct notifier_block *nb, unsigned long value, switch (value) { case ACPI_RECONFIG_DEVICE_ADD: - if (acpi_i2c_get_info(adev, &info, &adapter_handle)) + if (i2c_acpi_get_info(adev, &info, &adapter_handle)) break; - adapter = acpi_i2c_find_adapter_by_handle(adapter_handle); + adapter = i2c_acpi_find_adapter_by_handle(adapter_handle); if (!adapter) break; - acpi_i2c_register_device(adapter, adev, &info); + i2c_acpi_register_device(adapter, adev, &info); break; case ACPI_RECONFIG_DEVICE_REMOVE: if (!acpi_device_enumerated(adev)) break; - client = acpi_i2c_find_client_by_adev(adev); + client = i2c_acpi_find_client_by_adev(adev); if (!client) break; @@ -317,10 +394,10 @@ static int acpi_i2c_notify(struct notifier_block *nb, unsigned long value, } static struct notifier_block i2c_acpi_notifier = { - .notifier_call = acpi_i2c_notify, + .notifier_call = i2c_acpi_notify, }; #else /* CONFIG_ACPI */ -static inline void acpi_i2c_register_devices(struct i2c_adapter *adap) { } +static inline void i2c_acpi_register_devices(struct i2c_adapter *adap) { } extern struct notifier_block i2c_acpi_notifier; #endif /* CONFIG_ACPI */ @@ -386,12 +463,12 @@ static int acpi_gsb_i2c_write_bytes(struct i2c_client *client, } static acpi_status -acpi_i2c_space_handler(u32 function, acpi_physical_address command, +i2c_acpi_space_handler(u32 function, acpi_physical_address command, u32 bits, u64 *value64, void *handler_context, void *region_context) { struct gsb_buffer *gsb = (struct gsb_buffer *)value64; - struct acpi_i2c_handler_data *data = handler_context; + struct i2c_acpi_handler_data *data = handler_context; struct acpi_connection_info *info = &data->info; struct acpi_resource_i2c_serialbus *sb; struct i2c_adapter *adapter = data->adapter; @@ -510,10 +587,10 @@ acpi_i2c_space_handler(u32 function, acpi_physical_address command, } -static int acpi_i2c_install_space_handler(struct i2c_adapter *adapter) +static int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) { acpi_handle handle; - struct acpi_i2c_handler_data *data; + struct i2c_acpi_handler_data *data; acpi_status status; if (!adapter->dev.parent) @@ -524,7 +601,7 @@ static int acpi_i2c_install_space_handler(struct i2c_adapter *adapter) if (!handle) return -ENODEV; - data = kzalloc(sizeof(struct acpi_i2c_handler_data), + data = kzalloc(sizeof(struct i2c_acpi_handler_data), GFP_KERNEL); if (!data) return -ENOMEM; @@ -538,7 +615,7 @@ static int acpi_i2c_install_space_handler(struct i2c_adapter *adapter) status = acpi_install_address_space_handler(handle, ACPI_ADR_SPACE_GSBUS, - &acpi_i2c_space_handler, + &i2c_acpi_space_handler, NULL, data); if (ACPI_FAILURE(status)) { @@ -552,10 +629,10 @@ static int acpi_i2c_install_space_handler(struct i2c_adapter *adapter) return 0; } -static void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter) +static void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) { acpi_handle handle; - struct acpi_i2c_handler_data *data; + struct i2c_acpi_handler_data *data; acpi_status status; if (!adapter->dev.parent) @@ -568,7 +645,7 @@ static void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter) acpi_remove_address_space_handler(handle, ACPI_ADR_SPACE_GSBUS, - &acpi_i2c_space_handler); + &i2c_acpi_space_handler); status = acpi_bus_get_private_data(handle, (void **)&data); if (ACPI_SUCCESS(status)) @@ -577,10 +654,10 @@ static void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter) acpi_bus_detach_private_data(handle); } #else /* CONFIG_ACPI_I2C_OPREGION */ -static inline void acpi_i2c_remove_space_handler(struct i2c_adapter *adapter) +static inline void i2c_acpi_remove_space_handler(struct i2c_adapter *adapter) { } -static inline int acpi_i2c_install_space_handler(struct i2c_adapter *adapter) +static inline int i2c_acpi_install_space_handler(struct i2c_adapter *adapter) { return 0; } #endif /* CONFIG_ACPI_I2C_OPREGION */ @@ -1752,8 +1829,8 @@ static int i2c_register_adapter(struct i2c_adapter *adap) /* create pre-declared device nodes */ of_i2c_register_devices(adap); - acpi_i2c_register_devices(adap); - acpi_i2c_install_space_handler(adap); + i2c_acpi_register_devices(adap); + i2c_acpi_install_space_handler(adap); if (adap->nr < __i2c_first_dynamic_bus_num) i2c_scan_static_board_info(adap); @@ -1925,7 +2002,7 @@ void i2c_del_adapter(struct i2c_adapter *adap) return; } - acpi_i2c_remove_space_handler(adap); + i2c_acpi_remove_space_handler(adap); /* Tell drivers about this removal */ mutex_lock(&core_lock); bus_for_each_drv(&i2c_bus_type, NULL, adap, @@ -2459,7 +2536,7 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) #endif if (in_atomic() || irqs_disabled()) { - ret = adap->trylock_bus(adap, I2C_LOCK_SEGMENT); + ret = i2c_trylock_bus(adap, I2C_LOCK_SEGMENT); if (!ret) /* I2C activity is ongoing. */ return -EAGAIN; diff --git a/drivers/i2c/i2c-mux.c b/drivers/i2c/i2c-mux.c index c1ae719d1a7a..90f59c088750 100644 --- a/drivers/i2c/i2c-mux.c +++ b/drivers/i2c/i2c-mux.c @@ -159,7 +159,7 @@ static int i2c_mux_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) return 0; /* mux_lock not locked, failure */ if (!(flags & I2C_LOCK_ROOT_ADAPTER)) return 1; /* we only want mux_lock, success */ - if (parent->trylock_bus(parent, flags)) + if (i2c_trylock_bus(parent, flags)) return 1; /* parent locked too, success */ rt_mutex_unlock(&parent->mux_lock); return 0; /* parent not locked, failure */ @@ -193,7 +193,7 @@ static int i2c_parent_trylock_bus(struct i2c_adapter *adapter, if (!rt_mutex_trylock(&parent->mux_lock)) return 0; /* mux_lock not locked, failure */ - if (parent->trylock_bus(parent, flags)) + if (i2c_trylock_bus(parent, flags)) return 1; /* parent locked too, success */ rt_mutex_unlock(&parent->mux_lock); return 0; /* parent not locked, failure */ diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 3cdf8e1ca0ad..051b14766ef9 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -593,6 +593,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) struct at24_data *at24; int err; unsigned i, num_addresses; + u8 test_byte; if (client->dev.platform_data) { chip = *(struct at24_platform_data *)client->dev.platform_data; @@ -743,6 +744,18 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) } } + i2c_set_clientdata(client, at24); + + /* + * Perform a one-byte test read to verify that the + * chip is functional. + */ + err = at24_read(at24, 0, &test_byte, 1); + if (err) { + err = -ENODEV; + goto err_clients; + } + at24->nvmem_config.name = dev_name(&client->dev); at24->nvmem_config.dev = &client->dev; at24->nvmem_config.read_only = !writable; @@ -764,8 +777,6 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id) goto err_clients; } - i2c_set_clientdata(client, at24); - dev_info(&client->dev, "%u byte %s EEPROM, %s, %u bytes/write\n", chip.byte_len, client->name, writable ? "writable" : "read-only", at24->write_max); diff --git a/include/linux/i2c.h b/include/linux/i2c.h index fffdc270ca18..5b4a3cbe5d7d 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -601,6 +601,20 @@ i2c_lock_bus(struct i2c_adapter *adapter, unsigned int flags) } /** + * i2c_trylock_bus - Try to get exclusive access to an I2C bus segment + * @adapter: Target I2C bus segment + * @flags: I2C_LOCK_ROOT_ADAPTER tries to locks the root i2c adapter, + * I2C_LOCK_SEGMENT tries to lock only this branch in the adapter tree + * + * Return: true if the I2C bus segment is locked, false otherwise + */ +static inline int +i2c_trylock_bus(struct i2c_adapter *adapter, unsigned int flags) +{ + return adapter->trylock_bus(adapter, flags); +} + +/** * i2c_unlock_bus - Release exclusive access to an I2C bus segment * @adapter: Target I2C bus segment * @flags: I2C_LOCK_ROOT_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT @@ -766,4 +780,13 @@ static inline struct i2c_adapter *of_get_i2c_adapter_by_node(struct device_node } #endif /* CONFIG_OF */ +#if IS_ENABLED(CONFIG_ACPI) +u32 i2c_acpi_find_bus_speed(struct device *dev); +#else +static inline u32 i2c_acpi_find_bus_speed(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_ACPI */ + #endif /* _LINUX_I2C_H */ |