222 lines
6.0 KiB
Diff
222 lines
6.0 KiB
Diff
|
From 42e53668b933d08e893d9f76bdf0baed4f492d5c Mon Sep 17 00:00:00 2001
|
||
|
From: Samuel Holland <samuel@sholland.org>
|
||
|
Date: Mon, 30 Dec 2019 22:36:04 -0600
|
||
|
Subject: [PATCH 154/351] 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 call to reset_control_deassert() is replaced with a call to
|
||
|
reset_control_reset() to ensure the hardware is fully reinitialized,
|
||
|
regardless of the state firmware left it in.
|
||
|
|
||
|
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 | 129 +++++++++++++++++++++++-----------------
|
||
|
1 file changed, 73 insertions(+), 56 deletions(-)
|
||
|
|
||
|
diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c
|
||
|
index e67be95c0660..1795ad7f0bfe 100644
|
||
|
--- 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 */
|
||
|
@@ -585,15 +586,75 @@ static int of_rsb_register_devices(struct sunxi_rsb *rsb)
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
+static int sunxi_rsb_init_controller(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_reset(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 int sunxi_rsb_exit_controller(struct sunxi_rsb *rsb)
|
||
|
+{
|
||
|
+ reset_control_assert(rsb->rstc);
|
||
|
+ clk_disable_unprepare(rsb->clk);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
static int sunxi_rsb_probe(struct platform_device *pdev)
|
||
|
{
|
||
|
struct device *dev = &pdev->dev;
|
||
|
struct device_node *np = dev->of_node;
|
||
|
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) {
|
||
|
@@ -608,6 +669,7 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
|
||
|
return -ENOMEM;
|
||
|
|
||
|
rsb->dev = dev;
|
||
|
+ rsb->clk_freq = clk_freq;
|
||
|
platform_set_drvdata(pdev, rsb);
|
||
|
|
||
|
rsb->regs = devm_platform_ioremap_resource(pdev, 0);
|
||
|
@@ -625,63 +687,27 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
|
||
|
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_init_controller(rsb);
|
||
|
+ if (ret)
|
||
|
+ return ret;
|
||
|
+
|
||
|
/* initialize all devices on the bus into RSB mode */
|
||
|
ret = sunxi_rsb_init_device_mode(rsb);
|
||
|
if (ret)
|
||
|
@@ -690,14 +716,6 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
|
||
|
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)
|
||
|
@@ -705,8 +723,7 @@ static int sunxi_rsb_remove(struct platform_device *pdev)
|
||
|
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_exit_controller(rsb);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
--
|
||
|
2.34.0
|
||
|
|