| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright 2018-2019 NXP |
| */ |
| |
| #include <common.h> |
| #include <linux/errno.h> |
| #include <asm/arch/clock.h> |
| #include <asm/arch/i2c.h> |
| #include <asm/arch/sys_proto.h> |
| #include <asm/arch/lpcg.h> |
| #include <asm/arch/sci/sci.h> |
| |
| DECLARE_GLOBAL_DATA_PTR; |
| |
| 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; |
| |
| switch (clk) { |
| case MXC_UART_CLK: |
| err = sc_pm_get_clock_rate(-1, |
| 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(-1, |
| 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(-1, |
| 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(-1, |
| 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(-1, |
| 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_DDR_CLK: |
| err = sc_pm_get_clock_rate(-1, |
| SC_R_DRC_0, 0, &clkrate); |
| if (err != SC_ERR_NONE) { |
| printf("sc get DRC0 clk failed! err=%d\n", err); |
| return 0; |
| } |
| return clkrate; |
| default: |
| printf("Unsupported mxc_clock %d\n", clk); |
| break; |
| } |
| |
| return 0; |
| } |
| |
| u32 imx_get_fecclk(void) |
| { |
| return mxc_get_clock(MXC_FEC_CLK); |
| } |
| |
| static struct imx_i2c_map *get_i2c_desc(unsigned i2c_num) |
| { |
| int i; |
| for (i = 0; i < ARRAY_SIZE(imx_i2c_desc); i++) { |
| if (imx_i2c_desc[i].index == i2c_num) |
| return &imx_i2c_desc[i]; |
| } |
| return NULL; |
| } |
| |
| int enable_i2c_clk(unsigned char enable, unsigned i2c_num) |
| { |
| sc_err_t err; |
| struct imx_i2c_map *desc; |
| int i; |
| |
| desc = get_i2c_desc(i2c_num); |
| if (!desc) |
| return -EINVAL; |
| |
| |
| if (enable) |
| err = sc_pm_clock_enable(-1, |
| desc->rsrc, 2, true, false); |
| else |
| err = sc_pm_clock_enable(-1, |
| desc->rsrc, 2, false, false); |
| |
| if (err != SC_ERR_NONE) { |
| printf("i2c clock error %d\n", err); |
| return -EPERM; |
| } |
| |
| for (i = 0; i < 4; i++) { |
| if (desc->lpcg[i] == 0) |
| break; |
| lpcg_all_clock_on(desc->lpcg[i]); |
| } |
| |
| return 0; |
| } |
| |
| u32 imx_get_i2cclk(unsigned i2c_num) |
| { |
| sc_err_t err; |
| u32 clock_rate; |
| struct imx_i2c_map *desc; |
| |
| desc = get_i2c_desc(i2c_num); |
| if (!desc) |
| return -EINVAL; |
| |
| err = sc_pm_get_clock_rate(-1, desc->rsrc, 2, |
| &clock_rate); |
| if (err != SC_ERR_NONE) |
| return 0; |
| |
| return clock_rate; |
| } |
| |
| void init_clk_fspi(int index) |
| { |
| sc_err_t sciErr = 0; |
| sc_pm_clock_rate_t rate; |
| |
| /* Set FSPI0 clock root to 29 MHz */ |
| rate = 29000000; |
| sciErr = sc_pm_set_clock_rate(-1, 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(-1, SC_R_FSPI_0, SC_PM_CLK_PER, true, false); |
| if (sciErr != SC_ERR_NONE) { |
| puts("FSPI0 enable clock failed\n"); |
| return; |
| } |
| |
| lpcg_all_clock_on(FSPI_0_LPCG); |
| |
| return; |
| } |
| |
| void init_clk_gpmi_nand(void) |
| { |
| sc_err_t sciErr = 0; |
| sc_pm_clock_rate_t rate; |
| |
| /* Set NAND BCH clock root to 50 MHz */ |
| rate = 50000000; |
| sciErr = sc_pm_set_clock_rate(-1, 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(-1, 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(-1, 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(-1, SC_R_NAND, SC_PM_CLK_MST_BUS, true, false); |
| if (sciErr != SC_ERR_NONE) { |
| puts("NAND GPMI enable clock failed\n"); |
| return; |
| } |
| |
| lpcg_all_clock_on(NAND_LPCG); |
| lpcg_all_clock_on(NAND_LPCG + 0x4); |
| |
| return; |
| } |
| |
| void enable_usboh3_clk(unsigned char enable) |
| { |
| lpcg_all_clock_on(USB_2_LPCG); |
| return; |
| } |
| |
| void init_clk_usb3(int index) |
| { |
| sc_err_t err; |
| |
| err = sc_pm_clock_enable(-1, 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(-1, 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(-1, 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); |
| |
| lpcg_all_clock_on(USB_3_LPCG); |
| return; |
| } |
| |
| int cdns3_enable_clks(int index) |
| { |
| init_clk_usb3(index); |
| return 0; |
| } |
| |
| int cdns3_disable_clks(int index) |
| { |
| sc_err_t err; |
| |
| lpcg_all_clock_off(USB_3_LPCG); |
| |
| err = sc_pm_clock_enable(-1, 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(-1, 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(-1, 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_IMX8QM |
| sc_rsrc_t usdhcs[] = {SC_R_SDHC_0, SC_R_SDHC_1, SC_R_SDHC_2}; |
| u32 instances = 3; |
| #else |
| sc_rsrc_t usdhcs[] = {SC_R_SDHC_0, SC_R_SDHC_1}; |
| u32 instances = 2; |
| #endif |
| |
| sc_err_t err; |
| sc_pm_clock_rate_t actual = 400000000; |
| |
| if (index >= instances) |
| return; |
| |
| /* Must disable the clock before set clock parent */ |
| err = sc_pm_clock_enable(-1, usdhcs[index], SC_PM_CLK_PER, false, false); |
| if (err != SC_ERR_NONE) { |
| printf("SDHC_%d per clk enable failed!\n", index); |
| 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. |
| */ |
| if (is_imx8qxp()) { |
| err = sc_pm_set_clock_parent(-1, 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; |
| } |
| |
| err = sc_pm_set_clock_rate(-1, usdhcs[index], 2, &actual); |
| if (err != SC_ERR_NONE) { |
| printf("SDHC_%d set clock failed! (error = %d)\n", index, err); |
| return; |
| } |
| |
| if (actual != 400000000) |
| debug("Actual rate for SDHC_%d is %d\n", index, actual); |
| |
| err = sc_pm_clock_enable(-1, usdhcs[index], SC_PM_CLK_PER, true, false); |
| if (err != SC_ERR_NONE) { |
| printf("SDHC_%d per clk enable failed!\n", index); |
| return; |
| } |
| |
| lpcg_all_clock_on(USDHC_0_LPCG + index * 0x10000); |
| } |
| |
| void init_clk_fec(int index) |
| { |
| sc_err_t err; |
| 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; |
| |
| /* Disable SC_R_ENET_0 clock root */ |
| err = sc_pm_clock_enable(-1, enet[index], 0, false, false); |
| err |= sc_pm_clock_enable(-1, enet[index], 2, false, false); |
| err |= sc_pm_clock_enable(-1, enet[index], 4, false, false); |
| if (err != SC_ERR_NONE) { |
| printf("\nSC_R_ENET_0 set clock disable failed! (error = %d)\n", err); |
| return; |
| } |
| |
| /* Set SC_R_ENET_0 clock root to 250 MHz, the clkdiv is set to div 2 |
| * so finally RGMII TX clk is 125Mhz |
| */ |
| rate = 250000000; |
| |
| /* div = 8 clk_source = PLL_1 ss_slice #7 in verfication codes */ |
| err = sc_pm_set_clock_rate(-1, enet[index], 2, &rate); |
| if (err != SC_ERR_NONE) { |
| printf("\nSC_R_ENET_0 set clock ref clock 125M failed! (error = %d)\n", err); |
| return; |
| } |
| |
| /* Enable SC_R_ENET_0 clock root */ |
| err = sc_pm_clock_enable(-1, enet[index], 0, true, true); |
| err |= sc_pm_clock_enable(-1, enet[index], 2, true, true); |
| err |= sc_pm_clock_enable(-1, enet[index], 4, true, true); |
| if (err != SC_ERR_NONE) { |
| printf("\nSC_R_ENET_0 set clock enable failed! (error = %d)\n", err); |
| return; |
| } |
| |
| /* Configure GPR regisers */ |
| if (sc_misc_set_control(-1, enet[index], SC_C_TXCLK, 0) != SC_ERR_NONE) |
| printf("\nConfigure GPR registers operation(%d) failed!\n", SC_C_TXCLK); |
| /* Enable divclk */ |
| if (sc_misc_set_control(-1, enet[index], SC_C_CLKDIV, 1) != SC_ERR_NONE) |
| printf("\nConfigure GPR registers operation(%d) failed!\n", SC_C_CLKDIV); |
| if (sc_misc_set_control(-1, enet[index], SC_C_DISABLE_50, 1) != SC_ERR_NONE) |
| printf("\nConfigure GPR registers operation(%d) failed!\n", SC_C_DISABLE_50); |
| if (sc_misc_set_control(-1, enet[index], SC_C_DISABLE_125, 1) != SC_ERR_NONE) |
| printf("\nConfigure GPR registers operation(%d) failed!\n", SC_C_DISABLE_125); |
| if (sc_misc_set_control(-1, enet[index], SC_C_SEL_125, 0) != SC_ERR_NONE) |
| printf("\nConfigure GPR registers operation(%d) failed!\n", SC_C_SEL_125); |
| if (sc_misc_set_control(-1, enet[index], SC_C_IPG_STOP, 0) != SC_ERR_NONE) |
| printf("\nConfigure GPR registers operation(%d) failed!\n", SC_C_IPG_STOP); |
| |
| lpcg_all_clock_on(ENET_0_LPCG + index * 0x10000); |
| } |