All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 00/22] clk: at91: add sama7g5 support
@ 2020-07-29 14:51 Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 01/22] clk: check hw and hw->dev before dereference it Claudiu Beznea
                   ` (21 more replies)
  0 siblings, 22 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

The purpose of this series is to add clock support for SAMA7G5.
Allong with this, clock drivers were switched to CCF and aligned
with their corresponding versions present in Linux.
Some changes were done for CCF, patches 1, 2, 4, 5, 6 (I don't know
if they were as is by intention of a fixes tag is needed in there).

Also patch 3/22 has been added to support clock re-reparenting (this
is minimal support and hope it doesn't break anything if used).

Thank you,
Claudiu Beznea


Claudiu Beznea (22):
  clk: check hw and hw->dev before dereference it
  clk: check pointer returned by dev_get_parent()
  dm: core: add support for device re-parenting
  clk: bind clk to new parent device
  clk: do not disable clock if it is critical
  clk: get clock pointer before proceeding
  clk: at91: add pre-requisite headers for AT91 clock architecture
  clk: at91: pmc: add helpers for clock drivers
  clk: at91: move clock code to compat.c
  clk: at91: sckc: add driver compatible with ccf
  clk: at91: clk-main: add driver compatible with ccf
  clk: at91: sam9x60-pll: add driver compatible with ccf
  clk: at91: clk-master: add driver compatible with ccf
  clk: at91: clk-master: add support for sama7g5
  clk: at91: clk-utmi: add driver compatible with ccf
  clk: at91: clk-utmi: add support for sama7g5
  clk: at91: clk-programmable: add driver compatible with ccf
  clk: at91: clk-system: add driver compatible with ccf
  clk: at91: clk-peripheral: add driver compatible with ccf
  clk: at91: clk-generic: add driver compatible with ccf
  clk: at91: pmc: add generic clock ops
  clk: at91: sama7g5: add clock support

 drivers/clk/at91/Kconfig            |    7 +
 drivers/clk/at91/Makefile           |   15 +-
 drivers/clk/at91/clk-generated.c    |  178 -----
 drivers/clk/at91/clk-generic.c      |  202 +++++
 drivers/clk/at91/clk-h32mx.c        |   56 --
 drivers/clk/at91/clk-main.c         |  381 +++++++++-
 drivers/clk/at91/clk-master.c       |  331 ++++++++-
 drivers/clk/at91/clk-peripheral.c   |  291 ++++++--
 drivers/clk/at91/clk-plla.c         |   54 --
 drivers/clk/at91/clk-plladiv.c      |   85 ---
 drivers/clk/at91/clk-programmable.c |  208 ++++++
 drivers/clk/at91/clk-sam9x60-pll.c  |  442 +++++++++++
 drivers/clk/at91/clk-slow.c         |   36 -
 drivers/clk/at91/clk-system.c       |  143 ++--
 drivers/clk/at91/clk-usb.c          |  147 ----
 drivers/clk/at91/clk-utmi.c         |  234 ++++--
 drivers/clk/at91/compat.c           | 1023 +++++++++++++++++++++++++
 drivers/clk/at91/pmc.c              |  218 +++---
 drivers/clk/at91/pmc.h              |  140 +++-
 drivers/clk/at91/sama7g5.c          | 1401 +++++++++++++++++++++++++++++++++++
 drivers/clk/at91/sckc.c             |  169 ++++-
 drivers/clk/clk-uclass.c            |   58 +-
 drivers/clk/clk.c                   |    3 +
 drivers/core/device.c               |   26 +
 include/dm/device-internal.h        |    9 +
 include/dt-bindings/clk/at91.h      |   22 +
 include/linux/clk/at91_pmc.h        |  247 ++++++
 27 files changed, 5202 insertions(+), 924 deletions(-)
 delete mode 100644 drivers/clk/at91/clk-generated.c
 create mode 100644 drivers/clk/at91/clk-generic.c
 delete mode 100644 drivers/clk/at91/clk-h32mx.c
 delete mode 100644 drivers/clk/at91/clk-plla.c
 delete mode 100644 drivers/clk/at91/clk-plladiv.c
 create mode 100644 drivers/clk/at91/clk-programmable.c
 create mode 100644 drivers/clk/at91/clk-sam9x60-pll.c
 delete mode 100644 drivers/clk/at91/clk-slow.c
 delete mode 100644 drivers/clk/at91/clk-usb.c
 create mode 100644 drivers/clk/at91/compat.c
 create mode 100644 drivers/clk/at91/sama7g5.c
 create mode 100644 include/dt-bindings/clk/at91.h
 create mode 100644 include/linux/clk/at91_pmc.h

-- 
2.7.4

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

* [PATCH 01/22] clk: check hw and hw->dev before dereference it
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-08-04  2:00   ` Simon Glass
  2020-07-29 14:51 ` [PATCH 02/22] clk: check pointer returned by dev_get_parent() Claudiu Beznea
                   ` (20 subsequent siblings)
  21 siblings, 1 reply; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Check hw and hw->dev before dereference it.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/clk.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 0f55ba751c0f..9fa18e342eaf 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -57,6 +57,9 @@ ulong clk_generic_get_rate(struct clk *clk)
 
 const char *clk_hw_get_name(const struct clk *hw)
 {
+	if (!hw || !hw->dev)
+		return NULL;
+
 	return hw->dev->name;
 }
 
-- 
2.7.4

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

* [PATCH 02/22] clk: check pointer returned by dev_get_parent()
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 01/22] clk: check hw and hw->dev before dereference it Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-08-04  2:00   ` Simon Glass
  2020-07-29 14:51 ` [PATCH 03/22] dm: core: add support for device re-parenting Claudiu Beznea
                   ` (19 subsequent siblings)
  21 siblings, 1 reply; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Check pointer returned by dev_get_parent().

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/clk-uclass.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index 70df9d410f4c..aa1f11a27c41 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -459,6 +459,9 @@ struct clk *clk_get_parent(struct clk *clk)
 		return NULL;
 
 	pdev = dev_get_parent(clk->dev);
+	if (!pdev)
+		return ERR_PTR(-ENOMEM);
+
 	pclk = dev_get_clk_ptr(pdev);
 	if (!pclk)
 		return ERR_PTR(-ENODEV);
-- 
2.7.4

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

* [PATCH 03/22] dm: core: add support for device re-parenting
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 01/22] clk: check hw and hw->dev before dereference it Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 02/22] clk: check pointer returned by dev_get_parent() Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-08-04  2:00   ` Simon Glass
  2020-07-29 14:51 ` [PATCH 04/22] clk: bind clk to new parent device Claudiu Beznea
                   ` (18 subsequent siblings)
  21 siblings, 1 reply; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

In common clock framework the relation b/w parent and child clocks is
determined based on the udevice parent/child information. A clock
parent could be changed based on devices needs. In case this is happen
the functionalities for clock who's parent is changed are broken. Add
a function that reparent a device. This will be used in clk-uclass.c
to reparent a clock device.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/core/device.c        | 26 ++++++++++++++++++++++++++
 include/dm/device-internal.h |  9 +++++++++
 2 files changed, 35 insertions(+)

diff --git a/drivers/core/device.c b/drivers/core/device.c
index a7408d9c76c6..f149d55ac1e1 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -267,6 +267,32 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
 			devp);
 }
 
+int device_reparent(struct udevice *dev, struct udevice *new_parent)
+{
+	struct udevice *cparent;
+	struct udevice *pos, *n;
+
+	if (!dev || !new_parent)
+		return -EINVAL;
+
+	if (!dev->parent)
+		return -ENODEV;
+
+	list_for_each_entry_safe(pos, n, &dev->parent->child_head,
+				 sibling_node) {
+		if (pos->driver != dev->driver)
+			continue;
+
+		list_del(&dev->sibling_node);
+		list_add_tail(&dev->sibling_node, &new_parent->child_head);
+		dev->parent = new_parent;
+
+		return 0;
+	}
+
+	return -ENODEV;
+}
+
 static void *alloc_priv(int size, uint flags)
 {
 	void *priv;
diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h
index 294d6c18105a..c5d7ec0650f9 100644
--- a/include/dm/device-internal.h
+++ b/include/dm/device-internal.h
@@ -84,6 +84,15 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
 			const struct driver_info *info, struct udevice **devp);
 
 /**
+ * device_reparent: reparent the device to a new parent
+ *
+ * @dev: pointer to device to be reparented
+ * @new_parent: pointer to new parent device
+ * @return 0 if OK, -ve on error
+ */
+int device_reparent(struct udevice *dev, struct udevice *new_parent);
+
+/**
  * device_ofdata_to_platdata() - Read platform data for a device
  *
  * Read platform data for a device (typically from the device tree) so that
-- 
2.7.4

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

* [PATCH 04/22] clk: bind clk to new parent device
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (2 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 03/22] dm: core: add support for device re-parenting Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-08-04  2:00   ` Simon Glass
  2020-07-29 14:51 ` [PATCH 05/22] clk: do not disable clock if it is critical Claudiu Beznea
                   ` (17 subsequent siblings)
  21 siblings, 1 reply; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Clock re-parenting is not binding the clock's device to its new
parent device, it only calls the clock's ops->set_parent() API. The
changes in this commit re-parent the clock device to its new parent
so that subsequent operations like clk_get_parent() to point to the
proper parent.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/clk-uclass.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index aa1f11a27c41..b390a6b01c06 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -14,6 +14,7 @@
 #include <errno.h>
 #include <log.h>
 #include <malloc.h>
+#include <dm/device-internal.h>
 #include <dm/devres.h>
 #include <dm/read.h>
 #include <linux/bug.h>
@@ -511,6 +512,7 @@ ulong clk_set_rate(struct clk *clk, ulong rate)
 int clk_set_parent(struct clk *clk, struct clk *parent)
 {
 	const struct clk_ops *ops;
+	int ret;
 
 	debug("%s(clk=%p, parent=%p)\n", __func__, clk, parent);
 	if (!clk_valid(clk))
@@ -520,7 +522,14 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
 	if (!ops->set_parent)
 		return -ENOSYS;
 
-	return ops->set_parent(clk, parent);
+	ret = ops->set_parent(clk, parent);
+	if (ret)
+		return ret;
+
+	if (CONFIG_IS_ENABLED(CLK_CCF))
+		ret = device_reparent(clk->dev, parent->dev);
+
+	return ret;
 }
 
 int clk_enable(struct clk *clk)
-- 
2.7.4

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

* [PATCH 05/22] clk: do not disable clock if it is critical
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (3 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 04/22] clk: bind clk to new parent device Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-08-04  2:00   ` Simon Glass
  2020-07-29 14:51 ` [PATCH 06/22] clk: get clock pointer before proceeding Claudiu Beznea
                   ` (16 subsequent siblings)
  21 siblings, 1 reply; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Do not disable clock if it is a critical one.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/clk-uclass.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index b390a6b01c06..958a9490bee2 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -605,6 +605,9 @@ int clk_disable(struct clk *clk)
 
 	if (CONFIG_IS_ENABLED(CLK_CCF)) {
 		if (clk->id && !clk_get_by_id(clk->id, &clkp)) {
+			if (clkp->flags & CLK_IS_CRITICAL)
+				return 0;
+
 			if (clkp->enable_count == 0) {
 				printf("clk %s already disabled\n",
 				       clkp->dev->name);
-- 
2.7.4

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

* [PATCH 06/22] clk: get clock pointer before proceeding
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (4 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 05/22] clk: do not disable clock if it is critical Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-08-04  2:00   ` Simon Glass
  2020-07-29 14:51 ` [PATCH 07/22] clk: at91: add pre-requisite headers for AT91 clock architecture Claudiu Beznea
                   ` (15 subsequent siblings)
  21 siblings, 1 reply; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

clk_get_by_indexed_prop() retrieves a clock with dev member being set
with the pointer to the udevice for the clock controller driver. But
in case of CCF each struct clk object has set in dev member the reference
to its parent (the root of the clock tree is a fixed clock, every
node in clock tree is a clock registered with clk_register()). In this
case the subsequent operations like dev_get_clk_ptr() on clocks
retrieved by clk_get_by_indexed_prop() will fail. For this, get the
pointer to the proper clock registered (with clk_register()) using
clk_get_by_id() before proceeding.

Fixes: 1d7993d1d0ef ("clk: Port Linux common clock framework [CCF] for imx6q to U-boot (tag: v5.1.12)")
Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/clk-uclass.c | 41 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 37 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
index 958a9490bee2..8f926aad12cf 100644
--- a/drivers/clk/clk-uclass.c
+++ b/drivers/clk/clk-uclass.c
@@ -186,7 +186,7 @@ bulk_get_err:
 
 static int clk_set_default_parents(struct udevice *dev, int stage)
 {
-	struct clk clk, parent_clk;
+	struct clk clk, parent_clk, *c, *p;
 	int index;
 	int num_parents;
 	int ret;
@@ -212,6 +212,17 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
 			return ret;
 		}
 
+		if (CONFIG_IS_ENABLED(CLK_CCF)) {
+			ret = clk_get_by_id(parent_clk.id, &p);
+			if (ret) {
+				debug("%s(): could not get parent clock pointer, id %lu, for %s\n",
+				      __func__, parent_clk.id, dev_read_name(dev));
+				return ret;
+			}
+		} else {
+			p = &parent_clk;
+		}
+
 		ret = clk_get_by_indexed_prop(dev, "assigned-clocks",
 					      index, &clk);
 		if (ret) {
@@ -231,7 +242,18 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
 			/* do not setup twice the parent clocks */
 			continue;
 
-		ret = clk_set_parent(&clk, &parent_clk);
+		if (CONFIG_IS_ENABLED(CLK_CCF)) {
+			ret = clk_get_by_id(clk.id, &c);
+			if (ret) {
+				debug("%s(): could not get clock pointer, id %lu, for %s\n",
+				      __func__, clk.id, dev_read_name(dev));
+				return ret;
+			}
+		} else {
+			c = &clk;
+		}
+
+		ret = clk_set_parent(c, p);
 		/*
 		 * Not all drivers may support clock-reparenting (as of now).
 		 * Ignore errors due to this.
@@ -251,7 +273,7 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
 
 static int clk_set_default_rates(struct udevice *dev, int stage)
 {
-	struct clk clk;
+	struct clk clk, *c;
 	int index;
 	int num_rates;
 	int size;
@@ -295,7 +317,18 @@ static int clk_set_default_rates(struct udevice *dev, int stage)
 			/* do not setup twice the parent clocks */
 			continue;
 
-		ret = clk_set_rate(&clk, rates[index]);
+		if (CONFIG_IS_ENABLED(CLK_CCF)) {
+			ret = clk_get_by_id(clk.id, &c);
+			if (ret) {
+				debug("%s(): could not get clock pointer, id %lu, for %s\n",
+				      __func__, clk.id, dev_read_name(dev));
+				return ret;
+			}
+		} else {
+			c = &clk;
+		}
+
+		ret = clk_set_rate(c, rates[index]);
 
 		if (ret < 0) {
 			debug("%s: failed to set rate on clock index %d (%ld) for %s\n",
-- 
2.7.4

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

* [PATCH 07/22] clk: at91: add pre-requisite headers for AT91 clock architecture
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (5 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 06/22] clk: get clock pointer before proceeding Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 08/22] clk: at91: pmc: add helpers for clock drivers Claudiu Beznea
                   ` (14 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add pre-requisite headers for AT91 clock architecture. These
are based on already present files on Linux and will be used
by following commits for AT91 CCF clock drivers.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 include/dt-bindings/clk/at91.h |  22 ++++
 include/linux/clk/at91_pmc.h   | 247 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 269 insertions(+)
 create mode 100644 include/dt-bindings/clk/at91.h
 create mode 100644 include/linux/clk/at91_pmc.h

diff --git a/include/dt-bindings/clk/at91.h b/include/dt-bindings/clk/at91.h
new file mode 100644
index 000000000000..e30756b2804a
--- /dev/null
+++ b/include/dt-bindings/clk/at91.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * This header provides constants for AT91 pmc status.
+ * The constants defined in this header are being used in dts and PMC code.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on include/dt-bindings/clock/at91.h on Linux.
+ */
+
+#ifndef _DT_BINDINGS_CLK_AT91_H
+#define _DT_BINDINGS_CLK_AT91_H
+
+#define PMC_TYPE_CORE		1
+#define PMC_TYPE_SYSTEM		2
+#define PMC_TYPE_PERIPHERAL	3
+#define PMC_TYPE_GCK		4
+#define PMC_TYPE_SLOW		5
+
+#endif
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
new file mode 100644
index 000000000000..ee39e72e2b39
--- /dev/null
+++ b/include/linux/clk/at91_pmc.h
@@ -0,0 +1,247 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Power Management Controller (PMC) - System peripherals registers.
+ *
+ * Copyright (C) 2005 Ivan Kokshaysky
+ * Copyright (C) SAN People
+ *
+ * Based on AT91RM9200 datasheet revision E.
+ * Based on include/linux/clk/at91_pmc.h on Linux.
+ */
+
+#ifndef AT91_PMC_H_
+#define AT91_PMC_H_
+
+#define AT91_PMC_V1		(1)			/* PMC version 1 */
+#define AT91_PMC_V2		(2)			/* PMC version 2 [SAM9X60] */
+
+#define	AT91_PMC_SCER		0x00			/* System Clock Enable Register */
+#define	AT91_PMC_SCDR		0x04			/* System Clock Disable Register */
+
+#define	AT91_PMC_SCSR		0x08			/* System Clock Status Register */
+#define		AT91_PMC_PCK		(1 <<  0)		/* Processor Clock */
+#define		AT91RM9200_PMC_UDP	(1 <<  1)		/* USB Devcice Port Clock [AT91RM9200 only] */
+#define		AT91RM9200_PMC_MCKUDP	(1 <<  2)		/* USB Device Port Master Clock Automatic Disable on Suspend [AT91RM9200 only] */
+#define		AT91RM9200_PMC_UHP	(1 <<  4)		/* USB Host Port Clock [AT91RM9200 only] */
+#define		AT91SAM926x_PMC_UHP	(1 <<  6)		/* USB Host Port Clock [AT91SAM926x only] */
+#define		AT91SAM926x_PMC_UDP	(1 <<  7)		/* USB Devcice Port Clock [AT91SAM926x only] */
+#define		AT91_PMC_PCK0		(1 <<  8)		/* Programmable Clock 0 */
+#define		AT91_PMC_PCK1		(1 <<  9)		/* Programmable Clock 1 */
+#define		AT91_PMC_PCK2		(1 << 10)		/* Programmable Clock 2 */
+#define		AT91_PMC_PCK3		(1 << 11)		/* Programmable Clock 3 */
+#define		AT91_PMC_PCK4		(1 << 12)		/* Programmable Clock 4 [AT572D940HF only] */
+#define		AT91_PMC_HCK0		(1 << 16)		/* AHB Clock (USB host) [AT91SAM9261 only] */
+#define		AT91_PMC_HCK1		(1 << 17)		/* AHB Clock (LCD) [AT91SAM9261 only] */
+
+#define AT91_PMC_PLL_CTRL0		0x0C		/* PLL Control Register 0 [for SAM9X60] */
+#define		AT91_PMC_PLL_CTRL0_ENPLL	(1 << 28)	/* Enable PLL */
+#define		AT91_PMC_PLL_CTRL0_ENPLLCK	(1 << 29)	/* Enable PLL clock for PMC */
+#define		AT91_PMC_PLL_CTRL0_ENLOCK	(1 << 31)	/* Enable PLL lock */
+
+#define AT91_PMC_PLL_CTRL1		0x10		/* PLL Control Register 1 [for SAM9X60] */
+
+#define	AT91_PMC_PCER		0x10			/* Peripheral Clock Enable Register */
+#define	AT91_PMC_PCDR		0x14			/* Peripheral Clock Disable Register */
+#define	AT91_PMC_PCSR		0x18			/* Peripheral Clock Status Register */
+
+#define AT91_PMC_PLL_ACR	0x18			/* PLL Analog Control Register [for SAM9X60] */
+#define		AT91_PMC_PLL_ACR_DEFAULT_UPLL	0x12020010UL	/* Default PLL ACR value for UPLL */
+#define		AT91_PMC_PLL_ACR_DEFAULT_PLLA	0x00020010UL	/* Default PLL ACR value for PLLA */
+#define		AT91_PMC_PLL_ACR_UTMIVR		(1 << 12)	/* UPLL Voltage regulator Control */
+#define		AT91_PMC_PLL_ACR_UTMIBG		(1 << 13)	/* UPLL Bandgap Control */
+
+#define	AT91_CKGR_UCKR		0x1C			/* UTMI Clock Register [some SAM9] */
+#define		AT91_PMC_UPLLEN		(1   << 16)		/* UTMI PLL Enable */
+#define		AT91_PMC_UPLLCOUNT	(0xf << 20)		/* UTMI PLL Start-up Time */
+#define		AT91_PMC_BIASEN		(1   << 24)		/* UTMI BIAS Enable */
+#define		AT91_PMC_BIASCOUNT	(0xf << 28)		/* UTMI BIAS Start-up Time */
+
+#define AT91_PMC_PLL_UPDT		0x1C		/* PMC PLL update register [for SAM9X60] */
+#define		AT91_PMC_PLL_UPDT_ID_MSK	(0xf)
+#define		AT91_PMC_PLL_UPDT_UPDATE	(1 << 8)	/* Update PLL settings */
+#define		AT91_PMC_PLL_UPDT_ID		(1 << 0)	/* PLL ID */
+#define		AT91_PMC_PLL_UPDT_STUPTIM(n)	((n) << 16)	/* Startup time */
+#define		AT91_PMC_PMM_UPDT_STUPTIM_MSK	(0xff << 16)
+
+#define	AT91_CKGR_MOR		0x20			/* Main Oscillator Register [not on SAM9RL] */
+#define		AT91_PMC_MOSCEN		(1    <<  0)		/* Main Oscillator Enable */
+#define		AT91_PMC_OSCBYPASS	(1    <<  1)		/* Oscillator Bypass */
+#define		AT91_PMC_WAITMODE	(1    <<  2)		/* Wait Mode Command */
+#define		AT91_PMC_MOSCRCEN	(1    <<  3)		/* Main On-Chip RC Oscillator Enable [some SAM9] */
+#define		AT91_PMC_OSCOUNT	(0xff <<  8)		/* Main Oscillator Start-up Time */
+#define		AT91_PMC_KEY_MASK	(0xff << 16)
+#define		AT91_PMC_KEY		(0x37 << 16)		/* MOR Writing Key */
+#define		AT91_PMC_MOSCSEL	(1    << 24)		/* Main Oscillator Selection [some SAM9] */
+#define		AT91_PMC_CFDEN		(1    << 25)		/* Clock Failure Detector Enable [some SAM9] */
+
+#define	AT91_CKGR_MCFR		0x24			/* Main Clock Frequency Register */
+#define		AT91_PMC_MAINF		(0xffff <<  0)		/* Main Clock Frequency */
+#define		AT91_PMC_MAINRDY	(1	<< 16)		/* Main Clock Ready */
+
+#define	AT91_CKGR_PLLAR		0x28			/* PLL A Register */
+#define	AT91_CKGR_PLLBR		0x2c			/* PLL B Register */
+#define		AT91_PMC_DIV		(0xff  <<  0)		/* Divider */
+#define		AT91_PMC_PLLCOUNT	(0x3f  <<  8)		/* PLL Counter */
+#define		AT91_PMC_OUT		(3     << 14)		/* PLL Clock Frequency Range */
+#define		AT91_PMC_MUL		(0x7ff << 16)		/* PLL Multiplier */
+#define		AT91_PMC_MUL_GET(n)	((n) >> 16 & 0x7ff)
+#define		AT91_PMC3_MUL		(0x7f  << 18)		/* PLL Multiplier [SAMA5 only] */
+#define		AT91_PMC3_MUL_GET(n)	((n) >> 18 & 0x7f)
+#define		AT91_PMC_USBDIV		(3     << 28)		/* USB Divisor (PLLB only) */
+#define			AT91_PMC_USBDIV_1		(0 << 28)
+#define			AT91_PMC_USBDIV_2		(1 << 28)
+#define			AT91_PMC_USBDIV_4		(2 << 28)
+#define		AT91_PMC_USB96M		(1     << 28)		/* Divider by 2 Enable (PLLB only) */
+
+#define AT91_PMC_CPU_CKR	0x28			/* CPU Clock Register */
+
+#define	AT91_PMC_MCKR		0x30			/* Master Clock Register */
+#define		AT91_PMC_CSS		(3 <<  0)		/* Master Clock Selection */
+#define			AT91_PMC_CSS_SLOW		(0 << 0)
+#define			AT91_PMC_CSS_MAIN		(1 << 0)
+#define			AT91_PMC_CSS_PLLA		(2 << 0)
+#define			AT91_PMC_CSS_PLLB		(3 << 0)
+#define			AT91_PMC_CSS_UPLL		(3 << 0)	/* [some SAM9 only] */
+#define		PMC_PRES_OFFSET		2
+#define		AT91_PMC_PRES		(7 <<  PMC_PRES_OFFSET)		/* Master Clock Prescaler */
+#define			AT91_PMC_PRES_1			(0 << PMC_PRES_OFFSET)
+#define			AT91_PMC_PRES_2			(1 << PMC_PRES_OFFSET)
+#define			AT91_PMC_PRES_4			(2 << PMC_PRES_OFFSET)
+#define			AT91_PMC_PRES_8			(3 << PMC_PRES_OFFSET)
+#define			AT91_PMC_PRES_16		(4 << PMC_PRES_OFFSET)
+#define			AT91_PMC_PRES_32		(5 << PMC_PRES_OFFSET)
+#define			AT91_PMC_PRES_64		(6 << PMC_PRES_OFFSET)
+#define		PMC_ALT_PRES_OFFSET	4
+#define		AT91_PMC_ALT_PRES	(7 <<  PMC_ALT_PRES_OFFSET)		/* Master Clock Prescaler [alternate location] */
+#define			AT91_PMC_ALT_PRES_1		(0 << PMC_ALT_PRES_OFFSET)
+#define			AT91_PMC_ALT_PRES_2		(1 << PMC_ALT_PRES_OFFSET)
+#define			AT91_PMC_ALT_PRES_4		(2 << PMC_ALT_PRES_OFFSET)
+#define			AT91_PMC_ALT_PRES_8		(3 << PMC_ALT_PRES_OFFSET)
+#define			AT91_PMC_ALT_PRES_16		(4 << PMC_ALT_PRES_OFFSET)
+#define			AT91_PMC_ALT_PRES_32		(5 << PMC_ALT_PRES_OFFSET)
+#define			AT91_PMC_ALT_PRES_64		(6 << PMC_ALT_PRES_OFFSET)
+#define		AT91_PMC_MDIV		(3 <<  8)		/* Master Clock Division */
+#define			AT91RM9200_PMC_MDIV_1		(0 << 8)	/* [AT91RM9200 only] */
+#define			AT91RM9200_PMC_MDIV_2		(1 << 8)
+#define			AT91RM9200_PMC_MDIV_3		(2 << 8)
+#define			AT91RM9200_PMC_MDIV_4		(3 << 8)
+#define			AT91SAM9_PMC_MDIV_1		(0 << 8)	/* [SAM9 only] */
+#define			AT91SAM9_PMC_MDIV_2		(1 << 8)
+#define			AT91SAM9_PMC_MDIV_4		(2 << 8)
+#define			AT91SAM9_PMC_MDIV_6		(3 << 8)	/* [some SAM9 only] */
+#define			AT91SAM9_PMC_MDIV_3		(3 << 8)	/* [some SAM9 only] */
+#define		AT91_PMC_PDIV		(1 << 12)		/* Processor Clock Division [some SAM9 only] */
+#define			AT91_PMC_PDIV_1			(0 << 12)
+#define			AT91_PMC_PDIV_2			(1 << 12)
+#define		AT91_PMC_PLLADIV2	(1 << 12)		/* PLLA divisor by 2 [some SAM9 only] */
+#define			AT91_PMC_PLLADIV2_OFF		(0 << 12)
+#define			AT91_PMC_PLLADIV2_ON		(1 << 12)
+#define		AT91_PMC_H32MXDIV	BIT(24)
+
+#define AT91_PMC_XTALF		0x34			/* Main XTAL Frequency Register [SAMA7G5 only] */
+
+#define	AT91_PMC_USB		0x38			/* USB Clock Register [some SAM9 only] */
+#define		AT91_PMC_USBS		(0x1 <<  0)		/* USB OHCI Input clock selection */
+#define			AT91_PMC_USBS_PLLA		(0 << 0)
+#define			AT91_PMC_USBS_UPLL		(1 << 0)
+#define			AT91_PMC_USBS_PLLB		(1 << 0)	/* [AT91SAMN12 only] */
+#define		AT91_PMC_OHCIUSBDIV	(0xF <<  8)		/* Divider for USB OHCI Clock */
+#define			AT91_PMC_OHCIUSBDIV_1	(0x0 <<  8)
+#define			AT91_PMC_OHCIUSBDIV_2	(0x1 <<  8)
+
+#define	AT91_PMC_SMD		0x3c			/* Soft Modem Clock Register [some SAM9 only] */
+#define		AT91_PMC_SMDS		(0x1  <<  0)		/* SMD input clock selection */
+#define		AT91_PMC_SMD_DIV	(0x1f <<  8)		/* SMD input clock divider */
+#define		AT91_PMC_SMDDIV(n)	(((n) <<  8) & AT91_PMC_SMD_DIV)
+
+#define	AT91_PMC_PCKR(n)	(0x40 + ((n) * 4))	/* Programmable Clock 0-N Registers */
+#define		AT91_PMC_ALT_PCKR_CSS	(0x7 <<  0)		/* Programmable Clock Source Selection [alternate length] */
+#define			AT91_PMC_CSS_MASTER		(4 << 0)	/* [some SAM9 only] */
+#define		AT91_PMC_CSSMCK		(0x1 <<  8)		/* CSS or Master Clock Selection */
+#define			AT91_PMC_CSSMCK_CSS		(0 << 8)
+#define			AT91_PMC_CSSMCK_MCK		(1 << 8)
+
+#define	AT91_PMC_IER		0x60			/* Interrupt Enable Register */
+#define	AT91_PMC_IDR		0x64			/* Interrupt Disable Register */
+#define	AT91_PMC_SR		0x68			/* Status Register */
+#define		AT91_PMC_MOSCS		(1 <<  0)		/* MOSCS Flag */
+#define		AT91_PMC_LOCKA		(1 <<  1)		/* PLLA Lock */
+#define		AT91_PMC_LOCKB		(1 <<  2)		/* PLLB Lock */
+#define		AT91_PMC_MCKRDY		(1 <<  3)		/* Master Clock */
+#define		AT91_PMC_LOCKU		(1 <<  6)		/* UPLL Lock [some SAM9] */
+#define		AT91_PMC_OSCSEL		(1 <<  7)		/* Slow Oscillator Selection [some SAM9] */
+#define		AT91_PMC_PCK0RDY	(1 <<  8)		/* Programmable Clock 0 */
+#define		AT91_PMC_PCK1RDY	(1 <<  9)		/* Programmable Clock 1 */
+#define		AT91_PMC_PCK2RDY	(1 << 10)		/* Programmable Clock 2 */
+#define		AT91_PMC_PCK3RDY	(1 << 11)		/* Programmable Clock 3 */
+#define		AT91_PMC_MOSCSELS	(1 << 16)		/* Main Oscillator Selection [some SAM9] */
+#define		AT91_PMC_MOSCRCS	(1 << 17)		/* Main On-Chip RC [some SAM9] */
+#define		AT91_PMC_CFDEV		(1 << 18)		/* Clock Failure Detector Event [some SAM9] */
+#define		AT91_PMC_GCKRDY		(1 << 24)		/* Generated Clocks */
+#define		AT91_PMC_MCKXRDY	(1 << 26)		/* Master Clock x [x=1..4] Ready Status */
+#define	AT91_PMC_IMR		0x6c			/* Interrupt Mask Register */
+
+#define AT91_PMC_FSMR		0x70		/* Fast Startup Mode Register */
+#define AT91_PMC_FSTT(n)	BIT(n)
+#define AT91_PMC_RTTAL		BIT(16)
+#define AT91_PMC_RTCAL		BIT(17)		/* RTC Alarm Enable */
+#define AT91_PMC_USBAL		BIT(18)		/* USB Resume Enable */
+#define AT91_PMC_SDMMC_CD	BIT(19)		/* SDMMC Card Detect Enable */
+#define AT91_PMC_LPM		BIT(20)		/* Low-power Mode */
+#define AT91_PMC_RXLP_MCE	BIT(24)		/* Backup UART Receive Enable */
+#define AT91_PMC_ACC_CE		BIT(25)		/* ACC Enable */
+
+#define AT91_PMC_FSPR		0x74		/* Fast Startup Polarity Reg */
+
+#define AT91_PMC_FS_INPUT_MASK  0x7ff
+
+#define AT91_PMC_PLLICPR	0x80			/* PLL Charge Pump Current Register */
+
+#define AT91_PMC_PROT		0xe4			/* Write Protect Mode Register [some SAM9] */
+#define		AT91_PMC_WPEN		(0x1  <<  0)		/* Write Protect Enable */
+#define		AT91_PMC_WPKEY		(0xffffff << 8)		/* Write Protect Key */
+#define		AT91_PMC_PROTKEY	(0x504d43 << 8)		/* Activation Code */
+
+#define AT91_PMC_WPSR		0xe8			/* Write Protect Status Register [some SAM9] */
+#define		AT91_PMC_WPVS		(0x1  <<  0)		/* Write Protect Violation Status */
+#define		AT91_PMC_WPVSRC		(0xffff  <<  8)		/* Write Protect Violation Source */
+
+#define AT91_PMC_PLL_ISR0	0xEC			/* PLL Interrupt Status Register 0 [SAM9X60 only] */
+
+#define AT91_PMC_PCER1		0x100			/* Peripheral Clock Enable Register 1 [SAMA5 only]*/
+#define AT91_PMC_PCDR1		0x104			/* Peripheral Clock Enable Register 1 */
+#define AT91_PMC_PCSR1		0x108			/* Peripheral Clock Enable Register 1 */
+
+#define AT91_PMC_PCR		0x10c			/* Peripheral Control Register [some SAM9 and SAMA5] */
+#define		AT91_PMC_PCR_PID_MASK		0x3f
+#define		AT91_PMC_PCR_CMD		(0x1  <<  12)				/* Command (read=0, write=1) */
+#define		AT91_PMC_PCR_GCKDIV_MASK	GENMASK(27, 20)
+#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.7.4

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

* [PATCH 08/22] clk: at91: pmc: add helpers for clock drivers
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (6 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 07/22] clk: at91: add pre-requisite headers for AT91 clock architecture Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 09/22] clk: at91: move clock code to compat.c Claudiu Beznea
                   ` (13 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add helper for clock drivers. These will be used by following
commits in the process of switching AT91 clock drivers to CCF.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/pmc.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.h | 13 ++++++++
 2 files changed, 104 insertions(+)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 9d9d77d861d7..0984dc321578 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -118,3 +118,94 @@ int at91_clk_probe(struct udevice *dev)
 
 	return 0;
 }
+
+/**
+ * pmc_read() - read content at address base + off into val
+ *
+ * @base: base address
+ * @off: offset to read from
+ * @val: where the content of base + off is stored
+ *
+ * @return: void
+ */
+void pmc_read(void __iomem *base, unsigned int off, unsigned int *val)
+{
+	*val = readl(base + off);
+}
+
+/**
+ * pmc_write() - write content of val at address base + off
+ *
+ * @base: base address
+ * @off: offset to write to
+ * @val: content to be written at base + off
+ *
+ * @return: void
+ */
+void pmc_write(void __iomem *base, unsigned int off, unsigned int val)
+{
+	writel(val, base + off);
+}
+
+/**
+ * pmc_update_bits() - update a set of bits at address base + off
+ *
+ * @base: base address
+ * @off: offset to be updated
+ * @mask: mask of bits to be updated
+ * @bits: the new value to be updated
+ *
+ * @return: void
+ */
+void pmc_update_bits(void __iomem *base, unsigned int off,
+		     unsigned int mask, unsigned int bits)
+{
+	unsigned int tmp;
+
+	tmp = readl(base + off);
+	tmp &= ~mask;
+	writel(tmp | (bits & mask), base + off);
+}
+
+/**
+ * at91_clk_mux_val_to_index() - get parent index in mux table
+ *
+ * @table: clock mux table
+ * @num_parents: clock number of parents
+ * @val: clock id who's mux index should be retrieved
+ *
+ * @return: clock index in mux table or a negative error number in case of
+ *		failure
+ */
+int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val)
+{
+	int i;
+
+	if (!table || !num_parents)
+		return -EINVAL;
+
+	for (i = 0; i < num_parents; i++) {
+		if (table[i] == val)
+			return i;
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * at91_clk_mux_index_to_val() - get parent ID corresponding to an entry in
+ *	clock's mux table
+ *
+ * @table: clock's mux table
+ * @num_parents: clock's number of parents
+ * @index: index in mux table which clock's ID should be retrieved
+ *
+ * @return: clock ID or a negative error number in case of failure
+ */
+int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index)
+{
+	if (!table || !num_parents || index < 0 || index > num_parents)
+		return -EINVAL;
+
+	return table[index];
+}
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 517ba1d6b452..b1ab0a95c855 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -8,6 +8,12 @@
 #define __AT91_PMC_H__
 
 #include <regmap.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+
+/* Keep a range of 256 available clocks for every clock type. */
+#define AT91_TO_CLK_ID(_t, _i)		(((_t) << 8) | ((_i) & 0xff))
+#define AT91_CLK_ID_TO_DID(_i)		((_i) & 0xff)
 
 struct pmc_platdata {
 	struct at91_pmc *reg_base;
@@ -20,4 +26,11 @@ int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name);
 int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args);
 int at91_clk_probe(struct udevice *dev);
 
