Patch Detail
get:
Show a patch.
patch:
Update a patch.
put:
Update a patch.
GET /api/patches/2220050/?format=api
{ "id": 2220050, "url": "http://patchwork.ozlabs.org/api/patches/2220050/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-tegra/patch/20260406-t210-actmon-p2-v7-1-91adf535cf8f@gmail.com/", "project": { "id": 21, "url": "http://patchwork.ozlabs.org/api/projects/21/?format=api", "name": "Linux Tegra Development", "link_name": "linux-tegra", "list_id": "linux-tegra.vger.kernel.org", "list_email": "linux-tegra@vger.kernel.org", "web_url": null, "scm_url": null, "webscm_url": null, "list_archive_url": "", "list_archive_url_format": "", "commit_url_format": "" }, "msgid": "<20260406-t210-actmon-p2-v7-1-91adf535cf8f@gmail.com>", "list_archive_url": null, "date": "2026-04-06T06:36:20", "name": "[v7] memory: tegra210: Support interconnect framework", "commit_ref": null, "pull_url": null, "state": "new", "archived": false, "hash": "feefd8698ec1d6864bd2ee88240e09d30310c347", "submitter": { "id": 90344, "url": "http://patchwork.ozlabs.org/api/people/90344/?format=api", "name": "Aaron Kling via B4 Relay", "email": "devnull+webgeek1234.gmail.com@kernel.org" }, "delegate": null, "mbox": "http://patchwork.ozlabs.org/project/linux-tegra/patch/20260406-t210-actmon-p2-v7-1-91adf535cf8f@gmail.com/mbox/", "series": [ { "id": 498823, "url": "http://patchwork.ozlabs.org/api/series/498823/?format=api", "web_url": "http://patchwork.ozlabs.org/project/linux-tegra/list/?series=498823", "date": "2026-04-06T06:36:20", "name": "[v7] memory: tegra210: Support interconnect framework", "version": 7, "mbox": "http://patchwork.ozlabs.org/series/498823/mbox/" } ], "comments": "http://patchwork.ozlabs.org/api/patches/2220050/comments/", "check": "pending", "checks": "http://patchwork.ozlabs.org/api/patches/2220050/checks/", "tags": {}, "related": [], "headers": { "Return-Path": "\n <linux-tegra+bounces-13568-incoming=patchwork.ozlabs.org@vger.kernel.org>", "X-Original-To": [ "incoming@patchwork.ozlabs.org", "linux-tegra@vger.kernel.org" ], "Delivered-To": "patchwork-incoming@legolas.ozlabs.org", "Authentication-Results": [ "legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=kernel.org header.i=@kernel.org header.a=rsa-sha256\n header.s=k20201202 header.b=M4R2QUaY;\n\tdkim-atps=neutral", "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=vger.kernel.org\n (client-ip=172.234.253.10; helo=sea.lore.kernel.org;\n envelope-from=linux-tegra+bounces-13568-incoming=patchwork.ozlabs.org@vger.kernel.org;\n receiver=patchwork.ozlabs.org)", "smtp.subspace.kernel.org;\n\tdkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=\"M4R2QUaY\"", "smtp.subspace.kernel.org;\n arc=none smtp.client-ip=10.30.226.201" ], "Received": [ "from sea.lore.kernel.org (sea.lore.kernel.org [172.234.253.10])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fq03f0F9Bz1y2d\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 06 Apr 2026 16:36:45 +1000 (AEST)", "from smtp.subspace.kernel.org (conduit.subspace.kernel.org\n [100.90.174.1])\n\tby sea.lore.kernel.org (Postfix) with ESMTP id 11114301FA79\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 6 Apr 2026 06:36:29 +0000 (UTC)", "from localhost.localdomain (localhost.localdomain [127.0.0.1])\n\tby smtp.subspace.kernel.org (Postfix) with ESMTP id E3A9A3570C8;\n\tMon, 6 Apr 2026 06:36:28 +0000 (UTC)", "from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org\n [10.30.226.201])\n\t(using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby smtp.subspace.kernel.org (Postfix) with ESMTPS id A68CA7262B;\n\tMon, 6 Apr 2026 06:36:28 +0000 (UTC)", "by smtp.kernel.org (Postfix) with ESMTPS id 4F52BC4CEF7;\n\tMon, 6 Apr 2026 06:36:28 +0000 (UTC)", "from aws-us-west-2-korg-lkml-1.web.codeaurora.org\n (localhost.localdomain [127.0.0.1])\n\tby smtp.lore.kernel.org (Postfix) with ESMTP id 46506EF4EB0;\n\tMon, 6 Apr 2026 06:36:28 +0000 (UTC)" ], "ARC-Seal": "i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116;\n\tt=1775457388; cv=none;\n b=jO06PefJlMb4UawpqLaTo9RPi6LGY18ifwgBVp7NIw2+HKFWEQSrCpJgaxN7CSyyWmL2mFOyy6JA4Q8mleSCUKN59oXETVpJ8ubXXIfSaUGor+g4bLLd4ktApohpJyunPh7/kmmS2MF72aVWnc8EVCQVpBTTAVyLIG1XHgV2Hrc=", "ARC-Message-Signature": "i=1; a=rsa-sha256; d=subspace.kernel.org;\n\ts=arc-20240116; t=1775457388; c=relaxed/simple;\n\tbh=8EM53iH7JwSzKxm8wQ1wZ9jdKfJec0x03boGG4KY2VU=;\n\th=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc;\n b=eLyY8q2n2Qw6NCJYMxhnDhaTX1RT7TczGnq5xAG9qbZLa7ChtSMu+1kkdtnwrvPqGLSA1rmwo5cUVIAEKVV9eTEHTcKgU00XuU6p91FcmzDKg5BJRB5ylXRVENnr4LoAaU0KwSROcWdTvUIfs1M+Kwt/PYUi4dLk0S2XNqb4EDY=", "ARC-Authentication-Results": "i=1; smtp.subspace.kernel.org;\n dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org\n header.b=M4R2QUaY; arc=none smtp.client-ip=10.30.226.201", "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org;\n\ts=k20201202; t=1775457388;\n\tbh=8EM53iH7JwSzKxm8wQ1wZ9jdKfJec0x03boGG4KY2VU=;\n\th=From:Date:Subject:To:Cc:Reply-To:From;\n\tb=M4R2QUaYuSwgN7PVnW+eZ0a+zFzmVkferdT97CtdByeVSj2a9YpQD4xrZaB5xo3ZB\n\t 8sJGYHv0xF/cN/ieu4TgG5JyJ4nz9DbqxauhjDPCeBNWUbPZt3Tu4lQIRcV3nuS669\n\t JfsJkkWEFaW3ni0xeeTCBjiE4rXILDCMVd2YAPlmGs9JOWg0CrQbTisKvcVK/6auHT\n\t k+SLBOTXga+yiPQYfWbCsi+K8vI1CdmyRESaXkTMIG7vDcR0+dgrtVFtcJFFaRNZGZ\n\t HmROWQ/JHpACNifAM/o42Cmelj6DVTYq01lwuvB67/ys4U4ArqHje0eFQBlS0TJkmP\n\t IsIbD7lHGLxJA==", "From": "Aaron Kling via B4 Relay <devnull+webgeek1234.gmail.com@kernel.org>", "Date": "Mon, 06 Apr 2026 01:36:20 -0500", "Subject": "[PATCH v7] memory: tegra210: Support interconnect framework", "Precedence": "bulk", "X-Mailing-List": "linux-tegra@vger.kernel.org", "List-Id": "<linux-tegra.vger.kernel.org>", "List-Subscribe": "<mailto:linux-tegra+subscribe@vger.kernel.org>", "List-Unsubscribe": "<mailto:linux-tegra+unsubscribe@vger.kernel.org>", "MIME-Version": "1.0", "Content-Type": "text/plain; charset=\"utf-8\"", "Content-Transfer-Encoding": "7bit", "Message-Id": "<20260406-t210-actmon-p2-v7-1-91adf535cf8f@gmail.com>", "X-B4-Tracking": "v=1; b=H4sIAAAAAAAC/13OQQrCMBCF4auUrB1JhiYBV95DXKSTaRswTU1jE\n UrvbpC6cfkv3sfbxMI58CIuzSYyr2EJaaphT42g0U0DQ/C1BUo0spUaCioJjkpME8wIsiOvezK\n ayYg6mjP34f0Fb/fafU4RypjZ/RitJNp/ZjWgQFHbeUTrjTXXIbrwOFOKh5r5+arvykHv+wepB\n bd9uwAAAA==", "X-Change-ID": "20260405-t210-actmon-p2-0bcd5fc65ec6", "To": "Krzysztof Kozlowski <krzk@kernel.org>,\n Thierry Reding <thierry.reding@kernel.org>,\n Jonathan Hunter <jonathanh@nvidia.com>", "Cc": "linux-kernel@vger.kernel.org, linux-tegra@vger.kernel.org,\n Aaron Kling <webgeek1234@gmail.com>", "X-Mailer": "b4 0.14.2", "X-Developer-Signature": "v=1; a=ed25519-sha256; t=1775457387; l=17174;\n i=webgeek1234@gmail.com; s=20250217; h=from:subject:message-id;\n bh=KAHKZYvJmWAZZPhruG56Hw7Kl9tDtric0WozU0X9ZV0=;\n b=1bTMMvWxvfK//PkZDHXepwVCAg1vRVRVuGXq91inV7Gb1c72wEXlsEQZ+F09FF1spdsQ3Vzuj\n 2BJONyj0mG/DJIjn8yULPvdWtG66WKeCljzOY/Iu84OylH/IbjjwxGn", "X-Developer-Key": "i=webgeek1234@gmail.com; a=ed25519;\n pk=TQwd6q26txw7bkK7B8qtI/kcAohZc7bHHGSD7domdrU=", "X-Endpoint-Received": "by B4 Relay for webgeek1234@gmail.com/20250217 with\n auth_id=342", "X-Original-From": "Aaron Kling <webgeek1234@gmail.com>", "Reply-To": "webgeek1234@gmail.com" }, "content": "From: Aaron Kling <webgeek1234@gmail.com>\n\nThis makes mc and emc interconnect providers and allows for dynamic\nmemory clock scaling.\n\nSigned-off-by: Aaron Kling <webgeek1234@gmail.com>\n---\nChanges in v7:\n- Squash fixes found during previous foray through -next\n- Use kzalloc_obj in place of kzalloc\n- Link to v6: https://lore.kernel.org/r/20251027-t210-actmon-p2-v6-1-1c4bd227d676@gmail.com\n\nChanges in v6:\n- Fix style nit\n- Link to v5: https://lore.kernel.org/r/20251021-t210-actmon-p2-v5-1-a07dc70e948d@gmail.com\n\nChanges in v5:\n- Split series\n- Link to v4: https://lore.kernel.org/r/20250923-t210-actmon-v4-0-442d1eb6377c@gmail.com\n\nChanges in v4:\n- Various cleanups in patch 5 as requested by review\n- Fix a couple typos in patch 4\n- Link to v3: https://lore.kernel.org/r/20250906-t210-actmon-v3-0-1403365d571e@gmail.com\n\nChanges in v3:\n- In patch 5, don't fail mc probe if opp tables are missing\n- Add more mc bindings to patch 1\n- Add patch to use tegra210-mc bindings in the mc driver\n- Re-order series to align patches within a subsystem to each other\n- Link to v2: https://lore.kernel.org/r/20250903-t210-actmon-v2-0-e0d534d4f8ea@gmail.com\n\nChanges in v2:\n- Assume 64-bit dram bus width in patch 4\n- Add dt-bindings patch to document the new properties on the\n tegra210-emc node.\n- Link to v1: https://lore.kernel.org/r/20250828-t210-actmon-v1-0-aeb19ec1f244@gmail.com\n---\n drivers/memory/tegra/Kconfig | 1 +\n drivers/memory/tegra/tegra210-emc-core.c | 287 ++++++++++++++++++++++++++++++-\n drivers/memory/tegra/tegra210-emc.h | 24 +++\n drivers/memory/tegra/tegra210.c | 81 +++++++++\n 4 files changed, 390 insertions(+), 3 deletions(-)\n\n\n---\nbase-commit: 2febe6e6ee6e34c7754eff3c4d81aa7b0dcb7979\nchange-id: 20260405-t210-actmon-p2-0bcd5fc65ec6\n\nBest regards,", "diff": "diff --git a/drivers/memory/tegra/Kconfig b/drivers/memory/tegra/Kconfig\nindex fc5a277918267ee8240f9fb9efeb80275db4790b..2d0be29afe2b9ebf9a0630ef7fb6fb43ff359499 100644\n--- a/drivers/memory/tegra/Kconfig\n+++ b/drivers/memory/tegra/Kconfig\n@@ -55,6 +55,7 @@ config TEGRA210_EMC\n \ttristate \"NVIDIA Tegra210 External Memory Controller driver\"\n \tdepends on ARCH_TEGRA_210_SOC || COMPILE_TEST\n \tselect TEGRA210_EMC_TABLE\n+\tselect PM_OPP\n \thelp\n \t This driver is for the External Memory Controller (EMC) found on\n \t Tegra210 chips. The EMC controls the external DRAM on the board.\ndiff --git a/drivers/memory/tegra/tegra210-emc-core.c b/drivers/memory/tegra/tegra210-emc-core.c\nindex e96ca4157d48182574310f8caf72687bed7cc16a..c7850df2a21d9f6b837bc1fa24a159fc18b0498a 100644\n--- a/drivers/memory/tegra/tegra210-emc-core.c\n+++ b/drivers/memory/tegra/tegra210-emc-core.c\n@@ -13,6 +13,7 @@\n #include <linux/module.h>\n #include <linux/of_reserved_mem.h>\n #include <linux/platform_device.h>\n+#include <linux/pm_opp.h>\n #include <linux/slab.h>\n #include <linux/thermal.h>\n #include <soc/tegra/fuse.h>\n@@ -87,6 +88,13 @@\n \n #define LPDDR2_MR4_SRR GENMASK(2, 0)\n \n+/*\n+ * Tegra210 memory layout can be 1 channel at 64-bit or 2 channels at 32-bit\n+ * each. Either way, the total bus width will always be 64-bit.\n+ */\n+#define DRAM_DATA_BUS_WIDTH_BYTES (64 / 8)\n+#define DDR 2\n+\n static const struct tegra210_emc_sequence *tegra210_emc_sequences[] = {\n \t&tegra210_emc_r21021,\n };\n@@ -1569,6 +1577,79 @@ static int tegra210_emc_set_rate(struct device *dev,\n \treturn 0;\n }\n \n+static void tegra210_emc_rate_requests_init(struct tegra210_emc *emc)\n+{\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < EMC_RATE_TYPE_MAX; i++) {\n+\t\temc->requested_rate[i].min_rate = 0;\n+\t\temc->requested_rate[i].max_rate = ULONG_MAX;\n+\t}\n+}\n+\n+static int emc_request_rate(struct tegra210_emc *emc,\n+\t\t\t unsigned long new_min_rate,\n+\t\t\t unsigned long new_max_rate,\n+\t\t\t enum emc_rate_request_type type)\n+{\n+\tstruct emc_rate_request *req = emc->requested_rate;\n+\tunsigned long min_rate = 0, max_rate = ULONG_MAX;\n+\tunsigned int i;\n+\tint err;\n+\n+\t/* select minimum and maximum rates among the requested rates */\n+\tfor (i = 0; i < EMC_RATE_TYPE_MAX; i++, req++) {\n+\t\tif (i == type) {\n+\t\t\tmin_rate = max(new_min_rate, min_rate);\n+\t\t\tmax_rate = min(new_max_rate, max_rate);\n+\t\t} else {\n+\t\t\tmin_rate = max(req->min_rate, min_rate);\n+\t\t\tmax_rate = min(req->max_rate, max_rate);\n+\t\t}\n+\t}\n+\n+\tif (min_rate > max_rate) {\n+\t\tdev_err_ratelimited(emc->dev, \"%s: type %u: out of range: %lu %lu\\n\",\n+\t\t\t\t __func__, type, min_rate, max_rate);\n+\t\treturn -ERANGE;\n+\t}\n+\n+\terr = clk_set_rate(emc->clk, min_rate);\n+\tif (err)\n+\t\treturn err;\n+\n+\temc->requested_rate[type].min_rate = new_min_rate;\n+\temc->requested_rate[type].max_rate = new_max_rate;\n+\n+\treturn 0;\n+}\n+\n+static int emc_set_min_rate(struct tegra210_emc *emc, unsigned long rate,\n+\t\t\t enum emc_rate_request_type type)\n+{\n+\tstruct emc_rate_request *req = &emc->requested_rate[type];\n+\tint ret;\n+\n+\tmutex_lock(&emc->rate_lock);\n+\tret = emc_request_rate(emc, rate, req->max_rate, type);\n+\tmutex_unlock(&emc->rate_lock);\n+\n+\treturn ret;\n+}\n+\n+static int emc_set_max_rate(struct tegra210_emc *emc, unsigned long rate,\n+\t\t\t enum emc_rate_request_type type)\n+{\n+\tstruct emc_rate_request *req = &emc->requested_rate[type];\n+\tint ret;\n+\n+\tmutex_lock(&emc->rate_lock);\n+\tret = emc_request_rate(emc, req->min_rate, rate, type);\n+\tmutex_unlock(&emc->rate_lock);\n+\n+\treturn ret;\n+}\n+\n /*\n * debugfs interface\n *\n@@ -1641,7 +1722,7 @@ static int tegra210_emc_debug_min_rate_set(void *data, u64 rate)\n \tif (!tegra210_emc_validate_rate(emc, rate))\n \t\treturn -EINVAL;\n \n-\terr = clk_set_min_rate(emc->clk, rate);\n+\terr = emc_set_min_rate(emc, rate, EMC_RATE_DEBUG);\n \tif (err < 0)\n \t\treturn err;\n \n@@ -1671,7 +1752,7 @@ static int tegra210_emc_debug_max_rate_set(void *data, u64 rate)\n \tif (!tegra210_emc_validate_rate(emc, rate))\n \t\treturn -EINVAL;\n \n-\terr = clk_set_max_rate(emc->clk, rate);\n+\terr = emc_set_max_rate(emc, rate, EMC_RATE_DEBUG);\n \tif (err < 0)\n \t\treturn err;\n \n@@ -1758,6 +1839,194 @@ static void tegra210_emc_debugfs_init(struct tegra210_emc *emc)\n \t\t\t &tegra210_emc_debug_temperature_fops);\n }\n \n+static inline struct tegra210_emc *\n+to_tegra210_emc_provider(struct icc_provider *provider)\n+{\n+\treturn container_of(provider, struct tegra210_emc, icc_provider);\n+}\n+\n+static struct icc_node_data *\n+emc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data)\n+{\n+\tstruct icc_provider *provider = data;\n+\tstruct icc_node_data *ndata;\n+\tstruct icc_node *node;\n+\n+\t/* External Memory is the only possible ICC route */\n+\tlist_for_each_entry(node, &provider->nodes, node_list) {\n+\t\tif (node->id != TEGRA_ICC_EMEM)\n+\t\t\tcontinue;\n+\n+\t\tndata = kzalloc_obj(*ndata);\n+\t\tif (!ndata)\n+\t\t\treturn ERR_PTR(-ENOMEM);\n+\n+\t\t/*\n+\t\t * SRC and DST nodes should have matching TAG in order to have\n+\t\t * it set by default for a requested path.\n+\t\t */\n+\t\tndata->tag = TEGRA_MC_ICC_TAG_ISO;\n+\t\tndata->node = node;\n+\n+\t\treturn ndata;\n+\t}\n+\n+\treturn ERR_PTR(-EPROBE_DEFER);\n+}\n+\n+static int emc_icc_set(struct icc_node *src, struct icc_node *dst)\n+{\n+\tstruct tegra210_emc *emc = to_tegra210_emc_provider(dst->provider);\n+\tunsigned long long peak_bw = icc_units_to_bps(dst->peak_bw);\n+\tunsigned long long avg_bw = icc_units_to_bps(dst->avg_bw);\n+\tunsigned long long rate = max(avg_bw, peak_bw);\n+\tint err;\n+\n+\t/*\n+\t * Tegra210 EMC runs on a clock rate of SDRAM bus. This means that\n+\t * EMC clock rate is twice smaller than the peak data rate because\n+\t * data is sampled on both EMC clock edges.\n+\t */\n+\tdo_div(rate, DDR * DRAM_DATA_BUS_WIDTH_BYTES);\n+\trate = min_t(u64, rate, U32_MAX);\n+\n+\terr = emc_set_min_rate(emc, rate, EMC_RATE_ICC);\n+\tif (err)\n+\t\treturn err;\n+\n+\treturn 0;\n+}\n+\n+static int tegra210_emc_icc_get_init_bw(struct icc_node *node, u32 *avg, u32 *peak)\n+{\n+\t*avg = 0;\n+\t*peak = 0;\n+\n+\treturn 0;\n+}\n+\n+static int tegra210_emc_interconnect_init(struct tegra210_emc *emc)\n+{\n+\tconst struct tegra_mc_soc *soc = emc->mc->soc;\n+\tstruct icc_node *node;\n+\tint err;\n+\n+\temc->icc_provider.dev = emc->dev;\n+\temc->icc_provider.set = emc_icc_set;\n+\temc->icc_provider.data = &emc->icc_provider;\n+\temc->icc_provider.aggregate = soc->icc_ops->aggregate;\n+\temc->icc_provider.xlate_extended = emc_of_icc_xlate_extended;\n+\temc->icc_provider.get_bw = tegra210_emc_icc_get_init_bw;\n+\n+\ticc_provider_init(&emc->icc_provider);\n+\n+\t/* create External Memory Controller node */\n+\tnode = icc_node_create(TEGRA_ICC_EMC);\n+\tif (IS_ERR(node))\n+\t\treturn PTR_ERR(node);\n+\n+\tnode->name = \"External Memory Controller\";\n+\ticc_node_add(node, &emc->icc_provider);\n+\n+\t/* link External Memory Controller to External Memory (DRAM) */\n+\terr = icc_link_create(node, TEGRA_ICC_EMEM);\n+\tif (err)\n+\t\tgoto remove_nodes;\n+\n+\t/* create External Memory node */\n+\tnode = icc_node_create(TEGRA_ICC_EMEM);\n+\tif (IS_ERR(node)) {\n+\t\terr = PTR_ERR(node);\n+\t\tgoto remove_nodes;\n+\t}\n+\n+\tnode->name = \"External Memory (DRAM)\";\n+\ticc_node_add(node, &emc->icc_provider);\n+\n+\terr = icc_provider_register(&emc->icc_provider);\n+\tif (err)\n+\t\tgoto remove_nodes;\n+\n+\treturn 0;\n+\n+remove_nodes:\n+\ticc_nodes_remove(&emc->icc_provider);\n+\n+\treturn dev_err_probe(emc->dev, err, \"failed to initialize ICC\\n\");\n+}\n+\n+static int tegra210_emc_opp_table_init(struct tegra210_emc *emc)\n+{\n+\tu32 hw_version = BIT(tegra_sku_info.soc_speedo_id);\n+\tstruct dev_pm_opp *opp;\n+\tunsigned long rate;\n+\tint err, max_opps, i;\n+\n+\terr = dev_pm_opp_set_supported_hw(emc->dev, &hw_version, 1);\n+\tif (err < 0)\n+\t\treturn dev_err_probe(emc->dev, err, \"failed to set OPP supported HW\\n\");\n+\n+\temc->hw_opp_token = err;\n+\n+\terr = dev_pm_opp_of_add_table(emc->dev);\n+\tif (err) {\n+\t\tif (err == -ENODEV)\n+\t\t\tdev_err_probe(emc->dev, err,\n+\t\t\t\t \"OPP table not found, please update your device tree\\n\");\n+\t\telse\n+\t\t\tdev_err_probe(emc->dev, err, \"failed to add OPP table\\n\");\n+\n+\t\tgoto put_hw_table;\n+\t}\n+\n+\tmax_opps = dev_pm_opp_get_opp_count(emc->dev);\n+\tif (max_opps <= 0) {\n+\t\terr = max_opps ?: -EINVAL;\n+\t\tdev_err_probe(emc->dev, err, \"Failed to add OPPs\\n\");\n+\t\tgoto remove_table;\n+\t}\n+\n+\tif (emc->num_timings != max_opps) {\n+\t\terr = -EINVAL;\n+\t\tdev_err_probe(emc->dev, err, \"OPP table does not match emc table\\n\");\n+\t\tgoto remove_table;\n+\t}\n+\n+\tfor (i = 0; i < emc->num_timings; i++) {\n+\t\trate = emc->timings[i].rate * 1000;\n+\t\topp = dev_pm_opp_find_freq_exact(emc->dev, rate, true);\n+\t\tif (IS_ERR(opp)) {\n+\t\t\terr = PTR_ERR(opp);\n+\t\t\tdev_err_probe(emc->dev, err, \"Rate %lu not found in OPP table\\n\", rate);\n+\t\t\tgoto remove_table;\n+\t\t}\n+\n+\t\tdev_pm_opp_put(opp);\n+\t}\n+\n+\tdev_info_once(emc->dev, \"OPP HW ver. 0x%x, current clock rate %lu MHz\\n\",\n+\t\t hw_version, clk_get_rate(emc->clk) / 1000000);\n+\n+\treturn 0;\n+\n+remove_table:\n+\tdev_pm_opp_of_remove_table(emc->dev);\n+put_hw_table:\n+\tdev_pm_opp_put_supported_hw(emc->hw_opp_token);\n+\temc->hw_opp_token = 0;\n+\n+\treturn err;\n+}\n+\n+static void tegra210_emc_opp_table_cleanup(struct tegra210_emc *emc)\n+{\n+\tif (emc->hw_opp_token) {\n+\t\tdev_pm_opp_of_remove_table(emc->dev);\n+\t\tdev_pm_opp_put_supported_hw(emc->hw_opp_token);\n+\t\temc->hw_opp_token = 0;\n+\t}\n+}\n+\n static void tegra210_emc_detect(struct tegra210_emc *emc)\n {\n \tu32 value;\n@@ -1966,17 +2235,27 @@ static int tegra210_emc_probe(struct platform_device *pdev)\n \n \ttegra210_emc_debugfs_init(emc);\n \n+\terr = tegra210_emc_opp_table_init(emc);\n+\tif (!err) {\n+\t\ttegra210_emc_rate_requests_init(emc);\n+\t\ttegra210_emc_interconnect_init(emc);\n+\t} else if (err != -ENODEV) {\n+\t\tgoto detach;\n+\t}\n+\n \tcd = devm_thermal_of_cooling_device_register(emc->dev, np, \"emc\", emc,\n \t\t\t\t\t\t &tegra210_emc_cd_ops);\n \tif (IS_ERR(cd)) {\n \t\terr = PTR_ERR(cd);\n \t\tdev_err(emc->dev, \"failed to register cooling device: %d\\n\",\n \t\t\terr);\n-\t\tgoto detach;\n+\t\tgoto cleanup_table;\n \t}\n \n \treturn 0;\n \n+cleanup_table:\n+\ttegra210_emc_opp_table_cleanup(emc);\n detach:\n \tdebugfs_remove_recursive(emc->debugfs.root);\n \ttegra210_clk_emc_detach(emc->clk);\n@@ -1990,6 +2269,7 @@ static void tegra210_emc_remove(struct platform_device *pdev)\n {\n \tstruct tegra210_emc *emc = platform_get_drvdata(pdev);\n \n+\ttegra210_emc_opp_table_cleanup(emc);\n \tdebugfs_remove_recursive(emc->debugfs.root);\n \ttegra210_clk_emc_detach(emc->clk);\n \tof_reserved_mem_device_release(emc->dev);\n@@ -2050,6 +2330,7 @@ static struct platform_driver tegra210_emc_driver = {\n \t\t.name = \"tegra210-emc\",\n \t\t.of_match_table = tegra210_emc_of_match,\n \t\t.pm = &tegra210_emc_pm_ops,\n+\t\t.sync_state = icc_sync_state,\n \t},\n \t.probe = tegra210_emc_probe,\n \t.remove = tegra210_emc_remove,\ndiff --git a/drivers/memory/tegra/tegra210-emc.h b/drivers/memory/tegra/tegra210-emc.h\nindex 8988bcf1529072a7bdc93b185ebe0d51d82c1763..e6f267823f9f0ee18cd60778116dc5ca2730a5d4 100644\n--- a/drivers/memory/tegra/tegra210-emc.h\n+++ b/drivers/memory/tegra/tegra210-emc.h\n@@ -8,6 +8,7 @@\n \n #include <linux/clk.h>\n #include <linux/clk/tegra.h>\n+#include <linux/interconnect-provider.h>\n #include <linux/io.h>\n #include <linux/platform_device.h>\n \n@@ -784,6 +785,17 @@ enum {\n #define TRIM_REGS_SIZE 138\n #define BURST_REGS_SIZE 221\n \n+enum emc_rate_request_type {\n+\tEMC_RATE_DEBUG,\n+\tEMC_RATE_ICC,\n+\tEMC_RATE_TYPE_MAX,\n+};\n+\n+struct emc_rate_request {\n+\tunsigned long min_rate;\n+\tunsigned long max_rate;\n+};\n+\n struct tegra210_emc_per_channel_regs {\n \tu16 bank;\n \tu16 offset;\n@@ -932,6 +944,18 @@ struct tegra210_emc {\n \t} debugfs;\n \n \tstruct tegra210_clk_emc_provider provider;\n+\n+\tstruct icc_provider icc_provider;\n+\tint hw_opp_token;\n+\n+\t/*\n+\t * There are multiple sources in the EMC driver which could request\n+\t * a min/max clock rate, these rates are contained in this array.\n+\t */\n+\tstruct emc_rate_request requested_rate[EMC_RATE_TYPE_MAX];\n+\n+\t/* protect shared rate-change code path */\n+\tstruct mutex rate_lock;\n };\n \n struct tegra210_emc_sequence {\ndiff --git a/drivers/memory/tegra/tegra210.c b/drivers/memory/tegra/tegra210.c\nindex f58f3ef6f68191ede7098bfa4dad546ce4de275d..bcc441e786301438279e5318373d502e3b22eec5 100644\n--- a/drivers/memory/tegra/tegra210.c\n+++ b/drivers/memory/tegra/tegra210.c\n@@ -3,6 +3,9 @@\n * Copyright (C) 2015-2026 NVIDIA CORPORATION. All rights reserved.\n */\n \n+#include <linux/of.h>\n+#include <linux/device.h>\n+\n #include <dt-bindings/memory/tegra210-mc.h>\n \n #include \"mc.h\"\n@@ -1282,6 +1285,83 @@ static const struct tegra_mc_intmask tegra210_mc_intmasks[] = {\n \t},\n };\n \n+static int tegra210_mc_icc_set(struct icc_node *src, struct icc_node *dst)\n+{\n+\t/* TODO: program PTSA */\n+\treturn 0;\n+}\n+\n+static int tegra210_mc_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,\n+\t\t\t\t u32 peak_bw, u32 *agg_avg, u32 *agg_peak)\n+{\n+\t/*\n+\t * ISO clients need to reserve extra bandwidth up-front because\n+\t * there could be high bandwidth pressure during initial filling\n+\t * of the client's FIFO buffers. Secondly, we need to take into\n+\t * account impurities of the memory subsystem.\n+\t */\n+\tif (tag & TEGRA_MC_ICC_TAG_ISO)\n+\t\tpeak_bw = tegra_mc_scale_percents(peak_bw, 400);\n+\n+\t*agg_avg += avg_bw;\n+\t*agg_peak = max(*agg_peak, peak_bw);\n+\n+\treturn 0;\n+}\n+\n+static struct icc_node_data *\n+tegra210_mc_of_icc_xlate_extended(const struct of_phandle_args *spec, void *data)\n+{\n+\tstruct tegra_mc *mc = icc_provider_to_tegra_mc(data);\n+\tconst struct tegra_mc_client *client;\n+\tunsigned int i, idx = spec->args[0];\n+\tstruct icc_node_data *ndata;\n+\tstruct icc_node *node;\n+\n+\tlist_for_each_entry(node, &mc->provider.nodes, node_list) {\n+\t\tif (node->id != idx)\n+\t\t\tcontinue;\n+\n+\t\tndata = kzalloc_obj(*ndata);\n+\t\tif (!ndata)\n+\t\t\treturn ERR_PTR(-ENOMEM);\n+\n+\t\tclient = &mc->soc->clients[idx];\n+\t\tndata->node = node;\n+\n+\t\tswitch (client->swgroup) {\n+\t\tcase TEGRA_SWGROUP_DC:\n+\t\tcase TEGRA_SWGROUP_DCB:\n+\t\tcase TEGRA_SWGROUP_PTC:\n+\t\tcase TEGRA_SWGROUP_VI:\n+\t\t\t/* these clients are isochronous by default */\n+\t\t\tndata->tag = TEGRA_MC_ICC_TAG_ISO;\n+\t\t\tbreak;\n+\n+\t\tdefault:\n+\t\t\tndata->tag = TEGRA_MC_ICC_TAG_DEFAULT;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\treturn ndata;\n+\t}\n+\n+\tfor (i = 0; i < mc->soc->num_clients; i++) {\n+\t\tif (mc->soc->clients[i].id == idx)\n+\t\t\treturn ERR_PTR(-EPROBE_DEFER);\n+\t}\n+\n+\tdev_err(mc->dev, \"invalid ICC client ID %u\\n\", idx);\n+\n+\treturn ERR_PTR(-EINVAL);\n+}\n+\n+static const struct tegra_mc_icc_ops tegra210_mc_icc_ops = {\n+\t.xlate_extended = tegra210_mc_of_icc_xlate_extended,\n+\t.aggregate = tegra210_mc_icc_aggregate,\n+\t.set = tegra210_mc_icc_set,\n+};\n+\n const struct tegra_mc_soc tegra210_mc_soc = {\n \t.clients = tegra210_mc_clients,\n \t.num_clients = ARRAY_SIZE(tegra210_mc_clients),\n@@ -1294,6 +1374,7 @@ const struct tegra_mc_soc tegra210_mc_soc = {\n \t.reset_ops = &tegra_mc_reset_ops_common,\n \t.resets = tegra210_mc_resets,\n \t.num_resets = ARRAY_SIZE(tegra210_mc_resets),\n+\t.icc_ops = &tegra210_mc_icc_ops,\n \t.ops = &tegra30_mc_ops,\n \t.regs = &tegra20_mc_regs,\n \t.handle_irq = tegra30_mc_irq_handlers,\n", "prefixes": [ "v7" ] }