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