diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-designware-core.c')
-rw-r--r-- | drivers/i2c/busses/i2c-designware-core.c | 196 |
1 files changed, 141 insertions, 55 deletions
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; |