2010-12-08 04:28:30

by Jeff Ohlstein

[permalink] [raw]
Subject: [PATCH v2 0/6] SMP support for msm

This series adds support for SMP on msm targets, and specifically adds
support for SMP on the msm8x60.

Jeff Ohlstein (4):
arm: dma-mapping: move consistent_init to early_initcall
msm: timer: SMP timer support for msm
msm: hotplug: support cpu hotplug on msm
msm: add SMP support for msm

Stepan Moskovchenko (1):
msm: scm-boot: Support for setting cold/warm boot addresses

Stephen Boyd (1):
msm: Secure Channel Manager (SCM) support

arch/arm/mach-msm/Kconfig | 5 +
arch/arm/mach-msm/Makefile | 4 +
arch/arm/mach-msm/headsmp.S | 43 ++++
arch/arm/mach-msm/hotplug.c | 103 +++++++++
arch/arm/mach-msm/include/mach/msm_iomap-8x60.h | 6 +-
arch/arm/mach-msm/include/mach/smp.h | 2 +
arch/arm/mach-msm/io.c | 1 +
arch/arm/mach-msm/platsmp.c | 146 ++++++++++++
arch/arm/mach-msm/scm-boot.c | 39 +++
arch/arm/mach-msm/scm-boot.h | 38 +++
arch/arm/mach-msm/scm.c | 280 +++++++++++++++++++++++
arch/arm/mach-msm/scm.h | 41 ++++
arch/arm/mach-msm/timer.c | 130 +++++++++--
arch/arm/mm/dma-mapping.c | 2 +-
14 files changed, 817 insertions(+), 23 deletions(-)
create mode 100644 arch/arm/mach-msm/headsmp.S
create mode 100644 arch/arm/mach-msm/hotplug.c
create mode 100644 arch/arm/mach-msm/platsmp.c
create mode 100644 arch/arm/mach-msm/scm-boot.c
create mode 100644 arch/arm/mach-msm/scm-boot.h
create mode 100644 arch/arm/mach-msm/scm.c
create mode 100644 arch/arm/mach-msm/scm.h

--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.


2010-12-08 04:28:35

by Jeff Ohlstein

[permalink] [raw]
Subject: [PATCH v2 3/6] msm: scm-boot: Support for setting cold/warm boot addresses

From: Stepan Moskovchenko <[email protected]>

Add support for setting the cold boot address of core 1 and
the warm boot addresses of cores 0 and 1 using a secure
domain call.

Signed-off-by: Stepan Moskovchenko <[email protected]>
---
arch/arm/mach-msm/Makefile | 2 +-
arch/arm/mach-msm/scm-boot.c | 39 +++++++++++++++++++++++++++++++++++++++
arch/arm/mach-msm/scm-boot.h | 38 ++++++++++++++++++++++++++++++++++++++
3 files changed, 78 insertions(+), 1 deletions(-)
create mode 100644 arch/arm/mach-msm/scm-boot.c
create mode 100644 arch/arm/mach-msm/scm-boot.h

diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index eed503b..34cd0a8 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -18,7 +18,7 @@ obj-$(CONFIG_MSM_PROC_COMM) += clock.o
obj-$(CONFIG_ARCH_QSD8X50) += sirc.o
obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o
obj-$(CONFIG_MSM_SMD) += last_radio_log.o
-obj-$(CONFIG_MSM_SCM) += scm.o
+obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o

obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o
obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o
diff --git a/arch/arm/mach-msm/scm-boot.c b/arch/arm/mach-msm/scm-boot.c
new file mode 100644
index 0000000..5671d58
--- /dev/null
+++ b/arch/arm/mach-msm/scm-boot.c
@@ -0,0 +1,39 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "scm.h"
+#include "scm-boot.h"
+
+/*
+ * Set the cold/warm boot address for one of the CPU cores.
+ */
+int scm_set_boot_addr(void *addr, int flags)
+{
+ struct {
+ unsigned int flags;
+ void *addr;
+ } cmd;
+
+ cmd.addr = addr;
+ cmd.flags = flags;
+ return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR,
+ &cmd, sizeof(cmd), NULL, 0);
+}
+EXPORT_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 0000000..fdc1374
--- /dev/null
+++ b/arch/arm/mach-msm/scm-boot.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __MACH_SCM_BOOT_H
+#define __MACH_SCM_BOOT_H
+
+#define SCM_BOOT_ADDR 0x1
+#define SCM_FLAG_COLDBOOT_CPU1 0x1
+#define SCM_FLAG_WARMBOOT_CPU1 0x2
+#define SCM_FLAG_WARMBOOT_CPU0 0x4
+
+int scm_set_boot_addr(void *addr, int flags);
+
+#endif
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2010-12-08 04:28:33

by Jeff Ohlstein

[permalink] [raw]
Subject: [PATCH v2 1/6] arm: dma-mapping: move consistent_init to early_initcall

Some machines require the use of coherent memory to bring up auxiliary
cpus, and thus want to use dma_alloc_coherent prior to smp_init
completing.

Signed-off-by: Jeff Ohlstein <[email protected]>
---
arch/arm/mm/dma-mapping.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c
index ac6a361..d46ce8d 100644
--- a/arch/arm/mm/dma-mapping.c
+++ b/arch/arm/mm/dma-mapping.c
@@ -177,7 +177,7 @@ static int __init consistent_init(void)
return ret;
}

-core_initcall(consistent_init);
+early_initcall(consistent_init);

static void *
__dma_alloc_remap(struct page *page, size_t size, gfp_t gfp, pgprot_t prot)
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2010-12-08 04:28:41

by Jeff Ohlstein

[permalink] [raw]
Subject: [PATCH v2 4/6] msm: timer: SMP timer support for msm

Signed-off-by: Jeff Ohlstein <[email protected]>
---
arch/arm/mach-msm/include/mach/msm_iomap-8x60.h | 6 +-
arch/arm/mach-msm/io.c | 1 +
arch/arm/mach-msm/timer.c | 130 +++++++++++++++++++----
3 files changed, 115 insertions(+), 22 deletions(-)

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 45bab50..873e0b7 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 @@

#define MSM_TMR_BASE IOMEM(0xF0200000)
#define MSM_TMR_PHYS 0x02000000
-#define MSM_TMR_SIZE (SZ_1M)
+#define MSM_TMR_SIZE SZ_4K
+
+#define MSM_TMR0_BASE IOMEM(0xF0201000)
+#define MSM_TMR0_PHYS 0x02040000
+#define MSM_TMR0_SIZE SZ_4K

#define MSM_GPT_BASE (MSM_TMR_BASE + 0x4)
#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 d36b610..b826b6b 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 = {
MSM_DEVICE(QGIC_DIST),
MSM_DEVICE(QGIC_CPU),
MSM_DEVICE(TMR),
+ MSM_DEVICE(TMR0),
MSM_DEVICE(ACC),
MSM_DEVICE(GCC),
};
diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
index 950100f..92ac220 100644
--- a/arch/arm/mach-msm/timer.c
+++ b/arch/arm/mach-msm/timer.c
@@ -47,6 +47,19 @@ enum {

#define GPT_HZ 32768

+enum timer_location {
+ LOCAL_TIMER = 0,
+ GLOBAL_TIMER = 1,
+};
+
+#ifdef MSM_TMR0_BASE
+#define MSM_TMR_GLOBAL (MSM_TMR0_BASE - MSM_TMR_BASE)
+#else
+#define MSM_TMR_GLOBAL 0
+#endif
+
+#define MSM_GLOBAL_TIMER MSM_CLOCK_DGT
+
#if defined(CONFIG_ARCH_QSD8X50)
#define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */
#define MSM_DGT_SHIFT (0)
@@ -65,49 +78,67 @@ struct msm_clock {
void __iomem *regbase;
uint32_t freq;
uint32_t shift;
+ void *global_counter;
+ void *local_counter;
};

+enum {
+ MSM_CLOCK_GPT,
+ MSM_CLOCK_DGT,
+ NR_TIMERS,
+};
+
+
+static struct msm_clock msm_clocks[];
+static struct clock_event_device *local_clock_event;
+
static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
{
struct clock_event_device *evt = dev_id;
+ if (smp_processor_id() != 0)
+ evt = local_clock_event;
+ if (evt->event_handler == NULL)
+ return IRQ_HANDLED;
evt->event_handler(evt);
return IRQ_HANDLED;
}

-static cycle_t msm_gpt_read(struct clocksource *cs)
+static cycle_t msm_read_timer_count(struct clocksource *cs)
{
- return readl(MSM_GPT_BASE + TIMER_COUNT_VAL);
+ struct msm_clock *clk = container_of(cs, struct msm_clock, clocksource);
+
+ return readl(clk->global_counter) >> clk->shift;
}

-static cycle_t msm_dgt_read(struct clocksource *cs)
+static struct msm_clock *clockevent_to_clock(struct clock_event_device *evt)
{
- return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT;
+#ifdef CONFIG_SMP
+ int i;
+ for (i = 0; i < NR_TIMERS; i++)
+ if (evt == &(msm_clocks[i].clockevent))
+ return &msm_clocks[i];
+ return &msm_clocks[MSM_GLOBAL_TIMER];
+#else
+ return container_of(evt, struct msm_clock, clockevent);
+#endif
}

static int msm_timer_set_next_event(unsigned long cycles,
struct clock_event_device *evt)
{
- struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent);
- uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL);
+ struct msm_clock *clock = clockevent_to_clock(evt);
+ uint32_t now = readl(clock->local_counter);
uint32_t alarm = now + (cycles << clock->shift);
- int late;

writel(alarm, clock->regbase + TIMER_MATCH_VAL);
- now = readl(clock->regbase + TIMER_COUNT_VAL);
- late = now - alarm;
- if (late >= (-2 << clock->shift) && late < DGT_HZ*5) {
- printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, "
- "alarm already expired, now %x, alarm %x, late %d\n",
- cycles, clock->clockevent.name, now, alarm, late);
- return -ETIME;
- }
return 0;
}

