1/** 2 * \file 3 * \brief Platform code for the Cortex-A9 processors on TI OMAP44xx SoCs. 4 */ 5 6/* 7 * Copyright (c) 2009-2016 ETH Zurich. 8 * All rights reserved. 9 * 10 * This file is distributed under the terms in the attached LICENSE file. 11 * If you do not find this file, copies can be found by writing to: 12 * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. 13 */ 14 15#include <kernel.h> 16 17#include <cache.h> 18#include <platform.h> 19#include <serial.h> 20#include <assert.h> 21#include <errors/errno.h> 22#include <maps/a9mpcore_map.h> 23#include <maps/omap44xx_map.h> 24#include <omap_uart.h> 25#include <cp15.h> 26#include <a9_scu.h> 27#include <a9_gt.h> 28#include <gic.h> 29#include <init.h> 30#include <global.h> 31#include <paging_kernel_arch.h> 32#include <dev/omap/omap44xx_id_dev.h> 33#include <dev/omap/omap44xx_emif_dev.h> 34#include <dev/omap/omap44xx_cortexa9_wugen_dev.h> 35#include <dev/omap/omap44xx_ckgen_cm1_dev.h> 36#include <dev/omap/omap44xx_ckgen_prm_dev.h> 37#include <dev/cortex_a9_pit_dev.h> 38 39#define MSG(format, ...) printk( LOG_NOTE, "OMAP44xx: "format, ## __VA_ARGS__ ) 40 41/***************************************************************************** 42 * 43 * Implementation of serial.h 44 * 45 *****************************************************************************/ 46 47errval_t serial_init(unsigned port, bool initialize_hw) 48{ 49 lvaddr_t base = paging_map_device(uart_base[port], uart_size[port]); 50 omap_uart_init(port, base, initialize_hw); 51 return SYS_ERR_OK; 52}; 53 54/* 55 * Print system identification. MMU is NOT yet enabled. 56 * Use Mackerel to print the identification from the system 57 * configuration block. Documentation in the OMAP4460 TRM p. 18.6.2 58 */ 59void platform_print_id(void) 60{ 61 char buf[64]; 62 omap44xx_id_t id; 63 omap44xx_id_initialize(&id, 64 (mackerel_addr_t) OMAP44XX_MAP_L4_CFG_SYSCTRL_GENERAL_CORE); 65 66 omap44xx_id_codevals_prtval(buf, 63, omap44xx_id_code_rawrd(&id)); 67 MSG("This is a %s\n", buf); 68 69 omap44xx_id_stp_prtval(buf, 63, omap44xx_id_prod1_st_rdf(&id)); 70 MSG("Speed grade: %s\n", buf); 71 72 size_t sz = platform_get_ram_size(); 73 MSG("We have %uMb of DRAM\n", sz / 1024 / 1024); 74} 75 76void platform_get_info(struct platform_info *pi) 77{ 78 pi->arch = PI_ARCH_ARMV7A; 79 pi->platform = PI_PLATFORM_OMAP44XX; 80 armv7_get_info(&pi->arch_info.armv7); 81} 82 83/** 84 * Read the details of a memory bank (0 or 1) 85 */ 86static size_t bank_size(int bank, lpaddr_t base) 87{ 88 int rowbits; 89 int colbits; 90 int rowsize; 91 omap44xx_emif_t emif; 92 omap44xx_emif_initialize(&emif, (mackerel_addr_t)base); 93 94 assert( bank == 1 || bank == 2 ); 95 96 if (!omap44xx_emif_status_phy_dll_ready_rdf(&emif)) { 97 MSG(" EMIF%d doesn't seem to have any DDRAM attached.\n", bank); 98 return 0; 99 } 100 101 rowbits = omap44xx_emif_sdram_config_rowsize_rdf(&emif) + 9; 102 colbits = omap44xx_emif_sdram_config_pagesize_rdf(&emif) + 9; 103 rowsize = omap44xx_emif_sdram_config2_rdbsize_rdf(&emif) + 5; 104 105 MSG(" EMIF%d ready, %d-bit rows, %d-bit cols, %d-byte row buffer\n", 106 bank, rowbits, colbits, 1<<rowsize); 107 108 return (1 << (rowbits + colbits + rowsize)); 109} 110 111/** 112 * Calculate the size of available RAM by reading each bank 113 */ 114size_t platform_get_ram_size(void) 115{ 116 assert(!paging_mmu_enabled()); 117 return bank_size(1, OMAP44XX_MAP_EMIF1) + bank_size(2, OMAP44XX_MAP_EMIF2); 118} 119 120uint32_t tsc_hz = 0; 121uint32_t sys_clk; 122 123static lvaddr_t ckgen_cm1_base= 0; 124static omap44xx_ckgen_cm1_t ckgen_cm1; 125static lvaddr_t ckgen_prm_base= 0; 126static omap44xx_ckgen_prm_t ckgen_prm; 127 128#define IN_MHZ(f) ((f) / 1000 / 1000) 129#define KHZ_DIGIT(f) (((f) / 1000 / 100) % 10) 130 131void 132a9_probe_tsc(void) { 133 /* Read the base clock frequency, SYS_CLK. */ 134 ckgen_prm_base= 135 paging_map_device(OMAP44XX_MAP_L4_CKGEN_PRM, 136 OMAP44XX_MAP_L4_CKGEN_PRM_SIZE); 137 omap44xx_ckgen_prm_initialize(&ckgen_prm, 138 (mackerel_addr_t)ckgen_prm_base); 139 140 switch(omap44xx_ckgen_prm_cm_sys_clksel_sys_clksel_rdf(&ckgen_prm)) { 141 case omap44xx_ckgen_prm_SYS_CLKSEL_0: 142 panic("sys_clksel_status is uninitialised.\n"); 143 144 case omap44xx_ckgen_prm_SYS_CLKSEL_1: 145 sys_clk= 12000 * 1000; /* 12MHz */ 146 break; 147 case omap44xx_ckgen_prm_SYS_CLKSEL_3: 148 sys_clk= 16800 * 1000; /* 16.8MHz */ 149 break; 150 case omap44xx_ckgen_prm_SYS_CLKSEL_4: 151 sys_clk= 19200 * 1000; /* 19.2MHz */ 152 break; 153 case omap44xx_ckgen_prm_SYS_CLKSEL_5: 154 sys_clk= 26000 * 1000; /* 26MHz */ 155 break; 156 case omap44xx_ckgen_prm_SYS_CLKSEL_7: 157 sys_clk= 38400 * 1000; /* 38.4MHz */ 158 break; 159 160 default: 161 panic("sys_clksel_status is invalid.\n"); 162 } 163 164 MSG("SYS_CLK is %"PRIu32".%01"PRIu32"MHz\n", 165 IN_MHZ(sys_clk), KHZ_DIGIT(sys_clk)); 166 167 /* This is the main clock generator. */ 168 ckgen_cm1_base= 169 paging_map_device(OMAP44XX_MAP_L4_CKGEN_CM1, 170 OMAP44XX_MAP_L4_CKGEN_CM1_SIZE); 171 omap44xx_ckgen_cm1_initialize(&ckgen_cm1, 172 (mackerel_addr_t)ckgen_cm1_base); 173 174 if(omap44xx_ckgen_cm1_cm_clkmode_dpll_mpu_dpll_en_rdf(&ckgen_cm1) 175 != omap44xx_ckgen_cm1_DPLL_EN_LM) { 176 panic("MPU DPLL seems to be disabled.\n"); 177 } 178 179 uint32_t mult= /* This is M */ 180 omap44xx_ckgen_cm1_cm_clksel_dpll_mpu_dpll_mult_rdf(&ckgen_cm1); 181 uint32_t divisor = /* This is N+1 */ 182 omap44xx_ckgen_cm1_cm_clksel_dpll_mpu_dpll_div_rdf(&ckgen_cm1) + 1; 183 184 MSG("M = %"PRIx32", N = %"PRIx32"\n", mult, divisor - 1); 185 186 uint64_t f_dpll= ((uint64_t)sys_clk * 2 * mult) / divisor; 187 188 MSG("f_dpll = %"PRIu64"\n", f_dpll); 189 190 /* See TI SWPU235AB Figures 3-40 and 3-50. */ 191 bool dcc_en= 192 omap44xx_ckgen_cm1_cm_clksel_dpll_mpu_dcc_en_rdf(&ckgen_cm1); 193 uint32_t f_cpu; 194 if(dcc_en) { 195 MSG("MPU DPLL is in >=1GHz mode.\n"); 196 197 /* In high-speed mode, the divisor is hardcoded to 1, and an 198 * additional divide-by-two stage is bypassed. */ 199 assert(f_dpll <= (uint64_t)UINT32_MAX); 200 f_cpu= (uint32_t)f_dpll; 201 } 202 else { 203 MSG("MPU DPLL is in <1GHz mode.\n"); 204 205 /* Otherwise, f_dpll is divided by two, and then by M2, the 206 * post-oscillator divider. */ 207 int m2= 208 omap44xx_ckgen_cm1_cm_div_m2_dpll_mpu_dpll_clkout_div_rdf(&ckgen_cm1); 209 MSG("m2 = %d\n", m2); 210 assert(m2 != 0); 211 f_cpu= (f_dpll / 2) / m2; 212 } 213 214 /* The CPU clock is divided once more to generate the peripheral clock. */ 215 uint32_t f_periph= f_cpu / 2; 216 217 MSG("CPU frequency %"PRIu32".%01"PRIu32"MHz, " 218 "PERIPHCLK %"PRIu32".%01"PRIu32"MHz\n", 219 IN_MHZ(f_cpu), KHZ_DIGIT(f_cpu), 220 IN_MHZ(f_periph), KHZ_DIGIT(f_periph)); 221 222 tsc_hz= f_periph; 223} 224