diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index 26d4978c6df5..8eeea7f72261 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -29,6 +29,7 @@ #include #include #include +#include #define SE_I2C_TX_TRANS_LEN (0x26C) #define SE_I2C_RX_TRANS_LEN (0x270) @@ -82,6 +83,8 @@ #define I2C_TIMEOUT_MIN_USEC 500000 +#define MAX_SE 20 + enum i2c_se_mode { UNINITIALIZED, FIFO_SE_DMA, @@ -96,6 +99,16 @@ struct geni_i2c_clk_fld { u8 t_cycle; }; +struct geni_i2c_ssr { + struct mutex ssr_lock; + bool is_ssr_down; +}; + +struct dbg_buf_ctxt { + void *virt_buf; + void *map_buf; +}; + struct geni_i2c_dev { struct device *dev; void __iomem *base; @@ -131,8 +144,17 @@ struct geni_i2c_dev { enum i2c_se_mode se_mode; bool cmd_done; struct geni_i2c_clk_fld geni_i2c_clk_param; + struct geni_i2c_ssr i2c_ssr; + u32 dbg_num; + struct dbg_buf_ctxt *dbg_buf_ptr; }; +static void ssr_i2c_force_suspend(struct device *dev); +static void ssr_i2c_force_resume(struct device *dev); + +static struct geni_i2c_dev *gi2c_dev_dbg[MAX_SE]; +static int arr_idx; + struct geni_i2c_err_log { int err; const char *msg; @@ -210,12 +232,6 @@ static inline void qcom_geni_i2c_calc_timeout(struct geni_i2c_dev *gi2c) static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err) { - if (gi2c->cur) - GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, - "len:%d, slv-addr:0x%x, RD/WR:%d timeout:%u\n", - gi2c->cur->len, gi2c->cur->addr, gi2c->cur->flags, - gi2c->xfer_timeout); - if (err == I2C_NACK || err == GENI_ABORT_DONE) { GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "%s\n", gi2c_log[err].msg); @@ -224,8 +240,6 @@ static void geni_i2c_err(struct geni_i2c_dev *gi2c, int err) GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, "%s\n", gi2c_log[err].msg); } - GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "%s: se-mode:%d\n", __func__, - gi2c->se_mode); geni_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base, gi2c->ipcl); err_ret: gi2c->err = gi2c_log[err].err; @@ -235,14 +249,30 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev) { struct geni_i2c_dev *gi2c = dev; int i, j; - u32 m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS); - u32 rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS); - u32 dm_tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT); - u32 dm_rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT); - u32 dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN); + u32 m_stat, rx_st, dm_tx_st, dm_rx_st, dma; struct i2c_msg *cur = gi2c->cur; - if (!cur || (m_stat & M_CMD_FAILURE_EN) || + if (gi2c->i2c_ssr.is_ssr_down) { + gi2c->cmd_done = false; + complete(&gi2c->xfer); + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "%s: SSR down\n", __func__); + return IRQ_HANDLED; + } + + m_stat = readl_relaxed(gi2c->base + SE_GENI_M_IRQ_STATUS); + rx_st = readl_relaxed(gi2c->base + SE_GENI_RX_FIFO_STATUS); + dm_tx_st = readl_relaxed(gi2c->base + SE_DMA_TX_IRQ_STAT); + dm_rx_st = readl_relaxed(gi2c->base + SE_DMA_RX_IRQ_STAT); + dma = readl_relaxed(gi2c->base + SE_GENI_DMA_MODE_EN); + + if (!cur) { + geni_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base, gi2c->ipcl); + GENI_SE_ERR(gi2c->ipcl, false, gi2c->dev, "Spurious irq\n"); + goto irqret; + } + + if ((m_stat & M_CMD_FAILURE_EN) || (dm_rx_st & (DM_I2C_CB_ERR)) || (m_stat & M_CMD_CANCEL_EN) || (m_stat & M_CMD_ABORT_EN)) { @@ -269,12 +299,6 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev) goto irqret; } - if (dma) { - dev_dbg(gi2c->dev, "i2c dma tx:0x%x, dma rx:0x%x\n", dm_tx_st, - dm_rx_st); - goto irqret; - } - if (((m_stat & M_RX_FIFO_WATERMARK_EN) || (m_stat & M_RX_FIFO_LAST_EN)) && (cur->flags & I2C_M_RD)) { u32 rxcnt = rx_st & RX_FIFO_WC_MSK; @@ -283,6 +307,9 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev) u32 temp; int p; + if (gi2c->i2c_ssr.is_ssr_down) + break; + temp = readl_relaxed(gi2c->base + SE_GENI_RX_FIFOn); for (i = gi2c->cur_rd, p = 0; (i < cur->len && p < 4); i++, p++) @@ -303,6 +330,10 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev) for (i = gi2c->cur_wr, p = 0; (i < cur->len && p < 4); i++, p++) temp |= (((u32)(cur->buf[i]) << (p * 8))); + + if (gi2c->i2c_ssr.is_ssr_down) + break; + writel_relaxed(temp, gi2c->base + SE_GENI_TX_FIFOn); gi2c->cur_wr = i; dev_dbg(gi2c->dev, "FIFO i:%d,wrote 0x%x\n", i, temp); @@ -315,6 +346,14 @@ static irqreturn_t geni_i2c_irq(int irq, void *dev) } } irqret: + if (gi2c->i2c_ssr.is_ssr_down) { + gi2c->cmd_done = false; + complete(&gi2c->xfer); + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "%s: SSR down\n", __func__); + return IRQ_HANDLED; + } + if (m_stat) writel_relaxed(m_stat, gi2c->base + SE_GENI_M_IRQ_CLEAR); @@ -452,6 +491,7 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], goto geni_i2c_gsi_xfer_out; } } + if (!gi2c->rx_c) { gi2c->rx_c = dma_request_slave_channel(gi2c->dev, "rx"); if (!gi2c->rx_c) { @@ -530,6 +570,8 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], sizeof(gi2c->go_t)); if (msgs[i].flags & I2C_M_RD) { + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "msg[%d].len:%d R\n", i, gi2c->cur->len); sg_init_table(&gi2c->rx_sg, 1); ret = geni_se_iommu_map_buf(rx_dev, &gi2c->rx_ph, msgs[i].buf, msgs[i].len, @@ -540,6 +582,11 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], ret); goto geni_i2c_gsi_xfer_out; + } else if (gi2c->dbg_buf_ptr) { + gi2c->dbg_buf_ptr[i].virt_buf = + (void *)msgs[i].buf; + gi2c->dbg_buf_ptr[i].map_buf = + (void *)&gi2c->rx_ph; } gi2c->rx_t.dword[0] = MSM_GPI_DMA_W_BUFFER_TRE_DWORD0(gi2c->rx_ph); @@ -570,6 +617,8 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], rx_cookie = dmaengine_submit(gi2c->rx_desc); dma_async_issue_pending(gi2c->rx_c); } else { + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "msg[%d].len:%d W\n", i, gi2c->cur->len); ret = geni_se_iommu_map_buf(tx_dev, &gi2c->tx_ph, msgs[i].buf, msgs[i].len, DMA_TO_DEVICE); @@ -579,7 +628,13 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], ret); goto geni_i2c_gsi_xfer_out; + } else if (gi2c->dbg_buf_ptr) { + gi2c->dbg_buf_ptr[i].virt_buf = + (void *)msgs[i].buf; + gi2c->dbg_buf_ptr[i].map_buf = + (void *)&gi2c->tx_ph; } + gi2c->tx_t.dword[0] = MSM_GPI_DMA_W_BUFFER_TRE_DWORD0(gi2c->tx_ph); gi2c->tx_t.dword[1] = @@ -615,8 +670,11 @@ static int geni_i2c_gsi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], if (!timeout) { GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, - "GSI Txn timed out: %u len: %d\n", - gi2c->xfer_timeout, gi2c->cur->len); + "I2C gsi xfer timeout:%u flags:%d addr:0x%x\n", + gi2c->xfer_timeout, gi2c->cur->flags, + gi2c->cur->addr); + geni_se_dump_dbg_regs(&gi2c->i2c_rsc, gi2c->base, + gi2c->ipcl); gi2c->err = -ETIMEDOUT; } geni_i2c_err_prep_sg: @@ -650,8 +708,21 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, int i, ret = 0, timeout = 0; gi2c->err = 0; - gi2c->cur = &msgs[0]; reinit_completion(&gi2c->xfer); + mutex_lock(&gi2c->i2c_ssr.ssr_lock); + if (gi2c->i2c_ssr.is_ssr_down) { + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "%s: SSR Down\n", __func__); + mutex_unlock(&gi2c->i2c_ssr.ssr_lock); + return -EINVAL; + } + /* Client to respect system suspend */ + if (!pm_runtime_enabled(gi2c->dev)) { + GENI_SE_ERR(gi2c->ipcl, false, gi2c->dev, + "%s: System suspended\n", __func__); + return -EACCES; + } + ret = pm_runtime_get_sync(gi2c->dev); if (ret < 0) { GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, @@ -659,16 +730,26 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, pm_runtime_put_noidle(gi2c->dev); /* Set device in suspended since resume failed */ pm_runtime_set_suspended(gi2c->dev); + mutex_unlock(&gi2c->i2c_ssr.ssr_lock); return ret; } + + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "n:%d addr:0x%x\n", num, msgs[0].addr); + + gi2c->dbg_num = num; + kfree(gi2c->dbg_buf_ptr); + gi2c->dbg_buf_ptr = + kcalloc(num, sizeof(struct dbg_buf_ctxt), GFP_KERNEL); + if (!gi2c->dbg_buf_ptr) + GENI_SE_ERR(gi2c->ipcl, false, gi2c->dev, + "Buf logging pointer not available\n"); + if (gi2c->se_mode == GSI_ONLY) { ret = geni_i2c_gsi_xfer(adap, msgs, num); goto geni_i2c_txn_ret; } - qcom_geni_i2c_conf(gi2c, 0); - dev_dbg(gi2c->dev, "i2c xfer:num:%d, msgs:len:%d,flg:%d\n", - num, msgs[0].len, msgs[0].flags); for (i = 0; i < num; i++) { int stretch = (i < (num - 1)); u32 m_param = 0; @@ -691,9 +772,8 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, break; } if (msgs[i].flags & I2C_M_RD) { - dev_dbg(gi2c->dev, - "READ,n:%d,i:%d len:%d, stretch:%d\n", - num, i, msgs[i].len, stretch); + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "msgs[%d].len:%d R\n", i, msgs[i].len); geni_write_reg(msgs[i].len, gi2c->base, SE_I2C_RX_TRANS_LEN); m_cmd = I2C_READ; @@ -706,12 +786,16 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, mode = FIFO_MODE; ret = geni_se_select_mode(gi2c->base, mode); + } else if (gi2c->dbg_buf_ptr) { + gi2c->dbg_buf_ptr[i].virt_buf = + (void *)msgs[i].buf; + gi2c->dbg_buf_ptr[i].map_buf = + (void *)&rx_dma; } } } else { - dev_dbg(gi2c->dev, - "WRITE:n:%d,i:%d len:%d, stretch:%d, m_param:0x%x\n", - num, i, msgs[i].len, stretch, m_param); + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "msgs[%d].len:%d W\n", i, msgs[i].len); geni_write_reg(msgs[i].len, gi2c->base, SE_I2C_TX_TRANS_LEN); m_cmd = I2C_WRITE; @@ -724,6 +808,11 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, mode = FIFO_MODE; ret = geni_se_select_mode(gi2c->base, mode); + } else if (gi2c->dbg_buf_ptr) { + gi2c->dbg_buf_ptr[i].virt_buf = + (void *)msgs[i].buf; + gi2c->dbg_buf_ptr[i].map_buf = + (void *)&tx_dma; } } if (mode == FIFO_MODE) /* Get FIFO IRQ */ @@ -732,18 +821,40 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, } /* Ensure FIFO write go through before waiting for Done evet */ mb(); + mutex_unlock(&gi2c->i2c_ssr.ssr_lock); timeout = wait_for_completion_timeout(&gi2c->xfer, gi2c->xfer_timeout); - if (!timeout) + mutex_lock(&gi2c->i2c_ssr.ssr_lock); + if (gi2c->i2c_ssr.is_ssr_down) { + ret = -EINVAL; + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "%s: SSR Down\n", __func__); + goto geni_i2c_txn_ret; + } + if (!timeout) { + GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, + "I2C xfer timeout: %d\n", gi2c->xfer_timeout); geni_i2c_err(gi2c, GENI_TIMEOUT); + } if (gi2c->err) { reinit_completion(&gi2c->xfer); - gi2c->cur = NULL; geni_cancel_m_cmd(gi2c->base); + mutex_unlock(&gi2c->i2c_ssr.ssr_lock); timeout = wait_for_completion_timeout(&gi2c->xfer, HZ); - if (!timeout) + mutex_lock(&gi2c->i2c_ssr.ssr_lock); + if (gi2c->i2c_ssr.is_ssr_down) { + ret = -EINVAL; + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "%s: SSR Down\n", __func__); + goto geni_i2c_txn_ret; + } + + if (!timeout) { + GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, + "Abort\n"); geni_abort_m_cmd(gi2c->base); + } } gi2c->cur_wr = 0; @@ -764,9 +875,11 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, geni_se_tx_dma_unprep(gi2c->wrapper_dev, tx_dma, msgs[i].len); } + ret = gi2c->err; if (gi2c->err) { - dev_err(gi2c->dev, "i2c error :%d\n", gi2c->err); + GENI_SE_ERR(gi2c->ipcl, true, gi2c->dev, + "i2c error :%d\n", gi2c->err); break; } } @@ -776,9 +889,13 @@ geni_i2c_txn_ret: pm_runtime_mark_last_busy(gi2c->dev); pm_runtime_put_autosuspend(gi2c->dev); + gi2c->cur_wr = 0; + gi2c->cur_rd = 0; gi2c->cur = NULL; gi2c->err = 0; - dev_dbg(gi2c->dev, "i2c txn ret:%d\n", ret); + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "i2c txn ret:%d\n", ret); + mutex_unlock(&gi2c->i2c_ssr.ssr_lock); return ret; } @@ -806,6 +923,10 @@ static int geni_i2c_probe(struct platform_device *pdev) if (!gi2c) return -ENOMEM; + if (arr_idx++ < MAX_SE) + /* Debug purpose */ + gi2c_dev_dbg[arr_idx] = gi2c; + gi2c->dev = &pdev->dev; snprintf(boot_marker, sizeof(boot_marker), "M - DRIVER GENI_I2C Init"); @@ -845,14 +966,12 @@ static int geni_i2c_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Err getting SE Core clk %d\n", ret); return ret; } - gi2c->i2c_rsc.m_ahb_clk = devm_clk_get(&pdev->dev, "m-ahb"); if (IS_ERR(gi2c->i2c_rsc.m_ahb_clk)) { ret = PTR_ERR(gi2c->i2c_rsc.m_ahb_clk); dev_err(&pdev->dev, "Err getting M AHB clk %d\n", ret); return ret; } - gi2c->i2c_rsc.s_ahb_clk = devm_clk_get(&pdev->dev, "s-ahb"); if (IS_ERR(gi2c->i2c_rsc.s_ahb_clk)) { ret = PTR_ERR(gi2c->i2c_rsc.s_ahb_clk); @@ -937,6 +1056,7 @@ static int geni_i2c_probe(struct platform_device *pdev) gi2c->irq, ret); return ret; } + disable_irq(gi2c->irq); i2c_set_adapdata(&gi2c->adap, gi2c); gi2c->adap.dev.parent = gi2c->dev; @@ -948,7 +1068,14 @@ static int geni_i2c_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(gi2c->dev, I2C_AUTO_SUSPEND_DELAY); pm_runtime_use_autosuspend(gi2c->dev); pm_runtime_enable(gi2c->dev); - i2c_add_adapter(&gi2c->adap); + gi2c->i2c_rsc.rsc_ssr.force_suspend = ssr_i2c_force_suspend; + gi2c->i2c_rsc.rsc_ssr.force_resume = ssr_i2c_force_resume; + mutex_init(&gi2c->i2c_ssr.ssr_lock); + ret = i2c_add_adapter(&gi2c->adap); + if (ret) { + dev_err(gi2c->dev, "Add adapter failed\n"); + return ret; + } snprintf(boot_marker, sizeof(boot_marker), "M - DRIVER GENI_I2C_%d Ready", gi2c->adap.nr); @@ -970,6 +1097,9 @@ static int geni_i2c_remove(struct platform_device *pdev) static int geni_i2c_resume_noirq(struct device *device) { + struct geni_i2c_dev *gi2c = dev_get_drvdata(device); + + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "%s\n", __func__); return 0; } @@ -987,15 +1117,21 @@ static int geni_i2c_hib_resume_noirq(struct device *device) static int geni_i2c_runtime_suspend(struct device *dev) { struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + int ret = 0; if (gi2c->se_mode == FIFO_SE_DMA) { disable_irq(gi2c->irq); - se_geni_resources_off(&gi2c->i2c_rsc); + ret = se_geni_resources_off(&gi2c->i2c_rsc); } else { /* GPIO is set to sleep state already. So just clocks off */ - se_geni_clks_off(&gi2c->i2c_rsc); + ret = se_geni_clks_off(&gi2c->i2c_rsc); } - return 0; + if (ret) + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "%s failed ret:%d\n", __func__, ret); + + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "%s\n", __func__); + return ret; } static int geni_i2c_runtime_resume(struct device *dev) @@ -1034,23 +1170,27 @@ static int geni_i2c_runtime_resume(struct device *dev) gi2c->se_mode = GSI_ONLY; geni_se_select_mode(gi2c->base, GSI_DMA); GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, - "i2c in GSI ONLY mode\n"); + "i2c GSI mode\n"); } else { int gi2c_tx_depth = get_tx_fifo_depth(gi2c->base); gi2c->se_mode = FIFO_SE_DMA; - gi2c->tx_wm = gi2c_tx_depth - 1; geni_se_init(gi2c->base, gi2c->tx_wm, gi2c_tx_depth); se_config_packing(gi2c->base, 8, 4, true); + qcom_geni_i2c_conf(gi2c, 0); GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "i2c fifo/se-dma mode. fifo depth:%d\n", gi2c_tx_depth); } + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "i2c-%d: %s\n", + gi2c->adap.nr, dev_name(gi2c->dev)); } + if (gi2c->se_mode == FIFO_SE_DMA) enable_irq(gi2c->irq); + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "%s\n", __func__); return 0; } @@ -1067,6 +1207,8 @@ static int geni_i2c_suspend_noirq(struct device *device) return -EBUSY; } if (!pm_runtime_status_suspended(device)) { + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, + "%s\n", __func__); geni_i2c_runtime_suspend(device); pm_runtime_disable(device); pm_runtime_set_suspended(device); @@ -1102,6 +1244,39 @@ static const struct dev_pm_ops geni_i2c_pm_ops = { .thaw = geni_i2c_hib_resume_noirq, }; +static void ssr_i2c_force_suspend(struct device *dev) +{ + struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + int ret = 0; + + mutex_lock(&gi2c->i2c_ssr.ssr_lock); + gi2c->i2c_ssr.is_ssr_down = true; + if (!pm_runtime_status_suspended(gi2c->dev)) { + ret = geni_i2c_runtime_suspend(gi2c->dev); + if (ret) { + dev_err(gi2c->dev, "%s failed ret:%d\n", __func__, ret); + } else { + pm_runtime_disable(gi2c->dev); + pm_runtime_set_suspended(gi2c->dev); + pm_runtime_enable(gi2c->dev); + } + } + + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "%s done\n", __func__); + mutex_unlock(&gi2c->i2c_ssr.ssr_lock); +} + +static void ssr_i2c_force_resume(struct device *dev) +{ + struct geni_i2c_dev *gi2c = dev_get_drvdata(dev); + + mutex_lock(&gi2c->i2c_ssr.ssr_lock); + gi2c->i2c_ssr.is_ssr_down = false; + gi2c->se_mode = UNINITIALIZED; + GENI_SE_DBG(gi2c->ipcl, false, gi2c->dev, "%s done\n", __func__); + mutex_unlock(&gi2c->i2c_ssr.ssr_lock); +} + static const struct of_device_id geni_i2c_dt_match[] = { { .compatible = "qcom,i2c-geni" }, {} diff --git a/drivers/platform/msm/qcom-geni-se.c b/drivers/platform/msm/qcom-geni-se.c index 66a8d3f45274..d2b8a8d75099 100644 --- a/drivers/platform/msm/qcom-geni-se.c +++ b/drivers/platform/msm/qcom-geni-se.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -159,7 +159,7 @@ static int geni_se_iommu_map_and_attach(struct geni_se_device *geni_se_dev); */ unsigned int geni_read_reg_nolog(void __iomem *base, int offset) { - return readl_relaxed(base + offset); + return readl_relaxed_no_log(base + offset); } EXPORT_SYMBOL(geni_read_reg_nolog); @@ -171,7 +171,7 @@ EXPORT_SYMBOL(geni_read_reg_nolog); */ void geni_write_reg_nolog(unsigned int value, void __iomem *base, int offset) { - return writel_relaxed(value, (base + offset)); + return writel_relaxed_no_log(value, (base + offset)); } EXPORT_SYMBOL(geni_write_reg_nolog); @@ -458,11 +458,7 @@ static int geni_se_ssr_notify_block(struct notifier_block *n, "SSR notification before power down\n"); break; case SUBSYS_AFTER_POWERUP: - if (dev->ssr.probe_completed) - geni_se_ssc_qup_up(dev); - else - dev->ssr.probe_completed = true; - + geni_se_ssc_qup_up(dev); GENI_SE_DBG(dev->log_ctx, false, NULL, "SSR notification after power up\n"); break; @@ -1233,7 +1229,7 @@ int geni_se_resources_init(struct se_geni_rsc *rsc, INIT_LIST_HEAD(&rsc->ab_list); INIT_LIST_HEAD(&rsc->ib_list); - if (geni_se_dev->ssr.subsys_name && rsc->rsc_ssr.ssr_enable) { + if (geni_se_dev->ssr.subsys_name) { INIT_LIST_HEAD(&rsc->rsc_ssr.active_list); list_add(&rsc->rsc_ssr.active_list, &geni_se_dev->ssr.active_list_head); @@ -1722,16 +1718,6 @@ int geni_se_iommu_free_buf(struct device *wrapper_dev, dma_addr_t *iova, } EXPORT_SYMBOL(geni_se_iommu_free_buf); -struct device *geni_get_iommu_dev(struct device *wrapper_dev) -{ - struct geni_se_device *geni_se_dev; - - geni_se_dev = dev_get_drvdata(wrapper_dev); - - return geni_se_dev->cb_dev; -} -EXPORT_SYMBOL(geni_get_iommu_dev); - /** * geni_se_dump_dbg_regs() - Print relevant registers that capture most * accurately the state of an SE. @@ -2013,7 +1999,7 @@ static int geni_se_probe(struct platform_device *pdev) geni_se_dev->log_ctx = ipc_log_context_create(NUM_LOG_PAGES, dev_name(geni_se_dev->dev), 0); if (!geni_se_dev->log_ctx) - dev_dbg(dev, "%s Failed to allocate log context\n", __func__); + dev_err(dev, "%s Failed to allocate log context\n", __func__); dev_set_drvdata(dev, geni_se_dev); ret = of_platform_populate(dev->of_node, geni_se_dt_match, NULL, dev); @@ -2045,19 +2031,18 @@ static int geni_se_probe(struct platform_device *pdev) } INIT_LIST_HEAD(&geni_se_dev->ssr.active_list_head); - geni_se_dev->ssr.probe_completed = false; ret = geni_se_ssc_qup_ssr_reg(geni_se_dev); if (ret) { dev_err(dev, "Unable to register SSR notification\n"); return ret; } - sysfs_create_file(&geni_se_dev->dev->kobj, + ret = sysfs_create_file(&geni_se_dev->dev->kobj, &dev_attr_ssc_qup_state.attr); + if (ret) + dev_err(dev, "Unable to create sysfs file\n"); } - device_enable_async_suspend(&pdev->dev); - GENI_SE_DBG(geni_se_dev->log_ctx, false, NULL, "%s: Probe successful\n", __func__); return 0; diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index a1a19c0a31c3..585786b3cfe5 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -103,6 +104,10 @@ #define MAX_TX_SG (3) #define NUM_SPI_XFER (8) +#define SPI_ERROR_BITS (M_CMD_OVERRUN_EN | M_ILLEGAL_CMD_EN | \ + M_RX_FIFO_RD_ERR_EN | M_RX_FIFO_WR_ERR_EN | \ + M_TX_FIFO_RD_ERR_EN | M_TX_FIFO_WR_ERR_EN) + struct gsi_desc_cb { struct spi_master *spi; struct spi_transfer *xfer; @@ -173,8 +178,8 @@ struct spi_geni_master { }; static void spi_slv_setup(struct spi_geni_master *mas); -static int ssr_spi_force_suspend(struct device *dev); -static int ssr_spi_force_resume(struct device *dev); +static void ssr_spi_force_suspend(struct device *dev); +static void ssr_spi_force_resume(struct device *dev); static ssize_t show_slave_state(struct device *dev, struct device_attribute *attr, char *buf) @@ -278,6 +283,7 @@ static int get_spi_clk_cfg(u32 speed_hz, struct spi_geni_master *mas, static void spi_setup_word_len(struct spi_geni_master *mas, u32 mode, int bits_per_word) { + struct spi_master *spi = get_spi_master(mas->dev); int pack_words = 1; bool msb_first = (mode & SPI_LSB_FIRST) ? false : true; u32 word_len = geni_read_reg(mas->base, SE_SPI_WORD_LEN); @@ -289,10 +295,18 @@ static void spi_setup_word_len(struct spi_geni_master *mas, u32 mode, */ if (!(mas->tx_fifo_width % bits_per_word)) pack_words = mas->tx_fifo_width / bits_per_word; - word_len &= ~WORD_LEN_MSK; - word_len |= ((bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK); se_config_packing(mas->base, bits_per_word, pack_words, msb_first); - geni_write_reg(word_len, mas->base, SE_SPI_WORD_LEN); + + /* + * Don't configure SPI word length for SPI Slave it is an optional + * property for SPI Slave. H/W will take care of SPI slave word + * if SPI_SLAVE_EN bit is set. + */ + if (!spi->slave) { + word_len &= ~WORD_LEN_MSK; + word_len |= ((bits_per_word - MIN_WORD_LEN) & WORD_LEN_MSK); + geni_write_reg(word_len, mas->base, SE_SPI_WORD_LEN); + } se_get_packing_config(bits_per_word, pack_words, msb_first, &cfg0, &cfg1); GENI_SE_DBG(mas->ipc, false, mas->dev, @@ -328,9 +342,15 @@ static int setup_fifo_params(struct spi_device *spi_slv, if (mode & SPI_CPOL) cpol |= CPOL; - if (!spi->slave) { - if (mode & SPI_CPHA) - cpha |= CPHA; + if (mode & SPI_CPHA) + cpha |= CPHA; + + /* SPI slave supports only mode 1, log unsuppoted mode and exit */ + if (spi->slave && !(cpol == 0 && cpha == 1)) { + GENI_SE_DBG(mas->ipc, false, mas->dev, + "%s: Unsupported SPI Slave mode cpol %d cpha %d\n", + __func__, cpol, cpha); + return -EINVAL; } if (spi_slv->mode & SPI_CS_HIGH) @@ -1042,10 +1062,11 @@ static int spi_geni_unprepare_transfer_hardware(struct spi_master *spi) if (count < 0) GENI_SE_ERR(mas->ipc, false, NULL, "suspend usage count mismatch:%d", count); - } else { + } else if (!pm_runtime_suspended(mas->dev)) { pm_runtime_mark_last_busy(mas->dev); pm_runtime_put_autosuspend(mas->dev); } + return 0; } @@ -1184,16 +1205,22 @@ static void setup_fifo_xfer(struct spi_transfer *xfer, mb(); } -static void handle_fifo_timeout(struct spi_geni_master *mas, +static void handle_fifo_timeout(struct spi_master *spi, struct spi_transfer *xfer) { + struct spi_geni_master *mas = spi_master_get_devdata(spi); unsigned long timeout; geni_se_dump_dbg_regs(&mas->spi_rsc, mas->base, mas->ipc); - reinit_completion(&mas->xfer_done); - geni_cancel_m_cmd(mas->base); + if (mas->cur_xfer_mode == FIFO_MODE) geni_write_reg(0, mas->base, SE_GENI_TX_WATERMARK_REG); + + if (spi->slave) + goto dma_unprep; + + reinit_completion(&mas->xfer_done); + geni_cancel_m_cmd(mas->base); /* Ensure cmd cancel is written */ mb(); timeout = wait_for_completion_timeout(&mas->xfer_done, HZ); @@ -1208,6 +1235,7 @@ static void handle_fifo_timeout(struct spi_geni_master *mas, dev_err(mas->dev, "Failed to cancel/abort m_cmd\n"); } +dma_unprep: if (mas->cur_xfer_mode == SE_DMA) { if (xfer->tx_buf) { reinit_completion(&mas->xfer_done); @@ -1234,6 +1262,8 @@ static void handle_fifo_timeout(struct spi_geni_master *mas, xfer->rx_dma, xfer->len); } } + if (spi->slave && !mas->dis_autosuspend) + pm_runtime_put_sync_suspend(mas->dev); } @@ -1250,18 +1280,18 @@ static int spi_geni_transfer_one(struct spi_master *spi, return -EINVAL; } - mutex_lock(&mas->spi_ssr.ssr_lock); - if (mas->spi_ssr.is_ssr_down || !mas->spi_ssr.xfer_prepared) { - mutex_unlock(&mas->spi_ssr.ssr_lock); - return -EINVAL; - } - /* Check for zero length transfer */ if (xfer->len < 1) { dev_err(mas->dev, "Zero length transfer\n"); return -EINVAL; } + mutex_lock(&mas->spi_ssr.ssr_lock); + if (mas->spi_ssr.is_ssr_down || !mas->spi_ssr.xfer_prepared) { + mutex_unlock(&mas->spi_ssr.ssr_lock); + return -EINVAL; + } + if (mas->cur_xfer_mode != GSI_DMA) { reinit_completion(&mas->xfer_done); setup_fifo_xfer(xfer, mas, slv->mode, spi); @@ -1348,10 +1378,7 @@ err_gsi_geni_transfer_one: mutex_unlock(&mas->spi_ssr.ssr_lock); return ret; err_fifo_geni_transfer_one: - if (!spi->slave) - handle_fifo_timeout(mas, xfer); - if (spi->slave) - geni_se_dump_dbg_regs(&mas->spi_rsc, mas->base, mas->ipc); + handle_fifo_timeout(spi, xfer); err_ssr_transfer_one: mutex_unlock(&mas->spi_ssr.ssr_lock); return ret; @@ -1485,6 +1512,14 @@ static irqreturn_t geni_spi_irq(int irq, void *data) } m_irq = geni_read_reg(mas->base, SE_GENI_M_IRQ_STATUS); + if (SPI_ERROR_BITS & m_irq) { + dev_err_ratelimited(mas->dev, "%s: Error m_irq status:0x%x\n", + __func__, m_irq); + GENI_SE_ERR(mas->ipc, false, mas->dev, + "%s: Error m_irq status:0x%x\n", __func__, m_irq); + goto exit_geni_spi_irq; + } + if (mas->cur_xfer_mode == FIFO_MODE) { if ((m_irq & M_RX_FIFO_WATERMARK_EN) || (m_irq & M_RX_FIFO_LAST_EN)) @@ -1593,8 +1628,6 @@ static int spi_geni_probe(struct platform_device *pdev) } geni_mas->wrapper_dev = &wrapper_pdev->dev; geni_mas->spi_rsc.wrapper_dev = &wrapper_pdev->dev; - rsc->rsc_ssr.ssr_enable = of_property_read_bool(pdev->dev.of_node, - "ssr-enable"); ret = geni_se_resources_init(rsc, SPI_CORE2X_VOTE, (DEFAULT_SE_CLK * DEFAULT_BUS_WIDTH)); if (ret) { @@ -1704,6 +1737,7 @@ static int spi_geni_probe(struct platform_device *pdev) ret = geni_mas->irq; goto spi_geni_probe_unmap; } + irq_set_status_flags(geni_mas->irq, IRQ_NOAUTOEN); ret = devm_request_irq(&pdev->dev, geni_mas->irq, geni_spi_irq, IRQF_TRIGGER_HIGH, "spi_geni", geni_mas); if (ret) { @@ -1749,7 +1783,7 @@ static int spi_geni_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to register SPI master\n"); goto spi_geni_probe_unmap; } - sysfs_create_file(&(geni_mas->dev->kobj), + ret = sysfs_create_file(&(geni_mas->dev->kobj), &dev_attr_spi_slave_state.attr); snprintf(boot_marker, sizeof(boot_marker), "M - DRIVER GENI_SPI_%d Ready", spi->bus_num); @@ -1783,6 +1817,7 @@ static int spi_geni_runtime_suspend(struct device *dev) struct spi_master *spi = get_spi_master(dev); struct spi_geni_master *geni_mas = spi_master_get_devdata(spi); + disable_irq(geni_mas->irq); if (geni_mas->shared_se) { ret = se_geni_clks_off(&geni_mas->spi_rsc); if (ret) @@ -1814,6 +1849,7 @@ static int spi_geni_runtime_resume(struct device *dev) } else { ret = se_geni_resources_on(&geni_mas->spi_rsc); } + enable_irq(geni_mas->irq); return ret; } @@ -1870,7 +1906,7 @@ static int spi_geni_suspend(struct device *dev) } #endif -static int ssr_spi_force_suspend(struct device *dev) +static void ssr_spi_force_suspend(struct device *dev) { struct spi_master *spi = get_spi_master(dev); struct spi_geni_master *mas = spi_master_get_devdata(spi); @@ -1878,7 +1914,6 @@ static int ssr_spi_force_suspend(struct device *dev) mutex_lock(&mas->spi_ssr.ssr_lock); mas->spi_ssr.xfer_prepared = false; - disable_irq(mas->irq); mas->spi_ssr.is_ssr_down = true; complete(&mas->xfer_done); @@ -1895,22 +1930,17 @@ static int ssr_spi_force_suspend(struct device *dev) GENI_SE_DBG(mas->ipc, false, mas->dev, "force suspend done\n"); mutex_unlock(&mas->spi_ssr.ssr_lock); - - return ret; } -static int ssr_spi_force_resume(struct device *dev) +static void ssr_spi_force_resume(struct device *dev) { struct spi_master *spi = get_spi_master(dev); struct spi_geni_master *mas = spi_master_get_devdata(spi); mutex_lock(&mas->spi_ssr.ssr_lock); mas->spi_ssr.is_ssr_down = false; - enable_irq(mas->irq); GENI_SE_DBG(mas->ipc, false, mas->dev, "force resume done\n"); mutex_unlock(&mas->spi_ssr.ssr_lock); - - return 0; } static const struct dev_pm_ops spi_geni_pm_ops = { diff --git a/drivers/tty/serial/8250/8250_aspeed_vuart.c b/drivers/tty/serial/8250/8250_aspeed_vuart.c index 0a89df390f24..33a801353114 100644 --- a/drivers/tty/serial/8250/8250_aspeed_vuart.c +++ b/drivers/tty/serial/8250/8250_aspeed_vuart.c @@ -256,6 +256,7 @@ static int aspeed_vuart_probe(struct platform_device *pdev) port.port.line = rc; port.port.irq = irq_of_parse_and_map(np, 0); + port.port.irqflags = IRQF_SHARED; port.port.iotype = UPIO_MEM; port.port.type = PORT_16550A; port.port.uartclk = clk; diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c index 5017a0f46b82..c698ebab6d3b 100644 --- a/drivers/tty/serial/8250/8250_core.c +++ b/drivers/tty/serial/8250/8250_core.c @@ -181,7 +181,7 @@ static int serial_link_irq_chain(struct uart_8250_port *up) struct hlist_head *h; struct hlist_node *n; struct irq_info *i; - int ret; + int ret, irq_flags = up->port.flags & UPF_SHARE_IRQ ? IRQF_SHARED : 0; mutex_lock(&hash_mutex); @@ -216,8 +216,9 @@ static int serial_link_irq_chain(struct uart_8250_port *up) INIT_LIST_HEAD(&up->list); i->head = &up->list; spin_unlock_irq(&i->lock); + irq_flags |= up->port.irqflags; ret = request_irq(up->port.irq, serial8250_interrupt, - up->port.irqflags, up->port.name, i); + irq_flags, up->port.name, i); if (ret < 0) serial_do_unlink(i, up); } diff --git a/drivers/tty/serial/8250/8250_exar.c b/drivers/tty/serial/8250/8250_exar.c index 899f36b59af7..411b4b03457b 100644 --- a/drivers/tty/serial/8250/8250_exar.c +++ b/drivers/tty/serial/8250/8250_exar.c @@ -27,14 +27,6 @@ #include "8250.h" -#define PCI_DEVICE_ID_ACCES_COM_2S 0x1052 -#define PCI_DEVICE_ID_ACCES_COM_4S 0x105d -#define PCI_DEVICE_ID_ACCES_COM_8S 0x106c -#define PCI_DEVICE_ID_ACCES_COM232_8 0x10a8 -#define PCI_DEVICE_ID_ACCES_COM_2SM 0x10d2 -#define PCI_DEVICE_ID_ACCES_COM_4SM 0x10db -#define PCI_DEVICE_ID_ACCES_COM_8SM 0x10ea - #define PCI_DEVICE_ID_COMMTECH_4224PCI335 0x0002 #define PCI_DEVICE_ID_COMMTECH_4222PCI335 0x0004 #define PCI_DEVICE_ID_COMMTECH_2324PCI335 0x000a @@ -570,22 +562,6 @@ static int __maybe_unused exar_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(exar_pci_pm, exar_suspend, exar_resume); -static const struct exar8250_board acces_com_2x = { - .num_ports = 2, - .setup = pci_xr17c154_setup, -}; - -static const struct exar8250_board acces_com_4x = { - .num_ports = 4, - .setup = pci_xr17c154_setup, -}; - -static const struct exar8250_board acces_com_8x = { - .num_ports = 8, - .setup = pci_xr17c154_setup, -}; - - static const struct exar8250_board pbn_fastcom335_2 = { .num_ports = 2, .setup = pci_fastcom335_setup, @@ -656,15 +632,6 @@ static const struct exar8250_board pbn_exar_XR17V8358 = { } static const struct pci_device_id exar_pci_tbl[] = { - EXAR_DEVICE(ACCESSIO, ACCES_COM_2S, acces_com_2x), - EXAR_DEVICE(ACCESSIO, ACCES_COM_4S, acces_com_4x), - EXAR_DEVICE(ACCESSIO, ACCES_COM_8S, acces_com_8x), - EXAR_DEVICE(ACCESSIO, ACCES_COM232_8, acces_com_8x), - EXAR_DEVICE(ACCESSIO, ACCES_COM_2SM, acces_com_2x), - EXAR_DEVICE(ACCESSIO, ACCES_COM_4SM, acces_com_4x), - EXAR_DEVICE(ACCESSIO, ACCES_COM_8SM, acces_com_8x), - - CONNECT_DEVICE(XR17C152, UART_2_232, pbn_connect), CONNECT_DEVICE(XR17C154, UART_4_232, pbn_connect), CONNECT_DEVICE(XR17C158, UART_8_232, pbn_connect), diff --git a/drivers/tty/serial/8250/8250_port.c b/drivers/tty/serial/8250/8250_port.c index 90a93c001e16..a73d2bc4b685 100644 --- a/drivers/tty/serial/8250/8250_port.c +++ b/drivers/tty/serial/8250/8250_port.c @@ -2258,10 +2258,6 @@ int serial8250_do_startup(struct uart_port *port) } } - /* Check if we need to have shared IRQs */ - if (port->irq && (up->port.flags & UPF_SHARE_IRQ)) - up->port.irqflags |= IRQF_SHARED; - if (port->irq && !(up->port.flags & UPF_NO_THRE_TEST)) { unsigned char iir1; /* diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index e55b55633721..637f72fb6427 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -2605,7 +2605,6 @@ static int pl011_setup_port(struct device *dev, struct uart_amba_port *uap, uap->port.fifosize = uap->fifosize; uap->port.flags = UPF_BOOT_AUTOCONF; uap->port.line = index; - spin_lock_init(&uap->port.lock); amba_ports[index] = uap; diff --git a/drivers/tty/serial/ar933x_uart.c b/drivers/tty/serial/ar933x_uart.c index ac56a5131a9c..ed545a61413c 100644 --- a/drivers/tty/serial/ar933x_uart.c +++ b/drivers/tty/serial/ar933x_uart.c @@ -289,10 +289,6 @@ static void ar933x_uart_set_termios(struct uart_port *port, ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, AR933X_UART_CS_HOST_INT_EN); - /* enable RX and TX ready overide */ - ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, - AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE); - /* reenable the UART */ ar933x_uart_rmw(up, AR933X_UART_CS_REG, AR933X_UART_CS_IF_MODE_M << AR933X_UART_CS_IF_MODE_S, @@ -425,10 +421,6 @@ static int ar933x_uart_startup(struct uart_port *port) ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, AR933X_UART_CS_HOST_INT_EN); - /* enable RX and TX ready overide */ - ar933x_uart_rmw_set(up, AR933X_UART_CS_REG, - AR933X_UART_CS_TX_READY_ORIDE | AR933X_UART_CS_RX_READY_ORIDE); - /* Enable RX interrupts */ up->ier = AR933X_UART_INT_RX_VALID; ar933x_uart_write(up, AR933X_UART_INT_EN_REG, up->ier); diff --git a/drivers/tty/serial/atmel_serial.c b/drivers/tty/serial/atmel_serial.c index a00227d312d3..367ce812743e 100644 --- a/drivers/tty/serial/atmel_serial.c +++ b/drivers/tty/serial/atmel_serial.c @@ -498,8 +498,7 @@ static void atmel_stop_tx(struct uart_port *port) atmel_uart_writel(port, ATMEL_US_IDR, atmel_port->tx_done_mask); if (atmel_uart_is_half_duplex(port)) - if (!atomic_read(&atmel_port->tasklet_shutdown)) - atmel_start_rx(port); + atmel_start_rx(port); } diff --git a/drivers/tty/serial/imx.c b/drivers/tty/serial/imx.c index dfa2db6ed322..a81a5be0cf7a 100644 --- a/drivers/tty/serial/imx.c +++ b/drivers/tty/serial/imx.c @@ -80,7 +80,7 @@ #define UCR1_IDEN (1<<12) /* Idle condition interrupt */ #define UCR1_ICD_REG(x) (((x) & 3) << 10) /* idle condition detect */ #define UCR1_RRDYEN (1<<9) /* Recv ready interrupt enable */ -#define UCR1_RXDMAEN (1<<8) /* Recv ready DMA enable */ +#define UCR1_RDMAEN (1<<8) /* Recv ready DMA enable */ #define UCR1_IREN (1<<7) /* Infrared interface enable */ #define UCR1_TXMPTYEN (1<<6) /* Transimitter empty interrupt enable */ #define UCR1_RTSDEN (1<<5) /* RTS delta interrupt enable */ @@ -352,30 +352,6 @@ static void imx_port_rts_auto(struct imx_port *sport, unsigned long *ucr2) *ucr2 |= UCR2_CTSC; } -/* - * interrupts disabled on entry - */ -static void imx_start_rx(struct uart_port *port) -{ - struct imx_port *sport = (struct imx_port *)port; - unsigned int ucr1, ucr2; - - ucr1 = readl(port->membase + UCR1); - ucr2 = readl(port->membase + UCR2); - - ucr2 |= UCR2_RXEN; - - if (sport->dma_is_enabled) { - ucr1 |= UCR1_RXDMAEN | UCR1_ATDMAEN; - } else { - ucr1 |= UCR1_RRDYEN; - } - - /* Write UCR2 first as it includes RXEN */ - writel(ucr2, port->membase + UCR2); - writel(ucr1, port->membase + UCR1); -} - /* * interrupts disabled on entry */ @@ -402,10 +378,9 @@ static void imx_stop_tx(struct uart_port *port) imx_port_rts_active(sport, &temp); else imx_port_rts_inactive(sport, &temp); + temp |= UCR2_RXEN; writel(temp, port->membase + UCR2); - imx_start_rx(port); - temp = readl(port->membase + UCR4); temp &= ~UCR4_TCEN; writel(temp, port->membase + UCR4); @@ -418,7 +393,7 @@ static void imx_stop_tx(struct uart_port *port) static void imx_stop_rx(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; - unsigned long ucr1, ucr2; + unsigned long temp; if (sport->dma_is_enabled && sport->dma_is_rxing) { if (sport->port.suspended) { @@ -429,18 +404,12 @@ static void imx_stop_rx(struct uart_port *port) } } - ucr1 = readl(sport->port.membase + UCR1); - ucr2 = readl(sport->port.membase + UCR2); + temp = readl(sport->port.membase + UCR2); + writel(temp & ~UCR2_RXEN, sport->port.membase + UCR2); - if (sport->dma_is_enabled) { - ucr1 &= ~(UCR1_RXDMAEN | UCR1_ATDMAEN); - } else { - ucr1 &= ~UCR1_RRDYEN; - } - writel(ucr1, port->membase + UCR1); - - ucr2 &= ~UCR2_RXEN; - writel(ucr2, port->membase + UCR2); + /* disable the `Receiver Ready Interrrupt` */ + temp = readl(sport->port.membase + UCR1); + writel(temp & ~UCR1_RRDYEN, sport->port.membase + UCR1); } /* @@ -538,11 +507,6 @@ static void dma_tx_callback(void *data) if (!uart_circ_empty(xmit) && !uart_tx_stopped(&sport->port)) imx_dma_tx(sport); - else if (sport->port.rs485.flags & SER_RS485_ENABLED) { - temp = readl(sport->port.membase + UCR4); - temp |= UCR4_TCEN; - writel(temp, sport->port.membase + UCR4); - } spin_unlock_irqrestore(&sport->port.lock, flags); } @@ -560,13 +524,9 @@ static void imx_dma_tx(struct imx_port *sport) if (sport->dma_is_txing) return; - temp = readl(sport->port.membase + UCR4); - temp &= ~UCR4_TCEN; - writel(temp, sport->port.membase + UCR4); - sport->tx_bytes = uart_circ_chars_pending(xmit); - if (xmit->tail < xmit->head || xmit->head == 0) { + if (xmit->tail < xmit->head) { sport->dma_tx_nents = 1; sg_init_one(sgl, xmit->buf + xmit->tail, sport->tx_bytes); } else { @@ -621,20 +581,14 @@ static void imx_start_tx(struct uart_port *port) imx_port_rts_active(sport, &temp); else imx_port_rts_inactive(sport, &temp); + if (!(port->rs485.flags & SER_RS485_RX_DURING_TX)) + temp &= ~UCR2_RXEN; writel(temp, port->membase + UCR2); - if (!(port->rs485.flags & SER_RS485_RX_DURING_TX)) - imx_stop_rx(port); - - /* - * Enable transmitter and shifter empty irq only if DMA is off. - * In the DMA case this is done in the tx-callback. - */ - if (!sport->dma_is_enabled) { - temp = readl(port->membase + UCR4); - temp |= UCR4_TCEN; - writel(temp, port->membase + UCR4); - } + /* enable transmitter and shifter empty irq */ + temp = readl(port->membase + UCR4); + temp |= UCR4_TCEN; + writel(temp, port->membase + UCR4); } if (!sport->dma_is_enabled) { @@ -857,42 +811,14 @@ static void imx_mctrl_check(struct imx_port *sport) static irqreturn_t imx_int(int irq, void *dev_id) { struct imx_port *sport = dev_id; - unsigned int usr1, usr2, ucr1, ucr2, ucr3, ucr4; + unsigned int sts; + unsigned int sts2; irqreturn_t ret = IRQ_NONE; - usr1 = readl(sport->port.membase + USR1); - usr2 = readl(sport->port.membase + USR2); - ucr1 = readl(sport->port.membase + UCR1); - ucr2 = readl(sport->port.membase + UCR2); - ucr3 = readl(sport->port.membase + UCR3); - ucr4 = readl(sport->port.membase + UCR4); + sts = readl(sport->port.membase + USR1); + sts2 = readl(sport->port.membase + USR2); - /* - * Even if a condition is true that can trigger an irq only handle it if - * the respective irq source is enabled. This prevents some undesired - * actions, for example if a character that sits in the RX FIFO and that - * should be fetched via DMA is tried to be fetched using PIO. Or the - * receiver is currently off and so reading from URXD0 results in an - * exception. So just mask the (raw) status bits for disabled irqs. - */ - if ((ucr1 & UCR1_RRDYEN) == 0) - usr1 &= ~USR1_RRDY; - if ((ucr2 & UCR2_ATEN) == 0) - usr1 &= ~USR1_AGTIM; - if ((ucr1 & UCR1_TXMPTYEN) == 0) - usr1 &= ~USR1_TRDY; - if ((ucr4 & UCR4_TCEN) == 0) - usr2 &= ~USR2_TXDC; - if ((ucr3 & UCR3_DTRDEN) == 0) - usr1 &= ~USR1_DTRD; - if ((ucr1 & UCR1_RTSDEN) == 0) - usr1 &= ~USR1_RTSD; - if ((ucr3 & UCR3_AWAKEN) == 0) - usr1 &= ~USR1_AWAKE; - if ((ucr4 & UCR4_OREN) == 0) - usr2 &= ~USR2_ORE; - - if (usr1 & (USR1_RRDY | USR1_AGTIM)) { + if (sts & (USR1_RRDY | USR1_AGTIM)) { if (sport->dma_is_enabled) imx_dma_rxint(sport); else @@ -900,15 +826,18 @@ static irqreturn_t imx_int(int irq, void *dev_id) ret = IRQ_HANDLED; } - if ((usr1 & USR1_TRDY) || (usr2 & USR2_TXDC)) { + if ((sts & USR1_TRDY && + readl(sport->port.membase + UCR1) & UCR1_TXMPTYEN) || + (sts2 & USR2_TXDC && + readl(sport->port.membase + UCR4) & UCR4_TCEN)) { imx_txint(irq, dev_id); ret = IRQ_HANDLED; } - if (usr1 & USR1_DTRD) { + if (sts & USR1_DTRD) { unsigned long flags; - if (usr1 & USR1_DTRD) + if (sts & USR1_DTRD) writel(USR1_DTRD, sport->port.membase + USR1); spin_lock_irqsave(&sport->port.lock, flags); @@ -918,17 +847,17 @@ static irqreturn_t imx_int(int irq, void *dev_id) ret = IRQ_HANDLED; } - if (usr1 & USR1_RTSD) { + if (sts & USR1_RTSD) { imx_rtsint(irq, dev_id); ret = IRQ_HANDLED; } - if (usr1 & USR1_AWAKE) { + if (sts & USR1_AWAKE) { writel(USR1_AWAKE, sport->port.membase + USR1); ret = IRQ_HANDLED; } - if (usr2 & USR2_ORE) { + if (sts2 & USR2_ORE) { sport->port.icount.overrun++; writel(USR2_ORE, sport->port.membase + USR2); ret = IRQ_HANDLED; @@ -1277,7 +1206,7 @@ static void imx_enable_dma(struct imx_port *sport) /* set UCR1 */ temp = readl(sport->port.membase + UCR1); - temp |= UCR1_RXDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN; + temp |= UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN; writel(temp, sport->port.membase + UCR1); temp = readl(sport->port.membase + UCR2); @@ -1295,7 +1224,7 @@ static void imx_disable_dma(struct imx_port *sport) /* clear UCR1 */ temp = readl(sport->port.membase + UCR1); - temp &= ~(UCR1_RXDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN); + temp &= ~(UCR1_RDMAEN | UCR1_TDMAEN | UCR1_ATDMAEN); writel(temp, sport->port.membase + UCR1); /* clear UCR2 */ @@ -1360,9 +1289,11 @@ static int imx_startup(struct uart_port *port) writel(USR1_RTSD | USR1_DTRD, sport->port.membase + USR1); writel(USR2_ORE, sport->port.membase + USR2); + if (sport->dma_is_inited && !sport->dma_is_enabled) + imx_enable_dma(sport); + temp = readl(sport->port.membase + UCR1); - temp &= ~UCR1_RRDYEN; - temp |= UCR1_UARTEN; + temp |= UCR1_RRDYEN | UCR1_UARTEN; if (sport->have_rtscts) temp |= UCR1_RTSDEN; @@ -1401,13 +1332,14 @@ static int imx_startup(struct uart_port *port) */ imx_enable_ms(&sport->port); - if (sport->dma_is_inited) { - imx_enable_dma(sport); + /* + * Start RX DMA immediately instead of waiting for RX FIFO interrupts. + * In our iMX53 the average delay for the first reception dropped from + * approximately 35000 microseconds to 1000 microseconds. + */ + if (sport->dma_is_enabled) { + imx_disable_rx_int(sport); start_rx_dma(sport); - } else { - temp = readl(sport->port.membase + UCR1); - temp |= UCR1_RRDYEN; - writel(temp, sport->port.membase + UCR1); } spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1454,8 +1386,7 @@ static void imx_shutdown(struct uart_port *port) spin_lock_irqsave(&sport->port.lock, flags); temp = readl(sport->port.membase + UCR1); - temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN | - UCR1_RXDMAEN | UCR1_ATDMAEN); + temp &= ~(UCR1_TXMPTYEN | UCR1_RRDYEN | UCR1_RTSDEN | UCR1_UARTEN); writel(temp, sport->port.membase + UCR1); spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1728,7 +1659,7 @@ static int imx_poll_init(struct uart_port *port) { struct imx_port *sport = (struct imx_port *)port; unsigned long flags; - unsigned long ucr1, ucr2; + unsigned long temp; int retval; retval = clk_prepare_enable(sport->clk_ipg); @@ -1742,29 +1673,16 @@ static int imx_poll_init(struct uart_port *port) spin_lock_irqsave(&sport->port.lock, flags); - /* - * Be careful about the order of enabling bits here. First enable the - * receiver (UARTEN + RXEN) and only then the corresponding irqs. - * This prevents that a character that already sits in the RX fifo is - * triggering an irq but the try to fetch it from there results in an - * exception because UARTEN or RXEN is still off. - */ - ucr1 = readl(port->membase + UCR1); - ucr2 = readl(port->membase + UCR2); - + temp = readl(sport->port.membase + UCR1); if (is_imx1_uart(sport)) - ucr1 |= IMX1_UCR1_UARTCLKEN; + temp |= IMX1_UCR1_UARTCLKEN; + temp |= UCR1_UARTEN | UCR1_RRDYEN; + temp &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN); + writel(temp, sport->port.membase + UCR1); - ucr1 |= UCR1_UARTEN; - ucr1 &= ~(UCR1_TXMPTYEN | UCR1_RTSDEN | UCR1_RRDYEN); - - ucr2 |= UCR2_RXEN; - - writel(ucr1, sport->port.membase + UCR1); - writel(ucr2, sport->port.membase + UCR2); - - /* now enable irqs */ - writel(ucr1 | UCR1_RRDYEN, sport->port.membase + UCR1); + temp = readl(sport->port.membase + UCR2); + temp |= UCR2_RXEN; + writel(temp, sport->port.membase + UCR2); spin_unlock_irqrestore(&sport->port.lock, flags); @@ -1824,8 +1742,11 @@ static int imx_rs485_config(struct uart_port *port, /* Make sure Rx is enabled in case Tx is active with Rx disabled */ if (!(rs485conf->flags & SER_RS485_ENABLED) || - rs485conf->flags & SER_RS485_RX_DURING_TX) - imx_start_rx(port); + rs485conf->flags & SER_RS485_RX_DURING_TX) { + temp = readl(sport->port.membase + UCR2); + temp |= UCR2_RXEN; + writel(temp, sport->port.membase + UCR2); + } port->rs485 = *rs485conf; diff --git a/drivers/tty/serial/msm_geni_serial.c b/drivers/tty/serial/msm_geni_serial.c index bff6aaf346ae..e5245d5316dc 100644 --- a/drivers/tty/serial/msm_geni_serial.c +++ b/drivers/tty/serial/msm_geni_serial.c @@ -11,16 +11,14 @@ * GNU General Public License for more details. */ -#if defined(CONFIG_SERIAL_MSM_GENI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) -#define SUPPORT_SYSRQ -#endif - #include #include #include #include #include #include +#include +#include #include #include #include @@ -35,9 +33,6 @@ #include #include -/* Show PM related logs */ -//#define DEBUG_PM - /* UART specific GENI registers */ #define SE_UART_LOOPBACK_CFG (0x22C) #define SE_UART_TX_TRANS_CFG (0x25C) @@ -122,15 +117,46 @@ #define UART_CORE2X_VOTE (5000) #define UART_CONSOLE_CORE2X_VOTE (960) -#define WAKEBYTE_TIMEOUT_MSEC (100) +#define WAKEBYTE_TIMEOUT_MSEC (2000) #define WAIT_XFER_MAX_ITER (2) #define WAIT_XFER_MAX_TIMEOUT_US (10000) #define WAIT_XFER_MIN_TIMEOUT_US (9000) +#define IPC_LOG_PWR_PAGES (6) +#define IPC_LOG_MISC_PAGES (10) +#define IPC_LOG_TX_RX_PAGES (10) #define DATA_BYTES_PER_LINE (32) +#define M_IRQ_BITS (M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN |\ + M_CMD_CANCEL_EN | M_CMD_ABORT_EN) +#define S_IRQ_BITS (S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN |\ + S_CMD_CANCEL_EN | S_CMD_ABORT_EN) +#define DMA_TX_IRQ_BITS (TX_RESET_DONE | TX_DMA_DONE) +#define DMA_RX_IRQ_BITS (RX_EOT | RX_RESET_DONE | UART_DMA_RX_ERRS |\ + UART_DMA_RX_PARITY_ERR | UART_DMA_RX_BREAK |\ + RX_DMA_DONE) + +/* Required for polling for 100 msecs */ +#define POLL_WAIT_TIMEOUT_MSEC 100 + +/* + * Number of iterrations required while polling + * where each iterration has a delay of 100 usecs + */ +#define POLL_ITERATIONS 1000 + +#define IPC_LOG_MSG(ctx, x...) do { \ + if (ctx) \ + ipc_log_string(ctx, x); \ +} while (0) + #define DMA_RX_BUF_SIZE (2048) #define UART_CONSOLE_RX_WM (2) +struct msm_geni_serial_ssr { + struct mutex ssr_lock; + bool is_ssr_down; +}; + struct msm_geni_serial_ver_info { int hw_major_ver; int hw_minor_ver; @@ -139,13 +165,9 @@ struct msm_geni_serial_ver_info { int s_fw_ver; }; -#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) -#define SERIAL_CONSOLE -#endif - struct msm_geni_serial_port { struct uart_port uport; - char name[20]; + const char *name; unsigned int tx_fifo_depth; unsigned int tx_fifo_width; unsigned int rx_fifo_depth; @@ -156,8 +178,6 @@ struct msm_geni_serial_port { struct dentry *dbg; bool port_setup; unsigned int *rx_fifo; - unsigned char *rx_poll_next; - unsigned int rx_poll_unread; int (*handle_rx)(struct uart_port *uport, unsigned int rx_fifo_wc, unsigned int rx_last_byte_valid, @@ -173,6 +193,10 @@ struct msm_geni_serial_port { int wakeup_irq; unsigned char wakeup_byte; struct wakeup_source geni_wake; + void *ipc_log_tx; + void *ipc_log_rx; + void *ipc_log_pwr; + void *ipc_log_misc; void *console_log; unsigned int cur_baud; int ioctl_count; @@ -181,14 +205,20 @@ struct msm_geni_serial_port { struct msm_geni_serial_ver_info ver_info; u32 cur_tx_remaining; bool startup_in_progress; - struct mutex ioctl_mutex; - bool brk; + bool pm_auto_suspend_disable; + struct msm_geni_serial_ssr uart_ssr; + bool is_console; + bool rumi_platform; + bool m_cmd_done; + bool s_cmd_done; + bool m_cmd; + bool s_cmd; + struct completion m_cmd_timeout; + struct completion s_cmd_timeout; }; static const struct uart_ops msm_geni_serial_pops; -#ifdef SERIAL_CONSOLE static struct uart_driver msm_geni_console_driver; -#endif static struct uart_driver msm_geni_serial_hs_driver; static int handle_rx_console(struct uart_port *uport, unsigned int rx_fifo_wc, @@ -204,7 +234,7 @@ static unsigned int msm_geni_serial_tx_empty(struct uart_port *port); static int msm_geni_serial_power_on(struct uart_port *uport); static void msm_geni_serial_power_off(struct uart_port *uport); static int msm_geni_serial_poll_bit(struct uart_port *uport, - int offset, u32 bit_field, bool set); + int offset, int bit_field, bool set); static void msm_geni_serial_stop_rx(struct uart_port *uport); static int msm_geni_serial_runtime_resume(struct device *dev); static int msm_geni_serial_runtime_suspend(struct device *dev); @@ -212,14 +242,178 @@ static int uart_line_id; static int msm_geni_serial_get_ver_info(struct uart_port *uport); static void msm_geni_serial_set_manual_flow(bool enable, struct msm_geni_serial_port *port); +static void msm_geni_serial_ssr_down(struct device *dev); +static void msm_geni_serial_ssr_up(struct device *dev); #define GET_DEV_PORT(uport) \ container_of(uport, struct msm_geni_serial_port, uport) -#ifdef SERIAL_CONSOLE static struct msm_geni_serial_port msm_geni_console_port; -#endif static struct msm_geni_serial_port msm_geni_serial_ports[GENI_UART_NR_PORTS]; +static void msm_geni_serial_handle_isr(struct uart_port *uport, + unsigned long *flags, bool is_irq_masked); + +/* + * The below API is required to check if uport->lock (spinlock) + * is taken by the serial layer or not. If the lock is not taken + * then we can rely on the isr to be fired and if the lock is taken + * by the serial layer then we need to poll for the interrupts. + * + * Returns true(1) if spinlock is already taken by framework (serial layer) + * Return false(0) if spinlock is not taken by framework. + */ +static int msm_geni_serial_spinlocked(struct uart_port *uport) +{ + unsigned long flags; + bool locked; + + locked = spin_trylock_irqsave(&uport->lock, flags); + if (locked) + spin_unlock_irqrestore(&uport->lock, flags); + + return !locked; +} + +/* + * We are enabling the interrupts once the polling operations + * is completed. + */ +static void msm_geni_serial_enable_interrupts(struct uart_port *uport) +{ + unsigned int geni_m_irq_en, geni_s_irq_en; + unsigned int dma_m_irq_en, dma_s_irq_en; + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + + geni_m_irq_en = geni_read_reg_nolog(uport->membase, + SE_GENI_M_IRQ_EN); + geni_s_irq_en = geni_read_reg_nolog(uport->membase, + SE_GENI_S_IRQ_EN); + if (port->xfer_mode == SE_DMA) { + dma_m_irq_en = geni_read_reg_nolog(uport->membase, + SE_DMA_TX_IRQ_EN); + dma_s_irq_en = geni_read_reg_nolog(uport->membase, + SE_DMA_RX_IRQ_EN); + } + + geni_m_irq_en |= M_IRQ_BITS; + geni_s_irq_en |= S_IRQ_BITS; + if (port->xfer_mode == SE_DMA) { + if (((port->ver_info.hw_major_ver <= 1) && + (port->ver_info.hw_minor_ver <= 2))) + dma_m_irq_en |= DMA_TX_IRQ_BITS; + else + dma_m_irq_en |= DMA_TX_IRQ_BITS | TX_GENI_CANCEL_IRQ; + dma_s_irq_en |= (DMA_RX_IRQ_BITS | + RX_GENI_CANCEL_IRQ(port->ver_info)); + } + + geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN); + geni_write_reg_nolog(geni_s_irq_en, uport->membase, SE_GENI_S_IRQ_EN); + if (port->xfer_mode == SE_DMA) { + geni_write_reg_nolog(dma_m_irq_en, uport->membase, + SE_DMA_TX_IRQ_EN); + geni_write_reg_nolog(dma_s_irq_en, uport->membase, + SE_DMA_RX_IRQ_EN); + } +} + +/* Try Disabling the interrupts in order to do polling in an atomic contexts. */ +static bool msm_serial_try_disable_interrupts(struct uart_port *uport) +{ + unsigned int geni_m_irq_en, geni_s_irq_en; + unsigned int dma_m_irq_en, dma_s_irq_en; + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + + /* + * We don't need to disable interrupts if spinlock is not taken + * by framework as we can rely on ISR. + */ + if (!msm_geni_serial_spinlocked(uport)) + return false; + + geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN); + geni_s_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_S_IRQ_EN); + if (port->xfer_mode == SE_DMA) { + dma_m_irq_en = geni_read_reg_nolog(uport->membase, + SE_DMA_TX_IRQ_EN); + dma_s_irq_en = geni_read_reg_nolog(uport->membase, + SE_DMA_RX_IRQ_EN); + } + + geni_m_irq_en &= ~M_IRQ_BITS; + geni_s_irq_en &= ~S_IRQ_BITS; + if (port->xfer_mode == SE_DMA) { + if (((port->ver_info.hw_major_ver <= 1) && + (port->ver_info.hw_minor_ver <= 2))) + dma_m_irq_en &= ~DMA_TX_IRQ_BITS; + else + dma_m_irq_en &= ~(DMA_TX_IRQ_BITS | + TX_GENI_CANCEL_IRQ); + dma_s_irq_en &= DMA_RX_IRQ_BITS | + RX_GENI_CANCEL_IRQ(port->ver_info); + } + + geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN); + geni_write_reg_nolog(geni_s_irq_en, uport->membase, SE_GENI_S_IRQ_EN); + if (port->xfer_mode == SE_DMA) { + geni_write_reg_nolog(dma_m_irq_en, uport->membase, + SE_DMA_TX_IRQ_EN); + geni_write_reg_nolog(dma_s_irq_en, uport->membase, + SE_DMA_RX_IRQ_EN); + } + + return true; +} + +/* + * We need to poll for interrupt if we are in an atomic context + * as serial framework might be taking spinlocks and depend on the isr + * in a non-atomic context. This API decides wheather to poll for + * interrupt or depend on the isr based on in_atomic() call. + */ +static bool geni_wait_for_cmd_done(struct uart_port *uport, bool is_irq_masked) +{ + struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); + unsigned long timeout = POLL_ITERATIONS; + unsigned long flags = 0; + + /* + * We need to do polling if spinlock is taken + * by framework as we cannot rely on ISR. + */ + if (is_irq_masked) { + /* + * Polling is done for 1000 iterrations with + * 10 usecs interval which in total accumulates + * to 10 msecs + */ + if (msm_port->m_cmd) { + while (!msm_port->m_cmd_done && timeout > 0) { + msm_geni_serial_handle_isr(uport, &flags, true); + timeout--; + udelay(100); + } + } else if (msm_port->s_cmd) { + while (!msm_port->s_cmd_done && timeout > 0) { + msm_geni_serial_handle_isr(uport, &flags, true); + timeout--; + udelay(100); + } + } + } else { + /* Waiting for 10 milli second for interrupt to be fired */ + if (msm_port->m_cmd) + timeout = wait_for_completion_timeout + (&msm_port->m_cmd_timeout, + msecs_to_jiffies(POLL_WAIT_TIMEOUT_MSEC)); + else if (msm_port->s_cmd) + timeout = wait_for_completion_timeout + (&msm_port->s_cmd_timeout, + msecs_to_jiffies(POLL_WAIT_TIMEOUT_MSEC)); + } + + return timeout ? 0 : 1; +} static void msm_geni_serial_config_port(struct uart_port *uport, int cfg_flags) { @@ -253,6 +447,22 @@ static ssize_t msm_geni_serial_loopback_store(struct device *dev, static DEVICE_ATTR(loopback, 0644, msm_geni_serial_loopback_show, msm_geni_serial_loopback_store); +static void dump_ipc(void *ipc_ctx, char *prefix, char *string, + u64 addr, int size) + +{ + char buf[DATA_BYTES_PER_LINE * 2]; + int len = 0; + + if (!ipc_ctx) + return; + len = min(size, DATA_BYTES_PER_LINE); + hex_dump_to_buffer(string, len, DATA_BYTES_PER_LINE, 1, buf, + sizeof(buf), false); + ipc_log_string(ipc_ctx, "%s[0x%.10x:%d] : %s", prefix, + (unsigned int)addr, size, buf); +} + static bool device_pending_suspend(struct uart_port *uport) { int usage_count = atomic_read(&uport->dev->power.usage_count); @@ -297,8 +507,14 @@ static bool check_transfers_inflight(struct uart_port *uport) static void wait_for_transfers_inflight(struct uart_port *uport) { int iter = 0; + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); unsigned int geni_status; - bool ret = false; + + if (port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return; + } geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); /* Possible stop rx is called before this. */ @@ -306,7 +522,7 @@ static void wait_for_transfers_inflight(struct uart_port *uport) return; while (iter < WAIT_XFER_MAX_ITER) { - if ((ret = check_transfers_inflight(uport))) { + if (check_transfers_inflight(uport)) { usleep_range(WAIT_XFER_MIN_TIMEOUT_US, WAIT_XFER_MAX_TIMEOUT_US); iter++; @@ -314,14 +530,25 @@ static void wait_for_transfers_inflight(struct uart_port *uport) break; } } + if (check_transfers_inflight(uport)) { + u32 geni_status = geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS); + u32 geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS); + u32 rx_fifo_status = geni_read_reg_nolog(uport->membase, + SE_GENI_RX_FIFO_STATUS); + u32 rx_dma = + geni_read_reg_nolog(uport->membase, SE_DMA_RX_LEN_IN); - if (ret) - dev_err(uport->dev, "%s: timeout!\n", __func__); + IPC_LOG_MSG(port->ipc_log_misc, + "%s IOS 0x%x geni status 0x%x rx: fifo 0x%x dma 0x%x\n", + __func__, geni_ios, geni_status, rx_fifo_status, rx_dma); + } } static int vote_clock_on(struct uart_port *uport) { struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + int usage_count; int ret = 0; ret = msm_geni_serial_power_on(uport); @@ -330,20 +557,18 @@ static int vote_clock_on(struct uart_port *uport) return ret; } port->ioctl_count++; - -#ifdef DEBUG_PM - dev_info(uport->dev, - "pid: %d, comm: %s voted clock to be on, " - "ioctl_count: %d\n", - current->pid, current->comm, port->ioctl_count); -#endif - + usage_count = atomic_read(&uport->dev->power.usage_count); + IPC_LOG_MSG(port->ipc_log_pwr, + "%s :%s ioctl:%d usage_count:%d edge-Count:%d\n", + __func__, current->comm, port->ioctl_count, + usage_count, port->edge_count); return 0; } static int vote_clock_off(struct uart_port *uport) { struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + int usage_count; if (!pm_runtime_enabled(uport->dev)) { dev_err(uport->dev, "RPM not available.Can't enable clocks\n"); @@ -352,24 +577,17 @@ static int vote_clock_off(struct uart_port *uport) if (!port->ioctl_count) { dev_warn(uport->dev, "%s:Imbalanced vote off ioctl %d\n", __func__, port->ioctl_count); + IPC_LOG_MSG(port->ipc_log_pwr, + "%s:Imbalanced vote_off from userspace. %d", + __func__, port->ioctl_count); return -EPERM; } wait_for_transfers_inflight(uport); port->ioctl_count--; - if (port->ioctl_count < 0) { - dev_err(uport->dev, "%s: ioctl_count < 0(%d), fixing it to 0\n", - __func__, port->ioctl_count); - port->ioctl_count = 0; - } - -#ifdef DEBUG_PM - dev_info(uport->dev, - "pid: %d, comm: %s voted clock to be off, " - "ioctl_count: %d\n", - current->pid, current->comm, port->ioctl_count); -#endif msm_geni_serial_power_off(uport); - + usage_count = atomic_read(&uport->dev->power.usage_count); + IPC_LOG_MSG(port->ipc_log_pwr, "%s:%s ioctl:%d usage_count:%d\n", + __func__, current->comm, port->ioctl_count, usage_count); return 0; }; @@ -377,33 +595,38 @@ static int msm_geni_serial_ioctl(struct uart_port *uport, unsigned int cmd, unsigned long arg) { struct msm_geni_serial_port *port = GET_DEV_PORT(uport); - int ret; + int ret = -ENOIOCTLCMD; - mutex_lock(&port->ioctl_mutex); + if (port->pm_auto_suspend_disable) + return ret; switch (cmd) { - case TIOCPMGET: + case TIOCPMGET: { ret = vote_clock_on(uport); break; - case TIOCPMPUT: + } + case TIOCPMPUT: { ret = vote_clock_off(uport); break; - case TIOCPMACT: + } + case TIOCPMACT: { ret = !pm_runtime_status_suspended(uport->dev); break; + } default: - ret = -ENOIOCTLCMD; break; } - - mutex_unlock(&port->ioctl_mutex); - return ret; } static void msm_geni_serial_break_ctl(struct uart_port *uport, int ctl) { + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + if (!uart_console(uport) && device_pending_suspend(uport)) { + IPC_LOG_MSG(port->ipc_log_misc, + "%s.Device is suspended, %s\n", + __func__, current->comm); return; } @@ -426,14 +649,26 @@ static unsigned int msm_geni_serial_get_mctrl(struct uart_port *uport) { u32 geni_ios = 0; unsigned int mctrl = TIOCM_DSR | TIOCM_CAR; + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); - if (!uart_console(uport) && device_pending_suspend(uport)) + if (port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return 0; + } + + if (!uart_console(uport) && device_pending_suspend(uport)) { + IPC_LOG_MSG(port->ipc_log_misc, + "%s.Device is suspended, %s\n", + __func__, current->comm); return TIOCM_DSR | TIOCM_CAR | TIOCM_CTS; + } geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS); if (!(geni_ios & IO2_DATA_IN)) mctrl |= TIOCM_CTS; - + IPC_LOG_MSG(port->ipc_log_misc, "%s: geni_ios:0x%x, mctrl:0x%x\n", + __func__, geni_ios, mctrl); return mctrl; } @@ -448,7 +683,16 @@ static void msm_geni_serial_set_mctrl(struct uart_port *uport, u32 uart_manual_rfr = 0; struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + if (port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return; + } + if (device_pending_suspend(uport)) { + IPC_LOG_MSG(port->ipc_log_misc, + "%s.Device is suspended, %s: mctrl=0x%x\n", + __func__, current->comm, mctrl); return; } if (!(mctrl & TIOCM_RTS)) { @@ -461,6 +705,10 @@ static void msm_geni_serial_set_mctrl(struct uart_port *uport, SE_UART_MANUAL_RFR); /* Write to flow control must complete before return to client*/ mb(); + IPC_LOG_MSG(port->ipc_log_misc, + "%s:%s, mctrl=0x%x, manual_rfr=0x%x, flow=%s\n", + __func__, current->comm, mctrl, uart_manual_rfr, + (port->manual_flow ? "OFF" : "ON")); } static const char *msm_geni_serial_get_type(struct uart_port *uport) @@ -468,7 +716,6 @@ static const char *msm_geni_serial_get_type(struct uart_port *uport) return "MSM"; } -#ifdef SERIAL_CONSOLE static struct msm_geni_serial_port *get_port_from_line(int line, bool is_console) { @@ -486,19 +733,19 @@ static struct msm_geni_serial_port *get_port_from_line(int line, return port; } -#else -static struct msm_geni_serial_port *get_port_from_line(int line) -{ - if ((line < 0) || (line >= GENI_UART_NR_PORTS)) - return ERR_PTR(-ENXIO); - - return &msm_geni_serial_ports[line]; -} -#endif static int msm_geni_serial_power_on(struct uart_port *uport) { int ret = 0; + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + + mutex_lock(&port->uart_ssr.ssr_lock); + if (port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + mutex_unlock(&port->uart_ssr.ssr_lock); + return -EINVAL; + } if (!pm_runtime_enabled(uport->dev)) { if (pm_runtime_status_suspended(uport->dev)) { @@ -506,12 +753,17 @@ static int msm_geni_serial_power_on(struct uart_port *uport) struct tty_port *tport = &state->port; int lock = mutex_trylock(&tport->mutex); + IPC_LOG_MSG(port->ipc_log_pwr, + "%s:Manual resume\n", __func__); pm_runtime_disable(uport->dev); ret = msm_geni_serial_runtime_resume(uport->dev); - if (!ret) { + if (ret) { + IPC_LOG_MSG(port->ipc_log_pwr, + "%s:Manual RPM CB failed %d\n", + __func__, ret); + } else { pm_runtime_get_noresume(uport->dev); pm_runtime_set_active(uport->dev); - enable_irq(uport->irq); } pm_runtime_enable(uport->dev); if (lock) @@ -520,28 +772,37 @@ static int msm_geni_serial_power_on(struct uart_port *uport) } else { ret = pm_runtime_get_sync(uport->dev); if (ret < 0) { + IPC_LOG_MSG(port->ipc_log_pwr, "%s Err\n", __func__); WARN_ON_ONCE(1); pm_runtime_put_noidle(uport->dev); pm_runtime_set_suspended(uport->dev); + mutex_unlock(&port->uart_ssr.ssr_lock); return ret; } } + mutex_unlock(&port->uart_ssr.ssr_lock); return 0; } static void msm_geni_serial_power_off(struct uart_port *uport) { + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); int usage_count = atomic_read(&uport->dev->power.usage_count); - if (!usage_count) + if (!usage_count) { + IPC_LOG_MSG(port->ipc_log_pwr, "%s: Usage Count is already 0\n", + __func__); return; + } - pm_runtime_mark_last_busy(uport->dev); - pm_runtime_put_autosuspend(uport->dev); + if (pm_runtime_enabled(uport->dev)) { + pm_runtime_mark_last_busy(uport->dev); + pm_runtime_put_autosuspend(uport->dev); + } } static int msm_geni_serial_poll_bit(struct uart_port *uport, - int offset, u32 bit_field, bool set) + int offset, int bit_field, bool set) { int iter = 0; unsigned int reg; @@ -551,7 +812,6 @@ static int msm_geni_serial_poll_bit(struct uart_port *uport, unsigned int baud = 115200; unsigned int fifo_bits = DEF_FIFO_DEPTH_WORDS * DEF_FIFO_WIDTH_BITS; unsigned long total_iter = 1000; - u32 set_bits = 0; if (uport->private_data && !uart_console(uport)) { @@ -568,8 +828,7 @@ static int msm_geni_serial_poll_bit(struct uart_port *uport, while (iter < total_iter) { reg = geni_read_reg_nolog(uport->membase, offset); - set_bits = reg & bit_field; - cond = (set_bits == bit_field); + cond = reg & bit_field; if (cond == set) { met = true; break; @@ -595,55 +854,44 @@ static void msm_geni_serial_setup_tx(struct uart_port *uport, mb(); } -static void msm_geni_serial_poll_cancel_tx(struct uart_port *uport) +static void msm_geni_serial_poll_tx_done(struct uart_port *uport) { int done = 0; - unsigned int irq_clear = M_CMD_DONE_EN; + unsigned int irq_clear = 0; done = msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, M_CMD_DONE_EN, true); if (!done) { - geni_write_reg_nolog(M_GENI_CMD_ABORT, uport->membase, - SE_GENI_M_CMD_CTRL_REG); - irq_clear |= M_CMD_ABORT_EN; - msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_ABORT_EN, true); + /* + * Failure IPC logs are not added as this API is + * used by early console and it doesn't have log handle. + */ + geni_write_reg(M_GENI_CMD_CANCEL, uport->membase, + SE_GENI_M_CMD_CTRL_REG); + done = msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_CMD_CANCEL_EN, true); + if (!done) { + geni_write_reg_nolog(M_GENI_CMD_ABORT, uport->membase, + SE_GENI_M_CMD_CTRL_REG); + msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_CMD_ABORT_EN, true); + } } + + irq_clear = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_STATUS); geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_M_IRQ_CLEAR); } -static void msm_geni_serial_abort_rx(struct uart_port *uport) -{ - unsigned int irq_clear = S_CMD_DONE_EN; - struct msm_geni_serial_port *port = GET_DEV_PORT(uport); - - geni_abort_s_cmd(uport->membase); - /* Ensure this goes through before polling. */ - mb(); - irq_clear |= S_CMD_ABORT_EN; - msm_geni_serial_poll_bit(uport, SE_GENI_S_CMD_CTRL_REG, - S_GENI_CMD_ABORT, false); - geni_write_reg_nolog(irq_clear, uport->membase, SE_GENI_S_IRQ_CLEAR); - /* FORCE_DEFAULT makes RFR default high, hence set manually Low */ - msm_geni_serial_set_manual_flow(true, port); - geni_write_reg(FORCE_DEFAULT, uport->membase, GENI_FORCE_DEFAULT_REG); -} - #ifdef CONFIG_CONSOLE_POLL static int msm_geni_serial_get_char(struct uart_port *uport) { + unsigned int rx_fifo; unsigned int m_irq_status; unsigned int s_irq_status; - unsigned int rx_fifo_status; - unsigned int rx_fifo_wc; - unsigned int rx_last; - unsigned int rx_last_byte_valid; - struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); - if (msm_port->rx_poll_unread > 0) { - msm_port->rx_poll_unread--; - return *msm_port->rx_poll_next++; - } + if (!(msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, + M_SEC_IRQ_EN, true))) + return -ENXIO; m_irq_status = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_STATUS); @@ -654,24 +902,18 @@ static int msm_geni_serial_get_char(struct uart_port *uport) geni_write_reg_nolog(s_irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR); - rx_fifo_status = geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFO_STATUS); - rx_fifo_wc = rx_fifo_status & RX_FIFO_WC_MSK; - if (rx_fifo_wc == 0) - return NO_POLL_CHAR; + if (!(msm_geni_serial_poll_bit(uport, SE_GENI_RX_FIFO_STATUS, + RX_FIFO_WC_MSK, true))) + return -ENXIO; - *msm_port->rx_fifo = geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn); - msm_port->rx_poll_next = (unsigned char *)msm_port->rx_fifo; - - rx_last = rx_fifo_status & RX_LAST; - rx_last_byte_valid = ((rx_fifo_status & RX_LAST_BYTE_VALID_MSK) >> - RX_LAST_BYTE_VALID_SHFT); - if (rx_fifo_wc == 1 && rx_last && rx_last_byte_valid) - msm_port->rx_poll_unread = rx_last_byte_valid; - else - msm_port->rx_poll_unread = 4; - - msm_port->rx_poll_unread--; - return *msm_port->rx_poll_next++; + /* + * Read the Rx FIFO only after clearing the interrupt registers and + * getting valid RX fifo status. + */ + mb(); + rx_fifo = geni_read_reg_nolog(uport->membase, SE_GENI_RX_FIFOn); + rx_fifo &= 0xFF; + return rx_fifo; } static void msm_geni_serial_poll_put_char(struct uart_port *uport, @@ -693,11 +935,13 @@ static void msm_geni_serial_poll_put_char(struct uart_port *uport, * Ensure FIFO write goes through before polling for status but. */ mb(); - msm_geni_serial_poll_cancel_tx(uport); + msm_serial_try_disable_interrupts(uport); + msm_geni_serial_poll_tx_done(uport); + msm_geni_serial_enable_interrupts(uport); } #endif -#ifdef SERIAL_CONSOLE +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) static void msm_geni_serial_wr_char(struct uart_port *uport, int ch) { geni_write_reg_nolog(ch, uport->membase, SE_GENI_TX_FIFOn); @@ -753,7 +997,9 @@ __msm_geni_serial_console_write(struct uart_port *uport, const char *s, mb(); i += chars_to_write; } - msm_geni_serial_poll_cancel_tx(uport); + msm_serial_try_disable_interrupts(uport); + msm_geni_serial_poll_tx_done(uport); + msm_geni_serial_enable_interrupts(uport); } static void msm_geni_serial_console_write(struct console *co, const char *s, @@ -764,6 +1010,8 @@ static void msm_geni_serial_console_write(struct console *co, const char *s, bool locked = true; unsigned long flags; unsigned int geni_status; + bool timeout; + bool is_irq_masked; int irq_en; WARN_ON(co->index < 0 || co->index >= GENI_UART_NR_PORTS); @@ -773,11 +1021,6 @@ static void msm_geni_serial_console_write(struct console *co, const char *s, return; uport = &port->uport; -#ifdef SUPPORT_SYSRQ - if (uport->sysrq) { - locked = spin_trylock_irqsave(&uport->lock, flags); - } else -#endif if (oops_in_progress) locked = spin_trylock_irqsave(&uport->lock, flags); else @@ -786,24 +1029,45 @@ static void msm_geni_serial_console_write(struct console *co, const char *s, geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS); /* Cancel the current write to log the fault */ - if (!locked) { + if ((geni_status & M_GENI_CMD_ACTIVE) && !locked) { + port->m_cmd_done = false; + port->m_cmd = true; + reinit_completion(&port->m_cmd_timeout); + is_irq_masked = msm_serial_try_disable_interrupts(uport); geni_cancel_m_cmd(uport->membase); - if (!msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_CANCEL_EN, true)) { + + /* + * Console should be in polling mode. Hence directly pass true + * as argument for wait_for_cmd_done here to handle cancel tx + * in polling mode. + */ + timeout = geni_wait_for_cmd_done(uport, true); + if (timeout) { + IPC_LOG_MSG(port->console_log, + "%s: tx_cancel failed 0x%x\n", + __func__, geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS)); + + reinit_completion(&port->m_cmd_timeout); geni_abort_m_cmd(uport->membase); - msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_ABORT_EN, true); - geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase, - SE_GENI_M_IRQ_CLEAR); + timeout = geni_wait_for_cmd_done(uport, true); + if (timeout) + IPC_LOG_MSG(port->console_log, + "%s: tx abort failed 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS)); } - writel_relaxed(M_CMD_CANCEL_EN, uport->membase + - SE_GENI_M_IRQ_CLEAR); + + msm_geni_serial_enable_interrupts(uport); + port->m_cmd = false; } else if ((geni_status & M_GENI_CMD_ACTIVE) && !port->cur_tx_remaining) { /* It seems we can interrupt existing transfers unless all data * has been sent, in which case we need to look for done first. */ - msm_geni_serial_poll_cancel_tx(uport); + msm_serial_try_disable_interrupts(uport); + msm_geni_serial_poll_tx_done(uport); + msm_geni_serial_enable_interrupts(uport); /* Enable WATERMARK interrupt for every new console write op */ if (uart_circ_chars_pending(&uport->state->xmit)) { @@ -814,11 +1078,11 @@ static void msm_geni_serial_console_write(struct console *co, const char *s, } } + __msm_geni_serial_console_write(uport, s, count); + if (port->cur_tx_remaining) msm_geni_serial_setup_tx(uport, port->cur_tx_remaining); - __msm_geni_serial_console_write(uport, s, count); - if (locked) spin_unlock_irqrestore(&uport->lock, flags); } @@ -853,24 +1117,11 @@ static int handle_rx_console(struct uart_port *uport, int sysrq; uport->icount.rx++; - if (msm_port->brk && rx_char[c] == 0) { - flag = TTY_BREAK; - msm_port->brk = false; - if (uart_handle_break(uport)) - continue; - } - sysrq = uart_handle_sysrq_char(uport, rx_char[c]); if (!sysrq) tty_insert_flip_char(tport, rx_char[c], flag); } } - if (!drop_rx) { - spin_unlock(&uport->lock); - tty_flip_buffer_push(tport); - spin_lock(&uport->lock); - } - return 0; } #else @@ -890,8 +1141,16 @@ static int msm_geni_serial_prep_dma_tx(struct uart_port *uport) struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); struct circ_buf *xmit = &uport->state->xmit; unsigned int xmit_size; + unsigned int dma_dbg; + bool timeout, is_irq_masked; int ret = 0; + if (msm_port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return -EINVAL; + } + xmit_size = uart_circ_chars_pending(xmit); if (xmit_size < WAKEUP_CHARS) uart_write_wakeup(uport); @@ -902,27 +1161,83 @@ static int msm_geni_serial_prep_dma_tx(struct uart_port *uport) if (!xmit_size) return ret; + dump_ipc(msm_port->ipc_log_tx, "DMA Tx", + (char *)&xmit->buf[xmit->tail], 0, xmit_size); msm_geni_serial_setup_tx(uport, xmit_size); ret = geni_se_tx_dma_prep(msm_port->wrapper_dev, uport->membase, &xmit->buf[xmit->tail], xmit_size, &msm_port->tx_dma); if (!ret) { msm_port->xmit_size = xmit_size; } else { - geni_write_reg_nolog(0, uport->membase, - SE_UART_TX_TRANS_LEN); + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: TX DMA map Fail %d\n", __func__, ret); + + geni_write_reg_nolog(0, uport->membase, SE_UART_TX_TRANS_LEN); + msm_port->m_cmd_done = false; + msm_port->m_cmd = true; + reinit_completion(&msm_port->m_cmd_timeout); + + /* + * Try disabling the interrupts before giving the + * cancel command as this might be in an atomic context. + */ + is_irq_masked = msm_serial_try_disable_interrupts(uport); geni_cancel_m_cmd(uport->membase); - if (!msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_CANCEL_EN, true)) { + + timeout = geni_wait_for_cmd_done(uport, is_irq_masked); + if (timeout) { + IPC_LOG_MSG(msm_port->console_log, + "%s: tx_cancel fail 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: tx_cancel failed 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + + msm_port->m_cmd_done = false; + reinit_completion(&msm_port->m_cmd_timeout); + /* Give abort command as cancel command failed */ geni_abort_m_cmd(uport->membase); - msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_ABORT_EN, true); - geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase, - SE_GENI_M_IRQ_CLEAR); + + timeout = geni_wait_for_cmd_done(uport, is_irq_masked); + if (timeout) { + IPC_LOG_MSG(msm_port->console_log, + "%s: tx abort failed 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS)); + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: tx abort failed 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS)); + } + } + + if (msm_port->xfer_mode == SE_DMA) { + dma_dbg = geni_read_reg(uport->membase, + SE_DMA_DEBUG_REG0); + if (dma_dbg & DMA_TX_ACTIVE) { + msm_port->m_cmd_done = false; + reinit_completion(&msm_port->m_cmd_timeout); + geni_write_reg_nolog(1, uport->membase, + SE_DMA_TX_FSM_RST); + + timeout = geni_wait_for_cmd_done(uport, + is_irq_masked); + if (timeout) + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: tx fsm reset failed\n", __func__); + } + + if (msm_port->tx_dma) { + geni_se_tx_dma_unprep(msm_port->wrapper_dev, + msm_port->tx_dma, msm_port->xmit_size); + msm_port->tx_dma = (dma_addr_t)NULL; + } } - geni_write_reg_nolog(M_CMD_CANCEL_EN, uport->membase, - SE_GENI_M_IRQ_CLEAR); - msm_port->tx_dma = (dma_addr_t)NULL; msm_port->xmit_size = 0; + /* Enable the interrupts once the cancel operation is done. */ + msm_geni_serial_enable_interrupts(uport); + msm_port->m_cmd = false; } return ret; } @@ -932,13 +1247,25 @@ static void msm_geni_serial_start_tx(struct uart_port *uport) unsigned int geni_m_irq_en; struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); unsigned int geni_status; + unsigned int geni_ios; + static unsigned int ios_log_limit; + + if (msm_port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return; + } if (!uart_console(uport) && !pm_runtime_active(uport->dev)) { + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.Putting in async RPM vote\n", __func__); pm_runtime_get(uport->dev); goto exit_start_tx; } - if (!uart_console(uport)) { + if (!uart_console(uport) && pm_runtime_enabled(uport->dev)) { + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.Power on.\n", __func__); pm_runtime_get(uport->dev); } @@ -969,69 +1296,96 @@ static void msm_geni_serial_start_tx(struct uart_port *uport) } return; check_flow_ctrl: + geni_ios = geni_read_reg_nolog(uport->membase, SE_GENI_IOS); + if (++ios_log_limit % 5 == 0) { + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: ios: 0x%08x\n", + __func__, geni_ios); + ios_log_limit = 0; + } exit_start_tx: if (!uart_console(uport)) msm_geni_serial_power_off(uport); } -static void msm_geni_serial_tx_fsm_rst(struct uart_port *uport) -{ - unsigned int tx_irq_en; - int done = 0; - int tries = 0; - - tx_irq_en = geni_read_reg_nolog(uport->membase, SE_DMA_TX_IRQ_EN); - geni_write_reg_nolog(0, uport->membase, SE_DMA_TX_IRQ_EN_SET); - geni_write_reg_nolog(1, uport->membase, SE_DMA_TX_FSM_RST); - do { - done = msm_geni_serial_poll_bit(uport, SE_DMA_TX_IRQ_STAT, - TX_RESET_DONE, true); - tries++; - } while (!done && tries < 5); - geni_write_reg_nolog(TX_DMA_DONE | TX_RESET_DONE, uport->membase, - SE_DMA_TX_IRQ_CLR); - geni_write_reg_nolog(tx_irq_en, uport->membase, SE_DMA_TX_IRQ_EN_SET); -} - static void stop_tx_sequencer(struct uart_port *uport) { - unsigned int geni_m_irq_en; unsigned int geni_status; + bool timeout, is_irq_masked; + unsigned int dma_dbg; struct msm_geni_serial_port *port = GET_DEV_PORT(uport); - geni_m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN); - geni_m_irq_en &= ~M_CMD_DONE_EN; - if (port->xfer_mode == FIFO_MODE) { - geni_m_irq_en &= ~M_TX_FIFO_WATERMARK_EN; - geni_write_reg_nolog(0, uport->membase, - SE_GENI_TX_WATERMARK_REG); - } else if (port->xfer_mode == SE_DMA) { - if (port->tx_dma) { - msm_geni_serial_tx_fsm_rst(uport); - geni_se_tx_dma_unprep(port->wrapper_dev, port->tx_dma, - port->xmit_size); - port->tx_dma = (dma_addr_t)NULL; - } + if (port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return; } - port->xmit_size = 0; - geni_write_reg_nolog(geni_m_irq_en, uport->membase, SE_GENI_M_IRQ_EN); - geni_status = geni_read_reg_nolog(uport->membase, - SE_GENI_STATUS); + + geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); /* Possible stop tx is called multiple times. */ if (!(geni_status & M_GENI_CMD_ACTIVE)) return; + IPC_LOG_MSG(port->ipc_log_misc, + "%s: Start GENI: 0x%x\n", __func__, geni_status); + + port->m_cmd_done = false; + port->m_cmd = true; + reinit_completion(&port->m_cmd_timeout); + /* + * Try to mask the interrupts before giving the + * cancel command as this might be in an atomic context + * from framework driver. + */ + is_irq_masked = msm_serial_try_disable_interrupts(uport); geni_cancel_m_cmd(uport->membase); - if (!msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_CANCEL_EN, true)) { + + timeout = geni_wait_for_cmd_done(uport, is_irq_masked); + if (timeout) { + IPC_LOG_MSG(port->console_log, "%s: tx_cancel failed 0x%x\n", + __func__, geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + IPC_LOG_MSG(port->ipc_log_misc, "%s: tx_cancel failed 0x%x\n", + __func__, geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + + port->m_cmd_done = false; + reinit_completion(&port->m_cmd_timeout); geni_abort_m_cmd(uport->membase); - msm_geni_serial_poll_bit(uport, SE_GENI_M_IRQ_STATUS, - M_CMD_ABORT_EN, true); - geni_write_reg_nolog(M_CMD_ABORT_EN, uport->membase, - SE_GENI_M_IRQ_CLEAR); + + timeout = geni_wait_for_cmd_done(uport, is_irq_masked); + if (timeout) { + IPC_LOG_MSG(port->console_log, + "%s: tx abort failed 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + IPC_LOG_MSG(port->ipc_log_misc, + "%s: tx abort failed 0x%x\n", __func__, + geni_read_reg_nolog(uport->membase, SE_GENI_STATUS)); + } } - geni_write_reg_nolog(M_CMD_CANCEL_EN, uport->membase, - SE_GENI_M_IRQ_CLEAR); + + if (port->xfer_mode == SE_DMA) { + dma_dbg = geni_read_reg(uport->membase, SE_DMA_DEBUG_REG0); + if (dma_dbg & DMA_TX_ACTIVE) { + port->m_cmd_done = false; + reinit_completion(&port->m_cmd_timeout); + geni_write_reg_nolog(1, uport->membase, + SE_DMA_TX_FSM_RST); + + timeout = geni_wait_for_cmd_done(uport, is_irq_masked); + if (timeout) + IPC_LOG_MSG(port->ipc_log_misc, + "%s: tx fsm reset failed\n", __func__); + } + + if (port->tx_dma) { + geni_se_tx_dma_unprep(port->wrapper_dev, + port->tx_dma, port->xmit_size); + port->tx_dma = (dma_addr_t)NULL; + } + } + /* unmask the interrupts once the cancel operation is done. */ + msm_geni_serial_enable_interrupts(uport); + port->m_cmd = false; + port->xmit_size = 0; + /* * If we end up having to cancel an on-going Tx for non-console usecase * then it means there was some unsent data in the Tx FIFO, consequently @@ -1040,24 +1394,29 @@ static void stop_tx_sequencer(struct uart_port *uport) * this out, remove the vote put in during start_tx(). */ if (!uart_console(uport)) { + IPC_LOG_MSG(port->ipc_log_misc, "%s:Removing vote\n", __func__); msm_geni_serial_power_off(uport); } + geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); + IPC_LOG_MSG(port->ipc_log_misc, "%s: End GENI:0x%x\n", + __func__, geni_status); } static void msm_geni_serial_stop_tx(struct uart_port *uport) { + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + if (!uart_console(uport) && device_pending_suspend(uport)) { dev_err(uport->dev, "%s.Device is suspended.\n", __func__); + IPC_LOG_MSG(port->ipc_log_misc, + "%s.Device is suspended.\n", __func__); return; } - stop_tx_sequencer(uport); } static void start_rx_sequencer(struct uart_port *uport) { - unsigned int geni_s_irq_en; - unsigned int geni_m_irq_en; unsigned int geni_status; struct msm_geni_serial_port *port = GET_DEV_PORT(uport); u32 geni_se_param = UART_PARAM_RFR_OPEN; @@ -1065,9 +1424,20 @@ static void start_rx_sequencer(struct uart_port *uport) if (port->startup_in_progress) return; + if (port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return; + } + geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); + IPC_LOG_MSG(port->ipc_log_misc, "%s: 0x%x\n", + __func__, geni_status); + if (geni_status & S_GENI_CMD_ACTIVE) { if (port->xfer_mode == SE_DMA) { + IPC_LOG_MSG(port->ipc_log_misc, + "%s: GENI: 0x%x\n", __func__, geni_status); geni_se_rx_dma_start(uport->membase, DMA_RX_BUF_SIZE, &port->rx_dma); } @@ -1075,30 +1445,18 @@ static void start_rx_sequencer(struct uart_port *uport) } /* Start RX with the RFR_OPEN to keep RFR in always ready state */ + msm_geni_serial_enable_interrupts(uport); geni_setup_s_cmd(uport->membase, UART_START_READ, geni_se_param); - if (port->xfer_mode == FIFO_MODE) { - geni_s_irq_en = geni_read_reg_nolog(uport->membase, - SE_GENI_S_IRQ_EN); - geni_m_irq_en = geni_read_reg_nolog(uport->membase, - SE_GENI_M_IRQ_EN); - - geni_s_irq_en |= S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN; - geni_m_irq_en |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN; - - geni_write_reg_nolog(geni_s_irq_en, uport->membase, - SE_GENI_S_IRQ_EN); - geni_write_reg_nolog(geni_m_irq_en, uport->membase, - SE_GENI_M_IRQ_EN); - } else if (port->xfer_mode == SE_DMA) { + if (port->xfer_mode == SE_DMA) geni_se_rx_dma_start(uport->membase, DMA_RX_BUF_SIZE, &port->rx_dma); - } - /* - * Ensure the writes to the secondary sequencer and interrupt enables - * go through. - */ + + /* Ensure that the above writes go through */ mb(); + geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); + IPC_LOG_MSG(port->ipc_log_misc, "%s: 0x%x, dma_dbg:0x%x\n", __func__, + geni_status, geni_read_reg(uport->membase, SE_DMA_DEBUG_REG0)); } static void msm_geni_serial_start_rx(struct uart_port *uport) @@ -1107,6 +1465,8 @@ static void msm_geni_serial_start_rx(struct uart_port *uport) if (!uart_console(uport) && device_pending_suspend(uport)) { dev_err(uport->dev, "%s.Device is suspended.\n", __func__); + IPC_LOG_MSG(port->ipc_log_misc, + "%s.Device is suspended.\n", __func__); return; } start_rx_sequencer(&port->uport); @@ -1118,6 +1478,13 @@ static void msm_geni_serial_rx_fsm_rst(struct uart_port *uport) unsigned int rx_irq_en; int done = 0; int tries = 0; + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + + if (port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return; + } rx_irq_en = geni_read_reg_nolog(uport->membase, SE_DMA_RX_IRQ_EN); geni_write_reg_nolog(0, uport->membase, SE_DMA_RX_IRQ_EN_SET); @@ -1137,6 +1504,12 @@ static void msm_geni_serial_set_manual_flow(bool enable, { u32 uart_manual_rfr = 0; + if (port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return; + } + if (!enable) { uart_manual_rfr |= (UART_MANUAL_RFR_EN); geni_write_reg_nolog(uart_manual_rfr, port->uport.membase, @@ -1152,104 +1525,121 @@ static void msm_geni_serial_set_manual_flow(bool enable, * doing a stop_rx. */ mb(); + IPC_LOG_MSG(port->ipc_log_misc, + "%s: Manual Flow Enabled, HW Flow OFF\n", __func__); } else { geni_write_reg_nolog(0, port->uport.membase, SE_UART_MANUAL_RFR); /* Ensure that the manual flow off writes go through */ mb(); + IPC_LOG_MSG(port->ipc_log_misc, + "%s: Manual Flow Disabled, HW Flow ON\n", __func__); } } static void stop_rx_sequencer(struct uart_port *uport) { - unsigned int geni_s_irq_en; - unsigned int geni_m_irq_en; unsigned int geni_status; + bool timeout, is_irq_masked; struct msm_geni_serial_port *port = GET_DEV_PORT(uport); - bool done; - u32 geni_s_irq_status; - u32 dma_rx_irq_stat; - u32 DMA_RX_CANCEL_BIT; + unsigned long flags = 0; - if (port->xfer_mode == FIFO_MODE) { - geni_s_irq_en = geni_read_reg_nolog(uport->membase, - SE_GENI_S_IRQ_EN); - geni_m_irq_en = geni_read_reg_nolog(uport->membase, - SE_GENI_M_IRQ_EN); - geni_s_irq_en &= ~(S_RX_FIFO_WATERMARK_EN | S_RX_FIFO_LAST_EN); - geni_m_irq_en &= ~(M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN); - - geni_write_reg_nolog(geni_s_irq_en, uport->membase, - SE_GENI_S_IRQ_EN); - geni_write_reg_nolog(geni_m_irq_en, uport->membase, - SE_GENI_M_IRQ_EN); + IPC_LOG_MSG(port->ipc_log_misc, "%s\n", __func__); + if (port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return; } + geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); /* Possible stop rx is called multiple times. */ - if (!(geni_status & S_GENI_CMD_ACTIVE)) + if (!(geni_status & S_GENI_CMD_ACTIVE)) { + IPC_LOG_MSG(port->ipc_log_misc, + "%s: RX is Inactive, geni_sts: 0x%x\n", + __func__, geni_status); goto exit_rx_seq; - - /* - * Clear previous unhandled interrupts, because in VI experiment it's - * observed that previous interrupts bits can cause problem to - * successive commands given to sequencers. - * To do: Handle RX EOT bit properly by restarting the DMA engine. - */ - geni_s_irq_status = geni_read_reg_nolog(uport->membase, - SE_GENI_S_IRQ_STATUS); - geni_write_reg_nolog(geni_s_irq_status, uport->membase, - SE_GENI_S_IRQ_CLEAR); - if (port->xfer_mode == SE_DMA) { - dma_rx_irq_stat = geni_read_reg_nolog(uport->membase, - SE_DMA_RX_IRQ_STAT); - geni_write_reg_nolog(dma_rx_irq_stat, - uport->membase, SE_DMA_RX_IRQ_CLR); } + port->s_cmd_done = false; + port->s_cmd = true; + reinit_completion(&port->s_cmd_timeout); + + IPC_LOG_MSG(port->ipc_log_misc, "%s: Start 0x%x\n", + __func__, geni_status); + /* + * Try disabling the interrupts before giving the + * cancel command as this might be in an atomic context. + */ + is_irq_masked = msm_serial_try_disable_interrupts(uport); geni_cancel_s_cmd(uport->membase); + /* * Ensure that the cancel goes through before polling for the * cancel control bit. */ mb(); - if (port->xfer_mode == SE_DMA) { - /* - * QUPS which has HW version <= 1.2 11th bit of - * SE_DMA_RX_IRQ_STAT register denotes DMA_RX_CANCEL_BIT. - */ - DMA_RX_CANCEL_BIT = ((port->ver_info.hw_major_ver <= 1) - && (port->ver_info.hw_minor_ver <= 2)) ? - BIT(11) : BIT(14); + timeout = geni_wait_for_cmd_done(uport, is_irq_masked); + if (timeout) { + bool is_rx_active; - done = msm_geni_serial_poll_bit(uport, SE_DMA_RX_IRQ_STAT, - DMA_RX_CANCEL_BIT | RX_EOT | RX_DMA_DONE, true); - if (done) { - dma_rx_irq_stat = geni_read_reg_nolog(uport->membase, - SE_DMA_RX_IRQ_STAT); - geni_write_reg_nolog(dma_rx_irq_stat, - uport->membase, SE_DMA_RX_IRQ_CLR); - geni_s_irq_status = geni_read_reg_nolog(uport->membase, - SE_GENI_S_IRQ_STATUS); - geni_write_reg_nolog(geni_s_irq_status, uport->membase, - SE_GENI_S_IRQ_CLEAR); + geni_status = geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS); + /* + * Possible that stop_rx is called from system resume context + * for console usecase. In early resume, irq remains disabled + * in the system. call msm_geni_serial_isr to clear + * the interrupts. + */ + is_rx_active = geni_status & S_GENI_CMD_ACTIVE; + IPC_LOG_MSG(port->ipc_log_misc, + "%s cancel failed is_rx_active: %d 0x%x\n", + __func__, is_rx_active, geni_status); + IPC_LOG_MSG(port->console_log, + "%s cancel failed is_rx_active:%d 0x%x\n", + __func__, is_rx_active, geni_status); + if (uart_console(uport) && !is_rx_active) { + msm_geni_serial_handle_isr(uport, &flags, true); + goto exit_rx_seq; + } + port->s_cmd_done = false; + reinit_completion(&port->s_cmd_timeout); + geni_abort_s_cmd(uport->membase); + /* Ensure this goes through before polling. */ + mb(); + + timeout = geni_wait_for_cmd_done(uport, is_irq_masked); + if (timeout) { + geni_status = geni_read_reg_nolog(uport->membase, + SE_GENI_STATUS); + IPC_LOG_MSG(port->ipc_log_misc, + "%s abort fail 0x%x\n", __func__, geni_status); + IPC_LOG_MSG(port->console_log, + "%s abort fail 0x%x\n", __func__, geni_status); } } + /* Enable the interrupts once the cancel operation is done. */ + msm_geni_serial_enable_interrupts(uport); + port->s_cmd = false; - geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); - if ((geni_status & S_GENI_CMD_ACTIVE)) { - msm_geni_serial_abort_rx(uport); - } exit_rx_seq: if (port->xfer_mode == SE_DMA && port->rx_dma) msm_geni_serial_rx_fsm_rst(uport); + + geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); + IPC_LOG_MSG(port->ipc_log_misc, "%s: End 0x%x\n", + __func__, geni_status); } static void msm_geni_serial_stop_rx(struct uart_port *uport) { - if (!uart_console(uport) && device_pending_suspend(uport)) - return; + struct msm_geni_serial_port *port = GET_DEV_PORT(uport); + if (!uart_console(uport) && device_pending_suspend(uport)) { + IPC_LOG_MSG(port->ipc_log_misc, + "%s.Device is suspended.\n", __func__); + return; + } stop_rx_sequencer(uport); } @@ -1284,6 +1674,8 @@ static int handle_rx_hs(struct uart_port *uport, } uport->icount.rx += ret; tty_flip_buffer_push(tport); + dump_ipc(msm_port->ipc_log_rx, "Rx", (char *)msm_port->rx_fifo, 0, + rx_bytes); return ret; } @@ -1305,8 +1697,8 @@ static int msm_geni_serial_handle_rx(struct uart_port *uport, bool drop_rx) RX_LAST_BYTE_VALID_SHFT); rx_last = rx_fifo_status & RX_LAST; if (rx_fifo_wc) - port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid, - rx_last, drop_rx); + ret = port->handle_rx(uport, rx_fifo_wc, rx_last_byte_valid, + rx_last, drop_rx); return ret; } @@ -1327,6 +1719,12 @@ static int msm_geni_serial_handle_tx(struct uart_port *uport, bool done, int temp_tail = 0; int irq_en; + if (msm_port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return -EINVAL; + } + tx_fifo_status = geni_read_reg_nolog(uport->membase, SE_GENI_TX_FIFO_STATUS); @@ -1371,6 +1769,13 @@ static int msm_geni_serial_handle_tx(struct uart_port *uport, bool done, unsigned int buf = 0; int c; + if (msm_port->uart_ssr.is_ssr_down) { + ret = -EINVAL; + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.SSR Down event set\n", __func__); + goto exit_ssr; + } + tx_bytes = ((bytes_remaining < fifo_width_bytes) ? bytes_remaining : fifo_width_bytes); @@ -1409,6 +1814,8 @@ exit_handle_tx: if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) uart_write_wakeup(uport); + +exit_ssr: return ret; } @@ -1417,20 +1824,33 @@ static int msm_geni_serial_handle_dma_rx(struct uart_port *uport, bool drop_rx) struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); unsigned int rx_bytes = 0; struct tty_port *tport; - int ret; + int ret = 0; unsigned int geni_status; + if (msm_port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return -EINVAL; + } + geni_status = geni_read_reg_nolog(uport->membase, SE_GENI_STATUS); /* Possible stop rx is called */ - if (!(geni_status & S_GENI_CMD_ACTIVE)) + if (!(geni_status & S_GENI_CMD_ACTIVE)) { + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: GENI: 0x%x\n", __func__, geni_status); return 0; + } if (unlikely(!msm_port->rx_buf)) { + IPC_LOG_MSG(msm_port->ipc_log_rx, "%s: NULL Rx_buf\n", + __func__); return 0; } rx_bytes = geni_read_reg_nolog(uport->membase, SE_DMA_RX_LEN_IN); if (unlikely(!rx_bytes)) { + IPC_LOG_MSG(msm_port->ipc_log_rx, "%s: Size %d\n", + __func__, rx_bytes); goto exit_handle_dma_rx; } if (drop_rx) @@ -1446,10 +1866,14 @@ static int msm_geni_serial_handle_dma_rx(struct uart_port *uport, bool drop_rx) } uport->icount.rx += ret; tty_flip_buffer_push(tport); + dump_ipc(msm_port->ipc_log_rx, "DMA Rx", (char *)msm_port->rx_buf, 0, + rx_bytes); exit_handle_dma_rx: - geni_se_rx_dma_start(uport->membase, DMA_RX_BUF_SIZE, - &msm_port->rx_dma); - + if (msm_port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return -EINVAL; + } return ret; } @@ -1458,6 +1882,12 @@ static int msm_geni_serial_handle_dma_tx(struct uart_port *uport) struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); struct circ_buf *xmit = &uport->state->xmit; + if (msm_port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: SSR Down event set\n", + __func__); + return -EINVAL; + } + xmit->tail = (xmit->tail + msm_port->xmit_size) & (UART_XMIT_SIZE - 1); geni_se_tx_dma_unprep(msm_port->wrapper_dev, msm_port->tx_dma, msm_port->xmit_size); @@ -1473,6 +1903,8 @@ static int msm_geni_serial_handle_dma_tx(struct uart_port *uport) * allowing the device to suspend. */ if (!uart_console(uport)) { + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.Power Off.\n", __func__); msm_geni_serial_power_off(uport); } uart_write_wakeup(uport); @@ -1480,43 +1912,67 @@ static int msm_geni_serial_handle_dma_tx(struct uart_port *uport) return 0; } -static irqreturn_t msm_geni_serial_isr(int isr, void *dev) +static void msm_geni_serial_handle_isr(struct uart_port *uport, + unsigned long *flags, + bool is_irq_masked) { unsigned int m_irq_status; unsigned int s_irq_status; unsigned int dma; unsigned int dma_tx_status; unsigned int dma_rx_status; - struct uart_port *uport = dev; - unsigned long flags; unsigned int m_irq_en; unsigned int geni_status; + struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); struct tty_port *tport = &uport->state->port; bool drop_rx = false; - struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); + bool s_cmd_done = false; + bool m_cmd_done = false; + + if (msm_port->uart_ssr.is_ssr_down) { + m_irq_status = geni_read_reg_nolog(uport->membase, + SE_GENI_M_IRQ_STATUS); + s_irq_status = geni_read_reg_nolog(uport->membase, + SE_GENI_S_IRQ_STATUS); + geni_write_reg_nolog(m_irq_status, uport->membase, + SE_GENI_M_IRQ_CLEAR); + geni_write_reg_nolog(s_irq_status, uport->membase, + SE_GENI_S_IRQ_CLEAR); + dma_tx_status = geni_read_reg_nolog(uport->membase, + SE_DMA_TX_IRQ_STAT); + dma_rx_status = geni_read_reg_nolog(uport->membase, + SE_DMA_RX_IRQ_STAT); + if (dma_tx_status) + geni_write_reg_nolog(dma_tx_status, uport->membase, + SE_DMA_TX_IRQ_CLR); + if (dma_rx_status) + geni_write_reg_nolog(dma_rx_status, uport->membase, + SE_DMA_RX_IRQ_CLR); + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.SSR Down event set\n", __func__); + return; + } - spin_lock_irqsave(&uport->lock, flags); if (uart_console(uport) && uport->suspended) { + IPC_LOG_MSG(msm_port->console_log, + "%s. Console in suspend state\n", __func__); goto exit_geni_serial_isr; } - if (!uart_console(uport) && pm_runtime_status_suspended(uport->dev)) { - dev_err(uport->dev, "%s.Device is suspended.\n", __func__); - goto exit_geni_serial_isr; - } + m_irq_status = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_STATUS); s_irq_status = geni_read_reg_nolog(uport->membase, SE_GENI_S_IRQ_STATUS); if (uart_console(uport)) + IPC_LOG_MSG(msm_port->console_log, + "%s. sirq 0x%x mirq:0x%x\n", __func__, s_irq_status, + m_irq_status); + + geni_write_reg_nolog(m_irq_status, uport->membase, + SE_GENI_M_IRQ_CLEAR); + geni_write_reg_nolog(s_irq_status, uport->membase, + SE_GENI_S_IRQ_CLEAR); m_irq_en = geni_read_reg_nolog(uport->membase, SE_GENI_M_IRQ_EN); - dma = geni_read_reg_nolog(uport->membase, SE_GENI_DMA_MODE_EN); - dma_tx_status = geni_read_reg_nolog(uport->membase, SE_DMA_TX_IRQ_STAT); - dma_rx_status = geni_read_reg_nolog(uport->membase, SE_DMA_RX_IRQ_STAT); - geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS); - - geni_write_reg_nolog(m_irq_status, uport->membase, SE_GENI_M_IRQ_CLEAR); - geni_write_reg_nolog(s_irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR); - if ((m_irq_status & M_ILLEGAL_CMD_EN)) { WARN_ON(1); goto exit_geni_serial_isr; @@ -1525,56 +1981,161 @@ static irqreturn_t msm_geni_serial_isr(int isr, void *dev) if (s_irq_status & S_RX_FIFO_WR_ERR_EN) { uport->icount.overrun++; tty_insert_flip_char(tport, 0, TTY_OVERRUN); + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.sirq 0x%x buf_overrun:%d\n", + __func__, s_irq_status, uport->icount.buf_overrun); } + dma = geni_read_reg_nolog(uport->membase, SE_GENI_DMA_MODE_EN); if (!dma) { + geni_status = readl_relaxed(uport->membase + SE_GENI_STATUS); + if ((m_irq_status & m_irq_en) & (M_TX_FIFO_WATERMARK_EN | M_CMD_DONE_EN)) msm_geni_serial_handle_tx(uport, m_irq_status & M_CMD_DONE_EN, geni_status & M_GENI_CMD_ACTIVE); - if ((s_irq_status & S_GP_IRQ_0_EN) || - (s_irq_status & S_GP_IRQ_1_EN)) { + if (m_irq_status & (M_CMD_CANCEL_EN | M_CMD_ABORT_EN)) + m_cmd_done = true; + + if (s_irq_status & (S_GP_IRQ_0_EN | S_GP_IRQ_1_EN)) { if (s_irq_status & S_GP_IRQ_0_EN) uport->icount.parity++; + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.sirq 0x%x parity:%d\n", + __func__, s_irq_status, uport->icount.parity); drop_rx = true; - } else if ((s_irq_status & S_GP_IRQ_2_EN) || - (s_irq_status & S_GP_IRQ_3_EN)) { + } else if (s_irq_status & (S_GP_IRQ_2_EN | S_GP_IRQ_3_EN)) { uport->icount.brk++; - msm_port->brk = true; + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.sirq 0x%x break:%d\n", + __func__, s_irq_status, uport->icount.brk); + } + /* + * In case of stop_rx handling there is a chance + * for RX data can come in parallel. set drop_rx to + * avoid data push to framework from handle_rx_console() + * API for stop_rx case. + */ + + if (s_irq_status & (S_CMD_CANCEL_EN | S_CMD_ABORT_EN)) { + s_cmd_done = true; + drop_rx = true; } - if ((s_irq_status & S_RX_FIFO_WATERMARK_EN) || - (s_irq_status & S_RX_FIFO_LAST_EN)) + if (s_irq_status & (S_RX_FIFO_WATERMARK_EN | + S_RX_FIFO_LAST_EN)) { msm_geni_serial_handle_rx(uport, drop_rx); + if (!drop_rx && !is_irq_masked) { + spin_unlock_irqrestore(&uport->lock, *flags); + tty_flip_buffer_push(tport); + spin_lock_irqsave(&uport->lock, *flags); + } else if (!drop_rx) { + tty_flip_buffer_push(tport); + } + } } else { + dma_tx_status = geni_read_reg_nolog(uport->membase, + SE_DMA_TX_IRQ_STAT); + dma_rx_status = geni_read_reg_nolog(uport->membase, + SE_DMA_RX_IRQ_STAT); + if (dma_tx_status) { + geni_write_reg_nolog(dma_tx_status, uport->membase, - SE_DMA_TX_IRQ_CLR); - if (dma_tx_status & TX_DMA_DONE) + SE_DMA_TX_IRQ_CLR); + + + if (((msm_port->ver_info.hw_major_ver <= 1) && + (msm_port->ver_info.hw_minor_ver <= 2)) && + (dma_tx_status & TX_RESET_DONE)) { + m_cmd_done = true; + } else if (dma_tx_status & (TX_RESET_DONE | + TX_GENI_CANCEL_IRQ)) { + m_cmd_done = true; + } + if (m_irq_status & (M_CMD_CANCEL_EN | M_CMD_ABORT_EN)) + m_cmd_done = true; + + if ((dma_tx_status & TX_DMA_DONE) && !m_cmd_done) msm_geni_serial_handle_dma_tx(uport); } if (dma_rx_status) { geni_write_reg_nolog(dma_rx_status, uport->membase, - SE_DMA_RX_IRQ_CLR); + SE_DMA_RX_IRQ_CLR); + if (dma_rx_status & RX_RESET_DONE) { + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.Reset done. 0x%x.\n", + __func__, dma_rx_status); goto exit_geni_serial_isr; } + if (dma_rx_status & UART_DMA_RX_ERRS) { if (dma_rx_status & UART_DMA_RX_PARITY_ERR) uport->icount.parity++; + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.Rx Errors. 0x%x parity:%d\n", + __func__, dma_rx_status, + uport->icount.parity); drop_rx = true; } else if (dma_rx_status & UART_DMA_RX_BREAK) { uport->icount.brk++; + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.Rx Errors. 0x%x break:%d\n", + __func__, dma_rx_status, + uport->icount.brk); } - if (dma_rx_status & RX_DMA_DONE) - msm_geni_serial_handle_dma_rx(uport, drop_rx); + + if (dma_rx_status & RX_EOT || + dma_rx_status & RX_DMA_DONE) { + msm_geni_serial_handle_dma_rx(uport, + drop_rx); + if (!(dma_rx_status & + RX_GENI_CANCEL_IRQ(msm_port->ver_info))) { + geni_se_rx_dma_start(uport->membase, + DMA_RX_BUF_SIZE, &msm_port->rx_dma); + } + } + + if (dma_rx_status & RX_SBE) { + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s.Rx Errors. 0x%x\n", + __func__, dma_rx_status); + WARN_ON(1); + } + + if (dma_rx_status & (RX_EOT | + RX_GENI_CANCEL_IRQ(msm_port->ver_info) | + RX_DMA_DONE)) + s_cmd_done = true; + + if (s_irq_status & (S_CMD_CANCEL_EN | S_CMD_ABORT_EN)) + s_cmd_done = true; } } exit_geni_serial_isr: + if (m_cmd_done) { + msm_port->m_cmd_done = true; + complete(&msm_port->m_cmd_timeout); + } + + if (s_cmd_done) { + msm_port->s_cmd_done = true; + complete(&msm_port->s_cmd_timeout); + } +} + +static irqreturn_t msm_geni_serial_isr(int isr, void *dev) +{ + struct uart_port *uport = dev; + unsigned long flags; + + spin_lock_irqsave(&uport->lock, flags); + msm_geni_serial_handle_isr(uport, &flags, false); spin_unlock_irqrestore(&uport->lock, flags); return IRQ_HANDLED; } @@ -1587,16 +2148,16 @@ static irqreturn_t msm_geni_wakeup_isr(int isr, void *dev) unsigned long flags; spin_lock_irqsave(&uport->lock, flags); + IPC_LOG_MSG(port->ipc_log_rx, "%s: Edge-Count %d\n", __func__, + port->edge_count); if (port->wakeup_byte && (port->edge_count == 2)) { tty = uport->state->port.tty; tty_insert_flip_char(tty->port, port->wakeup_byte, TTY_NORMAL); + IPC_LOG_MSG(port->ipc_log_rx, "%s: Inject 0x%x\n", + __func__, port->wakeup_byte); port->edge_count = 0; tty_flip_buffer_push(tty->port); __pm_wakeup_event(&port->geni_wake, WAKEBYTE_TIMEOUT_MSEC); -#ifdef DEBUG_PM - dev_info(uport->dev, "%s: Holding wakelock for %d ms\n", - __func__, WAKEBYTE_TIMEOUT_MSEC); -#endif } else if (port->edge_count < 2) { port->edge_count++; } @@ -1656,31 +2217,45 @@ static void set_rfr_wm(struct msm_geni_serial_port *port) static void msm_geni_serial_shutdown(struct uart_port *uport) { struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); - unsigned long flags; + int ret; - disable_irq(uport->irq); - free_irq(uport->irq, uport); - spin_lock_irqsave(&uport->lock, flags); - msm_geni_serial_stop_tx(uport); - msm_geni_serial_stop_rx(uport); - spin_unlock_irqrestore(&uport->lock, flags); + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s:\n", __func__); + if (uart_console(uport)) { + console_stop(uport->cons); + disable_irq(uport->irq); + } else { + msm_geni_serial_power_on(uport); + wait_for_transfers_inflight(uport); + } if (!uart_console(uport)) { if (msm_port->ioctl_count) { int i; for (i = 0; i < msm_port->ioctl_count; i++) { + IPC_LOG_MSG(msm_port->ipc_log_pwr, + "%s IOCTL vote present. Forcing off\n", + __func__); msm_geni_serial_power_off(uport); } msm_port->ioctl_count = 0; } + if (pm_runtime_enabled(uport->dev)) { + ret = pm_runtime_put_sync_suspend(uport->dev); + if (ret) { + IPC_LOG_MSG(msm_port->ipc_log_pwr, + "%s: Failed to suspend:%d\n", __func__, ret); + } + } + if (msm_port->wakeup_irq > 0) { irq_set_irq_wake(msm_port->wakeup_irq, 0); disable_irq(msm_port->wakeup_irq); free_irq(msm_port->wakeup_irq, uport); } } + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: End\n", __func__); } static int msm_geni_serial_port_setup(struct uart_port *uport) @@ -1690,7 +2265,6 @@ static int msm_geni_serial_port_setup(struct uart_port *uport) unsigned long cfg0, cfg1; dma_addr_t dma_address; unsigned int rxstale = DEFAULT_BITS_PER_CHAR * STALE_TIMEOUT; - struct device *cb_dev = geni_get_iommu_dev(msm_port->wrapper_dev); set_rfr_wm(msm_port); geni_write_reg_nolog(rxstale, uport->membase, SE_UART_RX_STALE_CNT); @@ -1714,8 +2288,11 @@ static int msm_geni_serial_port_setup(struct uart_port *uport) ret = -ENOMEM; goto exit_portsetup; } - msm_port->rx_buf = dma_alloc_coherent(cb_dev, DMA_RX_BUF_SIZE, - &dma_address, GFP_KERNEL); + + msm_port->rx_buf = + geni_se_iommu_alloc_buf(msm_port->wrapper_dev, + &dma_address, DMA_RX_BUF_SIZE); + if (!msm_port->rx_buf) { devm_kfree(uport->dev, msm_port->rx_fifo); msm_port->rx_fifo = NULL; @@ -1729,7 +2306,9 @@ static int msm_geni_serial_port_setup(struct uart_port *uport) * it else we could end up in data loss scenarios. */ msm_port->xfer_mode = FIFO_MODE; - msm_geni_serial_poll_cancel_tx(uport); + msm_serial_try_disable_interrupts(uport); + msm_geni_serial_poll_tx_done(uport); + msm_geni_serial_enable_interrupts(uport); se_get_packing_config(8, 1, false, &cfg0, &cfg1); geni_write_reg_nolog(cfg0, uport->membase, SE_GENI_TX_PACKING_CFG0); @@ -1741,6 +2320,7 @@ static int msm_geni_serial_port_setup(struct uart_port *uport) geni_write_reg_nolog(cfg1, uport->membase, SE_GENI_RX_PACKING_CFG1); } + ret = geni_se_init(uport->membase, msm_port->rx_wm, msm_port->rx_rfr); if (ret) { dev_err(uport->dev, "%s: Fail\n", __func__); @@ -1761,8 +2341,8 @@ static int msm_geni_serial_port_setup(struct uart_port *uport) return 0; free_dma: if (msm_port->rx_dma) { - dma_free_coherent(msm_port->wrapper_dev, DMA_RX_BUF_SIZE, - msm_port->rx_buf, msm_port->rx_dma); + geni_se_iommu_free_buf(msm_port->wrapper_dev, + &msm_port->rx_dma, msm_port->rx_buf, DMA_RX_BUF_SIZE); msm_port->rx_dma = (dma_addr_t)NULL; } exit_portsetup: @@ -1774,8 +2354,7 @@ static int msm_geni_serial_startup(struct uart_port *uport) int ret = 0; struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); - scnprintf(msm_port->name, sizeof(msm_port->name), "msm_serial_geni%d", - uport->line); + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s:\n", __func__); msm_port->startup_in_progress = true; @@ -1790,8 +2369,13 @@ static int msm_geni_serial_startup(struct uart_port *uport) get_tx_fifo_size(msm_port); if (!msm_port->port_setup) { - if (msm_geni_serial_port_setup(uport)) + ret = msm_geni_serial_port_setup(uport); + if (ret) { + IPC_LOG_MSG(msm_port->ipc_log_misc, + "%s: port_setup Fail ret:%d\n", + __func__, ret); goto exit_startup; + } } /* @@ -1799,13 +2383,16 @@ static int msm_geni_serial_startup(struct uart_port *uport) * before returning to the framework. */ mb(); - ret = request_irq(uport->irq, msm_geni_serial_isr, IRQF_TRIGGER_HIGH, - msm_port->name, uport); - if (unlikely(ret)) { - dev_err(uport->dev, "%s: Failed to get IRQ ret %d\n", - __func__, ret); - goto exit_startup; - } + + /* Console usecase requires irq to be in enable state after early + * console switch from probe to handle RX data. Hence enable IRQ + * from startup and disable it from shutdown APIs for console case. + * BT HSUART usecase, IRQ will be enabled from runtime resume() + * and disabled in runtime_suspend to avoid spurious interrupts + * after suspend. + */ + if (uart_console(uport)) + enable_irq(uport->irq); if (msm_port->wakeup_irq > 0) { ret = request_irq(msm_port->wakeup_irq, msm_geni_wakeup_isr, @@ -1828,6 +2415,7 @@ exit_startup: if (likely(!uart_console(uport))) msm_geni_serial_power_off(&msm_port->uport); msm_port->startup_in_progress = false; + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: ret:%d\n", __func__, ret); return ret; } @@ -1916,23 +2504,42 @@ static void msm_geni_serial_set_termios(struct uart_port *uport, unsigned long ser_clk_cfg = 0; struct msm_geni_serial_port *port = GET_DEV_PORT(uport); unsigned long clk_rate; - unsigned long flags; + unsigned long desired_rate; + int uart_sampling; + /* QUP_2.5.0 and older RUMI has sampling rate as 32 */ + if (port->rumi_platform && port->is_console) { + geni_write_reg_nolog(0x21, uport->membase, GENI_SER_M_CLK_CFG); + geni_write_reg_nolog(0x21, uport->membase, GENI_SER_S_CLK_CFG); + geni_read_reg_nolog(uport->membase, GENI_SER_M_CLK_CFG); + } if (!uart_console(uport)) { int ret = msm_geni_serial_power_on(uport); if (ret) { + IPC_LOG_MSG(port->ipc_log_misc, + "%s: Failed to vote clock on:%d\n", + __func__, ret); return; } msm_geni_serial_set_manual_flow(false, port); } - /* Take a spinlock else stop_rx causes a race with an ISR due to Cancel - * and FSM_RESET. This also has a potential race with the dma_map/unmap - * operations of ISR. - */ - spin_lock_irqsave(&uport->lock, flags); msm_geni_serial_stop_rx(uport); - spin_unlock_irqrestore(&uport->lock, flags); + /* baud rate */ + baud = uart_get_baud_rate(uport, termios, old, 300, 4000000); + port->cur_baud = baud; + uart_sampling = IS_ENABLED(CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING) ? + UART_OVERSAMPLING / 2 : UART_OVERSAMPLING; + desired_rate = baud * uart_sampling; + + mutex_lock(&port->uart_ssr.ssr_lock); + if (port->uart_ssr.is_ssr_down) { + IPC_LOG_MSG(port->ipc_log_misc, "%s. SSR Down event set\n", + __func__); + mutex_unlock(&port->uart_ssr.ssr_lock); + return; + } + /* baud rate */ baud = uart_get_baud_rate(uport, termios, old, 300, 4000000); port->cur_baud = baud; @@ -2019,15 +2626,27 @@ static void msm_geni_serial_set_termios(struct uart_port *uport, tx_parity_cfg, rx_trans_cfg, rx_parity_cfg, bits_per_char, stop_bit_len, ser_clk_cfg); - if (termios->c_cflag & CRTSCTS) + if (termios->c_cflag & CRTSCTS) { geni_write_reg_nolog(0x0, uport->membase, SE_UART_MANUAL_RFR); + IPC_LOG_MSG(port->ipc_log_misc, + "%s: Manual flow Disabled, HW Flow ON\n", __func__); + } + IPC_LOG_MSG(port->ipc_log_misc, "%s: baud %d\n", __func__, baud); + IPC_LOG_MSG(port->ipc_log_misc, "Tx: trans_cfg%d parity %d\n", + tx_trans_cfg, tx_parity_cfg); + IPC_LOG_MSG(port->ipc_log_misc, "Rx: trans_cfg%d parity %d", + rx_trans_cfg, rx_parity_cfg); + IPC_LOG_MSG(port->ipc_log_misc, "BitsChar%d stop bit%d\n", + bits_per_char, stop_bit_len); exit_set_termios: if (!uart_console(uport)) msm_geni_serial_set_manual_flow(true, port); + msm_geni_serial_start_rx(uport); if (!uart_console(uport)) msm_geni_serial_power_off(uport); + mutex_unlock(&port->uart_ssr.ssr_lock); return; } @@ -2093,9 +2712,9 @@ static ssize_t msm_geni_serial_xfer_mode_store(struct device *dev, return size; msm_geni_serial_power_on(uport); - spin_lock_irqsave(&uport->lock, flags); msm_geni_serial_stop_tx(uport); msm_geni_serial_stop_rx(uport); + spin_lock_irqsave(&uport->lock, flags); port->xfer_mode = xfer_mode; geni_se_select_mode(uport->membase, port->xfer_mode); spin_unlock_irqrestore(&uport->lock, flags); @@ -2125,7 +2744,7 @@ static ssize_t ver_info_show(struct device *dev, } static DEVICE_ATTR_RO(ver_info); -#ifdef SERIAL_CONSOLE +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) static int __init msm_geni_console_setup(struct console *co, char *options) { struct uart_port *uport; @@ -2179,6 +2798,45 @@ msm_geni_serial_early_console_write(struct console *con, const char *s, __msm_geni_serial_console_write(&dev->port, s, n); } +static void msm_geni_serial_cancel_rx(struct uart_port *uport) +{ + int done = 0; + int i = 0; + unsigned int irq_status; + u32 rx_fifo_status; + u32 rx_fifo_wc; + + geni_cancel_s_cmd(uport->membase); + /* Ensure this goes through before polling. */ + mb(); + + done = msm_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS, + S_CMD_CANCEL_EN, true); + if (!done) { + geni_abort_s_cmd(uport->membase); + /* Ensure this goes through before polling. */ + mb(); + msm_geni_serial_poll_bit(uport, SE_GENI_S_IRQ_STATUS, + S_CMD_ABORT_EN, false); + } else if (msm_geni_serial_poll_bit(uport, + SE_GENI_S_IRQ_STATUS, S_RX_FIFO_LAST_EN, true)) { + rx_fifo_status = geni_read_reg_nolog(uport->membase, + SE_GENI_RX_FIFO_STATUS); + rx_fifo_wc = rx_fifo_status & RX_FIFO_WC_MSK; + for (i = 0; i < rx_fifo_wc; i++) + geni_read_reg_nolog(uport->membase, + SE_GENI_RX_FIFOn); + } + + irq_status = geni_read_reg_nolog(uport->membase, + SE_GENI_S_IRQ_STATUS); + geni_write_reg_nolog(irq_status, uport->membase, SE_GENI_S_IRQ_CLEAR); + + if (!done) + geni_write_reg(FORCE_DEFAULT, uport->membase, + GENI_FORCE_DEFAULT_REG); +} + static int __init msm_geni_serial_earlycon_setup(struct earlycon_device *dev, const char *opt) @@ -2237,10 +2895,25 @@ msm_geni_serial_earlycon_setup(struct earlycon_device *dev, s_clk_cfg |= (clk_div << CLK_DIV_SHFT); /* - * Make an unconditional cancel on the main sequencer to reset - * it else we could end up in data loss scenarios. + * Here we need to poll for command done which indicates that + * the previous tx transfer is done. And if the command done interrupt + * is not getting set, then we need to cancel the command. */ - msm_geni_serial_poll_cancel_tx(uport); + msm_geni_serial_poll_tx_done(uport); + + /* + * Here cancel rx is done in polling mode as there is + * no isr support during early console time. + */ + msm_geni_serial_cancel_rx(uport); + + /* Only for earlyconsole */ + if (IS_ENABLED(CONFIG_SERIAL_MSM_GENI_HALF_SAMPLING)) { + geni_write_reg_nolog(0x21, uport->membase, GENI_SER_M_CLK_CFG); + geni_write_reg_nolog(0x21, uport->membase, GENI_SER_S_CLK_CFG); + geni_read_reg_nolog(uport->membase, GENI_SER_M_CLK_CFG); + } + se_get_packing_config(8, 1, false, &cfg0, &cfg1); geni_se_init(uport->membase, (DEF_FIFO_DEPTH_WORDS >> 1), (DEF_FIFO_DEPTH_WORDS - 2)); @@ -2251,8 +2924,14 @@ msm_geni_serial_earlycon_setup(struct earlycon_device *dev, SE_UART_TX_TRANS_CFG); geni_write_reg_nolog(tx_parity_cfg, uport->membase, SE_UART_TX_PARITY_CFG); + geni_write_reg_nolog(rx_trans_cfg, uport->membase, + SE_UART_RX_TRANS_CFG); + geni_write_reg_nolog(rx_parity_cfg, uport->membase, + SE_UART_RX_PARITY_CFG); geni_write_reg_nolog(bits_per_char, uport->membase, SE_UART_TX_WORD_LEN); + geni_write_reg_nolog(bits_per_char, uport->membase, + SE_UART_RX_WORD_LEN); geni_write_reg_nolog(stop_bit, uport->membase, SE_UART_TX_STOP_BIT_LEN); geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_M_CLK_CFG); geni_write_reg_nolog(s_clk_cfg, uport->membase, GENI_SER_S_CLK_CFG); @@ -2296,9 +2975,17 @@ static struct uart_driver msm_geni_console_driver = { .nr = GENI_UART_NR_PORTS, .cons = &cons_ops, }; -#endif +#else +static int console_register(struct uart_driver *drv) +{ + return 0; +} + +static void console_unregister(struct uart_driver *drv) +{ +} +#endif /* defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) */ -#ifdef DEBUG static void msm_geni_serial_debug_init(struct uart_port *uport, bool console) { struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); @@ -2307,17 +2994,88 @@ static void msm_geni_serial_debug_init(struct uart_port *uport, bool console) msm_port->dbg = debugfs_create_dir(dev_name(uport->dev), NULL); if (IS_ERR_OR_NULL(msm_port->dbg)) dev_err(uport->dev, "Failed to create dbg dir\n"); + + if (!console) { + memset(name, 0, sizeof(name)); + if (!msm_port->ipc_log_rx) { + scnprintf(name, sizeof(name), "%s%s", + dev_name(uport->dev), "_rx"); + msm_port->ipc_log_rx = ipc_log_context_create( + IPC_LOG_TX_RX_PAGES, name, 0); + if (!msm_port->ipc_log_rx) + dev_info(uport->dev, "Err in Rx IPC Log\n"); + } + memset(name, 0, sizeof(name)); + if (!msm_port->ipc_log_tx) { + scnprintf(name, sizeof(name), "%s%s", + dev_name(uport->dev), "_tx"); + msm_port->ipc_log_tx = ipc_log_context_create( + IPC_LOG_TX_RX_PAGES, name, 0); + if (!msm_port->ipc_log_tx) + dev_info(uport->dev, "Err in Tx IPC Log\n"); + } + memset(name, 0, sizeof(name)); + if (!msm_port->ipc_log_pwr) { + scnprintf(name, sizeof(name), "%s%s", + dev_name(uport->dev), "_pwr"); + msm_port->ipc_log_pwr = ipc_log_context_create( + IPC_LOG_PWR_PAGES, name, 0); + if (!msm_port->ipc_log_pwr) + dev_info(uport->dev, "Err in Pwr IPC Log\n"); + } + memset(name, 0, sizeof(name)); + if (!msm_port->ipc_log_misc) { + scnprintf(name, sizeof(name), "%s%s", + dev_name(uport->dev), "_misc"); + msm_port->ipc_log_misc = ipc_log_context_create( + IPC_LOG_MISC_PAGES, name, 0); + if (!msm_port->ipc_log_misc) + dev_info(uport->dev, "Err in Misc IPC Log\n"); + } + } else { + memset(name, 0, sizeof(name)); + if (!msm_port->console_log) { + scnprintf(name, sizeof(name), "%s%s", + dev_name(uport->dev), "_console"); + msm_port->console_log = ipc_log_context_create( + IPC_LOG_MISC_PAGES, name, 0); + if (!msm_port->console_log) + dev_info(uport->dev, "Err in Misc IPC Log\n"); + } + } } -#endif static void msm_geni_serial_cons_pm(struct uart_port *uport, unsigned int new_state, unsigned int old_state) { struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); - if (unlikely(!uart_console(uport))) + if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) + se_geni_resources_on(&msm_port->serial_rsc); + else if (new_state == UART_PM_STATE_OFF && + old_state == UART_PM_STATE_ON) + se_geni_resources_off(&msm_port->serial_rsc); +} + +static void msm_geni_serial_hs_pm(struct uart_port *uport, + unsigned int new_state, unsigned int old_state) +{ + struct msm_geni_serial_port *msm_port = GET_DEV_PORT(uport); + + /* + * This will get call for system suspend/resume and + * Applicable for hs-uart without runtime pm framework support. + */ + if (pm_runtime_enabled(uport->dev)) return; + /* + * Default PM State is UNDEFINED Setting it to OFF State. + * This will allow add one port to do resources on and off during probe + */ + if (old_state == UART_PM_STATE_UNDEFINED) + old_state = UART_PM_STATE_OFF; + if (new_state == UART_PM_STATE_ON && old_state == UART_PM_STATE_OFF) se_geni_resources_on(&msm_port->serial_rsc); else if (new_state == UART_PM_STATE_OFF && @@ -2359,10 +3117,12 @@ static const struct uart_ops msm_geni_serial_pops = { .break_ctl = msm_geni_serial_break_ctl, .flush_buffer = NULL, .ioctl = msm_geni_serial_ioctl, + /* For HSUART nodes without IOCTL support */ + .pm = msm_geni_serial_hs_pm, }; static const struct of_device_id msm_geni_device_tbl[] = { -#ifdef SERIAL_CONSOLE +#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL) { .compatible = "qcom,msm-geni-console", .data = (void *)&msm_geni_console_driver}, #endif @@ -2387,15 +3147,24 @@ static int msm_geni_serial_get_ver_info(struct uart_port *uport) msm_port->ver_info.m_fw_ver = get_se_m_fw(uport->membase); msm_port->ver_info.s_fw_ver = get_se_s_fw(uport->membase); + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: FW Ver:0x%x%x\n", + __func__, + msm_port->ver_info.m_fw_ver, msm_port->ver_info.s_fw_ver); hw_ver = geni_se_qupv3_hw_version(msm_port->wrapper_dev, &msm_port->ver_info.hw_major_ver, &msm_port->ver_info.hw_minor_ver, &msm_port->ver_info.hw_step_ver); - if (hw_ver) dev_err(uport->dev, "%s:Err getting HW version %d\n", __func__, hw_ver); + else + IPC_LOG_MSG(msm_port->ipc_log_misc, "%s: HW Ver:%x.%x.%x\n", + __func__, msm_port->ver_info.hw_major_ver, + msm_port->ver_info.hw_minor_ver, + msm_port->ver_info.hw_step_ver); + + msm_geni_serial_enable_interrupts(uport); exit_ver_info: se_geni_clks_off(&msm_port->serial_rsc); return ret; @@ -2449,13 +3218,8 @@ static int msm_geni_serial_probe(struct platform_device *pdev) "M - DRIVER GENI_HS_UART_%d Init", line); place_marker(boot_marker); -#ifdef SERIAL_CONSOLE is_console = (drv->cons ? true : false); dev_port = get_port_from_line(line, is_console); -#else - dev_port = get_port_from_line(line); -#endif - if (IS_ERR_OR_NULL(dev_port)) { ret = PTR_ERR(dev_port); dev_err(&pdev->dev, "Invalid line %d(%d)\n", @@ -2603,6 +3367,9 @@ static int msm_geni_serial_probe(struct platform_device *pdev) dev_port->tx_fifo_width = DEF_FIFO_WIDTH_BITS; uport->fifosize = ((dev_port->tx_fifo_depth * dev_port->tx_fifo_width) >> 3); + /* Complete signals to handle cancel cmd completion */ + init_completion(&dev_port->m_cmd_timeout); + init_completion(&dev_port->s_cmd_timeout); uport->irq = platform_get_irq(pdev, 0); if (uport->irq < 0) { @@ -2611,43 +3378,84 @@ static int msm_geni_serial_probe(struct platform_device *pdev) goto exit_geni_serial_probe; } + dev_port->name = devm_kasprintf(uport->dev, GFP_KERNEL, + "msm_serial_geni%d", uport->line); + irq_set_status_flags(uport->irq, IRQ_NOAUTOEN); + ret = devm_request_irq(uport->dev, uport->irq, msm_geni_serial_isr, + IRQF_TRIGGER_HIGH, dev_port->name, uport); + if (ret) { + dev_err(uport->dev, "%s: Failed to get IRQ ret %d\n", + __func__, ret); + goto exit_geni_serial_probe; + } + uport->private_data = (void *)drv; platform_set_drvdata(pdev, dev_port); + /* + * To Disable PM runtime API that will make ioctl based + * vote_clock_on/off optional and rely on system PM + */ + dev_port->pm_auto_suspend_disable = + of_property_read_bool(pdev->dev.of_node, + "qcom,auto-suspend-disable"); + if (is_console) { dev_port->handle_rx = handle_rx_console; dev_port->rx_fifo = devm_kzalloc(uport->dev, sizeof(u32), GFP_KERNEL); } else { - pm_runtime_set_suspended(&pdev->dev); - pm_runtime_set_autosuspend_delay(&pdev->dev, 150); - pm_runtime_use_autosuspend(&pdev->dev); - pm_runtime_enable(&pdev->dev); + if (dev_port->pm_auto_suspend_disable) { + pm_runtime_set_active(&pdev->dev); + pm_runtime_forbid(&pdev->dev); + } else { + pm_runtime_set_suspended(&pdev->dev); + pm_runtime_set_autosuspend_delay(&pdev->dev, 150); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + } } dev_info(&pdev->dev, "Serial port%d added.FifoSize %d is_console%d\n", line, uport->fifosize, is_console); + + /* + * SSR functionalities are required for SSC QUP in + * Automotive platform only + */ + dev_port->serial_rsc.rsc_ssr.force_suspend = + msm_geni_serial_ssr_down; + dev_port->serial_rsc.rsc_ssr.force_resume = + msm_geni_serial_ssr_up; + mutex_init(&dev_port->uart_ssr.ssr_lock); + device_create_file(uport->dev, &dev_attr_loopback); device_create_file(uport->dev, &dev_attr_xfer_mode); device_create_file(uport->dev, &dev_attr_ver_info); -#ifdef DEBUG msm_geni_serial_debug_init(uport, is_console); -#endif dev_port->port_setup = false; ret = msm_geni_serial_get_ver_info(uport); if (ret) goto exit_geni_serial_probe; ret = uart_add_one_port(drv, uport); - if (!ret) { - if (strcmp(id->compatible, "qcom,msm-geni-console") == 0) - snprintf(boot_marker, sizeof(boot_marker), - "M - DRIVER GENI_UART_%d Ready", line); - else - snprintf(boot_marker, sizeof(boot_marker), - "M - DRIVER GENI_HS_UART_%d Ready", line); - place_marker(boot_marker); - } + if (ret) + goto exit_geni_serial_probe; + + if (strcmp(id->compatible, "qcom,msm-geni-console") == 0) + snprintf(boot_marker, sizeof(boot_marker), + "M - DRIVER GENI_UART_%d Ready", line); + else + snprintf(boot_marker, sizeof(boot_marker), + "M - DRIVER GENI_HS_UART_%d Ready", line); + place_marker(boot_marker); + + IPC_LOG_MSG(dev_port->ipc_log_misc, "%s: port:%s irq:%d\n", __func__, + uport->name, uport->irq); + + return 0; + exit_geni_serial_probe: + IPC_LOG_MSG(dev_port->ipc_log_misc, "%s: ret:%d\n", __func__, ret); return ret; } @@ -2657,11 +3465,13 @@ static int msm_geni_serial_remove(struct platform_device *pdev) struct uart_driver *drv = (struct uart_driver *)port->uport.private_data; + if (port->pm_auto_suspend_disable) + pm_runtime_allow(&pdev->dev); wakeup_source_trash(&port->geni_wake); uart_remove_one_port(drv, &port->uport); if (port->rx_dma) { - dma_free_coherent(port->wrapper_dev, DMA_RX_BUF_SIZE, - port->rx_buf, port->rx_dma); + geni_se_iommu_free_buf(port->wrapper_dev, &port->rx_dma, + port->rx_buf, DMA_RX_BUF_SIZE); port->rx_dma = (dma_addr_t)NULL; } return 0; @@ -2679,16 +3489,17 @@ static int msm_geni_serial_runtime_suspend(struct device *dev) wait_for_transfers_inflight(&port->uport); /* - * Disable Interrupt * Manual RFR On. * Stop Rx. + * Disable Interrupt * Resources off */ - disable_irq(port->uport.irq); stop_rx_sequencer(&port->uport); geni_status = geni_read_reg_nolog(port->uport.membase, SE_GENI_STATUS); if ((geni_status & M_GENI_CMD_ACTIVE)) stop_tx_sequencer(&port->uport); + + disable_irq(port->uport.irq); ret = se_geni_resources_off(&port->serial_rsc); if (ret) { dev_err(dev, "%s: Error ret %d\n", __func__, ret); @@ -2698,6 +3509,7 @@ static int msm_geni_serial_runtime_suspend(struct device *dev) port->edge_count = 0; enable_irq(port->wakeup_irq); } + IPC_LOG_MSG(port->ipc_log_pwr, "%s:\n", __func__); __pm_relax(&port->geni_wake); exit_runtime_suspend: return ret; @@ -2732,8 +3544,9 @@ static int msm_geni_serial_runtime_resume(struct device *dev) start_rx_sequencer(&port->uport); /* Ensure that the Rx is running before enabling interrupts */ mb(); - if (pm_runtime_enabled(dev)) - enable_irq(port->uport.irq); + /* Enable interrupt */ + enable_irq(port->uport.irq); + IPC_LOG_MSG(port->ipc_log_pwr, "%s:\n", __func__); exit_runtime_resume: return ret; } @@ -2744,7 +3557,7 @@ static int msm_geni_serial_sys_suspend_noirq(struct device *dev) struct msm_geni_serial_port *port = platform_get_drvdata(pdev); struct uart_port *uport = &port->uport; - if (uart_console(uport)) { + if (uart_console(uport) || !pm_runtime_enabled(uport->dev)) { uart_suspend_port((struct uart_driver *)uport->private_data, uport); } else { @@ -2755,9 +3568,13 @@ static int msm_geni_serial_sys_suspend_noirq(struct device *dev) if (!pm_runtime_status_suspended(dev)) { dev_err(dev, "%s:Active userspace vote; ioctl_cnt %d\n", __func__, port->ioctl_count); + IPC_LOG_MSG(port->ipc_log_pwr, + "%s:Active userspace vote; ioctl_cnt %d\n", + __func__, port->ioctl_count); mutex_unlock(&tty_port->mutex); return -EBUSY; } + IPC_LOG_MSG(port->ipc_log_pwr, "%s\n", __func__); mutex_unlock(&tty_port->mutex); } return 0; @@ -2769,11 +3586,11 @@ static int msm_geni_serial_sys_resume_noirq(struct device *dev) struct msm_geni_serial_port *port = platform_get_drvdata(pdev); struct uart_port *uport = &port->uport; - if (uart_console(uport) && - console_suspend_enabled && uport->suspended) { + if ((uart_console(uport) && + console_suspend_enabled && uport->suspended) || + !pm_runtime_enabled(uport->dev)) { uart_resume_port((struct uart_driver *)uport->private_data, uport); - disable_irq(uport->irq); } return 0; } @@ -2783,19 +3600,24 @@ static int msm_geni_serial_sys_hib_resume_noirq(struct device *dev) struct platform_device *pdev = to_platform_device(dev); struct msm_geni_serial_port *port = platform_get_drvdata(pdev); struct uart_port *uport = &port->uport; - unsigned long cfg0, cfg1; - if (uart_console(uport) && - console_suspend_enabled && uport->suspended) { + if (uart_console(uport)) { uart_resume_port((struct uart_driver *)uport->private_data, uport); - dev_dbg(dev, "%s\n", __func__); - se_get_packing_config(8, 1, false, &cfg0, &cfg1); - geni_write_reg_nolog(cfg0, uport->membase, - SE_GENI_TX_PACKING_CFG0); - geni_write_reg_nolog(cfg1, uport->membase, - SE_GENI_TX_PACKING_CFG1); - disable_irq(uport->irq); + /* + * For hibernation usecase clients for + * console UART won't call port setup during restore. + * Hence call port setup for console uart. + */ + msm_geni_serial_port_setup(uport); + } else { + /* + * Peripheral register settings are lost during hibernation. + * Update setup flag such that port setup happens again + * during next session. Clients of HS-UART will close and + * open the port during hibernation. + */ + port->port_setup = false; } return 0; } @@ -2836,6 +3658,42 @@ static const struct dev_pm_ops msm_geni_serial_pm_ops = { .thaw = msm_geni_serial_sys_hib_resume_noirq, }; +static void msm_geni_serial_ssr_down(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct msm_geni_serial_port *port = platform_get_drvdata(pdev); + struct uart_port *uport = &port->uport; + int ret = 0; + + mutex_lock(&port->uart_ssr.ssr_lock); + port->uart_ssr.is_ssr_down = true; + ret = pm_runtime_force_suspend(uport->dev); + if (ret) { + dev_err(uport->dev, "%s:force suspend failed %d\n", + ret, __func__); + goto exit; + } + + pm_runtime_put_noidle(uport->dev); + pm_runtime_enable(uport->dev); + + IPC_LOG_MSG(port->ipc_log_misc, "%s: Force suspend done\n", __func__); +exit: + mutex_unlock(&port->uart_ssr.ssr_lock); +} + +static void msm_geni_serial_ssr_up(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct msm_geni_serial_port *port = platform_get_drvdata(pdev); + + mutex_lock(&port->uart_ssr.ssr_lock); + port->uart_ssr.is_ssr_down = false; + port->port_setup = false; + IPC_LOG_MSG(port->ipc_log_misc, "%s: Force resume done\n", __func__); + mutex_unlock(&port->uart_ssr.ssr_lock); +} + static struct platform_driver msm_geni_serial_platform_driver = { .remove = msm_geni_serial_remove, .probe = msm_geni_serial_probe, @@ -2864,37 +3722,28 @@ static int __init msm_geni_serial_init(void) msm_geni_serial_ports[i].uport.ops = &msm_geni_serial_pops; msm_geni_serial_ports[i].uport.flags = UPF_BOOT_AUTOCONF; msm_geni_serial_ports[i].uport.line = i; - mutex_init(&msm_geni_serial_ports[i].ioctl_mutex); } -#ifdef SERIAL_CONSOLE for (i = 0; i < GENI_UART_CONS_PORTS; i++) { msm_geni_console_port.uport.iotype = UPIO_MEM; msm_geni_console_port.uport.ops = &msm_geni_console_pops; msm_geni_console_port.uport.flags = UPF_BOOT_AUTOCONF; msm_geni_console_port.uport.line = i; - mutex_init(&msm_geni_console_port.ioctl_mutex); } -#endif -#ifdef SERIAL_CONSOLE ret = console_register(&msm_geni_console_driver); if (ret) return ret; -#endif + ret = uart_register_driver(&msm_geni_serial_hs_driver); if (ret) { -#ifdef SERIAL_CONSOLE uart_unregister_driver(&msm_geni_console_driver); -#endif return ret; } ret = platform_driver_register(&msm_geni_serial_platform_driver); if (ret) { -#ifdef SERIAL_CONSOLE console_unregister(&msm_geni_console_driver); -#endif uart_unregister_driver(&msm_geni_serial_hs_driver); return ret; } @@ -2908,9 +3757,7 @@ static void __exit msm_geni_serial_exit(void) { platform_driver_unregister(&msm_geni_serial_platform_driver); uart_unregister_driver(&msm_geni_serial_hs_driver); -#ifdef SERIAL_CONSOLE console_unregister(&msm_geni_console_driver); -#endif } module_exit(msm_geni_serial_exit); diff --git a/drivers/tty/serial/msm_serial.c b/drivers/tty/serial/msm_serial.c index a852e82f4ac5..f5578b315054 100644 --- a/drivers/tty/serial/msm_serial.c +++ b/drivers/tty/serial/msm_serial.c @@ -391,10 +391,14 @@ no_rx: static inline void msm_wait_for_xmitr(struct uart_port *port) { + unsigned int timeout = 500000; + while (!(msm_read(port, UART_SR) & UART_SR_TX_EMPTY)) { if (msm_read(port, UART_ISR) & UART_ISR_TX_READY) break; udelay(1); + if (!timeout--) + break; } msm_write(port, UART_CR_CMD_RESET_TX_READY, UART_CR); } @@ -868,6 +872,7 @@ static void msm_handle_tx(struct uart_port *port) struct circ_buf *xmit = &msm_port->uart.state->xmit; struct msm_dma *dma = &msm_port->tx_dma; unsigned int pio_count, dma_count, dma_min; + char buf[4] = { 0 }; void __iomem *tf; int err = 0; @@ -877,10 +882,12 @@ static void msm_handle_tx(struct uart_port *port) else tf = port->membase + UART_TF; + buf[0] = port->x_char; + if (msm_port->is_uartdm) msm_reset_dm_count(port, 1); - iowrite8_rep(tf, &port->x_char, 1); + iowrite32_rep(tf, buf, 1); port->icount.tx++; port->x_char = 0; return; @@ -981,6 +988,7 @@ static unsigned int msm_get_mctrl(struct uart_port *port) static void msm_reset(struct uart_port *port) { struct msm_port *msm_port = UART_TO_MSM(port); + unsigned int mr; /* reset everything */ msm_write(port, UART_CR_CMD_RESET_RX, UART_CR); @@ -988,7 +996,10 @@ static void msm_reset(struct uart_port *port) msm_write(port, UART_CR_CMD_RESET_ERR, UART_CR); msm_write(port, UART_CR_CMD_RESET_BREAK_INT, UART_CR); msm_write(port, UART_CR_CMD_RESET_CTS, UART_CR); - msm_write(port, UART_CR_CMD_SET_RFR, UART_CR); + msm_write(port, UART_CR_CMD_RESET_RFR, UART_CR); + mr = msm_read(port, UART_MR1); + mr &= ~UART_MR1_RX_RDY_CTL; + msm_write(port, mr, UART_MR1); /* Disable DM modes */ if (msm_port->is_uartdm) @@ -1591,6 +1602,7 @@ static void __msm_console_write(struct uart_port *port, const char *s, int num_newlines = 0; bool replaced = false; void __iomem *tf; + int locked = 1; if (is_uartdm) tf = port->membase + UARTDM_TF; @@ -1603,7 +1615,13 @@ static void __msm_console_write(struct uart_port *port, const char *s, num_newlines++; count += num_newlines; - spin_lock(&port->lock); + if (port->sysrq) + locked = 0; + else if (oops_in_progress) + locked = spin_trylock(&port->lock); + else + spin_lock(&port->lock); + if (is_uartdm) msm_reset_dm_count(port, count); @@ -1639,7 +1657,9 @@ static void __msm_console_write(struct uart_port *port, const char *s, iowrite32_rep(tf, buf, 1); i += num_chars; } - spin_unlock(&port->lock); + + if (locked) + spin_unlock(&port->lock); } #ifdef CONFIG_SERIAL_RX_CONSOLE_ONLY diff --git a/drivers/tty/serial/msm_serial_hs.c b/drivers/tty/serial/msm_serial_hs.c index 9a2ca1c4fe03..4346ed2c1c4a 100644 --- a/drivers/tty/serial/msm_serial_hs.c +++ b/drivers/tty/serial/msm_serial_hs.c @@ -61,7 +61,6 @@ #include #include #include -#include #include #include @@ -89,13 +88,26 @@ enum { DBG_LEV = 4U, }; -#define MSM_HS_DBG(x...) ((void)0) +#define MSM_HS_DBG(x...) do { \ + if (msm_uport->ipc_debug_mask >= DBG_LEV) { \ + if (msm_uport->ipc_msm_hs_log_ctxt) \ + ipc_log_string(msm_uport->ipc_msm_hs_log_ctxt, x); \ + } \ +} while (0) -#define MSM_HS_INFO(x...) ((void)0) +#define MSM_HS_INFO(x...) do { \ + if (msm_uport->ipc_debug_mask >= INFO_LEV) {\ + if (msm_uport->ipc_msm_hs_log_ctxt) \ + ipc_log_string(msm_uport->ipc_msm_hs_log_ctxt, x); \ + } \ +} while (0) /* warnings and errors show up on console always */ #define MSM_HS_WARN(x...) do { \ pr_warn(x); \ + if (msm_uport->ipc_msm_hs_log_ctxt && \ + msm_uport->ipc_debug_mask >= WARN_LEV) \ + ipc_log_string(msm_uport->ipc_msm_hs_log_ctxt, x); \ } while (0) /* ERROR condition in the driver sets the hs_serial_debug_mask @@ -104,9 +116,17 @@ enum { */ #define MSM_HS_ERR(x...) do { \ pr_err(x); \ + if (msm_uport->ipc_msm_hs_log_ctxt && \ + msm_uport->ipc_debug_mask >= ERR_LEV) { \ + ipc_log_string(msm_uport->ipc_msm_hs_log_ctxt, x); \ + msm_uport->ipc_debug_mask = FATAL_LEV; \ + } \ } while (0) -#define LOG_USR_MSG(ctx, x...) ((void)0) +#define LOG_USR_MSG(ctx, x...) do { \ + if (ctx) \ + ipc_log_string(ctx, x); \ +} while (0) /* * There are 3 different kind of UART Core available on MSM. @@ -2739,7 +2759,6 @@ static int uartdm_init_port(struct uart_port *uport) struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); struct msm_hs_tx *tx = &msm_uport->tx; struct msm_hs_rx *rx = &msm_uport->rx; - struct sched_param param = { .sched_priority = 1 }; init_waitqueue_head(&rx->wait); init_waitqueue_head(&tx->wait); @@ -2754,18 +2773,15 @@ static int uartdm_init_port(struct uart_port *uport) MSM_HS_ERR("%s(): error creating task\n", __func__); goto exit_lh_init; } - sched_setscheduler(rx->task, SCHED_FIFO, ¶m); - kthread_init_work(&rx->kwork, msm_serial_hs_rx_work); kthread_init_worker(&tx->kworker); tx->task = kthread_run(kthread_worker_fn, &tx->kworker, "msm_serial_hs_%d_tx_work", uport->line); - if (IS_ERR(tx->task)) { + if (IS_ERR(rx->task)) { MSM_HS_ERR("%s(): error creating task\n", __func__); goto exit_lh_init; } - sched_setscheduler(tx->task, SCHED_FIFO, ¶m); kthread_init_work(&tx->kwork, msm_serial_hs_tx_work); @@ -3285,7 +3301,6 @@ static void msm_serial_hs_rt_init(struct uart_port *uport) msm_uport->pm_state = MSM_HS_PM_SUSPENDED; mutex_unlock(&msm_uport->mtx); pm_runtime_enable(uport->dev); - tty_port_set_policy(&uport->state->port, SCHED_FIFO, 1); } static int msm_hs_runtime_suspend(struct device *dev) diff --git a/drivers/tty/serial/mvebu-uart.c b/drivers/tty/serial/mvebu-uart.c index a10e4aa9e18e..401c983ec5f3 100644 --- a/drivers/tty/serial/mvebu-uart.c +++ b/drivers/tty/serial/mvebu-uart.c @@ -581,7 +581,7 @@ static int mvebu_uart_probe(struct platform_device *pdev) port->membase = devm_ioremap_resource(&pdev->dev, reg); if (IS_ERR(port->membase)) - return PTR_ERR(port->membase); + return -PTR_ERR(port->membase); data = devm_kzalloc(&pdev->dev, sizeof(struct mvebu_uart_data), GFP_KERNEL); diff --git a/drivers/tty/serial/sc16is7xx.c b/drivers/tty/serial/sc16is7xx.c index 590acca60134..c1655aba131f 100644 --- a/drivers/tty/serial/sc16is7xx.c +++ b/drivers/tty/serial/sc16is7xx.c @@ -1524,12 +1524,10 @@ static int __init sc16is7xx_init(void) #endif return ret; -#ifdef CONFIG_SERIAL_SC16IS7XX_SPI err_spi: #ifdef CONFIG_SERIAL_SC16IS7XX_I2C i2c_del_driver(&sc16is7xx_i2c_uart_driver); #endif -#endif err_i2c: uart_unregister_driver(&sc16is7xx_uart); return ret; diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c index 06cf474072d6..333de7d3fe86 100644 --- a/drivers/tty/serial/sh-sci.c +++ b/drivers/tty/serial/sh-sci.c @@ -841,16 +841,9 @@ static void sci_receive_chars(struct uart_port *port) tty_insert_flip_char(tport, c, TTY_NORMAL); } else { for (i = 0; i < count; i++) { - char c; + char c = serial_port_in(port, SCxRDR); - if (port->type == PORT_SCIF || - port->type == PORT_HSCIF) { - status = serial_port_in(port, SCxSR); - c = serial_port_in(port, SCxRDR); - } else { - c = serial_port_in(port, SCxRDR); - status = serial_port_in(port, SCxSR); - } + status = serial_port_in(port, SCxSR); if (uart_handle_sysrq_char(port, c)) { count--; i--; continue; diff --git a/drivers/tty/serial/xilinx_uartps.c b/drivers/tty/serial/xilinx_uartps.c index 81657f09761c..0dbfd02e3b19 100644 --- a/drivers/tty/serial/xilinx_uartps.c +++ b/drivers/tty/serial/xilinx_uartps.c @@ -31,7 +31,6 @@ #include #include #include -#include #define CDNS_UART_TTY_NAME "ttyPS" #define CDNS_UART_NAME "xuartps" @@ -40,7 +39,6 @@ #define CDNS_UART_NR_PORTS 2 #define CDNS_UART_FIFO_SIZE 64 /* FIFO size */ #define CDNS_UART_REGISTER_SPACE 0x1000 -#define TX_TIMEOUT 500000 /* Rx Trigger level */ static int rx_trigger_level = 56; @@ -687,21 +685,18 @@ static void cdns_uart_set_termios(struct uart_port *port, unsigned int cval = 0; unsigned int baud, minbaud, maxbaud; unsigned long flags; - unsigned int ctrl_reg, mode_reg, val; - int err; + unsigned int ctrl_reg, mode_reg; + + spin_lock_irqsave(&port->lock, flags); /* Wait for the transmit FIFO to empty before making changes */ if (!(readl(port->membase + CDNS_UART_CR) & CDNS_UART_CR_TX_DIS)) { - err = readl_poll_timeout(port->membase + CDNS_UART_SR, - val, (val & CDNS_UART_SR_TXEMPTY), - 1000, TX_TIMEOUT); - if (err) { - dev_err(port->dev, "timed out waiting for tx empty"); - return; + while (!(readl(port->membase + CDNS_UART_SR) & + CDNS_UART_SR_TXEMPTY)) { + cpu_relax(); } } - spin_lock_irqsave(&port->lock, flags); /* Disable the TX and RX to set baud rate */ ctrl_reg = readl(port->membase + CDNS_UART_CR); diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h index e7f5b4c62b18..8a2ecede870b 100644 --- a/include/linux/qcom-geni-se.h +++ b/include/linux/qcom-geni-se.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -50,14 +50,12 @@ struct ssc_qup_nb { /** * struct ssc_qup_ssr GENI Serial Engine SSC qup SSR Structure. - * @probe_completed To ignore up notification during probe. * @is_ssr_down To check SE status. * @subsys_name Subsystem name for ssr registration. * @active_list_head List Head of all client in SSC QUPv3. */ struct ssc_qup_ssr { struct ssc_qup_nb ssc_qup_nb; - bool probe_completed; bool is_ssr_down; const char *subsys_name; struct list_head active_list_head; @@ -68,13 +66,11 @@ struct ssc_qup_ssr { * @active_list List of SSC qup SE clients. * @force_suspend Function pointer for Subsystem shutdown case. * @force_resume Function pointer for Subsystem restart case. - * @ssr_enable To check SSC Qup SSR enable status. */ struct se_rsc_ssr { struct list_head active_list; - int (*force_suspend)(struct device *ctrl_dev); - int (*force_resume)(struct device *ctrl_dev); - bool ssr_enable; + void (*force_suspend)(struct device *ctrl_dev); + void (*force_resume)(struct device *ctrl_dev); }; /** @@ -181,8 +177,8 @@ struct se_geni_rsc { /* GENI_OUTPUT_CTRL fields */ #define DEFAULT_IO_OUTPUT_CTRL_MSK (GENMASK(6, 0)) -#define GENI_IO_MUX_0_EN BIT(1) -#define GENI_IO_MUX_1_EN BIT(2) +#define GENI_IO_MUX_0_EN BIT(0) +#define GENI_IO_MUX_1_EN BIT(1) /* GENI_CFG_REG80 fields */ #define IO1_SEL_TX BIT(2) @@ -386,6 +382,7 @@ struct se_geni_rsc { #define TX_EOT (BIT(1)) #define TX_SBE (BIT(2)) #define TX_RESET_DONE (BIT(3)) +#define TX_GENI_CANCEL_IRQ (BIT(14)) /* SE_DMA_RX_IRQ_STAT Register fields */ #define RX_DMA_DONE (BIT(0)) @@ -394,9 +391,20 @@ struct se_geni_rsc { #define RX_RESET_DONE (BIT(3)) #define RX_FLUSH_DONE (BIT(4)) #define RX_GENI_GP_IRQ (GENMASK(10, 5)) -#define RX_GENI_CANCEL_IRQ (BIT(11)) +/* + * QUPs which have HW version <=1.2 11th bit of + * DMA_RX_IRQ_STAT register denotes RX_GENI_CANCEL_IRQ bit. + */ +#define RX_GENI_CANCEL_IRQ(n) (((n.hw_major_ver <= 1) &&\ + (n.hw_minor_ver <= 2)) ? BIT(11) : BIT(14)) #define RX_GENI_GP_IRQ_EXT (GENMASK(13, 12)) +/* DMA DEBUG Register fields */ +#define DMA_TX_ACTIVE (BIT(0)) +#define DMA_RX_ACTIVE (BIT(1)) +#define DMA_TX_STATE (GENMASK(7, 4)) +#define DMA_RX_STATE (GENMASK(11, 8)) + #define DEFAULT_BUS_WIDTH (4) #define DEFAULT_SE_CLK (19200000) @@ -740,16 +748,6 @@ int geni_se_tx_dma_prep(struct device *wrapper_dev, void __iomem *base, */ void geni_se_rx_dma_start(void __iomem *base, int rx_len, dma_addr_t *rx_dma); -/** - * geni_get_iommu_dev() - Returns IOMMU device attached to QUP wrapper. - * @wrapper_dev Pointer to QUP wrapper device. - * - * This functions returns IOMMU device attached to QUP wrapper node. - * - * Return Pointer to IOMMU dev. - */ -struct device *geni_get_iommu_dev(struct device *wrapper_dev); - /** * geni_se_rx_dma_prep() - Prepare the Serial Engine for RX DMA transfer * @wrapper_dev: QUPv3 Wrapper Device to which the TX buffer is mapped. @@ -1070,9 +1068,5 @@ static void geni_se_rx_dma_start(void __iomem *base, int rx_len, { } -struct device *geni_get_iommu_dev(struct device *wrapper_dev) -{ -} - #endif #endif