211 lines
5.6 KiB
Diff
211 lines
5.6 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Samuel Holland <samuel@sholland.org>
|
|
Date: Mon, 30 Dec 2019 22:36:04 -0600
|
|
Subject: [PATCH] bus: sunxi-rsb: Split out controller init/exit functions
|
|
|
|
This separates the resource acquisition from the hardware initialization
|
|
phase, so the hardware initialization can be repeated after system
|
|
suspend/resume. The same is done for the exit/remove function, except
|
|
that there is no resource deallocation phase due to the use of devres.
|
|
|
|
The requested RSB clock frequency is stored in `struct sunxi_rsb` so it
|
|
will be available when reinitializing the hardware.
|
|
|
|
Signed-off-by: Samuel Holland <samuel@sholland.org>
|
|
---
|
|
drivers/bus/sunxi-rsb.c | 127 ++++++++++++++++++++++------------------
|
|
1 file changed, 71 insertions(+), 56 deletions(-)
|
|
|
|
--- a/drivers/bus/sunxi-rsb.c
|
|
+++ b/drivers/bus/sunxi-rsb.c
|
|
@@ -126,6 +126,7 @@ struct sunxi_rsb {
|
|
struct completion complete;
|
|
struct mutex lock;
|
|
unsigned int status;
|
|
+ u32 clk_freq;
|
|
};
|
|
|
|
/* bus / slave device related functions */
|
|
@@ -614,16 +615,74 @@ static int of_rsb_register_devices(struc
|
|
return 0;
|
|
}
|
|
|
|
+static int sunxi_rsb_hw_init(struct sunxi_rsb *rsb)
|
|
+{
|
|
+ struct device *dev = rsb->dev;
|
|
+ unsigned long p_clk_freq;
|
|
+ u32 clk_delay, reg;
|
|
+ int clk_div, ret;
|
|
+
|
|
+ ret = clk_prepare_enable(rsb->clk);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "failed to enable clk: %d\n", ret);
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+ ret = reset_control_deassert(rsb->rstc);
|
|
+ if (ret) {
|
|
+ dev_err(dev, "failed to deassert reset line: %d\n", ret);
|
|
+ goto err_clk_disable;
|
|
+ }
|
|
+
|
|
+ /* reset the controller */
|
|
+ writel(RSB_CTRL_SOFT_RST, rsb->regs + RSB_CTRL);
|
|
+ readl_poll_timeout(rsb->regs + RSB_CTRL, reg,
|
|
+ !(reg & RSB_CTRL_SOFT_RST), 1000, 100000);
|
|
+
|
|
+ /*
|
|
+ * Clock frequency and delay calculation code is from
|
|
+ * Allwinner U-boot sources.
|
|
+ *
|
|
+ * From A83 user manual:
|
|
+ * bus clock frequency = parent clock frequency / (2 * (divider + 1))
|
|
+ */
|
|
+ p_clk_freq = clk_get_rate(rsb->clk);
|
|
+ clk_div = p_clk_freq / rsb->clk_freq / 2;
|
|
+ if (!clk_div)
|
|
+ clk_div = 1;
|
|
+ else if (clk_div > RSB_CCR_MAX_CLK_DIV + 1)
|
|
+ clk_div = RSB_CCR_MAX_CLK_DIV + 1;
|
|
+
|
|
+ clk_delay = clk_div >> 1;
|
|
+ if (!clk_delay)
|
|
+ clk_delay = 1;
|
|
+
|
|
+ dev_info(dev, "RSB running at %lu Hz\n", p_clk_freq / clk_div / 2);
|
|
+ writel(RSB_CCR_SDA_OUT_DELAY(clk_delay) | RSB_CCR_CLK_DIV(clk_div - 1),
|
|
+ rsb->regs + RSB_CCR);
|
|
+
|
|
+ return 0;
|
|
+
|
|
+err_clk_disable:
|
|
+ clk_disable_unprepare(rsb->clk);
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static void sunxi_rsb_hw_exit(struct sunxi_rsb *rsb)
|
|
+{
|
|
+ reset_control_assert(rsb->rstc);
|
|
+ clk_disable_unprepare(rsb->clk);
|
|
+}
|
|
+
|
|
static int sunxi_rsb_probe(struct platform_device *pdev)
|
|
{
|
|
struct device *dev = &pdev->dev;
|
|
struct device_node *np = dev->of_node;
|
|
struct resource *r;
|
|
struct sunxi_rsb *rsb;
|
|
- unsigned long p_clk_freq;
|
|
- u32 clk_delay, clk_freq = 3000000;
|
|
- int clk_div, irq, ret;
|
|
- u32 reg;
|
|
+ u32 clk_freq = 3000000;
|
|
+ int irq, ret;
|
|
|
|
of_property_read_u32(np, "clock-frequency", &clk_freq);
|
|
if (clk_freq > RSB_MAX_FREQ) {
|
|
@@ -638,6 +697,7 @@ static int sunxi_rsb_probe(struct platfo
|
|
return -ENOMEM;
|
|
|
|
rsb->dev = dev;
|
|
+ rsb->clk_freq = clk_freq;
|
|
platform_set_drvdata(pdev, rsb);
|
|
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
|
rsb->regs = devm_ioremap_resource(dev, r);
|
|
@@ -655,63 +715,27 @@ static int sunxi_rsb_probe(struct platfo
|
|
return ret;
|
|
}
|
|
|
|
- ret = clk_prepare_enable(rsb->clk);
|
|
- if (ret) {
|
|
- dev_err(dev, "failed to enable clk: %d\n", ret);
|
|
- return ret;
|
|
- }
|
|
-
|
|
- p_clk_freq = clk_get_rate(rsb->clk);
|
|
-
|
|
rsb->rstc = devm_reset_control_get(dev, NULL);
|
|
if (IS_ERR(rsb->rstc)) {
|
|
ret = PTR_ERR(rsb->rstc);
|
|
dev_err(dev, "failed to retrieve reset controller: %d\n", ret);
|
|
- goto err_clk_disable;
|
|
- }
|
|
-
|
|
- ret = reset_control_deassert(rsb->rstc);
|
|
- if (ret) {
|
|
- dev_err(dev, "failed to deassert reset line: %d\n", ret);
|
|
- goto err_clk_disable;
|
|
+ return ret;
|
|
}
|
|
|
|
init_completion(&rsb->complete);
|
|
mutex_init(&rsb->lock);
|
|
|
|
- /* reset the controller */
|
|
- writel(RSB_CTRL_SOFT_RST, rsb->regs + RSB_CTRL);
|
|
- readl_poll_timeout(rsb->regs + RSB_CTRL, reg,
|
|
- !(reg & RSB_CTRL_SOFT_RST), 1000, 100000);
|
|
-
|
|
- /*
|
|
- * Clock frequency and delay calculation code is from
|
|
- * Allwinner U-boot sources.
|
|
- *
|
|
- * From A83 user manual:
|
|
- * bus clock frequency = parent clock frequency / (2 * (divider + 1))
|
|
- */
|
|
- clk_div = p_clk_freq / clk_freq / 2;
|
|
- if (!clk_div)
|
|
- clk_div = 1;
|
|
- else if (clk_div > RSB_CCR_MAX_CLK_DIV + 1)
|
|
- clk_div = RSB_CCR_MAX_CLK_DIV + 1;
|
|
-
|
|
- clk_delay = clk_div >> 1;
|
|
- if (!clk_delay)
|
|
- clk_delay = 1;
|
|
-
|
|
- dev_info(dev, "RSB running at %lu Hz\n", p_clk_freq / clk_div / 2);
|
|
- writel(RSB_CCR_SDA_OUT_DELAY(clk_delay) | RSB_CCR_CLK_DIV(clk_div - 1),
|
|
- rsb->regs + RSB_CCR);
|
|
-
|
|
ret = devm_request_irq(dev, irq, sunxi_rsb_irq, 0, RSB_CTRL_NAME, rsb);
|
|
if (ret) {
|
|
dev_err(dev, "can't register interrupt handler irq %d: %d\n",
|
|
irq, ret);
|
|
- goto err_reset_assert;
|
|
+ return ret;
|
|
}
|
|
|
|
+ ret = sunxi_rsb_hw_init(rsb);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+
|
|
/* initialize all devices on the bus into RSB mode */
|
|
ret = sunxi_rsb_init_device_mode(rsb);
|
|
if (ret)
|
|
@@ -720,14 +744,6 @@ static int sunxi_rsb_probe(struct platfo
|
|
of_rsb_register_devices(rsb);
|
|
|
|
return 0;
|
|
-
|
|
-err_reset_assert:
|
|
- reset_control_assert(rsb->rstc);
|
|
-
|
|
-err_clk_disable:
|
|
- clk_disable_unprepare(rsb->clk);
|
|
-
|
|
- return ret;
|
|
}
|
|
|
|
static int sunxi_rsb_remove(struct platform_device *pdev)
|
|
@@ -735,8 +751,7 @@ static int sunxi_rsb_remove(struct platf
|
|
struct sunxi_rsb *rsb = platform_get_drvdata(pdev);
|
|
|
|
device_for_each_child(rsb->dev, NULL, sunxi_rsb_remove_devices);
|
|
- reset_control_assert(rsb->rstc);
|
|
- clk_disable_unprepare(rsb->clk);
|
|
+ sunxi_rsb_hw_exit(rsb);
|
|
|
|
return 0;
|
|
}
|