diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c index a665cd237a07..2b501f31e874 100644 --- a/drivers/i3c/master/renesas-i3c.c +++ b/drivers/i3c/master/renesas-i3c.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include "../internals.h" @@ -199,8 +200,6 @@ #define RENESAS_I3C_MAX_DEVS 8 #define I2C_INIT_MSG -1 -#define RENESAS_I3C_TCLK_IDX 1 - enum i3c_internal_state { I3C_INTERNAL_STATE_DISABLED, I3C_INTERNAL_STATE_CONTROLLER_IDLE, @@ -254,9 +253,10 @@ struct renesas_i3c_xferqueue { struct renesas_i3c { void __iomem *regs; - struct clk_bulk_data *clks; + struct clk *tclk; struct reset_control *presetn; struct reset_control *tresetn; + struct device *dev; struct renesas_i3c_xferqueue xferqueue; struct i3c_master_controller base; u8 addrs[RENESAS_I3C_MAX_DEVS]; @@ -269,7 +269,6 @@ struct renesas_i3c { u32 i3c_STDBR; u32 extbr; u16 maxdevs; - u8 num_clks; u8 refclk_div; }; @@ -449,7 +448,14 @@ static void renesas_i3c_irqs_mask_and_clear_locked(struct renesas_i3c *i3c) renesas_readl(i3c->regs, BST); } -static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer) +static void renesas_i3c_irqs_mask_and_clear(struct renesas_i3c *i3c) +{ + guard(spinlock_irqsave)(&i3c->xferqueue.lock); + + renesas_i3c_irqs_mask_and_clear_locked(i3c); +} + +static unsigned long renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xfer *xfer) { unsigned long time_left; @@ -458,6 +464,8 @@ static void renesas_i3c_wait_xfer(struct renesas_i3c *i3c, struct renesas_i3c_xf time_left = wait_for_completion_timeout(&xfer->comp, msecs_to_jiffies(1000)); if (!time_left) renesas_i3c_dequeue_xfer(i3c, xfer); + + return time_left; } static void renesas_i3c_set_prts(struct renesas_i3c *i3c, u32 val) @@ -491,6 +499,12 @@ static void renesas_i3c_bus_enable(struct i3c_master_controller *m, bool i3c_mod static int renesas_i3c_reset(struct renesas_i3c *i3c) { u32 val; + int ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; renesas_writel(i3c->regs, BCTL, 0); renesas_set_bit(i3c->regs, RSTCTL, RSTCTL_RI3CRST); @@ -562,7 +576,7 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m) int od_high_ticks, od_low_ticks, i2c_total_ticks; int ret; - i3c->rate = clk_get_rate(i3c->clks[RENESAS_I3C_TCLK_IDX].clk); + i3c->rate = clk_get_rate(i3c->tclk); if (!i3c->rate) return -EINVAL; @@ -633,6 +647,11 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m) if (ret) return ret; + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR); renesas_writel(i3c->regs, EXTBR, i3c->extbr); renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks)); @@ -655,6 +674,7 @@ static int renesas_i3c_daa(struct i3c_master_controller *m) { struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_cmd *cmd; + unsigned long time_left; u32 olddevs, newdevs; u8 last_addr = 0, pos; int ret; @@ -667,6 +687,11 @@ static int renesas_i3c_daa(struct i3c_master_controller *m) cmd = xfer->cmds; cmd->rx_count = 0; + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + /* Enable I3C bus. */ renesas_i3c_bus_enable(m, true); @@ -701,7 +726,9 @@ static int renesas_i3c_daa(struct i3c_master_controller *m) NCMDQP_CMD(I3C_CCC_ENTDAA) | NCMDQP_DEV_INDEX(ret) | NCMDQP_DEV_COUNT(i3c->maxdevs - ret) | NCMDQP_TOC; - renesas_i3c_wait_xfer(i3c, xfer); + time_left = renesas_i3c_wait_xfer(i3c, xfer); + if (!time_left) + renesas_i3c_irqs_mask_and_clear(i3c); if (cmd->rx_count >= i3c->maxdevs) newdevs = 0; @@ -767,6 +794,7 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m, { struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_cmd *cmd; + unsigned long time_left; int ret, pos = 0; if (ccc->id & I3C_CCC_DIRECT) { @@ -784,6 +812,11 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m, cmd->rnw = ccc->rnw; cmd->cmd0 = 0; + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + renesas_i3c_bus_enable(m, true); /* Calculate the command descriptor. */ @@ -818,7 +851,9 @@ static int renesas_i3c_send_ccc_cmd(struct i3c_master_controller *m, } } - renesas_i3c_wait_xfer(i3c, xfer); + time_left = renesas_i3c_wait_xfer(i3c, xfer); + if (!time_left) + renesas_i3c_irqs_mask_and_clear(i3c); ret = xfer->ret; if (ret) @@ -835,7 +870,9 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_ struct i3c_master_controller *m = i3c_dev_get_master(dev); struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); - int i; + unsigned long time_left; + bool xfer_failed = false; + int i, ret; struct renesas_i3c_xfer *xfer __free(kfree) = renesas_i3c_alloc_xfer(i3c, 1); if (!xfer) @@ -843,6 +880,11 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_ init_completion(&xfer->comp); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + /* Enable I3C bus. */ renesas_i3c_bus_enable(m, true); @@ -874,9 +916,14 @@ static int renesas_i3c_i3c_xfers(struct i3c_dev_desc *dev, struct i3c_xfer *i3c_ renesas_set_bit(i3c->regs, NTIE, NTIE_TDBEIE0); } - renesas_i3c_wait_xfer(i3c, xfer); + time_left = renesas_i3c_wait_xfer(i3c, xfer); + if (!time_left) + xfer_failed = true; } + if (xfer_failed) + renesas_i3c_irqs_mask_and_clear(i3c); + return 0; } @@ -885,12 +932,17 @@ static int renesas_i3c_attach_i3c_dev(struct i3c_dev_desc *dev) struct i3c_master_controller *m = i3c_dev_get_master(dev); struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_i2c_dev_data *data; - int pos; + int pos, ret; pos = renesas_i3c_get_free_pos(i3c); if (pos < 0) return pos; + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + data = kzalloc_obj(*data); if (!data) return -ENOMEM; @@ -912,7 +964,12 @@ static int renesas_i3c_reattach_i3c_dev(struct i3c_dev_desc *dev, struct i3c_master_controller *m = i3c_dev_get_master(dev); struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); - int pos; + int pos, ret; + + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; pos = renesas_i3c_get_free_pos(i3c); @@ -940,8 +997,12 @@ static void renesas_i3c_detach_i3c_dev(struct i3c_dev_desc *dev) struct renesas_i3c_i2c_dev_data *data = i3c_dev_get_master_data(dev); struct i3c_master_controller *m = i3c_dev_get_master(dev); struct renesas_i3c *i3c = to_renesas_i3c(m); + int ret; - renesas_writel(i3c->regs, DATBAS(data->index), 0); + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (!ret) + renesas_writel(i3c->regs, DATBAS(data->index), 0); i3c_dev_set_master_data(dev, NULL); i3c->addrs[data->index] = 0; @@ -957,7 +1018,9 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev, struct renesas_i3c *i3c = to_renesas_i3c(m); struct renesas_i3c_cmd *cmd; u8 start_bit = CNDCTL_STCND; - int i; + unsigned long time_left; + bool xfer_failed = false; + int i, ret; if (!i2c_nxfers) return 0; @@ -970,6 +1033,11 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev, xfer->is_i2c_xfer = true; cmd = xfer->cmds; + PM_RUNTIME_ACQUIRE_IF_ENABLED_AUTOSUSPEND(i3c->dev, pm); + ret = PM_RUNTIME_ACQUIRE_ERR(&pm); + if (ret) + return ret; + renesas_i3c_bus_enable(m, false); if (!(renesas_readl(i3c->regs, BCST) & BCST_BFREF)) { @@ -996,7 +1064,9 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev, renesas_set_bit(i3c->regs, NTSTE, NTSTE_TDBEE0); - wait_for_completion_timeout(&xfer->comp, m->i2c.timeout); + time_left = wait_for_completion_timeout(&xfer->comp, m->i2c.timeout); + if (!time_left) + xfer_failed = true; if (cmd->err) break; @@ -1005,6 +1075,10 @@ static int renesas_i3c_i2c_xfers(struct i2c_dev_desc *dev, } renesas_i3c_dequeue_xfer(i3c, xfer); + + if (xfer_failed) + renesas_i3c_irqs_mask_and_clear(i3c); + return cmd->err; } @@ -1392,12 +1466,16 @@ static int renesas_i3c_probe(struct platform_device *pdev) if (IS_ERR(i3c->regs)) return PTR_ERR(i3c->regs); - ret = devm_clk_bulk_get_all_enabled(&pdev->dev, &i3c->clks); - if (ret <= RENESAS_I3C_TCLK_IDX) - return dev_err_probe(&pdev->dev, ret < 0 ? ret : -EINVAL, - "Failed to get clocks (need > %d, got %d)\n", - RENESAS_I3C_TCLK_IDX, ret); - i3c->num_clks = ret; + i3c->tclk = devm_clk_get(&pdev->dev, "tclk"); + if (IS_ERR(i3c->tclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(i3c->tclk), "Failed to get tclk"); + + i3c->dev = &pdev->dev; + pm_runtime_set_autosuspend_delay(&pdev->dev, 300); + pm_runtime_use_autosuspend(&pdev->dev); + ret = devm_pm_runtime_enable(&pdev->dev); + if (ret) + return ret; i3c->tresetn = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "tresetn"); if (IS_ERR(i3c->tresetn)) @@ -1464,8 +1542,6 @@ static int renesas_i3c_suspend(struct device *dev) if (ret) goto err_mark_resumed; - clk_bulk_disable(i3c->num_clks, i3c->clks); - return 0; err_mark_resumed: @@ -1487,13 +1563,13 @@ static int renesas_i3c_resume(struct device *dev) if (ret) return ret; - ret = clk_bulk_enable(i3c->num_clks, i3c->clks); + ret = renesas_i3c_reset(i3c); if (ret) goto err_resets_asserted; - ret = renesas_i3c_reset(i3c); + ret = pm_runtime_resume_and_get(dev); if (ret) - goto err_clks_disable; + goto err_resets_asserted; /* Re-store I3C registers value. */ renesas_writel(i3c->regs, STDBR, i3c->i3c_STDBR); @@ -1512,15 +1588,23 @@ static int renesas_i3c_resume(struct device *dev) i2c_mark_adapter_resumed(&i3c->base.i2c); + pm_runtime_put_autosuspend(dev); + /* * I3C devices may have retained their dynamic address anyway. Do not * fail the resume because of DAA error. */ return 0; -err_clks_disable: - clk_bulk_disable(i3c->num_clks, i3c->clks); err_resets_asserted: + /* + * If this happens, there is no way to recover from this state without + * reloading the driver. We want to avoid keeping the reset line + * deasserted unnecessarily. The runtime paths will still work correctly + * even if the IP registers are accessed while reset is asserted (e.g. + * if a runtime path is triggered after a failed resume). Checked on + * RZ/G3S. + */ reset_control_bulk_assert(ARRAY_SIZE(resets), resets); return ret; }