--- zzzz-none-000/linux-5.15.111/drivers/i2c/busses/i2c-designware-master.c 2023-05-11 14:00:40.000000000 +0000 +++ puma7-atom-6670-761/linux-5.15.111/drivers/i2c/busses/i2c-designware-master.c 2024-02-07 10:23:01.000000000 +0000 @@ -26,6 +26,9 @@ #define AMD_TIMEOUT_MIN_US 25 #define AMD_TIMEOUT_MAX_US 250 #define AMD_MASTERCFG_MASK GENMASK(15, 0) +#define TIMEOUT 20 /* ms */ + +static int i2c_dw_xfer_polling(struct dw_i2c_dev *dev); static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev) { @@ -46,10 +49,14 @@ u32 ic_clk; int ret; - ret = i2c_dw_acquire_lock(dev); - if (ret) - return ret; - + /* 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"); + return ret; + } + } ret = regmap_read(dev->map, DW_IC_COMP_PARAM_1, &comp_param1); i2c_dw_release_lock(dev); if (ret) @@ -171,14 +178,55 @@ * 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. */ static int i2c_dw_init_master(struct dw_i2c_dev *dev) { int ret; + int timeout = TIMEOUT; + u32 dummy; - ret = i2c_dw_acquire_lock(dev); - if (ret) + /* + * 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(); + + 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 (regmap_read(dev->map, DW_IC_COMP_TYPE, &dummy) == DW_IC_COMP_TYPE_VALUE) + break; + /* + * reset apb and clock domain + */ + regmap_write(dev->map, DW_IC_RESETS, 0); + regmap_write(dev->map, DW_IC_GENERAL, 0); + if (dev->polling) + udelay(10); + else + usleep_range(10, 100); + regmap_write(dev->map, DW_IC_RESETS, + DW_IC_RESETS_APB | DW_IC_RESETS_FUNC); + if (dev->polling) + udelay(10); + else + usleep_range(10, 100); + } while (timeout--); + + if (unlikely(timeout == 0)) + dev_err(dev->dev, "controller time out\n"); /* Disable the adapter */ __i2c_dw_disable(dev); @@ -249,7 +297,8 @@ /* Clear and enable interrupts */ regmap_read(dev->map, DW_IC_CLR_INTR, &dummy); - regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_MASTER_MASK); + if (!dev->polling) + regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_MASTER_MASK); } static int i2c_dw_check_stopbit(struct dw_i2c_dev *dev) @@ -343,7 +392,11 @@ } } else { regmap_write(dev->map, DW_IC_DATA_CMD, *tx_buf++ | cmd); - usleep_range(AMD_TIMEOUT_MIN_US, AMD_TIMEOUT_MAX_US); + if (dev->polling) { + /* Wait less if it's a polling */ + udelay(25); + } else + usleep_range(AMD_TIMEOUT_MIN_US, AMD_TIMEOUT_MAX_US); } } status = i2c_dw_check_stopbit(dev); @@ -477,7 +530,8 @@ if (dev->msg_err) intr_mask = 0; - regmap_write(dev->map, DW_IC_INTR_MASK, intr_mask); + if (!dev->polling) + regmap_write(dev->map, DW_IC_INTR_MASK, intr_mask); } static u8 @@ -555,6 +609,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); /* @@ -583,9 +640,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) @@ -594,8 +656,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_recover_bus(&dev->adapter); @@ -638,7 +701,9 @@ 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); @@ -712,12 +777,46 @@ } /* + * 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) { + regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &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; + regmap_read(dev->map, DW_IC_TX_ABRT_SOURCE, &dev->abort_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; +} + +/* * Interrupt service routine. This gets called whenever an I2C master interrupt * occurs. */ static int i2c_dw_irq_handler_master(struct dw_i2c_dev *dev) { u32 stat; + u32 dummy; stat = i2c_dw_read_clear_intrbits(dev); @@ -758,9 +857,18 @@ */ 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) && + regmap_read(dev->map, DW_IC_RXFLR, &dummy) > 0) + i2c_dw_read(dev); + complete(&dev->cmd_complete); - else if (unlikely(dev->flags & ACCESS_INTR_MASK)) { + } else if (unlikely(dev->flags & ACCESS_INTR_MASK)) { /* Workaround to trigger pending interrupt */ regmap_read(dev->map, DW_IC_INTR_MASK, &stat); i2c_dw_disable_int(dev); @@ -901,6 +1009,7 @@ snprintf(adap->name, sizeof(adap->name), "Synopsys DesignWare I2C adapter"); adap->retries = 3; + adap->timeout = 3 * HZ; adap->algo = &i2c_dw_algo; adap->quirks = &i2c_dw_quirks; adap->dev.parent = dev->dev;