From 014d65b60e46e1af262419139c07bfa1f0775715 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 3 Jan 2021 05:06:32 -0600 Subject: bus: sunxi-rsb: Move OF match table For some reason, this driver's OF match table was placed above the probe/remove functions, far away from the platform_driver definition. Adding device PM ops would move the table even farther away. Let's move it to the usual place, right before the platform_driver. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- drivers/bus/sunxi-rsb.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/bus') diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index 1bb00a959c67..c13340cab27a 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -614,12 +614,6 @@ static int of_rsb_register_devices(struct sunxi_rsb *rsb) return 0; } -static const struct of_device_id sunxi_rsb_of_match_table[] = { - { .compatible = "allwinner,sun8i-a23-rsb" }, - {} -}; -MODULE_DEVICE_TABLE(of, sunxi_rsb_of_match_table); - static int sunxi_rsb_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -747,6 +741,12 @@ static int sunxi_rsb_remove(struct platform_device *pdev) return 0; } +static const struct of_device_id sunxi_rsb_of_match_table[] = { + { .compatible = "allwinner,sun8i-a23-rsb" }, + {} +}; +MODULE_DEVICE_TABLE(of, sunxi_rsb_of_match_table); + static struct platform_driver sunxi_rsb_driver = { .probe = sunxi_rsb_probe, .remove = sunxi_rsb_remove, -- cgit v1.2.3 From 22754ac9a632f9bcb2cfd91243459e679778a497 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 3 Jan 2021 05:06:33 -0600 Subject: 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 Acked-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- drivers/bus/sunxi-rsb.c | 127 +++++++++++++++++++++++++++--------------------- 1 file changed, 71 insertions(+), 56 deletions(-) (limited to 'drivers/bus') diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index c13340cab27a..3f290da45619 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 */ @@ -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; } -- cgit v1.2.3 From 843107498f91e57d1d4b22cd8787112726fdaeb4 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 3 Jan 2021 05:06:34 -0600 Subject: bus: sunxi-rsb: Implement suspend/resume/shutdown callbacks Since system firmware is likely to use the RSB bus to communicate with a PMIC while the system is suspended, we cannot make any assumptions about the controller state after resuming. Thus it is important to completely reinitialize the controller. The RSB bus needs to be ready as soon as IRQs are enabled, to handle wakeup event IRQs coming from the PMIC. Thus it uses NOIRQ callbacks. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- drivers/bus/sunxi-rsb.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers/bus') diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index 3f290da45619..efd222f36cdc 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -45,6 +45,7 @@ #include #include #include +#include #include #include #include @@ -675,6 +676,22 @@ static void sunxi_rsb_hw_exit(struct sunxi_rsb *rsb) clk_disable_unprepare(rsb->clk); } +static int __maybe_unused sunxi_rsb_suspend(struct device *dev) +{ + struct sunxi_rsb *rsb = dev_get_drvdata(dev); + + sunxi_rsb_hw_exit(rsb); + + return 0; +} + +static int __maybe_unused sunxi_rsb_resume(struct device *dev) +{ + struct sunxi_rsb *rsb = dev_get_drvdata(dev); + + return sunxi_rsb_hw_init(rsb); +} + static int sunxi_rsb_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -756,6 +773,17 @@ static int sunxi_rsb_remove(struct platform_device *pdev) return 0; } +static void sunxi_rsb_shutdown(struct platform_device *pdev) +{ + struct sunxi_rsb *rsb = platform_get_drvdata(pdev); + + sunxi_rsb_hw_exit(rsb); +} + +static const struct dev_pm_ops sunxi_rsb_dev_pm_ops = { + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sunxi_rsb_suspend, sunxi_rsb_resume) +}; + static const struct of_device_id sunxi_rsb_of_match_table[] = { { .compatible = "allwinner,sun8i-a23-rsb" }, {} @@ -765,9 +793,11 @@ MODULE_DEVICE_TABLE(of, sunxi_rsb_of_match_table); static struct platform_driver sunxi_rsb_driver = { .probe = sunxi_rsb_probe, .remove = sunxi_rsb_remove, + .shutdown = sunxi_rsb_shutdown, .driver = { .name = RSB_CTRL_NAME, .of_match_table = sunxi_rsb_of_match_table, + .pm = &sunxi_rsb_dev_pm_ops, }, }; -- cgit v1.2.3 From 4a0dbc12e61823ece7172df953c3333c8f5f9222 Mon Sep 17 00:00:00 2001 From: Samuel Holland Date: Sun, 3 Jan 2021 05:06:35 -0600 Subject: bus: sunxi-rsb: Implement runtime power management Gate the clock to save power while the controller is idle. Signed-off-by: Samuel Holland Acked-by: Maxime Ripard Signed-off-by: Chen-Yu Tsai --- drivers/bus/sunxi-rsb.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'drivers/bus') diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index efd222f36cdc..ba5100dfc413 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include #include @@ -337,6 +338,10 @@ static int sunxi_rsb_read(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr, return -EINVAL; } + ret = pm_runtime_resume_and_get(rsb->dev); + if (ret) + return ret; + mutex_lock(&rsb->lock); writel(addr, rsb->regs + RSB_ADDR); @@ -352,6 +357,9 @@ static int sunxi_rsb_read(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr, unlock: mutex_unlock(&rsb->lock); + pm_runtime_mark_last_busy(rsb->dev); + pm_runtime_put_autosuspend(rsb->dev); + return ret; } @@ -379,6 +387,10 @@ static int sunxi_rsb_write(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr, return -EINVAL; } + ret = pm_runtime_resume_and_get(rsb->dev); + if (ret) + return ret; + mutex_lock(&rsb->lock); writel(addr, rsb->regs + RSB_ADDR); @@ -389,6 +401,9 @@ static int sunxi_rsb_write(struct sunxi_rsb *rsb, u8 rtaddr, u8 addr, mutex_unlock(&rsb->lock); + pm_runtime_mark_last_busy(rsb->dev); + pm_runtime_put_autosuspend(rsb->dev); + return ret; } @@ -672,10 +687,29 @@ err_clk_disable: static void sunxi_rsb_hw_exit(struct sunxi_rsb *rsb) { + /* Keep the clock and PM reference counts consistent. */ + if (pm_runtime_status_suspended(rsb->dev)) + pm_runtime_resume(rsb->dev); reset_control_assert(rsb->rstc); clk_disable_unprepare(rsb->clk); } +static int __maybe_unused sunxi_rsb_runtime_suspend(struct device *dev) +{ + struct sunxi_rsb *rsb = dev_get_drvdata(dev); + + clk_disable_unprepare(rsb->clk); + + return 0; +} + +static int __maybe_unused sunxi_rsb_runtime_resume(struct device *dev) +{ + struct sunxi_rsb *rsb = dev_get_drvdata(dev); + + return clk_prepare_enable(rsb->clk); +} + static int __maybe_unused sunxi_rsb_suspend(struct device *dev) { struct sunxi_rsb *rsb = dev_get_drvdata(dev); @@ -758,6 +792,12 @@ static int sunxi_rsb_probe(struct platform_device *pdev) if (ret) dev_warn(dev, "Initialize device mode failed: %d\n", ret); + pm_suspend_ignore_children(dev, true); + pm_runtime_set_active(dev); + pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC); + pm_runtime_use_autosuspend(dev); + pm_runtime_enable(dev); + of_rsb_register_devices(rsb); return 0; @@ -768,6 +808,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); + pm_runtime_disable(&pdev->dev); sunxi_rsb_hw_exit(rsb); return 0; @@ -777,10 +818,13 @@ static void sunxi_rsb_shutdown(struct platform_device *pdev) { struct sunxi_rsb *rsb = platform_get_drvdata(pdev); + pm_runtime_disable(&pdev->dev); sunxi_rsb_hw_exit(rsb); } static const struct dev_pm_ops sunxi_rsb_dev_pm_ops = { + SET_RUNTIME_PM_OPS(sunxi_rsb_runtime_suspend, + sunxi_rsb_runtime_resume, NULL) SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sunxi_rsb_suspend, sunxi_rsb_resume) }; -- cgit v1.2.3 From 3c15e00e7b58bc2b37e53d2612f0a0163281be77 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Thu, 26 Nov 2020 11:41:42 +0100 Subject: mfd/bus: sunxi-rsb: Make .remove() callback return void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver core ignores the return value of struct device_driver::remove because there is only little that can be done. To simplify the quest to make this function return void, let struct sunxi_rsb_driver::remove return void, too. All users already unconditionally return 0, this commit makes this obvious and ensures future users don't behave differently. To simplify even further, make axp20x_device_remove() return void instead of returning 0 unconditionally, too. Signed-off-by: Uwe Kleine-König Reviewed-by: Chen-Yu Tsai Signed-off-by: Lee Jones --- drivers/bus/sunxi-rsb.c | 4 +++- drivers/mfd/axp20x-i2c.c | 4 +++- drivers/mfd/axp20x-rsb.c | 4 ++-- drivers/mfd/axp20x.c | 4 +--- include/linux/mfd/axp20x.h | 2 +- include/linux/sunxi-rsb.h | 2 +- 6 files changed, 11 insertions(+), 9 deletions(-) (limited to 'drivers/bus') diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index 1bb00a959c67..117716e23ffb 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -170,7 +170,9 @@ static int sunxi_rsb_device_remove(struct device *dev) { const struct sunxi_rsb_driver *drv = to_sunxi_rsb_driver(dev->driver); - return drv->remove(to_sunxi_rsb_device(dev)); + drv->remove(to_sunxi_rsb_device(dev)); + + return 0; } static struct bus_type sunxi_rsb_bus = { diff --git a/drivers/mfd/axp20x-i2c.c b/drivers/mfd/axp20x-i2c.c index 2cfde81f5fbf..00ab48018d8d 100644 --- a/drivers/mfd/axp20x-i2c.c +++ b/drivers/mfd/axp20x-i2c.c @@ -54,7 +54,9 @@ static int axp20x_i2c_remove(struct i2c_client *i2c) { struct axp20x_dev *axp20x = i2c_get_clientdata(i2c); - return axp20x_device_remove(axp20x); + axp20x_device_remove(axp20x); + + return 0; } #ifdef CONFIG_OF diff --git a/drivers/mfd/axp20x-rsb.c b/drivers/mfd/axp20x-rsb.c index 4cdc79f5cc48..214bc0d84d44 100644 --- a/drivers/mfd/axp20x-rsb.c +++ b/drivers/mfd/axp20x-rsb.c @@ -49,11 +49,11 @@ static int axp20x_rsb_probe(struct sunxi_rsb_device *rdev) return axp20x_device_probe(axp20x); } -static int axp20x_rsb_remove(struct sunxi_rsb_device *rdev) +static void axp20x_rsb_remove(struct sunxi_rsb_device *rdev) { struct axp20x_dev *axp20x = sunxi_rsb_device_get_drvdata(rdev); - return axp20x_device_remove(axp20x); + axp20x_device_remove(axp20x); } static const struct of_device_id axp20x_rsb_of_match[] = { diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index aa59496e4376..3eae04e24ac8 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -987,7 +987,7 @@ int axp20x_device_probe(struct axp20x_dev *axp20x) } EXPORT_SYMBOL(axp20x_device_probe); -int axp20x_device_remove(struct axp20x_dev *axp20x) +void axp20x_device_remove(struct axp20x_dev *axp20x) { if (axp20x == axp20x_pm_power_off) { axp20x_pm_power_off = NULL; @@ -996,8 +996,6 @@ int axp20x_device_remove(struct axp20x_dev *axp20x) mfd_remove_devices(axp20x->dev); regmap_del_irq_chip(axp20x->irq, axp20x->regmap_irqc); - - return 0; } EXPORT_SYMBOL(axp20x_device_remove); diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index fd5957c042da..9ab0e2fca7ea 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -696,6 +696,6 @@ int axp20x_device_probe(struct axp20x_dev *axp20x); * * This tells the axp20x core to remove the associated mfd devices */ -int axp20x_device_remove(struct axp20x_dev *axp20x); +void axp20x_device_remove(struct axp20x_dev *axp20x); #endif /* __LINUX_MFD_AXP20X_H */ diff --git a/include/linux/sunxi-rsb.h b/include/linux/sunxi-rsb.h index 7e75bb0346d0..bf0d365f471c 100644 --- a/include/linux/sunxi-rsb.h +++ b/include/linux/sunxi-rsb.h @@ -59,7 +59,7 @@ static inline void sunxi_rsb_device_set_drvdata(struct sunxi_rsb_device *rdev, struct sunxi_rsb_driver { struct device_driver driver; int (*probe)(struct sunxi_rsb_device *rdev); - int (*remove)(struct sunxi_rsb_device *rdev); + void (*remove)(struct sunxi_rsb_device *rdev); }; static inline struct sunxi_rsb_driver *to_sunxi_rsb_driver(struct device_driver *d) -- cgit v1.2.3 From 2241ed9205ed91072d20ae10ad6904dc2bec5612 Mon Sep 17 00:00:00 2001 From: Chris Packham Date: Thu, 12 Nov 2020 16:21:49 +1300 Subject: bus: mvebu-mbus: make iounmap() symmetric with ioremap() make coccicheck complains: ./drivers/bus/mvebu-mbus.c:1113:2-8: ERROR: missing iounmap; ioremap on line 1106 and execution via conditional on line 1111 It took some staring but I don't think there is a problem because the file global `mbus_state` is passed mvebu_mbus_common_init() as the `mbus` parameter so `mbus_state.mbuswins_base` and `mbus->mbuswins_base` are the same thing. But this is confusing for anyone reading the code and one less complaint from coccicheck would be nice so lets fix it. Signed-off-by: Chris Packham Acked-by: Thomas Petazzoni Signed-off-by: Gregory CLEMENT --- drivers/bus/mvebu-mbus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/bus') diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 2519ceede64b..dd9e7343a5e3 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -1111,7 +1111,7 @@ static int __init mvebu_mbus_common_init(struct mvebu_mbus_state *mbus, mbus->sdramwins_base = ioremap(sdramwins_phys_base, sdramwins_size); if (!mbus->sdramwins_base) { - iounmap(mbus_state.mbuswins_base); + iounmap(mbus->mbuswins_base); return -ENOMEM; } -- cgit v1.2.3