--- zzzz-none-000/linux-4.9.279/drivers/i2c/busses/i2c-designware-core.c 2021-08-08 06:38:54.000000000 +0000 +++ puma7-atom-6591-750/linux-4.9.279/drivers/i2c/busses/i2c-designware-core.c 2023-02-08 11:43:42.000000000 +0000 @@ -21,6 +21,11 @@ * ---------------------------------------------------------------------------- * */ +/* + * Includes Intel Corporation's changes/modifications dated: 2018. + * Changed/modified portions - Copyright (c) 2018, Intel Corporation. + */ + #include #include #include @@ -85,6 +90,10 @@ #define DW_IC_INTR_STOP_DET 0x200 #define DW_IC_INTR_START_DET 0x400 #define DW_IC_INTR_GEN_CALL 0x800 +#define DW_IC_RESETS 0x804 +# define DW_IC_RESETS_FUNC BIT(0) +# define DW_IC_RESETS_APB BIT(1) +#define DW_IC_GENERAL 0x808 #define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \ DW_IC_INTR_TX_EMPTY | \ @@ -277,7 +286,10 @@ * transfer supported by the driver (for 400KHz this is * 25us) as described in the DesignWare I2C databook. */ - usleep_range(25, 250); + if (dev->polling) + udelay(25); + else + usleep_range(25, 250); } while (timeout--); dev_warn(dev->dev, "timeout in %sabling adapter\n", @@ -295,6 +307,7 @@ return dev->get_clk_rate_khz(dev); } +#if 0 static int i2c_dw_acquire_lock(struct dw_i2c_dev *dev) { int ret; @@ -310,6 +323,7 @@ return ret; } +#endif static void i2c_dw_release_lock(struct dw_i2c_dev *dev) { @@ -324,6 +338,7 @@ * This functions configures and enables the I2C master. * This function is called during I2C init function, and in case of timeout at * run time. + * This function must be wrapped by acquire_ownership for shared host. */ int i2c_dw_init(struct dw_i2c_dev *dev) { @@ -331,11 +346,49 @@ u32 reg, comp_param1; u32 sda_falling_time, scl_falling_time; int ret; + int timeout = TIMEOUT; + + /* + * acquire_ownership may disable local irq. + * So release it first and do operations that might sleep. + */ + if (dev->shared_host && dev->release_ownership) + dev->release_ownership(); - ret = i2c_dw_acquire_lock(dev); - if (ret) + if (dev->shared_host && dev->acquire_ownership) { + ret = dev->acquire_ownership(); + if (ret < 0) { + dev_WARN(dev->dev, "%s couldn't acquire ownership\n", + __func__); return ret; + } + } + + do { + /* + * We need to reset the controller if it's not accessible + */ + if (dw_readl(dev, DW_IC_COMP_TYPE) == DW_IC_COMP_TYPE_VALUE) + break; + /* + * reset apb and clock domain + */ + dw_writel(dev, 0, DW_IC_RESETS); + dw_writel(dev, 0, DW_IC_GENERAL); + if (dev->polling) + udelay(10); + else + usleep_range(10, 100); + dw_writel(dev, DW_IC_RESETS_APB | DW_IC_RESETS_FUNC, + DW_IC_RESETS); + if (dev->polling) + udelay(10); + else + usleep_range(10, 100); + } while (timeout--); + if (unlikely(timeout == 0)) + dev_err(dev->dev, "controller time out\n"); reg = dw_readl(dev, DW_IC_COMP_TYPE); if (reg == ___constant_swab32(DW_IC_COMP_TYPE_VALUE)) { /* Configure register endianess access */ @@ -466,7 +519,11 @@ return -ETIMEDOUT; } timeout--; - usleep_range(1000, 1100); + if (dev->polling) { + /* Wait less if it's a polling */ + udelay(25); + } else + usleep_range(1000, 1100); } return 0; @@ -514,7 +571,8 @@ /* Clear and enable interrupts */ dw_readl(dev, DW_IC_CLR_INTR); - dw_writel(dev, DW_IC_INTR_DEFAULT_MASK, DW_IC_INTR_MASK); + if (!dev->polling) + dw_writel(dev, DW_IC_INTR_DEFAULT_MASK, DW_IC_INTR_MASK); } /* @@ -626,7 +684,8 @@ if (dev->msg_err) intr_mask = 0; - dw_writel(dev, intr_mask, DW_IC_INTR_MASK); + if (!dev->polling) + dw_writel(dev, intr_mask, DW_IC_INTR_MASK); } static void @@ -691,6 +750,39 @@ } /* + * Return value resembles wait_for_completion_timeout. + */ +static int i2c_dw_xfer_polling(struct dw_i2c_dev *dev) +{ + int ret = 1, timeout = 100000 /* 1 second timeout */; + u32 stat = 0; + + while (--timeout) { + stat = dw_readl(dev, DW_IC_RAW_INTR_STAT); + /* If error occurs, set cmd_err and go out. */ + if (stat & DW_IC_INTR_TX_ABRT) { + dev->cmd_err |= DW_IC_ERR_TX_ABRT; + dev->abort_source = dw_readl(dev, DW_IC_TX_ABRT_SOURCE); + dev->status = STATUS_IDLE; + goto out; + } + + i2c_dw_read(dev); + i2c_dw_xfer_msg(dev); + + if (stat & DW_IC_INTR_STOP_DET) + goto out; + + udelay(10); + } + + if (timeout == 0) + ret = 0; +out: + return ret; +} + +/* * Prepare controller for a transaction and call i2c_dw_xfer_msg */ static int @@ -701,6 +793,9 @@ dev_dbg(dev->dev, "%s: msgs: %d\n", __func__, num); + if (dev->shared_host) + usleep_range(10000, 12000); + pm_runtime_get_sync(dev->dev); reinit_completion(&dev->cmd_complete); @@ -714,9 +809,14 @@ dev->abort_source = 0; dev->rx_outstanding = 0; - ret = i2c_dw_acquire_lock(dev); - if (ret) - goto done_nolock; + /* if the host is shared between other units on the SoC */ + if (dev->shared_host && dev->acquire_ownership) { + ret = dev->acquire_ownership(); + if (ret < 0) { + dev_WARN(dev->dev, "couldnt acquire ownership\n"); + goto done_nolock; + } + } ret = i2c_dw_wait_bus_not_busy(dev); if (ret < 0) @@ -725,8 +825,9 @@ /* start the transfers */ i2c_dw_xfer_init(dev); - /* wait for tx to complete */ - if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) { + if (dev->polling) + ret = i2c_dw_xfer_polling(dev); + else if (!wait_for_completion_timeout(&dev->cmd_complete, adap->timeout)) { /* wait for tx to complete */ dev_err(dev->dev, "controller timed out\n"); /* i2c_dw_init implicitly disables the adapter */ i2c_dw_init(dev); @@ -768,7 +869,8 @@ ret = -EIO; done: - i2c_dw_release_lock(dev); + if (dev->shared_host && dev->release_ownership) + dev->release_ownership(); done_nolock: pm_runtime_mark_last_busy(dev->dev); @@ -885,9 +987,17 @@ */ 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) { + /* + * Check DW_IC_RXFLR register and + * read from the RX FIFO if it's not + * empty. + */ + if ((stat & DW_IC_INTR_STOP_DET) && + dw_readl(dev, DW_IC_RXFLR) > 0) + i2c_dw_read(dev); 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); @@ -934,6 +1044,7 @@ snprintf(adap->name, sizeof(adap->name), "Synopsys DesignWare I2C adapter"); adap->retries = 3; + adap->timeout = 3 * HZ; adap->algo = &i2c_dw_algo; adap->dev.parent = dev->dev; i2c_set_adapdata(adap, dev);