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>
This commit is contained in:
parent
2acf004330
commit
f40008a4be
|
@ -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 */
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue