From a1c62566fcc5ce8476a66a51920af7ddf45fc2d3 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 12 Sep 2019 14:53:50 +0300 Subject: [PATCH 01/71] imx_v6_v7_defconfig: Build USB_CONFIGFS into kernel Some imx chips don't have chips and a limited number of USB controllers. For such cases NXP suggests configuring the USB controller as an ethernet gadget for network boot testing. Switch USB_CONFIGFS to be built into the kernel so that we can configure the ethernet gadget without needing modules. Signed-off-by: Leonard Crestez Reviewed-by: Peter Chen --- arch/arm/configs/imx_v6_v7_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/configs/imx_v6_v7_defconfig b/arch/arm/configs/imx_v6_v7_defconfig index a53b29251ed4..220069fd1a22 100644 --- a/arch/arm/configs/imx_v6_v7_defconfig +++ b/arch/arm/configs/imx_v6_v7_defconfig @@ -332,7 +332,7 @@ CONFIG_NOP_USB_XCEIV=y CONFIG_USB_MXS_PHY=y CONFIG_USB_GADGET=y CONFIG_USB_FSL_USB2=y -CONFIG_USB_CONFIGFS=m +CONFIG_USB_CONFIGFS=y CONFIG_USB_CONFIGFS_SERIAL=y CONFIG_USB_CONFIGFS_ACM=y CONFIG_USB_CONFIGFS_OBEX=y -- GitLab From 0ec82876868445e2965105ec6b8b096548b10db2 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Wed, 4 Sep 2019 10:38:46 +0300 Subject: [PATCH 02/71] firmware: imx: warn on unexpected RX The imx_scu_call_rpc function function returns the result inside the same "msg" struct containing the transmitted message. This is implemented by holding a pointer to msg (which is usually on the stack) in sc_imx_rpc and writing to it from imx_scu_rx_callback. This means that if the have_resp parameter is incorrect or SCU sends an unexpected for any reason the most likely result is kernel stack corruption. Fix this by only setting sc_imx_rpc.msg for the duration of the imx_scu_call_rpc call and warning in imx_scu_rx_callback if unset. Print the unexpected response data to help debugging. Signed-off-by: Leonard Crestez Acked-by: Anson Huang --- drivers/firmware/imx/imx-scu.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c index 04a24a863d6e..869be7a5172c 100644 --- a/drivers/firmware/imx/imx-scu.c +++ b/drivers/firmware/imx/imx-scu.c @@ -107,6 +107,12 @@ static void imx_scu_rx_callback(struct mbox_client *c, void *msg) struct imx_sc_rpc_msg *hdr; u32 *data = msg; + if (!sc_ipc->msg) { + dev_warn(sc_ipc->dev, "unexpected rx idx %d 0x%08x, ignore!\n", + sc_chan->idx, *data); + return; + } + if (sc_chan->idx == 0) { hdr = msg; sc_ipc->rx_size = hdr->size; @@ -165,7 +171,8 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) mutex_lock(&sc_ipc->lock); reinit_completion(&sc_ipc->done); - sc_ipc->msg = msg; + if (have_resp) + sc_ipc->msg = msg; sc_ipc->count = 0; ret = imx_scu_ipc_write(sc_ipc, msg); if (ret < 0) { @@ -187,6 +194,7 @@ int imx_scu_call_rpc(struct imx_sc_ipc *sc_ipc, void *msg, bool have_resp) } out: + sc_ipc->msg = NULL; mutex_unlock(&sc_ipc->lock); dev_dbg(sc_ipc->dev, "RPC SVC done\n"); -- GitLab From d961572ac0ec130f5f954dd9ecc7f63696a428cc Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Fri, 2 Aug 2019 20:53:29 +0300 Subject: [PATCH 03/71] clk: imx: pll14xx: Fix quick switch of S/K parameter The PLL14xx on imx8m can change the S and K parameter without requiring a reset and relock of the whole PLL. Fix clk_pll144xx_mp_change register reading and use it for pll1443 as well since no reset+relock is required on K changes either. The PLLs are currently table-based and none of the entries differ only in S/K so further work would be required to make use of this. The prospective user is audio doing tiny freq adjustments and there is no standard API for that. However lacking users is not a good reason for clk not to implement this. Signed-off-by: Leonard Crestez --- drivers/clk/imx/clk-pll14xx.c | 40 +++++++---------------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/drivers/clk/imx/clk-pll14xx.c b/drivers/clk/imx/clk-pll14xx.c index b7213023b238..25342297e5a6 100644 --- a/drivers/clk/imx/clk-pll14xx.c +++ b/drivers/clk/imx/clk-pll14xx.c @@ -112,43 +112,17 @@ static unsigned long clk_pll1443x_recalc_rate(struct clk_hw *hw, return fvco; } -static inline bool clk_pll1416x_mp_change(const struct imx_pll14xx_rate_table *rate, +static inline bool clk_pll14xx_mp_change(const struct imx_pll14xx_rate_table *rate, u32 pll_div) { u32 old_mdiv, old_pdiv; - old_mdiv = (pll_div >> MDIV_SHIFT) & MDIV_MASK; - old_pdiv = (pll_div >> PDIV_SHIFT) & PDIV_MASK; + old_mdiv = (pll_div & MDIV_MASK) >> MDIV_SHIFT; + old_pdiv = (pll_div & PDIV_MASK) >> PDIV_SHIFT; return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv; } -static inline bool clk_pll1443x_mpk_change(const struct imx_pll14xx_rate_table *rate, - u32 pll_div_ctl0, u32 pll_div_ctl1) -{ - u32 old_mdiv, old_pdiv, old_kdiv; - - old_mdiv = (pll_div_ctl0 >> MDIV_SHIFT) & MDIV_MASK; - old_pdiv = (pll_div_ctl0 >> PDIV_SHIFT) & PDIV_MASK; - old_kdiv = (pll_div_ctl1 >> KDIV_SHIFT) & KDIV_MASK; - - return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv || - rate->kdiv != old_kdiv; -} - -static inline bool clk_pll1443x_mp_change(const struct imx_pll14xx_rate_table *rate, - u32 pll_div_ctl0, u32 pll_div_ctl1) -{ - u32 old_mdiv, old_pdiv, old_kdiv; - - old_mdiv = (pll_div_ctl0 >> MDIV_SHIFT) & MDIV_MASK; - old_pdiv = (pll_div_ctl0 >> PDIV_SHIFT) & PDIV_MASK; - old_kdiv = (pll_div_ctl1 >> KDIV_SHIFT) & KDIV_MASK; - - return rate->mdiv != old_mdiv || rate->pdiv != old_pdiv || - rate->kdiv != old_kdiv; -} - static int clk_pll14xx_wait_lock(struct clk_pll14xx *pll) { u32 val; @@ -174,7 +148,7 @@ static int clk_pll1416x_set_rate(struct clk_hw *hw, unsigned long drate, tmp = readl_relaxed(pll->base + 4); - if (!clk_pll1416x_mp_change(rate, tmp)) { + if (!clk_pll14xx_mp_change(rate, tmp)) { tmp &= ~(SDIV_MASK) << SDIV_SHIFT; tmp |= rate->sdiv << SDIV_SHIFT; writel_relaxed(tmp, pll->base + 4); @@ -235,13 +209,15 @@ static int clk_pll1443x_set_rate(struct clk_hw *hw, unsigned long drate, } tmp = readl_relaxed(pll->base + 4); - div_val = readl_relaxed(pll->base + 8); - if (!clk_pll1443x_mpk_change(rate, tmp, div_val)) { + if (!clk_pll14xx_mp_change(rate, tmp)) { tmp &= ~(SDIV_MASK) << SDIV_SHIFT; tmp |= rate->sdiv << SDIV_SHIFT; writel_relaxed(tmp, pll->base + 4); + tmp = rate->kdiv << KDIV_SHIFT; + writel_relaxed(tmp, pll->base + 8); + return 0; } -- GitLab From 2396ee907103b584080930d6bb5ee3989ce6da79 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Fri, 19 Jul 2019 20:20:00 +0300 Subject: [PATCH 04/71] clk: imx8mq: Define gates for pll1/2 fixed dividers On imx8mq there are 9 fixed-factor dividers for SYS_PLL1 and SYS_PLL2 each with their own gate but these gates are not currently defined. Add the other gates to the clock tree between sys1/2_pll_out and the fixed dividers. Signed-off-by: Leonard Crestez --- drivers/clk/imx/clk-imx8mq.c | 62 ++++++++++++++++-------- include/dt-bindings/clock/imx8mq-clock.h | 22 ++++++++- 2 files changed, 63 insertions(+), 21 deletions(-) diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c index 1e5421bf2be1..371193116c0f 100644 --- a/drivers/clk/imx/clk-imx8mq.c +++ b/drivers/clk/imx/clk-imx8mq.c @@ -346,26 +346,48 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_sccg_pll("sys2_pll_out", sys2_pll_out_sels, ARRAY_SIZE(sys2_pll_out_sels), 0, 0, 1, base + 0x3c, CLK_IS_CRITICAL); 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); - /* SYS PLL fixed output */ - clks[IMX8MQ_SYS1_PLL_40M] = imx_clk_fixed_factor("sys1_pll_40m", "sys1_pll_out", 1, 20); - clks[IMX8MQ_SYS1_PLL_80M] = imx_clk_fixed_factor("sys1_pll_80m", "sys1_pll_out", 1, 10); - clks[IMX8MQ_SYS1_PLL_100M] = imx_clk_fixed_factor("sys1_pll_100m", "sys1_pll_out", 1, 8); - clks[IMX8MQ_SYS1_PLL_133M] = imx_clk_fixed_factor("sys1_pll_133m", "sys1_pll_out", 1, 6); - clks[IMX8MQ_SYS1_PLL_160M] = imx_clk_fixed_factor("sys1_pll_160m", "sys1_pll_out", 1, 5); - clks[IMX8MQ_SYS1_PLL_200M] = imx_clk_fixed_factor("sys1_pll_200m", "sys1_pll_out", 1, 4); - clks[IMX8MQ_SYS1_PLL_266M] = imx_clk_fixed_factor("sys1_pll_266m", "sys1_pll_out", 1, 3); - clks[IMX8MQ_SYS1_PLL_400M] = imx_clk_fixed_factor("sys1_pll_400m", "sys1_pll_out", 1, 2); - clks[IMX8MQ_SYS1_PLL_800M] = imx_clk_fixed_factor("sys1_pll_800m", "sys1_pll_out", 1, 1); - - clks[IMX8MQ_SYS2_PLL_50M] = imx_clk_fixed_factor("sys2_pll_50m", "sys2_pll_out", 1, 20); - clks[IMX8MQ_SYS2_PLL_100M] = imx_clk_fixed_factor("sys2_pll_100m", "sys2_pll_out", 1, 10); - clks[IMX8MQ_SYS2_PLL_125M] = imx_clk_fixed_factor("sys2_pll_125m", "sys2_pll_out", 1, 8); - clks[IMX8MQ_SYS2_PLL_166M] = imx_clk_fixed_factor("sys2_pll_166m", "sys2_pll_out", 1, 6); - clks[IMX8MQ_SYS2_PLL_200M] = imx_clk_fixed_factor("sys2_pll_200m", "sys2_pll_out", 1, 5); - clks[IMX8MQ_SYS2_PLL_250M] = imx_clk_fixed_factor("sys2_pll_250m", "sys2_pll_out", 1, 4); - clks[IMX8MQ_SYS2_PLL_333M] = imx_clk_fixed_factor("sys2_pll_333m", "sys2_pll_out", 1, 3); - clks[IMX8MQ_SYS2_PLL_500M] = imx_clk_fixed_factor("sys2_pll_500m", "sys2_pll_out", 1, 2); - clks[IMX8MQ_SYS2_PLL_1000M] = imx_clk_fixed_factor("sys2_pll_1000m", "sys2_pll_out", 1, 1); + + /* 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); + clks[IMX8MQ_SYS1_PLL_100M_CG] = imx_clk_gate("sys1_pll_100m_cg", "sys1_pll_out", base + 0x30, 13); + clks[IMX8MQ_SYS1_PLL_133M_CG] = imx_clk_gate("sys1_pll_133m_cg", "sys1_pll_out", base + 0x30, 15); + clks[IMX8MQ_SYS1_PLL_160M_CG] = imx_clk_gate("sys1_pll_160m_cg", "sys1_pll_out", base + 0x30, 17); + clks[IMX8MQ_SYS1_PLL_200M_CG] = imx_clk_gate("sys1_pll_200m_cg", "sys1_pll_out", base + 0x30, 19); + clks[IMX8MQ_SYS1_PLL_266M_CG] = imx_clk_gate("sys1_pll_266m_cg", "sys1_pll_out", base + 0x30, 21); + clks[IMX8MQ_SYS1_PLL_400M_CG] = imx_clk_gate("sys1_pll_400m_cg", "sys1_pll_out", base + 0x30, 23); + clks[IMX8MQ_SYS1_PLL_800M_CG] = imx_clk_gate("sys1_pll_800m_cg", "sys1_pll_out", base + 0x30, 25); + + clks[IMX8MQ_SYS1_PLL_40M] = imx_clk_fixed_factor("sys1_pll_40m", "sys1_pll_40m_cg", 1, 20); + clks[IMX8MQ_SYS1_PLL_80M] = imx_clk_fixed_factor("sys1_pll_80m", "sys1_pll_80m_cg", 1, 10); + clks[IMX8MQ_SYS1_PLL_100M] = imx_clk_fixed_factor("sys1_pll_100m", "sys1_pll_100m_cg", 1, 8); + clks[IMX8MQ_SYS1_PLL_133M] = imx_clk_fixed_factor("sys1_pll_133m", "sys1_pll_133m_cg", 1, 6); + clks[IMX8MQ_SYS1_PLL_160M] = imx_clk_fixed_factor("sys1_pll_160m", "sys1_pll_160m_cg", 1, 5); + clks[IMX8MQ_SYS1_PLL_200M] = imx_clk_fixed_factor("sys1_pll_200m", "sys1_pll_200m_cg", 1, 4); + clks[IMX8MQ_SYS1_PLL_266M] = imx_clk_fixed_factor("sys1_pll_266m", "sys1_pll_266m_cg", 1, 3); + clks[IMX8MQ_SYS1_PLL_400M] = imx_clk_fixed_factor("sys1_pll_400m", "sys1_pll_400m_cg", 1, 2); + clks[IMX8MQ_SYS1_PLL_800M] = imx_clk_fixed_factor("sys1_pll_800m", "sys1_pll_800m_cg", 1, 1); + + /* SYS PLL2 fixed output */ + clks[IMX8MQ_SYS2_PLL_50M_CG] = imx_clk_gate("sys2_pll_50m_cg", "sys2_pll_out", base + 0x3c, 9); + clks[IMX8MQ_SYS2_PLL_100M_CG] = imx_clk_gate("sys2_pll_100m_cg", "sys2_pll_out", base + 0x3c, 11); + clks[IMX8MQ_SYS2_PLL_125M_CG] = imx_clk_gate("sys2_pll_125m_cg", "sys2_pll_out", base + 0x3c, 13); + clks[IMX8MQ_SYS2_PLL_166M_CG] = imx_clk_gate("sys2_pll_166m_cg", "sys2_pll_out", base + 0x3c, 15); + clks[IMX8MQ_SYS2_PLL_200M_CG] = imx_clk_gate("sys2_pll_200m_cg", "sys2_pll_out", base + 0x3c, 17); + clks[IMX8MQ_SYS2_PLL_250M_CG] = imx_clk_gate("sys2_pll_250m_cg", "sys2_pll_out", base + 0x3c, 19); + clks[IMX8MQ_SYS2_PLL_333M_CG] = imx_clk_gate("sys2_pll_333m_cg", "sys2_pll_out", base + 0x3c, 21); + clks[IMX8MQ_SYS2_PLL_500M_CG] = imx_clk_gate("sys2_pll_500m_cg", "sys2_pll_out", base + 0x3c, 23); + clks[IMX8MQ_SYS2_PLL_1000M_CG] = imx_clk_gate("sys2_pll_1000m_cg", "sys2_pll_out", base + 0x3c, 25); + + clks[IMX8MQ_SYS2_PLL_50M] = imx_clk_fixed_factor("sys2_pll_50m", "sys2_pll_50m_cg", 1, 20); + clks[IMX8MQ_SYS2_PLL_100M] = imx_clk_fixed_factor("sys2_pll_100m", "sys2_pll_100m_cg", 1, 10); + clks[IMX8MQ_SYS2_PLL_125M] = imx_clk_fixed_factor("sys2_pll_125m", "sys2_pll_125m_cg", 1, 8); + clks[IMX8MQ_SYS2_PLL_166M] = imx_clk_fixed_factor("sys2_pll_166m", "sys2_pll_166m_cg", 1, 6); + clks[IMX8MQ_SYS2_PLL_200M] = imx_clk_fixed_factor("sys2_pll_200m", "sys2_pll_200m_cg", 1, 5); + clks[IMX8MQ_SYS2_PLL_250M] = imx_clk_fixed_factor("sys2_pll_250m", "sys2_pll_250m_cg", 1, 4); + clks[IMX8MQ_SYS2_PLL_333M] = imx_clk_fixed_factor("sys2_pll_333m", "sys2_pll_333m_cg", 1, 3); + clks[IMX8MQ_SYS2_PLL_500M] = imx_clk_fixed_factor("sys2_pll_500m", "sys2_pll_500m_cg", 1, 2); + clks[IMX8MQ_SYS2_PLL_1000M] = imx_clk_fixed_factor("sys2_pll_1000m", "sys2_pll_1000m_cg", 1, 1); np = dev->of_node; base = devm_platform_ioremap_resource(pdev, 0); diff --git a/include/dt-bindings/clock/imx8mq-clock.h b/include/dt-bindings/clock/imx8mq-clock.h index 65463673d25e..9b031f93b7d1 100644 --- a/include/dt-bindings/clock/imx8mq-clock.h +++ b/include/dt-bindings/clock/imx8mq-clock.h @@ -403,5 +403,25 @@ #define IMX8MQ_CLK_SNVS_ROOT 264 #define IMX8MQ_CLK_GIC 265 -#define IMX8MQ_CLK_END 266 +#define IMX8MQ_SYS1_PLL_40M_CG 266 +#define IMX8MQ_SYS1_PLL_80M_CG 267 +#define IMX8MQ_SYS1_PLL_100M_CG 268 +#define IMX8MQ_SYS1_PLL_133M_CG 269 +#define IMX8MQ_SYS1_PLL_160M_CG 270 +#define IMX8MQ_SYS1_PLL_200M_CG 271 +#define IMX8MQ_SYS1_PLL_266M_CG 272 +#define IMX8MQ_SYS1_PLL_400M_CG 273 +#define IMX8MQ_SYS1_PLL_800M_CG 274 +#define IMX8MQ_SYS2_PLL_50M_CG 275 +#define IMX8MQ_SYS2_PLL_100M_CG 276 +#define IMX8MQ_SYS2_PLL_125M_CG 277 +#define IMX8MQ_SYS2_PLL_166M_CG 278 +#define IMX8MQ_SYS2_PLL_200M_CG 279 +#define IMX8MQ_SYS2_PLL_250M_CG 280 +#define IMX8MQ_SYS2_PLL_333M_CG 281 +#define IMX8MQ_SYS2_PLL_500M_CG 282 +#define IMX8MQ_SYS2_PLL_1000M_CG 283 + +#define IMX8MQ_CLK_END 384 + #endif /* __DT_BINDINGS_CLOCK_IMX8MQ_H */ -- GitLab From f54edd3213500e83fc2094ae333f28c2931b22b3 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Fri, 19 Jul 2019 18:59:02 +0300 Subject: [PATCH 05/71] clk: imx8mm: Define gates for pll1/2 fixed dividers On imx8mm there are 9 fixed-factor dividers for SYS_PLL1 and SYS_PLL2 each with their own gate. Only one of these gates (the one "dividing" by one) is currently defined and it's incorrectly set as the parent of all the fixed-factor dividers. Add the other 8 gates to the clock tree between sys_pll1/2_bypass and the fixed dividers. Signed-off-by: Leonard Crestez --- drivers/clk/imx/clk-imx8mm.c | 57 ++++++++++++++++-------- include/dt-bindings/clock/imx8mm-clock.h | 19 +++++++- 2 files changed, 56 insertions(+), 20 deletions(-) diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index 6b8e75df994d..41c343048818 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -455,29 +455,48 @@ static int __init imx8mm_clocks_init(struct device_node *ccm_node) clks[IMX8MM_GPU_PLL_OUT] = imx_clk_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x64, 11); clks[IMX8MM_VPU_PLL_OUT] = imx_clk_gate("vpu_pll_out", "vpu_pll_bypass", base + 0x74, 11); clks[IMX8MM_ARM_PLL_OUT] = imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x84, 11); - clks[IMX8MM_SYS_PLL1_OUT] = imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11); - clks[IMX8MM_SYS_PLL2_OUT] = imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11); clks[IMX8MM_SYS_PLL3_OUT] = imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11); - /* SYS PLL fixed output */ - clks[IMX8MM_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_out", 1, 20); - clks[IMX8MM_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_out", 1, 10); - clks[IMX8MM_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_out", 1, 8); - clks[IMX8MM_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_out", 1, 6); - clks[IMX8MM_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_out", 1, 5); - clks[IMX8MM_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_out", 1, 4); - clks[IMX8MM_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_out", 1, 3); - clks[IMX8MM_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_out", 1, 2); + /* SYS PLL1 fixed output */ + clks[IMX8MM_SYS_PLL1_40M_CG] = imx_clk_gate("sys_pll1_40m_cg", "sys_pll1_bypass", base + 0x94, 27); + clks[IMX8MM_SYS_PLL1_80M_CG] = imx_clk_gate("sys_pll1_80m_cg", "sys_pll1_bypass", base + 0x94, 25); + clks[IMX8MM_SYS_PLL1_100M_CG] = imx_clk_gate("sys_pll1_100m_cg", "sys_pll1_bypass", base + 0x94, 23); + clks[IMX8MM_SYS_PLL1_133M_CG] = imx_clk_gate("sys_pll1_133m_cg", "sys_pll1_bypass", base + 0x94, 21); + clks[IMX8MM_SYS_PLL1_160M_CG] = imx_clk_gate("sys_pll1_160m_cg", "sys_pll1_bypass", base + 0x94, 19); + clks[IMX8MM_SYS_PLL1_200M_CG] = imx_clk_gate("sys_pll1_200m_cg", "sys_pll1_bypass", base + 0x94, 17); + clks[IMX8MM_SYS_PLL1_266M_CG] = imx_clk_gate("sys_pll1_266m_cg", "sys_pll1_bypass", base + 0x94, 15); + clks[IMX8MM_SYS_PLL1_400M_CG] = imx_clk_gate("sys_pll1_400m_cg", "sys_pll1_bypass", base + 0x94, 13); + clks[IMX8MM_SYS_PLL1_OUT] = imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11); + + clks[IMX8MM_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_40m_cg", 1, 20); + clks[IMX8MM_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_80m_cg", 1, 10); + clks[IMX8MM_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_100m_cg", 1, 8); + clks[IMX8MM_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_133m_cg", 1, 6); + clks[IMX8MM_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_160m_cg", 1, 5); + clks[IMX8MM_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_200m_cg", 1, 4); + clks[IMX8MM_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_266m_cg", 1, 3); + clks[IMX8MM_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_400m_cg", 1, 2); clks[IMX8MM_SYS_PLL1_800M] = imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1); - clks[IMX8MM_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_out", 1, 20); - clks[IMX8MM_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_out", 1, 10); - clks[IMX8MM_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_out", 1, 8); - clks[IMX8MM_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_out", 1, 6); - clks[IMX8MM_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_out", 1, 5); - clks[IMX8MM_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_out", 1, 4); - clks[IMX8MM_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_out", 1, 3); - clks[IMX8MM_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_out", 1, 2); + /* SYS PLL2 fixed output */ + clks[IMX8MM_SYS_PLL2_50M_CG] = imx_clk_gate("sys_pll2_50m_cg", "sys_pll2_bypass", base + 0x104, 27); + clks[IMX8MM_SYS_PLL2_100M_CG] = imx_clk_gate("sys_pll2_100m_cg", "sys_pll2_bypass", base + 0x104, 25); + clks[IMX8MM_SYS_PLL2_125M_CG] = imx_clk_gate("sys_pll2_125m_cg", "sys_pll2_bypass", base + 0x104, 23); + clks[IMX8MM_SYS_PLL2_166M_CG] = imx_clk_gate("sys_pll2_166m_cg", "sys_pll2_bypass", base + 0x104, 21); + clks[IMX8MM_SYS_PLL2_200M_CG] = imx_clk_gate("sys_pll2_200m_cg", "sys_pll2_bypass", base + 0x104, 19); + clks[IMX8MM_SYS_PLL2_250M_CG] = imx_clk_gate("sys_pll2_250m_cg", "sys_pll2_bypass", base + 0x104, 17); + clks[IMX8MM_SYS_PLL2_333M_CG] = imx_clk_gate("sys_pll2_333m_cg", "sys_pll2_bypass", base + 0x104, 15); + clks[IMX8MM_SYS_PLL2_500M_CG] = imx_clk_gate("sys_pll2_500m_cg", "sys_pll2_bypass", base + 0x104, 13); + clks[IMX8MM_SYS_PLL2_OUT] = imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11); + + clks[IMX8MM_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_50m_cg", 1, 20); + clks[IMX8MM_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_100m_cg", 1, 10); + clks[IMX8MM_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_125m_cg", 1, 8); + clks[IMX8MM_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_166m_cg", 1, 6); + clks[IMX8MM_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_200m_cg", 1, 5); + clks[IMX8MM_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_250m_cg", 1, 4); + clks[IMX8MM_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_333m_cg", 1, 3); + clks[IMX8MM_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_500m_cg", 1, 2); clks[IMX8MM_SYS_PLL2_1000M] = imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1); np = ccm_node; diff --git a/include/dt-bindings/clock/imx8mm-clock.h b/include/dt-bindings/clock/imx8mm-clock.h index 07e6c686f3ef..8cf994043bcd 100644 --- a/include/dt-bindings/clock/imx8mm-clock.h +++ b/include/dt-bindings/clock/imx8mm-clock.h @@ -248,6 +248,23 @@ #define IMX8MM_CLK_SNVS_ROOT 228 #define IMX8MM_CLK_GIC 229 -#define IMX8MM_CLK_END 230 +#define IMX8MM_SYS_PLL1_40M_CG 230 +#define IMX8MM_SYS_PLL1_80M_CG 231 +#define IMX8MM_SYS_PLL1_100M_CG 232 +#define IMX8MM_SYS_PLL1_133M_CG 233 +#define IMX8MM_SYS_PLL1_160M_CG 234 +#define IMX8MM_SYS_PLL1_200M_CG 235 +#define IMX8MM_SYS_PLL1_266M_CG 236 +#define IMX8MM_SYS_PLL1_400M_CG 237 +#define IMX8MM_SYS_PLL2_50M_CG 239 +#define IMX8MM_SYS_PLL2_100M_CG 240 +#define IMX8MM_SYS_PLL2_125M_CG 241 +#define IMX8MM_SYS_PLL2_166M_CG 242 +#define IMX8MM_SYS_PLL2_200M_CG 243 +#define IMX8MM_SYS_PLL2_250M_CG 244 +#define IMX8MM_SYS_PLL2_333M_CG 245 +#define IMX8MM_SYS_PLL2_500M_CG 246 + +#define IMX8MM_CLK_END 248 #endif -- GitLab From f90cbc3e3a87e2390e1be1f671a031d2d2669d1b Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 12 Aug 2019 19:41:22 +0300 Subject: [PATCH 06/71] clk: imx8mn: Define gates for pll1/2 fixed dividers On imx8mn there are 9 fixed-factor dividers for SYS_PLL1 and SYS_PLL2 each with their own gate. Only one of these gates (the one "dividing" by one) is currently defined and it's incorrectly set as the parent of all the fixed-factor dividers. Add the other 8 gates to the clock tree between sys_pll1/2_bypass and the fixed dividers. Signed-off-by: Leonard Crestez --- drivers/clk/imx/clk-imx8mn.c | 667 +++++++++++++++++++++++ include/dt-bindings/clock/imx8mn-clock.h | 233 ++++++++ 2 files changed, 900 insertions(+) create mode 100644 drivers/clk/imx/clk-imx8mn.c create mode 100644 include/dt-bindings/clock/imx8mn-clock.h diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c new file mode 100644 index 000000000000..ccd074dfdc20 --- /dev/null +++ b/drivers/clk/imx/clk-imx8mn.c @@ -0,0 +1,667 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2018-2019 NXP. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk.h" + +static u32 share_count_sai2; +static u32 share_count_sai3; +static u32 share_count_sai5; +static u32 share_count_sai6; +static u32 share_count_sai7; +static u32 share_count_disp; +static u32 share_count_pdm; +static u32 share_count_nand; + +enum { + ARM_PLL, + GPU_PLL, + VPU_PLL, + SYS_PLL1, + SYS_PLL2, + SYS_PLL3, + DRAM_PLL, + AUDIO_PLL1, + AUDIO_PLL2, + VIDEO_PLL2, + NR_PLLS, +}; + +static const struct imx_pll14xx_rate_table imx8mn_pll1416x_tbl[] = { + PLL_1416X_RATE(1800000000U, 225, 3, 0), + PLL_1416X_RATE(1600000000U, 200, 3, 0), + PLL_1416X_RATE(1500000000U, 375, 3, 1), + PLL_1416X_RATE(1400000000U, 350, 3, 1), + PLL_1416X_RATE(1200000000U, 300, 3, 1), + PLL_1416X_RATE(1000000000U, 250, 3, 1), + PLL_1416X_RATE(800000000U, 200, 3, 1), + PLL_1416X_RATE(750000000U, 250, 2, 2), + PLL_1416X_RATE(700000000U, 350, 3, 2), + PLL_1416X_RATE(600000000U, 300, 3, 2), +}; + +static const struct imx_pll14xx_rate_table imx8mn_audiopll_tbl[] = { + PLL_1443X_RATE(393216000U, 262, 2, 3, 9437), + PLL_1443X_RATE(361267200U, 361, 3, 3, 17511), +}; + +static const struct imx_pll14xx_rate_table imx8mn_videopll_tbl[] = { + PLL_1443X_RATE(650000000U, 325, 3, 2, 0), + PLL_1443X_RATE(594000000U, 198, 2, 2, 0), +}; + +static const struct imx_pll14xx_rate_table imx8mn_drampll_tbl[] = { + PLL_1443X_RATE(650000000U, 325, 3, 2, 0), +}; + +static struct imx_pll14xx_clk imx8mn_audio_pll = { + .type = PLL_1443X, + .rate_table = imx8mn_audiopll_tbl, + .rate_count = ARRAY_SIZE(imx8mn_audiopll_tbl), +}; + +static struct imx_pll14xx_clk imx8mn_video_pll = { + .type = PLL_1443X, + .rate_table = imx8mn_videopll_tbl, + .rate_count = ARRAY_SIZE(imx8mn_videopll_tbl), +}; + +static struct imx_pll14xx_clk imx8mn_dram_pll = { + .type = PLL_1443X, + .rate_table = imx8mn_drampll_tbl, + .rate_count = ARRAY_SIZE(imx8mn_drampll_tbl), +}; + +static struct imx_pll14xx_clk imx8mn_arm_pll = { + .type = PLL_1416X, + .rate_table = imx8mn_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx8mn_pll1416x_tbl), +}; + +static struct imx_pll14xx_clk imx8mn_gpu_pll = { + .type = PLL_1416X, + .rate_table = imx8mn_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx8mn_pll1416x_tbl), +}; + +static struct imx_pll14xx_clk imx8mn_vpu_pll = { + .type = PLL_1416X, + .rate_table = imx8mn_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx8mn_pll1416x_tbl), +}; + +static struct imx_pll14xx_clk imx8mn_sys_pll = { + .type = PLL_1416X, + .rate_table = imx8mn_pll1416x_tbl, + .rate_count = ARRAY_SIZE(imx8mn_pll1416x_tbl), +}; + +static const char * const pll_ref_sels[] = { "osc_24m", "dummy", "dummy", "dummy", }; +static const char * const audio_pll1_bypass_sels[] = {"audio_pll1", "audio_pll1_ref_sel", }; +static const char * const audio_pll2_bypass_sels[] = {"audio_pll2", "audio_pll2_ref_sel", }; +static const char * const video_pll1_bypass_sels[] = {"video_pll1", "video_pll1_ref_sel", }; +static const char * const dram_pll_bypass_sels[] = {"dram_pll", "dram_pll_ref_sel", }; +static const char * const gpu_pll_bypass_sels[] = {"gpu_pll", "gpu_pll_ref_sel", }; +static const char * const vpu_pll_bypass_sels[] = {"vpu_pll", "vpu_pll_ref_sel", }; +static const char * const arm_pll_bypass_sels[] = {"arm_pll", "arm_pll_ref_sel", }; +static const char * const sys_pll1_bypass_sels[] = {"sys_pll1", "sys_pll1_ref_sel", }; +static const char * const sys_pll2_bypass_sels[] = {"sys_pll2", "sys_pll2_ref_sel", }; +static const char * const sys_pll3_bypass_sels[] = {"sys_pll3", "sys_pll3_ref_sel", }; + +static const char * const imx8mn_a53_sels[] = {"osc_24m", "arm_pll_out", "sys_pll2_500m", + "sys_pll2_1000m", "sys_pll1_800m", "sys_pll1_400m", + "audio_pll1_out", "sys_pll3_out", }; + +static const char * const imx8mn_gpu_core_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mn_gpu_shader_sels[] = {"osc_24m", "gpu_pll_out", "sys_pll1_800m", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mn_main_axi_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll1_800m", + "sys_pll2_250m", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "sys_pll1_100m",}; + +static const char * const imx8mn_enet_axi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", + "sys_pll2_250m", "sys_pll2_200m", "audio_pll1_out", + "video_pll1_out", "sys_pll3_out", }; + +static const char * const imx8mn_nand_usdhc_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll1_800m", + "sys_pll2_200m", "sys_pll1_133m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll1_out", }; + +static const char * const imx8mn_disp_axi_sels[] = {"osc_24m", "sys_pll2_1000m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_40m", "audio_pll2_out", + "clk_ext1", "clk_ext4", }; + +static const char * const imx8mn_disp_apb_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll1_800m", + "sys_pll3_out", "sys_pll1_40m", "audio_pll2_out", + "clk_ext1", "clk_ext3", }; + +static const char * const imx8mn_usb_bus_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", + "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mn_gpu_axi_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mn_gpu_ahb_sels[] = {"osc_24m", "sys_pll1_800m", "gpu_pll_out", + "sys_pll3_out", "sys_pll2_1000m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mn_noc_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_1000m", "sys_pll2_500m", "audio_pll1_out", + "video_pll1_out", "audio_pll2_out", }; + +static const char * const imx8mn_ahb_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_800m", + "sys_pll1_400m", "sys_pll2_125m", "sys_pll3_out", + "audio_pll1_out", "video_pll1_out", }; + +static const char * const imx8mn_audio_ahb_sels[] = {"osc_24m", "sys_pll2_500m", "sys_pll1_800m", + "sys_pll2_1000m", "sys_pll2_166m", "sys_pll3_out", + "audio_pll1_out", "video_pll1_out", }; + +static const char * const imx8mn_dram_alt_sels[] = {"osc_24m", "sys_pll1_800m", "sys_pll1_100m", + "sys_pll2_500m", "sys_pll2_1000m", "sys_pll3_out", + "audio_pll1_out", "sys_pll1_266m", }; + +static const char * const imx8mn_dram_apb_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mn_disp_pixel_sels[] = {"osc_24m", "video_pll1_out", "audio_pll2_out", + "audio_pll1_out", "sys_pll1_800m", "sys_pll2_1000m", + "sys_pll3_out", "clk_ext4", }; + +static const char * const imx8mn_sai2_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "clk_ext3", "clk_ext4", }; + +static const char * const imx8mn_sai3_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "clk_ext3", "clk_ext4", }; + +static const char * const imx8mn_sai5_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "clk_ext2", "clk_ext3", }; + +static const char * const imx8mn_sai6_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "clk_ext3", "clk_ext4", }; + +static const char * const imx8mn_sai7_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "clk_ext3", "clk_ext4", }; + +static const char * const imx8mn_spdif1_sels[] = {"osc_24m", "audio_pll1_out", "audio_pll2_out", + "video_pll1_out", "sys_pll1_133m", "osc_hdmi", + "clk_ext2", "clk_ext3", }; + +static const char * const imx8mn_enet_ref_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_50m", + "sys_pll2_100m", "sys_pll1_160m", "audio_pll1_out", + "video_pll1_out", "clk_ext4", }; + +static const char * const imx8mn_enet_timer_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", + "clk_ext1", "clk_ext2", "clk_ext3", + "clk_ext4", "video_pll1_out", }; + +static const char * const imx8mn_enet_phy_sels[] = {"osc_24m", "sys_pll2_50m", "sys_pll2_125m", + "sys_pll2_200m", "sys_pll2_500m", "video_pll1_out", + "audio_pll2_out", }; + +static const char * const imx8mn_nand_sels[] = {"osc_24m", "sys_pll2_500m", "audio_pll1_out", + "sys_pll1_400m", "audio_pll2_out", "sys_pll3_out", + "sys_pll2_250m", "video_pll1_out", }; + +static const char * const imx8mn_qspi_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll2_333m", + "sys_pll2_500m", "audio_pll2_out", "sys_pll1_266m", + "sys_pll3_out", "sys_pll1_100m", }; + +static const char * const imx8mn_usdhc1_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", + "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", + "audio_pll2_out", "sys_pll1_100m", }; + +static const char * const imx8mn_usdhc2_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", + "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", + "audio_pll2_out", "sys_pll1_100m", }; + +static const char * const imx8mn_i2c1_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mn_i2c2_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mn_i2c3_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mn_i2c4_sels[] = {"osc_24m", "sys_pll1_160m", "sys_pll2_50m", + "sys_pll3_out", "audio_pll1_out", "video_pll1_out", + "audio_pll2_out", "sys_pll1_133m", }; + +static const char * const imx8mn_uart1_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mn_uart2_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_uart3_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext4", "audio_pll2_out", }; + +static const char * const imx8mn_uart4_sels[] = {"osc_24m", "sys_pll1_80m", "sys_pll2_200m", + "sys_pll2_100m", "sys_pll3_out", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_usb_core_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", + "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_usb_phy_sels[] = {"osc_24m", "sys_pll1_100m", "sys_pll1_40m", + "sys_pll2_100m", "sys_pll2_200m", "clk_ext2", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_gic_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll2_100m", "sys_pll1_800m", "clk_ext2", + "clk_ext4", "audio_pll2_out" }; + +static const char * const imx8mn_ecspi1_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mn_ecspi2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mn_pwm1_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", + "sys_pll1_40m", "sys_pll3_out", "clk_ext1", + "sys_pll1_80m", "video_pll1_out", }; + +static const char * const imx8mn_pwm2_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", + "sys_pll1_40m", "sys_pll3_out", "clk_ext1", + "sys_pll1_80m", "video_pll1_out", }; + +static const char * const imx8mn_pwm3_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", + "sys_pll1_40m", "sys_pll3_out", "clk_ext2", + "sys_pll1_80m", "video_pll1_out", }; + +static const char * const imx8mn_pwm4_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_160m", + "sys_pll1_40m", "sys_pll3_out", "clk_ext2", + "sys_pll1_80m", "video_pll1_out", }; + +static const char * const imx8mn_wdog_sels[] = {"osc_24m", "sys_pll1_133m", "sys_pll1_160m", + "vpu_pll_out", "sys_pll2_125m", "sys_pll3_out", + "sys_pll1_80m", "sys_pll2_166m", }; + +static const char * const imx8mn_wrclk_sels[] = {"osc_24m", "sys_pll1_40m", "vpu_pll_out", + "sys_pll3_out", "sys_pll2_200m", "sys_pll1_266m", + "sys_pll2_500m", "sys_pll1_100m", }; + +static const char * const imx8mn_dsi_core_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mn_dsi_phy_sels[] = {"osc_24m", "sys_pll2_125m", "sys_pll2_100m", + "sys_pll1_800m", "sys_pll2_1000m", "clk_ext2", + "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mn_dsi_dbi_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_100m", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mn_usdhc3_sels[] = {"osc_24m", "sys_pll1_400m", "sys_pll1_800m", + "sys_pll2_500m", "sys_pll3_out", "sys_pll1_266m", + "audio_pll2_out", "sys_pll1_100m", }; + +static const char * const imx8mn_camera_pixel_sels[] = {"osc_24m", "sys_pll1_266m", "sys_pll2_250m", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mn_csi1_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", + "sys_pll1_800m", "sys_pll2_1000m", "clk_ext2", + "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mn_csi2_phy_sels[] = {"osc_24m", "sys_pll2_333m", "sys_pll2_100m", + "sys_pll1_800m", "sys_pll2_1000m", "clk_ext2", + "audio_pll2_out", "video_pll1_out", }; + +static const char * const imx8mn_csi2_esc_sels[] = {"osc_24m", "sys_pll2_100m", "sys_pll1_80m", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_ecspi3_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_40m", + "sys_pll1_160m", "sys_pll1_800m", "sys_pll3_out", + "sys_pll2_250m", "audio_pll2_out", }; + +static const char * const imx8mn_pdm_sels[] = {"osc_24m", "sys_pll2_100m", "audio_pll1_out", + "sys_pll1_800m", "sys_pll2_1000m", "sys_pll3_out", + "clk_ext3", "audio_pll2_out", }; + +static const char * const imx8mn_dram_core_sels[] = {"dram_pll_out", "dram_alt_root", }; + +static const char * const imx8mn_clko1_sels[] = {"osc_24m", "sys_pll1_800m", "osc_27m", + "sys_pll1_200m", "audio_pll2_out", "vpu_pll", + "sys_pll1_80m", }; +static const char * const imx8mn_clko2_sels[] = {"osc_24m", "sys_pll2_200m", "sys_pll1_400m", + "sys_pll2_166m", "sys_pll3_out", "audio_pll1_out", + "video_pll1_out", "osc_32k", }; + +static struct clk *clks[IMX8MN_CLK_END]; +static struct clk_onecell_data clk_data; + +static struct clk ** const uart_clks[] = { + &clks[IMX8MN_CLK_UART1_ROOT], + &clks[IMX8MN_CLK_UART2_ROOT], + &clks[IMX8MN_CLK_UART3_ROOT], + &clks[IMX8MN_CLK_UART4_ROOT], + NULL +}; + +static int imx8mn_clocks_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + void __iomem *base; + int ret; + + clks[IMX8MN_CLK_DUMMY] = imx_clk_fixed("dummy", 0); + clks[IMX8MN_CLK_24M] = of_clk_get_by_name(np, "osc_24m"); + clks[IMX8MN_CLK_32K] = of_clk_get_by_name(np, "osc_32k"); + clks[IMX8MN_CLK_EXT1] = of_clk_get_by_name(np, "clk_ext1"); + clks[IMX8MN_CLK_EXT2] = of_clk_get_by_name(np, "clk_ext2"); + clks[IMX8MN_CLK_EXT3] = of_clk_get_by_name(np, "clk_ext3"); + clks[IMX8MN_CLK_EXT4] = of_clk_get_by_name(np, "clk_ext4"); + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mn-anatop"); + base = of_iomap(np, 0); + if (WARN_ON(!base)) { + ret = -ENOMEM; + goto unregister_clks; + } + + clks[IMX8MN_AUDIO_PLL1_REF_SEL] = imx_clk_mux("audio_pll1_ref_sel", base + 0x0, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MN_AUDIO_PLL2_REF_SEL] = imx_clk_mux("audio_pll2_ref_sel", base + 0x14, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MN_VIDEO_PLL1_REF_SEL] = imx_clk_mux("video_pll1_ref_sel", base + 0x28, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MN_DRAM_PLL_REF_SEL] = imx_clk_mux("dram_pll_ref_sel", base + 0x50, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MN_GPU_PLL_REF_SEL] = imx_clk_mux("gpu_pll_ref_sel", base + 0x64, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MN_VPU_PLL_REF_SEL] = imx_clk_mux("vpu_pll_ref_sel", base + 0x74, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MN_ARM_PLL_REF_SEL] = imx_clk_mux("arm_pll_ref_sel", base + 0x84, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MN_SYS_PLL1_REF_SEL] = imx_clk_mux("sys_pll1_ref_sel", base + 0x94, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + clks[IMX8MN_SYS_PLL2_REF_SEL] = imx_clk_mux("sys_pll2_ref_sel", base + 0x104, 0, 2, pll_ref_sels, ARRAY_SIZE(pll_ref_sels)); + 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, &imx8mn_audio_pll); + clks[IMX8MN_AUDIO_PLL2] = imx_clk_pll14xx("audio_pll2", "audio_pll2_ref_sel", base + 0x14, &imx8mn_audio_pll); + clks[IMX8MN_VIDEO_PLL1] = imx_clk_pll14xx("video_pll1", "video_pll1_ref_sel", base + 0x28, &imx8mn_video_pll); + clks[IMX8MN_DRAM_PLL] = imx_clk_pll14xx("dram_pll", "dram_pll_ref_sel", base + 0x50, &imx8mn_dram_pll); + clks[IMX8MN_GPU_PLL] = imx_clk_pll14xx("gpu_pll", "gpu_pll_ref_sel", base + 0x64, &imx8mn_gpu_pll); + clks[IMX8MN_VPU_PLL] = imx_clk_pll14xx("vpu_pll", "vpu_pll_ref_sel", base + 0x74, &imx8mn_vpu_pll); + clks[IMX8MN_ARM_PLL] = imx_clk_pll14xx("arm_pll", "arm_pll_ref_sel", base + 0x84, &imx8mn_arm_pll); + clks[IMX8MN_SYS_PLL1] = imx_clk_pll14xx("sys_pll1", "sys_pll1_ref_sel", base + 0x94, &imx8mn_sys_pll); + clks[IMX8MN_SYS_PLL2] = imx_clk_pll14xx("sys_pll2", "sys_pll2_ref_sel", base + 0x104, &imx8mn_sys_pll); + clks[IMX8MN_SYS_PLL3] = imx_clk_pll14xx("sys_pll3", "sys_pll3_ref_sel", base + 0x114, &imx8mn_sys_pll); + + /* PLL bypass out */ + clks[IMX8MN_AUDIO_PLL1_BYPASS] = imx_clk_mux_flags("audio_pll1_bypass", base, 16, 1, audio_pll1_bypass_sels, ARRAY_SIZE(audio_pll1_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MN_AUDIO_PLL2_BYPASS] = imx_clk_mux_flags("audio_pll2_bypass", base + 0x14, 16, 1, audio_pll2_bypass_sels, ARRAY_SIZE(audio_pll2_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MN_VIDEO_PLL1_BYPASS] = imx_clk_mux_flags("video_pll1_bypass", base + 0x28, 16, 1, video_pll1_bypass_sels, ARRAY_SIZE(video_pll1_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MN_DRAM_PLL_BYPASS] = imx_clk_mux_flags("dram_pll_bypass", base + 0x50, 16, 1, dram_pll_bypass_sels, ARRAY_SIZE(dram_pll_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MN_GPU_PLL_BYPASS] = imx_clk_mux_flags("gpu_pll_bypass", base + 0x64, 28, 1, gpu_pll_bypass_sels, ARRAY_SIZE(gpu_pll_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MN_VPU_PLL_BYPASS] = imx_clk_mux_flags("vpu_pll_bypass", base + 0x74, 28, 1, vpu_pll_bypass_sels, ARRAY_SIZE(vpu_pll_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MN_ARM_PLL_BYPASS] = imx_clk_mux_flags("arm_pll_bypass", base + 0x84, 28, 1, arm_pll_bypass_sels, ARRAY_SIZE(arm_pll_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MN_SYS_PLL1_BYPASS] = imx_clk_mux_flags("sys_pll1_bypass", base + 0x94, 28, 1, sys_pll1_bypass_sels, ARRAY_SIZE(sys_pll1_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MN_SYS_PLL2_BYPASS] = imx_clk_mux_flags("sys_pll2_bypass", base + 0x104, 28, 1, sys_pll2_bypass_sels, ARRAY_SIZE(sys_pll2_bypass_sels), CLK_SET_RATE_PARENT); + clks[IMX8MN_SYS_PLL3_BYPASS] = imx_clk_mux_flags("sys_pll3_bypass", base + 0x114, 28, 1, sys_pll3_bypass_sels, ARRAY_SIZE(sys_pll3_bypass_sels), CLK_SET_RATE_PARENT); + + /* PLL out gate */ + clks[IMX8MN_AUDIO_PLL1_OUT] = imx_clk_gate("audio_pll1_out", "audio_pll1_bypass", base, 13); + clks[IMX8MN_AUDIO_PLL2_OUT] = imx_clk_gate("audio_pll2_out", "audio_pll2_bypass", base + 0x14, 13); + clks[IMX8MN_VIDEO_PLL1_OUT] = imx_clk_gate("video_pll1_out", "video_pll1_bypass", base + 0x28, 13); + clks[IMX8MN_DRAM_PLL_OUT] = imx_clk_gate("dram_pll_out", "dram_pll_bypass", base + 0x50, 13); + clks[IMX8MN_GPU_PLL_OUT] = imx_clk_gate("gpu_pll_out", "gpu_pll_bypass", base + 0x64, 11); + clks[IMX8MN_VPU_PLL_OUT] = imx_clk_gate("vpu_pll_out", "vpu_pll_bypass", base + 0x74, 11); + clks[IMX8MN_ARM_PLL_OUT] = imx_clk_gate("arm_pll_out", "arm_pll_bypass", base + 0x84, 11); + clks[IMX8MN_SYS_PLL3_OUT] = imx_clk_gate("sys_pll3_out", "sys_pll3_bypass", base + 0x114, 11); + + /* SYS PLL1 fixed output */ + clks[IMX8MN_SYS_PLL1_40M_CG] = imx_clk_gate("sys_pll1_40m_cg", "sys_pll1_bypass", base + 0x94, 27); + clks[IMX8MN_SYS_PLL1_80M_CG] = imx_clk_gate("sys_pll1_80m_cg", "sys_pll1_bypass", base + 0x94, 25); + clks[IMX8MN_SYS_PLL1_100M_CG] = imx_clk_gate("sys_pll1_100m_cg", "sys_pll1_bypass", base + 0x94, 23); + clks[IMX8MN_SYS_PLL1_133M_CG] = imx_clk_gate("sys_pll1_133m_cg", "sys_pll1_bypass", base + 0x94, 21); + clks[IMX8MN_SYS_PLL1_160M_CG] = imx_clk_gate("sys_pll1_160m_cg", "sys_pll1_bypass", base + 0x94, 19); + clks[IMX8MN_SYS_PLL1_200M_CG] = imx_clk_gate("sys_pll1_200m_cg", "sys_pll1_bypass", base + 0x94, 17); + clks[IMX8MN_SYS_PLL1_266M_CG] = imx_clk_gate("sys_pll1_266m_cg", "sys_pll1_bypass", base + 0x94, 15); + clks[IMX8MN_SYS_PLL1_400M_CG] = imx_clk_gate("sys_pll1_400m_cg", "sys_pll1_bypass", base + 0x94, 13); + clks[IMX8MN_SYS_PLL1_OUT] = imx_clk_gate("sys_pll1_out", "sys_pll1_bypass", base + 0x94, 11); + + clks[IMX8MN_SYS_PLL1_40M] = imx_clk_fixed_factor("sys_pll1_40m", "sys_pll1_40m_cg", 1, 20); + clks[IMX8MN_SYS_PLL1_80M] = imx_clk_fixed_factor("sys_pll1_80m", "sys_pll1_80m_cg", 1, 10); + clks[IMX8MN_SYS_PLL1_100M] = imx_clk_fixed_factor("sys_pll1_100m", "sys_pll1_100m_cg", 1, 8); + clks[IMX8MN_SYS_PLL1_133M] = imx_clk_fixed_factor("sys_pll1_133m", "sys_pll1_133m_cg", 1, 6); + clks[IMX8MN_SYS_PLL1_160M] = imx_clk_fixed_factor("sys_pll1_160m", "sys_pll1_160m_cg", 1, 5); + clks[IMX8MN_SYS_PLL1_200M] = imx_clk_fixed_factor("sys_pll1_200m", "sys_pll1_200m_cg", 1, 4); + clks[IMX8MN_SYS_PLL1_266M] = imx_clk_fixed_factor("sys_pll1_266m", "sys_pll1_266m_cg", 1, 3); + clks[IMX8MN_SYS_PLL1_400M] = imx_clk_fixed_factor("sys_pll1_400m", "sys_pll1_400m_cg", 1, 2); + clks[IMX8MN_SYS_PLL1_800M] = imx_clk_fixed_factor("sys_pll1_800m", "sys_pll1_out", 1, 1); + + /* SYS PLL2 fixed output */ + clks[IMX8MN_SYS_PLL2_50M_CG] = imx_clk_gate("sys_pll2_50m_cg", "sys_pll2_bypass", base + 0x104, 27); + clks[IMX8MN_SYS_PLL2_100M_CG] = imx_clk_gate("sys_pll2_100m_cg", "sys_pll2_bypass", base + 0x104, 25); + clks[IMX8MN_SYS_PLL2_125M_CG] = imx_clk_gate("sys_pll2_125m_cg", "sys_pll2_bypass", base + 0x104, 23); + clks[IMX8MN_SYS_PLL2_166M_CG] = imx_clk_gate("sys_pll2_166m_cg", "sys_pll2_bypass", base + 0x104, 21); + clks[IMX8MN_SYS_PLL2_200M_CG] = imx_clk_gate("sys_pll2_200m_cg", "sys_pll2_bypass", base + 0x104, 19); + clks[IMX8MN_SYS_PLL2_250M_CG] = imx_clk_gate("sys_pll2_250m_cg", "sys_pll2_bypass", base + 0x104, 17); + clks[IMX8MN_SYS_PLL2_333M_CG] = imx_clk_gate("sys_pll2_333m_cg", "sys_pll2_bypass", base + 0x104, 15); + clks[IMX8MN_SYS_PLL2_500M_CG] = imx_clk_gate("sys_pll2_500m_cg", "sys_pll2_bypass", base + 0x104, 13); + clks[IMX8MN_SYS_PLL2_OUT] = imx_clk_gate("sys_pll2_out", "sys_pll2_bypass", base + 0x104, 11); + + clks[IMX8MN_SYS_PLL2_50M] = imx_clk_fixed_factor("sys_pll2_50m", "sys_pll2_50m_cg", 1, 20); + clks[IMX8MN_SYS_PLL2_100M] = imx_clk_fixed_factor("sys_pll2_100m", "sys_pll2_100m_cg", 1, 10); + clks[IMX8MN_SYS_PLL2_125M] = imx_clk_fixed_factor("sys_pll2_125m", "sys_pll2_125m_cg", 1, 8); + clks[IMX8MN_SYS_PLL2_166M] = imx_clk_fixed_factor("sys_pll2_166m", "sys_pll2_166m_cg", 1, 6); + clks[IMX8MN_SYS_PLL2_200M] = imx_clk_fixed_factor("sys_pll2_200m", "sys_pll2_200m_cg", 1, 5); + clks[IMX8MN_SYS_PLL2_250M] = imx_clk_fixed_factor("sys_pll2_250m", "sys_pll2_250m_cg", 1, 4); + clks[IMX8MN_SYS_PLL2_333M] = imx_clk_fixed_factor("sys_pll2_333m", "sys_pll2_333m_cg", 1, 3); + clks[IMX8MN_SYS_PLL2_500M] = imx_clk_fixed_factor("sys_pll2_500m", "sys_pll2_500m_cg", 1, 2); + clks[IMX8MN_SYS_PLL2_1000M] = imx_clk_fixed_factor("sys_pll2_1000m", "sys_pll2_out", 1, 1); + + np = dev->of_node; + base = devm_platform_ioremap_resource(pdev, 0); + if (WARN_ON(IS_ERR(base))) { + ret = PTR_ERR(base); + goto unregister_clks; + } + + /* CORE */ + clks[IMX8MN_CLK_A53_SRC] = imx_clk_mux2("arm_a53_src", base + 0x8000, 24, 3, imx8mn_a53_sels, ARRAY_SIZE(imx8mn_a53_sels)); + clks[IMX8MN_CLK_GPU_CORE_SRC] = imx_clk_mux2("gpu_core_src", base + 0x8180, 24, 3, imx8mn_gpu_core_sels, ARRAY_SIZE(imx8mn_gpu_core_sels)); + clks[IMX8MN_CLK_GPU_SHADER_SRC] = imx_clk_mux2("gpu_shader_src", base + 0x8200, 24, 3, imx8mn_gpu_shader_sels, ARRAY_SIZE(imx8mn_gpu_shader_sels)); + clks[IMX8MN_CLK_A53_CG] = imx_clk_gate3("arm_a53_cg", "arm_a53_src", base + 0x8000, 28); + clks[IMX8MN_CLK_GPU_CORE_CG] = imx_clk_gate3("gpu_core_cg", "gpu_core_src", base + 0x8180, 28); + clks[IMX8MN_CLK_GPU_SHADER_CG] = imx_clk_gate3("gpu_shader_cg", "gpu_shader_src", base + 0x8200, 28); + + clks[IMX8MN_CLK_A53_DIV] = imx_clk_divider2("arm_a53_div", "arm_a53_cg", base + 0x8000, 0, 3); + clks[IMX8MN_CLK_GPU_CORE_DIV] = imx_clk_divider2("gpu_core_div", "gpu_core_cg", base + 0x8180, 0, 3); + clks[IMX8MN_CLK_GPU_SHADER_DIV] = imx_clk_divider2("gpu_shader_div", "gpu_shader_cg", base + 0x8200, 0, 3); + + /* BUS */ + clks[IMX8MN_CLK_MAIN_AXI] = imx8m_clk_composite_critical("main_axi", imx8mn_main_axi_sels, base + 0x8800); + clks[IMX8MN_CLK_ENET_AXI] = imx8m_clk_composite("enet_axi", imx8mn_enet_axi_sels, base + 0x8880); + clks[IMX8MN_CLK_NAND_USDHC_BUS] = imx8m_clk_composite("nand_usdhc_bus", imx8mn_nand_usdhc_sels, base + 0x8900); + clks[IMX8MN_CLK_DISP_AXI] = imx8m_clk_composite("disp_axi", imx8mn_disp_axi_sels, base + 0x8a00); + clks[IMX8MN_CLK_DISP_APB] = imx8m_clk_composite("disp_apb", imx8mn_disp_apb_sels, base + 0x8a80); + clks[IMX8MN_CLK_USB_BUS] = imx8m_clk_composite("usb_bus", imx8mn_usb_bus_sels, base + 0x8b80); + clks[IMX8MN_CLK_GPU_AXI] = imx8m_clk_composite("gpu_axi", imx8mn_gpu_axi_sels, base + 0x8c00); + clks[IMX8MN_CLK_GPU_AHB] = imx8m_clk_composite("gpu_ahb", imx8mn_gpu_ahb_sels, base + 0x8c80); + clks[IMX8MN_CLK_NOC] = imx8m_clk_composite_critical("noc", imx8mn_noc_sels, base + 0x8d00); + + 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); + 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); + clks[IMX8MN_CLK_SPDIF1] = imx8m_clk_composite("spdif1", imx8mn_spdif1_sels, base + 0xa880); + clks[IMX8MN_CLK_ENET_REF] = imx8m_clk_composite("enet_ref", imx8mn_enet_ref_sels, base + 0xa980); + clks[IMX8MN_CLK_ENET_TIMER] = imx8m_clk_composite("enet_timer", imx8mn_enet_timer_sels, base + 0xaa00); + clks[IMX8MN_CLK_ENET_PHY_REF] = imx8m_clk_composite("enet_phy", imx8mn_enet_phy_sels, base + 0xaa80); + clks[IMX8MN_CLK_NAND] = imx8m_clk_composite("nand", imx8mn_nand_sels, base + 0xab00); + clks[IMX8MN_CLK_QSPI] = imx8m_clk_composite("qspi", imx8mn_qspi_sels, base + 0xab80); + clks[IMX8MN_CLK_USDHC1] = imx8m_clk_composite("usdhc1", imx8mn_usdhc1_sels, base + 0xac00); + clks[IMX8MN_CLK_USDHC2] = imx8m_clk_composite("usdhc2", imx8mn_usdhc2_sels, base + 0xac80); + clks[IMX8MN_CLK_I2C1] = imx8m_clk_composite("i2c1", imx8mn_i2c1_sels, base + 0xad00); + clks[IMX8MN_CLK_I2C2] = imx8m_clk_composite("i2c2", imx8mn_i2c2_sels, base + 0xad80); + clks[IMX8MN_CLK_I2C3] = imx8m_clk_composite("i2c3", imx8mn_i2c3_sels, base + 0xae00); + clks[IMX8MN_CLK_I2C4] = imx8m_clk_composite("i2c4", imx8mn_i2c4_sels, base + 0xae80); + clks[IMX8MN_CLK_UART1] = imx8m_clk_composite("uart1", imx8mn_uart1_sels, base + 0xaf00); + clks[IMX8MN_CLK_UART2] = imx8m_clk_composite("uart2", imx8mn_uart2_sels, base + 0xaf80); + clks[IMX8MN_CLK_UART3] = imx8m_clk_composite("uart3", imx8mn_uart3_sels, base + 0xb000); + clks[IMX8MN_CLK_UART4] = imx8m_clk_composite("uart4", imx8mn_uart4_sels, base + 0xb080); + clks[IMX8MN_CLK_USB_CORE_REF] = imx8m_clk_composite("usb_core_ref", imx8mn_usb_core_sels, base + 0xb100); + clks[IMX8MN_CLK_USB_PHY_REF] = imx8m_clk_composite("usb_phy_ref", imx8mn_usb_phy_sels, base + 0xb180); + clks[IMX8MN_CLK_GIC] = imx8m_clk_composite_critical("gic", imx8mn_gic_sels, base + 0xb200); + clks[IMX8MN_CLK_ECSPI1] = imx8m_clk_composite("ecspi1", imx8mn_ecspi1_sels, base + 0xb280); + clks[IMX8MN_CLK_ECSPI2] = imx8m_clk_composite("ecspi2", imx8mn_ecspi2_sels, base + 0xb300); + clks[IMX8MN_CLK_PWM1] = imx8m_clk_composite("pwm1", imx8mn_pwm1_sels, base + 0xb380); + clks[IMX8MN_CLK_PWM2] = imx8m_clk_composite("pwm2", imx8mn_pwm2_sels, base + 0xb400); + clks[IMX8MN_CLK_PWM3] = imx8m_clk_composite("pwm3", imx8mn_pwm3_sels, base + 0xb480); + clks[IMX8MN_CLK_PWM4] = imx8m_clk_composite("pwm4", imx8mn_pwm4_sels, base + 0xb500); + clks[IMX8MN_CLK_WDOG] = imx8m_clk_composite("wdog", imx8mn_wdog_sels, base + 0xb900); + clks[IMX8MN_CLK_WRCLK] = imx8m_clk_composite("wrclk", imx8mn_wrclk_sels, base + 0xb980); + clks[IMX8MN_CLK_CLKO1] = imx8m_clk_composite("clko1", imx8mn_clko1_sels, base + 0xba00); + clks[IMX8MN_CLK_CLKO2] = imx8m_clk_composite("clko2", imx8mn_clko2_sels, base + 0xba80); + clks[IMX8MN_CLK_DSI_CORE] = imx8m_clk_composite("dsi_core", imx8mn_dsi_core_sels, base + 0xbb00); + clks[IMX8MN_CLK_DSI_PHY_REF] = imx8m_clk_composite("dsi_phy_ref", imx8mn_dsi_phy_sels, base + 0xbb80); + clks[IMX8MN_CLK_DSI_DBI] = imx8m_clk_composite("dsi_dbi", imx8mn_dsi_dbi_sels, base + 0xbc00); + clks[IMX8MN_CLK_USDHC3] = imx8m_clk_composite("usdhc3", imx8mn_usdhc3_sels, base + 0xbc80); + clks[IMX8MN_CLK_CAMERA_PIXEL] = imx8m_clk_composite("camera_pixel", imx8mn_camera_pixel_sels, base + 0xbd00); + clks[IMX8MN_CLK_CSI1_PHY_REF] = imx8m_clk_composite("csi1_phy_ref", imx8mn_csi1_phy_sels, base + 0xbd80); + clks[IMX8MN_CLK_CSI2_PHY_REF] = imx8m_clk_composite("csi2_phy_ref", imx8mn_csi2_phy_sels, base + 0xbf00); + clks[IMX8MN_CLK_CSI2_ESC] = imx8m_clk_composite("csi2_esc", imx8mn_csi2_esc_sels, base + 0xbf80); + clks[IMX8MN_CLK_ECSPI3] = imx8m_clk_composite("ecspi3", imx8mn_ecspi3_sels, base + 0xc180); + clks[IMX8MN_CLK_PDM] = imx8m_clk_composite("pdm", imx8mn_pdm_sels, base + 0xc200); + clks[IMX8MN_CLK_SAI7] = imx8m_clk_composite("sai7", imx8mn_sai7_sels, base + 0xc300); + + clks[IMX8MN_CLK_ECSPI1_ROOT] = imx_clk_gate4("ecspi1_root_clk", "ecspi1", base + 0x4070, 0); + clks[IMX8MN_CLK_ECSPI2_ROOT] = imx_clk_gate4("ecspi2_root_clk", "ecspi2", base + 0x4080, 0); + clks[IMX8MN_CLK_ECSPI3_ROOT] = imx_clk_gate4("ecspi3_root_clk", "ecspi3", base + 0x4090, 0); + clks[IMX8MN_CLK_ENET1_ROOT] = imx_clk_gate4("enet1_root_clk", "enet_axi", base + 0x40a0, 0); + clks[IMX8MN_CLK_GPIO1_ROOT] = imx_clk_gate4("gpio1_root_clk", "ipg_root", base + 0x40b0, 0); + clks[IMX8MN_CLK_GPIO2_ROOT] = imx_clk_gate4("gpio2_root_clk", "ipg_root", base + 0x40c0, 0); + clks[IMX8MN_CLK_GPIO3_ROOT] = imx_clk_gate4("gpio3_root_clk", "ipg_root", base + 0x40d0, 0); + clks[IMX8MN_CLK_GPIO4_ROOT] = imx_clk_gate4("gpio4_root_clk", "ipg_root", base + 0x40e0, 0); + clks[IMX8MN_CLK_GPIO5_ROOT] = imx_clk_gate4("gpio5_root_clk", "ipg_root", base + 0x40f0, 0); + clks[IMX8MN_CLK_I2C1_ROOT] = imx_clk_gate4("i2c1_root_clk", "i2c1", base + 0x4170, 0); + clks[IMX8MN_CLK_I2C2_ROOT] = imx_clk_gate4("i2c2_root_clk", "i2c2", base + 0x4180, 0); + clks[IMX8MN_CLK_I2C3_ROOT] = imx_clk_gate4("i2c3_root_clk", "i2c3", base + 0x4190, 0); + clks[IMX8MN_CLK_I2C4_ROOT] = imx_clk_gate4("i2c4_root_clk", "i2c4", base + 0x41a0, 0); + clks[IMX8MN_CLK_MU_ROOT] = imx_clk_gate4("mu_root_clk", "ipg_root", base + 0x4210, 0); + clks[IMX8MN_CLK_OCOTP_ROOT] = imx_clk_gate4("ocotp_root_clk", "ipg_root", base + 0x4220, 0); + clks[IMX8MN_CLK_PWM1_ROOT] = imx_clk_gate4("pwm1_root_clk", "pwm1", base + 0x4280, 0); + clks[IMX8MN_CLK_PWM2_ROOT] = imx_clk_gate4("pwm2_root_clk", "pwm2", base + 0x4290, 0); + clks[IMX8MN_CLK_PWM3_ROOT] = imx_clk_gate4("pwm3_root_clk", "pwm3", base + 0x42a0, 0); + clks[IMX8MN_CLK_PWM4_ROOT] = imx_clk_gate4("pwm4_root_clk", "pwm4", base + 0x42b0, 0); + clks[IMX8MN_CLK_QSPI_ROOT] = imx_clk_gate4("qspi_root_clk", "qspi", base + 0x42f0, 0); + clks[IMX8MN_CLK_NAND_ROOT] = imx_clk_gate2_shared2("nand_root_clk", "nand", base + 0x4300, 0, &share_count_nand); + clks[IMX8MN_CLK_NAND_USDHC_BUS_RAWNAND_CLK] = imx_clk_gate2_shared2("nand_usdhc_rawnand_clk", "nand_usdhc_bus", base + 0x4300, 0, &share_count_nand); + clks[IMX8MN_CLK_SAI2_ROOT] = imx_clk_gate2_shared2("sai2_root_clk", "sai2", base + 0x4340, 0, &share_count_sai2); + clks[IMX8MN_CLK_SAI2_IPG] = imx_clk_gate2_shared2("sai2_ipg_clk", "ipg_audio_root", base + 0x4340, 0, &share_count_sai2); + clks[IMX8MN_CLK_SAI3_ROOT] = imx_clk_gate2_shared2("sai3_root_clk", "sai3", base + 0x4350, 0, &share_count_sai3); + clks[IMX8MN_CLK_SAI3_IPG] = imx_clk_gate2_shared2("sai3_ipg_clk", "ipg_audio_root", base + 0x4350, 0, &share_count_sai3); + clks[IMX8MN_CLK_SAI5_ROOT] = imx_clk_gate2_shared2("sai5_root_clk", "sai5", base + 0x4370, 0, &share_count_sai5); + clks[IMX8MN_CLK_SAI5_IPG] = imx_clk_gate2_shared2("sai5_ipg_clk", "ipg_audio_root", base + 0x4370, 0, &share_count_sai5); + clks[IMX8MN_CLK_SAI6_ROOT] = imx_clk_gate2_shared2("sai6_root_clk", "sai6", base + 0x4380, 0, &share_count_sai6); + clks[IMX8MN_CLK_SAI6_IPG] = imx_clk_gate2_shared2("sai6_ipg_clk", "ipg_audio_root", base + 0x4380, 0, &share_count_sai6); + clks[IMX8MN_CLK_UART1_ROOT] = imx_clk_gate4("uart1_root_clk", "uart1", base + 0x4490, 0); + clks[IMX8MN_CLK_UART2_ROOT] = imx_clk_gate4("uart2_root_clk", "uart2", base + 0x44a0, 0); + clks[IMX8MN_CLK_UART3_ROOT] = imx_clk_gate4("uart3_root_clk", "uart3", base + 0x44b0, 0); + clks[IMX8MN_CLK_UART4_ROOT] = imx_clk_gate4("uart4_root_clk", "uart4", base + 0x44c0, 0); + clks[IMX8MN_CLK_USB1_CTRL_ROOT] = imx_clk_gate4("usb1_ctrl_root_clk", "usb_core_ref", base + 0x44d0, 0); + clks[IMX8MN_CLK_GPU_CORE_ROOT] = imx_clk_gate4("gpu_core_root_clk", "gpu_core_div", base + 0x44f0, 0); + clks[IMX8MN_CLK_USDHC1_ROOT] = imx_clk_gate4("usdhc1_root_clk", "usdhc1", base + 0x4510, 0); + clks[IMX8MN_CLK_USDHC2_ROOT] = imx_clk_gate4("usdhc2_root_clk", "usdhc2", base + 0x4520, 0); + clks[IMX8MN_CLK_WDOG1_ROOT] = imx_clk_gate4("wdog1_root_clk", "wdog", base + 0x4530, 0); + clks[IMX8MN_CLK_WDOG2_ROOT] = imx_clk_gate4("wdog2_root_clk", "wdog", base + 0x4540, 0); + clks[IMX8MN_CLK_WDOG3_ROOT] = imx_clk_gate4("wdog3_root_clk", "wdog", base + 0x4550, 0); + clks[IMX8MN_CLK_GPU_BUS_ROOT] = imx_clk_gate4("gpu_root_clk", "gpu_axi", base + 0x4570, 0); + clks[IMX8MN_CLK_ASRC_ROOT] = imx_clk_gate4("asrc_root_clk", "audio_ahb", base + 0x4580, 0); + clks[IMX8MN_CLK_PDM_ROOT] = imx_clk_gate2_shared2("pdm_root_clk", "pdm", base + 0x45b0, 0, &share_count_pdm); + clks[IMX8MN_CLK_PDM_IPG] = imx_clk_gate2_shared2("pdm_ipg_clk", "ipg_audio_root", base + 0x45b0, 0, &share_count_pdm); + clks[IMX8MN_CLK_DISP_AXI_ROOT] = imx_clk_gate2_shared2("disp_axi_root_clk", "disp_axi", base + 0x45d0, 0, &share_count_disp); + clks[IMX8MN_CLK_DISP_APB_ROOT] = imx_clk_gate2_shared2("disp_apb_root_clk", "disp_apb", base + 0x45d0, 0, &share_count_disp); + clks[IMX8MN_CLK_CAMERA_PIXEL_ROOT] = imx_clk_gate2_shared2("camera_pixel_clk", "camera_pixel", base + 0x45d0, 0, &share_count_disp); + clks[IMX8MN_CLK_DISP_PIXEL_ROOT] = imx_clk_gate2_shared2("disp_pixel_clk", "disp_pixel", base + 0x45d0, 0, &share_count_disp); + clks[IMX8MN_CLK_USDHC3_ROOT] = imx_clk_gate4("usdhc3_root_clk", "usdhc3", base + 0x45e0, 0); + clks[IMX8MN_CLK_TMU_ROOT] = imx_clk_gate4("tmu_root_clk", "ipg_root", base + 0x4620, 0); + clks[IMX8MN_CLK_SDMA1_ROOT] = imx_clk_gate4("sdma1_clk", "ipg_root", base + 0x43a0, 0); + clks[IMX8MN_CLK_SDMA2_ROOT] = imx_clk_gate4("sdma2_clk", "ipg_audio_root", base + 0x43b0, 0); + clks[IMX8MN_CLK_SDMA3_ROOT] = imx_clk_gate4("sdma3_clk", "ipg_audio_root", base + 0x45f0, 0); + clks[IMX8MN_CLK_SAI7_ROOT] = imx_clk_gate2_shared2("sai7_root_clk", "sai7", base + 0x4650, 0, &share_count_sai7); + + clks[IMX8MN_CLK_DRAM_ALT_ROOT] = imx_clk_fixed_factor("dram_alt_root", "dram_alt", 1, 4); + + clks[IMX8MN_CLK_ARM] = imx_clk_cpu("arm", "arm_a53_div", + clks[IMX8MN_CLK_A53_DIV], + clks[IMX8MN_CLK_A53_SRC], + clks[IMX8MN_ARM_PLL_OUT], + clks[IMX8MN_CLK_24M]); + + imx_check_clocks(clks, ARRAY_SIZE(clks)); + + clk_data.clks = clks; + clk_data.clk_num = ARRAY_SIZE(clks); + ret = of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data); + if (ret < 0) { + dev_err(dev, "failed to register clks for i.MX8MN\n"); + goto unregister_clks; + } + + imx_register_uart_clocks(uart_clks); + + return 0; + +unregister_clks: + imx_unregister_clocks(clks, ARRAY_SIZE(clks)); + + return ret; +} + +static const struct of_device_id imx8mn_clk_of_match[] = { + { .compatible = "fsl,imx8mn-ccm" }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, imx8mn_clk_of_match); + +static struct platform_driver imx8mn_clk_driver = { + .probe = imx8mn_clocks_probe, + .driver = { + .name = "imx8mn-ccm", + .of_match_table = of_match_ptr(imx8mn_clk_of_match), + }, +}; +module_platform_driver(imx8mn_clk_driver); diff --git a/include/dt-bindings/clock/imx8mn-clock.h b/include/dt-bindings/clock/imx8mn-clock.h new file mode 100644 index 000000000000..0f2b8423ce1d --- /dev/null +++ b/include/dt-bindings/clock/imx8mn-clock.h @@ -0,0 +1,233 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2018-2019 NXP + */ + +#ifndef __DT_BINDINGS_CLOCK_IMX8MN_H +#define __DT_BINDINGS_CLOCK_IMX8MN_H + +#define IMX8MN_CLK_DUMMY 0 +#define IMX8MN_CLK_32K 1 +#define IMX8MN_CLK_24M 2 +#define IMX8MN_OSC_HDMI_CLK 3 +#define IMX8MN_CLK_EXT1 4 +#define IMX8MN_CLK_EXT2 5 +#define IMX8MN_CLK_EXT3 6 +#define IMX8MN_CLK_EXT4 7 +#define IMX8MN_AUDIO_PLL1_REF_SEL 8 +#define IMX8MN_AUDIO_PLL2_REF_SEL 9 +#define IMX8MN_VIDEO_PLL1_REF_SEL 10 +#define IMX8MN_DRAM_PLL_REF_SEL 11 +#define IMX8MN_GPU_PLL_REF_SEL 12 +#define IMX8MN_VPU_PLL_REF_SEL 13 +#define IMX8MN_ARM_PLL_REF_SEL 14 +#define IMX8MN_SYS_PLL1_REF_SEL 15 +#define IMX8MN_SYS_PLL2_REF_SEL 16 +#define IMX8MN_SYS_PLL3_REF_SEL 17 +#define IMX8MN_AUDIO_PLL1 18 +#define IMX8MN_AUDIO_PLL2 19 +#define IMX8MN_VIDEO_PLL1 20 +#define IMX8MN_DRAM_PLL 21 +#define IMX8MN_GPU_PLL 22 +#define IMX8MN_VPU_PLL 23 +#define IMX8MN_ARM_PLL 24 +#define IMX8MN_SYS_PLL1 25 +#define IMX8MN_SYS_PLL2 26 +#define IMX8MN_SYS_PLL3 27 +#define IMX8MN_AUDIO_PLL1_BYPASS 28 +#define IMX8MN_AUDIO_PLL2_BYPASS 29 +#define IMX8MN_VIDEO_PLL1_BYPASS 30 +#define IMX8MN_DRAM_PLL_BYPASS 31 +#define IMX8MN_GPU_PLL_BYPASS 32 +#define IMX8MN_VPU_PLL_BYPASS 33 +#define IMX8MN_ARM_PLL_BYPASS 34 +#define IMX8MN_SYS_PLL1_BYPASS 35 +#define IMX8MN_SYS_PLL2_BYPASS 36 +#define IMX8MN_SYS_PLL3_BYPASS 37 +#define IMX8MN_AUDIO_PLL1_OUT 38 +#define IMX8MN_AUDIO_PLL2_OUT 39 +#define IMX8MN_VIDEO_PLL1_OUT 40 +#define IMX8MN_DRAM_PLL_OUT 41 +#define IMX8MN_GPU_PLL_OUT 42 +#define IMX8MN_VPU_PLL_OUT 43 +#define IMX8MN_ARM_PLL_OUT 44 +#define IMX8MN_SYS_PLL1_OUT 45 +#define IMX8MN_SYS_PLL2_OUT 46 +#define IMX8MN_SYS_PLL3_OUT 47 +#define IMX8MN_SYS_PLL1_40M 48 +#define IMX8MN_SYS_PLL1_80M 49 +#define IMX8MN_SYS_PLL1_100M 50 +#define IMX8MN_SYS_PLL1_133M 51 +#define IMX8MN_SYS_PLL1_160M 52 +#define IMX8MN_SYS_PLL1_200M 53 +#define IMX8MN_SYS_PLL1_266M 54 +#define IMX8MN_SYS_PLL1_400M 55 +#define IMX8MN_SYS_PLL1_800M 56 +#define IMX8MN_SYS_PLL2_50M 57 +#define IMX8MN_SYS_PLL2_100M 58 +#define IMX8MN_SYS_PLL2_125M 59 +#define IMX8MN_SYS_PLL2_166M 60 +#define IMX8MN_SYS_PLL2_200M 61 +#define IMX8MN_SYS_PLL2_250M 62 +#define IMX8MN_SYS_PLL2_333M 63 +#define IMX8MN_SYS_PLL2_500M 64 +#define IMX8MN_SYS_PLL2_1000M 65 + +/* CORE CLOCK ROOT */ +#define IMX8MN_CLK_A53_SRC 66 +#define IMX8MN_CLK_GPU_CORE_SRC 67 +#define IMX8MN_CLK_GPU_SHADER_SRC 68 +#define IMX8MN_CLK_A53_CG 69 +#define IMX8MN_CLK_GPU_CORE_CG 70 +#define IMX8MN_CLK_GPU_SHADER_CG 71 +#define IMX8MN_CLK_A53_DIV 72 +#define IMX8MN_CLK_GPU_CORE_DIV 73 +#define IMX8MN_CLK_GPU_SHADER_DIV 74 + +/* BUS CLOCK ROOT */ +#define IMX8MN_CLK_MAIN_AXI 75 +#define IMX8MN_CLK_ENET_AXI 76 +#define IMX8MN_CLK_NAND_USDHC_BUS 77 +#define IMX8MN_CLK_DISP_AXI 78 +#define IMX8MN_CLK_DISP_APB 79 +#define IMX8MN_CLK_USB_BUS 80 +#define IMX8MN_CLK_GPU_AXI 81 +#define IMX8MN_CLK_GPU_AHB 82 +#define IMX8MN_CLK_NOC 83 +#define IMX8MN_CLK_AHB 84 +#define IMX8MN_CLK_AUDIO_AHB 85 + +/* IPG CLOCK ROOT */ +#define IMX8MN_CLK_IPG_ROOT 86 +#define IMX8MN_CLK_IPG_AUDIO_ROOT 87 + +/* IP */ +#define IMX8MN_CLK_DRAM_CORE 88 +#define IMX8MN_CLK_DRAM_ALT 89 +#define IMX8MN_CLK_DRAM_APB 90 +#define IMX8MN_CLK_DRAM_ALT_ROOT 91 +#define IMX8MN_CLK_DISP_PIXEL 92 +#define IMX8MN_CLK_SAI2 93 +#define IMX8MN_CLK_SAI3 94 +#define IMX8MN_CLK_SAI5 95 +#define IMX8MN_CLK_SAI6 96 +#define IMX8MN_CLK_SPDIF1 97 +#define IMX8MN_CLK_ENET_REF 98 +#define IMX8MN_CLK_ENET_TIMER 99 +#define IMX8MN_CLK_ENET_PHY_REF 100 +#define IMX8MN_CLK_NAND 101 +#define IMX8MN_CLK_QSPI 102 +#define IMX8MN_CLK_USDHC1 103 +#define IMX8MN_CLK_USDHC2 104 +#define IMX8MN_CLK_I2C1 105 +#define IMX8MN_CLK_I2C2 106 +#define IMX8MN_CLK_I2C3 107 +#define IMX8MN_CLK_I2C4 118 +#define IMX8MN_CLK_UART1 119 +#define IMX8MN_CLK_UART2 110 +#define IMX8MN_CLK_UART3 111 +#define IMX8MN_CLK_UART4 112 +#define IMX8MN_CLK_USB_CORE_REF 113 +#define IMX8MN_CLK_USB_PHY_REF 114 +#define IMX8MN_CLK_ECSPI1 115 +#define IMX8MN_CLK_ECSPI2 116 +#define IMX8MN_CLK_PWM1 117 +#define IMX8MN_CLK_PWM2 118 +#define IMX8MN_CLK_PWM3 119 +#define IMX8MN_CLK_PWM4 120 +#define IMX8MN_CLK_WDOG 121 +#define IMX8MN_CLK_WRCLK 122 +#define IMX8MN_CLK_CLKO1 123 +#define IMX8MN_CLK_CLKO2 124 +#define IMX8MN_CLK_DSI_CORE 125 +#define IMX8MN_CLK_DSI_PHY_REF 126 +#define IMX8MN_CLK_DSI_DBI 127 +#define IMX8MN_CLK_USDHC3 128 +#define IMX8MN_CLK_CAMERA_PIXEL 129 +#define IMX8MN_CLK_CSI1_PHY_REF 130 +#define IMX8MN_CLK_CSI2_PHY_REF 131 +#define IMX8MN_CLK_CSI2_ESC 132 +#define IMX8MN_CLK_ECSPI3 133 +#define IMX8MN_CLK_PDM 134 +#define IMX8MN_CLK_SAI7 135 + +#define IMX8MN_CLK_ECSPI1_ROOT 136 +#define IMX8MN_CLK_ECSPI2_ROOT 137 +#define IMX8MN_CLK_ECSPI3_ROOT 138 +#define IMX8MN_CLK_ENET1_ROOT 139 +#define IMX8MN_CLK_GPIO1_ROOT 140 +#define IMX8MN_CLK_GPIO2_ROOT 141 +#define IMX8MN_CLK_GPIO3_ROOT 142 +#define IMX8MN_CLK_GPIO4_ROOT 143 +#define IMX8MN_CLK_GPIO5_ROOT 144 +#define IMX8MN_CLK_I2C1_ROOT 145 +#define IMX8MN_CLK_I2C2_ROOT 146 +#define IMX8MN_CLK_I2C3_ROOT 147 +#define IMX8MN_CLK_I2C4_ROOT 148 +#define IMX8MN_CLK_MU_ROOT 149 +#define IMX8MN_CLK_OCOTP_ROOT 150 +#define IMX8MN_CLK_PWM1_ROOT 151 +#define IMX8MN_CLK_PWM2_ROOT 152 +#define IMX8MN_CLK_PWM3_ROOT 153 +#define IMX8MN_CLK_PWM4_ROOT 154 +#define IMX8MN_CLK_QSPI_ROOT 155 +#define IMX8MN_CLK_NAND_ROOT 156 +#define IMX8MN_CLK_SAI2_ROOT 157 +#define IMX8MN_CLK_SAI2_IPG 158 +#define IMX8MN_CLK_SAI3_ROOT 159 +#define IMX8MN_CLK_SAI3_IPG 160 +#define IMX8MN_CLK_SAI5_ROOT 161 +#define IMX8MN_CLK_SAI5_IPG 162 +#define IMX8MN_CLK_SAI6_ROOT 163 +#define IMX8MN_CLK_SAI6_IPG 164 +#define IMX8MN_CLK_SAI7_ROOT 165 +#define IMX8MN_CLK_SAI7_IPG 166 +#define IMX8MN_CLK_SDMA1_ROOT 167 +#define IMX8MN_CLK_SDMA2_ROOT 168 +#define IMX8MN_CLK_UART1_ROOT 169 +#define IMX8MN_CLK_UART2_ROOT 170 +#define IMX8MN_CLK_UART3_ROOT 171 +#define IMX8MN_CLK_UART4_ROOT 172 +#define IMX8MN_CLK_USB1_CTRL_ROOT 173 +#define IMX8MN_CLK_USDHC1_ROOT 174 +#define IMX8MN_CLK_USDHC2_ROOT 175 +#define IMX8MN_CLK_WDOG1_ROOT 176 +#define IMX8MN_CLK_WDOG2_ROOT 177 +#define IMX8MN_CLK_WDOG3_ROOT 178 +#define IMX8MN_CLK_GPU_BUS_ROOT 179 +#define IMX8MN_CLK_ASRC_ROOT 180 +#define IMX8MN_CLK_GPU3D_ROOT 181 +#define IMX8MN_CLK_PDM_ROOT 182 +#define IMX8MN_CLK_PDM_IPG 183 +#define IMX8MN_CLK_DISP_AXI_ROOT 184 +#define IMX8MN_CLK_DISP_APB_ROOT 185 +#define IMX8MN_CLK_DISP_PIXEL_ROOT 186 +#define IMX8MN_CLK_CAMERA_PIXEL_ROOT 187 +#define IMX8MN_CLK_USDHC3_ROOT 188 +#define IMX8MN_CLK_SDMA3_ROOT 189 +#define IMX8MN_CLK_TMU_ROOT 190 +#define IMX8MN_CLK_ARM 191 +#define IMX8MN_CLK_NAND_USDHC_BUS_RAWNAND_CLK 192 +#define IMX8MN_CLK_GPU_CORE_ROOT 193 +#define IMX8MN_CLK_GIC 194 + +#define IMX8MN_SYS_PLL1_40M_CG 195 +#define IMX8MN_SYS_PLL1_80M_CG 196 +#define IMX8MN_SYS_PLL1_100M_CG 197 +#define IMX8MN_SYS_PLL1_133M_CG 198 +#define IMX8MN_SYS_PLL1_160M_CG 199 +#define IMX8MN_SYS_PLL1_200M_CG 200 +#define IMX8MN_SYS_PLL1_266M_CG 201 +#define IMX8MN_SYS_PLL1_400M_CG 202 +#define IMX8MN_SYS_PLL2_50M_CG 203 +#define IMX8MN_SYS_PLL2_100M_CG 204 +#define IMX8MN_SYS_PLL2_125M_CG 205 +#define IMX8MN_SYS_PLL2_166M_CG 206 +#define IMX8MN_SYS_PLL2_200M_CG 207 +#define IMX8MN_SYS_PLL2_250M_CG 208 +#define IMX8MN_SYS_PLL2_333M_CG 209 +#define IMX8MN_SYS_PLL2_500M_CG 210 + +#define IMX8MN_CLK_END 211 + +#endif -- GitLab From 26560a6f6bf3a2da7b68df4dcd7a562dfd3f21ca Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 19 Sep 2019 21:39:03 +0300 Subject: [PATCH 07/71] PM / devfreq: Check NULL governor in available_governors_show The governor is initialized after sysfs attributes become visible so in theory the governor field can be NULL here. Fixes: bcf23c79c4e46 ("PM / devfreq: Fix available_governor sysfs") Signed-off-by: Leonard Crestez Reviewed-by: Matthias Kaehlcke Reviewed-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index ab22bf8a12d6..1406e98b6d7f 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -1195,7 +1195,7 @@ static ssize_t available_governors_show(struct device *d, * The devfreq with immutable governor (e.g., passive) shows * only own governor. */ - if (df->governor->immutable) { + if (df->governor && df->governor->immutable) { count = scnprintf(&buf[count], DEVFREQ_NAME_LEN, "%s ", df->governor_name); /* -- GitLab From ecc7105a1fde71a1d0859c9403b93d00128b1a9e Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 17 Sep 2019 17:50:27 +0300 Subject: [PATCH 08/71] PM / devfreq: Lock devfreq in trans_stat_show There is no locking in this sysfs show function so stats printing can race with a devfreq_update_status called as part of freq switching or with initialization. Also add an assert in devfreq_update_status to make it clear that lock must be held by caller. Fixes: 39688ce6facd ("PM / devfreq: account suspend/resume for stats") Cc: stable@vger.kernel.org Signed-off-by: Leonard Crestez Reviewed-by: Matthias Kaehlcke Reviewed-by: Chanwoo Choi --- Changes since v2: * Move assert higher * Drop useless comment * Add Fixes: tag and Cc: stable Link to v2: https://patchwork.kernel.org/patch/11157315/ Changes since v1: * Split from series: low-priority bugfix not strictly required for PM QoS * Only keep lock during update, release before sprintf Link to v1: https://patchwork.kernel.org/patch/11149493/ --- drivers/devfreq/devfreq.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 1406e98b6d7f..584322d3042a 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -160,6 +160,7 @@ int devfreq_update_status(struct devfreq *devfreq, unsigned long freq) int lev, prev_lev, ret = 0; unsigned long cur_time; + lockdep_assert_held(&devfreq->lock); cur_time = jiffies; /* Immediately exit if previous_freq is not initialized yet. */ @@ -1397,12 +1398,17 @@ static ssize_t trans_stat_show(struct device *dev, int i, j; unsigned int max_state = devfreq->profile->max_state; - if (!devfreq->stop_polling && - devfreq_update_status(devfreq, devfreq->previous_freq)) - return 0; if (max_state == 0) return sprintf(buf, "Not Supported.\n"); + mutex_lock(&devfreq->lock); + if (!devfreq->stop_polling && + devfreq_update_status(devfreq, devfreq->previous_freq)) { + mutex_unlock(&devfreq->lock); + return 0; + } + mutex_unlock(&devfreq->lock); + len = sprintf(buf, " From : To\n"); len += sprintf(buf + len, " :"); for (i = 0; i < max_state; i++) -- GitLab From acd4d109b754f13df2a028c1ba887f040a2b7a10 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Wed, 18 Sep 2019 01:45:34 +0300 Subject: [PATCH 09/71] PM / devfreq: Don't fail devfreq_dev_release if not in list Right now devfreq_dev_release will print a warning and abort the rest of the cleanup if the devfreq instance is not part of the global devfreq_list. But this is a valid scenario, for example it can happen if the governor can't be found or on any other init error that happens after device_register. Initialize devfreq->node to an empty list head in devfreq_add_device so that list_del becomes a safe noop inside devfreq_dev_release and we can continue the rest of the cleanup. Signed-off-by: Leonard Crestez Reviewed-by: Matthias Kaehlcke Reviewed-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 584322d3042a..b20f0e59ca64 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -584,11 +584,6 @@ static void devfreq_dev_release(struct device *dev) struct devfreq *devfreq = to_devfreq(dev); mutex_lock(&devfreq_list_lock); - if (IS_ERR(find_device_devfreq(devfreq->dev.parent))) { - mutex_unlock(&devfreq_list_lock); - dev_warn(&devfreq->dev, "releasing devfreq which doesn't exist\n"); - return; - } list_del(&devfreq->node); mutex_unlock(&devfreq_list_lock); @@ -643,6 +638,7 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->dev.parent = dev; devfreq->dev.class = devfreq_class; devfreq->dev.release = devfreq_dev_release; + INIT_LIST_HEAD(&devfreq->node); devfreq->profile = profile; strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN); devfreq->previous_freq = profile->initial_freq; -- GitLab From 572cdd4718ab2f6418f1f9b5eb160a68ed489c2b Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 17 Sep 2019 23:20:00 +0300 Subject: [PATCH 10/71] PM / devfreq: Move more initialization before registration In general it is a better to initialize an object before making it accessible externally (through device_register). This makes it possible to avoid relying on locking a partially initialized object. Signed-off-by: Leonard Crestez Reviewed-by: Matthias Kaehlcke --- drivers/devfreq/devfreq.c | 43 +++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index b20f0e59ca64..0f0c3abf8ebd 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -590,6 +590,8 @@ static void devfreq_dev_release(struct device *dev) if (devfreq->profile->exit) devfreq->profile->exit(devfreq->dev.parent); + kfree(devfreq->time_in_state); + kfree(devfreq->trans_table); mutex_destroy(&devfreq->lock); kfree(devfreq); } @@ -673,16 +675,7 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); atomic_set(&devfreq->suspend_count, 0); - dev_set_name(&devfreq->dev, "devfreq%d", - atomic_inc_return(&devfreq_no)); - err = device_register(&devfreq->dev); - if (err) { - mutex_unlock(&devfreq->lock); - put_device(&devfreq->dev); - goto err_out; - } - - devfreq->trans_table = devm_kzalloc(&devfreq->dev, + devfreq->trans_table = kzalloc( array3_size(sizeof(unsigned int), devfreq->profile->max_state, devfreq->profile->max_state), @@ -690,23 +683,31 @@ struct devfreq *devfreq_add_device(struct device *dev, if (!devfreq->trans_table) { mutex_unlock(&devfreq->lock); err = -ENOMEM; - goto err_devfreq; + goto err_dev; } - devfreq->time_in_state = devm_kcalloc(&devfreq->dev, - devfreq->profile->max_state, - sizeof(unsigned long), - GFP_KERNEL); + devfreq->time_in_state = kcalloc(devfreq->profile->max_state, + sizeof(unsigned long), + GFP_KERNEL); if (!devfreq->time_in_state) { mutex_unlock(&devfreq->lock); err = -ENOMEM; - goto err_devfreq; + goto err_dev; } devfreq->last_stat_updated = jiffies; srcu_init_notifier_head(&devfreq->transition_notifier_list); + dev_set_name(&devfreq->dev, "devfreq%d", + atomic_inc_return(&devfreq_no)); + err = device_register(&devfreq->dev); + if (err) { + mutex_unlock(&devfreq->lock); + put_device(&devfreq->dev); + goto err_out; + } + mutex_unlock(&devfreq->lock); mutex_lock(&devfreq_list_lock); @@ -736,10 +737,16 @@ struct devfreq *devfreq_add_device(struct device *dev, err_init: mutex_unlock(&devfreq_list_lock); -err_devfreq: devfreq_remove_device(devfreq); - devfreq = NULL; + return ERR_PTR(err); + err_dev: + /* + * Cleanup path for errors that happen before registration. + * Otherwise we rely on devfreq_dev_release. + */ + kfree(devfreq->time_in_state); + kfree(devfreq->trans_table); kfree(devfreq); err_out: return ERR_PTR(err); -- GitLab From b370d0e2c482d8655dd1f53a6811700c62563717 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 26 Aug 2019 15:18:01 +0300 Subject: [PATCH 11/71] PM / devfreq: Don't take lock in devfreq_add_device A device usually doesn't need to lock itself during initialization because it is not yet reachable from other threads. This simplifies the code and helps avoid recursive lock warnings. Signed-off-by: Leonard Crestez Reviewed-by: Matthias Kaehlcke Reviewed-by: Chanwoo Choi --- drivers/devfreq/devfreq.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 0f0c3abf8ebd..17106f4c782d 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -636,7 +636,6 @@ struct devfreq *devfreq_add_device(struct device *dev, } mutex_init(&devfreq->lock); - mutex_lock(&devfreq->lock); devfreq->dev.parent = dev; devfreq->dev.class = devfreq_class; devfreq->dev.release = devfreq_dev_release; @@ -649,16 +648,13 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->nb.notifier_call = devfreq_notifier_call; if (!devfreq->profile->max_state && !devfreq->profile->freq_table) { - mutex_unlock(&devfreq->lock); err = set_freq_table(devfreq); if (err < 0) goto err_dev; - mutex_lock(&devfreq->lock); } devfreq->scaling_min_freq = find_available_min_freq(devfreq); if (!devfreq->scaling_min_freq) { - mutex_unlock(&devfreq->lock); err = -EINVAL; goto err_dev; } @@ -666,7 +662,6 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->scaling_max_freq = find_available_max_freq(devfreq); if (!devfreq->scaling_max_freq) { - mutex_unlock(&devfreq->lock); err = -EINVAL; goto err_dev; } @@ -681,7 +676,6 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->profile->max_state), GFP_KERNEL); if (!devfreq->trans_table) { - mutex_unlock(&devfreq->lock); err = -ENOMEM; goto err_dev; } @@ -690,7 +684,6 @@ struct devfreq *devfreq_add_device(struct device *dev, sizeof(unsigned long), GFP_KERNEL); if (!devfreq->time_in_state) { - mutex_unlock(&devfreq->lock); err = -ENOMEM; goto err_dev; } @@ -703,13 +696,10 @@ struct devfreq *devfreq_add_device(struct device *dev, atomic_inc_return(&devfreq_no)); err = device_register(&devfreq->dev); if (err) { - mutex_unlock(&devfreq->lock); put_device(&devfreq->dev); goto err_out; } - mutex_unlock(&devfreq->lock); - mutex_lock(&devfreq_list_lock); governor = try_then_request_governor(devfreq->governor_name); -- GitLab From 01a79d4e19dc0c08e2a3834df932058372b38ae0 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Fri, 23 Aug 2019 19:57:18 +0300 Subject: [PATCH 12/71] PM / devfreq: Introduce get_freq_range helper Moving handling of min/max freq to a single function and call it from update_devfreq and for printing min/max freq values in sysfs. This changes the behavior of out-of-range min_freq/max_freq: clamping is now done at evaluation time. This means that if an out-of-range constraint is imposed by sysfs and it later becomes valid then it will be enforced. Signed-off-by: Leonard Crestez Reviewed-by: Matthias Kaehlcke --- drivers/devfreq/devfreq.c | 112 ++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 48 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 17106f4c782d..2872d1f4fa5a 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -98,6 +98,50 @@ static unsigned long find_available_max_freq(struct devfreq *devfreq) return max_freq; } +/** + * get_freq_range() - Get the current freq range + * @devfreq: the devfreq instance + * @min_freq: the min frequency + * @max_freq: the max frequency + * + * This takes into consideration all constraints. + */ +static void get_freq_range(struct devfreq *devfreq, + unsigned long *min_freq, + unsigned long *max_freq) +{ + unsigned long *freq_table = devfreq->profile->freq_table; + + lockdep_assert_held(&devfreq->lock); + + /* + * Init min/max frequency from freq table. + * Drivers can initialize this in either ascending or descending order + * and devfreq core supports both. + */ + if (freq_table[0] < freq_table[devfreq->profile->max_state - 1]) { + *min_freq = freq_table[0]; + *max_freq = freq_table[devfreq->profile->max_state - 1]; + } else { + *min_freq = freq_table[devfreq->profile->max_state - 1]; + *max_freq = freq_table[0]; + } + + /* constraints from sysfs */ + *min_freq = max(*min_freq, devfreq->min_freq); + *max_freq = min(*max_freq, devfreq->max_freq); + + /* constraints from OPP interface */ + *min_freq = max(*min_freq, devfreq->scaling_min_freq); + /* scaling_max_freq can be zero on error */ + if (devfreq->scaling_max_freq) + *max_freq = min(*max_freq, devfreq->scaling_max_freq); + + /* max_freq takes precedence over min_freq */ + if (*min_freq > *max_freq) + *min_freq = *max_freq; +} + /** * devfreq_get_freq_level() - Lookup freq_table for the frequency * @devfreq: the devfreq instance @@ -351,16 +395,7 @@ int update_devfreq(struct devfreq *devfreq) err = devfreq->governor->get_target_freq(devfreq, &freq); if (err) return err; - - /* - * Adjust the frequency with user freq, QoS and available freq. - * - * List from the highest priority - * max_freq - * min_freq - */ - max_freq = min(devfreq->scaling_max_freq, devfreq->max_freq); - min_freq = max(devfreq->scaling_min_freq, devfreq->min_freq); + get_freq_range(devfreq, &min_freq, &max_freq); if (freq < min_freq) { freq = min_freq; @@ -1280,36 +1315,24 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&df->lock); - - if (value) { - if (value > df->max_freq) { - ret = -EINVAL; - goto unlock; - } - } else { - unsigned long *freq_table = df->profile->freq_table; - - /* Get minimum frequency according to sorting order */ - if (freq_table[0] < freq_table[df->profile->max_state - 1]) - value = freq_table[0]; - else - value = freq_table[df->profile->max_state - 1]; - } - df->min_freq = value; update_devfreq(df); - ret = count; -unlock: mutex_unlock(&df->lock); - return ret; + + return count; } static ssize_t min_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { struct devfreq *df = to_devfreq(dev); + unsigned long min_freq, max_freq; + + mutex_lock(&df->lock); + get_freq_range(df, &min_freq, &max_freq); + mutex_unlock(&df->lock); - return sprintf(buf, "%lu\n", max(df->scaling_min_freq, df->min_freq)); + return sprintf(buf, "%lu\n", min_freq); } static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, @@ -1325,27 +1348,15 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, mutex_lock(&df->lock); - if (value) { - if (value < df->min_freq) { - ret = -EINVAL; - goto unlock; - } - } else { - unsigned long *freq_table = df->profile->freq_table; - - /* Get maximum frequency according to sorting order */ - if (freq_table[0] < freq_table[df->profile->max_state - 1]) - value = freq_table[df->profile->max_state - 1]; - else - value = freq_table[0]; - } + /* Interpret zero as "don't care" */ + if (!value) + value = ULONG_MAX; df->max_freq = value; update_devfreq(df); - ret = count; -unlock: mutex_unlock(&df->lock); - return ret; + + return count; } static DEVICE_ATTR_RW(min_freq); @@ -1353,8 +1364,13 @@ static ssize_t max_freq_show(struct device *dev, struct device_attribute *attr, char *buf) { struct devfreq *df = to_devfreq(dev); + unsigned long min_freq, max_freq; + + mutex_lock(&df->lock); + get_freq_range(df, &min_freq, &max_freq); + mutex_unlock(&df->lock); - return sprintf(buf, "%lu\n", min(df->scaling_max_freq, df->max_freq)); + return sprintf(buf, "%lu\n", max_freq); } static DEVICE_ATTR_RW(max_freq); -- GitLab From 6d69fbaa57e2d8f68295d71d6db8570558d2b711 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Fri, 23 Aug 2019 19:58:46 +0300 Subject: [PATCH 13/71] PM / devfreq: Add PM QoS support Register notifiers with the PM QoS framework in order to respond to requests for DEV_PM_QOS_MIN_FREQUENCY and DEV_PM_QOS_MAX_FREQUENCY. No notifiers are added by this patch but PM QoS constraints can be imposed externally (for example from other devices). Signed-off-by: Leonard Crestez Reviewed-by: Matthias Kaehlcke --- drivers/devfreq/devfreq.c | 75 +++++++++++++++++++++++++++++++++++++++ include/linux/devfreq.h | 5 +++ 2 files changed, 80 insertions(+) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 2872d1f4fa5a..85f5a13278d4 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -24,11 +24,14 @@ #include #include #include +#include #include "governor.h" #define CREATE_TRACE_POINTS #include +#define HZ_PER_KHZ 1000 + static struct class *devfreq_class; /* @@ -111,6 +114,7 @@ static void get_freq_range(struct devfreq *devfreq, unsigned long *max_freq) { unsigned long *freq_table = devfreq->profile->freq_table; + unsigned long qos_min_freq, qos_max_freq; lockdep_assert_held(&devfreq->lock); @@ -127,6 +131,14 @@ static void get_freq_range(struct devfreq *devfreq, *max_freq = freq_table[0]; } + /* constraints from PM QoS */ + qos_min_freq = dev_pm_qos_read_value(devfreq->dev.parent, + DEV_PM_QOS_MIN_FREQUENCY); + qos_max_freq = dev_pm_qos_read_value(devfreq->dev.parent, + DEV_PM_QOS_MIN_FREQUENCY); + *min_freq = max(*min_freq, HZ_PER_KHZ * qos_min_freq); + *max_freq = min(*max_freq, HZ_PER_KHZ * qos_max_freq); + /* constraints from sysfs */ *min_freq = max(*min_freq, devfreq->min_freq); *max_freq = min(*max_freq, devfreq->max_freq); @@ -608,6 +620,45 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, return ret; } +/** + * qos_notifier_call() - Common handler for QoS constraints. + * @devfreq: the devfreq instance. + */ +static int qos_notifier_call(struct devfreq *devfreq) +{ + int err; + + mutex_lock(&devfreq->lock); + err = update_devfreq(devfreq); + mutex_unlock(&devfreq->lock); + if (err) + dev_err(devfreq->dev.parent, + "failed to update frequency for PM QoS constraints (%d)\n", + err); + + return NOTIFY_OK; +} + +/** + * qos_min_notifier_call() - Callback for QoS min_freq changes. + * @nb: Should be devfreq->nb_min + */ +static int qos_min_notifier_call(struct notifier_block *nb, + unsigned long val, void *ptr) +{ + return qos_notifier_call(container_of(nb, struct devfreq, nb_min)); +} + +/** + * qos_max_notifier_call() - Callback for QoS max_freq changes. + * @nb: Should be devfreq->nb_max + */ +static int qos_max_notifier_call(struct notifier_block *nb, + unsigned long val, void *ptr) +{ + return qos_notifier_call(container_of(nb, struct devfreq, nb_max)); +} + /** * devfreq_dev_release() - Callback for struct device to release the device. * @dev: the devfreq device @@ -622,6 +673,11 @@ static void devfreq_dev_release(struct device *dev) list_del(&devfreq->node); mutex_unlock(&devfreq_list_lock); + dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_max, + DEV_PM_QOS_MAX_FREQUENCY); + dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_min, + DEV_PM_QOS_MIN_FREQUENCY); + if (devfreq->profile->exit) devfreq->profile->exit(devfreq->dev.parent); @@ -735,6 +791,24 @@ struct devfreq *devfreq_add_device(struct device *dev, goto err_out; } + /* + * Register notifiers for updates to min/max_freq after device is + * initialized (and we can handle notifications) but before the + * governor is started (which should do an initial enforcement of + * constraints). + */ + devfreq->nb_min.notifier_call = qos_min_notifier_call; + err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_min, + DEV_PM_QOS_MIN_FREQUENCY); + if (err) + goto err_devfreq; + + devfreq->nb_max.notifier_call = qos_max_notifier_call; + err = dev_pm_qos_add_notifier(devfreq->dev.parent, &devfreq->nb_max, + DEV_PM_QOS_MAX_FREQUENCY); + if (err) + goto err_devfreq; + mutex_lock(&devfreq_list_lock); governor = try_then_request_governor(devfreq->governor_name); @@ -762,6 +836,7 @@ struct devfreq *devfreq_add_device(struct device *dev, err_init: mutex_unlock(&devfreq_list_lock); +err_devfreq: devfreq_remove_device(devfreq); return ERR_PTR(err); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 2bae9ed3c783..8b92ccbd1962 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -136,6 +136,8 @@ struct devfreq_dev_profile { * @time_in_state: Statistics of devfreq states * @last_stat_updated: The last time stat updated * @transition_notifier_list: list head of DEVFREQ_TRANSITION_NOTIFIER notifier + * @nb_min: Notifier block for DEV_PM_QOS_MIN_FREQUENCY + * @nb_max: Notifier block for DEV_PM_QOS_MAX_FREQUENCY * * This structure stores the devfreq information for a give device. * @@ -178,6 +180,9 @@ struct devfreq { unsigned long last_stat_updated; struct srcu_notifier_head transition_notifier_list; + + struct notifier_block nb_min; + struct notifier_block nb_max; }; struct devfreq_freqs { -- GitLab From 5a179bf90d296f54d65bb1208f0c87c028facbe2 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Fri, 23 Aug 2019 20:00:23 +0300 Subject: [PATCH 14/71] PM / devfreq: Use PM QoS for sysfs min/max_freq Switch the handling of min_freq and max_freq from sysfs to use the dev_pm_qos_request interface. Since PM QoS handles frequencies as kHz this change reduces the precision of min_freq and max_freq. This shouldn't introduce problems because frequencies which are not an integer number of kHz are likely not an integer number of Hz either. Try to ensure compatibility by rounding min values down and rounding max values up. Signed-off-by: Leonard Crestez Reviewed-by: Matthias Kaehlcke --- drivers/devfreq/devfreq.c | 46 ++++++++++++++++++++++++--------------- include/linux/devfreq.h | 9 ++++---- 2 files changed, 33 insertions(+), 22 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 85f5a13278d4..dcd887e039f5 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -139,10 +139,6 @@ static void get_freq_range(struct devfreq *devfreq, *min_freq = max(*min_freq, HZ_PER_KHZ * qos_min_freq); *max_freq = min(*max_freq, HZ_PER_KHZ * qos_max_freq); - /* constraints from sysfs */ - *min_freq = max(*min_freq, devfreq->min_freq); - *max_freq = min(*max_freq, devfreq->max_freq); - /* constraints from OPP interface */ *min_freq = max(*min_freq, devfreq->scaling_min_freq); /* scaling_max_freq can be zero on error */ @@ -681,6 +677,8 @@ static void devfreq_dev_release(struct device *dev) if (devfreq->profile->exit) devfreq->profile->exit(devfreq->dev.parent); + dev_pm_qos_remove_request(&devfreq->user_max_freq_req); + dev_pm_qos_remove_request(&devfreq->user_min_freq_req); kfree(devfreq->time_in_state); kfree(devfreq->trans_table); mutex_destroy(&devfreq->lock); @@ -749,14 +747,21 @@ struct devfreq *devfreq_add_device(struct device *dev, err = -EINVAL; goto err_dev; } - devfreq->min_freq = devfreq->scaling_min_freq; devfreq->scaling_max_freq = find_available_max_freq(devfreq); if (!devfreq->scaling_max_freq) { err = -EINVAL; goto err_dev; } - devfreq->max_freq = devfreq->scaling_max_freq; + + err = dev_pm_qos_add_request(dev, &devfreq->user_min_freq_req, + DEV_PM_QOS_MIN_FREQUENCY, 0); + if (err < 0) + goto err_dev; + err = dev_pm_qos_add_request(dev, &devfreq->user_max_freq_req, + DEV_PM_QOS_MAX_FREQUENCY, S32_MAX); + if (err < 0) + goto err_dev; devfreq->suspend_freq = dev_pm_opp_get_suspend_opp_freq(dev); atomic_set(&devfreq->suspend_count, 0); @@ -845,6 +850,10 @@ struct devfreq *devfreq_add_device(struct device *dev, * Cleanup path for errors that happen before registration. * Otherwise we rely on devfreq_dev_release. */ + if (dev_pm_qos_request_active(&devfreq->user_max_freq_req)) + dev_pm_qos_remove_request(&devfreq->user_max_freq_req); + if (dev_pm_qos_request_active(&devfreq->user_min_freq_req)) + dev_pm_qos_remove_request(&devfreq->user_min_freq_req); kfree(devfreq->time_in_state); kfree(devfreq->trans_table); kfree(devfreq); @@ -1389,10 +1398,11 @@ static ssize_t min_freq_store(struct device *dev, struct device_attribute *attr, if (ret != 1) return -EINVAL; - mutex_lock(&df->lock); - df->min_freq = value; - update_devfreq(df); - mutex_unlock(&df->lock); + /* round down to kHz for PM QoS */ + ret = dev_pm_qos_update_request(&df->user_min_freq_req, + value / HZ_PER_KHZ); + if (ret < 0) + return ret; return count; } @@ -1421,15 +1431,15 @@ static ssize_t max_freq_store(struct device *dev, struct device_attribute *attr, if (ret != 1) return -EINVAL; - mutex_lock(&df->lock); - - /* Interpret zero as "don't care" */ - if (!value) - value = ULONG_MAX; + /* round up to kHz for PM QoS and interpret zero as "don't care" */ + if (value) + value = DIV_ROUND_UP(value, HZ_PER_KHZ); + else + value = S32_MAX; - df->max_freq = value; - update_devfreq(df); - mutex_unlock(&df->lock); + ret = dev_pm_qos_update_request(&df->user_max_freq_req, value); + if (ret < 0) + return ret; return count; } diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 8b92ccbd1962..3162eb9b0954 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -13,6 +13,7 @@ #include #include #include +#include #define DEVFREQ_NAME_LEN 16 @@ -123,8 +124,8 @@ struct devfreq_dev_profile { * @previous_freq: previously configured frequency value. * @data: Private data of the governor. The devfreq framework does not * touch this. - * @min_freq: Limit minimum frequency requested by user (0: none) - * @max_freq: Limit maximum frequency requested by user (0: none) + * @user_min_freq_req: PM QoS min frequency request from user (via sysfs) + * @user_max_freq_req: PM QoS max frequency request from user (via sysfs) * @scaling_min_freq: Limit minimum frequency requested by OPP interface * @scaling_max_freq: Limit maximum frequency requested by OPP interface * @stop_polling: devfreq polling status of a device. @@ -163,8 +164,8 @@ struct devfreq { void *data; /* private data for governors */ - unsigned long min_freq; - unsigned long max_freq; + struct dev_pm_qos_request user_min_freq_req; + struct dev_pm_qos_request user_max_freq_req; unsigned long scaling_min_freq; unsigned long scaling_max_freq; bool stop_polling; -- GitLab From 49dfac4f7dea8f12e7279fd56a1c7a1b702d811e Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Wed, 5 Jun 2019 14:42:17 +0300 Subject: [PATCH 15/71] arm64: defconfig: Enable imx devfreq module Signed-off-by: Leonard Crestez --- arch/arm64/configs/defconfig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 0e58ef02880c..6bfa6727c3f6 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -738,7 +738,11 @@ CONFIG_ARCH_K3_AM6_SOC=y CONFIG_ARCH_K3_J721E_SOC=y CONFIG_SOC_TI=y CONFIG_TI_SCI_PM_DOMAINS=y -CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y +CONFIG_DEVFREQ_GOV_PERFORMANCE=y +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_DEVFREQ_GOV_PASSIVE=y +CONFIG_ARM_IMX_DEVFREQ=m +CONFIG_PM_DEVFREQ_EVENT=y CONFIG_EXTCON_USB_GPIO=y CONFIG_EXTCON_USBC_CROS_EC=y CONFIG_MEMORY=y -- GitLab From 048df7859e201285e685091f9b6e4bb89011dc82 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 12 Aug 2019 16:12:33 +0300 Subject: [PATCH 16/71] clk: imx8m: Set CLK_GET_RATE_NOCACHE on dram_alt/apb 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 are and 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 | 12 +++++++++--- 3 files changed, 28 insertions(+), 7 deletions(-) diff --git a/drivers/clk/imx/clk-imx8mm.c b/drivers/clk/imx/clk-imx8mm.c index 41c343048818..b32417111af9 100644 --- a/drivers/clk/imx/clk-imx8mm.c +++ b/drivers/clk/imx/clk-imx8mm.c @@ -543,9 +543,16 @@ static int __init imx8mm_clocks_init(struct device_node *ccm_node) 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); diff --git a/drivers/clk/imx/clk-imx8mn.c b/drivers/clk/imx/clk-imx8mn.c index ccd074dfdc20..4f70520ed236 100644 --- a/drivers/clk/imx/clk-imx8mn.c +++ b/drivers/clk/imx/clk-imx8mn.c @@ -519,8 +519,16 @@ static int imx8mn_clocks_probe(struct platform_device *pdev) 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); diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c index 371193116c0f..2813884f69c1 100644 --- a/drivers/clk/imx/clk-imx8mq.c +++ b/drivers/clk/imx/clk-imx8mq.c @@ -435,11 +435,17 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) 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); -- GitLab From 2d861eea4c0906e93561f4c0bb09222f60f51f1a Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Wed, 3 Jul 2019 19:46:25 +0300 Subject: [PATCH 17/71] dt-bindings: devfreq: Add bindings for generic imx buses Add initial dt bindings for the interconnects inside i.MX chips. Multiple external IPs are involved but SOC integration means the software controllable interfaces are very similar. This is initially only for imx8mm but add an "fsl,imx8m-nic" fallback similar to exynos-bus. Signed-off-by: Leonard Crestez Acked-by: MyungJoo Ham --- .../devicetree/bindings/devfreq/imx.yaml | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 Documentation/devicetree/bindings/devfreq/imx.yaml diff --git a/Documentation/devicetree/bindings/devfreq/imx.yaml b/Documentation/devicetree/bindings/devfreq/imx.yaml new file mode 100644 index 000000000000..634870496d5e --- /dev/null +++ b/Documentation/devicetree/bindings/devfreq/imx.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/devfreq/imx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Generic i.MX bus frequency device + +maintainers: + - Leonard Crestez + +description: | + The i.MX SoC family has multiple buses for which clock frequency (and sometimes + voltage) can be adjusted. + + Some of those buses expose register areas mentioned in the memory maps as GPV + ("Global Programmers View") but not all. Access to this area might be denied for + normal world. + + The buses are based on externally licensed IPs such as ARM NIC-301 and Arteris + FlexNOC but DT bindings are specific to the integration of these bus + interconnect IPs into imx SOCs. + +properties: + compatible: + oneOf: + - items: + - enum: + - fsl,imx8mn-nic + - fsl,imx8mm-nic + - fsl,imx8mq-nic + - const: fsl,imx8m-nic + - items: + - enum: + - fsl,imx8mn-noc + - fsl,imx8mm-noc + - fsl,imx8mq-noc + - const: fsl,imx8m-noc + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + operating-points-v2: true + + devfreq: + description: | + Phandle to another devfreq device to match OPPs with by using the + passive governor. + $ref: "/schemas/types.yaml#/definitions/phandle" + +required: + - compatible + - clocks + +additionalProperties: false + +examples: + - | + #include + noc: noc@32700000 { + compatible = "fsl,imx8mm-noc", "fsl,imx8m-noc"; + reg = <0x32700000 0x100000>; + clocks = <&clk IMX8MM_CLK_NOC>; + operating-points-v2 = <&noc_opp_table>; + }; -- GitLab From 5c91adfdffd8e06c4563ebfb9a67f888191886b9 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 14 May 2019 23:28:07 +0300 Subject: [PATCH 18/71] PM / devfreq: Add generic imx bus driver Add initial support for dynamic frequency switching on pieces of the imx interconnect fabric. All this driver actually does is set a clk rate based on an opp table. No attempt is made to map registers or anything clever. Signed-off-by: Leonard Crestez --- drivers/devfreq/Kconfig | 12 +++ drivers/devfreq/Makefile | 1 + drivers/devfreq/imx-devfreq.c | 148 ++++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+) create mode 100644 drivers/devfreq/imx-devfreq.c diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig index ba98a4e3ad33..dda8a3b41bf3 100644 --- a/drivers/devfreq/Kconfig +++ b/drivers/devfreq/Kconfig @@ -92,6 +92,18 @@ config ARM_EXYNOS_BUS_DEVFREQ and adjusts the operating frequencies and voltages with OPP support. This does not yet operate with optimal voltages. +config ARM_IMX_DEVFREQ + tristate "i.MX DEVFREQ Driver" + depends on ARCH_MXC || COMPILE_TEST + select DEVFREQ_GOV_PASSIVE + select DEVFREQ_GOV_SIMPLE_ONDEMAND + select DEVFREQ_GOV_USERSPACE + select PM_OPP + help + This adds the DEVFREQ driver for the i.MX family of SoCs. + It allows adjusting frequencies for DDRC (DDR Controller) and various + NICs and NOCs which form the SOC interconnect fabric + config ARM_TEGRA_DEVFREQ tristate "Tegra DEVFREQ Driver" depends on ARCH_TEGRA_124_SOC diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index 32b8d4d3f12c..fb25bc1768e1 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -9,6 +9,7 @@ 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-devfreq.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o diff --git a/drivers/devfreq/imx-devfreq.c b/drivers/devfreq/imx-devfreq.c new file mode 100644 index 000000000000..5c907b3ab9c0 --- /dev/null +++ b/drivers/devfreq/imx-devfreq.c @@ -0,0 +1,148 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +struct imx_devfreq { + struct devfreq_dev_profile profile; + struct devfreq *devfreq; + struct clk *clk; + struct devfreq_passive_data passive_data; +}; + +static int imx_devfreq_target(struct device *dev, unsigned long *freq, u32 flags) +{ + struct imx_devfreq *priv = dev_get_drvdata(dev); + struct dev_pm_opp *new_opp; + unsigned long 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; + } + new_freq = dev_pm_opp_get_freq(new_opp); + dev_pm_opp_put(new_opp); + + return clk_set_rate(priv->clk, new_freq); +} + +static int imx_devfreq_get_cur_freq(struct device *dev, unsigned long *freq) +{ + struct imx_devfreq *priv = dev_get_drvdata(dev); + + *freq = clk_get_rate(priv->clk); + + return 0; +} + +static int imx_devfreq_get_dev_status(struct device *dev, + struct devfreq_dev_status *stat) +{ + struct imx_devfreq *priv = dev_get_drvdata(dev); + + stat->busy_time = 0; + stat->total_time = 0; + stat->current_frequency = clk_get_rate(priv->clk); + + return 0; +} + +static void imx_devfreq_exit(struct device *dev) +{ + dev_pm_opp_of_remove_table(dev); +} + +static int imx_devfreq_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct imx_devfreq *priv; + const char *gov = DEVFREQ_GOV_USERSPACE; + void *govdata = NULL; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->clk = devm_clk_get(dev, NULL); + if (IS_ERR(priv->clk)) { + ret = PTR_ERR(priv->clk); + dev_err(dev, "failed to fetch clk: %d\n", ret); + return ret; + } + platform_set_drvdata(pdev, priv); + + ret = dev_pm_opp_of_add_table(dev); + if (ret < 0) { + dev_err(dev, "failed to get OPP table\n"); + return ret; + } + + priv->profile.polling_ms = 1000; + priv->profile.target = imx_devfreq_target; + priv->profile.get_dev_status = imx_devfreq_get_dev_status; + priv->profile.exit = imx_devfreq_exit; + priv->profile.get_cur_freq = imx_devfreq_get_cur_freq; + priv->profile.initial_freq = clk_get_rate(priv->clk); + + /* Handle passive devfreq parent link */ + priv->passive_data.parent = devfreq_get_devfreq_by_phandle(dev, 0); + if (!IS_ERR(priv->passive_data.parent)) { + dev_info(dev, "passive link to %s\n", + dev_name(priv->passive_data.parent->dev.parent)); + gov = DEVFREQ_GOV_PASSIVE; + govdata = &priv->passive_data; + } else if (priv->passive_data.parent != ERR_PTR(-ENODEV)) { + // -ENODEV means no parent: not an error. + ret = PTR_ERR(priv->passive_data.parent); + if (ret != -EPROBE_DEFER) + dev_warn(dev, "failed to get passive parent: %d\n", ret); + goto err; + } + + priv->devfreq = devm_devfreq_add_device(dev, &priv->profile, + gov, govdata); + 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_devfreq_of_match[] = { + { .compatible = "fsl,imx8m-noc", }, + { .compatible = "fsl,imx8m-nic", }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, imx_devfreq_of_match); + +static struct platform_driver imx_devfreq_platdrv = { + .probe = imx_devfreq_probe, + .driver = { + .name = "imx-devfreq", + .of_match_table = of_match_ptr(imx_devfreq_of_match), + }, +}; +module_platform_driver(imx_devfreq_platdrv); + +MODULE_DESCRIPTION("Generic i.MX bus frequency driver"); +MODULE_AUTHOR("Leonard Crestez "); +MODULE_LICENSE("GPL v2"); -- GitLab From 68d8939a158722765f57ef28e0bbd37ad51d4394 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Fri, 9 Aug 2019 18:25:27 +0300 Subject: [PATCH 19/71] dt-bindings: devfreq: Add bindings for imx ddr controller 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>; + }; -- GitLab From d39623cf8b2db129af3ba79d02aa431eb58bbcec Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Fri, 9 Aug 2019 18:19:13 +0300 Subject: [PATCH 20/71] PM / devfreq: Add dynamic scaling for imx ddr controller Add driver for dynamic scaling the DDR Controller on imx8m chips. Actual frequency switching is implemented in TF-A, this driver just wraps the SMC calls and updates the clk tree. Signed-off-by: Leonard Crestez --- drivers/devfreq/Makefile | 2 +- drivers/devfreq/imx-ddrc.c | 374 +++++++++++++++++++++++++++++++++++++ 2 files changed, 375 insertions(+), 1 deletion(-) create mode 100644 drivers/devfreq/imx-ddrc.c diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile index fb25bc1768e1..12571bd95dda 100644 --- a/drivers/devfreq/Makefile +++ b/drivers/devfreq/Makefile @@ -9,7 +9,7 @@ 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-devfreq.o +obj-$(CONFIG_ARM_IMX_DEVFREQ) += imx-devfreq.o imx-ddrc.o obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra-devfreq.o diff --git a/drivers/devfreq/imx-ddrc.c b/drivers/devfreq/imx-ddrc.c new file mode 100644 index 000000000000..253138f608e7 --- /dev/null +++ b/drivers/devfreq/imx-ddrc.c @@ -0,0 +1,374 @@ +// 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; + + for (i = 0; i < priv->freq_count; ++i) + if (priv->freq_table[i].rate == rate) + return &priv->freq_table[i]; + + 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); + } + + /* + * 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 new_freq, cur_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; + } + new_freq = dev_pm_opp_get_freq(new_opp); + dev_pm_opp_put(new_opp); + cur_freq = clk_get_rate(priv->dram_core); + + if (new_freq == cur_freq) + return 0; + + freq_info = imx_ddrc_find_freq(priv, new_freq); + if (!freq_info) + return -EINVAL; + ret = imx_ddrc_set_freq(dev, freq_info); + if (ret) + dev_err(dev, "ddrc failed to change freq %lu to %lu\n", + cur_freq, new_freq); + else + dev_dbg(dev, "ddrc changed freq %lu to %lu\n", + cur_freq, new_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 * 250000; + 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; +} + +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; + } + + 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"); -- GitLab From f7cc82281ede0036846727809d6412123154fbc5 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Fri, 9 Aug 2019 19:15:10 +0300 Subject: [PATCH 21/71] PM / devfreq: imx-ddrc: Measure bandwidth with perf 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 253138f608e7..f9a11a1825ea 100644 --- a/drivers/devfreq/imx-ddrc.c +++ b/drivers/devfreq/imx-ddrc.c @@ -13,6 +13,9 @@ #include #include +#include +#include + #define IMX_SIP_DDR_DVFS 0xc2000004 /* Values starting from 0 switch to specific frequency */ @@ -82,6 +85,18 @@ struct imx_ddrc { 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, @@ -230,15 +245,123 @@ static int imx_ddrc_get_cur_freq(struct device *dev, unsigned long *freq) } 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) / + (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) / + (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) @@ -290,6 +413,11 @@ static int imx_ddrc_init_freq_info(struct device *dev) 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); } @@ -339,6 +467,16 @@ static int imx_ddrc_probe(struct platform_device *pdev) priv->profile.get_cur_freq = imx_ddrc_get_cur_freq; priv->profile.initial_freq = clk_get_rate(priv->dram_core); + /* Handle devfreq-events */ + 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->devfreq = devm_devfreq_add_device(dev, &priv->profile, gov, NULL); if (IS_ERR(priv->devfreq)) { @@ -350,6 +488,8 @@ 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; } -- GitLab From 99a641c55e58b60554afa1df006a14174b647678 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 22 Aug 2019 20:49:19 +0300 Subject: [PATCH 22/71] PM / devfreq: imx-ddrc: Use do_div Use do_div to make it compile on 32-bit arm. Signed-off-by: Leonard Crestez --- drivers/devfreq/imx-ddrc.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/devfreq/imx-ddrc.c b/drivers/devfreq/imx-ddrc.c index f9a11a1825ea..aa414bb980f3 100644 --- a/drivers/devfreq/imx-ddrc.c +++ b/drivers/devfreq/imx-ddrc.c @@ -261,14 +261,15 @@ static int imx_ddrc_get_dev_status(struct device *dev, &wr_ena, &wr_run); rd_delta = (rd_val - priv->last_rd_val) * - (rd_ena - priv->last_rd_ena) / - (rd_run - priv->last_rd_run); + (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) / - (wr_run - priv->last_wr_run); + (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; -- GitLab From afca4f07857a998fd976b5ee5c350a2a5847240d Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Wed, 29 May 2019 18:31:07 +0300 Subject: [PATCH 23/71] arm64: dts: imx8mm: Add devfreq nodes Add initial support for bus scaling on imx8m, starting with noc and ddrc because they're the biggest power hogs. Add a devfreq-event link to the PMU in order to support on-demand scaling of ddrc based on measured dram bandwith usage. Make ddrc a parent of the NOC because all traffic to ddrc goes through Support for proactive scaling via interconnect and support for scaling additional NICs 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.dtsi | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi index 232a7412755a..63c629bdf42d 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi @@ -122,6 +122,34 @@ opp-1800000000 { }; }; + 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>; + }; + }; + + noc_opp_table: noc-opp-table { + compatible = "operating-points-v2"; + + opp-150M { + opp-hz = /bits/ 64 <150000000>; + }; + opp-375M { + opp-hz = /bits/ 64 <375000000>; + }; + opp-750M { + opp-hz = /bits/ 64 <750000000>; + }; + }; + memory@40000000 { device_type = "memory"; reg = <0x0 0x40000000 0 0x80000000>; @@ -731,6 +759,14 @@ fec1: ethernet@30be0000 { }; + noc: noc@32700000 { + compatible = "fsl,imx8mm-noc", "fsl,imx8m-noc"; + reg = <0x32700000 0x100000>; + clocks = <&clk IMX8MM_CLK_NOC>; + devfreq = <&ddrc>; + operating-points-v2 = <&noc_opp_table>; + }; + aips4: bus@32c00000 { compatible = "fsl,aips-bus", "simple-bus"; #address-cells = <1>; @@ -818,5 +854,27 @@ gic: interrupt-controller@38800000 { interrupt-controller; interrupts = ; }; + + 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 = ; + }; }; }; -- GitLab From 9ec1cafce0f1104b7b1c7f6947e07191d72cdaff Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 12 Aug 2019 17:48:39 +0300 Subject: [PATCH 24/71] arm64: dts: imx8mq: Add devfreq nodes Signed-off-by: Leonard Crestez --- arch/arm64/boot/dts/freescale/imx8mq.dtsi | 58 +++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi b/arch/arm64/boot/dts/freescale/imx8mq.dtsi index 5bae6bbc9946..0281b7b7e3dd 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi @@ -201,6 +201,34 @@ opp-1500000000 { }; }; + 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-800M { + opp-hz = /bits/ 64 <800000000>; + }; + }; + + noc_opp_table: noc-opp-table { + compatible = "operating-points-v2"; + + opp-133M { + opp-hz = /bits/ 64 <133333333>; + }; + opp-400M { + opp-hz = /bits/ 64 <400000000>; + }; + opp-800M { + opp-hz = /bits/ 64 <800000000>; + }; + }; + pmu { compatible = "arm,cortex-a53-pmu"; interrupts = ; @@ -959,6 +987,14 @@ fec1: ethernet@30be0000 { }; }; + noc: noc@32700000 { + compatible = "fsl,imx8mq-noc", "fsl,imx8m-noc"; + reg = <0x32700000 0x100000>; + clocks = <&clk IMX8MQ_CLK_NOC>; + devfreq = <&ddrc>; + operating-points-v2 = <&noc_opp_table>; + }; + bus@32c00000 { /* AIPS4 */ compatible = "fsl,imx8mq-aips-bus", "simple-bus"; #address-cells = <1>; @@ -1162,5 +1198,27 @@ gic: interrupt-controller@38800000 { interrupts = ; interrupt-parent = <&gic>; }; + + 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_PLL1_OUT>, + <&clk IMX8MQ_CLK_DRAM_ALT>, + <&clk IMX8MQ_CLK_DRAM_APB>; + devfreq-events = <&ddr_pmu>; + operating-points-v2 = <&ddrc_opp_table>; + }; + + ddr_pmu: ddr-pmu@3d800000 { + compatible = "fsl,imx8mq-ddr-pmu", "fsl,imx8m-ddr-pmu"; + reg = <0x3d800000 0x400000>; + interrupt-parent = <&gic>; + interrupts = ; + }; }; }; -- GitLab From 92b5102e247527b6073fb6f53741944aba8d336b Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Wed, 14 Aug 2019 17:56:48 +0300 Subject: [PATCH 25/71] arm64: dts: imx8mn: Add devfreq nodes Signed-off-by: Leonard Crestez --- arch/arm64/boot/dts/freescale/imx8mn.dtsi | 816 ++++++++++++++++++++++ 1 file changed, 816 insertions(+) create mode 100644 arch/arm64/boot/dts/freescale/imx8mn.dtsi diff --git a/arch/arm64/boot/dts/freescale/imx8mn.dtsi b/arch/arm64/boot/dts/freescale/imx8mn.dtsi new file mode 100644 index 000000000000..c7e2d32278d1 --- /dev/null +++ b/arch/arm64/boot/dts/freescale/imx8mn.dtsi @@ -0,0 +1,816 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright 2019 NXP + */ + +#include +#include +#include +#include + +#include "imx8mn-pinfunc.h" + +/ { + compatible = "fsl,imx8mn"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + aliases { + ethernet0 = &fec1; + gpio0 = &gpio1; + gpio1 = &gpio2; + gpio2 = &gpio3; + gpio3 = &gpio4; + gpio4 = &gpio5; + i2c0 = &i2c1; + i2c1 = &i2c2; + i2c2 = &i2c3; + i2c3 = &i2c4; + mmc0 = &usdhc1; + mmc1 = &usdhc2; + mmc2 = &usdhc3; + serial0 = &uart1; + serial1 = &uart2; + serial2 = &uart3; + serial3 = &uart4; + spi0 = &ecspi1; + spi1 = &ecspi2; + spi2 = &ecspi3; + }; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + A53_0: cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x0>; + clock-latency = <61036>; + clocks = <&clk IMX8MN_CLK_ARM>; + enable-method = "psci"; + next-level-cache = <&A53_L2>; + operating-points-v2 = <&a53_opp_table>; + nvmem-cells = <&cpu_speed_grade>; + nvmem-cell-names = "speed_grade"; + }; + + A53_1: cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x1>; + clock-latency = <61036>; + clocks = <&clk IMX8MN_CLK_ARM>; + enable-method = "psci"; + next-level-cache = <&A53_L2>; + operating-points-v2 = <&a53_opp_table>; + }; + + A53_2: cpu@2 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x2>; + clock-latency = <61036>; + clocks = <&clk IMX8MN_CLK_ARM>; + enable-method = "psci"; + next-level-cache = <&A53_L2>; + operating-points-v2 = <&a53_opp_table>; + }; + + A53_3: cpu@3 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + reg = <0x3>; + clock-latency = <61036>; + clocks = <&clk IMX8MN_CLK_ARM>; + enable-method = "psci"; + next-level-cache = <&A53_L2>; + operating-points-v2 = <&a53_opp_table>; + }; + + A53_L2: l2-cache0 { + compatible = "cache"; + }; + }; + + a53_opp_table: opp-table { + compatible = "operating-points-v2"; + opp-shared; + + opp-1200000000 { + opp-hz = /bits/ 64 <1200000000>; + opp-microvolt = <850000>; + opp-supported-hw = <0xb00>, <0x7>; + clock-latency-ns = <150000>; + opp-suspend; + }; + + opp-1400000000 { + opp-hz = /bits/ 64 <1400000000>; + opp-microvolt = <950000>; + opp-supported-hw = <0x300>, <0x7>; + clock-latency-ns = <150000>; + opp-suspend; + }; + + opp-1500000000 { + opp-hz = /bits/ 64 <1500000000>; + opp-microvolt = <1000000>; + opp-supported-hw = <0x100>, <0x3>; + clock-latency-ns = <150000>; + opp-suspend; + }; + }; + + 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>; + }; + }; + + noc_opp_table: noc-opp-table { + compatible = "operating-points-v2"; + + opp-100M { + opp-hz = /bits/ 64 <100000000>; + }; + opp-600M { + opp-hz = /bits/ 64 <600000000>; + }; + opp-800M { + opp-hz = /bits/ 64 <800000000>; + }; + }; + + memory@40000000 { + device_type = "memory"; + reg = <0x0 0x40000000 0 0x80000000>; + }; + + osc_32k: clock-osc-32k { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <32768>; + clock-output-names = "osc_32k"; + }; + + osc_24m: clock-osc-24m { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <24000000>; + clock-output-names = "osc_24m"; + }; + + clk_ext1: clock-ext1 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <133000000>; + clock-output-names = "clk_ext1"; + }; + + clk_ext2: clock-ext2 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <133000000>; + clock-output-names = "clk_ext2"; + }; + + clk_ext3: clock-ext3 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <133000000>; + clock-output-names = "clk_ext3"; + }; + + clk_ext4: clock-ext4 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency= <133000000>; + clock-output-names = "clk_ext4"; + }; + + psci { + compatible = "arm,psci-1.0"; + method = "smc"; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupts = , + , + , + ; + clock-frequency = <8000000>; + arm,no-tick-in-suspend; + }; + + soc@0 { + compatible = "simple-bus"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x0 0x0 0x3e000000>; + + aips1: bus@30000000 { + compatible = "fsl,aips-bus", "simple-bus"; + reg = <0x30000000 0x400000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + gpio1: gpio@30200000 { + compatible = "fsl,imx8mn-gpio", "fsl,imx35-gpio"; + reg = <0x30200000 0x10000>; + interrupts = , + ; + clocks = <&clk IMX8MN_CLK_GPIO1_ROOT>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&iomuxc 0 10 30>; + }; + + gpio2: gpio@30210000 { + compatible = "fsl,imx8mn-gpio", "fsl,imx35-gpio"; + reg = <0x30210000 0x10000>; + interrupts = , + ; + clocks = <&clk IMX8MN_CLK_GPIO2_ROOT>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&iomuxc 0 40 21>; + }; + + gpio3: gpio@30220000 { + compatible = "fsl,imx8mn-gpio", "fsl,imx35-gpio"; + reg = <0x30220000 0x10000>; + interrupts = , + ; + clocks = <&clk IMX8MN_CLK_GPIO3_ROOT>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&iomuxc 0 61 26>; + }; + + gpio4: gpio@30230000 { + compatible = "fsl,imx8mn-gpio", "fsl,imx35-gpio"; + reg = <0x30230000 0x10000>; + interrupts = , + ; + clocks = <&clk IMX8MN_CLK_GPIO4_ROOT>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&iomuxc 21 108 11>; + }; + + gpio5: gpio@30240000 { + compatible = "fsl,imx8mn-gpio", "fsl,imx35-gpio"; + reg = <0x30240000 0x10000>; + interrupts = , + ; + clocks = <&clk IMX8MN_CLK_GPIO5_ROOT>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + gpio-ranges = <&iomuxc 0 119 30>; + }; + + wdog1: watchdog@30280000 { + compatible = "fsl,imx8mn-wdt", "fsl,imx21-wdt"; + reg = <0x30280000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_WDOG1_ROOT>; + status = "disabled"; + }; + + wdog2: watchdog@30290000 { + compatible = "fsl,imx8mn-wdt", "fsl,imx21-wdt"; + reg = <0x30290000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_WDOG2_ROOT>; + status = "disabled"; + }; + + wdog3: watchdog@302a0000 { + compatible = "fsl,imx8mn-wdt", "fsl,imx21-wdt"; + reg = <0x302a0000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_WDOG3_ROOT>; + status = "disabled"; + }; + + sdma3: dma-controller@302b0000 { + compatible = "fsl,imx8mn-sdma", "fsl,imx7d-sdma"; + reg = <0x302b0000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_SDMA3_ROOT>, + <&clk IMX8MN_CLK_SDMA3_ROOT>; + clock-names = "ipg", "ahb"; + #dma-cells = <3>; + fsl,sdma-ram-script-name = "imx/sdma/sdma-imx7d.bin"; + }; + + sdma2: dma-controller@302c0000 { + compatible = "fsl,imx8mn-sdma", "fsl,imx7d-sdma"; + reg = <0x302c0000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_SDMA2_ROOT>, + <&clk IMX8MN_CLK_SDMA2_ROOT>; + clock-names = "ipg", "ahb"; + #dma-cells = <3>; + fsl,sdma-ram-script-name = "imx/sdma/sdma-imx7d.bin"; + }; + + iomuxc: pinctrl@30330000 { + compatible = "fsl,imx8mn-iomuxc"; + reg = <0x30330000 0x10000>; + }; + + gpr: iomuxc-gpr@30340000 { + compatible = "fsl,imx8mn-iomuxc-gpr", "syscon"; + reg = <0x30340000 0x10000>; + }; + + ocotp: ocotp-ctrl@30350000 { + compatible = "fsl,imx8mn-ocotp", "fsl,imx7d-ocotp", "syscon"; + reg = <0x30350000 0x10000>; + clocks = <&clk IMX8MN_CLK_OCOTP_ROOT>; + #address-cells = <1>; + #size-cells = <1>; + + cpu_speed_grade: speed-grade@10 { + reg = <0x10 4>; + }; + }; + + anatop: anatop@30360000 { + compatible = "fsl,imx8mn-anatop", "fsl,imx8mm-anatop", + "syscon", "simple-bus"; + reg = <0x30360000 0x10000>; + }; + + snvs: snvs@30370000 { + compatible = "fsl,sec-v4.0-mon","syscon", "simple-mfd"; + reg = <0x30370000 0x10000>; + + snvs_rtc: snvs-rtc-lp { + compatible = "fsl,sec-v4.0-mon-rtc-lp"; + regmap = <&snvs>; + offset = <0x34>; + interrupts = , + ; + clock-names = "snvs-rtc"; + }; + + snvs_pwrkey: snvs-powerkey { + compatible = "fsl,sec-v4.0-pwrkey"; + regmap = <&snvs>; + interrupts = ; + linux,keycode = ; + wakeup-source; + status = "disabled"; + }; + }; + + clk: clock-controller@30380000 { + compatible = "fsl,imx8mn-ccm"; + reg = <0x30380000 0x10000>; + #clock-cells = <1>; + clocks = <&osc_32k>, <&osc_24m>, <&clk_ext1>, <&clk_ext2>, + <&clk_ext3>, <&clk_ext4>; + clock-names = "osc_32k", "osc_24m", "clk_ext1", "clk_ext2", + "clk_ext3", "clk_ext4"; + }; + + src: reset-controller@30390000 { + compatible = "fsl,imx8mn-src", "syscon"; + reg = <0x30390000 0x10000>; + interrupts = ; + #reset-cells = <1>; + }; + }; + + aips2: bus@30400000 { + compatible = "fsl,aips-bus", "simple-bus"; + reg = <0x30400000 0x400000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + pwm1: pwm@30660000 { + compatible = "fsl,imx8mn-pwm", "fsl,imx27-pwm"; + reg = <0x30660000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_PWM1_ROOT>, + <&clk IMX8MN_CLK_PWM1_ROOT>; + clock-names = "ipg", "per"; + #pwm-cells = <2>; + status = "disabled"; + }; + + pwm2: pwm@30670000 { + compatible = "fsl,imx8mn-pwm", "fsl,imx27-pwm"; + reg = <0x30670000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_PWM2_ROOT>, + <&clk IMX8MN_CLK_PWM2_ROOT>; + clock-names = "ipg", "per"; + #pwm-cells = <2>; + status = "disabled"; + }; + + pwm3: pwm@30680000 { + compatible = "fsl,imx8mn-pwm", "fsl,imx27-pwm"; + reg = <0x30680000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_PWM3_ROOT>, + <&clk IMX8MN_CLK_PWM3_ROOT>; + clock-names = "ipg", "per"; + #pwm-cells = <2>; + status = "disabled"; + }; + + pwm4: pwm@30690000 { + compatible = "fsl,imx8mn-pwm", "fsl,imx27-pwm"; + reg = <0x30690000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_PWM4_ROOT>, + <&clk IMX8MN_CLK_PWM4_ROOT>; + clock-names = "ipg", "per"; + #pwm-cells = <2>; + status = "disabled"; + }; + }; + + aips3: bus@30800000 { + compatible = "fsl,aips-bus", "simple-bus"; + reg = <0x30800000 0x400000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + ecspi1: spi@30820000 { + compatible = "fsl,imx8mn-ecspi", "fsl,imx51-ecspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x30820000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_ECSPI1_ROOT>, + <&clk IMX8MN_CLK_ECSPI1_ROOT>; + clock-names = "ipg", "per"; + dmas = <&sdma1 0 7 1>, <&sdma1 1 7 2>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + + ecspi2: spi@30830000 { + compatible = "fsl,imx8mn-ecspi", "fsl,imx51-ecspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x30830000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_ECSPI2_ROOT>, + <&clk IMX8MN_CLK_ECSPI2_ROOT>; + clock-names = "ipg", "per"; + dmas = <&sdma1 2 7 1>, <&sdma1 3 7 2>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + + ecspi3: spi@30840000 { + compatible = "fsl,imx8mn-ecspi", "fsl,imx51-ecspi"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x30840000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_ECSPI3_ROOT>, + <&clk IMX8MN_CLK_ECSPI3_ROOT>; + clock-names = "ipg", "per"; + dmas = <&sdma1 4 7 1>, <&sdma1 5 7 2>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + + uart1: serial@30860000 { + compatible = "fsl,imx8mn-uart", "fsl,imx6q-uart"; + reg = <0x30860000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_UART1_ROOT>, + <&clk IMX8MN_CLK_UART1_ROOT>; + clock-names = "ipg", "per"; + dmas = <&sdma1 22 4 0>, <&sdma1 23 4 0>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + + uart3: serial@30880000 { + compatible = "fsl,imx8mn-uart", "fsl,imx6q-uart"; + reg = <0x30880000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_UART3_ROOT>, + <&clk IMX8MN_CLK_UART3_ROOT>; + clock-names = "ipg", "per"; + dmas = <&sdma1 26 4 0>, <&sdma1 27 4 0>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + + uart2: serial@30890000 { + compatible = "fsl,imx8mn-uart", "fsl,imx6q-uart"; + reg = <0x30890000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_UART2_ROOT>, + <&clk IMX8MN_CLK_UART2_ROOT>; + clock-names = "ipg", "per"; + status = "disabled"; + }; + + i2c1: i2c@30a20000 { + compatible = "fsl,imx8mn-i2c", "fsl,imx21-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x30a20000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_I2C1_ROOT>; + status = "disabled"; + }; + + i2c2: i2c@30a30000 { + compatible = "fsl,imx8mn-i2c", "fsl,imx21-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x30a30000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_I2C2_ROOT>; + status = "disabled"; + }; + + i2c3: i2c@30a40000 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "fsl,imx8mn-i2c", "fsl,imx21-i2c"; + reg = <0x30a40000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_I2C3_ROOT>; + status = "disabled"; + }; + + i2c4: i2c@30a50000 { + compatible = "fsl,imx8mn-i2c", "fsl,imx21-i2c"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x30a50000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_I2C4_ROOT>; + status = "disabled"; + }; + + uart4: serial@30a60000 { + compatible = "fsl,imx8mn-uart", "fsl,imx6q-uart"; + reg = <0x30a60000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_UART4_ROOT>, + <&clk IMX8MN_CLK_UART4_ROOT>; + clock-names = "ipg", "per"; + dmas = <&sdma1 28 4 0>, <&sdma1 29 4 0>; + dma-names = "rx", "tx"; + status = "disabled"; + }; + + usdhc1: mmc@30b40000 { + compatible = "fsl,imx8mn-usdhc", "fsl,imx7d-usdhc"; + reg = <0x30b40000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_DUMMY>, + <&clk IMX8MN_CLK_NAND_USDHC_BUS>, + <&clk IMX8MN_CLK_USDHC1_ROOT>; + clock-names = "ipg", "ahb", "per"; + assigned-clocks = <&clk IMX8MN_CLK_USDHC1>; + assigned-clock-rates = <400000000>; + fsl,tuning-start-tap = <20>; + fsl,tuning-step= <2>; + bus-width = <4>; + status = "disabled"; + }; + + usdhc2: mmc@30b50000 { + compatible = "fsl,imx8mn-usdhc", "fsl,imx7d-usdhc"; + reg = <0x30b50000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_DUMMY>, + <&clk IMX8MN_CLK_NAND_USDHC_BUS>, + <&clk IMX8MN_CLK_USDHC2_ROOT>; + clock-names = "ipg", "ahb", "per"; + fsl,tuning-start-tap = <20>; + fsl,tuning-step= <2>; + bus-width = <4>; + status = "disabled"; + }; + + usdhc3: mmc@30b60000 { + compatible = "fsl,imx8mn-usdhc", "fsl,imx7d-usdhc"; + reg = <0x30b60000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_DUMMY>, + <&clk IMX8MN_CLK_NAND_USDHC_BUS>, + <&clk IMX8MN_CLK_USDHC3_ROOT>; + clock-names = "ipg", "ahb", "per"; + assigned-clocks = <&clk IMX8MN_CLK_USDHC3_ROOT>; + assigned-clock-rates = <400000000>; + fsl,tuning-start-tap = <20>; + fsl,tuning-step= <2>; + bus-width = <4>; + status = "disabled"; + }; + + sdma1: dma-controller@30bd0000 { + compatible = "fsl,imx8mn-sdma", "fsl,imx7d-sdma"; + reg = <0x30bd0000 0x10000>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_SDMA1_ROOT>, + <&clk IMX8MN_CLK_SDMA1_ROOT>; + clock-names = "ipg", "ahb"; + #dma-cells = <3>; + fsl,sdma-ram-script-name = "imx/sdma/sdma-imx7d.bin"; + }; + + fec1: ethernet@30be0000 { + compatible = "fsl,imx8mn-fec", "fsl,imx6sx-fec"; + reg = <0x30be0000 0x10000>; + interrupts = , + , + ; + clocks = <&clk IMX8MN_CLK_ENET1_ROOT>, + <&clk IMX8MN_CLK_ENET1_ROOT>, + <&clk IMX8MN_CLK_ENET_TIMER>, + <&clk IMX8MN_CLK_ENET_REF>, + <&clk IMX8MN_CLK_ENET_PHY_REF>; + clock-names = "ipg", "ahb", "ptp", + "enet_clk_ref", "enet_out"; + assigned-clocks = <&clk IMX8MN_CLK_ENET_AXI>, + <&clk IMX8MN_CLK_ENET_TIMER>, + <&clk IMX8MN_CLK_ENET_REF>, + <&clk IMX8MN_CLK_ENET_TIMER>; + assigned-clock-parents = <&clk IMX8MN_SYS_PLL1_266M>, + <&clk IMX8MN_SYS_PLL2_100M>, + <&clk IMX8MN_SYS_PLL2_125M>; + assigned-clock-rates = <0>, <0>, <125000000>, <100000000>; + fsl,num-tx-queues = <3>; + fsl,num-rx-queues = <3>; + status = "disabled"; + }; + + }; + + noc: noc@32700000 { + compatible = "fsl,imx8mn-noc", "fsl,imx8m-noc"; + reg = <0x32700000 0x100000>; + clocks = <&clk IMX8MN_CLK_NOC>; + devfreq = <&ddrc>; + operating-points-v2 = <&noc_opp_table>; + }; + + aips4: bus@32c00000 { + compatible = "fsl,aips-bus", "simple-bus"; + reg = <0x32c00000 0x400000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + usbotg1: usb@32e40000 { + compatible = "fsl,imx8mn-usb", "fsl,imx7d-usb"; + reg = <0x32e40000 0x200>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_USB1_CTRL_ROOT>; + clock-names = "usb1_ctrl_root_clk"; + assigned-clocks = <&clk IMX8MN_CLK_USB_BUS>, + <&clk IMX8MN_CLK_USB_CORE_REF>; + assigned-clock-parents = <&clk IMX8MN_SYS_PLL2_500M>, + <&clk IMX8MN_SYS_PLL1_100M>; + fsl,usbphy = <&usbphynop1>; + fsl,usbmisc = <&usbmisc1 0>; + status = "disabled"; + }; + + usbmisc1: usbmisc@32e40200 { + compatible = "fsl,imx8mn-usbmisc", "fsl,imx7d-usbmisc"; + #index-cells = <1>; + reg = <0x32e40200 0x200>; + }; + + usbotg2: usb@32e50000 { + compatible = "fsl,imx8mn-usb", "fsl,imx7d-usb"; + reg = <0x32e50000 0x200>; + interrupts = ; + clocks = <&clk IMX8MN_CLK_USB1_CTRL_ROOT>; + clock-names = "usb1_ctrl_root_clk"; + assigned-clocks = <&clk IMX8MN_CLK_USB_BUS>, + <&clk IMX8MN_CLK_USB_CORE_REF>; + assigned-clock-parents = <&clk IMX8MN_SYS_PLL2_500M>, + <&clk IMX8MN_SYS_PLL1_100M>; + fsl,usbphy = <&usbphynop2>; + fsl,usbmisc = <&usbmisc2 0>; + status = "disabled"; + }; + + usbmisc2: usbmisc@32e50200 { + compatible = "fsl,imx8mn-usbmisc", "fsl,imx7d-usbmisc"; + #index-cells = <1>; + reg = <0x32e50200 0x200>; + }; + + }; + + dma_apbh: dma-controller@33000000 { + compatible = "fsl,imx7d-dma-apbh", "fsl,imx28-dma-apbh"; + reg = <0x33000000 0x2000>; + interrupts = , + , + , + ; + interrupt-names = "gpmi0", "gpmi1", "gpmi2", "gpmi3"; + #dma-cells = <1>; + dma-channels = <4>; + clocks = <&clk IMX8MN_CLK_NAND_USDHC_BUS_RAWNAND_CLK>; + }; + + gpmi: nand-controller@33002000 { + compatible = "fsl,imx8mn-gpmi-nand", "fsl,imx7d-gpmi-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0x33002000 0x2000>, <0x33004000 0x4000>; + reg-names = "gpmi-nand", "bch"; + interrupts = ; + interrupt-names = "bch"; + clocks = <&clk IMX8MN_CLK_NAND_ROOT>, + <&clk IMX8MN_CLK_NAND_USDHC_BUS_RAWNAND_CLK>; + clock-names = "gpmi_io", "gpmi_bch_apb"; + dmas = <&dma_apbh 0>; + dma-names = "rx-tx"; + status = "disabled"; + }; + + gic: interrupt-controller@38800000 { + compatible = "arm,gic-v3"; + reg = <0x38800000 0x10000>, + <0x38880000 0xc0000>; + #interrupt-cells = <3>; + interrupt-controller; + interrupts = ; + }; + + 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>; + operating-points-v2 = <&ddrc_opp_table>; + }; + + ddr_pmu: ddr-pmu@3d800000 { + compatible = "fsl,imx8mn-ddr-pmu", "fsl,imx8m-ddr-pmu"; + reg = <0x3d800000 0x400000>; + interrupt-parent = <&gic>; + interrupts = ; + }; + }; + + usbphynop1: usbphynop1 { + compatible = "usb-nop-xceiv"; + clocks = <&clk IMX8MN_CLK_USB_PHY_REF>; + assigned-clocks = <&clk IMX8MN_CLK_USB_PHY_REF>; + assigned-clock-parents = <&clk IMX8MN_SYS_PLL1_100M>; + clock-names = "main_clk"; + }; + + usbphynop2: usbphynop2 { + compatible = "usb-nop-xceiv"; + clocks = <&clk IMX8MN_CLK_USB_PHY_REF>; + assigned-clocks = <&clk IMX8MN_CLK_USB_PHY_REF>; + assigned-clock-parents = <&clk IMX8MN_SYS_PLL1_100M>; + clock-names = "main_clk"; + }; +}; -- GitLab From 0e00e5968b420a3cdea9ce80806b07e505ed4d2b Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 3 Jun 2019 11:47:55 +0300 Subject: [PATCH 26/71] arm64: defconfig: Enable imx8mm interconnect Signed-off-by: Leonard Crestez --- arch/arm64/configs/defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index 6bfa6727c3f6..7a660bb03146 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -802,6 +802,8 @@ CONFIG_FPGA_REGION=m CONFIG_OF_FPGA_REGION=m CONFIG_TEE=y CONFIG_OPTEE=y +CONFIG_INTERCONNECT=y +CONFIG_INTERCONNECT_IMX=y CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y CONFIG_EXT4_FS_POSIX_ACL=y -- GitLab From 1e27132fc355205dcaa3870a82156eb67e1a2749 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Wed, 21 Aug 2019 15:09:38 +0300 Subject: [PATCH 27/71] PM / devfreq: Add devfreq_get_devfreq_by_node Split off part of devfreq_get_devfreq_by_phandle into a separate function. This allows callers to fetch devfreq instances by enumerating devicetree instead of explicit phandles. Signed-off-by: Leonard Crestez --- drivers/devfreq/devfreq.c | 42 +++++++++++++++++++++++++++++---------- include/linux/devfreq.h | 1 + 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index dcd887e039f5..02d3848695c4 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -934,6 +934,29 @@ struct devfreq *devm_devfreq_add_device(struct device *dev, EXPORT_SYMBOL(devm_devfreq_add_device); #ifdef CONFIG_OF +/* + * devfreq_get_devfreq_by_node - Get the devfreq device from devicetree + * @np - pointer to device_node + * + * return the instance of devfreq device + */ +struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node) +{ + struct devfreq *devfreq; + + mutex_lock(&devfreq_list_lock); + list_for_each_entry(devfreq, &devfreq_list, node) { + if (devfreq->dev.parent + && devfreq->dev.parent->of_node == node) { + mutex_unlock(&devfreq_list_lock); + return devfreq; + } + } + mutex_unlock(&devfreq_list_lock); + + return ERR_PTR(-EPROBE_DEFER); +} + /* * devfreq_get_devfreq_by_phandle - Get the devfreq device from devicetree * @dev - instance to the given device @@ -956,21 +979,18 @@ struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index) if (!node) return ERR_PTR(-ENODEV); - mutex_lock(&devfreq_list_lock); - list_for_each_entry(devfreq, &devfreq_list, node) { - if (devfreq->dev.parent - && devfreq->dev.parent->of_node == node) { - mutex_unlock(&devfreq_list_lock); - of_node_put(node); - return devfreq; - } - } - mutex_unlock(&devfreq_list_lock); + devfreq = devfreq_get_devfreq_by_node(node); of_node_put(node); - return ERR_PTR(-EPROBE_DEFER); + return devfreq; } + #else +struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node) +{ + return ERR_PTR(-ENODEV); +} + struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index) { return ERR_PTR(-ENODEV); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 3162eb9b0954..7849fe4c666d 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -244,6 +244,7 @@ extern void devm_devfreq_unregister_notifier(struct device *dev, struct devfreq *devfreq, struct notifier_block *nb, unsigned int list); +extern struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node); extern struct devfreq *devfreq_get_devfreq_by_phandle(struct device *dev, int index); -- GitLab From 6acbef4b6c83627e7b882c38f45d13f4a97f03b9 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Wed, 21 Aug 2019 15:10:18 +0300 Subject: [PATCH 28/71] interconnect: Add of_icc_add_proxy On many SOCs there is no single node that describes the "interconnect", instead are multiple pieces of bus fabric which already support scaling. Add support for mapping multiple device nodes to the same icc_provider (likely a platform-level singleton). This is implemented at the devicetree parsing level: just add more device nodes which map to the same icc_provider instead. Signed-off-by: Leonard Crestez --- drivers/interconnect/core.c | 88 ++++++++++++++++++++++++--- include/linux/interconnect-provider.h | 7 +++ 2 files changed, 88 insertions(+), 7 deletions(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index 871eb4bc4efc..e00fe846ac9f 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -19,8 +19,15 @@ #include #include +struct of_icc_proxy { + struct device_node *of_node; + struct icc_provider *provider; + struct list_head list_node; +}; + static DEFINE_IDR(icc_idr); static LIST_HEAD(icc_providers); +static LIST_HEAD(icc_proxy_list); static DEFINE_MUTEX(icc_lock); static struct dentry *icc_debugfs_dir; @@ -264,6 +271,57 @@ struct icc_node *of_icc_xlate_onecell(struct of_phandle_args *spec, } EXPORT_SYMBOL_GPL(of_icc_xlate_onecell); +struct icc_provider *__of_icc_get_provider(struct device_node *np) +{ + struct of_icc_proxy *proxy; + + lockdep_assert_held(&icc_lock); + list_for_each_entry(proxy, &icc_proxy_list, list_node) + if (proxy->of_node == np) + return proxy->provider; + + return NULL; +} + +static int __of_icc_add_proxy(struct device_node *np, + struct icc_provider *provider) +{ + struct of_icc_proxy *proxy; + + lockdep_assert_held(&icc_lock); + proxy = kmalloc(sizeof(*proxy), GFP_KERNEL); + if (!proxy) + return -ENOMEM; + proxy->of_node = np; + proxy->provider = provider; + list_add_tail(&proxy->list_node, &icc_proxy_list); + + return 0; +} + +/** + * of_icc_add_proxy() - Add another device_node for a provider + * @np: OF node to alias from + * @provider: Interconnect provider to map to + * + * Make another device_node map to the same provider. + * + * This lasts until icc_provider_del. + */ +int of_icc_add_proxy(struct device_node *np, struct icc_provider *provider) +{ + int ret; + + mutex_lock(&icc_lock); + + ret = __of_icc_add_proxy(np, provider); + + mutex_unlock(&icc_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(of_icc_add_proxy); + /** * of_icc_get_from_provider() - Look-up interconnect node * @spec: OF phandle args to use for look-up @@ -276,19 +334,18 @@ EXPORT_SYMBOL_GPL(of_icc_xlate_onecell); */ static struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec) { - struct icc_node *node = ERR_PTR(-EPROBE_DEFER); struct icc_provider *provider; + struct icc_node *node; if (!spec || spec->args_count != 1) return ERR_PTR(-EINVAL); mutex_lock(&icc_lock); - list_for_each_entry(provider, &icc_providers, provider_list) { - if (provider->dev->of_node == spec->np) - node = provider->xlate(spec, provider->data); - if (!IS_ERR(node)) - break; - } + provider = __of_icc_get_provider(spec->np); + if (provider) + node = provider->xlate(spec, provider->data); + else + node = ERR_PTR(-EPROBE_DEFER); mutex_unlock(&icc_lock); return node; @@ -721,6 +778,8 @@ EXPORT_SYMBOL_GPL(icc_node_del); */ int icc_provider_add(struct icc_provider *provider) { + int ret; + if (WARN_ON(!provider->set)) return -EINVAL; if (WARN_ON(!provider->xlate)) @@ -728,6 +787,13 @@ int icc_provider_add(struct icc_provider *provider) mutex_lock(&icc_lock); + if (provider->dev) { + ret = __of_icc_add_proxy(provider->dev->of_node, provider); + if (ret) { + mutex_unlock(&icc_lock); + return ret; + } + } INIT_LIST_HEAD(&provider->nodes); list_add_tail(&provider->provider_list, &icc_providers); @@ -747,6 +813,8 @@ EXPORT_SYMBOL_GPL(icc_provider_add); */ int icc_provider_del(struct icc_provider *provider) { + struct of_icc_proxy *proxy, *tmp; + mutex_lock(&icc_lock); if (provider->users) { pr_warn("interconnect provider still has %d users\n", @@ -762,6 +830,12 @@ int icc_provider_del(struct icc_provider *provider) } list_del(&provider->provider_list); + list_for_each_entry_safe(proxy, tmp, &icc_proxy_list, list_node) + if (proxy->provider == provider) { + list_del(&proxy->list_node); + of_node_put(proxy->of_node); + kfree(proxy); + } mutex_unlock(&icc_lock); return 0; diff --git a/include/linux/interconnect-provider.h b/include/linux/interconnect-provider.h index 63caccadc2db..74e1e41c8fe5 100644 --- a/include/linux/interconnect-provider.h +++ b/include/linux/interconnect-provider.h @@ -97,6 +97,7 @@ void icc_node_add(struct icc_node *node, struct icc_provider *provider); void icc_node_del(struct icc_node *node); int icc_provider_add(struct icc_provider *provider); int icc_provider_del(struct icc_provider *provider); +int of_icc_add_proxy(struct device_node *np, struct icc_provider *provider); #else @@ -137,6 +138,12 @@ static inline int icc_provider_del(struct icc_provider *provider) return -ENOTSUPP; } +static inline int of_icc_add_proxy(struct device_node *np, + struct icc_provider *provider) +{ + return -ENOTSUPP; +} + #endif /* CONFIG_INTERCONNECT */ #endif /* __LINUX_INTERCONNECT_PROVIDER_H */ -- GitLab From fe5601c0f5974996ea2e399104c03c1f5297f5d7 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 22 Aug 2019 22:06:16 +0300 Subject: [PATCH 29/71] dt-bindings: devfreq: imx: Describe interconnect properties The interconnect-node-id property is parsed by the imx interconnect driver to find nodes on which frequencies can be adjusted. Add #interconnect-cells so that device drivers can request paths from bus nodes instead of requiring a separate "virtual" node to represent the interconnect itself. Signed-off-by: Leonard Crestez --- Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml | 5 +++++ Documentation/devicetree/bindings/devfreq/imx.yaml | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml b/Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml index 31db204e6845..014449a9dd01 100644 --- a/Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml +++ b/Documentation/devicetree/bindings/devfreq/imx-ddrc.yaml @@ -33,6 +33,11 @@ properties: operating-points-v2: true + interconnect-node-id: + $ref: /schemas/types.yaml#/definitions/uint32 + '#interconnect-cells': + const: 1 + devfreq-events: description: Phandle of PMU node $ref: "/schemas/types.yaml#/definitions/phandle" diff --git a/Documentation/devicetree/bindings/devfreq/imx.yaml b/Documentation/devicetree/bindings/devfreq/imx.yaml index 634870496d5e..f2f9b76c752f 100644 --- a/Documentation/devicetree/bindings/devfreq/imx.yaml +++ b/Documentation/devicetree/bindings/devfreq/imx.yaml @@ -45,6 +45,11 @@ properties: operating-points-v2: true + interconnect-node-id: + $ref: /schemas/types.yaml#/definitions/uint32 + '#interconnect-cells': + const: 1 + devfreq: description: | Phandle to another devfreq device to match OPPs with by using the -- GitLab From 5f96ff409faf17a179117cf59dc200cf691f4956 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Wed, 13 Mar 2019 20:34:06 +0100 Subject: [PATCH 30/71] interconnect: Add imx core driver This adds support for i.MX SoC family to interconnect framework. Platform drivers can describe the interconnect graph and several adjustment knobs where icc node bandwidth is converted to a DEV_PM_QOS_MIN_FREQUENCY request. The adjustable nodes are found based on an "interconnect-node-id" property by scanning the entire device tree. The interconnect provider doesn't need an virtual OF node, instead those same adjustable nodes are registered as proxies which xlate to the platform-level provider. The platform device for the interconnect needs to be registered from a SOC driver (similar to cpufreq). Signed-off-by: Alexandre Bailon Signed-off-by: Leonard Crestez --- drivers/interconnect/Kconfig | 1 + drivers/interconnect/Makefile | 1 + drivers/interconnect/imx/Kconfig | 5 + drivers/interconnect/imx/Makefile | 1 + drivers/interconnect/imx/imx.c | 279 ++++++++++++++++++++++++++++++ drivers/interconnect/imx/imx.h | 60 +++++++ 6 files changed, 347 insertions(+) create mode 100644 drivers/interconnect/imx/Kconfig create mode 100644 drivers/interconnect/imx/Makefile create mode 100644 drivers/interconnect/imx/imx.c create mode 100644 drivers/interconnect/imx/imx.h diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig index bfa4ca3ab7a9..e61802230f90 100644 --- a/drivers/interconnect/Kconfig +++ b/drivers/interconnect/Kconfig @@ -12,5 +12,6 @@ menuconfig INTERCONNECT if INTERCONNECT source "drivers/interconnect/qcom/Kconfig" +source "drivers/interconnect/imx/Kconfig" endif diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile index 28f2ab0824d5..20a13b7eb37f 100644 --- a/drivers/interconnect/Makefile +++ b/drivers/interconnect/Makefile @@ -4,3 +4,4 @@ icc-core-objs := core.o obj-$(CONFIG_INTERCONNECT) += icc-core.o obj-$(CONFIG_INTERCONNECT_QCOM) += qcom/ +obj-$(CONFIG_INTERCONNECT_IMX) += imx/ diff --git a/drivers/interconnect/imx/Kconfig b/drivers/interconnect/imx/Kconfig new file mode 100644 index 000000000000..7d81d3c83a61 --- /dev/null +++ b/drivers/interconnect/imx/Kconfig @@ -0,0 +1,5 @@ +config INTERCONNECT_IMX + bool "i.MX interconnect drivers" + depends on ARCH_MXC || COMPILE_TEST + help + Generic interconnect driver for i.MX SOCs diff --git a/drivers/interconnect/imx/Makefile b/drivers/interconnect/imx/Makefile new file mode 100644 index 000000000000..bb92fd9fe4a5 --- /dev/null +++ b/drivers/interconnect/imx/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_INTERCONNECT_IMX) += imx.o diff --git a/drivers/interconnect/imx/imx.c b/drivers/interconnect/imx/imx.c new file mode 100644 index 000000000000..0a04ac723c15 --- /dev/null +++ b/drivers/interconnect/imx/imx.c @@ -0,0 +1,279 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Interconnect framework driver for i.MX SoC + * + * Copyright (c) 2019, BayLibre + * Copyright (c) 2019, NXP + * Author: Alexandre Bailon + * Author: Leonard Crestez + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "imx.h" + +/* private icc_provider data */ +struct imx_icc_provider { + struct device *dev; +}; + +/* private icc_node data */ +struct imx_icc_node { + const struct imx_icc_node_desc *desc; + struct devfreq *devfreq; + struct dev_pm_qos_request qos_req; +}; + +static int imx_icc_aggregate(struct icc_node *node, u32 tag, + u32 avg_bw, u32 peak_bw, + u32 *agg_avg, u32 *agg_peak) +{ + *agg_avg += avg_bw; + *agg_peak = max(*agg_peak, peak_bw); + + return 0; +} + +static struct icc_node *imx_icc_xlate(struct of_phandle_args *spec, void *data) +{ + struct imx_icc_provider *desc = data; + struct icc_provider *provider = dev_get_drvdata(desc->dev); + unsigned int id = spec->args[0]; + struct icc_node *node; + + list_for_each_entry(node, &provider->nodes, node_list) + if (node->id == id) + return node; + + return ERR_PTR(-EINVAL); +} + +static int imx_icc_node_set(struct icc_node *node) +{ + struct device *dev = node->provider->dev; + struct imx_icc_node *node_data = node->data; + u64 freq; + + if (!node_data->devfreq) + return 0; + + freq = (node->avg_bw + node->peak_bw) * node_data->desc->adj->bw_mul; + do_div(freq, node_data->desc->adj->bw_div); + dev_dbg(dev, "%s avg_bw %ukBps peak_bw %ukBps min_freq %llukHz\n", + node->name, node->avg_bw, node->peak_bw, freq); + + if (freq > S32_MAX) { + dev_err(dev, "%s can't request more S32_MAX freq\n", + node->name); + return -ERANGE; + } + + dev_pm_qos_update_request(&node_data->qos_req, freq); + + return 0; +} + +static int imx_icc_set(struct icc_node *src, struct icc_node *dst) +{ + return imx_icc_node_set(dst); +} + +static const struct of_device_id imx_icc_node_of_match[] = { + { .compatible = "fsl,imx8m-nic" }, + { .compatible = "fsl,imx8m-noc" }, + { .compatible = "fsl,imx8m-ddrc" }, + {}, +}; + +static int imx_icc_node_init_devfreq(struct device *dev, + struct icc_node *node) +{ + struct imx_icc_node *node_data = node->data; + struct device_node *dn; + u32 node_id; + int ret; + + for_each_matching_node(dn, imx_icc_node_of_match) { + ret = of_property_read_u32(dn, "interconnect-node-id", + &node_id); + if (ret != 0) + continue; + + if (node_id == node->id) { + of_node_get(dn); + break; + } + } + + if (!dn) + return 0; + + dev_info(dev, "node %s[%d] has device node %pOF\n", + node->name, node->id, dn); + node_data->devfreq = devfreq_get_devfreq_by_node(dn); + if (IS_ERR(node_data->devfreq)) { + of_node_put(dn); + ret = PTR_ERR(node_data->devfreq); + dev_err(dev, "failed to fetch devfreq for %s: %d\n", + node->name, ret); + return ret; + } + + of_icc_add_proxy(dn, node->provider); + of_node_put(dn); + + return dev_pm_qos_add_request(node_data->devfreq->dev.parent, + &node_data->qos_req, + DEV_PM_QOS_MIN_FREQUENCY, 0); +} + +static struct icc_node *imx_icc_node_add(struct icc_provider *provider, + const struct imx_icc_node_desc *node_desc) +{ + struct imx_icc_provider *provider_data = provider->data; + struct device *dev = provider_data->dev; + struct imx_icc_node *node_data; + struct icc_node *node; + int ret; + + node = icc_node_create(node_desc->id); + if (IS_ERR(node)) { + dev_err(dev, "failed to create node %d\n", node_desc->id); + return node; + } + + if (node->data) { + dev_err(dev, "already created node %s id=%d\n", + node_desc->name, node_desc->id); + return ERR_PTR(-EEXIST); + } + + node_data = devm_kzalloc(dev, sizeof(*node_data), GFP_KERNEL); + if (!node_data) { + icc_node_destroy(node->id); + return ERR_PTR(-ENOMEM); + } + + node->name = node_desc->name; + node->data = node_data; + node_data->desc = node_desc; + icc_node_add(node, provider); + + if (node_desc->adj) { + ret = imx_icc_node_init_devfreq(dev, node); + if (ret < 0) { + icc_node_del(node); + icc_node_destroy(node->id); + return ERR_PTR(ret); + } + } + + return node; +} + +static void imx_icc_unregister_nodes(struct icc_provider *provider) +{ + struct icc_node *node, *tmp; + + list_for_each_entry_safe(node, tmp, &provider->nodes, node_list) { + struct imx_icc_node *node_data = node->data; + + icc_node_del(node); + icc_node_destroy(node->id); + if (dev_pm_qos_request_active(&node_data->qos_req)) + dev_pm_qos_remove_request(&node_data->qos_req); + } +} + +static int imx_icc_register_nodes(struct icc_provider *provider, + const struct imx_icc_node_desc *descs, + int count) +{ + int ret; + int i; + + for (i = 0; i < count; i++) { + struct icc_node *node; + const struct imx_icc_node_desc *node_desc = &descs[i]; + size_t j; + + node = imx_icc_node_add(provider, node_desc); + if (IS_ERR(node)) { + ret = PTR_ERR(node); + if (ret != -EPROBE_DEFER) + dev_err(provider->dev, "failed to add %s: %d\n", + node_desc->name, ret); + goto err; + } + + for (j = 0; j < node_desc->num_links; j++) + icc_link_create(node, node_desc->links[j]); + } + + return 0; + +err: + imx_icc_unregister_nodes(provider); + + return ret; +} + +int imx_icc_register(struct platform_device *pdev, + struct imx_icc_node_desc *nodes, int nodes_count) +{ + struct device *dev = &pdev->dev; + struct imx_icc_provider *provider_data; + struct icc_provider *provider; + int ret; + + provider_data = devm_kzalloc(dev, sizeof(*provider_data), GFP_KERNEL); + if (!provider_data) + return -ENOMEM; + provider_data->dev = dev; + + provider = devm_kzalloc(dev, sizeof(*provider), GFP_KERNEL); + if (!provider) + return -ENOMEM; + provider->set = imx_icc_set; + provider->aggregate = imx_icc_aggregate; + provider->xlate = imx_icc_xlate; + provider->data = provider_data; + provider->dev = dev; + platform_set_drvdata(pdev, provider); + + ret = icc_provider_add(provider); + if (ret) { + dev_err(dev, "error adding interconnect provider: %d\n", ret); + return ret; + } + + ret = imx_icc_register_nodes(provider, nodes, nodes_count); + if (ret) + goto provider_del; + + pr_info("registered %s\n", pdev->name); + + return 0; + +provider_del: + icc_provider_del(provider); + return ret; +} +EXPORT_SYMBOL_GPL(imx_icc_register); + +int imx_icc_unregister(struct platform_device *pdev) +{ + struct icc_provider *provider = platform_get_drvdata(pdev); + + icc_provider_del(provider); + imx_icc_unregister_nodes(provider); + + return 0; +} +EXPORT_SYMBOL_GPL(imx_icc_unregister); diff --git a/drivers/interconnect/imx/imx.h b/drivers/interconnect/imx/imx.h new file mode 100644 index 000000000000..9299b8d941f0 --- /dev/null +++ b/drivers/interconnect/imx/imx.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Interconnect framework driver for i.MX SoC + * + * Copyright (c) 2019, BayLibre + * Copyright (c) 2019, NXP + * Author: Alexandre Bailon + * Author: Leonard Crestez + */ +#ifndef __DRIVERS_INTERCONNECT_IMX_H +#define __DRIVERS_INTERCONNECT_IMX_H + +#include + +#define IMX_ICC_MAX_LINKS 4 + +/* + * struct imx_icc_node_adj - Describe a dynamic adjustment knob + */ +struct imx_icc_node_adj_desc { + unsigned int bw_mul, bw_div; +}; + +/* + * struct imx_icc_node - Describe an interconnect node + * @name: name of the node + * @id: an unique id to identify the node + * @links: an array of slaves' node id + * @num_links: number of id defined in links + */ +struct imx_icc_node_desc { + const char *name; + u16 id; + u16 links[IMX_ICC_MAX_LINKS]; + u16 num_links; + + const struct imx_icc_node_adj_desc *adj; +}; + +#define DEFINE_BUS_INTERCONNECT(_name, _id, _adj, ...) \ + { \ + .id = _id, \ + .name = _name, \ + .adj = _adj, \ + .num_links = ARRAY_SIZE(((int[]){ __VA_ARGS__ })), \ + .links = { __VA_ARGS__ }, \ + } + +#define DEFINE_BUS_MASTER(_name, _id, _dest_id) \ + DEFINE_BUS_INTERCONNECT(_name, _id, NULL, 1, _dest_id) + +#define DEFINE_BUS_SLAVE(_name, _id, _adj) \ + DEFINE_BUS_INTERCONNECT(_name, _id, _adj, 0) + +int imx_icc_register(struct platform_device *pdev, + struct imx_icc_node_desc *nodes, + int nodes_count); +int imx_icc_unregister(struct platform_device *pdev); + +#endif /* __DRIVERS_INTERCONNECT_IMX_H */ -- GitLab From 3f1ebe0bd2dd5437637a766a091d51256d215f69 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Wed, 13 Mar 2019 20:34:07 +0100 Subject: [PATCH 31/71] interconnect: imx: Add platform driver for imx8mm Add a platform driver for the i.MX8MM SoC describing bus topology. Bandwidth adjustments is currently only supported on the DDRC and main NOC. Scaling for the vpu/gpu/display NICs could be added in the future. Signed-off-by: Alexandre Bailon Signed-off-by: Leonard Crestez --- drivers/interconnect/imx/Kconfig | 4 + drivers/interconnect/imx/Makefile | 1 + drivers/interconnect/imx/imx8mm.c | 105 ++++++++++++++++++++++ include/dt-bindings/interconnect/imx8mm.h | 49 ++++++++++ 4 files changed, 159 insertions(+) create mode 100644 drivers/interconnect/imx/imx8mm.c create mode 100644 include/dt-bindings/interconnect/imx8mm.h diff --git a/drivers/interconnect/imx/Kconfig b/drivers/interconnect/imx/Kconfig index 7d81d3c83a61..15671fe7f600 100644 --- a/drivers/interconnect/imx/Kconfig +++ b/drivers/interconnect/imx/Kconfig @@ -3,3 +3,7 @@ config INTERCONNECT_IMX depends on ARCH_MXC || COMPILE_TEST help Generic interconnect driver for i.MX SOCs + +config INTERCONNECT_IMX8MM + def_bool y + depends on INTERCONNECT_IMX diff --git a/drivers/interconnect/imx/Makefile b/drivers/interconnect/imx/Makefile index bb92fd9fe4a5..5f658c1608a6 100644 --- a/drivers/interconnect/imx/Makefile +++ b/drivers/interconnect/imx/Makefile @@ -1 +1,2 @@ obj-$(CONFIG_INTERCONNECT_IMX) += imx.o +obj-$(CONFIG_INTERCONNECT_IMX8MM) += imx8mm.o diff --git a/drivers/interconnect/imx/imx8mm.c b/drivers/interconnect/imx/imx8mm.c new file mode 100644 index 000000000000..acc002153729 --- /dev/null +++ b/drivers/interconnect/imx/imx8mm.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Interconnect framework driver for i.MX SoC + * + * Copyright (c) 2019, BayLibre + * Copyright (c) 2019, NXP + * Author: Alexandre Bailon + * Author: Leonard Crestez + */ + +#include +#include +#include +#include + +#include + +#include "imx.h" + +static const struct imx_icc_node_adj_desc imx8mm_dram_adj = { + .bw_mul = 1, + .bw_div = 16, +}; + +static const struct imx_icc_node_adj_desc imx8mm_noc_adj = { + .bw_mul = 1, + .bw_div = 16, +}; + +/* + * Describe bus masters, slaves and connections between them + * + * This is a simplified subset of the bus diagram, there are several other + * PL301 nics which are skipped/merged into PL301_MAIN + */ +static struct imx_icc_node_desc nodes[] = { + DEFINE_BUS_INTERCONNECT("NOC", IMX8MM_ICN_NOC, &imx8mm_noc_adj, + IMX8MM_ICS_DRAM, IMX8MM_ICN_MAIN), + + DEFINE_BUS_SLAVE("DRAM", IMX8MM_ICS_DRAM, &imx8mm_dram_adj), + DEFINE_BUS_SLAVE("OCRAM", IMX8MM_ICS_OCRAM, NULL), + DEFINE_BUS_MASTER("A53", IMX8MM_ICM_A53, IMX8MM_ICN_NOC), + + /* VPUMIX */ + DEFINE_BUS_MASTER("VPU H1", IMX8MM_ICM_VPU_H1, IMX8MM_ICN_VIDEO), + DEFINE_BUS_MASTER("VPU G1", IMX8MM_ICM_VPU_G1, IMX8MM_ICN_VIDEO), + DEFINE_BUS_MASTER("VPU G2", IMX8MM_ICM_VPU_G2, IMX8MM_ICN_VIDEO), + DEFINE_BUS_INTERCONNECT("PL301_VIDEO", IMX8MM_ICN_VIDEO, NULL, IMX8MM_ICN_NOC), + + /* GPUMIX */ + DEFINE_BUS_MASTER("GPU 2D", IMX8MM_ICM_GPU2D, IMX8MM_ICN_GPU), + DEFINE_BUS_MASTER("GPU 3D", IMX8MM_ICM_GPU3D, IMX8MM_ICN_GPU), + DEFINE_BUS_INTERCONNECT("PL301_GPU", IMX8MM_ICN_GPU, NULL, IMX8MM_ICN_NOC), + + /* DISPLAYMIX */ + DEFINE_BUS_MASTER("CSI", IMX8MM_ICM_CSI, IMX8MM_ICN_MIPI), + DEFINE_BUS_MASTER("LCDIF", IMX8MM_ICM_LCDIF, IMX8MM_ICN_MIPI), + DEFINE_BUS_INTERCONNECT("PL301_MIPI", IMX8MM_ICN_MIPI, NULL, IMX8MM_ICN_NOC), + + /* HSIO */ + DEFINE_BUS_MASTER("USB1", IMX8MM_ICM_USB1, IMX8MM_ICN_HSIO), + DEFINE_BUS_MASTER("USB2", IMX8MM_ICM_USB2, IMX8MM_ICN_HSIO), + DEFINE_BUS_MASTER("PCIE", IMX8MM_ICM_PCIE, IMX8MM_ICN_HSIO), + DEFINE_BUS_INTERCONNECT("PL301_HSIO", IMX8MM_ICN_HSIO, NULL, IMX8MM_ICN_NOC), + + /* Audio */ + DEFINE_BUS_MASTER("SDMA2", IMX8MM_ICM_SDMA2, IMX8MM_ICN_AUDIO), + DEFINE_BUS_MASTER("SDMA3", IMX8MM_ICM_SDMA3, IMX8MM_ICN_AUDIO), + DEFINE_BUS_INTERCONNECT("PL301_AUDIO", IMX8MM_ICN_AUDIO, NULL, IMX8MM_ICN_MAIN), + + /* Ethernet */ + DEFINE_BUS_MASTER("ENET", IMX8MM_ICM_ENET, IMX8MM_ICN_ENET), + DEFINE_BUS_INTERCONNECT("PL301_ENET", IMX8MM_ICN_ENET, NULL, IMX8MM_ICN_MAIN), + + /* Other */ + DEFINE_BUS_MASTER("SDMA1", IMX8MM_ICM_SDMA1, IMX8MM_ICN_MAIN), + DEFINE_BUS_MASTER("NAND", IMX8MM_ICM_NAND, IMX8MM_ICN_MAIN), + DEFINE_BUS_MASTER("USDHC1", IMX8MM_ICM_USDHC1, IMX8MM_ICN_MAIN), + DEFINE_BUS_MASTER("USDHC2", IMX8MM_ICM_USDHC2, IMX8MM_ICN_MAIN), + DEFINE_BUS_MASTER("USDHC3", IMX8MM_ICM_USDHC3, IMX8MM_ICN_MAIN), + DEFINE_BUS_INTERCONNECT("PL301_MAIN", IMX8MM_ICN_MAIN, NULL, + IMX8MM_ICN_NOC, IMX8MM_ICS_OCRAM), +}; + +static int imx8mm_icc_probe(struct platform_device *pdev) +{ + return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes)); +} + +static int imx8mm_icc_remove(struct platform_device *pdev) +{ + return imx_icc_unregister(pdev); +} + +static struct platform_driver imx8mm_icc_driver = { + .probe = imx8mm_icc_probe, + .remove = imx8mm_icc_remove, + .driver = { + .name = "imx8mm-interconnect", + }, +}; + +module_platform_driver(imx8mm_icc_driver); +MODULE_AUTHOR("Alexandre Bailon "); +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/interconnect/imx8mm.h b/include/dt-bindings/interconnect/imx8mm.h new file mode 100644 index 000000000000..5404f2af15c3 --- /dev/null +++ b/include/dt-bindings/interconnect/imx8mm.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Interconnect framework driver for i.MX SoC + * + * Copyright (c) 2019, BayLibre + * Author: Alexandre Bailon + */ + +#ifndef __IMX8MM_ICM_INTERCONNECT_IDS_H +#define __IMX8MM_ICM_INTERCONNECT_IDS_H + +#define IMX8MM_ICN_NOC 1 +#define IMX8MM_ICS_DRAM 2 +#define IMX8MM_ICS_OCRAM 3 +#define IMX8MM_ICM_A53 4 + +#define IMX8MM_ICM_VPU_H1 5 +#define IMX8MM_ICM_VPU_G1 6 +#define IMX8MM_ICM_VPU_G2 7 +#define IMX8MM_ICN_VIDEO 8 + +#define IMX8MM_ICM_GPU2D 9 +#define IMX8MM_ICM_GPU3D 10 +#define IMX8MM_ICN_GPU 11 + +#define IMX8MM_ICM_CSI 12 +#define IMX8MM_ICM_LCDIF 13 +#define IMX8MM_ICN_MIPI 14 + +#define IMX8MM_ICM_USB1 15 +#define IMX8MM_ICM_USB2 16 +#define IMX8MM_ICM_PCIE 17 +#define IMX8MM_ICN_HSIO 18 + +#define IMX8MM_ICM_SDMA2 19 +#define IMX8MM_ICM_SDMA3 20 +#define IMX8MM_ICN_AUDIO 21 + +#define IMX8MM_ICN_ENET 22 +#define IMX8MM_ICM_ENET 23 + +#define IMX8MM_ICN_MAIN 24 +#define IMX8MM_ICM_NAND 25 +#define IMX8MM_ICM_SDMA1 26 +#define IMX8MM_ICM_USDHC1 27 +#define IMX8MM_ICM_USDHC2 28 +#define IMX8MM_ICM_USDHC3 29 + +#endif /* __IMX8MM_ICM_INTERCONNECT_IDS_H */ -- GitLab From 17e17876ae05b1aa6e0a4366e64a6ea78fe846da Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Thu, 22 Aug 2019 17:51:28 +0300 Subject: [PATCH 32/71] soc: imx8mm: Register interconnect platform device Since there is no virtual devicetree node representing the interconnect we need to probe the icc device externally. Probing this from the SOC driver allows the interconnect device to be built as a module. This is very similar to imx-cpufreq-dt. Signed-off-by: Leonard Crestez --- drivers/soc/imx/soc-imx8.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/soc/imx/soc-imx8.c b/drivers/soc/imx/soc-imx8.c index 6346923e86c6..6626a7704e99 100644 --- a/drivers/soc/imx/soc-imx8.c +++ b/drivers/soc/imx/soc-imx8.c @@ -27,6 +27,7 @@ struct imx8_soc_data { char *name; + char *icc_driver; u32 (*soc_revision)(void); }; @@ -118,6 +119,7 @@ static const struct imx8_soc_data imx8mq_soc_data = { static const struct imx8_soc_data imx8mm_soc_data = { .name = "i.MX8MM", .soc_revision = imx8mm_soc_revision, + .icc_driver = "imx8mm-interconnect", }; static const struct imx8_soc_data imx8mn_soc_data = { @@ -183,6 +185,8 @@ static int __init imx8_soc_init(void) if (IS_ENABLED(CONFIG_ARM_IMX_CPUFREQ_DT)) platform_device_register_simple("imx-cpufreq-dt", -1, NULL, 0); + if (IS_ENABLED(CONFIG_INTERCONNECT_IMX) && data->icc_driver) + platform_device_register_simple(data->icc_driver, -1, NULL, 0); return 0; -- GitLab From cd30a89a544883f81bd0ced977b9e4dc0ea207b3 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 23 Sep 2019 16:46:00 +0300 Subject: [PATCH 33/71] soc: imx8mq: Register interconnect platform device Signed-off-by: Leonard Crestez --- drivers/soc/imx/soc-imx8.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/imx/soc-imx8.c b/drivers/soc/imx/soc-imx8.c index 6626a7704e99..17ebb454fbaf 100644 --- a/drivers/soc/imx/soc-imx8.c +++ b/drivers/soc/imx/soc-imx8.c @@ -114,6 +114,7 @@ static u32 __init imx8mm_soc_revision(void) static const struct imx8_soc_data imx8mq_soc_data = { .name = "i.MX8MQ", .soc_revision = imx8mq_soc_revision, + .icc_driver = "imx8mq-interconnect", }; static const struct imx8_soc_data imx8mm_soc_data = { -- GitLab From 3833bb1d79f5c076527a15085cca6f2ee05e94b1 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 23 Sep 2019 16:46:32 +0300 Subject: [PATCH 34/71] soc: imx8mn: Register interconnect platform device Signed-off-by: Leonard Crestez --- drivers/soc/imx/soc-imx8.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/imx/soc-imx8.c b/drivers/soc/imx/soc-imx8.c index 17ebb454fbaf..f6d28aba203e 100644 --- a/drivers/soc/imx/soc-imx8.c +++ b/drivers/soc/imx/soc-imx8.c @@ -126,6 +126,7 @@ static const struct imx8_soc_data imx8mm_soc_data = { static const struct imx8_soc_data imx8mn_soc_data = { .name = "i.MX8MN", .soc_revision = imx8mm_soc_revision, + .icc_driver = "imx8mn-interconnect", }; static const struct of_device_id imx8_soc_match[] = { -- GitLab From 0b45759e4b50363176930406f867d26554b0c01b Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 4 Jun 2019 13:40:00 +0300 Subject: [PATCH 35/71] arm64: dts: imx8mm: Add interconnect properties Add #interconnect-cells and interconnect-node-id properties on devfreq nodes. The imx interconnect provider will scan these. Signed-off-by: Leonard Crestez --- arch/arm64/boot/dts/freescale/imx8mm.dtsi | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi index 63c629bdf42d..ce0bd79ee43e 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi @@ -8,6 +8,7 @@ #include #include #include +#include #include "imx8mm-pinfunc.h" @@ -764,6 +765,8 @@ noc: noc@32700000 { reg = <0x32700000 0x100000>; clocks = <&clk IMX8MM_CLK_NOC>; devfreq = <&ddrc>; + #interconnect-cells = <1>; + interconnect-node-id = ; operating-points-v2 = <&noc_opp_table>; }; @@ -858,6 +861,8 @@ gic: interrupt-controller@38800000 { ddrc: dram-controller@3d400000 { compatible = "fsl,imx8mm-ddrc", "fsl,imx8m-ddrc"; reg = <0x3d400000 0x400000>; + #interconnect-cells = <1>; + interconnect-node-id = ; clock-names = "dram_core", "dram_pll", "dram_alt", -- GitLab From ff4d9cfd91bfa68efca10ebaa216bcb935f1a630 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 5 Aug 2019 12:57:41 +0300 Subject: [PATCH 36/71] interconnect: imx: Add platform driver for imx8mq Interconnect bus graph is based on internal architecture documents. Signed-off-by: Leonard Crestez --- drivers/interconnect/imx/Kconfig | 4 + drivers/interconnect/imx/Makefile | 1 + drivers/interconnect/imx/imx8mq.c | 103 ++++++++++++++++++++++ include/dt-bindings/interconnect/imx8mq.h | 48 ++++++++++ 4 files changed, 156 insertions(+) create mode 100644 drivers/interconnect/imx/imx8mq.c create mode 100644 include/dt-bindings/interconnect/imx8mq.h diff --git a/drivers/interconnect/imx/Kconfig b/drivers/interconnect/imx/Kconfig index 15671fe7f600..e0d36355eeb8 100644 --- a/drivers/interconnect/imx/Kconfig +++ b/drivers/interconnect/imx/Kconfig @@ -7,3 +7,7 @@ config INTERCONNECT_IMX config INTERCONNECT_IMX8MM def_bool y depends on INTERCONNECT_IMX + +config INTERCONNECT_IMX8MQ + def_bool y + depends on INTERCONNECT_IMX diff --git a/drivers/interconnect/imx/Makefile b/drivers/interconnect/imx/Makefile index 5f658c1608a6..8c5d6f9e47f5 100644 --- a/drivers/interconnect/imx/Makefile +++ b/drivers/interconnect/imx/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_INTERCONNECT_IMX) += imx.o obj-$(CONFIG_INTERCONNECT_IMX8MM) += imx8mm.o +obj-$(CONFIG_INTERCONNECT_IMX8MQ) += imx8mq.o diff --git a/drivers/interconnect/imx/imx8mq.c b/drivers/interconnect/imx/imx8mq.c new file mode 100644 index 000000000000..1dccb05b6336 --- /dev/null +++ b/drivers/interconnect/imx/imx8mq.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Interconnect framework driver for i.MX SoC + * + * Copyright (c) 2019, NXP + */ + +#include +#include +#include +#include + +#include + +#include "imx.h" + +static const struct imx_icc_node_adj_desc imx8mq_dram_adj = { + .bw_mul = 1, + .bw_div = 4, +}; + +static const struct imx_icc_node_adj_desc imx8mq_noc_adj = { + .bw_mul = 1, + .bw_div = 4, +}; + +/* + * Describe bus masters, slaves and connections between them + * + * This is a simplified subset of the bus diagram, there are several other + * PL301 nics which are skipped/merged into PL301_MAIN + */ +static struct imx_icc_node_desc nodes[] = { + DEFINE_BUS_INTERCONNECT("NOC", IMX8MQ_ICN_NOC, &imx8mq_noc_adj, + IMX8MQ_ICS_DRAM, IMX8MQ_ICN_MAIN), + + DEFINE_BUS_SLAVE("DRAM", IMX8MQ_ICS_DRAM, &imx8mq_dram_adj), + DEFINE_BUS_SLAVE("OCRAM", IMX8MQ_ICS_OCRAM, NULL), + DEFINE_BUS_MASTER("A53", IMX8MQ_ICM_A53, IMX8MQ_ICN_NOC), + + /* VPUMIX */ + DEFINE_BUS_MASTER("VPU", IMX8MQ_ICM_VPU, IMX8MQ_ICN_VIDEO), + DEFINE_BUS_INTERCONNECT("PL301_VIDEO", IMX8MQ_ICN_VIDEO, NULL, IMX8MQ_ICN_NOC), + + /* GPUMIX */ + DEFINE_BUS_MASTER("GPU", IMX8MQ_ICM_GPU, IMX8MQ_ICN_GPU), + DEFINE_BUS_INTERCONNECT("PL301_GPU", IMX8MQ_ICN_GPU, NULL, IMX8MQ_ICN_NOC), + + /* DISPMIX (only for DCSS) */ + DEFINE_BUS_MASTER("DC", IMX8MQ_ICM_DCSS, IMX8MQ_ICN_DCSS), + DEFINE_BUS_INTERCONNECT("PL301_DC", IMX8MQ_ICN_DCSS, NULL, IMX8MQ_ICN_NOC), + + /* USBMIX */ + DEFINE_BUS_MASTER("USB1", IMX8MQ_ICM_USB1, IMX8MQ_ICN_USB), + DEFINE_BUS_MASTER("USB2", IMX8MQ_ICM_USB2, IMX8MQ_ICN_USB), + DEFINE_BUS_INTERCONNECT("PL301_USB", IMX8MQ_ICN_USB, NULL, IMX8MQ_ICN_NOC), + + /* PL301_DISPLAY (IPs other than DCSS, inside SUPERMIX) */ + DEFINE_BUS_MASTER("CSI1", IMX8MQ_ICM_CSI1, IMX8MQ_ICN_DISPLAY), + DEFINE_BUS_MASTER("CSI2", IMX8MQ_ICM_CSI2, IMX8MQ_ICN_DISPLAY), + DEFINE_BUS_MASTER("LCDIF", IMX8MQ_ICM_LCDIF, IMX8MQ_ICN_DISPLAY), + DEFINE_BUS_INTERCONNECT("PL301_DISPLAY", IMX8MQ_ICN_DISPLAY, NULL, IMX8MQ_ICN_MAIN), + + /* AUDIO */ + DEFINE_BUS_MASTER("SDMA2", IMX8MQ_ICM_SDMA2, IMX8MQ_ICN_AUDIO), + DEFINE_BUS_INTERCONNECT("PL301_AUDIO", IMX8MQ_ICN_AUDIO, NULL, IMX8MQ_ICN_DISPLAY), + + /* ENET */ + DEFINE_BUS_MASTER("ENET", IMX8MQ_ICM_ENET, IMX8MQ_ICN_ENET), + DEFINE_BUS_INTERCONNECT("PL301_ENET", IMX8MQ_ICN_ENET, NULL, IMX8MQ_ICN_MAIN), + + /* OTHER */ + DEFINE_BUS_MASTER("SDMA1", IMX8MQ_ICM_SDMA1, IMX8MQ_ICN_MAIN), + DEFINE_BUS_MASTER("NAND", IMX8MQ_ICM_NAND, IMX8MQ_ICN_MAIN), + DEFINE_BUS_MASTER("USDHC1", IMX8MQ_ICM_USDHC1, IMX8MQ_ICN_MAIN), + DEFINE_BUS_MASTER("USDHC2", IMX8MQ_ICM_USDHC2, IMX8MQ_ICN_MAIN), + DEFINE_BUS_MASTER("PCIE1", IMX8MQ_ICM_PCIE1, IMX8MQ_ICN_MAIN), + DEFINE_BUS_MASTER("PCIE2", IMX8MQ_ICM_PCIE2, IMX8MQ_ICN_MAIN), + DEFINE_BUS_INTERCONNECT("PL301_MAIN", IMX8MQ_ICN_MAIN, NULL, + IMX8MQ_ICN_NOC, IMX8MQ_ICS_OCRAM), +}; + +static int imx8mq_icc_probe(struct platform_device *pdev) +{ + return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes)); +} + +static int imx8mq_icc_remove(struct platform_device *pdev) +{ + return imx_icc_unregister(pdev); +} + +static struct platform_driver imx8mq_icc_driver = { + .probe = imx8mq_icc_probe, + .remove = imx8mq_icc_remove, + .driver = { + .name = "imx8mq-interconnect", + }, +}; + +module_platform_driver(imx8mq_icc_driver); +MODULE_AUTHOR("Leonard Crestez "); +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/interconnect/imx8mq.h b/include/dt-bindings/interconnect/imx8mq.h new file mode 100644 index 000000000000..350cdbda466b --- /dev/null +++ b/include/dt-bindings/interconnect/imx8mq.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Interconnect framework driver for i.MX SoC + * + * Copyright (c) 2019, NXP + */ + +#ifndef __IMX8MQ_ICM_INTERCONNECT_IDS_H +#define __IMX8MQ_ICM_INTERCONNECT_IDS_H + +#define IMX8MQ_ICN_NOC 1 +#define IMX8MQ_ICS_DRAM 2 +#define IMX8MQ_ICS_OCRAM 3 +#define IMX8MQ_ICM_A53 4 + +#define IMX8MQ_ICM_VPU 5 +#define IMX8MQ_ICN_VIDEO 6 + +#define IMX8MQ_ICM_GPU 7 +#define IMX8MQ_ICN_GPU 8 + +#define IMX8MQ_ICM_DCSS 9 +#define IMX8MQ_ICN_DCSS 10 + +#define IMX8MQ_ICM_USB1 11 +#define IMX8MQ_ICM_USB2 12 +#define IMX8MQ_ICN_USB 13 + +#define IMX8MQ_ICM_CSI1 14 +#define IMX8MQ_ICM_CSI2 15 +#define IMX8MQ_ICM_LCDIF 16 +#define IMX8MQ_ICN_DISPLAY 17 + +#define IMX8MQ_ICM_SDMA2 18 +#define IMX8MQ_ICN_AUDIO 19 + +#define IMX8MQ_ICN_ENET 20 +#define IMX8MQ_ICM_ENET 21 + +#define IMX8MQ_ICM_SDMA1 22 +#define IMX8MQ_ICM_NAND 23 +#define IMX8MQ_ICM_USDHC1 24 +#define IMX8MQ_ICM_USDHC2 25 +#define IMX8MQ_ICM_PCIE1 26 +#define IMX8MQ_ICM_PCIE2 27 +#define IMX8MQ_ICN_MAIN 28 + +#endif /* __IMX8MQ_ICM_INTERCONNECT_IDS_H */ -- GitLab From cb314976560686f8e2e9b4f872798ca9eed0cc9a Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 5 Aug 2019 13:13:58 +0300 Subject: [PATCH 37/71] interconnect: imx: Add platform driver for imx8mn Bus graph absed on internal architecture documents Signed-off-by: Leonard Crestez --- drivers/interconnect/imx/Kconfig | 4 + drivers/interconnect/imx/Makefile | 1 + drivers/interconnect/imx/imx8mn.c | 94 +++++++++++++++++++++++ include/dt-bindings/interconnect/imx8mn.h | 41 ++++++++++ 4 files changed, 140 insertions(+) create mode 100644 drivers/interconnect/imx/imx8mn.c create mode 100644 include/dt-bindings/interconnect/imx8mn.h diff --git a/drivers/interconnect/imx/Kconfig b/drivers/interconnect/imx/Kconfig index e0d36355eeb8..bc311e86d255 100644 --- a/drivers/interconnect/imx/Kconfig +++ b/drivers/interconnect/imx/Kconfig @@ -8,6 +8,10 @@ config INTERCONNECT_IMX8MM def_bool y depends on INTERCONNECT_IMX +config INTERCONNECT_IMX8MN + def_bool y + depends on INTERCONNECT_IMX + config INTERCONNECT_IMX8MQ def_bool y depends on INTERCONNECT_IMX diff --git a/drivers/interconnect/imx/Makefile b/drivers/interconnect/imx/Makefile index 8c5d6f9e47f5..e39d6c6af3b7 100644 --- a/drivers/interconnect/imx/Makefile +++ b/drivers/interconnect/imx/Makefile @@ -1,3 +1,4 @@ obj-$(CONFIG_INTERCONNECT_IMX) += imx.o obj-$(CONFIG_INTERCONNECT_IMX8MM) += imx8mm.o +obj-$(CONFIG_INTERCONNECT_IMX8MN) += imx8mn.o obj-$(CONFIG_INTERCONNECT_IMX8MQ) += imx8mq.o diff --git a/drivers/interconnect/imx/imx8mn.c b/drivers/interconnect/imx/imx8mn.c new file mode 100644 index 000000000000..3141ac42c8e6 --- /dev/null +++ b/drivers/interconnect/imx/imx8mn.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Interconnect framework driver for i.MX SoC + * + * Copyright (c) 2019, NXP + */ + +#include +#include +#include +#include + +#include + +#include "imx.h" + +static const struct imx_icc_node_adj_desc imx8mn_dram_adj = { + .bw_mul = 1, + .bw_div = 4, +}; + +static const struct imx_icc_node_adj_desc imx8mn_noc_adj = { + .bw_mul = 1, + .bw_div = 4, +}; + +/* + * Describe bus masters, slaves and connections between them + * + * This is a simplified subset of the bus diagram, there are several other + * PL301 nics which are skipped/merged into PL301_MAIN + */ +static struct imx_icc_node_desc nodes[] = { + DEFINE_BUS_INTERCONNECT("NOC", IMX8MN_ICN_NOC, &imx8mn_noc_adj, + IMX8MN_ICS_DRAM, IMX8MN_ICN_MAIN), + + DEFINE_BUS_SLAVE("DRAM", IMX8MN_ICS_DRAM, &imx8mn_dram_adj), + DEFINE_BUS_SLAVE("OCRAM", IMX8MN_ICS_OCRAM, NULL), + DEFINE_BUS_MASTER("A53", IMX8MN_ICM_A53, IMX8MN_ICN_NOC), + + /* GPUMIX */ + DEFINE_BUS_MASTER("GPU", IMX8MN_ICM_GPU, IMX8MN_ICN_GPU), + DEFINE_BUS_INTERCONNECT("PL301_GPU", IMX8MN_ICN_GPU, NULL, IMX8MN_ICN_NOC), + + /* DISPLAYMIX */ + DEFINE_BUS_MASTER("CSI1", IMX8MN_ICM_CSI1, IMX8MN_ICN_MIPI), + DEFINE_BUS_MASTER("CSI2", IMX8MN_ICM_CSI2, IMX8MN_ICN_MIPI), + DEFINE_BUS_MASTER("ISI", IMX8MN_ICM_ISI, IMX8MN_ICN_MIPI), + DEFINE_BUS_MASTER("LCDIF", IMX8MN_ICM_LCDIF, IMX8MN_ICN_MIPI), + DEFINE_BUS_INTERCONNECT("PL301_MIPI", IMX8MN_ICN_MIPI, NULL, IMX8MN_ICN_NOC), + + /* USB goes straight to NOC */ + DEFINE_BUS_MASTER("USB", IMX8MN_ICM_USB, IMX8MN_ICN_NOC), + + /* Audio */ + DEFINE_BUS_MASTER("SDMA2", IMX8MN_ICM_SDMA2, IMX8MN_ICN_AUDIO), + DEFINE_BUS_MASTER("SDMA3", IMX8MN_ICM_SDMA3, IMX8MN_ICN_AUDIO), + DEFINE_BUS_INTERCONNECT("PL301_AUDIO", IMX8MN_ICN_AUDIO, NULL, IMX8MN_ICN_MAIN), + + /* Ethernet */ + DEFINE_BUS_MASTER("ENET", IMX8MN_ICM_ENET, IMX8MN_ICN_ENET), + DEFINE_BUS_INTERCONNECT("PL301_ENET", IMX8MN_ICN_ENET, NULL, IMX8MN_ICN_MAIN), + + /* Other */ + DEFINE_BUS_MASTER("SDMA1", IMX8MN_ICM_SDMA1, IMX8MN_ICN_MAIN), + DEFINE_BUS_MASTER("NAND", IMX8MN_ICM_NAND, IMX8MN_ICN_MAIN), + DEFINE_BUS_MASTER("USDHC1", IMX8MN_ICM_USDHC1, IMX8MN_ICN_MAIN), + DEFINE_BUS_MASTER("USDHC2", IMX8MN_ICM_USDHC2, IMX8MN_ICN_MAIN), + DEFINE_BUS_MASTER("USDHC3", IMX8MN_ICM_USDHC3, IMX8MN_ICN_MAIN), + DEFINE_BUS_INTERCONNECT("PL301_MAIN", IMX8MN_ICN_MAIN, NULL, + IMX8MN_ICN_NOC, IMX8MN_ICS_OCRAM), +}; + +static int imx8mn_icc_probe(struct platform_device *pdev) +{ + return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes)); +} + +static int imx8mn_icc_remove(struct platform_device *pdev) +{ + return imx_icc_unregister(pdev); +} + +static struct platform_driver imx8mn_icc_driver = { + .probe = imx8mn_icc_probe, + .remove = imx8mn_icc_remove, + .driver = { + .name = "imx8mn-interconnect", + }, +}; + +module_platform_driver(imx8mn_icc_driver); +MODULE_AUTHOR("Leonard Crestez "); +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/interconnect/imx8mn.h b/include/dt-bindings/interconnect/imx8mn.h new file mode 100644 index 000000000000..03d099dd71f8 --- /dev/null +++ b/include/dt-bindings/interconnect/imx8mn.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Interconnect framework driver for i.MX SoC + * + * Copyright (c) 2019, NXP + */ + +#ifndef __IMX8MN_ICM_INTERCONNECT_IDS_H +#define __IMX8MN_ICM_INTERCONNECT_IDS_H + +#define IMX8MN_ICN_NOC 1 +#define IMX8MN_ICS_DRAM 2 +#define IMX8MN_ICS_OCRAM 3 +#define IMX8MN_ICM_A53 4 + +#define IMX8MN_ICM_GPU 5 +#define IMX8MN_ICN_GPU 6 + +#define IMX8MN_ICM_CSI1 7 +#define IMX8MN_ICM_CSI2 8 +#define IMX8MN_ICM_ISI 9 +#define IMX8MN_ICM_LCDIF 10 +#define IMX8MN_ICN_MIPI 11 + +#define IMX8MN_ICM_USB 12 + +#define IMX8MN_ICM_SDMA2 13 +#define IMX8MN_ICM_SDMA3 14 +#define IMX8MN_ICN_AUDIO 15 + +#define IMX8MN_ICN_ENET 16 +#define IMX8MN_ICM_ENET 17 + +#define IMX8MN_ICM_NAND 18 +#define IMX8MN_ICM_SDMA1 19 +#define IMX8MN_ICM_USDHC1 20 +#define IMX8MN_ICM_USDHC2 21 +#define IMX8MN_ICM_USDHC3 22 +#define IMX8MN_ICN_MAIN 23 + +#endif /* __IMX8MN_ICM_INTERCONNECT_IDS_H */ -- GitLab From 42bee585d1d1757cb396c0c9a63dc35a60882ba3 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 5 Aug 2019 16:57:43 +0300 Subject: [PATCH 38/71] interconnect: imx: Add platform driver for imx8mp Based on internal architecture documents. Signed-off-by: Leonard Crestez --- drivers/interconnect/imx/Kconfig | 4 + drivers/interconnect/imx/Makefile | 1 + drivers/interconnect/imx/imx8mp.c | 124 ++++++++++++++++++++++ include/dt-bindings/interconnect/imx8mp.h | 68 ++++++++++++ 4 files changed, 197 insertions(+) create mode 100644 drivers/interconnect/imx/imx8mp.c create mode 100644 include/dt-bindings/interconnect/imx8mp.h diff --git a/drivers/interconnect/imx/Kconfig b/drivers/interconnect/imx/Kconfig index bc311e86d255..1280c2b6fee2 100644 --- a/drivers/interconnect/imx/Kconfig +++ b/drivers/interconnect/imx/Kconfig @@ -12,6 +12,10 @@ config INTERCONNECT_IMX8MN def_bool y depends on INTERCONNECT_IMX +config INTERCONNECT_IMX8MP + def_bool y + depends on INTERCONNECT_IMX + config INTERCONNECT_IMX8MQ def_bool y depends on INTERCONNECT_IMX diff --git a/drivers/interconnect/imx/Makefile b/drivers/interconnect/imx/Makefile index e39d6c6af3b7..fecd6c5a5de6 100644 --- a/drivers/interconnect/imx/Makefile +++ b/drivers/interconnect/imx/Makefile @@ -1,4 +1,5 @@ obj-$(CONFIG_INTERCONNECT_IMX) += imx.o obj-$(CONFIG_INTERCONNECT_IMX8MM) += imx8mm.o obj-$(CONFIG_INTERCONNECT_IMX8MN) += imx8mn.o +obj-$(CONFIG_INTERCONNECT_IMX8MP) += imx8mp.o obj-$(CONFIG_INTERCONNECT_IMX8MQ) += imx8mq.o diff --git a/drivers/interconnect/imx/imx8mp.c b/drivers/interconnect/imx/imx8mp.c new file mode 100644 index 000000000000..65a88323b258 --- /dev/null +++ b/drivers/interconnect/imx/imx8mp.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Interconnect framework driver for i.MX SoC + * + * Copyright (c) 2019, NXP + */ + +#include +#include +#include +#include + +#include + +#include "imx.h" + +static const struct imx_icc_node_adj_desc imx8mp_dram_adj = { + .bw_mul = 1, + .bw_div = 4, +}; + +static const struct imx_icc_node_adj_desc imx8mp_noc_adj = { + .bw_mul = 1, + .bw_div = 4, +}; + +/* + * Describe bus masters, slaves and connections between them + * + * This is a simplified subset of the bus diagram, there are several other + * PL301 nics which are skipped/merged into PL301_MAIN + */ +static struct imx_icc_node_desc nodes[] = { + DEFINE_BUS_INTERCONNECT("NOC", IMX8MP_ICN_NOC, &imx8mp_noc_adj, + IMX8MP_ICS_DRAM, IMX8MP_ICN_MAIN), + + DEFINE_BUS_SLAVE("DRAM", IMX8MP_ICS_DRAM, &imx8mp_dram_adj), + DEFINE_BUS_SLAVE("OCRAM", IMX8MP_ICS_OCRAM, NULL), + DEFINE_BUS_MASTER("A53", IMX8MP_ICM_A53, IMX8MP_ICN_NOC), + + /* MLMIX */ + DEFINE_BUS_MASTER("ML", IMX8MP_ICM_ML, IMX8MP_ICL_ML), + DEFINE_BUS_INTERCONNECT("NTTP_ML", IMX8MP_ICL_ML, NULL, IMX8MP_ICN_NOC), + + /* VPUMIX */ + DEFINE_BUS_MASTER("VPU G1", IMX8MP_ICM_VPU_G1, IMX8MP_ICN_VIDEO), + DEFINE_BUS_MASTER("VPU G2", IMX8MP_ICM_VPU_G2, IMX8MP_ICN_VIDEO), + DEFINE_BUS_MASTER("VPU E", IMX8MP_ICM_VPU_E, IMX8MP_ICN_VIDEO), + DEFINE_BUS_INTERCONNECT("NOC_VIDEO", IMX8MP_ICN_VIDEO, NULL, IMX8MP_ICL_VIDEO), + DEFINE_BUS_INTERCONNECT("NTTP_VIDEO", IMX8MP_ICL_VIDEO, NULL, IMX8MP_ICN_NOC), + + /* GPUMIX */ + DEFINE_BUS_MASTER("GPU 2D", IMX8MP_ICM_GPU_2D, IMX8MP_ICN_GPU), + DEFINE_BUS_MASTER("GPU 3D", IMX8MP_ICM_GPU_3D, IMX8MP_ICN_GPU), + DEFINE_BUS_INTERCONNECT("NOC_GPU", IMX8MP_ICN_GPU, NULL, IMX8MP_ICL_GPU), + DEFINE_BUS_INTERCONNECT("NTTP_GPU", IMX8MP_ICL_GPU, NULL, IMX8MP_ICN_NOC), + + /* MEDIAMIX */ + DEFINE_BUS_MASTER("CSI1", IMX8MP_ICM_CSI1, IMX8MP_ICN_MEDIA), + DEFINE_BUS_MASTER("CSI2", IMX8MP_ICM_CSI2, IMX8MP_ICN_MEDIA), + DEFINE_BUS_MASTER("ISI", IMX8MP_ICM_ISI, IMX8MP_ICN_MEDIA), + DEFINE_BUS_MASTER("DEWARP", IMX8MP_ICM_DEWARP, IMX8MP_ICN_MEDIA), + DEFINE_BUS_MASTER("ISP1", IMX8MP_ICM_ISP1, IMX8MP_ICN_MEDIA), + DEFINE_BUS_MASTER("ISP2", IMX8MP_ICM_ISP2, IMX8MP_ICN_MEDIA), + DEFINE_BUS_MASTER("LCDIF_MIPI", IMX8MP_ICM_LCDIF_MIPI, IMX8MP_ICN_MEDIA), + DEFINE_BUS_MASTER("LCDIF_LVDS", IMX8MP_ICM_LCDIF_LVDS, IMX8MP_ICN_MEDIA), + DEFINE_BUS_INTERCONNECT("NOC_MEDIA", IMX8MP_ICN_MEDIA, NULL, IMX8MP_ICL_MEDIA), + DEFINE_BUS_INTERCONNECT("NTTP_MEDIA", IMX8MP_ICL_MEDIA, NULL, IMX8MP_ICN_NOC), + + /* HDMIMIX */ + DEFINE_BUS_MASTER("LCDIF_HDMI", IMX8MP_ICM_LCDIF_HDMI, IMX8MP_ICN_HDMI), + DEFINE_BUS_INTERCONNECT("NOC_HDMI", IMX8MP_ICN_HDMI, NULL, IMX8MP_ICL_HDMI), + DEFINE_BUS_INTERCONNECT("NTTP_HDMI", IMX8MP_ICL_HDMI, NULL, IMX8MP_ICN_NOC), + + /* HSIOMIX */ + DEFINE_BUS_MASTER("PCIE", IMX8MP_ICM_PCIE, IMX8MP_ICN_HSIO), + DEFINE_BUS_MASTER("USB1", IMX8MP_ICM_USB1, IMX8MP_ICN_HSIO), + DEFINE_BUS_MASTER("USB2", IMX8MP_ICM_USB2, IMX8MP_ICN_HSIO), + DEFINE_BUS_INTERCONNECT("NOC_HSIO", IMX8MP_ICN_HSIO, NULL, IMX8MP_ICL_HSIO), + DEFINE_BUS_INTERCONNECT("NTTP_HSIO", IMX8MP_ICL_HSIO, NULL, IMX8MP_ICN_NOC), + + /* AUDIOMIX */ + DEFINE_BUS_MASTER("AUDIO_DSP", IMX8MP_ICM_AUDIO_DSP, IMX8MP_ICN_AUDIO), + DEFINE_BUS_MASTER("SDMA2", IMX8MP_ICM_SDMA2, IMX8MP_ICN_AUDIO), + DEFINE_BUS_MASTER("SDMA3", IMX8MP_ICM_SDMA3, IMX8MP_ICN_AUDIO), + DEFINE_BUS_INTERCONNECT("NOC_AUDIO", IMX8MP_ICN_AUDIO, NULL, IMX8MP_ICL_AUDIO), + DEFINE_BUS_INTERCONNECT("NTTP_AUDIO", IMX8MP_ICL_AUDIO, NULL, IMX8MP_ICN_NOC), + + /* Ethernet */ + DEFINE_BUS_MASTER("ENET", IMX8MP_ICM_ENET, IMX8MP_ICN_ENET), + DEFINE_BUS_MASTER("NETC", IMX8MP_ICM_NETC, IMX8MP_ICN_ENET), + DEFINE_BUS_INTERCONNECT("PL301_ENET", IMX8MP_ICN_ENET, NULL, IMX8MP_ICN_MAIN), + + /* Other */ + DEFINE_BUS_MASTER("SDMA1", IMX8MP_ICM_SDMA1, IMX8MP_ICN_MAIN), + DEFINE_BUS_MASTER("NAND", IMX8MP_ICM_NAND, IMX8MP_ICN_MAIN), + DEFINE_BUS_MASTER("USDHC1", IMX8MP_ICM_USDHC1, IMX8MP_ICN_MAIN), + DEFINE_BUS_MASTER("USDHC2", IMX8MP_ICM_USDHC2, IMX8MP_ICN_MAIN), + DEFINE_BUS_MASTER("USDHC3", IMX8MP_ICM_USDHC3, IMX8MP_ICN_MAIN), + DEFINE_BUS_INTERCONNECT("PL301_MAIN", IMX8MP_ICN_MAIN, NULL, + IMX8MP_ICN_NOC, IMX8MP_ICS_OCRAM), +}; + +static int imx8mp_icc_probe(struct platform_device *pdev) +{ + return imx_icc_register(pdev, nodes, ARRAY_SIZE(nodes)); +} + +static int imx8mp_icc_remove(struct platform_device *pdev) +{ + return imx_icc_unregister(pdev); +} + +static struct platform_driver imx8mp_icc_driver = { + .probe = imx8mp_icc_probe, + .remove = imx8mp_icc_remove, + .driver = { + .name = "imx8mp-interconnect", + }, +}; + +module_platform_driver(imx8mp_icc_driver); +MODULE_AUTHOR("Leonard Crestez "); +MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/interconnect/imx8mp.h b/include/dt-bindings/interconnect/imx8mp.h new file mode 100644 index 000000000000..ddd0ba69c18e --- /dev/null +++ b/include/dt-bindings/interconnect/imx8mp.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Interconnect framework driver for i.MX SoC + * + * Copyright (c) 2019, NXP + */ + +#ifndef __IMX8MP_ICM_INTERCONNECT_IDS_H +#define __IMX8MP_ICM_INTERCONNECT_IDS_H + +#define IMX8MP_ICN_NOC 1 +#define IMX8MP_ICS_DRAM 2 +#define IMX8MP_ICS_OCRAM 3 +#define IMX8MP_ICM_A53 4 + +#define IMX8MP_ICM_ML 5 +#define IMX8MP_ICL_ML 6 + +#define IMX8MP_ICM_VPU_G1 7 +#define IMX8MP_ICM_VPU_G2 8 +#define IMX8MP_ICM_VPU_E 9 +#define IMX8MP_ICN_VIDEO 10 +#define IMX8MP_ICL_VIDEO 11 + +#define IMX8MP_ICM_GPU_2D 12 +#define IMX8MP_ICM_GPU_3D 13 +#define IMX8MP_ICN_GPU 14 +#define IMX8MP_ICL_GPU 15 + +#define IMX8MP_ICM_CSI1 16 +#define IMX8MP_ICM_CSI2 17 +#define IMX8MP_ICM_ISI 18 +#define IMX8MP_ICM_DEWARP 19 +#define IMX8MP_ICM_ISP1 20 +#define IMX8MP_ICM_ISP2 21 +#define IMX8MP_ICM_LCDIF_MIPI 22 +#define IMX8MP_ICM_LCDIF_LVDS 23 +#define IMX8MP_ICN_MEDIA 24 +#define IMX8MP_ICL_MEDIA 25 + +#define IMX8MP_ICM_LCDIF_HDMI 26 +#define IMX8MP_ICN_HDMI 27 +#define IMX8MP_ICL_HDMI 28 + +#define IMX8MP_ICM_PCIE 29 +#define IMX8MP_ICM_USB1 30 +#define IMX8MP_ICM_USB2 31 +#define IMX8MP_ICN_HSIO 32 +#define IMX8MP_ICL_HSIO 33 + +#define IMX8MP_ICM_AUDIO_DSP 34 +#define IMX8MP_ICM_SDMA2 35 +#define IMX8MP_ICM_SDMA3 35 +#define IMX8MP_ICN_AUDIO 36 +#define IMX8MP_ICL_AUDIO 37 + +#define IMX8MP_ICM_ENET 38 +#define IMX8MP_ICM_NETC 39 +#define IMX8MP_ICN_ENET 40 + +#define IMX8MP_ICM_NAND 41 +#define IMX8MP_ICM_SDMA1 42 +#define IMX8MP_ICM_USDHC1 43 +#define IMX8MP_ICM_USDHC2 44 +#define IMX8MP_ICM_USDHC3 45 +#define IMX8MP_ICN_MAIN 46 + +#endif /* __IMX8MP_ICM_INTERCONNECT_IDS_H */ -- GitLab From d0a3f71317ce929d00f421c5bf3d7602f19581ec Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Fri, 28 Jun 2019 15:01:48 +0300 Subject: [PATCH 39/71] clk: imx8m-composite: Rename constants to drop PCG suffix Acronyms like "PCG" "CSC" "PSC" do not appear in imx8m manuals. These terms do appear in imx7ulp RM but that's a different design. Since these defines are private to a single C file drop the prefix and use more readable constants. Signed-off-by: Leonard Crestez --- drivers/clk/imx/clk-composite-8m.c | 42 +++++++++++++++--------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c index 388bdb94f841..751857a795d6 100644 --- a/drivers/clk/imx/clk-composite-8m.c +++ b/drivers/clk/imx/clk-composite-8m.c @@ -10,18 +10,18 @@ #include "clk.h" -#define PCG_PREDIV_SHIFT 16 -#define PCG_PREDIV_WIDTH 3 -#define PCG_PREDIV_MAX 8 +#define PREDIV_SHIFT 16 +#define PREDIV_WIDTH 3 +#define PREDIV_MAX 8 -#define PCG_DIV_SHIFT 0 -#define PCG_DIV_WIDTH 6 -#define PCG_DIV_MAX 64 +#define DIV_SHIFT 0 +#define DIV_WIDTH 6 +#define DIV_MAX 64 -#define PCG_PCS_SHIFT 24 -#define PCG_PCS_MASK 0x7 +#define MUX_SHIFT 24 +#define MUX_MASK 0x7 -#define PCG_CGC_SHIFT 28 +#define GATE_SHIFT 28 static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) @@ -38,11 +38,11 @@ static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk_hw *hw, NULL, divider->flags, divider->width); - div_value = readl(divider->reg) >> PCG_DIV_SHIFT; - div_value &= clk_div_mask(PCG_DIV_WIDTH); + div_value = readl(divider->reg) >> DIV_SHIFT; + div_value &= clk_div_mask(DIV_WIDTH); return divider_recalc_rate(hw, prediv_rate, div_value, NULL, - divider->flags, PCG_DIV_WIDTH); + divider->flags, DIV_WIDTH); } static int imx8m_clk_composite_compute_dividers(unsigned long rate, @@ -56,8 +56,8 @@ static int imx8m_clk_composite_compute_dividers(unsigned long rate, *prediv = 1; *postdiv = 1; - for (div1 = 1; div1 <= PCG_PREDIV_MAX; div1++) { - for (div2 = 1; div2 <= PCG_DIV_MAX; div2++) { + for (div1 = 1; div1 <= PREDIV_MAX; div1++) { + for (div2 = 1; div2 <= DIV_MAX; div2++) { int new_error = ((parent_rate / div1) / div2) - rate; if (abs(new_error) < abs(error)) { @@ -106,10 +106,10 @@ static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw, val = readl(divider->reg); val &= ~((clk_div_mask(divider->width) << divider->shift) | - (clk_div_mask(PCG_DIV_WIDTH) << PCG_DIV_SHIFT)); + (clk_div_mask(DIV_WIDTH) << DIV_SHIFT)); val |= (u32)(prediv_value - 1) << divider->shift; - val |= (u32)(div_value - 1) << PCG_DIV_SHIFT; + val |= (u32)(div_value - 1) << DIV_SHIFT; writel(val, divider->reg); spin_unlock_irqrestore(divider->lock, flags); @@ -140,8 +140,8 @@ struct clk *imx8m_clk_composite_flags(const char *name, mux_hw = &mux->hw; mux->reg = reg; - mux->shift = PCG_PCS_SHIFT; - mux->mask = PCG_PCS_MASK; + mux->shift = MUX_SHIFT; + mux->mask = MUX_MASK; div = kzalloc(sizeof(*div), GFP_KERNEL); if (!div) @@ -149,8 +149,8 @@ struct clk *imx8m_clk_composite_flags(const char *name, div_hw = &div->hw; div->reg = reg; - div->shift = PCG_PREDIV_SHIFT; - div->width = PCG_PREDIV_WIDTH; + div->shift = PREDIV_SHIFT; + div->width = PREDIV_WIDTH; div->lock = &imx_ccm_lock; div->flags = CLK_DIVIDER_ROUND_CLOSEST; @@ -160,7 +160,7 @@ struct clk *imx8m_clk_composite_flags(const char *name, gate_hw = &gate->hw; gate->reg = reg; - gate->bit_idx = PCG_CGC_SHIFT; + gate->bit_idx = GATE_SHIFT; hw = clk_hw_register_composite(NULL, name, parent_names, num_parents, mux_hw, &clk_mux_ops, div_hw, -- GitLab From 1a2013006c35f2c7d19e407eb1b21bbd8334ac45 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 17 Jun 2019 16:19:10 +0300 Subject: [PATCH 40/71] clk: imx8m-composite: Switch to determine_rate This allows consumers to use min_rate max_rate. Signed-off-by: Leonard Crestez --- drivers/clk/imx/clk-composite-8m.c | 34 +++++++++++++++++++----------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/drivers/clk/imx/clk-composite-8m.c b/drivers/clk/imx/clk-composite-8m.c index 751857a795d6..d884195d01a3 100644 --- a/drivers/clk/imx/clk-composite-8m.c +++ b/drivers/clk/imx/clk-composite-8m.c @@ -47,6 +47,8 @@ static unsigned long imx8m_clk_composite_divider_recalc_rate(struct clk_hw *hw, static int imx8m_clk_composite_compute_dividers(unsigned long rate, unsigned long parent_rate, + unsigned long min_rate, + unsigned long max_rate, int *prediv, int *postdiv) { int div1, div2; @@ -58,7 +60,13 @@ static int imx8m_clk_composite_compute_dividers(unsigned long rate, for (div1 = 1; div1 <= PREDIV_MAX; div1++) { for (div2 = 1; div2 <= DIV_MAX; div2++) { - int new_error = ((parent_rate / div1) / div2) - rate; + unsigned long new_rate; + int new_error; + + new_rate = ((parent_rate / div1) / div2); + if (new_rate < min_rate || new_rate > max_rate) + continue; + new_error = new_rate - rate; if (abs(new_error) < abs(error)) { *prediv = div1; @@ -71,24 +79,25 @@ static int imx8m_clk_composite_compute_dividers(unsigned long rate, return ret; } -static long imx8m_clk_composite_divider_round_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long *prate) +static int imx8m_clk_composite_divider_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { int prediv_value; int div_value; - imx8m_clk_composite_compute_dividers(rate, *prate, - &prediv_value, &div_value); - rate = DIV_ROUND_UP(*prate, prediv_value); + imx8m_clk_composite_compute_dividers(req->rate, req->best_parent_rate, + req->min_rate, req->max_rate, + &prediv_value, &div_value); - return DIV_ROUND_UP(rate, div_value); + req->rate = DIV_ROUND_UP(req->best_parent_rate, prediv_value); + req->rate = DIV_ROUND_UP(req->rate, div_value); + return 0; } static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw, - unsigned long rate, - unsigned long parent_rate) + unsigned long rate, + unsigned long parent_rate) { struct clk_divider *divider = to_clk_divider(hw); unsigned long flags = 0; @@ -98,7 +107,8 @@ static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw, u32 val; ret = imx8m_clk_composite_compute_dividers(rate, parent_rate, - &prediv_value, &div_value); + 0, ULONG_MAX, + &prediv_value, &div_value); if (ret) return -EINVAL; @@ -119,7 +129,7 @@ static int imx8m_clk_composite_divider_set_rate(struct clk_hw *hw, static const struct clk_ops imx8m_clk_composite_divider_ops = { .recalc_rate = imx8m_clk_composite_divider_recalc_rate, - .round_rate = imx8m_clk_composite_divider_round_rate, + .determine_rate = imx8m_clk_composite_divider_determine_rate, .set_rate = imx8m_clk_composite_divider_set_rate, }; -- GitLab From 4d758cb2bb5429c62f5246fd1ed7bb9f412ceaff Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 3 Jun 2019 17:29:46 +0300 Subject: [PATCH 41/71] HACK arm64: dts: imx8mm: Add interconnect property to fec Signed-off-by: Leonard Crestez --- arch/arm64/boot/dts/freescale/imx8mm.dtsi | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/boot/dts/freescale/imx8mm.dtsi b/arch/arm64/boot/dts/freescale/imx8mm.dtsi index ce0bd79ee43e..5df7e3b2419f 100644 --- a/arch/arm64/boot/dts/freescale/imx8mm.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mm.dtsi @@ -753,6 +753,7 @@ fec1: ethernet@30be0000 { <&clk IMX8MM_SYS_PLL2_100M>, <&clk IMX8MM_SYS_PLL2_125M>; assigned-clock-rates = <0>, <0>, <125000000>, <100000000>; + interconnects = <&noc IMX8MM_ICM_ENET &noc IMX8MM_ICS_DRAM>; fsl,num-tx-queues = <3>; fsl,num-rx-queues = <3>; status = "disabled"; -- GitLab From 1b646ffdd4deb270fb7157682c54f2d668d0beb9 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 3 Jun 2019 15:50:57 +0300 Subject: [PATCH 42/71] HACK net: fec: Add interconnect path request Request a peak of 125000 kbps because FEC is a gigabit ethernet controller. Signed-off-by: Leonard Crestez --- drivers/net/ethernet/freescale/fec.h | 3 +++ drivers/net/ethernet/freescale/fec_main.c | 29 +++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index f79e57f735b3..8ca529d82174 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -544,6 +544,9 @@ struct fec_enet_private { int wol_flag; u32 quirks; + struct icc_path *icc_path; + s32 icc_path_bw; + struct napi_struct napi; int csum_flags; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index e5610a4da539..a39c29dc4bd9 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -156,6 +157,9 @@ static unsigned char macaddr[ETH_ALEN]; module_param_array(macaddr, byte, NULL, 0); MODULE_PARM_DESC(macaddr, "FEC Ethernet MAC address"); +static bool fec_needs_icc = true; +module_param(fec_needs_icc, bool, 0644); + #if defined(CONFIG_M5272) /* * Some hardware gets it MAC address out of local flash memory. @@ -3398,6 +3402,23 @@ fec_probe(struct platform_device *pdev) if (of_get_property(np, "fsl,magic-packet", NULL)) fep->wol_flag |= FEC_WOL_HAS_MAGIC_PACKET; + /* Optional interconnect request */ + fep->icc_path = of_icc_get(&pdev->dev, NULL); + if (PTR_ERR(fep->icc_path) == -EPROBE_DEFER) { + ret = -EPROBE_DEFER; + goto failed_icc; + } else if (IS_ERR(fep->icc_path)) { + fep->icc_path = NULL; + } + /* Gigabit Ethernet in KBps */ + //fep->icc_path_bw = 125000; + /* Force DDRC med: */ + fep->icc_path_bw = 1599999; + /* Force DDRC high: */ + //fep->icc_path_bw = 2000000; + if (fec_needs_icc) + icc_set_bw(fep->icc_path, 0, fep->icc_path_bw); + phy_node = of_parse_phandle(np, "phy-handle", 0); if (!phy_node && of_phy_is_fixed_link(np)) { ret = of_phy_register_fixed_link(np); @@ -3567,6 +3588,8 @@ fec_probe(struct platform_device *pdev) of_phy_deregister_fixed_link(np); of_node_put(phy_node); failed_phy: + icc_put(fep->icc_path); +failed_icc: dev_id--; failed_ioremap: free_netdev(ndev); @@ -3592,6 +3615,7 @@ fec_drv_remove(struct platform_device *pdev) if (of_phy_is_fixed_link(np)) of_phy_deregister_fixed_link(np); of_node_put(fep->phy_node); + icc_put(fep->icc_path); free_netdev(ndev); return 0; @@ -3683,6 +3707,8 @@ static int __maybe_unused fec_runtime_suspend(struct device *dev) struct net_device *ndev = dev_get_drvdata(dev); struct fec_enet_private *fep = netdev_priv(ndev); + icc_set_bw(fep->icc_path, 0, 0); + clk_disable_unprepare(fep->clk_ahb); clk_disable_unprepare(fep->clk_ipg); @@ -3702,6 +3728,9 @@ static int __maybe_unused fec_runtime_resume(struct device *dev) if (ret) goto failed_clk_ipg; + if (fec_needs_icc) + icc_set_bw(fep->icc_path, 0, fep->icc_path_bw); + return 0; failed_clk_ipg: -- GitLab From a1b5d76082730a365f37742e136d560532a92aa7 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Fri, 20 Sep 2019 19:15:18 +0300 Subject: [PATCH 43/71] PM / devfreq: Call mutex_destroy if devfreq init fails The mutex_destroy call is only used for debugging but it's good to be consistent and clean this up anyway. Signed-off-by: Leonard Crestez --- drivers/devfreq/devfreq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 02d3848695c4..a7df161945c9 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -856,6 +856,7 @@ struct devfreq *devfreq_add_device(struct device *dev, dev_pm_qos_remove_request(&devfreq->user_min_freq_req); kfree(devfreq->time_in_state); kfree(devfreq->trans_table); + mutex_destroy(&devfreq->lock); kfree(devfreq); err_out: return ERR_PTR(err); -- GitLab From 756b8d1045b84460f61a455ca9a2842cbe28390c Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Mon, 23 Sep 2019 23:05:21 +0300 Subject: [PATCH 44/71] PM / devfreq: Fix devfreq_notifier_call returning errno Notifier callbacks shouldn't return negative errno but one of the NOTIFY_ values. Signed-off-by: Leonard Crestez --- drivers/devfreq/devfreq.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index a7df161945c9..0561bb5d349b 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -594,26 +594,27 @@ static int devfreq_notifier_call(struct notifier_block *nb, unsigned long type, void *devp) { struct devfreq *devfreq = container_of(nb, struct devfreq, nb); - int ret; + int err = -EINVAL; mutex_lock(&devfreq->lock); devfreq->scaling_min_freq = find_available_min_freq(devfreq); - if (!devfreq->scaling_min_freq) { - mutex_unlock(&devfreq->lock); - return -EINVAL; - } + if (!devfreq->scaling_min_freq) + goto out; devfreq->scaling_max_freq = find_available_max_freq(devfreq); - if (!devfreq->scaling_max_freq) { - mutex_unlock(&devfreq->lock); - return -EINVAL; - } + if (!devfreq->scaling_max_freq) + goto out; + + err = update_devfreq(devfreq); - ret = update_devfreq(devfreq); +out: mutex_unlock(&devfreq->lock); + if (err) + dev_err(&devfreq->dev, "dvfs OPP notifier" + " failed with (%d) error\n", err); - return ret; + return NOTIFY_OK; } /** -- GitLab From 68261a97c6f3337b1c64af22ba00fd7e924e826a Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 20 Aug 2019 15:01:41 +0300 Subject: [PATCH 45/71] PM / devfreq: Move OPP notifier registration to core An opp notifier can be registered by devfreq in order to respond to OPPs being enabled or disabled at runtime (for example by thermal). This is currently handled explicitly by drivers. Move notifier handling to devfreq_add_device because this shouldn't be hardware-specific. Handling this inside the core also takes less code. Signed-off-by: Leonard Crestez --- drivers/devfreq/devfreq.c | 84 +++--------------------------------- drivers/devfreq/exynos-bus.c | 7 --- drivers/devfreq/rk3399_dmc.c | 6 --- include/linux/devfreq.h | 8 ---- 4 files changed, 6 insertions(+), 99 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 0561bb5d349b..a1976daa4308 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -670,6 +670,7 @@ static void devfreq_dev_release(struct device *dev) list_del(&devfreq->node); mutex_unlock(&devfreq_list_lock); + dev_pm_opp_unregister_notifier(devfreq->dev.parent, &devfreq->nb); dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_max, DEV_PM_QOS_MAX_FREQUENCY); dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_min, @@ -735,7 +736,6 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->previous_freq = profile->initial_freq; devfreq->last_status.current_frequency = profile->initial_freq; devfreq->data = data; - devfreq->nb.notifier_call = devfreq_notifier_call; if (!devfreq->profile->max_state && !devfreq->profile->freq_table) { err = set_freq_table(devfreq); @@ -815,6 +815,11 @@ struct devfreq *devfreq_add_device(struct device *dev, if (err) goto err_devfreq; + devfreq->nb.notifier_call = devfreq_notifier_call; + err = dev_pm_opp_register_notifier(devfreq->dev.parent, &devfreq->nb); + if (err) + goto err_devfreq; + mutex_lock(&devfreq_list_lock); governor = try_then_request_governor(devfreq->governor_name); @@ -1630,83 +1635,6 @@ struct dev_pm_opp *devfreq_recommended_opp(struct device *dev, } EXPORT_SYMBOL(devfreq_recommended_opp); -/** - * devfreq_register_opp_notifier() - Helper function to get devfreq notified - * for any changes in the OPP availability - * changes - * @dev: The devfreq user device. (parent of devfreq) - * @devfreq: The devfreq object. - */ -int devfreq_register_opp_notifier(struct device *dev, struct devfreq *devfreq) -{ - return dev_pm_opp_register_notifier(dev, &devfreq->nb); -} -EXPORT_SYMBOL(devfreq_register_opp_notifier); - -/** - * devfreq_unregister_opp_notifier() - Helper function to stop getting devfreq - * notified for any changes in the OPP - * availability changes anymore. - * @dev: The devfreq user device. (parent of devfreq) - * @devfreq: The devfreq object. - * - * At exit() callback of devfreq_dev_profile, this must be included if - * devfreq_recommended_opp is used. - */ -int devfreq_unregister_opp_notifier(struct device *dev, struct devfreq *devfreq) -{ - return dev_pm_opp_unregister_notifier(dev, &devfreq->nb); -} -EXPORT_SYMBOL(devfreq_unregister_opp_notifier); - -static void devm_devfreq_opp_release(struct device *dev, void *res) -{ - devfreq_unregister_opp_notifier(dev, *(struct devfreq **)res); -} - -/** - * devm_devfreq_register_opp_notifier() - Resource-managed - * devfreq_register_opp_notifier() - * @dev: The devfreq user device. (parent of devfreq) - * @devfreq: The devfreq object. - */ -int devm_devfreq_register_opp_notifier(struct device *dev, - struct devfreq *devfreq) -{ - struct devfreq **ptr; - int ret; - - ptr = devres_alloc(devm_devfreq_opp_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - ret = devfreq_register_opp_notifier(dev, devfreq); - if (ret) { - devres_free(ptr); - return ret; - } - - *ptr = devfreq; - devres_add(dev, ptr); - - return 0; -} -EXPORT_SYMBOL(devm_devfreq_register_opp_notifier); - -/** - * devm_devfreq_unregister_opp_notifier() - Resource-managed - * devfreq_unregister_opp_notifier() - * @dev: The devfreq user device. (parent of devfreq) - * @devfreq: The devfreq object. - */ -void devm_devfreq_unregister_opp_notifier(struct device *dev, - struct devfreq *devfreq) -{ - WARN_ON(devres_release(dev, devm_devfreq_opp_release, - devm_devfreq_dev_match, devfreq)); -} -EXPORT_SYMBOL(devm_devfreq_unregister_opp_notifier); - /** * devfreq_register_notifier() - Register a driver with devfreq * @devfreq: The devfreq object. diff --git a/drivers/devfreq/exynos-bus.c b/drivers/devfreq/exynos-bus.c index d9f377912c10..2d9a4781f401 100644 --- a/drivers/devfreq/exynos-bus.c +++ b/drivers/devfreq/exynos-bus.c @@ -442,13 +442,6 @@ static int exynos_bus_probe(struct platform_device *pdev) goto err; } - /* Register opp_notifier to catch the change of OPP */ - ret = devm_devfreq_register_opp_notifier(dev, bus->devfreq); - if (ret < 0) { - dev_err(dev, "failed to register opp notifier\n"); - goto err; - } - /* * Enable devfreq-event to get raw data which is used to determine * current bus load. diff --git a/drivers/devfreq/rk3399_dmc.c b/drivers/devfreq/rk3399_dmc.c index 682465fa57e1..0c6ea8a024a7 100644 --- a/drivers/devfreq/rk3399_dmc.c +++ b/drivers/devfreq/rk3399_dmc.c @@ -456,8 +456,6 @@ static int rk3399_dmcfreq_probe(struct platform_device *pdev) goto err_free_opp; } - devm_devfreq_register_opp_notifier(dev, data->devfreq); - data->dev = dev; platform_set_drvdata(pdev, data); @@ -472,10 +470,6 @@ static int rk3399_dmcfreq_remove(struct platform_device *pdev) { struct rk3399_dmcfreq *dmcfreq = dev_get_drvdata(&pdev->dev); - /* - * Before remove the opp table we need to unregister the opp notifier. - */ - devm_devfreq_unregister_opp_notifier(dmcfreq->dev, dmcfreq->devfreq); dev_pm_opp_of_remove_table(dmcfreq->dev); return 0; diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 7849fe4c666d..42cf5e4cce94 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -222,14 +222,6 @@ extern int update_devfreq(struct devfreq *devfreq); /* Helper functions for devfreq user device driver with OPP. */ extern struct dev_pm_opp *devfreq_recommended_opp(struct device *dev, unsigned long *freq, u32 flags); -extern int devfreq_register_opp_notifier(struct device *dev, - struct devfreq *devfreq); -extern int devfreq_unregister_opp_notifier(struct device *dev, - struct devfreq *devfreq); -extern int devm_devfreq_register_opp_notifier(struct device *dev, - struct devfreq *devfreq); -extern void devm_devfreq_unregister_opp_notifier(struct device *dev, - struct devfreq *devfreq); extern int devfreq_register_notifier(struct devfreq *devfreq, struct notifier_block *nb, unsigned int list); -- GitLab From 4fb63a7aa441e97621d0b03ad12150af1eb7b1a0 Mon Sep 17 00:00:00 2001 From: Leonard Crestez Date: Tue, 17 Sep 2019 19:47:10 +0300 Subject: [PATCH 46/71] PM / devfreq: Replace devfreq_list with devfreq_class iteration It is already possible to enumerate all devfreq instances via "device class" functions so we don't need to track instances manually. The devfreq_list_lock still exists but now it only protects governors. Signed-off-by: Leonard Crestez --- drivers/devfreq/devfreq.c | 207 +++++++++++++++++++------------------- include/linux/devfreq.h | 4 - 2 files changed, 101 insertions(+), 110 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index a1976daa4308..243c1ba38d89 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -43,10 +43,14 @@ static struct workqueue_struct *devfreq_wq; /* The list of all device-devfreq governors */ static LIST_HEAD(devfreq_governor_list); -/* The list of all device-devfreq */ -static LIST_HEAD(devfreq_list); static DEFINE_MUTEX(devfreq_list_lock); +/** devfreq_match_parent() - Helper for find_device_devfreq() */ +static int devfreq_match_parent(struct device* dev, const void *data) +{ + return dev->parent == data; +} + /** * find_device_devfreq() - find devfreq struct using device pointer * @dev: device pointer used to lookup device devfreq. @@ -56,21 +60,16 @@ static DEFINE_MUTEX(devfreq_list_lock); */ static struct devfreq *find_device_devfreq(struct device *dev) { - struct devfreq *tmp_devfreq; + struct device *found_dev; if (IS_ERR_OR_NULL(dev)) { pr_err("DEVFREQ: %s: Invalid parameters\n", __func__); return ERR_PTR(-EINVAL); } - WARN(!mutex_is_locked(&devfreq_list_lock), - "devfreq_list_lock must be locked."); + found_dev = class_find_device(devfreq_class, NULL, + dev, devfreq_match_parent); - list_for_each_entry(tmp_devfreq, &devfreq_list, node) { - if (tmp_devfreq->dev.parent == dev) - return tmp_devfreq; - } - - return ERR_PTR(-ENODEV); + return found_dev ? to_devfreq(found_dev) : ERR_PTR(-ENODEV); } static unsigned long find_available_min_freq(struct devfreq *devfreq) @@ -666,10 +665,6 @@ static void devfreq_dev_release(struct device *dev) { struct devfreq *devfreq = to_devfreq(dev); - mutex_lock(&devfreq_list_lock); - list_del(&devfreq->node); - mutex_unlock(&devfreq_list_lock); - dev_pm_opp_unregister_notifier(devfreq->dev.parent, &devfreq->nb); dev_pm_qos_remove_notifier(devfreq->dev.parent, &devfreq->nb_max, DEV_PM_QOS_MAX_FREQUENCY); @@ -730,7 +725,6 @@ struct devfreq *devfreq_add_device(struct device *dev, devfreq->dev.parent = dev; devfreq->dev.class = devfreq_class; devfreq->dev.release = devfreq_dev_release; - INIT_LIST_HEAD(&devfreq->node); devfreq->profile = profile; strncpy(devfreq->governor_name, governor_name, DEVFREQ_NAME_LEN); devfreq->previous_freq = profile->initial_freq; @@ -839,8 +833,6 @@ struct devfreq *devfreq_add_device(struct device *dev, goto err_init; } - list_add(&devfreq->node, &devfreq_list); - mutex_unlock(&devfreq_list_lock); return devfreq; @@ -941,6 +933,12 @@ struct devfreq *devm_devfreq_add_device(struct device *dev, EXPORT_SYMBOL(devm_devfreq_add_device); #ifdef CONFIG_OF +/** devfreq_of_parent_match() - Helper for devfreq_get_devfreq_by_phandle() */ +static int devfreq_of_parent_match(struct device *dev, const void *data) +{ + return dev->parent && dev->parent->of_node == data; +} + /* * devfreq_get_devfreq_by_node - Get the devfreq device from devicetree * @np - pointer to device_node @@ -949,19 +947,12 @@ EXPORT_SYMBOL(devm_devfreq_add_device); */ struct devfreq *devfreq_get_devfreq_by_node(struct device_node *node) { - struct devfreq *devfreq; + struct device *found_dev; - mutex_lock(&devfreq_list_lock); - list_for_each_entry(devfreq, &devfreq_list, node) { - if (devfreq->dev.parent - && devfreq->dev.parent->of_node == node) { - mutex_unlock(&devfreq_list_lock); - return devfreq; - } - } - mutex_unlock(&devfreq_list_lock); + found_dev = class_find_device(devfreq_class, NULL, + node, devfreq_of_parent_match); - return ERR_PTR(-EPROBE_DEFER); + return found_dev ? to_devfreq(found_dev) : ERR_PTR(-EPROBE_DEFER); } /* @@ -1087,6 +1078,24 @@ int devfreq_resume_device(struct devfreq *devfreq) } EXPORT_SYMBOL(devfreq_resume_device); +/** devfreq_suspend_fn() - Helper for devfreq_suspend() */ +static int devfreq_suspend_fn(struct device *dev, void *data) +{ + if (devfreq_suspend_device(to_devfreq(dev))) + dev_err(dev, "failed to suspend devfreq device\n"); + + return 0; +} + +/** devfreq_resume_fn() - Helper for devfreq_resume() */ +static int devfreq_resume_fn(struct device *dev, void *data) +{ + if (devfreq_resume_device(to_devfreq(dev))) + dev_err(dev, "failed to resume devfreq device\n"); + + return 0; +} + /** * devfreq_suspend() - Suspend devfreq governors and devices * @@ -1097,17 +1106,7 @@ EXPORT_SYMBOL(devfreq_resume_device); */ void devfreq_suspend(void) { - struct devfreq *devfreq; - int ret; - - mutex_lock(&devfreq_list_lock); - list_for_each_entry(devfreq, &devfreq_list, node) { - ret = devfreq_suspend_device(devfreq); - if (ret) - dev_err(&devfreq->dev, - "failed to suspend devfreq device\n"); - } - mutex_unlock(&devfreq_list_lock); + class_for_each_device(devfreq_class, NULL, NULL, devfreq_suspend_fn); } /** @@ -1118,17 +1117,40 @@ void devfreq_suspend(void) */ void devfreq_resume(void) { - struct devfreq *devfreq; + class_for_each_device(devfreq_class, NULL, NULL, devfreq_resume_fn); +} + +/** devfreq_add_governor_fn() - Helper for devfreq_add_governor */ +static int devfreq_add_governor_fn(struct device *dev, void *data) +{ + struct devfreq *devfreq = to_devfreq(dev); + struct devfreq_governor *governor = data; int ret; - mutex_lock(&devfreq_list_lock); - list_for_each_entry(devfreq, &devfreq_list, node) { - ret = devfreq_resume_device(devfreq); - if (ret) - dev_warn(&devfreq->dev, - "failed to resume devfreq device\n"); + if (strncmp(devfreq->governor_name, governor->name, DEVFREQ_NAME_LEN)) + return 0; + + /* The following should never occur */ + if (devfreq->governor) { + dev_warn(dev->parent, "%s: Governor %s already present\n", + __func__, devfreq->governor->name); + ret = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_STOP, NULL); + if (ret) { + dev_warn(dev->parent, "%s: Governor %s stop = %d\n", + __func__, devfreq->governor->name, ret); + } } - mutex_unlock(&devfreq_list_lock); + + devfreq->governor = governor; + ret = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_START, NULL); + if (ret) { + dev_warn(dev->parent, "%s: Governor %s start=%d\n", + __func__, devfreq->governor->name, ret); + } + + return 0; } /** @@ -1138,7 +1160,6 @@ void devfreq_resume(void) int devfreq_add_governor(struct devfreq_governor *governor) { struct devfreq_governor *g; - struct devfreq *devfreq; int err = 0; if (!governor) { @@ -1156,38 +1177,8 @@ int devfreq_add_governor(struct devfreq_governor *governor) } list_add(&governor->node, &devfreq_governor_list); - - list_for_each_entry(devfreq, &devfreq_list, node) { - int ret = 0; - struct device *dev = devfreq->dev.parent; - - if (!strncmp(devfreq->governor_name, governor->name, - DEVFREQ_NAME_LEN)) { - /* The following should never occur */ - if (devfreq->governor) { - dev_warn(dev, - "%s: Governor %s already present\n", - __func__, devfreq->governor->name); - ret = devfreq->governor->event_handler(devfreq, - DEVFREQ_GOV_STOP, NULL); - if (ret) { - dev_warn(dev, - "%s: Governor %s stop = %d\n", - __func__, - devfreq->governor->name, ret); - } - /* Fall through */ - } - devfreq->governor = governor; - ret = devfreq->governor->event_handler(devfreq, - DEVFREQ_GOV_START, NULL); - if (ret) { - dev_warn(dev, "%s: Governor %s start=%d\n", - __func__, devfreq->governor->name, - ret); - } - } - } + class_for_each_device(devfreq_class, NULL, + governor, devfreq_add_governor_fn); err_out: mutex_unlock(&devfreq_list_lock); @@ -1196,6 +1187,32 @@ int devfreq_add_governor(struct devfreq_governor *governor) } EXPORT_SYMBOL(devfreq_add_governor); +static int devfreq_remove_governor_fn(struct device *dev, void *data) +{ + struct devfreq *devfreq = to_devfreq(dev); + struct devfreq_governor *governor = data; + int ret; + + if (strncmp(devfreq->governor_name, governor->name, DEVFREQ_NAME_LEN)) + return 0; + + /* we should have a devfreq governor! */ + if (!devfreq->governor) { + dev_warn(dev->parent, "%s: Governor %s NOT present\n", + __func__, governor->name); + return 0; + } + ret = devfreq->governor->event_handler(devfreq, + DEVFREQ_GOV_STOP, NULL); + if (ret) { + dev_warn(dev->parent, "%s: Governor %s stop=%d\n", + __func__, devfreq->governor->name, ret); + } + devfreq->governor = NULL; + + return 0; +} + /** * devfreq_remove_governor() - Remove devfreq feature from a device. * @governor: the devfreq governor to be removed @@ -1203,7 +1220,6 @@ EXPORT_SYMBOL(devfreq_add_governor); int devfreq_remove_governor(struct devfreq_governor *governor) { struct devfreq_governor *g; - struct devfreq *devfreq; int err = 0; if (!governor) { @@ -1219,31 +1235,10 @@ int devfreq_remove_governor(struct devfreq_governor *governor) err = PTR_ERR(g); goto err_out; } - list_for_each_entry(devfreq, &devfreq_list, node) { - int ret; - struct device *dev = devfreq->dev.parent; - - if (!strncmp(devfreq->governor_name, governor->name, - DEVFREQ_NAME_LEN)) { - /* we should have a devfreq governor! */ - if (!devfreq->governor) { - dev_warn(dev, "%s: Governor %s NOT present\n", - __func__, governor->name); - continue; - /* Fall through */ - } - ret = devfreq->governor->event_handler(devfreq, - DEVFREQ_GOV_STOP, NULL); - if (ret) { - dev_warn(dev, "%s: Governor %s stop=%d\n", - __func__, devfreq->governor->name, - ret); - } - devfreq->governor = NULL; - } - } - + class_for_each_device(devfreq_class, NULL, + governor, devfreq_remove_governor_fn); list_del(&governor->node); + err_out: mutex_unlock(&devfreq_list_lock); diff --git a/include/linux/devfreq.h b/include/linux/devfreq.h index 42cf5e4cce94..eb4c7f593ad2 100644 --- a/include/linux/devfreq.h +++ b/include/linux/devfreq.h @@ -109,8 +109,6 @@ struct devfreq_dev_profile { /** * struct devfreq - Device devfreq structure - * @node: list node - contains the devices with devfreq that have been - * registered. * @lock: a mutex to protect accessing devfreq. * @dev: device registered by devfreq class. dev.parent is the device * using devfreq. @@ -149,8 +147,6 @@ struct devfreq_dev_profile { * to protect its own private data in void *data as well. */ struct devfreq { - struct list_head node; - struct mutex lock; struct device dev; struct devfreq_dev_profile *profile; -- GitLab From a4477898f746814c5ef5732404ee39d903b06025 Mon Sep 17 00:00:00 2001 From: Angus Ainslie Date: Fri, 27 Sep 2019 06:47:41 -0700 Subject: [PATCH 47/71] arm64: config: librem5: enable devfreq Signed-off-by: Angus Ainslie --- arch/arm64/configs/librem5_defconfig | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/arch/arm64/configs/librem5_defconfig b/arch/arm64/configs/librem5_defconfig index 727c8067dc99..8cb6b4f61764 100644 --- a/arch/arm64/configs/librem5_defconfig +++ b/arch/arm64/configs/librem5_defconfig @@ -699,6 +699,10 @@ CONFIG_ARM_SMMU=y CONFIG_ARM_SMMU_V3=y CONFIG_RPMSG_VIRTIO=y CONFIG_PM_DEVFREQ=y +CONFIG_DEVFREQ_GOV_PERFORMANCE=m +CONFIG_DEVFREQ_GOV_POWERSAVE=y +CONFIG_ARM_IMX_DEVFREQ=m +CONFIG_PM_DEVFREQ_EVENT=y CONFIG_IIO=y CONFIG_IIO_BUFFER_CB=m CONFIG_IIO_BUFFER_HW_CONSUMER=m @@ -717,6 +721,8 @@ CONFIG_PHY_FSL_IMX8MQ_USB=m CONFIG_PHY_MIXEL_MIPI_DPHY=y CONFIG_POWERCAP=y CONFIG_NVMEM_IMX_OCOTP=y +CONFIG_INTERCONNECT=y +CONFIG_INTERCONNECT_IMX=y CONFIG_PHY_MIXEL_MIPI_DPHY=y CONFIG_VALIDATE_FS_PARSER=y CONFIG_EXT2_FS=y -- GitLab From 84275c92a06ea2c5522f6435ca48c45f7e8d2b6c Mon Sep 17 00:00:00 2001 From: Angus Ainslie Date: Fri, 27 Sep 2019 08:00:46 -0700 Subject: [PATCH 48/71] devfreq: imx-ddrc: add probe error messages output debug information on probe fail Signed-off-by: Angus Ainslie --- drivers/devfreq/imx-ddrc.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/devfreq/imx-ddrc.c b/drivers/devfreq/imx-ddrc.c index aa414bb980f3..f73c3076616e 100644 --- a/drivers/devfreq/imx-ddrc.c +++ b/drivers/devfreq/imx-ddrc.c @@ -377,8 +377,10 @@ static int imx_ddrc_init_freq_info(struct device *dev) 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) + if (priv->freq_count <= 0 || priv->freq_count > IMX_DDRC_MAX_FREQ_COUNT) { + dev_err(dev, "ATF doesn't support devfreq\n"); return -ENODEV; + } for (index = 0; index < priv->freq_count; ++index) { struct imx_ddrc_freq *freq = &priv->freq_table[index]; @@ -386,8 +388,10 @@ static int imx_ddrc_init_freq_info(struct device *dev) 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) + if ((long)res.a0 <= 0) { + dev_err(dev, "freq info less than 0\n"); return -ENODEV; + } freq->rate = res.a0 * 250000; freq->smcarg = index; @@ -397,16 +401,25 @@ static int imx_ddrc_init_freq_info(struct device *dev) /* dram_core has 2 options: dram_pll or dram_alt_root */ if (freq->dram_core_parent_index != 1 && - freq->dram_core_parent_index != 2) + freq->dram_core_parent_index != 2) { + dev_err(dev, "dram core doesn't have enough options %d\n", + freq->dram_core_parent_index ); 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) + freq->dram_apb_parent_index > 8) { + dev_err(dev, "dram core doesn't have enough options %d:%d\n", + freq->dram_alt_parent_index, freq->dram_alt_parent_index ); return -ENODEV; + } /* dram_core from alt requires explicit dram_alt parent */ if (freq->dram_core_parent_index == 2 && - freq->dram_alt_parent_index == 0) + freq->dram_alt_parent_index == 0) { + dev_err(dev, "dram_core requires dram_alt parent %d:%d\n", + freq->dram_core_parent_index, freq->dram_alt_parent_index ); return -ENODEV; + } } return 0; -- GitLab From f3f46d1365fe5e831e2a97bc67377fdc5f5933c8 Mon Sep 17 00:00:00 2001 From: Angus Ainslie Date: Fri, 27 Sep 2019 09:44:01 -0700 Subject: [PATCH 49/71] interconnect: imx: drop unused tag parameter Signed-off-by: Angus Ainslie --- drivers/interconnect/imx/imx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/interconnect/imx/imx.c b/drivers/interconnect/imx/imx.c index 0a04ac723c15..aea2e708094d 100644 --- a/drivers/interconnect/imx/imx.c +++ b/drivers/interconnect/imx/imx.c @@ -30,7 +30,7 @@ struct imx_icc_node { struct dev_pm_qos_request qos_req; }; -static int imx_icc_aggregate(struct icc_node *node, u32 tag, +static int imx_icc_aggregate(struct icc_node *node, u32 avg_bw, u32 peak_bw, u32 *agg_avg, u32 *agg_peak) { -- GitLab From 05c6087bcdd83f1dbfbbe9ff7c6ee41747256c07 Mon Sep 17 00:00:00 2001 From: Angus Ainslie Date: Sat, 28 Sep 2019 05:25:24 -0700 Subject: [PATCH 50/71] arm64: config: librem5: enable DEVFREQ_THERMAL Signed-off-by: Angus Ainslie (Purism) --- arch/arm64/configs/librem5_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm64/configs/librem5_defconfig b/arch/arm64/configs/librem5_defconfig index 8cb6b4f61764..865958f79861 100644 --- a/arch/arm64/configs/librem5_defconfig +++ b/arch/arm64/configs/librem5_defconfig @@ -363,6 +363,7 @@ CONFIG_THERMAL_GOV_POWER_ALLOCATOR=y CONFIG_CPU_THERMAL=y CONFIG_CPU_IDLE_THERMAL=y CONFIG_CLOCK_THERMAL=y +CONFIG_DEVFREQ_THERMAL=y CONFIG_THERMAL_EMULATION=y CONFIG_IMX_THERMAL=y CONFIG_QORIQ_THERMAL=y -- GitLab From 1835cee17719cd4632d81db9dc1d527f81756119 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Sat, 28 Sep 2019 10:34:55 -0700 Subject: [PATCH 51/71] arm64: configs: librem5_defconfig: build the imx_devfreq dirver into the kernel The imx_devfreq driver doesn't probe correctly unless its built into the kernel. The devfreq driver also needs the PMU drvier so eanble that Signed-off-by: Angus Ainslie (Purism) --- arch/arm64/configs/librem5_defconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm64/configs/librem5_defconfig b/arch/arm64/configs/librem5_defconfig index 865958f79861..c2305efd1c96 100644 --- a/arch/arm64/configs/librem5_defconfig +++ b/arch/arm64/configs/librem5_defconfig @@ -702,7 +702,7 @@ CONFIG_RPMSG_VIRTIO=y CONFIG_PM_DEVFREQ=y CONFIG_DEVFREQ_GOV_PERFORMANCE=m CONFIG_DEVFREQ_GOV_POWERSAVE=y -CONFIG_ARM_IMX_DEVFREQ=m +CONFIG_ARM_IMX_DEVFREQ=y CONFIG_PM_DEVFREQ_EVENT=y CONFIG_IIO=y CONFIG_IIO_BUFFER_CB=m @@ -721,6 +721,7 @@ CONFIG_PHY_XGENE=y CONFIG_PHY_FSL_IMX8MQ_USB=m CONFIG_PHY_MIXEL_MIPI_DPHY=y CONFIG_POWERCAP=y +CONFIG_FSL_IMX8_DDR_PMU=y CONFIG_NVMEM_IMX_OCOTP=y CONFIG_INTERCONNECT=y CONFIG_INTERCONNECT_IMX=y -- GitLab From 903dba5929ae00fd125a88819287f20b4e45a36d Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Sat, 28 Sep 2019 10:30:15 -0700 Subject: [PATCH 52/71] devfreq: HACK governor debugging Signed-off-by: Angus Ainslie (Purism) --- arch/arm64/configs/librem5_defconfig | 2 +- drivers/devfreq/devfreq.c | 9 +++++++-- drivers/devfreq/governor_passive.c | 6 ++++++ drivers/devfreq/imx-ddrc.c | 12 ++++++++++-- drivers/devfreq/imx-devfreq.c | 2 ++ 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/arch/arm64/configs/librem5_defconfig b/arch/arm64/configs/librem5_defconfig index c2305efd1c96..dfc1b4885de1 100644 --- a/arch/arm64/configs/librem5_defconfig +++ b/arch/arm64/configs/librem5_defconfig @@ -15,7 +15,7 @@ CONFIG_PSI=y CONFIG_PSI_DEFAULT_DISABLED=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y -CONFIG_LOG_BUF_SHIFT=18 +CONFIG_LOG_BUF_SHIFT=19 CONFIG_MEMCG=y CONFIG_MEMCG_SWAP=y CONFIG_BLK_CGROUP=y diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index 243c1ba38d89..c0c3fae2e5e5 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -392,16 +392,21 @@ int update_devfreq(struct devfreq *devfreq) if (!mutex_is_locked(&devfreq->lock)) { WARN(true, "devfreq->lock must be locked by the caller.\n"); + dev_err(&devfreq->dev, "devfreq->lock must be locked by the caller\n"); return -EINVAL; } - if (!devfreq->governor) + if (!devfreq->governor) { + dev_err(&devfreq->dev, "no governor\n"); return -EINVAL; + } /* Reevaluate the proper frequency */ err = devfreq->governor->get_target_freq(devfreq, &freq); - if (err) + if (err) { + dev_err(&devfreq->dev, "failed to get target freq %d\n", freq); return err; + } get_freq_range(devfreq, &min_freq, &max_freq); if (freq < min_freq) { diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c index 58308948b863..6fcd78298de6 100644 --- a/drivers/devfreq/governor_passive.c +++ b/drivers/devfreq/governor_passive.c @@ -49,6 +49,8 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq, */ if (!devfreq->profile || !devfreq->profile->freq_table || devfreq->profile->max_state <= 0) + dev_err(&devfreq->dev, + "No frequency table.\n"); return -EINVAL; /* @@ -59,6 +61,8 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq, opp = devfreq_recommended_opp(parent_devfreq->dev.parent, freq, 0); if (IS_ERR(opp)) { ret = PTR_ERR(opp); + dev_err(&devfreq->dev, + "Couldn't find recommended frequency.\n"); goto out; } @@ -74,6 +78,8 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq, if (i == parent_devfreq->profile->max_state) { ret = -EINVAL; + dev_err(&devfreq->dev, + "Reached max_state.\n"); goto out; } diff --git a/drivers/devfreq/imx-ddrc.c b/drivers/devfreq/imx-ddrc.c index f73c3076616e..41abef6a6aed 100644 --- a/drivers/devfreq/imx-ddrc.c +++ b/drivers/devfreq/imx-ddrc.c @@ -3,6 +3,8 @@ * Copyright 2019 NXP */ +#define DEBUG 1 + #include #include #include @@ -343,11 +345,17 @@ static int imx_ddrc_init_events(struct device *dev, * cast that to a struct pmu instead. */ priv->pmu_pdev = of_find_device_by_node(events_node); - if (!priv->pmu_pdev) + if (!priv->pmu_pdev) { + dev_warn(dev, "devfreq-events node %p not found\n", + events_node); return -EPROBE_DEFER; + } driver = priv->pmu_pdev->dev.driver; - if (!driver) + if (!driver) { + dev_warn(dev, "devfreq-events node %p driver not found\n", + events_node); 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); diff --git a/drivers/devfreq/imx-devfreq.c b/drivers/devfreq/imx-devfreq.c index 5c907b3ab9c0..7970e923b6b9 100644 --- a/drivers/devfreq/imx-devfreq.c +++ b/drivers/devfreq/imx-devfreq.c @@ -3,6 +3,8 @@ * Copyright 2019 NXP */ +#define DEBUG 1 + #include #include #include -- GitLab From 78a6b348ec37937f194f4076ff9bf4f89c24310d Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Wed, 9 Oct 2019 06:58:00 -0700 Subject: [PATCH 53/71] devfreq: use the correct size in the format string Get rid of compile time warning Signed-off-by: Angus Ainslie (Purism) --- drivers/devfreq/devfreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index c0c3fae2e5e5..a11ea533de7d 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -404,7 +404,7 @@ int update_devfreq(struct devfreq *devfreq) /* Reevaluate the proper frequency */ err = devfreq->governor->get_target_freq(devfreq, &freq); if (err) { - dev_err(&devfreq->dev, "failed to get target freq %d\n", freq); + dev_err(&devfreq->dev, "failed to get target freq %lu\n", freq); return err; } get_freq_range(devfreq, &min_freq, &max_freq); -- GitLab From 344584f82aa4952b1dc5fbb456cae3e4d8488561 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Thu, 10 Oct 2019 06:49:48 -0700 Subject: [PATCH 54/71] HACK: devfreq: ignore QoS setting for now The PM QoS is setting the top and bottom freq to 399 MHz so ignore it until we figure out why. Signed-off-by: Angus Ainslie (Purism) --- drivers/devfreq/devfreq.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index a11ea533de7d..c9380d1829ea 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -130,13 +130,21 @@ static void get_freq_range(struct devfreq *devfreq, *max_freq = freq_table[0]; } + pr_debug( "%s : min %lu max %lu\n", __func__, *min_freq, *max_freq ); + /* constraints from PM QoS */ qos_min_freq = dev_pm_qos_read_value(devfreq->dev.parent, DEV_PM_QOS_MIN_FREQUENCY); qos_max_freq = dev_pm_qos_read_value(devfreq->dev.parent, DEV_PM_QOS_MIN_FREQUENCY); - *min_freq = max(*min_freq, HZ_PER_KHZ * qos_min_freq); - *max_freq = min(*max_freq, HZ_PER_KHZ * qos_max_freq); + + pr_debug( "%s : qos min %lu max %lu\n", __func__, HZ_PER_KHZ * qos_min_freq, HZ_PER_KHZ * qos_max_freq ); + + /* if qos lies to us skip it for now */ + if (qos_min_freq != qos_max_freq) { + *min_freq = max(*min_freq, HZ_PER_KHZ * qos_min_freq); + *max_freq = min(*max_freq, HZ_PER_KHZ * qos_max_freq); + } /* constraints from OPP interface */ *min_freq = max(*min_freq, devfreq->scaling_min_freq); @@ -144,6 +152,8 @@ static void get_freq_range(struct devfreq *devfreq, if (devfreq->scaling_max_freq) *max_freq = min(*max_freq, devfreq->scaling_max_freq); + pr_debug( "%s : scaling min %lu max %lu\n", __func__, *min_freq, *max_freq ); + /* max_freq takes precedence over min_freq */ if (*min_freq > *max_freq) *min_freq = *max_freq; @@ -174,8 +184,10 @@ static int set_freq_table(struct devfreq *devfreq) /* Initialize the freq_table from OPP table */ count = dev_pm_opp_get_opp_count(devfreq->dev.parent); - if (count <= 0) + if (count <= 0) { + pr_err("No opp table\n"); return -EINVAL; + } profile->max_state = count; profile->freq_table = devm_kcalloc(devfreq->dev.parent, @@ -192,9 +204,11 @@ static int set_freq_table(struct devfreq *devfreq) if (IS_ERR(opp)) { devm_kfree(devfreq->dev.parent, profile->freq_table); profile->max_state = 0; + pr_err("Failed to find freq ceil %lu\n", freq); return PTR_ERR(opp); } dev_pm_opp_put(opp); + pr_err("Added freq %d %lu\n", i, freq); profile->freq_table[i] = freq; } @@ -357,6 +371,8 @@ static int devfreq_set_target(struct devfreq *devfreq, unsigned long new_freq, if (err) { freqs.new = cur_freq; devfreq_notify_transition(devfreq, &freqs, DEVFREQ_POSTCHANGE); + dev_err(&devfreq->dev, + "target new freq failed %lu.\n", new_freq); return err; } -- GitLab From 21057169d5ba2260509218b3dd33200f2378a79e Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Thu, 10 Oct 2019 06:51:59 -0700 Subject: [PATCH 55/71] arm64: dts: imx8mq: Add the 166MHz opp u-boot and the ATF limits B0 silicon to 800MHz and 166MHz Signed-off-by: Angus Ainslie (Purism) --- arch/arm64/boot/dts/freescale/imx8mq.dtsi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi b/arch/arm64/boot/dts/freescale/imx8mq.dtsi index 0281b7b7e3dd..6fa093abcaed 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi @@ -210,6 +210,9 @@ opp-25M { opp-100M { opp-hz = /bits/ 64 <100000000>; }; + opp-166M { + opp-hz = /bits/ 64 <166750000>; + }; opp-800M { opp-hz = /bits/ 64 <800000000>; }; -- GitLab From 268abcf713b34268dbc7b9af716875bfadff6abe Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Thu, 10 Oct 2019 07:10:56 -0700 Subject: [PATCH 56/71] HACK imc-ddrc: add some debugging information Signed-off-by: Angus Ainslie (Purism) --- drivers/devfreq/governor_simpleondemand.c | 8 +++++++- drivers/devfreq/imx-ddrc.c | 13 +++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index 3d809f228619..531e955df86f 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -38,12 +38,15 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, dfso_downdifferential = data->downdifferential; } if (dfso_upthreshold > 100 || - dfso_upthreshold < dfso_downdifferential) + dfso_upthreshold < dfso_downdifferential) { + pr_err("%s: thresholds failed %d %d \n", __func__, dfso_upthreshold, dfso_downdifferential ); return -EINVAL; + } /* Assume MAX if it is going to be divided by zero */ if (stat->total_time == 0) { *freq = DEVFREQ_MAX_FREQ; + pr_err("%s: setting max frequency\n", __func__ ); return 0; } @@ -57,12 +60,14 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, if (stat->busy_time * 100 > stat->total_time * dfso_upthreshold) { *freq = DEVFREQ_MAX_FREQ; + pr_err("%s: setting max frequency\n", __func__ ); return 0; } /* Set MAX if we do not know the initial frequency */ if (stat->current_frequency == 0) { *freq = DEVFREQ_MAX_FREQ; + pr_err("%s: setting max frequency\n", __func__ ); return 0; } @@ -70,6 +75,7 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, if (stat->busy_time * 100 > stat->total_time * (dfso_upthreshold - dfso_downdifferential)) { *freq = stat->current_frequency; + pr_err("%s: using current frequency\n", __func__ ); return 0; } diff --git a/drivers/devfreq/imx-ddrc.c b/drivers/devfreq/imx-ddrc.c index 41abef6a6aed..4eed6f50bb8d 100644 --- a/drivers/devfreq/imx-ddrc.c +++ b/drivers/devfreq/imx-ddrc.c @@ -106,9 +106,11 @@ static struct imx_ddrc_freq *imx_ddrc_find_freq(struct imx_ddrc *priv, { int i; - for (i = 0; i < priv->freq_count; ++i) + for (i = 0; i < priv->freq_count; ++i) { + pr_err("ddrc checking rate %lu %lu\n", priv->freq_table[i].rate, rate); if (priv->freq_table[i].rate == rate) return &priv->freq_table[i]; + } return NULL; } @@ -224,8 +226,13 @@ static int imx_ddrc_target(struct device *dev, unsigned long *freq, u32 flags) return 0; freq_info = imx_ddrc_find_freq(priv, new_freq); - if (!freq_info) + if (!freq_info) { + dev_err(dev, "ddrc failed to get freq info %lu\n", new_freq); return -EINVAL; + } + + dev_dbg(dev, "ddrc about to change freq %lu to %lu\n", cur_freq, new_freq); + ret = imx_ddrc_set_freq(dev, freq_info); if (ret) dev_err(dev, "ddrc failed to change freq %lu to %lu\n", @@ -428,6 +435,8 @@ static int imx_ddrc_init_freq_info(struct device *dev) freq->dram_core_parent_index, freq->dram_alt_parent_index ); return -ENODEV; } + + pr_err( "arm_smcc rate %d %lu\n", index, freq->rate ); } return 0; -- GitLab From 38567df5ca2968dc93baf5c510b7d76c91333491 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Wed, 16 Oct 2019 10:35:20 -0700 Subject: [PATCH 57/71] devfreq: imx-ddrc: busfreq imx8mq B0 HACKS Match freqency 166935483 and make imx_ddrc_find_freq more forgiving on finding frequencies Signed-off-by: Angus Ainslie (Purism) --- arch/arm64/boot/dts/freescale/imx8mq.dtsi | 2 +- drivers/clk/imx/clk-imx8mq.c | 2 +- drivers/devfreq/imx-ddrc.c | 16 +++++++++++++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi b/arch/arm64/boot/dts/freescale/imx8mq.dtsi index 6fa093abcaed..c1dd73033666 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi @@ -211,7 +211,7 @@ opp-100M { opp-hz = /bits/ 64 <100000000>; }; opp-166M { - opp-hz = /bits/ 64 <166750000>; + opp-hz = /bits/ 64 <166935483>; }; opp-800M { opp-hz = /bits/ 64 <800000000>; diff --git a/drivers/clk/imx/clk-imx8mq.c b/drivers/clk/imx/clk-imx8mq.c index 2813884f69c1..e5f50cf8a264 100644 --- a/drivers/clk/imx/clk-imx8mq.c +++ b/drivers/clk/imx/clk-imx8mq.c @@ -345,7 +345,7 @@ static int imx8mq_clocks_probe(struct platform_device *pdev) clks[IMX8MQ_SYS1_PLL_OUT] = imx_clk_sccg_pll("sys1_pll_out", sys1_pll_out_sels, ARRAY_SIZE(sys1_pll_out_sels), 0, 0, 0, base + 0x30, CLK_IS_CRITICAL); clks[IMX8MQ_SYS2_PLL_OUT] = imx_clk_sccg_pll("sys2_pll_out", sys2_pll_out_sels, ARRAY_SIZE(sys2_pll_out_sels), 0, 0, 1, base + 0x3c, CLK_IS_CRITICAL); 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); /* SYS PLL1 fixed output */ clks[IMX8MQ_SYS1_PLL_40M_CG] = imx_clk_gate("sys1_pll_40m_cg", "sys1_pll_out", base + 0x30, 9); diff --git a/drivers/devfreq/imx-ddrc.c b/drivers/devfreq/imx-ddrc.c index 4eed6f50bb8d..f02c46b8845f 100644 --- a/drivers/devfreq/imx-ddrc.c +++ b/drivers/devfreq/imx-ddrc.c @@ -106,9 +106,17 @@ static struct imx_ddrc_freq *imx_ddrc_find_freq(struct imx_ddrc *priv, { 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) { - pr_err("ddrc checking rate %lu %lu\n", priv->freq_table[i].rate, rate); - if (priv->freq_table[i].rate == rate) + struct imx_ddrc_freq *freq = &priv->freq_table[i]; + unsigned long match_rate = DIV_ROUND_CLOSEST(freq->rate,250000); + pr_err("ddrc checking rate %lu %lu\n", match_rate, rate); + if (match_rate + 1 >= rate && + match_rate - 1 <= rate) return &priv->freq_table[i]; } @@ -190,7 +198,9 @@ static int imx_ddrc_set_freq(struct device *dev, struct imx_ddrc_freq *freq) if (ret) dev_err(dev, "failed set dram_apb parent: %d\n", ret); } - + + clk_get_rate(new_dram_core_parent); + /* * clk_set_parent transfer the reference count from old parent. * now we drop extra reference counts used during the switch -- GitLab From cff0f070fdac1ce42d094aae49333d7fd8a36795 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Wed, 16 Oct 2019 10:41:52 -0700 Subject: [PATCH 58/71] Revert "HACK imc-ddrc: add some debugging information" This reverts commit 268abcf713b34268dbc7b9af716875bfadff6abe. --- drivers/devfreq/governor_simpleondemand.c | 8 +------- drivers/devfreq/imx-ddrc.c | 10 +--------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/drivers/devfreq/governor_simpleondemand.c b/drivers/devfreq/governor_simpleondemand.c index 531e955df86f..3d809f228619 100644 --- a/drivers/devfreq/governor_simpleondemand.c +++ b/drivers/devfreq/governor_simpleondemand.c @@ -38,15 +38,12 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, dfso_downdifferential = data->downdifferential; } if (dfso_upthreshold > 100 || - dfso_upthreshold < dfso_downdifferential) { - pr_err("%s: thresholds failed %d %d \n", __func__, dfso_upthreshold, dfso_downdifferential ); + dfso_upthreshold < dfso_downdifferential) return -EINVAL; - } /* Assume MAX if it is going to be divided by zero */ if (stat->total_time == 0) { *freq = DEVFREQ_MAX_FREQ; - pr_err("%s: setting max frequency\n", __func__ ); return 0; } @@ -60,14 +57,12 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, if (stat->busy_time * 100 > stat->total_time * dfso_upthreshold) { *freq = DEVFREQ_MAX_FREQ; - pr_err("%s: setting max frequency\n", __func__ ); return 0; } /* Set MAX if we do not know the initial frequency */ if (stat->current_frequency == 0) { *freq = DEVFREQ_MAX_FREQ; - pr_err("%s: setting max frequency\n", __func__ ); return 0; } @@ -75,7 +70,6 @@ static int devfreq_simple_ondemand_func(struct devfreq *df, if (stat->busy_time * 100 > stat->total_time * (dfso_upthreshold - dfso_downdifferential)) { *freq = stat->current_frequency; - pr_err("%s: using current frequency\n", __func__ ); return 0; } diff --git a/drivers/devfreq/imx-ddrc.c b/drivers/devfreq/imx-ddrc.c index f02c46b8845f..c80b837184ec 100644 --- a/drivers/devfreq/imx-ddrc.c +++ b/drivers/devfreq/imx-ddrc.c @@ -114,7 +114,6 @@ static struct imx_ddrc_freq *imx_ddrc_find_freq(struct imx_ddrc *priv, for (i = 0; i < priv->freq_count; ++i) { struct imx_ddrc_freq *freq = &priv->freq_table[i]; unsigned long match_rate = DIV_ROUND_CLOSEST(freq->rate,250000); - pr_err("ddrc checking rate %lu %lu\n", match_rate, rate); if (match_rate + 1 >= rate && match_rate - 1 <= rate) return &priv->freq_table[i]; @@ -236,13 +235,8 @@ static int imx_ddrc_target(struct device *dev, unsigned long *freq, u32 flags) return 0; freq_info = imx_ddrc_find_freq(priv, new_freq); - if (!freq_info) { - dev_err(dev, "ddrc failed to get freq info %lu\n", new_freq); + if (!freq_info) return -EINVAL; - } - - dev_dbg(dev, "ddrc about to change freq %lu to %lu\n", cur_freq, new_freq); - ret = imx_ddrc_set_freq(dev, freq_info); if (ret) dev_err(dev, "ddrc failed to change freq %lu to %lu\n", @@ -445,8 +439,6 @@ static int imx_ddrc_init_freq_info(struct device *dev) freq->dram_core_parent_index, freq->dram_alt_parent_index ); return -ENODEV; } - - pr_err( "arm_smcc rate %d %lu\n", index, freq->rate ); } return 0; -- GitLab From 1b453c6f6cf796169387826144150c618a838bea Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Wed, 16 Oct 2019 11:59:56 -0700 Subject: [PATCH 59/71] Revert "devfreq: HACK governor debugging" This reverts commit 903dba5929ae00fd125a88819287f20b4e45a36d. --- arch/arm64/configs/librem5_defconfig | 2 +- drivers/devfreq/devfreq.c | 9 ++------- drivers/devfreq/governor_passive.c | 6 ------ drivers/devfreq/imx-ddrc.c | 12 ++---------- drivers/devfreq/imx-devfreq.c | 2 -- 5 files changed, 5 insertions(+), 26 deletions(-) diff --git a/arch/arm64/configs/librem5_defconfig b/arch/arm64/configs/librem5_defconfig index dfc1b4885de1..c2305efd1c96 100644 --- a/arch/arm64/configs/librem5_defconfig +++ b/arch/arm64/configs/librem5_defconfig @@ -15,7 +15,7 @@ CONFIG_PSI=y CONFIG_PSI_DEFAULT_DISABLED=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y -CONFIG_LOG_BUF_SHIFT=19 +CONFIG_LOG_BUF_SHIFT=18 CONFIG_MEMCG=y CONFIG_MEMCG_SWAP=y CONFIG_BLK_CGROUP=y diff --git a/drivers/devfreq/devfreq.c b/drivers/devfreq/devfreq.c index c9380d1829ea..c240c3bf9af2 100644 --- a/drivers/devfreq/devfreq.c +++ b/drivers/devfreq/devfreq.c @@ -408,21 +408,16 @@ int update_devfreq(struct devfreq *devfreq) if (!mutex_is_locked(&devfreq->lock)) { WARN(true, "devfreq->lock must be locked by the caller.\n"); - dev_err(&devfreq->dev, "devfreq->lock must be locked by the caller\n"); return -EINVAL; } - if (!devfreq->governor) { - dev_err(&devfreq->dev, "no governor\n"); + if (!devfreq->governor) return -EINVAL; - } /* Reevaluate the proper frequency */ err = devfreq->governor->get_target_freq(devfreq, &freq); - if (err) { - dev_err(&devfreq->dev, "failed to get target freq %lu\n", freq); + if (err) return err; - } get_freq_range(devfreq, &min_freq, &max_freq); if (freq < min_freq) { diff --git a/drivers/devfreq/governor_passive.c b/drivers/devfreq/governor_passive.c index 6fcd78298de6..58308948b863 100644 --- a/drivers/devfreq/governor_passive.c +++ b/drivers/devfreq/governor_passive.c @@ -49,8 +49,6 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq, */ if (!devfreq->profile || !devfreq->profile->freq_table || devfreq->profile->max_state <= 0) - dev_err(&devfreq->dev, - "No frequency table.\n"); return -EINVAL; /* @@ -61,8 +59,6 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq, opp = devfreq_recommended_opp(parent_devfreq->dev.parent, freq, 0); if (IS_ERR(opp)) { ret = PTR_ERR(opp); - dev_err(&devfreq->dev, - "Couldn't find recommended frequency.\n"); goto out; } @@ -78,8 +74,6 @@ static int devfreq_passive_get_target_freq(struct devfreq *devfreq, if (i == parent_devfreq->profile->max_state) { ret = -EINVAL; - dev_err(&devfreq->dev, - "Reached max_state.\n"); goto out; } diff --git a/drivers/devfreq/imx-ddrc.c b/drivers/devfreq/imx-ddrc.c index c80b837184ec..ad6d35013cd0 100644 --- a/drivers/devfreq/imx-ddrc.c +++ b/drivers/devfreq/imx-ddrc.c @@ -3,8 +3,6 @@ * Copyright 2019 NXP */ -#define DEBUG 1 - #include #include #include @@ -356,17 +354,11 @@ static int imx_ddrc_init_events(struct device *dev, * cast that to a struct pmu instead. */ priv->pmu_pdev = of_find_device_by_node(events_node); - if (!priv->pmu_pdev) { - dev_warn(dev, "devfreq-events node %p not found\n", - events_node); + if (!priv->pmu_pdev) return -EPROBE_DEFER; - } driver = priv->pmu_pdev->dev.driver; - if (!driver) { - dev_warn(dev, "devfreq-events node %p driver not found\n", - events_node); + 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); diff --git a/drivers/devfreq/imx-devfreq.c b/drivers/devfreq/imx-devfreq.c index 7970e923b6b9..5c907b3ab9c0 100644 --- a/drivers/devfreq/imx-devfreq.c +++ b/drivers/devfreq/imx-devfreq.c @@ -3,8 +3,6 @@ * Copyright 2019 NXP */ -#define DEBUG 1 - #include #include #include -- GitLab From 99f68ad84ce7263ed6db1093d2849338096449af Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Wed, 23 Oct 2019 08:57:14 -0700 Subject: [PATCH 60/71] power: bq25890_charger: when ocv table is missing return ENODEV If the monitored battery does not supply an OCV table and the driver returns EINVAL when userspace reads the capacity value we get an error message on the console. power_supply bq25890-charger: driver failed to report `capacity' property: -22 As supplying the OCV table is optional we should not be printing an error. If we return ENODEV the error message gets suppressed by power_supply_core.c Signed-off-by: Angus Ainslie (Purism) --- drivers/power/supply/bq25890_charger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/power/supply/bq25890_charger.c b/drivers/power/supply/bq25890_charger.c index c694f04f1019..c1eb65c5299d 100644 --- a/drivers/power/supply/bq25890_charger.c +++ b/drivers/power/supply/bq25890_charger.c @@ -474,7 +474,7 @@ static int bq25890_power_supply_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_CAPACITY: if (!bq->cap_table) - return -EINVAL; + return -ENODEV; ret = bq25890_field_read(bq, F_SYSV); /* read measured value */ if (ret < 0) -- GitLab From 33c924f6b80f83b9c08bb0908ef2451874d950c0 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Thu, 17 Oct 2019 04:47:03 -0700 Subject: [PATCH 61/71] arm64: dts: librem5: disable RS9116 WOW until redpine gets it working Signed-off-by: Angus Ainslie (Purism) --- arch/arm64/configs/librem5_defconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm64/configs/librem5_defconfig b/arch/arm64/configs/librem5_defconfig index c2305efd1c96..3a8227973964 100644 --- a/arch/arm64/configs/librem5_defconfig +++ b/arch/arm64/configs/librem5_defconfig @@ -274,7 +274,7 @@ CONFIG_REDPINE_91X=m # CONFIG_REDPINE_USB is not set CONFIG_REDPINE_BT_ALONE=y CONFIG_REDPINE_COEX_MODE=y -CONFIG_REDPINE_WOW=y +# CONFIG_REDPINE_WOW is not set CONFIG_REDPINE_HW_SCAN_OFFLOAD=y CONFIG_REDPINE_PURISM=y # CONFIG_WLAN_VENDOR_ST is not set -- GitLab From 9f35f058758b3d56d3afe704ddf98e00cc8d6f21 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Thu, 17 Oct 2019 06:15:47 -0700 Subject: [PATCH 62/71] power: supply: max17042: add code to support the MAX17055 The MAX17055 is very similar to the MAX17042 so extend the driver. Signed-off-by: Angus Ainslie (Purism) --- drivers/power/supply/max17042_battery.c | 19 ++++++++-- include/linux/power/max17042_battery.h | 48 ++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 4 deletions(-) diff --git a/drivers/power/supply/max17042_battery.c b/drivers/power/supply/max17042_battery.c index 64f3358eaa3c..fa34f67ba830 100644 --- a/drivers/power/supply/max17042_battery.c +++ b/drivers/power/supply/max17042_battery.c @@ -282,6 +282,8 @@ static int max17042_get_property(struct power_supply *psy, case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) ret = regmap_read(map, MAX17042_V_empty, &data); + else if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) + ret = regmap_read(map, MAX17055_V_empty, &data); else ret = regmap_read(map, MAX17047_V_empty, &data); if (ret < 0) @@ -627,7 +629,8 @@ static void max17042_write_config_regs(struct max17042_chip *chip) config->filter_cfg); regmap_write(map, MAX17042_RelaxCFG, config->relax_cfg); if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17047 || - chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050) + chip->chip_type == MAXIM_DEVICE_TYPE_MAX17050 || + chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) regmap_write(map, MAX17047_FullSOCThr, config->full_soc_thresh); } @@ -673,6 +676,10 @@ static void max17042_reset_vfsoc0_reg(struct max17042_chip *chip) unsigned int vfSoc; struct regmap *map = chip->regmap; + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) { + dev_warn(&chip->client->dev, "%s untested on MAX17055\n", __func__); + } + regmap_read(map, MAX17042_VFSOC, &vfSoc); regmap_write(map, MAX17042_VFSOC0Enable, VFSOC0_UNLOCK); max17042_write_verify_reg(map, MAX17042_VFSOC0, vfSoc); @@ -758,6 +765,8 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17042) max17042_override_por(map, MAX17042_V_empty, config->vempty); + if (chip->chip_type == MAXIM_DEVICE_TYPE_MAX17055) + max17042_override_por(map, MAX17055_V_empty, config->vempty); else max17042_override_por(map, MAX17047_V_empty, config->vempty); max17042_override_por(map, MAX17042_TempNom, config->temp_nom); @@ -765,7 +774,8 @@ static inline void max17042_override_por_values(struct max17042_chip *chip) max17042_override_por(map, MAX17042_FCTC, config->fctc); max17042_override_por(map, MAX17042_RCOMP0, config->rcomp0); max17042_override_por(map, MAX17042_TempCo, config->tcompc0); - if (chip->chip_type) { + if (chip->chip_type && + (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055)) { max17042_override_por(map, MAX17042_EmptyTempCo, config->empty_tempco); max17042_override_por(map, MAX17042_K_empty0, @@ -929,7 +939,8 @@ max17042_get_default_pdata(struct max17042_chip *chip) if (!pdata) return pdata; - if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17042) { + if (chip->chip_type != MAXIM_DEVICE_TYPE_MAX17042 && + chip->chip_type != MAXIM_DEVICE_TYPE_MAX17055) { pdata->init_data = max17047_default_pdata_init_regs; pdata->num_init_data = ARRAY_SIZE(max17047_default_pdata_init_regs); @@ -1167,6 +1178,7 @@ static const struct of_device_id max17042_dt_match[] = { { .compatible = "maxim,max17042" }, { .compatible = "maxim,max17047" }, { .compatible = "maxim,max17050" }, + { .compatible = "maxim,max17055" }, { }, }; MODULE_DEVICE_TABLE(of, max17042_dt_match); @@ -1176,6 +1188,7 @@ static const struct i2c_device_id max17042_id[] = { { "max17042", MAXIM_DEVICE_TYPE_MAX17042 }, { "max17047", MAXIM_DEVICE_TYPE_MAX17047 }, { "max17050", MAXIM_DEVICE_TYPE_MAX17050 }, + { "max17055", MAXIM_DEVICE_TYPE_MAX17055 }, { } }; MODULE_DEVICE_TABLE(i2c, max17042_id); diff --git a/include/linux/power/max17042_battery.h b/include/linux/power/max17042_battery.h index 4badd5322949..d55c746ac56e 100644 --- a/include/linux/power/max17042_battery.h +++ b/include/linux/power/max17042_battery.h @@ -105,11 +105,56 @@ enum max17042_register { MAX17042_OCV = 0xEE, - MAX17042_OCVInternal = 0xFB, + MAX17042_OCVInternal = 0xFB, /* MAX17055 VFOCV */ MAX17042_VFSOC = 0xFF, }; +enum max17055_register { + MAX17055_QRes = 0x0C, + MAX17055_TTF = 0x20, + MAX17055_V_empty = 0x3A, + MAX17055_TIMER = 0x3E, + MAX17055_USER_MEM = 0x40, + MAX17055_RGAIN = 0x42, + + MAX17055_ConvgCfg = 0x49, + MAX17055_VFRemCap = 0x4A, + + MAX17055_STATUS2 = 0xB0, + MAX17055_POWER = 0xB1, + MAX17055_ID = 0xB2, + MAX17055_AvgPower = 0xB3, + MAX17055_IAlrtTh = 0xB4, + MAX17055_TTFCfg = 0xB5, + MAX17055_CVMixCap = 0xB6, + MAX17055_CVHalfTime = 0xB7, + MAX17055_CGTempCo = 0xB8, + MAX17055_Curve = 0xB9, + MAX17055_HibCfg = 0xBA, + MAX17055_Config2 = 0xBB, + MAX17055_VRipple = 0xBC, + MAX17055_RippleCfg = 0xBD, + MAX17055_TimerH = 0xBE, + + MAX17055_RSense = 0xD0, + MAX17055_ScOcvLim = 0xD1, + + MAX17055_SOCHold = 0xD3, + MAX17055_MaxPeakPwr = 0xD4, + MAX17055_SusPeakPwr = 0xD5, + MAX17055_PackResistance = 0xD6, + MAX17055_SysResistance = 0xD7, + MAX17055_MinSysV = 0xD8, + MAX17055_MPPCurrent = 0xD9, + MAX17055_SPPCurrent = 0xDA, + MAX17055_ModelCfg = 0xDB, + MAX17055_AtQResidual = 0xDC, + MAX17055_AtTTE = 0xDD, + MAX17055_AtAvSOC = 0xDE, + MAX17055_AtAvCap = 0xDF, +}; + /* Registers specific to max17047/50 */ enum max17047_register { MAX17047_QRTbl00 = 0x12, @@ -125,6 +170,7 @@ enum max170xx_chip_type { MAXIM_DEVICE_TYPE_MAX17042, MAXIM_DEVICE_TYPE_MAX17047, MAXIM_DEVICE_TYPE_MAX17050, + MAXIM_DEVICE_TYPE_MAX17055, MAXIM_DEVICE_TYPE_NUM }; -- GitLab From d1582e7fec1b03702131385a3959e1ffde54aa98 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Thu, 17 Oct 2019 06:17:27 -0700 Subject: [PATCH 63/71] arm64: dts: librem5: enable the fuel gauge Enable the MAX17055 driver Signed-off-by: Angus Ainslie (Purism) --- arch/arm64/boot/dts/freescale/imx8mq-librem5.dts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts index eca9305edfab..86209d8ff1c0 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts +++ b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts @@ -789,8 +789,7 @@ camera2@2d { /* just a placeholder for nuw */ }; bat: fuel_gauge@36 { - // We have a ,max17055 which is very different - compatible = "maxim,max17050"; + compatible = "maxim,max17055"; interrupt-parent = <&gpio3>; interrupts = <20 IRQ_TYPE_LEVEL_LOW>; pinctrl-names = "default"; @@ -798,7 +797,7 @@ bat: fuel_gauge@36 { reg = <0x36>; maxim,over-heat-temp = <700>; maxim,over-volt = <4500>; - //maxim,rsns-microohm = <5000>; + maxim,rsns-microohm = <5000>; }; charger@6a { /* bq25895 */ @@ -817,7 +816,7 @@ charger@6a { /* bq25895 */ ti,boost-max-current = <50000>; /* 50mA */ ti,use-vinmin-threshold = <1>; /* enable VINDPM */ ti,vinmin-threshold = <3900000>; /* 3.9V */ - //monitored-battery = <&bat>; + monitored-battery = <&bat>; }; }; -- GitLab From a60133f128d5327bf93144275aea5f72f65ad946 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Wed, 23 Oct 2019 11:57:08 -0700 Subject: [PATCH 64/71] arm64: dts: librem5-devkit: get rid of change freq warning Delete the opp entries that can't be reached on the devkit Signed-off-by: Angus Ainslie (Purism) --- arch/arm64/boot/dts/freescale/imx8mq-librem5-devkit.dts | 8 ++++++++ arch/arm64/boot/dts/freescale/imx8mq.dtsi | 3 --- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mq-librem5-devkit.dts b/arch/arm64/boot/dts/freescale/imx8mq-librem5-devkit.dts index 52bd7ecbe5d9..c3ba1562b07d 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq-librem5-devkit.dts +++ b/arch/arm64/boot/dts/freescale/imx8mq-librem5-devkit.dts @@ -286,6 +286,14 @@ &clk { assigned-clock-rates = <786432000>, <722534400>; }; +&ddrc_opp_table { + /delete-node/ opp-25M; + /delete-node/ opp-100M; + opp-166M { + opp-hz = /bits/ 64 <166935483>; + }; +}; + &fec1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_fec1>; diff --git a/arch/arm64/boot/dts/freescale/imx8mq.dtsi b/arch/arm64/boot/dts/freescale/imx8mq.dtsi index c1dd73033666..0281b7b7e3dd 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq.dtsi +++ b/arch/arm64/boot/dts/freescale/imx8mq.dtsi @@ -210,9 +210,6 @@ opp-25M { opp-100M { opp-hz = /bits/ 64 <100000000>; }; - opp-166M { - opp-hz = /bits/ 64 <166935483>; - }; opp-800M { opp-hz = /bits/ 64 <800000000>; }; -- GitLab From 462624d7d6f02cbf1ee04d4d1e68b1151a39700b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 12 Sep 2019 18:19:04 -0700 Subject: [PATCH 65/71] dts: sound: wm8962: Document clock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The binding accepts a single phandle as the devices master clock (mclk) Signed-off-by: Guido Günther --- Documentation/devicetree/bindings/sound/wm8962.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/sound/wm8962.txt b/Documentation/devicetree/bindings/sound/wm8962.txt index dcfa9a3369fd..bc26127e2a81 100644 --- a/Documentation/devicetree/bindings/sound/wm8962.txt +++ b/Documentation/devicetree/bindings/sound/wm8962.txt @@ -22,6 +22,8 @@ Optional properties: Any other value is regarded as setting the GPIO register by its reset value 0x0. + - clocks: a single phandle to the master clock (mclk) + Example: wm8962: codec@1a { -- GitLab From 04301f84a86de0ec96e1cba9008ec18cde6d6435 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Wed, 13 Nov 2019 09:43:14 -0800 Subject: [PATCH 66/71] arm64: dts: freescale: librem5: add some aspen switches Aspen uses a different TPS reset line Update the flash stanza Signed-off-by: Angus Ainslie (Purism) --- .../boot/dts/freescale/imx8mq-librem5.dts | 37 +++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts index 86209d8ff1c0..7910b9b02834 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts +++ b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts @@ -12,6 +12,7 @@ #define GPIO_CONTROL #define AUDIO_REGULATOR +#define LIBREM5_ASPEN / { model = "Purism Librem 5"; @@ -111,6 +112,9 @@ leds { #if !defined( AUDIO_REGULATOR ) ,<&pinctrl_audiopwr> #endif +#if defined( LIBREM5_ASPEN ) + ,<&pinctrl_nfc> +#endif #endif ; @@ -153,6 +157,13 @@ sd_en { default-state = "on"; }; +#ifdef LIBREM5_ASPEN + tps65982_reset { + label = "tps65982_reset"; + gpios = <&gpio4 28 GPIO_ACTIVE_HIGH>; + default-state = "off"; + }; +#endif #ifdef GPIO_CONTROL gps_nreset { label = "gps_nreset"; @@ -421,14 +432,24 @@ &clk { assigned-clock-rates = <786432000>, <722534400>; }; +#ifdef LIBREM5_ASPEN +&ddrc_opp_table { + /delete-node/ opp-25M; + /delete-node/ opp-100M; + opp-166M { + opp-hz = /bits/ 64 <166935483>; + }; +}; +#endif + &gpio1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_pmic_5v>; pmic_5v { gpio-hog; - gpio = <1 GPIO_ACTIVE_HIGH>; - output-high; + gpio = <&gpio1 1 GPIO_ACTIVE_HIGH>; + input; }; }; @@ -582,7 +603,7 @@ ldo7_reg: LDO7 { }; typec_pd: usb_pd@3f { - compatible = "tps6598x"; + compatible = "ti,tps6598x"; reg = <0x3f>; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_typec>, <&pinctrl_tcpc>; @@ -1290,22 +1311,16 @@ MX8MQ_IOMUXC_ENET_RD1_GPIO1_IO27 0x83 /* TP_INT */ &ecspi1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_ecspi1>; - cs-gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>, <&gpio5 28 GPIO_ACTIVE_HIGH>; + cs-gpios = <&gpio5 9 GPIO_ACTIVE_HIGH>; status = "okay"; #address-cells = <1>; #size-cells = <0>; nor_flash: flash@0 { compatible = "jedec,spi-nor"; - spi-max-frequency = <20000000>; + spi-max-frequency = <1000000>; reg = <0>; }; - - ext_flash: flash@1 { - compatible = "jedec,spi-nor"; - spi-max-frequency = <20000000>; - reg = <1>; - }; }; &pgc_gpu { -- GitLab From db8b966f23fd7981e4bbbb667f30aa4c4100eb91 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Wed, 13 Nov 2019 09:50:03 -0800 Subject: [PATCH 67/71] usb: typec: tps6598x: add device tree hooks Signed-off-by: Angus Ainslie (Purism) --- drivers/usb/typec/tps6598x.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/usb/typec/tps6598x.c b/drivers/usb/typec/tps6598x.c index a38d1409f15b..1cda3f396252 100644 --- a/drivers/usb/typec/tps6598x.c +++ b/drivers/usb/typec/tps6598x.c @@ -566,9 +566,16 @@ static const struct i2c_device_id tps6598x_id[] = { }; MODULE_DEVICE_TABLE(i2c, tps6598x_id); +static const struct of_device_id tps6598x_of_match[] = { + { .compatible = "ti,tps6598x", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tps6598x_of_match); + static struct i2c_driver tps6598x_i2c_driver = { .driver = { .name = "tps6598x", + .of_match_table = of_match_ptr(tps6598x_of_match), }, .probe_new = tps6598x_probe, .remove = tps6598x_remove, -- GitLab From 89d52a24b3b54484c9538db26c6455350ddb2b00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 12 Sep 2019 12:44:02 -0700 Subject: [PATCH 68/71] dts: librem5: Add simple sound card MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Guido Günther --- .../boot/dts/freescale/imx8mq-librem5.dts | 52 ++++++++++++++++--- 1 file changed, 46 insertions(+), 6 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts index 7910b9b02834..f94f7c3e517b 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts +++ b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts @@ -419,6 +419,37 @@ rfkill_wwan { #endif + sound { + compatible = "simple-audio-card"; + simple-audio-card,name = "wm8962"; + simple-audio-card,format = "i2s"; + simple-audio-card,widgets = + "Headphone", "Headphone Jack"; +#if 0 + "Microphone", "Microphone Jack", + "Speaker", "Speaker Ext", + "Line", "Line In Jack"; +#endif + simple-audio-card,routing = + /* sink - source pairs */ + "Headphone Jack", "HPOUT"; + #if 0 + "MIC_IN", "Microphone Jack", + "Microphone Jack", "Mic Bias", + "LINE_IN", "Line In Jack", + "Speaker Ext", "LINE_OUT"; + #endif + simple-audio-card,cpu { + sound-dai = <&sai2>; + }; + simple-audio-card,codec { + sound-dai = <&codec>; + clocks = <&clk IMX8MQ_CLK_SAI2_ROOT>; + frame-master; + bitclock-master; + }; + }; + vibrator { compatible = "pwm-vibrator"; pwms = <&pwm1 0 1000000000 0>; @@ -769,12 +800,12 @@ codec: wm8962@1a { SPKVDD2-supply = <®_vsys_3v4>; #endif gpio-cfg = < - 0x0000 /* 0:Default */ - 0x0000 /* 1:Default */ - 0x0000 /* 2:Default */ - 0x0000 /* 3:Default */ - 0x0000 /* 4:Default */ - 0x0000 /* 5:Default */ + 0x0000 /* n/c */ + 0x0000 /* gpio2, 1: default */ + 0x0000 /* gpio3, 2: todo: dmicclk, 1_0011 */ + 0x0000 /* n/c, 3: default */ + 0x0000 /* gpio5, 4: default */ + 0x0000 /* gpio6, 5: dmic_dat, 1_0100 */ >; status = "okay"; }; @@ -1359,6 +1390,15 @@ &pwm4 { status = "okay"; }; +&sai2 { + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_sai2>; + assigned-clocks = <&clk IMX8MQ_CLK_SAI2>; + assigned-clock-parents = <&clk IMX8MQ_AUDIO_PLL1_OUT>; + assigned-clock-rates = <24576000>; + status = "okay"; +}; + &snvs_pwrkey { status = "okay"; }; -- GitLab From 51f562f0d06452ba7f866ddb9aea34cf4222fd83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guido=20G=C3=BCnther?= Date: Thu, 12 Sep 2019 12:38:28 -0700 Subject: [PATCH 69/71] wm8962: delay probe due to i2c not being ready MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit another device on i2c2 that times out during first probe need to investigate why the imx8mq's i2c controller is not ready although it claims to be - missing clock? Signed-off-by: Guido Günther --- sound/soc/codecs/wm8962.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 3e5c69fbc33a..73eed57aa682 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -35,6 +35,9 @@ #include "wm8962.h" +static int defers; +#define MAX_DEFERS 1 + #define WM8962_NUM_SUPPLIES 8 static const char *wm8962_supply_names[WM8962_NUM_SUPPLIES] = { "DCVDD", @@ -3538,6 +3541,12 @@ static int wm8962_i2c_probe(struct i2c_client *i2c, unsigned int reg; int ret, i, irq_pol, trigger; + if (defers < MAX_DEFERS) { + printk("%s: %d: defering probe", __func__, __LINE__); + defers++; + return -EPROBE_DEFER; + } + wm8962 = devm_kzalloc(&i2c->dev, sizeof(*wm8962), GFP_KERNEL); if (wm8962 == NULL) return -ENOMEM; -- GitLab From 53047dd350f26f36f08b80e18baf5cd2017d0aa6 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Wed, 13 Nov 2019 13:07:56 -0800 Subject: [PATCH 70/71] arm64: dts: frescale: librem5: configure the codec clock for 44100 Statically configure the wm8962 codec clock for 44100 kHz sample rate Signed-off-by: Angus Ainslie (Purism) --- arch/arm64/boot/dts/freescale/imx8mq-librem5.dts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts index f94f7c3e517b..9a2f9f9dffa5 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts +++ b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts @@ -788,6 +788,10 @@ codec: wm8962@1a { compatible = "wlf,wm8962"; reg = <0x1a>; // 0x4a is the test address clocks = <&clk IMX8MQ_CLK_SAI2_ROOT>; + assigned-clocks = <&clk IMX8MQ_CLK_SAI2>; + assigned-clock-parents = <&clk IMX8MQ_AUDIO_PLL2_OUT>; + assigned-clock-rates = <22579200>; + //assigned-clock-rates = <24576000>; #sound-dai-cells = <0>; #if defined( AUDIO_REGULATOR ) DCVDD-supply = <®_aud_1v8>; -- GitLab From 8908ba21ffeb3d7795c9c1137cf7d1efb4422525 Mon Sep 17 00:00:00 2001 From: "Angus Ainslie (Purism)" Date: Fri, 15 Nov 2019 12:08:55 -0800 Subject: [PATCH 71/71] arm64: dts: librem5: add digital mic routing Enable the digital mics Signed-off-by: Angus Ainslie (Purism) --- .../boot/dts/freescale/imx8mq-librem5.dts | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts index 9a2f9f9dffa5..12886faccb1d 100644 --- a/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts +++ b/arch/arm64/boot/dts/freescale/imx8mq-librem5.dts @@ -424,21 +424,18 @@ sound { simple-audio-card,name = "wm8962"; simple-audio-card,format = "i2s"; simple-audio-card,widgets = - "Headphone", "Headphone Jack"; -#if 0 - "Microphone", "Microphone Jack", - "Speaker", "Speaker Ext", - "Line", "Line In Jack"; -#endif + "Headphone", "Headphone", + "Microphone", "Headset Mic", + "Microphone", "Digital Mic", + "Speaker", "Speaker"; simple-audio-card,routing = - /* sink - source pairs */ - "Headphone Jack", "HPOUT"; - #if 0 - "MIC_IN", "Microphone Jack", - "Microphone Jack", "Mic Bias", - "LINE_IN", "Line In Jack", - "Speaker Ext", "LINE_OUT"; - #endif + "Headphone", "HPOUTL", + "Headphone", "HPOUTR", + "Speaker", "SPKOUTL", + "Speaker", "SPKOUTR", + "Headset Mic", "MICBIAS", + "IN3R", "Headset Mic", + "DMICDAT", "Digital Mic"; simple-audio-card,cpu { sound-dai = <&sai2>; }; @@ -793,6 +790,7 @@ codec: wm8962@1a { assigned-clock-rates = <22579200>; //assigned-clock-rates = <24576000>; #sound-dai-cells = <0>; + mic-cfg = <0x200>; #if defined( AUDIO_REGULATOR ) DCVDD-supply = <®_aud_1v8>; DBVDD-supply = <®_aud_1v8>; @@ -805,11 +803,11 @@ codec: wm8962@1a { #endif gpio-cfg = < 0x0000 /* n/c */ - 0x0000 /* gpio2, 1: default */ - 0x0000 /* gpio3, 2: todo: dmicclk, 1_0011 */ + 0x0001 /* gpio2, 1: default */ + 0x0013 /* gpio3, 2: dmicclk */ 0x0000 /* n/c, 3: default */ - 0x0000 /* gpio5, 4: default */ - 0x0000 /* gpio6, 5: dmic_dat, 1_0100 */ + 0x8014 /* gpio5, 4: dmic_dat */ + 0x0000 /* gpio6, 5: default */ >; status = "okay"; }; -- GitLab