i2c: i2c-qcom-geni: Add shutdown callback for i2c
authorRoja Rani Yarubandi <rojay@codeaurora.org>
Fri, 8 Jan 2021 15:05:45 +0000 (20:35 +0530)
committerWolfram Sang <wsa@kernel.org>
Mon, 1 Feb 2021 22:09:34 +0000 (23:09 +0100)
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".

Fixes: 37692de5d523 ("i2c: i2c-qcom-geni: Add bus driver for the Qualcomm GENI I2C controller")
Signed-off-by: Roja Rani Yarubandi <rojay@codeaurora.org>
Reviewed-by: Akash Asthana <akashast@codeaurora.org>
Signed-off-by: Wolfram Sang <wsa@kernel.org>
drivers/i2c/busses/i2c-qcom-geni.c

index 214b4c9..c3f5847 100644 (file)
@@ -375,6 +375,32 @@ static void geni_i2c_tx_msg_cleanup(struct geni_i2c_dev *gi2c,
        }
 }
 
+static void geni_i2c_stop_xfer(struct geni_i2c_dev *gi2c)
+{
+       int ret;
+       u32 geni_status;
+       struct i2c_msg *cur;
+
+       /* Resume device, as runtime suspend can happen anytime during transfer */
+       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) {
+               cur = gi2c->cur;
+               geni_i2c_abort_xfer(gi2c);
+               if (cur->flags & I2C_M_RD)
+                       geni_i2c_rx_msg_cleanup(gi2c, cur);
+               else
+                       geni_i2c_tx_msg_cleanup(gi2c, cur);
+       }
+
+       pm_runtime_put_sync_suspend(gi2c->se.dev);
+}
+
 static int geni_i2c_rx_one_msg(struct geni_i2c_dev *gi2c, struct i2c_msg *msg,
                                u32 m_param)
 {
@@ -650,6 +676,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;
@@ -714,6 +747,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,