From 626c8a6df5bea994aa821bb3a318a99634a4fbcc Mon Sep 17 00:00:00 2001 From: Peng Fan Date: Wed, 22 Feb 2017 16:21:42 +0800 Subject: imx: mx7ulp: Add clock framework and functions Add a clock framework to support SCG1/PCC2/PCC3 for A7 to support get/set clock source, divider, clock rate and parent source. Users need to include pcc.h to use the APIs to for peripherals clock. Each peripheral clock is defined in enum pcc_clk type. SCG relevants APIs are defined in scg.h which supports clock rate get, PLL/PFD enablement and settings, and all SCG clock initialization. User need use enum scg_clk to access each clock source. In clock.c, we initialize necessary clocks at u-boot s_init and implement the clock functions used by driver modules to operate clocks dynamically. Signed-off-by: Peng Fan Signed-off-by: Ye Li Cc: Stefano Babic --- arch/arm/cpu/armv7/mx7ulp/Makefile | 2 +- arch/arm/cpu/armv7/mx7ulp/clock.c | 315 +++++++++++ arch/arm/cpu/armv7/mx7ulp/pcc.c | 286 ++++++++++ arch/arm/cpu/armv7/mx7ulp/scg.c | 1086 ++++++++++++++++++++++++++++++++++++ 4 files changed, 1688 insertions(+), 1 deletion(-) create mode 100644 arch/arm/cpu/armv7/mx7ulp/clock.c create mode 100644 arch/arm/cpu/armv7/mx7ulp/pcc.c create mode 100644 arch/arm/cpu/armv7/mx7ulp/scg.c (limited to 'arch/arm/cpu') diff --git a/arch/arm/cpu/armv7/mx7ulp/Makefile b/arch/arm/cpu/armv7/mx7ulp/Makefile index be038e7..6f37e8c 100644 --- a/arch/arm/cpu/armv7/mx7ulp/Makefile +++ b/arch/arm/cpu/armv7/mx7ulp/Makefile @@ -5,4 +5,4 @@ # # -obj-y := iomux.o +obj-y := clock.o iomux.o pcc.o scg.o diff --git a/arch/arm/cpu/armv7/mx7ulp/clock.c b/arch/arm/cpu/armv7/mx7ulp/clock.c new file mode 100644 index 0000000..e6225bb --- /dev/null +++ b/arch/arm/cpu/armv7/mx7ulp/clock.c @@ -0,0 +1,315 @@ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +int get_clocks(void) +{ +#ifdef CONFIG_FSL_ESDHC +#if CONFIG_SYS_FSL_ESDHC_ADDR == USDHC0_RBASE + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK); +#elif CONFIG_SYS_FSL_ESDHC_ADDR == USDHC1_RBASE + gd->arch.sdhc_clk = mxc_get_clock(MXC_ESDHC2_CLK); +#endif +#endif + return 0; +} + +static u32 get_fast_plat_clk(void) +{ + return scg_clk_get_rate(SCG_NIC0_CLK); +} + +static u32 get_slow_plat_clk(void) +{ + return scg_clk_get_rate(SCG_NIC1_CLK); +} + +static u32 get_ipg_clk(void) +{ + return scg_clk_get_rate(SCG_NIC1_BUS_CLK); +} + +u32 get_lpuart_clk(void) +{ + int index = 0; + + const u32 lpuart_array[] = { + LPUART0_RBASE, + LPUART1_RBASE, + LPUART2_RBASE, + LPUART3_RBASE, + LPUART4_RBASE, + LPUART5_RBASE, + LPUART6_RBASE, + LPUART7_RBASE, + }; + + const enum pcc_clk lpuart_pcc_clks[] = { + PER_CLK_LPUART4, + PER_CLK_LPUART5, + PER_CLK_LPUART6, + PER_CLK_LPUART7, + }; + + for (index = 0; index < 8; index++) { + if (lpuart_array[index] == LPUART_BASE) + break; + } + + if (index < 4 || index > 7) + return 0; + + return pcc_clock_get_rate(lpuart_pcc_clks[index - 4]); +} + +unsigned int mxc_get_clock(enum mxc_clock clk) +{ + switch (clk) { + case MXC_ARM_CLK: + return scg_clk_get_rate(SCG_CORE_CLK); + case MXC_AXI_CLK: + return get_fast_plat_clk(); + case MXC_AHB_CLK: + return get_slow_plat_clk(); + case MXC_IPG_CLK: + return get_ipg_clk(); + case MXC_I2C_CLK: + return pcc_clock_get_rate(PER_CLK_LPI2C4); + case MXC_UART_CLK: + return get_lpuart_clk(); + case MXC_ESDHC_CLK: + return pcc_clock_get_rate(PER_CLK_USDHC0); + case MXC_ESDHC2_CLK: + return pcc_clock_get_rate(PER_CLK_USDHC1); + case MXC_DDR_CLK: + return scg_clk_get_rate(SCG_DDR_CLK); + default: + printf("Unsupported mxc_clock %d\n", clk); + break; + } + + return 0; +} + +void init_clk_usdhc(u32 index) +{ + switch (index) { + case 0: + /*Disable the clock before configure it */ + pcc_clock_enable(PER_CLK_USDHC0, false); + + /* 158MHz / 1 = 158MHz */ + pcc_clock_sel(PER_CLK_USDHC0, SCG_NIC1_CLK); + pcc_clock_div_config(PER_CLK_USDHC0, false, 1); + pcc_clock_enable(PER_CLK_USDHC0, true); + break; + case 1: + /*Disable the clock before configure it */ + pcc_clock_enable(PER_CLK_USDHC1, false); + + /* 158MHz / 1 = 158MHz */ + pcc_clock_sel(PER_CLK_USDHC1, SCG_NIC1_CLK); + pcc_clock_div_config(PER_CLK_USDHC1, false, 1); + pcc_clock_enable(PER_CLK_USDHC1, true); + break; + default: + printf("Invalid index for USDHC %d\n", index); + break; + } +} + +#ifdef CONFIG_MXC_OCOTP + +#define OCOTP_CTRL_PCC1_SLOT (38) +#define OCOTP_CTRL_HIGH4K_PCC1_SLOT (39) + +void enable_ocotp_clk(unsigned char enable) +{ + u32 val; + + /* + * Seems the OCOTP CLOCKs have been enabled at default, + * check its inuse flag + */ + + val = readl(PCC1_RBASE + 4 * OCOTP_CTRL_PCC1_SLOT); + if (!(val & PCC_INUSE_MASK)) + writel(PCC_CGC_MASK, (PCC1_RBASE + 4 * OCOTP_CTRL_PCC1_SLOT)); + + val = readl(PCC1_RBASE + 4 * OCOTP_CTRL_HIGH4K_PCC1_SLOT); + if (!(val & PCC_INUSE_MASK)) + writel(PCC_CGC_MASK, + (PCC1_RBASE + 4 * OCOTP_CTRL_HIGH4K_PCC1_SLOT)); +} +#endif + +void enable_usboh3_clk(unsigned char enable) +{ + if (enable) { + pcc_clock_enable(PER_CLK_USB0, false); + pcc_clock_sel(PER_CLK_USB0, SCG_NIC1_BUS_CLK); + pcc_clock_enable(PER_CLK_USB0, true); + +#ifdef CONFIG_USB_MAX_CONTROLLER_COUNT + if (CONFIG_USB_MAX_CONTROLLER_COUNT > 1) { + pcc_clock_enable(PER_CLK_USB1, false); + pcc_clock_sel(PER_CLK_USB1, SCG_NIC1_BUS_CLK); + pcc_clock_enable(PER_CLK_USB1, true); + } +#endif + + pcc_clock_enable(PER_CLK_USB_PHY, true); + pcc_clock_enable(PER_CLK_USB_PL301, true); + } else { + pcc_clock_enable(PER_CLK_USB0, false); + pcc_clock_enable(PER_CLK_USB1, false); + pcc_clock_enable(PER_CLK_USB_PHY, false); + pcc_clock_enable(PER_CLK_USB_PL301, false); + } +} + +static void lpuart_set_clk(uint32_t index, enum scg_clk clk) +{ + const enum pcc_clk lpuart_pcc_clks[] = { + PER_CLK_LPUART4, + PER_CLK_LPUART5, + PER_CLK_LPUART6, + PER_CLK_LPUART7, + }; + + if (index < 4 || index > 7) + return; + +#ifndef CONFIG_CLK_DEBUG + pcc_clock_enable(lpuart_pcc_clks[index - 4], false); +#endif + pcc_clock_sel(lpuart_pcc_clks[index - 4], clk); + pcc_clock_enable(lpuart_pcc_clks[index - 4], true); +} + +static void init_clk_lpuart(void) +{ + u32 index = 0, i; + + const u32 lpuart_array[] = { + LPUART0_RBASE, + LPUART1_RBASE, + LPUART2_RBASE, + LPUART3_RBASE, + LPUART4_RBASE, + LPUART5_RBASE, + LPUART6_RBASE, + LPUART7_RBASE, + }; + + for (i = 0; i < 8; i++) { + if (lpuart_array[i] == LPUART_BASE) { + index = i; + break; + } + } + + lpuart_set_clk(index, SCG_SOSC_DIV2_CLK); +} + +static void init_clk_rgpio2p(void) +{ + /*Enable RGPIO2P1 clock */ + pcc_clock_enable(PER_CLK_RGPIO2P1, true); + + /* + * Hard code to enable RGPIO2P0 clock since it is not + * in clock frame for A7 domain + */ + writel(PCC_CGC_MASK, (PCC0_RBASE + 0x3C)); +} + +/* Configure PLL/PFD freq */ +void clock_init(void) +{ + /* + * ROM has enabled clocks: + * A4 side: SIRC 16Mhz (DIV1-3 off), FIRC 48Mhz (DIV1-2 on), + * Non-LP-boot: SOSC, SPLL PFD0 (scs selected) + * A7 side: SPLL PFD0 (scs selected, 413Mhz), + * APLL PFD0 (352Mhz), DDRCLK, all NIC clocks + * A7 Plat0 (NIC0) = 176Mhz, Plat1 (NIC1) = 176Mhz, + * IP BUS (NIC1_BUS) = 58.6Mhz + * + * In u-boot: + * 1. Enable PFD1-3 of APLL for A7 side. Enable FIRC and DIVs. + * 2. Enable USB PLL + * 3. Init the clocks of peripherals used in u-boot bu + * without set rate interface.The clocks for these + * peripherals are enabled in this intialization. + * 4.Other peripherals with set clock rate interface + * does not be set in this function. + */ + + scg_a7_firc_init(); + + scg_a7_soscdiv_init(); + + /* APLL PFD1 = 270Mhz, PFD2=480Mhz, PFD3=800Mhz */ + scg_enable_pll_pfd(SCG_APLL_PFD1_CLK, 35); + scg_enable_pll_pfd(SCG_APLL_PFD2_CLK, 20); + scg_enable_pll_pfd(SCG_APLL_PFD3_CLK, 12); + + init_clk_lpuart(); + + init_clk_rgpio2p(); + + enable_usboh3_clk(1); +} + +/* + * Dump some core clockes. + */ +int do_mx7_showclocks(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + u32 addr = 0; + u32 freq; + freq = decode_pll(PLL_A7_SPLL); + printf("PLL_A7_SPLL %8d MHz\n", freq / 1000000); + + freq = decode_pll(PLL_A7_APLL); + printf("PLL_A7_APLL %8d MHz\n", freq / 1000000); + + freq = decode_pll(PLL_USB); + printf("PLL_USB %8d MHz\n", freq / 1000000); + + printf("\n"); + + printf("CORE %8d kHz\n", scg_clk_get_rate(SCG_CORE_CLK) / 1000); + printf("IPG %8d kHz\n", mxc_get_clock(MXC_IPG_CLK) / 1000); + printf("UART %8d kHz\n", mxc_get_clock(MXC_UART_CLK) / 1000); + printf("AHB %8d kHz\n", mxc_get_clock(MXC_AHB_CLK) / 1000); + printf("AXI %8d kHz\n", mxc_get_clock(MXC_AXI_CLK) / 1000); + printf("DDR %8d kHz\n", mxc_get_clock(MXC_DDR_CLK) / 1000); + printf("USDHC1 %8d kHz\n", mxc_get_clock(MXC_ESDHC_CLK) / 1000); + printf("USDHC2 %8d kHz\n", mxc_get_clock(MXC_ESDHC2_CLK) / 1000); + printf("I2C4 %8d kHz\n", mxc_get_clock(MXC_I2C_CLK) / 1000); + + addr = (u32) clock_init; + printf("[%s] addr = 0x%08X\r\n", __func__, addr); + scg_a7_info(); + + return 0; +} + +U_BOOT_CMD( + clocks, CONFIG_SYS_MAXARGS, 1, do_mx7_showclocks, + "display clocks", + "" +); diff --git a/arch/arm/cpu/armv7/mx7ulp/pcc.c b/arch/arm/cpu/armv7/mx7ulp/pcc.c new file mode 100644 index 0000000..edd84e5 --- /dev/null +++ b/arch/arm/cpu/armv7/mx7ulp/pcc.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +#define PCC_CLKSRC_TYPES 2 +#define PCC_CLKSRC_NUM 7 + +static enum scg_clk pcc_clksrc[PCC_CLKSRC_TYPES][PCC_CLKSRC_NUM] = { + { SCG_NIC1_BUS_CLK, + SCG_NIC1_CLK, + SCG_DDR_CLK, + SCG_APLL_PFD2_CLK, + SCG_APLL_PFD1_CLK, + SCG_APLL_PFD0_CLK, + USB_PLL_OUT, + }, + { SCG_SOSC_DIV2_CLK, /* SOSC BUS clock */ + MIPI_PLL_OUT, + SCG_FIRC_DIV2_CLK, /* FIRC BUS clock */ + SCG_ROSC_CLK, + SCG_NIC1_BUS_CLK, + SCG_NIC1_CLK, + SCG_APLL_PFD3_CLK, + }, +}; + +static struct pcc_entry pcc_arrays[] = { + {PCC2_RBASE, DMA1_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, RGPIO1_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, FLEXBUS0_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, SEMA42_1_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, DMA1_CH_MUX0_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, SNVS_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, CAAM_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, LPTPM4_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPTPM5_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPIT1_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPSPI2_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPSPI3_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPI2C4_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPI2C5_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPUART4_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, LPUART5_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, FLEXIO1_PCC2_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC2_RBASE, USBOTG0_PCC2_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV}, + {PCC2_RBASE, USBOTG1_PCC2_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV}, + {PCC2_RBASE, USBPHY_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, USB_PL301_PCC2_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC2_RBASE, USDHC0_PCC2_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV}, + {PCC2_RBASE, USDHC1_PCC2_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV}, + {PCC2_RBASE, WDG1_PCC2_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV}, + {PCC2_RBASE, WDG2_PCC2_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV}, + + {PCC3_RBASE, LPTPM6_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC3_RBASE, LPTPM7_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC3_RBASE, LPI2C6_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC3_RBASE, LPI2C7_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC3_RBASE, LPUART6_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC3_RBASE, LPUART7_PCC3_SLOT, CLKSRC_PER_BUS, PCC_NO_DIV}, + {PCC3_RBASE, VIU0_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC3_RBASE, DSI0_PCC3_SLOT, CLKSRC_PER_BUS, PCC_HAS_DIV}, + {PCC3_RBASE, LCDIF0_PCC3_SLOT, CLKSRC_PER_PLAT, PCC_HAS_DIV}, + {PCC3_RBASE, MMDC0_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC3_RBASE, PORTC_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC3_RBASE, PORTD_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC3_RBASE, PORTE_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC3_RBASE, PORTF_PCC3_SLOT, CLKSRC_NO_PCS, PCC_NO_DIV}, + {PCC3_RBASE, GPU3D_PCC3_SLOT, CLKSRC_PER_PLAT, PCC_NO_DIV}, + {PCC3_RBASE, GPU2D_PCC3_SLOT, CLKSRC_PER_PLAT, PCC_NO_DIV}, +}; + +int pcc_clock_enable(enum pcc_clk clk, bool enable) +{ + u32 reg, val; + + if (clk >= ARRAY_SIZE(pcc_arrays)) + return -EINVAL; + + reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4; + + val = readl(reg); + + clk_debug("pcc_clock_enable: clk %d, reg 0x%x, val 0x%x, enable %d\n", + clk, reg, val, enable); + + if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK)) + return -EPERM; + + if (enable) + val |= PCC_CGC_MASK; + else + val &= ~PCC_CGC_MASK; + + writel(val, reg); + + clk_debug("pcc_clock_enable: val 0x%x\n", val); + + return 0; +} + +/* The clock source select needs clock is disabled */ +int pcc_clock_sel(enum pcc_clk clk, enum scg_clk src) +{ + u32 reg, val, i, clksrc_type; + + if (clk >= ARRAY_SIZE(pcc_arrays)) + return -EINVAL; + + clksrc_type = pcc_arrays[clk].clksrc; + if (clksrc_type >= CLKSRC_NO_PCS) { + printf("No PCS field for the PCC %d, clksrc type %d\n", + clk, clksrc_type); + return -EPERM; + } + + for (i = 0; i < PCC_CLKSRC_NUM; i++) { + if (pcc_clksrc[clksrc_type][i] == src) { + /* Find the clock src, then set it to PCS */ + break; + } + } + + if (i == PCC_CLKSRC_NUM) { + printf("Not find the parent scg_clk in PCS of PCC %d, invalid scg_clk %d\n", clk, src); + return -EINVAL; + } + + reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4; + + val = readl(reg); + + clk_debug("pcc_clock_sel: clk %d, reg 0x%x, val 0x%x, clksrc_type %d\n", + clk, reg, val, clksrc_type); + + if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) || + (val & PCC_CGC_MASK)) { + printf("Not permit to select clock source val = 0x%x\n", val); + return -EPERM; + } + + val &= ~PCC_PCS_MASK; + val |= ((i + 1) << PCC_PCS_OFFSET); + + writel(val, reg); + + clk_debug("pcc_clock_sel: val 0x%x\n", val); + + return 0; +} + +int pcc_clock_div_config(enum pcc_clk clk, bool frac, u8 div) +{ + u32 reg, val; + + if (clk >= ARRAY_SIZE(pcc_arrays) || div > 8 || + (div == 1 && frac != 0)) + return -EINVAL; + + if (pcc_arrays[clk].div >= PCC_NO_DIV) { + printf("No DIV/FRAC field for the PCC %d\n", clk); + return -EPERM; + } + + reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4; + + val = readl(reg); + + if (!(val & PCC_PR_MASK) || (val & PCC_INUSE_MASK) || + (val & PCC_CGC_MASK)) { + printf("Not permit to set div/frac val = 0x%x\n", val); + return -EPERM; + } + + if (frac) + val |= PCC_FRAC_MASK; + else + val &= ~PCC_FRAC_MASK; + + val &= ~PCC_PCD_MASK; + val |= (div - 1) & PCC_PCD_MASK; + + writel(val, reg); + + return 0; +} + +bool pcc_clock_is_enable(enum pcc_clk clk) +{ + u32 reg, val; + + if (clk >= ARRAY_SIZE(pcc_arrays)) + return -EINVAL; + + reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4; + val = readl(reg); + + if ((val & PCC_INUSE_MASK) || (val & PCC_CGC_MASK)) + return true; + + return false; +} + +int pcc_clock_get_clksrc(enum pcc_clk clk, enum scg_clk *src) +{ + u32 reg, val, clksrc_type; + + if (clk >= ARRAY_SIZE(pcc_arrays)) + return -EINVAL; + + clksrc_type = pcc_arrays[clk].clksrc; + if (clksrc_type >= CLKSRC_NO_PCS) { + printf("No PCS field for the PCC %d, clksrc type %d\n", + clk, clksrc_type); + return -EPERM; + } + + reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4; + + val = readl(reg); + + clk_debug("pcc_clock_get_clksrc: clk %d, reg 0x%x, val 0x%x, type %d\n", + clk, reg, val, clksrc_type); + + if (!(val & PCC_PR_MASK)) { + printf("This pcc slot is not present = 0x%x\n", val); + return -EPERM; + } + + val &= PCC_PCS_MASK; + val = (val >> PCC_PCS_OFFSET); + + if (!val) { + printf("Clock source is off\n"); + return -EIO; + } + + *src = pcc_clksrc[clksrc_type][val - 1]; + + clk_debug("pcc_clock_get_clksrc: parent scg clk %d\n", *src); + + return 0; +} + +u32 pcc_clock_get_rate(enum pcc_clk clk) +{ + u32 reg, val, rate, frac, div; + enum scg_clk parent; + int ret; + + ret = pcc_clock_get_clksrc(clk, &parent); + if (ret) + return 0; + + rate = scg_clk_get_rate(parent); + + clk_debug("pcc_clock_get_rate: parent rate %u\n", rate); + + if (pcc_arrays[clk].div == PCC_HAS_DIV) { + reg = pcc_arrays[clk].pcc_base + pcc_arrays[clk].pcc_slot * 4; + val = readl(reg); + + frac = (val & PCC_FRAC_MASK) >> PCC_FRAC_OFFSET; + div = (val & PCC_PCD_MASK) >> PCC_PCD_OFFSET; + + /* + * Theoretically don't have overflow in the calc, + * the rate won't exceed 2G + */ + rate = rate * (frac + 1) / (div + 1); + } + + clk_debug("pcc_clock_get_rate: rate %u\n", rate); + return rate; +} diff --git a/arch/arm/cpu/armv7/mx7ulp/scg.c b/arch/arm/cpu/armv7/mx7ulp/scg.c new file mode 100644 index 0000000..ca8252d --- /dev/null +++ b/arch/arm/cpu/armv7/mx7ulp/scg.c @@ -0,0 +1,1086 @@ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +scg_p scg1_regs = (scg_p)SCG1_RBASE; + +static u32 scg_src_get_rate(enum scg_clk clksrc) +{ + u32 reg; + + switch (clksrc) { + case SCG_SOSC_CLK: + reg = readl(&scg1_regs->sosccsr); + if (!(reg & SCG_SOSC_CSR_SOSCVLD_MASK)) + return 0; + + return 24000000; + case SCG_FIRC_CLK: + reg = readl(&scg1_regs->firccsr); + if (!(reg & SCG_FIRC_CSR_FIRCVLD_MASK)) + return 0; + + return 48000000; + case SCG_SIRC_CLK: + reg = readl(&scg1_regs->sirccsr); + if (!(reg & SCG_SIRC_CSR_SIRCVLD_MASK)) + return 0; + + return 16000000; + case SCG_ROSC_CLK: + reg = readl(&scg1_regs->rtccsr); + if (!(reg & SCG_ROSC_CSR_ROSCVLD_MASK)) + return 0; + + return 32768; + default: + break; + } + + return 0; +} + +static u32 scg_sircdiv_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + u32 shift, mask; + + switch (clk) { + case SCG_SIRC_DIV1_CLK: + mask = SCG_SIRCDIV_DIV1_MASK; + shift = SCG_SIRCDIV_DIV1_SHIFT; + break; + case SCG_SIRC_DIV2_CLK: + mask = SCG_SIRCDIV_DIV2_MASK; + shift = SCG_SIRCDIV_DIV2_SHIFT; + break; + case SCG_SIRC_DIV3_CLK: + mask = SCG_SIRCDIV_DIV3_MASK; + shift = SCG_SIRCDIV_DIV3_SHIFT; + break; + default: + return 0; + } + + reg = readl(&scg1_regs->sirccsr); + if (!(reg & SCG_SIRC_CSR_SIRCVLD_MASK)) + return 0; + + reg = readl(&scg1_regs->sircdiv); + val = (reg & mask) >> shift; + + if (!val) /*clock disabled*/ + return 0; + + rate = scg_src_get_rate(SCG_SIRC_CLK); + rate = rate / (1 << (val - 1)); + + return rate; +} + +static u32 scg_fircdiv_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + u32 shift, mask; + + switch (clk) { + case SCG_FIRC_DIV1_CLK: + mask = SCG_FIRCDIV_DIV1_MASK; + shift = SCG_FIRCDIV_DIV1_SHIFT; + break; + case SCG_FIRC_DIV2_CLK: + mask = SCG_FIRCDIV_DIV2_MASK; + shift = SCG_FIRCDIV_DIV2_SHIFT; + break; + case SCG_FIRC_DIV3_CLK: + mask = SCG_FIRCDIV_DIV3_MASK; + shift = SCG_FIRCDIV_DIV3_SHIFT; + break; + default: + return 0; + } + + reg = readl(&scg1_regs->firccsr); + if (!(reg & SCG_FIRC_CSR_FIRCVLD_MASK)) + return 0; + + reg = readl(&scg1_regs->fircdiv); + val = (reg & mask) >> shift; + + if (!val) /*clock disabled*/ + return 0; + + rate = scg_src_get_rate(SCG_FIRC_CLK); + rate = rate / (1 << (val - 1)); + + return rate; +} + +static u32 scg_soscdiv_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + u32 shift, mask; + + switch (clk) { + case SCG_SOSC_DIV1_CLK: + mask = SCG_SOSCDIV_DIV1_MASK; + shift = SCG_SOSCDIV_DIV1_SHIFT; + break; + case SCG_SOSC_DIV2_CLK: + mask = SCG_SOSCDIV_DIV2_MASK; + shift = SCG_SOSCDIV_DIV2_SHIFT; + break; + case SCG_SOSC_DIV3_CLK: + mask = SCG_SOSCDIV_DIV3_MASK; + shift = SCG_SOSCDIV_DIV3_SHIFT; + break; + default: + return 0; + } + + reg = readl(&scg1_regs->sosccsr); + if (!(reg & SCG_SOSC_CSR_SOSCVLD_MASK)) + return 0; + + reg = readl(&scg1_regs->soscdiv); + val = (reg & mask) >> shift; + + if (!val) /*clock disabled*/ + return 0; + + rate = scg_src_get_rate(SCG_SOSC_CLK); + rate = rate / (1 << (val - 1)); + + return rate; +} + +static u32 scg_apll_pfd_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + u32 shift, mask, gate, valid; + + switch (clk) { + case SCG_APLL_PFD0_CLK: + gate = SCG_PLL_PFD0_GATE_MASK; + valid = SCG_PLL_PFD0_VALID_MASK; + mask = SCG_PLL_PFD0_FRAC_MASK; + shift = SCG_PLL_PFD0_FRAC_SHIFT; + break; + case SCG_APLL_PFD1_CLK: + gate = SCG_PLL_PFD1_GATE_MASK; + valid = SCG_PLL_PFD1_VALID_MASK; + mask = SCG_PLL_PFD1_FRAC_MASK; + shift = SCG_PLL_PFD1_FRAC_SHIFT; + break; + case SCG_APLL_PFD2_CLK: + gate = SCG_PLL_PFD2_GATE_MASK; + valid = SCG_PLL_PFD2_VALID_MASK; + mask = SCG_PLL_PFD2_FRAC_MASK; + shift = SCG_PLL_PFD2_FRAC_SHIFT; + break; + case SCG_APLL_PFD3_CLK: + gate = SCG_PLL_PFD3_GATE_MASK; + valid = SCG_PLL_PFD3_VALID_MASK; + mask = SCG_PLL_PFD3_FRAC_MASK; + shift = SCG_PLL_PFD3_FRAC_SHIFT; + break; + default: + return 0; + } + + reg = readl(&scg1_regs->apllpfd); + if (reg & gate || !(reg & valid)) + return 0; + + clk_debug("scg_apll_pfd_get_rate reg 0x%x\n", reg); + + val = (reg & mask) >> shift; + rate = decode_pll(PLL_A7_APLL); + + rate = rate / val * 18; + + clk_debug("scg_apll_pfd_get_rate rate %u\n", rate); + + return rate; +} + +static u32 scg_spll_pfd_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + u32 shift, mask, gate, valid; + + switch (clk) { + case SCG_SPLL_PFD0_CLK: + gate = SCG_PLL_PFD0_GATE_MASK; + valid = SCG_PLL_PFD0_VALID_MASK; + mask = SCG_PLL_PFD0_FRAC_MASK; + shift = SCG_PLL_PFD0_FRAC_SHIFT; + break; + case SCG_SPLL_PFD1_CLK: + gate = SCG_PLL_PFD1_GATE_MASK; + valid = SCG_PLL_PFD1_VALID_MASK; + mask = SCG_PLL_PFD1_FRAC_MASK; + shift = SCG_PLL_PFD1_FRAC_SHIFT; + break; + case SCG_SPLL_PFD2_CLK: + gate = SCG_PLL_PFD2_GATE_MASK; + valid = SCG_PLL_PFD2_VALID_MASK; + mask = SCG_PLL_PFD2_FRAC_MASK; + shift = SCG_PLL_PFD2_FRAC_SHIFT; + break; + case SCG_SPLL_PFD3_CLK: + gate = SCG_PLL_PFD3_GATE_MASK; + valid = SCG_PLL_PFD3_VALID_MASK; + mask = SCG_PLL_PFD3_FRAC_MASK; + shift = SCG_PLL_PFD3_FRAC_SHIFT; + break; + default: + return 0; + } + + reg = readl(&scg1_regs->spllpfd); + if (reg & gate || !(reg & valid)) + return 0; + + clk_debug("scg_spll_pfd_get_rate reg 0x%x\n", reg); + + val = (reg & mask) >> shift; + rate = decode_pll(PLL_A7_SPLL); + + rate = rate / val * 18; + + clk_debug("scg_spll_pfd_get_rate rate %u\n", rate); + + return rate; +} + +static u32 scg_apll_get_rate(void) +{ + u32 reg, val, rate; + + reg = readl(&scg1_regs->apllcfg); + val = (reg & SCG_PLL_CFG_PLLSEL_MASK) >> SCG_PLL_CFG_PLLSEL_SHIFT; + + if (!val) { + /* APLL clock after two dividers */ + rate = decode_pll(PLL_A7_APLL); + + val = (reg & SCG_PLL_CFG_POSTDIV1_MASK) >> + SCG_PLL_CFG_POSTDIV1_SHIFT; + rate = rate / (val + 1); + + val = (reg & SCG_PLL_CFG_POSTDIV2_MASK) >> + SCG_PLL_CFG_POSTDIV2_SHIFT; + rate = rate / (val + 1); + } else { + /* APLL PFD clock */ + val = (reg & SCG_PLL_CFG_PFDSEL_MASK) >> + SCG_PLL_CFG_PFDSEL_SHIFT; + rate = scg_apll_pfd_get_rate(SCG_APLL_PFD0_CLK + val); + } + + return rate; +} + +static u32 scg_spll_get_rate(void) +{ + u32 reg, val, rate; + + reg = readl(&scg1_regs->spllcfg); + val = (reg & SCG_PLL_CFG_PLLSEL_MASK) >> SCG_PLL_CFG_PLLSEL_SHIFT; + + clk_debug("scg_spll_get_rate reg 0x%x\n", reg); + + if (!val) { + /* APLL clock after two dividers */ + rate = decode_pll(PLL_A7_SPLL); + + val = (reg & SCG_PLL_CFG_POSTDIV1_MASK) >> + SCG_PLL_CFG_POSTDIV1_SHIFT; + rate = rate / (val + 1); + + val = (reg & SCG_PLL_CFG_POSTDIV2_MASK) >> + SCG_PLL_CFG_POSTDIV2_SHIFT; + rate = rate / (val + 1); + + clk_debug("scg_spll_get_rate SPLL %u\n", rate); + + } else { + /* APLL PFD clock */ + val = (reg & SCG_PLL_CFG_PFDSEL_MASK) >> + SCG_PLL_CFG_PFDSEL_SHIFT; + rate = scg_spll_pfd_get_rate(SCG_SPLL_PFD0_CLK + val); + + clk_debug("scg_spll_get_rate PFD %u\n", rate); + } + + return rate; +} + +static u32 scg_ddr_get_rate(void) +{ + u32 reg, val, rate, div; + + reg = readl(&scg1_regs->ddrccr); + val = (reg & SCG_DDRCCR_DDRCS_MASK) >> SCG_DDRCCR_DDRCS_SHIFT; + div = (reg & SCG_DDRCCR_DDRDIV_MASK) >> SCG_DDRCCR_DDRDIV_SHIFT; + + if (!div) + return 0; + + if (!val) { + reg = readl(&scg1_regs->apllcfg); + val = (reg & SCG_PLL_CFG_PFDSEL_MASK) >> + SCG_PLL_CFG_PFDSEL_SHIFT; + rate = scg_apll_pfd_get_rate(SCG_APLL_PFD0_CLK + val); + } else { + rate = decode_pll(PLL_USB); + } + + rate = rate / (1 << (div - 1)); + return rate; +} + +static u32 scg_nic_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + u32 shift, mask; + + reg = readl(&scg1_regs->niccsr); + val = (reg & SCG_NICCSR_NICCS_MASK) >> SCG_NICCSR_NICCS_SHIFT; + + clk_debug("scg_nic_get_rate niccsr 0x%x\n", reg); + + if (!val) + rate = scg_src_get_rate(SCG_FIRC_CLK); + else + rate = scg_ddr_get_rate(); + + clk_debug("scg_nic_get_rate parent rate %u\n", rate); + + val = (reg & SCG_NICCSR_NIC0DIV_MASK) >> SCG_NICCSR_NIC0DIV_SHIFT; + + rate = rate / (val + 1); + + clk_debug("scg_nic_get_rate NIC0 rate %u\n", rate); + + switch (clk) { + case SCG_NIC0_CLK: + return rate; + case SCG_GPU_CLK: + mask = SCG_NICCSR_GPUDIV_MASK; + shift = SCG_NICCSR_GPUDIV_SHIFT; + break; + case SCG_NIC1_EXT_CLK: + case SCG_NIC1_BUS_CLK: + case SCG_NIC1_CLK: + mask = SCG_NICCSR_NIC1DIV_MASK; + shift = SCG_NICCSR_NIC1DIV_SHIFT; + break; + default: + return 0; + } + + val = (reg & mask) >> shift; + rate = rate / (val + 1); + + clk_debug("scg_nic_get_rate NIC1 rate %u\n", rate); + + switch (clk) { + case SCG_GPU_CLK: + case SCG_NIC1_CLK: + return rate; + case SCG_NIC1_EXT_CLK: + mask = SCG_NICCSR_NIC1EXTDIV_MASK; + shift = SCG_NICCSR_NIC1EXTDIV_SHIFT; + break; + case SCG_NIC1_BUS_CLK: + mask = SCG_NICCSR_NIC1BUSDIV_MASK; + shift = SCG_NICCSR_NIC1BUSDIV_SHIFT; + break; + default: + return 0; + } + + val = (reg & mask) >> shift; + rate = rate / (val + 1); + + clk_debug("scg_nic_get_rate NIC1 bus rate %u\n", rate); + return rate; +} + + +static enum scg_clk scg_scs_array[4] = { + SCG_SOSC_CLK, SCG_SIRC_CLK, SCG_FIRC_CLK, SCG_ROSC_CLK, +}; + +static u32 scg_sys_get_rate(enum scg_clk clk) +{ + u32 reg, val, rate; + + if (clk != SCG_CORE_CLK && clk != SCG_BUS_CLK) + return 0; + + reg = readl(&scg1_regs->csr); + val = (reg & SCG_CCR_SCS_MASK) >> SCG_CCR_SCS_SHIFT; + + clk_debug("scg_sys_get_rate reg 0x%x\n", reg); + + switch (val) { + case SCG_SCS_SYS_OSC: + case SCG_SCS_SLOW_IRC: + case SCG_SCS_FAST_IRC: + case SCG_SCS_RTC_OSC: + rate = scg_src_get_rate(scg_scs_array[val]); + break; + case 5: + rate = scg_apll_get_rate(); + break; + case 6: + rate = scg_spll_get_rate(); + break; + default: + return 0; + } + + clk_debug("scg_sys_get_rate parent rate %u\n", rate); + + val = (reg & SCG_CCR_DIVCORE_MASK) >> SCG_CCR_DIVCORE_SHIFT; + + rate = rate / (val + 1); + + if (clk == SCG_BUS_CLK) { + val = (reg & SCG_CCR_DIVBUS_MASK) >> SCG_CCR_DIVBUS_SHIFT; + rate = rate / (val + 1); + } + + return rate; +} + +u32 decode_pll(enum pll_clocks pll) +{ + u32 reg, pre_div, infreq, mult; + u32 num, denom; + + /* + * Alought there are four choices for the bypass src, + * we choose OSC_24M which is the default set in ROM. + */ + switch (pll) { + case PLL_A7_SPLL: + reg = readl(&scg1_regs->spllcsr); + + if (!(reg & SCG_SPLL_CSR_SPLLVLD_MASK)) + return 0; + + reg = readl(&scg1_regs->spllcfg); + + pre_div = (reg & SCG_PLL_CFG_PREDIV_MASK) >> + SCG_PLL_CFG_PREDIV_SHIFT; + pre_div += 1; + + mult = (reg & SCG1_SPLL_CFG_MULT_MASK) >> + SCG_PLL_CFG_MULT_SHIFT; + + infreq = (reg & SCG_PLL_CFG_CLKSRC_MASK) >> + SCG_PLL_CFG_CLKSRC_SHIFT; + if (!infreq) + infreq = scg_src_get_rate(SCG_SOSC_CLK); + else + infreq = scg_src_get_rate(SCG_FIRC_CLK); + + num = readl(&scg1_regs->spllnum); + denom = readl(&scg1_regs->splldenom); + + return (infreq / pre_div) * (mult + num / denom); + + case PLL_A7_APLL: + reg = readl(&scg1_regs->apllcsr); + + if (!(reg & SCG_APLL_CSR_APLLVLD_MASK)) + return 0; + + reg = readl(&scg1_regs->apllcfg); + + pre_div = (reg & SCG_PLL_CFG_PREDIV_MASK) >> + SCG_PLL_CFG_PREDIV_SHIFT; + pre_div += 1; + + mult = (reg & SCG_APLL_CFG_MULT_MASK) >> + SCG_PLL_CFG_MULT_SHIFT; + + infreq = (reg & SCG_PLL_CFG_CLKSRC_MASK) >> + SCG_PLL_CFG_CLKSRC_SHIFT; + if (!infreq) + infreq = scg_src_get_rate(SCG_SOSC_CLK); + else + infreq = scg_src_get_rate(SCG_FIRC_CLK); + + num = readl(&scg1_regs->apllnum); + denom = readl(&scg1_regs->aplldenom); + + return (infreq / pre_div) * (mult + num / denom); + + case PLL_USB: + reg = readl(&scg1_regs->upllcsr); + + if (!(reg & SCG_UPLL_CSR_UPLLVLD_MASK)) + return 0; + + return 480000000u; + + case PLL_MIPI: + return 480000000u; + default: + printf("Unsupported pll clocks %d\n", pll); + break; + } + + return 0; +} + +u32 scg_clk_get_rate(enum scg_clk clk) +{ + switch (clk) { + case SCG_SIRC_DIV1_CLK: + case SCG_SIRC_DIV2_CLK: + case SCG_SIRC_DIV3_CLK: + return scg_sircdiv_get_rate(clk); + + case SCG_FIRC_DIV1_CLK: + case SCG_FIRC_DIV2_CLK: + case SCG_FIRC_DIV3_CLK: + return scg_fircdiv_get_rate(clk); + + case SCG_SOSC_DIV1_CLK: + case SCG_SOSC_DIV2_CLK: + case SCG_SOSC_DIV3_CLK: + return scg_soscdiv_get_rate(clk); + + case SCG_CORE_CLK: + case SCG_BUS_CLK: + return scg_sys_get_rate(clk); + + case SCG_SPLL_PFD0_CLK: + case SCG_SPLL_PFD1_CLK: + case SCG_SPLL_PFD2_CLK: + case SCG_SPLL_PFD3_CLK: + return scg_spll_pfd_get_rate(clk); + + case SCG_APLL_PFD0_CLK: + case SCG_APLL_PFD1_CLK: + case SCG_APLL_PFD2_CLK: + case SCG_APLL_PFD3_CLK: + return scg_apll_pfd_get_rate(clk); + + case SCG_DDR_CLK: + return scg_ddr_get_rate(); + + case SCG_NIC0_CLK: + case SCG_GPU_CLK: + case SCG_NIC1_CLK: + case SCG_NIC1_BUS_CLK: + case SCG_NIC1_EXT_CLK: + return scg_nic_get_rate(clk); + + case USB_PLL_OUT: + return decode_pll(PLL_USB); + + case MIPI_PLL_OUT: + return decode_pll(PLL_MIPI); + + case SCG_SOSC_CLK: + case SCG_FIRC_CLK: + case SCG_SIRC_CLK: + case SCG_ROSC_CLK: + return scg_src_get_rate(clk); + default: + return 0; + } +} + +int scg_enable_pll_pfd(enum scg_clk clk, u32 frac) +{ + u32 reg; + u32 shift, mask, gate, valid; + u32 addr; + + if (frac < 12 || frac > 35) + return -EINVAL; + + switch (clk) { + case SCG_SPLL_PFD0_CLK: + case SCG_APLL_PFD0_CLK: + gate = SCG_PLL_PFD0_GATE_MASK; + valid = SCG_PLL_PFD0_VALID_MASK; + mask = SCG_PLL_PFD0_FRAC_MASK; + shift = SCG_PLL_PFD0_FRAC_SHIFT; + + if (clk == SCG_SPLL_PFD0_CLK) + addr = (u32)(&scg1_regs->spllpfd); + else + addr = (u32)(&scg1_regs->apllpfd); + break; + case SCG_SPLL_PFD1_CLK: + case SCG_APLL_PFD1_CLK: + gate = SCG_PLL_PFD1_GATE_MASK; + valid = SCG_PLL_PFD1_VALID_MASK; + mask = SCG_PLL_PFD1_FRAC_MASK; + shift = SCG_PLL_PFD1_FRAC_SHIFT; + + if (clk == SCG_SPLL_PFD1_CLK) + addr = (u32)(&scg1_regs->spllpfd); + else + addr = (u32)(&scg1_regs->apllpfd); + break; + case SCG_SPLL_PFD2_CLK: + case SCG_APLL_PFD2_CLK: + gate = SCG_PLL_PFD2_GATE_MASK; + valid = SCG_PLL_PFD2_VALID_MASK; + mask = SCG_PLL_PFD2_FRAC_MASK; + shift = SCG_PLL_PFD2_FRAC_SHIFT; + + if (clk == SCG_SPLL_PFD2_CLK) + addr = (u32)(&scg1_regs->spllpfd); + else + addr = (u32)(&scg1_regs->apllpfd); + break; + case SCG_SPLL_PFD3_CLK: + case SCG_APLL_PFD3_CLK: + gate = SCG_PLL_PFD3_GATE_MASK; + valid = SCG_PLL_PFD3_VALID_MASK; + mask = SCG_PLL_PFD3_FRAC_MASK; + shift = SCG_PLL_PFD3_FRAC_SHIFT; + + if (clk == SCG_SPLL_PFD3_CLK) + addr = (u32)(&scg1_regs->spllpfd); + else + addr = (u32)(&scg1_regs->apllpfd); + break; + default: + return -EINVAL; + } + + /* Gate the PFD */ + reg = readl(addr); + reg |= gate; + writel(reg, addr); + + /* Write Frac divider */ + reg &= ~mask; + reg |= (frac << shift) & mask; + writel(reg, addr); + + /* + * Un-gate the PFD + * (Need un-gate before checking valid, not align with RM) + */ + reg &= ~gate; + writel(reg, addr); + + /* Wait for PFD clock being valid */ + do { + reg = readl(addr); + } while (!(reg & valid)); + + return 0; +} + +#define SIM_MISC_CTRL0_USB_PLL_EN_MASK (0x1 << 2) +int scg_enable_usb_pll(bool usb_control) +{ + u32 sosc_rate; + s32 timeout = 1000000; + u32 reg; + + struct usbphy_regs *usbphy = + (struct usbphy_regs *)USBPHY_RBASE; + + sosc_rate = scg_src_get_rate(SCG_SOSC_CLK); + if (!sosc_rate) + return -EPERM; + + reg = readl(SIM0_RBASE + 0x3C); + if (usb_control) + reg &= ~SIM_MISC_CTRL0_USB_PLL_EN_MASK; + else + reg |= SIM_MISC_CTRL0_USB_PLL_EN_MASK; + writel(reg, SIM0_RBASE + 0x3C); + + if (!(readl(&usbphy->usb1_pll_480_ctrl) & PLL_USB_LOCK_MASK)) { + writel(0x1c00000, &usbphy->usb1_pll_480_ctrl_clr); + + switch (sosc_rate) { + case 24000000: + writel(0xc00000, &usbphy->usb1_pll_480_ctrl_set); + break; + + case 30000000: + writel(0x800000, &usbphy->usb1_pll_480_ctrl_set); + break; + + case 19200000: + writel(0x1400000, &usbphy->usb1_pll_480_ctrl_set); + break; + + default: + writel(0xc00000, &usbphy->usb1_pll_480_ctrl_set); + break; + } + + /* Enable the regulator first */ + writel(PLL_USB_REG_ENABLE_MASK, + &usbphy->usb1_pll_480_ctrl_set); + + /* Wait at least 15us */ + udelay(15); + + /* Enable the power */ + writel(PLL_USB_PWR_MASK, &usbphy->usb1_pll_480_ctrl_set); + + /* Wait lock */ + while (timeout--) { + if (readl(&usbphy->usb1_pll_480_ctrl) & + PLL_USB_LOCK_MASK) + break; + } + + if (timeout <= 0) { + /* If timeout, we power down the pll */ + writel(PLL_USB_PWR_MASK, + &usbphy->usb1_pll_480_ctrl_clr); + return -ETIME; + } + } + + /* Clear the bypass */ + writel(PLL_USB_BYPASS_MASK, &usbphy->usb1_pll_480_ctrl_clr); + + /* Enable the PLL clock out to USB */ + writel((PLL_USB_EN_USB_CLKS_MASK | PLL_USB_ENABLE_MASK), + &usbphy->usb1_pll_480_ctrl_set); + + if (!usb_control) { + while (timeout--) { + if (readl(&scg1_regs->upllcsr) & + SCG_UPLL_CSR_UPLLVLD_MASK) + break; + } + + if (timeout <= 0) { + reg = readl(SIM0_RBASE + 0x3C); + reg &= ~SIM_MISC_CTRL0_USB_PLL_EN_MASK; + writel(reg, SIM0_RBASE + 0x3C); + return -ETIME; + } + } + + return 0; +} + + +/* A7 domain system clock source is SPLL */ +#define SCG1_RCCR_SCS_NUM ((SCG_SCS_SYS_PLL) << SCG_CCR_SCS_SHIFT) + +/* A7 Core clck = SPLL PFD0 / 1 = 500MHz / 1 = 500MHz */ +#define SCG1_RCCR_DIVCORE_NUM ((0x0) << SCG_CCR_DIVCORE_SHIFT) +#define SCG1_RCCR_CFG_MASK (SCG_CCR_SCS_MASK | SCG_CCR_DIVBUS_MASK) + +/* A7 Plat clck = A7 Core Clock / 2 = 250MHz / 1 = 250MHz */ +#define SCG1_RCCR_DIVBUS_NUM ((0x1) << SCG_CCR_DIVBUS_SHIFT) +#define SCG1_RCCR_CFG_NUM (SCG1_RCCR_SCS_NUM | SCG1_RCCR_DIVBUS_NUM) + +void scg_a7_rccr_init(void) +{ + u32 rccr_reg_val = 0; + + rccr_reg_val = readl(&scg1_regs->rccr); + + rccr_reg_val &= (~SCG1_RCCR_CFG_MASK); + rccr_reg_val |= (SCG1_RCCR_CFG_NUM); + + writel(rccr_reg_val, &scg1_regs->rccr); +} + +/* POSTDIV2 = 1 */ +#define SCG1_SPLL_CFG_POSTDIV2_NUM ((0x0) << SCG_PLL_CFG_POSTDIV2_SHIFT) +/* POSTDIV1 = 1 */ +#define SCG1_SPLL_CFG_POSTDIV1_NUM ((0x0) << SCG_PLL_CFG_POSTDIV1_SHIFT) + +/* MULT = 22 */ +#define SCG1_SPLL_CFG_MULT_NUM ((22) << SCG_PLL_CFG_MULT_SHIFT) + +/* PFD0 output clock selected */ +#define SCG1_SPLL_CFG_PFDSEL_NUM ((0) << SCG_PLL_CFG_PFDSEL_SHIFT) +/* PREDIV = 1 */ +#define SCG1_SPLL_CFG_PREDIV_NUM ((0x0) << SCG_PLL_CFG_PREDIV_SHIFT) +/* SPLL output clocks (including PFD outputs) selected */ +#define SCG1_SPLL_CFG_BYPASS_NUM ((0x0) << SCG_PLL_CFG_BYPASS_SHIFT) +/* SPLL PFD output clock selected */ +#define SCG1_SPLL_CFG_PLLSEL_NUM ((0x1) << SCG_PLL_CFG_PLLSEL_SHIFT) +/* Clock source is System OSC */ +#define SCG1_SPLL_CFG_CLKSRC_NUM ((0x0) << SCG_PLL_CFG_CLKSRC_SHIFT) +#define SCG1_SPLL_CFG_NUM_24M_OSC (SCG1_SPLL_CFG_POSTDIV2_NUM | \ + SCG1_SPLL_CFG_POSTDIV1_NUM | \ + (22 << SCG_PLL_CFG_MULT_SHIFT) | \ + SCG1_SPLL_CFG_PFDSEL_NUM | \ + SCG1_SPLL_CFG_PREDIV_NUM | \ + SCG1_SPLL_CFG_BYPASS_NUM | \ + SCG1_SPLL_CFG_PLLSEL_NUM | \ + SCG1_SPLL_CFG_CLKSRC_NUM) +/*413Mhz = A7 SPLL(528MHz) * 18/23 */ +#define SCG1_SPLL_PFD0_FRAC_NUM ((23) << SCG_PLL_PFD0_FRAC_SHIFT) + +void scg_a7_spll_init(void) +{ + u32 val = 0; + + /* Disable A7 System PLL */ + val = readl(&scg1_regs->spllcsr); + val &= ~SCG_SPLL_CSR_SPLLEN_MASK; + writel(val, &scg1_regs->spllcsr); + + /* + * Per block guide, + * "When changing PFD values, it is recommneded PFDx clock + * gets gated first by writing a value of 1 to PFDx_CLKGATE register, + * then program the new PFD value, then poll the PFDx_VALID + * flag to set before writing a value of 0 to PFDx_CLKGATE + * to ungate the PFDx clock and allow PFDx clock to run" + */ + + /* Gate off A7 SPLL PFD0 ~ PDF4 */ + val = readl(&scg1_regs->spllpfd); + val |= (SCG_PLL_PFD3_GATE_MASK | + SCG_PLL_PFD2_GATE_MASK | + SCG_PLL_PFD1_GATE_MASK | + SCG_PLL_PFD0_GATE_MASK); + writel(val, &scg1_regs->spllpfd); + + /* ================ A7 SPLL Configuration Start ============== */ + + /* Configure A7 System PLL */ + writel(SCG1_SPLL_CFG_NUM_24M_OSC, &scg1_regs->spllcfg); + + /* Enable A7 System PLL */ + val = readl(&scg1_regs->spllcsr); + val |= SCG_SPLL_CSR_SPLLEN_MASK; + writel(val, &scg1_regs->spllcsr); + + /* Wait for A7 SPLL clock ready */ + while (!(readl(&scg1_regs->spllcsr) & SCG_SPLL_CSR_SPLLVLD_MASK)) + ; + + /* Configure A7 SPLL PFD0 */ + val = readl(&scg1_regs->spllpfd); + val &= ~SCG_PLL_PFD0_FRAC_MASK; + val |= SCG1_SPLL_PFD0_FRAC_NUM; + writel(val, &scg1_regs->spllpfd); + + /* Un-gate A7 SPLL PFD0 */ + val = readl(&scg1_regs->spllpfd); + val &= ~SCG_PLL_PFD0_GATE_MASK; + writel(val, &scg1_regs->spllpfd); + + /* Wait for A7 SPLL PFD0 clock being valid */ + while (!(readl(&scg1_regs->spllpfd) & SCG_PLL_PFD0_VALID_MASK)) + ; + + /* ================ A7 SPLL Configuration End ============== */ +} + +/* DDR clock source is APLL PFD0 (396MHz) */ +#define SCG1_DDRCCR_DDRCS_NUM ((0x0) << SCG_DDRCCR_DDRCS_SHIFT) +/* DDR clock = APLL PFD0 / 1 = 396MHz / 1 = 396MHz */ +#define SCG1_DDRCCR_DDRDIV_NUM ((0x1) << SCG_DDRCCR_DDRDIV_SHIFT) +/* DDR clock = APLL PFD0 / 2 = 396MHz / 2 = 198MHz */ +#define SCG1_DDRCCR_DDRDIV_LF_NUM ((0x2) << SCG_DDRCCR_DDRDIV_SHIFT) +#define SCG1_DDRCCR_CFG_NUM (SCG1_DDRCCR_DDRCS_NUM | \ + SCG1_DDRCCR_DDRDIV_NUM) +#define SCG1_DDRCCR_CFG_LF_NUM (SCG1_DDRCCR_DDRCS_NUM | \ + SCG1_DDRCCR_DDRDIV_LF_NUM) +void scg_a7_ddrclk_init(void) +{ + writel(SCG1_DDRCCR_CFG_NUM, &scg1_regs->ddrccr); +} + +/* SCG1(A7) APLLCFG configurations */ +/* divide by 1 <<28 */ +#define SCG1_APLL_CFG_POSTDIV2_NUM ((0x0) << SCG_PLL_CFG_POSTDIV2_SHIFT) +/* divide by 1 <<24 */ +#define SCG1_APLL_CFG_POSTDIV1_NUM ((0x0) << SCG_PLL_CFG_POSTDIV1_SHIFT) +/* MULT is 22 <<16 */ +#define SCG1_APLL_CFG_MULT_NUM ((22) << SCG_PLL_CFG_MULT_SHIFT) +/* PFD0 output clock selected <<14 */ +#define SCG1_APLL_CFG_PFDSEL_NUM ((0) << SCG_PLL_CFG_PFDSEL_SHIFT) +/* PREDIV = 1 <<8 */ +#define SCG1_APLL_CFG_PREDIV_NUM ((0x0) << SCG_PLL_CFG_PREDIV_SHIFT) +/* APLL output clocks (including PFD outputs) selected <<2 */ +#define SCG1_APLL_CFG_BYPASS_NUM ((0x0) << SCG_PLL_CFG_BYPASS_SHIFT) +/* APLL PFD output clock selected <<1 */ +#define SCG1_APLL_CFG_PLLSEL_NUM ((0x0) << SCG_PLL_CFG_PLLSEL_SHIFT) +/* Clock source is System OSC <<0 */ +#define SCG1_APLL_CFG_CLKSRC_NUM ((0x0) << SCG_PLL_CFG_CLKSRC_SHIFT) + +/* + * A7 APLL = 24MHz / 1 * 22 / 1 / 1 = 528MHz, + * system PLL is sourced from APLL, + * APLL clock source is system OSC (24MHz) + */ +#define SCG1_APLL_CFG_NUM_24M_OSC (SCG1_APLL_CFG_POSTDIV2_NUM | \ + SCG1_APLL_CFG_POSTDIV1_NUM | \ + (22 << SCG_PLL_CFG_MULT_SHIFT) | \ + SCG1_APLL_CFG_PFDSEL_NUM | \ + SCG1_APLL_CFG_PREDIV_NUM | \ + SCG1_APLL_CFG_BYPASS_NUM | \ + SCG1_APLL_CFG_PLLSEL_NUM | \ + SCG1_APLL_CFG_CLKSRC_NUM) + +/* PFD0 Freq = A7 APLL(528MHz) * 18 / 27 = 352MHz */ +#define SCG1_APLL_PFD0_FRAC_NUM (27) + + +void scg_a7_apll_init(void) +{ + u32 val = 0; + + /* Disable A7 Auxiliary PLL */ + val = readl(&scg1_regs->apllcsr); + val &= ~SCG_APLL_CSR_APLLEN_MASK; + writel(val, &scg1_regs->apllcsr); + + /* Gate off A7 APLL PFD0 ~ PDF4 */ + val = readl(&scg1_regs->apllpfd); + val |= 0x80808080; + writel(val, &scg1_regs->apllpfd); + + /* ================ A7 APLL Configuration Start ============== */ + /* Configure A7 Auxiliary PLL */ + writel(SCG1_APLL_CFG_NUM_24M_OSC, &scg1_regs->apllcfg); + + /* Enable A7 Auxiliary PLL */ + val = readl(&scg1_regs->apllcsr); + val |= SCG_APLL_CSR_APLLEN_MASK; + writel(val, &scg1_regs->apllcsr); + + /* Wait for A7 APLL clock ready */ + while (!(readl(&scg1_regs->apllcsr) & SCG_APLL_CSR_APLLVLD_MASK)) + ; + + /* Configure A7 APLL PFD0 */ + val = readl(&scg1_regs->apllpfd); + val &= ~SCG_PLL_PFD0_FRAC_MASK; + val |= SCG1_APLL_PFD0_FRAC_NUM; + writel(val, &scg1_regs->apllpfd); + + /* Un-gate A7 APLL PFD0 */ + val = readl(&scg1_regs->apllpfd); + val &= ~SCG_PLL_PFD0_GATE_MASK; + writel(val, &scg1_regs->apllpfd); + + /* Wait for A7 APLL PFD0 clock being valid */ + while (!(readl(&scg1_regs->apllpfd) & SCG_PLL_PFD0_VALID_MASK)) + ; +} + +/* SCG1(A7) FIRC DIV configurations */ +/* Disable FIRC DIV3 */ +#define SCG1_FIRCDIV_DIV3_NUM ((0x0) << SCG_FIRCDIV_DIV3_SHIFT) +/* FIRC DIV2 = 48MHz / 1 = 48MHz */ +#define SCG1_FIRCDIV_DIV2_NUM ((0x1) << SCG_FIRCDIV_DIV2_SHIFT) +/* Disable FIRC DIV1 */ +#define SCG1_FIRCDIV_DIV1_NUM ((0x0) << SCG_FIRCDIV_DIV1_SHIFT) + +void scg_a7_firc_init(void) +{ + /* Wait for FIRC clock ready */ + while (!(readl(&scg1_regs->firccsr) & SCG_FIRC_CSR_FIRCVLD_MASK)) + ; + + /* Configure A7 FIRC DIV1 ~ DIV3 */ + writel((SCG1_FIRCDIV_DIV3_NUM | + SCG1_FIRCDIV_DIV2_NUM | + SCG1_FIRCDIV_DIV1_NUM), &scg1_regs->fircdiv); +} + +/* SCG1(A7) NICCCR configurations */ +/* NIC clock source is DDR clock (396/198MHz) */ +#define SCG1_NICCCR_NICCS_NUM ((0x1) << SCG_NICCCR_NICCS_SHIFT) + +/* NIC0 clock = DDR Clock / 2 = 396MHz / 2 = 198MHz */ +#define SCG1_NICCCR_NIC0_DIV_NUM ((0x1) << SCG_NICCCR_NIC0_DIV_SHIFT) +/* NIC0 clock = DDR Clock / 1 = 198MHz / 1 = 198MHz */ +#define SCG1_NICCCR_NIC0_DIV_LF_NUM ((0x0) << SCG_NICCCR_NIC0_DIV_SHIFT) +/* NIC1 clock = NIC0 Clock / 1 = 198MHz / 2 = 198MHz */ +#define SCG1_NICCCR_NIC1_DIV_NUM ((0x0) << SCG_NICCCR_NIC1_DIV_SHIFT) +/* NIC1 bus clock = NIC1 Clock / 3 = 198MHz / 3 = 66MHz */ +#define SCG1_NICCCR_NIC1_DIVBUS_NUM ((0x2) << SCG_NICCCR_NIC1_DIVBUS_SHIFT) +#define SCG1_NICCCR_CFG_NUM (SCG1_NICCCR_NICCS_NUM | \ + SCG1_NICCCR_NIC0_DIV_NUM | \ + SCG1_NICCCR_NIC1_DIV_NUM | \ + SCG1_NICCCR_NIC1_DIVBUS_NUM) + +void scg_a7_nicclk_init(void) +{ + writel(SCG1_NICCCR_CFG_NUM, &scg1_regs->nicccr); +} + +/* SCG1(A7) FIRC DIV configurations */ +/* Enable FIRC DIV3 */ +#define SCG1_SOSCDIV_DIV3_NUM ((0x1) << SCG_SOSCDIV_DIV3_SHIFT) +/* FIRC DIV2 = 48MHz / 1 = 48MHz */ +#define SCG1_SOSCDIV_DIV2_NUM ((0x1) << SCG_SOSCDIV_DIV2_SHIFT) +/* Enable FIRC DIV1 */ +#define SCG1_SOSCDIV_DIV1_NUM ((0x1) << SCG_SOSCDIV_DIV1_SHIFT) + +void scg_a7_soscdiv_init(void) +{ + /* Wait for FIRC clock ready */ + while (!(readl(&scg1_regs->sosccsr) & SCG_SOSC_CSR_SOSCVLD_MASK)) + ; + + /* Configure A7 FIRC DIV1 ~ DIV3 */ + writel((SCG1_SOSCDIV_DIV3_NUM | SCG1_SOSCDIV_DIV2_NUM | + SCG1_SOSCDIV_DIV1_NUM), &scg1_regs->soscdiv); +} + +void scg_a7_sys_clk_sel(enum scg_sys_src clk) +{ + u32 rccr_reg_val = 0; + + clk_debug("%s: system clock selected as %s\n", "[SCG]", + clk == SCG_SCS_SYS_OSC ? "SYS_OSC" : + clk == SCG_SCS_SLOW_IRC ? "SLOW_IRC" : + clk == SCG_SCS_FAST_IRC ? "FAST_IRC" : + clk == SCG_SCS_RTC_OSC ? "RTC_OSC" : + clk == SCG_SCS_AUX_PLL ? "AUX_PLL" : + clk == SCG_SCS_SYS_PLL ? "SYS_PLL" : + clk == SCG_SCS_USBPHY_PLL ? "USBPHY_PLL" : + "Invalid source" + ); + + rccr_reg_val = readl(&scg1_regs->rccr); + rccr_reg_val &= ~SCG_CCR_SCS_MASK; + rccr_reg_val |= (clk << SCG_CCR_SCS_SHIFT); + writel(rccr_reg_val, &scg1_regs->rccr); +} + +void scg_a7_info(void) +{ + debug("SCG Version: 0x%x\n", readl(&scg1_regs->verid)); + debug("SCG Parameter: 0x%x\n", readl(&scg1_regs->param)); + debug("SCG RCCR Value: 0x%x\n", readl(&scg1_regs->rccr)); + debug("SCG Clock Status: 0x%x\n", readl(&scg1_regs->csr)); +} -- cgit v1.1