Skip to contentSkip to conversation

clk/arc: Add I2S PLL clock driver

PATCH New Alexey Brodkin 3 participants 5 messages Mar 29, 2016 – Mar 31, 2016 1 message not in the archive Synopsys ARC architecture

lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): e580aaf30e3c66d13693a4e6db0ced1996d37c04.1459246186.git.joabreu@synopsys.com

#1 lore raw

PATCH 1/1 clk/arc: Add I2S PLL clock driver

Hi Jose,

Quoted 6 lines · Jose Abreu

The ARC SDP I2S clock can be programmed using a
specific PLL.

This patch has the goal of adding a clock driver
that programs this PLL.

Quoted 7 lines

At this moment the rate values are hardcoded in
a table but in the future it would be ideal to
use a function which determines the PLL values
given the desired rate.

Signed-off-by Jose Abreu <joabreu at synopsys.com>

---

I believe these kind of patches worth sending to linux-snps mailing list (at least add it in Cc) so they won't be lost in time and could be used later as useful references.

Quoted 29 lines
?arch/arc/boot/dts/axs10x_mb.dtsi |???5 ++
?drivers/clk/Makefile?????????????|???1 +
?drivers/clk/arc/Makefile?????????|???1 +
?drivers/clk/arc/i2s_pll_clock.c??| 143 +++++++++++++++++++++++++++++++++++++++

?4 files changed, 150 insertions(+)
?create mode 100644 drivers/clk/arc/Makefile ?create mode 100644 drivers/clk/arc/i2s_pll_clock.c

arch/arc/boot/dts/axs10x_mb.dtsi +0 -0
index ab5d570..9c68226 100644
@@ -23,6 +23,11 @@
 ?				#clock-cells = <0>;
 ?			};

?

arch/arc/boot/dts/axs10x_mb.dtsi +5 -0
+			i2sclk: i2sclk {
+				compatible = "snps,i2s-pll-clock";
+				#clock-cells = <0>;
+			};
+

? apbclk: apbclk {
? compatible = "fixed-clock";
? clock-frequency = <50000000>;

drivers/clk/Makefile +0 -0
index 46869d6..620e47f 100644
@@ -84,3 +84,4 @@ obj-$(CONFIG_X86)			+= x86/
Quoted 4 lines

?obj-$(CONFIG_ARCH_ZX) += zte/
?obj-$(CONFIG_ARCH_ZYNQ) += zynq/
?obj-$(CONFIG_H8300) += h8300/
+obj-$(CONFIG_ARC) += arc/

Two things here:
?[1] This clock is relevant to only one board but not SoC, CPU core ? ? ?or whole architecture, so it makes sense to make it dependent on ? ? ?the AXS platform, i.e. CONFIG_ARC_PLAT_AXS10X.

?[2] Something similar is applicable to folder name, I would suggest to ? ? ?use "drivers/clk/axs10x"

Quoted 13 lines
drivers/clk/arc/Makefile new file +1 -0
new file mode 100644
index 0000000..01996b8
@@ -0,0 +1 @@
+obj-y += i2s_pll_clock.o
drivers/clk/arc/i2s_pll_clock.c new file +0 -0
new file mode 100644
index 0000000..8c401f1

drivers/clk/arc/i2s_pll_clock.c line 2

Quoted 2 lines
drivers/clk/arc/i2s_pll_clock.c new file +2 -0
@@ -0,0 +1,143 @@
+/*
+ * Synopsys I2S PLL clock driver

Again that's not generic SNPS I2S clock but clock driver for a particular board.

drivers/clk/arc/i2s_pll_clock.c line 4

Quoted 2 lines
drivers/clk/arc/i2s_pll_clock.c new file +2 -0
+ *
+ * drivers/clk/arc/i2s_pll_clock.c

BTW not sure why this path could be needed here.
IMHO that might be safely dropped.

Quoted 6 lines
drivers/clk/arc/i2s_pll_clock.c new file +6 -0
+ *
+ * Copyright (C) 2016 Synopsys
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.

drivers/clk/arc/i2s_pll_clock.c line 20

Quoted 10 lines
drivers/clk/arc/i2s_pll_clock.c new file +10 -0
+ */
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+/* FPGA Version Info */
+#define FPGA_VER_INFO	0xE0011230
+#define FPGA_VER_27M	0x000FBED9

This is a little bit tricky.
We'll need to do that check in each and every clock driver for AXS10x and so it worth putting this code in some common location (I mean common for all axs10x clock drivers).

Quoted 47 lines
drivers/clk/arc/i2s_pll_clock.c new file +45 -0
+/* PLL registers addresses */
+#define PLL_IDIV_ADDR	0xE00100A0
+#define PLL_FBDIV_ADDR	0xE00100A4
+#define PLL_ODIV0_ADDR	0xE00100A8
+#define PLL_ODIV1_ADDR	0xE00100AC
 
 
+struct i2s_pll_clk {
+	struct clk_hw hw;
+};
+
+struct i2s_pll_cfg {
+	unsigned int rate;
+	unsigned int idiv;
+	unsigned int fbdiv;
+	unsigned int odiv0;
+	unsigned int odiv1;
+};
+
+static const struct i2s_pll_cfg i2s_pll_cfg_27m[] = {
+	/* 27 Mhz */
+	{ 1024000, 0x104, 0x451, 0x10E38, 0x2000 },
+	{ 1411200, 0x104, 0x596, 0x10D35, 0x2000 },
+	{ 1536000, 0x208, 0xA28, 0x10B2C, 0x2000 },
+	{ 2048000, 0x82, 0x451, 0x10E38, 0x2000 },
+	{ 2822400, 0x82, 0x596, 0x10D35, 0x2000 },
+	{ 3072000, 0x104, 0xA28, 0x10B2C, 0x2000 },
+	{ 0, 0, 0, 0, 0 },
+};
+
+static const struct i2s_pll_cfg i2s_pll_cfg_28m[] = {
+	/* 28.224 Mhz */
+	{ 1024000, 0x82, 0x105, 0x107DF, 0x2000 },
+	{ 1411200, 0x28A, 0x1, 0x10001, 0x2000 },
+	{ 1536000, 0xA28, 0x187, 0x10042, 0x2000 },
+	{ 2048000, 0x41, 0x105, 0x107DF, 0x2000 },
+	{ 2822400, 0x145, 0x1, 0x10001, 0x2000 },
+	{ 3072000, 0x514, 0x187, 0x10042, 0x2000 },
+	{ 0, 0, 0, 0, 0 },
+};
+
+static unsigned long i2s_pll_recalc_rate(struct clk_hw *hw,
+			unsigned long parent_rate)
+{
+	/* TODO */
+	pr_info("%s: parent_rate=%ld\n", __func__, parent_rate);
+	return parent_rate;

drivers/clk/arc/i2s_pll_clock.c

Quoted 10 lines
drivers/clk/arc/i2s_pll_clock.c new file +10 -0
+}
+
+static long i2s_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long *prate)
+{
+	/* TODO: Round rate to nearest valid rate */
+	*prate = rate;
+	pr_info("%s: rate=%ld, prate=%ld\n", __func__, rate, *prate);
+	return *prate;
+}

These are now just dummy functions so probably they could be dropped.

drivers/clk/arc/i2s_pll_clock.c

Quoted 10 lines
drivers/clk/arc/i2s_pll_clock.c new file +10 -0
+static int i2s_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long parent_rate)
+{
+	const struct i2s_pll_cfg *pll_cfg;
+	int i;
+
+	if (readl((void *)FPGA_VER_INFO) <= FPGA_VER_27M)
+		pll_cfg = i2s_pll_cfg_27m;
+	else
+		pll_cfg = i2s_pll_cfg_28m;

As I mentioned above we need to check board version in common place and then just use some variable or structure member for comparison.

Otherwise it looks pretty nice!

-Alexey

#2 lore raw

PATCH 1/1 clk/arc: Add I2S PLL clock driver

Quoted 14 lines · Alexey Brodkin

Hi Jose,

Quoted 11 lines · Jose Abreu

The ARC SDP I2S clock can be programmed using a
specific PLL.

This patch has the goal of adding a clock driver
that programs this PLL.

At this moment the rate values are hardcoded in
a table but in the future it would be ideal to
use a function which determines the PLL values
given the desired rate.

Quoted 6 lines
Quoted 2 lines
Signed-off-by Jose Abreu <joabreu at synopsys.com>

---

I believe these kind of patches worth sending to linux-snps mailing list (at least add it in Cc) so they won't be lost in time and could be used later as useful references.

Please do also CC the common clk maintainers

COMMON CLK FRAMEWORK

M:    Michael Turquette <mturquette at baylibre.com>
M:    Stephen Boyd <sboyd at codeaurora.org>
L:    linux-clk at vger.kernel.org
Quoted 159 lines
Quoted 30 lines
 arch/arc/boot/dts/axs10x_mb.dtsi |   5 ++
 drivers/clk/Makefile             |   1 +
 drivers/clk/arc/Makefile         |   1 +
 drivers/clk/arc/i2s_pll_clock.c  | 143 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 150 insertions(+)
 create mode 100644 drivers/clk/arc/Makefile
 create mode 100644 drivers/clk/arc/i2s_pll_clock.c
arch/arc/boot/dts/axs10x_mb.dtsi +5 -0
index ab5d570..9c68226 100644
@@ -23,6 +23,11 @@
 				#clock-cells = <0>;
 			};
 
+			i2sclk: i2sclk {
+				compatible = "snps,i2s-pll-clock";
+				#clock-cells = <0>;
+			};
+
 			apbclk: apbclk {
 				compatible = "fixed-clock";
 				clock-frequency = <50000000>;
drivers/clk/Makefile +0 -0
index 46869d6..620e47f 100644

drivers/clk/Makefile obj-$(CONFIG_X86) += x86/ · line 87

Quoted 4 lines
drivers/clk/Makefile +1 -0
@@ -84,3 +84,4 @@ obj-$(CONFIG_X86)			+= x86/
 obj-$(CONFIG_ARCH_ZX)			+= zte/
 obj-$(CONFIG_ARCH_ZYNQ)			+= zynq/
 obj-$(CONFIG_H8300)		+= h8300/
+obj-$(CONFIG_ARC)		+= arc/

Two things here:
[1] This clock is relevant to only one board but not SoC, CPU core or whole architecture, so it makes sense to make it dependent on the AXS platform, i.e. CONFIG_ARC_PLAT_AXS10X.

[2] Something similar is applicable to folder name, I would suggest to

     use "drivers/clk/axs10x"
Quoted 13 lines
drivers/clk/arc/Makefile new file +1 -0
new file mode 100644
index 0000000..01996b8
@@ -0,0 +1 @@
+obj-y += i2s_pll_clock.o
drivers/clk/arc/i2s_pll_clock.c new file +0 -0
new file mode 100644
index 0000000..8c401f1

drivers/clk/arc/i2s_pll_clock.c line 2

Quoted 2 lines
drivers/clk/arc/i2s_pll_clock.c new file +2 -0
@@ -0,0 +1,143 @@
+/*
+ * Synopsys I2S PLL clock driver

Again that's not generic SNPS I2S clock but clock driver for a particular board.

drivers/clk/arc/i2s_pll_clock.c line 4

Quoted 2 lines
drivers/clk/arc/i2s_pll_clock.c new file +2 -0
+ *
+ * drivers/clk/arc/i2s_pll_clock.c

BTW not sure why this path could be needed here.
IMHO that might be safely dropped.

Quoted 6 lines
drivers/clk/arc/i2s_pll_clock.c new file +6 -0
+ *
+ * Copyright (C) 2016 Synopsys
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.

drivers/clk/arc/i2s_pll_clock.c line 20

Quoted 10 lines
drivers/clk/arc/i2s_pll_clock.c new file +10 -0
+ */
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+/* FPGA Version Info */
+#define FPGA_VER_INFO	0xE0011230
+#define FPGA_VER_27M	0x000FBED9

This is a little bit tricky.
We'll need to do that check in each and every clock driver for AXS10x and so it worth putting this code in some common location (I mean common for all axs10x clock drivers).

Quoted 46 lines
drivers/clk/arc/i2s_pll_clock.c new file +45 -0
+/* PLL registers addresses */
+#define PLL_IDIV_ADDR	0xE00100A0
+#define PLL_FBDIV_ADDR	0xE00100A4
+#define PLL_ODIV0_ADDR	0xE00100A8
+#define PLL_ODIV1_ADDR	0xE00100AC
 
+struct i2s_pll_clk {
+	struct clk_hw hw;
+};
+
+struct i2s_pll_cfg {
+	unsigned int rate;
+	unsigned int idiv;
+	unsigned int fbdiv;
+	unsigned int odiv0;
+	unsigned int odiv1;
+};
+
+static const struct i2s_pll_cfg i2s_pll_cfg_27m[] = {
+	/* 27 Mhz */
+	{ 1024000, 0x104, 0x451, 0x10E38, 0x2000 },
+	{ 1411200, 0x104, 0x596, 0x10D35, 0x2000 },
+	{ 1536000, 0x208, 0xA28, 0x10B2C, 0x2000 },
+	{ 2048000, 0x82, 0x451, 0x10E38, 0x2000 },
+	{ 2822400, 0x82, 0x596, 0x10D35, 0x2000 },
+	{ 3072000, 0x104, 0xA28, 0x10B2C, 0x2000 },
+	{ 0, 0, 0, 0, 0 },
+};
+
+static const struct i2s_pll_cfg i2s_pll_cfg_28m[] = {
+	/* 28.224 Mhz */
+	{ 1024000, 0x82, 0x105, 0x107DF, 0x2000 },
+	{ 1411200, 0x28A, 0x1, 0x10001, 0x2000 },
+	{ 1536000, 0xA28, 0x187, 0x10042, 0x2000 },
+	{ 2048000, 0x41, 0x105, 0x107DF, 0x2000 },
+	{ 2822400, 0x145, 0x1, 0x10001, 0x2000 },
+	{ 3072000, 0x514, 0x187, 0x10042, 0x2000 },
+	{ 0, 0, 0, 0, 0 },
+};
+
+static unsigned long i2s_pll_recalc_rate(struct clk_hw *hw,
+			unsigned long parent_rate)
+{
+	/* TODO */
+	pr_info("%s: parent_rate=%ld\n", __func__, parent_rate);
+	return parent_rate;

drivers/clk/arc/i2s_pll_clock.c

Quoted 10 lines
drivers/clk/arc/i2s_pll_clock.c new file +10 -0
+}
+
+static long i2s_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long *prate)
+{
+	/* TODO: Round rate to nearest valid rate */
+	*prate = rate;
+	pr_info("%s: rate=%ld, prate=%ld\n", __func__, rate, *prate);
+	return *prate;
+}

These are now just dummy functions so probably they could be dropped.

drivers/clk/arc/i2s_pll_clock.c

Quoted 10 lines
drivers/clk/arc/i2s_pll_clock.c new file +10 -0
+static int i2s_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long parent_rate)
+{
+	const struct i2s_pll_cfg *pll_cfg;
+	int i;
+
+	if (readl((void *)FPGA_VER_INFO) <= FPGA_VER_27M)
+		pll_cfg = i2s_pll_cfg_27m;
+	else
+		pll_cfg = i2s_pll_cfg_28m;

As I mentioned above we need to check board version in common place and then just use some variable or structure member for comparison.

Otherwise it looks pretty nice!

-Alexey

#3 lore raw

PATCH 1/1 clk/arc: Add I2S PLL clock driver

Hi Vineet,

Quoted 21 lines · Vineet Gupta
Quoted 14 lines · Alexey Brodkin

Hi Jose,

Quoted 11 lines · Jose Abreu

The ARC SDP I2S clock can be programmed using a
specific PLL.

This patch has the goal of adding a clock driver
that programs this PLL.

At this moment the rate values are hardcoded in
a table but in the future it would be ideal to
use a function which determines the PLL values
given the desired rate.

Quoted 6 lines
Quoted 2 lines
Signed-off-by Jose Abreu <joabreu at synopsys.com>

---

I believe these kind of patches worth sending to linux-snps mailing list (at least add it in Cc) so they won't be lost in time and could be used later as useful references.

Quoted 6 lines

Please do also CC the common clk maintainers

COMMON CLK FRAMEWORK

M:    Michael Turquette <mturquette at baylibre.com>
M:    Stephen Boyd <sboyd at codeaurora.org>
L:    linux-clk at vger.kernel.org

Well, this was just an experiment and I think this patch is still far from being ready to go mainline, that's why I did not cc'ed the clk maintainers. I will send a v2 soon so you can review and if you think its worth sending to mainline I will do that.

Quoted 155 lines
Quoted 155 lines
Quoted 29 lines
 arch/arc/boot/dts/axs10x_mb.dtsi |   5 ++
 drivers/clk/Makefile             |   1 +
 drivers/clk/arc/Makefile         |   1 +
 drivers/clk/arc/i2s_pll_clock.c  | 143 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 150 insertions(+)
 create mode 100644 drivers/clk/arc/Makefile
 create mode 100644 drivers/clk/arc/i2s_pll_clock.c
arch/arc/boot/dts/axs10x_mb.dtsi +5 -0
index ab5d570..9c68226 100644
@@ -23,6 +23,11 @@
 				#clock-cells = <0>;
 			};
 
+			i2sclk: i2sclk {
+				compatible = "snps,i2s-pll-clock";
+				#clock-cells = <0>;
+			};
+
 			apbclk: apbclk {
 				compatible = "fixed-clock";
 				clock-frequency = <50000000>;
drivers/clk/Makefile +0 -0
index 46869d6..620e47f 100644

drivers/clk/Makefile obj-$(CONFIG_X86) += x86/ · line 87

Quoted 4 lines
drivers/clk/Makefile +1 -0
@@ -84,3 +84,4 @@ obj-$(CONFIG_X86)			+= x86/
 obj-$(CONFIG_ARCH_ZX)			+= zte/
 obj-$(CONFIG_ARCH_ZYNQ)			+= zynq/
 obj-$(CONFIG_H8300)		+= h8300/
+obj-$(CONFIG_ARC)		+= arc/

Two things here:
[1] This clock is relevant to only one board but not SoC, CPU core or whole architecture, so it makes sense to make it dependent on the AXS platform, i.e. CONFIG_ARC_PLAT_AXS10X.

[2] Something similar is applicable to folder name, I would suggest to

     use "drivers/clk/axs10x"
Quoted 13 lines
drivers/clk/arc/Makefile new file +1 -0
new file mode 100644
index 0000000..01996b8
@@ -0,0 +1 @@
+obj-y += i2s_pll_clock.o
drivers/clk/arc/i2s_pll_clock.c new file +0 -0
new file mode 100644
index 0000000..8c401f1

drivers/clk/arc/i2s_pll_clock.c line 2

Quoted 2 lines
drivers/clk/arc/i2s_pll_clock.c new file +2 -0
@@ -0,0 +1,143 @@
+/*
+ * Synopsys I2S PLL clock driver

Again that's not generic SNPS I2S clock but clock driver for a particular board.

drivers/clk/arc/i2s_pll_clock.c line 4

Quoted 2 lines
drivers/clk/arc/i2s_pll_clock.c new file +2 -0
+ *
+ * drivers/clk/arc/i2s_pll_clock.c

BTW not sure why this path could be needed here.
IMHO that might be safely dropped.

Quoted 6 lines
drivers/clk/arc/i2s_pll_clock.c new file +6 -0
+ *
+ * Copyright (C) 2016 Synopsys
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.

drivers/clk/arc/i2s_pll_clock.c line 20

Quoted 10 lines
drivers/clk/arc/i2s_pll_clock.c new file +10 -0
+ */
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+/* FPGA Version Info */
+#define FPGA_VER_INFO	0xE0011230
+#define FPGA_VER_27M	0x000FBED9

This is a little bit tricky.
We'll need to do that check in each and every clock driver for AXS10x and so it worth putting this code in some common location (I mean common for all axs10x clock drivers).

Quoted 46 lines
drivers/clk/arc/i2s_pll_clock.c new file +45 -0
+/* PLL registers addresses */
+#define PLL_IDIV_ADDR	0xE00100A0
+#define PLL_FBDIV_ADDR	0xE00100A4
+#define PLL_ODIV0_ADDR	0xE00100A8
+#define PLL_ODIV1_ADDR	0xE00100AC
 
+struct i2s_pll_clk {
+	struct clk_hw hw;
+};
+
+struct i2s_pll_cfg {
+	unsigned int rate;
+	unsigned int idiv;
+	unsigned int fbdiv;
+	unsigned int odiv0;
+	unsigned int odiv1;
+};
+
+static const struct i2s_pll_cfg i2s_pll_cfg_27m[] = {
+	/* 27 Mhz */
+	{ 1024000, 0x104, 0x451, 0x10E38, 0x2000 },
+	{ 1411200, 0x104, 0x596, 0x10D35, 0x2000 },
+	{ 1536000, 0x208, 0xA28, 0x10B2C, 0x2000 },
+	{ 2048000, 0x82, 0x451, 0x10E38, 0x2000 },
+	{ 2822400, 0x82, 0x596, 0x10D35, 0x2000 },
+	{ 3072000, 0x104, 0xA28, 0x10B2C, 0x2000 },
+	{ 0, 0, 0, 0, 0 },
+};
+
+static const struct i2s_pll_cfg i2s_pll_cfg_28m[] = {
+	/* 28.224 Mhz */
+	{ 1024000, 0x82, 0x105, 0x107DF, 0x2000 },
+	{ 1411200, 0x28A, 0x1, 0x10001, 0x2000 },
+	{ 1536000, 0xA28, 0x187, 0x10042, 0x2000 },
+	{ 2048000, 0x41, 0x105, 0x107DF, 0x2000 },
+	{ 2822400, 0x145, 0x1, 0x10001, 0x2000 },
+	{ 3072000, 0x514, 0x187, 0x10042, 0x2000 },
+	{ 0, 0, 0, 0, 0 },
+};
+
+static unsigned long i2s_pll_recalc_rate(struct clk_hw *hw,
+			unsigned long parent_rate)
+{
+	/* TODO */
+	pr_info("%s: parent_rate=%ld\n", __func__, parent_rate);
+	return parent_rate;

drivers/clk/arc/i2s_pll_clock.c

Quoted 10 lines
drivers/clk/arc/i2s_pll_clock.c new file +10 -0
+}
+
+static long i2s_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long *prate)
+{
+	/* TODO: Round rate to nearest valid rate */
+	*prate = rate;
+	pr_info("%s: rate=%ld, prate=%ld\n", __func__, rate, *prate);
+	return *prate;
+}

