[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