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