These are now just dummy functions so probably they could be dropped.

drivers/clk/arc/i2s_pll_clock.c

Quoted 10 lines
drivers/clk/arc/i2s_pll_clock.c new file +10 -0
+static int i2s_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long parent_rate)
+{
+	const struct i2s_pll_cfg *pll_cfg;
+	int i;
+
+	if (readl((void *)FPGA_VER_INFO) <= FPGA_VER_27M)
+		pll_cfg = i2s_pll_cfg_27m;
+	else
+		pll_cfg = i2s_pll_cfg_28m;

As I mentioned above we need to check board version in common place and then just use some variable or structure member for comparison.

Quoted 4 lines
Quoted 3 lines

Otherwise it looks pretty nice!

-Alexey

Best regards,
Jose Miguel Abreu

#4 lore raw

PATCH 1/1 clk/arc: Add I2S PLL clock driver

On Tuesday 29 March 2016 11:21 PM, Jose Abreu wrote:

Well, this was just an experiment and I think this patch is still far from being ready to go mainline, that's why I did not cc'ed the clk maintainers. I will send a v2 soon so you can review and if you think its worth sending to mainline I will do that.

It never hurts to CC actual maintainer as they will be able to provide you the best feedback.
Each of us have our domain expertise so I may review your code superficially but only the person who knows the subsystem inside-out can tell you exactly what to do.

They are grumpy at times and might bitch a bit but as they they "it is your code that gets criticized - not you" I've learnt this myself over the years :-)

-Vineet

#5 lore raw

PATCH 1/1 clk/arc: Add I2S PLL clock driver

In reply to Alexey Brodkin

Hi Alexey,

Quoted 14 lines · Alexey Brodkin

Hi Jose,

Quoted 11 lines · Jose Abreu

The ARC SDP I2S clock can be programmed using a
specific PLL.

This patch has the goal of adding a clock driver
that programs this PLL.

At this moment the rate values are hardcoded in
a table but in the future it would be ideal to
use a function which determines the PLL values
given the desired rate.

Quoted 6 lines
Quoted 2 lines
Signed-off-by Jose Abreu <joabreu at synopsys.com>

---

I believe these kind of patches worth sending to linux-snps mailing list (at least add it in Cc) so they won't be lost in time and could be used later as useful references.

Agree.

Quoted 39 lines
Quoted 30 lines
 arch/arc/boot/dts/axs10x_mb.dtsi |   5 ++
 drivers/clk/Makefile             |   1 +
 drivers/clk/arc/Makefile         |   1 +
 drivers/clk/arc/i2s_pll_clock.c  | 143 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 150 insertions(+)
 create mode 100644 drivers/clk/arc/Makefile
 create mode 100644 drivers/clk/arc/i2s_pll_clock.c
arch/arc/boot/dts/axs10x_mb.dtsi +5 -0
index ab5d570..9c68226 100644
@@ -23,6 +23,11 @@
 				#clock-cells = <0>;
 			};
 