+int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
+int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
+
+void pmc_read(void __iomem *base, unsigned int off, unsigned int *val);
+void pmc_write(void __iomem *base, unsigned int off, unsigned int val);
+void pmc_update_bits(void __iomem *base, unsigned int off, unsigned int mask,
+			unsigned int bits);
 #endif
-- 
2.7.4

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

* [PATCH 09/22] clk: at91: move clock code to compat.c
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (7 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 08/22] clk: at91: pmc: add helpers for clock drivers Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 10/22] clk: at91: sckc: add driver compatible with ccf Claudiu Beznea
                   ` (12 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Move clock code to compat.c to allow switching to CCF
without mixing CCF code with non CCF code. This prepares the
field for next commits.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/Makefile         |   13 +-
 drivers/clk/at91/clk-generated.c  |  178 -------
 drivers/clk/at91/clk-h32mx.c      |   56 --
 drivers/clk/at91/clk-main.c       |   54 --
 drivers/clk/at91/clk-master.c     |   33 --
 drivers/clk/at91/clk-peripheral.c |  113 ----
 drivers/clk/at91/clk-plla.c       |   54 --
 drivers/clk/at91/clk-plladiv.c    |   85 ---
 drivers/clk/at91/clk-slow.c       |   36 --
 drivers/clk/at91/clk-system.c     |  111 ----
 drivers/clk/at91/clk-usb.c        |  147 ------
 drivers/clk/at91/clk-utmi.c       |  142 -----
 drivers/clk/at91/compat.c         | 1023 +++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.c            |  114 +----
 drivers/clk/at91/pmc.h            |   13 +-
 drivers/clk/at91/sckc.c           |   19 -
 16 files changed, 1030 insertions(+), 1161 deletions(-)
 delete mode 100644 drivers/clk/at91/clk-generated.c
 delete mode 100644 drivers/clk/at91/clk-h32mx.c
 delete mode 100644 drivers/clk/at91/clk-main.c
 delete mode 100644 drivers/clk/at91/clk-master.c
 delete mode 100644 drivers/clk/at91/clk-peripheral.c
 delete mode 100644 drivers/clk/at91/clk-plla.c
 delete mode 100644 drivers/clk/at91/clk-plladiv.c
 delete mode 100644 drivers/clk/at91/clk-slow.c
 delete mode 100644 drivers/clk/at91/clk-system.c
 delete mode 100644 drivers/clk/at91/clk-usb.c
 delete mode 100644 drivers/clk/at91/clk-utmi.c
 create mode 100644 drivers/clk/at91/compat.c
 delete mode 100644 drivers/clk/at91/sckc.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 8c197ff949a8..e2413af40360 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -2,11 +2,8 @@
 # Makefile for at91 specific clk
 #
 
-obj-y += pmc.o sckc.o
-obj-y += clk-slow.o clk-main.o clk-plla.o clk-plladiv.o clk-master.o
-obj-y += clk-system.o clk-peripheral.o
-
-obj-$(CONFIG_AT91_UTMI)		+= clk-utmi.o
-obj-$(CONFIG_AT91_USB_CLK)	+= clk-usb.o
-obj-$(CONFIG_AT91_H32MX)	+= clk-h32mx.o
-obj-$(CONFIG_AT91_GENERIC_CLK)	+= clk-generated.o
+ifdef CONFIG_CLK_CCF
+obj-y += pmc.o
+else
+obj-y += compat.o
+endif
diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
deleted file mode 100644
index c0610940c3be..000000000000
--- a/drivers/clk/at91/clk-generated.c
+++ /dev/null
@@ -1,178 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-#include <log.h>
-#include <malloc.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-DECLARE_GLOBAL_DATA_PTR;
-
-#define GENERATED_SOURCE_MAX	6
-#define GENERATED_MAX_DIV	255
-
-/**
- * generated_clk_bind() - for the generated clock driver
- * Recursively bind its children as clk devices.
- *
- * @return: 0 on success, or negative error code on failure
- */
-static int generated_clk_bind(struct udevice *dev)
-{
-	return at91_clk_sub_device_bind(dev, "generic-clk");
-}
-
-static const struct udevice_id generated_clk_match[] = {
-	{ .compatible = "atmel,sama5d2-clk-generated" },
-	{}
-};
-
-U_BOOT_DRIVER(generated_clk) = {
-	.name = "generated-clk",
-	.id = UCLASS_MISC,
-	.of_match = generated_clk_match,
-	.bind = generated_clk_bind,
-};
-
-/*-------------------------------------------------------------*/
-
-struct generic_clk_priv {
-	u32 num_parents;
-};
-
-static ulong generic_clk_get_rate(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	struct clk parent;
-	ulong clk_rate;
-	u32 tmp, gckdiv;
-	u8 clock_source, parent_index;
-	int ret;
-
-	writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
-	tmp = readl(&pmc->pcr);
-	clock_source = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) &
-		    AT91_PMC_PCR_GCKCSS_MASK;
-	gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK;
-
-	parent_index = clock_source - 1;
-	ret = clk_get_by_index(dev_get_parent(clk->dev), parent_index, &parent);
-	if (ret)
-		return 0;
-
-	clk_rate = clk_get_rate(&parent) / (gckdiv + 1);
-
-	clk_free(&parent);
-
-	return clk_rate;
-}
-
-static ulong generic_clk_set_rate(struct clk *clk, ulong rate)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	struct generic_clk_priv *priv = dev_get_priv(clk->dev);
-	struct clk parent, best_parent;
-	ulong tmp_rate, best_rate = rate, parent_rate;
-	int tmp_diff, best_diff = -1;
-	u32 div, best_div = 0;
-	u8 best_parent_index, best_clock_source = 0;
-	u8 i;
-	u32 tmp;
-	int ret;
-
-	for (i = 0; i < priv->num_parents; i++) {
-		ret = clk_get_by_index(dev_get_parent(clk->dev), i, &parent);
-		if (ret)
-			return ret;
-
-		parent_rate = clk_get_rate(&parent);
-		if (IS_ERR_VALUE(parent_rate))
-			return parent_rate;
-
-		for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
-			tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div);
-			tmp_diff = abs(rate - tmp_rate);
-
-			if (best_diff < 0 || best_diff > tmp_diff) {
-				best_rate = tmp_rate;
-				best_diff = tmp_diff;
-
-				best_div = div - 1;
-				best_parent = parent;
-				best_parent_index = i;
-				best_clock_source = best_parent_index + 1;
-			}
-
-			if (!best_diff || tmp_rate < rate)
-				break;
-		}
-
-		if (!best_diff)
-			break;
-	}
-
-	debug("GCK: best parent: %s, best_rate = %ld, best_div = %d\n",
-	      best_parent.dev->name, best_rate, best_div);
-
-	ret = clk_enable(&best_parent);
-	if (ret)
-		return ret;
-
-	writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
-	tmp = readl(&pmc->pcr);
-	tmp &= ~(AT91_PMC_PCR_GCKDIV | AT91_PMC_PCR_GCKCSS);
-	tmp |= AT91_PMC_PCR_GCKCSS_(best_clock_source) |
-	       AT91_PMC_PCR_CMD_WRITE |
-	       AT91_PMC_PCR_GCKDIV_(best_div) |
-	       AT91_PMC_PCR_GCKEN;
-	writel(tmp, &pmc->pcr);
-
-	while (!(readl(&pmc->sr) & AT91_PMC_GCKRDY))
-		;
-
-	return 0;
-}
-
-static struct clk_ops generic_clk_ops = {
-	.of_xlate = at91_clk_of_xlate,
-	.get_rate = generic_clk_get_rate,
-	.set_rate = generic_clk_set_rate,
-};
-
-static int generic_clk_ofdata_to_platdata(struct udevice *dev)
-{
-	struct generic_clk_priv *priv = dev_get_priv(dev);
-	u32 cells[GENERATED_SOURCE_MAX];
-	u32 num_parents;
-
-	num_parents = fdtdec_get_int_array_count(gd->fdt_blob,
-			dev_of_offset(dev_get_parent(dev)), "clocks", cells,
-			GENERATED_SOURCE_MAX);
-
-	if (!num_parents)
-		return -1;
-
-	priv->num_parents = num_parents;
-
-	return 0;
-}
-
-U_BOOT_DRIVER(generic_clk) = {
-	.name = "generic-clk",
-	.id = UCLASS_CLK,
-	.probe = at91_clk_probe,
-	.ofdata_to_platdata = generic_clk_ofdata_to_platdata,
-	.priv_auto_alloc_size = sizeof(struct generic_clk_priv),
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &generic_clk_ops,
-};
diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c
deleted file mode 100644
index 86bb71f6128b..000000000000
--- a/drivers/clk/at91/clk-h32mx.c
+++ /dev/null
@@ -1,56 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-#include <dm/device_compat.h>
-#include <dm/util.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-DECLARE_GLOBAL_DATA_PTR;
-
-#define H32MX_MAX_FREQ	90000000
-
-static ulong sama5d4_h32mx_clk_get_rate(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	ulong rate = gd->arch.mck_rate_hz;
-
-	if (readl(&pmc->mckr) & AT91_PMC_MCKR_H32MXDIV)
-		rate /= 2;
-
-	if (rate > H32MX_MAX_FREQ)
-		dev_dbg(clk->dev, "H32MX clock is too fast\n");
-
-	return rate;
-}
-
-static struct clk_ops sama5d4_h32mx_clk_ops = {
-	.get_rate = sama5d4_h32mx_clk_get_rate,
-};
-
-static int sama5d4_h32mx_clk_probe(struct udevice *dev)
-{
-	return at91_pmc_core_probe(dev);
-}
-
-static const struct udevice_id sama5d4_h32mx_clk_match[] = {
-	{ .compatible = "atmel,sama5d4-clk-h32mx" },
-	{}
-};
-
-U_BOOT_DRIVER(sama5d4_h32mx_clk) = {
-	.name = "sama5d4-h32mx-clk",
-	.id = UCLASS_CLK,
-	.of_match = sama5d4_h32mx_clk_match,
-	.probe = sama5d4_h32mx_clk_probe,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &sama5d4_h32mx_clk_ops,
-};
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
deleted file mode 100644
index b31a1cb68286..000000000000
--- a/drivers/clk/at91/clk-main.c
+++ /dev/null
@@ -1,54 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-DECLARE_GLOBAL_DATA_PTR;
-
-static int main_osc_clk_enable(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-
-	if (readl(&pmc->sr) & AT91_PMC_MOSCSELS)
-		return 0;
-
-	return -EINVAL;
-}
-
-static ulong main_osc_clk_get_rate(struct clk *clk)
-{
-	return gd->arch.main_clk_rate_hz;
-}
-
-static struct clk_ops main_osc_clk_ops = {
-	.enable = main_osc_clk_enable,
-	.get_rate = main_osc_clk_get_rate,
-};
-
-static int main_osc_clk_probe(struct udevice *dev)
-{
-	return at91_pmc_core_probe(dev);
-}
-
-static const struct udevice_id main_osc_clk_match[] = {
-	{ .compatible = "atmel,at91sam9x5-clk-main" },
-	{}
-};
-
-U_BOOT_DRIVER(at91sam9x5_main_osc_clk) = {
-	.name = "at91sam9x5-main-osc-clk",
-	.id = UCLASS_CLK,
-	.of_match = main_osc_clk_match,
-	.probe = main_osc_clk_probe,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &main_osc_clk_ops,
-};
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
deleted file mode 100644
index 4fa33c4715c9..000000000000
--- a/drivers/clk/at91/clk-master.c
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-
-DECLARE_GLOBAL_DATA_PTR;
-
-static ulong at91_master_clk_get_rate(struct clk *clk)
-{
-	return gd->arch.mck_rate_hz;
-}
-
-static struct clk_ops at91_master_clk_ops = {
-	.get_rate = at91_master_clk_get_rate,
-};
-
-static const struct udevice_id at91_master_clk_match[] = {
-	{ .compatible = "atmel,at91rm9200-clk-master" },
-	{ .compatible = "atmel,at91sam9x5-clk-master" },
-	{}
-};
-
-U_BOOT_DRIVER(at91_master_clk) = {
-	.name = "at91-master-clk",
-	.id = UCLASS_CLK,
-	.of_match = at91_master_clk_match,
-	.ops = &at91_master_clk_ops,
-};
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
deleted file mode 100644
index c55e6214b226..000000000000
--- a/drivers/clk/at91/clk-peripheral.c
+++ /dev/null
@@ -1,113 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-#include <malloc.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-#define PERIPHERAL_ID_MIN	2
-#define PERIPHERAL_ID_MAX	31
-#define PERIPHERAL_MASK(id)	(1 << ((id) & PERIPHERAL_ID_MAX))
-
-enum periph_clk_type {
-	CLK_PERIPH_AT91RM9200 = 0,
-	CLK_PERIPH_AT91SAM9X5,
-};
-/**
- * sam9x5_periph_clk_bind() - for the periph clock driver
- * Recursively bind its children as clk devices.
- *
- * @return: 0 on success, or negative error code on failure
- */
-static int sam9x5_periph_clk_bind(struct udevice *dev)
-{
-	return at91_clk_sub_device_bind(dev, "periph-clk");
-}
-
-static const struct udevice_id sam9x5_periph_clk_match[] = {
-	{
-		.compatible = "atmel,at91rm9200-clk-peripheral",
-		.data = CLK_PERIPH_AT91RM9200,
-	},
-	{
-		.compatible = "atmel,at91sam9x5-clk-peripheral",
-		.data = CLK_PERIPH_AT91SAM9X5,
-	},
-	{}
-};
-
-U_BOOT_DRIVER(sam9x5_periph_clk) = {
-	.name = "sam9x5-periph-clk",
-	.id = UCLASS_MISC,
-	.of_match = sam9x5_periph_clk_match,
-	.bind = sam9x5_periph_clk_bind,
-};
-
-/*---------------------------------------------------------*/
-
-static int periph_clk_enable(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	enum periph_clk_type clk_type;
-	void *addr;
-
-	if (clk->id < PERIPHERAL_ID_MIN)
-		return -1;
-
-	clk_type = dev_get_driver_data(dev_get_parent(clk->dev));
-	if (clk_type == CLK_PERIPH_AT91RM9200) {
-		addr = &pmc->pcer;
-		if (clk->id > PERIPHERAL_ID_MAX)
-			addr = &pmc->pcer1;
-
-		setbits_le32(addr, PERIPHERAL_MASK(clk->id));
-	} else {
-		writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
-		setbits_le32(&pmc->pcr,
-			     AT91_PMC_PCR_CMD_WRITE | AT91_PMC_PCR_EN);
-	}
-
-	return 0;
-}
-
-static ulong periph_get_rate(struct clk *clk)
-{
-	struct udevice *dev;
-	struct clk clk_dev;
-	ulong clk_rate;
-	int ret;
-
-	dev = dev_get_parent(clk->dev);
-
-	ret = clk_get_by_index(dev, 0, &clk_dev);
-	if (ret)
-		return ret;
-
-	clk_rate = clk_get_rate(&clk_dev);
-
-	clk_free(&clk_dev);
-
-	return clk_rate;
-}
-
-static struct clk_ops periph_clk_ops = {
-	.of_xlate = at91_clk_of_xlate,
-	.enable = periph_clk_enable,
-	.get_rate = periph_get_rate,
-};
-
-U_BOOT_DRIVER(clk_periph) = {
-	.name	= "periph-clk",
-	.id	= UCLASS_CLK,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.probe = at91_clk_probe,
-	.ops	= &periph_clk_ops,
-};
diff --git a/drivers/clk/at91/clk-plla.c b/drivers/clk/at91/clk-plla.c
deleted file mode 100644
index 79d725819fca..000000000000
--- a/drivers/clk/at91/clk-plla.c
+++ /dev/null
@@ -1,54 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-DECLARE_GLOBAL_DATA_PTR;
-
-static int plla_clk_enable(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-
-	if (readl(&pmc->sr) & AT91_PMC_LOCKA)
-		return 0;
-
-	return -EINVAL;
-}
-
-static ulong plla_clk_get_rate(struct clk *clk)
-{
-	return gd->arch.plla_rate_hz;
-}
-
-static struct clk_ops plla_clk_ops = {
-	.enable = plla_clk_enable,
-	.get_rate = plla_clk_get_rate,
-};
-
-static int plla_clk_probe(struct udevice *dev)
-{
-	return at91_pmc_core_probe(dev);
-}
-
-static const struct udevice_id plla_clk_match[] = {
-	{ .compatible = "atmel,sama5d3-clk-pll" },
-	{}
-};
-
-U_BOOT_DRIVER(at91_plla_clk) = {
-	.name = "at91-plla-clk",
-	.id = UCLASS_CLK,
-	.of_match = plla_clk_match,
-	.probe = plla_clk_probe,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &plla_clk_ops,
-};
diff --git a/drivers/clk/at91/clk-plladiv.c b/drivers/clk/at91/clk-plladiv.c
deleted file mode 100644
index ca6158ef6ab2..000000000000
--- a/drivers/clk/at91/clk-plladiv.c
+++ /dev/null
@@ -1,85 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2018 Microhip / Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@microchip.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm/device.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-static int at91_plladiv_clk_enable(struct clk *clk)
-{
-	return 0;
-}
-
-static ulong at91_plladiv_clk_get_rate(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	struct clk source;
-	ulong clk_rate;
-	int ret;
-
-	ret = clk_get_by_index(clk->dev, 0, &source);
-	if (ret)
-		return -EINVAL;
-
-	clk_rate = clk_get_rate(&source);
-	if (readl(&pmc->mckr) & AT91_PMC_MCKR_PLLADIV_2)
-		clk_rate /= 2;
-
-	return clk_rate;
-}
-
-static ulong at91_plladiv_clk_set_rate(struct clk *clk, ulong rate)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	struct clk source;
-	ulong parent_rate;
-	int ret;
-
-	ret = clk_get_by_index(clk->dev, 0, &source);
-	if (ret)
-		return -EINVAL;
-
-	parent_rate = clk_get_rate(&source);
-	if ((parent_rate != rate) && ((parent_rate) / 2 != rate))
-		return -EINVAL;
-
-	if (parent_rate != rate) {
-		writel((readl(&pmc->mckr) | AT91_PMC_MCKR_PLLADIV_2),
-		       &pmc->mckr);
-	}
-
-	return 0;
-}
-
-static struct clk_ops at91_plladiv_clk_ops = {
-	.enable = at91_plladiv_clk_enable,
-	.get_rate = at91_plladiv_clk_get_rate,
-	.set_rate = at91_plladiv_clk_set_rate,
-};
-
-static int at91_plladiv_clk_probe(struct udevice *dev)
-{
-	return at91_pmc_core_probe(dev);
-}
-
-static const struct udevice_id at91_plladiv_clk_match[] = {
-	{ .compatible = "atmel,at91sam9x5-clk-plldiv" },
-	{}
-};
-
-U_BOOT_DRIVER(at91_plladiv_clk) = {
-	.name = "at91-plladiv-clk",
-	.id = UCLASS_CLK,
-	.of_match = at91_plladiv_clk_match,
-	.probe = at91_plladiv_clk_probe,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &at91_plladiv_clk_ops,
-};
diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c
deleted file mode 100644
index 1f8665768b00..000000000000
--- a/drivers/clk/at91/clk-slow.c
+++ /dev/null
@@ -1,36 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-
-static int at91_slow_clk_enable(struct clk *clk)
-{
-	return 0;
-}
-
-static ulong at91_slow_clk_get_rate(struct clk *clk)
-{
-	return CONFIG_SYS_AT91_SLOW_CLOCK;
-}
-
-static struct clk_ops at91_slow_clk_ops = {
-	.enable = at91_slow_clk_enable,
-	.get_rate = at91_slow_clk_get_rate,
-};
-
-static const struct udevice_id at91_slow_clk_match[] = {
-	{ .compatible = "atmel,at91sam9x5-clk-slow" },
-	{}
-};
-
-U_BOOT_DRIVER(at91_slow_clk) = {
-	.name = "at91-slow-clk",
-	.id = UCLASS_CLK,
-	.of_match = at91_slow_clk_match,
-	.ops = &at91_slow_clk_ops,
-};
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
deleted file mode 100644
index 76b1958670d6..000000000000
--- a/drivers/clk/at91/clk-system.c
+++ /dev/null
@@ -1,111 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-#include <linux/bitops.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-#define SYSTEM_MAX_ID		31
-
-/**
- * at91_system_clk_bind() - for the system clock driver
- * Recursively bind its children as clk devices.
- *
- * @return: 0 on success, or negative error code on failure
- */
-static int at91_system_clk_bind(struct udevice *dev)
-{
-	return at91_clk_sub_device_bind(dev, "system-clk");
-}
-
-static const struct udevice_id at91_system_clk_match[] = {
-	{ .compatible = "atmel,at91rm9200-clk-system" },
-	{}
-};
-
-U_BOOT_DRIVER(at91_system_clk) = {
-	.name = "at91-system-clk",
-	.id = UCLASS_MISC,
-	.of_match = at91_system_clk_match,
-	.bind = at91_system_clk_bind,
-};
-
-/*----------------------------------------------------------*/
-
-static inline int is_pck(int id)
-{
-	return (id >= 8) && (id <= 15);
-}
-
-static ulong system_clk_get_rate(struct clk *clk)
-{
-	struct clk clk_dev;
-	int ret;
-
-	ret = clk_get_by_index(clk->dev, 0, &clk_dev);
-	if (ret)
-		return -EINVAL;
-
-	return clk_get_rate(&clk_dev);
-}
-
-static ulong system_clk_set_rate(struct clk *clk, ulong rate)
-{
-	struct clk clk_dev;
-	int ret;
-
-	ret = clk_get_by_index(clk->dev, 0, &clk_dev);
-	if (ret)
-		return -EINVAL;
-
-	return clk_set_rate(&clk_dev, rate);
-}
-
-static int system_clk_enable(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	u32 mask;
-
-	if (clk->id > SYSTEM_MAX_ID)
-		return -EINVAL;
-
-	mask = BIT(clk->id);
-
-	writel(mask, &pmc->scer);
-
-	/**
-	 * For the programmable clocks the Ready status in the PMC
-	 * status register should be checked after enabling.
-	 * For other clocks this is unnecessary.
-	 */
-	if (!is_pck(clk->id))
-		return 0;
-
-	while (!(readl(&pmc->sr) & mask))
-		;
-
-	return 0;
-}
-
-static struct clk_ops system_clk_ops = {
-	.of_xlate = at91_clk_of_xlate,
-	.get_rate = system_clk_get_rate,
-	.set_rate = system_clk_set_rate,
-	.enable = system_clk_enable,
-};
-
-U_BOOT_DRIVER(system_clk) = {
-	.name = "system-clk",
-	.id = UCLASS_CLK,
-	.probe = at91_clk_probe,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &system_clk_ops,
-};
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
deleted file mode 100644
index af9d72436933..000000000000
--- a/drivers/clk/at91/clk-usb.c
+++ /dev/null
@@ -1,147 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2018 Microhip / Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@microchip.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <log.h>
-#include <dm/device.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include "pmc.h"
-
-DECLARE_GLOBAL_DATA_PTR;
-
-#define AT91_USB_CLK_SOURCE_MAX	2
-#define AT91_USB_CLK_MAX_DIV	15
-
-struct at91_usb_clk_priv {
-	u32 num_clksource;
-};
-
-static ulong at91_usb_clk_get_rate(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	struct clk source;
-	u32 tmp, usbdiv;
-	u8 source_index;
-	int ret;
-
-	tmp = readl(&pmc->pcr);
-	source_index = (tmp >> AT91_PMC_USB_USBS_OFFSET) &
-			AT91_PMC_USB_USBS_MASK;
-	usbdiv = (tmp >> AT91_PMC_USB_DIV_OFFSET) & AT91_PMC_USB_DIV_MASK;
-
-	ret = clk_get_by_index(clk->dev, source_index, &source);
-	if (ret)
-		return 0;
-
-	return clk_get_rate(&source) / (usbdiv + 1);
-}
-
-static ulong at91_usb_clk_set_rate(struct clk *clk, ulong rate)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	struct at91_usb_clk_priv *priv = dev_get_priv(clk->dev);
-	struct clk source, best_source;
-	ulong tmp_rate, best_rate = rate, source_rate;
-	int tmp_diff, best_diff = -1;
-	u32 div, best_div = 0;
-	u8 best_source_index = 0;
-	u8 i;
-	u32 tmp;
-	int ret;
-
-	for (i = 0; i < priv->num_clksource; i++) {
-		ret = clk_get_by_index(clk->dev, i, &source);
-		if (ret)
-			return ret;
-
-		source_rate = clk_get_rate(&source);
-		if (IS_ERR_VALUE(source_rate))
-			return source_rate;
-
-		for (div = 1; div < AT91_USB_CLK_MAX_DIV + 2; div++) {
-			tmp_rate = DIV_ROUND_CLOSEST(source_rate, div);
-			tmp_diff = abs(rate - tmp_rate);
-
-			if (best_diff < 0 || best_diff > tmp_diff) {
-				best_rate = tmp_rate;
-				best_diff = tmp_diff;
-
-				best_div = div - 1;
-				best_source = source;
-				best_source_index = i;
-			}
-
-			if (!best_diff || tmp_rate < rate)
-				break;
-		}
-
-		if (!best_diff)
-			break;
-	}
-
-	debug("AT91 USB: best sourc: %s, best_rate = %ld, best_div = %d\n",
-	      best_source.dev->name, best_rate, best_div);
-
-	ret = clk_enable(&best_source);
-	if (ret)
-		return ret;
-
-	tmp = AT91_PMC_USB_USBS_(best_source_index) |
-	      AT91_PMC_USB_DIV_(best_div);
-	writel(tmp, &pmc->usb);
-
-	return 0;
-}
-
-static struct clk_ops at91_usb_clk_ops = {
-	.get_rate = at91_usb_clk_get_rate,
-	.set_rate = at91_usb_clk_set_rate,
-};
-
-static int at91_usb_clk_ofdata_to_platdata(struct udevice *dev)
-{
-	struct at91_usb_clk_priv *priv = dev_get_priv(dev);
-	u32 cells[AT91_USB_CLK_SOURCE_MAX];
-	u32 num_clksource;
-
-	num_clksource = fdtdec_get_int_array_count(gd->fdt_blob,
-						   dev_of_offset(dev),
-						   "clocks", cells,
-						   AT91_USB_CLK_SOURCE_MAX);
-
-	if (!num_clksource)
-		return -1;
-
-	priv->num_clksource = num_clksource;
-
-	return 0;
-}
-
-static int at91_usb_clk_probe(struct udevice *dev)
-{
-	return at91_pmc_core_probe(dev);
-}
-
-static const struct udevice_id at91_usb_clk_match[] = {
-	{ .compatible = "atmel,at91sam9x5-clk-usb" },
-	{}
-};
-
-U_BOOT_DRIVER(at91_usb_clk) = {
-	.name = "at91-usb-clk",
-	.id = UCLASS_CLK,
-	.of_match = at91_usb_clk_match,
-	.probe = at91_usb_clk_probe,
-	.ofdata_to_platdata = at91_usb_clk_ofdata_to_platdata,
-	.priv_auto_alloc_size = sizeof(struct at91_usb_clk_priv),
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &at91_usb_clk_ops,
-};
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
deleted file mode 100644
index 18af0bfeaad1..000000000000
--- a/drivers/clk/at91/clk-utmi.c
+++ /dev/null
@@ -1,142 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-#include <syscon.h>
-#include <linux/io.h>
-#include <mach/at91_pmc.h>
-#include <mach/at91_sfr.h>
-#include "pmc.h"
-
-/*
- * The purpose of this clock is to generate a 480 MHz signal. A different
- * rate can't be configured.
- */
-#define UTMI_RATE	480000000
-
-static int utmi_clk_enable(struct clk *clk)
-{
-	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
-	struct at91_pmc *pmc = plat->reg_base;
-	struct clk clk_dev;
-	ulong clk_rate;
-	u32 utmi_ref_clk_freq;
-	u32 tmp;
-	int err;
-	int timeout = 2000000;
-
-	if (readl(&pmc->sr) & AT91_PMC_LOCKU)
-		return 0;
-
-	/*
-	 * If mainck rate is different from 12 MHz, we have to configure the
-	 * FREQ field of the SFR_UTMICKTRIM register to generate properly
-	 * the utmi clock.
-	 */
-	err = clk_get_by_index(clk->dev, 0, &clk_dev);
-	if (err)
-		return -EINVAL;
-
-	clk_rate = clk_get_rate(&clk_dev);
-	switch (clk_rate) {
-	case 12000000:
-		utmi_ref_clk_freq = 0;
-		break;
-	case 16000000:
-		utmi_ref_clk_freq = 1;
-		break;
-	case 24000000:
-		utmi_ref_clk_freq = 2;
-		break;
-	/*
-	 * Not supported on SAMA5D2 but it's not an issue since MAINCK
-	 * maximum value is 24 MHz.
-	 */
-	case 48000000:
-		utmi_ref_clk_freq = 3;
-		break;
-	default:
-		printf("UTMICK: unsupported mainck rate\n");
-		return -EINVAL;
-	}
-
-	if (plat->regmap_sfr) {
-		err = regmap_read(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, &tmp);
-		if (err)
-			return -EINVAL;
-
-		tmp &= ~AT91_UTMICKTRIM_FREQ;
-		tmp |= utmi_ref_clk_freq;
-		err = regmap_write(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, tmp);
-		if (err)
-			return -EINVAL;
-	} else if (utmi_ref_clk_freq) {
-		printf("UTMICK: sfr node required\n");
-		return -EINVAL;
-	}
-
-	tmp = readl(&pmc->uckr);
-	tmp |= AT91_PMC_UPLLEN |
-	       AT91_PMC_UPLLCOUNT |
-	       AT91_PMC_BIASEN;
-	writel(tmp, &pmc->uckr);
-
-	while ((--timeout) && !(readl(&pmc->sr) & AT91_PMC_LOCKU))
-		;
-	if (!timeout) {
-		printf("UTMICK: timeout waiting for UPLL lock\n");
-		return -ETIMEDOUT;
-	}
-
-	return 0;
-}
-
-static ulong utmi_clk_get_rate(struct clk *clk)
-{
-	/* UTMI clk rate is fixed. */
-	return UTMI_RATE;
-}
-
-static struct clk_ops utmi_clk_ops = {
-	.enable = utmi_clk_enable,
-	.get_rate = utmi_clk_get_rate,
-};
-
-static int utmi_clk_ofdata_to_platdata(struct udevice *dev)
-{
-	struct pmc_platdata *plat = dev_get_platdata(dev);
-	struct udevice *syscon;
-
-	uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
-				     "regmap-sfr", &syscon);
-
-	if (syscon)
-		plat->regmap_sfr = syscon_get_regmap(syscon);
-
-	return 0;
-}
-
-static int utmi_clk_probe(struct udevice *dev)
-{
-	return at91_pmc_core_probe(dev);
-}
-
-static const struct udevice_id utmi_clk_match[] = {
-	{ .compatible = "atmel,at91sam9x5-clk-utmi" },
-	{}
-};
-
-U_BOOT_DRIVER(at91sam9x5_utmi_clk) = {
-	.name = "at91sam9x5-utmi-clk",
-	.id = UCLASS_CLK,
-	.of_match = utmi_clk_match,
-	.probe = utmi_clk_probe,
-	.ofdata_to_platdata = utmi_clk_ofdata_to_platdata,
-	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
-	.ops = &utmi_clk_ops,
-};
diff --git a/drivers/clk/at91/compat.c b/drivers/clk/at91/compat.c
new file mode 100644
index 000000000000..60b607a47192
--- /dev/null
+++ b/drivers/clk/at91/compat.c
@@ -0,0 +1,1023 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Compatible code for non CCF AT91 platforms.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ */
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dm/lists.h>
+#include <dm/util.h>
+#include <mach/at91_pmc.h>
+#include <mach/at91_sfr.h>
+#include <regmap.h>
+#include <syscon.h>
+
+#include "pmc.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct pmc_platdata {
+	struct at91_pmc *reg_base;
+	struct regmap *regmap_sfr;
+};
+
+static const struct udevice_id at91_pmc_match[] = {
+	{ .compatible = "atmel,at91rm9200-pmc" },
+	{ .compatible = "atmel,at91sam9260-pmc" },
+	{ .compatible = "atmel,at91sam9g45-pmc" },
+	{ .compatible = "atmel,at91sam9n12-pmc" },
+	{ .compatible = "atmel,at91sam9x5-pmc" },
+	{ .compatible = "atmel,sama5d3-pmc" },
+	{ .compatible = "atmel,sama5d2-pmc" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_pmc) = {
+	.name = "at91-pmc",
+	.id = UCLASS_SIMPLE_BUS,
+	.of_match = at91_pmc_match,
+};
+
+static int at91_pmc_core_probe(struct udevice *dev)
+{
+	struct pmc_platdata *plat = dev_get_platdata(dev);
+
+	dev = dev_get_parent(dev);
+
+	plat->reg_base = (struct at91_pmc *)devfdt_get_addr_ptr(dev);
+
+	return 0;
+}
+
+/**
+ * at91_clk_sub_device_bind() - for the at91 clock driver
+ * Recursively bind its children as clk devices.
+ *
+ * @return: 0 on success, or negative error code on failure
+ */
+int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name)
+{
+	const void *fdt = gd->fdt_blob;
+	int offset = dev_of_offset(dev);
+	bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC);
+	const char *name;
+	int ret;
+
+	for (offset = fdt_first_subnode(fdt, offset);
+	     offset > 0;
+	     offset = fdt_next_subnode(fdt, offset)) {
+		if (pre_reloc_only &&
+		    !ofnode_pre_reloc(offset_to_ofnode(offset)))
+			continue;
+		/*
+		 * If this node has "compatible" property, this is not
+		 * a clock sub-node, but a normal device. skip.
+		 */
+		fdt_get_property(fdt, offset, "compatible", &ret);
+		if (ret >= 0)
+			continue;
+
+		if (ret != -FDT_ERR_NOTFOUND)
+			return ret;
+
+		name = fdt_get_name(fdt, offset, NULL);
+		if (!name)
+			return -EINVAL;
+		ret = device_bind_driver_to_node(dev, drv_name, name,
+					offset_to_ofnode(offset), NULL);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+	int periph;
+
+	if (args->args_count) {
+		debug("Invalid args_count: %d\n", args->args_count);
+		return -EINVAL;
+	}
+
+	periph = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(clk->dev), "reg",
+				 -1);
+	if (periph < 0)
+		return -EINVAL;
+
+	clk->id = periph;
+
+	return 0;
+}
+
+int at91_clk_probe(struct udevice *dev)
+{
+	struct udevice *dev_periph_container, *dev_pmc;
+	struct pmc_platdata *plat = dev_get_platdata(dev);
+
+	dev_periph_container = dev_get_parent(dev);
+	dev_pmc = dev_get_parent(dev_periph_container);
+
+	plat->reg_base = (struct at91_pmc *)devfdt_get_addr_ptr(dev_pmc);
+
+	return 0;
+}
+
+/* SCKC specific code. */
+static const struct udevice_id at91_sckc_match[] = {
+	{ .compatible = "atmel,at91sam9x5-sckc" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_sckc) = {
+	.name = "at91-sckc",
+	.id = UCLASS_SIMPLE_BUS,
+	.of_match = at91_sckc_match,
+};
+
+/* Slow clock specific code. */
+static int at91_slow_clk_enable(struct clk *clk)
+{
+	return 0;
+}
+
+static ulong at91_slow_clk_get_rate(struct clk *clk)
+{
+	return CONFIG_SYS_AT91_SLOW_CLOCK;
+}
+
+static struct clk_ops at91_slow_clk_ops = {
+	.enable = at91_slow_clk_enable,
+	.get_rate = at91_slow_clk_get_rate,
+};
+
+static const struct udevice_id at91_slow_clk_match[] = {
+	{ .compatible = "atmel,at91sam9x5-clk-slow" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_slow_clk) = {
+	.name = "at91-slow-clk",
+	.id = UCLASS_CLK,
+	.of_match = at91_slow_clk_match,
+	.ops = &at91_slow_clk_ops,
+};
+
+/* Master clock specific code. */
+static ulong at91_master_clk_get_rate(struct clk *clk)
+{
+	return gd->arch.mck_rate_hz;
+}
+
+static struct clk_ops at91_master_clk_ops = {
+	.get_rate = at91_master_clk_get_rate,
+};
+
+static const struct udevice_id at91_master_clk_match[] = {
+	{ .compatible = "atmel,at91rm9200-clk-master" },
+	{ .compatible = "atmel,at91sam9x5-clk-master" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_master_clk) = {
+	.name = "at91-master-clk",
+	.id = UCLASS_CLK,
+	.of_match = at91_master_clk_match,
+	.ops = &at91_master_clk_ops,
+};
+
+/* Main osc clock specific code. */
+static int main_osc_clk_enable(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+
+	if (readl(&pmc->sr) & AT91_PMC_MOSCSELS)
+		return 0;
+
+	return -EINVAL;
+}
+
+static ulong main_osc_clk_get_rate(struct clk *clk)
+{
+	return gd->arch.main_clk_rate_hz;
+}
+
+static struct clk_ops main_osc_clk_ops = {
+	.enable = main_osc_clk_enable,
+	.get_rate = main_osc_clk_get_rate,
+};
+
+static int main_osc_clk_probe(struct udevice *dev)
+{
+	return at91_pmc_core_probe(dev);
+}
+
+static const struct udevice_id main_osc_clk_match[] = {
+	{ .compatible = "atmel,at91sam9x5-clk-main" },
+	{}
+};
+
+U_BOOT_DRIVER(at91sam9x5_main_osc_clk) = {
+	.name = "at91sam9x5-main-osc-clk",
+	.id = UCLASS_CLK,
+	.of_match = main_osc_clk_match,
+	.probe = main_osc_clk_probe,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &main_osc_clk_ops,
+};
+
+/* PLLA clock specific code. */
+static int plla_clk_enable(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+
+	if (readl(&pmc->sr) & AT91_PMC_LOCKA)
+		return 0;
+
+	return -EINVAL;
+}
+
+static ulong plla_clk_get_rate(struct clk *clk)
+{
+	return gd->arch.plla_rate_hz;
+}
+
+static struct clk_ops plla_clk_ops = {
+	.enable = plla_clk_enable,
+	.get_rate = plla_clk_get_rate,
+};
+
+static int plla_clk_probe(struct udevice *dev)
+{
+	return at91_pmc_core_probe(dev);
+}
+
+static const struct udevice_id plla_clk_match[] = {
+	{ .compatible = "atmel,sama5d3-clk-pll" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_plla_clk) = {
+	.name = "at91-plla-clk",
+	.id = UCLASS_CLK,
+	.of_match = plla_clk_match,
+	.probe = plla_clk_probe,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &plla_clk_ops,
+};
+
+/* PLLA DIV clock specific code. */
+static int at91_plladiv_clk_enable(struct clk *clk)
+{
+	return 0;
+}
+
+static ulong at91_plladiv_clk_get_rate(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	struct clk source;
+	ulong clk_rate;
+	int ret;
+
+	ret = clk_get_by_index(clk->dev, 0, &source);
+	if (ret)
+		return -EINVAL;
+
+	clk_rate = clk_get_rate(&source);
+	if (readl(&pmc->mckr) & AT91_PMC_MCKR_PLLADIV_2)
+		clk_rate /= 2;
+
+	return clk_rate;
+}
+
+static ulong at91_plladiv_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	struct clk source;
+	ulong parent_rate;
+	int ret;
+
+	ret = clk_get_by_index(clk->dev, 0, &source);
+	if (ret)
+		return -EINVAL;
+
+	parent_rate = clk_get_rate(&source);
+	if ((parent_rate != rate) && ((parent_rate) / 2 != rate))
+		return -EINVAL;
+
+	if (parent_rate != rate) {
+		writel((readl(&pmc->mckr) | AT91_PMC_MCKR_PLLADIV_2),
+		       &pmc->mckr);
+	}
+
+	return 0;
+}
+
+static struct clk_ops at91_plladiv_clk_ops = {
+	.enable = at91_plladiv_clk_enable,
+	.get_rate = at91_plladiv_clk_get_rate,
+	.set_rate = at91_plladiv_clk_set_rate,
+};
+
+static int at91_plladiv_clk_probe(struct udevice *dev)
+{
+	return at91_pmc_core_probe(dev);
+}
+
+static const struct udevice_id at91_plladiv_clk_match[] = {
+	{ .compatible = "atmel,at91sam9x5-clk-plldiv" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_plladiv_clk) = {
+	.name = "at91-plladiv-clk",
+	.id = UCLASS_CLK,
+	.of_match = at91_plladiv_clk_match,
+	.probe = at91_plladiv_clk_probe,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &at91_plladiv_clk_ops,
+};
+
+/* System clock specific code. */
+#define SYSTEM_MAX_ID		31
+
+/**
+ * at91_system_clk_bind() - for the system clock driver
+ * Recursively bind its children as clk devices.
+ *
+ * @return: 0 on success, or negative error code on failure
+ */
+static int at91_system_clk_bind(struct udevice *dev)
+{
+	return at91_clk_sub_device_bind(dev, "system-clk");
+}
+
+static const struct udevice_id at91_system_clk_match[] = {
+	{ .compatible = "atmel,at91rm9200-clk-system" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_system_clk) = {
+	.name = "at91-system-clk",
+	.id = UCLASS_MISC,
+	.of_match = at91_system_clk_match,
+	.bind = at91_system_clk_bind,
+};
+
+static inline int is_pck(int id)
+{
+	return (id >= 8) && (id <= 15);
+}
+
+static ulong system_clk_get_rate(struct clk *clk)
+{
+	struct clk clk_dev;
+	int ret;
+
+	ret = clk_get_by_index(clk->dev, 0, &clk_dev);
+	if (ret)
+		return -EINVAL;
+
+	return clk_get_rate(&clk_dev);
+}
+
+static ulong system_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk clk_dev;
+	int ret;
+
+	ret = clk_get_by_index(clk->dev, 0, &clk_dev);
+	if (ret)
+		return -EINVAL;
+
+	return clk_set_rate(&clk_dev, rate);
+}
+
+static int system_clk_enable(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	u32 mask;
+
+	if (clk->id > SYSTEM_MAX_ID)
+		return -EINVAL;
+
+	mask = BIT(clk->id);
+
+	writel(mask, &pmc->scer);
+
+	/**
+	 * For the programmable clocks the Ready status in the PMC
+	 * status register should be checked after enabling.
+	 * For other clocks this is unnecessary.
+	 */
+	if (!is_pck(clk->id))
+		return 0;
+
+	while (!(readl(&pmc->sr) & mask))
+		;
+
+	return 0;
+}
+
+static struct clk_ops system_clk_ops = {
+	.of_xlate = at91_clk_of_xlate,
+	.get_rate = system_clk_get_rate,
+	.set_rate = system_clk_set_rate,
+	.enable = system_clk_enable,
+};
+
+U_BOOT_DRIVER(system_clk) = {
+	.name = "system-clk",
+	.id = UCLASS_CLK,
+	.probe = at91_clk_probe,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &system_clk_ops,
+};
+
+/* Peripheral clock specific code. */
+#define PERIPHERAL_ID_MIN	2
+#define PERIPHERAL_ID_MAX	31
+#define PERIPHERAL_MASK(id)	(1 << ((id) & PERIPHERAL_ID_MAX))
+
+enum periph_clk_type {
+	CLK_PERIPH_AT91RM9200 = 0,
+	CLK_PERIPH_AT91SAM9X5,
+};
+
+/**
+ * sam9x5_periph_clk_bind() - for the periph clock driver
+ * Recursively bind its children as clk devices.
+ *
+ * @return: 0 on success, or negative error code on failure
+ */
+static int sam9x5_periph_clk_bind(struct udevice *dev)
+{
+	return at91_clk_sub_device_bind(dev, "periph-clk");
+}
+
+static const struct udevice_id sam9x5_periph_clk_match[] = {
+	{
+		.compatible = "atmel,at91rm9200-clk-peripheral",
+		.data = CLK_PERIPH_AT91RM9200,
+	},
+	{
+		.compatible = "atmel,at91sam9x5-clk-peripheral",
+		.data = CLK_PERIPH_AT91SAM9X5,
+	},
+	{}
+};
+
+U_BOOT_DRIVER(sam9x5_periph_clk) = {
+	.name = "sam9x5-periph-clk",
+	.id = UCLASS_MISC,
+	.of_match = sam9x5_periph_clk_match,
+	.bind = sam9x5_periph_clk_bind,
+};
+
+static int periph_clk_enable(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	enum periph_clk_type clk_type;
+	void *addr;
+
+	if (clk->id < PERIPHERAL_ID_MIN)
+		return -1;
+
+	clk_type = dev_get_driver_data(dev_get_parent(clk->dev));
+	if (clk_type == CLK_PERIPH_AT91RM9200) {
+		addr = &pmc->pcer;
+		if (clk->id > PERIPHERAL_ID_MAX)
+			addr = &pmc->pcer1;
+
+		setbits_le32(addr, PERIPHERAL_MASK(clk->id));
+	} else {
+		writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
+		setbits_le32(&pmc->pcr,
+			     AT91_PMC_PCR_CMD_WRITE | AT91_PMC_PCR_EN);
+	}
+
+	return 0;
+}
+
+static ulong periph_get_rate(struct clk *clk)
+{
+	struct udevice *dev;
+	struct clk clk_dev;
+	ulong clk_rate;
+	int ret;
+
+	dev = dev_get_parent(clk->dev);
+
+	ret = clk_get_by_index(dev, 0, &clk_dev);
+	if (ret)
+		return ret;
+
+	clk_rate = clk_get_rate(&clk_dev);
+
+	clk_free(&clk_dev);
+
+	return clk_rate;
+}
+
+static struct clk_ops periph_clk_ops = {
+	.of_xlate = at91_clk_of_xlate,
+	.enable = periph_clk_enable,
+	.get_rate = periph_get_rate,
+};
+
+U_BOOT_DRIVER(clk_periph) = {
+	.name	= "periph-clk",
+	.id	= UCLASS_CLK,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.probe = at91_clk_probe,
+	.ops	= &periph_clk_ops,
+};
+
+/* UTMI clock specific code. */
+#ifdef CONFIG_AT91_UTMI
+
+/*
+ * The purpose of this clock is to generate a 480 MHz signal. A different
+ * rate can't be configured.
+ */
+#define UTMI_RATE	480000000
+
+static int utmi_clk_enable(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	struct clk clk_dev;
+	ulong clk_rate;
+	u32 utmi_ref_clk_freq;
+	u32 tmp;
+	int err;
+	int timeout = 2000000;
+
+	if (readl(&pmc->sr) & AT91_PMC_LOCKU)
+		return 0;
+
+	/*
+	 * If mainck rate is different from 12 MHz, we have to configure the
+	 * FREQ field of the SFR_UTMICKTRIM register to generate properly
+	 * the utmi clock.
+	 */
+	err = clk_get_by_index(clk->dev, 0, &clk_dev);
+	if (err)
+		return -EINVAL;
+
+	clk_rate = clk_get_rate(&clk_dev);
+	switch (clk_rate) {
+	case 12000000:
+		utmi_ref_clk_freq = 0;
+		break;
+	case 16000000:
+		utmi_ref_clk_freq = 1;
+		break;
+	case 24000000:
+		utmi_ref_clk_freq = 2;
+		break;
+	/*
+	 * Not supported on SAMA5D2 but it's not an issue since MAINCK
+	 * maximum value is 24 MHz.
+	 */
+	case 48000000:
+		utmi_ref_clk_freq = 3;
+		break;
+	default:
+		printf("UTMICK: unsupported mainck rate\n");
+		return -EINVAL;
+	}
+
+	if (plat->regmap_sfr) {
+		err = regmap_read(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, &tmp);
+		if (err)
+			return -EINVAL;
+
+		tmp &= ~AT91_UTMICKTRIM_FREQ;
+		tmp |= utmi_ref_clk_freq;
+		err = regmap_write(plat->regmap_sfr, AT91_SFR_UTMICKTRIM, tmp);
+		if (err)
+			return -EINVAL;
+	} else if (utmi_ref_clk_freq) {
+		printf("UTMICK: sfr node required\n");
+		return -EINVAL;
+	}
+
+	tmp = readl(&pmc->uckr);
+	tmp |= AT91_PMC_UPLLEN |
+	       AT91_PMC_UPLLCOUNT |
+	       AT91_PMC_BIASEN;
+	writel(tmp, &pmc->uckr);
+
+	while ((--timeout) && !(readl(&pmc->sr) & AT91_PMC_LOCKU))
+		;
+	if (!timeout) {
+		printf("UTMICK: timeout waiting for UPLL lock\n");
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static ulong utmi_clk_get_rate(struct clk *clk)
+{
+	/* UTMI clk rate is fixed. */
+	return UTMI_RATE;
+}
+
+static struct clk_ops utmi_clk_ops = {
+	.enable = utmi_clk_enable,
+	.get_rate = utmi_clk_get_rate,
+};
+
+static int utmi_clk_ofdata_to_platdata(struct udevice *dev)
+{
+	struct pmc_platdata *plat = dev_get_platdata(dev);
+	struct udevice *syscon;
+
+	uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
+				     "regmap-sfr", &syscon);
+
+	if (syscon)
+		plat->regmap_sfr = syscon_get_regmap(syscon);
+
+	return 0;
+}
+
+static int utmi_clk_probe(struct udevice *dev)
+{
+	return at91_pmc_core_probe(dev);
+}
+
+static const struct udevice_id utmi_clk_match[] = {
+	{ .compatible = "atmel,at91sam9x5-clk-utmi" },
+	{}
+};
+
+U_BOOT_DRIVER(at91sam9x5_utmi_clk) = {
+	.name = "at91sam9x5-utmi-clk",
+	.id = UCLASS_CLK,
+	.of_match = utmi_clk_match,
+	.probe = utmi_clk_probe,
+	.ofdata_to_platdata = utmi_clk_ofdata_to_platdata,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &utmi_clk_ops,
+};
+
+#endif /* CONFIG_AT91_UTMI */
+
+/* H32MX clock specific code. */
+#ifdef CONFIG_AT91_H32MX
+
+#define H32MX_MAX_FREQ	90000000
+
+static ulong sama5d4_h32mx_clk_get_rate(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	ulong rate = gd->arch.mck_rate_hz;
+
+	if (readl(&pmc->mckr) & AT91_PMC_MCKR_H32MXDIV)
+		rate /= 2;
+
+	if (rate > H32MX_MAX_FREQ)
+		dev_dbg(clk->dev, "H32MX clock is too fast\n");
+
+	return rate;
+}
+
+static struct clk_ops sama5d4_h32mx_clk_ops = {
+	.get_rate = sama5d4_h32mx_clk_get_rate,
+};
+
+static int sama5d4_h32mx_clk_probe(struct udevice *dev)
+{
+	return at91_pmc_core_probe(dev);
+}
+
+static const struct udevice_id sama5d4_h32mx_clk_match[] = {
+	{ .compatible = "atmel,sama5d4-clk-h32mx" },
+	{}
+};
+
+U_BOOT_DRIVER(sama5d4_h32mx_clk) = {
+	.name = "sama5d4-h32mx-clk",
+	.id = UCLASS_CLK,
+	.of_match = sama5d4_h32mx_clk_match,
+	.probe = sama5d4_h32mx_clk_probe,
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &sama5d4_h32mx_clk_ops,
+};
+
+#endif /* CONFIG_AT91_H32MX */
+
+/* Generic clock specific code. */
+#ifdef CONFIG_AT91_GENERIC_CLK
+
+#define GENERATED_SOURCE_MAX	6
+#define GENERATED_MAX_DIV	255
+
+/**
+ * generated_clk_bind() - for the generated clock driver
+ * Recursively bind its children as clk devices.
+ *
+ * @return: 0 on success, or negative error code on failure
+ */
+static int generated_clk_bind(struct udevice *dev)
+{
+	return at91_clk_sub_device_bind(dev, "generic-clk");
+}
+
+static const struct udevice_id generated_clk_match[] = {
+	{ .compatible = "atmel,sama5d2-clk-generated" },
+	{}
+};
+
+U_BOOT_DRIVER(generated_clk) = {
+	.name = "generated-clk",
+	.id = UCLASS_MISC,
+	.of_match = generated_clk_match,
+	.bind = generated_clk_bind,
+};
+
+struct generic_clk_priv {
+	u32 num_parents;
+};
+
+static ulong generic_clk_get_rate(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	struct clk parent;
+	ulong clk_rate;
+	u32 tmp, gckdiv;
+	u8 clock_source, parent_index;
+	int ret;
+
+	writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
+	tmp = readl(&pmc->pcr);
+	clock_source = (tmp >> AT91_PMC_PCR_GCKCSS_OFFSET) &
+		    AT91_PMC_PCR_GCKCSS_MASK;
+	gckdiv = (tmp >> AT91_PMC_PCR_GCKDIV_OFFSET) & AT91_PMC_PCR_GCKDIV_MASK;
+
+	parent_index = clock_source - 1;
+	ret = clk_get_by_index(dev_get_parent(clk->dev), parent_index, &parent);
+	if (ret)
+		return 0;
+
+	clk_rate = clk_get_rate(&parent) / (gckdiv + 1);
+
+	clk_free(&parent);
+
+	return clk_rate;
+}
+
+static ulong generic_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	struct generic_clk_priv *priv = dev_get_priv(clk->dev);
+	struct clk parent, best_parent;
+	ulong tmp_rate, best_rate = rate, parent_rate;
+	int tmp_diff, best_diff = -1;
+	u32 div, best_div = 0;
+	u8 best_parent_index, best_clock_source = 0;
+	u8 i;
+	u32 tmp;
+	int ret;
+
+	for (i = 0; i < priv->num_parents; i++) {
+		ret = clk_get_by_index(dev_get_parent(clk->dev), i, &parent);
+		if (ret)
+			return ret;
+
+		parent_rate = clk_get_rate(&parent);
+		if (IS_ERR_VALUE(parent_rate))
+			return parent_rate;
+
+		for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
+			tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div);
+			tmp_diff = abs(rate - tmp_rate);
+
+			if (best_diff < 0 || best_diff > tmp_diff) {
+				best_rate = tmp_rate;
+				best_diff = tmp_diff;
+
+				best_div = div - 1;
+				best_parent = parent;
+				best_parent_index = i;
+				best_clock_source = best_parent_index + 1;
+			}
+
+			if (!best_diff || tmp_rate < rate)
+				break;
+		}
+
+		if (!best_diff)
+			break;
+	}
+
+	debug("GCK: best parent: %s, best_rate = %ld, best_div = %d\n",
+	      best_parent.dev->name, best_rate, best_div);
+
+	ret = clk_enable(&best_parent);
+	if (ret)
+		return ret;
+
+	writel(clk->id & AT91_PMC_PCR_PID_MASK, &pmc->pcr);
+	tmp = readl(&pmc->pcr);
+	tmp &= ~(AT91_PMC_PCR_GCKDIV | AT91_PMC_PCR_GCKCSS);
+	tmp |= AT91_PMC_PCR_GCKCSS_(best_clock_source) |
+	       AT91_PMC_PCR_CMD_WRITE |
+	       AT91_PMC_PCR_GCKDIV_(best_div) |
+	       AT91_PMC_PCR_GCKEN;
+	writel(tmp, &pmc->pcr);
+
+	while (!(readl(&pmc->sr) & AT91_PMC_GCKRDY))
+		;
+
+	return 0;
+}
+
+static struct clk_ops generic_clk_ops = {
+	.of_xlate = at91_clk_of_xlate,
+	.get_rate = generic_clk_get_rate,
+	.set_rate = generic_clk_set_rate,
+};
+
+static int generic_clk_ofdata_to_platdata(struct udevice *dev)
+{
+	struct generic_clk_priv *priv = dev_get_priv(dev);
+	u32 cells[GENERATED_SOURCE_MAX];
+	u32 num_parents;
+
+	num_parents = fdtdec_get_int_array_count(gd->fdt_blob,
+			dev_of_offset(dev_get_parent(dev)), "clocks", cells,
+			GENERATED_SOURCE_MAX);
+
+	if (!num_parents)
+		return -1;
+
+	priv->num_parents = num_parents;
+
+	return 0;
+}
+
+U_BOOT_DRIVER(generic_clk) = {
+	.name = "generic-clk",
+	.id = UCLASS_CLK,
+	.probe = at91_clk_probe,
+	.ofdata_to_platdata = generic_clk_ofdata_to_platdata,
+	.priv_auto_alloc_size = sizeof(struct generic_clk_priv),
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &generic_clk_ops,
+};
+
+#endif /* CONFIG_AT91_GENERIC_CLK */
+
+/* USB clock specific code. */
+#ifdef CONFIG_AT91_USB_CLK
+
+#define AT91_USB_CLK_SOURCE_MAX	2
+#define AT91_USB_CLK_MAX_DIV	15
+
+struct at91_usb_clk_priv {
+	u32 num_clksource;
+};
+
+static ulong at91_usb_clk_get_rate(struct clk *clk)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	struct clk source;
+	u32 tmp, usbdiv;
+	u8 source_index;
+	int ret;
+
+	tmp = readl(&pmc->pcr);
+	source_index = (tmp >> AT91_PMC_USB_USBS_OFFSET) &
+			AT91_PMC_USB_USBS_MASK;
+	usbdiv = (tmp >> AT91_PMC_USB_DIV_OFFSET) & AT91_PMC_USB_DIV_MASK;
+
+	ret = clk_get_by_index(clk->dev, source_index, &source);
+	if (ret)
+		return 0;
+
+	return clk_get_rate(&source) / (usbdiv + 1);
+}
+
+static ulong at91_usb_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct pmc_platdata *plat = dev_get_platdata(clk->dev);
+	struct at91_pmc *pmc = plat->reg_base;
+	struct at91_usb_clk_priv *priv = dev_get_priv(clk->dev);
+	struct clk source, best_source;
+	ulong tmp_rate, best_rate = rate, source_rate;
+	int tmp_diff, best_diff = -1;
+	u32 div, best_div = 0;
+	u8 best_source_index = 0;
+	u8 i;
+	u32 tmp;
+	int ret;
+
+	for (i = 0; i < priv->num_clksource; i++) {
+		ret = clk_get_by_index(clk->dev, i, &source);
+		if (ret)
+			return ret;
+
+		source_rate = clk_get_rate(&source);
+		if (IS_ERR_VALUE(source_rate))
+			return source_rate;
+
+		for (div = 1; div < AT91_USB_CLK_MAX_DIV + 2; div++) {
+			tmp_rate = DIV_ROUND_CLOSEST(source_rate, div);
+			tmp_diff = abs(rate - tmp_rate);
+
+			if (best_diff < 0 || best_diff > tmp_diff) {
+				best_rate = tmp_rate;
+				best_diff = tmp_diff;
+
+				best_div = div - 1;
+				best_source = source;
+				best_source_index = i;
+			}
+
+			if (!best_diff || tmp_rate < rate)
+				break;
+		}
+
+		if (!best_diff)
+			break;
+	}
+
+	debug("AT91 USB: best sourc: %s, best_rate = %ld, best_div = %d\n",
+	      best_source.dev->name, best_rate, best_div);
+
+	ret = clk_enable(&best_source);
+	if (ret)
+		return ret;
+
+	tmp = AT91_PMC_USB_USBS_(best_source_index) |
+	      AT91_PMC_USB_DIV_(best_div);
+	writel(tmp, &pmc->usb);
+
+	return 0;
+}
+
+static struct clk_ops at91_usb_clk_ops = {
+	.get_rate = at91_usb_clk_get_rate,
+	.set_rate = at91_usb_clk_set_rate,
+};
+
+static int at91_usb_clk_ofdata_to_platdata(struct udevice *dev)
+{
+	struct at91_usb_clk_priv *priv = dev_get_priv(dev);
+	u32 cells[AT91_USB_CLK_SOURCE_MAX];
+	u32 num_clksource;
+
+	num_clksource = fdtdec_get_int_array_count(gd->fdt_blob,
+						   dev_of_offset(dev),
+						   "clocks", cells,
+						   AT91_USB_CLK_SOURCE_MAX);
+
+	if (!num_clksource)
+		return -1;
+
+	priv->num_clksource = num_clksource;
+
+	return 0;
+}
+
+static int at91_usb_clk_probe(struct udevice *dev)
+{
+	return at91_pmc_core_probe(dev);
+}
+
+static const struct udevice_id at91_usb_clk_match[] = {
+	{ .compatible = "atmel,at91sam9x5-clk-usb" },
+	{}
+};
+
+U_BOOT_DRIVER(at91_usb_clk) = {
+	.name = "at91-usb-clk",
+	.id = UCLASS_CLK,
+	.of_match = at91_usb_clk_match,
+	.probe = at91_usb_clk_probe,
+	.ofdata_to_platdata = at91_usb_clk_ofdata_to_platdata,
+	.priv_auto_alloc_size = sizeof(struct at91_usb_clk_priv),
+	.platdata_auto_alloc_size = sizeof(struct pmc_platdata),
+	.ops = &at91_usb_clk_ops,
+};
+
+#endif /* CONFIG_AT91_USB_CLK */
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 0984dc321578..29c6452497b7 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -4,120 +4,8 @@
  *               Wenyou.Yang <wenyou.yang@atmel.com>
  */
 
+#include <asm/io.h>
 #include <common.h>
-#include <clk-uclass.h>
-#include <dm.h>
-#include <log.h>
-#include <dm/lists.h>
-#include <dm/util.h>
-#include "pmc.h"
-
-DECLARE_GLOBAL_DATA_PTR;
-
-static const struct udevice_id at91_pmc_match[] = {
-	{ .compatible = "atmel,at91rm9200-pmc" },
-	{ .compatible = "atmel,at91sam9260-pmc" },
-	{ .compatible = "atmel,at91sam9g45-pmc" },
-	{ .compatible = "atmel,at91sam9n12-pmc" },
-	{ .compatible = "atmel,at91sam9x5-pmc" },
-	{ .compatible = "atmel,sama5d3-pmc" },
-	{ .compatible = "atmel,sama5d2-pmc" },
-	{}
-};
-
-U_BOOT_DRIVER(at91_pmc) = {
-	.name = "at91-pmc",
-	.id = UCLASS_SIMPLE_BUS,
-	.of_match = at91_pmc_match,
-};
-
-/*---------------------------------------------------------*/
-
-int at91_pmc_core_probe(struct udevice *dev)
-{
-	struct pmc_platdata *plat = dev_get_platdata(dev);
-
-	dev = dev_get_parent(dev);
-
-	plat->reg_base = (struct at91_pmc *)devfdt_get_addr_ptr(dev);
-
-	return 0;
-}
-
-/**
- * at91_clk_sub_device_bind() - for the at91 clock driver
- * Recursively bind its children as clk devices.
- *
- * @return: 0 on success, or negative error code on failure
- */
-int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name)
-{
-	const void *fdt = gd->fdt_blob;
-	int offset = dev_of_offset(dev);
-	bool pre_reloc_only = !(gd->flags & GD_FLG_RELOC);
-	const char *name;
-	int ret;
-
-	for (offset = fdt_first_subnode(fdt, offset);
-	     offset > 0;
-	     offset = fdt_next_subnode(fdt, offset)) {
-		if (pre_reloc_only &&
-		    !ofnode_pre_reloc(offset_to_ofnode(offset)))
-			continue;
-		/*
-		 * If this node has "compatible" property, this is not
-		 * a clock sub-node, but a normal device. skip.
-		 */
-		fdt_get_property(fdt, offset, "compatible", &ret);
-		if (ret >= 0)
-			continue;
-
-		if (ret != -FDT_ERR_NOTFOUND)
-			return ret;
-
-		name = fdt_get_name(fdt, offset, NULL);
-		if (!name)
-			return -EINVAL;
-		ret = device_bind_driver_to_node(dev, drv_name, name,
-					offset_to_ofnode(offset), NULL);
-		if (ret)
-			return ret;
-	}
-
-	return 0;
-}
-
-int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
-{
-	int periph;
-
-	if (args->args_count) {
-		debug("Invalid args_count: %d\n", args->args_count);
-		return -EINVAL;
-	}
-
-	periph = fdtdec_get_uint(gd->fdt_blob, dev_of_offset(clk->dev), "reg",
-				 -1);
-	if (periph < 0)
-		return -EINVAL;
-
-	clk->id = periph;
-
-	return 0;
-}
-
-int at91_clk_probe(struct udevice *dev)
-{
-	struct udevice *dev_periph_container, *dev_pmc;
-	struct pmc_platdata *plat = dev_get_platdata(dev);
-
-	dev_periph_container = dev_get_parent(dev);
-	dev_pmc = dev_get_parent(dev_periph_container);
-
-	plat->reg_base = (struct at91_pmc *)devfdt_get_addr_ptr(dev_pmc);
-
-	return 0;
-}
 
 /**
  * pmc_read() - read content at address base + off into val
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index b1ab0a95c855..33ded02156c8 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -7,7 +7,6 @@
 #ifndef __AT91_PMC_H__
 #define __AT91_PMC_H__
 
-#include <regmap.h>
 #include <linux/bitops.h>
 #include <linux/io.h>
 
@@ -15,17 +14,6 @@
 #define AT91_TO_CLK_ID(_t, _i)		(((_t) << 8) | ((_i) & 0xff))
 #define AT91_CLK_ID_TO_DID(_i)		((_i) & 0xff)
 
-struct pmc_platdata {
-	struct at91_pmc *reg_base;
-	struct regmap *regmap_sfr;
-};
-
-int at91_pmc_core_probe(struct udevice *dev);
-int at91_clk_sub_device_bind(struct udevice *dev, const char *drv_name);
-
-int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args);
-int at91_clk_probe(struct udevice *dev);
-
 int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
 int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
 
@@ -33,4 +21,5 @@ void pmc_read(void __iomem *base, unsigned int off, unsigned int *val);
 void pmc_write(void __iomem *base, unsigned int off, unsigned int val);
 void pmc_update_bits(void __iomem *base, unsigned int off, unsigned int mask,
 			unsigned int bits);
+
 #endif
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
deleted file mode 100644
index a879b008ffd6..000000000000
--- a/drivers/clk/at91/sckc.c
+++ /dev/null
@@ -1,19 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0+
-/*
- * Copyright (C) 2016 Atmel Corporation
- *               Wenyou.Yang <wenyou.yang@atmel.com>
- */
-
-#include <common.h>
-#include <dm.h>
-
-static const struct udevice_id at91_sckc_match[] = {
-	{ .compatible = "atmel,at91sam9x5-sckc" },
-	{}
-};
-
-U_BOOT_DRIVER(at91_sckc) = {
-	.name = "at91-sckc",
-	.id = UCLASS_SIMPLE_BUS,
-	.of_match = at91_sckc_match,
-};
-- 
2.7.4

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

* [PATCH 10/22] clk: at91: sckc: add driver compatible with ccf
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (8 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 09/22] clk: at91: move clock code to compat.c Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 11/22] clk: at91: clk-main: " Claudiu Beznea
                   ` (11 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add sckc driver compatible with common clock framework. Driver
implements slow clock support for SAM9X60 compatible IPs (in this
list it is also present SAMA7G5's slow clock IP).

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/Makefile |   2 +-
 drivers/clk/at91/sckc.c   | 172 ++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 173 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/at91/sckc.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index e2413af40360..7083ce2890d6 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -3,7 +3,7 @@
 #
 
 ifdef CONFIG_CLK_CCF
-obj-y += pmc.o
+obj-y += pmc.o sckc.o
 else
 obj-y += compat.o
 endif
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
new file mode 100644
index 000000000000..dd62dc5510e4
--- /dev/null
+++ b/drivers/clk/at91/sckc.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Slow clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dt-bindings/clk/at91.h>
+#include <linux/clk-provider.h>
+
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK	"at91-sam9x60-td-slck"
+#define UBOOT_DM_CLK_AT91_SCKC			"at91-sckc"
+
+#define AT91_OSC_SEL		BIT(24)
+#define AT91_OSC_SEL_SHIFT	(24)
+
+struct sam9x60_sckc {
+	void __iomem *reg;
+	const char **parent_names;
+	unsigned int num_parents;
+	struct clk clk;
+};
+
+#define to_sam9x60_sckc(c)	container_of(c, struct sam9x60_sckc, clk)
+
+static int sam9x60_sckc_of_xlate(struct clk *clk,
+				 struct ofnode_phandle_args *args)
+{
+	if (args->args_count != 1) {
+		debug("AT91: SCKC: Invalid args_count: %d\n", args->args_count);
+		return -EINVAL;
+	}
+
+	clk->id = AT91_TO_CLK_ID(PMC_TYPE_SLOW, args->args[0]);
+
+	return 0;
+}
+
+static const struct clk_ops sam9x60_sckc_ops = {
+	.of_xlate = sam9x60_sckc_of_xlate,
+	.get_rate = clk_generic_get_rate,
+};
+
+static int sam9x60_td_slck_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct sam9x60_sckc *sckc = to_sam9x60_sckc(clk);
+	u32 i;
+
+	for (i = 0; i < sckc->num_parents; i++) {
+		if (!strcmp(parent->dev->name, sckc->parent_names[i]))
+			break;
+	}
+	if (i == sckc->num_parents)
+		return -EINVAL;
+
+	pmc_update_bits(sckc->reg, 0, AT91_OSC_SEL, (i << AT91_OSC_SEL_SHIFT));
+
+	return 0;
+}
+
+static const struct clk_ops sam9x60_td_slck_ops = {
+	.get_rate = clk_generic_get_rate,
+	.set_parent = sam9x60_td_slck_set_parent,
+};
+
+static struct clk *at91_sam9x60_clk_register_td_slck(struct sam9x60_sckc *sckc,
+		const char *name, const char * const *parent_names,
+		int num_parents)
+{
+	struct clk *clk;
+	int ret = -ENOMEM;
+	u32 val, i;
+
+	if (!sckc || !name || !parent_names || num_parents != 2)
+		return ERR_PTR(-EINVAL);
+
+	sckc->parent_names = kzalloc(sizeof(*sckc->parent_names) * num_parents,
+				     GFP_KERNEL);
+	if (!sckc->parent_names)
+		return ERR_PTR(ret);
+
+	for (i = 0; i < num_parents; i++) {
+		sckc->parent_names[i] = kmemdup(parent_names[i],
+				strlen(parent_names[i]) + 1, GFP_KERNEL);
+		if (!sckc->parent_names[i])
+			goto free;
+	}
+	sckc->num_parents = num_parents;
+
+	pmc_read(sckc->reg, 0, &val);
+	val = (val & AT91_OSC_SEL) >> AT91_OSC_SEL_SHIFT;
+
+	clk = &sckc->clk;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK, name,
+			   parent_names[val]);
+	if (ret)
+		goto free;
+
+	return clk;
+
+free:
+	for (; i >= 0; i--)
+		kfree(sckc->parent_names[i]);
+	kfree(sckc->parent_names);
+
+	return ERR_PTR(ret);
+}
+
+U_BOOT_DRIVER(at91_sam9x60_td_slck) = {
+	.name = UBOOT_DM_CLK_AT91_SAM9X60_TD_SLCK,
+	.id = UCLASS_CLK,
+	.ops = &sam9x60_td_slck_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+static int at91_sam9x60_sckc_probe(struct udevice *dev)
+{
+	struct sam9x60_sckc *sckc = dev_get_priv(dev);
+	void __iomem *base = (void *)devfdt_get_addr(dev);
+	const char *slow_rc_osc, *slow_osc;
+	const char *parents[2];
+	struct clk *clk, c;
+	int ret;
+
+	ret = clk_get_by_index(dev, 0, &c);
+	if (ret)
+		return ret;
+	slow_rc_osc = clk_hw_get_name(&c);
+
+	ret = clk_get_by_index(dev, 1, &c);
+	if (ret)
+		return ret;
+	slow_osc = clk_hw_get_name(&c);
+
+	clk = clk_register_fixed_factor(NULL, "md_slck", slow_rc_osc, 0, 1, 1);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 0), clk);
+
+	parents[0] = slow_rc_osc;
+	parents[1] = slow_osc;
+	sckc[1].reg = base;
+	clk = at91_sam9x60_clk_register_td_slck(&sckc[1], "td_slck",
+						parents, 2);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SLOW, 1), clk);
+
+	return 0;
+}
+
+static const struct udevice_id sam9x60_sckc_ids[] = {
+	{ .compatible = "microchip,sam9x60-sckc" },
+	{ /* Sentinel. */ },
+};
+
+U_BOOT_DRIVER(at91_sckc) = {
+	.name = UBOOT_DM_CLK_AT91_SCKC,
+	.id = UCLASS_CLK,
+	.of_match = sam9x60_sckc_ids,
+	.priv_auto_alloc_size = sizeof(struct sam9x60_sckc) * 2,
+	.ops = &sam9x60_sckc_ops,
+	.probe = at91_sam9x60_sckc_probe,
+	.flags = DM_FLAG_PRE_RELOC,
+};
-- 
2.7.4

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

* [PATCH 11/22] clk: at91: clk-main: add driver compatible with ccf
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (9 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 10/22] clk: at91: sckc: add driver compatible with ccf Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 12/22] clk: at91: sam9x60-pll: " Claudiu Beznea
                   ` (10 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add clk-main driver compatible with common clock framework.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/Makefile   |   2 +-
 drivers/clk/at91/clk-main.c | 387 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.h      |  10 ++
 3 files changed, 398 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/at91/clk-main.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 7083ce2890d6..805f67677a9b 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -3,7 +3,7 @@
 #
 
 ifdef CONFIG_CLK_CCF
-obj-y += pmc.o sckc.o
+obj-y += pmc.o sckc.o clk-main.o
 else
 obj-y += compat.o
 endif
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
new file mode 100644
index 000000000000..b52d926f3390
--- /dev/null
+++ b/drivers/clk/at91/clk-main.c
@@ -0,0 +1,387 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Main clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-main.c from Linux.
+ */
+
+#include <asm/processor.h>
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_MAIN_RC		"at91-main-rc-clk"
+#define UBOOT_DM_CLK_AT91_MAIN_OSC		"at91-main-osc-clk"
+#define UBOOT_DM_CLK_AT91_RM9200_MAIN		"at91-rm9200-main-clk"
+#define UBOOT_DM_CLK_AT91_SAM9X5_MAIN		"at91-sam9x5-main-clk"
+
+#define MOR_KEY_MASK		GENMASK(23, 16)
+#define USEC_PER_SEC		1000000UL
+#define SLOW_CLOCK_FREQ		32768
+
+#define clk_main_parent_select(s)	(((s) & \
+					(AT91_PMC_MOSCEN | \
+					AT91_PMC_OSCBYPASS)) ? 1 : 0)
+
+struct clk_main_rc {
+	void __iomem	*reg;
+	struct clk	clk;
+};
+
+#define to_clk_main_rc(_clk) container_of(_clk, struct clk_main_rc, clk)
+
+struct clk_main_osc {
+	void __iomem	*reg;
+	struct clk	clk;
+};
+
+#define to_clk_main_osc(_clk) container_of(_clk, struct clk_main_osc, clk)
+
+struct clk_main {
+	void __iomem		*reg;
+	const unsigned int	*clk_mux_table;
+	const char * const	*parent_names;
+	unsigned int		num_parents;
+	int			type;
+	struct clk		clk;
+};
+
+#define to_clk_main(_clk) container_of(_clk, struct clk_main, clk)
+
+static int main_rc_enable(struct clk *clk)
+{
+	struct clk_main_rc *main_rc = to_clk_main_rc(clk);
+	void __iomem *reg = main_rc->reg;
+	unsigned int val;
+
+	pmc_read(reg, AT91_CKGR_MOR, &val);
+
+	if (!(val & AT91_PMC_MOSCRCEN)) {
+		pmc_update_bits(reg, AT91_CKGR_MOR,
+				MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
+				AT91_PMC_KEY | AT91_PMC_MOSCRCEN);
+	}
+
+	pmc_read(reg, AT91_PMC_SR, &val);
+	while (!(val & AT91_PMC_MOSCRCS)) {
+		pmc_read(reg, AT91_PMC_SR, &val);
+		debug("waiting for main rc...\n");
+		cpu_relax();
+	}
+
+	return 0;
+}
+
+static int main_rc_disable(struct clk *clk)
+{
+	struct clk_main_rc *main_rc = to_clk_main_rc(clk);
+	struct reg *reg = main_rc->reg;
+	unsigned int val;
+
+	pmc_read(reg, AT91_CKGR_MOR, &val);
+
+	if (!(val & AT91_PMC_MOSCRCEN))
+		return 0;
+
+	pmc_update_bits(reg, AT91_CKGR_MOR, MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
+			AT91_PMC_KEY);
+
+	return 0;
+}
+
+static const struct clk_ops main_rc_clk_ops = {
+	.enable = main_rc_enable,
+	.disable = main_rc_disable,
+	.get_rate = clk_generic_get_rate,
+};
+
+struct clk *at91_clk_main_rc(void __iomem *reg, const char *name,
+			     const char *parent_name)
+{
+	struct clk_main_rc *main_rc;
+	struct clk *clk;
+	int ret;
+
+	if (!reg || !name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	main_rc = kzalloc(sizeof(*main_rc), GFP_KERNEL);
+	if (!main_rc)
+		return ERR_PTR(-ENOMEM);
+
+	main_rc->reg = reg;
+	clk = &main_rc->clk;
+
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_MAIN_RC, name,
+			   parent_name);
+	if (ret) {
+		kfree(main_rc);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_main_rc_clk) = {
+	.name = UBOOT_DM_CLK_AT91_MAIN_RC,
+	.id = UCLASS_CLK,
+	.ops = &main_rc_clk_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+static int clk_main_osc_enable(struct clk *clk)
+{
+	struct clk_main_osc *main = to_clk_main_osc(clk);
+	void __iomem *reg = main->reg;
+	unsigned int val;
+
+	pmc_read(reg, AT91_CKGR_MOR, &val);
+	val &= ~MOR_KEY_MASK;
+
+	if (val & AT91_PMC_OSCBYPASS)
+		return 0;
+
+	if (!(val & AT91_PMC_MOSCEN)) {
+		val |= AT91_PMC_MOSCEN | AT91_PMC_KEY;
+		pmc_write(reg, AT91_CKGR_MOR, val);
+	}
+
+	pmc_read(reg, AT91_PMC_SR, &val);
+	while (!(val & AT91_PMC_MOSCS)) {
+		pmc_read(reg, AT91_PMC_SR, &val);
+		debug("waiting for main osc..\n");
+		cpu_relax();
+	}
+
+	return 0;
+}
+
+static int clk_main_osc_disable(struct clk *clk)
+{
+	struct clk_main_osc *main = to_clk_main_osc(clk);
+	void __iomem *reg = main->reg;
+	unsigned int val;
+
+	pmc_read(reg, AT91_CKGR_MOR, &val);
+	if (val & AT91_PMC_OSCBYPASS)
+		return 0;
+
+	if (!(val & AT91_PMC_MOSCEN))
+		return 0;
+
+	val &= ~(AT91_PMC_KEY | AT91_PMC_MOSCEN);
+	pmc_write(reg, AT91_CKGR_MOR, val | AT91_PMC_KEY);
+
+	return 0;
+}
+
+static const struct clk_ops main_osc_clk_ops = {
+	.enable = clk_main_osc_enable,
+	.disable = clk_main_osc_disable,
+	.get_rate = clk_generic_get_rate,
+};
+
+struct clk *at91_clk_main_osc(void __iomem *reg, const char *name,
+			      const char *parent_name, bool bypass)
+{
+	struct clk_main_osc *main;
+	struct clk *clk;
+	int ret;
+
+	if (!reg || !name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	main = kzalloc(sizeof(*main), GFP_KERNEL);
+	if (!main)
+		return ERR_PTR(-ENOMEM);
+
+	main->reg = reg;
+	clk = &main->clk;
+
+	if (bypass) {
+		pmc_update_bits(reg, AT91_CKGR_MOR,
+				MOR_KEY_MASK | AT91_PMC_OSCBYPASS,
+				AT91_PMC_KEY | AT91_PMC_OSCBYPASS);
+	}
+
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_MAIN_OSC, name, parent_name);
+	if (ret) {
+		kfree(main);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_main_osc_clk) = {
+	.name = UBOOT_DM_CLK_AT91_MAIN_OSC,
+	.id = UCLASS_CLK,
+	.ops = &main_osc_clk_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+static int clk_main_probe_frequency(void __iomem *reg)
+{
+	unsigned int cycles = 16;
+	unsigned int cycle = DIV_ROUND_UP(USEC_PER_SEC, SLOW_CLOCK_FREQ);
+	unsigned int mcfr;
+
+	while (cycles--) {
+		pmc_read(reg, AT91_CKGR_MCFR, &mcfr);
+		if (mcfr & AT91_PMC_MAINRDY)
+			return 0;
+		udelay(cycle);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int clk_rm9200_main_enable(struct clk *clk)
+{
+	struct clk_main *main = to_clk_main(clk);
+
+	return clk_main_probe_frequency(main->reg);
+}
+
+static const struct clk_ops rm9200_main_clk_ops = {
+	.enable = clk_rm9200_main_enable,
+};
+
+struct clk *at91_clk_rm9200_main(void __iomem *reg, const char *name,
+				 const char *parent_name)
+{
+	struct clk_main *main;
+	struct clk *clk;
+	int ret;
+
+	if (!reg || !name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	main = kzalloc(sizeof(*main), GFP_KERNEL);
+	if (!main)
+		return ERR_PTR(-ENOMEM);
+
+	main->reg = reg;
+	clk = &main->clk;
+
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_RM9200_MAIN, name,
+			   parent_name);
+	if (ret) {
+		kfree(main);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_rm9200_main_clk) = {
+	.name = UBOOT_DM_CLK_AT91_RM9200_MAIN,
+	.id = UCLASS_CLK,
+	.ops = &rm9200_main_clk_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+static inline bool clk_sam9x5_main_ready(void __iomem *reg)
+{
+	unsigned int val;
+
+	pmc_read(reg, AT91_PMC_SR, &val);
+
+	return !!(val & AT91_PMC_MOSCSELS);
+}
+
+static int clk_sam9x5_main_enable(struct clk *clk)
+{
+	struct clk_main *main = to_clk_main(clk);
+	void __iomem *reg = main->reg;
+
+	while (!clk_sam9x5_main_ready(reg)) {
+		debug("waiting for main...");
+		cpu_relax();
+	}
+
+	return clk_main_probe_frequency(reg);
+}
+
+static int clk_sam9x5_main_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk_main *main = to_clk_main(clk);
+	void __iomem *reg = main->reg;
+	unsigned int tmp, index;
+
+	index = at91_clk_mux_val_to_index(main->clk_mux_table,
+			main->num_parents, AT91_CLK_ID_TO_DID(parent->id));
+	if (index < 0)
+		return index;
+
+	pmc_read(reg, AT91_CKGR_MOR, &tmp);
+	tmp &= ~MOR_KEY_MASK;
+	tmp |= AT91_PMC_KEY;
+
+	if (index && !(tmp & AT91_PMC_MOSCSEL))
+		pmc_write(reg, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL);
+	else if (!index && (tmp & AT91_PMC_MOSCSEL))
+		pmc_write(reg, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL);
+
+	while (!clk_sam9x5_main_ready(reg))
+		cpu_relax();
+
+	return 0;
+}
+
+static const struct clk_ops sam9x5_main_clk_ops = {
+	.enable = clk_sam9x5_main_enable,
+	.set_parent = clk_sam9x5_main_set_parent,
+	.get_rate = clk_generic_get_rate,
+};
+
+struct clk *at91_clk_sam9x5_main(void __iomem *reg, const char *name,
+				 const char * const *parent_names,
+				 int num_parents, const u32 *clk_mux_table,
+				 int type)
+{
+	struct clk *clk = ERR_PTR(-ENOMEM);
+	struct clk_main *main = NULL;
+	unsigned int val;
+	int ret;
+
+	if (!reg || !name || !parent_names || !num_parents || !clk_mux_table)
+		return ERR_PTR(-EINVAL);
+
+	main = kzalloc(sizeof(*main), GFP_KERNEL);
+	if (!main)
+		return ERR_PTR(-ENOMEM);
+
+	main->reg = reg;
+	main->parent_names = parent_names;
+	main->num_parents = num_parents;
+	main->clk_mux_table = clk_mux_table;
+	main->type = type;
+	clk = &main->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+	pmc_read(reg, AT91_CKGR_MOR, &val);
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X5_MAIN, name,
+			   main->parent_names[clk_main_parent_select(val)]);
+	if (ret) {
+		kfree(main);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_sam9x5_main_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SAM9X5_MAIN,
+	.id = UCLASS_CLK,
+	.ops = &sam9x5_main_clk_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 33ded02156c8..62bfc4b3054c 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -14,6 +14,16 @@
 #define AT91_TO_CLK_ID(_t, _i)		(((_t) << 8) | ((_i) & 0xff))
 #define AT91_CLK_ID_TO_DID(_i)		((_i) & 0xff)
 
+struct clk *at91_clk_main_rc(void __iomem *reg, const char *name,
+			const char *parent_name);
+struct clk *at91_clk_main_osc(void __iomem *reg, const char *name,
+			const char *parent_name, bool bypass);
+struct clk *at91_clk_rm9200_main(void __iomem *reg, const char *name,
+			const char *parent_name);
+struct clk *at91_clk_sam9x5_main(void __iomem *reg, const char *name,
+			const char * const *parent_names, int num_parents,
+			const u32 *mux_table, int type);
+
 int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
 int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
 
-- 
2.7.4

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

* [PATCH 12/22] clk: at91: sam9x60-pll: add driver compatible with ccf
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (10 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 11/22] clk: at91: clk-main: " Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 13/22] clk: at91: clk-master: " Claudiu Beznea
                   ` (9 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add sam9x60-pll driver compatible with common clock framework.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/Kconfig           |   7 +
 drivers/clk/at91/Makefile          |   1 +
 drivers/clk/at91/clk-sam9x60-pll.c | 442 +++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.h             |  36 +++
 4 files changed, 486 insertions(+)
 create mode 100644 drivers/clk/at91/clk-sam9x60-pll.c

diff --git a/drivers/clk/at91/Kconfig b/drivers/clk/at91/Kconfig
index 8d482a275239..4abc8026b4da 100644
--- a/drivers/clk/at91/Kconfig
+++ b/drivers/clk/at91/Kconfig
@@ -54,3 +54,10 @@ config AT91_GENERIC_CLK
 	  that may be different from the system clock. This second
 	  clock is the generic clock (GCLK) and is managed by
 	  the PMC via PMC_PCR register.
+
+config AT91_SAM9X60_PLL
+	bool "PLL support for SAM9X60 SoCs"
+	depends on CLK_AT91
+	help
+	  This option is used to enable the AT91 SAM9X60's PLL clock
+	  driver.
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 805f67677a9b..338582b88aee 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -4,6 +4,7 @@
 
 ifdef CONFIG_CLK_CCF
 obj-y += pmc.o sckc.o clk-main.o
+obj-$(CONFIG_AT91_SAM9X60_PLL)	+= clk-sam9x60-pll.o
 else
 obj-y += compat.o
 endif
diff --git a/drivers/clk/at91/clk-sam9x60-pll.c b/drivers/clk/at91/clk-sam9x60-pll.c
new file mode 100644
index 000000000000..1bfae5fd0165
--- /dev/null
+++ b/drivers/clk/at91/clk-sam9x60-pll.c
@@ -0,0 +1,442 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SAM9X60's PLL clock support.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-sam9x60-pll.c from Linux.
+ *
+ */
+
+#include <asm/processor.h>
+#include <common.h>
+#include <clk-uclass.h>
+#include <div64.h>
+#include <dm.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/delay.h>
+
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL	"at91-sam9x60-div-pll-clk"
+#define UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL	"at91-sam9x60-frac-pll-clk"
+
+#define	PMC_PLL_CTRL0_DIV_MSK	GENMASK(7, 0)
+#define	PMC_PLL_CTRL1_MUL_MSK	GENMASK(31, 24)
+#define PMC_PLL_CTRL1_FRACR_MSK	GENMASK(21, 0)
+
+#define PLL_DIV_MAX		(FIELD_GET(PMC_PLL_CTRL0_DIV_MSK, UINT_MAX) + 1)
+#define UPLL_DIV		2
+#define PLL_MUL_MAX		(FIELD_GET(PMC_PLL_CTRL1_MUL_MSK, UINT_MAX) + 1)
+
+#define FCORE_MIN		(600000000)
+#define FCORE_MAX		(1200000000)
+
+#define PLL_MAX_ID		7
+
+struct sam9x60_pll {
+	void __iomem *base;
+	const struct clk_pll_characteristics *characteristics;
+	const struct clk_pll_layout *layout;
+	struct clk clk;
+	u8 id;
+};
+
+#define to_sam9x60_pll(_clk)	container_of(_clk, struct sam9x60_pll, clk)
+
+static inline bool sam9x60_pll_ready(void __iomem *base, int id)
+{
+	unsigned int status;
+
+	pmc_read(base, AT91_PMC_PLL_ISR0, &status);
+
+	return !!(status & BIT(id));
+}
+
+static long sam9x60_frac_pll_compute_mul_frac(u32 *mul, u32 *frac, ulong rate,
+					      ulong parent_rate)
+{
+	unsigned long tmprate, remainder;
+	unsigned long nmul = 0;
+	unsigned long nfrac = 0;
+
+	if (rate < FCORE_MIN || rate > FCORE_MAX)
+		return -ERANGE;
+
+	/*
+	 * Calculate the multiplier associated with the current
+	 * divider that provide the closest rate to the requested one.
+	 */
+	nmul = mult_frac(rate, 1, parent_rate);
+	tmprate = mult_frac(parent_rate, nmul, 1);
+	remainder = rate - tmprate;
+
+	if (remainder) {
+		nfrac = DIV_ROUND_CLOSEST_ULL((u64)remainder * (1 << 22),
+					      parent_rate);
+
+		tmprate += DIV_ROUND_CLOSEST_ULL((u64)nfrac * parent_rate,
+						 (1 << 22));
+	}
+
+	/* Check if resulted rate is valid.  */
+	if (tmprate < FCORE_MIN || tmprate > FCORE_MAX)
+		return -ERANGE;
+
+	*mul = nmul - 1;
+	*frac = nfrac;
+
+	return tmprate;
+}
+
+static ulong sam9x60_frac_pll_set_rate(struct clk *clk, ulong rate)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u32 nmul, cmul, nfrac, cfrac, val;
+	bool ready = sam9x60_pll_ready(base, pll->id);
+	long ret;
+
+	if (!parent_rate)
+		return 0;
+
+	ret = sam9x60_frac_pll_compute_mul_frac(&nmul, &nfrac, rate,
+						parent_rate);
+	if (ret < 0)
+		return 0;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+	pmc_read(base, AT91_PMC_PLL_CTRL1, &val);
+	cmul = (val & pll->layout->mul_mask) >> pll->layout->mul_shift;
+	cfrac = (val & pll->layout->frac_mask) >> pll->layout->frac_shift;
+
+	/* Check against current values. */
+	if (sam9x60_pll_ready(base, pll->id) &&
+	    nmul == cmul && nfrac == cfrac)
+		return 0;
+
+	/* Update it to hardware. */
+	pmc_write(base, AT91_PMC_PLL_CTRL1,
+		  (nmul << pll->layout->mul_shift) |
+		  (nfrac << pll->layout->frac_shift));
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+
+	while (ready && !sam9x60_pll_ready(base, pll->id)) {
+		debug("waiting for pll %u...\n", pll->id);
+		cpu_relax();
+	}
+
+	return parent_rate * (nmul + 1) + ((u64)parent_rate * nfrac >> 22);
+}
+
+static ulong sam9x60_frac_pll_get_rate(struct clk *clk)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u32 mul, frac, val;
+
+	if (!parent_rate)
+		return 0;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+	pmc_read(base, AT91_PMC_PLL_CTRL1, &val);
+	mul = (val & pll->layout->mul_mask) >> pll->layout->mul_shift;
+	frac = (val & pll->layout->frac_mask) >> pll->layout->frac_shift;
+
+	return (parent_rate * (mul + 1) + ((u64)parent_rate * frac >> 22));
+}
+
+static int sam9x60_frac_pll_enable(struct clk *clk)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+	unsigned int val;
+	ulong crate;
+
+	crate = sam9x60_frac_pll_get_rate(clk);
+	if (crate < FCORE_MIN || crate > FCORE_MAX)
+		return -ERANGE;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+	pmc_read(base, AT91_PMC_PLL_CTRL1, &val);
+
+	if (sam9x60_pll_ready(base, pll->id))
+		return 0;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PMM_UPDT_STUPTIM_MSK |
+			AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_STUPTIM(0x3f) | pll->id);
+
+	/* Recommended value for AT91_PMC_PLL_ACR */
+	if (pll->characteristics->upll)
+		val = AT91_PMC_PLL_ACR_DEFAULT_UPLL;
+	else
+		val = AT91_PMC_PLL_ACR_DEFAULT_PLLA;
+	pmc_write(base, AT91_PMC_PLL_ACR, val);
+
+	if (pll->characteristics->upll) {
+		/* Enable the UTMI internal bandgap */
+		val |= AT91_PMC_PLL_ACR_UTMIBG;
+		pmc_write(base, AT91_PMC_PLL_ACR, val);
+
+		udelay(10);
+
+		/* Enable the UTMI internal regulator */
+		val |= AT91_PMC_PLL_ACR_UTMIVR;
+		pmc_write(base, AT91_PMC_PLL_ACR, val);
+
+		udelay(10);
+
+		pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+				AT91_PMC_PLL_UPDT_UPDATE |
+				AT91_PMC_PLL_UPDT_ID_MSK,
+				AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+	}
+
+	pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
+			AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL,
+			AT91_PMC_PLL_CTRL0_ENLOCK | AT91_PMC_PLL_CTRL0_ENPLL);
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+
+	while (!sam9x60_pll_ready(base, pll->id)) {
+		debug("waiting for pll %u...\n", pll->id);
+		cpu_relax();
+	}
+
+	return 0;
+}
+
+static int sam9x60_frac_pll_disable(struct clk *clk)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+
+	pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
+			AT91_PMC_PLL_CTRL0_ENPLL, 0);
+
+	if (pll->characteristics->upll)
+		pmc_update_bits(base, AT91_PMC_PLL_ACR,
+				AT91_PMC_PLL_ACR_UTMIBG |
+				AT91_PMC_PLL_ACR_UTMIVR, 0);
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+
+	return 0;
+}
+
+static const struct clk_ops sam9x60_frac_pll_ops = {
+	.enable = sam9x60_frac_pll_enable,
+	.disable = sam9x60_frac_pll_disable,
+	.set_rate = sam9x60_frac_pll_set_rate,
+	.get_rate = sam9x60_frac_pll_get_rate,
+};
+
+static int sam9x60_div_pll_enable(struct clk *clk)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+	unsigned int val;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+	pmc_read(base, AT91_PMC_PLL_CTRL0, &val);
+
+	/* Stop if enabled. */
+	if (val & pll->layout->endiv_mask)
+		return 0;
+
+	pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
+			pll->layout->endiv_mask,
+			(1 << pll->layout->endiv_shift));
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+
+	while (!sam9x60_pll_ready(base, pll->id)) {
+		debug("waiting for pll %u...\n", pll->id);
+		cpu_relax();
+	}
+
+	return 0;
+}
+
+static int sam9x60_div_pll_disable(struct clk *clk)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+
+	pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
+			pll->layout->endiv_mask, 0);
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+
+	return 0;
+}
+
+static ulong sam9x60_div_pll_set_rate(struct clk *clk, ulong rate)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+	const struct clk_pll_characteristics *characteristics =
+							pll->characteristics;
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u8 div = DIV_ROUND_CLOSEST_ULL(parent_rate, rate) - 1;
+	ulong req_rate = parent_rate / (div + 1);
+	bool ready = sam9x60_pll_ready(base, pll->id);
+	u32 val;
+
+	if (!parent_rate || div > pll->layout->div_mask ||
+	    req_rate < characteristics->output[0].min ||
+	    req_rate > characteristics->output[0].max)
+		return 0;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+	pmc_read(base, AT91_PMC_PLL_CTRL0, &val);
+	/* Compare against current value. */
+	if (div == ((val & pll->layout->div_mask) >> pll->layout->div_shift))
+		return 0;
+
+	/* Update it to hardware. */
+	pmc_update_bits(base, AT91_PMC_PLL_CTRL0,
+			pll->layout->div_mask,
+			div << pll->layout->div_shift);
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT,
+			AT91_PMC_PLL_UPDT_UPDATE | AT91_PMC_PLL_UPDT_ID_MSK,
+			AT91_PMC_PLL_UPDT_UPDATE | pll->id);
+
+	while (ready && !sam9x60_pll_ready(base, pll->id)) {
+		debug("waiting for pll %u...\n", pll->id);
+		cpu_relax();
+	}
+
+	return req_rate;
+}
+
+static ulong sam9x60_div_pll_get_rate(struct clk *clk)
+{
+	struct sam9x60_pll *pll = to_sam9x60_pll(clk);
+	void __iomem *base = pll->base;
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u32 val;
+	u8 div;
+
+	if (!parent_rate)
+		return 0;
+
+	pmc_update_bits(base, AT91_PMC_PLL_UPDT, AT91_PMC_PLL_UPDT_ID_MSK,
+			pll->id);
+
+	pmc_read(base, AT91_PMC_PLL_CTRL0, &val);
+
+	div = (val & pll->layout->div_mask) >> pll->layout->div_shift;
+
+	return parent_rate / (div + 1);
+}
+
+static const struct clk_ops sam9x60_div_pll_ops = {
+	.enable = sam9x60_div_pll_enable,
+	.disable = sam9x60_div_pll_disable,
+	.set_rate = sam9x60_div_pll_set_rate,
+	.get_rate = sam9x60_div_pll_get_rate,
+};
+
+static struct clk *
+sam9x60_clk_register_pll(void __iomem *base, const char *type,
+			 const char *name, const char *parent_name, u8 id,
+			 const struct clk_pll_characteristics *characteristics,
+			 const struct clk_pll_layout *layout, u32 flags)
+{
+	struct sam9x60_pll *pll;
+	struct clk *clk;
+	int ret;
+
+	if (!base || !type || !name || !parent_name || !characteristics ||
+	    !layout || id > PLL_MAX_ID)
+		return ERR_PTR(-EINVAL);
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	pll->id = id;
+	pll->characteristics = characteristics;
+	pll->layout = layout;
+	pll->base = base;
+	clk = &pll->clk;
+	clk->flags = flags;
+
+	ret = clk_register(clk, type, name, parent_name);
+	if (ret) {
+		kfree(pll);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+struct clk *
+sam9x60_clk_register_div_pll(void __iomem *base, const char *name,
+			     const char *parent_name, u8 id,
+			     const struct clk_pll_characteristics *characteristics,
+			     const struct clk_pll_layout *layout, bool critical)
+{
+	return sam9x60_clk_register_pll(base,
+		UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL, name, parent_name, id,
+		characteristics, layout,
+		CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0));
+}
+
+struct clk *
+sam9x60_clk_register_frac_pll(void __iomem *base, const char *name,
+			      const char *parent_name, u8 id,
+			      const struct clk_pll_characteristics *characteristics,
+			      const struct clk_pll_layout *layout, bool critical)
+{
+	return sam9x60_clk_register_pll(base,
+		UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL, name, parent_name, id,
+		characteristics, layout,
+		CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0));
+}
+
+U_BOOT_DRIVER(at91_sam9x60_div_pll_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SAM9X60_DIV_PLL,
+	.id = UCLASS_CLK,
+	.ops = &sam9x60_div_pll_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+U_BOOT_DRIVER(at91_sam9x60_frac_pll_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SAM9X60_FRAC_PLL,
+	.id = UCLASS_CLK,
+	.ops = &sam9x60_frac_pll_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 62bfc4b3054c..b24d2d89903d 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -14,6 +14,32 @@
 #define AT91_TO_CLK_ID(_t, _i)		(((_t) << 8) | ((_i) & 0xff))
 #define AT91_CLK_ID_TO_DID(_i)		((_i) & 0xff)
 
+struct clk_range {
+	unsigned long min;
+	unsigned long max;
+};
+
+struct clk_pll_characteristics {
+	struct clk_range input;
+	int num_output;
+	const struct clk_range *output;
+	u16 *icpll;
+	u8 *out;
+	u8 upll : 1;
+};
+
+struct clk_pll_layout {
+	u32 pllr_mask;
+	u32 mul_mask;
+	u32 frac_mask;
+	u32 div_mask;
+	u32 endiv_mask;
+	u8 mul_shift;
+	u8 frac_shift;
+	u8 div_shift;
+	u8 endiv_shift;
+};
+
 struct clk *at91_clk_main_rc(void __iomem *reg, const char *name,
 			const char *parent_name);
 struct clk *at91_clk_main_osc(void __iomem *reg, const char *name,
@@ -23,6 +49,16 @@ struct clk *at91_clk_rm9200_main(void __iomem *reg, const char *name,
 struct clk *at91_clk_sam9x5_main(void __iomem *reg, const char *name,
 			const char * const *parent_names, int num_parents,
 			const u32 *mux_table, int type);
+struct clk *
+sam9x60_clk_register_div_pll(void __iomem *base, const char *name,
+			const char *parent_name, u8 id,
+			const struct clk_pll_characteristics *characteristics,
+			const struct clk_pll_layout *layout, bool critical);
+struct clk *
+sam9x60_clk_register_frac_pll(void __iomem *base, const char *name,
+			const char *parent_name, u8 id,
+			const struct clk_pll_characteristics *characteristics,
+			const struct clk_pll_layout *layout, bool critical);
 
 int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
 int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
-- 
2.7.4

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

* [PATCH 13/22] clk: at91: clk-master: add driver compatible with ccf
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (11 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 12/22] clk: at91: sam9x60-pll: " Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 14/22] clk: at91: clk-master: add support for sama7g5 Claudiu Beznea
                   ` (8 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add clk-master driver compatible with common clock framework.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/Makefile     |   2 +-
 drivers/clk/at91/clk-master.c | 156 ++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.h        |  21 ++++++
 3 files changed, 178 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/at91/clk-master.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 338582b88aee..a4e397066e1e 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -3,7 +3,7 @@
 #
 
 ifdef CONFIG_CLK_CCF
-obj-y += pmc.o sckc.o clk-main.o
+obj-y += pmc.o sckc.o clk-main.o clk-master.o
 obj-$(CONFIG_AT91_SAM9X60_PLL)	+= clk-sam9x60-pll.o
 else
 obj-y += compat.o
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
new file mode 100644
index 000000000000..1d388b6b2519
--- /dev/null
+++ b/drivers/clk/at91/clk-master.c
@@ -0,0 +1,156 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Master clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-master.c from Linux.
+ */
+
+#include <asm/processor.h>
+#include <clk-uclass.h>
+#include <common.h>
+#include <dm.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_MASTER		"at91-master-clk"
+
+#define MASTER_PRES_MASK	0x7
+#define MASTER_PRES_MAX		MASTER_PRES_MASK
+#define MASTER_DIV_SHIFT	8
+#define MASTER_DIV_MASK		0x3
+
+struct clk_master {
+	void __iomem *base;
+	const struct clk_master_layout *layout;
+	const struct clk_master_characteristics *characteristics;
+	const u32 *mux_table;
+	const u32 *clk_mux_table;
+	u32 num_parents;
+	struct clk clk;
+	u8 id;
+};
+
+#define to_clk_master(_clk) container_of(_clk, struct clk_master, clk)
+
+static inline bool clk_master_ready(struct clk_master *master)
+{
+	unsigned int status;
+
+	pmc_read(master->base, AT91_PMC_SR, &status);
+
+	return !!(status & AT91_PMC_MCKRDY);
+}
+
+static int clk_master_enable(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+
+	while (!clk_master_ready(master)) {
+		debug("waiting for mck %d\n", master->id);
+		cpu_relax();
+	}
+
+	return 0;
+}
+
+static ulong clk_master_get_rate(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+	const struct clk_master_layout *layout = master->layout;
+	const struct clk_master_characteristics *characteristics =
+						master->characteristics;
+	ulong rate = clk_get_parent_rate(clk);
+	unsigned int mckr;
+	u8 pres, div;
+
+	if (!rate)
+		return 0;
+
+	pmc_read(master->base, master->layout->offset, &mckr);
+	mckr &= layout->mask;
+
+	pres = (mckr >> layout->pres_shift) & MASTER_PRES_MASK;
+	div = (mckr >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
+
+	if (characteristics->have_div3_pres && pres == MASTER_PRES_MAX)
+		rate /= 3;
+	else
+		rate >>= pres;
+
+	rate /= characteristics->divisors[div];
+
+	if (rate < characteristics->output.min)
+		pr_warn("master clk is underclocked");
+	else if (rate > characteristics->output.max)
+		pr_warn("master clk is overclocked");
+
+	return rate;
+}
+
+static const struct clk_ops master_ops = {
+	.enable = clk_master_enable,
+	.get_rate = clk_master_get_rate,
+};
+
+struct clk *at91_clk_register_master(void __iomem *base,
+		const char *name, const char * const *parent_names,
+		int num_parents, const struct clk_master_layout *layout,
+		const struct clk_master_characteristics *characteristics,
+		const u32 *mux_table)
+{
+	struct clk_master *master;
+	struct clk *clk;
+	unsigned int val;
+	int ret;
+
+	if (!base || !name || !num_parents || !parent_names ||
+	    !layout || !characteristics || !mux_table)
+		return ERR_PTR(-EINVAL);
+
+	master = kzalloc(sizeof(*master), GFP_KERNEL);
+	if (!master)
+		return ERR_PTR(-ENOMEM);
+
+	master->layout = layout;
+	master->characteristics = characteristics;
+	master->base = base;
+	master->num_parents = num_parents;
+	master->mux_table = mux_table;
+
+	pmc_read(master->base, master->layout->offset, &val);
+	clk = &master->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE | CLK_IS_CRITICAL;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_MASTER, name,
+			   parent_names[val & AT91_PMC_CSS]);
+	if (ret) {
+		kfree(master);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_master_clk) = {
+	.name = UBOOT_DM_CLK_AT91_MASTER,
+	.id = UCLASS_CLK,
+	.ops = &master_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+const struct clk_master_layout at91rm9200_master_layout = {
+	.mask = 0x31F,
+	.pres_shift = 2,
+	.offset = AT91_PMC_MCKR,
+};
+
+const struct clk_master_layout at91sam9x5_master_layout = {
+	.mask = 0x373,
+	.pres_shift = 4,
+	.offset = AT91_PMC_MCKR,
+};
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index b24d2d89903d..33c7a66e84b5 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -19,6 +19,21 @@ struct clk_range {
 	unsigned long max;
 };
 
+struct clk_master_layout {
+	u32 offset;
+	u32 mask;
+	u8 pres_shift;
+};
+
+extern const struct clk_master_layout at91rm9200_master_layout;
+extern const struct clk_master_layout at91sam9x5_master_layout;
+
+struct clk_master_characteristics {
+	struct clk_range output;
+	u32 divisors[4];
+	u8 have_div3_pres;
+};
+
 struct clk_pll_characteristics {
 	struct clk_range input;
 	int num_output;
@@ -59,6 +74,12 @@ sam9x60_clk_register_frac_pll(void __iomem *base, const char *name,
 			const char *parent_name, u8 id,
 			const struct clk_pll_characteristics *characteristics,
 			const struct clk_pll_layout *layout, bool critical);
+struct clk *
+at91_clk_register_master(void __iomem *base, const char *name,
+			const char * const *parent_names, int num_parents,
+			const struct clk_master_layout *layout,
+			const struct clk_master_characteristics *characteristics,
+			const u32 *mux_table);
 
 int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
 int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
-- 
2.7.4

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

* [PATCH 14/22] clk: at91: clk-master: add support for sama7g5
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (12 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 13/22] clk: at91: clk-master: " Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 15/22] clk: at91: clk-utmi: add driver compatible with ccf Claudiu Beznea
                   ` (7 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add master clock (MCK1..MCK4) support for SAMA7G5. SAMA7G5's PMC has
multiple master clocks feeding different subsystems.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/clk-master.c | 178 +++++++++++++++++++++++++++++++++++++++++-
 drivers/clk/at91/pmc.h        |   5 ++
 2 files changed, 182 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
index 1d388b6b2519..759df93697d2 100644
--- a/drivers/clk/at91/clk-master.c
+++ b/drivers/clk/at91/clk-master.c
@@ -19,12 +19,25 @@
 #include "pmc.h"
 
 #define UBOOT_DM_CLK_AT91_MASTER		"at91-master-clk"
+#define UBOOT_DM_CLK_AT91_SAMA7G5_MASTER	"at91-sama7g5-master-clk"
 
 #define MASTER_PRES_MASK	0x7
 #define MASTER_PRES_MAX		MASTER_PRES_MASK
 #define MASTER_DIV_SHIFT	8
 #define MASTER_DIV_MASK		0x3
 
+#define PMC_MCR			0x30
+#define PMC_MCR_ID_MSK		GENMASK(3, 0)
+#define PMC_MCR_CMD		BIT(7)
+#define PMC_MCR_DIV		GENMASK(10, 8)
+#define PMC_MCR_CSS		GENMASK(20, 16)
+#define PMC_MCR_CSS_SHIFT	(16)
+#define PMC_MCR_EN		BIT(28)
+
+#define PMC_MCR_ID(x)		((x) & PMC_MCR_ID_MSK)
+
+#define MASTER_MAX_ID		4
+
 struct clk_master {
 	void __iomem *base;
 	const struct clk_master_layout *layout;
@@ -40,11 +53,12 @@ struct clk_master {
 
 static inline bool clk_master_ready(struct clk_master *master)
 {
+	unsigned int bit = master->id ? AT91_PMC_MCKXRDY : AT91_PMC_MCKRDY;
 	unsigned int status;
 
 	pmc_read(master->base, AT91_PMC_SR, &status);
 
-	return !!(status & AT91_PMC_MCKRDY);
+	return !!(status & bit);
 }
 
 static int clk_master_enable(struct clk *clk)
@@ -143,6 +157,168 @@ U_BOOT_DRIVER(at91_master_clk) = {
 	.flags = DM_FLAG_PRE_RELOC,
 };
 
+static int clk_sama7g5_master_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk_master *master = to_clk_master(clk);
+	int index;
+
+	index = at91_clk_mux_val_to_index(master->clk_mux_table,
+					  master->num_parents, parent->id);
+	if (index < 0)
+		return index;
+
+	index = at91_clk_mux_index_to_val(master->mux_table,
+					  master->num_parents, index);
+	if (index < 0)
+		return index;
+
+	pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
+	pmc_update_bits(master->base, PMC_MCR,
+			PMC_MCR_CSS | PMC_MCR_CMD | PMC_MCR_ID_MSK,
+			(index << PMC_MCR_CSS_SHIFT) | PMC_MCR_CMD |
+			PMC_MCR_ID(master->id));
+	return 0;
+}
+
+static int clk_sama7g5_master_enable(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+
+	pmc_write(master->base, PMC_MCR, PMC_MCR_ID(master->id));
+	pmc_update_bits(master->base, PMC_MCR,
+			PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
+			PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID(master->id));
+
+	return 0;
+}
+
+static int clk_sama7g5_master_disable(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+
+	pmc_write(master->base, PMC_MCR, master->id);
+	pmc_update_bits(master->base, PMC_MCR,
+			PMC_MCR_EN | PMC_MCR_CMD | PMC_MCR_ID_MSK,
+			PMC_MCR_CMD | PMC_MCR_ID(master->id));
+
+	return 0;
+}
+
+static ulong clk_sama7g5_master_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk_master *master = to_clk_master(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	ulong div, rrate;
+
+	if (!parent_rate)
+		return 0;
+
+	div = DIV_ROUND_CLOSEST(parent_rate, rate);
+	if ((div > (1 << (MASTER_PRES_MAX - 1))) || (div & (div - 1))) {
+		return 0;
+	} else if (div == 3) {
+		rrate = DIV_ROUND_CLOSEST(parent_rate, MASTER_PRES_MAX);
+		div = MASTER_PRES_MAX;
+	} else {
+		rrate = DIV_ROUND_CLOSEST(parent_rate, div);
+		div = ffs(div) - 1;
+	}
+
+	pmc_write(master->base, PMC_MCR, master->id);
+	pmc_update_bits(master->base, PMC_MCR,
+			PMC_MCR_DIV | PMC_MCR_CMD | PMC_MCR_ID_MSK,
+			(div << MASTER_DIV_SHIFT) | PMC_MCR_CMD |
+			PMC_MCR_ID(master->id));
+
+	return rrate;
+}
+
+static ulong clk_sama7g5_master_get_rate(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	unsigned int val;
+	ulong div;
+
+	if (!parent_rate)
+		return 0;
+
+	pmc_write(master->base, PMC_MCR, master->id);
+	pmc_read(master->base, PMC_MCR, &val);
+
+	div = (val >> MASTER_DIV_SHIFT) & MASTER_DIV_MASK;
+
+	if (div == MASTER_PRES_MAX)
+		div = 3;
+	else
+		div = 1 << div;
+
+	return DIV_ROUND_CLOSEST(parent_rate, div);
+}
+
+static const struct clk_ops sama7g5_master_ops = {
+	.enable = clk_sama7g5_master_enable,
+	.disable = clk_sama7g5_master_disable,
+	.set_rate = clk_sama7g5_master_set_rate,
+	.get_rate = clk_sama7g5_master_get_rate,
+	.set_parent = clk_sama7g5_master_set_parent,
+};
+
+struct clk *at91_clk_sama7g5_register_master(void __iomem *base,
+		const char *name, const char * const *parent_names,
+		int num_parents, const u32 *mux_table, const u32 *clk_mux_table,
+		bool critical, u8 id)
+{
+	struct clk_master *master;
+	struct clk *clk;
+	u32 val, index;
+	int ret;
+
+	if (!base || !name || !num_parents || !parent_names ||
+	    !mux_table || !clk_mux_table || id > MASTER_MAX_ID)
+		return ERR_PTR(-EINVAL);
+
+	master = kzalloc(sizeof(*master), GFP_KERNEL);
+	if (!master)
+		return ERR_PTR(-ENOMEM);
+
+	master->base = base;
+	master->id = id;
+	master->mux_table = mux_table;
+	master->clk_mux_table = clk_mux_table;
+	master->num_parents = num_parents;
+
+	pmc_write(master->base, PMC_MCR, master->id);
+	pmc_read(master->base, PMC_MCR, &val);
+
+	index = at91_clk_mux_val_to_index(master->mux_table,
+				master->num_parents,
+				(val & PMC_MCR_CSS) >> PMC_MCR_CSS_SHIFT);
+	if (index < 0) {
+		kfree(master);
+		return ERR_PTR(index);
+	}
+
+	clk = &master->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE | (critical ? CLK_IS_CRITICAL : 0);
+
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_MASTER, name,
+			   parent_names[index]);
+	if (ret) {
+		kfree(master);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_sama7g5_master_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SAMA7G5_MASTER,
+	.id = UCLASS_CLK,
+	.ops = &sama7g5_master_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
 const struct clk_master_layout at91rm9200_master_layout = {
 	.mask = 0x31F,
 	.pres_shift = 2,
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 33c7a66e84b5..49e1e372b897 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -80,6 +80,11 @@ at91_clk_register_master(void __iomem *base, const char *name,
 			const struct clk_master_layout *layout,
 			const struct clk_master_characteristics *characteristics,
 			const u32 *mux_table);
+struct clk *
+at91_clk_sama7g5_register_master(void __iomem *base, const char *name,
+			const char * const *parent_names, int num_parents,
+			const u32 *mux_table, const u32 *clk_mux_table,
+			bool critical, u8 id);
 
 int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
 int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
-- 
2.7.4

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

* [PATCH 15/22] clk: at91: clk-utmi: add driver compatible with ccf
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (13 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 14/22] clk: at91: clk-master: add support for sama7g5 Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 16/22] clk: at91: clk-utmi: add support for sama7g5 Claudiu Beznea
                   ` (6 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add clk-utmi driver compatible with common clock framework.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/Makefile   |   1 +
 drivers/clk/at91/clk-utmi.c | 165 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.h      |   3 +
 3 files changed, 169 insertions(+)
 create mode 100644 drivers/clk/at91/clk-utmi.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index a4e397066e1e..9c38bda5e6b7 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -4,6 +4,7 @@
 
 ifdef CONFIG_CLK_CCF
 obj-y += pmc.o sckc.o clk-main.o clk-master.o
+obj-$(CONFIG_AT91_UTMI)		+= clk-utmi.o
 obj-$(CONFIG_AT91_SAM9X60_PLL)	+= clk-sam9x60-pll.o
 else
 obj-y += compat.o
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
new file mode 100644
index 000000000000..b60fd35b6b6a
--- /dev/null
+++ b/drivers/clk/at91/clk-utmi.c
@@ -0,0 +1,165 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * UTMI clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-utmi.c from Linux.
+ */
+#include <asm/processor.h>
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+#include <mach/at91_sfr.h>
+#include <regmap.h>
+#include <syscon.h>
+
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_UTMI		"at91-utmi-clk"
+
+/*
+ * The purpose of this clock is to generate a 480 MHz signal. A different
+ * rate can't be configured.
+ */
+#define UTMI_RATE	480000000
+
+struct clk_utmi {
+	void __iomem *base;
+	struct regmap *regmap_sfr;
+	struct clk clk;
+};
+
+#define to_clk_utmi(_clk) container_of(_clk, struct clk_utmi, clk)
+
+static inline bool clk_utmi_ready(struct regmap *regmap)
+{
+	unsigned int status;
+
+	pmc_read(regmap, AT91_PMC_SR, &status);
+
+	return !!(status & AT91_PMC_LOCKU);
+}
+
+static int clk_utmi_enable(struct clk *clk)
+{
+	struct clk_utmi *utmi = to_clk_utmi(clk);
+	unsigned int uckr = AT91_PMC_UPLLEN | AT91_PMC_UPLLCOUNT |
+			    AT91_PMC_BIASEN;
+	unsigned int utmi_ref_clk_freq;
+	ulong parent_rate = clk_get_parent_rate(clk);
+
+	/*
+	 * If mainck rate is different from 12 MHz, we have to configure the
+	 * FREQ field of the SFR_UTMICKTRIM register to generate properly
+	 * the utmi clock.
+	 */
+	switch (parent_rate) {
+	case 12000000:
+		utmi_ref_clk_freq = 0;
+		break;
+	case 16000000:
+		utmi_ref_clk_freq = 1;
+		break;
+	case 24000000:
+		utmi_ref_clk_freq = 2;
+		break;
+	/*
+	 * Not supported on SAMA5D2 but it's not an issue since MAINCK
+	 * maximum value is 24 MHz.
+	 */
+	case 48000000:
+		utmi_ref_clk_freq = 3;
+		break;
+	default:
+		debug("UTMICK: unsupported mainck rate\n");
+		return -EINVAL;
+	}
+
+	if (utmi->regmap_sfr) {
+		regmap_update_bits(utmi->regmap_sfr, AT91_SFR_UTMICKTRIM,
+				   AT91_UTMICKTRIM_FREQ, utmi_ref_clk_freq);
+	} else if (utmi_ref_clk_freq) {
+		debug("UTMICK: sfr node required\n");
+		return -EINVAL;
+	}
+
+	pmc_update_bits(utmi->base, AT91_CKGR_UCKR, uckr, uckr);
+
+	while (!clk_utmi_ready(utmi->base)) {
+		debug("waiting for utmi...\n");
+		cpu_relax();
+	}
+
+	return 0;
+}
+
+static int clk_utmi_disable(struct clk *clk)
+{
+	struct clk_utmi *utmi = to_clk_utmi(clk);
+
+	pmc_update_bits(utmi->base, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0);
+
+	return 0;
+}
+
+static ulong clk_utmi_get_rate(struct clk *clk)
+{
+	/* UTMI clk rate is fixed. */
+	return UTMI_RATE;
+}
+
+static const struct clk_ops utmi_ops = {
+	.enable = clk_utmi_enable,
+	.disable = clk_utmi_disable,
+	.get_rate = clk_utmi_get_rate,
+};
+
+struct clk *at91_clk_register_utmi(void __iomem *base, struct udevice *dev,
+				   const char *name, const char *parent_name)
+{
+	struct udevice *syscon;
+	struct clk_utmi *utmi;
+	struct clk *clk;
+	int ret;
+
+	if (!base || !dev || !name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	ret = uclass_get_device_by_phandle(UCLASS_SYSCON, dev,
+					   "regmap-sfr", &syscon);
+	if (ret)
+		return ERR_PTR(ret);
+
+	utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
+	if (!utmi)
+		return ERR_PTR(-ENOMEM);
+
+	utmi->base = base;
+	utmi->regmap_sfr = syscon_get_regmap(syscon);
+	if (!utmi->regmap_sfr) {
+		kfree(utmi);
+		return ERR_PTR(-ENODEV);
+	}
+
+	clk = &utmi->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_UTMI, name, parent_name);
+	if (ret) {
+		kfree(utmi);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_utmi_clk) = {
+	.name = UBOOT_DM_CLK_AT91_UTMI,
+	.id = UCLASS_CLK,
+	.ops = &utmi_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 49e1e372b897..264b36c7b4f5 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -85,6 +85,9 @@ at91_clk_sama7g5_register_master(void __iomem *base, const char *name,
 			const char * const *parent_names, int num_parents,
 			const u32 *mux_table, const u32 *clk_mux_table,
 			bool critical, u8 id);
+struct clk *
+at91_clk_register_utmi(void __iomem *base, struct udevice *dev,
+			const char *name, const char *parent_name);
 
 int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
 int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
-- 
2.7.4

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

* [PATCH 16/22] clk: at91: clk-utmi: add support for sama7g5
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (14 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 15/22] clk: at91: clk-utmi: add driver compatible with ccf Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 17/22] clk: at91: clk-programmable: add driver compatible with ccf Claudiu Beznea
                   ` (5 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add UTMI support for SAMA7G5. SAMA7G5's UTMI control is done via
XTALF register. Values written at bits 2..0 in this register
correspond to the on board crystal oscillator frequency.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/clk-utmi.c | 71 ++++++++++++++++++++++++++++++++++++++++++++-
 drivers/clk/at91/pmc.h      |  3 ++
 2 files changed, 73 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
index b60fd35b6b6a..7c8bcfb51dba 100644
--- a/drivers/clk/at91/clk-utmi.c
+++ b/drivers/clk/at91/clk-utmi.c
@@ -20,7 +20,8 @@
 
 #include "pmc.h"
 
-#define UBOOT_DM_CLK_AT91_UTMI		"at91-utmi-clk"
+#define UBOOT_DM_CLK_AT91_UTMI			"at91-utmi-clk"
+#define UBOOT_DM_CLK_AT91_SAMA7G5_UTMI		"at91-sama7g5-utmi-clk"
 
 /*
  * The purpose of this clock is to generate a 480 MHz signal. A different
@@ -163,3 +164,71 @@ U_BOOT_DRIVER(at91_utmi_clk) = {
 	.ops = &utmi_ops,
 	.flags = DM_FLAG_PRE_RELOC,
 };
+
+static int clk_utmi_sama7g5_enable(struct clk *clk)
+{
+	struct clk_utmi *utmi = to_clk_utmi(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	unsigned int val;
+
+	switch (parent_rate) {
+	case 16000000:
+		val = 0;
+		break;
+	case 20000000:
+		val = 2;
+		break;
+	case 24000000:
+		val = 3;
+		break;
+	case 32000000:
+		val = 5;
+		break;
+	default:
+		debug("UTMICK: unsupported main_xtal rate\n");
+		return -EINVAL;
+	}
+
+	pmc_write(utmi->base, AT91_PMC_XTALF, val);
+
+	return 0;
+}
+
+static const struct clk_ops sama7g5_utmi_ops = {
+	.enable = clk_utmi_sama7g5_enable,
+	.get_rate = clk_utmi_get_rate,
+};
+
+struct clk *at91_clk_sama7g5_register_utmi(void __iomem *base,
+		const char *name, const char *parent_name)
+{
+	struct clk_utmi *utmi;
+	struct clk *clk;
+	int ret;
+
+	if (!base || !name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	utmi = kzalloc(sizeof(*utmi), GFP_KERNEL);
+	if (!utmi)
+		return ERR_PTR(-ENOMEM);
+
+	utmi->base = base;
+
+	clk = &utmi->clk;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAMA7G5_UTMI, name,
+			   parent_name);
+	if (ret) {
+		kfree(utmi);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_sama7g5_utmi_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SAMA7G5_UTMI,
+	.id = UCLASS_CLK,
+	.ops = &sama7g5_utmi_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 264b36c7b4f5..d34005f6986c 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -88,6 +88,9 @@ at91_clk_sama7g5_register_master(void __iomem *base, const char *name,
 struct clk *
 at91_clk_register_utmi(void __iomem *base, struct udevice *dev,
 			const char *name, const char *parent_name);
+struct clk *
+at91_clk_sama7g5_register_utmi(void __iomem *base, const char *name,
+			const char *parent_name);
 
 int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
 int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
-- 
2.7.4

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

* [PATCH 17/22] clk: at91: clk-programmable: add driver compatible with ccf
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (15 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 16/22] clk: at91: clk-utmi: add support for sama7g5 Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 18/22] clk: at91: clk-system: " Claudiu Beznea
                   ` (4 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add clk-programmable driver compatible with common clock framework.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/Makefile           |   2 +-
 drivers/clk/at91/clk-programmable.c | 208 ++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.h              |  17 +++
 3 files changed, 226 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/at91/clk-programmable.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 9c38bda5e6b7..8951052eb0f3 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -3,7 +3,7 @@
 #
 
 ifdef CONFIG_CLK_CCF
-obj-y += pmc.o sckc.o clk-main.o clk-master.o
+obj-y += pmc.o sckc.o clk-main.o clk-master.o clk-programmable.o
 obj-$(CONFIG_AT91_UTMI)		+= clk-utmi.o
 obj-$(CONFIG_AT91_SAM9X60_PLL)	+= clk-sam9x60-pll.o
 else
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
new file mode 100644
index 000000000000..868de4b1774b
--- /dev/null
+++ b/drivers/clk/at91/clk-programmable.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Programmable clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-programmable.c from Linux.
+ */
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_PROG		"at91-prog-clk"
+
+#define PROG_ID_MAX		7
+
+#define PROG_STATUS_MASK(id)	(1 << ((id) + 8))
+#define PROG_PRES(_l, _p)	(((_p) >> (_l)->pres_shift) & (_l)->pres_mask)
+#define PROG_MAX_RM9200_CSS	3
+
+struct clk_programmable {
+	void __iomem *base;
+	const u32 *clk_mux_table;
+	const u32 *mux_table;
+	const struct clk_programmable_layout *layout;
+	u32 num_parents;
+	struct clk clk;
+	u8 id;
+};
+
+#define to_clk_programmable(_c) container_of(_c, struct clk_programmable, clk)
+
+static ulong clk_programmable_get_rate(struct clk *clk)
+{
+	struct clk_programmable *prog = to_clk_programmable(clk);
+	const struct clk_programmable_layout *layout = prog->layout;
+	ulong rate, parent_rate = clk_get_parent_rate(clk);
+	unsigned int pckr;
+
+	pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &pckr);
+
+	if (layout->is_pres_direct)
+		rate = parent_rate / (PROG_PRES(layout, pckr) + 1);
+	else
+		rate = parent_rate >> PROG_PRES(layout, pckr);
+
+	return rate;
+}
+
+static int clk_programmable_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk_programmable *prog = to_clk_programmable(clk);
+	const struct clk_programmable_layout *layout = prog->layout;
+	unsigned int mask = layout->css_mask;
+	int index;
+
+	index = at91_clk_mux_val_to_index(prog->clk_mux_table,
+					  prog->num_parents, parent->id);
+	if (index < 0)
+		return index;
+
+	index = at91_clk_mux_index_to_val(prog->mux_table, prog->num_parents,
+					  index);
+	if (index < 0)
+		return index;
+
+	if (layout->have_slck_mck)
+		mask |= AT91_PMC_CSSMCK_MCK;
+
+	if (index > layout->css_mask) {
+		if (index > PROG_MAX_RM9200_CSS && !layout->have_slck_mck)
+			return -EINVAL;
+
+		index |= AT91_PMC_CSSMCK_MCK;
+	}
+
+	pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id), mask, index);
+
+	return 0;
+}
+
+static ulong clk_programmable_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk_programmable *prog = to_clk_programmable(clk);
+	const struct clk_programmable_layout *layout = prog->layout;
+	ulong parent_rate = clk_get_parent_rate(clk);
+	ulong div = parent_rate / rate;
+	int shift = 0;
+
+	if (!parent_rate || !div)
+		return -EINVAL;
+
+	if (layout->is_pres_direct) {
+		shift = div - 1;
+
+		if (shift > layout->pres_mask)
+			return -EINVAL;
+	} else {
+		shift = fls(div) - 1;
+
+		if (div != (1 << shift))
+			return -EINVAL;
+
+		if (shift >= layout->pres_mask)
+			return -EINVAL;
+	}
+
+	pmc_update_bits(prog->base, AT91_PMC_PCKR(prog->id),
+			layout->pres_mask << layout->pres_shift,
+			shift << layout->pres_shift);
+
+	if (layout->is_pres_direct)
+		return (parent_rate / shift + 1);
+
+	return parent_rate >> shift;
+}
+
+static const struct clk_ops programmable_ops = {
+	.get_rate = clk_programmable_get_rate,
+	.set_parent = clk_programmable_set_parent,
+	.set_rate = clk_programmable_set_rate,
+};
+
+struct clk *at91_clk_register_programmable(void __iomem *base, const char *name,
+			const char *const *parent_names, u8 num_parents, u8 id,
+			const struct clk_programmable_layout *layout,
+			const u32 *clk_mux_table, const u32 *mux_table)
+{
+	struct clk_programmable *prog;
+	struct clk *clk;
+	u32 val, tmp;
+	int ret;
+
+	if (!base || !name || !parent_names || !num_parents ||
+	    !layout || !clk_mux_table || !mux_table || id > PROG_ID_MAX)
+		return ERR_PTR(-EINVAL);
+
+	prog = kzalloc(sizeof(*prog), GFP_KERNEL);
+	if (!prog)
+		return ERR_PTR(-ENOMEM);
+
+	prog->id = id;
+	prog->layout = layout;
+	prog->base = base;
+	prog->clk_mux_table = clk_mux_table;
+	prog->mux_table = mux_table;
+	prog->num_parents = num_parents;
+
+	pmc_read(prog->base, AT91_PMC_PCKR(prog->id), &tmp);
+	val = tmp & prog->layout->css_mask;
+	if (layout->have_slck_mck && (tmp & AT91_PMC_CSSMCK_MCK) && !val)
+		ret = PROG_MAX_RM9200_CSS + 1;
+	else
+		ret = at91_clk_mux_val_to_index(prog->mux_table,
+						prog->num_parents, val);
+	if (ret < 0) {
+		kfree(prog);
+		return ERR_PTR(ret);
+	}
+
+	clk = &prog->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_PROG, name,
+			   parent_names[ret]);
+	if (ret) {
+		kfree(prog);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_prog_clk) = {
+	.name = UBOOT_DM_CLK_AT91_PROG,
+	.id = UCLASS_CLK,
+	.ops = &programmable_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+const struct clk_programmable_layout at91rm9200_programmable_layout = {
+	.pres_mask = 0x7,
+	.pres_shift = 2,
+	.css_mask = 0x3,
+	.have_slck_mck = 0,
+	.is_pres_direct = 0,
+};
+
+const struct clk_programmable_layout at91sam9g45_programmable_layout = {
+	.pres_mask = 0x7,
+	.pres_shift = 2,
+	.css_mask = 0x3,
+	.have_slck_mck = 1,
+	.is_pres_direct = 0,
+};
+
+const struct clk_programmable_layout at91sam9x5_programmable_layout = {
+	.pres_mask = 0x7,
+	.pres_shift = 4,
+	.css_mask = 0x7,
+	.have_slck_mck = 0,
+	.is_pres_direct = 0,
+};
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index d34005f6986c..a443b65257b9 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -55,6 +55,18 @@ struct clk_pll_layout {
 	u8 endiv_shift;
 };
 
+struct clk_programmable_layout {
+	u8 pres_mask;
+	u8 pres_shift;
+	u8 css_mask;
+	u8 have_slck_mck;
+	u8 is_pres_direct;
+};
+
+extern const struct clk_programmable_layout at91rm9200_programmable_layout;
+extern const struct clk_programmable_layout at91sam9g45_programmable_layout;
+extern const struct clk_programmable_layout at91sam9x5_programmable_layout;
+
 struct clk *at91_clk_main_rc(void __iomem *reg, const char *name,
 			const char *parent_name);
 struct clk *at91_clk_main_osc(void __iomem *reg, const char *name,
@@ -91,6 +103,11 @@ at91_clk_register_utmi(void __iomem *base, struct udevice *dev,
 struct clk *
 at91_clk_sama7g5_register_utmi(void __iomem *base, const char *name,
 			const char *parent_name);
+struct clk *
+at91_clk_register_programmable(void __iomem *base, const char *name,
+			const char * const *parent_names, u8 num_parents, u8 id,
+			const struct clk_programmable_layout *layout,
+			const u32 *clk_mux_table, const u32 *mux_table);
 
 int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
 int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
-- 
2.7.4

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

* [PATCH 18/22] clk: at91: clk-system: add driver compatible with ccf
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (16 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 17/22] clk: at91: clk-programmable: add driver compatible with ccf Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 19/22] clk: at91: clk-peripheral: " Claudiu Beznea
                   ` (3 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add clk-system driver compatible with common clock framework.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/Makefile     |   2 +-
 drivers/clk/at91/clk-system.c | 112 ++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.h        |   3 ++
 3 files changed, 116 insertions(+), 1 deletion(-)
 create mode 100644 drivers/clk/at91/clk-system.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 8951052eb0f3..1d59531e0c42 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -3,7 +3,7 @@
 #
 
 ifdef CONFIG_CLK_CCF
-obj-y += pmc.o sckc.o clk-main.o clk-master.o clk-programmable.o
+obj-y += pmc.o sckc.o clk-main.o clk-master.o clk-programmable.o clk-system.o
 obj-$(CONFIG_AT91_UTMI)		+= clk-utmi.o
 obj-$(CONFIG_AT91_SAM9X60_PLL)	+= clk-sam9x60-pll.o
 else
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
new file mode 100644
index 000000000000..82f79e74a190
--- /dev/null
+++ b/drivers/clk/at91/clk-system.c
@@ -0,0 +1,112 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * System clock support for AT91 architectures.
+ *
+ * Copyright (C) Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-system.c from Linux.
+ */
+#include <asm/processor.h>
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_SYSTEM		"at91-system-clk"
+
+#define SYSTEM_MAX_ID		31
+
+struct clk_system {
+	void __iomem *base;
+	struct clk clk;
+	u8 id;
+};
+
+#define to_clk_system(_c) container_of(_c, struct clk_system, clk)
+
+static inline int is_pck(int id)
+{
+	return (id >= 8) && (id <= 15);
+}
+
+static inline bool clk_system_ready(void __iomem *base, int id)
+{
+	unsigned int status;
+
+	pmc_read(base, AT91_PMC_SR, &status);
+
+	return !!(status & (1 << id));
+}
+
+static int clk_system_enable(struct clk *clk)
+{
+	struct clk_system *sys = to_clk_system(clk);
+
+	pmc_write(sys->base, AT91_PMC_SCER, 1 << sys->id);
+
+	if (!is_pck(sys->id))
+		return 0;
+
+	while (!clk_system_ready(sys->base, sys->id)) {
+		debug("waiting for pck%u\n", sys->id);
+		cpu_relax();
+	}
+
+	return 0;
+}
+
+static int clk_system_disable(struct clk *clk)
+{
+	struct clk_system *sys = to_clk_system(clk);
+
+	pmc_write(sys->base, AT91_PMC_SCDR, 1 << sys->id);
+
+	return 0;
+}
+
+static const struct clk_ops system_ops = {
+	.enable = clk_system_enable,
+	.disable = clk_system_disable,
+	.get_rate = clk_generic_get_rate,
+};
+
+struct clk *at91_clk_register_system(void __iomem *base, const char *name,
+				     const char *parent_name, u8 id)
+{
+	struct clk_system *sys;
+	struct clk *clk;
+	int ret;
+
+	if (!base || !name || !parent_name || id > SYSTEM_MAX_ID)
+		return ERR_PTR(-EINVAL);
+
+	sys = kzalloc(sizeof(*sys), GFP_KERNEL);
+	if (!sys)
+		return ERR_PTR(-ENOMEM);
+
+	sys->id = id;
+	sys->base = base;
+
+	clk = &sys->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SYSTEM, name, parent_name);
+	if (ret) {
+		kfree(sys);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_system_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SYSTEM,
+	.id = UCLASS_CLK,
+	.ops = &system_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index a443b65257b9..c372f39fc9f6 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -108,6 +108,9 @@ at91_clk_register_programmable(void __iomem *base, const char *name,
 			const char * const *parent_names, u8 num_parents, u8 id,
 			const struct clk_programmable_layout *layout,
 			const u32 *clk_mux_table, const u32 *mux_table);
+struct clk *
+at91_clk_register_system(void __iomem *base, const char *name,
+			const char *parent_name, u8 id);
 
 int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
 int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
-- 
2.7.4

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

* [PATCH 19/22] clk: at91: clk-peripheral: add driver compatible with ccf
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (17 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 18/22] clk: at91: clk-system: " Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 20/22] clk: at91: clk-generic: " Claudiu Beznea
                   ` (2 subsequent siblings)
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add clk-peripheral compatible with common clock framework.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/Makefile         |   2 +
 drivers/clk/at91/clk-peripheral.c | 254 ++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.h            |  16 +++
 3 files changed, 272 insertions(+)
 create mode 100644 drivers/clk/at91/clk-peripheral.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 1d59531e0c42..b5529065b5c4 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -4,6 +4,8 @@
 
 ifdef CONFIG_CLK_CCF
 obj-y += pmc.o sckc.o clk-main.o clk-master.o clk-programmable.o clk-system.o
+obj-y += clk-peripheral.o
+
 obj-$(CONFIG_AT91_UTMI)		+= clk-utmi.o
 obj-$(CONFIG_AT91_SAM9X60_PLL)	+= clk-sam9x60-pll.o
 else
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
new file mode 100644
index 000000000000..52cbc520cef4
--- /dev/null
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Peripheral clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-peripheral.c from Linux.
+ */
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_PERIPH		"at91-periph-clk"
+#define UBOOT_DM_CLK_AT91_SAM9X5_PERIPH		"at91-sam9x5-periph-clk"
+
+#define PERIPHERAL_ID_MIN	2
+#define PERIPHERAL_ID_MAX	31
+#define PERIPHERAL_MASK(id)	(1 << ((id) & PERIPHERAL_ID_MAX))
+
+#define PERIPHERAL_MAX_SHIFT	3
+
+struct clk_peripheral {
+	void __iomem *base;
+	struct clk clk;
+	u32 id;
+};
+
+#define to_clk_peripheral(_c) container_of(_c, struct clk_peripheral, clk)
+
+struct clk_sam9x5_peripheral {
+	const struct clk_pcr_layout *layout;
+	void __iomem *base;
+	struct clk clk;
+	struct clk_range range;
+	u32 id;
+	u32 div;
+	bool auto_div;
+};
+
+#define to_clk_sam9x5_peripheral(_c) \
+	container_of(_c, struct clk_sam9x5_peripheral, clk)
+
+static int clk_peripheral_enable(struct clk *clk)
+{
+	struct clk_peripheral *periph = to_clk_peripheral(clk);
+	int offset = AT91_PMC_PCER;
+	u32 id = periph->id;
+
+	if (id < PERIPHERAL_ID_MIN)
+		return 0;
+	if (id > PERIPHERAL_ID_MAX)
+		offset = AT91_PMC_PCER1;
+	pmc_write(periph->base, offset, PERIPHERAL_MASK(id));
+
+	return 0;
+}
+
+static int clk_peripheral_disable(struct clk *clk)
+{
+	struct clk_peripheral *periph = to_clk_peripheral(clk);
+	int offset = AT91_PMC_PCDR;
+	u32 id = periph->id;
+
+	if (id < PERIPHERAL_ID_MIN)
+		return -EINVAL;
+
+	if (id > PERIPHERAL_ID_MAX)
+		offset = AT91_PMC_PCDR1;
+	pmc_write(periph->base, offset, PERIPHERAL_MASK(id));
+
+	return 0;
+}
+
+static const struct clk_ops peripheral_ops = {
+	.enable = clk_peripheral_enable,
+	.disable = clk_peripheral_disable,
+	.get_rate = clk_generic_get_rate,
+};
+
+struct clk *
+at91_clk_register_peripheral(void __iomem *base, const char *name,
+			     const char *parent_name, u32 id)
+{
+	struct clk_peripheral *periph;
+	struct clk *clk;
+	int ret;
+
+	if (!base || !name || !parent_name || id > PERIPHERAL_ID_MAX)
+		return ERR_PTR(-EINVAL);
+
+	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
+	if (!periph)
+		return ERR_PTR(-ENOMEM);
+
+	periph->id = id;
+	periph->base = base;
+
+	clk = &periph->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_PERIPH, name, parent_name);
+	if (ret) {
+		kfree(periph);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_periph_clk) = {
+	.name = UBOOT_DM_CLK_AT91_PERIPH,
+	.id = UCLASS_CLK,
+	.ops = &peripheral_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
+
+static int clk_sam9x5_peripheral_enable(struct clk *clk)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+
+	if (periph->id < PERIPHERAL_ID_MIN)
+		return 0;
+
+	pmc_write(periph->base, periph->layout->offset,
+		  (periph->id & periph->layout->pid_mask));
+	pmc_update_bits(periph->base, periph->layout->offset,
+			periph->layout->cmd | AT91_PMC_PCR_EN,
+			periph->layout->cmd | AT91_PMC_PCR_EN);
+
+	return 0;
+}
+
+static int clk_sam9x5_peripheral_disable(struct clk *clk)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+
+	if (periph->id < PERIPHERAL_ID_MIN)
+		return -EINVAL;
+
+	pmc_write(periph->base, periph->layout->offset,
+		  (periph->id & periph->layout->pid_mask));
+	pmc_update_bits(periph->base, periph->layout->offset,
+			AT91_PMC_PCR_EN | periph->layout->cmd,
+			periph->layout->cmd);
+
+	return 0;
+}
+
+static ulong clk_sam9x5_peripheral_get_rate(struct clk *clk)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u32 val, shift = ffs(periph->layout->div_mask) - 1;
+
+	if (!parent_rate)
+		return 0;
+
+	pmc_write(periph->base, periph->layout->offset,
+		  (periph->id & periph->layout->pid_mask));
+	pmc_read(periph->base, periph->layout->offset, &val);
+	shift = (val & periph->layout->div_mask) >> shift;
+
+	return parent_rate >> shift;
+}
+
+static ulong clk_sam9x5_peripheral_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	int shift;
+
+	if (!parent_rate)
+		return 0;
+
+	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
+		if (parent_rate == rate)
+			return rate;
+		else
+			return 0;
+	}
+
+	if (periph->range.max && rate > periph->range.max)
+		return 0;
+
+	for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
+		if (parent_rate >> shift <= rate)
+			break;
+	}
+	if (shift == PERIPHERAL_MAX_SHIFT + 1)
+		return 0;
+
+	pmc_write(periph->base, periph->layout->offset,
+		  (periph->id & periph->layout->pid_mask));
+	pmc_update_bits(periph->base, periph->layout->offset,
+			periph->layout->div_mask | periph->layout->cmd,
+			(shift << (ffs(periph->layout->div_mask) - 1)) |
+			periph->layout->cmd);
+
+	return parent_rate >> shift;
+}
+
+static const struct clk_ops sam9x5_peripheral_ops = {
+	.enable = clk_sam9x5_peripheral_enable,
+	.disable = clk_sam9x5_peripheral_disable,
+	.get_rate = clk_sam9x5_peripheral_get_rate,
+	.set_rate = clk_sam9x5_peripheral_set_rate,
+};
+
+struct clk *
+at91_clk_register_sam9x5_peripheral(void __iomem *base,
+				    const struct clk_pcr_layout *layout,
+				    const char *name, const char *parent_name,
+				    u32 id, const struct clk_range *range)
+{
+	struct clk_sam9x5_peripheral *periph;
+	struct clk *clk;
+	int ret;
+
+	if (!base || !layout || !name || !parent_name || !range)
+		return ERR_PTR(-EINVAL);
+
+	periph = kzalloc(sizeof(*periph), GFP_KERNEL);
+	if (!periph)
+		return ERR_PTR(-ENOMEM);
+
+	periph->id = id;
+	periph->base = base;
+	periph->layout = layout;
+	periph->range = *range;
+
+	clk = &periph->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_SAM9X5_PERIPH, name,
+			   parent_name);
+	if (ret) {
+		kfree(periph);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_sam9x5_periph_clk) = {
+	.name = UBOOT_DM_CLK_AT91_SAM9X5_PERIPH,
+	.id = UCLASS_CLK,
+	.ops = &sam9x5_peripheral_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index c372f39fc9f6..5e80f353e6c2 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -63,6 +63,14 @@ struct clk_programmable_layout {
 	u8 is_pres_direct;
 };
 
+struct clk_pcr_layout {
+	u32 offset;
+	u32 cmd;
+	u32 div_mask;
+	u32 gckcss_mask;
+	u32 pid_mask;
+};
+
 extern const struct clk_programmable_layout at91rm9200_programmable_layout;
 extern const struct clk_programmable_layout at91sam9g45_programmable_layout;
 extern const struct clk_programmable_layout at91sam9x5_programmable_layout;
@@ -111,6 +119,14 @@ at91_clk_register_programmable(void __iomem *base, const char *name,
 struct clk *
 at91_clk_register_system(void __iomem *base, const char *name,
 			const char *parent_name, u8 id);
+struct clk *
+at91_clk_register_peripheral(void __iomem *base, const char *name,
+			const char *parent_name, u32 id);
+struct clk *
+at91_clk_register_sam9x5_peripheral(void __iomem *base,
+			const struct clk_pcr_layout *layout,
+			const char *name, const char *parent_name,
+			u32 id, const struct clk_range *range);
 
 int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
 int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
-- 
2.7.4

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

* [PATCH 20/22] clk: at91: clk-generic: add driver compatible with ccf
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (18 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 19/22] clk: at91: clk-peripheral: " Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 21/22] clk: at91: pmc: add generic clock ops Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 22/22] clk: at91: sama7g5: add clock support Claudiu Beznea
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add clk-generic driver compatible with common clock framework.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/Makefile      |   1 +
 drivers/clk/at91/clk-generic.c | 202 +++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.h         |   6 ++
 3 files changed, 209 insertions(+)
 create mode 100644 drivers/clk/at91/clk-generic.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index b5529065b5c4..2cd840af3859 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -6,6 +6,7 @@ ifdef CONFIG_CLK_CCF
 obj-y += pmc.o sckc.o clk-main.o clk-master.o clk-programmable.o clk-system.o
 obj-y += clk-peripheral.o
 
+obj-$(CONFIG_AT91_GENERIC_CLK)	+= clk-generic.o
 obj-$(CONFIG_AT91_UTMI)		+= clk-utmi.o
 obj-$(CONFIG_AT91_SAM9X60_PLL)	+= clk-sam9x60-pll.o
 else
diff --git a/drivers/clk/at91/clk-generic.c b/drivers/clk/at91/clk-generic.c
new file mode 100644
index 000000000000..87738b7b5bff
--- /dev/null
+++ b/drivers/clk/at91/clk-generic.c
@@ -0,0 +1,202 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Generic clock support for AT91 architectures.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/clk-generated.c from Linux.
+ */
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <linux/io.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/at91_pmc.h>
+
+#include "pmc.h"
+
+#define UBOOT_DM_CLK_AT91_GCK		"at91-gck-clk"
+
+#define GENERATED_MAX_DIV	255
+
+struct clk_gck {
+	void __iomem *base;
+	const u32 *clk_mux_table;
+	const u32 *mux_table;
+	const struct clk_pcr_layout *layout;
+	struct clk_range range;
+	struct clk clk;
+	u32 num_parents;
+	u32 id;
+};
+
+#define to_clk_gck(_c) container_of(_c, struct clk_gck, clk)
+
+static int clk_gck_enable(struct clk *clk)
+{
+	struct clk_gck *gck = to_clk_gck(clk);
+
+	pmc_write(gck->base, gck->layout->offset,
+		  (gck->id & gck->layout->pid_mask));
+	pmc_update_bits(gck->base, gck->layout->offset,
+			gck->layout->cmd | AT91_PMC_PCR_GCKEN,
+			gck->layout->cmd | AT91_PMC_PCR_GCKEN);
+
+	return 0;
+}
+
+static int clk_gck_disable(struct clk *clk)
+{
+	struct clk_gck *gck = to_clk_gck(clk);
+
+	pmc_write(gck->base, gck->layout->offset,
+		  (gck->id & gck->layout->pid_mask));
+	pmc_update_bits(gck->base, gck->layout->offset,
+			gck->layout->cmd | AT91_PMC_PCR_GCKEN,
+			gck->layout->cmd);
+
+	return 0;
+}
+
+static int clk_gck_set_parent(struct clk *clk, struct clk *parent)
+{
+	struct clk_gck *gck = to_clk_gck(clk);
+	int index;
+
+	index = at91_clk_mux_val_to_index(gck->clk_mux_table, gck->num_parents,
+					  parent->id);
+	if (index < 0)
+		return index;
+
+	index = at91_clk_mux_index_to_val(gck->mux_table, gck->num_parents,
+					  index);
+	if (index < 0)
+		return index;
+
+	pmc_write(gck->base, gck->layout->offset,
+		  (gck->id & gck->layout->pid_mask));
+	pmc_update_bits(gck->base, gck->layout->offset,
+			gck->layout->gckcss_mask | gck->layout->cmd,
+			(index << (ffs(gck->layout->gckcss_mask) - 1)) |
+			gck->layout->cmd);
+
+	return 0;
+}
+
+static ulong clk_gck_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk_gck *gck = to_clk_gck(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u32 div;
+
+	if (!rate || !parent_rate)
+		return 0;
+
+	if (gck->range.max && rate > gck->range.max)
+		return 0;
+
+	div = DIV_ROUND_CLOSEST(parent_rate, rate);
+	if (div > GENERATED_MAX_DIV + 1 || !div)
+		return 0;
+
+	pmc_write(gck->base, gck->layout->offset,
+		  (gck->id & gck->layout->pid_mask));
+	pmc_update_bits(gck->base, gck->layout->offset,
+			AT91_PMC_PCR_GCKDIV_MASK | gck->layout->cmd,
+			((div - 1) << (ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1)) |
+			gck->layout->cmd);
+
+	return parent_rate / div;
+}
+
+static ulong clk_gck_get_rate(struct clk *clk)
+{
+	struct clk_gck *gck = to_clk_gck(clk);
+	ulong parent_rate = clk_get_parent_rate(clk);
+	u32 val, div;
+
+	if (!parent_rate)
+		return 0;
+
+	pmc_write(gck->base, gck->layout->offset,
+		  (gck->id & gck->layout->pid_mask));
+	pmc_read(gck->base, gck->layout->offset, &val);
+
+	div = (val & AT91_PMC_PCR_GCKDIV_MASK) >>
+		(ffs(AT91_PMC_PCR_GCKDIV_MASK) - 1);
+
+	return parent_rate / (div + 1);
+}
+
+static const struct clk_ops gck_ops = {
+	.enable = clk_gck_enable,
+	.disable = clk_gck_disable,
+	.set_parent = clk_gck_set_parent,
+	.set_rate = clk_gck_set_rate,
+	.get_rate = clk_gck_get_rate,
+};
+
+struct clk *
+at91_clk_register_generic(void __iomem *base,
+			  const struct clk_pcr_layout *layout,
+			  const char *name, const char * const *parent_names,
+			  const u32 *clk_mux_table, const u32 *mux_table,
+			  u8 num_parents, u8 id,
+			  const struct clk_range *range)
+{
+	struct clk_gck *gck;
+	struct clk *clk;
+	int ret, index;
+	u32 val;
+
+	if (!base || !layout || !name || !parent_names || !num_parents ||
+	    !clk_mux_table || !mux_table || !range)
+		return ERR_PTR(-EINVAL);
+
+	gck = kzalloc(sizeof(*gck), GFP_KERNEL);
+	if (!gck)
+		return ERR_PTR(-ENOMEM);
+
+	gck->id = id;
+	gck->base = base;
+	gck->range = *range;
+	gck->layout = layout;
+	gck->clk_mux_table = clk_mux_table;
+	gck->mux_table = mux_table;
+	gck->num_parents = num_parents;
+
+	clk = &gck->clk;
+	clk->flags = CLK_GET_RATE_NOCACHE;
+
+	pmc_write(gck->base, gck->layout->offset,
+		  (gck->id & gck->layout->pid_mask));
+	pmc_read(gck->base, gck->layout->offset, &val);
+
+	val = (val & gck->layout->gckcss_mask) >>
+		(ffs(gck->layout->gckcss_mask) - 1);
+
+	index = at91_clk_mux_val_to_index(gck->mux_table, gck->num_parents,
+					  val);
+	if (index < 0) {
+		kfree(gck);
+		return ERR_PTR(index);
+	}
+
+	ret = clk_register(clk, UBOOT_DM_CLK_AT91_GCK, name,
+			   parent_names[index]);
+	if (ret) {
+		kfree(gck);
+		clk = ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(at91_gck_clk) = {
+	.name = UBOOT_DM_CLK_AT91_GCK,
+	.id = UCLASS_CLK,
+	.ops = &gck_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 5e80f353e6c2..176855e7a0ed 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -127,6 +127,12 @@ at91_clk_register_sam9x5_peripheral(void __iomem *base,
 			const struct clk_pcr_layout *layout,
 			const char *name, const char *parent_name,
 			u32 id, const struct clk_range *range);
+struct clk *
+at91_clk_register_generic(void __iomem *base,
+			const struct clk_pcr_layout *layout, const char *name,
+			const char * const *parent_names,
+			const u32 *clk_mux_table, const u32 *mux_table,
+			u8 num_parents, u8 id, const struct clk_range *range);
 
 int at91_clk_mux_val_to_index(const u32 *table, u32 num_parents, u32 val);
 int at91_clk_mux_index_to_val(const u32 *table, u32 num_parents, u32 index);
-- 
2.7.4

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

* [PATCH 21/22] clk: at91: pmc: add generic clock ops
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (19 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 20/22] clk: at91: clk-generic: " Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  2020-07-29 14:51 ` [PATCH 22/22] clk: at91: sama7g5: add clock support Claudiu Beznea
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add generic clock ops to be used by every AT91 PMC driver
built on top of CCF.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/pmc.c | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/pmc.h |  2 ++
 2 files changed, 73 insertions(+)

diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
index 29c6452497b7..660e23192149 100644
--- a/drivers/clk/at91/pmc.c
+++ b/drivers/clk/at91/pmc.c
@@ -5,8 +5,79 @@
  */
 
 #include <asm/io.h>
+#include <clk-uclass.h>
 #include <common.h>
 
+#include "pmc.h"
+
+static int at91_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
+{
+	if (args->args_count != 2) {
+		debug("AT91: clk: Invalid args_count: %d\n", args->args_count);
+		return -EINVAL;
+	}
+
+	clk->id = AT91_TO_CLK_ID(args->args[0], args->args[1]);
+
+	return 0;
+}
+
+static ulong at91_clk_get_rate(struct clk *clk)
+{
+	struct clk *c;
+	int ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	return clk_get_rate(c);
+}
+
+static ulong at91_clk_set_rate(struct clk *clk, ulong rate)
+{
+	struct clk *c;
+	int ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	return clk_set_rate(c, rate);
+}
+
+static int at91_clk_enable(struct clk *clk)
+{
+	struct clk *c;
+	int ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	return clk_enable(c);
+}
+
+static int at91_clk_disable(struct clk *clk)
+{
+	struct clk *c;
+	int ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	return clk_disable(c);
+}
+
+const struct clk_ops at91_clk_ops = {
+	.of_xlate	= at91_clk_of_xlate,
+	.set_rate	= at91_clk_set_rate,
+	.get_rate	= at91_clk_get_rate,
+	.enable		= at91_clk_enable,
+	.disable	= at91_clk_disable,
+};
+
 /**
  * pmc_read() - read content at address base + off into val
  *
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
index 176855e7a0ed..a6a714fd220b 100644
--- a/drivers/clk/at91/pmc.h
+++ b/drivers/clk/at91/pmc.h
@@ -75,6 +75,8 @@ extern const struct clk_programmable_layout at91rm9200_programmable_layout;
 extern const struct clk_programmable_layout at91sam9g45_programmable_layout;
 extern const struct clk_programmable_layout at91sam9x5_programmable_layout;
 
+extern const struct clk_ops at91_clk_ops;
+
 struct clk *at91_clk_main_rc(void __iomem *reg, const char *name,
 			const char *parent_name);
 struct clk *at91_clk_main_osc(void __iomem *reg, const char *name,
-- 
2.7.4

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

* [PATCH 22/22] clk: at91: sama7g5: add clock support
  2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
                   ` (20 preceding siblings ...)
  2020-07-29 14:51 ` [PATCH 21/22] clk: at91: pmc: add generic clock ops Claudiu Beznea
@ 2020-07-29 14:51 ` Claudiu Beznea
  21 siblings, 0 replies; 38+ messages in thread
From: Claudiu Beznea @ 2020-07-29 14:51 UTC (permalink / raw)
  To: u-boot

Add clock support for SAMA7G5.

Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
---
 drivers/clk/at91/Makefile  |    1 +
 drivers/clk/at91/sama7g5.c | 1401 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1402 insertions(+)
 create mode 100644 drivers/clk/at91/sama7g5.c

diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
index 2cd840af3859..2453c38af1aa 100644
--- a/drivers/clk/at91/Makefile
+++ b/drivers/clk/at91/Makefile
@@ -9,6 +9,7 @@ obj-y += clk-peripheral.o
 obj-$(CONFIG_AT91_GENERIC_CLK)	+= clk-generic.o
 obj-$(CONFIG_AT91_UTMI)		+= clk-utmi.o
 obj-$(CONFIG_AT91_SAM9X60_PLL)	+= clk-sam9x60-pll.o
+obj-$(CONFIG_SAMA7G5)		+= sama7g5.o
 else
 obj-y += compat.o
 endif
diff --git a/drivers/clk/at91/sama7g5.c b/drivers/clk/at91/sama7g5.c
new file mode 100644
index 000000000000..b96937673bed
--- /dev/null
+++ b/drivers/clk/at91/sama7g5.c
@@ -0,0 +1,1401 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * SAMA7G5 PMC clock support.
+ *
+ * Copyright (C) 2020 Microchip Technology Inc. and its subsidiaries
+ *
+ * Author: Claudiu Beznea <claudiu.beznea@microchip.com>
+ *
+ * Based on drivers/clk/at91/sama7g5.c from Linux.
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <dt-bindings/clk/at91.h>
+#include <linux/clk-provider.h>
+
+#include "pmc.h"
+
+/**
+ * Clock identifiers to be used in conjunction with macros like
+ * AT91_TO_CLK_ID()
+ *
+ * @ID_MD_SLCK:			TD slow clock identifier
+ * @ID_TD_SLCK:			MD slow clock identifier
+ * @ID_MAIN_XTAL:		Main Xtal clock identifier
+ * @ID_MAIN_RC:			Main RC clock identifier
+ * @ID_MAIN_RC_OSC:		Main RC Oscillator clock identifier
+ * @ID_MAIN_OSC:		Main Oscillator clock identifier
+ * @ID_MAINCK:			MAINCK clock identifier
+ * @ID_PLL_CPU_FRAC:		CPU PLL fractional clock identifier
+ * @ID_PLL_CPU_DIV:		CPU PLL divider clock identifier
+ * @ID_PLL_SYS_FRAC:		SYS PLL fractional clock identifier
+ * @ID_PLL_SYS_DIV:		SYS PLL divider clock identifier
+ * @ID_PLL_DDR_FRAC:		DDR PLL fractional clock identifier
+ * @ID_PLL_DDR_DIV:		DDR PLL divider clock identifier
+ * @ID_PLL_IMG_FRAC:		IMC PLL fractional clock identifier
+ * @ID_PLL_IMG_DIV:		IMG PLL divider clock identifier
+ * @ID_PLL_BAUD_FRAC:		Baud PLL fractional clock identifier
+ * @ID_PLL_BAUD_DIV:		Baud PLL divider clock identifier
+ * @ID_PLL_AUDIO_FRAC:		Audio PLL fractional clock identifier
+ * @ID_PLL_AUDIO_DIVPMC:	Audio PLL PMC divider clock identifier
+ * @ID_PLL_AUDIO_DIVIO:		Audio PLL IO divider clock identifier
+ * @ID_PLL_ETH_FRAC:		Ethernet PLL fractional clock identifier
+ * @ID_PLL_ETH_DIV:		Ethernet PLL divider clock identifier
+
+ * @ID_MCK0:			MCK0 clock identifier
+ * @ID_MCK1:			MCK1 clock identifier
+ * @ID_MCK2:			MCK2 clock identifier
+ * @ID_MCK3:			MCK3 clock identifier
+ * @ID_MCK4:			MCK4 clock identifier
+
+ * @ID_UTMI:			UTMI clock identifier
+
+ * @ID_PROG0:			Programmable 0 clock identifier
+ * @ID_PROG1:			Programmable 1 clock identifier
+ * @ID_PROG2:			Programmable 2 clock identifier
+ * @ID_PROG3:			Programmable 3 clock identifier
+ * @ID_PROG4:			Programmable 4 clock identifier
+ * @ID_PROG5:			Programmable 5 clock identifier
+ * @ID_PROG6:			Programmable 6 clock identifier
+ * @ID_PROG7:			Programmable 7 clock identifier
+
+ * @ID_PCK0:			System clock 0 clock identifier
+ * @ID_PCK1:			System clock 1 clock identifier
+ * @ID_PCK2:			System clock 2 clock identifier
+ * @ID_PCK3:			System clock 3 clock identifier
+ * @ID_PCK4:			System clock 4 clock identifier
+ * @ID_PCK5:			System clock 5 clock identifier
+ * @ID_PCK6:			System clock 6 clock identifier
+ * @ID_PCK7:			System clock 7 clock identifier
+ */
+enum pmc_clk_ids {
+	ID_MD_SLCK		= 0,
+	ID_TD_SLCK		= 1,
+	ID_MAIN_XTAL		= 2,
+	ID_MAIN_RC		= 3,
+	ID_MAIN_RC_OSC		= 4,
+	ID_MAIN_OSC		= 5,
+	ID_MAINCK		= 6,
+
+	ID_PLL_CPU_FRAC		= 7,
+	ID_PLL_CPU_DIV		= 8,
+	ID_PLL_SYS_FRAC		= 9,
+	ID_PLL_SYS_DIV		= 10,
+	ID_PLL_DDR_FRAC		= 11,
+	ID_PLL_DDR_DIV		= 12,
+	ID_PLL_IMG_FRAC		= 13,
+	ID_PLL_IMG_DIV		= 14,
+	ID_PLL_BAUD_FRAC	= 15,
+	ID_PLL_BAUD_DIV		= 16,
+	ID_PLL_AUDIO_FRAC	= 17,
+	ID_PLL_AUDIO_DIVPMC	= 18,
+	ID_PLL_AUDIO_DIVIO	= 19,
+	ID_PLL_ETH_FRAC		= 20,
+	ID_PLL_ETH_DIV		= 21,
+
+	ID_MCK0			= 22,
+	ID_MCK1			= 23,
+	ID_MCK2			= 24,
+	ID_MCK3			= 25,
+	ID_MCK4			= 26,
+
+	ID_UTMI			= 27,
+
+	ID_PROG0		= 28,
+	ID_PROG1		= 29,
+	ID_PROG2		= 30,
+	ID_PROG3		= 31,
+	ID_PROG4		= 32,
+	ID_PROG5		= 33,
+	ID_PROG6		= 34,
+	ID_PROG7		= 35,
+
+	ID_PCK0			= 36,
+	ID_PCK1			= 37,
+	ID_PCK2			= 38,
+	ID_PCK3			= 39,
+	ID_PCK4			= 40,
+	ID_PCK5			= 41,
+	ID_PCK6			= 42,
+	ID_PCK7			= 43,
+
+	ID_MAX,
+};
+
+/**
+ * PLL type identifiers
+ * @PLL_TYPE_FRAC:	fractional PLL identifier
+ * @PLL_TYPE_DIV:	divider PLL identifier
+ */
+enum pll_type {
+	PLL_TYPE_FRAC,
+	PLL_TYPE_DIV,
+};
+
+/* Clock names used as parents for multiple clocks. */
+static const char *clk_names[] = {
+	[ID_MAIN_RC_OSC]	= "main_rc_osc",
+	[ID_MAIN_OSC]		= "main_osc",
+	[ID_MAINCK]		= "mainck",
+	[ID_PLL_CPU_DIV]	= "cpupll_divpmcck",
+	[ID_PLL_SYS_DIV]	= "syspll_divpmcck",
+	[ID_PLL_DDR_DIV]	= "ddrpll_divpmcck",
+	[ID_PLL_IMG_DIV]	= "imgpll_divpmcck",
+	[ID_PLL_BAUD_DIV]	= "baudpll_divpmcck",
+	[ID_PLL_AUDIO_DIVPMC]	= "audiopll_divpmcck",
+	[ID_PLL_AUDIO_DIVIO]	= "audiopll_diviock",
+	[ID_PLL_ETH_DIV]	= "ethpll_divpmcck",
+	[ID_MCK0]		= "mck0",
+};
+
+/* Fractional PLL output range. */
+static const struct clk_range pll_outputs[] = {
+	{ .min = 2343750, .max = 1200000000 },
+};
+
+/* PLL characteristics. */
+static const struct clk_pll_characteristics pll_characteristics = {
+	.input = { .min = 12000000, .max = 50000000 },
+	.num_output = ARRAY_SIZE(pll_outputs),
+	.output = pll_outputs,
+};
+
+/* Layout for fractional PLLs. */
+static const struct clk_pll_layout pll_layout_frac = {
+	.mul_mask	= GENMASK(31, 24),
+	.frac_mask	= GENMASK(21, 0),
+	.mul_shift	= 24,
+	.frac_shift	= 0,
+};
+
+/* Layout for DIVPMC dividers. */
+static const struct clk_pll_layout pll_layout_divpmc = {
+	.div_mask	= GENMASK(7, 0),
+	.endiv_mask	= BIT(29),
+	.div_shift	= 0,
+	.endiv_shift	= 29,
+};
+
+/* Layout for DIVIO dividers. */
+static const struct clk_pll_layout pll_layout_divio = {
+	.div_mask	= GENMASK(19, 12),
+	.endiv_mask	= BIT(30),
+	.div_shift	= 12,
+	.endiv_shift	= 30,
+};
+
+/* MCK0 characteristics. */
+static const struct clk_master_characteristics mck0_characteristics = {
+	.output = { .min = 140000000, .max = 200000000 },
+	.divisors = { 1, 2, 4, 3 },
+	.have_div3_pres = 1,
+};
+
+/* MCK0 layout. */
+static const struct clk_master_layout mck0_layout = {
+	.mask = 0x373,
+	.pres_shift = 4,
+	.offset = 0x28,
+};
+
+/* Programmable clock layout. */
+static const struct clk_programmable_layout programmable_layout = {
+	.pres_mask = 0xff,
+	.pres_shift = 8,
+	.css_mask = 0x1f,
+	.have_slck_mck = 0,
+	.is_pres_direct = 1,
+};
+
+/* Peripheral clock layout. */
+static const struct clk_pcr_layout sama7g5_pcr_layout = {
+	.offset = 0x88,
+	.cmd = BIT(31),
+	.gckcss_mask = GENMASK(12, 8),
+	.pid_mask = GENMASK(6, 0),
+	.div_mask = GENMASK(15, 14),
+};
+
+/**
+ * PLL clocks description
+ * @n:		clock name
+ * @p:		clock parent
+ * @l:		clock layout
+ * @t:		clock type
+ * @c:		true if clock is critical and cannot be disabled
+ * @id:		clock id corresponding to PLL driver
+ * @cid:	clock id corresponding to clock subsystem
+ */
+static const struct {
+	const char *n;
+	const char *p;
+	const struct clk_pll_layout *l;
+	u8 t;
+	u8 c;
+	u8 id;
+	u8 cid;
+} sama7g5_plls[] = {
+	{
+		.n = "cpupll_fracck",
+		.p = "mainck",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.c = 1,
+		.id = 0,
+		.cid = ID_PLL_CPU_FRAC,
+	},
+
+	{
+		.n = "cpupll_divpmcck",
+		.p = "cpupll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.c = 1,
+		.id = 0,
+		.cid = ID_PLL_CPU_DIV,
+	},
+
+	{
+		.n = "syspll_fracck",
+		.p = "mainck",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.c = 1,
+		.id = 1,
+		.cid = ID_PLL_SYS_FRAC,
+	},
+
+	{
+		.n = "syspll_divpmcck",
+		.p = "syspll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.c = 1,
+		.id = 1,
+		.cid = ID_PLL_SYS_DIV,
+	},
+
+	{
+		.n = "ddrpll_fracck",
+		.p = "mainck",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.c = 1,
+		.id = 2,
+		.cid = ID_PLL_DDR_FRAC,
+	},
+
+	{
+		.n = "ddrpll_divpmcck",
+		.p = "ddrpll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.c = 1,
+		.id = 2,
+		.cid = ID_PLL_DDR_DIV,
+	},
+
+	{
+		.n = "imgpll_fracck",
+		.p = "mainck",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.id = 3,
+		.cid = ID_PLL_IMG_FRAC,
+	},
+
+	{
+		.n = "imgpll_divpmcck",
+		.p = "imgpll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.id = 3,
+		.cid = ID_PLL_IMG_DIV
+	},
+
+	{
+		.n = "baudpll_fracck",
+		.p = "mainck",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.id = 4,
+		.cid = ID_PLL_BAUD_FRAC,
+	},
+
+	{
+		.n = "baudpll_divpmcck",
+		.p = "baudpll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.id = 4,
+		.cid = ID_PLL_BAUD_DIV,
+	},
+
+	{
+		.n = "audiopll_fracck",
+		.p = "main_osc",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.id = 5,
+		.cid = ID_PLL_AUDIO_FRAC,
+	},
+
+	{
+		.n = "audiopll_divpmcck",
+		.p = "audiopll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.id = 5,
+		.cid = ID_PLL_AUDIO_DIVPMC,
+	},
+
+	{
+		.n = "audiopll_diviock",
+		.p = "audiopll_fracck",
+		.l = &pll_layout_divio,
+		.t = PLL_TYPE_DIV,
+		.id = 5,
+		.cid = ID_PLL_AUDIO_DIVIO,
+	},
+
+	{
+		.n = "ethpll_fracck",
+		.p = "main_osc",
+		.l = &pll_layout_frac,
+		.t = PLL_TYPE_FRAC,
+		.id = 6,
+		.cid = ID_PLL_ETH_FRAC,
+	},
+
+	{
+		.n = "ethpll_divpmcck",
+		.p = "ethpll_fracck",
+		.l = &pll_layout_divpmc,
+		.t = PLL_TYPE_DIV,
+		.id = 6,
+		.cid = ID_PLL_ETH_DIV,
+	},
+};
+
+/**
+ * Master clock (MCK[1..4]) description
+ * @n:			clock name
+ * @ep:			extra parents names array
+ * @ep_chg_chg_id:	index in parents array that specifies the changeable
+ *			parent
+ * @ep_count:		extra parents count
+ * @ep_mux_table:	mux table for extra parents
+ * @ep_clk_mux_table:	mux table to deal with subsystem clock ids
+ * @id:			clock id corresponding to MCK driver
+ * @cid:		clock id corresponding to clock subsystem
+ * @c:			true if clock is critical and cannot be disabled
+ */
+static const struct {
+	const char *n;
+	const char *ep[4];
+	u8 ep_count;
+	u8 ep_mux_table[4];
+	u8 ep_clk_mux_table[4];
+	u8 id;
+	u8 cid;
+	u8 c;
+} sama7g5_mckx[] = {
+	{
+		.n = "mck1",
+		.id = 1,
+		.cid = ID_MCK1,
+		.ep = { "syspll_divpmcck", },
+		.ep_mux_table = { 5, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, },
+		.ep_count = 1,
+		.c = 1,
+	},
+
+	{
+		.n = "mck2",
+		.id = 2,
+		.cid = ID_MCK2,
+		.ep = { "ddrpll_divpmcck", },
+		.ep_mux_table = { 6, },
+		.ep_clk_mux_table = { ID_PLL_DDR_DIV, },
+		.ep_count = 1,
+		.c = 1,
+	},
+
+	{
+		.n = "mck3",
+		.id = 3,
+		.cid = ID_MCK3,
+		.ep = { "syspll_divpmcck", "ddrpll_divpmcck", "imgpll_divpmcck", },
+		.ep_mux_table = { 5, 6, 7, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_DDR_DIV, ID_PLL_IMG_DIV, },
+		.ep_count = 3,
+	},
+
+	{
+		.n = "mck4",
+		.id = 4,
+		.cid = ID_MCK4,
+		.ep = { "syspll_divpmcck", },
+		.ep_mux_table = { 5, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, },
+		.ep_count = 1,
+		.c = 1,
+	},
+};
+
+/**
+ * Programmable clock description
+ * @n:			clock name
+ * @cid:		clock id corresponding to clock subsystem
+ */
+static const struct {
+	const char *n;
+	u8 cid;
+} sama7g5_prog[] = {
+	{ .n = "prog0", .cid = ID_PROG0, },
+	{ .n = "prog1", .cid = ID_PROG1, },
+	{ .n = "prog2", .cid = ID_PROG2, },
+	{ .n = "prog3", .cid = ID_PROG3, },
+	{ .n = "prog4", .cid = ID_PROG4, },
+	{ .n = "prog5", .cid = ID_PROG5, },
+	{ .n = "prog6", .cid = ID_PROG6, },
+	{ .n = "prog7", .cid = ID_PROG7, },
+};
+
+/* Mux table for programmable clocks. */
+static u32 sama7g5_prog_mux_table[] = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, };
+
+/**
+ * System clock description
+ * @n:			clock name
+ * @p:			parent clock name
+ * @id:			clock id corresponding to system clock driver
+ * @cid:		clock id corresponding to clock subsystem
+ */
+static const struct {
+	const char *n;
+	const char *p;
+	u8 id;
+	u8 cid;
+} sama7g5_systemck[] = {
+	{ .n = "pck0", .p = "prog0", .id = 8, .cid = ID_PCK0, },
+	{ .n = "pck1", .p = "prog1", .id = 9, .cid = ID_PCK1, },
+	{ .n = "pck2", .p = "prog2", .id = 10, .cid = ID_PCK2, },
+	{ .n = "pck3", .p = "prog3", .id = 11, .cid = ID_PCK3, },
+	{ .n = "pck4", .p = "prog4", .id = 12, .cid = ID_PCK4, },
+	{ .n = "pck5", .p = "prog5", .id = 13, .cid = ID_PCK5, },
+	{ .n = "pck6", .p = "prog6", .id = 14, .cid = ID_PCK6, },
+	{ .n = "pck7", .p = "prog7", .id = 15, .cid = ID_PCK7, },
+};
+
+/**
+ * Peripheral clock description
+ * @n:		clock name
+ * @p:		clock parent name
+ * @r:		clock range values
+ * @id:		clock id
+ */
+static const struct {
+	const char *n;
+	const char *p;
+	struct clk_range r;
+	u8 id;
+} sama7g5_periphck[] = {
+	{ .n = "pioA_clk",	.p = "mck0", .id = 11, },
+	{ .n = "sfr_clk",	.p = "mck1", .id = 19, },
+	{ .n = "hsmc_clk",	.p = "mck1", .id = 21, },
+	{ .n = "xdmac0_clk",	.p = "mck1", .id = 22, },
+	{ .n = "xdmac1_clk",	.p = "mck1", .id = 23, },
+	{ .n = "xdmac2_clk",	.p = "mck1", .id = 24, },
+	{ .n = "acc_clk",	.p = "mck1", .id = 25, },
+	{ .n = "aes_clk",	.p = "mck1", .id = 27, },
+	{ .n = "tzaesbasc_clk",	.p = "mck1", .id = 28, },
+	{ .n = "asrc_clk",	.p = "mck1", .id = 30, .r = { .max = 200000000, }, },
+	{ .n = "cpkcc_clk",	.p = "mck0", .id = 32, },
+	{ .n = "csi_clk",	.p = "mck3", .id = 33, .r = { .max = 266000000, }, },
+	{ .n = "csi2dc_clk",	.p = "mck3", .id = 34, .r = { .max = 266000000, }, },
+	{ .n = "eic_clk",	.p = "mck1", .id = 37, },
+	{ .n = "flex0_clk",	.p = "mck1", .id = 38, },
+	{ .n = "flex1_clk",	.p = "mck1", .id = 39, },
+	{ .n = "flex2_clk",	.p = "mck1", .id = 40, },
+	{ .n = "flex3_clk",	.p = "mck1", .id = 41, },
+	{ .n = "flex4_clk",	.p = "mck1", .id = 42, },
+	{ .n = "flex5_clk",	.p = "mck1", .id = 43, },
+	{ .n = "flex6_clk",	.p = "mck1", .id = 44, },
+	{ .n = "flex7_clk",	.p = "mck1", .id = 45, },
+	{ .n = "flex8_clk",	.p = "mck1", .id = 46, },
+	{ .n = "flex9_clk",	.p = "mck1", .id = 47, },
+	{ .n = "flex10_clk",	.p = "mck1", .id = 48, },
+	{ .n = "flex11_clk",	.p = "mck1", .id = 49, },
+	{ .n = "gmac0_clk",	.p = "mck1", .id = 51, },
+	{ .n = "gmac1_clk",	.p = "mck1", .id = 52, },
+	{ .n = "gmac0_tsu_clk",	.p = "mck1", .id = 53, },
+	{ .n = "gmac1_tsu_clk",	.p = "mck1", .id = 54, },
+	{ .n = "icm_clk",	.p = "mck1", .id = 55, },
+	{ .n = "isc_clk",	.p = "mck3", .id = 56, .r = { .max = 266000000, }, },
+	{ .n = "i2smcc0_clk",	.p = "mck1", .id = 57, .r = { .max = 200000000, }, },
+	{ .n = "i2smcc1_clk",	.p = "mck1", .id = 58, .r = { .max = 200000000, }, },
+	{ .n = "matrix_clk",	.p = "mck1", .id = 60, },
+	{ .n = "mcan0_clk",	.p = "mck1", .id = 61, .r = { .max = 200000000, }, },
+	{ .n = "mcan1_clk",	.p = "mck1", .id = 62, .r = { .max = 200000000, }, },
+	{ .n = "mcan2_clk",	.p = "mck1", .id = 63, .r = { .max = 200000000, }, },
+	{ .n = "mcan3_clk",	.p = "mck1", .id = 64, .r = { .max = 200000000, }, },
+	{ .n = "mcan4_clk",	.p = "mck1", .id = 65, .r = { .max = 200000000, }, },
+	{ .n = "mcan5_clk",	.p = "mck1", .id = 66, .r = { .max = 200000000, }, },
+	{ .n = "pdmc0_clk",	.p = "mck1", .id = 68, .r = { .max = 200000000, }, },
+	{ .n = "pdmc1_clk",	.p = "mck1", .id = 69, .r = { .max = 200000000, }, },
+	{ .n = "pit64b0_clk",	.p = "mck1", .id = 70, },
+	{ .n = "pit64b1_clk",	.p = "mck1", .id = 71, },
+	{ .n = "pit64b2_clk",	.p = "mck1", .id = 72, },
+	{ .n = "pit64b3_clk",	.p = "mck1", .id = 73, },
+	{ .n = "pit64b4_clk",	.p = "mck1", .id = 74, },
+	{ .n = "pit64b5_clk",	.p = "mck1", .id = 75, },
+	{ .n = "pwm_clk",	.p = "mck1", .id = 77, },
+	{ .n = "qspi0_clk",	.p = "mck1", .id = 78, },
+	{ .n = "qspi1_clk",	.p = "mck1", .id = 79, },
+	{ .n = "sdmmc0_clk",	.p = "mck1", .id = 80, },
+	{ .n = "sdmmc1_clk",	.p = "mck1", .id = 81, },
+	{ .n = "sdmmc2_clk",	.p = "mck1", .id = 82, },
+	{ .n = "sha_clk",	.p = "mck1", .id = 83, },
+	{ .n = "spdifrx_clk",	.p = "mck1", .id = 84, .r = { .max = 200000000, }, },
+	{ .n = "spdiftx_clk",	.p = "mck1", .id = 85, .r = { .max = 200000000, }, },
+	{ .n = "ssc0_clk",	.p = "mck1", .id = 86, .r = { .max = 200000000, }, },
+	{ .n = "ssc1_clk",	.p = "mck1", .id = 87, .r = { .max = 200000000, }, },
+	{ .n = "tcb0_ch0_clk",	.p = "mck1", .id = 88, .r = { .max = 200000000, }, },
+	{ .n = "tcb0_ch1_clk",	.p = "mck1", .id = 89, .r = { .max = 200000000, }, },
+	{ .n = "tcb0_ch2_clk",	.p = "mck1", .id = 90, .r = { .max = 200000000, }, },
+	{ .n = "tcb1_ch0_clk",	.p = "mck1", .id = 91, .r = { .max = 200000000, }, },
+	{ .n = "tcb1_ch1_clk",	.p = "mck1", .id = 92, .r = { .max = 200000000, }, },
+	{ .n = "tcb1_ch2_clk",	.p = "mck1", .id = 93, .r = { .max = 200000000, }, },
+	{ .n = "tcpca_clk",	.p = "mck1", .id = 94, },
+	{ .n = "tcpcb_clk",	.p = "mck1", .id = 95, },
+	{ .n = "tdes_clk",	.p = "mck1", .id = 96, },
+	{ .n = "trng_clk",	.p = "mck1", .id = 97, },
+	{ .n = "udphsa_clk",	.p = "mck1", .id = 104, },
+	{ .n = "udphsb_clk",	.p = "mck1", .id = 105, },
+	{ .n = "uhphs_clk",	.p = "mck1", .id = 106, },
+};
+
+/**
+ * Generic clock description
+ * @n:			clock name
+ * @ep:			extra parents names
+ * @ep_mux_table:	extra parents mux table
+ * @ep_clk_mux_table:	extra parents clock mux table (for CCF)
+ * @r:			clock output range
+ * @ep_count:		extra parents count
+ * @id:			clock id
+ */
+static const struct {
+	const char *n;
+	const char *ep[8];
+	const char ep_mux_table[8];
+	const char ep_clk_mux_table[8];
+	struct clk_range r;
+	u8 ep_count;
+	u8 id;
+} sama7g5_gck[] = {
+	{
+		.n  = "adc_gclk",
+		.id = 26,
+		.r = { .max = 100000000, },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "audiopll_divpmcck", },
+		.ep_mux_table = { 5, 7, 9, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_AUDIO_DIVPMC, },
+		.ep_count = 3,
+	},
+
+	{
+		.n  = "asrc_gclk",
+		.id = 30,
+		.r = { .max = 200000000 },
+		.ep = { "audiopll_divpmcck", },
+		.ep_mux_table = { 9, },
+		.ep_clk_mux_table = { ID_PLL_AUDIO_DIVPMC, },
+		.ep_count = 1,
+	},
+
+	{
+		.n  = "csi_gclk",
+		.id = 33,
+		.r = { .max = 27000000  },
+		.ep = { "ddrpll_divpmcck", "imgpll_divpmcck", },
+		.ep_clk_mux_table = { ID_PLL_DDR_DIV, ID_PLL_IMG_DIV, },
+		.ep_mux_table = { 6, 7, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex0_gclk",
+		.id = 38,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex1_gclk",
+		.id = 39,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex2_gclk",
+		.id = 40,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex3_gclk",
+		.id = 41,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex4_gclk",
+		.id = 42,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex5_gclk",
+		.id = 43,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex6_gclk",
+		.id = 44,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex7_gclk",
+		.id = 45,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex8_gclk",
+		.id = 46,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex9_gclk",
+		.id = 47,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex10_gclk",
+		.id = 48,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "flex11_gclk",
+		.id = 49,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "gmac0_gclk",
+		.id = 51,
+		.r = { .max = 125000000 },
+		.ep = { "ethpll_divpmcck", },
+		.ep_clk_mux_table = { ID_PLL_ETH_DIV, },
+		.ep_mux_table = { 10, },
+		.ep_count = 1,
+	},
+
+	{
+		.n  = "gmac1_gclk",
+		.id = 52,
+		.r = { .max = 50000000  },
+		.ep = { "ethpll_divpmcck", },
+		.ep_mux_table = { 10, },
+		.ep_clk_mux_table = { ID_PLL_ETH_DIV, },
+		.ep_count = 1,
+	},
+
+	{
+		.n  = "gmac0_tsu_gclk",
+		.id = 53,
+		.r = { .max = 300000000 },
+		.ep = { "audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_AUDIO_DIVPMC, ID_PLL_ETH_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "gmac1_tsu_gclk",
+		.id = 54,
+		.r = { .max = 300000000 },
+		.ep = { "audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_AUDIO_DIVPMC, ID_PLL_ETH_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "i2smcc0_gclk",
+		.id = 57,
+		.r = { .max = 100000000 },
+		.ep = { "syspll_divpmcck", "audiopll_divpmcck", },
+		.ep_mux_table = { 5, 9, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_AUDIO_DIVPMC, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "i2smcc1_gclk",
+		.id = 58,
+		.r = { .max = 100000000 },
+		.ep = { "syspll_divpmcck", "audiopll_divpmcck", },
+		.ep_mux_table = { 5, 9, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_AUDIO_DIVPMC, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "mcan0_gclk",
+		.id = 61,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "mcan1_gclk",
+		.id = 62,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "mcan2_gclk",
+		.id = 63,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "mcan3_gclk",
+		.id = 64,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "mcan4_gclk",
+		.id = 65,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "mcan5_gclk",
+		.id = 66,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "pdmc0_gclk",
+		.id = 68,
+		.r = { .max = 50000000  },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "pdmc1_gclk",
+		.id = 69,
+		.r = { .max = 50000000, },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "pit64b0_gclk",
+		.id = 70,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "pit64b1_gclk",
+		.id = 71,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "pit64b2_gclk",
+		.id = 72,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "pit64b3_gclk",
+		.id = 73,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "pit64b4_gclk",
+		.id = 74,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "pit64b5_gclk",
+		.id = 75,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "qspi0_gclk",
+		.id = 78,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "qspi1_gclk",
+		.id = 79,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "sdmmc0_gclk",
+		.id = 80,
+		.r = { .max = 208000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "sdmmc1_gclk",
+		.id = 81,
+		.r = { .max = 208000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "sdmmc2_gclk",
+		.id = 82,
+		.r = { .max = 208000000 },
+		.ep = { "syspll_divpmcck", "baudpll_divpmcck", },
+		.ep_mux_table = { 5, 8, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_BAUD_DIV, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "spdifrx_gclk",
+		.id = 84,
+		.r = { .max = 150000000 },
+		.ep = { "syspll_divpmcck", "audiopll_divpmcck", },
+		.ep_mux_table = { 5, 9, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_AUDIO_DIVPMC, },
+		.ep_count = 2,
+	},
+
+	{
+		.n = "spdiftx_gclk",
+		.id = 85,
+		.r = { .max = 25000000  },
+		.ep = { "syspll_divpmcck", "audiopll_divpmcck", },
+		.ep_mux_table = { 5, 9, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_AUDIO_DIVPMC, },
+		.ep_count = 2,
+	},
+
+	{
+		.n  = "tcb0_ch0_gclk",
+		.id = 88,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+
+	{
+		.n  = "tcb1_ch0_gclk",
+		.id = 91,
+		.r = { .max = 200000000 },
+		.ep = { "syspll_divpmcck", "imgpll_divpmcck", "baudpll_divpmcck",
+			"audiopll_divpmcck", "ethpll_divpmcck", },
+		.ep_mux_table = { 5, 7, 8, 9, 10, },
+		.ep_clk_mux_table = { ID_PLL_SYS_DIV, ID_PLL_IMG_DIV,
+				      ID_PLL_BAUD_DIV, ID_PLL_AUDIO_DIVPMC,
+				      ID_PLL_ETH_DIV, },
+		.ep_count = 5,
+	},
+};
+
+/**
+ * Clock setup description
+ * @cid:	clock id corresponding to clock subsystem
+ * @pid:	parent clock id corresponding to clock subsystem
+ * @rate:	clock rate
+ * @prate:	parent rate
+ */
+static const struct pmc_clk_setup {
+	unsigned int cid;
+	unsigned int pid;
+	unsigned long rate;
+	unsigned long prate;
+} sama7g5_clk_setup[] = {
+	{
+		.cid = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_ETH_FRAC),
+		.rate = 625000000,
+	},
+
+	{
+		.cid = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_ETH_DIV),
+		.rate = 625000000,
+	},
+};
+
+#define SAMA7G5_MAX_MUX_ALLOCS		(64)
+
+#define prepare_mux_table(_allocs, _index, _dst, _src, _num, _label)	\
+	do {								\
+		int _i;							\
+		if ((_index) >= SAMA7G5_MAX_MUX_ALLOCS) {		\
+			debug("%s(): AT91: MUX: insufficient space\n",	\
+			      __func__);				\
+			goto _label;					\
+		}							\
+		(_dst) = kzalloc(sizeof(*(_dst)) * (_num), GFP_KERNEL);	\
+		if (!(_dst))						\
+			goto _label;					\
+		(_allocs)[(_index)++] = (_dst);				\
+		for (_i = 0; _i < (_num); _i++)				\
+			(_dst)[_i] = (_src)[_i];			\
+	} while (0)
+
+static int sama7g5_clk_probe(struct udevice *dev)
+{
+	void __iomem *base = (void *)devfdt_get_addr(dev);
+	unsigned int *clkmuxallocs[SAMA7G5_MAX_MUX_ALLOCS];
+	unsigned int *muxallocs[SAMA7G5_MAX_MUX_ALLOCS];
+	const char *p[10];
+	unsigned int cm[10], m[10], *tmpclkmux, *tmpmux;
+	struct clk clk, *c, *parent;
+	bool main_osc_bypass;
+	int ret, muxallocindex = 0, clkmuxallocindex = 0, i, j;
+
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	memset(muxallocs,    0, ARRAY_SIZE(muxallocs));
+	memset(clkmuxallocs, 0, ARRAY_SIZE(clkmuxallocs));
+
+	ret = clk_get_by_index(dev, 0, &clk);
+	if (ret)
+		return ret;
+	ret = clk_get_by_id(clk.id, &c);
+	if (ret)
+		return ret;
+	clk_names[ID_TD_SLCK] = kmemdup(clk_hw_get_name(c),
+		strlen(clk_hw_get_name(c)) + 1, GFP_KERNEL);
+	if (!clk_names[ID_TD_SLCK])
+		return -ENOMEM;
+
+	ret = clk_get_by_index(dev, 1, &clk);
+	if (ret)
+		return ret;
+	ret = clk_get_by_id(clk.id, &c);
+	if (ret)
+		return ret;
+	clk_names[ID_MD_SLCK] = kmemdup(clk_hw_get_name(c),
+		strlen(clk_hw_get_name(c)) + 1, GFP_KERNEL);
+	if (!clk_names[ID_MD_SLCK])
+		return -ENOMEM;
+
+	ret = clk_get_by_index(dev, 2, &clk);
+	if (ret)
+		return ret;
+	clk_names[ID_MAIN_XTAL] = kmemdup(clk_hw_get_name(&clk),
+		strlen(clk_hw_get_name(&clk)) + 1, GFP_KERNEL);
+	if (!clk_names[ID_MAIN_XTAL])
+		return -ENOMEM;
+
+	ret = clk_get_by_index(dev, 3, &clk);
+	if (ret)
+		goto fail;
+	clk_names[ID_MAIN_RC] = kmemdup(clk_hw_get_name(&clk),
+		strlen(clk_hw_get_name(&clk)) + 1, GFP_KERNEL);
+	if (ret)
+		goto fail;
+
+	main_osc_bypass = dev_read_bool(dev, "atmel,main-osc-bypass");
+
+	/* Register main rc oscillator. */
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_RC_OSC),
+		at91_clk_main_rc(base, clk_names[ID_MAIN_RC_OSC],
+		clk_names[ID_MAIN_RC]));
+
+	/* Register main oscillator. */
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_OSC),
+		at91_clk_main_osc(base, clk_names[ID_MAIN_OSC],
+		clk_names[ID_MAIN_XTAL], main_osc_bypass));
+
+	/* Register mainck. */
+	p[0] = clk_names[ID_MAIN_RC_OSC];
+	p[1] = clk_names[ID_MAIN_OSC];
+	cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_RC_OSC);
+	cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAIN_OSC);
+	prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm, 2,
+			  fail);
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK),
+		at91_clk_sam9x5_main(base, clk_names[ID_MAINCK], p,
+		2, tmpclkmux, PMC_TYPE_CORE));
+
+	/* Register PLL fracs clocks. */
+	for (i = 0; i < ARRAY_SIZE(sama7g5_plls); i++) {
+		if (sama7g5_plls[i].t != PLL_TYPE_FRAC)
+			continue;
+
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sama7g5_plls[i].cid),
+			sam9x60_clk_register_frac_pll(base, sama7g5_plls[i].n,
+			sama7g5_plls[i].p, sama7g5_plls[i].id,
+			&pll_characteristics, sama7g5_plls[i].l,
+			sama7g5_plls[i].c));
+	}
+
+	/* Register PLL div clocks. */
+	for (i = 0; i < ARRAY_SIZE(sama7g5_plls); i++) {
+		if (sama7g5_plls[i].t != PLL_TYPE_DIV)
+			continue;
+
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sama7g5_plls[i].cid),
+			sam9x60_clk_register_div_pll(base, sama7g5_plls[i].n,
+			sama7g5_plls[i].p, sama7g5_plls[i].id,
+			&pll_characteristics, sama7g5_plls[i].l,
+			sama7g5_plls[i].c));
+	}
+
+	/* Register MCK0 clock. */
+	p[0] = clk_names[ID_MD_SLCK];
+	p[1] = clk_names[ID_MAINCK];
+	p[2] = clk_names[ID_PLL_CPU_DIV];
+	p[3] = clk_names[ID_PLL_SYS_DIV];
+	cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
+	cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
+	cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_CPU_DIV);
+	cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_SYS_DIV);
+	prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm, 2,
+			  fail);
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0),
+		at91_clk_register_master(base, clk_names[ID_MCK0], p,
+		4, &mck0_layout, &mck0_characteristics, tmpclkmux));
+
+	/* Register MCK1-4 clocks. */
+	p[0] = clk_names[ID_MD_SLCK];
+	p[1] = clk_names[ID_TD_SLCK];
+	p[2] = clk_names[ID_MAINCK];
+	p[3] = clk_names[ID_MCK0];
+	m[0] = 0;
+	m[1] = 1;
+	m[2] = 2;
+	m[3] = 3;
+	cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
+	cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK);
+	cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
+	cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0);
+	for (i = 0; i < ARRAY_SIZE(sama7g5_mckx); i++) {
+		for (j = 0; j < sama7g5_mckx[i].ep_count; j++) {
+			p[4 + j] = sama7g5_mckx[i].ep[j];
+			m[4 + j] = sama7g5_mckx[i].ep_mux_table[j];
+			cm[4 + j] = AT91_TO_CLK_ID(PMC_TYPE_CORE,
+					sama7g5_mckx[i].ep_clk_mux_table[j]);
+		}
+
+		prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm,
+			4 + sama7g5_mckx[i].ep_count, fail);
+		prepare_mux_table(muxallocs, muxallocindex, tmpmux, m,
+			4 + sama7g5_mckx[i].ep_count, fail);
+
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sama7g5_mckx[i].cid),
+			at91_clk_sama7g5_register_master(base,
+			sama7g5_mckx[i].n, p, 4 + sama7g5_mckx[i].ep_count,
+			tmpmux, tmpclkmux, sama7g5_mckx[i].c,
+			sama7g5_mckx[i].id));
+	}
+
+	/* Register UTMI clock. */
+	clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_UTMI),
+		at91_clk_sama7g5_register_utmi(base, "utmick",
+		clk_names[ID_MAIN_XTAL]));
+
+	/* Register programmable clocks. */
+	p[0] = clk_names[ID_MD_SLCK];
+	p[1] = clk_names[ID_TD_SLCK];
+	p[2] = clk_names[ID_MAINCK];
+	p[3] = clk_names[ID_MCK0];
+	p[4] = clk_names[ID_PLL_SYS_DIV];
+	p[5] = clk_names[ID_PLL_DDR_DIV];
+	p[6] = clk_names[ID_PLL_IMG_DIV];
+	p[7] = clk_names[ID_PLL_BAUD_DIV];
+	p[8] = clk_names[ID_PLL_AUDIO_DIVPMC];
+	p[9] = clk_names[ID_PLL_ETH_DIV];
+	cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
+	cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK);
+	cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
+	cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0);
+	cm[4] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_SYS_DIV);
+	cm[5] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_DDR_DIV);
+	cm[6] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_IMG_DIV);
+	cm[7] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_BAUD_DIV);
+	cm[8] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_AUDIO_DIVPMC);
+	cm[9] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_PLL_ETH_DIV);
+	for (i = 0; i < ARRAY_SIZE(sama7g5_prog); i++) {
+		prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm,
+			10, fail);
+
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_CORE, sama7g5_prog[i].cid),
+			at91_clk_register_programmable(base, sama7g5_prog[i].n,
+			p, 10, i, &programmable_layout, tmpclkmux,
+			sama7g5_prog_mux_table));
+	}
+
+	/* System clocks. */
+	for (i = 0; i < ARRAY_SIZE(sama7g5_systemck); i++) {
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_SYSTEM, sama7g5_systemck[i].cid),
+			at91_clk_register_system(base, sama7g5_systemck[i].n,
+			sama7g5_systemck[i].p, sama7g5_systemck[i].id));
+	}
+
+	/* Peripheral clocks. */
+	for (i = 0; i < ARRAY_SIZE(sama7g5_periphck); i++) {
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_PERIPHERAL,
+			sama7g5_periphck[i].id),
+			at91_clk_register_sam9x5_peripheral(base,
+			&sama7g5_pcr_layout, sama7g5_periphck[i].n,
+			sama7g5_periphck[i].p, sama7g5_periphck[i].id,
+			&sama7g5_periphck[i].r));
+	}
+
+	/* Generic clocks. */
+	p[0] = clk_names[ID_MD_SLCK];
+	p[1] = clk_names[ID_TD_SLCK];
+	p[2] = clk_names[ID_MAINCK];
+	p[3] = clk_names[ID_MCK0];
+	m[0] = 0;
+	m[1] = 1;
+	m[2] = 2;
+	m[3] = 3;
+	cm[0] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MD_SLCK);
+	cm[1] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_TD_SLCK);
+	cm[2] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MAINCK);
+	cm[3] = AT91_TO_CLK_ID(PMC_TYPE_CORE, ID_MCK0);
+	for (i = 0; i < ARRAY_SIZE(sama7g5_gck); i++) {
+		for (j = 0; j < sama7g5_gck[i].ep_count; j++) {
+			p[4 + j] = sama7g5_gck[i].ep[j];
+			m[4 + j] = sama7g5_gck[i].ep_mux_table[j];
+			cm[4 + j] = AT91_TO_CLK_ID(PMC_TYPE_CORE,
+					sama7g5_gck[i].ep_clk_mux_table[j]);
+		}
+
+		prepare_mux_table(clkmuxallocs, clkmuxallocindex, tmpclkmux, cm,
+			4 + sama7g5_gck[i].ep_count, fail);
+		prepare_mux_table(muxallocs, muxallocindex, tmpmux, m,
+			4 + sama7g5_gck[i].ep_count, fail);
+
+		clk_dm(AT91_TO_CLK_ID(PMC_TYPE_GCK, sama7g5_gck[i].id),
+			at91_clk_register_generic(base, &sama7g5_pcr_layout,
+			sama7g5_gck[i].n, p, tmpclkmux, tmpmux,
+			4 + sama7g5_gck[i].ep_count, sama7g5_gck[i].id,
+			&sama7g5_gck[i].r));
+	}
+
+	/* Setup clocks. */
+	for (i = 0; i < ARRAY_SIZE(sama7g5_clk_setup); i++) {
+		ret = clk_get_by_id(sama7g5_clk_setup[i].cid, &c);
+		if (ret)
+			goto fail;
+
+		if (sama7g5_clk_setup[i].pid) {
+			ret = clk_get_by_id(sama7g5_clk_setup[i].pid, &parent);
+			if (ret)
+				goto fail;
+
+			ret = clk_set_parent(c, parent);
+			if (ret)
+				goto fail;
+
+			if (sama7g5_clk_setup[i].prate) {
+				ret = clk_set_rate(parent,
+					sama7g5_clk_setup[i].prate);
+				if (ret < 0)
+					goto fail;
+			}
+		}
+
+		if (sama7g5_clk_setup[i].rate) {
+			ret = clk_set_rate(c, sama7g5_clk_setup[i].rate);
+			if (ret < 0)
+				goto fail;
+		}
+	}
+
+	return 0;
+
+fail:
+	for (i = 0; i < ARRAY_SIZE(muxallocs); i++)
+		kfree(muxallocs[i]);
+
+	for (i = 0; i < ARRAY_SIZE(clkmuxallocs); i++)
+		kfree(clkmuxallocs[i]);
+
+	return -ENOMEM;
+}
+
+static const struct udevice_id sama7g5_clk_ids[] = {
+	{ .compatible = "microchip,sama7g5-pmc" },
+	{ /* Sentinel. */ },
+};
+
+U_BOOT_DRIVER(at91_sama7g5_pmc) = {
+	.name = "at91-sama7g5-pmc",
+	.id = UCLASS_CLK,
+	.of_match = sama7g5_clk_ids,
+	.ops = &at91_clk_ops,
+	.probe = sama7g5_clk_probe,
+	.flags = DM_FLAG_PRE_RELOC,
+};
-- 
2.7.4

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

* [PATCH 01/22] clk: check hw and hw->dev before dereference it
  2020-07-29 14:51 ` [PATCH 01/22] clk: check hw and hw->dev before dereference it Claudiu Beznea
@ 2020-08-04  2:00   ` Simon Glass
  2020-08-04  7:19     ` Claudiu.Beznea at microchip.com
  0 siblings, 1 reply; 38+ messages in thread
From: Simon Glass @ 2020-08-04  2:00 UTC (permalink / raw)
  To: u-boot

Hi Claudiu,

On Wed, 29 Jul 2020 at 08:51, Claudiu Beznea
<claudiu.beznea@microchip.com> wrote:
>
> Check hw and hw->dev before dereference it.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> ---
>  drivers/clk/clk.c | 3 +++
>  1 file changed, 3 insertions(+)
>

Why is this needed? It adds to code size and these situations should
not occur. Perhaps use assert()?

> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 0f55ba751c0f..9fa18e342eaf 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -57,6 +57,9 @@ ulong clk_generic_get_rate(struct clk *clk)
>
>  const char *clk_hw_get_name(const struct clk *hw)
>  {
> +       if (!hw || !hw->dev)
> +               return NULL;
> +
>         return hw->dev->name;
>  }
>
> --
> 2.7.4
>

Regards,
SImon

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

* [PATCH 02/22] clk: check pointer returned by dev_get_parent()
  2020-07-29 14:51 ` [PATCH 02/22] clk: check pointer returned by dev_get_parent() Claudiu Beznea
@ 2020-08-04  2:00   ` Simon Glass
  2020-08-04  7:19     ` Claudiu.Beznea at microchip.com
  0 siblings, 1 reply; 38+ messages in thread
From: Simon Glass @ 2020-08-04  2:00 UTC (permalink / raw)
  To: u-boot

Hi Claudiu,

On Wed, 29 Jul 2020 at 08:51, Claudiu Beznea
<claudiu.beznea@microchip.com> wrote:
>
> Check pointer returned by dev_get_parent().
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> ---
>  drivers/clk/clk-uclass.c | 3 +++
>  1 file changed, 3 insertions(+)
>
> diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
> index 70df9d410f4c..aa1f11a27c41 100644
> --- a/drivers/clk/clk-uclass.c
> +++ b/drivers/clk/clk-uclass.c
> @@ -459,6 +459,9 @@ struct clk *clk_get_parent(struct clk *clk)
>                 return NULL;
>
>         pdev = dev_get_parent(clk->dev);
> +       if (!pdev)
> +               return ERR_PTR(-ENOMEM);

A clock device must always have a parent (e.g. the root device). So
this check is not useful and adds to code size.

> +
>         pclk = dev_get_clk_ptr(pdev);
>         if (!pclk)
>                 return ERR_PTR(-ENODEV);
> --
> 2.7.4
>

