Hi Caleb, On Mon, 17 Jun 2024 at 14:02, Caleb Connolly <caleb.conno...@linaro.org> wrote: > > Introduce two Qualcomm SoC drivers, the RPMh and cmd-db. RPMh is a the > name for the second generation Resource Power Management hub on Qualcomm > SoCs. Most core regulators have to be controlled via this hub. > > The cmd-db is a region of memory which contains offsets and data about > how to communicate with the RPMh. > > Signed-off-by: Caleb Connolly <caleb.conno...@linaro.org> > --- > drivers/soc/Kconfig | 1 + > drivers/soc/Makefile | 1 + > drivers/soc/qcom/Kconfig | 25 ++ > drivers/soc/qcom/Makefile | 4 + > drivers/soc/qcom/cmd-db.c | 246 ++++++++++++++++ > drivers/soc/qcom/rpmh-internal.h | 141 +++++++++ > drivers/soc/qcom/rpmh-rsc.c | 619 > +++++++++++++++++++++++++++++++++++++++ > drivers/soc/qcom/rpmh.c | 110 +++++++ > include/soc/qcom/cmd-db.h | 42 +++ > include/soc/qcom/rpmh.h | 29 ++ > include/soc/qcom/tcs.h | 78 +++++ > 11 files changed, 1296 insertions(+) > > diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig > index 03433bc0e6d2..8ceca0cb1386 100644 > --- a/drivers/soc/Kconfig > +++ b/drivers/soc/Kconfig > @@ -39,8 +39,9 @@ config SOC_XILINX_VERSAL_NET > Enable this option to select SoC device id driver for Xilinx Versal > NET. > This allows other drivers to verify the SoC familiy & revision using > matching SoC attributes. > > +source "drivers/soc/qcom/Kconfig" > source "drivers/soc/samsung/Kconfig" > source "drivers/soc/ti/Kconfig" > > endmenu > diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile > index 610bf816d40a..c3d484e5bf6c 100644 > --- a/drivers/soc/Makefile > +++ b/drivers/soc/Makefile > @@ -3,8 +3,9 @@ > # Makefile for the U-Boot SOC specific device drivers. > > obj-$(CONFIG_SOC_SAMSUNG) += samsung/ > obj-$(CONFIG_SOC_TI) += ti/ > +obj-$(CONFIG_SOC_QCOM) += qcom/ > obj-$(CONFIG_SOC_DEVICE) += soc-uclass.o > obj-$(CONFIG_SOC_DEVICE_TI_K3) += soc_ti_k3.o > obj-$(CONFIG_SANDBOX) += soc_sandbox.o > obj-$(CONFIG_SOC_XILINX_ZYNQMP) += soc_xilinx_zynqmp.o > diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig > new file mode 100644 > index 000000000000..a0872c5b3c83 > --- /dev/null > +++ b/drivers/soc/qcom/Kconfig > @@ -0,0 +1,25 @@ > +# SPDX-License-Identifier: GPL-2.0+ > + > +menuconfig SOC_QCOM > + bool "Qualcomm SOC drivers support" > + help > + Say Y here if you want to enable Qualcomm SOC drivers support. > + > +if SOC_QCOM > + > +config QCOM_COMMAND_DB > + bool "Qualcomm Command DB" > + help > + Command DB queries shared memory by key string for shared system > + resources. Platform drivers that require to set state of a shared > + resource on a RPM-hardened platform must use this database to get > + SoC specific identifier and information for the shared resources. > + > +config QCOM_RPMH > + bool "Qualcomm RPMh support" > + depends on QCOM_COMMAND_DB > + help > + Say y here to support the Qualcomm RPMh (resource peripheral > manager) > + if you need to control regulators on Qualcomm platforms, say y here. > + > +endif # SOC_QCOM > diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile > new file mode 100644 > index 000000000000..4fca569cfb77 > --- /dev/null > +++ b/drivers/soc/qcom/Makefile > @@ -0,0 +1,4 @@ > +# SPDX-License-Identifier: GPL-2.0+ > + > +obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o > +obj-$(CONFIG_QCOM_RPMH) += rpmh-rsc.o rpmh.o > diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c > new file mode 100644 > index 000000000000..7bfd72ae2f3f > --- /dev/null > +++ b/drivers/soc/qcom/cmd-db.c > @@ -0,0 +1,246 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2016-2018, 2020, The Linux Foundation. All rights reserved. > + * Copyright (c) 2023, Linaro Ltd. > + */ > + > +#include <dm/device.h> > +#include <dm.h> > +#include <dm/device_compat.h> > +#include <linux/kernel.h> > +#include <linux/types.h> > +#include <linux/ioport.h> > +#include <linux/byteorder/generic.h> > + > +#include <soc/qcom/cmd-db.h> > + > +#define NUM_PRIORITY 2 > +#define MAX_SLV_ID 8 > +#define SLAVE_ID_MASK 0x7 > +#define SLAVE_ID_SHIFT 16 > + > +/** > + * struct entry_header: header for each entry in cmddb > + * > + * @id: resource's identifier > + * @priority: unused > + * @addr: the address of the resource > + * @len: length of the data > + * @offset: offset from :@data_offset, start of the data > + */ > +struct entry_header { > + u8 id[8]; > + __le32 priority[NUM_PRIORITY]; > + __le32 addr; > + __le16 len; > + __le16 offset; > +}; > + > +/** > + * struct rsc_hdr: resource header information > + * > + * @slv_id: id for the resource > + * @header_offset: entry's header at offset from the end of the cmd_db_header > + * @data_offset: entry's data at offset from the end of the cmd_db_header > + * @cnt: number of entries for HW type > + * @version: MSB is major, LSB is minor > + * @reserved: reserved for future use. > + */ > +struct rsc_hdr { > + __le16 slv_id; > + __le16 header_offset; > + __le16 data_offset; > + __le16 cnt; > + __le16 version; > + __le16 reserved[3]; > +}; > + > +/** > + * struct cmd_db_header: The DB header information > + * > + * @version: The cmd db version > + * @magic: constant expected in the database > + * @header: array of resources > + * @checksum: checksum for the header. Unused. > + * @reserved: reserved memory > + * @data: driver specific data > + */ > +struct cmd_db_header { > + __le32 version; > + u8 magic[4]; > + struct rsc_hdr header[MAX_SLV_ID]; > + __le32 checksum; > + __le32 reserved; > + u8 data[]; > +}; > + > +/** > + * DOC: Description of the Command DB database. > + * > + * At the start of the command DB memory is the cmd_db_header structure. > + * The cmd_db_header holds the version, checksum, magic key as well as an > + * array for header for each slave (depicted by the rsc_header). Each h/w > + * based accelerator is a 'slave' (shared resource) and has slave id > indicating > + * the type of accelerator. The rsc_header is the header for such individual > + * slaves of a given type. The entries for each of these slaves begin at the > + * rsc_hdr.header_offset. In addition each slave could have auxiliary data > + * that may be needed by the driver. The data for the slave starts at the > + * entry_header.offset to the location pointed to by the rsc_hdr.data_offset. > + * > + * Drivers have a stringified key to a slave/resource. They can query the > slave > + * information and get the slave id and the auxiliary data and the length of > the > + * data. Using this information, they can format the request to be sent to > the > + * h/w accelerator and request a resource state. > + */ > + > +static const u8 CMD_DB_MAGIC[] = { 0xdb, 0x30, 0x03, 0x0c }; > + > +static bool cmd_db_magic_matches(const struct cmd_db_header *header) > +{ > + const u8 *magic = header->magic; > + > + return memcmp(magic, CMD_DB_MAGIC, ARRAY_SIZE(CMD_DB_MAGIC)) == 0; > +} > + > +static struct cmd_db_header *cmd_db_header; > + > +static inline const void *rsc_to_entry_header(const struct rsc_hdr *hdr) > +{ > + u16 offset = le16_to_cpu(hdr->header_offset); > + > + return cmd_db_header->data + offset; > +} > + > +static inline void * > +rsc_offset(const struct rsc_hdr *hdr, const struct entry_header *ent) > +{ > + u16 offset = le16_to_cpu(hdr->data_offset); > + u16 loffset = le16_to_cpu(ent->offset); > + > + return cmd_db_header->data + offset + loffset; > +} > + > +static int cmd_db_get_header(const char *id, const struct entry_header **eh, > + const struct rsc_hdr **rh) > +{ > + const struct rsc_hdr *rsc_hdr; > + const struct entry_header *ent; > + int i, j; > + u8 query[sizeof(ent->id)] __nonstring; > + > + /* > + * Pad out query string to same length as in DB. NOTE: the output > + * query string is not necessarily '\0' terminated if it bumps up > + * against the max size. That's OK and expected. > + */ > + strncpy(query, id, sizeof(query)); > + > + for (i = 0; i < MAX_SLV_ID; i++) { > + rsc_hdr = &cmd_db_header->header[i]; > + if (!rsc_hdr->slv_id) > + break; > + > + ent = rsc_to_entry_header(rsc_hdr); > + for (j = 0; j < le16_to_cpu(rsc_hdr->cnt); j++, ent++) { > + if (memcmp(ent->id, query, sizeof(ent->id)) == 0) { > + if (eh) > + *eh = ent; > + if (rh) > + *rh = rsc_hdr; > + return 0; > + } > + } > + } > + > + return -ENODEV; > +} > + > +/** > + * cmd_db_read_addr() - Query command db for resource id address. > + * > + * @id: resource id to query for address > + * > + * Return: resource address on success, 0 on error > + * > + * This is used to retrieve resource address based on resource > + * id. > + */ > +u32 cmd_db_read_addr(const char *id) > +{ > + int ret; > + const struct entry_header *ent; > + > + ret = cmd_db_get_header(id, &ent, NULL); > + > + return ret < 0 ? 0 : le32_to_cpu(ent->addr); > +} > +EXPORT_SYMBOL(cmd_db_read_addr);
Export symbols aren't required in U-Boot here and other instances in this patch. > + > +/** > + * cmd_db_read_aux_data() - Query command db for aux data. > + * > + * @id: Resource to retrieve AUX Data on > + * @len: size of data buffer returned > + * > + * Return: pointer to data on success, error pointer otherwise > + */ > +const void *cmd_db_read_aux_data(const char *id, size_t *len) > +{ > + int ret; > + const struct entry_header *ent; > + const struct rsc_hdr *rsc_hdr; > + > + ret = cmd_db_get_header(id, &ent, &rsc_hdr); > + if (ret) > + return ERR_PTR(ret); > + > + if (len) > + *len = le16_to_cpu(ent->len); > + > + return rsc_offset(rsc_hdr, ent); > +} > +EXPORT_SYMBOL(cmd_db_read_aux_data); > + > +/** > + * cmd_db_read_slave_id - Get the slave ID for a given resource address > + * > + * @id: Resource id to query the DB for version > + * > + * Return: cmd_db_hw_type enum on success, CMD_DB_HW_INVALID on error > + */ > +enum cmd_db_hw_type cmd_db_read_slave_id(const char *id) > +{ > + int ret; > + const struct entry_header *ent; > + u32 addr; > + > + ret = cmd_db_get_header(id, &ent, NULL); > + if (ret < 0) > + return CMD_DB_HW_INVALID; > + > + addr = le32_to_cpu(ent->addr); > + return (addr >> SLAVE_ID_SHIFT) & SLAVE_ID_MASK; > +} > +EXPORT_SYMBOL(cmd_db_read_slave_id); > + > +int cmd_db_init(ofnode node) > +{ > + void __iomem *base; > + > + debug("%s(%s)\n", __func__, ofnode_get_name(node)); > + > + base = (void __iomem *)ofnode_get_addr(node); > + if ((fdt_addr_t)base == FDT_ADDR_T_NONE) { > + log_err("%s: Failed to read base address\n", __func__); > + return -ENOENT; > + } > + > + cmd_db_header = base; > + if (!cmd_db_magic_matches(cmd_db_header)) { > + log_err("%s: Invalid Command DB Magic\n", __func__); > + return -EINVAL; > + } > + > + return 0; > +} > +EXPORT_SYMBOL(cmd_db_init); > diff --git a/drivers/soc/qcom/rpmh-internal.h > b/drivers/soc/qcom/rpmh-internal.h > new file mode 100644 > index 000000000000..9dbb1c51cc35 > --- /dev/null > +++ b/drivers/soc/qcom/rpmh-internal.h > @@ -0,0 +1,141 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. > + */ > + > +#ifndef __RPM_INTERNAL_H__ > +#define __RPM_INTERNAL_H__ > + > +#include <linux/bitmap.h> > +#include <soc/qcom/tcs.h> > + > +#define TCS_TYPE_NR 4 > +#define MAX_CMDS_PER_TCS 16 > +#define MAX_TCS_PER_TYPE 3 > +#define MAX_TCS_NR (MAX_TCS_PER_TYPE * TCS_TYPE_NR) > +#define MAX_TCS_SLOTS (MAX_CMDS_PER_TCS * MAX_TCS_PER_TYPE) > + > +#define USEC_PER_SEC 1000000UL > + > +struct rsc_drv; > + > +/** > + * struct tcs_group: group of Trigger Command Sets (TCS) to send state > requests > + * to the controller > + * > + * @drv: The controller. > + * @type: Type of the TCS in this group - active, sleep, wake. > + * @mask: Mask of the TCSes relative to all the TCSes in the RSC. > + * @offset: Start of the TCS group relative to the TCSes in the RSC. > + * @num_tcs: Number of TCSes in this type. > + * @ncpt: Number of commands in each TCS. > + * @req: Requests that are sent from the TCS; only used for ACTIVE_ONLY > + * transfers (could be on a wake/sleep TCS if we are borrowing > for > + * an ACTIVE_ONLY transfer). > + * Start: grab drv->lock, set req, set tcs_in_use, drop > drv->lock, > + * trigger > + * End: get irq, access req, > + * grab drv->lock, clear tcs_in_use, drop drv->lock > + * @slots: Indicates which of @cmd_addr are occupied; only used for > + * SLEEP / WAKE TCSs. Things are tightly packed in the > + * case that (ncpt < MAX_CMDS_PER_TCS). That is if ncpt = 2 and > + * MAX_CMDS_PER_TCS = 16 then bit[2] = the first bit in 2nd TCS. > + */ > +struct tcs_group { > + struct rsc_drv *drv; > + int type; > + u32 mask; > + u32 offset; > + int num_tcs; > + int ncpt; > + const struct tcs_request *req[MAX_TCS_PER_TYPE]; > + DECLARE_BITMAP(slots, MAX_TCS_SLOTS); > +}; > + > +/** > + * struct rpmh_request: the message to be sent to rpmh-rsc > + * > + * @msg: the request > + * @cmd: the payload that will be part of the @msg > + * @completion: triggered when request is done > + * @dev: the device making the request > + * @needs_free: check to free dynamically allocated request object > + */ > +struct rpmh_request { > + struct tcs_request msg; > + struct tcs_cmd cmd[MAX_RPMH_PAYLOAD]; > + const struct udevice *dev; > + bool needs_free; > +}; > + > +/** > + * struct rpmh_ctrlr: our representation of the controller > + * > + * @cache: the list of cached requests > + * @cache_lock: synchronize access to the cache data > + * @dirty: was the cache updated since flush > + * @batch_cache: Cache sleep and wake requests sent as batch > + */ > +struct rpmh_ctrlr { > + struct list_head cache; > + bool dirty; > + struct list_head batch_cache; > +}; > + > +struct rsc_ver { > + u32 major; > + u32 minor; > +}; > + > +/** > + * struct rsc_drv: the Direct Resource Voter (DRV) of the > + * Resource State Coordinator controller (RSC) > + * > + * @name: Controller identifier. > + * @base: Start address of the DRV registers in this > controller. > + * @tcs_base: Start address of the TCS registers in this > controller. > + * @id: Instance id in the controller (Direct Resource > Voter). > + * @num_tcs: Number of TCSes in this DRV. > + * @rsc_pm: CPU PM notifier for controller. > + * Used when solver mode is not present. > + * @cpus_in_pm: Number of CPUs not in idle power collapse. > + * Used when solver mode and "power-domains" is not > present. > + * @genpd_nb: PM Domain notifier for cluster genpd notifications. > + * @tcs: TCS groups. > + * @tcs_in_use: S/W state of the TCS; only set for ACTIVE_ONLY > + * transfers, but might show a sleep/wake TCS in use if > + * it was borrowed for an active_only transfer. You > + * must hold the lock in this struct (AKA drv->lock) in > + * order to update this. > + * @lock: Synchronize state of the controller. If RPMH's cache > + * lock will also be held, the order is: drv->lock then > + * cache_lock. > + * @tcs_wait: Wait queue used to wait for @tcs_in_use to free up a > + * slot > + * @client: Handle to the DRV's client. > + * @dev: RSC device. > + */ > +struct rsc_drv { > + const char *name; > + void __iomem *base; > + void __iomem *tcs_base; > + int id; > + int num_tcs; > + struct tcs_group tcs[TCS_TYPE_NR]; > + DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR); > + struct rpmh_ctrlr client; > + struct udevice *dev; > + struct rsc_ver ver; > + u32 *regs; > +}; > + > +int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg); > +int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, > + const struct tcs_request *msg); > +void rpmh_rsc_invalidate(struct rsc_drv *drv); > +void rpmh_rsc_write_next_wakeup(struct rsc_drv *drv); > + > +int rpmh_rsc_wait_for_resp(struct rsc_drv *drv, int tcs_id); > +int rpmh_flush(struct rpmh_ctrlr *ctrlr); > + > +#endif /* __RPM_INTERNAL_H__ */ > diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c > new file mode 100644 > index 000000000000..3c73dc0ffe25 > --- /dev/null > +++ b/drivers/soc/qcom/rpmh-rsc.c > @@ -0,0 +1,619 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2016-2018, 2020, The Linux Foundation. All rights reserved. > + * Copyright (c) 2023, Linaro Ltd. > + */ > + > +#include <linux/err.h> > +#include <linux/types.h> > +#include <dm.h> > +#include <dm/ofnode.h> > +#include <dm/devres.h> > +#include <dm/device_compat.h> > +#include <linux/delay.h> > +#include <dm/lists.h> > +#include <errno.h> > +#include <asm/io.h> > +#include <asm/bitops.h> > +#include <linux/bitmap.h> > +#include <log.h> > + > +#include <dt-bindings/soc/qcom,rpmh-rsc.h> > +#include "rpmh-internal.h" > + > +#include <soc/qcom/rpmh.h> > +#include <soc/qcom/cmd-db.h> > + > +#define RSC_DRV_ID 0 > + > +#define MAJOR_VER_MASK 0xFF > +#define MAJOR_VER_SHIFT 16 > +#define MINOR_VER_MASK 0xFF > +#define MINOR_VER_SHIFT 8 > + > +enum { > + RSC_DRV_TCS_OFFSET, > + RSC_DRV_CMD_OFFSET, > + DRV_SOLVER_CONFIG, > + DRV_PRNT_CHLD_CONFIG, > + RSC_DRV_IRQ_ENABLE, > + RSC_DRV_IRQ_STATUS, > + RSC_DRV_IRQ_CLEAR, > + RSC_DRV_CMD_WAIT_FOR_CMPL, > + RSC_DRV_CONTROL, > + RSC_DRV_STATUS, > + RSC_DRV_CMD_ENABLE, > + RSC_DRV_CMD_MSGID, > + RSC_DRV_CMD_ADDR, > + RSC_DRV_CMD_DATA, > + RSC_DRV_CMD_STATUS, > + RSC_DRV_CMD_RESP_DATA, > +}; > + > +/* DRV HW Solver Configuration Information Register */ > +#define DRV_HW_SOLVER_MASK 1 > +#define DRV_HW_SOLVER_SHIFT 24 > + > +/* DRV TCS Configuration Information Register */ > +#define DRV_NUM_TCS_MASK 0x3F > +#define DRV_NUM_TCS_SHIFT 6 > +#define DRV_NCPT_MASK 0x1F > +#define DRV_NCPT_SHIFT 27 > + > +/* Offsets for CONTROL TCS Registers */ > +#define RSC_DRV_CTL_TCS_DATA_HI 0x38 > +#define RSC_DRV_CTL_TCS_DATA_HI_MASK 0xFFFFFF > +#define RSC_DRV_CTL_TCS_DATA_HI_VALID BIT(31) > +#define RSC_DRV_CTL_TCS_DATA_LO 0x40 > +#define RSC_DRV_CTL_TCS_DATA_LO_MASK 0xFFFFFFFF > +#define RSC_DRV_CTL_TCS_DATA_SIZE 32 > + > +#define TCS_AMC_MODE_ENABLE BIT(16) > +#define TCS_AMC_MODE_TRIGGER BIT(24) > + > +/* TCS CMD register bit mask */ > +#define CMD_MSGID_LEN 8 > +#define CMD_MSGID_RESP_REQ BIT(8) > +#define CMD_MSGID_WRITE BIT(16) > +#define CMD_STATUS_ISSUED BIT(8) > +#define CMD_STATUS_COMPL BIT(16) > + > +/* > + * Here's a high level overview of how all the registers in RPMH work > + * together: > + * > + * - The main rpmh-rsc address is the base of a register space that can > + * be used to find overall configuration of the hardware > + * (DRV_PRNT_CHLD_CONFIG). Also found within the rpmh-rsc register > + * space are all the TCS blocks. The offset of the TCS blocks is > + * specified in the device tree by "qcom,tcs-offset" and used to > + * compute tcs_base. > + * - TCS blocks come one after another. Type, count, and order are > + * specified by the device tree as "qcom,tcs-config". > + * - Each TCS block has some registers, then space for up to 16 commands. > + * Note that though address space is reserved for 16 commands, fewer > + * might be present. See ncpt (num cmds per TCS). > + * > + * Here's a picture: > + * > + * +---------------------------------------------------+ > + * |RSC | > + * | ctrl | > + * | | > + * | Drvs: | > + * | +-----------------------------------------------+ | > + * | |DRV0 | | > + * | | ctrl/config | | > + * | | IRQ | | > + * | | | | > + * | | TCSes: | | > + * | | +------------------------------------------+ | | > + * | | |TCS0 | | | | | | | | | | | | | | | > + * | | | ctrl | 0| 1| 2| 3| 4| 5| .| .| .| .|14|15| | | > + * | | | | | | | | | | | | | | | | | | > + * | | +------------------------------------------+ | | > + * | | +------------------------------------------+ | | > + * | | |TCS1 | | | | | | | | | | | | | | | > + * | | | ctrl | 0| 1| 2| 3| 4| 5| .| .| .| .|14|15| | | > + * | | | | | | | | | | | | | | | | | | > + * | | +------------------------------------------+ | | > + * | | +------------------------------------------+ | | > + * | | |TCS2 | | | | | | | | | | | | | | | > + * | | | ctrl | 0| 1| 2| 3| 4| 5| .| .| .| .|14|15| | | > + * | | | | | | | | | | | | | | | | | | > + * | | +------------------------------------------+ | | > + * | | ...... | | > + * | +-----------------------------------------------+ | > + * | +-----------------------------------------------+ | > + * | |DRV1 | | > + * | | (same as DRV0) | | > + * | +-----------------------------------------------+ | > + * | ...... | > + * +---------------------------------------------------+ > + */ > + > +static u32 rpmh_rsc_reg_offset_ver_2_7[] = { > + [RSC_DRV_TCS_OFFSET] = 672, > + [RSC_DRV_CMD_OFFSET] = 20, > + [DRV_SOLVER_CONFIG] = 0x04, > + [DRV_PRNT_CHLD_CONFIG] = 0x0C, > + [RSC_DRV_IRQ_ENABLE] = 0x00, > + [RSC_DRV_IRQ_STATUS] = 0x04, > + [RSC_DRV_IRQ_CLEAR] = 0x08, > + [RSC_DRV_CMD_WAIT_FOR_CMPL] = 0x10, > + [RSC_DRV_CONTROL] = 0x14, > + [RSC_DRV_STATUS] = 0x18, > + [RSC_DRV_CMD_ENABLE] = 0x1C, > + [RSC_DRV_CMD_MSGID] = 0x30, > + [RSC_DRV_CMD_ADDR] = 0x34, > + [RSC_DRV_CMD_DATA] = 0x38, > + [RSC_DRV_CMD_STATUS] = 0x3C, > + [RSC_DRV_CMD_RESP_DATA] = 0x40, > +}; > + > +static inline void __iomem * > +tcs_reg_addr(const struct rsc_drv *drv, int reg, int tcs_id) > +{ > + return drv->tcs_base + drv->regs[RSC_DRV_TCS_OFFSET] * tcs_id + reg; > +} > + > +static inline void __iomem * > +tcs_cmd_addr(const struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) > +{ > + return tcs_reg_addr(drv, reg, tcs_id) + drv->regs[RSC_DRV_CMD_OFFSET] > * cmd_id; > +} > + > +static u32 read_tcs_reg(const struct rsc_drv *drv, int reg, int tcs_id) > +{ > + return readl_relaxed(tcs_reg_addr(drv, reg, tcs_id)); > +} > + > +static void write_tcs_cmd(const struct rsc_drv *drv, int reg, int tcs_id, > + int cmd_id, u32 data) > +{ > + void __iomem *addr = tcs_cmd_addr(drv, reg, tcs_id, cmd_id); > + > + debug("%s: tcs(m): %d cmd(n): %d addr: %#x data: %#x\n", drv->name, > + tcs_id, cmd_id, reg, data); > + writel_relaxed(data, addr); > +} > + > +static void write_tcs_reg(const struct rsc_drv *drv, int reg, int tcs_id, > + u32 data) > +{ > + void __iomem *addr = tcs_reg_addr(drv, reg, tcs_id); > + > + debug("%s: tcs(m): %d addr: %#x data: %#x\n", drv->name, > + tcs_id, reg, data); > + writel_relaxed(data, addr); > +} > + > +static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int > tcs_id, > + u32 data) > +{ > + int i; > + void __iomem *addr = tcs_reg_addr(drv, reg, tcs_id); > + > + debug("%s: tcs(m): %d addr: %#x data: %#x\n", drv->name, > + tcs_id, reg, data); > + > + writel(data, addr); > + > + /* > + * Wait until we read back the same value. Use a counter rather than > + * ktime for timeout since this may be called after timekeeping stops. > + */ > + for (i = 0; i < USEC_PER_SEC; i++) { > + if (readl(addr) == data) > + return; > + udelay(1); > + } Can we rather use readx_poll_sleep_timeout() here instead? > + pr_err("%s: error writing %#x to %d:%#x\n", drv->name, > + data, tcs_id, reg); > +} > + > +/** > + * tcs_invalidate() - Invalidate all TCSes of the given type (sleep or wake). > + * @drv: The RSC controller. > + * @type: SLEEP_TCS or WAKE_TCS > + * > + * This will clear the "slots" variable of the given tcs_group and also > + * tell the hardware to forget about all entries. > + * > + * The caller must ensure that no other RPMH actions are happening when this > + * function is called, since otherwise the device may immediately become > + * used again even before this function exits. > + */ > +static void tcs_invalidate(struct rsc_drv *drv, int type) > +{ > + int m; > + struct tcs_group *tcs = &drv->tcs[type]; > + > + /* Caller ensures nobody else is running so no lock */ > + if (bitmap_empty(tcs->slots, MAX_TCS_SLOTS)) > + return; > + > + for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) > + write_tcs_reg_sync(drv, drv->regs[RSC_DRV_CMD_ENABLE], m, 0); > + > + bitmap_zero(tcs->slots, MAX_TCS_SLOTS); > +} > + > +/** > + * rpmh_rsc_invalidate() - Invalidate sleep and wake TCSes. > + * @drv: The RSC controller. > + * > + * The caller must ensure that no other RPMH actions are happening when this > + * function is called, since otherwise the device may immediately become > + * used again even before this function exits. > + */ > +void rpmh_rsc_invalidate(struct rsc_drv *drv) > +{ > + tcs_invalidate(drv, SLEEP_TCS); > + tcs_invalidate(drv, WAKE_TCS); > +} > + > +/** > + * rpmh_rsc_wait_for_resp() - Spin until we get a response from the rpmh > + * @drv: The controller. > + * @tcs_id: The global ID of this TCS. > + * > + * This is for ACTIVE_ONLY transfers (which are the only ones we support in > + * u-boot). As we don't support interrupts, we just spin on the IRQ_STATUS > + * register until the bit is set to confirm that the TCS TX is done. > + */ > +int rpmh_rsc_wait_for_resp(struct rsc_drv *drv, int tcs_id) > +{ > + u32 reg; > + int i; > + > + reg = drv->regs[RSC_DRV_IRQ_STATUS]; > + > + debug("%s: waiting for response on tcs %d\n", __func__, tcs_id); > + > + for (i = 0; i < 5 * USEC_PER_SEC; i++) { > + if (readl(tcs_reg_addr(drv, reg, tcs_id)) & BIT(tcs_id)) > + break; > + udelay(1); > + } Can we rather use readx_poll_sleep_timeout() here instead? > + > + if (i == 5 * USEC_PER_SEC) { > + printf("%s: timeout waiting for response\n", drv->name); > + return -ETIMEDOUT; > + } > + > + writel_relaxed(BIT(tcs_id), drv->tcs_base + > drv->regs[RSC_DRV_IRQ_CLEAR]); > + > + return 0; > +} > + > +/** > + * __tcs_buffer_write() - Write to TCS hardware from a request; don't > trigger. > + * @drv: The controller. > + * @tcs_id: The global ID of this TCS. > + * @cmd_id: The index within the TCS to start writing. > + * @msg: The message we want to send, which will contain several addr/data > + * pairs to program (but few enough that they all fit in one TCS). > + * > + * This is used for all types of transfers (active, sleep, and wake). > + */ > +static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, > + const struct tcs_request *msg) > +{ > + u32 msgid; > + u32 cmd_msgid = CMD_MSGID_LEN | CMD_MSGID_WRITE; > + u32 cmd_enable = 0; > + struct tcs_cmd *cmd; > + int i, j; > + > + /* u-boot: get a response to ensure everything is golden before > continuing */ > + cmd_msgid |= CMD_MSGID_RESP_REQ; > + > + for (i = 0, j = cmd_id; i < msg->num_cmds; i++, j++) { > + cmd = &msg->cmds[i]; > + cmd_enable |= BIT(j); > + msgid = cmd_msgid; > + /* > + * Additionally, if the cmd->wait is set, make the command > + * response reqd even if the overall request was > fire-n-forget. > + */ > + msgid |= cmd->wait ? CMD_MSGID_RESP_REQ : 0; > + > + write_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_MSGID], tcs_id, j, > msgid); > + write_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_ADDR], tcs_id, j, > cmd->addr); > + write_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_DATA], tcs_id, j, > cmd->data); > + debug("%s: tcs(m): %d [%s] cmd(n): %d msgid: %#x addr: %#x > data: %#x complete: %d\n", > + drv->name, tcs_id, msg->state == RPMH_ACTIVE_ONLY_STATE > ? "active" : "?", j, msgid, > + cmd->addr, cmd->data, cmd->wait); > + } > + > + cmd_enable |= read_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], > tcs_id); > + write_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], tcs_id, cmd_enable); > +} > + > +/** > + * __tcs_set_trigger() - Start xfer on a TCS or unset trigger on a borrowed > TCS > + * @drv: The controller. > + * @tcs_id: The global ID of this TCS. > + * @trigger: If true then untrigger/retrigger. If false then just untrigger. > + * > + * In the normal case we only ever call with "trigger=true" to start a > + * transfer. That will un-trigger/disable the TCS from the last transfer > + * then trigger/enable for this transfer. > + * > + * If we borrowed a wake TCS for an active-only transfer we'll also call > + * this function with "trigger=false" to just do the un-trigger/disable > + * before using the TCS for wake purposes again. > + * > + * Note that the AP is only in charge of triggering active-only transfers. > + * The AP never triggers sleep/wake values using this function. > + */ > +static void __tcs_set_trigger(struct rsc_drv *drv, int tcs_id, bool trigger) > +{ > + u32 enable; > + u32 reg = drv->regs[RSC_DRV_CONTROL]; > + > + /* > + * HW req: Clear the DRV_CONTROL and enable TCS again > + * While clearing ensure that the AMC mode trigger is cleared > + * and then the mode enable is cleared. > + */ > + enable = read_tcs_reg(drv, reg, tcs_id); > + enable &= ~TCS_AMC_MODE_TRIGGER; > + write_tcs_reg_sync(drv, reg, tcs_id, enable); > + enable &= ~TCS_AMC_MODE_ENABLE; > + write_tcs_reg_sync(drv, reg, tcs_id, enable); > + > + if (trigger) { > + /* Enable the AMC mode on the TCS and then trigger the TCS */ > + enable = TCS_AMC_MODE_ENABLE; > + write_tcs_reg_sync(drv, reg, tcs_id, enable); > + enable |= TCS_AMC_MODE_TRIGGER; > + write_tcs_reg(drv, reg, tcs_id, enable); > + } > +} > + > +/** > + * get_tcs_for_msg() - Get the tcs_group used to send the given message. > + * @drv: The RSC controller. > + * @msg: The message we want to send. > + * > + * This is normally pretty straightforward except if we are trying to send > + * an ACTIVE_ONLY message but don't have any active_only TCSes. > + * > + * Return: A pointer to a tcs_group or an ERR_PTR. > + */ > +static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, > + const struct tcs_request *msg) > +{ > + if (msg->state != RPMH_ACTIVE_ONLY_STATE) { > + printf("WARN: only ACTIVE_ONLY state supported\n"); > + return ERR_PTR(-EINVAL); > + } > + > + return &drv->tcs[ACTIVE_TCS]; > +} > + > +/** > + * rpmh_rsc_send_data() - Write / trigger active-only message. > + * @drv: The controller. > + * @msg: The data to be sent. > + * > + * NOTES: > + * - This is only used for "ACTIVE_ONLY" since the limitations of this > + * function don't make sense for sleep/wake cases. > + * - To do the transfer, we will grab a whole TCS for ourselves--we don't > + * try to share. If there are none available we'll wait indefinitely > + * for a free one. > + * - This function will not wait for the commands to be finished, only for > + * data to be programmed into the RPMh. See rpmh_tx_done() which will > + * be called when the transfer is fully complete. > + * - This function must be called with interrupts enabled. If the hardware > + * is busy doing someone else's transfer we need that transfer to fully > + * finish so that we can have the hardware, and to fully finish it needs > + * the interrupt handler to run. If the interrupts is set to run on the > + * active CPU this can never happen if interrupts are disabled. > + * > + * Return: 0 on success, -EINVAL on error. > + */ > +int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) > +{ > + struct tcs_group *tcs; > + int tcs_id; > + unsigned long flags; > + > + tcs = get_tcs_for_msg(drv, msg); > + if (IS_ERR(tcs)) > + return PTR_ERR(tcs); > + > + spin_lock_irqsave(&drv->lock, flags); Locks aren't needed in U-Boot, can be dropped here and other places. > + > + /* u-boot is single-threaded, always use the first TCS as we'll never > conflict */ > + tcs_id = tcs->offset; > + > + tcs->req[tcs_id - tcs->offset] = msg; > + generic_set_bit(tcs_id, drv->tcs_in_use); > + if (msg->state == RPMH_ACTIVE_ONLY_STATE && tcs->type != ACTIVE_TCS) { > + /* > + * Clear previously programmed WAKE commands in selected > + * repurposed TCS to avoid triggering them. tcs->slots will be > + * cleaned from rpmh_flush() by invoking rpmh_rsc_invalidate() > + */ > + write_tcs_reg_sync(drv, drv->regs[RSC_DRV_CMD_ENABLE], > tcs_id, 0); > + } > + spin_unlock_irqrestore(&drv->lock, flags); > + > + /* > + * These two can be done after the lock is released because: > + * - We marked "tcs_in_use" under lock. > + * - Once "tcs_in_use" has been marked nobody else could be writing > + * to these registers until the interrupt goes off. > + * - The interrupt can't go off until we trigger w/ the last line > + * of __tcs_set_trigger() below. > + */ > + __tcs_buffer_write(drv, tcs_id, 0, msg); > + __tcs_set_trigger(drv, tcs_id, true); > + > + rpmh_rsc_wait_for_resp(drv, tcs_id); > + > + return 0; > +} > + > +static int rpmh_probe_tcs_config(struct udevice *dev, struct rsc_drv *drv) > +{ > + struct tcs_type_config { > + u32 type; > + u32 n; > + } tcs_cfg[TCS_TYPE_NR] = { { 0 } }; > + ofnode dn = dev_ofnode(dev); > + u32 config, max_tcs, ncpt, offset; > + int i, ret, n, st = 0; > + struct tcs_group *tcs; > + > + ret = ofnode_read_u32(dn, "qcom,tcs-offset", &offset); > + if (ret) > + return ret; > + drv->tcs_base = drv->base + offset; > + > + config = readl_relaxed(drv->base + drv->regs[DRV_PRNT_CHLD_CONFIG]); > + > + max_tcs = config; > + max_tcs &= DRV_NUM_TCS_MASK << (DRV_NUM_TCS_SHIFT * drv->id); > + max_tcs = max_tcs >> (DRV_NUM_TCS_SHIFT * drv->id); > + > + ncpt = config & (DRV_NCPT_MASK << DRV_NCPT_SHIFT); > + ncpt = ncpt >> DRV_NCPT_SHIFT; > + > + n = ofnode_read_u32_array(dn, "qcom,tcs-config", (u32 *)tcs_cfg, 2 * > TCS_TYPE_NR); > + if (n < 0) { > + printf("RPMh: %s: error reading qcom,tcs-config %d\n", > dev->name, n); > + return n; > + } > + > + for (i = 0; i < TCS_TYPE_NR; i++) { > + if (tcs_cfg[i].n > MAX_TCS_PER_TYPE) > + return -EINVAL; > + } > + > + for (i = 0; i < TCS_TYPE_NR; i++) { > + tcs = &drv->tcs[tcs_cfg[i].type]; > + if (tcs->drv) > + return -EINVAL; > + tcs->drv = drv; > + tcs->type = tcs_cfg[i].type; > + tcs->num_tcs = tcs_cfg[i].n; > + tcs->ncpt = ncpt; > + > + if (!tcs->num_tcs || tcs->type == CONTROL_TCS) > + continue; > + > + if (st + tcs->num_tcs > max_tcs || > + st + tcs->num_tcs >= BITS_PER_BYTE * sizeof(tcs->mask)) > + return -EINVAL; > + > + tcs->mask = ((1 << tcs->num_tcs) - 1) << st; > + tcs->offset = st; > + st += tcs->num_tcs; > + } > + > + drv->num_tcs = st; > + > + return 0; > +} > + > +static int rpmh_rsc_probe(struct udevice *dev) > +{ > + ofnode dn = dev_ofnode(dev); > + ofnode rmem, node; > + struct rsc_drv *drv; > + char drv_id[10] = {0}; > + int ret; > + u32 rsc_id; > + > + /* > + * Even though RPMh doesn't directly use cmd-db, all of its children > + * do. We init cmd-db here or bail out if we can't. All child devices > + * can therefore safely assume that cmd-db is available. > + */ > + rmem = ofnode_path("/reserved-memory"); > + ofnode_for_each_subnode(node, rmem) { > + if (ofnode_device_is_compatible(node, "qcom,cmd-db")) > + goto found; > + } > + > + printf("Couldn't find qcom,cmd-db node!\n"); > + return -ENODEV; > +found: > + ret = cmd_db_init(node); > + if (ret < 0) { > + printf("Couldn't init cmd-db!\n"); > + return ret; > + } > + > + drv = dev_get_priv(dev); > + > + ret = ofnode_read_u32(dn, "qcom,drv-id", &drv->id); > + if (ret) > + return ret; > + > + drv->name = ofnode_get_property(dn, "label", NULL); > + if (!drv->name) > + drv->name = dev->name; > + > + snprintf(drv_id, ARRAY_SIZE(drv_id), "drv-%d", drv->id); > + drv->base = (void __iomem*)dev_read_addr_name(dev, drv_id); > + if (IS_ERR(drv->base)) > + return PTR_ERR(drv->base); > + > + rsc_id = readl_relaxed(drv->base + RSC_DRV_ID); > + drv->ver.major = rsc_id & (MAJOR_VER_MASK << MAJOR_VER_SHIFT); > + drv->ver.major >>= MAJOR_VER_SHIFT; > + drv->ver.minor = rsc_id & (MINOR_VER_MASK << MINOR_VER_SHIFT); > + drv->ver.minor >>= MINOR_VER_SHIFT; > + > + if (drv->ver.major == 3) { > + printf("RPMh v3 not supported\n"); > + return -EOPNOTSUPP; > + } else { > + drv->regs = rpmh_rsc_reg_offset_ver_2_7; > + } > + > + ret = rpmh_probe_tcs_config(dev, drv); > + if (ret) > + return ret; > + > + spin_lock_init(&drv->lock); > + init_waitqueue_head(&drv->tcs_wait); Similarly waitqueue should be dropped too. -Sumit > + bitmap_zero(drv->tcs_in_use, MAX_TCS_NR); > + > + /* Enable the active TCS to send requests immediately */ > + writel_relaxed(drv->tcs[ACTIVE_TCS].mask, > + drv->tcs_base + drv->regs[RSC_DRV_IRQ_ENABLE]); > + > + spin_lock_init(&drv->client.cache_lock); > + INIT_LIST_HEAD(&drv->client.cache); > + INIT_LIST_HEAD(&drv->client.batch_cache); > + > + dev_set_drvdata(dev, drv); > + drv->dev = dev; > + > + log_debug("RPMh: %s: v%d.%d\n", dev->name, drv->ver.major, > drv->ver.minor); > + > + return ret; > +} > + > +static const struct udevice_id qcom_rpmh_ids[] = { > + { .compatible = "qcom,rpmh-rsc" }, > + { } > +}; > + > +U_BOOT_DRIVER(qcom_rpmh_rsc) = { > + .name = "qcom_rpmh_rsc", > + .id = UCLASS_MISC, > + .priv_auto = sizeof(struct rsc_drv), > + .probe = rpmh_rsc_probe, > + .bind = dm_scan_fdt_dev, > + .of_match = qcom_rpmh_ids, > + /* rpmh is under CLUSTER_PD which we don't support */ > + .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF, > +}; > diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c > new file mode 100644 > index 000000000000..f00c241de373 > --- /dev/null > +++ b/drivers/soc/qcom/rpmh.c > @@ -0,0 +1,110 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. > + */ > + > +#include <linux/bug.h> > +#include <linux/kernel.h> > +#include <linux/list.h> > +#include <linux/types.h> > +#include <dm/device.h> > + > +#include <soc/qcom/rpmh.h> > + > +#include "rpmh-internal.h" > + > +#define RPMH_TIMEOUT_MS msecs_to_jiffies(10000) > + > +#define DEFINE_RPMH_MSG_ONSTACK(device, s, name) \ > + struct rpmh_request name = { \ > + .msg = { \ > + .state = s, \ > + .cmds = name.cmd, \ > + .num_cmds = 0, \ > + }, \ > + .cmd = { { 0 } }, \ > + .dev = device, \ > + .needs_free = false, \ > + } > + > +#define ctrlr_to_drv(ctrlr) container_of(ctrlr, struct rsc_drv, client) > + > +static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct udevice *dev) > +{ > + struct rsc_drv *drv = (struct rsc_drv *)dev_get_priv(dev->parent); > + > + if (!drv) { > + printf("BUG: no RPMh driver for %s (parent %s)\n", dev->name, > dev->parent->name); > + BUG(); > + } > + > + return &drv->client; > +} > + > +/** > + * __rpmh_write: Cache and send the RPMH request > + * > + * @dev: The device making the request > + * @state: Active/Sleep request type > + * @rpm_msg: The data that needs to be sent (cmds). > + * > + * Cache the RPMH request and send if the state is ACTIVE_ONLY. > + * SLEEP/WAKE_ONLY requests are not sent to the controller at > + * this time. Use rpmh_flush() to send them to the controller. > + */ > +static int __rpmh_write(const struct udevice *dev, enum rpmh_state state, > + struct rpmh_request *rpm_msg) > +{ > + struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); > + > + debug("rpmh_write: %s, %d\n", dev->name, state); > + > + if (state != RPMH_ACTIVE_ONLY_STATE) { > + printf("WARN: only ACTIVE_ONLY state supported\n"); > + return -EINVAL; > + } > + > + return rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg); > +} > + > +static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state, > + const struct tcs_cmd *cmd, u32 n) > +{ > + if (!cmd || !n || n > MAX_RPMH_PAYLOAD) > + return -EINVAL; > + > + memcpy(req->cmd, cmd, n * sizeof(*cmd)); > + > + req->msg.state = state; > + req->msg.cmds = req->cmd; > + req->msg.num_cmds = n; > + > + debug("rpmh_msg: %d, %d cmds [first %#x/%#x]\n", state, n, cmd->addr, > cmd->data); > + > + return 0; > +} > + > +/** > + * rpmh_write: Write a set of RPMH commands and block until response > + * > + * @dev: The device making the request > + * @state: Active/sleep set > + * @cmd: The payload data > + * @n: The number of elements in @cmd > + * > + * May sleep. Do not call from atomic contexts. > + */ > +int rpmh_write(const struct udevice *dev, enum rpmh_state state, > + const struct tcs_cmd *cmd, u32 n) > +{ > + DEFINE_RPMH_MSG_ONSTACK(dev, state, rpm_msg); > + int ret; > + > + ret = __fill_rpmh_msg(&rpm_msg, state, cmd, n); > + if (ret) > + return ret; > + > + ret = __rpmh_write(dev, state, &rpm_msg); > + > + return ret; > +} > diff --git a/include/soc/qcom/cmd-db.h b/include/soc/qcom/cmd-db.h > new file mode 100644 > index 000000000000..1e22d86c66c9 > --- /dev/null > +++ b/include/soc/qcom/cmd-db.h > @@ -0,0 +1,42 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */ > + > +#ifndef __QCOM_COMMAND_DB_H__ > +#define __QCOM_COMMAND_DB_H__ > + > +#include <linux/err.h> > +#include <dm/device.h> > + > +enum cmd_db_hw_type { > + CMD_DB_HW_INVALID = 0, > + CMD_DB_HW_MIN = 3, > + CMD_DB_HW_ARC = CMD_DB_HW_MIN, > + CMD_DB_HW_VRM = 4, > + CMD_DB_HW_BCM = 5, > + CMD_DB_HW_MAX = CMD_DB_HW_BCM, > + CMD_DB_HW_ALL = 0xff, > +}; > + > +#if IS_ENABLED(CONFIG_QCOM_COMMAND_DB) > +u32 cmd_db_read_addr(const char *resource_id); > + > +const void *cmd_db_read_aux_data(const char *resource_id, size_t *len); > + > +enum cmd_db_hw_type cmd_db_read_slave_id(const char *resource_id); > + > +int cmd_db_init(ofnode node); > + > +#else > +static inline u32 cmd_db_read_addr(const char *resource_id) > +{ return 0; } > + > +static inline const void *cmd_db_read_aux_data(const char *resource_id, > size_t *len) > +{ return ERR_PTR(-ENODEV); } > + > +static inline enum cmd_db_hw_type cmd_db_read_slave_id(const char > *resource_id) > +{ return -ENODEV; } > + > +static inline int cmd_db_ready(void) > +{ return -ENODEV; } > +#endif /* CONFIG_QCOM_COMMAND_DB */ > +#endif /* __QCOM_COMMAND_DB_H__ */ > diff --git a/include/soc/qcom/rpmh.h b/include/soc/qcom/rpmh.h > new file mode 100644 > index 000000000000..eb7c067cc5fd > --- /dev/null > +++ b/include/soc/qcom/rpmh.h > @@ -0,0 +1,29 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. > + */ > + > +#ifndef __SOC_QCOM_RPMH_H__ > +#define __SOC_QCOM_RPMH_H__ > + > +#include <linux/types.h> > +#include <linux/errno.h> > +#include <soc/qcom/tcs.h> > +#include <dm/device-internal.h> > + > +#if IS_ENABLED(CONFIG_QCOM_RPMH) > +int rpmh_write(const struct udevice *dev, enum rpmh_state state, > + const struct tcs_cmd *cmd, u32 n); > + > +#else > + > +static inline int rpmh_write(const struct udevice *dev, enum rpmh_state > state, > + const struct tcs_cmd *cmd, u32 n) > +{ return -ENODEV; } > + > +#endif /* CONFIG_QCOM_RPMH */ > + > +/* u-boot: no multithreading */ > +#define rpmh_write_async(dev, state, cmd, n) rpmh_write(dev, state, cmd, n) > + > +#endif /* __SOC_QCOM_RPMH_H__ */ > diff --git a/include/soc/qcom/tcs.h b/include/soc/qcom/tcs.h > new file mode 100644 > index 000000000000..c8d28b052f1d > --- /dev/null > +++ b/include/soc/qcom/tcs.h > @@ -0,0 +1,78 @@ > +/* SPDX-License-Identifier: GPL-2.0 */ > +/* > + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. > + */ > + > +#ifndef __SOC_QCOM_TCS_H__ > +#define __SOC_QCOM_TCS_H__ > + > +#define MAX_RPMH_PAYLOAD 16 > + > +/** > + * rpmh_state: state for the request > + * > + * RPMH_SLEEP_STATE: State of the resource when the processor subsystem > + * is powered down. There is no client using the > + * resource actively. > + * RPMH_WAKE_ONLY_STATE: Resume resource state to the value previously > + * requested before the processor was powered down. > + * RPMH_ACTIVE_ONLY_STATE: Active or AMC mode requests. Resource state > + * is aggregated immediately. > + */ > +enum rpmh_state { > + RPMH_SLEEP_STATE, > + RPMH_WAKE_ONLY_STATE, > + RPMH_ACTIVE_ONLY_STATE, > +}; > + > +/** > + * struct tcs_cmd: an individual request to RPMH. > + * > + * @addr: the address of the resource slv_id:18:16 | offset:0:15 > + * @data: the resource state request > + * @wait: ensure that this command is complete before returning. > + * Setting "wait" here only makes sense during rpmh_write_batch() for > + * active-only transfers, this is because: > + * rpmh_write() - Always waits. > + * (DEFINE_RPMH_MSG_ONSTACK will set .wait_for_compl) > + * rpmh_write_async() - Never waits. > + * (There's no request completion callback) > + */ > +struct tcs_cmd { > + u32 addr; > + u32 data; > + u32 wait; > +}; > + > +/** > + * struct tcs_request: A set of tcs_cmds sent together in a TCS > + * > + * @state: state for the request. > + * @num_cmds: the number of @cmds in this request > + * @cmds: an array of tcs_cmds > + */ > +struct tcs_request { > + enum rpmh_state state; > + u32 num_cmds; > + struct tcs_cmd *cmds; > +}; > + > +#define BCM_TCS_CMD_COMMIT_SHFT 30 > +#define BCM_TCS_CMD_COMMIT_MASK 0x40000000 > +#define BCM_TCS_CMD_VALID_SHFT 29 > +#define BCM_TCS_CMD_VALID_MASK 0x20000000 > +#define BCM_TCS_CMD_VOTE_X_SHFT 14 > +#define BCM_TCS_CMD_VOTE_MASK 0x3fff > +#define BCM_TCS_CMD_VOTE_Y_SHFT 0 > +#define BCM_TCS_CMD_VOTE_Y_MASK 0xfffc000 > + > +/* Construct a Bus Clock Manager (BCM) specific TCS command */ > +#define BCM_TCS_CMD(commit, valid, vote_x, vote_y) \ > + (((commit) << BCM_TCS_CMD_COMMIT_SHFT) | \ > + ((valid) << BCM_TCS_CMD_VALID_SHFT) | \ > + ((cpu_to_le32(vote_x) & \ > + BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_X_SHFT) | \ > + ((cpu_to_le32(vote_y) & \ > + BCM_TCS_CMD_VOTE_MASK) << BCM_TCS_CMD_VOTE_Y_SHFT)) > + > +#endif /* __SOC_QCOM_TCS_H__ */ > > -- > 2.45.0 >