diff options
| -rw-r--r-- | drivers/i3c/master/renesas-i3c.c | 89 |
1 files changed, 89 insertions, 0 deletions
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c index 008b6ee60e26..528bccc2c68e 100644 --- a/drivers/i3c/master/renesas-i3c.c +++ b/drivers/i3c/master/renesas-i3c.c @@ -256,16 +256,19 @@ struct renesas_i3c { enum i3c_internal_state internal_state; u16 maxdevs; u32 free_pos; + u32 dyn_addr; u32 i2c_STDBR; u32 i3c_STDBR; unsigned long rate; u8 addrs[RENESAS_I3C_MAX_DEVS]; struct renesas_i3c_xferqueue xferqueue; void __iomem *regs; + u32 *DATBASn; struct clk_bulk_data *clks; struct reset_control *presetn; struct reset_control *tresetn; u8 num_clks; + u8 refclk_div; }; struct renesas_i3c_i2c_dev_data { @@ -609,6 +612,7 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m) EXTBR_EBRHP(pp_high_ticks)); renesas_writel(i3c->regs, REFCKCTL, REFCKCTL_IREFCKS(cks)); + i3c->refclk_div = cks; /* I3C hw init*/ renesas_i3c_hw_init(i3c); @@ -617,6 +621,7 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m) if (ret < 0) return ret; + i3c->dyn_addr = ret; renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(ret) | MSDVAD_MDYADV); memset(&info, 0, sizeof(info)); @@ -1363,6 +1368,12 @@ static int renesas_i3c_probe(struct platform_device *pdev) i3c->maxdevs = RENESAS_I3C_MAX_DEVS; i3c->free_pos = GENMASK(i3c->maxdevs - 1, 0); + /* Allocate dynamic Device Address Table backup. */ + i3c->DATBASn = devm_kzalloc(&pdev->dev, sizeof(u32) * i3c->maxdevs, + GFP_KERNEL); + if (!i3c->DATBASn) + return -ENOMEM; + return i3c_master_register(&i3c->base, &pdev->dev, &renesas_i3c_ops, false); } @@ -1373,6 +1384,83 @@ static void renesas_i3c_remove(struct platform_device *pdev) i3c_master_unregister(&i3c->base); } +static int renesas_i3c_suspend_noirq(struct device *dev) +{ + struct renesas_i3c *i3c = dev_get_drvdata(dev); + int i, ret; + + i2c_mark_adapter_suspended(&i3c->base.i2c); + + /* Store Device Address Table values. */ + for (i = 0; i < i3c->maxdevs; i++) + i3c->DATBASn[i] = renesas_readl(i3c->regs, DATBAS(i)); + + ret = reset_control_assert(i3c->presetn); + if (ret) + goto err_mark_resumed; + + ret = reset_control_assert(i3c->tresetn); + if (ret) + goto err_presetn; + + clk_bulk_disable(i3c->num_clks, i3c->clks); + + return 0; + +err_presetn: + reset_control_deassert(i3c->presetn); +err_mark_resumed: + i2c_mark_adapter_resumed(&i3c->base.i2c); + + return ret; +} + +static int renesas_i3c_resume_noirq(struct device *dev) +{ + struct renesas_i3c *i3c = dev_get_drvdata(dev); + int i, ret; + + ret = reset_control_deassert(i3c->presetn); + if (ret) + return ret; + + ret = reset_control_deassert(i3c->tresetn); + if (ret) + goto err_presetn; + + ret = clk_bulk_enable(i3c->num_clks, i3c->clks); + if (ret) + goto err_tresetn; + + /* Re-store I3C registers value. */ + renesas_writel(i3c->regs, REFCKCTL, + REFCKCTL_IREFCKS(i3c->refclk_div)); + renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYADV | + MSDVAD_MDYAD(i3c->dyn_addr)); + + /* Restore Device Address Table values. */ + for (i = 0; i < i3c->maxdevs; i++) + renesas_writel(i3c->regs, DATBAS(i), i3c->DATBASn[i]); + + /* I3C hw init. */ + renesas_i3c_hw_init(i3c); + + i2c_mark_adapter_resumed(&i3c->base.i2c); + + return 0; + +err_tresetn: + reset_control_assert(i3c->tresetn); +err_presetn: + reset_control_assert(i3c->presetn); + return ret; +} + +static const struct dev_pm_ops renesas_i3c_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(renesas_i3c_suspend_noirq, + renesas_i3c_resume_noirq) +}; + static const struct of_device_id renesas_i3c_of_ids[] = { { .compatible = "renesas,r9a08g045-i3c" }, { .compatible = "renesas,r9a09g047-i3c" }, @@ -1386,6 +1474,7 @@ static struct platform_driver renesas_i3c = { .driver = { .name = "renesas-i3c", .of_match_table = renesas_i3c_of_ids, + .pm = pm_sleep_ptr(&renesas_i3c_pm_ops), }, }; module_platform_driver(renesas_i3c); |
