Alexandru Gagniuc | bc30b2b | 2013-12-24 16:48:03 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Helpers for clock control and gating on Allwinner CPUs |
| 3 | * |
| 4 | * Copyright (C) 2013 Alexandru Gagniuc <mr.nuke.me@gmail.com> |
| 5 | * Subject to the GNU GPL v2, or (at your option) any later version. |
| 6 | */ |
| 7 | |
| 8 | #include "clock.h" |
| 9 | |
| 10 | #include <arch/io.h> |
Alexandru Gagniuc | 601b5b5 | 2013-12-28 19:29:36 -0500 | [diff] [blame] | 11 | #include <console/console.h> |
| 12 | #include <delay.h> |
| 13 | #include <lib.h> |
| 14 | #include <stdlib.h> |
Alexandru Gagniuc | bc30b2b | 2013-12-24 16:48:03 -0500 | [diff] [blame] | 15 | |
Alexandru Gagniuc | cce6c1c | 2013-12-31 13:20:27 -0500 | [diff] [blame] | 16 | static struct a10_ccm *const ccm = (void *)A1X_CCM_BASE; |
| 17 | |
Alexandru Gagniuc | bc30b2b | 2013-12-24 16:48:03 -0500 | [diff] [blame] | 18 | /** |
| 19 | * \brief Enable the clock source for the peripheral |
| 20 | * |
| 21 | * @param[in] periph peripheral and clock type to enable @see a1x_clken |
| 22 | */ |
| 23 | void a1x_periph_clock_enable(enum a1x_clken periph) |
| 24 | { |
| 25 | void *addr; |
| 26 | u32 reg32; |
| 27 | |
| 28 | addr = (void *)A1X_CCM_BASE + (periph >> 5); |
| 29 | reg32 = read32(addr); |
| 30 | reg32 |= 1 << (periph & 0x1f); |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 31 | write32(addr, reg32); |
Alexandru Gagniuc | bc30b2b | 2013-12-24 16:48:03 -0500 | [diff] [blame] | 32 | } |
| 33 | |
| 34 | /** |
| 35 | * \brief Disable the clock source for the peripheral |
| 36 | * |
| 37 | * @param[in] periph peripheral and clock type to disable @see a1x_clken |
| 38 | */ |
| 39 | void a1x_periph_clock_disable(enum a1x_clken periph) |
| 40 | { |
| 41 | void *addr; |
| 42 | u32 reg32; |
| 43 | |
| 44 | addr = (void *)A1X_CCM_BASE + (periph >> 5); |
| 45 | reg32 = read32(addr); |
| 46 | reg32 &= ~(1 << (periph & 0x1f)); |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 47 | write32(addr, reg32); |
Alexandru Gagniuc | bc30b2b | 2013-12-24 16:48:03 -0500 | [diff] [blame] | 48 | } |
Alexandru Gagniuc | cce6c1c | 2013-12-31 13:20:27 -0500 | [diff] [blame] | 49 | |
| 50 | /** |
| 51 | * \brief Configure PLL5 factors |
| 52 | * |
| 53 | * This is a low-level accessor to configure the divisors and multipliers of |
| 54 | * PLL5. PLL5 uses two factors to multiply the 24MHz oscillator clock to |
| 55 | * generate a pre-clock. The pre-divided clock is then divided by one of two |
| 56 | * independent divisors, one for DRAM, and another for peripherals clocked from |
| 57 | * this PLL. If the PLL was previously disabled, this function will enable it. |
| 58 | * Other than that, this function only modifies these factors, and leaves the |
| 59 | * other settings unchanged. |
| 60 | * |
| 61 | * The output clocks are given by the following formulas: |
| 62 | * |
| 63 | * Pre-clock = (24 MHz * N * K) <- Must be between 240MHz and 2GHz |
| 64 | * DRAM clock = pre / M |
| 65 | * Other module = pre / P |
| 66 | * |
| 67 | * It is the caller's responsibility to make sure the pre-divided clock falls |
| 68 | * within the operational range of the PLL, and that the divisors and |
| 69 | * multipliers are within their ranges. |
| 70 | * |
| 71 | * @param[in] mul_n Multiplier N, between 0 and 32 |
| 72 | * @param[in] mul_k Multiplier K, between 1 and 4 |
| 73 | * @param[in] div_m DRAM clock divisor, between 1 and 4 |
| 74 | * @param[in] exp_div_p Peripheral clock divisor exponent, between 0 and 3 |
| 75 | * (P = 1/2/4/8, respectively) |
| 76 | */ |
| 77 | void a1x_pll5_configure(u8 mul_n, u8 mul_k, u8 div_m, u8 exp_div_p) |
| 78 | { |
| 79 | u32 reg32; |
| 80 | |
| 81 | reg32 = read32(&ccm->pll5_cfg); |
| 82 | reg32 &= ~(PLL5_FACTOR_M_MASK | PLL5_FACTOR_N_MASK | |
| 83 | PLL5_FACTOR_K_MASK | PLL5_DIV_EXP_P_MASK); |
| 84 | /* The M1 factor is not documented in the datasheet, and the reference |
| 85 | * raminit code does not use it. Whether this is a fractional divisor, |
| 86 | * or an additional divisor is unknown, so don't use it for now */ |
| 87 | reg32 &= ~PLL5_FACTOR_M1_MASK; |
| 88 | reg32 |= (PLL5_FACTOR_M(div_m) | PLL5_FACTOR_N(mul_n) | |
| 89 | PLL5_FACTOR_K(mul_k) | PLL5_DIV_EXP_P(exp_div_p)); |
| 90 | reg32 |= PLL5_PLL_ENABLE; |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 91 | write32(&ccm->pll5_cfg, reg32); |
Alexandru Gagniuc | cce6c1c | 2013-12-31 13:20:27 -0500 | [diff] [blame] | 92 | } |
| 93 | |
| 94 | /** |
| 95 | * \brief Enable the clock output to DRAM chips |
| 96 | * |
| 97 | * This enables the DRAM clock to be sent to DRAM chips. This should normally be |
| 98 | * done after PLL5 is configured and locked. Note that the clock may be gated, |
| 99 | * and also needs to be ungated in order to reach the DDR chips. |
| 100 | * Also see @ref clock_ungate_dram_clk_output |
| 101 | */ |
| 102 | void a1x_pll5_enable_dram_clock_output(void) |
| 103 | { |
| 104 | setbits_le32(&ccm->pll5_cfg, PLL5_DDR_CLK_OUT_EN); |
| 105 | } |
| 106 | |
| 107 | /** |
| 108 | * \brief Ungate the clock to DRAM chips |
| 109 | * |
| 110 | * Although the DRAM clock output may be enabled, it is by default gated. It |
| 111 | * needs to be ungated before reaching DRAM. |
| 112 | */ |
| 113 | void a1x_ungate_dram_clock_output(void) |
| 114 | { |
| 115 | setbits_le32(&ccm->dram_clk_cfg, DRAM_CTRL_DCLK_OUT); |
| 116 | } |
| 117 | |
| 118 | /** |
| 119 | * \brief Gate the clock to DRAM chips |
| 120 | * |
| 121 | * Disable the clock to DRAM without altering PLL configuration, by closing the |
| 122 | * DRAM clock gate. |
| 123 | */ |
| 124 | void a1x_gate_dram_clock_output(void) |
| 125 | { |
| 126 | clrbits_le32(&ccm->dram_clk_cfg, DRAM_CTRL_DCLK_OUT); |
| 127 | } |
Alexandru Gagniuc | 601b5b5 | 2013-12-28 19:29:36 -0500 | [diff] [blame] | 128 | |
| 129 | /* |
| 130 | * Linker doesn't garbage collect and the function below adds about half |
| 131 | * kilobyte to the bootblock, and log2_ceil is not available in the bootblock. |
| 132 | */ |
Patrick Georgi | 56b8309 | 2015-04-02 19:44:19 +0200 | [diff] [blame] | 133 | #ifndef __BOOTBLOCK__ |
Alexandru Gagniuc | 601b5b5 | 2013-12-28 19:29:36 -0500 | [diff] [blame] | 134 | |
| 135 | #define PLL1_CFG(N, K, M, P_EXP) \ |
| 136 | ((1 << 31 | 0 << 30 | 8 << 26 | 0 << 25 | 16 << 20 | 2 << 13) | \ |
| 137 | (P_EXP) << 16 | (N) << 8 | \ |
| 138 | (K - 1) << 4 | 0 << 3 | 0 << 2 | (M -1) << 0) |
| 139 | |
| 140 | static const struct { |
| 141 | u32 pll1_cfg; |
| 142 | u16 freq_mhz; |
| 143 | } pll1_table[] = { |
| 144 | /* PLL1 output = (24MHz * N * K) / (M * P) */ |
| 145 | { PLL1_CFG(16, 1, 1, 0), 384 }, |
| 146 | { PLL1_CFG(16, 2, 1, 0), 768 }, |
| 147 | { PLL1_CFG(20, 2, 1, 0), 960 }, |
| 148 | { PLL1_CFG(21, 2, 1, 0), 1008 }, |
| 149 | { PLL1_CFG(22, 2, 1, 0), 1056 }, |
| 150 | { PLL1_CFG(23, 2, 1, 0), 1104 }, |
| 151 | { PLL1_CFG(24, 2, 1, 0), 1152 }, |
| 152 | { PLL1_CFG(25, 2, 1, 0), 1200 }, |
| 153 | { PLL1_CFG(26, 2, 1, 0), 1248 }, |
| 154 | { PLL1_CFG(27, 2, 1, 0), 1296 }, |
| 155 | { PLL1_CFG(28, 2, 1, 0), 1344 }, |
| 156 | { PLL1_CFG(29, 2, 1, 0), 1392 }, |
| 157 | { PLL1_CFG(30, 2, 1, 0), 1440 }, |
| 158 | { PLL1_CFG(31, 2, 1, 0), 1488 }, |
| 159 | { PLL1_CFG(20, 4, 1, 0), 1944 }, |
| 160 | }; |
| 161 | |
Alexandru Gagniuc | 601b5b5 | 2013-12-28 19:29:36 -0500 | [diff] [blame] | 162 | static void cpu_clk_src_switch(u32 clksel_bits) |
| 163 | { |
| 164 | u32 reg32; |
| 165 | |
| 166 | reg32 = read32(&ccm->cpu_ahb_apb0_cfg); |
| 167 | reg32 &= ~CPU_CLK_SRC_MASK; |
| 168 | reg32 |= clksel_bits & CPU_CLK_SRC_MASK; |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 169 | write32(&ccm->cpu_ahb_apb0_cfg, reg32); |
Alexandru Gagniuc | 601b5b5 | 2013-12-28 19:29:36 -0500 | [diff] [blame] | 170 | } |
| 171 | |
| 172 | static void change_sys_divisors(u8 axi, u8 ahb_exp, u8 apb0_exp) |
| 173 | { |
| 174 | u32 reg32; |
| 175 | |
| 176 | reg32 = read32(&ccm->cpu_ahb_apb0_cfg); |
| 177 | /* Not a typo: We want to keep only the CLK_SRC bits */ |
| 178 | reg32 &= CPU_CLK_SRC_MASK; |
| 179 | reg32 |= ((axi - 1) << 0) & AXI_DIV_MASK; |
| 180 | reg32 |= (ahb_exp << 4) & AHB_DIV_MASK; |
| 181 | reg32 |= (apb0_exp << 8) & APB0_DIV_MASK; |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 182 | write32(&ccm->cpu_ahb_apb0_cfg, reg32); |
Alexandru Gagniuc | 601b5b5 | 2013-12-28 19:29:36 -0500 | [diff] [blame] | 183 | } |
| 184 | |
| 185 | static void spin_delay(u32 loops) |
| 186 | { |
| 187 | volatile u32 x = loops; |
| 188 | while (x--); |
| 189 | } |
| 190 | |
| 191 | /** |
| 192 | * \brief Configure the CPU clock and PLL1 |
| 193 | * |
| 194 | * To run at full speed, the CPU uses PLL1 as the clock source. AXI, AHB, and |
| 195 | * APB0 are derived from the CPU clock, and need to be kept within certain |
| 196 | * limits. This function configures PLL1 as close as possible to the desired |
| 197 | * frequency, based on a set of known working configurations for PLL1. It then |
| 198 | * calculates and applies the appropriate divisors for the AXI/AHB/APB0 clocks, |
| 199 | * before finally switching the CPU to run from the new clock. |
| 200 | * No further configuration of the CPU clock or divisors is needed. after |
| 201 | * calling this function. |
| 202 | * |
| 203 | * @param[in] cpu_clk_mhz Desired CPU clock, in MHz |
| 204 | */ |
| 205 | void a1x_set_cpu_clock(u16 cpu_clk_mhz) |
| 206 | { |
| 207 | int i = 0; |
| 208 | u8 axi, ahb, ahb_exp, apb0, apb0_exp; |
| 209 | u32 actual_mhz; |
| 210 | |
| 211 | /* |
| 212 | * Rated clock for PLL1 is 2000 MHz, but there is no combination of |
| 213 | * parameters that yields that exact frequency. 1944 MHz is the highest. |
| 214 | */ |
| 215 | if (cpu_clk_mhz > 1944) { |
| 216 | printk(BIOS_CRIT, "BUG! maximum PLL1 clock is 1944 MHz," |
| 217 | "but asked to clock CPU at %d MHz\n", |
| 218 | cpu_clk_mhz); |
| 219 | cpu_clk_mhz = 1944; |
| 220 | } |
| 221 | /* Find target frequency */ |
| 222 | while (pll1_table[i].freq_mhz < cpu_clk_mhz) |
| 223 | i++; |
| 224 | |
| 225 | actual_mhz = pll1_table[i].freq_mhz; |
| 226 | |
| 227 | if (cpu_clk_mhz != actual_mhz) { |
| 228 | printk(BIOS_WARNING, "Parameters for %d MHz not available, " |
| 229 | "setting CPU clock at %d MHz\n", |
| 230 | cpu_clk_mhz, actual_mhz); |
| 231 | } |
| 232 | |
| 233 | /* |
| 234 | * Calculate system clock divisors: |
| 235 | * The minimum clock divisor for APB0 is 2, which guarantees that AHB0 |
| 236 | * will always be in spec, as long as AHB is in spec, although the max |
| 237 | * AHB0 clock we can get is 125 MHz |
| 238 | */ |
Edward O'Callaghan | 7116ac8 | 2014-07-08 01:53:24 +1000 | [diff] [blame] | 239 | axi = CEIL_DIV(actual_mhz, 450); /* Max 450 MHz */ |
| 240 | ahb = CEIL_DIV(actual_mhz/axi, 250); /* Max 250 MHz */ |
Alexandru Gagniuc | 601b5b5 | 2013-12-28 19:29:36 -0500 | [diff] [blame] | 241 | apb0 = 2; /* Max 150 MHz */ |
| 242 | |
| 243 | ahb_exp = log2_ceil(ahb); |
| 244 | ahb = 1 << ahb_exp; |
| 245 | apb0_exp = 1; |
| 246 | |
| 247 | printk(BIOS_INFO, "CPU: %d MHz, AXI %d Mhz, AHB: %d MHz APB0: %d MHz\n", |
| 248 | actual_mhz, |
| 249 | actual_mhz / axi, |
| 250 | actual_mhz / (axi * ahb), |
| 251 | actual_mhz / (axi * ahb * apb0)); |
| 252 | |
| 253 | /* Keep the CPU off PLL1 while we change PLL parameters */ |
| 254 | cpu_clk_src_switch(CPU_CLK_SRC_OSC24M); |
| 255 | /* |
| 256 | * We can't use udelay() here. udelay() relies on timer 0, but timers |
| 257 | * have the habit of not ticking when the CPU is clocked from the main |
| 258 | * oscillator. |
| 259 | */ |
| 260 | spin_delay(8); |
| 261 | |
| 262 | change_sys_divisors(axi, ahb_exp, apb0_exp); |
| 263 | |
| 264 | /* Configure PLL1 at the desired frequency */ |
Julius Werner | 2f37bd6 | 2015-02-19 14:51:15 -0800 | [diff] [blame] | 265 | write32(&ccm->pll1_cfg, pll1_table[i].pll1_cfg); |
Alexandru Gagniuc | 601b5b5 | 2013-12-28 19:29:36 -0500 | [diff] [blame] | 266 | spin_delay(8); |
| 267 | |
| 268 | cpu_clk_src_switch(CPU_CLK_SRC_PLL1); |
| 269 | /* Here, we're running from PLL, so timers will tick */ |
| 270 | udelay(1); |
| 271 | } |
| 272 | |
Patrick Georgi | 56b8309 | 2015-04-02 19:44:19 +0200 | [diff] [blame] | 273 | #endif /* __BOOTBLOCK__ */ |