From patchwork Thu Oct 31 21:50:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 11221913 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 14B7D1390 for ; Thu, 31 Oct 2019 21:50:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id F1280208E3 for ; Thu, 31 Oct 2019 21:50:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729118AbfJaVug (ORCPT ); Thu, 31 Oct 2019 17:50:36 -0400 Received: from inva020.nxp.com ([92.121.34.13]:42330 "EHLO inva020.nxp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728713AbfJaVug (ORCPT ); Thu, 31 Oct 2019 17:50:36 -0400 Received: from inva020.nxp.com (localhost [127.0.0.1]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 32F331A0596; Thu, 31 Oct 2019 22:50:33 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 239551A00B6; Thu, 31 Oct 2019 22:50:33 +0100 (CET) Received: from fsr-ub1864-112.ea.freescale.net (fsr-ub1864-112.ea.freescale.net [10.171.82.98]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id 14856205E9; Thu, 31 Oct 2019 22:50:32 +0100 (CET) From: Leonard Crestez To: Stephen Boyd , MyungJoo Ham , Kyungmin Park , Rob Herring Cc: "Rafael J. Wysocki" , Shawn Guo , Chanwoo Choi , Mark Rutland , Michael Turquette , =?utf-8?b?QXJ0dXIgxZp3aWdvxYQ=?= , Saravana Kannan , Angus Ainslie , Martin Kepplinger , Matthias Kaehlcke , Krzysztof Kozlowski , Alexandre Bailon , Georgi Djakov , Dong Aisheng , Abel Vesa , Jacky Bai , Anson Huang , Fabio Estevam , Viresh Kumar , devicetree@vger.kernel.org, linux-pm@vger.kernel.org, linux-clk@vger.kernel.org, linux-imx@nxp.com, kernel@pengutronix.de, linux-arm-kernel@lists.infradead.org Subject: [PATCH v3 1/6] clk: imx8m: Set CLK_GET_RATE_NOCACHE on dram clocks Date: Thu, 31 Oct 2019 23:50:22 +0200 Message-Id: <94c478c1209704a3da4577ae79ea28888759e8a7.1572558427.git.leonard.crestez@nxp.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org These clocks are only modified as part of DRAM frequency switches during which DRAM itself is briefly inaccessible. The switch is performed with a SMC call to by TF-A which runs from a SRAM area; upon returning to linux several clocks bits are modified and we need to update them. For rate bits an easy solution is to just mark with CLK_GET_RATE_NOCACHE so that new rates are always read back from registers. Signed-off-by: Leonard Crestez --- drivers/clk/imx/clk-imx8mm.c | 11 +++++++++-- drivers/clk/imx/clk-imx8mn.c | 12 ++++++++++-- drivers/clk/imx/clk-imx8mq.c | 15 +++++++++++---- 3 files changed, 30 insertions(+), 8 deletions(-) diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index 030b15d7c0ce..c58f988191a5 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -440,13 +440,20 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) /* IPG */ clks[IMX8MM_CLK_IPG_ROOT] = imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1); clks[IMX8MM_CLK_IPG_AUDIO_ROOT] = imx_clk_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1); + /* + * DRAM clocks are manipulated from TF-A outside clock framework. + * Mark with GET_RATE_NOCACHE to always read div value from hardware + */ + clks[IMX8MM_CLK_DRAM_ALT] = __imx8m_clk_composite("dram_alt", imx8mm_dram_alt_sels, base + 0xa000, + CLK_GET_RATE_NOCACHE); + clks[IMX8MM_CLK_DRAM_APB] = __imx8m_clk_composite("dram_apb", imx8mm_dram_apb_sels, base + 0xa080, + CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE); + /* IP */ - clks[IMX8MM_CLK_DRAM_ALT] = imx8m_clk_composite("dram_alt", imx8mm_dram_alt_sels, base + 0xa000); - clks[IMX8MM_CLK_DRAM_APB] = imx8m_clk_composite_critical("dram_apb", imx8mm_dram_apb_sels, base + 0xa080); clks[IMX8MM_CLK_VPU_G1] = imx8m_clk_composite("vpu_g1", imx8mm_vpu_g1_sels, base + 0xa100); clks[IMX8MM_CLK_VPU_G2] = imx8m_clk_composite("vpu_g2", imx8mm_vpu_g2_sels, base + 0xa180); clks[IMX8MM_CLK_DISP_DTRC] = imx8m_clk_composite("disp_dtrc", imx8mm_disp_dtrc_sels, base + 0xa200); clks[IMX8MM_CLK_DISP_DC8000] = imx8m_clk_composite("disp_dc8000", imx8mm_disp_dc8000_sels, base + 0xa280); clks[IMX8MM_CLK_PCIE1_CTRL] = imx8m_clk_composite("pcie1_ctrl", imx8mm_pcie1_ctrl_sels, base + 0xa300); diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index 9f5a5a56b45e..ca78cb1249a7 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -428,12 +428,20 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) clks[IMX8MN_CLK_AHB] = imx8m_clk_composite_critical("ahb", imx8mn_ahb_sels, base + 0x9000); clks[IMX8MN_CLK_AUDIO_AHB] = imx8m_clk_composite("audio_ahb", imx8mn_audio_ahb_sels, base + 0x9100); clks[IMX8MN_CLK_IPG_ROOT] = imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1); clks[IMX8MN_CLK_IPG_AUDIO_ROOT] = imx_clk_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1); clks[IMX8MN_CLK_DRAM_CORE] = imx_clk_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mn_dram_core_sels, ARRAY_SIZE(imx8mn_dram_core_sels), CLK_IS_CRITICAL); - clks[IMX8MN_CLK_DRAM_ALT] = imx8m_clk_composite("dram_alt", imx8mn_dram_alt_sels, base + 0xa000); - clks[IMX8MN_CLK_DRAM_APB] = imx8m_clk_composite_critical("dram_apb", imx8mn_dram_apb_sels, base + 0xa080); + + /* + * DRAM clocks are manipulated from TF-A outside clock framework. + * Mark with GET_RATE_NOCACHE to always read div value from hardware + */ + clks[IMX8MN_CLK_DRAM_ALT] = __imx8m_clk_composite("dram_alt", imx8mn_dram_alt_sels, base + 0xa000, + CLK_GET_RATE_NOCACHE); + clks[IMX8MN_CLK_DRAM_APB] = __imx8m_clk_composite("dram_apb", imx8mn_dram_apb_sels, base + 0xa080, + CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE); + clks[IMX8MN_CLK_DISP_PIXEL] = imx8m_clk_composite("disp_pixel", imx8mn_disp_pixel_sels, base + 0xa500); clks[IMX8MN_CLK_SAI2] = imx8m_clk_composite("sai2", imx8mn_sai2_sels, base + 0xa600); clks[IMX8MN_CLK_SAI3] = imx8m_clk_composite("sai3", imx8mn_sai3_sels, base + 0xa680); clks[IMX8MN_CLK_SAI5] = imx8m_clk_composite("sai5", imx8mn_sai5_sels, base + 0xa780); clks[IMX8MN_CLK_SAI6] = imx8m_clk_composite("sai6", imx8mn_sai6_sels, base + 0xa800); diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c index 4a5dbc4366a5..ceb1e79cf2e9 100644 --- a/drivers/clk/imx/clk-imx8mq.c +++ b/drivers/clk/imx/clk-imx8mq.c @@ -341,11 +341,12 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) clks[IMX8MQ_VIDEO_PLL1_OUT] = imx_clk_gate("video_pll1_out", "video_pll1_bypass", base + 0x10, 21); clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_fixed("sys1_pll_out", 800000000); clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_fixed("sys2_pll_out", 1000000000); clks[IMX8MQ_SYS3_PLL_OUT] = imx_clk_sccg_pll("sys3_pll_out", sys3_pll_out_sels, ARRAY_SIZE(sys3_pll_out_sels), 0, 0, 1, base + 0x48, CLK_IS_CRITICAL); - clks[IMX8MQ_DRAM_PLL_OUT] = imx_clk_sccg_pll("dram_pll_out", dram_pll_out_sels, ARRAY_SIZE(dram_pll_out_sels), 0, 0, 0, base + 0x60, CLK_IS_CRITICAL); + clks[IMX8MQ_DRAM_PLL_OUT] = imx_clk_sccg_pll("dram_pll_out", dram_pll_out_sels, ARRAY_SIZE(dram_pll_out_sels), 0, 0, 0, base + 0x60, + CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE); clks[IMX8MQ_VIDEO2_PLL_OUT] = imx_clk_sccg_pll("video2_pll_out", video2_pll_out_sels, ARRAY_SIZE(video2_pll_out_sels), 0, 0, 0, base + 0x54, 0); /* SYS PLL1 fixed output */ clks[IMX8MQ_SYS1_PLL_40M_CG] = imx_clk_gate("sys1_pll_40m_cg", "sys1_pll_out", base + 0x30, 9); clks[IMX8MQ_SYS1_PLL_80M_CG] = imx_clk_gate("sys1_pll_80m_cg", "sys1_pll_out", base + 0x30, 11); @@ -433,15 +434,21 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) /* IPG */ clks[IMX8MQ_CLK_IPG_ROOT] = imx_clk_divider2("ipg_root", "ahb", base + 0x9080, 0, 1); clks[IMX8MQ_CLK_IPG_AUDIO_ROOT] = imx_clk_divider2("ipg_audio_root", "audio_ahb", base + 0x9180, 0, 1); - /* IP */ + /* + * DRAM clocks are manipulated from TF-A outside clock framework. + * Mark with GET_RATE_NOCACHE to always read div value from hardware + */ clks[IMX8MQ_CLK_DRAM_CORE] = imx_clk_mux2_flags("dram_core_clk", base + 0x9800, 24, 1, imx8mq_dram_core_sels, ARRAY_SIZE(imx8mq_dram_core_sels), CLK_IS_CRITICAL); + clks[IMX8MQ_CLK_DRAM_ALT] = __imx8m_clk_composite("dram_alt", imx8mq_dram_alt_sels, base + 0xa000, + CLK_GET_RATE_NOCACHE); + clks[IMX8MQ_CLK_DRAM_APB] = __imx8m_clk_composite("dram_apb", imx8mq_dram_apb_sels, base + 0xa080, + CLK_IS_CRITICAL | CLK_GET_RATE_NOCACHE); - clks[IMX8MQ_CLK_DRAM_ALT] = imx8m_clk_composite("dram_alt", imx8mq_dram_alt_sels, base + 0xa000); - clks[IMX8MQ_CLK_DRAM_APB] = imx8m_clk_composite_critical("dram_apb", imx8mq_dram_apb_sels, base + 0xa080); + /* IP */ clks[IMX8MQ_CLK_VPU_G1] = imx8m_clk_composite("vpu_g1", imx8mq_vpu_g1_sels, base + 0xa100); clks[IMX8MQ_CLK_VPU_G2] = imx8m_clk_composite("vpu_g2", imx8mq_vpu_g2_sels, base + 0xa180); clks[IMX8MQ_CLK_DISP_DTRC] = imx8m_clk_composite("disp_dtrc", imx8mq_disp_dtrc_sels, base + 0xa200); clks[IMX8MQ_CLK_DISP_DC8000] = imx8m_clk_composite("disp_dc8000", imx8mq_disp_dc8000_sels, base + 0xa280); clks[IMX8MQ_CLK_PCIE1_CTRL] = imx8m_clk_composite("pcie1_ctrl", imx8mq_pcie1_ctrl_sels, base + 0xa300); From patchwork Thu Oct 31 21:50:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 11221917 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C04CC1599 for ; Thu, 31 Oct 2019 21:50:37 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A7E5A218AC for ; Thu, 31 Oct 2019 21:50:37 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728713AbfJaVug (ORCPT ); Thu, 31 Oct 2019 17:50:36 -0400 Received: from inva020.nxp.com ([92.121.34.13]:42356 "EHLO inva020.nxp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728829AbfJaVug (ORCPT ); Thu, 31 Oct 2019 17:50:36 -0400 Received: from inva020.nxp.com (localhost [127.0.0.1]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 507C01A0598; Thu, 31 Oct 2019 22:50:34 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 425AD1A0112; Thu, 31 Oct 2019 22:50:34 +0100 (CET) Received: from fsr-ub1864-112.ea.freescale.net (fsr-ub1864-112.ea.freescale.net [10.171.82.98]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id 33939205E9; Thu, 31 Oct 2019 22:50:33 +0100 (CET) From: Leonard Crestez To: Stephen Boyd , MyungJoo Ham , Kyungmin Park , Rob Herring Cc: "Rafael J. Wysocki" , Shawn Guo , Chanwoo Choi , Mark Rutland , Michael Turquette , =?utf-8?b?QXJ0dXIgxZp3aWdvxYQ=?= , Saravana Kannan , Angus Ainslie , Martin Kepplinger , Matthias Kaehlcke , Krzysztof Kozlowski , Alexandre Bailon , Georgi Djakov , Dong Aisheng , Abel Vesa , Jacky Bai , Anson Huang , Fabio Estevam , Viresh Kumar , devicetree@vger.kernel.org, linux-pm@vger.kernel.org, linux-clk@vger.kernel.org, linux-imx@nxp.com, kernel@pengutronix.de, linux-arm-kernel@lists.infradead.org Subject: [PATCH v3 2/6] clk: imx: Mark dram pll on 8mm and 8mn with CLK_GET_RATE_NOCACHE Date: Thu, 31 Oct 2019 23:50:23 +0200 Message-Id: <3dd51e03a73f53f6cc7ec90c306d6cdbfa2204f7.1572558427.git.leonard.crestez@nxp.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org DRAM frequency switches are executed in firmware and can change the configuration of the DRAM PLL outside linux. Mark these CLKs with CLK_GET_RATE_NOCACHE so we always read back the PLL config registers and recalculate rates. In current DRAM frequency tables on 8mm/8mn only the maximum frequency uses the PLL so it's always configured in the same way. However reading back the PLL configuration is the correct behavior and allows additional setpoints in the future. Signed-off-by: Leonard Crestez --- drivers/clk/imx/clk-imx8mm.c | 2 +- drivers/clk/imx/clk-imx8mn.c | 2 +- drivers/clk/imx/clk-pll14xx.c | 7 +++++++ drivers/clk/imx/clk.h | 1 + 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index c58f988191a5..d500bac3afa1 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -326,11 +326,11 @@ static int imx8mm_clocks_probe(struct platform_device *pdev) clks[IMX8MM_SYS_PLL3_REF_SEL] = imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MM_AUDIO_PLL1] = imx_clk_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx_1443x_pll); clks[IMX8MM_AUDIO_PLL2] = imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx_1443x_pll); clks[IMX8MM_VIDEO_PLL1] = imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx_1443x_pll); - clks[IMX8MM_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_pll); + clks[IMX8MM_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_dram_pll); clks[IMX8MM_GPU_PLL] = imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx_1416x_pll); clks[IMX8MM_VPU_PLL] = imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx_1416x_pll); clks[IMX8MM_ARM_PLL] = imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx_1416x_pll); clks[IMX8MM_SYS_PLL1] = imx_clk_fixed("sys_pll1", 800000000); clks[IMX8MM_SYS_PLL2] = imx_clk_fixed("sys_pll2", 1000000000); diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index ca78cb1249a7..9c605ca1b631 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -323,11 +323,11 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) clks[IMX8MN_SYS_PLL3_REF_SEL] = imx_clk_mux("sys_pll3_ref_sel", base + 0x114, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); clks[IMX8MN_AUDIO_PLL1] = imx_clk_pll14xx("audio_pll1", "audio_pll1_ref_sel", base, &imx_1443x_pll); clks[IMX8MN_AUDIO_PLL2] = imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx_1443x_pll); clks[IMX8MN_VIDEO_PLL1] = imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx_1443x_pll); - clks[IMX8MN_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_pll); + clks[IMX8MN_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx_1443x_dram_pll); clks[IMX8MN_GPU_PLL] = imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx_1416x_pll); clks[IMX8MN_VPU_PLL] = imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx_1416x_pll); clks[IMX8MN_ARM_PLL] = imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx_1416x_pll); clks[IMX8MN_SYS_PLL1] = imx_clk_fixed("sys_pll1", 800000000); clks[IMX8MN_SYS_PLL2] = imx_clk_fixed("sys_pll2", 1000000000); diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c index 5c458199060a..a6d31a7262ef 100644 --- a/drivers/clk/imx/clk-pll14xx.c +++ b/drivers/clk/imx/clk-pll14xx.c @@ -65,10 +65,17 @@ struct imx_pll14xx_clk imx_1443x_pll = { .type = PLL_1443X, .rate_table = imx_pll1443x_tbl, .rate_count = ARRAY_SIZE(imx_pll1443x_tbl), }; +struct imx_pll14xx_clk imx_1443x_dram_pll = { + .type = PLL_1443X, + .rate_table = imx_pll1443x_tbl, + .rate_count = ARRAY_SIZE(imx_pll1443x_tbl), + .flags = CLK_GET_RATE_NOCACHE, +}; + struct imx_pll14xx_clk imx_1416x_pll = { .type = PLL_1416X, .rate_table = imx_pll1416x_tbl, .rate_count = ARRAY_SIZE(imx_pll1416x_tbl), }; diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h index bc5bb6ac8636..81122c9ab842 100644 --- a/drivers/clk/imx/clk.h +++ b/drivers/clk/imx/clk.h @@ -50,10 +50,11 @@ struct imx_pll14xx_clk { int flags; }; extern struct imx_pll14xx_clk imx_1416x_pll; extern struct imx_pll14xx_clk imx_1443x_pll; +extern struct imx_pll14xx_clk imx_1443x_dram_pll; #define imx_clk_cpu(name, parent_name, div, mux, pll, step) \ imx_clk_hw_cpu(name, parent_name, div, mux, pll, step)->clk #define clk_register_gate2(dev, name, parent_name, flags, reg, bit_idx, \ From patchwork Thu Oct 31 21:50:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 11221919 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 14B491599 for ; Thu, 31 Oct 2019 21:50:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id F0A4A2173E for ; Thu, 31 Oct 2019 21:50:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729138AbfJaVui (ORCPT ); Thu, 31 Oct 2019 17:50:38 -0400 Received: from inva021.nxp.com ([92.121.34.21]:41856 "EHLO inva021.nxp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728561AbfJaVui (ORCPT ); Thu, 31 Oct 2019 17:50:38 -0400 Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 7D82A200578; Thu, 31 Oct 2019 22:50:35 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 620362001E6; Thu, 31 Oct 2019 22:50:35 +0100 (CET) Received: from fsr-ub1864-112.ea.freescale.net (fsr-ub1864-112.ea.freescale.net [10.171.82.98]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id 53A00205E9; Thu, 31 Oct 2019 22:50:34 +0100 (CET) From: Leonard Crestez To: Stephen Boyd , MyungJoo Ham , Kyungmin Park , Rob Herring Cc: "Rafael J. Wysocki" , Shawn Guo , Chanwoo Choi , Mark Rutland , Michael Turquette , =?utf-8?b?QXJ0dXIgxZp3aWdvxYQ=?= , Saravana Kannan , Angus Ainslie , Martin Kepplinger , Matthias Kaehlcke , Krzysztof Kozlowski , Alexandre Bailon , Georgi Djakov , Dong Aisheng , Abel Vesa , Jacky Bai , Anson Huang , Fabio Estevam , Viresh Kumar , devicetree@vger.kernel.org, linux-pm@vger.kernel.org, linux-clk@vger.kernel.org, linux-imx@nxp.com, kernel@pengutronix.de, linux-arm-kernel@lists.infradead.org Subject: [PATCH v3 3/6] dt-bindings: devfreq: Add bindings for imx ddr controller Date: Thu, 31 Oct 2019 23:50:24 +0200 Message-Id: X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Add devicetree bindings for the i.MX DDR Controller on imx8m series chips. It supports dynamic frequency switching between multiple data rates and this is exposed to Linux via the devfreq subsystem. Signed-off-by: Leonard Crestez --- .../devicetree/bindings/devfreq/imx-ddrc.yaml | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml diff --git a/Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml b/Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml new file mode 100644 index 000000000000..31db204e6845 --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/devfreq/imx-devfreq.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: i.MX DDR Controller + +maintainers: + - Leonard Crestez + +properties: + compatible: + items: + - enum: + - fsl,imx8mn-ddrc + - fsl,imx8mm-ddrc + - fsl,imx8mq-ddrc + - const: fsl,imx8m-ddrc + + reg: + maxItems: 1 + + clocks: + maxItems: 4 + + clock-names: + items: + - const: dram_core + - const: dram_pll + - const: dram_alt + - const: dram_apb + + operating-points-v2: true + + devfreq-events: + description: Phandle of PMU node + $ref: "/schemas/types.yaml#/definitions/phandle" + +required: + - reg + - compatible + - clocks + - clock-names + +additionalProperties: false + +examples: + - | + #include + ddrc: dram-controller@3d400000 { + compatible = "fsl,imx8mm-ddrc", "fsl,imx8m-ddrc"; + reg = <0x3d400000 0x400000>; + clock-names = "dram_core", "dram_pll", "dram_alt", "dram_apb"; + clocks = <&clk IMX8MM_CLK_DRAM_CORE>, + <&clk IMX8MM_DRAM_PLL>, + <&clk IMX8MM_CLK_DRAM_ALT>, + <&clk IMX8MM_CLK_DRAM_APB>; + operating-points-v2 = <&ddrc_opp_table>; + }; From patchwork Thu Oct 31 21:50:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 11221923 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A7EB617E6 for ; Thu, 31 Oct 2019 21:50:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 8ECD6208C0 for ; Thu, 31 Oct 2019 21:50:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729148AbfJaVui (ORCPT ); Thu, 31 Oct 2019 17:50:38 -0400 Received: from inva020.nxp.com ([92.121.34.13]:42418 "EHLO inva020.nxp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728829AbfJaVui (ORCPT ); Thu, 31 Oct 2019 17:50:38 -0400 Received: from inva020.nxp.com (localhost [127.0.0.1]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 981641A0112; Thu, 31 Oct 2019 22:50:36 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id 7EC3B1A00B6; Thu, 31 Oct 2019 22:50:36 +0100 (CET) Received: from fsr-ub1864-112.ea.freescale.net (fsr-ub1864-112.ea.freescale.net [10.171.82.98]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id 70EAE20605; Thu, 31 Oct 2019 22:50:35 +0100 (CET) From: Leonard Crestez To: Stephen Boyd , MyungJoo Ham , Kyungmin Park , Rob Herring Cc: "Rafael J. Wysocki" , Shawn Guo , Chanwoo Choi , Mark Rutland , Michael Turquette , =?utf-8?b?QXJ0dXIgxZp3aWdvxYQ=?= , Saravana Kannan , Angus Ainslie , Martin Kepplinger , Matthias Kaehlcke , Krzysztof Kozlowski , Alexandre Bailon , Georgi Djakov , Dong Aisheng , Abel Vesa , Jacky Bai , Anson Huang , Fabio Estevam , Viresh Kumar , devicetree@vger.kernel.org, linux-pm@vger.kernel.org, linux-clk@vger.kernel.org, linux-imx@nxp.com, kernel@pengutronix.de, linux-arm-kernel@lists.infradead.org Subject: [PATCH v3 4/6] PM / devfreq: Add dynamic scaling for imx ddr controller Date: Thu, 31 Oct 2019 23:50:25 +0200 Message-Id: <5fcf829265ecde1af32cb1369528c97361c76992.1572558427.git.leonard.crestez@nxp.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org Add driver for dynamic scaling the DDR Controller on imx8m chips. Actual frequency switching is implemented inside TF-A, this driver wraps the SMC calls and synchronizes the clk tree. The DRAM clocks on imx8m have the following structure (abridged): +----------+ |\ +------+ | dram_pll |-------|M| dram_core | | +----------+ |U|---------->| D | /--|X| | D | dram_alt_root | |/ | R | | | C | +---------+ | | |FIX DIV/4| | | +---------+ | | composite: | | | +----------+ | | | | dram_alt |----/ | | +----------+ | | | dram_apb |-------------------->| | +----------+ +------+ The dram_pll is used for higher rates and dram_alt is used for lower rates. The dram_alt and dram_apb clocks are "imx composite" and their parent can also be modified. This driver will prepare/enable the new parents ahead of switching (so that the expected roots are enabled) and afterwards it will call clk_set_parent to ensure the parents in clock framework are up-to-date. The driver relies on dram_pll dram_alt and dram_apb being marked with CLK_GET_RATE_NOCACHE for rate updates. Signed-off-by: Leonard Crestez --- drivers/devfreq/Makefile | 1 + drivers/devfreq/imx-ddrc.c | 430 +++++++++++++++++++++++++++++++++++++ 2 files changed, 431 insertions(+) create mode 100644 drivers/devfreq/imx-ddrc.c diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 338ae8440db6..1ac92614b6aa 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -7,10 +7,11 @@ obj-$(CONFIG_DEVFREQ_GOV_POWERSAVE) += governor_powersave.o obj-$(CONFIG_DEVFREQ_GOV_USERSPACE) += governor_userspace.o obj-$(CONFIG_DEVFREQ_GOV_PASSIVE) += governor_passive.o # DEVFREQ Drivers obj-$(CONFIG_ARM_EXYNOS_BUS_DEVFREQ) += exynos-bus.o +obj-$(CONFIG_ARM_IMX_DEVFREQ) += imx-ddrc.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o obj-$(CONFIG_ARM_TEGRA20_DEVFREQ) += tegra20-devfreq.o # DEVFREQ Event Drivers diff --git a/drivers/devfreq/imx-ddrc.c b/drivers/devfreq/imx-ddrc.c new file mode 100644 index 000000000000..3ce51614ecab --- /dev/null +++ b/drivers/devfreq/imx-ddrc.c @@ -0,0 +1,430 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMX_SIP_DDR_DVFS 0xc2000004 + +/* Values starting from 0 switch to specific frequency */ +#define IMX_SIP_DDR_FREQ_SET_HIGH 0x00 + +/* Deprecated after moving IRQ handling to ATF */ +#define IMX_SIP_DDR_DVFS_WAIT_CHANGE 0x0F + +/* Query available frequencies. */ +#define IMX_SIP_DDR_DVFS_GET_FREQ_COUNT 0x10 +#define IMX_SIP_DDR_DVFS_GET_FREQ_INFO 0x11 + +/* + * This should be in a 1:1 mapping with devicetree OPPs but + * firmware provides additional info. + */ +struct imx_ddrc_freq { + unsigned long rate; + unsigned long smcarg; + int dram_core_parent_index; + int dram_alt_parent_index; + int dram_apb_parent_index; +}; + +/* Hardware limitation */ +#define IMX_DDRC_MAX_FREQ_COUNT 4 + +/* + * imx DRAM controller + * + * imx DRAM controller clocks have the following structure (abridged): + * + * +----------+ |\ +------+ + * | dram_pll |-------|M| dram_core | | + * +----------+ |U|---------->| D | + * /--|X| | D | + * dram_alt_root | |/ | R | + * | | C | + * +---------+ | | + * |FIX DIV/4| | | + * +---------+ | | + * composite: | | | + * +----------+ | | | + * | dram_alt |----/ | | + * +----------+ | | + * | dram_apb |-------------------->| | + * +----------+ +------+ + * + * The dram_pll is used for higher rates and dram_alt is used for lower rates. + * + * Frequency switching is implemented in TF-A (via SMC call) and can change the + * configuration of the clocks, including mux parents. The dram_alt and + * dram_apb clocks are "imx composite" and their parent can change too. + * + * We need to prepare/enable the new mux parents head of switching and update + * their information afterwards. + */ +struct imx_ddrc { + struct devfreq_dev_profile profile; + struct devfreq *devfreq; + + /* For frequency switching: */ + struct clk *dram_core; + struct clk *dram_pll; + struct clk *dram_alt; + struct clk *dram_apb; + + int freq_count; + struct imx_ddrc_freq freq_table[IMX_DDRC_MAX_FREQ_COUNT]; +}; + +static struct imx_ddrc_freq *imx_ddrc_find_freq(struct imx_ddrc *priv, + unsigned long rate) +{ + int i; + + /* + * Firmware reports values in MT/s, so we round-down from Hz + * Rounding is extra generous to ensure a match. + */ + rate = DIV_ROUND_CLOSEST(rate, 250000); + for (i = 0; i < priv->freq_count; ++i) { + struct imx_ddrc_freq *freq = &priv->freq_table[i]; + if (freq->rate == rate || + freq->rate + 1 == rate || + freq->rate - 1 == rate) + return freq; + } + + return NULL; +} + +static void imx_ddrc_smc_set_freq(int target_freq) +{ + struct arm_smccc_res res; + u32 online_cpus = 0; + int cpu; + + local_irq_disable(); + + for_each_online_cpu(cpu) + online_cpus |= (1 << (cpu * 8)); + + /* change the ddr freqency */ + arm_smccc_smc(IMX_SIP_DDR_DVFS, target_freq, online_cpus, + 0, 0, 0, 0, 0, &res); + + local_irq_enable(); +} + +struct clk *clk_get_parent_by_index(struct clk *clk, int index) +{ + struct clk_hw *hw; + + hw = clk_hw_get_parent_by_index(__clk_get_hw(clk), index); + + return hw ? hw->clk : NULL; +} + +static int imx_ddrc_set_freq(struct device *dev, struct imx_ddrc_freq *freq) +{ + struct imx_ddrc *priv = dev_get_drvdata(dev); + struct clk *new_dram_core_parent; + struct clk *new_dram_alt_parent; + struct clk *new_dram_apb_parent; + int ret; + + new_dram_core_parent = clk_get_parent_by_index( + priv->dram_core, freq->dram_core_parent_index - 1); + new_dram_alt_parent = clk_get_parent_by_index( + priv->dram_alt, freq->dram_alt_parent_index - 1); + new_dram_apb_parent = clk_get_parent_by_index( + priv->dram_apb, freq->dram_apb_parent_index - 1); + + /* increase reference counts and ensure clks are ON before switch */ + ret = clk_prepare_enable(new_dram_core_parent); + if (ret) { + dev_err(dev, "failed enable new dram_core parent: %d\n", ret); + goto out; + } + ret = clk_prepare_enable(new_dram_alt_parent); + if (ret) { + dev_err(dev, "failed enable new dram_alt parent: %d\n", ret); + goto out_dis_core; + } + ret = clk_prepare_enable(new_dram_apb_parent); + if (ret) { + dev_err(dev, "failed enable new dram_apb parent: %d\n", ret); + goto out_dis_alt; + } + + imx_ddrc_smc_set_freq(freq->smcarg); + + /* update parents in clk tree after switch. */ + ret = clk_set_parent(priv->dram_core, new_dram_core_parent); + if (ret) + dev_err(dev, "failed set dram_core parent: %d\n", ret); + if (new_dram_alt_parent) { + ret = clk_set_parent(priv->dram_alt, new_dram_alt_parent); + if (ret) + dev_err(dev, "failed set dram_alt parent: %d\n", ret); + } + if (new_dram_apb_parent) { + ret = clk_set_parent(priv->dram_apb, new_dram_apb_parent); + if (ret) + dev_err(dev, "failed set dram_apb parent: %d\n", ret); + } + + /* + * Explicitly refresh dram PLL rate. + * + * Even if it's marked with CLK_GET_RATE_NOCACHE the rate will not be + * automatically refreshed when clk_get_rate is called on children. + */ + clk_get_rate(priv->dram_pll); + + /* + * clk_set_parent transfer the reference count from old parent. + * now we drop extra reference counts used during the switch + */ + clk_disable_unprepare(new_dram_apb_parent); +out_dis_alt: + clk_disable_unprepare(new_dram_alt_parent); +out_dis_core: + clk_disable_unprepare(new_dram_core_parent); +out: + return ret; +} + +static int imx_ddrc_target(struct device *dev, unsigned long *freq, u32 flags) +{ + struct imx_ddrc *priv = dev_get_drvdata(dev); + struct imx_ddrc_freq *freq_info; + struct dev_pm_opp *new_opp; + unsigned long old_freq, new_freq; + int ret; + + new_opp = devfreq_recommended_opp(dev, freq, flags); + if (IS_ERR(new_opp)) { + ret = PTR_ERR(new_opp); + dev_err(dev, "failed to get recommended opp: %d\n", ret); + return ret; + } + dev_pm_opp_put(new_opp); + + old_freq = clk_get_rate(priv->dram_core); + if (*freq == old_freq) + return 0; + + freq_info = imx_ddrc_find_freq(priv, *freq); + if (!freq_info) + return -EINVAL; + ret = imx_ddrc_set_freq(dev, freq_info); + + /* Also read back the clk rate to verify switch was correct */ + new_freq = clk_get_rate(priv->dram_core); + if (ret || *freq != new_freq) + dev_err(dev, "ddrc failed to change freq %lu to %lu: now at %lu\n", + old_freq, *freq, new_freq); + else + dev_dbg(dev, "ddrc changed freq %lu to %lu\n", + old_freq, *freq); + + return ret; +} + +static int imx_ddrc_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct imx_ddrc *priv = dev_get_drvdata(dev); + + *freq = clk_get_rate(priv->dram_core); + + return 0; +} + +static int imx_ddrc_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct imx_ddrc *priv = dev_get_drvdata(dev); + + stat->busy_time = 0; + stat->total_time = 0; + stat->current_frequency = clk_get_rate(priv->dram_core); + + return 0; +} + +static int imx_ddrc_init_freq_info(struct device *dev) +{ + struct imx_ddrc *priv = dev_get_drvdata(dev); + struct arm_smccc_res res; + int index; + + /* + * An error here means DDR DVFS API not supported by firmware + */ + arm_smccc_smc(IMX_SIP_DDR_DVFS, IMX_SIP_DDR_DVFS_GET_FREQ_COUNT, + 0, 0, 0, 0, 0, 0, &res); + priv->freq_count = res.a0; + if (priv->freq_count <= 0 || priv->freq_count > IMX_DDRC_MAX_FREQ_COUNT) + return -ENODEV; + + for (index = 0; index < priv->freq_count; ++index) { + struct imx_ddrc_freq *freq = &priv->freq_table[index]; + + arm_smccc_smc(IMX_SIP_DDR_DVFS, IMX_SIP_DDR_DVFS_GET_FREQ_INFO, + index, 0, 0, 0, 0, 0, &res); + /* Result should be strictly positive */ + if ((long)res.a0 <= 0) + return -ENODEV; + + freq->rate = res.a0; + freq->smcarg = index; + freq->dram_core_parent_index = res.a1; + freq->dram_alt_parent_index = res.a2; + freq->dram_apb_parent_index = res.a3; + + /* dram_core has 2 options: dram_pll or dram_alt_root */ + if (freq->dram_core_parent_index != 1 && + freq->dram_core_parent_index != 2) + return -ENODEV; + /* dram_apb and dram_alt have exactly 8 possible parents */ + if (freq->dram_alt_parent_index > 8 || + freq->dram_apb_parent_index > 8) + return -ENODEV; + /* dram_core from alt requires explicit dram_alt parent */ + if (freq->dram_core_parent_index == 2 && + freq->dram_alt_parent_index == 0) + return -ENODEV; + } + + return 0; +} + +/* imx_ddrc_check_opps() - disable OPPs not supported by firmware */ +static int imx_ddrc_check_opps(struct device *dev) +{ + struct imx_ddrc *priv = dev_get_drvdata(dev); + struct imx_ddrc_freq *freq_info; + struct dev_pm_opp *opp; + unsigned long freq; + + freq = ULONG_MAX; + while (true) { + opp = dev_pm_opp_find_freq_floor(dev, &freq); + if (opp == ERR_PTR(-ERANGE)) + break; + if (IS_ERR(opp)) { + dev_err(dev, "Failed enumerating OPPs: %ld\n", + PTR_ERR(opp)); + return PTR_ERR(opp); + } + dev_pm_opp_put(opp); + + freq_info = imx_ddrc_find_freq(priv, freq); + if (!freq_info) { + dev_info(dev, "Disable unsupported OPP %luHz %luMT/s\n", + freq, DIV_ROUND_CLOSEST(freq, 250000)); + dev_pm_opp_disable(dev, freq); + } + + freq--; + } + + return 0; +} + +static void imx_ddrc_exit(struct device *dev) +{ + dev_pm_opp_of_remove_table(dev); +} + +static int imx_ddrc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imx_ddrc *priv; + struct device_node *events_node; + const char *gov = DEVFREQ_GOV_USERSPACE; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + ret = imx_ddrc_init_freq_info(dev); + if (ret) { + dev_err(dev, "failed to init firmware freq info: %d\n", ret); + return ret; + } + + priv->dram_core = devm_clk_get(dev, "dram_core"); + priv->dram_pll = devm_clk_get(dev, "dram_pll"); + priv->dram_alt = devm_clk_get(dev, "dram_alt"); + priv->dram_apb = devm_clk_get(dev, "dram_apb"); + if (IS_ERR(priv->dram_core) || + IS_ERR(priv->dram_pll) || + IS_ERR(priv->dram_alt) || + IS_ERR(priv->dram_apb)) { + ret = PTR_ERR(priv->devfreq); + dev_err(dev, "failed to fetch clocks: %d\n", ret); + return ret; + } + + ret = dev_pm_opp_of_add_table(dev); + if (ret < 0) { + dev_err(dev, "failed to get OPP table\n"); + return ret; + } + + ret = imx_ddrc_check_opps(dev); + if (ret < 0) + goto err; + + priv->profile.polling_ms = 1000; + priv->profile.target = imx_ddrc_target; + priv->profile.get_dev_status = imx_ddrc_get_dev_status; + priv->profile.exit = imx_ddrc_exit; + priv->profile.get_cur_freq = imx_ddrc_get_cur_freq; + priv->profile.initial_freq = clk_get_rate(priv->dram_core); + + priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, + gov, NULL); + if (IS_ERR(priv->devfreq)) { + ret = PTR_ERR(priv->devfreq); + dev_err(dev, "failed to add devfreq device: %d\n", ret); + goto err; + } + + return 0; + +err: + dev_pm_opp_of_remove_table(dev); + return ret; +} + +static const struct of_device_id imx_ddrc_of_match[] = { + { .compatible = "fsl,imx8m-ddrc", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, imx_ddrc_of_match); + +static struct platform_driver imx_ddrc_platdrv = { + .probe = imx_ddrc_probe, + .driver = { + .name = "imx-ddrc-devfreq", + .of_match_table = of_match_ptr(imx_ddrc_of_match), + }, +}; +module_platform_driver(imx_ddrc_platdrv); + +MODULE_DESCRIPTION("i.MX DDR controller frequency driver"); +MODULE_AUTHOR("Leonard Crestez "); +MODULE_LICENSE("GPL v2"); From patchwork Thu Oct 31 21:50:26 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 11221929 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 20D191599 for ; Thu, 31 Oct 2019 21:50:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 08B0D21734 for ; Thu, 31 Oct 2019 21:50:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729152AbfJaVuk (ORCPT ); Thu, 31 Oct 2019 17:50:40 -0400 Received: from inva021.nxp.com ([92.121.34.21]:41914 "EHLO inva021.nxp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729136AbfJaVuj (ORCPT ); Thu, 31 Oct 2019 17:50:39 -0400 Received: from inva021.nxp.com (localhost [127.0.0.1]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id B7DC920056E; Thu, 31 Oct 2019 22:50:37 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva021.eu-rdc02.nxp.com (Postfix) with ESMTP id 9C26C2001E6; Thu, 31 Oct 2019 22:50:37 +0100 (CET) Received: from fsr-ub1864-112.ea.freescale.net (fsr-ub1864-112.ea.freescale.net [10.171.82.98]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id 8DA2D20605; Thu, 31 Oct 2019 22:50:36 +0100 (CET) From: Leonard Crestez To: Stephen Boyd , MyungJoo Ham , Kyungmin Park , Rob Herring Cc: "Rafael J. Wysocki" , Shawn Guo , Chanwoo Choi , Mark Rutland , Michael Turquette , =?utf-8?b?QXJ0dXIgxZp3aWdvxYQ=?= , Saravana Kannan , Angus Ainslie , Martin Kepplinger , Matthias Kaehlcke , Krzysztof Kozlowski , Alexandre Bailon , Georgi Djakov , Dong Aisheng , Abel Vesa , Jacky Bai , Anson Huang , Fabio Estevam , Viresh Kumar , devicetree@vger.kernel.org, linux-pm@vger.kernel.org, linux-clk@vger.kernel.org, linux-imx@nxp.com, kernel@pengutronix.de, linux-arm-kernel@lists.infradead.org Subject: [PATCH v3 5/6] PM / devfreq: imx-ddrc: Measure bandwidth with perf Date: Thu, 31 Oct 2019 23:50:26 +0200 Message-Id: <259544688d43fbe66290d1b4f06826f57c8f18a0.1572558427.git.leonard.crestez@nxp.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org The imx8m ddrc has a performance monitoring block attached which can be used to measure bandwidth usage and automatically adjust frequency. There is already a perf driver for that block so instead of implementing a devfreq events driver use the in-kernel perf API to implement get_dev_status directly. Signed-off-by: Leonard Crestez --- drivers/devfreq/imx-ddrc.c | 146 ++++++++++++++++++++++++++++++++++++- 1 file changed, 143 insertions(+), 3 deletions(-) diff --git a/drivers/devfreq/imx-ddrc.c b/drivers/devfreq/imx-ddrc.c index 3ce51614ecab..c74dec7fbfe4 100644 --- a/drivers/devfreq/imx-ddrc.c +++ b/drivers/devfreq/imx-ddrc.c @@ -11,10 +11,13 @@ #include #include #include #include +#include +#include + #define IMX_SIP_DDR_DVFS 0xc2000004 /* Values starting from 0 switch to specific frequency */ #define IMX_SIP_DDR_FREQ_SET_HIGH 0x00 @@ -80,10 +83,22 @@ struct imx_ddrc { struct clk *dram_alt; struct clk *dram_apb; int freq_count; struct imx_ddrc_freq freq_table[IMX_DDRC_MAX_FREQ_COUNT]; + + /* For measuring load with perf events: */ + struct platform_device *pmu_pdev; + struct pmu *pmu; + + struct perf_event_attr rd_event_attr; + struct perf_event_attr wr_event_attr; + struct perf_event *rd_event; + struct perf_event *wr_event; + + u64 last_rd_val, last_rd_ena, last_rd_run; + u64 last_wr_val, last_wr_ena, last_wr_run; }; static struct imx_ddrc_freq *imx_ddrc_find_freq(struct imx_ddrc *priv, unsigned long rate) { @@ -247,19 +262,128 @@ static int imx_ddrc_get_cur_freq(struct device *dev, unsigned long *freq) return 0; } static int imx_ddrc_get_dev_status(struct device *dev, - struct devfreq_dev_status *stat) + struct devfreq_dev_status *stat) { struct imx_ddrc *priv = dev_get_drvdata(dev); - stat->busy_time = 0; - stat->total_time = 0; stat->current_frequency = clk_get_rate(priv->dram_core); + if (priv->rd_event && priv->wr_event) { + u64 rd_delta, rd_val, rd_ena, rd_run; + u64 wr_delta, wr_val, wr_ena, wr_run; + + rd_val = perf_event_read_value(priv->rd_event, + &rd_ena, &rd_run); + wr_val = perf_event_read_value(priv->wr_event, + &wr_ena, &wr_run); + + rd_delta = (rd_val - priv->last_rd_val) * + (rd_ena - priv->last_rd_ena); + do_div(rd_delta, rd_run - priv->last_rd_run); + priv->last_rd_val = rd_val; + priv->last_rd_ena = rd_ena; + priv->last_rd_run = rd_run; + + wr_delta = (wr_val - priv->last_wr_val) * + (wr_ena - priv->last_wr_ena); + do_div(wr_delta, wr_run - priv->last_wr_run); + priv->last_wr_val = wr_val; + priv->last_wr_ena = wr_ena; + priv->last_wr_run = wr_run; + + /* magic numbers, possibly wrong */ + stat->busy_time = 4 * (rd_delta + wr_delta); + stat->total_time = stat->current_frequency; + } else { + stat->busy_time = 0; + stat->total_time = 0; + } + + return 0; +} + +static int imx_ddrc_perf_disable(struct imx_ddrc *priv) +{ + /* release and set to NULL */ + if (!IS_ERR_OR_NULL(priv->rd_event)) + perf_event_release_kernel(priv->rd_event); + if (!IS_ERR_OR_NULL(priv->wr_event)) + perf_event_release_kernel(priv->wr_event); + priv->rd_event = NULL; + priv->wr_event = NULL; + + return 0; +} + +static int imx_ddrc_perf_enable(struct imx_ddrc *priv) +{ + int ret; + + priv->rd_event_attr.size = sizeof(priv->rd_event_attr); + priv->rd_event_attr.type = priv->pmu->type; + priv->rd_event_attr.config = 0x2a; + + priv->rd_event = perf_event_create_kernel_counter( + &priv->rd_event_attr, 0, NULL, NULL, NULL); + if (IS_ERR(priv->rd_event)) { + ret = PTR_ERR(priv->rd_event); + goto err; + } + + priv->wr_event_attr.size = sizeof(priv->wr_event_attr); + priv->wr_event_attr.type = priv->pmu->type; + priv->wr_event_attr.config = 0x2b; + + priv->wr_event = perf_event_create_kernel_counter( + &priv->wr_event_attr, 0, NULL, NULL, NULL); + if (IS_ERR(priv->wr_event)) { + ret = PTR_ERR(priv->wr_event); + goto err; + } + return 0; + +err: + imx_ddrc_perf_disable(priv); + return ret; +} + +static int imx_ddrc_init_events(struct device *dev, + struct device_node *events_node) +{ + struct imx_ddrc *priv = dev_get_drvdata(dev); + struct device_driver *driver; + + /* + * We need pmu->type for perf_event_attr but there is no API for + * mapping device_node to pmu. Fetch private data for imx-ddr-pmu and + * cast that to a struct pmu instead. + */ + priv->pmu_pdev = of_find_device_by_node(events_node); + if (!priv->pmu_pdev) + return -EPROBE_DEFER; + driver = priv->pmu_pdev->dev.driver; + if (!driver) + return -EPROBE_DEFER; + if (strcmp(driver->name, "imx-ddr-pmu")) { + dev_warn(dev, "devfreq-events node %pOF has unexpected driver %s\n", + events_node, driver->name); + return -ENODEV; + } + + priv->pmu = platform_get_drvdata(priv->pmu_pdev); + if (!priv->pmu) { + dev_err(dev, "devfreq-events device missing private data\n"); + return -EINVAL; + } + + dev_dbg(dev, "events from pmu %s\n", priv->pmu->name); + + return imx_ddrc_perf_enable(priv); } static int imx_ddrc_init_freq_info(struct device *dev) { struct imx_ddrc *priv = dev_get_drvdata(dev); @@ -340,10 +464,15 @@ static int imx_ddrc_check_opps(struct device *dev) return 0; } static void imx_ddrc_exit(struct device *dev) { + struct imx_ddrc *priv = dev_get_drvdata(dev); + + imx_ddrc_perf_disable(priv); + platform_device_put(priv->pmu_pdev); + dev_pm_opp_of_remove_table(dev); } static int imx_ddrc_probe(struct platform_device *pdev) { @@ -363,10 +492,19 @@ static int imx_ddrc_probe(struct platform_device *pdev) if (ret) { dev_err(dev, "failed to init firmware freq info: %d\n", ret); return ret; } + events_node = of_parse_phandle(dev->of_node, "devfreq-events", 0); + if (events_node) { + ret = imx_ddrc_init_events(dev, events_node); + of_node_put(events_node); + if (ret) + goto err; + gov = DEVFREQ_GOV_SIMPLE_ONDEMAND; + } + priv->dram_core = devm_clk_get(dev, "dram_core"); priv->dram_pll = devm_clk_get(dev, "dram_pll"); priv->dram_alt = devm_clk_get(dev, "dram_alt"); priv->dram_apb = devm_clk_get(dev, "dram_apb"); if (IS_ERR(priv->dram_core) || @@ -404,10 +542,12 @@ static int imx_ddrc_probe(struct platform_device *pdev) } return 0; err: + imx_ddrc_perf_disable(priv); + platform_device_put(priv->pmu_pdev); dev_pm_opp_of_remove_table(dev); return ret; } static const struct of_device_id imx_ddrc_of_match[] = { From patchwork Thu Oct 31 21:50:27 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leonard Crestez X-Patchwork-Id: 11221933 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E3748913 for ; Thu, 31 Oct 2019 21:50:41 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id CB141217D9 for ; Thu, 31 Oct 2019 21:50:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729169AbfJaVul (ORCPT ); Thu, 31 Oct 2019 17:50:41 -0400 Received: from inva020.nxp.com ([92.121.34.13]:42456 "EHLO inva020.nxp.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728829AbfJaVuk (ORCPT ); Thu, 31 Oct 2019 17:50:40 -0400 Received: from inva020.nxp.com (localhost [127.0.0.1]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id D11281A059C; Thu, 31 Oct 2019 22:50:38 +0100 (CET) Received: from inva024.eu-rdc02.nxp.com (inva024.eu-rdc02.nxp.com [134.27.226.22]) by inva020.eu-rdc02.nxp.com (Postfix) with ESMTP id B98B81A0214; Thu, 31 Oct 2019 22:50:38 +0100 (CET) Received: from fsr-ub1864-112.ea.freescale.net (fsr-ub1864-112.ea.freescale.net [10.171.82.98]) by inva024.eu-rdc02.nxp.com (Postfix) with ESMTP id AB5102062B; Thu, 31 Oct 2019 22:50:37 +0100 (CET) From: Leonard Crestez To: Stephen Boyd , MyungJoo Ham , Kyungmin Park , Rob Herring Cc: "Rafael J. Wysocki" , Shawn Guo , Chanwoo Choi , Mark Rutland , Michael Turquette , =?utf-8?b?QXJ0dXIgxZp3aWdvxYQ=?= , Saravana Kannan , Angus Ainslie , Martin Kepplinger , Matthias Kaehlcke , Krzysztof Kozlowski , Alexandre Bailon , Georgi Djakov , Dong Aisheng , Abel Vesa , Jacky Bai , Anson Huang , Fabio Estevam , Viresh Kumar , devicetree@vger.kernel.org, linux-pm@vger.kernel.org, linux-clk@vger.kernel.org, linux-imx@nxp.com, kernel@pengutronix.de, linux-arm-kernel@lists.infradead.org Subject: [PATCH v3 6/6] arm64: dts: imx8m: Add ddr controller nodes Date: Thu, 31 Oct 2019 23:50:27 +0200 Message-Id: <44dcab5a136f5b046092e6ed456d8e206413059f.1572558427.git.leonard.crestez@nxp.com> X-Mailer: git-send-email 2.17.1 In-Reply-To: References: In-Reply-To: References: X-Virus-Scanned: ClamAV using ClamSMTP Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org This is used by the imx-ddrc devfreq driver to implement dynamic frequency scaling of DRAM. Add a devfreq-event link to the dram PMU in order to support on-demand scaling of ddrc based on measured dram bandwidth usage. Support for proactive scaling via interconnect will come later. The high-performance bus masters which need that (display, vpu, gpu) are not yet enabled in upstream anyway. Signed-off-by: Leonard Crestez --- arch/arm64/boot/dts/freescale/imx8mm-evk.dts | 18 ++++++++++++++ arch/arm64/boot/dts/freescale/imx8mm.dtsi | 17 ++++++++++++- .../boot/dts/freescale/imx8mn-ddr4-evk.dts | 18 ++++++++++++++ arch/arm64/boot/dts/freescale/imx8mn.dtsi | 16 ++++++++++++- arch/arm64/boot/dts/freescale/imx8mq-evk.dts | 24 +++++++++++++++++++ arch/arm64/boot/dts/freescale/imx8mq.dtsi | 16 ++++++++++++- 6 files changed, 106 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk.dts b/arch/arm64/boot/dts/freescale/imx8mm-evk.dts index 4f5e408d6e6a..be9abd8e4478 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx8mm-evk.dts @@ -69,16 +69,34 @@ simple-audio-card,codec { sound-dai = <&wm8524>; clocks = <&clk IMX8MM_CLK_SAI3_ROOT>; }; }; + + ddrc_opp_table: ddrc-opp-table { + compatible = "operating-points-v2"; + + opp-25M { + opp-hz = /bits/ 64 <25000000>; + }; + opp-100M { + opp-hz = /bits/ 64 <100000000>; + }; + opp-750M { + opp-hz = /bits/ 64 <750000000>; + }; + }; }; &A53_0 { cpu-supply = <&buck2_reg>; }; +&ddrc { + operating-points-v2 = <&ddrc_opp_table>; +}; + &fec1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_fec1>; phy-mode = "rgmii-id"; phy-handle = <ðphy0>; diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi index 6edbdfe2d0d7..5404870d80d5 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi @@ -856,11 +856,26 @@ #interrupt-cells = <3>; interrupt-controller; interrupts = ; }; - ddr-pmu@3d800000 { + ddrc: dram-controller@3d400000 { + compatible = "fsl,imx8mm-ddrc", "fsl,imx8m-ddrc"; + reg = <0x3d400000 0x400000>; + clock-names = "dram_core", + "dram_pll", + "dram_alt", + "dram_apb"; + clocks = <&clk IMX8MM_CLK_DRAM_CORE>, + <&clk IMX8MM_DRAM_PLL>, + <&clk IMX8MM_CLK_DRAM_ALT>, + <&clk IMX8MM_CLK_DRAM_APB>; + devfreq-events = <&ddr_pmu>; + operating-points-v2 = <&ddrc_opp_table>; + }; + + ddr_pmu: ddr-pmu@3d800000 { compatible = "fsl,imx8mm-ddr-pmu", "fsl,imx8m-ddr-pmu"; reg = <0x3d800000 0x400000>; interrupt-parent = <&gic>; interrupts = ; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mn-ddr4-evk.dts b/arch/arm64/boot/dts/freescale/imx8mn-ddr4-evk.dts index 071949412caf..ab2060667671 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn-ddr4-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx8mn-ddr4-evk.dts @@ -9,16 +9,34 @@ #include "imx8mn-evk.dtsi" / { model = "NXP i.MX8MNano DDR4 EVK board"; compatible = "fsl,imx8mn-ddr4-evk", "fsl,imx8mn"; + + ddrc_opp_table: ddrc-opp-table { + compatible = "operating-points-v2"; + + opp-25M { + opp-hz = /bits/ 64 <25000000>; + }; + opp-100M { + opp-hz = /bits/ 64 <100000000>; + }; + opp-600M { + opp-hz = /bits/ 64 <600000000>; + }; + }; }; &A53_0 { cpu-supply = <&buck2_reg>; }; +&ddrc { + operating-points-v2 = <&ddrc_opp_table>; +}; + &i2c1 { pmic@4b { compatible = "rohm,bd71847"; reg = <0x4b>; pinctrl-0 = <&pinctrl_pmic>; diff --git a/arch/arm64/boot/dts/freescale/imx8mn.dtsi b/arch/arm64/boot/dts/freescale/imx8mn.dtsi index e91625063f8e..344dd777635f 100644 --- a/arch/arm64/boot/dts/freescale/imx8mn.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mn.dtsi @@ -757,11 +757,25 @@ #interrupt-cells = <3>; interrupt-controller; interrupts = ; }; - ddr-pmu@3d800000 { + ddrc: dram-controller@3d400000 { + compatible = "fsl,imx8mn-ddrc", "fsl,imx8m-ddrc"; + reg = <0x3d400000 0x400000>; + clock-names = "dram_core", + "dram_pll", + "dram_alt", + "dram_apb"; + clocks = <&clk IMX8MN_CLK_DRAM_CORE>, + <&clk IMX8MN_DRAM_PLL>, + <&clk IMX8MN_CLK_DRAM_ALT>, + <&clk IMX8MN_CLK_DRAM_APB>; + devfreq-events = <&ddr_pmu>; + }; + + ddr_pmu: ddr-pmu@3d800000 { compatible = "fsl,imx8mn-ddr-pmu", "fsl,imx8m-ddr-pmu"; reg = <0x3d800000 0x400000>; interrupts = ; }; }; diff --git a/arch/arm64/boot/dts/freescale/imx8mq-evk.dts b/arch/arm64/boot/dts/freescale/imx8mq-evk.dts index c36685916683..fc4c12ab8991 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq-evk.dts +++ b/arch/arm64/boot/dts/freescale/imx8mq-evk.dts @@ -85,10 +85,30 @@ link_codec: simple-audio-card,codec { sound-dai = <&wm8524>; clocks = <&clk IMX8MQ_CLK_SAI2_ROOT>; }; }; + + ddrc_opp_table: ddrc-opp-table { + compatible = "operating-points-v2"; + + opp-25M { + opp-hz = /bits/ 64 <25000000>; + }; + opp-100M { + opp-hz = /bits/ 64 <100000000>; + }; + /* + * On imx8mq B0 PLL can't be bypassed so low bus is 166M + */ + opp-166M { + opp-hz = /bits/ 64 <166935483>; + }; + opp-800M { + opp-hz = /bits/ 64 <800000000>; + }; + }; }; &A53_0 { cpu-supply = <&buck2_reg>; }; @@ -103,10 +123,14 @@ &A53_3 { cpu-supply = <&buck2_reg>; }; +&ddrc { + operating-points-v2 = <&ddrc_opp_table>; +}; + &fec1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_fec1>; phy-mode = "rgmii-id"; phy-handle = <ðphy0>; diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi b/arch/arm64/boot/dts/freescale/imx8mq.dtsi index 7f9319452b58..6ef1af41ef68 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi @@ -1111,11 +1111,25 @@ interrupt-controller; interrupts = ; interrupt-parent = <&gic>; }; - ddr-pmu@3d800000 { + ddrc: dram-controller@3d400000 { + compatible = "fsl,imx8mq-ddrc", "fsl,imx8m-ddrc"; + reg = <0x3d400000 0x400000>; + clock-names = "dram_core", + "dram_pll", + "dram_alt", + "dram_apb"; + clocks = <&clk IMX8MQ_CLK_DRAM_CORE>, + <&clk IMX8MQ_DRAM_PLL_OUT>, + <&clk IMX8MQ_CLK_DRAM_ALT>, + <&clk IMX8MQ_CLK_DRAM_APB>; + devfreq-events = <&ddr_pmu>; + }; + + ddr_pmu: ddr-pmu@3d800000 { compatible = "fsl,imx8mq-ddr-pmu", "fsl,imx8m-ddr-pmu"; reg = <0x3d800000 0x400000>; interrupt-parent = <&gic>; interrupts = ; };