aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2011-01-10 17:50:08 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2011-01-10 17:50:08 -0500
commitabf8792d0e1b203e303ed1c02437e0e10a39dcda (patch)
tree8f0a9c4db37c6e776adf98e1a066f8dcb889e22d /arch/arm
parente0e736fc0d33861335e2a132e4f688f7fd380c61 (diff)
parente14411da420bad7bdaae65cccd8787674e6c565e (diff)
Merge branch 'msm-smp' of git://codeaurora.org/quic/kernel/davidb/linux-msm
* 'msm-smp' of git://codeaurora.org/quic/kernel/davidb/linux-msm: msm: add SMP support for msm msm: hotplug: support cpu hotplug on msm msm: timer: SMP timer support for msm msm: scm-boot: Support for setting cold/warm boot addresses msm: Secure Channel Manager (SCM) support
Diffstat (limited to 'arch/arm')
-rw-r--r--arch/arm/mach-msm/Kconfig5
-rw-r--r--arch/arm/mach-msm/Makefile4
-rw-r--r--arch/arm/mach-msm/headsmp.S40
-rw-r--r--arch/arm/mach-msm/hotplug.c91
-rw-r--r--arch/arm/mach-msm/include/mach/msm_iomap-8x60.h6
-rw-r--r--arch/arm/mach-msm/io.c1
-rw-r--r--arch/arm/mach-msm/platsmp.c166
-rw-r--r--arch/arm/mach-msm/scm-boot.c39
-rw-r--r--arch/arm/mach-msm/scm-boot.h38
-rw-r--r--arch/arm/mach-msm/scm.c287
-rw-r--r--arch/arm/mach-msm/scm.h41
-rw-r--r--arch/arm/mach-msm/timer.c125
12 files changed, 819 insertions, 24 deletions
diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index fae931ac2e56..5d3d9ade12fb 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -40,11 +40,13 @@ config ARCH_MSM8X60
40 bool "MSM8X60" 40 bool "MSM8X60"
41 select MACH_MSM8X60_SURF if (!MACH_MSM8X60_RUMI3 && !MACH_MSM8X60_SIM \ 41 select MACH_MSM8X60_SURF if (!MACH_MSM8X60_RUMI3 && !MACH_MSM8X60_SIM \
42 && !MACH_MSM8X60_FFA) 42 && !MACH_MSM8X60_FFA)
43 select ARCH_MSM_SCORPIONMP
43 select ARM_GIC 44 select ARM_GIC
44 select CPU_V7 45 select CPU_V7
45 select MSM_V2_TLMM 46 select MSM_V2_TLMM
46 select MSM_GPIOMUX 47 select MSM_GPIOMUX
47 select IOMMU_API 48 select IOMMU_API
49 select MSM_SCM if SMP
48 50
49endchoice 51endchoice
50 52
@@ -172,4 +174,7 @@ config MSM_V2_TLMM
172 174
173config IOMMU_API 175config IOMMU_API
174 bool 176 bool
177
178config MSM_SCM
179 bool
175endif 180endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 59646bbd6195..94195c190e13 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -18,6 +18,10 @@ obj-$(CONFIG_MSM_PROC_COMM) += clock.o
18obj-$(CONFIG_ARCH_QSD8X50) += sirc.o 18obj-$(CONFIG_ARCH_QSD8X50) += sirc.o
19obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o 19obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o
20obj-$(CONFIG_MSM_SMD) += last_radio_log.o 20obj-$(CONFIG_MSM_SMD) += last_radio_log.o
21obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o
22
23obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
24obj-$(CONFIG_SMP) += headsmp.o platsmp.o
21 25
22obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o 26obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o
23obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o 27obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o
diff --git a/arch/arm/mach-msm/headsmp.S b/arch/arm/mach-msm/headsmp.S
new file mode 100644
index 000000000000..d0c214338df9
--- /dev/null
+++ b/arch/arm/mach-msm/headsmp.S
@@ -0,0 +1,40 @@
1/*
2 * linux/arch/arm/mach-realview/headsmp.S
3 *
4 * Copyright (c) 2003 ARM Limited
5 * All Rights Reserved
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
10 */
11#include <linux/linkage.h>
12#include <linux/init.h>
13
14 __INIT
15
16/*
17 * MSM specific entry point for secondary CPUs. This provides
18 * a "holding pen" into which all secondary cores are held until we're
19 * ready for them to initialise.
20 */
21ENTRY(msm_secondary_startup)
22 mrc p15, 0, r0, c0, c0, 5
23 and r0, r0, #15
24 adr r4, 1f
25 ldmia r4, {r5, r6}
26 sub r4, r4, r5
27 add r6, r6, r4
28pen: ldr r7, [r6]
29 cmp r7, r0
30 bne pen
31
32 /*
33 * we've been released from the holding pen: secondary_stack
34 * should now contain the SVC stack for this core
35 */
36 b secondary_startup
37
38 .align
391: .long .
40 .long pen_release
diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c
new file mode 100644
index 000000000000..5a31f70dfb8e
--- /dev/null
+++ b/arch/arm/mach-msm/hotplug.c
@@ -0,0 +1,91 @@
1/*
2 * Copyright (C) 2002 ARM Ltd.
3 * All Rights Reserved
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 */
9#include <linux/kernel.h>
10#include <linux/errno.h>
11#include <linux/smp.h>
12
13#include <asm/cacheflush.h>
14
15extern volatile int pen_release;
16
17static inline void cpu_enter_lowpower(void)
18{
19 /* Just flush the cache. Changing the coherency is not yet
20 * available on msm. */
21 flush_cache_all();
22}
23
24static inline void cpu_leave_lowpower(void)
25{
26}
27
28static inline void platform_do_lowpower(unsigned int cpu)
29{
30 /* Just enter wfi for now. TODO: Properly shut off the cpu. */
31 for (;;) {
32 /*
33 * here's the WFI
34 */
35 asm("wfi"
36 :
37 :
38 : "memory", "cc");
39
40 if (pen_release == cpu) {
41 /*
42 * OK, proper wakeup, we're done
43 */
44 break;
45 }
46
47 /*
48 * getting here, means that we have come out of WFI without
49 * having been woken up - this shouldn't happen
50 *
51 * The trouble is, letting people know about this is not really
52 * possible, since we are currently running incoherently, and
53 * therefore cannot safely call printk() or anything else
54 */
55 pr_debug("CPU%u: spurious wakeup call\n", cpu);
56 }
57}
58
59int platform_cpu_kill(unsigned int cpu)
60{
61 return 1;
62}
63
64/*
65 * platform-specific code to shutdown a CPU
66 *
67 * Called with IRQs disabled
68 */
69void platform_cpu_die(unsigned int cpu)
70{
71 /*
72 * we're ready for shutdown now, so do it
73 */
74 cpu_enter_lowpower();
75 platform_do_lowpower(cpu);
76
77 /*
78 * bring this CPU back into the world of cache
79 * coherency, and then restore interrupts
80 */
81 cpu_leave_lowpower();
82}
83
84int platform_cpu_disable(unsigned int cpu)
85{
86 /*
87 * we don't allow CPU 0 to be shutdown (it is still too special
88 * e.g. clock tick interrupts)
89 */
90 return cpu == 0 ? -EPERM : 0;
91}
diff --git a/arch/arm/mach-msm/include/mach/msm_iomap-8x60.h b/arch/arm/mach-msm/include/mach/msm_iomap-8x60.h
index 7c43a9bff1a9..a54e33b0882e 100644
--- a/arch/arm/mach-msm/include/mach/msm_iomap-8x60.h
+++ b/arch/arm/mach-msm/include/mach/msm_iomap-8x60.h
@@ -60,7 +60,11 @@
60 60
61#define MSM_TMR_BASE IOMEM(0xF0200000) 61#define MSM_TMR_BASE IOMEM(0xF0200000)
62#define MSM_TMR_PHYS 0x02000000 62#define MSM_TMR_PHYS 0x02000000
63#define MSM_TMR_SIZE (SZ_1M) 63#define MSM_TMR_SIZE SZ_4K
64
65#define MSM_TMR0_BASE IOMEM(0xF0201000)
66#define MSM_TMR0_PHYS 0x02040000
67#define MSM_TMR0_SIZE SZ_4K
64 68
65#define MSM_GPT_BASE (MSM_TMR_BASE + 0x4) 69#define MSM_GPT_BASE (MSM_TMR_BASE + 0x4)
66#define MSM_DGT_BASE (MSM_TMR_BASE + 0x24) 70#define MSM_DGT_BASE (MSM_TMR_BASE + 0x24)
diff --git a/arch/arm/mach-msm/io.c b/arch/arm/mach-msm/io.c
index f912d7bf1889..800f327a7ecc 100644
--- a/arch/arm/mach-msm/io.c
+++ b/arch/arm/mach-msm/io.c
@@ -105,6 +105,7 @@ static struct map_desc msm8x60_io_desc[] __initdata = {
105 MSM_DEVICE(QGIC_DIST), 105 MSM_DEVICE(QGIC_DIST),
106 MSM_DEVICE(QGIC_CPU), 106 MSM_DEVICE(QGIC_CPU),
107 MSM_DEVICE(TMR), 107 MSM_DEVICE(TMR),
108 MSM_DEVICE(TMR0),
108 MSM_DEVICE(ACC), 109 MSM_DEVICE(ACC),
109 MSM_DEVICE(GCC), 110 MSM_DEVICE(GCC),
110}; 111};
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
new file mode 100644
index 000000000000..0f427bc94447
--- /dev/null
+++ b/arch/arm/mach-msm/platsmp.c
@@ -0,0 +1,166 @@
1/*
2 * Copyright (C) 2002 ARM Ltd.
3 * All Rights Reserved
4 * Copyright (c) 2010, Code Aurora Forum. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
9 */
10
11#include <linux/init.h>
12#include <linux/errno.h>
13#include <linux/delay.h>
14#include <linux/device.h>
15#include <linux/jiffies.h>
16#include <linux/smp.h>
17#include <linux/io.h>
18
19#include <asm/hardware/gic.h>
20#include <asm/cacheflush.h>
21#include <asm/mach-types.h>
22
23#include <mach/msm_iomap.h>
24
25#include "scm-boot.h"
26
27#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0
28#define SCSS_CPU1CORE_RESET 0xD80
29#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64
30
31/* Mask for edge trigger PPIs except AVS_SVICINT and AVS_SVICINTSWDONE */
32#define GIC_PPI_EDGE_MASK 0xFFFFD7FF
33
34extern void msm_secondary_startup(void);
35/*
36 * control for which core is the next to come out of the secondary
37 * boot "holding pen".
38 */
39volatile int pen_release = -1;
40
41static DEFINE_SPINLOCK(boot_lock);
42
43void __cpuinit platform_secondary_init(unsigned int cpu)
44{
45 /* Configure edge-triggered PPIs */
46 writel(GIC_PPI_EDGE_MASK, MSM_QGIC_DIST_BASE + GIC_DIST_CONFIG + 4);
47
48 /*
49 * if any interrupts are already enabled for the primary
50 * core (e.g. timer irq), then they will not have been enabled
51 * for us: do so
52 */
53 gic_secondary_init(0);
54
55 /*
56 * let the primary processor know we're out of the
57 * pen, then head off into the C entry point
58 */
59 pen_release = -1;
60 smp_wmb();
61
62 /*
63 * Synchronise with the boot thread.
64 */
65 spin_lock(&boot_lock);
66 spin_unlock(&boot_lock);
67}
68
69static __cpuinit void prepare_cold_cpu(unsigned int cpu)
70{
71 int ret;
72 ret = scm_set_boot_addr(virt_to_phys(msm_secondary_startup),
73 SCM_FLAG_COLDBOOT_CPU1);
74 if (ret == 0) {
75 void *sc1_base_ptr;
76 sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2);
77 if (sc1_base_ptr) {
78 writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
79 writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET);
80 writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP);
81 iounmap(sc1_base_ptr);
82 }
83 } else
84 printk(KERN_DEBUG "Failed to set secondary core boot "
85 "address\n");
86}
87
88int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
89{
90 unsigned long timeout;
91 static int cold_boot_done;
92
93 /* Only need to bring cpu out of reset this way once */
94 if (cold_boot_done == false) {
95 prepare_cold_cpu(cpu);
96 cold_boot_done = true;
97 }
98
99 /*
100 * set synchronisation state between this boot processor
101 * and the secondary one
102 */
103 spin_lock(&boot_lock);
104
105 /*
106 * The secondary processor is waiting to be released from
107 * the holding pen - release it, then wait for it to flag
108 * that it has been released by resetting pen_release.
109 *
110 * Note that "pen_release" is the hardware CPU ID, whereas
111 * "cpu" is Linux's internal ID.
112 */
113 pen_release = cpu;
114 __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
115 outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
116
117 /*
118 * Send the secondary CPU a soft interrupt, thereby causing
119 * the boot monitor to read the system wide flags register,
120 * and branch to the address found there.
121 */
122 smp_cross_call(cpumask_of(cpu), 1);
123
124 timeout = jiffies + (1 * HZ);
125 while (time_before(jiffies, timeout)) {
126 smp_rmb();
127 if (pen_release == -1)
128 break;
129
130 udelay(10);
131 }
132
133 /*
134 * now the secondary core is starting up let it run its
135 * calibrations, then wait for it to finish
136 */
137 spin_unlock(&boot_lock);
138
139 return pen_release != -1 ? -ENOSYS : 0;
140}
141
142/*
143 * Initialise the CPU possible map early - this describes the CPUs
144 * which may be present or become present in the system. The msm8x60
145 * does not support the ARM SCU, so just set the possible cpu mask to
146 * NR_CPUS.
147 */
148void __init smp_init_cpus(void)
149{
150 unsigned int i;
151
152 for (i = 0; i < NR_CPUS; i++)
153 set_cpu_possible(i, true);
154}
155
156void __init platform_smp_prepare_cpus(unsigned int max_cpus)
157{
158 int i;
159
160 /*
161 * Initialise the present map, which describes the set of CPUs
162 * actually populated at the present time.
163 */
164 for (i = 0; i < max_cpus; i++)
165 set_cpu_present(i, true);
166}
diff --git a/arch/arm/mach-msm/scm-boot.c b/arch/arm/mach-msm/scm-boot.c
new file mode 100644
index 000000000000..45cee3e469a5
--- /dev/null
+++ b/arch/arm/mach-msm/scm-boot.c
@@ -0,0 +1,39 @@
1/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18#include <linux/module.h>
19#include <linux/slab.h>
20
21#include "scm.h"
22#include "scm-boot.h"
23
24/*
25 * Set the cold/warm boot address for one of the CPU cores.
26 */
27int scm_set_boot_addr(phys_addr_t addr, int flags)
28{
29 struct {
30 unsigned int flags;
31 phys_addr_t addr;
32 } cmd;
33
34 cmd.addr = addr;
35 cmd.flags = flags;
36 return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR,
37 &cmd, sizeof(cmd), NULL, 0);
38}
39EXPORT_SYMBOL(scm_set_boot_addr);
diff --git a/arch/arm/mach-msm/scm-boot.h b/arch/arm/mach-msm/scm-boot.h
new file mode 100644
index 000000000000..68f9b6153d74
--- /dev/null
+++ b/arch/arm/mach-msm/scm-boot.h
@@ -0,0 +1,38 @@
1/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of Code Aurora Forum, Inc. nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28#ifndef __MACH_SCM_BOOT_H
29#define __MACH_SCM_BOOT_H
30
31#define SCM_BOOT_ADDR 0x1
32#define SCM_FLAG_COLDBOOT_CPU1 0x1
33#define SCM_FLAG_WARMBOOT_CPU1 0x2
34#define SCM_FLAG_WARMBOOT_CPU0 0x4
35
36int scm_set_boot_addr(phys_addr_t addr, int flags);
37
38#endif
diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c
new file mode 100644
index 000000000000..f4b9bc90d6a7
--- /dev/null
+++ b/arch/arm/mach-msm/scm.c
@@ -0,0 +1,287 @@
1/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15 * 02110-1301, USA.
16 */
17
18#include <linux/slab.h>
19#include <linux/io.h>
20#include <linux/module.h>
21#include <linux/mutex.h>
22#include <linux/errno.h>
23#include <linux/err.h>
24
25#include <asm/cacheflush.h>
26
27#include "scm.h"
28
29/* Cache line size for msm8x60 */
30#define CACHELINESIZE 32
31
32#define SCM_ENOMEM -5
33#define SCM_EOPNOTSUPP -4
34#define SCM_EINVAL_ADDR -3
35#define SCM_EINVAL_ARG -2
36#define SCM_ERROR -1
37#define SCM_INTERRUPTED 1
38
39static DEFINE_MUTEX(scm_lock);
40
41/**
42 * struct scm_command - one SCM command buffer
43 * @len: total available memory for command and response
44 * @buf_offset: start of command buffer
45 * @resp_hdr_offset: start of response buffer
46 * @id: command to be executed
47 * @buf: buffer returned from scm_get_command_buffer()
48 *
49 * An SCM command is layed out in memory as follows:
50 *
51 * ------------------- <--- struct scm_command
52 * | command header |
53 * ------------------- <--- scm_get_command_buffer()
54 * | command buffer |
55 * ------------------- <--- struct scm_response and
56 * | response header | scm_command_to_response()
57 * ------------------- <--- scm_get_response_buffer()
58 * | response buffer |
59 * -------------------
60 *
61 * There can be arbitrary padding between the headers and buffers so
62 * you should always use the appropriate scm_get_*_buffer() routines
63 * to access the buffers in a safe manner.
64 */
65struct scm_command {
66 u32 len;
67 u32 buf_offset;
68 u32 resp_hdr_offset;
69 u32 id;
70 u32 buf[0];
71};
72
73/**
74 * struct scm_response - one SCM response buffer
75 * @len: total available memory for response
76 * @buf_offset: start of response data relative to start of scm_response
77 * @is_complete: indicates if the command has finished processing
78 */
79struct scm_response {
80 u32 len;
81 u32 buf_offset;
82 u32 is_complete;
83};
84
85/**
86 * alloc_scm_command() - Allocate an SCM command
87 * @cmd_size: size of the command buffer
88 * @resp_size: size of the response buffer
89 *
90 * Allocate an SCM command, including enough room for the command
91 * and response headers as well as the command and response buffers.
92 *
93 * Returns a valid &scm_command on success or %NULL if the allocation fails.
94 */
95static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size)
96{
97 struct scm_command *cmd;
98 size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size +
99 resp_size;
100
101 cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
102 if (cmd) {
103 cmd->len = len;
104 cmd->buf_offset = offsetof(struct scm_command, buf);
105 cmd->resp_hdr_offset = cmd->buf_offset + cmd_size;
106 }
107 return cmd;
108}
109
110/**
111 * free_scm_command() - Free an SCM command
112 * @cmd: command to free
113 *
114 * Free an SCM command.
115 */
116static inline void free_scm_command(struct scm_command *cmd)
117{
118 kfree(cmd);
119}
120
121/**
122 * scm_command_to_response() - Get a pointer to a scm_response
123 * @cmd: command
124 *
125 * Returns a pointer to a response for a command.
126 */
127static inline struct scm_response *scm_command_to_response(
128 const struct scm_command *cmd)
129{
130 return (void *)cmd + cmd->resp_hdr_offset;
131}
132
133/**
134 * scm_get_command_buffer() - Get a pointer to a command buffer
135 * @cmd: command
136 *
137 * Returns a pointer to the command buffer of a command.
138 */
139static inline void *scm_get_command_buffer(const struct scm_command *cmd)
140{
141 return (void *)cmd->buf;
142}
143
144/**
145 * scm_get_response_buffer() - Get a pointer to a response buffer
146 * @rsp: response
147 *
148 * Returns a pointer to a response buffer of a response.
149 */
150static inline void *scm_get_response_buffer(const struct scm_response *rsp)
151{
152 return (void *)rsp + rsp->buf_offset;
153}
154
155static int scm_remap_error(int err)
156{
157 switch (err) {
158 case SCM_ERROR:
159 return -EIO;
160 case SCM_EINVAL_ADDR:
161 case SCM_EINVAL_ARG:
162 return -EINVAL;
163 case SCM_EOPNOTSUPP:
164 return -EOPNOTSUPP;
165 case SCM_ENOMEM:
166 return -ENOMEM;
167 }
168 return -EINVAL;
169}
170
171static u32 smc(u32 cmd_addr)
172{
173 int context_id;
174 register u32 r0 asm("r0") = 1;
175 register u32 r1 asm("r1") = (u32)&context_id;
176 register u32 r2 asm("r2") = cmd_addr;
177 asm(
178 __asmeq("%0", "r0")
179 __asmeq("%1", "r0")
180 __asmeq("%2", "r1")
181 __asmeq("%3", "r2")
182 "smc #0 @ switch to secure world\n"
183 : "=r" (r0)
184 : "r" (r0), "r" (r1), "r" (r2)
185 : "r3");
186 return r0;
187}
188
189static int __scm_call(const struct scm_command *cmd)
190{
191 int ret;
192 u32 cmd_addr = virt_to_phys(cmd);
193
194 /*
195 * Flush the entire cache here so callers don't have to remember
196 * to flush the cache when passing physical addresses to the secure
197 * side in the buffer.
198 */
199 flush_cache_all();
200 do {
201 ret = smc(cmd_addr);
202 if (ret < 0) {
203 ret = scm_remap_error(ret);
204 break;
205 }
206 } while (ret == SCM_INTERRUPTED);
207
208 return ret;
209}
210
211/**
212 * scm_call() - Send an SCM command
213 * @svc_id: service identifier
214 * @cmd_id: command identifier
215 * @cmd_buf: command buffer
216 * @cmd_len: length of the command buffer
217 * @resp_buf: response buffer
218 * @resp_len: length of the response buffer
219 *
220 * Sends a command to the SCM and waits for the command to finish processing.
221 */
222int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
223 void *resp_buf, size_t resp_len)
224{
225 int ret;
226 struct scm_command *cmd;
227 struct scm_response *rsp;
228
229 cmd = alloc_scm_command(cmd_len, resp_len);
230 if (!cmd)
231 return -ENOMEM;
232
233 cmd->id = (svc_id << 10) | cmd_id;
234 if (cmd_buf)
235 memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len);
236
237 mutex_lock(&scm_lock);
238 ret = __scm_call(cmd);
239 mutex_unlock(&scm_lock);
240 if (ret)
241 goto out;
242
243 rsp = scm_command_to_response(cmd);
244 do {
245 u32 start = (u32)rsp;
246 u32 end = (u32)scm_get_response_buffer(rsp) + resp_len;
247 start &= ~(CACHELINESIZE - 1);
248 while (start < end) {
249 asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
250 : "memory");
251 start += CACHELINESIZE;
252 }
253 } while (!rsp->is_complete);
254
255 if (resp_buf)
256 memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);
257out:
258 free_scm_command(cmd);
259 return ret;
260}
261EXPORT_SYMBOL(scm_call);
262
263u32 scm_get_version(void)
264{
265 int context_id;
266 static u32 version = -1;
267 register u32 r0 asm("r0") = 0x1 << 8;
268 register u32 r1 asm("r1") = (u32)&context_id;
269
270 if (version != -1)
271 return version;
272
273 mutex_lock(&scm_lock);
274 asm(
275 __asmeq("%0", "r1")
276 __asmeq("%1", "r0")
277 __asmeq("%2", "r1")
278 "smc #0 @ switch to secure world\n"
279 : "=r" (r1)
280 : "r" (r0), "r" (r1)
281 : "r2", "r3");
282 version = r1;
283 mutex_unlock(&scm_lock);
284
285 return version;
286}
287EXPORT_SYMBOL(scm_get_version);
diff --git a/arch/arm/mach-msm/scm.h b/arch/arm/mach-msm/scm.h
new file mode 100644
index 000000000000..261786be11c5
--- /dev/null
+++ b/arch/arm/mach-msm/scm.h
@@ -0,0 +1,41 @@
1/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2 *
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are
5 * met:
6 * * Redistributions of source code must retain the above copyright
7 * notice, this list of conditions and the following disclaimer.
8 * * Redistributions in binary form must reproduce the above
9 * copyright notice, this list of conditions and the following
10 * disclaimer in the documentation and/or other materials provided
11 * with the distribution.
12 * * Neither the name of Code Aurora Forum, Inc. nor the names of its
13 * contributors may be used to endorse or promote products derived
14 * from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
23 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
25 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
26 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28#ifndef __MACH_SCM_H
29#define __MACH_SCM_H
30
31#define SCM_SVC_BOOT 0x1
32#define SCM_SVC_PIL 0x2
33
34extern int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
35 void *resp_buf, size_t resp_len);
36
37#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))
38
39extern u32 scm_get_version(void);
40
41#endif
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index 595be7fea31a..c105d28b53e3 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -47,6 +47,19 @@ enum {
47 47
48#define GPT_HZ 32768 48#define GPT_HZ 32768
49 49
50enum timer_location {
51 LOCAL_TIMER = 0,
52 GLOBAL_TIMER = 1,
53};
54
55#ifdef MSM_TMR0_BASE
56#define MSM_TMR_GLOBAL (MSM_TMR0_BASE - MSM_TMR_BASE)
57#else
58#define MSM_TMR_GLOBAL 0
59#endif
60
61#define MSM_GLOBAL_TIMER MSM_CLOCK_DGT
62
50#if defined(CONFIG_ARCH_QSD8X50) 63#if defined(CONFIG_ARCH_QSD8X50)
51#define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */ 64#define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */
52#define MSM_DGT_SHIFT (0) 65#define MSM_DGT_SHIFT (0)
@@ -65,49 +78,67 @@ struct msm_clock {
65 void __iomem *regbase; 78 void __iomem *regbase;
66 uint32_t freq; 79 uint32_t freq;
67 uint32_t shift; 80 uint32_t shift;
81 void __iomem *global_counter;
82 void __iomem *local_counter;
83};
84
85enum {
86 MSM_CLOCK_GPT,
87 MSM_CLOCK_DGT,
88 NR_TIMERS,
68}; 89};
69 90
91
92static struct msm_clock msm_clocks[];
93static struct clock_event_device *local_clock_event;
94
70static irqreturn_t msm_timer_interrupt(int irq, void *dev_id) 95static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
71{ 96{
72 struct clock_event_device *evt = dev_id; 97 struct clock_event_device *evt = dev_id;
98 if (smp_processor_id() != 0)
99 evt = local_clock_event;
100 if (evt->event_handler == NULL)
101 return IRQ_HANDLED;
73 evt->event_handler(evt); 102 evt->event_handler(evt);
74 return IRQ_HANDLED; 103 return IRQ_HANDLED;
75} 104}
76 105
77static cycle_t msm_gpt_read(struct clocksource *cs) 106static cycle_t msm_read_timer_count(struct clocksource *cs)
78{ 107{
79 return readl(MSM_GPT_BASE + TIMER_COUNT_VAL); 108 struct msm_clock *clk = container_of(cs, struct msm_clock, clocksource);
109
110 return readl(clk->global_counter);
80} 111}
81 112
82static cycle_t msm_dgt_read(struct clocksource *cs) 113static struct msm_clock *clockevent_to_clock(struct clock_event_device *evt)
83{ 114{
84 return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT; 115#ifdef CONFIG_SMP
116 int i;
117 for (i = 0; i < NR_TIMERS; i++)
118 if (evt == &(msm_clocks[i].clockevent))
119 return &msm_clocks[i];
120 return &msm_clocks[MSM_GLOBAL_TIMER];
121#else
122 return container_of(evt, struct msm_clock, clockevent);
123#endif
85} 124}
86 125
87static int msm_timer_set_next_event(unsigned long cycles, 126static int msm_timer_set_next_event(unsigned long cycles,
88 struct clock_event_device *evt) 127 struct clock_event_device *evt)
89{ 128{
90 struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); 129 struct msm_clock *clock = clockevent_to_clock(evt);
91 uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL); 130 uint32_t now = readl(clock->local_counter);
92 uint32_t alarm = now + (cycles << clock->shift); 131 uint32_t alarm = now + (cycles << clock->shift);
93 int late;
94 132
95 writel(alarm, clock->regbase + TIMER_MATCH_VAL); 133 writel(alarm, clock->regbase + TIMER_MATCH_VAL);
96 now = readl(clock->regbase + TIMER_COUNT_VAL);
97 late = now - alarm;
98 if (late >= (-2 << clock->shift) && late < DGT_HZ*5) {
99 printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, "
100 "alarm already expired, now %x, alarm %x, late %d\n",
101 cycles, clock->clockevent.name, now, alarm, late);
102 return -ETIME;
103 }
104 return 0; 134 return 0;
105} 135}
106 136
107static void msm_timer_set_mode(enum clock_event_mode mode, 137static void msm_timer_set_mode(enum clock_event_mode mode,
108 struct clock_event_device *evt) 138 struct clock_event_device *evt)
109{ 139{
110 struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent); 140 struct msm_clock *clock = clockevent_to_clock(evt);
141
111 switch (mode) { 142 switch (mode) {
112 case CLOCK_EVT_MODE_RESUME: 143 case CLOCK_EVT_MODE_RESUME:
113 case CLOCK_EVT_MODE_PERIODIC: 144 case CLOCK_EVT_MODE_PERIODIC:
@@ -123,7 +154,7 @@ static void msm_timer_set_mode(enum clock_event_mode mode,
123} 154}
124 155
125static struct msm_clock msm_clocks[] = { 156static struct msm_clock msm_clocks[] = {
126 { 157 [MSM_CLOCK_GPT] = {
127 .clockevent = { 158 .clockevent = {
128 .name = "gp_timer", 159 .name = "gp_timer",
129 .features = CLOCK_EVT_FEAT_ONESHOT, 160 .features = CLOCK_EVT_FEAT_ONESHOT,
@@ -135,7 +166,7 @@ static struct msm_clock msm_clocks[] = {
135 .clocksource = { 166 .clocksource = {
136 .name = "gp_timer", 167 .name = "gp_timer",
137 .rating = 200, 168 .rating = 200,
138 .read = msm_gpt_read, 169 .read = msm_read_timer_count,
139 .mask = CLOCKSOURCE_MASK(32), 170 .mask = CLOCKSOURCE_MASK(32),
140 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 171 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
141 }, 172 },
@@ -147,9 +178,12 @@ static struct msm_clock msm_clocks[] = {
147 .irq = INT_GP_TIMER_EXP 178 .irq = INT_GP_TIMER_EXP
148 }, 179 },
149 .regbase = MSM_GPT_BASE, 180 .regbase = MSM_GPT_BASE,
150 .freq = GPT_HZ 181 .freq = GPT_HZ,
182 .local_counter = MSM_GPT_BASE + TIMER_COUNT_VAL,
183 .global_counter = MSM_GPT_BASE + TIMER_COUNT_VAL +
184 MSM_TMR_GLOBAL,
151 }, 185 },
152 { 186 [MSM_CLOCK_DGT] = {
153 .clockevent = { 187 .clockevent = {
154 .name = "dg_timer", 188 .name = "dg_timer",
155 .features = CLOCK_EVT_FEAT_ONESHOT, 189 .features = CLOCK_EVT_FEAT_ONESHOT,
@@ -161,7 +195,7 @@ static struct msm_clock msm_clocks[] = {
161 .clocksource = { 195 .clocksource = {
162 .name = "dg_timer", 196 .name = "dg_timer",
163 .rating = 300, 197 .rating = 300,
164 .read = msm_dgt_read, 198 .read = msm_read_timer_count,
165 .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)), 199 .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)),
166 .flags = CLOCK_SOURCE_IS_CONTINUOUS, 200 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
167 }, 201 },
@@ -174,7 +208,10 @@ static struct msm_clock msm_clocks[] = {
174 }, 208 },
175 .regbase = MSM_DGT_BASE, 209 .regbase = MSM_DGT_BASE,
176 .freq = DGT_HZ >> MSM_DGT_SHIFT, 210 .freq = DGT_HZ >> MSM_DGT_SHIFT,
177 .shift = MSM_DGT_SHIFT 211 .shift = MSM_DGT_SHIFT,
212 .local_counter = MSM_DGT_BASE + TIMER_COUNT_VAL,
213 .global_counter = MSM_DGT_BASE + TIMER_COUNT_VAL +
214 MSM_TMR_GLOBAL,
178 } 215 }
179}; 216};
180 217
@@ -183,7 +220,7 @@ static void __init msm_timer_init(void)
183 int i; 220 int i;
184 int res; 221 int res;
185 222
186#ifdef CONFIG_ARCH_MSM8X60 223#ifdef CONFIG_ARCH_MSM_SCORPIONMP
187 writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL); 224 writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
188#endif 225#endif
189 226
@@ -217,6 +254,48 @@ static void __init msm_timer_init(void)
217 } 254 }
218} 255}
219 256
257#ifdef CONFIG_SMP
258void __cpuinit local_timer_setup(struct clock_event_device *evt)
259{
260 struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER];
261
262 /* Use existing clock_event for cpu 0 */
263 if (!smp_processor_id())
264 return;
265
266 writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
267
268 if (!local_clock_event) {
269 writel(0, clock->regbase + TIMER_ENABLE);
270 writel(0, clock->regbase + TIMER_CLEAR);
271 writel(~0, clock->regbase + TIMER_MATCH_VAL);
272 }
273 evt->irq = clock->irq.irq;
274 evt->name = "local_timer";
275 evt->features = CLOCK_EVT_FEAT_ONESHOT;
276 evt->rating = clock->clockevent.rating;
277 evt->set_mode = msm_timer_set_mode;
278 evt->set_next_event = msm_timer_set_next_event;
279 evt->shift = clock->clockevent.shift;
280 evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift);
281 evt->max_delta_ns =
282 clockevent_delta2ns(0xf0000000 >> clock->shift, evt);
283 evt->min_delta_ns = clockevent_delta2ns(4, evt);
284
285 local_clock_event = evt;
286
287 gic_enable_ppi(clock->irq.irq);
288
289 clockevents_register_device(evt);
290}
291
292inline int local_timer_ack(void)
293{
294 return 1;
295}
296
297#endif
298
220struct sys_timer msm_timer = { 299struct sys_timer msm_timer = {
221 .init = msm_timer_init 300 .init = msm_timer_init
222}; 301};