From 13a7b53d55185c440598d58c4f1341100082737c Mon Sep 17 00:00:00 2001 From: zador-blood-stained Date: Wed, 23 Aug 2017 18:27:58 +0300 Subject: [PATCH] Add A64 dev DVFS and THS --- config/kernel/linux-sun50i-dev.config | 7 +- .../atf/atf-sun50iw1/01-add-SMC-mailbox.patch | 238 +++++++ .../02-add-handler-boilerplate.patch | 193 ++++++ .../atf-sun50iw1/03-add-clock-handling.patch | 208 ++++++ .../atf-sun50iw1/04-implement-CPU-clock.patch | 140 ++++ .../05-allow-setting-CPU-voltage.patch | 78 +++ .../atf-sun50iw1/06-refactor-poweroff.patch | 34 + .../atf-sun50iw1/07-refactor-power-code.patch | 648 ++++++++++++++++++ patch/atf/atf-sun50iw1/08-add-DVFS.patch | 195 ++++++ .../atf/atf-sun50iw1/09-add-THS-sensor.patch | 317 +++++++++ .../10-add-device-power-state.patch | 233 +++++++ .../add-SRAM-mapping-for-SCPI.patch | 29 + .../set-lpddr3-dram-voltage.patch | 28 +- .../set-lpddr3-dram-voltage.patch | 28 +- .../enable-a53-errata-workaround.patch | 28 + .../add-smc-mailbox-a64-DT-01.patch | 50 ++ .../add-smc-mailbox-a64-DT-02.patch | 67 ++ .../add-smc-mailbox-a64-DT-03.patch | 40 ++ .../sun50i-dev/add-smc-mailbox-docs.patch | 78 +++ .../sun50i-dev/add-smc-mailbox-driver.patch | 225 ++++++ 20 files changed, 2826 insertions(+), 38 deletions(-) create mode 100644 patch/atf/atf-sun50iw1/01-add-SMC-mailbox.patch create mode 100644 patch/atf/atf-sun50iw1/02-add-handler-boilerplate.patch create mode 100644 patch/atf/atf-sun50iw1/03-add-clock-handling.patch create mode 100644 patch/atf/atf-sun50iw1/04-implement-CPU-clock.patch create mode 100644 patch/atf/atf-sun50iw1/05-allow-setting-CPU-voltage.patch create mode 100644 patch/atf/atf-sun50iw1/06-refactor-poweroff.patch create mode 100644 patch/atf/atf-sun50iw1/07-refactor-power-code.patch create mode 100644 patch/atf/atf-sun50iw1/08-add-DVFS.patch create mode 100644 patch/atf/atf-sun50iw1/09-add-THS-sensor.patch create mode 100644 patch/atf/atf-sun50iw1/10-add-device-power-state.patch create mode 100644 patch/atf/atf-sun50iw1/add-SRAM-mapping-for-SCPI.patch create mode 100644 patch/atf/atf-sun50iw1/enable-a53-errata-workaround.patch create mode 100644 patch/kernel/sun50i-dev/add-smc-mailbox-a64-DT-01.patch create mode 100644 patch/kernel/sun50i-dev/add-smc-mailbox-a64-DT-02.patch create mode 100644 patch/kernel/sun50i-dev/add-smc-mailbox-a64-DT-03.patch create mode 100644 patch/kernel/sun50i-dev/add-smc-mailbox-docs.patch create mode 100644 patch/kernel/sun50i-dev/add-smc-mailbox-driver.patch diff --git a/config/kernel/linux-sun50i-dev.config b/config/kernel/linux-sun50i-dev.config index 9a61915555..e8779234d7 100644 --- a/config/kernel/linux-sun50i-dev.config +++ b/config/kernel/linux-sun50i-dev.config @@ -619,9 +619,11 @@ CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y # CONFIG_CPUFREQ_DT=y CONFIG_CPUFREQ_DT_PLATDEV=y -# CONFIG_ARM_BIG_LITTLE_CPUFREQ is not set +CONFIG_ARM_BIG_LITTLE_CPUFREQ=y +# CONFIG_ARM_DT_BL_CPUFREQ is not set # CONFIG_ARM_DB8500_CPUFREQ is not set # CONFIG_ARM_KIRKWOOD_CPUFREQ is not set +CONFIG_ARM_SCPI_CPUFREQ=y # CONFIG_QORIQ_CPUFREQ is not set CONFIG_NET=y CONFIG_COMPAT_NETLINK_MESSAGES=y @@ -2596,7 +2598,7 @@ CONFIG_HWMON=y # CONFIG_SENSORS_ADT7470 is not set # CONFIG_SENSORS_ADT7475 is not set # CONFIG_SENSORS_ASC7621 is not set -# CONFIG_SENSORS_ARM_SCPI is not set +CONFIG_SENSORS_ARM_SCPI=y # CONFIG_SENSORS_ASPEED is not set # CONFIG_SENSORS_ATXP1 is not set # CONFIG_SENSORS_DS620 is not set @@ -4278,6 +4280,7 @@ CONFIG_ARM_MHU=y # CONFIG_ALTERA_MBOX is not set # CONFIG_MAILBOX_TEST is not set # CONFIG_BCM_FLEXRM_MBOX is not set +CONFIG_ARM_SMC_MBOX=y CONFIG_IOMMU_API=y CONFIG_IOMMU_SUPPORT=y diff --git a/patch/atf/atf-sun50iw1/01-add-SMC-mailbox.patch b/patch/atf/atf-sun50iw1/01-add-SMC-mailbox.patch new file mode 100644 index 0000000000..3a30a66ad2 --- /dev/null +++ b/patch/atf/atf-sun50iw1/01-add-SMC-mailbox.patch @@ -0,0 +1,238 @@ +From 923ed37b583ad9f78ca36e0bb372e2d397a900af Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Tue, 12 Jul 2016 22:41:52 +0100 +Subject: [PATCH] sunxi: add smc service handler for an SMC mailbox + +The SCPI protocol uses a combination of a shared memory region and a +mailbox to signal requests from the application cores to the management +processor. However the Allwinner mailbox device in the A64 SoC can only +signal requests between the ARM cores and the OpenRISC core, not between +the ARM cores themselves. +As an alternative we can directly trigger SCPI requests by using an +smc instruction to call routines in the runtime code of ARM Trusted +Firmware (similar to what PSCI does). +Add a vendor defined (SiP) runtime service handler, which dispatches +incoming smc calls to the SCPI handler if the function ID matches. + +Signed-off-by: Andre Przywara +--- + plat/sun50iw1p1/bl31_sunxi_setup.c | 2 + + plat/sun50iw1p1/platform.mk | 1 + + plat/sun50iw1p1/sunxi_sip_svc.c | 123 +++++++++++++++++++++++++++++++++++++ + plat/sun50iw1p1/sunxi_sip_svc.h | 53 ++++++++++++++++ + 4 files changed, 179 insertions(+) + create mode 100644 plat/sun50iw1p1/sunxi_sip_svc.c + create mode 100644 plat/sun50iw1p1/sunxi_sip_svc.h + +diff --git a/plat/sun50iw1p1/bl31_sunxi_setup.c b/plat/sun50iw1p1/bl31_sunxi_setup.c +index 3e30814ff..5e71cc589 100644 +--- a/plat/sun50iw1p1/bl31_sunxi_setup.c ++++ b/plat/sun50iw1p1/bl31_sunxi_setup.c +@@ -255,6 +255,8 @@ void bl31_platform_setup(void) + } + + sunxi_setup_clocks(socid); ++ ++ NOTICE("SCPI: installed handler, implementation level: 000000\n"); + } + + /******************************************************************************* +diff --git a/plat/sun50iw1p1/platform.mk b/plat/sun50iw1p1/platform.mk +index b788f81f2..482b11567 100644 +--- a/plat/sun50iw1p1/platform.mk ++++ b/plat/sun50iw1p1/platform.mk +@@ -50,5 +50,6 @@ BL31_SOURCES += drivers/arm/gic/arm_gic.c \ + plat/sun50iw1p1/plat_topology.c \ + plat/sun50iw1p1/aarch64/plat_helpers.S \ + plat/sun50iw1p1/sunxi_clocks.c \ ++ plat/sun50iw1p1/sunxi_sip_svc.c \ + plat/sun50iw1p1/aarch64/sunxi_common.c + +diff --git a/plat/sun50iw1p1/sunxi_sip_svc.c b/plat/sun50iw1p1/sunxi_sip_svc.c +new file mode 100644 +index 000000000..827aa70eb +--- /dev/null ++++ b/plat/sun50iw1p1/sunxi_sip_svc.c +@@ -0,0 +1,123 @@ ++/* ++ * Copyright (c) 2016,2017 ARM Limited and Contributors. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * Redistributions of source code must retain the above copyright notice, this ++ * list of conditions and the following disclaimer. ++ * ++ * Redistributions in binary form must reproduce the above copyright notice, ++ * this list of conditions and the following disclaimer in the documentation ++ * and/or other materials provided with the distribution. ++ * ++ * Neither the name of ARM nor the names of its contributors may be used ++ * to endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++#include ++#include ++#include ++#include ++#include "sunxi_sip_svc.h" ++#include "sunxi_private.h" ++#include ++#include ++ ++/* SiP Service UUID */ ++DEFINE_SVC_UUID(sunxi_sip_svc_uid, ++ 0x06016e09, 0xd859, 0x4c24, 0xbb, 0x9d, ++ 0x18, 0x92, 0xb4, 0x8d, 0xa5, 0x03); ++ ++#pragma weak sunxi_plat_sip_handler ++uint64_t sunxi_plat_sip_handler(uint32_t smc_fid, ++ uint64_t x1, ++ uint64_t x2, ++ uint64_t x3, ++ uint64_t x4, ++ void *cookie, ++ void *handle, ++ uint64_t flags) ++{ ++ ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid); ++ SMC_RET1(handle, SMC_UNK); ++} ++ ++/* ++ * This function handles Allwinner defined SiP Calls */ ++uint64_t sunxi_sip_handler(uint32_t smc_fid, ++ uint64_t x1, ++ uint64_t x2, ++ uint64_t x3, ++ uint64_t x4, ++ void *cookie, ++ void *handle, ++ uint64_t flags) ++{ ++ /* Determine which security state this SMC originated from */ ++ if (!is_caller_non_secure(flags)) ++ SMC_RET1(handle, SMC_UNK); ++ ++ /* SiP SMC service normal world's call */ ++ switch (smc_fid) { ++ case SUNXI_SIP_MBOX_TRIGGER: ++ SMC_RET1(handle, ~0); ++ } ++ ++ return sunxi_plat_sip_handler(smc_fid, x1, x2, x3, x4, ++ cookie, handle, flags); ++} ++ ++/* ++ * This function is responsible for handling all SiP calls from the NS world ++ */ ++uint64_t sip_smc_handler(uint32_t smc_fid, ++ uint64_t x1, ++ uint64_t x2, ++ uint64_t x3, ++ uint64_t x4, ++ void *cookie, ++ void *handle, ++ uint64_t flags) ++{ ++ switch (smc_fid) { ++ case SIP_SVC_CALL_COUNT: ++ /* Return the number of Allwinner SiP Service Calls. */ ++ SMC_RET1(handle, SUNXI_COMMON_SIP_NUM_CALLS); ++ ++ case SIP_SVC_UID: ++ /* Return UID to the caller */ ++ SMC_UUID_RET(handle, sunxi_sip_svc_uid); ++ ++ case SIP_SVC_VERSION: ++ /* Return the version of current implementation */ ++ SMC_RET2(handle, SUNXI_SIP_SVC_VERSION_MAJOR, ++ SUNXI_SIP_SVC_VERSION_MINOR); ++ ++ default: ++ return sunxi_sip_handler(smc_fid, x1, x2, x3, x4, ++ cookie, handle, flags); ++ } ++} ++ ++/* Define a runtime service descriptor for fast SMC calls */ ++DECLARE_RT_SVC( ++ sunxi_sip_svc, ++ OEN_SIP_START, ++ OEN_SIP_END, ++ SMC_TYPE_FAST, ++ NULL, ++ sip_smc_handler ++); +diff --git a/plat/sun50iw1p1/sunxi_sip_svc.h b/plat/sun50iw1p1/sunxi_sip_svc.h +new file mode 100644 +index 000000000..08d52bd7b +--- /dev/null ++++ b/plat/sun50iw1p1/sunxi_sip_svc.h +@@ -0,0 +1,53 @@ ++/* ++ * Copyright (c) 2016,2017 ARM Limited and Contributors. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * Redistributions of source code must retain the above copyright notice, this ++ * list of conditions and the following disclaimer. ++ * ++ * Redistributions in binary form must reproduce the above copyright notice, ++ * this list of conditions and the following disclaimer in the documentation ++ * and/or other materials provided with the distribution. ++ * ++ * Neither the name of ARM nor the names of its contributors may be used ++ * to endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++#ifndef __PLAT_SIP_SVC_H__ ++#define __PLAT_SIP_SVC_H__ ++ ++#include ++ ++/* SMC function IDs for SiP Service queries */ ++#define SIP_SVC_CALL_COUNT 0x8200ff00 ++#define SIP_SVC_UID 0x8200ff01 ++/* 0x8200ff02 is reserved */ ++#define SIP_SVC_VERSION 0x8200ff03 ++ ++/* Allwinner SiP Service Calls version numbers */ ++#define SUNXI_SIP_SVC_VERSION_MAJOR 0x0 ++#define SUNXI_SIP_SVC_VERSION_MINOR 0x1 ++ ++#define SMC_AARCH64_BIT 0x40000000 ++ ++/* Number of Allwinner SiP Calls implemented */ ++#define SUNXI_COMMON_SIP_NUM_CALLS 1 ++ ++/* Allwinner SiP Service Calls function IDs */ ++#define SUNXI_SIP_MBOX_TRIGGER 0x82000001 ++ ++#endif /* __PLAT_SIP_SVC_H__ */ diff --git a/patch/atf/atf-sun50iw1/02-add-handler-boilerplate.patch b/patch/atf/atf-sun50iw1/02-add-handler-boilerplate.patch new file mode 100644 index 0000000000..43ba715acd --- /dev/null +++ b/patch/atf/atf-sun50iw1/02-add-handler-boilerplate.patch @@ -0,0 +1,193 @@ +From dbba58ae9f77912f2d6661e8b06817327f1e3565 Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Mon, 8 Aug 2016 23:45:41 +0100 +Subject: [PATCH] sunxi: SCPI: add handler boilerplate + +Add the basic framework to handle request via the SCPI interface. This +reads and decodes the request found in the shared memory region, +provides the function framework to handle a certain command and takes +care about passing on the return value. +This for just the basic capability call is implemented. + +Signed-off-by: Andre Przywara +--- + plat/sun50iw1p1/bl31_sunxi_setup.c | 2 +- + plat/sun50iw1p1/platform.mk | 1 + + plat/sun50iw1p1/sunxi_private.h | 3 + + plat/sun50iw1p1/sunxi_scpi.c | 110 +++++++++++++++++++++++++++++++++++++ + plat/sun50iw1p1/sunxi_sip_svc.c | 7 ++- + 5 files changed, 120 insertions(+), 3 deletions(-) + create mode 100644 plat/sun50iw1p1/sunxi_scpi.c + +diff --git a/plat/sun50iw1p1/bl31_sunxi_setup.c b/plat/sun50iw1p1/bl31_sunxi_setup.c +index 5e71cc589..9e303f688 100644 +--- a/plat/sun50iw1p1/bl31_sunxi_setup.c ++++ b/plat/sun50iw1p1/bl31_sunxi_setup.c +@@ -256,7 +256,7 @@ void bl31_platform_setup(void) + + sunxi_setup_clocks(socid); + +- NOTICE("SCPI: installed handler, implementation level: 000000\n"); ++ NOTICE("SCPI: installed handler, implementation level: 100000\n"); + } + + /******************************************************************************* +diff --git a/plat/sun50iw1p1/platform.mk b/plat/sun50iw1p1/platform.mk +index 482b11567..188e4e84d 100644 +--- a/plat/sun50iw1p1/platform.mk ++++ b/plat/sun50iw1p1/platform.mk +@@ -51,5 +51,6 @@ BL31_SOURCES += drivers/arm/gic/arm_gic.c \ + plat/sun50iw1p1/aarch64/plat_helpers.S \ + plat/sun50iw1p1/sunxi_clocks.c \ + plat/sun50iw1p1/sunxi_sip_svc.c \ ++ plat/sun50iw1p1/sunxi_scpi.c \ + plat/sun50iw1p1/aarch64/sunxi_common.c + +diff --git a/plat/sun50iw1p1/sunxi_private.h b/plat/sun50iw1p1/sunxi_private.h +index aefa763c7..b07e56e79 100644 +--- a/plat/sun50iw1p1/sunxi_private.h ++++ b/plat/sun50iw1p1/sunxi_private.h +@@ -75,6 +75,9 @@ int sunxi_pmic_write(uint8_t address, uint8_t value); + void udelay(unsigned int delay); + int sunxi_setup_clocks(uint16_t socid); + ++/* Declarations for sunxi_scpi.c */ ++uint32_t sunxi_trigger_scpi(uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4); ++ + /* Gets the SPSR for BL33 entry */ + uint32_t sunxi_get_spsr_for_bl33_entry(int aarch); + +diff --git a/plat/sun50iw1p1/sunxi_scpi.c b/plat/sun50iw1p1/sunxi_scpi.c +new file mode 100644 +index 000000000..b13dfa7d5 +--- /dev/null ++++ b/plat/sun50iw1p1/sunxi_scpi.c +@@ -0,0 +1,110 @@ ++/* ++ * Copyright (c) 2016,2017 ARM Limited and Contributors. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * Redistributions of source code must retain the above copyright notice, this ++ * list of conditions and the following disclaimer. ++ * ++ * Redistributions in binary form must reproduce the above copyright notice, ++ * this list of conditions and the following disclaimer in the documentation ++ * and/or other materials provided with the distribution. ++ * ++ * Neither the name of ARM nor the names of its contributors may be used ++ * to endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "sunxi_def.h" ++#include "sunxi_private.h" ++ ++#define BIT(n) (1U << (n)) ++#define GENMASK(hi, lo) (BIT(hi) - 1 - BIT(lo) + 1) ++ ++#define SCPI_OK 0 ++#define SCPI_E_PARAM 1 ++#define SCPI_E_ALIGN 2 ++#define SCPI_E_SIZE 3 ++#define SCPI_E_HANDLER 4 ++#define SCPI_E_ACCESS 5 ++#define SCPI_E_RANGE 6 ++#define SCPI_E_TIMEOUT 7 ++#define SCPI_E_NOMEM 8 ++#define SCPI_E_PWRSTATE 9 ++#define SCPI_E_SUPPORT 10 ++#define SCPI_E_DEVICE 11 ++#define SCPI_E_BUSY 12 ++ ++#define SCP_CMD_CAPABILITY 0x02 ++ ++#define SCP_CMDS_IMPLEMENTED \ ++ 0 ++ ++/* end of SRAM A1 */ ++#define SUNXI_SCPI_SHMEM_BASE 0x17e00 ++ ++static uint32_t scpi_handle_cmd(int cmd, uint8_t *payload_size, ++ uintptr_t payload_in, uintptr_t payload_out) ++{ ++ switch (cmd) { ++ case SCP_CMD_CAPABILITY: ++ mmio_write_32(payload_out + 0x00, (1U << 16) | (2U << 0)); ++ /* ++ * The SCPI spec says this field holds the payload sizes for ++ * the receive and transmit channel, but the Linux driver ++ * decodes an event version ID from it. ++ * Let's play nice with Linux for now and ignore the spec. ++ * ++ * mmio_write_32(payload_out + 0x04, ++ * ((256 - 1) << 16) | (256 - 1)); ++ */ ++ mmio_write_32(payload_out + 0x04, 1U << 16); ++ ++ mmio_write_32(payload_out + 0x08, 1U << 24); ++ mmio_write_32(payload_out + 0x0c, SCP_CMDS_IMPLEMENTED); ++ mmio_write_32(payload_out + 0x10, 0x0); ++ mmio_write_32(payload_out + 0x14, 0x0); ++ mmio_write_32(payload_out + 0x18, 0x0); ++ *payload_size = 0x1c; ++ return SCPI_OK; ++ } ++ ++ return SCPI_E_SUPPORT; ++} ++ ++uint32_t sunxi_trigger_scpi(uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4) ++{ ++ uint32_t ret; ++ uint64_t scpi_header; ++ uint8_t payload_size; ++ ++ scpi_header = *(uint64_t *)(SUNXI_SCPI_SHMEM_BASE + 0x100); ++ payload_size = (scpi_header >> 16) & 0xff; ++ ++ ret = scpi_handle_cmd(scpi_header & 0xff, &payload_size, ++ SUNXI_SCPI_SHMEM_BASE + 0x108, ++ SUNXI_SCPI_SHMEM_BASE + 0x8); ++ ++ mmio_write_32(SUNXI_SCPI_SHMEM_BASE, (scpi_header & 0xffff) | ++ (uint32_t)payload_size << 16); ++ mmio_write_32(SUNXI_SCPI_SHMEM_BASE + 4, ret); ++ ++ return ret; ++} +diff --git a/plat/sun50iw1p1/sunxi_sip_svc.c b/plat/sun50iw1p1/sunxi_sip_svc.c +index 827aa70eb..270b488f4 100644 +--- a/plat/sun50iw1p1/sunxi_sip_svc.c ++++ b/plat/sun50iw1p1/sunxi_sip_svc.c +@@ -72,8 +72,11 @@ uint64_t sunxi_sip_handler(uint32_t smc_fid, + + /* SiP SMC service normal world's call */ + switch (smc_fid) { +- case SUNXI_SIP_MBOX_TRIGGER: +- SMC_RET1(handle, ~0); ++ case SUNXI_SIP_MBOX_TRIGGER: { ++ uint32_t ret = sunxi_trigger_scpi(x1, x2, x3, x4); ++ ++ SMC_RET1(handle, ret); ++ } + } + + return sunxi_plat_sip_handler(smc_fid, x1, x2, x3, x4, diff --git a/patch/atf/atf-sun50iw1/03-add-clock-handling.patch b/patch/atf/atf-sun50iw1/03-add-clock-handling.patch new file mode 100644 index 0000000000..f2c8214221 --- /dev/null +++ b/patch/atf/atf-sun50iw1/03-add-clock-handling.patch @@ -0,0 +1,208 @@ +From 426631883e372b3e95c94a148923396a11afc3ca Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Mon, 8 Aug 2016 23:45:41 +0100 +Subject: [PATCH] sunxi: SCPI: add clock handler framework + +SCPI features an interface to control and query clocks with a variable +frequency. +Implement the SCPI protocol part of that interface and provide a +framework to easily add any specific clock desired. + +Signed-off-by: Andre Przywara +--- + plat/sun50iw1p1/sunxi_clocks.c | 78 +++++++++++++++++++++++++++++++++++++++++ + plat/sun50iw1p1/sunxi_private.h | 8 +++++ + plat/sun50iw1p1/sunxi_scpi.c | 59 ++++++++++++++++++++++++++++++- + 3 files changed, 144 insertions(+), 1 deletion(-) + +diff --git a/plat/sun50iw1p1/sunxi_clocks.c b/plat/sun50iw1p1/sunxi_clocks.c +index ff02bfa14..1c5ec2975 100644 +--- a/plat/sun50iw1p1/sunxi_clocks.c ++++ b/plat/sun50iw1p1/sunxi_clocks.c +@@ -105,3 +105,81 @@ int sunxi_setup_clocks(uint16_t socid) + + return 0; + } ++ ++struct scpi_clock { ++ uint32_t min_freq; ++ uint32_t max_freq; ++ uint32_t (*getter)(uint32_t); ++ uint32_t (*setter)(uint32_t, uint32_t); ++ uint32_t reg_addr; ++ const char *name; ++ uint16_t clockid; ++}; ++ ++struct scpi_clock sunxi_clocks[] = { ++}; ++ ++#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) ++ ++static struct scpi_clock *get_sunxi_clock(int clocknr) ++{ ++ if (clocknr < 0 || clocknr >= ARRAY_SIZE(sunxi_clocks)) ++ return NULL; ++ ++ return &sunxi_clocks[clocknr]; ++} ++ ++uint32_t sunxi_clock_get_min_rate(int clocknr) ++{ ++ struct scpi_clock *clk = get_sunxi_clock(clocknr); ++ ++ if (!clk) ++ return ~0; ++ ++ return clk->min_freq; ++} ++ ++uint32_t sunxi_clock_get_max_rate(int clocknr) ++{ ++ struct scpi_clock *clk = get_sunxi_clock(clocknr); ++ ++ if (!clk) ++ return ~0; ++ ++ return clk->max_freq; ++} ++ ++const char* sunxi_clock_get_name(int clocknr) ++{ ++ struct scpi_clock *clk = get_sunxi_clock(clocknr); ++ ++ if (!clk) ++ return NULL; ++ ++ return clk->name; ++} ++ ++uint32_t sunxi_clock_get_rate(int clocknr) ++{ ++ struct scpi_clock *clk = get_sunxi_clock(clocknr); ++ ++ if (!clk) ++ return ~0; ++ ++ return clk->getter(clk->reg_addr); ++} ++ ++int sunxi_clock_set_rate(int clocknr, uint32_t freq) ++{ ++ struct scpi_clock *clk = get_sunxi_clock(clocknr); ++ ++ if (!clk) ++ return ~0; ++ ++ return clk->setter(clk->reg_addr, freq); ++} ++ ++int sunxi_clock_nr_clocks(void) ++{ ++ return ARRAY_SIZE(sunxi_clocks); ++} +diff --git a/plat/sun50iw1p1/sunxi_private.h b/plat/sun50iw1p1/sunxi_private.h +index b07e56e79..08aeb94da 100644 +--- a/plat/sun50iw1p1/sunxi_private.h ++++ b/plat/sun50iw1p1/sunxi_private.h +@@ -78,6 +78,14 @@ int sunxi_setup_clocks(uint16_t socid); + /* Declarations for sunxi_scpi.c */ + uint32_t sunxi_trigger_scpi(uint32_t x1, uint32_t x2, uint32_t x3, uint32_t x4); + ++/* Declarations for sunxi_clocks.c */ ++int sunxi_clock_nr_clocks(void); ++uint32_t sunxi_clock_get_min_rate(int clocknr); ++uint32_t sunxi_clock_get_max_rate(int clocknr); ++const char* sunxi_clock_get_name(int clocknr); ++uint32_t sunxi_clock_get_rate(int clocknr); ++int sunxi_clock_set_rate(int clocknr, uint32_t freq); ++ + /* Gets the SPSR for BL33 entry */ + uint32_t sunxi_get_spsr_for_bl33_entry(int aarch); + +diff --git a/plat/sun50iw1p1/sunxi_scpi.c b/plat/sun50iw1p1/sunxi_scpi.c +index b13dfa7d5..3ca287ed2 100644 +--- a/plat/sun50iw1p1/sunxi_scpi.c ++++ b/plat/sun50iw1p1/sunxi_scpi.c +@@ -53,16 +53,42 @@ + #define SCPI_E_BUSY 12 + + #define SCP_CMD_CAPABILITY 0x02 ++#define SCP_CMD_CLOCKS_CAPS 0x0d ++#define SCP_CMD_CLOCK_GET_INFO 0x0e ++#define SCP_CMD_CLOCK_SET_RATE 0x0f ++#define SCP_CMD_CLOCK_GET_RATE 0x10 + + #define SCP_CMDS_IMPLEMENTED \ +- 0 ++ GENMASK(SCP_CMD_CLOCK_GET_RATE, SCP_CMD_CLOCKS_CAPS) + + /* end of SRAM A1 */ + #define SUNXI_SCPI_SHMEM_BASE 0x17e00 + ++static int write_clock_info(uintptr_t payload, int clocknr) ++{ ++ const char *name, *s; ++ int i; ++ ++ name = sunxi_clock_get_name(clocknr); ++ if (!name) ++ return -SCPI_E_PARAM; ++ ++ mmio_write_32(payload + 0x0, (clocknr & 0xffff) | (0x03 << 16)); ++ mmio_write_32(payload + 0x4, sunxi_clock_get_min_rate(clocknr)); ++ mmio_write_32(payload + 0x8, sunxi_clock_get_max_rate(clocknr)); ++ for (i = 0, s = name; s[i] != 0; i++) ++ mmio_write_8(payload + 12 + i, s[i]); ++ mmio_write_8(payload + 12 + i, 0); ++ ++ return 12 + i; ++} ++ + static uint32_t scpi_handle_cmd(int cmd, uint8_t *payload_size, + uintptr_t payload_in, uintptr_t payload_out) + { ++ uint32_t par1 = mmio_read_32(payload_in); ++ uint32_t ret; ++ + switch (cmd) { + case SCP_CMD_CAPABILITY: + mmio_write_32(payload_out + 0x00, (1U << 16) | (2U << 0)); +@@ -84,6 +110,37 @@ static uint32_t scpi_handle_cmd(int cmd, uint8_t *payload_size, + mmio_write_32(payload_out + 0x18, 0x0); + *payload_size = 0x1c; + return SCPI_OK; ++ case SCP_CMD_CLOCKS_CAPS: ++ /* number of implemented clocks */ ++ mmio_write_32(payload_out, sunxi_clock_nr_clocks()); ++ *payload_size = 0x4; ++ return SCPI_OK; ++ case SCP_CMD_CLOCK_GET_INFO: ++ ret = write_clock_info(payload_out, par1 & 0xffff); ++ if (ret < 0) { ++ *payload_size = 0; ++ return SCPI_E_PARAM; ++ } ++ ++ *payload_size = ret; ++ return SCPI_OK; ++ case SCP_CMD_CLOCK_SET_RATE: { ++ uint32_t freq = mmio_read_32(payload_in + 4); ++ ++ ret = sunxi_clock_set_rate(par1 & 0xffff, freq); ++ if (ret < 0) ++ return SCPI_E_RANGE; ++ *payload_size = 0; ++ return SCPI_OK; ++ } ++ case SCP_CMD_CLOCK_GET_RATE: ++ ret = sunxi_clock_get_rate(par1 & 0xffff); ++ if (ret == ~0) ++ return SCPI_E_RANGE; ++ ++ mmio_write_32(payload_out, ret); ++ *payload_size = 4; ++ return 0; + } + + return SCPI_E_SUPPORT; diff --git a/patch/atf/atf-sun50iw1/04-implement-CPU-clock.patch b/patch/atf/atf-sun50iw1/04-implement-CPU-clock.patch new file mode 100644 index 0000000000..95e246ba8e --- /dev/null +++ b/patch/atf/atf-sun50iw1/04-implement-CPU-clock.patch @@ -0,0 +1,140 @@ +From a641a3a8523759cc73a79d062d934949620ba72f Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Thu, 22 Jun 2017 02:17:40 +0100 +Subject: [PATCH] sunxi: clocks: implement CPU clock and export as an SCPI + clock + +Add functions to read and set the frequency of the (single) CPU clock, +which is done via a register controlling the CPUX PLL. +This replaces the hardcoded reset value for the safe 816 MHz by a call +to that function. +Also export the CPU clock via the SCPI interface, so that any OS can +easily change that clock. + +Signed-off-by: Andre Przywara +--- + plat/sun50iw1p1/bl31_sunxi_setup.c | 2 +- + plat/sun50iw1p1/sunxi_clocks.c | 61 ++++++++++++++++++++++++++++++++++---- + plat/sun50iw1p1/sunxi_private.h | 2 ++ + 3 files changed, 59 insertions(+), 6 deletions(-) + +diff --git a/plat/sun50iw1p1/bl31_sunxi_setup.c b/plat/sun50iw1p1/bl31_sunxi_setup.c +index 9e303f688..dc9b3435a 100644 +--- a/plat/sun50iw1p1/bl31_sunxi_setup.c ++++ b/plat/sun50iw1p1/bl31_sunxi_setup.c +@@ -256,7 +256,7 @@ void bl31_platform_setup(void) + + sunxi_setup_clocks(socid); + +- NOTICE("SCPI: installed handler, implementation level: 100000\n"); ++ NOTICE("SCPI: installed handler, implementation level: 101000\n"); + } + + /******************************************************************************* +diff --git a/plat/sun50iw1p1/sunxi_clocks.c b/plat/sun50iw1p1/sunxi_clocks.c +index 1c5ec2975..21e3b7cf8 100644 +--- a/plat/sun50iw1p1/sunxi_clocks.c ++++ b/plat/sun50iw1p1/sunxi_clocks.c +@@ -33,9 +33,10 @@ + #include + #include "sunxi_private.h" + +-#define PLL_CPUX_1008MHZ 0x1410 +-#define PLL_CPUX_816MHZ 0x1010 +-#define PLL_CPUX_408MHZ 0x1000 ++#define INITIAL_CPU_FREQ 816 ++ ++#define MHz(f) ((f) * 1000000) ++#define inMHz(mhzf) ((mhzf) / 1000000) + + static void mmio_clrsetbits32(uintptr_t addr, uint32_t mask, uint32_t bits) + { +@@ -64,6 +65,27 @@ static int pll_wait_until_stable(uintptr_t addr) + return 0; + } + ++int sunxi_clock_set_cpu_clock(uint32_t freq_mhz, int enable) ++{ ++ int n, k = 1, m = 1, factor; ++ uint32_t reg; ++ ++ factor = freq_mhz / 24; ++ if (factor < 10 || factor > 88) ++ return -1; ++ ++ for (n = factor; n > 33 && k < 5; ++k, n = factor / k) ++ ; ++ ++ reg = (m - 1) | ((k - 1) << 4) | ((n - 1) << 8); ++ if (enable) ++ reg |= PLL_ENABLE_BIT; ++ ++ mmio_write_32(CCMU_PLL_CPUX_CTRL_REG, reg); ++ ++ return 24 * n * k / m; ++} ++ + int sunxi_setup_clocks(uint16_t socid) + { + uint32_t reg; +@@ -80,8 +102,8 @@ int sunxi_setup_clocks(uint16_t socid) + AXI_CLKDIV(3) )); + udelay(20); + +- /* Set to 816MHz, but don't enable yet. */ +- mmio_write_32(CCMU_PLL_CPUX_CTRL_REG, PLL_CPUX_816MHZ); ++ /* Setup the clock parameters, but don't enable yet. */ ++ sunxi_clock_set_cpu_clock(INITIAL_CPU_FREQ, 0); + + /* Enable PLL_CPUX again */ + mmio_setbits32(CCMU_PLL_CPUX_CTRL_REG, PLL_ENABLE_BIT); +@@ -116,7 +138,36 @@ struct scpi_clock { + uint16_t clockid; + }; + ++static uint32_t set_cpu_clk_rate(uint32_t reg_addr, uint32_t freq) ++{ ++ return sunxi_clock_set_cpu_clock(inMHz(freq), 1); ++} ++ ++static uint32_t get_cpu_clk_rate(uint32_t reg_addr) ++{ ++ uint32_t clkreg = mmio_read_32(reg_addr); ++ int n, k, m, p; ++ ++ if (!(clkreg & PLL_ENABLE_BIT)) ++ return 0; ++ ++ n = ((clkreg >> 8) & 0x1f) + 1; ++ k = ((clkreg >> 4) & 0x03) + 1; ++ m = ((clkreg >> 0) & 0x03) + 1; ++ p = 1 << ((clkreg >> 16) & 0x3); ++ ++ return MHz(24) * n * k / (m * p); ++} ++ ++#define CPU_CLK_DESC \ ++ {.min_freq = MHz(240), .max_freq= MHz(1536), \ ++ .getter = get_cpu_clk_rate, .setter = set_cpu_clk_rate, \ ++ .reg_addr = CCMU_PLL_CPUX_CTRL_REG, \ ++ .name = "cpu_clk", \ ++ .clockid = 0 } ++ + struct scpi_clock sunxi_clocks[] = { ++ CPU_CLK_DESC, + }; + + #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +diff --git a/plat/sun50iw1p1/sunxi_private.h b/plat/sun50iw1p1/sunxi_private.h +index 08aeb94da..519422343 100644 +--- a/plat/sun50iw1p1/sunxi_private.h ++++ b/plat/sun50iw1p1/sunxi_private.h +@@ -86,6 +86,8 @@ const char* sunxi_clock_get_name(int clocknr); + uint32_t sunxi_clock_get_rate(int clocknr); + int sunxi_clock_set_rate(int clocknr, uint32_t freq); + ++int sunxi_clock_set_cpu_clock(uint32_t freq_mhz, int enable); ++ + /* Gets the SPSR for BL33 entry */ + uint32_t sunxi_get_spsr_for_bl33_entry(int aarch); + diff --git a/patch/atf/atf-sun50iw1/05-allow-setting-CPU-voltage.patch b/patch/atf/atf-sun50iw1/05-allow-setting-CPU-voltage.patch new file mode 100644 index 0000000000..8a52235c33 --- /dev/null +++ b/patch/atf/atf-sun50iw1/05-allow-setting-CPU-voltage.patch @@ -0,0 +1,78 @@ +From 8d1687f42bf86fe8197950b86a50936e28054534 Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Sat, 10 Dec 2016 12:21:38 +0000 +Subject: [PATCH] sunxi: power: allow setting CPU voltage + +Provide high level functions to allow programming certain voltages for +the CPU cores. This calculates the respective AXP803 register value for +the DCDC2/DCDC3 pair from a given voltage. +Also this allow to turn the CPU cores off completely, to easily +implement a shutdown functionality. + +Signed-off-by: Andre Przywara +--- + plat/sun50iw1p1/sunxi_power.c | 30 ++++++++++++++++++++++++++++++ + plat/sun50iw1p1/sunxi_private.h | 2 ++ + 2 files changed, 32 insertions(+) + +diff --git a/plat/sun50iw1p1/sunxi_power.c b/plat/sun50iw1p1/sunxi_power.c +index 0c2487e20..095a0e757 100644 +--- a/plat/sun50iw1p1/sunxi_power.c ++++ b/plat/sun50iw1p1/sunxi_power.c +@@ -176,6 +176,31 @@ static void rsb_wait(const char *desc) + ERROR("%s: 0x%x\n", desc, reg); + } + ++static int axp803_set_cpu_voltage(int millivolt) ++{ ++ uint8_t reg; ++ ++ if (millivolt <= 0) { /* power off system */ ++ sunxi_pmic_write(0x32, sunxi_pmic_read(0x32) | 0x80); ++ return 0; /* hopefully not ... */ ++ } ++ ++ if (millivolt < 800 || millivolt > 1300) ++ return -1; ++ ++ if (millivolt > 1200) ++ reg = (millivolt - 1200) / 20 + 70; ++ else ++ reg = (millivolt - 500) / 10 + 0; ++ ++ sunxi_pmic_write(0x21, reg); /* DCDC2 */ ++ ++ while (!(sunxi_pmic_read(0x21) & 0x80)) ++ ; ++ ++ return 0; ++} ++ + /* Initialize the RSB PMIC connection. */ + static int pmic_init(uint16_t hw_addr, uint8_t rt_addr) + { +@@ -266,6 +291,11 @@ static int pmic_setup(void) + + sunxi_pmic_write(0x15, 0x1a); /* DLDO1 = VCC3V3_HDMI voltage = 3.3V */ + ++ ret = sunxi_rsb_read(0x14); ++ sunxi_rsb_write(0x14, ret | 0x40); /* DCDC2/3 dual phase */ ++ ++ axp803_set_cpu_voltage(1100); ++ + return 0; + } + +diff --git a/plat/sun50iw1p1/sunxi_private.h b/plat/sun50iw1p1/sunxi_private.h +index 519422343..0c8b640bd 100644 +--- a/plat/sun50iw1p1/sunxi_private.h ++++ b/plat/sun50iw1p1/sunxi_private.h +@@ -72,6 +72,8 @@ int sunxi_pmic_setup(void); + int sunxi_pmic_read(uint8_t address); + int sunxi_pmic_write(uint8_t address, uint8_t value); + ++int sunxi_power_set_cpu_voltage(int millivolt); ++ + void udelay(unsigned int delay); + int sunxi_setup_clocks(uint16_t socid); + diff --git a/patch/atf/atf-sun50iw1/06-refactor-poweroff.patch b/patch/atf/atf-sun50iw1/06-refactor-poweroff.patch new file mode 100644 index 0000000000..eccbe13851 --- /dev/null +++ b/patch/atf/atf-sun50iw1/06-refactor-poweroff.patch @@ -0,0 +1,34 @@ +From b3272daf1df2eb8b96654d2884eab391f85549ea Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Fri, 16 Dec 2016 01:32:36 +0000 +Subject: [PATCH] sunxi: power: move system power off into set_voltage() + function + +Instead of directly poking the appropriate AXP PMIC register to turn the +voltage for the ARM CPU cores off completely, call the newly exported +function to set the CPU voltage with a negative argument. +This abstracts the power off functionality from the actual PMIC used. + +Signed-off-by: Andre Przywara +--- + plat/sun50iw1p1/plat_pm.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/plat/sun50iw1p1/plat_pm.c b/plat/sun50iw1p1/plat_pm.c +index ec26248e0..c2ef5a6b7 100644 +--- a/plat/sun50iw1p1/plat_pm.c ++++ b/plat/sun50iw1p1/plat_pm.c +@@ -254,8 +254,11 @@ static int32_t sunxi_affinst_suspend_finish(uint64_t mpidr, + ******************************************************************************/ + static void __dead2 sunxi_system_off(void) + { +- sunxi_pmic_write(0x32, sunxi_pmic_read(0x32) | 0x80); +- ERROR("PSCI system shutdown: still alive ...\n"); ++ int ret; ++ ++ ret = sunxi_power_set_cpu_voltage(-1); ++ ++ ERROR("PSCI system shutdown: %d: still alive ...\n", ret); + + wfi(); + panic(); diff --git a/patch/atf/atf-sun50iw1/07-refactor-power-code.patch b/patch/atf/atf-sun50iw1/07-refactor-power-code.patch new file mode 100644 index 0000000000..5e17504fa4 --- /dev/null +++ b/patch/atf/atf-sun50iw1/07-refactor-power-code.patch @@ -0,0 +1,648 @@ +From 7f15fa4721edf35fa3eb6494c9518379eea4604d Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Sun, 19 Mar 2017 01:02:19 +0000 +Subject: [PATCH] sunxi: power: refactor power specific code into extra file + +At the moment the power code mixes the code to access the RSB bus with +the actual PMIC register control and setup via this bus. +Separate those two to make the code more readable, also to provide +better abstraction. +This for instance later allows using I2C to control different PMICs, +for instance. + +Signed-off-by: Andre Przywara +--- + plat/sun50iw1p1/bl31_sunxi_setup.c | 8 +- + plat/sun50iw1p1/platform.mk | 1 + + plat/sun50iw1p1/sunxi_power.c | 278 +++++++++++-------------------------- + plat/sun50iw1p1/sunxi_private.h | 9 +- + plat/sun50iw1p1/sunxi_rsb.c | 198 ++++++++++++++++++++++++++ + 5 files changed, 286 insertions(+), 208 deletions(-) + create mode 100644 plat/sun50iw1p1/sunxi_rsb.c + +diff --git a/plat/sun50iw1p1/bl31_sunxi_setup.c b/plat/sun50iw1p1/bl31_sunxi_setup.c +index dc9b3435a..7c1109f4e 100644 +--- a/plat/sun50iw1p1/bl31_sunxi_setup.c ++++ b/plat/sun50iw1p1/bl31_sunxi_setup.c +@@ -246,13 +246,7 @@ void bl31_platform_setup(void) + /* Detect if this SoC is a multi-cluster one. */ + plat_setup_topology(); + +- switch (socid) { +- case 0x1689: +- sunxi_pmic_setup(); +- break; +- case 0x1718: +- break; +- } ++ sunxi_power_setup(socid); + + sunxi_setup_clocks(socid); + +diff --git a/plat/sun50iw1p1/platform.mk b/plat/sun50iw1p1/platform.mk +index 188e4e84d..95cff562f 100644 +--- a/plat/sun50iw1p1/platform.mk ++++ b/plat/sun50iw1p1/platform.mk +@@ -52,5 +52,6 @@ BL31_SOURCES += drivers/arm/gic/arm_gic.c \ + plat/sun50iw1p1/sunxi_clocks.c \ + plat/sun50iw1p1/sunxi_sip_svc.c \ + plat/sun50iw1p1/sunxi_scpi.c \ ++ plat/sun50iw1p1/sunxi_rsb.c \ + plat/sun50iw1p1/aarch64/sunxi_common.c + +diff --git a/plat/sun50iw1p1/sunxi_power.c b/plat/sun50iw1p1/sunxi_power.c +index 095a0e757..5aa63fe4f 100644 +--- a/plat/sun50iw1p1/sunxi_power.c ++++ b/plat/sun50iw1p1/sunxi_power.c +@@ -35,153 +35,17 @@ + #include "sunxi_def.h" + #include "sunxi_private.h" + +-#define R_PRCM_BASE 0x1f01400ULL +-#define R_TWI_BASE 0x1f02400ULL +-#define R_PIO_BASE 0x1f02c00ULL +- +-#define RSB_BASE 0x1f03400ULL +-#define RSB_CTRL 0x00 +-#define RSB_CCR 0x04 +-#define RSB_INTE 0x08 +-#define RSB_STAT 0x0c +-#define RSB_DADDR0 0x10 +-#define RSB_DLEN 0x18 +-#define RSB_DATA0 0x1c +-#define RSB_LCR 0x24 +-#define RSB_PMCR 0x28 +-#define RSB_CMD 0x2c +-#define RSB_SADDR 0x30 +- +-#define RSBCMD_SRTA 0xE8 +-#define RSBCMD_RD8 0x8B +-#define RSBCMD_RD16 0x9C +-#define RSBCMD_RD32 0xA6 +-#define RSBCMD_WR8 0x4E +-#define RSBCMD_WR16 0x59 +-#define RSBCMD_WR32 0x63 +- + #define BIT(n) (1U << (n)) + + #define RUNTIME_ADDR 0x2d + #define AXP803_HW_ADDR 0x3a3 + +-/* Initialize the RSB controller and its pins. */ +-static int init_rsb(void) +-{ +- uint32_t reg; +- +- /* un-gate PIO clock */ +- reg = mmio_read_32(R_PRCM_BASE + 0x28); +- mmio_write_32(R_PRCM_BASE + 0x28, reg | 0x01); +- +- /* get currently configured function for pins PL0 and PL1 */ +- reg = mmio_read_32(R_PIO_BASE + 0x00); +- if ((reg & 0xff) == 0x33) { +- NOTICE("already configured for TWI\n"); +- return -EBUSY; +- } +- +- if ((reg & 0xff) == 0x22) { +- NOTICE("PMIC: already configured for RSB\n"); +- return -EEXIST; /* configured for RSB mode already */ +- } +- +- /* switch pins PL0 and PL1 to RSB */ +- mmio_write_32(R_PIO_BASE + 0, (reg & ~0xff) | 0x22); +- +- /* level 2 drive strength */ +- reg = mmio_read_32(R_PIO_BASE + 0x14); +- mmio_write_32(R_PIO_BASE + 0x14, (reg & ~0x0f) | 0xa); +- +- /* set both ports to pull-up */ +- reg = mmio_read_32(R_PIO_BASE + 0x1c); +- mmio_write_32(R_PIO_BASE + 0x1c, (reg & ~0x0f) | 0x5); +- +- /* assert & de-assert reset of RSB */ +- reg = mmio_read_32(R_PRCM_BASE + 0xb0); +- mmio_write_32(R_PRCM_BASE + 0xb0, reg & ~0x08); +- reg = mmio_read_32(R_PRCM_BASE + 0xb0); +- mmio_write_32(R_PRCM_BASE + 0xb0, reg | 0x08); +- +- /* un-gate RSB clock */ +- reg = mmio_read_32(R_PRCM_BASE + 0x28); +- mmio_write_32(R_PRCM_BASE + 0x28, reg | 0x08); +- +- mmio_write_32(RSB_BASE + RSB_CTRL, 0x01); /* soft reset */ +- +- mmio_write_32(RSB_BASE + RSB_CCR, 0x11d); /* clock to 400 KHz */ +- +- do { +- reg = mmio_read_32(RSB_BASE + RSB_CTRL); +- } while (reg & 1); /* transaction in progress */ +- +- return 0; +-} +- +-int sunxi_pmic_read(uint8_t address) +-{ +- uint32_t reg; +- +- mmio_write_32(RSB_BASE + RSB_DLEN, 0x10); /* read a byte, snake oil? */ +- mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_RD8); /* read a byte */ +- mmio_write_32(RSB_BASE + RSB_DADDR0, address); +- mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); /* start transaction */ +- do { +- reg = mmio_read_32(RSB_BASE + RSB_CTRL); +- } while (reg & 0x80); /* transaction in progress */ +- +- reg = mmio_read_32(RSB_BASE + RSB_STAT); +- if (reg == 0x01) { /* transaction complete */ +- reg = mmio_read_32(RSB_BASE + RSB_DATA0); /* result register */ +- return reg & 0xff; +- } +- +- return -reg; +-} +- +-int sunxi_pmic_write(uint8_t address, uint8_t value) +-{ +- uint32_t reg; +- +- mmio_write_32(RSB_BASE + RSB_DLEN, 0x00); /* write a byte, snake oil? */ +- mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_WR8); /* write a byte */ +- mmio_write_32(RSB_BASE + RSB_DADDR0, address); +- mmio_write_32(RSB_BASE + RSB_DATA0, value); +- mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); /* start transaction */ +- do { +- reg = mmio_read_32(RSB_BASE + RSB_CTRL); +- } while (reg & 0x80); /* transaction in progress */ +- +- reg = mmio_read_32(RSB_BASE + RSB_STAT); +- if (reg == 0x01) /* transaction complete */ +- return 0; +- +- return -reg; +-} +- +-static void rsb_wait(const char *desc) +-{ +- uint32_t reg; +- int cnt = 0; +- +- do { +- reg = mmio_read_32(RSB_BASE + RSB_CTRL); +- cnt++; +- } while (reg & 0x80); /* transaction in progress */ +- +- reg = mmio_read_32(RSB_BASE + RSB_STAT); +- if (reg == 0x01) +- return; +- +- ERROR("%s: 0x%x\n", desc, reg); +-} +- + static int axp803_set_cpu_voltage(int millivolt) + { + uint8_t reg; + + if (millivolt <= 0) { /* power off system */ +- sunxi_pmic_write(0x32, sunxi_pmic_read(0x32) | 0x80); ++ sunxi_rsb_write(0x32, sunxi_rsb_read(0x32) | 0x80); + return 0; /* hopefully not ... */ + } + +@@ -193,56 +57,30 @@ static int axp803_set_cpu_voltage(int millivolt) + else + reg = (millivolt - 500) / 10 + 0; + +- sunxi_pmic_write(0x21, reg); /* DCDC2 */ ++ sunxi_rsb_write(0x21, reg); /* DCDC2 */ + +- while (!(sunxi_pmic_read(0x21) & 0x80)) ++ while (!(sunxi_rsb_read(0x21) & 0x80)) + ; + + return 0; + } + +-/* Initialize the RSB PMIC connection. */ +-static int pmic_init(uint16_t hw_addr, uint8_t rt_addr) +-{ +- int ret; +- +- /* Switch PMIC to RSB mode */ +- mmio_write_32(RSB_BASE + RSB_PMCR, +- 0x00 | (0x3e << 8) | (0x7c << 16) | BIT(31)); +- do { +- ret = mmio_read_32(RSB_BASE + RSB_PMCR); +- } while (ret & (1U << 31)); /* transaction in progress */ +- +- mmio_write_32(RSB_BASE + RSB_CCR, 0x103); /* 3 MHz */ +- +- mmio_write_32(RSB_BASE + RSB_SADDR, hw_addr | (rt_addr << 16)); +- mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_SRTA); +- mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); +- rsb_wait("set run-time address"); +- +- /* Set slave runtime address */ +- mmio_write_32(RSB_BASE + RSB_SADDR, rt_addr << 16); +- +- ret = sunxi_pmic_read(0x03); +- if (ret < 0) { +- ERROR("PMIC: error %d reading PMIC type\n", ret); +- return -2; +- } +- +- if ((ret & 0xcf) != 0x41) { +- ERROR("PMIC: unknown PMIC type number 0x%x\n", ret); +- return -3; +- } +- +- return 0; +-} +- +-/* Setup the PMIC: DCDC1 to 3.3V, enable DC1SW and DLDO4 */ +-static int pmic_setup(void) ++/* ++ * Initial PMIC setup for boards using the AXP803 PMIC. ++ * DCDC1 must be corrected to 3.3 volts. Also we enable: ++ * - DC1SW: Ethernet PHY on most boards ++ * - DLDO1: HDMI power ++ * - DLDO4: WiFi power ++ * Technically those should be enabled by the users (via SCPI), but until ++ * U-Boot learns how to do this we do it here. ++ * Also this contains a quirk to fix the DRAM voltage on Pine64 boards, ++ * which have a wrong default (1.24V instead of 1.36V). ++ */ ++static int axp803_initial_setup(void) + { + int ret; + +- ret = sunxi_pmic_read(0x20); ++ ret = sunxi_rsb_read(0x20); + if (ret != 0x0e && ret != 0x11) { + int voltage = (ret & 0x1f) * 10 + 16; + +@@ -253,14 +91,14 @@ static int pmic_setup(void) + + if (ret != 0x11) { + /* Set DCDC1 voltage to 3.3 Volts */ +- ret = sunxi_pmic_write(0x20, 0x11); ++ ret = sunxi_rsb_write(0x20, 0x11); + if (ret < 0) { + NOTICE("PMIC: error %d writing DCDC1 voltage\n", ret); + return -2; + } + } + +- ret = sunxi_pmic_read(0x12); ++ ret = sunxi_rsb_read(0x12); + if ((ret & 0x37) != 0x01) { + NOTICE("PMIC: Output power control 2 is an unexpected 0x%x\n", + ret); +@@ -268,10 +106,12 @@ static int pmic_setup(void) + } + + if ((ret & 0xc9) != 0xc9) { +- /* Enable DC1SW to power PHY, DLDO4 for WiFi and DLDO1 for HDMI */ +- ret = sunxi_pmic_write(0x12, ret | 0xc8); ++ /* Enable DC1SW to power PHY, DLDO4 for WiFi, DLDO1 for HDMI */ ++ /* TODO: keep WiFi disabled, as not needed in U-Boot? */ ++ ret = sunxi_rsb_write(0x12, ret | 0xc8); + if (ret < 0) { +- NOTICE("PMIC: error %d enabling DC1SW/DLDO4/DLDO1\n", ret); ++ NOTICE("PMIC: error %d enabling DC1SW/DLDO4/DLDO1\n", ++ ret); + return -4; + } + } +@@ -283,13 +123,13 @@ static int pmic_setup(void) + * changes. This should be further confined once we are able to + * reliably detect a Pine64 board. + */ +- ret = sunxi_pmic_read(0x24); /* read DCDC5 register */ ++ ret = sunxi_rsb_read(0x24); /* read DCDC5 register */ + if ((ret & 0x7f) == 0x26) { /* check for 1.24V value */ + NOTICE("PMIC: fixing DRAM voltage from 1.24V to 1.36V\n"); +- sunxi_pmic_write(0x24, 0x2c); ++ sunxi_rsb_write(0x24, 0x2c); + } + +- sunxi_pmic_write(0x15, 0x1a); /* DLDO1 = VCC3V3_HDMI voltage = 3.3V */ ++ sunxi_rsb_write(0x15, 0x1a); /* DLDO1 = VCC3V3_HDMI voltage = 3.3V */ + + ret = sunxi_rsb_read(0x14); + sunxi_rsb_write(0x14, ret | 0x40); /* DCDC2/3 dual phase */ +@@ -302,31 +142,69 @@ static int pmic_setup(void) + /* + * Program the AXP803 via the RSB bus. + */ +-int sunxi_pmic_setup(void) ++static int axp803_probe(void) + { + int ret; + +- NOTICE("Configuring AXP PMIC\n"); +- +- ret = init_rsb(); ++ ret = sunxi_rsb_init(); + if (ret && ret != -EEXIST) { + ERROR("Could not init RSB controller.\n"); + return -1; + } + +- if (ret != -EEXIST) { +- ret = pmic_init(AXP803_HW_ADDR, RUNTIME_ADDR); ++ if (ret == -EEXIST) ++ return ret; ++ ++ ret = sunxi_rsb_configure(AXP803_HW_ADDR, RUNTIME_ADDR); ++ if (ret) { ++ ERROR("Could not configure RSB.\n"); ++ return -2; ++ } ++ ret = sunxi_rsb_read(0x03); ++ if (ret < 0) { ++ ERROR("PMIC: error %d reading PMIC type\n", ret); ++ return -2; ++ } ++ if ((ret & 0xcf) != 0x41) { ++ ERROR("PMIC: unknown PMIC type number 0x%x\n", ret); ++ return -3; ++ } ++ ++ return 0; ++} ++ ++enum pmic_type { ++ PMIC_AXP803, ++} pmic_type; ++ ++int sunxi_power_setup(uint16_t socid) ++{ ++ int ret; ++ ++ switch (socid) { ++ case 0x1689: ++ pmic_type = PMIC_AXP803; ++ ++ NOTICE("PMIC: Probing for AXP803 on A64\n"); ++ ret = axp803_probe(); + if (ret) { +- ERROR("Could not connect to AXP PMIC.\n"); +- return -2; ++ ERROR("PMIC: AXP803 initialization failed: %d\n", ret); ++ return ret; ++ } ++ ret = axp803_initial_setup(); ++ if (ret) { ++ ERROR("PMIC: AXP803 power setup failed: %d\n", ret); ++ return ret; + } ++ NOTICE("PMIC: AXP803 successfully setup\n"); ++ break; ++ case 0x1718: ++ ret = -ENXIO; ++ break; ++ default: ++ NOTICE("power setup not defined for SoC 0x%04x\n", socid); ++ ret = -ENODEV; + } + +- ret = pmic_setup(); +- if (!ret) +- NOTICE("PMIC: setup successful\n"); +- else +- ERROR("PMIC: setup failed: %d\n", ret); +- + return ret; + } +diff --git a/plat/sun50iw1p1/sunxi_private.h b/plat/sun50iw1p1/sunxi_private.h +index 0c8b640bd..07a329598 100644 +--- a/plat/sun50iw1p1/sunxi_private.h ++++ b/plat/sun50iw1p1/sunxi_private.h +@@ -71,7 +71,7 @@ void sunxi_security_setup(void); + int sunxi_pmic_setup(void); + int sunxi_pmic_read(uint8_t address); + int sunxi_pmic_write(uint8_t address, uint8_t value); +- ++int sunxi_power_setup(uint16_t socid); + int sunxi_power_set_cpu_voltage(int millivolt); + + void udelay(unsigned int delay); +@@ -90,6 +90,13 @@ int sunxi_clock_set_rate(int clocknr, uint32_t freq); + + int sunxi_clock_set_cpu_clock(uint32_t freq_mhz, int enable); + ++/* Declarations for sunxi_rsb.c */ ++int sunxi_rsb_init(void); ++int sunxi_rsb_read(uint8_t address); ++int sunxi_rsb_write(uint8_t address, uint8_t value); ++void sunxi_rsb_wait(const char *desc); ++int sunxi_rsb_configure(uint16_t hw_addr, uint8_t rt_addr); ++ + /* Gets the SPSR for BL33 entry */ + uint32_t sunxi_get_spsr_for_bl33_entry(int aarch); + +diff --git a/plat/sun50iw1p1/sunxi_rsb.c b/plat/sun50iw1p1/sunxi_rsb.c +new file mode 100644 +index 000000000..098c8ad0f +--- /dev/null ++++ b/plat/sun50iw1p1/sunxi_rsb.c +@@ -0,0 +1,198 @@ ++/* ++ * Copyright (c) 2017 ARM Limited and Contributors. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * Redistributions of source code must retain the above copyright notice, this ++ * list of conditions and the following disclaimer. ++ * ++ * Redistributions in binary form must reproduce the above copyright notice, ++ * this list of conditions and the following disclaimer in the documentation ++ * and/or other materials provided with the distribution. ++ * ++ * Neither the name of ARM nor the names of its contributors may be used ++ * to endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include "sunxi_def.h" ++#include "sunxi_private.h" ++ ++#define R_PRCM_BASE 0x1f01400ULL ++#define R_TWI_BASE 0x1f02400ULL ++#define R_PIO_BASE 0x1f02c00ULL ++ ++#define RSB_BASE 0x1f03400ULL ++#define RSB_CTRL 0x00 ++#define RSB_CCR 0x04 ++#define RSB_INTE 0x08 ++#define RSB_STAT 0x0c ++#define RSB_DADDR0 0x10 ++#define RSB_DLEN 0x18 ++#define RSB_DATA0 0x1c ++#define RSB_LCR 0x24 ++#define RSB_PMCR 0x28 ++#define RSB_CMD 0x2c ++#define RSB_SADDR 0x30 ++ ++#define RSBCMD_SRTA 0xE8 ++#define RSBCMD_RD8 0x8B ++#define RSBCMD_RD16 0x9C ++#define RSBCMD_RD32 0xA6 ++#define RSBCMD_WR8 0x4E ++#define RSBCMD_WR16 0x59 ++#define RSBCMD_WR32 0x63 ++ ++#define BIT(n) (1U << (n)) ++ ++/* Initialize the RSB controller and its pins. */ ++int sunxi_rsb_init(void) ++{ ++ uint32_t reg; ++ ++ /* un-gate PIO clock */ ++ reg = mmio_read_32(R_PRCM_BASE + 0x28); ++ mmio_write_32(R_PRCM_BASE + 0x28, reg | 0x01); ++ ++ /* get currently configured function for pins PL0 and PL1 */ ++ reg = mmio_read_32(R_PIO_BASE + 0x00); ++ if ((reg & 0xff) == 0x33) { ++ NOTICE("already configured for TWI\n"); ++ return -EBUSY; ++ } ++ ++ if ((reg & 0xff) == 0x22) { ++ NOTICE("PMIC: already configured for RSB\n"); ++ return -EEXIST; /* configured for RSB mode already */ ++ } ++ ++ /* switch pins PL0 and PL1 to RSB */ ++ mmio_write_32(R_PIO_BASE + 0, (reg & ~0xff) | 0x22); ++ ++ /* level 2 drive strength */ ++ reg = mmio_read_32(R_PIO_BASE + 0x14); ++ mmio_write_32(R_PIO_BASE + 0x14, (reg & ~0x0f) | 0xa); ++ ++ /* set both ports to pull-up */ ++ reg = mmio_read_32(R_PIO_BASE + 0x1c); ++ mmio_write_32(R_PIO_BASE + 0x1c, (reg & ~0x0f) | 0x5); ++ ++ /* assert & de-assert reset of RSB */ ++ reg = mmio_read_32(R_PRCM_BASE + 0xb0); ++ mmio_write_32(R_PRCM_BASE + 0xb0, reg & ~0x08); ++ reg = mmio_read_32(R_PRCM_BASE + 0xb0); ++ mmio_write_32(R_PRCM_BASE + 0xb0, reg | 0x08); ++ ++ /* un-gate RSB clock */ ++ reg = mmio_read_32(R_PRCM_BASE + 0x28); ++ mmio_write_32(R_PRCM_BASE + 0x28, reg | 0x08); ++ ++ mmio_write_32(RSB_BASE + RSB_CTRL, 0x01); /* soft reset */ ++ ++ mmio_write_32(RSB_BASE + RSB_CCR, 0x11d); /* clock to 400 KHz */ ++ ++ do { ++ reg = mmio_read_32(RSB_BASE + RSB_CTRL); ++ } while (reg & 1); /* transaction in progress */ ++ ++ return 0; ++} ++ ++int sunxi_rsb_read(uint8_t address) ++{ ++ uint32_t reg; ++ ++ mmio_write_32(RSB_BASE + RSB_DLEN, 0x10); /* read a byte, snake oil? */ ++ mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_RD8); /* read a byte */ ++ mmio_write_32(RSB_BASE + RSB_DADDR0, address); ++ mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); /* start transaction */ ++ do { ++ reg = mmio_read_32(RSB_BASE + RSB_CTRL); ++ } while (reg & 0x80); /* transaction in progress */ ++ ++ reg = mmio_read_32(RSB_BASE + RSB_STAT); ++ if (reg == 0x01) { /* transaction complete */ ++ reg = mmio_read_32(RSB_BASE + RSB_DATA0); /* result register */ ++ return reg & 0xff; ++ } ++ ++ return -reg; ++} ++ ++int sunxi_rsb_write(uint8_t address, uint8_t value) ++{ ++ uint32_t reg; ++ ++ mmio_write_32(RSB_BASE + RSB_DLEN, 0x00); /* write a byte, snake oil? */ ++ mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_WR8); /* write a byte */ ++ mmio_write_32(RSB_BASE + RSB_DADDR0, address); ++ mmio_write_32(RSB_BASE + RSB_DATA0, value); ++ mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); /* start transaction */ ++ do { ++ reg = mmio_read_32(RSB_BASE + RSB_CTRL); ++ } while (reg & 0x80); /* transaction in progress */ ++ ++ reg = mmio_read_32(RSB_BASE + RSB_STAT); ++ if (reg == 0x01) /* transaction complete */ ++ return 0; ++ ++ return -reg; ++} ++ ++void sunxi_rsb_wait(const char *desc) ++{ ++ uint32_t reg; ++ int cnt = 0; ++ ++ do { ++ reg = mmio_read_32(RSB_BASE + RSB_CTRL); ++ cnt++; ++ } while (reg & 0x80); /* transaction in progress */ ++ ++ reg = mmio_read_32(RSB_BASE + RSB_STAT); ++ if (reg == 0x01) ++ return; ++ ++ ERROR("%s: 0x%x\n", desc, reg); ++} ++ ++/* Initialize the RSB PMIC connection. */ ++int sunxi_rsb_configure(uint16_t hw_addr, uint8_t rt_addr) ++{ ++ int ret; ++ ++ mmio_write_32(RSB_BASE + RSB_PMCR, ++ 0x00 | (0x3e << 8) | (0x7c << 16) | BIT(31)); ++ ++ do { ++ ret = mmio_read_32(RSB_BASE + RSB_PMCR); ++ } while (ret & (1U << 31)); /* transaction in progress */ ++ ++ mmio_write_32(RSB_BASE + RSB_CCR, 0x103); /* 3 MHz */ ++ mmio_write_32(RSB_BASE + RSB_SADDR, hw_addr | (rt_addr << 16)); ++ mmio_write_32(RSB_BASE + RSB_CMD, RSBCMD_SRTA); ++ mmio_write_32(RSB_BASE + RSB_CTRL, 0x80); ++ sunxi_rsb_wait("set run-time address"); ++ ++ /* Set slave runtime address */ ++ mmio_write_32(RSB_BASE + RSB_SADDR, rt_addr << 16); ++ ++ return 0; ++} diff --git a/patch/atf/atf-sun50iw1/08-add-DVFS.patch b/patch/atf/atf-sun50iw1/08-add-DVFS.patch new file mode 100644 index 0000000000..86e0889d18 --- /dev/null +++ b/patch/atf/atf-sun50iw1/08-add-DVFS.patch @@ -0,0 +1,195 @@ +From e2b4f43ae7c138f3502e3df4e3928827a169592b Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Sat, 10 Dec 2016 12:22:49 +0000 +Subject: [PATCH] sunxi: SCPI: add DVFS functionality + +DVFS (dynamic voltage and frequency scaling) allows an OS to set certain +CPU operating points described by a pair of frequency and required voltage. +Using the recently introduced wrappers for the CPU voltage and the PLL +frequency implement the required SCPI boilerplate to export those +operating points and allow an OS to choose from a provided list. +The actual frequency/voltage data used here is taken from Allwinner's BSP +code, which seems to provide stable and sensible values. + +Signed-off-by: Andre Przywara +--- + plat/sun50iw1p1/bl31_sunxi_setup.c | 2 +- + plat/sun50iw1p1/platform.mk | 1 + + plat/sun50iw1p1/sunxi_dvfs.c | 65 ++++++++++++++++++++++++++++++++++++++ + plat/sun50iw1p1/sunxi_private.h | 7 ++++ + plat/sun50iw1p1/sunxi_scpi.c | 35 ++++++++++++++++++++ + 5 files changed, 109 insertions(+), 1 deletion(-) + create mode 100644 plat/sun50iw1p1/sunxi_dvfs.c + +diff --git a/plat/sun50iw1p1/bl31_sunxi_setup.c b/plat/sun50iw1p1/bl31_sunxi_setup.c +index 7c1109f4e..4c480e545 100644 +--- a/plat/sun50iw1p1/bl31_sunxi_setup.c ++++ b/plat/sun50iw1p1/bl31_sunxi_setup.c +@@ -250,7 +250,7 @@ void bl31_platform_setup(void) + + sunxi_setup_clocks(socid); + +- NOTICE("SCPI: installed handler, implementation level: 101000\n"); ++ NOTICE("SCPI: installed handler, implementation level: 111000\n"); + } + + /******************************************************************************* +diff --git a/plat/sun50iw1p1/platform.mk b/plat/sun50iw1p1/platform.mk +index 95cff562f..f56e79355 100644 +--- a/plat/sun50iw1p1/platform.mk ++++ b/plat/sun50iw1p1/platform.mk +@@ -53,5 +53,6 @@ BL31_SOURCES += drivers/arm/gic/arm_gic.c \ + plat/sun50iw1p1/sunxi_sip_svc.c \ + plat/sun50iw1p1/sunxi_scpi.c \ + plat/sun50iw1p1/sunxi_rsb.c \ ++ plat/sun50iw1p1/sunxi_dvfs.c \ + plat/sun50iw1p1/aarch64/sunxi_common.c + +diff --git a/plat/sun50iw1p1/sunxi_dvfs.c b/plat/sun50iw1p1/sunxi_dvfs.c +new file mode 100644 +index 000000000..16049e968 +--- /dev/null ++++ b/plat/sun50iw1p1/sunxi_dvfs.c +@@ -0,0 +1,65 @@ ++#include ++#include ++#include ++#include ++ ++#include "sunxi_def.h" ++#include "sunxi_private.h" ++ ++struct op_points ++{ ++ uint32_t freq; ++ uint32_t voltage; ++} sunxi_op_points[] = { ++ { 408, 1000}, { 648, 1040}, { 816, 1080}, { 912, 1120}, { 960, 1160}, ++ {1008, 1200}, {1056, 1240}, {1104, 1260}, {1152, 1300} ++}; ++ ++#define NR_OPP (sizeof(sunxi_op_points) / sizeof(sunxi_op_points[0])) ++ ++int current_opp_index = 2; ++int current_opp_limit = NR_OPP; ++ ++uint32_t sunxi_dvfs_get_get_opp_voltage(int oppnr) ++{ ++ if (oppnr < 0 || oppnr >= NR_OPP) ++ return ~0; ++ ++ return sunxi_op_points[oppnr].voltage; ++} ++ ++uint32_t sunxi_dvfs_get_get_opp_frequency(int oppnr) ++{ ++ if (oppnr < 0 || oppnr >= NR_OPP) ++ return ~0; ++ ++ return sunxi_op_points[oppnr].freq * 1000000; ++} ++ ++int sunxi_dvfs_set_index(int index) ++{ ++ if (index < 0 || index >= NR_OPP) ++ return -1; ++ ++ if (index < current_opp_index) { ++ sunxi_clock_set_cpu_clock(sunxi_op_points[index].freq, 1); ++ sunxi_power_set_cpu_voltage(sunxi_op_points[index].voltage); ++ } else { ++ sunxi_power_set_cpu_voltage(sunxi_op_points[index].voltage); ++ sunxi_clock_set_cpu_clock(sunxi_op_points[index].freq, 1); ++ } ++ ++ current_opp_index = index; ++ ++ return 0; ++} ++ ++int sunxi_dvfs_get_index(void) ++{ ++ return current_opp_index; ++} ++ ++int sunxi_dvfs_get_nr_opp(void) ++{ ++ return NR_OPP; ++} +diff --git a/plat/sun50iw1p1/sunxi_private.h b/plat/sun50iw1p1/sunxi_private.h +index 07a329598..b98400107 100644 +--- a/plat/sun50iw1p1/sunxi_private.h ++++ b/plat/sun50iw1p1/sunxi_private.h +@@ -97,6 +97,13 @@ int sunxi_rsb_write(uint8_t address, uint8_t value); + void sunxi_rsb_wait(const char *desc); + int sunxi_rsb_configure(uint16_t hw_addr, uint8_t rt_addr); + ++/* Declarations for sunxi_dvfs.c */ ++uint32_t sunxi_dvfs_get_get_opp_voltage(int oppnr); ++uint32_t sunxi_dvfs_get_get_opp_frequency(int oppnr); ++int sunxi_dvfs_set_index(int index); ++int sunxi_dvfs_get_index(void); ++int sunxi_dvfs_get_nr_opp(void); ++ + /* Gets the SPSR for BL33 entry */ + uint32_t sunxi_get_spsr_for_bl33_entry(int aarch); + +diff --git a/plat/sun50iw1p1/sunxi_scpi.c b/plat/sun50iw1p1/sunxi_scpi.c +index 3ca287ed2..50aabeb27 100644 +--- a/plat/sun50iw1p1/sunxi_scpi.c ++++ b/plat/sun50iw1p1/sunxi_scpi.c +@@ -53,12 +53,18 @@ + #define SCPI_E_BUSY 12 + + #define SCP_CMD_CAPABILITY 0x02 ++#define SCP_CMD_DVFS_CAPABILITY 0x08 ++#define SCP_CMD_DVFS_GET_INFO 0x09 ++#define SCP_CMD_DVFS_SET_INDEX 0x0a ++#define SCP_CMD_DVFS_GET_INDEX 0x0b ++#define SCP_CMD_DVFS_GET_STAT 0x0c + #define SCP_CMD_CLOCKS_CAPS 0x0d + #define SCP_CMD_CLOCK_GET_INFO 0x0e + #define SCP_CMD_CLOCK_SET_RATE 0x0f + #define SCP_CMD_CLOCK_GET_RATE 0x10 + + #define SCP_CMDS_IMPLEMENTED \ ++ GENMASK(SCP_CMD_DVFS_GET_INDEX, SCP_CMD_DVFS_CAPABILITY) | \ + GENMASK(SCP_CMD_CLOCK_GET_RATE, SCP_CMD_CLOCKS_CAPS) + + /* end of SRAM A1 */ +@@ -141,6 +147,35 @@ static uint32_t scpi_handle_cmd(int cmd, uint8_t *payload_size, + mmio_write_32(payload_out, ret); + *payload_size = 4; + return 0; ++ case SCP_CMD_DVFS_CAPABILITY: ++ /* number of implemented voltage domains: only one */ ++ mmio_write_32(payload_out, 1); ++ *payload_size = 0x1; ++ return SCPI_OK; ++ case SCP_CMD_DVFS_GET_INFO: { ++ int i, nr_opp = sunxi_dvfs_get_nr_opp(); ++ ++ mmio_write_32(payload_out, nr_opp << 8); ++ for (i = 0; i < nr_opp; i++) { ++ mmio_write_32(payload_out + 4 + 2 * i * 4, ++ sunxi_dvfs_get_get_opp_frequency(i)); ++ mmio_write_32(payload_out + 4 + 2 * i * 4 + 4, ++ sunxi_dvfs_get_get_opp_voltage(i)); ++ } ++ *payload_size = 4 + 2 * nr_opp * 4; ++ return SCPI_OK; ++ } ++ case SCP_CMD_DVFS_SET_INDEX: ++ if ((par1 & 0xff) != 0) ++ return SCPI_E_PARAM; ++ ++ if (sunxi_dvfs_set_index((par1 >> 8) & 0xff)) ++ return SCPI_E_RANGE; ++ return SCPI_OK; ++ case SCP_CMD_DVFS_GET_INDEX: ++ mmio_write_32(payload_out, sunxi_dvfs_get_index()); ++ *payload_size = 0x1; ++ return SCPI_OK; + } + + return SCPI_E_SUPPORT; diff --git a/patch/atf/atf-sun50iw1/09-add-THS-sensor.patch b/patch/atf/atf-sun50iw1/09-add-THS-sensor.patch new file mode 100644 index 0000000000..54731d4f09 --- /dev/null +++ b/patch/atf/atf-sun50iw1/09-add-THS-sensor.patch @@ -0,0 +1,317 @@ +From 96fcf67c30ca4526641e08e16ba0992cd15e05c0 Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Fri, 23 Sep 2016 01:30:18 +0100 +Subject: [PATCH] sunxi: SCPI: add temperature sensor readout + +The SCPI interface provides abstracted access to sensors, to monitor +voltage, current, power and temperature. +Introduce the required code to read and decode Allwinner's CPU and GPU +temperature sensors and export them via the SCPI interface. + +Signed-off-by: Andre Przywara +--- + plat/sun50iw1p1/bl31_sunxi_setup.c | 4 +- + plat/sun50iw1p1/platform.mk | 2 + + plat/sun50iw1p1/sunxi_private.h | 10 ++++ + plat/sun50iw1p1/sunxi_scpi.c | 46 +++++++++++++- + plat/sun50iw1p1/sunxi_sensors.c | 40 +++++++++++++ + plat/sun50iw1p1/sunxi_temp.c | 119 +++++++++++++++++++++++++++++++++++++ + 6 files changed, 219 insertions(+), 2 deletions(-) + create mode 100644 plat/sun50iw1p1/sunxi_sensors.c + create mode 100644 plat/sun50iw1p1/sunxi_temp.c + +diff --git a/plat/sun50iw1p1/bl31_sunxi_setup.c b/plat/sun50iw1p1/bl31_sunxi_setup.c +index 4c480e545..503f4006d 100644 +--- a/plat/sun50iw1p1/bl31_sunxi_setup.c ++++ b/plat/sun50iw1p1/bl31_sunxi_setup.c +@@ -248,9 +248,11 @@ void bl31_platform_setup(void) + + sunxi_power_setup(socid); + ++ sunxi_ths_setup(); ++ + sunxi_setup_clocks(socid); + +- NOTICE("SCPI: installed handler, implementation level: 111000\n"); ++ NOTICE("SCPI: installed handler, implementation level: 111010\n"); + } + + /******************************************************************************* +diff --git a/plat/sun50iw1p1/platform.mk b/plat/sun50iw1p1/platform.mk +index f56e79355..b1b0ac298 100644 +--- a/plat/sun50iw1p1/platform.mk ++++ b/plat/sun50iw1p1/platform.mk +@@ -54,5 +54,7 @@ BL31_SOURCES += drivers/arm/gic/arm_gic.c \ + plat/sun50iw1p1/sunxi_scpi.c \ + plat/sun50iw1p1/sunxi_rsb.c \ + plat/sun50iw1p1/sunxi_dvfs.c \ ++ plat/sun50iw1p1/sunxi_sensors.c \ ++ plat/sun50iw1p1/sunxi_temp.c \ + plat/sun50iw1p1/aarch64/sunxi_common.c + +diff --git a/plat/sun50iw1p1/sunxi_private.h b/plat/sun50iw1p1/sunxi_private.h +index b98400107..d2ab0e6c2 100644 +--- a/plat/sun50iw1p1/sunxi_private.h ++++ b/plat/sun50iw1p1/sunxi_private.h +@@ -104,6 +104,16 @@ int sunxi_dvfs_set_index(int index); + int sunxi_dvfs_get_index(void); + int sunxi_dvfs_get_nr_opp(void); + ++/* Declarations for sunxi_sensors.c */ ++int sunxi_setup_sensors(void); ++const char* sunxi_sensor_get_name(int sensornr); ++uint32_t sunxi_sensor_get_value(int sensornr); ++int sunxi_sensors_nr_sensors(void); ++ ++/* Declarations for sunxi_temp.c */ ++int sunxi_ths_setup(void); ++int sunxi_ths_read_temp(int sensornr); ++ + /* Gets the SPSR for BL33 entry */ + uint32_t sunxi_get_spsr_for_bl33_entry(int aarch); + +diff --git a/plat/sun50iw1p1/sunxi_scpi.c b/plat/sun50iw1p1/sunxi_scpi.c +index 50aabeb27..4d0d71e97 100644 +--- a/plat/sun50iw1p1/sunxi_scpi.c ++++ b/plat/sun50iw1p1/sunxi_scpi.c +@@ -62,10 +62,14 @@ + #define SCP_CMD_CLOCK_GET_INFO 0x0e + #define SCP_CMD_CLOCK_SET_RATE 0x0f + #define SCP_CMD_CLOCK_GET_RATE 0x10 ++#define SCP_CMD_SENSORS_CAPS 0x15 ++#define SCP_CMD_SENSORS_INFO 0x16 ++#define SCP_CMD_SENSORS_VALUE 0x17 + + #define SCP_CMDS_IMPLEMENTED \ + GENMASK(SCP_CMD_DVFS_GET_INDEX, SCP_CMD_DVFS_CAPABILITY) | \ +- GENMASK(SCP_CMD_CLOCK_GET_RATE, SCP_CMD_CLOCKS_CAPS) ++ GENMASK(SCP_CMD_CLOCK_GET_RATE, SCP_CMD_CLOCKS_CAPS) | \ ++ GENMASK(SCP_CMD_SENSORS_VALUE, SCP_CMD_SENSORS_CAPS) + + /* end of SRAM A1 */ + #define SUNXI_SCPI_SHMEM_BASE 0x17e00 +@@ -89,6 +93,25 @@ static int write_clock_info(uintptr_t payload, int clocknr) + return 12 + i; + } + ++static int write_sensor_info(uintptr_t payload, int sensornr) ++{ ++ const char *name, *s; ++ int i; ++ ++ name = sunxi_sensor_get_name(sensornr); ++ if (!name) ++ return -SCPI_E_PARAM; ++ ++ /* no triggers, always temperature sensor (for now) */ ++ mmio_write_32(payload + 0x0, (sensornr & 0xffff)); ++ ++ for (i = 0, s = name; s[i] != 0; i++) ++ mmio_write_8(payload + 4 + i, s[i]); ++ mmio_write_8(payload + 4 + i, 0); ++ ++ return 4 + i; ++} ++ + static uint32_t scpi_handle_cmd(int cmd, uint8_t *payload_size, + uintptr_t payload_in, uintptr_t payload_out) + { +@@ -176,6 +199,27 @@ static uint32_t scpi_handle_cmd(int cmd, uint8_t *payload_size, + mmio_write_32(payload_out, sunxi_dvfs_get_index()); + *payload_size = 0x1; + return SCPI_OK; ++ case SCP_CMD_SENSORS_CAPS: ++ /* number of implemented sensors */ ++ mmio_write_32(payload_out, sunxi_sensors_nr_sensors()); ++ *payload_size = 0x2; ++ return SCPI_OK; ++ case SCP_CMD_SENSORS_INFO: ++ ret = write_sensor_info(payload_out, par1 & 0xffff); ++ if (ret < 0) { ++ *payload_size = 0; ++ return SCPI_E_PARAM; ++ } ++ *payload_size = ret; ++ return SCPI_OK; ++ case SCP_CMD_SENSORS_VALUE: ++ ret = sunxi_sensor_get_value(par1 & 0xffff); ++ if (ret == ~0) ++ return SCPI_E_RANGE; ++ mmio_write_32(payload_out, ret); ++ mmio_write_32(payload_out + 4, 0); ++ *payload_size = 8; ++ return 0; + } + + return SCPI_E_SUPPORT; +diff --git a/plat/sun50iw1p1/sunxi_sensors.c b/plat/sun50iw1p1/sunxi_sensors.c +new file mode 100644 +index 000000000..ee8f216b6 +--- /dev/null ++++ b/plat/sun50iw1p1/sunxi_sensors.c +@@ -0,0 +1,40 @@ ++#include ++#include ++#include ++#include ++ ++#include "sunxi_def.h" ++#include "sunxi_private.h" ++ ++int sunxi_setup_sensors(void) ++{ ++ return 0; ++} ++ ++const char* sunxi_sensor_get_name(int sensornr) ++{ ++ switch(sensornr) { ++ case 0: return "cpu_temp"; ++ case 1: return "gpu_temp0"; ++ case 2: return "gpu_temp1"; ++ } ++ ++ return NULL; ++} ++ ++uint32_t sunxi_sensor_get_value(int sensornr) ++{ ++ switch(sensornr) { ++ case 0: ++ case 1: ++ case 2: ++ return sunxi_ths_read_temp(sensornr); ++ } ++ ++ return ~0; ++} ++ ++int sunxi_sensors_nr_sensors(void) ++{ ++ return 3; ++} +diff --git a/plat/sun50iw1p1/sunxi_temp.c b/plat/sun50iw1p1/sunxi_temp.c +new file mode 100644 +index 000000000..8c777a34f +--- /dev/null ++++ b/plat/sun50iw1p1/sunxi_temp.c +@@ -0,0 +1,119 @@ ++/* ++ * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * ++ * Redistributions of source code must retain the above copyright notice, this ++ * list of conditions and the following disclaimer. ++ * ++ * Redistributions in binary form must reproduce the above copyright notice, ++ * this list of conditions and the following disclaimer in the documentation ++ * and/or other materials provided with the distribution. ++ * ++ * Neither the name of ARM nor the names of its contributors may be used ++ * to endorse or promote products derived from this software without specific ++ * prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE ++ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "sunxi_def.h" ++#include "sunxi_private.h" ++ ++#define CCU_BASE 0x1c20000ULL ++#define BUS_CLK_GATING_REG2 0x068 ++#define THS_CLK_REG 0x074 ++#define BUS_SOFT_RST_REG3 0x2d0 ++#define THS_BASE 0x1c25000ULL ++ ++#define BIT(n) (1U << (n)) ++ ++/* temperature = ( MINUPA - reg * MULPA) / DIVPA */ ++#define MULPA 25000 ++#define DIVPA 214 ++#define MINUPA 2170 ++static int sun50_th_reg_to_temp(uint32_t reg_data) ++{ ++ return ((MINUPA - (int)reg_data) * MULPA) / DIVPA; ++} ++ ++/* Initialize the temperature sensor */ ++static int init_ths(void) ++{ ++ uint32_t reg; ++ ++ /* de-assert reset of THS */ ++ reg = mmio_read_32(CCU_BASE + BUS_SOFT_RST_REG3); ++ mmio_write_32(CCU_BASE + BUS_SOFT_RST_REG3, reg | BIT(8)); ++ ++ /* enable THS clock at 4 MHz */ ++ reg = mmio_read_32(CCU_BASE + THS_CLK_REG) & ~0x3; ++ mmio_write_32(CCU_BASE + THS_CLK_REG, reg | 0x3 | BIT(31)); ++ ++ /* un-gate THS clock */ ++ reg = mmio_read_32(CCU_BASE + BUS_CLK_GATING_REG2); ++ mmio_write_32(CCU_BASE + BUS_CLK_GATING_REG2, reg | BIT(8)); ++ ++ /* start calibration */ ++ mmio_write_32(THS_BASE + 0x04, BIT(17)); ++ /* set aquire times */ ++ mmio_write_32(THS_BASE + 0x00, 0x190); ++ mmio_write_32(THS_BASE + 0x40, 0x190 << 16); ++ /* enable filter, average over 8 values */ ++ mmio_write_32(THS_BASE + 0x70, 0x06); ++ /* enable sensors 0-2 (CPU & GPUs) measurement */ ++ reg = mmio_read_32(THS_BASE + 0x40); ++ mmio_write_32(THS_BASE + 0x40, reg | BIT(0) | BIT(1) | BIT(2)); ++ ++ return 0; ++} ++ ++/* Setup the temperature sensor */ ++int sunxi_ths_setup(void) ++{ ++ int ret; ++ ++ NOTICE("Configuring thermal sensors\n"); ++ ++ ret = init_ths(); ++ if (ret) { ++ ERROR("THS: cannot initialize temperature sensor\n"); ++ return -1; ++ } ++ ++ return ret; ++} ++ ++int sunxi_ths_read_temp(int sensor) ++{ ++ int reg; ++ ++ if (sensor < 0 || sensor > 2) ++ return ~0; ++ ++ reg = mmio_read_32(THS_BASE + 0x80 + (4 * sensor)); ++ ++ return sun50_th_reg_to_temp(reg & 0xfff); ++} diff --git a/patch/atf/atf-sun50iw1/10-add-device-power-state.patch b/patch/atf/atf-sun50iw1/10-add-device-power-state.patch new file mode 100644 index 0000000000..0481300447 --- /dev/null +++ b/patch/atf/atf-sun50iw1/10-add-device-power-state.patch @@ -0,0 +1,233 @@ +From 321bc21a00f9b46735bcfdca5a683ab240cd0f33 Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Mon, 27 Feb 2017 23:10:56 +0000 +Subject: [PATCH] UNTESTED: sunxi: SCPI: add device power state support + +Some generic framework to toggle AXP power rails via the SCPI device +power state interface. +TODO: + - testing + - check how H5 folds in + +Signed-off-by: Andre Przywara +--- + plat/sun50iw1p1/bl31_sunxi_setup.c | 2 +- + plat/sun50iw1p1/sunxi_power.c | 142 +++++++++++++++++++++++++++++++++++++ + plat/sun50iw1p1/sunxi_private.h | 2 + + plat/sun50iw1p1/sunxi_scpi.c | 13 +++- + 4 files changed, 157 insertions(+), 2 deletions(-) + +diff --git a/plat/sun50iw1p1/bl31_sunxi_setup.c b/plat/sun50iw1p1/bl31_sunxi_setup.c +index 503f4006d..027436725 100644 +--- a/plat/sun50iw1p1/bl31_sunxi_setup.c ++++ b/plat/sun50iw1p1/bl31_sunxi_setup.c +@@ -252,7 +252,7 @@ void bl31_platform_setup(void) + + sunxi_setup_clocks(socid); + +- NOTICE("SCPI: installed handler, implementation level: 111010\n"); ++ NOTICE("SCPI: installed handler, implementation level: 111011\n"); + } + + /******************************************************************************* +diff --git a/plat/sun50iw1p1/sunxi_power.c b/plat/sun50iw1p1/sunxi_power.c +index 5aa63fe4f..8849af8bc 100644 +--- a/plat/sun50iw1p1/sunxi_power.c ++++ b/plat/sun50iw1p1/sunxi_power.c +@@ -177,6 +177,148 @@ enum pmic_type { + PMIC_AXP803, + } pmic_type; + ++int sunxi_power_set_cpu_voltage(int millivolt) ++{ ++ switch (pmic_type) { ++ case PMIC_AXP803: ++ return axp803_set_cpu_voltage(millivolt); ++ } ++ ++ return -ENODEV; ++} ++ ++/* ++ * device mapping: blocks of 8 (or 16?) ++ * block 0: DCDCn ++ * block 1: special (DC1SW, ...) ++ * block 2: ALDO ++ * block 3: DLDO ++ * block 4: ELDO ++ * block 5: FLDO ++ * block 6: GPIO ++ * ++ * state: == 0: power off ++ * != 0: power on ++ */ ++ ++static uint32_t axp803_map_devices(uint16_t devid) ++{ ++ unsigned int reg, bit; ++ ++ switch (devid) { ++ case 0: /* DCDC */ ++ case 1: ++ case 2: ++ case 3: ++ case 4: ++ case 5: ++ reg = 0x10; ++ bit = devid; ++ break; ++ case 8: /* DC1SW */ ++ reg = 0x12; ++ bit = 7; ++ break; ++ case 16: /* ALDO */ ++ case 17: ++ case 18: ++ reg = 0x13; ++ bit = (devid & 7) + 5; ++ break; ++ case 24: /* DLDO */ ++ case 25: ++ case 26: ++ case 27: ++ reg = 0x12; ++ bit = (devid & 7) + 3; ++ break; ++ case 32: /* ELDO */ ++ case 33: ++ case 34: ++ reg = 0x13; ++ bit = (devid & 7) + 0; ++ break; ++ case 40: /* FLDO */ ++ case 41: ++ reg = 0x13; ++ bit = (devid & 7) + 2; ++ break; ++ case 48: /* GPIO0LDO */ ++ case 49: /* GPIO1LDO */ ++ /* TODO: implement */ ++ default: ++ return -1; ++ } ++ ++ return (bit & 0xff) | (reg << 8); ++} ++ ++static unsigned int axp803_pstate_get(uint16_t device) ++{ ++ uint32_t regmap = axp803_map_devices(device); ++ uint8_t reg; ++ int bit, val; ++ ++ if (regmap == -1) ++ return -1; ++ ++ reg = (regmap >> 8) & 0xff; ++ bit = regmap & 0xff; ++ ++ val = sunxi_rsb_read(reg); ++ if (val < 0) ++ return -2; ++ ++ if (val & BIT(bit)) ++ return 1; ++ ++ return 0; ++} ++ ++unsigned int sunxi_pstate_get(uint16_t device) ++{ ++ switch (pmic_type) { ++ case PMIC_AXP803: ++ return axp803_pstate_get(device); ++ } ++ ++ return ~0; ++} ++ ++static int axp803_pstate_set(uint16_t device, uint8_t state) ++{ ++ uint32_t regmap = axp803_map_devices(device); ++ uint8_t reg; ++ int bit, val; ++ ++ if (regmap == -1) ++ return -1; ++ ++ reg = (regmap >> 8) & 0xff; ++ bit = regmap & 0xff; ++ ++ val = sunxi_rsb_read(reg); ++ if (val < 0) ++ return -2; ++ ++ if (state) ++ val |= BIT(bit); ++ else ++ val &= ~BIT(bit); ++ ++ return sunxi_rsb_write(reg, val); ++} ++ ++int sunxi_pstate_set(uint16_t device, uint8_t state) ++{ ++ switch (pmic_type) { ++ case PMIC_AXP803: ++ return axp803_pstate_set(device, state); ++ } ++ ++ return -EINVAL; ++} ++ + int sunxi_power_setup(uint16_t socid) + { + int ret; +diff --git a/plat/sun50iw1p1/sunxi_private.h b/plat/sun50iw1p1/sunxi_private.h +index d2ab0e6c2..27f87839a 100644 +--- a/plat/sun50iw1p1/sunxi_private.h ++++ b/plat/sun50iw1p1/sunxi_private.h +@@ -73,6 +73,8 @@ int sunxi_pmic_read(uint8_t address); + int sunxi_pmic_write(uint8_t address, uint8_t value); + int sunxi_power_setup(uint16_t socid); + int sunxi_power_set_cpu_voltage(int millivolt); ++unsigned int sunxi_pstate_get(uint16_t device); ++int sunxi_pstate_set(uint16_t device, uint8_t state); + + void udelay(unsigned int delay); + int sunxi_setup_clocks(uint16_t socid); +diff --git a/plat/sun50iw1p1/sunxi_scpi.c b/plat/sun50iw1p1/sunxi_scpi.c +index 4d0d71e97..6856e400e 100644 +--- a/plat/sun50iw1p1/sunxi_scpi.c ++++ b/plat/sun50iw1p1/sunxi_scpi.c +@@ -65,11 +65,14 @@ + #define SCP_CMD_SENSORS_CAPS 0x15 + #define SCP_CMD_SENSORS_INFO 0x16 + #define SCP_CMD_SENSORS_VALUE 0x17 ++#define SCP_CMD_PSTATE_SET 0x1b ++#define SCP_CMD_PSTATE_GET 0x1c + + #define SCP_CMDS_IMPLEMENTED \ + GENMASK(SCP_CMD_DVFS_GET_INDEX, SCP_CMD_DVFS_CAPABILITY) | \ + GENMASK(SCP_CMD_CLOCK_GET_RATE, SCP_CMD_CLOCKS_CAPS) | \ +- GENMASK(SCP_CMD_SENSORS_VALUE, SCP_CMD_SENSORS_CAPS) ++ GENMASK(SCP_CMD_SENSORS_VALUE, SCP_CMD_SENSORS_CAPS) | \ ++ GENMASK(SCP_CMD_PSTATE_SET, SCP_CMD_PSTATE_GET) + + /* end of SRAM A1 */ + #define SUNXI_SCPI_SHMEM_BASE 0x17e00 +@@ -220,6 +223,14 @@ static uint32_t scpi_handle_cmd(int cmd, uint8_t *payload_size, + mmio_write_32(payload_out + 4, 0); + *payload_size = 8; + return 0; ++ case SCP_CMD_PSTATE_SET: ++ if (sunxi_pstate_set(par1 & 0xffff, (par1 >> 16) & 0xff)) ++ return SCPI_E_RANGE; ++ return SCPI_OK; ++ case SCP_CMD_PSTATE_GET: ++ mmio_write_32(payload_out, sunxi_pstate_get(par1 & 0xffff)); ++ *payload_size = 0x1; ++ return SCPI_OK; + } + + return SCPI_E_SUPPORT; diff --git a/patch/atf/atf-sun50iw1/add-SRAM-mapping-for-SCPI.patch b/patch/atf/atf-sun50iw1/add-SRAM-mapping-for-SCPI.patch new file mode 100644 index 0000000000..cd7af1f437 --- /dev/null +++ b/patch/atf/atf-sun50iw1/add-SRAM-mapping-for-SCPI.patch @@ -0,0 +1,29 @@ +From 5ff3a09d64b6b9172d9007e3f77116902b9c2965 Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Mon, 8 Aug 2016 02:34:27 +0100 +Subject: [PATCH] sunxi: add SRAM regions to EL3 mapping + +Currently we only map the MMIO regions for actual peripheral devices +in EL3. For the SCPI implementation we need access to the SRAM regions +as well. +Add a mapping entry that covers all three SRAM regions on the A64. + +Signed-off-by: Andre Przywara +--- + plat/sun50iw1p1/aarch64/sunxi_common.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/plat/sun50iw1p1/aarch64/sunxi_common.c b/plat/sun50iw1p1/aarch64/sunxi_common.c +index a2fa37a39..8ebcd9852 100644 +--- a/plat/sun50iw1p1/aarch64/sunxi_common.c ++++ b/plat/sun50iw1p1/aarch64/sunxi_common.c +@@ -56,6 +56,9 @@ plat_config_t plat_config; + */ + const mmap_region_t sunxi_mmap[] = { + ++ // SRAM regions ++ { 0x0010000, 0x0010000, ++ 0x0030000, MT_DEVICE | MT_RW | MT_NS }, + // MMI/O region used by peripherals from 0x100.0000 to 0x200.0000 + { 0x1000000, 0x1000000, + 0x1000000, MT_DEVICE | MT_RW | MT_SECURE }, diff --git a/patch/atf/atf-sun50iw1/board_pine64so/set-lpddr3-dram-voltage.patch b/patch/atf/atf-sun50iw1/board_pine64so/set-lpddr3-dram-voltage.patch index 41d419d111..d19db6c57d 100644 --- a/patch/atf/atf-sun50iw1/board_pine64so/set-lpddr3-dram-voltage.patch +++ b/patch/atf/atf-sun50iw1/board_pine64so/set-lpddr3-dram-voltage.patch @@ -1,21 +1,13 @@ diff --git a/plat/sun50iw1p1/sunxi_power.c b/plat/sun50iw1p1/sunxi_power.c -index 0c2487e..30708f4 100644 +index 5aa63fe4..95bef606 100644 --- a/plat/sun50iw1p1/sunxi_power.c +++ b/plat/sun50iw1p1/sunxi_power.c -@@ -258,12 +258,10 @@ static int pmic_setup(void) - * changes. This should be further confined once we are able to - * reliably detect a Pine64 board. - */ -- ret = sunxi_pmic_read(0x24); /* read DCDC5 register */ -- if ((ret & 0x7f) == 0x26) { /* check for 1.24V value */ -- NOTICE("PMIC: fixing DRAM voltage from 1.24V to 1.36V\n"); -- sunxi_pmic_write(0x24, 0x2c); -- } -- -+ -+ NOTICE("PMIC: setting DRAM voltage to 1.24V\n"); -+ sunxi_pmic_write(0x24, 0x25); /* DCDC5 = LPDDR RAM voltage = 1.24V */ -+ - sunxi_pmic_write(0x15, 0x1a); /* DLDO1 = VCC3V3_HDMI voltage = 3.3V */ - - return 0; +@@ -126,7 +126,7 @@ static int axp803_initial_setup(void) + ret = sunxi_rsb_read(0x24); /* read DCDC5 register */ + if ((ret & 0x7f) == 0x26) { /* check for 1.24V value */ + NOTICE("PMIC: fixing DRAM voltage from 1.24V to 1.36V\n"); +- sunxi_rsb_write(0x24, 0x2c); ++ sunxi_rsb_write(0x24, 0x25); + } + + sunxi_rsb_write(0x15, 0x1a); /* DLDO1 = VCC3V3_HDMI voltage = 3.3V */ diff --git a/patch/atf/atf-sun50iw1/board_pinebook-a64/set-lpddr3-dram-voltage.patch b/patch/atf/atf-sun50iw1/board_pinebook-a64/set-lpddr3-dram-voltage.patch index 41d419d111..d19db6c57d 100644 --- a/patch/atf/atf-sun50iw1/board_pinebook-a64/set-lpddr3-dram-voltage.patch +++ b/patch/atf/atf-sun50iw1/board_pinebook-a64/set-lpddr3-dram-voltage.patch @@ -1,21 +1,13 @@ diff --git a/plat/sun50iw1p1/sunxi_power.c b/plat/sun50iw1p1/sunxi_power.c -index 0c2487e..30708f4 100644 +index 5aa63fe4..95bef606 100644 --- a/plat/sun50iw1p1/sunxi_power.c +++ b/plat/sun50iw1p1/sunxi_power.c -@@ -258,12 +258,10 @@ static int pmic_setup(void) - * changes. This should be further confined once we are able to - * reliably detect a Pine64 board. - */ -- ret = sunxi_pmic_read(0x24); /* read DCDC5 register */ -- if ((ret & 0x7f) == 0x26) { /* check for 1.24V value */ -- NOTICE("PMIC: fixing DRAM voltage from 1.24V to 1.36V\n"); -- sunxi_pmic_write(0x24, 0x2c); -- } -- -+ -+ NOTICE("PMIC: setting DRAM voltage to 1.24V\n"); -+ sunxi_pmic_write(0x24, 0x25); /* DCDC5 = LPDDR RAM voltage = 1.24V */ -+ - sunxi_pmic_write(0x15, 0x1a); /* DLDO1 = VCC3V3_HDMI voltage = 3.3V */ - - return 0; +@@ -126,7 +126,7 @@ static int axp803_initial_setup(void) + ret = sunxi_rsb_read(0x24); /* read DCDC5 register */ + if ((ret & 0x7f) == 0x26) { /* check for 1.24V value */ + NOTICE("PMIC: fixing DRAM voltage from 1.24V to 1.36V\n"); +- sunxi_rsb_write(0x24, 0x2c); ++ sunxi_rsb_write(0x24, 0x25); + } + + sunxi_rsb_write(0x15, 0x1a); /* DLDO1 = VCC3V3_HDMI voltage = 3.3V */ diff --git a/patch/atf/atf-sun50iw1/enable-a53-errata-workaround.patch b/patch/atf/atf-sun50iw1/enable-a53-errata-workaround.patch new file mode 100644 index 0000000000..1518d4d370 --- /dev/null +++ b/patch/atf/atf-sun50iw1/enable-a53-errata-workaround.patch @@ -0,0 +1,28 @@ +From 3efb52570e581d79f6e451ef88933423a61a9b55 Mon Sep 17 00:00:00 2001 +From: Andre Przywara +Date: Mon, 27 Mar 2017 21:56:08 +0100 +Subject: [PATCH] Makefile: (re-)enable A53 errata workaround + +The --fix-cortex-a53-843418 option to the linker was disabled before to +also support older toolchains which don't know of this option. +To not loose the bugfix for more recent toolchains introduce a feature +check, which enables the option if the linker knows about it. + +Signed-off-by: Andre Przywara +--- + Makefile | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/Makefile b/Makefile +index f96e2cb37..87c307ded 100644 +--- a/Makefile ++++ b/Makefile +@@ -234,7 +234,7 @@ CFLAGS += -nostdinc -pedantic -ffreestanding -Wall \ + CFLAGS += -ffunction-sections -fdata-sections + LDFLAGS += --fatal-warnings -O1 + LDFLAGS += --gc-sections +-#LDFLAGS += --fix-cortex-a53-843419 ++LDFLAGS += $(if $(shell $(LD) -v --fix-cortex-a53-843419 > /dev/null 2>&1 && echo 1),--fix-cortex-a53-843419) + + + CC := ${CROSS_COMPILE}gcc diff --git a/patch/kernel/sun50i-dev/add-smc-mailbox-a64-DT-01.patch b/patch/kernel/sun50i-dev/add-smc-mailbox-a64-DT-01.patch new file mode 100644 index 0000000000..f15d358a4b --- /dev/null +++ b/patch/kernel/sun50i-dev/add-smc-mailbox-a64-DT-01.patch @@ -0,0 +1,50 @@ +This adds support for the SCPI protocol using an SMC mailbox and some +shared memory in SRAM. +The SCPI provider is implemented in the ARM Trusted Firmware layer +(running in EL3 on the application processor cores), triggered by an smc +call. + +Signed-off-by: Andre Przywara +--- + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 26 ++++++++++++++++++++++++++ + 1 file changed, 26 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +index 9d00622..ef6f10e 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +@@ -124,6 +124,32 @@ + (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>; + }; + ++ mailbox: mbox@0 { ++ compatible = "arm,smc-mbox"; ++ #mbox-cells = <1>; ++ arm,smc-func-ids = <0x82000001>; ++ }; ++ ++ sram: sram@10000{ ++ compatible = "mmio-sram"; ++ reg = <0x10000 0x8000>; ++ ++ #address-cells = <1>; ++ #size-cells = <1>; ++ ranges = <0 0x10000 0x8000>; ++ ++ cpu_scp_mem: scp-shmem@7e00 { ++ compatible = "mmio-sram"; ++ reg = <0x7e00 0x200>; ++ }; ++ }; ++ ++ scpi { ++ compatible = "arm,scpi"; ++ mboxes = <&mailbox 0>; ++ shmem = <&cpu_scp_mem>; ++ }; ++ + soc { + compatible = "simple-bus"; + #address-cells = <1>; +-- +2.9.0 diff --git a/patch/kernel/sun50i-dev/add-smc-mailbox-a64-DT-02.patch b/patch/kernel/sun50i-dev/add-smc-mailbox-a64-DT-02.patch new file mode 100644 index 0000000000..c1a139aa1b --- /dev/null +++ b/patch/kernel/sun50i-dev/add-smc-mailbox-a64-DT-02.patch @@ -0,0 +1,67 @@ +One functionality provided by the SCPI handler is frequency scaling, +which allows to switch the one CPU cluster between several operating +points, each specifying a matching frequency and CPU voltage. +The actual table is specified in firmware and can be queried by Linux +using standardised SCPI calls. + +Signed-off-by: Andre Przywara +--- + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +index ef6f10e..58c3675 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +@@ -61,6 +61,7 @@ + device_type = "cpu"; + reg = <0>; + enable-method = "psci"; ++ clocks = <&scpi_dvfs 0>; + }; + + cpu1: cpu@1 { +@@ -68,6 +69,7 @@ + device_type = "cpu"; + reg = <1>; + enable-method = "psci"; ++ clocks = <&scpi_dvfs 0>; + }; + + cpu2: cpu@2 { +@@ -75,6 +77,7 @@ + device_type = "cpu"; + reg = <2>; + enable-method = "psci"; ++ clocks = <&scpi_dvfs 0>; + }; + + cpu3: cpu@3 { +@@ -82,6 +85,7 @@ + device_type = "cpu"; + reg = <3>; + enable-method = "psci"; ++ clocks = <&scpi_dvfs 0>; + }; + }; + +@@ -148,6 +152,17 @@ + compatible = "arm,scpi"; + mboxes = <&mailbox 0>; + shmem = <&cpu_scp_mem>; ++ ++ scpi-clocks { ++ compatible = "arm,scpi-clocks"; ++ ++ scpi_dvfs: scpi_dvfs_clocks { ++ compatible = "arm,scpi-dvfs-clocks"; ++ #clock-cells = <1>; ++ clock-indices = <0>; ++ clock-output-names = "cpu_clk"; ++ }; ++ }; + }; + + soc { +-- +2.9.0 diff --git a/patch/kernel/sun50i-dev/add-smc-mailbox-a64-DT-03.patch b/patch/kernel/sun50i-dev/add-smc-mailbox-a64-DT-03.patch new file mode 100644 index 0000000000..9d72d2be81 --- /dev/null +++ b/patch/kernel/sun50i-dev/add-smc-mailbox-a64-DT-03.patch @@ -0,0 +1,40 @@ +The SCPI protocol allows various sensors to be exposed to the OS. The +list of supported sensors (and their kind) is provided by the SCPI +provider, which is in ARM Trusted Firmware. The current implementation +exports the temperature sensors, for instance. +Since the temperature sensor requires a clock to be running, we set +a fixed clock rate for this particular clock to prevent the Linux driver +from turning it off. + +Signed-off-by: Andre Przywara +--- + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +index 58c3675..7cb1b04 100644 +--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi ++++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +@@ -163,6 +163,11 @@ + clock-output-names = "cpu_clk"; + }; + }; ++ ++ scpi_sensors0: sensors { ++ compatible = "arm,scpi-sensors"; ++ #thermal-sensor-cells = <1>; ++ }; + }; + + soc { +@@ -307,6 +312,8 @@ + clock-names = "hosc", "losc"; + #clock-cells = <1>; + #reset-cells = <1>; ++ assigned-clocks = <&ccu CLK_THS>; ++ assigned-clock-rates = <4000000>; + }; + + pio: pinctrl@1c20800 { +-- +2.9.0 diff --git a/patch/kernel/sun50i-dev/add-smc-mailbox-docs.patch b/patch/kernel/sun50i-dev/add-smc-mailbox-docs.patch new file mode 100644 index 0000000000..ff2b1becde --- /dev/null +++ b/patch/kernel/sun50i-dev/add-smc-mailbox-docs.patch @@ -0,0 +1,78 @@ +Add binding documentation for the generic ARM SMC mailbox. +This is not describing hardware, but a firmware interface. + +Signed-off-by: Andre Przywara +--- + .../devicetree/bindings/mailbox/arm-smc.txt | 61 ++++++++++++++++++++++ + 1 file changed, 61 insertions(+) + create mode 100644 Documentation/devicetree/bindings/mailbox/arm-smc.txt + +diff --git a/Documentation/devicetree/bindings/mailbox/arm-smc.txt b/Documentation/devicetree/bindings/mailbox/arm-smc.txt +new file mode 100644 +index 0000000..90c5926 +--- /dev/null ++++ b/Documentation/devicetree/bindings/mailbox/arm-smc.txt +@@ -0,0 +1,61 @@ ++ARM SMC Mailbox Driver ++====================== ++ ++This mailbox uses the ARM smc (secure monitor call) instruction to ++trigger a mailbox-connected activity in firmware, executing on the very same ++core as the caller. By nature this operation is synchronous and this ++mailbox provides no way for asynchronous messages to be delivered the other ++way round, from firmware to the OS. However the value of r0/w0/x0 the firmware ++returns after the smc call is delivered as a received message to the ++mailbox framework, so a synchronous communication can be established. ++ ++One use case of this mailbox is the SCP interface, which uses shared memory ++to transfer commands and parameters, and a mailbox to trigger a function ++call. This allows SoCs without a separate management processor (or ++when such a processor is not available or used) to use this standardized ++interface anyway. ++ ++This binding describes no hardware, but establishes a firmware interface. ++The communication follows the ARM SMC calling convention[1]. ++Any core which supports the SMC or HVC instruction can be used, as long as ++a firmware component running in EL3 or EL2 is handling these calls. ++ ++Mailbox Device Node: ++==================== ++ ++Required properties: ++-------------------- ++- compatible: Shall be "arm,smc-mbox" ++- #mbox-cells Shall be 1 - the index of the channel needed. ++- arm,smc-func-ids An array of 32-bit values specifying the function ++ IDs used by each mailbox channel. Those function IDs ++ follow the ARM SMC calling convention standard [1]. ++ There is one identifier per channel and the number ++ of supported channels is determined by the length ++ of this array. ++ ++Optional properties: ++-------------------- ++- method: A string, either: ++ "hvc": if the driver shall use an HVC call, or ++ "smc": if the driver shall use an SMC call ++ If omitted, defaults to an SMC call. ++ ++Example: ++-------- ++ ++ mailbox: smc_mbox { ++ #mbox-cells = <1>; ++ compatible = "arm,smc-mbox"; ++ identifiers = <0x82000001>, <0x82000002>; ++ }; ++ ++ scpi { ++ compatible = "arm,scpi"; ++ mboxes = <&mailbox 0>; ++ shmem = <&cpu_scp_shmem>; ++ }; ++ ++ ++[1] ++http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0028a/index.html +-- +2.9.0 diff --git a/patch/kernel/sun50i-dev/add-smc-mailbox-driver.patch b/patch/kernel/sun50i-dev/add-smc-mailbox-driver.patch new file mode 100644 index 0000000000..ccbf103758 --- /dev/null +++ b/patch/kernel/sun50i-dev/add-smc-mailbox-driver.patch @@ -0,0 +1,225 @@ +This mailbox driver implements a mailbox which signals transmitted data +via an ARM smc (secure monitor call) instruction. The mailbox receiver +is implemented in firmware and can synchronously return data when it +returns execution to the non-secure world again. +An asynchronous receive path is not implemented. +This allows the usage of a mailbox to trigger firmware actions on SoCs +which either don't have a separate management processor or on which such +a core is not available. A user of this mailbox could be the SCP +interface. + +Signed-off-by: Andre Przywara +--- + drivers/mailbox/Kconfig | 8 ++ + drivers/mailbox/Makefile | 2 + + drivers/mailbox/arm-smc-mailbox.c | 172 ++++++++++++++++++++++++++++++++++++++ + 3 files changed, 182 insertions(+) + create mode 100644 drivers/mailbox/arm-smc-mailbox.c + +diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig +index c5731e5..5664b7f 100644 +--- a/drivers/mailbox/Kconfig ++++ b/drivers/mailbox/Kconfig +@@ -170,4 +170,12 @@ config BCM_FLEXRM_MBOX + Mailbox implementation of the Broadcom FlexRM ring manager, + which provides access to various offload engines on Broadcom + SoCs. Say Y here if you want to use the Broadcom FlexRM. ++ ++config ARM_SMC_MBOX ++ tristate "Generic ARM smc mailbox" ++ depends on OF && HAVE_ARM_SMCCC ++ help ++ Generic mailbox driver which uses ARM smc calls to call into ++ firmware for triggering mailboxes. ++ + endif +diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile +index d54e412..8ec6869 100644 +--- a/drivers/mailbox/Makefile ++++ b/drivers/mailbox/Makefile +@@ -35,3 +35,5 @@ obj-$(CONFIG_BCM_FLEXRM_MBOX) += bcm-flexrm-mailbox.o + obj-$(CONFIG_QCOM_APCS_IPC) += qcom-apcs-ipc-mailbox.o + + obj-$(CONFIG_TEGRA_HSP_MBOX) += tegra-hsp.o ++ ++obj-$(CONFIG_ARM_SMC_MBOX) += arm-smc-mailbox.o +diff --git a/drivers/mailbox/arm-smc-mailbox.c b/drivers/mailbox/arm-smc-mailbox.c +new file mode 100644 +index 0000000..578aed2 +--- /dev/null ++++ b/drivers/mailbox/arm-smc-mailbox.c +@@ -0,0 +1,172 @@ ++/* ++ * Copyright (C) 2016,2017 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 device provides a mechanism for emulating a mailbox by using ++ * smc calls, allowing a "mailbox" consumer to sit in firmware running ++ * on the same core. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define ARM_SMC_MBOX_SMC (0 << 0) ++#define ARM_SMC_MBOX_HVC (1 << 0) ++#define ARM_SMC_MBOX_METHOD_MASK (1 << 0) ++ ++struct arm_smc_chan_data { ++ u32 function_id; ++ u32 flags; ++}; ++ ++static int arm_smc_send_data(struct mbox_chan *link, void *data) ++{ ++ struct arm_smc_chan_data *chan_data = link->con_priv; ++ u32 function_id = chan_data->function_id; ++ struct arm_smccc_res res; ++ u32 msg = *(u32 *)data; ++ ++ if ((chan_data->flags & ARM_SMC_MBOX_METHOD_MASK) == ARM_SMC_MBOX_SMC) ++ arm_smccc_smc(function_id, msg, 0, 0, 0, 0, 0, 0, &res); ++ else ++ arm_smccc_hvc(function_id, msg, 0, 0, 0, 0, 0, 0, &res); ++ ++ mbox_chan_received_data(link, (void *)res.a0); ++ ++ return 0; ++} ++ ++static int arm_smc_startup(struct mbox_chan *link) ++{ ++ return 0; ++} ++ ++static void arm_smc_shutdown(struct mbox_chan *link) ++{ ++} ++ ++/* This mailbox is synchronous, so we are always done. */ ++static bool arm_smc_last_tx_done(struct mbox_chan *link) ++{ ++ return true; ++} ++ ++static const struct mbox_chan_ops arm_smc_mbox_chan_ops = { ++ .send_data = arm_smc_send_data, ++ .startup = arm_smc_startup, ++ .shutdown = arm_smc_shutdown, ++ .last_tx_done = arm_smc_last_tx_done ++}; ++ ++static int arm_smc_mbox_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct mbox_controller *mbox; ++ struct arm_smc_chan_data *chan_data; ++ const char *method; ++ bool use_hvc = false; ++ int ret = 0, i; ++ ++ ret = of_property_count_elems_of_size(dev->of_node, "arm,smc-func-ids", ++ sizeof(u32)); ++ if (ret < 0) ++ return ret; ++ ++ if (!of_property_read_string(dev->of_node, "method", &method)) { ++ if (!strcmp("hvc", method)) { ++ use_hvc = true; ++ } else if (!strcmp("smc", method)) { ++ use_hvc = false; ++ } else { ++ dev_warn(dev, "invalid \"method\" property: %s\n", ++ method); ++ ++ return -EINVAL; ++ } ++ } ++ ++ mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); ++ if (!mbox) ++ return -ENOMEM; ++ ++ mbox->num_chans = ret; ++ mbox->chans = devm_kcalloc(dev, mbox->num_chans, sizeof(*mbox->chans), ++ GFP_KERNEL); ++ if (!mbox->chans) ++ return -ENOMEM; ++ ++ chan_data = devm_kcalloc(dev, mbox->num_chans, sizeof(*chan_data), ++ GFP_KERNEL); ++ if (!chan_data) ++ return -ENOMEM; ++ ++ for (i = 0; i < mbox->num_chans; i++) { ++ u32 function_id; ++ ++ ret = of_property_read_u32_index(dev->of_node, ++ "arm,smc-func-ids", i, ++ &function_id); ++ if (ret) ++ return ret; ++ ++ chan_data[i].function_id = function_id; ++ if (use_hvc) ++ chan_data[i].flags |= ARM_SMC_MBOX_HVC; ++ mbox->chans[i].con_priv = &chan_data[i]; ++ } ++ ++ mbox->txdone_poll = true; ++ mbox->txdone_irq = false; ++ mbox->txpoll_period = 1; ++ mbox->ops = &arm_smc_mbox_chan_ops; ++ mbox->dev = dev; ++ ++ ret = mbox_controller_register(mbox); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, mbox); ++ dev_info(dev, "ARM SMC mailbox enabled with %d chan%s.\n", ++ mbox->num_chans, mbox->num_chans == 1 ? "" : "s"); ++ ++ return ret; ++} ++ ++static int arm_smc_mbox_remove(struct platform_device *pdev) ++{ ++ struct mbox_controller *mbox = platform_get_drvdata(pdev); ++ ++ mbox_controller_unregister(mbox); ++ return 0; ++} ++ ++static const struct of_device_id arm_smc_mbox_of_match[] = { ++ { .compatible = "arm,smc-mbox", }, ++ {}, ++}; ++MODULE_DEVICE_TABLE(of, arm_smc_mbox_of_match); ++ ++static struct platform_driver arm_smc_mbox_driver = { ++ .driver = { ++ .name = "arm-smc-mbox", ++ .of_match_table = arm_smc_mbox_of_match, ++ }, ++ .probe = arm_smc_mbox_probe, ++ .remove = arm_smc_mbox_remove, ++}; ++module_platform_driver(arm_smc_mbox_driver); ++ ++MODULE_AUTHOR("Andre Przywara "); ++MODULE_DESCRIPTION("Generic ARM smc mailbox driver"); ++MODULE_LICENSE("GPL v2"); +-- +2.9.0