summaryrefslogtreecommitdiff
path: root/drivers/i3c/master/renesas-i3c.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i3c/master/renesas-i3c.c')
-rw-r--r--drivers/i3c/master/renesas-i3c.c259
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);