
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@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; + } +}