mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III)
@ 2017-03-14 15:52 Andrey Smirnov
  2017-03-14 15:52 ` [PATCH 01/11] clocksource: at91: Add DT compatibility table Andrey Smirnov
                   ` (11 more replies)
  0 siblings, 12 replies; 15+ messages in thread
From: Andrey Smirnov @ 2017-03-14 15:52 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Hi everone,

This is the second batch of AT91 related patches (original thread
[1]). The patches gathered in this set are with a few exceptions are
patches adding DT probing support for various AT91 drivers.

Feedback from Sam has been incorporated, but other than that the code
should be as it was in [1].

Any feedback is appreciated.

Thank you,
Andrey Smirnov

[1] http://lists.infradead.org/pipermail/barebox/2017-March/029337.html

Andrey Smirnov (11):
  clocksource: at91: Add DT compatibility table
  serial: atmel: Add DT compatibility table
  clk: at91: Port at91 DT clock code
  mci: Allow parsing for explicit DT node
  mci: atmel_mci: Add DT support
  spi: atmel_spi: Add DT support
  w1-gpio: Add DT support
  usb: ohci-at91: Add DT support
  usb/host: Allow USB_OHCI_AT91 even if USB_OHCI is disabled
  usb: echi-atmel: Add DT support
  net: macb: Add DT support

 arch/arm/Kconfig                        |   1 +
 arch/arm/mach-at91/Kconfig              |  20 ++
 arch/arm/mach-at91/include/mach/board.h |   6 +-
 drivers/clk/Makefile                    |   1 +
 drivers/clk/at91/Makefile               |  15 +
 drivers/clk/at91/clk-generated.c        | 323 ++++++++++++++++++
 drivers/clk/at91/clk-h32mx.c            | 125 +++++++
 drivers/clk/at91/clk-main.c             | 576 ++++++++++++++++++++++++++++++++
 drivers/clk/at91/clk-master.c           | 245 ++++++++++++++
 drivers/clk/at91/clk-peripheral.c       | 430 ++++++++++++++++++++++++
 drivers/clk/at91/clk-pll.c              | 516 ++++++++++++++++++++++++++++
 drivers/clk/at91/clk-plldiv.c           | 135 ++++++++
 drivers/clk/at91/clk-programmable.c     | 254 ++++++++++++++
 drivers/clk/at91/clk-slow.c             | 108 ++++++
 drivers/clk/at91/clk-smd.c              | 172 ++++++++++
 drivers/clk/at91/clk-system.c           | 160 +++++++++
 drivers/clk/at91/clk-usb.c              | 397 ++++++++++++++++++++++
 drivers/clk/at91/clk-utmi.c             | 138 ++++++++
 drivers/clk/at91/pmc.c                  |  41 +++
 drivers/clk/at91/pmc.h                  |  27 ++
 drivers/clk/at91/sckc.c                 | 485 +++++++++++++++++++++++++++
 drivers/clocksource/timer-atmel-pit.c   |   9 +
 drivers/mci/atmel_mci.c                 | 101 ++++--
 drivers/mci/mci-core.c                  |  13 +-
 drivers/net/macb.c                      |  56 +++-
 drivers/serial/atmel.c                  |   7 +
 drivers/spi/atmel_spi.c                 |  29 +-
 drivers/usb/host/Kconfig                |   5 +-
 drivers/usb/host/ehci-atmel.c           |  11 +-
 drivers/usb/host/ohci-at91.c            |  93 +++++-
 drivers/w1/masters/w1-gpio.c            |  53 +++
 include/linux/clk/at91_pmc.h            | 188 +++++++++++
 include/mci.h                           |   1 +
 33 files changed, 4679 insertions(+), 62 deletions(-)
 create mode 100644 drivers/clk/at91/Makefile
 create mode 100644 drivers/clk/at91/clk-generated.c
 create mode 100644 drivers/clk/at91/clk-h32mx.c
 create mode 100644 drivers/clk/at91/clk-main.c
 create mode 100644 drivers/clk/at91/clk-master.c
 create mode 100644 drivers/clk/at91/clk-peripheral.c
 create mode 100644 drivers/clk/at91/clk-pll.c
 create mode 100644 drivers/clk/at91/clk-plldiv.c
 create mode 100644 drivers/clk/at91/clk-programmable.c
 create mode 100644 drivers/clk/at91/clk-slow.c
 create mode 100644 drivers/clk/at91/clk-smd.c
 create mode 100644 drivers/clk/at91/clk-system.c
 create mode 100644 drivers/clk/at91/clk-usb.c
 create mode 100644 drivers/clk/at91/clk-utmi.c
 create mode 100644 drivers/clk/at91/pmc.c
 create mode 100644 drivers/clk/at91/pmc.h
 create mode 100644 drivers/clk/at91/sckc.c
 create mode 100644 include/linux/clk/at91_pmc.h

-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 01/11] clocksource: at91: Add DT compatibility table
  2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
@ 2017-03-14 15:52 ` Andrey Smirnov
  2017-03-15 20:33   ` Sam Ravnborg
  2017-03-14 15:52 ` [PATCH 02/11] serial: atmel: " Andrey Smirnov
                   ` (10 subsequent siblings)
  11 siblings, 1 reply; 15+ messages in thread
From: Andrey Smirnov @ 2017-03-14 15:52 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 drivers/clocksource/timer-atmel-pit.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c
index cc7ad2f..d59efa6 100644
--- a/drivers/clocksource/timer-atmel-pit.c
+++ b/drivers/clocksource/timer-atmel-pit.c
@@ -102,9 +102,18 @@ static int at91_pit_probe(struct device_d *dev)
 	return init_clock(&cs);
 }
 
+static __maybe_unused struct of_device_id at91_pit_dt_ids[] = {
+	{
+		.compatible = "atmel,at91sam9260-pit",
+	}, {
+		/* sentinel */
+	}
+};
+
 static struct driver_d at91_pit_driver = {
 	.name = "at91-pit",
 	.probe = at91_pit_probe,
+	.of_compatible = DRV_OF_COMPAT(at91_pit_dt_ids),
 };
 
 static int at91_pit_init(void)
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 02/11] serial: atmel: Add DT compatibility table
  2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
  2017-03-14 15:52 ` [PATCH 01/11] clocksource: at91: Add DT compatibility table Andrey Smirnov
@ 2017-03-14 15:52 ` Andrey Smirnov
  2017-03-14 15:52 ` [PATCH 03/11] clk: at91: Port at91 DT clock code Andrey Smirnov
                   ` (9 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Andrey Smirnov @ 2017-03-14 15:52 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 drivers/serial/atmel.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/serial/atmel.c b/drivers/serial/atmel.c
index ab94fd2..2f8adc9 100644
--- a/drivers/serial/atmel.c
+++ b/drivers/serial/atmel.c
@@ -446,8 +446,15 @@ static int atmel_serial_probe(struct device_d *dev)
 	return 0;
 }
 
+static const struct of_device_id __maybe_unused atmel_serial_dt_ids[] = {
+	{ .compatible = "atmel,at91rm9200-usart" },
+	{ .compatible = "atmel,at91sam9260-usart" },
+	{ /* sentinel */ }
+};
+
 static struct driver_d atmel_serial_driver = {
         .name  = "atmel_usart",
         .probe = atmel_serial_probe,
+	.of_compatible = DRV_OF_COMPAT(atmel_serial_dt_ids),
 };
 console_platform_driver(atmel_serial_driver);
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 03/11] clk: at91: Port at91 DT clock code
  2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
  2017-03-14 15:52 ` [PATCH 01/11] clocksource: at91: Add DT compatibility table Andrey Smirnov
  2017-03-14 15:52 ` [PATCH 02/11] serial: atmel: " Andrey Smirnov