static void msm_timer_set_mode(enum clock_event_mode mode,
struct clock_event_device *evt)
{
- struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent);
+ struct msm_clock *clock = clockevent_to_clock(evt);
+
switch (mode) {
case CLOCK_EVT_MODE_RESUME:
case CLOCK_EVT_MODE_PERIODIC:
@@ -135,7 +166,7 @@ static struct msm_clock msm_clocks[] = {
.clocksource = {
.name = "gp_timer",
.rating = 200,
- .read = msm_gpt_read,
+ .read = msm_read_timer_count,
.mask = CLOCKSOURCE_MASK(32),
.shift = 17,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
@@ -148,7 +179,10 @@ static struct msm_clock msm_clocks[] = {
.irq = INT_GP_TIMER_EXP
},
.regbase = MSM_GPT_BASE,
- .freq = GPT_HZ
+ .freq = GPT_HZ,
+ .local_counter = MSM_GPT_BASE + TIMER_COUNT_VAL,
+ .global_counter = MSM_GPT_BASE + TIMER_COUNT_VAL +
+ MSM_TMR_GLOBAL,
},
{
.clockevent = {
@@ -162,7 +196,7 @@ static struct msm_clock msm_clocks[] = {
.clocksource = {
.name = "dg_timer",
.rating = 300,
- .read = msm_dgt_read,
+ .read = msm_read_timer_count,
.mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)),
.shift = 24 - MSM_DGT_SHIFT,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
@@ -176,7 +210,10 @@ static struct msm_clock msm_clocks[] = {
},
.regbase = MSM_DGT_BASE,
.freq = DGT_HZ >> MSM_DGT_SHIFT,
- .shift = MSM_DGT_SHIFT
+ .shift = MSM_DGT_SHIFT,
+ .local_counter = MSM_DGT_BASE + TIMER_COUNT_VAL,
+ .global_counter = MSM_DGT_BASE + TIMER_COUNT_VAL +
+ MSM_TMR_GLOBAL,
}
};

@@ -185,7 +222,7 @@ static void __init msm_timer_init(void)
int i;
int res;

-#ifdef CONFIG_ARCH_MSM8X60
+#ifdef CONFIG_ARCH_MSM_SCORPIONMP
writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
#endif

@@ -220,6 +257,57 @@ static void __init msm_timer_init(void)
}
}

+#ifdef CONFIG_SMP
+void local_timer_setup(struct clock_event_device *evt)
+{
+ unsigned long flags;
+ struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER];
+
+ writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
+
+ if (!local_clock_event) {
+ writel(0, clock->regbase + TIMER_ENABLE);
+ writel(0, clock->regbase + TIMER_CLEAR);
+ writel(~0, clock->regbase + TIMER_MATCH_VAL);
+ }
+ evt->irq = clock->irq.irq;
+ evt->name = "local_timer";
+ evt->features = CLOCK_EVT_FEAT_ONESHOT;
+ evt->rating = clock->clockevent.rating;
+ evt->set_mode = msm_timer_set_mode;
+ evt->set_next_event = msm_timer_set_next_event;
+ evt->shift = clock->clockevent.shift;
+ evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift);
+ evt->max_delta_ns =
+ clockevent_delta2ns(0xf0000000 >> clock->shift, evt);
+ evt->min_delta_ns = clockevent_delta2ns(4, evt);
+ evt->cpumask = cpumask_of(smp_processor_id());
+
+ local_clock_event = evt;
+
+ local_irq_save(flags);
+ get_irq_chip(clock->irq.irq)->unmask(clock->irq.irq);
+ local_irq_restore(flags);
+
+ clockevents_register_device(evt);
+}
+
+inline int local_timer_ack(void)
+{
+ return 1;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+void __cpuexit local_timer_stop(void)
+{
+ struct msm_clock *clock = clockevent_to_clock(local_clock_event);
+ writel(0, clock->regbase + TIMER_MATCH_VAL);
+ writel(0, clock->regbase + TIMER_ENABLE);
+ get_irq_chip(local_clock_event->irq)->mask(local_clock_event->irq);
+}
+#endif
+#endif
+
struct sys_timer msm_timer = {
.init = msm_timer_init
};
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2010-12-08 04:28:43

by Jeff Ohlstein

[permalink] [raw]
Subject: [PATCH v2 5/6] msm: hotplug: support cpu hotplug on msm

Signed-off-by: Jeff Ohlstein <[email protected]>
---
arch/arm/mach-msm/Makefile | 2 +
arch/arm/mach-msm/hotplug.c | 103 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 105 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-msm/hotplug.c

diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 34cd0a8..7a11b4a 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -20,6 +20,8 @@ obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o
obj-$(CONFIG_MSM_SMD) += last_radio_log.o
obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o

+obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+
obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o
obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o
obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o
diff --git a/arch/arm/mach-msm/hotplug.c b/arch/arm/mach-msm/hotplug.c
new file mode 100644
index 0000000..59c1dfa
--- /dev/null
+++ b/arch/arm/mach-msm/hotplug.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2002 ARM Ltd.
+ * 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/completion.h>
+#include <linux/irq.h>
+
+#include <asm/cacheflush.h>
+
+#include <mach/smp.h>
+
+static DECLARE_COMPLETION(cpu_killed);
+
+static inline void cpu_enter_lowpower(void)
+{
+ flush_cache_all();
+}
+
+static inline void cpu_leave_lowpower(void)
+{
+ pen_release = -1;
+ smp_wmb();
+}
+
+static inline void platform_do_lowpower(unsigned int cpu)
+{
+ /* Just enter wfe for now. */
+ for (;;) {
+ asm("wfe");
+ if (pen_release == cpu) {
+ /*
+ * OK, proper wakeup, we're done
+ */
+ break;
+ }
+ smp_rmb();
+
+ /*
+ * 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
+ */
+ pr_debug("CPU%u: spurious wakeup call\n", cpu);
+ }
+}
+
+int platform_cpu_kill(unsigned int cpu)
+{
+ return wait_for_completion_timeout(&cpu_killed, 50000);
+}
+
+/*
+ * platform-specific code to shutdown a CPU
+ *
+ * Called with IRQs disabled
+ */
+void platform_cpu_die(unsigned int cpu)
+{
+#ifdef DEBUG
+ unsigned int this_cpu = hard_smp_processor_id();
+
+ if (cpu != this_cpu) {
+ printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
+ this_cpu, cpu);
+ BUG();
+ }
+#endif
+
+ printk(KERN_NOTICE "CPU%u: shutdown\n", cpu);
+ complete(&cpu_killed);
+
+ /*
+ * we're ready for shutdown now, so do it
+ */
+ cpu_enter_lowpower();
+ platform_do_lowpower(cpu);
+
+ /*
+ * bring this CPU back into the world of cache
+ * coherency, and then restore interrupts
+ */
+ cpu_leave_lowpower();
+}
+
+int platform_cpu_disable(unsigned int cpu)
+{
+ /*
+ * we don't allow CPU 0 to be shutdown (it is still too special
+ * e.g. clock tick interrupts)
+ */
+ return cpu == 0 ? -EPERM : 0;
+}
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2010-12-08 04:28:44

by Jeff Ohlstein

[permalink] [raw]
Subject: [PATCH v2 6/6] msm: add SMP support for msm

Signed-off-by: Jeff Ohlstein <[email protected]>
---
arch/arm/mach-msm/Kconfig | 1 +
arch/arm/mach-msm/Makefile | 1 +
arch/arm/mach-msm/headsmp.S | 43 ++++++++++
arch/arm/mach-msm/include/mach/smp.h | 2 +
arch/arm/mach-msm/platsmp.c | 146 ++++++++++++++++++++++++++++++++++
5 files changed, 193 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-msm/headsmp.S
create mode 100644 arch/arm/mach-msm/platsmp.c

diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index ab5338f..8c57425 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -40,6 +40,7 @@ config ARCH_MSM8X60
bool "MSM8X60"
select MACH_MSM8X60_SURF if (!MACH_MSM8X60_RUMI3 && !MACH_MSM8X60_SIM \
&& !MACH_MSM8X60_FFA)
+ select ARCH_MSM_SCORPIONMP
select ARM_GIC
select CPU_V7
select MSM_V2_TLMM
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index 7a11b4a..1945f9c 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_MSM_SMD) += last_radio_log.o
obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o

obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
+obj-$(CONFIG_SMP) += headsmp.o platsmp.o

obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o
obj-$(CONFIG_MACH_HALIBUT) += board-halibut.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 0000000..438cfeb
--- /dev/null
+++ b/arch/arm/mach-msm/headsmp.S
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2003 ARM Limited
+ * All Rights Reserved
+ * Copyright (c) 2010, Code Aurora Forum. 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>
+
+/*
+ * 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)
+ mrc p15, 0, r0, c0, c0, 5 @ MPIDR
+ and r0, r0, #15 @ 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:
+ wfe
+ dsb @ ensure subsequent access is
+ @ after event
+
+ 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
+
+1: .long .
+ .long pen_release
diff --git a/arch/arm/mach-msm/include/mach/smp.h b/arch/arm/mach-msm/include/mach/smp.h
index 3ff7bf5..79f94b0 100644
--- a/arch/arm/mach-msm/include/mach/smp.h
+++ b/arch/arm/mach-msm/include/mach/smp.h
@@ -36,4 +36,6 @@ static inline void smp_cross_call(const struct cpumask *mask)
gic_raise_softirq(mask, 1);
}

+extern int pen_release;
+extern void msm_secondary_startup(void);
#endif
diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
new file mode 100644
index 0000000..0ab2bba
--- /dev/null
+++ b/arch/arm/mach-msm/platsmp.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2002 ARM Ltd.
+ * All Rights Reserved
+ * Copyright (c) 2010, Code Aurora Forum. 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/init.h>
+#include <linux/cpumask.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+
+#include <asm/hardware/gic.h>
+#include <asm/cacheflush.h>
+#include <asm/mach-types.h>
+
+#include <mach/smp.h>
+#include <mach/msm_iomap.h>
+
+#include "scm-boot.h"
+
+#define SECONDARY_CPU_WAIT_MS 10
+
+#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0
+#define SCSS_CPU1CORE_RESET 0xD80
+#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64
+
+int pen_release = -1;
+
+int get_core_count(void)
+{
+#ifdef CONFIG_NR_CPUS
+ return CONFIG_NR_CPUS;
+#else
+ return 1;
+#endif
+}
+
+/* Initialize the present map (set_cpu_present(i, true)). */
+void smp_prepare_cpus(unsigned int max_cpus)
+{
+ int i;
+ unsigned int cpu = smp_processor_id();
+
+ smp_store_cpu_info(cpu);
+
+ for (i = 0; i < max_cpus; i++)
+ set_cpu_present(i, true);
+}
+
+void smp_init_cpus(void)
+{
+ unsigned int i, ncores = get_core_count();
+
+ for (i = 0; i < ncores; i++)
+ set_cpu_possible(i, true);
+}
+
+static void prepare_cold_cpu(unsigned int cpu)
+{
+ int ret;
+ ret = scm_set_boot_addr((void *)
+ virt_to_phys(msm_secondary_startup),
+ SCM_FLAG_COLDBOOT_CPU1);
+ if (ret == 0) {
+ void *sc1_base_ptr;
+ sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2);
+ if (sc1_base_ptr) {
+ writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
+ writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET);
+ writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP);
+ iounmap(sc1_base_ptr);
+ }
+ } else
+ printk(KERN_DEBUG "Failed to set secondary core boot "
+ "address\n");
+}
+
+/* Executed by primary CPU, brings other CPUs out of reset. Called at boot
+ as well as when a CPU is coming out of shutdown induced by echo 0 >
+ /sys/devices/.../cpuX.
+*/
+int boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+ static int cold_boot_done;
+ int cnt = 0;
+ printk(KERN_DEBUG "Starting secondary CPU %d\n", cpu);
+
+ if (cold_boot_done == false) {
+ prepare_cold_cpu(cpu);
+ cold_boot_done = true;
+ }
+
+ pen_release = cpu;
+ dmac_flush_range((void *)&pen_release,
+ (void *)(&pen_release + sizeof(pen_release)));
+ __asm__("sev");
+ dsb();
+
+ /* Use smp_cross_call() to send a soft interrupt to wake up
+ * the other core.
+ */
+ smp_cross_call(cpumask_of(cpu));
+
+ while (pen_release != 0xFFFFFFFF) {
+ smp_rmb();
+ msleep_interruptible(1);
+ if (cnt++ >= SECONDARY_CPU_WAIT_MS)
+ break;
+ }
+
+ return 0;
+}
+
+/* Mask for edge trigger PPIs except AVS_SVICINT and AVS_SVICINTSWDONE */
+#define GIC_PPI_EDGE_MASK 0xFFFFD7FF
+
+/* Initialization routine for secondary CPUs after they are brought out of
+ * reset.
+*/
+void platform_secondary_init(unsigned int cpu)
+{
+ printk(KERN_DEBUG "%s: cpu:%d\n", __func__, cpu);
+
+ trace_hardirqs_off();
+
+ writel(GIC_PPI_EDGE_MASK, MSM_QGIC_DIST_BASE + GIC_DIST_CONFIG + 4);
+
+ /* RUMI does not adhere to GIC spec by enabling STIs by default.
+ * Enable/clear is supposed to be RO for STIs, but is RW on RUMI.
+ */
+ if (!machine_is_msm8x60_sim())
+ writel(0x0000FFFF, MSM_QGIC_DIST_BASE + GIC_DIST_ENABLE_SET);
+
+ /*
+ * setup GIC (GIC number NOT CPU number and the base address of the
+ * GIC CPU interface
+ */
+ gic_cpu_init(0, MSM_QGIC_CPU_BASE);
+ pen_release = -1;
+ smp_wmb();
+}
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2010-12-08 04:29:28

by Jeff Ohlstein

[permalink] [raw]
Subject: [PATCH v2 2/6] msm: Secure Channel Manager (SCM) support

From: Stephen Boyd <[email protected]>

SCM is the protocol used to communicate between the secure and
non-secure code executing on the applications processor. The
non-secure side uses a physically contiguous buffer to pass
information to the secure side; where the buffer conforms to a
format that is agreed upon by both sides. The use of a buffer
allows multiple pending requests to be in flight on the secure
side. It also benefits use cases where the command or response
buffer contains large chunks of data.

Signed-off-by: Stephen Boyd <[email protected]>
---
arch/arm/mach-msm/Kconfig | 4 +
arch/arm/mach-msm/Makefile | 1 +
arch/arm/mach-msm/scm.c | 280 ++++++++++++++++++++++++++++++++++++++++++++
arch/arm/mach-msm/scm.h | 41 +++++++
4 files changed, 326 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-msm/scm.c
create mode 100644 arch/arm/mach-msm/scm.h

diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
index 31e5fd6..ab5338f 100644
--- a/arch/arm/mach-msm/Kconfig
+++ b/arch/arm/mach-msm/Kconfig
@@ -44,6 +44,7 @@ config ARCH_MSM8X60
select CPU_V7
select MSM_V2_TLMM
select MSM_GPIOMUX
+ select MSM_SCM if SMP

endchoice

@@ -164,4 +165,7 @@ config MSM_GPIOMUX

config MSM_V2_TLMM
bool
+
+config MSM_SCM
+ bool
endif
diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
index b5a7b07..eed503b 100644
--- a/arch/arm/mach-msm/Makefile
+++ b/arch/arm/mach-msm/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_MSM_PROC_COMM) += clock.o
obj-$(CONFIG_ARCH_QSD8X50) += sirc.o
obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o
obj-$(CONFIG_MSM_SMD) += last_radio_log.o
+obj-$(CONFIG_MSM_SCM) += scm.o

obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o
obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o
diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c
new file mode 100644
index 0000000..85ed2cc
--- /dev/null
+++ b/arch/arm/mach-msm/scm.c
@@ -0,0 +1,280 @@
+/* Copyright (c) 2010, Code Aurora Forum. 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/cacheflush.h>
+
+#include "scm.h"
+
+#define SCM_ENOMEM -5
+#define SCM_EOPNOTSUPP -4
+#define SCM_EINVAL_ADDR -3
+#define SCM_EINVAL_ARG -2
+#define SCM_ERROR -1
+#define SCM_INTERRUPTED 1
+
+static DEFINE_MUTEX(scm_lock);
+
+/**
+ * struct scm_command - one SCM command buffer
+ * @len: total available memory for command and response
+ * @buf_offset: start of command buffer
+ * @resp_hdr_offset: start of response buffer
+ * @id: command to be executed
+ *
+ * An SCM command is layed out in memory as follows:
+ *
+ * ------------------- <--- struct scm_command
+ * | command header |
+ * ------------------- <--- scm_get_command_buffer()
+ * | command buffer |
+ * ------------------- <--- struct scm_response and
+ * | response header | scm_command_to_response()
+ * ------------------- <--- scm_get_response_buffer()
+ * | response buffer |
+ * -------------------
+ *
+ * There can be arbitrary padding between the headers and buffers so
+ * you should always use the appropriate scm_get_*_buffer() routines
+ * to access the buffers in a safe manner.
+ */
+struct scm_command {
+ u32 len;
+ u32 buf_offset;
+ u32 resp_hdr_offset;
+ u32 id;
+};
+
+/**
+ * struct scm_response - one SCM response buffer
+ * @len: total available memory for response
+ * @buf_offset: start of response data relative to start of scm_response
+ * @is_complete: indicates if the command has finished processing
+ */
+struct scm_response {
+ u32 len;
+ u32 buf_offset;
+ u32 is_complete;
+};
+
+/**
+ * alloc_scm_command() - Allocate an SCM command
+ * @cmd_size: size of the command buffer
+ * @resp_size: size of the response buffer
+ * @handle: dma handle
+ *
+ * Allocate an SCM command, including enough room for the command
+ * and response headers as well as the command and response buffers.
+ *
+ * Returns a valid &scm_command on success or %NULL if the allocation fails.
+ */
+static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size,
+ dma_addr_t *handle)
+{
+ struct scm_command *cmd;
+ size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size +
+ resp_size;
+
+ cmd = dma_alloc_coherent(NULL, len, handle, GFP_KERNEL);
+ if (cmd) {
+ cmd->len = len;
+ cmd->buf_offset = sizeof(*cmd);
+ cmd->resp_hdr_offset = cmd->buf_offset + cmd_size;
+ }
+ return cmd;
+}
+
+/**
+ * free_scm_command() - Free an SCM command
+ * @cmd: command to free
+ * @handle: dma handle
+ *
+ * Free an SCM command.
+ */
+static inline void free_scm_command(struct scm_command *cmd, dma_addr_t handle)
+{
+ dma_free_coherent(NULL, cmd->len, cmd, handle);
+}
+
+/**
+ * scm_command_to_response() - Get a pointer to a scm_response
+ * @cmd: command
+ *
+ * Returns a pointer to a response for a command.
+ */
+static inline struct scm_response *scm_command_to_response(
+ const struct scm_command *cmd)
+{
+ return (void *)cmd + cmd->resp_hdr_offset;
+}
+
+/**
+ * scm_get_command_buffer() - Get a pointer to a command buffer
+ * @cmd: command
+ *
+ * Returns a pointer to the command buffer of a command.
+ */
+static inline void *scm_get_command_buffer(const struct scm_command *cmd)
+{
+ return (void *)cmd + cmd->buf_offset;
+}
+
+/**
+ * scm_get_response_buffer() - Get a pointer to a response buffer
+ * @rsp: response
+ *
+ * Returns a pointer to a response buffer of a response.
+ */
+static inline void *scm_get_response_buffer(const struct scm_response *rsp)
+{
+ return (void *)rsp + rsp->buf_offset;
+}
+
+static int scm_remap_error(int err)
+{
+ switch (err) {
+ case SCM_ERROR:
+ return -EIO;
+ case SCM_EINVAL_ADDR:
+ case SCM_EINVAL_ARG:
+ return -EINVAL;
+ case SCM_EOPNOTSUPP:
+ return -EOPNOTSUPP;
+ case SCM_ENOMEM:
+ return -ENOMEM;
+ }
+ return -EINVAL;
+}
+
+static u32 smc(dma_addr_t cmd_addr)
+{
+ int context_id;
+ register u32 r0 asm("r0") = 1;
+ register u32 r1 asm("r1") = (u32)&context_id;
+ register u32 r2 asm("r2") = (u32)cmd_addr;
+ asm(
+ __asmeq("%0", "r0")
+ __asmeq("%1", "r0")
+ __asmeq("%2", "r1")
+ __asmeq("%3", "r2")
+ "smc #0 @ switch to secure world\n"
+ : "=r" (r0)
+ : "r" (r0), "r" (r1), "r" (r2)
+ : "r3");
+ return r0;
+}
+
+static int __scm_call(const dma_addr_t cmd)
+{
+ int ret;
+
+ /*
+ * Flush the entire cache here so callers don't have to remember
+ * to flush the cache when passing physical addresses to the secure
+ * side in the buffer.
+ */
+ flush_cache_all();
+ do {
+ ret = smc(cmd);
+ if (ret < 0) {
+ ret = scm_remap_error(ret);
+ break;
+ }
+ } while (ret == SCM_INTERRUPTED);
+
+ return ret;
+}
+
+/**
+ * scm_call() - Send an SCM command
+ * @svc_id: service identifier
+ * @cmd_id: command identifier
+ * @cmd_buf: command buffer
+ * @cmd_len: length of the command buffer
+ * @resp_buf: response buffer
+ * @resp_len: length of the response buffer
+ *
+ * Sends a command to the SCM and waits for the command to finish processing.
+ */
+int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
+ void *resp_buf, size_t resp_len)
+{
+ int ret;
+ struct scm_command *cmd;
+ struct scm_response *rsp;
+ dma_addr_t handle;
+
+ cmd = alloc_scm_command(cmd_len, resp_len, &handle);
+ if (!cmd)
+ return -ENOMEM;
+
+ cmd->id = (svc_id << 10) | cmd_id;
+ if (cmd_buf)
+ memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len);
+ wmb();
+
+ mutex_lock(&scm_lock);
+ ret = __scm_call(handle);
+ mutex_unlock(&scm_lock);
+ if (ret)
+ goto out;
+
+ rsp = scm_command_to_response(cmd);
+ do {
+ rmb();
+ } while (!rsp->is_complete);
+
+ if (resp_buf)
+ memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);
+out:
+ free_scm_command(cmd, handle);
+ return ret;
+}
+EXPORT_SYMBOL(scm_call);
+
+u32 scm_get_version(void)
+{
+ int context_id;
+ static u32 version = -1;
+ register u32 r0 asm("r0") = 0x1 << 8;
+ register u32 r1 asm("r1") = (u32)&context_id;
+
+ if (version != -1)
+ return version;
+
+ mutex_lock(&scm_lock);
+ asm(
+ __asmeq("%0", "r1")
+ __asmeq("%1", "r0")
+ __asmeq("%2", "r1")
+ "smc #0 @ switch to secure world\n"
+ : "=r" (r1)
+ : "r" (r0), "r" (r1)
+ : "r2", "r3");
+ version = r1;
+ mutex_unlock(&scm_lock);
+
+ return version;
+}
+EXPORT_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 0000000..261786b
--- /dev/null
+++ b/arch/arm/mach-msm/scm.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of Code Aurora Forum, Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef __MACH_SCM_H
+#define __MACH_SCM_H
+
+#define SCM_SVC_BOOT 0x1
+#define SCM_SVC_PIL 0x2
+
+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);
+
+#endif
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2010-12-08 15:17:13

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH v2 5/6] msm: hotplug: support cpu hotplug on msm

On Tue, Dec 07, 2010 at 08:28:20PM -0800, Jeff Ohlstein wrote:
> +static DECLARE_COMPLETION(cpu_killed);

There's changes in my tree for 2.6.38 which gets rid of this completion
from platform code.

> +void platform_cpu_die(unsigned int cpu)
> +{
> +#ifdef DEBUG
> + unsigned int this_cpu = hard_smp_processor_id();
> +
> + if (cpu != this_cpu) {
> + printk(KERN_CRIT "Eek! platform_cpu_die running on %u, should be %u\n",
> + this_cpu, cpu);
> + BUG();
> + }
> +#endif

This debug check can not possibly ever happen; it can be removed.

> +
> + printk(KERN_NOTICE "CPU%u: shutdown\n", cpu);

This printk is not required with those changes I mentioned above.

2010-12-08 15:22:07

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH v2 6/6] msm: add SMP support for msm

On Tue, Dec 07, 2010 at 08:28:21PM -0800, Jeff Ohlstein wrote:
> Signed-off-by: Jeff Ohlstein <[email protected]>
> ---
> arch/arm/mach-msm/Kconfig | 1 +
> arch/arm/mach-msm/Makefile | 1 +
> arch/arm/mach-msm/headsmp.S | 43 ++++++++++
> arch/arm/mach-msm/include/mach/smp.h | 2 +
> arch/arm/mach-msm/platsmp.c | 146 ++++++++++++++++++++++++++++++++++
> 5 files changed, 193 insertions(+), 0 deletions(-)
> create mode 100644 arch/arm/mach-msm/headsmp.S
> create mode 100644 arch/arm/mach-msm/platsmp.c
>
> diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
> index ab5338f..8c57425 100644
> --- a/arch/arm/mach-msm/Kconfig
> +++ b/arch/arm/mach-msm/Kconfig
> @@ -40,6 +40,7 @@ config ARCH_MSM8X60
> bool "MSM8X60"
> select MACH_MSM8X60_SURF if (!MACH_MSM8X60_RUMI3 && !MACH_MSM8X60_SIM \
> && !MACH_MSM8X60_FFA)
> + select ARCH_MSM_SCORPIONMP
> select ARM_GIC
> select CPU_V7
> select MSM_V2_TLMM
> diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
> index 7a11b4a..1945f9c 100644
> --- a/arch/arm/mach-msm/Makefile
> +++ b/arch/arm/mach-msm/Makefile
> @@ -21,6 +21,7 @@ obj-$(CONFIG_MSM_SMD) += last_radio_log.o
> obj-$(CONFIG_MSM_SCM) += scm.o scm-boot.o
>
> obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
> +obj-$(CONFIG_SMP) += headsmp.o platsmp.o
>
> obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o
> obj-$(CONFIG_MACH_HALIBUT) += board-halibut.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 0000000..438cfeb
> --- /dev/null
> +++ b/arch/arm/mach-msm/headsmp.S
> @@ -0,0 +1,43 @@
> +/*
> + * Copyright (c) 2003 ARM Limited
> + * All Rights Reserved
> + * Copyright (c) 2010, Code Aurora Forum. 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>
> +
> +/*
> + * 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)
> + mrc p15, 0, r0, c0, c0, 5 @ MPIDR
> + and r0, r0, #15 @ 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:
> + wfe
> + dsb @ ensure subsequent access is
> + @ after event
> +
> + 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
> +
> +1: .long .
> + .long pen_release
> diff --git a/arch/arm/mach-msm/include/mach/smp.h b/arch/arm/mach-msm/include/mach/smp.h
> index 3ff7bf5..79f94b0 100644
> --- a/arch/arm/mach-msm/include/mach/smp.h
> +++ b/arch/arm/mach-msm/include/mach/smp.h
> @@ -36,4 +36,6 @@ static inline void smp_cross_call(const struct cpumask *mask)
> gic_raise_softirq(mask, 1);
> }
>
> +extern int pen_release;
> +extern void msm_secondary_startup(void);
> #endif
> diff --git a/arch/arm/mach-msm/platsmp.c b/arch/arm/mach-msm/platsmp.c
> new file mode 100644
> index 0000000..0ab2bba
> --- /dev/null
> +++ b/arch/arm/mach-msm/platsmp.c
> @@ -0,0 +1,146 @@
> +/*
> + * Copyright (C) 2002 ARM Ltd.
> + * All Rights Reserved
> + * Copyright (c) 2010, Code Aurora Forum. 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/init.h>
> +#include <linux/cpumask.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +
> +#include <asm/hardware/gic.h>
> +#include <asm/cacheflush.h>
> +#include <asm/mach-types.h>
> +
> +#include <mach/smp.h>
> +#include <mach/msm_iomap.h>
> +
> +#include "scm-boot.h"
> +
> +#define SECONDARY_CPU_WAIT_MS 10
> +
> +#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0
> +#define SCSS_CPU1CORE_RESET 0xD80
> +#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64
> +
> +int pen_release = -1;
> +
> +int get_core_count(void)
> +{
> +#ifdef CONFIG_NR_CPUS
> + return CONFIG_NR_CPUS;
> +#else
> + return 1;
> +#endif

When would CONFIG_NR_CPUS not be set when SMP is available?

Note that linux/threads.h defines this to 1 if it's not already defined
anyway. Does this really need to be a separate function?

> +}
> +
> +/* Initialize the present map (set_cpu_present(i, true)). */
> +void smp_prepare_cpus(unsigned int max_cpus)
> +{
> + int i;
> + unsigned int cpu = smp_processor_id();
> +
> + smp_store_cpu_info(cpu);
> +
> + for (i = 0; i < max_cpus; i++)
> + set_cpu_present(i, true);
> +}
> +
> +void smp_init_cpus(void)
> +{
> + unsigned int i, ncores = get_core_count();
> +
> + for (i = 0; i < ncores; i++)
> + set_cpu_possible(i, true);
> +}
> +
> +static void prepare_cold_cpu(unsigned int cpu)
> +{
> + int ret;
> + ret = scm_set_boot_addr((void *)
> + virt_to_phys(msm_secondary_startup),
> + SCM_FLAG_COLDBOOT_CPU1);
> + if (ret == 0) {
> + void *sc1_base_ptr;
> + sc1_base_ptr = ioremap_nocache(0x00902000, SZ_4K*2);
> + if (sc1_base_ptr) {
> + writel(0, sc1_base_ptr + VDD_SC1_ARRAY_CLAMP_GFS_CTL);
> + writel(0, sc1_base_ptr + SCSS_CPU1CORE_RESET);
> + writel(3, sc1_base_ptr + SCSS_DBG_STATUS_CORE_PWRDUP);
> + iounmap(sc1_base_ptr);
> + }
> + } else
> + printk(KERN_DEBUG "Failed to set secondary core boot "
> + "address\n");
> +}
> +
> +/* Executed by primary CPU, brings other CPUs out of reset. Called at boot
> + as well as when a CPU is coming out of shutdown induced by echo 0 >
> + /sys/devices/.../cpuX.
> +*/
> +int boot_secondary(unsigned int cpu, struct task_struct *idle)
> +{
> + static int cold_boot_done;
> + int cnt = 0;
> + printk(KERN_DEBUG "Starting secondary CPU %d\n", cpu);
> +
> + if (cold_boot_done == false) {
> + prepare_cold_cpu(cpu);
> + cold_boot_done = true;
> + }
> +
> + pen_release = cpu;
> + dmac_flush_range((void *)&pen_release,
> + (void *)(&pen_release + sizeof(pen_release)));

Abuse of the DMA API. See how other platforms deal with this.

> + __asm__("sev");
> + dsb();
> +
> + /* Use smp_cross_call() to send a soft interrupt to wake up
> + * the other core.
> + */
> + smp_cross_call(cpumask_of(cpu));
> +
> + while (pen_release != 0xFFFFFFFF) {

Why 0xFFFFFFFF rather than -1 like everyone else does?

> + smp_rmb();
> + msleep_interruptible(1);
> + if (cnt++ >= SECONDARY_CPU_WAIT_MS)
> + break;
> + }

And why not use the same loop as everyone else does?

timeout = jiffies + (1 * HZ);
while (time_before(jiffies, timeout)) {
smp_rmb();
if (pen_release == -1)
break;

udelay(10);
}

IOW, what's the point of being different when you're trying to do the
same task?

> +
> + return 0;
> +}
> +
> +/* Mask for edge trigger PPIs except AVS_SVICINT and AVS_SVICINTSWDONE */
> +#define GIC_PPI_EDGE_MASK 0xFFFFD7FF
> +
> +/* Initialization routine for secondary CPUs after they are brought out of
> + * reset.
> +*/
> +void platform_secondary_init(unsigned int cpu)
> +{
> + printk(KERN_DEBUG "%s: cpu:%d\n", __func__, cpu);
> +
> + trace_hardirqs_off();

This has been moved into the generic SMP code.

> +
> + writel(GIC_PPI_EDGE_MASK, MSM_QGIC_DIST_BASE + GIC_DIST_CONFIG + 4);
> +
> + /* RUMI does not adhere to GIC spec by enabling STIs by default.
> + * Enable/clear is supposed to be RO for STIs, but is RW on RUMI.
> + */
> + if (!machine_is_msm8x60_sim())
> + writel(0x0000FFFF, MSM_QGIC_DIST_BASE + GIC_DIST_ENABLE_SET);

The gic secondary CPU initialization now takes care of this in my tree.

> +
> + /*
> + * setup GIC (GIC number NOT CPU number and the base address of the
> + * GIC CPU interface
> + */
> + gic_cpu_init(0, MSM_QGIC_CPU_BASE);

This has been renamed to gic_secondary_init() for 2.6.38.

> + pen_release = -1;
> + smp_wmb();
> +}

2010-12-08 15:23:52

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH v2 5/6] msm: hotplug: support cpu hotplug on msm

On Tue, Dec 07, 2010 at 08:28:20PM -0800, Jeff Ohlstein wrote:
> +static inline void cpu_enter_lowpower(void)
> +{
> + flush_cache_all();
> +}
> +
> +static inline void cpu_leave_lowpower(void)
> +{
> + pen_release = -1;
> + smp_wmb();
> +}
> +
> +static inline void platform_do_lowpower(unsigned int cpu)
> +{
> + /* Just enter wfe for now. */
> + for (;;) {
> + asm("wfe");
> + if (pen_release == cpu) {
> + /*
> + * OK, proper wakeup, we're done
> + */
> + break;
> + }
> + smp_rmb();
> +
> + /*
> + * 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
> + */
> + pr_debug("CPU%u: spurious wakeup call\n", cpu);
> + }
> +}

BTW, just because Realview and Versatile Express implement their hotplug
CPU tihs way, does not mean that you have to as well. If you can put the
CPU to sleep properly without keeping it in the kernel, that would be far
more preferable, than having platform_cpu_die() return on hot-plugging.

2010-12-08 16:40:26

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH v2 4/6] msm: timer: SMP timer support for msm

On Tue, Dec 07, 2010 at 08:28:19PM -0800, Jeff Ohlstein wrote:
> Signed-off-by: Jeff Ohlstein <[email protected]>

I think Thomas' comments still apply to this patch. In addition:

> @@ -65,49 +78,67 @@ struct msm_clock {
> void __iomem *regbase;
> uint32_t freq;
> uint32_t shift;
> + void *global_counter;
> + void *local_counter;

These seem to be mmio addresses, so they should be 'void __iomem *' not
just 'void *'.

> };
>
> +enum {
> + MSM_CLOCK_GPT,
> + MSM_CLOCK_DGT,
> + NR_TIMERS,
> +};

If these are supposed to be indexes to msm_clocks[], then please use
them as explicit array initializers in there - it adds useful
documentation so its not necessary to search around to find out
what's going on.

> -static cycle_t msm_gpt_read(struct clocksource *cs)
> +static cycle_t msm_read_timer_count(struct clocksource *cs)
> {
> - return readl(MSM_GPT_BASE + TIMER_COUNT_VAL);
> + struct msm_clock *clk = container_of(cs, struct msm_clock, clocksource);
> +
> + return readl(clk->global_counter) >> clk->shift;
> }

Err. This is what the core clocksource code does:

cycle_now = tc->cc->read(tc->cc);

/* calculate the delta since the last timecounter_read_delta(): */
cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask;

/* convert to nanoseconds: */
ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta);

static inline u64 cyclecounter_cyc2ns(const struct cyclecounter *cc,
cycle_t cycles)
{
u64 ret = (u64)cycles;
ret = (ret * cc->mult) >> cc->shift;
return ret;

So doesn't this result in shifting left twice?

> +#ifdef CONFIG_SMP
> +void local_timer_setup(struct clock_event_device *evt)
> +{
> + unsigned long flags;
> + struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER];
> +
> + writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
> +
> + if (!local_clock_event) {
> + writel(0, clock->regbase + TIMER_ENABLE);
> + writel(0, clock->regbase + TIMER_CLEAR);
> + writel(~0, clock->regbase + TIMER_MATCH_VAL);
> + }
> + evt->irq = clock->irq.irq;
> + evt->name = "local_timer";
> + evt->features = CLOCK_EVT_FEAT_ONESHOT;
> + evt->rating = clock->clockevent.rating;
> + evt->set_mode = msm_timer_set_mode;
> + evt->set_next_event = msm_timer_set_next_event;
> + evt->shift = clock->clockevent.shift;
> + evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift);
> + evt->max_delta_ns =
> + clockevent_delta2ns(0xf0000000 >> clock->shift, evt);
> + evt->min_delta_ns = clockevent_delta2ns(4, evt);
> + evt->cpumask = cpumask_of(smp_processor_id());
> +
> + local_clock_event = evt;
> +
> + local_irq_save(flags);
> + get_irq_chip(clock->irq.irq)->unmask(clock->irq.irq);
> + local_irq_restore(flags);

I can't make out what the situation is here. Are you using what is a
local timer on CPU0 as a global timer?

2010-12-08 16:53:48

by Russell King - ARM Linux

[permalink] [raw]
Subject: Re: [PATCH v2 2/6] msm: Secure Channel Manager (SCM) support

On Tue, Dec 07, 2010 at 08:28:17PM -0800, Jeff Ohlstein wrote:
> From: Stephen Boyd <[email protected]>
>
> SCM is the protocol used to communicate between the secure and
> non-secure code executing on the applications processor. The
> non-secure side uses a physically contiguous buffer to pass
> information to the secure side; where the buffer conforms to a
> format that is agreed upon by both sides. The use of a buffer
> allows multiple pending requests to be in flight on the secure
> side. It also benefits use cases where the command or response
> buffer contains large chunks of data.
>
> Signed-off-by: Stephen Boyd <[email protected]>
> ---
> arch/arm/mach-msm/Kconfig | 4 +
> arch/arm/mach-msm/Makefile | 1 +
> arch/arm/mach-msm/scm.c | 280 ++++++++++++++++++++++++++++++++++++++++++++
> arch/arm/mach-msm/scm.h | 41 +++++++
> 4 files changed, 326 insertions(+), 0 deletions(-)
> create mode 100644 arch/arm/mach-msm/scm.c
> create mode 100644 arch/arm/mach-msm/scm.h
>
> diff --git a/arch/arm/mach-msm/Kconfig b/arch/arm/mach-msm/Kconfig
> index 31e5fd6..ab5338f 100644
> --- a/arch/arm/mach-msm/Kconfig
> +++ b/arch/arm/mach-msm/Kconfig
> @@ -44,6 +44,7 @@ config ARCH_MSM8X60
> select CPU_V7
> select MSM_V2_TLMM
> select MSM_GPIOMUX
> + select MSM_SCM if SMP
>
> endchoice
>
> @@ -164,4 +165,7 @@ config MSM_GPIOMUX
>
> config MSM_V2_TLMM
> bool
> +
> +config MSM_SCM
> + bool
> endif
> diff --git a/arch/arm/mach-msm/Makefile b/arch/arm/mach-msm/Makefile
> index b5a7b07..eed503b 100644
> --- a/arch/arm/mach-msm/Makefile
> +++ b/arch/arm/mach-msm/Makefile
> @@ -18,6 +18,7 @@ obj-$(CONFIG_MSM_PROC_COMM) += clock.o
> obj-$(CONFIG_ARCH_QSD8X50) += sirc.o
> obj-$(CONFIG_MSM_SMD) += smd.o smd_debug.o
> obj-$(CONFIG_MSM_SMD) += last_radio_log.o
> +obj-$(CONFIG_MSM_SCM) += scm.o
>
> obj-$(CONFIG_MACH_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o devices-msm7x00.o
> obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o
> diff --git a/arch/arm/mach-msm/scm.c b/arch/arm/mach-msm/scm.c
> new file mode 100644
> index 0000000..85ed2cc
> --- /dev/null
> +++ b/arch/arm/mach-msm/scm.c
> @@ -0,0 +1,280 @@
> +/* Copyright (c) 2010, Code Aurora Forum. 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.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
> + * 02110-1301, USA.
> + */
> +
> +#include <linux/slab.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/errno.h>
> +#include <linux/err.h>
> +#include <linux/dma-mapping.h>
> +
> +#include <asm/cacheflush.h>
> +
> +#include "scm.h"
> +
> +#define SCM_ENOMEM -5
> +#define SCM_EOPNOTSUPP -4
> +#define SCM_EINVAL_ADDR -3
> +#define SCM_EINVAL_ARG -2
> +#define SCM_ERROR -1
> +#define SCM_INTERRUPTED 1
> +
> +static DEFINE_MUTEX(scm_lock);
> +
> +/**
> + * struct scm_command - one SCM command buffer
> + * @len: total available memory for command and response
> + * @buf_offset: start of command buffer
> + * @resp_hdr_offset: start of response buffer
> + * @id: command to be executed
> + *
> + * An SCM command is layed out in memory as follows:
> + *
> + * ------------------- <--- struct scm_command
> + * | command header |
> + * ------------------- <--- scm_get_command_buffer()
> + * | command buffer |
> + * ------------------- <--- struct scm_response and
> + * | response header | scm_command_to_response()
> + * ------------------- <--- scm_get_response_buffer()
> + * | response buffer |
> + * -------------------
> + *
> + * There can be arbitrary padding between the headers and buffers so
> + * you should always use the appropriate scm_get_*_buffer() routines
> + * to access the buffers in a safe manner.
> + */
> +struct scm_command {
> + u32 len;
> + u32 buf_offset;
> + u32 resp_hdr_offset;
> + u32 id;
> +};
> +
> +/**
> + * struct scm_response - one SCM response buffer
> + * @len: total available memory for response
> + * @buf_offset: start of response data relative to start of scm_response
> + * @is_complete: indicates if the command has finished processing
> + */
> +struct scm_response {
> + u32 len;
> + u32 buf_offset;
> + u32 is_complete;
> +};
> +
> +/**
> + * alloc_scm_command() - Allocate an SCM command
> + * @cmd_size: size of the command buffer
> + * @resp_size: size of the response buffer
> + * @handle: dma handle
> + *
> + * Allocate an SCM command, including enough room for the command
> + * and response headers as well as the command and response buffers.
> + *
> + * Returns a valid &scm_command on success or %NULL if the allocation fails.
> + */
> +static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size,
> + dma_addr_t *handle)
> +{
> + struct scm_command *cmd;
> + size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size +
> + resp_size;
> +
> + cmd = dma_alloc_coherent(NULL, len, handle, GFP_KERNEL);
> + if (cmd) {
> + cmd->len = len;
> + cmd->buf_offset = sizeof(*cmd);

Ok, so the real command bytes come after this struct scm_command structure.
So why not do this:

+struct scm_command {
+ u32 len;
+ u32 buf_offset;
+ u32 resp_hdr_offset;
+ u32 id;
+ u32 cmdwds[0];
+};

and

cmd->buff_offset = offsetof(struct scm_command, cmdwds);

sizeof(struct scm_command) will still be 32, but now you can do:

> + cmd->resp_hdr_offset = cmd->buf_offset + cmd_size;
> + }
> + return cmd;
> +}
> +
> +/**
> + * free_scm_command() - Free an SCM command
> + * @cmd: command to free
> + * @handle: dma handle
> + *
> + * Free an SCM command.
> + */
> +static inline void free_scm_command(struct scm_command *cmd, dma_addr_t handle)
> +{
> + dma_free_coherent(NULL, cmd->len, cmd, handle);
> +}
> +
> +/**
> + * scm_command_to_response() - Get a pointer to a scm_response
> + * @cmd: command
> + *
> + * Returns a pointer to a response for a command.
> + */
> +static inline struct scm_response *scm_command_to_response(
> + const struct scm_command *cmd)
> +{
> + return (void *)cmd + cmd->resp_hdr_offset;
> +}
> +
> +/**
> + * scm_get_command_buffer() - Get a pointer to a command buffer
> + * @cmd: command
> + *
> + * Returns a pointer to the command buffer of a command.
> + */
> +static inline void *scm_get_command_buffer(const struct scm_command *cmd)
> +{
> + return (void *)cmd + cmd->buf_offset;

and here becomes:
return (void *)cmd->cmdwds;
if it's necessary for it to exist at all.

> +}
> +
> +/**
> + * scm_get_response_buffer() - Get a pointer to a response buffer
> + * @rsp: response
> + *
> + * Returns a pointer to a response buffer of a response.
> + */
> +static inline void *scm_get_response_buffer(const struct scm_response *rsp)
> +{
> + return (void *)rsp + rsp->buf_offset;
> +}
> +
> +static int scm_remap_error(int err)
> +{
> + switch (err) {
> + case SCM_ERROR:
> + return -EIO;
> + case SCM_EINVAL_ADDR:
> + case SCM_EINVAL_ARG:
> + return -EINVAL;
> + case SCM_EOPNOTSUPP:
> + return -EOPNOTSUPP;
> + case SCM_ENOMEM:
> + return -ENOMEM;
> + }
> + return -EINVAL;
> +}
> +
> +static u32 smc(dma_addr_t cmd_addr)
> +{
> + int context_id;
> + register u32 r0 asm("r0") = 1;
> + register u32 r1 asm("r1") = (u32)&context_id;
> + register u32 r2 asm("r2") = (u32)cmd_addr;
> + asm(
> + __asmeq("%0", "r0")
> + __asmeq("%1", "r0")
> + __asmeq("%2", "r1")
> + __asmeq("%3", "r2")
> + "smc #0 @ switch to secure world\n"
> + : "=r" (r0)
> + : "r" (r0), "r" (r1), "r" (r2)
> + : "r3");
> + return r0;
> +}
> +
> +static int __scm_call(const dma_addr_t cmd)
> +{
> + int ret;
> +
> + /*
> + * Flush the entire cache here so callers don't have to remember
> + * to flush the cache when passing physical addresses to the secure
> + * side in the buffer.
> + */
> + flush_cache_all();
> + do {
> + ret = smc(cmd);
> + if (ret < 0) {
> + ret = scm_remap_error(ret);
> + break;
> + }
> + } while (ret == SCM_INTERRUPTED);
> +
> + return ret;
> +}
> +
> +/**
> + * scm_call() - Send an SCM command
> + * @svc_id: service identifier
> + * @cmd_id: command identifier
> + * @cmd_buf: command buffer
> + * @cmd_len: length of the command buffer
> + * @resp_buf: response buffer
> + * @resp_len: length of the response buffer
> + *
> + * Sends a command to the SCM and waits for the command to finish processing.
> + */
> +int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
> + void *resp_buf, size_t resp_len)
> +{
> + int ret;
> + struct scm_command *cmd;
> + struct scm_response *rsp;
> + dma_addr_t handle;
> +
> + cmd = alloc_scm_command(cmd_len, resp_len, &handle);
> + if (!cmd)
> + return -ENOMEM;
> +
> + cmd->id = (svc_id << 10) | cmd_id;
> + if (cmd_buf)
> + memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len);
> + wmb();
> +
> + mutex_lock(&scm_lock);
> + ret = __scm_call(handle);
> + mutex_unlock(&scm_lock);
> + if (ret)
> + goto out;
> +
> + rsp = scm_command_to_response(cmd);
> + do {
> + rmb();
> + } while (!rsp->is_complete);

Hmm, this seems weird. Doesn't the SMC call only return once the request
has been completed?

2010-12-08 19:59:52

by Stephen Boyd

[permalink] [raw]
Subject: Re: [PATCH v2 2/6] msm: Secure Channel Manager (SCM) support

On 12/08/2010 08:53 AM, Russell King - ARM Linux wrote:
>
> Ok, so the real command bytes come after this struct scm_command structure.
> So why not do this:
>
> +struct scm_command {
> + u32 len;
> + u32 buf_offset;
> + u32 resp_hdr_offset;
> + u32 id;
> + u32 cmdwds[0];
> +};
>
> and
>
> cmd->buff_offset = offsetof(struct scm_command, cmdwds);
>
> sizeof(struct scm_command) will still be 32, but now you can do:
[snip]
>> +/**
>> + * scm_get_command_buffer() - Get a pointer to a command buffer
>> + * @cmd: command
>> + *
>> + * Returns a pointer to the command buffer of a command.
>> + */
>> +static inline void *scm_get_command_buffer(const struct scm_command *cmd)
>> +{
>> + return (void *)cmd + cmd->buf_offset;
>
> and here becomes:
> return (void *)cmd->cmdwds;
> if it's necessary for it to exist at all.
>

Ok. I was thinking that the command should be treated the same as the
response, since the padding between the response header and response
buffer can be arbitrary (and not under our control). This isn't true for
the command header and command buffer since we define the padding.

I still like the idea of scm_get_command_buffer() though since it duals
scm_get_response_buffer().

>> +int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len,
>> + void *resp_buf, size_t resp_len)
>> +{
>> + int ret;
>> + struct scm_command *cmd;
>> + struct scm_response *rsp;
>> + dma_addr_t handle;
>> +
>> + cmd = alloc_scm_command(cmd_len, resp_len, &handle);
>> + if (!cmd)
>> + return -ENOMEM;
>> +
>> + cmd->id = (svc_id << 10) | cmd_id;
>> + if (cmd_buf)
>> + memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len);
>> + wmb();
>> +
>> + mutex_lock(&scm_lock);
>> + ret = __scm_call(handle);
>> + mutex_unlock(&scm_lock);
>> + if (ret)
>> + goto out;
>> +
>> + rsp = scm_command_to_response(cmd);
>> + do {
>> + rmb();
>> + } while (!rsp->is_complete);
>
> Hmm, this seems weird. Doesn't the SMC call only return once the request
> has been completed?

No. __scm_call() returns once the request has been accepted. scm_call()
returns once the request has completed. Multiple scm_command's can be in
flight at the same time on the secure side.

--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2010-12-09 03:41:49

by Jeff Ohlstein

[permalink] [raw]
Subject: Re: [PATCH v2 6/6] msm: add SMP support for msm

Russell King - ARM Linux wrote:
> On Tue, Dec 07, 2010 at 08:28:21PM -0800, Jeff Ohlstein wrote:
>> +int get_core_count(void)
>> +{
>> +#ifdef CONFIG_NR_CPUS
>> + return CONFIG_NR_CPUS;
>> +#else
>> + return 1;
>> +#endif
>>
>
> When would CONFIG_NR_CPUS not be set when SMP is available?
>
> Note that linux/threads.h defines this to 1 if it's not already defined
> anyway. Does this really need to be a separate function?
>
>
Removed.
>> +int boot_secondary(unsigned int cpu, struct task_struct *idle)
>> +{
>> + static int cold_boot_done;
>> + int cnt = 0;
>> + printk(KERN_DEBUG "Starting secondary CPU %d\n", cpu);
>> +
>> + if (cold_boot_done == false) {
>> + prepare_cold_cpu(cpu);
>> + cold_boot_done = true;
>> + }
>> +
>> + pen_release = cpu;
>> + dmac_flush_range((void *)&pen_release,
>> + (void *)(&pen_release + sizeof(pen_release)));
>>
>
> Abuse of the DMA API. See how other platforms deal with this.
>
>
Fixed to use __cpuc_flush_dcache_area and outer_clean_range.
>> + __asm__("sev");
>> + dsb();
>> +
>> + /* Use smp_cross_call() to send a soft interrupt to wake up
>> + * the other core.
>> + */
>> + smp_cross_call(cpumask_of(cpu));
>> +
>> + while (pen_release != 0xFFFFFFFF) {
>>
>
> Why 0xFFFFFFFF rather than -1 like everyone else does?
>
>
Removed due to below.
>> + smp_rmb();
>> + msleep_interruptible(1);
>> + if (cnt++ >= SECONDARY_CPU_WAIT_MS)
>> + break;
>> + }
>>
>
> And why not use the same loop as everyone else does?
>
> timeout = jiffies + (1 * HZ);
> while (time_before(jiffies, timeout)) {
> smp_rmb();
> if (pen_release == -1)
> break;
>
> udelay(10);
> }
>
> IOW, what's the point of being different when you're trying to do the
> same task?
>
>
Done.
>> +
>> + return 0;
>> +}
>> +
>> +/* Mask for edge trigger PPIs except AVS_SVICINT and AVS_SVICINTSWDONE */
>> +#define GIC_PPI_EDGE_MASK 0xFFFFD7FF
>> +
>> +/* Initialization routine for secondary CPUs after they are brought out of
>> + * reset.
>> +*/
>> +void platform_secondary_init(unsigned int cpu)
>> +{
>> + printk(KERN_DEBUG "%s: cpu:%d\n", __func__, cpu);
>> +
>> + trace_hardirqs_off();
>>
>
> This has been moved into the generic SMP code.
>
>
I've rebased onto your gic patches and your smp series, and removed this.
>> + if (!machine_is_msm8x60_sim())
>> + writel(0x0000FFFF, MSM_QGIC_DIST_BASE + GIC_DIST_ENABLE_SET);
>>
>
> The gic secondary CPU initialization now takes care of this in my tree.
>
>
Removed.
>> +
>> + /*
>> + * setup GIC (GIC number NOT CPU number and the base address of the
>> + * GIC CPU interface
>> + */
>> + gic_cpu_init(0, MSM_QGIC_CPU_BASE);
>>
>
> This has been renamed to gic_secondary_init() for 2.6.38.
>
>
Done.

--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2010-12-09 04:22:39

by Jeff Ohlstein

[permalink] [raw]
Subject: Re: [PATCH v2 4/6] msm: timer: SMP timer support for msm

Russell King - ARM Linux wrote:
> On Tue, Dec 07, 2010 at 08:28:19PM -0800, Jeff Ohlstein wrote:
>> Signed-off-by: Jeff Ohlstein <[email protected]>
>
> I think Thomas' comments still apply to this patch. In addition:
>
>> @@ -65,49 +78,67 @@ struct msm_clock {
>> void __iomem *regbase;
>> uint32_t freq;
>> uint32_t shift;
>> + void *global_counter;
>> + void *local_counter;
>
> These seem to be mmio addresses, so they should be 'void __iomem *' not
> just 'void *'.
>

Done.

>> +enum {
>> + MSM_CLOCK_GPT,
>> + MSM_CLOCK_DGT,
>> + NR_TIMERS,
>> +};
>
> If these are supposed to be indexes to msm_clocks[], then please use
> them as explicit array initializers in there - it adds useful
> documentation so its not necessary to search around to find out
> what's going on.
>

Yes that is what they are for, done.

>> -static cycle_t msm_gpt_read(struct clocksource *cs)
>> +static cycle_t msm_read_timer_count(struct clocksource *cs)
>> {
>> - return readl(MSM_GPT_BASE + TIMER_COUNT_VAL);
>> + struct msm_clock *clk = container_of(cs, struct msm_clock, clocksource);
>> +
>> + return readl(clk->global_counter) >> clk->shift;
>> }
>
> Err. This is what the core clocksource code does:
>
> cycle_now = tc->cc->read(tc->cc);
>
> /* calculate the delta since the last timecounter_read_delta(): */
> cycle_delta = (cycle_now - tc->cycle_last) & tc->cc->mask;
>
> /* convert to nanoseconds: */
> ns_offset = cyclecounter_cyc2ns(tc->cc, cycle_delta);
>
> static inline u64 cyclecounter_cyc2ns(const struct cyclecounter *cc,
> cycle_t cycles)
> {
> u64 ret = (u64)cycles;
> ret = (ret * cc->mult) >> cc->shift;
> return ret;
>
> So doesn't this result in shifting left twice?
>
Seems so, fixed.
>> +#ifdef CONFIG_SMP
>> +void local_timer_setup(struct clock_event_device *evt)
>> +{
>> + unsigned long flags;
>> + struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER];
>> +
>> + writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
>> +
>> + if (!local_clock_event) {
>> + writel(0, clock->regbase + TIMER_ENABLE);
>> + writel(0, clock->regbase + TIMER_CLEAR);
>> + writel(~0, clock->regbase + TIMER_MATCH_VAL);
>> + }
>> + evt->irq = clock->irq.irq;
>> + evt->name = "local_timer";
>> + evt->features = CLOCK_EVT_FEAT_ONESHOT;
>> + evt->rating = clock->clockevent.rating;
>> + evt->set_mode = msm_timer_set_mode;
>> + evt->set_next_event = msm_timer_set_next_event;
>> + evt->shift = clock->clockevent.shift;
>> + evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift);
>> + evt->max_delta_ns =
>> + clockevent_delta2ns(0xf0000000 >> clock->shift, evt);
>> + evt->min_delta_ns = clockevent_delta2ns(4, evt);
>> + evt->cpumask = cpumask_of(smp_processor_id());
>> +
>> + local_clock_event = evt;
>> +
>> + local_irq_save(flags);
>> + get_irq_chip(clock->irq.irq)->unmask(clock->irq.irq);
>> + local_irq_restore(flags);
>
> I can't make out what the situation is here. Are you using what is a
> local timer on CPU0 as a global timer?
>
Yes, sorry for the lack of clarity here, I will add better commit text
explaining it. The chip has a set of timers private to each core. They are
mapped at the same address, and which one you access is dependent on
which core
you are running on. However, since Linux wants a non-percpu clocksource
that it
can read from either core, I have decided to designate cpu 0's timer as the
global timer. So, we ought to configure the local clock_event_device to
be the
same as whichever timer we are using on cpu0.

-Jeff

--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2010-12-10 02:07:55

by Jeff Ohlstein

[permalink] [raw]
Subject: Re: [PATCH v2 4/6] msm: timer: SMP timer support for msm

Jeff Ohlstein wrote:
> Signed-off-by: Jeff Ohlstein <[email protected]>
> ---
> arch/arm/mach-msm/include/mach/msm_iomap-8x60.h | 6 +-
> arch/arm/mach-msm/io.c | 1 +
> arch/arm/mach-msm/timer.c | 130 +++++++++++++++++++----
> 3 files changed, 115 insertions(+), 22 deletions(-)
>
> 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 45bab50..873e0b7 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 @@
>
> #define MSM_TMR_BASE IOMEM(0xF0200000)
> #define MSM_TMR_PHYS 0x02000000
> -#define MSM_TMR_SIZE (SZ_1M)
> +#define MSM_TMR_SIZE SZ_4K
> +
> +#define MSM_TMR0_BASE IOMEM(0xF0201000)
> +#define MSM_TMR0_PHYS 0x02040000
> +#define MSM_TMR0_SIZE SZ_4K
>
> #define MSM_GPT_BASE (MSM_TMR_BASE + 0x4)
> #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 d36b610..b826b6b 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 = {
> MSM_DEVICE(QGIC_DIST),
> MSM_DEVICE(QGIC_CPU),
> MSM_DEVICE(TMR),
> + MSM_DEVICE(TMR0),
> MSM_DEVICE(ACC),
> MSM_DEVICE(GCC),
> };
> diff --git a/arch/arm/mach-msm/timer.c b/arch/arm/mach-msm/timer.c
> index 950100f..92ac220 100644
> --- a/arch/arm/mach-msm/timer.c
> +++ b/arch/arm/mach-msm/timer.c
> @@ -47,6 +47,19 @@ enum {
>
> #define GPT_HZ 32768
>
> +enum timer_location {
> + LOCAL_TIMER = 0,
> + GLOBAL_TIMER = 1,
> +};
> +
> +#ifdef MSM_TMR0_BASE
> +#define MSM_TMR_GLOBAL (MSM_TMR0_BASE - MSM_TMR_BASE)
> +#else
> +#define MSM_TMR_GLOBAL 0
> +#endif
> +
> +#define MSM_GLOBAL_TIMER MSM_CLOCK_DGT
> +
> #if defined(CONFIG_ARCH_QSD8X50)
> #define DGT_HZ (19200000 / 4) /* 19.2 MHz / 4 by default */
> #define MSM_DGT_SHIFT (0)
> @@ -65,49 +78,67 @@ struct msm_clock {
> void __iomem *regbase;
> uint32_t freq;
> uint32_t shift;
> + void *global_counter;
> + void *local_counter;
> };
>
> +enum {
> + MSM_CLOCK_GPT,
> + MSM_CLOCK_DGT,
> + NR_TIMERS,
> +};
> +
> +
> +static struct msm_clock msm_clocks[];
> +static struct clock_event_device *local_clock_event;
> +
> static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
> {
> struct clock_event_device *evt = dev_id;
> + if (smp_processor_id() != 0)
> + evt = local_clock_event;
> + if (evt->event_handler == NULL)
> + return IRQ_HANDLED;
> evt->event_handler(evt);
> return IRQ_HANDLED;
> }
>
> -static cycle_t msm_gpt_read(struct clocksource *cs)
> +static cycle_t msm_read_timer_count(struct clocksource *cs)
> {
> - return readl(MSM_GPT_BASE + TIMER_COUNT_VAL);
> + struct msm_clock *clk = container_of(cs, struct msm_clock, clocksource);
> +
> + return readl(clk->global_counter) >> clk->shift;
> }
>
> -static cycle_t msm_dgt_read(struct clocksource *cs)
> +static struct msm_clock *clockevent_to_clock(struct clock_event_device *evt)
> {
> - return readl(MSM_DGT_BASE + TIMER_COUNT_VAL) >> MSM_DGT_SHIFT;
> +#ifdef CONFIG_SMP
> + int i;
> + for (i = 0; i < NR_TIMERS; i++)
> + if (evt == &(msm_clocks[i].clockevent))
> + return &msm_clocks[i];
> + return &msm_clocks[MSM_GLOBAL_TIMER];
> +#else
> + return container_of(evt, struct msm_clock, clockevent);
> +#endif
> }
>
> static int msm_timer_set_next_event(unsigned long cycles,
> struct clock_event_device *evt)
> {
> - struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent);
> - uint32_t now = readl(clock->regbase + TIMER_COUNT_VAL);
> + struct msm_clock *clock = clockevent_to_clock(evt);
> + uint32_t now = readl(clock->local_counter);
> uint32_t alarm = now + (cycles << clock->shift);
> - int late;
>
> writel(alarm, clock->regbase + TIMER_MATCH_VAL);
> - now = readl(clock->regbase + TIMER_COUNT_VAL);
> - late = now - alarm;
> - if (late >= (-2 << clock->shift) && late < DGT_HZ*5) {
> - printk(KERN_NOTICE "msm_timer_set_next_event(%lu) clock %s, "
> - "alarm already expired, now %x, alarm %x, late %d\n",
> - cycles, clock->clockevent.name, now, alarm, late);
> - return -ETIME;
> - }
> return 0;
> }
>
> static void msm_timer_set_mode(enum clock_event_mode mode,
> struct clock_event_device *evt)
> {
> - struct msm_clock *clock = container_of(evt, struct msm_clock, clockevent);
> + struct msm_clock *clock = clockevent_to_clock(evt);
> +
> switch (mode) {
> case CLOCK_EVT_MODE_RESUME:
> case CLOCK_EVT_MODE_PERIODIC:
> @@ -135,7 +166,7 @@ static struct msm_clock msm_clocks[] = {
> .clocksource = {
> .name = "gp_timer",
> .rating = 200,
> - .read = msm_gpt_read,
> + .read = msm_read_timer_count,
> .mask = CLOCKSOURCE_MASK(32),
> .shift = 17,
> .flags = CLOCK_SOURCE_IS_CONTINUOUS,
> @@ -148,7 +179,10 @@ static struct msm_clock msm_clocks[] = {
> .irq = INT_GP_TIMER_EXP
> },
> .regbase = MSM_GPT_BASE,
> - .freq = GPT_HZ
> + .freq = GPT_HZ,
> + .local_counter = MSM_GPT_BASE + TIMER_COUNT_VAL,
> + .global_counter = MSM_GPT_BASE + TIMER_COUNT_VAL +
> + MSM_TMR_GLOBAL,
> },
> {
> .clockevent = {
> @@ -162,7 +196,7 @@ static struct msm_clock msm_clocks[] = {
> .clocksource = {
> .name = "dg_timer",
> .rating = 300,
> - .read = msm_dgt_read,
> + .read = msm_read_timer_count,
> .mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT)),
> .shift = 24 - MSM_DGT_SHIFT,
> .flags = CLOCK_SOURCE_IS_CONTINUOUS,
> @@ -176,7 +210,10 @@ static struct msm_clock msm_clocks[] = {
> },
> .regbase = MSM_DGT_BASE,
> .freq = DGT_HZ >> MSM_DGT_SHIFT,
> - .shift = MSM_DGT_SHIFT
> + .shift = MSM_DGT_SHIFT,
> + .local_counter = MSM_DGT_BASE + TIMER_COUNT_VAL,
> + .global_counter = MSM_DGT_BASE + TIMER_COUNT_VAL +
> + MSM_TMR_GLOBAL,
> }
> };
>
> @@ -185,7 +222,7 @@ static void __init msm_timer_init(void)
> int i;
> int res;
>
> -#ifdef CONFIG_ARCH_MSM8X60
> +#ifdef CONFIG_ARCH_MSM_SCORPIONMP
> writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
> #endif
>
> @@ -220,6 +257,57 @@ static void __init msm_timer_init(void)
> }
> }
>
> +#ifdef CONFIG_SMP
> +void local_timer_setup(struct clock_event_device *evt)
> +{
> + unsigned long flags;
> + struct msm_clock *clock = &msm_clocks[MSM_GLOBAL_TIMER];
> +
> + writel(DGT_CLK_CTL_DIV_4, MSM_TMR_BASE + DGT_CLK_CTL);
> +
> + if (!local_clock_event) {
> + writel(0, clock->regbase + TIMER_ENABLE);
> + writel(0, clock->regbase + TIMER_CLEAR);
> + writel(~0, clock->regbase + TIMER_MATCH_VAL);
> + }
> + evt->irq = clock->irq.irq;
> + evt->name = "local_timer";
> + evt->features = CLOCK_EVT_FEAT_ONESHOT;
> + evt->rating = clock->clockevent.rating;
> + evt->set_mode = msm_timer_set_mode;
> + evt->set_next_event = msm_timer_set_next_event;
> + evt->shift = clock->clockevent.shift;
> + evt->mult = div_sc(clock->freq, NSEC_PER_SEC, evt->shift);
> + evt->max_delta_ns =
> + clockevent_delta2ns(0xf0000000 >> clock->shift, evt);
> + evt->min_delta_ns = clockevent_delta2ns(4, evt);
> + evt->cpumask = cpumask_of(smp_processor_id());
> +
> + local_clock_event = evt;
> +
> + local_irq_save(flags);
> + get_irq_chip(clock->irq.irq)->unmask(clock->irq.irq);
> + local_irq_restore(flags);
> +
> + clockevents_register_device(evt);
> +}
> +
> +inline int local_timer_ack(void)
> +{
> + return 1;
> +}
> +
> +#ifdef CONFIG_HOTPLUG_CPU
> +void __cpuexit local_timer_stop(void)
> +{
> + struct msm_clock *clock = clockevent_to_clock(local_clock_event);
> + writel(0, clock->regbase + TIMER_MATCH_VAL);
> + writel(0, clock->regbase + TIMER_ENABLE);
> + get_irq_chip(local_clock_event->irq)->mask(local_clock_event->irq);
> +}
> +#endif
> +#endif
> +
> struct sys_timer msm_timer = {
> .init = msm_timer_init
> };
>
Thomas, do you have any additional thoughts on this? Did I answer your
questions
sufficiently in the other thread?

--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2010-12-10 02:25:03

by Thomas Gleixner

[permalink] [raw]
Subject: Re: [PATCH v2 4/6] msm: timer: SMP timer support for msm

On Thu, 9 Dec 2010, Jeff Ohlstein wrote:
> > struct sys_timer msm_timer = {
> > .init = msm_timer_init
> > };
> >
> Thomas, do you have any additional thoughts on this? Did I answer your
> questions
> sufficiently in the other thread?

I think so. I did not realize the PPI issue in the first place.

Thanks,

tglx