diff options
Diffstat (limited to 'arch/arm/mach-exynos')
-rw-r--r-- | arch/arm/mach-exynos/common.h | 4 | ||||
-rw-r--r-- | arch/arm/mach-exynos/exynos.c | 24 | ||||
-rw-r--r-- | arch/arm/mach-exynos/firmware.c | 18 | ||||
-rw-r--r-- | arch/arm/mach-exynos/platsmp.c | 86 | ||||
-rw-r--r-- | arch/arm/mach-exynos/pm.c | 51 | ||||
-rw-r--r-- | arch/arm/mach-exynos/pm_domains.c | 52 | ||||
-rw-r--r-- | arch/arm/mach-exynos/pmu.c | 6 | ||||
-rw-r--r-- | arch/arm/mach-exynos/suspend.c | 10 |
8 files changed, 182 insertions, 69 deletions
diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h index 5f5cd562c..e3a9256ed 100644 --- a/arch/arm/mach-exynos/common.h +++ b/arch/arm/mach-exynos/common.h @@ -163,7 +163,9 @@ extern void exynos_set_delayed_reset_assertion(bool enable); extern void s5p_init_cpu(void __iomem *cpuid_addr); extern unsigned int samsung_rev(void); -extern void __iomem *cpu_boot_reg_base(void); +extern void exynos_core_restart(u32 core_id); +extern int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr); +extern int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr); static inline void pmu_raw_writel(u32 val, u32 offset) { diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c index 5917a30ee..5f8ddcdee 100644 --- a/arch/arm/mach-exynos/exynos.c +++ b/arch/arm/mach-exynos/exynos.c @@ -224,6 +224,25 @@ static void __init exynos_init_irq(void) exynos_map_pmu(); } +static const struct of_device_id exynos_cpufreq_matches[] = { + { .compatible = "samsung,exynos4210", .data = "cpufreq-dt" }, + { /* sentinel */ } +}; + +static void __init exynos_cpufreq_init(void) +{ + struct device_node *root = of_find_node_by_path("/"); + const struct of_device_id *match; + + match = of_match_node(exynos_cpufreq_matches, root); + if (!match) { + platform_device_register_simple("exynos-cpufreq", -1, NULL, 0); + return; + } + + platform_device_register_simple(match->data, -1, NULL, 0); +} + static void __init exynos_dt_machine_init(void) { /* @@ -234,7 +253,8 @@ static void __init exynos_dt_machine_init(void) exynos_sysram_init(); #if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE) - if (of_machine_is_compatible("samsung,exynos4210")) + if (of_machine_is_compatible("samsung,exynos4210") || + of_machine_is_compatible("samsung,exynos3250")) exynos_cpuidle.dev.platform_data = &cpuidle_coupled_exynos_data; #endif if (of_machine_is_compatible("samsung,exynos4210") || @@ -245,7 +265,7 @@ static void __init exynos_dt_machine_init(void) of_machine_is_compatible("samsung,exynos5250")) platform_device_register(&exynos_cpuidle); - platform_device_register_simple("exynos-cpufreq", -1, NULL, 0); + exynos_cpufreq_init(); of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); } diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c index 1bd35763f..245f6dec1 100644 --- a/arch/arm/mach-exynos/firmware.c +++ b/arch/arm/mach-exynos/firmware.c @@ -49,6 +49,7 @@ static int exynos_do_idle(unsigned long mode) sysram_ns_base_addr + 0x24); __raw_writel(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20); if (soc_is_exynos3250()) { + flush_cache_all(); exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE, SMC_POWERSTATE_IDLE, 0); exynos_smc(SMC_CMD_SHUTDOWN, OP_TYPE_CLUSTER, @@ -104,6 +105,22 @@ static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr) return 0; } +static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr) +{ + void __iomem *boot_reg; + + if (!sysram_ns_base_addr) + return -ENODEV; + + boot_reg = sysram_ns_base_addr + 0x1c; + + if (soc_is_exynos4412()) + boot_reg += 4 * cpu; + + *boot_addr = __raw_readl(boot_reg); + return 0; +} + static int exynos_cpu_suspend(unsigned long arg) { flush_cache_all(); @@ -138,6 +155,7 @@ static int exynos_resume(void) static const struct firmware_ops exynos_firmware_ops = { .do_idle = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_do_idle : NULL, .set_cpu_boot_addr = exynos_set_cpu_boot_addr, + .get_cpu_boot_addr = exynos_get_cpu_boot_addr, .cpu_boot = exynos_cpu_boot, .suspend = IS_ENABLED(CONFIG_PM_SLEEP) ? exynos_suspend : NULL, .resume = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_resume : NULL, diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c index a825bca2a..58e05a2ea 100644 --- a/arch/arm/mach-exynos/platsmp.c +++ b/arch/arm/mach-exynos/platsmp.c @@ -169,7 +169,7 @@ int exynos_cluster_power_state(int cluster) S5P_CORE_LOCAL_PWR_EN); } -void __iomem *cpu_boot_reg_base(void) +static void __iomem *cpu_boot_reg_base(void) { if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1) return pmu_base_addr + S5P_INFORM5; @@ -195,7 +195,7 @@ static inline void __iomem *cpu_boot_reg(int cpu) * * Currently this is needed only when booting secondary CPU on Exynos3250. */ -static void exynos_core_restart(u32 core_id) +void exynos_core_restart(u32 core_id) { u32 val; @@ -210,7 +210,6 @@ static void exynos_core_restart(u32 core_id) val |= S5P_CORE_WAKEUP_FROM_LOCAL_CFG; pmu_raw_writel(val, EXYNOS_ARM_CORE_STATUS(core_id)); - pr_info("CPU%u: Software reset\n", core_id); pmu_raw_writel(EXYNOS_CORE_PO_RESET(core_id), EXYNOS_SWRESET); } @@ -248,6 +247,56 @@ static void exynos_secondary_init(unsigned int cpu) spin_unlock(&boot_lock); } +int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr) +{ + int ret; + + /* + * Try to set boot address using firmware first + * and fall back to boot register if it fails. + */ + ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr); + if (ret && ret != -ENOSYS) + goto fail; + if (ret == -ENOSYS) { + void __iomem *boot_reg = cpu_boot_reg(core_id); + + if (IS_ERR(boot_reg)) { + ret = PTR_ERR(boot_reg); + goto fail; + } + __raw_writel(boot_addr, boot_reg); + ret = 0; + } +fail: + return ret; +} + +int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr) +{ + int ret; + + /* + * Try to get boot address using firmware first + * and fall back to boot register if it fails. + */ + ret = call_firmware_op(get_cpu_boot_addr, core_id, boot_addr); + if (ret && ret != -ENOSYS) + goto fail; + if (ret == -ENOSYS) { + void __iomem *boot_reg = cpu_boot_reg(core_id); + + if (IS_ERR(boot_reg)) { + ret = PTR_ERR(boot_reg); + goto fail; + } + *boot_addr = __raw_readl(boot_reg); + ret = 0; + } +fail: + return ret; +} + static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) { unsigned long timeout; @@ -307,22 +356,9 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) boot_addr = virt_to_phys(exynos4_secondary_startup); - /* - * Try to set boot address using firmware first - * and fall back to boot register if it fails. - */ - ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr); - if (ret && ret != -ENOSYS) + ret = exynos_set_boot_addr(core_id, boot_addr); + if (ret) goto fail; - if (ret == -ENOSYS) { - void __iomem *boot_reg = cpu_boot_reg(core_id); - - if (IS_ERR(boot_reg)) { - ret = PTR_ERR(boot_reg); - goto fail; - } - __raw_writel(boot_addr, boot_reg); - } call_firmware_op(cpu_boot, core_id); @@ -337,6 +373,9 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle) udelay(10); } + if (pen_release != -1) + ret = -ETIMEDOUT; + /* * now the secondary core is starting up let it run its * calibrations, then wait for it to finish @@ -407,16 +446,9 @@ static void __init exynos_smp_prepare_cpus(unsigned int max_cpus) core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0); boot_addr = virt_to_phys(exynos4_secondary_startup); - ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr); - if (ret && ret != -ENOSYS) + ret = exynos_set_boot_addr(core_id, boot_addr); + if (ret) break; - if (ret == -ENOSYS) { - void __iomem *boot_reg = cpu_boot_reg(core_id); - - if (IS_ERR(boot_reg)) - break; - __raw_writel(boot_addr, boot_reg); - } } } diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c index cc75ab448..9c1506b49 100644 --- a/arch/arm/mach-exynos/pm.c +++ b/arch/arm/mach-exynos/pm.c @@ -22,6 +22,7 @@ #include <asm/firmware.h> #include <asm/smp_scu.h> #include <asm/suspend.h> +#include <asm/cacheflush.h> #include <mach/map.h> @@ -209,6 +210,8 @@ static int exynos_cpu0_enter_aftr(void) * sequence, let's wait for one of these to happen */ while (exynos_cpu_power_state(1)) { + unsigned long boot_addr; + /* * The other cpu may skip idle and boot back * up again @@ -221,7 +224,11 @@ static int exynos_cpu0_enter_aftr(void) * boot back up again, getting stuck in the * boot rom code */ - if (__raw_readl(cpu_boot_reg_base()) == 0) + ret = exynos_get_boot_addr(1, &boot_addr); + if (ret) + goto fail; + ret = -1; + if (boot_addr == 0) goto abort; cpu_relax(); @@ -233,11 +240,14 @@ static int exynos_cpu0_enter_aftr(void) abort: if (cpu_online(1)) { + unsigned long boot_addr = virt_to_phys(exynos_cpu_resume); + /* * Set the boot vector to something non-zero */ - __raw_writel(virt_to_phys(exynos_cpu_resume), - cpu_boot_reg_base()); + ret = exynos_set_boot_addr(1, boot_addr); + if (ret) + goto fail; dsb(); /* @@ -247,22 +257,42 @@ abort: while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN) cpu_relax(); + if (soc_is_exynos3250()) { + while (!pmu_raw_readl(S5P_PMU_SPARE2) && + !atomic_read(&cpu1_wakeup)) + cpu_relax(); + + if (!atomic_read(&cpu1_wakeup)) + exynos_core_restart(1); + } + while (!atomic_read(&cpu1_wakeup)) { + smp_rmb(); + /* * Poke cpu1 out of the boot rom */ - __raw_writel(virt_to_phys(exynos_cpu_resume), - cpu_boot_reg_base()); - arch_send_wakeup_ipi_mask(cpumask_of(1)); + ret = exynos_set_boot_addr(1, boot_addr); + if (ret) + goto fail; + + call_firmware_op(cpu_boot, 1); + + if (soc_is_exynos3250()) + dsb_sev(); + else + arch_send_wakeup_ipi_mask(cpumask_of(1)); } } - +fail: return ret; } static int exynos_wfi_finisher(unsigned long flags) { + if (soc_is_exynos3250()) + flush_cache_all(); cpu_do_idle(); return -1; @@ -283,6 +313,9 @@ static int exynos_cpu1_powerdown(void) */ exynos_cpu_power_down(1); + if (soc_is_exynos3250()) + pmu_raw_writel(0, S5P_PMU_SPARE2); + ret = cpu_suspend(0, exynos_wfi_finisher); cpu_pm_exit(); @@ -299,7 +332,9 @@ cpu1_aborted: static void exynos_pre_enter_aftr(void) { - __raw_writel(virt_to_phys(exynos_cpu_resume), cpu_boot_reg_base()); + unsigned long boot_addr = virt_to_phys(exynos_cpu_resume); + + (void)exynos_set_boot_addr(1, boot_addr); } static void exynos_post_enter_aftr(void) diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index a9686535f..4a87e86de 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -62,6 +62,7 @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { if (IS_ERR(pd->clk[i])) break; + pd->pclk[i] = clk_get_parent(pd->clk[i]); if (clk_set_parent(pd->clk[i], pd->oscclk)) pr_err("%s: error setting oscclk as parent to clock %d\n", pd->name, i); @@ -90,6 +91,9 @@ static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) for (i = 0; i < MAX_CLK_PER_DOMAIN; i++) { if (IS_ERR(pd->clk[i])) break; + + if (IS_ERR(pd->clk[i])) + continue; /* Skip on first power up */ if (clk_set_parent(pd->clk[i], pd->pclk[i])) pr_err("%s: error setting parent to clock%d\n", pd->name, i); @@ -117,27 +121,36 @@ static int exynos_pd_power_off(struct generic_pm_domain *domain) static __init int exynos4_pm_init_power_domain(void) { - struct platform_device *pdev; struct device_node *np; for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") { struct exynos_pm_domain *pd; int on, i; - struct device *dev; - - pdev = of_find_device_by_node(np); - dev = &pdev->dev; pd = kzalloc(sizeof(*pd), GFP_KERNEL); if (!pd) { pr_err("%s: failed to allocate memory for domain\n", __func__); + of_node_put(np); + return -ENOMEM; + } + pd->pd.name = kstrdup_const(strrchr(np->full_name, '/') + 1, + GFP_KERNEL); + if (!pd->pd.name) { + kfree(pd); + of_node_put(np); return -ENOMEM; } - pd->pd.name = kstrdup(dev_name(dev), GFP_KERNEL); pd->name = pd->pd.name; pd->base = of_iomap(np, 0); + if (!pd->base) { + pr_warn("%s: failed to map memory\n", __func__); + kfree_const(pd->pd.name); + kfree(pd); + continue; + } + pd->pd.power_off = exynos_pd_power_off; pd->pd.power_on = exynos_pd_power_on; @@ -145,12 +158,12 @@ static __init int exynos4_pm_init_power_domain(void) char clk_name[8]; snprintf(clk_name, sizeof(clk_name), "asb%d", i); - pd->asb_clk[i] = clk_get(dev, clk_name); + pd->asb_clk[i] = of_clk_get_by_name(np, clk_name); if (IS_ERR(pd->asb_clk[i])) break; } - pd->oscclk = clk_get(dev, "oscclk"); + pd->oscclk = of_clk_get_by_name(np, "oscclk"); if (IS_ERR(pd->oscclk)) goto no_clk; @@ -158,16 +171,14 @@ static __init int exynos4_pm_init_power_domain(void) char clk_name[8]; snprintf(clk_name, sizeof(clk_name), "clk%d", i); - pd->clk[i] = clk_get(dev, clk_name); + pd->clk[i] = of_clk_get_by_name(np, clk_name); if (IS_ERR(pd->clk[i])) break; - snprintf(clk_name, sizeof(clk_name), "pclk%d", i); - pd->pclk[i] = clk_get(dev, clk_name); - if (IS_ERR(pd->pclk[i])) { - clk_put(pd->clk[i]); - pd->clk[i] = ERR_PTR(-EINVAL); - break; - } + /* + * Skip setting parent on first power up. + * The parent at this time may not be useful at all. + */ + pd->pclk[i] = ERR_PTR(-EINVAL); } if (IS_ERR(pd->clk[0])) @@ -189,15 +200,15 @@ no_clk: args.args_count = 0; child_domain = of_genpd_get_from_provider(&args); if (IS_ERR(child_domain)) - continue; + goto next_pd; if (of_parse_phandle_with_args(np, "power-domains", "#power-domain-cells", 0, &args) != 0) - continue; + goto next_pd; parent_domain = of_genpd_get_from_provider(&args); if (IS_ERR(parent_domain)) - continue; + goto next_pd; if (pm_genpd_add_subdomain(parent_domain, child_domain)) pr_warn("%s failed to add subdomain: %s\n", @@ -205,9 +216,10 @@ no_clk: else pr_info("%s has as child subdomain: %s.\n", parent_domain->name, child_domain->name); +next_pd: of_node_put(np); } return 0; } -arch_initcall(exynos4_pm_init_power_domain); +core_initcall(exynos4_pm_init_power_domain); diff --git a/arch/arm/mach-exynos/pmu.c b/arch/arm/mach-exynos/pmu.c index c15761ca2..e812c1c85 100644 --- a/arch/arm/mach-exynos/pmu.c +++ b/arch/arm/mach-exynos/pmu.c @@ -681,7 +681,7 @@ static unsigned int const exynos5420_list_disable_pmu_reg[] = { EXYNOS5420_CMU_RESET_FSYS_SYS_PWR_REG, }; -static void exynos5_power_off(void) +static void exynos_power_off(void) { unsigned int tmp; @@ -872,8 +872,6 @@ static void exynos5420_pmu_init(void) EXYNOS5420_ARM_INTR_SPREAD_USE_STANDBYWFI); pmu_raw_writel(0x1, EXYNOS5420_UP_SCHEDULER); - - pm_power_off = exynos5_power_off; pr_info("EXYNOS5420 PMU initialized\n"); } @@ -984,6 +982,8 @@ static int exynos_pmu_probe(struct platform_device *pdev) if (ret) dev_warn(dev, "can't register restart handler err=%d\n", ret); + pm_power_off = exynos_power_off; + dev_dbg(dev, "Exynos PMU Driver probe done\n"); return 0; } diff --git a/arch/arm/mach-exynos/suspend.c b/arch/arm/mach-exynos/suspend.c index 7d23ce04c..f572219c7 100644 --- a/arch/arm/mach-exynos/suspend.c +++ b/arch/arm/mach-exynos/suspend.c @@ -223,7 +223,7 @@ static int exynos_pmu_domain_alloc(struct irq_domain *domain, return irq_domain_alloc_irqs_parent(domain, virq, nr_irqs, &parent_args); } -static struct irq_domain_ops exynos_pmu_domain_ops = { +static const struct irq_domain_ops exynos_pmu_domain_ops = { .xlate = exynos_pmu_domain_xlate, .alloc = exynos_pmu_domain_alloc, .free = irq_domain_free_irqs_common, @@ -311,13 +311,7 @@ static int exynos5420_cpu_suspend(unsigned long arg) if (IS_ENABLED(CONFIG_EXYNOS5420_MCPM)) { mcpm_set_entry_vector(cpu, cluster, exynos_cpu_resume); - - /* - * Residency value passed to mcpm_cpu_suspend back-end - * has to be given clear semantics. Set to 0 as a - * temporary value. - */ - mcpm_cpu_suspend(0); + mcpm_cpu_suspend(); } pr_info("Failed to suspend the system\n"); |