Skip to contentSkip to conversation

dt-bindings: zx296718-clk: add compatible for audio clock controller

PATCH New Shawn Guo 4 participants 6 messages Dec 8, 2016 – Dec 16, 2016

Subsystems (2)
lore
Share & tools
lore

Shawn Guo

author
#0 lore raw

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
index 8c18b7b237bf..4ad703808407 100644
@@ -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
author
#1 lore raw

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
index 707d62956e9b..eed8581b1b25 100644
@@ -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
index 0df3474b2cf3..6e7ccb752c24 100644
@@ -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
#2 lore raw

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.

---
 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
index 707d62956e9b..eed8581b1b25 100644
@@ -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
index c4c1251bc1e7..ea97024b37aa 100644
@@ -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
index 0df3474b2cf3..6e7ccb752c24 100644
@@ -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

#3 lore raw

PATCH 2/2 clk: zte: add audio clocks for zx296718

In reply to Shawn Guo
Quoted 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
index c4c1251bc1e7..ea97024b37aa 100644
@@ -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
author
#4 lore raw

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