Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 1 | /* |
| 2 | * (C) Copyright 2010-2011 |
| 3 | * NVIDIA Corporation <www.nvidia.com> |
| 4 | * |
| 5 | * See file CREDITS for list of people who contributed to this |
| 6 | * project. |
| 7 | * |
| 8 | * This program is free software; you can redistribute it and/or |
| 9 | * modify it under the terms of the GNU General Public License as |
| 10 | * published by the Free Software Foundation; either version 2 of |
| 11 | * the License, or (at your option) any later version. |
| 12 | * |
| 13 | * This program is distributed in the hope that it will be useful, |
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 16 | * GNU General Public License for more details. |
| 17 | * |
| 18 | * You should have received a copy of the GNU General Public License |
| 19 | * along with this program; if not, write to the Free Software |
| 20 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, |
| 21 | * MA 02111-1307 USA |
| 22 | */ |
| 23 | |
| 24 | #include "ap20.h" |
| 25 | #include <asm/io.h> |
| 26 | #include <asm/arch/tegra2.h> |
| 27 | #include <asm/arch/clk_rst.h> |
Simon Glass | b4ba2be | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 28 | #include <asm/arch/clock.h> |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 29 | #include <asm/arch/pmc.h> |
| 30 | #include <asm/arch/pinmux.h> |
| 31 | #include <asm/arch/scu.h> |
| 32 | #include <common.h> |
| 33 | |
Simon Glass | 210576f | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 34 | /* Returns 1 if the current CPU executing is a Cortex-A9, else 0 */ |
| 35 | static int ap20_cpu_is_cortexa9(void) |
| 36 | { |
| 37 | u32 id = readb(NV_PA_PG_UP_BASE + PG_UP_TAG_0); |
| 38 | return id == (PG_UP_TAG_0_PID_CPU & 0xff); |
| 39 | } |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 40 | |
Tom Warren | 1436d51 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 41 | void init_pllx(void) |
| 42 | { |
| 43 | struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; |
Simon Glass | 03c609f | 2011-09-21 12:40:02 +0000 | [diff] [blame] | 44 | struct clk_pll *pll = &clkrst->crc_pll[CLOCK_ID_XCPU]; |
Tom Warren | 1436d51 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 45 | u32 reg; |
| 46 | |
| 47 | /* If PLLX is already enabled, just return */ |
Simon Glass | d07dc49 | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 48 | if (readl(&pll->pll_base) & PLL_ENABLE_MASK) |
Tom Warren | 1436d51 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 49 | return; |
| 50 | |
| 51 | /* Set PLLX_MISC */ |
Simon Glass | d07dc49 | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 52 | writel(1 << PLL_CPCON_SHIFT, &pll->pll_misc); |
Tom Warren | 1436d51 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 53 | |
| 54 | /* Use 12MHz clock here */ |
Simon Glass | d07dc49 | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 55 | reg = PLL_BYPASS_MASK | (12 << PLL_DIVM_SHIFT); |
| 56 | reg |= 1000 << PLL_DIVN_SHIFT; |
Simon Glass | b4ba2be | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 57 | writel(reg, &pll->pll_base); |
Tom Warren | 1436d51 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 58 | |
Simon Glass | d07dc49 | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 59 | reg |= PLL_ENABLE_MASK; |
Simon Glass | b4ba2be | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 60 | writel(reg, &pll->pll_base); |
Tom Warren | 1436d51 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 61 | |
Simon Glass | d07dc49 | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 62 | reg &= ~PLL_BYPASS_MASK; |
Simon Glass | b4ba2be | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 63 | writel(reg, &pll->pll_base); |
Tom Warren | 1436d51 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 64 | } |
| 65 | |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 66 | static void enable_cpu_clock(int enable) |
| 67 | { |
| 68 | struct clk_rst_ctlr *clkrst = (struct clk_rst_ctlr *)NV_PA_CLK_RST_BASE; |
Simon Glass | b4ba2be | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 69 | u32 clk; |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 70 | |
| 71 | /* |
| 72 | * NOTE: |
| 73 | * Regardless of whether the request is to enable or disable the CPU |
| 74 | * clock, every processor in the CPU complex except the master (CPU 0) |
| 75 | * will have it's clock stopped because the AVP only talks to the |
| 76 | * master. The AVP does not know (nor does it need to know) that there |
| 77 | * are multiple processors in the CPU complex. |
| 78 | */ |
| 79 | |
| 80 | if (enable) { |
Tom Warren | 1436d51 | 2011-04-14 12:09:39 +0000 | [diff] [blame] | 81 | /* Initialize PLLX */ |
| 82 | init_pllx(); |
| 83 | |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 84 | /* Wait until all clocks are stable */ |
| 85 | udelay(PLL_STABILIZATION_DELAY); |
| 86 | |
| 87 | writel(CCLK_BURST_POLICY, &clkrst->crc_cclk_brst_pol); |
| 88 | writel(SUPER_CCLK_DIVIDER, &clkrst->crc_super_cclk_div); |
| 89 | } |
| 90 | |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 91 | /* |
| 92 | * Read the register containing the individual CPU clock enables and |
| 93 | * always stop the clock to CPU 1. |
| 94 | */ |
| 95 | clk = readl(&clkrst->crc_clk_cpu_cmplx); |
Simon Glass | d07dc49 | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 96 | clk |= 1 << CPU1_CLK_STP_SHIFT; |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 97 | |
Simon Glass | d07dc49 | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 98 | /* Stop/Unstop the CPU clock */ |
| 99 | clk &= ~CPU0_CLK_STP_MASK; |
| 100 | clk |= !enable << CPU0_CLK_STP_SHIFT; |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 101 | writel(clk, &clkrst->crc_clk_cpu_cmplx); |
Simon Glass | b4ba2be | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 102 | |
| 103 | clock_enable(PERIPH_ID_CPU); |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 104 | } |
| 105 | |
| 106 | static int is_cpu_powered(void) |
| 107 | { |
| 108 | struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; |
| 109 | |
| 110 | return (readl(&pmc->pmc_pwrgate_status) & CPU_PWRED) ? 1 : 0; |
| 111 | } |
| 112 | |
| 113 | static void remove_cpu_io_clamps(void) |
| 114 | { |
| 115 | struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; |
| 116 | u32 reg; |
| 117 | |
| 118 | /* Remove the clamps on the CPU I/O signals */ |
| 119 | reg = readl(&pmc->pmc_remove_clamping); |
| 120 | reg |= CPU_CLMP; |
| 121 | writel(reg, &pmc->pmc_remove_clamping); |
| 122 | |
| 123 | /* Give I/O signals time to stabilize */ |
| 124 | udelay(IO_STABILIZATION_DELAY); |
| 125 | } |
| 126 | |
| 127 | static void powerup_cpu(void) |
| 128 | { |
| 129 | struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; |
| 130 | u32 reg; |
| 131 | int timeout = IO_STABILIZATION_DELAY; |
| 132 | |
| 133 | if (!is_cpu_powered()) { |
| 134 | /* Toggle the CPU power state (OFF -> ON) */ |
| 135 | reg = readl(&pmc->pmc_pwrgate_toggle); |
| 136 | reg &= PARTID_CP; |
| 137 | reg |= START_CP; |
| 138 | writel(reg, &pmc->pmc_pwrgate_toggle); |
| 139 | |
| 140 | /* Wait for the power to come up */ |
| 141 | while (!is_cpu_powered()) { |
| 142 | if (timeout-- == 0) |
| 143 | printf("CPU failed to power up!\n"); |
| 144 | else |
| 145 | udelay(10); |
| 146 | } |
| 147 | |
| 148 | /* |
| 149 | * Remove the I/O clamps from CPU power partition. |
| 150 | * Recommended only on a Warm boot, if the CPU partition gets |
| 151 | * power gated. Shouldn't cause any harm when called after a |
| 152 | * cold boot according to HW, probably just redundant. |
| 153 | */ |
| 154 | remove_cpu_io_clamps(); |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | static void enable_cpu_power_rail(void) |
| 159 | { |
| 160 | struct pmc_ctlr *pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; |
| 161 | u32 reg; |
| 162 | |
| 163 | reg = readl(&pmc->pmc_cntrl); |
| 164 | reg |= CPUPWRREQ_OE; |
| 165 | writel(reg, &pmc->pmc_cntrl); |
| 166 | |
| 167 | /* |
| 168 | * The TI PMU65861C needs a 3.75ms delay between enabling |
| 169 | * the power rail and enabling the CPU clock. This delay |
| 170 | * between SM1EN and SM1 is for switching time + the ramp |
| 171 | * up of the voltage to the CPU (VDD_CPU from PMU). |
| 172 | */ |
| 173 | udelay(3750); |
| 174 | } |
| 175 | |
| 176 | static void reset_A9_cpu(int reset) |
| 177 | { |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 178 | /* |
| 179 | * NOTE: Regardless of whether the request is to hold the CPU in reset |
| 180 | * or take it out of reset, every processor in the CPU complex |
| 181 | * except the master (CPU 0) will be held in reset because the |
| 182 | * AVP only talks to the master. The AVP does not know that there |
| 183 | * are multiple processors in the CPU complex. |
| 184 | */ |
| 185 | |
Simon Glass | d07dc49 | 2011-08-30 06:23:15 +0000 | [diff] [blame] | 186 | /* Hold CPU 1 in reset, and CPU 0 if asked */ |
| 187 | reset_cmplx_set_enable(1, crc_rst_cpu | crc_rst_de | crc_rst_debug, 1); |
| 188 | reset_cmplx_set_enable(0, crc_rst_cpu | crc_rst_de | crc_rst_debug, |
| 189 | reset); |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 190 | |
Simon Glass | b4ba2be | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 191 | /* Enable/Disable master CPU reset */ |
| 192 | reset_set_enable(PERIPH_ID_CPU, reset); |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 193 | } |
| 194 | |
| 195 | static void clock_enable_coresight(int enable) |
| 196 | { |
Simon Glass | b4ba2be | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 197 | u32 rst, src; |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 198 | |
Simon Glass | b4ba2be | 2011-08-30 06:23:13 +0000 | [diff] [blame] | 199 | clock_set_enable(PERIPH_ID_CORESIGHT, enable); |
| 200 | reset_set_enable(PERIPH_ID_CORESIGHT, !enable); |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 201 | |
| 202 | if (enable) { |
| 203 | /* |
| 204 | * Put CoreSight on PLLP_OUT0 (216 MHz) and divide it down by |
| 205 | * 1.5, giving an effective frequency of 144MHz. |
| 206 | * Set PLLP_OUT0 [bits31:30 = 00], and use a 7.1 divisor |
| 207 | * (bits 7:0), so 00000001b == 1.5 (n+1 + .5) |
| 208 | */ |
| 209 | src = CLK_DIVIDER(NVBL_PLLP_KHZ, 144000); |
Simon Glass | 4ed59e7 | 2011-09-21 12:40:04 +0000 | [diff] [blame] | 210 | clock_ll_set_source_divisor(PERIPH_ID_CSI, 0, src); |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 211 | |
| 212 | /* Unlock the CPU CoreSight interfaces */ |
| 213 | rst = 0xC5ACCE55; |
| 214 | writel(rst, CSITE_CPU_DBG0_LAR); |
| 215 | writel(rst, CSITE_CPU_DBG1_LAR); |
| 216 | } |
| 217 | } |
| 218 | |
| 219 | void start_cpu(u32 reset_vector) |
| 220 | { |
| 221 | /* Enable VDD_CPU */ |
| 222 | enable_cpu_power_rail(); |
| 223 | |
| 224 | /* Hold the CPUs in reset */ |
| 225 | reset_A9_cpu(1); |
| 226 | |
| 227 | /* Disable the CPU clock */ |
| 228 | enable_cpu_clock(0); |
| 229 | |
| 230 | /* Enable CoreSight */ |
| 231 | clock_enable_coresight(1); |
| 232 | |
| 233 | /* |
| 234 | * Set the entry point for CPU execution from reset, |
| 235 | * if it's a non-zero value. |
| 236 | */ |
| 237 | if (reset_vector) |
| 238 | writel(reset_vector, EXCEP_VECTOR_CPU_RESET_VECTOR); |
| 239 | |
| 240 | /* Enable the CPU clock */ |
| 241 | enable_cpu_clock(1); |
| 242 | |
| 243 | /* If the CPU doesn't already have power, power it up */ |
| 244 | powerup_cpu(); |
| 245 | |
| 246 | /* Take the CPU out of reset */ |
| 247 | reset_A9_cpu(0); |
| 248 | } |
| 249 | |
| 250 | |
| 251 | void halt_avp(void) |
| 252 | { |
| 253 | for (;;) { |
| 254 | writel((HALT_COP_EVENT_JTAG | HALT_COP_EVENT_IRQ_1 \ |
| 255 | | HALT_COP_EVENT_FIQ_1 | (FLOW_MODE_STOP<<29)), |
| 256 | FLOW_CTLR_HALT_COP_EVENTS); |
| 257 | } |
| 258 | } |
| 259 | |
| 260 | void enable_scu(void) |
| 261 | { |
| 262 | struct scu_ctlr *scu = (struct scu_ctlr *)NV_PA_ARM_PERIPHBASE; |
| 263 | u32 reg; |
| 264 | |
| 265 | /* If SCU already setup/enabled, return */ |
| 266 | if (readl(&scu->scu_ctrl) & SCU_CTRL_ENABLE) |
| 267 | return; |
| 268 | |
| 269 | /* Invalidate all ways for all processors */ |
| 270 | writel(0xFFFF, &scu->scu_inv_all); |
| 271 | |
| 272 | /* Enable SCU - bit 0 */ |
| 273 | reg = readl(&scu->scu_ctrl); |
| 274 | reg |= SCU_CTRL_ENABLE; |
| 275 | writel(reg, &scu->scu_ctrl); |
| 276 | } |
| 277 | |
| 278 | void init_pmc_scratch(void) |
| 279 | { |
| 280 | struct pmc_ctlr *const pmc = (struct pmc_ctlr *)NV_PA_PMC_BASE; |
| 281 | int i; |
| 282 | |
| 283 | /* SCRATCH0 is initialized by the boot ROM and shouldn't be cleared */ |
| 284 | for (i = 0; i < 23; i++) |
| 285 | writel(0, &pmc->pmc_scratch1+i); |
| 286 | |
| 287 | /* ODMDATA is for kernel use to determine RAM size, LP config, etc. */ |
| 288 | writel(CONFIG_SYS_BOARD_ODMDATA, &pmc->pmc_scratch20); |
| 289 | } |
| 290 | |
Simon Glass | 210576f | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 291 | void tegra2_start(void) |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 292 | { |
| 293 | struct pmux_tri_ctlr *pmt = (struct pmux_tri_ctlr *)NV_PA_APB_MISC_BASE; |
| 294 | |
Simon Glass | 210576f | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 295 | /* If we are the AVP, start up the first Cortex-A9 */ |
| 296 | if (!ap20_cpu_is_cortexa9()) { |
| 297 | /* enable JTAG */ |
| 298 | writel(0xC0, &pmt->pmt_cfg_ctl); |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 299 | |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 300 | /* |
Simon Glass | 210576f | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 301 | * If we are ARM7 - give it a different stack. We are about to |
| 302 | * start up the A9 which will want to use this one. |
| 303 | */ |
| 304 | asm volatile("ldr sp, =%c0\n" |
| 305 | : : "i"(AVP_EARLY_BOOT_STACK_LIMIT)); |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 306 | |
Simon Glass | 210576f | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 307 | start_cpu((u32)_start); |
| 308 | halt_avp(); |
| 309 | /* not reached */ |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 310 | } |
| 311 | |
Simon Glass | 210576f | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 312 | /* Init PMC scratch memory */ |
| 313 | init_pmc_scratch(); |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 314 | |
Simon Glass | 210576f | 2011-11-05 03:56:50 +0000 | [diff] [blame] | 315 | enable_scu(); |
| 316 | |
| 317 | /* enable SMP mode and FW for CPU0, by writing to Auxiliary Ctl reg */ |
| 318 | asm volatile( |
| 319 | "mrc p15, 0, r0, c1, c0, 1\n" |
| 320 | "orr r0, r0, #0x41\n" |
| 321 | "mcr p15, 0, r0, c1, c0, 1\n"); |
| 322 | |
| 323 | /* FIXME: should have ap20's L2 disabled too? */ |
Tom Warren | 74652cf | 2011-04-14 12:18:06 +0000 | [diff] [blame] | 324 | } |