summaryrefslogtreecommitdiffstats
path: root/plat/qti/common/src/spmi_arb.c
diff options
context:
space:
mode:
Diffstat (limited to 'plat/qti/common/src/spmi_arb.c')
-rw-r--r--plat/qti/common/src/spmi_arb.c113
1 files changed, 113 insertions, 0 deletions
diff --git a/plat/qti/common/src/spmi_arb.c b/plat/qti/common/src/spmi_arb.c
new file mode 100644
index 0000000..4213ed1
--- /dev/null
+++ b/plat/qti/common/src/spmi_arb.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 2020, Google LLC. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+
+#include <spmi_arb.h>
+
+#define REG_APID_MAP(apid) (0x0C440900U + sizeof(uint32_t) * apid)
+#define NUM_APID ((0x1100U - 0x900U) / sizeof(uint32_t))
+
+#define PPID_MASK (0xfffU << 8)
+
+#define REG_ARB_CMD(apid) (0x0C600000U + 0x10000U * apid)
+/* These are opcodes specific to this SPMI arbitrator, *not* SPMI commands. */
+#define OPC_EXT_WRITEL 0
+#define OPC_EXT_READL 1
+
+#define REG_ARB_STATUS(apid) (0x0C600008U + 0x10000U * apid)
+#define ARB_STATUS_DONE BIT(0)
+#define ARB_STATUS_FAILURE BIT(1)
+#define ARB_STATUS_DENIED BIT(2)
+#define ARB_STATUS_DROPPED BIT(3)
+
+/* Fake status to report driver errors. */
+#define ARB_FAKE_STATUS_TIMEOUT BIT(8)
+
+#define REG_ARB_RDATA0(apid) (0x0C600018U + 0x10000U * apid)
+#define REG_ARB_WDATA0(apid) (0x0C600010U + 0x10000U * apid)
+
+static int addr_to_apid(uint32_t addr)
+{
+ unsigned int i;
+
+ for (i = 0U; i < NUM_APID; i++) {
+ uint32_t reg = mmio_read_32(REG_APID_MAP(i));
+ if ((reg != 0U) && ((addr & PPID_MASK) == (reg & PPID_MASK))) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+static int wait_for_done(uint16_t apid)
+{
+ unsigned int timeout = 100;
+
+ while (timeout-- != 0U) {
+ uint32_t status = mmio_read_32(REG_ARB_STATUS(apid));
+ if ((status & ARB_STATUS_DONE) != 0U) {
+ if ((status & ARB_STATUS_FAILURE) != 0U ||
+ (status & ARB_STATUS_DENIED) != 0U ||
+ (status & ARB_STATUS_DROPPED) != 0U) {
+ return status & 0xff;
+ }
+ return 0;
+ }
+ mdelay(1);
+ }
+ ERROR("SPMI_ARB timeout!\n");
+ return ARB_FAKE_STATUS_TIMEOUT;
+}
+
+static void arb_command(uint16_t apid, uint8_t opcode, uint32_t addr,
+ uint8_t bytes)
+{
+ mmio_write_32(REG_ARB_CMD(apid), (uint32_t)opcode << 27 |
+ (addr & 0xff) << 4 | (bytes - 1));
+}
+
+int spmi_arb_read8(uint32_t addr)
+{
+ int apid = addr_to_apid(addr);
+
+ if (apid < 0) {
+ return apid;
+ }
+
+ arb_command(apid, OPC_EXT_READL, addr, 1);
+
+ int ret = wait_for_done(apid);
+ if (ret != 0) {
+ ERROR("SPMI_ARB read error [0x%x]: 0x%x\n", addr, ret);
+ return ret;
+ }
+
+ return mmio_read_32(REG_ARB_RDATA0(apid)) & 0xff;
+}
+
+int spmi_arb_write8(uint32_t addr, uint8_t data)
+{
+ int apid = addr_to_apid(addr);
+
+ if (apid < 0) {
+ return apid;
+ }
+
+ mmio_write_32(REG_ARB_WDATA0(apid), data);
+ arb_command(apid, OPC_EXT_WRITEL, addr, 1);
+
+ int ret = wait_for_done(apid);
+ if (ret != 0) {
+ ERROR("SPMI_ARB write error [0x%x] = 0x%x: 0x%x\n",
+ addr, data, ret);
+ }
+
+ return ret;
+}