diff options
Diffstat (limited to 'arch/arm/mach-qcom')
-rw-r--r-- | arch/arm/mach-qcom/Kconfig | 34 | ||||
-rw-r--r-- | arch/arm/mach-qcom/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-qcom/board.c | 73 | ||||
-rw-r--r-- | arch/arm/mach-qcom/headsmp.S | 48 | ||||
-rw-r--r-- | arch/arm/mach-qcom/hotplug.c | 160 | ||||
-rw-r--r-- | arch/arm/mach-qcom/platsmp.c | 427 | ||||
-rw-r--r-- | arch/arm/mach-qcom/platsmp.h | 26 | ||||
-rw-r--r-- | arch/arm/mach-qcom/scm-boot.c | 55 | ||||
-rw-r--r-- | arch/arm/mach-qcom/scm.c | 81 | ||||
-rw-r--r-- | arch/arm/mach-qcom/scm.h | 28 |
10 files changed, 653 insertions, 281 deletions
diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig index ee5697ba05bc..dbe148b0e625 100644 --- a/arch/arm/mach-qcom/Kconfig +++ b/arch/arm/mach-qcom/Kconfig @@ -23,6 +23,40 @@ config ARCH_MSM8974 bool "Enable support for MSM8974" select HAVE_ARM_ARCH_TIMER +config ARCH_MSM8916 + bool "MSM8916" + select ARCH_MSM_CORTEXMP + select HAVE_GENERIC_HARDIRQS + select USE_GENERIC_CPU_HELPERS + select ARM_GIC + select MULTI_IRQ_HANDLER + select CPU_V7 + select HAVE_ARM_ARCH_TIMER + select MAY_HAVE_SPARSE_IRQ + select SPARSE_IRQ + select HAVE_CLK + select HAVE_CLK_PREPARE + select CLKDEV_LOOKUP + select PINCTRL + select PINCTRL_MSM_TLMM_V4 + select USE_PINCTRL_IRQ + select MSM_PM if PM + select MSM_RPM_SMD + select MEMORY_HOLE_CARVEOUT + select DONT_MAP_HOLE_AFTER_MEMBANK0 + select QMI_ENCDEC + select MSM_IRQ + select MSM_CORTEX_A53 + #select CPU_FREQ_MSM + #select CPU_FREQ + select PM_DEVFREQ + select MSM_DEVFREQ_CPUBW + select ARM_HAS_SG_CHAIN + select ARCH_WANT_KMAP_ATOMIC_FLUSH + select SOC_BUS + select MSM_SCM + select MSM_SPM_V2 + config QCOM_SCM bool diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile index 8f756ae1ae31..55dba28ca36a 100644 --- a/arch/arm/mach-qcom/Makefile +++ b/arch/arm/mach-qcom/Makefile @@ -1,5 +1,5 @@ obj-y := board.o -obj-$(CONFIG_SMP) += platsmp.o +obj-$(CONFIG_SMP) += platsmp.o headsmp.o hotplug.o obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) diff --git a/arch/arm/mach-qcom/board.c b/arch/arm/mach-qcom/board.c index 6d8bbf7d39d8..5a5b9eb48f90 100644 --- a/arch/arm/mach-qcom/board.c +++ b/arch/arm/mach-qcom/board.c @@ -11,9 +11,42 @@ */ #include <linux/init.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_platform.h> +#include <linux/of_fdt.h> +#include <linux/of_irq.h> +#include <linux/cma.h> +#include <soc/qcom/socinfo.h> +#include <soc/qcom/rpm-smd.h> +#include <soc/qcom/smd.h> +#include <soc/qcom/smem.h> +#include <soc/qcom/spm.h> +#include <soc/qcom/pm.h> + +#include <linux/regulator/spm-regulator.h> +#include <linux/regulator/qpnp-regulator.h> + +#include <asm/mach/map.h> #include <asm/mach/arch.h> +#include "platsmp.h" + +#define MSM_CHIP_DEVICE_TYPE(name, chip, mem_type) { \ + .virtual = (unsigned long) MSM_##name##_BASE, \ + .pfn = __phys_to_pfn(chip##_##name##_PHYS), \ + .length = chip##_##name##_SIZE, \ + .type = mem_type, \ + } +#define MSM_CHIP_DEVICE(name, chip) \ + MSM_CHIP_DEVICE_TYPE(name, chip, MT_DEVICE) + +#define MSM_APCS_GCC_BASE IOMEM(0xFA006000) +#define MSM8916_APCS_GCC_PHYS 0xB011000 +#define MSM8916_APCS_GCC_SIZE SZ_4K + + static const char * const qcom_dt_match[] __initconst = { "qcom,apq8064", "qcom,apq8074-dragonboard", @@ -22,9 +55,49 @@ static const char * const qcom_dt_match[] __initconst = { "qcom,ipq8064", "qcom,msm8660-surf", "qcom,msm8960-cdp", + "qcom,msm8916", + NULL }; +static void __init msm8916_init(void) +{ + /* + * populate devices from DT first so smem probe will get called as part + * of msm_smem_init. socinfo_init needs smem support so call + * msm_smem_init before it. + */ + of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); + msm_smem_init(); + + if (socinfo_init() < 0) + pr_err("%s: socinfo_init() failed\n", __func__); + + msm_smd_init(); + msm_rpm_driver_init(); + spm_regulator_init(); + msm_spm_device_init(); + qpnp_regulator_init(); + msm_pm_sleep_status_init(); + +} +#ifdef CONFIG_ARCH_MSM8916 +static struct map_desc msm8916_io_desc[] __initdata = { + MSM_CHIP_DEVICE(APCS_GCC, MSM8916), +}; + +void __init msm_map_msm8916_io(void) +{ + iotable_init(msm8916_io_desc, ARRAY_SIZE(msm8916_io_desc)); + debug_ll_io_init(); +} +#endif /* CONFIG_ARCH_MSM8916 */ + + + DT_MACHINE_START(QCOM_DT, "Qualcomm (Flattened Device Tree)") + .map_io = msm_map_msm8916_io, + .init_machine = msm8916_init, .dt_compat = qcom_dt_match, + .smp = &msm8916_smp_ops, MACHINE_END diff --git a/arch/arm/mach-qcom/headsmp.S b/arch/arm/mach-qcom/headsmp.S new file mode 100644 index 000000000000..5d50606d3c3f --- /dev/null +++ b/arch/arm/mach-qcom/headsmp.S @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2003 ARM Limited + * All Rights Reserved + * Copyright (c) 2010, 2012, 2014 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/linkage.h> +#include <linux/init.h> + + .arm + +__CPUINIT + +/* + * MSM specific entry point for secondary CPUs. This provides + * a "holding pen" into which all secondary cores are held until we're + * ready for them to initialise. + * + * This is executing in physical space with cache's off. + */ +ENTRY(msm_secondary_startup) +THUMB( adr r9, BSYM(2f) ) @ Kernel is always entered in ARM. +THUMB( bx r9 ) @ If this is a Thumb-2 kernel, +THUMB( .thumb ) @ switch to Thumb now. +THUMB(2: ) + mrc p15, 0, r0, c0, c0, 5 @ MPIDR + bic r0, #0xff000000 @ What CPU am I + adr r4, 1f @ address of + ldmia r4, {r5, r6} @ load curr addr and pen_rel addr + sub r4, r4, r5 @ determine virtual/phys offsets + add r6, r6, r4 @ apply +pen: + ldr r7, [r6] @ pen_rel has cpu to remove from reset + cmp r7, r0 @ are we lucky? + bne pen + + /* + * we've been released from the holding pen: secondary_stack + * should now contain the SVC stack for this core + */ + b secondary_startup +ENDPROC(msm_secondary_startup) + +1: .long . + .long pen_release diff --git a/arch/arm/mach-qcom/hotplug.c b/arch/arm/mach-qcom/hotplug.c new file mode 100644 index 000000000000..d3dc455e2b67 --- /dev/null +++ b/arch/arm/mach-qcom/hotplug.c @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2002 ARM Ltd. + * All Rights Reserved + * Copyright (c) 2011-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/smp.h> +#include <linux/cpu.h> +#include <linux/notifier.h> +#include <linux/msm_rtb.h> +#include <soc/qcom/spm.h> +#include <soc/qcom/pm.h> + +#include <asm/smp_plat.h> +#include <asm/vfp.h> + +#include <soc/qcom/jtag.h> + +static cpumask_t cpu_dying_mask; + +static DEFINE_PER_CPU(unsigned int, warm_boot_flag); + +static inline void cpu_enter_lowpower(void) +{ +} + +static inline void cpu_leave_lowpower(void) +{ +} + +static inline void platform_do_lowpower(unsigned int cpu, int *spurious) +{ + /* Just enter wfi for now. TODO: Properly shut off the cpu. */ + for (;;) { + + lpm_cpu_hotplug_enter(cpu); + if (pen_release == cpu_logical_map(cpu)) { + /* + * OK, proper wakeup, we're done + */ + break; + } + + /* + * getting here, means that we have come out of WFI without + * having been woken up - this shouldn't happen + * + * The trouble is, letting people know about this is not really + * possible, since we are currently running incoherently, and + * therefore cannot safely call printk() or anything else + */ + (*spurious)++; + } +} + +int msm_cpu_kill(unsigned int cpu) +{ + int ret = 0; + + if (cpumask_test_and_clear_cpu(cpu, &cpu_dying_mask)) + ret = msm_pm_wait_cpu_shutdown(cpu); + + return ret ? 0 : 1; +} + +/* + * platform-specific code to shutdown a CPU + * + * Called with IRQs disabled + */ +void __ref msm_cpu_die(unsigned int cpu) +{ + int spurious = 0; + + if (unlikely(cpu != smp_processor_id())) { + pr_crit("%s: running on %u, should be %u\n", + __func__, smp_processor_id(), cpu); + BUG(); + } + /* + * we're ready for shutdown now, so do it + */ + cpu_enter_lowpower(); + platform_do_lowpower(cpu, &spurious); + + pr_debug("CPU%u: %s: normal wakeup\n", cpu, __func__); + cpu_leave_lowpower(); + + if (spurious) + pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious); +} + +#define CPU_SHIFT 0 +#define CPU_MASK 0xF +#define CPU_OF(n) (((n) & CPU_MASK) << CPU_SHIFT) +#define CPUSET_SHIFT 4 +#define CPUSET_MASK 0xFFFF +#define CPUSET_OF(n) (((n) & CPUSET_MASK) << CPUSET_SHIFT) + +static int hotplug_rtb_callback(struct notifier_block *nfb, + unsigned long action, void *hcpu) +{ + /* + * Bits [19:4] of the data are the online mask, lower 4 bits are the + * cpu number that is being changed. Additionally, changes to the + * online_mask that will be done by the current hotplug will be made + * even though they aren't necessarily in the online mask yet. + * + * XXX: This design is limited to supporting at most 16 cpus + */ + int this_cpumask = CPUSET_OF(1 << (int)hcpu); + int cpumask = CPUSET_OF(cpumask_bits(cpu_online_mask)[0]); + int cpudata = CPU_OF((int)hcpu) | cpumask; + + switch (action & (~CPU_TASKS_FROZEN)) { + case CPU_STARTING: + uncached_logk(LOGK_HOTPLUG, (void *)(cpudata | this_cpumask)); + break; + case CPU_DYING: + cpumask_set_cpu((unsigned long)hcpu, &cpu_dying_mask); + uncached_logk(LOGK_HOTPLUG, (void *)(cpudata & ~this_cpumask)); + break; + default: + break; + } + + return NOTIFY_OK; +} +static struct notifier_block hotplug_rtb_notifier = { + .notifier_call = hotplug_rtb_callback, +}; + +int msm_platform_secondary_init(unsigned int cpu) +{ + int ret; + unsigned int *warm_boot = &get_cpu_var(warm_boot_flag); + + if (!(*warm_boot)) { + *warm_boot = 1; + return 0; + } + msm_jtag_restore_state(); +#if defined(CONFIG_VFP) && defined (CONFIG_CPU_PM) + //vfp_pm_resume(); +#endif + ret = msm_spm_set_low_power_mode(MSM_SPM_MODE_CLOCK_GATING, false); + + return ret; +} + +static int __init init_hotplug(void) +{ + return register_hotcpu_notifier(&hotplug_rtb_notifier); +} +early_initcall(init_hotplug); diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c index d6908569ecaf..cb38cf76df3e 100644 --- a/arch/arm/mach-qcom/platsmp.c +++ b/arch/arm/mach-qcom/platsmp.c @@ -1,8 +1,7 @@ /* * Copyright (C) 2002 ARM Ltd. * All Rights Reserved - * Copyright (c) 2010, Code Aurora Forum. All rights reserved. - * Copyright (c) 2014 The Linux Foundation. All rights reserved. + * Copyright (c) 2010-2014, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -10,53 +9,60 @@ */ #include <linux/init.h> -#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/bitops.h> +#include <linux/cpumask.h> #include <linux/delay.h> -#include <linux/device.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/smp.h> +#include <linux/interrupt.h> #include <linux/io.h> - +#include <linux/regulator/krait-regulator.h> +#include <soc/qcom/spm.h> +#include <soc/qcom/pm.h> +#include <soc/qcom/scm-boot.h> + +#include <asm/cacheflush.h> +#include <asm/cputype.h> +#include <asm/mach-types.h> #include <asm/smp_plat.h> -#include "scm-boot.h" - -#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0 -#define SCSS_CPU1CORE_RESET 0x2d80 -#define SCSS_DBG_STATUS_CORE_PWRDUP 0x2e64 - -#define APCS_CPU_PWR_CTL 0x04 -#define PLL_CLAMP BIT(8) -#define CORE_PWRD_UP BIT(7) -#define COREPOR_RST BIT(5) -#define CORE_RST BIT(4) -#define L2DT_SLP BIT(3) -#define CLAMP BIT(0) - -#define APC_PWR_GATE_CTL 0x14 -#define BHS_CNT_SHIFT 24 -#define LDO_PWR_DWN_SHIFT 16 -#define LDO_BYP_SHIFT 8 -#define BHS_SEG_SHIFT 1 -#define BHS_EN BIT(0) +#include <soc/qcom/socinfo.h> +//#include <mach/hardware.h> +#include "../mach-msm/include/mach/msm_iomap.h" -#define APCS_SAW2_VCTL 0x14 -#define APCS_SAW2_2_VCTL 0x1c +#include "platsmp.h" -extern void secondary_startup(void); +#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0 +#define SCSS_CPU1CORE_RESET 0xD80 +#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64 +#define MSM8960_SAW2_BASE_ADDR 0x02089000 +#define MSM8962_SAW2_BASE_ADDR 0xF9089000 +#define APCS_ALIAS0_BASE_ADDR 0xF9088000 +#define MSM_APCS_GCC_BASE IOMEM(0xFA006000) +/* + * Write pen_release in a way that is guaranteed to be visible to all + * observers, irrespective of whether they're taking part in coherency + * or not. This is necessary for the hotplug code to work reliably. + */ +void __cpuinit write_pen_release(int val) +{ + pen_release = val; + smp_wmb(); + __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release)); + outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1)); +} static DEFINE_SPINLOCK(boot_lock); -#ifdef CONFIG_HOTPLUG_CPU -static void __ref qcom_cpu_die(unsigned int cpu) +void __cpuinit msm_secondary_init(unsigned int cpu) { - wfi(); -} -#endif + WARN_ON(msm_platform_secondary_init(cpu)); + + /* + * let the primary processor know we're out of the + * pen, then head off into the C entry point + */ + write_pen_release(-1); -static void qcom_secondary_init(unsigned int cpu) -{ /* * Synchronise with the boot thread. */ @@ -64,221 +70,47 @@ static void qcom_secondary_init(unsigned int cpu) spin_unlock(&boot_lock); } -static int scss_release_secondary(unsigned int cpu) -{ - struct device_node *node; - void __iomem *base; - - node = of_find_compatible_node(NULL, NULL, "qcom,gcc-msm8660"); - if (!node) { - pr_err("%s: can't find node\n", __func__); - return -ENXIO; - } - - base = of_iomap(node, 0); - of_node_put(node); - if (!base) - return -ENOMEM; - - writel_relaxed(0, base + VDD_SC1_ARRAY_CLAMP_GFS_CTL); - writel_relaxed(0, base + SCSS_CPU1CORE_RESET); - writel_relaxed(3, base + SCSS_DBG_STATUS_CORE_PWRDUP); - mb(); - iounmap(base); - - return 0; -} - -static int kpssv1_release_secondary(unsigned int cpu) +static int __cpuinit arm_release_secondary(unsigned long base, unsigned int cpu) { - int ret = 0; - void __iomem *reg, *saw_reg; - struct device_node *cpu_node, *acc_node, *saw_node; - u32 val; - - cpu_node = of_get_cpu_node(cpu, NULL); - if (!cpu_node) + void *base_ptr = ioremap_nocache(base + (cpu * 0x10000), SZ_4K); + if (!base_ptr) return -ENODEV; - acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); - if (!acc_node) { - ret = -ENODEV; - goto out_acc; - } - - saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); - if (!saw_node) { - ret = -ENODEV; - goto out_saw; - } - - reg = of_iomap(acc_node, 0); - if (!reg) { - ret = -ENOMEM; - goto out_acc_map; - } - - saw_reg = of_iomap(saw_node, 0); - if (!saw_reg) { - ret = -ENOMEM; - goto out_saw_map; - } - - /* Turn on CPU rail */ - writel_relaxed(0xA4, saw_reg + APCS_SAW2_VCTL); - mb(); - udelay(512); - - /* Krait bring-up sequence */ - val = PLL_CLAMP | L2DT_SLP | CLAMP; - writel_relaxed(val, reg + APCS_CPU_PWR_CTL); - val &= ~L2DT_SLP; - writel_relaxed(val, reg + APCS_CPU_PWR_CTL); - mb(); - ndelay(300); - - val |= COREPOR_RST; - writel_relaxed(val, reg + APCS_CPU_PWR_CTL); + writel_relaxed(0x00000033, base_ptr+0x04); mb(); - udelay(2); - val &= ~CLAMP; - writel_relaxed(val, reg + APCS_CPU_PWR_CTL); + writel_relaxed(0x10000001, base_ptr+0x14); mb(); udelay(2); - val &= ~COREPOR_RST; - writel_relaxed(val, reg + APCS_CPU_PWR_CTL); - mb(); - udelay(100); - - val |= CORE_PWRD_UP; - writel_relaxed(val, reg + APCS_CPU_PWR_CTL); - mb(); - - iounmap(saw_reg); -out_saw_map: - iounmap(reg); -out_acc_map: - of_node_put(saw_node); -out_saw: - of_node_put(acc_node); -out_acc: - of_node_put(cpu_node); - return ret; -} - -static int kpssv2_release_secondary(unsigned int cpu) -{ - void __iomem *reg; - struct device_node *cpu_node, *l2_node, *acc_node, *saw_node; - void __iomem *l2_saw_base; - unsigned reg_val; - int ret; - - cpu_node = of_get_cpu_node(cpu, NULL); - if (!cpu_node) - return -ENODEV; - - acc_node = of_parse_phandle(cpu_node, "qcom,acc", 0); - if (!acc_node) { - ret = -ENODEV; - goto out_acc; - } - - l2_node = of_parse_phandle(cpu_node, "next-level-cache", 0); - if (!l2_node) { - ret = -ENODEV; - goto out_l2; - } - - saw_node = of_parse_phandle(l2_node, "qcom,saw", 0); - if (!saw_node) { - ret = -ENODEV; - goto out_saw; - } - - reg = of_iomap(acc_node, 0); - if (!reg) { - ret = -ENOMEM; - goto out_map; - } - - l2_saw_base = of_iomap(saw_node, 0); - if (!l2_saw_base) { - ret = -ENOMEM; - goto out_saw_map; - } - - /* Turn on the BHS, turn off LDO Bypass and power down LDO */ - reg_val = (64 << BHS_CNT_SHIFT) | (0x3f << LDO_PWR_DWN_SHIFT) | BHS_EN; - writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); - mb(); - /* wait for the BHS to settle */ - udelay(1); - - /* Turn on BHS segments */ - reg_val |= 0x3f << BHS_SEG_SHIFT; - writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); - mb(); - /* wait for the BHS to settle */ - udelay(1); - - /* Finally turn on the bypass so that BHS supplies power */ - reg_val |= 0x3f << LDO_BYP_SHIFT; - writel_relaxed(reg_val, reg + APC_PWR_GATE_CTL); - - /* enable max phases */ - writel_relaxed(0x10003, l2_saw_base + APCS_SAW2_2_VCTL); + writel_relaxed(0x00000031, base_ptr+0x04); mb(); - udelay(50); - reg_val = COREPOR_RST | CLAMP; - writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); + writel_relaxed(0x00000039, base_ptr+0x04); mb(); udelay(2); - reg_val &= ~CLAMP; - writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); + writel_relaxed(0x00020038, base_ptr+0x04); mb(); udelay(2); - reg_val &= ~COREPOR_RST; - writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); + + writel_relaxed(0x00020008, base_ptr+0x04); mb(); - reg_val |= CORE_PWRD_UP; - writel_relaxed(reg_val, reg + APCS_CPU_PWR_CTL); + writel_relaxed(0x00020088, base_ptr+0x04); mb(); - ret = 0; - - iounmap(l2_saw_base); -out_saw_map: - iounmap(reg); -out_map: - of_node_put(saw_node); -out_saw: - of_node_put(l2_node); -out_l2: - of_node_put(acc_node); -out_acc: - of_node_put(cpu_node); - - return ret; + iounmap(base_ptr); + return 0; } -static DEFINE_PER_CPU(int, cold_boot_done); - -static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) +static int __cpuinit release_from_pen(unsigned int cpu) { - int ret = 0; + unsigned long timeout; - if (!per_cpu(cold_boot_done, cpu)) { - ret = func(cpu); - if (!ret) - per_cpu(cold_boot_done, cpu) = true; - } + /* Set preset_lpj to avoid subsequent lpj recalculations */ + preset_lpj = loops_per_jiffy; /* * set synchronisation state between this boot processor @@ -287,92 +119,129 @@ static int qcom_boot_secondary(unsigned int cpu, int (*func)(unsigned int)) spin_lock(&boot_lock); /* + * The secondary processor is waiting to be released from + * the holding pen - release it, then wait for it to flag + * that it has been released by resetting pen_release. + * + * Note that "pen_release" is the hardware CPU ID, whereas + * "cpu" is Linux's internal ID. + */ + write_pen_release(cpu_logical_map(cpu)); + + /* * Send the secondary CPU a soft interrupt, thereby causing * the boot monitor to read the system wide flags register, * and branch to the address found there. */ arch_send_wakeup_ipi_mask(cpumask_of(cpu)); + timeout = jiffies + (1 * HZ); + while (time_before(jiffies, timeout)) { + smp_rmb(); + if (pen_release == -1) + break; + + udelay(10); + } + /* * now the secondary core is starting up let it run its * calibrations, then wait for it to finish */ spin_unlock(&boot_lock); - return ret; + return pen_release != -1 ? -ENOSYS : 0; } -static int msm8660_boot_secondary(unsigned int cpu, struct task_struct *idle) +DEFINE_PER_CPU(int, cold_boot_done); + +static int __cpuinit msm8916_boot_secondary(unsigned int cpu, + struct task_struct *idle) { - return qcom_boot_secondary(cpu, scss_release_secondary); + pr_info("Starting secondary CPU %d\n", cpu); + + if (per_cpu(cold_boot_done, cpu) == false) { + arm_release_secondary(0xb088000, cpu); + + per_cpu(cold_boot_done, cpu) = true; + } + return release_from_pen(cpu); } -static int kpssv1_boot_secondary(unsigned int cpu, struct task_struct *idle) +static void __init arm_smp_init_cpus(void) { - return qcom_boot_secondary(cpu, kpssv1_release_secondary); + unsigned int i, ncores; + + pr_info("%s\n", __func__); + + ncores = (__raw_readl(MSM_APCS_GCC_BASE + 0x30)) & 0xF; + + if (ncores > nr_cpu_ids) { + pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", + ncores, nr_cpu_ids); + ncores = nr_cpu_ids; + } + + for (i = 0; i < ncores; i++) + set_cpu_possible(i, true); } -static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle) +static int cold_boot_flags[] __initdata = { + 0, + SCM_FLAG_COLDBOOT_CPU1, + SCM_FLAG_COLDBOOT_CPU2, + SCM_FLAG_COLDBOOT_CPU3, +}; + +static void __init msm_platform_smp_prepare_cpus_mc(unsigned int max_cpus) { - return qcom_boot_secondary(cpu, kpssv2_release_secondary); + int cpu, map; + u32 aff0_mask = 0; + u32 aff1_mask = 0; + u32 aff2_mask = 0; + + for_each_present_cpu(cpu) { + map = cpu_logical_map(cpu); + aff0_mask |= BIT(MPIDR_AFFINITY_LEVEL(map, 0)); + aff1_mask |= BIT(MPIDR_AFFINITY_LEVEL(map, 1)); + aff2_mask |= BIT(MPIDR_AFFINITY_LEVEL(map, 2)); + } + + if (scm_set_boot_addr_mc(virt_to_phys(msm_secondary_startup), + aff0_mask, aff1_mask, aff2_mask, SCM_FLAG_COLDBOOT_MC)) + pr_warn("Failed to set CPU boot address\n"); } -static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) +static void __init msm_platform_smp_prepare_cpus(unsigned int max_cpus) { int cpu, map; unsigned int flags = 0; - static const int cold_boot_flags[] = { - 0, - SCM_FLAG_COLDBOOT_CPU1, - SCM_FLAG_COLDBOOT_CPU2, - SCM_FLAG_COLDBOOT_CPU3, - }; + + if (scm_is_mc_boot_available()) + return msm_platform_smp_prepare_cpus_mc(max_cpus); for_each_present_cpu(cpu) { map = cpu_logical_map(cpu); - if (WARN_ON(map >= ARRAY_SIZE(cold_boot_flags))) { + if (map > ARRAY_SIZE(cold_boot_flags)) { set_cpu_present(cpu, false); + __WARN(); continue; } flags |= cold_boot_flags[map]; } - if (scm_set_boot_addr(virt_to_phys(secondary_startup), flags)) { - for_each_present_cpu(cpu) { - if (cpu == smp_processor_id()) - continue; - set_cpu_present(cpu, false); - } - pr_warn("Failed to set CPU boot address, disabling SMP\n"); - } + if (scm_set_boot_addr(virt_to_phys(msm_secondary_startup), flags)) + pr_warn("Failed to set CPU boot address\n"); } -static struct smp_operations smp_msm8660_ops __initdata = { - .smp_prepare_cpus = qcom_smp_prepare_cpus, - .smp_secondary_init = qcom_secondary_init, - .smp_boot_secondary = msm8660_boot_secondary, -#ifdef CONFIG_HOTPLUG_CPU - .cpu_die = qcom_cpu_die, -#endif -}; -CPU_METHOD_OF_DECLARE(qcom_smp, "qcom,gcc-msm8660", &smp_msm8660_ops); - -static struct smp_operations qcom_smp_kpssv1_ops __initdata = { - .smp_prepare_cpus = qcom_smp_prepare_cpus, - .smp_secondary_init = qcom_secondary_init, - .smp_boot_secondary = kpssv1_boot_secondary, -#ifdef CONFIG_HOTPLUG_CPU - .cpu_die = qcom_cpu_die, +struct smp_operations msm8916_smp_ops __initdata = { + .smp_init_cpus = arm_smp_init_cpus, + .smp_prepare_cpus = msm_platform_smp_prepare_cpus, + .smp_secondary_init = msm_secondary_init, + .smp_boot_secondary = msm8916_boot_secondary, +#ifdef CONFIG_HOTPLUG + .cpu_die = msm_cpu_die, + .cpu_kill = msm_cpu_kill, #endif }; -CPU_METHOD_OF_DECLARE(qcom_smp_kpssv1, "qcom,kpss-acc-v1", &qcom_smp_kpssv1_ops); - -static struct smp_operations qcom_smp_kpssv2_ops __initdata = { - .smp_prepare_cpus = qcom_smp_prepare_cpus, - .smp_secondary_init = qcom_secondary_init, - .smp_boot_secondary = kpssv2_boot_secondary, -#ifdef CONFIG_HOTPLUG_CPU - .cpu_die = qcom_cpu_die, -#endif -}; -CPU_METHOD_OF_DECLARE(qcom_smp_kpssv2, "qcom,kpss-acc-v2", &qcom_smp_kpssv2_ops); + diff --git a/arch/arm/mach-qcom/platsmp.h b/arch/arm/mach-qcom/platsmp.h new file mode 100644 index 000000000000..c321069a5c9c --- /dev/null +++ b/arch/arm/mach-qcom/platsmp.h @@ -0,0 +1,26 @@ +/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +void msm_secondary_startup(void); +void write_pen_release(int val); + +void msm_cpu_die(unsigned int cpu); +int msm_cpu_kill(unsigned int cpu); + +extern struct smp_operations arm_smp_ops; +extern struct smp_operations msm8960_smp_ops; +extern struct smp_operations msm8974_smp_ops; +extern struct smp_operations msm8962_smp_ops; +extern struct smp_operations msm8625_smp_ops; +extern struct smp_operations scorpion_smp_ops; +extern struct smp_operations msm8916_smp_ops; +extern struct smp_operations msm8936_smp_ops; diff --git a/arch/arm/mach-qcom/scm-boot.c b/arch/arm/mach-qcom/scm-boot.c index 45cee3e469a5..2110979b9e62 100644 --- a/arch/arm/mach-qcom/scm-boot.c +++ b/arch/arm/mach-qcom/scm-boot.c @@ -21,6 +21,15 @@ #include "scm.h" #include "scm-boot.h" +#define SCM_BOOT_ADDR_MC 0x11 + +#ifdef CONFIG_ARM64 +#define SCM_FLAG_HLOS 0x01 +#else +#define SCM_FLAG_HLOS 0x0 +#endif + + /* * Set the cold/warm boot address for one of the CPU cores. */ @@ -37,3 +46,49 @@ int scm_set_boot_addr(phys_addr_t addr, int flags) &cmd, sizeof(cmd), NULL, 0); } EXPORT_SYMBOL(scm_set_boot_addr); + +/** + * scm_set_boot_addr_mc - Set entry physical address for cpus + * @addr: 32bit physical address + * @aff0: Collective bitmask of the affinity-level-0 of the mpidr + * 1<<aff0_CPU0| 1<<aff0_CPU1....... | 1<<aff0_CPU32 + * Supports maximum 32 cpus under any affinity level. + * @aff1: Collective bitmask of the affinity-level-1 of the mpidr + * @aff2: Collective bitmask of the affinity-level-2 of the mpidr + * @flags: Flag to differentiate between coldboot vs warmboot + */ +int scm_set_boot_addr_mc(phys_addr_t addr, u32 aff0, + u32 aff1, u32 aff2, u32 flags) +{ + struct { + u32 addr; + u32 aff0; + u32 aff1; + u32 aff2; + u32 reserved; + u32 flags; + } cmd; + + cmd.addr = addr; + cmd.aff0 = aff0; + cmd.aff1 = aff1; + cmd.aff2 = aff2; + /* Reserved for future chips with affinity level 3 effectively 1 << 0 */ + cmd.reserved = ~0U; + cmd.flags = flags | SCM_FLAG_HLOS; + return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR_MC, + &cmd, sizeof(cmd), NULL, 0); +} +EXPORT_SYMBOL(scm_set_boot_addr_mc); + +/** + * scm_is_mc_boot_available - + * Checks if TZ supports the boot API for multi-cluster configuration + * Returns true if available and false otherwise + */ +int scm_is_mc_boot_available(void) +{ + return scm_is_call_available(SCM_SVC_BOOT, SCM_BOOT_ADDR_MC); +} +EXPORT_SYMBOL(scm_is_mc_boot_available); + diff --git a/arch/arm/mach-qcom/scm.c b/arch/arm/mach-qcom/scm.c index c536fd6bf827..eae84a9b1ed5 100644 --- a/arch/arm/mach-qcom/scm.c +++ b/arch/arm/mach-qcom/scm.c @@ -82,6 +82,32 @@ struct scm_response { u32 is_complete; }; +#ifdef CONFIG_ARM64 + +#define R0_STR "x0" +#define R1_STR "x1" +#define R2_STR "x2" +#define R3_STR "x3" +#define R4_STR "x4" + +/* Outer caches unsupported on ARM64 platforms */ +#define outer_inv_range(x, y) +#define outer_flush_range(x, y) + +#define __cpuc_flush_dcache_area __flush_dcache_area + +#else + +#define R0_STR "r0" +#define R1_STR "r1" +#define R2_STR "r2" +#define R3_STR "r3" +#define R4_STR "r4" + +#endif + + + /** * alloc_scm_command() - Allocate an SCM command * @cmd_size: size of the command buffer @@ -297,3 +323,58 @@ u32 scm_get_version(void) return version; } EXPORT_SYMBOL(scm_get_version); + +#define SCM_CLASS_REGISTER (0x2 << 8) +#define SCM_MASK_IRQS BIT(5) +#define SCM_ATOMIC(svc, cmd, n) (((((svc) << 10)|((cmd) & 0x3ff)) << 12) | \ + SCM_CLASS_REGISTER | \ + SCM_MASK_IRQS | \ + (n & 0xf)) + +/** + * scm_call_atomic1() - Send an atomic SCM command with one argument + * @svc_id: service identifier + * @cmd_id: command identifier + * @arg1: first argument + * + * This shall only be used with commands that are guaranteed to be + * uninterruptable, atomic and SMP safe. + */ +s32 scm_call_atomic1(u32 svc, u32 cmd, u32 arg1) +{ + int context_id; + register u32 r0 asm("r0") = SCM_ATOMIC(svc, cmd, 1); + register u32 r1 asm("r1") = (uintptr_t)&context_id; + register u32 r2 asm("r2") = arg1; + + asm volatile( + __asmeq("%0", R0_STR) + __asmeq("%1", R0_STR) + __asmeq("%2", R1_STR) + __asmeq("%3", R2_STR) +#ifdef REQUIRES_SEC + ".arch_extension sec\n" +#endif + "smc #0\n" + : "=r" (r0) + : "r" (r0), "r" (r1), "r" (r2) + : "r3"); + return r0; +} +EXPORT_SYMBOL(scm_call_atomic1); + +#define IS_CALL_AVAIL_CMD 1 +int scm_is_call_available(u32 svc_id, u32 cmd_id) +{ + int ret; + u32 svc_cmd = (svc_id << 10) | cmd_id; + u32 ret_val = 0; + + ret = scm_call(SCM_SVC_INFO, IS_CALL_AVAIL_CMD, &svc_cmd, + sizeof(svc_cmd), &ret_val, sizeof(ret_val)); + if (ret) + return ret; + + return ret_val; +} +EXPORT_SYMBOL(scm_is_call_available); diff --git a/arch/arm/mach-qcom/scm.h b/arch/arm/mach-qcom/scm.h index 00b31ea58f29..2661e598dc69 100644 --- a/arch/arm/mach-qcom/scm.h +++ b/arch/arm/mach-qcom/scm.h @@ -14,12 +14,38 @@ #define SCM_SVC_BOOT 0x1 #define SCM_SVC_PIL 0x2 +#define SCM_SVC_UTIL 0x3 +#define SCM_SVC_TZ 0x4 +#define SCM_SVC_IO 0x5 +#define SCM_SVC_INFO 0x6 +#define SCM_SVC_SSD 0x7 +#define SCM_SVC_FUSE 0x8 +#define SCM_SVC_PWR 0x9 +#define SCM_SVC_MP 0xC +#define SCM_SVC_DCVS 0xD +#define SCM_SVC_ES 0x10 +#define SCM_SVC_HDCP 0x11 +#define SCM_SVC_TZSCHEDULER 0xFC +#define SCM_FUSE_READ 0x7 +#define SCM_CMD_HDCP 0x01 + +/* SCM Features */ +#define SCM_SVC_SEC_CAMERA 0xD + +#define DEFINE_SCM_BUFFER(__n) \ +static char __n[PAGE_SIZE] __aligned(PAGE_SIZE); + +#define SCM_BUFFER_SIZE(__buf) sizeof(__buf) + +#define SCM_BUFFER_PHYS(__buf) virt_to_phys(__buf) + +# extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, void *resp_buf, size_t resp_len); #define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) extern u32 scm_get_version(void); - +extern int scm_is_call_available(u32 svc_id, u32 cmd_id); #endif |