From b65a3fa38dbb5c84f360a48f0d5da3aed0011964 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Fri, 19 Jan 2024 09:13:37 -0800 Subject: soc: qcom: aoss: Mark qmp_send() __printf() As reported by lkp, qmp_send() would benefit from a __printf() marker to allow the compiler to further validate the passed parameters, fix this. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202401100855.UYl3HPPt-lkp@intel.com/ Signed-off-by: Bjorn Andersson Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20240119-aoss-printf-annotation-v1-1-27e2ceb8937a@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_aoss.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index aff0cfb71482..9d7a74767008 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -214,7 +214,7 @@ static bool qmp_message_empty(struct qmp *qmp) * * Return: 0 on success, negative errno on failure */ -int qmp_send(struct qmp *qmp, const char *fmt, ...) +int __printf(2, 3) qmp_send(struct qmp *qmp, const char *fmt, ...) { char buf[QMP_MSG_LEN]; long time_left; -- cgit v1.2.3-58-ga151 From b8631e4b96bb4b0c4f5a57d5767afe5e56eba075 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Dec 2023 20:31:31 +0100 Subject: memory: emif: Simplify code handling CONFIG_DEBUG_FS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of using #ifdef make use of IS_ENABLED(). According to bloat-o-meter this patch doesn't change code sizes with CONFIG_DEBUG_FS=n. Also change emif_debugfs_init() to return void. The only caller doesn't check the return value anyhow. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/2c7dd66d4a101b74b16e7e1839e30f3c88510c33.1702829744.git.u.kleine-koenig@pengutronix.de Signed-off-by: Krzysztof Kozlowski --- drivers/memory/emif.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c index 434982545be6..f5e654abe04c 100644 --- a/drivers/memory/emif.c +++ b/drivers/memory/emif.c @@ -72,7 +72,6 @@ static DEFINE_SPINLOCK(emif_lock); static unsigned long irq_state; static LIST_HEAD(device_list); -#ifdef CONFIG_DEBUG_FS static void do_emif_regdump_show(struct seq_file *s, struct emif_data *emif, struct emif_regs *regs) { @@ -140,31 +139,24 @@ static int emif_mr4_show(struct seq_file *s, void *unused) DEFINE_SHOW_ATTRIBUTE(emif_mr4); -static int __init_or_module emif_debugfs_init(struct emif_data *emif) +static void __init_or_module emif_debugfs_init(struct emif_data *emif) { - emif->debugfs_root = debugfs_create_dir(dev_name(emif->dev), NULL); - debugfs_create_file("regcache_dump", S_IRUGO, emif->debugfs_root, emif, - &emif_regdump_fops); - debugfs_create_file("mr4", S_IRUGO, emif->debugfs_root, emif, - &emif_mr4_fops); - return 0; + if (IS_ENABLED(CONFIG_DEBUG_FS)) { + emif->debugfs_root = debugfs_create_dir(dev_name(emif->dev), NULL); + debugfs_create_file("regcache_dump", S_IRUGO, emif->debugfs_root, emif, + &emif_regdump_fops); + debugfs_create_file("mr4", S_IRUGO, emif->debugfs_root, emif, + &emif_mr4_fops); + } } static void __exit emif_debugfs_exit(struct emif_data *emif) { - debugfs_remove_recursive(emif->debugfs_root); - emif->debugfs_root = NULL; -} -#else -static inline int __init_or_module emif_debugfs_init(struct emif_data *emif) -{ - return 0; -} - -static inline void __exit emif_debugfs_exit(struct emif_data *emif) -{ + if (IS_ENABLED(CONFIG_DEBUG_FS)) { + debugfs_remove_recursive(emif->debugfs_root); + emif->debugfs_root = NULL; + } } -#endif /* * Get bus width used by EMIF. Note that this may be different from the -- cgit v1.2.3-58-ga151 From d10e03cf9a4d78c67ef779dab5a5f4fb94fb835e Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Sun, 17 Dec 2023 20:31:32 +0100 Subject: memory: emif: Simplify code handling CONFIG_OF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first thing that of_get_memory_device_details() does is calling of_parse_phandle(). With CONFIG_OF=n this returns NULL in a static inline function. So the compiler can determine that of_get_memory_device_details() also returns NULL. bloat-o-meter confirms that this patch has no effects on the size of the generated code for CONFIG_OF=n builds. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/9d53683c34a730c8579a1468b643b11d1379106e.1702829744.git.u.kleine-koenig@pengutronix.de Signed-off-by: Krzysztof Kozlowski --- drivers/memory/emif.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c index f5e654abe04c..c5a24501db9b 100644 --- a/drivers/memory/emif.c +++ b/drivers/memory/emif.c @@ -826,7 +826,6 @@ static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs, return valid; } -#if defined(CONFIG_OF) static void __init_or_module of_get_custom_configs(struct device_node *np_emif, struct emif_data *emif) { @@ -983,15 +982,6 @@ out: return emif; } -#else - -static struct emif_data * __init_or_module of_get_memory_device_details( - struct device_node *np_emif, struct device *dev) -{ - return NULL; -} -#endif - static struct emif_data *__init_or_module get_device_details( struct platform_device *pdev) { -- cgit v1.2.3-58-ga151 From 4abcc42a3f874b515c27bf2d33f1c1d567afcf7f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 22 Jan 2024 21:33:29 -0800 Subject: soc: qcom: smp2p: fix all kernel-doc warnings Use the documented notation for nested struct members. Add a Return: comment for qcom_smp2p_intr(). These changes prevent these kernel-doc warnings: smp2p.c:78: warning: Excess struct member 'name' description in 'smp2p_smem_item' smp2p.c:78: warning: Excess struct member 'value' description in 'smp2p_smem_item' smp2p.c:280: warning: No description found for return value of 'qcom_smp2p_intr' Signed-off-by: Randy Dunlap Cc: Bjorn Andersson Cc: Konrad Dybcio Cc: linux-arm-msm@vger.kernel.org Cc: Jeff Johnson Link: https://lore.kernel.org/r/20240123053329.12893-1-rdunlap@infradead.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/smp2p.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/smp2p.c b/drivers/soc/qcom/smp2p.c index 914b2246148f..a21241cbeec7 100644 --- a/drivers/soc/qcom/smp2p.c +++ b/drivers/soc/qcom/smp2p.c @@ -58,8 +58,8 @@ * @valid_entries: number of allocated entries * @flags: * @entries: individual communication entries - * @name: name of the entry - * @value: content of the entry + * @entries.name: name of the entry + * @entries.value: content of the entry */ struct smp2p_smem_item { u32 magic; @@ -275,6 +275,8 @@ static void qcom_smp2p_notify_in(struct qcom_smp2p *smp2p) * * Handle notifications from the remote side to handle newly allocated entries * or any changes to the state bits of existing entries. + * + * Return: %IRQ_HANDLED */ static irqreturn_t qcom_smp2p_intr(int irq, void *data) { -- cgit v1.2.3-58-ga151 From f0b203bf9bbc89d3230d6a3d6254d11f7a4b6064 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 23 Jan 2024 18:08:47 +0100 Subject: memory: emif: Drop usage of platform_driver_probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are considerations to drop platform_driver_probe() as a concept that isn't relevant any more today. It comes with an added complexity that makes many users hold it wrong. (E.g. this driver should have better used __init instead of __init_or_module to mark functions only relevant to .probe() and mark the driver struct with __refdata.) This fixes a W=1 build warning: WARNING: modpost: drivers/memory/emif: section mismatch in reference: emif_driver+0x4 (section: .data) -> emif_remove (section: .exit.text) Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20240123170846.1362597-2-u.kleine-koenig@pengutronix.de Signed-off-by: Krzysztof Kozlowski --- drivers/memory/emif.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/emif.c b/drivers/memory/emif.c index c5a24501db9b..8c5ad5c025fa 100644 --- a/drivers/memory/emif.c +++ b/drivers/memory/emif.c @@ -139,7 +139,7 @@ static int emif_mr4_show(struct seq_file *s, void *unused) DEFINE_SHOW_ATTRIBUTE(emif_mr4); -static void __init_or_module emif_debugfs_init(struct emif_data *emif) +static void emif_debugfs_init(struct emif_data *emif) { if (IS_ENABLED(CONFIG_DEBUG_FS)) { emif->debugfs_root = debugfs_create_dir(dev_name(emif->dev), NULL); @@ -150,7 +150,7 @@ static void __init_or_module emif_debugfs_init(struct emif_data *emif) } } -static void __exit emif_debugfs_exit(struct emif_data *emif) +static void emif_debugfs_exit(struct emif_data *emif) { if (IS_ENABLED(CONFIG_DEBUG_FS)) { debugfs_remove_recursive(emif->debugfs_root); @@ -671,7 +671,7 @@ static void disable_and_clear_all_interrupts(struct emif_data *emif) clear_all_interrupts(emif); } -static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq) +static int setup_interrupts(struct emif_data *emif, u32 irq) { u32 interrupts, type; void __iomem *base = emif->base; @@ -702,7 +702,7 @@ static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq) } -static void __init_or_module emif_onetime_settings(struct emif_data *emif) +static void emif_onetime_settings(struct emif_data *emif) { u32 pwr_mgmt_ctrl, zq, temp_alert_cfg; void __iomem *base = emif->base; @@ -826,7 +826,7 @@ static int is_custom_config_valid(struct emif_custom_configs *cust_cfgs, return valid; } -static void __init_or_module of_get_custom_configs(struct device_node *np_emif, +static void of_get_custom_configs(struct device_node *np_emif, struct emif_data *emif) { struct emif_custom_configs *cust_cfgs = NULL; @@ -875,7 +875,7 @@ static void __init_or_module of_get_custom_configs(struct device_node *np_emif, emif->plat_data->custom_configs = cust_cfgs; } -static void __init_or_module of_get_ddr_info(struct device_node *np_emif, +static void of_get_ddr_info(struct device_node *np_emif, struct device_node *np_ddr, struct ddr_device_info *dev_info) { @@ -909,7 +909,7 @@ static void __init_or_module of_get_ddr_info(struct device_node *np_emif, dev_info->io_width = __fls(io_width) - 1; } -static struct emif_data * __init_or_module of_get_memory_device_details( +static struct emif_data *of_get_memory_device_details( struct device_node *np_emif, struct device *dev) { struct emif_data *emif = NULL; @@ -982,7 +982,7 @@ out: return emif; } -static struct emif_data *__init_or_module get_device_details( +static struct emif_data *get_device_details( struct platform_device *pdev) { u32 size; @@ -1086,7 +1086,7 @@ error: return NULL; } -static int __init_or_module emif_probe(struct platform_device *pdev) +static int emif_probe(struct platform_device *pdev) { struct emif_data *emif; int irq, ret; @@ -1141,7 +1141,7 @@ error: return -ENODEV; } -static void __exit emif_remove(struct platform_device *pdev) +static void emif_remove(struct platform_device *pdev) { struct emif_data *emif = platform_get_drvdata(pdev); @@ -1165,7 +1165,8 @@ MODULE_DEVICE_TABLE(of, emif_of_match); #endif static struct platform_driver emif_driver = { - .remove_new = __exit_p(emif_remove), + .probe = emif_probe, + .remove_new = emif_remove, .shutdown = emif_shutdown, .driver = { .name = "emif", @@ -1173,7 +1174,7 @@ static struct platform_driver emif_driver = { }, }; -module_platform_driver_probe(emif_driver, emif_probe); +module_platform_driver(emif_driver); MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver"); MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From 27825593c972abac86b9a4453a8c8c9a2c1ec60f Mon Sep 17 00:00:00 2001 From: Tao Zhang Date: Tue, 2 Jan 2024 07:55:12 +0530 Subject: soc: qcom: smem: remove hwspinlock from item get routine During an SSR(Sub-System Restart) process, the remoteproc driver will try to read the crash reason from SMEM. The qcom_smem_get() backing such operations does however take the hwspinlock (tcsr mutex), which might be held by the dying remoteproc. The associated timeout on the hwspin_lock_timeout_irqsave() would take care of the system not hanging forever, but the get operation will fail, unnecessarily delaying the process for the 'HWSPINLOCK_TIMEOUT' duration (currently is '1s'), and finally resulting in failure to get crash information from SMEM. This timeout can be avoided by removing the hwspinlock in the qcom_smem_get routine. SMEM ensures that the allocated item will only be visible after the new item is safe to use by following a specific order of updates. In the private partition case, qcom_smem_get_private() will use 'offset_free_uncached' as a loop boundary when looking for existing allocated items. The corresponding allocation will only update offset_free_uncached once the item is fully initialized. hdr->canary = SMEM_PRIVATE_CANARY; hdr->item = cpu_to_le16(item); hdr->size = cpu_to_le32(ALIGN(size, 8)); hdr->padding_data = cpu_to_le16(le32_to_cpu(hdr->size) - size); hdr->padding_hdr = 0; wmb(); le32_add_cpu(&phdr->offset_free_uncached, alloc_size); The global partition is similar but uses the "entry->allocated" variable to ensure the item is not visible to qcom_smem_get_global(). Signed-off-by: Tao Zhang Reviewed-by: Chris Lew Link: https://lore.kernel.org/r/20240102022512.999635-1-quic_taozhan@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/smem.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/smem.c b/drivers/soc/qcom/smem.c index 690afc9a12f4..7191fa0c087f 100644 --- a/drivers/soc/qcom/smem.c +++ b/drivers/soc/qcom/smem.c @@ -655,8 +655,6 @@ invalid_canary: void *qcom_smem_get(unsigned host, unsigned item, size_t *size) { struct smem_partition *part; - unsigned long flags; - int ret; void *ptr = ERR_PTR(-EPROBE_DEFER); if (!__smem) @@ -665,12 +663,6 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size) if (WARN_ON(item >= __smem->item_count)) return ERR_PTR(-EINVAL); - ret = hwspin_lock_timeout_irqsave(__smem->hwlock, - HWSPINLOCK_TIMEOUT, - &flags); - if (ret) - return ERR_PTR(ret); - if (host < SMEM_HOST_COUNT && __smem->partitions[host].virt_base) { part = &__smem->partitions[host]; ptr = qcom_smem_get_private(__smem, part, item, size); @@ -681,10 +673,7 @@ void *qcom_smem_get(unsigned host, unsigned item, size_t *size) ptr = qcom_smem_get_global(__smem, item, size); } - hwspin_unlock_irqrestore(__smem->hwlock, &flags); - return ptr; - } EXPORT_SYMBOL_GPL(qcom_smem_get); -- cgit v1.2.3-58-ga151 From d51d984c5525aebac0f90356ab1d923541b5cc60 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 17 Jan 2024 18:31:52 -0800 Subject: soc: qcom: aoss: Add debugfs interface for sending messages In addition to the normal runtime commands, the Always On Processor (AOP) provides a number of debug commands which can be used during system debugging for things such as preventing power collapse or placing floor votes for certain resources. Some of these are documented in the Robotics RB5 "Debug AOP ADB" linked below. Provide a debugfs interface for the developer/tester to send some of these commands to the AOP, which allow the user to override the DDR frequency, preventing power collapse of cx and ddr, and prevent AOSS from going to sleep. Link: https://docs.qualcomm.com/bundle/publicresource/topics/80-88500-3/85_Debugging_AOP_ADB.html Signed-off-by: Bjorn Andersson Reviewed-by: Chris Lew Link: https://lore.kernel.org/r/20240117-qcom-aoss-debugfs-v2-v3-1-1aa779124822@quicinc.com [bjorn: Dropped S_IWGRP from the debugfs files] Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_aoss.c | 94 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index 9d7a74767008..085dc887631c 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -3,6 +3,7 @@ * Copyright (c) 2019, Linaro Ltd */ #include +#include #include #include #include @@ -44,6 +45,8 @@ #define QMP_NUM_COOLING_RESOURCES 2 +#define QMP_DEBUGFS_FILES 4 + static bool qmp_cdev_max_state = 1; struct qmp_cooling_device { @@ -82,6 +85,8 @@ struct qmp { struct clk_hw qdss_clk; struct qmp_cooling_device *cooling_devs; + struct dentry *debugfs_root; + struct dentry *debugfs_files[QMP_DEBUGFS_FILES]; }; static void qmp_kick(struct qmp *qmp) @@ -475,6 +480,91 @@ void qmp_put(struct qmp *qmp) } EXPORT_SYMBOL_GPL(qmp_put); +struct qmp_debugfs_entry { + const char *name; + const char *fmt; + bool is_bool; + const char *true_val; + const char *false_val; +}; + +static const struct qmp_debugfs_entry qmp_debugfs_entries[QMP_DEBUGFS_FILES] = { + { "ddr_frequency_mhz", "{class: ddr, res: fixed, val: %u}", false }, + { "prevent_aoss_sleep", "{class: aoss_slp, res: sleep: %s}", true, "enable", "disable" }, + { "prevent_cx_collapse", "{class: cx_mol, res: cx, val: %s}", true, "mol", "off" }, + { "prevent_ddr_collapse", "{class: ddr_mol, res: ddr, val: %s}", true, "mol", "off" }, +}; + +static ssize_t qmp_debugfs_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *pos) +{ + const struct qmp_debugfs_entry *entry = NULL; + struct qmp *qmp = file->private_data; + char buf[QMP_MSG_LEN]; + unsigned int uint_val; + const char *str_val; + bool bool_val; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(qmp->debugfs_files); i++) { + if (qmp->debugfs_files[i] == file->f_path.dentry) { + entry = &qmp_debugfs_entries[i]; + break; + } + } + if (WARN_ON(!entry)) + return -EFAULT; + + if (entry->is_bool) { + ret = kstrtobool_from_user(user_buf, count, &bool_val); + if (ret) + return ret; + + str_val = bool_val ? entry->true_val : entry->false_val; + + ret = snprintf(buf, sizeof(buf), entry->fmt, str_val); + if (ret >= sizeof(buf)) + return -EINVAL; + } else { + ret = kstrtou32_from_user(user_buf, count, 0, &uint_val); + if (ret) + return ret; + + ret = snprintf(buf, sizeof(buf), entry->fmt, uint_val); + if (ret >= sizeof(buf)) + return -EINVAL; + } + + ret = qmp_send(qmp, buf); + if (ret < 0) + return ret; + + return count; +} + +static const struct file_operations qmp_debugfs_fops = { + .open = simple_open, + .write = qmp_debugfs_write, +}; + +static void qmp_debugfs_create(struct qmp *qmp) +{ + const struct qmp_debugfs_entry *entry; + int i; + + qmp->debugfs_root = debugfs_create_dir("qcom_aoss", NULL); + + for (i = 0; i < ARRAY_SIZE(qmp->debugfs_files); i++) { + entry = &qmp_debugfs_entries[i]; + + qmp->debugfs_files[i] = debugfs_create_file(entry->name, 0200, + qmp->debugfs_root, + qmp, + &qmp_debugfs_fops); + } +} + static int qmp_probe(struct platform_device *pdev) { struct qmp *qmp; @@ -523,6 +613,8 @@ static int qmp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, qmp); + qmp_debugfs_create(qmp); + return 0; err_close_qmp: @@ -537,6 +629,8 @@ static void qmp_remove(struct platform_device *pdev) { struct qmp *qmp = platform_get_drvdata(pdev); + debugfs_remove_recursive(qmp->debugfs_root); + qmp_qdss_clk_remove(qmp); qmp_cooling_devices_remove(qmp); -- cgit v1.2.3-58-ga151 From d7f3a3691e3c133c637fa381cdc91e7d1af9c5d7 Mon Sep 17 00:00:00 2001 From: Tengfei Fan Date: Fri, 19 Jan 2024 18:06:18 +0800 Subject: soc: qcom: socinfo: add SoC Info support for QCM8550 and QCS8550 platform Add SoC Info support for QCM8550 and QCS8550 platform. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Tengfei Fan Link: https://lore.kernel.org/r/20240119100621.11788-4-quic_tengfan@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 6349a0debeb5..321b39b48b80 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -437,6 +437,8 @@ static const struct soc_id soc_id[] = { { qcom_board_id(IPQ5322) }, { qcom_board_id(IPQ5312) }, { qcom_board_id(IPQ5302) }, + { qcom_board_id(QCS8550) }, + { qcom_board_id(QCM8550) }, { qcom_board_id(IPQ5300) }, }; -- cgit v1.2.3-58-ga151 From 651893a7c99ad935de24792bf2019c00ee2fca10 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Tue, 23 Jan 2024 19:40:33 -0800 Subject: soc: qcom: aoss: Add tracepoints in qmp_send() Add tracepoint for tracing the messages being sent and the success thereof. This is useful as the system has a variety of clients sending requests to the always-on subsystem. Signed-off-by: Bjorn Andersson Reviewed-by: Chris Lew Link: https://lore.kernel.org/r/20240123-qcom-aoss-tracepoints-v2-1-bd73baa31977@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/qcom_aoss.c | 7 +++++++ drivers/soc/qcom/trace-aoss.h | 48 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 drivers/soc/qcom/trace-aoss.h (limited to 'drivers') diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 05b3d54e8dc9..9268e15e1719 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 CFLAGS_rpmh-rsc.o := -I$(src) +CFLAGS_qcom_aoss.o := -I$(src) obj-$(CONFIG_QCOM_AOSS_QMP) += qcom_aoss.o obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o obj-$(CONFIG_QCOM_COMMAND_DB) += cmd-db.o diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index 085dc887631c..e8ae8aa6391f 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -14,6 +14,9 @@ #include #include +#define CREATE_TRACE_POINTS +#include "trace-aoss.h" + #define QMP_DESC_MAGIC 0x0 #define QMP_DESC_VERSION 0x4 #define QMP_DESC_FEATURES 0x8 @@ -240,6 +243,8 @@ int __printf(2, 3) qmp_send(struct qmp *qmp, const char *fmt, ...) mutex_lock(&qmp->tx_lock); + trace_aoss_send(buf); + /* The message RAM only implements 32-bit accesses */ __iowrite32_copy(qmp->msgram + qmp->offset + sizeof(u32), buf, sizeof(buf) / sizeof(u32)); @@ -261,6 +266,8 @@ int __printf(2, 3) qmp_send(struct qmp *qmp, const char *fmt, ...) ret = 0; } + trace_aoss_send_done(buf, ret); + mutex_unlock(&qmp->tx_lock); return ret; diff --git a/drivers/soc/qcom/trace-aoss.h b/drivers/soc/qcom/trace-aoss.h new file mode 100644 index 000000000000..554029b33b44 --- /dev/null +++ b/drivers/soc/qcom/trace-aoss.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM qcom_aoss + +#if !defined(_TRACE_QCOM_AOSS_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_QCOM_AOSS_H + +#include + +TRACE_EVENT(aoss_send, + TP_PROTO(const char *msg), + TP_ARGS(msg), + TP_STRUCT__entry( + __string(msg, msg) + ), + TP_fast_assign( + __assign_str(msg, msg); + ), + TP_printk("%s", __get_str(msg)) +); + +TRACE_EVENT(aoss_send_done, + TP_PROTO(const char *msg, int ret), + TP_ARGS(msg, ret), + TP_STRUCT__entry( + __string(msg, msg) + __field(int, ret) + ), + TP_fast_assign( + __assign_str(msg, msg); + __entry->ret = ret; + ), + TP_printk("%s: %d", __get_str(msg), __entry->ret) +); + +#endif /* _TRACE_QCOM_AOSS_H */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace-aoss + +#include -- cgit v1.2.3-58-ga151 From 5155e48128826d0c5999dc9f47aa746df54da448 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sun, 28 Jan 2024 03:32:44 +0200 Subject: soc: qcom: socinfo: rename PM2250 to PM4125 It seems, the only actual mentions of PM2250 can be found are related to the Qualcomm RB1 platform. However even RB1 schematics use PM4125 as a PMIC name. Rename PM2250 to PM4125 to follow the documentation. Fixes: 082f9bc60f33 ("soc: qcom: spmi-pmic: add more PMIC SUBTYPE IDs") Fixes: 112d96fd2927 ("soc: qcom: socinfo: Add some PMICs") Acked-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20240128-pm2250-pm4125-rename-v2-1-d51987e9f83a@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 2 +- include/soc/qcom/qcom-spmi-pmic.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 321b39b48b80..5a44ad870fb6 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -124,7 +124,7 @@ static const char *const pmic_models[] = { [50] = "PM8350B", [51] = "PMR735A", [52] = "PMR735B", - [55] = "PM2250", + [55] = "PM4125", [58] = "PM8450", [65] = "PM8010", [69] = "PM8550VS", diff --git a/include/soc/qcom/qcom-spmi-pmic.h b/include/soc/qcom/qcom-spmi-pmic.h index 17a0a8c3d656..a62d500a6fda 100644 --- a/include/soc/qcom/qcom-spmi-pmic.h +++ b/include/soc/qcom/qcom-spmi-pmic.h @@ -49,7 +49,7 @@ #define PMK8350_SUBTYPE 0x2f #define PMR735B_SUBTYPE 0x34 #define PM6350_SUBTYPE 0x36 -#define PM2250_SUBTYPE 0x37 +#define PM4125_SUBTYPE 0x37 #define PMI8998_FAB_ID_SMIC 0x11 #define PMI8998_FAB_ID_GF 0x30 -- cgit v1.2.3-58-ga151 From 423a54da3c7ee6d27b862b1bae6e639a1374a36a Mon Sep 17 00:00:00 2001 From: William-tw Lin Date: Fri, 22 Dec 2023 16:07:38 +0800 Subject: soc: mediatek: mtk-socinfo: Add driver for getting chip information Add driver for socinfo retrieval. This patch includes the following: 1. mtk-socinfo driver for chip info retrieval 2. Related changes to Makefile and Kconfig Signed-off-by: William-tw Lin Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20231222080739.21706-3-william-tw.lin@mediatek.com Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/Kconfig | 9 ++ drivers/soc/mediatek/Makefile | 1 + drivers/soc/mediatek/mtk-socinfo.c | 186 +++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+) create mode 100644 drivers/soc/mediatek/mtk-socinfo.c (limited to 'drivers') diff --git a/drivers/soc/mediatek/Kconfig b/drivers/soc/mediatek/Kconfig index 0810b5b0c688..50c664b65f4d 100644 --- a/drivers/soc/mediatek/Kconfig +++ b/drivers/soc/mediatek/Kconfig @@ -68,4 +68,13 @@ config MTK_SVS chip process corner, temperatures and other factors. Then DVFS driver could apply SVS bank voltage to PMIC/Buck. +config MTK_SOCINFO + tristate "MediaTek SoC Information" + default y + depends on NVMEM_MTK_EFUSE + help + The MediaTek SoC Information (mtk-socinfo) driver provides + information about the SoC to the userspace including the + manufacturer name, marketing name and soc name. + endmenu diff --git a/drivers/soc/mediatek/Makefile b/drivers/soc/mediatek/Makefile index 9d3ce7878c5c..6830512848fd 100644 --- a/drivers/soc/mediatek/Makefile +++ b/drivers/soc/mediatek/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_MTK_REGULATOR_COUPLER) += mtk-regulator-coupler.o obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o obj-$(CONFIG_MTK_MMSYS) += mtk-mutex.o obj-$(CONFIG_MTK_SVS) += mtk-svs.o +obj-$(CONFIG_MTK_SOCINFO) += mtk-socinfo.o diff --git a/drivers/soc/mediatek/mtk-socinfo.c b/drivers/soc/mediatek/mtk-socinfo.c new file mode 100644 index 000000000000..0094f43e1e08 --- /dev/null +++ b/drivers/soc/mediatek/mtk-socinfo.c @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 MediaTek Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define MTK_SOCINFO_ENTRY(_soc_name, _segment_name, _marketing_name, _cell_data1, _cell_data2) {\ + .soc_name = _soc_name, \ + .segment_name = _segment_name, \ + .marketing_name = _marketing_name, \ + .cell_data = {_cell_data1, _cell_data2} \ +} +#define CELL_NOT_USED (0xFFFFFFFF) +#define MAX_CELLS (2) + +struct mtk_socinfo { + struct device *dev; + struct name_data *name_data; + struct socinfo_data *socinfo_data; + struct soc_device *soc_dev; +}; + +struct socinfo_data { + char *soc_name; + char *segment_name; + char *marketing_name; + u32 cell_data[MAX_CELLS]; +}; + +static const char *cell_names[MAX_CELLS] = {"socinfo-data1", "socinfo-data2"}; + +static struct socinfo_data socinfo_data_table[] = { + MTK_SOCINFO_ENTRY("MT8173", "MT8173V/AC", "MT8173", 0x6CA20004, 0x10000000), + MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000840), + MTK_SOCINFO_ENTRY("MT8186", "MT8186GV/AZA", "Kompanio 520", 0x81861001, CELL_NOT_USED), + MTK_SOCINFO_ENTRY("MT8186T", "MT8186TV/AZA", "Kompanio 528", 0x81862001, CELL_NOT_USED), + MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/AZA", "Kompanio 830", 0x81880000, 0x00000010), + MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/HZA", "Kompanio 830", 0x81880000, 0x00000011), + MTK_SOCINFO_ENTRY("MT8192", "MT8192V/AZA", "Kompanio 820", 0x00001100, 0x00040080), + MTK_SOCINFO_ENTRY("MT8192T", "MT8192V/ATZA", "Kompanio 828", 0x00000100, 0x000400C0), + MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EZA", "Kompanio 1200", 0x81950300, CELL_NOT_USED), + MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EHZA", "Kompanio 1200", 0x81950304, CELL_NOT_USED), + MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EZA", "Kompanio 1380", 0x81950400, CELL_NOT_USED), + MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EHZA", "Kompanio 1380", 0x81950404, CELL_NOT_USED), +}; + +static int mtk_socinfo_create_socinfo_node(struct mtk_socinfo *mtk_socinfop) +{ + struct soc_device_attribute *attrs; + static char machine[30] = {0}; + static const char *soc_manufacturer = "MediaTek"; + + attrs = devm_kzalloc(mtk_socinfop->dev, sizeof(*attrs), GFP_KERNEL); + if (!attrs) + return -ENOMEM; + + snprintf(machine, sizeof(machine), "%s (%s)", mtk_socinfop->socinfo_data->marketing_name, + mtk_socinfop->socinfo_data->soc_name); + attrs->family = soc_manufacturer; + attrs->machine = machine; + + mtk_socinfop->soc_dev = soc_device_register(attrs); + if (IS_ERR(mtk_socinfop->soc_dev)) + return PTR_ERR(mtk_socinfop->soc_dev); + + dev_info(mtk_socinfop->dev, "%s %s SoC detected.\n", soc_manufacturer, attrs->machine); + return 0; +} + +static u32 mtk_socinfo_read_cell(struct device *dev, const char *name) +{ + struct nvmem_device *nvmemp; + struct device_node *np = dev->of_node; + u32 offset; + u32 cell_val = CELL_NOT_USED; + + nvmemp = devm_nvmem_device_get(dev, "mtk-efuse0"); + if (IS_ERR(nvmemp)) + goto out; + + np = of_find_node_by_name(NULL, name); + if (!np) + goto out; + + if (of_property_read_u32_index(np, "reg", 0, &offset)) + goto out; + + nvmem_device_read(nvmemp, offset, sizeof(cell_val), &cell_val); + + nvmem_device_put(nvmemp); + +out: + return cell_val; +} + +static int mtk_socinfo_get_socinfo_data(struct mtk_socinfo *mtk_socinfop) +{ + unsigned int i, j; + unsigned int num_cell_data = 0; + u32 cell_data[MAX_CELLS] = {0}; + bool match_socinfo; + int match_socinfo_index = -1; + + for (i = 0; i < MAX_CELLS; i++) { + cell_data[i] = mtk_socinfo_read_cell(mtk_socinfop->dev, cell_names[i]); + if (cell_data[i] != CELL_NOT_USED) + num_cell_data++; + else + break; + } + + if (!num_cell_data) + return -ENOENT; + + for (i = 0; i < ARRAY_SIZE(socinfo_data_table); i++) { + match_socinfo = true; + for (j = 0; j < num_cell_data; j++) { + if (cell_data[j] != socinfo_data_table[i].cell_data[j]) { + match_socinfo = false; + break; + } + } + if (match_socinfo) { + mtk_socinfop->socinfo_data = &(socinfo_data_table[i]); + match_socinfo_index = i; + break; + } + } + + return match_socinfo_index >= 0 ? match_socinfo_index : -ENOENT; +} + +static int mtk_socinfo_probe(struct platform_device *pdev) +{ + struct mtk_socinfo *mtk_socinfop; + int ret; + + mtk_socinfop = devm_kzalloc(&pdev->dev, sizeof(*mtk_socinfop), GFP_KERNEL); + if (!mtk_socinfop) + return -ENOMEM; + + mtk_socinfop->dev = &pdev->dev; + + ret = mtk_socinfo_get_socinfo_data(mtk_socinfop); + if (ret < 0) + return dev_err_probe(mtk_socinfop->dev, ret, "Failed to get socinfo data\n"); + + ret = mtk_socinfo_create_socinfo_node(mtk_socinfop); + if (ret) + return dev_err_probe(mtk_socinfop->dev, ret, "Cannot create node\n"); + + platform_set_drvdata(pdev, mtk_socinfop); + return 0; +} + +static void mtk_socinfo_remove(struct platform_device *pdev) +{ + struct mtk_socinfo *mtk_socinfop = platform_get_drvdata(pdev); + + soc_device_unregister(mtk_socinfop->soc_dev); +} + +static struct platform_driver mtk_socinfo = { + .probe = mtk_socinfo_probe, + .remove_new = mtk_socinfo_remove, + .driver = { + .name = "mtk-socinfo", + }, +}; +module_platform_driver(mtk_socinfo); + +MODULE_AUTHOR("William-TW LIN "); +MODULE_DESCRIPTION("MediaTek socinfo driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-58-ga151 From 883957bee580b723fd87d49ac73e0c84fc03a446 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Thu, 25 Jan 2024 13:05:10 -0800 Subject: pmdomain: qcom: rpmhpd: Drop SA8540P gfx.lvl On SA8295P and SA8540P gfx.lvl is not provdied by rpmh, but rather is handled by an external regulator (max20411). Drop gfx.lvl from the list of power-domains exposed on this platform. Fixes: f68f1cb3437d ("soc: qcom: rpmhpd: add sc8280xp & sa8540p rpmh power-domains") Reviewed-by: Dmitry Baryshkov Acked-by: Ulf Hansson Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/20240125-sa8295p-gpu-v4-4-7011c2a63037@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/pmdomain/qcom/rpmhpd.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c index 3078896b1300..27a73ff72614 100644 --- a/drivers/pmdomain/qcom/rpmhpd.c +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -217,7 +217,6 @@ static struct rpmhpd *sa8540p_rpmhpds[] = { [SC8280XP_CX] = &cx, [SC8280XP_CX_AO] = &cx_ao, [SC8280XP_EBI] = &ebi, - [SC8280XP_GFX] = &gfx, [SC8280XP_LCX] = &lcx, [SC8280XP_LMX] = &lmx, [SC8280XP_MMCX] = &mmcx, -- cgit v1.2.3-58-ga151 From 82e5d7d793e8aef1275dae266427cf048a7459d6 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 30 Jan 2024 17:56:51 +0800 Subject: soc: mediatek: mtk-socinfo: Clean up NVMEM cell read The mtk-socinfo grabs the NVMEM device devm_nvmem_device_get(), but then proceeds to put the device directly with nvmem_device_put() if the read is successful. If the device fails to probe and goes through the devres release path, the device would be put a second time, triggering a use-after-free error from KASAN. Fix this by dropping the devres part. Since the NVMEM cell data is read only once, there is no need to keep the reference around. While at it, clean up the function to directly reference the NVMEM device node and use that to find the NVMEM device, instead of finding it by name, which is more fragile. The cell node is always a direct child of the NVMEM device node, courtesy of the legacy NVMEM cell layout. Thus of_get_child_by_name() is a better way of finding the cell. Last, correctly put the device node once its use is over. Fixes: 423a54da3c7e ("soc: mediatek: mtk-socinfo: Add driver for getting chip information") Signed-off-by: Chen-Yu Tsai Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20240130095656.3712469-2-wenst@chromium.org Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-socinfo.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/mediatek/mtk-socinfo.c b/drivers/soc/mediatek/mtk-socinfo.c index 0094f43e1e08..3909d22062ce 100644 --- a/drivers/soc/mediatek/mtk-socinfo.c +++ b/drivers/soc/mediatek/mtk-socinfo.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -82,25 +83,28 @@ static int mtk_socinfo_create_socinfo_node(struct mtk_socinfo *mtk_socinfop) static u32 mtk_socinfo_read_cell(struct device *dev, const char *name) { struct nvmem_device *nvmemp; - struct device_node *np = dev->of_node; + struct device_node *np, *nvmem_node = dev->parent->of_node; u32 offset; u32 cell_val = CELL_NOT_USED; - nvmemp = devm_nvmem_device_get(dev, "mtk-efuse0"); + /* should never fail since the nvmem driver registers this child */ + nvmemp = nvmem_device_find(nvmem_node, device_match_of_node); if (IS_ERR(nvmemp)) goto out; - np = of_find_node_by_name(NULL, name); + np = of_get_child_by_name(nvmem_node, name); if (!np) - goto out; + goto put_device; if (of_property_read_u32_index(np, "reg", 0, &offset)) - goto out; + goto put_node; nvmem_device_read(nvmemp, offset, sizeof(cell_val), &cell_val); +put_node: + of_node_put(np); +put_device: nvmem_device_put(nvmemp); - out: return cell_val; } -- cgit v1.2.3-58-ga151 From 54d21dea6a6c117f3cab4caa1f9c3ffafb515dd6 Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Tue, 30 Jan 2024 17:56:52 +0800 Subject: soc: mediatek: mtk-socinfo: Add extra entry for MT8183 The MT8183 has another socinfo match, with the second cell only differing by one bit. Add it to the driver. Fixes: 423a54da3c7e ("soc: mediatek: mtk-socinfo: Add driver for getting chip information") Signed-off-by: Chen-Yu Tsai Acked-by: William-tw Lin Link: https://lore.kernel.org/r/20240130095656.3712469-3-wenst@chromium.org Signed-off-by: AngeloGioacchino Del Regno --- drivers/soc/mediatek/mtk-socinfo.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/soc/mediatek/mtk-socinfo.c b/drivers/soc/mediatek/mtk-socinfo.c index 3909d22062ce..42572e8c1520 100644 --- a/drivers/soc/mediatek/mtk-socinfo.c +++ b/drivers/soc/mediatek/mtk-socinfo.c @@ -45,6 +45,7 @@ static const char *cell_names[MAX_CELLS] = {"socinfo-data1", "socinfo-data2"}; static struct socinfo_data socinfo_data_table[] = { MTK_SOCINFO_ENTRY("MT8173", "MT8173V/AC", "MT8173", 0x6CA20004, 0x10000000), MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000840), + MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000940), MTK_SOCINFO_ENTRY("MT8186", "MT8186GV/AZA", "Kompanio 520", 0x81861001, CELL_NOT_USED), MTK_SOCINFO_ENTRY("MT8186T", "MT8186TV/AZA", "Kompanio 528", 0x81862001, CELL_NOT_USED), MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/AZA", "Kompanio 830", 0x81880000, 0x00000010), -- cgit v1.2.3-58-ga151 From ae286bd7e9ed889b7284aa8bdb321a9938cc89ca Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Thu, 25 Jan 2024 16:34:38 +0100 Subject: soc: renesas: Introduce ARCH_RCAR_GEN4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, all Kconfig symbols for R-Car Gen4 SoCs select the ARCH_RCAR_GEN3 SoC family symbol, which might confuse the casual reader. Fix this by introducing a new SoC family symbol for R-Car Gen4 SoCs. For now this just selects ARCH_RCAR_GEN3, to avoid duplication, and to relax dependencies. Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Reviewed-by: Niklas Söderlund Link: https://lore.kernel.org/r/a283913ba7a39ec2dae0ebf9fc2b06096f009511.1706194617.git.geert+renesas@glider.be --- drivers/soc/renesas/Kconfig | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 0986672f6375..784a2b50561d 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -34,6 +34,10 @@ config ARCH_RCAR_GEN3 select SYS_SUPPORTS_SH_CMT select SYS_SUPPORTS_SH_TMU +config ARCH_RCAR_GEN4 + bool + select ARCH_RCAR_GEN3 + config ARCH_RMOBILE bool select PM @@ -240,7 +244,7 @@ config ARCH_R8A77961 config ARCH_R8A779F0 bool "ARM64 Platform support for R-Car S4-8" - select ARCH_RCAR_GEN3 + select ARCH_RCAR_GEN4 select SYSC_R8A779F0 help This enables support for the Renesas R-Car S4-8 SoC. @@ -261,14 +265,14 @@ config ARCH_R8A77970 config ARCH_R8A779A0 bool "ARM64 Platform support for R-Car V3U" - select ARCH_RCAR_GEN3 + select ARCH_RCAR_GEN4 select SYSC_R8A779A0 help This enables support for the Renesas R-Car V3U SoC. config ARCH_R8A779G0 bool "ARM64 Platform support for R-Car V4H" - select ARCH_RCAR_GEN3 + select ARCH_RCAR_GEN4 select SYSC_R8A779G0 help This enables support for the Renesas R-Car V4H SoC. -- cgit v1.2.3-58-ga151 From 2969768dae22c1b76f3784f9630002113b5867d5 Mon Sep 17 00:00:00 2001 From: Duy Nguyen Date: Thu, 25 Jan 2024 16:34:39 +0100 Subject: soc: renesas: Identify R-Car V4M MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for identifying the R-Car V4M (R8A779H0) SoC. Signed-off-by: Duy Nguyen Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Reviewed-by: Niklas Söderlund Link: https://lore.kernel.org/r/31e06d055aec1bc70c3e9a02f9268bcfc72b2204.1706194617.git.geert+renesas@glider.be --- drivers/soc/renesas/Kconfig | 7 +++++++ drivers/soc/renesas/renesas-soc.c | 8 ++++++++ 2 files changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/renesas/Kconfig b/drivers/soc/renesas/Kconfig index 784a2b50561d..5deca747fb77 100644 --- a/drivers/soc/renesas/Kconfig +++ b/drivers/soc/renesas/Kconfig @@ -277,6 +277,13 @@ config ARCH_R8A779G0 help This enables support for the Renesas R-Car V4H SoC. +config ARCH_R8A779H0 + bool "ARM64 Platform support for R-Car V4M" + select ARCH_RCAR_GEN4 + select SYSC_R8A779H0 + help + This enables support for the Renesas R-Car V4M SoC. + config ARCH_R8A774C0 bool "ARM64 Platform support for RZ/G2E" select ARCH_RCAR_GEN3 diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 27eae1a354ab..8f9b8d3736dc 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -270,6 +270,11 @@ static const struct renesas_soc soc_rcar_v4h __initconst __maybe_unused = { .id = 0x5c, }; +static const struct renesas_soc soc_rcar_v4m __initconst __maybe_unused = { + .family = &fam_rcar_gen4, + .id = 0x5d, +}; + static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = { .family = &fam_shmobile, .id = 0x37, @@ -380,6 +385,9 @@ static const struct of_device_id renesas_socs[] __initconst __maybe_unused = { #ifdef CONFIG_ARCH_R8A779G0 { .compatible = "renesas,r8a779g0", .data = &soc_rcar_v4h }, #endif +#ifdef CONFIG_ARCH_R8A779H0 + { .compatible = "renesas,r8a779h0", .data = &soc_rcar_v4m }, +#endif #ifdef CONFIG_ARCH_R9A07G043 #ifdef CONFIG_RISCV { .compatible = "renesas,r9a07g043", .data = &soc_rz_five }, -- cgit v1.2.3-58-ga151 From 14656ed72fa5d0a2f98be0ffd56274db30171f49 Mon Sep 17 00:00:00 2001 From: Duy Nguyen Date: Thu, 25 Jan 2024 16:34:40 +0100 Subject: soc: renesas: rcar-rst: Add support for R-Car V4M MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the R-Car V4M (R8A779H0) SoC to the R-Car RST driver. Signed-off-by: Duy Nguyen Signed-off-by: Geert Uytterhoeven Reviewed-by: Wolfram Sang Reviewed-by: Niklas Söderlund Link: https://lore.kernel.org/r/13dc9f014e27db5092b3cc23edddf4b5e01a6645.1706194617.git.geert+renesas@glider.be --- drivers/soc/renesas/rcar-rst.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/soc/renesas/rcar-rst.c b/drivers/soc/renesas/rcar-rst.c index 98fd97da6cd4..7ba02f3a4a4f 100644 --- a/drivers/soc/renesas/rcar-rst.c +++ b/drivers/soc/renesas/rcar-rst.c @@ -117,6 +117,7 @@ static const struct of_device_id rcar_rst_matches[] __initconst = { { .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_v3u }, { .compatible = "renesas,r8a779f0-rst", .data = &rcar_rst_gen4 }, { .compatible = "renesas,r8a779g0-rst", .data = &rcar_rst_gen4 }, + { .compatible = "renesas,r8a779h0-rst", .data = &rcar_rst_gen4 }, { /* sentinel */ } }; -- cgit v1.2.3-58-ga151 From 9863084dd9939e53eb67a689f13503e8025434ac Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Sun, 12 Nov 2023 08:04:14 +0100 Subject: soc/tegra: pmc: Remove some old and deprecated functions and constants These TEGRA_IO_RAIL_... functions and constants have been deprecated in commit 21b499105178 ("soc/tegra: pmc: Add I/O pad voltage support") in 2016-11. There seems to be no users since kernel 4.16. Remove them now. Signed-off-by: Christophe JAILLET Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 24 ------------------------ include/soc/tegra/pmc.h | 18 ------------------ 2 files changed, 42 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index f432aa022ace..6dfcc7f50ece 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -1777,30 +1777,6 @@ static int tegra_io_pad_get_voltage(struct tegra_pmc *pmc, enum tegra_io_pad id) return TEGRA_IO_PAD_VOLTAGE_3V3; } -/** - * tegra_io_rail_power_on() - enable power to I/O rail - * @id: Tegra I/O pad ID for which to enable power - * - * See also: tegra_io_pad_power_enable() - */ -int tegra_io_rail_power_on(unsigned int id) -{ - return tegra_io_pad_power_enable(id); -} -EXPORT_SYMBOL(tegra_io_rail_power_on); - -/** - * tegra_io_rail_power_off() - disable power to I/O rail - * @id: Tegra I/O pad ID for which to disable power - * - * See also: tegra_io_pad_power_disable() - */ -int tegra_io_rail_power_off(unsigned int id) -{ - return tegra_io_pad_power_disable(id); -} -EXPORT_SYMBOL(tegra_io_rail_power_off); - #ifdef CONFIG_PM_SLEEP enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void) { diff --git a/include/soc/tegra/pmc.h b/include/soc/tegra/pmc.h index aadb845d281d..c545875d0ff1 100644 --- a/include/soc/tegra/pmc.h +++ b/include/soc/tegra/pmc.h @@ -148,10 +148,6 @@ enum tegra_io_pad { TEGRA_IO_PAD_AO_HV, }; -/* deprecated, use TEGRA_IO_PAD_{HDMI,LVDS} instead */ -#define TEGRA_IO_RAIL_HDMI TEGRA_IO_PAD_HDMI -#define TEGRA_IO_RAIL_LVDS TEGRA_IO_PAD_LVDS - #ifdef CONFIG_SOC_TEGRA_PMC int tegra_powergate_power_on(unsigned int id); int tegra_powergate_power_off(unsigned int id); @@ -164,10 +160,6 @@ int tegra_powergate_sequence_power_up(unsigned int id, struct clk *clk, int tegra_io_pad_power_enable(enum tegra_io_pad id); int tegra_io_pad_power_disable(enum tegra_io_pad id); -/* deprecated, use tegra_io_pad_power_{enable,disable}() instead */ -int tegra_io_rail_power_on(unsigned int id); -int tegra_io_rail_power_off(unsigned int id); - void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode); void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode); @@ -211,16 +203,6 @@ static inline int tegra_io_pad_get_voltage(enum tegra_io_pad id) return -ENOSYS; } -static inline int tegra_io_rail_power_on(unsigned int id) -{ - return -ENOSYS; -} - -static inline int tegra_io_rail_power_off(unsigned int id) -{ - return -ENOSYS; -} - static inline void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode) { } -- cgit v1.2.3-58-ga151 From 4569e604b5abc2eacc30dd6ac7e3e0fbaa87bc42 Mon Sep 17 00:00:00 2001 From: Kartik Date: Tue, 17 Oct 2023 10:53:16 +0530 Subject: soc/tegra: fuse: Use dev_err_probe for probe failures Currently, in tegra_fuse_probe() if clock/reset get fails, then the driver prints an error if the error is not caused by -EPROBE_DEFER. This can be improved by using dev_err_probe() instead. So, return dev_err_probe() if clock/reset get fails. Signed-off-by: Kartik Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index a2c28f493a75..98805885158e 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -131,13 +131,8 @@ static int tegra_fuse_probe(struct platform_device *pdev) fuse->phys = res->start; fuse->clk = devm_clk_get(&pdev->dev, "fuse"); - if (IS_ERR(fuse->clk)) { - if (PTR_ERR(fuse->clk) != -EPROBE_DEFER) - dev_err(&pdev->dev, "failed to get FUSE clock: %ld", - PTR_ERR(fuse->clk)); - - return PTR_ERR(fuse->clk); - } + if (IS_ERR(fuse->clk)) + return dev_err_probe(&pdev->dev, PTR_ERR(fuse->clk), "failed to get FUSE clock\n"); platform_set_drvdata(pdev, fuse); fuse->dev = &pdev->dev; @@ -179,12 +174,8 @@ static int tegra_fuse_probe(struct platform_device *pdev) } fuse->rst = devm_reset_control_get_optional(&pdev->dev, "fuse"); - if (IS_ERR(fuse->rst)) { - err = PTR_ERR(fuse->rst); - dev_err(&pdev->dev, "failed to get FUSE reset: %pe\n", - fuse->rst); - return err; - } + if (IS_ERR(fuse->rst)) + return dev_err_probe(&pdev->dev, PTR_ERR(fuse->rst), "failed to get FUSE reset\n"); /* * FUSE clock is enabled at a boot time, hence this resume/suspend -- cgit v1.2.3-58-ga151 From f0139d666685ef339fe5b588696db754e0ac8159 Mon Sep 17 00:00:00 2001 From: Kartik Date: Tue, 17 Oct 2023 10:53:17 +0530 Subject: soc/tegra: fuse: Refactor resource mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To prepare for adding ACPI support to the tegra-apbmisc driver, relocate the code responsible for mapping memory resources from the function ‘tegra_init_apbmisc’ to the function ‘tegra_init_apbmisc_resources.’ This adjustment will allow the code to be shared between ‘tegra_init_apbmisc’ and the upcoming ‘tegra_acpi_init_apbmisc’ function. Signed-off-by: Kartik Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/tegra-apbmisc.c | 37 +++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index da970f3dbf35..06c1b3a2c7ec 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -160,9 +160,28 @@ void __init tegra_init_revision(void) tegra_sku_info.platform = tegra_get_platform(); } -void __init tegra_init_apbmisc(void) +static void tegra_init_apbmisc_resources(struct resource *apbmisc, + struct resource *straps) { void __iomem *strapping_base; + + apbmisc_base = ioremap(apbmisc->start, resource_size(apbmisc)); + if (apbmisc_base) + chipid = readl_relaxed(apbmisc_base + 4); + else + pr_err("failed to map APBMISC registers\n"); + + strapping_base = ioremap(straps->start, resource_size(straps)); + if (strapping_base) { + strapping = readl_relaxed(strapping_base); + iounmap(strapping_base); + } else { + pr_err("failed to map strapping options registers\n"); + } +} + +void __init tegra_init_apbmisc(void) +{ struct resource apbmisc, straps; struct device_node *np; @@ -219,21 +238,7 @@ void __init tegra_init_apbmisc(void) } } - apbmisc_base = ioremap(apbmisc.start, resource_size(&apbmisc)); - if (!apbmisc_base) { - pr_err("failed to map APBMISC registers\n"); - } else { - chipid = readl_relaxed(apbmisc_base + 4); - } - - strapping_base = ioremap(straps.start, resource_size(&straps)); - if (!strapping_base) { - pr_err("failed to map strapping options registers\n"); - } else { - strapping = readl_relaxed(strapping_base); - iounmap(strapping_base); - } - + tegra_init_apbmisc_resources(&apbmisc, &straps); long_ram_code = of_property_read_bool(np, "nvidia,long-ram-code"); put: -- cgit v1.2.3-58-ga151 From 7b0c505eb3414619feef0bd8acc455858f17c1c6 Mon Sep 17 00:00:00 2001 From: Kartik Date: Tue, 17 Oct 2023 10:53:18 +0530 Subject: soc/tegra: fuse: Add tegra_acpi_init_apbmisc() In preparation to ACPI support in Tegra fuse driver add function tegra_acpi_init_apbmisc() to initialize tegra-apbmisc driver. Also, document the reason of calling tegra_init_apbmisc() at early init. Note that function tegra_acpi_init_apbmisc() is not placed in the __init section, because it will be called during probe. Signed-off-by: Kartik Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse.h | 1 + drivers/soc/tegra/fuse/tegra-apbmisc.c | 72 ++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/tegra/fuse/fuse.h b/drivers/soc/tegra/fuse/fuse.h index 90f23be73894..a41e9f85281a 100644 --- a/drivers/soc/tegra/fuse/fuse.h +++ b/drivers/soc/tegra/fuse/fuse.h @@ -69,6 +69,7 @@ struct tegra_fuse { void tegra_init_revision(void); void tegra_init_apbmisc(void); +void tegra_acpi_init_apbmisc(void); u32 __init tegra_fuse_read_spare(unsigned int spare); u32 __init tegra_fuse_read_early(unsigned int offset); diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index 06c1b3a2c7ec..6457f80821bb 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -3,9 +3,11 @@ * Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved. */ +#include #include #include #include +#include #include #include @@ -180,6 +182,12 @@ static void tegra_init_apbmisc_resources(struct resource *apbmisc, } } +/** + * tegra_init_apbmisc - Initializes Tegra APBMISC and Strapping registers. + * + * This is called during early init as some of the old 32-bit ARM code needs + * information from the APBMISC registers very early during boot. + */ void __init tegra_init_apbmisc(void) { struct resource apbmisc, straps; @@ -244,3 +252,67 @@ void __init tegra_init_apbmisc(void) put: of_node_put(np); } + +#ifdef CONFIG_ACPI +static const struct acpi_device_id apbmisc_acpi_match[] = { + { "NVDA2010" }, + { /* sentinel */ } +}; + +void tegra_acpi_init_apbmisc(void) +{ + struct resource *resources[2] = { NULL }; + struct resource_entry *rentry; + struct acpi_device *adev = NULL; + struct list_head resource_list; + int rcount = 0; + int ret; + + adev = acpi_dev_get_first_match_dev(apbmisc_acpi_match[0].id, NULL, -1); + if (!adev) + return; + + INIT_LIST_HEAD(&resource_list); + + ret = acpi_dev_get_memory_resources(adev, &resource_list); + if (ret < 0) { + pr_err("failed to get APBMISC memory resources"); + goto out_put_acpi_dev; + } + + /* + * Get required memory resources. + * + * resources[0]: apbmisc. + * resources[1]: straps. + */ + resource_list_for_each_entry(rentry, &resource_list) { + if (rcount >= ARRAY_SIZE(resources)) + break; + + resources[rcount++] = rentry->res; + } + + if (!resources[0]) { + pr_err("failed to get APBMISC registers\n"); + goto out_free_resource_list; + } + + if (!resources[1]) { + pr_err("failed to get strapping options registers\n"); + goto out_free_resource_list; + } + + tegra_init_apbmisc_resources(resources[0], resources[1]); + +out_free_resource_list: + acpi_dev_free_resource_list(&resource_list); + +out_put_acpi_dev: + acpi_dev_put(adev); +} +#else +void tegra_acpi_init_apbmisc(void) +{ +} +#endif -- cgit v1.2.3-58-ga151 From 71661c1c8c34d100be03b229dfc7cd7d2db7f62e Mon Sep 17 00:00:00 2001 From: Kartik Date: Tue, 17 Oct 2023 10:53:19 +0530 Subject: soc/tegra: fuse: Add function to add lookups Add helper function tegra_fuse_add_lookups() to register Tegra fuse nvmem lookups. So, this can be shared between tegra_fuse_init() and ACPI probe, which is to be introduced later. Use kmemdup_array to duplicate fuse->soc->lookups. Signed-off-by: Kartik Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 98805885158e..4ebb5597a77b 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -113,6 +113,18 @@ static void tegra_fuse_restore(void *base) fuse->clk = NULL; } +static int tegra_fuse_add_lookups(struct tegra_fuse *fuse) +{ + fuse->lookups = kmemdup_array(fuse->soc->lookups, sizeof(*fuse->lookups), + fuse->soc->num_lookups, GFP_KERNEL); + if (!fuse->lookups) + return -ENOMEM; + + nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups); + + return 0; +} + static int tegra_fuse_probe(struct platform_device *pdev) { void __iomem *base = fuse->base; @@ -398,6 +410,7 @@ static int __init tegra_init_fuse(void) const struct of_device_id *match; struct device_node *np; struct resource regs; + int err; tegra_init_apbmisc(); @@ -495,15 +508,11 @@ static int __init tegra_init_fuse(void) pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n", tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id); - if (fuse->soc->lookups) { - size_t size = sizeof(*fuse->lookups) * fuse->soc->num_lookups; - - fuse->lookups = kmemdup(fuse->soc->lookups, size, GFP_KERNEL); - if (fuse->lookups) - nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups); - } + err = tegra_fuse_add_lookups(fuse); + if (err) + pr_err("failed to add FUSE lookups\n"); - return 0; + return err; } early_initcall(tegra_init_fuse); -- cgit v1.2.3-58-ga151 From 13a69354147e0aaf39695bfb9062738916e924a0 Mon Sep 17 00:00:00 2001 From: Kartik Date: Tue, 17 Oct 2023 10:53:20 +0530 Subject: soc/tegra: fuse: Add function to print SKU info Add helper function tegra_fuse_print_sku_info() to print Tegra SKU information. So, it can be shared between tegra_fuse_init() and ACPI probe which is to be introduced later. Signed-off-by: Kartik Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 4ebb5597a77b..7a93c6512f7b 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -113,6 +113,16 @@ static void tegra_fuse_restore(void *base) fuse->clk = NULL; } +static void tegra_fuse_print_sku_info(struct tegra_sku_info *tegra_sku_info) +{ + pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n", + tegra_revision_name[tegra_sku_info->revision], + tegra_sku_info->sku_id, tegra_sku_info->cpu_process_id, + tegra_sku_info->soc_process_id); + pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n", + tegra_sku_info->cpu_speedo_id, tegra_sku_info->soc_speedo_id); +} + static int tegra_fuse_add_lookups(struct tegra_fuse *fuse) { fuse->lookups = kmemdup_array(fuse->soc->lookups, sizeof(*fuse->lookups), @@ -501,12 +511,7 @@ static int __init tegra_init_fuse(void) fuse->soc->init(fuse); - pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n", - tegra_revision_name[tegra_sku_info.revision], - tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id, - tegra_sku_info.soc_process_id); - pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n", - tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id); + tegra_fuse_print_sku_info(&tegra_sku_info); err = tegra_fuse_add_lookups(fuse); if (err) -- cgit v1.2.3-58-ga151 From 972167c690801ddf60e88da50493b4ffe103c7f2 Mon Sep 17 00:00:00 2001 From: Kartik Date: Tue, 17 Oct 2023 10:53:21 +0530 Subject: soc/tegra: fuse: Add ACPI support for Tegra194 and Tegra234 Add ACPI support for Tegra194 & Tegra243 SoC's. This requires following modifications to the probe when ACPI boot is used: - Initialize soc data. - Add nvmem lookups. - Register soc device. - use devm_clk_get_optional() instead of devm_clk_get() to get fuse->clk, as fuse clocks are not required when using ACPI boot. Also, drop '__init' keyword for tegra_soc_device_register() as this is also used by tegra_fuse_probe() and use dev_err_probe() wherever applicable. Signed-off-by: Kartik Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 52 ++++++++++++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 7a93c6512f7b..1c758f121f91 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -3,11 +3,13 @@ * Copyright (c) 2013-2023, NVIDIA CORPORATION. All rights reserved. */ +#include #include #include #include #include #include +#include #include #include #include @@ -152,7 +154,38 @@ static int tegra_fuse_probe(struct platform_device *pdev) return PTR_ERR(fuse->base); fuse->phys = res->start; - fuse->clk = devm_clk_get(&pdev->dev, "fuse"); + /* Initialize the soc data and lookups if using ACPI boot. */ + if (is_acpi_node(dev_fwnode(&pdev->dev)) && !fuse->soc) { + u8 chip; + + tegra_acpi_init_apbmisc(); + + chip = tegra_get_chip_id(); + switch (chip) { +#if defined(CONFIG_ARCH_TEGRA_194_SOC) + case TEGRA194: + fuse->soc = &tegra194_fuse_soc; + break; +#endif +#if defined(CONFIG_ARCH_TEGRA_234_SOC) + case TEGRA234: + fuse->soc = &tegra234_fuse_soc; + break; +#endif + default: + return dev_err_probe(&pdev->dev, -EINVAL, "Unsupported SoC: %02x\n", chip); + } + + fuse->soc->init(fuse); + tegra_fuse_print_sku_info(&tegra_sku_info); + tegra_soc_device_register(); + + err = tegra_fuse_add_lookups(fuse); + if (err) + return dev_err_probe(&pdev->dev, err, "failed to add FUSE lookups\n"); + } + + fuse->clk = devm_clk_get_optional(&pdev->dev, "fuse"); if (IS_ERR(fuse->clk)) return dev_err_probe(&pdev->dev, PTR_ERR(fuse->clk), "failed to get FUSE clock\n"); @@ -275,10 +308,17 @@ static const struct dev_pm_ops tegra_fuse_pm = { SET_SYSTEM_SLEEP_PM_OPS(tegra_fuse_suspend, tegra_fuse_resume) }; +static const struct acpi_device_id tegra_fuse_acpi_match[] = { + { "NVDA200F" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(acpi, tegra_fuse_acpi_match); + static struct platform_driver tegra_fuse_driver = { .driver = { .name = "tegra-fuse", .of_match_table = tegra_fuse_match, + .acpi_match_table = tegra_fuse_acpi_match, .pm = &tegra_fuse_pm, .suppress_bind_attrs = true, }, @@ -300,7 +340,13 @@ u32 __init tegra_fuse_read_early(unsigned int offset) int tegra_fuse_readl(unsigned long offset, u32 *value) { - if (!fuse->read || !fuse->clk) + /* + * Wait for fuse->clk to be initialized if device-tree boot is used. + */ + if (is_of_node(dev_fwnode(fuse->dev)) && !fuse->clk) + return -EPROBE_DEFER; + + if (!fuse->read) return -EPROBE_DEFER; if (IS_ERR(fuse->clk)) @@ -383,7 +429,7 @@ const struct attribute_group tegra194_soc_attr_group = { }; #endif -struct device * __init tegra_soc_device_register(void) +struct device *tegra_soc_device_register(void) { struct soc_device_attribute *attr; struct soc_device *dev; -- cgit v1.2.3-58-ga151 From 8402074f30238ee1bdc70b843932cd7350830ab6 Mon Sep 17 00:00:00 2001 From: Kartik Date: Tue, 17 Oct 2023 10:53:22 +0530 Subject: soc/tegra: fuse: Add support for Tegra241 Add support for Tegra241 which use ACPI boot. Signed-off-by: Kartik Signed-off-by: Thierry Reding --- drivers/soc/tegra/Kconfig | 5 +++++ drivers/soc/tegra/fuse/fuse-tegra.c | 5 +++++ drivers/soc/tegra/fuse/fuse-tegra30.c | 20 ++++++++++++++++++++ drivers/soc/tegra/fuse/fuse.h | 4 ++++ drivers/soc/tegra/fuse/tegra-apbmisc.c | 1 + include/soc/tegra/fuse.h | 1 + 6 files changed, 36 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig index f16beeabaa92..33512558af9f 100644 --- a/drivers/soc/tegra/Kconfig +++ b/drivers/soc/tegra/Kconfig @@ -133,6 +133,11 @@ config ARCH_TEGRA_234_SOC help Enable support for the NVIDIA Tegra234 SoC. +config ARCH_TEGRA_241_SOC + bool "NVIDIA Tegra241 SoC" + help + Enable support for the NVIDIA Tegra241 SoC. + endif endif diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 1c758f121f91..233b8e7bb41b 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -171,6 +171,11 @@ static int tegra_fuse_probe(struct platform_device *pdev) case TEGRA234: fuse->soc = &tegra234_fuse_soc; break; +#endif +#if defined(CONFIG_ARCH_TEGRA_241_SOC) + case TEGRA241: + fuse->soc = &tegra241_fuse_soc; + break; #endif default: return dev_err_probe(&pdev->dev, -EINVAL, "Unsupported SoC: %02x\n", chip); diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c index e94d46372a63..2070d36c510d 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra30.c +++ b/drivers/soc/tegra/fuse/fuse-tegra30.c @@ -678,3 +678,23 @@ const struct tegra_fuse_soc tegra234_fuse_soc = { .clk_suspend_on = false, }; #endif + +#if defined(CONFIG_ARCH_TEGRA_241_SOC) +static const struct tegra_fuse_info tegra241_fuse_info = { + .read = tegra30_fuse_read, + .size = 0x16008, + .spare = 0xcf0, +}; + +static const struct nvmem_keepout tegra241_fuse_keepouts[] = { + { .start = 0xc, .end = 0x1600c } +}; + +const struct tegra_fuse_soc tegra241_fuse_soc = { + .init = tegra30_fuse_init, + .info = &tegra241_fuse_info, + .keepouts = tegra241_fuse_keepouts, + .num_keepouts = ARRAY_SIZE(tegra241_fuse_keepouts), + .soc_attr_group = &tegra194_soc_attr_group, +}; +#endif diff --git a/drivers/soc/tegra/fuse/fuse.h b/drivers/soc/tegra/fuse/fuse.h index a41e9f85281a..f3b705327c20 100644 --- a/drivers/soc/tegra/fuse/fuse.h +++ b/drivers/soc/tegra/fuse/fuse.h @@ -136,4 +136,8 @@ extern const struct tegra_fuse_soc tegra194_fuse_soc; extern const struct tegra_fuse_soc tegra234_fuse_soc; #endif +#ifdef CONFIG_ARCH_TEGRA_241_SOC +extern const struct tegra_fuse_soc tegra241_fuse_soc; +#endif + #endif diff --git a/drivers/soc/tegra/fuse/tegra-apbmisc.c b/drivers/soc/tegra/fuse/tegra-apbmisc.c index 6457f80821bb..e2ca5d55fd31 100644 --- a/drivers/soc/tegra/fuse/tegra-apbmisc.c +++ b/drivers/soc/tegra/fuse/tegra-apbmisc.c @@ -64,6 +64,7 @@ bool tegra_is_silicon(void) switch (tegra_get_chip_id()) { case TEGRA194: case TEGRA234: + case TEGRA241: case TEGRA264: if (tegra_get_platform() == 0) return true; diff --git a/include/soc/tegra/fuse.h b/include/soc/tegra/fuse.h index 3a513be50243..8f421b9f7585 100644 --- a/include/soc/tegra/fuse.h +++ b/include/soc/tegra/fuse.h @@ -17,6 +17,7 @@ #define TEGRA186 0x18 #define TEGRA194 0x19 #define TEGRA234 0x23 +#define TEGRA241 0x24 #define TEGRA264 0x26 #define TEGRA_FUSE_SKU_CALIB_0 0xf0 -- cgit v1.2.3-58-ga151 From 7a849d0b757c8afa642e112a676767687e46d3a5 Mon Sep 17 00:00:00 2001 From: Kartik Date: Wed, 20 Dec 2023 11:40:13 +0530 Subject: soc/tegra: fuse: Define tegra194_soc_attr_group for Tegra241 Tegra241 SoC data uses tegra194_soc_attr_group, which is only defined if config CONFIG_ARCH_TEGRA_194_SOC or CONFIG_ARCH_TEGRA_234_SOC or both are enabled. This causes a build failure if both of these configs are disabled and CONFIG_ARCH_TEGRA_241_SOC is enabled. Define tegra194_soc_attr_group if CONFIG_ARCH_TEGRA_241_SOC is enabled. Signed-off-by: Kartik Acked-by: Randy Dunlap Tested-by: Randy Dunlap # build-tested Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 3 ++- drivers/soc/tegra/fuse/fuse.h | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index 233b8e7bb41b..c34efa5bf44c 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -407,7 +407,8 @@ const struct attribute_group tegra_soc_attr_group = { }; #if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) + IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \ + IS_ENABLED(CONFIG_ARCH_TEGRA_241_SOC) static ssize_t platform_show(struct device *dev, struct device_attribute *attr, char *buf) { diff --git a/drivers/soc/tegra/fuse/fuse.h b/drivers/soc/tegra/fuse/fuse.h index f3b705327c20..9fee6ad6ad9e 100644 --- a/drivers/soc/tegra/fuse/fuse.h +++ b/drivers/soc/tegra/fuse/fuse.h @@ -124,7 +124,8 @@ extern const struct tegra_fuse_soc tegra186_fuse_soc; #endif #if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \ - IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) + IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \ + IS_ENABLED(CONFIG_ARCH_TEGRA_241_SOC) extern const struct attribute_group tegra194_soc_attr_group; #endif -- cgit v1.2.3-58-ga151 From 81b3f0efbbced8dbf4ef4a4c0008a7ada427b38d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Mon, 29 Jan 2024 13:46:59 +0000 Subject: soc/tegra: fuse: Fix crash in tegra_fuse_readl() Commit c5b2d43e67bb ("soc/tegra: fuse: Add ACPI support for Tegra194 and Tegra234") updated the Tegra fuse driver to add ACPI support and added a test to the tegra_fuse_readl() function to check if the device is booting with device-tree. This test passes 'fuse->dev' variable to dev_fwnode() but does not check first is 'fuse->dev' is valid. This is causing a crash to occur in Tegra XUSB PHY driver that calls the tegra_fuse_readl() function before 'fuse->dev' variable has been initialised ... Unable to handle kernel NULL pointer dereference at virtual address 0000000000000290 Mem abort info: ESR = 0x0000000096000004 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x04: level 0 translation fault Data abort info: ISV = 0, ISS = 0x00000004, ISS2 = 0x00000000 CM = 0, WnR = 0, TnD = 0, TagAccess = 0 GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [0000000000000290] user address but active_mm is swapper Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP Modules linked in: CPU: 7 PID: 70 Comm: kworker/u16:4 Not tainted 6.8.0-rc1-next-20240129-02825-g596764183be8 #1 Hardware name: NVIDIA Jetson AGX Xavier Developer Kit (DT) Workqueue: events_unbound deferred_probe_work_func pstate: 60400009 (nZCv daif +PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : __dev_fwnode+0x0/0x18 lr : tegra_fuse_readl+0x24/0x98 sp : ffff80008393ba10 x29: ffff80008393ba10 x28: 0000000000000000 x27: ffff800081233c10 x26: 00000000000001c8 x25: ffff000080b7bc10 x24: ffff000082df3b00 x23: fffffffffffffff4 x22: 0000000000000004 x21: ffff80008393ba84 x20: 00000000000000f0 x19: ffff800082f1e000 x18: ffff800081d72000 x17: 0000000000000001 x16: 0000000000000001 x15: ffff800082fcdfff x14: 0000000000000000 x13: 0000000003541000 x12: 0000000000000020 x11: 0140000000000000 x10: ffff800080000000 x9 : 0000000000000000 x8 : ffff000082df3b40 x7 : 0000000000000000 x6 : 000000000000003f x5 : 00000000ffffffff x4 : 0000000000000dc0 x3 : 00000000000000c0 x2 : 0000000000000001 x1 : ffff80008393ba84 x0 : 0000000000000000 Call trace: __dev_fwnode+0x0/0x18 tegra186_xusb_padctl_probe+0xb0/0x1a8 tegra_xusb_padctl_probe+0x7c/0xebc platform_probe+0x90/0xd8 really_probe+0x13c/0x29c __driver_probe_device+0x7c/0x124 driver_probe_device+0x38/0x11c __device_attach_driver+0x90/0xdc bus_for_each_drv+0x78/0xdc __device_attach+0xfc/0x188 device_initial_probe+0x10/0x18 bus_probe_device+0xa4/0xa8 deferred_probe_work_func+0x80/0xb4 process_scheduled_works+0x178/0x3e0 worker_thread+0x164/0x2e8 kthread+0xfc/0x11c ret_from_fork+0x10/0x20 Code: a8c27bfd d65f03c0 128002a0 d65f03c0 (f9414801) ---[ end trace 0000000000000000 ]--- Fix this by verifying that 'fuse->dev' is valid before passing to dev_fwnode(). Fixes: c5b2d43e67bb ("soc/tegra: fuse: Add ACPI support for Tegra194 and Tegra234") Signed-off-by: Jon Hunter Reviewed-by: Kartik Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/tegra/fuse/fuse-tegra.c b/drivers/soc/tegra/fuse/fuse-tegra.c index c34efa5bf44c..b6bfd6729df3 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra.c +++ b/drivers/soc/tegra/fuse/fuse-tegra.c @@ -345,6 +345,9 @@ u32 __init tegra_fuse_read_early(unsigned int offset) int tegra_fuse_readl(unsigned long offset, u32 *value) { + if (!fuse->dev) + return -EPROBE_DEFER; + /* * Wait for fuse->clk to be initialized if device-tree boot is used. */ -- cgit v1.2.3-58-ga151 From d820100a1bdec5fd671310de902dc8baea317a3a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 3 Jan 2024 11:26:49 +0100 Subject: soc/tegra: Fix build failure on Tegra241 If all the other SoCs are disabled, the driver fails to build: drivers/soc/tegra/fuse/fuse-tegra30.c:684:17: error: 'tegra30_fuse_read' undeclared here (not in a function); did you mean 'tegra_fuse_readl'? 684 | .read = tegra30_fuse_read, | ^~~~~~~~~~~~~~~~~ | tegra_fuse_readl drivers/soc/tegra/fuse/fuse-tegra30.c:694:17: error: 'tegra30_fuse_init' undeclared here (not in a function); did you mean 'tegra_fuse_info'? 694 | .init = tegra30_fuse_init, | ^~~~~~~~~~~~~~~~~ Fix the list of SoCs using this function to include the newly added one. Fixes: dee509eb9cd5 ("soc/tegra: fuse: Add support for Tegra241") Signed-off-by: Arnd Bergmann Reviewed-by: Jon Hunter Reviewed-by: Kartik Signed-off-by: Thierry Reding --- drivers/soc/tegra/fuse/fuse-tegra30.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/fuse/fuse-tegra30.c b/drivers/soc/tegra/fuse/fuse-tegra30.c index 2070d36c510d..eb14e5ff5a0a 100644 --- a/drivers/soc/tegra/fuse/fuse-tegra30.c +++ b/drivers/soc/tegra/fuse/fuse-tegra30.c @@ -38,7 +38,8 @@ defined(CONFIG_ARCH_TEGRA_210_SOC) || \ defined(CONFIG_ARCH_TEGRA_186_SOC) || \ defined(CONFIG_ARCH_TEGRA_194_SOC) || \ - defined(CONFIG_ARCH_TEGRA_234_SOC) + defined(CONFIG_ARCH_TEGRA_234_SOC) || \ + defined(CONFIG_ARCH_TEGRA_241_SOC) static u32 tegra30_fuse_read_early(struct tegra_fuse *fuse, unsigned int offset) { if (WARN_ON(!fuse->base)) -- cgit v1.2.3-58-ga151 From 5b2dd77be1d85ac3a8be3749f5605bf0830e2998 Mon Sep 17 00:00:00 2001 From: Anjelique Melendez Date: Thu, 1 Feb 2024 12:44:24 -0800 Subject: soc: qcom: add QCOM PBS driver Add the Qualcomm PBS (Programmable Boot Sequencer) driver. The QCOM PBS driver supports configuring software PBS trigger events through PBS RAM on Qualcomm Technologies, Inc (QTI) PMICs. Signed-off-by: Anjelique Melendez Link: https://lore.kernel.org/r/20240201204421.16992-6-quic_amelende@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/Kconfig | 9 ++ drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/qcom-pbs.c | 236 ++++++++++++++++++++++++++++++++++++++ include/linux/soc/qcom/qcom-pbs.h | 30 +++++ 4 files changed, 276 insertions(+) create mode 100644 drivers/soc/qcom/qcom-pbs.c create mode 100644 include/linux/soc/qcom/qcom-pbs.h (limited to 'drivers') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index c6ca4de42586..5af33b0e3470 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -268,4 +268,13 @@ config QCOM_INLINE_CRYPTO_ENGINE tristate select QCOM_SCM +config QCOM_PBS + tristate "PBS trigger support for Qualcomm Technologies, Inc. PMICS" + depends on SPMI + help + This driver supports configuring software programmable boot sequencer (PBS) + trigger event through PBS RAM on Qualcomm Technologies, Inc. PMICs. + This module provides the APIs to the client drivers that wants to send the + PBS trigger event to the PBS RAM. + endmenu diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index 05b3d54e8dc9..0a419b458fb2 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -34,3 +34,4 @@ obj-$(CONFIG_QCOM_KRYO_L2_ACCESSORS) += kryo-l2-accessors.o obj-$(CONFIG_QCOM_ICC_BWMON) += icc-bwmon.o qcom_ice-objs += ice.o obj-$(CONFIG_QCOM_INLINE_CRYPTO_ENGINE) += qcom_ice.o +obj-$(CONFIG_QCOM_PBS) += qcom-pbs.o diff --git a/drivers/soc/qcom/qcom-pbs.c b/drivers/soc/qcom/qcom-pbs.c new file mode 100644 index 000000000000..6af49b5060e5 --- /dev/null +++ b/drivers/soc/qcom/qcom-pbs.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PBS_CLIENT_TRIG_CTL 0x42 +#define PBS_CLIENT_SW_TRIG_BIT BIT(7) +#define PBS_CLIENT_SCRATCH1 0x50 +#define PBS_CLIENT_SCRATCH2 0x51 +#define PBS_CLIENT_SCRATCH2_ERROR 0xFF + +#define RETRIES 2000 +#define DELAY 1100 + +struct pbs_dev { + struct device *dev; + struct regmap *regmap; + struct mutex lock; + struct device_link *link; + + u32 base; +}; + +static int qcom_pbs_wait_for_ack(struct pbs_dev *pbs, u8 bit_pos) +{ + unsigned int val; + int ret; + + ret = regmap_read_poll_timeout(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, + val, val & BIT(bit_pos), DELAY, DELAY * RETRIES); + + if (ret < 0) { + dev_err(pbs->dev, "Timeout for PBS ACK/NACK for bit %u\n", bit_pos); + return -ETIMEDOUT; + } + + if (val == PBS_CLIENT_SCRATCH2_ERROR) { + ret = regmap_write(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 0); + dev_err(pbs->dev, "NACK from PBS for bit %u\n", bit_pos); + return -EINVAL; + } + + dev_dbg(pbs->dev, "PBS sequence for bit %u executed!\n", bit_pos); + return 0; +} + +/** + * qcom_pbs_trigger_event() - Trigger the PBS RAM sequence + * @pbs: Pointer to PBS device + * @bitmap: bitmap + * + * This function is used to trigger the PBS RAM sequence to be + * executed by the client driver. + * + * The PBS trigger sequence involves + * 1. setting the PBS sequence bit in PBS_CLIENT_SCRATCH1 + * 2. Initiating the SW PBS trigger + * 3. Checking the equivalent bit in PBS_CLIENT_SCRATCH2 for the + * completion of the sequence. + * 4. If PBS_CLIENT_SCRATCH2 == 0xFF, the PBS sequence failed to execute + * + * Return: 0 on success, < 0 on failure + */ +int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap) +{ + unsigned int val; + u16 bit_pos; + int ret; + + if (WARN_ON(!bitmap)) + return -EINVAL; + + if (IS_ERR_OR_NULL(pbs)) + return -EINVAL; + + mutex_lock(&pbs->lock); + ret = regmap_read(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, &val); + if (ret < 0) + goto out; + + if (val == PBS_CLIENT_SCRATCH2_ERROR) { + /* PBS error - clear SCRATCH2 register */ + ret = regmap_write(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, 0); + if (ret < 0) + goto out; + } + + for (bit_pos = 0; bit_pos < 8; bit_pos++) { + if (!(bitmap & BIT(bit_pos))) + continue; + + /* Clear the PBS sequence bit position */ + ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, + BIT(bit_pos), 0); + if (ret < 0) + goto out_clear_scratch1; + + /* Set the PBS sequence bit position */ + ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, + BIT(bit_pos), BIT(bit_pos)); + if (ret < 0) + goto out_clear_scratch1; + + /* Initiate the SW trigger */ + ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_TRIG_CTL, + PBS_CLIENT_SW_TRIG_BIT, PBS_CLIENT_SW_TRIG_BIT); + if (ret < 0) + goto out_clear_scratch1; + + ret = qcom_pbs_wait_for_ack(pbs, bit_pos); + if (ret < 0) + goto out_clear_scratch1; + + /* Clear the PBS sequence bit position */ + regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, BIT(bit_pos), 0); + regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH2, BIT(bit_pos), 0); + } + +out_clear_scratch1: + /* Clear all the requested bitmap */ + ret = regmap_update_bits(pbs->regmap, pbs->base + PBS_CLIENT_SCRATCH1, bitmap, 0); + +out: + mutex_unlock(&pbs->lock); + + return ret; +} +EXPORT_SYMBOL_GPL(qcom_pbs_trigger_event); + +/** + * get_pbs_client_device() - Get the PBS device used by client + * @dev: Client device + * + * This function is used to get the PBS device that is being + * used by the client. + * + * Return: pbs_dev on success, ERR_PTR on failure + */ +struct pbs_dev *get_pbs_client_device(struct device *dev) +{ + struct device_node *pbs_dev_node; + struct platform_device *pdev; + struct pbs_dev *pbs; + + pbs_dev_node = of_parse_phandle(dev->of_node, "qcom,pbs", 0); + if (!pbs_dev_node) { + dev_err(dev, "Missing qcom,pbs property\n"); + return ERR_PTR(-ENODEV); + } + + pdev = of_find_device_by_node(pbs_dev_node); + if (!pdev) { + dev_err(dev, "Unable to find PBS dev_node\n"); + pbs = ERR_PTR(-EPROBE_DEFER); + goto out; + } + + pbs = platform_get_drvdata(pdev); + if (!pbs) { + dev_err(dev, "Cannot get pbs instance from %s\n", dev_name(&pdev->dev)); + platform_device_put(pdev); + pbs = ERR_PTR(-EPROBE_DEFER); + goto out; + } + + pbs->link = device_link_add(dev, &pdev->dev, DL_FLAG_AUTOREMOVE_SUPPLIER); + if (!pbs->link) { + dev_err(&pdev->dev, "Failed to create device link to consumer %s\n", dev_name(dev)); + platform_device_put(pdev); + pbs = ERR_PTR(-EINVAL); + goto out; + } + +out: + of_node_put(pbs_dev_node); + return pbs; +} +EXPORT_SYMBOL_GPL(get_pbs_client_device); + +static int qcom_pbs_probe(struct platform_device *pdev) +{ + struct pbs_dev *pbs; + u32 val; + int ret; + + pbs = devm_kzalloc(&pdev->dev, sizeof(*pbs), GFP_KERNEL); + if (!pbs) + return -ENOMEM; + + pbs->dev = &pdev->dev; + pbs->regmap = dev_get_regmap(pbs->dev->parent, NULL); + if (!pbs->regmap) { + dev_err(pbs->dev, "Couldn't get parent's regmap\n"); + return -EINVAL; + } + + ret = device_property_read_u32(pbs->dev, "reg", &val); + if (ret < 0) { + dev_err(pbs->dev, "Couldn't find reg, ret = %d\n", ret); + return ret; + } + pbs->base = val; + mutex_init(&pbs->lock); + + platform_set_drvdata(pdev, pbs); + + return 0; +} + +static const struct of_device_id qcom_pbs_match_table[] = { + { .compatible = "qcom,pbs" }, + {} +}; +MODULE_DEVICE_TABLE(of, qcom_pbs_match_table); + +static struct platform_driver qcom_pbs_driver = { + .driver = { + .name = "qcom-pbs", + .of_match_table = qcom_pbs_match_table, + }, + .probe = qcom_pbs_probe, +}; +module_platform_driver(qcom_pbs_driver) + +MODULE_DESCRIPTION("QCOM PBS DRIVER"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/soc/qcom/qcom-pbs.h b/include/linux/soc/qcom/qcom-pbs.h new file mode 100644 index 000000000000..8a46209ccf13 --- /dev/null +++ b/include/linux/soc/qcom/qcom-pbs.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _QCOM_PBS_H +#define _QCOM_PBS_H + +#include +#include + +struct device_node; +struct pbs_dev; + +#if IS_ENABLED(CONFIG_QCOM_PBS) +int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap); +struct pbs_dev *get_pbs_client_device(struct device *client_dev); +#else +static inline int qcom_pbs_trigger_event(struct pbs_dev *pbs, u8 bitmap) +{ + return -ENODEV; +} + +static inline struct pbs_dev *get_pbs_client_device(struct device *client_dev) +{ + return ERR_PTR(-ENODEV); +} +#endif + +#endif -- cgit v1.2.3-58-ga151 From 0daf87e7b47e05d0c9ccab648d5e45eaddb547f8 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sun, 4 Feb 2024 12:56:44 -0300 Subject: bus: sunxi-rsb: make sunxi_rsb_bus const Now that the driver core can properly handle constant struct bus_type, move the sunxi_rsb_bus variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Acked-by: Jernej Skrabec Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240204-bus_cleanup-bus-v1-1-bda309c4b829@marliere.net Signed-off-by: Jernej Skrabec --- drivers/bus/sunxi-rsb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index fd3e9d82340a..1e29ba76615d 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -128,7 +128,7 @@ struct sunxi_rsb { }; /* bus / slave device related functions */ -static struct bus_type sunxi_rsb_bus; +static const struct bus_type sunxi_rsb_bus; static int sunxi_rsb_device_match(struct device *dev, struct device_driver *drv) { @@ -177,7 +177,7 @@ static int sunxi_rsb_device_modalias(const struct device *dev, struct kobj_ueven return of_device_uevent_modalias(dev, env); } -static struct bus_type sunxi_rsb_bus = { +static const struct bus_type sunxi_rsb_bus = { .name = RSB_CTRL_NAME, .match = sunxi_rsb_device_match, .probe = sunxi_rsb_device_probe, -- cgit v1.2.3-58-ga151 From d2e8899de71cd0a3c22a0eadfb9d54604d34eb96 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Tue, 13 Feb 2024 11:44:01 -0300 Subject: soc: qcom: apr: make aprbus const Since commit d492cc2573a0 ("driver core: device.h: make struct bus_type a const *"), the driver core can properly handle constant struct bus_type, move the aprbus variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: "Ricardo B. Marliere" Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240213-bus_cleanup-apr-v1-1-50c824eec06d@marliere.net Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/apr.c | 2 +- include/linux/soc/qcom/apr.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/apr.c b/drivers/soc/qcom/apr.c index 1f8b315576a4..50749e870efa 100644 --- a/drivers/soc/qcom/apr.c +++ b/drivers/soc/qcom/apr.c @@ -399,7 +399,7 @@ static int apr_uevent(const struct device *dev, struct kobj_uevent_env *env) return add_uevent_var(env, "MODALIAS=apr:%s", adev->name); } -struct bus_type aprbus = { +const struct bus_type aprbus = { .name = "aprbus", .match = apr_device_match, .probe = apr_device_probe, diff --git a/include/linux/soc/qcom/apr.h b/include/linux/soc/qcom/apr.h index be98aebcb3e1..7161a3183eda 100644 --- a/include/linux/soc/qcom/apr.h +++ b/include/linux/soc/qcom/apr.h @@ -9,7 +9,7 @@ #include #include -extern struct bus_type aprbus; +extern const struct bus_type aprbus; #define APR_HDR_LEN(hdr_len) ((hdr_len)/4) -- cgit v1.2.3-58-ga151 From c8f349ac13d174f0ceb26df67b86075b491cacc1 Mon Sep 17 00:00:00 2001 From: Danila Tikhonov Date: Mon, 12 Feb 2024 23:14:28 +0300 Subject: soc: qcom: socinfo: Add Soc IDs for SM8475 family Add Soc ID table entries for Qualcomm SM8475 family. Signed-off-by: Danila Tikhonov Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20240212201428.87151-3-danila@jiaxyga.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/socinfo.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/qcom/socinfo.c b/drivers/soc/qcom/socinfo.c index 5a44ad870fb6..e8ff9819ac47 100644 --- a/drivers/soc/qcom/socinfo.c +++ b/drivers/soc/qcom/socinfo.c @@ -424,8 +424,11 @@ static const struct soc_id soc_id[] = { { qcom_board_id(IPQ9510) }, { qcom_board_id(QRB4210) }, { qcom_board_id(QRB2210) }, + { qcom_board_id(SM8475) }, + { qcom_board_id(SM8475P) }, { qcom_board_id(SA8775P) }, { qcom_board_id(QRU1000) }, + { qcom_board_id(SM8475_2) }, { qcom_board_id(QDU1000) }, { qcom_board_id(SM8650) }, { qcom_board_id(SM4450) }, -- cgit v1.2.3-58-ga151 From ceeaddc19a90039861564d8e1078b778a8f95101 Mon Sep 17 00:00:00 2001 From: Unnathi Chalicheemala Date: Mon, 12 Feb 2024 10:35:15 -0800 Subject: soc: qcom: llcc: Check return value on Broadcast_OR reg read Commit c72ca343f911 ("soc: qcom: llcc: Add v4.1 HW version support") introduced a new 4.1 if statement in llcc_update_act_ctrl() without considering that ret might be overwritten. So, add return value check after Broadcast_OR register read in llcc_update_act_ctrl(). Fixes: c72ca343f911 ("soc: qcom: llcc: Add v4.1 HW version support") Signed-off-by: Unnathi Chalicheemala Reviewed-by: Elliot Berman Reviewed-by: Mukesh Ojha Link: https://lore.kernel.org/r/20240212183515.433873-1-quic_uchalich@quicinc.com Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/llcc-qcom.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/qcom/llcc-qcom.c b/drivers/soc/qcom/llcc-qcom.c index 4ca88eaebf06..cbef0dea1d5d 100644 --- a/drivers/soc/qcom/llcc-qcom.c +++ b/drivers/soc/qcom/llcc-qcom.c @@ -859,6 +859,8 @@ static int llcc_update_act_ctrl(u32 sid, ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg, slice_status, !(slice_status & status), 0, LLCC_STATUS_READ_DELAY); + if (ret) + return ret; if (drv_data->version >= LLCC_VERSION_4_1_0_0) ret = regmap_write(drv_data->bcast_regmap, act_clear_reg, -- cgit v1.2.3-58-ga151 From 4acd21a45c1446277e2abaece97d7fa7c2e692a9 Mon Sep 17 00:00:00 2001 From: Peter Robinson Date: Fri, 16 Feb 2024 10:02:37 +0000 Subject: bus: tegra-aconnect: Update dependency to ARCH_TEGRA Update the architecture dependency to be the generic Tegra because the driver works on the four latest Tegra generations not just Tegra210, if you build a kernel with a specific ARCH_TEGRA_xxx_SOC option that excludes Tegra210 you don't get this driver. Fixes: 46a88534afb59 ("bus: Add support for Tegra ACONNECT") Signed-off-by: Peter Robinson Cc: Jon Hunter Cc: Thierry Reding Signed-off-by: Thierry Reding --- drivers/bus/Kconfig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index e6742998f372..d5e7fa9173a1 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -186,11 +186,12 @@ config SUNXI_RSB config TEGRA_ACONNECT tristate "Tegra ACONNECT Bus Driver" - depends on ARCH_TEGRA_210_SOC + depends on ARCH_TEGRA depends on OF && PM help Driver for the Tegra ACONNECT bus which is used to interface with - the devices inside the Audio Processing Engine (APE) for Tegra210. + the devices inside the Audio Processing Engine (APE) for + Tegra210 and later. config TEGRA_GMI tristate "Tegra Generic Memory Interface bus driver" -- cgit v1.2.3-58-ga151 From 6f4429e21a7fef60df80c567eed0af189e2c02c7 Mon Sep 17 00:00:00 2001 From: Petlozu Pravareshwar Date: Sun, 11 Feb 2024 17:17:25 +0000 Subject: soc/tegra: pmc: Update address mapping sequence for PMC apertures On Tegra SoCs prior to Tegra186, PMC has single address range only. Starting from and after Tegra186, PMC has additional address ranges apart from base address range. Currently in PMC driver, we try to map these additional address ranges on all SoCs and if we fail then we assume that the range is not valid for an SoC. This change makes it more explicit on which address ranges are expected to be present on which SoCs and maps the additional address ranges only on SoCs from and after Tegra186. Signed-off-by: Petlozu Pravareshwar Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 6dfcc7f50ece..0bc983f6b088 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -384,6 +384,7 @@ struct tegra_pmc_soc { bool has_blink_output; bool has_usb_sleepwalk; bool supports_core_domain; + bool has_single_mmio_aperture; }; /** @@ -2885,31 +2886,28 @@ static int tegra_pmc_probe(struct platform_device *pdev) if (IS_ERR(base)) return PTR_ERR(base); - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wake"); - if (res) { + if (pmc->soc->has_single_mmio_aperture) { + pmc->wake = base; + pmc->aotag = base; + pmc->scratch = base; + } else { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "wake"); pmc->wake = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pmc->wake)) return PTR_ERR(pmc->wake); - } else { - pmc->wake = base; - } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aotag"); - if (res) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "aotag"); pmc->aotag = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pmc->aotag)) return PTR_ERR(pmc->aotag); - } else { - pmc->aotag = base; - } - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch"); - if (res) { + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, + "scratch"); pmc->scratch = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(pmc->scratch)) return PTR_ERR(pmc->scratch); - } else { - pmc->scratch = base; } pmc->clk = devm_clk_get_optional(&pdev->dev, "pclk"); @@ -3300,6 +3298,7 @@ static const struct tegra_pmc_soc tegra20_pmc_soc = { .num_pmc_clks = 0, .has_blink_output = true, .has_usb_sleepwalk = true, + .has_single_mmio_aperture = true, }; static const char * const tegra30_powergates[] = { @@ -3361,6 +3360,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = { .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data), .has_blink_output = true, .has_usb_sleepwalk = true, + .has_single_mmio_aperture = true, }; static const char * const tegra114_powergates[] = { @@ -3418,6 +3418,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = { .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data), .has_blink_output = true, .has_usb_sleepwalk = true, + .has_single_mmio_aperture = true, }; static const char * const tegra124_powergates[] = { @@ -3562,6 +3563,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = { .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data), .has_blink_output = true, .has_usb_sleepwalk = true, + .has_single_mmio_aperture = true, }; static const char * const tegra210_powergates[] = { @@ -3725,6 +3727,7 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { .num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data), .has_blink_output = true, .has_usb_sleepwalk = true, + .has_single_mmio_aperture = true, }; static const struct tegra_io_pad_soc tegra186_io_pads[] = { @@ -3922,6 +3925,7 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { .num_pmc_clks = 0, .has_blink_output = false, .has_usb_sleepwalk = false, + .has_single_mmio_aperture = false, }; static const struct tegra_io_pad_soc tegra194_io_pads[] = { @@ -4107,6 +4111,7 @@ static const struct tegra_pmc_soc tegra194_pmc_soc = { .num_pmc_clks = 0, .has_blink_output = false, .has_usb_sleepwalk = false, + .has_single_mmio_aperture = false, }; static const struct tegra_io_pad_soc tegra234_io_pads[] = { @@ -4235,6 +4240,7 @@ static const struct tegra_pmc_soc tegra234_pmc_soc = { .pmc_clks_data = NULL, .num_pmc_clks = 0, .has_blink_output = false, + .has_single_mmio_aperture = false, }; static const struct of_device_id tegra_pmc_match[] = { -- cgit v1.2.3-58-ga151 From ccd8e76fdb8d4219097b09660cfc41385e055906 Mon Sep 17 00:00:00 2001 From: Petlozu Pravareshwar Date: Sun, 11 Feb 2024 17:17:27 +0000 Subject: soc/tegra: pmc: Update scratch as an optional aperture Scratch address space register is used to store reboot reason. For some Tegra234 systems, the scratch space is not available to store the reboot reason. This is because scratch region on these systems is not accessible by the kernel as restricted by the Hypervisor. Such systems would delist scratch aperture from PMC DT node. Hence this change makes scratch as optional aperture and also avoids registering reboot notifier if scratch address space isn't mapped. Signed-off-by: Petlozu Pravareshwar Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 0bc983f6b088..6948f78c7a4a 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -2903,11 +2903,16 @@ static int tegra_pmc_probe(struct platform_device *pdev) if (IS_ERR(pmc->aotag)) return PTR_ERR(pmc->aotag); + /* "scratch" is an optional aperture */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch"); - pmc->scratch = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(pmc->scratch)) - return PTR_ERR(pmc->scratch); + if (res) { + pmc->scratch = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(pmc->scratch)) + return PTR_ERR(pmc->scratch); + } else { + pmc->scratch = NULL; + } } pmc->clk = devm_clk_get_optional(&pdev->dev, "pclk"); @@ -2919,12 +2924,15 @@ static int tegra_pmc_probe(struct platform_device *pdev) * PMC should be last resort for restarting since it soft-resets * CPU without resetting everything else. */ - err = devm_register_reboot_notifier(&pdev->dev, - &tegra_pmc_reboot_notifier); - if (err) { - dev_err(&pdev->dev, "unable to register reboot notifier, %d\n", - err); - return err; + if (pmc->scratch) { + err = devm_register_reboot_notifier(&pdev->dev, + &tegra_pmc_reboot_notifier); + if (err) { + dev_err(&pdev->dev, + "unable to register reboot notifier, %d\n", + err); + return err; + } } err = devm_register_sys_off_handler(&pdev->dev, -- cgit v1.2.3-58-ga151 From ae7d2d9b8ebe9f107c500808d5bcd68397645720 Mon Sep 17 00:00:00 2001 From: Prathamesh Shete Date: Fri, 16 Feb 2024 08:04:50 +0000 Subject: soc/tegra: pmc: Add SD wake event for Tegra234 Add SD wake event for Tegra234 so that system can be woken up from suspend when SD card hot-plug/unplug event is detected. Signed-off-by: Prathamesh Shete Signed-off-by: Petlozu Pravareshwar Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 6948f78c7a4a..d6bfcea5ee65 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -3,7 +3,7 @@ * drivers/soc/tegra/pmc.c * * Copyright (c) 2010 Google, Inc - * Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved. * * Author: * Colin Cross @@ -4209,6 +4209,7 @@ static const char * const tegra234_reset_sources[] = { }; static const struct tegra_wake_event tegra234_wake_events[] = { + TEGRA_WAKE_GPIO("sd-wake", 8, 0, TEGRA234_MAIN_GPIO(G, 7)), TEGRA_WAKE_IRQ("pmu", 24, 209), TEGRA_WAKE_GPIO("power", 29, 1, TEGRA234_AON_GPIO(EE, 4)), TEGRA_WAKE_GPIO("mgbe", 56, 0, TEGRA234_MAIN_GPIO(Y, 3)), -- cgit v1.2.3-58-ga151 From 57e2b067f19b8de616d1e849ce3786df602bfe7f Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 2 Jan 2024 07:17:24 +0200 Subject: soc: qcom: spm: remove driver-internal structures from the driver API Move internal SPM driver structures to the driver itself, removing them from the public API. The CPUidle driver doesn't use them at all. Reviewed-by: Konrad Dybcio Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20240102-saw2-spm-regulator-v7-4-0472ec237f49@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/spm.c | 20 ++++++++++++++++++++ include/soc/qcom/spm.h | 23 +---------------------- 2 files changed, 21 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index 2f0b1bfe7658..b15435f7cb0d 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -36,6 +36,26 @@ enum spm_reg { SPM_REG_NR, }; +#define MAX_PMIC_DATA 2 +#define MAX_SEQ_DATA 64 + +struct spm_reg_data { + const u16 *reg_offset; + u32 spm_cfg; + u32 spm_dly; + u32 pmic_dly; + u32 pmic_data[MAX_PMIC_DATA]; + u32 avs_ctl; + u32 avs_limit; + u8 seq[MAX_SEQ_DATA]; + u8 start_index[PM_SLEEP_MODE_NR]; +}; + +struct spm_driver_data { + void __iomem *reg_base; + const struct spm_reg_data *reg_data; +}; + static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = { [SPM_REG_AVS_CTL] = 0x904, [SPM_REG_AVS_LIMIT] = 0x908, diff --git a/include/soc/qcom/spm.h b/include/soc/qcom/spm.h index 4951f9d8b0bd..5b263c685812 100644 --- a/include/soc/qcom/spm.h +++ b/include/soc/qcom/spm.h @@ -7,11 +7,6 @@ #ifndef __SPM_H__ #define __SPM_H__ -#include - -#define MAX_PMIC_DATA 2 -#define MAX_SEQ_DATA 64 - enum pm_sleep_mode { PM_SLEEP_MODE_STBY, PM_SLEEP_MODE_RET, @@ -20,23 +15,7 @@ enum pm_sleep_mode { PM_SLEEP_MODE_NR, }; -struct spm_reg_data { - const u16 *reg_offset; - u32 spm_cfg; - u32 spm_dly; - u32 pmic_dly; - u32 pmic_data[MAX_PMIC_DATA]; - u32 avs_ctl; - u32 avs_limit; - u8 seq[MAX_SEQ_DATA]; - u8 start_index[PM_SLEEP_MODE_NR]; -}; - -struct spm_driver_data { - void __iomem *reg_base; - const struct spm_reg_data *reg_data; -}; - +struct spm_driver_data; void spm_set_low_power_mode(struct spm_driver_data *drv, enum pm_sleep_mode mode); -- cgit v1.2.3-58-ga151 From 6496dba142f4461360cae263126965e4ac761ab9 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Tue, 2 Jan 2024 07:17:25 +0200 Subject: soc: qcom: spm: add support for voltage regulator The SPM / SAW2 device also provides a voltage regulator functionality with optional AVS (Adaptive Voltage Scaling) support. The exact register sequence and voltage ranges differs from device to device. Signed-off-by: Dmitry Baryshkov Acked-by: Konrad Dybcio Link: https://lore.kernel.org/r/20240102-saw2-spm-regulator-v7-5-0472ec237f49@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/spm.c | 234 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 229 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index b15435f7cb0d..5eefaec72a13 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -6,20 +6,40 @@ * SAW power controller driver */ -#include +#include +#include #include #include +#include +#include +#include #include -#include #include -#include #include +#include +#include + +#include + #include +#define FIELD_SET(current, mask, val) \ + (((current) & ~(mask)) | FIELD_PREP((mask), (val))) + #define SPM_CTL_INDEX 0x7f #define SPM_CTL_INDEX_SHIFT 4 #define SPM_CTL_EN BIT(0) +/* These registers might be specific to SPM 1.1 */ +#define SPM_VCTL_VLVL GENMASK(7, 0) +#define SPM_PMIC_DATA_0_VLVL GENMASK(7, 0) +#define SPM_PMIC_DATA_1_MIN_VSEL GENMASK(5, 0) +#define SPM_PMIC_DATA_1_MAX_VSEL GENMASK(21, 16) + +#define SPM_1_1_AVS_CTL_AVS_ENABLED BIT(27) +#define SPM_AVS_CTL_MAX_VLVL GENMASK(22, 17) +#define SPM_AVS_CTL_MIN_VLVL GENMASK(15, 10) + enum spm_reg { SPM_REG_CFG, SPM_REG_SPM_CTL, @@ -29,10 +49,12 @@ enum spm_reg { SPM_REG_PMIC_DATA_1, SPM_REG_VCTL, SPM_REG_SEQ_ENTRY, - SPM_REG_SPM_STS, + SPM_REG_STS0, + SPM_REG_STS1, SPM_REG_PMIC_STS, SPM_REG_AVS_CTL, SPM_REG_AVS_LIMIT, + SPM_REG_RST, SPM_REG_NR, }; @@ -49,11 +71,20 @@ struct spm_reg_data { u32 avs_limit; u8 seq[MAX_SEQ_DATA]; u8 start_index[PM_SLEEP_MODE_NR]; + + smp_call_func_t set_vdd; + /* for now we support only a single range */ + struct linear_range *range; + unsigned int ramp_delay; + unsigned int init_uV; }; struct spm_driver_data { void __iomem *reg_base; const struct spm_reg_data *reg_data; + struct device *dev; + unsigned int volt_sel; + int reg_cpu; }; static const u16 spm_reg_offset_v4_1[SPM_REG_NR] = { @@ -189,6 +220,10 @@ static const struct spm_reg_data spm_reg_8226_cpu = { static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = { [SPM_REG_CFG] = 0x08, + [SPM_REG_STS0] = 0x0c, + [SPM_REG_STS1] = 0x10, + [SPM_REG_VCTL] = 0x14, + [SPM_REG_AVS_CTL] = 0x18, [SPM_REG_SPM_CTL] = 0x20, [SPM_REG_PMIC_DLY] = 0x24, [SPM_REG_PMIC_DATA_0] = 0x28, @@ -196,7 +231,12 @@ static const u16 spm_reg_offset_v1_1[SPM_REG_NR] = { [SPM_REG_SEQ_ENTRY] = 0x80, }; +static void smp_set_vdd_v1_1(void *data); + /* SPM register data for 8064 */ +static struct linear_range spm_v1_1_regulator_range = + REGULATOR_LINEAR_RANGE(700000, 0, 56, 12500); + static const struct spm_reg_data spm_reg_8064_cpu = { .reg_offset = spm_reg_offset_v1_1, .spm_cfg = 0x1F, @@ -207,6 +247,10 @@ static const struct spm_reg_data spm_reg_8064_cpu = { 0x10, 0x54, 0x30, 0x0C, 0x24, 0x30, 0x0F }, .start_index[PM_SLEEP_MODE_STBY] = 0, .start_index[PM_SLEEP_MODE_SPC] = 2, + .set_vdd = smp_set_vdd_v1_1, + .range = &spm_v1_1_regulator_range, + .init_uV = 1300000, + .ramp_delay = 1250, }; static inline void spm_register_write(struct spm_driver_data *drv, @@ -258,6 +302,185 @@ void spm_set_low_power_mode(struct spm_driver_data *drv, spm_register_write_sync(drv, SPM_REG_SPM_CTL, ctl_val); } +static int spm_set_voltage_sel(struct regulator_dev *rdev, unsigned int selector) +{ + struct spm_driver_data *drv = rdev_get_drvdata(rdev); + + drv->volt_sel = selector; + + /* Always do the SAW register writes on the corresponding CPU */ + return smp_call_function_single(drv->reg_cpu, drv->reg_data->set_vdd, drv, true); +} + +static int spm_get_voltage_sel(struct regulator_dev *rdev) +{ + struct spm_driver_data *drv = rdev_get_drvdata(rdev); + + return drv->volt_sel; +} + +static const struct regulator_ops spm_reg_ops = { + .set_voltage_sel = spm_set_voltage_sel, + .get_voltage_sel = spm_get_voltage_sel, + .list_voltage = regulator_list_voltage_linear_range, + .set_voltage_time_sel = regulator_set_voltage_time_sel, +}; + +static void smp_set_vdd_v1_1(void *data) +{ + struct spm_driver_data *drv = data; + unsigned int vctl, data0, data1, avs_ctl, sts; + unsigned int vlevel, volt_sel; + bool avs_enabled; + + volt_sel = drv->volt_sel; + vlevel = volt_sel | 0x80; /* band */ + + avs_ctl = spm_register_read(drv, SPM_REG_AVS_CTL); + vctl = spm_register_read(drv, SPM_REG_VCTL); + data0 = spm_register_read(drv, SPM_REG_PMIC_DATA_0); + data1 = spm_register_read(drv, SPM_REG_PMIC_DATA_1); + + avs_enabled = avs_ctl & SPM_1_1_AVS_CTL_AVS_ENABLED; + + /* If AVS is enabled, switch it off during the voltage change */ + if (avs_enabled) { + avs_ctl &= ~SPM_1_1_AVS_CTL_AVS_ENABLED; + spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl); + } + + /* Kick the state machine back to idle */ + spm_register_write(drv, SPM_REG_RST, 1); + + vctl = FIELD_SET(vctl, SPM_VCTL_VLVL, vlevel); + data0 = FIELD_SET(data0, SPM_PMIC_DATA_0_VLVL, vlevel); + data1 = FIELD_SET(data1, SPM_PMIC_DATA_1_MIN_VSEL, volt_sel); + data1 = FIELD_SET(data1, SPM_PMIC_DATA_1_MAX_VSEL, volt_sel); + + spm_register_write(drv, SPM_REG_VCTL, vctl); + spm_register_write(drv, SPM_REG_PMIC_DATA_0, data0); + spm_register_write(drv, SPM_REG_PMIC_DATA_1, data1); + + if (read_poll_timeout_atomic(spm_register_read, + sts, sts == vlevel, + 1, 200, false, + drv, SPM_REG_STS1)) { + dev_err_ratelimited(drv->dev, "timeout setting the voltage (%x %x)!\n", sts, vlevel); + goto enable_avs; + } + + if (avs_enabled) { + unsigned int max_avs = volt_sel; + unsigned int min_avs = max(max_avs, 4U) - 4; + + avs_ctl = FIELD_SET(avs_ctl, SPM_AVS_CTL_MIN_VLVL, min_avs); + avs_ctl = FIELD_SET(avs_ctl, SPM_AVS_CTL_MAX_VLVL, max_avs); + spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl); + } + +enable_avs: + if (avs_enabled) { + avs_ctl |= SPM_1_1_AVS_CTL_AVS_ENABLED; + spm_register_write(drv, SPM_REG_AVS_CTL, avs_ctl); + } +} + +static int spm_get_cpu(struct device *dev) +{ + int cpu; + bool found; + + for_each_possible_cpu(cpu) { + struct device_node *cpu_node, *saw_node; + + cpu_node = of_cpu_device_node_get(cpu); + if (!cpu_node) + continue; + + saw_node = of_parse_phandle(cpu_node, "qcom,saw", 0); + found = (saw_node == dev->of_node); + of_node_put(saw_node); + of_node_put(cpu_node); + + if (found) + return cpu; + } + + /* L2 SPM is not bound to any CPU, voltage setting is not supported */ + + return -EOPNOTSUPP; +} + +#ifdef CONFIG_REGULATOR +static int spm_register_regulator(struct device *dev, struct spm_driver_data *drv) +{ + struct regulator_config config = { + .dev = dev, + .driver_data = drv, + }; + struct regulator_desc *rdesc; + struct regulator_dev *rdev; + int ret; + bool found; + + if (!drv->reg_data->set_vdd) + return 0; + + rdesc = devm_kzalloc(dev, sizeof(*rdesc), GFP_KERNEL); + if (!rdesc) + return -ENOMEM; + + rdesc->name = "spm"; + rdesc->of_match = of_match_ptr("regulator"); + rdesc->type = REGULATOR_VOLTAGE; + rdesc->owner = THIS_MODULE; + rdesc->ops = &spm_reg_ops; + + rdesc->linear_ranges = drv->reg_data->range; + rdesc->n_linear_ranges = 1; + rdesc->n_voltages = rdesc->linear_ranges[rdesc->n_linear_ranges - 1].max_sel + 1; + rdesc->ramp_delay = drv->reg_data->ramp_delay; + + ret = spm_get_cpu(dev); + if (ret < 0) + return ret; + + drv->reg_cpu = ret; + dev_dbg(dev, "SAW2 bound to CPU %d\n", drv->reg_cpu); + + /* + * Program initial voltage, otherwise registration will also try + * setting the voltage, which might result in undervolting the CPU. + */ + drv->volt_sel = DIV_ROUND_UP(drv->reg_data->init_uV - rdesc->min_uV, + rdesc->uV_step); + ret = linear_range_get_selector_high(drv->reg_data->range, + drv->reg_data->init_uV, + &drv->volt_sel, + &found); + if (ret) { + dev_err(dev, "Initial uV value out of bounds\n"); + return ret; + } + + /* Always do the SAW register writes on the corresponding CPU */ + smp_call_function_single(drv->reg_cpu, drv->reg_data->set_vdd, drv, true); + + rdev = devm_regulator_register(dev, rdesc, &config); + if (IS_ERR(rdev)) { + dev_err(dev, "failed to register regulator\n"); + return PTR_ERR(rdev); + } + + return 0; +} +#else +static int spm_register_regulator(struct device *dev, struct spm_driver_data *drv) +{ + return 0; +} +#endif + static const struct of_device_id spm_match_table[] = { { .compatible = "qcom,sdm660-gold-saw2-v4.1-l2", .data = &spm_reg_660_gold_l2 }, @@ -308,6 +531,7 @@ static int spm_dev_probe(struct platform_device *pdev) return -ENODEV; drv->reg_data = match_id->data; + drv->dev = &pdev->dev; platform_set_drvdata(pdev, drv); /* Write the SPM sequences first.. */ @@ -335,7 +559,7 @@ static int spm_dev_probe(struct platform_device *pdev) if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL]) spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); - return 0; + return spm_register_regulator(&pdev->dev, drv); } static struct platform_driver spm_driver = { -- cgit v1.2.3-58-ga151 From 534224b958dfc5c7beca2542e27a094d64d66b1a Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Fri, 19 Jan 2024 15:23:37 +0000 Subject: firmware: arm_scmi: Warn if domain frequency multiplier is 0 or rounded off When (sustained_freq_khz * 1000) is less than sustained_perf_level, the multiplier will be less than 1 and hence rounded down as 0. Similarly if it is not multiple of sustained_perf_level the dom_info->mult_factor will contain rounded down value and will end up impacting all the frequency calculations done using it. Add warning if and when the domain frequency multiplier is 0 or rounded down so that it gives a clue to get the firmware tables fixed. Suggested-by: Pierre Gondois Reviewed-by: Pierre Gondois Reviewed-by: Cristian Marussi Link: https://lore.kernel.org/r/20240119152338.3047620-1-sudeep.holla@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 211e8e0aef2c..9e7b1ee94940 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -270,15 +270,30 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, le32_to_cpu(attr->sustained_freq_khz); dom_info->sustained_perf_level = le32_to_cpu(attr->sustained_perf_level); + /* + * sustained_freq_khz = mult_factor * sustained_perf_level + * mult_factor must be non zero positive integer(not fraction) + */ if (!dom_info->sustained_freq_khz || !dom_info->sustained_perf_level || - dom_info->level_indexing_mode) + dom_info->level_indexing_mode) { /* CPUFreq converts to kHz, hence default 1000 */ dom_info->mult_factor = 1000; - else + } else { dom_info->mult_factor = (dom_info->sustained_freq_khz * 1000UL) / dom_info->sustained_perf_level; + if ((dom_info->sustained_freq_khz * 1000UL) % + dom_info->sustained_perf_level) + dev_warn(ph->dev, + "multiplier for domain %d rounded\n", + dom_info->id); + } + if (!dom_info->mult_factor) + dev_warn(ph->dev, + "Wrong sustained perf/frequency(domain %d)\n", + dom_info->id); + strscpy(dom_info->info.name, attr->name, SCMI_SHORT_NAME_MAX_SIZE); } -- cgit v1.2.3-58-ga151 From dea893a66cd5b0cbd2f215137c32962a9a0cd178 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 10 Jan 2024 12:09:16 +0000 Subject: firmware: arm_scmi: Rework clock domain info lookups Accessing clock domains descriptors by the index from the SCMI drivers can potentially lead to out-of-bound violations if the SCMI drivers misbehaves. Use a common helper to check the consistency of such accesses. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240110120916.2482603-1-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index e2050adbf85c..2e4d6479a639 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -167,6 +167,15 @@ static enum scmi_clock_protocol_cmd evt_2_cmd[] = { CLOCK_RATE_CHANGE_REQUESTED_NOTIFY, }; +static inline struct scmi_clock_info * +scmi_clock_domain_lookup(struct clock_info *ci, u32 clk_id) +{ + if (clk_id >= ci->num_clocks) + return ERR_PTR(-EINVAL); + + return ci->clk + clk_id; +} + static int scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph, struct clock_info *ci) @@ -580,10 +589,9 @@ scmi_clock_set_parent(const struct scmi_protocol_handle *ph, u32 clk_id, struct clock_info *ci = ph->get_priv(ph); struct scmi_clock_info *clk; - if (clk_id >= ci->num_clocks) - return -EINVAL; - - clk = ci->clk + clk_id; + clk = scmi_clock_domain_lookup(ci, clk_id); + if (IS_ERR(clk)) + return PTR_ERR(clk); if (parent_id >= clk->num_parents) return -EINVAL; @@ -800,10 +808,10 @@ scmi_clock_info_get(const struct scmi_protocol_handle *ph, u32 clk_id) struct scmi_clock_info *clk; struct clock_info *ci = ph->get_priv(ph); - if (clk_id >= ci->num_clocks) + clk = scmi_clock_domain_lookup(ci, clk_id); + if (IS_ERR(clk)) return NULL; - clk = ci->clk + clk_id; if (!clk->name[0]) return NULL; -- cgit v1.2.3-58-ga151 From 2858f6e5f06440d6b9e9c3f57bc68915344830a9 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Thu, 21 Dec 2023 15:11:29 +0000 Subject: firmware: arm_scmi: Add multiple protocols registration support Add the capability for a SCMI driver to register to the core SCMI stack with multiple SCMI protocols. In such a case the SCMI driver probe function will end up being called once for each registered protocol which have been also found as implemented on the platform. This is especially useful in testing scenarios. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20231221151129.325749-1-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/bus.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index c15928b8c5cc..1f9735dbebec 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -141,6 +141,17 @@ out: return ret; } +static int scmi_protocol_table_register(const struct scmi_device_id *id_table) +{ + int ret = 0; + const struct scmi_device_id *entry; + + for (entry = id_table; entry->name && ret == 0; entry++) + ret = scmi_protocol_device_request(entry); + + return ret; +} + /** * scmi_protocol_device_unrequest - Helper to unrequest a device * @@ -186,6 +197,15 @@ static void scmi_protocol_device_unrequest(const struct scmi_device_id *id_table mutex_unlock(&scmi_requested_devices_mtx); } +static void +scmi_protocol_table_unregister(const struct scmi_device_id *id_table) +{ + const struct scmi_device_id *entry; + + for (entry = id_table; entry->name; entry++) + scmi_protocol_device_unrequest(entry); +} + static const struct scmi_device_id * scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv) { @@ -279,7 +299,7 @@ int scmi_driver_register(struct scmi_driver *driver, struct module *owner, if (!driver->probe) return -EINVAL; - retval = scmi_protocol_device_request(driver->id_table); + retval = scmi_protocol_table_register(driver->id_table); if (retval) return retval; @@ -299,7 +319,7 @@ EXPORT_SYMBOL_GPL(scmi_driver_register); void scmi_driver_unregister(struct scmi_driver *driver) { driver_unregister(&driver->driver); - scmi_protocol_device_unrequest(driver->id_table); + scmi_protocol_table_unregister(driver->id_table); } EXPORT_SYMBOL_GPL(scmi_driver_unregister); -- cgit v1.2.3-58-ga151 From dc36561e1548a8ca93b34ef385da03c289ec5ac0 Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Sun, 21 Jan 2024 19:09:00 +0800 Subject: firmware: arm_scmi: Implement clock get permissions ARM SCMI v3.2 introduces clock get permission command. To implement the same let us stash the values of those permissions in the scmi_clock_info. They indicate if the operation is forbidden or not. If the CLOCK_GET_PERMISSIONS command is not supported, the default permissions are set to allow the operations, otherwise they will be set according to the response of CLOCK_GET_PERMISSIONS from the SCMI platform firmware. Reviewed-by: Cristian Marussi Signed-off-by: Peng Fan Link: https://lore.kernel.org/r/20240121110901.1414856-1-peng.fan@oss.nxp.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 64 +++++++++++++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 3 ++ 2 files changed, 67 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 2e4d6479a639..959e48aba1b5 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -28,8 +28,13 @@ enum scmi_clock_protocol_cmd { CLOCK_POSSIBLE_PARENTS_GET = 0xC, CLOCK_PARENT_SET = 0xD, CLOCK_PARENT_GET = 0xE, + CLOCK_GET_PERMISSIONS = 0xF, }; +#define CLOCK_STATE_CONTROL_ALLOWED BIT(31) +#define CLOCK_PARENT_CONTROL_ALLOWED BIT(30) +#define CLOCK_RATE_CONTROL_ALLOWED BIT(29) + enum clk_state { CLK_STATE_DISABLE, CLK_STATE_ENABLE, @@ -49,6 +54,7 @@ struct scmi_msg_resp_clock_attributes { #define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x) ((x) & BIT(30)) #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29)) #define SUPPORTS_PARENT_CLOCK(x) ((x) & BIT(28)) +#define SUPPORTS_GET_PERMISSIONS(x) ((x) & BIT(1)) u8 name[SCMI_SHORT_NAME_MAX_SIZE]; __le32 clock_enable_latency; }; @@ -293,6 +299,35 @@ static int scmi_clock_possible_parents(const struct scmi_protocol_handle *ph, u3 return ret; } +static int +scmi_clock_get_permissions(const struct scmi_protocol_handle *ph, u32 clk_id, + struct scmi_clock_info *clk) +{ + struct scmi_xfer *t; + u32 perm; + int ret; + + ret = ph->xops->xfer_get_init(ph, CLOCK_GET_PERMISSIONS, + sizeof(clk_id), sizeof(perm), &t); + if (ret) + return ret; + + put_unaligned_le32(clk_id, t->tx.buf); + + ret = ph->xops->do_xfer(ph, t); + if (!ret) { + perm = get_unaligned_le32(t->rx.buf); + + clk->state_ctrl_forbidden = !(perm & CLOCK_STATE_CONTROL_ALLOWED); + clk->rate_ctrl_forbidden = !(perm & CLOCK_RATE_CONTROL_ALLOWED); + clk->parent_ctrl_forbidden = !(perm & CLOCK_PARENT_CONTROL_ALLOWED); + } + + ph->xops->xfer_put(ph, t); + + return ret; +} + static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, u32 clk_id, struct scmi_clock_info *clk, u32 version) @@ -339,6 +374,8 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, clk->rate_change_requested_notifications = true; if (SUPPORTS_PARENT_CLOCK(attributes)) scmi_clock_possible_parents(ph, clk_id, clk); + if (SUPPORTS_GET_PERMISSIONS(attributes)) + scmi_clock_get_permissions(ph, clk_id, clk); } return ret; @@ -511,6 +548,14 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph, struct scmi_xfer *t; struct scmi_clock_set_rate *cfg; struct clock_info *ci = ph->get_priv(ph); + struct scmi_clock_info *clk; + + clk = scmi_clock_domain_lookup(ci, clk_id); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (clk->rate_ctrl_forbidden) + return -EACCES; ret = ph->xops->xfer_get_init(ph, CLOCK_RATE_SET, sizeof(*cfg), 0, &t); if (ret) @@ -596,6 +641,9 @@ scmi_clock_set_parent(const struct scmi_protocol_handle *ph, u32 clk_id, if (parent_id >= clk->num_parents) return -EINVAL; + if (clk->parent_ctrl_forbidden) + return -EACCES; + ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_SET, sizeof(*cfg), 0, &t); if (ret) @@ -679,6 +727,14 @@ static int scmi_clock_enable(const struct scmi_protocol_handle *ph, u32 clk_id, bool atomic) { struct clock_info *ci = ph->get_priv(ph); + struct scmi_clock_info *clk; + + clk = scmi_clock_domain_lookup(ci, clk_id); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (clk->state_ctrl_forbidden) + return -EACCES; return ci->clock_config_set(ph, clk_id, CLK_STATE_ENABLE, NULL_OEM_TYPE, 0, atomic); @@ -688,6 +744,14 @@ static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id, bool atomic) { struct clock_info *ci = ph->get_priv(ph); + struct scmi_clock_info *clk; + + clk = scmi_clock_domain_lookup(ci, clk_id); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (clk->state_ctrl_forbidden) + return -EACCES; return ci->clock_config_set(ph, clk_id, CLK_STATE_DISABLE, NULL_OEM_TYPE, 0, atomic); diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index f2f05fb42d28..0cc40af5519a 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -47,6 +47,9 @@ struct scmi_clock_info { bool rate_discrete; bool rate_changed_notifications; bool rate_change_requested_notifications; + bool state_ctrl_forbidden; + bool rate_ctrl_forbidden; + bool parent_ctrl_forbidden; union { struct { int num_rates; -- cgit v1.2.3-58-ga151 From f1d71576d2c9ec8fdb822173fa7f3de79475e9bd Mon Sep 17 00:00:00 2001 From: Andre Przywara Date: Fri, 26 Jan 2024 12:23:25 +0000 Subject: firmware: arm_scmi: Fix double free in SMC transport cleanup path When the generic SCMI code tears down a channel, it calls the chan_free callback function, defined by each transport. Since multiple protocols might share the same transport_info member, chan_free() might want to clean up the same member multiple times within the given SCMI transport implementation. In this case, it is SMC transport. This will lead to a NULL pointer dereference at the second time: | scmi_protocol scmi_dev.1: Enabled polling mode TX channel - prot_id:16 | arm-scmi firmware:scmi: SCMI Notifications - Core Enabled. | arm-scmi firmware:scmi: unable to communicate with SCMI | Unable to handle kernel NULL pointer dereference at virtual address 0000000000000000 | Mem abort info: | ESR = 0x0000000096000004 | EC = 0x25: DABT (current EL), IL = 32 bits | SET = 0, FnV = 0 | EA = 0, S1PTW = 0 | FSC = 0x04: level 0 translation fault | Data abort info: | ISV = 0, ISS = 0x00000004, ISS2 = 0x00000000 | CM = 0, WnR = 0, TnD = 0, TagAccess = 0 | GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 | user pgtable: 4k pages, 48-bit VAs, pgdp=0000000881ef8000 | [0000000000000000] pgd=0000000000000000, p4d=0000000000000000 | Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP | Modules linked in: | CPU: 4 PID: 1 Comm: swapper/0 Not tainted 6.7.0-rc2-00124-g455ef3d016c9-dirty #793 | Hardware name: FVP Base RevC (DT) | pstate: 61400009 (nZCv daif +PAN -UAO -TCO +DIT -SSBS BTYPE=--) | pc : smc_chan_free+0x3c/0x6c | lr : smc_chan_free+0x3c/0x6c | Call trace: | smc_chan_free+0x3c/0x6c | idr_for_each+0x68/0xf8 | scmi_cleanup_channels.isra.0+0x2c/0x58 | scmi_probe+0x434/0x734 | platform_probe+0x68/0xd8 | really_probe+0x110/0x27c | __driver_probe_device+0x78/0x12c | driver_probe_device+0x3c/0x118 | __driver_attach+0x74/0x128 | bus_for_each_dev+0x78/0xe0 | driver_attach+0x24/0x30 | bus_add_driver+0xe4/0x1e8 | driver_register+0x60/0x128 | __platform_driver_register+0x28/0x34 | scmi_driver_init+0x84/0xc0 | do_one_initcall+0x78/0x33c | kernel_init_freeable+0x2b8/0x51c | kernel_init+0x24/0x130 | ret_from_fork+0x10/0x20 | Code: f0004701 910a0021 aa1403e5 97b91c70 (b9400280) | ---[ end trace 0000000000000000 ]--- Simply check for the struct pointer being NULL before trying to access its members, to avoid this situation. This was found when a transport doesn't really work (for instance no SMC service), the probe routines then tries to clean up, and triggers a crash. Signed-off-by: Andre Przywara Fixes: 1dc6558062da ("firmware: arm_scmi: Add smc/hvc transport") Reviewed-by: Cristian Marussi Link: https://lore.kernel.org/r/20240126122325.2039669-1-andre.przywara@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/smc.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/smc.c b/drivers/firmware/arm_scmi/smc.c index 7611e9665038..39936e1dd30e 100644 --- a/drivers/firmware/arm_scmi/smc.c +++ b/drivers/firmware/arm_scmi/smc.c @@ -214,6 +214,13 @@ static int smc_chan_free(int id, void *p, void *data) struct scmi_chan_info *cinfo = p; struct scmi_smc *scmi_info = cinfo->transport_info; + /* + * Different protocols might share the same chan info, so a previous + * smc_chan_free call might have already freed the structure. + */ + if (!scmi_info) + return 0; + /* Ignore any possible further reception on the IRQ path */ if (scmi_info->irq > 0) free_irq(scmi_info->irq, scmi_info); -- cgit v1.2.3-58-ga151 From 961745b2c42eb562a6b4433dc5f8fbd974ce4137 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sun, 11 Feb 2024 12:51:30 -0300 Subject: firmware: arm_scmi: Make scmi_bus_type const Now that the driver core can properly handle constant struct bus_type, move the scmi_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Reviewed-by: Cristian Marussi Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240211-bus_cleanup-firmware2-v1-2-1851c92c7be7@marliere.net Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/bus.c | 2 +- drivers/firmware/arm_scmi/common.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/bus.c b/drivers/firmware/arm_scmi/bus.c index 1f9735dbebec..77c78be6e79c 100644 --- a/drivers/firmware/arm_scmi/bus.c +++ b/drivers/firmware/arm_scmi/bus.c @@ -283,7 +283,7 @@ static void scmi_dev_remove(struct device *dev) scmi_drv->remove(scmi_dev); } -struct bus_type scmi_bus_type = { +const struct bus_type scmi_bus_type = { .name = "scmi_protocol", .match = scmi_dev_match, .probe = scmi_dev_probe, diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 00b165d1f502..6affbfdd1dec 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -141,7 +141,7 @@ scmi_revision_area_get(const struct scmi_protocol_handle *ph); void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph, u8 *prot_imp); -extern struct bus_type scmi_bus_type; +extern const struct bus_type scmi_bus_type; #define SCMI_BUS_NOTIFY_DEVICE_REQUEST 0 #define SCMI_BUS_NOTIFY_DEVICE_UNREQUEST 1 -- cgit v1.2.3-58-ga151 From 8733e86a80f5a7abb7b4b6ca3f417b32c3eb68e3 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 12 Feb 2024 12:32:23 +0000 Subject: firmware: arm_scmi: Check for notification support When registering protocol events, use the optional .is_notify_supported callback provided by the protocol to check if that specific notification type is available for that particular resource on the running system, marking it as unsupported otherwise. Then, when a notification enable request is received, return an error if it was previously marked as unsuppported, so avoiding to send a needless notification enable command and check the returned value for failure. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240212123233.1230090-2-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/notify.c | 17 ++++++++++++++++- drivers/firmware/arm_scmi/notify.h | 4 ++++ 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/notify.c b/drivers/firmware/arm_scmi/notify.c index 0efd20cd9d69..27c52531194d 100644 --- a/drivers/firmware/arm_scmi/notify.c +++ b/drivers/firmware/arm_scmi/notify.c @@ -99,6 +99,7 @@ #define PROTO_ID_MASK GENMASK(31, 24) #define EVT_ID_MASK GENMASK(23, 16) #define SRC_ID_MASK GENMASK(15, 0) +#define NOTIF_UNSUPP -1 /* * Builds an unsigned 32bit key from the given input tuple to be used @@ -788,6 +789,7 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id, pd->ph = ph; for (i = 0; i < ee->num_events; i++, evt++) { + int id; struct scmi_registered_event *r_evt; r_evt = devm_kzalloc(ni->handle->dev, sizeof(*r_evt), @@ -809,6 +811,11 @@ int scmi_register_protocol_events(const struct scmi_handle *handle, u8 proto_id, if (!r_evt->report) return -ENOMEM; + for (id = 0; id < r_evt->num_sources; id++) + if (ee->ops->is_notify_supported && + !ee->ops->is_notify_supported(ph, r_evt->evt->id, id)) + refcount_set(&r_evt->sources[id], NOTIF_UNSUPP); + pd->registered_events[i] = r_evt; /* Ensure events are updated */ smp_wmb(); @@ -1166,7 +1173,13 @@ static inline int __scmi_enable_evt(struct scmi_registered_event *r_evt, int ret = 0; sid = &r_evt->sources[src_id]; - if (refcount_read(sid) == 0) { + if (refcount_read(sid) == NOTIF_UNSUPP) { + dev_dbg(r_evt->proto->ph->dev, + "Notification NOT supported - proto_id:%d evt_id:%d src_id:%d", + r_evt->proto->id, r_evt->evt->id, + src_id); + ret = -EOPNOTSUPP; + } else if (refcount_read(sid) == 0) { ret = REVT_NOTIFY_ENABLE(r_evt, r_evt->evt->id, src_id); if (!ret) @@ -1179,6 +1192,8 @@ static inline int __scmi_enable_evt(struct scmi_registered_event *r_evt, } else { for (; num_sources; src_id++, num_sources--) { sid = &r_evt->sources[src_id]; + if (refcount_read(sid) == NOTIF_UNSUPP) + continue; if (refcount_dec_and_test(sid)) REVT_NOTIFY_DISABLE(r_evt, r_evt->evt->id, src_id); diff --git a/drivers/firmware/arm_scmi/notify.h b/drivers/firmware/arm_scmi/notify.h index 4e9b627edfef..76758a736cf4 100644 --- a/drivers/firmware/arm_scmi/notify.h +++ b/drivers/firmware/arm_scmi/notify.h @@ -35,6 +35,8 @@ struct scmi_protocol_handle; /** * struct scmi_event_ops - Protocol helpers called by the notification core. + * @is_notify_supported: Return 0 if the specified notification for the + * specified resource (src_id) is supported. * @get_num_sources: Returns the number of possible events' sources for this * protocol * @set_notify_enabled: Enable/disable the required evt_id/src_id notifications @@ -50,6 +52,8 @@ struct scmi_protocol_handle; * process context. */ struct scmi_event_ops { + bool (*is_notify_supported)(const struct scmi_protocol_handle *ph, + u8 evt_id, u32 src_id); int (*get_num_sources)(const struct scmi_protocol_handle *ph); int (*set_notify_enabled)(const struct scmi_protocol_handle *ph, u8 evt_id, u32 src_id, bool enabled); -- cgit v1.2.3-58-ga151 From 637b6d6cae9c42db5a9525da67c991294924d2cd Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 12 Feb 2024 12:32:24 +0000 Subject: firmware: arm_scmi: Add a common helper to check if a message is supported A common helper is provided to check if a specific protocol message is supported or not. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240212123233.1230090-3-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 34 ++++++++++++++++++++++++++++++++++ drivers/firmware/arm_scmi/protocols.h | 4 ++++ 2 files changed, 38 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 3ea64b22cf0d..4a64ad5c21ee 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -1754,10 +1754,44 @@ static void scmi_common_fastchannel_db_ring(struct scmi_fc_db_info *db) #endif } +/** + * scmi_protocol_msg_check - Check protocol message attributes + * + * @ph: A reference to the protocol handle. + * @message_id: The ID of the message to check. + * @attributes: A parameter to optionally return the retrieved message + * attributes, in case of Success. + * + * An helper to check protocol message attributes for a specific protocol + * and message pair. + * + * Return: 0 on SUCCESS + */ +static int scmi_protocol_msg_check(const struct scmi_protocol_handle *ph, + u32 message_id, u32 *attributes) +{ + int ret; + struct scmi_xfer *t; + + ret = xfer_get_init(ph, PROTOCOL_MESSAGE_ATTRIBUTES, + sizeof(__le32), 0, &t); + if (ret) + return ret; + + put_unaligned_le32(message_id, t->tx.buf); + ret = do_xfer(ph, t); + if (!ret && attributes) + *attributes = get_unaligned_le32(t->rx.buf); + xfer_put(ph, t); + + return ret; +} + static const struct scmi_proto_helpers_ops helpers_ops = { .extended_name_get = scmi_common_extended_name_get, .iter_response_init = scmi_iterator_init, .iter_response_run = scmi_iterator_run, + .protocol_msg_check = scmi_protocol_msg_check, .fastchannel_init = scmi_common_fastchannel_init, .fastchannel_db_ring = scmi_common_fastchannel_db_ring, }; diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h index e683c26f24eb..26a3edd49fea 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -251,6 +251,8 @@ struct scmi_fc_info { * provided in @ops. * @iter_response_run: A common helper to trigger the run of a previously * initialized iterator. + * @protocol_msg_check: A common helper to check is a specific protocol message + * is supported. * @fastchannel_init: A common helper used to initialize FC descriptors by * gathering FC descriptions from the SCMI platform server. * @fastchannel_db_ring: A common helper to ring a FC doorbell. @@ -264,6 +266,8 @@ struct scmi_proto_helpers_ops { unsigned int max_resources, u8 msg_id, size_t tx_size, void *priv); int (*iter_response_run)(void *iter); + int (*protocol_msg_check)(const struct scmi_protocol_handle *ph, + u32 message_id, u32 *attributes); void (*fastchannel_init)(const struct scmi_protocol_handle *ph, u8 describe_id, u32 message_id, u32 valid_size, u32 domain, -- cgit v1.2.3-58-ga151 From 120d26312abce67d034977e6ce032a7053309b14 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 12 Feb 2024 12:32:25 +0000 Subject: firmware: arm_scmi: Implement is_notify_supported callback in perf protocol Add a preliminary check to verify if the performance related notify enable commands are supported at all by the SCMI platform, and then provide the callback needed to allow the core SCMI notification subsytem to do a fine-grain check if a specific resource domain supports notifications. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240212123233.1230090-4-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 45 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 9e7b1ee94940..2627ace5b07f 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -182,6 +182,8 @@ struct scmi_perf_info { enum scmi_power_scale power_scale; u64 stats_addr; u32 stats_size; + bool notify_lvl_cmd; + bool notify_lim_cmd; struct perf_dom_info *dom_info; }; @@ -222,6 +224,15 @@ static int scmi_perf_attributes_get(const struct scmi_protocol_handle *ph, } ph->xops->xfer_put(ph, t); + + if (!ret) { + if (!ph->hops->protocol_msg_check(ph, PERF_NOTIFY_LEVEL, NULL)) + pi->notify_lvl_cmd = true; + + if (!ph->hops->protocol_msg_check(ph, PERF_NOTIFY_LIMITS, NULL)) + pi->notify_lim_cmd = true; + } + return ret; } @@ -239,6 +250,7 @@ static void scmi_perf_xa_destroy(void *data) static int scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, struct perf_dom_info *dom_info, + bool notify_lim_cmd, bool notify_lvl_cmd, u32 version) { int ret; @@ -260,8 +272,12 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, dom_info->set_limits = SUPPORTS_SET_LIMITS(flags); dom_info->info.set_perf = SUPPORTS_SET_PERF_LVL(flags); - dom_info->perf_limit_notify = SUPPORTS_PERF_LIMIT_NOTIFY(flags); - dom_info->perf_level_notify = SUPPORTS_PERF_LEVEL_NOTIFY(flags); + if (notify_lim_cmd) + dom_info->perf_limit_notify = + SUPPORTS_PERF_LIMIT_NOTIFY(flags); + if (notify_lvl_cmd) + dom_info->perf_level_notify = + SUPPORTS_PERF_LEVEL_NOTIFY(flags); dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags); if (PROTOCOL_REV_MAJOR(version) >= 0x4) dom_info->level_indexing_mode = @@ -993,6 +1009,27 @@ static const struct scmi_perf_proto_ops perf_proto_ops = { .power_scale_get = scmi_power_scale_get, }; +static bool scmi_perf_notify_supported(const struct scmi_protocol_handle *ph, + u8 evt_id, u32 src_id) +{ + bool supported; + struct perf_dom_info *dom; + + if (evt_id >= ARRAY_SIZE(evt_2_cmd)) + return false; + + dom = scmi_perf_domain_lookup(ph, src_id); + if (IS_ERR(dom)) + return false; + + if (evt_id == SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED) + supported = dom->perf_limit_notify; + else + supported = dom->perf_level_notify; + + return supported; +} + static int scmi_perf_set_notify_enabled(const struct scmi_protocol_handle *ph, u8 evt_id, u32 src_id, bool enable) { @@ -1082,6 +1119,7 @@ static const struct scmi_event perf_events[] = { }; static const struct scmi_event_ops perf_event_ops = { + .is_notify_supported = scmi_perf_notify_supported, .get_num_sources = scmi_perf_get_num_sources, .set_notify_enabled = scmi_perf_set_notify_enabled, .fill_custom_report = scmi_perf_fill_custom_report, @@ -1126,7 +1164,8 @@ static int scmi_perf_protocol_init(const struct scmi_protocol_handle *ph) struct perf_dom_info *dom = pinfo->dom_info + domain; dom->id = domain; - scmi_perf_domain_attributes_get(ph, dom, version); + scmi_perf_domain_attributes_get(ph, dom, pinfo->notify_lim_cmd, + pinfo->notify_lvl_cmd, version); scmi_perf_describe_levels_get(ph, dom, version); if (dom->perf_fastchannels) -- cgit v1.2.3-58-ga151 From b7e400bc2e2dd9adf5996f8a496d3badb3ecb09e Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 12 Feb 2024 12:32:26 +0000 Subject: firmware: arm_scmi: Implement is_notify_supported callback in power protocol Add a preliminary check to verify if the power related notify enable commands are supported at all by the SCMI platform, and then provide the callback needed to allow the core SCMI notification subsytem to do a fine-grain check if a specific resource domain supports notifications. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240212123233.1230090-5-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/power.c | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/power.c b/drivers/firmware/arm_scmi/power.c index c2e6b9b4d941..49666bd1d8ac 100644 --- a/drivers/firmware/arm_scmi/power.c +++ b/drivers/firmware/arm_scmi/power.c @@ -68,6 +68,7 @@ struct power_dom_info { struct scmi_power_info { u32 version; + bool notify_state_change_cmd; int num_domains; u64 stats_addr; u32 stats_size; @@ -97,13 +98,18 @@ static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph, } ph->xops->xfer_put(ph, t); + + if (!ret) + if (!ph->hops->protocol_msg_check(ph, POWER_STATE_NOTIFY, NULL)) + pi->notify_state_change_cmd = true; + return ret; } static int scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph, u32 domain, struct power_dom_info *dom_info, - u32 version) + u32 version, bool notify_state_change_cmd) { int ret; u32 flags; @@ -122,7 +128,9 @@ scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph, if (!ret) { flags = le32_to_cpu(attr->flags); - dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags); + if (notify_state_change_cmd) + dom_info->state_set_notify = + SUPPORTS_STATE_SET_NOTIFY(flags); dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags); dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags); strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE); @@ -231,6 +239,20 @@ static int scmi_power_request_notify(const struct scmi_protocol_handle *ph, return ret; } +static bool scmi_power_notify_supported(const struct scmi_protocol_handle *ph, + u8 evt_id, u32 src_id) +{ + struct power_dom_info *dom; + struct scmi_power_info *pinfo = ph->get_priv(ph); + + if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED || + src_id >= pinfo->num_domains) + return false; + + dom = pinfo->dom_info + src_id; + return dom->state_set_notify; +} + static int scmi_power_set_notify_enabled(const struct scmi_protocol_handle *ph, u8 evt_id, u32 src_id, bool enable) { @@ -285,6 +307,7 @@ static const struct scmi_event power_events[] = { }; static const struct scmi_event_ops power_event_ops = { + .is_notify_supported = scmi_power_notify_supported, .get_num_sources = scmi_power_get_num_sources, .set_notify_enabled = scmi_power_set_notify_enabled, .fill_custom_report = scmi_power_fill_custom_report, @@ -326,7 +349,8 @@ static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph) for (domain = 0; domain < pinfo->num_domains; domain++) { struct power_dom_info *dom = pinfo->dom_info + domain; - scmi_power_domain_attributes_get(ph, domain, dom, version); + scmi_power_domain_attributes_get(ph, domain, dom, version, + pinfo->notify_state_change_cmd); } pinfo->version = version; -- cgit v1.2.3-58-ga151 From 9f5ddbc22225565df5fa1b918a02f4e9e5f7bf0a Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 12 Feb 2024 12:32:27 +0000 Subject: firmware: arm_scmi: Implement is_notify_supported callback in system power protocol Add a preliminary check to verify if the system power protocol related notify enable commands are supported at all by the SCMI platform, and then provide the callback needed to allow the core SCMI notification subsytem to do a fine-grain check if a specific resource domain supports notifications. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240212123233.1230090-6-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/system.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/system.c b/drivers/firmware/arm_scmi/system.c index 1621da97bcbb..b6358c155f7f 100644 --- a/drivers/firmware/arm_scmi/system.c +++ b/drivers/firmware/arm_scmi/system.c @@ -36,8 +36,20 @@ struct scmi_system_power_state_notifier_payld { struct scmi_system_info { u32 version; bool graceful_timeout_supported; + bool power_state_notify_cmd; }; +static bool scmi_system_notify_supported(const struct scmi_protocol_handle *ph, + u8 evt_id, u32 src_id) +{ + struct scmi_system_info *pinfo = ph->get_priv(ph); + + if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER) + return false; + + return pinfo->power_state_notify_cmd; +} + static int scmi_system_request_notify(const struct scmi_protocol_handle *ph, bool enable) { @@ -114,6 +126,7 @@ static const struct scmi_event system_events[] = { }; static const struct scmi_event_ops system_event_ops = { + .is_notify_supported = scmi_system_notify_supported, .set_notify_enabled = scmi_system_set_notify_enabled, .fill_custom_report = scmi_system_fill_custom_report, }; @@ -147,6 +160,9 @@ static int scmi_system_protocol_init(const struct scmi_protocol_handle *ph) if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2) pinfo->graceful_timeout_supported = true; + if (!ph->hops->protocol_msg_check(ph, SYSTEM_POWER_STATE_NOTIFY, NULL)) + pinfo->power_state_notify_cmd = true; + return ph->set_priv(ph, pinfo, version); } -- cgit v1.2.3-58-ga151 From 989e8661dc45babf43070d519011dfc1e33c8875 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Sun, 11 Feb 2024 12:51:29 -0300 Subject: firmware: arm_ffa: Make ffa_bus_type const Now that the driver core can properly handle constant struct bus_type, move the ffa_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Reviewed-by: Cristian Marussi Reviewed-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20240211-bus_cleanup-firmware2-v1-1-1851c92c7be7@marliere.net Signed-off-by: Sudeep Holla --- drivers/firmware/arm_ffa/bus.c | 2 +- include/linux/arm_ffa.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_ffa/bus.c b/drivers/firmware/arm_ffa/bus.c index 1c7940ba5539..2f557e90f2eb 100644 --- a/drivers/firmware/arm_ffa/bus.c +++ b/drivers/firmware/arm_ffa/bus.c @@ -105,7 +105,7 @@ static struct attribute *ffa_device_attributes_attrs[] = { }; ATTRIBUTE_GROUPS(ffa_device_attributes); -struct bus_type ffa_bus_type = { +const struct bus_type ffa_bus_type = { .name = "arm_ffa", .match = ffa_device_match, .probe = ffa_device_probe, diff --git a/include/linux/arm_ffa.h b/include/linux/arm_ffa.h index 3d0fde57ba90..c906f666ff5d 100644 --- a/include/linux/arm_ffa.h +++ b/include/linux/arm_ffa.h @@ -209,7 +209,7 @@ bool ffa_device_is_valid(struct ffa_device *ffa_dev) { return false; } #define module_ffa_driver(__ffa_driver) \ module_driver(__ffa_driver, ffa_register, ffa_unregister) -extern struct bus_type ffa_bus_type; +extern const struct bus_type ffa_bus_type; /* FFA transport related */ struct ffa_partition_info { -- cgit v1.2.3-58-ga151 From cf1bba2775ecb459d8209a65eacb464df9490e30 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 12 Feb 2024 12:32:28 +0000 Subject: firmware: arm_scmi: Implement is_notify_supported callback in clock protocol Add a preliminary check to verify if the clock protocol related notify enable commands are supported at all by the SCMI platform, and then provide the callback needed to allow the core SCMI notification subsytem to do a fine-grain check if a specific resource domain supports notifications. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240212123233.1230090-7-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 47 +++++++++++++++++++++++++++++++++++---- 1 file changed, 43 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 959e48aba1b5..85eda5db40ba 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -158,6 +158,8 @@ struct clock_info { u32 version; int num_clocks; int max_async_req; + bool notify_rate_changed_cmd; + bool notify_rate_change_requested_cmd; atomic_t cur_async_req; struct scmi_clock_info *clk; int (*clock_config_set)(const struct scmi_protocol_handle *ph, @@ -204,6 +206,17 @@ scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph, } ph->xops->xfer_put(ph, t); + + if (!ret) { + if (!ph->hops->protocol_msg_check(ph, CLOCK_RATE_NOTIFY, NULL)) + ci->notify_rate_changed_cmd = true; + + if (!ph->hops->protocol_msg_check(ph, + CLOCK_RATE_CHANGE_REQUESTED_NOTIFY, + NULL)) + ci->notify_rate_change_requested_cmd = true; + } + return ret; } @@ -329,13 +342,14 @@ scmi_clock_get_permissions(const struct scmi_protocol_handle *ph, u32 clk_id, } static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, - u32 clk_id, struct scmi_clock_info *clk, + u32 clk_id, struct clock_info *cinfo, u32 version) { int ret; u32 attributes; struct scmi_xfer *t; struct scmi_msg_resp_clock_attributes *attr; + struct scmi_clock_info *clk = cinfo->clk + clk_id; ret = ph->xops->xfer_get_init(ph, CLOCK_ATTRIBUTES, sizeof(clk_id), sizeof(*attr), &t); @@ -368,9 +382,11 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, NULL, clk->name, SCMI_MAX_STR_SIZE); - if (SUPPORTS_RATE_CHANGED_NOTIF(attributes)) + if (cinfo->notify_rate_changed_cmd && + SUPPORTS_RATE_CHANGED_NOTIF(attributes)) clk->rate_changed_notifications = true; - if (SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes)) + if (cinfo->notify_rate_change_requested_cmd && + SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes)) clk->rate_change_requested_notifications = true; if (SUPPORTS_PARENT_CLOCK(attributes)) scmi_clock_possible_parents(ph, clk_id, clk); @@ -896,6 +912,28 @@ static const struct scmi_clk_proto_ops clk_proto_ops = { .parent_get = scmi_clock_get_parent, }; +static bool scmi_clk_notify_supported(const struct scmi_protocol_handle *ph, + u8 evt_id, u32 src_id) +{ + bool supported; + struct scmi_clock_info *clk; + struct clock_info *ci = ph->get_priv(ph); + + if (evt_id >= ARRAY_SIZE(evt_2_cmd)) + return false; + + clk = scmi_clock_domain_lookup(ci, src_id); + if (IS_ERR(clk)) + return false; + + if (evt_id == SCMI_EVENT_CLOCK_RATE_CHANGED) + supported = clk->rate_changed_notifications; + else + supported = clk->rate_change_requested_notifications; + + return supported; +} + static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph, u32 clk_id, int message_id, bool enable) { @@ -980,6 +1018,7 @@ static const struct scmi_event clk_events[] = { }; static const struct scmi_event_ops clk_event_ops = { + .is_notify_supported = scmi_clk_notify_supported, .get_num_sources = scmi_clk_get_num_sources, .set_notify_enabled = scmi_clk_set_notify_enabled, .fill_custom_report = scmi_clk_fill_custom_report, @@ -1021,7 +1060,7 @@ static int scmi_clock_protocol_init(const struct scmi_protocol_handle *ph) for (clkid = 0; clkid < cinfo->num_clocks; clkid++) { struct scmi_clock_info *clk = cinfo->clk + clkid; - ret = scmi_clock_attributes_get(ph, clkid, clk, version); + ret = scmi_clock_attributes_get(ph, clkid, cinfo, version); if (!ret) scmi_clock_describe_rates_get(ph, clkid, clk); } -- cgit v1.2.3-58-ga151 From 7ac7932df2477ee95f8902d62611134fe7c8a133 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 12 Feb 2024 12:32:29 +0000 Subject: firmware: arm_scmi: Implement is_notify_supported callback in sensor protocol Add a preliminary check to verify if the sensor protocol related notify enable commands are supported at all by the SCMI platform, and then provide the callback needed to allow the core SCMI notification subsytem to do a fine-grain check if a specific resource domain supports notifications. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240212123233.1230090-8-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/sensors.c | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index 311149965370..7fc5535ca34c 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -215,6 +215,8 @@ struct scmi_sensor_update_notify_payld { struct sensors_info { u32 version; + bool notify_trip_point_cmd; + bool notify_continuos_update_cmd; int num_sensors; int max_requests; u64 reg_addr; @@ -246,6 +248,18 @@ static int scmi_sensor_attributes_get(const struct scmi_protocol_handle *ph, } ph->xops->xfer_put(ph, t); + + if (!ret) { + if (!ph->hops->protocol_msg_check(ph, + SENSOR_TRIP_POINT_NOTIFY, NULL)) + si->notify_trip_point_cmd = true; + + if (!ph->hops->protocol_msg_check(ph, + SENSOR_CONTINUOUS_UPDATE_NOTIFY, + NULL)) + si->notify_continuos_update_cmd = true; + } + return ret; } @@ -594,7 +608,8 @@ iter_sens_descr_process_response(const struct scmi_protocol_handle *ph, * Such bitfields are assumed to be zeroed on non * relevant fw versions...assuming fw not buggy ! */ - s->update = SUPPORTS_UPDATE_NOTIFY(attrl); + if (si->notify_continuos_update_cmd) + s->update = SUPPORTS_UPDATE_NOTIFY(attrl); s->timestamped = SUPPORTS_TIMESTAMP(attrl); if (s->timestamped) s->tstamp_scale = S32_EXT(SENSOR_TSTAMP_EXP(attrl)); @@ -988,6 +1003,25 @@ static const struct scmi_sensor_proto_ops sensor_proto_ops = { .config_set = scmi_sensor_config_set, }; +static bool scmi_sensor_notify_supported(const struct scmi_protocol_handle *ph, + u8 evt_id, u32 src_id) +{ + bool supported = false; + const struct scmi_sensor_info *s; + struct sensors_info *sinfo = ph->get_priv(ph); + + s = scmi_sensor_info_get(ph, src_id); + if (!s) + return false; + + if (evt_id == SCMI_EVENT_SENSOR_TRIP_POINT_EVENT) + supported = sinfo->notify_trip_point_cmd; + else if (evt_id == SCMI_EVENT_SENSOR_UPDATE) + supported = s->update; + + return supported; +} + static int scmi_sensor_set_notify_enabled(const struct scmi_protocol_handle *ph, u8 evt_id, u32 src_id, bool enable) { @@ -1099,6 +1133,7 @@ static const struct scmi_event sensor_events[] = { }; static const struct scmi_event_ops sensor_event_ops = { + .is_notify_supported = scmi_sensor_notify_supported, .get_num_sources = scmi_sensor_get_num_sources, .set_notify_enabled = scmi_sensor_set_notify_enabled, .fill_custom_report = scmi_sensor_fill_custom_report, -- cgit v1.2.3-58-ga151 From 12d6a03f3224f7acd37466046f61ea19d8aae1a4 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 12 Feb 2024 12:32:30 +0000 Subject: firmware: arm_scmi: Implement is_notify_supported callback in reset protocol Add a preliminary check to verify if the reset protocol related notify enable commands are supported at all by the SCMI platform, and then provide the callback needed to allow the core SCMI notification subsytem to do a fine-grain check if a specific resource domain supports notifications. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240212123233.1230090-9-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/reset.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/reset.c b/drivers/firmware/arm_scmi/reset.c index 19970d9f9e36..1b318316535e 100644 --- a/drivers/firmware/arm_scmi/reset.c +++ b/drivers/firmware/arm_scmi/reset.c @@ -67,6 +67,7 @@ struct reset_dom_info { struct scmi_reset_info { u32 version; int num_domains; + bool notify_reset_cmd; struct reset_dom_info *dom_info; }; @@ -89,18 +90,24 @@ static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph, } ph->xops->xfer_put(ph, t); + + if (!ret) + if (!ph->hops->protocol_msg_check(ph, RESET_NOTIFY, NULL)) + pi->notify_reset_cmd = true; + return ret; } static int scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph, - u32 domain, struct reset_dom_info *dom_info, - u32 version) + struct scmi_reset_info *pinfo, + u32 domain, u32 version) { int ret; u32 attributes; struct scmi_xfer *t; struct scmi_msg_resp_reset_domain_attributes *attr; + struct reset_dom_info *dom_info = pinfo->dom_info + domain; ret = ph->xops->xfer_get_init(ph, RESET_DOMAIN_ATTRIBUTES, sizeof(domain), sizeof(*attr), &t); @@ -115,7 +122,9 @@ scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph, attributes = le32_to_cpu(attr->attributes); dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes); - dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes); + if (pinfo->notify_reset_cmd) + dom_info->reset_notify = + SUPPORTS_NOTIFY_RESET(attributes); dom_info->latency_us = le32_to_cpu(attr->latency); if (dom_info->latency_us == U32_MAX) dom_info->latency_us = 0; @@ -226,6 +235,20 @@ static const struct scmi_reset_proto_ops reset_proto_ops = { .deassert = scmi_reset_domain_deassert, }; +static bool scmi_reset_notify_supported(const struct scmi_protocol_handle *ph, + u8 evt_id, u32 src_id) +{ + struct reset_dom_info *dom; + struct scmi_reset_info *pi = ph->get_priv(ph); + + if (evt_id != SCMI_EVENT_RESET_ISSUED || src_id >= pi->num_domains) + return false; + + dom = pi->dom_info + src_id; + + return dom->reset_notify; +} + static int scmi_reset_notify(const struct scmi_protocol_handle *ph, u32 domain_id, bool enable) { @@ -301,6 +324,7 @@ static const struct scmi_event reset_events[] = { }; static const struct scmi_event_ops reset_event_ops = { + .is_notify_supported = scmi_reset_notify_supported, .get_num_sources = scmi_reset_get_num_sources, .set_notify_enabled = scmi_reset_set_notify_enabled, .fill_custom_report = scmi_reset_fill_custom_report, @@ -339,11 +363,8 @@ static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph) if (!pinfo->dom_info) return -ENOMEM; - for (domain = 0; domain < pinfo->num_domains; domain++) { - struct reset_dom_info *dom = pinfo->dom_info + domain; - - scmi_reset_domain_attributes_get(ph, domain, dom, version); - } + for (domain = 0; domain < pinfo->num_domains; domain++) + scmi_reset_domain_attributes_get(ph, pinfo, domain, version); pinfo->version = version; return ph->set_priv(ph, pinfo, version); -- cgit v1.2.3-58-ga151 From e85beaf760807577e3fe2b825b4f45ec804b269d Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 12 Feb 2024 12:32:31 +0000 Subject: firmware: arm_scmi: Implement is_notify_supported callback in powercap protocol Add a preliminary check to verify if the powercap protocol related notify enable commands are supported at all by the SCMI platform, and then provide the callback needed to allow the core SCMI notification subsytem to do a fine-grain check if a specific resource domain supports notifications. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240212123233.1230090-10-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/powercap.c | 45 ++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/powercap.c b/drivers/firmware/arm_scmi/powercap.c index a4c6cd4716fe..2fab92367e42 100644 --- a/drivers/firmware/arm_scmi/powercap.c +++ b/drivers/firmware/arm_scmi/powercap.c @@ -124,6 +124,8 @@ struct scmi_powercap_state { struct powercap_info { u32 version; int num_domains; + bool notify_cap_cmd; + bool notify_measurements_cmd; struct scmi_powercap_state *states; struct scmi_powercap_info *powercaps; }; @@ -157,6 +159,18 @@ scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph, } ph->xops->xfer_put(ph, t); + + if (!ret) { + if (!ph->hops->protocol_msg_check(ph, + POWERCAP_CAP_NOTIFY, NULL)) + pi->notify_cap_cmd = true; + + if (!ph->hops->protocol_msg_check(ph, + POWERCAP_MEASUREMENTS_NOTIFY, + NULL)) + pi->notify_measurements_cmd = true; + } + return ret; } @@ -200,10 +214,12 @@ scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph, flags = le32_to_cpu(resp->attributes); dom_info->id = domain; - dom_info->notify_powercap_cap_change = - SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags); - dom_info->notify_powercap_measurement_change = - SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags); + if (pinfo->notify_cap_cmd) + dom_info->notify_powercap_cap_change = + SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags); + if (pinfo->notify_measurements_cmd) + dom_info->notify_powercap_measurement_change = + SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags); dom_info->async_powercap_cap_set = SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags); dom_info->powercap_cap_config = @@ -788,6 +804,26 @@ static int scmi_powercap_notify(const struct scmi_protocol_handle *ph, return ret; } +static bool +scmi_powercap_notify_supported(const struct scmi_protocol_handle *ph, + u8 evt_id, u32 src_id) +{ + bool supported = false; + const struct scmi_powercap_info *dom_info; + struct powercap_info *pi = ph->get_priv(ph); + + if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains) + return false; + + dom_info = pi->powercaps + src_id; + if (evt_id == SCMI_EVENT_POWERCAP_CAP_CHANGED) + supported = dom_info->notify_powercap_cap_change; + else if (evt_id == SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED) + supported = dom_info->notify_powercap_measurement_change; + + return supported; +} + static int scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph, u8 evt_id, u32 src_id, bool enable) @@ -904,6 +940,7 @@ static const struct scmi_event powercap_events[] = { }; static const struct scmi_event_ops powercap_event_ops = { + .is_notify_supported = scmi_powercap_notify_supported, .get_num_sources = scmi_powercap_get_num_sources, .set_notify_enabled = scmi_powercap_set_notify_enabled, .fill_custom_report = scmi_powercap_fill_custom_report, -- cgit v1.2.3-58-ga151 From 23443a3c7c0c285cbeb5f95c20b630617d8d80e0 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 12 Feb 2024 12:32:32 +0000 Subject: firmware: arm_scmi: Use opps_by_lvl to store opps Store all the discovered OPPs into the XArray opps_by_lvl even when level_indexing mode is not used, since it comes handy to easily retrieve OPPs by level. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240212123233.1230090-11-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 2627ace5b07f..e20d137a92f6 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -326,9 +326,9 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph, dom_info->id, NULL, dom_info->info.name, SCMI_MAX_STR_SIZE); + xa_init(&dom_info->opps_by_lvl); if (dom_info->level_indexing_mode) { xa_init(&dom_info->opps_by_idx); - xa_init(&dom_info->opps_by_lvl); hash_init(dom_info->opps_by_freq); } @@ -371,13 +371,21 @@ static int iter_perf_levels_update_state(struct scmi_iterator_state *st, } static inline void -process_response_opp(struct scmi_opp *opp, unsigned int loop_idx, +process_response_opp(struct device *dev, struct perf_dom_info *dom, + struct scmi_opp *opp, unsigned int loop_idx, const struct scmi_msg_resp_perf_describe_levels *r) { + int ret; + opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val); opp->power = le32_to_cpu(r->opp[loop_idx].power); opp->trans_latency_us = le16_to_cpu(r->opp[loop_idx].transition_latency_us); + + ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL); + if (ret) + dev_warn(dev, "Failed to add opps_by_lvl at %d - ret:%d\n", + opp->perf, ret); } static inline void @@ -385,16 +393,21 @@ process_response_opp_v4(struct device *dev, struct perf_dom_info *dom, struct scmi_opp *opp, unsigned int loop_idx, const struct scmi_msg_resp_perf_describe_levels_v4 *r) { + int ret; + opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val); opp->power = le32_to_cpu(r->opp[loop_idx].power); opp->trans_latency_us = le16_to_cpu(r->opp[loop_idx].transition_latency_us); + ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL); + if (ret) + dev_warn(dev, "Failed to add opps_by_lvl at %d - ret:%d\n", + opp->perf, ret); + /* Note that PERF v4 reports always five 32-bit words */ opp->indicative_freq = le32_to_cpu(r->opp[loop_idx].indicative_freq); if (dom->level_indexing_mode) { - int ret; - opp->level_index = le32_to_cpu(r->opp[loop_idx].level_index); ret = xa_insert(&dom->opps_by_idx, opp->level_index, opp, @@ -404,12 +417,6 @@ process_response_opp_v4(struct device *dev, struct perf_dom_info *dom, "Failed to add opps_by_idx at %d - ret:%d\n", opp->level_index, ret); - ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL); - if (ret) - dev_warn(dev, - "Failed to add opps_by_lvl at %d - ret:%d\n", - opp->perf, ret); - hash_add(dom->opps_by_freq, &opp->hash, opp->indicative_freq); } } @@ -424,7 +431,8 @@ iter_perf_levels_process_response(const struct scmi_protocol_handle *ph, opp = &p->perf_dom->opp[st->desc_index + st->loop_idx]; if (PROTOCOL_REV_MAJOR(p->version) <= 0x3) - process_response_opp(opp, st->loop_idx, response); + process_response_opp(ph->dev, p->perf_dom, opp, st->loop_idx, + response); else process_response_opp_v4(ph->dev, p->perf_dom, opp, st->loop_idx, response); -- cgit v1.2.3-58-ga151 From 22ffc748a6475e75e058ecb16c5afdd6b9f1885f Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Mon, 12 Feb 2024 12:32:33 +0000 Subject: firmware: arm_scmi: Report frequencies in the perf notifications Extend the perf notification report to include pre-calculated frequencies corresponding to the reported limits/levels event; such frequencies are properly computed based on the stored known OPPs information taking into consideration if the current operating mode is level indexed or not. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240212123233.1230090-12-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/perf.c | 69 ++++++++++++++++++++++++++++++++++++++++ include/linux/scmi_protocol.h | 3 ++ 2 files changed, 72 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index e20d137a92f6..981e327e63e3 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -1055,18 +1055,47 @@ static int scmi_perf_set_notify_enabled(const struct scmi_protocol_handle *ph, return ret; } +static int +scmi_perf_xlate_opp_to_freq(struct perf_dom_info *dom, + unsigned int index, unsigned long *freq) +{ + struct scmi_opp *opp; + + if (!dom || !freq) + return -EINVAL; + + if (!dom->level_indexing_mode) { + opp = xa_load(&dom->opps_by_lvl, index); + if (!opp) + return -ENODEV; + + *freq = opp->perf * dom->mult_factor; + } else { + opp = xa_load(&dom->opps_by_idx, index); + if (!opp) + return -ENODEV; + + *freq = opp->indicative_freq * dom->mult_factor; + } + + return 0; +} + static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph, u8 evt_id, ktime_t timestamp, const void *payld, size_t payld_sz, void *report, u32 *src_id) { + int ret; void *rep = NULL; + struct perf_dom_info *dom; switch (evt_id) { case SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED: { const struct scmi_perf_limits_notify_payld *p = payld; struct scmi_perf_limits_report *r = report; + unsigned long freq_min, freq_max; if (sizeof(*p) != payld_sz) break; @@ -1076,14 +1105,36 @@ static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph, r->domain_id = le32_to_cpu(p->domain_id); r->range_max = le32_to_cpu(p->range_max); r->range_min = le32_to_cpu(p->range_min); + /* Check if the reported domain exist at all */ + dom = scmi_perf_domain_lookup(ph, r->domain_id); + if (IS_ERR(dom)) + break; + /* + * Event will be reported from this point on... + * ...even if, later, xlated frequencies were not retrieved. + */ *src_id = r->domain_id; rep = r; + + ret = scmi_perf_xlate_opp_to_freq(dom, r->range_max, &freq_max); + if (ret) + break; + + ret = scmi_perf_xlate_opp_to_freq(dom, r->range_min, &freq_min); + if (ret) + break; + + /* Report translated freqs ONLY if both available */ + r->range_max_freq = freq_max; + r->range_min_freq = freq_min; + break; } case SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED: { const struct scmi_perf_level_notify_payld *p = payld; struct scmi_perf_level_report *r = report; + unsigned long freq; if (sizeof(*p) != payld_sz) break; @@ -1091,9 +1142,27 @@ static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph, r->timestamp = timestamp; r->agent_id = le32_to_cpu(p->agent_id); r->domain_id = le32_to_cpu(p->domain_id); + /* Report translated freqs ONLY if available */ r->performance_level = le32_to_cpu(p->performance_level); + /* Check if the reported domain exist at all */ + dom = scmi_perf_domain_lookup(ph, r->domain_id); + if (IS_ERR(dom)) + break; + /* + * Event will be reported from this point on... + * ...even if, later, xlated frequencies were not retrieved. + */ *src_id = r->domain_id; rep = r; + + /* Report translated freqs ONLY if available */ + ret = scmi_perf_xlate_opp_to_freq(dom, r->performance_level, + &freq); + if (ret) + break; + + r->performance_level_freq = freq; + break; } default: diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 0cc40af5519a..9b9351e07a11 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -956,6 +956,8 @@ struct scmi_perf_limits_report { unsigned int domain_id; unsigned int range_max; unsigned int range_min; + unsigned long range_max_freq; + unsigned long range_min_freq; }; struct scmi_perf_level_report { @@ -963,6 +965,7 @@ struct scmi_perf_level_report { unsigned int agent_id; unsigned int domain_id; unsigned int performance_level; + unsigned long performance_level_freq; }; struct scmi_sensor_trip_point_report { -- cgit v1.2.3-58-ga151 From 1315848f1f8a0100cb6f8a7187bc320c5d98947f Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Mon, 25 Dec 2023 20:03:56 +0100 Subject: firmware: tegra: bpmp: Return directly after a failed kzalloc() in get_filename() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The kfree() function was called in one case by the get_filename() function during error handling even if the passed variable contained a null pointer. This issue was detected by using the Coccinelle software. Thus return directly after a call of the function “kzalloc” failed at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Thierry Reding --- drivers/firmware/tegra/bpmp-debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/tegra/bpmp-debugfs.c b/drivers/firmware/tegra/bpmp-debugfs.c index bbcdd9fed3fb..4221fed70ad4 100644 --- a/drivers/firmware/tegra/bpmp-debugfs.c +++ b/drivers/firmware/tegra/bpmp-debugfs.c @@ -77,7 +77,7 @@ static const char *get_filename(struct tegra_bpmp *bpmp, root_path_buf = kzalloc(root_path_buf_len, GFP_KERNEL); if (!root_path_buf) - goto out; + return NULL; root_path = dentry_path(bpmp->debugfs_mirror, root_path_buf, root_path_buf_len); -- cgit v1.2.3-58-ga151 From a9c049f47e06cebeefff33a27ae7111818950ab9 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 20 Feb 2024 22:21:57 -0800 Subject: firmware: arm_scmi: Fix struct kernel-doc warnings in optee transport Fix the kernel-doc notation for the nested union in struct scmi_optee_channel to eliminate kernel-doc warnings: | optee.c:130: warning: Excess struct member 'shmem' description | in 'scmi_optee_channel' | optee.c:131: warning: Function parameter or struct member 'req' | not described in 'scmi_optee_channel' Signed-off-by: Randy Dunlap Cc: Sudeep Holla Cc: Cristian Marussi Cc: Reviewed-by: Cristian Marussi Link: https://lore.kernel.org/r/20240221062157.8694-1-rdunlap@infradead.org Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/optee.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/optee.c b/drivers/firmware/arm_scmi/optee.c index 25bfb465484d..4e7944b91e38 100644 --- a/drivers/firmware/arm_scmi/optee.c +++ b/drivers/firmware/arm_scmi/optee.c @@ -109,8 +109,10 @@ enum scmi_optee_pta_cmd { * @rx_len: Response size * @mu: Mutex protection on channel access * @cinfo: SCMI channel information - * @shmem: Virtual base address of the shared memory - * @req: Shared memory protocol handle for SCMI request and synchronous response + * @req: union for SCMI interface + * @req.shmem: Virtual base address of the shared memory + * @req.msg: Shared memory protocol handle for SCMI request and + * synchronous response * @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem * @link: Reference in agent's channel list */ -- cgit v1.2.3-58-ga151 From 8c80c42ad4014cc84bdcd4d5586730536f76f888 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 14 Feb 2024 18:30:01 +0000 Subject: firmware: arm_scmi: Add support for v3.2 NEGOTIATE_PROTOCOL_VERSION Freshly introduced NEGOTIATE_PROTOCOL_VERSION allows the agent to ascertain upfront if a specific protocol(usually older) version is supported by the platform. It is used by the agent in case the platform has advertised the support of a newer protocol version than the latest version supported by the agent, since backward compatibility cannot be automatically assumed. Emit a warning about possible incompatibility when negotiation was not possible or just print the successfully negotiated protocol. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240214183006.3403207-3-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 65 ++++++++++++++++++++++++++++++++--- drivers/firmware/arm_scmi/protocols.h | 1 + 2 files changed, 61 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 4a64ad5c21ee..34d77802c990 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -86,6 +86,12 @@ struct scmi_xfers_info { * @users: A refcount to track effective users of this protocol. * @priv: Reference for optional protocol private data. * @version: Protocol version supported by the platform as detected at runtime. + * @negotiated_version: When the platform supports a newer protocol version, + * the agent will try to negotiate with the platform the + * usage of the newest version known to it, since + * backward compatibility is NOT automatically assured. + * This field is NON-zero when a successful negotiation + * has completed. * @ph: An embedded protocol handle that will be passed down to protocol * initialization code to identify this instance. * @@ -99,6 +105,7 @@ struct scmi_protocol_instance { refcount_t users; void *priv; unsigned int version; + unsigned int negotiated_version; struct scmi_protocol_handle ph; }; @@ -1815,6 +1822,44 @@ scmi_revision_area_get(const struct scmi_protocol_handle *ph) return pi->handle->version; } +/** + * scmi_protocol_version_negotiate - Negotiate protocol version + * + * @ph: A reference to the protocol handle. + * + * An helper to negotiate a protocol version different from the latest + * advertised as supported from the platform: on Success backward + * compatibility is assured by the platform. + * + * Return: 0 on Success + */ +static int scmi_protocol_version_negotiate(struct scmi_protocol_handle *ph) +{ + int ret; + struct scmi_xfer *t; + struct scmi_protocol_instance *pi = ph_to_pi(ph); + + /* At first check if NEGOTIATE_PROTOCOL_VERSION is supported ... */ + ret = scmi_protocol_msg_check(ph, NEGOTIATE_PROTOCOL_VERSION, NULL); + if (ret) + return ret; + + /* ... then attempt protocol version negotiation */ + ret = xfer_get_init(ph, NEGOTIATE_PROTOCOL_VERSION, + sizeof(__le32), 0, &t); + if (ret) + return ret; + + put_unaligned_le32(pi->proto->supported_version, t->tx.buf); + ret = do_xfer(ph, t); + if (!ret) + pi->negotiated_version = pi->proto->supported_version; + + xfer_put(ph, t); + + return ret; +} + /** * scmi_alloc_init_protocol_instance - Allocate and initialize a protocol * instance descriptor. @@ -1887,11 +1932,21 @@ scmi_alloc_init_protocol_instance(struct scmi_info *info, devres_close_group(handle->dev, pi->gid); dev_dbg(handle->dev, "Initialized protocol: 0x%X\n", pi->proto->id); - if (pi->version > proto->supported_version) - dev_warn(handle->dev, - "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X." - "Backward compatibility is NOT assured.\n", - pi->version, pi->proto->id); + if (pi->version > proto->supported_version) { + ret = scmi_protocol_version_negotiate(&pi->ph); + if (!ret) { + dev_info(handle->dev, + "Protocol 0x%X successfully negotiated version 0x%X\n", + proto->id, pi->negotiated_version); + } else { + dev_warn(handle->dev, + "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n", + pi->version, pi->proto->id); + dev_warn(handle->dev, + "Trying version 0x%X. Backward compatibility is NOT assured.\n", + pi->proto->supported_version); + } + } return pi; diff --git a/drivers/firmware/arm_scmi/protocols.h b/drivers/firmware/arm_scmi/protocols.h index 26a3edd49fea..693019fff0f6 100644 --- a/drivers/firmware/arm_scmi/protocols.h +++ b/drivers/firmware/arm_scmi/protocols.h @@ -33,6 +33,7 @@ enum scmi_common_cmd { PROTOCOL_VERSION = 0x0, PROTOCOL_ATTRIBUTES = 0x1, PROTOCOL_MESSAGE_ATTRIBUTES = 0x2, + NEGOTIATE_PROTOCOL_VERSION = 0x10, }; /** -- cgit v1.2.3-58-ga151 From e4ad2b0130eff1cc72f93ea7fd184b0e420f0736 Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 14 Feb 2024 18:30:02 +0000 Subject: firmware: arm_scmi: Add clock check for extended config support SCMI v3.2 added support to set/get clock custom OEM types; such support is conditionally present, though, depending on an extended config attribute bit possibly advertised by the platform server on a per-domain base. Add a check to verify if OEM types are supported before allowing any kind of OEM-specific get/set operation. Also add a check around all the new v3.2 clock features. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240214183006.3403207-4-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 33 +++++++++++++++++++++++++++------ include/linux/scmi_protocol.h | 1 + 2 files changed, 28 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 85eda5db40ba..add350bf2a7f 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -54,6 +54,7 @@ struct scmi_msg_resp_clock_attributes { #define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x) ((x) & BIT(30)) #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29)) #define SUPPORTS_PARENT_CLOCK(x) ((x) & BIT(28)) +#define SUPPORTS_EXTENDED_CONFIG(x) ((x) & BIT(27)) #define SUPPORTS_GET_PERMISSIONS(x) ((x) & BIT(1)) u8 name[SCMI_SHORT_NAME_MAX_SIZE]; __le32 clock_enable_latency; @@ -388,10 +389,14 @@ static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph, if (cinfo->notify_rate_change_requested_cmd && SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes)) clk->rate_change_requested_notifications = true; - if (SUPPORTS_PARENT_CLOCK(attributes)) - scmi_clock_possible_parents(ph, clk_id, clk); - if (SUPPORTS_GET_PERMISSIONS(attributes)) - scmi_clock_get_permissions(ph, clk_id, clk); + if (PROTOCOL_REV_MAJOR(version) >= 0x3) { + if (SUPPORTS_PARENT_CLOCK(attributes)) + scmi_clock_possible_parents(ph, clk_id, clk); + if (SUPPORTS_GET_PERMISSIONS(attributes)) + scmi_clock_get_permissions(ph, clk_id, clk); + if (SUPPORTS_EXTENDED_CONFIG(attributes)) + clk->extended_config = true; + } } return ret; @@ -700,7 +705,7 @@ scmi_clock_get_parent(const struct scmi_protocol_handle *ph, u32 clk_id, return ret; } -/* For SCMI clock v2.1 and onwards */ +/* For SCMI clock v3.0 and onwards */ static int scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id, enum clk_state state, u8 oem_type, u32 oem_val, @@ -773,7 +778,7 @@ static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id, NULL_OEM_TYPE, 0, atomic); } -/* For SCMI clock v2.1 and onwards */ +/* For SCMI clock v3.0 and onwards */ static int scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id, u8 oem_type, u32 *attributes, bool *enabled, @@ -860,6 +865,14 @@ static int scmi_clock_config_oem_set(const struct scmi_protocol_handle *ph, bool atomic) { struct clock_info *ci = ph->get_priv(ph); + struct scmi_clock_info *clk; + + clk = scmi_clock_domain_lookup(ci, clk_id); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (!clk->extended_config) + return -EOPNOTSUPP; return ci->clock_config_set(ph, clk_id, CLK_STATE_UNCHANGED, oem_type, oem_val, atomic); @@ -870,6 +883,14 @@ static int scmi_clock_config_oem_get(const struct scmi_protocol_handle *ph, u32 *attributes, bool atomic) { struct clock_info *ci = ph->get_priv(ph); + struct scmi_clock_info *clk; + + clk = scmi_clock_domain_lookup(ci, clk_id); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (!clk->extended_config) + return -EOPNOTSUPP; return ci->clock_config_get(ph, clk_id, oem_type, attributes, NULL, oem_val, atomic); diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 9b9351e07a11..46a61173c91c 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -50,6 +50,7 @@ struct scmi_clock_info { bool state_ctrl_forbidden; bool rate_ctrl_forbidden; bool parent_ctrl_forbidden; + bool extended_config; union { struct { int num_rates; -- cgit v1.2.3-58-ga151 From 62092c428fb528fcd117a580216915af04df450e Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 14 Feb 2024 18:30:03 +0000 Subject: firmware: arm_scmi: Add standard clock OEM definitions Add a common enum to define the standard clock OEM types defined by the SCMI specification, so as to enable the configuration of such extended configuration properties with the existent clock protocol operations. Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240214183006.3403207-5-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 32 +++++++++++++++++++------------- include/linux/scmi_protocol.h | 14 +++++++++++--- 2 files changed, 30 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index add350bf2a7f..95393af655ed 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -165,10 +165,12 @@ struct clock_info { struct scmi_clock_info *clk; int (*clock_config_set)(const struct scmi_protocol_handle *ph, u32 clk_id, enum clk_state state, - u8 oem_type, u32 oem_val, bool atomic); + enum scmi_clock_oem_config oem_type, + u32 oem_val, bool atomic); int (*clock_config_get)(const struct scmi_protocol_handle *ph, - u32 clk_id, u8 oem_type, u32 *attributes, - bool *enabled, u32 *oem_val, bool atomic); + u32 clk_id, enum scmi_clock_oem_config oem_type, + u32 *attributes, bool *enabled, u32 *oem_val, + bool atomic); }; static enum scmi_clock_protocol_cmd evt_2_cmd[] = { @@ -618,7 +620,8 @@ static int scmi_clock_rate_set(const struct scmi_protocol_handle *ph, static int scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id, - enum clk_state state, u8 __unused0, u32 __unused1, + enum clk_state state, + enum scmi_clock_oem_config __unused0, u32 __unused1, bool atomic) { int ret; @@ -708,7 +711,8 @@ scmi_clock_get_parent(const struct scmi_protocol_handle *ph, u32 clk_id, /* For SCMI clock v3.0 and onwards */ static int scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id, - enum clk_state state, u8 oem_type, u32 oem_val, + enum clk_state state, + enum scmi_clock_oem_config oem_type, u32 oem_val, bool atomic) { int ret; @@ -781,8 +785,8 @@ static int scmi_clock_disable(const struct scmi_protocol_handle *ph, u32 clk_id, /* For SCMI clock v3.0 and onwards */ static int scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id, - u8 oem_type, u32 *attributes, bool *enabled, - u32 *oem_val, bool atomic) + enum scmi_clock_oem_config oem_type, u32 *attributes, + bool *enabled, u32 *oem_val, bool atomic) { int ret; u32 flags; @@ -823,8 +827,8 @@ scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id, static int scmi_clock_config_get(const struct scmi_protocol_handle *ph, u32 clk_id, - u8 oem_type, u32 *attributes, bool *enabled, - u32 *oem_val, bool atomic) + enum scmi_clock_oem_config oem_type, u32 *attributes, + bool *enabled, u32 *oem_val, bool atomic) { int ret; struct scmi_xfer *t; @@ -861,8 +865,9 @@ static int scmi_clock_state_get(const struct scmi_protocol_handle *ph, } static int scmi_clock_config_oem_set(const struct scmi_protocol_handle *ph, - u32 clk_id, u8 oem_type, u32 oem_val, - bool atomic) + u32 clk_id, + enum scmi_clock_oem_config oem_type, + u32 oem_val, bool atomic) { struct clock_info *ci = ph->get_priv(ph); struct scmi_clock_info *clk; @@ -879,8 +884,9 @@ static int scmi_clock_config_oem_set(const struct scmi_protocol_handle *ph, } static int scmi_clock_config_oem_get(const struct scmi_protocol_handle *ph, - u32 clk_id, u8 oem_type, u32 *oem_val, - u32 *attributes, bool atomic) + u32 clk_id, + enum scmi_clock_oem_config oem_type, + u32 *oem_val, u32 *attributes, bool atomic) { struct clock_info *ci = ph->get_priv(ph); struct scmi_clock_info *clk; diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index 46a61173c91c..2ee94ff0320c 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -76,6 +76,13 @@ struct scmi_handle; struct scmi_device; struct scmi_protocol_handle; +enum scmi_clock_oem_config { + SCMI_CLOCK_CFG_DUTY_CYCLE = 0x1, + SCMI_CLOCK_CFG_PHASE, + SCMI_CLOCK_CFG_OEM_START = 0x80, + SCMI_CLOCK_CFG_OEM_END = 0xFF, +}; + /** * struct scmi_clk_proto_ops - represents the various operations provided * by SCMI Clock Protocol @@ -108,10 +115,11 @@ struct scmi_clk_proto_ops { int (*state_get)(const struct scmi_protocol_handle *ph, u32 clk_id, bool *enabled, bool atomic); int (*config_oem_get)(const struct scmi_protocol_handle *ph, u32 clk_id, - u8 oem_type, u32 *oem_val, u32 *attributes, - bool atomic); + enum scmi_clock_oem_config oem_type, + u32 *oem_val, u32 *attributes, bool atomic); int (*config_oem_set)(const struct scmi_protocol_handle *ph, u32 clk_id, - u8 oem_type, u32 oem_val, bool atomic); + enum scmi_clock_oem_config oem_type, + u32 oem_val, bool atomic); int (*parent_get)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 *parent_id); int (*parent_set)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 parent_id); }; -- cgit v1.2.3-58-ga151 From 5e0d2fe70cb8030ded45da21bf5bce35b1dbfdfb Mon Sep 17 00:00:00 2001 From: Cristian Marussi Date: Wed, 14 Feb 2024 18:30:04 +0000 Subject: firmware: arm_scmi: Update the supported clock protocol version And finally update the supported clock protocol version to v3.2(0x30000). Signed-off-by: Cristian Marussi Link: https://lore.kernel.org/r/20240214183006.3403207-6-cristian.marussi@arm.com Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/clock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 95393af655ed..134019297d08 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -13,7 +13,7 @@ #include "notify.h" /* Updated only after ALL the mandatory features for that version are merged */ -#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000 +#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000 enum scmi_clock_protocol_cmd { CLOCK_ATTRIBUTES = 0x3, -- cgit v1.2.3-58-ga151 From 51d915cbeef4c7a154f5d810b1e10d8125f2b0cc Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 20 Feb 2024 12:44:28 +0000 Subject: memory: tegra: Correct DLA client names Some of the names for the Tegra234 DLA clients are not unique and do not align with the name of the client ID definitions. Therefore, it is not possible to determine the exact DLA client from messages that print the client name. Fix this by correcting the DLA memory client names for Tegra234 to align with the name of the corresponding memory client ID. Note that although the client names are also used by the interconnect framework, interconnect support for the DLA clients has not been added and so this issue does not impact the interconnect support. Fixes: 5cd24ca0985f ("memory: tegra: Add DLA clients for Tegra234") Signed-off-by: Jon Hunter Link: https://lore.kernel.org/r/20240220124430.19072-1-jonathanh@nvidia.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/tegra/tegra234.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/tegra/tegra234.c b/drivers/memory/tegra/tegra234.c index abff87f917cb..b8a7af2d36c1 100644 --- a/drivers/memory/tegra/tegra234.c +++ b/drivers/memory/tegra/tegra234.c @@ -121,7 +121,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, }, { .id = TEGRA234_MEMORY_CLIENT_DLA1RDB, - .name = "dla0rdb", + .name = "dla1rdb", .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -407,7 +407,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, }, { .id = TEGRA234_MEMORY_CLIENT_DLA1RDB1, - .name = "dla0rdb1", + .name = "dla1rdb1", .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -417,7 +417,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, }, { .id = TEGRA234_MEMORY_CLIENT_DLA1WRB, - .name = "dla0wrb", + .name = "dla1wrb", .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -699,7 +699,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, }, { .id = TEGRA234_MEMORY_CLIENT_DLA1RDA, - .name = "dla0rda", + .name = "dla1rda", .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -709,7 +709,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, }, { .id = TEGRA234_MEMORY_CLIENT_DLA1FALRDB, - .name = "dla0falrdb", + .name = "dla1falrdb", .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -719,7 +719,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, }, { .id = TEGRA234_MEMORY_CLIENT_DLA1WRA, - .name = "dla0wra", + .name = "dla1wra", .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -729,7 +729,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, }, { .id = TEGRA234_MEMORY_CLIENT_DLA1FALWRB, - .name = "dla0falwrb", + .name = "dla1falwrb", .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -917,7 +917,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, }, { .id = TEGRA234_MEMORY_CLIENT_DLA1RDA1, - .name = "dla0rda1", + .name = "dla1rda1", .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { -- cgit v1.2.3-58-ga151 From 6a598c6c08dc6f34235ee6ee246962ccb14a47c3 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 20 Feb 2024 12:44:29 +0000 Subject: memory: tegra: Add BPMP and ICC info for DLA clients Add the BPMP IDs and ICC type information for the Tegra234 DLA memory clients. Signed-off-by: Jon Hunter Link: https://lore.kernel.org/r/20240220124430.19072-2-jonathanh@nvidia.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/tegra/tegra234.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/memory/tegra/tegra234.c b/drivers/memory/tegra/tegra234.c index b8a7af2d36c1..d9cfd2eb0d7f 100644 --- a/drivers/memory/tegra/tegra234.c +++ b/drivers/memory/tegra/tegra234.c @@ -92,6 +92,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA0RDB, .name = "dla0rdb", + .bpmp_id = TEGRA_ICC_BPMP_DLA_0, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA0, .regs = { .sid = { @@ -102,6 +104,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA0RDB1, .name = "dla0rdb1", + .bpmp_id = TEGRA_ICC_BPMP_DLA_0, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA0, .regs = { .sid = { @@ -112,6 +116,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA0WRB, .name = "dla0wrb", + .bpmp_id = TEGRA_ICC_BPMP_DLA_0, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA0, .regs = { .sid = { @@ -122,6 +128,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA1RDB, .name = "dla1rdb", + .bpmp_id = TEGRA_ICC_BPMP_DLA_1, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -408,6 +416,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA1RDB1, .name = "dla1rdb1", + .bpmp_id = TEGRA_ICC_BPMP_DLA_1, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -418,6 +428,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA1WRB, .name = "dla1wrb", + .bpmp_id = TEGRA_ICC_BPMP_DLA_1, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -660,6 +672,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA0RDA, .name = "dla0rda", + .bpmp_id = TEGRA_ICC_BPMP_DLA_0, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA0, .regs = { .sid = { @@ -670,6 +684,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA0FALRDB, .name = "dla0falrdb", + .bpmp_id = TEGRA_ICC_BPMP_DLA_0, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA0, .regs = { .sid = { @@ -680,6 +696,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA0WRA, .name = "dla0wra", + .bpmp_id = TEGRA_ICC_BPMP_DLA_0, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA0, .regs = { .sid = { @@ -690,6 +708,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA0FALWRB, .name = "dla0falwrb", + .bpmp_id = TEGRA_ICC_BPMP_DLA_0, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA0, .regs = { .sid = { @@ -700,6 +720,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA1RDA, .name = "dla1rda", + .bpmp_id = TEGRA_ICC_BPMP_DLA_1, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -710,6 +732,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA1FALRDB, .name = "dla1falrdb", + .bpmp_id = TEGRA_ICC_BPMP_DLA_1, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -720,6 +744,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA1WRA, .name = "dla1wra", + .bpmp_id = TEGRA_ICC_BPMP_DLA_1, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -730,6 +756,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA1FALWRB, .name = "dla1falwrb", + .bpmp_id = TEGRA_ICC_BPMP_DLA_1, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA1, .regs = { .sid = { @@ -908,6 +936,8 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { }, { .id = TEGRA234_MEMORY_CLIENT_DLA0RDA1, .name = "dla0rda1", + .bpmp_id = TEGRA_ICC_BPMP_DLA_0, + .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVDLA0, .regs = { .sid = { -- cgit v1.2.3-58-ga151 From ae1e06fb62c3a0fbcc7c58f45cf1a19641272799 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 20 Feb 2024 12:44:30 +0000 Subject: memory: tegra: Fix indentation Fix the indentation of the '.regs' member for the Tegra234 NVJPG memory client. Signed-off-by: Jon Hunter Link: https://lore.kernel.org/r/20240220124430.19072-3-jonathanh@nvidia.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/tegra/tegra234.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/memory/tegra/tegra234.c b/drivers/memory/tegra/tegra234.c index d9cfd2eb0d7f..5f57cea48b62 100644 --- a/drivers/memory/tegra/tegra234.c +++ b/drivers/memory/tegra/tegra234.c @@ -551,7 +551,7 @@ static const struct tegra_mc_client tegra234_mc_clients[] = { .bpmp_id = TEGRA_ICC_BPMP_NVJPG_0, .type = TEGRA_ICC_NISO, .sid = TEGRA234_SID_NVJPG, - .regs = { + .regs = { .sid = { .override = 0x3f8, .security = 0x3fc, -- cgit v1.2.3-58-ga151 From 0b7c6075022ccff529318597dc6b165dd6a25c8f Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Tue, 20 Feb 2024 22:06:12 +0000 Subject: soc: samsung: exynos-pmu: Add regmap support for SoCs that protect PMU regs Some Exynos based SoCs like Tensor gs101 protect the PMU registers for security hardening reasons so that they are only write accessible in el3 via an SMC call. As most Exynos drivers that need to write PMU registers currently obtain a regmap via syscon (phys, pinctrl, watchdog). Support for the above usecase is implemented in this driver using a custom regmap similar to syscon to handle the SMC call. Platforms that don't secure PMU registers, get a mmio regmap like before. As regmaps abstract out the underlying register access changes to the leaf drivers are minimal. A new API exynos_get_pmu_regmap_by_phandle() is provided for leaf drivers that currently use syscon_regmap_lookup_by_phandle(). This also handles deferred probing. Tested-by: Sam Protsenko Tested-by: Alexey Klimov Reviewed-by: Sam Protsenko Signed-off-by: Peter Griffin Link: https://lore.kernel.org/r/20240220220613.797068-2-peter.griffin@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/soc/samsung/Kconfig | 1 + drivers/soc/samsung/exynos-pmu.c | 235 ++++++++++++++++++++++++++++++++- drivers/soc/samsung/exynos-pmu.h | 1 + include/linux/soc/samsung/exynos-pmu.h | 11 +- 4 files changed, 245 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/samsung/Kconfig b/drivers/soc/samsung/Kconfig index 27ec99af77e3..1a5dfdc978dc 100644 --- a/drivers/soc/samsung/Kconfig +++ b/drivers/soc/samsung/Kconfig @@ -42,6 +42,7 @@ config EXYNOS_PMU depends on ARCH_EXYNOS || ((ARM || ARM64) && COMPILE_TEST) select EXYNOS_PMU_ARM_DRIVERS if ARM && ARCH_EXYNOS select MFD_CORE + select REGMAP_MMIO # There is no need to enable these drivers for ARMv8 config EXYNOS_PMU_ARM_DRIVERS diff --git a/drivers/soc/samsung/exynos-pmu.c b/drivers/soc/samsung/exynos-pmu.c index 250537d7cfd6..fd8b6ac06656 100644 --- a/drivers/soc/samsung/exynos-pmu.c +++ b/drivers/soc/samsung/exynos-pmu.c @@ -5,6 +5,7 @@ // // Exynos - CPU PMU(Power Management Unit) support +#include #include #include #include @@ -12,19 +13,134 @@ #include #include #include +#include #include #include #include "exynos-pmu.h" +#define PMUALIVE_MASK GENMASK(13, 0) +#define TENSOR_SET_BITS (BIT(15) | BIT(14)) +#define TENSOR_CLR_BITS BIT(15) +#define TENSOR_SMC_PMU_SEC_REG 0x82000504 +#define TENSOR_PMUREG_READ 0 +#define TENSOR_PMUREG_WRITE 1 +#define TENSOR_PMUREG_RMW 2 + struct exynos_pmu_context { struct device *dev; const struct exynos_pmu_data *pmu_data; + struct regmap *pmureg; }; void __iomem *pmu_base_addr; static struct exynos_pmu_context *pmu_context; +/* forward declaration */ +static struct platform_driver exynos_pmu_driver; + +/* + * Tensor SoCs are configured so that PMU_ALIVE registers can only be written + * from EL3, but are still read accessible. As Linux needs to write some of + * these registers, the following functions are provided and exposed via + * regmap. + * + * Note: This SMC interface is known to be implemented on gs101 and derivative + * SoCs. + */ + +/* Write to a protected PMU register. */ +static int tensor_sec_reg_write(void *context, unsigned int reg, + unsigned int val) +{ + struct arm_smccc_res res; + unsigned long pmu_base = (unsigned long)context; + + arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg, + TENSOR_PMUREG_WRITE, val, 0, 0, 0, 0, &res); + + /* returns -EINVAL if access isn't allowed or 0 */ + if (res.a0) + pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0); + + return (int)res.a0; +} + +/* Read/Modify/Write a protected PMU register. */ +static int tensor_sec_reg_rmw(void *context, unsigned int reg, + unsigned int mask, unsigned int val) +{ + struct arm_smccc_res res; + unsigned long pmu_base = (unsigned long)context; + + arm_smccc_smc(TENSOR_SMC_PMU_SEC_REG, pmu_base + reg, + TENSOR_PMUREG_RMW, mask, val, 0, 0, 0, &res); + + /* returns -EINVAL if access isn't allowed or 0 */ + if (res.a0) + pr_warn("%s(): SMC failed: %d\n", __func__, (int)res.a0); + + return (int)res.a0; +} + +/* + * Read a protected PMU register. All PMU registers can be read by Linux. + * Note: The SMC read register is not used, as only registers that can be + * written are readable via SMC. + */ +static int tensor_sec_reg_read(void *context, unsigned int reg, + unsigned int *val) +{ + *val = pmu_raw_readl(reg); + return 0; +} + +/* + * For SoCs that have set/clear bit hardware this function can be used when + * the PMU register will be accessed by multiple masters. + * + * For example, to set bits 13:8 in PMU reg offset 0x3e80 + * tensor_set_bits_atomic(ctx, 0x3e80, 0x3f00, 0x3f00); + * + * Set bit 8, and clear bits 13:9 PMU reg offset 0x3e80 + * tensor_set_bits_atomic(0x3e80, 0x100, 0x3f00); + */ +static int tensor_set_bits_atomic(void *ctx, unsigned int offset, u32 val, + u32 mask) +{ + int ret; + unsigned int i; + + for (i = 0; i < 32; i++) { + if (!(mask & BIT(i))) + continue; + + offset &= ~TENSOR_SET_BITS; + + if (val & BIT(i)) + offset |= TENSOR_SET_BITS; + else + offset |= TENSOR_CLR_BITS; + + ret = tensor_sec_reg_write(ctx, offset, i); + if (ret) + return ret; + } + return ret; +} + +static int tensor_sec_update_bits(void *ctx, unsigned int reg, + unsigned int mask, unsigned int val) +{ + /* + * Use atomic operations for PMU_ALIVE registers (offset 0~0x3FFF) + * as the target registers can be accessed by multiple masters. + */ + if (reg > PMUALIVE_MASK) + return tensor_sec_reg_rmw(ctx, reg, mask, val); + + return tensor_set_bits_atomic(ctx, reg, val, mask); +} void pmu_raw_writel(u32 val, u32 offset) { @@ -75,11 +191,41 @@ void exynos_sys_powerdown_conf(enum sys_powerdown mode) #define exynos_pmu_data_arm_ptr(data) NULL #endif +static const struct regmap_config regmap_smccfg = { + .name = "pmu_regs", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .use_single_read = true, + .use_single_write = true, + .reg_read = tensor_sec_reg_read, + .reg_write = tensor_sec_reg_write, + .reg_update_bits = tensor_sec_update_bits, +}; + +static const struct regmap_config regmap_mmiocfg = { + .name = "pmu_regs", + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct exynos_pmu_data gs101_pmu_data = { + .pmu_secure = true +}; + /* * PMU platform driver and devicetree bindings. */ static const struct of_device_id exynos_pmu_of_device_ids[] = { { + .compatible = "google,gs101-pmu", + .data = &gs101_pmu_data, + }, { .compatible = "samsung,exynos3250-pmu", .data = exynos_pmu_data_arm_ptr(exynos3250_pmu_data), }, { @@ -113,19 +259,75 @@ static const struct mfd_cell exynos_pmu_devs[] = { { .name = "exynos-clkout", }, }; +/** + * exynos_get_pmu_regmap() - Obtain pmureg regmap + * + * Find the pmureg regmap previously configured in probe() and return regmap + * pointer. + * + * Return: A pointer to regmap if found or ERR_PTR error value. + */ struct regmap *exynos_get_pmu_regmap(void) { struct device_node *np = of_find_matching_node(NULL, exynos_pmu_of_device_ids); if (np) - return syscon_node_to_regmap(np); + return exynos_get_pmu_regmap_by_phandle(np, NULL); return ERR_PTR(-ENODEV); } EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap); +/** + * exynos_get_pmu_regmap_by_phandle() - Obtain pmureg regmap via phandle + * @np: Device node holding PMU phandle property + * @propname: Name of property holding phandle value + * + * Find the pmureg regmap previously configured in probe() and return regmap + * pointer. + * + * Return: A pointer to regmap if found or ERR_PTR error value. + */ +struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np, + const char *propname) +{ + struct exynos_pmu_context *ctx; + struct device_node *pmu_np; + struct device *dev; + + if (propname) + pmu_np = of_parse_phandle(np, propname, 0); + else + pmu_np = np; + + if (!pmu_np) + return ERR_PTR(-ENODEV); + + /* + * Determine if exynos-pmu device has probed and therefore regmap + * has been created and can be returned to the caller. Otherwise we + * return -EPROBE_DEFER. + */ + dev = driver_find_device_by_of_node(&exynos_pmu_driver.driver, + (void *)pmu_np); + + if (propname) + of_node_put(pmu_np); + + if (!dev) + return ERR_PTR(-EPROBE_DEFER); + + ctx = dev_get_drvdata(dev); + + return ctx->pmureg; +} +EXPORT_SYMBOL_GPL(exynos_get_pmu_regmap_by_phandle); + static int exynos_pmu_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct regmap_config pmu_regmcfg; + struct regmap *regmap; + struct resource *res; int ret; pmu_base_addr = devm_platform_ioremap_resource(pdev, 0); @@ -137,9 +339,38 @@ static int exynos_pmu_probe(struct platform_device *pdev) GFP_KERNEL); if (!pmu_context) return -ENOMEM; - pmu_context->dev = dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENODEV; + pmu_context->pmu_data = of_device_get_match_data(dev); + /* For SoCs that secure PMU register writes use custom regmap */ + if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_secure) { + pmu_regmcfg = regmap_smccfg; + pmu_regmcfg.max_register = resource_size(res) - + pmu_regmcfg.reg_stride; + /* Need physical address for SMC call */ + regmap = devm_regmap_init(dev, NULL, + (void *)(uintptr_t)res->start, + &pmu_regmcfg); + } else { + /* All other SoCs use a MMIO regmap */ + pmu_regmcfg = regmap_mmiocfg; + pmu_regmcfg.max_register = resource_size(res) - + pmu_regmcfg.reg_stride; + regmap = devm_regmap_init_mmio(dev, pmu_base_addr, + &pmu_regmcfg); + } + + if (IS_ERR(regmap)) + return dev_err_probe(&pdev->dev, PTR_ERR(regmap), + "regmap init failed\n"); + + pmu_context->pmureg = regmap; + pmu_context->dev = dev; + if (pmu_context->pmu_data && pmu_context->pmu_data->pmu_init) pmu_context->pmu_data->pmu_init(); diff --git a/drivers/soc/samsung/exynos-pmu.h b/drivers/soc/samsung/exynos-pmu.h index 1c652ffd79b4..0a49a2c9a08e 100644 --- a/drivers/soc/samsung/exynos-pmu.h +++ b/drivers/soc/samsung/exynos-pmu.h @@ -21,6 +21,7 @@ struct exynos_pmu_conf { struct exynos_pmu_data { const struct exynos_pmu_conf *pmu_config; const struct exynos_pmu_conf *pmu_config_extra; + bool pmu_secure; void (*pmu_init)(void); void (*powerdown_conf)(enum sys_powerdown); diff --git a/include/linux/soc/samsung/exynos-pmu.h b/include/linux/soc/samsung/exynos-pmu.h index a4f5516cc956..2bd9d12d9a52 100644 --- a/include/linux/soc/samsung/exynos-pmu.h +++ b/include/linux/soc/samsung/exynos-pmu.h @@ -10,6 +10,7 @@ #define __LINUX_SOC_EXYNOS_PMU_H struct regmap; +struct device_node; enum sys_powerdown { SYS_AFTR, @@ -20,12 +21,20 @@ enum sys_powerdown { extern void exynos_sys_powerdown_conf(enum sys_powerdown mode); #ifdef CONFIG_EXYNOS_PMU -extern struct regmap *exynos_get_pmu_regmap(void); +struct regmap *exynos_get_pmu_regmap(void); +struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np, + const char *propname); #else static inline struct regmap *exynos_get_pmu_regmap(void) { return ERR_PTR(-ENODEV); } + +static inline struct regmap *exynos_get_pmu_regmap_by_phandle(struct device_node *np, + const char *propname) +{ + return ERR_PTR(-ENODEV); +} #endif #endif /* __LINUX_SOC_EXYNOS_PMU_H */ -- cgit v1.2.3-58-ga151 From 746f0770f916e6c48e422d6a34e67eae16707f0e Mon Sep 17 00:00:00 2001 From: Peter Griffin Date: Tue, 20 Feb 2024 22:06:13 +0000 Subject: watchdog: s3c2410_wdt: use exynos_get_pmu_regmap_by_phandle() for PMU regs Obtain the PMU regmap using the new API added to exynos-pmu driver rather than syscon_regmap_lookup_by_phandle(). As this driver no longer depends on mfd syscon remove that header and Kconfig dependency. Tested-by: Alexey Klimov Tested-by: Sam Protsenko Acked-by: Guenter Roeck Reviewed-by: Sam Protsenko Signed-off-by: Peter Griffin Link: https://lore.kernel.org/r/20240220220613.797068-3-peter.griffin@linaro.org Signed-off-by: Krzysztof Kozlowski --- drivers/watchdog/Kconfig | 1 - drivers/watchdog/s3c2410_wdt.c | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 7d22051b15a2..d78fe7137799 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -512,7 +512,6 @@ config S3C2410_WATCHDOG tristate "S3C6410/S5Pv210/Exynos Watchdog" depends on ARCH_S3C64XX || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST select WATCHDOG_CORE - select MFD_SYSCON if ARCH_EXYNOS help Watchdog timer block in the Samsung S3C64xx, S5Pv210 and Exynos SoCs. This will reboot the system when the timer expires with diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 349d30462c8c..686cf544d0ae 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -24,9 +24,9 @@ #include #include #include -#include #include #include +#include #define S3C2410_WTCON 0x00 #define S3C2410_WTDAT 0x04 @@ -699,11 +699,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev) return ret; if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) { - wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, - "samsung,syscon-phandle"); + wdt->pmureg = exynos_get_pmu_regmap_by_phandle(dev->of_node, + "samsung,syscon-phandle"); if (IS_ERR(wdt->pmureg)) return dev_err_probe(dev, PTR_ERR(wdt->pmureg), - "syscon regmap lookup failed.\n"); + "PMU regmap lookup failed.\n"); } wdt_irq = platform_get_irq(pdev, 0); -- cgit v1.2.3-58-ga151 From 719e366a6696020af1e396946f7e56afbc427646 Mon Sep 17 00:00:00 2001 From: Sebastian Reichel Date: Sat, 24 Feb 2024 22:29:34 +0100 Subject: dt-bindings: bus: imx-weim: convert to YAML Convert the i.MX Wireless External Interface Module binding to YAML. Signed-off-by: Sebastian Reichel Link: https://lore.kernel.org/r/20240224213240.1854709-3-sre@kernel.org Reviewed-by: Krzysztof Kozlowski Signed-off-by: Krzysztof Kozlowski --- Documentation/devicetree/bindings/bus/imx-weim.txt | 117 ------------ .../fsl/fsl,imx-weim-peripherals.yaml | 31 ++++ .../memory-controllers/fsl/fsl,imx-weim.yaml | 204 +++++++++++++++++++++ .../memory-controllers/mc-peripheral-props.yaml | 1 + .../bindings/fieldbus/arcx,anybus-controller.txt | 2 +- 5 files changed, 237 insertions(+), 118 deletions(-) delete mode 100644 Documentation/devicetree/bindings/bus/imx-weim.txt create mode 100644 Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim-peripherals.yaml create mode 100644 Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim.yaml (limited to 'drivers') diff --git a/Documentation/devicetree/bindings/bus/imx-weim.txt b/Documentation/devicetree/bindings/bus/imx-weim.txt deleted file mode 100644 index e7f502070d77..000000000000 --- a/Documentation/devicetree/bindings/bus/imx-weim.txt +++ /dev/null @@ -1,117 +0,0 @@ -Device tree bindings for i.MX Wireless External Interface Module (WEIM) - -The term "wireless" does not imply that the WEIM is literally an interface -without wires. It simply means that this module was originally designed for -wireless and mobile applications that use low-power technology. - -The actual devices are instantiated from the child nodes of a WEIM node. - -Required properties: - - - compatible: Should contain one of the following: - "fsl,imx1-weim" - "fsl,imx27-weim" - "fsl,imx51-weim" - "fsl,imx50-weim" - "fsl,imx6q-weim" - - reg: A resource specifier for the register space - (see the example below) - - clocks: the clock, see the example below. - - #address-cells: Must be set to 2 to allow memory address translation - - #size-cells: Must be set to 1 to allow CS address passing - - ranges: Must be set up to reflect the memory layout with four - integer values for each chip-select line in use: - - 0 - -Optional properties: - - - fsl,weim-cs-gpr: For "fsl,imx50-weim" and "fsl,imx6q-weim" type of - devices, it should be the phandle to the system General - Purpose Register controller that contains WEIM CS GPR - register, e.g. IOMUXC_GPR1 on i.MX6Q. IOMUXC_GPR1[11:0] - should be set up as one of the following 4 possible - values depending on the CS space configuration. - - IOMUXC_GPR1[11:0] CS0 CS1 CS2 CS3 - --------------------------------------------- - 05 128M 0M 0M 0M - 033 64M 64M 0M 0M - 0113 64M 32M 32M 0M - 01111 32M 32M 32M 32M - - In case that the property is absent, the reset value or - what bootloader sets up in IOMUXC_GPR1[11:0] will be - used. - - - fsl,burst-clk-enable For "fsl,imx50-weim" and "fsl,imx6q-weim" type of - devices, the presence of this property indicates that - the weim bus should operate in Burst Clock Mode. - - - fsl,continuous-burst-clk Make Burst Clock to output continuous clock. - Without this option Burst Clock will output clock - only when necessary. This takes effect only if - "fsl,burst-clk-enable" is set. - -Timing property for child nodes. It is mandatory, not optional. - - - fsl,weim-cs-timing: The timing array, contains timing values for the - child node. We get the CS indexes from the address - ranges in the child node's "reg" property. - The number of registers depends on the selected chip: - For i.MX1, i.MX21 ("fsl,imx1-weim") there are two - registers: CSxU, CSxL. - For i.MX25, i.MX27, i.MX31 and i.MX35 ("fsl,imx27-weim") - there are three registers: CSCRxU, CSCRxL, CSCRxA. - For i.MX50, i.MX53 ("fsl,imx50-weim"), - i.MX51 ("fsl,imx51-weim") and i.MX6Q ("fsl,imx6q-weim") - there are six registers: CSxGCR1, CSxGCR2, CSxRCR1, - CSxRCR2, CSxWCR1, CSxWCR2. - -Example for an imx6q-sabreauto board, the NOR flash connected to the WEIM: - - weim: weim@21b8000 { - compatible = "fsl,imx6q-weim"; - reg = <0x021b8000 0x4000>; - clocks = <&clks 196>; - #address-cells = <2>; - #size-cells = <1>; - ranges = <0 0 0x08000000 0x08000000>; - fsl,weim-cs-gpr = <&gpr>; - - nor@0,0 { - compatible = "cfi-flash"; - reg = <0 0 0x02000000>; - #address-cells = <1>; - #size-cells = <1>; - bank-width = <2>; - fsl,weim-cs-timing = <0x00620081 0x00000001 0x1c022000 - 0x0000c000 0x1404a38e 0x00000000>; - }; - }; - -Example for an imx6q-based board, a multi-chipselect device connected to WEIM: - -In this case, both chip select 0 and 1 will be configured with the same timing -array values. - - weim: weim@21b8000 { - compatible = "fsl,imx6q-weim"; - reg = <0x021b8000 0x4000>; - clocks = <&clks 196>; - #address-cells = <2>; - #size-cells = <1>; - ranges = <0 0 0x08000000 0x02000000 - 1 0 0x0a000000 0x02000000 - 2 0 0x0c000000 0x02000000 - 3 0 0x0e000000 0x02000000>; - fsl,weim-cs-gpr = <&gpr>; - - acme@0 { - compatible = "acme,whatever"; - reg = <0 0 0x100>, <0 0x400000 0x800>, - <1 0x400000 0x800>; - fsl,weim-cs-timing = <0x024400b1 0x00001010 0x20081100 - 0x00000000 0xa0000240 0x00000000>; - }; - }; diff --git a/Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim-peripherals.yaml b/Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim-peripherals.yaml new file mode 100644 index 000000000000..82fc5f4a1ed6 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim-peripherals.yaml @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/fsl/fsl,imx-weim-peripherals.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: i.MX WEIM Bus Peripheral Nodes + +maintainers: + - Shawn Guo + - Sascha Hauer + +description: + This binding is meant for the child nodes of the WEIM node. The node + represents any device connected to the WEIM bus. It may be a Flash chip, + RAM chip or Ethernet controller, etc. These properties are meant for + configuring the WEIM settings/timings and will accompany the bindings + supported by the respective device. + +properties: + reg: true + + fsl,weim-cs-timing: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: + Timing values for the child node. + minItems: 2 + maxItems: 6 + +# the WEIM child will have its own native properties +additionalProperties: true diff --git a/Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim.yaml b/Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim.yaml new file mode 100644 index 000000000000..3f40ca5b13f6 --- /dev/null +++ b/Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim.yaml @@ -0,0 +1,204 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/memory-controllers/fsl/fsl,imx-weim.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: i.MX Wireless External Interface Module (WEIM) + +maintainers: + - Shawn Guo + - Sascha Hauer + +description: + The term "wireless" does not imply that the WEIM is literally an interface + without wires. It simply means that this module was originally designed for + wireless and mobile applications that use low-power technology. The actual + devices are instantiated from the child nodes of a WEIM node. + +properties: + $nodename: + pattern: "^memory-controller@[0-9a-f]+$" + + compatible: + oneOf: + - enum: + - fsl,imx1-weim + - fsl,imx27-weim + - fsl,imx50-weim + - fsl,imx51-weim + - fsl,imx6q-weim + - items: + - enum: + - fsl,imx31-weim + - fsl,imx35-weim + - const: fsl,imx27-weim + - items: + - enum: + - fsl,imx6sx-weim + - fsl,imx6ul-weim + - const: fsl,imx6q-weim + + "#address-cells": + const: 2 + + "#size-cells": + const: 1 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + + ranges: true + + fsl,weim-cs-gpr: + $ref: /schemas/types.yaml#/definitions/phandle + description: | + Phandle to the system General Purpose Register controller that contains + WEIM CS GPR register, e.g. IOMUXC_GPR1 on i.MX6Q. IOMUXC_GPR1[11:0] + should be set up as one of the following 4 possible values depending on + the CS space configuration. + + IOMUXC_GPR1[11:0] CS0 CS1 CS2 CS3 + --------------------------------------------- + 05 128M 0M 0M 0M + 033 64M 64M 0M 0M + 0113 64M 32M 32M 0M + 01111 32M 32M 32M 32M + + In case that the property is absent, the reset value or what bootloader + sets up in IOMUXC_GPR1[11:0] will be used. + + fsl,burst-clk-enable: + type: boolean + description: + The presence of this property indicates that the weim bus should operate + in Burst Clock Mode. + + fsl,continuous-burst-clk: + type: boolean + description: + Make Burst Clock to output continuous clock. Without this option Burst + Clock will output clock only when necessary. + +patternProperties: + "^.*@[0-7],[0-9a-f]+$": + type: object + description: Devices attached to chip selects are represented as subnodes. + $ref: fsl,imx-weim-peripherals.yaml + additionalProperties: true + required: + - fsl,weim-cs-timing + +required: + - compatible + - reg + - clocks + - "#address-cells" + - "#size-cells" + - ranges + +allOf: + - if: + properties: + compatible: + not: + contains: + enum: + - fsl,imx50-weim + - fsl,imx6q-weim + then: + properties: + fsl,weim-cs-gpr: false + fsl,burst-clk-enable: false + - if: + not: + required: + - fsl,burst-clk-enable + then: + properties: + fsl,continuous-burst-clk: false + - if: + properties: + compatible: + contains: + const: fsl,imx1-weim + then: + patternProperties: + "^.*@[0-7],[0-9a-f]+$": + properties: + fsl,weim-cs-timing: + items: + items: + - description: CSxU + - description: CSxL + - if: + properties: + compatible: + contains: + enum: + - fsl,imx27-weim + - fsl,imx31-weim + - fsl,imx35-weim + then: + patternProperties: + "^.*@[0-7],[0-9a-f]+$": + properties: + fsl,weim-cs-timing: + items: + items: + - description: CSCRxU + - description: CSCRxL + - description: CSCRxA + - if: + properties: + compatible: + contains: + enum: + - fsl,imx50-weim + - fsl,imx51-weim + - fsl,imx6q-weim + - fsl,imx6sx-weim + - fsl,imx6ul-weim + then: + patternProperties: + "^.*@[0-7],[0-9a-f]+$": + properties: + fsl,weim-cs-timing: + items: + items: + - description: CSxGCR1 + - description: CSxGCR2 + - description: CSxRCR1 + - description: CSxRCR2 + - description: CSxWCR1 + - description: CSxWCR2 + +additionalProperties: false + +examples: + - | + memory-controller@21b8000 { + compatible = "fsl,imx6q-weim"; + reg = <0x021b8000 0x4000>; + clocks = <&clks 196>; + #address-cells = <2>; + #size-cells = <1>; + ranges = <0 0 0x08000000 0x08000000>; + fsl,weim-cs-gpr = <&gpr>; + + flash@0,0 { + compatible = "cfi-flash"; + reg = <0 0 0x02000000>; + #address-cells = <1>; + #size-cells = <1>; + bank-width = <2>; + fsl,weim-cs-timing = <0x00620081 0x00000001 0x1c022000 + 0x0000c000 0x1404a38e 0x00000000>; + }; + }; diff --git a/Documentation/devicetree/bindings/memory-controllers/mc-peripheral-props.yaml b/Documentation/devicetree/bindings/memory-controllers/mc-peripheral-props.yaml index 8d9dae15ade0..00deeb09f87d 100644 --- a/Documentation/devicetree/bindings/memory-controllers/mc-peripheral-props.yaml +++ b/Documentation/devicetree/bindings/memory-controllers/mc-peripheral-props.yaml @@ -37,5 +37,6 @@ allOf: - $ref: ingenic,nemc-peripherals.yaml# - $ref: intel,ixp4xx-expansion-peripheral-props.yaml# - $ref: ti,gpmc-child.yaml# + - $ref: fsl/fsl,imx-weim-peripherals.yaml additionalProperties: true diff --git a/drivers/staging/fieldbus/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt b/drivers/staging/fieldbus/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt index b1f9474f36d5..f34a95611645 100644 --- a/drivers/staging/fieldbus/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt +++ b/drivers/staging/fieldbus/Documentation/devicetree/bindings/fieldbus/arcx,anybus-controller.txt @@ -48,7 +48,7 @@ Example of usage: ----------------- This example places the bridge on top of the i.MX WEIM parallel bus, see: -Documentation/devicetree/bindings/bus/imx-weim.txt +Documentation/devicetree/bindings/memory-controllers/fsl/fsl,imx-weim.yaml &weim { controller@0,0 { -- cgit v1.2.3-58-ga151 From 722463f73bcf65a8c818752a38c14ee672c77da1 Mon Sep 17 00:00:00 2001 From: Christophe Kerello Date: Mon, 26 Feb 2024 11:14:25 +0100 Subject: memory: stm32-fmc2-ebi: check regmap_read return value Check regmap_read return value to avoid to use uninitialized local variables. Signed-off-by: Christophe Kerello Link: https://lore.kernel.org/r/20240226101428.37791-3-christophe.kerello@foss.st.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/stm32-fmc2-ebi.c | 122 +++++++++++++++++++++++++++++----------- 1 file changed, 88 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/stm32-fmc2-ebi.c b/drivers/memory/stm32-fmc2-ebi.c index 47d0ea5f1616..81c1cd88fb97 100644 --- a/drivers/memory/stm32-fmc2-ebi.c +++ b/drivers/memory/stm32-fmc2-ebi.c @@ -181,8 +181,11 @@ static int stm32_fmc2_ebi_check_mux(struct stm32_fmc2_ebi *ebi, int cs) { u32 bcr; + int ret; - regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if (ret) + return ret; if (bcr & FMC2_BCR_MTYP) return 0; @@ -195,8 +198,11 @@ static int stm32_fmc2_ebi_check_waitcfg(struct stm32_fmc2_ebi *ebi, int cs) { u32 bcr, val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_NOR); + int ret; - regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if (ret) + return ret; if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN) return 0; @@ -209,8 +215,11 @@ static int stm32_fmc2_ebi_check_sync_trans(struct stm32_fmc2_ebi *ebi, int cs) { u32 bcr; + int ret; - regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if (ret) + return ret; if (bcr & FMC2_BCR_BURSTEN) return 0; @@ -223,8 +232,11 @@ static int stm32_fmc2_ebi_check_async_trans(struct stm32_fmc2_ebi *ebi, int cs) { u32 bcr; + int ret; - regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if (ret) + return ret; if (!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW)) return 0; @@ -237,8 +249,11 @@ static int stm32_fmc2_ebi_check_cpsize(struct stm32_fmc2_ebi *ebi, int cs) { u32 bcr, val = FIELD_PREP(FMC2_BCR_MTYP, FMC2_BCR_MTYP_PSRAM); + int ret; - regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if (ret) + return ret; if ((bcr & FMC2_BCR_MTYP) == val && bcr & FMC2_BCR_BURSTEN) return 0; @@ -251,12 +266,18 @@ static int stm32_fmc2_ebi_check_address_hold(struct stm32_fmc2_ebi *ebi, int cs) { u32 bcr, bxtr, val = FIELD_PREP(FMC2_BXTR_ACCMOD, FMC2_BXTR_EXTMOD_D); + int ret; + + ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if (ret) + return ret; - regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); if (prop->reg_type == FMC2_REG_BWTR) - regmap_read(ebi->regmap, FMC2_BWTR(cs), &bxtr); + ret = regmap_read(ebi->regmap, FMC2_BWTR(cs), &bxtr); else - regmap_read(ebi->regmap, FMC2_BTR(cs), &bxtr); + ret = regmap_read(ebi->regmap, FMC2_BTR(cs), &bxtr); + if (ret) + return ret; if ((!(bcr & FMC2_BCR_BURSTEN) || !(bcr & FMC2_BCR_CBURSTRW)) && ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN)) @@ -270,12 +291,19 @@ static int stm32_fmc2_ebi_check_clk_period(struct stm32_fmc2_ebi *ebi, int cs) { u32 bcr, bcr1; + int ret; - regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); - if (cs) - regmap_read(ebi->regmap, FMC2_BCR1, &bcr1); - else + ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if (ret) + return ret; + + if (cs) { + ret = regmap_read(ebi->regmap, FMC2_BCR1, &bcr1); + if (ret) + return ret; + } else { bcr1 = bcr; + } if (bcr & FMC2_BCR_BURSTEN && (!cs || !(bcr1 & FMC2_BCR1_CCLKEN))) return 0; @@ -307,12 +335,18 @@ static u32 stm32_fmc2_ebi_ns_to_clk_period(struct stm32_fmc2_ebi *ebi, { u32 nb_clk_cycles = stm32_fmc2_ebi_ns_to_clock_cycles(ebi, cs, setup); u32 bcr, btr, clk_period; + int ret; + + ret = regmap_read(ebi->regmap, FMC2_BCR1, &bcr); + if (ret) + return ret; - regmap_read(ebi->regmap, FMC2_BCR1, &bcr); if (bcr & FMC2_BCR1_CCLKEN || !cs) - regmap_read(ebi->regmap, FMC2_BTR1, &btr); + ret = regmap_read(ebi->regmap, FMC2_BTR1, &btr); else - regmap_read(ebi->regmap, FMC2_BTR(cs), &btr); + ret = regmap_read(ebi->regmap, FMC2_BTR(cs), &btr); + if (ret) + return ret; clk_period = FIELD_GET(FMC2_BTR_CLKDIV, btr) + 1; @@ -571,11 +605,16 @@ static int stm32_fmc2_ebi_set_address_setup(struct stm32_fmc2_ebi *ebi, if (ret) return ret; - regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if (ret) + return ret; + if (prop->reg_type == FMC2_REG_BWTR) - regmap_read(ebi->regmap, FMC2_BWTR(cs), &bxtr); + ret = regmap_read(ebi->regmap, FMC2_BWTR(cs), &bxtr); else - regmap_read(ebi->regmap, FMC2_BTR(cs), &bxtr); + ret = regmap_read(ebi->regmap, FMC2_BTR(cs), &bxtr); + if (ret) + return ret; if ((bxtr & FMC2_BXTR_ACCMOD) == val || bcr & FMC2_BCR_MUXEN) val = clamp_val(setup, 1, FMC2_BXTR_ADDSET_MAX); @@ -693,11 +732,14 @@ static int stm32_fmc2_ebi_set_max_low_pulse(struct stm32_fmc2_ebi *ebi, int cs, u32 setup) { u32 old_val, new_val, pcscntr; + int ret; if (setup < 1) return 0; - regmap_read(ebi->regmap, FMC2_PCSCNTR, &pcscntr); + ret = regmap_read(ebi->regmap, FMC2_PCSCNTR, &pcscntr); + if (ret) + return ret; /* Enable counter for the bank */ regmap_update_bits(ebi->regmap, FMC2_PCSCNTR, @@ -944,17 +986,20 @@ static void stm32_fmc2_ebi_disable_bank(struct stm32_fmc2_ebi *ebi, int cs) regmap_update_bits(ebi->regmap, FMC2_BCR(cs), FMC2_BCR_MBKEN, 0); } -static void stm32_fmc2_ebi_save_setup(struct stm32_fmc2_ebi *ebi) +static int stm32_fmc2_ebi_save_setup(struct stm32_fmc2_ebi *ebi) { unsigned int cs; + int ret; for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) { - regmap_read(ebi->regmap, FMC2_BCR(cs), &ebi->bcr[cs]); - regmap_read(ebi->regmap, FMC2_BTR(cs), &ebi->btr[cs]); - regmap_read(ebi->regmap, FMC2_BWTR(cs), &ebi->bwtr[cs]); + ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &ebi->bcr[cs]); + ret |= regmap_read(ebi->regmap, FMC2_BTR(cs), &ebi->btr[cs]); + ret |= regmap_read(ebi->regmap, FMC2_BWTR(cs), &ebi->bwtr[cs]); + if (ret) + return ret; } - regmap_read(ebi->regmap, FMC2_PCSCNTR, &ebi->pcscntr); + return regmap_read(ebi->regmap, FMC2_PCSCNTR, &ebi->pcscntr); } static void stm32_fmc2_ebi_set_setup(struct stm32_fmc2_ebi *ebi) @@ -983,22 +1028,29 @@ static void stm32_fmc2_ebi_disable_banks(struct stm32_fmc2_ebi *ebi) } /* NWAIT signal can not be connected to EBI controller and NAND controller */ -static bool stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi) +static int stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi) { + struct device *dev = ebi->dev; unsigned int cs; u32 bcr; + int ret; for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) { if (!(ebi->bank_assigned & BIT(cs))) continue; - regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &bcr); + if (ret) + return ret; + if ((bcr & FMC2_BCR_WAITEN || bcr & FMC2_BCR_ASYNCWAIT) && - ebi->bank_assigned & BIT(FMC2_NAND)) - return true; + ebi->bank_assigned & BIT(FMC2_NAND)) { + dev_err(dev, "NWAIT signal connected to EBI and NAND controllers\n"); + return -EINVAL; + } } - return false; + return 0; } static void stm32_fmc2_ebi_enable(struct stm32_fmc2_ebi *ebi) @@ -1085,10 +1137,9 @@ static int stm32_fmc2_ebi_parse_dt(struct stm32_fmc2_ebi *ebi) return -ENODEV; } - if (stm32_fmc2_ebi_nwait_used_by_ctrls(ebi)) { - dev_err(dev, "NWAIT signal connected to EBI and NAND controllers\n"); - return -EINVAL; - } + ret = stm32_fmc2_ebi_nwait_used_by_ctrls(ebi); + if (ret) + return ret; stm32_fmc2_ebi_enable(ebi); @@ -1133,7 +1184,10 @@ static int stm32_fmc2_ebi_probe(struct platform_device *pdev) if (ret) goto err_release; - stm32_fmc2_ebi_save_setup(ebi); + ret = stm32_fmc2_ebi_save_setup(ebi); + if (ret) + goto err_release; + platform_set_drvdata(pdev, ebi); return 0; -- cgit v1.2.3-58-ga151 From 2ff761ff29f6e2d0e616b21af3e054dac1f2c5f4 Mon Sep 17 00:00:00 2001 From: Christophe Kerello Date: Mon, 26 Feb 2024 11:14:26 +0100 Subject: memory: stm32-fmc2-ebi: add MP25 support Add the support of the revision 2 of FMC2 IP. - PCSCNTR register has been removed, - CFGR register has been added, - the bit used to enable the IP has moved from BCR1 to CFGR, - the timeout for CEx deassertion has moved from PCSCNTR to BCRx, - the continuous clock enable has moved from BCR1 to CFGR, - the clk divide ratio has moved from BCR1 to CFGR. The MP1 SoCs have only one signal to manage all the controllers (NWAIT). The MP25 SOC has one RNB signal for the NAND controller and one NWAIT signal for the memory controller. Let's use a platform data structure for parameters that will differ between MP1 and MP25. Signed-off-by: Christophe Kerello Link: https://lore.kernel.org/r/20240226101428.37791-4-christophe.kerello@foss.st.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/stm32-fmc2-ebi.c | 362 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 350 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/stm32-fmc2-ebi.c b/drivers/memory/stm32-fmc2-ebi.c index 81c1cd88fb97..e81ba43b14db 100644 --- a/drivers/memory/stm32-fmc2-ebi.c +++ b/drivers/memory/stm32-fmc2-ebi.c @@ -20,6 +20,7 @@ #define FMC2_BCR(x) ((x) * 0x8 + FMC2_BCR1) #define FMC2_BTR(x) ((x) * 0x8 + FMC2_BTR1) #define FMC2_PCSCNTR 0x20 +#define FMC2_CFGR 0x20 #define FMC2_BWTR1 0x104 #define FMC2_BWTR(x) ((x) * 0x8 + FMC2_BWTR1) @@ -42,6 +43,7 @@ #define FMC2_BCR_ASYNCWAIT BIT(15) #define FMC2_BCR_CPSIZE GENMASK(18, 16) #define FMC2_BCR_CBURSTRW BIT(19) +#define FMC2_BCR_CSCOUNT GENMASK(21, 20) #define FMC2_BCR_NBLSET GENMASK(23, 22) /* Register: FMC2_BTRx/FMC2_BWTRx */ @@ -58,6 +60,11 @@ #define FMC2_PCSCNTR_CSCOUNT GENMASK(15, 0) #define FMC2_PCSCNTR_CNTBEN(x) BIT((x) + 16) +/* Register: FMC2_CFGR */ +#define FMC2_CFGR_CLKDIV GENMASK(19, 16) +#define FMC2_CFGR_CCLKEN BIT(20) +#define FMC2_CFGR_FMC2EN BIT(31) + #define FMC2_MAX_EBI_CE 4 #define FMC2_MAX_BANKS 5 @@ -74,6 +81,11 @@ #define FMC2_BCR_MTYP_PSRAM 0x1 #define FMC2_BCR_MTYP_NOR 0x2 +#define FMC2_BCR_CSCOUNT_0 0x0 +#define FMC2_BCR_CSCOUNT_1 0x1 +#define FMC2_BCR_CSCOUNT_64 0x2 +#define FMC2_BCR_CSCOUNT_256 0x3 + #define FMC2_BXTR_EXTMOD_A 0x0 #define FMC2_BXTR_EXTMOD_B 0x1 #define FMC2_BXTR_EXTMOD_C 0x2 @@ -88,6 +100,7 @@ #define FMC2_BTR_CLKDIV_MAX 0xf #define FMC2_BTR_DATLAT_MAX 0xf #define FMC2_PCSCNTR_CSCOUNT_MAX 0xff +#define FMC2_CFGR_CLKDIV_MAX 0xf enum stm32_fmc2_ebi_bank { FMC2_EBI1 = 0, @@ -101,7 +114,8 @@ enum stm32_fmc2_ebi_register_type { FMC2_REG_BCR = 1, FMC2_REG_BTR, FMC2_REG_BWTR, - FMC2_REG_PCSCNTR + FMC2_REG_PCSCNTR, + FMC2_REG_CFGR }; enum stm32_fmc2_ebi_transaction_type { @@ -132,16 +146,37 @@ enum stm32_fmc2_ebi_cpsize { FMC2_CPSIZE_1024 = 1024 }; +enum stm32_fmc2_ebi_cscount { + FMC2_CSCOUNT_0 = 0, + FMC2_CSCOUNT_1 = 1, + FMC2_CSCOUNT_64 = 64, + FMC2_CSCOUNT_256 = 256 +}; + +struct stm32_fmc2_ebi; + +struct stm32_fmc2_ebi_data { + const struct stm32_fmc2_prop *child_props; + unsigned int nb_child_props; + u32 fmc2_enable_reg; + u32 fmc2_enable_bit; + int (*nwait_used_by_ctrls)(struct stm32_fmc2_ebi *ebi); + void (*set_setup)(struct stm32_fmc2_ebi *ebi); + int (*save_setup)(struct stm32_fmc2_ebi *ebi); +}; + struct stm32_fmc2_ebi { struct device *dev; struct clk *clk; struct regmap *regmap; + const struct stm32_fmc2_ebi_data *data; u8 bank_assigned; u32 bcr[FMC2_MAX_EBI_CE]; u32 btr[FMC2_MAX_EBI_CE]; u32 bwtr[FMC2_MAX_EBI_CE]; u32 pcscntr; + u32 cfgr; }; /* @@ -353,6 +388,30 @@ static u32 stm32_fmc2_ebi_ns_to_clk_period(struct stm32_fmc2_ebi *ebi, return DIV_ROUND_UP(nb_clk_cycles, clk_period); } +static u32 stm32_fmc2_ebi_mp25_ns_to_clk_period(struct stm32_fmc2_ebi *ebi, + int cs, u32 setup) +{ + u32 nb_clk_cycles = stm32_fmc2_ebi_ns_to_clock_cycles(ebi, cs, setup); + u32 cfgr, btr, clk_period; + int ret; + + ret = regmap_read(ebi->regmap, FMC2_CFGR, &cfgr); + if (ret) + return ret; + + if (cfgr & FMC2_CFGR_CCLKEN) { + clk_period = FIELD_GET(FMC2_CFGR_CLKDIV, cfgr) + 1; + } else { + ret = regmap_read(ebi->regmap, FMC2_BTR(cs), &btr); + if (ret) + return ret; + + clk_period = FIELD_GET(FMC2_BTR_CLKDIV, btr) + 1; + } + + return DIV_ROUND_UP(nb_clk_cycles, clk_period); +} + static int stm32_fmc2_ebi_get_reg(int reg_type, int cs, u32 *reg) { switch (reg_type) { @@ -368,6 +427,9 @@ static int stm32_fmc2_ebi_get_reg(int reg_type, int cs, u32 *reg) case FMC2_REG_PCSCNTR: *reg = FMC2_PCSCNTR; break; + case FMC2_REG_CFGR: + *reg = FMC2_CFGR; + break; default: return -EINVAL; } @@ -714,6 +776,30 @@ static int stm32_fmc2_ebi_set_clk_period(struct stm32_fmc2_ebi *ebi, return 0; } +static int stm32_fmc2_ebi_mp25_set_clk_period(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 val, cfgr; + int ret; + + ret = regmap_read(ebi->regmap, FMC2_CFGR, &cfgr); + if (ret) + return ret; + + if (cfgr & FMC2_CFGR_CCLKEN) { + val = setup ? clamp_val(setup - 1, 1, FMC2_CFGR_CLKDIV_MAX) : 1; + val = FIELD_PREP(FMC2_CFGR_CLKDIV, val); + regmap_update_bits(ebi->regmap, FMC2_CFGR, FMC2_CFGR_CLKDIV, val); + } else { + val = setup ? clamp_val(setup - 1, 1, FMC2_BTR_CLKDIV_MAX) : 1; + val = FIELD_PREP(FMC2_BTR_CLKDIV, val); + regmap_update_bits(ebi->regmap, FMC2_BTR(cs), FMC2_BTR_CLKDIV, val); + } + + return 0; +} + static int stm32_fmc2_ebi_set_data_latency(struct stm32_fmc2_ebi *ebi, const struct stm32_fmc2_prop *prop, int cs, u32 setup) @@ -759,6 +845,27 @@ static int stm32_fmc2_ebi_set_max_low_pulse(struct stm32_fmc2_ebi *ebi, return 0; } +static int stm32_fmc2_ebi_mp25_set_max_low_pulse(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs, u32 setup) +{ + u32 val; + + if (setup == FMC2_CSCOUNT_0) + val = FIELD_PREP(FMC2_BCR_CSCOUNT, FMC2_BCR_CSCOUNT_0); + else if (setup == FMC2_CSCOUNT_1) + val = FIELD_PREP(FMC2_BCR_CSCOUNT, FMC2_BCR_CSCOUNT_1); + else if (setup <= FMC2_CSCOUNT_64) + val = FIELD_PREP(FMC2_BCR_CSCOUNT, FMC2_BCR_CSCOUNT_64); + else + val = FIELD_PREP(FMC2_BCR_CSCOUNT, FMC2_BCR_CSCOUNT_256); + + regmap_update_bits(ebi->regmap, FMC2_BCR(cs), + FMC2_BCR_CSCOUNT, val); + + return 0; +} + static const struct stm32_fmc2_prop stm32_fmc2_child_props[] = { /* st,fmc2-ebi-cs-trans-type must be the first property */ { @@ -924,6 +1031,171 @@ static const struct stm32_fmc2_prop stm32_fmc2_child_props[] = { }, }; +static const struct stm32_fmc2_prop stm32_fmc2_mp25_child_props[] = { + /* st,fmc2-ebi-cs-trans-type must be the first property */ + { + .name = "st,fmc2-ebi-cs-transaction-type", + .mprop = true, + .set = stm32_fmc2_ebi_set_trans_type, + }, + { + .name = "st,fmc2-ebi-cs-cclk-enable", + .bprop = true, + .reg_type = FMC2_REG_CFGR, + .reg_mask = FMC2_CFGR_CCLKEN, + .check = stm32_fmc2_ebi_check_sync_trans, + .set = stm32_fmc2_ebi_set_bit_field, + }, + { + .name = "st,fmc2-ebi-cs-mux-enable", + .bprop = true, + .reg_type = FMC2_REG_BCR, + .reg_mask = FMC2_BCR_MUXEN, + .check = stm32_fmc2_ebi_check_mux, + .set = stm32_fmc2_ebi_set_bit_field, + }, + { + .name = "st,fmc2-ebi-cs-buswidth", + .reset_val = FMC2_BUSWIDTH_16, + .set = stm32_fmc2_ebi_set_buswidth, + }, + { + .name = "st,fmc2-ebi-cs-waitpol-high", + .bprop = true, + .reg_type = FMC2_REG_BCR, + .reg_mask = FMC2_BCR_WAITPOL, + .set = stm32_fmc2_ebi_set_bit_field, + }, + { + .name = "st,fmc2-ebi-cs-waitcfg-enable", + .bprop = true, + .reg_type = FMC2_REG_BCR, + .reg_mask = FMC2_BCR_WAITCFG, + .check = stm32_fmc2_ebi_check_waitcfg, + .set = stm32_fmc2_ebi_set_bit_field, + }, + { + .name = "st,fmc2-ebi-cs-wait-enable", + .bprop = true, + .reg_type = FMC2_REG_BCR, + .reg_mask = FMC2_BCR_WAITEN, + .check = stm32_fmc2_ebi_check_sync_trans, + .set = stm32_fmc2_ebi_set_bit_field, + }, + { + .name = "st,fmc2-ebi-cs-asyncwait-enable", + .bprop = true, + .reg_type = FMC2_REG_BCR, + .reg_mask = FMC2_BCR_ASYNCWAIT, + .check = stm32_fmc2_ebi_check_async_trans, + .set = stm32_fmc2_ebi_set_bit_field, + }, + { + .name = "st,fmc2-ebi-cs-cpsize", + .check = stm32_fmc2_ebi_check_cpsize, + .set = stm32_fmc2_ebi_set_cpsize, + }, + { + .name = "st,fmc2-ebi-cs-byte-lane-setup-ns", + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_bl_setup, + }, + { + .name = "st,fmc2-ebi-cs-address-setup-ns", + .reg_type = FMC2_REG_BTR, + .reset_val = FMC2_BXTR_ADDSET_MAX, + .check = stm32_fmc2_ebi_check_async_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_address_setup, + }, + { + .name = "st,fmc2-ebi-cs-address-hold-ns", + .reg_type = FMC2_REG_BTR, + .reset_val = FMC2_BXTR_ADDHLD_MAX, + .check = stm32_fmc2_ebi_check_address_hold, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_address_hold, + }, + { + .name = "st,fmc2-ebi-cs-data-setup-ns", + .reg_type = FMC2_REG_BTR, + .reset_val = FMC2_BXTR_DATAST_MAX, + .check = stm32_fmc2_ebi_check_async_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_data_setup, + }, + { + .name = "st,fmc2-ebi-cs-bus-turnaround-ns", + .reg_type = FMC2_REG_BTR, + .reset_val = FMC2_BXTR_BUSTURN_MAX + 1, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_bus_turnaround, + }, + { + .name = "st,fmc2-ebi-cs-data-hold-ns", + .reg_type = FMC2_REG_BTR, + .check = stm32_fmc2_ebi_check_async_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_data_hold, + }, + { + .name = "st,fmc2-ebi-cs-clk-period-ns", + .reset_val = FMC2_CFGR_CLKDIV_MAX + 1, + .check = stm32_fmc2_ebi_check_sync_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_mp25_set_clk_period, + }, + { + .name = "st,fmc2-ebi-cs-data-latency-ns", + .check = stm32_fmc2_ebi_check_sync_trans, + .calculate = stm32_fmc2_ebi_mp25_ns_to_clk_period, + .set = stm32_fmc2_ebi_set_data_latency, + }, + { + .name = "st,fmc2-ebi-cs-write-address-setup-ns", + .reg_type = FMC2_REG_BWTR, + .reset_val = FMC2_BXTR_ADDSET_MAX, + .check = stm32_fmc2_ebi_check_async_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_address_setup, + }, + { + .name = "st,fmc2-ebi-cs-write-address-hold-ns", + .reg_type = FMC2_REG_BWTR, + .reset_val = FMC2_BXTR_ADDHLD_MAX, + .check = stm32_fmc2_ebi_check_address_hold, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_address_hold, + }, + { + .name = "st,fmc2-ebi-cs-write-data-setup-ns", + .reg_type = FMC2_REG_BWTR, + .reset_val = FMC2_BXTR_DATAST_MAX, + .check = stm32_fmc2_ebi_check_async_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_data_setup, + }, + { + .name = "st,fmc2-ebi-cs-write-bus-turnaround-ns", + .reg_type = FMC2_REG_BWTR, + .reset_val = FMC2_BXTR_BUSTURN_MAX + 1, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_bus_turnaround, + }, + { + .name = "st,fmc2-ebi-cs-write-data-hold-ns", + .reg_type = FMC2_REG_BWTR, + .check = stm32_fmc2_ebi_check_async_trans, + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_set_data_hold, + }, + { + .name = "st,fmc2-ebi-cs-max-low-pulse-ns", + .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, + .set = stm32_fmc2_ebi_mp25_set_max_low_pulse, + }, +}; + static int stm32_fmc2_ebi_parse_prop(struct stm32_fmc2_ebi *ebi, struct device_node *dev_node, const struct stm32_fmc2_prop *prop, @@ -999,9 +1271,31 @@ static int stm32_fmc2_ebi_save_setup(struct stm32_fmc2_ebi *ebi) return ret; } + return 0; +} + +static int stm32_fmc2_ebi_mp1_save_setup(struct stm32_fmc2_ebi *ebi) +{ + int ret; + + ret = stm32_fmc2_ebi_save_setup(ebi); + if (ret) + return ret; + return regmap_read(ebi->regmap, FMC2_PCSCNTR, &ebi->pcscntr); } +static int stm32_fmc2_ebi_mp25_save_setup(struct stm32_fmc2_ebi *ebi) +{ + int ret; + + ret = stm32_fmc2_ebi_save_setup(ebi); + if (ret) + return ret; + + return regmap_read(ebi->regmap, FMC2_CFGR, &ebi->cfgr); +} + static void stm32_fmc2_ebi_set_setup(struct stm32_fmc2_ebi *ebi) { unsigned int cs; @@ -1011,10 +1305,20 @@ static void stm32_fmc2_ebi_set_setup(struct stm32_fmc2_ebi *ebi) regmap_write(ebi->regmap, FMC2_BTR(cs), ebi->btr[cs]); regmap_write(ebi->regmap, FMC2_BWTR(cs), ebi->bwtr[cs]); } +} +static void stm32_fmc2_ebi_mp1_set_setup(struct stm32_fmc2_ebi *ebi) +{ + stm32_fmc2_ebi_set_setup(ebi); regmap_write(ebi->regmap, FMC2_PCSCNTR, ebi->pcscntr); } +static void stm32_fmc2_ebi_mp25_set_setup(struct stm32_fmc2_ebi *ebi) +{ + stm32_fmc2_ebi_set_setup(ebi); + regmap_write(ebi->regmap, FMC2_CFGR, ebi->cfgr); +} + static void stm32_fmc2_ebi_disable_banks(struct stm32_fmc2_ebi *ebi) { unsigned int cs; @@ -1055,13 +1359,15 @@ static int stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi) static void stm32_fmc2_ebi_enable(struct stm32_fmc2_ebi *ebi) { - regmap_update_bits(ebi->regmap, FMC2_BCR1, - FMC2_BCR1_FMC2EN, FMC2_BCR1_FMC2EN); + regmap_update_bits(ebi->regmap, ebi->data->fmc2_enable_reg, + ebi->data->fmc2_enable_bit, + ebi->data->fmc2_enable_bit); } static void stm32_fmc2_ebi_disable(struct stm32_fmc2_ebi *ebi) { - regmap_update_bits(ebi->regmap, FMC2_BCR1, FMC2_BCR1_FMC2EN, 0); + regmap_update_bits(ebi->regmap, ebi->data->fmc2_enable_reg, + ebi->data->fmc2_enable_bit, 0); } static int stm32_fmc2_ebi_setup_cs(struct stm32_fmc2_ebi *ebi, @@ -1073,8 +1379,8 @@ static int stm32_fmc2_ebi_setup_cs(struct stm32_fmc2_ebi *ebi, stm32_fmc2_ebi_disable_bank(ebi, cs); - for (i = 0; i < ARRAY_SIZE(stm32_fmc2_child_props); i++) { - const struct stm32_fmc2_prop *p = &stm32_fmc2_child_props[i]; + for (i = 0; i < ebi->data->nb_child_props; i++) { + const struct stm32_fmc2_prop *p = &ebi->data->child_props[i]; ret = stm32_fmc2_ebi_parse_prop(ebi, dev_node, p, cs); if (ret) { @@ -1137,9 +1443,11 @@ static int stm32_fmc2_ebi_parse_dt(struct stm32_fmc2_ebi *ebi) return -ENODEV; } - ret = stm32_fmc2_ebi_nwait_used_by_ctrls(ebi); - if (ret) - return ret; + if (ebi->data->nwait_used_by_ctrls) { + ret = ebi->data->nwait_used_by_ctrls(ebi); + if (ret) + return ret; + } stm32_fmc2_ebi_enable(ebi); @@ -1159,6 +1467,10 @@ static int stm32_fmc2_ebi_probe(struct platform_device *pdev) ebi->dev = dev; + ebi->data = of_device_get_match_data(dev); + if (!ebi->data) + return -EINVAL; + ebi->regmap = device_node_to_regmap(dev->of_node); if (IS_ERR(ebi->regmap)) return PTR_ERR(ebi->regmap); @@ -1184,7 +1496,7 @@ static int stm32_fmc2_ebi_probe(struct platform_device *pdev) if (ret) goto err_release; - ret = stm32_fmc2_ebi_save_setup(ebi); + ret = ebi->data->save_setup(ebi); if (ret) goto err_release; @@ -1232,7 +1544,7 @@ static int __maybe_unused stm32_fmc2_ebi_resume(struct device *dev) if (ret) return ret; - stm32_fmc2_ebi_set_setup(ebi); + ebi->data->set_setup(ebi); stm32_fmc2_ebi_enable(ebi); return 0; @@ -1241,8 +1553,34 @@ static int __maybe_unused stm32_fmc2_ebi_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(stm32_fmc2_ebi_pm_ops, stm32_fmc2_ebi_suspend, stm32_fmc2_ebi_resume); +static const struct stm32_fmc2_ebi_data stm32_fmc2_ebi_mp1_data = { + .child_props = stm32_fmc2_child_props, + .nb_child_props = ARRAY_SIZE(stm32_fmc2_child_props), + .fmc2_enable_reg = FMC2_BCR1, + .fmc2_enable_bit = FMC2_BCR1_FMC2EN, + .nwait_used_by_ctrls = stm32_fmc2_ebi_nwait_used_by_ctrls, + .set_setup = stm32_fmc2_ebi_mp1_set_setup, + .save_setup = stm32_fmc2_ebi_mp1_save_setup, +}; + +static const struct stm32_fmc2_ebi_data stm32_fmc2_ebi_mp25_data = { + .child_props = stm32_fmc2_mp25_child_props, + .nb_child_props = ARRAY_SIZE(stm32_fmc2_mp25_child_props), + .fmc2_enable_reg = FMC2_CFGR, + .fmc2_enable_bit = FMC2_CFGR_FMC2EN, + .set_setup = stm32_fmc2_ebi_mp25_set_setup, + .save_setup = stm32_fmc2_ebi_mp25_save_setup, +}; + static const struct of_device_id stm32_fmc2_ebi_match[] = { - {.compatible = "st,stm32mp1-fmc2-ebi"}, + { + .compatible = "st,stm32mp1-fmc2-ebi", + .data = &stm32_fmc2_ebi_mp1_data, + }, + { + .compatible = "st,stm32mp25-fmc2-ebi", + .data = &stm32_fmc2_ebi_mp25_data, + }, {} }; MODULE_DEVICE_TABLE(of, stm32_fmc2_ebi_match); -- cgit v1.2.3-58-ga151 From cc7d5cf8021983a736f9d963dda2dd45de02b395 Mon Sep 17 00:00:00 2001 From: Christophe Kerello Date: Mon, 26 Feb 2024 11:14:27 +0100 Subject: memory: stm32-fmc2-ebi: add MP25 RIF support The FMC2 revision 2 supports security and isolation compliant with the Resource Isolation Framework (RIF). From RIF point of view, the FMC2 is composed of several independent resources, listed below, which can be assigned to different security and compartment domains: - 0: Common FMC_CFGR register. - 1: EBI controller for Chip Select 1. - 2: EBI controller for Chip Select 2. - 3: EBI controller for Chip Select 3. - 4: EBI controller for Chip Select 4. - 5: NAND controller. Signed-off-by: Christophe Kerello Link: https://lore.kernel.org/r/20240226101428.37791-5-christophe.kerello@foss.st.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/stm32-fmc2-ebi.c | 224 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 220 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/stm32-fmc2-ebi.c b/drivers/memory/stm32-fmc2-ebi.c index e81ba43b14db..45c658af613d 100644 --- a/drivers/memory/stm32-fmc2-ebi.c +++ b/drivers/memory/stm32-fmc2-ebi.c @@ -21,8 +21,14 @@ #define FMC2_BTR(x) ((x) * 0x8 + FMC2_BTR1) #define FMC2_PCSCNTR 0x20 #define FMC2_CFGR 0x20 +#define FMC2_SR 0x84 #define FMC2_BWTR1 0x104 #define FMC2_BWTR(x) ((x) * 0x8 + FMC2_BWTR1) +#define FMC2_SECCFGR 0x300 +#define FMC2_CIDCFGR0 0x30c +#define FMC2_CIDCFGR(x) ((x) * 0x8 + FMC2_CIDCFGR0) +#define FMC2_SEMCR0 0x310 +#define FMC2_SEMCR(x) ((x) * 0x8 + FMC2_SEMCR0) /* Register: FMC2_BCR1 */ #define FMC2_BCR1_CCLKEN BIT(20) @@ -65,8 +71,23 @@ #define FMC2_CFGR_CCLKEN BIT(20) #define FMC2_CFGR_FMC2EN BIT(31) +/* Register: FMC2_SR */ +#define FMC2_SR_ISOST GENMASK(1, 0) + +/* Register: FMC2_CIDCFGR */ +#define FMC2_CIDCFGR_CFEN BIT(0) +#define FMC2_CIDCFGR_SEMEN BIT(1) +#define FMC2_CIDCFGR_SCID GENMASK(6, 4) +#define FMC2_CIDCFGR_SEMWLC1 BIT(17) + +/* Register: FMC2_SEMCR */ +#define FMC2_SEMCR_SEM_MUTEX BIT(0) +#define FMC2_SEMCR_SEMCID GENMASK(6, 4) + #define FMC2_MAX_EBI_CE 4 #define FMC2_MAX_BANKS 5 +#define FMC2_MAX_RESOURCES 6 +#define FMC2_CID1 1 #define FMC2_BCR_CPSIZE_0 0x0 #define FMC2_BCR_CPSIZE_128 0x1 @@ -163,6 +184,9 @@ struct stm32_fmc2_ebi_data { int (*nwait_used_by_ctrls)(struct stm32_fmc2_ebi *ebi); void (*set_setup)(struct stm32_fmc2_ebi *ebi); int (*save_setup)(struct stm32_fmc2_ebi *ebi); + int (*check_rif)(struct stm32_fmc2_ebi *ebi, u32 resource); + void (*put_sems)(struct stm32_fmc2_ebi *ebi); + void (*get_sems)(struct stm32_fmc2_ebi *ebi); }; struct stm32_fmc2_ebi { @@ -171,6 +195,8 @@ struct stm32_fmc2_ebi { struct regmap *regmap; const struct stm32_fmc2_ebi_data *data; u8 bank_assigned; + u8 sem_taken; + bool access_granted; u32 bcr[FMC2_MAX_EBI_CE]; u32 btr[FMC2_MAX_EBI_CE]; @@ -262,6 +288,33 @@ static int stm32_fmc2_ebi_check_sync_trans(struct stm32_fmc2_ebi *ebi, return -EINVAL; } +static int stm32_fmc2_ebi_mp25_check_cclk(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs) +{ + if (!ebi->access_granted) + return -EACCES; + + return stm32_fmc2_ebi_check_sync_trans(ebi, prop, cs); +} + +static int stm32_fmc2_ebi_mp25_check_clk_period(struct stm32_fmc2_ebi *ebi, + const struct stm32_fmc2_prop *prop, + int cs) +{ + u32 cfgr; + int ret; + + ret = regmap_read(ebi->regmap, FMC2_CFGR, &cfgr); + if (ret) + return ret; + + if (cfgr & FMC2_CFGR_CCLKEN && !ebi->access_granted) + return -EACCES; + + return stm32_fmc2_ebi_check_sync_trans(ebi, prop, cs); +} + static int stm32_fmc2_ebi_check_async_trans(struct stm32_fmc2_ebi *ebi, const struct stm32_fmc2_prop *prop, int cs) @@ -1043,7 +1096,7 @@ static const struct stm32_fmc2_prop stm32_fmc2_mp25_child_props[] = { .bprop = true, .reg_type = FMC2_REG_CFGR, .reg_mask = FMC2_CFGR_CCLKEN, - .check = stm32_fmc2_ebi_check_sync_trans, + .check = stm32_fmc2_ebi_mp25_check_cclk, .set = stm32_fmc2_ebi_set_bit_field, }, { @@ -1141,7 +1194,7 @@ static const struct stm32_fmc2_prop stm32_fmc2_mp25_child_props[] = { { .name = "st,fmc2-ebi-cs-clk-period-ns", .reset_val = FMC2_CFGR_CLKDIV_MAX + 1, - .check = stm32_fmc2_ebi_check_sync_trans, + .check = stm32_fmc2_ebi_mp25_check_clk_period, .calculate = stm32_fmc2_ebi_ns_to_clock_cycles, .set = stm32_fmc2_ebi_mp25_set_clk_period, }, @@ -1196,6 +1249,110 @@ static const struct stm32_fmc2_prop stm32_fmc2_mp25_child_props[] = { }, }; +static int stm32_fmc2_ebi_mp25_check_rif(struct stm32_fmc2_ebi *ebi, u32 resource) +{ + u32 seccfgr, cidcfgr, semcr; + int cid, ret; + + if (resource >= FMC2_MAX_RESOURCES) + return -EINVAL; + + ret = regmap_read(ebi->regmap, FMC2_SECCFGR, &seccfgr); + if (ret) + return ret; + + if (seccfgr & BIT(resource)) { + if (resource) + dev_err(ebi->dev, "resource %d is configured as secure\n", + resource); + + return -EACCES; + } + + ret = regmap_read(ebi->regmap, FMC2_CIDCFGR(resource), &cidcfgr); + if (ret) + return ret; + + if (!(cidcfgr & FMC2_CIDCFGR_CFEN)) + /* CID filtering is turned off: access granted */ + return 0; + + if (!(cidcfgr & FMC2_CIDCFGR_SEMEN)) { + /* Static CID mode */ + cid = FIELD_GET(FMC2_CIDCFGR_SCID, cidcfgr); + if (cid != FMC2_CID1) { + if (resource) + dev_err(ebi->dev, "static CID%d set for resource %d\n", + cid, resource); + + return -EACCES; + } + + return 0; + } + + /* Pass-list with semaphore mode */ + if (!(cidcfgr & FMC2_CIDCFGR_SEMWLC1)) { + if (resource) + dev_err(ebi->dev, "CID1 is block-listed for resource %d\n", + resource); + + return -EACCES; + } + + ret = regmap_read(ebi->regmap, FMC2_SEMCR(resource), &semcr); + if (ret) + return ret; + + if (!(semcr & FMC2_SEMCR_SEM_MUTEX)) { + regmap_update_bits(ebi->regmap, FMC2_SEMCR(resource), + FMC2_SEMCR_SEM_MUTEX, FMC2_SEMCR_SEM_MUTEX); + + ret = regmap_read(ebi->regmap, FMC2_SEMCR(resource), &semcr); + if (ret) + return ret; + } + + cid = FIELD_GET(FMC2_SEMCR_SEMCID, semcr); + if (cid != FMC2_CID1) { + if (resource) + dev_err(ebi->dev, "resource %d is already used by CID%d\n", + resource, cid); + + return -EACCES; + } + + ebi->sem_taken |= BIT(resource); + + return 0; +} + +static void stm32_fmc2_ebi_mp25_put_sems(struct stm32_fmc2_ebi *ebi) +{ + unsigned int resource; + + for (resource = 0; resource < FMC2_MAX_RESOURCES; resource++) { + if (!(ebi->sem_taken & BIT(resource))) + continue; + + regmap_update_bits(ebi->regmap, FMC2_SEMCR(resource), + FMC2_SEMCR_SEM_MUTEX, 0); + } +} + +static void stm32_fmc2_ebi_mp25_get_sems(struct stm32_fmc2_ebi *ebi) +{ + unsigned int resource; + + for (resource = 0; resource < FMC2_MAX_RESOURCES; resource++) { + if (!(ebi->sem_taken & BIT(resource))) + continue; + + regmap_update_bits(ebi->regmap, FMC2_SEMCR(resource), + FMC2_SEMCR_SEM_MUTEX, FMC2_SEMCR_SEM_MUTEX); + } +} + static int stm32_fmc2_ebi_parse_prop(struct stm32_fmc2_ebi *ebi, struct device_node *dev_node, const struct stm32_fmc2_prop *prop, @@ -1264,6 +1421,9 @@ static int stm32_fmc2_ebi_save_setup(struct stm32_fmc2_ebi *ebi) int ret; for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) { + if (!(ebi->bank_assigned & BIT(cs))) + continue; + ret = regmap_read(ebi->regmap, FMC2_BCR(cs), &ebi->bcr[cs]); ret |= regmap_read(ebi->regmap, FMC2_BTR(cs), &ebi->btr[cs]); ret |= regmap_read(ebi->regmap, FMC2_BWTR(cs), &ebi->bwtr[cs]); @@ -1293,7 +1453,10 @@ static int stm32_fmc2_ebi_mp25_save_setup(struct stm32_fmc2_ebi *ebi) if (ret) return ret; - return regmap_read(ebi->regmap, FMC2_CFGR, &ebi->cfgr); + if (ebi->access_granted) + ret = regmap_read(ebi->regmap, FMC2_CFGR, &ebi->cfgr); + + return ret; } static void stm32_fmc2_ebi_set_setup(struct stm32_fmc2_ebi *ebi) @@ -1301,6 +1464,9 @@ static void stm32_fmc2_ebi_set_setup(struct stm32_fmc2_ebi *ebi) unsigned int cs; for (cs = 0; cs < FMC2_MAX_EBI_CE; cs++) { + if (!(ebi->bank_assigned & BIT(cs))) + continue; + regmap_write(ebi->regmap, FMC2_BCR(cs), ebi->bcr[cs]); regmap_write(ebi->regmap, FMC2_BTR(cs), ebi->btr[cs]); regmap_write(ebi->regmap, FMC2_BWTR(cs), ebi->bwtr[cs]); @@ -1316,7 +1482,9 @@ static void stm32_fmc2_ebi_mp1_set_setup(struct stm32_fmc2_ebi *ebi) static void stm32_fmc2_ebi_mp25_set_setup(struct stm32_fmc2_ebi *ebi) { stm32_fmc2_ebi_set_setup(ebi); - regmap_write(ebi->regmap, FMC2_CFGR, ebi->cfgr); + + if (ebi->access_granted) + regmap_write(ebi->regmap, FMC2_CFGR, ebi->cfgr); } static void stm32_fmc2_ebi_disable_banks(struct stm32_fmc2_ebi *ebi) @@ -1359,6 +1527,9 @@ static int stm32_fmc2_ebi_nwait_used_by_ctrls(struct stm32_fmc2_ebi *ebi) static void stm32_fmc2_ebi_enable(struct stm32_fmc2_ebi *ebi) { + if (!ebi->access_granted) + return; + regmap_update_bits(ebi->regmap, ebi->data->fmc2_enable_reg, ebi->data->fmc2_enable_bit, ebi->data->fmc2_enable_bit); @@ -1366,6 +1537,9 @@ static void stm32_fmc2_ebi_enable(struct stm32_fmc2_ebi *ebi) static void stm32_fmc2_ebi_disable(struct stm32_fmc2_ebi *ebi) { + if (!ebi->access_granted) + return; + regmap_update_bits(ebi->regmap, ebi->data->fmc2_enable_reg, ebi->data->fmc2_enable_bit, 0); } @@ -1424,6 +1598,15 @@ static int stm32_fmc2_ebi_parse_dt(struct stm32_fmc2_ebi *ebi) return -EINVAL; } + if (ebi->data->check_rif) { + ret = ebi->data->check_rif(ebi, bank + 1); + if (ret) { + dev_err(dev, "bank access failed: %d\n", bank); + of_node_put(child); + return ret; + } + } + if (bank < FMC2_MAX_EBI_CE) { ret = stm32_fmc2_ebi_setup_cs(ebi, child, bank); if (ret) { @@ -1492,6 +1675,28 @@ static int stm32_fmc2_ebi_probe(struct platform_device *pdev) reset_control_deassert(rstc); } + /* Check if CFGR register can be modified */ + ebi->access_granted = true; + if (ebi->data->check_rif) { + ret = ebi->data->check_rif(ebi, 0); + if (ret) { + u32 sr; + + ebi->access_granted = false; + + ret = regmap_read(ebi->regmap, FMC2_SR, &sr); + if (ret) + goto err_release; + + /* In case of CFGR is secure, just check that the FMC2 is enabled */ + if (sr & FMC2_SR_ISOST) { + dev_err(dev, "FMC2 is not ready to be used.\n"); + ret = -EACCES; + goto err_release; + } + } + } + ret = stm32_fmc2_ebi_parse_dt(ebi); if (ret) goto err_release; @@ -1507,6 +1712,8 @@ static int stm32_fmc2_ebi_probe(struct platform_device *pdev) err_release: stm32_fmc2_ebi_disable_banks(ebi); stm32_fmc2_ebi_disable(ebi); + if (ebi->data->put_sems) + ebi->data->put_sems(ebi); clk_disable_unprepare(ebi->clk); return ret; @@ -1519,6 +1726,8 @@ static void stm32_fmc2_ebi_remove(struct platform_device *pdev) of_platform_depopulate(&pdev->dev); stm32_fmc2_ebi_disable_banks(ebi); stm32_fmc2_ebi_disable(ebi); + if (ebi->data->put_sems) + ebi->data->put_sems(ebi); clk_disable_unprepare(ebi->clk); } @@ -1527,6 +1736,8 @@ static int __maybe_unused stm32_fmc2_ebi_suspend(struct device *dev) struct stm32_fmc2_ebi *ebi = dev_get_drvdata(dev); stm32_fmc2_ebi_disable(ebi); + if (ebi->data->put_sems) + ebi->data->put_sems(ebi); clk_disable_unprepare(ebi->clk); pinctrl_pm_select_sleep_state(dev); @@ -1544,6 +1755,8 @@ static int __maybe_unused stm32_fmc2_ebi_resume(struct device *dev) if (ret) return ret; + if (ebi->data->get_sems) + ebi->data->get_sems(ebi); ebi->data->set_setup(ebi); stm32_fmc2_ebi_enable(ebi); @@ -1570,6 +1783,9 @@ static const struct stm32_fmc2_ebi_data stm32_fmc2_ebi_mp25_data = { .fmc2_enable_bit = FMC2_CFGR_FMC2EN, .set_setup = stm32_fmc2_ebi_mp25_set_setup, .save_setup = stm32_fmc2_ebi_mp25_save_setup, + .check_rif = stm32_fmc2_ebi_mp25_check_rif, + .put_sems = stm32_fmc2_ebi_mp25_put_sems, + .get_sems = stm32_fmc2_ebi_mp25_get_sems, }; static const struct of_device_id stm32_fmc2_ebi_match[] = { -- cgit v1.2.3-58-ga151 From e46076906722ee6f9e7fd5abad7f909cd11a26af Mon Sep 17 00:00:00 2001 From: Christophe Kerello Date: Mon, 26 Feb 2024 11:14:28 +0100 Subject: memory: stm32-fmc2-ebi: keep power domain on MP25 FMC2 domain has to be kept on. To handle it throw PSCI OS-initiated, basic PM for keeping domain on is introduced. Signed-off-by: Christophe Kerello Link: https://lore.kernel.org/r/20240226101428.37791-6-christophe.kerello@foss.st.com Signed-off-by: Krzysztof Kozlowski --- drivers/memory/stm32-fmc2-ebi.c | 41 ++++++++++++++++++++++++++++++++--------- 1 file changed, 32 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/stm32-fmc2-ebi.c b/drivers/memory/stm32-fmc2-ebi.c index 45c658af613d..1c63eeacd071 100644 --- a/drivers/memory/stm32-fmc2-ebi.c +++ b/drivers/memory/stm32-fmc2-ebi.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -1649,6 +1650,7 @@ static int stm32_fmc2_ebi_probe(struct platform_device *pdev) return -ENOMEM; ebi->dev = dev; + platform_set_drvdata(pdev, ebi); ebi->data = of_device_get_match_data(dev); if (!ebi->data) @@ -1666,10 +1668,14 @@ static int stm32_fmc2_ebi_probe(struct platform_device *pdev) if (PTR_ERR(rstc) == -EPROBE_DEFER) return -EPROBE_DEFER; - ret = clk_prepare_enable(ebi->clk); + ret = devm_pm_runtime_enable(dev); if (ret) return ret; + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) + return ret; + if (!IS_ERR(rstc)) { reset_control_assert(rstc); reset_control_deassert(rstc); @@ -1705,8 +1711,6 @@ static int stm32_fmc2_ebi_probe(struct platform_device *pdev) if (ret) goto err_release; - platform_set_drvdata(pdev, ebi); - return 0; err_release: @@ -1714,7 +1718,7 @@ err_release: stm32_fmc2_ebi_disable(ebi); if (ebi->data->put_sems) ebi->data->put_sems(ebi); - clk_disable_unprepare(ebi->clk); + pm_runtime_put_sync_suspend(dev); return ret; } @@ -1728,7 +1732,23 @@ static void stm32_fmc2_ebi_remove(struct platform_device *pdev) stm32_fmc2_ebi_disable(ebi); if (ebi->data->put_sems) ebi->data->put_sems(ebi); + pm_runtime_put_sync_suspend(&pdev->dev); +} + +static int __maybe_unused stm32_fmc2_ebi_runtime_suspend(struct device *dev) +{ + struct stm32_fmc2_ebi *ebi = dev_get_drvdata(dev); + clk_disable_unprepare(ebi->clk); + + return 0; +} + +static int __maybe_unused stm32_fmc2_ebi_runtime_resume(struct device *dev) +{ + struct stm32_fmc2_ebi *ebi = dev_get_drvdata(dev); + + return clk_prepare_enable(ebi->clk); } static int __maybe_unused stm32_fmc2_ebi_suspend(struct device *dev) @@ -1738,7 +1758,7 @@ static int __maybe_unused stm32_fmc2_ebi_suspend(struct device *dev) stm32_fmc2_ebi_disable(ebi); if (ebi->data->put_sems) ebi->data->put_sems(ebi); - clk_disable_unprepare(ebi->clk); + pm_runtime_put_sync_suspend(dev); pinctrl_pm_select_sleep_state(dev); return 0; @@ -1751,8 +1771,8 @@ static int __maybe_unused stm32_fmc2_ebi_resume(struct device *dev) pinctrl_pm_select_default_state(dev); - ret = clk_prepare_enable(ebi->clk); - if (ret) + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) return ret; if (ebi->data->get_sems) @@ -1763,8 +1783,11 @@ static int __maybe_unused stm32_fmc2_ebi_resume(struct device *dev) return 0; } -static SIMPLE_DEV_PM_OPS(stm32_fmc2_ebi_pm_ops, stm32_fmc2_ebi_suspend, - stm32_fmc2_ebi_resume); +static const struct dev_pm_ops stm32_fmc2_ebi_pm_ops = { + SET_RUNTIME_PM_OPS(stm32_fmc2_ebi_runtime_suspend, + stm32_fmc2_ebi_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(stm32_fmc2_ebi_suspend, stm32_fmc2_ebi_resume) +}; static const struct stm32_fmc2_ebi_data stm32_fmc2_ebi_mp1_data = { .child_props = stm32_fmc2_child_props, -- cgit v1.2.3-58-ga151 From 097948afa1c2220609683106c884a3c710e62c80 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Mon, 19 Feb 2024 09:10:00 -0300 Subject: bus: ti-sysc: constify the struct device_type usage Since commit aed65af1cc2f ("drivers: make device_type const"), the driver core can properly handle constant struct device_type. Move the sysc_device_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Message-ID: <20240219-device_cleanup-ti-sysc-v1-1-13b53177d0a5@marliere.net> Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 245e5e827d0d..41d33f39efe5 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -2400,7 +2400,7 @@ static int sysc_child_add_clocks(struct sysc *ddata, return 0; } -static struct device_type sysc_device_type = { +static const struct device_type sysc_device_type = { }; static struct sysc *sysc_child_to_parent(struct device *dev) -- cgit v1.2.3-58-ga151 From 26a526c25606495e7442feeea53061f81eca22be Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 21 Feb 2024 16:44:51 +0100 Subject: soc: qcom: spm: fix building with CONFIG_REGULATOR=n The newly added code causes a build failure when -Werror is set: drivers/soc/qcom/spm.c:388:12: error: 'spm_get_cpu' defined but not used [-Werror=unused-function] Remove the #ifdef and instead use an IS_ENABLED() check that lets the compiler perform dead code elimination instead of the preprocessor. Fixes: 6496dba142f4 ("soc: qcom: spm: add support for voltage regulator") Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20240221154457.2007420-1-arnd@kernel.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/spm.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/spm.c b/drivers/soc/qcom/spm.c index 5eefaec72a13..06e2c4c2a4a8 100644 --- a/drivers/soc/qcom/spm.c +++ b/drivers/soc/qcom/spm.c @@ -411,7 +411,6 @@ static int spm_get_cpu(struct device *dev) return -EOPNOTSUPP; } -#ifdef CONFIG_REGULATOR static int spm_register_regulator(struct device *dev, struct spm_driver_data *drv) { struct regulator_config config = { @@ -474,12 +473,6 @@ static int spm_register_regulator(struct device *dev, struct spm_driver_data *dr return 0; } -#else -static int spm_register_regulator(struct device *dev, struct spm_driver_data *drv) -{ - return 0; -} -#endif static const struct of_device_id spm_match_table[] = { { .compatible = "qcom,sdm660-gold-saw2-v4.1-l2", @@ -559,7 +552,10 @@ static int spm_dev_probe(struct platform_device *pdev) if (drv->reg_data->reg_offset[SPM_REG_SPM_CTL]) spm_set_low_power_mode(drv, PM_SLEEP_MODE_STBY); - return spm_register_regulator(&pdev->dev, drv); + if (IS_ENABLED(CONFIG_REGULATOR)) + return spm_register_regulator(&pdev->dev, drv); + + return 0; } static struct platform_driver spm_driver = { -- cgit v1.2.3-58-ga151 From 87edd944ff568fe923530b3b15fe96380d4442e3 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 25 Feb 2024 21:25:44 +0100 Subject: soc: qcom: geni-se: drop unused kerneldoc struct geni_wrapper param Drop description of non-existing 'struct geni_wrapper' member: qcom-geni-se.c:99: warning: Excess struct member 'to_core' description in 'geni_wrapper' Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20240225202545.59113-1-krzysztof.kozlowski@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom-geni-se.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c index bdcf44b85b2f..2e8f24d5da80 100644 --- a/drivers/soc/qcom/qcom-geni-se.c +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -89,7 +89,6 @@ * @base: Base address of this instance of QUP wrapper core * @clks: Handle to the primary & optional secondary AHB clocks * @num_clks: Count of clocks - * @to_core: Core ICC path */ struct geni_wrapper { struct device *dev; -- cgit v1.2.3-58-ga151 From 9f378a62164cfe218b5a355696464d93b132edfb Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 25 Feb 2024 21:25:45 +0100 Subject: soc: qcom: aoss: add missing kerneldoc for qmp members Add missing kerneldoc to silence: qcom_aoss.c:93: warning: Function parameter or struct member 'debugfs_root' not described in 'qmp' qcom_aoss.c:93: warning: Function parameter or struct member 'debugfs_files' not described in 'qmp' Fixes: d51d984c5525 ("soc: qcom: aoss: Add debugfs interface for sending messages") Signed-off-by: Krzysztof Kozlowski Reviewed-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20240225202545.59113-2-krzysztof.kozlowski@linaro.org Signed-off-by: Bjorn Andersson --- drivers/soc/qcom/qcom_aoss.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index e8ae8aa6391f..ca2f6b7629ce 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -71,6 +71,8 @@ struct qmp_cooling_device { * @tx_lock: provides synchronization between multiple callers of qmp_send() * @qdss_clk: QDSS clock hw struct * @cooling_devs: thermal cooling devices + * @debugfs_root: directory for the developer/tester interface + * @debugfs_files: array of individual debugfs entries under debugfs_root */ struct qmp { void __iomem *msgram; -- cgit v1.2.3-58-ga151 From 469f6acd9a538ea963e2d4d13ba721a7ad1c1813 Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Tue, 13 Feb 2024 11:46:25 -0300 Subject: tee: make tee_bus_type const Since commit d492cc2573a0 ("driver core: device.h: make struct bus_type a const *"), the driver core can properly handle constant struct bus_type, move the tee_bus_type variable to be a constant structure as well, placing it into read-only memory which can not be modified at runtime. Cc: Greg Kroah-Hartman Suggested-by: Greg Kroah-Hartman Signed-off-by: Ricardo B. Marliere Reviewed-by: Sumit Garg Reviewed-by: Greg Kroah-Hartman Signed-off-by: Jens Wiklander Signed-off-by: Arnd Bergmann --- drivers/tee/tee_core.c | 2 +- include/linux/tee_drv.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/tee/tee_core.c b/drivers/tee/tee_core.c index 792d6fae4354..e59c20d74b36 100644 --- a/drivers/tee/tee_core.c +++ b/drivers/tee/tee_core.c @@ -1226,7 +1226,7 @@ static int tee_client_device_uevent(const struct device *dev, return add_uevent_var(env, "MODALIAS=tee:%pUb", dev_id); } -struct bus_type tee_bus_type = { +const struct bus_type tee_bus_type = { .name = "tee", .match = tee_client_device_match, .uevent = tee_client_device_uevent, diff --git a/include/linux/tee_drv.h b/include/linux/tee_drv.h index 911ddf92dcee..71632e3c5f18 100644 --- a/include/linux/tee_drv.h +++ b/include/linux/tee_drv.h @@ -482,7 +482,7 @@ static inline bool tee_param_is_memref(struct tee_param *param) } } -extern struct bus_type tee_bus_type; +extern const struct bus_type tee_bus_type; /** * struct tee_client_device - tee based device -- cgit v1.2.3-58-ga151