diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-21 05:42:28 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-02-21 05:42:28 +0300 |
commit | e767b3530acbf651593e3d357fe1168a024d8061 (patch) | |
tree | 0f549bbb4cf187661416f672dd0329c59b7a8fee /drivers/bus | |
parent | 82851fce6107d5a3e66d95aee2ae68860a732703 (diff) | |
parent | 4f79a8b06fccec3dd68935db5a4662435c472abf (diff) | |
download | linux-e767b3530acbf651593e3d357fe1168a024d8061.tar.xz |
Merge tag 'arm-drivers-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull ARM SoC driver updates from Arnd Bergmann:
"Updates for SoC specific drivers include a few subsystems that have
their own maintainers but send them through the soc tree:
SCMI firmware:
- add support for a completion interrupt
Reset controllers:
- new driver for BCM4908
- new devm_reset_control_get_optional_exclusive_released() function
Memory controllers:
- Renesas RZ/G2 support
- Tegra124 interconnect support
- Allow more drivers to be loadable modules
TEE/optee firmware:
- minor code cleanup
The other half of this is SoC specific drivers that do not belong into
any other subsystem, most of them living in drivers/soc:
- Allwinner/sunxi power management work
- Allwinner H616 support
- ASpeed AST2600 system identification support
- AT91 SAMA7G5 SoC ID driver
- AT91 SoC driver cleanups
- Broadcom BCM4908 power management bus support
- Marvell mbus cleanups
- Mediatek MT8167 power domain support
- Qualcomm socinfo driver support for PMIC
- Qualcomm SoC identification for many more products
- TI Keystone driver cleanups for PRUSS and elsewhere"
* tag 'arm-drivers-v5.12' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (89 commits)
soc: aspeed: socinfo: Add new systems
soc: aspeed: snoop: Add clock control logic
memory: tegra186-emc: Replace DEFINE_SIMPLE_ATTRIBUTE with DEFINE_DEBUGFS_ATTRIBUTE
memory: samsung: exynos5422-dmc: Correct function names in kerneldoc
memory: ti-emif-pm: Drop of_match_ptr from of_device_id table
optee: simplify i2c access
drivers: soc: atmel: fix type for same7
tee: optee: remove need_resched() before cond_resched()
soc: qcom: ocmem: don't return NULL in of_get_ocmem
optee: sync OP-TEE headers
tee: optee: fix 'physical' typos
drivers: optee: use flexible-array member instead of zero-length array
tee: fix some comment typos in header files
soc: ti: k3-ringacc: Use of_device_get_match_data()
soc: ti: pruss: Refactor the CFG sub-module init
soc: mediatek: pm-domains: Don't print an error if child domain is deferred
soc: mediatek: pm-domains: Add domain regulator supply
dt-bindings: power: Add domain regulator supply
soc: mediatek: cmdq: Remove cmdq_pkt_flush()
soc: mediatek: pm-domains: Add support for mt8167
...
Diffstat (limited to 'drivers/bus')
-rw-r--r-- | drivers/bus/mvebu-mbus.c | 2 | ||||
-rw-r--r-- | drivers/bus/sunxi-rsb.c | 215 |
2 files changed, 154 insertions, 63 deletions
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; } diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index 1bb00a959c67..d46db132d085 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -45,6 +45,8 @@ #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/reset.h> #include <linux/slab.h> @@ -126,6 +128,7 @@ struct sunxi_rsb { struct completion complete; struct mutex lock; unsigned int status; + u32 clk_freq; }; /* bus / slave device related functions */ @@ -170,7 +173,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 = { @@ -335,6 +340,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); @@ -350,6 +359,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; } @@ -377,6 +389,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); @@ -387,6 +403,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; } @@ -614,11 +633,100 @@ 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_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) +{ + /* 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); + + 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) { @@ -626,10 +734,8 @@ static int sunxi_rsb_probe(struct platform_device *pdev) 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) { @@ -644,6 +750,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); @@ -661,79 +768,41 @@ 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) 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; - -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) @@ -741,18 +810,40 @@ 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); + pm_runtime_disable(&pdev->dev); + sunxi_rsb_hw_exit(rsb); return 0; } +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) +}; + +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, + .shutdown = sunxi_rsb_shutdown, .driver = { .name = RSB_CTRL_NAME, .of_match_table = sunxi_rsb_of_match_table, + .pm = &sunxi_rsb_dev_pm_ops, }, }; |