dt-bindings: zx296718-clk: add compatible for audio clock controller
Subsystems (2)
PATCH 1/2 dt-bindings: zx296718-clk: add compatible for audio clock controller
From: Shawn Guo <[email protected]>
It adds the compatible string for zx296718 audio clock controller.
--- Documentation/devicetree/bindings/clock/zx296718-clk.txt | 3 +++ 1 file changed, 3 insertions(+)
Documentation/devicetree/bindings/clock/zx296718-clk.txt +3 -0
@@ -13,6 +13,9 @@ Required properties: "zte,zx296718-lsp1crm": zx296718 device level clock selection and gating + "zte,zx296718-audiocrm": + zx296718 audio clock selection, divider and gating + - reg: Address and length of the register set The clock consumer should specify the desired clock by having the clock
Signature · 1 line
1.9.1
PATCH 2/2 clk: zte: add audio clocks for zx296718
From: Jun Nie <[email protected]>
The audio related clock support is missing from the existing zx296718 clock driver. Let's add it, so that the upstream ZX SPDIF driver can work for HDMI audio support.
--- drivers/clk/zte/clk-zx296718.c | 150 +++++++++++++++++++++++++++++++++++++++++ drivers/clk/zte/clk.c | 149 ++++++++++++++++++++++++++++++++++++++++ drivers/clk/zte/clk.h | 28 ++++++++ 3 files changed, 327 insertions(+)
drivers/clk/zte/clk-zx296718.c +150 -0
@@ -888,10 +888,160 @@ static int __init lsp1_clocks_init(struct device_node *np)return 0 ; } +PNAME (audio_wclk_common_p) = { +"audio_99m" , +"audio_24m" , +}; + +PNAME (audio_timer_p) = { +"audio_24m" , +"audio_32k" , +}; + +static struct zx_clk_mux audio_mux_clk[] = { +MUX (0 ,"i2s0_wclk_mux" , audio_wclk_common_p,AUDIO_I2S0_CLK ,0 ,1 ), +MUX (0 ,"i2s1_wclk_mux" , audio_wclk_common_p,AUDIO_I2S1_CLK ,0 ,1 ), +MUX (0 ,"i2s2_wclk_mux" , audio_wclk_common_p,AUDIO_I2S2_CLK ,0 ,1 ), +MUX (0 ,"i2s3_wclk_mux" , audio_wclk_common_p,AUDIO_I2S3_CLK ,0 ,1 ), +MUX (0 ,"i2c0_wclk_mux" , audio_wclk_common_p,AUDIO_I2C0_CLK ,0 ,1 ), +MUX (0 ,"spdif0_wclk_mux" , audio_wclk_common_p,AUDIO_SPDIF0_CLK ,0 ,1 ), +MUX (0 ,"spdif1_wclk_mux" , audio_wclk_common_p,AUDIO_SPDIF1_CLK ,0 ,1 ), +MUX (0 ,"timer_wclk_mux" , audio_timer_p,AUDIO_TIMER_CLK ,0 ,1 ), +}; + +struct zx_clk_audio_div_table i2s_wclk_div_table[] = { + {2048000 ,0x3000030 ,0xffff5700 }, + {4096000 ,0x3000018 ,0xffff2b80 }, + {2822400 ,0x3000011 ,0xffff89cb }, + {3072000 ,0x3000010 ,0xffff1d00 }, + {4096000 ,0x300000c ,0xffff15c0 }, + {5644800 ,0x3000008 ,0xffffc4e5 }, + {6144000 ,0x3000008 ,0xffff0e80 }, + {11289600 ,0x3000004 ,0xffff6273 }, + {12288000 ,0x3000004 ,0xffff0740 }, + {22579200 ,0x3000002 ,0xffff3139 }, + {24576000 ,0x3000002 ,0xffff03a0 }, +}; + +struct zx_clk_audio_div_table spdif_wclk_div_table[] = { + {2822400 ,0x00023 ,0xffff1397 }, + {3072000 ,0x00020 ,0xffff3a00 }, + {4096000 ,0x00018 ,0xffff2b80 }, + {5644800 ,0x00011 ,0xffff89cb }, + {6144000 ,0x00010 ,0xffff1d00 }, + {11289600 ,0x00008 ,0xffffc4e5 }, + {12288000 ,0x00008 ,0xffff0e80 }, + {22579200 ,0x00004 ,0xffff6273 }, + {24576000 ,0x00004 ,0xffff0740 }, +}; + +struct clk_zx_audio_divider audio_adiv_clk[] = { +AUDIO_DIV (0 ,"i2s0_wclk_div" ,"i2s0_wclk_mux" ,AUDIO_I2S0_DIV_CFG1 , i2s_wclk_div_table), +AUDIO_DIV (0 ,"i2s1_wclk_div" ,"i2s1_wclk_mux" ,AUDIO_I2S1_DIV_CFG1 , i2s_wclk_div_table), +AUDIO_DIV (0 ,"i2s2_wclk_div" ,"i2s2_wclk_mux" ,AUDIO_I2S2_DIV_CFG1 , i2s_wclk_div_table), +AUDIO_DIV (0 ,"i2s3_wclk_div" ,"i2s3_wclk_mux" ,AUDIO_I2S3_DIV_CFG1 , i2s_wclk_div_table), +AUDIO_DIV (0 ,"spdif0_wclk_div" ,"spdif0_wclk_mux" ,AUDIO_SPDIF0_DIV_CFG1 , spdif_wclk_div_table), +AUDIO_DIV (0 ,"spdif1_wclk_div" ,"spdif1_wclk_mux" ,AUDIO_SPDIF1_DIV_CFG1 , spdif_wclk_div_table), +}; + +struct zx_clk_div audio_div_clk[] = { +DIV_T (0 ,"tdm_wclk_div" ,"audio_16m384" ,AUDIO_TDM_CLK ,8 ,4 ,0 , common_div_table), +}; + +struct zx_clk_gate audio_gate_clk[] = { +GATE (AUDIO_I2S0_WCLK ,"i2s0_wclk" ,"i2s0_wclk_div" ,AUDIO_I2S0_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_I2S1_WCLK ,"i2s1_wclk" ,"i2s1_wclk_div" ,AUDIO_I2S1_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_I2S2_WCLK ,"i2s2_wclk" ,"i2s2_wclk_div" ,AUDIO_I2S2_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_I2S3_WCLK ,"i2s3_wclk" ,"i2s3_wclk_div" ,AUDIO_I2S3_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_I2C0_WCLK ,"i2c0_wclk" ,"i2c0_wclk_mux" ,AUDIO_I2C0_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_SPDIF0_WCLK ,"spdif0_wclk" ,"spdif0_wclk_div" ,AUDIO_SPDIF0_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_SPDIF1_WCLK ,"spdif1_wclk" ,"spdif1_wclk_div" ,AUDIO_SPDIF1_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_TDM_WCLK ,"tdm_wclk" ,"tdm_wclk_div" ,AUDIO_TDM_CLK ,17 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_TS_PCLK ,"tempsensor_pclk" ,"clk49m5" ,AUDIO_TS_CLK ,1 ,0 ,0 ), +}; + +static struct clk_hw_onecell_data audio_hw_onecell_data = { + .num =AUDIO_NR_CLKS , + .hws = { + [AUDIO_NR_CLKS -1 ] =NULL , + }, +}; + +static int __init audio_clocks_init (struct device_node *np) +{ +void __iomem *reg_base; +int i, ret; + + reg_base =of_iomap (np,0 ); +if (!reg_base) { +pr_err ("%s: Unable to map audio clk base\n" , __func__); +return -ENXIO ; + } + +for (i =0 ; i <ARRAY_SIZE (audio_mux_clk); i++) { +if (audio_mux_clk[i].id) + audio_hw_onecell_data.hws[audio_mux_clk[i].id] = + &audio_mux_clk[i].mux.hw; + + audio_mux_clk[i].mux.reg += (u64 )reg_base; + ret =clk_hw_register (NULL , &audio_mux_clk[i].mux.hw); +if (ret) { +pr_warn ("audio clk %s init error!\n" , + audio_mux_clk[i].mux.hw.init->name); + } + } + +for (i =0 ; i <ARRAY_SIZE (audio_adiv_clk); i++) { +if (audio_adiv_clk[i].id) + audio_hw_onecell_data.hws[audio_adiv_clk[i].id] = + &audio_adiv_clk[i].hw; + + audio_adiv_clk[i].reg_base += (u64 )reg_base; + ret =clk_hw_register (NULL , &audio_adiv_clk[i].hw); +if (ret) { +pr_warn ("audio clk %s init error!\n" , + audio_adiv_clk[i].hw.init->name); + } + } + +for (i =0 ; i <ARRAY_SIZE (audio_div_clk); i++) { +if (audio_div_clk[i].id) + audio_hw_onecell_data.hws[audio_div_clk[i].id] = + &audio_div_clk[i].div.hw; + + audio_div_clk[i].div.reg += (u64 )reg_base; + ret =clk_hw_register (NULL , &audio_div_clk[i].div.hw); +if (ret) { +pr_warn ("audio clk %s init error!\n" , + audio_div_clk[i].div.hw.init->name); + } + } + +for (i =0 ; i <ARRAY_SIZE (audio_gate_clk); i++) { +if (audio_gate_clk[i].id) + audio_hw_onecell_data.hws[audio_gate_clk[i].id] = + &audio_gate_clk[i].gate.hw; + + audio_gate_clk[i].gate.reg += (u64 )reg_base; + ret =clk_hw_register (NULL , &audio_gate_clk[i].gate.hw); +if (ret) { +pr_warn ("audio clk %s init error!\n" , + audio_gate_clk[i].gate.hw.init->name); + } + } + +if (of_clk_add_hw_provider (np, of_clk_hw_onecell_get, &audio_hw_onecell_data)) +panic ("could not register clk provider\n" ); +pr_info ("audio-clk init over, nr:%d\n" ,AUDIO_NR_CLKS ); + +return 0 ; +} +static const struct of_device_id zx_clkc_match_table[] = { { .compatible ="zte,zx296718-topcrm" , .data = &top_clocks_init }, { .compatible ="zte,zx296718-lsp0crm" , .data = &lsp0_clocks_init }, { .compatible ="zte,zx296718-lsp1crm" , .data = &lsp1_clocks_init }, + { .compatible ="zte,zx296718-audiocrm" , .data = &audio_clocks_init }, { } };
drivers/clk/zte/clk.c +149 -0
158 lines not on this page · raw message
drivers/clk/zte/clk.h +28 -0
@@ -153,6 +153,32 @@ struct zx_clk_div { .id = _id, \ } +struct zx_clk_audio_div_table { +unsigned long rate; +unsigned int int_reg; +unsigned int frac_reg; +}; + +struct clk_zx_audio_divider { +struct clk_hw hw; +void __iomem *reg_base; +const struct zx_clk_audio_div_table *table; +unsigned int rate_count; +spinlock_t *lock; +u16 id; +}; + +#define AUDIO_DIV (_id, _name, _parent, _reg, _table) \ +{ \ + .reg_base = (void __iomem *) _reg, \ + .lock = &clk_lock, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &zx_audio_div_ops, \ + 0), \ + .id = _id, \ +} +struct clk *clk_register_zx_pll (const char *name,const char *parent_name,unsigned long flags,void __iomem *reg_base,const struct zx_pll_config *lookup_table,int count,spinlock_t *lock);
@@ -167,4 +193,6 @@ struct clk *clk_register_zx_audio(const char *name, unsigned long flags, void __iomem *reg_base);extern const struct clk_ops zx_pll_ops; +extern const struct clk_ops zx_audio_div_ops; +#endif
Signature · 1 line
1.9.1
PATCH 2/2 clk: zte: add audio clocks for zx296718
2016-12-08 17:25 GMT+08:00 Shawn Guo <[email protected]>:
Quoted 58 lines
From: Jun Nie <[email protected]>
The audio related clock support is missing from the existing zx296718 clock driver. Let's add it, so that the upstream ZX SPDIF driver can work for HDMI audio support.
Signed-off-by Jun Nie <[email protected]>Signed-off-by Shawn Guo <[email protected]>--- drivers/clk/zte/clk-zx296718.c | 150 +++++++++++++++++++++++++++++++++++++++++ drivers/clk/zte/clk.c | 149 ++++++++++++++++++++++++++++++++++++++++ drivers/clk/zte/clk.h | 28 ++++++++ 3 files changed, 327 insertions(+)drivers/clk/zte/clk-zx296718.c +36
-0@@ -888,10 +888,160 @@ static int __init lsp1_clocks_init(struct device_node *np)return 0 ; } +PNAME (audio_wclk_common_p) = { +"audio_99m" , +"audio_24m" , +}; + +PNAME (audio_timer_p) = { +"audio_24m" , +"audio_32k" , +}; + +static struct zx_clk_mux audio_mux_clk[] = { +MUX (0 ,"i2s0_wclk_mux" , audio_wclk_common_p,AUDIO_I2S0_CLK ,0 ,1 ), +MUX (0 ,"i2s1_wclk_mux" , audio_wclk_common_p,AUDIO_I2S1_CLK ,0 ,1 ), +MUX (0 ,"i2s2_wclk_mux" , audio_wclk_common_p,AUDIO_I2S2_CLK ,0 ,1 ), +MUX (0 ,"i2s3_wclk_mux" , audio_wclk_common_p,AUDIO_I2S3_CLK ,0 ,1 ), +MUX (0 ,"i2c0_wclk_mux" , audio_wclk_common_p,AUDIO_I2C0_CLK ,0 ,1 ), +MUX (0 ,"spdif0_wclk_mux" , audio_wclk_common_p,AUDIO_SPDIF0_CLK ,0 ,1 ), +MUX (0 ,"spdif1_wclk_mux" , audio_wclk_common_p,AUDIO_SPDIF1_CLK ,0 ,1 ), +MUX (0 ,"timer_wclk_mux" , audio_timer_p,AUDIO_TIMER_CLK ,0 ,1 ), +}; + +struct zx_clk_audio_div_table i2s_wclk_div_table[] = { + {2048000 ,0x3000030 ,0xffff5700 }, + {4096000 ,0x3000018 ,0xffff2b80 }, + {2822400 ,0x3000011 ,0xffff89cb }, + {3072000 ,0x3000010 ,0xffff1d00 }, + {4096000 ,0x300000c ,0xffff15c0 }, + {5644800 ,0x3000008 ,0xffffc4e5 }, + {6144000 ,0x3000008 ,0xffff0e80 }, + {11289600 ,0x3000004 ,0xffff6273 }, + {12288000 ,0x3000004 ,0xffff0740 }, + {22579200 ,0x3000002 ,0xffff3139 }, + {24576000 ,0x3000002 ,0xffff03a0 }, +}; + +struct zx_clk_audio_div_table spdif_wclk_div_table[] = {
drivers/clk/zte/clk-zx296718.c static int __init lsp1_clocks_init(struct device_node *np) · line 936
Quoted 10 lines
drivers/clk/zte/clk-zx296718.c +10
-0+ {2822400, 0x00023, 0xffff1397}, + {3072000, 0x00020, 0xffff3a00}, + {4096000, 0x00018, 0xffff2b80}, + {5644800, 0x00011, 0xffff89cb}, + {6144000, 0x00010, 0xffff1d00}, + {11289600, 0x00008, 0xffffc4e5}, + {12288000, 0x00008, 0xffff0e80}, + {22579200, 0x00004, 0xffff6273}, + {24576000, 0x00004, 0xffff0740}, +};
You can remove these two tables and table pointer member as I already removed table pointer assignment in macro AUDIO_DIV in this code. I am sorry for not cleaning code properly.
Quoted 40 lines
drivers/clk/zte/clk-zx296718.c +40
-0+ +struct clk_zx_audio_divider audio_adiv_clk[] = { +AUDIO_DIV (0 ,"i2s0_wclk_div" ,"i2s0_wclk_mux" ,AUDIO_I2S0_DIV_CFG1 , i2s_wclk_div_table), +AUDIO_DIV (0 ,"i2s1_wclk_div" ,"i2s1_wclk_mux" ,AUDIO_I2S1_DIV_CFG1 , i2s_wclk_div_table), +AUDIO_DIV (0 ,"i2s2_wclk_div" ,"i2s2_wclk_mux" ,AUDIO_I2S2_DIV_CFG1 , i2s_wclk_div_table), +AUDIO_DIV (0 ,"i2s3_wclk_div" ,"i2s3_wclk_mux" ,AUDIO_I2S3_DIV_CFG1 , i2s_wclk_div_table), +AUDIO_DIV (0 ,"spdif0_wclk_div" ,"spdif0_wclk_mux" ,AUDIO_SPDIF0_DIV_CFG1 , spdif_wclk_div_table), +AUDIO_DIV (0 ,"spdif1_wclk_div" ,"spdif1_wclk_mux" ,AUDIO_SPDIF1_DIV_CFG1 , spdif_wclk_div_table), +}; + +struct zx_clk_div audio_div_clk[] = { +DIV_T (0 ,"tdm_wclk_div" ,"audio_16m384" ,AUDIO_TDM_CLK ,8 ,4 ,0 , common_div_table), +}; + +struct zx_clk_gate audio_gate_clk[] = { +GATE (AUDIO_I2S0_WCLK ,"i2s0_wclk" ,"i2s0_wclk_div" ,AUDIO_I2S0_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_I2S1_WCLK ,"i2s1_wclk" ,"i2s1_wclk_div" ,AUDIO_I2S1_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_I2S2_WCLK ,"i2s2_wclk" ,"i2s2_wclk_div" ,AUDIO_I2S2_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_I2S3_WCLK ,"i2s3_wclk" ,"i2s3_wclk_div" ,AUDIO_I2S3_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_I2C0_WCLK ,"i2c0_wclk" ,"i2c0_wclk_mux" ,AUDIO_I2C0_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_SPDIF0_WCLK ,"spdif0_wclk" ,"spdif0_wclk_div" ,AUDIO_SPDIF0_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_SPDIF1_WCLK ,"spdif1_wclk" ,"spdif1_wclk_div" ,AUDIO_SPDIF1_CLK ,9 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_TDM_WCLK ,"tdm_wclk" ,"tdm_wclk_div" ,AUDIO_TDM_CLK ,17 ,CLK_SET_RATE_PARENT ,0 ), +GATE (AUDIO_TS_PCLK ,"tempsensor_pclk" ,"clk49m5" ,AUDIO_TS_CLK ,1 ,0 ,0 ), +}; + +static struct clk_hw_onecell_data audio_hw_onecell_data = { + .num =AUDIO_NR_CLKS , + .hws = { + [AUDIO_NR_CLKS -1 ] =NULL , + }, +}; + +static int __init audio_clocks_init (struct device_node *np) +{ +void __iomem *reg_base; +int i, ret; + + reg_base =of_iomap (np,0 ); +if (!reg_base) {
drivers/clk/zte/clk-zx296718.c static int __init lsp1_clocks_init(struct device_node *np) · line 986
Quoted 10 lines
drivers/clk/zte/clk-zx296718.c +10
-0+pr_err ("%s: Unable to map audio clk base\n" , __func__); +return -ENXIO ; + } + +for (i =0 ; i <ARRAY_SIZE (audio_mux_clk); i++) { +if (audio_mux_clk[i].id) + audio_hw_onecell_data.hws[audio_mux_clk[i].id] = + &audio_mux_clk[i].mux.hw; + + audio_mux_clk[i].mux.reg += (u64 )reg_base;
Fix build test failure on 32bit system.
audio_mux_clk[i].mux.reg += (uintptr_t)reg_base;
drivers/clk/zte/clk-zx296718.c static int __init lsp1_clocks_init(struct device_node *np) · line 999
Quoted 13 lines
drivers/clk/zte/clk-zx296718.c +13
-0+ ret =clk_hw_register (NULL , &audio_mux_clk[i].mux.hw); +if (ret) { +pr_warn ("audio clk %s init error!\n" , + audio_mux_clk[i].mux.hw.init->name); + } + } + +for (i =0 ; i <ARRAY_SIZE (audio_adiv_clk); i++) { +if (audio_adiv_clk[i].id) + audio_hw_onecell_data.hws[audio_adiv_clk[i].id] = + &audio_adiv_clk[i].hw; + + audio_adiv_clk[i].reg_base += (u64 )reg_base;
The same to this line and below cases.
Quoted 85 lines
drivers/clk/zte/clk-zx296718.c +41
-0+ ret =clk_hw_register (NULL , &audio_adiv_clk[i].hw); +if (ret) { +pr_warn ("audio clk %s init error!\n" , + audio_adiv_clk[i].hw.init->name); + } + } + +for (i =0 ; i <ARRAY_SIZE (audio_div_clk); i++) { +if (audio_div_clk[i].id) + audio_hw_onecell_data.hws[audio_div_clk[i].id] = + &audio_div_clk[i].div.hw; + + audio_div_clk[i].div.reg += (u64 )reg_base; + ret =clk_hw_register (NULL , &audio_div_clk[i].div.hw); +if (ret) { +pr_warn ("audio clk %s init error!\n" , + audio_div_clk[i].div.hw.init->name); + } + } + +for (i =0 ; i <ARRAY_SIZE (audio_gate_clk); i++) { +if (audio_gate_clk[i].id) + audio_hw_onecell_data.hws[audio_gate_clk[i].id] = + &audio_gate_clk[i].gate.hw; + + audio_gate_clk[i].gate.reg += (u64 )reg_base; + ret =clk_hw_register (NULL , &audio_gate_clk[i].gate.hw); +if (ret) { +pr_warn ("audio clk %s init error!\n" , + audio_gate_clk[i].gate.hw.init->name); + } + } + +if (of_clk_add_hw_provider (np, of_clk_hw_onecell_get, &audio_hw_onecell_data)) +panic ("could not register clk provider\n" ); +pr_info ("audio-clk init over, nr:%d\n" ,AUDIO_NR_CLKS ); + +return 0 ; +} +static const struct of_device_id zx_clkc_match_table[] = { { .compatible ="zte,zx296718-topcrm" , .data = &top_clocks_init }, { .compatible ="zte,zx296718-lsp0crm" , .data = &lsp0_clocks_init }, { .compatible ="zte,zx296718-lsp1crm" , .data = &lsp1_clocks_init }, + { .compatible ="zte,zx296718-audiocrm" , .data = &audio_clocks_init }, { } };drivers/clk/zte/clk.c +22
-0@@ -9,6 +9,7 @@#include <linux/clk-provider.h> #include <linux/err.h> +#include <linux/gcd.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/slab.h> @@ -310,3 +311,151 @@ struct clk *clk_register_zx_audio(const char *name,return clk; } + +#define CLK_AUDIO_DIV_FRAC BIT(0) +#define CLK_AUDIO_DIV_INT BIT(1) +#define CLK_AUDIO_DIV_UNCOMMON BIT(1) + +#define CLK_AUDIO_DIV_FRAC_NSHIFT 16 +#define CLK_AUDIO_DIV_INT_FRAC_RE BIT(16) +#define CLK_AUDIO_DIV_INT_FRAC_MAX (0xffff) +#define CLK_AUDIO_DIV_INT_FRAC_MIN (0x2) +#define CLK_AUDIO_DIV_INT_INT_SHIFT 24 +#define CLK_AUDIO_DIV_INT_INT_WIDTH 4 + +#define to_clk_zx_audio_div (_hw) container_of(_hw, struct clk_zx_audio_divider, hw) + +static unsigned long audio_calc_rate (struct clk_zx_audio_divider *audio_div, +u32 reg_frac,u32 reg_int, +unsigned long parent_rate) +{ +unsigned long rate, m, n; + +if (audio_div->table) {
drivers/clk/zte/clk.c struct clk *clk_register_zx_audio(const char *name, · line 344
Quoted 10 lines
drivers/clk/zte/clk.c +10
-0+const struct zx_clk_audio_div_table *divt = audio_div->table; + +for (; divt->rate; divt++) { +if ((divt->int_reg == reg_int) && (divt->frac_reg == reg_frac)) +return divt->rate; + } + } +if (audio_div->table) +pr_warn ("cannot found the config(int_reg:0x%x, frac_reg:0x%x) in table, we will caculate it\n" , + reg_int, reg_frac);
Logic of register value table can be removed now.
Quoted 22 lines
drivers/clk/zte/clk.c +22
-0+ + m = reg_frac &0xffff ; + n = (reg_frac >>16 ) &0xffff ; + + m = (reg_int &0xffff ) * n + m; + rate = (parent_rate * n) / m; + +return rate; +} + +static void audio_calc_reg (struct clk_zx_audio_divider *audio_div, +struct zx_clk_audio_div_table *div_table, +unsigned long rate,unsigned long parent_rate) +{ +unsigned int reg_int, reg_frac; +unsigned long m, n, div; + +if (audio_div->table) { +const struct zx_clk_audio_div_table *divt = audio_div->table; + +for (; divt->rate; divt++) { +if (divt->rate == rate) {
drivers/clk/zte/clk.c struct clk *clk_register_zx_audio(const char *name, · line 376
Quoted 10 lines
drivers/clk/zte/clk.c +10
-0+ div_table->rate = divt->rate; + div_table->int_reg = divt->int_reg; + div_table->frac_reg = divt->frac_reg; +return ; + } + } + } +if (audio_div->table) +pr_warn ("cannot found the rate(%ld) in table, we will caculate the config\n" , + rate);
Table is not used here actually neither.
Quoted 99 lines
drivers/clk/zte/clk.c +85
-0+ + reg_int = parent_rate / rate; + +if (reg_int >CLK_AUDIO_DIV_INT_FRAC_MAX ) + reg_int =CLK_AUDIO_DIV_INT_FRAC_MAX ; +else if (reg_int <CLK_AUDIO_DIV_INT_FRAC_MIN ) + reg_int =0 ; + m = parent_rate - rate * reg_int; + n = rate; + + div =gcd (m, n); + m = m / div; + n = n / div; + +if ((m >>16 ) || (n >>16 )) { +if (m > n) { + n = n *0xffff / m; + m =0xffff ; + }else { + m = m *0xffff / n; + n =0xffff ; + } + } + reg_frac = m | (n <<16 ); + + div_table->rate = (ulong)(parent_rate * n) / ((ulong )reg_int * n + m); + div_table->int_reg = reg_int; + div_table->frac_reg = reg_frac; +} + +static unsigned long zx_audio_div_recalc_rate (struct clk_hw *hw, +unsigned long parent_rate) +{ +struct clk_zx_audio_divider *zx_audio_div =to_clk_zx_audio_div (hw); +u32 reg_frac, reg_int; + + reg_frac =readl_relaxed (zx_audio_div->reg_base); + reg_int =readl_relaxed (zx_audio_div->reg_base +0x4 ); + +return audio_calc_rate (zx_audio_div, reg_frac, reg_int, parent_rate); +} + +static long zx_audio_div_round_rate (struct clk_hw *hw,unsigned long rate, +unsigned long *prate) +{ +struct clk_zx_audio_divider *zx_audio_div =to_clk_zx_audio_div (hw); +struct zx_clk_audio_div_table divt; + +audio_calc_reg (zx_audio_div, &divt, rate, *prate); + +return audio_calc_rate (zx_audio_div, divt.frac_reg, divt.int_reg, *prate); +} + +static int zx_audio_div_set_rate (struct clk_hw *hw,unsigned long rate, +unsigned long parent_rate) +{ +struct clk_zx_audio_divider *zx_audio_div =to_clk_zx_audio_div (hw); +struct zx_clk_audio_div_table divt; +unsigned int val; + +audio_calc_reg (zx_audio_div, &divt, rate, parent_rate); +if (divt.rate != rate) +pr_info ("the real rate is:%ld" , divt.rate); + +writel_relaxed (divt.frac_reg, zx_audio_div->reg_base); + + val =readl_relaxed (zx_audio_div->reg_base +0x4 ); + val &= ~0xffff ; + val |= divt.int_reg |CLK_AUDIO_DIV_INT_FRAC_RE ; +writel_relaxed (val, zx_audio_div->reg_base +0x4 ); + +mdelay (1 ); + + val =readl_relaxed (zx_audio_div->reg_base +0x4 ); + val &= ~CLK_AUDIO_DIV_INT_FRAC_RE ; +writel_relaxed (val, zx_audio_div->reg_base +0x4 ); + +return 0 ; +} + +const struct clk_ops zx_audio_div_ops = { + .recalc_rate = zx_audio_div_recalc_rate, + .round_rate = zx_audio_div_round_rate, + .set_rate = zx_audio_div_set_rate, +};drivers/clk/zte/clk.h +6
-0@@ -153,6 +153,32 @@ struct zx_clk_div { .id = _id, \ } +struct zx_clk_audio_div_table { +unsigned long rate; +unsigned int int_reg; +unsigned int frac_reg; +}; +
drivers/clk/zte/clk.h struct zx_clk_div { · line 171
Quoted 10 lines
drivers/clk/zte/clk.h +10
-0+struct clk_zx_audio_divider { +struct clk_hw hw; +void __iomem *reg_base; +const struct zx_clk_audio_div_table *table; +unsigned int rate_count; +spinlock_t *lock; +u16 id; +}; + +#define AUDIO_DIV (_id, _name, _parent, _reg, _table) \
Remove unused table here.
Quoted 23 lines
drivers/clk/zte/clk.h +12
-1+{ \ + .reg_base = (void __iomem *) _reg, \ + .lock = &clk_lock, \ + .hw.init = CLK_HW_INIT(_name, \ + _parent, \ + &zx_audio_div_ops, \ + 0), \ + .id = _id, \ +} +struct clk *clk_register_zx_pll (const char *name,const char *parent_name,unsigned long flags,void __iomem *reg_base,const struct zx_pll_config *lookup_table,int count,spinlock_t *lock);@@ -167,4 +193,6 @@ struct clk *clk_register_zx_audio(const char *name, unsigned long flags, void __iomem *reg_base);extern const struct clk_ops zx_pll_ops; +extern const struct clk_ops zx_audio_div_ops; +#endif --1.9.1
PATCH 2/2 clk: zte: add audio clocks for zx296718
In reply to Shawn GuoQuoted 56 lines · Shawn Guo
drivers/clk/zte/clk-zx296718.c +56
-0+ +static int __init audio_clocks_init (struct device_node *np) +{ +void __iomem *reg_base; +int i, ret; + + reg_base =of_iomap (np,0 ); +if (!reg_base) { +pr_err ("%s: Unable to map audio clk base\n" , __func__); +return -ENXIO ; + } + +for (i =0 ; i <ARRAY_SIZE (audio_mux_clk); i++) { +if (audio_mux_clk[i].id) + audio_hw_onecell_data.hws[audio_mux_clk[i].id] = + &audio_mux_clk[i].mux.hw; + + audio_mux_clk[i].mux.reg += (u64 )reg_base; + ret =clk_hw_register (NULL , &audio_mux_clk[i].mux.hw); +if (ret) { +pr_warn ("audio clk %s init error!\n" , + audio_mux_clk[i].mux.hw.init->name); + } + } + +for (i =0 ; i <ARRAY_SIZE (audio_adiv_clk); i++) { +if (audio_adiv_clk[i].id) + audio_hw_onecell_data.hws[audio_adiv_clk[i].id] = + &audio_adiv_clk[i].hw; + + audio_adiv_clk[i].reg_base += (u64 )reg_base; + ret =clk_hw_register (NULL , &audio_adiv_clk[i].hw); +if (ret) { +pr_warn ("audio clk %s init error!\n" , + audio_adiv_clk[i].hw.init->name); + } + } + +for (i =0 ; i <ARRAY_SIZE (audio_div_clk); i++) { +if (audio_div_clk[i].id) + audio_hw_onecell_data.hws[audio_div_clk[i].id] = + &audio_div_clk[i].div.hw; + + audio_div_clk[i].div.reg += (u64 )reg_base; + ret =clk_hw_register (NULL , &audio_div_clk[i].div.hw); +if (ret) { +pr_warn ("audio clk %s init error!\n" , + audio_div_clk[i].div.hw.init->name); + } + } + +for (i =0 ; i <ARRAY_SIZE (audio_gate_clk); i++) { +if (audio_gate_clk[i].id) + audio_hw_onecell_data.hws[audio_gate_clk[i].id] = + &audio_gate_clk[i].gate.hw; +
drivers/clk/zte/clk-zx296718.c static int __init lsp1_clocks_init(struct device_node *np) · line 1034
Quoted 10 lines
drivers/clk/zte/clk-zx296718.c +10
-0+ audio_gate_clk[i].gate.reg += (u64 )reg_base; + ret =clk_hw_register (NULL , &audio_gate_clk[i].gate.hw); +if (ret) { +pr_warn ("audio clk %s init error!\n" , + audio_gate_clk[i].gate.hw.init->name); + } + } + +if (of_clk_add_hw_provider (np, of_clk_hw_onecell_get, &audio_hw_onecell_data)) +panic ("could not register clk provider\n" );
Why don't we return error? We returned errors before if we couldn't map the ioregion.
drivers/clk/zte/clk-zx296718.c static int __init lsp1_clocks_init(struct device_node *np) · line 1035
Quoted 1 line
drivers/clk/zte/clk-zx296718.c +1
-0+pr_info ("audio-clk init over, nr:%d\n" ,AUDIO_NR_CLKS );
debug noise?
Quoted 43 lines
drivers/clk/zte/clk-zx296718.c +5
-0+ +return 0 ; +} +static const struct of_device_id zx_clkc_match_table[] = { { .compatible ="zte,zx296718-topcrm" , .data = &top_clocks_init }, { .compatible ="zte,zx296718-lsp0crm" , .data = &lsp0_clocks_init }, { .compatible ="zte,zx296718-lsp1crm" , .data = &lsp1_clocks_init }, + { .compatible ="zte,zx296718-audiocrm" , .data = &audio_clocks_init }, { } };drivers/clk/zte/clk.c +16
-0@@ -9,6 +9,7 @@#include <linux/clk-provider.h> #include <linux/err.h> +#include <linux/gcd.h> #include <linux/io.h> #include <linux/iopoll.h> #include <linux/slab.h> @@ -310,3 +311,151 @@ struct clk *clk_register_zx_audio(const char *name,return clk; } + +#define CLK_AUDIO_DIV_FRAC BIT(0) +#define CLK_AUDIO_DIV_INT BIT(1) +#define CLK_AUDIO_DIV_UNCOMMON BIT(1) + +#define CLK_AUDIO_DIV_FRAC_NSHIFT 16 +#define CLK_AUDIO_DIV_INT_FRAC_RE BIT(16) +#define CLK_AUDIO_DIV_INT_FRAC_MAX (0xffff) +#define CLK_AUDIO_DIV_INT_FRAC_MIN (0x2) +#define CLK_AUDIO_DIV_INT_INT_SHIFT 24 +#define CLK_AUDIO_DIV_INT_INT_WIDTH 4 + +#define to_clk_zx_audio_div (_hw) container_of(_hw, struct clk_zx_audio_divider, hw) + +static unsigned long audio_calc_rate(struct clk_zx_audio_divider *audio_div,
drivers/clk/zte/clk.c struct clk *clk_register_zx_audio(const char *name, · line 338
Quoted 10 lines
drivers/clk/zte/clk.c +10
-0+ u32 reg_frac, u32 reg_int, + unsigned long parent_rate) +{ + unsigned long rate, m, n; + +if (audio_div->table) { +const struct zx_clk_audio_div_table *divt = audio_div->table; + +for (; divt->rate; divt++) { +if ((divt->int_reg == reg_int) && (divt->frac_reg == reg_frac))
Please remove extra parenthesis here.
Quoted 54 lines
drivers/clk/zte/clk.c +54
-0+return divt->rate; + } + } +if (audio_div->table) +pr_warn ("cannot found the config(int_reg:0x%x, frac_reg:0x%x) in table, we will caculate it\n" , + reg_int, reg_frac); + + m = reg_frac &0xffff ; + n = (reg_frac >>16 ) &0xffff ; + + m = (reg_int &0xffff ) * n + m; + rate = (parent_rate * n) / m; + +return rate; +} + +static void audio_calc_reg (struct clk_zx_audio_divider *audio_div, +struct zx_clk_audio_div_table *div_table, +unsigned long rate,unsigned long parent_rate) +{ +unsigned int reg_int, reg_frac; +unsigned long m, n, div; + +if (audio_div->table) { +const struct zx_clk_audio_div_table *divt = audio_div->table; + +for (; divt->rate; divt++) { +if (divt->rate == rate) { + div_table->rate = divt->rate; + div_table->int_reg = divt->int_reg; + div_table->frac_reg = divt->frac_reg; +return ; + } + } + } +if (audio_div->table) +pr_warn ("cannot found the rate(%ld) in table, we will caculate the config\n" , + rate); + + reg_int = parent_rate / rate; + +if (reg_int >CLK_AUDIO_DIV_INT_FRAC_MAX ) + reg_int =CLK_AUDIO_DIV_INT_FRAC_MAX ; +else if (reg_int <CLK_AUDIO_DIV_INT_FRAC_MIN ) + reg_int =0 ; + m = parent_rate - rate * reg_int; + n = rate; + + div =gcd (m, n); + m = m / div; + n = n / div; + +if ((m >>16 ) || (n >>16 )) { +if (m > n) {
drivers/clk/zte/clk.c struct clk *clk_register_zx_audio(const char *name, · line 402
Quoted 10 lines
drivers/clk/zte/clk.c +10
-0+ n = n *0xffff / m; + m =0xffff ; + }else { + m = m *0xffff / n; + n =0xffff ; + } + } + reg_frac = m | (n <<16 ); + + div_table->rate = (ulong)(parent_rate * n) / ((ulong )reg_int * n + m);
Please don't use ulong, use unsigned long. Also consider using local variables so the line isn't overly long.
Quoted 3 lines
drivers/clk/zte/clk.c +3
-0+ div_table->int_reg = reg_int; + div_table->frac_reg = reg_frac; +}
[...]
drivers/clk/zte/clk.c struct clk *clk_register_zx_audio(const char *name, · line 439
Quoted 11 lines
drivers/clk/zte/clk.c +11
-0+ +static int zx_audio_div_set_rate (struct clk_hw *hw,unsigned long rate, +unsigned long parent_rate) +{ +struct clk_zx_audio_divider *zx_audio_div =to_clk_zx_audio_div (hw); +struct zx_clk_audio_div_table divt; +unsigned int val; + +audio_calc_reg (zx_audio_div, &divt, rate, parent_rate); +if (divt.rate != rate) +pr_info ("the real rate is:%ld" , divt.rate);
Debug noise?
Quoted 23 lines
drivers/clk/zte/clk.c +22
-0+ +writel_relaxed (divt.frac_reg, zx_audio_div->reg_base); + + val =readl_relaxed (zx_audio_div->reg_base +0x4 ); + val &= ~0xffff ; + val |= divt.int_reg |CLK_AUDIO_DIV_INT_FRAC_RE ; +writel_relaxed (val, zx_audio_div->reg_base +0x4 ); + +mdelay (1 ); + + val =readl_relaxed (zx_audio_div->reg_base +0x4 ); + val &= ~CLK_AUDIO_DIV_INT_FRAC_RE ; +writel_relaxed (val, zx_audio_div->reg_base +0x4 ); + +return 0 ; +} + +const struct clk_ops zx_audio_div_ops = { + .recalc_rate = zx_audio_div_recalc_rate, + .round_rate = zx_audio_div_round_rate, + .set_rate = zx_audio_div_set_rate, +};
Signature · 2 lines
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a Linux Foundation Collaborative Project
PATCH 2/2 clk: zte: add audio clocks for zx296718
Quoted 5 lines · Stephen Boyd
drivers/clk/zte/clk-zx296718.c static int __init lsp1_clocks_init(struct device_node *np) · line 1034
Quoted 2 lines
drivers/clk/zte/clk-zx296718.c +2
-0+if (of_clk_add_hw_provider (np, of_clk_hw_onecell_get, &audio_hw_onecell_data)) +panic ("could not register clk provider\n" );Why don't we return error? We returned errors before if we couldn't map the ioregion.
Yes. Rather than panic, we should check return and give an error message in case of failure.
Quoted 4 lines
drivers/clk/zte/clk-zx296718.c static int __init lsp1_clocks_init(struct device_node *np) · line 1035
Quoted 2 lines
drivers/clk/zte/clk-zx296718.c +1
-0+pr_info ("audio-clk init over, nr:%d\n" ,AUDIO_NR_CLKS );debug noise?
Agreed. I will just clean it up.
<snip>
Quoted 5 lines
Quoted 5 lines
drivers/clk/zte/clk.c +5
-0+static unsigned long audio_calc_rate (struct clk_zx_audio_divider *audio_div, +u32 reg_frac,u32 reg_int, +unsigned long parent_rate) +{ +unsigned long rate, m, n;
Quoted 8 lines
drivers/clk/zte/clk.c struct clk *clk_register_zx_audio(const char *name, · line 338
Quoted 6 lines
drivers/clk/zte/clk.c +6
-0+ +if (audio_div->table) { +const struct zx_clk_audio_div_table *divt = audio_div->table; + +for (; divt->rate; divt++) { +if ((divt->int_reg == reg_int) && (divt->frac_reg == reg_frac))Please remove extra parenthesis here.
The whole hunk of the code will be dropped, since the divider lookup table is not actually used.
Quoted 7 lines
Quoted 7 lines
drivers/clk/zte/clk.c +6
-0+return divt->rate; + } + } +if (audio_div->table) +pr_warn ("cannot found the config(int_reg:0x%x, frac_reg:0x%x) in table, we will caculate it\n" , + reg_int, reg_frac);
<snip>
Quoted 4 lines
drivers/clk/zte/clk.c struct clk *clk_register_zx_audio(const char *name, · line 402
Quoted 1 line
drivers/clk/zte/clk.c +1
-0+ div_table->rate = (ulong)(parent_rate * n) / ((ulong )reg_int * n + m);Please don't use ulong, use unsigned long. Also consider using local variables so the line isn't overly long.
It seems to me that none of the type casts is really necessary. So I will just drop them.
Quoted 5 lines
Quoted 4 lines
drivers/clk/zte/clk.c +3
-0+ div_table->int_reg = reg_int; + div_table->frac_reg = reg_frac; +}[...]
Quoted 13 lines
drivers/clk/zte/clk.c struct clk *clk_register_zx_audio(const char *name, · line 439
Quoted 11 lines
drivers/clk/zte/clk.c +11
-0+ +static int zx_audio_div_set_rate (struct clk_hw *hw,unsigned long rate, +unsigned long parent_rate) +{ +struct clk_zx_audio_divider *zx_audio_div =to_clk_zx_audio_div (hw); +struct zx_clk_audio_div_table divt; +unsigned int val; + +audio_calc_reg (zx_audio_div, &divt, rate, parent_rate); +if (divt.rate != rate) +pr_info ("the real rate is:%ld" , divt.rate);Debug noise?
I will change it to pr_debug.
Shawn
Rob Herring
maintainerPATCH 1/2 dt-bindings: zx296718-clk: add compatible for audio clock controller
In reply to Shawn GuoQuoted 8 lines · Shawn Guo
From: Shawn Guo <[email protected]>
It adds the compatible string for zx296718 audio clock controller.
Signed-off-by Shawn Guo <[email protected]>--- Documentation/devicetree/bindings/clock/zx296718-clk.txt | 3 +++ 1 file changed, 3 insertions(+)