diff options
Diffstat (limited to 'drivers/i3c/master/renesas-i3c.c')
| -rw-r--r-- | drivers/i3c/master/renesas-i3c.c | 259 |
1 files changed, 170 insertions, 89 deletions
diff --git a/drivers/i3c/master/renesas-i3c.c b/drivers/i3c/master/renesas-i3c.c index 426a418f29b6..528bccc2c68e 100644 --- a/drivers/i3c/master/renesas-i3c.c +++ b/drivers/i3c/master/renesas-i3c.c @@ -198,6 +198,8 @@ #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,12 +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; - struct clk *tclk; + 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 { @@ -272,10 +281,6 @@ struct renesas_i3c_irq_desc { const char *desc; }; -struct renesas_i3c_config { - unsigned int has_pclkrw:1; -}; - static inline void renesas_i3c_reg_update(void __iomem *reg, u32 mask, u32 val) { u32 data = readl(reg); @@ -477,28 +482,79 @@ static int renesas_i3c_reset(struct renesas_i3c *i3c) 0, 1000, false, i3c->regs, RSTCTL); } +static void renesas_i3c_hw_init(struct renesas_i3c *i3c) +{ + u32 val; + + /* Disable Slave Mode */ + renesas_writel(i3c->regs, SVCTL, 0); + + /* Initialize Queue/Buffer threshold */ + renesas_writel(i3c->regs, NQTHCTL, NQTHCTL_IBIDSSZ(6) | + NQTHCTL_CMDQTH(1)); + + /* The only supported configuration is two entries*/ + renesas_writel(i3c->regs, NTBTHCTL0, 0); + /* Interrupt when there is one entry in the queue */ + renesas_writel(i3c->regs, NRQTHCTL, 0); + + /* Enable all Bus/Transfer Status Flags */ + renesas_writel(i3c->regs, BSTE, BSTE_ALL_FLAG); + renesas_writel(i3c->regs, NTSTE, NTSTE_ALL_FLAG); + + /* Interrupt enable settings */ + renesas_writel(i3c->regs, BIE, BIE_NACKDIE | BIE_TENDIE); + renesas_writel(i3c->regs, NTIE, 0); + + /* Clear Status register */ + renesas_writel(i3c->regs, NTST, 0); + renesas_writel(i3c->regs, INST, 0); + renesas_writel(i3c->regs, BST, 0); + + /* Hot-Join Acknowlege setting. */ + renesas_set_bit(i3c->regs, BCTL, BCTL_HJACKCTL); + + renesas_writel(i3c->regs, IBINCTL, IBINCTL_NRHJCTL | IBINCTL_NRMRCTL | + IBINCTL_NRSIRCTL); + + renesas_writel(i3c->regs, SCSTLCTL, 0); + renesas_set_bit(i3c->regs, SCSTRCTL, SCSTRCTL_ACKTWE); + + /* Bus condition timing */ + val = DIV_ROUND_UP(I3C_BUS_TBUF_MIXED_FM_MIN_NS, + NSEC_PER_SEC / i3c->rate); + renesas_writel(i3c->regs, BFRECDT, BFRECDT_FRECYC(val)); + + val = DIV_ROUND_UP(I3C_BUS_TAVAL_MIN_NS, + NSEC_PER_SEC / i3c->rate); + renesas_writel(i3c->regs, BAVLCDT, BAVLCDT_AVLCYC(val)); + + val = DIV_ROUND_UP(I3C_BUS_TIDLE_MIN_NS, + NSEC_PER_SEC / i3c->rate); + renesas_writel(i3c->regs, BIDLCDT, BIDLCDT_IDLCYC(val)); +} + static int renesas_i3c_bus_init(struct i3c_master_controller *m) { struct renesas_i3c *i3c = to_renesas_i3c(m); struct i3c_bus *bus = i3c_master_get_bus(m); struct i3c_device_info info = {}; struct i2c_timings t; - unsigned long rate; - u32 double_SBR, val; + u32 double_SBR; int cks, pp_high_ticks, pp_low_ticks, i3c_total_ticks; int od_high_ticks, od_low_ticks, i2c_total_ticks; int ret; - rate = clk_get_rate(i3c->tclk); - if (!rate) + i3c->rate = clk_get_rate(i3c->clks[RENESAS_I3C_TCLK_IDX].clk); + if (!i3c->rate) return -EINVAL; ret = renesas_i3c_reset(i3c); if (ret) return ret; - i2c_total_ticks = DIV_ROUND_UP(rate, bus->scl_rate.i2c); - i3c_total_ticks = DIV_ROUND_UP(rate, bus->scl_rate.i3c); + i2c_total_ticks = DIV_ROUND_UP(i3c->rate, bus->scl_rate.i2c); + i3c_total_ticks = DIV_ROUND_UP(i3c->rate, bus->scl_rate.i3c); i2c_parse_fw_timings(&m->dev, &t, true); @@ -511,7 +567,7 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m) pp_high_ticks = ((i3c_total_ticks * 5) / 10); else pp_high_ticks = DIV_ROUND_UP(I3C_BUS_THIGH_MIXED_MAX_NS, - NSEC_PER_SEC / rate); + NSEC_PER_SEC / i3c->rate); pp_low_ticks = i3c_total_ticks - pp_high_ticks; if ((od_low_ticks / 2) <= 0xFF && pp_low_ticks < 0x3F) @@ -519,7 +575,7 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m) i2c_total_ticks /= 2; i3c_total_ticks /= 2; - rate /= 2; + i3c->rate /= 2; } /* SCL clock period calculation in Open-drain mode */ @@ -540,8 +596,8 @@ static int renesas_i3c_bus_init(struct i3c_master_controller *m) STDBR_SBRLP(pp_low_ticks) | STDBR_SBRHP(pp_high_ticks); - od_low_ticks -= t.scl_fall_ns / (NSEC_PER_SEC / rate) + 1; - od_high_ticks -= t.scl_rise_ns / (NSEC_PER_SEC / rate) + 1; + od_low_ticks -= t.scl_fall_ns / (NSEC_PER_SEC / i3c->rate) + 1; + od_high_ticks -= t.scl_rise_ns / (NSEC_PER_SEC / i3c->rate) + 1; i3c->i2c_STDBR = (double_SBR ? STDBR_DSBRPO : 0) | STDBR_SBRLO(double_SBR, od_low_ticks) | STDBR_SBRHO(double_SBR, od_high_ticks) | @@ -556,55 +612,16 @@ 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; - /* Disable Slave Mode */ - renesas_writel(i3c->regs, SVCTL, 0); - - /* Initialize Queue/Buffer threshold */ - renesas_writel(i3c->regs, NQTHCTL, NQTHCTL_IBIDSSZ(6) | - NQTHCTL_CMDQTH(1)); - - /* The only supported configuration is two entries*/ - renesas_writel(i3c->regs, NTBTHCTL0, 0); - /* Interrupt when there is one entry in the queue */ - renesas_writel(i3c->regs, NRQTHCTL, 0); - - /* Enable all Bus/Transfer Status Flags */ - renesas_writel(i3c->regs, BSTE, BSTE_ALL_FLAG); - renesas_writel(i3c->regs, NTSTE, NTSTE_ALL_FLAG); - - /* Interrupt enable settings */ - renesas_writel(i3c->regs, BIE, BIE_NACKDIE | BIE_TENDIE); - renesas_writel(i3c->regs, NTIE, 0); - - /* Clear Status register */ - renesas_writel(i3c->regs, NTST, 0); - renesas_writel(i3c->regs, INST, 0); - renesas_writel(i3c->regs, BST, 0); - - /* Hot-Join Acknowlege setting. */ - renesas_set_bit(i3c->regs, BCTL, BCTL_HJACKCTL); - - renesas_writel(i3c->regs, IBINCTL, IBINCTL_NRHJCTL | IBINCTL_NRMRCTL | - IBINCTL_NRSIRCTL); - - renesas_writel(i3c->regs, SCSTLCTL, 0); - renesas_set_bit(i3c->regs, SCSTRCTL, SCSTRCTL_ACKTWE); - - /* Bus condition timing */ - val = DIV_ROUND_UP(I3C_BUS_TBUF_MIXED_FM_MIN_NS, NSEC_PER_SEC / rate); - renesas_writel(i3c->regs, BFRECDT, BFRECDT_FRECYC(val)); - - val = DIV_ROUND_UP(I3C_BUS_TAVAL_MIN_NS, NSEC_PER_SEC / rate); - renesas_writel(i3c->regs, BAVLCDT, BAVLCDT_AVLCYC(val)); - - val = DIV_ROUND_UP(I3C_BUS_TIDLE_MIN_NS, NSEC_PER_SEC / rate); - renesas_writel(i3c->regs, BIDLCDT, BIDLCDT_IDLCYC(val)); + /* I3C hw init*/ + renesas_i3c_hw_init(i3c); ret = i3c_master_get_free_addr(m, 0); if (ret < 0) return ret; + i3c->dyn_addr = ret; renesas_writel(i3c->regs, MSDVAD, MSDVAD_MDYAD(ret) | MSDVAD_MDYADV); memset(&info, 0, sizeof(info)); @@ -1301,14 +1318,8 @@ static const struct renesas_i3c_irq_desc renesas_i3c_irqs[] = { static int renesas_i3c_probe(struct platform_device *pdev) { struct renesas_i3c *i3c; - struct reset_control *reset; - struct clk *clk; - const struct renesas_i3c_config *config = of_device_get_match_data(&pdev->dev); int ret, i; - if (!config) - return -ENODATA; - i3c = devm_kzalloc(&pdev->dev, sizeof(*i3c), GFP_KERNEL); if (!i3c) return -ENOMEM; @@ -1317,28 +1328,21 @@ static int renesas_i3c_probe(struct platform_device *pdev) if (IS_ERR(i3c->regs)) return PTR_ERR(i3c->regs); - clk = devm_clk_get_enabled(&pdev->dev, "pclk"); - if (IS_ERR(clk)) - return PTR_ERR(clk); + 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; - if (config->has_pclkrw) { - clk = devm_clk_get_enabled(&pdev->dev, "pclkrw"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - } - - i3c->tclk = devm_clk_get_enabled(&pdev->dev, "tclk"); - if (IS_ERR(i3c->tclk)) - return PTR_ERR(i3c->tclk); - - reset = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "tresetn"); - if (IS_ERR(reset)) - return dev_err_probe(&pdev->dev, PTR_ERR(reset), + i3c->tresetn = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "tresetn"); + if (IS_ERR(i3c->tresetn)) + return dev_err_probe(&pdev->dev, PTR_ERR(i3c->tresetn), "Error: missing tresetn ctrl\n"); - reset = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "presetn"); - if (IS_ERR(reset)) - return dev_err_probe(&pdev->dev, PTR_ERR(reset), + i3c->presetn = devm_reset_control_get_optional_exclusive_deasserted(&pdev->dev, "presetn"); + if (IS_ERR(i3c->presetn)) + return dev_err_probe(&pdev->dev, PTR_ERR(i3c->presetn), "Error: missing presetn ctrl\n"); spin_lock_init(&i3c->xferqueue.lock); @@ -1364,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); } @@ -1374,16 +1384,86 @@ static void renesas_i3c_remove(struct platform_device *pdev) i3c_master_unregister(&i3c->base); } -static const struct renesas_i3c_config empty_i3c_config = { -}; +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 renesas_i3c_config r9a09g047_i3c_config = { - .has_pclkrw = 1, +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", .data = &empty_i3c_config }, - { .compatible = "renesas,r9a09g047-i3c", .data = &r9a09g047_i3c_config }, + { .compatible = "renesas,r9a08g045-i3c" }, + { .compatible = "renesas,r9a09g047-i3c" }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, renesas_i3c_of_ids); @@ -1394,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); |