@ 2017-03-14 15:52 ` Andrey Smirnov
  2017-03-14 15:52 ` [PATCH 04/11] mci: Allow parsing for explicit DT node Andrey Smirnov
                   ` (8 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Andrey Smirnov @ 2017-03-14 15:52 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Port at91 DT clock code from Linux 4.9-rc3.

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 arch/arm/Kconfig                    |   1 +
 arch/arm/mach-at91/Kconfig          |  20 ++
 drivers/clk/Makefile                |   1 +
 drivers/clk/at91/Makefile           |  15 +
 drivers/clk/at91/clk-generated.c    | 323 ++++++++++++++++++++
 drivers/clk/at91/clk-h32mx.c        | 125 ++++++++
 drivers/clk/at91/clk-main.c         | 576 ++++++++++++++++++++++++++++++++++++
 drivers/clk/at91/clk-master.c       | 245 +++++++++++++++
 drivers/clk/at91/clk-peripheral.c   | 430 +++++++++++++++++++++++++++
 drivers/clk/at91/clk-pll.c          | 516 ++++++++++++++++++++++++++++++++
 drivers/clk/at91/clk-plldiv.c       | 135 +++++++++
 drivers/clk/at91/clk-programmable.c | 254 ++++++++++++++++
 drivers/clk/at91/clk-slow.c         | 108 +++++++
 drivers/clk/at91/clk-smd.c          | 172 +++++++++++
 drivers/clk/at91/clk-system.c       | 160 ++++++++++
 drivers/clk/at91/clk-usb.c          | 397 +++++++++++++++++++++++++
 drivers/clk/at91/clk-utmi.c         | 138 +++++++++
 drivers/clk/at91/pmc.c              |  41 +++
 drivers/clk/at91/pmc.h              |  27 ++
 drivers/clk/at91/sckc.c             | 485 ++++++++++++++++++++++++++++++
 include/linux/clk/at91_pmc.h        | 188 ++++++++++++
 21 files changed, 4357 insertions(+)
 create mode 100644 drivers/clk/at91/Makefile
 create mode 100644 drivers/clk/at91/clk-generated.c
 create mode 100644 drivers/clk/at91/clk-h32mx.c
 create mode 100644 drivers/clk/at91/clk-main.c
 create mode 100644 drivers/clk/at91/clk-master.c
 create mode 100644 drivers/clk/at91/clk-peripheral.c
 create mode 100644 drivers/clk/at91/clk-pll.c
 create mode 100644 drivers/clk/at91/clk-plldiv.c
 create mode 100644 drivers/clk/at91/clk-programmable.c
 create mode 100644 drivers/clk/at91/clk-slow.c
 create mode 100644 drivers/clk/at91/clk-smd.c
 create mode 100644 drivers/clk/at91/clk-system.c
 create mode 100644 drivers/clk/at91/clk-usb.c
 create mode 100644 drivers/clk/at91/clk-utmi.c
 create mode 100644 drivers/clk/at91/pmc.c
 create mode 100644 drivers/clk/at91/pmc.h
 create mode 100644 drivers/clk/at91/sckc.c
 create mode 100644 include/linux/clk/at91_pmc.h

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index e4663ea..e69ed9c 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -55,6 +55,7 @@ config ARCH_AT91
 	select HAVE_MACH_ARM_HEAD
 	select HAVE_CLK
 	select PINCTRL_AT91
+	select COMMON_CLK_AT91
 
 config ARCH_BCM2835
 	bool "Broadcom BCM2835 boards"
diff --git a/arch/arm/mach-at91/Kconfig b/arch/arm/mach-at91/Kconfig
index 2d4721a..4ac69fc 100644
--- a/arch/arm/mach-at91/Kconfig
+++ b/arch/arm/mach-at91/Kconfig
@@ -9,6 +9,26 @@ config HAVE_AT91_DBGU1
 config HAVE_AT91_DBGU2
 	bool
 
+config HAVE_AT91_UTMI
+	bool
+
+config HAVE_AT91_USB_CLK
+	bool
+
+config COMMON_CLK_AT91
+	bool
+	select COMMON_CLK
+	select MFD_SYSCON
+
+config HAVE_AT91_SMD
+	bool
+
+config HAVE_AT91_H32MX
+	bool
+
+config HAVE_AT91_GENERATED_CLK
+	bool
+
 config HAVE_AT91_LOWLEVEL_INIT
 	bool
 
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 5811d28..d75b954 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_ARCH_TEGRA)	+= tegra/
 obj-$(CONFIG_CLK_SOCFPGA)	+= socfpga.o
 obj-$(CONFIG_MACH_MIPS_ATH79)	+= clk-ar933x.o
 obj-$(CONFIG_ARCH_IMX)		+= imx/
+obj-$(CONFIG_COMMON_CLK_AT91)	+= at91/
diff --git a/drivers/clk/at91/Makefile b/drivers/clk/at91/Makefile
new file mode 100644
index 0000000..bfd06a4
--- /dev/null
+++ b/drivers/clk/at91/Makefile
@@ -0,0 +1,15 @@
+#
+# Makefile for at91 specific clk
+#
+
+obj-y += pmc.o sckc.o
+obj-y += clk-slow.o clk-main.o clk-pll.o clk-plldiv.o clk-master.o
+obj-y += clk-system.o clk-peripheral.o clk-programmable.o
+
+obj-$(CONFIG_HAVE_AT91_UTMI)		+= clk-utmi.o
+obj-$(CONFIG_HAVE_AT91_USB_CLK)		+= clk-usb.o
+obj-$(CONFIG_HAVE_AT91_SMD)		+= clk-smd.o
+obj-$(CONFIG_HAVE_AT91_H32MX)		+= clk-h32mx.o
+obj-$(CONFIG_HAVE_AT91_GENERATED_CLK)	+= clk-generated.o
+
+
diff --git a/drivers/clk/at91/clk-generated.c b/drivers/clk/at91/clk-generated.c
new file mode 100644
index 0000000..4e1cd5a
--- /dev/null
+++ b/drivers/clk/at91/clk-generated.c
@@ -0,0 +1,323 @@
+/*
+ *  Copyright (C) 2015 Atmel Corporation,
+ *                     Nicolas Ferre <nicolas.ferre@atmel.com>
+ *
+ * Based on clk-programmable & clk-peripheral drivers by Boris BREZILLON.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include "pmc.h"
+
+#define PERIPHERAL_MAX		64
+#define PERIPHERAL_ID_MIN	2
+
+#define GENERATED_SOURCE_MAX	6
+#define GENERATED_MAX_DIV	255
+
+struct clk_generated {
+	struct clk_hw hw;
+	struct regmap *regmap;
+	struct clk_range range;
+	spinlock_t *lock;
+	u32 id;
+	u32 gckdiv;
+	u8 parent_id;
+};
+
+#define to_clk_generated(hw) \
+	container_of(hw, struct clk_generated, hw)
+
+static int clk_generated_enable(struct clk_hw *hw)
+{
+	struct clk_generated *gck = to_clk_generated(hw);
+	unsigned long flags;
+
+	pr_debug("GCLK: %s, gckdiv = %d, parent id = %d\n",
+		 __func__, gck->gckdiv, gck->parent_id);
+
+	spin_lock_irqsave(gck->lock, flags);
+	regmap_write(gck->regmap, AT91_PMC_PCR,
+		     (gck->id & AT91_PMC_PCR_PID_MASK));
+	regmap_update_bits(gck->regmap, AT91_PMC_PCR,
+			   AT91_PMC_PCR_GCKDIV_MASK | AT91_PMC_PCR_GCKCSS_MASK |
+			   AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN,
+			   AT91_PMC_PCR_GCKCSS(gck->parent_id) |
+			   AT91_PMC_PCR_CMD |
+			   AT91_PMC_PCR_GCKDIV(gck->gckdiv) |
+			   AT91_PMC_PCR_GCKEN);
+	spin_unlock_irqrestore(gck->lock, flags);
+	return 0;
+}
+
+static void clk_generated_disable(struct clk_hw *hw)
+{
+	struct clk_generated *gck = to_clk_generated(hw);
+	unsigned long flags;
+
+	spin_lock_irqsave(gck->lock, flags);
+	regmap_write(gck->regmap, AT91_PMC_PCR,
+		     (gck->id & AT91_PMC_PCR_PID_MASK));
+	regmap_update_bits(gck->regmap, AT91_PMC_PCR,
+			   AT91_PMC_PCR_CMD | AT91_PMC_PCR_GCKEN,
+			   AT91_PMC_PCR_CMD);
+	spin_unlock_irqrestore(gck->lock, flags);
+}
+
+static int clk_generated_is_enabled(struct clk_hw *hw)
+{
+	struct clk_generated *gck = to_clk_generated(hw);
+	unsigned long flags;
+	unsigned int status;
+
+	spin_lock_irqsave(gck->lock, flags);
+	regmap_write(gck->regmap, AT91_PMC_PCR,
+		     (gck->id & AT91_PMC_PCR_PID_MASK));
+	regmap_read(gck->regmap, AT91_PMC_PCR, &status);
+	spin_unlock_irqrestore(gck->lock, flags);
+
+	return status & AT91_PMC_PCR_GCKEN ? 1 : 0;
+}
+
+static unsigned long
+clk_generated_recalc_rate(struct clk_hw *hw,
+			  unsigned long parent_rate)
+{
+	struct clk_generated *gck = to_clk_generated(hw);
+
+	return DIV_ROUND_CLOSEST(parent_rate, gck->gckdiv + 1);
+}
+
+static int clk_generated_determine_rate(struct clk_hw *hw,
+					struct clk_rate_request *req)
+{
+	struct clk_generated *gck = to_clk_generated(hw);
+	struct clk_hw *parent = NULL;
+	long best_rate = -EINVAL;
+	unsigned long tmp_rate, min_rate;
+	int best_diff = -1;
+	int tmp_diff;
+	int i;
+
+	for (i = 0; i < clk_hw_get_num_parents(hw); i++) {
+		u32 div;
+		unsigned long parent_rate;
+
+		parent = clk_hw_get_parent_by_index(hw, i);
+		if (!parent)
+			continue;
+
+		parent_rate = clk_hw_get_rate(parent);
+		min_rate = DIV_ROUND_CLOSEST(parent_rate, GENERATED_MAX_DIV + 1);
+		if (!parent_rate ||
+		    (gck->range.max && min_rate > gck->range.max))
+			continue;
+
+		for (div = 1; div < GENERATED_MAX_DIV + 2; div++) {
+			tmp_rate = DIV_ROUND_CLOSEST(parent_rate, div);
+			tmp_diff = abs(req->rate - tmp_rate);
+
+			if (best_diff < 0 || best_diff > tmp_diff) {
+				best_rate = tmp_rate;
+				best_diff = tmp_diff;
+				req->best_parent_rate = parent_rate;
+				req->best_parent_hw = parent;
+			}
+
+			if (!best_diff || tmp_rate < req->rate)
+				break;
+		}
+
+		if (!best_diff)
+			break;
+	}
+
+	pr_debug("GCLK: %s, best_rate = %ld, parent clk: %s @ %ld\n",
+		 __func__, best_rate,
+		 __clk_get_name((req->best_parent_hw)->clk),
+		 req->best_parent_rate);
+
+	if (best_rate < 0)
+		return best_rate;
+
+	req->rate = best_rate;
+	return 0;
+}
+
+/* No modification of hardware as we have the flag CLK_SET_PARENT_GATE set */
+static int clk_generated_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct clk_generated *gck = to_clk_generated(hw);
+
+	if (index >= clk_hw_get_num_parents(hw))
+		return -EINVAL;
+
+	gck->parent_id = index;
+	return 0;
+}
+
+static u8 clk_generated_get_parent(struct clk_hw *hw)
+{
+	struct clk_generated *gck = to_clk_generated(hw);
+
+	return gck->parent_id;
+}
+
+/* No modification of hardware as we have the flag CLK_SET_RATE_GATE set */
+static int clk_generated_set_rate(struct clk_hw *hw,
+				  unsigned long rate,
+				  unsigned long parent_rate)
+{
+	struct clk_generated *gck = to_clk_generated(hw);
+	u32 div;
+
+	if (!rate)
+		return -EINVAL;
+
+	if (gck->range.max && rate > gck->range.max)
+		return -EINVAL;
+
+	div = DIV_ROUND_CLOSEST(parent_rate, rate);
+	if (div > GENERATED_MAX_DIV + 1 || !div)
+		return -EINVAL;
+
+	gck->gckdiv = div - 1;
+	return 0;
+}
+
+static const struct clk_ops generated_ops = {
+	.enable = clk_generated_enable,
+	.disable = clk_generated_disable,
+	.is_enabled = clk_generated_is_enabled,
+	.recalc_rate = clk_generated_recalc_rate,
+	.determine_rate = clk_generated_determine_rate,
+	.get_parent = clk_generated_get_parent,
+	.set_parent = clk_generated_set_parent,
+	.set_rate = clk_generated_set_rate,
+};
+
+/**
+ * clk_generated_startup - Initialize a given clock to its default parent and
+ * divisor parameter.
+ *
+ * @gck:	Generated clock to set the startup parameters for.
+ *
+ * Take parameters from the hardware and update local clock configuration
+ * accordingly.
+ */
+static void clk_generated_startup(struct clk_generated *gck)
+{
+	u32 tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(gck->lock, flags);
+	regmap_write(gck->regmap, AT91_PMC_PCR,
+		     (gck->id & AT91_PMC_PCR_PID_MASK));
+	regmap_read(gck->regmap, AT91_PMC_PCR, &tmp);
+	spin_unlock_irqrestore(gck->lock, flags);
+
+	gck->parent_id = (tmp & AT91_PMC_PCR_GCKCSS_MASK)
+					>> AT91_PMC_PCR_GCKCSS_OFFSET;
+	gck->gckdiv = (tmp & AT91_PMC_PCR_GCKDIV_MASK)
+					>> AT91_PMC_PCR_GCKDIV_OFFSET;
+}
+
+static struct clk_hw * __init
+at91_clk_register_generated(struct regmap *regmap, spinlock_t *lock,
+			    const char *name, const char **parent_names,
+			    u8 num_parents, u8 id,
+			    const struct clk_range *range)
+{
+	struct clk_generated *gck;
+	struct clk_init_data init;
+	struct clk_hw *hw;
+	int ret;
+
+	gck = kzalloc(sizeof(*gck), GFP_KERNEL);
+	if (!gck)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &generated_ops;
+	init.parent_names = parent_names;
+	init.num_parents = num_parents;
+	init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE;
+
+	gck->id = id;
+	gck->hw.init = &init;
+	gck->regmap = regmap;
+	gck->lock = lock;
+	gck->range = *range;
+
+	hw = &gck->hw;
+	ret = clk_hw_register(NULL, &gck->hw);
+	if (ret) {
+		kfree(gck);
+		hw = ERR_PTR(ret);
+	} else
+		clk_generated_startup(gck);
+
+	return hw;
+}
+
+static void __init of_sama5d2_clk_generated_setup(struct device_node *np)
+{
+	int num;
+	u32 id;
+	const char *name;
+	struct clk_hw *hw;
+	unsigned int num_parents;
+	const char *parent_names[GENERATED_SOURCE_MAX];
+	struct device_node *gcknp;
+	struct clk_range range = CLK_RANGE(0, 0);
+	struct regmap *regmap;
+
+	num_parents = of_clk_get_parent_count(np);
+	if (num_parents == 0 || num_parents > GENERATED_SOURCE_MAX)
+		return;
+
+	of_clk_parent_fill(np, parent_names, num_parents);
+
+	num = of_get_child_count(np);
+	if (!num || num > PERIPHERAL_MAX)
+		return;
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return;
+
+	for_each_child_of_node(np, gcknp) {
+		if (of_property_read_u32(gcknp, "reg", &id))
+			continue;
+
+		if (id < PERIPHERAL_ID_MIN || id >= PERIPHERAL_MAX)
+			continue;
+
+		if (of_property_read_string(np, "clock-output-names", &name))
+			name = gcknp->name;
+
+		of_at91_get_clk_range(gcknp, "atmel,clk-output-range",
+				      &range);
+
+		hw = at91_clk_register_generated(regmap, &pmc_pcr_lock, name,
+						  parent_names, num_parents,
+						  id, &range);
+		if (IS_ERR(hw))
+			continue;
+
+		of_clk_add_hw_provider(gcknp, of_clk_hw_simple_get, hw);
+	}
+}
+CLK_OF_DECLARE(of_sama5d2_clk_generated_setup, "atmel,sama5d2-clk-generated",
+	       of_sama5d2_clk_generated_setup);
diff --git a/drivers/clk/at91/clk-h32mx.c b/drivers/clk/at91/clk-h32mx.c
new file mode 100644
index 0000000..e0daa4a
--- /dev/null
+++ b/drivers/clk/at91/clk-h32mx.c
@@ -0,0 +1,125 @@
+/*
+ * clk-h32mx.c
+ *
+ *  Copyright (C) 2014 Atmel
+ *
+ * Alexandre Belloni <alexandre.belloni@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/clk/at91_pmc.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#include "pmc.h"
+
+#define H32MX_MAX_FREQ	90000000
+
+struct clk_sama5d4_h32mx {
+	struct clk_hw hw;
+	struct regmap *regmap;
+};
+
+#define to_clk_sama5d4_h32mx(hw) container_of(hw, struct clk_sama5d4_h32mx, hw)
+
+static unsigned long clk_sama5d4_h32mx_recalc_rate(struct clk_hw *hw,
+						 unsigned long parent_rate)
+{
+	struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw);
+	unsigned int mckr;
+
+	regmap_read(h32mxclk->regmap, AT91_PMC_MCKR, &mckr);
+	if (mckr & AT91_PMC_H32MXDIV)
+		return parent_rate / 2;
+
+	if (parent_rate > H32MX_MAX_FREQ)
+		pr_warn("H32MX clock is too fast\n");
+	return parent_rate;
+}
+
+static long clk_sama5d4_h32mx_round_rate(struct clk_hw *hw, unsigned long rate,
+				       unsigned long *parent_rate)
+{
+	unsigned long div;
+
+	if (rate > *parent_rate)
+		return *parent_rate;
+	div = *parent_rate / 2;
+	if (rate < div)
+		return div;
+
+	if (rate - div < *parent_rate - rate)
+		return div;
+
+	return *parent_rate;
+}
+
+static int clk_sama5d4_h32mx_set_rate(struct clk_hw *hw, unsigned long rate,
+				    unsigned long parent_rate)
+{
+	struct clk_sama5d4_h32mx *h32mxclk = to_clk_sama5d4_h32mx(hw);
+	u32 mckr = 0;
+
+	if (parent_rate != rate && (parent_rate / 2) != rate)
+		return -EINVAL;
+
+	if ((parent_rate / 2) == rate)
+		mckr = AT91_PMC_H32MXDIV;
+
+	regmap_update_bits(h32mxclk->regmap, AT91_PMC_MCKR,
+			   AT91_PMC_H32MXDIV, mckr);
+
+	return 0;
+}
+
+static const struct clk_ops h32mx_ops = {
+	.recalc_rate = clk_sama5d4_h32mx_recalc_rate,
+	.round_rate = clk_sama5d4_h32mx_round_rate,
+	.set_rate = clk_sama5d4_h32mx_set_rate,
+};
+
+static void __init of_sama5d4_clk_h32mx_setup(struct device_node *np)
+{
+	struct clk_sama5d4_h32mx *h32mxclk;
+	struct clk_init_data init;
+	const char *parent_name;
+	struct regmap *regmap;
+	int ret;
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return;
+
+	h32mxclk = kzalloc(sizeof(*h32mxclk), GFP_KERNEL);
+	if (!h32mxclk)
+		return;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	init.name = np->name;
+	init.ops = &h32mx_ops;
+	init.parent_names = parent_name ? &parent_name : NULL;
+	init.num_parents = parent_name ? 1 : 0;
+	init.flags = CLK_SET_RATE_GATE;
+
+	h32mxclk->hw.init = &init;
+	h32mxclk->regmap = regmap;
+
+	ret = clk_hw_register(NULL, &h32mxclk->hw);
+	if (ret) {
+		kfree(h32mxclk);
+		return;
+	}
+
+	of_clk_add_hw_provider(np, of_clk_hw_simple_get, &h32mxclk->hw);
+}
+CLK_OF_DECLARE(of_sama5d4_clk_h32mx_setup, "atmel,sama5d4-clk-h32mx",
+	       of_sama5d4_clk_h32mx_setup);
diff --git a/drivers/clk/at91/clk-main.c b/drivers/clk/at91/clk-main.c
new file mode 100644
index 0000000..c1f47c1
--- /dev/null
+++ b/drivers/clk/at91/clk-main.c
@@ -0,0 +1,576 @@
+/*
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define SLOW_CLOCK_FREQ		32768
+#define MAINF_DIV		16
+#define MAINFRDY_TIMEOUT	(((MAINF_DIV + 1) * SECOND) / \
+				 SLOW_CLOCK_FREQ)
+#define MAINF_LOOP_MIN_WAIT	(SECOND / SLOW_CLOCK_FREQ)
+#define MAINF_LOOP_MAX_WAIT	MAINFRDY_TIMEOUT
+
+#define MOR_KEY_MASK		(0xff << 16)
+
+struct clk_main_osc {
+	struct clk clk;
+	struct regmap *regmap;
+	const char *parent;
+};
+
+#define to_clk_main_osc(clk) container_of(clk, struct clk_main_osc, clk)
+
+struct clk_main_rc_osc {
+	struct clk clk;
+	struct regmap *regmap;
+	unsigned long frequency;
+};
+
+#define to_clk_main_rc_osc(clk) container_of(clk, struct clk_main_rc_osc, clk)
+
+struct clk_rm9200_main {
+	struct clk clk;
+	struct regmap *regmap;
+	const char *parent;
+};
+
+#define to_clk_rm9200_main(clk) container_of(clk, struct clk_rm9200_main, clk)
+
+struct clk_sam9x5_main {
+	struct clk clk;
+	struct regmap *regmap;
+	u8 parent;
+};
+
+#define to_clk_sam9x5_main(clk) container_of(clk, struct clk_sam9x5_main, clk)
+
+static inline bool clk_main_osc_ready(struct regmap *regmap)
+{
+	unsigned int status;
+
+	regmap_read(regmap, AT91_PMC_SR, &status);
+
+	return status & AT91_PMC_MOSCS;
+}
+
+static int clk_main_osc_enable(struct clk *clk)
+{
+	struct clk_main_osc *osc = to_clk_main_osc(clk);
+	struct regmap *regmap = osc->regmap;
+	u32 tmp;
+
+	regmap_read(regmap, AT91_CKGR_MOR, &tmp);
+	tmp &= ~MOR_KEY_MASK;
+
+	if (tmp & AT91_PMC_OSCBYPASS)
+		return 0;
+
+	if (!(tmp & AT91_PMC_MOSCEN)) {
+		tmp |= AT91_PMC_MOSCEN | AT91_PMC_KEY;
+		regmap_write(regmap, AT91_CKGR_MOR, tmp);
+	}
+
+	while (!clk_main_osc_ready(regmap))
+		barrier();
+
+	return 0;
+}
+
+static void clk_main_osc_disable(struct clk *clk)
+{
+	struct clk_main_osc *osc = to_clk_main_osc(clk);
+	struct regmap *regmap = osc->regmap;
+	u32 tmp;
+
+	regmap_read(regmap, AT91_CKGR_MOR, &tmp);
+	if (tmp & AT91_PMC_OSCBYPASS)
+		return;
+
+	if (!(tmp & AT91_PMC_MOSCEN))
+		return;
+
+	tmp &= ~(AT91_PMC_KEY | AT91_PMC_MOSCEN);
+	regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_KEY);
+}
+
+static int clk_main_osc_is_enabled(struct clk *clk)
+{
+	struct clk_main_osc *osc = to_clk_main_osc(clk);
+	struct regmap *regmap = osc->regmap;
+	u32 tmp, status;
+
+	regmap_read(regmap, AT91_CKGR_MOR, &tmp);
+	if (tmp & AT91_PMC_OSCBYPASS)
+		return 1;
+
+	regmap_read(regmap, AT91_PMC_SR, &status);
+
+	return (status & AT91_PMC_MOSCS) && (tmp & AT91_PMC_MOSCEN);
+}
+
+static const struct clk_ops main_osc_ops = {
+	.enable = clk_main_osc_enable,
+	.disable = clk_main_osc_disable,
+	.is_enabled = clk_main_osc_is_enabled,
+};
+
+static struct clk *
+at91_clk_register_main_osc(struct regmap *regmap,
+			   const char *name,
+			   const char *parent_name,
+			   bool bypass)
+{
+	struct clk_main_osc *osc;
+	int ret;
+
+	if (!name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	osc = xzalloc(sizeof(*osc));
+
+	osc->parent = parent_name;
+	osc->clk.name = name;
+	osc->clk.ops = &main_osc_ops;
+	osc->clk.parent_names = &osc->parent;
+	osc->clk.num_parents = 1;
+	osc->regmap = regmap;
+
+	if (bypass)
+		regmap_write_bits(regmap,
+				  AT91_CKGR_MOR, MOR_KEY_MASK |
+				  AT91_PMC_MOSCEN,
+				  AT91_PMC_OSCBYPASS | AT91_PMC_KEY);
+
+	ret = clk_register(&osc->clk);
+	if (ret) {
+		free(osc);
+		return ERR_PTR(ret);
+	}
+
+	return &osc->clk;
+}
+
+static int of_at91rm9200_clk_main_osc_setup(struct device_node *np)
+{
+	struct clk *clk;
+	const char *name = np->name;
+	const char *parent_name;
+	struct regmap *regmap;
+	bool bypass;
+
+	of_property_read_string(np, "clock-output-names", &name);
+	bypass = of_property_read_bool(np, "atmel,osc-bypass");
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	clk = at91_clk_register_main_osc(regmap, name, parent_name, bypass);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91rm9200_clk_main_osc, "atmel,at91rm9200-clk-main-osc",
+	       of_at91rm9200_clk_main_osc_setup);
+
+static bool clk_main_rc_osc_ready(struct regmap *regmap)
+{
+	unsigned int status;
+
+	regmap_read(regmap, AT91_PMC_SR, &status);
+
+	return status & AT91_PMC_MOSCRCS;
+}
+
+static int clk_main_rc_osc_enable(struct clk *clk)
+{
+	struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk);
+	struct regmap *regmap = osc->regmap;
+	unsigned int mor;
+
+	regmap_read(regmap, AT91_CKGR_MOR, &mor);
+
+	if (!(mor & AT91_PMC_MOSCRCEN))
+		regmap_write_bits(regmap, AT91_CKGR_MOR,
+				  MOR_KEY_MASK | AT91_PMC_MOSCRCEN,
+				  AT91_PMC_MOSCRCEN | AT91_PMC_KEY);
+
+	while (!clk_main_rc_osc_ready(regmap))
+		barrier();
+
+	return 0;
+}
+
+static void clk_main_rc_osc_disable(struct clk *clk)
+{
+	struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk);
+	struct regmap *regmap = osc->regmap;
+	unsigned int mor;
+
+	regmap_read(regmap, AT91_CKGR_MOR, &mor);
+
+	if (!(mor & AT91_PMC_MOSCRCEN))
+		return;
+
+	regmap_write_bits(regmap, AT91_CKGR_MOR,
+			  MOR_KEY_MASK | AT91_PMC_MOSCRCEN, AT91_PMC_KEY);
+}
+
+static int clk_main_rc_osc_is_enabled(struct clk *clk)
+{
+	struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk);
+	struct regmap *regmap = osc->regmap;
+	unsigned int mor, status;
+
+	regmap_read(regmap, AT91_CKGR_MOR, &mor);
+	regmap_read(regmap, AT91_PMC_SR, &status);
+
+	return (mor & AT91_PMC_MOSCRCEN) && (status & AT91_PMC_MOSCRCS);
+}
+
+static unsigned long clk_main_rc_osc_recalc_rate(struct clk *clk,
+						 unsigned long parent_rate)
+{
+	struct clk_main_rc_osc *osc = to_clk_main_rc_osc(clk);
+
+	return osc->frequency;
+}
+
+static const struct clk_ops main_rc_osc_ops = {
+	.enable = clk_main_rc_osc_enable,
+	.disable = clk_main_rc_osc_disable,
+	.is_enabled = clk_main_rc_osc_is_enabled,
+	.recalc_rate = clk_main_rc_osc_recalc_rate,
+};
+
+static struct clk *
+at91_clk_register_main_rc_osc(struct regmap *regmap,
+			      const char *name,
+			      u32 frequency)
+{
+	int ret;
+	struct clk_main_rc_osc *osc;
+
+	if (!name || !frequency)
+		return ERR_PTR(-EINVAL);
+
+	osc = xzalloc(sizeof(*osc));
+
+	osc->clk.name = name;
+	osc->clk.ops = &main_rc_osc_ops;
+	osc->clk.parent_names = NULL;
+	osc->clk.num_parents = 0;
+
+	osc->regmap = regmap;
+	osc->frequency = frequency;
+
+	ret = clk_register(&osc->clk);
+	if (ret) {
+		kfree(osc);
+		return ERR_PTR(ret);
+	}
+
+	return &osc->clk;
+}
+
+static int of_at91sam9x5_clk_main_rc_osc_setup(struct device_node *np)
+{
+	struct clk *clk;
+	u32 frequency = 0;
+	const char *name = np->name;
+	struct regmap *regmap;
+
+	of_property_read_string(np, "clock-output-names", &name);
+	of_property_read_u32(np, "clock-frequency", &frequency);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	clk = at91_clk_register_main_rc_osc(regmap, name, frequency);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_main_rc_osc, "atmel,at91sam9x5-clk-main-rc-osc",
+	       of_at91sam9x5_clk_main_rc_osc_setup);
+
+
+static int clk_main_probe_frequency(struct regmap *regmap)
+{
+	unsigned int mcfr;
+	uint64_t start = get_time_ns();
+
+	do {
+		regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);
+		if (mcfr & AT91_PMC_MAINRDY)
+			return 0;
+	} while (!is_timeout(start, MAINFRDY_TIMEOUT *  USECOND));
+
+	return -ETIMEDOUT;
+}
+
+static unsigned long clk_main_recalc_rate(struct regmap *regmap,
+					  unsigned long parent_rate)
+{
+	unsigned int mcfr;
+
+	if (parent_rate)
+		return parent_rate;
+
+	pr_warn("Main crystal frequency not set, using approximate value\n");
+	regmap_read(regmap, AT91_CKGR_MCFR, &mcfr);
+	if (!(mcfr & AT91_PMC_MAINRDY))
+		return 0;
+
+	return ((mcfr & AT91_PMC_MAINF) * SLOW_CLOCK_FREQ) / MAINF_DIV;
+}
+
+static int clk_rm9200_main_enable(struct clk *clk)
+{
+	struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk);
+
+	return clk_main_probe_frequency(clkmain->regmap);
+}
+
+static int clk_rm9200_main_is_enabled(struct clk *clk)
+{
+	struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk);
+	unsigned int status;
+
+	regmap_read(clkmain->regmap, AT91_CKGR_MCFR, &status);
+
+	return status & AT91_PMC_MAINRDY ? 1 : 0;
+}
+
+static unsigned long clk_rm9200_main_recalc_rate(struct clk *clk,
+						 unsigned long parent_rate)
+{
+	struct clk_rm9200_main *clkmain = to_clk_rm9200_main(clk);
+
+	return clk_main_recalc_rate(clkmain->regmap, parent_rate);
+}
+
+static const struct clk_ops rm9200_main_ops = {
+	.enable = clk_rm9200_main_enable,
+	.is_enabled = clk_rm9200_main_is_enabled,
+	.recalc_rate = clk_rm9200_main_recalc_rate,
+};
+
+static struct clk *
+at91_clk_register_rm9200_main(struct regmap *regmap,
+			      const char *name,
+			      const char *parent_name)
+{
+	int ret;
+	struct clk_rm9200_main *clkmain;
+
+	if (!name)
+		return ERR_PTR(-EINVAL);
+
+	if (!parent_name)
+		return ERR_PTR(-EINVAL);
+
+	clkmain = xzalloc(sizeof(*clkmain));
+
+	clkmain->clk.name = name;
+	clkmain->clk.ops = &rm9200_main_ops;
+	clkmain->clk.parent_names = &clkmain->parent;
+	clkmain->clk.num_parents = 1;
+	clkmain->regmap = regmap;
+
+	ret = clk_register(&clkmain->clk);
+	if (ret) {
+		kfree(clkmain);
+		return ERR_PTR(ret);
+	}
+
+	return &clkmain->clk;
+}
+
+static int of_at91rm9200_clk_main_setup(struct device_node *np)
+{
+	struct clk *clk;
+	const char *parent_name;
+	const char *name = np->name;
+	struct regmap *regmap;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	clk = at91_clk_register_rm9200_main(regmap, name, parent_name);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91rm9200_clk_main, "atmel,at91rm9200-clk-main",
+	       of_at91rm9200_clk_main_setup);
+
+static inline bool clk_sam9x5_main_ready(struct regmap *regmap)
+{
+	unsigned int status;
+
+	regmap_read(regmap, AT91_PMC_SR, &status);
+
+	return status & AT91_PMC_MOSCSELS ? 1 : 0;
+}
+
+static int clk_sam9x5_main_enable(struct clk *clk)
+{
+	struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+	struct regmap *regmap = clkmain->regmap;
+
+	while (!clk_sam9x5_main_ready(regmap))
+		barrier();
+
+	return clk_main_probe_frequency(regmap);
+}
+
+static int clk_sam9x5_main_is_enabled(struct clk *clk)
+{
+	struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+
+	return clk_sam9x5_main_ready(clkmain->regmap);
+}
+
+static unsigned long clk_sam9x5_main_recalc_rate(struct clk *clk,
+						 unsigned long parent_rate)
+{
+	struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+
+	return clk_main_recalc_rate(clkmain->regmap, parent_rate);
+}
+
+static int clk_sam9x5_main_set_parent(struct clk *clk, u8 index)
+{
+	struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+	struct regmap *regmap = clkmain->regmap;
+	unsigned int tmp;
+
+	if (index > 1)
+		return -EINVAL;
+
+	regmap_read(regmap, AT91_CKGR_MOR, &tmp);
+	tmp &= ~MOR_KEY_MASK;
+
+	if (index && !(tmp & AT91_PMC_MOSCSEL))
+		regmap_write(regmap, AT91_CKGR_MOR, tmp | AT91_PMC_MOSCSEL);
+	else if (!index && (tmp & AT91_PMC_MOSCSEL))
+		regmap_write(regmap, AT91_CKGR_MOR, tmp & ~AT91_PMC_MOSCSEL);
+
+	while (!clk_sam9x5_main_ready(regmap))
+		barrier();
+
+	return 0;
+}
+
+static int clk_sam9x5_main_get_parent(struct clk *clk)
+{
+	struct clk_sam9x5_main *clkmain = to_clk_sam9x5_main(clk);
+	unsigned int status;
+
+	regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status);
+
+	return status & AT91_PMC_MOSCEN ? 1 : 0;
+}
+
+static const struct clk_ops sam9x5_main_ops = {
+	.enable = clk_sam9x5_main_enable,
+	.is_enabled = clk_sam9x5_main_is_enabled,
+	.recalc_rate = clk_sam9x5_main_recalc_rate,
+	.set_parent = clk_sam9x5_main_set_parent,
+	.get_parent = clk_sam9x5_main_get_parent,
+};
+
+static struct clk *
+at91_clk_register_sam9x5_main(struct regmap *regmap,
+			      const char *name,
+			      const char **parent_names,
+			      int num_parents)
+{
+	int ret;
+	unsigned int status;
+	size_t parents_array_size;
+	struct clk_sam9x5_main *clkmain;
+
+	if (!name)
+		return ERR_PTR(-EINVAL);
+
+	if (!parent_names || !num_parents)
+		return ERR_PTR(-EINVAL);
+
+	clkmain = xzalloc(sizeof(*clkmain));
+
+	clkmain->clk.name = name;
+	clkmain->clk.ops = &sam9x5_main_ops;
+	parents_array_size = num_parents * sizeof (clkmain->clk.parent_names[0]);
+	clkmain->clk.parent_names = xzalloc(parents_array_size);
+	memcpy(clkmain->clk.parent_names, parent_names, parents_array_size);
+	clkmain->clk.num_parents = num_parents;
+	
+	/* init.flags = CLK_SET_PARENT_GATE; */
+
+	clkmain->regmap = regmap;
+	regmap_read(clkmain->regmap, AT91_CKGR_MOR, &status);
+	clkmain->parent = status & AT91_PMC_MOSCEN ? 1 : 0;
+
+	ret = clk_register(&clkmain->clk);
+	if (ret) {
+		kfree(clkmain);
+		return ERR_PTR(ret);
+	}
+
+	return &clkmain->clk;
+}
+
+static int of_at91sam9x5_clk_main_setup(struct device_node *np)
+{
+	struct clk *clk;
+	const char *parent_names[2];
+	unsigned int num_parents;
+	const char *name = np->name;
+	struct regmap *regmap;
+
+	num_parents = of_clk_get_parent_count(np);
+	if (num_parents == 0 || num_parents > 2)
+		return -EINVAL;
+
+	of_clk_parent_fill(np, parent_names, num_parents);
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	clk = at91_clk_register_sam9x5_main(regmap, name, parent_names,
+					    num_parents);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_main, "atmel,at91sam9x5-clk-main",
+	       of_at91sam9x5_clk_main_setup);
diff --git a/drivers/clk/at91/clk-master.c b/drivers/clk/at91/clk-master.c
new file mode 100644
index 0000000..b3a50ce
--- /dev/null
+++ b/drivers/clk/at91/clk-master.c
@@ -0,0 +1,245 @@
+/*
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define MASTER_SOURCE_MAX	4
+
+#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_characteristics {
+	struct clk_range output;
+	u32 divisors[4];
+	u8 have_div3_pres;
+};
+
+struct clk_master_layout {
+	u32 mask;
+	u8 pres_shift;
+};
+
+#define to_clk_master(clk) container_of(clk, struct clk_master, clk)
+
+struct clk_master {
+	struct clk clk;
+	struct regmap *regmap;
+	const struct clk_master_layout *layout;
+	const struct clk_master_characteristics *characteristics;
+	const char *parents[MASTER_SOURCE_MAX];
+};
+
+static inline bool clk_master_ready(struct regmap *regmap)
+{
+	unsigned int status;
+
+	regmap_read(regmap, AT91_PMC_SR, &status);
+
+	return status & AT91_PMC_MCKRDY ? 1 : 0;
+}
+
+static int clk_master_enable(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+
+	while (!clk_master_ready(master->regmap))
+		barrier();
+
+	return 0;
+}
+
+static int clk_master_is_enabled(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+
+	return clk_master_ready(master->regmap);
+}
+
+static unsigned long clk_master_recalc_rate(struct clk *clk,
+					    unsigned long parent_rate)
+{
+	u8 pres;
+	u8 div;
+	unsigned long rate = parent_rate;
+	struct clk_master *master = to_clk_master(clk);
+	const struct clk_master_layout *layout = master->layout;
+	const struct clk_master_characteristics *characteristics =
+						master->characteristics;
+	unsigned int mckr;
+
+	regmap_read(master->regmap, AT91_PMC_MCKR, &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 int clk_master_get_parent(struct clk *clk)
+{
+	struct clk_master *master = to_clk_master(clk);
+	unsigned int mckr;
+
+	regmap_read(master->regmap, AT91_PMC_MCKR, &mckr);
+
+	return mckr & AT91_PMC_CSS;
+}
+
+static const struct clk_ops master_ops = {
+	.enable = clk_master_enable,
+	.is_enabled = clk_master_is_enabled,
+	.recalc_rate = clk_master_recalc_rate,
+	.get_parent = clk_master_get_parent,
+};
+
+static struct clk *
+at91_clk_register_master(struct regmap *regmap,
+			 const char *name, int num_parents,
+			 const char **parent_names,
+			 const struct clk_master_layout *layout,
+			 const struct clk_master_characteristics *characteristics)
+{
+	int ret;
+	const size_t parent_names_size = num_parents * sizeof(parent_names[0]);
+	struct clk_master *master;
+
+	if (!name || !num_parents || !parent_names)
+		return ERR_PTR(-EINVAL);
+
+	master = xzalloc(sizeof(*master));
+
+	master->clk.name = name;
+	master->clk.ops = &master_ops;
+	memcpy(master->parents, parent_names, parent_names_size);
+	master->clk.parent_names = master->parents;
+	master->clk.num_parents = num_parents;
+
+	master->layout = layout;
+	master->characteristics = characteristics;
+	master->regmap = regmap;
+
+	ret = clk_register(&master->clk);
+	if (ret) {
+		kfree(master);
+		return ERR_PTR(ret);
+	}
+
+	return &master->clk;
+}
+
+
+static const struct clk_master_layout at91rm9200_master_layout = {
+	.mask = 0x31F,
+	.pres_shift = 2,
+};
+
+static const struct clk_master_layout at91sam9x5_master_layout = {
+	.mask = 0x373,
+	.pres_shift = 4,
+};
+
+
+static struct clk_master_characteristics *
+of_at91_clk_master_get_characteristics(struct device_node *np)
+{
+	struct clk_master_characteristics *characteristics;
+
+	characteristics = xzalloc(sizeof(*characteristics));
+
+	if (of_at91_get_clk_range(np, "atmel,clk-output-range", &characteristics->output))
+		goto out_free_characteristics;
+
+	of_property_read_u32_array(np, "atmel,clk-divisors",
+				   characteristics->divisors, 4);
+
+	characteristics->have_div3_pres =
+		of_property_read_bool(np, "atmel,master-clk-have-div3-pres");
+
+	return characteristics;
+
+out_free_characteristics:
+	kfree(characteristics);
+	return NULL;
+}
+
+static int
+of_at91_clk_master_setup(struct device_node *np,
+			 const struct clk_master_layout *layout)
+{
+	struct clk *clk;
+	unsigned int num_parents;
+	const char *parent_names[MASTER_SOURCE_MAX];
+	const char *name = np->name;
+	struct clk_master_characteristics *characteristics;
+	struct regmap *regmap;
+
+	num_parents = of_clk_get_parent_count(np);
+	if (num_parents == 0 || num_parents > MASTER_SOURCE_MAX)
+		return -EINVAL;
+
+	of_clk_parent_fill(np, parent_names, num_parents);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	characteristics = of_at91_clk_master_get_characteristics(np);
+	if (!characteristics)
+		return -EINVAL;
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	clk = at91_clk_register_master(regmap, name, num_parents,
+				       parent_names, layout,
+				       characteristics);
+	if (IS_ERR(clk)) {
+		kfree(characteristics);
+		return PTR_ERR(clk);
+	}
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static void __init of_at91rm9200_clk_master_setup(struct device_node *np)
+{
+	of_at91_clk_master_setup(np, &at91rm9200_master_layout);
+}
+CLK_OF_DECLARE(at91rm9200_clk_master, "atmel,at91rm9200-clk-master",
+	       of_at91rm9200_clk_master_setup);
+
+static void __init of_at91sam9x5_clk_master_setup(struct device_node *np)
+{
+	of_at91_clk_master_setup(np, &at91sam9x5_master_layout);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_master, "atmel,at91sam9x5-clk-master",
+	       of_at91sam9x5_clk_master_setup);
diff --git a/drivers/clk/at91/clk-peripheral.c b/drivers/clk/at91/clk-peripheral.c
new file mode 100644
index 0000000..4a76c46
--- /dev/null
+++ b/drivers/clk/at91/clk-peripheral.c
@@ -0,0 +1,430 @@
+/*
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define PERIPHERAL_MAX		64
+
+#define PERIPHERAL_AT91RM9200	0
+#define PERIPHERAL_AT91SAM9X5	1
+
+#define PERIPHERAL_ID_MIN	2
+#define PERIPHERAL_ID_MAX	31
+#define PERIPHERAL_MASK(id)	(1 << ((id) & PERIPHERAL_ID_MAX))
+
+#define PERIPHERAL_RSHIFT_MASK	0x3
+#define PERIPHERAL_RSHIFT(val)	(((val) >> 16) & PERIPHERAL_RSHIFT_MASK)
+
+#define PERIPHERAL_MAX_SHIFT	3
+
+struct clk_peripheral {
+	struct clk clk;
+	struct regmap *regmap;
+	u32 id;
+	const char *parent;
+};
+
+#define to_clk_peripheral(clk) container_of(clk, struct clk_peripheral, clk)
+
+struct clk_sam9x5_peripheral {
+	struct clk clk;
+	struct regmap *regmap;
+	struct clk_range range;
+	u32 id;
+	u32 div;
+	bool auto_div;
+	const char *parent;
+};
+
+#define to_clk_sam9x5_peripheral(clk) \
+	container_of(clk, 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;
+	regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
+
+	return 0;
+}
+
+static void 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;
+	if (id > PERIPHERAL_ID_MAX)
+		offset = AT91_PMC_PCDR1;
+	regmap_write(periph->regmap, offset, PERIPHERAL_MASK(id));
+}
+
+static int clk_peripheral_is_enabled(struct clk *clk)
+{
+	struct clk_peripheral *periph = to_clk_peripheral(clk);
+	int offset = AT91_PMC_PCSR;
+	unsigned int status;
+	u32 id = periph->id;
+
+	if (id < PERIPHERAL_ID_MIN)
+		return 1;
+	if (id > PERIPHERAL_ID_MAX)
+		offset = AT91_PMC_PCSR1;
+	regmap_read(periph->regmap, offset, &status);
+
+	return status & PERIPHERAL_MASK(id) ? 1 : 0;
+}
+
+static const struct clk_ops peripheral_ops = {
+	.enable = clk_peripheral_enable,
+	.disable = clk_peripheral_disable,
+	.is_enabled = clk_peripheral_is_enabled,
+};
+
+static struct clk *
+at91_clk_register_peripheral(struct regmap *regmap, const char *name,
+			     const char *parent_name, u32 id)
+{
+	int ret;
+	struct clk_peripheral *periph;
+
+	if (!name || !parent_name || id > PERIPHERAL_ID_MAX)
+		return ERR_PTR(-EINVAL);
+
+	periph = xzalloc(sizeof(*periph));
+
+	periph->clk.name = name;
+	periph->clk.ops = &peripheral_ops;
+
+	if (parent_name) {
+		periph->parent = parent_name;
+		periph->clk.parent_names = &periph->parent;
+		periph->clk.num_parents = 1;
+	}
+
+	periph->id = id;
+	periph->regmap = regmap;
+
+	ret = clk_register(&periph->clk);
+	if (ret) {
+		kfree(periph);
+		return ERR_PTR(ret);
+	}
+
+	return &periph->clk;
+}
+
+static void clk_sam9x5_peripheral_autodiv(struct clk_sam9x5_peripheral *periph)
+{
+	struct clk *parent;
+	unsigned long parent_rate;
+	int shift = 0;
+
+	if (!periph->auto_div)
+		return;
+
+	if (periph->range.max) {
+		parent = clk_get_parent(&periph->clk);
+		parent_rate = clk_get_rate(parent);
+		if (!parent_rate)
+			return;
+
+		for (; shift < PERIPHERAL_MAX_SHIFT; shift++) {
+			if (parent_rate >> shift <= periph->range.max)
+				break;
+		}
+	}
+
+	periph->auto_div = false;
+	periph->div = shift;
+}
+
+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;
+
+	regmap_write(periph->regmap, AT91_PMC_PCR,
+		     (periph->id & AT91_PMC_PCR_PID_MASK));
+	regmap_write_bits(periph->regmap, AT91_PMC_PCR,
+			  AT91_PMC_PCR_DIV_MASK | AT91_PMC_PCR_CMD |
+			  AT91_PMC_PCR_EN,
+			  AT91_PMC_PCR_DIV(periph->div) |
+			  AT91_PMC_PCR_CMD |
+			  AT91_PMC_PCR_EN);
+
+	return 0;
+}
+
+static void clk_sam9x5_peripheral_disable(struct clk *clk)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+
+	if (periph->id < PERIPHERAL_ID_MIN)
+		return;
+
+	regmap_write(periph->regmap, AT91_PMC_PCR,
+		     (periph->id & AT91_PMC_PCR_PID_MASK));
+	regmap_write_bits(periph->regmap, AT91_PMC_PCR,
+			  AT91_PMC_PCR_EN | AT91_PMC_PCR_CMD,
+			  AT91_PMC_PCR_CMD);
+}
+
+static int clk_sam9x5_peripheral_is_enabled(struct clk *clk)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+	unsigned int status;
+
+	if (periph->id < PERIPHERAL_ID_MIN)
+		return 1;
+
+	regmap_write(periph->regmap, AT91_PMC_PCR,
+		     (periph->id & AT91_PMC_PCR_PID_MASK));
+	regmap_read(periph->regmap, AT91_PMC_PCR, &status);
+
+	return status & AT91_PMC_PCR_EN ? 1 : 0;
+}
+
+static unsigned long
+clk_sam9x5_peripheral_recalc_rate(struct clk *clk,
+				  unsigned long parent_rate)
+{
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+	unsigned int status;
+
+	if (periph->id < PERIPHERAL_ID_MIN)
+		return parent_rate;
+
+	regmap_write(periph->regmap, AT91_PMC_PCR,
+		     (periph->id & AT91_PMC_PCR_PID_MASK));
+	regmap_read(periph->regmap, AT91_PMC_PCR, &status);
+
+	if (status & AT91_PMC_PCR_EN) {
+		periph->div = PERIPHERAL_RSHIFT(status);
+		periph->auto_div = false;
+	} else {
+		clk_sam9x5_peripheral_autodiv(periph);
+	}
+
+	return parent_rate >> periph->div;
+}
+
+static long clk_sam9x5_peripheral_round_rate(struct clk *clk,
+					     unsigned long rate,
+					     unsigned long *parent_rate)
+{
+	int shift = 0;
+	unsigned long best_rate;
+	unsigned long best_diff;
+	unsigned long cur_rate = *parent_rate;
+	unsigned long cur_diff;
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+
+	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max)
+		return *parent_rate;
+
+	if (periph->range.max) {
+		for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
+			cur_rate = *parent_rate >> shift;
+			if (cur_rate <= periph->range.max)
+				break;
+		}
+	}
+
+	if (rate >= cur_rate)
+		return cur_rate;
+
+	best_diff = cur_rate - rate;
+	best_rate = cur_rate;
+	for (; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
+		cur_rate = *parent_rate >> shift;
+		if (cur_rate < rate)
+			cur_diff = rate - cur_rate;
+		else
+			cur_diff = cur_rate - rate;
+
+		if (cur_diff < best_diff) {
+			best_diff = cur_diff;
+			best_rate = cur_rate;
+		}
+
+		if (!best_diff || cur_rate < rate)
+			break;
+	}
+
+	return best_rate;
+}
+
+static int clk_sam9x5_peripheral_set_rate(struct clk *clk,
+					  unsigned long rate,
+					  unsigned long parent_rate)
+{
+	int shift;
+	struct clk_sam9x5_peripheral *periph = to_clk_sam9x5_peripheral(clk);
+	if (periph->id < PERIPHERAL_ID_MIN || !periph->range.max) {
+		if (parent_rate == rate)
+			return 0;
+		else
+			return -EINVAL;
+	}
+
+	if (periph->range.max && rate > periph->range.max)
+		return -EINVAL;
+
+	for (shift = 0; shift <= PERIPHERAL_MAX_SHIFT; shift++) {
+		if (parent_rate >> shift == rate) {
+			periph->auto_div = false;
+			periph->div = shift;
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static const struct clk_ops sam9x5_peripheral_ops = {
+	.enable = clk_sam9x5_peripheral_enable,
+	.disable = clk_sam9x5_peripheral_disable,
+	.is_enabled = clk_sam9x5_peripheral_is_enabled,
+	.recalc_rate = clk_sam9x5_peripheral_recalc_rate,
+	.round_rate = clk_sam9x5_peripheral_round_rate,
+	.set_rate = clk_sam9x5_peripheral_set_rate,
+};
+
+static struct clk *
+at91_clk_register_sam9x5_peripheral(struct regmap *regmap,
+				    const char *name, const char *parent_name,
+				    u32 id, const struct clk_range *range)
+{
+	int ret;
+	struct clk_sam9x5_peripheral *periph;
+
+	if (!name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	periph = xzalloc(sizeof(*periph));
+
+	periph->clk.name = name;
+	periph->clk.ops = &sam9x5_peripheral_ops;
+
+	if (parent_name) {
+		periph->parent = parent_name;
+		periph->clk.parent_names = &periph->parent;
+		periph->clk.num_parents = 1;
+	}
+
+	periph->id = id;
+	periph->div = 0;
+	periph->regmap = regmap;
+	periph->auto_div = true;
+	periph->range = *range;
+
+	ret = clk_register(&periph->clk);
+	if (ret) {
+		kfree(periph);
+		return ERR_PTR(ret);
+	}
+
+	clk_sam9x5_peripheral_autodiv(periph);
+
+	return &periph->clk;
+}
+
+static int
+of_at91_clk_periph_setup(struct device_node *np, u8 type)
+{
+	int num;
+	u32 id;
+	struct clk *clk;
+	const char *parent_name;
+	const char *name;
+	struct device_node *periphclknp;
+	struct regmap *regmap;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+	if (!parent_name)
+		return -ENOENT;
+
+	num = of_get_child_count(np);
+	if (!num || num > PERIPHERAL_MAX)
+		return -EINVAL;
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	for_each_child_of_node(np, periphclknp) {
+		if (of_property_read_u32(periphclknp, "reg", &id))
+			continue;
+
+		if (id >= PERIPHERAL_MAX)
+			continue;
+
+		if (of_property_read_string(np, "clock-output-names", &name))
+			name = periphclknp->name;
+
+		if (type == PERIPHERAL_AT91RM9200) {
+			clk = at91_clk_register_peripheral(regmap, name,
+							   parent_name, id);
+		} else {
+			struct clk_range range = CLK_RANGE(0, 0);
+
+			of_at91_get_clk_range(periphclknp,
+					      "atmel,clk-output-range",
+					      &range);
+
+			clk = at91_clk_register_sam9x5_peripheral(regmap,
+								  name,
+								  parent_name,
+								  id, &range);
+		}
+
+		if (IS_ERR(clk))
+			continue;
+
+		of_clk_add_provider(periphclknp, of_clk_src_simple_get, clk);
+	}
+
+	return 0;
+}
+
+static int of_at91rm9200_clk_periph_setup(struct device_node *np)
+{
+	return of_at91_clk_periph_setup(np, PERIPHERAL_AT91RM9200);
+}
+CLK_OF_DECLARE(at91rm9200_clk_periph, "atmel,at91rm9200-clk-peripheral",
+	       of_at91rm9200_clk_periph_setup);
+
+static int of_at91sam9x5_clk_periph_setup(struct device_node *np)
+{
+	return of_at91_clk_periph_setup(np, PERIPHERAL_AT91SAM9X5);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_periph, "atmel,at91sam9x5-clk-peripheral",
+	       of_at91sam9x5_clk_periph_setup);
+
diff --git a/drivers/clk/at91/clk-pll.c b/drivers/clk/at91/clk-pll.c
new file mode 100644
index 0000000..cf38742
--- /dev/null
+++ b/drivers/clk/at91/clk-pll.c
@@ -0,0 +1,516 @@
+/*
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define PLL_STATUS_MASK(id)	(1 << (1 + (id)))
+#define PLL_REG(id)		(AT91_CKGR_PLLAR + ((id) * 4))
+#define PLL_DIV_MASK		0xff
+#define PLL_DIV_MAX		PLL_DIV_MASK
+#define PLL_DIV(reg)		((reg) & PLL_DIV_MASK)
+#define PLL_MUL(reg, layout)	(((reg) >> (layout)->mul_shift) & \
+				 (layout)->mul_mask)
+#define PLL_MUL_MIN		2
+#define PLL_MUL_MASK(layout)	((layout)->mul_mask)
+#define PLL_MUL_MAX(layout)	(PLL_MUL_MASK(layout) + 1)
+#define PLL_ICPR_SHIFT(id)	((id) * 16)
+#define PLL_ICPR_MASK(id)	(0xffff << PLL_ICPR_SHIFT(id))
+#define PLL_MAX_COUNT		0x3f
+#define PLL_COUNT_SHIFT		8
+#define PLL_OUT_SHIFT		14
+#define PLL_MAX_ID		1
+
+struct clk_pll_characteristics {
+	struct clk_range input;
+	int num_output;
+	struct clk_range *output;
+	u16 *icpll;
+	u8 *out;
+};
+
+struct clk_pll_layout {
+	u32 pllr_mask;
+	u16 mul_mask;
+	u8 mul_shift;
+};
+
+#define to_clk_pll(clk) container_of(clk, struct clk_pll, clk)
+
+struct clk_pll {
+	struct clk clk;
+	struct regmap *regmap;
+	u8 id;
+	u8 div;
+	u8 range;
+	u16 mul;
+	const struct clk_pll_layout *layout;
+	const struct clk_pll_characteristics *characteristics;
+	const char *parent;
+};
+
+static inline bool clk_pll_ready(struct regmap *regmap, int id)
+{
+	unsigned int status;
+
+	regmap_read(regmap, AT91_PMC_SR, &status);
+
+	return status & PLL_STATUS_MASK(id) ? 1 : 0;
+}
+
+static int clk_pll_enable(struct clk *clk)
+{
+	struct clk_pll *pll = to_clk_pll(clk);
+	struct regmap *regmap = pll->regmap;
+	const struct clk_pll_layout *layout = pll->layout;
+	const struct clk_pll_characteristics *characteristics =
+							pll->characteristics;
+	u8 id = pll->id;
+	u32 mask = PLL_STATUS_MASK(id);
+	int offset = PLL_REG(id);
+	u8 out = 0;
+	unsigned int pllr;
+	unsigned int status;
+	u8 div;
+	u16 mul;
+
+	regmap_read(regmap, offset, &pllr);
+	div = PLL_DIV(pllr);
+	mul = PLL_MUL(pllr, layout);
+
+	regmap_read(regmap, AT91_PMC_SR, &status);
+	if ((status & mask) &&
+	    (div == pll->div && mul == pll->mul))
+		return 0;
+
+	if (characteristics->out)
+		out = characteristics->out[pll->range];
+
+	if (characteristics->icpll)
+		regmap_write_bits(regmap, AT91_PMC_PLLICPR, PLL_ICPR_MASK(id),
+			characteristics->icpll[pll->range] << PLL_ICPR_SHIFT(id));
+
+	regmap_write_bits(regmap, offset, layout->pllr_mask,
+			  pll->div | (PLL_MAX_COUNT << PLL_COUNT_SHIFT) |
+			  (out << PLL_OUT_SHIFT) |
+			  ((pll->mul & layout->mul_mask) << layout->mul_shift));
+
+	while (!clk_pll_ready(regmap, pll->id))
+		barrier();
+
+	return 0;
+}
+
+static int clk_pll_is_enabled(struct clk *clk)
+{
+	struct clk_pll *pll = to_clk_pll(clk);
+
+	return clk_pll_ready(pll->regmap, pll->id);
+}
+
+static void clk_pll_disable(struct clk *clk)
+{
+	struct clk_pll *pll = to_clk_pll(clk);
+	unsigned int mask = pll->layout->pllr_mask;
+
+	regmap_write_bits(pll->regmap, PLL_REG(pll->id), mask, ~mask);
+}
+
+static unsigned long clk_pll_recalc_rate(struct clk *clk,
+					 unsigned long parent_rate)
+{
+	struct clk_pll *pll = to_clk_pll(clk);
+	unsigned int pllr;
+	u16 mul;
+	u8 div;
+
+	regmap_read(pll->regmap, PLL_REG(pll->id), &pllr);
+
+	div = PLL_DIV(pllr);
+	mul = PLL_MUL(pllr, pll->layout);
+
+	if (!div || !mul)
+		return 0;
+
+	return (parent_rate / div) * (mul + 1);
+}
+
+static long clk_pll_get_best_div_mul(struct clk_pll *pll, unsigned long rate,
+				     unsigned long parent_rate,
+				     u32 *div, u32 *mul,
+				     u32 *index) {
+	const struct clk_pll_layout *layout = pll->layout;
+	const struct clk_pll_characteristics *characteristics =
+							pll->characteristics;
+	unsigned long bestremainder = ULONG_MAX;
+	unsigned long maxdiv, mindiv, tmpdiv;
+	long bestrate = -ERANGE;
+	unsigned long bestdiv;
+	unsigned long bestmul;
+	int i = 0;
+
+	/* Check if parent_rate is a valid input rate */
+	if (parent_rate < characteristics->input.min)
+		return -ERANGE;
+
+	/*
+	 * Calculate minimum divider based on the minimum multiplier, the
+	 * parent_rate and the requested rate.
+	 * Should always be 2 according to the input and output characteristics
+	 * of the PLL blocks.
+	 */
+	mindiv = (parent_rate * PLL_MUL_MIN) / rate;
+	if (!mindiv)
+		mindiv = 1;
+
+	if (parent_rate > characteristics->input.max) {
+		tmpdiv = DIV_ROUND_UP(parent_rate, characteristics->input.max);
+		if (tmpdiv > PLL_DIV_MAX)
+			return -ERANGE;
+
+		if (tmpdiv > mindiv)
+			mindiv = tmpdiv;
+	}
+
+	/*
+	 * Calculate the maximum divider which is limited by PLL register
+	 * layout (limited by the MUL or DIV field size).
+	 */
+	maxdiv = DIV_ROUND_UP(parent_rate * PLL_MUL_MAX(layout), rate);
+	if (maxdiv > PLL_DIV_MAX)
+		maxdiv = PLL_DIV_MAX;
+
+	/*
+	 * Iterate over the acceptable divider values to find the best
+	 * divider/multiplier pair (the one that generates the closest
+	 * rate to the requested one).
+	 */
+	for (tmpdiv = mindiv; tmpdiv <= maxdiv; tmpdiv++) {
+		unsigned long remainder;
+		unsigned long tmprate;
+		unsigned long tmpmul;
+
+		/*
+		 * Calculate the multiplier associated with the current
+		 * divider that provide the closest rate to the requested one.
+		 */
+		tmpmul = DIV_ROUND_CLOSEST(rate, parent_rate / tmpdiv);
+		tmprate = (parent_rate / tmpdiv) * tmpmul;
+		if (tmprate > rate)
+			remainder = tmprate - rate;
+		else
+			remainder = rate - tmprate;
+
+		/*
+		 * Compare the remainder with the best remainder found until
+		 * now and elect a new best multiplier/divider pair if the
+		 * current remainder is smaller than the best one.
+		 */
+		if (remainder < bestremainder) {
+			bestremainder = remainder;
+			bestdiv = tmpdiv;
+			bestmul = tmpmul;
+			bestrate = tmprate;
+		}
+
+		/*
+		 * We've found a perfect match!
+		 * Stop searching now and use this multiplier/divider pair.
+		 */
+		if (!remainder)
+			break;
+	}
+
+	/* We haven't found any multiplier/divider pair => return -ERANGE */
+	if (bestrate < 0)
+		return bestrate;
+
+	/* Check if bestrate is a valid output rate  */
+	for (i = 0; i < characteristics->num_output; i++) {
+		if (bestrate >= characteristics->output[i].min &&
+		    bestrate <= characteristics->output[i].max)
+			break;
+	}
+
+	if (i >= characteristics->num_output)
+		return -ERANGE;
+
+	if (div)
+		*div = bestdiv;
+	if (mul)
+		*mul = bestmul - 1;
+	if (index)
+		*index = i;
+
+	return bestrate;
+}
+
+static long clk_pll_round_rate(struct clk *clk, unsigned long rate,
+			       unsigned long *parent_rate)
+{
+	struct clk_pll *pll = to_clk_pll(clk);
+
+	return clk_pll_get_best_div_mul(pll, rate, *parent_rate,
+					NULL, NULL, NULL);
+}
+
+static int clk_pll_set_rate(struct clk *clk, unsigned long rate,
+			    unsigned long parent_rate)
+{
+	struct clk_pll *pll = to_clk_pll(clk);
+	long ret;
+	u32 div;
+	u32 mul;
+	u32 index;
+
+	ret = clk_pll_get_best_div_mul(pll, rate, parent_rate,
+				       &div, &mul, &index);
+	if (ret < 0)
+		return ret;
+
+	pll->range = index;
+	pll->div = div;
+	pll->mul = mul;
+
+	return 0;
+}
+
+static const struct clk_ops pll_ops = {
+	.enable = clk_pll_enable,
+	.disable = clk_pll_disable,
+	.is_enabled = clk_pll_is_enabled,
+	.recalc_rate = clk_pll_recalc_rate,
+	.round_rate = clk_pll_round_rate,
+	.set_rate = clk_pll_set_rate,
+};
+
+static struct clk *
+at91_clk_register_pll(struct regmap *regmap, const char *name,
+		      const char *parent_name, u8 id,
+		      const struct clk_pll_layout *layout,
+		      const struct clk_pll_characteristics *characteristics)
+{
+	struct clk_pll *pll;
+	int offset = PLL_REG(id);
+	unsigned int pllr;
+	int ret;
+
+	if (id > PLL_MAX_ID)
+		return ERR_PTR(-EINVAL);
+
+	pll = xzalloc(sizeof(*pll));
+
+	pll->parent = parent_name;
+	pll->clk.name = name;
+	pll->clk.ops = &pll_ops;
+	pll->clk.parent_names = &pll->parent;
+	pll->clk.num_parents = 1;
+
+	/* init.flags = CLK_SET_RATE_GATE; */
+
+	pll->id = id;
+	pll->layout = layout;
+	pll->characteristics = characteristics;
+	pll->regmap = regmap;
+	regmap_read(regmap, offset, &pllr);
+	pll->div = PLL_DIV(pllr);
+	pll->mul = PLL_MUL(pllr, layout);
+
+	ret = clk_register(&pll->clk);
+	if (ret) {
+		kfree(pll);
+		return ERR_PTR(ret);
+	}
+
+	return &pll->clk;
+}
+
+
+static const struct clk_pll_layout at91rm9200_pll_layout = {
+	.pllr_mask = 0x7FFFFFF,
+	.mul_shift = 16,
+	.mul_mask = 0x7FF,
+};
+
+static const struct clk_pll_layout at91sam9g45_pll_layout = {
+	.pllr_mask = 0xFFFFFF,
+	.mul_shift = 16,
+	.mul_mask = 0xFF,
+};
+
+static const struct clk_pll_layout at91sam9g20_pllb_layout = {
+	.pllr_mask = 0x3FFFFF,
+	.mul_shift = 16,
+	.mul_mask = 0x3F,
+};
+
+static const struct clk_pll_layout sama5d3_pll_layout = {
+	.pllr_mask = 0x1FFFFFF,
+	.mul_shift = 18,
+	.mul_mask = 0x7F,
+};
+
+
+static struct clk_pll_characteristics *
+of_at91_clk_pll_get_characteristics(struct device_node *np)
+{
+	int i;
+	int offset;
+	u32 tmp;
+	int num_output;
+	u32 num_cells;
+	struct clk_range input;
+	struct clk_range *output;
+	u8 *out = NULL;
+	u16 *icpll = NULL;
+	struct clk_pll_characteristics *characteristics;
+
+	if (of_at91_get_clk_range(np, "atmel,clk-input-range", &input))
+		return NULL;
+
+	if (of_property_read_u32(np, "#atmel,pll-clk-output-range-cells",
+				 &num_cells))
+		return NULL;
+
+	if (num_cells < 2 || num_cells > 4)
+		return NULL;
+
+	if (!of_get_property(np, "atmel,pll-clk-output-ranges", &tmp))
+		return NULL;
+	num_output = tmp / (sizeof(u32) * num_cells);
+
+	characteristics = xzalloc(sizeof(*characteristics));
+	output = xzalloc(sizeof(*output) * num_output);
+
+	if (num_cells > 2)
+		out = xzalloc(sizeof(*out) * num_output);
+
+	if (num_cells > 3)
+		icpll = xzalloc(sizeof(*icpll) * num_output);
+
+
+	for (i = 0; i < num_output; i++) {
+		offset = i * num_cells;
+		if (of_property_read_u32_index(np,
+					       "atmel,pll-clk-output-ranges",
+					       offset, &tmp))
+			goto out_free_output;
+		output[i].min = tmp;
+		if (of_property_read_u32_index(np,
+					       "atmel,pll-clk-output-ranges",
+					       offset + 1, &tmp))
+			goto out_free_output;
+		output[i].max = tmp;
+
+		if (num_cells == 2)
+			continue;
+
+		if (of_property_read_u32_index(np,
+					       "atmel,pll-clk-output-ranges",
+					       offset + 2, &tmp))
+			goto out_free_output;
+		out[i] = tmp;
+
+		if (num_cells == 3)
+			continue;
+
+		if (of_property_read_u32_index(np,
+					       "atmel,pll-clk-output-ranges",
+					       offset + 3, &tmp))
+			goto out_free_output;
+		icpll[i] = tmp;
+	}
+
+	characteristics->input = input;
+	characteristics->num_output = num_output;
+	characteristics->output = output;
+	characteristics->out = out;
+	characteristics->icpll = icpll;
+	return characteristics;
+
+out_free_output:
+	kfree(icpll);
+	kfree(out);
+	kfree(output);
+	kfree(characteristics);
+	return NULL;
+}
+
+static int
+of_at91_clk_pll_setup(struct device_node *np,
+		      const struct clk_pll_layout *layout)
+{
+	u32 id;
+	struct clk *clk;
+	struct regmap *regmap;
+	const char *parent_name;
+	const char *name = np->name;
+	struct clk_pll_characteristics *characteristics;
+
+	if (of_property_read_u32(np, "reg", &id))
+		return -EINVAL;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	characteristics = of_at91_clk_pll_get_characteristics(np);
+	if (!characteristics)
+		return -EINVAL;
+
+	clk = at91_clk_register_pll(regmap, name, parent_name, id, layout,
+				    characteristics);
+	if (IS_ERR(clk)) {
+		kfree(characteristics);
+		return PTR_ERR(clk);
+	}
+	
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static int of_at91rm9200_clk_pll_setup(struct device_node *np)
+{
+	return of_at91_clk_pll_setup(np, &at91rm9200_pll_layout);
+}
+CLK_OF_DECLARE(at91rm9200_clk_pll, "atmel,at91rm9200-clk-pll",
+	       of_at91rm9200_clk_pll_setup);
+
+static int of_at91sam9g45_clk_pll_setup(struct device_node *np)
+{
+	return of_at91_clk_pll_setup(np, &at91sam9g45_pll_layout);
+}
+CLK_OF_DECLARE(at91sam9g45_clk_pll, "atmel,at91sam9g45-clk-pll",
+	       of_at91sam9g45_clk_pll_setup);
+
+static int of_at91sam9g20_clk_pllb_setup(struct device_node *np)
+{
+	return of_at91_clk_pll_setup(np, &at91sam9g20_pllb_layout);
+}
+CLK_OF_DECLARE(at91sam9g20_clk_pllb, "atmel,at91sam9g20-clk-pllb",
+	       of_at91sam9g20_clk_pllb_setup);
+
+static int of_sama5d3_clk_pll_setup(struct device_node *np)
+{
+	return of_at91_clk_pll_setup(np, &sama5d3_pll_layout);
+}
+CLK_OF_DECLARE(sama5d3_clk_pll, "atmel,sama5d3-clk-pll",
+	       of_sama5d3_clk_pll_setup);
diff --git a/drivers/clk/at91/clk-plldiv.c b/drivers/clk/at91/clk-plldiv.c
new file mode 100644
index 0000000..917108e
--- /dev/null
+++ b/drivers/clk/at91/clk-plldiv.c
@@ -0,0 +1,135 @@
+/*
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define to_clk_plldiv(hw) container_of(clk, struct clk_plldiv, clk)
+
+struct clk_plldiv {
+	struct clk clk;
+	struct regmap *regmap;
+	const char *parent;
+};
+
+static unsigned long clk_plldiv_recalc_rate(struct clk *clk,
+					    unsigned long parent_rate)
+{
+	struct clk_plldiv *plldiv = to_clk_plldiv(clk);
+	unsigned int mckr;
+
+	regmap_read(plldiv->regmap, AT91_PMC_MCKR, &mckr);
+
+	if (mckr & AT91_PMC_PLLADIV2)
+		return parent_rate / 2;
+
+	return parent_rate;
+}
+
+static long clk_plldiv_round_rate(struct clk *clk, unsigned long rate,
+				  unsigned long *parent_rate)
+{
+	unsigned long div;
+
+	if (rate > *parent_rate)
+		return *parent_rate;
+	div = *parent_rate / 2;
+	if (rate < div)
+		return div;
+
+	if (rate - div < *parent_rate - rate)
+		return div;
+
+	return *parent_rate;
+}
+
+static int clk_plldiv_set_rate(struct clk *clk, unsigned long rate,
+			       unsigned long parent_rate)
+{
+	struct clk_plldiv *plldiv = to_clk_plldiv(clk);
+
+	if ((parent_rate != rate) && (parent_rate / 2 != rate))
+		return -EINVAL;
+
+	regmap_write_bits(plldiv->regmap, AT91_PMC_MCKR, AT91_PMC_PLLADIV2,
+			  parent_rate != rate ? AT91_PMC_PLLADIV2 : 0);
+
+	return 0;
+}
+
+static const struct clk_ops plldiv_ops = {
+	.recalc_rate = clk_plldiv_recalc_rate,
+	.round_rate = clk_plldiv_round_rate,
+	.set_rate = clk_plldiv_set_rate,
+};
+
+static struct clk *
+at91_clk_register_plldiv(struct regmap *regmap, const char *name,
+			 const char *parent_name)
+{
+	int ret;
+	struct clk_plldiv *plldiv;
+
+	plldiv = xzalloc(sizeof(*plldiv));
+
+	plldiv->clk.name = name;
+	plldiv->clk.ops  = &plldiv_ops;
+
+	if (parent_name) {
+		plldiv->parent = parent_name;
+		plldiv->clk.parent_names = &plldiv->parent;
+		plldiv->clk.num_parents = 1;
+	}
+
+	/* init.flags = CLK_SET_RATE_GATE; */
+
+	plldiv->regmap = regmap;
+
+	ret = clk_register(&plldiv->clk);
+	if (ret) {
+		kfree(plldiv);
+		return ERR_PTR(ret);
+	}
+
+	return &plldiv->clk;
+}
+
+static int
+of_at91sam9x5_clk_plldiv_setup(struct device_node *np)
+{
+	struct clk *clk;
+	const char *parent_name;
+	const char *name = np->name;
+	struct regmap *regmap;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	clk = at91_clk_register_plldiv(regmap, name, parent_name);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_plldiv, "atmel,at91sam9x5-clk-plldiv",
+	       of_at91sam9x5_clk_plldiv_setup);
diff --git a/drivers/clk/at91/clk-programmable.c b/drivers/clk/at91/clk-programmable.c
new file mode 100644
index 0000000..ddb18c0
--- /dev/null
+++ b/drivers/clk/at91/clk-programmable.c
@@ -0,0 +1,254 @@
+/*
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define PROG_SOURCE_MAX		5
+#define PROG_ID_MAX		7
+
+#define PROG_STATUS_MASK(id)	(1 << ((id) + 8))
+#define PROG_PRES_MASK		0x7
+#define PROG_PRES(layout, pckr)	((pckr >> layout->pres_shift) & PROG_PRES_MASK)
+#define PROG_MAX_RM9200_CSS	3
+
+struct clk_programmable_layout {
+	u8 pres_shift;
+	u8 css_mask;
+	u8 have_slck_mck;
+};
+
+struct clk_programmable {
+	struct clk clk;
+	struct regmap *regmap;
+	u8 id;
+	const struct clk_programmable_layout *layout;
+	const char *parent_names[PROG_SOURCE_MAX];
+};
+
+#define to_clk_programmable(clk) container_of(clk, struct clk_programmable, clk)
+
+static unsigned long clk_programmable_recalc_rate(struct clk *clk,
+						  unsigned long parent_rate)
+{
+	struct clk_programmable *prog = to_clk_programmable(clk);
+	unsigned int pckr;
+
+	regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
+
+	return parent_rate >> PROG_PRES(prog->layout, pckr);
+}
+
+static int clk_programmable_set_parent(struct clk *clk, u8 index)
+{
+	struct clk_programmable *prog = to_clk_programmable(clk);
+	const struct clk_programmable_layout *layout = prog->layout;
+	unsigned int mask = layout->css_mask;
+	unsigned int pckr = 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;
+
+		pckr |= AT91_PMC_CSSMCK_MCK;
+	}
+
+	regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id), mask, pckr);
+
+	return 0;
+}
+
+static int clk_programmable_get_parent(struct clk *clk)
+{
+	struct clk_programmable *prog = to_clk_programmable(clk);
+	const struct clk_programmable_layout *layout = prog->layout;
+	unsigned int pckr;
+	u8 ret;
+
+	regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
+
+	ret = pckr & layout->css_mask;
+
+	if (layout->have_slck_mck && (pckr & AT91_PMC_CSSMCK_MCK) && !ret)
+		ret = PROG_MAX_RM9200_CSS + 1;
+
+	return ret;
+}
+
+static int clk_programmable_set_rate(struct clk *clk, unsigned long rate,
+				     unsigned long parent_rate)
+{
+	struct clk_programmable *prog = to_clk_programmable(clk);
+	const struct clk_programmable_layout *layout = prog->layout;
+	unsigned long div = parent_rate / rate;
+	unsigned int pckr;
+	int shift = 0;
+
+	regmap_read(prog->regmap, AT91_PMC_PCKR(prog->id), &pckr);
+
+	if (!div)
+		return -EINVAL;
+
+	shift = fls(div) - 1;
+
+	if (div != (1 << shift))
+		return -EINVAL;
+
+	if (shift >= PROG_PRES_MASK)
+		return -EINVAL;
+
+	regmap_write_bits(prog->regmap, AT91_PMC_PCKR(prog->id),
+			   PROG_PRES_MASK << layout->pres_shift,
+			   shift << layout->pres_shift);
+
+	return 0;
+}
+
+static const struct clk_ops programmable_ops = {
+	.recalc_rate = clk_programmable_recalc_rate,
+	.get_parent = clk_programmable_get_parent,
+	.set_parent = clk_programmable_set_parent,
+	.set_rate = clk_programmable_set_rate,
+};
+
+static struct clk *
+at91_clk_register_programmable(struct regmap *regmap,
+			       const char *name, const char **parent_names,
+			       u8 num_parents, u8 id,
+			       const struct clk_programmable_layout *layout)
+{
+	struct clk_programmable *prog;
+	int ret;
+
+	if (id > PROG_ID_MAX)
+		return ERR_PTR(-EINVAL);
+
+	prog = kzalloc(sizeof(*prog), GFP_KERNEL);
+	if (!prog)
+		return ERR_PTR(-ENOMEM);
+
+	prog->clk.name = name;
+	prog->clk.ops = &programmable_ops;
+	memcpy(prog->parent_names, parent_names,
+	       num_parents * sizeof(prog->parent_names[0]));
+	prog->clk.parent_names = &prog->parent_names[0];
+	prog->clk.num_parents = num_parents;
+	/* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */
+
+	prog->id = id;
+	prog->layout = layout;
+	prog->regmap = regmap;
+
+	ret = clk_register(&prog->clk);
+	if (ret) {
+		kfree(prog);
+		return ERR_PTR(ret);
+	}
+
+	return &prog->clk;
+}
+
+static const struct clk_programmable_layout at91rm9200_programmable_layout = {
+	.pres_shift = 2,
+	.css_mask = 0x3,
+	.have_slck_mck = 0,
+};
+
+static const struct clk_programmable_layout at91sam9g45_programmable_layout = {
+	.pres_shift = 2,
+	.css_mask = 0x3,
+	.have_slck_mck = 1,
+};
+
+static const struct clk_programmable_layout at91sam9x5_programmable_layout = {
+	.pres_shift = 4,
+	.css_mask = 0x7,
+	.have_slck_mck = 0,
+};
+
+static int
+of_at91_clk_prog_setup(struct device_node *np,
+		       const struct clk_programmable_layout *layout)
+{
+	int num;
+	u32 id;
+	struct clk *clk;
+	unsigned int num_parents;
+	const char *parent_names[PROG_SOURCE_MAX];
+	const char *name;
+	struct device_node *progclknp;
+	struct regmap *regmap;
+
+	num_parents = of_clk_get_parent_count(np);
+	if (num_parents == 0 || num_parents > PROG_SOURCE_MAX)
+		return -EINVAL;
+
+	of_clk_parent_fill(np, parent_names, num_parents);
+
+	num = of_get_child_count(np);
+	if (!num || num > (PROG_ID_MAX + 1))
+		return -EINVAL;
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	for_each_child_of_node(np, progclknp) {
+		if (of_property_read_u32(progclknp, "reg", &id))
+			continue;
+
+		if (of_property_read_string(np, "clock-output-names", &name))
+			name = progclknp->name;
+
+		clk = at91_clk_register_programmable(regmap, name,
+						     parent_names, num_parents,
+						     id, layout);
+		if (IS_ERR(clk))
+			continue;
+
+		of_clk_add_provider(progclknp, of_clk_src_simple_get, clk);
+	}
+
+	return 0;
+}
+
+
+static void __init of_at91rm9200_clk_prog_setup(struct device_node *np)
+{
+	of_at91_clk_prog_setup(np, &at91rm9200_programmable_layout);
+}
+CLK_OF_DECLARE(at91rm9200_clk_prog, "atmel,at91rm9200-clk-programmable",
+	       of_at91rm9200_clk_prog_setup);
+
+static int of_at91sam9g45_clk_prog_setup(struct device_node *np)
+{
+	return of_at91_clk_prog_setup(np, &at91sam9g45_programmable_layout);
+}
+CLK_OF_DECLARE(at91sam9g45_clk_prog, "atmel,at91sam9g45-clk-programmable",
+	       of_at91sam9g45_clk_prog_setup);
+
+static int of_at91sam9x5_clk_prog_setup(struct device_node *np)
+{
+	return of_at91_clk_prog_setup(np, &at91sam9x5_programmable_layout);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_prog, "atmel,at91sam9x5-clk-programmable",
+	       of_at91sam9x5_clk_prog_setup);
diff --git a/drivers/clk/at91/clk-slow.c b/drivers/clk/at91/clk-slow.c
new file mode 100644
index 0000000..d4981e7
--- /dev/null
+++ b/drivers/clk/at91/clk-slow.c
@@ -0,0 +1,108 @@
+/*
+ * drivers/clk/at91/clk-slow.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+struct clk_sam9260_slow {
+	struct clk clk;
+	struct regmap *regmap;
+	const char *parent_names[2];
+};
+
+#define to_clk_sam9260_slow(clk) container_of(clk, struct clk_sam9260_slow, clk)
+
+static int clk_sam9260_slow_get_parent(struct clk *clk)
+{
+	struct clk_sam9260_slow *slowck = to_clk_sam9260_slow(clk);
+	unsigned int status;
+
+	regmap_read(slowck->regmap, AT91_PMC_SR, &status);
+
+	return status & AT91_PMC_OSCSEL ? 1 : 0;
+}
+
+static const struct clk_ops sam9260_slow_ops = {
+	.get_parent = clk_sam9260_slow_get_parent,
+};
+
+static struct clk * __init
+at91_clk_register_sam9260_slow(struct regmap *regmap,
+			       const char *name,
+			       const char **parent_names,
+			       int num_parents)
+{
+	struct clk_sam9260_slow *slowck;
+	int ret;
+
+	if (!name)
+		return ERR_PTR(-EINVAL);
+
+	if (!parent_names || !num_parents)
+		return ERR_PTR(-EINVAL);
+
+	slowck = xzalloc(sizeof(*slowck));
+	slowck->clk.name = name;
+	slowck->clk.ops = &sam9260_slow_ops;
+	memcpy(slowck->parent_names, parent_names,
+	       num_parents * sizeof(slowck->parent_names[0]));
+	slowck->clk.parent_names = slowck->parent_names;
+	slowck->clk.num_parents = num_parents;
+	slowck->regmap = regmap;
+
+	ret = clk_register(&slowck->clk);
+	if (ret) {
+		kfree(slowck);
+		return ERR_PTR(ret);
+	}
+
+	return &slowck->clk;
+}
+
+static int of_at91sam9260_clk_slow_setup(struct device_node *np)
+{
+	struct clk *clk;
+	const char *parent_names[2];
+	unsigned int num_parents;
+	const char *name = np->name;
+	struct regmap *regmap;
+
+	num_parents = of_clk_get_parent_count(np);
+	if (num_parents != 2)
+		return -EINVAL;
+
+	of_clk_parent_fill(np, parent_names, num_parents);
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	clk = at91_clk_register_sam9260_slow(regmap, name, parent_names,
+					     num_parents);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+CLK_OF_DECLARE(at91sam9260_clk_slow, "atmel,at91sam9260-clk-slow",
+	       of_at91sam9260_clk_slow_setup);
diff --git a/drivers/clk/at91/clk-smd.c b/drivers/clk/at91/clk-smd.c
new file mode 100644
index 0000000..0d72491
--- /dev/null
+++ b/drivers/clk/at91/clk-smd.c
@@ -0,0 +1,172 @@
+/*
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define SMD_SOURCE_MAX		2
+
+#define SMD_DIV_SHIFT		8
+#define SMD_MAX_DIV		0xf
+
+struct at91sam9x5_clk_smd {
+	struct clk clk;
+	struct regmap *regmap;
+	const char *parent_names[SMD_SOURCE_MAX];	
+};
+
+#define to_at91sam9x5_clk_smd(clk) \
+	container_of(clk, struct at91sam9x5_clk_smd, clk)
+
+static unsigned long at91sam9x5_clk_smd_recalc_rate(struct clk *clk,
+						    unsigned long parent_rate)
+{
+	struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk);
+	unsigned int smdr;
+	u8 smddiv;
+
+	regmap_read(smd->regmap, AT91_PMC_SMD, &smdr);
+	smddiv = (smdr & AT91_PMC_SMD_DIV) >> SMD_DIV_SHIFT;
+
+	return parent_rate / (smddiv + 1);
+}
+
+static long at91sam9x5_clk_smd_round_rate(struct clk *clk, unsigned long rate,
+					  unsigned long *parent_rate)
+{
+	unsigned long div;
+	unsigned long bestrate;
+	unsigned long tmp;
+
+	if (rate >= *parent_rate)
+		return *parent_rate;
+
+	div = *parent_rate / rate;
+	if (div > SMD_MAX_DIV)
+		return *parent_rate / (SMD_MAX_DIV + 1);
+
+	bestrate = *parent_rate / div;
+	tmp = *parent_rate / (div + 1);
+	if (bestrate - rate > rate - tmp)
+		bestrate = tmp;
+
+	return bestrate;
+}
+
+static int at91sam9x5_clk_smd_set_parent(struct clk *clk, u8 index)
+{
+	struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk);
+
+	if (index > 1)
+		return -EINVAL;
+
+	regmap_write_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMDS,
+			  index ? AT91_PMC_SMDS : 0);
+
+	return 0;
+}
+
+static int at91sam9x5_clk_smd_get_parent(struct clk *clk)
+{
+	struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk);
+	unsigned int smdr;
+
+	regmap_read(smd->regmap, AT91_PMC_SMD, &smdr);
+
+	return smdr & AT91_PMC_SMDS;
+}
+
+static int at91sam9x5_clk_smd_set_rate(struct clk *clk, unsigned long rate,
+				       unsigned long parent_rate)
+{
+	struct at91sam9x5_clk_smd *smd = to_at91sam9x5_clk_smd(clk);
+	unsigned long div = parent_rate / rate;
+
+	if (parent_rate % rate || div < 1 || div > (SMD_MAX_DIV + 1))
+		return -EINVAL;
+
+	regmap_write_bits(smd->regmap, AT91_PMC_SMD, AT91_PMC_SMD_DIV,
+			  (div - 1) << SMD_DIV_SHIFT);
+
+	return 0;
+}
+
+static const struct clk_ops at91sam9x5_smd_ops = {
+	.recalc_rate = at91sam9x5_clk_smd_recalc_rate,
+	.round_rate = at91sam9x5_clk_smd_round_rate,
+	.get_parent = at91sam9x5_clk_smd_get_parent,
+	.set_parent = at91sam9x5_clk_smd_set_parent,
+	.set_rate = at91sam9x5_clk_smd_set_rate,
+};
+
+static struct clk *
+at91sam9x5_clk_register_smd(struct regmap *regmap, const char *name,
+			    const char **parent_names, u8 num_parents)
+{
+	struct at91sam9x5_clk_smd *smd;
+	int ret;
+
+	smd = xzalloc(sizeof(*smd));
+	smd->clk.name = name;
+	smd->clk.ops = &at91sam9x5_smd_ops;
+	memcpy(smd->parent_names, parent_names,
+	       num_parents * sizeof(smd->parent_names[0]));
+	smd->clk.parent_names = smd->parent_names;
+	smd->clk.num_parents = num_parents;
+	/* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; */
+	smd->regmap = regmap;
+
+	ret = clk_register(&smd->clk);
+	if (ret) {
+		kfree(smd);
+		return ERR_PTR(ret);
+	}
+
+	return &smd->clk;
+}
+
+static int of_at91sam9x5_clk_smd_setup(struct device_node *np)
+{
+	struct clk *clk;
+	unsigned int num_parents;
+	const char *parent_names[SMD_SOURCE_MAX];
+	const char *name = np->name;
+	struct regmap *regmap;
+
+	num_parents = of_clk_get_parent_count(np);
+	if (num_parents == 0 || num_parents > SMD_SOURCE_MAX)
+		return -EINVAL;
+
+	of_clk_parent_fill(np, parent_names, num_parents);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	clk = at91sam9x5_clk_register_smd(regmap, name, parent_names,
+					  num_parents);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_smd, "atmel,at91sam9x5-clk-smd",
+	       of_at91sam9x5_clk_smd_setup);
diff --git a/drivers/clk/at91/clk-system.c b/drivers/clk/at91/clk-system.c
new file mode 100644
index 0000000..021930e
--- /dev/null
+++ b/drivers/clk/at91/clk-system.c
@@ -0,0 +1,160 @@
+/*
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define SYSTEM_MAX_ID		31
+
+#define SYSTEM_MAX_NAME_SZ	32
+
+#define to_clk_system(clk) container_of(clk, struct clk_system, clk)
+struct clk_system {
+	struct clk clk;
+	struct regmap *regmap;
+	u8 id;
+	const char *parent_name;
+};
+
+static inline int is_pck(int id)
+{
+	return (id >= 8) && (id <= 15);
+}
+
+static inline bool clk_system_ready(struct regmap *regmap, int id)
+{
+	unsigned int status;
+
+	regmap_read(regmap, AT91_PMC_SR, &status);
+
+	return status & (1 << id) ? 1 : 0;
+}
+
+static int clk_system_enable(struct clk *clk)
+{
+	struct clk_system *sys = to_clk_system(clk);
+
+	regmap_write(sys->regmap, AT91_PMC_SCER, 1 << sys->id);
+
+	if (!is_pck(sys->id))
+		return 0;
+
+	while (!clk_system_ready(sys->regmap, sys->id))
+		barrier();
+
+	return 0;
+}
+
+static void clk_system_disable(struct clk *clk)
+{
+	struct clk_system *sys = to_clk_system(clk);
+
+	regmap_write(sys->regmap, AT91_PMC_SCDR, 1 << sys->id);
+}
+
+static int clk_system_is_enabled(struct clk *clk)
+{
+	struct clk_system *sys = to_clk_system(clk);
+	unsigned int status;
+
+	regmap_read(sys->regmap, AT91_PMC_SCSR, &status);
+
+	if (!(status & (1 << sys->id)))
+		return 0;
+
+	if (!is_pck(sys->id))
+		return 1;
+
+	regmap_read(sys->regmap, AT91_PMC_SR, &status);
+
+	return status & (1 << sys->id) ? 1 : 0;
+}
+
+static const struct clk_ops system_ops = {
+	.enable = clk_system_enable,
+	.disable = clk_system_disable,
+	.is_enabled = clk_system_is_enabled,
+};
+
+static struct clk *
+at91_clk_register_system(struct regmap *regmap, const char *name,
+			 const char *parent_name, u8 id)
+{
+	struct clk_system *sys;
+	int ret;
+
+	if (!parent_name || id > SYSTEM_MAX_ID)
+		return ERR_PTR(-EINVAL);
+
+	sys = xzalloc(sizeof(*sys));
+	sys->clk.name = name;
+	sys->clk.ops = &system_ops;
+	sys->parent_name = parent_name;
+	sys->clk.parent_names = &sys->parent_name;
+	sys->clk.num_parents = 1;
+	/* init.flags = CLK_SET_RATE_PARENT; */
+	sys->id = id;
+	sys->regmap = regmap;
+
+	ret = clk_register(&sys->clk);
+	if (ret) {
+		kfree(sys);
+		return ERR_PTR(ret);
+	}
+
+	return &sys->clk;
+}
+
+static int of_at91rm9200_clk_sys_setup(struct device_node *np)
+{
+	int num;
+	u32 id;
+	struct clk *clk;
+	const char *name;
+	struct device_node *sysclknp;
+	const char *parent_name;
+	struct regmap *regmap;
+
+	num = of_get_child_count(np);
+	if (num > (SYSTEM_MAX_ID + 1))
+		return -EINVAL;
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	for_each_child_of_node(np, sysclknp) {
+		if (of_property_read_u32(sysclknp, "reg", &id))
+			continue;
+
+		if (of_property_read_string(np, "clock-output-names", &name))
+			name = sysclknp->name;
+
+		parent_name = of_clk_get_parent_name(sysclknp, 0);
+
+		clk = at91_clk_register_system(regmap, name, parent_name, id);
+		if (IS_ERR(clk))
+			continue;
+
+		of_clk_add_provider(sysclknp, of_clk_src_simple_get, clk);
+	}
+
+	return 0;
+}
+CLK_OF_DECLARE(at91rm9200_clk_sys, "atmel,at91rm9200-clk-system",
+	       of_at91rm9200_clk_sys_setup);
diff --git a/drivers/clk/at91/clk-usb.c b/drivers/clk/at91/clk-usb.c
new file mode 100644
index 0000000..99ba671
--- /dev/null
+++ b/drivers/clk/at91/clk-usb.c
@@ -0,0 +1,397 @@
+/*
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define USB_SOURCE_MAX		2
+
+#define SAM9X5_USB_DIV_SHIFT	8
+#define SAM9X5_USB_MAX_DIV	0xf
+
+#define RM9200_USB_DIV_SHIFT	28
+#define RM9200_USB_DIV_TAB_SIZE	4
+
+struct at91sam9x5_clk_usb {
+	struct clk clk;
+	struct regmap *regmap;
+	const char *parent_names[USB_SOURCE_MAX];
+};
+
+#define to_at91sam9x5_clk_usb(clk) \
+	container_of(clk, struct at91sam9x5_clk_usb, clk)
+
+struct at91rm9200_clk_usb {
+	struct clk clk;
+	struct regmap *regmap;
+	u32 divisors[4];
+	const char *parent_name;
+};
+
+#define to_at91rm9200_clk_usb(clk) \
+	container_of(clk, struct at91rm9200_clk_usb, clk)
+
+static unsigned long at91sam9x5_clk_usb_recalc_rate(struct clk *clk,
+						    unsigned long parent_rate)
+{
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+	unsigned int usbr;
+	u8 usbdiv;
+
+	regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
+	usbdiv = (usbr & AT91_PMC_OHCIUSBDIV) >> SAM9X5_USB_DIV_SHIFT;
+
+	return DIV_ROUND_CLOSEST(parent_rate, (usbdiv + 1));
+}
+
+static int at91sam9x5_clk_usb_set_parent(struct clk *clk, u8 index)
+{
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+
+	if (index > 1)
+		return -EINVAL;
+
+	regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
+			  index ? AT91_PMC_USBS : 0);
+
+	return 0;
+}
+
+static int at91sam9x5_clk_usb_get_parent(struct clk *clk)
+{
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+	unsigned int usbr;
+
+	regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
+
+	return usbr & AT91_PMC_USBS;
+}
+
+static int at91sam9x5_clk_usb_set_rate(struct clk *clk, unsigned long rate,
+				       unsigned long parent_rate)
+{
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+	unsigned long div;
+
+	if (!rate)
+		return -EINVAL;
+
+	div = DIV_ROUND_CLOSEST(parent_rate, rate);
+	if (div > SAM9X5_USB_MAX_DIV + 1 || !div)
+		return -EINVAL;
+
+	regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_OHCIUSBDIV,
+			  (div - 1) << SAM9X5_USB_DIV_SHIFT);
+
+	return 0;
+}
+
+static const struct clk_ops at91sam9x5_usb_ops = {
+	.recalc_rate = at91sam9x5_clk_usb_recalc_rate,
+	.get_parent = at91sam9x5_clk_usb_get_parent,
+	.set_parent = at91sam9x5_clk_usb_set_parent,
+	.set_rate = at91sam9x5_clk_usb_set_rate,
+};
+
+static int at91sam9n12_clk_usb_enable(struct clk *clk)
+{
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+
+	regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS,
+			  AT91_PMC_USBS);
+
+	return 0;
+}
+
+static void at91sam9n12_clk_usb_disable(struct clk *clk)
+{
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+
+	regmap_write_bits(usb->regmap, AT91_PMC_USB, AT91_PMC_USBS, 0);
+}
+
+static int at91sam9n12_clk_usb_is_enabled(struct clk *clk)
+{
+	struct at91sam9x5_clk_usb *usb = to_at91sam9x5_clk_usb(clk);
+	unsigned int usbr;
+
+	regmap_read(usb->regmap, AT91_PMC_USB, &usbr);
+
+	return usbr & AT91_PMC_USBS;
+}
+
+static const struct clk_ops at91sam9n12_usb_ops = {
+	.enable = at91sam9n12_clk_usb_enable,
+	.disable = at91sam9n12_clk_usb_disable,
+	.is_enabled = at91sam9n12_clk_usb_is_enabled,
+	.recalc_rate = at91sam9x5_clk_usb_recalc_rate,
+	.set_rate = at91sam9x5_clk_usb_set_rate,
+};
+
+static struct clk *
+at91sam9x5_clk_register_usb(struct regmap *regmap, const char *name,
+			    const char **parent_names, u8 num_parents)
+{
+	struct at91sam9x5_clk_usb *usb;
+	int ret;
+
+	usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+	usb->clk.name = name;
+	usb->clk.ops = &at91sam9x5_usb_ops;
+	memcpy(usb->parent_names, parent_names,
+	       num_parents * sizeof(usb->parent_names[0]));
+	usb->clk.parent_names = usb->parent_names;
+	usb->clk.num_parents = num_parents;
+	usb->clk.flags = CLK_SET_RATE_PARENT;
+	/* init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE | */
+	/* 	     CLK_SET_RATE_PARENT; */
+	usb->regmap = regmap;
+
+	ret = clk_register(&usb->clk);
+	if (ret) {
+		kfree(usb);
+		return ERR_PTR(ret);
+	}
+
+	return &usb->clk;
+}
+
+static struct clk *
+at91sam9n12_clk_register_usb(struct regmap *regmap, const char *name,
+			     const char *parent_name)
+{
+	struct at91sam9x5_clk_usb *usb;
+	int ret;
+
+	usb = xzalloc(sizeof(*usb));
+	usb->clk.name = name;
+	usb->clk.ops = &at91sam9n12_usb_ops;
+	usb->parent_names[0] = parent_name;
+	usb->clk.parent_names = &usb->parent_names[0];
+	usb->clk.num_parents = 1;
+	/* init.flags = CLK_SET_RATE_GATE | CLK_SET_RATE_PARENT; */
+	usb->regmap = regmap;
+
+	ret = clk_register(&usb->clk);
+	if (ret) {
+		kfree(usb);
+		return ERR_PTR(ret);
+	}
+
+	return &usb->clk;
+}
+
+static unsigned long at91rm9200_clk_usb_recalc_rate(struct clk *clk,
+						    unsigned long parent_rate)
+{
+	struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk);
+	unsigned int pllbr;
+	u8 usbdiv;
+
+	regmap_read(usb->regmap, AT91_CKGR_PLLBR, &pllbr);
+
+	usbdiv = (pllbr & AT91_PMC_USBDIV) >> RM9200_USB_DIV_SHIFT;
+	if (usb->divisors[usbdiv])
+		return parent_rate / usb->divisors[usbdiv];
+
+	return 0;
+}
+
+static long at91rm9200_clk_usb_round_rate(struct clk *clk, unsigned long rate,
+					  unsigned long *parent_rate)
+{
+	struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk);
+	struct clk *parent = clk_get_parent(clk);
+	unsigned long bestrate = 0;
+	int bestdiff = -1;
+	unsigned long tmprate;
+	int tmpdiff;
+	int i = 0;
+
+	for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
+		unsigned long tmp_parent_rate;
+
+		if (!usb->divisors[i])
+			continue;
+
+		tmp_parent_rate = rate * usb->divisors[i];
+		tmp_parent_rate = clk_round_rate(parent, tmp_parent_rate);
+		tmprate = DIV_ROUND_CLOSEST(tmp_parent_rate, usb->divisors[i]);
+		if (tmprate < rate)
+			tmpdiff = rate - tmprate;
+		else
+			tmpdiff = tmprate - rate;
+
+		if (bestdiff < 0 || bestdiff > tmpdiff) {
+			bestrate = tmprate;
+			bestdiff = tmpdiff;
+			*parent_rate = tmp_parent_rate;
+		}
+
+		if (!bestdiff)
+			break;
+	}
+
+	return bestrate;
+}
+
+static int at91rm9200_clk_usb_set_rate(struct clk *clk, unsigned long rate,
+				       unsigned long parent_rate)
+{
+	int i;
+	struct at91rm9200_clk_usb *usb = to_at91rm9200_clk_usb(clk);
+	unsigned long div;
+
+	if (!rate)
+		return -EINVAL;
+
+	div = DIV_ROUND_CLOSEST(parent_rate, rate);
+
+	for (i = 0; i < RM9200_USB_DIV_TAB_SIZE; i++) {
+		if (usb->divisors[i] == div) {
+			regmap_write_bits(usb->regmap, AT91_CKGR_PLLBR,
+					  AT91_PMC_USBDIV,
+					  i << RM9200_USB_DIV_SHIFT);
+
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static const struct clk_ops at91rm9200_usb_ops = {
+	.recalc_rate = at91rm9200_clk_usb_recalc_rate,
+	.round_rate = at91rm9200_clk_usb_round_rate,
+	.set_rate = at91rm9200_clk_usb_set_rate,
+};
+
+static struct clk *
+at91rm9200_clk_register_usb(struct regmap *regmap, const char *name,
+			    const char *parent_name, const u32 *divisors)
+{
+	struct at91rm9200_clk_usb *usb;
+	int ret;
+
+	usb = xzalloc(sizeof(*usb));
+	usb->clk.name = name;
+	usb->clk.ops = &at91rm9200_usb_ops;
+	usb->parent_name = parent_name;
+	usb->clk.parent_names = &usb->parent_name;
+	usb->clk.num_parents = 1;
+	/* init.flags = CLK_SET_RATE_PARENT; */
+
+	usb->regmap = regmap;
+	memcpy(usb->divisors, divisors, sizeof(usb->divisors));
+
+	ret = clk_register(&usb->clk);
+	if (ret) {
+		kfree(usb);
+		return ERR_PTR(ret);
+	}
+
+	return &usb->clk;
+}
+
+static int of_at91sam9x5_clk_usb_setup(struct device_node *np)
+{
+	struct clk *clk;
+	unsigned int num_parents;
+	const char *parent_names[USB_SOURCE_MAX];
+	const char *name = np->name;
+	struct regmap *regmap;
+
+	num_parents = of_clk_get_parent_count(np);
+	if (num_parents == 0 || num_parents > USB_SOURCE_MAX)
+		return -EINVAL;
+
+	of_clk_parent_fill(np, parent_names, num_parents);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	clk = at91sam9x5_clk_register_usb(regmap, name, parent_names,
+					 num_parents);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91sam9x5_clk_usb, "atmel,at91sam9x5-clk-usb",
+	       of_at91sam9x5_clk_usb_setup);
+
+static int of_at91sam9n12_clk_usb_setup(struct device_node *np)
+{
+	struct clk *clk;
+	const char *parent_name;
+	const char *name = np->name;
+	struct regmap *regmap;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+	if (!parent_name)
+		return -EINVAL;
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	clk = at91sam9n12_clk_register_usb(regmap, name, parent_name);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91sam9n12_clk_usb, "atmel,at91sam9n12-clk-usb",
+	       of_at91sam9n12_clk_usb_setup);
+
+static int of_at91rm9200_clk_usb_setup(struct device_node *np)
+{
+	struct clk *clk;
+	const char *parent_name;
+	const char *name = np->name;
+	u32 divisors[4] = {0, 0, 0, 0};
+	struct regmap *regmap;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+	if (!parent_name)
+		return -EINVAL;
+
+	of_property_read_u32_array(np, "atmel,clk-divisors", divisors, 4);
+	if (!divisors[0])
+		return -EINVAL;
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	clk = at91rm9200_clk_register_usb(regmap, name, parent_name, divisors);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(at91rm9200_clk_usb, "atmel,at91rm9200-clk-usb",
+	       of_at91rm9200_clk_usb_setup);
diff --git a/drivers/clk/at91/clk-utmi.c b/drivers/clk/at91/clk-utmi.c
new file mode 100644
index 0000000..96ce35c
--- /dev/null
+++ b/drivers/clk/at91/clk-utmi.c
@@ -0,0 +1,138 @@
+/*
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+#define UTMI_FIXED_MUL		40
+
+struct clk_utmi {
+	struct clk clk;
+	struct regmap *regmap;
+	const char *parent;
+};
+
+#define to_clk_utmi(clk) container_of(clk, struct clk_utmi, clk)
+
+static inline bool clk_utmi_ready(struct regmap *regmap)
+{
+	unsigned int status;
+
+	regmap_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;
+
+	regmap_write_bits(utmi->regmap, AT91_CKGR_UCKR, uckr, uckr);
+
+	while (!clk_utmi_ready(utmi->regmap))
+		barrier();
+
+	return 0;
+}
+
+static int clk_utmi_is_enabled(struct clk *clk)
+{
+	struct clk_utmi *utmi = to_clk_utmi(clk);
+	
+	return clk_utmi_ready(utmi->regmap);
+}
+
+static void clk_utmi_disable(struct clk *clk)
+{
+	struct clk_utmi *utmi = to_clk_utmi(clk);
+
+	regmap_write_bits(utmi->regmap, AT91_CKGR_UCKR, AT91_PMC_UPLLEN, 0);
+}
+
+static unsigned long clk_utmi_recalc_rate(struct clk *clk,
+					  unsigned long parent_rate)
+{
+	/* UTMI clk is a fixed clk multiplier */
+	return parent_rate * UTMI_FIXED_MUL;
+}
+
+static const struct clk_ops utmi_ops = {
+	.enable = clk_utmi_enable,
+	.disable = clk_utmi_disable,
+	.is_enabled = clk_utmi_is_enabled,
+	.recalc_rate = clk_utmi_recalc_rate,
+};
+
+static struct clk * __init
+at91_clk_register_utmi(struct regmap *regmap,
+		       const char *name, const char *parent_name)
+{
+	int ret;
+	struct clk_utmi *utmi;
+
+	utmi = xzalloc(sizeof(*utmi));
+
+	utmi->clk.name = name;
+	utmi->clk.ops = &utmi_ops;
+
+	if (parent_name) {
+		utmi->parent = parent_name;
+		utmi->clk.parent_names = &utmi->parent;
+		utmi->clk.num_parents = 1;
+	}
+
+	/* utmi->clk.flags = CLK_SET_RATE_GATE; */
+
+	utmi->regmap = regmap;
+
+	ret = clk_register(&utmi->clk);
+	if (ret) {
+		kfree(utmi);
+		return ERR_PTR(ret);
+	}
+
+	return &utmi->clk;
+}
+#if defined(CONFIG_OFTREE) && defined(CONFIG_COMMON_CLK_OF_PROVIDER)
+static void __init of_at91sam9x5_clk_utmi_setup(struct device_node *np)
+{
+	struct clk *clk;
+	const char *parent_name;
+	const char *name = np->name;
+	struct regmap *regmap;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	regmap = syscon_node_to_regmap(of_get_parent(np));
+	if (IS_ERR(regmap))
+		return;
+
+	clk = at91_clk_register_utmi(regmap, name, parent_name);
+	if (IS_ERR(clk))
+		return;
+
+	of_clk_add_provider(np, of_clk_src_simple_get, clk);
+	return;
+}
+CLK_OF_DECLARE(at91sam9x5_clk_utmi, "atmel,at91sam9x5-clk-utmi",
+	       of_at91sam9x5_clk_utmi_setup);
+#endif
diff --git a/drivers/clk/at91/pmc.c b/drivers/clk/at91/pmc.c
new file mode 100644
index 0000000..d156d50
--- /dev/null
+++ b/drivers/clk/at91/pmc.c
@@ -0,0 +1,41 @@
+/*
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <module.h>
+#include <linux/list.h>
+#include <linux/clkdev.h>
+#include <of.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+#include "pmc.h"
+
+int of_at91_get_clk_range(struct device_node *np, const char *propname,
+			  struct clk_range *range)
+{
+	u32 min, max;
+	int ret;
+
+	ret = of_property_read_u32_index(np, propname, 0, &min);
+	if (ret)
+		return ret;
+
+	ret = of_property_read_u32_index(np, propname, 1, &max);
+	if (ret)
+		return ret;
+
+	if (range) {
+		range->min = min;
+		range->max = max;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(of_at91_get_clk_range);
diff --git a/drivers/clk/at91/pmc.h b/drivers/clk/at91/pmc.h
new file mode 100644
index 0000000..c6c14a7
--- /dev/null
+++ b/drivers/clk/at91/pmc.h
@@ -0,0 +1,27 @@
+/*
+ * drivers/clk/at91/pmc.h
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef __PMC_H_
+#define __PMC_H_
+
+#include <io.h>
+
+struct clk_range {
+	unsigned long min;
+	unsigned long max;
+};
+
+#define CLK_RANGE(MIN, MAX) {.min = MIN, .max = MAX,}
+
+int of_at91_get_clk_range(struct device_node *np, const char *propname,
+			  struct clk_range *range);
+
+#endif /* __PMC_H_ */
diff --git a/drivers/clk/at91/sckc.c b/drivers/clk/at91/sckc.c
new file mode 100644
index 0000000..debaafb
--- /dev/null
+++ b/drivers/clk/at91/sckc.c
@@ -0,0 +1,485 @@
+/*
+ * drivers/clk/at91/sckc.c
+ *
+ *  Copyright (C) 2013 Boris BREZILLON <b.brezillon@overkiz.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <common.h>
+#include <clock.h>
+#include <of.h>
+#include <of_address.h>
+#include <io.h>
+#include <linux/list.h>
+#include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
+#include <mfd/syscon.h>
+#include <regmap.h>
+
+
+
+#define SLOW_CLOCK_FREQ		32768
+#define SLOWCK_SW_CYCLES	5
+#define SLOWCK_SW_TIME_USEC	((SLOWCK_SW_CYCLES * SECOND) / \
+				 SLOW_CLOCK_FREQ)
+
+#define	AT91_SCKC_CR			0x00
+#define		AT91_SCKC_RCEN		(1 << 0)
+#define		AT91_SCKC_OSC32EN	(1 << 1)
+#define		AT91_SCKC_OSC32BYP	(1 << 2)
+#define		AT91_SCKC_OSCSEL	(1 << 3)
+
+struct clk_slow_osc {
+	struct clk clk;
+	void __iomem *sckcr;
+	unsigned long startup_usec;
+	const char *parent_name;
+};
+
+#define to_clk_slow_osc(clk) container_of(clk, struct clk_slow_osc, clk)
+
+struct clk_sama5d4_slow_osc {
+	struct clk clk;
+	void __iomem *sckcr;
+	unsigned long startup_usec;
+	bool prepared;
+	const char *parent_name;
+};
+
+#define to_clk_sama5d4_slow_osc(clk) container_of(clk, struct clk_sama5d4_slow_osc, clk)
+
+struct clk_slow_rc_osc {
+	struct clk clk;
+	void __iomem *sckcr;
+	unsigned long frequency;
+	unsigned long startup_usec;
+	const char *parent_name;
+};
+
+#define to_clk_slow_rc_osc(clk) container_of(clk, struct clk_slow_rc_osc, clk)
+
+struct clk_sam9x5_slow {
+	struct clk clk;
+	void __iomem *sckcr;
+	u8 parent;
+	const char *parent_names[2];
+};
+
+#define to_clk_sam9x5_slow(clk) container_of(clk, struct clk_sam9x5_slow, clk)
+
+static int clk_slow_osc_enable(struct clk *clk)
+{
+	struct clk_slow_osc *osc = to_clk_slow_osc(clk);
+	void __iomem *sckcr = osc->sckcr;
+	u32 tmp = readl(sckcr);
+
+	if (tmp & (AT91_SCKC_OSC32BYP | AT91_SCKC_OSC32EN))
+		return 0;
+
+	writel(tmp | AT91_SCKC_OSC32EN, sckcr);
+
+	udelay(osc->startup_usec);
+
+	return 0;
+}
+
+static void clk_slow_osc_disable(struct clk *clk)
+{
+	struct clk_slow_osc *osc = to_clk_slow_osc(clk);
+	void __iomem *sckcr = osc->sckcr;
+	u32 tmp = readl(sckcr);
+
+	if (tmp & AT91_SCKC_OSC32BYP)
+		return;
+
+	writel(tmp & ~AT91_SCKC_OSC32EN, sckcr);
+}
+
+static int clk_slow_osc_is_enabled(struct clk *clk)
+{
+	struct clk_slow_osc *osc = to_clk_slow_osc(clk);
+	void __iomem *sckcr = osc->sckcr;
+	u32 tmp = readl(sckcr);
+
+	if (tmp & AT91_SCKC_OSC32BYP)
+		return 1;
+
+	return !!(tmp & AT91_SCKC_OSC32EN);
+}
+
+static const struct clk_ops slow_osc_ops = {
+	.enable = clk_slow_osc_enable,
+	.disable = clk_slow_osc_disable,
+	.is_enabled = clk_slow_osc_is_enabled,
+};
+
+static struct clk * 
+at91_clk_register_slow_osc(void __iomem *sckcr,
+			   const char *name,
+			   const char *parent_name,
+			   unsigned long startup,
+			   bool bypass)
+{
+	int ret;
+	struct clk_slow_osc *osc;
+
+	if (!sckcr || !name || !parent_name)
+		return ERR_PTR(-EINVAL);
+
+	osc = xzalloc(sizeof(*osc));
+
+	osc->clk.name = name;
+	osc->clk.ops = &slow_osc_ops;
+	osc->parent_name = parent_name;
+	osc->clk.parent_names = &osc->parent_name;
+	osc->clk.num_parents = 1;
+
+	osc->sckcr = sckcr;
+	osc->startup_usec = startup;
+
+	if (bypass)
+		writel((readl(sckcr) & ~AT91_SCKC_OSC32EN) | AT91_SCKC_OSC32BYP,
+		       sckcr);
+
+	ret = clk_register(&osc->clk);
+	if (ret) {
+		kfree(osc);
+		return ERR_PTR(ret);
+	}
+
+	return &osc->clk;
+}
+
+static void 
+of_at91sam9x5_clk_slow_osc_setup(struct device_node *np, void __iomem *sckcr)
+{
+	struct clk *clk;
+	const char *parent_name;
+	const char *name = np->name;
+	u32 startup;
+	bool bypass;
+
+	parent_name = of_clk_get_parent_name(np, 0);
+	of_property_read_string(np, "clock-output-names", &name);
+	of_property_read_u32(np, "atmel,startup-time-usec", &startup);
+	bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+	clk = at91_clk_register_slow_osc(sckcr, name, parent_name, startup,
+					 bypass);
+	if (IS_ERR(clk))
+		return;
+
+	of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static unsigned long clk_slow_rc_osc_recalc_rate(struct clk *clk,
+						 unsigned long parent_rate)
+{
+	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+
+	return osc->frequency;
+}
+
+static int clk_slow_rc_osc_enable(struct clk *clk)
+{
+	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+	void __iomem *sckcr = osc->sckcr;
+
+	writel(readl(sckcr) | AT91_SCKC_RCEN, sckcr);
+
+	udelay(osc->startup_usec);
+
+	return 0;
+}
+
+static void clk_slow_rc_osc_disable(struct clk *clk)
+{
+	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+	void __iomem *sckcr = osc->sckcr;
+
+	writel(readl(sckcr) & ~AT91_SCKC_RCEN, sckcr);
+}
+
+static int clk_slow_rc_osc_is_enabled(struct clk *clk)
+{
+	struct clk_slow_rc_osc *osc = to_clk_slow_rc_osc(clk);
+
+	return !!(readl(osc->sckcr) & AT91_SCKC_RCEN);
+}
+
+static const struct clk_ops slow_rc_osc_ops = {
+	.enable = clk_slow_rc_osc_enable,
+	.disable = clk_slow_rc_osc_disable,
+	.is_enabled = clk_slow_rc_osc_is_enabled,
+	.recalc_rate = clk_slow_rc_osc_recalc_rate,
+};
+
+static struct clk * 
+at91_clk_register_slow_rc_osc(void __iomem *sckcr,
+			      const char *name,
+			      unsigned long frequency,
+			      unsigned long startup)
+{
+	struct clk_slow_rc_osc *osc;
+	int ret;
+
+	if (!sckcr || !name)
+		return ERR_PTR(-EINVAL);
+
+	osc = xzalloc(sizeof(*osc));
+	osc->clk.name = name;
+	osc->clk.ops = &slow_rc_osc_ops;
+	osc->clk.parent_names = NULL;
+	osc->clk.num_parents = 0;
+	/* init.flags = CLK_IGNORE_UNUSED; */
+
+	osc->sckcr = sckcr;
+	osc->frequency = frequency;
+	osc->startup_usec = startup;
+
+	ret = clk_register(&osc->clk);
+	if (ret) {
+		kfree(osc);
+		return ERR_PTR(ret);
+	}
+
+	return &osc->clk;
+}
+
+static void 
+of_at91sam9x5_clk_slow_rc_osc_setup(struct device_node *np, void __iomem *sckcr)
+{
+	struct clk *clk;
+	u32 frequency = 0;
+	u32 startup = 0;
+	const char *name = np->name;
+
+	of_property_read_string(np, "clock-output-names", &name);
+	of_property_read_u32(np, "clock-frequency", &frequency);
+	of_property_read_u32(np, "atmel,startup-time-usec", &startup);
+
+	clk = at91_clk_register_slow_rc_osc(sckcr, name, frequency, startup);
+	if (IS_ERR(clk))
+		return;
+
+	of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static int clk_sam9x5_slow_set_parent(struct clk *clk, u8 index)
+{
+	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk);
+	void __iomem *sckcr = slowck->sckcr;
+	u32 tmp;
+
+	if (index > 1)
+		return -EINVAL;
+
+	tmp = readl(sckcr);
+
+	if ((!index && !(tmp & AT91_SCKC_OSCSEL)) ||
+	    (index && (tmp & AT91_SCKC_OSCSEL)))
+		return 0;
+
+	if (index)
+		tmp |= AT91_SCKC_OSCSEL;
+	else
+		tmp &= ~AT91_SCKC_OSCSEL;
+
+	writel(tmp, sckcr);
+
+	udelay(SLOWCK_SW_TIME_USEC);
+
+	return 0;
+}
+
+static int clk_sam9x5_slow_get_parent(struct clk *clk)
+{
+	struct clk_sam9x5_slow *slowck = to_clk_sam9x5_slow(clk);
+
+	return !!(readl(slowck->sckcr) & AT91_SCKC_OSCSEL);
+}
+
+static const struct clk_ops sam9x5_slow_ops = {
+	.set_parent = clk_sam9x5_slow_set_parent,
+	.get_parent = clk_sam9x5_slow_get_parent,
+};
+
+static struct clk * 
+at91_clk_register_sam9x5_slow(void __iomem *sckcr,
+			      const char *name,
+			      const char **parent_names,
+			      int num_parents)
+{
+	struct clk_sam9x5_slow *slowck;
+	int ret;
+
+	if (!sckcr || !name || !parent_names || !num_parents)
+		return ERR_PTR(-EINVAL);
+
+	slowck = xzalloc(sizeof(*slowck));
+	slowck->clk.name = name;
+	slowck->clk.ops = &sam9x5_slow_ops;
+
+	memcpy(slowck->parent_names, parent_names,
+	       num_parents * sizeof(slowck->parent_names[0]));
+	slowck->clk.parent_names = slowck->parent_names;
+	slowck->clk.num_parents = num_parents;
+	slowck->sckcr = sckcr;
+	slowck->parent = !!(readl(sckcr) & AT91_SCKC_OSCSEL);
+
+	ret = clk_register(&slowck->clk);
+	if (ret) {
+		kfree(slowck);
+		return ERR_PTR(ret);
+	}
+
+	return &slowck->clk;
+}
+
+static int
+of_at91sam9x5_clk_slow_setup(struct device_node *np, void __iomem *sckcr)
+{
+	struct clk *clk;
+	const char *parent_names[2];
+	unsigned int num_parents;
+	const char *name = np->name;
+
+	num_parents = of_clk_get_parent_count(np);
+	if (num_parents == 0 || num_parents > 2)
+		return -EINVAL;
+
+	of_clk_parent_fill(np, parent_names, num_parents);
+
+	of_property_read_string(np, "clock-output-names", &name);
+
+	clk = at91_clk_register_sam9x5_slow(sckcr, name, parent_names,
+					    num_parents);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+
+static const struct of_device_id sckc_clk_ids[] = {
+	/* Slow clock */
+	{
+		.compatible = "atmel,at91sam9x5-clk-slow-osc",
+		.data = of_at91sam9x5_clk_slow_osc_setup,
+	},
+	{
+		.compatible = "atmel,at91sam9x5-clk-slow-rc-osc",
+		.data = of_at91sam9x5_clk_slow_rc_osc_setup,
+	},
+	{
+		.compatible = "atmel,at91sam9x5-clk-slow",
+		.data = of_at91sam9x5_clk_slow_setup,
+	},
+	{ /*sentinel*/ }
+};
+
+static int of_at91sam9x5_sckc_setup(struct device_node *np)
+{
+	struct device_node *childnp;
+	void (*clk_setup)(struct device_node *, void __iomem *);
+	const struct of_device_id *clk_id;
+	void __iomem *regbase = of_iomap(np, 0);
+
+	if (!regbase)
+		return -ENOMEM;
+
+	for_each_child_of_node(np, childnp) {
+		clk_id = of_match_node(sckc_clk_ids, childnp);
+		if (!clk_id)
+			continue;
+		clk_setup = clk_id->data;
+		clk_setup(childnp, regbase);
+	}
+
+	return 0;
+}
+CLK_OF_DECLARE(at91sam9x5_clk_sckc, "atmel,at91sam9x5-sckc",
+	       of_at91sam9x5_sckc_setup);
+
+static int clk_sama5d4_slow_osc_enable(struct clk *clk)
+{
+	struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(clk);
+
+	if (osc->prepared)
+		return 0;
+
+	/*
+	 * Assume that if it has already been selected (for example by the
+	 * bootloader), enough time has aready passed.
+	 */
+	if ((readl(osc->sckcr) & AT91_SCKC_OSCSEL)) {
+		osc->prepared = true;
+		return 0;
+	}
+
+	udelay(osc->startup_usec);
+	osc->prepared = true;
+
+	return 0;
+}
+
+static int clk_sama5d4_slow_osc_is_enabled(struct clk *clk)
+{
+	struct clk_sama5d4_slow_osc *osc = to_clk_sama5d4_slow_osc(clk);
+
+	return osc->prepared;
+}
+
+static const struct clk_ops sama5d4_slow_osc_ops = {
+	.enable = clk_sama5d4_slow_osc_enable,
+	.is_enabled = clk_sama5d4_slow_osc_is_enabled,
+};
+
+static int of_sama5d4_sckc_setup(struct device_node *np)
+{
+	void __iomem *regbase = of_iomap(np, 0);
+	struct clk *clk;
+	struct clk_sama5d4_slow_osc *osc;
+	const char *parent_names[2] = { "slow_rc_osc", "slow_osc" };
+	bool bypass;
+	int ret;
+
+	if (!regbase)
+		return -ENOMEM;
+
+	clk = clk_fixed(parent_names[0], 32768);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	bypass = of_property_read_bool(np, "atmel,osc-bypass");
+
+	osc = xzalloc(sizeof(*osc));
+	osc->parent_name = of_clk_get_parent_name(np, 0);
+	osc->clk.name = parent_names[1];
+	osc->clk.ops = &sama5d4_slow_osc_ops;
+	osc->clk.parent_names = &osc->parent_name;
+	osc->clk.num_parents = 1;
+	osc->sckcr = regbase;
+	osc->startup_usec = 1200000;
+
+	if (bypass)
+		writel((readl(regbase) | AT91_SCKC_OSC32BYP), regbase);
+
+	ret = clk_register(&osc->clk);
+	if (ret) {
+		kfree(osc);
+		return ret;
+	}
+
+	clk = at91_clk_register_sam9x5_slow(regbase, "slowck", parent_names, 2);
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	return of_clk_add_provider(np, of_clk_src_simple_get, clk);
+}
+CLK_OF_DECLARE(sama5d4_clk_sckc, "atmel,sama5d4-sckc",
+	       of_sama5d4_sckc_setup);
diff --git a/include/linux/clk/at91_pmc.h b/include/linux/clk/at91_pmc.h
new file mode 100644
index 0000000..17f413b
--- /dev/null
+++ b/include/linux/clk/at91_pmc.h
@@ -0,0 +1,188 @@
+/*
+ * include/linux/clk/at91_pmc.h
+ *
+ * Copyright (C) 2005 Ivan Kokshaysky
+ * Copyright (C) SAN People
+ *
+ * Power Management Controller (PMC) - System peripherals registers.
+ * Based on AT91RM9200 datasheet revision E.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef AT91_PMC_H
+#define AT91_PMC_H
+
+#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_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_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_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_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		(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_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_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_IMR		0x6c			/* Interrupt Mask Register */
+
+#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_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_GCKCSS_OFFSET	8
+#define		AT91_PMC_PCR_GCKCSS_MASK	(0x7  << AT91_PMC_PCR_GCKCSS_OFFSET)
+#define		AT91_PMC_PCR_GCKCSS(n)		((n)  << AT91_PMC_PCR_GCKCSS_OFFSET)	/* GCK Clock Source Selection */
+#define		AT91_PMC_PCR_CMD		(0x1  <<  12)				/* Command (read=0, write=1) */
+#define		AT91_PMC_PCR_DIV_OFFSET		16
+#define		AT91_PMC_PCR_DIV_MASK		(0x3  << AT91_PMC_PCR_DIV_OFFSET)
+#define		AT91_PMC_PCR_DIV(n)		((n)  << AT91_PMC_PCR_DIV_OFFSET)	/* Divisor Value */
+#define		AT91_PMC_PCR_GCKDIV_OFFSET	20
+#define		AT91_PMC_PCR_GCKDIV_MASK	(0xff  << AT91_PMC_PCR_GCKDIV_OFFSET)
+#define		AT91_PMC_PCR_GCKDIV(n)		((n)  << AT91_PMC_PCR_GCKDIV_OFFSET)	/* Generated Clock Divisor Value */
+#define		AT91_PMC_PCR_EN			(0x1  <<  28)				/* Enable */
+#define		AT91_PMC_PCR_GCKEN		(0x1  <<  29)				/* GCK Enable */
+
+#endif
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 04/11] mci: Allow parsing for explicit DT node
  2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
                   ` (2 preceding siblings ...)
  2017-03-14 15:52 ` [PATCH 03/11] clk: at91: Port at91 DT clock code Andrey Smirnov
@ 2017-03-14 15:52 ` Andrey Smirnov
  2017-03-14 15:52 ` [PATCH 05/11] mci: atmel_mci: Add DT support Andrey Smirnov
                   ` (7 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Andrey Smirnov @ 2017-03-14 15:52 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Convert mci_of_parse into mci_of_parse_node, a function that takes
explicit deivce tree node pointer to be used for SD/MMC related
properties extraction. Implement original mci_of_parse as a wrapper
around the call to new function.

This is useful for controllers who specify parameter like bus witdth and
GPIOs as a part of main controller's child nodes (e.g. AT91 SoCs).

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 drivers/mci/mci-core.c | 13 ++++++++-----
 include/mci.h          |  1 +
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 055a5e2..928277a 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1843,20 +1843,18 @@ err_free:
 	return ret;
 }
 
-void mci_of_parse(struct mci_host *host)
+void mci_of_parse_node(struct mci_host *host,
+		       struct device_node *np)
 {
-	struct device_node *np;
 	u32 bus_width;
 	u32 dsr_val;
 
 	if (!IS_ENABLED(CONFIG_OFDEVICE))
 		return;
 
-	if (!host->hw_dev || !host->hw_dev->device_node)
+	if (!host->hw_dev || !np)
 		return;
 
-	np = host->hw_dev->device_node;
-
 	/* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */
 	if (of_property_read_u32(np, "bus-width", &bus_width) < 0) {
 		/* If bus-width is missing we get the driver's default, which
@@ -1897,6 +1895,11 @@ void mci_of_parse(struct mci_host *host)
 	host->non_removable = of_property_read_bool(np, "non-removable");
 }
 
+void mci_of_parse(struct mci_host *host)
+{
+	return mci_of_parse_node(host, host->hw_dev->device_node);
+}
+
 struct mci *mci_get_device_by_name(const char *name)
 {
 	struct mci *mci;
diff --git a/include/mci.h b/include/mci.h
index cc4712c..781e6e0 100644
--- a/include/mci.h
+++ b/include/mci.h
@@ -480,6 +480,7 @@ struct mci {
 
 int mci_register(struct mci_host*);
 void mci_of_parse(struct mci_host *host);
+void mci_of_parse_node(struct mci_host *host, struct device_node *np);
 int mci_detect_card(struct mci_host *);
 int mci_send_ext_csd(struct mci *mci, char *ext_csd);
 int mci_switch(struct mci *mci, unsigned set, unsigned index,
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 05/11] mci: atmel_mci: Add DT support
  2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
                   ` (3 preceding siblings ...)
  2017-03-14 15:52 ` [PATCH 04/11] mci: Allow parsing for explicit DT node Andrey Smirnov
@ 2017-03-14 15:52 ` Andrey Smirnov
  2017-03-14 15:52 ` [PATCH 06/11] spi: atmel_spi: " Andrey Smirnov
                   ` (6 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Andrey Smirnov @ 2017-03-14 15:52 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 drivers/mci/atmel_mci.c | 101 +++++++++++++++++++++++++++++++++---------------
 1 file changed, 70 insertions(+), 31 deletions(-)

diff --git a/drivers/mci/atmel_mci.c b/drivers/mci/atmel_mci.c
index 2a0ddb0..317cf46 100644
--- a/drivers/mci/atmel_mci.c
+++ b/drivers/mci/atmel_mci.c
@@ -24,6 +24,7 @@
 #include <mach/board.h>
 #include <linux/clk.h>
 #include <linux/err.h>
+#include <of_gpio.h>
 
 #include "atmel-mci-regs.h"
 
@@ -53,6 +54,7 @@ struct atmel_mci {
 	u32			cfg_reg;
 	u32			sdc_reg;
 	bool			need_reset;
+	int			detect_pin;
 };
 
 #define to_mci_host(mci)	container_of(mci, struct atmel_mci, mci)
@@ -360,14 +362,13 @@ static int atmci_start_cmd(struct atmel_mci *host, struct mci_cmd *cmd,
 static int atmci_card_present(struct mci_host *mci)
 {
 	struct atmel_mci *host = to_mci_host(mci);
-	struct atmel_mci_platform_data *pd = host->hw_dev->platform_data;
 	int ret;
 
 	/* No gpio, assume card is present */
-	if (!gpio_is_valid(pd->detect_pin))
+	if (!gpio_is_valid(host->detect_pin))
 		return 1;
 
-	ret = gpio_get_value(pd->detect_pin);
+	ret = gpio_get_value(host->detect_pin);
 
 	return ret == 0 ? 1 : 0;
 }
@@ -535,44 +536,71 @@ static int atmci_probe(struct device_d *hw_dev)
 {
 	struct resource *iores;
 	struct atmel_mci *host;
+	struct device_node *np = hw_dev->device_node;
 	struct atmel_mci_platform_data *pd = hw_dev->platform_data;
 	int ret;
 
-	if (!pd) {
-		dev_err(hw_dev, "missing platform data\n");
-		return -EINVAL;
+	host = xzalloc(sizeof(*host));
+	host->mci.send_cmd = atmci_request;
+	host->mci.set_ios = atmci_set_ios;
+	host->mci.init = atmci_reset;
+	host->mci.card_present = atmci_card_present;
+	host->mci.hw_dev = hw_dev;
+	host->detect_pin = -EINVAL;
+
+	if (pd) {
+		host->detect_pin  = pd->detect_pin;
+		host->mci.devname = pd->devname;
+
+		if (pd->bus_width >= 4)
+			host->mci.host_caps |= MMC_CAP_4_BIT_DATA;
+		if (pd->bus_width == 8)
+			host->mci.host_caps |= MMC_CAP_8_BIT_DATA;
+
+		host->slot_b = pd->slot_b;
+	} else if (np) {
+		u32 slot_id;
+		struct device_node *cnp;
+		const char *alias = of_alias_get(np);
+
+		if (alias)
+			host->mci.devname = xstrdup(alias);
+
+		host->detect_pin = of_get_named_gpio(np, "cd-gpios", 0);
+
+		for_each_child_of_node(np, cnp) {
+			if (of_property_read_u32(cnp, "reg", &slot_id)) {
+				dev_warn(hw_dev, "reg property is missing for %s\n",
+					 cnp->full_name);
+				continue;
+			}
+
+			host->slot_b = slot_id;
+			mci_of_parse_node(&host->mci, cnp);
+			break;
+		}
+	} else {
+		dev_err(hw_dev, "Missing device information\n");
+		ret = -EINVAL;
+		goto error_out;
 	}
 
-	if (gpio_is_valid(pd->detect_pin)) {
-		ret = gpio_request(pd->detect_pin, "mci_cd");
+	if (gpio_is_valid(host->detect_pin)) {
+		ret = gpio_request(host->detect_pin, "mci_cd");
 		if (ret) {
 			dev_err(hw_dev, "Impossible to request CD gpio %d (%d)\n",
-				ret, pd->detect_pin);
-			return ret;
+				ret, host->detect_pin);
+			goto error_out;
 		}
 
-		ret = gpio_direction_input(pd->detect_pin);
+		ret = gpio_direction_input(host->detect_pin);
 		if (ret) {
 			dev_err(hw_dev, "Impossible to configure CD gpio %d as input (%d)\n",
-				ret, pd->detect_pin);
-			goto err_gpio_cd_request;
+				ret, host->detect_pin);
+			goto error_out;
 		}
 	}
 
-	host = xzalloc(sizeof(*host));
-	host->mci.send_cmd = atmci_request;
-	host->mci.set_ios = atmci_set_ios;
-	host->mci.init = atmci_reset;
-	host->mci.card_present = atmci_card_present;
-	host->mci.hw_dev = hw_dev;
-	host->mci.devname = pd->devname;
-
-	if (pd->bus_width >= 4)
-		host->mci.host_caps |= MMC_CAP_4_BIT_DATA;
-	if (pd->bus_width == 8)
-		host->mci.host_caps |= MMC_CAP_8_BIT_DATA;
-	host->slot_b = pd->slot_b;
-
 	iores = dev_request_mem_resource(hw_dev, 0);
 	if (IS_ERR(iores))
 		return PTR_ERR(iores);
@@ -583,7 +611,7 @@ static int atmci_probe(struct device_d *hw_dev)
 	if (IS_ERR(host->clk)) {
 		dev_err(hw_dev, "no mci_clk\n");
 		ret = PTR_ERR(host->clk);
-		goto err_gpio_cd_request;
+		goto error_out;
 	}
 
 	clk_enable(host->clk);
@@ -614,15 +642,26 @@ static int atmci_probe(struct device_d *hw_dev)
 
 	return 0;
 
-err_gpio_cd_request:
-	if (gpio_is_valid(pd->detect_pin))
-		gpio_free(pd->detect_pin);
+error_out:
+	free(host);
+
+	if (gpio_is_valid(host->detect_pin))
+		gpio_free(host->detect_pin);
 
 	return ret;
 }
 
+static __maybe_unused struct of_device_id atmci_compatible[] = {
+	{
+		.compatible = "atmel,hsmci",
+	}, {
+		/* sentinel */
+	}
+};
+
 static struct driver_d atmci_driver = {
 	.name	= "atmel_mci",
 	.probe	= atmci_probe,
+	.of_compatible = DRV_OF_COMPAT(atmci_compatible),
 };
 device_platform_driver(atmci_driver);
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 06/11] spi: atmel_spi: Add DT support
  2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
                   ` (4 preceding siblings ...)
  2017-03-14 15:52 ` [PATCH 05/11] mci: atmel_mci: Add DT support Andrey Smirnov
@ 2017-03-14 15:52 ` Andrey Smirnov
  2017-03-15 20:54   ` Sam Ravnborg
  2017-03-14 15:52 ` [PATCH 07/11] w1-gpio: " Andrey Smirnov
                   ` (5 subsequent siblings)
  11 siblings, 1 reply; 15+ messages in thread
From: Andrey Smirnov @ 2017-03-14 15:52 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 drivers/spi/atmel_spi.c | 29 ++++++++++++++++++++++++++---
 1 file changed, 26 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
index ef57867..55032be 100644
--- a/drivers/spi/atmel_spi.c
+++ b/drivers/spi/atmel_spi.c
@@ -29,6 +29,7 @@
 #include <clock.h>
 #include <xfuncs.h>
 #include <gpio.h>
+#include <of_gpio.h>
 #include <io.h>
 #include <spi/spi.h>
 #include <mach/io.h>
@@ -400,10 +401,11 @@ static int atmel_spi_probe(struct device_d *dev)
 	int ret = 0;
 	int i;
 	struct spi_master *master;
+	struct device_node *node = dev->device_node;
 	struct atmel_spi *as;
 	struct at91_spi_platform_data *pdata = dev->platform_data;
 
-	if (!pdata) {
+	if (!IS_ENABLED(CONFIG_OFDEVICE) && !pdata) {
 		dev_err(dev, "missing platform data\n");
 		return -EINVAL;
 	}
@@ -414,6 +416,23 @@ static int atmel_spi_probe(struct device_d *dev)
 	master->dev = dev;
 	master->bus_num = dev->id;
 
+	if (pdata) {
+		master->num_chipselect = pdata->num_chipselect;
+		as->cs_pins = pdata->chipselect;
+	} else {
+		master->num_chipselect = of_gpio_named_count(node, "cs-gpios");
+		as->cs_pins = xzalloc(sizeof(u32) * master->num_chipselect);
+
+		for (i = 0; i < master->num_chipselect; i++) {
+			as->cs_pins[i] = of_get_named_gpio(node, "cs-gpios", i);
+
+			if (!gpio_is_valid(as->cs_pins[i]))
+			    break;
+		}
+
+		master->num_chipselect = i;
+	}
+
 	as->clk = clk_get(dev, "spi_clk");
 	if (IS_ERR(as->clk)) {
 		dev_err(dev, "no spi_clk\n");
@@ -423,8 +442,6 @@ static int atmel_spi_probe(struct device_d *dev)
 
 	master->setup = atmel_spi_setup;
 	master->transfer = atmel_spi_transfer;
-	master->num_chipselect = pdata->num_chipselect;
-	as->cs_pins = pdata->chipselect;
 	iores = dev_request_mem_resource(dev, 0);
 	if (IS_ERR(iores))
 		return PTR_ERR(iores);
@@ -465,8 +482,14 @@ out_free:
 	return ret;
 }
 
+static __maybe_unused const struct of_device_id atmel_spi_dt_ids[] = {
+	{ .compatible = "atmel,at91rm9200-spi" },
+	{ /* sentinel */ }
+};
+
 static struct driver_d atmel_spi_driver = {
 	.name  = "atmel_spi",
 	.probe = atmel_spi_probe,
+	.of_compatible = DRV_OF_COMPAT(atmel_spi_dt_ids),
 };
 device_platform_driver(atmel_spi_driver);
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 07/11] w1-gpio: Add DT support
  2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
                   ` (5 preceding siblings ...)
  2017-03-14 15:52 ` [PATCH 06/11] spi: atmel_spi: " Andrey Smirnov
@ 2017-03-14 15:52 ` Andrey Smirnov
  2017-03-14 15:52 ` [PATCH 08/11] usb: ohci-at91: " Andrey Smirnov
                   ` (4 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Andrey Smirnov @ 2017-03-14 15:52 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 drivers/w1/masters/w1-gpio.c | 53 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 53 insertions(+)

diff --git a/drivers/w1/masters/w1-gpio.c b/drivers/w1/masters/w1-gpio.c
index 946e9d3..916027e 100644
--- a/drivers/w1/masters/w1-gpio.c
+++ b/drivers/w1/masters/w1-gpio.c
@@ -16,6 +16,7 @@
 #include <driver.h>
 #include <linux/w1-gpio.h>
 #include <gpio.h>
+#include <of_gpio.h>
 
 #include "../w1.h"
 
@@ -43,12 +44,58 @@ static u8 w1_gpio_read_bit(struct w1_bus *bus)
 	return gpio_get_value(pdata->pin) ? 1 : 0;
 }
 
+static int w1_gpio_probe_dt(struct device_d *dev)
+{
+	struct w1_gpio_platform_data *pdata;
+	struct device_node *np = dev->device_node;
+	int gpio;
+
+	if (dev->platform_data)
+		return 0;
+
+	pdata = xzalloc(sizeof(*pdata));
+
+	if (of_get_property(np, "linux,open-drain", NULL))
+		pdata->is_open_drain = 1;
+
+	gpio = of_get_gpio(np, 0);
+	if (!gpio_is_valid(gpio)) {
+		if (gpio != -EPROBE_DEFER)
+			dev_err(dev,
+				"Failed to parse gpio property for data pin (%d)\n",
+				gpio);
+
+		goto free_pdata;
+	}
+	pdata->pin = gpio;
+
+	gpio = of_get_gpio(np, 1);
+	if (gpio == -EPROBE_DEFER)
+		goto free_pdata;
+
+	/* ignore other errors as the pullup gpio is optional */
+	pdata->ext_pullup_enable_pin = gpio;
+
+	dev->platform_data = pdata;
+	return 0;
+
+free_pdata:
+	free(pdata);
+	return gpio;
+}
+
 static int __init w1_gpio_probe(struct device_d *dev)
 {
 	struct w1_bus *master;
 	struct w1_gpio_platform_data *pdata;
 	int err;
 
+	if (IS_ENABLED(CONFIG_OFDEVICE)) {
+		err = w1_gpio_probe_dt(dev);
+		if (err < 0)
+			return err;
+	}
+
 	pdata = dev->platform_data;
 
 	if (!pdata)
@@ -104,8 +151,14 @@ static int __init w1_gpio_probe(struct device_d *dev)
 	return err;
 }
 
+static __maybe_unused const struct of_device_id w1_gpio_dt_ids[] = {
+	{ .compatible = "w1-gpio" },
+	{}
+};
+
 static struct driver_d w1_gpio_driver = {
 	.name	= "w1-gpio",
 	.probe	= w1_gpio_probe,
+	.of_compatible = DRV_OF_COMPAT(w1_gpio_dt_ids),
 };
 device_platform_driver(w1_gpio_driver);
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 08/11] usb: ohci-at91: Add DT support
  2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
                   ` (6 preceding siblings ...)
  2017-03-14 15:52 ` [PATCH 07/11] w1-gpio: " Andrey Smirnov
@ 2017-03-14 15:52 ` Andrey Smirnov
  2017-03-14 15:52 ` [PATCH 09/11] usb/host: Allow USB_OHCI_AT91 even if USB_OHCI is disabled Andrey Smirnov
                   ` (3 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Andrey Smirnov @ 2017-03-14 15:52 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 arch/arm/mach-at91/include/mach/board.h |  6 ++-
 drivers/usb/host/ohci-at91.c            | 93 ++++++++++++++++++++++++++++++++-
 2 files changed, 95 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-at91/include/mach/board.h b/arch/arm/mach-at91/include/mach/board.h
index 491b220..5d76e00 100644
--- a/arch/arm/mach-at91/include/mach/board.h
+++ b/arch/arm/mach-at91/include/mach/board.h
@@ -32,11 +32,13 @@
 
 void at91_set_main_clock(unsigned long rate);
 
+#define AT91_MAX_USBH_PORTS	3
+
  /* USB Host */
 struct at91_usbh_data {
 	u8		ports;		/* number of ports on root hub */
-	int		vbus_pin[2];	/* port power-control pin */
-	u8	vbus_pin_active_low[2];	/* vbus polarity */
+	int		vbus_pin[AT91_MAX_USBH_PORTS];	/* port power-control pin */
+	u8	vbus_pin_active_low[AT91_MAX_USBH_PORTS];	/* vbus polarity */
 };
 extern void __init at91_add_device_usbh_ohci(struct at91_usbh_data *data);
 extern void __init at91_add_device_usbh_ehci(struct at91_usbh_data *data);
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 5f74526..1013ba3 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -23,10 +23,18 @@
 #include <usb/usb.h>
 #include <usb/usb_defs.h>
 #include <errno.h>
+#include <gpio.h>
+#include <of_gpio.h>
 #include <io.h>
 
+#include <mach/board.h>
+
 #include "ohci.h"
 
+#define at91_for_each_port(index)					\
+	for ((index) = 0; (index) < AT91_MAX_USBH_PORTS; (index)++)
+
+
 struct ohci_at91_priv {
 	struct device_d *dev;
 	struct clk *iclk;
@@ -59,6 +67,59 @@ static void at91_stop_clock(struct ohci_at91_priv *ohci_at91)
 	clk_disable(ohci_at91->iclk);
 }
 
+static int at91_ohci_probe_dt(struct device_d *dev)
+{
+	u32 ports;
+	int i, ret, gpio;
+	enum of_gpio_flags flags;
+	struct at91_usbh_data *pdata;
+	struct device_node *np = dev->device_node;
+
+	pdata = xzalloc(sizeof(*pdata));
+	dev->platform_data = pdata;
+
+	if (!of_property_read_u32(np, "num-ports", &ports)) {
+		pdata->ports = ports;
+	} else {
+		dev_err(dev, "Failed to read 'num-ports' property\n");
+		return -EINVAL;
+	}
+
+	at91_for_each_port(i) {
+		/*
+		 * do not configure PIO if not in relation with
+		 * real USB port on board
+		 */
+		if (i >= pdata->ports) {
+			pdata->vbus_pin[i] = -EINVAL;
+			continue;
+		}
+
+		gpio = of_get_named_gpio_flags(np, "atmel,vbus-gpio", i,
+					       &flags);
+		pdata->vbus_pin[i] = gpio;
+		if (!gpio_is_valid(gpio))
+			continue;
+		pdata->vbus_pin_active_low[i] = flags & OF_GPIO_ACTIVE_LOW;
+
+		ret = gpio_request(gpio, "ohci_vbus");
+		if (ret) {
+			dev_err(dev, "can't request vbus gpio %d\n", gpio);
+			continue;
+		}
+		ret = gpio_direction_output(gpio,
+					    !pdata->vbus_pin_active_low[i]);
+		if (ret) {
+			dev_err(dev, "can't put vbus gpio %d as output %d\n",
+				gpio, !pdata->vbus_pin_active_low[i]);
+			gpio_free(gpio);
+			continue;
+		}
+	}
+
+	return 0;
+}
+
 static int at91_ohci_probe(struct device_d *dev)
 {
 	int ret;
@@ -68,6 +129,12 @@ static int at91_ohci_probe(struct device_d *dev)
 	dev->priv = ohci_at91;
 	ohci_at91->dev = dev;
 
+	if (dev->device_node) {
+		ret = at91_ohci_probe_dt(dev);
+		if (ret < 0)
+			return ret;
+	}
+
 	io = dev_get_resource(dev, IORESOURCE_MEM, 0);
 	if (IS_ERR(io)) {
 		dev_err(dev, "Failed to get IORESOURCE_MEM\n");
@@ -75,13 +142,13 @@ static int at91_ohci_probe(struct device_d *dev)
 	}
 	ohci_at91->regs = IOMEM(io->start);
 
-	ohci_at91->iclk = clk_get(NULL, "ohci_clk");
+	ohci_at91->iclk = clk_get(dev, "ohci_clk");
 	if (IS_ERR(ohci_at91->iclk)) {
 		dev_err(dev, "Failed to get 'ohci_clk'\n");
 		return PTR_ERR(ohci_at91->iclk);
 	}
 
-	ohci_at91->fclk = clk_get(NULL, "uhpck");
+	ohci_at91->fclk = clk_get(dev, "uhpck");
 	if (IS_ERR(ohci_at91->fclk)) {
 		dev_err(dev, "Failed to get 'uhpck'\n");
 		return PTR_ERR(ohci_at91->fclk);
@@ -107,6 +174,7 @@ static int at91_ohci_probe(struct device_d *dev)
 
 static void at91_ohci_remove(struct device_d *dev)
 {
+	struct at91_usbh_data *pdata = dev->platform_data;
 	struct ohci_at91_priv *ohci_at91 = dev->priv;
 
 	/*
@@ -118,11 +186,32 @@ static void at91_ohci_remove(struct device_d *dev)
 	 * Stop the USB clocks.
 	 */
 	at91_stop_clock(ohci_at91);
+
+	if (pdata) {
+		bool active_low;
+		int  i, gpio;
+
+		at91_for_each_port(i) {
+			gpio = pdata->vbus_pin[i];
+			active_low = pdata->vbus_pin_active_low[i];
+
+			if (gpio_is_valid(gpio)) {
+				gpio_set_value(gpio, active_low);
+				gpio_free(gpio);
+			}
+		}
+	}
 }
 
+static const struct of_device_id at91_ohci_dt_ids[] = {
+	{ .compatible = "atmel,at91rm9200-ohci" },
+	{ /* sentinel */ }
+};
+
 static struct driver_d at91_ohci_driver = {
 	.name = "at91_ohci",
 	.probe = at91_ohci_probe,
 	.remove = at91_ohci_remove,
+	.of_compatible = DRV_OF_COMPAT(at91_ohci_dt_ids),
 };
 device_platform_driver(at91_ohci_driver);
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 09/11] usb/host: Allow USB_OHCI_AT91 even if USB_OHCI is disabled
  2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
                   ` (7 preceding siblings ...)
  2017-03-14 15:52 ` [PATCH 08/11] usb: ohci-at91: " Andrey Smirnov
@ 2017-03-14 15:52 ` Andrey Smirnov
  2017-03-14 15:52 ` [PATCH 10/11] usb: echi-atmel: Add DT support Andrey Smirnov
                   ` (2 subsequent siblings)
  11 siblings, 0 replies; 15+ messages in thread
From: Andrey Smirnov @ 2017-03-14 15:52 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

When probing devices from DT, AT91 SoCs rely on OHCI driver to setup
various GPIO (e.g. VBUS enabled) for EHCI block as well. So enable
USB_OHCI_AT91 to be selected even if USB_OHCI is unavailible (due to its
dependency on !MMU).

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 drivers/usb/host/Kconfig | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index 54eaf46..db44052 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -10,20 +10,17 @@ config USB_EHCI_OMAP
 config USB_EHCI_ATMEL
 	depends on ARCH_AT91
 	depends on USB_EHCI
+	select USB_OHCI_AT91
 	bool "Atmel EHCI driver"
 
 config USB_OHCI
 	bool "OHCI driver"
 	depends on !MMU
 
-if USB_OHCI
-
 config USB_OHCI_AT91
 	depends on ARCH_AT91
 	bool "AT91 OHCI driver"
 
-endif
-
 config USB_XHCI
 	bool "xHCI driver"
 	depends on HAS_DMA
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 10/11] usb: echi-atmel: Add DT support
  2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
                   ` (8 preceding siblings ...)
  2017-03-14 15:52 ` [PATCH 09/11] usb/host: Allow USB_OHCI_AT91 even if USB_OHCI is disabled Andrey Smirnov
@ 2017-03-14 15:52 ` Andrey Smirnov
  2017-03-14 15:52 ` [PATCH 11/11] net: macb: " Andrey Smirnov
  2017-03-15 21:03 ` [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Sam Ravnborg
  11 siblings, 0 replies; 15+ messages in thread
From: Andrey Smirnov @ 2017-03-14 15:52 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 drivers/usb/host/ehci-atmel.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c
index f075b50..1132879 100644
--- a/drivers/usb/host/ehci-atmel.c
+++ b/drivers/usb/host/ehci-atmel.c
@@ -65,6 +65,9 @@ static int atmel_ehci_probe(struct device_d *dev)
 	struct resource *iores;
 	struct ehci_data data;
 	struct atmel_ehci_priv *atehci;
+	const char *uclk_name;
+
+	uclk_name = (dev->device_node) ? "usb_clk" : "uhpck";
 
 	atehci = xzalloc(sizeof(*atehci));
 	atehci->dev = dev;
@@ -76,7 +79,7 @@ static int atmel_ehci_probe(struct device_d *dev)
 		return -ENOENT;
 	}
 
-	atehci->uclk = clk_get(dev, "uhpck");
+	atehci->uclk = clk_get(dev, uclk_name);
 	if (IS_ERR(atehci->iclk)) {
 		dev_err(dev, "Error getting function clock\n");
 		return -ENOENT;
@@ -107,9 +110,15 @@ static void atmel_ehci_remove(struct device_d *dev)
 	atmel_stop_clock(dev->priv);
 }
 
+static const struct of_device_id atmel_ehci_dt_ids[] = {
+	{ .compatible = "atmel,at91sam9g45-ehci" },
+	{ /* sentinel */ }
+};
+
 static struct driver_d atmel_ehci_driver = {
 	.name = "atmel-ehci",
 	.probe = atmel_ehci_probe,
 	.remove = atmel_ehci_remove,
+	.of_compatible = DRV_OF_COMPAT(atmel_ehci_dt_ids),
 };
 device_platform_driver(atmel_ehci_driver);
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 11/11] net: macb: Add DT support
  2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
                   ` (9 preceding siblings ...)
  2017-03-14 15:52 ` [PATCH 10/11] usb: echi-atmel: Add DT support Andrey Smirnov
@ 2017-03-14 15:52 ` Andrey Smirnov
  2017-03-15 21:03 ` [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Sam Ravnborg
  11 siblings, 0 replies; 15+ messages in thread
From: Andrey Smirnov @ 2017-03-14 15:52 UTC (permalink / raw)
  To: barebox; +Cc: Andrey Smirnov

Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
---
 drivers/net/macb.c | 56 ++++++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 42 insertions(+), 14 deletions(-)

diff --git a/drivers/net/macb.c b/drivers/net/macb.c
index 5f2e5e5..36e49c3 100644
--- a/drivers/net/macb.c
+++ b/drivers/net/macb.c
@@ -48,6 +48,7 @@
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/phy.h>
+#include <of_net.h>
 
 #include "macb.h"
 
@@ -615,14 +616,8 @@ static int macb_probe(struct device_d *dev)
 	struct resource *iores;
 	struct eth_device *edev;
 	struct macb_device *macb;
+	const char *pclk_name;
 	u32 ncfgr;
-	struct macb_platform_data *pdata;
-
-	if (!dev->platform_data) {
-		dev_err(dev, "macb: no platform_data\n");
-		return -ENODEV;
-	}
-	pdata = dev->platform_data;
 
 	edev = xzalloc(sizeof(struct eth_device) + sizeof(struct macb_device));
 	edev->priv = (struct macb_device *)(edev + 1);
@@ -633,22 +628,49 @@ static int macb_probe(struct device_d *dev)
 	edev->open = macb_open;
 	edev->send = macb_send;
 	edev->halt = macb_halt;
-	edev->get_ethaddr = pdata->get_ethaddr ? pdata->get_ethaddr : macb_get_ethaddr;
+	edev->get_ethaddr = macb_get_ethaddr;
 	edev->set_ethaddr = macb_set_ethaddr;
 	edev->parent = dev;
 
 	macb->miibus.read = macb_phy_read;
 	macb->miibus.write = macb_phy_write;
-	macb->phy_addr = pdata->phy_addr;
 	macb->miibus.priv = macb;
 	macb->miibus.parent = dev;
 
-	if (pdata->phy_interface == PHY_INTERFACE_MODE_NA)
-		macb->interface = PHY_INTERFACE_MODE_MII;
-	else
-		macb->interface = pdata->phy_interface;
+	if (dev->platform_data) {
+		struct macb_platform_data *pdata = dev->platform_data;
+
+		if (pdata->phy_interface == PHY_INTERFACE_MODE_NA)
+			macb->interface = PHY_INTERFACE_MODE_MII;
+		else
+			macb->interface = pdata->phy_interface;
+
+		if (pdata->get_ethaddr)
+			edev->get_ethaddr = pdata->get_ethaddr;
+
+		macb->phy_addr = pdata->phy_addr;
+		macb->phy_flags = pdata->phy_flags;
+		pclk_name = "macb_clk";
+	} else if (dev->device_node) {
+		int ret;
+		struct device_node *mdiobus;
 
-	macb->phy_flags = pdata->phy_flags;
+		ret = of_get_phy_mode(dev->device_node);
+		if (ret < 0)
+			macb->interface = PHY_INTERFACE_MODE_MII;
+		else
+			macb->interface = ret;
+
+		mdiobus = of_get_child_by_name(dev->device_node, "mdio");
+		if (mdiobus)
+			macb->miibus.dev.device_node = mdiobus;
+
+		macb->phy_addr = -1;
+		pclk_name = NULL;
+	} else {
+		dev_err(dev, "macb: no platform_data\n");
+		return -ENODEV;
+	}
 
 	iores = dev_request_mem_resource(dev, 0);
 	if (IS_ERR(iores))
@@ -698,8 +720,14 @@ static int macb_probe(struct device_d *dev)
 	return 0;
 }
 
+static const struct of_device_id macb_dt_ids[] = {
+	{ .compatible = "cdns,at91sam9260-macb",},
+	{ /* sentinel */ }
+};
+
 static struct driver_d macb_driver = {
 	.name  = "macb",
 	.probe = macb_probe,
+	.of_compatible = DRV_OF_COMPAT(macb_dt_ids),
 };
 device_platform_driver(macb_driver);
-- 
2.9.3


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH 01/11] clocksource: at91: Add DT compatibility table
  2017-03-14 15:52 ` [PATCH 01/11] clocksource: at91: Add DT compatibility table Andrey Smirnov
@ 2017-03-15 20:33   ` Sam Ravnborg
  0 siblings, 0 replies; 15+ messages in thread
From: Sam Ravnborg @ 2017-03-15 20:33 UTC (permalink / raw)
  To: Andrey Smirnov; +Cc: barebox

On Tue, Mar 14, 2017 at 08:52:12AM -0700, Andrey Smirnov wrote:
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
>  drivers/clocksource/timer-atmel-pit.c | 9 +++++++++
>  1 file changed, 9 insertions(+)
> 
> diff --git a/drivers/clocksource/timer-atmel-pit.c b/drivers/clocksource/timer-atmel-pit.c
> index cc7ad2f..d59efa6 100644
> --- a/drivers/clocksource/timer-atmel-pit.c
> +++ b/drivers/clocksource/timer-atmel-pit.c
> @@ -102,9 +102,18 @@ static int at91_pit_probe(struct device_d *dev)
>  	return init_clock(&cs);
>  }
>  
> +static __maybe_unused struct of_device_id at91_pit_dt_ids[] = {

should this be const?

	Sam

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH 06/11] spi: atmel_spi: Add DT support
  2017-03-14 15:52 ` [PATCH 06/11] spi: atmel_spi: " Andrey Smirnov
@ 2017-03-15 20:54   ` Sam Ravnborg
  0 siblings, 0 replies; 15+ messages in thread
From: Sam Ravnborg @ 2017-03-15 20:54 UTC (permalink / raw)
  To: Andrey Smirnov; +Cc: barebox

On Tue, Mar 14, 2017 at 08:52:17AM -0700, Andrey Smirnov wrote:
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
> ---
>  drivers/spi/atmel_spi.c | 29 ++++++++++++++++++++++++++---
>  1 file changed, 26 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c
> index ef57867..55032be 100644
> --- a/drivers/spi/atmel_spi.c
> +++ b/drivers/spi/atmel_spi.c
> @@ -29,6 +29,7 @@
>  #include <clock.h>
>  #include <xfuncs.h>
>  #include <gpio.h>
> +#include <of_gpio.h>
>  #include <io.h>
>  #include <spi/spi.h>
>  #include <mach/io.h>
> @@ -400,10 +401,11 @@ static int atmel_spi_probe(struct device_d *dev)
>  	int ret = 0;
>  	int i;
>  	struct spi_master *master;
> +	struct device_node *node = dev->device_node;
>  	struct atmel_spi *as;
>  	struct at91_spi_platform_data *pdata = dev->platform_data;
>  
> -	if (!pdata) {
> +	if (!IS_ENABLED(CONFIG_OFDEVICE) && !pdata) {
>  		dev_err(dev, "missing platform data\n");
>  		return -EINVAL;
>  	}
> @@ -414,6 +416,23 @@ static int atmel_spi_probe(struct device_d *dev)
>  	master->dev = dev;
>  	master->bus_num = dev->id;
>  
> +	if (pdata) {
> +		master->num_chipselect = pdata->num_chipselect;
> +		as->cs_pins = pdata->chipselect;
> +	} else {
> +		master->num_chipselect = of_gpio_named_count(node, "cs-gpios");
> +		as->cs_pins = xzalloc(sizeof(u32) * master->num_chipselect);
> +
> +		for (i = 0; i < master->num_chipselect; i++) {
> +			as->cs_pins[i] = of_get_named_gpio(node, "cs-gpios", i);
> +
> +			if (!gpio_is_valid(as->cs_pins[i]))
> +			    break;
> +		}
> +
> +		master->num_chipselect = i;
> +	}
> +
>  	as->clk = clk_get(dev, "spi_clk");
>  	if (IS_ERR(as->clk)) {
>  		dev_err(dev, "no spi_clk\n");
> @@ -423,8 +442,6 @@ static int atmel_spi_probe(struct device_d *dev)
>  
>  	master->setup = atmel_spi_setup;
>  	master->transfer = atmel_spi_transfer;
> -	master->num_chipselect = pdata->num_chipselect;
> -	as->cs_pins = pdata->chipselect;
>  	iores = dev_request_mem_resource(dev, 0);
>  	if (IS_ERR(iores))
>  		return PTR_ERR(iores);
> @@ -465,8 +482,14 @@ out_free:
>  	return ret;
>  }
>  
> +static __maybe_unused const struct of_device_id atmel_spi_dt_ids[] = {
const?

	Sam


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III)
  2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
                   ` (10 preceding siblings ...)
  2017-03-14 15:52 ` [PATCH 11/11] net: macb: " Andrey Smirnov
@ 2017-03-15 21:03 ` Sam Ravnborg
  11 siblings, 0 replies; 15+ messages in thread
From: Sam Ravnborg @ 2017-03-15 21:03 UTC (permalink / raw)
  To: Andrey Smirnov; +Cc: barebox

On Tue, Mar 14, 2017 at 08:52:11AM -0700, Andrey Smirnov wrote:
> Hi everone,
> 
> This is the second batch of AT91 related patches (original thread
> [1]). The patches gathered in this set are with a few exceptions are
> patches adding DT probing support for various AT91 drivers.
> 
> Feedback from Sam has been incorporated, but other than that the code
> should be as it was in [1].

Everything looked good to me - except two small nits about missing const.

Feel free to add my:
Acked-by: Sam Ravnborg <sam@ravnborg.org>

	Sam

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

end of thread, other threads:[~2017-03-15 21:04 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-14 15:52 [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Andrey Smirnov
2017-03-14 15:52 ` [PATCH 01/11] clocksource: at91: Add DT compatibility table Andrey Smirnov
2017-03-15 20:33   ` Sam Ravnborg
2017-03-14 15:52 ` [PATCH 02/11] serial: atmel: " Andrey Smirnov
2017-03-14 15:52 ` [PATCH 03/11] clk: at91: Port at91 DT clock code Andrey Smirnov
2017-03-14 15:52 ` [PATCH 04/11] mci: Allow parsing for explicit DT node Andrey Smirnov
2017-03-14 15:52 ` [PATCH 05/11] mci: atmel_mci: Add DT support Andrey Smirnov
2017-03-14 15:52 ` [PATCH 06/11] spi: atmel_spi: " Andrey Smirnov
2017-03-15 20:54   ` Sam Ravnborg
2017-03-14 15:52 ` [PATCH 07/11] w1-gpio: " Andrey Smirnov
2017-03-14 15:52 ` [PATCH 08/11] usb: ohci-at91: " Andrey Smirnov
2017-03-14 15:52 ` [PATCH 09/11] usb/host: Allow USB_OHCI_AT91 even if USB_OHCI is disabled Andrey Smirnov
2017-03-14 15:52 ` [PATCH 10/11] usb: echi-atmel: Add DT support Andrey Smirnov
2017-03-14 15:52 ` [PATCH 11/11] net: macb: " Andrey Smirnov
2017-03-15 21:03 ` [PATCH 00/11] AT91, at91sam9x5ek updates (part II/III) Sam Ravnborg

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox