Merge tag 'qcom-cleanup-for-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom into next/cleanup
Merge "General cleanups for MSM/QCOM for 3.15" from Kumar Gala: Split of the multiplatform support for the Qualcomm SoCs into a mach-qcom while we leave mach-msm as legacy support. Also, some smp and device tree related cleanups. * tag 'qcom-cleanup-for-3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom: ARM: qcom: Rename various msm prefixed functions to qcom clocksource: qcom: split building of legacy vs multiplatform support ARM: qcom: Split Qualcomm support into legacy and multiplatform clocksource: qcom: Move clocksource code out of mach-msm ARM: msm: kill off hotplug.c ARM: msm: Remove pen_release usage ARM: dts: msm: split out msm8660 and msm8960 soc into dts include Signed-off-by: Olof Johansson <olof@lixom.net>
This commit is contained in:
@@ -1,50 +1,9 @@
|
||||
config ARCH_MSM
|
||||
bool
|
||||
|
||||
config ARCH_MSM_DT
|
||||
bool "Qualcomm MSM DT Support" if ARCH_MULTI_V7
|
||||
select ARCH_MSM
|
||||
select ARCH_REQUIRE_GPIOLIB
|
||||
select CLKSRC_OF
|
||||
select GENERIC_CLOCKEVENTS
|
||||
help
|
||||
Support for Qualcomm's devicetree based MSM systems.
|
||||
|
||||
if ARCH_MSM
|
||||
|
||||
menu "Qualcomm MSM SoC Selection"
|
||||
depends on ARCH_MSM_DT
|
||||
|
||||
config ARCH_MSM8X60
|
||||
bool "Enable support for MSM8X60"
|
||||
select ARM_GIC
|
||||
select CPU_V7
|
||||
select HAVE_SMP
|
||||
select MSM_SCM if SMP
|
||||
select MSM_TIMER
|
||||
|
||||
config ARCH_MSM8960
|
||||
bool "Enable support for MSM8960"
|
||||
select ARM_GIC
|
||||
select CPU_V7
|
||||
select HAVE_SMP
|
||||
select MSM_SCM if SMP
|
||||
select MSM_TIMER
|
||||
|
||||
config ARCH_MSM8974
|
||||
bool "Enable support for MSM8974"
|
||||
select ARM_GIC
|
||||
select CPU_V7
|
||||
select HAVE_ARM_ARCH_TIMER
|
||||
select HAVE_SMP
|
||||
select MSM_SCM if SMP
|
||||
|
||||
endmenu
|
||||
|
||||
choice
|
||||
prompt "Qualcomm MSM SoC Type"
|
||||
default ARCH_MSM7X00A
|
||||
depends on ARCH_MSM_NODT
|
||||
depends on ARCH_MSM
|
||||
|
||||
config ARCH_MSM7X00A
|
||||
bool "MSM7x00A / MSM7x01A"
|
||||
@@ -54,7 +13,7 @@ config ARCH_MSM7X00A
|
||||
select MACH_TROUT if !MACH_HALIBUT
|
||||
select MSM_PROC_COMM
|
||||
select MSM_SMD
|
||||
select MSM_TIMER
|
||||
select CLKSRC_QCOM
|
||||
select MSM_SMD_PKG3
|
||||
|
||||
config ARCH_MSM7X30
|
||||
@@ -66,7 +25,7 @@ config ARCH_MSM7X30
|
||||
select MSM_GPIOMUX
|
||||
select MSM_PROC_COMM
|
||||
select MSM_SMD
|
||||
select MSM_TIMER
|
||||
select CLKSRC_QCOM
|
||||
select MSM_VIC
|
||||
|
||||
config ARCH_QSD8X50
|
||||
@@ -78,7 +37,7 @@ config ARCH_QSD8X50
|
||||
select MSM_GPIOMUX
|
||||
select MSM_PROC_COMM
|
||||
select MSM_SMD
|
||||
select MSM_TIMER
|
||||
select CLKSRC_QCOM
|
||||
select MSM_VIC
|
||||
|
||||
endchoice
|
||||
@@ -99,7 +58,7 @@ config MSM_VIC
|
||||
bool
|
||||
|
||||
menu "Qualcomm MSM Board Type"
|
||||
depends on ARCH_MSM_NODT
|
||||
depends on ARCH_MSM
|
||||
|
||||
config MACH_HALIBUT
|
||||
depends on ARCH_MSM
|
||||
@@ -153,7 +112,4 @@ config MSM_GPIOMUX
|
||||
config MSM_SCM
|
||||
bool
|
||||
|
||||
config MSM_TIMER
|
||||
bool
|
||||
|
||||
endif
|
||||
|
@@ -1,4 +1,3 @@
|
||||
obj-$(CONFIG_MSM_TIMER) += timer.o
|
||||
obj-$(CONFIG_MSM_PROC_COMM) += clock.o
|
||||
|
||||
obj-$(CONFIG_MSM_VIC) += irq-vic.o
|
||||
@@ -14,18 +13,11 @@ obj-$(CONFIG_ARCH_QSD8X50) += dma.o io.o
|
||||
|
||||
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
|
||||
|
||||
CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1)
|
||||
|
||||
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_TROUT) += board-trout.o board-trout-gpio.o board-trout-mmc.o board-trout-panel.o devices-msm7x00.o
|
||||
obj-$(CONFIG_MACH_HALIBUT) += board-halibut.o devices-msm7x00.o
|
||||
obj-$(CONFIG_ARCH_MSM7X30) += board-msm7x30.o devices-msm7x30.o
|
||||
obj-$(CONFIG_ARCH_QSD8X50) += board-qsd8x50.o devices-qsd8x50.o
|
||||
obj-$(CONFIG_ARCH_MSM_DT) += board-dt.o
|
||||
obj-$(CONFIG_MSM_GPIOMUX) += gpiomux.o
|
||||
obj-$(CONFIG_ARCH_QSD8X50) += gpiomux-8x50.o
|
||||
|
@@ -1,41 +0,0 @@
|
||||
/* Copyright (c) 2010-2012,2013 The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
|
||||
#include <asm/mach/arch.h>
|
||||
#include <asm/mach/map.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
static const char * const msm_dt_match[] __initconst = {
|
||||
"qcom,msm8660-fluid",
|
||||
"qcom,msm8660-surf",
|
||||
"qcom,msm8960-cdp",
|
||||
NULL
|
||||
};
|
||||
|
||||
static const char * const apq8074_dt_match[] __initconst = {
|
||||
"qcom,apq8074-dragonboard",
|
||||
NULL
|
||||
};
|
||||
|
||||
DT_MACHINE_START(MSM_DT, "Qualcomm MSM (Flattened Device Tree)")
|
||||
.smp = smp_ops(msm_smp_ops),
|
||||
.dt_compat = msm_dt_match,
|
||||
MACHINE_END
|
||||
|
||||
DT_MACHINE_START(APQ_DT, "Qualcomm MSM (Flattened Device Tree)")
|
||||
.dt_compat = apq8074_dt_match,
|
||||
MACHINE_END
|
@@ -24,7 +24,6 @@ extern void __iomem *__msm_ioremap_caller(phys_addr_t phys_addr, size_t size,
|
||||
unsigned int mtype, void *caller);
|
||||
|
||||
extern struct smp_operations msm_smp_ops;
|
||||
extern void msm_cpu_die(unsigned int cpu);
|
||||
|
||||
struct msm_mmc_platform_data;
|
||||
|
||||
|
@@ -1,39 +0,0 @@
|
||||
/*
|
||||
* linux/arch/arm/mach-realview/headsmp.S
|
||||
*
|
||||
* Copyright (c) 2003 ARM Limited
|
||||
* 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.
|
||||
*/
|
||||
ENTRY(msm_secondary_startup)
|
||||
mrc p15, 0, r0, c0, c0, 5
|
||||
and r0, r0, #15
|
||||
adr r4, 1f
|
||||
ldmia r4, {r5, r6}
|
||||
sub r4, r4, r5
|
||||
add r6, r6, r4
|
||||
pen: ldr r7, [r6]
|
||||
cmp r7, r0
|
||||
bne pen
|
||||
|
||||
/*
|
||||
* we've been released from the holding pen: secondary_stack
|
||||
* should now contain the SVC stack for this core
|
||||
*/
|
||||
b secondary_startup
|
||||
ENDPROC(msm_secondary_startup)
|
||||
|
||||
.align
|
||||
1: .long .
|
||||
.long pen_release
|
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* 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 <asm/smp_plat.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
static inline void cpu_enter_lowpower(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void cpu_leave_lowpower(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void platform_do_lowpower(unsigned int cpu)
|
||||
{
|
||||
/* Just enter wfi for now. TODO: Properly shut off the cpu. */
|
||||
for (;;) {
|
||||
/*
|
||||
* here's the WFI
|
||||
*/
|
||||
asm("wfi"
|
||||
:
|
||||
:
|
||||
: "memory", "cc");
|
||||
|
||||
if (pen_release == cpu_logical_map(cpu)) {
|
||||
/*
|
||||
* OK, proper wakeup, we're done
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* getting here, means that we have come out of WFI without
|
||||
* having been woken up - this shouldn't happen
|
||||
*
|
||||
* The trouble is, letting people know about this is not really
|
||||
* possible, since we are currently running incoherently, and
|
||||
* therefore cannot safely call printk() or anything else
|
||||
*/
|
||||
pr_debug("CPU%u: spurious wakeup call\n", cpu);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* platform-specific code to shutdown a CPU
|
||||
*
|
||||
* Called with IRQs disabled
|
||||
*/
|
||||
void __ref msm_cpu_die(unsigned int cpu)
|
||||
{
|
||||
/*
|
||||
* 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();
|
||||
}
|
@@ -1,161 +0,0 @@
|
||||
/*
|
||||
* 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/errno.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/jiffies.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/cputype.h>
|
||||
#include <asm/mach-types.h>
|
||||
#include <asm/smp_plat.h>
|
||||
|
||||
#include "scm-boot.h"
|
||||
#include "common.h"
|
||||
|
||||
#define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x15A0
|
||||
#define SCSS_CPU1CORE_RESET 0xD80
|
||||
#define SCSS_DBG_STATUS_CORE_PWRDUP 0xE64
|
||||
|
||||
extern void msm_secondary_startup(void);
|
||||
|
||||
static DEFINE_SPINLOCK(boot_lock);
|
||||
|
||||
static inline int get_core_count(void)
|
||||
{
|
||||
/* 1 + the PART[1:0] field of MIDR */
|
||||
return ((read_cpuid_id() >> 4) & 3) + 1;
|
||||
}
|
||||
|
||||
static void msm_secondary_init(unsigned int cpu)
|
||||
{
|
||||
/*
|
||||
* let the primary processor know we're out of the
|
||||
* pen, then head off into the C entry point
|
||||
*/
|
||||
pen_release = -1;
|
||||
smp_wmb();
|
||||
|
||||
/*
|
||||
* Synchronise with the boot thread.
|
||||
*/
|
||||
spin_lock(&boot_lock);
|
||||
spin_unlock(&boot_lock);
|
||||
}
|
||||
|
||||
static void prepare_cold_cpu(unsigned int cpu)
|
||||
{
|
||||
int ret;
|
||||
ret = scm_set_boot_addr(virt_to_phys(msm_secondary_startup),
|
||||
SCM_FLAG_COLDBOOT_CPU1);
|
||||
if (ret == 0) {
|
||||
void __iomem *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");
|
||||
}
|
||||
|
||||
static int msm_boot_secondary(unsigned int cpu, struct task_struct *idle)
|
||||
{
|
||||
unsigned long timeout;
|
||||
static int cold_boot_done;
|
||||
|
||||
/* Only need to bring cpu out of reset this way once */
|
||||
if (cold_boot_done == false) {
|
||||
prepare_cold_cpu(cpu);
|
||||
cold_boot_done = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* set synchronisation state between this boot processor
|
||||
* and the secondary one
|
||||
*/
|
||||
spin_lock(&boot_lock);
|
||||
|
||||
/*
|
||||
* The secondary processor is waiting to be released from
|
||||
* the holding pen - release it, then wait for it to flag
|
||||
* that it has been released by resetting pen_release.
|
||||
*
|
||||
* Note that "pen_release" is the hardware CPU ID, whereas
|
||||
* "cpu" is Linux's internal ID.
|
||||
*/
|
||||
pen_release = cpu_logical_map(cpu);
|
||||
sync_cache_w(&pen_release);
|
||||
|
||||
/*
|
||||
* Send the secondary CPU a soft interrupt, thereby causing
|
||||
* the boot monitor to read the system wide flags register,
|
||||
* and branch to the address found there.
|
||||
*/
|
||||
arch_send_wakeup_ipi_mask(cpumask_of(cpu));
|
||||
|
||||
timeout = jiffies + (1 * HZ);
|
||||
while (time_before(jiffies, timeout)) {
|
||||
smp_rmb();
|
||||
if (pen_release == -1)
|
||||
break;
|
||||
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
/*
|
||||
* now the secondary core is starting up let it run its
|
||||
* calibrations, then wait for it to finish
|
||||
*/
|
||||
spin_unlock(&boot_lock);
|
||||
|
||||
return pen_release != -1 ? -ENOSYS : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise the CPU possible map early - this describes the CPUs
|
||||
* which may be present or become present in the system. The msm8x60
|
||||
* does not support the ARM SCU, so just set the possible cpu mask to
|
||||
* NR_CPUS.
|
||||
*/
|
||||
static void __init msm_smp_init_cpus(void)
|
||||
{
|
||||
unsigned int i, ncores = get_core_count();
|
||||
|
||||
if (ncores > nr_cpu_ids) {
|
||||
pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
|
||||
ncores, nr_cpu_ids);
|
||||
ncores = nr_cpu_ids;
|
||||
}
|
||||
|
||||
for (i = 0; i < ncores; i++)
|
||||
set_cpu_possible(i, true);
|
||||
}
|
||||
|
||||
static void __init msm_smp_prepare_cpus(unsigned int max_cpus)
|
||||
{
|
||||
}
|
||||
|
||||
struct smp_operations msm_smp_ops __initdata = {
|
||||
.smp_init_cpus = msm_smp_init_cpus,
|
||||
.smp_prepare_cpus = msm_smp_prepare_cpus,
|
||||
.smp_secondary_init = msm_secondary_init,
|
||||
.smp_boot_secondary = msm_boot_secondary,
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
.cpu_die = msm_cpu_die,
|
||||
#endif
|
||||
};
|
@@ -1,39 +0,0 @@
|
||||
/* 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(phys_addr_t addr, int flags)
|
||||
{
|
||||
struct {
|
||||
unsigned int flags;
|
||||
phys_addr_t 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);
|
@@ -1,22 +0,0 @@
|
||||
/* 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.
|
||||
*/
|
||||
#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(phys_addr_t addr, int flags);
|
||||
|
||||
#endif
|
@@ -1,299 +0,0 @@
|
||||
/* 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 <asm/cacheflush.h>
|
||||
|
||||
#include "scm.h"
|
||||
|
||||
/* Cache line size for msm8x60 */
|
||||
#define CACHELINESIZE 32
|
||||
|
||||
#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
|
||||
* @buf: buffer returned from scm_get_command_buffer()
|
||||
*
|
||||
* An SCM command is laid 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;
|
||||
u32 buf[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
struct scm_command *cmd;
|
||||
size_t len = sizeof(*cmd) + sizeof(struct scm_response) + cmd_size +
|
||||
resp_size;
|
||||
|
||||
cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL);
|
||||
if (cmd) {
|
||||
cmd->len = len;
|
||||
cmd->buf_offset = offsetof(struct scm_command, buf);
|
||||
cmd->resp_hdr_offset = cmd->buf_offset + cmd_size;
|
||||
}
|
||||
return cmd;
|
||||
}
|
||||
|
||||
/**
|
||||
* free_scm_command() - Free an SCM command
|
||||
* @cmd: command to free
|
||||
*
|
||||
* Free an SCM command.
|
||||
*/
|
||||
static inline void free_scm_command(struct scm_command *cmd)
|
||||
{
|
||||
kfree(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(u32 cmd_addr)
|
||||
{
|
||||
int context_id;
|
||||
register u32 r0 asm("r0") = 1;
|
||||
register u32 r1 asm("r1") = (u32)&context_id;
|
||||
register u32 r2 asm("r2") = cmd_addr;
|
||||
do {
|
||||
asm volatile(
|
||||
__asmeq("%0", "r0")
|
||||
__asmeq("%1", "r0")
|
||||
__asmeq("%2", "r1")
|
||||
__asmeq("%3", "r2")
|
||||
#ifdef REQUIRES_SEC
|
||||
".arch_extension sec\n"
|
||||
#endif
|
||||
"smc #0 @ switch to secure world\n"
|
||||
: "=r" (r0)
|
||||
: "r" (r0), "r" (r1), "r" (r2)
|
||||
: "r3");
|
||||
} while (r0 == SCM_INTERRUPTED);
|
||||
|
||||
return r0;
|
||||
}
|
||||
|
||||
static int __scm_call(const struct scm_command *cmd)
|
||||
{
|
||||
int ret;
|
||||
u32 cmd_addr = virt_to_phys(cmd);
|
||||
|
||||
/*
|
||||
* 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();
|
||||
ret = smc(cmd_addr);
|
||||
if (ret < 0)
|
||||
ret = scm_remap_error(ret);
|
||||
|
||||
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;
|
||||
|
||||
cmd = alloc_scm_command(cmd_len, resp_len);
|
||||
if (!cmd)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd->id = (svc_id << 10) | cmd_id;
|
||||
if (cmd_buf)
|
||||
memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len);
|
||||
|
||||
mutex_lock(&scm_lock);
|
||||
ret = __scm_call(cmd);
|
||||
mutex_unlock(&scm_lock);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
rsp = scm_command_to_response(cmd);
|
||||
do {
|
||||
u32 start = (u32)rsp;
|
||||
u32 end = (u32)scm_get_response_buffer(rsp) + resp_len;
|
||||
start &= ~(CACHELINESIZE - 1);
|
||||
while (start < end) {
|
||||
asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start)
|
||||
: "memory");
|
||||
start += CACHELINESIZE;
|
||||
}
|
||||
} while (!rsp->is_complete);
|
||||
|
||||
if (resp_buf)
|
||||
memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len);
|
||||
out:
|
||||
free_scm_command(cmd);
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(scm_call);
|
||||
|
||||
u32 scm_get_version(void)
|
||||
{
|
||||
int context_id;
|
||||
static u32 version = -1;
|
||||
register u32 r0 asm("r0");
|
||||
register u32 r1 asm("r1");
|
||||
|
||||
if (version != -1)
|
||||
return version;
|
||||
|
||||
mutex_lock(&scm_lock);
|
||||
|
||||
r0 = 0x1 << 8;
|
||||
r1 = (u32)&context_id;
|
||||
do {
|
||||
asm volatile(
|
||||
__asmeq("%0", "r0")
|
||||
__asmeq("%1", "r1")
|
||||
__asmeq("%2", "r0")
|
||||
__asmeq("%3", "r1")
|
||||
#ifdef REQUIRES_SEC
|
||||
".arch_extension sec\n"
|
||||
#endif
|
||||
"smc #0 @ switch to secure world\n"
|
||||
: "=r" (r0), "=r" (r1)
|
||||
: "r" (r0), "r" (r1)
|
||||
: "r2", "r3");
|
||||
} while (r0 == SCM_INTERRUPTED);
|
||||
|
||||
version = r1;
|
||||
mutex_unlock(&scm_lock);
|
||||
|
||||
return version;
|
||||
}
|
||||
EXPORT_SYMBOL(scm_get_version);
|
@@ -1,25 +0,0 @@
|
||||
/* 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.
|
||||
*/
|
||||
#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
|
@@ -1,333 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Copyright (C) 2007 Google, Inc.
|
||||
* Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/clocksource.h>
|
||||
#include <linux/clockchips.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/sched_clock.h>
|
||||
|
||||
#include <asm/mach/time.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
#define TIMER_MATCH_VAL 0x0000
|
||||
#define TIMER_COUNT_VAL 0x0004
|
||||
#define TIMER_ENABLE 0x0008
|
||||
#define TIMER_ENABLE_CLR_ON_MATCH_EN BIT(1)
|
||||
#define TIMER_ENABLE_EN BIT(0)
|
||||
#define TIMER_CLEAR 0x000C
|
||||
#define DGT_CLK_CTL 0x10
|
||||
#define DGT_CLK_CTL_DIV_4 0x3
|
||||
#define TIMER_STS_GPT0_CLR_PEND BIT(10)
|
||||
|
||||
#define GPT_HZ 32768
|
||||
|
||||
#define MSM_DGT_SHIFT 5
|
||||
|
||||
static void __iomem *event_base;
|
||||
static void __iomem *sts_base;
|
||||
|
||||
static irqreturn_t msm_timer_interrupt(int irq, void *dev_id)
|
||||
{
|
||||
struct clock_event_device *evt = dev_id;
|
||||
/* Stop the timer tick */
|
||||
if (evt->mode == CLOCK_EVT_MODE_ONESHOT) {
|
||||
u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
|
||||
ctrl &= ~TIMER_ENABLE_EN;
|
||||
writel_relaxed(ctrl, event_base + TIMER_ENABLE);
|
||||
}
|
||||
evt->event_handler(evt);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int msm_timer_set_next_event(unsigned long cycles,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
u32 ctrl = readl_relaxed(event_base + TIMER_ENABLE);
|
||||
|
||||
ctrl &= ~TIMER_ENABLE_EN;
|
||||
writel_relaxed(ctrl, event_base + TIMER_ENABLE);
|
||||
|
||||
writel_relaxed(ctrl, event_base + TIMER_CLEAR);
|
||||
writel_relaxed(cycles, event_base + TIMER_MATCH_VAL);
|
||||
|
||||
if (sts_base)
|
||||
while (readl_relaxed(sts_base) & TIMER_STS_GPT0_CLR_PEND)
|
||||
cpu_relax();
|
||||
|
||||
writel_relaxed(ctrl | TIMER_ENABLE_EN, event_base + TIMER_ENABLE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_timer_set_mode(enum clock_event_mode mode,
|
||||
struct clock_event_device *evt)
|
||||
{
|
||||
u32 ctrl;
|
||||
|
||||
ctrl = readl_relaxed(event_base + TIMER_ENABLE);
|
||||
ctrl &= ~(TIMER_ENABLE_EN | TIMER_ENABLE_CLR_ON_MATCH_EN);
|
||||
|
||||
switch (mode) {
|
||||
case CLOCK_EVT_MODE_RESUME:
|
||||
case CLOCK_EVT_MODE_PERIODIC:
|
||||
break;
|
||||
case CLOCK_EVT_MODE_ONESHOT:
|
||||
/* Timer is enabled in set_next_event */
|
||||
break;
|
||||
case CLOCK_EVT_MODE_UNUSED:
|
||||
case CLOCK_EVT_MODE_SHUTDOWN:
|
||||
break;
|
||||
}
|
||||
writel_relaxed(ctrl, event_base + TIMER_ENABLE);
|
||||
}
|
||||
|
||||
static struct clock_event_device __percpu *msm_evt;
|
||||
|
||||
static void __iomem *source_base;
|
||||
|
||||
static notrace cycle_t msm_read_timer_count(struct clocksource *cs)
|
||||
{
|
||||
return readl_relaxed(source_base + TIMER_COUNT_VAL);
|
||||
}
|
||||
|
||||
static notrace cycle_t msm_read_timer_count_shift(struct clocksource *cs)
|
||||
{
|
||||
/*
|
||||
* Shift timer count down by a constant due to unreliable lower bits
|
||||
* on some targets.
|
||||
*/
|
||||
return msm_read_timer_count(cs) >> MSM_DGT_SHIFT;
|
||||
}
|
||||
|
||||
static struct clocksource msm_clocksource = {
|
||||
.name = "dg_timer",
|
||||
.rating = 300,
|
||||
.read = msm_read_timer_count,
|
||||
.mask = CLOCKSOURCE_MASK(32),
|
||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||
};
|
||||
|
||||
static int msm_timer_irq;
|
||||
static int msm_timer_has_ppi;
|
||||
|
||||
static int msm_local_timer_setup(struct clock_event_device *evt)
|
||||
{
|
||||
int cpu = smp_processor_id();
|
||||
int err;
|
||||
|
||||
evt->irq = msm_timer_irq;
|
||||
evt->name = "msm_timer";
|
||||
evt->features = CLOCK_EVT_FEAT_ONESHOT;
|
||||
evt->rating = 200;
|
||||
evt->set_mode = msm_timer_set_mode;
|
||||
evt->set_next_event = msm_timer_set_next_event;
|
||||
evt->cpumask = cpumask_of(cpu);
|
||||
|
||||
clockevents_config_and_register(evt, GPT_HZ, 4, 0xffffffff);
|
||||
|
||||
if (msm_timer_has_ppi) {
|
||||
enable_percpu_irq(evt->irq, IRQ_TYPE_EDGE_RISING);
|
||||
} else {
|
||||
err = request_irq(evt->irq, msm_timer_interrupt,
|
||||
IRQF_TIMER | IRQF_NOBALANCING |
|
||||
IRQF_TRIGGER_RISING, "gp_timer", evt);
|
||||
if (err)
|
||||
pr_err("request_irq failed\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void msm_local_timer_stop(struct clock_event_device *evt)
|
||||
{
|
||||
evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
|
||||
disable_percpu_irq(evt->irq);
|
||||
}
|
||||
|
||||
static int msm_timer_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
/*
|
||||
* Grab cpu pointer in each case to avoid spurious
|
||||
* preemptible warnings
|
||||
*/
|
||||
switch (action & ~CPU_TASKS_FROZEN) {
|
||||
case CPU_STARTING:
|
||||
msm_local_timer_setup(this_cpu_ptr(msm_evt));
|
||||
break;
|
||||
case CPU_DYING:
|
||||
msm_local_timer_stop(this_cpu_ptr(msm_evt));
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block msm_timer_cpu_nb = {
|
||||
.notifier_call = msm_timer_cpu_notify,
|
||||
};
|
||||
|
||||
static u64 notrace msm_sched_clock_read(void)
|
||||
{
|
||||
return msm_clocksource.read(&msm_clocksource);
|
||||
}
|
||||
|
||||
static void __init msm_timer_init(u32 dgt_hz, int sched_bits, int irq,
|
||||
bool percpu)
|
||||
{
|
||||
struct clocksource *cs = &msm_clocksource;
|
||||
int res = 0;
|
||||
|
||||
msm_timer_irq = irq;
|
||||
msm_timer_has_ppi = percpu;
|
||||
|
||||
msm_evt = alloc_percpu(struct clock_event_device);
|
||||
if (!msm_evt) {
|
||||
pr_err("memory allocation failed for clockevents\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (percpu)
|
||||
res = request_percpu_irq(irq, msm_timer_interrupt,
|
||||
"gp_timer", msm_evt);
|
||||
|
||||
if (res) {
|
||||
pr_err("request_percpu_irq failed\n");
|
||||
} else {
|
||||
res = register_cpu_notifier(&msm_timer_cpu_nb);
|
||||
if (res) {
|
||||
free_percpu_irq(irq, msm_evt);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Immediately configure the timer on the boot CPU */
|
||||
msm_local_timer_setup(__this_cpu_ptr(msm_evt));
|
||||
}
|
||||
|
||||
err:
|
||||
writel_relaxed(TIMER_ENABLE_EN, source_base + TIMER_ENABLE);
|
||||
res = clocksource_register_hz(cs, dgt_hz);
|
||||
if (res)
|
||||
pr_err("clocksource_register failed\n");
|
||||
sched_clock_register(msm_sched_clock_read, sched_bits, dgt_hz);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static void __init msm_dt_timer_init(struct device_node *np)
|
||||
{
|
||||
u32 freq;
|
||||
int irq;
|
||||
struct resource res;
|
||||
u32 percpu_offset;
|
||||
void __iomem *base;
|
||||
void __iomem *cpu0_base;
|
||||
|
||||
base = of_iomap(np, 0);
|
||||
if (!base) {
|
||||
pr_err("Failed to map event base\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* We use GPT0 for the clockevent */
|
||||
irq = irq_of_parse_and_map(np, 1);
|
||||
if (irq <= 0) {
|
||||
pr_err("Can't get irq\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* We use CPU0's DGT for the clocksource */
|
||||
if (of_property_read_u32(np, "cpu-offset", &percpu_offset))
|
||||
percpu_offset = 0;
|
||||
|
||||
if (of_address_to_resource(np, 0, &res)) {
|
||||
pr_err("Failed to parse DGT resource\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cpu0_base = ioremap(res.start + percpu_offset, resource_size(&res));
|
||||
if (!cpu0_base) {
|
||||
pr_err("Failed to map source base\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (of_property_read_u32(np, "clock-frequency", &freq)) {
|
||||
pr_err("Unknown frequency\n");
|
||||
return;
|
||||
}
|
||||
|
||||
event_base = base + 0x4;
|
||||
sts_base = base + 0x88;
|
||||
source_base = cpu0_base + 0x24;
|
||||
freq /= 4;
|
||||
writel_relaxed(DGT_CLK_CTL_DIV_4, source_base + DGT_CLK_CTL);
|
||||
|
||||
msm_timer_init(freq, 32, irq, !!percpu_offset);
|
||||
}
|
||||
CLOCKSOURCE_OF_DECLARE(kpss_timer, "qcom,kpss-timer", msm_dt_timer_init);
|
||||
CLOCKSOURCE_OF_DECLARE(scss_timer, "qcom,scss-timer", msm_dt_timer_init);
|
||||
#endif
|
||||
|
||||
static int __init msm_timer_map(phys_addr_t addr, u32 event, u32 source,
|
||||
u32 sts)
|
||||
{
|
||||
void __iomem *base;
|
||||
|
||||
base = ioremap(addr, SZ_256);
|
||||
if (!base) {
|
||||
pr_err("Failed to map timer base\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
event_base = base + event;
|
||||
source_base = base + source;
|
||||
if (sts)
|
||||
sts_base = base + sts;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init msm7x01_timer_init(void)
|
||||
{
|
||||
struct clocksource *cs = &msm_clocksource;
|
||||
|
||||
if (msm_timer_map(0xc0100000, 0x0, 0x10, 0x0))
|
||||
return;
|
||||
cs->read = msm_read_timer_count_shift;
|
||||
cs->mask = CLOCKSOURCE_MASK((32 - MSM_DGT_SHIFT));
|
||||
/* 600 KHz */
|
||||
msm_timer_init(19200000 >> MSM_DGT_SHIFT, 32 - MSM_DGT_SHIFT, 7,
|
||||
false);
|
||||
}
|
||||
|
||||
void __init msm7x30_timer_init(void)
|
||||
{
|
||||
if (msm_timer_map(0xc0100000, 0x4, 0x24, 0x80))
|
||||
return;
|
||||
msm_timer_init(24576000 / 4, 32, 1, false);
|
||||
}
|
||||
|
||||
void __init qsd8x50_timer_init(void)
|
||||
{
|
||||
if (msm_timer_map(0xAC100000, 0x0, 0x10, 0x34))
|
||||
return;
|
||||
msm_timer_init(19200000 / 4, 32, 7, false);
|
||||
}
|
Reference in New Issue
Block a user