Add A64 dev DVFS and THS

This commit is contained in:
zador-blood-stained 2017-08-23 18:27:58 +03:00
parent 40e0e25454
commit 13a7b53d55
20 changed files with 2826 additions and 38 deletions

View file

@ -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

View file

@ -0,0 +1,238 @@
From 923ed37b583ad9f78ca36e0bb372e2d397a900af Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
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 <andre.przywara@arm.com>
---
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 <assert.h>
+#include <console.h>
+#include <debug.h>
+#include <mmio.h>
+#include "sunxi_sip_svc.h"
+#include "sunxi_private.h"
+#include <runtime_svc.h>
+#include <uuid.h>
+
+/* 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 <stdint.h>
+
+/* 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__ */

View file

@ -0,0 +1,193 @@
From dbba58ae9f77912f2d6661e8b06817327f1e3565 Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
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 <andre.przywara@arm.com>
---
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 <debug.h>
+#include <plat_config.h>
+#include <mmio.h>
+#include <sys/errno.h>
+#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,

View file

@ -0,0 +1,208 @@
From 426631883e372b3e95c94a148923396a11afc3ca Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
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 <andre.przywara@arm.com>
---
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;

View file

@ -0,0 +1,140 @@
From a641a3a8523759cc73a79d062d934949620ba72f Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
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 <andre.przywara@arm.com>
---
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 <ccmu.h>
#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);

View file

@ -0,0 +1,78 @@
From 8d1687f42bf86fe8197950b86a50936e28054534 Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
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 <andre.przywara@arm.com>
---
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);

View file

@ -0,0 +1,34 @@
From b3272daf1df2eb8b96654d2884eab391f85549ea Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
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 <andre.przywara@arm.com>
---
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();

View file

@ -0,0 +1,648 @@
From 7f15fa4721edf35fa3eb6494c9518379eea4604d Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
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 <andre.przywara@arm.com>
---
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 <debug.h>
+#include <plat_config.h>
+#include <mmio.h>
+#include <sys/errno.h>
+#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;
+}

View file

@ -0,0 +1,195 @@
From e2b4f43ae7c138f3502e3df4e3928827a169592b Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
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 <andre.przywara@arm.com>
---
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 <debug.h>
+#include <plat_config.h>
+#include <mmio.h>
+#include <sys/errno.h>
+
+#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;

View file

@ -0,0 +1,317 @@
From 96fcf67c30ca4526641e08e16ba0992cd15e05c0 Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
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 <andre.przywara@arm.com>
---
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 <debug.h>
+#include <plat_config.h>
+#include <mmio.h>
+#include <sys/errno.h>
+
+#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 <debug.h>
+#include <plat_config.h>
+#include <mmio.h>
+#include <assert.h>
+#include <bl_common.h>
+#include <context.h>
+#include <context_mgmt.h>
+#include <interrupt_mgmt.h>
+#include <runtime_svc.h>
+#include <sys/errno.h>
+
+#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);
+}

View file

@ -0,0 +1,233 @@
From 321bc21a00f9b46735bcfdca5a683ab240cd0f33 Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
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 <andre.przywara@arm.com>
---
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;

View file

@ -0,0 +1,29 @@
From 5ff3a09d64b6b9172d9007e3f77116902b9c2965 Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
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 <andre.przywara@arm.com>
---
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 },

View file

@ -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 */

View file

@ -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 */

View file

@ -0,0 +1,28 @@
From 3efb52570e581d79f6e451ef88933423a61a9b55 Mon Sep 17 00:00:00 2001
From: Andre Przywara <andre.przywara@arm.com>
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 <andre.przywara@arm.com>
---
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

View file

@ -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 <andre.p...@arm.com>
---
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

View file

@ -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 <andre.p...@arm.com>
---
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

View file

@ -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 <andre.p...@arm.com>
---
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

View file

@ -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 <andre.p...@arm.com>
---
.../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

View file

@ -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 <andre.p...@arm.com>
---
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 <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/arm-smccc.h>
+
+#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 <andre.p...@arm.com>");
+MODULE_DESCRIPTION("Generic ARM smc mailbox driver");
+MODULE_LICENSE("GPL v2");
--
2.9.0