diff options
author | Julius Werner <jwerner@chromium.org> | 2019-06-05 12:40:35 -0700 |
---|---|---|
committer | Julius Werner <jwerner@chromium.org> | 2020-08-24 18:38:47 -0700 |
commit | f40008a4be26dc43cbf50f68c4f20803ea735807 (patch) | |
tree | 5f511e26f6660bb963abad6c6a99af51972d753c /plat/qti | |
parent | 2acf00433023a16ff5f076e2d551961567c022d5 (diff) | |
download | trusted-firmware-a-f40008a4be26dc43cbf50f68c4f20803ea735807.tar.gz |
qti: Add SPMI PMIC arbitrator driver
This patch adds a very rudimentary driver for the SPMI arbitrator used
to access the PMIC. It doesn't support all the controller's actual
arbitration features, so it should probably not be used concurrently
with a running kernel (and it's also not optimized for performance). But
it can be used to set a few registers during boot or on shutdown to
control reset handling, which is all we need it for.
Change-Id: I8631c34a2a89ac71aa1ec9b8266e818c922fe34a
Signed-off-by: Julius Werner <jwerner@chromium.org>
Diffstat (limited to 'plat/qti')
-rw-r--r-- | plat/qti/common/inc/spmi_arb.h | 23 | ||||
-rw-r--r-- | plat/qti/common/src/spmi_arb.c | 113 |
2 files changed, 136 insertions, 0 deletions
diff --git a/plat/qti/common/inc/spmi_arb.h b/plat/qti/common/inc/spmi_arb.h new file mode 100644 index 0000000000..362f7406cf --- /dev/null +++ b/plat/qti/common/inc/spmi_arb.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2020, Google LLC. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SPMI_ARB_H +#define SPMI_ARB_H + +#include <stdint.h> + +/******************************************************************************* + * WARNING: This driver does not arbitrate access with the kernel. These APIs + * must only be called when the kernel is known to be quiesced (such as before + * boot or while the system is shutting down). + ******************************************************************************/ + +/* 32-bit addresses combine (U)SID, PID and register address. */ + +int spmi_arb_read8(uint32_t addr); +int spmi_arb_write8(uint32_t addr, uint8_t data); + +#endif /* SPMI_ARB_H */ diff --git a/plat/qti/common/src/spmi_arb.c b/plat/qti/common/src/spmi_arb.c new file mode 100644 index 0000000000..81cc577293 --- /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) (0x0C440900 + 4 * i) +#define NUM_APID 0x80 + +#define PPID_MASK (0xfff << 8) + +#define REG_ARB_CMD(apid) (0x0C600000 + 0x10000 * 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) (0x0C600008 + 0x10000 * 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) (0x0C600018 + 0x10000 * apid) +#define REG_ARB_WDATA0(apid) (0x0C600010 + 0x10000 * 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; +} |