From 7ad18bb5c2c4bd50c8b83d375ee19be992022cf3 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Thu, 27 Feb 2020 18:26:15 +0530 Subject: soc: qcom: cmd-db: Fix compilation error when CMD_DB is disabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CONFIG_QCOM_COMMAND_DB=n the following compilation errors will be seen. Fix this by including the appropriate linux headers. ./include/soc/qcom/cmd-db.h: In function ‘cmd_db_read_aux_data’: ./include/soc/qcom/cmd-db.h: error: implicit declaration of function ‘ERR_PTR’; Reviewed-by: Stephen Boyd Signed-off-by: Sibi Sankar Link: https://lore.kernel.org/r/20200227125615.4727-1-sibis@codeaurora.org Signed-off-by: Bjorn Andersson --- include/soc/qcom/cmd-db.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/soc/qcom/cmd-db.h b/include/soc/qcom/cmd-db.h index af9722223925..c8bb56e6852a 100644 --- a/include/soc/qcom/cmd-db.h +++ b/include/soc/qcom/cmd-db.h @@ -4,6 +4,7 @@ #ifndef __QCOM_COMMAND_DB_H__ #define __QCOM_COMMAND_DB_H__ +#include enum cmd_db_hw_type { CMD_DB_HW_INVALID = 0, -- cgit v1.2.3-59-g8ed1b From 27a344139c186889d742764d3c2a62b395949cef Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Thu, 19 Mar 2020 12:14:18 +0000 Subject: soc: qcom: socinfo: add missing soc_id sysfs entry Looks like SoC ID is not exported to sysfs for some reason. This patch adds it! This is mostly used by userspace libraries like Snapdragon Neural Processing Engine (SNPE) SDK for checking supported SoC info. Fixes: efb448d0a3fc ("soc: qcom: Add socinfo driver") Reviewed-by: Stephen Boyd Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20200319121418.5180-1-srinivas.kandagatla@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index ebb49aee179b..08a4b8ae1764 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -430,6 +430,8 @@ static int qcom_socinfo_probe(struct platform_device *pdev) qs->attr.family = "Snapdragon"; qs->attr.machine = socinfo_machine(&pdev->dev, le32_to_cpu(info->id)); + qs->attr.soc_id = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%u", + le32_to_cpu(info->id)); qs->attr.revision = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%u.%u", SOCINFO_MAJOR(le32_to_cpu(info->ver)), SOCINFO_MINOR(le32_to_cpu(info->ver))); -- cgit v1.2.3-59-g8ed1b From d6815c5c43d4f9d18e557d27fd27ae8d9cfd450c Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Mon, 9 Mar 2020 11:57:04 -0700 Subject: soc: qcom: cmd-db: Add debugfs dumping file It's useful for kernel devs to understand what resources and data is stored inside command db. Add a file in debugufs called 'cmd-db' to dump the memory contents and strings for resources along with their addresses. E.g. Command DB DUMP Slave ARC (v16.0) ------------------------- 0x00030000: cx.lvl [00 00 10 00 40 00 80 00 c0 00 00 01 80 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00] 0x00030004: cx.tmr 0x00030010: mx.lvl [00 00 10 00 00 01 80 01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00] 0x00030014: mx.tmr Cc: Lina Iyer Cc: Maulik Shah Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20200309185704.2491-1-swboyd@chromium.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/cmd-db.c | 78 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index f6c3d17b05c7..8b2b7357b6da 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. */ +#include #include #include #include -#include #include #include +#include #include #include @@ -236,6 +237,77 @@ enum cmd_db_hw_type cmd_db_read_slave_id(const char *id) } EXPORT_SYMBOL(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 (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%08x: %*pEp", le32_to_cpu(ent->addr), + 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; @@ -259,12 +331,14 @@ static int cmd_db_dev_probe(struct platform_device *pdev) return -EINVAL; } + debugfs_create_file("cmd-db", 0400, NULL, NULL, &cmd_db_debugfs_ops); + return 0; } static const struct of_device_id cmd_db_match_table[] = { { .compatible = "qcom,cmd-db" }, - { }, + { } }; static struct platform_driver cmd_db_dev_driver = { -- cgit v1.2.3-59-g8ed1b From 1790c97125dd29d58d2404192c542966b0056309 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 5 Apr 2020 18:08:13 +0200 Subject: soc: qcom: smp2p: Delete an error message in qcom_smp2p_probe() The function platform_get_irq() can log an error already. Thus omit a redundant message for the exception handling in the calling function. This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Link: https://lore.kernel.org/r/eb92fcfb-6181-1f9d-2601-61e5231bd892@web.de Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/smp2p.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index c7300d54e444..07183d731d74 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -474,10 +474,8 @@ static int qcom_smp2p_probe(struct platform_device *pdev) goto report_read_failure; irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "unable to acquire smp2p interrupt\n"); + if (irq < 0) return irq; - } smp2p->mbox_client.dev = &pdev->dev; smp2p->mbox_client.knows_txdone = true; -- cgit v1.2.3-59-g8ed1b From bb7000677a1b287206c8d4327c62442fa3050a8f Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Sun, 12 Apr 2020 20:20:00 +0530 Subject: soc: qcom: rpmh: Update dirty flag only when data changes Currently rpmh ctrlr dirty flag is set for all cases regardless of data is really changed or not. Add changes to update dirty flag when data is changed to newer values. Update dirty flag everytime when data in batch cache is updated since rpmh_flush() may get invoked from any CPU instead of only last CPU going to low power mode. Also move dirty flag updates to happen from within cache_lock and remove unnecessary INIT_LIST_HEAD() call and a default case from switch. Fixes: 600513dfeef3 ("drivers: qcom: rpmh: cache sleep/wake state requests") Signed-off-by: Maulik Shah Reviewed-by: Srinivas Rao L Reviewed-by: Evan Green Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1586703004-13674-3-git-send-email-mkshah@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index eb0ded059d2e..03630aeb4fef 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -119,6 +119,7 @@ static struct cache_req *cache_rpm_request(struct rpmh_ctrlr *ctrlr, { 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); @@ -133,26 +134,27 @@ static struct cache_req *cache_rpm_request(struct rpmh_ctrlr *ctrlr, req->addr = cmd->addr; req->sleep_val = req->wake_val = UINT_MAX; - INIT_LIST_HEAD(&req->list); 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: - if (req->sleep_val != UINT_MAX) - req->wake_val = cmd->data; - break; case RPMH_WAKE_ONLY_STATE: req->wake_val = cmd->data; break; case RPMH_SLEEP_STATE: req->sleep_val = cmd->data; break; - default: - break; } - ctrlr->dirty = true; + 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); @@ -287,6 +289,7 @@ static void cache_batch(struct rpmh_ctrlr *ctrlr, struct batch_cache_req *req) 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); } @@ -323,6 +326,7 @@ static void invalidate_batch(struct rpmh_ctrlr *ctrlr) 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); } @@ -507,7 +511,6 @@ int rpmh_invalidate(const struct device *dev) int ret; invalidate_batch(ctrlr); - ctrlr->dirty = true; do { ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); -- cgit v1.2.3-59-g8ed1b From f5ac95f9ca2f439179a5baf48e1c0f22f83d936e Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Sun, 12 Apr 2020 20:20:01 +0530 Subject: soc: qcom: rpmh: Invalidate SLEEP and WAKE TCSes before flushing new data TCSes have previously programmed data when rpmh_flush() is called. This can cause old data to trigger along with newly flushed. Fix this by cleaning SLEEP and WAKE TCSes before new data is flushed. With this there is no need to invoke rpmh_rsc_invalidate() call from rpmh_invalidate(). Simplify rpmh_invalidate() by moving invalidate_batch() inside. Fixes: 600513dfeef3 ("drivers: qcom: rpmh: cache sleep/wake state requests") Signed-off-by: Maulik Shah Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1586703004-13674-4-git-send-email-mkshah@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh.c | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 03630aeb4fef..a75f3df97742 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -317,19 +317,6 @@ static int flush_batch(struct rpmh_ctrlr *ctrlr) return ret; } -static void invalidate_batch(struct rpmh_ctrlr *ctrlr) -{ - 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); -} - /** * rpmh_write_batch: Write multiple sets of RPMH commands and wait for the * batch to finish. @@ -467,6 +454,13 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) return 0; } + /* Invalidate the TCSes first to avoid stale data */ + do { + ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); + } while (ret == -EAGAIN); + if (ret) + return ret; + /* First flush the cached batch requests */ ret = flush_batch(ctrlr); if (ret) @@ -498,24 +492,25 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) } /** - * rpmh_invalidate: Invalidate all sleep and active sets - * sets. + * rpmh_invalidate: Invalidate sleep and wake sets in batch_cache * * @dev: The device making the request * - * Invalidate the sleep and active values in the TCS blocks. + * Invalidate the sleep and wake values in batch_cache. */ int rpmh_invalidate(const struct device *dev) { struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); - int ret; - - invalidate_batch(ctrlr); + struct batch_cache_req *req, *tmp; + unsigned long flags; - do { - ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); - } while (ret == -EAGAIN); + 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); - return ret; + return 0; } EXPORT_SYMBOL(rpmh_invalidate); -- cgit v1.2.3-59-g8ed1b From 985427f997b6a31155cce841eb395d43c64771c5 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Sun, 12 Apr 2020 20:20:02 +0530 Subject: soc: qcom: rpmh: Invoke rpmh_flush() for dirty caches Add changes to invoke rpmh flush() from CPU PM notification. This is done when the last the cpu is entering deep CPU idle states and controller is not busy. Controllers that have 'HW solver' mode like display RSC do not need to register for CPU PM notification. They may be in autonomous mode executing low power mode and do not require rpmh_flush() to happen from CPU PM notification. Signed-off-by: Maulik Shah Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1586703004-13674-5-git-send-email-mkshah@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 25 +++++--- drivers/soc/qcom/rpmh-rsc.c | 122 +++++++++++++++++++++++++++++++++++---- drivers/soc/qcom/rpmh.c | 29 ++++------ 3 files changed, 139 insertions(+), 37 deletions(-) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 6eec32b97f83..e9a90cb7773e 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -84,23 +84,32 @@ struct rpmh_ctrlr { * struct rsc_drv: the Direct Resource Voter (DRV) of the * Resource State Coordinator controller (RSC) * - * @name: controller identifier - * @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 - * @tcs: TCS groups - * @tcs_in_use: s/w state of the TCS - * @lock: synchronize state of the controller - * @client: handle to the DRV's client. + * @name: Controller identifier + * @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_entered_pm: CPU mask for cpus in idle power collapse + * Used when solver mode is not present + * @tcs: TCS groups + * @tcs_in_use: S/W state of the TCS + * @lock: Synchronize state of the controller + * @pm_lock: Synchronize during PM notifications + * Used when solver mode is not present + * @client: Handle to the DRV's client. */ struct rsc_drv { const char *name; void __iomem *tcs_base; int id; int num_tcs; + struct notifier_block rsc_pm; + struct cpumask cpus_entered_pm; struct tcs_group tcs[TCS_TYPE_NR]; DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR); spinlock_t lock; + spinlock_t pm_lock; struct rpmh_ctrlr client; }; diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index b71822131f59..892c82b7e3fb 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -6,6 +6,7 @@ #define pr_fmt(fmt) "%s " fmt, KBUILD_MODNAME #include +#include #include #include #include @@ -30,7 +31,12 @@ #define RSC_DRV_TCS_OFFSET 672 #define RSC_DRV_CMD_OFFSET 20 -/* DRV Configuration Information Register */ +/* DRV HW Solver Configuration Information Register */ +#define DRV_SOLVER_CONFIG 0x04 +#define DRV_HW_SOLVER_MASK 1 +#define DRV_HW_SOLVER_SHIFT 24 + +/* DRV TCS Configuration Information Register */ #define DRV_PRNT_CHLD_CONFIG 0x0C #define DRV_NUM_TCS_MASK 0x3F #define DRV_NUM_TCS_SHIFT 6 @@ -521,8 +527,85 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) return tcs_ctrl_write(drv, msg); } +/** + * 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. + * + * Return: + * * False - AMCs are idle + * * True - AMCs are busy + */ +static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) +{ + int m; + struct tcs_group *tcs = get_tcs_of_type(drv, ACTIVE_TCS); + + /* + * 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. + * + * Since this is called from the last cpu, need not take drv or tcs + * lock before checking tcs_is_free(). + */ + if (!tcs->num_tcs) + tcs = get_tcs_of_type(drv, WAKE_TCS); + + for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) { + if (!tcs_is_free(drv, m)) + return true; + } + + return false; +} + +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; + + spin_lock(&drv->pm_lock); + + switch (action) { + case CPU_PM_ENTER: + cpumask_set_cpu(smp_processor_id(), &drv->cpus_entered_pm); + + if (!cpumask_equal(&drv->cpus_entered_pm, cpu_online_mask)) + goto exit; + break; + case CPU_PM_ENTER_FAILED: + case CPU_PM_EXIT: + cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm); + goto exit; + } + + ret = rpmh_rsc_ctrlr_is_busy(drv); + if (ret) { + ret = NOTIFY_BAD; + goto exit; + } + + ret = rpmh_flush(&drv->client); + if (ret) + ret = NOTIFY_BAD; + else + ret = NOTIFY_OK; + +exit: + spin_unlock(&drv->pm_lock); + return ret; +} + static int rpmh_probe_tcs_config(struct platform_device *pdev, - struct rsc_drv *drv) + struct rsc_drv *drv, void __iomem *base) { struct tcs_type_config { u32 type; @@ -532,15 +615,6 @@ static int rpmh_probe_tcs_config(struct platform_device *pdev, u32 config, max_tcs, ncpt, offset; int i, ret, n, st = 0; struct tcs_group *tcs; - struct resource *res; - void __iomem *base; - char drv_id[10] = {0}; - - snprintf(drv_id, ARRAY_SIZE(drv_id), "drv-%d", drv->id); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, drv_id); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); ret = of_property_read_u32(dn, "qcom,tcs-offset", &offset); if (ret) @@ -620,7 +694,11 @@ static int rpmh_rsc_probe(struct platform_device *pdev) { struct device_node *dn = pdev->dev.of_node; struct rsc_drv *drv; + struct resource *res; + char drv_id[10] = {0}; int ret, irq; + u32 solver_config; + void __iomem *base; /* * Even though RPMh doesn't directly use cmd-db, all of its children @@ -646,7 +724,13 @@ static int rpmh_rsc_probe(struct platform_device *pdev) if (!drv->name) drv->name = dev_name(&pdev->dev); - ret = rpmh_probe_tcs_config(pdev, drv); + snprintf(drv_id, ARRAY_SIZE(drv_id), "drv-%d", drv->id); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, drv_id); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + ret = rpmh_probe_tcs_config(pdev, drv, base); if (ret) return ret; @@ -663,6 +747,20 @@ static int rpmh_rsc_probe(struct platform_device *pdev) if (ret) return ret; + /* + * CPU PM 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(base + DRV_SOLVER_CONFIG); + solver_config &= DRV_HW_SOLVER_MASK << DRV_HW_SOLVER_SHIFT; + solver_config = solver_config >> DRV_HW_SOLVER_SHIFT; + if (!solver_config) { + drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback; + spin_lock_init(&drv->pm_lock); + cpu_pm_register_notifier(&drv->rsc_pm); + } + /* Enable the active TCS to send requests immediately */ write_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, drv->tcs[ACTIVE_TCS].mask); diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index a75f3df97742..be5e89d73526 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -297,12 +298,10 @@ static int flush_batch(struct rpmh_ctrlr *ctrlr) { struct batch_cache_req *req; const struct rpmh_request *rpm_msg; - unsigned long flags; int ret = 0; int i; /* Send Sleep/Wake requests to the controller, expect no response */ - spin_lock_irqsave(&ctrlr->cache_lock, flags); list_for_each_entry(req, &ctrlr->batch_cache, list) { for (i = 0; i < req->count; i++) { rpm_msg = req->rpm_msgs + i; @@ -312,7 +311,6 @@ static int flush_batch(struct rpmh_ctrlr *ctrlr) break; } } - spin_unlock_irqrestore(&ctrlr->cache_lock, flags); return ret; } @@ -433,31 +431,32 @@ static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, } /** - * rpmh_flush: Flushes the buffered active and sleep sets to TCS + * rpmh_flush() - Flushes the buffered sleep and wake sets to TCSes * - * @ctrlr: controller making request to flush cached data + * @ctrlr: Controller making request to flush cached data * - * Return: -EBUSY if the controller is busy, probably waiting on a response - * to a RPMH request sent earlier. + * This function is called from sleep code on the last CPU + * (thus no spinlock needed). * - * This function is always called from the sleep code from the last CPU - * that is powering down the entire system. Since no other RPMH API would be - * executing at this time, it is safe to run lockless. + * Return: + * * 0 - Success + * * -EAGAIN - Retry again + * * Error code - Otherwise */ int rpmh_flush(struct rpmh_ctrlr *ctrlr) { struct cache_req *p; int ret; + lockdep_assert_irqs_disabled(); + if (!ctrlr->dirty) { pr_debug("Skipping flush, TCS has latest data.\n"); return 0; } /* Invalidate the TCSes first to avoid stale data */ - do { - ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); - } while (ret == -EAGAIN); + ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); if (ret) return ret; @@ -466,10 +465,6 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) if (ret) return ret; - /* - * Nobody else should be calling this function other than system PM, - * hence we can run without locks. - */ 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", -- cgit v1.2.3-59-g8ed1b From 15b3bf61b8d48f8e0ccd9d7f1bcb468b543da396 Mon Sep 17 00:00:00 2001 From: "Raju P.L.S.S.S.N" Date: Sun, 12 Apr 2020 20:20:03 +0530 Subject: soc: qcom: rpmh-rsc: Clear active mode configuration for wake TCS For RSCs that have sleep & wake TCS but no dedicated active TCS, wake TCS can be re-purposed to send active requests. Once the active requests are sent and response is received, the active mode configuration needs to be cleared so that controller can use wake TCS for sending wake requests. Introduce enable_tcs_irq() to enable completion IRQ for repurposed TCSes. Fixes: 2de4b8d33eab (drivers: qcom: rpmh-rsc: allow active requests from wake TCS) Signed-off-by: Raju P.L.S.S.S.N [mkshah: call enable_tcs_irq() within drv->lock, update commit message] Signed-off-by: Maulik Shah Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1586703004-13674-6-git-send-email-mkshah@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 77 +++++++++++++++++++++++++++++++-------------- 1 file changed, 54 insertions(+), 23 deletions(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 892c82b7e3fb..80e8a9485b6b 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -207,6 +207,42 @@ static const struct tcs_request *get_req_from_tcs(struct rsc_drv *drv, return NULL; } +static void __tcs_set_trigger(struct rsc_drv *drv, int tcs_id, bool trigger) +{ + u32 enable; + + /* + * 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, RSC_DRV_CONTROL, tcs_id, 0); + enable &= ~TCS_AMC_MODE_TRIGGER; + write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); + enable &= ~TCS_AMC_MODE_ENABLE; + write_tcs_reg_sync(drv, RSC_DRV_CONTROL, 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, RSC_DRV_CONTROL, tcs_id, enable); + enable |= TCS_AMC_MODE_TRIGGER; + write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); + } +} + +static void enable_tcs_irq(struct rsc_drv *drv, int tcs_id, bool enable) +{ + u32 data; + + data = read_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, 0); + if (enable) + data |= BIT(tcs_id); + else + data &= ~BIT(tcs_id); + write_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, data); +} + /** * tcs_tx_done: TX Done interrupt handler */ @@ -243,6 +279,14 @@ static irqreturn_t tcs_tx_done(int irq, void *p) } trace_rpmh_tx_done(drv, i, req, err); + + /* + * 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, RSC_DRV_CMD_ENABLE, i, 0); @@ -250,6 +294,13 @@ skip: write_tcs_reg(drv, RSC_DRV_IRQ_CLEAR, 0, BIT(i)); 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); if (req) rpmh_tx_done(req, err); @@ -291,28 +342,6 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, cmd_enable); } -static void __tcs_trigger(struct rsc_drv *drv, int tcs_id) -{ - u32 enable; - - /* - * 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, RSC_DRV_CONTROL, tcs_id, 0); - enable &= ~TCS_AMC_MODE_TRIGGER; - write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); - enable &= ~TCS_AMC_MODE_ENABLE; - write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); - - /* Enable the AMC mode on the TCS and then trigger the TCS */ - enable = TCS_AMC_MODE_ENABLE; - write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); - enable |= TCS_AMC_MODE_TRIGGER; - write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); -} - static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs, const struct tcs_request *msg) { @@ -383,10 +412,12 @@ static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg) 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) + enable_tcs_irq(drv, tcs_id, true); spin_unlock(&drv->lock); __tcs_buffer_write(drv, tcs_id, 0, msg); - __tcs_trigger(drv, tcs_id); + __tcs_set_trigger(drv, tcs_id, true); done_write: spin_unlock_irqrestore(&tcs->lock, flags); -- cgit v1.2.3-59-g8ed1b From 38427e5a47bf83299da930bd474c6cb2632ad810 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Sun, 12 Apr 2020 20:20:04 +0530 Subject: soc: qcom: rpmh-rsc: Allow using free WAKE TCS for active request When there are more than one WAKE TCS available and there is no dedicated ACTIVE TCS available, invalidating all WAKE TCSes and waiting for current transfer to complete in first WAKE TCS blocks using another free WAKE TCS to complete current request. Remove rpmh_rsc_invalidate() to happen from tcs_write() when WAKE TCSes is re-purposed to be used for Active mode. Clear only currently used WAKE TCS's register configuration. Fixes: 2de4b8d33eab (drivers: qcom: rpmh-rsc: allow active requests from wake TCS) Signed-off-by: Maulik Shah Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1586703004-13674-7-git-send-email-mkshah@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 80e8a9485b6b..cc1293cb15a5 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -154,7 +154,7 @@ int rpmh_rsc_invalidate(struct rsc_drv *drv) static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, const struct tcs_request *msg) { - int type, ret; + int type; struct tcs_group *tcs; switch (msg->state) { @@ -175,19 +175,10 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, * 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. - * NOTE: The driver must be aware that this RSC does not have a - * dedicated AMC, and therefore would invalidate the sleep and wake - * TCSes before making an active state request. */ tcs = get_tcs_of_type(drv, type); - if (msg->state == RPMH_ACTIVE_ONLY_STATE && !tcs->num_tcs) { + if (msg->state == RPMH_ACTIVE_ONLY_STATE && !tcs->num_tcs) tcs = get_tcs_of_type(drv, WAKE_TCS); - if (tcs->num_tcs) { - ret = rpmh_rsc_invalidate(drv); - if (ret) - return ERR_PTR(ret); - } - } return tcs; } @@ -412,8 +403,16 @@ static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg) 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) + 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, RSC_DRV_CMD_ENABLE, tcs_id, 0); + write_tcs_reg_sync(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, 0); enable_tcs_irq(drv, tcs_id, true); + } spin_unlock(&drv->lock); __tcs_buffer_write(drv, tcs_id, 0, msg); -- cgit v1.2.3-59-g8ed1b From 3b5e3d50f83a3706126ade45ba8d44ca829b0803 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:06 -0700 Subject: soc: qcom: rpmh-rsc: Clean code reading/writing TCS regs/cmds This patch makes two changes, both of which should be no-ops: 1. Make read_tcs_reg() / read_tcs_cmd() symmetric to write_tcs_reg() / write_tcs_cmd(). 2. Change the order of operations in the above functions to make it more obvious to me what the math is doing. Specifically first you want to find the right TCS, then the right register, and then multiply by the command ID if necessary. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.1.I1b754137e8089e46cf33fc2ea270734ec3847ec4@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index cc1293cb15a5..91fb5a6d68a2 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -67,28 +67,33 @@ #define CMD_STATUS_ISSUED BIT(8) #define CMD_STATUS_COMPL BIT(16) -static u32 read_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) +static u32 read_tcs_cmd(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) { - return readl_relaxed(drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id + + return readl_relaxed(drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg + RSC_DRV_CMD_OFFSET * cmd_id); } +static u32 read_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id) +{ + return readl_relaxed(drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg); +} + static void write_tcs_cmd(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id, u32 data) { - writel_relaxed(data, drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id + + writel_relaxed(data, drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg + RSC_DRV_CMD_OFFSET * cmd_id); } static void write_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, u32 data) { - writel_relaxed(data, drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id); + writel_relaxed(data, drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg); } static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, u32 data) { - writel(data, drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id); + writel(data, drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg); for (;;) { if (data == readl(drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id)) @@ -100,7 +105,7 @@ static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, static bool tcs_is_free(struct rsc_drv *drv, int tcs_id) { return !test_bit(tcs_id, drv->tcs_in_use) && - read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id, 0); + read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id); } static struct tcs_group *get_tcs_of_type(struct rsc_drv *drv, int type) @@ -207,7 +212,7 @@ static void __tcs_set_trigger(struct rsc_drv *drv, int tcs_id, bool trigger) * While clearing ensure that the AMC mode trigger is cleared * and then the mode enable is cleared. */ - enable = read_tcs_reg(drv, RSC_DRV_CONTROL, tcs_id, 0); + enable = read_tcs_reg(drv, RSC_DRV_CONTROL, tcs_id); enable &= ~TCS_AMC_MODE_TRIGGER; write_tcs_reg_sync(drv, RSC_DRV_CONTROL, tcs_id, enable); enable &= ~TCS_AMC_MODE_ENABLE; @@ -226,7 +231,7 @@ static void enable_tcs_irq(struct rsc_drv *drv, int tcs_id, bool enable) { u32 data; - data = read_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, 0); + data = read_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0); if (enable) data |= BIT(tcs_id); else @@ -245,7 +250,7 @@ static irqreturn_t tcs_tx_done(int irq, void *p) const struct tcs_request *req; struct tcs_cmd *cmd; - irq_status = read_tcs_reg(drv, RSC_DRV_IRQ_STATUS, 0, 0); + irq_status = read_tcs_reg(drv, RSC_DRV_IRQ_STATUS, 0); for_each_set_bit(i, &irq_status, BITS_PER_LONG) { req = get_req_from_tcs(drv, i); @@ -259,7 +264,7 @@ static irqreturn_t tcs_tx_done(int irq, void *p) u32 sts; cmd = &req->cmds[j]; - sts = read_tcs_reg(drv, RSC_DRV_CMD_STATUS, i, j); + sts = read_tcs_cmd(drv, RSC_DRV_CMD_STATUS, i, j); if (!(sts & CMD_STATUS_ISSUED) || ((req->wait_for_compl || cmd->wait) && !(sts & CMD_STATUS_COMPL))) { @@ -313,7 +318,7 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, cmd_msgid |= msg->wait_for_compl ? CMD_MSGID_RESP_REQ : 0; cmd_msgid |= CMD_MSGID_WRITE; - cmd_complete = read_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, 0); + cmd_complete = read_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id); for (i = 0, j = cmd_id; i < msg->num_cmds; i++, j++) { cmd = &msg->cmds[i]; @@ -329,7 +334,7 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, } write_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, cmd_complete); - cmd_enable |= read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, 0); + cmd_enable |= read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id); write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, cmd_enable); } @@ -345,10 +350,10 @@ static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs, if (tcs_is_free(drv, tcs_id)) continue; - curr_enabled = read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, 0); + curr_enabled = read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id); for_each_set_bit(j, &curr_enabled, MAX_CMDS_PER_TCS) { - addr = read_tcs_reg(drv, RSC_DRV_CMD_ADDR, tcs_id, j); + addr = read_tcs_cmd(drv, RSC_DRV_CMD_ADDR, tcs_id, j); for (k = 0; k < msg->num_cmds; k++) { if (addr == msg->cmds[k].addr) return -EBUSY; -- cgit v1.2.3-59-g8ed1b From 1f7dbeb51a4f555db4105dc7927be6c77f0b60fd Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:07 -0700 Subject: soc: qcom: rpmh-rsc: Document the register layout better Perhaps it's just me, it took a really long time to understand what the register layout of rpmh-rsc was just from the #defines. Let's add a bunch of comments describing which blocks are part of other blocks. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200413100321.v4.2.Iaddc29b72772e6ea381238a0ee85b82d3903e5f2@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 79 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 91fb5a6d68a2..439a0eadabf1 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -43,14 +43,29 @@ #define DRV_NCPT_MASK 0x1F #define DRV_NCPT_SHIFT 27 -/* Register offsets */ +/* Offsets for common TCS Registers, one bit per TCS */ #define RSC_DRV_IRQ_ENABLE 0x00 #define RSC_DRV_IRQ_STATUS 0x04 -#define RSC_DRV_IRQ_CLEAR 0x08 -#define RSC_DRV_CMD_WAIT_FOR_CMPL 0x10 +#define RSC_DRV_IRQ_CLEAR 0x08 /* w/o; write 1 to clear */ + +/* + * Offsets for per TCS Registers. + * + * TCSes start at 0x10 from tcs_base and are stored one after another. + * Multiply tcs_id by RSC_DRV_TCS_OFFSET to find a given TCS and add one + * of the below to find a register. + */ +#define RSC_DRV_CMD_WAIT_FOR_CMPL 0x10 /* 1 bit per command */ #define RSC_DRV_CONTROL 0x14 -#define RSC_DRV_STATUS 0x18 -#define RSC_DRV_CMD_ENABLE 0x1C +#define RSC_DRV_STATUS 0x18 /* zero if tcs is busy */ +#define RSC_DRV_CMD_ENABLE 0x1C /* 1 bit per command */ + +/* + * Offsets for per command in a TCS. + * + * Commands (up to 16) start at 0x30 in a TCS; multiply command index + * by RSC_DRV_CMD_OFFSET and add one of the below to find a register. + */ #define RSC_DRV_CMD_MSGID 0x30 #define RSC_DRV_CMD_ADDR 0x34 #define RSC_DRV_CMD_DATA 0x38 @@ -67,6 +82,60 @@ #define CMD_STATUS_ISSUED BIT(8) #define CMD_STATUS_COMPL BIT(16) +/* + * Here's a high level overview of how all the registers in RPMH work + * together: + * + * - The main rpmh-rsc address is the base of a register space that can + * be used to find overall configuration of the hardware + * (DRV_PRNT_CHLD_CONFIG). Also found within the rpmh-rsc register + * space are all the TCS blocks. The offset of the TCS blocks is + * specified in the device tree by "qcom,tcs-offset" and used to + * compute tcs_base. + * - TCS blocks come one after another. Type, count, and order are + * specified by the device tree as "qcom,tcs-config". + * - Each TCS block has some registers, then space for up to 16 commands. + * Note that though address space is reserved for 16 commands, fewer + * might be present. See ncpt (num cmds per TCS). + * + * Here's a picture: + * + * +---------------------------------------------------+ + * |RSC | + * | ctrl | + * | | + * | Drvs: | + * | +-----------------------------------------------+ | + * | |DRV0 | | + * | | ctrl/config | | + * | | IRQ | | + * | | | | + * | | TCSes: | | + * | | +------------------------------------------+ | | + * | | |TCS0 | | | | | | | | | | | | | | | + * | | | ctrl | 0| 1| 2| 3| 4| 5| .| .| .| .|14|15| | | + * | | | | | | | | | | | | | | | | | | + * | | +------------------------------------------+ | | + * | | +------------------------------------------+ | | + * | | |TCS1 | | | | | | | | | | | | | | | + * | | | ctrl | 0| 1| 2| 3| 4| 5| .| .| .| .|14|15| | | + * | | | | | | | | | | | | | | | | | | + * | | +------------------------------------------+ | | + * | | +------------------------------------------+ | | + * | | |TCS2 | | | | | | | | | | | | | | | + * | | | ctrl | 0| 1| 2| 3| 4| 5| .| .| .| .|14|15| | | + * | | | | | | | | | | | | | | | | | | + * | | +------------------------------------------+ | | + * | | ...... | | + * | +-----------------------------------------------+ | + * | +-----------------------------------------------+ | + * | |DRV1 | | + * | | (same as DRV0) | | + * | +-----------------------------------------------+ | + * | ...... | + * +---------------------------------------------------+ + */ + static u32 read_tcs_cmd(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) { return readl_relaxed(drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg + -- cgit v1.2.3-59-g8ed1b From 427ef4f72bba5c8d3fb7dce758c3afe99d9db9c6 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:08 -0700 Subject: soc: qcom: rpmh-rsc: Fold tcs_ctrl_write() into its single caller I was trying to write documentation for the functions in rpmh-rsc and I got to tcs_ctrl_write(). The documentation for the function would have been: "This is the core of rpmh_rsc_write_ctrl_data(); all the caller does is error-check and then call this". Having the error checks in a separate function doesn't help for anything since: - There are no other callers that need to bypass the error checks. - It's less documenting. When I read tcs_ctrl_write() I kept wondering if I need to handle cases other than ACTIVE_ONLY or cases with more commands than could fit in a TCS. This is obvious when the error checks and code are together. - The function just isn't that long, so there's no problem understanding the combined function. Things were even more confusing because the two functions names didn't make obvious (at least to me) their relationship. Simplify by folding one function into the other. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.3.Ie88ce5ccfc0c6055903ccca5286ae28ed3b85ed3@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 439a0eadabf1..d9177324c6a2 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -587,27 +587,6 @@ copy_data: return 0; } -static int tcs_ctrl_write(struct rsc_drv *drv, const struct tcs_request *msg) -{ - struct tcs_group *tcs; - int tcs_id = 0, cmd_id = 0; - unsigned long flags; - int ret; - - tcs = get_tcs_for_msg(drv, msg); - if (IS_ERR(tcs)) - return PTR_ERR(tcs); - - spin_lock_irqsave(&tcs->lock, flags); - /* 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); - spin_unlock_irqrestore(&tcs->lock, flags); - - return ret; -} - /** * rpmh_rsc_write_ctrl_data: Write request to the controller * @@ -618,6 +597,11 @@ static int tcs_ctrl_write(struct rsc_drv *drv, const struct tcs_request *msg) */ 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; + unsigned long flags; + int ret; + if (!msg || !msg->cmds || !msg->num_cmds || msg->num_cmds > MAX_RPMH_PAYLOAD) { pr_err("Payload error\n"); @@ -628,7 +612,18 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) if (msg->state == RPMH_ACTIVE_ONLY_STATE) return -EINVAL; - return tcs_ctrl_write(drv, msg); + tcs = get_tcs_for_msg(drv, msg); + if (IS_ERR(tcs)) + return PTR_ERR(tcs); + + spin_lock_irqsave(&tcs->lock, flags); + /* 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); + spin_unlock_irqrestore(&tcs->lock, flags); + + return ret; } /** -- cgit v1.2.3-59-g8ed1b From 53d49fe1ff49196712c416ddb987634483423934 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:09 -0700 Subject: soc: qcom: rpmh-rsc: Remove get_tcs_of_type() abstraction The get_tcs_of_type() function doesn't provide any value. It's not conceptually difficult to access a value in an array, even if that value is in a structure and we want a pointer to the value. Having the function in there makes me feel like it's doing something fancier like looping or searching. Remove it. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.4.Ia348ade7c6ed1d0d952ff2245bc854e5834c8d9a@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index d9177324c6a2..d0c187c17ce1 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -177,17 +177,10 @@ static bool tcs_is_free(struct rsc_drv *drv, int tcs_id) read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id); } -static struct tcs_group *get_tcs_of_type(struct rsc_drv *drv, int type) -{ - return &drv->tcs[type]; -} - static int tcs_invalidate(struct rsc_drv *drv, int type) { int m; - struct tcs_group *tcs; - - tcs = get_tcs_of_type(drv, type); + struct tcs_group *tcs = &drv->tcs[type]; spin_lock(&tcs->lock); if (bitmap_empty(tcs->slots, MAX_TCS_SLOTS)) { @@ -250,9 +243,9 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, * dedicated TCS for active state use, then re-purpose a wake TCS to * send active votes. */ - tcs = get_tcs_of_type(drv, type); + tcs = &drv->tcs[type]; if (msg->state == RPMH_ACTIVE_ONLY_STATE && !tcs->num_tcs) - tcs = get_tcs_of_type(drv, WAKE_TCS); + tcs = &drv->tcs[WAKE_TCS]; return tcs; } @@ -643,7 +636,7 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) { int m; - struct tcs_group *tcs = get_tcs_of_type(drv, ACTIVE_TCS); + struct tcs_group *tcs = &drv->tcs[ACTIVE_TCS]; /* * If we made an active request on a RSC that does not have a @@ -655,7 +648,7 @@ static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) * lock before checking tcs_is_free(). */ if (!tcs->num_tcs) - tcs = get_tcs_of_type(drv, WAKE_TCS); + tcs = &drv->tcs[WAKE_TCS]; for (m = tcs->offset; m < tcs->offset + tcs->num_tcs; m++) { if (!tcs_is_free(drv, m)) -- cgit v1.2.3-59-g8ed1b From 1bc92a933f19e2415353a4892f7df4617f691b6c Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:10 -0700 Subject: soc: qcom: rpmh-rsc: Kill cmd_cache and find_match() with fire The "cmd_cache" in RPMH wasn't terribly sensible. Specifically: - The current code doesn't really detect "conflicts" properly any case where the sequence being checked has more than one entry. One simple way to see this in the current code is that if cmd[0].addr isn't found then cmd[1].addr is never checked. - The code attempted to use the "cmd_cache" to update an existing message in a sleep/wake TCS with new data. The goal appeared to be to update part of a TCS while leaving the rest of the TCS alone. We never actually do this. We always fully invalidate and re-write everything. - If/when we try to optimize things to not fully invalidate / re-write every time we update the TCSes we'll need to think it through very carefully. Specifically requirement of find_match() that the new sequence of addrs must match exactly the old sequence of addrs seems inflexible. It's also not documented in rpmh_write() and rpmh_write_batch(). In any case, if we do decide to require updates to keep the exact same sequence and length then presumably the API and data structures should be updated to understand groups more properly. The current algorithm doesn't really keep track of the length of the old sequence and there are several boundary-condition bugs because of that. Said another way: if we decide to do something like this in the future we should start from scratch and thus find_match() isn't useful to keep around. This patch isn't quite a no-op. Specifically: - It should be a slight performance boost of not searching through so many arrays. - The old code would have done something useful in one case: it would allow someone calling rpmh_write() to override the data that came from rpmh_write_batch(). I don't believe that actually happens in reality. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.5.I6d3d0a3ec810dc72ff1df3cbf97deefdcdeb8eef@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 2 -- drivers/soc/qcom/rpmh-rsc.c | 47 ---------------------------------------- 2 files changed, 49 deletions(-) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index e9a90cb7773e..6a6d776ccca9 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -30,7 +30,6 @@ struct rsc_drv; * @ncpt: number of commands in each TCS * @lock: lock for synchronizing this TCS writes * @req: requests that are sent from the TCS - * @cmd_cache: flattened cache of cmds in sleep/wake TCS * @slots: indicates which of @cmd_addr are occupied */ struct tcs_group { @@ -42,7 +41,6 @@ struct tcs_group { int ncpt; spinlock_t lock; const struct tcs_request *req[MAX_TCS_PER_TYPE]; - u32 *cmd_cache; DECLARE_BITMAP(slots, MAX_TCS_SLOTS); }; diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index d0c187c17ce1..c9e5cddbc099 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -522,42 +522,12 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) return ret; } -static int find_match(const struct tcs_group *tcs, const struct tcs_cmd *cmd, - int len) -{ - int i, j; - - /* Check for already cached commands */ - for_each_set_bit(i, tcs->slots, MAX_TCS_SLOTS) { - if (tcs->cmd_cache[i] != cmd[0].addr) - continue; - if (i + len >= tcs->num_tcs * tcs->ncpt) - goto seq_err; - for (j = 0; j < len; j++) { - if (tcs->cmd_cache[i + j] != cmd[j].addr) - goto seq_err; - } - return i; - } - - return -ENODATA; - -seq_err: - WARN(1, "Message does not match previous sequence.\n"); - return -EINVAL; -} - 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; - /* Find if we already have the msg in our TCS */ - slot = find_match(tcs, msg->cmds, msg->num_cmds); - if (slot >= 0) - goto copy_data; - /* Do over, until we can fit the full payload in a TCS */ do { slot = bitmap_find_next_zero_area(tcs->slots, MAX_TCS_SLOTS, @@ -567,11 +537,7 @@ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, i += tcs->ncpt; } while (slot + msg->num_cmds - 1 >= i); -copy_data: bitmap_set(tcs->slots, slot, msg->num_cmds); - /* Copy the addresses of the resources over to the slots */ - for (i = 0; i < msg->num_cmds; i++) - tcs->cmd_cache[slot + i] = msg->cmds[i].addr; offset = slot / tcs->ncpt; *tcs_id = offset + tcs->offset; @@ -762,19 +728,6 @@ static int rpmh_probe_tcs_config(struct platform_device *pdev, tcs->mask = ((1 << tcs->num_tcs) - 1) << st; tcs->offset = st; st += tcs->num_tcs; - - /* - * Allocate memory to cache sleep and wake requests to - * avoid reading TCS register memory. - */ - if (tcs->type == ACTIVE_TCS) - continue; - - tcs->cmd_cache = devm_kcalloc(&pdev->dev, - tcs->num_tcs * ncpt, sizeof(u32), - GFP_KERNEL); - if (!tcs->cmd_cache) - return -ENOMEM; } drv->num_tcs = st; -- cgit v1.2.3-59-g8ed1b From e40b0c1628f27986dd90f94c43464df5aa8968cf Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:11 -0700 Subject: soc: qcom: rpmh-rsc: A lot of comments I've been pouring through the rpmh-rsc code and trying to understand it. Document everything to the best of my ability. All documentation here is strictly from code analysis--no actual knowledge of the hardware was used. If something is wrong in here I either misunderstood the code, had a typo, or the code has a bug in it leading to my incorrect understanding. In a few places here I have documented things that don't make tons of sense. A future patch will try to address this. While this means I'm adding comments / todos and then later fixing them in the series, it seemed more urgent to get things documented first so that people could understand the later patches. Any comments I adjusted I also tried to make match kernel-doc better. Specifically: - kernel-doc says do not leave a blank line between the function description and the arguments - kernel-doc examples always have things starting w/ a capital and ending with a period. This should be a no-op. It's just comment changes. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200413100321.v4.6.I52653eb85d7dc8981ee0dafcd0b6cc0f273e9425@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 62 +++++++---- drivers/soc/qcom/rpmh-rsc.c | 222 ++++++++++++++++++++++++++++++++++++--- 2 files changed, 247 insertions(+), 37 deletions(-) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 6a6d776ccca9..f06350cbc9a2 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -22,15 +22,24 @@ 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 - * @lock: lock for synchronizing this TCS writes - * @req: requests that are sent from the TCS - * @slots: indicates which of @cmd_addr are occupied + * @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. + * @lock: Lock for synchronizing this TCS writes. + * @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; @@ -82,19 +91,28 @@ struct rpmh_ctrlr { * struct rsc_drv: the Direct Resource Voter (DRV) of the * Resource State Coordinator controller (RSC) * - * @name: Controller identifier - * @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_entered_pm: CPU mask for cpus in idle power collapse - * Used when solver mode is not present - * @tcs: TCS groups - * @tcs_in_use: S/W state of the TCS - * @lock: Synchronize state of the controller - * @pm_lock: Synchronize during PM notifications - * Used when solver mode is not present + * @name: Controller identifier. + * @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_entered_pm: CPU mask for cpus in idle power collapse. + * Used when solver mode is not present. + * @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 both the lock in this struct and the + * tcs_lock for the TCS in order to mark a TCS as + * in-use, but you only need the lock in this structure + * (aka the drv->lock) to mark one freed. + * @lock: Synchronize state of the controller. If you will be + * grabbing this lock and a tcs_lock at the same time, + * grab the tcs_lock first so we always have a + * consistent lock ordering. + * @pm_lock: Synchronize during PM notifications. + * Used when solver mode is not present. * @client: Handle to the DRV's client. */ struct rsc_drv { diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index c9e5cddbc099..78fe9344ecd3 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -171,12 +171,39 @@ static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, } } +/** + * tcs_is_free() - Return if a TCS is totally free. + * @drv: The RSC controller. + * @tcs_id: The global ID of this TCS. + * + * Returns true if nobody has claimed this TCS (by setting tcs_in_use). + * If the TCS looks free, checks that the hardware agrees. + * + * Context: Must be called with the drv->lock held or the tcs_lock for the TCS + * being tested. If only the tcs_lock is held then it is possible that + * this function will return that a tcs is still busy when it has been + * recently been freed but it will never return free when a TCS is + * actually in use. + * + * Return: true if the given TCS is free. + */ static bool tcs_is_free(struct rsc_drv *drv, int tcs_id) { return !test_bit(tcs_id, drv->tcs_in_use) && read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id); } +/** + * 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. + * + * Return: 0 if no problem, or -EAGAIN if the caller should try again in a + * bit. Caller should make sure to enable interrupts between tries. + */ static int tcs_invalidate(struct rsc_drv *drv, int type) { int m; @@ -203,9 +230,11 @@ static int tcs_invalidate(struct rsc_drv *drv, int type) } /** - * rpmh_rsc_invalidate - Invalidate sleep and wake TCSes + * rpmh_rsc_invalidate() - Invalidate sleep and wake TCSes. + * @drv: The RSC controller. * - * @drv: the RSC controller + * Return: 0 if no problem, or -EAGAIN if the caller should try again in a + * bit. Caller should make sure to enable interrupts between tries. */ int rpmh_rsc_invalidate(struct rsc_drv *drv) { @@ -218,6 +247,18 @@ int rpmh_rsc_invalidate(struct rsc_drv *drv) return ret; } +/** + * 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. + * + * Called without drv->lock held and with no tcs_lock locks held. + * + * 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) { @@ -241,7 +282,9 @@ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, /* * 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. + * 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) @@ -250,6 +293,22 @@ 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) { @@ -265,6 +324,23 @@ static const struct tcs_request *get_req_from_tcs(struct rsc_drv *drv, 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; @@ -289,6 +365,15 @@ static void __tcs_set_trigger(struct rsc_drv *drv, int tcs_id, bool trigger) } } +/** + * 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; @@ -302,7 +387,14 @@ static void enable_tcs_irq(struct rsc_drv *drv, int tcs_id, bool enable) } /** - * tcs_tx_done: TX Done interrupt handler + * 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) { @@ -367,6 +459,16 @@ skip: 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) { @@ -400,6 +502,26 @@ static void __tcs_buffer_write(struct rsc_drv *drv, int tcs_id, int cmd_id, write_tcs_reg(drv, 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_is_free() will ever return true 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) { @@ -426,6 +548,15 @@ static int check_for_req_inflight(struct rsc_drv *drv, struct tcs_group *tcs, 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. + */ static int find_free_tcs(struct tcs_group *tcs) { int i; @@ -438,6 +569,20 @@ static int find_free_tcs(struct tcs_group *tcs) return -EBUSY; } +/** + * tcs_write() - Store messages into a TCS right now, or return -EBUSY. + * @drv: The controller. + * @msg: The data to be sent. + * + * Grabs a TCS for ACTIVE_ONLY transfers and writes the messages to it. + * + * If there are no free TCSes for ACTIVE_ONLY transfers or if a command for + * the same address is already transferring returns -EBUSY which means the + * client should retry shortly. + * + * Return: 0 on success, -EBUSY if client should retry, or an error. + * Client should have interrupts enabled for a bit before retrying. + */ static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg) { struct tcs_group *tcs; @@ -491,14 +636,26 @@ done_write: } /** - * rpmh_rsc_send_data: Validate the incoming message and write to the - * appropriate TCS block. + * rpmh_rsc_send_data() - Validate the incoming message + write to TCS block. + * @drv: The controller. + * @msg: The data to be sent. * - * @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. - * Note: This call blocks until a valid data is written to the TCS. */ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) { @@ -522,13 +679,30 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) return ret; } +/** + * 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. + * + * Must be called with the tcs_lock for the group held. + * + * 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 TCS */ + /* 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); @@ -547,12 +721,14 @@ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, } /** - * rpmh_rsc_write_ctrl_data: Write request to the controller + * 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. * - * @drv: the controller - * @msg: the data to be written to the controller + * This should only be called for for sleep/wake state, never active-only + * state. * - * There is no response returned for writing the request to the controller. + * Return: 0 if no error; else -error. */ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) { @@ -587,7 +763,6 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) /** * 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. @@ -624,6 +799,23 @@ static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) return false; } +/** + * 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) { -- cgit v1.2.3-59-g8ed1b From ff304ea34d2e2d7ef7a13aefb4e62f456cf78e99 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:12 -0700 Subject: soc: qcom: rpmh-rsc: tcs_is_free() can just check tcs_in_use tcs_is_free() had two checks in it: does the software think that the TCS is free and does the hardware think that the TCS is free. I couldn't figure out in which case the hardware could think that a TCS was in-use but software thought it was free. Apparently there is no case and the extra check can be removed. This apparently has already been done in a downstream patch. Suggested-by: Maulik Shah Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.7.Icf2213131ea652087f100129359052c83601f8b0@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 78fe9344ecd3..dc4bad01c000 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -177,7 +177,6 @@ static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, * @tcs_id: The global ID of this TCS. * * Returns true if nobody has claimed this TCS (by setting tcs_in_use). - * If the TCS looks free, checks that the hardware agrees. * * Context: Must be called with the drv->lock held or the tcs_lock for the TCS * being tested. If only the tcs_lock is held then it is possible that @@ -189,8 +188,7 @@ static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, */ static bool tcs_is_free(struct rsc_drv *drv, int tcs_id) { - return !test_bit(tcs_id, drv->tcs_in_use) && - read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id); + return !test_bit(tcs_id, drv->tcs_in_use); } /** -- cgit v1.2.3-59-g8ed1b From dded0317f510352ee622e526e113b9478ca406da Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:13 -0700 Subject: soc: qcom: rpmh-rsc: Don't double-check rpmh payload The calls rpmh_rsc_write_ctrl_data() and rpmh_rsc_send_data() are only ever called from rpmh.c. We know that rpmh.c already error checked the message. There's no reason to do it again in rpmh-rsc. Suggested-by: Maulik Shah Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.8.I8e187cdfb7a31f5bb7724f1f937f2862ee464a35@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index dc4bad01c000..59ff4d17ddf5 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -634,7 +634,7 @@ done_write: } /** - * rpmh_rsc_send_data() - Validate the incoming message + write to TCS block. + * rpmh_rsc_send_data() - Write / trigger active-only message. * @drv: The controller. * @msg: The data to be sent. * @@ -659,12 +659,6 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) { int ret; - if (!msg || !msg->cmds || !msg->num_cmds || - msg->num_cmds > MAX_RPMH_PAYLOAD) { - WARN_ON(1); - return -EINVAL; - } - do { ret = tcs_write(drv, msg); if (ret == -EBUSY) { @@ -735,16 +729,6 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) unsigned long flags; int ret; - if (!msg || !msg->cmds || !msg->num_cmds || - msg->num_cmds > MAX_RPMH_PAYLOAD) { - pr_err("Payload error\n"); - return -EINVAL; - } - - /* Data sent to this API will not be sent immediately */ - if (msg->state == RPMH_ACTIVE_ONLY_STATE) - return -EINVAL; - tcs = get_tcs_for_msg(drv, msg); if (IS_ERR(tcs)) return PTR_ERR(tcs); -- cgit v1.2.3-59-g8ed1b From 881808d0bbf336d333981ad86bde62ef2165e437 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:14 -0700 Subject: soc: qcom: rpmh-rsc: Caller handles tcs_invalidate() exclusivity Auditing tcs_invalidate() made me worried. Specifically I saw that it used spin_lock(), not spin_lock_irqsave(). That always worries me unless I can trace for sure that I'm in the interrupt handler or that someone else already disabled interrupts. Looking more at it, there is actually no reason for these locks anyway. Specifically the only reason you'd ever call rpmh_rsc_invalidate() is if you cared that the sleep/wake TCSes were empty. That means that they need to continue to be empty even after rpmh_rsc_invalidate() returns. The only way that can happen is if the caller already has done something to keep all other RPMH users out. It should be noted that even though the caller is only worried about making sleep/wake TCSes empty, they also need to worry about stopping active-only transfers if they need to handle the case where active-only transfers might borrow the wake TCS. At the moment rpmh_rsc_invalidate() is only called in PM code from the last CPU. If that later changes the caller will still need to solve the above problems themselves, so these locks will never be useful. Continuing to audit tcs_invalidate(), I found a bug. The function didn't properly check for a borrowed TCS if we hadn't recently written anything into the TCS. Specifically, if we've never written to the WAKE_TCS (or we've flushed it recently) then tcs->slots is empty. We'll early-out and we'll never call tcs_is_free(). I thought about fixing this bug by either deleting the early check for bitmap_empty() or possibly only doing it if we knew we weren't on a TCS that could be borrowed. However, I think it's better to just delete the checks. As argued above it's up to the caller to make sure that all other users of RPMH are quiet before tcs_invalidate() is called. Since callers need to handle the zero-active-TCS case anyway that means they need to make sure that the active-only transfers are quiet before calling too. The one way tcs_invalidate() gets called today is through rpmh_rsc_cpu_pm_callback() which calls rpmh_rsc_ctrlr_is_busy() to handle this. When we have another path to get to tcs_invalidate() it will also need to come up with something similar and it won't need this extra check either. If we later find some code path that actually needs this check back in (and somehow manages to be race free) we can always add it back in. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.9.I07c1f70e0e8f2dc0004bd38970b4e258acdc773e@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 2 +- drivers/soc/qcom/rpmh-rsc.c | 38 +++++++++++++------------------------- drivers/soc/qcom/rpmh.c | 5 +---- 3 files changed, 15 insertions(+), 30 deletions(-) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index f06350cbc9a2..dba8510c0669 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -132,7 +132,7 @@ 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); -int rpmh_rsc_invalidate(struct rsc_drv *drv); +void rpmh_rsc_invalidate(struct rsc_drv *drv); void rpmh_tx_done(const struct tcs_request *msg, int r); int rpmh_flush(struct rpmh_ctrlr *ctrlr); diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 59ff4d17ddf5..5303837bb866 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -199,50 +199,38 @@ static bool tcs_is_free(struct rsc_drv *drv, int tcs_id) * This will clear the "slots" variable of the given tcs_group and also * tell the hardware to forget about all entries. * - * Return: 0 if no problem, or -EAGAIN if the caller should try again in a - * bit. Caller should make sure to enable interrupts between tries. + * 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 int tcs_invalidate(struct rsc_drv *drv, int type) +static void tcs_invalidate(struct rsc_drv *drv, int type) { int m; struct tcs_group *tcs = &drv->tcs[type]; - spin_lock(&tcs->lock); - if (bitmap_empty(tcs->slots, MAX_TCS_SLOTS)) { - spin_unlock(&tcs->lock); - return 0; - } + /* 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++) { - if (!tcs_is_free(drv, m)) { - spin_unlock(&tcs->lock); - return -EAGAIN; - } write_tcs_reg_sync(drv, RSC_DRV_CMD_ENABLE, m, 0); write_tcs_reg_sync(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, m, 0); } bitmap_zero(tcs->slots, MAX_TCS_SLOTS); - spin_unlock(&tcs->lock); - - return 0; } /** * rpmh_rsc_invalidate() - Invalidate sleep and wake TCSes. * @drv: The RSC controller. * - * Return: 0 if no problem, or -EAGAIN if the caller should try again in a - * bit. Caller should make sure to enable interrupts between tries. + * 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. */ -int rpmh_rsc_invalidate(struct rsc_drv *drv) +void rpmh_rsc_invalidate(struct rsc_drv *drv) { - int ret; - - ret = tcs_invalidate(drv, SLEEP_TCS); - if (!ret) - ret = tcs_invalidate(drv, WAKE_TCS); - - return ret; + tcs_invalidate(drv, SLEEP_TCS); + tcs_invalidate(drv, WAKE_TCS); } /** diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index be5e89d73526..3abbb08cd6e1 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -440,7 +440,6 @@ static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, * * Return: * * 0 - Success - * * -EAGAIN - Retry again * * Error code - Otherwise */ int rpmh_flush(struct rpmh_ctrlr *ctrlr) @@ -456,9 +455,7 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) } /* Invalidate the TCSes first to avoid stale data */ - ret = rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); - if (ret) - return ret; + rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); /* First flush the cached batch requests */ ret = flush_batch(ctrlr); -- cgit v1.2.3-59-g8ed1b From 032c692ae588324f66ac07c0357d2e681a9d0e1e Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 13 Apr 2020 10:04:15 -0700 Subject: soc: qcom: rpmh-rsc: read_tcs_reg()/write_tcs_reg() are not for IRQ The RSC_DRV_IRQ_ENABLE, RSC_DRV_IRQ_STATUS, and RSC_DRV_IRQ_CLEAR registers are not part of TCS 0. Let's not pretend that they are by using read_tcs_reg() and write_tcs_reg() and passing a bogus tcs_id of 0. We could introduce a new wrapper for these registers but it wouldn't buy us much. Let's just read/write directly. Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Tested-by: Maulik Shah Link: https://lore.kernel.org/r/20200413100321.v4.10.I2adf93809c692d0b673e1a86ea97c45644aa8d97@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 5303837bb866..732316bb67dc 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -364,12 +364,12 @@ static void enable_tcs_irq(struct rsc_drv *drv, int tcs_id, bool enable) { u32 data; - data = read_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0); + data = readl_relaxed(drv->tcs_base + RSC_DRV_IRQ_ENABLE); if (enable) data |= BIT(tcs_id); else data &= ~BIT(tcs_id); - write_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, data); + writel_relaxed(data, drv->tcs_base + RSC_DRV_IRQ_ENABLE); } /** @@ -390,7 +390,7 @@ static irqreturn_t tcs_tx_done(int irq, void *p) const struct tcs_request *req; struct tcs_cmd *cmd; - irq_status = read_tcs_reg(drv, RSC_DRV_IRQ_STATUS, 0); + irq_status = readl_relaxed(drv->tcs_base + RSC_DRV_IRQ_STATUS); for_each_set_bit(i, &irq_status, BITS_PER_LONG) { req = get_req_from_tcs(drv, i); @@ -427,7 +427,7 @@ skip: /* Reclaim the TCS */ write_tcs_reg(drv, RSC_DRV_CMD_ENABLE, i, 0); write_tcs_reg(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, i, 0); - write_tcs_reg(drv, RSC_DRV_IRQ_CLEAR, 0, BIT(i)); + writel_relaxed(BIT(i), drv->tcs_base + RSC_DRV_IRQ_CLEAR); spin_lock(&drv->lock); clear_bit(i, drv->tcs_in_use); /* @@ -969,7 +969,8 @@ static int rpmh_rsc_probe(struct platform_device *pdev) } /* Enable the active TCS to send requests immediately */ - write_tcs_reg(drv, RSC_DRV_IRQ_ENABLE, 0, drv->tcs[ACTIVE_TCS].mask); + writel_relaxed(drv->tcs[ACTIVE_TCS].mask, + drv->tcs_base + RSC_DRV_IRQ_ENABLE); spin_lock_init(&drv->client.cache_lock); INIT_LIST_HEAD(&drv->client.cache); -- cgit v1.2.3-59-g8ed1b From 1d3c6f86fd3f8b88c707f56d8c3f94e014b40e83 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 26 Mar 2020 22:44:58 +0000 Subject: soc: qcom: rpmh: Allow RPMH driver to be loaded as a module This patch allow the rpmh driver to be loaded as a permenent module. Meaning it can be loaded from a module, but then cannot be unloaded. Ideally, it would include a remove hook and related logic, but the rpmh driver is fairly core to the system, so once its loaded with almost anythign else to get the system to go, the dependencies are not likely to ever also be removed. So making it a permenent module at least improves things slightly over requiring it to be a built in driver. Acked-by: Saravana Kannan Cc: Todd Kjos Cc: Saravana Kannan Cc: Andy Gross Cc: Bjorn Andersson Cc: Rajendra Nayak Cc: linux-arm-msm@vger.kernel.org Tested-by: Bjorn Andersson Reviewed-by: Bjorn Andersson Signed-off-by: John Stultz Link: https://lore.kernel.org/r/20200326224459.105170-3-john.stultz@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 2 +- drivers/soc/qcom/rpmh-rsc.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index bf42a17a45de..70d97147d87e 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -107,7 +107,7 @@ config QCOM_RMTFS_MEM Say y here if you intend to boot the modem remoteproc. config QCOM_RPMH - bool "Qualcomm RPM-Hardened (RPMH) Communication" + tristate "Qualcomm RPM-Hardened (RPMH) Communication" depends on ARCH_QCOM && ARM64 || COMPILE_TEST help Support for communication with the hardened-RPM blocks in diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 732316bb67dc..a9e15699f55f 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -985,6 +986,7 @@ 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, @@ -999,3 +1001,6 @@ static int __init rpmh_driver_init(void) return platform_driver_register(&rpmh_driver); } arch_initcall(rpmh_driver_init); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From d4889ec1fc6ac6321cc1e8b35bb656f970926a09 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 26 Mar 2020 22:44:59 +0000 Subject: soc: qcom: rpmhpd: Allow RPMHPD driver to be loaded as a module This patch allow the rpmhpd driver to be loaded as a permenent module. Meaning it can be loaded from a module, but then cannot be unloaded. Ideally, it would include a remove hook and related logic, but apparently the genpd code isn't able to track usage and cleaning things up? So making it a permenent module at least improves things slightly over requiring it to be a built in driver. Cc: Todd Kjos Cc: Saravana Kannan Cc: Andy Gross Cc: Bjorn Andersson Cc: Rajendra Nayak Cc: linux-arm-msm@vger.kernel.org Acked-by: Saravana Kannan Tested-by: Bjorn Andersson Reviewed-by: Bjorn Andersson Signed-off-by: John Stultz Link: https://lore.kernel.org/r/20200326224459.105170-4-john.stultz@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 2 +- drivers/soc/qcom/rpmhpd.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 70d97147d87e..c2a8fcdb24b9 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -117,7 +117,7 @@ config QCOM_RPMH help apply the aggregated state on the resource. config QCOM_RPMHPD - bool "Qualcomm RPMh Power domain driver" + tristate "Qualcomm RPMh Power domain driver" depends on QCOM_RPMH && QCOM_COMMAND_DB help QCOM RPMh Power domain driver to support power-domains with diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 4d264d0672c4..0bb12d5870a7 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -189,6 +190,7 @@ static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, { } }; +MODULE_DEVICE_TABLE(of, rpmhpd_match_table); static int rpmhpd_send_corner(struct rpmhpd *pd, int state, unsigned int corner, bool sync) @@ -460,3 +462,6 @@ static int __init rpmhpd_init(void) return platform_driver_register(&rpmhpd_driver); } core_initcall(rpmhpd_init); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Power Domain Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From f29808b2fb85a7ff2d4830aa1cb736c8c9b986f4 Mon Sep 17 00:00:00 2001 From: John Stultz Date: Thu, 26 Mar 2020 22:44:57 +0000 Subject: soc: qcom: rpmpd: Allow RPMPD driver to be loaded as a module This patch allow the rpmpd driver to be loaded as a permenent module. Meaning it can be loaded from a module, but then cannot be unloaded. Ideally, it would include a remove hook and related logic, but apparently the genpd code isn't able to track usage and cleaning things up? (See: https://lkml.org/lkml/2019/1/24/38) So making it a permenent module at least improves things slightly over requiring it to be a built in driver. Cc: Todd Kjos Cc: Saravana Kannan Cc: Andy Gross Cc: Bjorn Andersson Cc: Rajendra Nayak Cc: linux-arm-msm@vger.kernel.org Acked-by: Saravana Kannan Tested-by: Bjorn Andersson Reviewed-by: Bjorn Andersson Signed-off-by: John Stultz Link: https://lore.kernel.org/r/20200326224459.105170-2-john.stultz@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 4 ++-- drivers/soc/qcom/rpmpd.c | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index c2a8fcdb24b9..0d0123f6ec0a 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -126,8 +126,8 @@ config QCOM_RPMHPD for the voltage rail. config QCOM_RPMPD - bool "Qualcomm RPM Power domain driver" - depends on QCOM_SMD_RPM=y + tristate "Qualcomm RPM Power domain driver" + depends on QCOM_SMD_RPM help QCOM RPM Power domain driver to support power-domains with performance states. The driver communicates a performance state diff --git a/drivers/soc/qcom/rpmpd.c b/drivers/soc/qcom/rpmpd.c index 2b1834c5609a..f2168e4259b2 100644 --- a/drivers/soc/qcom/rpmpd.c +++ b/drivers/soc/qcom/rpmpd.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -226,6 +227,7 @@ static const struct of_device_id rpmpd_match_table[] = { { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, { } }; +MODULE_DEVICE_TABLE(of, rpmpd_match_table); static int rpmpd_send_enable(struct rpmpd *pd, bool enable) { @@ -422,3 +424,6 @@ static int __init rpmpd_init(void) return platform_driver_register(&rpmpd_driver); } core_initcall(rpmpd_init); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPM Power Domain Driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b From 9d6ba921acf43bf1894564eb3d51ced2145e0147 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Apr 2020 23:20:33 -0700 Subject: soc: qcom: cmd-db: Cast sizeof() to int to silence field width warning We pass the result of sizeof() here to tell the printk format specifier how many bytes to print. That expects an int though and sizeof() isn't that type. Cast to int to silence this warning: drivers/soc/qcom/cmd-db.c: In function 'cmd_db_debugfs_dump': drivers/soc/qcom/cmd-db.c:281:30: warning: field width specifier '*' expects argument of type 'int', but argument 4 has type 'long unsigned int' [-Wformat=] Reviewed-by: Guenter Roeck Fixes: d6815c5c43d4 ("soc: qcom: cmd-db: Add debugfs dumping file") Reported-by: Stephen Rothwell Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20200415062033.66406-1-swboyd@chromium.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/cmd-db.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index 8b2b7357b6da..57382b64f1da 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -279,7 +279,7 @@ static int cmd_db_debugfs_dump(struct seq_file *seq, void *p) ent = rsc_to_entry_header(rsc); for (j = 0; j < le16_to_cpu(rsc->cnt); j++, ent++) { seq_printf(seq, "0x%08x: %*pEp", le32_to_cpu(ent->addr), - sizeof(ent->id), ent->id); + (int)sizeof(ent->id), ent->id); len = le16_to_cpu(ent->len); if (len) { -- cgit v1.2.3-59-g8ed1b From 3adaf26e7b01691eeee4086b7ac70c7750ff126e Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Wed, 15 Apr 2020 12:29:16 -0700 Subject: soc: qcom: cmd-db: Use 5 digits for printing address The top few bits aren't relevant to pad out because they're always zero. Let's just print 5 digits instead of 8 so that it's a little shorter and more readable. Reviewed-by: Lina Iyer Suggested-by: Lina Iyer Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20200415192916.78339-1-swboyd@chromium.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/cmd-db.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index 57382b64f1da..4f8b739c6485 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -278,7 +278,7 @@ static int cmd_db_debugfs_dump(struct seq_file *seq, void *p) ent = rsc_to_entry_header(rsc); for (j = 0; j < le16_to_cpu(rsc->cnt); j++, ent++) { - seq_printf(seq, "0x%08x: %*pEp", le32_to_cpu(ent->addr), + seq_printf(seq, "0x%05x: %*pEp", le32_to_cpu(ent->addr), (int)sizeof(ent->id), ent->id); len = le16_to_cpu(ent->len); -- cgit v1.2.3-59-g8ed1b From 704887278b3fb6e72ef767e56fbae4129e567c48 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 16 Apr 2020 17:06:45 -0700 Subject: soc: qcom: cmd-db: Properly endian swap the slv_id for debugfs Read the slv_id properly by making sure the 16-bit number is endian swapped from little endian to CPU native before we read it to figure out what to print for the human readable name. Otherwise we may just show that all the elements in the cmd-db are "Unknown" which isn't right. Reviewed-by: Guenter Roeck Reported-by: kbuild test robot Cc: Lina Iyer Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20200417000645.234693-1-swboyd@chromium.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/cmd-db.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index 4f8b739c6485..fc5610603b17 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -254,7 +254,7 @@ static int cmd_db_debugfs_dump(struct seq_file *seq, void *p) if (!rsc->slv_id) break; - switch (rsc->slv_id) { + switch (le16_to_cpu(rsc->slv_id)) { case CMD_DB_HW_ARC: name = "ARC"; break; -- cgit v1.2.3-59-g8ed1b From 820f63652bb45f2a2de4f4ddf82577991fdb3d11 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Mon, 20 Apr 2020 20:35:16 +0800 Subject: firmware: qcom_scm: Remove unneeded conversion to bool The '>' expression itself is bool, no need to convert it to bool again. This fixes the following coccicheck warning: drivers/firmware/qcom_scm.c:946:25-30: WARNING: conversion to bool not needed here Reviewed-by: Bjorn Andersson Signed-off-by: Jason Yan Link: https://lore.kernel.org/r/20200420123516.7888-1-yanaijie@huawei.com Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom_scm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index 059bb0fbae9e..f714dc010109 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -943,7 +943,7 @@ bool qcom_scm_hdcp_available(void) qcom_scm_clk_disable(); - return ret > 0 ? true : false; + return ret > 0; } EXPORT_SYMBOL(qcom_scm_hdcp_available); -- cgit v1.2.3-59-g8ed1b From f49176fb13db5a1716255e8695b68feeb47cea3c Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Wed, 15 Apr 2020 10:11:59 +0200 Subject: dt-bindings: soc: qcom: apr: Use generic node names for APR services Device nodes should be named according to the class of devices they belong to. Change the suggested names of the subnodes to apr-service@, which is already in use in arch/arm64/boot/dts/qcom/sdm845.dtsi. Reviewed-by: Srinivas Kandagatla Reviewed-by: Rob Herring Cc: Srinivas Kandagatla Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20200415081159.1098-2-stephan@gerhold.net Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/soc/qcom/qcom,apr.txt | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,apr.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,apr.txt index f8fa71f5d84b..2e2f6dc351c0 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,apr.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,apr.txt @@ -65,30 +65,30 @@ which uses apr as communication between Apps and QDSP. compatible = "qcom,apr-v2"; qcom,apr-domain = ; - q6core@3 { + apr-service@3 { compatible = "qcom,q6core"; reg = ; }; - q6afe@4 { + apr-service@4 { compatible = "qcom,q6afe"; reg = ; dais { #sound-dai-cells = <1>; - hdmi@1 { - reg = <1>; + dai@1 { + reg = ; }; }; }; - q6asm@7 { + apr-service@7 { compatible = "qcom,q6asm"; reg = ; ... }; - q6adm@8 { + apr-service@8 { compatible = "qcom,q6adm"; reg = ; ... @@ -106,26 +106,26 @@ have no such dependency. qcom,glink-channels = "apr_audio_svc"; qcom,apr-domain = ; - q6core { + apr-service@3 { compatible = "qcom,q6core"; reg = ; }; - q6afe: q6afe { + q6afe: apr-service@4 { compatible = "qcom,q6afe"; reg = ; qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; ... }; - q6asm: q6asm { + q6asm: apr-service@7 { compatible = "qcom,q6asm"; reg = ; qcom,protection-domain = "tms/servreg", "msm/slpi/sensor_pd"; ... }; - q6adm: q6adm { + q6adm: apr-service@8 { compatible = "qcom,q6adm"; reg = ; qcom,protection-domain = "avs/audio", "msm/adsp/audio_pd"; -- cgit v1.2.3-59-g8ed1b From 459b1f86f1cba7de813fbc335df476c111feec22 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 14 Apr 2020 14:31:36 +0200 Subject: firmware: qcom_scm: fix bogous abuse of dma-direct internals As far as the device is concerned the dma address is the physical address. There is no need to convert it to a physical address, especially not using dma-direct internals that are not available to drivers and which will interact badly with IOMMUs. Last but not least the commit introducing it claimed to just fix a type issue, but actually changed behavior. Fixes: 6e37ccf78a532 ("firmware: qcom_scm: Use proper types for dma mappings") Reviewed-by: Bjorn Andersson Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20200414123136.441454-1-hch@lst.de Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom_scm.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index f714dc010109..0e7233a20f34 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include @@ -806,8 +805,7 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, struct qcom_scm_mem_map_info *mem_to_map; phys_addr_t mem_to_map_phys; phys_addr_t dest_phys; - phys_addr_t ptr_phys; - dma_addr_t ptr_dma; + dma_addr_t ptr_phys; size_t mem_to_map_sz; size_t dest_sz; size_t src_sz; @@ -824,10 +822,9 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, ptr_sz = ALIGN(src_sz, SZ_64) + ALIGN(mem_to_map_sz, SZ_64) + ALIGN(dest_sz, SZ_64); - ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_dma, GFP_KERNEL); + ptr = dma_alloc_coherent(__scm->dev, ptr_sz, &ptr_phys, GFP_KERNEL); if (!ptr) return -ENOMEM; - ptr_phys = dma_to_phys(__scm->dev, ptr_dma); /* Fill source vmid detail */ src = ptr; @@ -855,7 +852,7 @@ int qcom_scm_assign_mem(phys_addr_t mem_addr, size_t mem_sz, ret = __qcom_scm_assign_mem(__scm->dev, mem_to_map_phys, mem_to_map_sz, ptr_phys, src_sz, dest_phys, dest_sz); - dma_free_coherent(__scm->dev, ptr_sz, ptr, ptr_dma); + dma_free_coherent(__scm->dev, ptr_sz, ptr, ptr_phys); if (ret) { dev_err(__scm->dev, "Assign memory protection call failed %d\n", ret); -- cgit v1.2.3-59-g8ed1b From 64016bb88e8519d0f8512b9861837779c1239c0d Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 14 Apr 2020 23:21:53 -0700 Subject: soc: qcom: rpmhpd: Add SM8250 power domains Tested-by: Vinod Koul Reviewed-by: Vinod Koul Acked-by: Rob Herring Link: https://lore.kernel.org/r/20200415062154.741179-2-bjorn.andersson@linaro.org Signed-off-by: Bjorn Andersson --- .../devicetree/bindings/power/qcom,rpmpd.yaml | 1 + drivers/soc/qcom/rpmhpd.c | 19 +++++++++++++++++++ include/dt-bindings/power/qcom-rpmpd.h | 12 ++++++++++++ 3 files changed, 32 insertions(+) diff --git a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml index ba605310abeb..8058955fb3b9 100644 --- a/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml +++ b/Documentation/devicetree/bindings/power/qcom,rpmpd.yaml @@ -23,6 +23,7 @@ properties: - qcom,sc7180-rpmhpd - qcom,sdm845-rpmhpd - qcom,sm8150-rpmhpd + - qcom,sm8250-rpmhpd '#power-domain-cells': const: 1 diff --git a/drivers/soc/qcom/rpmhpd.c b/drivers/soc/qcom/rpmhpd.c index 0bb12d5870a7..e72426221a69 100644 --- a/drivers/soc/qcom/rpmhpd.c +++ b/drivers/soc/qcom/rpmhpd.c @@ -167,6 +167,24 @@ static const struct rpmhpd_desc sm8150_desc = { .num_pds = ARRAY_SIZE(sm8150_rpmhpds), }; +static struct rpmhpd *sm8250_rpmhpds[] = { + [SM8250_CX] = &sdm845_cx, + [SM8250_CX_AO] = &sdm845_cx_ao, + [SM8250_EBI] = &sdm845_ebi, + [SM8250_GFX] = &sdm845_gfx, + [SM8250_LCX] = &sdm845_lcx, + [SM8250_LMX] = &sdm845_lmx, + [SM8250_MMCX] = &sm8150_mmcx, + [SM8250_MMCX_AO] = &sm8150_mmcx_ao, + [SM8250_MX] = &sdm845_mx, + [SM8250_MX_AO] = &sdm845_mx_ao, +}; + +static const struct rpmhpd_desc sm8250_desc = { + .rpmhpds = sm8250_rpmhpds, + .num_pds = ARRAY_SIZE(sm8250_rpmhpds), +}; + /* SC7180 RPMH powerdomains */ static struct rpmhpd *sc7180_rpmhpds[] = { [SC7180_CX] = &sdm845_cx, @@ -188,6 +206,7 @@ static const struct of_device_id rpmhpd_match_table[] = { { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, + { .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc }, { } }; MODULE_DEVICE_TABLE(of, rpmhpd_match_table); diff --git a/include/dt-bindings/power/qcom-rpmpd.h b/include/dt-bindings/power/qcom-rpmpd.h index 3f74096d5a7c..dc146e44228b 100644 --- a/include/dt-bindings/power/qcom-rpmpd.h +++ b/include/dt-bindings/power/qcom-rpmpd.h @@ -28,6 +28,18 @@ #define SM8150_MMCX 9 #define SM8150_MMCX_AO 10 +/* SM8250 Power Domain Indexes */ +#define SM8250_CX 0 +#define SM8250_CX_AO 1 +#define SM8250_EBI 2 +#define SM8250_GFX 3 +#define SM8250_LCX 4 +#define SM8250_LMX 5 +#define SM8250_MMCX 6 +#define SM8250_MMCX_AO 7 +#define SM8250_MX 8 +#define SM8250_MX_AO 9 + /* SC7180 Power Domain Indexes */ #define SC7180_CX 0 #define SC7180_CX_AO 1 -- cgit v1.2.3-59-g8ed1b From 35bb4b22f606c0cc8eedf567313adc18161b1af4 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Fri, 17 Apr 2020 14:15:47 -0700 Subject: soc: qcom: rpmh: Dirt can only make you dirtier, not cleaner Adding an item into the cache should never be able to make the cache cleaner. Use "|=" rather than "=" to update the dirty flag. Reviewed-by: Matthias Kaehlcke Reviewed-by: Maulik Shah Thanks, Maulik Reviewed-by: Bjorn Andersson Fixes: bb7000677a1b ("soc: qcom: rpmh: Update dirty flag only when data changes") Reported-by: Stephen Boyd Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20200417141531.1.Ia4b74158497213eabad7c3d474c50bfccb3f342e@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 3abbb08cd6e1..d1626a1328d7 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -151,10 +151,10 @@ existing: 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; + 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); -- cgit v1.2.3-59-g8ed1b From 02d8ecc18b8f392389ac9e7b785b0230ecb80833 Mon Sep 17 00:00:00 2001 From: Sibi Sankar Date: Wed, 15 Apr 2020 11:59:55 +0530 Subject: soc: qcom: pdr: Remove impossible error condition The patch fbe639b44a82: "soc: qcom: Introduce Protection Domain Restart helpers" leads to the following static checker warning: drivers/soc/qcom/pdr_interface.c:158 pdr_register_listener() '(resp.curr_state < (-((~0 >> 1)) - 1)) => (s32min-s32max < s32min)' These are casted to int so they can't be outside of int range. Fixes: fbe639b44a82 ("soc: qcom: Introduce Protection Domain Restart helpers") Reported-by: Dan Carpenter Signed-off-by: Sibi Sankar Link: https://lore.kernel.org/r/20200415062955.21439-1-sibis@codeaurora.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/pdr_interface.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/soc/qcom/pdr_interface.c b/drivers/soc/qcom/pdr_interface.c index 17ad3b8698e1..bdcf16f88a97 100644 --- a/drivers/soc/qcom/pdr_interface.c +++ b/drivers/soc/qcom/pdr_interface.c @@ -155,10 +155,6 @@ static int pdr_register_listener(struct pdr_handle *pdr, return ret; } - if ((int)resp.curr_state < INT_MIN || (int)resp.curr_state > INT_MAX) - pr_err("PDR: %s notification state invalid: 0x%x\n", - pds->service_path, resp.curr_state); - pds->state = resp.curr_state; return 0; -- cgit v1.2.3-59-g8ed1b From ce187859cea2004d9520de76948d6fc3e2f4b4bb Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sun, 26 Apr 2020 22:42:02 -0700 Subject: soc: qcom: aoss: Add SM8250 compatible Add SM8250 compatible to the qcom_aoss binding and driver. Acked-by: Rob Herring Signed-off-by: Bjorn Andersson Link: https://lore.kernel.org/r/20200427054202.2822144-1-bjorn.andersson@linaro.org Signed-off-by: Bjorn Andersson --- Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt | 1 + drivers/soc/qcom/qcom_aoss.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt index 4fc571e78f01..953add19e937 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.txt @@ -19,6 +19,7 @@ power-domains. "qcom,sc7180-aoss-qmp" "qcom,sdm845-aoss-qmp" "qcom,sm8150-aoss-qmp" + "qcom,sm8250-aoss-qmp" - reg: Usage: required diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index f43a2e07ee83..ed2c687c16b3 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -599,6 +599,7 @@ static const struct of_device_id qmp_dt_match[] = { { .compatible = "qcom,sc7180-aoss-qmp", }, { .compatible = "qcom,sdm845-aoss-qmp", }, { .compatible = "qcom,sm8150-aoss-qmp", }, + { .compatible = "qcom,sm8250-aoss-qmp", }, {} }; MODULE_DEVICE_TABLE(of, qmp_dt_match); -- cgit v1.2.3-59-g8ed1b From 8f09210d89e7a6feb07699b4e73a7c34f39554e4 Mon Sep 17 00:00:00 2001 From: Vincent Knecht Date: Mon, 11 May 2020 23:27:33 +0200 Subject: soc: qcom: socinfo: add msm8936/39 and apq8036/39 soc ids This patch adds missing SoC IDs for MSM8936/39 and their APQ variants. Signed-off-by: Vincent Knecht Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20200511212733.214464-1-konradybcio@gmail.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 08a4b8ae1764..5983c6ffb078 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -188,6 +188,10 @@ static const struct soc_id soc_id[] = { { 216, "MSM8674PRO" }, { 217, "MSM8974-AA" }, { 218, "MSM8974-AB" }, + { 233, "MSM8936" }, + { 239, "MSM8939" }, + { 240, "APQ8036" }, + { 241, "APQ8039" }, { 246, "MSM8996" }, { 247, "APQ8016" }, { 248, "MSM8216" }, -- cgit v1.2.3-59-g8ed1b From faa0c1f106efd6b7f61e9df95e27cc748a7123ec Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 15 Apr 2020 10:00:27 -0700 Subject: soc: qcom: rpmh-rsc: Factor "tcs_reg_addr" and "tcs_cmd_addr" calculation We can make some of the register access functions more readable by factoring out the calculations a little bit. Suggested-by: Joe Perches Signed-off-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200415095953.v3.1.Ic70288f256ff0be65cac6a600367212dfe39f6c9@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 41 ++++++++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index a9e15699f55f..ce39d8399312 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -137,36 +137,47 @@ * +---------------------------------------------------+ */ -static u32 read_tcs_cmd(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) +static inline void __iomem * +tcs_reg_addr(const struct rsc_drv *drv, int reg, int tcs_id) { - return readl_relaxed(drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg + - RSC_DRV_CMD_OFFSET * cmd_id); + return drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg; } -static u32 read_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id) +static inline void __iomem * +tcs_cmd_addr(const struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) { - return readl_relaxed(drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg); + return tcs_reg_addr(drv, reg, tcs_id) + RSC_DRV_CMD_OFFSET * cmd_id; } -static void write_tcs_cmd(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id, - u32 data) +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) { - writel_relaxed(data, drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg + - RSC_DRV_CMD_OFFSET * cmd_id); + return readl_relaxed(tcs_reg_addr(drv, reg, tcs_id)); } -static void write_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, u32 data) +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, drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg); + writel_relaxed(data, tcs_reg_addr(drv, reg, tcs_id)); } -static void write_tcs_reg_sync(struct rsc_drv *drv, int reg, int tcs_id, +static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int tcs_id, u32 data) { - writel(data, drv->tcs_base + RSC_DRV_TCS_OFFSET * tcs_id + reg); + writel(data, tcs_reg_addr(drv, reg, tcs_id)); for (;;) { - if (data == readl(drv->tcs_base + reg + - RSC_DRV_TCS_OFFSET * tcs_id)) + if (data == readl(tcs_reg_addr(drv, reg, tcs_id))) break; udelay(1); } -- cgit v1.2.3-59-g8ed1b From 91160150aba03d0c173b3f5c859a795cc701bb8d Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 15 Apr 2020 10:00:28 -0700 Subject: soc: qcom: rpmh-rsc: Timeout after 1 second in write_tcs_reg_sync() If our data still isn't there after 1 second, shout and give up. Reported-by: Joe Perches Signed-off-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200415095953.v3.2.I8550512081c89ec7a545018a7d2d9418a27c1a7a@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index ce39d8399312..e09d1ada0cd2 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -175,12 +176,13 @@ static void write_tcs_reg(const struct rsc_drv *drv, int reg, int tcs_id, static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int tcs_id, u32 data) { + u32 new_data; + writel(data, tcs_reg_addr(drv, reg, tcs_id)); - for (;;) { - if (data == readl(tcs_reg_addr(drv, reg, tcs_id))) - break; - udelay(1); - } + if (readl_poll_timeout_atomic(tcs_reg_addr(drv, reg, tcs_id), new_data, + new_data == data, 1, USEC_PER_SEC)) + pr_err("%s: error writing %#x to %d:%#x\n", drv->name, + data, tcs_id, reg); } /** -- cgit v1.2.3-59-g8ed1b From c20977721631602eafb263fd89fbafaa11de1ebe Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Fri, 8 May 2020 16:08:05 -0500 Subject: firmware: qcom_scm-legacy: Replace zero-length array with flexible-array The current codebase makes use of the zero-length array language extension to the C90 standard, but the preferred mechanism to declare variable-length types such as these ones is a flexible array member[1][2], introduced in C99: struct foo { int stuff; struct boo array[]; }; By making use of the mechanism above, we will get a compiler warning in case the flexible array does not occur last in the structure, which will help us prevent some kind of undefined behavior bugs from being inadvertently introduced[3] to the codebase from now on. Also, notice that, dynamic memory allocations won't be affected by this change: "Flexible array members have incomplete type, and so the sizeof operator may not be applied. As a quirk of the original implementation of zero-length arrays, sizeof evaluates to zero."[1] sizeof(flexible-array-member) triggers a warning because flexible array members have incomplete type[1]. There are some instances of code in which the sizeof operator is being incorrectly/erroneously applied to zero-length arrays and the result is zero. Such instances may be hiding some bugs. So, this work (flexible-array member conversions) will also help to get completely rid of those sorts of issues. This issue was found with the help of Coccinelle. [1] https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html [2] https://github.com/KSPP/linux/issues/21 [3] commit 76497732932f ("cxgb3/l2t: Fix undefined behaviour") Reviewed-by: Jeffrey Hugo Signed-off-by: Gustavo A. R. Silva Link: https://lore.kernel.org/r/20200508210805.GA24170@embeddedor Signed-off-by: Bjorn Andersson --- drivers/firmware/qcom_scm-legacy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/qcom_scm-legacy.c b/drivers/firmware/qcom_scm-legacy.c index 8532e7c78ef7..eba6b60bfb61 100644 --- a/drivers/firmware/qcom_scm-legacy.c +++ b/drivers/firmware/qcom_scm-legacy.c @@ -56,7 +56,7 @@ struct scm_legacy_command { __le32 buf_offset; __le32 resp_hdr_offset; __le32 id; - __le32 buf[0]; + __le32 buf[]; }; /** -- cgit v1.2.3-59-g8ed1b From 1143c36656b8dccf0ece93053502a147d3c60961 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 4 May 2020 10:50:15 -0700 Subject: soc: qcom: rpmh-rsc: Correctly ignore CPU_CLUSTER_PM notifications Our switch statement doesn't have entries for CPU_CLUSTER_PM_ENTER, CPU_CLUSTER_PM_ENTER_FAILED, and CPU_CLUSTER_PM_EXIT and doesn't have a default. This means that we'll try to do a flush in those cases but we won't necessarily be the last CPU down. That's not so ideal since our (lack of) locking assumes we're on the last CPU. Luckily this isn't as big a problem as you'd think since (at least on the SoC I tested) we don't get these notifications except on full system suspend. ...and on full system suspend we get them on the last CPU down. That means that the worst problem we hit is flushing twice. Still, it's good to make it correct. Reviewed-by: Stephen Boyd Fixes: 985427f997b6 ("soc: qcom: rpmh: Invoke rpmh_flush() for dirty caches") Reported-by: Stephen Boyd Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20200504104917.v6.1.Ic7096b3b9b7828cdd41cd5469a6dee5eb6abf549@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index e09d1ada0cd2..5a2659df98da 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -819,6 +819,9 @@ static int rpmh_rsc_cpu_pm_callback(struct notifier_block *nfb, case CPU_PM_EXIT: cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm); goto exit; + default: + ret = NOTIFY_DONE; + goto exit; } ret = rpmh_rsc_ctrlr_is_busy(drv); -- cgit v1.2.3-59-g8ed1b From c45def5d804abaa48c205f8ba5cd52bfbeeae70c Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 4 May 2020 10:50:16 -0700 Subject: soc: qcom: rpmh-rsc: We aren't notified of our own failure w/ NOTIFY_BAD When a PM Notifier returns NOTIFY_BAD it doesn't get called with CPU_PM_ENTER_FAILED. It only get called for CPU_PM_ENTER_FAILED if someone else (further down the notifier chain) returns NOTIFY_BAD. Handle this case by taking our CPU out of the list of ones that have entered PM. Without this it's possible we could detect that the last CPU went down (and we would flush) even if some CPU was alive. That's not good since our flushing routines currently assume they're running on the last CPU for mutual exclusion. Fixes: 985427f997b6 ("soc: qcom: rpmh: Invoke rpmh_flush() for dirty caches") Signed-off-by: Douglas Anderson Reviewed-by: Maulik Shah Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200504104917.v6.2.I1927d1bca2569a27b2d04986baf285027f0818a2@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-rsc.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 5a2659df98da..a5659a7306e4 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -837,6 +837,10 @@ static int rpmh_rsc_cpu_pm_callback(struct notifier_block *nfb, ret = NOTIFY_OK; exit: + if (ret == NOTIFY_BAD) + /* We won't be called w/ CPU_PM_ENTER_FAILED */ + cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm); + spin_unlock(&drv->pm_lock); return ret; } -- cgit v1.2.3-59-g8ed1b From b5945214b76a1f22929481724ffd448000ede914 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 4 May 2020 10:50:17 -0700 Subject: kernel/cpu_pm: Fix uninitted local in cpu_pm cpu_pm_notify() is basically a wrapper of notifier_call_chain(). notifier_call_chain() doesn't initialize *nr_calls to 0 before it starts incrementing it--presumably it's up to the callers to do this. Unfortunately the callers of cpu_pm_notify() don't init *nr_calls. This potentially means you could get too many or two few calls to CPU_PM_ENTER_FAILED or CPU_CLUSTER_PM_ENTER_FAILED depending on the luck of the stack. Let's fix this. Fixes: ab10023e0088 ("cpu_pm: Add cpu power management notifiers") Cc: stable@vger.kernel.org Cc: Rafael J. Wysocki Reviewed-by: Stephen Boyd Reviewed-by: Greg Kroah-Hartman Signed-off-by: Douglas Anderson Link: https://lore.kernel.org/r/20200504104917.v6.3.I2d44fc0053d019f239527a4e5829416714b7e299@changeid Signed-off-by: Bjorn Andersson --- kernel/cpu_pm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/cpu_pm.c b/kernel/cpu_pm.c index cbca6879ab7d..44a259338e33 100644 --- a/kernel/cpu_pm.c +++ b/kernel/cpu_pm.c @@ -80,7 +80,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier); */ int cpu_pm_enter(void) { - int nr_calls; + int nr_calls = 0; int ret = 0; ret = cpu_pm_notify(CPU_PM_ENTER, -1, &nr_calls); @@ -131,7 +131,7 @@ EXPORT_SYMBOL_GPL(cpu_pm_exit); */ int cpu_cluster_pm_enter(void) { - int nr_calls; + int nr_calls = 0; int ret = 0; ret = cpu_pm_notify(CPU_CLUSTER_PM_ENTER, -1, &nr_calls); -- cgit v1.2.3-59-g8ed1b From 555701a45f146673c8961f084b6880c637d41129 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 4 May 2020 10:50:18 -0700 Subject: soc: qcom: rpmh-rsc: Simplify locking by eliminating the per-TCS lock The rpmh-rsc code had both a driver-level lock (sometimes referred to in comments as drv->lock) and a lock per-TCS. The idea was supposed to be that there would be times where you could get by with just locking a TCS lock and therefor other RPMH users wouldn't be blocked. The above didn't work out so well. Looking at tcs_write() the bigger drv->lock was held for most of the function anyway. Only the __tcs_buffer_write() and __tcs_set_trigger() calls were called without holding the drv->lock. It actually turns out that in tcs_write() we don't need to hold the drv->lock for those function calls anyway even if the per-TCS lock isn't there anymore. From the newly added comments in the code, this is 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(). Thus, from a tcs_write() point of view, the per-TCS lock was useless. Looking at rpmh_rsc_write_ctrl_data(), only the per-TCS lock was held. It turns out, though, that this function already needs to be called with the equivalent of the drv->lock held anyway (we either need to hold drv->lock as we will in a future patch or we need to know no other CPUs could be running as happens today). Specifically rpmh_rsc_write_ctrl_data() might be writing to a TCS that has been borrowed for writing an active transation but it never checks this. Let's eliminate this extra overhead and avoid possible AB BA locking headaches. Suggested-by: Maulik Shah Signed-off-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200504104917.v6.4.Ib8dccfdb10bf6b1fb1d600ca1c21d9c0db1ef746@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 13 +++------- drivers/soc/qcom/rpmh-rsc.c | 55 ++++++++++++++++++---------------------- 2 files changed, 28 insertions(+), 40 deletions(-) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index dba8510c0669..1f2857b3f38e 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -28,7 +28,6 @@ struct rsc_drv; * @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. - * @lock: Lock for synchronizing this TCS writes. * @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). @@ -48,7 +47,6 @@ struct tcs_group { u32 offset; int num_tcs; int ncpt; - spinlock_t lock; const struct tcs_request *req[MAX_TCS_PER_TYPE]; DECLARE_BITMAP(slots, MAX_TCS_SLOTS); }; @@ -103,14 +101,9 @@ struct rpmh_ctrlr { * @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 both the lock in this struct and the - * tcs_lock for the TCS in order to mark a TCS as - * in-use, but you only need the lock in this structure - * (aka the drv->lock) to mark one freed. - * @lock: Synchronize state of the controller. If you will be - * grabbing this lock and a tcs_lock at the same time, - * grab the tcs_lock first so we always have a - * consistent lock ordering. + * must hold the lock in this struct (AKA drv->lock) in + * order to update this. + * @lock: Synchronize state of the controller. * @pm_lock: Synchronize during PM notifications. * Used when solver mode is not present. * @client: Handle to the DRV's client. diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index a5659a7306e4..fb142dfbb237 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -192,11 +192,7 @@ static void write_tcs_reg_sync(const struct rsc_drv *drv, int reg, int tcs_id, * * Returns true if nobody has claimed this TCS (by setting tcs_in_use). * - * Context: Must be called with the drv->lock held or the tcs_lock for the TCS - * being tested. If only the tcs_lock is held then it is possible that - * this function will return that a tcs is still busy when it has been - * recently been freed but it will never return free when a TCS is - * actually in use. + * Context: Must be called with the drv->lock held. * * Return: true if the given TCS is free. */ @@ -255,8 +251,6 @@ void rpmh_rsc_invalidate(struct rsc_drv *drv) * This is normally pretty straightforward except if we are trying to send * an ACTIVE_ONLY message but don't have any active_only TCSes. * - * Called without drv->lock held and with no tcs_lock locks held. - * * Return: A pointer to a tcs_group or an ERR_PTR. */ static struct tcs_group *get_tcs_for_msg(struct rsc_drv *drv, @@ -594,24 +588,19 @@ static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg) if (IS_ERR(tcs)) return PTR_ERR(tcs); - spin_lock_irqsave(&tcs->lock, flags); - spin_lock(&drv->lock); + spin_lock_irqsave(&drv->lock, flags); /* * 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) { - spin_unlock(&drv->lock); - goto done_write; - } + if (ret) + goto unlock; - tcs_id = find_free_tcs(tcs); - if (tcs_id < 0) { - ret = tcs_id; - spin_unlock(&drv->lock); - goto done_write; - } + ret = find_free_tcs(tcs); + if (ret < 0) + goto unlock; + tcs_id = ret; tcs->req[tcs_id - tcs->offset] = msg; set_bit(tcs_id, drv->tcs_in_use); @@ -625,13 +614,22 @@ static int tcs_write(struct rsc_drv *drv, const struct tcs_request *msg) write_tcs_reg_sync(drv, RSC_DRV_CMD_WAIT_FOR_CMPL, tcs_id, 0); enable_tcs_irq(drv, tcs_id, true); } - spin_unlock(&drv->lock); + spin_unlock_irqrestore(&drv->lock, flags); + /* + * These two can be done after the lock is released because: + * - We marked "tcs_in_use" under lock. + * - Once "tcs_in_use" has been marked nobody else could be writing + * to these registers until the interrupt goes off. + * - The interrupt can't go off until we trigger w/ the last line + * of __tcs_set_trigger() below. + */ __tcs_buffer_write(drv, tcs_id, 0, msg); __tcs_set_trigger(drv, tcs_id, true); -done_write: - spin_unlock_irqrestore(&tcs->lock, flags); + return 0; +unlock: + spin_unlock_irqrestore(&drv->lock, flags); return ret; } @@ -686,8 +684,6 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) * Only for use on sleep/wake TCSes since those are the only ones we maintain * tcs->slots for. * - * Must be called with the tcs_lock for the group held. - * * Return: -ENOMEM if there was no room, else 0. */ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, @@ -722,25 +718,25 @@ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, * This should only be called for 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; - unsigned long flags; int ret; tcs = get_tcs_for_msg(drv, msg); if (IS_ERR(tcs)) return PTR_ERR(tcs); - spin_lock_irqsave(&tcs->lock, flags); /* 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); - spin_unlock_irqrestore(&tcs->lock, flags); return ret; } @@ -769,8 +765,8 @@ static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) * should be checked for not busy, because we used wake TCSes for * active requests in this case. * - * Since this is called from the last cpu, need not take drv or tcs - * lock before checking tcs_is_free(). + * Since this is called from the last cpu, need not take drv->lock + * before checking tcs_is_free(). */ if (!tcs->num_tcs) tcs = &drv->tcs[WAKE_TCS]; @@ -899,7 +895,6 @@ static int rpmh_probe_tcs_config(struct platform_device *pdev, tcs->type = tcs_cfg[i].type; tcs->num_tcs = tcs_cfg[i].n; tcs->ncpt = ncpt; - spin_lock_init(&tcs->lock); if (!tcs->num_tcs || tcs->type == CONTROL_TCS) continue; -- cgit v1.2.3-59-g8ed1b From d2a8cfc6f320263b90ca523590a339661d0f4fae Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Mon, 4 May 2020 10:50:19 -0700 Subject: soc: qcom: rpmh-rsc: Remove the pm_lock It has been postulated that the pm_lock is bad for performance because a CPU currently running rpmh_flush() could block other CPUs from coming out of idle. Similarly CPUs coming out of / going into idle all need to contend with each other for the spinlock just to update the variable tracking who's in PM. Let's optimize this a bit. Specifically: - Use a count rather than a bitmask. This is faster to access and also means we can use the atomic_inc_return() function to really detect who the last one to enter PM was. - Accept that it's OK if we race and are doing the flush (because we think we're last) while another CPU is coming out of idle. As long as we block that CPU if/when it tries to do an active-only transfer we're OK. Signed-off-by: Douglas Anderson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20200504104917.v6.5.I295cb72bc5334a2af80313cbe97cb5c9dcb1442c@changeid Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/rpmh-internal.h | 11 +++--- drivers/soc/qcom/rpmh-rsc.c | 75 ++++++++++++++++++++++++---------------- drivers/soc/qcom/rpmh.c | 25 +++++++++----- 3 files changed, 67 insertions(+), 44 deletions(-) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 1f2857b3f38e..ef60e790a750 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -95,7 +95,7 @@ struct rpmh_ctrlr { * @num_tcs: Number of TCSes in this DRV. * @rsc_pm: CPU PM notifier for controller. * Used when solver mode is not present. - * @cpus_entered_pm: CPU mask for cpus in idle power collapse. + * @cpus_in_pm: Number of CPUs not in idle power collapse. * Used when solver mode is not present. * @tcs: TCS groups. * @tcs_in_use: S/W state of the TCS; only set for ACTIVE_ONLY @@ -103,9 +103,9 @@ struct rpmh_ctrlr { * 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. - * @pm_lock: Synchronize during PM notifications. - * Used when solver mode is not present. + * @lock: Synchronize state of the controller. If RPMH's cache + * lock will also be held, the order is: drv->lock then + * cache_lock. * @client: Handle to the DRV's client. */ struct rsc_drv { @@ -114,11 +114,10 @@ struct rsc_drv { int id; int num_tcs; struct notifier_block rsc_pm; - struct cpumask cpus_entered_pm; + atomic_t cpus_in_pm; struct tcs_group tcs[TCS_TYPE_NR]; DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR); spinlock_t lock; - spinlock_t pm_lock; struct rpmh_ctrlr client; }; diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index fb142dfbb237..237d7d5cc8a8 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -750,6 +750,8 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg) * 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 @@ -764,9 +766,6 @@ static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) * 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. - * - * Since this is called from the last cpu, need not take drv->lock - * before checking tcs_is_free(). */ if (!tcs->num_tcs) tcs = &drv->tcs[WAKE_TCS]; @@ -801,43 +800,62 @@ static int rpmh_rsc_cpu_pm_callback(struct notifier_block *nfb, { struct rsc_drv *drv = container_of(nfb, struct rsc_drv, rsc_pm); int ret = NOTIFY_OK; - - spin_lock(&drv->pm_lock); + int cpus_in_pm; switch (action) { case CPU_PM_ENTER: - cpumask_set_cpu(smp_processor_id(), &drv->cpus_entered_pm); - - if (!cpumask_equal(&drv->cpus_entered_pm, cpu_online_mask)) - goto exit; + 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: - cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm); - goto exit; + atomic_dec(&drv->cpus_in_pm); + return NOTIFY_OK; default: - ret = NOTIFY_DONE; - goto exit; + return NOTIFY_DONE; } - ret = rpmh_rsc_ctrlr_is_busy(drv); - if (ret) { - ret = NOTIFY_BAD; - goto exit; + /* + * 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; } - ret = rpmh_flush(&drv->client); - if (ret) - ret = NOTIFY_BAD; - else - ret = NOTIFY_OK; - -exit: - if (ret == NOTIFY_BAD) - /* We won't be called w/ CPU_PM_ENTER_FAILED */ - cpumask_clear_cpu(smp_processor_id(), &drv->cpus_entered_pm); + 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); + } - spin_unlock(&drv->pm_lock); return ret; } @@ -980,7 +998,6 @@ static int rpmh_rsc_probe(struct platform_device *pdev) solver_config = solver_config >> DRV_HW_SOLVER_SHIFT; if (!solver_config) { drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback; - spin_lock_init(&drv->pm_lock); cpu_pm_register_notifier(&drv->rsc_pm); } diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index d1626a1328d7..f2b5b46ccd1f 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -435,9 +435,6 @@ static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, * * @ctrlr: Controller making request to flush cached data * - * This function is called from sleep code on the last CPU - * (thus no spinlock needed). - * * Return: * * 0 - Success * * Error code - Otherwise @@ -445,13 +442,21 @@ static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, int rpmh_flush(struct rpmh_ctrlr *ctrlr) { struct cache_req *p; - int ret; + 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"); - return 0; + goto exit; } /* Invalidate the TCSes first to avoid stale data */ @@ -460,7 +465,7 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) /* First flush the cached batch requests */ ret = flush_batch(ctrlr); if (ret) - return ret; + goto exit; list_for_each_entry(p, &ctrlr->cache, list) { if (!is_req_valid(p)) { @@ -471,16 +476,18 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) ret = send_single(ctrlr, RPMH_SLEEP_STATE, p->addr, p->sleep_val); if (ret) - return ret; + goto exit; ret = send_single(ctrlr, RPMH_WAKE_ONLY_STATE, p->addr, p->wake_val); if (ret) - return ret; + goto exit; } ctrlr->dirty = false; - return 0; +exit: + spin_unlock(&ctrlr->cache_lock); + return ret; } /** -- cgit v1.2.3-59-g8ed1b From 1f7a3eb785e4a4e196729cd3d5ec97bd5f9f2940 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sun, 17 May 2020 23:10:41 -0700 Subject: Revert "soc: qcom: rpmh: Allow RPMH driver to be loaded as a module" Attempting to compile rpmh-rsc.c as a module with TRACING enabled causes a build error as no _rcuidle function is generated for tracepoints when CONFIG_MODULE is set. Attempts has been made, but no resolution has been agreed upon, so lets revert this commit for now. This reverts commit 1d3c6f86fd3f8b88c707f56d8c3f94e014b40e83. Reported-by: Stephen Rothwell Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 2 +- drivers/soc/qcom/rpmh-rsc.c | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 0d0123f6ec0a..19332ea40234 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -107,7 +107,7 @@ config QCOM_RMTFS_MEM Say y here if you intend to boot the modem remoteproc. config QCOM_RPMH - tristate "Qualcomm RPM-Hardened (RPMH) Communication" + bool "Qualcomm RPM-Hardened (RPMH) Communication" depends on ARCH_QCOM && ARM64 || COMPILE_TEST help Support for communication with the hardened-RPM blocks in diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 237d7d5cc8a8..076fd27f3081 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -1018,7 +1017,6 @@ 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, @@ -1033,6 +1031,3 @@ static int __init rpmh_driver_init(void) return platform_driver_register(&rpmh_driver); } arch_initcall(rpmh_driver_init); - -MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Driver"); -MODULE_LICENSE("GPL v2"); -- cgit v1.2.3-59-g8ed1b