[PATCH v5 00/23] qcom: rpmh core and regulator support

This series introduces support for the RPMh (Resource Power Manager (hardened)) co-processor and associated regulator driver found on most modern Qualcomm platforms (since ~2017).
Even though most regulators are controlled via SPMI, the specific regions on the PMICs for controlling the regulators are restricted and can't be accessed by the Applications Processor (AP/HLOS). Instead, these resources are proxied via the RPMh where they can be voted on by multiple subsystems (Linux, the modem, and the other DSPs). This is done for security (to protect clocks, power domains, and regulators that are specifically relevant for the trustzone) as well as to simplify the management of shared peripherals and to allow for handover of peripherals like the USB controller.
For U-Boot, our main concern is the regulators. Since all regulators on the system are controlled via the RPMh, it is necessary to support it to enable USB VBUS on some platforms, and may be needed for other peripherals in the future.
Communicating with the RPMh additional requires accessing the cmd-db shared memory region, this contains key/value maps to determine the address of specific resources on the RPMh.
Introduce support for the cmd-db, the RPMh framework, and some of the regulators that are necessary to enable USB VBUS on the RB5 development board.
These drivers are taken from Linux, then modified and simplified for U-Boot. The original Linux drivers contain heavy optimisations related to multithreading and asynchronous probing, as well as support for idle and suspend states which we don't need to deal with here. This unused code is removed before finally adjusting the drivers to properly build for U-Boot and use its device model.
The U-Boot version of the driver supports a single ACTIVE_ONLY TCS and waits for it to be cleared after use. We don't support programming low power states.
--- Changes in v5: - Add Kconfig / Makefiles after introducing drivers to avoid breaking compilation when bisecting (Thanks Neil). - Link to v4: https://lore.kernel.org/r/20240709-b4-qcom-rpmh-v4-0-c06d0a26644a@linaro.org
Changes in v4: - Denote original Linux version in bitmap.h patch - Rebased on Linux 6.10-rc6 and re-apply U-Boot changes preserving git history. Allowing for future changes to the Linux drivers to be ported over more easily. - Add missing check to wait for the TCS to be cleared after use (seems we were just racing the RPMh before, oops!). - Fix missing n_modes in pmic5_pldo regulator. - Link to v3: https://lore.kernel.org/r/20240708-b4-qcom-rpmh-v3-0-846cc6c5b728@linaro.org
Changes in v3: - Don't call dm_scan_fdt_dev(), since DM core will scan. - Link to v2: https://lore.kernel.org/r/20240708-b4-qcom-rpmh-v2-0-8bc765606e56@linaro.org
Changes in v2: - Implement Neil's suggestions and fixes for SM8[56]50 - Slightly refactor cmd_db_init() for better abstraction. - Improve logging (printf -> log_err/dev_err) - Add missing error check in rpmh_regulators_bind() - Link to v1: https://lore.kernel.org/r/20240617-b4-qcom-rpmh-v1-0-bd2336923e0a@linaro.org
--- Caleb Connolly (23): linux/bitmap.h: add bitmap_empty helper soc: qcom: import rpmh and cmd-db drivers from Linux soc: qcom: cmd-db: adjust headers for U-Boot soc: qcom: cmd-db: drop unused functions soc: qcom: cmd-db: replace cmd_db_ready() with cmd_db_init() soc: qcom: cmd-db: adjust for U-Boot API soc: qcom: rpmh-rsc: drop unused multi-threading and non-active TCS support soc: qcom: rpmh-rsc: adjust headers for U-Boot soc: qcom: rpmh-rsc: adjust probe for U-Boot soc: qcom: rpmh-rsc: remaining U-Boot API changes soc: qcom: rpmh: adjust headers for U-Boot soc: qcom: rpmh: drop unused functions soc: qcom: rpmh: U-Boot API changes soc: qcom: add build infrastructure power: regulator: import qcom-rpmh-regulator from Linux power: regulator: qcom-rpmh-regulator: adjust headers for U-Boot power: regulator: qcom-rpmh-regulator: port over lineage_range helpers power: regulator: qcom-rpmh-regulator: adjust structs for U-Boot power: regulator: qcom-rpmh-regulator: remove unused regulators power: regulator: qcom-rpmh-regulator: port ops to U-Boot power: regulator: qcom-rpmh-regulator: adjust probe for U-Boot power: regulator: qcom-rpmh-regulator: add build infra qcom_defconfig: enable rpmh regulators
configs/qcom_defconfig | 5 + drivers/power/regulator/Kconfig | 8 + drivers/power/regulator/Makefile | 1 + drivers/power/regulator/qcom-rpmh-regulator.c | 544 ++++++++++++++++++++++++++ drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/qcom/Kconfig | 27 ++ drivers/soc/qcom/Makefile | 4 + drivers/soc/qcom/cmd-db.c | 213 ++++++++++ drivers/soc/qcom/rpmh-internal.h | 138 +++++++ drivers/soc/qcom/rpmh-rsc.c | 518 ++++++++++++++++++++++++ drivers/soc/qcom/rpmh.c | 110 ++++++ include/linux/bitmap.h | 8 + include/soc/qcom/cmd-db.h | 33 ++ include/soc/qcom/rpmh.h | 28 ++ include/soc/qcom/tcs.h | 81 ++++ 16 files changed, 1720 insertions(+) --- change-id: 20240611-b4-qcom-rpmh-fcfd32ac2940 base-commit: 834df20b4ef50cda26936fbc435689fa0c5d47b1
// Caleb (they/them)

Import this function from Linux as of 6.10-rc6
Reviewed-by: Tom Rini trini@konsulko.com Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- include/linux/bitmap.h | 8 ++++++++ 1 file changed, 8 insertions(+)
diff --git a/include/linux/bitmap.h b/include/linux/bitmap.h index 0a8503af9f14..40ca2212cb40 100644 --- a/include/linux/bitmap.h +++ b/include/linux/bitmap.h @@ -195,8 +195,16 @@ static inline void bitmap_fill(unsigned long *dst, unsigned int nbits) memset(dst, 0xff, len); } }
+static inline bool bitmap_empty(const unsigned long *src, unsigned int nbits) +{ + if (small_const_nbits(nbits)) + return !(*src & BITMAP_LAST_WORD_MASK(nbits)); + + return find_first_bit(src, nbits) == nbits; +} + static inline void bitmap_or(unsigned long *dst, const unsigned long *src1, const unsigned long *src2, unsigned int nbits) { if (small_const_nbits(nbits))

Import RPMh and cmd-db framework from Linux 6.10-rc6.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/qcom/cmd-db.c | 393 +++++++++++++ drivers/soc/qcom/rpmh-internal.h | 148 +++++ drivers/soc/qcom/rpmh-rsc.c | 1162 ++++++++++++++++++++++++++++++++++++++ drivers/soc/qcom/rpmh.c | 502 ++++++++++++++++ include/soc/qcom/cmd-db.h | 48 ++ include/soc/qcom/rpmh.h | 47 ++ include/soc/qcom/tcs.h | 81 +++ 7 files changed, 2381 insertions(+)
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c new file mode 100644 index 000000000000..d84572662017 --- /dev/null +++ b/drivers/soc/qcom/cmd-db.c @@ -0,0 +1,393 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016-2018, 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/bitfield.h> +#include <linux/debugfs.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_reserved_mem.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> +#include <linux/types.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 +#define SLAVE_ID(addr) FIELD_GET(GENMASK(19, 16), addr) +#define VRM_ADDR(addr) FIELD_GET(GENMASK(19, 4), addr) + +/** + * 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; +} + +/** + * cmd_db_ready - Indicates if command DB is available + * + * Return: 0 on success, errno otherwise + */ +int cmd_db_ready(void) +{ + if (cmd_db_header == NULL) + return -EPROBE_DEFER; + else if (!cmd_db_magic_matches(cmd_db_header)) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(cmd_db_ready); + +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 ret, i, j; + u8 query[sizeof(ent->id)] __nonstring; + + ret = cmd_db_ready(); + if (ret) + return ret; + + strtomem_pad(query, id, 0); + + 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_GPL(cmd_db_read_addr); + +/** + * 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_GPL(cmd_db_read_aux_data); + +/** + * cmd_db_match_resource_addr() - Compare if both Resource addresses are same + * + * @addr1: Resource address to compare + * @addr2: Resource address to compare + * + * Return: true if two addresses refer to the same resource, false otherwise + */ +bool cmd_db_match_resource_addr(u32 addr1, u32 addr2) +{ + /* + * Each RPMh VRM accelerator resource has 3 or 4 contiguous 4-byte + * aligned addresses associated with it. Ignore the offset to check + * for VRM requests. + */ + if (addr1 == addr2) + return true; + else if (SLAVE_ID(addr1) == CMD_DB_HW_VRM && VRM_ADDR(addr1) == VRM_ADDR(addr2)) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(cmd_db_match_resource_addr); + +/** + * 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_GPL(cmd_db_read_slave_id); + +#ifdef CONFIG_DEBUG_FS +static int cmd_db_debugfs_dump(struct seq_file *seq, void *p) +{ + int i, j; + const struct rsc_hdr *rsc; + const struct entry_header *ent; + const char *name; + u16 len, version; + u8 major, minor; + + seq_puts(seq, "Command DB DUMP\n"); + + for (i = 0; i < MAX_SLV_ID; i++) { + rsc = &cmd_db_header->header[i]; + if (!rsc->slv_id) + break; + + switch (le16_to_cpu(rsc->slv_id)) { + case CMD_DB_HW_ARC: + name = "ARC"; + break; + case CMD_DB_HW_VRM: + name = "VRM"; + break; + case CMD_DB_HW_BCM: + name = "BCM"; + break; + default: + name = "Unknown"; + break; + } + + version = le16_to_cpu(rsc->version); + major = version >> 8; + minor = version; + + seq_printf(seq, "Slave %s (v%u.%u)\n", name, major, minor); + seq_puts(seq, "-------------------------\n"); + + ent = rsc_to_entry_header(rsc); + for (j = 0; j < le16_to_cpu(rsc->cnt); j++, ent++) { + seq_printf(seq, "0x%05x: %*pEp", le32_to_cpu(ent->addr), + (int)strnlen(ent->id, sizeof(ent->id)), ent->id); + + len = le16_to_cpu(ent->len); + if (len) { + seq_printf(seq, " [%*ph]", + len, rsc_offset(rsc, ent)); + } + seq_putc(seq, '\n'); + } + } + + return 0; +} + +static int open_cmd_db_debugfs(struct inode *inode, struct file *file) +{ + return single_open(file, cmd_db_debugfs_dump, inode->i_private); +} +#endif + +static const struct file_operations cmd_db_debugfs_ops = { +#ifdef CONFIG_DEBUG_FS + .open = open_cmd_db_debugfs, +#endif + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int cmd_db_dev_probe(struct platform_device *pdev) +{ + struct reserved_mem *rmem; + int ret = 0; + + rmem = of_reserved_mem_lookup(pdev->dev.of_node); + if (!rmem) { + dev_err(&pdev->dev, "failed to acquire memory region\n"); + return -EINVAL; + } + + cmd_db_header = memremap(rmem->base, rmem->size, MEMREMAP_WB); + if (!cmd_db_header) { + ret = -ENOMEM; + cmd_db_header = NULL; + return ret; + } + + if (!cmd_db_magic_matches(cmd_db_header)) { + dev_err(&pdev->dev, "Invalid Command DB Magic\n"); + return -EINVAL; + } + + debugfs_create_file("cmd-db", 0400, NULL, NULL, &cmd_db_debugfs_ops); + + device_set_pm_not_required(&pdev->dev); + + return 0; +} + +static const struct of_device_id cmd_db_match_table[] = { + { .compatible = "qcom,cmd-db" }, + { } +}; +MODULE_DEVICE_TABLE(of, cmd_db_match_table); + +static struct platform_driver cmd_db_dev_driver = { + .probe = cmd_db_dev_probe, + .driver = { + .name = "cmd-db", + .of_match_table = cmd_db_match_table, + .suppress_bind_attrs = true, + }, +}; + +static int __init cmd_db_device_init(void) +{ + return platform_driver_register(&cmd_db_dev_driver); +} +core_initcall(cmd_db_device_init); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Command DB Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h new file mode 100644 index 000000000000..e3cf1beff803 --- /dev/null +++ b/drivers/soc/qcom/rpmh-internal.h @@ -0,0 +1,148 @@ +/* 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 <linux/wait.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) + +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]; + struct completion *completion; + const struct device *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; + spinlock_t cache_lock; + 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 notifier_block rsc_pm; + struct notifier_block genpd_nb; + atomic_t cpus_in_pm; + struct tcs_group tcs[TCS_TYPE_NR]; + DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR); + spinlock_t lock; + wait_queue_head_t tcs_wait; + struct rpmh_ctrlr client; + struct device *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); + +void rpmh_tx_done(const struct tcs_request *msg); +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..de86009ecd91 --- /dev/null +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -0,0 +1,1162 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2023-2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#define pr_fmt(fmt) "%s " fmt, KBUILD_MODNAME + +#include <linux/atomic.h> +#include <linux/cpu_pm.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/ktime.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/notifier.h> +#include <linux/of.h> +#include <linux/of_irq.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/pm_domain.h> +#include <linux/pm_runtime.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/wait.h> + +#include <clocksource/arm_arch_timer.h> +#include <soc/qcom/cmd-db.h> +#include <soc/qcom/tcs.h> +#include <dt-bindings/soc/qcom,rpmh-rsc.h> + +#include "rpmh-internal.h" + +#define CREATE_TRACE_POINTS +#include "trace-rpmh.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) | | + * | +-----------------------------------------------+ | + * | ...... | + * +---------------------------------------------------+ + */ + +#define USECS_TO_CYCLES(time_usecs) \ + xloops_to_cycles((time_usecs) * 0x10C7UL) + +static inline unsigned long xloops_to_cycles(u64 xloops) +{ + return (xloops * loops_per_jiffy * HZ) >> 32; +} + +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 u32 rpmh_rsc_reg_offset_ver_3_0[] = { + [RSC_DRV_TCS_OFFSET] = 672, + [RSC_DRV_CMD_OFFSET] = 24, + [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] = 0x20, + [RSC_DRV_CONTROL] = 0x24, + [RSC_DRV_STATUS] = 0x28, + [RSC_DRV_CMD_ENABLE] = 0x2C, + [RSC_DRV_CMD_MSGID] = 0x34, + [RSC_DRV_CMD_ADDR] = 0x38, + [RSC_DRV_CMD_DATA] = 0x3C, + [RSC_DRV_CMD_STATUS] = 0x40, + [RSC_DRV_CMD_RESP_DATA] = 0x44, +}; + +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_cmd(const struct rsc_drv *drv, int reg, int tcs_id, + int cmd_id) +{ + return readl_relaxed(tcs_cmd_addr(drv, reg, tcs_id, 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) +{ + writel_relaxed(data, tcs_cmd_addr(drv, reg, tcs_id, cmd_id)); +} + +static void write_tcs_reg(const struct rsc_drv *drv, int reg, int tcs_id, + u32 data) +{ + writel_relaxed(data, tcs_reg_addr(drv, reg, tcs_id)); +} + +static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int tcs_id, + u32 data) +{ + int i; + + writel(data, tcs_reg_addr(drv, reg, tcs_id)); + + /* + * 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(tcs_reg_addr(drv, reg, tcs_id)) == data) + return; + udelay(1); + } + 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); +} + +/** + * 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) +{ + int type; + struct tcs_group *tcs; + + switch (msg->state) { + case RPMH_ACTIVE_ONLY_STATE: + type = ACTIVE_TCS; + break; + case RPMH_WAKE_ONLY_STATE: + type = WAKE_TCS; + break; + case RPMH_SLEEP_STATE: + type = SLEEP_TCS; + break; + default: + return ERR_PTR(-EINVAL); + } + + /* + * If we are making an active request on a RSC that does not have a + * dedicated TCS for active state use, then re-purpose a wake TCS to + * send active votes. This is safe because we ensure any active-only + * transfers have finished before we use it (maybe by running from + * the last CPU in PM code). + */ + tcs = &drv->tcs[type]; + if (msg->state == RPMH_ACTIVE_ONLY_STATE && !tcs->num_tcs) + tcs = &drv->tcs[WAKE_TCS]; + + return tcs; +} + +/** + * get_req_from_tcs() - Get a stashed request that was xfering on the given TCS. + * @drv: The RSC controller. + * @tcs_id: The global ID of this TCS. + * + * For ACTIVE_ONLY transfers we want to call back into the client when the + * transfer finishes. To do this we need the "request" that the client + * originally provided us. This function grabs the request that we stashed + * when we started the transfer. + * + * This only makes sense for ACTIVE_ONLY transfers since those are the only + * ones we track sending (the only ones we enable interrupts for and the only + * ones we call back to the client for). + * + * Return: The stashed request. + */ +static const struct tcs_request *get_req_from_tcs(struct rsc_drv *drv, + int tcs_id) +{ + struct tcs_group *tcs; + int i; + + for (i = 0; i < TCS_TYPE_NR; i++) { + tcs = &drv->tcs[i]; + if (tcs->mask & BIT(tcs_id)) + return tcs->req[tcs_id - tcs->offset]; + } + + return NULL; +} + +/** + * __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); + } +} + +/** + * enable_tcs_irq() - Enable or disable interrupts on the given TCS. + * @drv: The controller. + * @tcs_id: The global ID of this TCS. + * @enable: If true then enable; if false then disable + * + * We only ever call this when we borrow a wake TCS for an active-only + * transfer. For active-only TCSes interrupts are always left enabled. + */ +static void enable_tcs_irq(struct rsc_drv *drv, int tcs_id, bool enable) +{ + u32 data; + u32 reg = drv->regs[RSC_DRV_IRQ_ENABLE]; + + data = readl_relaxed(drv->tcs_base + reg); + if (enable) + data |= BIT(tcs_id); + else + data &= ~BIT(tcs_id); + writel_relaxed(data, drv->tcs_base + reg); +} + +/** + * tcs_tx_done() - TX Done interrupt handler. + * @irq: The IRQ number (ignored). + * @p: Pointer to "struct rsc_drv". + * + * Called for ACTIVE_ONLY transfers (those are the only ones we enable the + * IRQ for) when a transfer is done. + * + * Return: IRQ_HANDLED + */ +static irqreturn_t tcs_tx_done(int irq, void *p) +{ + struct rsc_drv *drv = p; + int i; + unsigned long irq_status; + const struct tcs_request *req; + + irq_status = readl_relaxed(drv->tcs_base + drv->regs[RSC_DRV_IRQ_STATUS]); + + for_each_set_bit(i, &irq_status, BITS_PER_TYPE(u32)) { + req = get_req_from_tcs(drv, i); + if (WARN_ON(!req)) + goto skip; + + trace_rpmh_tx_done(drv, i, req); + + /* + * If wake tcs was re-purposed for sending active + * votes, clear AMC trigger & enable modes and + * disable interrupt for this TCS + */ + if (!drv->tcs[ACTIVE_TCS].num_tcs) + __tcs_set_trigger(drv, i, false); +skip: + /* Reclaim the TCS */ + write_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], i, 0); + writel_relaxed(BIT(i), drv->tcs_base + drv->regs[RSC_DRV_IRQ_CLEAR]); + spin_lock(&drv->lock); + clear_bit(i, drv->tcs_in_use); + /* + * Disable interrupt for WAKE TCS to avoid being + * spammed with interrupts coming when the solver + * sends its wake votes. + */ + if (!drv->tcs[ACTIVE_TCS].num_tcs) + enable_tcs_irq(drv, i, false); + spin_unlock(&drv->lock); + wake_up(&drv->tcs_wait); + if (req) + rpmh_tx_done(req); + } + + return IRQ_HANDLED; +} + +/** + * __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; + + /* Convert all commands to RR when the request has wait_for_compl set */ + cmd_msgid |= msg->wait_for_compl ? CMD_MSGID_RESP_REQ : 0; + + 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); + trace_rpmh_send_msg(drv, tcs_id, msg->state, j, msgid, cmd); + } + + 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); +} + +/** + * check_for_req_inflight() - Look to see if conflicting cmds are in flight. + * @drv: The controller. + * @tcs: A pointer to the tcs_group used for ACTIVE_ONLY transfers. + * @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 will walk through the TCSes in the group and check if any of them + * appear to be sending to addresses referenced in the message. If it finds + * one it'll return -EBUSY. + * + * Only for use for active-only transfers. + * + * Must be called with the drv->lock held since that protects tcs_in_use. + * + * Return: 0 if nothing in flight or -EBUSY if we should try again later. + * The caller must re-enable interrupts between tries since that's + * the only way tcs_in_use will ever be updated and the only way + * RSC_DRV_CMD_ENABLE will ever be cleared. + */ +static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs, + const struct tcs_request *msg) +{ + unsigned long curr_enabled; + u32 addr; + int j, k; + int i = tcs->offset; + + for_each_set_bit_from(i, drv->tcs_in_use, tcs->offset + tcs->num_tcs) { + curr_enabled = read_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], i); + + for_each_set_bit(j, &curr_enabled, MAX_CMDS_PER_TCS) { + addr = read_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_ADDR], i, j); + for (k = 0; k < msg->num_cmds; k++) { + if (cmd_db_match_resource_addr(msg->cmds[k].addr, addr)) + return -EBUSY; + } + } + } + + return 0; +} + +/** + * find_free_tcs() - Find free tcs in the given tcs_group; only for active. + * @tcs: A pointer to the active-only tcs_group (or the wake tcs_group if + * we borrowed it because there are zero active-only ones). + * + * Must be called with the drv->lock held since that protects tcs_in_use. + * + * Return: The first tcs that's free or -EBUSY if all in use. + */ +static int find_free_tcs(struct tcs_group *tcs) +{ + const struct rsc_drv *drv = tcs->drv; + unsigned long i; + unsigned long max = tcs->offset + tcs->num_tcs; + + i = find_next_zero_bit(drv->tcs_in_use, max, tcs->offset); + if (i >= max) + return -EBUSY; + + return i; +} + +/** + * claim_tcs_for_req() - Claim a tcs in the given tcs_group; only for active. + * @drv: The controller. + * @tcs: The tcs_group used for ACTIVE_ONLY transfers. + * @msg: The data to be sent. + * + * Claims a tcs in the given tcs_group while making sure that no existing cmd + * is in flight that would conflict with the one in @msg. + * + * Context: Must be called with the drv->lock held since that protects + * tcs_in_use. + * + * Return: The id of the claimed tcs or -EBUSY if a matching msg is in flight + * or the tcs_group is full. + */ +static int claim_tcs_for_req(struct rsc_drv *drv, struct tcs_group *tcs, + const struct tcs_request *msg) +{ + int ret; + + /* + * The h/w does not like if we send a request to the same address, + * when one is already in-flight or being processed. + */ + ret = check_for_req_inflight(drv, tcs, msg); + if (ret) + return ret; + + return find_free_tcs(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; + + might_sleep(); + + tcs = get_tcs_for_msg(drv, msg); + if (IS_ERR(tcs)) + return PTR_ERR(tcs); + + spin_lock_irq(&drv->lock); + + /* Wait forever for a free tcs. It better be there eventually! */ + wait_event_lock_irq(drv->tcs_wait, + (tcs_id = claim_tcs_for_req(drv, tcs, msg)) >= 0, + drv->lock); + + tcs->req[tcs_id - tcs->offset] = msg; + 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); + enable_tcs_irq(drv, tcs_id, true); + } + spin_unlock_irq(&drv->lock); + + /* + * 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); + + return 0; +} + +/** + * find_slots() - Find a place to write the given message. + * @tcs: The tcs group to search. + * @msg: The message we want to find room for. + * @tcs_id: If we return 0 from the function, we return the global ID of the + * TCS to write to here. + * @cmd_id: If we return 0 from the function, we return the index of + * the command array of the returned TCS where the client should + * start writing the message. + * + * Only for use on sleep/wake TCSes since those are the only ones we maintain + * tcs->slots for. + * + * Return: -ENOMEM if there was no room, else 0. + */ +static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, + int *tcs_id, int *cmd_id) +{ + int slot, offset; + int i = 0; + + /* Do over, until we can fit the full payload in a single TCS */ + do { + slot = bitmap_find_next_zero_area(tcs->slots, MAX_TCS_SLOTS, + i, msg->num_cmds, 0); + if (slot >= tcs->num_tcs * tcs->ncpt) + return -ENOMEM; + i += tcs->ncpt; + } while (slot + msg->num_cmds - 1 >= i); + + bitmap_set(tcs->slots, slot, msg->num_cmds); + + offset = slot / tcs->ncpt; + *tcs_id = offset + tcs->offset; + *cmd_id = slot % tcs->ncpt; + + return 0; +} + +/** + * rpmh_rsc_write_ctrl_data() - Write request to controller but don't trigger. + * @drv: The controller. + * @msg: The data to be written to the controller. + * + * This should only be called for sleep/wake state, never active-only + * state. + * + * The caller must ensure that no other RPMH actions are happening and the + * controller is idle when this function is called since it runs lockless. + * + * Return: 0 if no error; else -error. + */ +int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) +{ + struct tcs_group *tcs; + int tcs_id = 0, cmd_id = 0; + int ret; + + tcs = get_tcs_for_msg(drv, msg); + if (IS_ERR(tcs)) + return PTR_ERR(tcs); + + /* find the TCS id and the command in the TCS to write to */ + ret = find_slots(tcs, msg, &tcs_id, &cmd_id); + if (!ret) + __tcs_buffer_write(drv, tcs_id, cmd_id, msg); + + return ret; +} + +/** + * rpmh_rsc_ctrlr_is_busy() - Check if any of the AMCs are busy. + * @drv: The controller + * + * Checks if any of the AMCs are busy in handling ACTIVE sets. + * This is called from the last cpu powering down before flushing + * SLEEP and WAKE sets. If AMCs are busy, controller can not enter + * power collapse, so deny from the last cpu's pm notification. + * + * Context: Must be called with the drv->lock held. + * + * Return: + * * False - AMCs are idle + * * True - AMCs are busy + */ +static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) +{ + unsigned long set; + const struct tcs_group *tcs = &drv->tcs[ACTIVE_TCS]; + unsigned long max; + + /* + * If we made an active request on a RSC that does not have a + * dedicated TCS for active state use, then re-purposed wake TCSes + * should be checked for not busy, because we used wake TCSes for + * active requests in this case. + */ + if (!tcs->num_tcs) + tcs = &drv->tcs[WAKE_TCS]; + + max = tcs->offset + tcs->num_tcs; + set = find_next_bit(drv->tcs_in_use, max, tcs->offset); + + return set < max; +} + +/** + * rpmh_rsc_write_next_wakeup() - Write next wakeup in CONTROL_TCS. + * @drv: The controller + * + * Writes maximum wakeup cycles when called from suspend. + * Writes earliest hrtimer wakeup when called from idle. + */ +void rpmh_rsc_write_next_wakeup(struct rsc_drv *drv) +{ + ktime_t now, wakeup; + u64 wakeup_us, wakeup_cycles = ~0; + u32 lo, hi; + + if (!drv->tcs[CONTROL_TCS].num_tcs || !drv->genpd_nb.notifier_call) + return; + + /* Set highest time when system (timekeeping) is suspended */ + if (system_state == SYSTEM_SUSPEND) + goto exit; + + /* Find the earliest hrtimer wakeup from online cpus */ + wakeup = dev_pm_genpd_get_next_hrtimer(drv->dev); + + /* Find the relative wakeup in kernel time scale */ + now = ktime_get(); + wakeup = ktime_sub(wakeup, now); + wakeup_us = ktime_to_us(wakeup); + + /* Convert the wakeup to arch timer scale */ + wakeup_cycles = USECS_TO_CYCLES(wakeup_us); + wakeup_cycles += arch_timer_read_counter(); + +exit: + lo = wakeup_cycles & RSC_DRV_CTL_TCS_DATA_LO_MASK; + hi = wakeup_cycles >> RSC_DRV_CTL_TCS_DATA_SIZE; + hi &= RSC_DRV_CTL_TCS_DATA_HI_MASK; + hi |= RSC_DRV_CTL_TCS_DATA_HI_VALID; + + writel_relaxed(lo, drv->base + RSC_DRV_CTL_TCS_DATA_LO); + writel_relaxed(hi, drv->base + RSC_DRV_CTL_TCS_DATA_HI); +} + +/** + * rpmh_rsc_cpu_pm_callback() - Check if any of the AMCs are busy. + * @nfb: Pointer to the notifier block in struct rsc_drv. + * @action: CPU_PM_ENTER, CPU_PM_ENTER_FAILED, or CPU_PM_EXIT. + * @v: Unused + * + * This function is given to cpu_pm_register_notifier so we can be informed + * about when CPUs go down. When all CPUs go down we know no more active + * transfers will be started so we write sleep/wake sets. This function gets + * called from cpuidle code paths and also at system suspend time. + * + * If its last CPU going down and AMCs are not busy then writes cached sleep + * and wake messages to TCSes. The firmware then takes care of triggering + * them when entering deepest low power modes. + * + * Return: See cpu_pm_register_notifier() + */ +static int rpmh_rsc_cpu_pm_callback(struct notifier_block *nfb, + unsigned long action, void *v) +{ + struct rsc_drv *drv = container_of(nfb, struct rsc_drv, rsc_pm); + int ret = NOTIFY_OK; + int cpus_in_pm; + + switch (action) { + case CPU_PM_ENTER: + cpus_in_pm = atomic_inc_return(&drv->cpus_in_pm); + /* + * NOTE: comments for num_online_cpus() point out that it's + * only a snapshot so we need to be careful. It should be OK + * for us to use, though. It's important for us not to miss + * if we're the last CPU going down so it would only be a + * problem if a CPU went offline right after we did the check + * AND that CPU was not idle AND that CPU was the last non-idle + * CPU. That can't happen. CPUs would have to come out of idle + * before the CPU could go offline. + */ + if (cpus_in_pm < num_online_cpus()) + return NOTIFY_OK; + break; + case CPU_PM_ENTER_FAILED: + case CPU_PM_EXIT: + atomic_dec(&drv->cpus_in_pm); + return NOTIFY_OK; + default: + return NOTIFY_DONE; + } + + /* + * It's likely we're on the last CPU. Grab the drv->lock and write + * out the sleep/wake commands to RPMH hardware. Grabbing the lock + * means that if we race with another CPU coming up we are still + * guaranteed to be safe. If another CPU came up just after we checked + * and has grabbed the lock or started an active transfer then we'll + * notice we're busy and abort. If another CPU comes up after we start + * flushing it will be blocked from starting an active transfer until + * we're done flushing. If another CPU starts an active transfer after + * we release the lock we're still OK because we're no longer the last + * CPU. + */ + if (spin_trylock(&drv->lock)) { + if (rpmh_rsc_ctrlr_is_busy(drv) || rpmh_flush(&drv->client)) + ret = NOTIFY_BAD; + spin_unlock(&drv->lock); + } else { + /* Another CPU must be up */ + return NOTIFY_OK; + } + + if (ret == NOTIFY_BAD) { + /* Double-check if we're here because someone else is up */ + if (cpus_in_pm < num_online_cpus()) + ret = NOTIFY_OK; + else + /* We won't be called w/ CPU_PM_ENTER_FAILED */ + atomic_dec(&drv->cpus_in_pm); + } + + return ret; +} + +/** + * rpmh_rsc_pd_callback() - Check if any of the AMCs are busy. + * @nfb: Pointer to the genpd notifier block in struct rsc_drv. + * @action: GENPD_NOTIFY_PRE_OFF, GENPD_NOTIFY_OFF, GENPD_NOTIFY_PRE_ON or GENPD_NOTIFY_ON. + * @v: Unused + * + * This function is given to dev_pm_genpd_add_notifier() so we can be informed + * about when cluster-pd is going down. When cluster go down we know no more active + * transfers will be started so we write sleep/wake sets. This function gets + * called from cpuidle code paths and also at system suspend time. + * + * If AMCs are not busy then writes cached sleep and wake messages to TCSes. + * The firmware then takes care of triggering them when entering deepest low power modes. + * + * Return: + * * NOTIFY_OK - success + * * NOTIFY_BAD - failure + */ +static int rpmh_rsc_pd_callback(struct notifier_block *nfb, + unsigned long action, void *v) +{ + struct rsc_drv *drv = container_of(nfb, struct rsc_drv, genpd_nb); + + /* We don't need to lock as genpd on/off are serialized */ + if ((action == GENPD_NOTIFY_PRE_OFF) && + (rpmh_rsc_ctrlr_is_busy(drv) || rpmh_flush(&drv->client))) + return NOTIFY_BAD; + + return NOTIFY_OK; +} + +static int rpmh_rsc_pd_attach(struct rsc_drv *drv, struct device *dev) +{ + int ret; + + pm_runtime_enable(dev); + drv->genpd_nb.notifier_call = rpmh_rsc_pd_callback; + ret = dev_pm_genpd_add_notifier(dev, &drv->genpd_nb); + if (ret) + pm_runtime_disable(dev); + + return ret; +} + +static int rpmh_probe_tcs_config(struct platform_device *pdev, struct rsc_drv *drv) +{ + struct tcs_type_config { + u32 type; + u32 n; + } tcs_cfg[TCS_TYPE_NR] = { { 0 } }; + struct device_node *dn = pdev->dev.of_node; + u32 config, max_tcs, ncpt, offset; + int i, ret, n, st = 0; + struct tcs_group *tcs; + + ret = of_property_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 = of_property_count_u32_elems(dn, "qcom,tcs-config"); + if (n != 2 * TCS_TYPE_NR) + return -EINVAL; + + for (i = 0; i < TCS_TYPE_NR; i++) { + ret = of_property_read_u32_index(dn, "qcom,tcs-config", + i * 2, &tcs_cfg[i].type); + if (ret) + return ret; + if (tcs_cfg[i].type >= TCS_TYPE_NR) + return -EINVAL; + + ret = of_property_read_u32_index(dn, "qcom,tcs-config", + i * 2 + 1, &tcs_cfg[i].n); + if (ret) + return ret; + 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 platform_device *pdev) +{ + struct device_node *dn = pdev->dev.of_node; + struct rsc_drv *drv; + char drv_id[10] = {0}; + int ret, irq; + u32 solver_config; + u32 rsc_id; + + /* + * Even though RPMh doesn't directly use cmd-db, all of its children + * do. To avoid adding this check to our children we'll do it now. + */ + ret = cmd_db_ready(); + if (ret) { + if (ret != -EPROBE_DEFER) + dev_err(&pdev->dev, "Command DB not available (%d)\n", + ret); + return ret; + } + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + + ret = of_property_read_u32(dn, "qcom,drv-id", &drv->id); + if (ret) + return ret; + + drv->name = of_get_property(dn, "label", NULL); + if (!drv->name) + drv->name = dev_name(&pdev->dev); + + snprintf(drv_id, ARRAY_SIZE(drv_id), "drv-%d", drv->id); + drv->base = devm_platform_ioremap_resource_byname(pdev, 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) + drv->regs = rpmh_rsc_reg_offset_ver_3_0; + else + drv->regs = rpmh_rsc_reg_offset_ver_2_7; + + ret = rpmh_probe_tcs_config(pdev, drv); + if (ret) + return ret; + + spin_lock_init(&drv->lock); + init_waitqueue_head(&drv->tcs_wait); + bitmap_zero(drv->tcs_in_use, MAX_TCS_NR); + + irq = platform_get_irq(pdev, drv->id); + if (irq < 0) + return irq; + + ret = devm_request_irq(&pdev->dev, irq, tcs_tx_done, + IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, + drv->name, drv); + if (ret) + return ret; + + /* + * CPU PM/genpd notification are not required for controllers that support + * 'HW solver' mode where they can be in autonomous mode executing low + * power mode to power down. + */ + solver_config = readl_relaxed(drv->base + drv->regs[DRV_SOLVER_CONFIG]); + solver_config &= DRV_HW_SOLVER_MASK << DRV_HW_SOLVER_SHIFT; + solver_config = solver_config >> DRV_HW_SOLVER_SHIFT; + if (!solver_config) { + if (pdev->dev.pm_domain) { + ret = rpmh_rsc_pd_attach(drv, &pdev->dev); + if (ret) + return ret; + } else { + drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback; + cpu_pm_register_notifier(&drv->rsc_pm); + } + } + + /* 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(&pdev->dev, drv); + drv->dev = &pdev->dev; + + ret = devm_of_platform_populate(&pdev->dev); + if (ret && pdev->dev.pm_domain) { + dev_pm_genpd_remove_notifier(&pdev->dev); + pm_runtime_disable(&pdev->dev); + } + + return ret; +} + +static const struct of_device_id rpmh_drv_match[] = { + { .compatible = "qcom,rpmh-rsc", }, + { } +}; +MODULE_DEVICE_TABLE(of, rpmh_drv_match); + +static struct platform_driver rpmh_driver = { + .probe = rpmh_rsc_probe, + .driver = { + .name = "rpmh", + .of_match_table = rpmh_drv_match, + .suppress_bind_attrs = true, + }, +}; + +static int __init rpmh_driver_init(void) +{ + return platform_driver_register(&rpmh_driver); +} +core_initcall(rpmh_driver_init); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c new file mode 100644 index 000000000000..8903ed956312 --- /dev/null +++ b/drivers/soc/qcom/rpmh.c @@ -0,0 +1,502 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + */ + +#include <linux/atomic.h> +#include <linux/bug.h> +#include <linux/interrupt.h> +#include <linux/jiffies.h> +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/lockdep.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> +#include <linux/types.h> +#include <linux/wait.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, q, name) \ + struct rpmh_request name = { \ + .msg = { \ + .state = s, \ + .cmds = name.cmd, \ + .num_cmds = 0, \ + .wait_for_compl = true, \ + }, \ + .cmd = { { 0 } }, \ + .completion = q, \ + .dev = device, \ + .needs_free = false, \ + } + +#define ctrlr_to_drv(ctrlr) container_of(ctrlr, struct rsc_drv, client) + +/** + * struct cache_req: the request object for caching + * + * @addr: the address of the resource + * @sleep_val: the sleep vote + * @wake_val: the wake vote + * @list: linked list obj + */ +struct cache_req { + u32 addr; + u32 sleep_val; + u32 wake_val; + struct list_head list; +}; + +/** + * struct batch_cache_req - An entry in our batch catch + * + * @list: linked list obj + * @count: number of messages + * @rpm_msgs: the messages + */ + +struct batch_cache_req { + struct list_head list; + int count; + struct rpmh_request rpm_msgs[]; +}; + +static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct device *dev) +{ + struct rsc_drv *drv = dev_get_drvdata(dev->parent); + + return &drv->client; +} + +void rpmh_tx_done(const struct tcs_request *msg) +{ + struct rpmh_request *rpm_msg = container_of(msg, struct rpmh_request, + msg); + struct completion *compl = rpm_msg->completion; + bool free = rpm_msg->needs_free; + + if (!compl) + goto exit; + + /* Signal the blocking thread we are done */ + complete(compl); + +exit: + if (free) + kfree(rpm_msg); +} + +static struct cache_req *__find_req(struct rpmh_ctrlr *ctrlr, u32 addr) +{ + struct cache_req *p, *req = NULL; + + list_for_each_entry(p, &ctrlr->cache, list) { + if (p->addr == addr) { + req = p; + break; + } + } + + return req; +} + +static struct cache_req *cache_rpm_request(struct rpmh_ctrlr *ctrlr, + enum rpmh_state state, + struct tcs_cmd *cmd) +{ + struct cache_req *req; + unsigned long flags; + u32 old_sleep_val, old_wake_val; + + spin_lock_irqsave(&ctrlr->cache_lock, flags); + req = __find_req(ctrlr, cmd->addr); + if (req) + goto existing; + + req = kzalloc(sizeof(*req), GFP_ATOMIC); + if (!req) { + req = ERR_PTR(-ENOMEM); + goto unlock; + } + + req->addr = cmd->addr; + req->sleep_val = req->wake_val = UINT_MAX; + list_add_tail(&req->list, &ctrlr->cache); + +existing: + old_sleep_val = req->sleep_val; + old_wake_val = req->wake_val; + + switch (state) { + case RPMH_ACTIVE_ONLY_STATE: + case RPMH_WAKE_ONLY_STATE: + req->wake_val = cmd->data; + break; + case RPMH_SLEEP_STATE: + req->sleep_val = cmd->data; + break; + } + + ctrlr->dirty |= (req->sleep_val != old_sleep_val || + req->wake_val != old_wake_val) && + req->sleep_val != UINT_MAX && + req->wake_val != UINT_MAX; + +unlock: + spin_unlock_irqrestore(&ctrlr->cache_lock, flags); + + return req; +} + +/** + * __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 device *dev, enum rpmh_state state, + struct rpmh_request *rpm_msg) +{ + struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); + int ret = -EINVAL; + struct cache_req *req; + int i; + + /* Cache the request in our store and link the payload */ + for (i = 0; i < rpm_msg->msg.num_cmds; i++) { + req = cache_rpm_request(ctrlr, state, &rpm_msg->msg.cmds[i]); + if (IS_ERR(req)) + return PTR_ERR(req); + } + + if (state == RPMH_ACTIVE_ONLY_STATE) { + ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg); + } else { + /* Clean up our call by spoofing tx_done */ + ret = 0; + rpmh_tx_done(&rpm_msg->msg); + } + + return ret; +} + +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; + + return 0; +} + +/** + * rpmh_write_async: Write a set of RPMH commands + * + * @dev: The device making the request + * @state: Active/sleep set + * @cmd: The payload data + * @n: The number of elements in payload + * + * Write a set of RPMH commands, the order of commands is maintained + * and will be sent as a single shot. + */ +int rpmh_write_async(const struct device *dev, enum rpmh_state state, + const struct tcs_cmd *cmd, u32 n) +{ + struct rpmh_request *rpm_msg; + int ret; + + rpm_msg = kzalloc(sizeof(*rpm_msg), GFP_ATOMIC); + if (!rpm_msg) + return -ENOMEM; + rpm_msg->needs_free = true; + + ret = __fill_rpmh_msg(rpm_msg, state, cmd, n); + if (ret) { + kfree(rpm_msg); + return ret; + } + + return __rpmh_write(dev, state, rpm_msg); +} +EXPORT_SYMBOL_GPL(rpmh_write_async); + +/** + * 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 device *dev, enum rpmh_state state, + const struct tcs_cmd *cmd, u32 n) +{ + DECLARE_COMPLETION_ONSTACK(compl); + DEFINE_RPMH_MSG_ONSTACK(dev, state, &compl, rpm_msg); + int ret; + + ret = __fill_rpmh_msg(&rpm_msg, state, cmd, n); + if (ret) + return ret; + + ret = __rpmh_write(dev, state, &rpm_msg); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS); + WARN_ON(!ret); + return (ret > 0) ? 0 : -ETIMEDOUT; +} +EXPORT_SYMBOL_GPL(rpmh_write); + +static void cache_batch(struct rpmh_ctrlr *ctrlr, struct batch_cache_req *req) +{ + unsigned long flags; + + spin_lock_irqsave(&ctrlr->cache_lock, flags); + list_add_tail(&req->list, &ctrlr->batch_cache); + ctrlr->dirty = true; + spin_unlock_irqrestore(&ctrlr->cache_lock, flags); +} + +static int flush_batch(struct rpmh_ctrlr *ctrlr) +{ + struct batch_cache_req *req; + const struct rpmh_request *rpm_msg; + int ret = 0; + int i; + + /* Send Sleep/Wake requests to the controller, expect no response */ + list_for_each_entry(req, &ctrlr->batch_cache, list) { + for (i = 0; i < req->count; i++) { + rpm_msg = req->rpm_msgs + i; + ret = rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr), + &rpm_msg->msg); + if (ret) + break; + } + } + + return ret; +} + +/** + * rpmh_write_batch: Write multiple sets of RPMH commands and wait for the + * batch to finish. + * + * @dev: the device making the request + * @state: Active/sleep set + * @cmd: The payload data + * @n: The array of count of elements in each batch, 0 terminated. + * + * Write a request to the RSC controller without caching. If the request + * state is ACTIVE, then the requests are treated as completion request + * and sent to the controller immediately. The function waits until all the + * commands are complete. If the request was to SLEEP or WAKE_ONLY, then the + * request is sent as fire-n-forget and no ack is expected. + * + * May sleep. Do not call from atomic contexts for ACTIVE_ONLY requests. + */ +int rpmh_write_batch(const struct device *dev, enum rpmh_state state, + const struct tcs_cmd *cmd, u32 *n) +{ + struct batch_cache_req *req; + struct rpmh_request *rpm_msgs; + struct completion *compls; + struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); + unsigned long time_left; + int count = 0; + int ret, i; + void *ptr; + + if (!cmd || !n) + return -EINVAL; + + while (n[count] > 0) + count++; + if (!count) + return -EINVAL; + + ptr = kzalloc(sizeof(*req) + + count * (sizeof(req->rpm_msgs[0]) + sizeof(*compls)), + GFP_ATOMIC); + if (!ptr) + return -ENOMEM; + + req = ptr; + compls = ptr + sizeof(*req) + count * sizeof(*rpm_msgs); + + req->count = count; + rpm_msgs = req->rpm_msgs; + + for (i = 0; i < count; i++) { + __fill_rpmh_msg(rpm_msgs + i, state, cmd, n[i]); + cmd += n[i]; + } + + if (state != RPMH_ACTIVE_ONLY_STATE) { + cache_batch(ctrlr, req); + return 0; + } + + for (i = 0; i < count; i++) { + struct completion *compl = &compls[i]; + + init_completion(compl); + rpm_msgs[i].completion = compl; + ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msgs[i].msg); + if (ret) { + pr_err("Error(%d) sending RPMH message addr=%#x\n", + ret, rpm_msgs[i].msg.cmds[0].addr); + break; + } + } + + time_left = RPMH_TIMEOUT_MS; + while (i--) { + time_left = wait_for_completion_timeout(&compls[i], time_left); + if (!time_left) { + /* + * Better hope they never finish because they'll signal + * the completion that we're going to free once + * we've returned from this function. + */ + WARN_ON(1); + ret = -ETIMEDOUT; + goto exit; + } + } + +exit: + kfree(ptr); + + return ret; +} +EXPORT_SYMBOL_GPL(rpmh_write_batch); + +static int is_req_valid(struct cache_req *req) +{ + return (req->sleep_val != UINT_MAX && + req->wake_val != UINT_MAX && + req->sleep_val != req->wake_val); +} + +static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, + u32 addr, u32 data) +{ + DEFINE_RPMH_MSG_ONSTACK(NULL, state, NULL, rpm_msg); + + /* Wake sets are always complete and sleep sets are not */ + rpm_msg.msg.wait_for_compl = (state == RPMH_WAKE_ONLY_STATE); + rpm_msg.cmd[0].addr = addr; + rpm_msg.cmd[0].data = data; + rpm_msg.msg.num_cmds = 1; + + return rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr), &rpm_msg.msg); +} + +/** + * rpmh_flush() - Flushes the buffered sleep and wake sets to TCSes + * + * @ctrlr: Controller making request to flush cached data + * + * Return: + * * 0 - Success + * * Error code - Otherwise + */ +int rpmh_flush(struct rpmh_ctrlr *ctrlr) +{ + struct cache_req *p; + int ret = 0; + + lockdep_assert_irqs_disabled(); + + /* + * Currently rpmh_flush() is only called when we think we're running + * on the last processor. If the lock is busy it means another + * processor is up and it's better to abort than spin. + */ + if (!spin_trylock(&ctrlr->cache_lock)) + return -EBUSY; + + if (!ctrlr->dirty) { + pr_debug("Skipping flush, TCS has latest data.\n"); + goto write_next_wakeup; + } + + /* Invalidate the TCSes first to avoid stale data */ + rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); + + /* First flush the cached batch requests */ + ret = flush_batch(ctrlr); + if (ret) + goto exit; + + list_for_each_entry(p, &ctrlr->cache, list) { + if (!is_req_valid(p)) { + pr_debug("%s: skipping RPMH req: a:%#x s:%#x w:%#x", + __func__, p->addr, p->sleep_val, p->wake_val); + continue; + } + ret = send_single(ctrlr, RPMH_SLEEP_STATE, p->addr, + p->sleep_val); + if (ret) + goto exit; + ret = send_single(ctrlr, RPMH_WAKE_ONLY_STATE, p->addr, + p->wake_val); + if (ret) + goto exit; + } + + ctrlr->dirty = false; + +write_next_wakeup: + rpmh_rsc_write_next_wakeup(ctrlr_to_drv(ctrlr)); +exit: + spin_unlock(&ctrlr->cache_lock); + return ret; +} + +/** + * rpmh_invalidate: Invalidate sleep and wake sets in batch_cache + * + * @dev: The device making the request + * + * Invalidate the sleep and wake values in batch_cache. + */ +void rpmh_invalidate(const struct device *dev) +{ + struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); + struct batch_cache_req *req, *tmp; + unsigned long flags; + + spin_lock_irqsave(&ctrlr->cache_lock, flags); + list_for_each_entry_safe(req, tmp, &ctrlr->batch_cache, list) + kfree(req); + INIT_LIST_HEAD(&ctrlr->batch_cache); + ctrlr->dirty = true; + spin_unlock_irqrestore(&ctrlr->cache_lock, flags); +} +EXPORT_SYMBOL_GPL(rpmh_invalidate); diff --git a/include/soc/qcom/cmd-db.h b/include/soc/qcom/cmd-db.h new file mode 100644 index 000000000000..47a6cab75e63 --- /dev/null +++ b/include/soc/qcom/cmd-db.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef __QCOM_COMMAND_DB_H__ +#define __QCOM_COMMAND_DB_H__ + +#include <linux/err.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); + +bool cmd_db_match_resource_addr(u32 addr1, u32 addr2); + +enum cmd_db_hw_type cmd_db_read_slave_id(const char *resource_id); + +int cmd_db_ready(void); +#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 bool cmd_db_match_resource_addr(u32 addr1, u32 addr2) +{ return false; } + +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..bdbee1a97d36 --- /dev/null +++ b/include/soc/qcom/rpmh.h @@ -0,0 +1,47 @@ +/* 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 <soc/qcom/tcs.h> +#include <linux/platform_device.h> + + +#if IS_ENABLED(CONFIG_QCOM_RPMH) +int rpmh_write(const struct device *dev, enum rpmh_state state, + const struct tcs_cmd *cmd, u32 n); + +int rpmh_write_async(const struct device *dev, enum rpmh_state state, + const struct tcs_cmd *cmd, u32 n); + +int rpmh_write_batch(const struct device *dev, enum rpmh_state state, + const struct tcs_cmd *cmd, u32 *n); + +void rpmh_invalidate(const struct device *dev); + +#else + +static inline int rpmh_write(const struct device *dev, enum rpmh_state state, + const struct tcs_cmd *cmd, u32 n) +{ return -ENODEV; } + +static inline int rpmh_write_async(const struct device *dev, + enum rpmh_state state, + const struct tcs_cmd *cmd, u32 n) +{ return -ENODEV; } + +static inline int rpmh_write_batch(const struct device *dev, + enum rpmh_state state, + const struct tcs_cmd *cmd, u32 *n) +{ return -ENODEV; } + +static inline void rpmh_invalidate(const struct device *dev) +{ +} + +#endif /* CONFIG_QCOM_RPMH */ + +#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..3acca067c72b --- /dev/null +++ b/include/soc/qcom/tcs.h @@ -0,0 +1,81 @@ +/* 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. + * @wait_for_compl: wait until we get a response from the h/w accelerator + * (same as setting cmd->wait for all commands in 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 wait_for_compl; + 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__ */

Replace unused/unsupported Linux headers with appropriate U-Boot alternatives.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/qcom/cmd-db.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index d84572662017..685b6d530136 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -3,18 +3,16 @@ * Copyright (c) 2016-2018, 2020, The Linux Foundation. All rights reserved. * Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved. */
-#include <linux/bitfield.h> -#include <linux/debugfs.h> +#define pr_fmt(fmt) "cmd-db: " fmt + +#include <dm/ofnode.h> +#include <dm/device_compat.h> #include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_reserved_mem.h> -#include <linux/platform_device.h> -#include <linux/seq_file.h> #include <linux/types.h> +#include <linux/ioport.h> +#include <linux/byteorder/generic.h>
#include <soc/qcom/cmd-db.h>
#define NUM_PRIORITY 2

Due to our simpler rpmh-rsc driver and lack of debugfs, we don't need quite a few cmd-db functions, just drop them.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/qcom/cmd-db.c | 144 ---------------------------------------------- include/soc/qcom/cmd-db.h | 15 ----- 2 files changed, 159 deletions(-)
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index 685b6d530136..b6426ac3cafc 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -193,150 +193,8 @@ u32 cmd_db_read_addr(const char *id) return ret < 0 ? 0 : le32_to_cpu(ent->addr); } EXPORT_SYMBOL_GPL(cmd_db_read_addr);
-/** - * 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_GPL(cmd_db_read_aux_data); - -/** - * cmd_db_match_resource_addr() - Compare if both Resource addresses are same - * - * @addr1: Resource address to compare - * @addr2: Resource address to compare - * - * Return: true if two addresses refer to the same resource, false otherwise - */ -bool cmd_db_match_resource_addr(u32 addr1, u32 addr2) -{ - /* - * Each RPMh VRM accelerator resource has 3 or 4 contiguous 4-byte - * aligned addresses associated with it. Ignore the offset to check - * for VRM requests. - */ - if (addr1 == addr2) - return true; - else if (SLAVE_ID(addr1) == CMD_DB_HW_VRM && VRM_ADDR(addr1) == VRM_ADDR(addr2)) - return true; - - return false; -} -EXPORT_SYMBOL_GPL(cmd_db_match_resource_addr); - -/** - * 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_GPL(cmd_db_read_slave_id); - -#ifdef CONFIG_DEBUG_FS -static int cmd_db_debugfs_dump(struct seq_file *seq, void *p) -{ - int i, j; - const struct rsc_hdr *rsc; - const struct entry_header *ent; - const char *name; - u16 len, version; - u8 major, minor; - - seq_puts(seq, "Command DB DUMP\n"); - - for (i = 0; i < MAX_SLV_ID; i++) { - rsc = &cmd_db_header->header[i]; - if (!rsc->slv_id) - break; - - switch (le16_to_cpu(rsc->slv_id)) { - case CMD_DB_HW_ARC: - name = "ARC"; - break; - case CMD_DB_HW_VRM: - name = "VRM"; - break; - case CMD_DB_HW_BCM: - name = "BCM"; - break; - default: - name = "Unknown"; - break; - } - - version = le16_to_cpu(rsc->version); - major = version >> 8; - minor = version; - - seq_printf(seq, "Slave %s (v%u.%u)\n", name, major, minor); - seq_puts(seq, "-------------------------\n"); - - ent = rsc_to_entry_header(rsc); - for (j = 0; j < le16_to_cpu(rsc->cnt); j++, ent++) { - seq_printf(seq, "0x%05x: %*pEp", le32_to_cpu(ent->addr), - (int)strnlen(ent->id, sizeof(ent->id)), ent->id); - - len = le16_to_cpu(ent->len); - if (len) { - seq_printf(seq, " [%*ph]", - len, rsc_offset(rsc, ent)); - } - seq_putc(seq, '\n'); - } - } - - return 0; -} - -static int open_cmd_db_debugfs(struct inode *inode, struct file *file) -{ - return single_open(file, cmd_db_debugfs_dump, inode->i_private); -} -#endif - -static const struct file_operations cmd_db_debugfs_ops = { -#ifdef CONFIG_DEBUG_FS - .open = open_cmd_db_debugfs, -#endif - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - static int cmd_db_dev_probe(struct platform_device *pdev) { struct reserved_mem *rmem; int ret = 0; @@ -358,10 +216,8 @@ static int cmd_db_dev_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Invalid Command DB Magic\n"); return -EINVAL; }
- debugfs_create_file("cmd-db", 0400, NULL, NULL, &cmd_db_debugfs_ops); - device_set_pm_not_required(&pdev->dev);
return 0; } diff --git a/include/soc/qcom/cmd-db.h b/include/soc/qcom/cmd-db.h index 47a6cab75e63..753c7923f8e5 100644 --- a/include/soc/qcom/cmd-db.h +++ b/include/soc/qcom/cmd-db.h @@ -21,28 +21,13 @@ enum cmd_db_hw_type {
#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); - -bool cmd_db_match_resource_addr(u32 addr1, u32 addr2); - -enum cmd_db_hw_type cmd_db_read_slave_id(const char *resource_id); - int cmd_db_ready(void); #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 bool cmd_db_match_resource_addr(u32 addr1, u32 addr2) -{ return false; } - -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__ */

Using the driver model for cmd-db is fine, but it's unnecessary complexity which we can just avoid in U-Boot. Instead let's just have a function to initialize it and call said function when initializing rpmh.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/qcom/cmd-db.c | 74 ++++++++++++++--------------------------------- include/soc/qcom/cmd-db.h | 4 +-- 2 files changed, 24 insertions(+), 54 deletions(-)
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index b6426ac3cafc..4d3fd4db8852 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -122,24 +122,8 @@ rsc_offset(const struct rsc_hdr *hdr, const struct entry_header *ent)
return cmd_db_header->data + offset + loffset; }
-/** - * cmd_db_ready - Indicates if command DB is available - * - * Return: 0 on success, errno otherwise - */ -int cmd_db_ready(void) -{ - if (cmd_db_header == NULL) - return -EPROBE_DEFER; - else if (!cmd_db_magic_matches(cmd_db_header)) - return -EINVAL; - - return 0; -} -EXPORT_SYMBOL_GPL(cmd_db_ready); - 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; @@ -193,55 +177,41 @@ u32 cmd_db_read_addr(const char *id) return ret < 0 ? 0 : le32_to_cpu(ent->addr); } EXPORT_SYMBOL_GPL(cmd_db_read_addr);
-static int cmd_db_dev_probe(struct platform_device *pdev) +int cmd_db_init(void) { - struct reserved_mem *rmem; - int ret = 0; + void __iomem *base; + ofnode rmem, node;
- rmem = of_reserved_mem_lookup(pdev->dev.of_node); - if (!rmem) { - dev_err(&pdev->dev, "failed to acquire memory region\n"); - return -EINVAL; + if (cmd_db_header) + return 0; + + rmem = ofnode_path("/reserved-memory"); + ofnode_for_each_subnode(node, rmem) { + if (ofnode_device_is_compatible(node, "qcom,cmd-db")) + goto found; }
- cmd_db_header = memremap(rmem->base, rmem->size, MEMREMAP_WB); - if (!cmd_db_header) { - ret = -ENOMEM; - cmd_db_header = NULL; - return ret; + log_err("%s: Failed to find cmd-db node\n", __func__); + return -ENOENT; +found: + 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)) { - dev_err(&pdev->dev, "Invalid Command DB Magic\n"); + log_err("%s: Invalid Command DB Magic\n", __func__); return -EINVAL; }
- device_set_pm_not_required(&pdev->dev); - return 0; } - -static const struct of_device_id cmd_db_match_table[] = { - { .compatible = "qcom,cmd-db" }, - { } -}; -MODULE_DEVICE_TABLE(of, cmd_db_match_table); - -static struct platform_driver cmd_db_dev_driver = { - .probe = cmd_db_dev_probe, - .driver = { - .name = "cmd-db", - .of_match_table = cmd_db_match_table, - .suppress_bind_attrs = true, - }, -}; - -static int __init cmd_db_device_init(void) -{ - return platform_driver_register(&cmd_db_dev_driver); -} -core_initcall(cmd_db_device_init); +EXPORT_SYMBOL(cmd_db_init);
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Command DB Driver"); MODULE_LICENSE("GPL v2"); diff --git a/include/soc/qcom/cmd-db.h b/include/soc/qcom/cmd-db.h index 753c7923f8e5..535164cc7fb0 100644 --- a/include/soc/qcom/cmd-db.h +++ b/include/soc/qcom/cmd-db.h @@ -21,13 +21,13 @@ enum cmd_db_hw_type {
#if IS_ENABLED(CONFIG_QCOM_COMMAND_DB) u32 cmd_db_read_addr(const char *resource_id);
-int cmd_db_ready(void); +int cmd_db_init(void); #else static inline u32 cmd_db_read_addr(const char *resource_id) { return 0; }
-static inline int cmd_db_ready(void) +static inline int cmd_db_init(void) { return -ENODEV; } #endif /* CONFIG_QCOM_COMMAND_DB */ #endif /* __QCOM_COMMAND_DB_H__ */

Hi Caleb,
On Thu, 11 Jul 2024 at 17:47, Caleb Connolly caleb.connolly@linaro.org wrote:
Using the driver model for cmd-db is fine, but it's unnecessary complexity which we can just avoid in U-Boot. Instead let's just have a function to initialize it and call said function when initializing rpmh.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org
drivers/soc/qcom/cmd-db.c | 74 ++++++++++++++--------------------------------- include/soc/qcom/cmd-db.h | 4 +-- 2 files changed, 24 insertions(+), 54 deletions(-)
We should use driver model for drivers.
Regards, Simon

Keep the header pointer in the .data section so we don't initialize it again after relocation, adjust cmd_db_get_header() to work with the U-Boot API, and skip validating the header since all cmd-db users are children of the rpmh-rsc and those children will only probe if cmd-db initializes successfully.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/qcom/cmd-db.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index 4d3fd4db8852..b852a269ae12 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -104,9 +104,9 @@ static bool cmd_db_magic_matches(const struct cmd_db_header *header)
return memcmp(magic, CMD_DB_MAGIC, ARRAY_SIZE(CMD_DB_MAGIC)) == 0; }
-static struct cmd_db_header *cmd_db_header; +static struct cmd_db_header *cmd_db_header __section(".data") = NULL;
static inline const void *rsc_to_entry_header(const struct rsc_hdr *hdr) { u16 offset = le16_to_cpu(hdr->header_offset); @@ -127,16 +127,12 @@ 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 ret, i, j; + int i, j; u8 query[sizeof(ent->id)] __nonstring;
- ret = cmd_db_ready(); - if (ret) - return ret; - - strtomem_pad(query, id, 0); + 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)

Since U-Boot is single threaded, we can avoid most of the complexity that comes with handling more than one in-flight TCS. Drop all the rpmh code associated with multi-threading as we'll instead wait for a response on each TCS.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/qcom/rpmh-internal.h | 4 - drivers/soc/qcom/rpmh-rsc.c | 562 --------------------------------------- 2 files changed, 566 deletions(-)
diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index e3cf1beff803..12c5b8d9cf86 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -139,10 +139,6 @@ struct rsc_drv { 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); - -void rpmh_tx_done(const struct tcs_request *msg); -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 index de86009ecd91..fc8321bf208f 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -246,49 +246,8 @@ static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int tcs_id, 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); -} - /** * 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. @@ -331,158 +290,8 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv,
return tcs; }
-/** - * get_req_from_tcs() - Get a stashed request that was xfering on the given TCS. - * @drv: The RSC controller. - * @tcs_id: The global ID of this TCS. - * - * For ACTIVE_ONLY transfers we want to call back into the client when the - * transfer finishes. To do this we need the "request" that the client - * originally provided us. This function grabs the request that we stashed - * when we started the transfer. - * - * This only makes sense for ACTIVE_ONLY transfers since those are the only - * ones we track sending (the only ones we enable interrupts for and the only - * ones we call back to the client for). - * - * Return: The stashed request. - */ -static const struct tcs_request *get_req_from_tcs(struct rsc_drv *drv, - int tcs_id) -{ - struct tcs_group *tcs; - int i; - - for (i = 0; i < TCS_TYPE_NR; i++) { - tcs = &drv->tcs[i]; - if (tcs->mask & BIT(tcs_id)) - return tcs->req[tcs_id - tcs->offset]; - } - - return NULL; -} - -/** - * __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); - } -} - -/** - * enable_tcs_irq() - Enable or disable interrupts on the given TCS. - * @drv: The controller. - * @tcs_id: The global ID of this TCS. - * @enable: If true then enable; if false then disable - * - * We only ever call this when we borrow a wake TCS for an active-only - * transfer. For active-only TCSes interrupts are always left enabled. - */ -static void enable_tcs_irq(struct rsc_drv *drv, int tcs_id, bool enable) -{ - u32 data; - u32 reg = drv->regs[RSC_DRV_IRQ_ENABLE]; - - data = readl_relaxed(drv->tcs_base + reg); - if (enable) - data |= BIT(tcs_id); - else - data &= ~BIT(tcs_id); - writel_relaxed(data, drv->tcs_base + reg); -} - -/** - * tcs_tx_done() - TX Done interrupt handler. - * @irq: The IRQ number (ignored). - * @p: Pointer to "struct rsc_drv". - * - * Called for ACTIVE_ONLY transfers (those are the only ones we enable the - * IRQ for) when a transfer is done. - * - * Return: IRQ_HANDLED - */ -static irqreturn_t tcs_tx_done(int irq, void *p) -{ - struct rsc_drv *drv = p; - int i; - unsigned long irq_status; - const struct tcs_request *req; - - irq_status = readl_relaxed(drv->tcs_base + drv->regs[RSC_DRV_IRQ_STATUS]); - - for_each_set_bit(i, &irq_status, BITS_PER_TYPE(u32)) { - req = get_req_from_tcs(drv, i); - if (WARN_ON(!req)) - goto skip; - - trace_rpmh_tx_done(drv, i, req); - - /* - * If wake tcs was re-purposed for sending active - * votes, clear AMC trigger & enable modes and - * disable interrupt for this TCS - */ - if (!drv->tcs[ACTIVE_TCS].num_tcs) - __tcs_set_trigger(drv, i, false); -skip: - /* Reclaim the TCS */ - write_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], i, 0); - writel_relaxed(BIT(i), drv->tcs_base + drv->regs[RSC_DRV_IRQ_CLEAR]); - spin_lock(&drv->lock); - clear_bit(i, drv->tcs_in_use); - /* - * Disable interrupt for WAKE TCS to avoid being - * spammed with interrupts coming when the solver - * sends its wake votes. - */ - if (!drv->tcs[ACTIVE_TCS].num_tcs) - enable_tcs_irq(drv, i, false); - spin_unlock(&drv->lock); - wake_up(&drv->tcs_wait); - if (req) - rpmh_tx_done(req); - } - - return IRQ_HANDLED; -} - /** * __tcs_buffer_write() - Write to TCS hardware from a request; don't trigger. * @drv: The controller. * @tcs_id: The global ID of this TCS. @@ -523,104 +332,8 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, 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); }
-/** - * check_for_req_inflight() - Look to see if conflicting cmds are in flight. - * @drv: The controller. - * @tcs: A pointer to the tcs_group used for ACTIVE_ONLY transfers. - * @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 will walk through the TCSes in the group and check if any of them - * appear to be sending to addresses referenced in the message. If it finds - * one it'll return -EBUSY. - * - * Only for use for active-only transfers. - * - * Must be called with the drv->lock held since that protects tcs_in_use. - * - * Return: 0 if nothing in flight or -EBUSY if we should try again later. - * The caller must re-enable interrupts between tries since that's - * the only way tcs_in_use will ever be updated and the only way - * RSC_DRV_CMD_ENABLE will ever be cleared. - */ -static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs, - const struct tcs_request *msg) -{ - unsigned long curr_enabled; - u32 addr; - int j, k; - int i = tcs->offset; - - for_each_set_bit_from(i, drv->tcs_in_use, tcs->offset + tcs->num_tcs) { - curr_enabled = read_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], i); - - for_each_set_bit(j, &curr_enabled, MAX_CMDS_PER_TCS) { - addr = read_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_ADDR], i, j); - for (k = 0; k < msg->num_cmds; k++) { - if (cmd_db_match_resource_addr(msg->cmds[k].addr, addr)) - return -EBUSY; - } - } - } - - return 0; -} - -/** - * find_free_tcs() - Find free tcs in the given tcs_group; only for active. - * @tcs: A pointer to the active-only tcs_group (or the wake tcs_group if - * we borrowed it because there are zero active-only ones). - * - * Must be called with the drv->lock held since that protects tcs_in_use. - * - * Return: The first tcs that's free or -EBUSY if all in use. - */ -static int find_free_tcs(struct tcs_group *tcs) -{ - const struct rsc_drv *drv = tcs->drv; - unsigned long i; - unsigned long max = tcs->offset + tcs->num_tcs; - - i = find_next_zero_bit(drv->tcs_in_use, max, tcs->offset); - if (i >= max) - return -EBUSY; - - return i; -} - -/** - * claim_tcs_for_req() - Claim a tcs in the given tcs_group; only for active. - * @drv: The controller. - * @tcs: The tcs_group used for ACTIVE_ONLY transfers. - * @msg: The data to be sent. - * - * Claims a tcs in the given tcs_group while making sure that no existing cmd - * is in flight that would conflict with the one in @msg. - * - * Context: Must be called with the drv->lock held since that protects - * tcs_in_use. - * - * Return: The id of the claimed tcs or -EBUSY if a matching msg is in flight - * or the tcs_group is full. - */ -static int claim_tcs_for_req(struct rsc_drv *drv, struct tcs_group *tcs, - const struct tcs_request *msg) -{ - int ret; - - /* - * The h/w does not like if we send a request to the same address, - * when one is already in-flight or being processed. - */ - ret = check_for_req_inflight(drv, tcs, msg); - if (ret) - return ret; - - return find_free_tcs(tcs); -} - /** * rpmh_rsc_send_data() - Write / trigger active-only message. * @drv: The controller. * @msg: The data to be sent. @@ -668,9 +381,8 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) * 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); - enable_tcs_irq(drv, tcs_id, true); } spin_unlock_irq(&drv->lock);
/* @@ -681,286 +393,12 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) * - 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);
return 0; }
-/** - * find_slots() - Find a place to write the given message. - * @tcs: The tcs group to search. - * @msg: The message we want to find room for. - * @tcs_id: If we return 0 from the function, we return the global ID of the - * TCS to write to here. - * @cmd_id: If we return 0 from the function, we return the index of - * the command array of the returned TCS where the client should - * start writing the message. - * - * Only for use on sleep/wake TCSes since those are the only ones we maintain - * tcs->slots for. - * - * Return: -ENOMEM if there was no room, else 0. - */ -static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, - int *tcs_id, int *cmd_id) -{ - int slot, offset; - int i = 0; - - /* Do over, until we can fit the full payload in a single TCS */ - do { - slot = bitmap_find_next_zero_area(tcs->slots, MAX_TCS_SLOTS, - i, msg->num_cmds, 0); - if (slot >= tcs->num_tcs * tcs->ncpt) - return -ENOMEM; - i += tcs->ncpt; - } while (slot + msg->num_cmds - 1 >= i); - - bitmap_set(tcs->slots, slot, msg->num_cmds); - - offset = slot / tcs->ncpt; - *tcs_id = offset + tcs->offset; - *cmd_id = slot % tcs->ncpt; - - return 0; -} - -/** - * rpmh_rsc_write_ctrl_data() - Write request to controller but don't trigger. - * @drv: The controller. - * @msg: The data to be written to the controller. - * - * This should only be called for sleep/wake state, never active-only - * state. - * - * The caller must ensure that no other RPMH actions are happening and the - * controller is idle when this function is called since it runs lockless. - * - * Return: 0 if no error; else -error. - */ -int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) -{ - struct tcs_group *tcs; - int tcs_id = 0, cmd_id = 0; - int ret; - - tcs = get_tcs_for_msg(drv, msg); - if (IS_ERR(tcs)) - return PTR_ERR(tcs); - - /* find the TCS id and the command in the TCS to write to */ - ret = find_slots(tcs, msg, &tcs_id, &cmd_id); - if (!ret) - __tcs_buffer_write(drv, tcs_id, cmd_id, msg); - - return ret; -} - -/** - * rpmh_rsc_ctrlr_is_busy() - Check if any of the AMCs are busy. - * @drv: The controller - * - * Checks if any of the AMCs are busy in handling ACTIVE sets. - * This is called from the last cpu powering down before flushing - * SLEEP and WAKE sets. If AMCs are busy, controller can not enter - * power collapse, so deny from the last cpu's pm notification. - * - * Context: Must be called with the drv->lock held. - * - * Return: - * * False - AMCs are idle - * * True - AMCs are busy - */ -static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) -{ - unsigned long set; - const struct tcs_group *tcs = &drv->tcs[ACTIVE_TCS]; - unsigned long max; - - /* - * If we made an active request on a RSC that does not have a - * dedicated TCS for active state use, then re-purposed wake TCSes - * should be checked for not busy, because we used wake TCSes for - * active requests in this case. - */ - if (!tcs->num_tcs) - tcs = &drv->tcs[WAKE_TCS]; - - max = tcs->offset + tcs->num_tcs; - set = find_next_bit(drv->tcs_in_use, max, tcs->offset); - - return set < max; -} - -/** - * rpmh_rsc_write_next_wakeup() - Write next wakeup in CONTROL_TCS. - * @drv: The controller - * - * Writes maximum wakeup cycles when called from suspend. - * Writes earliest hrtimer wakeup when called from idle. - */ -void rpmh_rsc_write_next_wakeup(struct rsc_drv *drv) -{ - ktime_t now, wakeup; - u64 wakeup_us, wakeup_cycles = ~0; - u32 lo, hi; - - if (!drv->tcs[CONTROL_TCS].num_tcs || !drv->genpd_nb.notifier_call) - return; - - /* Set highest time when system (timekeeping) is suspended */ - if (system_state == SYSTEM_SUSPEND) - goto exit; - - /* Find the earliest hrtimer wakeup from online cpus */ - wakeup = dev_pm_genpd_get_next_hrtimer(drv->dev); - - /* Find the relative wakeup in kernel time scale */ - now = ktime_get(); - wakeup = ktime_sub(wakeup, now); - wakeup_us = ktime_to_us(wakeup); - - /* Convert the wakeup to arch timer scale */ - wakeup_cycles = USECS_TO_CYCLES(wakeup_us); - wakeup_cycles += arch_timer_read_counter(); - -exit: - lo = wakeup_cycles & RSC_DRV_CTL_TCS_DATA_LO_MASK; - hi = wakeup_cycles >> RSC_DRV_CTL_TCS_DATA_SIZE; - hi &= RSC_DRV_CTL_TCS_DATA_HI_MASK; - hi |= RSC_DRV_CTL_TCS_DATA_HI_VALID; - - writel_relaxed(lo, drv->base + RSC_DRV_CTL_TCS_DATA_LO); - writel_relaxed(hi, drv->base + RSC_DRV_CTL_TCS_DATA_HI); -} - -/** - * rpmh_rsc_cpu_pm_callback() - Check if any of the AMCs are busy. - * @nfb: Pointer to the notifier block in struct rsc_drv. - * @action: CPU_PM_ENTER, CPU_PM_ENTER_FAILED, or CPU_PM_EXIT. - * @v: Unused - * - * This function is given to cpu_pm_register_notifier so we can be informed - * about when CPUs go down. When all CPUs go down we know no more active - * transfers will be started so we write sleep/wake sets. This function gets - * called from cpuidle code paths and also at system suspend time. - * - * If its last CPU going down and AMCs are not busy then writes cached sleep - * and wake messages to TCSes. The firmware then takes care of triggering - * them when entering deepest low power modes. - * - * Return: See cpu_pm_register_notifier() - */ -static int rpmh_rsc_cpu_pm_callback(struct notifier_block *nfb, - unsigned long action, void *v) -{ - struct rsc_drv *drv = container_of(nfb, struct rsc_drv, rsc_pm); - int ret = NOTIFY_OK; - int cpus_in_pm; - - switch (action) { - case CPU_PM_ENTER: - cpus_in_pm = atomic_inc_return(&drv->cpus_in_pm); - /* - * NOTE: comments for num_online_cpus() point out that it's - * only a snapshot so we need to be careful. It should be OK - * for us to use, though. It's important for us not to miss - * if we're the last CPU going down so it would only be a - * problem if a CPU went offline right after we did the check - * AND that CPU was not idle AND that CPU was the last non-idle - * CPU. That can't happen. CPUs would have to come out of idle - * before the CPU could go offline. - */ - if (cpus_in_pm < num_online_cpus()) - return NOTIFY_OK; - break; - case CPU_PM_ENTER_FAILED: - case CPU_PM_EXIT: - atomic_dec(&drv->cpus_in_pm); - return NOTIFY_OK; - default: - return NOTIFY_DONE; - } - - /* - * It's likely we're on the last CPU. Grab the drv->lock and write - * out the sleep/wake commands to RPMH hardware. Grabbing the lock - * means that if we race with another CPU coming up we are still - * guaranteed to be safe. If another CPU came up just after we checked - * and has grabbed the lock or started an active transfer then we'll - * notice we're busy and abort. If another CPU comes up after we start - * flushing it will be blocked from starting an active transfer until - * we're done flushing. If another CPU starts an active transfer after - * we release the lock we're still OK because we're no longer the last - * CPU. - */ - if (spin_trylock(&drv->lock)) { - if (rpmh_rsc_ctrlr_is_busy(drv) || rpmh_flush(&drv->client)) - ret = NOTIFY_BAD; - spin_unlock(&drv->lock); - } else { - /* Another CPU must be up */ - return NOTIFY_OK; - } - - if (ret == NOTIFY_BAD) { - /* Double-check if we're here because someone else is up */ - if (cpus_in_pm < num_online_cpus()) - ret = NOTIFY_OK; - else - /* We won't be called w/ CPU_PM_ENTER_FAILED */ - atomic_dec(&drv->cpus_in_pm); - } - - return ret; -} - -/** - * rpmh_rsc_pd_callback() - Check if any of the AMCs are busy. - * @nfb: Pointer to the genpd notifier block in struct rsc_drv. - * @action: GENPD_NOTIFY_PRE_OFF, GENPD_NOTIFY_OFF, GENPD_NOTIFY_PRE_ON or GENPD_NOTIFY_ON. - * @v: Unused - * - * This function is given to dev_pm_genpd_add_notifier() so we can be informed - * about when cluster-pd is going down. When cluster go down we know no more active - * transfers will be started so we write sleep/wake sets. This function gets - * called from cpuidle code paths and also at system suspend time. - * - * If AMCs are not busy then writes cached sleep and wake messages to TCSes. - * The firmware then takes care of triggering them when entering deepest low power modes. - * - * Return: - * * NOTIFY_OK - success - * * NOTIFY_BAD - failure - */ -static int rpmh_rsc_pd_callback(struct notifier_block *nfb, - unsigned long action, void *v) -{ - struct rsc_drv *drv = container_of(nfb, struct rsc_drv, genpd_nb); - - /* We don't need to lock as genpd on/off are serialized */ - if ((action == GENPD_NOTIFY_PRE_OFF) && - (rpmh_rsc_ctrlr_is_busy(drv) || rpmh_flush(&drv->client))) - return NOTIFY_BAD; - - return NOTIFY_OK; -} - -static int rpmh_rsc_pd_attach(struct rsc_drv *drv, struct device *dev) -{ - int ret; - - pm_runtime_enable(dev); - drv->genpd_nb.notifier_call = rpmh_rsc_pd_callback; - ret = dev_pm_genpd_add_notifier(dev, &drv->genpd_nb); - if (ret) - pm_runtime_disable(dev); - - return ret; -} - static int rpmh_probe_tcs_config(struct platform_device *pdev, struct rsc_drv *drv) { struct tcs_type_config { u32 type;

Remove unsupported / unused Linux headers and add those needed for U-Boot.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/qcom/rpmh-rsc.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-)
diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index fc8321bf208f..c09214552cfb 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -5,39 +5,28 @@ */
#define pr_fmt(fmt) "%s " fmt, KBUILD_MODNAME
-#include <linux/atomic.h> -#include <linux/cpu_pm.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <dm/lists.h> +#include <dm/ofnode.h> +#include <linux/bitmap.h> #include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/iopoll.h> -#include <linux/kernel.h> -#include <linux/ktime.h> -#include <linux/list.h> -#include <linux/module.h> -#include <linux/notifier.h> -#include <linux/of.h> -#include <linux/of_irq.h> -#include <linux/of_platform.h> -#include <linux/platform_device.h> -#include <linux/pm_domain.h> -#include <linux/pm_runtime.h> -#include <linux/slab.h> -#include <linux/spinlock.h> -#include <linux/wait.h> +#include <linux/err.h> +#include <linux/types.h> +#include <asm/bitops.h> +#include <asm/io.h> + +#include <log.h>
-#include <clocksource/arm_arch_timer.h> #include <soc/qcom/cmd-db.h> #include <soc/qcom/tcs.h> #include <dt-bindings/soc/qcom,rpmh-rsc.h>
#include "rpmh-internal.h"
-#define CREATE_TRACE_POINTS -#include "trace-rpmh.h" -
#define RSC_DRV_ID 0
#define MAJOR_VER_MASK 0xFF

Rework the rpmh-rsc initialization to use U-Boot's driver model and initialize cmd-db.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/qcom/rpmh-internal.h | 14 ++-- drivers/soc/qcom/rpmh-rsc.c | 143 +++++++++++++-------------------------- 2 files changed, 50 insertions(+), 107 deletions(-)
diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 12c5b8d9cf86..ac8f6c35a7a4 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -7,17 +7,18 @@ #ifndef __RPM_INTERNAL_H__ #define __RPM_INTERNAL_H__
#include <linux/bitmap.h> -#include <linux/wait.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 @@ -63,10 +64,9 @@ struct tcs_group { */ struct rpmh_request { struct tcs_request msg; struct tcs_cmd cmd[MAX_RPMH_PAYLOAD]; - struct completion *completion; - const struct device *dev; + const struct udevice *dev; bool needs_free; };
/** @@ -78,9 +78,8 @@ struct rpmh_request { * @batch_cache: Cache sleep and wake requests sent as batch */ struct rpmh_ctrlr { struct list_head cache; - spinlock_t cache_lock; bool dirty; struct list_head batch_cache; };
@@ -122,17 +121,12 @@ struct rsc_drv { void __iomem *base; void __iomem *tcs_base; int id; int num_tcs; - struct notifier_block rsc_pm; - struct notifier_block genpd_nb; - atomic_t cpus_in_pm; struct tcs_group tcs[TCS_TYPE_NR]; DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR); - spinlock_t lock; - wait_queue_head_t tcs_wait; struct rpmh_ctrlr client; - struct device *dev; + struct udevice *dev; struct rsc_ver ver; u32 *regs; };
diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index c09214552cfb..d4ef88dda184 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -386,20 +386,20 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg)
return 0; }
-static int rpmh_probe_tcs_config(struct platform_device *pdev, struct rsc_drv *drv) +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 } }; - struct device_node *dn = pdev->dev.of_node; + ofnode dn = dev_ofnode(dev); u32 config, max_tcs, ncpt, offset; int i, ret, n, st = 0; struct tcs_group *tcs;
- ret = of_property_read_u32(dn, "qcom,tcs-offset", &offset); + ret = ofnode_read_u32(dn, "qcom,tcs-offset", &offset); if (ret) return ret; drv->tcs_base = drv->base + offset;
@@ -411,24 +411,15 @@ static int rpmh_probe_tcs_config(struct platform_device *pdev, struct rsc_drv *d
ncpt = config & (DRV_NCPT_MASK << DRV_NCPT_SHIFT); ncpt = ncpt >> DRV_NCPT_SHIFT;
- n = of_property_count_u32_elems(dn, "qcom,tcs-config"); - if (n != 2 * TCS_TYPE_NR) - return -EINVAL; + n = ofnode_read_u32_array(dn, "qcom,tcs-config", (u32 *)tcs_cfg, 2 * TCS_TYPE_NR); + if (n < 0) { + log_err("RPMh: %s: error reading qcom,tcs-config %d\n", dev->name, n); + return n; + }
for (i = 0; i < TCS_TYPE_NR; i++) { - ret = of_property_read_u32_index(dn, "qcom,tcs-config", - i * 2, &tcs_cfg[i].type); - if (ret) - return ret; - if (tcs_cfg[i].type >= TCS_TYPE_NR) - return -EINVAL; - - ret = of_property_read_u32_index(dn, "qcom,tcs-config", - i * 2 + 1, &tcs_cfg[i].n); - if (ret) - return ret; if (tcs_cfg[i].n > MAX_TCS_PER_TYPE) return -EINVAL; }
@@ -457,43 +448,39 @@ static int rpmh_probe_tcs_config(struct platform_device *pdev, struct rsc_drv *d
return 0; }
-static int rpmh_rsc_probe(struct platform_device *pdev) +static int rpmh_rsc_bind(struct udevice *dev) { - struct device_node *dn = pdev->dev.of_node; - struct rsc_drv *drv; - char drv_id[10] = {0}; - int ret, irq; - u32 solver_config; - u32 rsc_id; + int ret;
- /* - * Even though RPMh doesn't directly use cmd-db, all of its children - * do. To avoid adding this check to our children we'll do it now. - */ - ret = cmd_db_ready(); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&pdev->dev, "Command DB not available (%d)\n", - ret); - return ret; - } - - drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); - if (!drv) - return -ENOMEM; - - ret = of_property_read_u32(dn, "qcom,drv-id", &drv->id); + ret = cmd_db_init(); if (ret) return ret;
- drv->name = of_get_property(dn, "label", NULL); + return 0; +} + +static int rpmh_rsc_probe(struct udevice *dev) +{ + ofnode dn = dev_ofnode(dev); + struct rsc_drv *drv; + char drv_id[10] = {0}; + int ret; + u32 rsc_id; + + 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(&pdev->dev); + drv->name = dev->name;
snprintf(drv_id, ARRAY_SIZE(drv_id), "drv-%d", drv->id); - drv->base = devm_platform_ioremap_resource_byname(pdev, 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); @@ -506,84 +493,46 @@ static int rpmh_rsc_probe(struct platform_device *pdev) drv->regs = rpmh_rsc_reg_offset_ver_3_0; else drv->regs = rpmh_rsc_reg_offset_ver_2_7;
- ret = rpmh_probe_tcs_config(pdev, drv); + ret = rpmh_probe_tcs_config(dev, drv); if (ret) return ret;
spin_lock_init(&drv->lock); init_waitqueue_head(&drv->tcs_wait); bitmap_zero(drv->tcs_in_use, MAX_TCS_NR);
- irq = platform_get_irq(pdev, drv->id); - if (irq < 0) - return irq; - - ret = devm_request_irq(&pdev->dev, irq, tcs_tx_done, - IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, - drv->name, drv); - if (ret) - return ret; - - /* - * CPU PM/genpd notification are not required for controllers that support - * 'HW solver' mode where they can be in autonomous mode executing low - * power mode to power down. - */ - solver_config = readl_relaxed(drv->base + drv->regs[DRV_SOLVER_CONFIG]); - solver_config &= DRV_HW_SOLVER_MASK << DRV_HW_SOLVER_SHIFT; - solver_config = solver_config >> DRV_HW_SOLVER_SHIFT; - if (!solver_config) { - if (pdev->dev.pm_domain) { - ret = rpmh_rsc_pd_attach(drv, &pdev->dev); - if (ret) - return ret; - } else { - drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback; - cpu_pm_register_notifier(&drv->rsc_pm); - } - } - /* 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(&pdev->dev, drv); - drv->dev = &pdev->dev; + dev_set_drvdata(dev, drv); + drv->dev = dev;
- ret = devm_of_platform_populate(&pdev->dev); - if (ret && pdev->dev.pm_domain) { - dev_pm_genpd_remove_notifier(&pdev->dev); - pm_runtime_disable(&pdev->dev); - } + log_debug("RPMh: %s: v%d.%d\n", dev->name, drv->ver.major, drv->ver.minor);
return ret; }
-static const struct of_device_id rpmh_drv_match[] = { - { .compatible = "qcom,rpmh-rsc", }, +static const struct udevice_id qcom_rpmh_ids[] = { + { .compatible = "qcom,rpmh-rsc" }, { } }; -MODULE_DEVICE_TABLE(of, rpmh_drv_match);
-static struct platform_driver rpmh_driver = { - .probe = rpmh_rsc_probe, - .driver = { - .name = "rpmh", - .of_match_table = rpmh_drv_match, - .suppress_bind_attrs = true, - }, +U_BOOT_DRIVER(qcom_rpmh_rsc) = { + .name = "qcom_rpmh_rsc", + .id = UCLASS_MISC, + .priv_auto = sizeof(struct rsc_drv), + .probe = rpmh_rsc_probe, + .bind = rpmh_rsc_bind, + .of_match = qcom_rpmh_ids, + /* rpmh is under CLUSTER_PD which we don't support, so skip trying to enable PDs */ + .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF, };
-static int __init rpmh_driver_init(void) -{ - return platform_driver_register(&rpmh_driver); -} -core_initcall(rpmh_driver_init); - MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Driver"); MODULE_LICENSE("GPL v2");

Minor adjustments to fix building with U-Boot and work correctly as a synchronous driver without interrupts. RPMh is fast enough that we can get away with just firing off requests and assuming they complete.
U-Boot behaviour changes are annotated with a "U-Boot:" comment.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/qcom/rpmh-rsc.c | 78 +++++++++++++++++---------------------------- 1 file changed, 29 insertions(+), 49 deletions(-)
diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index d4ef88dda184..566fa16baba9 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -134,16 +134,8 @@ enum { * | ...... | * +---------------------------------------------------+ */
-#define USECS_TO_CYCLES(time_usecs) \ - xloops_to_cycles((time_usecs) * 0x10C7UL) - -static inline unsigned long xloops_to_cycles(u64 xloops) -{ - return (xloops * loops_per_jiffy * HZ) >> 32; -} - static u32 rpmh_rsc_reg_offset_ver_2_7[] = { [RSC_DRV_TCS_OFFSET] = 672, [RSC_DRV_CMD_OFFSET] = 20, [DRV_SOLVER_CONFIG] = 0x04, @@ -248,37 +240,18 @@ static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int tcs_id, */ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, const struct tcs_request *msg) { - int type; - struct tcs_group *tcs; - - switch (msg->state) { - case RPMH_ACTIVE_ONLY_STATE: - type = ACTIVE_TCS; - break; - case RPMH_WAKE_ONLY_STATE: - type = WAKE_TCS; - break; - case RPMH_SLEEP_STATE: - type = SLEEP_TCS; - break; - default: + /* + * U-Boot: since we're single threaded and running synchronously we can + * just always used the first active TCS. + */ + if (msg->state != RPMH_ACTIVE_ONLY_STATE) { + log_err("WARN: only ACTIVE_ONLY state supported\n"); return ERR_PTR(-EINVAL); }
- /* - * If we are making an active request on a RSC that does not have a - * dedicated TCS for active state use, then re-purpose a wake TCS to - * send active votes. This is safe because we ensure any active-only - * transfers have finished before we use it (maybe by running from - * the last CPU in PM code). - */ - tcs = &drv->tcs[type]; - if (msg->state == RPMH_ACTIVE_ONLY_STATE && !tcs->num_tcs) - tcs = &drv->tcs[WAKE_TCS]; - - return tcs; + return &drv->tcs[ACTIVE_TCS]; }
/** * __tcs_buffer_write() - Write to TCS hardware from a request; don't trigger. @@ -298,11 +271,8 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, u32 cmd_enable = 0; struct tcs_cmd *cmd; int i, j;
- /* Convert all commands to RR when the request has wait_for_compl set */ - cmd_msgid |= msg->wait_for_compl ? CMD_MSGID_RESP_REQ : 0; - for (i = 0, j = cmd_id; i < msg->num_cmds; i++, j++) { cmd = &msg->cmds[i]; cmd_enable |= BIT(j); msgid = cmd_msgid; @@ -314,9 +284,11 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id,
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); - trace_rpmh_send_msg(drv, tcs_id, msg->state, j, msgid, cmd); + debug("tcs(m): %d [%s] cmd(n): %d msgid: %#x addr: %#x data: %#x complete: %d\n", + 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); @@ -346,34 +318,28 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, */ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) { struct tcs_group *tcs; - int tcs_id; - - might_sleep(); + int tcs_id, i; + u32 addr;
tcs = get_tcs_for_msg(drv, msg); if (IS_ERR(tcs)) return PTR_ERR(tcs);
- spin_lock_irq(&drv->lock); - - /* Wait forever for a free tcs. It better be there eventually! */ - wait_event_lock_irq(drv->tcs_wait, - (tcs_id = claim_tcs_for_req(drv, tcs, msg)) >= 0, - drv->lock); + /* 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; - set_bit(tcs_id, drv->tcs_in_use); + 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_irq(&drv->lock);
/* * These two can be done after the lock is released because: * - We marked "tcs_in_use" under lock. @@ -383,8 +349,22 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) * of __tcs_set_trigger() below. */ __tcs_buffer_write(drv, tcs_id, 0, msg);
+ /* U-Boot: Now wait for the TCS to be cleared, indicating that we're done */ + for (i = 0; i < USEC_PER_SEC; i++) { + addr = read_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_ADDR], i, 0); + if (addr != msg->cmds[0].addr) + break; + udelay(1); + } + + if (i == USEC_PER_SEC) { + log_err("%s: error writing %#x to %d:%#x\n", drv->name, + msg->cmds[0].addr, tcs_id, drv->regs[RSC_DRV_CMD_ADDR]); + return -EINVAL; + } + return 0; }
static int rpmh_probe_tcs_config(struct udevice *dev, struct rsc_drv *drv)

Drop unused/unsupported Linux headers and add dm/device.h for U-Boot.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/qcom/rpmh.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-)
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 8903ed956312..03ef4106c9a6 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -2,22 +2,14 @@ /* * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */
-#include <linux/atomic.h> +#include <dm/device.h> +#include <dm/device_compat.h> #include <linux/bug.h> -#include <linux/interrupt.h> -#include <linux/jiffies.h> #include <linux/kernel.h> #include <linux/list.h> -#include <linux/lockdep.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/spinlock.h> #include <linux/types.h> -#include <linux/wait.h>
#include <soc/qcom/rpmh.h>
#include "rpmh-internal.h"

A lot of the features in here are only relevant when running multi-threaded with interrupts. Drop everything except what we need to run single-threaded with a single TCS (which is all the rpmh-rsc framework in U-Boot supports).
Keep rpmh_write_async() for simplicity and make it wrap the regular rpmh_write().
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/qcom/rpmh.c | 371 ------------------------------------------------ include/soc/qcom/rpmh.h | 25 +--- 2 files changed, 3 insertions(+), 393 deletions(-)
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 03ef4106c9a6..22605e0291a1 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -31,124 +31,15 @@ }
#define ctrlr_to_drv(ctrlr) container_of(ctrlr, struct rsc_drv, client)
-/** - * struct cache_req: the request object for caching - * - * @addr: the address of the resource - * @sleep_val: the sleep vote - * @wake_val: the wake vote - * @list: linked list obj - */ -struct cache_req { - u32 addr; - u32 sleep_val; - u32 wake_val; - struct list_head list; -}; - -/** - * struct batch_cache_req - An entry in our batch catch - * - * @list: linked list obj - * @count: number of messages - * @rpm_msgs: the messages - */ - -struct batch_cache_req { - struct list_head list; - int count; - struct rpmh_request rpm_msgs[]; -}; - static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct device *dev) { struct rsc_drv *drv = dev_get_drvdata(dev->parent);
return &drv->client; }
-void rpmh_tx_done(const struct tcs_request *msg) -{ - struct rpmh_request *rpm_msg = container_of(msg, struct rpmh_request, - msg); - struct completion *compl = rpm_msg->completion; - bool free = rpm_msg->needs_free; - - if (!compl) - goto exit; - - /* Signal the blocking thread we are done */ - complete(compl); - -exit: - if (free) - kfree(rpm_msg); -} - -static struct cache_req *__find_req(struct rpmh_ctrlr *ctrlr, u32 addr) -{ - struct cache_req *p, *req = NULL; - - list_for_each_entry(p, &ctrlr->cache, list) { - if (p->addr == addr) { - req = p; - break; - } - } - - return req; -} - -static struct cache_req *cache_rpm_request(struct rpmh_ctrlr *ctrlr, - enum rpmh_state state, - struct tcs_cmd *cmd) -{ - struct cache_req *req; - unsigned long flags; - u32 old_sleep_val, old_wake_val; - - spin_lock_irqsave(&ctrlr->cache_lock, flags); - req = __find_req(ctrlr, cmd->addr); - if (req) - goto existing; - - req = kzalloc(sizeof(*req), GFP_ATOMIC); - if (!req) { - req = ERR_PTR(-ENOMEM); - goto unlock; - } - - req->addr = cmd->addr; - req->sleep_val = req->wake_val = UINT_MAX; - list_add_tail(&req->list, &ctrlr->cache); - -existing: - old_sleep_val = req->sleep_val; - old_wake_val = req->wake_val; - - switch (state) { - case RPMH_ACTIVE_ONLY_STATE: - case RPMH_WAKE_ONLY_STATE: - req->wake_val = cmd->data; - break; - case RPMH_SLEEP_STATE: - req->sleep_val = cmd->data; - break; - } - - ctrlr->dirty |= (req->sleep_val != old_sleep_val || - req->wake_val != old_wake_val) && - req->sleep_val != UINT_MAX && - req->wake_val != UINT_MAX; - -unlock: - spin_unlock_irqrestore(&ctrlr->cache_lock, flags); - - return req; -} - /** * __rpmh_write: Cache and send the RPMH request * * @dev: The device making the request @@ -199,40 +90,8 @@ static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state,
return 0; }
-/** - * rpmh_write_async: Write a set of RPMH commands - * - * @dev: The device making the request - * @state: Active/sleep set - * @cmd: The payload data - * @n: The number of elements in payload - * - * Write a set of RPMH commands, the order of commands is maintained - * and will be sent as a single shot. - */ -int rpmh_write_async(const struct device *dev, enum rpmh_state state, - const struct tcs_cmd *cmd, u32 n) -{ - struct rpmh_request *rpm_msg; - int ret; - - rpm_msg = kzalloc(sizeof(*rpm_msg), GFP_ATOMIC); - if (!rpm_msg) - return -ENOMEM; - rpm_msg->needs_free = true; - - ret = __fill_rpmh_msg(rpm_msg, state, cmd, n); - if (ret) { - kfree(rpm_msg); - return ret; - } - - return __rpmh_write(dev, state, rpm_msg); -} -EXPORT_SYMBOL_GPL(rpmh_write_async); - /** * rpmh_write: Write a set of RPMH commands and block until response * * @dev: The device making the request @@ -261,234 +120,4 @@ int rpmh_write(const struct device *dev, enum rpmh_state state, WARN_ON(!ret); return (ret > 0) ? 0 : -ETIMEDOUT; } EXPORT_SYMBOL_GPL(rpmh_write); - -static void cache_batch(struct rpmh_ctrlr *ctrlr, struct batch_cache_req *req) -{ - unsigned long flags; - - spin_lock_irqsave(&ctrlr->cache_lock, flags); - list_add_tail(&req->list, &ctrlr->batch_cache); - ctrlr->dirty = true; - spin_unlock_irqrestore(&ctrlr->cache_lock, flags); -} - -static int flush_batch(struct rpmh_ctrlr *ctrlr) -{ - struct batch_cache_req *req; - const struct rpmh_request *rpm_msg; - int ret = 0; - int i; - - /* Send Sleep/Wake requests to the controller, expect no response */ - list_for_each_entry(req, &ctrlr->batch_cache, list) { - for (i = 0; i < req->count; i++) { - rpm_msg = req->rpm_msgs + i; - ret = rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr), - &rpm_msg->msg); - if (ret) - break; - } - } - - return ret; -} - -/** - * rpmh_write_batch: Write multiple sets of RPMH commands and wait for the - * batch to finish. - * - * @dev: the device making the request - * @state: Active/sleep set - * @cmd: The payload data - * @n: The array of count of elements in each batch, 0 terminated. - * - * Write a request to the RSC controller without caching. If the request - * state is ACTIVE, then the requests are treated as completion request - * and sent to the controller immediately. The function waits until all the - * commands are complete. If the request was to SLEEP or WAKE_ONLY, then the - * request is sent as fire-n-forget and no ack is expected. - * - * May sleep. Do not call from atomic contexts for ACTIVE_ONLY requests. - */ -int rpmh_write_batch(const struct device *dev, enum rpmh_state state, - const struct tcs_cmd *cmd, u32 *n) -{ - struct batch_cache_req *req; - struct rpmh_request *rpm_msgs; - struct completion *compls; - struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); - unsigned long time_left; - int count = 0; - int ret, i; - void *ptr; - - if (!cmd || !n) - return -EINVAL; - - while (n[count] > 0) - count++; - if (!count) - return -EINVAL; - - ptr = kzalloc(sizeof(*req) + - count * (sizeof(req->rpm_msgs[0]) + sizeof(*compls)), - GFP_ATOMIC); - if (!ptr) - return -ENOMEM; - - req = ptr; - compls = ptr + sizeof(*req) + count * sizeof(*rpm_msgs); - - req->count = count; - rpm_msgs = req->rpm_msgs; - - for (i = 0; i < count; i++) { - __fill_rpmh_msg(rpm_msgs + i, state, cmd, n[i]); - cmd += n[i]; - } - - if (state != RPMH_ACTIVE_ONLY_STATE) { - cache_batch(ctrlr, req); - return 0; - } - - for (i = 0; i < count; i++) { - struct completion *compl = &compls[i]; - - init_completion(compl); - rpm_msgs[i].completion = compl; - ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msgs[i].msg); - if (ret) { - pr_err("Error(%d) sending RPMH message addr=%#x\n", - ret, rpm_msgs[i].msg.cmds[0].addr); - break; - } - } - - time_left = RPMH_TIMEOUT_MS; - while (i--) { - time_left = wait_for_completion_timeout(&compls[i], time_left); - if (!time_left) { - /* - * Better hope they never finish because they'll signal - * the completion that we're going to free once - * we've returned from this function. - */ - WARN_ON(1); - ret = -ETIMEDOUT; - goto exit; - } - } - -exit: - kfree(ptr); - - return ret; -} -EXPORT_SYMBOL_GPL(rpmh_write_batch); - -static int is_req_valid(struct cache_req *req) -{ - return (req->sleep_val != UINT_MAX && - req->wake_val != UINT_MAX && - req->sleep_val != req->wake_val); -} - -static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, - u32 addr, u32 data) -{ - DEFINE_RPMH_MSG_ONSTACK(NULL, state, NULL, rpm_msg); - - /* Wake sets are always complete and sleep sets are not */ - rpm_msg.msg.wait_for_compl = (state == RPMH_WAKE_ONLY_STATE); - rpm_msg.cmd[0].addr = addr; - rpm_msg.cmd[0].data = data; - rpm_msg.msg.num_cmds = 1; - - return rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr), &rpm_msg.msg); -} - -/** - * rpmh_flush() - Flushes the buffered sleep and wake sets to TCSes - * - * @ctrlr: Controller making request to flush cached data - * - * Return: - * * 0 - Success - * * Error code - Otherwise - */ -int rpmh_flush(struct rpmh_ctrlr *ctrlr) -{ - struct cache_req *p; - int ret = 0; - - lockdep_assert_irqs_disabled(); - - /* - * Currently rpmh_flush() is only called when we think we're running - * on the last processor. If the lock is busy it means another - * processor is up and it's better to abort than spin. - */ - if (!spin_trylock(&ctrlr->cache_lock)) - return -EBUSY; - - if (!ctrlr->dirty) { - pr_debug("Skipping flush, TCS has latest data.\n"); - goto write_next_wakeup; - } - - /* Invalidate the TCSes first to avoid stale data */ - rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); - - /* First flush the cached batch requests */ - ret = flush_batch(ctrlr); - if (ret) - goto exit; - - list_for_each_entry(p, &ctrlr->cache, list) { - if (!is_req_valid(p)) { - pr_debug("%s: skipping RPMH req: a:%#x s:%#x w:%#x", - __func__, p->addr, p->sleep_val, p->wake_val); - continue; - } - ret = send_single(ctrlr, RPMH_SLEEP_STATE, p->addr, - p->sleep_val); - if (ret) - goto exit; - ret = send_single(ctrlr, RPMH_WAKE_ONLY_STATE, p->addr, - p->wake_val); - if (ret) - goto exit; - } - - ctrlr->dirty = false; - -write_next_wakeup: - rpmh_rsc_write_next_wakeup(ctrlr_to_drv(ctrlr)); -exit: - spin_unlock(&ctrlr->cache_lock); - return ret; -} - -/** - * rpmh_invalidate: Invalidate sleep and wake sets in batch_cache - * - * @dev: The device making the request - * - * Invalidate the sleep and wake values in batch_cache. - */ -void rpmh_invalidate(const struct device *dev) -{ - struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); - struct batch_cache_req *req, *tmp; - unsigned long flags; - - spin_lock_irqsave(&ctrlr->cache_lock, flags); - list_for_each_entry_safe(req, tmp, &ctrlr->batch_cache, list) - kfree(req); - INIT_LIST_HEAD(&ctrlr->batch_cache); - ctrlr->dirty = true; - spin_unlock_irqrestore(&ctrlr->cache_lock, flags); -} -EXPORT_SYMBOL_GPL(rpmh_invalidate); diff --git a/include/soc/qcom/rpmh.h b/include/soc/qcom/rpmh.h index bdbee1a97d36..9a5c5d992e04 100644 --- a/include/soc/qcom/rpmh.h +++ b/include/soc/qcom/rpmh.h @@ -13,35 +13,16 @@ #if IS_ENABLED(CONFIG_QCOM_RPMH) int rpmh_write(const struct device *dev, enum rpmh_state state, const struct tcs_cmd *cmd, u32 n);
-int rpmh_write_async(const struct device *dev, enum rpmh_state state, - const struct tcs_cmd *cmd, u32 n); - -int rpmh_write_batch(const struct device *dev, enum rpmh_state state, - const struct tcs_cmd *cmd, u32 *n); - -void rpmh_invalidate(const struct device *dev); - #else
static inline int rpmh_write(const struct device *dev, enum rpmh_state state, const struct tcs_cmd *cmd, u32 n) { return -ENODEV; }
-static inline int rpmh_write_async(const struct device *dev, - enum rpmh_state state, - const struct tcs_cmd *cmd, u32 n) -{ return -ENODEV; } - -static inline int rpmh_write_batch(const struct device *dev, - enum rpmh_state state, - const struct tcs_cmd *cmd, u32 *n) -{ return -ENODEV; } - -static inline void rpmh_invalidate(const struct device *dev) -{ -} - #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__ */

Fix build errors, add some debug logging.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/qcom/rpmh.c | 53 +++++++++++++++++++------------------------------ include/soc/qcom/rpmh.h | 4 ++-- 2 files changed, 22 insertions(+), 35 deletions(-)
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 22605e0291a1..96f14a9afdf2 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -15,27 +15,30 @@ #include "rpmh-internal.h"
#define RPMH_TIMEOUT_MS msecs_to_jiffies(10000)
-#define DEFINE_RPMH_MSG_ONSTACK(device, s, q, name) \ +#define DEFINE_RPMH_MSG_ONSTACK(device, s, name) \ struct rpmh_request name = { \ .msg = { \ .state = s, \ .cmds = name.cmd, \ .num_cmds = 0, \ - .wait_for_compl = true, \ }, \ .cmd = { { 0 } }, \ - .completion = q, \ .dev = device, \ - .needs_free = false, \ + .needs_free = false, \ }
#define ctrlr_to_drv(ctrlr) container_of(ctrlr, struct rsc_drv, client)
-static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct device *dev) +static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct udevice *dev) { - struct rsc_drv *drv = dev_get_drvdata(dev->parent); + struct rsc_drv *drv = (struct rsc_drv *)dev_get_priv(dev->parent); + + if (!drv) { + log_err("BUG: no RPMh driver for %s (parent %s)\n", dev->name, dev->parent->name); + BUG(); + }
return &drv->client; }
@@ -49,36 +52,23 @@ static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct device *dev) * 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 device *dev, enum rpmh_state state, +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); - int ret = -EINVAL; - struct cache_req *req; - int i;
- /* Cache the request in our store and link the payload */ - for (i = 0; i < rpm_msg->msg.num_cmds; i++) { - req = cache_rpm_request(ctrlr, state, &rpm_msg->msg.cmds[i]); - if (IS_ERR(req)) - return PTR_ERR(req); + if (state != RPMH_ACTIVE_ONLY_STATE) { + log_err("only ACTIVE_ONLY state supported\n"); + return -EINVAL; }
- if (state == RPMH_ACTIVE_ONLY_STATE) { - ret = rpmh_rsc_send_data(ctrlr_to_drv(ctrlr), &rpm_msg->msg); - } else { - /* Clean up our call by spoofing tx_done */ - ret = 0; - rpmh_tx_done(&rpm_msg->msg); - } - - return ret; + 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) + const struct tcs_cmd *cmd, u32 n) { if (!cmd || !n || n > MAX_RPMH_PAYLOAD) return -EINVAL;
@@ -87,8 +77,10 @@ static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state, 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; }
/** @@ -100,24 +92,19 @@ static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state, * @n: The number of elements in @cmd * * May sleep. Do not call from atomic contexts. */ -int rpmh_write(const struct device *dev, enum rpmh_state state, +int rpmh_write(const struct udevice *dev, enum rpmh_state state, const struct tcs_cmd *cmd, u32 n) { - DECLARE_COMPLETION_ONSTACK(compl); - DEFINE_RPMH_MSG_ONSTACK(dev, state, &compl, rpm_msg); + 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); - if (ret) - return ret;
- ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS); - WARN_ON(!ret); - return (ret > 0) ? 0 : -ETIMEDOUT; + return ret; } EXPORT_SYMBOL_GPL(rpmh_write); diff --git a/include/soc/qcom/rpmh.h b/include/soc/qcom/rpmh.h index 9a5c5d992e04..3421fbf1ee3e 100644 --- a/include/soc/qcom/rpmh.h +++ b/include/soc/qcom/rpmh.h @@ -5,14 +5,14 @@
#ifndef __SOC_QCOM_RPMH_H__ #define __SOC_QCOM_RPMH_H__
+#include <dm/device-internal.h> #include <soc/qcom/tcs.h> -#include <linux/platform_device.h>
#if IS_ENABLED(CONFIG_QCOM_RPMH) -int rpmh_write(const struct device *dev, enum rpmh_state state, +int rpmh_write(const struct udevice *dev, enum rpmh_state state, const struct tcs_cmd *cmd, u32 n);
#else

Add Kconfig / Makefiles to build rpmh and cmd-db drivers.
Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/qcom/Kconfig | 27 +++++++++++++++++++++++++++ drivers/soc/qcom/Makefile | 4 ++++ 4 files changed, 33 insertions(+)
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index cee506fe4747..8ef408d9ba1b 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -47,8 +47,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 5ec89a053165..00e6a5ac8e2b 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -2,8 +2,9 @@ # # Makefile for the U-Boot SOC specific device drivers.
obj-$(CONFIG_SOC_AMD_VERSAL2) += soc_amd_versal2.o +obj-$(CONFIG_SOC_QCOM) += qcom/ obj-$(CONFIG_SOC_SAMSUNG) += samsung/ obj-$(CONFIG_SOC_TI) += ti/ obj-$(CONFIG_SOC_DEVICE) += soc-uclass.o obj-$(CONFIG_SOC_DEVICE_TI_K3) += soc_ti_k3.o diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig new file mode 100644 index 000000000000..4aa7833930c7 --- /dev/null +++ b/drivers/soc/qcom/Kconfig @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# QCOM Soc drivers +# +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..78fae8bbfa16 --- /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

Import the driver from Linux 6.10-rc6.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/power/regulator/qcom-rpmh-regulator.c | 1709 +++++++++++++++++++++++++ 1 file changed, 1709 insertions(+)
diff --git a/drivers/power/regulator/qcom-rpmh-regulator.c b/drivers/power/regulator/qcom-rpmh-regulator.c new file mode 100644 index 000000000000..80e304711345 --- /dev/null +++ b/drivers/power/regulator/qcom-rpmh-regulator.c @@ -0,0 +1,1709 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. +// Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/string.h> +#include <linux/regulator/driver.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/of_regulator.h> + +#include <soc/qcom/cmd-db.h> +#include <soc/qcom/rpmh.h> + +#include <dt-bindings/regulator/qcom,rpmh-regulator.h> + +/** + * enum rpmh_regulator_type - supported RPMh accelerator types + * @VRM: RPMh VRM accelerator which supports voting on enable, voltage, + * and mode of LDO, SMPS, and BOB type PMIC regulators. + * @XOB: RPMh XOB accelerator which supports voting on the enable state + * of PMIC regulators. + */ +enum rpmh_regulator_type { + VRM, + XOB, +}; + +#define RPMH_REGULATOR_REG_VRM_VOLTAGE 0x0 +#define RPMH_REGULATOR_REG_ENABLE 0x4 +#define RPMH_REGULATOR_REG_VRM_MODE 0x8 + +#define PMIC4_LDO_MODE_RETENTION 4 +#define PMIC4_LDO_MODE_LPM 5 +#define PMIC4_LDO_MODE_HPM 7 + +#define PMIC4_SMPS_MODE_RETENTION 4 +#define PMIC4_SMPS_MODE_PFM 5 +#define PMIC4_SMPS_MODE_AUTO 6 +#define PMIC4_SMPS_MODE_PWM 7 + +#define PMIC4_BOB_MODE_PASS 0 +#define PMIC4_BOB_MODE_PFM 1 +#define PMIC4_BOB_MODE_AUTO 2 +#define PMIC4_BOB_MODE_PWM 3 + +#define PMIC5_LDO_MODE_RETENTION 3 +#define PMIC5_LDO_MODE_LPM 4 +#define PMIC5_LDO_MODE_HPM 7 + +#define PMIC5_SMPS_MODE_RETENTION 3 +#define PMIC5_SMPS_MODE_PFM 4 +#define PMIC5_SMPS_MODE_AUTO 6 +#define PMIC5_SMPS_MODE_PWM 7 + +#define PMIC5_BOB_MODE_PASS 2 +#define PMIC5_BOB_MODE_PFM 4 +#define PMIC5_BOB_MODE_AUTO 6 +#define PMIC5_BOB_MODE_PWM 7 + +/** + * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations + * @regulator_type: RPMh accelerator type used to manage this + * regulator + * @ops: Pointer to regulator ops callback structure + * @voltage_ranges: The possible ranges of voltages supported by this + * PMIC regulator type + * @n_linear_ranges: Number of entries in voltage_ranges + * @n_voltages: The number of unique voltage set points defined + * by voltage_ranges + * @hpm_min_load_uA: Minimum load current in microamps that requires + * high power mode (HPM) operation. This is used + * for LDO hardware type regulators only. + * @pmic_mode_map: Array indexed by regulator framework mode + * containing PMIC hardware modes. Must be large + * enough to index all framework modes supported + * by this regulator hardware type. + * @of_map_mode: Maps an RPMH_REGULATOR_MODE_* mode value defined + * in device tree to a regulator framework mode + */ +struct rpmh_vreg_hw_data { + enum rpmh_regulator_type regulator_type; + const struct regulator_ops *ops; + const struct linear_range *voltage_ranges; + int n_linear_ranges; + int n_voltages; + int hpm_min_load_uA; + const int *pmic_mode_map; + unsigned int (*of_map_mode)(unsigned int mode); +}; + +/** + * struct rpmh_vreg - individual RPMh regulator data structure encapsulating a + * single regulator device + * @dev: Device pointer for the top-level PMIC RPMh + * regulator parent device. This is used as a + * handle in RPMh write requests. + * @addr: Base address of the regulator resource within + * an RPMh accelerator + * @rdesc: Regulator descriptor + * @hw_data: PMIC regulator configuration data for this RPMh + * regulator + * @always_wait_for_ack: Boolean flag indicating if a request must always + * wait for an ACK from RPMh before continuing even + * if it corresponds to a strictly lower power + * state (e.g. enabled --> disabled). + * @enabled: Flag indicating if the regulator is enabled or + * not + * @bypassed: Boolean indicating if the regulator is in + * bypass (pass-through) mode or not. This is + * only used by BOB rpmh-regulator resources. + * @voltage_selector: Selector used for get_voltage_sel() and + * set_voltage_sel() callbacks + * @mode: RPMh VRM regulator current framework mode + */ +struct rpmh_vreg { + struct device *dev; + u32 addr; + struct regulator_desc rdesc; + const struct rpmh_vreg_hw_data *hw_data; + bool always_wait_for_ack; + + int enabled; + bool bypassed; + int voltage_selector; + unsigned int mode; +}; + +/** + * struct rpmh_vreg_init_data - initialization data for an RPMh regulator + * @name: Name for the regulator which also corresponds + * to the device tree subnode name of the regulator + * @resource_name: RPMh regulator resource name format string. + * This must include exactly one field: '%s' which + * is filled at run-time with the PMIC ID provided + * by device tree property qcom,pmic-id. Example: + * "ldo%s1" for RPMh resource "ldoa1". + * @supply_name: Parent supply regulator name + * @hw_data: Configuration data for this PMIC regulator type + */ +struct rpmh_vreg_init_data { + const char *name; + const char *resource_name; + const char *supply_name; + const struct rpmh_vreg_hw_data *hw_data; +}; + +/** + * rpmh_regulator_send_request() - send the request to RPMh + * @vreg: Pointer to the RPMh regulator + * @cmd: Pointer to the RPMh command to send + * @wait_for_ack: Boolean indicating if execution must wait until the + * request has been acknowledged as complete + * + * Return: 0 on success, errno on failure + */ +static int rpmh_regulator_send_request(struct rpmh_vreg *vreg, + struct tcs_cmd *cmd, bool wait_for_ack) +{ + int ret; + + if (wait_for_ack || vreg->always_wait_for_ack) + ret = rpmh_write(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd, 1); + else + ret = rpmh_write_async(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd, + 1); + + return ret; +} + +static int _rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector, bool wait_for_ack) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + struct tcs_cmd cmd = { + .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE, + }; + int ret; + + /* VRM voltage control register is set with voltage in millivolts. */ + cmd.data = DIV_ROUND_UP(regulator_list_voltage_linear_range(rdev, + selector), 1000); + + ret = rpmh_regulator_send_request(vreg, &cmd, wait_for_ack); + if (!ret) + vreg->voltage_selector = selector; + + return ret; +} + +static int rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev, + unsigned int selector) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + + if (vreg->enabled == -EINVAL) { + /* + * Cache the voltage and send it later when the regulator is + * enabled or disabled. + */ + vreg->voltage_selector = selector; + return 0; + } + + return _rpmh_regulator_vrm_set_voltage_sel(rdev, selector, + selector > vreg->voltage_selector); +} + +static int rpmh_regulator_vrm_get_voltage_sel(struct regulator_dev *rdev) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->voltage_selector; +} + +static int rpmh_regulator_is_enabled(struct regulator_dev *rdev) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->enabled; +} + +static int rpmh_regulator_set_enable_state(struct regulator_dev *rdev, + bool enable) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + struct tcs_cmd cmd = { + .addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE, + .data = enable, + }; + int ret; + + if (vreg->enabled == -EINVAL && + vreg->voltage_selector != -ENOTRECOVERABLE) { + ret = _rpmh_regulator_vrm_set_voltage_sel(rdev, + vreg->voltage_selector, true); + if (ret < 0) + return ret; + } + + ret = rpmh_regulator_send_request(vreg, &cmd, enable); + if (!ret) + vreg->enabled = enable; + + return ret; +} + +static int rpmh_regulator_enable(struct regulator_dev *rdev) +{ + return rpmh_regulator_set_enable_state(rdev, true); +} + +static int rpmh_regulator_disable(struct regulator_dev *rdev) +{ + return rpmh_regulator_set_enable_state(rdev, false); +} + +static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg, + unsigned int mode, bool bypassed) +{ + struct tcs_cmd cmd = { + .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE, + }; + int pmic_mode; + + if (mode > REGULATOR_MODE_STANDBY) + return -EINVAL; + + pmic_mode = vreg->hw_data->pmic_mode_map[mode]; + if (pmic_mode < 0) + return pmic_mode; + + if (bypassed) + cmd.data = PMIC4_BOB_MODE_PASS; + else + cmd.data = pmic_mode; + + return rpmh_regulator_send_request(vreg, &cmd, true); +} + +static int rpmh_regulator_vrm_set_mode(struct regulator_dev *rdev, + unsigned int mode) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + int ret; + + if (mode == vreg->mode) + return 0; + + ret = rpmh_regulator_vrm_set_mode_bypass(vreg, mode, vreg->bypassed); + if (!ret) + vreg->mode = mode; + + return ret; +} + +static unsigned int rpmh_regulator_vrm_get_mode(struct regulator_dev *rdev) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + + return vreg->mode; +} + +/** + * rpmh_regulator_vrm_get_optimum_mode() - get the mode based on the load + * @rdev: Regulator device pointer for the rpmh-regulator + * @input_uV: Input voltage + * @output_uV: Output voltage + * @load_uA: Aggregated load current in microamps + * + * This function is used in the regulator_ops for VRM type RPMh regulator + * devices. + * + * Return: 0 on success, errno on failure + */ +static unsigned int rpmh_regulator_vrm_get_optimum_mode( + struct regulator_dev *rdev, int input_uV, int output_uV, int load_uA) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + + if (load_uA >= vreg->hw_data->hpm_min_load_uA) + return REGULATOR_MODE_NORMAL; + else + return REGULATOR_MODE_IDLE; +} + +static int rpmh_regulator_vrm_set_bypass(struct regulator_dev *rdev, + bool enable) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + int ret; + + if (vreg->bypassed == enable) + return 0; + + ret = rpmh_regulator_vrm_set_mode_bypass(vreg, vreg->mode, enable); + if (!ret) + vreg->bypassed = enable; + + return ret; +} + +static int rpmh_regulator_vrm_get_bypass(struct regulator_dev *rdev, + bool *enable) +{ + struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + + *enable = vreg->bypassed; + + return 0; +} + +static const struct regulator_ops rpmh_regulator_vrm_ops = { + .enable = rpmh_regulator_enable, + .disable = rpmh_regulator_disable, + .is_enabled = rpmh_regulator_is_enabled, + .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel, + .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel, + .list_voltage = regulator_list_voltage_linear_range, + .set_mode = rpmh_regulator_vrm_set_mode, + .get_mode = rpmh_regulator_vrm_get_mode, +}; + +static const struct regulator_ops rpmh_regulator_vrm_drms_ops = { + .enable = rpmh_regulator_enable, + .disable = rpmh_regulator_disable, + .is_enabled = rpmh_regulator_is_enabled, + .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel, + .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel, + .list_voltage = regulator_list_voltage_linear_range, + .set_mode = rpmh_regulator_vrm_set_mode, + .get_mode = rpmh_regulator_vrm_get_mode, + .get_optimum_mode = rpmh_regulator_vrm_get_optimum_mode, +}; + +static const struct regulator_ops rpmh_regulator_vrm_bypass_ops = { + .enable = rpmh_regulator_enable, + .disable = rpmh_regulator_disable, + .is_enabled = rpmh_regulator_is_enabled, + .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel, + .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel, + .list_voltage = regulator_list_voltage_linear_range, + .set_mode = rpmh_regulator_vrm_set_mode, + .get_mode = rpmh_regulator_vrm_get_mode, + .set_bypass = rpmh_regulator_vrm_set_bypass, + .get_bypass = rpmh_regulator_vrm_get_bypass, +}; + +static const struct regulator_ops rpmh_regulator_xob_ops = { + .enable = rpmh_regulator_enable, + .disable = rpmh_regulator_disable, + .is_enabled = rpmh_regulator_is_enabled, +}; + +/** + * rpmh_regulator_init_vreg() - initialize all attributes of an rpmh-regulator + * @vreg: Pointer to the individual rpmh-regulator resource + * @dev: Pointer to the top level rpmh-regulator PMIC device + * @node: Pointer to the individual rpmh-regulator resource + * device node + * @pmic_id: String used to identify the top level rpmh-regulator + * PMIC device on the board + * @pmic_rpmh_data: Pointer to a null-terminated array of rpmh-regulator + * resources defined for the top level PMIC device + * + * Return: 0 on success, errno on failure + */ +static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev, + struct device_node *node, const char *pmic_id, + const struct rpmh_vreg_init_data *pmic_rpmh_data) +{ + struct regulator_config reg_config = {}; + char rpmh_resource_name[20] = ""; + const struct rpmh_vreg_init_data *rpmh_data; + struct regulator_init_data *init_data; + struct regulator_dev *rdev; + int ret; + + vreg->dev = dev; + + for (rpmh_data = pmic_rpmh_data; rpmh_data->name; rpmh_data++) + if (of_node_name_eq(node, rpmh_data->name)) + break; + + if (!rpmh_data->name) { + dev_err(dev, "Unknown regulator %pOFn\n", node); + return -EINVAL; + } + + scnprintf(rpmh_resource_name, sizeof(rpmh_resource_name), + rpmh_data->resource_name, pmic_id); + + vreg->addr = cmd_db_read_addr(rpmh_resource_name); + if (!vreg->addr) { + dev_err(dev, "%pOFn: could not find RPMh address for resource %s\n", + node, rpmh_resource_name); + return -ENODEV; + } + + vreg->rdesc.name = rpmh_data->name; + vreg->rdesc.supply_name = rpmh_data->supply_name; + vreg->hw_data = rpmh_data->hw_data; + + vreg->enabled = -EINVAL; + vreg->voltage_selector = -ENOTRECOVERABLE; + vreg->mode = REGULATOR_MODE_INVALID; + + if (rpmh_data->hw_data->n_voltages) { + vreg->rdesc.linear_ranges = rpmh_data->hw_data->voltage_ranges; + vreg->rdesc.n_linear_ranges = rpmh_data->hw_data->n_linear_ranges; + vreg->rdesc.n_voltages = rpmh_data->hw_data->n_voltages; + } + + vreg->always_wait_for_ack = of_property_read_bool(node, + "qcom,always-wait-for-ack"); + + vreg->rdesc.owner = THIS_MODULE; + vreg->rdesc.type = REGULATOR_VOLTAGE; + vreg->rdesc.ops = vreg->hw_data->ops; + vreg->rdesc.of_map_mode = vreg->hw_data->of_map_mode; + + init_data = of_get_regulator_init_data(dev, node, &vreg->rdesc); + if (!init_data) + return -ENOMEM; + + if (rpmh_data->hw_data->regulator_type == XOB && + init_data->constraints.min_uV && + init_data->constraints.min_uV == init_data->constraints.max_uV) { + vreg->rdesc.fixed_uV = init_data->constraints.min_uV; + vreg->rdesc.n_voltages = 1; + } + + reg_config.dev = dev; + reg_config.init_data = init_data; + reg_config.of_node = node; + reg_config.driver_data = vreg; + + rdev = devm_regulator_register(dev, &vreg->rdesc, ®_config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "%pOFn: devm_regulator_register() failed, ret=%d\n", + node, ret); + return ret; + } + + dev_dbg(dev, "%pOFn regulator registered for RPMh resource %s @ 0x%05X\n", + node, rpmh_resource_name, vreg->addr); + + return 0; +} + +static const int pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = PMIC4_LDO_MODE_RETENTION, + [REGULATOR_MODE_IDLE] = PMIC4_LDO_MODE_LPM, + [REGULATOR_MODE_NORMAL] = PMIC4_LDO_MODE_HPM, + [REGULATOR_MODE_FAST] = -EINVAL, +}; + +static const int pmic_mode_map_pmic5_ldo[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = PMIC5_LDO_MODE_RETENTION, + [REGULATOR_MODE_IDLE] = PMIC5_LDO_MODE_LPM, + [REGULATOR_MODE_NORMAL] = PMIC5_LDO_MODE_HPM, + [REGULATOR_MODE_FAST] = -EINVAL, +}; + +static const int pmic_mode_map_pmic5_ldo_hpm[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = -EINVAL, + [REGULATOR_MODE_IDLE] = -EINVAL, + [REGULATOR_MODE_NORMAL] = PMIC5_LDO_MODE_HPM, + [REGULATOR_MODE_FAST] = -EINVAL, +}; + +static unsigned int rpmh_regulator_pmic4_ldo_of_map_mode(unsigned int rpmh_mode) +{ + unsigned int mode; + + switch (rpmh_mode) { + case RPMH_REGULATOR_MODE_HPM: + mode = REGULATOR_MODE_NORMAL; + break; + case RPMH_REGULATOR_MODE_LPM: + mode = REGULATOR_MODE_IDLE; + break; + case RPMH_REGULATOR_MODE_RET: + mode = REGULATOR_MODE_STANDBY; + break; + default: + mode = REGULATOR_MODE_INVALID; + break; + } + + return mode; +} + +static const int pmic_mode_map_pmic4_smps[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = PMIC4_SMPS_MODE_RETENTION, + [REGULATOR_MODE_IDLE] = PMIC4_SMPS_MODE_PFM, + [REGULATOR_MODE_NORMAL] = PMIC4_SMPS_MODE_AUTO, + [REGULATOR_MODE_FAST] = PMIC4_SMPS_MODE_PWM, +}; + +static const int pmic_mode_map_pmic5_smps[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = PMIC5_SMPS_MODE_RETENTION, + [REGULATOR_MODE_IDLE] = PMIC5_SMPS_MODE_PFM, + [REGULATOR_MODE_NORMAL] = PMIC5_SMPS_MODE_AUTO, + [REGULATOR_MODE_FAST] = PMIC5_SMPS_MODE_PWM, +}; + +static unsigned int +rpmh_regulator_pmic4_smps_of_map_mode(unsigned int rpmh_mode) +{ + unsigned int mode; + + switch (rpmh_mode) { + case RPMH_REGULATOR_MODE_HPM: + mode = REGULATOR_MODE_FAST; + break; + case RPMH_REGULATOR_MODE_AUTO: + mode = REGULATOR_MODE_NORMAL; + break; + case RPMH_REGULATOR_MODE_LPM: + mode = REGULATOR_MODE_IDLE; + break; + case RPMH_REGULATOR_MODE_RET: + mode = REGULATOR_MODE_STANDBY; + break; + default: + mode = REGULATOR_MODE_INVALID; + break; + } + + return mode; +} + +static const int pmic_mode_map_pmic4_bob[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = -EINVAL, + [REGULATOR_MODE_IDLE] = PMIC4_BOB_MODE_PFM, + [REGULATOR_MODE_NORMAL] = PMIC4_BOB_MODE_AUTO, + [REGULATOR_MODE_FAST] = PMIC4_BOB_MODE_PWM, +}; + +static const int pmic_mode_map_pmic5_bob[REGULATOR_MODE_STANDBY + 1] = { + [REGULATOR_MODE_INVALID] = -EINVAL, + [REGULATOR_MODE_STANDBY] = -EINVAL, + [REGULATOR_MODE_IDLE] = PMIC5_BOB_MODE_PFM, + [REGULATOR_MODE_NORMAL] = PMIC5_BOB_MODE_AUTO, + [REGULATOR_MODE_FAST] = PMIC5_BOB_MODE_PWM, +}; + +static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int rpmh_mode) +{ + unsigned int mode; + + switch (rpmh_mode) { + case RPMH_REGULATOR_MODE_HPM: + mode = REGULATOR_MODE_FAST; + break; + case RPMH_REGULATOR_MODE_AUTO: + mode = REGULATOR_MODE_NORMAL; + break; + case RPMH_REGULATOR_MODE_LPM: + mode = REGULATOR_MODE_IDLE; + break; + default: + mode = REGULATOR_MODE_INVALID; + break; + } + + return mode; +} + +static const struct rpmh_vreg_hw_data pmic4_pldo = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1664000, 0, 255, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 256, + .hpm_min_load_uA = 10000, + .pmic_mode_map = pmic_mode_map_pmic4_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic4_pldo_lv = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1256000, 0, 127, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 128, + .hpm_min_load_uA = 10000, + .pmic_mode_map = pmic_mode_map_pmic4_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic4_nldo = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 128, + .hpm_min_load_uA = 30000, + .pmic_mode_map = pmic_mode_map_pmic4_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic4_hfsmps3 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 216, + .pmic_mode_map = pmic_mode_map_pmic4_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic4_ftsmps426 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(320000, 0, 258, 4000), + }, + .n_linear_ranges = 1, + .n_voltages = 259, + .pmic_mode_map = pmic_mode_map_pmic4_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic4_bob = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_bypass_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000), + }, + .n_linear_ranges = 1, + .n_voltages = 84, + .pmic_mode_map = pmic_mode_map_pmic4_bob, + .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic4_lvs = { + .regulator_type = XOB, + .ops = &rpmh_regulator_xob_ops, + /* LVS hardware does not support voltage or mode configuration. */ +}; + +static const struct rpmh_vreg_hw_data pmic5_pldo = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1504000, 0, 255, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 256, + .hpm_min_load_uA = 10000, + .pmic_mode_map = pmic_mode_map_pmic5_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_pldo_lv = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1504000, 0, 62, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 63, + .hpm_min_load_uA = 10000, + .pmic_mode_map = pmic_mode_map_pmic5_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_pldo515_mv = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1800000, 0, 187, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 188, + .hpm_min_load_uA = 10000, + .pmic_mode_map = pmic_mode_map_pmic5_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_pldo502 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1504000, 0, 255, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 256, + .pmic_mode_map = pmic_mode_map_pmic5_ldo_hpm, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_pldo502ln = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(1800000, 0, 2, 200000), + REGULATOR_LINEAR_RANGE(2608000, 3, 28, 16000), + REGULATOR_LINEAR_RANGE(3104000, 29, 30, 96000), + REGULATOR_LINEAR_RANGE(3312000, 31, 31, 0), + }, + .n_linear_ranges = 4, + .n_voltages = 32, + .pmic_mode_map = pmic_mode_map_pmic5_ldo_hpm, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_nldo = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(320000, 0, 123, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 124, + .hpm_min_load_uA = 30000, + .pmic_mode_map = pmic_mode_map_pmic5_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_nldo515 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(320000, 0, 210, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 211, + .hpm_min_load_uA = 30000, + .pmic_mode_map = pmic_mode_map_pmic5_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_nldo502 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_drms_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(528000, 0, 127, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 128, + .hpm_min_load_uA = 30000, + .pmic_mode_map = pmic_mode_map_pmic5_ldo, + .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_hfsmps510 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 216, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_ftsmps510 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(300000, 0, 263, 4000), + }, + .n_linear_ranges = 1, + .n_voltages = 264, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_ftsmps520 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(300000, 0, 263, 4000), + }, + .n_linear_ranges = 1, + .n_voltages = 264, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_ftsmps525_lv = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(300000, 0, 267, 4000), + }, + .n_linear_ranges = 1, + .n_voltages = 268, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_ftsmps525_mv = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(600000, 0, 267, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 268, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_ftsmps527 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), + }, + .n_linear_ranges = 1, + .n_voltages = 215, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_hfsmps515 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(320000, 0, 235, 16000), + }, + .n_linear_ranges = 1, + .n_voltages = 236, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_hfsmps515_1 = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(900000, 0, 4, 16000), + }, + .n_linear_ranges = 1, + .n_voltages = 5, + .pmic_mode_map = pmic_mode_map_pmic5_smps, + .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, +}; + +static const struct rpmh_vreg_hw_data pmic5_bob = { + .regulator_type = VRM, + .ops = &rpmh_regulator_vrm_bypass_ops, + .voltage_ranges = (struct linear_range[]) { + REGULATOR_LINEAR_RANGE(3000000, 0, 31, 32000), + }, + .n_linear_ranges = 1, + .n_voltages = 32, + .pmic_mode_map = pmic_mode_map_pmic5_bob, + .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode, +}; + +#define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \ +{ \ + .name = _name, \ + .resource_name = _resource_name, \ + .hw_data = _hw_data, \ + .supply_name = _supply_name, \ +} + +static const struct rpmh_vreg_init_data pm8998_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic4_hfsmps3, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic4_hfsmps3, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic4_hfsmps3, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic4_ftsmps426, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic4_ftsmps426, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic4_ftsmps426, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic4_ftsmps426, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic4_ftsmps426, "vdd-s10"), + RPMH_VREG("smps11", "smp%s11", &pmic4_ftsmps426, "vdd-s11"), + RPMH_VREG("smps12", "smp%s12", &pmic4_ftsmps426, "vdd-s12"), + RPMH_VREG("smps13", "smp%s13", &pmic4_ftsmps426, "vdd-s13"), + RPMH_VREG("ldo1", "ldo%s1", &pmic4_nldo, "vdd-l1-l27"), + RPMH_VREG("ldo2", "ldo%s2", &pmic4_nldo, "vdd-l2-l8-l17"), + RPMH_VREG("ldo3", "ldo%s3", &pmic4_nldo, "vdd-l3-l11"), + RPMH_VREG("ldo4", "ldo%s4", &pmic4_nldo, "vdd-l4-l5"), + RPMH_VREG("ldo5", "ldo%s5", &pmic4_nldo, "vdd-l4-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic4_pldo, "vdd-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo8", "ldo%s8", &pmic4_nldo, "vdd-l2-l8-l17"), + RPMH_VREG("ldo9", "ldo%s9", &pmic4_pldo, "vdd-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic4_pldo, "vdd-l10-l23-l25"), + RPMH_VREG("ldo11", "ldo%s11", &pmic4_nldo, "vdd-l3-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo13", "ldo%s13", &pmic4_pldo, "vdd-l13-l19-l21"), + RPMH_VREG("ldo14", "ldo%s14", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo15", "ldo%s15", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic4_pldo, "vdd-l16-l28"), + RPMH_VREG("ldo17", "ldo%s17", &pmic4_nldo, "vdd-l2-l8-l17"), + RPMH_VREG("ldo18", "ldo%s18", &pmic4_pldo, "vdd-l18-l22"), + RPMH_VREG("ldo19", "ldo%s19", &pmic4_pldo, "vdd-l13-l19-l21"), + RPMH_VREG("ldo20", "ldo%s20", &pmic4_pldo, "vdd-l20-l24"), + RPMH_VREG("ldo21", "ldo%s21", &pmic4_pldo, "vdd-l13-l19-l21"), + RPMH_VREG("ldo22", "ldo%s22", &pmic4_pldo, "vdd-l18-l22"), + RPMH_VREG("ldo23", "ldo%s23", &pmic4_pldo, "vdd-l10-l23-l25"), + RPMH_VREG("ldo24", "ldo%s24", &pmic4_pldo, "vdd-l20-l24"), + RPMH_VREG("ldo25", "ldo%s25", &pmic4_pldo, "vdd-l10-l23-l25"), + RPMH_VREG("ldo26", "ldo%s26", &pmic4_nldo, "vdd-l26"), + RPMH_VREG("ldo27", "ldo%s27", &pmic4_nldo, "vdd-l1-l27"), + RPMH_VREG("ldo28", "ldo%s28", &pmic4_pldo, "vdd-l16-l28"), + RPMH_VREG("lvs1", "vs%s1", &pmic4_lvs, "vin-lvs-1-2"), + RPMH_VREG("lvs2", "vs%s2", &pmic4_lvs, "vin-lvs-1-2"), + {} +}; + +static const struct rpmh_vreg_init_data pmg1110_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + {} +}; + +static const struct rpmh_vreg_init_data pmi8998_vreg_data[] = { + RPMH_VREG("bob", "bob%s1", &pmic4_bob, "vdd-bob"), + {} +}; + +static const struct rpmh_vreg_init_data pm8005_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic4_ftsmps426, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic4_ftsmps426, "vdd-s4"), + {} +}; + +static const struct rpmh_vreg_init_data pm8150_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l8-l11"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l10"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l4-l5-l18"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l3-l4-l5-l18"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l4-l5-l18"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l1-l8-l11"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l2-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo, "vdd-l1-l8-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l13-l16-l17"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l13-l16-l17"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l13-l16-l17"), + RPMH_VREG("ldo18", "ldo%s18", &pmic5_nldo, "vdd-l3-l4-l5-l18"), + {} +}; + +static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_hfsmps510, "vdd-s8"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l8"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2-l3"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l2-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l4-l5-l6"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l4-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l4-l5-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l7-l11"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo_lv, "vdd-l1-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l9-l10"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l9-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l7-l11"), + RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), + {} +}; + +static const struct rpmh_vreg_init_data pmm8155au_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l8-l11"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l10"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l4-l5-l18"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l3-l4-l5-l18"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l4-l5-l18"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l1-l8-l11"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l2-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo, "vdd-l1-l8-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l13-l16-l17"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l13-l16-l17"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l13-l16-l17"), + RPMH_VREG("ldo18", "ldo%s18", &pmic5_nldo, "vdd-l3-l4-l5-l18"), + {} +}; + +static const struct rpmh_vreg_init_data pmm8654au_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps527, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps527, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps527, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps527, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps527, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps527, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps527, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps527, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps527, "vdd-s9"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-s9"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2-l3"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l2-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-s9"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo515, "vdd-s9"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo515, "vdd-l6-l7"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo515, "vdd-l6-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo515_mv, "vdd-l8-l9"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l8-l9"), + {} +}; + +static const struct rpmh_vreg_init_data pm8350_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_hfsmps510, "vdd-s10"), + RPMH_VREG("smps11", "smp%s11", &pmic5_hfsmps510, "vdd-s11"), + RPMH_VREG("smps12", "smp%s12", &pmic5_hfsmps510, "vdd-s12"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l4"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l7"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l5"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l1-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l2-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l6-l9-l10"), + {} +}; + +static const struct rpmh_vreg_init_data pm8350c_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps515, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l12"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo_lv, "vdd-l2-l8"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo_lv, "vdd-l2-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l6-l9-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l1-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), + RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), + {} +}; + +static const struct rpmh_vreg_init_data pm8450_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps520, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps520, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps520, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps520, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps520, "vdd-s6"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo_lv, "vdd-l4"), + {} +}; + +static const struct rpmh_vreg_init_data pm8550_vreg_data[] = { + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1-l4-l10"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l13-l14"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-l1-l4-l10"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l16"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l6-l7"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l6-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo, "vdd-l8-l9"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l8-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo515, "vdd-l1-l4-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo515, "vdd-l11"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo515, "vdd-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l2-l13-l14"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo, "vdd-l2-l13-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo515, "vdd-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l5-l16"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l17"), + RPMH_VREG("bob1", "bob%s1", &pmic5_bob, "vdd-bob1"), + RPMH_VREG("bob2", "bob%s2", &pmic5_bob, "vdd-bob2"), + {} +}; + +static const struct rpmh_vreg_init_data pm8550vs_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_lv, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_mv, "vdd-s6"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), + {} +}; + +static const struct rpmh_vreg_init_data pm8550ve_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_mv, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_lv, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525_lv, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525_lv, "vdd-s8"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), + {} +}; + +static const struct rpmh_vreg_init_data pmc8380_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_mv, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_lv, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525_lv, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525_lv, "vdd-s8"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), + {} +}; + +static const struct rpmh_vreg_init_data pm8009_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515, "vdd-s2"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo_lv, "vdd-l7"), + {} +}; + +static const struct rpmh_vreg_init_data pm8009_1_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515_1, "vdd-s2"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo_lv, "vdd-l7"), + {} +}; + +static const struct rpmh_vreg_init_data pm8010_vreg_data[] = { + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo502, "vdd-l1-l2"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo502, "vdd-l1-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_pldo502ln, "vdd-l3-l4"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo502ln, "vdd-l3-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo502, "vdd-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo502ln, "vdd-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo502, "vdd-l7"), +}; + +static const struct rpmh_vreg_init_data pm6150_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2-l3"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l2-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4-l7-l8"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l16-l17-l18-l19"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l4-l7-l8"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l4-l7-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo_lv, "vdd-l10-l14-l15"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo_lv, "vdd-l11-l12-l13"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l11-l12-l13"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo_lv, "vdd-l11-l12-l13"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo_lv, "vdd-l10-l14-l15"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_pldo_lv, "vdd-l10-l14-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l5-l16-l17-l18-l19"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l5-l16-l17-l18-l19"), + RPMH_VREG("ldo18", "ldo%s18", &pmic5_pldo, "vdd-l5-l16-l17-l18-l19"), + RPMH_VREG("ldo19", "ldo%s19", &pmic5_pldo, "vdd-l5-l16-l17-l18-l19"), + {} +}; + +static const struct rpmh_vreg_init_data pm6150l_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_hfsmps510, "vdd-s8"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l8"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2-l3"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l2-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l4-l5-l6"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l4-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l4-l5-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l7-l11"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo, "vdd-l1-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l9-l10"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l9-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l7-l11"), + RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), + {} +}; + +static const struct rpmh_vreg_init_data pm6350_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, NULL), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, NULL), + /* smps3 - smps5 not configured */ + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, NULL), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, NULL), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_pldo, NULL), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, NULL), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, NULL), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, NULL), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, NULL), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo, NULL), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, NULL), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, NULL), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, NULL), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo, NULL), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_nldo, NULL), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo, NULL), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, NULL), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_nldo, NULL), + /* ldo17 not configured */ + RPMH_VREG("ldo18", "ldo%s18", &pmic5_nldo, NULL), + RPMH_VREG("ldo19", "ldo%s19", &pmic5_nldo, NULL), + RPMH_VREG("ldo20", "ldo%s20", &pmic5_nldo, NULL), + RPMH_VREG("ldo21", "ldo%s21", &pmic5_nldo, NULL), + RPMH_VREG("ldo22", "ldo%s22", &pmic5_nldo, NULL), +}; + +static const struct rpmh_vreg_init_data pmx55_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_hfsmps510, "vdd-s7"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l9"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4-l12"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l7-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l3-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l4-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l10-l11-l13"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l16"), + {} +}; + +static const struct rpmh_vreg_init_data pmx65_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_hfsmps510, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_hfsmps510, "vdd-s8"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2-l18"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6-l16"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6-l16"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8-l9"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l8-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l11-l13"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l11-l13"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l5-l6-l16"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_nldo, "vdd-l17"), + /* ldo18 not configured */ + RPMH_VREG("ldo19", "ldo%s19", &pmic5_nldo, "vdd-l19"), + RPMH_VREG("ldo20", "ldo%s20", &pmic5_nldo, "vdd-l20"), + RPMH_VREG("ldo21", "ldo%s21", &pmic5_nldo, "vdd-l21"), + {} +}; + +static const struct rpmh_vreg_init_data pmx75_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_mv, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_lv, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525_lv, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525_lv, "vdd-s8"), + RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps525_lv, "vdd-s9"), + RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps525_lv, "vdd-s10"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2-18"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-l4-l16"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo_lv, "vdd-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo_lv, "vdd-l5-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo515, "vdd-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo515, "vdd-l8-l9"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo515, "vdd-l8-l9"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l11-l13"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo515, "vdd-l12"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l11-l13"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo515, "vdd-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo515, "vdd-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_nldo515, "vdd-l4-l16"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_nldo515, "vdd-l17"), + /* ldo18 not configured */ + RPMH_VREG("ldo19", "ldo%s19", &pmic5_nldo515, "vdd-l19"), + RPMH_VREG("ldo20", "ldo%s20", &pmic5_nldo515, "vdd-l20-l21"), + RPMH_VREG("ldo21", "ldo%s21", &pmic5_nldo515, "vdd-l20-l21"), +}; + +static const struct rpmh_vreg_init_data pm7325_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps520, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps520, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps520, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps520, "vdd-s6"), + RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps520, "vdd-s7"), + RPMH_VREG("smps8", "smp%s8", &pmic5_hfsmps510, "vdd-s8"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l4-l12-l15"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l7"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l1-l4-l12-l15"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l2-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8"), + RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l6-l9-l10"), + RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo_lv, "vdd-l11-l17-l18-l19"), + RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l1-l4-l12-l15"), + RPMH_VREG("ldo13", "ldo%s13", &pmic5_nldo, "vdd-l13"), + RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14-l16"), + RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l1-l4-l12-l15"), + RPMH_VREG("ldo16", "ldo%s16", &pmic5_nldo, "vdd-l14-l16"), + RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo_lv, "vdd-l11-l17-l18-l19"), + RPMH_VREG("ldo18", "ldo%s18", &pmic5_pldo_lv, "vdd-l11-l17-l18-l19"), + RPMH_VREG("ldo19", "ldo%s19", &pmic5_pldo_lv, "vdd-l11-l17-l18-l19"), + {} +}; + +static const struct rpmh_vreg_init_data pmr735a_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps520, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps515, "vdd-s3"), + RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), + RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo_lv, "vdd-l4"), + RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l5-l6"), + RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l5-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l7-bob"), + {} +}; + +static const struct rpmh_vreg_init_data pm660_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic4_ftsmps426, "vdd-s3"), + RPMH_VREG("smps4", "smp%s4", &pmic4_hfsmps3, "vdd-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic4_hfsmps3, "vdd-s5"), + RPMH_VREG("smps6", "smp%s6", &pmic4_hfsmps3, "vdd-s6"), + RPMH_VREG("ldo1", "ldo%s1", &pmic4_nldo, "vdd-l1-l6-l7"), + RPMH_VREG("ldo2", "ldo%s2", &pmic4_nldo, "vdd-l2-l3"), + RPMH_VREG("ldo3", "ldo%s3", &pmic4_nldo, "vdd-l2-l3"), + /* ldo4 is inaccessible on PM660 */ + RPMH_VREG("ldo5", "ldo%s5", &pmic4_nldo, "vdd-l5"), + RPMH_VREG("ldo6", "ldo%s6", &pmic4_nldo, "vdd-l1-l6-l7"), + RPMH_VREG("ldo7", "ldo%s7", &pmic4_nldo, "vdd-l1-l6-l7"), + RPMH_VREG("ldo8", "ldo%s8", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), + RPMH_VREG("ldo9", "ldo%s9", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), + RPMH_VREG("ldo10", "ldo%s10", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), + RPMH_VREG("ldo11", "ldo%s11", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), + RPMH_VREG("ldo12", "ldo%s12", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), + RPMH_VREG("ldo13", "ldo%s13", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), + RPMH_VREG("ldo14", "ldo%s14", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), + RPMH_VREG("ldo15", "ldo%s15", &pmic4_pldo, "vdd-l15-l16-l17-l18-l19"), + RPMH_VREG("ldo16", "ldo%s16", &pmic4_pldo, "vdd-l15-l16-l17-l18-l19"), + RPMH_VREG("ldo17", "ldo%s17", &pmic4_pldo, "vdd-l15-l16-l17-l18-l19"), + RPMH_VREG("ldo18", "ldo%s18", &pmic4_pldo, "vdd-l15-l16-l17-l18-l19"), + RPMH_VREG("ldo19", "ldo%s19", &pmic4_pldo, "vdd-l15-l16-l17-l18-l19"), + {} +}; + +static const struct rpmh_vreg_init_data pm660l_vreg_data[] = { + RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), + RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), + RPMH_VREG("smps3", "smp%s3", &pmic4_ftsmps426, "vdd-s3-s4"), + RPMH_VREG("smps5", "smp%s5", &pmic4_ftsmps426, "vdd-s5"), + RPMH_VREG("ldo1", "ldo%s1", &pmic4_nldo, "vdd-l1-l9-l10"), + RPMH_VREG("ldo2", "ldo%s2", &pmic4_pldo, "vdd-l2"), + RPMH_VREG("ldo3", "ldo%s3", &pmic4_pldo, "vdd-l3-l5-l7-l8"), + RPMH_VREG("ldo4", "ldo%s4", &pmic4_pldo, "vdd-l4-l6"), + RPMH_VREG("ldo5", "ldo%s5", &pmic4_pldo, "vdd-l3-l5-l7-l8"), + RPMH_VREG("ldo6", "ldo%s6", &pmic4_pldo, "vdd-l4-l6"), + RPMH_VREG("ldo7", "ldo%s7", &pmic4_pldo, "vdd-l3-l5-l7-l8"), + RPMH_VREG("ldo8", "ldo%s8", &pmic4_pldo, "vdd-l3-l5-l7-l8"), + RPMH_VREG("bob", "bob%s1", &pmic4_bob, "vdd-bob"), + {} +}; + +static int rpmh_regulator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct rpmh_vreg_init_data *vreg_data; + struct device_node *node; + struct rpmh_vreg *vreg; + const char *pmic_id; + int ret; + + vreg_data = of_device_get_match_data(dev); + if (!vreg_data) + return -ENODEV; + + ret = of_property_read_string(dev->of_node, "qcom,pmic-id", &pmic_id); + if (ret < 0) { + dev_err(dev, "qcom,pmic-id missing in DT node\n"); + return ret; + } + + for_each_available_child_of_node(dev->of_node, node) { + vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); + if (!vreg) { + of_node_put(node); + return -ENOMEM; + } + + ret = rpmh_regulator_init_vreg(vreg, dev, node, pmic_id, + vreg_data); + if (ret < 0) { + of_node_put(node); + return ret; + } + } + + return 0; +} + +static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { + { + .compatible = "qcom,pm8005-rpmh-regulators", + .data = pm8005_vreg_data, + }, + { + .compatible = "qcom,pm8009-rpmh-regulators", + .data = pm8009_vreg_data, + }, + { + .compatible = "qcom,pm8009-1-rpmh-regulators", + .data = pm8009_1_vreg_data, + }, + { + .compatible = "qcom,pm8010-rpmh-regulators", + .data = pm8010_vreg_data, + }, + { + .compatible = "qcom,pm8150-rpmh-regulators", + .data = pm8150_vreg_data, + }, + { + .compatible = "qcom,pm8150l-rpmh-regulators", + .data = pm8150l_vreg_data, + }, + { + .compatible = "qcom,pm8350-rpmh-regulators", + .data = pm8350_vreg_data, + }, + { + .compatible = "qcom,pm8350c-rpmh-regulators", + .data = pm8350c_vreg_data, + }, + { + .compatible = "qcom,pm8450-rpmh-regulators", + .data = pm8450_vreg_data, + }, + { + .compatible = "qcom,pm8550-rpmh-regulators", + .data = pm8550_vreg_data, + }, + { + .compatible = "qcom,pm8550ve-rpmh-regulators", + .data = pm8550ve_vreg_data, + }, + { + .compatible = "qcom,pm8550vs-rpmh-regulators", + .data = pm8550vs_vreg_data, + }, + { + .compatible = "qcom,pm8998-rpmh-regulators", + .data = pm8998_vreg_data, + }, + { + .compatible = "qcom,pmg1110-rpmh-regulators", + .data = pmg1110_vreg_data, + }, + { + .compatible = "qcom,pmi8998-rpmh-regulators", + .data = pmi8998_vreg_data, + }, + { + .compatible = "qcom,pm6150-rpmh-regulators", + .data = pm6150_vreg_data, + }, + { + .compatible = "qcom,pm6150l-rpmh-regulators", + .data = pm6150l_vreg_data, + }, + { + .compatible = "qcom,pm6350-rpmh-regulators", + .data = pm6350_vreg_data, + }, + { + .compatible = "qcom,pmc8180-rpmh-regulators", + .data = pm8150_vreg_data, + }, + { + .compatible = "qcom,pmc8180c-rpmh-regulators", + .data = pm8150l_vreg_data, + }, + { + .compatible = "qcom,pmc8380-rpmh-regulators", + .data = pmc8380_vreg_data, + }, + { + .compatible = "qcom,pmm8155au-rpmh-regulators", + .data = pmm8155au_vreg_data, + }, + { + .compatible = "qcom,pmm8654au-rpmh-regulators", + .data = pmm8654au_vreg_data, + }, + { + .compatible = "qcom,pmx55-rpmh-regulators", + .data = pmx55_vreg_data, + }, + { + .compatible = "qcom,pmx65-rpmh-regulators", + .data = pmx65_vreg_data, + }, + { + .compatible = "qcom,pmx75-rpmh-regulators", + .data = pmx75_vreg_data, + }, + { + .compatible = "qcom,pm7325-rpmh-regulators", + .data = pm7325_vreg_data, + }, + { + .compatible = "qcom,pmr735a-rpmh-regulators", + .data = pmr735a_vreg_data, + }, + { + .compatible = "qcom,pm660-rpmh-regulators", + .data = pm660_vreg_data, + }, + { + .compatible = "qcom,pm660l-rpmh-regulators", + .data = pm660l_vreg_data, + }, + {} +}; +MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table); + +static struct platform_driver rpmh_regulator_driver = { + .driver = { + .name = "qcom-rpmh-regulator", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = of_match_ptr(rpmh_regulator_match_table), + }, + .probe = rpmh_regulator_probe, +}; +module_platform_driver(rpmh_regulator_driver); + +MODULE_DESCRIPTION("Qualcomm RPMh regulator driver"); +MODULE_LICENSE("GPL v2");

Remove unused/unsupported Linux headers and add necessary U-Boot ones.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/power/regulator/qcom-rpmh-regulator.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-)
diff --git a/drivers/power/regulator/qcom-rpmh-regulator.c b/drivers/power/regulator/qcom-rpmh-regulator.c index 80e304711345..5f522de44734 100644 --- a/drivers/power/regulator/qcom-rpmh-regulator.c +++ b/drivers/power/regulator/qcom-rpmh-regulator.c @@ -4,17 +4,14 @@
#define pr_fmt(fmt) "%s: " fmt, __func__
#include <linux/err.h> -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/of.h> -#include <linux/platform_device.h> -#include <linux/slab.h> -#include <linux/string.h> -#include <linux/regulator/driver.h> -#include <linux/regulator/machine.h> -#include <linux/regulator/of_regulator.h> +#include <dm/device_compat.h> +#include <dm/device.h> +#include <dm/devres.h> +#include <dm/lists.h> +#include <power/regulator.h> +#include <log.h>
#include <soc/qcom/cmd-db.h> #include <soc/qcom/rpmh.h>

Import struct linear_range() and builder macro from Linux regulator core.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/power/regulator/qcom-rpmh-regulator.c | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+)
diff --git a/drivers/power/regulator/qcom-rpmh-regulator.c b/drivers/power/regulator/qcom-rpmh-regulator.c index 5f522de44734..d0acee9f558e 100644 --- a/drivers/power/regulator/qcom-rpmh-regulator.c +++ b/drivers/power/regulator/qcom-rpmh-regulator.c @@ -28,8 +28,15 @@ enum rpmh_regulator_type { VRM, XOB, };
+enum rpmh_regulator_mode { + REGULATOR_MODE_RETENTION, + REGULATOR_MODE_LPM, + REGULATOR_MODE_AUTO, + REGULATOR_MODE_HPM, +}; + #define RPMH_REGULATOR_REG_VRM_VOLTAGE 0x0 #define RPMH_REGULATOR_REG_ENABLE 0x4 #define RPMH_REGULATOR_REG_VRM_MODE 0x8
@@ -60,8 +67,38 @@ enum rpmh_regulator_type { #define PMIC5_BOB_MODE_PFM 4 #define PMIC5_BOB_MODE_AUTO 6 #define PMIC5_BOB_MODE_PWM 7
+ +/** + * struct linear_range - table of selector - value pairs + * + * Define a lookup-table for range of values. Intended to help when looking + * for a register value matching certaing physical measure (like voltage). + * Usable when increment of one in register always results a constant increment + * of the physical measure (like voltage). + * + * @min: Lowest value in range + * @min_sel: Lowest selector for range + * @max_sel: Highest selector for range + * @step: Value step size + */ +struct linear_range { + unsigned int min; + unsigned int min_sel; + unsigned int max_sel; + unsigned int step; +}; + +/* Initialize struct linear_range for regulators */ +#define REGULATOR_LINEAR_RANGE(_min_uV, _min_sel, _max_sel, _step_uV) \ +{ \ + .min = _min_uV, \ + .min_sel = _min_sel, \ + .max_sel = _max_sel, \ + .step = _step_uV, \ +} + /** * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations * @regulator_type: RPMh accelerator type used to manage this * regulator

Switch to our linear_range helpers and remove unused/unsupported linux-isms.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/power/regulator/qcom-rpmh-regulator.c | 28 +++++++++++++-------------- 1 file changed, 13 insertions(+), 15 deletions(-)
diff --git a/drivers/power/regulator/qcom-rpmh-regulator.c b/drivers/power/regulator/qcom-rpmh-regulator.c index d0acee9f558e..089623f3a2b9 100644 --- a/drivers/power/regulator/qcom-rpmh-regulator.c +++ b/drivers/power/regulator/qcom-rpmh-regulator.c @@ -102,13 +102,12 @@ struct linear_range { * struct rpmh_vreg_hw_data - RPMh regulator hardware configurations * @regulator_type: RPMh accelerator type used to manage this * regulator * @ops: Pointer to regulator ops callback structure - * @voltage_ranges: The possible ranges of voltages supported by this - * PMIC regulator type - * @n_linear_ranges: Number of entries in voltage_ranges + * @voltage_range: The single range of voltages supported by this + * PMIC regulator type * @n_voltages: The number of unique voltage set points defined - * by voltage_ranges + * by voltage_range * @hpm_min_load_uA: Minimum load current in microamps that requires * high power mode (HPM) operation. This is used * for LDO hardware type regulators only. * @pmic_mode_map: Array indexed by regulator framework mode @@ -119,15 +118,15 @@ struct linear_range { * in device tree to a regulator framework mode */ struct rpmh_vreg_hw_data { enum rpmh_regulator_type regulator_type; - const struct regulator_ops *ops; - const struct linear_range *voltage_ranges; - int n_linear_ranges; + const struct dm_regulator_ops *ops; + struct linear_range voltage_range; int n_voltages; int hpm_min_load_uA; - const int *pmic_mode_map; - unsigned int (*of_map_mode)(unsigned int mode); + struct dm_regulator_mode *pmic_mode_map; + int n_modes; + unsigned int (*of_map_mode)(unsigned int mode); };
/** * struct rpmh_vreg - individual RPMh regulator data structure encapsulating a @@ -148,23 +147,22 @@ struct rpmh_vreg_hw_data { * not * @bypassed: Boolean indicating if the regulator is in * bypass (pass-through) mode or not. This is * only used by BOB rpmh-regulator resources. - * @voltage_selector: Selector used for get_voltage_sel() and - * set_voltage_sel() callbacks + * @uv: Selector used for get_voltage_sel() and + * set_value() callbacks * @mode: RPMh VRM regulator current framework mode */ struct rpmh_vreg { - struct device *dev; + struct udevice *dev; u32 addr; - struct regulator_desc rdesc; const struct rpmh_vreg_hw_data *hw_data; bool always_wait_for_ack;
int enabled; bool bypassed; - int voltage_selector; - unsigned int mode; + int uv; + int mode; };
/** * struct rpmh_vreg_init_data - initialization data for an RPMh regulator

Initially just include the few regulators needed for the RB5 board. Others can be added back as-needed.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/power/regulator/qcom-rpmh-regulator.c | 1281 ------------------------- 1 file changed, 1281 deletions(-)
diff --git a/drivers/power/regulator/qcom-rpmh-regulator.c b/drivers/power/regulator/qcom-rpmh-regulator.c index 089623f3a2b9..2a8e8f9ac444 100644 --- a/drivers/power/regulator/qcom-rpmh-regulator.c +++ b/drivers/power/regulator/qcom-rpmh-regulator.c @@ -338,68 +338,8 @@ static unsigned int rpmh_regulator_vrm_get_mode(struct regulator_dev *rdev)
return vreg->mode; }
-/** - * rpmh_regulator_vrm_get_optimum_mode() - get the mode based on the load - * @rdev: Regulator device pointer for the rpmh-regulator - * @input_uV: Input voltage - * @output_uV: Output voltage - * @load_uA: Aggregated load current in microamps - * - * This function is used in the regulator_ops for VRM type RPMh regulator - * devices. - * - * Return: 0 on success, errno on failure - */ -static unsigned int rpmh_regulator_vrm_get_optimum_mode( - struct regulator_dev *rdev, int input_uV, int output_uV, int load_uA) -{ - struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); - - if (load_uA >= vreg->hw_data->hpm_min_load_uA) - return REGULATOR_MODE_NORMAL; - else - return REGULATOR_MODE_IDLE; -} - -static int rpmh_regulator_vrm_set_bypass(struct regulator_dev *rdev, - bool enable) -{ - struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); - int ret; - - if (vreg->bypassed == enable) - return 0; - - ret = rpmh_regulator_vrm_set_mode_bypass(vreg, vreg->mode, enable); - if (!ret) - vreg->bypassed = enable; - - return ret; -} - -static int rpmh_regulator_vrm_get_bypass(struct regulator_dev *rdev, - bool *enable) -{ - struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); - - *enable = vreg->bypassed; - - return 0; -} - -static const struct regulator_ops rpmh_regulator_vrm_ops = { - .enable = rpmh_regulator_enable, - .disable = rpmh_regulator_disable, - .is_enabled = rpmh_regulator_is_enabled, - .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel, - .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel, - .list_voltage = regulator_list_voltage_linear_range, - .set_mode = rpmh_regulator_vrm_set_mode, - .get_mode = rpmh_regulator_vrm_get_mode, -}; - static const struct regulator_ops rpmh_regulator_vrm_drms_ops = { .enable = rpmh_regulator_enable, .disable = rpmh_regulator_disable, .is_enabled = rpmh_regulator_is_enabled, @@ -410,331 +350,8 @@ static const struct regulator_ops rpmh_regulator_vrm_drms_ops = { .get_mode = rpmh_regulator_vrm_get_mode, .get_optimum_mode = rpmh_regulator_vrm_get_optimum_mode, };
-static const struct regulator_ops rpmh_regulator_vrm_bypass_ops = { - .enable = rpmh_regulator_enable, - .disable = rpmh_regulator_disable, - .is_enabled = rpmh_regulator_is_enabled, - .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel, - .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel, - .list_voltage = regulator_list_voltage_linear_range, - .set_mode = rpmh_regulator_vrm_set_mode, - .get_mode = rpmh_regulator_vrm_get_mode, - .set_bypass = rpmh_regulator_vrm_set_bypass, - .get_bypass = rpmh_regulator_vrm_get_bypass, -}; - -static const struct regulator_ops rpmh_regulator_xob_ops = { - .enable = rpmh_regulator_enable, - .disable = rpmh_regulator_disable, - .is_enabled = rpmh_regulator_is_enabled, -}; - -/** - * rpmh_regulator_init_vreg() - initialize all attributes of an rpmh-regulator - * @vreg: Pointer to the individual rpmh-regulator resource - * @dev: Pointer to the top level rpmh-regulator PMIC device - * @node: Pointer to the individual rpmh-regulator resource - * device node - * @pmic_id: String used to identify the top level rpmh-regulator - * PMIC device on the board - * @pmic_rpmh_data: Pointer to a null-terminated array of rpmh-regulator - * resources defined for the top level PMIC device - * - * Return: 0 on success, errno on failure - */ -static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev, - struct device_node *node, const char *pmic_id, - const struct rpmh_vreg_init_data *pmic_rpmh_data) -{ - struct regulator_config reg_config = {}; - char rpmh_resource_name[20] = ""; - const struct rpmh_vreg_init_data *rpmh_data; - struct regulator_init_data *init_data; - struct regulator_dev *rdev; - int ret; - - vreg->dev = dev; - - for (rpmh_data = pmic_rpmh_data; rpmh_data->name; rpmh_data++) - if (of_node_name_eq(node, rpmh_data->name)) - break; - - if (!rpmh_data->name) { - dev_err(dev, "Unknown regulator %pOFn\n", node); - return -EINVAL; - } - - scnprintf(rpmh_resource_name, sizeof(rpmh_resource_name), - rpmh_data->resource_name, pmic_id); - - vreg->addr = cmd_db_read_addr(rpmh_resource_name); - if (!vreg->addr) { - dev_err(dev, "%pOFn: could not find RPMh address for resource %s\n", - node, rpmh_resource_name); - return -ENODEV; - } - - vreg->rdesc.name = rpmh_data->name; - vreg->rdesc.supply_name = rpmh_data->supply_name; - vreg->hw_data = rpmh_data->hw_data; - - vreg->enabled = -EINVAL; - vreg->voltage_selector = -ENOTRECOVERABLE; - vreg->mode = REGULATOR_MODE_INVALID; - - if (rpmh_data->hw_data->n_voltages) { - vreg->rdesc.linear_ranges = rpmh_data->hw_data->voltage_ranges; - vreg->rdesc.n_linear_ranges = rpmh_data->hw_data->n_linear_ranges; - vreg->rdesc.n_voltages = rpmh_data->hw_data->n_voltages; - } - - vreg->always_wait_for_ack = of_property_read_bool(node, - "qcom,always-wait-for-ack"); - - vreg->rdesc.owner = THIS_MODULE; - vreg->rdesc.type = REGULATOR_VOLTAGE; - vreg->rdesc.ops = vreg->hw_data->ops; - vreg->rdesc.of_map_mode = vreg->hw_data->of_map_mode; - - init_data = of_get_regulator_init_data(dev, node, &vreg->rdesc); - if (!init_data) - return -ENOMEM; - - if (rpmh_data->hw_data->regulator_type == XOB && - init_data->constraints.min_uV && - init_data->constraints.min_uV == init_data->constraints.max_uV) { - vreg->rdesc.fixed_uV = init_data->constraints.min_uV; - vreg->rdesc.n_voltages = 1; - } - - reg_config.dev = dev; - reg_config.init_data = init_data; - reg_config.of_node = node; - reg_config.driver_data = vreg; - - rdev = devm_regulator_register(dev, &vreg->rdesc, ®_config); - if (IS_ERR(rdev)) { - ret = PTR_ERR(rdev); - dev_err(dev, "%pOFn: devm_regulator_register() failed, ret=%d\n", - node, ret); - return ret; - } - - dev_dbg(dev, "%pOFn regulator registered for RPMh resource %s @ 0x%05X\n", - node, rpmh_resource_name, vreg->addr); - - return 0; -} - -static const int pmic_mode_map_pmic4_ldo[REGULATOR_MODE_STANDBY + 1] = { - [REGULATOR_MODE_INVALID] = -EINVAL, - [REGULATOR_MODE_STANDBY] = PMIC4_LDO_MODE_RETENTION, - [REGULATOR_MODE_IDLE] = PMIC4_LDO_MODE_LPM, - [REGULATOR_MODE_NORMAL] = PMIC4_LDO_MODE_HPM, - [REGULATOR_MODE_FAST] = -EINVAL, -}; - -static const int pmic_mode_map_pmic5_ldo[REGULATOR_MODE_STANDBY + 1] = { - [REGULATOR_MODE_INVALID] = -EINVAL, - [REGULATOR_MODE_STANDBY] = PMIC5_LDO_MODE_RETENTION, - [REGULATOR_MODE_IDLE] = PMIC5_LDO_MODE_LPM, - [REGULATOR_MODE_NORMAL] = PMIC5_LDO_MODE_HPM, - [REGULATOR_MODE_FAST] = -EINVAL, -}; - -static const int pmic_mode_map_pmic5_ldo_hpm[REGULATOR_MODE_STANDBY + 1] = { - [REGULATOR_MODE_INVALID] = -EINVAL, - [REGULATOR_MODE_STANDBY] = -EINVAL, - [REGULATOR_MODE_IDLE] = -EINVAL, - [REGULATOR_MODE_NORMAL] = PMIC5_LDO_MODE_HPM, - [REGULATOR_MODE_FAST] = -EINVAL, -}; - -static unsigned int rpmh_regulator_pmic4_ldo_of_map_mode(unsigned int rpmh_mode) -{ - unsigned int mode; - - switch (rpmh_mode) { - case RPMH_REGULATOR_MODE_HPM: - mode = REGULATOR_MODE_NORMAL; - break; - case RPMH_REGULATOR_MODE_LPM: - mode = REGULATOR_MODE_IDLE; - break; - case RPMH_REGULATOR_MODE_RET: - mode = REGULATOR_MODE_STANDBY; - break; - default: - mode = REGULATOR_MODE_INVALID; - break; - } - - return mode; -} - -static const int pmic_mode_map_pmic4_smps[REGULATOR_MODE_STANDBY + 1] = { - [REGULATOR_MODE_INVALID] = -EINVAL, - [REGULATOR_MODE_STANDBY] = PMIC4_SMPS_MODE_RETENTION, - [REGULATOR_MODE_IDLE] = PMIC4_SMPS_MODE_PFM, - [REGULATOR_MODE_NORMAL] = PMIC4_SMPS_MODE_AUTO, - [REGULATOR_MODE_FAST] = PMIC4_SMPS_MODE_PWM, -}; - -static const int pmic_mode_map_pmic5_smps[REGULATOR_MODE_STANDBY + 1] = { - [REGULATOR_MODE_INVALID] = -EINVAL, - [REGULATOR_MODE_STANDBY] = PMIC5_SMPS_MODE_RETENTION, - [REGULATOR_MODE_IDLE] = PMIC5_SMPS_MODE_PFM, - [REGULATOR_MODE_NORMAL] = PMIC5_SMPS_MODE_AUTO, - [REGULATOR_MODE_FAST] = PMIC5_SMPS_MODE_PWM, -}; - -static unsigned int -rpmh_regulator_pmic4_smps_of_map_mode(unsigned int rpmh_mode) -{ - unsigned int mode; - - switch (rpmh_mode) { - case RPMH_REGULATOR_MODE_HPM: - mode = REGULATOR_MODE_FAST; - break; - case RPMH_REGULATOR_MODE_AUTO: - mode = REGULATOR_MODE_NORMAL; - break; - case RPMH_REGULATOR_MODE_LPM: - mode = REGULATOR_MODE_IDLE; - break; - case RPMH_REGULATOR_MODE_RET: - mode = REGULATOR_MODE_STANDBY; - break; - default: - mode = REGULATOR_MODE_INVALID; - break; - } - - return mode; -} - -static const int pmic_mode_map_pmic4_bob[REGULATOR_MODE_STANDBY + 1] = { - [REGULATOR_MODE_INVALID] = -EINVAL, - [REGULATOR_MODE_STANDBY] = -EINVAL, - [REGULATOR_MODE_IDLE] = PMIC4_BOB_MODE_PFM, - [REGULATOR_MODE_NORMAL] = PMIC4_BOB_MODE_AUTO, - [REGULATOR_MODE_FAST] = PMIC4_BOB_MODE_PWM, -}; - -static const int pmic_mode_map_pmic5_bob[REGULATOR_MODE_STANDBY + 1] = { - [REGULATOR_MODE_INVALID] = -EINVAL, - [REGULATOR_MODE_STANDBY] = -EINVAL, - [REGULATOR_MODE_IDLE] = PMIC5_BOB_MODE_PFM, - [REGULATOR_MODE_NORMAL] = PMIC5_BOB_MODE_AUTO, - [REGULATOR_MODE_FAST] = PMIC5_BOB_MODE_PWM, -}; - -static unsigned int rpmh_regulator_pmic4_bob_of_map_mode(unsigned int rpmh_mode) -{ - unsigned int mode; - - switch (rpmh_mode) { - case RPMH_REGULATOR_MODE_HPM: - mode = REGULATOR_MODE_FAST; - break; - case RPMH_REGULATOR_MODE_AUTO: - mode = REGULATOR_MODE_NORMAL; - break; - case RPMH_REGULATOR_MODE_LPM: - mode = REGULATOR_MODE_IDLE; - break; - default: - mode = REGULATOR_MODE_INVALID; - break; - } - - return mode; -} - -static const struct rpmh_vreg_hw_data pmic4_pldo = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_drms_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(1664000, 0, 255, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 256, - .hpm_min_load_uA = 10000, - .pmic_mode_map = pmic_mode_map_pmic4_ldo, - .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic4_pldo_lv = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_drms_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(1256000, 0, 127, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 128, - .hpm_min_load_uA = 10000, - .pmic_mode_map = pmic_mode_map_pmic4_ldo, - .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic4_nldo = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_drms_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(312000, 0, 127, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 128, - .hpm_min_load_uA = 30000, - .pmic_mode_map = pmic_mode_map_pmic4_ldo, - .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic4_hfsmps3 = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 216, - .pmic_mode_map = pmic_mode_map_pmic4_smps, - .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic4_ftsmps426 = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(320000, 0, 258, 4000), - }, - .n_linear_ranges = 1, - .n_voltages = 259, - .pmic_mode_map = pmic_mode_map_pmic4_smps, - .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic4_bob = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_bypass_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(1824000, 0, 83, 32000), - }, - .n_linear_ranges = 1, - .n_voltages = 84, - .pmic_mode_map = pmic_mode_map_pmic4_bob, - .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic4_lvs = { - .regulator_type = XOB, - .ops = &rpmh_regulator_xob_ops, - /* LVS hardware does not support voltage or mode configuration. */ -}; - static const struct rpmh_vreg_hw_data pmic5_pldo = { .regulator_type = VRM, .ops = &rpmh_regulator_vrm_drms_ops, .voltage_ranges = (struct linear_range[]) { @@ -759,810 +376,24 @@ static const struct rpmh_vreg_hw_data pmic5_pldo_lv = { .pmic_mode_map = pmic_mode_map_pmic5_ldo, .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, };
-static const struct rpmh_vreg_hw_data pmic5_pldo515_mv = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_drms_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(1800000, 0, 187, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 188, - .hpm_min_load_uA = 10000, - .pmic_mode_map = pmic_mode_map_pmic5_ldo, - .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_pldo502 = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(1504000, 0, 255, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 256, - .pmic_mode_map = pmic_mode_map_pmic5_ldo_hpm, - .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_pldo502ln = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(1800000, 0, 2, 200000), - REGULATOR_LINEAR_RANGE(2608000, 3, 28, 16000), - REGULATOR_LINEAR_RANGE(3104000, 29, 30, 96000), - REGULATOR_LINEAR_RANGE(3312000, 31, 31, 0), - }, - .n_linear_ranges = 4, - .n_voltages = 32, - .pmic_mode_map = pmic_mode_map_pmic5_ldo_hpm, - .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_nldo = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_drms_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(320000, 0, 123, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 124, - .hpm_min_load_uA = 30000, - .pmic_mode_map = pmic_mode_map_pmic5_ldo, - .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_nldo515 = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_drms_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(320000, 0, 210, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 211, - .hpm_min_load_uA = 30000, - .pmic_mode_map = pmic_mode_map_pmic5_ldo, - .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_nldo502 = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_drms_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(528000, 0, 127, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 128, - .hpm_min_load_uA = 30000, - .pmic_mode_map = pmic_mode_map_pmic5_ldo, - .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_hfsmps510 = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 216, - .pmic_mode_map = pmic_mode_map_pmic5_smps, - .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_ftsmps510 = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(300000, 0, 263, 4000), - }, - .n_linear_ranges = 1, - .n_voltages = 264, - .pmic_mode_map = pmic_mode_map_pmic5_smps, - .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_ftsmps520 = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(300000, 0, 263, 4000), - }, - .n_linear_ranges = 1, - .n_voltages = 264, - .pmic_mode_map = pmic_mode_map_pmic5_smps, - .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_ftsmps525_lv = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(300000, 0, 267, 4000), - }, - .n_linear_ranges = 1, - .n_voltages = 268, - .pmic_mode_map = pmic_mode_map_pmic5_smps, - .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_ftsmps525_mv = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(600000, 0, 267, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 268, - .pmic_mode_map = pmic_mode_map_pmic5_smps, - .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_ftsmps527 = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(320000, 0, 215, 8000), - }, - .n_linear_ranges = 1, - .n_voltages = 215, - .pmic_mode_map = pmic_mode_map_pmic5_smps, - .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_hfsmps515 = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(320000, 0, 235, 16000), - }, - .n_linear_ranges = 1, - .n_voltages = 236, - .pmic_mode_map = pmic_mode_map_pmic5_smps, - .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_hfsmps515_1 = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(900000, 0, 4, 16000), - }, - .n_linear_ranges = 1, - .n_voltages = 5, - .pmic_mode_map = pmic_mode_map_pmic5_smps, - .of_map_mode = rpmh_regulator_pmic4_smps_of_map_mode, -}; - -static const struct rpmh_vreg_hw_data pmic5_bob = { - .regulator_type = VRM, - .ops = &rpmh_regulator_vrm_bypass_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(3000000, 0, 31, 32000), - }, - .n_linear_ranges = 1, - .n_voltages = 32, - .pmic_mode_map = pmic_mode_map_pmic5_bob, - .of_map_mode = rpmh_regulator_pmic4_bob_of_map_mode, -}; - #define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \ { \ .name = _name, \ .resource_name = _resource_name, \ .hw_data = _hw_data, \ .supply_name = _supply_name, \ }
-static const struct rpmh_vreg_init_data pm8998_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic4_hfsmps3, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic4_hfsmps3, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic4_hfsmps3, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic4_ftsmps426, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic4_ftsmps426, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic4_ftsmps426, "vdd-s8"), - RPMH_VREG("smps9", "smp%s9", &pmic4_ftsmps426, "vdd-s9"), - RPMH_VREG("smps10", "smp%s10", &pmic4_ftsmps426, "vdd-s10"), - RPMH_VREG("smps11", "smp%s11", &pmic4_ftsmps426, "vdd-s11"), - RPMH_VREG("smps12", "smp%s12", &pmic4_ftsmps426, "vdd-s12"), - RPMH_VREG("smps13", "smp%s13", &pmic4_ftsmps426, "vdd-s13"), - RPMH_VREG("ldo1", "ldo%s1", &pmic4_nldo, "vdd-l1-l27"), - RPMH_VREG("ldo2", "ldo%s2", &pmic4_nldo, "vdd-l2-l8-l17"), - RPMH_VREG("ldo3", "ldo%s3", &pmic4_nldo, "vdd-l3-l11"), - RPMH_VREG("ldo4", "ldo%s4", &pmic4_nldo, "vdd-l4-l5"), - RPMH_VREG("ldo5", "ldo%s5", &pmic4_nldo, "vdd-l4-l5"), - RPMH_VREG("ldo6", "ldo%s6", &pmic4_pldo, "vdd-l6"), - RPMH_VREG("ldo7", "ldo%s7", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), - RPMH_VREG("ldo8", "ldo%s8", &pmic4_nldo, "vdd-l2-l8-l17"), - RPMH_VREG("ldo9", "ldo%s9", &pmic4_pldo, "vdd-l9"), - RPMH_VREG("ldo10", "ldo%s10", &pmic4_pldo, "vdd-l10-l23-l25"), - RPMH_VREG("ldo11", "ldo%s11", &pmic4_nldo, "vdd-l3-l11"), - RPMH_VREG("ldo12", "ldo%s12", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), - RPMH_VREG("ldo13", "ldo%s13", &pmic4_pldo, "vdd-l13-l19-l21"), - RPMH_VREG("ldo14", "ldo%s14", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), - RPMH_VREG("ldo15", "ldo%s15", &pmic4_pldo_lv, "vdd-l7-l12-l14-l15"), - RPMH_VREG("ldo16", "ldo%s16", &pmic4_pldo, "vdd-l16-l28"), - RPMH_VREG("ldo17", "ldo%s17", &pmic4_nldo, "vdd-l2-l8-l17"), - RPMH_VREG("ldo18", "ldo%s18", &pmic4_pldo, "vdd-l18-l22"), - RPMH_VREG("ldo19", "ldo%s19", &pmic4_pldo, "vdd-l13-l19-l21"), - RPMH_VREG("ldo20", "ldo%s20", &pmic4_pldo, "vdd-l20-l24"), - RPMH_VREG("ldo21", "ldo%s21", &pmic4_pldo, "vdd-l13-l19-l21"), - RPMH_VREG("ldo22", "ldo%s22", &pmic4_pldo, "vdd-l18-l22"), - RPMH_VREG("ldo23", "ldo%s23", &pmic4_pldo, "vdd-l10-l23-l25"), - RPMH_VREG("ldo24", "ldo%s24", &pmic4_pldo, "vdd-l20-l24"), - RPMH_VREG("ldo25", "ldo%s25", &pmic4_pldo, "vdd-l10-l23-l25"), - RPMH_VREG("ldo26", "ldo%s26", &pmic4_nldo, "vdd-l26"), - RPMH_VREG("ldo27", "ldo%s27", &pmic4_nldo, "vdd-l1-l27"), - RPMH_VREG("ldo28", "ldo%s28", &pmic4_pldo, "vdd-l16-l28"), - RPMH_VREG("lvs1", "vs%s1", &pmic4_lvs, "vin-lvs-1-2"), - RPMH_VREG("lvs2", "vs%s2", &pmic4_lvs, "vin-lvs-1-2"), - {} -}; - -static const struct rpmh_vreg_init_data pmg1110_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), - {} -}; - -static const struct rpmh_vreg_init_data pmi8998_vreg_data[] = { - RPMH_VREG("bob", "bob%s1", &pmic4_bob, "vdd-bob"), - {} -}; - -static const struct rpmh_vreg_init_data pm8005_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic4_ftsmps426, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic4_ftsmps426, "vdd-s4"), - {} -}; - static const struct rpmh_vreg_init_data pm8150_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), - RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), - RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l8-l11"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l10"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l4-l5-l18"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l3-l4-l5-l18"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l4-l5-l18"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l7-l12-l14-l15"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l1-l8-l11"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9"), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l2-l10"), - RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo, "vdd-l1-l8-l11"), - RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l13-l16-l17"), - RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), - RPMH_VREG("ldo15", "ldo%s15", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), - RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l13-l16-l17"), - RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l13-l16-l17"), - RPMH_VREG("ldo18", "ldo%s18", &pmic5_nldo, "vdd-l3-l4-l5-l18"), {} };
static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_hfsmps510, "vdd-s8"), RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l8"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2-l3"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l2-l3"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l4-l5-l6"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l4-l5-l6"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l4-l5-l6"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l7-l11"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo_lv, "vdd-l1-l8"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l9-l10"), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l9-l10"), RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l7-l11"), - RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), - {} -}; - -static const struct rpmh_vreg_init_data pmm8155au_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), - RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), - RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l8-l11"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l10"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l4-l5-l18"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l3-l4-l5-l18"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l4-l5-l18"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l1-l8-l11"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9"), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l2-l10"), - RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo, "vdd-l1-l8-l11"), - RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), - RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l13-l16-l17"), - RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), - RPMH_VREG("ldo15", "ldo%s15", &pmic5_pldo_lv, "vdd-l7-l12-l14-l15"), - RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l13-l16-l17"), - RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l13-l16-l17"), - RPMH_VREG("ldo18", "ldo%s18", &pmic5_nldo, "vdd-l3-l4-l5-l18"), - {} -}; - -static const struct rpmh_vreg_init_data pmm8654au_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps527, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps527, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps527, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps527, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps527, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps527, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps527, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps527, "vdd-s8"), - RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps527, "vdd-s9"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-s9"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2-l3"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l2-l3"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-s9"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo515, "vdd-s9"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo515, "vdd-l6-l7"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo515, "vdd-l6-l7"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo515_mv, "vdd-l8-l9"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l8-l9"), - {} -}; - -static const struct rpmh_vreg_init_data pm8350_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), - RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), - RPMH_VREG("smps10", "smp%s10", &pmic5_hfsmps510, "vdd-s10"), - RPMH_VREG("smps11", "smp%s11", &pmic5_hfsmps510, "vdd-s11"), - RPMH_VREG("smps12", "smp%s12", &pmic5_hfsmps510, "vdd-s12"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l4"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l7"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l5"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l1-l4"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l3-l5"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9-l10"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l2-l7"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9-l10"), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l6-l9-l10"), - {} -}; - -static const struct rpmh_vreg_init_data pm8350c_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps515, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps510, "vdd-s8"), - RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps510, "vdd-s9"), - RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps510, "vdd-s10"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l12"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo_lv, "vdd-l2-l8"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l6-l9-l11"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo_lv, "vdd-l2-l8"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l6-l9-l11"), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l10"), - RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l6-l9-l11"), - RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l1-l12"), - RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l3-l4-l5-l7-l13"), - RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), - {} -}; - -static const struct rpmh_vreg_init_data pm8450_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps520, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps520, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps520, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps520, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps520, "vdd-s6"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo_lv, "vdd-l4"), - {} -}; - -static const struct rpmh_vreg_init_data pm8550_vreg_data[] = { - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1-l4-l10"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l13-l14"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-l1-l4-l10"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l16"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l6-l7"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l6-l7"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo, "vdd-l8-l9"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l8-l9"), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo515, "vdd-l1-l4-l10"), - RPMH_VREG("ldo11", "ldo%s11", &pmic5_nldo515, "vdd-l11"), - RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo515, "vdd-l12"), - RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l2-l13-l14"), - RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo, "vdd-l2-l13-l14"), - RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo515, "vdd-l15"), - RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l5-l16"), - RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l17"), - RPMH_VREG("bob1", "bob%s1", &pmic5_bob, "vdd-bob1"), - RPMH_VREG("bob2", "bob%s2", &pmic5_bob, "vdd-bob2"), - {} -}; - -static const struct rpmh_vreg_init_data pm8550vs_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_lv, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_mv, "vdd-s6"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), - {} -}; - -static const struct rpmh_vreg_init_data pm8550ve_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_mv, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_lv, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525_lv, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525_lv, "vdd-s8"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), - {} -}; - -static const struct rpmh_vreg_init_data pmc8380_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_mv, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_lv, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525_lv, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525_lv, "vdd-s8"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), - {} -}; - -static const struct rpmh_vreg_init_data pm8009_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515, "vdd-s2"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo_lv, "vdd-l7"), - {} -}; - -static const struct rpmh_vreg_init_data pm8009_1_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps515_1, "vdd-s2"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo_lv, "vdd-l7"), - {} -}; - -static const struct rpmh_vreg_init_data pm8010_vreg_data[] = { - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo502, "vdd-l1-l2"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo502, "vdd-l1-l2"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_pldo502ln, "vdd-l3-l4"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo502ln, "vdd-l3-l4"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo502, "vdd-l5"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo502ln, "vdd-l6"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo502, "vdd-l7"), -}; - -static const struct rpmh_vreg_init_data pm6150_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2-l3"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l2-l3"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4-l7-l8"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l16-l17-l18-l19"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l4-l7-l8"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l4-l7-l8"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l9"), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo_lv, "vdd-l10-l14-l15"), - RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo_lv, "vdd-l11-l12-l13"), - RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo_lv, "vdd-l11-l12-l13"), - RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo_lv, "vdd-l11-l12-l13"), - RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo_lv, "vdd-l10-l14-l15"), - RPMH_VREG("ldo15", "ldo%s15", &pmic5_pldo_lv, "vdd-l10-l14-l15"), - RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l5-l16-l17-l18-l19"), - RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo, "vdd-l5-l16-l17-l18-l19"), - RPMH_VREG("ldo18", "ldo%s18", &pmic5_pldo, "vdd-l5-l16-l17-l18-l19"), - RPMH_VREG("ldo19", "ldo%s19", &pmic5_pldo, "vdd-l5-l16-l17-l18-l19"), - {} -}; - -static const struct rpmh_vreg_init_data pm6150l_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps510, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps510, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps510, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps510, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps510, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_hfsmps510, "vdd-s8"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_pldo_lv, "vdd-l1-l8"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2-l3"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l2-l3"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo, "vdd-l4-l5-l6"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l4-l5-l6"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l4-l5-l6"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l7-l11"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo, "vdd-l1-l8"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, "vdd-l9-l10"), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l9-l10"), - RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l7-l11"), - RPMH_VREG("bob", "bob%s1", &pmic5_bob, "vdd-bob"), - {} -}; - -static const struct rpmh_vreg_init_data pm6350_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, NULL), - RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, NULL), - /* smps3 - smps5 not configured */ - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, NULL), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, NULL), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_pldo, NULL), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, NULL), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, NULL), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, NULL), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, NULL), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_pldo, NULL), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_pldo, NULL), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, NULL), - RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, NULL), - RPMH_VREG("ldo12", "ldo%s12", &pmic5_pldo, NULL), - RPMH_VREG("ldo13", "ldo%s13", &pmic5_nldo, NULL), - RPMH_VREG("ldo14", "ldo%s14", &pmic5_pldo, NULL), - RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, NULL), - RPMH_VREG("ldo16", "ldo%s16", &pmic5_nldo, NULL), - /* ldo17 not configured */ - RPMH_VREG("ldo18", "ldo%s18", &pmic5_nldo, NULL), - RPMH_VREG("ldo19", "ldo%s19", &pmic5_nldo, NULL), - RPMH_VREG("ldo20", "ldo%s20", &pmic5_nldo, NULL), - RPMH_VREG("ldo21", "ldo%s21", &pmic5_nldo, NULL), - RPMH_VREG("ldo22", "ldo%s22", &pmic5_nldo, NULL), -}; - -static const struct rpmh_vreg_init_data pmx55_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_hfsmps510, "vdd-s7"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3-l9"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4-l12"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7-l8"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l7-l8"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l3-l9"), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10-l11-l13"), - RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l10-l11-l13"), - RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l4-l12"), - RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l10-l11-l13"), - RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14"), - RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l15"), - RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l16"), - {} -}; - -static const struct rpmh_vreg_init_data pmx65_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps510, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_hfsmps510, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps510, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_hfsmps510, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_hfsmps510, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps510, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_hfsmps510, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_hfsmps510, "vdd-s8"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l2-l18"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l4"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo, "vdd-l5-l6-l16"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo, "vdd-l5-l6-l16"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo, "vdd-l7"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8-l9"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l8-l9"), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10"), - RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l11-l13"), - RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l12"), - RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l11-l13"), - RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14"), - RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l15"), - RPMH_VREG("ldo16", "ldo%s16", &pmic5_pldo, "vdd-l5-l6-l16"), - RPMH_VREG("ldo17", "ldo%s17", &pmic5_nldo, "vdd-l17"), - /* ldo18 not configured */ - RPMH_VREG("ldo19", "ldo%s19", &pmic5_nldo, "vdd-l19"), - RPMH_VREG("ldo20", "ldo%s20", &pmic5_nldo, "vdd-l20"), - RPMH_VREG("ldo21", "ldo%s21", &pmic5_nldo, "vdd-l21"), - {} -}; - -static const struct rpmh_vreg_init_data pmx75_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps525_lv, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps525_lv, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps525_lv, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps525_mv, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps525_lv, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps525_lv, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps525_lv, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_ftsmps525_lv, "vdd-s8"), - RPMH_VREG("smps9", "smp%s9", &pmic5_ftsmps525_lv, "vdd-s9"), - RPMH_VREG("smps10", "smp%s10", &pmic5_ftsmps525_lv, "vdd-s10"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo515, "vdd-l1"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo515, "vdd-l2-18"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo515, "vdd-l3"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo515, "vdd-l4-l16"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_pldo_lv, "vdd-l5-l6"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_pldo_lv, "vdd-l5-l6"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_nldo515, "vdd-l7"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo515, "vdd-l8-l9"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo515, "vdd-l8-l9"), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_pldo, "vdd-l10"), - RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l11-l13"), - RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo515, "vdd-l12"), - RPMH_VREG("ldo13", "ldo%s13", &pmic5_pldo, "vdd-l11-l13"), - RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo515, "vdd-l14"), - RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo515, "vdd-l15"), - RPMH_VREG("ldo16", "ldo%s16", &pmic5_nldo515, "vdd-l4-l16"), - RPMH_VREG("ldo17", "ldo%s17", &pmic5_nldo515, "vdd-l17"), - /* ldo18 not configured */ - RPMH_VREG("ldo19", "ldo%s19", &pmic5_nldo515, "vdd-l19"), - RPMH_VREG("ldo20", "ldo%s20", &pmic5_nldo515, "vdd-l20-l21"), - RPMH_VREG("ldo21", "ldo%s21", &pmic5_nldo515, "vdd-l20-l21"), -}; - -static const struct rpmh_vreg_init_data pm7325_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_hfsmps510, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_ftsmps520, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic5_ftsmps520, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic5_ftsmps520, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic5_ftsmps520, "vdd-s6"), - RPMH_VREG("smps7", "smp%s7", &pmic5_ftsmps520, "vdd-s7"), - RPMH_VREG("smps8", "smp%s8", &pmic5_hfsmps510, "vdd-s8"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l4-l12-l15"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_pldo, "vdd-l2-l7"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_nldo, "vdd-l1-l4-l12-l15"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l5"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l6-l9-l10"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l2-l7"), - RPMH_VREG("ldo8", "ldo%s8", &pmic5_nldo, "vdd-l8"), - RPMH_VREG("ldo9", "ldo%s9", &pmic5_nldo, "vdd-l6-l9-l10"), - RPMH_VREG("ldo10", "ldo%s10", &pmic5_nldo, "vdd-l6-l9-l10"), - RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo_lv, "vdd-l11-l17-l18-l19"), - RPMH_VREG("ldo12", "ldo%s12", &pmic5_nldo, "vdd-l1-l4-l12-l15"), - RPMH_VREG("ldo13", "ldo%s13", &pmic5_nldo, "vdd-l13"), - RPMH_VREG("ldo14", "ldo%s14", &pmic5_nldo, "vdd-l14-l16"), - RPMH_VREG("ldo15", "ldo%s15", &pmic5_nldo, "vdd-l1-l4-l12-l15"), - RPMH_VREG("ldo16", "ldo%s16", &pmic5_nldo, "vdd-l14-l16"), - RPMH_VREG("ldo17", "ldo%s17", &pmic5_pldo_lv, "vdd-l11-l17-l18-l19"), - RPMH_VREG("ldo18", "ldo%s18", &pmic5_pldo_lv, "vdd-l11-l17-l18-l19"), - RPMH_VREG("ldo19", "ldo%s19", &pmic5_pldo_lv, "vdd-l11-l17-l18-l19"), - {} -}; - -static const struct rpmh_vreg_init_data pmr735a_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic5_ftsmps520, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic5_ftsmps520, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic5_hfsmps515, "vdd-s3"), - RPMH_VREG("ldo1", "ldo%s1", &pmic5_nldo, "vdd-l1-l2"), - RPMH_VREG("ldo2", "ldo%s2", &pmic5_nldo, "vdd-l1-l2"), - RPMH_VREG("ldo3", "ldo%s3", &pmic5_nldo, "vdd-l3"), - RPMH_VREG("ldo4", "ldo%s4", &pmic5_pldo_lv, "vdd-l4"), - RPMH_VREG("ldo5", "ldo%s5", &pmic5_nldo, "vdd-l5-l6"), - RPMH_VREG("ldo6", "ldo%s6", &pmic5_nldo, "vdd-l5-l6"), - RPMH_VREG("ldo7", "ldo%s7", &pmic5_pldo, "vdd-l7-bob"), - {} -}; - -static const struct rpmh_vreg_init_data pm660_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic4_ftsmps426, "vdd-s3"), - RPMH_VREG("smps4", "smp%s4", &pmic4_hfsmps3, "vdd-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic4_hfsmps3, "vdd-s5"), - RPMH_VREG("smps6", "smp%s6", &pmic4_hfsmps3, "vdd-s6"), - RPMH_VREG("ldo1", "ldo%s1", &pmic4_nldo, "vdd-l1-l6-l7"), - RPMH_VREG("ldo2", "ldo%s2", &pmic4_nldo, "vdd-l2-l3"), - RPMH_VREG("ldo3", "ldo%s3", &pmic4_nldo, "vdd-l2-l3"), - /* ldo4 is inaccessible on PM660 */ - RPMH_VREG("ldo5", "ldo%s5", &pmic4_nldo, "vdd-l5"), - RPMH_VREG("ldo6", "ldo%s6", &pmic4_nldo, "vdd-l1-l6-l7"), - RPMH_VREG("ldo7", "ldo%s7", &pmic4_nldo, "vdd-l1-l6-l7"), - RPMH_VREG("ldo8", "ldo%s8", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), - RPMH_VREG("ldo9", "ldo%s9", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), - RPMH_VREG("ldo10", "ldo%s10", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), - RPMH_VREG("ldo11", "ldo%s11", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), - RPMH_VREG("ldo12", "ldo%s12", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), - RPMH_VREG("ldo13", "ldo%s13", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), - RPMH_VREG("ldo14", "ldo%s14", &pmic4_pldo_lv, "vdd-l8-l9-l10-l11-l12-l13-l14"), - RPMH_VREG("ldo15", "ldo%s15", &pmic4_pldo, "vdd-l15-l16-l17-l18-l19"), - RPMH_VREG("ldo16", "ldo%s16", &pmic4_pldo, "vdd-l15-l16-l17-l18-l19"), - RPMH_VREG("ldo17", "ldo%s17", &pmic4_pldo, "vdd-l15-l16-l17-l18-l19"), - RPMH_VREG("ldo18", "ldo%s18", &pmic4_pldo, "vdd-l15-l16-l17-l18-l19"), - RPMH_VREG("ldo19", "ldo%s19", &pmic4_pldo, "vdd-l15-l16-l17-l18-l19"), - {} -}; - -static const struct rpmh_vreg_init_data pm660l_vreg_data[] = { - RPMH_VREG("smps1", "smp%s1", &pmic4_ftsmps426, "vdd-s1"), - RPMH_VREG("smps2", "smp%s2", &pmic4_ftsmps426, "vdd-s2"), - RPMH_VREG("smps3", "smp%s3", &pmic4_ftsmps426, "vdd-s3-s4"), - RPMH_VREG("smps5", "smp%s5", &pmic4_ftsmps426, "vdd-s5"), - RPMH_VREG("ldo1", "ldo%s1", &pmic4_nldo, "vdd-l1-l9-l10"), - RPMH_VREG("ldo2", "ldo%s2", &pmic4_pldo, "vdd-l2"), - RPMH_VREG("ldo3", "ldo%s3", &pmic4_pldo, "vdd-l3-l5-l7-l8"), - RPMH_VREG("ldo4", "ldo%s4", &pmic4_pldo, "vdd-l4-l6"), - RPMH_VREG("ldo5", "ldo%s5", &pmic4_pldo, "vdd-l3-l5-l7-l8"), - RPMH_VREG("ldo6", "ldo%s6", &pmic4_pldo, "vdd-l4-l6"), - RPMH_VREG("ldo7", "ldo%s7", &pmic4_pldo, "vdd-l3-l5-l7-l8"), - RPMH_VREG("ldo8", "ldo%s8", &pmic4_pldo, "vdd-l3-l5-l7-l8"), - RPMH_VREG("bob", "bob%s1", &pmic4_bob, "vdd-bob"), {} };
static int rpmh_regulator_probe(struct platform_device *pdev) @@ -1602,128 +433,16 @@ static int rpmh_regulator_probe(struct platform_device *pdev) return 0; }
static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { - { - .compatible = "qcom,pm8005-rpmh-regulators", - .data = pm8005_vreg_data, - }, - { - .compatible = "qcom,pm8009-rpmh-regulators", - .data = pm8009_vreg_data, - }, - { - .compatible = "qcom,pm8009-1-rpmh-regulators", - .data = pm8009_1_vreg_data, - }, - { - .compatible = "qcom,pm8010-rpmh-regulators", - .data = pm8010_vreg_data, - }, { .compatible = "qcom,pm8150-rpmh-regulators", .data = pm8150_vreg_data, }, { .compatible = "qcom,pm8150l-rpmh-regulators", .data = pm8150l_vreg_data, }, - { - .compatible = "qcom,pm8350-rpmh-regulators", - .data = pm8350_vreg_data, - }, - { - .compatible = "qcom,pm8350c-rpmh-regulators", - .data = pm8350c_vreg_data, - }, - { - .compatible = "qcom,pm8450-rpmh-regulators", - .data = pm8450_vreg_data, - }, - { - .compatible = "qcom,pm8550-rpmh-regulators", - .data = pm8550_vreg_data, - }, - { - .compatible = "qcom,pm8550ve-rpmh-regulators", - .data = pm8550ve_vreg_data, - }, - { - .compatible = "qcom,pm8550vs-rpmh-regulators", - .data = pm8550vs_vreg_data, - }, - { - .compatible = "qcom,pm8998-rpmh-regulators", - .data = pm8998_vreg_data, - }, - { - .compatible = "qcom,pmg1110-rpmh-regulators", - .data = pmg1110_vreg_data, - }, - { - .compatible = "qcom,pmi8998-rpmh-regulators", - .data = pmi8998_vreg_data, - }, - { - .compatible = "qcom,pm6150-rpmh-regulators", - .data = pm6150_vreg_data, - }, - { - .compatible = "qcom,pm6150l-rpmh-regulators", - .data = pm6150l_vreg_data, - }, - { - .compatible = "qcom,pm6350-rpmh-regulators", - .data = pm6350_vreg_data, - }, - { - .compatible = "qcom,pmc8180-rpmh-regulators", - .data = pm8150_vreg_data, - }, - { - .compatible = "qcom,pmc8180c-rpmh-regulators", - .data = pm8150l_vreg_data, - }, - { - .compatible = "qcom,pmc8380-rpmh-regulators", - .data = pmc8380_vreg_data, - }, - { - .compatible = "qcom,pmm8155au-rpmh-regulators", - .data = pmm8155au_vreg_data, - }, - { - .compatible = "qcom,pmm8654au-rpmh-regulators", - .data = pmm8654au_vreg_data, - }, - { - .compatible = "qcom,pmx55-rpmh-regulators", - .data = pmx55_vreg_data, - }, - { - .compatible = "qcom,pmx65-rpmh-regulators", - .data = pmx65_vreg_data, - }, - { - .compatible = "qcom,pmx75-rpmh-regulators", - .data = pmx75_vreg_data, - }, - { - .compatible = "qcom,pm7325-rpmh-regulators", - .data = pm7325_vreg_data, - }, - { - .compatible = "qcom,pmr735a-rpmh-regulators", - .data = pmr735a_vreg_data, - }, - { - .compatible = "qcom,pm660-rpmh-regulators", - .data = pm660_vreg_data, - }, - { - .compatible = "qcom,pm660l-rpmh-regulators", - .data = pm660l_vreg_data, - }, {} }; MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table);

Port over the regulator ops to U-Boot's regulator API. Add back the pmic5 mode map using U-Boot dm_regulator_mode API and adjust the pmic5_pldo and pmic5_pldo_lv definitions. No functional changes.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/power/regulator/qcom-rpmh-regulator.c | 158 ++++++++++++++------------ 1 file changed, 87 insertions(+), 71 deletions(-)
diff --git a/drivers/power/regulator/qcom-rpmh-regulator.c b/drivers/power/regulator/qcom-rpmh-regulator.c index 2a8e8f9ac444..b716b380c148 100644 --- a/drivers/power/regulator/qcom-rpmh-regulator.c +++ b/drivers/power/regulator/qcom-rpmh-regulator.c @@ -192,87 +192,96 @@ struct rpmh_vreg_init_data { * * Return: 0 on success, errno on failure */ static int rpmh_regulator_send_request(struct rpmh_vreg *vreg, - struct tcs_cmd *cmd, bool wait_for_ack) + const struct tcs_cmd *cmd, bool wait_for_ack) { int ret;
if (wait_for_ack || vreg->always_wait_for_ack) - ret = rpmh_write(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd, 1); + ret = rpmh_write(vreg->dev->parent, RPMH_ACTIVE_ONLY_STATE, cmd, 1); else - ret = rpmh_write_async(vreg->dev, RPMH_ACTIVE_ONLY_STATE, cmd, - 1); + ret = rpmh_write_async(vreg->dev->parent, RPMH_ACTIVE_ONLY_STATE, cmd, 1);
return ret; }
-static int _rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev, - unsigned int selector, bool wait_for_ack) +static int _rpmh_regulator_vrm_set_value(struct udevice *rdev, + int uv, bool wait_for_ack) { - struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + struct rpmh_vreg *vreg = dev_get_priv(rdev); struct tcs_cmd cmd = { .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_VOLTAGE, }; int ret; + unsigned int selector;
- /* VRM voltage control register is set with voltage in millivolts. */ - cmd.data = DIV_ROUND_UP(regulator_list_voltage_linear_range(rdev, - selector), 1000); + selector = (uv - vreg->hw_data->voltage_range.min) / vreg->hw_data->voltage_range.step; + cmd.data = DIV_ROUND_UP(vreg->hw_data->voltage_range.min + + selector * vreg->hw_data->voltage_range.step, 1000);
ret = rpmh_regulator_send_request(vreg, &cmd, wait_for_ack); if (!ret) - vreg->voltage_selector = selector; + vreg->uv = cmd.data * 1000;
return ret; }
-static int rpmh_regulator_vrm_set_voltage_sel(struct regulator_dev *rdev, - unsigned int selector) +static int rpmh_regulator_vrm_set_value(struct udevice *rdev, + int uv) { - struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + struct rpmh_vreg *vreg = dev_get_priv(rdev); + + debug("%s: set_value %d (current %d)\n", rdev->name, uv, vreg->uv);
if (vreg->enabled == -EINVAL) { /* * Cache the voltage and send it later when the regulator is * enabled or disabled. */ - vreg->voltage_selector = selector; + vreg->uv = uv; return 0; }
- return _rpmh_regulator_vrm_set_voltage_sel(rdev, selector, - selector > vreg->voltage_selector); + return _rpmh_regulator_vrm_set_value(rdev, uv, + uv > vreg->uv); }
-static int rpmh_regulator_vrm_get_voltage_sel(struct regulator_dev *rdev) +static int rpmh_regulator_vrm_get_value(struct udevice *rdev) { - struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + struct rpmh_vreg *vreg = dev_get_priv(rdev);
- return vreg->voltage_selector; + debug("%s: get_value %d\n", rdev->name, vreg->uv); + + return vreg->uv; }
-static int rpmh_regulator_is_enabled(struct regulator_dev *rdev) +static int rpmh_regulator_is_enabled(struct udevice *rdev) { - struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + struct rpmh_vreg *vreg = dev_get_priv(rdev);
- return vreg->enabled; + debug("%s: is_enabled %d\n", rdev->name, vreg->enabled); + + return vreg->enabled > 0; }
-static int rpmh_regulator_set_enable_state(struct regulator_dev *rdev, - bool enable) +static int rpmh_regulator_set_enable_state(struct udevice *rdev, + bool enable) { - struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + struct rpmh_vreg *vreg = dev_get_priv(rdev); struct tcs_cmd cmd = { .addr = vreg->addr + RPMH_REGULATOR_REG_ENABLE, .data = enable, }; int ret;
+ debug("%s: set_enable %d (current %d)\n", rdev->name, enable, + vreg->enabled); + if (vreg->enabled == -EINVAL && - vreg->voltage_selector != -ENOTRECOVERABLE) { - ret = _rpmh_regulator_vrm_set_voltage_sel(rdev, - vreg->voltage_selector, true); + vreg->uv != -ENOTRECOVERABLE) { + ret = _rpmh_regulator_vrm_set_value(rdev, + vreg->uv, true); if (ret < 0) return ret; }
@@ -282,47 +291,46 @@ static int rpmh_regulator_set_enable_state(struct regulator_dev *rdev,
return ret; }
-static int rpmh_regulator_enable(struct regulator_dev *rdev) -{ - return rpmh_regulator_set_enable_state(rdev, true); -} - -static int rpmh_regulator_disable(struct regulator_dev *rdev) -{ - return rpmh_regulator_set_enable_state(rdev, false); -} - static int rpmh_regulator_vrm_set_mode_bypass(struct rpmh_vreg *vreg, - unsigned int mode, bool bypassed) + unsigned int mode, bool bypassed) { struct tcs_cmd cmd = { .addr = vreg->addr + RPMH_REGULATOR_REG_VRM_MODE, }; - int pmic_mode; + struct dm_regulator_mode *pmic_mode; + int i;
- if (mode > REGULATOR_MODE_STANDBY) + if (mode > REGULATOR_MODE_HPM) return -EINVAL;
- pmic_mode = vreg->hw_data->pmic_mode_map[mode]; - if (pmic_mode < 0) - return pmic_mode; + for (i = 0; i < vreg->hw_data->n_modes; i++) { + pmic_mode = &vreg->hw_data->pmic_mode_map[i]; + if (pmic_mode->id == mode) + break; + } + if (pmic_mode->id != mode) { + printf("Invalid mode %d\n", mode); + return -EINVAL; + }
if (bypassed) cmd.data = PMIC4_BOB_MODE_PASS; else - cmd.data = pmic_mode; + cmd.data = pmic_mode->id;
return rpmh_regulator_send_request(vreg, &cmd, true); }
-static int rpmh_regulator_vrm_set_mode(struct regulator_dev *rdev, - unsigned int mode) +static int rpmh_regulator_vrm_set_mode(struct udevice *rdev, + int mode) { - struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + struct rpmh_vreg *vreg = dev_get_priv(rdev); int ret;
+ debug("%s: set_mode %d (current %d)\n", rdev->name, mode, vreg->mode); + if (mode == vreg->mode) return 0;
ret = rpmh_regulator_vrm_set_mode_bypass(vreg, mode, vreg->bypassed); @@ -331,51 +339,59 @@ static int rpmh_regulator_vrm_set_mode(struct regulator_dev *rdev,
return ret; }
-static unsigned int rpmh_regulator_vrm_get_mode(struct regulator_dev *rdev) +static int rpmh_regulator_vrm_get_mode(struct udevice *rdev) { - struct rpmh_vreg *vreg = rdev_get_drvdata(rdev); + struct rpmh_vreg *vreg = dev_get_priv(rdev); + + debug("%s: get_mode %d\n", rdev->name, vreg->mode);
return vreg->mode; } +static const struct dm_regulator_ops rpmh_regulator_vrm_drms_ops = { + .get_value = rpmh_regulator_vrm_get_value, + .set_value = rpmh_regulator_vrm_set_value, + .set_enable = rpmh_regulator_set_enable_state, + .get_enable = rpmh_regulator_is_enabled, + .set_mode = rpmh_regulator_vrm_set_mode, + .get_mode = rpmh_regulator_vrm_get_mode, +};
-static const struct regulator_ops rpmh_regulator_vrm_drms_ops = { - .enable = rpmh_regulator_enable, - .disable = rpmh_regulator_disable, - .is_enabled = rpmh_regulator_is_enabled, - .set_voltage_sel = rpmh_regulator_vrm_set_voltage_sel, - .get_voltage_sel = rpmh_regulator_vrm_get_voltage_sel, - .list_voltage = regulator_list_voltage_linear_range, - .set_mode = rpmh_regulator_vrm_set_mode, - .get_mode = rpmh_regulator_vrm_get_mode, - .get_optimum_mode = rpmh_regulator_vrm_get_optimum_mode, +static struct dm_regulator_mode pmic_mode_map_pmic5_ldo[] = { + { + .id = REGULATOR_MODE_RETENTION, + .register_value = PMIC5_LDO_MODE_RETENTION, + .name = "PMIC5_LDO_MODE_RETENTION" + }, { + .id = REGULATOR_MODE_LPM, + .register_value = PMIC5_LDO_MODE_LPM, + .name = "PMIC5_LDO_MODE_LPM" + }, { + .id = REGULATOR_MODE_HPM, + .register_value = PMIC5_LDO_MODE_HPM, + .name = "PMIC5_LDO_MODE_HPM" + }, };
static const struct rpmh_vreg_hw_data pmic5_pldo = { .regulator_type = VRM, .ops = &rpmh_regulator_vrm_drms_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(1504000, 0, 255, 8000), - }, - .n_linear_ranges = 1, + .voltage_range = REGULATOR_LINEAR_RANGE(1504000, 0, 255, 8000), .n_voltages = 256, .hpm_min_load_uA = 10000, .pmic_mode_map = pmic_mode_map_pmic5_ldo, - .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, + .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_ldo), };
static const struct rpmh_vreg_hw_data pmic5_pldo_lv = { .regulator_type = VRM, .ops = &rpmh_regulator_vrm_drms_ops, - .voltage_ranges = (struct linear_range[]) { - REGULATOR_LINEAR_RANGE(1504000, 0, 62, 8000), - }, - .n_linear_ranges = 1, + .voltage_range = REGULATOR_LINEAR_RANGE(1504000, 0, 62, 8000), .n_voltages = 63, .hpm_min_load_uA = 10000, .pmic_mode_map = pmic_mode_map_pmic5_ldo, - .of_map_mode = rpmh_regulator_pmic4_ldo_of_map_mode, + .n_modes = ARRAY_SIZE(pmic_mode_map_pmic5_ldo), };
#define RPMH_VREG(_name, _resource_name, _hw_data, _supply_name) \ { \

Refactor initialization to use U-Boot's driver model and API.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/power/regulator/qcom-rpmh-regulator.c | 136 +++++++++++++++++++------- 1 file changed, 102 insertions(+), 34 deletions(-)
diff --git a/drivers/power/regulator/qcom-rpmh-regulator.c b/drivers/power/regulator/qcom-rpmh-regulator.c index b716b380c148..06fd3f31956f 100644 --- a/drivers/power/regulator/qcom-rpmh-regulator.c +++ b/drivers/power/regulator/qcom-rpmh-regulator.c @@ -411,66 +411,134 @@ static const struct rpmh_vreg_init_data pm8150l_vreg_data[] = { RPMH_VREG("ldo11", "ldo%s11", &pmic5_pldo, "vdd-l7-l11"), {} };
-static int rpmh_regulator_probe(struct platform_device *pdev) +/* probe an individual regulator */ +static int rpmh_regulator_probe(struct udevice *dev) { - struct device *dev = &pdev->dev; - const struct rpmh_vreg_init_data *vreg_data; - struct device_node *node; - struct rpmh_vreg *vreg; - const char *pmic_id; - int ret; + const struct rpmh_vreg_init_data *init_data; + struct rpmh_vreg *priv; + struct dm_regulator_uclass_plat *plat_data;
- vreg_data = of_device_get_match_data(dev); - if (!vreg_data) + init_data = (const struct rpmh_vreg_init_data *)dev_get_driver_data(dev); + priv = dev_get_priv(dev); + plat_data = dev_get_uclass_plat(dev); + + priv->dev = dev; + priv->addr = cmd_db_read_addr(dev->name); + if (!priv->addr) { + dev_err(dev, "Failed to read RPMh address for %s\n", dev->name); return -ENODEV; - - ret = of_property_read_string(dev->of_node, "qcom,pmic-id", &pmic_id); - if (ret < 0) { - dev_err(dev, "qcom,pmic-id missing in DT node\n"); - return ret; }
- for_each_available_child_of_node(dev->of_node, node) { - vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL); - if (!vreg) { - of_node_put(node); + priv->hw_data = init_data->hw_data; + priv->enabled = -EINVAL; + priv->uv = -ENOTRECOVERABLE; + if (ofnode_read_u32(dev_ofnode(dev), "regulator-initial-mode", &priv->mode)) + priv->mode = -EINVAL; + + plat_data->mode = priv->hw_data->pmic_mode_map; + plat_data->mode_count = priv->hw_data->n_modes; + + return 0; +} + +/* for non-drm, xob, or bypass regulators add additional driver definitions */ +U_BOOT_DRIVER(rpmh_regulator_drm) = { + .name = "rpmh_regulator_drm", + .id = UCLASS_REGULATOR, + .probe = rpmh_regulator_probe, + .priv_auto = sizeof(struct rpmh_vreg), + .ops = &rpmh_regulator_vrm_drms_ops, +}; + +/* This driver intentionally only supports a subset of the available regulators. + * This function checks to see if a given regulator node in DT matches a regulator + * defined in the driver. + */ +static const struct rpmh_vreg_init_data * +vreg_get_init_data(const struct rpmh_vreg_init_data *init_data, ofnode node) +{ + const struct rpmh_vreg_init_data *data; + + for (data = init_data; data->name; data++) { + if (!strcmp(data->name, ofnode_get_name(node))) + return data; + } + + return NULL; +} + +static int rpmh_regulators_bind(struct udevice *dev) +{ + const struct rpmh_vreg_init_data *init_data, *data; + const char *pmic_id; + char *name; + struct driver *drv; + ofnode node; + int ret; + size_t namelen; + + init_data = (const struct rpmh_vreg_init_data *)dev_get_driver_data(dev); + if (!init_data) { + dev_err(dev, "No RPMh regulator init data\n"); + return -ENODEV; + } + + pmic_id = ofnode_read_string(dev_ofnode(dev), "qcom,pmic-id"); + if (!pmic_id) { + dev_err(dev, "No PMIC ID\n"); + return -ENODEV; + } + + drv = lists_driver_lookup_name("rpmh_regulator_drm"); + + ofnode_for_each_subnode(node, dev_ofnode(dev)) { + data = vreg_get_init_data(init_data, node); + if (!data) + continue; + + /* %s is replaced with pmic_id, so subtract 2, then add 1 for the null terminator */ + namelen = strlen(data->resource_name) + strlen(pmic_id) - 1; + name = devm_kzalloc(dev, namelen, GFP_KERNEL); + ret = snprintf(name, namelen, data->resource_name, pmic_id); + if (ret < 0 || ret >= namelen) { + dev_err(dev, "Failed to create RPMh regulator name\n"); return -ENOMEM; }
- ret = rpmh_regulator_init_vreg(vreg, dev, node, pmic_id, - vreg_data); + ret = device_bind_with_driver_data(dev, drv, name, (ulong)data, + node, NULL); if (ret < 0) { - of_node_put(node); + dev_err(dev, "Failed to bind RPMh regulator %s: %d\n", name, ret); return ret; } }
return 0; }
-static const struct of_device_id __maybe_unused rpmh_regulator_match_table[] = { +static const struct udevice_id rpmh_regulator_ids[] = { { .compatible = "qcom,pm8150-rpmh-regulators", - .data = pm8150_vreg_data, + .data = (ulong)pm8150_vreg_data, }, { .compatible = "qcom,pm8150l-rpmh-regulators", - .data = pm8150l_vreg_data, + .data = (ulong)pm8150l_vreg_data, }, - {} + { /* sentinal */ }, }; -MODULE_DEVICE_TABLE(of, rpmh_regulator_match_table);
-static struct platform_driver rpmh_regulator_driver = { - .driver = { - .name = "qcom-rpmh-regulator", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .of_match_table = of_match_ptr(rpmh_regulator_match_table), - }, - .probe = rpmh_regulator_probe, +/* Driver for a 'bank' of regulators. This creates devices for each + * individual regulator + */ +U_BOOT_DRIVER(rpmh_regulators) = { + .name = "rpmh_regulators", + .id = UCLASS_MISC, + .bind = rpmh_regulators_bind, + .of_match = rpmh_regulator_ids, + .ops = &rpmh_regulator_vrm_drms_ops, }; -module_platform_driver(rpmh_regulator_driver);
MODULE_DESCRIPTION("Qualcomm RPMh regulator driver"); MODULE_LICENSE("GPL v2");

Add Kconfig and Makefile entries for this driver now that it can build for U-Boot.
Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- drivers/power/regulator/Kconfig | 8 ++++++++ drivers/power/regulator/Makefile | 1 + 2 files changed, 9 insertions(+)
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig index 102ec7bc5f89..bc061c20d75e 100644 --- a/drivers/power/regulator/Kconfig +++ b/drivers/power/regulator/Kconfig @@ -215,8 +215,16 @@ config DM_REGULATOR_GPIO This config enables implementation of driver-model regulator uclass features for gpio regulators. The driver implements get/set for voltage value.
+config DM_REGULATOR_QCOM_RPMH + bool "Enable driver model for Qualcomm RPMh regulator" + depends on DM_REGULATOR && QCOM_RPMH + ---help--- + Enable support for the Qualcomm RPMh regulator. The driver + implements get/set api for a limited set of regulators used + by u-boot. + config SPL_DM_REGULATOR_GPIO bool "Enable Driver Model for GPIO REGULATOR in SPL" depends on DM_REGULATOR_GPIO && SPL_GPIO select SPL_DM_REGULATOR_COMMON diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile index f79932d83307..56a527612b74 100644 --- a/drivers/power/regulator/Makefile +++ b/drivers/power/regulator/Makefile @@ -20,8 +20,9 @@ obj-$(CONFIG_$(SPL_)REGULATOR_PWM) += pwm_regulator.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_FAN53555) += fan53555.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_COMMON) += regulator_common.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_FIXED) += fixed.o obj-$(CONFIG_$(SPL_)DM_REGULATOR_GPIO) += gpio-regulator.o +obj-$(CONFIG_DM_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o obj-$(CONFIG_$(SPL_TPL_)REGULATOR_RK8XX) += rk8xx.o obj-$(CONFIG_DM_REGULATOR_S2MPS11) += s2mps11_regulator.o obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o

Enable RPMh, cmd-db, and RPMh regulators. Additionally enable CMD_REGULATOR for debugging.
Acked-by: Sumit Garg sumit.garg@linaro.org Signed-off-by: Caleb Connolly caleb.connolly@linaro.org --- configs/qcom_defconfig | 5 +++++ 1 file changed, 5 insertions(+)
diff --git a/configs/qcom_defconfig b/configs/qcom_defconfig index ac5ffe772ade..1cbad9db72eb 100644 --- a/configs/qcom_defconfig +++ b/configs/qcom_defconfig @@ -35,8 +35,9 @@ CONFIG_CMD_UFS=y CONFIG_CMD_USB=y CONFIG_CMD_CAT=y CONFIG_CMD_BMP=y CONFIG_CMD_EFIDEBUG=y +CONFIG_CMD_REGULATOR=y CONFIG_CMD_LOG=y CONFIG_OF_LIVE=y CONFIG_BUTTON_QCOM_PMIC=y CONFIG_CLK=y @@ -90,11 +91,15 @@ CONFIG_PINCTRL_QCOM_SM8650=y CONFIG_DM_PMIC=y CONFIG_PMIC_QCOM=y CONFIG_DM_REGULATOR=y CONFIG_DM_REGULATOR_FIXED=y +CONFIG_DM_REGULATOR_QCOM_RPMH=y CONFIG_SCSI=y CONFIG_MSM_SERIAL=y CONFIG_MSM_GENI_SERIAL=y +CONFIG_SOC_QCOM=y +CONFIG_QCOM_COMMAND_DB=y +CONFIG_QCOM_RPMH=y CONFIG_SPMI_MSM=y CONFIG_SYSINFO=y CONFIG_SYSINFO_SMBIOS=y CONFIG_USB=y
participants (2)
-
Caleb Connolly
-
Simon Glass