Commit 22754ac9 authored by Samuel Holland's avatar Samuel Holland Committed by Chen-Yu Tsai

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: default avatarSamuel Holland <samuel@sholland.org>
Acked-by: default avatarMaxime Ripard <mripard@kernel.org>
Signed-off-by: default avatarChen-Yu Tsai <wens@csie.org>
parent 014d65b6
...@@ -126,6 +126,7 @@ struct sunxi_rsb { ...@@ -126,6 +126,7 @@ struct sunxi_rsb {
struct completion complete; struct completion complete;
struct mutex lock; struct mutex lock;
unsigned int status; unsigned int status;
u32 clk_freq;
}; };
/* bus / slave device related functions */ /* bus / slave device related functions */
...@@ -614,16 +615,74 @@ static int of_rsb_register_devices(struct sunxi_rsb *rsb) ...@@ -614,16 +615,74 @@ static int of_rsb_register_devices(struct sunxi_rsb *rsb)
return 0; 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) static int sunxi_rsb_probe(struct platform_device *pdev)
{ {
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node; struct device_node *np = dev->of_node;
struct resource *r; struct resource *r;
struct sunxi_rsb *rsb; struct sunxi_rsb *rsb;
unsigned long p_clk_freq; u32 clk_freq = 3000000;
u32 clk_delay, clk_freq = 3000000; int irq, ret;
int clk_div, irq, ret;
u32 reg;
of_property_read_u32(np, "clock-frequency", &clk_freq); of_property_read_u32(np, "clock-frequency", &clk_freq);
if (clk_freq > RSB_MAX_FREQ) { if (clk_freq > RSB_MAX_FREQ) {
...@@ -638,6 +697,7 @@ static int sunxi_rsb_probe(struct platform_device *pdev) ...@@ -638,6 +697,7 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
return -ENOMEM; return -ENOMEM;
rsb->dev = dev; rsb->dev = dev;
rsb->clk_freq = clk_freq;
platform_set_drvdata(pdev, rsb); platform_set_drvdata(pdev, rsb);
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
rsb->regs = devm_ioremap_resource(dev, r); rsb->regs = devm_ioremap_resource(dev, r);
...@@ -655,63 +715,27 @@ static int sunxi_rsb_probe(struct platform_device *pdev) ...@@ -655,63 +715,27 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
return ret; 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); rsb->rstc = devm_reset_control_get(dev, NULL);
if (IS_ERR(rsb->rstc)) { if (IS_ERR(rsb->rstc)) {
ret = PTR_ERR(rsb->rstc); ret = PTR_ERR(rsb->rstc);
dev_err(dev, "failed to retrieve reset controller: %d\n", ret); dev_err(dev, "failed to retrieve reset controller: %d\n", ret);
goto err_clk_disable; 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;
} }
init_completion(&rsb->complete); init_completion(&rsb->complete);
mutex_init(&rsb->lock); 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); ret = devm_request_irq(dev, irq, sunxi_rsb_irq, 0, RSB_CTRL_NAME, rsb);
if (ret) { if (ret) {
dev_err(dev, "can't register interrupt handler irq %d: %d\n", dev_err(dev, "can't register interrupt handler irq %d: %d\n",
irq, ret); 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 */ /* initialize all devices on the bus into RSB mode */
ret = sunxi_rsb_init_device_mode(rsb); ret = sunxi_rsb_init_device_mode(rsb);
if (ret) if (ret)
...@@ -720,14 +744,6 @@ static int sunxi_rsb_probe(struct platform_device *pdev) ...@@ -720,14 +744,6 @@ static int sunxi_rsb_probe(struct platform_device *pdev)
of_rsb_register_devices(rsb); of_rsb_register_devices(rsb);
return 0; 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) static int sunxi_rsb_remove(struct platform_device *pdev)
...@@ -735,8 +751,7 @@ static int sunxi_rsb_remove(struct platform_device *pdev) ...@@ -735,8 +751,7 @@ static int sunxi_rsb_remove(struct platform_device *pdev)
struct sunxi_rsb *rsb = platform_get_drvdata(pdev); struct sunxi_rsb *rsb = platform_get_drvdata(pdev);
device_for_each_child(rsb->dev, NULL, sunxi_rsb_remove_devices); device_for_each_child(rsb->dev, NULL, sunxi_rsb_remove_devices);
reset_control_assert(rsb->rstc); sunxi_rsb_hw_exit(rsb);
clk_disable_unprepare(rsb->clk);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment