Patches for QCOM IPQ4019 clock driver
v2 cover Patches for QCOM IPQ4019 clock driver
These patches are related to Qualcomm IPQ4019 GCC (Global Clock Controller) driver code mainly adding the nodes for clock marked as fixed in current IPQ4019 clock driver and support for multiple CPU frequencies.
[V2]
1. Removed the fixed clock references and add the same as clock nodes with recalc_rate operation.
2. Added all the supported cpu frequencies in frequency table.
Abhishek Sahu (5):
clk: qcom: ipq4019: Added the clock nodes and operations for pll
clk: qcom: ipq4019: Added the apss cpu pll divider clock node
clk: qcom: ipq4019: Added the nodes for pcnoc
clk: qcom: ipq4019: Added the all frequencies for apps cpu
clk: qcom: ipq4019: Added the cpu clock frequency change notifier
drivers/clk/qcom/gcc-ipq4019.c | 508 +++++++++++++++++++++++++-- include/dt-bindings/clock/qcom,gcc-ipq4019.h | 11 + 2 files changed, 486 insertions(+), 33 deletions(-)
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v2 1/5 clk: qcom: ipq4019: Added the clock nodes and operations for pll
The current ipq4019 clock driver registered the PLL clocks and dividers as fixed clock. These fixed clock needs to be removed from driver probe function and same need to be registered with clock framework. These PLL clocks should be programmed only once and the same are being programmed already by the boot loader so the set rate operation is not required for these clocks. Only the rate can be calculated by clock operations in clock driver file so this patch adds the same.
The PLL takes the reference clock from XO and generates the intermediate VCO frequency. This VCO frequency will be divided down by different PLL internal dividers. Some of the PLL internal dividers are fixed while other are programmable.
This patch does the following changes.
1. Operation for calculating PLL intermediate VCO frequency by reading the reference clock divider and feedback divider from register. Since VCO frequency falls outside the limit of unsigned long for IPQ4019, so this operation will return the VCO frequency in KHz.
2. Operation for calculating the internal PLL divider clock frequency. Clock Divider node should give either fixed divider value or divider table(maps the register divider value to actual divider value).
3. Adds and registers clock nodes for VCO(APPS DDR PLL and FE PLL) and PLL internal dividers(SDCC, FEPLL 500 Mhz, FEPLL 200 Mhz, FEPLL 125 Mhz, FEPLL 125 Mhz with delay, programmable WCSS 2G and 5G).
4. Changes the regmap limit from 0x2DFFF to 0x2FFFF for supporting the PLL registers read.
5. Changes the fixed clock name to have consistency in all clock names
--- drivers/clk/qcom/gcc-ipq4019.c | 316 ++++++++++++++++++++++++--- include/dt-bindings/clock/qcom,gcc-ipq4019.h | 9 + 2 files changed, 297 insertions(+), 28 deletions(-)
drivers/clk/qcom/gcc-ipq4019.c +288 -28
@@ -20,6 +20,11 @@#include <linux/clk-provider.h> #include <linux/regmap.h> #include <linux/reset-controller.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/bitops.h> +#include <linux/math64.h> +#include <asm/div64.h> #include <dt-bindings/clock/qcom,gcc-ipq4019.h>
@@ -28,6 +33,16 @@#include "clk-rcg.h" #include "clk-branch.h" #include "reset.h" +#include "clk-regmap-divider.h" + +#define to_clk_regmap_div (_hw) container_of(to_clk_regmap(_hw),\ + struct clk_regmap_div, clkr) + +#define to_clk_pll_div (_hw) container_of((to_clk_regmap_div(_hw)),\ + struct clk_pll_div, cdiv) + +#define to_clk_pll_vco (_hw) container_of((to_clk_regmap_div(_hw)),\ + struct clk_pll_vco, cdiv)enum {P_XO ,
@@ -40,6 +55,20 @@ enum { P_DDRPLLAPSS, }; +struct clk_pll_vco { +u32 fdbkdiv_shift; +u32 fdbkdiv_width; +struct clk_regmap_div cdiv; +}; + +struct clk_pll_div { +u32 fixed_div; +const u8 *parent_map; +struct clk_regmap_div cdiv; +const struct clk_div_table *div_table; +const struct freq_tbl *freq_tbl; +}; +static struct parent_map gcc_xo_200_500_map[] = { {P_XO ,0 }, {P_FEPLL200 ,1 },
@@ -48,8 +77,8 @@ static struct parent_map gcc_xo_200_500_map[] = {static const char *const gcc_xo_200_500[] = {"xo" , -"fepll200" , -"fepll500" , +"gcc_fepll200_clk" , +"gcc_fepll500_clk" , };static struct parent_map gcc_xo_200_map[] = {
@@ -59,7 +88,7 @@ static struct parent_map gcc_xo_200_map[] = {static const char *const gcc_xo_200[] = {"xo" , -"fepll200" , +"gcc_fepll200_clk" , };static struct parent_map gcc_xo_200_spi_map[] = {
@@ -69,7 +98,7 @@ static struct parent_map gcc_xo_200_spi_map[] = {static const char *const gcc_xo_200_spi[] = {"xo" , -"fepll200" , +"gcc_fepll200_clk" , };static struct parent_map gcc_xo_sdcc1_500_map[] = {
@@ -80,8 +109,8 @@ static struct parent_map gcc_xo_sdcc1_500_map[] = {static const char *const gcc_xo_sdcc1_500[] = {"xo" , -"ddrpll" , -"fepll500" , +"gcc_apps_sdcc_clk" , +"gcc_fepll500_clk" , };static struct parent_map gcc_xo_wcss2g_map[] = {
@@ -91,7 +120,7 @@ static struct parent_map gcc_xo_wcss2g_map[] = {static const char *const gcc_xo_wcss2g[] = {"xo" , -"fepllwcss2g" , +"gcc_fepllwcss2g_clk" , };static struct parent_map gcc_xo_wcss5g_map[] = {
@@ -101,7 +130,7 @@ static struct parent_map gcc_xo_wcss5g_map[] = {static const char *const gcc_xo_wcss5g[] = {"xo" , -"fepllwcss5g" , +"gcc_fepllwcss5g_clk" , };static struct parent_map gcc_xo_125_dly_map[] = {
@@ -111,7 +140,7 @@ static struct parent_map gcc_xo_125_dly_map[] = {static const char *const gcc_xo_125_dly[] = {"xo" , -"fepll125dly" , +"gcc_fepll125dly_clk" , };static struct parent_map gcc_xo_ddr_500_200_map[] = {
@@ -123,8 +152,8 @@ static struct parent_map gcc_xo_ddr_500_200_map[] = {static const char *const gcc_xo_ddr_500_200[] = {"xo" , -"fepll200" , -"fepll500" , +"gcc_fepll200_clk" , +"gcc_fepll500_clk" ,"ddrpllapss" , };
@@ -506,7 +535,7 @@ static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk[] = {F (25000000 ,P_FEPLL500 ,1 ,1 ,20 ),F (50000000 ,P_FEPLL500 ,1 ,1 ,10 ),F (100000000 ,P_FEPLL500 ,1 ,1 ,5 ), - F(193000000, P_DDRPLL, 1, 0, 0), + F(190000000, P_DDRPLL, 1, 0, 0), { } };
@@ -658,7 +687,7 @@ static struct clk_branch gcc_crypto_axi_clk = { .hw.init = &(struct clk_init_data){ .name ="gcc_crypto_axi_clk" , .parent_names = (const char *[]){ -"fepll125" , +"gcc_fepll125_clk" , }, .num_parents =1 , .ops = &clk_branch2_ops,
@@ -675,7 +704,7 @@ static struct clk_branch gcc_crypto_clk = { .hw.init = &(struct clk_init_data){ .name ="gcc_crypto_clk" , .parent_names = (const char *[]){ -"fepll125" , +"gcc_fepll125_clk" , }, .num_parents =1 , .ops = &clk_branch2_ops,
@@ -709,7 +738,7 @@ static struct clk_branch gcc_imem_axi_clk = { .hw.init = &(struct clk_init_data){ .name ="gcc_imem_axi_clk" , .parent_names = (const char *[]){ -"fepll200" , +"gcc_fepll200_clk" , }, .num_parents =1 , .ops = &clk_branch2_ops,
@@ -773,7 +802,7 @@ static struct clk_branch gcc_pcie_axi_s_clk = { .hw.init = &(struct clk_init_data){ .name ="gcc_pcie_axi_s_clk" , .parent_names = (const char *[]){ -"fepll200" , +"gcc_fepll200_clk" , }, .num_parents =1 , .ops = &clk_branch2_ops,
@@ -955,7 +984,7 @@ static struct clk_branch gcc_usb3_master_clk = { .hw.init = &(struct clk_init_data){ .name ="gcc_usb3_master_clk" , .parent_names = (const char *[]){ -"fepll125" , +"gcc_fepll125_clk" , }, .num_parents =1 , .ops = &clk_branch2_ops,
@@ -1155,6 +1184,238 @@ static struct clk_branch gcc_wcss5g_rtc_clk = { }, }; +/* +* Calculates the rate from parent rate and divider and round the rate +* in Mhz. This function takes the parent rate in Khz and returns the +* rate in Hz. +*/ +static unsigned long clk_calc_divider_rate (unsigned long parent_rate, +unsigned int div) +{ +u64 rate = parent_rate; + +do_div (rate, div); + +/* +* This rate is in KHz so divide with 1000 and multiply with 1000000 +* to get the rate in Hz. +*/ +do_div (rate,1000 ); + rate *=1000000 ; + +return (unsigned long )rate; +} + +/* +* Calculates the VCO rate for PLL. +* VCO rate value is greater than unsigned long limit. Since this is an +* intermediate clock node for actual PLL dividers, so it returns the +* rate in Khz. The child nodes will return the value in Hz after its +* divide operation. +*/ +static unsigned long clk_regmap_vco_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct clk_pll_vco *rcg =to_clk_pll_vco (hw); +u32 fdbkdiv, refclkdiv, cdiv; +u64 vco; + +regmap_read (rcg->cdiv.clkr.regmap, rcg->cdiv.reg, &cdiv); + refclkdiv = (cdiv >> rcg->cdiv.shift) & (BIT (rcg->cdiv.width) -1 ); + fdbkdiv = (cdiv >> rcg->fdbkdiv_shift) & (BIT (rcg->fdbkdiv_width) -1 ); + + vco = parent_rate; +do_div (vco, refclkdiv); +do_div (vco,1000 ); + vco *=2 ; + vco *= fdbkdiv; + +return (unsigned long )vco; +} + +const struct clk_ops clk_regmap_vco_ops = { + .recalc_rate = clk_regmap_vco_recalc_rate, +}; + +static struct clk_pll_vco gcc_apps_ddrpll_vco = { + .fdbkdiv_shift =16 , + .fdbkdiv_width =8 , + .cdiv.reg =0x2E020 , + .cdiv.shift =24 , + .cdiv.width =5 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_apps_ddrpll_vco" , + .parent_names = (const char *[]){ +"xo" , + }, + .num_parents =1 , + .ops = &clk_regmap_vco_ops, + }, + }, +}; + +static struct clk_pll_vco gcc_fepll_vco = { + .fdbkdiv_shift =16 , + .fdbkdiv_width =8 , + .cdiv.reg =0x2F020 , + .cdiv.shift =24 , + .cdiv.width =5 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepll_vco" , + .parent_names = (const char *[]){ +"xo" , + }, + .num_parents =1 , + .ops = &clk_regmap_vco_ops, + }, + }, +}; + +/* Calculates the rate for PLL divider. +* If the divider value is not fixed then it gets the actual divider value +* from divider table. Then, it calculate the clock rate by dividing the +* parent rate with actual divider value. +*/ +static unsigned long clk_regmap_clk_div_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct clk_pll_div *rcg =to_clk_pll_div (hw); +u32 cdiv, pre_div =1 ; +const struct clk_div_table *clkt; + +if (rcg->fixed_div) { + pre_div = rcg->fixed_div; + }else { +regmap_read (rcg->cdiv.clkr.regmap, rcg->cdiv.reg, &cdiv); + cdiv = (cdiv >> rcg->cdiv.shift) & (BIT (rcg->cdiv.width) -1 ); + +for (clkt = rcg->div_table; clkt->div; clkt++) { +if (clkt->val == cdiv) + pre_div = clkt->div; + } + } + +return clk_calc_divider_rate (parent_rate, pre_div); +}; + +const struct clk_ops clk_regmap_clk_div_ops = { + .recalc_rate = clk_regmap_clk_div_recalc_rate, +}; + +static struct clk_pll_div gcc_apps_sdcc_clk = { + .fixed_div =28 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_apps_sdcc_clk" , + .parent_names = (const char *[]){ +"gcc_apps_ddrpll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, +}; + +static struct clk_pll_div gcc_fepll125_clk = { + .fixed_div =32 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepll125_clk" , + .parent_names = (const char *[]){ +"gcc_fepll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, +}; + +static struct clk_pll_div gcc_fepll125dly_clk = { + .fixed_div =32 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepll125dly_clk" , + .parent_names = (const char *[]){ +"gcc_fepll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, +}; + +static struct clk_pll_div gcc_fepll200_clk = { + .fixed_div =20 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepll200_clk" , + .parent_names = (const char *[]){ +"gcc_fepll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, +}; + +static struct clk_pll_div gcc_fepll500_clk = { + .fixed_div =8 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepll500_clk" , + .parent_names = (const char *[]){ +"gcc_fepll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, +}; + +static const struct clk_div_table fepllwcss_clk_div_table[] = { + {0 ,15 }, + {1 ,16 }, + {2 ,18 }, + {3 ,20 }, + {}, +}; + +static struct clk_pll_div gcc_fepllwcss2g_clk = { + .cdiv.reg =0x2F020 , + .cdiv.shift =8 , + .cdiv.width =2 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepllwcss2g_clk" , + .parent_names = (const char *[]){ +"gcc_fepll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, + .div_table = fepllwcss_clk_div_table +}; + +static struct clk_pll_div gcc_fepllwcss5g_clk = { + .cdiv.reg =0x2F020 , + .cdiv.shift =12 , + .cdiv.width =2 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepllwcss5g_clk" , + .parent_names = (const char *[]){ +"gcc_fepll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, + .div_table = fepllwcss_clk_div_table +}; +static struct clk_regmap *gcc_ipq4019_clocks[] = { [AUDIO_CLK_SRC ] = &audio_clk_src.clkr, [BLSP1_QUP1_I2C_APPS_CLK_SRC ] = &blsp1_qup1_i2c_apps_clk_src.clkr,
@@ -1215,6 +1476,15 @@ static struct clk_regmap *gcc_ipq4019_clocks[] = { [GCC_WCSS5G_CLK] = &gcc_wcss5g_clk.clkr, [GCC_WCSS5G_REF_CLK] = &gcc_wcss5g_ref_clk.clkr, [GCC_WCSS5G_RTC_CLK] = &gcc_wcss5g_rtc_clk.clkr, + [GCC_APPS_DDRPLL_VCO] = &gcc_apps_ddrpll_vco.cdiv.clkr, + [GCC_FEPLL_VCO] = &gcc_fepll_vco.cdiv.clkr, + [GCC_SDCC_PLLDIV_CLK] = &gcc_apps_sdcc_clk.cdiv.clkr, + [GCC_FEPLL125_CLK] = &gcc_fepll125_clk.cdiv.clkr, + [GCC_FEPLL125DLY_CLK] = &gcc_fepll125dly_clk.cdiv.clkr, + [GCC_FEPLL200_CLK] = &gcc_fepll200_clk.cdiv.clkr, + [GCC_FEPLL500_CLK] = &gcc_fepll500_clk.cdiv.clkr, + [GCC_FEPLL_WCSS2G_CLK] = &gcc_fepllwcss2g_clk.cdiv.clkr, + [GCC_FEPLL_WCSS5G_CLK] = &gcc_fepllwcss5g_clk.cdiv.clkr, };static const struct qcom_reset_map gcc_ipq4019_resets[] = {
@@ -1295,7 +1565,7 @@ static const struct regmap_config gcc_ipq4019_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = 0x2dfff, + .max_register = 0x2FFFF, .fast_io = true, };
@@ -1315,16 +1585,6 @@ MODULE_DEVICE_TABLE(of, gcc_ipq4019_match_table);static int gcc_ipq4019_probe (struct platform_device *pdev) { -struct device *dev = &pdev->dev; - -clk_register_fixed_rate (dev,"fepll125" ,"xo" ,0 ,200000000 ); -clk_register_fixed_rate (dev,"fepll125dly" ,"xo" ,0 ,200000000 ); -clk_register_fixed_rate (dev,"fepllwcss2g" ,"xo" ,0 ,200000000 ); -clk_register_fixed_rate (dev,"fepllwcss5g" ,"xo" ,0 ,200000000 ); -clk_register_fixed_rate (dev,"fepll200" ,"xo" ,0 ,200000000 ); -clk_register_fixed_rate (dev,"fepll500" ,"xo" ,0 ,200000000 ); -clk_register_fixed_rate (dev,"ddrpllapss" ,"xo" ,0 ,666000000 ); -return qcom_cc_probe (pdev, &gcc_ipq4019_desc); }
include/dt-bindings/clock/qcom,gcc-ipq4019.h +9 -0
@@ -81,6 +81,15 @@#define GCC_WCSS5G_CLK 62#define GCC_WCSS5G_REF_CLK 63#define GCC_WCSS5G_RTC_CLK 64 +#define GCC_APPS_DDRPLL_VCO 65 +#define GCC_SDCC_PLLDIV_CLK 66 +#define GCC_FEPLL_VCO 67 +#define GCC_FEPLL125_CLK 68 +#define GCC_FEPLL125DLY_CLK 69 +#define GCC_FEPLL200_CLK 70 +#define GCC_FEPLL500_CLK 71 +#define GCC_FEPLL_WCSS2G_CLK 72 +#define GCC_FEPLL_WCSS5G_CLK 73#define WIFI0_CPU_INIT_RESET 0#define WIFI0_RADIO_SRIF_RESET 1
Signature · 7 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
Re: [PATCH v2 1/5] clk: qcom: ipq4019: Added the clock nodes and operations for pll
Quoted 31 lines · Abhishek Sahu
The current ipq4019 clock driver registered the PLL clocks and dividers as fixed clock. These fixed clock needs to be removed from driver probe function and same need to be registered with clock framework. These PLL clocks should be programmed only once and the same are being programmed already by the boot loader so the set rate operation is not required for these clocks. Only the rate can be calculated by clock operations in clock driver file so this patch adds the same.
The PLL takes the reference clock from XO and generates the intermediate VCO frequency. This VCO frequency will be divided down by different PLL internal dividers. Some of the PLL internal dividers are fixed while other are programmable.
This patch does the following changes.
1. Operation for calculating PLL intermediate VCO frequency by reading the reference clock divider and feedback divider from register. Since VCO frequency falls outside the limit of unsigned long for IPQ4019, so this operation will return the VCO frequency in KHz.2. Operation for calculating the internal PLL divider clock frequency. Clock Divider node should give either fixed divider value or divider table(maps the register divider value to actual divider value).
3. Adds and registers clock nodes for VCO(APPS DDR PLL and FE PLL) and PLL internal dividers(SDCC, FEPLL 500 Mhz, FEPLL 200 Mhz, FEPLL 125 Mhz, FEPLL 125 Mhz with delay, programmable WCSS 2G and 5G).
Quoted 7 lines
4. Changes the regmap limit from
0x2DFFFto0x2FFFFfor supporting the PLL registers read.5. Changes the fixed clock name to have consistency in all clock names
Signed-off-by Abhishek Sahu <[email protected]>
Thanks for fixing this up, just some minor things to fix.
drivers/clk/qcom/gcc-ipq4019.c line 23
Quoted 9 lines
drivers/clk/qcom/gcc-ipq4019.c +1
-0@@ -20,6 +20,11 @@#include <linux/clk-provider.h> #include <linux/regmap.h> #include <linux/reset-controller.h> +#include <linux/clk.h>
Is this include used?
drivers/clk/qcom/gcc-ipq4019.c line 24
Quoted 1 line
drivers/clk/qcom/gcc-ipq4019.c +1
-0+#include <linux/delay.h>
Is this include used?
drivers/clk/qcom/gcc-ipq4019.c line 27
Quoted 3 lines
drivers/clk/qcom/gcc-ipq4019.c +3
-0+#include <linux/bitops.h> +#include <linux/math64.h> +#include <asm/div64.h>
Are both includes needed?
Quoted 89 lines
drivers/clk/qcom/gcc-ipq4019.c +23
-9@@ -40,6 +55,20 @@ enum { P_DDRPLLAPSS, }; +struct clk_pll_vco { +u32 fdbkdiv_shift; +u32 fdbkdiv_width; +struct clk_regmap_div cdiv; +}; + +struct clk_pll_div { +u32 fixed_div; +const u8 *parent_map; +struct clk_regmap_div cdiv; +const struct clk_div_table *div_table; +const struct freq_tbl *freq_tbl; +}; +static struct parent_map gcc_xo_200_500_map[] = { {P_XO ,0 }, {P_FEPLL200 ,1 },@@ -48,8 +77,8 @@ static struct parent_map gcc_xo_200_500_map[] = {static const char *const gcc_xo_200_500[] = {"xo" , -"fepll200" , -"fepll500" , +"gcc_fepll200_clk" , +"gcc_fepll500_clk" , };static struct parent_map gcc_xo_200_map[] = {@@ -59,7 +88,7 @@ static struct parent_map gcc_xo_200_map[] = {static const char *const gcc_xo_200[] = {"xo" , -"fepll200" , +"gcc_fepll200_clk" , };static struct parent_map gcc_xo_200_spi_map[] = {@@ -69,7 +98,7 @@ static struct parent_map gcc_xo_200_spi_map[] = {static const char *const gcc_xo_200_spi[] = {"xo" , -"fepll200" , +"gcc_fepll200_clk" , };static struct parent_map gcc_xo_sdcc1_500_map[] = {@@ -80,8 +109,8 @@ static struct parent_map gcc_xo_sdcc1_500_map[] = {static const char *const gcc_xo_sdcc1_500[] = {"xo" , -"ddrpll" , -"fepll500" , +"gcc_apps_sdcc_clk" , +"gcc_fepll500_clk" , };static struct parent_map gcc_xo_wcss2g_map[] = {@@ -91,7 +120,7 @@ static struct parent_map gcc_xo_wcss2g_map[] = {static const char *const gcc_xo_wcss2g[] = {"xo" , -"fepllwcss2g" , +"gcc_fepllwcss2g_clk" , };static struct parent_map gcc_xo_wcss5g_map[] = {@@ -101,7 +130,7 @@ static struct parent_map gcc_xo_wcss5g_map[] = {static const char *const gcc_xo_wcss5g[] = {"xo" , -"fepllwcss5g" , +"gcc_fepllwcss5g_clk" , };static struct parent_map gcc_xo_125_dly_map[] = {@@ -111,7 +140,7 @@ static struct parent_map gcc_xo_125_dly_map[] = {static const char *const gcc_xo_125_dly[] = {"xo" , -"fepll125dly" , +"gcc_fepll125dly_clk" , };static struct parent_map gcc_xo_ddr_500_200_map[] = {
drivers/clk/qcom/gcc-ipq4019.c static struct parent_map gcc_xo_ddr_500_200_map[] = { · line 158
Quoted 9 lines
drivers/clk/qcom/gcc-ipq4019.c +2
-2@@ -123,8 +152,8 @@ static struct parent_map gcc_xo_ddr_500_200_map[] = {static const char *const gcc_xo_ddr_500_200[] = {"xo" , -"fepll200" , -"fepll500" , +"gcc_fepll200_clk" , +"gcc_fepll500_clk" ,"ddrpllapss" , };
Was it necessary to change all these names? The gcc_ prefix doesn't seem to be adding much besides noise to this patch.
drivers/clk/qcom/gcc-ipq4019.c static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk[] = { · line 538
Quoted 7 lines
drivers/clk/qcom/gcc-ipq4019.c +1
-1@@ -506,7 +535,7 @@ static const struct freq_tbl ftbl_gcc_sdcc1_apps_clk[] = {F (25000000 ,P_FEPLL500 ,1 ,1 ,20 ),F (50000000 ,P_FEPLL500 ,1 ,1 ,10 ),F (100000000 ,P_FEPLL500 ,1 ,1 ,5 ), - F(193000000, P_DDRPLL, 1, 0, 0), + F(190000000, P_DDRPLL, 1, 0, 0),
This looks like an unrelated bug fix.
Quoted 56 lines
drivers/clk/qcom/gcc-ipq4019.c +9
-5{ } };@@ -658,7 +687,7 @@ static struct clk_branch gcc_crypto_axi_clk = { .hw.init = &(struct clk_init_data){ .name ="gcc_crypto_axi_clk" , .parent_names = (const char *[]){ -"fepll125" , +"gcc_fepll125_clk" , }, .num_parents =1 , .ops = &clk_branch2_ops,@@ -675,7 +704,7 @@ static struct clk_branch gcc_crypto_clk = { .hw.init = &(struct clk_init_data){ .name ="gcc_crypto_clk" , .parent_names = (const char *[]){ -"fepll125" , +"gcc_fepll125_clk" , }, .num_parents =1 , .ops = &clk_branch2_ops,@@ -709,7 +738,7 @@ static struct clk_branch gcc_imem_axi_clk = { .hw.init = &(struct clk_init_data){ .name ="gcc_imem_axi_clk" , .parent_names = (const char *[]){ -"fepll200" , +"gcc_fepll200_clk" , }, .num_parents =1 , .ops = &clk_branch2_ops,@@ -773,7 +802,7 @@ static struct clk_branch gcc_pcie_axi_s_clk = { .hw.init = &(struct clk_init_data){ .name ="gcc_pcie_axi_s_clk" , .parent_names = (const char *[]){ -"fepll200" , +"gcc_fepll200_clk" , }, .num_parents =1 , .ops = &clk_branch2_ops,@@ -955,7 +984,7 @@ static struct clk_branch gcc_usb3_master_clk = { .hw.init = &(struct clk_init_data){ .name ="gcc_usb3_master_clk" , .parent_names = (const char *[]){ -"fepll125" , +"gcc_fepll125_clk" , }, .num_parents =1 , .ops = &clk_branch2_ops,@@ -1155,6 +1184,238 @@ static struct clk_branch gcc_wcss5g_rtc_clk = { }, }; +/* +* Calculates the rate from parent rate and divider and round the rate +* in Mhz. This function takes the parent rate in Khz and returns the +* rate in Hz.
drivers/clk/qcom/gcc-ipq4019.c static struct clk_branch gcc_wcss5g_rtc_clk = { · line 1200
Quoted 10 lines
drivers/clk/qcom/gcc-ipq4019.c +10
-0+*/ +static unsigned long clk_calc_divider_rate (unsigned long parent_rate, +unsigned int div) +{ +u64 rate = parent_rate; + +do_div (rate, div); + +/* +* This rate is in KHz so divide with 1000 and multiply with 1000000
s/KHz/kHz/
drivers/clk/qcom/gcc-ipq4019.c static struct clk_branch gcc_wcss5g_rtc_clk = { · line 1206
Quoted 6 lines
drivers/clk/qcom/gcc-ipq4019.c +6
-0+* to get the rate in Hz. +*/ +do_div (rate,1000 ); + rate *=1000000 ; + +return (unsigned long )rate;
Unnecessary cast, please remove.
Quoted 17 lines
drivers/clk/qcom/gcc-ipq4019.c +17
-0+} + +/* +* Calculates the VCO rate for PLL. +* VCO rate value is greater than unsigned long limit. Since this is an +* intermediate clock node for actual PLL dividers, so it returns the +* rate in Khz. The child nodes will return the value in Hz after its +* divide operation. +*/ +static unsigned long clk_regmap_vco_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct clk_pll_vco *rcg =to_clk_pll_vco (hw); +u32 fdbkdiv, refclkdiv, cdiv; +u64 vco; + +regmap_read (rcg->cdiv.clkr.regmap, rcg->cdiv.reg, &cdiv);
drivers/clk/qcom/gcc-ipq4019.c static struct clk_branch gcc_wcss5g_rtc_clk = { · line 1233
Quoted 10 lines
drivers/clk/qcom/gcc-ipq4019.c +10
-0+ refclkdiv = (cdiv >> rcg->cdiv.shift) & (BIT (rcg->cdiv.width) -1 ); + fdbkdiv = (cdiv >> rcg->fdbkdiv_shift) & (BIT (rcg->fdbkdiv_width) -1 ); + + vco = parent_rate; +do_div (vco, refclkdiv); +do_div (vco,1000 ); + vco *=2 ; + vco *= fdbkdiv; + +return (unsigned long )vco;
unnecessary cast.
drivers/clk/qcom/gcc-ipq4019.c static struct clk_branch gcc_wcss5g_rtc_clk = { · line 1236
Quoted 3 lines
drivers/clk/qcom/gcc-ipq4019.c +3
-0+} + +const struct clk_ops clk_regmap_vco_ops = {
static?
drivers/clk/qcom/gcc-ipq4019.c static struct clk_branch gcc_wcss5g_rtc_clk = { · line 1243
Quoted 7 lines
drivers/clk/qcom/gcc-ipq4019.c +7
-0+ .recalc_rate = clk_regmap_vco_recalc_rate, +}; + +static struct clk_pll_vco gcc_apps_ddrpll_vco = { + .fdbkdiv_shift =16 , + .fdbkdiv_width =8 , + .cdiv.reg =0x2E020 ,
lowercase hex please.
Quoted 23 lines
drivers/clk/qcom/gcc-ipq4019.c +23
-0+ .cdiv.shift = 24, + .cdiv.width = 5, + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_apps_ddrpll_vco" , + .parent_names = (const char *[]){ +"xo" , + }, + .num_parents =1 , + .ops = &clk_regmap_vco_ops, + }, + }, +}; + +static struct clk_pll_vco gcc_fepll_vco = { + .fdbkdiv_shift =16 , + .fdbkdiv_width =8 , + .cdiv.reg =0x2F020 , + .cdiv.shift =24 , + .cdiv.width =5 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepll_vco" ,
drivers/clk/qcom/gcc-ipq4019.c static struct clk_branch gcc_wcss5g_rtc_clk = { · line 1276
Quoted 10 lines
drivers/clk/qcom/gcc-ipq4019.c +10
-0+ .parent_names = (const char *[]){ + "xo", + }, + .num_parents = 1, + .ops = &clk_regmap_vco_ops, + }, + }, +}; + +/* Calculates the rate for PLL divider.
Style nit, /* goes on its own line
Quoted 17 lines
drivers/clk/qcom/gcc-ipq4019.c +17
-0+* If the divider value is not fixed then it gets the actual divider value +* from divider table. Then, it calculate the clock rate by dividing the +* parent rate with actual divider value. +*/ +static unsigned long clk_regmap_clk_div_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct clk_pll_div *rcg =to_clk_pll_div (hw); +u32 cdiv, pre_div =1 ; +const struct clk_div_table *clkt; + +if (rcg->fixed_div) { + pre_div = rcg->fixed_div; + }else { +regmap_read (rcg->cdiv.clkr.regmap, rcg->cdiv.reg, &cdiv); + cdiv = (cdiv >> rcg->cdiv.shift) & (BIT (rcg->cdiv.width) -1 ); +
drivers/clk/qcom/gcc-ipq4019.c static struct clk_branch gcc_wcss5g_rtc_clk = { · line 1303
Quoted 10 lines
drivers/clk/qcom/gcc-ipq4019.c +10
-0+for (clkt = rcg->div_table; clkt->div; clkt++) { +if (clkt->val == cdiv) + pre_div = clkt->div; + } + } + +return clk_calc_divider_rate (parent_rate, pre_div); +}; + +const struct clk_ops clk_regmap_clk_div_ops = {
static?
Quoted 68 lines
drivers/clk/qcom/gcc-ipq4019.c +68
-0+ .recalc_rate = clk_regmap_clk_div_recalc_rate, +}; + +static struct clk_pll_div gcc_apps_sdcc_clk = { + .fixed_div =28 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_apps_sdcc_clk" , + .parent_names = (const char *[]){ +"gcc_apps_ddrpll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, +}; + +static struct clk_pll_div gcc_fepll125_clk = { + .fixed_div =32 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepll125_clk" , + .parent_names = (const char *[]){ +"gcc_fepll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, +}; + +static struct clk_pll_div gcc_fepll125dly_clk = { + .fixed_div =32 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepll125dly_clk" , + .parent_names = (const char *[]){ +"gcc_fepll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, +}; + +static struct clk_pll_div gcc_fepll200_clk = { + .fixed_div =20 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepll200_clk" , + .parent_names = (const char *[]){ +"gcc_fepll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, +}; + +static struct clk_pll_div gcc_fepll500_clk = { + .fixed_div =8 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepll500_clk" , + .parent_names = (const char *[]){ +"gcc_fepll_vco" , + }, + .num_parents =1 ,
drivers/clk/qcom/gcc-ipq4019.c static struct clk_branch gcc_wcss5g_rtc_clk = { · line 1381
Quoted 10 lines
drivers/clk/qcom/gcc-ipq4019.c +10
-0+ .ops = &clk_regmap_clk_div_ops, + }, + }, +}; + +static const struct clk_div_table fepllwcss_clk_div_table[] = { + {0 ,15 }, + {1 ,16 }, + {2 ,18 }, + {3, 20},
Nitpick: Please add some spaces here
{ 0, 15 },Quoted 57 lines
drivers/clk/qcom/gcc-ipq4019.c +46
-0+ {}, +}; + +static struct clk_pll_div gcc_fepllwcss2g_clk = { + .cdiv.reg =0x2F020 , + .cdiv.shift =8 , + .cdiv.width =2 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepllwcss2g_clk" , + .parent_names = (const char *[]){ +"gcc_fepll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, + .div_table = fepllwcss_clk_div_table +}; + +static struct clk_pll_div gcc_fepllwcss5g_clk = { + .cdiv.reg =0x2F020 , + .cdiv.shift =12 , + .cdiv.width =2 , + .cdiv.clkr = { + .hw.init = &(struct clk_init_data ){ + .name ="gcc_fepllwcss5g_clk" , + .parent_names = (const char *[]){ +"gcc_fepll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_clk_div_ops, + }, + }, + .div_table = fepllwcss_clk_div_table +}; +static struct clk_regmap *gcc_ipq4019_clocks[] = { [AUDIO_CLK_SRC ] = &audio_clk_src.clkr, [BLSP1_QUP1_I2C_APPS_CLK_SRC ] = &blsp1_qup1_i2c_apps_clk_src.clkr,@@ -1215,6 +1476,15 @@ static struct clk_regmap *gcc_ipq4019_clocks[] = { [GCC_WCSS5G_CLK] = &gcc_wcss5g_clk.clkr, [GCC_WCSS5G_REF_CLK] = &gcc_wcss5g_ref_clk.clkr, [GCC_WCSS5G_RTC_CLK] = &gcc_wcss5g_rtc_clk.clkr, + [GCC_APPS_DDRPLL_VCO] = &gcc_apps_ddrpll_vco.cdiv.clkr, + [GCC_FEPLL_VCO] = &gcc_fepll_vco.cdiv.clkr, + [GCC_SDCC_PLLDIV_CLK] = &gcc_apps_sdcc_clk.cdiv.clkr, + [GCC_FEPLL125_CLK] = &gcc_fepll125_clk.cdiv.clkr, + [GCC_FEPLL125DLY_CLK] = &gcc_fepll125dly_clk.cdiv.clkr, + [GCC_FEPLL200_CLK] = &gcc_fepll200_clk.cdiv.clkr, + [GCC_FEPLL500_CLK] = &gcc_fepll500_clk.cdiv.clkr, + [GCC_FEPLL_WCSS2G_CLK] = &gcc_fepllwcss2g_clk.cdiv.clkr, + [GCC_FEPLL_WCSS5G_CLK] = &gcc_fepllwcss5g_clk.cdiv.clkr, };static const struct qcom_reset_map gcc_ipq4019_resets[] = {
drivers/clk/qcom/gcc-ipq4019.c static const struct regmap_config gcc_ipq4019_regmap_config = { · line 1568
Quoted 5 lines
drivers/clk/qcom/gcc-ipq4019.c +1
-1@@ -1295,7 +1565,7 @@ static const struct regmap_config gcc_ipq4019_regmap_config = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .max_register = 0x2dfff, + .max_register = 0x2FFFF,
Lowercase.
Quoted 2 lines
drivers/clk/qcom/gcc-ipq4019.c +0
-0.fast_io = true, };
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v2 2/5 clk: qcom: ipq4019: Added the apss cpu pll divider clock node
In reply to Abhishek SahuThe current ipq4019 clock driver does not have support for all the frequency supported by APPS CPU. APPS CPU frequency is provided with APSS CPU PLL divider which divides down the VCO frequency. This divider is nonlinear and specific to IPQ4019 so the standard divider code cannot be used for this.
This patch registers new clock node and adds its clock operations for APPS CPU clock divider. Since, this divider is nonlinear, so frequency table is also added for this, which contains the frequency and its corresponding hardware divider values.
--- drivers/clk/qcom/gcc-ipq4019.c | 112 ++++++++++++++++++++++++++- include/dt-bindings/clock/qcom,gcc-ipq4019.h | 1 + 2 files changed, 112 insertions(+), 1 deletion(-)
drivers/clk/qcom/gcc-ipq4019.c +111 -1
@@ -154,7 +154,7 @@ static const char * const gcc_xo_ddr_500_200[] = {"xo" ,"gcc_fepll200_clk" ,"gcc_fepll500_clk" , - "ddrpllapss", + "gcc_apps_cpu_plldiv_clk", };#define F (f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }
@@ -571,6 +571,7 @@ static struct clk_rcg2 apps_clk_src = { .parent_names = gcc_xo_ddr_500_200, .num_parents = 4, .ops = &clk_rcg2_ops, + .flags = CLK_SET_RATE_PARENT, }, };
@@ -1273,6 +1274,114 @@ static struct clk_pll_vco gcc_fepll_vco = { }, }; +/* +* Round rate function for APPS CPU PLL Clock divider. +* It Returns the input rate without changing it. The hardware supported rate +* will be calculated in set function by getting the same from frequency table. +*/ +static long clk_cpu_div_round_rate (struct clk_hw *hw,unsigned long rate, +unsigned long *p_rate) +{ +return rate; +}; + +/* +* Clock set rate function for APPS CPU PLL Clock divider. +* It looks up the frequency table and updates the PLL divider to corresponding +* divider value. +*/ +static int clk_cpu_div_set_rate (struct clk_hw *hw,unsigned long rate, +unsigned long parent_rate) +{ +struct clk_pll_div *rcg =to_clk_pll_div (hw); +const struct freq_tbl *f; +u32 mask; +int ret; + + f =qcom_find_freq (rcg->freq_tbl, rate); +if (!f) +return -EINVAL ; + + mask = (BIT (rcg->cdiv.width) -1 ) << rcg->cdiv.shift; + ret =regmap_update_bits (rcg->cdiv.clkr.regmap, + rcg->cdiv.reg, mask, + (f->pre_div << rcg->cdiv.shift) & mask); +udelay (1 ); + +return 0 ; +}; + +/* +* Clock frequency calculation function for APPS CPU PLL Clock divider. +* This clock divider is nonlinear so this function calculates the actual +* divider and returns the output frequency by dividing VCO Frequency +* with this actual divider value. +*/ +static unsigned long clk_cpu_div_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct clk_pll_div *rcg =to_clk_pll_div (hw); +u32 cdiv, pre_div; + +regmap_read (rcg->cdiv.clkr.regmap, rcg->cdiv.reg, &cdiv); + cdiv = (cdiv >> rcg->cdiv.shift) & (BIT (rcg->cdiv.width) -1 ); + +/* +* Some dividers have value in 0.5 fraction so multiply both VCO +* frequency(parent_rate) and pre_div with 2 to make integer +* calculation. +*/ +if (cdiv >10 ) + pre_div = (cdiv +1 ) *2 ; +else + pre_div = cdiv +12 ; + +return clk_calc_divider_rate (parent_rate *2 , pre_div); +}; + +const struct clk_ops clk_regmap_cpu_div_ops = { + .round_rate = clk_cpu_div_round_rate, + .set_rate = clk_cpu_div_set_rate, + .recalc_rate = clk_cpu_div_recalc_rate, +}; + +static const struct freq_tbl ftbl_apps_ddr_pll[] = { + {380000000 ,P_XO ,0xD ,0 }, + {409000000 ,P_XO ,0xC ,0 ,0 }, + {444000000 ,P_XO ,0xB ,0 ,0 }, + {484000000 ,P_XO ,0xA ,0 ,0 }, + {507000000 ,P_XO ,0x9 ,0 ,0 }, + {532000000 ,P_XO ,0x8 ,0 ,0 }, + {560000000 ,P_XO ,0x7 ,0 ,0 }, + {592000000 ,P_XO ,0x6 ,0 ,0 }, + {626000000 ,P_XO ,0x5 ,0 ,0 }, + {666000000 ,P_XO ,0x4 ,0 ,0 }, + {710000000 ,P_XO ,0x3 ,0 ,0 }, + {761000000 ,P_XO ,0x2 ,0 ,0 }, + {819000000 ,P_XO ,0x1 ,0 ,0 }, + {888000000 ,P_XO ,0x0 ,0 ,0 }, + {} +}; + +static struct clk_pll_div gcc_apps_cpu_plldiv_clk = { + .cdiv.reg =0x2E020 , + .cdiv.shift =4 , + .cdiv.width =4 , + .cdiv.clkr = { + .enable_reg =0x2E000 , + .enable_mask =BIT (0 ), + .hw.init = &(struct clk_init_data ){ + .name ="gcc_apps_cpu_plldiv_clk" , + .parent_names = (const char *[]){ +"gcc_apps_ddrpll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_cpu_div_ops, + }, + }, + .freq_tbl = ftbl_apps_ddr_pll, +}; +/* Calculates the rate for PLL divider. * If the divider value is not fixed then it gets the actual divider value * from divider table. Then, it calculate the clock rate by dividing the
@@ -1485,6 +1594,7 @@ static struct clk_regmap *gcc_ipq4019_clocks[] = { [GCC_FEPLL500_CLK] = &gcc_fepll500_clk.cdiv.clkr, [GCC_FEPLL_WCSS2G_CLK] = &gcc_fepllwcss2g_clk.cdiv.clkr, [GCC_FEPLL_WCSS5G_CLK] = &gcc_fepllwcss5g_clk.cdiv.clkr, + [GCC_APPS_CPU_PLLDIV_CLK] = &gcc_apps_cpu_plldiv_clk.cdiv.clkr, };static const struct qcom_reset_map gcc_ipq4019_resets[] = {
include/dt-bindings/clock/qcom,gcc-ipq4019.h +1 -0
@@ -90,6 +90,7 @@#define GCC_FEPLL500_CLK 71#define GCC_FEPLL_WCSS2G_CLK 72#define GCC_FEPLL_WCSS5G_CLK 73 +#define GCC_APPS_CPU_PLLDIV_CLK 74#define WIFI0_CPU_INIT_RESET 0#define WIFI0_RADIO_SRIF_RESET 1
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH v2 2/5] clk: qcom: ipq4019: Added the apss cpu pll divider clock node
Quoted 24 lines · Abhishek Sahu
drivers/clk/qcom/gcc-ipq4019.c +2
-1@@ -154,7 +154,7 @@ static const char * const gcc_xo_ddr_500_200[] = {"xo" ,"gcc_fepll200_clk" ,"gcc_fepll500_clk" , - "ddrpllapss", + "gcc_apps_cpu_plldiv_clk", };#define F (f, s, h, m, n) { (f), (s), (2 * (h) - 1), (m), (n) }@@ -571,6 +571,7 @@ static struct clk_rcg2 apps_clk_src = { .parent_names = gcc_xo_ddr_500_200, .num_parents = 4, .ops = &clk_rcg2_ops, + .flags = CLK_SET_RATE_PARENT, }, };@@ -1273,6 +1274,114 @@ static struct clk_pll_vco gcc_fepll_vco = { }, };
drivers/clk/qcom/gcc-ipq4019.c static struct clk_pll_vco gcc_fepll_vco = { · line 1285
Quoted 10 lines
drivers/clk/qcom/gcc-ipq4019.c +9
-0+/* +* Round rate function for APPS CPU PLL Clock divider. +* It Returns the input rate without changing it. The hardware supported rate +* will be calculated in set function by getting the same from frequency table. +*/ +static long clk_cpu_div_round_rate (struct clk_hw *hw,unsigned long rate, +unsigned long *p_rate) +{ +return rate;
This needs to calculate what the rate will be if clk_set_rate() is called with the rate parameter. Blindly returning rate back is not acceptable.
Quoted 13 lines
drivers/clk/qcom/gcc-ipq4019.c +13
-0+}; + +/* +* Clock set rate function for APPS CPU PLL Clock divider. +* It looks up the frequency table and updates the PLL divider to corresponding +* divider value. +*/ +static int clk_cpu_div_set_rate (struct clk_hw *hw,unsigned long rate, +unsigned long parent_rate) +{ +struct clk_pll_div *rcg =to_clk_pll_div (hw); +const struct freq_tbl *f; +u32 mask;
drivers/clk/qcom/gcc-ipq4019.c static struct clk_pll_vco gcc_fepll_vco = { · line 1308
Quoted 10 lines
drivers/clk/qcom/gcc-ipq4019.c +10
-0+int ret; + + f =qcom_find_freq (rcg->freq_tbl, rate); +if (!f) +return -EINVAL ; + + mask = (BIT (rcg->cdiv.width) -1 ) << rcg->cdiv.shift; + ret =regmap_update_bits (rcg->cdiv.clkr.regmap, + rcg->cdiv.reg, mask, + (f->pre_div << rcg->cdiv.shift) & mask);
Why are we masking a value that will be masked byregmap_update_bits()?
drivers/clk/qcom/gcc-ipq4019.c static struct clk_pll_vco gcc_fepll_vco = { · line 1309
Quoted 1 line
drivers/clk/qcom/gcc-ipq4019.c +1
-0+udelay (1 );
Why do we wait? Please add some comment.
Quoted 23 lines
drivers/clk/qcom/gcc-ipq4019.c +23
-0+ +return 0 ; +}; + +/* +* Clock frequency calculation function for APPS CPU PLL Clock divider. +* This clock divider is nonlinear so this function calculates the actual +* divider and returns the output frequency by dividing VCO Frequency +* with this actual divider value. +*/ +static unsigned long clk_cpu_div_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct clk_pll_div *rcg =to_clk_pll_div (hw); +u32 cdiv, pre_div; + +regmap_read (rcg->cdiv.clkr.regmap, rcg->cdiv.reg, &cdiv); + cdiv = (cdiv >> rcg->cdiv.shift) & (BIT (rcg->cdiv.width) -1 ); + +/* +* Some dividers have value in 0.5 fraction so multiply both VCO +* frequency(parent_rate) and pre_div with 2 to make integer +* calculation.
drivers/clk/qcom/gcc-ipq4019.c static struct clk_pll_vco gcc_fepll_vco = { · line 1342
Quoted 10 lines
drivers/clk/qcom/gcc-ipq4019.c +10
-0+*/ +if (cdiv >10 ) + pre_div = (cdiv +1 ) *2 ; +else + pre_div = cdiv +12 ; + +return clk_calc_divider_rate (parent_rate *2 , pre_div); +}; + +const struct clk_ops clk_regmap_cpu_div_ops = {
static?
Quoted 10 lines
drivers/clk/qcom/gcc-ipq4019.c +10
-0+ .round_rate = clk_cpu_div_round_rate, + .set_rate = clk_cpu_div_set_rate, + .recalc_rate = clk_cpu_div_recalc_rate, +}; + +static const struct freq_tbl ftbl_apps_ddr_pll[] = { + {380000000 ,P_XO ,0xD ,0 }, + {409000000 ,P_XO ,0xC ,0 ,0 }, + {444000000 ,P_XO ,0xB ,0 ,0 }, + {484000000 ,P_XO ,0xA ,0 ,0 },
drivers/clk/qcom/gcc-ipq4019.c static struct clk_pll_vco gcc_fepll_vco = { · line 1362
Quoted 10 lines
drivers/clk/qcom/gcc-ipq4019.c +10
-0+ {507000000, P_XO, 0x9, 0, 0}, + {532000000, P_XO, 0x8, 0, 0}, + {560000000, P_XO, 0x7, 0, 0}, + {592000000, P_XO, 0x6, 0, 0}, + {626000000, P_XO, 0x5, 0, 0}, + {666000000, P_XO, 0x4, 0, 0}, + {710000000, P_XO, 0x3, 0, 0}, + {761000000, P_XO, 0x2, 0, 0}, + {819000000, P_XO, 0x1, 0, 0}, + {888000000, P_XO, 0x0, 0, 0},
Style nits from before apply here (lowercase, spaces)
Quoted 21 lines
drivers/clk/qcom/gcc-ipq4019.c +21
-0+ {} +}; + +static struct clk_pll_div gcc_apps_cpu_plldiv_clk = { + .cdiv.reg =0x2E020 , + .cdiv.shift =4 , + .cdiv.width =4 , + .cdiv.clkr = { + .enable_reg =0x2E000 , + .enable_mask =BIT (0 ), + .hw.init = &(struct clk_init_data ){ + .name ="gcc_apps_cpu_plldiv_clk" , + .parent_names = (const char *[]){ +"gcc_apps_ddrpll_vco" , + }, + .num_parents =1 , + .ops = &clk_regmap_cpu_div_ops, + }, + }, + .freq_tbl = ftbl_apps_ddr_pll, +};
Signature · 6 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
PATCH v2 3/5 clk: qcom: ipq4019: Added the nodes for pcnoc
In reply to Abhishek SahuThe current ipq4019 clock driver does not have the node for PCNOC so this patch adds and registers the PCNOC clock nodes.
--- drivers/clk/qcom/gcc-ipq4019.c | 37 ++++++++++++++++++++++++++++ include/dt-bindings/clock/qcom,gcc-ipq4019.h | 1 + 2 files changed, 38 insertions(+)
drivers/clk/qcom/gcc-ipq4019.c +37 -0
@@ -1525,6 +1525,41 @@ static struct clk_pll_div gcc_fepllwcss5g_clk = { .div_table = fepllwcss_clk_div_table }; +static const struct freq_tbl ftbl_gcc_pcnoc_ahb_clk[] = { +F (48000000 ,P_XO ,1 ,0 ,0 ), +F (100000000 ,P_FEPLL200 ,2 ,0 ,0 ), + { } +}; + +static struct clk_rcg2 gcc_pcnoc_ahb_clk_src = { + .cmd_rcgr =0x21024 , + .hid_width =5 , + .parent_map = gcc_xo_200_500_map, + .freq_tbl = ftbl_gcc_pcnoc_ahb_clk, + .clkr.hw.init = &(struct clk_init_data ){ + .name ="gcc_pcnoc_ahb_clk_src" , + .parent_names = gcc_xo_200_500, + .num_parents =3 , + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch pcnoc_clk_src = { + .halt_reg =0x21030 , + .clkr = { + .enable_reg =0x21030 , + .enable_mask =BIT (0 ), + .hw.init = &(struct clk_init_data ){ + .name ="pcnoc_clk_src" , + .parent_names = (const char *[]){ +"gcc_pcnoc_ahb_clk_src" , + }, + .num_parents =1 , + .ops = &clk_branch2_ops, + }, + }, +}; +static struct clk_regmap *gcc_ipq4019_clocks[] = { [AUDIO_CLK_SRC ] = &audio_clk_src.clkr, [BLSP1_QUP1_I2C_APPS_CLK_SRC ] = &blsp1_qup1_i2c_apps_clk_src.clkr,
@@ -1595,6 +1630,8 @@ static struct clk_regmap *gcc_ipq4019_clocks[] = { [GCC_FEPLL_WCSS2G_CLK] = &gcc_fepllwcss2g_clk.cdiv.clkr, [GCC_FEPLL_WCSS5G_CLK] = &gcc_fepllwcss5g_clk.cdiv.clkr, [GCC_APPS_CPU_PLLDIV_CLK] = &gcc_apps_cpu_plldiv_clk.cdiv.clkr, + [GCC_PCNOC_AHB_CLK_SRC] = &gcc_pcnoc_ahb_clk_src.clkr, + [GCC_PCNOC_AHB_CLK] = &pcnoc_clk_src.clkr, };static const struct qcom_reset_map gcc_ipq4019_resets[] = {
include/dt-bindings/clock/qcom,gcc-ipq4019.h +1 -0
@@ -91,6 +91,7 @@#define GCC_FEPLL_WCSS2G_CLK 72#define GCC_FEPLL_WCSS5G_CLK 73#define GCC_APPS_CPU_PLLDIV_CLK 74 +#define GCC_PCNOC_AHB_CLK_SRC 75#define WIFI0_CPU_INIT_RESET 0#define WIFI0_RADIO_SRIF_RESET 1
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH v2 3/5] clk: qcom: ipq4019: Added the nodes for pcnoc
Quoted 5 lines · Abhishek Sahu
The current ipq4019 clock driver does not have the node for PCNOC so this patch adds and registers the PCNOC clock nodes.
Signed-off-by Abhishek Sahu <[email protected]>---
This one looks good.
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v2 4/5 clk: qcom: ipq4019: Added the all frequencies for apps cpu
In reply to Abhishek SahuThe APPS CPU clock does not contain all the frequencies in its frequency table so this patch adds the same.
--- drivers/clk/qcom/gcc-ipq4019.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-)
drivers/clk/qcom/gcc-ipq4019.c +15 -5
@@ -554,11 +554,21 @@ static struct clk_rcg2 sdcc1_apps_clk_src = { };static const struct freq_tbl ftbl_gcc_apps_clk[] = { -F (48000000 ,P_XO ,1 ,0 ,0 ), -F (200000000 ,P_FEPLL200 ,1 ,0 ,0 ), -F (500000000 ,P_FEPLL500 ,1 ,0 ,0 ), -F (626000000 ,P_DDRPLLAPSS ,1 ,0 ,0 ), - { } + {48000000 ,P_XO ,1 ,0 ,0 }, + {200000000 ,P_FEPLL200 ,1 ,0 ,0 }, + {380000000 ,P_DDRPLLAPSS ,1 ,0 ,0 }, + {409000000 ,P_DDRPLLAPSS ,1 ,0 ,0 }, + {444000000 ,P_DDRPLLAPSS ,1 ,0 ,0 }, + {484000000 ,P_DDRPLLAPSS ,1 ,0 ,0 }, + {500000000 ,P_FEPLL500 ,1 ,0 ,0 }, + {507000000 ,P_DDRPLLAPSS ,1 ,0 ,0 }, + {532000000 ,P_DDRPLLAPSS ,1 ,0 ,0 }, + {560000000 ,P_DDRPLLAPSS ,1 ,0 ,0 }, + {592000000 ,P_DDRPLLAPSS ,1 ,0 ,0 }, + {626000000 ,P_DDRPLLAPSS ,1 ,0 ,0 }, + {666000000 ,P_DDRPLLAPSS ,1 ,0 ,0 }, + {710000000 ,P_DDRPLLAPSS ,1 ,0 ,0 }, + {} };static struct clk_rcg2 apps_clk_src = {
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH v2 4/5] clk: qcom: ipq4019: Added the all frequencies for apps cpu
Quoted 13 lines · Abhishek Sahu
drivers/clk/qcom/gcc-ipq4019.c +4
-5@@ -554,11 +554,21 @@ static struct clk_rcg2 sdcc1_apps_clk_src = { };static const struct freq_tbl ftbl_gcc_apps_clk[] = { -F (48000000 ,P_XO ,1 ,0 ,0 ), -F (200000000 ,P_FEPLL200 ,1 ,0 ,0 ), -F (500000000 ,P_FEPLL500 ,1 ,0 ,0 ), -F (626000000 ,P_DDRPLLAPSS ,1 ,0 ,0 ), - { } + {48000000 ,P_XO ,1 ,0 ,0 }, + {200000000 ,P_FEPLL200 ,1 ,0 ,0 }, + {380000000 ,P_DDRPLLAPSS ,1 ,0 ,0 }, + {409000000 ,P_DDRPLLAPSS ,1 ,0 ,0 },
drivers/clk/qcom/gcc-ipq4019.c static struct clk_rcg2 sdcc1_apps_clk_src = { · line 570
Quoted 10 lines
drivers/clk/qcom/gcc-ipq4019.c +10
-0+ {444000000, P_DDRPLLAPSS, 1, 0, 0}, + {484000000, P_DDRPLLAPSS, 1, 0, 0}, + {500000000, P_FEPLL500, 1, 0, 0}, + {507000000, P_DDRPLLAPSS, 1, 0, 0}, + {532000000, P_DDRPLLAPSS, 1, 0, 0}, + {560000000, P_DDRPLLAPSS, 1, 0, 0}, + {592000000, P_DDRPLLAPSS, 1, 0, 0}, + {626000000, P_DDRPLLAPSS, 1, 0, 0}, + {666000000, P_DDRPLLAPSS, 1, 0, 0}, + {710000000, P_DDRPLLAPSS, 1, 0, 0},
Please keep using F macro just in case the hid changes.
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH v2 5/5 clk: qcom: ipq4019: Added the cpu clock frequency change notifier
In reply to Abhishek SahuThe current driver code gives the crash or gets hang while switching the CPU frequency some time. The APSS CPU Clock divider is not glitch free so it the APPS clock need to be switched for stable clock during the change.
This patch adds the frequency change notifier for APSS CPU clock. It changes the parent of this clock to stable PLL FEPLL500 when it gets for PRE_RATE_CHANGE event. This event will be generated before actual clock set operations. The clock set operation will again change its corresponding parent by getting the same from frequency table.
--- drivers/clk/qcom/gcc-ipq4019.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-)
drivers/clk/qcom/gcc-ipq4019.c +26 -1
@@ -1740,9 +1740,34 @@ static const struct of_device_id gcc_ipq4019_match_table[] = { };MODULE_DEVICE_TABLE (of, gcc_ipq4019_match_table); +int cpu_clk_notifier_fn (struct notifier_block *nb, +unsigned long action,void *data) +{ +int err =0 ; + +if (action ==PRE_RATE_CHANGE ) { + err = clk_rcg2_ops.set_parent (&apps_clk_src.clkr.hw, +P_FEPLL500 ); + } + +return notifier_from_errno (err); +} + +struct notifier_block cpu_clk_notifier = { + .notifier_call = cpu_clk_notifier_fn, +}; +static int gcc_ipq4019_probe (struct platform_device *pdev) { -return qcom_cc_probe (pdev, &gcc_ipq4019_desc); +int err; + + err =qcom_cc_probe (pdev, &gcc_ipq4019_desc); + +if (!err) +clk_notifier_register (apps_clk_src.clkr.hw.clk, + &cpu_clk_notifier); + +return err; }static struct platform_driver gcc_ipq4019_driver = {
Signature · 2 lines
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project
Re: [PATCH v2 5/5] clk: qcom: ipq4019: Added the cpu clock frequency change notifier
Quoted 21 lines · Abhishek Sahu
The current driver code gives the crash or gets hang while switching the CPU frequency some time. The APSS CPU Clock divider is not glitch free so it the APPS clock need to be switched for stable clock during the change.
This patch adds the frequency change notifier for APSS CPU clock. It changes the parent of this clock to stable PLL FEPLL500 when it gets for
PRE_RATE_CHANGEevent. This event will be generated before actual clock set operations. The clock set operation will again change its corresponding parent by getting the same from frequency table.Signed-off-by Abhishek Sahu <[email protected]>--- drivers/clk/qcom/gcc-ipq4019.c | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-)drivers/clk/qcom/gcc-ipq4019.c +0
-0
drivers/clk/qcom/gcc-ipq4019.c static const struct of_device_id gcc_ipq4019_match_table[] = { · line 1743
Quoted 4 lines
drivers/clk/qcom/gcc-ipq4019.c +1
-0@@ -1740,9 +1740,34 @@ static const struct of_device_id gcc_ipq4019_match_table[] = { };MODULE_DEVICE_TABLE (of, gcc_ipq4019_match_table); +int cpu_clk_notifier_fn(struct notifier_block *nb,
static?
Please come up with a better name that is not so generic.
drivers/clk/qcom/gcc-ipq4019.c static const struct of_device_id gcc_ipq4019_match_table[] = { · line 1756
Quoted 13 lines
drivers/clk/qcom/gcc-ipq4019.c +13
-0+ unsigned long action, void *data) +{ + int err = 0; + +if (action ==PRE_RATE_CHANGE ) { + err = clk_rcg2_ops.set_parent (&apps_clk_src.clkr.hw, +P_FEPLL500 ); + } + +return notifier_from_errno (err); +} + +struct notifier_block cpu_clk_notifier = {
static?
drivers/clk/qcom/gcc-ipq4019.c static const struct of_device_id gcc_ipq4019_match_table[] = { · line 1768
Quoted 13 lines
drivers/clk/qcom/gcc-ipq4019.c +10
-1+ .notifier_call = cpu_clk_notifier_fn, +}; +static int gcc_ipq4019_probe (struct platform_device *pdev) { -return qcom_cc_probe (pdev, &gcc_ipq4019_desc); +int err; + + err =qcom_cc_probe (pdev, &gcc_ipq4019_desc); + +if (!err) +clk_notifier_register (apps_clk_src.clkr.hw.clk, + &cpu_clk_notifier);
This has to be unregistered on driver remove.
Quoted 3 lines
drivers/clk/qcom/gcc-ipq4019.c +2
-0+ +return err; }
Signature · 6 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html