diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-23 13:44:43 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-08-23 13:44:43 -0700 |
commit | 9e259f9352d52053058a234f7c062c4e4f56dc85 (patch) | |
tree | bfb1c442e27f95ddb9a04bae2a44ca9e3dd69a85 /drivers/soc | |
parent | 5563ae9b39c5ba492be1b18f2d72fd43ba549915 (diff) | |
parent | f0fc40aff6fee100ffbed8328a0df88f8aa75fce (diff) | |
download | linux-9e259f9352d52053058a234f7c062c4e4f56dc85.tar.gz linux-9e259f9352d52053058a234f7c062c4e4f56dc85.tar.bz2 linux-9e259f9352d52053058a234f7c062c4e4f56dc85.zip |
Merge tag 'armsoc-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM 32-bit SoC platform updates from Olof Johansson:
"Most of the SoC updates in this cycle are cleanups and moves to more
modern infrastructure:
- Davinci was moved to common clock framework
- OMAP1-based Amstrad E3 "Superphone" saw a bunch of cleanups to the
keyboard interface (bitbanged AT keyboard via GPIO).
- Removal of some stale code for Renesas platforms
- Power management improvements for i.MX6LL"
* tag 'armsoc-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (112 commits)
ARM: uniphier: select RESET_CONTROLLER
arm64: uniphier: select RESET_CONTROLLER
ARM: uniphier: remove empty Makefile
ARM: exynos: Clear global variable on init error path
ARM: exynos: Remove outdated maintainer information
ARM: shmobile: Always enable ARCH_TIMER on SoCs with A7 and/or A15
ARM: shmobile: r8a7779: hide unused r8a7779_platform_cpu_kill
soc: r9a06g032: don't build SMP files for non-SMP config
ARM: shmobile: Add the R9A06G032 SMP enabler driver
ARM: at91: pm: configure wakeup sources for ULP1 mode
ARM: at91: pm: add PMC fast startup registers defines
ARM: at91: pm: Add ULP1 mode support
ARM: at91: pm: Use ULP0 naming instead of slow clock
ARM: hisi: handle of_iomap and fix missing of_node_put
ARM: hisi: check of_iomap and fix missing of_node_put
ARM: hisi: fix error handling and missing of_node_put
ARM: mx5: Set the DBGEN bit in ARM_GPC register
ARM: imx51: Configure M4IF to avoid visual artifacts
ARM: imx: call imx6sx_cpuidle_init() conditionally for 6sll
ARM: imx: fix i.MX6SLL build
...
Diffstat (limited to 'drivers/soc')
-rw-r--r-- | drivers/soc/renesas/Makefile | 3 | ||||
-rw-r--r-- | drivers/soc/renesas/r9a06g032-smp.c | 96 | ||||
-rw-r--r-- | drivers/soc/renesas/rcar-sysc.c | 64 | ||||
-rw-r--r-- | drivers/soc/ti/pm33xx.c | 16 |
4 files changed, 155 insertions, 24 deletions
diff --git a/drivers/soc/renesas/Makefile b/drivers/soc/renesas/Makefile index 7dc0f20d7907..c37b0803c1b6 100644 --- a/drivers/soc/renesas/Makefile +++ b/drivers/soc/renesas/Makefile @@ -18,6 +18,9 @@ obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o obj-$(CONFIG_SYSC_R8A77990) += r8a77990-sysc.o obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o +ifdef CONFIG_SMP +obj-$(CONFIG_ARCH_R9A06G032) += r9a06g032-smp.o +endif # Family obj-$(CONFIG_RST_RCAR) += rcar-rst.o diff --git a/drivers/soc/renesas/r9a06g032-smp.c b/drivers/soc/renesas/r9a06g032-smp.c new file mode 100644 index 000000000000..a1926e8d73f2 --- /dev/null +++ b/drivers/soc/renesas/r9a06g032-smp.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R9A06G032 Second CA7 enabler. + * + * Copyright (C) 2018 Renesas Electronics Europe Limited + * + * Michel Pollet <michel.pollet@bp.renesas.com>, <buserror@gmail.com> + * Derived from actions,s500-smp + */ + +#include <linux/io.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/smp.h> + +/* + * The second CPU is parked in ROM at boot time. It requires waking it after + * writing an address into the BOOTADDR register of sysctrl. + * + * So the default value of the "cpu-release-addr" corresponds to BOOTADDR... + * + * *However* the BOOTADDR register is not available when the kernel + * starts in NONSEC mode. + * + * So for NONSEC mode, the bootloader re-parks the second CPU into a pen + * in SRAM, and changes the "cpu-release-addr" of linux's DT to a SRAM address, + * which is not restricted. + */ + +static void __iomem *cpu_bootaddr; + +static DEFINE_SPINLOCK(cpu_lock); + +static int +r9a06g032_smp_boot_secondary(unsigned int cpu, + struct task_struct *idle) +{ + if (!cpu_bootaddr) + return -ENODEV; + + spin_lock(&cpu_lock); + + writel(__pa_symbol(secondary_startup), cpu_bootaddr); + arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + + spin_unlock(&cpu_lock); + + return 0; +} + +static void __init r9a06g032_smp_prepare_cpus(unsigned int max_cpus) +{ + struct device_node *dn; + int ret = -EINVAL, dns; + u32 bootaddr; + + dn = of_get_cpu_node(1, NULL); + if (!dn) { + pr_err("CPU#1: missing device tree node\n"); + return; + } + /* + * Determine the address from which the CPU is polling. + * The bootloader *does* change this property. + * Note: The property can be either 64 or 32 bits, so handle both cases + */ + if (of_find_property(dn, "cpu-release-addr", &dns)) { + if (dns == sizeof(u64)) { + u64 temp; + + ret = of_property_read_u64(dn, + "cpu-release-addr", &temp); + bootaddr = temp; + } else { + ret = of_property_read_u32(dn, + "cpu-release-addr", + &bootaddr); + } + } + of_node_put(dn); + if (ret) { + pr_err("CPU#1: invalid cpu-release-addr property\n"); + return; + } + pr_info("CPU#1: cpu-release-addr %08x\n", bootaddr); + + cpu_bootaddr = ioremap(bootaddr, sizeof(bootaddr)); +} + +static const struct smp_operations r9a06g032_smp_ops __initconst = { + .smp_prepare_cpus = r9a06g032_smp_prepare_cpus, + .smp_boot_secondary = r9a06g032_smp_boot_secondary, +}; + +CPU_METHOD_OF_DECLARE(r9a06g032_smp, + "renesas,r9a06g032-smp", &r9a06g032_smp_ops); diff --git a/drivers/soc/renesas/rcar-sysc.c b/drivers/soc/renesas/rcar-sysc.c index 50d03d8b4f9a..029188e8be6e 100644 --- a/drivers/soc/renesas/rcar-sysc.c +++ b/drivers/soc/renesas/rcar-sysc.c @@ -58,6 +58,12 @@ #define RCAR_PD_ALWAYS_ON 32 /* Always-on power area */ +struct rcar_sysc_ch { + u16 chan_offs; + u8 chan_bit; + u8 isr_bit; +}; + static void __iomem *rcar_sysc_base; static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ @@ -143,12 +149,12 @@ static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) return ret; } -int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch) +static int rcar_sysc_power_down(const struct rcar_sysc_ch *sysc_ch) { return rcar_sysc_power(sysc_ch, false); } -int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch) +static int rcar_sysc_power_up(const struct rcar_sysc_ch *sysc_ch) { return rcar_sysc_power(sysc_ch, true); } @@ -315,6 +321,8 @@ struct rcar_pm_domains { struct generic_pm_domain *domains[RCAR_PD_ALWAYS_ON + 1]; }; +static struct genpd_onecell_data *rcar_sysc_onecell_data; + static int __init rcar_sysc_pd_init(void) { const struct rcar_sysc_info *info; @@ -326,9 +334,6 @@ static int __init rcar_sysc_pd_init(void) unsigned int i; int error; - if (rcar_sysc_base) - return 0; - np = of_find_matching_node_and_match(NULL, rcar_sysc_matches, &match); if (!np) return -ENODEV; @@ -361,6 +366,7 @@ static int __init rcar_sysc_pd_init(void) domains->onecell_data.domains = domains->domains; domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); + rcar_sysc_onecell_data = &domains->onecell_data; for (i = 0, syscier = 0; i < info->num_areas; i++) syscier |= BIT(info->areas[i].isr_bit); @@ -448,27 +454,39 @@ void __init rcar_sysc_nullify(struct rcar_sysc_area *areas, } } -void __init rcar_sysc_init(phys_addr_t base, u32 syscier) +#ifdef CONFIG_ARCH_R8A7779 +static int rcar_sysc_power_cpu(unsigned int idx, bool on) { - u32 syscimr; + struct generic_pm_domain *genpd; + struct rcar_sysc_pd *pd; + unsigned int i; - if (!rcar_sysc_pd_init()) - return; + if (!rcar_sysc_onecell_data) + return -ENODEV; - rcar_sysc_base = ioremap_nocache(base, PAGE_SIZE); + for (i = 0; i < rcar_sysc_onecell_data->num_domains; i++) { + genpd = rcar_sysc_onecell_data->domains[i]; + if (!genpd) + continue; - /* - * Mask all interrupt sources to prevent the CPU from receiving them. - * Make sure not to clear reserved bits that were set before. - */ - syscimr = ioread32(rcar_sysc_base + SYSCIMR); - syscimr |= syscier; - pr_debug("%s: syscimr = 0x%08x\n", __func__, syscimr); - iowrite32(syscimr, rcar_sysc_base + SYSCIMR); + pd = to_rcar_pd(genpd); + if (!(pd->flags & PD_CPU) || pd->ch.chan_bit != idx) + continue; - /* - * SYSC needs all interrupt sources enabled to control power. - */ - pr_debug("%s: syscier = 0x%08x\n", __func__, syscier); - iowrite32(syscier, rcar_sysc_base + SYSCIER); + return on ? rcar_sysc_power_up(&pd->ch) + : rcar_sysc_power_down(&pd->ch); + } + + return -ENOENT; +} + +int rcar_sysc_power_down_cpu(unsigned int cpu) +{ + return rcar_sysc_power_cpu(cpu, false); +} + +int rcar_sysc_power_up_cpu(unsigned int cpu) +{ + return rcar_sysc_power_cpu(cpu, true); } +#endif /* CONFIG_ARCH_R8A7779 */ diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c index 652739c7f718..d0dab323651f 100644 --- a/drivers/soc/ti/pm33xx.c +++ b/drivers/soc/ti/pm33xx.c @@ -41,6 +41,8 @@ static struct am33xx_pm_sram_addr *pm_sram; static struct device *pm33xx_dev; static struct wkup_m3_ipc *m3_ipc; +static unsigned long suspend_wfi_flags; + static u32 sram_suspend_address(unsigned long addr) { return ((unsigned long)am33xx_do_wfi_sram + @@ -53,7 +55,7 @@ static int am33xx_pm_suspend(suspend_state_t suspend_state) int i, ret = 0; ret = pm_ops->soc_suspend((unsigned long)suspend_state, - am33xx_do_wfi_sram); + am33xx_do_wfi_sram, suspend_wfi_flags); if (ret) { dev_err(pm33xx_dev, "PM: Kernel suspend failure\n"); @@ -227,6 +229,7 @@ static int am33xx_push_sram_idle(void) ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data; ro_sram_data.amx3_pm_sram_data_phys = gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data); + ro_sram_data.rtc_base_virt = pm_ops->get_rtc_base_addr(); /* Save physical address to calculate resume offset during pm init */ am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool, @@ -310,6 +313,17 @@ static int am33xx_pm_probe(struct platform_device *pdev) suspend_set_ops(&am33xx_pm_ops); #endif /* CONFIG_SUSPEND */ + /* + * For a system suspend we must flush the caches, we want + * the DDR in self-refresh, we want to save the context + * of the EMIF, and we want the wkup_m3 to handle low-power + * transition. + */ + suspend_wfi_flags |= WFI_FLAG_FLUSH_CACHE; + suspend_wfi_flags |= WFI_FLAG_SELF_REFRESH; + suspend_wfi_flags |= WFI_FLAG_SAVE_EMIF; + suspend_wfi_flags |= WFI_FLAG_WAKE_M3; + ret = pm_ops->init(); if (ret) { dev_err(dev, "Unable to call core pm init!\n"); |