York Sun | a84cd72 | 2014-06-23 15:15:54 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2014, Freescale Semiconductor, Inc. |
| 3 | * |
| 4 | * SPDX-License-Identifier: GPL-2.0+ |
| 5 | * |
| 6 | * Derived from arch/power/cpu/mpc85xx/speed.c |
| 7 | */ |
| 8 | |
| 9 | #include <common.h> |
| 10 | #include <linux/compiler.h> |
| 11 | #include <fsl_ifc.h> |
| 12 | #include <asm/processor.h> |
| 13 | #include <asm/io.h> |
| 14 | #include <asm/arch-fsl-lsch3/immap_lsch3.h> |
| 15 | #include <asm/arch/clock.h> |
| 16 | #include "cpu.h" |
| 17 | |
| 18 | DECLARE_GLOBAL_DATA_PTR; |
| 19 | |
| 20 | #ifndef CONFIG_SYS_FSL_NUM_CC_PLLS |
| 21 | #define CONFIG_SYS_FSL_NUM_CC_PLLS 6 |
| 22 | #endif |
| 23 | |
| 24 | |
| 25 | void get_sys_info(struct sys_info *sys_info) |
| 26 | { |
| 27 | struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR); |
| 28 | #ifdef CONFIG_FSL_IFC |
| 29 | struct fsl_ifc *ifc_regs = (void *)CONFIG_SYS_IFC_ADDR; |
| 30 | u32 ccr; |
| 31 | #endif |
| 32 | struct ccsr_clk_cluster_group __iomem *clk_grp[2] = { |
| 33 | (void *)(CONFIG_SYS_FSL_CH3_CLK_GRPA_ADDR), |
| 34 | (void *)(CONFIG_SYS_FSL_CH3_CLK_GRPB_ADDR) |
| 35 | }; |
| 36 | struct ccsr_clk_ctrl __iomem *clk_ctrl = |
| 37 | (void *)(CONFIG_SYS_FSL_CH3_CLK_CTRL_ADDR); |
| 38 | unsigned int cpu; |
| 39 | const u8 core_cplx_pll[16] = { |
| 40 | [0] = 0, /* CC1 PPL / 1 */ |
| 41 | [1] = 0, /* CC1 PPL / 2 */ |
| 42 | [2] = 0, /* CC1 PPL / 4 */ |
| 43 | [4] = 1, /* CC2 PPL / 1 */ |
| 44 | [5] = 1, /* CC2 PPL / 2 */ |
| 45 | [6] = 1, /* CC2 PPL / 4 */ |
| 46 | [8] = 2, /* CC3 PPL / 1 */ |
| 47 | [9] = 2, /* CC3 PPL / 2 */ |
| 48 | [10] = 2, /* CC3 PPL / 4 */ |
| 49 | [12] = 3, /* CC4 PPL / 1 */ |
| 50 | [13] = 3, /* CC4 PPL / 2 */ |
| 51 | [14] = 3, /* CC4 PPL / 4 */ |
| 52 | }; |
| 53 | |
| 54 | const u8 core_cplx_pll_div[16] = { |
| 55 | [0] = 1, /* CC1 PPL / 1 */ |
| 56 | [1] = 2, /* CC1 PPL / 2 */ |
| 57 | [2] = 4, /* CC1 PPL / 4 */ |
| 58 | [4] = 1, /* CC2 PPL / 1 */ |
| 59 | [5] = 2, /* CC2 PPL / 2 */ |
| 60 | [6] = 4, /* CC2 PPL / 4 */ |
| 61 | [8] = 1, /* CC3 PPL / 1 */ |
| 62 | [9] = 2, /* CC3 PPL / 2 */ |
| 63 | [10] = 4, /* CC3 PPL / 4 */ |
| 64 | [12] = 1, /* CC4 PPL / 1 */ |
| 65 | [13] = 2, /* CC4 PPL / 2 */ |
| 66 | [14] = 4, /* CC4 PPL / 4 */ |
| 67 | }; |
| 68 | |
| 69 | uint i, cluster; |
| 70 | uint freq_c_pll[CONFIG_SYS_FSL_NUM_CC_PLLS]; |
| 71 | uint ratio[CONFIG_SYS_FSL_NUM_CC_PLLS]; |
| 72 | unsigned long sysclk = CONFIG_SYS_CLK_FREQ; |
| 73 | int cc_group[12] = CONFIG_SYS_FSL_CLUSTER_CLOCKS; |
| 74 | u32 c_pll_sel, cplx_pll; |
| 75 | void *offset; |
| 76 | |
| 77 | sys_info->freq_systembus = sysclk; |
| 78 | #ifdef CONFIG_DDR_CLK_FREQ |
| 79 | sys_info->freq_ddrbus = CONFIG_DDR_CLK_FREQ; |
| 80 | #else |
| 81 | sys_info->freq_ddrbus = sysclk; |
| 82 | #endif |
| 83 | |
| 84 | sys_info->freq_systembus *= (in_le32(&gur->rcwsr[0]) >> |
| 85 | FSL_CHASSIS3_RCWSR0_SYS_PLL_RAT_SHIFT) & |
| 86 | FSL_CHASSIS3_RCWSR0_SYS_PLL_RAT_MASK; |
| 87 | sys_info->freq_ddrbus *= (in_le32(&gur->rcwsr[0]) >> |
| 88 | FSL_CHASSIS3_RCWSR0_MEM_PLL_RAT_SHIFT) & |
| 89 | FSL_CHASSIS3_RCWSR0_MEM_PLL_RAT_MASK; |
| 90 | |
| 91 | for (i = 0; i < CONFIG_SYS_FSL_NUM_CC_PLLS; i++) { |
| 92 | /* |
| 93 | * fixme: prefer to combine the following into one line, but |
| 94 | * cannot pass compiling without warning about in_le32. |
| 95 | */ |
| 96 | offset = (void *)((size_t)clk_grp[i/3] + |
| 97 | offsetof(struct ccsr_clk_cluster_group, |
| 98 | pllngsr[i%3].gsr)); |
| 99 | ratio[i] = (in_le32(offset) >> 1) & 0x3f; |
| 100 | if (ratio[i] > 4) |
| 101 | freq_c_pll[i] = sysclk * ratio[i]; |
| 102 | else |
| 103 | freq_c_pll[i] = sys_info->freq_systembus * ratio[i]; |
| 104 | } |
| 105 | |
| 106 | for_each_cpu(i, cpu, cpu_numcores(), cpu_mask()) { |
| 107 | cluster = fsl_qoriq_core_to_cluster(cpu); |
| 108 | c_pll_sel = (in_le32(&clk_ctrl->clkcncsr[cluster].csr) >> 27) |
| 109 | & 0xf; |
| 110 | cplx_pll = core_cplx_pll[c_pll_sel]; |
| 111 | cplx_pll += cc_group[cluster] - 1; |
| 112 | sys_info->freq_processor[cpu] = |
| 113 | freq_c_pll[cplx_pll] / core_cplx_pll_div[c_pll_sel]; |
| 114 | } |
| 115 | |
| 116 | #if defined(CONFIG_FSL_IFC) |
| 117 | ccr = in_le32(&ifc_regs->ifc_ccr); |
| 118 | ccr = ((ccr & IFC_CCR_CLK_DIV_MASK) >> IFC_CCR_CLK_DIV_SHIFT) + 1; |
| 119 | |
| 120 | sys_info->freq_localbus = sys_info->freq_systembus / ccr; |
| 121 | #endif |
| 122 | } |
| 123 | |
| 124 | |
| 125 | int get_clocks(void) |
| 126 | { |
| 127 | struct sys_info sys_info; |
| 128 | get_sys_info(&sys_info); |
| 129 | gd->cpu_clk = sys_info.freq_processor[0]; |
| 130 | gd->bus_clk = sys_info.freq_systembus; |
| 131 | gd->mem_clk = sys_info.freq_ddrbus; |
| 132 | |
| 133 | #if defined(CONFIG_FSL_ESDHC) |
| 134 | gd->arch.sdhc_clk = gd->bus_clk / 2; |
| 135 | #endif /* defined(CONFIG_FSL_ESDHC) */ |
| 136 | |
| 137 | if (gd->cpu_clk != 0) |
| 138 | return 0; |
| 139 | else |
| 140 | return 1; |
| 141 | } |
| 142 | |
| 143 | /******************************************** |
| 144 | * get_bus_freq |
| 145 | * return system bus freq in Hz |
| 146 | *********************************************/ |
| 147 | ulong get_bus_freq(ulong dummy) |
| 148 | { |
| 149 | if (!gd->bus_clk) |
| 150 | get_clocks(); |
| 151 | |
| 152 | return gd->bus_clk; |
| 153 | } |
| 154 | |
| 155 | /******************************************** |
| 156 | * get_ddr_freq |
| 157 | * return ddr bus freq in Hz |
| 158 | *********************************************/ |
| 159 | ulong get_ddr_freq(ulong dummy) |
| 160 | { |
| 161 | if (!gd->mem_clk) |
| 162 | get_clocks(); |
| 163 | |
| 164 | return gd->mem_clk; |
| 165 | } |
| 166 | |
| 167 | unsigned int mxc_get_clock(enum mxc_clock clk) |
| 168 | { |
| 169 | switch (clk) { |
| 170 | case MXC_I2C_CLK: |
| 171 | return get_bus_freq(0) / 2; |
| 172 | default: |
| 173 | printf("Unsupported clock\n"); |
| 174 | } |
| 175 | return 0; |
| 176 | } |