All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/9] add support for Sama5d2 audio PLLs and enable ClassD
@ 2017-07-13  7:49 ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: Quentin Schulz, cyrille.pitchen, thomas.petazzoni, linux-clk,
	devicetree, linux-kernel, alsa-devel, linux-arm-kernel

This patch series adds support for the audio PLLs and enables ClassD that
can be found in ATMEL Sama5d2 SoC.

There are two audio PLLs (PMC and PAD) that shares the same parent (FRAC).
FRAC can output between 620 and 700MHz and only multiply the rate of its
parent. The two audio PLLs then divide the FRAC rate to best match the
asked rate.

I basically took an old patch series posted by Nicolas on December, 6th
2016[1][2][3] and the comments Boris did on the first version[4] Nicolas
sent on July, 15th 2015.

I also fixed the function used to compute the divisors, removed useless
spinlocks and added a range to the audio frac PLL to stay within vendor's
supported range. Clocks that are children of gclk (generated-clk) are now
able to propagate rate to the audio PLL clocks when needed.

However, there are multiple children clocks that could technically
change the rate of audio_pll (via gck). With the rate locking introduced
in Jerome Brunet's patch series[5], the first consumer to enable the clock
will be the one definitely setting the rate of the clock. Without the rate
locking, the last consumer to set the rate will be able to mess with the
rate.
Since audio IPs are most likely to request the same rate, we enforce
that the only clks able to modify gck rate are those of audio IPs.

To remain consistent, we deny other clocks to be children of audio_pll.

Thanks,
Quentin

[1] https://patchwork.kernel.org/patch/9462351/
[2] https://patchwork.kernel.org/patch/9462347/
[3] https://patchwork.kernel.org/patch/9462349/
[4] https://www.spinics.net/lists/arm-kernel/msg436120.html
[5] http://www.spinics.net/lists/linux-clk/msg17927.html

Cyrille Pitchen (2):
  ARM: dts: at91: sama5d2: add classd nodes
  ARM: dts: at91: sama5d2_xplained: add pin muxing and enable classd

Quentin Schulz (7):
  clk: at91: clk-generated: remove useless divisor loop
  clk: at91: add audio plls to the compatible list in DT binding
  clk: at91: add audio pll clock drivers
  clk: at91: clk-generated: create function to find best_diff
  clk: at91: clk-generated: make gclk determine audio_pll rate
  ASoC: atmel-classd: remove aclk clock from DT binding
  ASoC: atmel-classd: remove aclk clock

 .../devicetree/bindings/clock/at91-clock.txt       |  10 +
 .../devicetree/bindings/sound/atmel-classd.txt     |   9 +-
 arch/arm/boot/dts/at91-sama5d2_xplained.dts        |  16 ++
 arch/arm/boot/dts/sama5d2.dtsi                     |  39 +++-
 arch/arm/mach-at91/Kconfig                         |   4 +
 drivers/clk/at91/Makefile                          |   2 +
 drivers/clk/at91/clk-audio-pll-pad.c               | 206 ++++++++++++++++++
 drivers/clk/at91/clk-audio-pll-pmc.c               | 174 +++++++++++++++
 drivers/clk/at91/clk-audio-pll.c                   | 239 +++++++++++++++++++++
 drivers/clk/at91/clk-generated.c                   | 101 +++++++--
 include/linux/clk/at91_pmc.h                       |  25 +++
 sound/soc/atmel/atmel-classd.c                     |  47 ++--
 12 files changed, 813 insertions(+), 59 deletions(-)
 create mode 100644 drivers/clk/at91/clk-audio-pll-pad.c
 create mode 100644 drivers/clk/at91/clk-audio-pll-pmc.c
 create mode 100644 drivers/clk/at91/clk-audio-pll.c

-- 
2.11.0

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 0/9] add support for Sama5d2 audio PLLs and enable ClassD
@ 2017-07-13  7:49 ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: linux-arm-kernel

This patch series adds support for the audio PLLs and enables ClassD that
can be found in ATMEL Sama5d2 SoC.

There are two audio PLLs (PMC and PAD) that shares the same parent (FRAC).
FRAC can output between 620 and 700MHz and only multiply the rate of its
parent. The two audio PLLs then divide the FRAC rate to best match the
asked rate.

I basically took an old patch series posted by Nicolas on December, 6th
2016[1][2][3] and the comments Boris did on the first version[4] Nicolas
sent on July, 15th 2015.

I also fixed the function used to compute the divisors, removed useless
spinlocks and added a range to the audio frac PLL to stay within vendor's
supported range. Clocks that are children of gclk (generated-clk) are now
able to propagate rate to the audio PLL clocks when needed.

However, there are multiple children clocks that could technically
change the rate of audio_pll (via gck). With the rate locking introduced
in Jerome Brunet's patch series[5], the first consumer to enable the clock
will be the one definitely setting the rate of the clock. Without the rate
locking, the last consumer to set the rate will be able to mess with the
rate.
Since audio IPs are most likely to request the same rate, we enforce
that the only clks able to modify gck rate are those of audio IPs.

To remain consistent, we deny other clocks to be children of audio_pll.

Thanks,
Quentin

[1] https://patchwork.kernel.org/patch/9462351/
[2] https://patchwork.kernel.org/patch/9462347/
[3] https://patchwork.kernel.org/patch/9462349/
[4] https://www.spinics.net/lists/arm-kernel/msg436120.html
[5] http://www.spinics.net/lists/linux-clk/msg17927.html

Cyrille Pitchen (2):
  ARM: dts: at91: sama5d2: add classd nodes
  ARM: dts: at91: sama5d2_xplained: add pin muxing and enable classd

Quentin Schulz (7):
  clk: at91: clk-generated: remove useless divisor loop
  clk: at91: add audio plls to the compatible list in DT binding
  clk: at91: add audio pll clock drivers
  clk: at91: clk-generated: create function to find best_diff
  clk: at91: clk-generated: make gclk determine audio_pll rate
  ASoC: atmel-classd: remove aclk clock from DT binding
  ASoC: atmel-classd: remove aclk clock

 .../devicetree/bindings/clock/at91-clock.txt       |  10 +
 .../devicetree/bindings/sound/atmel-classd.txt     |   9 +-
 arch/arm/boot/dts/at91-sama5d2_xplained.dts        |  16 ++
 arch/arm/boot/dts/sama5d2.dtsi                     |  39 +++-
 arch/arm/mach-at91/Kconfig                         |   4 +
 drivers/clk/at91/Makefile                          |   2 +
 drivers/clk/at91/clk-audio-pll-pad.c               | 206 ++++++++++++++++++
 drivers/clk/at91/clk-audio-pll-pmc.c               | 174 +++++++++++++++
 drivers/clk/at91/clk-audio-pll.c                   | 239 +++++++++++++++++++++
 drivers/clk/at91/clk-generated.c                   | 101 +++++++--
 include/linux/clk/at91_pmc.h                       |  25 +++
 sound/soc/atmel/atmel-classd.c                     |  47 ++--
 12 files changed, 813 insertions(+), 59 deletions(-)
 create mode 100644 drivers/clk/at91/clk-audio-pll-pad.c
 create mode 100644 drivers/clk/at91/clk-audio-pll-pmc.c
 create mode 100644 drivers/clk/at91/clk-audio-pll.c

-- 
2.11.0

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 1/9] clk: at91: clk-generated: remove useless divisor loop
  2017-07-13  7:49 ` Quentin Schulz
  (?)
@ 2017-07-13  7:49   ` Quentin Schulz
  -1 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: Quentin Schulz, cyrille.pitchen, thomas.petazzoni, linux-clk,
	devicetree, linux-kernel, alsa-devel, linux-arm-kernel

The driver requests the current clk rate of each of its parent clocks to
decide whether a clock rate is suitable or not. It does not request
determine_rate from a parent clock which could request a rate change in
parent clock (i.e. there is no parent rate propagation).

We know the rate we want (passed along req argument of the function) and
the parent clock rate, thus we know the closest rounded divisor, we
don't need to iterate over the available divisors to find the best one
for a given clock.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/clk/at91/clk-generated.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index f0b7ae904ce2..ef4b4e03de04 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -124,19 +124,18 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
 		    (gck->range.max && min_rate > gck->range.max))
 			continue;
 
-		for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
-			tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div);
-			tmp_diff = abs(req->rate - tmp_rate);
-
-			if (best_diff < 0 || best_diff > tmp_diff) {
-				best_rate = tmp_rate;
-				best_diff = tmp_diff;
-				req->best_parent_rate = parent_rate;
-				req->best_parent_hw = parent;
-			}
-
-			if (!best_diff || tmp_rate < req->rate)
-				break;
+		div = DIV_ROUND_CLOSEST(parent_rate, req->rate);
+		if (!div)
+			tmp_rate = parent_rate;
+		else
+			tmp_rate = parent_rate / div;
+		tmp_diff = abs(req->rate - tmp_rate);
+
+		if (best_diff < 0 || best_diff > tmp_diff) {
+			best_rate = tmp_rate;
+			best_diff = tmp_diff;
+			req->best_parent_rate = parent_rate;
+			req->best_parent_hw = parent;
 		}
 
 		if (!best_diff)
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 1/9] clk: at91: clk-generated: remove useless divisor loop
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: thomas.petazzoni, devicetree, alsa-devel, linux-kernel,
	Quentin Schulz, cyrille.pitchen, linux-clk, linux-arm-kernel

The driver requests the current clk rate of each of its parent clocks to
decide whether a clock rate is suitable or not. It does not request
determine_rate from a parent clock which could request a rate change in
parent clock (i.e. there is no parent rate propagation).

We know the rate we want (passed along req argument of the function) and
the parent clock rate, thus we know the closest rounded divisor, we
don't need to iterate over the available divisors to find the best one
for a given clock.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/clk/at91/clk-generated.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index f0b7ae904ce2..ef4b4e03de04 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -124,19 +124,18 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
 		    (gck->range.max && min_rate > gck->range.max))
 			continue;
 
-		for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
-			tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div);
-			tmp_diff = abs(req->rate - tmp_rate);
-
-			if (best_diff < 0 || best_diff > tmp_diff) {
-				best_rate = tmp_rate;
-				best_diff = tmp_diff;
-				req->best_parent_rate = parent_rate;
-				req->best_parent_hw = parent;
-			}
-
-			if (!best_diff || tmp_rate < req->rate)
-				break;
+		div = DIV_ROUND_CLOSEST(parent_rate, req->rate);
+		if (!div)
+			tmp_rate = parent_rate;
+		else
+			tmp_rate = parent_rate / div;
+		tmp_diff = abs(req->rate - tmp_rate);
+
+		if (best_diff < 0 || best_diff > tmp_diff) {
+			best_rate = tmp_rate;
+			best_diff = tmp_diff;
+			req->best_parent_rate = parent_rate;
+			req->best_parent_hw = parent;
 		}
 
 		if (!best_diff)
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 1/9] clk: at91: clk-generated: remove useless divisor loop
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: linux-arm-kernel

The driver requests the current clk rate of each of its parent clocks to
decide whether a clock rate is suitable or not. It does not request
determine_rate from a parent clock which could request a rate change in
parent clock (i.e. there is no parent rate propagation).

We know the rate we want (passed along req argument of the function) and
the parent clock rate, thus we know the closest rounded divisor, we
don't need to iterate over the available divisors to find the best one
for a given clock.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/clk/at91/clk-generated.c | 25 ++++++++++++-------------
 1 file changed, 12 insertions(+), 13 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index f0b7ae904ce2..ef4b4e03de04 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -124,19 +124,18 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
 		    (gck->range.max && min_rate > gck->range.max))
 			continue;
 
-		for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
-			tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div);
-			tmp_diff = abs(req->rate - tmp_rate);
-
-			if (best_diff < 0 || best_diff > tmp_diff) {
-				best_rate = tmp_rate;
-				best_diff = tmp_diff;
-				req->best_parent_rate = parent_rate;
-				req->best_parent_hw = parent;
-			}
-
-			if (!best_diff || tmp_rate < req->rate)
-				break;
+		div = DIV_ROUND_CLOSEST(parent_rate, req->rate);
+		if (!div)
+			tmp_rate = parent_rate;
+		else
+			tmp_rate = parent_rate / div;
+		tmp_diff = abs(req->rate - tmp_rate);
+
+		if (best_diff < 0 || best_diff > tmp_diff) {
+			best_rate = tmp_rate;
+			best_diff = tmp_diff;
+			req->best_parent_rate = parent_rate;
+			req->best_parent_hw = parent;
 		}
 
 		if (!best_diff)
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 2/9] clk: at91: add audio plls to the compatible list in DT binding
  2017-07-13  7:49 ` Quentin Schulz
@ 2017-07-13  7:49   ` Quentin Schulz
  -1 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: Quentin Schulz, cyrille.pitchen, thomas.petazzoni, linux-clk,
	devicetree, linux-kernel, alsa-devel, linux-arm-kernel

This new clock driver set allows to have a fractional divided clock that
would generate a precise clock particularly suitable for audio
applications.

The main audio pll clock has two children clocks: one that is connected
to the PMC, the other that can directly drive a pad. As these two routes
have different enable bits and different dividers and divider formulas,
they are handled by two different drivers.

This adds the audio plls (frac, pad and pmc) to the compatible list of
at91 clocks in DT binding.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Rob Herring <robh@kernel.org>
---

added in v2:
  - split from big patch with pll drivers and dt-binding

 Documentation/devicetree/bindings/clock/at91-clock.txt | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt
index 5f3ad65daf69..51c259a92d02 100644
--- a/Documentation/devicetree/bindings/clock/at91-clock.txt
+++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
@@ -81,6 +81,16 @@ Required properties:
 	"atmel,sama5d2-clk-generated":
 		at91 generated clock
 
+	"atmel,sama5d2-clk-audio-pll-frac":
+		at91 audio fractional pll
+
+	"atmel,sama5d2-clk-audio-pll-pad":
+		at91 audio pll CLK_AUDIO output pin
+
+	"atmel,sama5d2-clk-audio-pll-pmc"
+		at91 audio pll output on AUDIOPLLCLK that feeds the PMC
+		and can be used by peripheral clock or generic clock
+
 Required properties for SCKC node:
 - reg : defines the IO memory reserved for the SCKC.
 - #size-cells : shall be 0 (reg is used to encode clk id).
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 2/9] clk: at91: add audio plls to the compatible list in DT binding
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: linux-arm-kernel

This new clock driver set allows to have a fractional divided clock that
would generate a precise clock particularly suitable for audio
applications.

The main audio pll clock has two children clocks: one that is connected
to the PMC, the other that can directly drive a pad. As these two routes
have different enable bits and different dividers and divider formulas,
they are handled by two different drivers.

This adds the audio plls (frac, pad and pmc) to the compatible list of
at91 clocks in DT binding.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Rob Herring <robh@kernel.org>
---

added in v2:
  - split from big patch with pll drivers and dt-binding

 Documentation/devicetree/bindings/clock/at91-clock.txt | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt
index 5f3ad65daf69..51c259a92d02 100644
--- a/Documentation/devicetree/bindings/clock/at91-clock.txt
+++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
@@ -81,6 +81,16 @@ Required properties:
 	"atmel,sama5d2-clk-generated":
 		at91 generated clock
 
+	"atmel,sama5d2-clk-audio-pll-frac":
+		at91 audio fractional pll
+
+	"atmel,sama5d2-clk-audio-pll-pad":
+		at91 audio pll CLK_AUDIO output pin
+
+	"atmel,sama5d2-clk-audio-pll-pmc"
+		at91 audio pll output on AUDIOPLLCLK that feeds the PMC
+		and can be used by peripheral clock or generic clock
+
 Required properties for SCKC node:
 - reg : defines the IO memory reserved for the SCKC.
 - #size-cells : shall be 0 (reg is used to encode clk id).
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 3/9] clk: at91: add audio pll clock drivers
  2017-07-13  7:49 ` Quentin Schulz
  (?)
@ 2017-07-13  7:49   ` Quentin Schulz
  -1 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: Quentin Schulz, cyrille.pitchen, thomas.petazzoni, linux-clk,
	devicetree, linux-kernel, alsa-devel, linux-arm-kernel,
	Nicolas Ferre

This new clock driver set allows to have a fractional divided clock that
would generate a precise clock particularly suitable for audio
applications.

The main audio pll clock has two children clocks: one that is connected
to the PMC, the other that can directly drive a pad. As these two routes
have different enable bits and different dividers and divider formulas,
they are handled by two different drivers. Each of them could modify the
rate of the main audio pll parent.

The main audio pll clock can output 620MHz to 700MHz.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---

v2:
  - split DT binding in a different patch,
  - removed unused AUDIO_PLL_*FOUT* defines from clk-audio-pll-pmc,
  - split classD modifications in a different patch,

 arch/arm/mach-at91/Kconfig           |   4 +
 drivers/clk/at91/Makefile            |   2 +
 drivers/clk/at91/clk-audio-pll-pad.c | 206 ++++++++++++++++++++++++++++++
 drivers/clk/at91/clk-audio-pll-pmc.c | 174 +++++++++++++++++++++++++
 drivers/clk/at91/clk-audio-pll.c     | 239 +++++++++++++++++++++++++++++++++++
 include/linux/clk/at91_pmc.h         |  25 ++++
 6 files changed, 650 insertions(+)
 create mode 100644 drivers/clk/at91/clk-audio-pll-pad.c
 create mode 100644 drivers/clk/at91/clk-audio-pll-pmc.c
 create mode 100644 drivers/clk/at91/clk-audio-pll.c

diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index d735e5fc4772..9ae14d59a9ce 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -26,6 +26,7 @@ config SOC_SAMA5D2
 	select HAVE_AT91_USB_CLK
 	select HAVE_AT91_H32MX
 	select HAVE_AT91_GENERATED_CLK
+	select HAVE_AT91_AUDIO_PLL
 	select PINCTRL_AT91PIO4
 	help
 	  Select this if ou are using one of Atmel's SAMA5D2 family SoC.
@@ -125,6 +126,9 @@ config HAVE_AT91_H32MX
 config HAVE_AT91_GENERATED_CLK
 	bool
 
+config HAVE_AT91_AUDIO_PLL
+	bool
+
 config SOC_SAM_V4_V5
 	bool
 
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 13e67bd35cff..c9353d17763a 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -6,6 +6,8 @@ obj-y += pmc.o sckc.o
 obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o
 obj-y += clk-system.o clk-peripheral.o clk-programmable.o
 
+obj-$(CONFIG_HAVE_AT91_AUDIO_PLL)	+= clk-audio-pll.o
+obj-$(CONFIG_HAVE_AT91_AUDIO_PLL)	+= clk-audio-pll-pmc.o clk-audio-pll-pad.o
 obj-$(CONFIG_HAVE_AT91_UTMI)		+= clk-utmi.o
 obj-$(CONFIG_HAVE_AT91_USB_CLK)		+= clk-usb.o
 obj-$(CONFIG_HAVE_AT91_SMD)		+= clk-smd.o
diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
new file mode 100644
index 000000000000..10dd6d625696
--- /dev/null
+++ b/drivers/clk/at91/clk-audio-pll-pad.c
@@ -0,0 +1,206 @@
+/*
+ *  Copyright (C) 2016 Atmel Corporation,
+ *                     Nicolas Ferre <nicolas.ferre@atmel.com>
+ *  Copyright (C) 2017 Free Electrons,
+ *		       Quentin Schulz <quentin.schulz@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "pmc.h"
+
+/*
+ * DOC: PAD output for fractional PLL clock for audio
+ *
+ * Traits of this clock:
+ * enable - clk_enable writes divisors and enables PAD output
+ * rate - rate is adjustable.
+ *        clk->rate = parent->rate / (qdaudio * div))
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define AUDIO_PLL_QDPAD(qd, div)	((AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(qd) & \
+					  AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK) | \
+					 (AT91_PMC_AUDIO_PLL_QDPAD_DIV(div) & \
+					  AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK))
+
+struct clk_audio_pad {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	u8 qdaudio;
+	u8 div;
+};
+
+#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw)
+
+static int clk_audio_pll_pad_enable(struct clk_hw *hw)
+{
+	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
+
+	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1,
+			   AT91_PMC_AUDIO_PLL_QDPAD_MASK,
+			   AUDIO_PLL_QDPAD(apad_ck->qdaudio, apad_ck->div));
+	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PADEN, AT91_PMC_AUDIO_PLL_PADEN);
+
+	return 0;
+}
+
+static void clk_audio_pll_pad_disable(struct clk_hw *hw)
+{
+	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
+
+	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PADEN, 0);
+}
+
+static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw,
+						   unsigned long parent_rate)
+{
+	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
+	unsigned long apad_rate = 0;
+
+	if (apad_ck->qdaudio && apad_ck->div)
+		apad_rate = parent_rate / (apad_ck->qdaudio * apad_ck->div);
+
+	pr_debug("A PLL/PAD: %s, apad_rate = %lu (div = %u, qdaudio = %u)\n",
+		 __func__, apad_rate, apad_ck->div, apad_ck->qdaudio);
+
+	return apad_rate;
+}
+
+static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
+					 unsigned long *parent_rate)
+{
+	struct clk_hw *pclk = clk_hw_get_parent(hw);
+	long best_rate = -EINVAL;
+	unsigned long best_parent_rate;
+	unsigned long tmp_qd;
+	u32 div;
+	long tmp_rate;
+	int tmp_diff;
+	int best_diff = -1;
+
+	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
+		 rate, *parent_rate);
+
+	/*
+	 * Rate divisor is actually made of two different divisors, multiplied
+	 * between themselves before dividing the rate.
+	 * tmp_qd goes from 1 to 31 and div is either 2 or 3.
+	 * In order to avoid testing twice the rate divisor (e.g. divisor 12 can
+	 * be found with (tmp_qd, div) = (2, 6) or (3, 4)), we remove any loop
+	 * for a rate divisor when div is 2 and tmp_qd is a multiple of 3.
+	 * We cannot inverse it (condition div is 3 and tmp_qd is even) or we
+	 * would miss some rate divisor that aren't reachable with div being 2
+	 * (e.g. rate divisor 90 is made with div = 3 and tmp_qd = 30, thus
+	 * tmp_qd is even so we skip it because we think div 2 could make this
+	 * rate divisor which isn't possible since tmp_qd has to be <= 31).
+	 */
+	for (tmp_qd = 1; tmp_qd < AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX; tmp_qd++)
+		for (div = 2; div <= 3; div++) {
+			if (div == 2 && tmp_qd % 3 == 0)
+				continue;
+
+			best_parent_rate = clk_hw_round_rate(pclk,
+							rate * tmp_qd * div);
+			tmp_rate = best_parent_rate / (div * tmp_qd);
+			tmp_diff = abs(rate - tmp_rate);
+
+			if (best_diff < 0 || best_diff > tmp_diff) {
+				*parent_rate = best_parent_rate;
+				best_rate = tmp_rate;
+				best_diff = tmp_diff;
+			}
+		}
+
+	pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n",
+		 __func__, best_rate, best_parent_rate);
+
+	return best_rate;
+}
+
+static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long parent_rate)
+{
+	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
+	u8 tmp_div;
+
+	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
+		 rate, parent_rate);
+
+	if (!rate)
+		return -EINVAL;
+
+	tmp_div = parent_rate / rate;
+	if (tmp_div % 3 == 0) {
+		apad_ck->qdaudio = tmp_div / 3;
+		apad_ck->div = 3;
+	} else {
+		apad_ck->qdaudio = tmp_div / 2;
+		apad_ck->div = 2;
+	}
+
+	return 0;
+}
+
+static const struct clk_ops audio_pll_pad_ops = {
+	.enable = clk_audio_pll_pad_enable,
+	.disable = clk_audio_pll_pad_disable,
+	.recalc_rate = clk_audio_pll_pad_recalc_rate,
+	.round_rate = clk_audio_pll_pad_round_rate,
+	.set_rate = clk_audio_pll_pad_set_rate,
+};
+
+static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
+{
+	struct clk_audio_pad *apad_ck;
+	struct clk_init_data init;
+	struct regmap *regmap;
+	const char *parent_name;
+	const char *name = np->name;
+	int ret;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return;
+
+	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
+	if (!apad_ck)
+		return;
+
+	init.name = name;
+	init.ops = &audio_pll_pad_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+		CLK_SET_RATE_PARENT;
+
+	apad_ck->hw.init = &init;
+	apad_ck->regmap = regmap;
+
+	ret = clk_hw_register(NULL, &apad_ck->hw);
+	if (ret)
+		kfree(apad_ck);
+	else
+		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
+}
+
+CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
+	       "atmel,sama5d2-clk-audio-pll-pad",
+	       of_sama5d2_clk_audio_pll_pad_setup);
diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c
new file mode 100644
index 000000000000..7b0847ed7a4b
--- /dev/null
+++ b/drivers/clk/at91/clk-audio-pll-pmc.c
@@ -0,0 +1,174 @@
+/*
+ *  Copyright (C) 2016 Atmel Corporation,
+ *                     Nicolas Ferre <nicolas.ferre@atmel.com>
+ *  Copyright (C) 2017 Free Electrons,
+ *		       Quentin Schulz <quentin.schulz@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "pmc.h"
+
+/*
+ * DOC: PMC output for fractional PLL clock for audio
+ *
+ * Traits of this clock:
+ * enable - clk_enable writes qdpmc, and enables PMC output
+ * rate - rate is adjustable.
+ *        clk->rate = parent->rate / (qdpmc + 1)
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define AUDIO_PLL_QDPMC_MAX		(AT91_PMC_AUDIO_PLL_QDPMC_MASK >> \
+						AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
+struct clk_audio_pmc {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	u8 qdpmc;
+};
+
+#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw)
+
+static int clk_audio_pll_pmc_enable(struct clk_hw *hw)
+{
+	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
+
+	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PMCEN |
+			   AT91_PMC_AUDIO_PLL_QDPMC_MASK,
+			   AT91_PMC_AUDIO_PLL_PMCEN |
+			   AT91_PMC_AUDIO_PLL_QDPMC(apmc_ck->qdpmc));
+	return 0;
+}
+
+static void clk_audio_pll_pmc_disable(struct clk_hw *hw)
+{
+	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
+
+	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PMCEN, 0);
+}
+
+static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw,
+						   unsigned long parent_rate)
+{
+	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
+	unsigned long apmc_rate = 0;
+
+	apmc_rate = parent_rate / (apmc_ck->qdpmc + 1);
+
+	pr_debug("A PLL/PMC: %s, apmc_rate = %lu (qdpmc = %u)\n", __func__,
+		 apmc_rate, apmc_ck->qdpmc);
+
+	return apmc_rate;
+}
+
+static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
+					 unsigned long *parent_rate)
+{
+	struct clk_hw *pclk = clk_hw_get_parent(hw);
+	long best_rate = -EINVAL;
+	unsigned long best_parent_rate = 0;
+	u32 tmp_qd = 0, div;
+	long tmp_rate;
+	int tmp_diff;
+	int best_diff = -1;
+
+	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
+		 rate, *parent_rate);
+
+	for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) {
+		best_parent_rate = clk_round_rate(pclk->clk, rate * div);
+		tmp_rate = best_parent_rate / div;
+		tmp_diff = abs(rate - tmp_rate);
+
+		if (best_diff < 0 || best_diff > tmp_diff) {
+			*parent_rate = best_parent_rate;
+			best_rate = tmp_rate;
+			best_diff = tmp_diff;
+			tmp_qd = div;
+		}
+	}
+
+	pr_debug("A PLL/PMC: %s, best_rate = %ld, best_parent_rate = %lu (qd = %d)\n",
+		 __func__, best_rate, *parent_rate, tmp_qd - 1);
+
+	return best_rate;
+}
+
+static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long parent_rate)
+{
+	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
+
+	if (!rate)
+		return -EINVAL;
+
+	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
+		 rate, parent_rate);
+
+	apmc_ck->qdpmc = parent_rate / rate - 1;
+
+	return 0;
+}
+
+static const struct clk_ops audio_pll_pmc_ops = {
+	.enable = clk_audio_pll_pmc_enable,
+	.disable = clk_audio_pll_pmc_disable,
+	.recalc_rate = clk_audio_pll_pmc_recalc_rate,
+	.round_rate = clk_audio_pll_pmc_round_rate,
+	.set_rate = clk_audio_pll_pmc_set_rate,
+};
+
+static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
+{
+	struct clk_audio_pmc *apmc_ck;
+	struct clk_init_data init;
+	struct regmap *regmap;
+	const char *parent_name;
+	const char *name = np->name;
+	int ret;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return;
+
+	apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
+	if (!apmc_ck)
+		return;
+
+	init.name = name;
+	init.ops = &audio_pll_pmc_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+		CLK_SET_RATE_PARENT;
+
+	apmc_ck->hw.init = &init;
+	apmc_ck->regmap = regmap;
+
+	ret = clk_hw_register(NULL, &apmc_ck->hw);
+	if (ret)
+		kfree(apmc_ck);
+	else
+		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw);
+}
+
+CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
+	       "atmel,sama5d2-clk-audio-pll-pmc",
+	       of_sama5d2_clk_audio_pll_pmc_setup);
diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
new file mode 100644
index 000000000000..efc2cb58da85
--- /dev/null
+++ b/drivers/clk/at91/clk-audio-pll.c
@@ -0,0 +1,239 @@
+/*
+ *  Copyright (C) 2016 Atmel Corporation,
+ *		       Songjun Wu <songjun.wu@atmel.com>,
+ *                     Nicolas Ferre <nicolas.ferre@atmel.com>
+ *  Copyright (C) 2017 Free Electrons,
+ *		       Quentin Schulz <quentin.schulz@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "pmc.h"
+
+/*
+ * DOC: Fractional PLL clock for audio
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare puts audio PLL in reset state
+ * enable - clk_enable writes nd, fracr parameters and enables PLL
+ * rate - rate is adjustable.
+ *        clk->rate = parent->rate * ((nd + 1) + (fracr / 2^22))
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define AUDIO_PLL_DIV_FRAC	BIT(22)
+#define AUDIO_PLL_ND_MAX	(AT91_PMC_AUDIO_PLL_ND_MASK >> \
+					AT91_PMC_AUDIO_PLL_ND_OFFSET)
+
+#define AUDIO_PLL_FOUT_MIN	620000000
+#define AUDIO_PLL_FOUT_MAX	700000000
+
+struct clk_audio_frac {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	u32 fracr;
+	u8 nd;
+};
+
+#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw)
+
+static int clk_audio_pll_enable(struct clk_hw *hw)
+{
+	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
+
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_RESETN, 0);
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_RESETN,
+			   AT91_PMC_AUDIO_PLL_RESETN);
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL1,
+			   AT91_PMC_AUDIO_PLL_FRACR_MASK, fck->fracr);
+
+	/*
+	 * reset and enable have to be done in 2 separated writes
+	 * for AT91_PMC_AUDIO_PLL0
+	 */
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PLLEN |
+			   AT91_PMC_AUDIO_PLL_ND_MASK,
+			   AT91_PMC_AUDIO_PLL_PLLEN |
+			   AT91_PMC_AUDIO_PLL_ND(fck->nd));
+
+	return 0;
+}
+
+static void clk_audio_pll_disable(struct clk_hw *hw)
+{
+	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
+
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PLLEN, 0);
+	/* do it in 2 separated writes */
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_RESETN, 0);
+}
+
+static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
+					unsigned long nd, unsigned long fracr)
+{
+	unsigned long long fr = (unsigned long long)parent_rate *
+						(unsigned long long)fracr;
+
+	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
+
+	fr = DIV_ROUND_CLOSEST_ULL(fr, AUDIO_PLL_DIV_FRAC);
+
+	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
+
+	return parent_rate * (nd + 1) + fr;
+}
+
+static unsigned long clk_audio_pll_recalc_rate(struct clk_hw *hw,
+					       unsigned long parent_rate)
+{
+	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
+	unsigned long fout;
+
+	fout = clk_audio_pll_fout(parent_rate, fck->nd, fck->fracr);
+
+	pr_debug("A PLL: %s, fout = %lu (nd = %u, fracr = %lu)\n", __func__,
+		 fout, fck->nd, (unsigned long)fck->fracr);
+
+	return fout;
+}
+
+static int clk_audio_pll_compute_frac(unsigned long rate,
+				      unsigned long parent_rate,
+				      unsigned long *nd, unsigned long *fracr)
+{
+	unsigned long long tmp, rem;
+
+	if (!rate)
+		return -EINVAL;
+
+	tmp = rate;
+	rem = do_div(tmp, parent_rate);
+	if (!tmp || tmp >= AUDIO_PLL_ND_MAX)
+		return -EINVAL;
+
+	*nd = tmp - 1;
+
+	tmp = rem * AUDIO_PLL_DIV_FRAC;
+	tmp = DIV_ROUND_CLOSEST_ULL(tmp, parent_rate);
+	if (tmp > AT91_PMC_AUDIO_PLL_FRACR_MASK)
+		return -EINVAL;
+
+	/* we can cast here as we verified the bounds just above */
+	*fracr = (unsigned long)tmp;
+
+	return 0;
+}
+
+static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+				     unsigned long *parent_rate)
+{
+	long best_rate = -EINVAL;
+	unsigned long fracr, nd;
+	int ret;
+
+	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
+		 *parent_rate);
+
+	if (rate < AUDIO_PLL_FOUT_MIN)
+		rate = AUDIO_PLL_FOUT_MIN;
+	else if (rate > AUDIO_PLL_FOUT_MAX)
+		rate = AUDIO_PLL_FOUT_MAX;
+
+	ret = clk_audio_pll_compute_frac(rate, *parent_rate, &nd, &fracr);
+	if (ret)
+		return ret;
+
+	best_rate = clk_audio_pll_fout(*parent_rate, nd, fracr);
+
+	pr_debug("A PLL: %s, best_rate = %lu (nd = %lu, fracr = %lu)\n",
+		 __func__, best_rate, nd, fracr);
+
+	return best_rate;
+}
+
+static int clk_audio_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long parent_rate)
+{
+	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
+	unsigned long fracr, nd;
+	int ret;
+
+	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
+		 parent_rate);
+
+	if (rate < AUDIO_PLL_FOUT_MIN || rate > AUDIO_PLL_FOUT_MAX)
+		return -EINVAL;
+
+	ret = clk_audio_pll_compute_frac(rate, parent_rate, &nd, &fracr);
+	if (ret)
+		return ret;
+
+	fck->nd = nd;
+	fck->fracr = fracr;
+
+	return 0;
+}
+
+static const struct clk_ops audio_pll_ops = {
+	.enable = clk_audio_pll_enable,
+	.disable = clk_audio_pll_disable,
+	.recalc_rate = clk_audio_pll_recalc_rate,
+	.round_rate = clk_audio_pll_round_rate,
+	.set_rate = clk_audio_pll_set_rate,
+};
+
+static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
+{
+	struct clk_audio_frac *fck;
+	struct clk_init_data init;
+	struct regmap *regmap;
+	const char *parent_name;
+	const char *name = np->name;
+	int ret;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return;
+
+	fck = kzalloc(sizeof(*fck), GFP_KERNEL);
+	if (!fck)
+		return;
+
+	init.name = name;
+	init.ops = &audio_pll_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE;
+
+	fck->hw.init = &init;
+	fck->regmap = regmap;
+
+	ret = clk_hw_register(NULL, &fck->hw);
+	if (ret)
+		kfree(fck);
+	else
+		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fck->hw);
+}
+
+CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_setup,
+	       "atmel,sama5d2-clk-audio-pll-frac",
+	       of_sama5d2_clk_audio_pll_setup);
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
index 17f413bbbedf..6aca5ce8a99a 100644
--- a/include/linux/clk/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -185,4 +185,29 @@
 #define		AT91_PMC_PCR_EN			(0x1  <<  28)				/* Enable */
 #define		AT91_PMC_PCR_GCKEN		(0x1  <<  29)				/* GCK Enable */
 
+#define AT91_PMC_AUDIO_PLL0	0x14c
+#define		AT91_PMC_AUDIO_PLL_PLLEN	(1  <<  0)
+#define		AT91_PMC_AUDIO_PLL_PADEN	(1  <<  1)
+#define		AT91_PMC_AUDIO_PLL_PMCEN	(1  <<  2)
+#define		AT91_PMC_AUDIO_PLL_RESETN	(1  <<  3)
+#define		AT91_PMC_AUDIO_PLL_ND_OFFSET	8
+#define		AT91_PMC_AUDIO_PLL_ND_MASK	(0x7f << AT91_PMC_AUDIO_PLL_ND_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_ND(n)	((n)  << AT91_PMC_AUDIO_PLL_ND_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPMC_OFFSET	16
+#define		AT91_PMC_AUDIO_PLL_QDPMC_MASK	(0x7f << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPMC(n)	((n)  << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
+
+#define AT91_PMC_AUDIO_PLL1	0x150
+#define		AT91_PMC_AUDIO_PLL_FRACR_MASK		0x3fffff
+#define		AT91_PMC_AUDIO_PLL_QDPAD_OFFSET		24
+#define		AT91_PMC_AUDIO_PLL_QDPAD_MASK		(0x7f << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD(n)		((n)  << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET	AT91_PMC_AUDIO_PLL_QDPAD_OFFSET
+#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK	(0x3  << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV(n)		((n)  << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET	26
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX	0x1f
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK	(AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(n)	((n)  << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET)
+
 #endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: thomas.petazzoni, devicetree, alsa-devel, Nicolas Ferre,
	linux-kernel, Quentin Schulz, cyrille.pitchen, linux-clk,
	linux-arm-kernel

This new clock driver set allows to have a fractional divided clock that
would generate a precise clock particularly suitable for audio
applications.

The main audio pll clock has two children clocks: one that is connected
to the PMC, the other that can directly drive a pad. As these two routes
have different enable bits and different dividers and divider formulas,
they are handled by two different drivers. Each of them could modify the
rate of the main audio pll parent.

The main audio pll clock can output 620MHz to 700MHz.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---

v2:
  - split DT binding in a different patch,
  - removed unused AUDIO_PLL_*FOUT* defines from clk-audio-pll-pmc,
  - split classD modifications in a different patch,

 arch/arm/mach-at91/Kconfig           |   4 +
 drivers/clk/at91/Makefile            |   2 +
 drivers/clk/at91/clk-audio-pll-pad.c | 206 ++++++++++++++++++++++++++++++
 drivers/clk/at91/clk-audio-pll-pmc.c | 174 +++++++++++++++++++++++++
 drivers/clk/at91/clk-audio-pll.c     | 239 +++++++++++++++++++++++++++++++++++
 include/linux/clk/at91_pmc.h         |  25 ++++
 6 files changed, 650 insertions(+)
 create mode 100644 drivers/clk/at91/clk-audio-pll-pad.c
 create mode 100644 drivers/clk/at91/clk-audio-pll-pmc.c
 create mode 100644 drivers/clk/at91/clk-audio-pll.c

diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index d735e5fc4772..9ae14d59a9ce 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -26,6 +26,7 @@ config SOC_SAMA5D2
 	select HAVE_AT91_USB_CLK
 	select HAVE_AT91_H32MX
 	select HAVE_AT91_GENERATED_CLK
+	select HAVE_AT91_AUDIO_PLL
 	select PINCTRL_AT91PIO4
 	help
 	  Select this if ou are using one of Atmel's SAMA5D2 family SoC.
@@ -125,6 +126,9 @@ config HAVE_AT91_H32MX
 config HAVE_AT91_GENERATED_CLK
 	bool
 
+config HAVE_AT91_AUDIO_PLL
+	bool
+
 config SOC_SAM_V4_V5
 	bool
 
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 13e67bd35cff..c9353d17763a 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -6,6 +6,8 @@ obj-y += pmc.o sckc.o
 obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o
 obj-y += clk-system.o clk-peripheral.o clk-programmable.o
 
+obj-$(CONFIG_HAVE_AT91_AUDIO_PLL)	+= clk-audio-pll.o
+obj-$(CONFIG_HAVE_AT91_AUDIO_PLL)	+= clk-audio-pll-pmc.o clk-audio-pll-pad.o
 obj-$(CONFIG_HAVE_AT91_UTMI)		+= clk-utmi.o
 obj-$(CONFIG_HAVE_AT91_USB_CLK)		+= clk-usb.o
 obj-$(CONFIG_HAVE_AT91_SMD)		+= clk-smd.o
diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
new file mode 100644
index 000000000000..10dd6d625696
--- /dev/null
+++ b/drivers/clk/at91/clk-audio-pll-pad.c
@@ -0,0 +1,206 @@
+/*
+ *  Copyright (C) 2016 Atmel Corporation,
+ *                     Nicolas Ferre <nicolas.ferre@atmel.com>
+ *  Copyright (C) 2017 Free Electrons,
+ *		       Quentin Schulz <quentin.schulz@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "pmc.h"
+
+/*
+ * DOC: PAD output for fractional PLL clock for audio
+ *
+ * Traits of this clock:
+ * enable - clk_enable writes divisors and enables PAD output
+ * rate - rate is adjustable.
+ *        clk->rate = parent->rate / (qdaudio * div))
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define AUDIO_PLL_QDPAD(qd, div)	((AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(qd) & \
+					  AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK) | \
+					 (AT91_PMC_AUDIO_PLL_QDPAD_DIV(div) & \
+					  AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK))
+
+struct clk_audio_pad {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	u8 qdaudio;
+	u8 div;
+};
+
+#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw)
+
+static int clk_audio_pll_pad_enable(struct clk_hw *hw)
+{
+	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
+
+	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1,
+			   AT91_PMC_AUDIO_PLL_QDPAD_MASK,
+			   AUDIO_PLL_QDPAD(apad_ck->qdaudio, apad_ck->div));
+	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PADEN, AT91_PMC_AUDIO_PLL_PADEN);
+
+	return 0;
+}
+
+static void clk_audio_pll_pad_disable(struct clk_hw *hw)
+{
+	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
+
+	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PADEN, 0);
+}
+
+static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw,
+						   unsigned long parent_rate)
+{
+	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
+	unsigned long apad_rate = 0;
+
+	if (apad_ck->qdaudio && apad_ck->div)
+		apad_rate = parent_rate / (apad_ck->qdaudio * apad_ck->div);
+
+	pr_debug("A PLL/PAD: %s, apad_rate = %lu (div = %u, qdaudio = %u)\n",
+		 __func__, apad_rate, apad_ck->div, apad_ck->qdaudio);
+
+	return apad_rate;
+}
+
+static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
+					 unsigned long *parent_rate)
+{
+	struct clk_hw *pclk = clk_hw_get_parent(hw);
+	long best_rate = -EINVAL;
+	unsigned long best_parent_rate;
+	unsigned long tmp_qd;
+	u32 div;
+	long tmp_rate;
+	int tmp_diff;
+	int best_diff = -1;
+
+	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
+		 rate, *parent_rate);
+
+	/*
+	 * Rate divisor is actually made of two different divisors, multiplied
+	 * between themselves before dividing the rate.
+	 * tmp_qd goes from 1 to 31 and div is either 2 or 3.
+	 * In order to avoid testing twice the rate divisor (e.g. divisor 12 can
+	 * be found with (tmp_qd, div) = (2, 6) or (3, 4)), we remove any loop
+	 * for a rate divisor when div is 2 and tmp_qd is a multiple of 3.
+	 * We cannot inverse it (condition div is 3 and tmp_qd is even) or we
+	 * would miss some rate divisor that aren't reachable with div being 2
+	 * (e.g. rate divisor 90 is made with div = 3 and tmp_qd = 30, thus
+	 * tmp_qd is even so we skip it because we think div 2 could make this
+	 * rate divisor which isn't possible since tmp_qd has to be <= 31).
+	 */
+	for (tmp_qd = 1; tmp_qd < AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX; tmp_qd++)
+		for (div = 2; div <= 3; div++) {
+			if (div == 2 && tmp_qd % 3 == 0)
+				continue;
+
+			best_parent_rate = clk_hw_round_rate(pclk,
+							rate * tmp_qd * div);
+			tmp_rate = best_parent_rate / (div * tmp_qd);
+			tmp_diff = abs(rate - tmp_rate);
+
+			if (best_diff < 0 || best_diff > tmp_diff) {
+				*parent_rate = best_parent_rate;
+				best_rate = tmp_rate;
+				best_diff = tmp_diff;
+			}
+		}
+
+	pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n",
+		 __func__, best_rate, best_parent_rate);
+
+	return best_rate;
+}
+
+static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long parent_rate)
+{
+	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
+	u8 tmp_div;
+
+	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
+		 rate, parent_rate);
+
+	if (!rate)
+		return -EINVAL;
+
+	tmp_div = parent_rate / rate;
+	if (tmp_div % 3 == 0) {
+		apad_ck->qdaudio = tmp_div / 3;
+		apad_ck->div = 3;
+	} else {
+		apad_ck->qdaudio = tmp_div / 2;
+		apad_ck->div = 2;
+	}
+
+	return 0;
+}
+
+static const struct clk_ops audio_pll_pad_ops = {
+	.enable = clk_audio_pll_pad_enable,
+	.disable = clk_audio_pll_pad_disable,
+	.recalc_rate = clk_audio_pll_pad_recalc_rate,
+	.round_rate = clk_audio_pll_pad_round_rate,
+	.set_rate = clk_audio_pll_pad_set_rate,
+};
+
+static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
+{
+	struct clk_audio_pad *apad_ck;
+	struct clk_init_data init;
+	struct regmap *regmap;
+	const char *parent_name;
+	const char *name = np->name;
+	int ret;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return;
+
+	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
+	if (!apad_ck)
+		return;
+
+	init.name = name;
+	init.ops = &audio_pll_pad_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+		CLK_SET_RATE_PARENT;
+
+	apad_ck->hw.init = &init;
+	apad_ck->regmap = regmap;
+
+	ret = clk_hw_register(NULL, &apad_ck->hw);
+	if (ret)
+		kfree(apad_ck);
+	else
+		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
+}
+
+CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
+	       "atmel,sama5d2-clk-audio-pll-pad",
+	       of_sama5d2_clk_audio_pll_pad_setup);
diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c
new file mode 100644
index 000000000000..7b0847ed7a4b
--- /dev/null
+++ b/drivers/clk/at91/clk-audio-pll-pmc.c
@@ -0,0 +1,174 @@
+/*
+ *  Copyright (C) 2016 Atmel Corporation,
+ *                     Nicolas Ferre <nicolas.ferre@atmel.com>
+ *  Copyright (C) 2017 Free Electrons,
+ *		       Quentin Schulz <quentin.schulz@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "pmc.h"
+
+/*
+ * DOC: PMC output for fractional PLL clock for audio
+ *
+ * Traits of this clock:
+ * enable - clk_enable writes qdpmc, and enables PMC output
+ * rate - rate is adjustable.
+ *        clk->rate = parent->rate / (qdpmc + 1)
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define AUDIO_PLL_QDPMC_MAX		(AT91_PMC_AUDIO_PLL_QDPMC_MASK >> \
+						AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
+struct clk_audio_pmc {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	u8 qdpmc;
+};
+
+#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw)
+
+static int clk_audio_pll_pmc_enable(struct clk_hw *hw)
+{
+	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
+
+	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PMCEN |
+			   AT91_PMC_AUDIO_PLL_QDPMC_MASK,
+			   AT91_PMC_AUDIO_PLL_PMCEN |
+			   AT91_PMC_AUDIO_PLL_QDPMC(apmc_ck->qdpmc));
+	return 0;
+}
+
+static void clk_audio_pll_pmc_disable(struct clk_hw *hw)
+{
+	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
+
+	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PMCEN, 0);
+}
+
+static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw,
+						   unsigned long parent_rate)
+{
+	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
+	unsigned long apmc_rate = 0;
+
+	apmc_rate = parent_rate / (apmc_ck->qdpmc + 1);
+
+	pr_debug("A PLL/PMC: %s, apmc_rate = %lu (qdpmc = %u)\n", __func__,
+		 apmc_rate, apmc_ck->qdpmc);
+
+	return apmc_rate;
+}
+
+static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
+					 unsigned long *parent_rate)
+{
+	struct clk_hw *pclk = clk_hw_get_parent(hw);
+	long best_rate = -EINVAL;
+	unsigned long best_parent_rate = 0;
+	u32 tmp_qd = 0, div;
+	long tmp_rate;
+	int tmp_diff;
+	int best_diff = -1;
+
+	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
+		 rate, *parent_rate);
+
+	for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) {
+		best_parent_rate = clk_round_rate(pclk->clk, rate * div);
+		tmp_rate = best_parent_rate / div;
+		tmp_diff = abs(rate - tmp_rate);
+
+		if (best_diff < 0 || best_diff > tmp_diff) {
+			*parent_rate = best_parent_rate;
+			best_rate = tmp_rate;
+			best_diff = tmp_diff;
+			tmp_qd = div;
+		}
+	}
+
+	pr_debug("A PLL/PMC: %s, best_rate = %ld, best_parent_rate = %lu (qd = %d)\n",
+		 __func__, best_rate, *parent_rate, tmp_qd - 1);
+
+	return best_rate;
+}
+
+static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long parent_rate)
+{
+	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
+
+	if (!rate)
+		return -EINVAL;
+
+	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
+		 rate, parent_rate);
+
+	apmc_ck->qdpmc = parent_rate / rate - 1;
+
+	return 0;
+}
+
+static const struct clk_ops audio_pll_pmc_ops = {
+	.enable = clk_audio_pll_pmc_enable,
+	.disable = clk_audio_pll_pmc_disable,
+	.recalc_rate = clk_audio_pll_pmc_recalc_rate,
+	.round_rate = clk_audio_pll_pmc_round_rate,
+	.set_rate = clk_audio_pll_pmc_set_rate,
+};
+
+static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
+{
+	struct clk_audio_pmc *apmc_ck;
+	struct clk_init_data init;
+	struct regmap *regmap;
+	const char *parent_name;
+	const char *name = np->name;
+	int ret;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return;
+
+	apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
+	if (!apmc_ck)
+		return;
+
+	init.name = name;
+	init.ops = &audio_pll_pmc_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+		CLK_SET_RATE_PARENT;
+
+	apmc_ck->hw.init = &init;
+	apmc_ck->regmap = regmap;
+
+	ret = clk_hw_register(NULL, &apmc_ck->hw);
+	if (ret)
+		kfree(apmc_ck);
+	else
+		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw);
+}
+
+CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
+	       "atmel,sama5d2-clk-audio-pll-pmc",
+	       of_sama5d2_clk_audio_pll_pmc_setup);
diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
new file mode 100644
index 000000000000..efc2cb58da85
--- /dev/null
+++ b/drivers/clk/at91/clk-audio-pll.c
@@ -0,0 +1,239 @@
+/*
+ *  Copyright (C) 2016 Atmel Corporation,
+ *		       Songjun Wu <songjun.wu@atmel.com>,
+ *                     Nicolas Ferre <nicolas.ferre@atmel.com>
+ *  Copyright (C) 2017 Free Electrons,
+ *		       Quentin Schulz <quentin.schulz@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "pmc.h"
+
+/*
+ * DOC: Fractional PLL clock for audio
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare puts audio PLL in reset state
+ * enable - clk_enable writes nd, fracr parameters and enables PLL
+ * rate - rate is adjustable.
+ *        clk->rate = parent->rate * ((nd + 1) + (fracr / 2^22))
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define AUDIO_PLL_DIV_FRAC	BIT(22)
+#define AUDIO_PLL_ND_MAX	(AT91_PMC_AUDIO_PLL_ND_MASK >> \
+					AT91_PMC_AUDIO_PLL_ND_OFFSET)
+
+#define AUDIO_PLL_FOUT_MIN	620000000
+#define AUDIO_PLL_FOUT_MAX	700000000
+
+struct clk_audio_frac {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	u32 fracr;
+	u8 nd;
+};
+
+#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw)
+
+static int clk_audio_pll_enable(struct clk_hw *hw)
+{
+	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
+
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_RESETN, 0);
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_RESETN,
+			   AT91_PMC_AUDIO_PLL_RESETN);
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL1,
+			   AT91_PMC_AUDIO_PLL_FRACR_MASK, fck->fracr);
+
+	/*
+	 * reset and enable have to be done in 2 separated writes
+	 * for AT91_PMC_AUDIO_PLL0
+	 */
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PLLEN |
+			   AT91_PMC_AUDIO_PLL_ND_MASK,
+			   AT91_PMC_AUDIO_PLL_PLLEN |
+			   AT91_PMC_AUDIO_PLL_ND(fck->nd));
+
+	return 0;
+}
+
+static void clk_audio_pll_disable(struct clk_hw *hw)
+{
+	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
+
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PLLEN, 0);
+	/* do it in 2 separated writes */
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_RESETN, 0);
+}
+
+static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
+					unsigned long nd, unsigned long fracr)
+{
+	unsigned long long fr = (unsigned long long)parent_rate *
+						(unsigned long long)fracr;
+
+	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
+
+	fr = DIV_ROUND_CLOSEST_ULL(fr, AUDIO_PLL_DIV_FRAC);
+
+	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
+
+	return parent_rate * (nd + 1) + fr;
+}
+
+static unsigned long clk_audio_pll_recalc_rate(struct clk_hw *hw,
+					       unsigned long parent_rate)
+{
+	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
+	unsigned long fout;
+
+	fout = clk_audio_pll_fout(parent_rate, fck->nd, fck->fracr);
+
+	pr_debug("A PLL: %s, fout = %lu (nd = %u, fracr = %lu)\n", __func__,
+		 fout, fck->nd, (unsigned long)fck->fracr);
+
+	return fout;
+}
+
+static int clk_audio_pll_compute_frac(unsigned long rate,
+				      unsigned long parent_rate,
+				      unsigned long *nd, unsigned long *fracr)
+{
+	unsigned long long tmp, rem;
+
+	if (!rate)
+		return -EINVAL;
+
+	tmp = rate;
+	rem = do_div(tmp, parent_rate);
+	if (!tmp || tmp >= AUDIO_PLL_ND_MAX)
+		return -EINVAL;
+
+	*nd = tmp - 1;
+
+	tmp = rem * AUDIO_PLL_DIV_FRAC;
+	tmp = DIV_ROUND_CLOSEST_ULL(tmp, parent_rate);
+	if (tmp > AT91_PMC_AUDIO_PLL_FRACR_MASK)
+		return -EINVAL;
+
+	/* we can cast here as we verified the bounds just above */
+	*fracr = (unsigned long)tmp;
+
+	return 0;
+}
+
+static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+				     unsigned long *parent_rate)
+{
+	long best_rate = -EINVAL;
+	unsigned long fracr, nd;
+	int ret;
+
+	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
+		 *parent_rate);
+
+	if (rate < AUDIO_PLL_FOUT_MIN)
+		rate = AUDIO_PLL_FOUT_MIN;
+	else if (rate > AUDIO_PLL_FOUT_MAX)
+		rate = AUDIO_PLL_FOUT_MAX;
+
+	ret = clk_audio_pll_compute_frac(rate, *parent_rate, &nd, &fracr);
+	if (ret)
+		return ret;
+
+	best_rate = clk_audio_pll_fout(*parent_rate, nd, fracr);
+
+	pr_debug("A PLL: %s, best_rate = %lu (nd = %lu, fracr = %lu)\n",
+		 __func__, best_rate, nd, fracr);
+
+	return best_rate;
+}
+
+static int clk_audio_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long parent_rate)
+{
+	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
+	unsigned long fracr, nd;
+	int ret;
+
+	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
+		 parent_rate);
+
+	if (rate < AUDIO_PLL_FOUT_MIN || rate > AUDIO_PLL_FOUT_MAX)
+		return -EINVAL;
+
+	ret = clk_audio_pll_compute_frac(rate, parent_rate, &nd, &fracr);
+	if (ret)
+		return ret;
+
+	fck->nd = nd;
+	fck->fracr = fracr;
+
+	return 0;
+}
+
+static const struct clk_ops audio_pll_ops = {
+	.enable = clk_audio_pll_enable,
+	.disable = clk_audio_pll_disable,
+	.recalc_rate = clk_audio_pll_recalc_rate,
+	.round_rate = clk_audio_pll_round_rate,
+	.set_rate = clk_audio_pll_set_rate,
+};
+
+static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
+{
+	struct clk_audio_frac *fck;
+	struct clk_init_data init;
+	struct regmap *regmap;
+	const char *parent_name;
+	const char *name = np->name;
+	int ret;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return;
+
+	fck = kzalloc(sizeof(*fck), GFP_KERNEL);
+	if (!fck)
+		return;
+
+	init.name = name;
+	init.ops = &audio_pll_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE;
+
+	fck->hw.init = &init;
+	fck->regmap = regmap;
+
+	ret = clk_hw_register(NULL, &fck->hw);
+	if (ret)
+		kfree(fck);
+	else
+		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fck->hw);
+}
+
+CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_setup,
+	       "atmel,sama5d2-clk-audio-pll-frac",
+	       of_sama5d2_clk_audio_pll_setup);
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
index 17f413bbbedf..6aca5ce8a99a 100644
--- a/include/linux/clk/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -185,4 +185,29 @@
 #define		AT91_PMC_PCR_EN			(0x1  <<  28)				/* Enable */
 #define		AT91_PMC_PCR_GCKEN		(0x1  <<  29)				/* GCK Enable */
 
+#define AT91_PMC_AUDIO_PLL0	0x14c
+#define		AT91_PMC_AUDIO_PLL_PLLEN	(1  <<  0)
+#define		AT91_PMC_AUDIO_PLL_PADEN	(1  <<  1)
+#define		AT91_PMC_AUDIO_PLL_PMCEN	(1  <<  2)
+#define		AT91_PMC_AUDIO_PLL_RESETN	(1  <<  3)
+#define		AT91_PMC_AUDIO_PLL_ND_OFFSET	8
+#define		AT91_PMC_AUDIO_PLL_ND_MASK	(0x7f << AT91_PMC_AUDIO_PLL_ND_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_ND(n)	((n)  << AT91_PMC_AUDIO_PLL_ND_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPMC_OFFSET	16
+#define		AT91_PMC_AUDIO_PLL_QDPMC_MASK	(0x7f << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPMC(n)	((n)  << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
+
+#define AT91_PMC_AUDIO_PLL1	0x150
+#define		AT91_PMC_AUDIO_PLL_FRACR_MASK		0x3fffff
+#define		AT91_PMC_AUDIO_PLL_QDPAD_OFFSET		24
+#define		AT91_PMC_AUDIO_PLL_QDPAD_MASK		(0x7f << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD(n)		((n)  << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET	AT91_PMC_AUDIO_PLL_QDPAD_OFFSET
+#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK	(0x3  << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV(n)		((n)  << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET	26
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX	0x1f
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK	(AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(n)	((n)  << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET)
+
 #endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: linux-arm-kernel

This new clock driver set allows to have a fractional divided clock that
would generate a precise clock particularly suitable for audio
applications.

The main audio pll clock has two children clocks: one that is connected
to the PMC, the other that can directly drive a pad. As these two routes
have different enable bits and different dividers and divider formulas,
they are handled by two different drivers. Each of them could modify the
rate of the main audio pll parent.

The main audio pll clock can output 620MHz to 700MHz.

Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---

v2:
  - split DT binding in a different patch,
  - removed unused AUDIO_PLL_*FOUT* defines from clk-audio-pll-pmc,
  - split classD modifications in a different patch,

 arch/arm/mach-at91/Kconfig           |   4 +
 drivers/clk/at91/Makefile            |   2 +
 drivers/clk/at91/clk-audio-pll-pad.c | 206 ++++++++++++++++++++++++++++++
 drivers/clk/at91/clk-audio-pll-pmc.c | 174 +++++++++++++++++++++++++
 drivers/clk/at91/clk-audio-pll.c     | 239 +++++++++++++++++++++++++++++++++++
 include/linux/clk/at91_pmc.h         |  25 ++++
 6 files changed, 650 insertions(+)
 create mode 100644 drivers/clk/at91/clk-audio-pll-pad.c
 create mode 100644 drivers/clk/at91/clk-audio-pll-pmc.c
 create mode 100644 drivers/clk/at91/clk-audio-pll.c

diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index d735e5fc4772..9ae14d59a9ce 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -26,6 +26,7 @@ config SOC_SAMA5D2
 	select HAVE_AT91_USB_CLK
 	select HAVE_AT91_H32MX
 	select HAVE_AT91_GENERATED_CLK
+	select HAVE_AT91_AUDIO_PLL
 	select PINCTRL_AT91PIO4
 	help
 	  Select this if ou are using one of Atmel's SAMA5D2 family SoC.
@@ -125,6 +126,9 @@ config HAVE_AT91_H32MX
 config HAVE_AT91_GENERATED_CLK
 	bool
 
+config HAVE_AT91_AUDIO_PLL
+	bool
+
 config SOC_SAM_V4_V5
 	bool
 
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 13e67bd35cff..c9353d17763a 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -6,6 +6,8 @@ obj-y += pmc.o sckc.o
 obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o
 obj-y += clk-system.o clk-peripheral.o clk-programmable.o
 
+obj-$(CONFIG_HAVE_AT91_AUDIO_PLL)	+= clk-audio-pll.o
+obj-$(CONFIG_HAVE_AT91_AUDIO_PLL)	+= clk-audio-pll-pmc.o clk-audio-pll-pad.o
 obj-$(CONFIG_HAVE_AT91_UTMI)		+= clk-utmi.o
 obj-$(CONFIG_HAVE_AT91_USB_CLK)		+= clk-usb.o
 obj-$(CONFIG_HAVE_AT91_SMD)		+= clk-smd.o
diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
new file mode 100644
index 000000000000..10dd6d625696
--- /dev/null
+++ b/drivers/clk/at91/clk-audio-pll-pad.c
@@ -0,0 +1,206 @@
+/*
+ *  Copyright (C) 2016 Atmel Corporation,
+ *                     Nicolas Ferre <nicolas.ferre@atmel.com>
+ *  Copyright (C) 2017 Free Electrons,
+ *		       Quentin Schulz <quentin.schulz@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "pmc.h"
+
+/*
+ * DOC: PAD output for fractional PLL clock for audio
+ *
+ * Traits of this clock:
+ * enable - clk_enable writes divisors and enables PAD output
+ * rate - rate is adjustable.
+ *        clk->rate = parent->rate / (qdaudio * div))
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define AUDIO_PLL_QDPAD(qd, div)	((AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(qd) & \
+					  AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK) | \
+					 (AT91_PMC_AUDIO_PLL_QDPAD_DIV(div) & \
+					  AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK))
+
+struct clk_audio_pad {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	u8 qdaudio;
+	u8 div;
+};
+
+#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw)
+
+static int clk_audio_pll_pad_enable(struct clk_hw *hw)
+{
+	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
+
+	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1,
+			   AT91_PMC_AUDIO_PLL_QDPAD_MASK,
+			   AUDIO_PLL_QDPAD(apad_ck->qdaudio, apad_ck->div));
+	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PADEN, AT91_PMC_AUDIO_PLL_PADEN);
+
+	return 0;
+}
+
+static void clk_audio_pll_pad_disable(struct clk_hw *hw)
+{
+	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
+
+	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PADEN, 0);
+}
+
+static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw,
+						   unsigned long parent_rate)
+{
+	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
+	unsigned long apad_rate = 0;
+
+	if (apad_ck->qdaudio && apad_ck->div)
+		apad_rate = parent_rate / (apad_ck->qdaudio * apad_ck->div);
+
+	pr_debug("A PLL/PAD: %s, apad_rate = %lu (div = %u, qdaudio = %u)\n",
+		 __func__, apad_rate, apad_ck->div, apad_ck->qdaudio);
+
+	return apad_rate;
+}
+
+static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
+					 unsigned long *parent_rate)
+{
+	struct clk_hw *pclk = clk_hw_get_parent(hw);
+	long best_rate = -EINVAL;
+	unsigned long best_parent_rate;
+	unsigned long tmp_qd;
+	u32 div;
+	long tmp_rate;
+	int tmp_diff;
+	int best_diff = -1;
+
+	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
+		 rate, *parent_rate);
+
+	/*
+	 * Rate divisor is actually made of two different divisors, multiplied
+	 * between themselves before dividing the rate.
+	 * tmp_qd goes from 1 to 31 and div is either 2 or 3.
+	 * In order to avoid testing twice the rate divisor (e.g. divisor 12 can
+	 * be found with (tmp_qd, div) = (2, 6) or (3, 4)), we remove any loop
+	 * for a rate divisor when div is 2 and tmp_qd is a multiple of 3.
+	 * We cannot inverse it (condition div is 3 and tmp_qd is even) or we
+	 * would miss some rate divisor that aren't reachable with div being 2
+	 * (e.g. rate divisor 90 is made with div = 3 and tmp_qd = 30, thus
+	 * tmp_qd is even so we skip it because we think div 2 could make this
+	 * rate divisor which isn't possible since tmp_qd has to be <= 31).
+	 */
+	for (tmp_qd = 1; tmp_qd < AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX; tmp_qd++)
+		for (div = 2; div <= 3; div++) {
+			if (div == 2 && tmp_qd % 3 == 0)
+				continue;
+
+			best_parent_rate = clk_hw_round_rate(pclk,
+							rate * tmp_qd * div);
+			tmp_rate = best_parent_rate / (div * tmp_qd);
+			tmp_diff = abs(rate - tmp_rate);
+
+			if (best_diff < 0 || best_diff > tmp_diff) {
+				*parent_rate = best_parent_rate;
+				best_rate = tmp_rate;
+				best_diff = tmp_diff;
+			}
+		}
+
+	pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n",
+		 __func__, best_rate, best_parent_rate);
+
+	return best_rate;
+}
+
+static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long parent_rate)
+{
+	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
+	u8 tmp_div;
+
+	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
+		 rate, parent_rate);
+
+	if (!rate)
+		return -EINVAL;
+
+	tmp_div = parent_rate / rate;
+	if (tmp_div % 3 == 0) {
+		apad_ck->qdaudio = tmp_div / 3;
+		apad_ck->div = 3;
+	} else {
+		apad_ck->qdaudio = tmp_div / 2;
+		apad_ck->div = 2;
+	}
+
+	return 0;
+}
+
+static const struct clk_ops audio_pll_pad_ops = {
+	.enable = clk_audio_pll_pad_enable,
+	.disable = clk_audio_pll_pad_disable,
+	.recalc_rate = clk_audio_pll_pad_recalc_rate,
+	.round_rate = clk_audio_pll_pad_round_rate,
+	.set_rate = clk_audio_pll_pad_set_rate,
+};
+
+static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
+{
+	struct clk_audio_pad *apad_ck;
+	struct clk_init_data init;
+	struct regmap *regmap;
+	const char *parent_name;
+	const char *name = np->name;
+	int ret;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return;
+
+	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
+	if (!apad_ck)
+		return;
+
+	init.name = name;
+	init.ops = &audio_pll_pad_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+		CLK_SET_RATE_PARENT;
+
+	apad_ck->hw.init = &init;
+	apad_ck->regmap = regmap;
+
+	ret = clk_hw_register(NULL, &apad_ck->hw);
+	if (ret)
+		kfree(apad_ck);
+	else
+		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
+}
+
+CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
+	       "atmel,sama5d2-clk-audio-pll-pad",
+	       of_sama5d2_clk_audio_pll_pad_setup);
diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c
new file mode 100644
index 000000000000..7b0847ed7a4b
--- /dev/null
+++ b/drivers/clk/at91/clk-audio-pll-pmc.c
@@ -0,0 +1,174 @@
+/*
+ *  Copyright (C) 2016 Atmel Corporation,
+ *                     Nicolas Ferre <nicolas.ferre@atmel.com>
+ *  Copyright (C) 2017 Free Electrons,
+ *		       Quentin Schulz <quentin.schulz@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "pmc.h"
+
+/*
+ * DOC: PMC output for fractional PLL clock for audio
+ *
+ * Traits of this clock:
+ * enable - clk_enable writes qdpmc, and enables PMC output
+ * rate - rate is adjustable.
+ *        clk->rate = parent->rate / (qdpmc + 1)
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define AUDIO_PLL_QDPMC_MAX		(AT91_PMC_AUDIO_PLL_QDPMC_MASK >> \
+						AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
+struct clk_audio_pmc {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	u8 qdpmc;
+};
+
+#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw)
+
+static int clk_audio_pll_pmc_enable(struct clk_hw *hw)
+{
+	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
+
+	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PMCEN |
+			   AT91_PMC_AUDIO_PLL_QDPMC_MASK,
+			   AT91_PMC_AUDIO_PLL_PMCEN |
+			   AT91_PMC_AUDIO_PLL_QDPMC(apmc_ck->qdpmc));
+	return 0;
+}
+
+static void clk_audio_pll_pmc_disable(struct clk_hw *hw)
+{
+	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
+
+	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PMCEN, 0);
+}
+
+static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw,
+						   unsigned long parent_rate)
+{
+	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
+	unsigned long apmc_rate = 0;
+
+	apmc_rate = parent_rate / (apmc_ck->qdpmc + 1);
+
+	pr_debug("A PLL/PMC: %s, apmc_rate = %lu (qdpmc = %u)\n", __func__,
+		 apmc_rate, apmc_ck->qdpmc);
+
+	return apmc_rate;
+}
+
+static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
+					 unsigned long *parent_rate)
+{
+	struct clk_hw *pclk = clk_hw_get_parent(hw);
+	long best_rate = -EINVAL;
+	unsigned long best_parent_rate = 0;
+	u32 tmp_qd = 0, div;
+	long tmp_rate;
+	int tmp_diff;
+	int best_diff = -1;
+
+	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
+		 rate, *parent_rate);
+
+	for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) {
+		best_parent_rate = clk_round_rate(pclk->clk, rate * div);
+		tmp_rate = best_parent_rate / div;
+		tmp_diff = abs(rate - tmp_rate);
+
+		if (best_diff < 0 || best_diff > tmp_diff) {
+			*parent_rate = best_parent_rate;
+			best_rate = tmp_rate;
+			best_diff = tmp_diff;
+			tmp_qd = div;
+		}
+	}
+
+	pr_debug("A PLL/PMC: %s, best_rate = %ld, best_parent_rate = %lu (qd = %d)\n",
+		 __func__, best_rate, *parent_rate, tmp_qd - 1);
+
+	return best_rate;
+}
+
+static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long parent_rate)
+{
+	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
+
+	if (!rate)
+		return -EINVAL;
+
+	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
+		 rate, parent_rate);
+
+	apmc_ck->qdpmc = parent_rate / rate - 1;
+
+	return 0;
+}
+
+static const struct clk_ops audio_pll_pmc_ops = {
+	.enable = clk_audio_pll_pmc_enable,
+	.disable = clk_audio_pll_pmc_disable,
+	.recalc_rate = clk_audio_pll_pmc_recalc_rate,
+	.round_rate = clk_audio_pll_pmc_round_rate,
+	.set_rate = clk_audio_pll_pmc_set_rate,
+};
+
+static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
+{
+	struct clk_audio_pmc *apmc_ck;
+	struct clk_init_data init;
+	struct regmap *regmap;
+	const char *parent_name;
+	const char *name = np->name;
+	int ret;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return;
+
+	apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
+	if (!apmc_ck)
+		return;
+
+	init.name = name;
+	init.ops = &audio_pll_pmc_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+		CLK_SET_RATE_PARENT;
+
+	apmc_ck->hw.init = &init;
+	apmc_ck->regmap = regmap;
+
+	ret = clk_hw_register(NULL, &apmc_ck->hw);
+	if (ret)
+		kfree(apmc_ck);
+	else
+		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw);
+}
+
+CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
+	       "atmel,sama5d2-clk-audio-pll-pmc",
+	       of_sama5d2_clk_audio_pll_pmc_setup);
diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
new file mode 100644
index 000000000000..efc2cb58da85
--- /dev/null
+++ b/drivers/clk/at91/clk-audio-pll.c
@@ -0,0 +1,239 @@
+/*
+ *  Copyright (C) 2016 Atmel Corporation,
+ *		       Songjun Wu <songjun.wu@atmel.com>,
+ *                     Nicolas Ferre <nicolas.ferre@atmel.com>
+ *  Copyright (C) 2017 Free Electrons,
+ *		       Quentin Schulz <quentin.schulz@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "pmc.h"
+
+/*
+ * DOC: Fractional PLL clock for audio
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare puts audio PLL in reset state
+ * enable - clk_enable writes nd, fracr parameters and enables PLL
+ * rate - rate is adjustable.
+ *        clk->rate = parent->rate * ((nd + 1) + (fracr / 2^22))
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define AUDIO_PLL_DIV_FRAC	BIT(22)
+#define AUDIO_PLL_ND_MAX	(AT91_PMC_AUDIO_PLL_ND_MASK >> \
+					AT91_PMC_AUDIO_PLL_ND_OFFSET)
+
+#define AUDIO_PLL_FOUT_MIN	620000000
+#define AUDIO_PLL_FOUT_MAX	700000000
+
+struct clk_audio_frac {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	u32 fracr;
+	u8 nd;
+};
+
+#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw)
+
+static int clk_audio_pll_enable(struct clk_hw *hw)
+{
+	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
+
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_RESETN, 0);
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_RESETN,
+			   AT91_PMC_AUDIO_PLL_RESETN);
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL1,
+			   AT91_PMC_AUDIO_PLL_FRACR_MASK, fck->fracr);
+
+	/*
+	 * reset and enable have to be done in 2 separated writes
+	 * for AT91_PMC_AUDIO_PLL0
+	 */
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PLLEN |
+			   AT91_PMC_AUDIO_PLL_ND_MASK,
+			   AT91_PMC_AUDIO_PLL_PLLEN |
+			   AT91_PMC_AUDIO_PLL_ND(fck->nd));
+
+	return 0;
+}
+
+static void clk_audio_pll_disable(struct clk_hw *hw)
+{
+	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
+
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_PLLEN, 0);
+	/* do it in 2 separated writes */
+	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
+			   AT91_PMC_AUDIO_PLL_RESETN, 0);
+}
+
+static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
+					unsigned long nd, unsigned long fracr)
+{
+	unsigned long long fr = (unsigned long long)parent_rate *
+						(unsigned long long)fracr;
+
+	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
+
+	fr = DIV_ROUND_CLOSEST_ULL(fr, AUDIO_PLL_DIV_FRAC);
+
+	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
+
+	return parent_rate * (nd + 1) + fr;
+}
+
+static unsigned long clk_audio_pll_recalc_rate(struct clk_hw *hw,
+					       unsigned long parent_rate)
+{
+	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
+	unsigned long fout;
+
+	fout = clk_audio_pll_fout(parent_rate, fck->nd, fck->fracr);
+
+	pr_debug("A PLL: %s, fout = %lu (nd = %u, fracr = %lu)\n", __func__,
+		 fout, fck->nd, (unsigned long)fck->fracr);
+
+	return fout;
+}
+
+static int clk_audio_pll_compute_frac(unsigned long rate,
+				      unsigned long parent_rate,
+				      unsigned long *nd, unsigned long *fracr)
+{
+	unsigned long long tmp, rem;
+
+	if (!rate)
+		return -EINVAL;
+
+	tmp = rate;
+	rem = do_div(tmp, parent_rate);
+	if (!tmp || tmp >= AUDIO_PLL_ND_MAX)
+		return -EINVAL;
+
+	*nd = tmp - 1;
+
+	tmp = rem * AUDIO_PLL_DIV_FRAC;
+	tmp = DIV_ROUND_CLOSEST_ULL(tmp, parent_rate);
+	if (tmp > AT91_PMC_AUDIO_PLL_FRACR_MASK)
+		return -EINVAL;
+
+	/* we can cast here as we verified the bounds just above */
+	*fracr = (unsigned long)tmp;
+
+	return 0;
+}
+
+static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+				     unsigned long *parent_rate)
+{
+	long best_rate = -EINVAL;
+	unsigned long fracr, nd;
+	int ret;
+
+	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
+		 *parent_rate);
+
+	if (rate < AUDIO_PLL_FOUT_MIN)
+		rate = AUDIO_PLL_FOUT_MIN;
+	else if (rate > AUDIO_PLL_FOUT_MAX)
+		rate = AUDIO_PLL_FOUT_MAX;
+
+	ret = clk_audio_pll_compute_frac(rate, *parent_rate, &nd, &fracr);
+	if (ret)
+		return ret;
+
+	best_rate = clk_audio_pll_fout(*parent_rate, nd, fracr);
+
+	pr_debug("A PLL: %s, best_rate = %lu (nd = %lu, fracr = %lu)\n",
+		 __func__, best_rate, nd, fracr);
+
+	return best_rate;
+}
+
+static int clk_audio_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long parent_rate)
+{
+	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
+	unsigned long fracr, nd;
+	int ret;
+
+	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
+		 parent_rate);
+
+	if (rate < AUDIO_PLL_FOUT_MIN || rate > AUDIO_PLL_FOUT_MAX)
+		return -EINVAL;
+
+	ret = clk_audio_pll_compute_frac(rate, parent_rate, &nd, &fracr);
+	if (ret)
+		return ret;
+
+	fck->nd = nd;
+	fck->fracr = fracr;
+
+	return 0;
+}
+
+static const struct clk_ops audio_pll_ops = {
+	.enable = clk_audio_pll_enable,
+	.disable = clk_audio_pll_disable,
+	.recalc_rate = clk_audio_pll_recalc_rate,
+	.round_rate = clk_audio_pll_round_rate,
+	.set_rate = clk_audio_pll_set_rate,
+};
+
+static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
+{
+	struct clk_audio_frac *fck;
+	struct clk_init_data init;
+	struct regmap *regmap;
+	const char *parent_name;
+	const char *name = np->name;
+	int ret;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return;
+
+	fck = kzalloc(sizeof(*fck), GFP_KERNEL);
+	if (!fck)
+		return;
+
+	init.name = name;
+	init.ops = &audio_pll_ops;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = 1;
+	init.flags = CLK_SET_RATE_GATE;
+
+	fck->hw.init = &init;
+	fck->regmap = regmap;
+
+	ret = clk_hw_register(NULL, &fck->hw);
+	if (ret)
+		kfree(fck);
+	else
+		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fck->hw);
+}
+
+CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_setup,
+	       "atmel,sama5d2-clk-audio-pll-frac",
+	       of_sama5d2_clk_audio_pll_setup);
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
index 17f413bbbedf..6aca5ce8a99a 100644
--- a/include/linux/clk/at91_pmc.h
+++ b/include/linux/clk/at91_pmc.h
@@ -185,4 +185,29 @@
 #define		AT91_PMC_PCR_EN			(0x1  <<  28)				/* Enable */
 #define		AT91_PMC_PCR_GCKEN		(0x1  <<  29)				/* GCK Enable */
 
+#define AT91_PMC_AUDIO_PLL0	0x14c
+#define		AT91_PMC_AUDIO_PLL_PLLEN	(1  <<  0)
+#define		AT91_PMC_AUDIO_PLL_PADEN	(1  <<  1)
+#define		AT91_PMC_AUDIO_PLL_PMCEN	(1  <<  2)
+#define		AT91_PMC_AUDIO_PLL_RESETN	(1  <<  3)
+#define		AT91_PMC_AUDIO_PLL_ND_OFFSET	8
+#define		AT91_PMC_AUDIO_PLL_ND_MASK	(0x7f << AT91_PMC_AUDIO_PLL_ND_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_ND(n)	((n)  << AT91_PMC_AUDIO_PLL_ND_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPMC_OFFSET	16
+#define		AT91_PMC_AUDIO_PLL_QDPMC_MASK	(0x7f << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPMC(n)	((n)  << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
+
+#define AT91_PMC_AUDIO_PLL1	0x150
+#define		AT91_PMC_AUDIO_PLL_FRACR_MASK		0x3fffff
+#define		AT91_PMC_AUDIO_PLL_QDPAD_OFFSET		24
+#define		AT91_PMC_AUDIO_PLL_QDPAD_MASK		(0x7f << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD(n)		((n)  << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET	AT91_PMC_AUDIO_PLL_QDPAD_OFFSET
+#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK	(0x3  << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV(n)		((n)  << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET	26
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX	0x1f
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK	(AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET)
+#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(n)	((n)  << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET)
+
 #endif
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 4/9] ARM: dts: at91: sama5d2: add classd nodes
  2017-07-13  7:49 ` Quentin Schulz
  (?)
@ 2017-07-13  7:49   ` Quentin Schulz
  -1 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: Cyrille Pitchen, cyrille.pitchen, thomas.petazzoni, linux-clk,
	devicetree, linux-kernel, alsa-devel, linux-arm-kernel,
	Nicolas Ferre, Quentin Schulz

From: Cyrille Pitchen <cyrille.pitchen@atmel.com>

This patch adds nodes for the classd device and its generated clock.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---
 arch/arm/boot/dts/sama5d2.dtsi | 39 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index cc06da394366..a564cd1ba327 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -494,6 +494,24 @@
 					clocks = <&plla>;
 				};
 
+				audio_pll_frac: audiopll_fracck {
+					compatible = "atmel,sama5d2-clk-audio-pll-frac";
+					#clock-cells = <0>;
+					clocks = <&main>;
+				};
+
+				audio_pll_pad: audiopll_padck {
+					compatible = "atmel,sama5d2-clk-audio-pll-pad";
+					#clock-cells = <0>;
+					clocks = <&audio_pll_frac>;
+				};
+
+				audio_pll_pmc: audiopll_pmcck {
+					compatible = "atmel,sama5d2-clk-audio-pll-pmc";
+					#clock-cells = <0>;
+					clocks = <&audio_pll_frac>;
+				};
+
 				utmi: utmick {
 					compatible = "atmel,at91sam9x5-clk-utmi";
 					#clock-cells = <0>;
@@ -895,7 +913,7 @@
 					#address-cells = <1>;
 					#size-cells = <0>;
 					interrupt-parent = <&pmc>;
-					clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>;
+					clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>, <&audio_pll_pmc>;
 
 					sdmmc0_gclk: sdmmc0_gclk {
 						#clock-cells = <0>;
@@ -951,6 +969,12 @@
 						reg = <57>;
 						atmel,clk-output-range = <0 80000000>;
 					};
+
+					classd_gclk: classd_gclk {
+						#clock-cells = <0>;
+						reg = <59>;
+						atmel,clk-output-range = <0 100000000>;
+					};
 				};
 			};
 
@@ -1406,6 +1430,19 @@
 				status = "okay";
 			};
 
+			classd: classd@fc048000 {
+				compatible = "atmel,sama5d2-classd";
+				reg = <0xfc048000 0x100>;
+				interrupts = <59 IRQ_TYPE_LEVEL_HIGH 7>;
+				dmas = <&dma0
+					(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+					 AT91_XDMAC_DT_PERID(47))>;
+				dma-names = "tx";
+				clocks = <&classd_clk>, <&classd_gclk>;
+				clock-names = "pclk", "gclk";
+				status = "disabled";
+			};
+
 			can1: can@fc050000 {
 				compatible = "bosch,m_can";
 				reg = <0xfc050000 0x4000>, <0x210000 0x4000>;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 4/9] ARM: dts: at91: sama5d2: add classd nodes
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: thomas.petazzoni, devicetree, alsa-devel, Nicolas Ferre,
	linux-kernel, Quentin Schulz, cyrille.pitchen, Cyrille Pitchen,
	linux-clk, linux-arm-kernel

From: Cyrille Pitchen <cyrille.pitchen@atmel.com>

This patch adds nodes for the classd device and its generated clock.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---
 arch/arm/boot/dts/sama5d2.dtsi | 39 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index cc06da394366..a564cd1ba327 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -494,6 +494,24 @@
 					clocks = <&plla>;
 				};
 
+				audio_pll_frac: audiopll_fracck {
+					compatible = "atmel,sama5d2-clk-audio-pll-frac";
+					#clock-cells = <0>;
+					clocks = <&main>;
+				};
+
+				audio_pll_pad: audiopll_padck {
+					compatible = "atmel,sama5d2-clk-audio-pll-pad";
+					#clock-cells = <0>;
+					clocks = <&audio_pll_frac>;
+				};
+
+				audio_pll_pmc: audiopll_pmcck {
+					compatible = "atmel,sama5d2-clk-audio-pll-pmc";
+					#clock-cells = <0>;
+					clocks = <&audio_pll_frac>;
+				};
+
 				utmi: utmick {
 					compatible = "atmel,at91sam9x5-clk-utmi";
 					#clock-cells = <0>;
@@ -895,7 +913,7 @@
 					#address-cells = <1>;
 					#size-cells = <0>;
 					interrupt-parent = <&pmc>;
-					clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>;
+					clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>, <&audio_pll_pmc>;
 
 					sdmmc0_gclk: sdmmc0_gclk {
 						#clock-cells = <0>;
@@ -951,6 +969,12 @@
 						reg = <57>;
 						atmel,clk-output-range = <0 80000000>;
 					};
+
+					classd_gclk: classd_gclk {
+						#clock-cells = <0>;
+						reg = <59>;
+						atmel,clk-output-range = <0 100000000>;
+					};
 				};
 			};
 
@@ -1406,6 +1430,19 @@
 				status = "okay";
 			};
 
+			classd: classd@fc048000 {
+				compatible = "atmel,sama5d2-classd";
+				reg = <0xfc048000 0x100>;
+				interrupts = <59 IRQ_TYPE_LEVEL_HIGH 7>;
+				dmas = <&dma0
+					(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+					 AT91_XDMAC_DT_PERID(47))>;
+				dma-names = "tx";
+				clocks = <&classd_clk>, <&classd_gclk>;
+				clock-names = "pclk", "gclk";
+				status = "disabled";
+			};
+
 			can1: can@fc050000 {
 				compatible = "bosch,m_can";
 				reg = <0xfc050000 0x4000>, <0x210000 0x4000>;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 4/9] ARM: dts: at91: sama5d2: add classd nodes
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: linux-arm-kernel

From: Cyrille Pitchen <cyrille.pitchen@atmel.com>

This patch adds nodes for the classd device and its generated clock.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---
 arch/arm/boot/dts/sama5d2.dtsi | 39 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 38 insertions(+), 1 deletion(-)

diff --git a/arch/arm/boot/dts/sama5d2.dtsi b/arch/arm/boot/dts/sama5d2.dtsi
index cc06da394366..a564cd1ba327 100644
--- a/arch/arm/boot/dts/sama5d2.dtsi
+++ b/arch/arm/boot/dts/sama5d2.dtsi
@@ -494,6 +494,24 @@
 					clocks = <&plla>;
 				};
 
+				audio_pll_frac: audiopll_fracck {
+					compatible = "atmel,sama5d2-clk-audio-pll-frac";
+					#clock-cells = <0>;
+					clocks = <&main>;
+				};
+
+				audio_pll_pad: audiopll_padck {
+					compatible = "atmel,sama5d2-clk-audio-pll-pad";
+					#clock-cells = <0>;
+					clocks = <&audio_pll_frac>;
+				};
+
+				audio_pll_pmc: audiopll_pmcck {
+					compatible = "atmel,sama5d2-clk-audio-pll-pmc";
+					#clock-cells = <0>;
+					clocks = <&audio_pll_frac>;
+				};
+
 				utmi: utmick {
 					compatible = "atmel,at91sam9x5-clk-utmi";
 					#clock-cells = <0>;
@@ -895,7 +913,7 @@
 					#address-cells = <1>;
 					#size-cells = <0>;
 					interrupt-parent = <&pmc>;
-					clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>;
+					clocks = <&clk32k>, <&main>, <&plladiv>, <&utmi>, <&mck>, <&audio_pll_pmc>;
 
 					sdmmc0_gclk: sdmmc0_gclk {
 						#clock-cells = <0>;
@@ -951,6 +969,12 @@
 						reg = <57>;
 						atmel,clk-output-range = <0 80000000>;
 					};
+
+					classd_gclk: classd_gclk {
+						#clock-cells = <0>;
+						reg = <59>;
+						atmel,clk-output-range = <0 100000000>;
+					};
 				};
 			};
 
@@ -1406,6 +1430,19 @@
 				status = "okay";
 			};
 
+			classd: classd at fc048000 {
+				compatible = "atmel,sama5d2-classd";
+				reg = <0xfc048000 0x100>;
+				interrupts = <59 IRQ_TYPE_LEVEL_HIGH 7>;
+				dmas = <&dma0
+					(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1) |
+					 AT91_XDMAC_DT_PERID(47))>;
+				dma-names = "tx";
+				clocks = <&classd_clk>, <&classd_gclk>;
+				clock-names = "pclk", "gclk";
+				status = "disabled";
+			};
+
 			can1: can at fc050000 {
 				compatible = "bosch,m_can";
 				reg = <0xfc050000 0x4000>, <0x210000 0x4000>;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 5/9] clk: at91: clk-generated: create function to find best_diff
  2017-07-13  7:49 ` Quentin Schulz
@ 2017-07-13  7:49   ` Quentin Schulz
  -1 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: Quentin Schulz, cyrille.pitchen, thomas.petazzoni, linux-clk,
	devicetree, linux-kernel, alsa-devel, linux-arm-kernel

The way to find the best_diff and do the appropriate process afterwards
can be re-used.

This patch prepares the driver for an upcoming patch that will allow
clk_generated to determine the rate of the audio_pll.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/clk/at91/clk-generated.c | 41 ++++++++++++++++++++++++++--------------
 1 file changed, 27 insertions(+), 14 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index ef4b4e03de04..7260e498e059 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -99,15 +99,36 @@ clk_generated_recalc_rate(struct clk_hw *hw,
 	return DIV_ROUND_CLOSEST(parent_rate, gck->gckdiv + 1);
 }
 
+static void clk_generated_best_diff(struct clk_rate_request *req,
+				    struct clk_hw *parent,
+				    unsigned long parent_rate, u32 div,
+				    int *best_diff, long *best_rate)
+{
+	unsigned long tmp_rate;
+	int tmp_diff;
+
+	if (!div)
+		tmp_rate = parent_rate;
+	else
+		tmp_rate = parent_rate / div;
+	tmp_diff = abs(req->rate - tmp_rate);
+
+	if (*best_diff < 0 || *best_diff > tmp_diff) {
+		*best_rate = tmp_rate;
+		*best_diff = tmp_diff;
+		req->best_parent_rate = parent_rate;
+		req->best_parent_hw = parent;
+	}
+}
+
 static int clk_generated_determine_rate(struct clk_hw *hw,
 					struct clk_rate_request *req)
 {
 	struct clk_generated *gck = to_clk_generated(hw);
 	struct clk_hw *parent = NULL;
 	long best_rate = -EINVAL;
-	unsigned long tmp_rate, min_rate;
+	unsigned long min_rate;
 	int best_diff = -1;
-	int tmp_diff;
 	int i;
 
 	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
@@ -125,18 +146,10 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
 			continue;
 
 		div = DIV_ROUND_CLOSEST(parent_rate, req->rate);
-		if (!div)
-			tmp_rate = parent_rate;
-		else
-			tmp_rate = parent_rate / div;
-		tmp_diff = abs(req->rate - tmp_rate);
-
-		if (best_diff < 0 || best_diff > tmp_diff) {
-			best_rate = tmp_rate;
-			best_diff = tmp_diff;
-			req->best_parent_rate = parent_rate;
-			req->best_parent_hw = parent;
-		}
+
+		clk_generated_best_diff(req, parent, parent_rate, div,
+					&best_diff, &best_rate);
+
 
 		if (!best_diff)
 			break;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 5/9] clk: at91: clk-generated: create function to find best_diff
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: linux-arm-kernel

The way to find the best_diff and do the appropriate process afterwards
can be re-used.

This patch prepares the driver for an upcoming patch that will allow
clk_generated to determine the rate of the audio_pll.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---
 drivers/clk/at91/clk-generated.c | 41 ++++++++++++++++++++++++++--------------
 1 file changed, 27 insertions(+), 14 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index ef4b4e03de04..7260e498e059 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -99,15 +99,36 @@ clk_generated_recalc_rate(struct clk_hw *hw,
 	return DIV_ROUND_CLOSEST(parent_rate, gck->gckdiv + 1);
 }
 
+static void clk_generated_best_diff(struct clk_rate_request *req,
+				    struct clk_hw *parent,
+				    unsigned long parent_rate, u32 div,
+				    int *best_diff, long *best_rate)
+{
+	unsigned long tmp_rate;
+	int tmp_diff;
+
+	if (!div)
+		tmp_rate = parent_rate;
+	else
+		tmp_rate = parent_rate / div;
+	tmp_diff = abs(req->rate - tmp_rate);
+
+	if (*best_diff < 0 || *best_diff > tmp_diff) {
+		*best_rate = tmp_rate;
+		*best_diff = tmp_diff;
+		req->best_parent_rate = parent_rate;
+		req->best_parent_hw = parent;
+	}
+}
+
 static int clk_generated_determine_rate(struct clk_hw *hw,
 					struct clk_rate_request *req)
 {
 	struct clk_generated *gck = to_clk_generated(hw);
 	struct clk_hw *parent = NULL;
 	long best_rate = -EINVAL;
-	unsigned long tmp_rate, min_rate;
+	unsigned long min_rate;
 	int best_diff = -1;
-	int tmp_diff;
 	int i;
 
 	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
@@ -125,18 +146,10 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
 			continue;
 
 		div = DIV_ROUND_CLOSEST(parent_rate, req->rate);
-		if (!div)
-			tmp_rate = parent_rate;
-		else
-			tmp_rate = parent_rate / div;
-		tmp_diff = abs(req->rate - tmp_rate);
-
-		if (best_diff < 0 || best_diff > tmp_diff) {
-			best_rate = tmp_rate;
-			best_diff = tmp_diff;
-			req->best_parent_rate = parent_rate;
-			req->best_parent_hw = parent;
-		}
+
+		clk_generated_best_diff(req, parent, parent_rate, div,
+					&best_diff, &best_rate);
+
 
 		if (!best_diff)
 			break;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 6/9] clk: at91: clk-generated: make gclk determine audio_pll rate
  2017-07-13  7:49 ` Quentin Schulz
  (?)
@ 2017-07-13  7:49   ` Quentin Schulz
  -1 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: Quentin Schulz, cyrille.pitchen, thomas.petazzoni, linux-clk,
	devicetree, linux-kernel, alsa-devel, linux-arm-kernel

This allows gclk to determine audio_pll rate and set the parent rate
accordingly.

However, there are multiple children clocks that could technically
change the rate of audio_pll (via gck). With the rate locking, the first
consumer to enable the clock will be the one definitely setting the rate
of the clock.

Since audio IPs are most likely to request the same rate, we enforce
that the only clks able to modify gck rate are those of audio IPs.

To remain consistent, we deny other clocks to be children of audio_pll.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---

v3:
  - added a flag per generated clock to know which one is allowed to
modify audio_pll rate,
  - set the flag in setup function depending on the compatible and the
clock ID,

v2:
  - added conditions for audio pll rate setting restriction for SSC and
I2S,

 drivers/clk/at91/clk-generated.c | 63 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 57 insertions(+), 6 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index 7260e498e059..33481368740e 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -26,6 +26,13 @@
 #define GENERATED_SOURCE_MAX	6
 #define GENERATED_MAX_DIV	255
 
+#define GCK_ID_SSC0		43
+#define GCK_ID_SSC1		44
+#define GCK_ID_I2S0		54
+#define GCK_ID_I2S1		55
+#define GCK_ID_CLASSD		59
+#define GCK_INDEX_DT_AUDIO_PLL	5
+
 struct clk_generated {
 	struct clk_hw hw;
 	struct regmap *regmap;
@@ -34,6 +41,7 @@ struct clk_generated {
 	u32 id;
 	u32 gckdiv;
 	u8 parent_id;
+	bool audio_pll_allowed;
 };
 
 #define to_clk_generated(hw) \
@@ -126,15 +134,14 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
 {
 	struct clk_generated *gck = to_clk_generated(hw);
 	struct clk_hw *parent = NULL;
+	struct clk_rate_request req_parent = *req;
 	long best_rate = -EINVAL;
-	unsigned long min_rate;
+	unsigned long min_rate, parent_rate;
 	int best_diff = -1;
 	int i;
+	u32 div;
 
-	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
-		u32 div;
-		unsigned long parent_rate;
-
+	for (i = 0; i < clk_hw_get_num_parents(hw) - 1; i++) {
 		parent = clk_hw_get_parent_by_index(hw, i);
 		if (!parent)
 			continue;
@@ -150,11 +157,38 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
 		clk_generated_best_diff(req, parent, parent_rate, div,
 					&best_diff, &best_rate);
 
+		if (!best_diff)
+			break;
+	}
+
+	/*
+	 * The audio_pll rate can be modified, unlike the five others clocks
+	 * that should never be altered.
+	 * The audio_pll can technically be used by multiple consumers. However,
+	 * with the rate locking, the first consumer to enable to clock will be
+	 * the one definitely setting the rate of the clock.
+	 * Since audio IPs are most likely to request the same rate, we enforce
+	 * that the only clks able to modify gck rate are those of audio IPs.
+	 */
+
+	if (!gck->audio_pll_allowed)
+		goto end;
+
+	parent = clk_hw_get_parent_by_index(hw, GCK_INDEX_DT_AUDIO_PLL);
+	if (!parent)
+		goto end;
+
+	for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
+		req_parent.rate = req->rate * div;
+		__clk_determine_rate(parent, &req_parent);
+		clk_generated_best_diff(req, parent, req_parent.rate, div,
+					&best_diff, &best_rate);
 
 		if (!best_diff)
 			break;
 	}
 
+end:
 	pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
 		 __func__, best_rate,
 		 __clk_get_name((req->best_parent_hw)->clk),
@@ -264,7 +298,8 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
 	init.ops = &generated_ops;
 	init.parent_names = parent_names;
 	init.num_parents = num_parents;
-	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+		CLK_SET_RATE_PARENT;
 
 	gck->id = id;
 	gck->hw.init = &init;
@@ -296,6 +331,7 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
 	struct device_node *gcknp;
 	struct clk_range range = CLK_RANGE(0, 0);
 	struct regmap *regmap;
+	struct clk_generated *gck;
 
 	num_parents = of_clk_get_parent_count(np);
 	if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX)
@@ -327,6 +363,21 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
 		hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name,
 						  parent_names, num_parents,
 						  id, &range);
+
+		gck = to_clk_generated(hw);
+
+		if (of_device_is_compatible(np,
+					    "atmel,sama5d2-clk-generated")) {
+			if (gck->id == GCK_ID_SSC0 || gck->id == GCK_ID_SSC1 ||
+			    gck->id == GCK_ID_I2S0 || gck->id == GCK_ID_I2S1 ||
+			    gck->id == GCK_ID_CLASSD)
+				gck->audio_pll_allowed = true;
+			else
+				gck->audio_pll_allowed = false;
+		} else {
+			gck->audio_pll_allowed = false;
+		}
+
 		if (IS_ERR(hw))
 			continue;
 
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 6/9] clk: at91: clk-generated: make gclk determine audio_pll rate
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: thomas.petazzoni, devicetree, alsa-devel, linux-kernel,
	Quentin Schulz, cyrille.pitchen, linux-clk, linux-arm-kernel

This allows gclk to determine audio_pll rate and set the parent rate
accordingly.

However, there are multiple children clocks that could technically
change the rate of audio_pll (via gck). With the rate locking, the first
consumer to enable the clock will be the one definitely setting the rate
of the clock.

Since audio IPs are most likely to request the same rate, we enforce
that the only clks able to modify gck rate are those of audio IPs.

To remain consistent, we deny other clocks to be children of audio_pll.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---

v3:
  - added a flag per generated clock to know which one is allowed to
modify audio_pll rate,
  - set the flag in setup function depending on the compatible and the
clock ID,

v2:
  - added conditions for audio pll rate setting restriction for SSC and
I2S,

 drivers/clk/at91/clk-generated.c | 63 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 57 insertions(+), 6 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index 7260e498e059..33481368740e 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -26,6 +26,13 @@
 #define GENERATED_SOURCE_MAX	6
 #define GENERATED_MAX_DIV	255
 
+#define GCK_ID_SSC0		43
+#define GCK_ID_SSC1		44
+#define GCK_ID_I2S0		54
+#define GCK_ID_I2S1		55
+#define GCK_ID_CLASSD		59
+#define GCK_INDEX_DT_AUDIO_PLL	5
+
 struct clk_generated {
 	struct clk_hw hw;
 	struct regmap *regmap;
@@ -34,6 +41,7 @@ struct clk_generated {
 	u32 id;
 	u32 gckdiv;
 	u8 parent_id;
+	bool audio_pll_allowed;
 };
 
 #define to_clk_generated(hw) \
@@ -126,15 +134,14 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
 {
 	struct clk_generated *gck = to_clk_generated(hw);
 	struct clk_hw *parent = NULL;
+	struct clk_rate_request req_parent = *req;
 	long best_rate = -EINVAL;
-	unsigned long min_rate;
+	unsigned long min_rate, parent_rate;
 	int best_diff = -1;
 	int i;
+	u32 div;
 
-	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
-		u32 div;
-		unsigned long parent_rate;
-
+	for (i = 0; i < clk_hw_get_num_parents(hw) - 1; i++) {
 		parent = clk_hw_get_parent_by_index(hw, i);
 		if (!parent)
 			continue;
@@ -150,11 +157,38 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
 		clk_generated_best_diff(req, parent, parent_rate, div,
 					&best_diff, &best_rate);
 
+		if (!best_diff)
+			break;
+	}
+
+	/*
+	 * The audio_pll rate can be modified, unlike the five others clocks
+	 * that should never be altered.
+	 * The audio_pll can technically be used by multiple consumers. However,
+	 * with the rate locking, the first consumer to enable to clock will be
+	 * the one definitely setting the rate of the clock.
+	 * Since audio IPs are most likely to request the same rate, we enforce
+	 * that the only clks able to modify gck rate are those of audio IPs.
+	 */
+
+	if (!gck->audio_pll_allowed)
+		goto end;
+
+	parent = clk_hw_get_parent_by_index(hw, GCK_INDEX_DT_AUDIO_PLL);
+	if (!parent)
+		goto end;
+
+	for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
+		req_parent.rate = req->rate * div;
+		__clk_determine_rate(parent, &req_parent);
+		clk_generated_best_diff(req, parent, req_parent.rate, div,
+					&best_diff, &best_rate);
 
 		if (!best_diff)
 			break;
 	}
 
+end:
 	pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
 		 __func__, best_rate,
 		 __clk_get_name((req->best_parent_hw)->clk),
@@ -264,7 +298,8 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
 	init.ops = &generated_ops;
 	init.parent_names = parent_names;
 	init.num_parents = num_parents;
-	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+		CLK_SET_RATE_PARENT;
 
 	gck->id = id;
 	gck->hw.init = &init;
@@ -296,6 +331,7 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
 	struct device_node *gcknp;
 	struct clk_range range = CLK_RANGE(0, 0);
 	struct regmap *regmap;
+	struct clk_generated *gck;
 
 	num_parents = of_clk_get_parent_count(np);
 	if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX)
@@ -327,6 +363,21 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
 		hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name,
 						  parent_names, num_parents,
 						  id, &range);
+
+		gck = to_clk_generated(hw);
+
+		if (of_device_is_compatible(np,
+					    "atmel,sama5d2-clk-generated")) {
+			if (gck->id == GCK_ID_SSC0 || gck->id == GCK_ID_SSC1 ||
+			    gck->id == GCK_ID_I2S0 || gck->id == GCK_ID_I2S1 ||
+			    gck->id == GCK_ID_CLASSD)
+				gck->audio_pll_allowed = true;
+			else
+				gck->audio_pll_allowed = false;
+		} else {
+			gck->audio_pll_allowed = false;
+		}
+
 		if (IS_ERR(hw))
 			continue;
 
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 6/9] clk: at91: clk-generated: make gclk determine audio_pll rate
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: linux-arm-kernel

This allows gclk to determine audio_pll rate and set the parent rate
accordingly.

However, there are multiple children clocks that could technically
change the rate of audio_pll (via gck). With the rate locking, the first
consumer to enable the clock will be the one definitely setting the rate
of the clock.

Since audio IPs are most likely to request the same rate, we enforce
that the only clks able to modify gck rate are those of audio IPs.

To remain consistent, we deny other clocks to be children of audio_pll.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>
---

v3:
  - added a flag per generated clock to know which one is allowed to
modify audio_pll rate,
  - set the flag in setup function depending on the compatible and the
clock ID,

v2:
  - added conditions for audio pll rate setting restriction for SSC and
I2S,

 drivers/clk/at91/clk-generated.c | 63 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 57 insertions(+), 6 deletions(-)

diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
index 7260e498e059..33481368740e 100644
--- a/drivers/clk/at91/clk-generated.c
+++ b/drivers/clk/at91/clk-generated.c
@@ -26,6 +26,13 @@
 #define GENERATED_SOURCE_MAX	6
 #define GENERATED_MAX_DIV	255
 
+#define GCK_ID_SSC0		43
+#define GCK_ID_SSC1		44
+#define GCK_ID_I2S0		54
+#define GCK_ID_I2S1		55
+#define GCK_ID_CLASSD		59
+#define GCK_INDEX_DT_AUDIO_PLL	5
+
 struct clk_generated {
 	struct clk_hw hw;
 	struct regmap *regmap;
@@ -34,6 +41,7 @@ struct clk_generated {
 	u32 id;
 	u32 gckdiv;
 	u8 parent_id;
+	bool audio_pll_allowed;
 };
 
 #define to_clk_generated(hw) \
@@ -126,15 +134,14 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
 {
 	struct clk_generated *gck = to_clk_generated(hw);
 	struct clk_hw *parent = NULL;
+	struct clk_rate_request req_parent = *req;
 	long best_rate = -EINVAL;
-	unsigned long min_rate;
+	unsigned long min_rate, parent_rate;
 	int best_diff = -1;
 	int i;
+	u32 div;
 
-	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
-		u32 div;
-		unsigned long parent_rate;
-
+	for (i = 0; i < clk_hw_get_num_parents(hw) - 1; i++) {
 		parent = clk_hw_get_parent_by_index(hw, i);
 		if (!parent)
 			continue;
@@ -150,11 +157,38 @@ static int clk_generated_determine_rate(struct clk_hw *hw,
 		clk_generated_best_diff(req, parent, parent_rate, div,
 					&best_diff, &best_rate);
 
+		if (!best_diff)
+			break;
+	}
+
+	/*
+	 * The audio_pll rate can be modified, unlike the five others clocks
+	 * that should never be altered.
+	 * The audio_pll can technically be used by multiple consumers. However,
+	 * with the rate locking, the first consumer to enable to clock will be
+	 * the one definitely setting the rate of the clock.
+	 * Since audio IPs are most likely to request the same rate, we enforce
+	 * that the only clks able to modify gck rate are those of audio IPs.
+	 */
+
+	if (!gck->audio_pll_allowed)
+		goto end;
+
+	parent = clk_hw_get_parent_by_index(hw, GCK_INDEX_DT_AUDIO_PLL);
+	if (!parent)
+		goto end;
+
+	for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
+		req_parent.rate = req->rate * div;
+		__clk_determine_rate(parent, &req_parent);
+		clk_generated_best_diff(req, parent, req_parent.rate, div,
+					&best_diff, &best_rate);
 
 		if (!best_diff)
 			break;
 	}
 
+end:
 	pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
 		 __func__, best_rate,
 		 __clk_get_name((req->best_parent_hw)->clk),
@@ -264,7 +298,8 @@ at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
 	init.ops = &generated_ops;
 	init.parent_names = parent_names;
 	init.num_parents = num_parents;
-	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
+		CLK_SET_RATE_PARENT;
 
 	gck->id = id;
 	gck->hw.init = &init;
@@ -296,6 +331,7 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
 	struct device_node *gcknp;
 	struct clk_range range = CLK_RANGE(0, 0);
 	struct regmap *regmap;
+	struct clk_generated *gck;
 
 	num_parents = of_clk_get_parent_count(np);
 	if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX)
@@ -327,6 +363,21 @@ static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
 		hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name,
 						  parent_names, num_parents,
 						  id, &range);
+
+		gck = to_clk_generated(hw);
+
+		if (of_device_is_compatible(np,
+					    "atmel,sama5d2-clk-generated")) {
+			if (gck->id == GCK_ID_SSC0 || gck->id == GCK_ID_SSC1 ||
+			    gck->id == GCK_ID_I2S0 || gck->id == GCK_ID_I2S1 ||
+			    gck->id == GCK_ID_CLASSD)
+				gck->audio_pll_allowed = true;
+			else
+				gck->audio_pll_allowed = false;
+		} else {
+			gck->audio_pll_allowed = false;
+		}
+
 		if (IS_ERR(hw))
 			continue;
 
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 7/9] ASoC: atmel-classd: remove aclk clock from DT binding
  2017-07-13  7:49 ` Quentin Schulz
  (?)
@ 2017-07-13  7:49   ` Quentin Schulz
  -1 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: Quentin Schulz, cyrille.pitchen, thomas.petazzoni, linux-clk,
	devicetree, linux-kernel, alsa-devel, linux-arm-kernel

Since gclk (generated-clk) is now able to determine the rate of the
audio_pll, there is no need for classd to have a direct phandle to the
audio_pll while already having a phandle to gclk.

This binding is used by no board in mainline so it is safe to be
modified.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Mark Brown <broonie@kernel.org>
Acked-by: Rob Herring <robh@kernel.org>
---

added in v2

 Documentation/devicetree/bindings/sound/atmel-classd.txt | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/atmel-classd.txt b/Documentation/devicetree/bindings/sound/atmel-classd.txt
index 549e701cb7a1..898551076382 100644
--- a/Documentation/devicetree/bindings/sound/atmel-classd.txt
+++ b/Documentation/devicetree/bindings/sound/atmel-classd.txt
@@ -13,13 +13,11 @@ Required properties:
 	Must be "tx".
 - clock-names
 	Tuple listing input clock names.
-	Required elements: "pclk", "gclk" and "aclk".
+	Required elements: "pclk" and "gclk".
 - clocks
 	Please refer to clock-bindings.txt.
 - assigned-clocks
 	Should be <&classd_gclk>.
-- assigned-clock-parents
-	Should be <&audio_pll_pmc>.
 
 Optional properties:
 - pinctrl-names, pinctrl-0
@@ -45,10 +43,9 @@ classd: classd@fc048000 {
 			(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1)
 			| AT91_XDMAC_DT_PERID(47))>;
 		dma-names = "tx";
-		clocks = <&classd_clk>, <&classd_gclk>, <&audio_pll_pmc>;
-		clock-names = "pclk", "gclk", "aclk";
+		clocks = <&classd_clk>, <&classd_gclk>;
+		clock-names = "pclk", "gclk";
 		assigned-clocks = <&classd_gclk>;
-		assigned-clock-parents = <&audio_pll_pmc>;
 
 		pinctrl-names = "default";
 		pinctrl-0 = <&pinctrl_classd_default>;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 7/9] ASoC: atmel-classd: remove aclk clock from DT binding
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: thomas.petazzoni, devicetree, alsa-devel, linux-kernel,
	Quentin Schulz, cyrille.pitchen, linux-clk, linux-arm-kernel

Since gclk (generated-clk) is now able to determine the rate of the
audio_pll, there is no need for classd to have a direct phandle to the
audio_pll while already having a phandle to gclk.

This binding is used by no board in mainline so it is safe to be
modified.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Mark Brown <broonie@kernel.org>
Acked-by: Rob Herring <robh@kernel.org>
---

added in v2

 Documentation/devicetree/bindings/sound/atmel-classd.txt | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/atmel-classd.txt b/Documentation/devicetree/bindings/sound/atmel-classd.txt
index 549e701cb7a1..898551076382 100644
--- a/Documentation/devicetree/bindings/sound/atmel-classd.txt
+++ b/Documentation/devicetree/bindings/sound/atmel-classd.txt
@@ -13,13 +13,11 @@ Required properties:
 	Must be "tx".
 - clock-names
 	Tuple listing input clock names.
-	Required elements: "pclk", "gclk" and "aclk".
+	Required elements: "pclk" and "gclk".
 - clocks
 	Please refer to clock-bindings.txt.
 - assigned-clocks
 	Should be <&classd_gclk>.
-- assigned-clock-parents
-	Should be <&audio_pll_pmc>.
 
 Optional properties:
 - pinctrl-names, pinctrl-0
@@ -45,10 +43,9 @@ classd: classd@fc048000 {
 			(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1)
 			| AT91_XDMAC_DT_PERID(47))>;
 		dma-names = "tx";
-		clocks = <&classd_clk>, <&classd_gclk>, <&audio_pll_pmc>;
-		clock-names = "pclk", "gclk", "aclk";
+		clocks = <&classd_clk>, <&classd_gclk>;
+		clock-names = "pclk", "gclk";
 		assigned-clocks = <&classd_gclk>;
-		assigned-clock-parents = <&audio_pll_pmc>;
 
 		pinctrl-names = "default";
 		pinctrl-0 = <&pinctrl_classd_default>;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 7/9] ASoC: atmel-classd: remove aclk clock from DT binding
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: linux-arm-kernel

Since gclk (generated-clk) is now able to determine the rate of the
audio_pll, there is no need for classd to have a direct phandle to the
audio_pll while already having a phandle to gclk.

This binding is used by no board in mainline so it is safe to be
modified.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Mark Brown <broonie@kernel.org>
Acked-by: Rob Herring <robh@kernel.org>
---

added in v2

 Documentation/devicetree/bindings/sound/atmel-classd.txt | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/atmel-classd.txt b/Documentation/devicetree/bindings/sound/atmel-classd.txt
index 549e701cb7a1..898551076382 100644
--- a/Documentation/devicetree/bindings/sound/atmel-classd.txt
+++ b/Documentation/devicetree/bindings/sound/atmel-classd.txt
@@ -13,13 +13,11 @@ Required properties:
 	Must be "tx".
 - clock-names
 	Tuple listing input clock names.
-	Required elements: "pclk", "gclk" and "aclk".
+	Required elements: "pclk" and "gclk".
 - clocks
 	Please refer to clock-bindings.txt.
 - assigned-clocks
 	Should be <&classd_gclk>.
-- assigned-clock-parents
-	Should be <&audio_pll_pmc>.
 
 Optional properties:
 - pinctrl-names, pinctrl-0
@@ -45,10 +43,9 @@ classd: classd at fc048000 {
 			(AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1)
 			| AT91_XDMAC_DT_PERID(47))>;
 		dma-names = "tx";
-		clocks = <&classd_clk>, <&classd_gclk>, <&audio_pll_pmc>;
-		clock-names = "pclk", "gclk", "aclk";
+		clocks = <&classd_clk>, <&classd_gclk>;
+		clock-names = "pclk", "gclk";
 		assigned-clocks = <&classd_gclk>;
-		assigned-clock-parents = <&audio_pll_pmc>;
 
 		pinctrl-names = "default";
 		pinctrl-0 = <&pinctrl_classd_default>;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 8/9] ASoC: atmel-classd: remove aclk clock
  2017-07-13  7:49 ` Quentin Schulz
@ 2017-07-13  7:49   ` Quentin Schulz
  -1 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: Quentin Schulz, cyrille.pitchen, thomas.petazzoni, linux-clk,
	devicetree, linux-kernel, alsa-devel, linux-arm-kernel

Since gclk (generated-clk) is now able to determine the rate of the
audio_pll, there is no need for classd to have a direct phandle to the
audio_pll while already having a phandle to gclk.

Thus, remove all mentions to aclk in classd driver and update macros and
variable names.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Mark Brown <broonie@kernel.org>
---

added in v2:
  - split from bigger patch with audio PLLs and DT binding,
  - updated all variables and macros named ACLK to GCLK,

 sound/soc/atmel/atmel-classd.c | 47 +++++++++++++-----------------------------
 1 file changed, 14 insertions(+), 33 deletions(-)

diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
index b7ef8c59b49a..be6b775b6f46 100644
--- a/sound/soc/atmel/atmel-classd.c
+++ b/sound/soc/atmel/atmel-classd.c
@@ -32,7 +32,6 @@ struct atmel_classd {
 	struct regmap *regmap;
 	struct clk *pclk;
 	struct clk *gclk;
-	struct clk *aclk;
 	int irq;
 	const struct atmel_classd_pdata *pdata;
 };
@@ -330,11 +329,6 @@ static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
-	int ret;
-
-	ret = clk_prepare_enable(dd->aclk);
-	if (ret)
-		return ret;
 
 	return clk_prepare_enable(dd->gclk);
 }
@@ -357,31 +351,31 @@ static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai,
 	return 0;
 }
 
-#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
-#define CLASSD_ACLK_RATE_12M288_MPY_8  (12288 * 1000 * 8)
+#define CLASSD_GCLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
+#define CLASSD_GCLK_RATE_12M288_MPY_8  (12288 * 1000 * 8)
 
 static struct {
 	int rate;
 	int sample_rate;
 	int dsp_clk;
-	unsigned long aclk_rate;
+	unsigned long gclk_rate;
 } const sample_rates[] = {
 	{ 8000,  CLASSD_INTPMR_FRAME_8K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 	{ 16000, CLASSD_INTPMR_FRAME_16K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 	{ 32000, CLASSD_INTPMR_FRAME_32K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 	{ 48000, CLASSD_INTPMR_FRAME_48K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 	{ 96000, CLASSD_INTPMR_FRAME_96K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 	{ 22050, CLASSD_INTPMR_FRAME_22K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
 	{ 44100, CLASSD_INTPMR_FRAME_44K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
 	{ 88200, CLASSD_INTPMR_FRAME_88K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
 };
 
 static int
@@ -410,13 +404,12 @@ atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	dev_dbg(codec->dev,
-		"Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n",
-		sample_rates[best].rate, sample_rates[best].aclk_rate);
+		"Selected SAMPLE_RATE of %dHz, GCLK_RATE of %ldHz\n",
+		sample_rates[best].rate, sample_rates[best].gclk_rate);
 
 	clk_disable_unprepare(dd->gclk);
-	clk_disable_unprepare(dd->aclk);
 
-	ret = clk_set_rate(dd->aclk, sample_rates[best].aclk_rate);
+	ret = clk_set_rate(dd->gclk, sample_rates[best].gclk_rate);
 	if (ret)
 		return ret;
 
@@ -426,10 +419,6 @@ atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
 
 	snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val);
 
-	ret = clk_prepare_enable(dd->aclk);
-	if (ret)
-		return ret;
-
 	return clk_prepare_enable(dd->gclk);
 }
 
@@ -441,7 +430,6 @@ atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream,
 	struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
 
 	clk_disable_unprepare(dd->gclk);
-	clk_disable_unprepare(dd->aclk);
 }
 
 static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream,
@@ -596,13 +584,6 @@ static int atmel_classd_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	dd->aclk = devm_clk_get(dev, "aclk");
-	if (IS_ERR(dd->aclk)) {
-		ret = PTR_ERR(dd->aclk);
-		dev_err(dev, "failed to get audio clock: %d\n", ret);
-		return ret;
-	}
-
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	io_base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(io_base)) {
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 8/9] ASoC: atmel-classd: remove aclk clock
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: linux-arm-kernel

Since gclk (generated-clk) is now able to determine the rate of the
audio_pll, there is no need for classd to have a direct phandle to the
audio_pll while already having a phandle to gclk.

Thus, remove all mentions to aclk in classd driver and update macros and
variable names.

Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Acked-by: Mark Brown <broonie@kernel.org>
---

added in v2:
  - split from bigger patch with audio PLLs and DT binding,
  - updated all variables and macros named ACLK to GCLK,

 sound/soc/atmel/atmel-classd.c | 47 +++++++++++++-----------------------------
 1 file changed, 14 insertions(+), 33 deletions(-)

diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
index b7ef8c59b49a..be6b775b6f46 100644
--- a/sound/soc/atmel/atmel-classd.c
+++ b/sound/soc/atmel/atmel-classd.c
@@ -32,7 +32,6 @@ struct atmel_classd {
 	struct regmap *regmap;
 	struct clk *pclk;
 	struct clk *gclk;
-	struct clk *aclk;
 	int irq;
 	const struct atmel_classd_pdata *pdata;
 };
@@ -330,11 +329,6 @@ static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream,
 {
 	struct snd_soc_pcm_runtime *rtd = substream->private_data;
 	struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
-	int ret;
-
-	ret = clk_prepare_enable(dd->aclk);
-	if (ret)
-		return ret;
 
 	return clk_prepare_enable(dd->gclk);
 }
@@ -357,31 +351,31 @@ static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai,
 	return 0;
 }
 
-#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
-#define CLASSD_ACLK_RATE_12M288_MPY_8  (12288 * 1000 * 8)
+#define CLASSD_GCLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
+#define CLASSD_GCLK_RATE_12M288_MPY_8  (12288 * 1000 * 8)
 
 static struct {
 	int rate;
 	int sample_rate;
 	int dsp_clk;
-	unsigned long aclk_rate;
+	unsigned long gclk_rate;
 } const sample_rates[] = {
 	{ 8000,  CLASSD_INTPMR_FRAME_8K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 	{ 16000, CLASSD_INTPMR_FRAME_16K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 	{ 32000, CLASSD_INTPMR_FRAME_32K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 	{ 48000, CLASSD_INTPMR_FRAME_48K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 	{ 96000, CLASSD_INTPMR_FRAME_96K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_GCLK_RATE_12M288_MPY_8 },
 	{ 22050, CLASSD_INTPMR_FRAME_22K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
 	{ 44100, CLASSD_INTPMR_FRAME_44K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
 	{ 88200, CLASSD_INTPMR_FRAME_88K,
-	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+	CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_GCLK_RATE_11M2896_MPY_8 },
 };
 
 static int
@@ -410,13 +404,12 @@ atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
 	}
 
 	dev_dbg(codec->dev,
-		"Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n",
-		sample_rates[best].rate, sample_rates[best].aclk_rate);
+		"Selected SAMPLE_RATE of %dHz, GCLK_RATE of %ldHz\n",
+		sample_rates[best].rate, sample_rates[best].gclk_rate);
 
 	clk_disable_unprepare(dd->gclk);
-	clk_disable_unprepare(dd->aclk);
 
-	ret = clk_set_rate(dd->aclk, sample_rates[best].aclk_rate);
+	ret = clk_set_rate(dd->gclk, sample_rates[best].gclk_rate);
 	if (ret)
 		return ret;
 
@@ -426,10 +419,6 @@ atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
 
 	snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val);
 
-	ret = clk_prepare_enable(dd->aclk);
-	if (ret)
-		return ret;
-
 	return clk_prepare_enable(dd->gclk);
 }
 
@@ -441,7 +430,6 @@ atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream,
 	struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
 
 	clk_disable_unprepare(dd->gclk);
-	clk_disable_unprepare(dd->aclk);
 }
 
 static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream,
@@ -596,13 +584,6 @@ static int atmel_classd_probe(struct platform_device *pdev)
 		return ret;
 	}
 
-	dd->aclk = devm_clk_get(dev, "aclk");
-	if (IS_ERR(dd->aclk)) {
-		ret = PTR_ERR(dd->aclk);
-		dev_err(dev, "failed to get audio clock: %d\n", ret);
-		return ret;
-	}
-
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	io_base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(io_base)) {
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 9/9] ARM: dts: at91: sama5d2_xplained: add pin muxing and enable classd
  2017-07-13  7:49 ` Quentin Schulz
@ 2017-07-13  7:49   ` Quentin Schulz
  -1 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: Cyrille Pitchen, cyrille.pitchen, thomas.petazzoni, linux-clk,
	devicetree, linux-kernel, alsa-devel, linux-arm-kernel,
	Nicolas Ferre, Quentin Schulz

From: Cyrille Pitchen <cyrille.pitchen@atmel.com>

This patch adds the pin muxing for classd and enables it.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---
 arch/arm/boot/dts/at91-sama5d2_xplained.dts | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
index 2e2c3d1a1fa2..e92b030ca45a 100644
--- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
@@ -355,6 +355,14 @@
 					bias-pull-up;
 				};
 
+				pinctrl_classd_default: classd_default {
+					pinmux = <PIN_PB1__CLASSD_R0>,
+						 <PIN_PB2__CLASSD_R1>,
+						 <PIN_PB3__CLASSD_R2>,
+						 <PIN_PB4__CLASSD_R3>;
+					bias-pull-up;
+				};
+
 				pinctrl_flx0_default: flx0_default {
 					pinmux = <PIN_PB28__FLEXCOM0_IO0>,
 						 <PIN_PB29__FLEXCOM0_IO1>;
@@ -488,6 +496,14 @@
 
 			};
 
+			classd: classd@fc048000 {
+				pinctrl-names = "default";
+				pinctrl-0 = <&pinctrl_classd_default>;
+				atmel,pwm-type = "diff";
+				atmel,non-overlap-time = <10>;
+				status = "okay";
+			};
+
 			can1: can@fc050000 {
 				pinctrl-names = "default";
 				pinctrl-0 = <&pinctrl_can1_default>;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v3 9/9] ARM: dts: at91: sama5d2_xplained: add pin muxing and enable classd
@ 2017-07-13  7:49   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-13  7:49 UTC (permalink / raw)
  To: linux-arm-kernel

From: Cyrille Pitchen <cyrille.pitchen@atmel.com>

This patch adds the pin muxing for classd and enables it.

Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
---
 arch/arm/boot/dts/at91-sama5d2_xplained.dts | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/arch/arm/boot/dts/at91-sama5d2_xplained.dts b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
index 2e2c3d1a1fa2..e92b030ca45a 100644
--- a/arch/arm/boot/dts/at91-sama5d2_xplained.dts
+++ b/arch/arm/boot/dts/at91-sama5d2_xplained.dts
@@ -355,6 +355,14 @@
 					bias-pull-up;
 				};
 
+				pinctrl_classd_default: classd_default {
+					pinmux = <PIN_PB1__CLASSD_R0>,
+						 <PIN_PB2__CLASSD_R1>,
+						 <PIN_PB3__CLASSD_R2>,
+						 <PIN_PB4__CLASSD_R3>;
+					bias-pull-up;
+				};
+
 				pinctrl_flx0_default: flx0_default {
 					pinmux = <PIN_PB28__FLEXCOM0_IO0>,
 						 <PIN_PB29__FLEXCOM0_IO1>;
@@ -488,6 +496,14 @@
 
 			};
 
+			classd: classd at fc048000 {
+				pinctrl-names = "default";
+				pinctrl-0 = <&pinctrl_classd_default>;
+				atmel,pwm-type = "diff";
+				atmel,non-overlap-time = <10>;
+				status = "okay";
+			};
+
 			can1: can at fc050000 {
 				pinctrl-names = "default";
 				pinctrl-0 = <&pinctrl_can1_default>;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 0/9] add support for Sama5d2 audio PLLs and enable ClassD
  2017-07-13  7:49 ` Quentin Schulz
@ 2017-07-19  6:28   ` Quentin Schulz
  -1 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-19  6:28 UTC (permalink / raw)
  To: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai
  Cc: cyrille.pitchen, thomas.petazzoni, linux-clk, devicetree,
	linux-kernel, alsa-devel, linux-arm-kernel

Hi all,

It's been almost a week with no comments on this patch set, so kindly
pinging.

Thanks,
Quentin

On 13/07/2017 09:49, Quentin Schulz wrote:
> This patch series adds support for the audio PLLs and enables ClassD that
> can be found in ATMEL Sama5d2 SoC.
> 
> There are two audio PLLs (PMC and PAD) that shares the same parent (FRAC).
> FRAC can output between 620 and 700MHz and only multiply the rate of its
> parent. The two audio PLLs then divide the FRAC rate to best match the
> asked rate.
> 
> I basically took an old patch series posted by Nicolas on December, 6th
> 2016[1][2][3] and the comments Boris did on the first version[4] Nicolas
> sent on July, 15th 2015.
> 
> I also fixed the function used to compute the divisors, removed useless
> spinlocks and added a range to the audio frac PLL to stay within vendor's
> supported range. Clocks that are children of gclk (generated-clk) are now
> able to propagate rate to the audio PLL clocks when needed.
> 
> However, there are multiple children clocks that could technically
> change the rate of audio_pll (via gck). With the rate locking introduced
> in Jerome Brunet's patch series[5], the first consumer to enable the clock
> will be the one definitely setting the rate of the clock. Without the rate
> locking, the last consumer to set the rate will be able to mess with the
> rate.
> Since audio IPs are most likely to request the same rate, we enforce
> that the only clks able to modify gck rate are those of audio IPs.
> 
> To remain consistent, we deny other clocks to be children of audio_pll.
> 
> Thanks,
> Quentin
> 
> [1] https://patchwork.kernel.org/patch/9462351/
> [2] https://patchwork.kernel.org/patch/9462347/
> [3] https://patchwork.kernel.org/patch/9462349/
> [4] https://www.spinics.net/lists/arm-kernel/msg436120.html
> [5] http://www.spinics.net/lists/linux-clk/msg17927.html
> 
> Cyrille Pitchen (2):
>   ARM: dts: at91: sama5d2: add classd nodes
>   ARM: dts: at91: sama5d2_xplained: add pin muxing and enable classd
> 
> Quentin Schulz (7):
>   clk: at91: clk-generated: remove useless divisor loop
>   clk: at91: add audio plls to the compatible list in DT binding
>   clk: at91: add audio pll clock drivers
>   clk: at91: clk-generated: create function to find best_diff
>   clk: at91: clk-generated: make gclk determine audio_pll rate
>   ASoC: atmel-classd: remove aclk clock from DT binding
>   ASoC: atmel-classd: remove aclk clock
> 
>  .../devicetree/bindings/clock/at91-clock.txt       |  10 +
>  .../devicetree/bindings/sound/atmel-classd.txt     |   9 +-
>  arch/arm/boot/dts/at91-sama5d2_xplained.dts        |  16 ++
>  arch/arm/boot/dts/sama5d2.dtsi                     |  39 +++-
>  arch/arm/mach-at91/Kconfig                         |   4 +
>  drivers/clk/at91/Makefile                          |   2 +
>  drivers/clk/at91/clk-audio-pll-pad.c               | 206 ++++++++++++++++++
>  drivers/clk/at91/clk-audio-pll-pmc.c               | 174 +++++++++++++++
>  drivers/clk/at91/clk-audio-pll.c                   | 239 +++++++++++++++++++++
>  drivers/clk/at91/clk-generated.c                   | 101 +++++++--
>  include/linux/clk/at91_pmc.h                       |  25 +++
>  sound/soc/atmel/atmel-classd.c                     |  47 ++--
>  12 files changed, 813 insertions(+), 59 deletions(-)
>  create mode 100644 drivers/clk/at91/clk-audio-pll-pad.c
>  create mode 100644 drivers/clk/at91/clk-audio-pll-pmc.c
>  create mode 100644 drivers/clk/at91/clk-audio-pll.c
> 

-- 
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 0/9] add support for Sama5d2 audio PLLs and enable ClassD
@ 2017-07-19  6:28   ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-19  6:28 UTC (permalink / raw)
  To: linux-arm-kernel

Hi all,

It's been almost a week with no comments on this patch set, so kindly
pinging.

Thanks,
Quentin

On 13/07/2017 09:49, Quentin Schulz wrote:
> This patch series adds support for the audio PLLs and enables ClassD that
> can be found in ATMEL Sama5d2 SoC.
> 
> There are two audio PLLs (PMC and PAD) that shares the same parent (FRAC).
> FRAC can output between 620 and 700MHz and only multiply the rate of its
> parent. The two audio PLLs then divide the FRAC rate to best match the
> asked rate.
> 
> I basically took an old patch series posted by Nicolas on December, 6th
> 2016[1][2][3] and the comments Boris did on the first version[4] Nicolas
> sent on July, 15th 2015.
> 
> I also fixed the function used to compute the divisors, removed useless
> spinlocks and added a range to the audio frac PLL to stay within vendor's
> supported range. Clocks that are children of gclk (generated-clk) are now
> able to propagate rate to the audio PLL clocks when needed.
> 
> However, there are multiple children clocks that could technically
> change the rate of audio_pll (via gck). With the rate locking introduced
> in Jerome Brunet's patch series[5], the first consumer to enable the clock
> will be the one definitely setting the rate of the clock. Without the rate
> locking, the last consumer to set the rate will be able to mess with the
> rate.
> Since audio IPs are most likely to request the same rate, we enforce
> that the only clks able to modify gck rate are those of audio IPs.
> 
> To remain consistent, we deny other clocks to be children of audio_pll.
> 
> Thanks,
> Quentin
> 
> [1] https://patchwork.kernel.org/patch/9462351/
> [2] https://patchwork.kernel.org/patch/9462347/
> [3] https://patchwork.kernel.org/patch/9462349/
> [4] https://www.spinics.net/lists/arm-kernel/msg436120.html
> [5] http://www.spinics.net/lists/linux-clk/msg17927.html
> 
> Cyrille Pitchen (2):
>   ARM: dts: at91: sama5d2: add classd nodes
>   ARM: dts: at91: sama5d2_xplained: add pin muxing and enable classd
> 
> Quentin Schulz (7):
>   clk: at91: clk-generated: remove useless divisor loop
>   clk: at91: add audio plls to the compatible list in DT binding
>   clk: at91: add audio pll clock drivers
>   clk: at91: clk-generated: create function to find best_diff
>   clk: at91: clk-generated: make gclk determine audio_pll rate
>   ASoC: atmel-classd: remove aclk clock from DT binding
>   ASoC: atmel-classd: remove aclk clock
> 
>  .../devicetree/bindings/clock/at91-clock.txt       |  10 +
>  .../devicetree/bindings/sound/atmel-classd.txt     |   9 +-
>  arch/arm/boot/dts/at91-sama5d2_xplained.dts        |  16 ++
>  arch/arm/boot/dts/sama5d2.dtsi                     |  39 +++-
>  arch/arm/mach-at91/Kconfig                         |   4 +
>  drivers/clk/at91/Makefile                          |   2 +
>  drivers/clk/at91/clk-audio-pll-pad.c               | 206 ++++++++++++++++++
>  drivers/clk/at91/clk-audio-pll-pmc.c               | 174 +++++++++++++++
>  drivers/clk/at91/clk-audio-pll.c                   | 239 +++++++++++++++++++++
>  drivers/clk/at91/clk-generated.c                   | 101 +++++++--
>  include/linux/clk/at91_pmc.h                       |  25 +++
>  sound/soc/atmel/atmel-classd.c                     |  47 ++--
>  12 files changed, 813 insertions(+), 59 deletions(-)
>  create mode 100644 drivers/clk/at91/clk-audio-pll-pad.c
>  create mode 100644 drivers/clk/at91/clk-audio-pll-pmc.c
>  create mode 100644 drivers/clk/at91/clk-audio-pll.c
> 

-- 
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 2/9] clk: at91: add audio plls to the compatible list in DT binding
@ 2017-07-19  6:31     ` Boris Brezillon
  0 siblings, 0 replies; 49+ messages in thread
From: Boris Brezillon @ 2017-07-19  6:31 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, perex, tiwai,
	cyrille.pitchen, thomas.petazzoni, linux-clk, devicetree,
	linux-kernel, alsa-devel, linux-arm-kernel

On Thu, 13 Jul 2017 09:49:20 +0200
Quentin Schulz <quentin.schulz@free-electrons.com> wrote:

> This new clock driver set allows to have a fractional divided clock that
> would generate a precise clock particularly suitable for audio
> applications.
> 
> The main audio pll clock has two children clocks: one that is connected
> to the PMC, the other that can directly drive a pad. As these two routes
> have different enable bits and different dividers and divider formulas,
> they are handled by two different drivers.
> 
> This adds the audio plls (frac, pad and pmc) to the compatible list of
> at91 clocks in DT binding.
> 
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> Acked-by: Rob Herring <robh@kernel.org>

Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>

> ---
> 
> added in v2:
>   - split from big patch with pll drivers and dt-binding
> 
>  Documentation/devicetree/bindings/clock/at91-clock.txt | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt
> index 5f3ad65daf69..51c259a92d02 100644
> --- a/Documentation/devicetree/bindings/clock/at91-clock.txt
> +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
> @@ -81,6 +81,16 @@ Required properties:
>  	"atmel,sama5d2-clk-generated":
>  		at91 generated clock
>  
> +	"atmel,sama5d2-clk-audio-pll-frac":
> +		at91 audio fractional pll
> +
> +	"atmel,sama5d2-clk-audio-pll-pad":
> +		at91 audio pll CLK_AUDIO output pin
> +
> +	"atmel,sama5d2-clk-audio-pll-pmc"
> +		at91 audio pll output on AUDIOPLLCLK that feeds the PMC
> +		and can be used by peripheral clock or generic clock
> +
>  Required properties for SCKC node:
>  - reg : defines the IO memory reserved for the SCKC.
>  - #size-cells : shall be 0 (reg is used to encode clk id).

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 2/9] clk: at91: add audio plls to the compatible list in DT binding
@ 2017-07-19  6:31     ` Boris Brezillon
  0 siblings, 0 replies; 49+ messages in thread
From: Boris Brezillon @ 2017-07-19  6:31 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: mturquette-rdvid1DuHRBWk0Htik3J/w, sboyd-sgV2jX0FEOL9JmXXK+q4OQ,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A,
	nicolas.ferre-UWL1GkI3JZL3oGB3hsPCZA,
	alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw, perex-/Fr2/VpizcU,
	tiwai-IBi9RG/b67k, cyrille.pitchen-yU5RGvR974pGWvitb5QawA,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

On Thu, 13 Jul 2017 09:49:20 +0200
Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org> wrote:

> This new clock driver set allows to have a fractional divided clock that
> would generate a precise clock particularly suitable for audio
> applications.
> 
> The main audio pll clock has two children clocks: one that is connected
> to the PMC, the other that can directly drive a pad. As these two routes
> have different enable bits and different dividers and divider formulas,
> they are handled by two different drivers.
> 
> This adds the audio plls (frac, pad and pmc) to the compatible list of
> at91 clocks in DT binding.
> 
> Signed-off-by: Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

Acked-by: Boris Brezillon <boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>

> ---
> 
> added in v2:
>   - split from big patch with pll drivers and dt-binding
> 
>  Documentation/devicetree/bindings/clock/at91-clock.txt | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt
> index 5f3ad65daf69..51c259a92d02 100644
> --- a/Documentation/devicetree/bindings/clock/at91-clock.txt
> +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
> @@ -81,6 +81,16 @@ Required properties:
>  	"atmel,sama5d2-clk-generated":
>  		at91 generated clock
>  
> +	"atmel,sama5d2-clk-audio-pll-frac":
> +		at91 audio fractional pll
> +
> +	"atmel,sama5d2-clk-audio-pll-pad":
> +		at91 audio pll CLK_AUDIO output pin
> +
> +	"atmel,sama5d2-clk-audio-pll-pmc"
> +		at91 audio pll output on AUDIOPLLCLK that feeds the PMC
> +		and can be used by peripheral clock or generic clock
> +
>  Required properties for SCKC node:
>  - reg : defines the IO memory reserved for the SCKC.
>  - #size-cells : shall be 0 (reg is used to encode clk id).

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 2/9] clk: at91: add audio plls to the compatible list in DT binding
@ 2017-07-19  6:31     ` Boris Brezillon
  0 siblings, 0 replies; 49+ messages in thread
From: Boris Brezillon @ 2017-07-19  6:31 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 13 Jul 2017 09:49:20 +0200
Quentin Schulz <quentin.schulz@free-electrons.com> wrote:

> This new clock driver set allows to have a fractional divided clock that
> would generate a precise clock particularly suitable for audio
> applications.
> 
> The main audio pll clock has two children clocks: one that is connected
> to the PMC, the other that can directly drive a pad. As these two routes
> have different enable bits and different dividers and divider formulas,
> they are handled by two different drivers.
> 
> This adds the audio plls (frac, pad and pmc) to the compatible list of
> at91 clocks in DT binding.
> 
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> Acked-by: Rob Herring <robh@kernel.org>

Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>

> ---
> 
> added in v2:
>   - split from big patch with pll drivers and dt-binding
> 
>  Documentation/devicetree/bindings/clock/at91-clock.txt | 10 ++++++++++
>  1 file changed, 10 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/clock/at91-clock.txt b/Documentation/devicetree/bindings/clock/at91-clock.txt
> index 5f3ad65daf69..51c259a92d02 100644
> --- a/Documentation/devicetree/bindings/clock/at91-clock.txt
> +++ b/Documentation/devicetree/bindings/clock/at91-clock.txt
> @@ -81,6 +81,16 @@ Required properties:
>  	"atmel,sama5d2-clk-generated":
>  		at91 generated clock
>  
> +	"atmel,sama5d2-clk-audio-pll-frac":
> +		at91 audio fractional pll
> +
> +	"atmel,sama5d2-clk-audio-pll-pad":
> +		at91 audio pll CLK_AUDIO output pin
> +
> +	"atmel,sama5d2-clk-audio-pll-pmc"
> +		at91 audio pll output on AUDIOPLLCLK that feeds the PMC
> +		and can be used by peripheral clock or generic clock
> +
>  Required properties for SCKC node:
>  - reg : defines the IO memory reserved for the SCKC.
>  - #size-cells : shall be 0 (reg is used to encode clk id).

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 3/9] clk: at91: add audio pll clock drivers
  2017-07-13  7:49   ` Quentin Schulz
@ 2017-07-19  6:31     ` Boris Brezillon
  -1 siblings, 0 replies; 49+ messages in thread
From: Boris Brezillon @ 2017-07-19  6:31 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, perex, tiwai,
	cyrille.pitchen, thomas.petazzoni, linux-clk, devicetree,
	linux-kernel, alsa-devel, linux-arm-kernel, Nicolas Ferre

On Thu, 13 Jul 2017 09:49:21 +0200
Quentin Schulz <quentin.schulz@free-electrons.com> wrote:

> This new clock driver set allows to have a fractional divided clock that
> would generate a precise clock particularly suitable for audio
> applications.
> 
> The main audio pll clock has two children clocks: one that is connected
> to the PMC, the other that can directly drive a pad. As these two routes
> have different enable bits and different dividers and divider formulas,
> they are handled by two different drivers. Each of them could modify the
> rate of the main audio pll parent.
> 
> The main audio pll clock can output 620MHz to 700MHz.
> 
> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>

Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>

> ---
> 
> v2:
>   - split DT binding in a different patch,
>   - removed unused AUDIO_PLL_*FOUT* defines from clk-audio-pll-pmc,
>   - split classD modifications in a different patch,
> 
>  arch/arm/mach-at91/Kconfig           |   4 +
>  drivers/clk/at91/Makefile            |   2 +
>  drivers/clk/at91/clk-audio-pll-pad.c | 206 ++++++++++++++++++++++++++++++
>  drivers/clk/at91/clk-audio-pll-pmc.c | 174 +++++++++++++++++++++++++
>  drivers/clk/at91/clk-audio-pll.c     | 239 +++++++++++++++++++++++++++++++++++
>  include/linux/clk/at91_pmc.h         |  25 ++++
>  6 files changed, 650 insertions(+)
>  create mode 100644 drivers/clk/at91/clk-audio-pll-pad.c
>  create mode 100644 drivers/clk/at91/clk-audio-pll-pmc.c
>  create mode 100644 drivers/clk/at91/clk-audio-pll.c
> 
> diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
> index d735e5fc4772..9ae14d59a9ce 100644
> --- a/arch/arm/mach-at91/Kconfig
> +++ b/arch/arm/mach-at91/Kconfig
> @@ -26,6 +26,7 @@ config SOC_SAMA5D2
>  	select HAVE_AT91_USB_CLK
>  	select HAVE_AT91_H32MX
>  	select HAVE_AT91_GENERATED_CLK
> +	select HAVE_AT91_AUDIO_PLL
>  	select PINCTRL_AT91PIO4
>  	help
>  	  Select this if ou are using one of Atmel's SAMA5D2 family SoC.
> @@ -125,6 +126,9 @@ config HAVE_AT91_H32MX
>  config HAVE_AT91_GENERATED_CLK
>  	bool
>  
> +config HAVE_AT91_AUDIO_PLL
> +	bool
> +
>  config SOC_SAM_V4_V5
>  	bool
>  
> diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
> index 13e67bd35cff..c9353d17763a 100644
> --- a/drivers/clk/at91/Makefile
> +++ b/drivers/clk/at91/Makefile
> @@ -6,6 +6,8 @@ obj-y += pmc.o sckc.o
>  obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o
>  obj-y += clk-system.o clk-peripheral.o clk-programmable.o
>  
> +obj-$(CONFIG_HAVE_AT91_AUDIO_PLL)	+= clk-audio-pll.o
> +obj-$(CONFIG_HAVE_AT91_AUDIO_PLL)	+= clk-audio-pll-pmc.o clk-audio-pll-pad.o
>  obj-$(CONFIG_HAVE_AT91_UTMI)		+= clk-utmi.o
>  obj-$(CONFIG_HAVE_AT91_USB_CLK)		+= clk-usb.o
>  obj-$(CONFIG_HAVE_AT91_SMD)		+= clk-smd.o
> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
> new file mode 100644
> index 000000000000..10dd6d625696
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
> @@ -0,0 +1,206 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: PAD output for fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * enable - clk_enable writes divisors and enables PAD output
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate / (qdaudio * div))
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_QDPAD(qd, div)	((AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(qd) & \
> +					  AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK) | \
> +					 (AT91_PMC_AUDIO_PLL_QDPAD_DIV(div) & \
> +					  AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK))
> +
> +struct clk_audio_pad {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u8 qdaudio;
> +	u8 div;
> +};
> +
> +#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw)
> +
> +static int clk_audio_pll_pad_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1,
> +			   AT91_PMC_AUDIO_PLL_QDPAD_MASK,
> +			   AUDIO_PLL_QDPAD(apad_ck->qdaudio, apad_ck->div));
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PADEN, AT91_PMC_AUDIO_PLL_PADEN);
> +
> +	return 0;
> +}
> +
> +static void clk_audio_pll_pad_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PADEN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw,
> +						   unsigned long parent_rate)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +	unsigned long apad_rate = 0;
> +
> +	if (apad_ck->qdaudio && apad_ck->div)
> +		apad_rate = parent_rate / (apad_ck->qdaudio * apad_ck->div);
> +
> +	pr_debug("A PLL/PAD: %s, apad_rate = %lu (div = %u, qdaudio = %u)\n",
> +		 __func__, apad_rate, apad_ck->div, apad_ck->qdaudio);
> +
> +	return apad_rate;
> +}
> +
> +static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
> +					 unsigned long *parent_rate)
> +{
> +	struct clk_hw *pclk = clk_hw_get_parent(hw);
> +	long best_rate = -EINVAL;
> +	unsigned long best_parent_rate;
> +	unsigned long tmp_qd;
> +	u32 div;
> +	long tmp_rate;
> +	int tmp_diff;
> +	int best_diff = -1;
> +
> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, *parent_rate);
> +
> +	/*
> +	 * Rate divisor is actually made of two different divisors, multiplied
> +	 * between themselves before dividing the rate.
> +	 * tmp_qd goes from 1 to 31 and div is either 2 or 3.
> +	 * In order to avoid testing twice the rate divisor (e.g. divisor 12 can
> +	 * be found with (tmp_qd, div) = (2, 6) or (3, 4)), we remove any loop
> +	 * for a rate divisor when div is 2 and tmp_qd is a multiple of 3.
> +	 * We cannot inverse it (condition div is 3 and tmp_qd is even) or we
> +	 * would miss some rate divisor that aren't reachable with div being 2
> +	 * (e.g. rate divisor 90 is made with div = 3 and tmp_qd = 30, thus
> +	 * tmp_qd is even so we skip it because we think div 2 could make this
> +	 * rate divisor which isn't possible since tmp_qd has to be <= 31).
> +	 */
> +	for (tmp_qd = 1; tmp_qd < AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX; tmp_qd++)
> +		for (div = 2; div <= 3; div++) {
> +			if (div == 2 && tmp_qd % 3 == 0)
> +				continue;
> +
> +			best_parent_rate = clk_hw_round_rate(pclk,
> +							rate * tmp_qd * div);
> +			tmp_rate = best_parent_rate / (div * tmp_qd);
> +			tmp_diff = abs(rate - tmp_rate);
> +
> +			if (best_diff < 0 || best_diff > tmp_diff) {
> +				*parent_rate = best_parent_rate;
> +				best_rate = tmp_rate;
> +				best_diff = tmp_diff;
> +			}
> +		}
> +
> +	pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n",
> +		 __func__, best_rate, best_parent_rate);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long parent_rate)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +	u8 tmp_div;
> +
> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, parent_rate);
> +
> +	if (!rate)
> +		return -EINVAL;
> +
> +	tmp_div = parent_rate / rate;
> +	if (tmp_div % 3 == 0) {
> +		apad_ck->qdaudio = tmp_div / 3;
> +		apad_ck->div = 3;
> +	} else {
> +		apad_ck->qdaudio = tmp_div / 2;
> +		apad_ck->div = 2;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_pad_ops = {
> +	.enable = clk_audio_pll_pad_enable,
> +	.disable = clk_audio_pll_pad_disable,
> +	.recalc_rate = clk_audio_pll_pad_recalc_rate,
> +	.round_rate = clk_audio_pll_pad_round_rate,
> +	.set_rate = clk_audio_pll_pad_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
> +{
> +	struct clk_audio_pad *apad_ck;
> +	struct clk_init_data init;
> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);
> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
> +	if (!apad_ck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_pad_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> +		CLK_SET_RATE_PARENT;
> +
> +	apad_ck->hw.init = &init;
> +	apad_ck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &apad_ck->hw);
> +	if (ret)
> +		kfree(apad_ck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
> +	       "atmel,sama5d2-clk-audio-pll-pad",
> +	       of_sama5d2_clk_audio_pll_pad_setup);
> diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c
> new file mode 100644
> index 000000000000..7b0847ed7a4b
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll-pmc.c
> @@ -0,0 +1,174 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: PMC output for fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * enable - clk_enable writes qdpmc, and enables PMC output
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate / (qdpmc + 1)
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_QDPMC_MAX		(AT91_PMC_AUDIO_PLL_QDPMC_MASK >> \
> +						AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
> +struct clk_audio_pmc {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u8 qdpmc;
> +};
> +
> +#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw)
> +
> +static int clk_audio_pll_pmc_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PMCEN |
> +			   AT91_PMC_AUDIO_PLL_QDPMC_MASK,
> +			   AT91_PMC_AUDIO_PLL_PMCEN |
> +			   AT91_PMC_AUDIO_PLL_QDPMC(apmc_ck->qdpmc));
> +	return 0;
> +}
> +
> +static void clk_audio_pll_pmc_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PMCEN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw,
> +						   unsigned long parent_rate)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +	unsigned long apmc_rate = 0;
> +
> +	apmc_rate = parent_rate / (apmc_ck->qdpmc + 1);
> +
> +	pr_debug("A PLL/PMC: %s, apmc_rate = %lu (qdpmc = %u)\n", __func__,
> +		 apmc_rate, apmc_ck->qdpmc);
> +
> +	return apmc_rate;
> +}
> +
> +static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
> +					 unsigned long *parent_rate)
> +{
> +	struct clk_hw *pclk = clk_hw_get_parent(hw);
> +	long best_rate = -EINVAL;
> +	unsigned long best_parent_rate = 0;
> +	u32 tmp_qd = 0, div;
> +	long tmp_rate;
> +	int tmp_diff;
> +	int best_diff = -1;
> +
> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, *parent_rate);
> +
> +	for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) {
> +		best_parent_rate = clk_round_rate(pclk->clk, rate * div);
> +		tmp_rate = best_parent_rate / div;
> +		tmp_diff = abs(rate - tmp_rate);
> +
> +		if (best_diff < 0 || best_diff > tmp_diff) {
> +			*parent_rate = best_parent_rate;
> +			best_rate = tmp_rate;
> +			best_diff = tmp_diff;
> +			tmp_qd = div;
> +		}
> +	}
> +
> +	pr_debug("A PLL/PMC: %s, best_rate = %ld, best_parent_rate = %lu (qd = %d)\n",
> +		 __func__, best_rate, *parent_rate, tmp_qd - 1);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long parent_rate)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	if (!rate)
> +		return -EINVAL;
> +
> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, parent_rate);
> +
> +	apmc_ck->qdpmc = parent_rate / rate - 1;
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_pmc_ops = {
> +	.enable = clk_audio_pll_pmc_enable,
> +	.disable = clk_audio_pll_pmc_disable,
> +	.recalc_rate = clk_audio_pll_pmc_recalc_rate,
> +	.round_rate = clk_audio_pll_pmc_round_rate,
> +	.set_rate = clk_audio_pll_pmc_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
> +{
> +	struct clk_audio_pmc *apmc_ck;
> +	struct clk_init_data init;
> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);
> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
> +	if (!apmc_ck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_pmc_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> +		CLK_SET_RATE_PARENT;
> +
> +	apmc_ck->hw.init = &init;
> +	apmc_ck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &apmc_ck->hw);
> +	if (ret)
> +		kfree(apmc_ck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw);
> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
> +	       "atmel,sama5d2-clk-audio-pll-pmc",
> +	       of_sama5d2_clk_audio_pll_pmc_setup);
> diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
> new file mode 100644
> index 000000000000..efc2cb58da85
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll.c
> @@ -0,0 +1,239 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *		       Songjun Wu <songjun.wu@atmel.com>,
> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: Fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare puts audio PLL in reset state
> + * enable - clk_enable writes nd, fracr parameters and enables PLL
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate * ((nd + 1) + (fracr / 2^22))
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_DIV_FRAC	BIT(22)
> +#define AUDIO_PLL_ND_MAX	(AT91_PMC_AUDIO_PLL_ND_MASK >> \
> +					AT91_PMC_AUDIO_PLL_ND_OFFSET)
> +
> +#define AUDIO_PLL_FOUT_MIN	620000000
> +#define AUDIO_PLL_FOUT_MAX	700000000
> +
> +struct clk_audio_frac {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u32 fracr;
> +	u8 nd;
> +};
> +
> +#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw)
> +
> +static int clk_audio_pll_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN, 0);
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN,
> +			   AT91_PMC_AUDIO_PLL_RESETN);
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL1,
> +			   AT91_PMC_AUDIO_PLL_FRACR_MASK, fck->fracr);
> +
> +	/*
> +	 * reset and enable have to be done in 2 separated writes
> +	 * for AT91_PMC_AUDIO_PLL0
> +	 */
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PLLEN |
> +			   AT91_PMC_AUDIO_PLL_ND_MASK,
> +			   AT91_PMC_AUDIO_PLL_PLLEN |
> +			   AT91_PMC_AUDIO_PLL_ND(fck->nd));
> +
> +	return 0;
> +}
> +
> +static void clk_audio_pll_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PLLEN, 0);
> +	/* do it in 2 separated writes */
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
> +					unsigned long nd, unsigned long fracr)
> +{
> +	unsigned long long fr = (unsigned long long)parent_rate *
> +						(unsigned long long)fracr;
> +
> +	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
> +
> +	fr = DIV_ROUND_CLOSEST_ULL(fr, AUDIO_PLL_DIV_FRAC);
> +
> +	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
> +
> +	return parent_rate * (nd + 1) + fr;
> +}
> +
> +static unsigned long clk_audio_pll_recalc_rate(struct clk_hw *hw,
> +					       unsigned long parent_rate)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +	unsigned long fout;
> +
> +	fout = clk_audio_pll_fout(parent_rate, fck->nd, fck->fracr);
> +
> +	pr_debug("A PLL: %s, fout = %lu (nd = %u, fracr = %lu)\n", __func__,
> +		 fout, fck->nd, (unsigned long)fck->fracr);
> +
> +	return fout;
> +}
> +
> +static int clk_audio_pll_compute_frac(unsigned long rate,
> +				      unsigned long parent_rate,
> +				      unsigned long *nd, unsigned long *fracr)
> +{
> +	unsigned long long tmp, rem;
> +
> +	if (!rate)
> +		return -EINVAL;
> +
> +	tmp = rate;
> +	rem = do_div(tmp, parent_rate);
> +	if (!tmp || tmp >= AUDIO_PLL_ND_MAX)
> +		return -EINVAL;
> +
> +	*nd = tmp - 1;
> +
> +	tmp = rem * AUDIO_PLL_DIV_FRAC;
> +	tmp = DIV_ROUND_CLOSEST_ULL(tmp, parent_rate);
> +	if (tmp > AT91_PMC_AUDIO_PLL_FRACR_MASK)
> +		return -EINVAL;
> +
> +	/* we can cast here as we verified the bounds just above */
> +	*fracr = (unsigned long)tmp;
> +
> +	return 0;
> +}
> +
> +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +				     unsigned long *parent_rate)
> +{
> +	long best_rate = -EINVAL;
> +	unsigned long fracr, nd;
> +	int ret;
> +
> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> +		 *parent_rate);
> +
> +	if (rate < AUDIO_PLL_FOUT_MIN)
> +		rate = AUDIO_PLL_FOUT_MIN;
> +	else if (rate > AUDIO_PLL_FOUT_MAX)
> +		rate = AUDIO_PLL_FOUT_MAX;
> +
> +	ret = clk_audio_pll_compute_frac(rate, *parent_rate, &nd, &fracr);
> +	if (ret)
> +		return ret;
> +
> +	best_rate = clk_audio_pll_fout(*parent_rate, nd, fracr);
> +
> +	pr_debug("A PLL: %s, best_rate = %lu (nd = %lu, fracr = %lu)\n",
> +		 __func__, best_rate, nd, fracr);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +				  unsigned long parent_rate)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +	unsigned long fracr, nd;
> +	int ret;
> +
> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> +		 parent_rate);
> +
> +	if (rate < AUDIO_PLL_FOUT_MIN || rate > AUDIO_PLL_FOUT_MAX)
> +		return -EINVAL;
> +
> +	ret = clk_audio_pll_compute_frac(rate, parent_rate, &nd, &fracr);
> +	if (ret)
> +		return ret;
> +
> +	fck->nd = nd;
> +	fck->fracr = fracr;
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_ops = {
> +	.enable = clk_audio_pll_enable,
> +	.disable = clk_audio_pll_disable,
> +	.recalc_rate = clk_audio_pll_recalc_rate,
> +	.round_rate = clk_audio_pll_round_rate,
> +	.set_rate = clk_audio_pll_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
> +{
> +	struct clk_audio_frac *fck;
> +	struct clk_init_data init;
> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);
> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	fck = kzalloc(sizeof(*fck), GFP_KERNEL);
> +	if (!fck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE;
> +
> +	fck->hw.init = &init;
> +	fck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &fck->hw);
> +	if (ret)
> +		kfree(fck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fck->hw);
> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_setup,
> +	       "atmel,sama5d2-clk-audio-pll-frac",
> +	       of_sama5d2_clk_audio_pll_setup);
> diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
> index 17f413bbbedf..6aca5ce8a99a 100644
> --- a/include/linux/clk/at91_pmc.h
> +++ b/include/linux/clk/at91_pmc.h
> @@ -185,4 +185,29 @@
>  #define		AT91_PMC_PCR_EN			(0x1  <<  28)				/* Enable */
>  #define		AT91_PMC_PCR_GCKEN		(0x1  <<  29)				/* GCK Enable */
>  
> +#define AT91_PMC_AUDIO_PLL0	0x14c
> +#define		AT91_PMC_AUDIO_PLL_PLLEN	(1  <<  0)
> +#define		AT91_PMC_AUDIO_PLL_PADEN	(1  <<  1)
> +#define		AT91_PMC_AUDIO_PLL_PMCEN	(1  <<  2)
> +#define		AT91_PMC_AUDIO_PLL_RESETN	(1  <<  3)
> +#define		AT91_PMC_AUDIO_PLL_ND_OFFSET	8
> +#define		AT91_PMC_AUDIO_PLL_ND_MASK	(0x7f << AT91_PMC_AUDIO_PLL_ND_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_ND(n)	((n)  << AT91_PMC_AUDIO_PLL_ND_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPMC_OFFSET	16
> +#define		AT91_PMC_AUDIO_PLL_QDPMC_MASK	(0x7f << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPMC(n)	((n)  << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
> +
> +#define AT91_PMC_AUDIO_PLL1	0x150
> +#define		AT91_PMC_AUDIO_PLL_FRACR_MASK		0x3fffff
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_OFFSET		24
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_MASK		(0x7f << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPAD(n)		((n)  << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET	AT91_PMC_AUDIO_PLL_QDPAD_OFFSET
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK	(0x3  << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV(n)		((n)  << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET	26
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX	0x1f
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK	(AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(n)	((n)  << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET)
> +
>  #endif

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-19  6:31     ` Boris Brezillon
  0 siblings, 0 replies; 49+ messages in thread
From: Boris Brezillon @ 2017-07-19  6:31 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 13 Jul 2017 09:49:21 +0200
Quentin Schulz <quentin.schulz@free-electrons.com> wrote:

> This new clock driver set allows to have a fractional divided clock that
> would generate a precise clock particularly suitable for audio
> applications.
> 
> The main audio pll clock has two children clocks: one that is connected
> to the PMC, the other that can directly drive a pad. As these two routes
> have different enable bits and different dividers and divider formulas,
> they are handled by two different drivers. Each of them could modify the
> rate of the main audio pll parent.
> 
> The main audio pll clock can output 620MHz to 700MHz.
> 
> Signed-off-by: Nicolas Ferre <nicolas.ferre@atmel.com>
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>

Acked-by: Boris Brezillon <boris.brezillon@free-electrons.com>

> ---
> 
> v2:
>   - split DT binding in a different patch,
>   - removed unused AUDIO_PLL_*FOUT* defines from clk-audio-pll-pmc,
>   - split classD modifications in a different patch,
> 
>  arch/arm/mach-at91/Kconfig           |   4 +
>  drivers/clk/at91/Makefile            |   2 +
>  drivers/clk/at91/clk-audio-pll-pad.c | 206 ++++++++++++++++++++++++++++++
>  drivers/clk/at91/clk-audio-pll-pmc.c | 174 +++++++++++++++++++++++++
>  drivers/clk/at91/clk-audio-pll.c     | 239 +++++++++++++++++++++++++++++++++++
>  include/linux/clk/at91_pmc.h         |  25 ++++
>  6 files changed, 650 insertions(+)
>  create mode 100644 drivers/clk/at91/clk-audio-pll-pad.c
>  create mode 100644 drivers/clk/at91/clk-audio-pll-pmc.c
>  create mode 100644 drivers/clk/at91/clk-audio-pll.c
> 
> diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
> index d735e5fc4772..9ae14d59a9ce 100644
> --- a/arch/arm/mach-at91/Kconfig
> +++ b/arch/arm/mach-at91/Kconfig
> @@ -26,6 +26,7 @@ config SOC_SAMA5D2
>  	select HAVE_AT91_USB_CLK
>  	select HAVE_AT91_H32MX
>  	select HAVE_AT91_GENERATED_CLK
> +	select HAVE_AT91_AUDIO_PLL
>  	select PINCTRL_AT91PIO4
>  	help
>  	  Select this if ou are using one of Atmel's SAMA5D2 family SoC.
> @@ -125,6 +126,9 @@ config HAVE_AT91_H32MX
>  config HAVE_AT91_GENERATED_CLK
>  	bool
>  
> +config HAVE_AT91_AUDIO_PLL
> +	bool
> +
>  config SOC_SAM_V4_V5
>  	bool
>  
> diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
> index 13e67bd35cff..c9353d17763a 100644
> --- a/drivers/clk/at91/Makefile
> +++ b/drivers/clk/at91/Makefile
> @@ -6,6 +6,8 @@ obj-y += pmc.o sckc.o
>  obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o
>  obj-y += clk-system.o clk-peripheral.o clk-programmable.o
>  
> +obj-$(CONFIG_HAVE_AT91_AUDIO_PLL)	+= clk-audio-pll.o
> +obj-$(CONFIG_HAVE_AT91_AUDIO_PLL)	+= clk-audio-pll-pmc.o clk-audio-pll-pad.o
>  obj-$(CONFIG_HAVE_AT91_UTMI)		+= clk-utmi.o
>  obj-$(CONFIG_HAVE_AT91_USB_CLK)		+= clk-usb.o
>  obj-$(CONFIG_HAVE_AT91_SMD)		+= clk-smd.o
> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
> new file mode 100644
> index 000000000000..10dd6d625696
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
> @@ -0,0 +1,206 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: PAD output for fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * enable - clk_enable writes divisors and enables PAD output
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate / (qdaudio * div))
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_QDPAD(qd, div)	((AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(qd) & \
> +					  AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK) | \
> +					 (AT91_PMC_AUDIO_PLL_QDPAD_DIV(div) & \
> +					  AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK))
> +
> +struct clk_audio_pad {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u8 qdaudio;
> +	u8 div;
> +};
> +
> +#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw)
> +
> +static int clk_audio_pll_pad_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1,
> +			   AT91_PMC_AUDIO_PLL_QDPAD_MASK,
> +			   AUDIO_PLL_QDPAD(apad_ck->qdaudio, apad_ck->div));
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PADEN, AT91_PMC_AUDIO_PLL_PADEN);
> +
> +	return 0;
> +}
> +
> +static void clk_audio_pll_pad_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PADEN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw,
> +						   unsigned long parent_rate)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +	unsigned long apad_rate = 0;
> +
> +	if (apad_ck->qdaudio && apad_ck->div)
> +		apad_rate = parent_rate / (apad_ck->qdaudio * apad_ck->div);
> +
> +	pr_debug("A PLL/PAD: %s, apad_rate = %lu (div = %u, qdaudio = %u)\n",
> +		 __func__, apad_rate, apad_ck->div, apad_ck->qdaudio);
> +
> +	return apad_rate;
> +}
> +
> +static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
> +					 unsigned long *parent_rate)
> +{
> +	struct clk_hw *pclk = clk_hw_get_parent(hw);
> +	long best_rate = -EINVAL;
> +	unsigned long best_parent_rate;
> +	unsigned long tmp_qd;
> +	u32 div;
> +	long tmp_rate;
> +	int tmp_diff;
> +	int best_diff = -1;
> +
> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, *parent_rate);
> +
> +	/*
> +	 * Rate divisor is actually made of two different divisors, multiplied
> +	 * between themselves before dividing the rate.
> +	 * tmp_qd goes from 1 to 31 and div is either 2 or 3.
> +	 * In order to avoid testing twice the rate divisor (e.g. divisor 12 can
> +	 * be found with (tmp_qd, div) = (2, 6) or (3, 4)), we remove any loop
> +	 * for a rate divisor when div is 2 and tmp_qd is a multiple of 3.
> +	 * We cannot inverse it (condition div is 3 and tmp_qd is even) or we
> +	 * would miss some rate divisor that aren't reachable with div being 2
> +	 * (e.g. rate divisor 90 is made with div = 3 and tmp_qd = 30, thus
> +	 * tmp_qd is even so we skip it because we think div 2 could make this
> +	 * rate divisor which isn't possible since tmp_qd has to be <= 31).
> +	 */
> +	for (tmp_qd = 1; tmp_qd < AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX; tmp_qd++)
> +		for (div = 2; div <= 3; div++) {
> +			if (div == 2 && tmp_qd % 3 == 0)
> +				continue;
> +
> +			best_parent_rate = clk_hw_round_rate(pclk,
> +							rate * tmp_qd * div);
> +			tmp_rate = best_parent_rate / (div * tmp_qd);
> +			tmp_diff = abs(rate - tmp_rate);
> +
> +			if (best_diff < 0 || best_diff > tmp_diff) {
> +				*parent_rate = best_parent_rate;
> +				best_rate = tmp_rate;
> +				best_diff = tmp_diff;
> +			}
> +		}
> +
> +	pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n",
> +		 __func__, best_rate, best_parent_rate);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long parent_rate)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +	u8 tmp_div;
> +
> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, parent_rate);
> +
> +	if (!rate)
> +		return -EINVAL;
> +
> +	tmp_div = parent_rate / rate;
> +	if (tmp_div % 3 == 0) {
> +		apad_ck->qdaudio = tmp_div / 3;
> +		apad_ck->div = 3;
> +	} else {
> +		apad_ck->qdaudio = tmp_div / 2;
> +		apad_ck->div = 2;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_pad_ops = {
> +	.enable = clk_audio_pll_pad_enable,
> +	.disable = clk_audio_pll_pad_disable,
> +	.recalc_rate = clk_audio_pll_pad_recalc_rate,
> +	.round_rate = clk_audio_pll_pad_round_rate,
> +	.set_rate = clk_audio_pll_pad_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
> +{
> +	struct clk_audio_pad *apad_ck;
> +	struct clk_init_data init;
> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);
> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
> +	if (!apad_ck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_pad_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> +		CLK_SET_RATE_PARENT;
> +
> +	apad_ck->hw.init = &init;
> +	apad_ck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &apad_ck->hw);
> +	if (ret)
> +		kfree(apad_ck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
> +	       "atmel,sama5d2-clk-audio-pll-pad",
> +	       of_sama5d2_clk_audio_pll_pad_setup);
> diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c
> new file mode 100644
> index 000000000000..7b0847ed7a4b
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll-pmc.c
> @@ -0,0 +1,174 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: PMC output for fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * enable - clk_enable writes qdpmc, and enables PMC output
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate / (qdpmc + 1)
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_QDPMC_MAX		(AT91_PMC_AUDIO_PLL_QDPMC_MASK >> \
> +						AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
> +struct clk_audio_pmc {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u8 qdpmc;
> +};
> +
> +#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw)
> +
> +static int clk_audio_pll_pmc_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PMCEN |
> +			   AT91_PMC_AUDIO_PLL_QDPMC_MASK,
> +			   AT91_PMC_AUDIO_PLL_PMCEN |
> +			   AT91_PMC_AUDIO_PLL_QDPMC(apmc_ck->qdpmc));
> +	return 0;
> +}
> +
> +static void clk_audio_pll_pmc_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PMCEN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw,
> +						   unsigned long parent_rate)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +	unsigned long apmc_rate = 0;
> +
> +	apmc_rate = parent_rate / (apmc_ck->qdpmc + 1);
> +
> +	pr_debug("A PLL/PMC: %s, apmc_rate = %lu (qdpmc = %u)\n", __func__,
> +		 apmc_rate, apmc_ck->qdpmc);
> +
> +	return apmc_rate;
> +}
> +
> +static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
> +					 unsigned long *parent_rate)
> +{
> +	struct clk_hw *pclk = clk_hw_get_parent(hw);
> +	long best_rate = -EINVAL;
> +	unsigned long best_parent_rate = 0;
> +	u32 tmp_qd = 0, div;
> +	long tmp_rate;
> +	int tmp_diff;
> +	int best_diff = -1;
> +
> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, *parent_rate);
> +
> +	for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) {
> +		best_parent_rate = clk_round_rate(pclk->clk, rate * div);
> +		tmp_rate = best_parent_rate / div;
> +		tmp_diff = abs(rate - tmp_rate);
> +
> +		if (best_diff < 0 || best_diff > tmp_diff) {
> +			*parent_rate = best_parent_rate;
> +			best_rate = tmp_rate;
> +			best_diff = tmp_diff;
> +			tmp_qd = div;
> +		}
> +	}
> +
> +	pr_debug("A PLL/PMC: %s, best_rate = %ld, best_parent_rate = %lu (qd = %d)\n",
> +		 __func__, best_rate, *parent_rate, tmp_qd - 1);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long parent_rate)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	if (!rate)
> +		return -EINVAL;
> +
> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, parent_rate);
> +
> +	apmc_ck->qdpmc = parent_rate / rate - 1;
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_pmc_ops = {
> +	.enable = clk_audio_pll_pmc_enable,
> +	.disable = clk_audio_pll_pmc_disable,
> +	.recalc_rate = clk_audio_pll_pmc_recalc_rate,
> +	.round_rate = clk_audio_pll_pmc_round_rate,
> +	.set_rate = clk_audio_pll_pmc_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
> +{
> +	struct clk_audio_pmc *apmc_ck;
> +	struct clk_init_data init;
> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);
> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
> +	if (!apmc_ck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_pmc_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> +		CLK_SET_RATE_PARENT;
> +
> +	apmc_ck->hw.init = &init;
> +	apmc_ck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &apmc_ck->hw);
> +	if (ret)
> +		kfree(apmc_ck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw);
> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
> +	       "atmel,sama5d2-clk-audio-pll-pmc",
> +	       of_sama5d2_clk_audio_pll_pmc_setup);
> diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
> new file mode 100644
> index 000000000000..efc2cb58da85
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll.c
> @@ -0,0 +1,239 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *		       Songjun Wu <songjun.wu@atmel.com>,
> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>
> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: Fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare puts audio PLL in reset state
> + * enable - clk_enable writes nd, fracr parameters and enables PLL
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate * ((nd + 1) + (fracr / 2^22))
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_DIV_FRAC	BIT(22)
> +#define AUDIO_PLL_ND_MAX	(AT91_PMC_AUDIO_PLL_ND_MASK >> \
> +					AT91_PMC_AUDIO_PLL_ND_OFFSET)
> +
> +#define AUDIO_PLL_FOUT_MIN	620000000
> +#define AUDIO_PLL_FOUT_MAX	700000000
> +
> +struct clk_audio_frac {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u32 fracr;
> +	u8 nd;
> +};
> +
> +#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw)
> +
> +static int clk_audio_pll_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN, 0);
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN,
> +			   AT91_PMC_AUDIO_PLL_RESETN);
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL1,
> +			   AT91_PMC_AUDIO_PLL_FRACR_MASK, fck->fracr);
> +
> +	/*
> +	 * reset and enable have to be done in 2 separated writes
> +	 * for AT91_PMC_AUDIO_PLL0
> +	 */
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PLLEN |
> +			   AT91_PMC_AUDIO_PLL_ND_MASK,
> +			   AT91_PMC_AUDIO_PLL_PLLEN |
> +			   AT91_PMC_AUDIO_PLL_ND(fck->nd));
> +
> +	return 0;
> +}
> +
> +static void clk_audio_pll_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PLLEN, 0);
> +	/* do it in 2 separated writes */
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
> +					unsigned long nd, unsigned long fracr)
> +{
> +	unsigned long long fr = (unsigned long long)parent_rate *
> +						(unsigned long long)fracr;
> +
> +	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
> +
> +	fr = DIV_ROUND_CLOSEST_ULL(fr, AUDIO_PLL_DIV_FRAC);
> +
> +	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
> +
> +	return parent_rate * (nd + 1) + fr;
> +}
> +
> +static unsigned long clk_audio_pll_recalc_rate(struct clk_hw *hw,
> +					       unsigned long parent_rate)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +	unsigned long fout;
> +
> +	fout = clk_audio_pll_fout(parent_rate, fck->nd, fck->fracr);
> +
> +	pr_debug("A PLL: %s, fout = %lu (nd = %u, fracr = %lu)\n", __func__,
> +		 fout, fck->nd, (unsigned long)fck->fracr);
> +
> +	return fout;
> +}
> +
> +static int clk_audio_pll_compute_frac(unsigned long rate,
> +				      unsigned long parent_rate,
> +				      unsigned long *nd, unsigned long *fracr)
> +{
> +	unsigned long long tmp, rem;
> +
> +	if (!rate)
> +		return -EINVAL;
> +
> +	tmp = rate;
> +	rem = do_div(tmp, parent_rate);
> +	if (!tmp || tmp >= AUDIO_PLL_ND_MAX)
> +		return -EINVAL;
> +
> +	*nd = tmp - 1;
> +
> +	tmp = rem * AUDIO_PLL_DIV_FRAC;
> +	tmp = DIV_ROUND_CLOSEST_ULL(tmp, parent_rate);
> +	if (tmp > AT91_PMC_AUDIO_PLL_FRACR_MASK)
> +		return -EINVAL;
> +
> +	/* we can cast here as we verified the bounds just above */
> +	*fracr = (unsigned long)tmp;
> +
> +	return 0;
> +}
> +
> +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +				     unsigned long *parent_rate)
> +{
> +	long best_rate = -EINVAL;
> +	unsigned long fracr, nd;
> +	int ret;
> +
> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> +		 *parent_rate);
> +
> +	if (rate < AUDIO_PLL_FOUT_MIN)
> +		rate = AUDIO_PLL_FOUT_MIN;
> +	else if (rate > AUDIO_PLL_FOUT_MAX)
> +		rate = AUDIO_PLL_FOUT_MAX;
> +
> +	ret = clk_audio_pll_compute_frac(rate, *parent_rate, &nd, &fracr);
> +	if (ret)
> +		return ret;
> +
> +	best_rate = clk_audio_pll_fout(*parent_rate, nd, fracr);
> +
> +	pr_debug("A PLL: %s, best_rate = %lu (nd = %lu, fracr = %lu)\n",
> +		 __func__, best_rate, nd, fracr);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +				  unsigned long parent_rate)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +	unsigned long fracr, nd;
> +	int ret;
> +
> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> +		 parent_rate);
> +
> +	if (rate < AUDIO_PLL_FOUT_MIN || rate > AUDIO_PLL_FOUT_MAX)
> +		return -EINVAL;
> +
> +	ret = clk_audio_pll_compute_frac(rate, parent_rate, &nd, &fracr);
> +	if (ret)
> +		return ret;
> +
> +	fck->nd = nd;
> +	fck->fracr = fracr;
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_ops = {
> +	.enable = clk_audio_pll_enable,
> +	.disable = clk_audio_pll_disable,
> +	.recalc_rate = clk_audio_pll_recalc_rate,
> +	.round_rate = clk_audio_pll_round_rate,
> +	.set_rate = clk_audio_pll_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
> +{
> +	struct clk_audio_frac *fck;
> +	struct clk_init_data init;
> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);
> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	fck = kzalloc(sizeof(*fck), GFP_KERNEL);
> +	if (!fck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE;
> +
> +	fck->hw.init = &init;
> +	fck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &fck->hw);
> +	if (ret)
> +		kfree(fck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fck->hw);
> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_setup,
> +	       "atmel,sama5d2-clk-audio-pll-frac",
> +	       of_sama5d2_clk_audio_pll_setup);
> diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
> index 17f413bbbedf..6aca5ce8a99a 100644
> --- a/include/linux/clk/at91_pmc.h
> +++ b/include/linux/clk/at91_pmc.h
> @@ -185,4 +185,29 @@
>  #define		AT91_PMC_PCR_EN			(0x1  <<  28)				/* Enable */
>  #define		AT91_PMC_PCR_GCKEN		(0x1  <<  29)				/* GCK Enable */
>  
> +#define AT91_PMC_AUDIO_PLL0	0x14c
> +#define		AT91_PMC_AUDIO_PLL_PLLEN	(1  <<  0)
> +#define		AT91_PMC_AUDIO_PLL_PADEN	(1  <<  1)
> +#define		AT91_PMC_AUDIO_PLL_PMCEN	(1  <<  2)
> +#define		AT91_PMC_AUDIO_PLL_RESETN	(1  <<  3)
> +#define		AT91_PMC_AUDIO_PLL_ND_OFFSET	8
> +#define		AT91_PMC_AUDIO_PLL_ND_MASK	(0x7f << AT91_PMC_AUDIO_PLL_ND_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_ND(n)	((n)  << AT91_PMC_AUDIO_PLL_ND_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPMC_OFFSET	16
> +#define		AT91_PMC_AUDIO_PLL_QDPMC_MASK	(0x7f << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPMC(n)	((n)  << AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
> +
> +#define AT91_PMC_AUDIO_PLL1	0x150
> +#define		AT91_PMC_AUDIO_PLL_FRACR_MASK		0x3fffff
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_OFFSET		24
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_MASK		(0x7f << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPAD(n)		((n)  << AT91_PMC_AUDIO_PLL_QDPAD_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET	AT91_PMC_AUDIO_PLL_QDPAD_OFFSET
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK	(0x3  << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_DIV(n)		((n)  << AT91_PMC_AUDIO_PLL_QDPAD_DIV_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET	26
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX	0x1f
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK	(AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET)
> +#define		AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(n)	((n)  << AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_OFFSET)
> +
>  #endif

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 0/9] add support for Sama5d2 audio PLLs and enable ClassD
  2017-07-19  6:28   ` Quentin Schulz
@ 2017-07-19 10:19     ` Mark Brown
  -1 siblings, 0 replies; 49+ messages in thread
From: Mark Brown @ 2017-07-19 10:19 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: mturquette, sboyd, robh+dt, mark.rutland, lgirdwood,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai, cyrille.pitchen, thomas.petazzoni, linux-clk, devicetree,
	linux-kernel, alsa-devel, linux-arm-kernel

[-- Attachment #1: Type: text/plain, Size: 1127 bytes --]

On Wed, Jul 19, 2017 at 08:28:17AM +0200, Quentin Schulz wrote:
> Hi all,
> 
> It's been almost a week with no comments on this patch set, so kindly
> pinging.

Please don't top post, reply in line with needed context.  This allows
readers to readily follow the flow of conversation and understand what
you are talking about and also helps ensure that everything in the
discussion is being addressed.

Please don't send content free pings and please allow a reasonable time
for review.  People get busy, go on holiday, attend conferences and so 
on so unless there is some reason for urgency (like critical bug fixes)
please allow at least a couple of weeks for review.  If there have been
review comments then people may be waiting for those to be addressed.

Sending content free pings adds to the mail volume (if they are seen at
all) which is often the problem and since they can't be reviewed
directly if something has gone wrong you'll have to resend the patches
anyway, though there are some other maintainers who like them - if in
doubt look at how patches for the subsystem are normally handled.

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 0/9] add support for Sama5d2 audio PLLs and enable ClassD
@ 2017-07-19 10:19     ` Mark Brown
  0 siblings, 0 replies; 49+ messages in thread
From: Mark Brown @ 2017-07-19 10:19 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Jul 19, 2017 at 08:28:17AM +0200, Quentin Schulz wrote:
> Hi all,
> 
> It's been almost a week with no comments on this patch set, so kindly
> pinging.

Please don't top post, reply in line with needed context.  This allows
readers to readily follow the flow of conversation and understand what
you are talking about and also helps ensure that everything in the
discussion is being addressed.

Please don't send content free pings and please allow a reasonable time
for review.  People get busy, go on holiday, attend conferences and so 
on so unless there is some reason for urgency (like critical bug fixes)
please allow at least a couple of weeks for review.  If there have been
review comments then people may be waiting for those to be addressed.

Sending content free pings adds to the mail volume (if they are seen at
all) which is often the problem and since they can't be reviewed
directly if something has gone wrong you'll have to resend the patches
anyway, though there are some other maintainers who like them - if in
doubt look at how patches for the subsystem are normally handled.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 488 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20170719/c0ad2bdc/attachment.sig>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-21 22:20     ` Stephen Boyd
  0 siblings, 0 replies; 49+ messages in thread
From: Stephen Boyd @ 2017-07-21 22:20 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: mturquette, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai, cyrille.pitchen, thomas.petazzoni, linux-clk, devicetree,
	linux-kernel, alsa-devel, linux-arm-kernel, Nicolas Ferre

On 07/13, Quentin Schulz wrote:
> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
> new file mode 100644
> index 000000000000..10dd6d625696
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
> @@ -0,0 +1,206 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>

Used?

> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: PAD output for fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * enable - clk_enable writes divisors and enables PAD output
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate / (qdaudio * div))
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_QDPAD(qd, div)	((AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(qd) & \
> +					  AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK) | \
> +					 (AT91_PMC_AUDIO_PLL_QDPAD_DIV(div) & \
> +					  AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK))
> +
> +struct clk_audio_pad {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u8 qdaudio;
> +	u8 div;
> +};
> +
> +#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw)
> +
> +static int clk_audio_pll_pad_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1,
> +			   AT91_PMC_AUDIO_PLL_QDPAD_MASK,
> +			   AUDIO_PLL_QDPAD(apad_ck->qdaudio, apad_ck->div));
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PADEN, AT91_PMC_AUDIO_PLL_PADEN);
> +
> +	return 0;
> +}
> +
> +static void clk_audio_pll_pad_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PADEN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw,
> +						   unsigned long parent_rate)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +	unsigned long apad_rate = 0;
> +
> +	if (apad_ck->qdaudio && apad_ck->div)
> +		apad_rate = parent_rate / (apad_ck->qdaudio * apad_ck->div);
> +
> +	pr_debug("A PLL/PAD: %s, apad_rate = %lu (div = %u, qdaudio = %u)\n",
> +		 __func__, apad_rate, apad_ck->div, apad_ck->qdaudio);
> +
> +	return apad_rate;
> +}
> +
> +static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
> +					 unsigned long *parent_rate)
> +{
> +	struct clk_hw *pclk = clk_hw_get_parent(hw);
> +	long best_rate = -EINVAL;
> +	unsigned long best_parent_rate;
> +	unsigned long tmp_qd;
> +	u32 div;
> +	long tmp_rate;
> +	int tmp_diff;
> +	int best_diff = -1;
> +
> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, *parent_rate);
> +
> +	/*
> +	 * Rate divisor is actually made of two different divisors, multiplied
> +	 * between themselves before dividing the rate.
> +	 * tmp_qd goes from 1 to 31 and div is either 2 or 3.
> +	 * In order to avoid testing twice the rate divisor (e.g. divisor 12 can
> +	 * be found with (tmp_qd, div) = (2, 6) or (3, 4)), we remove any loop
> +	 * for a rate divisor when div is 2 and tmp_qd is a multiple of 3.
> +	 * We cannot inverse it (condition div is 3 and tmp_qd is even) or we
> +	 * would miss some rate divisor that aren't reachable with div being 2
> +	 * (e.g. rate divisor 90 is made with div = 3 and tmp_qd = 30, thus
> +	 * tmp_qd is even so we skip it because we think div 2 could make this
> +	 * rate divisor which isn't possible since tmp_qd has to be <= 31).
> +	 */
> +	for (tmp_qd = 1; tmp_qd < AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX; tmp_qd++)
> +		for (div = 2; div <= 3; div++) {
> +			if (div == 2 && tmp_qd % 3 == 0)
> +				continue;
> +
> +			best_parent_rate = clk_hw_round_rate(pclk,
> +							rate * tmp_qd * div);
> +			tmp_rate = best_parent_rate / (div * tmp_qd);
> +			tmp_diff = abs(rate - tmp_rate);
> +
> +			if (best_diff < 0 || best_diff > tmp_diff) {
> +				*parent_rate = best_parent_rate;
> +				best_rate = tmp_rate;
> +				best_diff = tmp_diff;
> +			}
> +		}
> +
> +	pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n",
> +		 __func__, best_rate, best_parent_rate);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long parent_rate)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +	u8 tmp_div;
> +
> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, parent_rate);
> +
> +	if (!rate)
> +		return -EINVAL;

