1/**
2 * \file
3 * \brief Platform code for the Xilinx Zynq7000-series SoCs
4 */
5
6/*
7 * Copyright (c) 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, Universitaetstr 6, CH-8092 Zurich. Attn: Systems Group.
13 */
14
15#include <kernel.h>
16
17#include <a9_gt.h>
18#include <a9_scu.h>
19#include <maps/a9mpcore_map.h>
20#include <assert.h>
21#include <cp15.h>
22#include <dev/cortex_a9_pit_dev.h>
23#include <dev/zynq7/zynq_slcr_dev.h>
24#include <errors/errno.h>
25#include <gic.h>
26#include <global.h>
27#include <init.h>
28#include <paging_kernel_arch.h>
29#include <systime.h>
30#include <arch/arm/platform.h>
31#include <serial.h>
32#include <maps/zynq7_map.h>
33#include <zynq_uart.h>
34
35#define MSG(format, ...) printk( LOG_NOTE, "ZYNQ7: "format, ## __VA_ARGS__ )
36
37/*****************************************************************************
38 *
39 * Implementation of serial.h
40 *
41 *****************************************************************************/
42
43/*
44 * Initialize the serial ports
45 */
46errval_t
47serial_init(unsigned port, bool initialize_hw) {
48    assert(paging_mmu_enabled());
49    assert(port < serial_num_physical_ports);
50    lvaddr_t base = paging_map_device(platform_uart_base[port], platform_uart_size[port]);
51    zynq_uart_init(port, base, initialize_hw);
52    return SYS_ERR_OK;
53};
54
55/* System control registers, after MMU initialisation. */
56static lvaddr_t slcr_base= 0;
57static zynq_slcr_t slcr_dev;
58
59/* XXX - centralise platform device management on ARMv7. */
60static zynq_slcr_t *
61get_slcr(void) {
62    /* If it's not yet mapped, do so. */
63    if(slcr_base == 0) {
64        slcr_base= paging_map_device(ZINQ7_SYS_CTRL_BASEADDR, BASE_PAGE_SIZE);
65        zynq_slcr_initialize(&slcr_dev, (mackerel_addr_t)slcr_base);
66    }
67
68    return &slcr_dev;
69}
70
71/* Print system identification. MMU is NOT yet enabled. */
72void
73platform_print_id(void) {
74    assert(!paging_mmu_enabled());
75
76    zynq_slcr_t slcr_early;
77    zynq_slcr_initialize(&slcr_early,
78                         (mackerel_addr_t)ZINQ7_SYS_CTRL_BASEADDR);
79
80#if 0
81    int family=       zynq_slcr_PSS_IDCODE_FAMILY_rdf(&slcr);
82    int subfamily=    zynq_slcr_PSS_IDCODE_SUBFAMILY_rdf(&slcr);
83    int manufacturer= zynq_slcr_PSS_IDCODE_MANUFACTURER_ID_rdf(&slcr);
84    zynq_slcr_devcode_t device= zynq_slcr_PSS_IDCODE_DEVICE_CODE_rdf(&slcr);
85
86    /* See Zynq 7000 TRM p1631. */
87    if(family != 0x1b || subfamily != 0x9 || manufacturer != 0x49) {
88        panic("This doesn't look like a Zynq\n");
89    }
90#endif
91
92    char buf[1024];
93    zynq_slcr_PSS_IDCODE_pr(buf, 1023, &slcr_early);
94
95    printf("This is a Zynq.\n");
96    printf("%s", buf);
97}
98
99void
100platform_get_info(struct platform_info *pi) {
101    pi->arch     = PI_ARCH_ARMV7A;
102    pi->platform = PI_PLATFORM_ZYNQ7;
103    armv7_get_info(&pi->arch_info.armv7);
104}
105
106/* The zc706 has 2GB of RAM beginning at address 0. */
107size_t
108platform_get_ram_size(void) {
109    return (ZYNQ7_DDR_MEM_HIGHADDR - ZYNQ7_DDR_MEM_BASEADDR) + 1;
110}
111
112/* Clocks on the Zynq processor subsystem are derived from PS_CLK, which is
113 * 33.33333MHz on the zc706.  This should be command-line configurable for
114 * other boards. */
115uint32_t ps_clk = 33333330;
116
117void
118a9_probe_tsc(void) {
119    zynq_slcr_t *slcr= get_slcr();
120
121    /* Figure out which PLL is driving the CPU. */
122    zynq_slcr_clksrc_t clksrc= zynq_slcr_ARM_CLK_CTRL_SRCSEL_rdf(slcr);
123    MSG("CPU clock is derived from %s\n", zynq_slcr_clksrc_describe(clksrc));
124
125    /* Read the appropriate control register. */
126    zynq_slcr_pll_ctrl_t pll_ctrl;
127    switch(clksrc) {
128        case zynq_slcr_iopll0:
129        case zynq_slcr_iopll1:
130            pll_ctrl= zynq_slcr_IO_PLL_CTRL_rd(slcr);
131            break;
132        case zynq_slcr_armpll:
133            pll_ctrl= zynq_slcr_ARM_PLL_CTRL_rd(slcr);
134            break;
135        case zynq_slcr_ddrpll:
136            pll_ctrl= zynq_slcr_DDR_PLL_CTRL_rd(slcr);
137            break;
138        default:
139            panic("Invalid PLL type.\n");
140    }
141
142    /* Pinstrap PLL bypass setting. */
143    bool bypass_pin=   zynq_slcr_BOOT_MODE_PLL_BYPASS_rdf(slcr);
144    /* Software bypass override (force). */
145    bool bypass_force= zynq_slcr_pll_ctrl_PLL_BYPASS_FORCE_extract(pll_ctrl);
146    /* Bypass control source. */
147    bool bypass_src=   zynq_slcr_pll_ctrl_PLL_BYPASS_QUAL_extract(pll_ctrl);
148
149    /* Find the PLL output frequency. */
150    uint32_t src_clk;
151    bool bypass= bypass_force || (bypass_src && bypass_pin);
152    if(bypass) {
153        MSG(" PLL is bypassed.\n");
154        src_clk= ps_clk;
155    }
156    else {
157        uint32_t M= zynq_slcr_pll_ctrl_PLL_FDIV_extract(pll_ctrl);
158        MSG(" PLL multiplier, M=%"PRIu32".\n", M);
159        src_clk= ps_clk * M;
160    }
161    MSG(" PLL frequency is %"PRIu32"kHz.\n", src_clk/1000);
162
163    /* The CPU clock is divided again. */
164    uint32_t divisor= zynq_slcr_ARM_CLK_CTRL_DIVISOR_rdf(slcr);
165    uint32_t cpu_clk= src_clk / divisor;
166    MSG(" CPU frequency is %"PRIu32"kHz.\n", cpu_clk/1000);
167
168    /* The timers run at half the core frequency. */
169    systime_frequency = cpu_clk / 2;
170    MSG(" Timer frequency is %"PRIu32"kHz.\n", systime_frequency / 1000);
171
172    /* The next step in the clock chain, for the fast IO peripherals, can be
173     * either a factor or 2, or of 3. */
174    uint32_t divisor2;
175    if(zynq_slcr_CLK_621_TRUE_CLK_621_TRUE_rdf(slcr)) divisor2= 3;
176    else                                              divisor2= 2;
177
178    MSG(" Central interconnect frequency is %"PRIu32"kHz.\n",
179        cpu_clk / divisor2 / 1000);
180    MSG(" AHB frequency is %"PRIu32"kHz.\n",
181        cpu_clk / divisor2 / 2 / 1000);
182}
183