From c14d28e86d3c70720622e1d517968c1721f23214 Mon Sep 17 00:00:00 2001 From: Urja Rannikko Date: Tue, 28 Aug 2018 18:55:07 +0000 Subject: [PATCH 01/24] clk: rockchip: improve rk3288 pll rates for better hdmi output Add and correct PLL rates for better hdmi output. This includes minimizing jitter on 213 MHz for better 71 MHz, 250.5 MHz for better 83.5 MHz, 428 MHz for better 25.175 Mhz, low jitter 273 MHz for better 68.25 mhz, 356 MHz for better 118.68 Mhz and 300MHz. Increase the used Fvco for 308, 324 MHz, 292.5 MHz, 273.6 MHz, 238 MHz and 216 MHz. And add some additional rates allowing to reach better hdmi-related rates in general. These match the rates used by ChromeOS, so have been quite widely tested. Signed-off-by: Urja Rannikko Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3288.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/drivers/clk/rockchip/clk-rk3288.c b/drivers/clk/rockchip/clk-rk3288.c index 450de24a1b42..5a67b7869960 100644 --- a/drivers/clk/rockchip/clk-rk3288.c +++ b/drivers/clk/rockchip/clk-rk3288.c @@ -83,22 +83,43 @@ static struct rockchip_pll_rate_table rk3288_pll_rates[] = { RK3066_PLL_RATE( 768000000, 1, 64, 2), RK3066_PLL_RATE( 742500000, 8, 495, 2), RK3066_PLL_RATE( 696000000, 1, 58, 2), + RK3066_PLL_RATE_NB(621000000, 1, 207, 8, 1), RK3066_PLL_RATE( 600000000, 1, 50, 2), RK3066_PLL_RATE_NB(594000000, 1, 198, 8, 1), RK3066_PLL_RATE( 552000000, 1, 46, 2), RK3066_PLL_RATE( 504000000, 1, 84, 4), RK3066_PLL_RATE( 500000000, 3, 125, 2), RK3066_PLL_RATE( 456000000, 1, 76, 4), + RK3066_PLL_RATE( 428000000, 1, 107, 6), RK3066_PLL_RATE( 408000000, 1, 68, 4), RK3066_PLL_RATE( 400000000, 3, 100, 2), + RK3066_PLL_RATE_NB( 394000000, 1, 197, 12, 1), RK3066_PLL_RATE( 384000000, 2, 128, 4), RK3066_PLL_RATE( 360000000, 1, 60, 4), + RK3066_PLL_RATE_NB( 356000000, 1, 178, 12, 1), + RK3066_PLL_RATE_NB( 324000000, 1, 189, 14, 1), RK3066_PLL_RATE( 312000000, 1, 52, 4), - RK3066_PLL_RATE( 300000000, 1, 50, 4), - RK3066_PLL_RATE( 297000000, 2, 198, 8), + RK3066_PLL_RATE_NB( 308000000, 1, 154, 12, 1), + RK3066_PLL_RATE_NB( 303000000, 1, 202, 16, 1), + RK3066_PLL_RATE( 300000000, 1, 75, 6), + RK3066_PLL_RATE_NB( 297750000, 2, 397, 16, 1), + RK3066_PLL_RATE_NB( 293250000, 2, 391, 16, 1), + RK3066_PLL_RATE_NB( 292500000, 1, 195, 16, 1), + RK3066_PLL_RATE( 273600000, 1, 114, 10), + RK3066_PLL_RATE_NB( 273000000, 1, 182, 16, 1), + RK3066_PLL_RATE_NB( 270000000, 1, 180, 16, 1), + RK3066_PLL_RATE_NB( 266250000, 2, 355, 16, 1), + RK3066_PLL_RATE_NB( 256500000, 1, 171, 16, 1), RK3066_PLL_RATE( 252000000, 1, 84, 8), - RK3066_PLL_RATE( 216000000, 1, 72, 8), - RK3066_PLL_RATE( 148500000, 2, 99, 8), + RK3066_PLL_RATE_NB( 250500000, 1, 167, 16, 1), + RK3066_PLL_RATE_NB( 243428571, 1, 142, 14, 1), + RK3066_PLL_RATE( 238000000, 1, 119, 12), + RK3066_PLL_RATE_NB( 219750000, 2, 293, 16, 1), + RK3066_PLL_RATE_NB( 216000000, 1, 144, 16, 1), + RK3066_PLL_RATE_NB( 213000000, 1, 142, 16, 1), + RK3066_PLL_RATE( 195428571, 1, 114, 14), + RK3066_PLL_RATE( 160000000, 1, 80, 12), + RK3066_PLL_RATE( 157500000, 1, 105, 16), RK3066_PLL_RATE( 126000000, 1, 84, 16), RK3066_PLL_RATE( 48000000, 1, 64, 32), { /* sentinel */ }, From 82f4b67f018c88a7cc9337f0067ed3d6ec352648 Mon Sep 17 00:00:00 2001 From: Ziyuan Xu Date: Thu, 11 Oct 2018 15:26:43 +0800 Subject: [PATCH 02/24] clk: rockchip: fix wrong mmc sample phase shift for rk3328 mmc sample shift is 0 for RK3328 referring to the TRM. So fix them. Fixes: fe3511ad8a1c ("clk: rockchip: add clock controller for rk3328") Cc: stable@vger.kernel.org Signed-off-by: Ziyuan Xu Signed-off-by: Shawn Lin Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3328.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/rockchip/clk-rk3328.c b/drivers/clk/rockchip/clk-rk3328.c index 252366a5231f..2c5426607790 100644 --- a/drivers/clk/rockchip/clk-rk3328.c +++ b/drivers/clk/rockchip/clk-rk3328.c @@ -813,22 +813,22 @@ static struct rockchip_clk_branch rk3328_clk_branches[] __initdata = { MMC(SCLK_SDMMC_DRV, "sdmmc_drv", "clk_sdmmc", RK3328_SDMMC_CON0, 1), MMC(SCLK_SDMMC_SAMPLE, "sdmmc_sample", "clk_sdmmc", - RK3328_SDMMC_CON1, 1), + RK3328_SDMMC_CON1, 0), MMC(SCLK_SDIO_DRV, "sdio_drv", "clk_sdio", RK3328_SDIO_CON0, 1), MMC(SCLK_SDIO_SAMPLE, "sdio_sample", "clk_sdio", - RK3328_SDIO_CON1, 1), + RK3328_SDIO_CON1, 0), MMC(SCLK_EMMC_DRV, "emmc_drv", "clk_emmc", RK3328_EMMC_CON0, 1), MMC(SCLK_EMMC_SAMPLE, "emmc_sample", "clk_emmc", - RK3328_EMMC_CON1, 1), + RK3328_EMMC_CON1, 0), MMC(SCLK_SDMMC_EXT_DRV, "sdmmc_ext_drv", "clk_sdmmc_ext", RK3328_SDMMC_EXT_CON0, 1), MMC(SCLK_SDMMC_EXT_SAMPLE, "sdmmc_ext_sample", "clk_sdmmc_ext", - RK3328_SDMMC_EXT_CON1, 1), + RK3328_SDMMC_EXT_CON1, 0), }; static const char *const rk3328_critical_clocks[] __initconst = { From 5f697a0e311c1e8e3cf56edaf1b757027f5275b0 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Sat, 27 Jan 2018 23:21:12 +0100 Subject: [PATCH 03/24] clk: rockchip: add clock-id for HCLK_HDMI on rk3066 RK3066 and RK3188 share most of the clock controller but the rk3066 does have an internal hdmi encoder and associated clock. Therefore add a clock-id so that this clock can be used. Signed-off-by: Heiko Stuebner --- include/dt-bindings/clock/rk3188-cru-common.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/dt-bindings/clock/rk3188-cru-common.h b/include/dt-bindings/clock/rk3188-cru-common.h index b9462b7d3dfe..dc2101a634be 100644 --- a/include/dt-bindings/clock/rk3188-cru-common.h +++ b/include/dt-bindings/clock/rk3188-cru-common.h @@ -139,8 +139,9 @@ #define HCLK_CIF1 470 #define HCLK_VEPU 471 #define HCLK_VDPU 472 +#define HCLK_HDMI 473 -#define CLK_NR_CLKS (HCLK_VDPU + 1) +#define CLK_NR_CLKS (HCLK_HDMI + 1) /* soft-reset indices */ #define SRST_MCORE 2 From cca8196bdfc2273df057e162bad58085d1c81a01 Mon Sep 17 00:00:00 2001 From: Heiko Stuebner Date: Sat, 27 Jan 2018 23:25:32 +0100 Subject: [PATCH 04/24] clk: rockchip: use the newly added clock-id for hdmi on RK3066 Export the clock for devicetree usage via the newly added id. Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-rk3188.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c index 67e73fd71f09..fa25e35ce7d5 100644 --- a/drivers/clk/rockchip/clk-rk3188.c +++ b/drivers/clk/rockchip/clk-rk3188.c @@ -645,7 +645,7 @@ static struct rockchip_clk_branch rk3066a_clk_branches[] __initdata = { GATE(HCLK_I2S1, "hclk_i2s1", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 3, GFLAGS), GATE(HCLK_I2S2, "hclk_i2s2", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 4, GFLAGS), GATE(HCLK_CIF1, "hclk_cif1", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 6, GFLAGS), - GATE(0, "hclk_hdmi", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS), + GATE(HCLK_HDMI, "hclk_hdmi", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS), GATE(HCLK_OTG1, "hclk_usbotg1", "hclk_peri", CLK_IGNORE_UNUSED, RK2928_CLKGATE_CON(5), 14, GFLAGS), From 8985167ecf57f97061599a155bb9652c84ea4913 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Wed, 29 Aug 2018 21:20:10 +0200 Subject: [PATCH 05/24] clk: s2mps11: Fix matching when built as module and DT node contains compatible When driver is built as module and DT node contains clocks compatible (e.g. "samsung,s2mps11-clk"), the module will not be autoloaded because module aliases won't match. The modalias from uevent: of:NclocksTCsamsung,s2mps11-clk The modalias from driver: platform:s2mps11-clk The devices are instantiated by parent's MFD. However both Device Tree bindings and parent define the compatible for clocks devices. In case of module matching this DT compatible will be used. The issue will not happen if this is a built-in (no need for module matching) or when clocks DT node does not contain compatible (not correct from bindings perspective but working for driver). Note when backporting to stable kernels: adjust the list of device ID entries. Cc: Fixes: 53c31b3437a6 ("mfd: sec-core: Add of_compatible strings for clock MFD cells") Signed-off-by: Krzysztof Kozlowski Acked-by: Stephen Boyd Signed-off-by: Stephen Boyd --- drivers/clk/clk-s2mps11.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index d44e0eea31ec..0934d3724495 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -245,6 +245,36 @@ static const struct platform_device_id s2mps11_clk_id[] = { }; MODULE_DEVICE_TABLE(platform, s2mps11_clk_id); +#ifdef CONFIG_OF +/* + * Device is instantiated through parent MFD device and device matching is done + * through platform_device_id. + * + * However if device's DT node contains proper clock compatible and driver is + * built as a module, then the *module* matching will be done trough DT aliases. + * This requires of_device_id table. In the same time this will not change the + * actual *device* matching so do not add .of_match_table. + */ +static const struct of_device_id s2mps11_dt_match[] = { + { + .compatible = "samsung,s2mps11-clk", + .data = (void *)S2MPS11X, + }, { + .compatible = "samsung,s2mps13-clk", + .data = (void *)S2MPS13X, + }, { + .compatible = "samsung,s2mps14-clk", + .data = (void *)S2MPS14X, + }, { + .compatible = "samsung,s5m8767-clk", + .data = (void *)S5M8767X, + }, { + /* Sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, s2mps11_dt_match); +#endif + static struct platform_driver s2mps11_clk_driver = { .driver = { .name = "s2mps11-clk", From 665636b2940d0897c4130253467f5e8c42eea392 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Tue, 16 Oct 2018 15:41:44 +0200 Subject: [PATCH 06/24] clk: rockchip: Fix static checker warning in rockchip_ddrclk_get_parent call Fixes the signedness bug returning '(-22)' on the return type by removing the sanity checker in rockchip_ddrclk_get_parent(). The function should return and unsigned value only and it's safe to remove the sanity checker as the core functions that call get_parent like clk_core_get_parent_by_index already ensures the validity of the clk index returned (index >= core->num_parents). Fixes: a4f182bf81f18 ("clk: rockchip: add new clock-type for the ddrclk") Cc: stable@vger.kernel.org Signed-off-by: Enric Balletbo i Serra Reviewed-by: Stephen Boyd Signed-off-by: Heiko Stuebner --- drivers/clk/rockchip/clk-ddr.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/clk/rockchip/clk-ddr.c b/drivers/clk/rockchip/clk-ddr.c index e8075359366b..ebce5260068b 100644 --- a/drivers/clk/rockchip/clk-ddr.c +++ b/drivers/clk/rockchip/clk-ddr.c @@ -80,16 +80,12 @@ static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw, static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw) { struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw); - int num_parents = clk_hw_get_num_parents(hw); u32 val; val = clk_readl(ddrclk->reg_base + ddrclk->mux_offset) >> ddrclk->mux_shift; val &= GENMASK(ddrclk->mux_width - 1, 0); - if (val >= num_parents) - return -EINVAL; - return val; } From acc4f98d44bfc7cccfcb866e991db755f2062e6f Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 31 Aug 2018 15:53:13 +0800 Subject: [PATCH 07/24] clk: imx6ul: add mmdc1 ipg clock i.MX6UL has MMDC1 ipg clock in CCM CCGR, add it into clock tree for clock management. Signed-off-by: Anson Huang Reviewed-by: Rob Herring Signed-off-by: Stephen Boyd --- drivers/clk/imx/clk-imx6ul.c | 1 + include/dt-bindings/clock/imx6ul-clock.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx6ul.c b/drivers/clk/imx/clk-imx6ul.c index 361b43f9742e..fd60d1549f71 100644 --- a/drivers/clk/imx/clk-imx6ul.c +++ b/drivers/clk/imx/clk-imx6ul.c @@ -408,6 +408,7 @@ static void __init imx6ul_clocks_init(struct device_node *ccm_node) clks[IMX6UL_CLK_WDOG1] = imx_clk_gate2("wdog1", "ipg", base + 0x74, 16); clks[IMX6UL_CLK_MMDC_P0_FAST] = imx_clk_gate_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL); clks[IMX6UL_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); + clks[IMX6UL_CLK_MMDC_P1_IPG] = imx_clk_gate2("mmdc_p1_ipg", "ipg", base + 0x74, 26); clks[IMX6UL_CLK_AXI] = imx_clk_gate_flags("axi", "axi_podf", base + 0x74, 28, CLK_IS_CRITICAL); /* CCGR4 */ diff --git a/include/dt-bindings/clock/imx6ul-clock.h b/include/dt-bindings/clock/imx6ul-clock.h index f8e0476a3a0e..f718aac9b9da 100644 --- a/include/dt-bindings/clock/imx6ul-clock.h +++ b/include/dt-bindings/clock/imx6ul-clock.h @@ -259,7 +259,8 @@ #define IMX6UL_CLK_GPIO3 246 #define IMX6UL_CLK_GPIO4 247 #define IMX6UL_CLK_GPIO5 248 +#define IMX6UL_CLK_MMDC_P1_IPG 249 -#define IMX6UL_CLK_END 249 +#define IMX6UL_CLK_END 250 #endif /* __DT_BINDINGS_CLOCK_IMX6UL_H */ From 891f30bf603ba49ac1cf1778fedef4e9d4ee3483 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 31 Aug 2018 15:53:14 +0800 Subject: [PATCH 08/24] clk: imx6sx: add mmdc1 ipg clock i.MX6SX has MMDC1 ipg clock in CCM CCGR, add it into clock tree for clock management. Signed-off-by: Anson Huang Reviewed-by: Rob Herring Signed-off-by: Stephen Boyd --- drivers/clk/imx/clk-imx6sx.c | 1 + include/dt-bindings/clock/imx6sx-clock.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx6sx.c b/drivers/clk/imx/clk-imx6sx.c index d9f2890ffe62..18527a335ace 100644 --- a/drivers/clk/imx/clk-imx6sx.c +++ b/drivers/clk/imx/clk-imx6sx.c @@ -431,6 +431,7 @@ static void __init imx6sx_clocks_init(struct device_node *ccm_node) clks[IMX6SX_CLK_MLB] = imx_clk_gate2("mlb", "ahb", base + 0x74, 18); clks[IMX6SX_CLK_MMDC_P0_FAST] = imx_clk_gate2_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL); clks[IMX6SX_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); + clks[IMX6SX_CLK_MMDC_P1_IPG] = imx_clk_gate2("mmdc_p1_ipg", "ipg", base + 0x74, 26); clks[IMX6SX_CLK_OCRAM] = imx_clk_gate2_flags("ocram", "ocram_podf", base + 0x74, 28, CLK_IS_CRITICAL); /* CCGR4 */ diff --git a/include/dt-bindings/clock/imx6sx-clock.h b/include/dt-bindings/clock/imx6sx-clock.h index cd2d6c570e86..fb420c734774 100644 --- a/include/dt-bindings/clock/imx6sx-clock.h +++ b/include/dt-bindings/clock/imx6sx-clock.h @@ -279,6 +279,7 @@ #define IMX6SX_CLK_LVDS2_OUT 266 #define IMX6SX_CLK_LVDS2_IN 267 #define IMX6SX_CLK_ANACLK2 268 -#define IMX6SX_CLK_CLK_END 269 +#define IMX6SX_CLK_MMDC_P1_IPG 269 +#define IMX6SX_CLK_CLK_END 270 #endif /* __DT_BINDINGS_CLOCK_IMX6SX_H */ From aac7ff2048a881975fceb41ae6545730dee310d2 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 31 Aug 2018 15:53:15 +0800 Subject: [PATCH 09/24] clk: imx6sll: add mmdc1 ipg clock i.MX6SLL has MMDC1 ipg clock in CCM CCGR, add it into clock tree for clock management. Signed-off-by: Anson Huang Reviewed-by: Rob Herring Signed-off-by: Stephen Boyd --- drivers/clk/imx/clk-imx6sll.c | 1 + include/dt-bindings/clock/imx6sll-clock.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx6sll.c b/drivers/clk/imx/clk-imx6sll.c index 52379ee49aec..3bd2044cf25c 100644 --- a/drivers/clk/imx/clk-imx6sll.c +++ b/drivers/clk/imx/clk-imx6sll.c @@ -293,6 +293,7 @@ static void __init imx6sll_clocks_init(struct device_node *ccm_node) clks[IMX6SLL_CLK_WDOG1] = imx_clk_gate2("wdog1", "ipg", base + 0x74, 16); clks[IMX6SLL_CLK_MMDC_P0_FAST] = imx_clk_gate_flags("mmdc_p0_fast", "mmdc_podf", base + 0x74, 20, CLK_IS_CRITICAL); clks[IMX6SLL_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); + clks[IMX6SLL_CLK_MMDC_P1_IPG] = imx_clk_gate2("mmdc_p1_ipg", "ipg", base + 0x74, 26); clks[IMX6SLL_CLK_OCRAM] = imx_clk_gate_flags("ocram","ahb", base + 0x74, 28, CLK_IS_CRITICAL); /* CCGR4 */ diff --git a/include/dt-bindings/clock/imx6sll-clock.h b/include/dt-bindings/clock/imx6sll-clock.h index 1036475f997d..f446710fe63d 100644 --- a/include/dt-bindings/clock/imx6sll-clock.h +++ b/include/dt-bindings/clock/imx6sll-clock.h @@ -203,7 +203,8 @@ #define IMX6SLL_CLK_GPIO4 176 #define IMX6SLL_CLK_GPIO5 177 #define IMX6SLL_CLK_GPIO6 178 +#define IMX6SLL_CLK_MMDC_P1_IPG 179 -#define IMX6SLL_CLK_END 179 +#define IMX6SLL_CLK_END 180 #endif /* __DT_BINDINGS_CLOCK_IMX6SLL_H */ From 09d47620d0f839bc4bf3d3061aabee0c9539407a Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 31 Aug 2018 15:53:16 +0800 Subject: [PATCH 10/24] clk: imx6sl: add mmdc ipg clocks i.MX6SL has MMDC0 and MMDC1 ipg clock in CCM CCGR, add them into clock tree for clock management. Signed-off-by: Anson Huang Reviewed-by: Rob Herring Signed-off-by: Stephen Boyd --- drivers/clk/imx/clk-imx6sl.c | 2 ++ include/dt-bindings/clock/imx6sl-clock.h | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx6sl.c b/drivers/clk/imx/clk-imx6sl.c index eb6bcbf345a3..6fcfbbd907a5 100644 --- a/drivers/clk/imx/clk-imx6sl.c +++ b/drivers/clk/imx/clk-imx6sl.c @@ -386,6 +386,8 @@ static void __init imx6sl_clocks_init(struct device_node *ccm_node) clks[IMX6SL_CLK_LCDIF_AXI] = imx_clk_gate2("lcdif_axi", "lcdif_axi_podf", base + 0x74, 6); clks[IMX6SL_CLK_LCDIF_PIX] = imx_clk_gate2("lcdif_pix", "lcdif_pix_podf", base + 0x74, 8); clks[IMX6SL_CLK_EPDC_PIX] = imx_clk_gate2("epdc_pix", "epdc_pix_podf", base + 0x74, 10); + clks[IMX6SL_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); + clks[IMX6SL_CLK_MMDC_P1_IPG] = imx_clk_gate2("mmdc_p1_ipg", "ipg", base + 0x74, 26); clks[IMX6SL_CLK_OCRAM] = imx_clk_gate2("ocram", "ocram_podf", base + 0x74, 28); clks[IMX6SL_CLK_PWM1] = imx_clk_gate2("pwm1", "perclk", base + 0x78, 16); clks[IMX6SL_CLK_PWM2] = imx_clk_gate2("pwm2", "perclk", base + 0x78, 18); diff --git a/include/dt-bindings/clock/imx6sl-clock.h b/include/dt-bindings/clock/imx6sl-clock.h index e14573e293c5..cfbfc39d1878 100644 --- a/include/dt-bindings/clock/imx6sl-clock.h +++ b/include/dt-bindings/clock/imx6sl-clock.h @@ -175,6 +175,8 @@ #define IMX6SL_CLK_SSI2_IPG 162 #define IMX6SL_CLK_SSI3_IPG 163 #define IMX6SL_CLK_SPDIF_GCLK 164 -#define IMX6SL_CLK_END 165 +#define IMX6SL_CLK_MMDC_P0_IPG 165 +#define IMX6SL_CLK_MMDC_P1_IPG 166 +#define IMX6SL_CLK_END 167 #endif /* __DT_BINDINGS_CLOCK_IMX6SL_H */ From 341ce3563e52d6adc69095f65d57d2c9b8c68a65 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Fri, 31 Aug 2018 15:53:17 +0800 Subject: [PATCH 11/24] clk: imx6q: add mmdc0 ipg clock i.MX6Q has MMDC0 ipg clock in CCM CCGR, add it into clock tree for clock management. Signed-off-by: Anson Huang Reviewed-by: Rob Herring Signed-off-by: Stephen Boyd --- drivers/clk/imx/clk-imx6q.c | 1 + include/dt-bindings/clock/imx6qdl-clock.h | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index 8c7c2fcb8d94..bbe0c60f4d09 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -789,6 +789,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_MLB] = imx_clk_gate2("mlb", "axi", base + 0x74, 18); clk[IMX6QDL_CLK_MMDC_CH0_AXI] = imx_clk_gate2_flags("mmdc_ch0_axi", "mmdc_ch0_axi_podf", base + 0x74, 20, CLK_IS_CRITICAL); clk[IMX6QDL_CLK_MMDC_CH1_AXI] = imx_clk_gate2("mmdc_ch1_axi", "mmdc_ch1_axi_podf", base + 0x74, 22); + clk[IMX6QDL_CLK_MMDC_P0_IPG] = imx_clk_gate2_flags("mmdc_p0_ipg", "ipg", base + 0x74, 24, CLK_IS_CRITICAL); clk[IMX6QDL_CLK_OCRAM] = imx_clk_gate2("ocram", "ahb", base + 0x74, 28); clk[IMX6QDL_CLK_OPENVG_AXI] = imx_clk_gate2("openvg_axi", "axi", base + 0x74, 30); clk[IMX6QDL_CLK_PCIE_AXI] = imx_clk_gate2("pcie_axi", "pcie_axi_sel", base + 0x78, 0); diff --git a/include/dt-bindings/clock/imx6qdl-clock.h b/include/dt-bindings/clock/imx6qdl-clock.h index 7ad171b8f3bf..87b068f4a998 100644 --- a/include/dt-bindings/clock/imx6qdl-clock.h +++ b/include/dt-bindings/clock/imx6qdl-clock.h @@ -273,6 +273,7 @@ #define IMX6QDL_CLK_MLB_PODF 260 #define IMX6QDL_CLK_EPIT1 261 #define IMX6QDL_CLK_EPIT2 262 -#define IMX6QDL_CLK_END 263 +#define IMX6QDL_CLK_MMDC_P0_IPG 263 +#define IMX6QDL_CLK_END 264 #endif /* __DT_BINDINGS_CLOCK_IMX6QDL_H */ From 36d68f64c411e09788687d5919886aadeb92adca Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Aug 2018 17:42:20 +0530 Subject: [PATCH 12/24] ARM: Add Krait L2 register accessor functions Krait CPUs have a handful of L2 cache controller registers that live behind a cp15 based indirection register. First you program the indirection register (l2cpselr) to point the L2 'window' register (l2cpdr) at what you want to read/write. Then you read/write the 'window' register to do what you want. The l2cpselr register is not banked per-cpu so we must lock around accesses to it to prevent other CPUs from re-pointing l2cpdr underneath us. Cc: Mark Rutland Cc: Russell King Acked-by: Bjorn Andersson Signed-off-by: Stephen Boyd Signed-off-by: Sricharan R Tested-by: Craig Tatlor Signed-off-by: Stephen Boyd --- arch/arm/common/Kconfig | 3 ++ arch/arm/common/Makefile | 1 + arch/arm/common/krait-l2-accessors.c | 48 +++++++++++++++++++++++ arch/arm/include/asm/krait-l2-accessors.h | 9 +++++ 4 files changed, 61 insertions(+) create mode 100644 arch/arm/common/krait-l2-accessors.c create mode 100644 arch/arm/include/asm/krait-l2-accessors.h diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig index e5ad0708849a..c8e198631d41 100644 --- a/arch/arm/common/Kconfig +++ b/arch/arm/common/Kconfig @@ -7,6 +7,9 @@ config DMABOUNCE bool select ZONE_DMA +config KRAIT_L2_ACCESSORS + bool + config SHARP_LOCOMO bool diff --git a/arch/arm/common/Makefile b/arch/arm/common/Makefile index 3157be413297..219a260bbe5f 100644 --- a/arch/arm/common/Makefile +++ b/arch/arm/common/Makefile @@ -7,6 +7,7 @@ obj-y += firmware.o obj-$(CONFIG_SA1111) += sa1111.o obj-$(CONFIG_DMABOUNCE) += dmabounce.o +obj-$(CONFIG_KRAIT_L2_ACCESSORS) += krait-l2-accessors.o obj-$(CONFIG_SHARP_LOCOMO) += locomo.o obj-$(CONFIG_SHARP_PARAM) += sharpsl_param.o obj-$(CONFIG_SHARP_SCOOP) += scoop.o diff --git a/arch/arm/common/krait-l2-accessors.c b/arch/arm/common/krait-l2-accessors.c new file mode 100644 index 000000000000..9a97ddadecd6 --- /dev/null +++ b/arch/arm/common/krait-l2-accessors.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, The Linux Foundation. All rights reserved. + +#include +#include + +#include +#include + +static DEFINE_RAW_SPINLOCK(krait_l2_lock); + +void krait_set_l2_indirect_reg(u32 addr, u32 val) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&krait_l2_lock, flags); + /* + * Select the L2 window by poking l2cpselr, then write to the window + * via l2cpdr. + */ + asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); + isb(); + asm volatile ("mcr p15, 3, %0, c15, c0, 7 @ l2cpdr" : : "r" (val)); + isb(); + + raw_spin_unlock_irqrestore(&krait_l2_lock, flags); +} +EXPORT_SYMBOL(krait_set_l2_indirect_reg); + +u32 krait_get_l2_indirect_reg(u32 addr) +{ + u32 val; + unsigned long flags; + + raw_spin_lock_irqsave(&krait_l2_lock, flags); + /* + * Select the L2 window by poking l2cpselr, then read from the window + * via l2cpdr. + */ + asm volatile ("mcr p15, 3, %0, c15, c0, 6 @ l2cpselr" : : "r" (addr)); + isb(); + asm volatile ("mrc p15, 3, %0, c15, c0, 7 @ l2cpdr" : "=r" (val)); + + raw_spin_unlock_irqrestore(&krait_l2_lock, flags); + + return val; +} +EXPORT_SYMBOL(krait_get_l2_indirect_reg); diff --git a/arch/arm/include/asm/krait-l2-accessors.h b/arch/arm/include/asm/krait-l2-accessors.h new file mode 100644 index 000000000000..a5f2cdd6445f --- /dev/null +++ b/arch/arm/include/asm/krait-l2-accessors.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __ASMARM_KRAIT_L2_ACCESSORS_H +#define __ASMARM_KRAIT_L2_ACCESSORS_H + +extern void krait_set_l2_indirect_reg(u32 addr, u32 val); +extern u32 krait_get_l2_indirect_reg(u32 addr); + +#endif From b3f2f10693aadeacf83ab5be03810941a4b77612 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Aug 2018 17:42:21 +0530 Subject: [PATCH 13/24] clk: qcom: Add support for High-Frequency PLLs (HFPLLs) HFPLLs are the main frequency source for Krait CPU clocks. Add support for changing the rate of these PLLs. Signed-off-by: Stephen Boyd Signed-off-by: Sricharan R Tested-by: Craig Tatlor Signed-off-by: Stephen Boyd --- drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-hfpll.c | 244 +++++++++++++++++++++++++++++++++++ drivers/clk/qcom/clk-hfpll.h | 44 +++++++ 3 files changed, 289 insertions(+) create mode 100644 drivers/clk/qcom/clk-hfpll.c create mode 100644 drivers/clk/qcom/clk-hfpll.h diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 21a45035930d..7818e6c78413 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -11,6 +11,7 @@ clk-qcom-y += clk-branch.o clk-qcom-y += clk-regmap-divider.o clk-qcom-y += clk-regmap-mux.o clk-qcom-y += clk-regmap-mux-div.o +clk-qcom-y += clk-hfpll.o clk-qcom-y += reset.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o diff --git a/drivers/clk/qcom/clk-hfpll.c b/drivers/clk/qcom/clk-hfpll.c new file mode 100644 index 000000000000..3c04805f2a55 --- /dev/null +++ b/drivers/clk/qcom/clk-hfpll.c @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include + +#include "clk-regmap.h" +#include "clk-hfpll.h" + +#define PLL_OUTCTRL BIT(0) +#define PLL_BYPASSNL BIT(1) +#define PLL_RESET_N BIT(2) + +/* Initialize a HFPLL at a given rate and enable it. */ +static void __clk_hfpll_init_once(struct clk_hw *hw) +{ + struct clk_hfpll *h = to_clk_hfpll(hw); + struct hfpll_data const *hd = h->d; + struct regmap *regmap = h->clkr.regmap; + + if (likely(h->init_done)) + return; + + /* Configure PLL parameters for integer mode. */ + if (hd->config_val) + regmap_write(regmap, hd->config_reg, hd->config_val); + regmap_write(regmap, hd->m_reg, 0); + regmap_write(regmap, hd->n_reg, 1); + + if (hd->user_reg) { + u32 regval = hd->user_val; + unsigned long rate; + + rate = clk_hw_get_rate(hw); + + /* Pick the right VCO. */ + if (hd->user_vco_mask && rate > hd->low_vco_max_rate) + regval |= hd->user_vco_mask; + regmap_write(regmap, hd->user_reg, regval); + } + + if (hd->droop_reg) + regmap_write(regmap, hd->droop_reg, hd->droop_val); + + h->init_done = true; +} + +static void __clk_hfpll_enable(struct clk_hw *hw) +{ + struct clk_hfpll *h = to_clk_hfpll(hw); + struct hfpll_data const *hd = h->d; + struct regmap *regmap = h->clkr.regmap; + u32 val; + + __clk_hfpll_init_once(hw); + + /* Disable PLL bypass mode. */ + regmap_update_bits(regmap, hd->mode_reg, PLL_BYPASSNL, PLL_BYPASSNL); + + /* + * H/W requires a 5us delay between disabling the bypass and + * de-asserting the reset. Delay 10us just to be safe. + */ + udelay(10); + + /* De-assert active-low PLL reset. */ + regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N); + + /* Wait for PLL to lock. */ + if (hd->status_reg) { + do { + regmap_read(regmap, hd->status_reg, &val); + } while (!(val & BIT(hd->lock_bit))); + } else { + udelay(60); + } + + /* Enable PLL output. */ + regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL); +} + +/* Enable an already-configured HFPLL. */ +static int clk_hfpll_enable(struct clk_hw *hw) +{ + unsigned long flags; + struct clk_hfpll *h = to_clk_hfpll(hw); + struct hfpll_data const *hd = h->d; + struct regmap *regmap = h->clkr.regmap; + u32 mode; + + spin_lock_irqsave(&h->lock, flags); + regmap_read(regmap, hd->mode_reg, &mode); + if (!(mode & (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL))) + __clk_hfpll_enable(hw); + spin_unlock_irqrestore(&h->lock, flags); + + return 0; +} + +static void __clk_hfpll_disable(struct clk_hfpll *h) +{ + struct hfpll_data const *hd = h->d; + struct regmap *regmap = h->clkr.regmap; + + /* + * Disable the PLL output, disable test mode, enable the bypass mode, + * and assert the reset. + */ + regmap_update_bits(regmap, hd->mode_reg, + PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL, 0); +} + +static void clk_hfpll_disable(struct clk_hw *hw) +{ + struct clk_hfpll *h = to_clk_hfpll(hw); + unsigned long flags; + + spin_lock_irqsave(&h->lock, flags); + __clk_hfpll_disable(h); + spin_unlock_irqrestore(&h->lock, flags); +} + +static long clk_hfpll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_hfpll *h = to_clk_hfpll(hw); + struct hfpll_data const *hd = h->d; + unsigned long rrate; + + rate = clamp(rate, hd->min_rate, hd->max_rate); + + rrate = DIV_ROUND_UP(rate, *parent_rate) * *parent_rate; + if (rrate > hd->max_rate) + rrate -= *parent_rate; + + return rrate; +} + +/* + * For optimization reasons, assumes no downstream clocks are actively using + * it. + */ +static int clk_hfpll_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_hfpll *h = to_clk_hfpll(hw); + struct hfpll_data const *hd = h->d; + struct regmap *regmap = h->clkr.regmap; + unsigned long flags; + u32 l_val, val; + bool enabled; + + l_val = rate / parent_rate; + + spin_lock_irqsave(&h->lock, flags); + + enabled = __clk_is_enabled(hw->clk); + if (enabled) + __clk_hfpll_disable(h); + + /* Pick the right VCO. */ + if (hd->user_reg && hd->user_vco_mask) { + regmap_read(regmap, hd->user_reg, &val); + if (rate <= hd->low_vco_max_rate) + val &= ~hd->user_vco_mask; + else + val |= hd->user_vco_mask; + regmap_write(regmap, hd->user_reg, val); + } + + regmap_write(regmap, hd->l_reg, l_val); + + if (enabled) + __clk_hfpll_enable(hw); + + spin_unlock_irqrestore(&h->lock, flags); + + return 0; +} + +static unsigned long clk_hfpll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_hfpll *h = to_clk_hfpll(hw); + struct hfpll_data const *hd = h->d; + struct regmap *regmap = h->clkr.regmap; + u32 l_val; + + regmap_read(regmap, hd->l_reg, &l_val); + + return l_val * parent_rate; +} + +static void clk_hfpll_init(struct clk_hw *hw) +{ + struct clk_hfpll *h = to_clk_hfpll(hw); + struct hfpll_data const *hd = h->d; + struct regmap *regmap = h->clkr.regmap; + u32 mode, status; + + regmap_read(regmap, hd->mode_reg, &mode); + if (mode != (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL)) { + __clk_hfpll_init_once(hw); + return; + } + + if (hd->status_reg) { + regmap_read(regmap, hd->status_reg, &status); + if (!(status & BIT(hd->lock_bit))) { + WARN(1, "HFPLL %s is ON, but not locked!\n", + __clk_get_name(hw->clk)); + clk_hfpll_disable(hw); + __clk_hfpll_init_once(hw); + } + } +} + +static int hfpll_is_enabled(struct clk_hw *hw) +{ + struct clk_hfpll *h = to_clk_hfpll(hw); + struct hfpll_data const *hd = h->d; + struct regmap *regmap = h->clkr.regmap; + u32 mode; + + regmap_read(regmap, hd->mode_reg, &mode); + mode &= 0x7; + return mode == (PLL_BYPASSNL | PLL_RESET_N | PLL_OUTCTRL); +} + +const struct clk_ops clk_ops_hfpll = { + .enable = clk_hfpll_enable, + .disable = clk_hfpll_disable, + .is_enabled = hfpll_is_enabled, + .round_rate = clk_hfpll_round_rate, + .set_rate = clk_hfpll_set_rate, + .recalc_rate = clk_hfpll_recalc_rate, + .init = clk_hfpll_init, +}; +EXPORT_SYMBOL_GPL(clk_ops_hfpll); diff --git a/drivers/clk/qcom/clk-hfpll.h b/drivers/clk/qcom/clk-hfpll.h new file mode 100644 index 000000000000..2a57b2fb2f2f --- /dev/null +++ b/drivers/clk/qcom/clk-hfpll.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __QCOM_CLK_HFPLL_H__ +#define __QCOM_CLK_HFPLL_H__ + +#include +#include +#include "clk-regmap.h" + +struct hfpll_data { + u32 mode_reg; + u32 l_reg; + u32 m_reg; + u32 n_reg; + u32 user_reg; + u32 droop_reg; + u32 config_reg; + u32 status_reg; + u8 lock_bit; + + u32 droop_val; + u32 config_val; + u32 user_val; + u32 user_vco_mask; + unsigned long low_vco_max_rate; + + unsigned long min_rate; + unsigned long max_rate; +}; + +struct clk_hfpll { + struct hfpll_data const *d; + int init_done; + + struct clk_regmap clkr; + spinlock_t lock; +}; + +#define to_clk_hfpll(_hw) \ + container_of(to_clk_regmap(_hw), struct clk_hfpll, clkr) + +extern const struct clk_ops clk_ops_hfpll; + +#endif From cb546b797a0da4dbb1a0c76a2a357921887b6189 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Aug 2018 17:42:22 +0530 Subject: [PATCH 14/24] clk: qcom: Add HFPLL driver On some devices (MSM8974 for example), the HFPLLs are instantiated within the Krait processor subsystem as separate register regions. Add a driver for these PLLs so that we can provide HFPLL clocks for use by the system. Cc: Signed-off-by: Stephen Boyd Signed-off-by: Sricharan R Tested-by: Craig Tatlor Signed-off-by: Stephen Boyd --- drivers/clk/qcom/Kconfig | 8 ++++ drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/hfpll.c | 96 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 drivers/clk/qcom/hfpll.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 064768699fe7..21aec1048225 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -272,3 +272,11 @@ config SPMI_PMIC_CLKDIV Technologies, Inc. SPMI PMIC. It configures the frequency of clkdiv outputs of the PMIC. These clocks are typically wired through alternate functions on GPIO pins. + +config QCOM_HFPLL + tristate "High-Frequency PLL (HFPLL) Clock Controller" + depends on COMMON_CLK_QCOM + help + Support for the high-frequency PLLs present on Qualcomm devices. + Say Y if you want to support CPU frequency scaling on devices + such as MSM8974, APQ8084, etc. diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 7818e6c78413..f82eeaca278e 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -44,3 +44,4 @@ obj-$(CONFIG_SDM_DISPCC_845) += dispcc-sdm845.o obj-$(CONFIG_SDM_GCC_845) += gcc-sdm845.o obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o +obj-$(CONFIG_QCOM_HFPLL) += hfpll.o diff --git a/drivers/clk/qcom/hfpll.c b/drivers/clk/qcom/hfpll.c new file mode 100644 index 000000000000..a6de7101430c --- /dev/null +++ b/drivers/clk/qcom/hfpll.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-regmap.h" +#include "clk-hfpll.h" + +static const struct hfpll_data hdata = { + .mode_reg = 0x00, + .l_reg = 0x04, + .m_reg = 0x08, + .n_reg = 0x0c, + .user_reg = 0x10, + .config_reg = 0x14, + .config_val = 0x430405d, + .status_reg = 0x1c, + .lock_bit = 16, + + .user_val = 0x8, + .user_vco_mask = 0x100000, + .low_vco_max_rate = 1248000000, + .min_rate = 537600000UL, + .max_rate = 2900000000UL, +}; + +static const struct of_device_id qcom_hfpll_match_table[] = { + { .compatible = "qcom,hfpll" }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_hfpll_match_table); + +static const struct regmap_config hfpll_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x30, + .fast_io = true, +}; + +static int qcom_hfpll_probe(struct platform_device *pdev) +{ + struct resource *res; + struct device *dev = &pdev->dev; + void __iomem *base; + struct regmap *regmap; + struct clk_hfpll *h; + struct clk_init_data init = { + .parent_names = (const char *[]){ "xo" }, + .num_parents = 1, + .ops = &clk_ops_hfpll, + }; + + h = devm_kzalloc(dev, sizeof(*h), GFP_KERNEL); + if (!h) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(&pdev->dev, base, &hfpll_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + if (of_property_read_string_index(dev->of_node, "clock-output-names", + 0, &init.name)) + return -ENODEV; + + h->d = &hdata; + h->clkr.hw.init = &init; + spin_lock_init(&h->lock); + + return devm_clk_register_regmap(&pdev->dev, &h->clkr); +} + +static struct platform_driver qcom_hfpll_driver = { + .probe = qcom_hfpll_probe, + .driver = { + .name = "qcom-hfpll", + .of_match_table = qcom_hfpll_match_table, + }, +}; +module_platform_driver(qcom_hfpll_driver); + +MODULE_DESCRIPTION("QCOM HFPLL Clock Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:qcom-hfpll"); From 1f924faa8b1e4789ecc06ed0dd58ca3487c89012 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Aug 2018 17:42:23 +0530 Subject: [PATCH 15/24] dt-bindings: clock: Document qcom,hfpll Adds bindings document for qcom,hfpll instantiated within the Krait processor subsystem as separate register region. Reviewed-by: Rob Herring Signed-off-by: Stephen Boyd Signed-off-by: Sricharan R Tested-by: Craig Tatlor Signed-off-by: Stephen Boyd --- .../devicetree/bindings/clock/qcom,hfpll.txt | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,hfpll.txt diff --git a/Documentation/devicetree/bindings/clock/qcom,hfpll.txt b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt new file mode 100644 index 000000000000..ec02a024424c --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,hfpll.txt @@ -0,0 +1,60 @@ +High-Frequency PLL (HFPLL) + +PROPERTIES + +- compatible: + Usage: required + Value type: : + shall contain only one of the following. The generic + compatible "qcom,hfpll" should be also included. + + "qcom,hfpll-ipq8064", "qcom,hfpll" + "qcom,hfpll-apq8064", "qcom,hfpll" + "qcom,hfpll-msm8974", "qcom,hfpll" + "qcom,hfpll-msm8960", "qcom,hfpll" + +- reg: + Usage: required + Value type: + Definition: address and size of HPLL registers. An optional second + element specifies the address and size of the alias + register region. + +- clocks: + Usage: required + Value type: + Definition: reference to the xo clock. + +- clock-names: + Usage: required + Value type: + Definition: must be "xo". + +- clock-output-names: + Usage: required + Value type: + Definition: Name of the PLL. Typically hfpllX where X is a CPU number + starting at 0. Otherwise hfpll_Y where Y is more specific + such as "l2". + +Example: + +1) An HFPLL for the L2 cache. + + clock-controller@f9016000 { + compatible = "qcom,hfpll-ipq8064", "qcom,hfpll"; + reg = <0xf9016000 0x30>; + clocks = <&xo_board>; + clock-names = "xo"; + clock-output-names = "hfpll_l2"; + }; + +2) An HFPLL for CPU0. This HFPLL has the alias register region. + + clock-controller@f908a000 { + compatible = "qcom,hfpll-ipq8064", "qcom,hfpll"; + reg = <0xf908a000 0x30>, <0xf900a000 0x30>; + clocks = <&xo_board>; + clock-names = "xo"; + clock-output-names = "hfpll0"; + }; From 72ad7207954dd622a662ba884dc6c30a820123f2 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Aug 2018 17:42:24 +0530 Subject: [PATCH 16/24] clk: qcom: Add MSM8960/APQ8064's HFPLLs Describe the HFPLLs present on MSM8960 and APQ8064 devices. Acked-by: Rob Herring (bindings) Signed-off-by: Stephen Boyd Signed-off-by: Sricharan R Tested-by: Craig Tatlor Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-msm8960.c | 172 +++++++++++++++++++ include/dt-bindings/clock/qcom,gcc-msm8960.h | 2 + 2 files changed, 174 insertions(+) diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c index fd495e0471bb..399474755654 100644 --- a/drivers/clk/qcom/gcc-msm8960.c +++ b/drivers/clk/qcom/gcc-msm8960.c @@ -30,6 +30,7 @@ #include "clk-pll.h" #include "clk-rcg.h" #include "clk-branch.h" +#include "clk-hfpll.h" #include "reset.h" static struct clk_pll pll3 = { @@ -86,6 +87,164 @@ static struct clk_regmap pll8_vote = { }, }; +static struct hfpll_data hfpll0_data = { + .mode_reg = 0x3200, + .l_reg = 0x3208, + .m_reg = 0x320c, + .n_reg = 0x3210, + .config_reg = 0x3204, + .status_reg = 0x321c, + .config_val = 0x7845c665, + .droop_reg = 0x3214, + .droop_val = 0x0108c000, + .min_rate = 600000000UL, + .max_rate = 1800000000UL, +}; + +static struct clk_hfpll hfpll0 = { + .d = &hfpll0_data, + .clkr.hw.init = &(struct clk_init_data){ + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .name = "hfpll0", + .ops = &clk_ops_hfpll, + .flags = CLK_IGNORE_UNUSED, + }, + .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock), +}; + +static struct hfpll_data hfpll1_8064_data = { + .mode_reg = 0x3240, + .l_reg = 0x3248, + .m_reg = 0x324c, + .n_reg = 0x3250, + .config_reg = 0x3244, + .status_reg = 0x325c, + .config_val = 0x7845c665, + .droop_reg = 0x3254, + .droop_val = 0x0108c000, + .min_rate = 600000000UL, + .max_rate = 1800000000UL, +}; + +static struct hfpll_data hfpll1_data = { + .mode_reg = 0x3300, + .l_reg = 0x3308, + .m_reg = 0x330c, + .n_reg = 0x3310, + .config_reg = 0x3304, + .status_reg = 0x331c, + .config_val = 0x7845c665, + .droop_reg = 0x3314, + .droop_val = 0x0108c000, + .min_rate = 600000000UL, + .max_rate = 1800000000UL, +}; + +static struct clk_hfpll hfpll1 = { + .d = &hfpll1_data, + .clkr.hw.init = &(struct clk_init_data){ + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .name = "hfpll1", + .ops = &clk_ops_hfpll, + .flags = CLK_IGNORE_UNUSED, + }, + .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock), +}; + +static struct hfpll_data hfpll2_data = { + .mode_reg = 0x3280, + .l_reg = 0x3288, + .m_reg = 0x328c, + .n_reg = 0x3290, + .config_reg = 0x3284, + .status_reg = 0x329c, + .config_val = 0x7845c665, + .droop_reg = 0x3294, + .droop_val = 0x0108c000, + .min_rate = 600000000UL, + .max_rate = 1800000000UL, +}; + +static struct clk_hfpll hfpll2 = { + .d = &hfpll2_data, + .clkr.hw.init = &(struct clk_init_data){ + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .name = "hfpll2", + .ops = &clk_ops_hfpll, + .flags = CLK_IGNORE_UNUSED, + }, + .lock = __SPIN_LOCK_UNLOCKED(hfpll2.lock), +}; + +static struct hfpll_data hfpll3_data = { + .mode_reg = 0x32c0, + .l_reg = 0x32c8, + .m_reg = 0x32cc, + .n_reg = 0x32d0, + .config_reg = 0x32c4, + .status_reg = 0x32dc, + .config_val = 0x7845c665, + .droop_reg = 0x32d4, + .droop_val = 0x0108c000, + .min_rate = 600000000UL, + .max_rate = 1800000000UL, +}; + +static struct clk_hfpll hfpll3 = { + .d = &hfpll3_data, + .clkr.hw.init = &(struct clk_init_data){ + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .name = "hfpll3", + .ops = &clk_ops_hfpll, + .flags = CLK_IGNORE_UNUSED, + }, + .lock = __SPIN_LOCK_UNLOCKED(hfpll3.lock), +}; + +static struct hfpll_data hfpll_l2_8064_data = { + .mode_reg = 0x3300, + .l_reg = 0x3308, + .m_reg = 0x330c, + .n_reg = 0x3310, + .config_reg = 0x3304, + .status_reg = 0x331c, + .config_val = 0x7845c665, + .droop_reg = 0x3314, + .droop_val = 0x0108c000, + .min_rate = 600000000UL, + .max_rate = 1800000000UL, +}; + +static struct hfpll_data hfpll_l2_data = { + .mode_reg = 0x3400, + .l_reg = 0x3408, + .m_reg = 0x340c, + .n_reg = 0x3410, + .config_reg = 0x3404, + .status_reg = 0x341c, + .config_val = 0x7845c665, + .droop_reg = 0x3414, + .droop_val = 0x0108c000, + .min_rate = 600000000UL, + .max_rate = 1800000000UL, +}; + +static struct clk_hfpll hfpll_l2 = { + .d = &hfpll_l2_data, + .clkr.hw.init = &(struct clk_init_data){ + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .name = "hfpll_l2", + .ops = &clk_ops_hfpll, + .flags = CLK_IGNORE_UNUSED, + }, + .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock), +}; + static struct clk_pll pll14 = { .l_reg = 0x31c4, .m_reg = 0x31c8, @@ -3107,6 +3266,9 @@ static struct clk_regmap *gcc_msm8960_clks[] = { [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr, [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr, [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr, + [PLL9] = &hfpll0.clkr, + [PLL10] = &hfpll1.clkr, + [PLL12] = &hfpll_l2.clkr, }; static const struct qcom_reset_map gcc_msm8960_resets[] = { @@ -3318,6 +3480,11 @@ static struct clk_regmap *gcc_apq8064_clks[] = { [PMIC_ARB1_H_CLK] = &pmic_arb1_h_clk.clkr, [PMIC_SSBI2_CLK] = &pmic_ssbi2_clk.clkr, [RPM_MSG_RAM_H_CLK] = &rpm_msg_ram_h_clk.clkr, + [PLL9] = &hfpll0.clkr, + [PLL10] = &hfpll1.clkr, + [PLL12] = &hfpll_l2.clkr, + [PLL16] = &hfpll2.clkr, + [PLL17] = &hfpll3.clkr, }; static const struct qcom_reset_map gcc_apq8064_resets[] = { @@ -3477,6 +3644,11 @@ static int gcc_msm8960_probe(struct platform_device *pdev) if (ret) return ret; + if (match->data == &gcc_apq8064_desc) { + hfpll1.d = &hfpll1_8064_data; + hfpll_l2.d = &hfpll_l2_8064_data; + } + tsens = platform_device_register_data(&pdev->dev, "qcom-tsens", -1, NULL, 0); if (IS_ERR(tsens)) diff --git a/include/dt-bindings/clock/qcom,gcc-msm8960.h b/include/dt-bindings/clock/qcom,gcc-msm8960.h index 7d20eedfee98..e02742fc81cc 100644 --- a/include/dt-bindings/clock/qcom,gcc-msm8960.h +++ b/include/dt-bindings/clock/qcom,gcc-msm8960.h @@ -319,5 +319,7 @@ #define CE3_SRC 303 #define CE3_CORE_CLK 304 #define CE3_H_CLK 305 +#define PLL16 306 +#define PLL17 307 #endif From 1f79131bfd512f322c16b58dca581ce39beafab9 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Aug 2018 17:42:25 +0530 Subject: [PATCH 17/24] clk: qcom: Add IPQ806X's HFPLLs Describe the HFPLLs present on IPQ806X devices. Signed-off-by: Stephen Boyd Signed-off-by: Sricharan R Tested-by: Craig Tatlor Signed-off-by: Stephen Boyd --- drivers/clk/qcom/gcc-ipq806x.c | 82 ++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/drivers/clk/qcom/gcc-ipq806x.c b/drivers/clk/qcom/gcc-ipq806x.c index 5f61225657ab..33d1bc5c6a46 100644 --- a/drivers/clk/qcom/gcc-ipq806x.c +++ b/drivers/clk/qcom/gcc-ipq806x.c @@ -30,6 +30,7 @@ #include "clk-pll.h" #include "clk-rcg.h" #include "clk-branch.h" +#include "clk-hfpll.h" #include "reset.h" static struct clk_pll pll0 = { @@ -113,6 +114,84 @@ static struct clk_regmap pll8_vote = { }, }; +static struct hfpll_data hfpll0_data = { + .mode_reg = 0x3200, + .l_reg = 0x3208, + .m_reg = 0x320c, + .n_reg = 0x3210, + .config_reg = 0x3204, + .status_reg = 0x321c, + .config_val = 0x7845c665, + .droop_reg = 0x3214, + .droop_val = 0x0108c000, + .min_rate = 600000000UL, + .max_rate = 1800000000UL, +}; + +static struct clk_hfpll hfpll0 = { + .d = &hfpll0_data, + .clkr.hw.init = &(struct clk_init_data){ + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .name = "hfpll0", + .ops = &clk_ops_hfpll, + .flags = CLK_IGNORE_UNUSED, + }, + .lock = __SPIN_LOCK_UNLOCKED(hfpll0.lock), +}; + +static struct hfpll_data hfpll1_data = { + .mode_reg = 0x3240, + .l_reg = 0x3248, + .m_reg = 0x324c, + .n_reg = 0x3250, + .config_reg = 0x3244, + .status_reg = 0x325c, + .config_val = 0x7845c665, + .droop_reg = 0x3314, + .droop_val = 0x0108c000, + .min_rate = 600000000UL, + .max_rate = 1800000000UL, +}; + +static struct clk_hfpll hfpll1 = { + .d = &hfpll1_data, + .clkr.hw.init = &(struct clk_init_data){ + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .name = "hfpll1", + .ops = &clk_ops_hfpll, + .flags = CLK_IGNORE_UNUSED, + }, + .lock = __SPIN_LOCK_UNLOCKED(hfpll1.lock), +}; + +static struct hfpll_data hfpll_l2_data = { + .mode_reg = 0x3300, + .l_reg = 0x3308, + .m_reg = 0x330c, + .n_reg = 0x3310, + .config_reg = 0x3304, + .status_reg = 0x331c, + .config_val = 0x7845c665, + .droop_reg = 0x3314, + .droop_val = 0x0108c000, + .min_rate = 600000000UL, + .max_rate = 1800000000UL, +}; + +static struct clk_hfpll hfpll_l2 = { + .d = &hfpll_l2_data, + .clkr.hw.init = &(struct clk_init_data){ + .parent_names = (const char *[]){ "pxo" }, + .num_parents = 1, + .name = "hfpll_l2", + .ops = &clk_ops_hfpll, + .flags = CLK_IGNORE_UNUSED, + }, + .lock = __SPIN_LOCK_UNLOCKED(hfpll_l2.lock), +}; + static struct clk_pll pll14 = { .l_reg = 0x31c4, .m_reg = 0x31c8, @@ -2797,6 +2876,9 @@ static struct clk_regmap *gcc_ipq806x_clks[] = { [UBI32_CORE2_CLK_SRC] = &ubi32_core2_src_clk.clkr, [NSSTCM_CLK_SRC] = &nss_tcm_src.clkr, [NSSTCM_CLK] = &nss_tcm_clk.clkr, + [PLL9] = &hfpll0.clkr, + [PLL10] = &hfpll1.clkr, + [PLL12] = &hfpll_l2.clkr, }; static const struct qcom_reset_map gcc_ipq806x_resets[] = { From 4d7dc77babfef1d6cb8fd825e2f17dc3384c3272 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Aug 2018 17:42:26 +0530 Subject: [PATCH 18/24] clk: qcom: Add support for Krait clocks The Krait clocks are made up of a series of muxes and a divider that choose between a fixed rate clock and dedicated HFPLLs for each CPU. Instead of using mmio accesses to remux parents, the Krait implementation exposes the remux control via cp15 registers. Support these clocks. Signed-off-by: Stephen Boyd Signed-off-by: Sricharan R Tested-by: Craig Tatlor [sboyd@kernel.org: Move hidden config to top outside of the visible qcom config zone so that menuconfig looks nice] Signed-off-by: Stephen Boyd --- drivers/clk/qcom/Kconfig | 4 ++ drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-krait.c | 124 +++++++++++++++++++++++++++++++++++ drivers/clk/qcom/clk-krait.h | 37 +++++++++++ 4 files changed, 166 insertions(+) create mode 100644 drivers/clk/qcom/clk-krait.c create mode 100644 drivers/clk/qcom/clk-krait.h diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 21aec1048225..93f1342f1949 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -1,3 +1,7 @@ +config KRAIT_CLOCKS + bool + select KRAIT_L2_ACCESSORS + config QCOM_GDSC bool select PM_GENERIC_DOMAINS if PM diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index f82eeaca278e..506c4cfd775f 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -11,6 +11,7 @@ clk-qcom-y += clk-branch.o clk-qcom-y += clk-regmap-divider.o clk-qcom-y += clk-regmap-mux.o clk-qcom-y += clk-regmap-mux-div.o +clk-qcom-$(CONFIG_KRAIT_CLOCKS) += clk-krait.o clk-qcom-y += clk-hfpll.o clk-qcom-y += reset.o clk-qcom-$(CONFIG_QCOM_GDSC) += gdsc.o diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c new file mode 100644 index 000000000000..2e417678029f --- /dev/null +++ b/drivers/clk/qcom/clk-krait.c @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-krait.h" + +/* Secondary and primary muxes share the same cp15 register */ +static DEFINE_SPINLOCK(krait_clock_reg_lock); + +#define LPL_SHIFT 8 +static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel) +{ + unsigned long flags; + u32 regval; + + spin_lock_irqsave(&krait_clock_reg_lock, flags); + regval = krait_get_l2_indirect_reg(mux->offset); + regval &= ~(mux->mask << mux->shift); + regval |= (sel & mux->mask) << mux->shift; + if (mux->lpl) { + regval &= ~(mux->mask << (mux->shift + LPL_SHIFT)); + regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT); + } + krait_set_l2_indirect_reg(mux->offset, regval); + spin_unlock_irqrestore(&krait_clock_reg_lock, flags); + + /* Wait for switch to complete. */ + mb(); + udelay(1); +} + +static int krait_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct krait_mux_clk *mux = to_krait_mux_clk(hw); + u32 sel; + + sel = clk_mux_reindex(index, mux->parent_map, 0); + mux->en_mask = sel; + /* Don't touch mux if CPU is off as it won't work */ + if (__clk_is_enabled(hw->clk)) + __krait_mux_set_sel(mux, sel); + + return 0; +} + +static u8 krait_mux_get_parent(struct clk_hw *hw) +{ + struct krait_mux_clk *mux = to_krait_mux_clk(hw); + u32 sel; + + sel = krait_get_l2_indirect_reg(mux->offset); + sel >>= mux->shift; + sel &= mux->mask; + mux->en_mask = sel; + + return clk_mux_get_parent(hw, sel, mux->parent_map, 0); +} + +const struct clk_ops krait_mux_clk_ops = { + .set_parent = krait_mux_set_parent, + .get_parent = krait_mux_get_parent, + .determine_rate = __clk_mux_determine_rate_closest, +}; +EXPORT_SYMBOL_GPL(krait_mux_clk_ops); + +/* The divider can divide by 2, 4, 6 and 8. But we only really need div-2. */ +static long krait_div2_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + *parent_rate = clk_hw_round_rate(clk_hw_get_parent(hw), rate * 2); + return DIV_ROUND_UP(*parent_rate, 2); +} + +static int krait_div2_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct krait_div2_clk *d = to_krait_div2_clk(hw); + unsigned long flags; + u32 val; + u32 mask = BIT(d->width) - 1; + + if (d->lpl) + mask = mask << (d->shift + LPL_SHIFT) | mask << d->shift; + + spin_lock_irqsave(&krait_clock_reg_lock, flags); + val = krait_get_l2_indirect_reg(d->offset); + val &= ~mask; + krait_set_l2_indirect_reg(d->offset, val); + spin_unlock_irqrestore(&krait_clock_reg_lock, flags); + + return 0; +} + +static unsigned long +krait_div2_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + struct krait_div2_clk *d = to_krait_div2_clk(hw); + u32 mask = BIT(d->width) - 1; + u32 div; + + div = krait_get_l2_indirect_reg(d->offset); + div >>= d->shift; + div &= mask; + div = (div + 1) * 2; + + return DIV_ROUND_UP(parent_rate, div); +} + +const struct clk_ops krait_div2_clk_ops = { + .round_rate = krait_div2_round_rate, + .set_rate = krait_div2_set_rate, + .recalc_rate = krait_div2_recalc_rate, +}; +EXPORT_SYMBOL_GPL(krait_div2_clk_ops); diff --git a/drivers/clk/qcom/clk-krait.h b/drivers/clk/qcom/clk-krait.h new file mode 100644 index 000000000000..441ba1e18b81 --- /dev/null +++ b/drivers/clk/qcom/clk-krait.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __QCOM_CLK_KRAIT_H +#define __QCOM_CLK_KRAIT_H + +#include + +struct krait_mux_clk { + unsigned int *parent_map; + u32 offset; + u32 mask; + u32 shift; + u32 en_mask; + bool lpl; + + struct clk_hw hw; + struct notifier_block clk_nb; +}; + +#define to_krait_mux_clk(_hw) container_of(_hw, struct krait_mux_clk, hw) + +extern const struct clk_ops krait_mux_clk_ops; + +struct krait_div2_clk { + u32 offset; + u8 width; + u32 shift; + bool lpl; + + struct clk_hw hw; +}; + +#define to_krait_div2_clk(_hw) container_of(_hw, struct krait_div2_clk, hw) + +extern const struct clk_ops krait_div2_clk_ops; + +#endif From 3ddc3564d3c9f097986bd4ccbe34152413811335 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Aug 2018 17:42:27 +0530 Subject: [PATCH 19/24] clk: qcom: Add KPSS ACC/GCC driver The ACC and GCC regions present in KPSSv1 contain registers to control clocks and power to each Krait CPU and L2. For CPUfreq purposes probe these devices and expose a mux clock that chooses between PXO and PLL8. Cc: Signed-off-by: Stephen Boyd Signed-off-by: Sricharan R Tested-by: Craig Tatlor Signed-off-by: Stephen Boyd --- drivers/clk/qcom/Kconfig | 8 ++++ drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/kpss-xcc.c | 87 +++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) create mode 100644 drivers/clk/qcom/kpss-xcc.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 93f1342f1949..bed7ef4f51a2 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -284,3 +284,11 @@ config QCOM_HFPLL Support for the high-frequency PLLs present on Qualcomm devices. Say Y if you want to support CPU frequency scaling on devices such as MSM8974, APQ8084, etc. + +config KPSS_XCC + tristate "KPSS Clock Controller" + depends on COMMON_CLK_QCOM + help + Support for the Krait ACC and GCC clock controllers. Say Y + if you want to support CPU frequency scaling on devices such + as MSM8960, APQ8064, etc. diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 506c4cfd775f..92310ee33e9e 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -45,4 +45,5 @@ obj-$(CONFIG_SDM_DISPCC_845) += dispcc-sdm845.o obj-$(CONFIG_SDM_GCC_845) += gcc-sdm845.o obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o +obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o obj-$(CONFIG_QCOM_HFPLL) += hfpll.o diff --git a/drivers/clk/qcom/kpss-xcc.c b/drivers/clk/qcom/kpss-xcc.c new file mode 100644 index 000000000000..8590b5edd19d --- /dev/null +++ b/drivers/clk/qcom/kpss-xcc.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *aux_parents[] = { + "pll8_vote", + "pxo", +}; + +static unsigned int aux_parent_map[] = { + 3, + 0, +}; + +static const struct of_device_id kpss_xcc_match_table[] = { + { .compatible = "qcom,kpss-acc-v1", .data = (void *)1UL }, + { .compatible = "qcom,kpss-gcc" }, + {} +}; +MODULE_DEVICE_TABLE(of, kpss_xcc_match_table); + +static int kpss_xcc_driver_probe(struct platform_device *pdev) +{ + const struct of_device_id *id; + struct clk *clk; + struct resource *res; + void __iomem *base; + const char *name; + + id = of_match_device(kpss_xcc_match_table, &pdev->dev); + if (!id) + return -ENODEV; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + if (id->data) { + if (of_property_read_string_index(pdev->dev.of_node, + "clock-output-names", + 0, &name)) + return -ENODEV; + base += 0x14; + } else { + name = "acpu_l2_aux"; + base += 0x28; + } + + clk = clk_register_mux_table(&pdev->dev, name, aux_parents, + ARRAY_SIZE(aux_parents), 0, base, 0, 0x3, + 0, aux_parent_map, NULL); + + platform_set_drvdata(pdev, clk); + + return PTR_ERR_OR_ZERO(clk); +} + +static int kpss_xcc_driver_remove(struct platform_device *pdev) +{ + clk_unregister_mux(platform_get_drvdata(pdev)); + return 0; +} + +static struct platform_driver kpss_xcc_driver = { + .probe = kpss_xcc_driver_probe, + .remove = kpss_xcc_driver_remove, + .driver = { + .name = "kpss-xcc", + .of_match_table = kpss_xcc_match_table, + }, +}; +module_platform_driver(kpss_xcc_driver); + +MODULE_DESCRIPTION("Krait Processor Sub System (KPSS) Clock Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kpss-xcc"); From 40e5ddf4f84869815129551f4a8cfc2c223ebeae Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Aug 2018 17:42:28 +0530 Subject: [PATCH 20/24] dt-bindings: arm: Document qcom,kpss-gcc The ACC and GCC regions present in KPSSv1 contain registers to control clocks and power to each Krait CPU and L2. Documenting the bindings here. Reviewed-by: Rob Herring Signed-off-by: Stephen Boyd Signed-off-by: Sricharan R Tested-by: Craig Tatlor Signed-off-by: Stephen Boyd --- .../bindings/arm/msm/qcom,kpss-acc.txt | 19 ++++++++ .../bindings/arm/msm/qcom,kpss-gcc.txt | 44 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt index 1333db9acfee..7f696362a4a1 100644 --- a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt +++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-acc.txt @@ -21,10 +21,29 @@ PROPERTIES the register region. An optional second element specifies the base address and size of the alias register region. +- clocks: + Usage: required + Value type: + Definition: reference to the pll parents. + +- clock-names: + Usage: required + Value type: + Definition: must be "pll8_vote", "pxo". + +- clock-output-names: + Usage: optional + Value type: + Definition: Name of the output clock. Typically acpuX_aux where X is a + CPU number starting at 0. + Example: clock-controller@2088000 { compatible = "qcom,kpss-acc-v2"; reg = <0x02088000 0x1000>, <0x02008000 0x1000>; + clocks = <&gcc PLL8_VOTE>, <&gcc PXO_SRC>; + clock-names = "pll8_vote", "pxo"; + clock-output-names = "acpu0_aux"; }; diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt new file mode 100644 index 000000000000..e628758950e1 --- /dev/null +++ b/Documentation/devicetree/bindings/arm/msm/qcom,kpss-gcc.txt @@ -0,0 +1,44 @@ +Krait Processor Sub-system (KPSS) Global Clock Controller (GCC) + +PROPERTIES + +- compatible: + Usage: required + Value type: + Definition: should be one of the following. The generic compatible + "qcom,kpss-gcc" should also be included. + "qcom,kpss-gcc-ipq8064", "qcom,kpss-gcc" + "qcom,kpss-gcc-apq8064", "qcom,kpss-gcc" + "qcom,kpss-gcc-msm8974", "qcom,kpss-gcc" + "qcom,kpss-gcc-msm8960", "qcom,kpss-gcc" + +- reg: + Usage: required + Value type: + Definition: base address and size of the register region + +- clocks: + Usage: required + Value type: + Definition: reference to the pll parents. + +- clock-names: + Usage: required + Value type: + Definition: must be "pll8_vote", "pxo". + +- clock-output-names: + Usage: required + Value type: + Definition: Name of the output clock. Typically acpu_l2_aux indicating + an L2 cache auxiliary clock. + +Example: + + l2cc: clock-controller@2011000 { + compatible = "qcom,kpss-gcc-ipq8064", "qcom,kpss-gcc"; + reg = <0x2011000 0x1000>; + clocks = <&gcc PLL8_VOTE>, <&gcc PXO_SRC>; + clock-names = "pll8_vote", "pxo"; + clock-output-names = "acpu_l2_aux"; + }; From bb5c4a85051e5e0be39c775b6df85521f2ae807d Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Aug 2018 17:42:29 +0530 Subject: [PATCH 21/24] clk: qcom: Add Krait clock controller driver The Krait CPU clocks are made up of a primary mux and secondary mux for each CPU and the L2, controlled via cp15 accessors. For Kraits within KPSSv1 each secondary mux accepts a different aux source, but on KPSSv2 each secondary mux accepts the same aux source. Cc: Signed-off-by: Stephen Boyd Signed-off-by: Sricharan R Tested-by: Craig Tatlor Signed-off-by: Stephen Boyd --- drivers/clk/qcom/Kconfig | 8 + drivers/clk/qcom/Makefile | 1 + drivers/clk/qcom/clk-krait.c | 4 +- drivers/clk/qcom/krait-cc.c | 341 +++++++++++++++++++++++++++++++++++ 4 files changed, 352 insertions(+), 2 deletions(-) create mode 100644 drivers/clk/qcom/krait-cc.c diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index bed7ef4f51a2..646bc38d2993 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -292,3 +292,11 @@ config KPSS_XCC Support for the Krait ACC and GCC clock controllers. Say Y if you want to support CPU frequency scaling on devices such as MSM8960, APQ8064, etc. + +config KRAITCC + tristate "Krait Clock Controller" + depends on COMMON_CLK_QCOM && ARM + select KRAIT_CLOCKS + help + Support for the Krait CPU clocks on Qualcomm devices. + Say Y if you want to support CPU frequency scaling. diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 92310ee33e9e..949829b18813 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -47,3 +47,4 @@ obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o obj-$(CONFIG_KPSS_XCC) += kpss-xcc.o obj-$(CONFIG_QCOM_HFPLL) += hfpll.o +obj-$(CONFIG_KRAITCC) += krait-cc.o diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c index 2e417678029f..7ede744c6a14 100644 --- a/drivers/clk/qcom/clk-krait.c +++ b/drivers/clk/qcom/clk-krait.c @@ -44,7 +44,7 @@ static int krait_mux_set_parent(struct clk_hw *hw, u8 index) struct krait_mux_clk *mux = to_krait_mux_clk(hw); u32 sel; - sel = clk_mux_reindex(index, mux->parent_map, 0); + sel = clk_mux_index_to_val(mux->parent_map, 0, index); mux->en_mask = sel; /* Don't touch mux if CPU is off as it won't work */ if (__clk_is_enabled(hw->clk)) @@ -63,7 +63,7 @@ static u8 krait_mux_get_parent(struct clk_hw *hw) sel &= mux->mask; mux->en_mask = sel; - return clk_mux_get_parent(hw, sel, mux->parent_map, 0); + return clk_mux_val_to_index(hw, mux->parent_map, 0, sel); } const struct clk_ops krait_mux_clk_ops = { diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c new file mode 100644 index 000000000000..7c9dfb032065 --- /dev/null +++ b/drivers/clk/qcom/krait-cc.c @@ -0,0 +1,341 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clk-krait.h" + +static unsigned int sec_mux_map[] = { + 2, + 0, +}; + +static unsigned int pri_mux_map[] = { + 1, + 2, + 0, +}; + +static int +krait_add_div(struct device *dev, int id, const char *s, unsigned int offset) +{ + struct krait_div2_clk *div; + struct clk_init_data init = { + .num_parents = 1, + .ops = &krait_div2_clk_ops, + .flags = CLK_SET_RATE_PARENT, + }; + const char *p_names[1]; + struct clk *clk; + + div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL); + if (!div) + return -ENOMEM; + + div->width = 2; + div->shift = 6; + div->lpl = id >= 0; + div->offset = offset; + div->hw.init = &init; + + init.name = kasprintf(GFP_KERNEL, "hfpll%s_div", s); + if (!init.name) + return -ENOMEM; + + init.parent_names = p_names; + p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); + if (!p_names[0]) { + kfree(init.name); + return -ENOMEM; + } + + clk = devm_clk_register(dev, &div->hw); + kfree(p_names[0]); + kfree(init.name); + + return PTR_ERR_OR_ZERO(clk); +} + +static int +krait_add_sec_mux(struct device *dev, int id, const char *s, + unsigned int offset, bool unique_aux) +{ + struct krait_mux_clk *mux; + static const char *sec_mux_list[] = { + "acpu_aux", + "qsb", + }; + struct clk_init_data init = { + .parent_names = sec_mux_list, + .num_parents = ARRAY_SIZE(sec_mux_list), + .ops = &krait_mux_clk_ops, + .flags = CLK_SET_RATE_PARENT, + }; + struct clk *clk; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return -ENOMEM; + + mux->offset = offset; + mux->lpl = id >= 0; + mux->mask = 0x3; + mux->shift = 2; + mux->parent_map = sec_mux_map; + mux->hw.init = &init; + + init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); + if (!init.name) + return -ENOMEM; + + if (unique_aux) { + sec_mux_list[0] = kasprintf(GFP_KERNEL, "acpu%s_aux", s); + if (!sec_mux_list[0]) { + clk = ERR_PTR(-ENOMEM); + goto err_aux; + } + } + + clk = devm_clk_register(dev, &mux->hw); + + if (unique_aux) + kfree(sec_mux_list[0]); +err_aux: + kfree(init.name); + return PTR_ERR_OR_ZERO(clk); +} + +static struct clk * +krait_add_pri_mux(struct device *dev, int id, const char *s, + unsigned int offset) +{ + struct krait_mux_clk *mux; + const char *p_names[3]; + struct clk_init_data init = { + .parent_names = p_names, + .num_parents = ARRAY_SIZE(p_names), + .ops = &krait_mux_clk_ops, + .flags = CLK_SET_RATE_PARENT, + }; + struct clk *clk; + + mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + mux->mask = 0x3; + mux->shift = 0; + mux->offset = offset; + mux->lpl = id >= 0; + mux->parent_map = pri_mux_map; + mux->hw.init = &init; + + init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s); + if (!init.name) + return ERR_PTR(-ENOMEM); + + p_names[0] = kasprintf(GFP_KERNEL, "hfpll%s", s); + if (!p_names[0]) { + clk = ERR_PTR(-ENOMEM); + goto err_p0; + } + + p_names[1] = kasprintf(GFP_KERNEL, "hfpll%s_div", s); + if (!p_names[1]) { + clk = ERR_PTR(-ENOMEM); + goto err_p1; + } + + p_names[2] = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); + if (!p_names[2]) { + clk = ERR_PTR(-ENOMEM); + goto err_p2; + } + + clk = devm_clk_register(dev, &mux->hw); + + kfree(p_names[2]); +err_p2: + kfree(p_names[1]); +err_p1: + kfree(p_names[0]); +err_p0: + kfree(init.name); + return clk; +} + +/* id < 0 for L2, otherwise id == physical CPU number */ +static struct clk *krait_add_clks(struct device *dev, int id, bool unique_aux) +{ + int ret; + unsigned int offset; + void *p = NULL; + const char *s; + struct clk *clk; + + if (id >= 0) { + offset = 0x4501 + (0x1000 * id); + s = p = kasprintf(GFP_KERNEL, "%d", id); + if (!s) + return ERR_PTR(-ENOMEM); + } else { + offset = 0x500; + s = "_l2"; + } + + ret = krait_add_div(dev, id, s, offset); + if (ret) { + clk = ERR_PTR(ret); + goto err; + } + + ret = krait_add_sec_mux(dev, id, s, offset, unique_aux); + if (ret) { + clk = ERR_PTR(ret); + goto err; + } + + clk = krait_add_pri_mux(dev, id, s, offset); +err: + kfree(p); + return clk; +} + +static struct clk *krait_of_get(struct of_phandle_args *clkspec, void *data) +{ + unsigned int idx = clkspec->args[0]; + struct clk **clks = data; + + if (idx >= 5) { + pr_err("%s: invalid clock index %d\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return clks[idx] ? : ERR_PTR(-ENODEV); +} + +static const struct of_device_id krait_cc_match_table[] = { + { .compatible = "qcom,krait-cc-v1", (void *)1UL }, + { .compatible = "qcom,krait-cc-v2" }, + {} +}; +MODULE_DEVICE_TABLE(of, krait_cc_match_table); + +static int krait_cc_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct of_device_id *id; + unsigned long cur_rate, aux_rate; + int cpu; + struct clk *clk; + struct clk **clks; + struct clk *l2_pri_mux_clk; + + id = of_match_device(krait_cc_match_table, dev); + if (!id) + return -ENODEV; + + /* Rate is 1 because 0 causes problems for __clk_mux_determine_rate */ + clk = clk_register_fixed_rate(dev, "qsb", NULL, 0, 1); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + if (!id->data) { + clk = clk_register_fixed_factor(dev, "acpu_aux", + "gpll0_vote", 0, 1, 2); + if (IS_ERR(clk)) + return PTR_ERR(clk); + } + + /* Krait configurations have at most 4 CPUs and one L2 */ + clks = devm_kcalloc(dev, 5, sizeof(*clks), GFP_KERNEL); + if (!clks) + return -ENOMEM; + + for_each_possible_cpu(cpu) { + clk = krait_add_clks(dev, cpu, id->data); + if (IS_ERR(clk)) + return PTR_ERR(clk); + clks[cpu] = clk; + } + + l2_pri_mux_clk = krait_add_clks(dev, -1, id->data); + if (IS_ERR(l2_pri_mux_clk)) + return PTR_ERR(l2_pri_mux_clk); + clks[4] = l2_pri_mux_clk; + + /* + * We don't want the CPU or L2 clocks to be turned off at late init + * if CPUFREQ or HOTPLUG configs are disabled. So, bump up the + * refcount of these clocks. Any cpufreq/hotplug manager can assume + * that the clocks have already been prepared and enabled by the time + * they take over. + */ + for_each_online_cpu(cpu) { + clk_prepare_enable(l2_pri_mux_clk); + WARN(clk_prepare_enable(clks[cpu]), + "Unable to turn on CPU%d clock", cpu); + } + + /* + * Force reinit of HFPLLs and muxes to overwrite any potential + * incorrect configuration of HFPLLs and muxes by the bootloader. + * While at it, also make sure the cores are running at known rates + * and print the current rate. + * + * The clocks are set to aux clock rate first to make sure the + * secondary mux is not sourcing off of QSB. The rate is then set to + * two different rates to force a HFPLL reinit under all + * circumstances. + */ + cur_rate = clk_get_rate(l2_pri_mux_clk); + aux_rate = 384000000; + if (cur_rate == 1) { + pr_info("L2 @ QSB rate. Forcing new rate.\n"); + cur_rate = aux_rate; + } + clk_set_rate(l2_pri_mux_clk, aux_rate); + clk_set_rate(l2_pri_mux_clk, 2); + clk_set_rate(l2_pri_mux_clk, cur_rate); + pr_info("L2 @ %lu KHz\n", clk_get_rate(l2_pri_mux_clk) / 1000); + for_each_possible_cpu(cpu) { + clk = clks[cpu]; + cur_rate = clk_get_rate(clk); + if (cur_rate == 1) { + pr_info("CPU%d @ QSB rate. Forcing new rate.\n", cpu); + cur_rate = aux_rate; + } + + clk_set_rate(clk, aux_rate); + clk_set_rate(clk, 2); + clk_set_rate(clk, cur_rate); + pr_info("CPU%d @ %lu KHz\n", cpu, clk_get_rate(clk) / 1000); + } + + of_clk_add_provider(dev->of_node, krait_of_get, clks); + + return 0; +} + +static struct platform_driver krait_cc_driver = { + .probe = krait_cc_probe, + .driver = { + .name = "krait-cc", + .of_match_table = krait_cc_match_table, + }, +}; +module_platform_driver(krait_cc_driver); + +MODULE_DESCRIPTION("Krait CPU Clock Driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:krait-cc"); From bf4503ccf321811192cb07f9711556237c3cf668 Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Tue, 14 Aug 2018 17:42:30 +0530 Subject: [PATCH 22/24] dt-bindings: clock: Document qcom,krait-cc The Krait clock controller controls the krait CPU and the L2 clocks consisting a primary mux and secondary mux. Add document for that. Reviewed-by: Rob Herring Signed-off-by: Stephen Boyd Signed-off-by: Sricharan R Tested-by: Craig Tatlor Signed-off-by: Stephen Boyd --- .../bindings/clock/qcom,krait-cc.txt | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/qcom,krait-cc.txt diff --git a/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt new file mode 100644 index 000000000000..030ba60dab08 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/qcom,krait-cc.txt @@ -0,0 +1,34 @@ +Krait Clock Controller + +PROPERTIES + +- compatible: + Usage: required + Value type: + Definition: must be one of: + "qcom,krait-cc-v1" + "qcom,krait-cc-v2" + +- #clock-cells: + Usage: required + Value type: + Definition: must be 1 + +- clocks: + Usage: required + Value type: + Definition: reference to the clock parents of hfpll, secondary muxes. + +- clock-names: + Usage: required + Value type: + Definition: must be "hfpll0", "hfpll1", "acpu0_aux", "acpu1_aux", "qsb". + +Example: + + kraitcc: clock-controller { + compatible = "qcom,krait-cc-v1"; + clocks = <&hfpll0>, <&hfpll1>, <&acpu0_aux>, <&acpu1_aux>, ; + clock-names = "hfpll0", "hfpll1", "acpu0_aux", "acpu1_aux", "qsb"; + #clock-cells = <1>; + }; From 77612720a2362230af726baa4149c40ec7a7fb05 Mon Sep 17 00:00:00 2001 From: Sricharan R Date: Tue, 14 Aug 2018 17:42:31 +0530 Subject: [PATCH 23/24] clk: qcom: Add safe switch hook for krait mux clocks When the Hfplls are reprogrammed during the rate change, the primary muxes which are sourced from the same hfpll for higher frequencies, needs to be switched to the 'safe secondary mux' as the parent for that small window. This is done by registering a clk notifier for the muxes and switching to the safe parent in the PRE_RATE_CHANGE notifier and back to the original parent in the POST_RATE_CHANGE notifier. Signed-off-by: Sricharan R Tested-by: Craig Tatlor Signed-off-by: Stephen Boyd --- drivers/clk/qcom/clk-krait.c | 2 ++ drivers/clk/qcom/clk-krait.h | 3 ++ drivers/clk/qcom/krait-cc.c | 56 ++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c index 7ede744c6a14..59f1af415b58 100644 --- a/drivers/clk/qcom/clk-krait.c +++ b/drivers/clk/qcom/clk-krait.c @@ -50,6 +50,8 @@ static int krait_mux_set_parent(struct clk_hw *hw, u8 index) if (__clk_is_enabled(hw->clk)) __krait_mux_set_sel(mux, sel); + mux->reparent = true; + return 0; } diff --git a/drivers/clk/qcom/clk-krait.h b/drivers/clk/qcom/clk-krait.h index 441ba1e18b81..9120bd2f5297 100644 --- a/drivers/clk/qcom/clk-krait.h +++ b/drivers/clk/qcom/clk-krait.h @@ -12,6 +12,9 @@ struct krait_mux_clk { u32 shift; u32 en_mask; bool lpl; + u8 safe_sel; + u8 old_index; + bool reparent; struct clk_hw hw; struct notifier_block clk_nb; diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c index 7c9dfb032065..4d4b657d33c3 100644 --- a/drivers/clk/qcom/krait-cc.c +++ b/drivers/clk/qcom/krait-cc.c @@ -26,6 +26,49 @@ static unsigned int pri_mux_map[] = { 0, }; +/* + * Notifier function for switching the muxes to safe parent + * while the hfpll is getting reprogrammed. + */ +static int krait_notifier_cb(struct notifier_block *nb, + unsigned long event, + void *data) +{ + int ret = 0; + struct krait_mux_clk *mux = container_of(nb, struct krait_mux_clk, + clk_nb); + /* Switch to safe parent */ + if (event == PRE_RATE_CHANGE) { + mux->old_index = krait_mux_clk_ops.get_parent(&mux->hw); + ret = krait_mux_clk_ops.set_parent(&mux->hw, mux->safe_sel); + mux->reparent = false; + /* + * By the time POST_RATE_CHANGE notifier is called, + * clk framework itself would have changed the parent for the new rate. + * Only otherwise, put back to the old parent. + */ + } else if (event == POST_RATE_CHANGE) { + if (!mux->reparent) + ret = krait_mux_clk_ops.set_parent(&mux->hw, + mux->old_index); + } + + return notifier_from_errno(ret); +} + +static int krait_notifier_register(struct device *dev, struct clk *clk, + struct krait_mux_clk *mux) +{ + int ret = 0; + + mux->clk_nb.notifier_call = krait_notifier_cb; + ret = clk_notifier_register(clk, &mux->clk_nb); + if (ret) + dev_err(dev, "failed to register clock notifier: %d\n", ret); + + return ret; +} + static int krait_add_div(struct device *dev, int id, const char *s, unsigned int offset) { @@ -70,6 +113,7 @@ static int krait_add_sec_mux(struct device *dev, int id, const char *s, unsigned int offset, bool unique_aux) { + int ret; struct krait_mux_clk *mux; static const char *sec_mux_list[] = { "acpu_aux", @@ -93,6 +137,7 @@ krait_add_sec_mux(struct device *dev, int id, const char *s, mux->shift = 2; mux->parent_map = sec_mux_map; mux->hw.init = &init; + mux->safe_sel = 0; init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); if (!init.name) @@ -108,6 +153,11 @@ krait_add_sec_mux(struct device *dev, int id, const char *s, clk = devm_clk_register(dev, &mux->hw); + ret = krait_notifier_register(dev, clk, mux); + if (ret) + goto unique_aux; + +unique_aux: if (unique_aux) kfree(sec_mux_list[0]); err_aux: @@ -119,6 +169,7 @@ static struct clk * krait_add_pri_mux(struct device *dev, int id, const char *s, unsigned int offset) { + int ret; struct krait_mux_clk *mux; const char *p_names[3]; struct clk_init_data init = { @@ -139,6 +190,7 @@ krait_add_pri_mux(struct device *dev, int id, const char *s, mux->lpl = id >= 0; mux->parent_map = pri_mux_map; mux->hw.init = &init; + mux->safe_sel = 2; init.name = kasprintf(GFP_KERNEL, "krait%s_pri_mux", s); if (!init.name) @@ -164,6 +216,10 @@ krait_add_pri_mux(struct device *dev, int id, const char *s, clk = devm_clk_register(dev, &mux->hw); + ret = krait_notifier_register(dev, clk, mux); + if (ret) + goto err_p3; +err_p3: kfree(p_names[2]); err_p2: kfree(p_names[1]); From 9c940bbe2bb47e03ca5e937d30b6a50bf9c0e671 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 18 Oct 2018 12:13:40 -0700 Subject: [PATCH 24/24] clk: s2mps11: Add used attribute to s2mps11_dt_match Clang warns after commit 8985167ecf57 ("clk: s2mps11: Fix matching when built as module and DT node contains compatible"): drivers/clk/clk-s2mps11.c:242:34: warning: variable 's2mps11_dt_match' is not needed and will not be emitted [-Wunneeded-internal-declaration] static const struct of_device_id s2mps11_dt_match[] = { ^ 1 warning generated. This warning happens when a variable is used in some construct that doesn't require a reference to that variable to be emitted in the symbol table; in this case, it's MODULE_DEVICE_TABLE, which only needs to hold the data of the variable, not the variable itself. $ nm -S drivers/clk/clk-s2mps11.o | rg s2mps11_dt_match 00000078 000003d4 R __mod_of__s2mps11_dt_match_device_table Normally, with device ID table variables, it means that the variable just needs to be tied to the device declaration at the bottom of the file, like s2mps11_clk_id: $ nm -S drivers/clk/clk-s2mps11.o | rg s2mps11_clk_id 00000000 00000078 R __mod_platform__s2mps11_clk_id_device_table 00000000 00000078 r s2mps11_clk_id However, because the comment above this deliberately doesn't want this variable added to .of_match_table, we need to mark s2mps11_dt_match as __used to silence this warning. This makes it clear to Clang that the variable is used for something, even if a reference to it isn't being emitted. Signed-off-by: Nathan Chancellor Fixes: 8985167ecf57 ("clk: s2mps11: Fix matching when built as module and DT node contains compatible") Signed-off-by: Stephen Boyd --- drivers/clk/clk-s2mps11.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-s2mps11.c b/drivers/clk/clk-s2mps11.c index 0934d3724495..4080d4e78e8e 100644 --- a/drivers/clk/clk-s2mps11.c +++ b/drivers/clk/clk-s2mps11.c @@ -255,7 +255,7 @@ MODULE_DEVICE_TABLE(platform, s2mps11_clk_id); * This requires of_device_id table. In the same time this will not change the * actual *device* matching so do not add .of_match_table. */ -static const struct of_device_id s2mps11_dt_match[] = { +static const struct of_device_id s2mps11_dt_match[] __used = { { .compatible = "samsung,s2mps11-clk", .data = (void *)S2MPS11X,