summaryrefslogtreecommitdiff
path: root/arch/arm/mach-qcom
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-qcom')
-rw-r--r--arch/arm/mach-qcom/Kconfig34
-rw-r--r--arch/arm/mach-qcom/Makefile2
-rw-r--r--arch/arm/mach-qcom/board.c73
-rw-r--r--arch/arm/mach-qcom/headsmp.S48
-rw-r--r--arch/arm/mach-qcom/hotplug.c160
-rw-r--r--arch/arm/mach-qcom/platsmp.c427
-rw-r--r--arch/arm/mach-qcom/platsmp.h26
-rw-r--r--arch/arm/mach-qcom/scm-boot.c55
-rw-r--r--arch/arm/mach-qcom/scm.c81
-rw-r--r--arch/arm/mach-qcom/scm.h28
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