From 2e67690137f3a7bac660edd548f8846709c55381 Mon Sep 17 00:00:00 2001 From: Robert ABEL Date: Fri, 27 Feb 2015 16:56:53 +0100 Subject: ARM OMAP2+ GPMC: calculate GPMCFCLKDIVIDER based on WAITMONITORINGTIME The WAITMONITORINGTIME is expressed as a number of GPMC_CLK clock cycles, even though the access is defined as asynchronous, and no GPMC_CLK clock is provided to the external device. Still, GPMCFCLKDIVIDER is used as a divider for the GPMC clock, so it must be programmed to define the correct WAITMONITORINGTIME delay. Calculate GPMCFCLKDIVIDER independent of gpmc,sync-clk-ps in DT for pure asynchronous accesses, i.e. both read and write asynchronous. Signed-off-by: Robert ABEL Acked-by: Tony Lindgren Signed-off-by: Roger Quadros --- arch/arm/mach-omap2/gpmc-nand.c | 18 ++++++++++-------- arch/arm/mach-omap2/gpmc-onenand.c | 4 ++-- arch/arm/mach-omap2/usb-tusb6010.c | 4 ++-- 3 files changed, 14 insertions(+), 12 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-omap2/gpmc-nand.c b/arch/arm/mach-omap2/gpmc-nand.c index d5951b17b736..72918c4973ea 100644 --- a/arch/arm/mach-omap2/gpmc-nand.c +++ b/arch/arm/mach-omap2/gpmc-nand.c @@ -96,14 +96,6 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, gpmc_nand_res[1].start = gpmc_get_client_irq(GPMC_IRQ_FIFOEVENTENABLE); gpmc_nand_res[2].start = gpmc_get_client_irq(GPMC_IRQ_COUNT_EVENT); - if (gpmc_t) { - err = gpmc_cs_set_timings(gpmc_nand_data->cs, gpmc_t); - if (err < 0) { - pr_err("omap2-gpmc: Unable to set gpmc timings: %d\n", err); - return err; - } - } - memset(&s, 0, sizeof(struct gpmc_settings)); if (gpmc_nand_data->of_node) gpmc_read_settings_dt(gpmc_nand_data->of_node, &s); @@ -111,6 +103,16 @@ int gpmc_nand_init(struct omap_nand_platform_data *gpmc_nand_data, gpmc_set_legacy(gpmc_nand_data, &s); s.device_nand = true; + + if (gpmc_t) { + err = gpmc_cs_set_timings(gpmc_nand_data->cs, gpmc_t, &s); + if (err < 0) { + pr_err("omap2-gpmc: Unable to set gpmc timings: %d\n", + err); + return err; + } + } + err = gpmc_cs_program_settings(gpmc_nand_data->cs, &s); if (err < 0) goto out_free_cs; diff --git a/arch/arm/mach-omap2/gpmc-onenand.c b/arch/arm/mach-omap2/gpmc-onenand.c index 53d197e0c1f3..f899e77ff5e6 100644 --- a/arch/arm/mach-omap2/gpmc-onenand.c +++ b/arch/arm/mach-omap2/gpmc-onenand.c @@ -293,7 +293,7 @@ static int omap2_onenand_setup_async(void __iomem *onenand_base) if (ret < 0) return ret; - ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t); + ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t, &onenand_async); if (ret < 0) return ret; @@ -331,7 +331,7 @@ static int omap2_onenand_setup_sync(void __iomem *onenand_base, int *freq_ptr) if (ret < 0) return ret; - ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t); + ret = gpmc_cs_set_timings(gpmc_onenand_data->cs, &t, &onenand_sync); if (ret < 0) return ret; diff --git a/arch/arm/mach-omap2/usb-tusb6010.c b/arch/arm/mach-omap2/usb-tusb6010.c index 8333400898fb..e554d9e66a1c 100644 --- a/arch/arm/mach-omap2/usb-tusb6010.c +++ b/arch/arm/mach-omap2/usb-tusb6010.c @@ -71,7 +71,7 @@ static int tusb_set_async_mode(unsigned sysclk_ps) gpmc_calc_timings(&t, &tusb_async, &dev_t); - return gpmc_cs_set_timings(async_cs, &t); + return gpmc_cs_set_timings(async_cs, &t, &tusb_async); } static int tusb_set_sync_mode(unsigned sysclk_ps) @@ -98,7 +98,7 @@ static int tusb_set_sync_mode(unsigned sysclk_ps) gpmc_calc_timings(&t, &tusb_sync, &dev_t); - return gpmc_cs_set_timings(sync_cs, &t); + return gpmc_cs_set_timings(sync_cs, &t, &tusb_sync); } /* tusb driver calls this when it changes the chip's clocking */ -- cgit v1.2.3 From 67fdcc96b49455569307e6ba7bba0905cf9d6596 Mon Sep 17 00:00:00 2001 From: Matthias Brugger Date: Fri, 6 Mar 2015 13:52:58 +0100 Subject: ARM: mediatek: enable the pin controller This patch enables the pin controller for Mediatek SoCs. Signed-off-by: Matthias Brugger --- arch/arm/mach-mediatek/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'arch') diff --git a/arch/arm/mach-mediatek/Kconfig b/arch/arm/mach-mediatek/Kconfig index f7e463ca0287..9f59e58da3a4 100644 --- a/arch/arm/mach-mediatek/Kconfig +++ b/arch/arm/mach-mediatek/Kconfig @@ -1,6 +1,7 @@ menuconfig ARCH_MEDIATEK bool "Mediatek MT65xx & MT81xx SoC" if ARCH_MULTI_V7 select ARM_GIC + select PINCTRL select MTK_TIMER help Support for Mediatek MT65xx & MT81xx SoCs -- cgit v1.2.3 From 3d9b448bd287f051f5380323d596a133f01c074b Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 4 Feb 2015 15:46:04 -0600 Subject: ARM: qcom: Merge scm and scm boot code together Put all scm related code into a single file as a first step in cleaning up the scm interface to just expose functional behavior insteam of making direct scm calls. Signed-off-by: Kumar Gala --- arch/arm/mach-qcom/Makefile | 2 +- arch/arm/mach-qcom/platsmp.c | 2 +- arch/arm/mach-qcom/scm-boot.c | 39 --------------------------------------- arch/arm/mach-qcom/scm-boot.h | 26 -------------------------- arch/arm/mach-qcom/scm.c | 17 +++++++++++++++++ arch/arm/mach-qcom/scm.h | 11 +++++++++++ 6 files changed, 30 insertions(+), 67 deletions(-) delete mode 100644 arch/arm/mach-qcom/scm-boot.c delete mode 100644 arch/arm/mach-qcom/scm-boot.h (limited to 'arch') diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile index 8f756ae1ae31..10b684140aa1 100644 --- a/arch/arm/mach-qcom/Makefile +++ b/arch/arm/mach-qcom/Makefile @@ -1,5 +1,5 @@ obj-y := board.o obj-$(CONFIG_SMP) += platsmp.o -obj-$(CONFIG_QCOM_SCM) += scm.o scm-boot.o +obj-$(CONFIG_QCOM_SCM) += scm.o CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c index 09cffed4c0a4..8f4962ebf8c2 100644 --- a/arch/arm/mach-qcom/platsmp.c +++ b/arch/arm/mach-qcom/platsmp.c @@ -20,7 +20,7 @@ #include -#include "scm-boot.h" +#include "scm.h" #define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0 #define SCSS_CPU1CORE_RESET 0x2d80 diff --git a/arch/arm/mach-qcom/scm-boot.c b/arch/arm/mach-qcom/scm-boot.c deleted file mode 100644 index e8ff7beb6218..000000000000 --- a/arch/arm/mach-qcom/scm-boot.c +++ /dev/null @@ -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 -#include - -#include "scm.h" -#include "scm-boot.h" - -/* - * Set the cold/warm boot address for one of the CPU cores. - */ -int scm_set_boot_addr(u32 addr, int flags) -{ - struct { - __le32 flags; - __le32 addr; - } cmd; - - cmd.addr = cpu_to_le32(addr); - cmd.flags = cpu_to_le32(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-qcom/scm-boot.h b/arch/arm/mach-qcom/scm-boot.h deleted file mode 100644 index 3e210fb818bb..000000000000 --- a/arch/arm/mach-qcom/scm-boot.h +++ /dev/null @@ -1,26 +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 0x01 -#define SCM_FLAG_COLDBOOT_CPU2 0x08 -#define SCM_FLAG_COLDBOOT_CPU3 0x20 -#define SCM_FLAG_WARMBOOT_CPU0 0x04 -#define SCM_FLAG_WARMBOOT_CPU1 0x02 -#define SCM_FLAG_WARMBOOT_CPU2 0x10 -#define SCM_FLAG_WARMBOOT_CPU3 0x40 - -int scm_set_boot_addr(u32 addr, int flags); - -#endif diff --git a/arch/arm/mach-qcom/scm.c b/arch/arm/mach-qcom/scm.c index 1d9cf18c7091..e20db10e96f4 100644 --- a/arch/arm/mach-qcom/scm.c +++ b/arch/arm/mach-qcom/scm.c @@ -324,3 +324,20 @@ u32 scm_get_version(void) return version; } EXPORT_SYMBOL(scm_get_version); + +/* + * Set the cold/warm boot address for one of the CPU cores. + */ +int scm_set_boot_addr(u32 addr, int flags) +{ + struct { + __le32 flags; + __le32 addr; + } cmd; + + cmd.addr = cpu_to_le32(addr); + cmd.flags = cpu_to_le32(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-qcom/scm.h b/arch/arm/mach-qcom/scm.h index 00b31ea58f29..cfe693575d51 100644 --- a/arch/arm/mach-qcom/scm.h +++ b/arch/arm/mach-qcom/scm.h @@ -18,6 +18,17 @@ 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_BOOT_ADDR 0x1 +#define SCM_FLAG_COLDBOOT_CPU1 0x01 +#define SCM_FLAG_COLDBOOT_CPU2 0x08 +#define SCM_FLAG_COLDBOOT_CPU3 0x20 +#define SCM_FLAG_WARMBOOT_CPU0 0x04 +#define SCM_FLAG_WARMBOOT_CPU1 0x02 +#define SCM_FLAG_WARMBOOT_CPU2 0x10 +#define SCM_FLAG_WARMBOOT_CPU3 0x40 + +extern int scm_set_boot_addr(u32 addr, int flags); + #define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) extern u32 scm_get_version(void); -- cgit v1.2.3 From b97fdb6dc0effe0612bc60fc9db7017b9fd81932 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 4 Feb 2015 16:10:13 -0600 Subject: ARM: qcom: Cleanup scm interface to only export what is needed Now that scom boot interface is merged we don't need export scm_call anymore. Some other minor cleanups related to boot interface to only export what is needed by scm_set_boot_addr(). Signed-off-by: Kumar Gala --- arch/arm/mach-qcom/scm.c | 7 ++++--- arch/arm/mach-qcom/scm.h | 7 ------- 2 files changed, 4 insertions(+), 10 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-qcom/scm.c b/arch/arm/mach-qcom/scm.c index e20db10e96f4..5147666be096 100644 --- a/arch/arm/mach-qcom/scm.c +++ b/arch/arm/mach-qcom/scm.c @@ -249,8 +249,8 @@ static void scm_inv_range(unsigned long start, unsigned long end) * response buffers is taken care of by scm_call; however, callers are * responsible for any other cached buffers passed over to the secure world. */ -int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, - void *resp_buf, size_t resp_len) +static 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; @@ -287,7 +287,6 @@ out: free_scm_command(cmd); return ret; } -EXPORT_SYMBOL(scm_call); u32 scm_get_version(void) { @@ -325,6 +324,8 @@ u32 scm_get_version(void) } EXPORT_SYMBOL(scm_get_version); +#define SCM_SVC_BOOT 0x1 +#define SCM_BOOT_ADDR 0x1 /* * Set the cold/warm boot address for one of the CPU cores. */ diff --git a/arch/arm/mach-qcom/scm.h b/arch/arm/mach-qcom/scm.h index cfe693575d51..d73c3786c881 100644 --- a/arch/arm/mach-qcom/scm.h +++ b/arch/arm/mach-qcom/scm.h @@ -12,13 +12,6 @@ #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_BOOT_ADDR 0x1 #define SCM_FLAG_COLDBOOT_CPU1 0x01 #define SCM_FLAG_COLDBOOT_CPU2 0x08 #define SCM_FLAG_COLDBOOT_CPU3 0x20 -- cgit v1.2.3 From 4de43476fc1baaf2bb7a520fc9e3b1797943b615 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Wed, 4 Feb 2015 16:30:46 -0600 Subject: ARM: qcom: Prep scm code for move to drivers/firmware Add qcom prefix to functions, etc to create a unique name space for the scm code as it gets ready to move out of qcom specific mach dir. Signed-off-by: Kumar Gala --- arch/arm/mach-qcom/platsmp.c | 8 +-- arch/arm/mach-qcom/scm.c | 148 +++++++++++++++++++++---------------------- arch/arm/mach-qcom/scm.h | 24 +++---- 3 files changed, 90 insertions(+), 90 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c index 8f4962ebf8c2..596e6237dc7e 100644 --- a/arch/arm/mach-qcom/platsmp.c +++ b/arch/arm/mach-qcom/platsmp.c @@ -323,9 +323,9 @@ static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) unsigned int flags = 0; static const int cold_boot_flags[] = { 0, - SCM_FLAG_COLDBOOT_CPU1, - SCM_FLAG_COLDBOOT_CPU2, - SCM_FLAG_COLDBOOT_CPU3, + QCOM_SCM_FLAG_COLDBOOT_CPU1, + QCOM_SCM_FLAG_COLDBOOT_CPU2, + QCOM_SCM_FLAG_COLDBOOT_CPU3, }; for_each_present_cpu(cpu) { @@ -337,7 +337,7 @@ static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) flags |= cold_boot_flags[map]; } - if (scm_set_boot_addr(virt_to_phys(secondary_startup_arm), flags)) { + if (qcom_scm_set_boot_addr(virt_to_phys(secondary_startup_arm), flags)) { for_each_present_cpu(cpu) { if (cpu == smp_processor_id()) continue; diff --git a/arch/arm/mach-qcom/scm.c b/arch/arm/mach-qcom/scm.c index 5147666be096..3e0e334374de 100644 --- a/arch/arm/mach-qcom/scm.c +++ b/arch/arm/mach-qcom/scm.c @@ -27,40 +27,40 @@ #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 +#define QCOM_SCM_ENOMEM -5 +#define QCOM_SCM_EOPNOTSUPP -4 +#define QCOM_SCM_EINVAL_ADDR -3 +#define QCOM_SCM_EINVAL_ARG -2 +#define QCOM_SCM_ERROR -1 +#define QCOM_SCM_INTERRUPTED 1 -static DEFINE_MUTEX(scm_lock); +static DEFINE_MUTEX(qcom_scm_lock); /** - * struct scm_command - one SCM command buffer + * struct qcom_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() + * @buf: buffer returned from qcom_scm_get_command_buffer() * * An SCM command is laid out in memory as follows: * - * ------------------- <--- struct scm_command + * ------------------- <--- struct qcom_scm_command * | command header | - * ------------------- <--- scm_get_command_buffer() + * ------------------- <--- qcom_scm_get_command_buffer() * | command buffer | - * ------------------- <--- struct scm_response and - * | response header | scm_command_to_response() - * ------------------- <--- scm_get_response_buffer() + * ------------------- <--- struct qcom_scm_response and + * | response header | qcom_scm_command_to_response() + * ------------------- <--- qcom_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 + * you should always use the appropriate qcom_scm_get_*_buffer() routines * to access the buffers in a safe manner. */ -struct scm_command { +struct qcom_scm_command { __le32 len; __le32 buf_offset; __le32 resp_hdr_offset; @@ -69,38 +69,38 @@ struct scm_command { }; /** - * struct scm_response - one SCM response buffer + * struct qcom_scm_response - one SCM response buffer * @len: total available memory for response - * @buf_offset: start of response data relative to start of scm_response + * @buf_offset: start of response data relative to start of qcom_scm_response * @is_complete: indicates if the command has finished processing */ -struct scm_response { +struct qcom_scm_response { __le32 len; __le32 buf_offset; __le32 is_complete; }; /** - * alloc_scm_command() - Allocate an SCM command + * alloc_qcom_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. + * Returns a valid &qcom_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) +static struct qcom_scm_command *alloc_qcom_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 + + struct qcom_scm_command *cmd; + size_t len = sizeof(*cmd) + sizeof(struct qcom_scm_response) + cmd_size + resp_size; u32 offset; cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); if (cmd) { cmd->len = cpu_to_le32(len); - offset = offsetof(struct scm_command, buf); + offset = offsetof(struct qcom_scm_command, buf); cmd->buf_offset = cpu_to_le32(offset); cmd->resp_hdr_offset = cpu_to_le32(offset + cmd_size); } @@ -108,62 +108,62 @@ static struct scm_command *alloc_scm_command(size_t cmd_size, size_t resp_size) } /** - * free_scm_command() - Free an SCM command + * free_qcom_scm_command() - Free an SCM command * @cmd: command to free * * Free an SCM command. */ -static inline void free_scm_command(struct scm_command *cmd) +static inline void free_qcom_scm_command(struct qcom_scm_command *cmd) { kfree(cmd); } /** - * scm_command_to_response() - Get a pointer to a scm_response + * qcom_scm_command_to_response() - Get a pointer to a qcom_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) +static inline struct qcom_scm_response *qcom_scm_command_to_response( + const struct qcom_scm_command *cmd) { return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); } /** - * scm_get_command_buffer() - Get a pointer to a command buffer + * qcom_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) +static inline void *qcom_scm_get_command_buffer(const struct qcom_scm_command *cmd) { return (void *)cmd->buf; } /** - * scm_get_response_buffer() - Get a pointer to a response buffer + * qcom_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) +static inline void *qcom_scm_get_response_buffer(const struct qcom_scm_response *rsp) { return (void *)rsp + le32_to_cpu(rsp->buf_offset); } -static int scm_remap_error(int err) +static int qcom_scm_remap_error(int err) { - pr_err("scm_call failed with error code %d\n", err); + pr_err("qcom_scm_call failed with error code %d\n", err); switch (err) { - case SCM_ERROR: + case QCOM_SCM_ERROR: return -EIO; - case SCM_EINVAL_ADDR: - case SCM_EINVAL_ARG: + case QCOM_SCM_EINVAL_ADDR: + case QCOM_SCM_EINVAL_ARG: return -EINVAL; - case SCM_EOPNOTSUPP: + case QCOM_SCM_EOPNOTSUPP: return -EOPNOTSUPP; - case SCM_ENOMEM: + case QCOM_SCM_ENOMEM: return -ENOMEM; } return -EINVAL; @@ -188,12 +188,12 @@ static u32 smc(u32 cmd_addr) : "=r" (r0) : "r" (r0), "r" (r1), "r" (r2) : "r3"); - } while (r0 == SCM_INTERRUPTED); + } while (r0 == QCOM_SCM_INTERRUPTED); return r0; } -static int __scm_call(const struct scm_command *cmd) +static int __qcom_scm_call(const struct qcom_scm_command *cmd) { int ret; u32 cmd_addr = virt_to_phys(cmd); @@ -207,12 +207,12 @@ static int __scm_call(const struct scm_command *cmd) ret = smc(cmd_addr); if (ret < 0) - ret = scm_remap_error(ret); + ret = qcom_scm_remap_error(ret); return ret; } -static void scm_inv_range(unsigned long start, unsigned long end) +static void qcom_scm_inv_range(unsigned long start, unsigned long end) { u32 cacheline_size, ctr; @@ -232,7 +232,7 @@ static void scm_inv_range(unsigned long start, unsigned long end) } /** - * scm_call() - Send an SCM command + * qcom_scm_call() - Send an SCM command * @svc_id: service identifier * @cmd_id: command identifier * @cmd_buf: command buffer @@ -244,51 +244,51 @@ static void scm_inv_range(unsigned long start, unsigned long end) * * A note on cache maintenance: * Note that any buffers that are expected to be accessed by the secure world - * must be flushed before invoking scm_call and invalidated in the cache - * immediately after scm_call returns. Cache maintenance on the command and - * response buffers is taken care of by scm_call; however, callers are + * must be flushed before invoking qcom_scm_call and invalidated in the cache + * immediately after qcom_scm_call returns. Cache maintenance on the command + * and response buffers is taken care of by qcom_scm_call; however, callers are * responsible for any other cached buffers passed over to the secure world. */ -static int scm_call(u32 svc_id, u32 cmd_id, const void *cmd_buf, size_t cmd_len, - void *resp_buf, size_t resp_len) +static int qcom_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; + struct qcom_scm_command *cmd; + struct qcom_scm_response *rsp; unsigned long start, end; - cmd = alloc_scm_command(cmd_len, resp_len); + cmd = alloc_qcom_scm_command(cmd_len, resp_len); if (!cmd) return -ENOMEM; cmd->id = cpu_to_le32((svc_id << 10) | cmd_id); if (cmd_buf) - memcpy(scm_get_command_buffer(cmd), cmd_buf, cmd_len); + memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len); - mutex_lock(&scm_lock); - ret = __scm_call(cmd); - mutex_unlock(&scm_lock); + mutex_lock(&qcom_scm_lock); + ret = __qcom_scm_call(cmd); + mutex_unlock(&qcom_scm_lock); if (ret) goto out; - rsp = scm_command_to_response(cmd); + rsp = qcom_scm_command_to_response(cmd); start = (unsigned long)rsp; do { - scm_inv_range(start, start + sizeof(*rsp)); + qcom_scm_inv_range(start, start + sizeof(*rsp)); } while (!rsp->is_complete); - end = (unsigned long)scm_get_response_buffer(rsp) + resp_len; - scm_inv_range(start, end); + end = (unsigned long)qcom_scm_get_response_buffer(rsp) + resp_len; + qcom_scm_inv_range(start, end); if (resp_buf) - memcpy(resp_buf, scm_get_response_buffer(rsp), resp_len); + memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), resp_len); out: - free_scm_command(cmd); + free_qcom_scm_command(cmd); return ret; } -u32 scm_get_version(void) +u32 qcom_scm_get_version(void) { int context_id; static u32 version = -1; @@ -298,7 +298,7 @@ u32 scm_get_version(void) if (version != -1) return version; - mutex_lock(&scm_lock); + mutex_lock(&qcom_scm_lock); r0 = 0x1 << 8; r1 = (u32)&context_id; @@ -315,21 +315,21 @@ u32 scm_get_version(void) : "=r" (r0), "=r" (r1) : "r" (r0), "r" (r1) : "r2", "r3"); - } while (r0 == SCM_INTERRUPTED); + } while (r0 == QCOM_SCM_INTERRUPTED); version = r1; - mutex_unlock(&scm_lock); + mutex_unlock(&qcom_scm_lock); return version; } -EXPORT_SYMBOL(scm_get_version); +EXPORT_SYMBOL(qcom_scm_get_version); -#define SCM_SVC_BOOT 0x1 -#define SCM_BOOT_ADDR 0x1 +#define QCOM_SCM_SVC_BOOT 0x1 +#define QCOM_SCM_BOOT_ADDR 0x1 /* * Set the cold/warm boot address for one of the CPU cores. */ -int scm_set_boot_addr(u32 addr, int flags) +int qcom_scm_set_boot_addr(u32 addr, int flags) { struct { __le32 flags; @@ -338,7 +338,7 @@ int scm_set_boot_addr(u32 addr, int flags) cmd.addr = cpu_to_le32(addr); cmd.flags = cpu_to_le32(flags); - return scm_call(SCM_SVC_BOOT, SCM_BOOT_ADDR, + return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, &cmd, sizeof(cmd), NULL, 0); } -EXPORT_SYMBOL(scm_set_boot_addr); +EXPORT_SYMBOL(qcom_scm_set_boot_addr); diff --git a/arch/arm/mach-qcom/scm.h b/arch/arm/mach-qcom/scm.h index d73c3786c881..6bb84cffb396 100644 --- a/arch/arm/mach-qcom/scm.h +++ b/arch/arm/mach-qcom/scm.h @@ -9,21 +9,21 @@ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ -#ifndef __MACH_SCM_H -#define __MACH_SCM_H +#ifndef __QCOM_SCM_H +#define __QCOM_SCM_H -#define SCM_FLAG_COLDBOOT_CPU1 0x01 -#define SCM_FLAG_COLDBOOT_CPU2 0x08 -#define SCM_FLAG_COLDBOOT_CPU3 0x20 -#define SCM_FLAG_WARMBOOT_CPU0 0x04 -#define SCM_FLAG_WARMBOOT_CPU1 0x02 -#define SCM_FLAG_WARMBOOT_CPU2 0x10 -#define SCM_FLAG_WARMBOOT_CPU3 0x40 +#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 +#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 +#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 +#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 +#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 +#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 +#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 -extern int scm_set_boot_addr(u32 addr, int flags); +extern int qcom_scm_set_boot_addr(u32 addr, int flags); -#define SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) +#define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) -extern u32 scm_get_version(void); +extern u32 qcom_scm_get_version(void); #endif -- cgit v1.2.3 From 916f743da3546c28a2f350d197e3bea95d97ba15 Mon Sep 17 00:00:00 2001 From: Kumar Gala Date: Thu, 26 Feb 2015 15:49:09 -0600 Subject: firmware: qcom: scm: Move the scm driver to drivers/firmware Architectural changes in the ARM Linux kernel tree mandate the eventual removal of the mach-* directories. Move the scm driver to drivers/firmware and the scm header to include/linux to support that removal. Signed-off-by: Kumar Gala --- MAINTAINERS | 1 + arch/arm/Kconfig | 2 + arch/arm/mach-qcom/Kconfig | 3 - arch/arm/mach-qcom/Makefile | 3 - arch/arm/mach-qcom/platsmp.c | 2 +- arch/arm/mach-qcom/scm.c | 344 ------------------------------------------- arch/arm/mach-qcom/scm.h | 29 ---- drivers/firmware/Kconfig | 4 + drivers/firmware/Makefile | 2 + drivers/firmware/qcom_scm.c | 344 +++++++++++++++++++++++++++++++++++++++++++ include/linux/qcom_scm.h | 29 ++++ 11 files changed, 383 insertions(+), 380 deletions(-) delete mode 100644 arch/arm/mach-qcom/scm.c delete mode 100644 arch/arm/mach-qcom/scm.h create mode 100644 drivers/firmware/qcom_scm.c create mode 100644 include/linux/qcom_scm.h (limited to 'arch') diff --git a/MAINTAINERS b/MAINTAINERS index ddc5a8cf9a8a..beb8aa4840e4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1317,6 +1317,7 @@ L: linux-soc@vger.kernel.org S: Maintained F: arch/arm/mach-qcom/ F: drivers/soc/qcom/ +F: drivers/firmware/qcom_scm.c T: git git://git.kernel.org/pub/scm/linux/kernel/git/galak/linux-qcom.git ARM/RADISYS ENP2611 MACHINE SUPPORT diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 9f1f09a2bc9b..7ffd1518d2aa 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -2160,6 +2160,8 @@ source "net/Kconfig" source "drivers/Kconfig" +source "drivers/firmware/Kconfig" + source "fs/Kconfig" source "arch/arm/Kconfig.debug" diff --git a/arch/arm/mach-qcom/Kconfig b/arch/arm/mach-qcom/Kconfig index 48003ea652b9..2256cd1e25d1 100644 --- a/arch/arm/mach-qcom/Kconfig +++ b/arch/arm/mach-qcom/Kconfig @@ -22,7 +22,4 @@ config ARCH_MSM8974 bool "Enable support for MSM8974" select HAVE_ARM_ARCH_TIMER -config QCOM_SCM - bool - endif diff --git a/arch/arm/mach-qcom/Makefile b/arch/arm/mach-qcom/Makefile index 10b684140aa1..e324375fa919 100644 --- a/arch/arm/mach-qcom/Makefile +++ b/arch/arm/mach-qcom/Makefile @@ -1,5 +1,2 @@ obj-y := board.o obj-$(CONFIG_SMP) += platsmp.o -obj-$(CONFIG_QCOM_SCM) += scm.o - -CFLAGS_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c index 596e6237dc7e..4b67e56911d3 100644 --- a/arch/arm/mach-qcom/platsmp.c +++ b/arch/arm/mach-qcom/platsmp.c @@ -17,10 +17,10 @@ #include #include #include +#include #include -#include "scm.h" #define VDD_SC1_ARRAY_CLAMP_GFS_CTL 0x35a0 #define SCSS_CPU1CORE_RESET 0x2d80 diff --git a/arch/arm/mach-qcom/scm.c b/arch/arm/mach-qcom/scm.c deleted file mode 100644 index 3e0e334374de..000000000000 --- a/arch/arm/mach-qcom/scm.c +++ /dev/null @@ -1,344 +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 -#include -#include -#include -#include -#include - -#include -#include - -#include "scm.h" - -#define QCOM_SCM_ENOMEM -5 -#define QCOM_SCM_EOPNOTSUPP -4 -#define QCOM_SCM_EINVAL_ADDR -3 -#define QCOM_SCM_EINVAL_ARG -2 -#define QCOM_SCM_ERROR -1 -#define QCOM_SCM_INTERRUPTED 1 - -static DEFINE_MUTEX(qcom_scm_lock); - -/** - * struct qcom_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 qcom_scm_get_command_buffer() - * - * An SCM command is laid out in memory as follows: - * - * ------------------- <--- struct qcom_scm_command - * | command header | - * ------------------- <--- qcom_scm_get_command_buffer() - * | command buffer | - * ------------------- <--- struct qcom_scm_response and - * | response header | qcom_scm_command_to_response() - * ------------------- <--- qcom_scm_get_response_buffer() - * | response buffer | - * ------------------- - * - * There can be arbitrary padding between the headers and buffers so - * you should always use the appropriate qcom_scm_get_*_buffer() routines - * to access the buffers in a safe manner. - */ -struct qcom_scm_command { - __le32 len; - __le32 buf_offset; - __le32 resp_hdr_offset; - __le32 id; - __le32 buf[0]; -}; - -/** - * struct qcom_scm_response - one SCM response buffer - * @len: total available memory for response - * @buf_offset: start of response data relative to start of qcom_scm_response - * @is_complete: indicates if the command has finished processing - */ -struct qcom_scm_response { - __le32 len; - __le32 buf_offset; - __le32 is_complete; -}; - -/** - * alloc_qcom_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 &qcom_scm_command on success or %NULL if the allocation fails. - */ -static struct qcom_scm_command *alloc_qcom_scm_command(size_t cmd_size, size_t resp_size) -{ - struct qcom_scm_command *cmd; - size_t len = sizeof(*cmd) + sizeof(struct qcom_scm_response) + cmd_size + - resp_size; - u32 offset; - - cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); - if (cmd) { - cmd->len = cpu_to_le32(len); - offset = offsetof(struct qcom_scm_command, buf); - cmd->buf_offset = cpu_to_le32(offset); - cmd->resp_hdr_offset = cpu_to_le32(offset + cmd_size); - } - return cmd; -} - -/** - * free_qcom_scm_command() - Free an SCM command - * @cmd: command to free - * - * Free an SCM command. - */ -static inline void free_qcom_scm_command(struct qcom_scm_command *cmd) -{ - kfree(cmd); -} - -/** - * qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response - * @cmd: command - * - * Returns a pointer to a response for a command. - */ -static inline struct qcom_scm_response *qcom_scm_command_to_response( - const struct qcom_scm_command *cmd) -{ - return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); -} - -/** - * qcom_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 *qcom_scm_get_command_buffer(const struct qcom_scm_command *cmd) -{ - return (void *)cmd->buf; -} - -/** - * qcom_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 *qcom_scm_get_response_buffer(const struct qcom_scm_response *rsp) -{ - return (void *)rsp + le32_to_cpu(rsp->buf_offset); -} - -static int qcom_scm_remap_error(int err) -{ - pr_err("qcom_scm_call failed with error code %d\n", err); - switch (err) { - case QCOM_SCM_ERROR: - return -EIO; - case QCOM_SCM_EINVAL_ADDR: - case QCOM_SCM_EINVAL_ARG: - return -EINVAL; - case QCOM_SCM_EOPNOTSUPP: - return -EOPNOTSUPP; - case QCOM_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 == QCOM_SCM_INTERRUPTED); - - return r0; -} - -static int __qcom_scm_call(const struct qcom_scm_command *cmd) -{ - int ret; - u32 cmd_addr = virt_to_phys(cmd); - - /* - * Flush the command buffer so that the secure world sees - * the correct data. - */ - __cpuc_flush_dcache_area((void *)cmd, cmd->len); - outer_flush_range(cmd_addr, cmd_addr + cmd->len); - - ret = smc(cmd_addr); - if (ret < 0) - ret = qcom_scm_remap_error(ret); - - return ret; -} - -static void qcom_scm_inv_range(unsigned long start, unsigned long end) -{ - u32 cacheline_size, ctr; - - asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr)); - cacheline_size = 4 << ((ctr >> 16) & 0xf); - - start = round_down(start, cacheline_size); - end = round_up(end, cacheline_size); - outer_inv_range(start, end); - while (start < end) { - asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) - : "memory"); - start += cacheline_size; - } - dsb(); - isb(); -} - -/** - * qcom_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. - * - * A note on cache maintenance: - * Note that any buffers that are expected to be accessed by the secure world - * must be flushed before invoking qcom_scm_call and invalidated in the cache - * immediately after qcom_scm_call returns. Cache maintenance on the command - * and response buffers is taken care of by qcom_scm_call; however, callers are - * responsible for any other cached buffers passed over to the secure world. - */ -static int qcom_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 qcom_scm_command *cmd; - struct qcom_scm_response *rsp; - unsigned long start, end; - - cmd = alloc_qcom_scm_command(cmd_len, resp_len); - if (!cmd) - return -ENOMEM; - - cmd->id = cpu_to_le32((svc_id << 10) | cmd_id); - if (cmd_buf) - memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len); - - mutex_lock(&qcom_scm_lock); - ret = __qcom_scm_call(cmd); - mutex_unlock(&qcom_scm_lock); - if (ret) - goto out; - - rsp = qcom_scm_command_to_response(cmd); - start = (unsigned long)rsp; - - do { - qcom_scm_inv_range(start, start + sizeof(*rsp)); - } while (!rsp->is_complete); - - end = (unsigned long)qcom_scm_get_response_buffer(rsp) + resp_len; - qcom_scm_inv_range(start, end); - - if (resp_buf) - memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), resp_len); -out: - free_qcom_scm_command(cmd); - return ret; -} - -u32 qcom_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(&qcom_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 == QCOM_SCM_INTERRUPTED); - - version = r1; - mutex_unlock(&qcom_scm_lock); - - return version; -} -EXPORT_SYMBOL(qcom_scm_get_version); - -#define QCOM_SCM_SVC_BOOT 0x1 -#define QCOM_SCM_BOOT_ADDR 0x1 -/* - * Set the cold/warm boot address for one of the CPU cores. - */ -int qcom_scm_set_boot_addr(u32 addr, int flags) -{ - struct { - __le32 flags; - __le32 addr; - } cmd; - - cmd.addr = cpu_to_le32(addr); - cmd.flags = cpu_to_le32(flags); - return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, - &cmd, sizeof(cmd), NULL, 0); -} -EXPORT_SYMBOL(qcom_scm_set_boot_addr); diff --git a/arch/arm/mach-qcom/scm.h b/arch/arm/mach-qcom/scm.h deleted file mode 100644 index 6bb84cffb396..000000000000 --- a/arch/arm/mach-qcom/scm.h +++ /dev/null @@ -1,29 +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 __QCOM_SCM_H -#define __QCOM_SCM_H - -#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 -#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 -#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 -#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 -#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 -#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 -#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 - -extern int qcom_scm_set_boot_addr(u32 addr, int flags); - -#define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) - -extern u32 qcom_scm_get_version(void); - -#endif diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 41983883cef4..6517132e5d8b 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -132,6 +132,10 @@ config ISCSI_IBFT detect iSCSI boot parameters dynamically during system boot, say Y. Otherwise, say N. +config QCOM_SCM + bool + depends on ARM || ARM64 + source "drivers/firmware/google/Kconfig" source "drivers/firmware/efi/Kconfig" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 5373dc5b6011..3fdd3912709a 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -11,6 +11,8 @@ obj-$(CONFIG_DMIID) += dmi-id.o obj-$(CONFIG_ISCSI_IBFT_FIND) += iscsi_ibft_find.o obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o +obj-$(CONFIG_QCOM_SCM) += qcom_scm.o +CFLAGS_qcom_scm.o :=$(call as-instr,.arch_extension sec,-DREQUIRES_SEC=1) obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ obj-$(CONFIG_EFI) += efi/ diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c new file mode 100644 index 000000000000..6e7a72bdb176 --- /dev/null +++ b/drivers/firmware/qcom_scm.c @@ -0,0 +1,344 @@ +/* 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 +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define QCOM_SCM_ENOMEM -5 +#define QCOM_SCM_EOPNOTSUPP -4 +#define QCOM_SCM_EINVAL_ADDR -3 +#define QCOM_SCM_EINVAL_ARG -2 +#define QCOM_SCM_ERROR -1 +#define QCOM_SCM_INTERRUPTED 1 + +static DEFINE_MUTEX(qcom_scm_lock); + +/** + * struct qcom_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 qcom_scm_get_command_buffer() + * + * An SCM command is laid out in memory as follows: + * + * ------------------- <--- struct qcom_scm_command + * | command header | + * ------------------- <--- qcom_scm_get_command_buffer() + * | command buffer | + * ------------------- <--- struct qcom_scm_response and + * | response header | qcom_scm_command_to_response() + * ------------------- <--- qcom_scm_get_response_buffer() + * | response buffer | + * ------------------- + * + * There can be arbitrary padding between the headers and buffers so + * you should always use the appropriate qcom_scm_get_*_buffer() routines + * to access the buffers in a safe manner. + */ +struct qcom_scm_command { + __le32 len; + __le32 buf_offset; + __le32 resp_hdr_offset; + __le32 id; + __le32 buf[0]; +}; + +/** + * struct qcom_scm_response - one SCM response buffer + * @len: total available memory for response + * @buf_offset: start of response data relative to start of qcom_scm_response + * @is_complete: indicates if the command has finished processing + */ +struct qcom_scm_response { + __le32 len; + __le32 buf_offset; + __le32 is_complete; +}; + +/** + * alloc_qcom_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 &qcom_scm_command on success or %NULL if the allocation fails. + */ +static struct qcom_scm_command *alloc_qcom_scm_command(size_t cmd_size, size_t resp_size) +{ + struct qcom_scm_command *cmd; + size_t len = sizeof(*cmd) + sizeof(struct qcom_scm_response) + cmd_size + + resp_size; + u32 offset; + + cmd = kzalloc(PAGE_ALIGN(len), GFP_KERNEL); + if (cmd) { + cmd->len = cpu_to_le32(len); + offset = offsetof(struct qcom_scm_command, buf); + cmd->buf_offset = cpu_to_le32(offset); + cmd->resp_hdr_offset = cpu_to_le32(offset + cmd_size); + } + return cmd; +} + +/** + * free_qcom_scm_command() - Free an SCM command + * @cmd: command to free + * + * Free an SCM command. + */ +static inline void free_qcom_scm_command(struct qcom_scm_command *cmd) +{ + kfree(cmd); +} + +/** + * qcom_scm_command_to_response() - Get a pointer to a qcom_scm_response + * @cmd: command + * + * Returns a pointer to a response for a command. + */ +static inline struct qcom_scm_response *qcom_scm_command_to_response( + const struct qcom_scm_command *cmd) +{ + return (void *)cmd + le32_to_cpu(cmd->resp_hdr_offset); +} + +/** + * qcom_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 *qcom_scm_get_command_buffer(const struct qcom_scm_command *cmd) +{ + return (void *)cmd->buf; +} + +/** + * qcom_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 *qcom_scm_get_response_buffer(const struct qcom_scm_response *rsp) +{ + return (void *)rsp + le32_to_cpu(rsp->buf_offset); +} + +static int qcom_scm_remap_error(int err) +{ + pr_err("qcom_scm_call failed with error code %d\n", err); + switch (err) { + case QCOM_SCM_ERROR: + return -EIO; + case QCOM_SCM_EINVAL_ADDR: + case QCOM_SCM_EINVAL_ARG: + return -EINVAL; + case QCOM_SCM_EOPNOTSUPP: + return -EOPNOTSUPP; + case QCOM_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 == QCOM_SCM_INTERRUPTED); + + return r0; +} + +static int __qcom_scm_call(const struct qcom_scm_command *cmd) +{ + int ret; + u32 cmd_addr = virt_to_phys(cmd); + + /* + * Flush the command buffer so that the secure world sees + * the correct data. + */ + __cpuc_flush_dcache_area((void *)cmd, cmd->len); + outer_flush_range(cmd_addr, cmd_addr + cmd->len); + + ret = smc(cmd_addr); + if (ret < 0) + ret = qcom_scm_remap_error(ret); + + return ret; +} + +static void qcom_scm_inv_range(unsigned long start, unsigned long end) +{ + u32 cacheline_size, ctr; + + asm volatile("mrc p15, 0, %0, c0, c0, 1" : "=r" (ctr)); + cacheline_size = 4 << ((ctr >> 16) & 0xf); + + start = round_down(start, cacheline_size); + end = round_up(end, cacheline_size); + outer_inv_range(start, end); + while (start < end) { + asm ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start) + : "memory"); + start += cacheline_size; + } + dsb(); + isb(); +} + +/** + * qcom_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. + * + * A note on cache maintenance: + * Note that any buffers that are expected to be accessed by the secure world + * must be flushed before invoking qcom_scm_call and invalidated in the cache + * immediately after qcom_scm_call returns. Cache maintenance on the command + * and response buffers is taken care of by qcom_scm_call; however, callers are + * responsible for any other cached buffers passed over to the secure world. + */ +static int qcom_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 qcom_scm_command *cmd; + struct qcom_scm_response *rsp; + unsigned long start, end; + + cmd = alloc_qcom_scm_command(cmd_len, resp_len); + if (!cmd) + return -ENOMEM; + + cmd->id = cpu_to_le32((svc_id << 10) | cmd_id); + if (cmd_buf) + memcpy(qcom_scm_get_command_buffer(cmd), cmd_buf, cmd_len); + + mutex_lock(&qcom_scm_lock); + ret = __qcom_scm_call(cmd); + mutex_unlock(&qcom_scm_lock); + if (ret) + goto out; + + rsp = qcom_scm_command_to_response(cmd); + start = (unsigned long)rsp; + + do { + qcom_scm_inv_range(start, start + sizeof(*rsp)); + } while (!rsp->is_complete); + + end = (unsigned long)qcom_scm_get_response_buffer(rsp) + resp_len; + qcom_scm_inv_range(start, end); + + if (resp_buf) + memcpy(resp_buf, qcom_scm_get_response_buffer(rsp), resp_len); +out: + free_qcom_scm_command(cmd); + return ret; +} + +u32 qcom_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(&qcom_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 == QCOM_SCM_INTERRUPTED); + + version = r1; + mutex_unlock(&qcom_scm_lock); + + return version; +} +EXPORT_SYMBOL(qcom_scm_get_version); + +#define QCOM_SCM_SVC_BOOT 0x1 +#define QCOM_SCM_BOOT_ADDR 0x1 +/* + * Set the cold/warm boot address for one of the CPU cores. + */ +int qcom_scm_set_boot_addr(u32 addr, int flags) +{ + struct { + __le32 flags; + __le32 addr; + } cmd; + + cmd.addr = cpu_to_le32(addr); + cmd.flags = cpu_to_le32(flags); + return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, + &cmd, sizeof(cmd), NULL, 0); +} +EXPORT_SYMBOL(qcom_scm_set_boot_addr); diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h new file mode 100644 index 000000000000..6bb84cffb396 --- /dev/null +++ b/include/linux/qcom_scm.h @@ -0,0 +1,29 @@ +/* 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 __QCOM_SCM_H +#define __QCOM_SCM_H + +#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 +#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 +#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 +#define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 +#define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 +#define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 +#define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 + +extern int qcom_scm_set_boot_addr(u32 addr, int flags); + +#define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) + +extern u32 qcom_scm_get_version(void); + +#endif -- cgit v1.2.3 From a353e4a06f24235138d483a2625726a5fc472949 Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Mon, 2 Mar 2015 16:30:28 -0700 Subject: firmware: qcom: scm: Clean cold boot entry to export only the API We dont need to export the SCM specific cold boot flags to the platform code. Export only a function to set the cold boot address. Signed-off-by: Lina Iyer Signed-off-by: Kumar Gala --- arch/arm/mach-qcom/platsmp.c | 21 +++------------------ drivers/firmware/qcom_scm.c | 41 +++++++++++++++++++++++++++++++++++++++-- include/linux/qcom_scm.h | 5 +---- 3 files changed, 43 insertions(+), 24 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-qcom/platsmp.c b/arch/arm/mach-qcom/platsmp.c index 4b67e56911d3..5cde63a64b34 100644 --- a/arch/arm/mach-qcom/platsmp.c +++ b/arch/arm/mach-qcom/platsmp.c @@ -319,25 +319,10 @@ static int kpssv2_boot_secondary(unsigned int cpu, struct task_struct *idle) static void __init qcom_smp_prepare_cpus(unsigned int max_cpus) { - int cpu, map; - unsigned int flags = 0; - static const int cold_boot_flags[] = { - 0, - QCOM_SCM_FLAG_COLDBOOT_CPU1, - QCOM_SCM_FLAG_COLDBOOT_CPU2, - QCOM_SCM_FLAG_COLDBOOT_CPU3, - }; - - for_each_present_cpu(cpu) { - map = cpu_logical_map(cpu); - if (WARN_ON(map >= ARRAY_SIZE(cold_boot_flags))) { - set_cpu_present(cpu, false); - continue; - } - flags |= cold_boot_flags[map]; - } + int cpu; - if (qcom_scm_set_boot_addr(virt_to_phys(secondary_startup_arm), flags)) { + if (qcom_scm_set_cold_boot_addr(secondary_startup_arm, + cpu_present_mask)) { for_each_present_cpu(cpu) { if (cpu == smp_processor_id()) continue; diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 6e7a72bdb176..c953cc38918f 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -34,6 +34,11 @@ #define QCOM_SCM_ERROR -1 #define QCOM_SCM_INTERRUPTED 1 +#define QCOM_SCM_FLAG_COLDBOOT_CPU0 0x00 +#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 +#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 +#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 + static DEFINE_MUTEX(qcom_scm_lock); /** @@ -329,7 +334,7 @@ EXPORT_SYMBOL(qcom_scm_get_version); /* * Set the cold/warm boot address for one of the CPU cores. */ -int qcom_scm_set_boot_addr(u32 addr, int flags) +static int qcom_scm_set_boot_addr(u32 addr, int flags) { struct { __le32 flags; @@ -341,4 +346,36 @@ int qcom_scm_set_boot_addr(u32 addr, int flags) return qcom_scm_call(QCOM_SCM_SVC_BOOT, QCOM_SCM_BOOT_ADDR, &cmd, sizeof(cmd), NULL, 0); } -EXPORT_SYMBOL(qcom_scm_set_boot_addr); + +/** + * qcom_scm_set_cold_boot_addr() - Set the cold boot address for cpus + * @entry: Entry point function for the cpus + * @cpus: The cpumask of cpus that will use the entry point + * + * Set the cold boot address of the cpus. Any cpu outside the supported + * range would be removed from the cpu present mask. + */ +int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus) +{ + int flags = 0; + int cpu; + int scm_cb_flags[] = { + QCOM_SCM_FLAG_COLDBOOT_CPU0, + QCOM_SCM_FLAG_COLDBOOT_CPU1, + QCOM_SCM_FLAG_COLDBOOT_CPU2, + QCOM_SCM_FLAG_COLDBOOT_CPU3, + }; + + if (!cpus || (cpus && cpumask_empty(cpus))) + return -EINVAL; + + for_each_cpu(cpu, cpus) { + if (cpu < ARRAY_SIZE(scm_cb_flags)) + flags |= scm_cb_flags[cpu]; + else + set_cpu_present(cpu, false); + } + + return qcom_scm_set_boot_addr(virt_to_phys(entry), flags); +} +EXPORT_SYMBOL(qcom_scm_set_cold_boot_addr); diff --git a/include/linux/qcom_scm.h b/include/linux/qcom_scm.h index 6bb84cffb396..68a1d8801c6f 100644 --- a/include/linux/qcom_scm.h +++ b/include/linux/qcom_scm.h @@ -12,15 +12,12 @@ #ifndef __QCOM_SCM_H #define __QCOM_SCM_H -#define QCOM_SCM_FLAG_COLDBOOT_CPU1 0x01 -#define QCOM_SCM_FLAG_COLDBOOT_CPU2 0x08 -#define QCOM_SCM_FLAG_COLDBOOT_CPU3 0x20 #define QCOM_SCM_FLAG_WARMBOOT_CPU0 0x04 #define QCOM_SCM_FLAG_WARMBOOT_CPU1 0x02 #define QCOM_SCM_FLAG_WARMBOOT_CPU2 0x10 #define QCOM_SCM_FLAG_WARMBOOT_CPU3 0x40 -extern int qcom_scm_set_boot_addr(u32 addr, int flags); +extern int qcom_scm_set_cold_boot_addr(void *entry, const cpumask_t *cpus); #define QCOM_SCM_VERSION(major, minor) (((major) << 16) | ((minor) & 0xFF)) -- cgit v1.2.3 From b595809b9842aaa1d67f77c52281a32636d64359 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 12 Mar 2015 13:07:25 +0100 Subject: ARM: at91/dt: declare atmel,at91rm9200-st as a syscon The system timer register range is used to handle three different functionalities: Periodic Interval Timer, Real Time Timer and watchdog. Declare it as a syscon to be able to get a regmap. Also declare it as a simple-mfd and its watchdog subnode. Finally, document the watchdog compatible string. Signed-off-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- Documentation/devicetree/bindings/arm/atmel-at91.txt | 4 +++- arch/arm/boot/dts/at91rm9200.dtsi | 6 +++++- 2 files changed, 8 insertions(+), 2 deletions(-) (limited to 'arch') diff --git a/Documentation/devicetree/bindings/arm/atmel-at91.txt b/Documentation/devicetree/bindings/arm/atmel-at91.txt index ad319f84f560..2e99b5b57350 100644 --- a/Documentation/devicetree/bindings/arm/atmel-at91.txt +++ b/Documentation/devicetree/bindings/arm/atmel-at91.txt @@ -46,10 +46,12 @@ PIT Timer required properties: shared across all System Controller members. System Timer (ST) required properties: -- compatible: Should be "atmel,at91rm9200-st" +- compatible: Should be "atmel,at91rm9200-st", "syscon", "simple-mfd" - reg: Should contain registers location and length - interrupts: Should contain interrupt for the ST which is the IRQ line shared across all System Controller members. +Its subnodes can be: +- watchdog: compatible should be "atmel,at91rm9200-wdt" TC/TCLIB Timer required properties: - compatible: Should be "atmel,-tcb". diff --git a/arch/arm/boot/dts/at91rm9200.dtsi b/arch/arm/boot/dts/at91rm9200.dtsi index c515e4b160be..4fb333bd1f85 100644 --- a/arch/arm/boot/dts/at91rm9200.dtsi +++ b/arch/arm/boot/dts/at91rm9200.dtsi @@ -356,9 +356,13 @@ }; st: timer@fffffd00 { - compatible = "atmel,at91rm9200-st"; + compatible = "atmel,at91rm9200-st", "syscon", "simple-mfd"; reg = <0xfffffd00 0x100>; interrupts = <1 IRQ_TYPE_LEVEL_HIGH 7>; + + watchdog { + compatible = "atmel,at91rm9200-wdt"; + }; }; rtc: rtc@fffffe00 { -- cgit v1.2.3 From d8d1c6c8b0bec79b42f9e85fbf449ba7dd77accd Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 12 Mar 2015 13:07:29 +0100 Subject: ARM: at91: at91rm9200: remove deprecated arm_pm_restart Now that a proper driver is available, remove at91rm9200_restart. Signed-off-by: Alexandre Belloni Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91rm9200.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c index bfc9aed90b91..7634e10bc5dc 100644 --- a/arch/arm/mach-at91/at91rm9200.c +++ b/arch/arm/mach-at91/at91rm9200.c @@ -15,8 +15,6 @@ #include #include -#include - #include "generic.h" #include "soc.h" @@ -25,15 +23,6 @@ static const struct at91_soc rm9200_socs[] = { { /* sentinel */ }, }; -static void at91rm9200_restart(enum reboot_mode reboot_mode, const char *cmd) -{ - /* - * Perform a hardware reset with the use of the Watchdog timer. - */ - at91_st_write(AT91_ST_WDMR, AT91_ST_RSTEN | AT91_ST_EXTEN | 1); - at91_st_write(AT91_ST_CR, AT91_ST_WDRST); -} - static void __init at91rm9200_dt_timer_init(void) { of_clk_init(NULL); @@ -52,7 +41,6 @@ static void __init at91rm9200_dt_device_init(void) of_platform_populate(NULL, of_default_bus_match_table, NULL, soc_dev); arm_pm_idle = at91rm9200_idle; - arm_pm_restart = at91rm9200_restart; at91rm9200_pm_init(); } -- cgit v1.2.3 From bbfc97e1b1b9fb2177a9134f57506eb371638b85 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 12 Mar 2015 13:07:30 +0100 Subject: ARM: at91: properly initialize timer Use clocksource_of_init to initialize the system timer instead of relying on a custom function. Signed-off-by: Alexandre Belloni Acked-by: Boris Brezillon Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/at91rm9200.c | 7 ------- arch/arm/mach-at91/at91rm9200_time.c | 4 +++- arch/arm/mach-at91/generic.h | 3 --- 3 files changed, 3 insertions(+), 11 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-at91/at91rm9200.c b/arch/arm/mach-at91/at91rm9200.c index 7634e10bc5dc..eaf58f88ef5d 100644 --- a/arch/arm/mach-at91/at91rm9200.c +++ b/arch/arm/mach-at91/at91rm9200.c @@ -23,12 +23,6 @@ static const struct at91_soc rm9200_socs[] = { { /* sentinel */ }, }; -static void __init at91rm9200_dt_timer_init(void) -{ - of_clk_init(NULL); - at91rm9200_timer_init(); -} - static void __init at91rm9200_dt_device_init(void) { struct soc_device *soc; @@ -50,7 +44,6 @@ static const char *at91rm9200_dt_board_compat[] __initconst = { }; DT_MACHINE_START(at91rm9200_dt, "Atmel AT91RM9200") - .init_time = at91rm9200_dt_timer_init, .init_machine = at91rm9200_dt_device_init, .dt_compat = at91rm9200_dt_board_compat, MACHINE_END diff --git a/arch/arm/mach-at91/at91rm9200_time.c b/arch/arm/mach-at91/at91rm9200_time.c index b00d09555f2b..7d062ab32674 100644 --- a/arch/arm/mach-at91/at91rm9200_time.c +++ b/arch/arm/mach-at91/at91rm9200_time.c @@ -222,7 +222,7 @@ err: /* * ST (system timer) module supports both clockevents and clocksource. */ -void __init at91rm9200_timer_init(void) +static void __init atmel_st_timer_init(struct device_node *node) { /* For device tree enabled device: initialize here */ of_at91rm9200_st_init(); @@ -249,3 +249,5 @@ void __init at91rm9200_timer_init(void) /* register clocksource */ clocksource_register_hz(&clk32k, AT91_SLOW_CLOCK); } +CLOCKSOURCE_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st", + atmel_st_timer_init); diff --git a/arch/arm/mach-at91/generic.h b/arch/arm/mach-at91/generic.h index 2b396c4e1fc1..b0fa7dc7286d 100644 --- a/arch/arm/mach-at91/generic.h +++ b/arch/arm/mach-at91/generic.h @@ -18,9 +18,6 @@ extern void __init at91_map_io(void); extern void __init at91_alt_map_io(void); - /* Timer */ -extern void at91rm9200_timer_init(void); - /* idle */ extern void at91rm9200_idle(void); extern void at91sam9_idle(void); -- cgit v1.2.3 From b53cdd03222391f76e82cd0c7d040816c919ab75 Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 12 Mar 2015 13:07:31 +0100 Subject: ARM: at91: time: move the system timer driver to drivers/clocksource Import at91rm9200_time.c from mach-at91 as timer-atmel-st.c. Further cleanup is required to get rid of the mach-at91 headers. Signed-off-by: Alexandre Belloni Acked-by: Boris Brezillon Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/Kconfig | 1 + arch/arm/mach-at91/Makefile | 2 +- arch/arm/mach-at91/at91rm9200_time.c | 253 ----------------------------------- drivers/clocksource/Kconfig | 4 + drivers/clocksource/Makefile | 1 + drivers/clocksource/timer-atmel-st.c | 253 +++++++++++++++++++++++++++++++++++ 6 files changed, 260 insertions(+), 254 deletions(-) delete mode 100644 arch/arm/mach-at91/at91rm9200_time.c create mode 100644 drivers/clocksource/timer-atmel-st.c (limited to 'arch') diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig index 4da6bae047f3..24b59c75f6af 100644 --- a/arch/arm/mach-at91/Kconfig +++ b/arch/arm/mach-at91/Kconfig @@ -77,6 +77,7 @@ if SOC_SAM_V4_V5 config SOC_AT91RM9200 bool "AT91RM9200" select ATMEL_AIC_IRQ + select ATMEL_ST select COMMON_CLK_AT91 select CPU_ARM920T select GENERIC_CLOCKEVENTS diff --git a/arch/arm/mach-at91/Makefile b/arch/arm/mach-at91/Makefile index ea54c9824d89..38aaef7b994e 100644 --- a/arch/arm/mach-at91/Makefile +++ b/arch/arm/mach-at91/Makefile @@ -7,7 +7,7 @@ obj-y := soc.o obj-$(CONFIG_SOC_AT91SAM9) += sam9_smc.o # CPU-specific support -obj-$(CONFIG_SOC_AT91RM9200) += at91rm9200.o at91rm9200_time.o +obj-$(CONFIG_SOC_AT91RM9200) += at91rm9200.o obj-$(CONFIG_SOC_AT91SAM9) += at91sam9.o obj-$(CONFIG_SOC_SAMA5) += sama5.o diff --git a/arch/arm/mach-at91/at91rm9200_time.c b/arch/arm/mach-at91/at91rm9200_time.c deleted file mode 100644 index 7d062ab32674..000000000000 --- a/arch/arm/mach-at91/at91rm9200_time.c +++ /dev/null @@ -1,253 +0,0 @@ -/* - * linux/arch/arm/mach-at91/at91rm9200_time.c - * - * Copyright (C) 2003 SAN People - * Copyright (C) 2003 ATMEL - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -static unsigned long last_crtr; -static u32 irqmask; -static struct clock_event_device clkevt; - -#define RM9200_TIMER_LATCH ((AT91_SLOW_CLOCK + HZ/2) / HZ) - -/* - * The ST_CRTR is updated asynchronously to the master clock ... but - * the updates as seen by the CPU don't seem to be strictly monotonic. - * Waiting until we read the same value twice avoids glitching. - */ -static inline unsigned long read_CRTR(void) -{ - unsigned long x1, x2; - - x1 = at91_st_read(AT91_ST_CRTR); - do { - x2 = at91_st_read(AT91_ST_CRTR); - if (x1 == x2) - break; - x1 = x2; - } while (1); - return x1; -} - -/* - * IRQ handler for the timer. - */ -static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id) -{ - u32 sr = at91_st_read(AT91_ST_SR) & irqmask; - - /* - * irqs should be disabled here, but as the irq is shared they are only - * guaranteed to be off if the timer irq is registered first. - */ - WARN_ON_ONCE(!irqs_disabled()); - - /* simulate "oneshot" timer with alarm */ - if (sr & AT91_ST_ALMS) { - clkevt.event_handler(&clkevt); - return IRQ_HANDLED; - } - - /* periodic mode should handle delayed ticks */ - if (sr & AT91_ST_PITS) { - u32 crtr = read_CRTR(); - - while (((crtr - last_crtr) & AT91_ST_CRTV) >= RM9200_TIMER_LATCH) { - last_crtr += RM9200_TIMER_LATCH; - clkevt.event_handler(&clkevt); - } - return IRQ_HANDLED; - } - - /* this irq is shared ... */ - return IRQ_NONE; -} - -static struct irqaction at91rm9200_timer_irq = { - .name = "at91_tick", - .flags = IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, - .handler = at91rm9200_timer_interrupt, - .irq = NR_IRQS_LEGACY + AT91_ID_SYS, -}; - -static cycle_t read_clk32k(struct clocksource *cs) -{ - return read_CRTR(); -} - -static struct clocksource clk32k = { - .name = "32k_counter", - .rating = 150, - .read = read_clk32k, - .mask = CLOCKSOURCE_MASK(20), - .flags = CLOCK_SOURCE_IS_CONTINUOUS, -}; - -static void -clkevt32k_mode(enum clock_event_mode mode, struct clock_event_device *dev) -{ - /* Disable and flush pending timer interrupts */ - at91_st_write(AT91_ST_IDR, AT91_ST_PITS | AT91_ST_ALMS); - at91_st_read(AT91_ST_SR); - - last_crtr = read_CRTR(); - switch (mode) { - case CLOCK_EVT_MODE_PERIODIC: - /* PIT for periodic irqs; fixed rate of 1/HZ */ - irqmask = AT91_ST_PITS; - at91_st_write(AT91_ST_PIMR, RM9200_TIMER_LATCH); - break; - case CLOCK_EVT_MODE_ONESHOT: - /* ALM for oneshot irqs, set by next_event() - * before 32 seconds have passed - */ - irqmask = AT91_ST_ALMS; - at91_st_write(AT91_ST_RTAR, last_crtr); - break; - case CLOCK_EVT_MODE_SHUTDOWN: - case CLOCK_EVT_MODE_UNUSED: - case CLOCK_EVT_MODE_RESUME: - irqmask = 0; - break; - } - at91_st_write(AT91_ST_IER, irqmask); -} - -static int -clkevt32k_next_event(unsigned long delta, struct clock_event_device *dev) -{ - u32 alm; - int status = 0; - - BUG_ON(delta < 2); - - /* The alarm IRQ uses absolute time (now+delta), not the relative - * time (delta) in our calling convention. Like all clockevents - * using such "match" hardware, we have a race to defend against. - * - * Our defense here is to have set up the clockevent device so the - * delta is at least two. That way we never end up writing RTAR - * with the value then held in CRTR ... which would mean the match - * wouldn't trigger until 32 seconds later, after CRTR wraps. - */ - alm = read_CRTR(); - - /* Cancel any pending alarm; flush any pending IRQ */ - at91_st_write(AT91_ST_RTAR, alm); - at91_st_read(AT91_ST_SR); - - /* Schedule alarm by writing RTAR. */ - alm += delta; - at91_st_write(AT91_ST_RTAR, alm); - - return status; -} - -static struct clock_event_device clkevt = { - .name = "at91_tick", - .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, - .rating = 150, - .set_next_event = clkevt32k_next_event, - .set_mode = clkevt32k_mode, -}; - -void __iomem *at91_st_base; -EXPORT_SYMBOL_GPL(at91_st_base); - -static const struct of_device_id at91rm9200_st_timer_ids[] = { - { .compatible = "atmel,at91rm9200-st" }, - { /* sentinel */ } -}; - -static int __init of_at91rm9200_st_init(void) -{ - struct device_node *np; - int ret; - - np = of_find_matching_node(NULL, at91rm9200_st_timer_ids); - if (!np) - goto err; - - at91_st_base = of_iomap(np, 0); - if (!at91_st_base) - goto node_err; - - /* Get the interrupts property */ - ret = irq_of_parse_and_map(np, 0); - if (!ret) - goto ioremap_err; - at91rm9200_timer_irq.irq = ret; - - of_node_put(np); - - return 0; - -ioremap_err: - iounmap(at91_st_base); -node_err: - of_node_put(np); -err: - return -EINVAL; -} - -/* - * ST (system timer) module supports both clockevents and clocksource. - */ -static void __init atmel_st_timer_init(struct device_node *node) -{ - /* For device tree enabled device: initialize here */ - of_at91rm9200_st_init(); - - /* Disable all timer interrupts, and clear any pending ones */ - at91_st_write(AT91_ST_IDR, - AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS); - at91_st_read(AT91_ST_SR); - - /* Make IRQs happen for the system timer */ - setup_irq(at91rm9200_timer_irq.irq, &at91rm9200_timer_irq); - - /* The 32KiHz "Slow Clock" (tick every 30517.58 nanoseconds) is used - * directly for the clocksource and all clockevents, after adjusting - * its prescaler from the 1 Hz default. - */ - at91_st_write(AT91_ST_RTMR, 1); - - /* Setup timer clockevent, with minimum of two ticks (important!!) */ - clkevt.cpumask = cpumask_of(0); - clockevents_config_and_register(&clkevt, AT91_SLOW_CLOCK, - 2, AT91_ST_ALMV); - - /* register clocksource */ - clocksource_register_hz(&clk32k, AT91_SLOW_CLOCK); -} -CLOCKSOURCE_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st", - atmel_st_timer_init); diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 68161f7a07d6..168035883afa 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -143,6 +143,10 @@ config ATMEL_PIT select CLKSRC_OF if OF def_bool SOC_AT91SAM9 || SOC_SAMA5 +config ATMEL_ST + bool + select CLKSRC_OF + config CLKSRC_METAG_GENERIC def_bool y if METAG help diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index 752d5c70b0ef..5b85f6adb258 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_CLKSRC_OF) += clksrc-of.o obj-$(CONFIG_ATMEL_PIT) += timer-atmel-pit.o +obj-$(CONFIG_ATMEL_ST) += timer-atmel-st.o obj-$(CONFIG_ATMEL_TCB_CLKSRC) += tcb_clksrc.o obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o diff --git a/drivers/clocksource/timer-atmel-st.c b/drivers/clocksource/timer-atmel-st.c new file mode 100644 index 000000000000..7d062ab32674 --- /dev/null +++ b/drivers/clocksource/timer-atmel-st.c @@ -0,0 +1,253 @@ +/* + * linux/arch/arm/mach-at91/at91rm9200_time.c + * + * Copyright (C) 2003 SAN People + * Copyright (C) 2003 ATMEL + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +static unsigned long last_crtr; +static u32 irqmask; +static struct clock_event_device clkevt; + +#define RM9200_TIMER_LATCH ((AT91_SLOW_CLOCK + HZ/2) / HZ) + +/* + * The ST_CRTR is updated asynchronously to the master clock ... but + * the updates as seen by the CPU don't seem to be strictly monotonic. + * Waiting until we read the same value twice avoids glitching. + */ +static inline unsigned long read_CRTR(void) +{ + unsigned long x1, x2; + + x1 = at91_st_read(AT91_ST_CRTR); + do { + x2 = at91_st_read(AT91_ST_CRTR); + if (x1 == x2) + break; + x1 = x2; + } while (1); + return x1; +} + +/* + * IRQ handler for the timer. + */ +static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id) +{ + u32 sr = at91_st_read(AT91_ST_SR) & irqmask; + + /* + * irqs should be disabled here, but as the irq is shared they are only + * guaranteed to be off if the timer irq is registered first. + */ + WARN_ON_ONCE(!irqs_disabled()); + + /* simulate "oneshot" timer with alarm */ + if (sr & AT91_ST_ALMS) { + clkevt.event_handler(&clkevt); + return IRQ_HANDLED; + } + + /* periodic mode should handle delayed ticks */ + if (sr & AT91_ST_PITS) { + u32 crtr = read_CRTR(); + + while (((crtr - last_crtr) & AT91_ST_CRTV) >= RM9200_TIMER_LATCH) { + last_crtr += RM9200_TIMER_LATCH; + clkevt.event_handler(&clkevt); + } + return IRQ_HANDLED; + } + + /* this irq is shared ... */ + return IRQ_NONE; +} + +static struct irqaction at91rm9200_timer_irq = { + .name = "at91_tick", + .flags = IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL, + .handler = at91rm9200_timer_interrupt, + .irq = NR_IRQS_LEGACY + AT91_ID_SYS, +}; + +static cycle_t read_clk32k(struct clocksource *cs) +{ + return read_CRTR(); +} + +static struct clocksource clk32k = { + .name = "32k_counter", + .rating = 150, + .read = read_clk32k, + .mask = CLOCKSOURCE_MASK(20), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; + +static void +clkevt32k_mode(enum clock_event_mode mode, struct clock_event_device *dev) +{ + /* Disable and flush pending timer interrupts */ + at91_st_write(AT91_ST_IDR, AT91_ST_PITS | AT91_ST_ALMS); + at91_st_read(AT91_ST_SR); + + last_crtr = read_CRTR(); + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: + /* PIT for periodic irqs; fixed rate of 1/HZ */ + irqmask = AT91_ST_PITS; + at91_st_write(AT91_ST_PIMR, RM9200_TIMER_LATCH); + break; + case CLOCK_EVT_MODE_ONESHOT: + /* ALM for oneshot irqs, set by next_event() + * before 32 seconds have passed + */ + irqmask = AT91_ST_ALMS; + at91_st_write(AT91_ST_RTAR, last_crtr); + break; + case CLOCK_EVT_MODE_SHUTDOWN: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_RESUME: + irqmask = 0; + break; + } + at91_st_write(AT91_ST_IER, irqmask); +} + +static int +clkevt32k_next_event(unsigned long delta, struct clock_event_device *dev) +{ + u32 alm; + int status = 0; + + BUG_ON(delta < 2); + + /* The alarm IRQ uses absolute time (now+delta), not the relative + * time (delta) in our calling convention. Like all clockevents + * using such "match" hardware, we have a race to defend against. + * + * Our defense here is to have set up the clockevent device so the + * delta is at least two. That way we never end up writing RTAR + * with the value then held in CRTR ... which would mean the match + * wouldn't trigger until 32 seconds later, after CRTR wraps. + */ + alm = read_CRTR(); + + /* Cancel any pending alarm; flush any pending IRQ */ + at91_st_write(AT91_ST_RTAR, alm); + at91_st_read(AT91_ST_SR); + + /* Schedule alarm by writing RTAR. */ + alm += delta; + at91_st_write(AT91_ST_RTAR, alm); + + return status; +} + +static struct clock_event_device clkevt = { + .name = "at91_tick", + .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, + .rating = 150, + .set_next_event = clkevt32k_next_event, + .set_mode = clkevt32k_mode, +}; + +void __iomem *at91_st_base; +EXPORT_SYMBOL_GPL(at91_st_base); + +static const struct of_device_id at91rm9200_st_timer_ids[] = { + { .compatible = "atmel,at91rm9200-st" }, + { /* sentinel */ } +}; + +static int __init of_at91rm9200_st_init(void) +{ + struct device_node *np; + int ret; + + np = of_find_matching_node(NULL, at91rm9200_st_timer_ids); + if (!np) + goto err; + + at91_st_base = of_iomap(np, 0); + if (!at91_st_base) + goto node_err; + + /* Get the interrupts property */ + ret = irq_of_parse_and_map(np, 0); + if (!ret) + goto ioremap_err; + at91rm9200_timer_irq.irq = ret; + + of_node_put(np); + + return 0; + +ioremap_err: + iounmap(at91_st_base); +node_err: + of_node_put(np); +err: + return -EINVAL; +} + +/* + * ST (system timer) module supports both clockevents and clocksource. + */ +static void __init atmel_st_timer_init(struct device_node *node) +{ + /* For device tree enabled device: initialize here */ + of_at91rm9200_st_init(); + + /* Disable all timer interrupts, and clear any pending ones */ + at91_st_write(AT91_ST_IDR, + AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS); + at91_st_read(AT91_ST_SR); + + /* Make IRQs happen for the system timer */ + setup_irq(at91rm9200_timer_irq.irq, &at91rm9200_timer_irq); + + /* The 32KiHz "Slow Clock" (tick every 30517.58 nanoseconds) is used + * directly for the clocksource and all clockevents, after adjusting + * its prescaler from the 1 Hz default. + */ + at91_st_write(AT91_ST_RTMR, 1); + + /* Setup timer clockevent, with minimum of two ticks (important!!) */ + clkevt.cpumask = cpumask_of(0); + clockevents_config_and_register(&clkevt, AT91_SLOW_CLOCK, + 2, AT91_ST_ALMV); + + /* register clocksource */ + clocksource_register_hz(&clk32k, AT91_SLOW_CLOCK); +} +CLOCKSOURCE_OF_DECLARE(atmel_st_timer, "atmel,at91rm9200-st", + atmel_st_timer_init); -- cgit v1.2.3 From 8590ca655a19f9e124b52bfbf28f14eb16a05bec Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Thu, 12 Mar 2015 13:07:34 +0100 Subject: ARM: at91: remove useless include Both drivers using the system timer are now converted to an MFD. mach/at91_st.h is now useless. Signed-off-by: Alexandre Belloni Acked-by: Boris Brezillon Acked-by: Daniel Lezcano Signed-off-by: Nicolas Ferre --- arch/arm/mach-at91/include/mach/at91_st.h | 61 ------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 arch/arm/mach-at91/include/mach/at91_st.h (limited to 'arch') diff --git a/arch/arm/mach-at91/include/mach/at91_st.h b/arch/arm/mach-at91/include/mach/at91_st.h deleted file mode 100644 index 67fdbd13c3ed..000000000000 --- a/arch/arm/mach-at91/include/mach/at91_st.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * arch/arm/mach-at91/include/mach/at91_st.h - * - * Copyright (C) 2005 Ivan Kokshaysky - * Copyright (C) SAN People - * - * System Timer (ST) - System peripherals registers. - * Based on AT91RM9200 datasheet revision E. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - */ - -#ifndef AT91_ST_H -#define AT91_ST_H - -#ifndef __ASSEMBLY__ -extern void __iomem *at91_st_base; - -#define at91_st_read(field) \ - __raw_readl(at91_st_base + field) - -#define at91_st_write(field, value) \ - __raw_writel(value, at91_st_base + field) -#else -.extern at91_st_base -#endif - -#define AT91_ST_CR 0x00 /* Control Register */ -#define AT91_ST_WDRST (1 << 0) /* Watchdog Timer Restart */ - -#define AT91_ST_PIMR 0x04 /* Period Interval Mode Register */ -#define AT91_ST_PIV (0xffff << 0) /* Period Interval Value */ - -#define AT91_ST_WDMR 0x08 /* Watchdog Mode Register */ -#define AT91_ST_WDV (0xffff << 0) /* Watchdog Counter Value */ -#define AT91_ST_RSTEN (1 << 16) /* Reset Enable */ -#define AT91_ST_EXTEN (1 << 17) /* External Signal Assertion Enable */ - -#define AT91_ST_RTMR 0x0c /* Real-time Mode Register */ -#define AT91_ST_RTPRES (0xffff << 0) /* Real-time Prescalar Value */ - -#define AT91_ST_SR 0x10 /* Status Register */ -#define AT91_ST_PITS (1 << 0) /* Period Interval Timer Status */ -#define AT91_ST_WDOVF (1 << 1) /* Watchdog Overflow */ -#define AT91_ST_RTTINC (1 << 2) /* Real-time Timer Increment */ -#define AT91_ST_ALMS (1 << 3) /* Alarm Status */ - -#define AT91_ST_IER 0x14 /* Interrupt Enable Register */ -#define AT91_ST_IDR 0x18 /* Interrupt Disable Register */ -#define AT91_ST_IMR 0x1c /* Interrupt Mask Register */ - -#define AT91_ST_RTAR 0x20 /* Real-time Alarm Register */ -#define AT91_ST_ALMV (0xfffff << 0) /* Alarm Value */ - -#define AT91_ST_CRTR 0x24 /* Current Real-time Register */ -#define AT91_ST_CRTV (0xfffff << 0) /* Current Real-Time Value */ - -#endif -- cgit v1.2.3 From 772742a6c7ea4612fe043353531e6435ed33e719 Mon Sep 17 00:00:00 2001 From: "Suzuki K. Poulose" Date: Wed, 18 Mar 2015 12:24:40 +0000 Subject: arm-cci: Get rid of secure transactions for PMU driver Avoid secure transactions while probing the CCI PMU. The existing code makes use of the Peripheral ID2 (PID2) register to determine the revision of the CCI400, which requires a secure transaction. This puts a limitation on the usage of the driver on systems running non-secure Linux(e.g, ARM64). Updated the device-tree binding for cci pmu node to add the explicit revision number for the compatible field. The supported strings are : arm,cci-400-pmu,r0 arm,cci-400-pmu,r1 arm,cci-400-pmu - DEPRECATED. See NOTE below NOTE: If the revision is not mentioned, we need to probe the cci revision, which could be fatal on a platform running non-secure. We need a reliable way to know if we can poke the CCI registers at runtime on ARM32. We depend on 'mcpm_is_available()' when it is available. mcpm_is_available() returns true only when there is a registered driver for mcpm. Otherwise, we assume that we don't have secure access, and skips probing the revision number(ARM64 case). The MCPM should figure out if it is safe to access the CCI. Unfortunately there isn't a reliable way to indicate the same via dtb. This patch doesn't address/change the current situation. It only deals with the CCI-PMU, leaving the assumptions about the secure access as it has been, prior to this patch. Cc: devicetree@vger.kernel.org Cc: Punit Agrawal Tested-by: Sudeep Holla Acked-by: Nicolas Pitre Acked-by: Mark Rutland Signed-off-by: Suzuki K. Poulose Signed-off-by: Will Deacon --- Documentation/devicetree/bindings/arm/cci.txt | 7 +++-- arch/arm/include/asm/arm-cci.h | 42 +++++++++++++++++++++++++++ arch/arm64/include/asm/arm-cci.h | 27 +++++++++++++++++ drivers/bus/arm-cci.c | 17 ++++++++++- include/linux/arm-cci.h | 2 ++ 5 files changed, 92 insertions(+), 3 deletions(-) create mode 100644 arch/arm/include/asm/arm-cci.h create mode 100644 arch/arm64/include/asm/arm-cci.h (limited to 'arch') diff --git a/Documentation/devicetree/bindings/arm/cci.txt b/Documentation/devicetree/bindings/arm/cci.txt index f28d82bbbc56..3c5c631328d3 100644 --- a/Documentation/devicetree/bindings/arm/cci.txt +++ b/Documentation/devicetree/bindings/arm/cci.txt @@ -94,8 +94,11 @@ specific to ARM. - compatible Usage: required Value type: - Definition: must be "arm,cci-400-pmu" - + Definition: Must contain one of: + "arm,cci-400-pmu,r0" + "arm,cci-400-pmu,r1" + "arm,cci-400-pmu" - DEPRECATED, permitted only where OS has + secure acces to CCI registers - reg: Usage: required Value type: Integer cells. A register entry, expressed diff --git a/arch/arm/include/asm/arm-cci.h b/arch/arm/include/asm/arm-cci.h new file mode 100644 index 000000000000..fe77f7ab7e6b --- /dev/null +++ b/arch/arm/include/asm/arm-cci.h @@ -0,0 +1,42 @@ +/* + * arch/arm/include/asm/arm-cci.h + * + * Copyright (C) 2015 ARM Ltd. + * + * 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. + * + * 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, see . + */ + +#ifndef __ASM_ARM_CCI_H +#define __ASM_ARM_CCI_H + +#ifdef CONFIG_MCPM +#include + +/* + * We don't have a reliable way of detecting whether, + * if we have access to secure-only registers, unless + * mcpm is registered. + */ +static inline bool platform_has_secure_cci_access(void) +{ + return mcpm_is_available(); +} + +#else +static inline bool platform_has_secure_cci_access(void) +{ + return false; +} +#endif + +#endif diff --git a/arch/arm64/include/asm/arm-cci.h b/arch/arm64/include/asm/arm-cci.h new file mode 100644 index 000000000000..f0b63712e10e --- /dev/null +++ b/arch/arm64/include/asm/arm-cci.h @@ -0,0 +1,27 @@ +/* + * arch/arm64/include/asm/arm-cci.h + * + * Copyright (C) 2015 ARM Ltd. + * + * 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. + * + * 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, see . + */ + +#ifndef __ASM_ARM_CCI_H +#define __ASM_ARM_CCI_H + +static inline bool platform_has_secure_cci_access(void) +{ + return false; +} + +#endif diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c index ae3864d95e6c..a23663c7a306 100644 --- a/drivers/bus/arm-cci.c +++ b/drivers/bus/arm-cci.c @@ -217,7 +217,9 @@ static int probe_cci_revision(void) static const struct cci_pmu_model *probe_cci_model(struct platform_device *pdev) { - return &cci_pmu_models[probe_cci_revision()]; + if (platform_has_secure_cci_access()) + return &cci_pmu_models[probe_cci_revision()]; + return NULL; } static int pmu_is_valid_counter(struct cci_pmu *cci_pmu, int idx) @@ -882,6 +884,15 @@ static struct cci_pmu_model cci_pmu_models[] = { static const struct of_device_id arm_cci_pmu_matches[] = { { .compatible = "arm,cci-400-pmu", + .data = NULL, + }, + { + .compatible = "arm,cci-400-pmu,r0", + .data = &cci_pmu_models[CCI_REV_R0], + }, + { + .compatible = "arm,cci-400-pmu,r1", + .data = &cci_pmu_models[CCI_REV_R1], }, {}, }; @@ -892,7 +903,11 @@ static inline const struct cci_pmu_model *get_cci_model(struct platform_device * pdev->dev.of_node); if (!match) return NULL; + if (match->data) + return match->data; + dev_warn(&pdev->dev, "DEPRECATED compatible property," + "requires secure access to CCI registers"); return probe_cci_model(pdev); } diff --git a/include/linux/arm-cci.h b/include/linux/arm-cci.h index 79d6edf446d5..aede5c765eec 100644 --- a/include/linux/arm-cci.h +++ b/include/linux/arm-cci.h @@ -24,6 +24,8 @@ #include #include +#include + struct device_node; #ifdef CONFIG_ARM_CCI -- cgit v1.2.3 From ee8e5d5fbec0e880b18bbdbfe12de53ab1dec21f Mon Sep 17 00:00:00 2001 From: "Suzuki K. Poulose" Date: Wed, 18 Mar 2015 12:24:41 +0000 Subject: arm-cci: Split the code for PMU vs driver support This patch separates the PMU driver code from the low level CCI driver code and enables the PMU driver for ARM64. Introduces config options for both. ARM_CCI400_PORT_CTRL - controls the low level driver code for CCI400 ports. ARM_CCI400_PMU - controls the PMU driver code ARM_CCI400_COMMON - Common defintions for CCI400 This patch also changes: ARM_CCI - common code for probing the CCI devices. This can be used for adding support for newer CCI versions(e.g, CCI-500). Cc: Bartlomiej Zolnierkiewicz Cc: Kukjin Kim Cc: Abhilash Kesavan Cc: Liviu Dudau Cc: Lorenzo Pieralisi Cc: Sudeep Holla Cc: Nicolas Pitre Cc: Punit Agrawal Acked-by: Sudeep Holla Acked-by: Nicolas Pitre Acked-by: Punit Agrawal Signed-off-by: Suzuki K. Poulose Signed-off-by: Will Deacon --- arch/arm/mach-exynos/Kconfig | 2 +- arch/arm/mach-vexpress/Kconfig | 4 ++-- drivers/bus/Kconfig | 28 ++++++++++++++++++++++++---- drivers/bus/arm-cci.c | 24 ++++++++++++++++++++---- include/linux/arm-cci.h | 7 ++++++- 5 files changed, 53 insertions(+), 12 deletions(-) (limited to 'arch') diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 603820e5aba7..81064cd61a0a 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -123,7 +123,7 @@ config SOC_EXYNOS5800 config EXYNOS5420_MCPM bool "Exynos5420 Multi-Cluster PM support" depends on MCPM && SOC_EXYNOS5420 - select ARM_CCI + select ARM_CCI400_PORT_CTRL select ARM_CPU_SUSPEND help This is needed to provide CPU and cluster power management diff --git a/arch/arm/mach-vexpress/Kconfig b/arch/arm/mach-vexpress/Kconfig index 3c2509b4b694..daa7ab6cb909 100644 --- a/arch/arm/mach-vexpress/Kconfig +++ b/arch/arm/mach-vexpress/Kconfig @@ -53,7 +53,7 @@ config ARCH_VEXPRESS_CORTEX_A5_A9_ERRATA config ARCH_VEXPRESS_DCSCB bool "Dual Cluster System Control Block (DCSCB) support" depends on MCPM - select ARM_CCI + select ARM_CCI400_PORT_CTRL help Support for the Dual Cluster System Configuration Block (DCSCB). This is needed to provide CPU and cluster power management @@ -71,7 +71,7 @@ config ARCH_VEXPRESS_SPC config ARCH_VEXPRESS_TC2_PM bool "Versatile Express TC2 power management" depends on MCPM - select ARM_CCI + select ARM_CCI400_PORT_CTRL select ARCH_VEXPRESS_SPC select ARM_CPU_SUSPEND help diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index b99729e36860..79e297b1f221 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -43,12 +43,32 @@ config OMAP_INTERCONNECT help Driver to enable OMAP interconnect error handling driver. -config ARM_CCI - bool "ARM CCI driver support" +config ARM_CCI400_PORT_CTRL + bool depends on ARM && OF && CPU_V7 + select ARM_CCI400_COMMON + help + Low level power management driver for CCI400 cache coherent + interconnect for ARM platforms. + +config ARM_CCI400_PMU + bool "ARM CCI400 PMU support" + default y + depends on ARM || ARM64 + depends on HW_PERF_EVENTS + select ARM_CCI400_COMMON help - Driver supporting the CCI cache coherent interconnect for ARM - platforms. + Support for PMU events monitoring on the ARM CCI cache coherent + interconnect. + + If unsure, say Y + +config ARM_CCI400_COMMON + bool + select ARM_CCI + +config ARM_CCI + bool config ARM_CCN bool "ARM CCN driver support" diff --git a/drivers/bus/arm-cci.c b/drivers/bus/arm-cci.c index a23663c7a306..054df84562c2 100644 --- a/drivers/bus/arm-cci.c +++ b/drivers/bus/arm-cci.c @@ -32,6 +32,7 @@ static void __iomem *cci_ctrl_base; static unsigned long cci_ctrl_phys; +#ifdef CONFIG_ARM_CCI400_PORT_CTRL struct cci_nb_ports { unsigned int nb_ace; unsigned int nb_ace_lite; @@ -42,12 +43,19 @@ static const struct cci_nb_ports cci400_ports = { .nb_ace_lite = 3 }; +#define CCI400_PORTS_DATA (&cci400_ports) +#else +#define CCI400_PORTS_DATA (NULL) +#endif + static const struct of_device_id arm_cci_matches[] = { - {.compatible = "arm,cci-400", .data = &cci400_ports }, +#ifdef CONFIG_ARM_CCI400_COMMON + {.compatible = "arm,cci-400", .data = CCI400_PORTS_DATA }, +#endif {}, }; -#ifdef CONFIG_HW_PERF_EVENTS +#ifdef CONFIG_ARM_CCI400_PMU #define DRIVER_NAME "CCI-400" #define DRIVER_NAME_PMU DRIVER_NAME " PMU" @@ -1022,14 +1030,16 @@ static int __init cci_platform_init(void) return platform_driver_register(&cci_platform_driver); } -#else /* !CONFIG_HW_PERF_EVENTS */ +#else /* !CONFIG_ARM_CCI400_PMU */ static int __init cci_platform_init(void) { return 0; } -#endif /* CONFIG_HW_PERF_EVENTS */ +#endif /* CONFIG_ARM_CCI400_PMU */ + +#ifdef CONFIG_ARM_CCI400_PORT_CTRL #define CCI_PORT_CTRL 0x0 #define CCI_CTRL_STATUS 0xc @@ -1460,6 +1470,12 @@ static int cci_probe_ports(struct device_node *np) return 0; } +#else /* !CONFIG_ARM_CCI400_PORT_CTRL */ +static inline int cci_probe_ports(struct device_node *np) +{ + return 0; +} +#endif /* CONFIG_ARM_CCI400_PORT_CTRL */ static int cci_probe(void) { diff --git a/include/linux/arm-cci.h b/include/linux/arm-cci.h index aede5c765eec..521ec1f2e6bc 100644 --- a/include/linux/arm-cci.h +++ b/include/linux/arm-cci.h @@ -30,12 +30,16 @@ struct device_node; #ifdef CONFIG_ARM_CCI extern bool cci_probed(void); +#else +static inline bool cci_probed(void) { return false; } +#endif + +#ifdef CONFIG_ARM_CCI400_PORT_CTRL extern int cci_ace_get_port(struct device_node *dn); extern int cci_disable_port_by_cpu(u64 mpidr); extern int __cci_control_port_by_device(struct device_node *dn, bool enable); extern int __cci_control_port_by_index(u32 port, bool enable); #else -static inline bool cci_probed(void) { return false; } static inline int cci_ace_get_port(struct device_node *dn) { return -ENODEV; @@ -51,6 +55,7 @@ static inline int __cci_control_port_by_index(u32 port, bool enable) return -ENODEV; } #endif + #define cci_disable_port_by_device(dev) \ __cci_control_port_by_device(dev, false) #define cci_enable_port_by_device(dev) \ -- cgit v1.2.3