This happens?

> +
> +	tmp_div = parent_rate / rate;
> +	if (tmp_div % 3 == 0) {
> +		apad_ck->qdaudio = tmp_div / 3;
> +		apad_ck->div = 3;
> +	} else {
> +		apad_ck->qdaudio = tmp_div / 2;
> +		apad_ck->div = 2;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_pad_ops = {
> +	.enable = clk_audio_pll_pad_enable,
> +	.disable = clk_audio_pll_pad_disable,
> +	.recalc_rate = clk_audio_pll_pad_recalc_rate,
> +	.round_rate = clk_audio_pll_pad_round_rate,
> +	.set_rate = clk_audio_pll_pad_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
> +{
> +	struct clk_audio_pad *apad_ck;
> +	struct clk_init_data init;

Best to initialize to { } just in case we add something later.

> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);
> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
> +	if (!apad_ck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_pad_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);

Use of_clk_parent_fill()?

> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> +		CLK_SET_RATE_PARENT;
> +
> +	apad_ck->hw.init = &init;
> +	apad_ck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &apad_ck->hw);
> +	if (ret)
> +		kfree(apad_ck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);

Maybe we should make this register sequence a helper function.
Seems common.

> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
> +	       "atmel,sama5d2-clk-audio-pll-pad",
> +	       of_sama5d2_clk_audio_pll_pad_setup);

We can't have a device driver for this?

> diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c
> new file mode 100644
> index 000000000000..7b0847ed7a4b
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll-pmc.c
> @@ -0,0 +1,174 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>

Used?

> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: PMC output for fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * enable - clk_enable writes qdpmc, and enables PMC output
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate / (qdpmc + 1)
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_QDPMC_MAX		(AT91_PMC_AUDIO_PLL_QDPMC_MASK >> \
> +						AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
> +struct clk_audio_pmc {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u8 qdpmc;
> +};
> +
> +#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw)
> +
> +static int clk_audio_pll_pmc_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PMCEN |
> +			   AT91_PMC_AUDIO_PLL_QDPMC_MASK,
> +			   AT91_PMC_AUDIO_PLL_PMCEN |
> +			   AT91_PMC_AUDIO_PLL_QDPMC(apmc_ck->qdpmc));
> +	return 0;
> +}
> +
> +static void clk_audio_pll_pmc_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PMCEN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw,
> +						   unsigned long parent_rate)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +	unsigned long apmc_rate = 0;
> +
> +	apmc_rate = parent_rate / (apmc_ck->qdpmc + 1);
> +
> +	pr_debug("A PLL/PMC: %s, apmc_rate = %lu (qdpmc = %u)\n", __func__,
> +		 apmc_rate, apmc_ck->qdpmc);
> +
> +	return apmc_rate;
> +}
> +
> +static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
> +					 unsigned long *parent_rate)
> +{
> +	struct clk_hw *pclk = clk_hw_get_parent(hw);
> +	long best_rate = -EINVAL;
> +	unsigned long best_parent_rate = 0;
> +	u32 tmp_qd = 0, div;
> +	long tmp_rate;
> +	int tmp_diff;
> +	int best_diff = -1;
> +
> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, *parent_rate);
> +
> +	for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) {
> +		best_parent_rate = clk_round_rate(pclk->clk, rate * div);
> +		tmp_rate = best_parent_rate / div;
> +		tmp_diff = abs(rate - tmp_rate);
> +
> +		if (best_diff < 0 || best_diff > tmp_diff) {
> +			*parent_rate = best_parent_rate;
> +			best_rate = tmp_rate;
> +			best_diff = tmp_diff;
> +			tmp_qd = div;
> +		}
> +	}
> +
> +	pr_debug("A PLL/PMC: %s, best_rate = %ld, best_parent_rate = %lu (qd = %d)\n",
> +		 __func__, best_rate, *parent_rate, tmp_qd - 1);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long parent_rate)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	if (!rate)
> +		return -EINVAL;
> +
> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, parent_rate);
> +
> +	apmc_ck->qdpmc = parent_rate / rate - 1;

Hopefully rate isn't 1 or that goes undefined.

> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_pmc_ops = {
> +	.enable = clk_audio_pll_pmc_enable,
> +	.disable = clk_audio_pll_pmc_disable,
> +	.recalc_rate = clk_audio_pll_pmc_recalc_rate,
> +	.round_rate = clk_audio_pll_pmc_round_rate,
> +	.set_rate = clk_audio_pll_pmc_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
> +{
> +	struct clk_audio_pmc *apmc_ck;
> +	struct clk_init_data init;
> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);
> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
> +	if (!apmc_ck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_pmc_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);

This feels repetitive.

> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> +		CLK_SET_RATE_PARENT;
> +
> +	apmc_ck->hw.init = &init;
> +	apmc_ck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &apmc_ck->hw);
> +	if (ret)
> +		kfree(apmc_ck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw);
> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
> +	       "atmel,sama5d2-clk-audio-pll-pmc",
> +	       of_sama5d2_clk_audio_pll_pmc_setup);

Very

> diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
> new file mode 100644
> index 000000000000..efc2cb58da85
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll.c
> @@ -0,0 +1,239 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *		       Songjun Wu <songjun.wu@atmel.com>,
> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>

Used?

> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: Fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare puts audio PLL in reset state
> + * enable - clk_enable writes nd, fracr parameters and enables PLL
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate * ((nd + 1) + (fracr / 2^22))
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_DIV_FRAC	BIT(22)
> +#define AUDIO_PLL_ND_MAX	(AT91_PMC_AUDIO_PLL_ND_MASK >> \
> +					AT91_PMC_AUDIO_PLL_ND_OFFSET)
> +
> +#define AUDIO_PLL_FOUT_MIN	620000000
> +#define AUDIO_PLL_FOUT_MAX	700000000
> +
> +struct clk_audio_frac {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u32 fracr;
> +	u8 nd;
> +};
> +
> +#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw)
> +
> +static int clk_audio_pll_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN, 0);
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN,
> +			   AT91_PMC_AUDIO_PLL_RESETN);
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL1,
> +			   AT91_PMC_AUDIO_PLL_FRACR_MASK, fck->fracr);
> +
> +	/*
> +	 * reset and enable have to be done in 2 separated writes
> +	 * for AT91_PMC_AUDIO_PLL0
> +	 */
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PLLEN |
> +			   AT91_PMC_AUDIO_PLL_ND_MASK,
> +			   AT91_PMC_AUDIO_PLL_PLLEN |
> +			   AT91_PMC_AUDIO_PLL_ND(fck->nd));
> +
> +	return 0;
> +}
> +
> +static void clk_audio_pll_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PLLEN, 0);
> +	/* do it in 2 separated writes */
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
> +					unsigned long nd, unsigned long fracr)
> +{
> +	unsigned long long fr = (unsigned long long)parent_rate *
> +						(unsigned long long)fracr;

We only need one cast here?

> +
> +	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
> +
> +	fr = DIV_ROUND_CLOSEST_ULL(fr, AUDIO_PLL_DIV_FRAC);
> +
> +	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
> +
> +	return parent_rate * (nd + 1) + fr;
> +}
> +
> +static unsigned long clk_audio_pll_recalc_rate(struct clk_hw *hw,
> +					       unsigned long parent_rate)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +	unsigned long fout;
> +
> +	fout = clk_audio_pll_fout(parent_rate, fck->nd, fck->fracr);
> +
> +	pr_debug("A PLL: %s, fout = %lu (nd = %u, fracr = %lu)\n", __func__,
> +		 fout, fck->nd, (unsigned long)fck->fracr);
> +
> +	return fout;
> +}
> +
> +static int clk_audio_pll_compute_frac(unsigned long rate,
> +				      unsigned long parent_rate,
> +				      unsigned long *nd, unsigned long *fracr)
> +{
> +	unsigned long long tmp, rem;
> +
> +	if (!rate)
> +		return -EINVAL;

This happens?

> +
> +	tmp = rate;
> +	rem = do_div(tmp, parent_rate);
> +	if (!tmp || tmp >= AUDIO_PLL_ND_MAX)
> +		return -EINVAL;
> +
> +	*nd = tmp - 1;
> +
> +	tmp = rem * AUDIO_PLL_DIV_FRAC;
> +	tmp = DIV_ROUND_CLOSEST_ULL(tmp, parent_rate);
> +	if (tmp > AT91_PMC_AUDIO_PLL_FRACR_MASK)
> +		return -EINVAL;
> +
> +	/* we can cast here as we verified the bounds just above */
> +	*fracr = (unsigned long)tmp;
> +
> +	return 0;
> +}
> +
> +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +				     unsigned long *parent_rate)
> +{
> +	long best_rate = -EINVAL;
> +	unsigned long fracr, nd;
> +	int ret;
> +
> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> +		 *parent_rate);
> +
> +	if (rate < AUDIO_PLL_FOUT_MIN)
> +		rate = AUDIO_PLL_FOUT_MIN;
> +	else if (rate > AUDIO_PLL_FOUT_MAX)
> +		rate = AUDIO_PLL_FOUT_MAX;

Use clamp. Also, did you want to use determine_rate callback and
clamp the requested rate range?

> +
> +	ret = clk_audio_pll_compute_frac(rate, *parent_rate, &nd, &fracr);
> +	if (ret)
> +		return ret;
> +
> +	best_rate = clk_audio_pll_fout(*parent_rate, nd, fracr);
> +
> +	pr_debug("A PLL: %s, best_rate = %lu (nd = %lu, fracr = %lu)\n",
> +		 __func__, best_rate, nd, fracr);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +				  unsigned long parent_rate)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +	unsigned long fracr, nd;
> +	int ret;
> +
> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> +		 parent_rate);
> +
> +	if (rate < AUDIO_PLL_FOUT_MIN || rate > AUDIO_PLL_FOUT_MAX)
> +		return -EINVAL;
> +
> +	ret = clk_audio_pll_compute_frac(rate, parent_rate, &nd, &fracr);
> +	if (ret)
> +		return ret;
> +
> +	fck->nd = nd;
> +	fck->fracr = fracr;
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_ops = {
> +	.enable = clk_audio_pll_enable,
> +	.disable = clk_audio_pll_disable,
> +	.recalc_rate = clk_audio_pll_recalc_rate,
> +	.round_rate = clk_audio_pll_round_rate,
> +	.set_rate = clk_audio_pll_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
> +{
> +	struct clk_audio_frac *fck;
> +	struct clk_init_data init;
> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);

Any way to not rely on clock-output-names?

> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	fck = kzalloc(sizeof(*fck), GFP_KERNEL);

This variable name looks like f*ck, perhaps name it something
else. frac?

> +	if (!fck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE;
> +
> +	fck->hw.init = &init;
> +	fck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &fck->hw);
> +	if (ret)
> +		kfree(fck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fck->hw);
> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_setup,
> +	       "atmel,sama5d2-clk-audio-pll-frac",
> +	       of_sama5d2_clk_audio_pll_setup);

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-21 22:20     ` Stephen Boyd
  0 siblings, 0 replies; 49+ messages in thread
From: Stephen Boyd @ 2017-07-21 22:20 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: mturquette-rdvid1DuHRBWk0Htik3J/w,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A,
	nicolas.ferre-UWL1GkI3JZL3oGB3hsPCZA,
	alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	perex-/Fr2/VpizcU, tiwai-IBi9RG/b67k,
	cyrille.pitchen-yU5RGvR974pGWvitb5QawA,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Nicolas Ferre

On 07/13, Quentin Schulz wrote:
> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
> new file mode 100644
> index 000000000000..10dd6d625696
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
> @@ -0,0 +1,206 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *                     Nicolas Ferre <nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>

Used?

> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: PAD output for fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * enable - clk_enable writes divisors and enables PAD output
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate / (qdaudio * div))
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_QDPAD(qd, div)	((AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(qd) & \
> +					  AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK) | \
> +					 (AT91_PMC_AUDIO_PLL_QDPAD_DIV(div) & \
> +					  AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK))
> +
> +struct clk_audio_pad {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u8 qdaudio;
> +	u8 div;
> +};
> +
> +#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw)
> +
> +static int clk_audio_pll_pad_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1,
> +			   AT91_PMC_AUDIO_PLL_QDPAD_MASK,
> +			   AUDIO_PLL_QDPAD(apad_ck->qdaudio, apad_ck->div));
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PADEN, AT91_PMC_AUDIO_PLL_PADEN);
> +
> +	return 0;
> +}
> +
> +static void clk_audio_pll_pad_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PADEN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw,
> +						   unsigned long parent_rate)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +	unsigned long apad_rate = 0;
> +
> +	if (apad_ck->qdaudio && apad_ck->div)
> +		apad_rate = parent_rate / (apad_ck->qdaudio * apad_ck->div);
> +
> +	pr_debug("A PLL/PAD: %s, apad_rate = %lu (div = %u, qdaudio = %u)\n",
> +		 __func__, apad_rate, apad_ck->div, apad_ck->qdaudio);
> +
> +	return apad_rate;
> +}
> +
> +static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
> +					 unsigned long *parent_rate)
> +{
> +	struct clk_hw *pclk = clk_hw_get_parent(hw);
> +	long best_rate = -EINVAL;
> +	unsigned long best_parent_rate;
> +	unsigned long tmp_qd;
> +	u32 div;
> +	long tmp_rate;
> +	int tmp_diff;
> +	int best_diff = -1;
> +
> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, *parent_rate);
> +
> +	/*
> +	 * Rate divisor is actually made of two different divisors, multiplied
> +	 * between themselves before dividing the rate.
> +	 * tmp_qd goes from 1 to 31 and div is either 2 or 3.
> +	 * In order to avoid testing twice the rate divisor (e.g. divisor 12 can
> +	 * be found with (tmp_qd, div) = (2, 6) or (3, 4)), we remove any loop
> +	 * for a rate divisor when div is 2 and tmp_qd is a multiple of 3.
> +	 * We cannot inverse it (condition div is 3 and tmp_qd is even) or we
> +	 * would miss some rate divisor that aren't reachable with div being 2
> +	 * (e.g. rate divisor 90 is made with div = 3 and tmp_qd = 30, thus
> +	 * tmp_qd is even so we skip it because we think div 2 could make this
> +	 * rate divisor which isn't possible since tmp_qd has to be <= 31).
> +	 */
> +	for (tmp_qd = 1; tmp_qd < AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX; tmp_qd++)
> +		for (div = 2; div <= 3; div++) {
> +			if (div == 2 && tmp_qd % 3 == 0)
> +				continue;
> +
> +			best_parent_rate = clk_hw_round_rate(pclk,
> +							rate * tmp_qd * div);
> +			tmp_rate = best_parent_rate / (div * tmp_qd);
> +			tmp_diff = abs(rate - tmp_rate);
> +
> +			if (best_diff < 0 || best_diff > tmp_diff) {
> +				*parent_rate = best_parent_rate;
> +				best_rate = tmp_rate;
> +				best_diff = tmp_diff;
> +			}
> +		}
> +
> +	pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n",
> +		 __func__, best_rate, best_parent_rate);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long parent_rate)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +	u8 tmp_div;
> +
> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, parent_rate);
> +
> +	if (!rate)
> +		return -EINVAL;

This happens?

> +
> +	tmp_div = parent_rate / rate;
> +	if (tmp_div % 3 == 0) {
> +		apad_ck->qdaudio = tmp_div / 3;
> +		apad_ck->div = 3;
> +	} else {
> +		apad_ck->qdaudio = tmp_div / 2;
> +		apad_ck->div = 2;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_pad_ops = {
> +	.enable = clk_audio_pll_pad_enable,
> +	.disable = clk_audio_pll_pad_disable,
> +	.recalc_rate = clk_audio_pll_pad_recalc_rate,
> +	.round_rate = clk_audio_pll_pad_round_rate,
> +	.set_rate = clk_audio_pll_pad_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
> +{
> +	struct clk_audio_pad *apad_ck;
> +	struct clk_init_data init;

Best to initialize to { } just in case we add something later.

> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);
> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
> +	if (!apad_ck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_pad_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);

Use of_clk_parent_fill()?

> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> +		CLK_SET_RATE_PARENT;
> +
> +	apad_ck->hw.init = &init;
> +	apad_ck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &apad_ck->hw);
> +	if (ret)
> +		kfree(apad_ck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);

Maybe we should make this register sequence a helper function.
Seems common.

> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
> +	       "atmel,sama5d2-clk-audio-pll-pad",
> +	       of_sama5d2_clk_audio_pll_pad_setup);

We can't have a device driver for this?

> diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c
> new file mode 100644
> index 000000000000..7b0847ed7a4b
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll-pmc.c
> @@ -0,0 +1,174 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *                     Nicolas Ferre <nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>

Used?

> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: PMC output for fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * enable - clk_enable writes qdpmc, and enables PMC output
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate / (qdpmc + 1)
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_QDPMC_MAX		(AT91_PMC_AUDIO_PLL_QDPMC_MASK >> \
> +						AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
> +struct clk_audio_pmc {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u8 qdpmc;
> +};
> +
> +#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw)
> +
> +static int clk_audio_pll_pmc_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PMCEN |
> +			   AT91_PMC_AUDIO_PLL_QDPMC_MASK,
> +			   AT91_PMC_AUDIO_PLL_PMCEN |
> +			   AT91_PMC_AUDIO_PLL_QDPMC(apmc_ck->qdpmc));
> +	return 0;
> +}
> +
> +static void clk_audio_pll_pmc_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PMCEN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw,
> +						   unsigned long parent_rate)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +	unsigned long apmc_rate = 0;
> +
> +	apmc_rate = parent_rate / (apmc_ck->qdpmc + 1);
> +
> +	pr_debug("A PLL/PMC: %s, apmc_rate = %lu (qdpmc = %u)\n", __func__,
> +		 apmc_rate, apmc_ck->qdpmc);
> +
> +	return apmc_rate;
> +}
> +
> +static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
> +					 unsigned long *parent_rate)
> +{
> +	struct clk_hw *pclk = clk_hw_get_parent(hw);
> +	long best_rate = -EINVAL;
> +	unsigned long best_parent_rate = 0;
> +	u32 tmp_qd = 0, div;
> +	long tmp_rate;
> +	int tmp_diff;
> +	int best_diff = -1;
> +
> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, *parent_rate);
> +
> +	for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) {
> +		best_parent_rate = clk_round_rate(pclk->clk, rate * div);
> +		tmp_rate = best_parent_rate / div;
> +		tmp_diff = abs(rate - tmp_rate);
> +
> +		if (best_diff < 0 || best_diff > tmp_diff) {
> +			*parent_rate = best_parent_rate;
> +			best_rate = tmp_rate;
> +			best_diff = tmp_diff;
> +			tmp_qd = div;
> +		}
> +	}
> +
> +	pr_debug("A PLL/PMC: %s, best_rate = %ld, best_parent_rate = %lu (qd = %d)\n",
> +		 __func__, best_rate, *parent_rate, tmp_qd - 1);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long parent_rate)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	if (!rate)
> +		return -EINVAL;
> +
> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, parent_rate);
> +
> +	apmc_ck->qdpmc = parent_rate / rate - 1;

Hopefully rate isn't 1 or that goes undefined.

> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_pmc_ops = {
> +	.enable = clk_audio_pll_pmc_enable,
> +	.disable = clk_audio_pll_pmc_disable,
> +	.recalc_rate = clk_audio_pll_pmc_recalc_rate,
> +	.round_rate = clk_audio_pll_pmc_round_rate,
> +	.set_rate = clk_audio_pll_pmc_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
> +{
> +	struct clk_audio_pmc *apmc_ck;
> +	struct clk_init_data init;
> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);
> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
> +	if (!apmc_ck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_pmc_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);

This feels repetitive.

> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> +		CLK_SET_RATE_PARENT;
> +
> +	apmc_ck->hw.init = &init;
> +	apmc_ck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &apmc_ck->hw);
> +	if (ret)
> +		kfree(apmc_ck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw);
> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
> +	       "atmel,sama5d2-clk-audio-pll-pmc",
> +	       of_sama5d2_clk_audio_pll_pmc_setup);

Very

> diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
> new file mode 100644
> index 000000000000..efc2cb58da85
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll.c
> @@ -0,0 +1,239 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *		       Songjun Wu <songjun.wu-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>,
> + *                     Nicolas Ferre <nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>

Used?

> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: Fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare puts audio PLL in reset state
> + * enable - clk_enable writes nd, fracr parameters and enables PLL
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate * ((nd + 1) + (fracr / 2^22))
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_DIV_FRAC	BIT(22)
> +#define AUDIO_PLL_ND_MAX	(AT91_PMC_AUDIO_PLL_ND_MASK >> \
> +					AT91_PMC_AUDIO_PLL_ND_OFFSET)
> +
> +#define AUDIO_PLL_FOUT_MIN	620000000
> +#define AUDIO_PLL_FOUT_MAX	700000000
> +
> +struct clk_audio_frac {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u32 fracr;
> +	u8 nd;
> +};
> +
> +#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw)
> +
> +static int clk_audio_pll_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN, 0);
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN,
> +			   AT91_PMC_AUDIO_PLL_RESETN);
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL1,
> +			   AT91_PMC_AUDIO_PLL_FRACR_MASK, fck->fracr);
> +
> +	/*
> +	 * reset and enable have to be done in 2 separated writes
> +	 * for AT91_PMC_AUDIO_PLL0
> +	 */
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PLLEN |
> +			   AT91_PMC_AUDIO_PLL_ND_MASK,
> +			   AT91_PMC_AUDIO_PLL_PLLEN |
> +			   AT91_PMC_AUDIO_PLL_ND(fck->nd));
> +
> +	return 0;
> +}
> +
> +static void clk_audio_pll_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PLLEN, 0);
> +	/* do it in 2 separated writes */
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
> +					unsigned long nd, unsigned long fracr)
> +{
> +	unsigned long long fr = (unsigned long long)parent_rate *
> +						(unsigned long long)fracr;

We only need one cast here?

> +
> +	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
> +
> +	fr = DIV_ROUND_CLOSEST_ULL(fr, AUDIO_PLL_DIV_FRAC);
> +
> +	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
> +
> +	return parent_rate * (nd + 1) + fr;
> +}
> +
> +static unsigned long clk_audio_pll_recalc_rate(struct clk_hw *hw,
> +					       unsigned long parent_rate)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +	unsigned long fout;
> +
> +	fout = clk_audio_pll_fout(parent_rate, fck->nd, fck->fracr);
> +
> +	pr_debug("A PLL: %s, fout = %lu (nd = %u, fracr = %lu)\n", __func__,
> +		 fout, fck->nd, (unsigned long)fck->fracr);
> +
> +	return fout;
> +}
> +
> +static int clk_audio_pll_compute_frac(unsigned long rate,
> +				      unsigned long parent_rate,
> +				      unsigned long *nd, unsigned long *fracr)
> +{
> +	unsigned long long tmp, rem;
> +
> +	if (!rate)
> +		return -EINVAL;

This happens?

> +
> +	tmp = rate;
> +	rem = do_div(tmp, parent_rate);
> +	if (!tmp || tmp >= AUDIO_PLL_ND_MAX)
> +		return -EINVAL;
> +
> +	*nd = tmp - 1;
> +
> +	tmp = rem * AUDIO_PLL_DIV_FRAC;
> +	tmp = DIV_ROUND_CLOSEST_ULL(tmp, parent_rate);
> +	if (tmp > AT91_PMC_AUDIO_PLL_FRACR_MASK)
> +		return -EINVAL;
> +
> +	/* we can cast here as we verified the bounds just above */
> +	*fracr = (unsigned long)tmp;
> +
> +	return 0;
> +}
> +
> +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +				     unsigned long *parent_rate)
> +{
> +	long best_rate = -EINVAL;
> +	unsigned long fracr, nd;
> +	int ret;
> +
> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> +		 *parent_rate);
> +
> +	if (rate < AUDIO_PLL_FOUT_MIN)
> +		rate = AUDIO_PLL_FOUT_MIN;
> +	else if (rate > AUDIO_PLL_FOUT_MAX)
> +		rate = AUDIO_PLL_FOUT_MAX;

Use clamp. Also, did you want to use determine_rate callback and
clamp the requested rate range?

> +
> +	ret = clk_audio_pll_compute_frac(rate, *parent_rate, &nd, &fracr);
> +	if (ret)
> +		return ret;
> +
> +	best_rate = clk_audio_pll_fout(*parent_rate, nd, fracr);
> +
> +	pr_debug("A PLL: %s, best_rate = %lu (nd = %lu, fracr = %lu)\n",
> +		 __func__, best_rate, nd, fracr);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +				  unsigned long parent_rate)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +	unsigned long fracr, nd;
> +	int ret;
> +
> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> +		 parent_rate);
> +
> +	if (rate < AUDIO_PLL_FOUT_MIN || rate > AUDIO_PLL_FOUT_MAX)
> +		return -EINVAL;
> +
> +	ret = clk_audio_pll_compute_frac(rate, parent_rate, &nd, &fracr);
> +	if (ret)
> +		return ret;
> +
> +	fck->nd = nd;
> +	fck->fracr = fracr;
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_ops = {
> +	.enable = clk_audio_pll_enable,
> +	.disable = clk_audio_pll_disable,
> +	.recalc_rate = clk_audio_pll_recalc_rate,
> +	.round_rate = clk_audio_pll_round_rate,
> +	.set_rate = clk_audio_pll_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
> +{
> +	struct clk_audio_frac *fck;
> +	struct clk_init_data init;
> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);

Any way to not rely on clock-output-names?

> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	fck = kzalloc(sizeof(*fck), GFP_KERNEL);

This variable name looks like f*ck, perhaps name it something
else. frac?

> +	if (!fck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE;
> +
> +	fck->hw.init = &init;
> +	fck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &fck->hw);
> +	if (ret)
> +		kfree(fck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fck->hw);
> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_setup,
> +	       "atmel,sama5d2-clk-audio-pll-frac",
> +	       of_sama5d2_clk_audio_pll_setup);

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-21 22:20     ` Stephen Boyd
  0 siblings, 0 replies; 49+ messages in thread
From: Stephen Boyd @ 2017-07-21 22:20 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/13, Quentin Schulz wrote:
> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
> new file mode 100644
> index 000000000000..10dd6d625696
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
> @@ -0,0 +1,206 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>

Used?

> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: PAD output for fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * enable - clk_enable writes divisors and enables PAD output
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate / (qdaudio * div))
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_QDPAD(qd, div)	((AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV(qd) & \
> +					  AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MASK) | \
> +					 (AT91_PMC_AUDIO_PLL_QDPAD_DIV(div) & \
> +					  AT91_PMC_AUDIO_PLL_QDPAD_DIV_MASK))
> +
> +struct clk_audio_pad {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u8 qdaudio;
> +	u8 div;
> +};
> +
> +#define to_clk_audio_pad(hw) container_of(hw, struct clk_audio_pad, hw)
> +
> +static int clk_audio_pll_pad_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL1,
> +			   AT91_PMC_AUDIO_PLL_QDPAD_MASK,
> +			   AUDIO_PLL_QDPAD(apad_ck->qdaudio, apad_ck->div));
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PADEN, AT91_PMC_AUDIO_PLL_PADEN);
> +
> +	return 0;
> +}
> +
> +static void clk_audio_pll_pad_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +
> +	regmap_update_bits(apad_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PADEN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_pad_recalc_rate(struct clk_hw *hw,
> +						   unsigned long parent_rate)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +	unsigned long apad_rate = 0;
> +
> +	if (apad_ck->qdaudio && apad_ck->div)
> +		apad_rate = parent_rate / (apad_ck->qdaudio * apad_ck->div);
> +
> +	pr_debug("A PLL/PAD: %s, apad_rate = %lu (div = %u, qdaudio = %u)\n",
> +		 __func__, apad_rate, apad_ck->div, apad_ck->qdaudio);
> +
> +	return apad_rate;
> +}
> +
> +static long clk_audio_pll_pad_round_rate(struct clk_hw *hw, unsigned long rate,
> +					 unsigned long *parent_rate)
> +{
> +	struct clk_hw *pclk = clk_hw_get_parent(hw);
> +	long best_rate = -EINVAL;
> +	unsigned long best_parent_rate;
> +	unsigned long tmp_qd;
> +	u32 div;
> +	long tmp_rate;
> +	int tmp_diff;
> +	int best_diff = -1;
> +
> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, *parent_rate);
> +
> +	/*
> +	 * Rate divisor is actually made of two different divisors, multiplied
> +	 * between themselves before dividing the rate.
> +	 * tmp_qd goes from 1 to 31 and div is either 2 or 3.
> +	 * In order to avoid testing twice the rate divisor (e.g. divisor 12 can
> +	 * be found with (tmp_qd, div) = (2, 6) or (3, 4)), we remove any loop
> +	 * for a rate divisor when div is 2 and tmp_qd is a multiple of 3.
> +	 * We cannot inverse it (condition div is 3 and tmp_qd is even) or we
> +	 * would miss some rate divisor that aren't reachable with div being 2
> +	 * (e.g. rate divisor 90 is made with div = 3 and tmp_qd = 30, thus
> +	 * tmp_qd is even so we skip it because we think div 2 could make this
> +	 * rate divisor which isn't possible since tmp_qd has to be <= 31).
> +	 */
> +	for (tmp_qd = 1; tmp_qd < AT91_PMC_AUDIO_PLL_QDPAD_EXTDIV_MAX; tmp_qd++)
> +		for (div = 2; div <= 3; div++) {
> +			if (div == 2 && tmp_qd % 3 == 0)
> +				continue;
> +
> +			best_parent_rate = clk_hw_round_rate(pclk,
> +							rate * tmp_qd * div);
> +			tmp_rate = best_parent_rate / (div * tmp_qd);
> +			tmp_diff = abs(rate - tmp_rate);
> +
> +			if (best_diff < 0 || best_diff > tmp_diff) {
> +				*parent_rate = best_parent_rate;
> +				best_rate = tmp_rate;
> +				best_diff = tmp_diff;
> +			}
> +		}
> +
> +	pr_debug("A PLL/PAD: %s, best_rate = %ld, best_parent_rate = %lu\n",
> +		 __func__, best_rate, best_parent_rate);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long parent_rate)
> +{
> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
> +	u8 tmp_div;
> +
> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, parent_rate);
> +
> +	if (!rate)
> +		return -EINVAL;

This happens?

> +
> +	tmp_div = parent_rate / rate;
> +	if (tmp_div % 3 == 0) {
> +		apad_ck->qdaudio = tmp_div / 3;
> +		apad_ck->div = 3;
> +	} else {
> +		apad_ck->qdaudio = tmp_div / 2;
> +		apad_ck->div = 2;
> +	}
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_pad_ops = {
> +	.enable = clk_audio_pll_pad_enable,
> +	.disable = clk_audio_pll_pad_disable,
> +	.recalc_rate = clk_audio_pll_pad_recalc_rate,
> +	.round_rate = clk_audio_pll_pad_round_rate,
> +	.set_rate = clk_audio_pll_pad_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
> +{
> +	struct clk_audio_pad *apad_ck;
> +	struct clk_init_data init;

Best to initialize to { } just in case we add something later.

> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);
> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
> +	if (!apad_ck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_pad_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);

Use of_clk_parent_fill()?

> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> +		CLK_SET_RATE_PARENT;
> +
> +	apad_ck->hw.init = &init;
> +	apad_ck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &apad_ck->hw);
> +	if (ret)
> +		kfree(apad_ck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);

Maybe we should make this register sequence a helper function.
Seems common.

> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
> +	       "atmel,sama5d2-clk-audio-pll-pad",
> +	       of_sama5d2_clk_audio_pll_pad_setup);

We can't have a device driver for this?

> diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c
> new file mode 100644
> index 000000000000..7b0847ed7a4b
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll-pmc.c
> @@ -0,0 +1,174 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>

Used?

> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: PMC output for fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * enable - clk_enable writes qdpmc, and enables PMC output
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate / (qdpmc + 1)
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_QDPMC_MAX		(AT91_PMC_AUDIO_PLL_QDPMC_MASK >> \
> +						AT91_PMC_AUDIO_PLL_QDPMC_OFFSET)
> +struct clk_audio_pmc {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u8 qdpmc;
> +};
> +
> +#define to_clk_audio_pmc(hw) container_of(hw, struct clk_audio_pmc, hw)
> +
> +static int clk_audio_pll_pmc_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PMCEN |
> +			   AT91_PMC_AUDIO_PLL_QDPMC_MASK,
> +			   AT91_PMC_AUDIO_PLL_PMCEN |
> +			   AT91_PMC_AUDIO_PLL_QDPMC(apmc_ck->qdpmc));
> +	return 0;
> +}
> +
> +static void clk_audio_pll_pmc_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	regmap_update_bits(apmc_ck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PMCEN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_pmc_recalc_rate(struct clk_hw *hw,
> +						   unsigned long parent_rate)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +	unsigned long apmc_rate = 0;
> +
> +	apmc_rate = parent_rate / (apmc_ck->qdpmc + 1);
> +
> +	pr_debug("A PLL/PMC: %s, apmc_rate = %lu (qdpmc = %u)\n", __func__,
> +		 apmc_rate, apmc_ck->qdpmc);
> +
> +	return apmc_rate;
> +}
> +
> +static long clk_audio_pll_pmc_round_rate(struct clk_hw *hw, unsigned long rate,
> +					 unsigned long *parent_rate)
> +{
> +	struct clk_hw *pclk = clk_hw_get_parent(hw);
> +	long best_rate = -EINVAL;
> +	unsigned long best_parent_rate = 0;
> +	u32 tmp_qd = 0, div;
> +	long tmp_rate;
> +	int tmp_diff;
> +	int best_diff = -1;
> +
> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, *parent_rate);
> +
> +	for (div = 1; div <= AUDIO_PLL_QDPMC_MAX; div++) {
> +		best_parent_rate = clk_round_rate(pclk->clk, rate * div);
> +		tmp_rate = best_parent_rate / div;
> +		tmp_diff = abs(rate - tmp_rate);
> +
> +		if (best_diff < 0 || best_diff > tmp_diff) {
> +			*parent_rate = best_parent_rate;
> +			best_rate = tmp_rate;
> +			best_diff = tmp_diff;
> +			tmp_qd = div;
> +		}
> +	}
> +
> +	pr_debug("A PLL/PMC: %s, best_rate = %ld, best_parent_rate = %lu (qd = %d)\n",
> +		 __func__, best_rate, *parent_rate, tmp_qd - 1);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long parent_rate)
> +{
> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
> +
> +	if (!rate)
> +		return -EINVAL;
> +
> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
> +		 rate, parent_rate);
> +
> +	apmc_ck->qdpmc = parent_rate / rate - 1;

Hopefully rate isn't 1 or that goes undefined.

> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_pmc_ops = {
> +	.enable = clk_audio_pll_pmc_enable,
> +	.disable = clk_audio_pll_pmc_disable,
> +	.recalc_rate = clk_audio_pll_pmc_recalc_rate,
> +	.round_rate = clk_audio_pll_pmc_round_rate,
> +	.set_rate = clk_audio_pll_pmc_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
> +{
> +	struct clk_audio_pmc *apmc_ck;
> +	struct clk_init_data init;
> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);
> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
> +	if (!apmc_ck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_pmc_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);

This feels repetitive.

> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> +		CLK_SET_RATE_PARENT;
> +
> +	apmc_ck->hw.init = &init;
> +	apmc_ck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &apmc_ck->hw);
> +	if (ret)
> +		kfree(apmc_ck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw);
> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
> +	       "atmel,sama5d2-clk-audio-pll-pmc",
> +	       of_sama5d2_clk_audio_pll_pmc_setup);

Very

> diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
> new file mode 100644
> index 000000000000..efc2cb58da85
> --- /dev/null
> +++ b/drivers/clk/at91/clk-audio-pll.c
> @@ -0,0 +1,239 @@
> +/*
> + *  Copyright (C) 2016 Atmel Corporation,
> + *		       Songjun Wu <songjun.wu@atmel.com>,
> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
> + *  Copyright (C) 2017 Free Electrons,
> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +#include <linux/clk-provider.h>
> +#include <linux/clkdev.h>

Used?

> +#include <linux/clk/at91_pmc.h>
> +#include <linux/of.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/regmap.h>
> +
> +#include "pmc.h"
> +
> +/*
> + * DOC: Fractional PLL clock for audio
> + *
> + * Traits of this clock:
> + * prepare - clk_prepare puts audio PLL in reset state
> + * enable - clk_enable writes nd, fracr parameters and enables PLL
> + * rate - rate is adjustable.
> + *        clk->rate = parent->rate * ((nd + 1) + (fracr / 2^22))
> + * parent - fixed parent.  No clk_set_parent support
> + */
> +
> +#define AUDIO_PLL_DIV_FRAC	BIT(22)
> +#define AUDIO_PLL_ND_MAX	(AT91_PMC_AUDIO_PLL_ND_MASK >> \
> +					AT91_PMC_AUDIO_PLL_ND_OFFSET)
> +
> +#define AUDIO_PLL_FOUT_MIN	620000000
> +#define AUDIO_PLL_FOUT_MAX	700000000
> +
> +struct clk_audio_frac {
> +	struct clk_hw hw;
> +	struct regmap *regmap;
> +	u32 fracr;
> +	u8 nd;
> +};
> +
> +#define to_clk_audio_frac(hw) container_of(hw, struct clk_audio_frac, hw)
> +
> +static int clk_audio_pll_enable(struct clk_hw *hw)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN, 0);
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN,
> +			   AT91_PMC_AUDIO_PLL_RESETN);
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL1,
> +			   AT91_PMC_AUDIO_PLL_FRACR_MASK, fck->fracr);
> +
> +	/*
> +	 * reset and enable have to be done in 2 separated writes
> +	 * for AT91_PMC_AUDIO_PLL0
> +	 */
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PLLEN |
> +			   AT91_PMC_AUDIO_PLL_ND_MASK,
> +			   AT91_PMC_AUDIO_PLL_PLLEN |
> +			   AT91_PMC_AUDIO_PLL_ND(fck->nd));
> +
> +	return 0;
> +}
> +
> +static void clk_audio_pll_disable(struct clk_hw *hw)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_PLLEN, 0);
> +	/* do it in 2 separated writes */
> +	regmap_update_bits(fck->regmap, AT91_PMC_AUDIO_PLL0,
> +			   AT91_PMC_AUDIO_PLL_RESETN, 0);
> +}
> +
> +static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
> +					unsigned long nd, unsigned long fracr)
> +{
> +	unsigned long long fr = (unsigned long long)parent_rate *
> +						(unsigned long long)fracr;

We only need one cast here?

> +
> +	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
> +
> +	fr = DIV_ROUND_CLOSEST_ULL(fr, AUDIO_PLL_DIV_FRAC);
> +
> +	pr_debug("A PLL: %s, fr = %llu\n", __func__, fr);
> +
> +	return parent_rate * (nd + 1) + fr;
> +}
> +
> +static unsigned long clk_audio_pll_recalc_rate(struct clk_hw *hw,
> +					       unsigned long parent_rate)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +	unsigned long fout;
> +
> +	fout = clk_audio_pll_fout(parent_rate, fck->nd, fck->fracr);
> +
> +	pr_debug("A PLL: %s, fout = %lu (nd = %u, fracr = %lu)\n", __func__,
> +		 fout, fck->nd, (unsigned long)fck->fracr);
> +
> +	return fout;
> +}
> +
> +static int clk_audio_pll_compute_frac(unsigned long rate,
> +				      unsigned long parent_rate,
> +				      unsigned long *nd, unsigned long *fracr)
> +{
> +	unsigned long long tmp, rem;
> +
> +	if (!rate)
> +		return -EINVAL;

This happens?

> +
> +	tmp = rate;
> +	rem = do_div(tmp, parent_rate);
> +	if (!tmp || tmp >= AUDIO_PLL_ND_MAX)
> +		return -EINVAL;
> +
> +	*nd = tmp - 1;
> +
> +	tmp = rem * AUDIO_PLL_DIV_FRAC;
> +	tmp = DIV_ROUND_CLOSEST_ULL(tmp, parent_rate);
> +	if (tmp > AT91_PMC_AUDIO_PLL_FRACR_MASK)
> +		return -EINVAL;
> +
> +	/* we can cast here as we verified the bounds just above */
> +	*fracr = (unsigned long)tmp;
> +
> +	return 0;
> +}
> +
> +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +				     unsigned long *parent_rate)
> +{
> +	long best_rate = -EINVAL;
> +	unsigned long fracr, nd;
> +	int ret;
> +
> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> +		 *parent_rate);
> +
> +	if (rate < AUDIO_PLL_FOUT_MIN)
> +		rate = AUDIO_PLL_FOUT_MIN;
> +	else if (rate > AUDIO_PLL_FOUT_MAX)
> +		rate = AUDIO_PLL_FOUT_MAX;

Use clamp. Also, did you want to use determine_rate callback and
clamp the requested rate range?

> +
> +	ret = clk_audio_pll_compute_frac(rate, *parent_rate, &nd, &fracr);
> +	if (ret)
> +		return ret;
> +
> +	best_rate = clk_audio_pll_fout(*parent_rate, nd, fracr);
> +
> +	pr_debug("A PLL: %s, best_rate = %lu (nd = %lu, fracr = %lu)\n",
> +		 __func__, best_rate, nd, fracr);
> +
> +	return best_rate;
> +}
> +
> +static int clk_audio_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +				  unsigned long parent_rate)
> +{
> +	struct clk_audio_frac *fck = to_clk_audio_frac(hw);
> +	unsigned long fracr, nd;
> +	int ret;
> +
> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> +		 parent_rate);
> +
> +	if (rate < AUDIO_PLL_FOUT_MIN || rate > AUDIO_PLL_FOUT_MAX)
> +		return -EINVAL;
> +
> +	ret = clk_audio_pll_compute_frac(rate, parent_rate, &nd, &fracr);
> +	if (ret)
> +		return ret;
> +
> +	fck->nd = nd;
> +	fck->fracr = fracr;
> +
> +	return 0;
> +}
> +
> +static const struct clk_ops audio_pll_ops = {
> +	.enable = clk_audio_pll_enable,
> +	.disable = clk_audio_pll_disable,
> +	.recalc_rate = clk_audio_pll_recalc_rate,
> +	.round_rate = clk_audio_pll_round_rate,
> +	.set_rate = clk_audio_pll_set_rate,
> +};
> +
> +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
> +{
> +	struct clk_audio_frac *fck;
> +	struct clk_init_data init;
> +	struct regmap *regmap;
> +	const char *parent_name;
> +	const char *name = np->name;
> +	int ret;
> +
> +	parent_name = of_clk_get_parent_name(np, 0);
> +
> +	of_property_read_string(np, "clock-output-names", &name);

Any way to not rely on clock-output-names?

> +
> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> +	if (IS_ERR(regmap))
> +		return;
> +
> +	fck = kzalloc(sizeof(*fck), GFP_KERNEL);

This variable name looks like f*ck, perhaps name it something
else. frac?

> +	if (!fck)
> +		return;
> +
> +	init.name = name;
> +	init.ops = &audio_pll_ops;
> +	init.parent_names = (parent_name ? &parent_name : NULL);
> +	init.num_parents = 1;
> +	init.flags = CLK_SET_RATE_GATE;
> +
> +	fck->hw.init = &init;
> +	fck->regmap = regmap;
> +
> +	ret = clk_hw_register(NULL, &fck->hw);
> +	if (ret)
> +		kfree(fck);
> +	else
> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &fck->hw);
> +}
> +
> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_setup,
> +	       "atmel,sama5d2-clk-audio-pll-frac",
> +	       of_sama5d2_clk_audio_pll_setup);

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 2/9] clk: at91: add audio plls to the compatible list in DT binding
  2017-07-13  7:49   ` Quentin Schulz
@ 2017-07-21 22:21     ` Stephen Boyd
  -1 siblings, 0 replies; 49+ messages in thread
From: Stephen Boyd @ 2017-07-21 22:21 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: mturquette, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai, cyrille.pitchen, thomas.petazzoni, linux-clk, devicetree,
	linux-kernel, alsa-devel, linux-arm-kernel

On 07/13, Quentin Schulz wrote:
> This new clock driver set allows to have a fractional divided clock that
> would generate a precise clock particularly suitable for audio
> applications.
> 
> The main audio pll clock has two children clocks: one that is connected
> to the PMC, the other that can directly drive a pad. As these two routes
> have different enable bits and different dividers and divider formulas,
> they are handled by two different drivers.
> 
> This adds the audio plls (frac, pad and pmc) to the compatible list of
> at91 clocks in DT binding.
> 
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> Acked-by: Rob Herring <robh@kernel.org>
> ---
> 
> added in v2:
>   - split from big patch with pll drivers and dt-binding

Subject seems wrong. Should have "bindings" somewhere?

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 2/9] clk: at91: add audio plls to the compatible list in DT binding
@ 2017-07-21 22:21     ` Stephen Boyd
  0 siblings, 0 replies; 49+ messages in thread
From: Stephen Boyd @ 2017-07-21 22:21 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/13, Quentin Schulz wrote:
> This new clock driver set allows to have a fractional divided clock that
> would generate a precise clock particularly suitable for audio
> applications.
> 
> The main audio pll clock has two children clocks: one that is connected
> to the PMC, the other that can directly drive a pad. As these two routes
> have different enable bits and different dividers and divider formulas,
> they are handled by two different drivers.
> 
> This adds the audio plls (frac, pad and pmc) to the compatible list of
> at91 clocks in DT binding.
> 
> Signed-off-by: Quentin Schulz <quentin.schulz@free-electrons.com>
> Acked-by: Rob Herring <robh@kernel.org>
> ---
> 
> added in v2:
>   - split from big patch with pll drivers and dt-binding

Subject seems wrong. Should have "bindings" somewhere?

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-24  8:37       ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-24  8:37 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: mturquette, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai, cyrille.pitchen, thomas.petazzoni, linux-clk, devicetree,
	linux-kernel, alsa-devel, linux-arm-kernel, Nicolas Ferre

Hi Stephen,

On 22/07/2017 00:20, Stephen Boyd wrote:
> On 07/13, Quentin Schulz wrote:
>> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
>> new file mode 100644
>> index 000000000000..10dd6d625696
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
>> @@ -0,0 +1,206 @@
>> +/*
>> + *  Copyright (C) 2016 Atmel Corporation,
>> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
>> + *  Copyright (C) 2017 Free Electrons,
>> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + */
>> +#include <linux/clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
> 
> Used?
> 

Not really, I need slab.h for kzalloc tough which was included by clkdev.

[...]
>> +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
>> +				      unsigned long parent_rate)
>> +{
>> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
>> +	u8 tmp_div;
>> +
>> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
>> +		 rate, parent_rate);
>> +
>> +	if (!rate)
>> +		return -EINVAL;
> 
> This happens?
> 

I don't know, but it's better to do this quick check rather than being
exposed to a division by zero IMHO. Nothing in clk_ops states that the
rate given to set_rate is non-zero, so I made sure this can't happen.

>> +
>> +	tmp_div = parent_rate / rate;
>> +	if (tmp_div % 3 == 0) {
>> +		apad_ck->qdaudio = tmp_div / 3;
>> +		apad_ck->div = 3;
>> +	} else {
>> +		apad_ck->qdaudio = tmp_div / 2;
>> +		apad_ck->div = 2;
>> +	}[...]
>> +static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
>> +{
>> +	struct clk_audio_pad *apad_ck;
>> +	struct clk_init_data init;
> 
> Best to initialize to { } just in case we add something later.
> 

ACK.

>> +	struct regmap *regmap;
>> +	const char *parent_name;
>> +	const char *name = np->name;
>> +	int ret;
>> +
>> +	parent_name = of_clk_get_parent_name(np, 0);
>> +
>> +	of_property_read_string(np, "clock-output-names", &name);
>> +
>> +	regmap = syscon_node_to_regmap(of_get_parent(np));
>> +	if (IS_ERR(regmap))
>> +		return;
>> +
>> +	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
>> +	if (!apad_ck)
>> +		return;
>> +
>> +	init.name = name;
>> +	init.ops = &audio_pll_pad_ops;
>> +	init.parent_names = (parent_name ? &parent_name : NULL);
> 
> Use of_clk_parent_fill()?
> 

[Deleting `parent_name = of_clk_get_parent_name(np, 0);`]
[Deleting `init.parent_names = (parent_name ? &parent_name : NULL);`]

+ const char *parent_names[1];
[...]
+ of_clk_parent_fill(np, parent_names, 1);
+ init.parent_names = parent_names;

Is it what you're asking?

>> +	init.num_parents = 1;
>> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
>> +		CLK_SET_RATE_PARENT;
>> +
>> +	apad_ck->hw.init = &init;
>> +	apad_ck->regmap = regmap;
>> +
>> +	ret = clk_hw_register(NULL, &apad_ck->hw);
>> +	if (ret)
>> +		kfree(apad_ck);
>> +	else
>> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
> 
> Maybe we should make this register sequence a helper function.
> Seems common.
> 

I can put such an helper in an header if this is what you meant.

>> +}
>> +
>> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
>> +	       "atmel,sama5d2-clk-audio-pll-pad",
>> +	       of_sama5d2_clk_audio_pll_pad_setup);
> 
> We can't have a device driver for this?
> 

Could you elaborate please?

>> diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c
>> new file mode 100644
>> index 000000000000..7b0847ed7a4b
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll-pmc.c
[...]
>> +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
>> +				      unsigned long parent_rate)
>> +{
>> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
>> +
>> +	if (!rate)
>> +		return -EINVAL;
>> +
>> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
>> +		 rate, parent_rate);
>> +
>> +	apmc_ck->qdpmc = parent_rate / rate - 1;
> 
> Hopefully rate isn't 1 or that goes undefined.
> 

Thanks to operator precedence, the only check to do is rate != 0 (done
few lines above). Division has precedence over substraction.

[...]
>> +static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
>> +{
>> +	struct clk_audio_pmc *apmc_ck;
>> +	struct clk_init_data init;
>> +	struct regmap *regmap;
>> +	const char *parent_name;
>> +	const char *name = np->name;
>> +	int ret;
>> +
>> +	parent_name = of_clk_get_parent_name(np, 0);
>> +
>> +	of_property_read_string(np, "clock-output-names", &name);
>> +
>> +	regmap = syscon_node_to_regmap(of_get_parent(np));
>> +	if (IS_ERR(regmap))
>> +		return;
>> +
>> +	apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
>> +	if (!apmc_ck)
>> +		return;
>> +
>> +	init.name = name;
>> +	init.ops = &audio_pll_pmc_ops;
>> +	init.parent_names = (parent_name ? &parent_name : NULL);
> 
> This feels repetitive.
> 
>> +	init.num_parents = 1;
>> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
>> +		CLK_SET_RATE_PARENT;
>> +
>> +	apmc_ck->hw.init = &init;
>> +	apmc_ck->regmap = regmap;
>> +
>> +	ret = clk_hw_register(NULL, &apmc_ck->hw);
>> +	if (ret)
>> +		kfree(apmc_ck);
>> +	else
>> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw);
>> +}
>> +
>> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
>> +	       "atmel,sama5d2-clk-audio-pll-pmc",
>> +	       of_sama5d2_clk_audio_pll_pmc_setup);
> 
> Very
> 

Basically, both share almost the same code but have different formulae
for the rate.

>> diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
>> new file mode 100644
>> index 000000000000..efc2cb58da85
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll.c
[...]
>> +static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
>> +					unsigned long nd, unsigned long fracr)
>> +{
>> +	unsigned long long fr = (unsigned long long)parent_rate *
>> +						(unsigned long long)fracr;
> 
> We only need one cast here?
> 

Indeed, I'll remove the casting of fracr.

[...]
>> +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
>> +				     unsigned long *parent_rate)
>> +{
>> +	long best_rate = -EINVAL;
>> +	unsigned long fracr, nd;
>> +	int ret;
>> +
>> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
>> +		 *parent_rate);
>> +
>> +	if (rate < AUDIO_PLL_FOUT_MIN)
>> +		rate = AUDIO_PLL_FOUT_MIN;
>> +	else if (rate > AUDIO_PLL_FOUT_MAX)
>> +		rate = AUDIO_PLL_FOUT_MAX;
> 
> Use clamp. Also, did you want to use determine_rate callback and
> clamp the requested rate range?
> 

Didn't know this one, thanks!

I want determine_rate to return a valid rate for the pll so I clamp the
requested rate range in this one. In set_rate, I just tell the user that
any requested rate outside of the valid range is invalid. Does that
answer your question?

[...]
>> +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
>> +{
>> +	struct clk_audio_frac *fck;
>> +	struct clk_init_data init;
>> +	struct regmap *regmap;
>> +	const char *parent_name;
>> +	const char *name = np->name;
>> +	int ret;
>> +
>> +	parent_name = of_clk_get_parent_name(np, 0);
>> +
>> +	of_property_read_string(np, "clock-output-names", &name);
> 
> Any way to not rely on clock-output-names?
> 

I guess we could use the name of the DT node (as it's done in the
variable initialization block above) and not override it by
clock-output-names?

>> +
>> +	regmap = syscon_node_to_regmap(of_get_parent(np));
>> +	if (IS_ERR(regmap))
>> +		return;
>> +
>> +	fck = kzalloc(sizeof(*fck), GFP_KERNEL);
> 
> This variable name looks like f*ck, perhaps name it something
> else. frac?

Sure.

[...]

Thanks,
Quentin

-- 
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-24  8:37       ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-24  8:37 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: mturquette-rdvid1DuHRBWk0Htik3J/w,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A,
	nicolas.ferre-UWL1GkI3JZL3oGB3hsPCZA,
	alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	perex-/Fr2/VpizcU, tiwai-IBi9RG/b67k,
	cyrille.pitchen-yU5RGvR974pGWvitb5QawA,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Nicolas Ferre

Hi Stephen,

On 22/07/2017 00:20, Stephen Boyd wrote:
> On 07/13, Quentin Schulz wrote:
>> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
>> new file mode 100644
>> index 000000000000..10dd6d625696
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
>> @@ -0,0 +1,206 @@
>> +/*
>> + *  Copyright (C) 2016 Atmel Corporation,
>> + *                     Nicolas Ferre <nicolas.ferre-AIFe0yeh4nAAvxtiuMwx3w@public.gmane.org>
>> + *  Copyright (C) 2017 Free Electrons,
>> + *		       Quentin Schulz <quentin.schulz-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8@public.gmane.org>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + */
>> +#include <linux/clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
> 
> Used?
> 

Not really, I need slab.h for kzalloc tough which was included by clkdev.

[...]
>> +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
>> +				      unsigned long parent_rate)
>> +{
>> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
>> +	u8 tmp_div;
>> +
>> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
>> +		 rate, parent_rate);
>> +
>> +	if (!rate)
>> +		return -EINVAL;
> 
> This happens?
> 

I don't know, but it's better to do this quick check rather than being
exposed to a division by zero IMHO. Nothing in clk_ops states that the
rate given to set_rate is non-zero, so I made sure this can't happen.

>> +
>> +	tmp_div = parent_rate / rate;
>> +	if (tmp_div % 3 == 0) {
>> +		apad_ck->qdaudio = tmp_div / 3;
>> +		apad_ck->div = 3;
>> +	} else {
>> +		apad_ck->qdaudio = tmp_div / 2;
>> +		apad_ck->div = 2;
>> +	}[...]
>> +static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
>> +{
>> +	struct clk_audio_pad *apad_ck;
>> +	struct clk_init_data init;
> 
> Best to initialize to { } just in case we add something later.
> 

ACK.

>> +	struct regmap *regmap;
>> +	const char *parent_name;
>> +	const char *name = np->name;
>> +	int ret;
>> +
>> +	parent_name = of_clk_get_parent_name(np, 0);
>> +
>> +	of_property_read_string(np, "clock-output-names", &name);
>> +
>> +	regmap = syscon_node_to_regmap(of_get_parent(np));
>> +	if (IS_ERR(regmap))
>> +		return;
>> +
>> +	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
>> +	if (!apad_ck)
>> +		return;
>> +
>> +	init.name = name;
>> +	init.ops = &audio_pll_pad_ops;
>> +	init.parent_names = (parent_name ? &parent_name : NULL);
> 
> Use of_clk_parent_fill()?
> 

[Deleting `parent_name = of_clk_get_parent_name(np, 0);`]
[Deleting `init.parent_names = (parent_name ? &parent_name : NULL);`]

+ const char *parent_names[1];
[...]
+ of_clk_parent_fill(np, parent_names, 1);
+ init.parent_names = parent_names;

Is it what you're asking?

>> +	init.num_parents = 1;
>> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
>> +		CLK_SET_RATE_PARENT;
>> +
>> +	apad_ck->hw.init = &init;
>> +	apad_ck->regmap = regmap;
>> +
>> +	ret = clk_hw_register(NULL, &apad_ck->hw);
>> +	if (ret)
>> +		kfree(apad_ck);
>> +	else
>> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
> 
> Maybe we should make this register sequence a helper function.
> Seems common.
> 

I can put such an helper in an header if this is what you meant.

>> +}
>> +
>> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
>> +	       "atmel,sama5d2-clk-audio-pll-pad",
>> +	       of_sama5d2_clk_audio_pll_pad_setup);
> 
> We can't have a device driver for this?
> 

Could you elaborate please?

>> diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c
>> new file mode 100644
>> index 000000000000..7b0847ed7a4b
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll-pmc.c
[...]
>> +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
>> +				      unsigned long parent_rate)
>> +{
>> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
>> +
>> +	if (!rate)
>> +		return -EINVAL;
>> +
>> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
>> +		 rate, parent_rate);
>> +
>> +	apmc_ck->qdpmc = parent_rate / rate - 1;
> 
> Hopefully rate isn't 1 or that goes undefined.
> 

Thanks to operator precedence, the only check to do is rate != 0 (done
few lines above). Division has precedence over substraction.

[...]
>> +static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
>> +{
>> +	struct clk_audio_pmc *apmc_ck;
>> +	struct clk_init_data init;
>> +	struct regmap *regmap;
>> +	const char *parent_name;
>> +	const char *name = np->name;
>> +	int ret;
>> +
>> +	parent_name = of_clk_get_parent_name(np, 0);
>> +
>> +	of_property_read_string(np, "clock-output-names", &name);
>> +
>> +	regmap = syscon_node_to_regmap(of_get_parent(np));
>> +	if (IS_ERR(regmap))
>> +		return;
>> +
>> +	apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
>> +	if (!apmc_ck)
>> +		return;
>> +
>> +	init.name = name;
>> +	init.ops = &audio_pll_pmc_ops;
>> +	init.parent_names = (parent_name ? &parent_name : NULL);
> 
> This feels repetitive.
> 
>> +	init.num_parents = 1;
>> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
>> +		CLK_SET_RATE_PARENT;
>> +
>> +	apmc_ck->hw.init = &init;
>> +	apmc_ck->regmap = regmap;
>> +
>> +	ret = clk_hw_register(NULL, &apmc_ck->hw);
>> +	if (ret)
>> +		kfree(apmc_ck);
>> +	else
>> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw);
>> +}
>> +
>> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
>> +	       "atmel,sama5d2-clk-audio-pll-pmc",
>> +	       of_sama5d2_clk_audio_pll_pmc_setup);
> 
> Very
> 

Basically, both share almost the same code but have different formulae
for the rate.

>> diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
>> new file mode 100644
>> index 000000000000..efc2cb58da85
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll.c
[...]
>> +static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
>> +					unsigned long nd, unsigned long fracr)
>> +{
>> +	unsigned long long fr = (unsigned long long)parent_rate *
>> +						(unsigned long long)fracr;
> 
> We only need one cast here?
> 

Indeed, I'll remove the casting of fracr.

[...]
>> +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
>> +				     unsigned long *parent_rate)
>> +{
>> +	long best_rate = -EINVAL;
>> +	unsigned long fracr, nd;
>> +	int ret;
>> +
>> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
>> +		 *parent_rate);
>> +
>> +	if (rate < AUDIO_PLL_FOUT_MIN)
>> +		rate = AUDIO_PLL_FOUT_MIN;
>> +	else if (rate > AUDIO_PLL_FOUT_MAX)
>> +		rate = AUDIO_PLL_FOUT_MAX;
> 
> Use clamp. Also, did you want to use determine_rate callback and
> clamp the requested rate range?
> 

Didn't know this one, thanks!

I want determine_rate to return a valid rate for the pll so I clamp the
requested rate range in this one. In set_rate, I just tell the user that
any requested rate outside of the valid range is invalid. Does that
answer your question?

[...]
>> +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
>> +{
>> +	struct clk_audio_frac *fck;
>> +	struct clk_init_data init;
>> +	struct regmap *regmap;
>> +	const char *parent_name;
>> +	const char *name = np->name;
>> +	int ret;
>> +
>> +	parent_name = of_clk_get_parent_name(np, 0);
>> +
>> +	of_property_read_string(np, "clock-output-names", &name);
> 
> Any way to not rely on clock-output-names?
> 

I guess we could use the name of the DT node (as it's done in the
variable initialization block above) and not override it by
clock-output-names?

>> +
>> +	regmap = syscon_node_to_regmap(of_get_parent(np));
>> +	if (IS_ERR(regmap))
>> +		return;
>> +
>> +	fck = kzalloc(sizeof(*fck), GFP_KERNEL);
> 
> This variable name looks like f*ck, perhaps name it something
> else. frac?

Sure.

[...]

Thanks,
Quentin

-- 
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-24  8:37       ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-24  8:37 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Stephen,

On 22/07/2017 00:20, Stephen Boyd wrote:
> On 07/13, Quentin Schulz wrote:
>> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
>> new file mode 100644
>> index 000000000000..10dd6d625696
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
>> @@ -0,0 +1,206 @@
>> +/*
>> + *  Copyright (C) 2016 Atmel Corporation,
>> + *                     Nicolas Ferre <nicolas.ferre@atmel.com>
>> + *  Copyright (C) 2017 Free Electrons,
>> + *		       Quentin Schulz <quentin.schulz@free-electrons.com>
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation; either version 2 of the License, or
>> + * (at your option) any later version.
>> + *
>> + */
>> +#include <linux/clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/clkdev.h>
> 
> Used?
> 

Not really, I need slab.h for kzalloc tough which was included by clkdev.

[...]
>> +static int clk_audio_pll_pad_set_rate(struct clk_hw *hw, unsigned long rate,
>> +				      unsigned long parent_rate)
>> +{
>> +	struct clk_audio_pad *apad_ck = to_clk_audio_pad(hw);
>> +	u8 tmp_div;
>> +
>> +	pr_debug("A PLL/PAD: %s, rate = %lu (parent_rate = %lu)\n", __func__,
>> +		 rate, parent_rate);
>> +
>> +	if (!rate)
>> +		return -EINVAL;
> 
> This happens?
> 

I don't know, but it's better to do this quick check rather than being
exposed to a division by zero IMHO. Nothing in clk_ops states that the
rate given to set_rate is non-zero, so I made sure this can't happen.

>> +
>> +	tmp_div = parent_rate / rate;
>> +	if (tmp_div % 3 == 0) {
>> +		apad_ck->qdaudio = tmp_div / 3;
>> +		apad_ck->div = 3;
>> +	} else {
>> +		apad_ck->qdaudio = tmp_div / 2;
>> +		apad_ck->div = 2;
>> +	}[...]
>> +static void __init of_sama5d2_clk_audio_pll_pad_setup(struct device_node *np)
>> +{
>> +	struct clk_audio_pad *apad_ck;
>> +	struct clk_init_data init;
> 
> Best to initialize to { } just in case we add something later.
> 

ACK.

>> +	struct regmap *regmap;
>> +	const char *parent_name;
>> +	const char *name = np->name;
>> +	int ret;
>> +
>> +	parent_name = of_clk_get_parent_name(np, 0);
>> +
>> +	of_property_read_string(np, "clock-output-names", &name);
>> +
>> +	regmap = syscon_node_to_regmap(of_get_parent(np));
>> +	if (IS_ERR(regmap))
>> +		return;
>> +
>> +	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
>> +	if (!apad_ck)
>> +		return;
>> +
>> +	init.name = name;
>> +	init.ops = &audio_pll_pad_ops;
>> +	init.parent_names = (parent_name ? &parent_name : NULL);
> 
> Use of_clk_parent_fill()?
> 

[Deleting `parent_name = of_clk_get_parent_name(np, 0);`]
[Deleting `init.parent_names = (parent_name ? &parent_name : NULL);`]

+ const char *parent_names[1];
[...]
+ of_clk_parent_fill(np, parent_names, 1);
+ init.parent_names = parent_names;

Is it what you're asking?

>> +	init.num_parents = 1;
>> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
>> +		CLK_SET_RATE_PARENT;
>> +
>> +	apad_ck->hw.init = &init;
>> +	apad_ck->regmap = regmap;
>> +
>> +	ret = clk_hw_register(NULL, &apad_ck->hw);
>> +	if (ret)
>> +		kfree(apad_ck);
>> +	else
>> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
> 
> Maybe we should make this register sequence a helper function.
> Seems common.
> 

I can put such an helper in an header if this is what you meant.

>> +}
>> +
>> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
>> +	       "atmel,sama5d2-clk-audio-pll-pad",
>> +	       of_sama5d2_clk_audio_pll_pad_setup);
> 
> We can't have a device driver for this?
> 

Could you elaborate please?

>> diff --git a/drivers/clk/at91/clk-audio-pll-pmc.c b/drivers/clk/at91/clk-audio-pll-pmc.c
>> new file mode 100644
>> index 000000000000..7b0847ed7a4b
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll-pmc.c
[...]
>> +static int clk_audio_pll_pmc_set_rate(struct clk_hw *hw, unsigned long rate,
>> +				      unsigned long parent_rate)
>> +{
>> +	struct clk_audio_pmc *apmc_ck = to_clk_audio_pmc(hw);
>> +
>> +	if (!rate)
>> +		return -EINVAL;
>> +
>> +	pr_debug("A PLL/PMC: %s, rate = %lu (parent_rate = %lu)\n", __func__,
>> +		 rate, parent_rate);
>> +
>> +	apmc_ck->qdpmc = parent_rate / rate - 1;
> 
> Hopefully rate isn't 1 or that goes undefined.
> 

Thanks to operator precedence, the only check to do is rate != 0 (done
few lines above). Division has precedence over substraction.

[...]
>> +static void __init of_sama5d2_clk_audio_pll_pmc_setup(struct device_node *np)
>> +{
>> +	struct clk_audio_pmc *apmc_ck;
>> +	struct clk_init_data init;
>> +	struct regmap *regmap;
>> +	const char *parent_name;
>> +	const char *name = np->name;
>> +	int ret;
>> +
>> +	parent_name = of_clk_get_parent_name(np, 0);
>> +
>> +	of_property_read_string(np, "clock-output-names", &name);
>> +
>> +	regmap = syscon_node_to_regmap(of_get_parent(np));
>> +	if (IS_ERR(regmap))
>> +		return;
>> +
>> +	apmc_ck = kzalloc(sizeof(*apmc_ck), GFP_KERNEL);
>> +	if (!apmc_ck)
>> +		return;
>> +
>> +	init.name = name;
>> +	init.ops = &audio_pll_pmc_ops;
>> +	init.parent_names = (parent_name ? &parent_name : NULL);
> 
> This feels repetitive.
> 
>> +	init.num_parents = 1;
>> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
>> +		CLK_SET_RATE_PARENT;
>> +
>> +	apmc_ck->hw.init = &init;
>> +	apmc_ck->regmap = regmap;
>> +
>> +	ret = clk_hw_register(NULL, &apmc_ck->hw);
>> +	if (ret)
>> +		kfree(apmc_ck);
>> +	else
>> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apmc_ck->hw);
>> +}
>> +
>> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pmc_setup,
>> +	       "atmel,sama5d2-clk-audio-pll-pmc",
>> +	       of_sama5d2_clk_audio_pll_pmc_setup);
> 
> Very
> 

Basically, both share almost the same code but have different formulae
for the rate.

>> diff --git a/drivers/clk/at91/clk-audio-pll.c b/drivers/clk/at91/clk-audio-pll.c
>> new file mode 100644
>> index 000000000000..efc2cb58da85
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll.c
[...]
>> +static unsigned long clk_audio_pll_fout(unsigned long parent_rate,
>> +					unsigned long nd, unsigned long fracr)
>> +{
>> +	unsigned long long fr = (unsigned long long)parent_rate *
>> +						(unsigned long long)fracr;
> 
> We only need one cast here?
> 

Indeed, I'll remove the casting of fracr.

[...]
>> +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
>> +				     unsigned long *parent_rate)
>> +{
>> +	long best_rate = -EINVAL;
>> +	unsigned long fracr, nd;
>> +	int ret;
>> +
>> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
>> +		 *parent_rate);
>> +
>> +	if (rate < AUDIO_PLL_FOUT_MIN)
>> +		rate = AUDIO_PLL_FOUT_MIN;
>> +	else if (rate > AUDIO_PLL_FOUT_MAX)
>> +		rate = AUDIO_PLL_FOUT_MAX;
> 
> Use clamp. Also, did you want to use determine_rate callback and
> clamp the requested rate range?
> 

Didn't know this one, thanks!

I want determine_rate to return a valid rate for the pll so I clamp the
requested rate range in this one. In set_rate, I just tell the user that
any requested rate outside of the valid range is invalid. Does that
answer your question?

[...]
>> +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
>> +{
>> +	struct clk_audio_frac *fck;
>> +	struct clk_init_data init;
>> +	struct regmap *regmap;
>> +	const char *parent_name;
>> +	const char *name = np->name;
>> +	int ret;
>> +
>> +	parent_name = of_clk_get_parent_name(np, 0);
>> +
>> +	of_property_read_string(np, "clock-output-names", &name);
> 
> Any way to not rely on clock-output-names?
> 

I guess we could use the name of the DT node (as it's done in the
variable initialization block above) and not override it by
clock-output-names?

>> +
>> +	regmap = syscon_node_to_regmap(of_get_parent(np));
>> +	if (IS_ERR(regmap))
>> +		return;
>> +
>> +	fck = kzalloc(sizeof(*fck), GFP_KERNEL);
> 
> This variable name looks like f*ck, perhaps name it something
> else. frac?

Sure.

[...]

Thanks,
Quentin

-- 
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 3/9] clk: at91: add audio pll clock drivers
  2017-07-21 22:20     ` Stephen Boyd
@ 2017-07-25 11:30       ` Quentin Schulz
  -1 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-25 11:30 UTC (permalink / raw)
  To: Stephen Boyd
  Cc: mark.rutland, boris.brezillon, alsa-devel, devicetree,
	linux-kernel, mturquette, tiwai, linux-clk, lgirdwood,
	Nicolas Ferre, robh+dt, alexandre.belloni, broonie,
	cyrille.pitchen, linux, perex, thomas.petazzoni,
	linux-arm-kernel

Hi Stephen,

I forgot to answer one of your questions:

On 22/07/2017 00:20, Stephen Boyd wrote:
> On 07/13, Quentin Schulz wrote:
>> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
>> new file mode 100644
>> index 000000000000..10dd6d625696
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
[...]
>> +}
>> +
>> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
>> +	       "atmel,sama5d2-clk-audio-pll-pad",
>> +	       of_sama5d2_clk_audio_pll_pad_setup);
> 
> We can't have a device driver for this?
> 

I tried to work on that but the device does not get probed because the
parent node (pmc) does not register its children. We would need to
modify the DT to add such a support and break DT compat.

As we are speaking about getting rid of clk subnodes (c.f. Rob and
Alexandre answers on the first version of this patch series:
https://patchwork.kernel.org/patch/9805991/) in favor of a clock
controller node that would break the DT compat as well, IMHO it would be
best to break the DT compat as few times as possible and thus, rework
this driver once we switch to a clock controller node.

Thanks,
Quentin
-- 
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-25 11:30       ` Quentin Schulz
  0 siblings, 0 replies; 49+ messages in thread
From: Quentin Schulz @ 2017-07-25 11:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Stephen,

I forgot to answer one of your questions:

On 22/07/2017 00:20, Stephen Boyd wrote:
> On 07/13, Quentin Schulz wrote:
>> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
>> new file mode 100644
>> index 000000000000..10dd6d625696
>> --- /dev/null
>> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
[...]
>> +}
>> +
>> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
>> +	       "atmel,sama5d2-clk-audio-pll-pad",
>> +	       of_sama5d2_clk_audio_pll_pad_setup);
> 
> We can't have a device driver for this?
> 

I tried to work on that but the device does not get probed because the
parent node (pmc) does not register its children. We would need to
modify the DT to add such a support and break DT compat.

As we are speaking about getting rid of clk subnodes (c.f. Rob and
Alexandre answers on the first version of this patch series:
https://patchwork.kernel.org/patch/9805991/) in favor of a clock
controller node that would break the DT compat as well, IMHO it would be
best to break the DT compat as few times as possible and thus, rework
this driver once we switch to a clock controller node.

Thanks,
Quentin
-- 
Quentin Schulz, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 3/9] clk: at91: add audio pll clock drivers
  2017-07-25 11:30       ` Quentin Schulz
@ 2017-07-26  0:14         ` Stephen Boyd
  -1 siblings, 0 replies; 49+ messages in thread
From: Stephen Boyd @ 2017-07-26  0:14 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: mark.rutland, boris.brezillon, alsa-devel, devicetree,
	linux-kernel, mturquette, tiwai, linux-clk, lgirdwood,
	Nicolas Ferre, robh+dt, alexandre.belloni, broonie,
	cyrille.pitchen, linux, perex, thomas.petazzoni,
	linux-arm-kernel

On 07/25, Quentin Schulz wrote:
> Hi Stephen,
> 
> I forgot to answer one of your questions:
> 
> On 22/07/2017 00:20, Stephen Boyd wrote:
> > On 07/13, Quentin Schulz wrote:
> >> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
> >> new file mode 100644
> >> index 000000000000..10dd6d625696
> >> --- /dev/null
> >> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
> [...]
> >> +}
> >> +
> >> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
> >> +	       "atmel,sama5d2-clk-audio-pll-pad",
> >> +	       of_sama5d2_clk_audio_pll_pad_setup);
> > 
> > We can't have a device driver for this?
> > 
> 
> I tried to work on that but the device does not get probed because the
> parent node (pmc) does not register its children. We would need to
> modify the DT to add such a support and break DT compat.
> 
> As we are speaking about getting rid of clk subnodes (c.f. Rob and
> Alexandre answers on the first version of this patch series:
> https://patchwork.kernel.org/patch/9805991/) in favor of a clock
> controller node that would break the DT compat as well, IMHO it would be
> best to break the DT compat as few times as possible and thus, rework
> this driver once we switch to a clock controller node.
> 

Ok. That's fine. Is there work in progress to make a new binding
here?

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-26  0:14         ` Stephen Boyd
  0 siblings, 0 replies; 49+ messages in thread
From: Stephen Boyd @ 2017-07-26  0:14 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/25, Quentin Schulz wrote:
> Hi Stephen,
> 
> I forgot to answer one of your questions:
> 
> On 22/07/2017 00:20, Stephen Boyd wrote:
> > On 07/13, Quentin Schulz wrote:
> >> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
> >> new file mode 100644
> >> index 000000000000..10dd6d625696
> >> --- /dev/null
> >> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
> [...]
> >> +}
> >> +
> >> +CLK_OF_DECLARE(of_sama5d2_clk_audio_pll_pad_setup,
> >> +	       "atmel,sama5d2-clk-audio-pll-pad",
> >> +	       of_sama5d2_clk_audio_pll_pad_setup);
> > 
> > We can't have a device driver for this?
> > 
> 
> I tried to work on that but the device does not get probed because the
> parent node (pmc) does not register its children. We would need to
> modify the DT to add such a support and break DT compat.
> 
> As we are speaking about getting rid of clk subnodes (c.f. Rob and
> Alexandre answers on the first version of this patch series:
> https://patchwork.kernel.org/patch/9805991/) in favor of a clock
> controller node that would break the DT compat as well, IMHO it would be
> best to break the DT compat as few times as possible and thus, rework
> this driver once we switch to a clock controller node.
> 

Ok. That's fine. Is there work in progress to make a new binding
here?

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-26  0:20         ` Stephen Boyd
  0 siblings, 0 replies; 49+ messages in thread
From: Stephen Boyd @ 2017-07-26  0:20 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: mturquette, robh+dt, mark.rutland, lgirdwood, broonie,
	nicolas.ferre, alexandre.belloni, linux, boris.brezillon, perex,
	tiwai, cyrille.pitchen, thomas.petazzoni, linux-clk, devicetree,
	linux-kernel, alsa-devel, linux-arm-kernel, Nicolas Ferre

On 07/24, Quentin Schulz wrote:
> Hi Stephen,
> 
> On 22/07/2017 00:20, Stephen Boyd wrote:
> > On 07/13, Quentin Schulz wrote:
> >> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
> >> new file mode 100644
> >> index 000000000000..10dd6d625696
> >> --- /dev/null
> >> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
> >> +	struct regmap *regmap;
> >> +	const char *parent_name;
> >> +	const char *name = np->name;
> >> +	int ret;
> >> +
> >> +	parent_name = of_clk_get_parent_name(np, 0);
> >> +
> >> +	of_property_read_string(np, "clock-output-names", &name);
> >> +
> >> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> >> +	if (IS_ERR(regmap))
> >> +		return;
> >> +
> >> +	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
> >> +	if (!apad_ck)
> >> +		return;
> >> +
> >> +	init.name = name;
> >> +	init.ops = &audio_pll_pad_ops;
> >> +	init.parent_names = (parent_name ? &parent_name : NULL);
> > 
> > Use of_clk_parent_fill()?
> > 
> 
> [Deleting `parent_name = of_clk_get_parent_name(np, 0);`]
> [Deleting `init.parent_names = (parent_name ? &parent_name : NULL);`]
> 
> + const char *parent_names[1];
> [...]
> + of_clk_parent_fill(np, parent_names, 1);
> + init.parent_names = parent_names;
> 
> Is it what you're asking?
> 

Yes.

> >> +	init.num_parents = 1;
> >> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> >> +		CLK_SET_RATE_PARENT;
> >> +
> >> +	apad_ck->hw.init = &init;
> >> +	apad_ck->regmap = regmap;
> >> +
> >> +	ret = clk_hw_register(NULL, &apad_ck->hw);
> >> +	if (ret)
> >> +		kfree(apad_ck);
> >> +	else
> >> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
> > 
> > Maybe we should make this register sequence a helper function.
> > Seems common.
> > 
> 
> I can put such an helper in an header if this is what you meant.

No big deal either way.

> [...]
> >> +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> >> +				     unsigned long *parent_rate)
> >> +{
> >> +	long best_rate = -EINVAL;
> >> +	unsigned long fracr, nd;
> >> +	int ret;
> >> +
> >> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> >> +		 *parent_rate);
> >> +
> >> +	if (rate < AUDIO_PLL_FOUT_MIN)
> >> +		rate = AUDIO_PLL_FOUT_MIN;
> >> +	else if (rate > AUDIO_PLL_FOUT_MAX)
> >> +		rate = AUDIO_PLL_FOUT_MAX;
> > 
> > Use clamp. Also, did you want to use determine_rate callback and
> > clamp the requested rate range?
> > 
> 
> Didn't know this one, thanks!
> 
> I want determine_rate to return a valid rate for the pll so I clamp the
> requested rate range in this one. In set_rate, I just tell the user that
> any requested rate outside of the valid range is invalid. Does that
> answer your question?

I meant to use the determine rate op here instead of round_rate.
That way, the min/max ranges can be updated here and the core
should figure out that something went out of range. Of course,
the rounded rate needs to be clamped still, but the ranges could
be expressed back as well.

> 
> [...]
> >> +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
> >> +{
> >> +	struct clk_audio_frac *fck;
> >> +	struct clk_init_data init;
> >> +	struct regmap *regmap;
> >> +	const char *parent_name;
> >> +	const char *name = np->name;
> >> +	int ret;
> >> +
> >> +	parent_name = of_clk_get_parent_name(np, 0);
> >> +
> >> +	of_property_read_string(np, "clock-output-names", &name);
> > 
> > Any way to not rely on clock-output-names?
> > 
> 
> I guess we could use the name of the DT node (as it's done in the
> variable initialization block above) and not override it by
> clock-output-names?

If that works, sure.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-26  0:20         ` Stephen Boyd
  0 siblings, 0 replies; 49+ messages in thread
From: Stephen Boyd @ 2017-07-26  0:20 UTC (permalink / raw)
  To: Quentin Schulz
  Cc: mturquette-rdvid1DuHRBWk0Htik3J/w,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, mark.rutland-5wv7dgnIgG8,
	lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A,
	nicolas.ferre-UWL1GkI3JZL3oGB3hsPCZA,
	alexandre.belloni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw,
	boris.brezillon-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	perex-/Fr2/VpizcU, tiwai-IBi9RG/b67k,
	cyrille.pitchen-yU5RGvR974pGWvitb5QawA,
	thomas.petazzoni-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
	linux-clk-u79uwXL29TY76Z2rM5mHXA,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	alsa-devel-K7yf7f+aM1XWsZ/bQMPhNw,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Nicolas Ferre

On 07/24, Quentin Schulz wrote:
> Hi Stephen,
> 
> On 22/07/2017 00:20, Stephen Boyd wrote:
> > On 07/13, Quentin Schulz wrote:
> >> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
> >> new file mode 100644
> >> index 000000000000..10dd6d625696
> >> --- /dev/null
> >> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
> >> +	struct regmap *regmap;
> >> +	const char *parent_name;
> >> +	const char *name = np->name;
> >> +	int ret;
> >> +
> >> +	parent_name = of_clk_get_parent_name(np, 0);
> >> +
> >> +	of_property_read_string(np, "clock-output-names", &name);
> >> +
> >> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> >> +	if (IS_ERR(regmap))
> >> +		return;
> >> +
> >> +	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
> >> +	if (!apad_ck)
> >> +		return;
> >> +
> >> +	init.name = name;
> >> +	init.ops = &audio_pll_pad_ops;
> >> +	init.parent_names = (parent_name ? &parent_name : NULL);
> > 
> > Use of_clk_parent_fill()?
> > 
> 
> [Deleting `parent_name = of_clk_get_parent_name(np, 0);`]
> [Deleting `init.parent_names = (parent_name ? &parent_name : NULL);`]
> 
> + const char *parent_names[1];
> [...]
> + of_clk_parent_fill(np, parent_names, 1);
> + init.parent_names = parent_names;
> 
> Is it what you're asking?
> 

Yes.

> >> +	init.num_parents = 1;
> >> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> >> +		CLK_SET_RATE_PARENT;
> >> +
> >> +	apad_ck->hw.init = &init;
> >> +	apad_ck->regmap = regmap;
> >> +
> >> +	ret = clk_hw_register(NULL, &apad_ck->hw);
> >> +	if (ret)
> >> +		kfree(apad_ck);
> >> +	else
> >> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
> > 
> > Maybe we should make this register sequence a helper function.
> > Seems common.
> > 
> 
> I can put such an helper in an header if this is what you meant.

No big deal either way.

> [...]
> >> +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> >> +				     unsigned long *parent_rate)
> >> +{
> >> +	long best_rate = -EINVAL;
> >> +	unsigned long fracr, nd;
> >> +	int ret;
> >> +
> >> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> >> +		 *parent_rate);
> >> +
> >> +	if (rate < AUDIO_PLL_FOUT_MIN)
> >> +		rate = AUDIO_PLL_FOUT_MIN;
> >> +	else if (rate > AUDIO_PLL_FOUT_MAX)
> >> +		rate = AUDIO_PLL_FOUT_MAX;
> > 
> > Use clamp. Also, did you want to use determine_rate callback and
> > clamp the requested rate range?
> > 
> 
> Didn't know this one, thanks!
> 
> I want determine_rate to return a valid rate for the pll so I clamp the
> requested rate range in this one. In set_rate, I just tell the user that
> any requested rate outside of the valid range is invalid. Does that
> answer your question?

I meant to use the determine rate op here instead of round_rate.
That way, the min/max ranges can be updated here and the core
should figure out that something went out of range. Of course,
the rounded rate needs to be clamped still, but the ranges could
be expressed back as well.

> 
> [...]
> >> +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
> >> +{
> >> +	struct clk_audio_frac *fck;
> >> +	struct clk_init_data init;
> >> +	struct regmap *regmap;
> >> +	const char *parent_name;
> >> +	const char *name = np->name;
> >> +	int ret;
> >> +
> >> +	parent_name = of_clk_get_parent_name(np, 0);
> >> +
> >> +	of_property_read_string(np, "clock-output-names", &name);
> > 
> > Any way to not rely on clock-output-names?
> > 
> 
> I guess we could use the name of the DT node (as it's done in the
> variable initialization block above) and not override it by
> clock-output-names?

If that works, sure.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v3 3/9] clk: at91: add audio pll clock drivers
@ 2017-07-26  0:20         ` Stephen Boyd
  0 siblings, 0 replies; 49+ messages in thread
From: Stephen Boyd @ 2017-07-26  0:20 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/24, Quentin Schulz wrote:
> Hi Stephen,
> 
> On 22/07/2017 00:20, Stephen Boyd wrote:
> > On 07/13, Quentin Schulz wrote:
> >> diff --git a/drivers/clk/at91/clk-audio-pll-pad.c b/drivers/clk/at91/clk-audio-pll-pad.c
> >> new file mode 100644
> >> index 000000000000..10dd6d625696
> >> --- /dev/null
> >> +++ b/drivers/clk/at91/clk-audio-pll-pad.c
> >> +	struct regmap *regmap;
> >> +	const char *parent_name;
> >> +	const char *name = np->name;
> >> +	int ret;
> >> +
> >> +	parent_name = of_clk_get_parent_name(np, 0);
> >> +
> >> +	of_property_read_string(np, "clock-output-names", &name);
> >> +
> >> +	regmap = syscon_node_to_regmap(of_get_parent(np));
> >> +	if (IS_ERR(regmap))
> >> +		return;
> >> +
> >> +	apad_ck = kzalloc(sizeof(*apad_ck), GFP_KERNEL);
> >> +	if (!apad_ck)
> >> +		return;
> >> +
> >> +	init.name = name;
> >> +	init.ops = &audio_pll_pad_ops;
> >> +	init.parent_names = (parent_name ? &parent_name : NULL);
> > 
> > Use of_clk_parent_fill()?
> > 
> 
> [Deleting `parent_name = of_clk_get_parent_name(np, 0);`]
> [Deleting `init.parent_names = (parent_name ? &parent_name : NULL);`]
> 
> + const char *parent_names[1];
> [...]
> + of_clk_parent_fill(np, parent_names, 1);
> + init.parent_names = parent_names;
> 
> Is it what you're asking?
> 

Yes.

> >> +	init.num_parents = 1;
> >> +	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE |
> >> +		CLK_SET_RATE_PARENT;
> >> +
> >> +	apad_ck->hw.init = &init;
> >> +	apad_ck->regmap = regmap;
> >> +
> >> +	ret = clk_hw_register(NULL, &apad_ck->hw);
> >> +	if (ret)
> >> +		kfree(apad_ck);
> >> +	else
> >> +		of_clk_add_hw_provider(np, of_clk_hw_simple_get, &apad_ck->hw);
> > 
> > Maybe we should make this register sequence a helper function.
> > Seems common.
> > 
> 
> I can put such an helper in an header if this is what you meant.

No big deal either way.

> [...]
> >> +static long clk_audio_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> >> +				     unsigned long *parent_rate)
> >> +{
> >> +	long best_rate = -EINVAL;
> >> +	unsigned long fracr, nd;
> >> +	int ret;
> >> +
> >> +	pr_debug("A PLL: %s, rate = %lu (parent_rate = %lu)\n", __func__, rate,
> >> +		 *parent_rate);
> >> +
> >> +	if (rate < AUDIO_PLL_FOUT_MIN)
> >> +		rate = AUDIO_PLL_FOUT_MIN;
> >> +	else if (rate > AUDIO_PLL_FOUT_MAX)
> >> +		rate = AUDIO_PLL_FOUT_MAX;
> > 
> > Use clamp. Also, did you want to use determine_rate callback and
> > clamp the requested rate range?
> > 
> 
> Didn't know this one, thanks!
> 
> I want determine_rate to return a valid rate for the pll so I clamp the
> requested rate range in this one. In set_rate, I just tell the user that
> any requested rate outside of the valid range is invalid. Does that
> answer your question?

I meant to use the determine rate op here instead of round_rate.
That way, the min/max ranges can be updated here and the core
should figure out that something went out of range. Of course,
the rounded rate needs to be clamped still, but the ranges could
be expressed back as well.

> 
> [...]
> >> +static void __init of_sama5d2_clk_audio_pll_setup(struct device_node *np)
> >> +{
> >> +	struct clk_audio_frac *fck;
> >> +	struct clk_init_data init;
> >> +	struct regmap *regmap;
> >> +	const char *parent_name;
> >> +	const char *name = np->name;
> >> +	int ret;
> >> +
> >> +	parent_name = of_clk_get_parent_name(np, 0);
> >> +
> >> +	of_property_read_string(np, "clock-output-names", &name);
> > 
> > Any way to not rely on clock-output-names?
> > 
> 
> I guess we could use the name of the DT node (as it's done in the
> variable initialization block above) and not override it by
> clock-output-names?

If that works, sure.

-- 
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
a Linux Foundation Collaborative Project

^ permalink raw reply	[flat|nested] 49+ messages in thread

end of thread, other threads:[~2017-07-26  0:20 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-13  7:49 [PATCH v3 0/9] add support for Sama5d2 audio PLLs and enable ClassD Quentin Schulz
2017-07-13  7:49 ` Quentin Schulz
2017-07-13  7:49 ` [PATCH v3 1/9] clk: at91: clk-generated: remove useless divisor loop Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-13  7:49 ` [PATCH v3 2/9] clk: at91: add audio plls to the compatible list in DT binding Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-19  6:31   ` Boris Brezillon
2017-07-19  6:31     ` Boris Brezillon
2017-07-19  6:31     ` Boris Brezillon
2017-07-21 22:21   ` Stephen Boyd
2017-07-21 22:21     ` Stephen Boyd
2017-07-13  7:49 ` [PATCH v3 3/9] clk: at91: add audio pll clock drivers Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-19  6:31   ` Boris Brezillon
2017-07-19  6:31     ` Boris Brezillon
2017-07-21 22:20   ` Stephen Boyd
2017-07-21 22:20     ` Stephen Boyd
2017-07-21 22:20     ` Stephen Boyd
2017-07-24  8:37     ` Quentin Schulz
2017-07-24  8:37       ` Quentin Schulz
2017-07-24  8:37       ` Quentin Schulz
2017-07-26  0:20       ` Stephen Boyd
2017-07-26  0:20         ` Stephen Boyd
2017-07-26  0:20         ` Stephen Boyd
2017-07-25 11:30     ` Quentin Schulz
2017-07-25 11:30       ` Quentin Schulz
2017-07-26  0:14       ` Stephen Boyd
2017-07-26  0:14         ` Stephen Boyd
2017-07-13  7:49 ` [PATCH v3 4/9] ARM: dts: at91: sama5d2: add classd nodes Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-13  7:49 ` [PATCH v3 5/9] clk: at91: clk-generated: create function to find best_diff Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-13  7:49 ` [PATCH v3 6/9] clk: at91: clk-generated: make gclk determine audio_pll rate Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-13  7:49 ` [PATCH v3 7/9] ASoC: atmel-classd: remove aclk clock from DT binding Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-13  7:49 ` [PATCH v3 8/9] ASoC: atmel-classd: remove aclk clock Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-13  7:49 ` [PATCH v3 9/9] ARM: dts: at91: sama5d2_xplained: add pin muxing and enable classd Quentin Schulz
2017-07-13  7:49   ` Quentin Schulz
2017-07-19  6:28 ` [PATCH v3 0/9] add support for Sama5d2 audio PLLs and enable ClassD Quentin Schulz
2017-07-19  6:28   ` Quentin Schulz
2017-07-19 10:19   ` Mark Brown
2017-07-19 10:19     ` Mark Brown

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.