mirror of https://gitee.com/openkylin/linux.git
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> Acked-by: Maxime Ripard <mripard@kernel.org> Signed-off-by: Chen-Yu Tsai <wens@csie.org>
This commit is contained in:
parent
014d65b60e
commit
22754ac9a6
|
@ -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(struct sunxi_rsb *rsb)
|
|||
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 platform_device *pdev)
|
|||
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 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_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 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)
|
||||
|
@ -735,8 +751,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_hw_exit(rsb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue