diff mbox series

[U-Boot,v4,12/13] clk: Port Linux common clock framework[CCF] for imx6q to U-boot (tag: 5.0-rc3)

Message ID 20190516221042.3583-13-lukma@denx.de
State Awaiting Upstream
Delegated to: Stefano Babic
Headers show
Series clk: Port Linux common clock framework[CCF] to U-boot (tag: 5.0-rc3) | expand

Commit Message

Lukasz Majewski May 16, 2019, 10:10 p.m. UTC
This commit brings the files from Linux kernel to provide clocks support
as it is used on the Linux kernel with common clock framework [CCF] setup.

The directory structure has been preserved. The ported code only supports
reading information from PLL, MUX, Divider, etc and enabling/disabling
the clocks USDHCx/ECSPIx depending on used bus. Moreover, it is agnostic
to the alias numbering as the information about the clock is read from
device tree.

One needs to pay attention to the comments indicating necessary for U-boot's
DM changes.

If needed the code can be extended to support the "set" part of the clock
management.


Signed-off-by: Lukasz Majewski <lukma@denx.de>
---

Changes in v4:
- Port some more Linux code to facilitate imx8 code porting (most notably
  flags)
- Explicitly use container_of() based macro to provide struct clk in various
  places (e.g. gate2, mux, etc)
  Following patches has been squashed:
  http://patchwork.ozlabs.org/patch/1093141/
  http://patchwork.ozlabs.org/patch/1093142/
  http://patchwork.ozlabs.org/patch/1093146/

Changes in v3: None

 drivers/clk/Kconfig            |  14 ++++
 drivers/clk/Makefile           |   2 +
 drivers/clk/clk-divider.c      | 148 ++++++++++++++++++++++++++++++++++
 drivers/clk/clk-fixed-factor.c |  87 ++++++++++++++++++++
 drivers/clk/clk-mux.c          | 164 +++++++++++++++++++++++++++++++++++++
 drivers/clk/clk.c              |  56 +++++++++++++
 drivers/clk/imx/Kconfig        |   9 +++
 drivers/clk/imx/Makefile       |   2 +
 drivers/clk/imx/clk-gate2.c    | 113 ++++++++++++++++++++++++++
 drivers/clk/imx/clk-imx6q.c    | 179 +++++++++++++++++++++++++++++++++++++++++
 drivers/clk/imx/clk-pfd.c      |  91 +++++++++++++++++++++
 drivers/clk/imx/clk-pllv3.c    |  83 +++++++++++++++++++
 drivers/clk/imx/clk.h          |  75 +++++++++++++++++
 include/linux/clk-provider.h   | 115 ++++++++++++++++++++++++++
 14 files changed, 1138 insertions(+)
 create mode 100644 drivers/clk/clk-divider.c
 create mode 100644 drivers/clk/clk-fixed-factor.c
 create mode 100644 drivers/clk/clk-mux.c
 create mode 100644 drivers/clk/clk.c
 create mode 100644 drivers/clk/imx/clk-gate2.c
 create mode 100644 drivers/clk/imx/clk-imx6q.c
 create mode 100644 drivers/clk/imx/clk-pfd.c
 create mode 100644 drivers/clk/imx/clk-pllv3.c
 create mode 100644 drivers/clk/imx/clk.h
 create mode 100644 include/linux/clk-provider.h

Comments

Bin Meng May 17, 2019, 2:48 a.m. UTC | #1
Hi Lukasz,

On Fri, May 17, 2019 at 6:16 AM Lukasz Majewski <lukma@denx.de> wrote:
>
> This commit brings the files from Linux kernel to provide clocks support
> as it is used on the Linux kernel with common clock framework [CCF] setup.
>
> The directory structure has been preserved. The ported code only supports
> reading information from PLL, MUX, Divider, etc and enabling/disabling
> the clocks USDHCx/ECSPIx depending on used bus. Moreover, it is agnostic
> to the alias numbering as the information about the clock is read from
> device tree.
>
> One needs to pay attention to the comments indicating necessary for U-boot's

nits: it's U-Boot. Please fix this globally in the commit message, as
well as in the code comments in this commit.

> DM changes.
>
> If needed the code can be extended to support the "set" part of the clock
> management.
>
>
> Signed-off-by: Lukasz Majewski <lukma@denx.de>
> ---
>
> Changes in v4:
> - Port some more Linux code to facilitate imx8 code porting (most notably
>   flags)
> - Explicitly use container_of() based macro to provide struct clk in various
>   places (e.g. gate2, mux, etc)
>   Following patches has been squashed:
>   http://patchwork.ozlabs.org/patch/1093141/
>   http://patchwork.ozlabs.org/patch/1093142/
>   http://patchwork.ozlabs.org/patch/1093146/
>
> Changes in v3: None
>
>  drivers/clk/Kconfig            |  14 ++++
>  drivers/clk/Makefile           |   2 +
>  drivers/clk/clk-divider.c      | 148 ++++++++++++++++++++++++++++++++++
>  drivers/clk/clk-fixed-factor.c |  87 ++++++++++++++++++++
>  drivers/clk/clk-mux.c          | 164 +++++++++++++++++++++++++++++++++++++
>  drivers/clk/clk.c              |  56 +++++++++++++
>  drivers/clk/imx/Kconfig        |   9 +++
>  drivers/clk/imx/Makefile       |   2 +
>  drivers/clk/imx/clk-gate2.c    | 113 ++++++++++++++++++++++++++
>  drivers/clk/imx/clk-imx6q.c    | 179 +++++++++++++++++++++++++++++++++++++++++
>  drivers/clk/imx/clk-pfd.c      |  91 +++++++++++++++++++++
>  drivers/clk/imx/clk-pllv3.c    |  83 +++++++++++++++++++
>  drivers/clk/imx/clk.h          |  75 +++++++++++++++++
>  include/linux/clk-provider.h   | 115 ++++++++++++++++++++++++++
>  14 files changed, 1138 insertions(+)
>  create mode 100644 drivers/clk/clk-divider.c
>  create mode 100644 drivers/clk/clk-fixed-factor.c
>  create mode 100644 drivers/clk/clk-mux.c
>  create mode 100644 drivers/clk/clk.c
>  create mode 100644 drivers/clk/imx/clk-gate2.c
>  create mode 100644 drivers/clk/imx/clk-imx6q.c
>  create mode 100644 drivers/clk/imx/clk-pfd.c
>  create mode 100644 drivers/clk/imx/clk-pllv3.c
>  create mode 100644 drivers/clk/imx/clk.h
>  create mode 100644 include/linux/clk-provider.h
>

Regards,
Bin
Adam Ford May 17, 2019, 12:31 p.m. UTC | #2
On Thu, May 16, 2019 at 9:48 PM Bin Meng <bmeng.cn@gmail.com> wrote:
>
> Hi Lukasz,
>
> On Fri, May 17, 2019 at 6:16 AM Lukasz Majewski <lukma@denx.de> wrote:
> >
> > This commit brings the files from Linux kernel to provide clocks support
> > as it is used on the Linux kernel with common clock framework [CCF] setup.
> >


Two issues / questions:

1)  I tried to test this on the imx6q_logic board, and I enabled
CLK_IMX6Q in the menu config, but when I save the defconfig, it
appears there are unmet dependencies.

WARNING: unmet direct dependencies detected for SPL_CLK_CCF
  Depends on [n]: SPL_CLK [=n]
  Selected by [y]:
  - CLK_IMX6Q [=y] && ARCH_MX6 [=y]

WARNING: unmet direct dependencies detected for SPL_CLK_CCF
  Depends on [n]: SPL_CLK [=n]
  Selected by [y]:
  - CLK_IMX6Q [=y] && ARCH_MX6 [=y]

you may want to consider implying the correct functions if ARCH_MX6 &&
CLK_IMX6Q or select the proper SPL_CLK info.  My board doesn't do DM
in SPL right now, but I assume the CLK driver you ported is using DM.

2)  I didn't attempt to load the SPL portion, but when I loaded the
u-boot-dtb.img file, it didn't appear to boot.


U-Boot SPL 2019.01 (May 13 2019 - 16:57:41 -0500)
Trying to boot from MMC1
spl_load_image_fat_os: error reading image args, err - -2


(hang)

Other than enabling CLK_IMX6Q, are there other items I need to enable?
 My board has OF_CONTROL in U-Boot with DM support working in the
U-Boot portion.

adam
> > The directory structure has been preserved. The ported code only supports
> > reading information from PLL, MUX, Divider, etc and enabling/disabling
> > the clocks USDHCx/ECSPIx depending on used bus. Moreover, it is agnostic
> > to the alias numbering as the information about the clock is read from
> > device tree.
> >
> > One needs to pay attention to the comments indicating necessary for U-boot's
>
> nits: it's U-Boot. Please fix this globally in the commit message, as
> well as in the code comments in this commit.
>
> > DM changes.
> >
> > If needed the code can be extended to support the "set" part of the clock
> > management.
> >
> >
> > Signed-off-by: Lukasz Majewski <lukma@denx.de>
> > ---
> >
> > Changes in v4:
> > - Port some more Linux code to facilitate imx8 code porting (most notably
> >   flags)
> > - Explicitly use container_of() based macro to provide struct clk in various
> >   places (e.g. gate2, mux, etc)
> >   Following patches has been squashed:
> >   http://patchwork.ozlabs.org/patch/1093141/
> >   http://patchwork.ozlabs.org/patch/1093142/
> >   http://patchwork.ozlabs.org/patch/1093146/
> >
> > Changes in v3: None
> >
> >  drivers/clk/Kconfig            |  14 ++++
> >  drivers/clk/Makefile           |   2 +
> >  drivers/clk/clk-divider.c      | 148 ++++++++++++++++++++++++++++++++++
> >  drivers/clk/clk-fixed-factor.c |  87 ++++++++++++++++++++
> >  drivers/clk/clk-mux.c          | 164 +++++++++++++++++++++++++++++++++++++
> >  drivers/clk/clk.c              |  56 +++++++++++++
> >  drivers/clk/imx/Kconfig        |   9 +++
> >  drivers/clk/imx/Makefile       |   2 +
> >  drivers/clk/imx/clk-gate2.c    | 113 ++++++++++++++++++++++++++
> >  drivers/clk/imx/clk-imx6q.c    | 179 +++++++++++++++++++++++++++++++++++++++++
> >  drivers/clk/imx/clk-pfd.c      |  91 +++++++++++++++++++++
> >  drivers/clk/imx/clk-pllv3.c    |  83 +++++++++++++++++++
> >  drivers/clk/imx/clk.h          |  75 +++++++++++++++++
> >  include/linux/clk-provider.h   | 115 ++++++++++++++++++++++++++
> >  14 files changed, 1138 insertions(+)
> >  create mode 100644 drivers/clk/clk-divider.c
> >  create mode 100644 drivers/clk/clk-fixed-factor.c
> >  create mode 100644 drivers/clk/clk-mux.c
> >  create mode 100644 drivers/clk/clk.c
> >  create mode 100644 drivers/clk/imx/clk-gate2.c
> >  create mode 100644 drivers/clk/imx/clk-imx6q.c
> >  create mode 100644 drivers/clk/imx/clk-pfd.c
> >  create mode 100644 drivers/clk/imx/clk-pllv3.c
> >  create mode 100644 drivers/clk/imx/clk.h
> >  create mode 100644 include/linux/clk-provider.h
> >
>
> Regards,
> Bin
Lukasz Majewski May 17, 2019, 1:19 p.m. UTC | #3
On Fri, 17 May 2019 07:31:38 -0500
Adam Ford <aford173@gmail.com> wrote:

> On Thu, May 16, 2019 at 9:48 PM Bin Meng <bmeng.cn@gmail.com> wrote:
> >
> > Hi Lukasz,
> >
> > On Fri, May 17, 2019 at 6:16 AM Lukasz Majewski <lukma@denx.de>
> > wrote:  
> > >
> > > This commit brings the files from Linux kernel to provide clocks
> > > support as it is used on the Linux kernel with common clock
> > > framework [CCF] setup. 
> 
> 
> Two issues / questions:
> 
> 1)  I tried to test this on the imx6q_logic board, and I enabled
> CLK_IMX6Q in the menu config, but when I save the defconfig, it
> appears there are unmet dependencies.
> 
> WARNING: unmet direct dependencies detected for SPL_CLK_CCF
>   Depends on [n]: SPL_CLK [=n]
>   Selected by [y]:
>   - CLK_IMX6Q [=y] && ARCH_MX6 [=y]
> 
> WARNING: unmet direct dependencies detected for SPL_CLK_CCF
>   Depends on [n]: SPL_CLK [=n]
>   Selected by [y]:
>   - CLK_IMX6Q [=y] && ARCH_MX6 [=y]
> 
> you may want to consider implying the correct functions if ARCH_MX6 &&
> CLK_IMX6Q or select the proper SPL_CLK info.  My board doesn't do DM
> in SPL right now, but I assume the CLK driver you ported is using DM.

Ok. I will look into the Kconfig options.

> 
> 2)  I didn't attempt to load the SPL portion, but when I loaded the
> u-boot-dtb.img file, it didn't appear to boot.
> 
> 
> U-Boot SPL 2019.01 (May 13 2019 - 16:57:41 -0500)
> Trying to boot from MMC1
> spl_load_image_fat_os: error reading image args, err - -2
> 
> 
> (hang)
> 
> Other than enabling CLK_IMX6Q, are there other items I need to enable?
>  My board has OF_CONTROL in U-Boot with DM support working in the
> U-Boot portion.
> 

The CCF shall work also with a setup where it is enabled only in u-boot
proper (CONFIG_CLK_CCF).

Please look into following TPC DM/DTS port as a reference:
https://github.com/lmajewski/u-boot-dfu/commits/DM-SPL-TPC70

This board is fully converted and uses DM/DTS in SPL and u-boot proper
(with CCF).

> adam
> > > The directory structure has been preserved. The ported code only
> > > supports reading information from PLL, MUX, Divider, etc and
> > > enabling/disabling the clocks USDHCx/ECSPIx depending on used
> > > bus. Moreover, it is agnostic to the alias numbering as the
> > > information about the clock is read from device tree.
> > >
> > > One needs to pay attention to the comments indicating necessary
> > > for U-boot's  
> >
> > nits: it's U-Boot. Please fix this globally in the commit message,
> > as well as in the code comments in this commit.
> >  
> > > DM changes.
> > >
> > > If needed the code can be extended to support the "set" part of
> > > the clock management.
> > >
> > >
> > > Signed-off-by: Lukasz Majewski <lukma@denx.de>
> > > ---
> > >
> > > Changes in v4:
> > > - Port some more Linux code to facilitate imx8 code porting (most
> > > notably flags)
> > > - Explicitly use container_of() based macro to provide struct clk
> > > in various places (e.g. gate2, mux, etc)
> > >   Following patches has been squashed:
> > >   http://patchwork.ozlabs.org/patch/1093141/
> > >   http://patchwork.ozlabs.org/patch/1093142/
> > >   http://patchwork.ozlabs.org/patch/1093146/
> > >
> > > Changes in v3: None
> > >
> > >  drivers/clk/Kconfig            |  14 ++++
> > >  drivers/clk/Makefile           |   2 +
> > >  drivers/clk/clk-divider.c      | 148
> > > ++++++++++++++++++++++++++++++++++ drivers/clk/clk-fixed-factor.c
> > > |  87 ++++++++++++++++++++ drivers/clk/clk-mux.c          | 164
> > > +++++++++++++++++++++++++++++++++++++
> > > drivers/clk/clk.c              |  56 +++++++++++++
> > > drivers/clk/imx/Kconfig        |   9 +++
> > > drivers/clk/imx/Makefile       |   2 +
> > > drivers/clk/imx/clk-gate2.c    | 113 ++++++++++++++++++++++++++
> > > drivers/clk/imx/clk-imx6q.c    | 179
> > > +++++++++++++++++++++++++++++++++++++++++
> > > drivers/clk/imx/clk-pfd.c      |  91 +++++++++++++++++++++
> > > drivers/clk/imx/clk-pllv3.c    |  83 +++++++++++++++++++
> > > drivers/clk/imx/clk.h          |  75 +++++++++++++++++
> > > include/linux/clk-provider.h   | 115 ++++++++++++++++++++++++++
> > > 14 files changed, 1138 insertions(+) create mode 100644
> > > drivers/clk/clk-divider.c create mode 100644
> > > drivers/clk/clk-fixed-factor.c create mode 100644
> > > drivers/clk/clk-mux.c create mode 100644 drivers/clk/clk.c create
> > > mode 100644 drivers/clk/imx/clk-gate2.c create mode 100644
> > > drivers/clk/imx/clk-imx6q.c create mode 100644
> > > drivers/clk/imx/clk-pfd.c create mode 100644
> > > drivers/clk/imx/clk-pllv3.c create mode 100644
> > > drivers/clk/imx/clk.h create mode 100644
> > > include/linux/clk-provider.h 
> >
> > Regards,
> > Bin  




Best regards,

Lukasz Majewski

--

DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de
Stefano Babic June 10, 2019, 9:40 a.m. UTC | #4
> This commit brings the files from Linux kernel to provide clocks support
> as it is used on the Linux kernel with common clock framework [CCF] setup.
> The directory structure has been preserved. The ported code only supports
> reading information from PLL, MUX, Divider, etc and enabling/disabling
> the clocks USDHCx/ECSPIx depending on used bus. Moreover, it is agnostic
> to the alias numbering as the information about the clock is read from
> device tree.
> One needs to pay attention to the comments indicating necessary for U-boot's
> DM changes.
> If needed the code can be extended to support the "set" part of the clock
> management.
> Signed-off-by: Lukasz Majewski <lukma@denx.de>

Applied to u-boot-imx, master, thanks !

Best regards,
Stefano Babic
Lukasz Majewski June 10, 2019, 9:36 p.m. UTC | #5
Hi Stefano,

> > This commit brings the files from Linux kernel to provide clocks
> > support as it is used on the Linux kernel with common clock
> > framework [CCF] setup. The directory structure has been preserved.
> > The ported code only supports reading information from PLL, MUX,
> > Divider, etc and enabling/disabling the clocks USDHCx/ECSPIx
> > depending on used bus. Moreover, it is agnostic to the alias
> > numbering as the information about the clock is read from device
> > tree. One needs to pay attention to the comments indicating
> > necessary for U-boot's DM changes.
> > If needed the code can be extended to support the "set" part of the
> > clock management.
> > Signed-off-by: Lukasz Majewski <lukma@denx.de>  
> 
> Applied to u-boot-imx, master, thanks !

Thanks for pulling this code, but the v4 is not the final one.

Simon had some comments regarding the overall design and architecture
of this port. Unfortunately I did not have time to address them yet.

I'm planning to do it ASAP, but IMHO it would be best to drop this
series (so the final one would be applied from the outset).

Sorry for trouble.

> 
> Best regards,
> Stefano Babic
> 




Best regards,

Lukasz Majewski

--

DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-59 Fax: (+49)-8142-66989-80 Email: lukma@denx.de
Stefano Babic June 11, 2019, 8:29 a.m. UTC | #6
On 10/06/19 23:36, Lukasz Majewski wrote:
> Hi Stefano,
> 
>>> This commit brings the files from Linux kernel to provide clocks
>>> support as it is used on the Linux kernel with common clock
>>> framework [CCF] setup. The directory structure has been preserved.
>>> The ported code only supports reading information from PLL, MUX,
>>> Divider, etc and enabling/disabling the clocks USDHCx/ECSPIx
>>> depending on used bus. Moreover, it is agnostic to the alias
>>> numbering as the information about the clock is read from device
>>> tree. One needs to pay attention to the comments indicating
>>> necessary for U-boot's DM changes.
>>> If needed the code can be extended to support the "set" part of the
>>> clock management.
>>> Signed-off-by: Lukasz Majewski <lukma@denx.de>  
>>
>> Applied to u-boot-imx, master, thanks !
> 
> Thanks for pulling this code, but the v4 is not the final one.
> 
> Simon had some comments regarding the overall design and architecture
> of this port. Unfortunately I did not have time to address them yet.
> 

Ouch...I have not seen this, my mistake !

> I'm planning to do it ASAP, but IMHO it would be best to drop this
> series (so the final one would be applied from the outset).
> 

Right - I will drop the series, even if this means a rebase for u-boot-imx.

> Sorry for trouble.
> 

Best regards,
Stefano Babic
diff mbox series

Patch

diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 96969b9e30..344cd4415c 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -46,6 +46,20 @@  config CLK_BOSTON
 	help
 	  Enable this to support the clocks
 
+config SPL_CLK_CCF
+	bool "SPL Common Clock Framework [CCF] support "
+	depends on SPL_CLK
+	help
+	  Enable this option if you want to (re-)use the Linux kernel's Common
+	  Clock Framework [CCF] code in U-Boot's SPL.
+
+config CLK_CCF
+	bool "Common Clock Framework [CCF] support "
+	depends on CLK
+	help
+	  Enable this option if you want to (re-)use the Linux kernel's Common
+	  Clock Framework [CCF] code in U-Boot's clock driver.
+
 config CLK_STM32F
 	bool "Enable clock driver support for STM32F family"
 	depends on CLK && (STM32F7 || STM32F4)
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 719b9b8e02..1a45bf3505 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -7,6 +7,8 @@ 
 obj-$(CONFIG_$(SPL_TPL_)CLK) += clk-uclass.o
 obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_rate.o
 obj-$(CONFIG_$(SPL_TPL_)CLK) += clk_fixed_factor.o
+obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk.o clk-divider.o clk-mux.o
+obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-fixed-factor.o
 
 obj-y += imx/
 obj-y += tegra/
diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c
new file mode 100644
index 0000000000..1d2c1b1ec4
--- /dev/null
+++ b/drivers/clk/clk-divider.c
@@ -0,0 +1,148 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <linux/clk-provider.h>
+#include <div64.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_CCF_DIVIDER "ccf_clk_divider"
+
+static unsigned int _get_table_div(const struct clk_div_table *table,
+				   unsigned int val)
+{
+	const struct clk_div_table *clkt;
+
+	for (clkt = table; clkt->div; clkt++)
+		if (clkt->val == val)
+			return clkt->div;
+	return 0;
+}
+
+static unsigned int _get_div(const struct clk_div_table *table,
+			     unsigned int val, unsigned long flags, u8 width)
+{
+	if (flags & CLK_DIVIDER_ONE_BASED)
+		return val;
+	if (flags & CLK_DIVIDER_POWER_OF_TWO)
+		return 1 << val;
+	if (flags & CLK_DIVIDER_MAX_AT_ZERO)
+		return val ? val : clk_div_mask(width) + 1;
+	if (table)
+		return _get_table_div(table, val);
+	return val + 1;
+}
+
+unsigned long divider_recalc_rate(struct clk *hw, unsigned long parent_rate,
+				  unsigned int val,
+				  const struct clk_div_table *table,
+				  unsigned long flags, unsigned long width)
+{
+	unsigned int div;
+
+	div = _get_div(table, val, flags, width);
+	if (!div) {
+		WARN(!(flags & CLK_DIVIDER_ALLOW_ZERO),
+		     "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n",
+		     clk_hw_get_name(hw));
+		return parent_rate;
+	}
+
+	return DIV_ROUND_UP_ULL((u64)parent_rate, div);
+}
+
+static ulong clk_divider_recalc_rate(struct clk *clk)
+{
+	struct clk_divider *divider =
+		to_clk_divider((struct clk *)dev_get_driver_data(clk->dev));
+	unsigned long parent_rate = clk_get_parent_rate(clk);
+	unsigned int val;
+
+	val = readl(divider->reg) >> divider->shift;
+	val &= clk_div_mask(divider->width);
+
+	return divider_recalc_rate(clk, parent_rate, val, divider->table,
+				   divider->flags, divider->width);
+}
+
+const struct clk_ops clk_divider_ops = {
+	.get_rate = clk_divider_recalc_rate,
+};
+
+static struct clk *_register_divider(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		u8 clk_divider_flags, const struct clk_div_table *table)
+{
+	struct clk_divider *div;
+	struct clk *clk;
+	int ret;
+
+	if (clk_divider_flags & CLK_DIVIDER_HIWORD_MASK) {
+		if (width + shift > 16) {
+			pr_warn("divider value exceeds LOWORD field\n");
+			return ERR_PTR(-EINVAL);
+		}
+	}
+
+	/* allocate the divider */
+	div = kzalloc(sizeof(*div), GFP_KERNEL);
+	if (!div)
+		return ERR_PTR(-ENOMEM);
+
+	/* struct clk_divider assignments */
+	div->reg = reg;
+	div->shift = shift;
+	div->width = width;
+	div->flags = clk_divider_flags;
+	div->table = table;
+
+	/* register the clock */
+	clk = &div->clk;
+
+	ret = clk_register(clk, UBOOT_DM_CLK_CCF_DIVIDER, (ulong)clk,
+			   name, parent_name);
+	if (ret) {
+		kfree(div);
+		return ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+struct clk *clk_register_divider(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		u8 clk_divider_flags)
+{
+	struct clk *clk;
+
+	clk =  _register_divider(dev, name, parent_name, flags, reg, shift,
+				 width, clk_divider_flags, NULL);
+	if (IS_ERR(clk))
+		return ERR_CAST(clk);
+	return clk;
+}
+
+U_BOOT_DRIVER(ccf_clk_divider) = {
+	.name	= UBOOT_DM_CLK_CCF_DIVIDER,
+	.id	= UCLASS_CLK,
+	.ops	= &clk_divider_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c
new file mode 100644
index 0000000000..c535aadc48
--- /dev/null
+++ b/drivers/clk/clk-fixed-factor.c
@@ -0,0 +1,87 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ */
+#include <common.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <div64.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_FIXED_FACTOR "ccf_clk_fixed_factor"
+
+static ulong clk_factor_recalc_rate(struct clk *clk)
+{
+	struct clk_fixed_factor *fix =
+		to_clk_fixed_factor((struct clk *)dev_get_driver_data(clk->dev));
+	unsigned long parent_rate = clk_get_parent_rate(clk);
+	unsigned long long int rate;
+
+	rate = (unsigned long long int)parent_rate * fix->mult;
+	do_div(rate, fix->div);
+	return (ulong)rate;
+}
+
+const struct clk_ops ccf_clk_fixed_factor_ops = {
+	.get_rate = clk_factor_recalc_rate,
+};
+
+struct clk *clk_hw_register_fixed_factor(struct device *dev,
+		const char *name, const char *parent_name, unsigned long flags,
+		unsigned int mult, unsigned int div)
+{
+	struct clk_fixed_factor *fix;
+	struct clk *clk;
+	int ret;
+
+	fix = kmalloc(sizeof(*fix), GFP_KERNEL);
+	if (!fix)
+		return ERR_PTR(-ENOMEM);
+
+	/* struct clk_fixed_factor assignments */
+	fix->mult = mult;
+	fix->div = div;
+	clk = &fix->clk;
+
+	/*
+	 * We pass the struct clk *clk pointer (which is the same as
+	 * clk_fixed_factor *fix - by the struct elements alignment) to DM as a
+	 * driver_data, so it can be easily accessible from the udevice level.
+	 * Moreover, the struct clk is only a wrapper on udevice which
+	 * corresponds to the "real" clock device.
+	 */
+	ret = clk_register(clk, UBOOT_DM_CLK_IMX_FIXED_FACTOR, (ulong)clk,
+			   name, parent_name);
+	if (ret) {
+		kfree(fix);
+		return ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		unsigned int mult, unsigned int div)
+{
+	struct clk *clk;
+
+	clk = clk_hw_register_fixed_factor(dev, name, parent_name, flags, mult,
+					  div);
+	if (IS_ERR(clk))
+		return ERR_CAST(clk);
+	return clk;
+}
+
+U_BOOT_DRIVER(imx_clk_fixed_factor) = {
+	.name	= UBOOT_DM_CLK_IMX_FIXED_FACTOR,
+	.id	= UCLASS_CLK,
+	.ops	= &ccf_clk_fixed_factor_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c
new file mode 100644
index 0000000000..2c85f2052c
--- /dev/null
+++ b/drivers/clk/clk-mux.c
@@ -0,0 +1,164 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (C) 2011 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
+ * Copyright (C) 2011 Richard Zhao, Linaro <richard.zhao@linaro.org>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * Simple multiplexer clock implementation
+ */
+
+/*
+ * U-Boot CCF porting node:
+ *
+ * The Linux kernel - as of tag: 5.0-rc3 is using also the imx_clk_fixup_mux()
+ * version of CCF mux. It is used on e.g. imx6q to provide fixes (like
+ * imx_cscmr1_fixup) for broken HW.
+ *
+ * At least for IMX6Q (but NOT IMX6QP) it is important when we set the parent
+ * clock.
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_CCF_MUX "ccf_clk_mux"
+
+int clk_mux_val_to_index(struct clk *clk, u32 *table, unsigned int flags,
+			 unsigned int val)
+{
+	struct clk_mux *mux = to_clk_mux(clk);
+	int num_parents = mux->num_parents;
+
+	if (table) {
+		int i;
+
+		for (i = 0; i < num_parents; i++)
+			if (table[i] == val)
+				return i;
+		return -EINVAL;
+	}
+
+	if (val && (flags & CLK_MUX_INDEX_BIT))
+		val = ffs(val) - 1;
+
+	if (val && (flags & CLK_MUX_INDEX_ONE))
+		val--;
+
+	if (val >= num_parents)
+		return -EINVAL;
+
+	return val;
+}
+
+static u8 clk_mux_get_parent(struct clk *clk)
+{
+	struct clk_mux *mux = to_clk_mux(clk);
+	u32 val;
+
+	val = readl(mux->reg) >> mux->shift;
+	val &= mux->mask;
+
+	return clk_mux_val_to_index(clk, mux->table, mux->flags, val);
+}
+
+const struct clk_ops clk_mux_ops = {
+		.get_rate = clk_generic_get_rate,
+};
+
+struct clk *clk_hw_register_mux_table(struct device *dev, const char *name,
+		const char * const *parent_names, u8 num_parents,
+		unsigned long flags,
+		void __iomem *reg, u8 shift, u32 mask,
+		u8 clk_mux_flags, u32 *table)
+{
+	struct clk_mux *mux;
+	struct clk *clk;
+	u8 width = 0;
+	int ret;
+
+	if (clk_mux_flags & CLK_MUX_HIWORD_MASK) {
+		width = fls(mask) - ffs(mask) + 1;
+		if (width + shift > 16) {
+			pr_err("mux value exceeds LOWORD field\n");
+			return ERR_PTR(-EINVAL);
+		}
+	}
+
+	/* allocate the mux */
+	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+	if (!mux)
+		return ERR_PTR(-ENOMEM);
+
+	/* U-boot specific assignments */
+	mux->parent_names = parent_names;
+	mux->num_parents = num_parents;
+
+	/* struct clk_mux assignments */
+	mux->reg = reg;
+	mux->shift = shift;
+	mux->mask = mask;
+	mux->flags = clk_mux_flags;
+	mux->table = table;
+
+	clk = &mux->clk;
+
+	/*
+	 * Read the current mux setup - so we assign correct parent.
+	 *
+	 * Changing parent would require changing internals of udevice struct
+	 * for the corresponding clock (to do that define .set_parent() method.
+	 */
+	ret = clk_register(clk, UBOOT_DM_CLK_CCF_MUX, (ulong)clk, name,
+			   parent_names[clk_mux_get_parent(clk)]);
+	if (ret) {
+		kfree(mux);
+		return ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+struct clk *clk_register_mux_table(struct device *dev, const char *name,
+		const char * const *parent_names, u8 num_parents,
+		unsigned long flags,
+		void __iomem *reg, u8 shift, u32 mask,
+		u8 clk_mux_flags, u32 *table)
+{
+	struct clk *clk;
+
+	clk = clk_hw_register_mux_table(dev, name, parent_names, num_parents,
+				       flags, reg, shift, mask, clk_mux_flags,
+				       table);
+	if (IS_ERR(clk))
+		return ERR_CAST(clk);
+	return clk;
+}
+
+struct clk *clk_register_mux(struct device *dev, const char *name,
+		const char * const *parent_names, u8 num_parents,
+		unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		u8 clk_mux_flags)
+{
+	u32 mask = BIT(width) - 1;
+
+	return clk_register_mux_table(dev, name, parent_names, num_parents,
+				      flags, reg, shift, mask, clk_mux_flags,
+				      NULL);
+}
+
+U_BOOT_DRIVER(ccf_clk_mux) = {
+	.name	= UBOOT_DM_CLK_CCF_MUX,
+	.id	= UCLASS_CLK,
+	.ops	= &clk_mux_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
new file mode 100644
index 0000000000..0a0fffb50b
--- /dev/null
+++ b/drivers/clk/clk.c
@@ -0,0 +1,56 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <dm/lists.h>
+#include <dm/device-internal.h>
+#include <clk.h>
+
+int clk_register(struct clk *clk, const char *drv_name,
+		 ulong drv_data, const char *name,
+		 const char *parent_name)
+{
+	struct udevice *parent;
+	struct driver *drv;
+	int ret;
+
+	ret = uclass_get_device_by_name(UCLASS_CLK, parent_name, &parent);
+	if (ret)
+		printf("%s: UCLASS parent: 0x%p\n", __func__, parent);
+
+	debug("%s: name: %s parent: %s [0x%p]\n", __func__, name, parent->name,
+	      parent);
+
+	drv = lists_driver_lookup_name(drv_name);
+	if (!drv) {
+		printf("%s: %s is not a valid driver name\n",
+		       __func__, drv_name);
+		return -ENOENT;
+	}
+
+	ret = device_bind_with_driver_data(parent, drv, name, drv_data,
+					   ofnode_null(), &clk->dev);
+	if (ret) {
+		printf("%s: CLK: %s driver bind error [%d]!\n", __func__, name,
+		       ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+ulong clk_generic_get_rate(struct clk *clk)
+{
+	return clk_get_parent_rate(clk);
+}
+
+const char *clk_hw_get_name(const struct clk *hw)
+{
+	return hw->dev->name;
+}
diff --git a/drivers/clk/imx/Kconfig b/drivers/clk/imx/Kconfig
index a6fb58d6cf..469768b5c3 100644
--- a/drivers/clk/imx/Kconfig
+++ b/drivers/clk/imx/Kconfig
@@ -1,3 +1,12 @@ 
+config CLK_IMX6Q
+	bool "Clock support for i.MX6Q"
+	depends on ARCH_MX6
+	select CLK
+	select CLK_CCF
+	select SPL_CLK_CCF
+	help
+	  This enables DM/DTS support for clock driver in i.MX6Q platforms.
+
 config CLK_IMX8
 	bool "Clock support for i.MX8"
 	depends on ARCH_IMX8
diff --git a/drivers/clk/imx/Makefile b/drivers/clk/imx/Makefile
index eb379c188a..64f1846b4d 100644
--- a/drivers/clk/imx/Makefile
+++ b/drivers/clk/imx/Makefile
@@ -2,6 +2,8 @@ 
 #
 # SPDX-License-Identifier: GPL-2.0
 
+obj-$(CONFIG_$(SPL_TPL_)CLK_CCF) += clk-gate2.o clk-pllv3.o clk-pfd.o
+obj-$(CONFIG_CLK_IMX6Q) += clk-imx6q.o
 obj-$(CONFIG_CLK_IMX8) += clk-imx8.o
 
 ifdef CONFIG_CLK_IMX8
diff --git a/drivers/clk/imx/clk-gate2.c b/drivers/clk/imx/clk-gate2.c
new file mode 100644
index 0000000000..83589b9206
--- /dev/null
+++ b/drivers/clk/imx/clk-gate2.c
@@ -0,0 +1,113 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (C) 2010-2011 Canonical Ltd <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Mike Turquette, Linaro Ltd <mturquette@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Gated clock implementation
+ *
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_GATE2 "imx_clk_gate2"
+
+struct clk_gate2 {
+	struct clk clk;
+	void __iomem	*reg;
+	u8		bit_idx;
+	u8		cgr_val;
+	u8		flags;
+};
+
+#define to_clk_gate2(_clk) container_of(_clk, struct clk_gate2, clk)
+
+static int clk_gate2_enable(struct clk *clk)
+{
+	struct clk_gate2 *gate =
+		to_clk_gate2((struct clk *)dev_get_driver_data(clk->dev));
+	u32 reg;
+
+	reg = readl(gate->reg);
+	reg &= ~(3 << gate->bit_idx);
+	reg |= gate->cgr_val << gate->bit_idx;
+	writel(reg, gate->reg);
+
+	return 0;
+}
+
+static int clk_gate2_disable(struct clk *clk)
+{
+	struct clk_gate2 *gate =
+		to_clk_gate2((struct clk *)dev_get_driver_data(clk->dev));
+	u32 reg;
+
+	reg = readl(gate->reg);
+	reg &= ~(3 << gate->bit_idx);
+	writel(reg, gate->reg);
+
+	return 0;
+}
+
+static const struct clk_ops clk_gate2_ops = {
+	.enable = clk_gate2_enable,
+	.disable = clk_gate2_disable,
+	.get_rate = clk_generic_get_rate,
+};
+
+struct clk *clk_register_gate2(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 bit_idx, u8 cgr_val,
+		u8 clk_gate2_flags)
+{
+	struct clk_gate2 *gate;
+	struct clk *clk;
+	int ret;
+
+	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+	if (!gate)
+		return ERR_PTR(-ENOMEM);
+
+	/* struct clk_gate2 assignments */
+	gate->reg = reg;
+	gate->bit_idx = bit_idx;
+	gate->cgr_val = cgr_val;
+	gate->flags = clk_gate2_flags;
+
+	/*
+	 * U-boot DM adjustments:
+	 *
+	 * clk and gate reslove to the same address - lets pass clock
+	 * for better readability.
+	 */
+	clk = &gate->clk;
+
+	ret = clk_register(clk, UBOOT_DM_CLK_IMX_GATE2, (ulong)clk,
+			   name, parent_name);
+	if (ret) {
+		kfree(gate);
+		return ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(clk_gate2) = {
+	.name	= UBOOT_DM_CLK_IMX_GATE2,
+	.id	= UCLASS_CLK,
+	.ops	= &clk_gate2_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c
new file mode 100644
index 0000000000..92e9337d44
--- /dev/null
+++ b/drivers/clk/imx/clk-imx6q.c
@@ -0,0 +1,179 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/imx-regs.h>
+#include <dt-bindings/clock/imx6qdl-clock.h>
+
+#include "clk.h"
+
+static int imx6q_check_id(ulong id)
+{
+	if (id < IMX6QDL_CLK_DUMMY || id >= IMX6QDL_CLK_END) {
+		printf("%s: Invalid clk ID #%lu\n", __func__, id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static ulong imx6q_clk_get_rate(struct clk *clk)
+{
+	struct clk *c;
+	int ret;
+
+	debug("%s(#%lu)\n", __func__, clk->id);
+
+	ret = imx6q_check_id(clk->id);
+	if (ret)
+		return ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	return clk_get_rate(c);
+}
+
+static ulong imx6q_clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	debug("%s(#%lu), rate: %lu\n", __func__, clk->id, rate);
+
+	return rate;
+}
+
+static int __imx6q_clk_enable(struct clk *clk, bool enable)
+{
+	struct clk *c;
+	int ret = 0;
+
+	debug("%s(#%lu) en: %d\n", __func__, clk->id, enable);
+
+	ret = imx6q_check_id(clk->id);
+	if (ret)
+		return ret;
+
+	ret = clk_get_by_id(clk->id, &c);
+	if (ret)
+		return ret;
+
+	if (enable)
+		ret = clk_enable(c);
+	else
+		ret = clk_disable(c);
+
+	return ret;
+}
+
+static int imx6q_clk_disable(struct clk *clk)
+{
+	return __imx6q_clk_enable(clk, 0);
+}
+
+static int imx6q_clk_enable(struct clk *clk)
+{
+	return __imx6q_clk_enable(clk, 1);
+}
+
+static struct clk_ops imx6q_clk_ops = {
+	.set_rate = imx6q_clk_set_rate,
+	.get_rate = imx6q_clk_get_rate,
+	.enable = imx6q_clk_enable,
+	.disable = imx6q_clk_disable,
+};
+
+static const char *const usdhc_sels[] = { "pll2_pfd2_396m", "pll2_pfd0_352m", };
+
+static int imx6q_clk_probe(struct udevice *dev)
+{
+	void *base;
+
+	/* Anatop clocks */
+	base = (void *)ANATOP_BASE_ADDR;
+
+	clk_dm(IMX6QDL_CLK_PLL2,
+	       imx_clk_pllv3(IMX_PLLV3_GENERIC, "pll2_bus", "osc",
+			     base + 0x30, 0x1));
+	clk_dm(IMX6QDL_CLK_PLL3_USB_OTG,
+	       imx_clk_pllv3(IMX_PLLV3_USB, "pll3_usb_otg", "osc",
+			     base + 0x10, 0x3));
+	clk_dm(IMX6QDL_CLK_PLL3_60M,
+	       imx_clk_fixed_factor("pll3_60m",  "pll3_usb_otg",   1, 8));
+	clk_dm(IMX6QDL_CLK_PLL2_PFD0_352M,
+	       imx_clk_pfd("pll2_pfd0_352m", "pll2_bus", base + 0x100, 0));
+	clk_dm(IMX6QDL_CLK_PLL2_PFD2_396M,
+	       imx_clk_pfd("pll2_pfd2_396m", "pll2_bus", base + 0x100, 2));
+
+	/* CCM clocks */
+	base = dev_read_addr_ptr(dev);
+	if (base == (void *)FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	clk_dm(IMX6QDL_CLK_USDHC1_SEL,
+	       imx_clk_mux("usdhc1_sel", base + 0x1c, 16, 1,
+			   usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+	clk_dm(IMX6QDL_CLK_USDHC2_SEL,
+	       imx_clk_mux("usdhc2_sel", base + 0x1c, 17, 1,
+			   usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+	clk_dm(IMX6QDL_CLK_USDHC3_SEL,
+	       imx_clk_mux("usdhc3_sel", base + 0x1c, 18, 1,
+			   usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+	clk_dm(IMX6QDL_CLK_USDHC4_SEL,
+	       imx_clk_mux("usdhc4_sel", base + 0x1c, 19, 1,
+			   usdhc_sels, ARRAY_SIZE(usdhc_sels)));
+
+	clk_dm(IMX6QDL_CLK_USDHC1_PODF,
+	       imx_clk_divider("usdhc1_podf", "usdhc1_sel",
+			       base + 0x24, 11, 3));
+	clk_dm(IMX6QDL_CLK_USDHC2_PODF,
+	       imx_clk_divider("usdhc2_podf", "usdhc2_sel",
+			       base + 0x24, 16, 3));
+	clk_dm(IMX6QDL_CLK_USDHC3_PODF,
+	       imx_clk_divider("usdhc3_podf", "usdhc3_sel",
+			       base + 0x24, 19, 3));
+	clk_dm(IMX6QDL_CLK_USDHC4_PODF,
+	       imx_clk_divider("usdhc4_podf", "usdhc4_sel",
+			       base + 0x24, 22, 3));
+
+	clk_dm(IMX6QDL_CLK_ECSPI_ROOT,
+	       imx_clk_divider("ecspi_root", "pll3_60m", base + 0x38, 19, 6));
+
+	clk_dm(IMX6QDL_CLK_ECSPI1,
+	       imx_clk_gate2("ecspi1", "ecspi_root", base + 0x6c, 0));
+	clk_dm(IMX6QDL_CLK_ECSPI2,
+	       imx_clk_gate2("ecspi2", "ecspi_root", base + 0x6c, 2));
+	clk_dm(IMX6QDL_CLK_ECSPI3,
+	       imx_clk_gate2("ecspi3", "ecspi_root", base + 0x6c, 4));
+	clk_dm(IMX6QDL_CLK_ECSPI4,
+	       imx_clk_gate2("ecspi4", "ecspi_root", base + 0x6c, 6));
+	clk_dm(IMX6QDL_CLK_USDHC1,
+	       imx_clk_gate2("usdhc1", "usdhc1_podf", base + 0x80, 2));
+	clk_dm(IMX6QDL_CLK_USDHC2,
+	       imx_clk_gate2("usdhc2", "usdhc2_podf", base + 0x80, 4));
+	clk_dm(IMX6QDL_CLK_USDHC3,
+	       imx_clk_gate2("usdhc3", "usdhc3_podf", base + 0x80, 6));
+	clk_dm(IMX6QDL_CLK_USDHC4,
+	       imx_clk_gate2("usdhc4", "usdhc4_podf", base + 0x80, 8));
+
+	return 0;
+}
+
+static const struct udevice_id imx6q_clk_ids[] = {
+	{ .compatible = "fsl,imx6q-ccm" },
+	{ },
+};
+
+U_BOOT_DRIVER(imx6q_clk) = {
+	.name = "clk_imx6q",
+	.id = UCLASS_CLK,
+	.of_match = imx6q_clk_ids,
+	.ops = &imx6q_clk_ops,
+	.probe = imx6q_clk_probe,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/imx/clk-pfd.c b/drivers/clk/imx/clk-pfd.c
new file mode 100644
index 0000000000..51521ccee6
--- /dev/null
+++ b/drivers/clk/imx/clk-pfd.c
@@ -0,0 +1,91 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2012 Linaro Ltd.
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <linux/clk-provider.h>
+#include <div64.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_PFD "imx_clk_pfd"
+
+struct clk_pfd {
+	struct clk	clk;
+	void __iomem	*reg;
+	u8		idx;
+};
+
+#define to_clk_pfd(_clk) container_of(_clk, struct clk_pfd, clk)
+
+#define SET	0x4
+#define CLR	0x8
+#define OTG	0xc
+
+static unsigned long clk_pfd_recalc_rate(struct clk *clk)
+{
+	struct clk_pfd *pfd =
+		to_clk_pfd((struct clk *)dev_get_driver_data(clk->dev));
+	unsigned long parent_rate = clk_get_parent_rate(clk);
+	u64 tmp = parent_rate;
+	u8 frac = (readl(pfd->reg) >> (pfd->idx * 8)) & 0x3f;
+
+	tmp *= 18;
+	do_div(tmp, frac);
+
+	return tmp;
+}
+
+static const struct clk_ops clk_pfd_ops = {
+	.get_rate	= clk_pfd_recalc_rate,
+};
+
+struct clk *imx_clk_pfd(const char *name, const char *parent_name,
+			void __iomem *reg, u8 idx)
+{
+	struct clk_pfd *pfd;
+	struct clk *clk;
+	int ret;
+
+	pfd = kzalloc(sizeof(*pfd), GFP_KERNEL);
+	if (!pfd)
+		return ERR_PTR(-ENOMEM);
+
+	pfd->reg = reg;
+	pfd->idx = idx;
+
+	/* register the clock */
+	clk = &pfd->clk;
+
+	ret = clk_register(clk, UBOOT_DM_CLK_IMX_PFD, (ulong)clk,
+			   name, parent_name);
+	if (ret) {
+		kfree(pfd);
+		return ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(clk_pfd) = {
+	.name	= UBOOT_DM_CLK_IMX_PFD,
+	.id	= UCLASS_CLK,
+	.ops	= &clk_pfd_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/imx/clk-pllv3.c b/drivers/clk/imx/clk-pllv3.c
new file mode 100644
index 0000000000..28c0439878
--- /dev/null
+++ b/drivers/clk/imx/clk-pllv3.c
@@ -0,0 +1,83 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+
+#include <common.h>
+#include <asm/io.h>
+#include <malloc.h>
+#include <clk-uclass.h>
+#include <dm/device.h>
+#include <dm/uclass.h>
+#include <clk.h>
+#include "clk.h"
+
+#define UBOOT_DM_CLK_IMX_PLLV3 "imx_clk_pllv3"
+
+struct clk_pllv3 {
+	struct clk	clk;
+	void __iomem	*base;
+	u32		div_mask;
+	u32		div_shift;
+};
+
+#define to_clk_pllv3(_clk) container_of(_clk, struct clk_pllv3, clk)
+
+static ulong clk_pllv3_get_rate(struct clk *clk)
+{
+	struct clk_pllv3 *pll =
+		to_clk_pllv3((struct clk *)dev_get_driver_data(clk->dev));
+	unsigned long parent_rate = clk_get_parent_rate(clk);
+
+	u32 div = (readl(pll->base) >> pll->div_shift) & pll->div_mask;
+
+	return (div == 1) ? parent_rate * 22 : parent_rate * 20;
+}
+
+static const struct clk_ops clk_pllv3_generic_ops = {
+	.get_rate       = clk_pllv3_get_rate,
+};
+
+struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
+			  const char *parent_name, void __iomem *base,
+			  u32 div_mask)
+{
+	struct clk_pllv3 *pll;
+	struct clk *clk;
+	char *drv_name;
+	int ret;
+
+	pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	switch (type) {
+	case IMX_PLLV3_GENERIC:
+	case IMX_PLLV3_USB:
+		drv_name = UBOOT_DM_CLK_IMX_PLLV3;
+		break;
+	default:
+		kfree(pll);
+		return ERR_PTR(-ENOTSUPP);
+	}
+
+	pll->base = base;
+	pll->div_mask = div_mask;
+	clk = &pll->clk;
+
+	ret = clk_register(clk, drv_name, (ulong)clk, name, parent_name);
+	if (ret) {
+		kfree(pll);
+		return ERR_PTR(ret);
+	}
+
+	return clk;
+}
+
+U_BOOT_DRIVER(clk_pllv3_generic) = {
+	.name	= UBOOT_DM_CLK_IMX_PLLV3,
+	.id	= UCLASS_CLK,
+	.ops	= &clk_pllv3_generic_ops,
+	.flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/drivers/clk/imx/clk.h b/drivers/clk/imx/clk.h
new file mode 100644
index 0000000000..864a215a22
--- /dev/null
+++ b/drivers/clk/imx/clk.h
@@ -0,0 +1,75 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ */
+#ifndef __MACH_IMX_CLK_H
+#define __MACH_IMX_CLK_H
+
+#include <linux/clk-provider.h>
+
+static inline void clk_dm(ulong id, struct clk *clk)
+{
+	if (!IS_ERR(clk))
+		clk->id = id;
+}
+
+enum imx_pllv3_type {
+	IMX_PLLV3_GENERIC,
+	IMX_PLLV3_SYS,
+	IMX_PLLV3_USB,
+	IMX_PLLV3_USB_VF610,
+	IMX_PLLV3_AV,
+	IMX_PLLV3_ENET,
+	IMX_PLLV3_ENET_IMX7,
+	IMX_PLLV3_SYS_VF610,
+	IMX_PLLV3_DDR_IMX7,
+};
+
+struct clk *clk_register_gate2(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 bit_idx, u8 cgr_val,
+		u8 clk_gate_flags);
+
+struct clk *imx_clk_pllv3(enum imx_pllv3_type type, const char *name,
+			  const char *parent_name, void __iomem *base,
+			  u32 div_mask);
+
+static inline struct clk *imx_clk_gate2(const char *name, const char *parent,
+					void __iomem *reg, u8 shift)
+{
+	return clk_register_gate2(NULL, name, parent, CLK_SET_RATE_PARENT, reg,
+			shift, 0x3, 0);
+}
+
+static inline struct clk *imx_clk_fixed_factor(const char *name,
+		const char *parent, unsigned int mult, unsigned int div)
+{
+	return clk_register_fixed_factor(NULL, name, parent,
+			CLK_SET_RATE_PARENT, mult, div);
+}
+
+static inline struct clk *imx_clk_divider(const char *name, const char *parent,
+		void __iomem *reg, u8 shift, u8 width)
+{
+	return clk_register_divider(NULL, name, parent, CLK_SET_RATE_PARENT,
+			reg, shift, width, 0);
+}
+
+struct clk *imx_clk_pfd(const char *name, const char *parent_name,
+			void __iomem *reg, u8 idx);
+
+struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg,
+			      u8 shift, u8 width, const char * const *parents,
+			      int num_parents, void (*fixup)(u32 *val));
+
+static inline struct clk *imx_clk_mux(const char *name, void __iomem *reg,
+			u8 shift, u8 width, const char * const *parents,
+			int num_parents)
+{
+	return clk_register_mux(NULL, name, parents, num_parents,
+			CLK_SET_RATE_NO_REPARENT, reg, shift,
+			width, 0);
+}
+
+#endif /* __MACH_IMX_CLK_H */
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
new file mode 100644
index 0000000000..3458746a60
--- /dev/null
+++ b/include/linux/clk-provider.h
@@ -0,0 +1,115 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2019 DENX Software Engineering
+ * Lukasz Majewski, DENX Software Engineering, lukma@denx.de
+ *
+ * Copyright (c) 2010-2011 Jeremy Kerr <jeremy.kerr@canonical.com>
+ * Copyright (C) 2011-2012 Linaro Ltd <mturquette@linaro.org>
+ */
+#ifndef __LINUX_CLK_PROVIDER_H
+#define __LINUX_CLK_PROVIDER_H
+
+/*
+ * flags used across common struct clk.  these flags should only affect the
+ * top-level framework.  custom flags for dealing with hardware specifics
+ * belong in struct clk_foo
+ *
+ * Please update clk_flags[] in drivers/clk/clk.c when making changes here!
+ */
+#define CLK_SET_RATE_GATE	BIT(0) /* must be gated across rate change */
+#define CLK_SET_PARENT_GATE	BIT(1) /* must be gated across re-parent */
+#define CLK_SET_RATE_PARENT	BIT(2) /* propagate rate change up one level */
+#define CLK_IGNORE_UNUSED	BIT(3) /* do not gate even if unused */
+				/* unused */
+#define CLK_IS_BASIC		BIT(5) /* Basic clk, can't do a to_clk_foo() */
+#define CLK_GET_RATE_NOCACHE	BIT(6) /* do not use the cached clk rate */
+#define CLK_SET_RATE_NO_REPARENT BIT(7) /* don't re-parent on rate change */
+#define CLK_GET_ACCURACY_NOCACHE BIT(8) /* do not use the cached clk accuracy */
+#define CLK_RECALC_NEW_RATES	BIT(9) /* recalc rates after notifications */
+#define CLK_SET_RATE_UNGATE	BIT(10) /* clock needs to run to set rate */
+#define CLK_IS_CRITICAL		BIT(11) /* do not gate, ever */
+/* parents need enable during gate/ungate, set rate and re-parent */
+#define CLK_OPS_PARENT_ENABLE	BIT(12)
+/* duty cycle call may be forwarded to the parent clock */
+#define CLK_DUTY_CYCLE_PARENT	BIT(13)
+
+#define CLK_MUX_INDEX_ONE		BIT(0)
+#define CLK_MUX_INDEX_BIT		BIT(1)
+#define CLK_MUX_HIWORD_MASK		BIT(2)
+#define CLK_MUX_READ_ONLY		BIT(3) /* mux can't be changed */
+#define CLK_MUX_ROUND_CLOSEST		BIT(4)
+
+struct clk_mux {
+	struct clk	clk;
+	void __iomem	*reg;
+	u32		*table;
+	u32		mask;
+	u8		shift;
+	u8		flags;
+
+	/*
+	 * Fields from struct clk_init_data - this struct has been
+	 * omitted to avoid too deep level of CCF for bootloader
+	 */
+	const char	* const *parent_names;
+	u8		num_parents;
+};
+
+#define to_clk_mux(_clk) container_of(_clk, struct clk_mux, clk)
+
+struct clk_div_table {
+	unsigned int	val;
+	unsigned int	div;
+};
+
+struct clk_divider {
+	struct clk	clk;
+	void __iomem	*reg;
+	u8		shift;
+	u8		width;
+	u8		flags;
+	const struct clk_div_table	*table;
+};
+
+#define clk_div_mask(width)	((1 << (width)) - 1)
+#define to_clk_divider(_clk) container_of(_clk, struct clk_divider, clk)
+
+#define CLK_DIVIDER_ONE_BASED		BIT(0)
+#define CLK_DIVIDER_POWER_OF_TWO	BIT(1)
+#define CLK_DIVIDER_ALLOW_ZERO		BIT(2)
+#define CLK_DIVIDER_HIWORD_MASK		BIT(3)
+#define CLK_DIVIDER_ROUND_CLOSEST	BIT(4)
+#define CLK_DIVIDER_READ_ONLY		BIT(5)
+#define CLK_DIVIDER_MAX_AT_ZERO		BIT(6)
+
+struct clk_fixed_factor {
+	struct clk	clk;
+	unsigned int	mult;
+	unsigned int	div;
+};
+
+#define to_clk_fixed_factor(_clk) container_of(_clk, struct clk_fixed_factor,\
+					       clk)
+
+int clk_register(struct clk *clk, const char *drv_name,
+		 ulong drv_data, const char *name,
+		 const char *parent_name);
+
+struct clk *clk_register_fixed_factor(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		unsigned int mult, unsigned int div);
+
+struct clk *clk_register_divider(struct device *dev, const char *name,
+		const char *parent_name, unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		u8 clk_divider_flags);
+
+struct clk *clk_register_mux(struct device *dev, const char *name,
+		const char * const *parent_names, u8 num_parents,
+		unsigned long flags,
+		void __iomem *reg, u8 shift, u8 width,
+		u8 clk_mux_flags);
+
+const char *clk_hw_get_name(const struct clk *hw);
+ulong clk_generic_get_rate(struct clk *clk);
+#endif /* __LINUX_CLK_PROVIDER_H */