From: Roja Rani Yarubandi <rojay@codeaurora.org> To: wsa@kernel.org Cc: swboyd@chromium.org, dianders@chromium.org, saiprakash.ranjan@codeaurora.org, gregkh@linuxfoundation.org, mka@chromium.org, akashast@codeaurora.org, msavaliy@qti.qualcomm.com, skakit@codeaurora.org, rnayak@codeaurora.org, agross@kernel.org, bjorn.andersson@linaro.org, linux-arm-msm@vger.kernel.org, linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org, sumit.semwal@linaro.org, linux-media@vger.kernel.org, dri-devel@lists.freedesktop.org, linaro-mm-sig@lists.linaro.org, Roja Rani Yarubandi <rojay@codeaurora.org> Subject: [PATCH V3] i2c: i2c-qcom-geni: Add shutdown callback for i2c Date: Mon, 7 Sep 2020 18:37:31 +0530 [thread overview] Message-ID: <20200907130731.2607-1-rojay@codeaurora.org> (raw) If the hardware is still accessing memory after SMMU translation is disabled (as part of smmu shutdown callback), then the IOVAs (I/O virtual address) which it was using will go on the bus as the physical addresses which will result in unknown crashes like NoC/interconnect errors. So, implement shutdown callback to i2c driver to stop on-going transfer and unmap DMA mappings during system "reboot" or "shutdown". Store DMA mapping data in geni_i2c_dev struct to enhance DMA mapping data scope. For example during shutdown callback to unmap DMA mapping, this stored DMA mapping data can be used to call geni_se_tx_dma_unprep and geni_se_rx_dma_unprep functions. Fixes: 37692de5d523 ("i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller") Signed-off-by: Roja Rani Yarubandi <rojay@codeaurora.org> --- Changes in V2: - As per Stephen's comments added seperate function for stop transfer, fixed minor nitpicks. - As per Stephen's comments, changed commit text. Changes in V3: - As per Stephen's comments, squashed patch 1 into patch 2, added Fixes tag. - As per Akash's comments, included FIFO case in stop_xfer, fixed minor nitpicks. drivers/i2c/busses/i2c-qcom-geni.c | 70 +++++++++++++++++++++++++++--- include/linux/qcom-geni-se.h | 5 +++ 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index dead5db3315a..b3609760909f 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -86,6 +86,9 @@ struct geni_i2c_dev { u32 clk_freq_out; const struct geni_i2c_clk_fld *clk_fld; int suspended; + dma_addr_t tx_dma; + dma_addr_t rx_dma; + size_t xfer_len; }; struct geni_i2c_err_log { @@ -352,12 +355,12 @@ static void geni_i2c_tx_fsm_rst(struct geni_i2c_dev *gi2c) static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, u32 m_param) { - dma_addr_t rx_dma; unsigned long time_left; void *dma_buf = NULL; struct geni_se *se = &gi2c->se; size_t len = msg->len; + gi2c->xfer_len = len; if (!of_machine_is_compatible("lenovo,yoga-c630")) dma_buf = i2c_get_dma_safe_msg_buf(msg, 32); @@ -368,7 +371,7 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, writel_relaxed(len, se->base + SE_I2C_RX_TRANS_LEN); - if (dma_buf && geni_se_rx_dma_prep(se, dma_buf, len, &rx_dma)) { + if (dma_buf && geni_se_rx_dma_prep(se, dma_buf, len, &gi2c->rx_dma)) { geni_se_select_mode(se, GENI_SE_FIFO); i2c_put_dma_safe_msg_buf(dma_buf, msg, false); dma_buf = NULL; @@ -384,7 +387,8 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, if (dma_buf) { if (gi2c->err) geni_i2c_rx_fsm_rst(gi2c); - geni_se_rx_dma_unprep(se, rx_dma, len); + geni_se_rx_dma_unprep(se, gi2c->rx_dma, len); + gi2c->rx_dma = (dma_addr_t)NULL; i2c_put_dma_safe_msg_buf(dma_buf, msg, !gi2c->err); } @@ -394,12 +398,12 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, u32 m_param) { - dma_addr_t tx_dma; unsigned long time_left; void *dma_buf = NULL; struct geni_se *se = &gi2c->se; size_t len = msg->len; + gi2c->xfer_len = len; if (!of_machine_is_compatible("lenovo,yoga-c630")) dma_buf = i2c_get_dma_safe_msg_buf(msg, 32); @@ -410,7 +414,7 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, writel_relaxed(len, se->base + SE_I2C_TX_TRANS_LEN); - if (dma_buf && geni_se_tx_dma_prep(se, dma_buf, len, &tx_dma)) { + if (dma_buf && geni_se_tx_dma_prep(se, dma_buf, len, &gi2c->tx_dma)) { geni_se_select_mode(se, GENI_SE_FIFO); i2c_put_dma_safe_msg_buf(dma_buf, msg, false); dma_buf = NULL; @@ -429,7 +433,8 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, if (dma_buf) { if (gi2c->err) geni_i2c_tx_fsm_rst(gi2c); - geni_se_tx_dma_unprep(se, tx_dma, len); + geni_se_tx_dma_unprep(se, gi2c->tx_dma, len); + gi2c->tx_dma = (dma_addr_t)NULL; i2c_put_dma_safe_msg_buf(dma_buf, msg, !gi2c->err); } @@ -479,6 +484,51 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, return ret; } +static void geni_i2c_stop_xfer(struct geni_i2c_dev *gi2c) +{ + int ret; + u32 dma; + u32 val; + u32 geni_status; + struct geni_se *se = &gi2c->se; + + ret = pm_runtime_get_sync(gi2c->se.dev); + if (ret < 0) { + dev_err(gi2c->se.dev, "Failed to resume device: %d\n", ret); + return; + } + + geni_status = readl_relaxed(gi2c->se.base + SE_GENI_STATUS); + if (geni_status & M_GENI_CMD_ACTIVE) { + geni_i2c_abort_xfer(gi2c); + dma = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN); + if (dma) { + val = readl_relaxed(gi2c->se.base + SE_DMA_DEBUG_REG0); + if (val & DMA_TX_ACTIVE) { + gi2c->cur_wr = 0; + if (gi2c->err) + geni_i2c_tx_fsm_rst(gi2c); + if (gi2c->tx_dma) { + geni_se_tx_dma_unprep(se, + gi2c->tx_dma, gi2c->xfer_len); + gi2c->tx_dma = (dma_addr_t)NULL; + } + } else if (val & DMA_RX_ACTIVE) { + gi2c->cur_rd = 0; + if (gi2c->err) + geni_i2c_rx_fsm_rst(gi2c); + if (gi2c->rx_dma) { + geni_se_rx_dma_unprep(se, + gi2c->rx_dma, gi2c->xfer_len); + gi2c->rx_dma = (dma_addr_t)NULL; + } + } + } + } + + pm_runtime_put_sync_suspend(gi2c->se.dev); +} + static u32 geni_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); @@ -630,6 +680,13 @@ static int geni_i2c_remove(struct platform_device *pdev) return 0; } +static void geni_i2c_shutdown(struct platform_device *pdev) +{ + struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev); + + geni_i2c_stop_xfer(gi2c); +} + static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev) { int ret; @@ -694,6 +751,7 @@ MODULE_DEVICE_TABLE(of, geni_i2c_dt_match); static struct platform_driver geni_i2c_driver = { .probe = geni_i2c_probe, .remove = geni_i2c_remove, + .shutdown = geni_i2c_shutdown, .driver = { .name = "geni_i2c", .pm = &geni_i2c_pm_ops, diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h index 8f385fbe5a0e..7279d8b3b04c 100644 --- a/include/linux/qcom-geni-se.h +++ b/include/linux/qcom-geni-se.h @@ -96,6 +96,7 @@ struct geni_se { #define SE_DMA_RX_FSM_RST 0xd58 #define SE_HW_PARAM_0 0xe24 #define SE_HW_PARAM_1 0xe28 +#define SE_DMA_DEBUG_REG0 0xe40 /* GENI_FORCE_DEFAULT_REG fields */ #define FORCE_DEFAULT BIT(0) @@ -226,6 +227,10 @@ struct geni_se { #define RX_GENI_CANCEL_IRQ BIT(11) #define RX_GENI_GP_IRQ_EXT GENMASK(13, 12) +/* SE_DMA_DEBUG_REG0 Register fields */ +#define DMA_TX_ACTIVE BIT(0) +#define DMA_RX_ACTIVE BIT(1) + /* SE_HW_PARAM_0 fields */ #define TX_FIFO_WIDTH_MSK GENMASK(29, 24) #define TX_FIFO_WIDTH_SHFT 24 -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation
WARNING: multiple messages have this Message-ID (diff)
From: Roja Rani Yarubandi <rojay@codeaurora.org> To: wsa@kernel.org Cc: linaro-mm-sig@lists.linaro.org, saiprakash.ranjan@codeaurora.org, rnayak@codeaurora.org, linux-media@vger.kernel.org, gregkh@linuxfoundation.org, linux-arm-msm@vger.kernel.org, Roja Rani Yarubandi <rojay@codeaurora.org>, dianders@chromium.org, dri-devel@lists.freedesktop.org, swboyd@chromium.org, akashast@codeaurora.org, mka@chromium.org, agross@kernel.org, msavaliy@qti.qualcomm.com, bjorn.andersson@linaro.org, skakit@codeaurora.org, linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org Subject: [PATCH V3] i2c: i2c-qcom-geni: Add shutdown callback for i2c Date: Mon, 7 Sep 2020 18:37:31 +0530 [thread overview] Message-ID: <20200907130731.2607-1-rojay@codeaurora.org> (raw) If the hardware is still accessing memory after SMMU translation is disabled (as part of smmu shutdown callback), then the IOVAs (I/O virtual address) which it was using will go on the bus as the physical addresses which will result in unknown crashes like NoC/interconnect errors. So, implement shutdown callback to i2c driver to stop on-going transfer and unmap DMA mappings during system "reboot" or "shutdown". Store DMA mapping data in geni_i2c_dev struct to enhance DMA mapping data scope. For example during shutdown callback to unmap DMA mapping, this stored DMA mapping data can be used to call geni_se_tx_dma_unprep and geni_se_rx_dma_unprep functions. Fixes: 37692de5d523 ("i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller") Signed-off-by: Roja Rani Yarubandi <rojay@codeaurora.org> --- Changes in V2: - As per Stephen's comments added seperate function for stop transfer, fixed minor nitpicks. - As per Stephen's comments, changed commit text. Changes in V3: - As per Stephen's comments, squashed patch 1 into patch 2, added Fixes tag. - As per Akash's comments, included FIFO case in stop_xfer, fixed minor nitpicks. drivers/i2c/busses/i2c-qcom-geni.c | 70 +++++++++++++++++++++++++++--- include/linux/qcom-geni-se.h | 5 +++ 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/drivers/i2c/busses/i2c-qcom-geni.c b/drivers/i2c/busses/i2c-qcom-geni.c index dead5db3315a..b3609760909f 100644 --- a/drivers/i2c/busses/i2c-qcom-geni.c +++ b/drivers/i2c/busses/i2c-qcom-geni.c @@ -86,6 +86,9 @@ struct geni_i2c_dev { u32 clk_freq_out; const struct geni_i2c_clk_fld *clk_fld; int suspended; + dma_addr_t tx_dma; + dma_addr_t rx_dma; + size_t xfer_len; }; struct geni_i2c_err_log { @@ -352,12 +355,12 @@ static void geni_i2c_tx_fsm_rst(struct geni_i2c_dev *gi2c) static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, u32 m_param) { - dma_addr_t rx_dma; unsigned long time_left; void *dma_buf = NULL; struct geni_se *se = &gi2c->se; size_t len = msg->len; + gi2c->xfer_len = len; if (!of_machine_is_compatible("lenovo,yoga-c630")) dma_buf = i2c_get_dma_safe_msg_buf(msg, 32); @@ -368,7 +371,7 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, writel_relaxed(len, se->base + SE_I2C_RX_TRANS_LEN); - if (dma_buf && geni_se_rx_dma_prep(se, dma_buf, len, &rx_dma)) { + if (dma_buf && geni_se_rx_dma_prep(se, dma_buf, len, &gi2c->rx_dma)) { geni_se_select_mode(se, GENI_SE_FIFO); i2c_put_dma_safe_msg_buf(dma_buf, msg, false); dma_buf = NULL; @@ -384,7 +387,8 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, if (dma_buf) { if (gi2c->err) geni_i2c_rx_fsm_rst(gi2c); - geni_se_rx_dma_unprep(se, rx_dma, len); + geni_se_rx_dma_unprep(se, gi2c->rx_dma, len); + gi2c->rx_dma = (dma_addr_t)NULL; i2c_put_dma_safe_msg_buf(dma_buf, msg, !gi2c->err); } @@ -394,12 +398,12 @@ static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, u32 m_param) { - dma_addr_t tx_dma; unsigned long time_left; void *dma_buf = NULL; struct geni_se *se = &gi2c->se; size_t len = msg->len; + gi2c->xfer_len = len; if (!of_machine_is_compatible("lenovo,yoga-c630")) dma_buf = i2c_get_dma_safe_msg_buf(msg, 32); @@ -410,7 +414,7 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, writel_relaxed(len, se->base + SE_I2C_TX_TRANS_LEN); - if (dma_buf && geni_se_tx_dma_prep(se, dma_buf, len, &tx_dma)) { + if (dma_buf && geni_se_tx_dma_prep(se, dma_buf, len, &gi2c->tx_dma)) { geni_se_select_mode(se, GENI_SE_FIFO); i2c_put_dma_safe_msg_buf(dma_buf, msg, false); dma_buf = NULL; @@ -429,7 +433,8 @@ static int geni_i2c_tx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg, if (dma_buf) { if (gi2c->err) geni_i2c_tx_fsm_rst(gi2c); - geni_se_tx_dma_unprep(se, tx_dma, len); + geni_se_tx_dma_unprep(se, gi2c->tx_dma, len); + gi2c->tx_dma = (dma_addr_t)NULL; i2c_put_dma_safe_msg_buf(dma_buf, msg, !gi2c->err); } @@ -479,6 +484,51 @@ static int geni_i2c_xfer(struct i2c_adapter *adap, return ret; } +static void geni_i2c_stop_xfer(struct geni_i2c_dev *gi2c) +{ + int ret; + u32 dma; + u32 val; + u32 geni_status; + struct geni_se *se = &gi2c->se; + + ret = pm_runtime_get_sync(gi2c->se.dev); + if (ret < 0) { + dev_err(gi2c->se.dev, "Failed to resume device: %d\n", ret); + return; + } + + geni_status = readl_relaxed(gi2c->se.base + SE_GENI_STATUS); + if (geni_status & M_GENI_CMD_ACTIVE) { + geni_i2c_abort_xfer(gi2c); + dma = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN); + if (dma) { + val = readl_relaxed(gi2c->se.base + SE_DMA_DEBUG_REG0); + if (val & DMA_TX_ACTIVE) { + gi2c->cur_wr = 0; + if (gi2c->err) + geni_i2c_tx_fsm_rst(gi2c); + if (gi2c->tx_dma) { + geni_se_tx_dma_unprep(se, + gi2c->tx_dma, gi2c->xfer_len); + gi2c->tx_dma = (dma_addr_t)NULL; + } + } else if (val & DMA_RX_ACTIVE) { + gi2c->cur_rd = 0; + if (gi2c->err) + geni_i2c_rx_fsm_rst(gi2c); + if (gi2c->rx_dma) { + geni_se_rx_dma_unprep(se, + gi2c->rx_dma, gi2c->xfer_len); + gi2c->rx_dma = (dma_addr_t)NULL; + } + } + } + } + + pm_runtime_put_sync_suspend(gi2c->se.dev); +} + static u32 geni_i2c_func(struct i2c_adapter *adap) { return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); @@ -630,6 +680,13 @@ static int geni_i2c_remove(struct platform_device *pdev) return 0; } +static void geni_i2c_shutdown(struct platform_device *pdev) +{ + struct geni_i2c_dev *gi2c = platform_get_drvdata(pdev); + + geni_i2c_stop_xfer(gi2c); +} + static int __maybe_unused geni_i2c_runtime_suspend(struct device *dev) { int ret; @@ -694,6 +751,7 @@ MODULE_DEVICE_TABLE(of, geni_i2c_dt_match); static struct platform_driver geni_i2c_driver = { .probe = geni_i2c_probe, .remove = geni_i2c_remove, + .shutdown = geni_i2c_shutdown, .driver = { .name = "geni_i2c", .pm = &geni_i2c_pm_ops, diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h index 8f385fbe5a0e..7279d8b3b04c 100644 --- a/include/linux/qcom-geni-se.h +++ b/include/linux/qcom-geni-se.h @@ -96,6 +96,7 @@ struct geni_se { #define SE_DMA_RX_FSM_RST 0xd58 #define SE_HW_PARAM_0 0xe24 #define SE_HW_PARAM_1 0xe28 +#define SE_DMA_DEBUG_REG0 0xe40 /* GENI_FORCE_DEFAULT_REG fields */ #define FORCE_DEFAULT BIT(0) @@ -226,6 +227,10 @@ struct geni_se { #define RX_GENI_CANCEL_IRQ BIT(11) #define RX_GENI_GP_IRQ_EXT GENMASK(13, 12) +/* SE_DMA_DEBUG_REG0 Register fields */ +#define DMA_TX_ACTIVE BIT(0) +#define DMA_RX_ACTIVE BIT(1) + /* SE_HW_PARAM_0 fields */ #define TX_FIFO_WIDTH_MSK GENMASK(29, 24) #define TX_FIFO_WIDTH_SHFT 24 -- QUALCOMM INDIA, on behalf of Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, hosted by The Linux Foundation _______________________________________________ dri-devel mailing list dri-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/dri-devel
next reply other threads:[~2020-09-07 13:09 UTC|newest] Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top 2020-09-07 13:07 Roja Rani Yarubandi [this message] 2020-09-07 13:07 ` [PATCH V3] i2c: i2c-qcom-geni: Add shutdown callback for i2c Roja Rani Yarubandi 2020-09-08 19:30 ` Stephen Boyd 2020-09-08 19:30 ` Stephen Boyd 2020-09-17 12:03 ` rojay
Reply instructions: You may reply publicly to this message via plain-text email using any one of the following methods: * Save the following mbox file, import it into your mail client, and reply-to-all from there: mbox Avoid top-posting and favor interleaved quoting: https://en.wikipedia.org/wiki/Posting_style#Interleaved_style * Reply using the --to, --cc, and --in-reply-to switches of git-send-email(1): git send-email \ --in-reply-to=20200907130731.2607-1-rojay@codeaurora.org \ --to=rojay@codeaurora.org \ --cc=agross@kernel.org \ --cc=akashast@codeaurora.org \ --cc=bjorn.andersson@linaro.org \ --cc=dianders@chromium.org \ --cc=dri-devel@lists.freedesktop.org \ --cc=gregkh@linuxfoundation.org \ --cc=linaro-mm-sig@lists.linaro.org \ --cc=linux-arm-msm@vger.kernel.org \ --cc=linux-i2c@vger.kernel.org \ --cc=linux-kernel@vger.kernel.org \ --cc=linux-media@vger.kernel.org \ --cc=mka@chromium.org \ --cc=msavaliy@qti.qualcomm.com \ --cc=rnayak@codeaurora.org \ --cc=saiprakash.ranjan@codeaurora.org \ --cc=skakit@codeaurora.org \ --cc=sumit.semwal@linaro.org \ --cc=swboyd@chromium.org \ --cc=wsa@kernel.org \ /path/to/YOUR_REPLY https://kernel.org/pub/software/scm/git/docs/git-send-email.html * If your mail client supports setting the In-Reply-To header via mailto: links, try the mailto: linkBe sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.