[U-Boot] [PATCH 18/41] imx8: add clock driver

Peng Fan peng.fan at nxp.com
Mon May 28 12:25:03 UTC 2018


Add clock driver for i.MX8, including get arm core clock, peripheral's
clock, configure I2C/flexspi/enet/gpmi-nand/usb clock.

Signed-off-by: Peng Fan <peng.fan at nxp.com>
---
 arch/arm/include/asm/arch-imx8/clock.h |  36 ++++
 arch/arm/include/asm/arch-imx8/i2c.h   |  34 ++++
 arch/arm/mach-imx/imx8/Makefile        |   2 +-
 arch/arm/mach-imx/imx8/clock.c         | 361 +++++++++++++++++++++++++++++++++
 4 files changed, 432 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/include/asm/arch-imx8/clock.h
 create mode 100644 arch/arm/include/asm/arch-imx8/i2c.h
 create mode 100644 arch/arm/mach-imx/imx8/clock.c

diff --git a/arch/arm/include/asm/arch-imx8/clock.h b/arch/arm/include/asm/arch-imx8/clock.h
new file mode 100644
index 0000000000..ff98324b1c
--- /dev/null
+++ b/arch/arm/include/asm/arch-imx8/clock.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2018 NXP
+ */
+
+#ifndef __ASM_ARCH_IMX8_CLOCK_H__
+#define __ASM_ARCH_IMX8_CLOCK_H__
+
+/* Mainly for compatible to imx common code. */
+enum mxc_clock {
+	MXC_ARM_CLK = 0,
+	MXC_AHB_CLK,
+	MXC_IPG_CLK,
+	MXC_UART_CLK,
+	MXC_CSPI_CLK,
+	MXC_AXI_CLK,
+	MXC_DDR_CLK,
+	MXC_ESDHC_CLK,
+	MXC_ESDHC2_CLK,
+	MXC_ESDHC3_CLK,
+	MXC_I2C_CLK,
+	MXC_FEC_CLK,
+};
+
+u32 mxc_get_clock(enum mxc_clock clk);
+u32 get_lpuart_clk(void);
+int enable_i2c_clk(u8 enable, u32 i2c_num);
+u32 imx_get_i2cclk(u32 i2c_num);
+void enable_usboh3_clk(bool enable);
+int set_clk_qspi(void);
+u32 imx_get_fecclk(void);
+void init_clk_usdhc(u32 index);
+void init_clk_gpmi_nand(void);
+void init_clk_usb3(int index);
+
+#endif /* __ASM_ARCH_IMX8_CLOCK_H__ */
diff --git a/arch/arm/include/asm/arch-imx8/i2c.h b/arch/arm/include/asm/arch-imx8/i2c.h
new file mode 100644
index 0000000000..b20db5eb1e
--- /dev/null
+++ b/arch/arm/include/asm/arch-imx8/i2c.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2017 NXP
+ */
+#ifndef __ASM_ARCH_IMX8_I2C_H__
+#define __ASM_ARCH_IMX8_I2C_H__
+
+#include <asm/arch/sci/sci.h>
+
+struct imx_i2c_map {
+	int index;
+	sc_rsrc_t rsrc;
+};
+
+static struct imx_i2c_map imx_i2c_desc[] = {
+	{0, SC_R_I2C_0},
+	{1, SC_R_I2C_1},
+	{2, SC_R_I2C_2},
+	{3, SC_R_I2C_3},
+	{4, SC_R_I2C_4},
+	{5, SC_R_LVDS_0_I2C_0}, /* lvds0 i2c0 */
+	{6, SC_R_LVDS_0_I2C_0}, /* lvds0 i2c1 */
+	{7, SC_R_LVDS_1_I2C_0}, /* lvds1 i2c0 */
+	{8, SC_R_LVDS_1_I2C_0}, /* lvds1 i2c1 */
+	{9, SC_R_CSI_0_I2C_0},
+	{10, SC_R_CSI_1_I2C_0},
+	{11, SC_R_HDMI_I2C_0},
+	{12, SC_R_HDMI_RX_I2C_0},
+	{13, SC_R_MIPI_0_I2C_0},
+	{14, SC_R_MIPI_0_I2C_1},
+	{15, SC_R_MIPI_1_I2C_0},
+	{16, SC_R_MIPI_1_I2C_1},
+};
+#endif /* __ASM_ARCH_IMX8_I2C_H__ */
diff --git a/arch/arm/mach-imx/imx8/Makefile b/arch/arm/mach-imx/imx8/Makefile
index 9545fd8d03..2921d18f9f 100644
--- a/arch/arm/mach-imx/imx8/Makefile
+++ b/arch/arm/mach-imx/imx8/Makefile
@@ -4,7 +4,7 @@
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
-obj-y += cpu.o
+obj-y += cpu.o clock.o
 obj-y += fsl_mu_hal.o sci/ipc.o
 obj-y += sci/svc/misc/rpc_clnt.o
 obj-y += sci/svc/pad/rpc_clnt.o
diff --git a/arch/arm/mach-imx/imx8/clock.c b/arch/arm/mach-imx/imx8/clock.c
new file mode 100644
index 0000000000..a12bfea633
--- /dev/null
+++ b/arch/arm/mach-imx/imx8/clock.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2017-2018 NXP
+ */
+
+#include <common.h>
+#include <linux/errno.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/sci/sci.h>
+#include <asm/arch/imx8-pins.h>
+#include <asm/arch/i2c.h>
+#include <asm/arch/sys_proto.h>
+#include <asm/arch/cpu.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static u32 get_arm_main_clk(void)
+{
+	sc_err_t err;
+	sc_pm_clock_rate_t clkrate;
+	sc_ipc_t ipchandle = (sc_ipc_t)gd->arch.ipc_channel_handle;
+
+	if (is_cortex_a35())
+		err = sc_pm_get_clock_rate(ipchandle, SC_R_A35, SC_PM_CLK_CPU,
+					   &clkrate);
+	else
+		err = SC_ERR_UNAVAILABLE;
+
+	if (err != SC_ERR_NONE) {
+		printf("sc get ARM clk failed! err=%d\n", err);
+		return 0;
+	}
+
+	return clkrate;
+}
+
+u32 get_lpuart_clk(void)
+{
+	return mxc_get_clock(MXC_UART_CLK);
+}
+
+u32 mxc_get_clock(enum mxc_clock clk)
+{
+	sc_err_t err;
+	sc_pm_clock_rate_t clkrate;
+	sc_ipc_t ipchandle = (sc_ipc_t)gd->arch.ipc_channel_handle;
+
+	switch (clk) {
+	case MXC_UART_CLK:
+		err = sc_pm_get_clock_rate(ipchandle, SC_R_UART_0, 2, &clkrate);
+		if (err != SC_ERR_NONE) {
+			printf("sc get UART clk failed! err=%d\n", err);
+			return 0;
+		}
+		return clkrate;
+	case MXC_ESDHC_CLK:
+		err = sc_pm_get_clock_rate(ipchandle, SC_R_SDHC_0, 2, &clkrate);
+		if (err != SC_ERR_NONE) {
+			printf("sc get uSDHC1 clk failed! err=%d\n", err);
+			return 0;
+		}
+		return clkrate;
+	case MXC_ESDHC2_CLK:
+		err = sc_pm_get_clock_rate(ipchandle, SC_R_SDHC_1, 2, &clkrate);
+		if (err != SC_ERR_NONE) {
+			printf("sc get uSDHC2 clk failed! err=%d\n", err);
+			return 0;
+		}
+		return clkrate;
+	case MXC_ESDHC3_CLK:
+		err = sc_pm_get_clock_rate(ipchandle, SC_R_SDHC_2, 2, &clkrate);
+		if (err != SC_ERR_NONE) {
+			printf("sc get uSDHC3 clk failed! err=%d\n", err);
+			return 0;
+		}
+		return clkrate;
+	case MXC_FEC_CLK:
+		err = sc_pm_get_clock_rate(ipchandle, SC_R_ENET_0, 2, &clkrate);
+		if (err != SC_ERR_NONE) {
+			printf("sc get ENET clk failed! err=%d\n", err);
+			return 0;
+		}
+		return clkrate;
+	case MXC_ARM_CLK:
+		return get_arm_main_clk();
+	default:
+		printf("Unsupported mxc_clock %d\n", clk);
+		break;
+	}
+
+	return 0;
+}
+
+u32 imx_get_fecclk(void)
+{
+	return mxc_get_clock(MXC_FEC_CLK);
+}
+
+int enable_i2c_clk(u8 enable, u32 i2c_num)
+{
+	sc_ipc_t ipc;
+	sc_err_t err;
+
+	if (i2c_num >= ARRAY_SIZE(imx_i2c_desc))
+		return -EINVAL;
+
+	ipc = gd->arch.ipc_channel_handle;
+
+	if (enable)
+		err = sc_pm_clock_enable(ipc, imx_i2c_desc[i2c_num].rsrc, 2,
+					 true, false);
+	else
+		err = sc_pm_clock_enable(ipc, imx_i2c_desc[i2c_num].rsrc, 2,
+					 false, false);
+
+	if (err != SC_ERR_NONE) {
+		printf("i2c clock error %d\n", err);
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+u32 imx_get_i2cclk(u32 i2c_num)
+{
+	sc_err_t err;
+	sc_ipc_t ipc;
+	u32 clock_rate;
+
+	if (i2c_num >= ARRAY_SIZE(imx_i2c_desc))
+		return 0;
+
+	ipc = gd->arch.ipc_channel_handle;
+	err = sc_pm_get_clock_rate(ipc, imx_i2c_desc[i2c_num].rsrc, 2,
+				   &clock_rate);
+	if (err != SC_ERR_NONE)
+		return 0;
+
+	return clock_rate;
+}
+
+void init_clk_fspi(int index)
+{
+	sc_err_t scierr;
+	sc_pm_clock_rate_t rate;
+	sc_ipc_t ipchndl = gd->arch.ipc_channel_handle;
+
+	/* Set FSPI0 clock root to 29 MHz */
+	rate = 29000000;
+	scierr = sc_pm_set_clock_rate(ipchndl, SC_R_FSPI_0, SC_PM_CLK_PER,
+				      &rate);
+	if (scierr != SC_ERR_NONE) {
+		puts("FSPI0 setrate failed\n");
+		return;
+	}
+
+	/* Enable FSPI0 clock root */
+	scierr = sc_pm_clock_enable(ipchndl, SC_R_FSPI_0, SC_PM_CLK_PER, true,
+				    false);
+	if (scierr != SC_ERR_NONE) {
+		puts("FSPI0 enable clock failed\n");
+		return;
+	}
+}
+
+void init_clk_gpmi_nand(void)
+{
+	sc_err_t scierr = 0;
+	sc_pm_clock_rate_t rate;
+	sc_ipc_t ipchndl = gd->arch.ipc_channel_handle;
+
+	/* Set NAND BCH clock root to 50 MHz */
+	rate = 50000000;
+	scierr = sc_pm_set_clock_rate(ipchndl, SC_R_NAND, SC_PM_CLK_PER, &rate);
+	if (scierr != SC_ERR_NONE) {
+		puts("NAND BCH set rate failed\n");
+		return;
+	}
+
+	/* Enable NAND BCH clock root */
+	scierr = sc_pm_clock_enable(ipchndl, SC_R_NAND, SC_PM_CLK_PER, true,
+				    false);
+	if (scierr != SC_ERR_NONE) {
+		puts("NAND BCH enable clock failed\n");
+		return;
+	}
+
+	/* Set NAND GPMI clock root to 50 MHz */
+	rate = 50000000;
+	scierr = sc_pm_set_clock_rate(ipchndl, SC_R_NAND, SC_PM_CLK_MST_BUS,
+				      &rate);
+	if (scierr != SC_ERR_NONE) {
+		puts("NAND GPMI set rate failed\n");
+		return;
+	}
+
+	/* Enable NAND GPMI clock root */
+	scierr = sc_pm_clock_enable(ipchndl, SC_R_NAND, SC_PM_CLK_MST_BUS,
+				    true, false);
+	if (scierr != SC_ERR_NONE) {
+		puts("NAND GPMI enable clock failed\n");
+		return;
+	}
+}
+
+void enable_usboh3_clk(bool enable)
+{
+	/* Dummy function */
+}
+
+void init_clk_usb3(int index)
+{
+	sc_err_t err;
+	sc_ipc_t ipc;
+
+	ipc = gd->arch.ipc_channel_handle;
+
+	err = sc_pm_clock_enable(ipc, SC_R_USB_2, SC_PM_CLK_MISC, true, false);
+	if (err != SC_ERR_NONE)
+		printf("USB3 set clock failed!, line=%d (error = %d)\n",
+		       __LINE__, err);
+
+	err = sc_pm_clock_enable(ipc, SC_R_USB_2, SC_PM_CLK_MST_BUS, true,
+				 false);
+	if (err != SC_ERR_NONE)
+		printf("USB3 set clock failed!, line=%d (error = %d)\n",
+		       __LINE__, err);
+
+	err = sc_pm_clock_enable(ipc, SC_R_USB_2, SC_PM_CLK_PER, true, false);
+	if (err != SC_ERR_NONE)
+		printf("USB3 set clock failed!, line=%d (error = %d)\n",
+		       __LINE__, err);
+}
+
+int cdns3_enable_clks(int index)
+{
+	init_clk_usb3(index);
+
+	return 0;
+}
+
+int cdns3_disable_clks(int index)
+{
+	sc_err_t err;
+	sc_ipc_t ipc;
+
+	ipc = gd->arch.ipc_channel_handle;
+
+	err = sc_pm_clock_enable(ipc, SC_R_USB_2, SC_PM_CLK_MISC, false, false);
+	if (err != SC_ERR_NONE)
+		printf("USB3 disable clock failed!, line=%d (error = %d)\n",
+		       __LINE__, err);
+
+	err = sc_pm_clock_enable(ipc, SC_R_USB_2, SC_PM_CLK_MST_BUS, false,
+				 false);
+	if (err != SC_ERR_NONE)
+		printf("USB3 disable clock failed!, line=%d (error = %d)\n",
+		       __LINE__, err);
+
+	err = sc_pm_clock_enable(ipc, SC_R_USB_2, SC_PM_CLK_PER, false, false);
+	if (err != SC_ERR_NONE)
+		printf("USB3 disable clock failed!, line=%d (error = %d)\n",
+		       __LINE__, err);
+
+	return 0;
+}
+
+void init_clk_usdhc(u32 index)
+{
+#ifdef CONFIG_IMX8QXP
+	sc_rsrc_t usdhcs[] = {SC_R_SDHC_0, SC_R_SDHC_1};
+	u32 instances = 2;
+#endif
+
+	sc_err_t err;
+	sc_ipc_t ipc;
+	sc_pm_clock_rate_t actual = 400000000;
+
+	ipc = gd->arch.ipc_channel_handle;
+
+	if (index >= instances)
+		return;
+
+	/*
+	 * IMX8QXP USDHC_CLK_ROOT default source from DPLL, but this DPLL
+	 * do not stable, will cause usdhc data transfer crc error. So here
+	 * is a workaround, let USDHC_CLK_ROOT source from AVPLL. Due to
+	 * AVPLL is fixed to 1000MHz, so here config USDHC1_CLK_ROOT to 333MHz,
+	 * USDHC2_CLK_ROOT to 200MHz, make eMMC HS400ES work at 166MHz, and SD
+	 * SDR104 work at 200MHz.
+	 */
+#ifdef CONFIG_IMX8QXP
+	err = sc_pm_set_clock_parent(ipc, usdhcs[index], 2, SC_PM_PARENT_PLL1);
+	if (err != SC_ERR_NONE)
+		printf("SDHC_%d set clock parent failed!(error = %d)\n",
+		       index, err);
+
+	if (index == 1)
+		actual = 200000000;
+#endif
+
+	err = sc_pm_set_clock_rate(ipc, usdhcs[index], 2, &actual);
+	if (err != SC_ERR_NONE) {
+		printf("SDHC_%d set clock failed! (error = %d)\n", index, err);
+		return;
+	}
+
+	if (actual != 400000000)
+		printf("Actual rate for SDHC_%d is %d\n", index, actual);
+
+	err = sc_pm_clock_enable(ipc, usdhcs[index], SC_PM_CLK_PER, true,
+				 false);
+	if (err != SC_ERR_NONE) {
+		printf("SDHC_%d per clk enable failed!\n", index);
+		return;
+	}
+}
+
+void init_clk_fec(int index)
+{
+	sc_err_t err;
+	sc_ipc_t ipc;
+	sc_pm_clock_rate_t rate = 24000000;
+	sc_rsrc_t enet[2] = {SC_R_ENET_0, SC_R_ENET_1};
+
+	if (index > 1)
+		return;
+
+	if (index == -1)
+		index = 0;
+
+	ipc = gd->arch.ipc_channel_handle;
+
+	/* Disable SC_R_ENET_0 clock root */
+	err = sc_pm_clock_enable(ipc, enet[index], 0, false, false);
+	err |= sc_pm_clock_enable(ipc, enet[index], 2, false, false);
+	err |= sc_pm_clock_enable(ipc, enet[index], 4, false, false);
+	if (err != SC_ERR_NONE) {
+		printf("SC_R_ENET_0 disable clock failed! (error = %d)\n", err);
+		return;
+	}
+
+	/* Set SC_R_ENET_0 clock root to 125 MHz */
+	rate = 125000000;
+
+	/* div = 8 clk_source = PLL_1 ss_slice #7 in verfication codes */
+	err = sc_pm_set_clock_rate(ipc, enet[index], 2, &rate);
+	if (err != SC_ERR_NONE) {
+		printf("SC_R_ENET_0 set ref clock failed! (err = %d)\n", err);
+		return;
+	}
+
+	/* Enable SC_R_ENET_0 clock root */
+	err = sc_pm_clock_enable(ipc, enet[index], 0, true, true);
+	err |= sc_pm_clock_enable(ipc, enet[index], 2, true, true);
+	err |= sc_pm_clock_enable(ipc, enet[index], 4, true, true);
+	if (err != SC_ERR_NONE) {
+		printf("SC_R_ENET_0 enable clock failed! (error = %d)\n", err);
+		return;
+	}
+}
-- 
2.14.1



More information about the U-Boot mailing list