Regards,
Simon

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

* [PATCH 03/22] dm: core: add support for device re-parenting
  2020-07-29 14:51 ` [PATCH 03/22] dm: core: add support for device re-parenting Claudiu Beznea
@ 2020-08-04  2:00   ` Simon Glass
  2020-08-04  7:24     ` Claudiu.Beznea at microchip.com
  0 siblings, 1 reply; 38+ messages in thread
From: Simon Glass @ 2020-08-04  2:00 UTC (permalink / raw)
  To: u-boot

Hi Claudiu,

On Wed, 29 Jul 2020 at 08:51, Claudiu Beznea
<claudiu.beznea@microchip.com> wrote:
>
> In common clock framework the relation b/w parent and child clocks is
> determined based on the udevice parent/child information. A clock
> parent could be changed based on devices needs. In case this is happen
> the functionalities for clock who's parent is changed are broken. Add
> a function that reparent a device. This will be used in clk-uclass.c
> to reparent a clock device.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> ---
>  drivers/core/device.c        | 26 ++++++++++++++++++++++++++
>  include/dm/device-internal.h |  9 +++++++++
>  2 files changed, 35 insertions(+)

Please add a sandbox test for this function.

>
> diff --git a/drivers/core/device.c b/drivers/core/device.c
> index a7408d9c76c6..f149d55ac1e1 100644
> --- a/drivers/core/device.c
> +++ b/drivers/core/device.c
> @@ -267,6 +267,32 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
>                         devp);
>  }
>
> +int device_reparent(struct udevice *dev, struct udevice *new_parent)
> +{
> +       struct udevice *cparent;
> +       struct udevice *pos, *n;
> +
> +       if (!dev || !new_parent)
> +               return -EINVAL;
> +

This is an error by the caller and would not be present in production
code. Perhaps use assert()?

> +       if (!dev->parent)
> +               return -ENODEV;

This can't happen. Every device except for the root one has a parent.

> +
> +       list_for_each_entry_safe(pos, n, &dev->parent->child_head,
> +                                sibling_node) {
> +               if (pos->driver != dev->driver)
> +                       continue;
> +
> +               list_del(&dev->sibling_node);
> +               list_add_tail(&dev->sibling_node, &new_parent->child_head);
> +               dev->parent = new_parent;
> +
> +               return 0;
> +       }
> +
> +       return -ENODEV;

What does this error mean?

> +}
> +
>  static void *alloc_priv(int size, uint flags)
>  {
>         void *priv;
> diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h
> index 294d6c18105a..c5d7ec0650f9 100644
> --- a/include/dm/device-internal.h
> +++ b/include/dm/device-internal.h
> @@ -84,6 +84,15 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
>                         const struct driver_info *info, struct udevice **devp);
>
>  /**
> + * device_reparent: reparent the device to a new parent
> + *
> + * @dev: pointer to device to be reparented
> + * @new_parent: pointer to new parent device
> + * @return 0 if OK, -ve on error
> + */
> +int device_reparent(struct udevice *dev, struct udevice *new_parent);
> +
> +/**
>   * device_ofdata_to_platdata() - Read platform data for a device
>   *
>   * Read platform data for a device (typically from the device tree) so that
> --
> 2.7.4
>

Regards,
Simon

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

* [PATCH 04/22] clk: bind clk to new parent device
  2020-07-29 14:51 ` [PATCH 04/22] clk: bind clk to new parent device Claudiu Beznea
@ 2020-08-04  2:00   ` Simon Glass
  2020-08-04  7:24     ` Claudiu.Beznea at microchip.com
  0 siblings, 1 reply; 38+ messages in thread
From: Simon Glass @ 2020-08-04  2:00 UTC (permalink / raw)
  To: u-boot

On Wed, 29 Jul 2020 at 08:51, Claudiu Beznea
<claudiu.beznea@microchip.com> wrote:
>
> Clock re-parenting is not binding the clock's device to its new
> parent device, it only calls the clock's ops->set_parent() API. The
> changes in this commit re-parent the clock device to its new parent
> so that subsequent operations like clk_get_parent() to point to the
> proper parent.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> ---
>  drivers/clk/clk-uclass.c | 11 ++++++++++-
>  1 file changed, 10 insertions(+), 1 deletion(-)

Reviewed-by: Simon Glass <sjg@chromium.org>

But please add a sandbox test.


>
> diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
> index aa1f11a27c41..b390a6b01c06 100644
> --- a/drivers/clk/clk-uclass.c
> +++ b/drivers/clk/clk-uclass.c
> @@ -14,6 +14,7 @@
>  #include <errno.h>
>  #include <log.h>
>  #include <malloc.h>
> +#include <dm/device-internal.h>
>  #include <dm/devres.h>
>  #include <dm/read.h>
>  #include <linux/bug.h>
> @@ -511,6 +512,7 @@ ulong clk_set_rate(struct clk *clk, ulong rate)
>  int clk_set_parent(struct clk *clk, struct clk *parent)
>  {
>         const struct clk_ops *ops;
> +       int ret;
>
>         debug("%s(clk=%p, parent=%p)\n", __func__, clk, parent);
>         if (!clk_valid(clk))
> @@ -520,7 +522,14 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
>         if (!ops->set_parent)
>                 return -ENOSYS;
>
> -       return ops->set_parent(clk, parent);
> +       ret = ops->set_parent(clk, parent);
> +       if (ret)
> +               return ret;
> +
> +       if (CONFIG_IS_ENABLED(CLK_CCF))
> +               ret = device_reparent(clk->dev, parent->dev);
> +
> +       return ret;
>  }
>
>  int clk_enable(struct clk *clk)
> --
> 2.7.4
>

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

* [PATCH 05/22] clk: do not disable clock if it is critical
  2020-07-29 14:51 ` [PATCH 05/22] clk: do not disable clock if it is critical Claudiu Beznea
@ 2020-08-04  2:00   ` Simon Glass
  0 siblings, 0 replies; 38+ messages in thread
From: Simon Glass @ 2020-08-04  2:00 UTC (permalink / raw)
  To: u-boot

On Wed, 29 Jul 2020 at 08:52, Claudiu Beznea
<claudiu.beznea@microchip.com> wrote:
>
> Do not disable clock if it is a critical one.
>
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> ---
>  drivers/clk/clk-uclass.c | 3 +++
>  1 file changed, 3 insertions(+)

Reviewed-by: Simon Glass <sjg@chromium.org>

Please add a sandbox test


>
> diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
> index b390a6b01c06..958a9490bee2 100644
> --- a/drivers/clk/clk-uclass.c
> +++ b/drivers/clk/clk-uclass.c
> @@ -605,6 +605,9 @@ int clk_disable(struct clk *clk)
>
>         if (CONFIG_IS_ENABLED(CLK_CCF)) {
>                 if (clk->id && !clk_get_by_id(clk->id, &clkp)) {
> +                       if (clkp->flags & CLK_IS_CRITICAL)
> +                               return 0;
> +
>                         if (clkp->enable_count == 0) {
>                                 printf("clk %s already disabled\n",
>                                        clkp->dev->name);
> --
> 2.7.4
>

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

* [PATCH 06/22] clk: get clock pointer before proceeding
  2020-07-29 14:51 ` [PATCH 06/22] clk: get clock pointer before proceeding Claudiu Beznea
@ 2020-08-04  2:00   ` Simon Glass
  2020-08-04  7:26     ` Claudiu.Beznea at microchip.com
  0 siblings, 1 reply; 38+ messages in thread
From: Simon Glass @ 2020-08-04  2:00 UTC (permalink / raw)
  To: u-boot

Hi Claudiu,

On Wed, 29 Jul 2020 at 08:52, Claudiu Beznea
<claudiu.beznea@microchip.com> wrote:
>
> clk_get_by_indexed_prop() retrieves a clock with dev member being set
> with the pointer to the udevice for the clock controller driver. But
> in case of CCF each struct clk object has set in dev member the reference
> to its parent (the root of the clock tree is a fixed clock, every
> node in clock tree is a clock registered with clk_register()). In this
> case the subsequent operations like dev_get_clk_ptr() on clocks
> retrieved by clk_get_by_indexed_prop() will fail. For this, get the
> pointer to the proper clock registered (with clk_register()) using
> clk_get_by_id() before proceeding.
>
> Fixes: 1d7993d1d0ef ("clk: Port Linux common clock framework [CCF] for imx6q to U-boot (tag: v5.1.12)")
> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> ---
>  drivers/clk/clk-uclass.c | 41 +++++++++++++++++++++++++++++++++++++----
>  1 file changed, 37 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
> index 958a9490bee2..8f926aad12cf 100644
> --- a/drivers/clk/clk-uclass.c
> +++ b/drivers/clk/clk-uclass.c
> @@ -186,7 +186,7 @@ bulk_get_err:
>
>  static int clk_set_default_parents(struct udevice *dev, int stage)
>  {
> -       struct clk clk, parent_clk;
> +       struct clk clk, parent_clk, *c, *p;
>         int index;
>         int num_parents;
>         int ret;
> @@ -212,6 +212,17 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
>                         return ret;
>                 }
>
> +               if (CONFIG_IS_ENABLED(CLK_CCF)) {
> +                       ret = clk_get_by_id(parent_clk.id, &p);
> +                       if (ret) {
> +                               debug("%s(): could not get parent clock pointer, id %lu, for %s\n",
> +                                     __func__, parent_clk.id, dev_read_name(dev));
> +                               return ret;
> +                       }
> +               } else {
> +                       p = &parent_clk;
> +               }
> +
>                 ret = clk_get_by_indexed_prop(dev, "assigned-clocks",
>                                               index, &clk);
>                 if (ret) {
> @@ -231,7 +242,18 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
>                         /* do not setup twice the parent clocks */
>                         continue;
>
> -               ret = clk_set_parent(&clk, &parent_clk);
> +               if (CONFIG_IS_ENABLED(CLK_CCF)) {
> +                       ret = clk_get_by_id(clk.id, &c);
> +                       if (ret) {
> +                               debug("%s(): could not get clock pointer, id %lu, for %s\n",
> +                                     __func__, clk.id, dev_read_name(dev));
> +                               return ret;
> +                       }
> +               } else {
> +                       c = &clk;
> +               }

Could this code go in a function? It seems to be repeated three times.

> +
> +               ret = clk_set_parent(c, p);
>                 /*
>                  * Not all drivers may support clock-reparenting (as of now).
>                  * Ignore errors due to this.
> @@ -251,7 +273,7 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
>
>  static int clk_set_default_rates(struct udevice *dev, int stage)
>  {
> -       struct clk clk;
> +       struct clk clk, *c;
>         int index;
>         int num_rates;
>         int size;
> @@ -295,7 +317,18 @@ static int clk_set_default_rates(struct udevice *dev, int stage)
>                         /* do not setup twice the parent clocks */
>                         continue;
>
> -               ret = clk_set_rate(&clk, rates[index]);
> +               if (CONFIG_IS_ENABLED(CLK_CCF)) {
> +                       ret = clk_get_by_id(clk.id, &c);
> +                       if (ret) {
> +                               debug("%s(): could not get clock pointer, id %lu, for %s\n",
> +                                     __func__, clk.id, dev_read_name(dev));
> +                               return ret;
> +                       }
> +               } else {
> +                       c = &clk;
> +               }
> +
> +               ret = clk_set_rate(c, rates[index]);
>
>                 if (ret < 0) {
>                         debug("%s: failed to set rate on clock index %d (%ld) for %s\n",
> --
> 2.7.4
>

Regards,
Simon

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

* [PATCH 01/22] clk: check hw and hw->dev before dereference it
  2020-08-04  2:00   ` Simon Glass
@ 2020-08-04  7:19     ` Claudiu.Beznea at microchip.com
  2020-08-04 15:08       ` Simon Glass
  0 siblings, 1 reply; 38+ messages in thread
From: Claudiu.Beznea at microchip.com @ 2020-08-04  7:19 UTC (permalink / raw)
  To: u-boot



On 04.08.2020 05:00, Simon Glass wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> Hi Claudiu,
> 
> On Wed, 29 Jul 2020 at 08:51, Claudiu Beznea
> <claudiu.beznea@microchip.com> wrote:
>>
>> Check hw and hw->dev before dereference it.
>>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
>> ---
>>  drivers/clk/clk.c | 3 +++
>>  1 file changed, 3 insertions(+)
>>
> 
> Why is this needed? It adds to code size and these situations should
> not occur. Perhaps use assert()?

In my debugging, investigating the issues that patches 03/22, 04/22, 06/22
try to address, I reached also this function and checked these pointers. In
the end the issue was not related to them but I though it might be useful
to keep these in a patch. I will remove it in the next version.

> 
>> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
>> index 0f55ba751c0f..9fa18e342eaf 100644
>> --- a/drivers/clk/clk.c
>> +++ b/drivers/clk/clk.c
>> @@ -57,6 +57,9 @@ ulong clk_generic_get_rate(struct clk *clk)
>>
>>  const char *clk_hw_get_name(const struct clk *hw)
>>  {
>> +       if (!hw || !hw->dev)
>> +               return NULL;
>> +
>>         return hw->dev->name;
>>  }
>>
>> --
>> 2.7.4
>>
> 
> Regards,
> SImon
> 

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

* [PATCH 02/22] clk: check pointer returned by dev_get_parent()
  2020-08-04  2:00   ` Simon Glass
@ 2020-08-04  7:19     ` Claudiu.Beznea at microchip.com
  0 siblings, 0 replies; 38+ messages in thread
From: Claudiu.Beznea at microchip.com @ 2020-08-04  7:19 UTC (permalink / raw)
  To: u-boot



On 04.08.2020 05:00, Simon Glass wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> Hi Claudiu,
> 
> On Wed, 29 Jul 2020 at 08:51, Claudiu Beznea
> <claudiu.beznea@microchip.com> wrote:
>>
>> Check pointer returned by dev_get_parent().
>>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
>> ---
>>  drivers/clk/clk-uclass.c | 3 +++
>>  1 file changed, 3 insertions(+)
>>
>> diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
>> index 70df9d410f4c..aa1f11a27c41 100644
>> --- a/drivers/clk/clk-uclass.c
>> +++ b/drivers/clk/clk-uclass.c
>> @@ -459,6 +459,9 @@ struct clk *clk_get_parent(struct clk *clk)
>>                 return NULL;
>>
>>         pdev = dev_get_parent(clk->dev);
>> +       if (!pdev)
>> +               return ERR_PTR(-ENOMEM);
> 
> A clock device must always have a parent (e.g. the root device). So
> this check is not useful and adds to code size.

Sure, I'll remove it.

> >> +
>>         pclk = dev_get_clk_ptr(pdev);
>>         if (!pclk)
>>                 return ERR_PTR(-ENODEV);
>> --
>> 2.7.4
>>
> 
> Regards,
> Simon
> 

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

* [PATCH 03/22] dm: core: add support for device re-parenting
  2020-08-04  2:00   ` Simon Glass
@ 2020-08-04  7:24     ` Claudiu.Beznea at microchip.com
  0 siblings, 0 replies; 38+ messages in thread
From: Claudiu.Beznea at microchip.com @ 2020-08-04  7:24 UTC (permalink / raw)
  To: u-boot



On 04.08.2020 05:00, Simon Glass wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> Hi Claudiu,
> 
> On Wed, 29 Jul 2020 at 08:51, Claudiu Beznea
> <claudiu.beznea@microchip.com> wrote:
>>
>> In common clock framework the relation b/w parent and child clocks is
>> determined based on the udevice parent/child information. A clock
>> parent could be changed based on devices needs. In case this is happen
>> the functionalities for clock who's parent is changed are broken. Add
>> a function that reparent a device. This will be used in clk-uclass.c
>> to reparent a clock device.
>>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
>> ---
>>  drivers/core/device.c        | 26 ++++++++++++++++++++++++++
>>  include/dm/device-internal.h |  9 +++++++++
>>  2 files changed, 35 insertions(+)
> 
> Please add a sandbox test for this function.

OK.

> 
>>
>> diff --git a/drivers/core/device.c b/drivers/core/device.c
>> index a7408d9c76c6..f149d55ac1e1 100644
>> --- a/drivers/core/device.c
>> +++ b/drivers/core/device.c
>> @@ -267,6 +267,32 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
>>                         devp);
>>  }
>>
>> +int device_reparent(struct udevice *dev, struct udevice *new_parent)
>> +{
>> +       struct udevice *cparent;
>> +       struct udevice *pos, *n;
>> +
>> +       if (!dev || !new_parent)
>> +               return -EINVAL;
>> +
> 
> This is an error by the caller and would not be present in production
> code. Perhaps use assert()?

OK, I'll use assert().

> 
>> +       if (!dev->parent)
>> +               return -ENODEV;
> 
> This can't happen. Every device except for the root one has a parent.

Sure, I'll remove it.

> 
>> +
>> +       list_for_each_entry_safe(pos, n, &dev->parent->child_head,
>> +                                sibling_node) {
>> +               if (pos->driver != dev->driver)
>> +                       continue;
>> +
>> +               list_del(&dev->sibling_node);
>> +               list_add_tail(&dev->sibling_node, &new_parent->child_head);
>> +               dev->parent = new_parent;
>> +
>> +               return 0;
>> +       }
>> +
>> +       return -ENODEV;
> 
> What does this error mean?

That the device who needs re-parenting has no parent. But you already
pointed that this should not happen. This means that the above loop will
always have a match and here we should return success code.

> 
>> +}
>> +
>>  static void *alloc_priv(int size, uint flags)
>>  {
>>         void *priv;
>> diff --git a/include/dm/device-internal.h b/include/dm/device-internal.h
>> index 294d6c18105a..c5d7ec0650f9 100644
>> --- a/include/dm/device-internal.h
>> +++ b/include/dm/device-internal.h
>> @@ -84,6 +84,15 @@ int device_bind_by_name(struct udevice *parent, bool pre_reloc_only,
>>                         const struct driver_info *info, struct udevice **devp);
>>
>>  /**
>> + * device_reparent: reparent the device to a new parent
>> + *
>> + * @dev: pointer to device to be reparented
>> + * @new_parent: pointer to new parent device
>> + * @return 0 if OK, -ve on error
>> + */
>> +int device_reparent(struct udevice *dev, struct udevice *new_parent);
>> +
>> +/**
>>   * device_ofdata_to_platdata() - Read platform data for a device
>>   *
>>   * Read platform data for a device (typically from the device tree) so that
>> --
>> 2.7.4
>>
> 
> Regards,
> Simon
> 

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

* [PATCH 04/22] clk: bind clk to new parent device
  2020-08-04  2:00   ` Simon Glass
@ 2020-08-04  7:24     ` Claudiu.Beznea at microchip.com
  0 siblings, 0 replies; 38+ messages in thread
From: Claudiu.Beznea at microchip.com @ 2020-08-04  7:24 UTC (permalink / raw)
  To: u-boot



On 04.08.2020 05:00, Simon Glass wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> On Wed, 29 Jul 2020 at 08:51, Claudiu Beznea
> <claudiu.beznea@microchip.com> wrote:
>>
>> Clock re-parenting is not binding the clock's device to its new
>> parent device, it only calls the clock's ops->set_parent() API. The
>> changes in this commit re-parent the clock device to its new parent
>> so that subsequent operations like clk_get_parent() to point to the
>> proper parent.
>>
>> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
>> ---
>>  drivers/clk/clk-uclass.c | 11 ++++++++++-
>>  1 file changed, 10 insertions(+), 1 deletion(-)
> 
> Reviewed-by: Simon Glass <sjg@chromium.org>
> 
> But please add a sandbox test.

Sure!

> 
> 
>>
>> diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
>> index aa1f11a27c41..b390a6b01c06 100644
>> --- a/drivers/clk/clk-uclass.c
>> +++ b/drivers/clk/clk-uclass.c
>> @@ -14,6 +14,7 @@
>>  #include <errno.h>
>>  #include <log.h>
>>  #include <malloc.h>
>> +#include <dm/device-internal.h>
>>  #include <dm/devres.h>
>>  #include <dm/read.h>
>>  #include <linux/bug.h>
>> @@ -511,6 +512,7 @@ ulong clk_set_rate(struct clk *clk, ulong rate)
>>  int clk_set_parent(struct clk *clk, struct clk *parent)
>>  {
>>         const struct clk_ops *ops;
>> +       int ret;
>>
>>         debug("%s(clk=%p, parent=%p)\n", __func__, clk, parent);
>>         if (!clk_valid(clk))
>> @@ -520,7 +522,14 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
>>         if (!ops->set_parent)
>>                 return -ENOSYS;
>>
>> -       return ops->set_parent(clk, parent);
>> +       ret = ops->set_parent(clk, parent);
>> +       if (ret)
>> +               return ret;
>> +
>> +       if (CONFIG_IS_ENABLED(CLK_CCF))
>> +               ret = device_reparent(clk->dev, parent->dev);
>> +
>> +       return ret;
>>  }
>>
>>  int clk_enable(struct clk *clk)
>> --
>> 2.7.4
>>

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

* [PATCH 06/22] clk: get clock pointer before proceeding
  2020-08-04  2:00   ` Simon Glass
@ 2020-08-04  7:26     ` Claudiu.Beznea at microchip.com
  0 siblings, 0 replies; 38+ messages in thread
From: Claudiu.Beznea at microchip.com @ 2020-08-04  7:26 UTC (permalink / raw)
  To: u-boot



On 04.08.2020 05:00, Simon Glass wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> Hi Claudiu,
> 
> On Wed, 29 Jul 2020 at 08:52, Claudiu Beznea
> <claudiu.beznea@microchip.com> wrote:
>>
>> clk_get_by_indexed_prop() retrieves a clock with dev member being set
>> with the pointer to the udevice for the clock controller driver. But
>> in case of CCF each struct clk object has set in dev member the reference
>> to its parent (the root of the clock tree is a fixed clock, every
>> node in clock tree is a clock registered with clk_register()). In this
>> case the subsequent operations like dev_get_clk_ptr() on clocks
>> retrieved by clk_get_by_indexed_prop() will fail. For this, get the
>> pointer to the proper clock registered (with clk_register()) using
>> clk_get_by_id() before proceeding.
>>
>> Fixes: 1d7993d1d0ef ("clk: Port Linux common clock framework [CCF] for imx6q to U-boot (tag: v5.1.12)")
>> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
>> ---
>>  drivers/clk/clk-uclass.c | 41 +++++++++++++++++++++++++++++++++++++----
>>  1 file changed, 37 insertions(+), 4 deletions(-)
>>
>> diff --git a/drivers/clk/clk-uclass.c b/drivers/clk/clk-uclass.c
>> index 958a9490bee2..8f926aad12cf 100644
>> --- a/drivers/clk/clk-uclass.c
>> +++ b/drivers/clk/clk-uclass.c
>> @@ -186,7 +186,7 @@ bulk_get_err:
>>
>>  static int clk_set_default_parents(struct udevice *dev, int stage)
>>  {
>> -       struct clk clk, parent_clk;
>> +       struct clk clk, parent_clk, *c, *p;
>>         int index;
>>         int num_parents;
>>         int ret;
>> @@ -212,6 +212,17 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
>>                         return ret;
>>                 }
>>
>> +               if (CONFIG_IS_ENABLED(CLK_CCF)) {
>> +                       ret = clk_get_by_id(parent_clk.id, &p);
>> +                       if (ret) {
>> +                               debug("%s(): could not get parent clock pointer, id %lu, for %s\n",
>> +                                     __func__, parent_clk.id, dev_read_name(dev));
>> +                               return ret;
>> +                       }
>> +               } else {
>> +                       p = &parent_clk;
>> +               }
>> +
>>                 ret = clk_get_by_indexed_prop(dev, "assigned-clocks",
>>                                               index, &clk);
>>                 if (ret) {
>> @@ -231,7 +242,18 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
>>                         /* do not setup twice the parent clocks */
>>                         continue;
>>
>> -               ret = clk_set_parent(&clk, &parent_clk);
>> +               if (CONFIG_IS_ENABLED(CLK_CCF)) {
>> +                       ret = clk_get_by_id(clk.id, &c);
>> +                       if (ret) {
>> +                               debug("%s(): could not get clock pointer, id %lu, for %s\n",
>> +                                     __func__, clk.id, dev_read_name(dev));
>> +                               return ret;
>> +                       }
>> +               } else {
>> +                       c = &clk;
>> +               }
> 
> Could this code go in a function? It seems to be repeated three times.

Sure, it will!

Thank you reviewing this,
Claudiu Beznea

> 
>> +
>> +               ret = clk_set_parent(c, p);
>>                 /*
>>                  * Not all drivers may support clock-reparenting (as of now).
>>                  * Ignore errors due to this.
>> @@ -251,7 +273,7 @@ static int clk_set_default_parents(struct udevice *dev, int stage)
>>
>>  static int clk_set_default_rates(struct udevice *dev, int stage)
>>  {
>> -       struct clk clk;
>> +       struct clk clk, *c;
>>         int index;
>>         int num_rates;
>>         int size;
>> @@ -295,7 +317,18 @@ static int clk_set_default_rates(struct udevice *dev, int stage)
>>                         /* do not setup twice the parent clocks */
>>                         continue;
>>
>> -               ret = clk_set_rate(&clk, rates[index]);
>> +               if (CONFIG_IS_ENABLED(CLK_CCF)) {
>> +                       ret = clk_get_by_id(clk.id, &c);
>> +                       if (ret) {
>> +                               debug("%s(): could not get clock pointer, id %lu, for %s\n",
>> +                                     __func__, clk.id, dev_read_name(dev));
>> +                               return ret;
>> +                       }
>> +               } else {
>> +                       c = &clk;
>> +               }
>> +
>> +               ret = clk_set_rate(c, rates[index]);
>>
>>                 if (ret < 0) {
>>                         debug("%s: failed to set rate on clock index %d (%ld) for %s\n",
>> --
>> 2.7.4
>>
> 
> Regards,
> Simon
> 

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

* [PATCH 01/22] clk: check hw and hw->dev before dereference it
  2020-08-04  7:19     ` Claudiu.Beznea at microchip.com
@ 2020-08-04 15:08       ` Simon Glass
  2020-08-04 15:25         ` Claudiu.Beznea at microchip.com
  0 siblings, 1 reply; 38+ messages in thread
From: Simon Glass @ 2020-08-04 15:08 UTC (permalink / raw)
  To: u-boot

Hi Claudiu,

On Tue, 4 Aug 2020 at 01:19, <Claudiu.Beznea@microchip.com> wrote:
>
>
>
> On 04.08.2020 05:00, Simon Glass wrote:
> > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> >
> > Hi Claudiu,
> >
> > On Wed, 29 Jul 2020 at 08:51, Claudiu Beznea
> > <claudiu.beznea@microchip.com> wrote:
> >>
> >> Check hw and hw->dev before dereference it.
> >>
> >> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> >> ---
> >>  drivers/clk/clk.c | 3 +++
> >>  1 file changed, 3 insertions(+)
> >>
> >
> > Why is this needed? It adds to code size and these situations should
> > not occur. Perhaps use assert()?
>
> In my debugging, investigating the issues that patches 03/22, 04/22, 06/22
> try to address, I reached also this function and checked these pointers. In
> the end the issue was not related to them but I though it might be useful
> to keep these in a patch. I will remove it in the next version.

IMO we should use assert() to check invariants and catch basic
programming errors. But production testing should make sure that the
software basically works.

Of course it is nice to have these checks, but they add to code size
which is always a concern. So I think we should rely on assert() to
catch the errors during development, so we are not wasting code
checking for things that we know cannot happen.

Regards,
Simon

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

* [PATCH 01/22] clk: check hw and hw->dev before dereference it
  2020-08-04 15:08       ` Simon Glass
@ 2020-08-04 15:25         ` Claudiu.Beznea at microchip.com
  2020-08-04 15:29           ` Simon Glass
  0 siblings, 1 reply; 38+ messages in thread
From: Claudiu.Beznea at microchip.com @ 2020-08-04 15:25 UTC (permalink / raw)
  To: u-boot

Hi Simon,

On 04.08.2020 18:08, Simon Glass wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> Hi Claudiu,
> 
> On Tue, 4 Aug 2020 at 01:19, <Claudiu.Beznea@microchip.com> wrote:
>>
>>
>>
>> On 04.08.2020 05:00, Simon Glass wrote:
>>> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>>>
>>> Hi Claudiu,
>>>
>>> On Wed, 29 Jul 2020 at 08:51, Claudiu Beznea
>>> <claudiu.beznea@microchip.com> wrote:
>>>>
>>>> Check hw and hw->dev before dereference it.
>>>>
>>>> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
>>>> ---
>>>>  drivers/clk/clk.c | 3 +++
>>>>  1 file changed, 3 insertions(+)
>>>>
>>>
>>> Why is this needed? It adds to code size and these situations should
>>> not occur. Perhaps use assert()?
>>
>> In my debugging, investigating the issues that patches 03/22, 04/22, 06/22
>> try to address, I reached also this function and checked these pointers. In
>> the end the issue was not related to them but I though it might be useful
>> to keep these in a patch. I will remove it in the next version.
> 
> IMO we should use assert() to check invariants and catch basic
> programming errors. But production testing should make sure that the
> software basically works.
> 
> Of course it is nice to have these checks, but they add to code size
> which is always a concern. So I think we should rely on assert() to
> catch the errors during development, so we are not wasting code
> checking for things that we know cannot happen.

OK, I'll switch to assert().

Thank you,
Claudiu Beznea

> 
> Regards,
> Simon
> 

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

* [PATCH 01/22] clk: check hw and hw->dev before dereference it
  2020-08-04 15:25         ` Claudiu.Beznea at microchip.com
@ 2020-08-04 15:29           ` Simon Glass
  2020-08-04 15:55             ` Claudiu.Beznea at microchip.com
  0 siblings, 1 reply; 38+ messages in thread
From: Simon Glass @ 2020-08-04 15:29 UTC (permalink / raw)
  To: u-boot

Hi Claudiu,

On Tue, 4 Aug 2020 at 09:25, <Claudiu.Beznea@microchip.com> wrote:
>
> Hi Simon,
>
> On 04.08.2020 18:08, Simon Glass wrote:
> > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> >
> > Hi Claudiu,
> >
> > On Tue, 4 Aug 2020 at 01:19, <Claudiu.Beznea@microchip.com> wrote:
> >>
> >>
> >>
> >> On 04.08.2020 05:00, Simon Glass wrote:
> >>> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> >>>
> >>> Hi Claudiu,
> >>>
> >>> On Wed, 29 Jul 2020 at 08:51, Claudiu Beznea
> >>> <claudiu.beznea@microchip.com> wrote:
> >>>>
> >>>> Check hw and hw->dev before dereference it.
> >>>>
> >>>> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
> >>>> ---
> >>>>  drivers/clk/clk.c | 3 +++
> >>>>  1 file changed, 3 insertions(+)
> >>>>
> >>>
> >>> Why is this needed? It adds to code size and these situations should
> >>> not occur. Perhaps use assert()?
> >>
> >> In my debugging, investigating the issues that patches 03/22, 04/22, 06/22
> >> try to address, I reached also this function and checked these pointers. In
> >> the end the issue was not related to them but I though it might be useful
> >> to keep these in a patch. I will remove it in the next version.
> >
> > IMO we should use assert() to check invariants and catch basic
> > programming errors. But production testing should make sure that the
> > software basically works.
> >
> > Of course it is nice to have these checks, but they add to code size
> > which is always a concern. So I think we should rely on assert() to
> > catch the errors during development, so we are not wasting code
> > checking for things that we know cannot happen.
>
> OK, I'll switch to assert().

One more point I should have made is that my comments apply mostly to
common code that everyone has to use - e.g. the core clock code. So if
you want to put dev_err() and other things in your driver and you know
about the code-size implications that is less of a concern. But with
common code, we should be careful.

Regards,
Simon

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

* [PATCH 01/22] clk: check hw and hw->dev before dereference it
  2020-08-04 15:29           ` Simon Glass
@ 2020-08-04 15:55             ` Claudiu.Beznea at microchip.com
  0 siblings, 0 replies; 38+ messages in thread
From: Claudiu.Beznea at microchip.com @ 2020-08-04 15:55 UTC (permalink / raw)
  To: u-boot



On 04.08.2020 18:29, Simon Glass wrote:
> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> 
> Hi Claudiu,
> 
> On Tue, 4 Aug 2020 at 09:25, <Claudiu.Beznea@microchip.com> wrote:
>>
>> Hi Simon,
>>
>> On 04.08.2020 18:08, Simon Glass wrote:
>>> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>>>
>>> Hi Claudiu,
>>>
>>> On Tue, 4 Aug 2020 at 01:19, <Claudiu.Beznea@microchip.com> wrote:
>>>>
>>>>
>>>>
>>>> On 04.08.2020 05:00, Simon Glass wrote:
>>>>> EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
>>>>>
>>>>> Hi Claudiu,
>>>>>
>>>>> On Wed, 29 Jul 2020 at 08:51, Claudiu Beznea
>>>>> <claudiu.beznea@microchip.com> wrote:
>>>>>>
>>>>>> Check hw and hw->dev before dereference it.
>>>>>>
>>>>>> Signed-off-by: Claudiu Beznea <claudiu.beznea@microchip.com>
>>>>>> ---
>>>>>>  drivers/clk/clk.c | 3 +++
>>>>>>  1 file changed, 3 insertions(+)
>>>>>>
>>>>>
>>>>> Why is this needed? It adds to code size and these situations should
>>>>> not occur. Perhaps use assert()?
>>>>
>>>> In my debugging, investigating the issues that patches 03/22, 04/22, 06/22
>>>> try to address, I reached also this function and checked these pointers. In
>>>> the end the issue was not related to them but I though it might be useful
>>>> to keep these in a patch. I will remove it in the next version.
>>>
>>> IMO we should use assert() to check invariants and catch basic
>>> programming errors. But production testing should make sure that the
>>> software basically works.
>>>
>>> Of course it is nice to have these checks, but they add to code size
>>> which is always a concern. So I think we should rely on assert() to
>>> catch the errors during development, so we are not wasting code
>>> checking for things that we know cannot happen.
>>
>> OK, I'll switch to assert().
> 
> One more point I should have made is that my comments apply mostly to
> common code that everyone has to use - e.g. the core clock code. So if
> you want to put dev_err() and other things in your driver and you know
> about the code-size implications that is less of a concern. But with
> common code, we should be careful.

Sure!

Thank you,
Claudiu Beznea

> 
> Regards,
> Simon
> 

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

end of thread, other threads:[~2020-08-04 15:55 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-29 14:51 [PATCH 00/22] clk: at91: add sama7g5 support Claudiu Beznea
2020-07-29 14:51 ` [PATCH 01/22] clk: check hw and hw->dev before dereference it Claudiu Beznea
2020-08-04  2:00   ` Simon Glass
2020-08-04  7:19     ` Claudiu.Beznea at microchip.com
2020-08-04 15:08       ` Simon Glass
2020-08-04 15:25         ` Claudiu.Beznea at microchip.com
2020-08-04 15:29           ` Simon Glass
2020-08-04 15:55             ` Claudiu.Beznea at microchip.com
2020-07-29 14:51 ` [PATCH 02/22] clk: check pointer returned by dev_get_parent() Claudiu Beznea
2020-08-04  2:00   ` Simon Glass
2020-08-04  7:19     ` Claudiu.Beznea at microchip.com
2020-07-29 14:51 ` [PATCH 03/22] dm: core: add support for device re-parenting Claudiu Beznea
2020-08-04  2:00   ` Simon Glass
2020-08-04  7:24     ` Claudiu.Beznea at microchip.com
2020-07-29 14:51 ` [PATCH 04/22] clk: bind clk to new parent device Claudiu Beznea
2020-08-04  2:00   ` Simon Glass
2020-08-04  7:24     ` Claudiu.Beznea at microchip.com
2020-07-29 14:51 ` [PATCH 05/22] clk: do not disable clock if it is critical Claudiu Beznea
2020-08-04  2:00   ` Simon Glass
2020-07-29 14:51 ` [PATCH 06/22] clk: get clock pointer before proceeding Claudiu Beznea
2020-08-04  2:00   ` Simon Glass
2020-08-04  7:26     ` Claudiu.Beznea at microchip.com
2020-07-29 14:51 ` [PATCH 07/22] clk: at91: add pre-requisite headers for AT91 clock architecture Claudiu Beznea
2020-07-29 14:51 ` [PATCH 08/22] clk: at91: pmc: add helpers for clock drivers Claudiu Beznea
2020-07-29 14:51 ` [PATCH 09/22] clk: at91: move clock code to compat.c Claudiu Beznea
2020-07-29 14:51 ` [PATCH 10/22] clk: at91: sckc: add driver compatible with ccf Claudiu Beznea
2020-07-29 14:51 ` [PATCH 11/22] clk: at91: clk-main: " Claudiu Beznea
2020-07-29 14:51 ` [PATCH 12/22] clk: at91: sam9x60-pll: " Claudiu Beznea
2020-07-29 14:51 ` [PATCH 13/22] clk: at91: clk-master: " Claudiu Beznea
2020-07-29 14:51 ` [PATCH 14/22] clk: at91: clk-master: add support for sama7g5 Claudiu Beznea
2020-07-29 14:51 ` [PATCH 15/22] clk: at91: clk-utmi: add driver compatible with ccf Claudiu Beznea
2020-07-29 14:51 ` [PATCH 16/22] clk: at91: clk-utmi: add support for sama7g5 Claudiu Beznea
2020-07-29 14:51 ` [PATCH 17/22] clk: at91: clk-programmable: add driver compatible with ccf Claudiu Beznea
2020-07-29 14:51 ` [PATCH 18/22] clk: at91: clk-system: " Claudiu Beznea
2020-07-29 14:51 ` [PATCH 19/22] clk: at91: clk-peripheral: " Claudiu Beznea
2020-07-29 14:51 ` [PATCH 20/22] clk: at91: clk-generic: " Claudiu Beznea
2020-07-29 14:51 ` [PATCH 21/22] clk: at91: pmc: add generic clock ops Claudiu Beznea
2020-07-29 14:51 ` [PATCH 22/22] clk: at91: sama7g5: add clock support Claudiu Beznea

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.