+			i2sclk: i2sclk {
+				compatible = "snps,i2s-pll-clock";
+				#clock-cells = <0>;
+			};
+
 			apbclk: apbclk {
 				compatible = "fixed-clock";
 				clock-frequency = <50000000>;
drivers/clk/Makefile +0 -0
index 46869d6..620e47f 100644

drivers/clk/Makefile obj-$(CONFIG_X86) += x86/ · line 87

Quoted 4 lines
drivers/clk/Makefile +1 -0
@@ -84,3 +84,4 @@ obj-$(CONFIG_X86)			+= x86/
 obj-$(CONFIG_ARCH_ZX)			+= zte/
 obj-$(CONFIG_ARCH_ZYNQ)			+= zynq/
 obj-$(CONFIG_H8300)		+= h8300/
+obj-$(CONFIG_ARC)		+= arc/

Two things here:
[1] This clock is relevant to only one board but not SoC, CPU core or whole architecture, so it makes sense to make it dependent on the AXS platform, i.e. CONFIG_ARC_PLAT_AXS10X.

Quoted 2 lines

[2] Something similar is applicable to folder name, I would suggest to

     use "drivers/clk/axs10x"

I will rename to axs10x and use CONFIG_ARC_PLAT_AXS10X.

Quoted 34 lines
Quoted 13 lines
drivers/clk/arc/Makefile new file +1 -0
new file mode 100644
index 0000000..01996b8
@@ -0,0 +1 @@
+obj-y += i2s_pll_clock.o
drivers/clk/arc/i2s_pll_clock.c new file +0 -0
new file mode 100644
index 0000000..8c401f1

drivers/clk/arc/i2s_pll_clock.c line 2

Quoted 2 lines
drivers/clk/arc/i2s_pll_clock.c new file +2 -0
@@ -0,0 +1,143 @@
+/*
+ * Synopsys I2S PLL clock driver

Again that's not generic SNPS I2S clock but clock driver for a particular board.

drivers/clk/arc/i2s_pll_clock.c line 4

Quoted 2 lines
drivers/clk/arc/i2s_pll_clock.c new file +2 -0
+ *
+ * drivers/clk/arc/i2s_pll_clock.c

BTW not sure why this path could be needed here.
IMHO that might be safely dropped.

Quoted 12 lines
drivers/clk/arc/i2s_pll_clock.c new file +12 -0
+ *
+ * Copyright (C) 2016 Synopsys
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
Quoted 9 lines

drivers/clk/arc/i2s_pll_clock.c line 20

Quoted 4 lines
drivers/clk/arc/i2s_pll_clock.c new file +4 -0
+
+/* FPGA Version Info */
+#define FPGA_VER_INFO	0xE0011230
+#define FPGA_VER_27M	0x000FBED9

This is a little bit tricky.
We'll need to do that check in each and every clock driver for AXS10x and so it worth putting this code in some common location (I mean common for all axs10x clock drivers).

This is not common to all clocks. We are expecting a new firmware release which will change the reference clock input for the I2S PLL from 27Mhz to 28.224 Mhz. To support both the old and the new firmware we check this version info register so that we can determine which firmware version is being used. Due to this change the PLL dividers are different. As the remaining clocks will be unaffected I think this can remain only in this driver.

Quoted 49 lines
Quoted 49 lines
drivers/clk/arc/i2s_pll_clock.c new file +48 -0
+/* PLL registers addresses */
+#define PLL_IDIV_ADDR	0xE00100A0
+#define PLL_FBDIV_ADDR	0xE00100A4
+#define PLL_ODIV0_ADDR	0xE00100A8
+#define PLL_ODIV1_ADDR	0xE00100AC
 
+struct i2s_pll_clk {
+	struct clk_hw hw;
+};
+
+struct i2s_pll_cfg {
+	unsigned int rate;
+	unsigned int idiv;
+	unsigned int fbdiv;
+	unsigned int odiv0;
+	unsigned int odiv1;
+};
+
+static const struct i2s_pll_cfg i2s_pll_cfg_27m[] = {
+	/* 27 Mhz */
+	{ 1024000, 0x104, 0x451, 0x10E38, 0x2000 },
+	{ 1411200, 0x104, 0x596, 0x10D35, 0x2000 },
+	{ 1536000, 0x208, 0xA28, 0x10B2C, 0x2000 },
+	{ 2048000, 0x82, 0x451, 0x10E38, 0x2000 },
+	{ 2822400, 0x82, 0x596, 0x10D35, 0x2000 },
+	{ 3072000, 0x104, 0xA28, 0x10B2C, 0x2000 },
+	{ 0, 0, 0, 0, 0 },
+};
+
+static const struct i2s_pll_cfg i2s_pll_cfg_28m[] = {
+	/* 28.224 Mhz */
+	{ 1024000, 0x82, 0x105, 0x107DF, 0x2000 },
+	{ 1411200, 0x28A, 0x1, 0x10001, 0x2000 },
+	{ 1536000, 0xA28, 0x187, 0x10042, 0x2000 },
+	{ 2048000, 0x41, 0x105, 0x107DF, 0x2000 },
+	{ 2822400, 0x145, 0x1, 0x10001, 0x2000 },
+	{ 3072000, 0x514, 0x187, 0x10042, 0x2000 },
+	{ 0, 0, 0, 0, 0 },
+};
+
+static unsigned long i2s_pll_recalc_rate(struct clk_hw *hw,
+			unsigned long parent_rate)
+{
+	/* TODO */
+	pr_info("%s: parent_rate=%ld\n", __func__, parent_rate);
+	return parent_rate;
+}
+
+static long i2s_pll_round_rate(struct clk_hw *hw, unsigned long rate,
Quoted 8 lines

drivers/clk/arc/i2s_pll_clock.c

Quoted 7 lines
drivers/clk/arc/i2s_pll_clock.c new file +7 -0
+			unsigned long *prate)
+{
+	/* TODO: Round rate to nearest valid rate */
+	*prate = rate;
+	pr_info("%s: rate=%ld, prate=%ld\n", __func__, rate, *prate);
+	return *prate;
+}

These are now just dummy functions so probably they could be dropped.

Agree but I will have to check if the clock core needs this functions to be defined.

Quoted 13 lines

drivers/clk/arc/i2s_pll_clock.c

Quoted 10 lines
drivers/clk/arc/i2s_pll_clock.c new file +10 -0
+static int i2s_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			unsigned long parent_rate)
+{
+	const struct i2s_pll_cfg *pll_cfg;
+	int i;
+
+	if (readl((void *)FPGA_VER_INFO) <= FPGA_VER_27M)
+		pll_cfg = i2s_pll_cfg_27m;
+	else
+		pll_cfg = i2s_pll_cfg_28m;

As I mentioned above we need to check board version in common place and then just use some variable or structure member for comparison.

Quoted 3 lines

Otherwise it looks pretty nice!

-Alexey

Thanks for your comments!

Best regards,
Jose Miguel Abreu