[Feature] add GA346 baseline version
Change-Id: Ic62933698569507dcf98240cdf5d9931ae34348f
diff --git a/src/bsp/lk/platform/zynq/clocks.c b/src/bsp/lk/platform/zynq/clocks.c
new file mode 100644
index 0000000..660f941
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/clocks.c
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <reg.h>
+#include <bits.h>
+#include <stdio.h>
+#include <assert.h>
+#include <trace.h>
+#include <err.h>
+#include <kernel/thread.h>
+#include <platform/debug.h>
+#include <platform/zynq.h>
+#include <target/debugconfig.h>
+#include <reg.h>
+
+#define LOCAL_TRACE 0
+
+static uint32_t get_arm_pll_freq(void)
+{
+ LTRACEF("ARM_PLL_CTRL 0x%x\n", SLCR_REG(ARM_PLL_CTRL));
+
+ // XXX test that the pll is actually enabled
+
+ uint32_t fdiv = BITS_SHIFT(SLCR_REG(ARM_PLL_CTRL), 18, 12);
+
+ return EXTERNAL_CLOCK_FREQ * fdiv;
+}
+
+static uint32_t get_ddr_pll_freq(void)
+{
+ LTRACEF("DDR_PLL_CTRL 0x%x\n", SLCR_REG(DDR_PLL_CTRL));
+
+ // XXX test that the pll is actually enabled
+
+ uint32_t fdiv = BITS_SHIFT(SLCR_REG(DDR_PLL_CTRL), 18, 12);
+
+ return EXTERNAL_CLOCK_FREQ * fdiv;
+}
+
+static uint32_t get_io_pll_freq(void)
+{
+ LTRACEF("IO_PLL_CTRL 0x%x\n", SLCR_REG(IO_PLL_CTRL));
+
+ // XXX test that the pll is actually enabled
+
+ uint32_t fdiv = BITS_SHIFT(SLCR_REG(IO_PLL_CTRL), 18, 12);
+
+ return EXTERNAL_CLOCK_FREQ * fdiv;
+}
+
+static uint32_t get_cpu_input_freq(void)
+{
+ LTRACEF("ARM_CLK_CTRL 0x%x\n", SLCR_REG(ARM_CLK_CTRL));
+
+ uint32_t divisor = BITS_SHIFT(SLCR_REG(ARM_CLK_CTRL), 13, 8);
+ uint32_t srcsel = BITS_SHIFT(SLCR_REG(ARM_CLK_CTRL), 5, 4);
+
+ uint32_t srcclk;
+ switch (srcsel) {
+ default: case 0: case 1: // arm pll
+ srcclk = get_arm_pll_freq();
+ break;
+ case 2: // ddr pll
+ srcclk = get_ddr_pll_freq();
+ break;
+ case 3: // io pll
+ srcclk = get_io_pll_freq();
+ break;
+ }
+
+ // cpu 6x4x
+ return srcclk / divisor;
+}
+
+static uint32_t get_cpu_6x4x_freq(void)
+{
+ // cpu 6x4x is the post divided frequency in the cpu clock block
+ return get_cpu_input_freq();
+}
+
+static uint32_t get_cpu_3x2x_freq(void)
+{
+ // cpu 3x2x is always half the speed of 6x4x
+ return get_cpu_input_freq() / 2;
+}
+
+static uint32_t get_cpu_2x_freq(void)
+{
+ // cpu 2x is either /3 or /2 the speed of 6x4x
+ return get_cpu_input_freq() / ((SLCR_REG(CLK_621_TRUE) & 1) ? 3 : 2);
+}
+
+static uint32_t get_cpu_1x_freq(void)
+{
+ // cpu 1x is either /6 or /4 the speed of 6x4x
+ return get_cpu_input_freq() / ((SLCR_REG(CLK_621_TRUE) & 1) ? 6 : 4);
+}
+
+uint32_t zynq_get_arm_freq(void)
+{
+ return get_cpu_6x4x_freq();
+}
+
+uint32_t zynq_get_arm_timer_freq(void)
+{
+ return get_cpu_3x2x_freq();
+}
+
+uint32_t zynq_get_swdt_freq(void)
+{
+ return get_cpu_1x_freq();
+}
+
+struct periph_clock {
+ addr_t clk_ctrl_reg;
+ uint enable_bit_pos;
+};
+
+static addr_t periph_clk_ctrl_reg(enum zynq_periph periph)
+{
+ DEBUG_ASSERT(periph < _PERIPH_MAX);
+
+ switch (periph) {
+ case PERIPH_USB0: return (uintptr_t)&SLCR->USB0_CLK_CTRL;
+ case PERIPH_USB1: return (uintptr_t)&SLCR->USB1_CLK_CTRL;
+ case PERIPH_GEM0: return (uintptr_t)&SLCR->GEM0_CLK_CTRL;
+ case PERIPH_GEM1: return (uintptr_t)&SLCR->GEM1_CLK_CTRL;
+ case PERIPH_SMC: return (uintptr_t)&SLCR->SMC_CLK_CTRL;
+ case PERIPH_LQSPI: return (uintptr_t)&SLCR->LQSPI_CLK_CTRL;
+ case PERIPH_SDIO0: return (uintptr_t)&SLCR->SDIO_CLK_CTRL;
+ case PERIPH_SDIO1: return (uintptr_t)&SLCR->SDIO_CLK_CTRL;
+ case PERIPH_UART0: return (uintptr_t)&SLCR->UART_CLK_CTRL;
+ case PERIPH_UART1: return (uintptr_t)&SLCR->UART_CLK_CTRL;
+ case PERIPH_SPI0: return (uintptr_t)&SLCR->SPI_CLK_CTRL;
+ case PERIPH_SPI1: return (uintptr_t)&SLCR->SPI_CLK_CTRL;
+ case PERIPH_CAN0: return (uintptr_t)&SLCR->CAN_CLK_CTRL;
+ case PERIPH_CAN1: return (uintptr_t)&SLCR->CAN_CLK_CTRL;
+ case PERIPH_DBG: return (uintptr_t)&SLCR->DBG_CLK_CTRL;
+ case PERIPH_PCAP: return (uintptr_t)&SLCR->PCAP_CLK_CTRL;
+ case PERIPH_FPGA0: return (uintptr_t)&SLCR->FPGA0_CLK_CTRL;
+ case PERIPH_FPGA1: return (uintptr_t)&SLCR->FPGA1_CLK_CTRL;
+ case PERIPH_FPGA2: return (uintptr_t)&SLCR->FPGA2_CLK_CTRL;
+ case PERIPH_FPGA3: return (uintptr_t)&SLCR->FPGA3_CLK_CTRL;
+ default: return 0;
+ }
+}
+
+static int periph_clk_ctrl_enable_bitpos(enum zynq_periph periph)
+{
+ switch (periph) {
+ case PERIPH_SDIO1:
+ case PERIPH_UART1:
+ case PERIPH_SPI1:
+ case PERIPH_CAN1:
+ return 1;
+ case PERIPH_FPGA0:
+ case PERIPH_FPGA1:
+ case PERIPH_FPGA2:
+ case PERIPH_FPGA3:
+ return -1; // enable bit is more complicated on fpga
+ default:
+ // most peripherals have the enable bit in bit0
+ return 0;
+ }
+}
+
+static uint periph_clk_ctrl_divisor_count(enum zynq_periph periph)
+{
+ switch (periph) {
+ case PERIPH_GEM0:
+ case PERIPH_GEM1:
+ case PERIPH_CAN0:
+ case PERIPH_CAN1:
+ case PERIPH_FPGA0:
+ case PERIPH_FPGA1:
+ case PERIPH_FPGA2:
+ case PERIPH_FPGA3:
+ return 2;
+ default:
+ // most peripherals have a single divisor
+ return 1;
+ }
+}
+
+static const char *periph_to_name(enum zynq_periph periph)
+{
+ switch (periph) {
+ case PERIPH_USB0: return "USB0";
+ case PERIPH_USB1: return "USB1";
+ case PERIPH_GEM0: return "GEM0";
+ case PERIPH_GEM1: return "GEM1";
+ case PERIPH_SMC: return "SMC";
+ case PERIPH_LQSPI: return "LQSPI";
+ case PERIPH_SDIO0: return "SDIO0";
+ case PERIPH_SDIO1: return "SDIO1";
+ case PERIPH_UART0: return "UART0";
+ case PERIPH_UART1: return "UART1";
+ case PERIPH_SPI0: return "SPI0";
+ case PERIPH_SPI1: return "SPI1";
+ case PERIPH_CAN0: return "CAN0";
+ case PERIPH_CAN1: return "CAN1";
+ case PERIPH_DBG: return "DBG";
+ case PERIPH_PCAP: return "PCAP";
+ case PERIPH_FPGA0: return "FPGA0";
+ case PERIPH_FPGA1: return "FPGA1";
+ case PERIPH_FPGA2: return "FPGA2";
+ case PERIPH_FPGA3: return "FPGA3";
+ default: return "unknown";
+ }
+}
+
+status_t zynq_set_clock(enum zynq_periph periph, bool enable, enum zynq_clock_source source, uint32_t divisor, uint32_t divisor2)
+{
+ DEBUG_ASSERT(periph < _PERIPH_MAX);
+ DEBUG_ASSERT(!enable || (divisor > 0 && divisor <= 0x3f));
+ DEBUG_ASSERT(source < 4);
+
+ // get the clock control register base
+ addr_t clk_reg = periph_clk_ctrl_reg(periph);
+ DEBUG_ASSERT(clk_reg != 0);
+
+ int enable_bitpos = periph_clk_ctrl_enable_bitpos(periph);
+
+ zynq_slcr_unlock();
+
+ // if we're enabling
+ if (enable) {
+ uint32_t ctrl = *REG32(clk_reg);
+
+ // set the divisor, divisor2 (if applicable), source, and enable
+ ctrl = (ctrl & ~(0x3f << 20)) | (divisor2 << 20);
+ ctrl = (ctrl & ~(0x3f << 8)) | (divisor << 8);
+ ctrl = (ctrl & ~(0x3 << 4)) | (source << 4);
+
+ if (enable_bitpos >= 0)
+ ctrl |= (1 << enable_bitpos);
+
+ *REG32(clk_reg) = ctrl;
+ } else {
+ if (enable_bitpos >= 0) {
+ // disabling
+ uint32_t ctrl = *REG32(clk_reg);
+
+ ctrl &= ~(1 << enable_bitpos);
+
+ *REG32(clk_reg) = ctrl;
+ }
+ }
+
+ zynq_slcr_lock();
+
+ return NO_ERROR;
+}
+
+uint32_t zynq_get_clock(enum zynq_periph periph)
+{
+ DEBUG_ASSERT(periph < _PERIPH_MAX);
+
+ // get the clock control register base
+ addr_t clk_reg = periph_clk_ctrl_reg(periph);
+ DEBUG_ASSERT(clk_reg != 0);
+
+ int enable_bitpos = periph_clk_ctrl_enable_bitpos(periph);
+
+ LTRACEF("clkreg 0x%x\n", *REG32(clk_reg));
+
+ // see if it's enabled
+ if (enable_bitpos >= 0) {
+ if ((*REG32(clk_reg) & (1 << enable_bitpos)) == 0) {
+ // not enabled
+ return 0;
+ }
+ }
+
+ // get the source clock
+ uint32_t srcclk;
+ switch (BITS_SHIFT(*REG32(clk_reg), 5, 4)) {
+ case 0: case 1:
+ srcclk = get_io_pll_freq();
+ break;
+ case 2:
+ srcclk = get_arm_pll_freq();
+ break;
+ case 3:
+ srcclk = get_ddr_pll_freq();
+ break;
+ }
+
+ // get the divisor out of the register
+ uint32_t divisor = BITS_SHIFT(*REG32(clk_reg), 13, 8);
+ if (divisor == 0)
+ return 0;
+
+ uint32_t divisor2 = 1;
+ if (periph_clk_ctrl_divisor_count(periph) == 2) {
+ divisor2 = BITS_SHIFT(*REG32(clk_reg), 25, 20);
+ if (divisor2 == 0)
+ return 0;
+ }
+
+ uint32_t clk = srcclk / divisor / divisor2;
+
+ return clk;
+}
+
+void zynq_dump_clocks(void)
+{
+ printf("zynq clocks:\n");
+ printf("\tarm pll %d\n", get_arm_pll_freq());
+ printf("\tddr pll %d\n", get_ddr_pll_freq());
+ printf("\tio pll %d\n", get_io_pll_freq());
+
+ printf("\tarm clock %d\n", zynq_get_arm_freq());
+ printf("\tarm timer clock %d\n", zynq_get_arm_timer_freq());
+ printf("\tcpu6x4x clock %d\n", get_cpu_6x4x_freq());
+ printf("\tcpu3x2x clock %d\n", get_cpu_3x2x_freq());
+ printf("\tcpu2x clock %d\n", get_cpu_2x_freq());
+ printf("\tcpu1x clock %d\n", get_cpu_1x_freq());
+
+ printf("peripheral clocks:\n");
+ for (uint i = 0; i < _PERIPH_MAX; i++) {
+ printf("\tperiph %d (%s) clock %u\n", i, periph_to_name(i), zynq_get_clock(i));
+ }
+}
+
diff --git a/src/bsp/lk/platform/zynq/debug.c b/src/bsp/lk/platform/zynq/debug.c
new file mode 100644
index 0000000..8424416
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/debug.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2008-2014 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <reg.h>
+#include <stdio.h>
+#include <kernel/thread.h>
+#include <dev/uart.h>
+#include <platform.h>
+#include <platform/debug.h>
+#include <platform/zynq.h>
+#include <target/debugconfig.h>
+#include <reg.h>
+
+/* DEBUG_UART must be defined to 0 or 1 */
+#if defined(DEBUG_UART) && DEBUG_UART == 0
+#define DEBUG_UART_BASE UART0_BASE
+#elif defined(DEBUG_UART) && DEBUG_UART == 1
+#define DEBUG_UART_BASE UART1_BASE
+#else
+#error define DEBUG_UART to something valid
+#endif
+
+void platform_dputc(char c)
+{
+ if (c == '\n')
+ uart_putc(DEBUG_UART, '\r');
+ uart_putc(DEBUG_UART, c);
+}
+
+int platform_dgetc(char *c, bool wait)
+{
+ int ret = uart_getc(DEBUG_UART, wait);
+ if (ret == -1)
+ return -1;
+ *c = ret;
+ return 0;
+
+}
+
+/* zynq specific halt */
+void platform_halt(platform_halt_action suggested_action,
+ platform_halt_reason reason)
+{
+ switch (suggested_action) {
+ default:
+ case HALT_ACTION_SHUTDOWN:
+ case HALT_ACTION_HALT:
+ printf("HALT: spinning forever... (reason = %d)\n", reason);
+ arch_disable_ints();
+ for(;;)
+ arch_idle();
+ break;
+ case HALT_ACTION_REBOOT:
+ printf("REBOOT\n");
+ arch_disable_ints();
+ for (;;) {
+ zynq_slcr_unlock();
+ SLCR->PSS_RST_CTRL = 1;
+ }
+ break;
+ }
+}
diff --git a/src/bsp/lk/platform/zynq/fpga.c b/src/bsp/lk/platform/zynq/fpga.c
new file mode 100644
index 0000000..fbdbe8f
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/fpga.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2014 Brian Swetland
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <platform/fpga.h>
+#include <trace.h>
+#include <reg.h>
+#include <err.h>
+#include <platform.h>
+#include <kernel/thread.h>
+
+#define LOCAL_TRACE 0
+#define FPGA_TIMEOUT 1000
+
+#define DEVCFG_CTRL 0xF8007000
+#define PCFG_PROG_B (1 << 30)
+#define PCFG_POR_CNT_4K (1 << 29)
+#define PCAP_PR (1 << 27) // 0 = ICAP CFG, 1 = PCAP CFG
+#define PCAP_MODE (1 << 26) // 1 = Enable PCAP Interface
+#define DEVCFG_LOCK 0xF8007004
+#define DEVCFG_CFG 0xF8007008
+#define DEVCFG_INT_STS 0xF800700C
+#define DMA_DONE_INT (1 << 13)
+#define PSS_CFG_RESET_B (1 << 5) // 1 = PL in reset state
+#define PCFG_DONE_INT (1 << 2) // 1 = PL successfully programmed
+#define PCFG_INIT_PE_INT (1 << 1)
+#define PCFG_INIT_NE_INT (1 << 0)
+#define DEVCFG_INT_MASK 0xF8007010
+#define DEVCFG_STATUS 0xF8007014
+#define PCFG_INIT (1 << 4) // 1 = ready for bitstream
+#define DEVCFG_DMA_SRC_ADDR 0xF8007018
+#define DEVCFG_DMA_DST_ADDR 0xF800701C
+#define DEVCFG_DMA_SRC_LEN 0xF8007020 // words
+#define DEVCFG_DMA_DST_LEN 0xF8007024 // words
+#define DEVCFG_SW_ID 0xF8007030
+#define DEVCFG_MCTRL 0xF8007080
+#define PCFG_POR_B (1 << 8) // 1 = PL is powered on
+#define INT_PCAP_LPBK (1 << 4) // 1 = Loopback Enabled
+
+// Per Zynq TRM, 6.4.4
+// 1. wait for PCFG_INIT==1
+// 2. disable loopback
+// 3. set DEVCFG CTRL PCAP_PR and PCAP_MODE
+// 4. set dma src, dst, srclen, dstlen (in that specific order)
+// 5. wait for PCFG_DONE_INT==1
+
+status_t zynq_program_fpga(paddr_t physaddr, size_t length) {
+ LTRACEF("phys 0x%lx, len 0x%zx\n", physaddr, length);
+
+ lk_bigtime_t bt = current_time_hires();
+
+ /* length is in words */
+ length /= 4;
+
+ lk_time_t t;
+
+ t = current_time();
+ while(!(readl(DEVCFG_STATUS) & PCFG_INIT)) {
+ if (current_time() - t > FPGA_TIMEOUT) {
+ TRACEF("timeout waiting for PCFG_INIT\n");
+ return ERR_TIMED_OUT;
+ }
+ }
+ writel(readl(DEVCFG_MCTRL) & (~INT_PCAP_LPBK), DEVCFG_MCTRL);
+ writel(readl(DEVCFG_CTRL) | PCAP_PR | PCAP_MODE, DEVCFG_CTRL);
+ writel(0xffffffff, DEVCFG_INT_STS);
+ writel(physaddr, DEVCFG_DMA_SRC_ADDR);
+ writel(0xFFFFFFFF, DEVCFG_DMA_DST_ADDR);
+ writel(length, DEVCFG_DMA_SRC_LEN);
+ writel(length, DEVCFG_DMA_DST_LEN);
+
+ t = current_time();
+ uint32_t sts = 0;
+ for (;;) {
+ sts = readl(DEVCFG_INT_STS);
+#if LOCAL_TRACE
+ static uint32_t last = 0;
+ if (last != sts) {
+ printf("dsts 0x%x\n", sts);
+ }
+ last = sts;
+#endif
+ if (sts & PCFG_DONE_INT)
+ break;
+
+ if (current_time() - t > FPGA_TIMEOUT) {
+ TRACEF("timeout waiting for PCFG_DONE_INT, DEVCFG_INT_STS is 0x%x\n", sts);
+ return ERR_TIMED_OUT;
+ }
+ }
+
+ bt = current_time_hires() - bt;
+ LTRACEF("fpga program took %llu usecs\n", bt);
+
+ return NO_ERROR;
+}
+
+bool zync_fpga_config_done(void) {
+ return (0 != (readl(DEVCFG_INT_STS) & PCFG_DONE_INT));
+}
+
+void zynq_reset_fpga(void) {
+ writel(readl(DEVCFG_CTRL) & (~PCFG_PROG_B), DEVCFG_CTRL);
+ writel(readl(DEVCFG_CTRL) | PCFG_PROG_B, DEVCFG_CTRL);
+}
+
diff --git a/src/bsp/lk/platform/zynq/gem.c b/src/bsp/lk/platform/zynq/gem.c
new file mode 100644
index 0000000..188e615
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/gem.c
@@ -0,0 +1,639 @@
+/*
+ * Copyright (c) 2014 Christopher Anderson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <assert.h>
+#include <lib/console.h>
+#include <debug.h>
+#include <list.h>
+#include <err.h>
+#include <errno.h>
+#include <reg.h>
+#include <endian.h>
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <trace.h>
+#include <bits.h>
+#include <pow2.h>
+#include <sys/types.h>
+#include <lib/cbuf.h>
+#include <kernel/timer.h>
+#include <kernel/thread.h>
+#include <kernel/vm.h>
+#include <kernel/spinlock.h>
+#include <kernel/debug.h>
+#include <platform/interrupts.h>
+#include <platform/debug.h>
+#include <platform/gem.h>
+#include <platform.h>
+#include <kernel/event.h>
+#include <kernel/semaphore.h>
+
+#include <lib/pktbuf.h>
+#include <lib/pool.h>
+
+#define LOCAL_TRACE 0
+
+/* Allow targets to override these values */
+#ifndef GEM_RX_DESC_CNT
+#define GEM_RX_DESC_CNT 32
+#endif
+
+#ifndef GEM_TX_DESC_CNT
+#define GEM_TX_DESC_CNT 32
+#endif
+
+#ifndef GEM_RX_BUF_SIZE
+#define GEM_RX_BUF_SIZE 1536
+#endif
+
+#ifndef GEM_TX_BUF_SIZE
+#define GEM_TX_BUF_SIZE 1536
+#endif
+
+pool_t rx_buf_pool;
+static spin_lock_t lock = SPIN_LOCK_INITIAL_VALUE;
+
+struct gem_desc {
+ uint32_t addr;
+ uint32_t ctrl;
+};
+
+/* Quick overview:
+ * RX:
+ * rx_tbl contains rx descriptors. A pktbuf is allocated for each of these and a descriptor
+ * entry in the table points to a buffer in the pktbuf. rx_tbl[X]'s pktbuf is stored in rx_pbufs[X]
+ *
+ * TX:
+ * The current position to write new tx descriptors to is maintained by gem.tx_head. As frames are
+ * queued in tx_tbl their pktbufs are stored in the list queued_pbufs. As frame transmission is
+ * completed these pktbufs are released back to the pool by the interrupt handler for TX_COMPLETE
+ */
+struct gem_descs {
+ struct gem_desc rx_tbl[GEM_RX_DESC_CNT];
+ struct gem_desc tx_tbl[GEM_TX_DESC_CNT];
+};
+
+struct gem_state {
+ volatile struct gem_regs *regs;
+
+ struct gem_descs *descs;
+ paddr_t descs_phys;
+
+ unsigned int tx_head;
+ unsigned int tx_tail;
+ unsigned int tx_count;
+ struct list_node tx_queue;
+ struct list_node queued_pbufs;
+
+ gem_cb_t rx_callback;
+ event_t rx_pending;
+ event_t tx_complete;
+ bool debug_rx;
+ pktbuf_t *rx_pbufs[GEM_RX_DESC_CNT];
+};
+
+struct gem_state gem;
+
+static void debug_rx_handler(pktbuf_t *p)
+{
+ static uint32_t pkt = 0;
+
+ printf("[%10u] packet %u, %zu bytes:\n", (uint32_t)current_time(), ++pkt, p->dlen);
+ hexdump8(p->data, p->dlen);
+ putchar('\n');
+}
+
+static int free_completed_pbuf_frames(void) {
+ int ret = 0;
+
+ gem.regs->tx_status = gem.regs->tx_status;
+
+ while (gem.tx_count > 0 &&
+ (gem.descs->tx_tbl[gem.tx_tail].ctrl & TX_DESC_USED)) {
+
+ bool eof;
+ do {
+ pktbuf_t *p = list_remove_head_type(&gem.queued_pbufs, pktbuf_t, list);
+ DEBUG_ASSERT(p);
+ eof = p->flags & PKTBUF_FLAG_EOF;
+ ret += pktbuf_free(p, false);
+ } while (!eof);
+
+ gem.tx_tail = (gem.tx_tail + 1) % GEM_TX_DESC_CNT;
+ gem.tx_count--;
+ }
+
+ return ret;
+}
+
+void queue_pkts_in_tx_tbl(void) {
+ pktbuf_t *p;
+ unsigned int cur_pos;
+
+ if (list_is_empty(&gem.tx_queue)) {
+ return;
+ }
+
+ // XXX handle multi part buffers
+
+ /* Queue packets in the descriptor table until we're either out of space in the table
+ * or out of packets in our tx queue. Any packets left will remain in the list and be
+ * processed the next time available */
+ while (gem.tx_count < GEM_TX_DESC_CNT &&
+ ((p = list_remove_head_type(&gem.tx_queue, pktbuf_t, list)) != NULL)) {
+ cur_pos = gem.tx_head;
+
+ uint32_t addr = pktbuf_data_phys(p);
+ uint32_t ctrl = gem.descs->tx_tbl[cur_pos].ctrl & TX_DESC_WRAP; /* protect the wrap bit */
+ ctrl |= TX_BUF_LEN(p->dlen);
+
+ DEBUG_ASSERT(p->flags & PKTBUF_FLAG_EOF); // a multi part buffer would have caused a race condition w/hardware
+ if (p->flags & PKTBUF_FLAG_EOF) {
+ ctrl |= TX_LAST_BUF;
+ }
+
+ /* fill in the descriptor, control word last (in case hardware is racing us) */
+ gem.descs->tx_tbl[cur_pos].addr = addr;
+ gem.descs->tx_tbl[cur_pos].ctrl = ctrl;
+
+ gem.tx_head = (gem.tx_head + 1) % GEM_TX_DESC_CNT;
+ gem.tx_count++;
+ list_add_tail(&gem.queued_pbufs, &p->list);
+ }
+
+ DMB;
+ gem.regs->net_ctrl |= NET_CTRL_START_TX;
+}
+
+int gem_send_raw_pkt(struct pktbuf *p)
+{
+ status_t ret = NO_ERROR;
+
+ if (!p || !p->dlen) {
+ ret = -1;
+ goto err;
+ }
+
+ /* make sure the output buffer is fully written to memory before
+ * placing on the outgoing list. */
+
+ // XXX handle multi part buffers
+ arch_clean_cache_range((vaddr_t)p->data, p->dlen);
+
+ spin_lock_saved_state_t irqstate;
+ spin_lock_irqsave(&lock, irqstate);
+ list_add_tail(&gem.tx_queue, &p->list);
+ queue_pkts_in_tx_tbl();
+ spin_unlock_irqrestore(&lock, irqstate);
+
+err:
+ return ret;
+}
+
+
+enum handler_return gem_int_handler(void *arg) {
+ uint32_t intr_status;
+ bool resched = false;
+
+ intr_status = gem.regs->intr_status;
+
+ spin_lock(&lock);
+
+ while (intr_status) {
+ // clear any pending status
+ gem.regs->intr_status = intr_status;
+
+ // Received an RX complete
+ if (intr_status & INTR_RX_COMPLETE) {
+ event_signal(&gem.rx_pending, false);
+
+ gem.regs->rx_status |= INTR_RX_COMPLETE;
+
+ resched = true;
+ }
+
+ if (intr_status & INTR_RX_USED_READ) {
+
+ for (int i = 0; i < GEM_RX_DESC_CNT; i++) {
+ gem.descs->rx_tbl[i].addr &= ~RX_DESC_USED;
+ }
+
+ gem.regs->rx_status &= ~RX_STATUS_BUFFER_NOT_AVAIL;
+ gem.regs->net_ctrl &= ~NET_CTRL_RX_EN;
+ gem.regs->net_ctrl |= NET_CTRL_RX_EN;
+ printf("GEM overflow, dumping pending packets\n");
+ }
+
+ if (intr_status & INTR_TX_CORRUPT) {
+ printf("tx ahb error!\n");
+ if (free_completed_pbuf_frames() > 0) {
+ resched = true;
+ }
+ }
+
+ /* A frame has been completed so we can clean up ownership of its buffers */
+ if (intr_status & INTR_TX_COMPLETE) {
+ if (free_completed_pbuf_frames() > 0) {
+ resched = true;
+ }
+ }
+
+ /* The controller has processed packets until it hit a buffer owned by the driver */
+ if (intr_status & INTR_TX_USED_READ) {
+ queue_pkts_in_tx_tbl();
+ gem.regs->tx_status |= TX_STATUS_USED_READ;
+ }
+
+ /* see if we have any more */
+ intr_status = gem.regs->intr_status;
+ }
+
+ spin_unlock(&lock);
+
+ return (resched) ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
+}
+
+static bool wait_for_phy_idle(void)
+{
+ int iters = 1000;
+ while (iters && !(gem.regs->net_status & NET_STATUS_PHY_MGMT_IDLE)) {
+ iters--;
+ }
+
+ if (iters == 0) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool gem_phy_init(void) {
+ return wait_for_phy_idle();
+}
+
+static status_t gem_cfg_buffer_descs(void)
+{
+ void *rx_buf_vaddr;
+ status_t ret;
+
+
+ if ((ret = vmm_alloc_contiguous(vmm_get_kernel_aspace(), "gem_rx_bufs",
+ GEM_RX_DESC_CNT * GEM_RX_BUF_SIZE, (void **) &rx_buf_vaddr, 0, 0,
+ ARCH_MMU_FLAG_CACHED)) < 0) {
+ return ret;
+ }
+
+ /* Take pktbufs from the allocated target pool and assign them to the gem RX
+ * descriptor table */
+ pool_init(&rx_buf_pool, GEM_RX_BUF_SIZE, CACHE_LINE, GEM_RX_DESC_CNT, rx_buf_vaddr);
+ for (unsigned int n = 0; n < GEM_RX_DESC_CNT; n++) {
+ void *b = pool_alloc(&rx_buf_pool);
+ pktbuf_t *p = pktbuf_alloc_empty();
+ if (!p || !b) {
+ return -1;
+ }
+
+ pktbuf_add_buffer(p, b, GEM_RX_BUF_SIZE, 0, PKTBUF_FLAG_CACHED, NULL, NULL);
+ gem.rx_pbufs[n] = p;
+ gem.descs->rx_tbl[n].addr = (uintptr_t) p->phys_base;
+ gem.descs->rx_tbl[n].ctrl = 0;
+ }
+
+ /* Claim ownership of TX descriptors for the driver */
+ for (unsigned i = 0; i < GEM_TX_DESC_CNT; i++) {
+ gem.descs->tx_tbl[i].addr = 0;
+ gem.descs->tx_tbl[i].ctrl = TX_DESC_USED;
+ }
+
+ /* Both set of descriptors need wrap bits set at the end of their tables*/
+ gem.descs->rx_tbl[GEM_RX_DESC_CNT-1].addr |= RX_DESC_WRAP;
+ gem.descs->tx_tbl[GEM_TX_DESC_CNT-1].ctrl |= TX_DESC_WRAP;
+
+ /* Point the controller at the offset into state's physical location for RX descs */
+ gem.regs->rx_qbar = ((uintptr_t)&gem.descs->rx_tbl[0] - (uintptr_t)gem.descs) + gem.descs_phys;
+ gem.regs->tx_qbar = ((uintptr_t)&gem.descs->tx_tbl[0] - (uintptr_t)gem.descs) + gem.descs_phys;
+
+ return NO_ERROR;
+}
+
+static void gem_cfg_ints(void)
+{
+ uint32_t gem_base = (uintptr_t)gem.regs;
+
+ if (gem_base == GEM0_BASE) {
+ register_int_handler(ETH0_INT, gem_int_handler, NULL);
+ unmask_interrupt(ETH0_INT);
+ } else if (gem_base == GEM1_BASE) {
+ register_int_handler(ETH1_INT, gem_int_handler, NULL);
+ unmask_interrupt(ETH1_INT);
+ } else {
+ printf("Illegal gem periph base address 0x%08X!\n", gem_base);
+ return;
+ }
+
+ /* Enable all interrupts */
+ gem.regs->intr_en = INTR_RX_COMPLETE | INTR_TX_COMPLETE | INTR_HRESP_NOT_OK | INTR_MGMT_SENT |
+ INTR_RX_USED_READ | INTR_TX_CORRUPT | INTR_TX_USED_READ | INTR_RX_OVERRUN;
+}
+
+int gem_rx_thread(void *arg)
+{
+ pktbuf_t *p;
+ int bp = 0;
+
+ while (1) {
+ event_wait(&gem.rx_pending);
+
+ for (;;) {
+ if (gem.descs->rx_tbl[bp].addr & RX_DESC_USED) {
+ uint32_t ctrl = gem.descs->rx_tbl[bp].ctrl;
+
+ p = gem.rx_pbufs[bp];
+ p->dlen = RX_BUF_LEN(ctrl);
+ p->data = p->buffer + 2;
+
+ /* copy the checksum offloading bits */
+ p->flags = 0;
+ p->flags |= (BITS_SHIFT(ctrl, 23, 22) != 0) ? PKTBUF_FLAG_CKSUM_IP_GOOD : 0;
+ p->flags |= (BITS_SHIFT(ctrl, 23, 22) == 1) ? PKTBUF_FLAG_CKSUM_UDP_GOOD : 0;
+ p->flags |= (BITS_SHIFT(ctrl, 23, 22) == 2) ? PKTBUF_FLAG_CKSUM_TCP_GOOD : 0;
+
+ /* invalidate any stale cache lines on the receive buffer to ensure
+ * the cpu has a fresh copy of incomding data. */
+ arch_invalidate_cache_range((vaddr_t)p->data, p->dlen);
+
+ if (unlikely(gem.debug_rx)) {
+ debug_rx_handler(p);
+ }
+
+ if (likely(gem.rx_callback)) {
+ gem.rx_callback(p);
+ }
+
+ /* make sure all dirty data is flushed out of the buffer before
+ * putting into the receive queue */
+ arch_clean_invalidate_cache_range((vaddr_t)p->buffer, PKTBUF_SIZE);
+
+ gem.descs->rx_tbl[bp].addr &= ~RX_DESC_USED;
+ gem.descs->rx_tbl[bp].ctrl = 0;
+ bp = (bp + 1) % GEM_RX_DESC_CNT;
+ } else {
+ break;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+int gem_stat_thread(void *arg) {
+ volatile bool *run = ((bool *)arg);
+ static uint32_t frames_rx = 0, frames_tx = 0;
+
+ while (*run) {
+ frames_tx += gem.regs->frames_tx;
+ frames_rx += gem.regs->frames_rx;
+ printf("GEM tx_head %u, tx_tail %u, tx_count %u, tx_frames %u, rx_frames %u\n",
+ gem.tx_head, gem.tx_tail, gem.tx_count, frames_tx, frames_rx);
+ thread_sleep(1000);
+ }
+
+ return 0;
+}
+
+void gem_deinit(uintptr_t base)
+{
+ /* reset the gem peripheral */
+ uint32_t rst_mask;
+ if (base == GEM0_BASE) {
+ rst_mask = (1<<6) | (1<<4) | (1<<0);
+ } else {
+ rst_mask = (1<<7) | (1<<5) | (1<<1);
+ }
+ SLCR->GEM_RST_CTRL |= rst_mask;
+ spin(1);
+ SLCR->GEM_RST_CTRL &= ~rst_mask;
+
+
+ /* Clear Network control / status registers */
+ gem.regs->net_ctrl |= NET_CTRL_STATCLR;
+ gem.regs->rx_status = 0x0F;
+ gem.regs->tx_status = 0xFF;
+ /* Disable interrupts */
+ gem.regs->intr_dis = 0x7FFFEFF;
+
+ /* Empty out the buffer queues */
+ gem.regs->rx_qbar = 0;
+ gem.regs->tx_qbar = 0;
+}
+
+status_t gem_init(uintptr_t gem_base)
+{
+ status_t ret;
+ uint32_t reg_val;
+ thread_t *rx_thread;
+ void *descs_vaddr;
+ paddr_t descs_paddr;
+
+ DEBUG_ASSERT(gem_base == GEM0_BASE || gem_base == GEM1_BASE);
+
+ /* Data structure init */
+ event_init(&gem.tx_complete, false, EVENT_FLAG_AUTOUNSIGNAL);
+ event_init(&gem.rx_pending, false, EVENT_FLAG_AUTOUNSIGNAL);
+ list_initialize(&gem.queued_pbufs);
+ list_initialize(&gem.tx_queue);
+
+ /* allocate a block of uncached contiguous memory for the peripheral descriptors */
+ if ((ret = vmm_alloc_contiguous(vmm_get_kernel_aspace(), "gem_desc",
+ sizeof(*gem.descs), &descs_vaddr, 0, 0, ARCH_MMU_FLAG_UNCACHED_DEVICE)) < 0) {
+ return ret;
+ }
+ descs_paddr = kvaddr_to_paddr((void *)descs_vaddr);
+
+ /* tx/rx descriptor tables and memory mapped registers */
+ gem.descs = (void *)descs_vaddr;
+ gem.descs_phys = descs_paddr;
+ gem.regs = (struct gem_regs *)gem_base;
+
+ /* rx background thread */
+ rx_thread = thread_create("gem_rx", gem_rx_thread, NULL, HIGH_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(rx_thread);
+
+ /* Bring whatever existing configuration is up down so we can do it cleanly */
+ gem_deinit(gem_base);
+ gem_cfg_buffer_descs();
+
+ /* Self explanatory configuration for the gige */
+ reg_val = NET_CFG_FULL_DUPLEX;
+ reg_val |= NET_CFG_GIGE_EN;
+ reg_val |= NET_CFG_SPEED_100;
+ reg_val |= NET_CFG_RX_CHKSUM_OFFLD_EN;
+ reg_val |= NET_CFG_FCS_REMOVE;
+ reg_val |= NET_CFG_MDC_CLK_DIV(0x7);
+ reg_val |= NET_CFG_RX_BUF_OFFSET(2);
+ gem.regs->net_cfg = reg_val;
+
+ /* Set DMA to 1600 byte rx buffer, 8KB addr space for rx, 4KB addr space for tx,
+ * hw checksumming, little endian, and use INCR16 ahb bursts
+ */
+ reg_val = DMA_CFG_AHB_MEM_RX_BUF_SIZE(0x19);
+ reg_val |= DMA_CFG_RX_PKTBUF_MEMSZ_SEL(0x3);
+ reg_val |= DMA_CFG_TX_PKTBUF_MEMSZ_SEL;
+ reg_val |= DMA_CFG_CSUM_GEN_OFFLOAD_EN;
+ reg_val |= DMA_CFG_AHB_FIXED_BURST_LEN(0x10);
+ gem.regs->dma_cfg = reg_val;
+
+ /* Enable VREF from GPIOB */
+ SLCR_REG(GPIOB_CTRL) = 0x1;
+
+ ret = gem_phy_init();
+ if (!ret) {
+ printf("Phy not idle, aborting!\n");
+ return ret;
+ }
+
+ gem_cfg_ints();
+
+ reg_val = NET_CTRL_MD_EN;
+ reg_val |= NET_CTRL_RX_EN;
+ reg_val |= NET_CTRL_TX_EN;
+ gem.regs->net_ctrl = reg_val;
+
+ return NO_ERROR;
+}
+
+void gem_disable(void)
+{
+ /* disable all the interrupts */
+ gem.regs->intr_en = 0;
+ mask_interrupt(ETH0_INT);
+
+ /* stop tx and rx */
+ gem.regs->net_ctrl = 0;
+}
+
+void gem_set_callback(gem_cb_t rx)
+{
+ gem.rx_callback = rx;
+}
+
+void gem_set_macaddr(uint8_t mac[6]) {
+ uint32_t en = gem.regs->net_ctrl &= NET_CTRL_RX_EN | NET_CTRL_TX_EN;
+
+ if (en) {
+ gem.regs->net_ctrl &= ~(en);
+ }
+
+ /* _top register must be written after _bot register */
+ gem.regs->spec_addr1_bot = (mac[3] << 24) | (mac[2] << 16) | (mac[1] << 8) | mac[0];
+ gem.regs->spec_addr1_top = (mac[5] << 8) | mac[4];
+
+ if (en) {
+ gem.regs->net_ctrl |= en;
+ }
+}
+
+
+/* Debug console commands */
+static int cmd_gem(int argc, const cmd_args *argv)
+{
+ static uint32_t frames_rx = 0;
+ static uint32_t frames_tx = 0;
+ static bool run_stats = false;
+ thread_t *stat_thread;
+
+ if (argc == 1) {
+ printf("gem raw <iter> <length>: Send <iter> raw mac packet for testing\n");
+ printf("gem rx_debug: toggle RX debug output\n");
+ printf("gem stats toggle periodic output of driver stats\n");
+ printf("gem status: print driver status\n");
+ } else if (strncmp(argv[1].str, "rx_debug", sizeof("rx_debug")) == 0) {
+ pktbuf_t *p;
+ int iter;
+ if (argc < 4) {
+ return 0;
+ }
+
+ if ((p = pktbuf_alloc()) == NULL) {
+ printf("out of buffers\n");
+ }
+
+ iter = argv[2].u;
+ p->dlen = argv[3].u;
+ while (iter--) {
+ memset(p->data, iter, 12);
+ gem_send_raw_pkt(p);
+ }
+ } else if (strncmp(argv[1].str, "status", sizeof("status")) == 0) {
+ uint32_t mac_top = gem.regs->spec_addr1_top;
+ uint32_t mac_bot = gem.regs->spec_addr1_bot;
+ printf("mac addr: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac_top >> 8, mac_top & 0xFF, mac_bot >> 24, (mac_bot >> 16) & 0xFF,
+ (mac_bot >> 8) & 0xFF, mac_bot & 0xFF);
+ uint32_t rx_used = 0, tx_used = 0;
+ for (int i = 0; i < GEM_RX_DESC_CNT; i++) {
+ rx_used += !!(gem.descs->rx_tbl[i].addr & RX_DESC_USED);
+ }
+
+ for (int i = 0; i < GEM_TX_DESC_CNT; i++) {
+ tx_used += !!(gem.descs->tx_tbl[i].ctrl & TX_DESC_USED);
+ }
+
+ frames_tx += gem.regs->frames_tx;
+ frames_rx += gem.regs->frames_rx;
+ printf("rx usage: %u/%u, tx usage %u/%u\n",
+ rx_used, GEM_RX_DESC_CNT, tx_used, GEM_TX_DESC_CNT);
+ printf("frames rx: %u, frames tx: %u\n",
+ frames_rx, frames_tx);
+ printf("tx:\n");
+ for (size_t i = 0; i < GEM_TX_DESC_CNT; i++) {
+ uint32_t ctrl = gem.descs->tx_tbl[i].ctrl;
+ uint32_t addr = gem.descs->tx_tbl[i].addr;
+
+ printf("%3zu 0x%08X 0x%08X: len %u, %s%s%s %s%s\n",
+ i, addr, ctrl, TX_BUF_LEN(ctrl),
+ (ctrl & TX_DESC_USED) ? "driver " : "controller ",
+ (ctrl & TX_DESC_WRAP) ? "wrap " : "",
+ (ctrl & TX_LAST_BUF) ? "eof " : "",
+ (i == gem.tx_head) ? "<-- HEAD " : "",
+ (i == gem.tx_tail) ? "<-- TAIL " : "");
+ }
+
+ } else if (strncmp(argv[1].str, "stats", sizeof("stats")) == 0) {
+ run_stats = !run_stats;
+ if (run_stats) {
+ stat_thread = thread_create("gem_stat",
+ gem_stat_thread, &run_stats, LOW_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_resume(stat_thread);
+ }
+ } else if (argv[1].str[0] == 'd') {
+ gem.debug_rx = !gem.debug_rx;
+ }
+
+ return 0;
+}
+
+STATIC_COMMAND_START
+STATIC_COMMAND("gem", "ZYNQ GEM commands", &cmd_gem)
+STATIC_COMMAND_END(gem);
diff --git a/src/bsp/lk/platform/zynq/gpio.c b/src/bsp/lk/platform/zynq/gpio.c
new file mode 100644
index 0000000..1f0412c
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/gpio.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2012-2015 Travis Geiselbrecht
+ * Copyright (c) 2015 Christopher Anderson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <assert.h>
+#include <debug.h>
+#include <reg.h>
+#include <stdio.h>
+#include <string.h>
+#include <dev/gpio.h>
+#include <platform/gpio.h>
+#include <platform/interrupts.h>
+#include <target/gpioconfig.h>
+
+#define MAX_GPIO 128
+
+static inline uint16_t extract_bank(unsigned gpio_id) { return gpio_id / 32; }
+static inline uint16_t extract_bit (unsigned gpio_id) { return gpio_id % 32; }
+
+struct {
+ int_handler callback;
+ void *args;
+} irq_callbacks[MAX_GPIO];
+
+static enum handler_return gpio_int_handler(void *arg) {
+
+ /* The mask register uses 1 to respresent masked, 0 for unmasked. Comparing that
+ * register with the interrupt status register is the only way to determine
+ * which gpio triggered the interrupt in the gic. */
+ for (uint32_t bank = 0; bank < 4; bank++) {
+ uint32_t mask = *REG32(GPIO_INT_MASK(bank));
+ uint32_t stat = *REG32(GPIO_INT_STAT(bank));
+ uint32_t active = ~mask & stat;
+
+ if (active == 0) {
+ continue;
+ }
+
+ //printf("mask 0x%08x stat 0x%08x active 0x%08x\n", mask, stat, active);
+ while (active) {
+ /* Find the rightmost set bit, calculate the associated gpio, and call the callback */
+ uint16_t bit = 32 - clz(active) - 1;
+ uint16_t gpio = bit + (bank * 32);
+
+ active ^= (1 << bit);
+ if (irq_callbacks[gpio].callback) {
+ irq_callbacks[gpio].callback(irq_callbacks[gpio].args);
+ }
+ //printf("bit %u bank %u gpio %u was triggered\n", bit, bank, gpio);
+ }
+
+ *REG32(GPIO_INT_STAT(bank)) = stat;
+ }
+
+ return 0;
+}
+
+void zynq_unmask_gpio_interrupt(unsigned gpio)
+{
+ uint16_t bank = extract_bank(gpio);
+ uint16_t bit = extract_bit(gpio);
+
+ RMWREG32(GPIO_INT_EN(bank), bit, 1, 1);
+ RMWREG32(GPIO_INT_STAT(bank), bit, 1, 1);
+}
+
+void zynq_mask_gpio_interrupt(unsigned gpio)
+{
+ uint16_t bank = extract_bank(gpio);
+ uint16_t bit = extract_bit(gpio);
+
+ RMWREG32(GPIO_INT_DIS(bank), bit, 1, 1);
+}
+
+
+void zynq_gpio_init(void)
+{
+ register_int_handler(GPIO_INT, gpio_int_handler, NULL);
+ unmask_interrupt(GPIO_INT);
+
+ // Note(johngro):
+ //
+ // The Zynq 700 series documentation describes two bits which affect the
+ // input vs. output nature of the GPIOs (DIRM and OEN, or output enable).
+ // On the surface, the docs seem to indicate that they do the same thing,
+ // which is to enable or disable the output driver on the pin. The docs
+ // make it clear that the input function is always enabled, regardless of
+ // the settings of either the DIRM or OEN bits (input state is readable via
+ // the DATA_RO registers). Also, they state that the output drivers cannot
+ // be enabled if the TRISTATE bit is set on the pin in the SLCR unit.
+ //
+ // In practice, however, there seems to be a subtle, undocumented,
+ // difference between these bits (At least whent the GPIOs in question are
+ // MIOs, this behavior has not been verified on EMIOs).
+ //
+ // The OEN bit seems to do what you would want it to do (toggle the output
+ // drivers on and off). The desired drive state of the pin (reflected in
+ // the DATA and MASK_DATA registers) holds the user setting regardless of
+ // the state of the OEN bit, and the state of the line can be read via the
+ // DATA_RO bit.
+ //
+ // When the DIRM bit is cleared, however, the state of the line seems to
+ // become latched into the desired drive state of the pin instead of holding
+ // its last programmed state. For example; say the pin is being used to
+ // as a line in an open collector bus, such as i2c. The output drivers
+ // are enabled (OEN and DIRM == 1), and the pin is being driven low (DATA ==
+ // 0). When it comes time to release the line, if a user clears OEN, the
+ // driver will be disabled and the line will be pulled high by the external
+ // pullup. DATA will still read 0 (the desired drive state of the pin) and
+ // DATA_RO will read 1 (the actual state of the line). If, on the other
+ // hand, the user clears the DIRM bit instead of the OEN bit, the driver
+ // will be disabled and the line will be pulled high again, but this time
+ // DATA will latch the value of the line itself (DATA == 1, DATA_RO == 1).
+ //
+ // Because of this behvior, we NEVER use the DIRM bit to control the driver
+ // state of the pin. During init, set all pins to input mode by first
+ // clearing the OEN bits, and then making sure that the DIRM bits are all
+ // set.
+ for (unsigned int bank = 0; bank < 4; bank++) {
+ *REG32(GPIO_OEN(bank)) = 0x00000000;
+ *REG32(GPIO_DIRM(bank)) = 0xFFFFFFFF;
+ }
+}
+
+void zynq_gpio_early_init(void)
+{
+}
+
+void register_gpio_int_handler(unsigned gpio, int_handler handler, void *args)
+{
+ DEBUG_ASSERT(gpio < MAX_GPIO);
+ DEBUG_ASSERT(handler);
+
+ irq_callbacks[gpio].callback = handler;
+ irq_callbacks[gpio].args = args;
+}
+
+void unregister_gpio_int_handler(unsigned gpio)
+{
+ DEBUG_ASSERT(gpio < MAX_GPIO);
+
+ irq_callbacks[gpio].callback = NULL;
+ irq_callbacks[gpio].args = NULL;
+}
+
+int gpio_config(unsigned gpio, unsigned flags)
+{
+ DEBUG_ASSERT(gpio < MAX_GPIO);
+
+ uint16_t bank = extract_bank(gpio);
+ uint16_t bit = extract_bit(gpio);
+
+ /* MIO region, exclude EMIO. MIO needs to be configured before the GPIO block. */
+ if (bank < 2) {
+ // Preserve all of the fields of the current pin configuration, except
+ // for PULLUP and the MUX configurtaion. Force the mux config to select
+ // GPIO mode, and turn the pullup on or off as requested by the user.
+ uint32_t mio_cfg = SLCR_REG(MIO_PIN_00 + (gpio * 4));
+ mio_cfg &= MIO_TRI_ENABLE |
+ MIO_SPEED_FAST |
+ MIO_IO_TYPE_MASK |
+ MIO_DISABLE_RCVR;
+
+ // No need to set any bits in the L[0123]_SEL fields; we want them to
+ // all be zero.
+
+ if (flags & GPIO_PULLUP) {
+ mio_cfg |= MIO_PULLUP;
+ }
+
+ SLCR_REG(MIO_PIN_00 + (gpio * 4)) = mio_cfg;
+ }
+
+ if (flags & GPIO_OUTPUT || flags & GPIO_INPUT) {
+ if (flags & GPIO_OUTPUT && flags & GPIO_INPUT) {
+ printf("Cannot configure a gpio as both an input and output direction.\n");
+ return -1;
+ }
+
+ // Note(johngro): use only the OEN bit to control the output driver. Do
+ // not use the DIRM bit; see the note in zynq_gpio_init.
+ RMWREG32(GPIO_OEN(bank), bit, 1, ((flags & GPIO_OUTPUT) > 0));
+ }
+
+ if (flags & GPIO_EDGE || flags & GPIO_LEVEL) {
+ if (flags & GPIO_EDGE && flags & GPIO_LEVEL) {
+ printf("Cannot configure a gpio as both edge and level sensitive.\n");
+ return -1;
+ }
+ RMWREG32(GPIO_INT_TYPE(bank), bit, 1, ((flags & GPIO_EDGE) > 0));
+ }
+
+ if (flags & GPIO_RISING || flags & GPIO_FALLING) {
+ /* Zynq has a specific INT_ANY register for handling interrupts that trigger on both
+ * rising and falling edges, but it specifically must only be used in edge mode */
+ if (flags & GPIO_RISING && flags & GPIO_FALLING) {
+ if ((flags & GPIO_EDGE) == 0) {
+ printf("polarity must be rising or falling if level sensitivity is used.\n");
+ return -1;
+ }
+
+ RMWREG32(GPIO_INT_ANY(bank), bit, 1, 1);
+ } else {
+ RMWREG32(GPIO_INT_POLARITY(bank), bit, 1, ((flags & GPIO_RISING) > 0));
+ RMWREG32(GPIO_INT_ANY(bank), bit, 1, 0);
+ }
+ }
+
+ return 0;
+}
+
+void gpio_set(unsigned gpio, unsigned on)
+{
+ DEBUG_ASSERT(gpio < MAX_GPIO);
+
+ uint16_t bank = extract_bank(gpio);
+ uint16_t bit = extract_bit(gpio);
+ uintptr_t reg;
+
+ if (bit < 16) {
+ reg = GPIO_MASK_DATA_LSW(bank);
+ } else {
+ reg = GPIO_MASK_DATA_MSW(bank);
+ bit -= 16;
+ }
+
+ *REG32(reg) = (~(1 << bit) << 16) | (!!on << bit);
+}
+
+int gpio_get(unsigned gpio)
+{
+ DEBUG_ASSERT(gpio < MAX_GPIO);
+
+ uint16_t bank = extract_bank(gpio);
+ uint16_t bit = extract_bit(gpio);
+
+ return ((*REG32(GPIO_DATA_RO(bank)) & (1 << bit)) > 0);
+}
+
+#include <lib/console.h>
+#ifdef WITH_LIB_CONSOLE
+static int cmd_zynq_gpio(int argc, const cmd_args *argv)
+{
+ for (unsigned int bank = 0; bank < 4; bank++) {
+ printf("DIRM_%u (0x%08x): 0x%08x\n", bank, GPIO_DIRM(bank), *REG32(GPIO_DIRM(bank)));
+ printf("OEN_%u (0x%08x): 0x%08x\n", bank, GPIO_OEN(bank), *REG32(GPIO_OEN(bank)));
+ printf("MASK_DATA_LSW_%u (0x%08x): 0x%08x\n", bank, GPIO_MASK_DATA_LSW(bank), *REG32(GPIO_MASK_DATA_LSW(bank)));
+ printf("MASK_DATA_MSW_%u (0x%08x): 0x%08x\n", bank, GPIO_MASK_DATA_MSW(bank), *REG32(GPIO_MASK_DATA_MSW(bank)));
+ printf("DATA_%u (0x%08x): 0x%08x\n", bank, GPIO_DATA(bank), *REG32(GPIO_DATA(bank)));
+ printf("DATA_RO_%u (0x%08x): 0x%08x\n", bank, GPIO_DATA_RO(bank), *REG32(GPIO_DATA_RO(bank)));
+ printf("INT_MASK_%u (0x%08x): 0x%08x\n", bank, GPIO_INT_MASK(bank), *REG32(GPIO_INT_MASK(bank)));
+ printf("INT_STAT_%u (0x%08x): 0x%08x\n", bank, GPIO_INT_STAT(bank), *REG32(GPIO_INT_STAT(bank)));
+ printf("INT_TYPE_%u (0x%08x): 0x%08x\n", bank, GPIO_INT_TYPE(bank), *REG32(GPIO_INT_TYPE(bank)));
+ printf("INT_POLARITY_%u (0x%08x): 0x%08x\n", bank, GPIO_INT_POLARITY(bank), *REG32(GPIO_INT_POLARITY(bank)));
+ printf("INT_ANY_%u (0x%08x): 0x%08x\n", bank, GPIO_INT_ANY(bank), *REG32(GPIO_INT_ANY(bank)));
+ }
+ return 0;
+}
+STATIC_COMMAND_START
+#if LK_DEBUGLEVEL > 1
+STATIC_COMMAND("zynq_gpio", "Dump Zynq GPIO registers", &cmd_zynq_gpio)
+#endif
+STATIC_COMMAND_END(zynq_gpio);
+
+#endif
diff --git a/src/bsp/lk/platform/zynq/include/dev/cache/pl310_config.h b/src/bsp/lk/platform/zynq/include/dev/cache/pl310_config.h
new file mode 100644
index 0000000..124d6c5
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/include/dev/cache/pl310_config.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <platform/zynq.h>
+
+/* configuration for the PL310 L2 cache controller */
+#define PL310_BASE L2CACHE_BASE
+#define PL310_TAG_RAM_LATENCY ((1 << 8) | (1 << 4) | (1 << 0))
+#define PL310_DATA_RAM_LATENCY ((1 << 8) | (2 << 4) | (1 << 0))
+
diff --git a/src/bsp/lk/platform/zynq/include/dev/qspi.h b/src/bsp/lk/platform/zynq/include/dev/qspi.h
new file mode 100644
index 0000000..61c2487
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/include/dev/qspi.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+/* a highly Zynq specific qspi interface */
+
+struct qspi_ctxt {
+ uint32_t cfg;
+ uint32_t khz;
+ bool linear_mode;
+};
+
+int qspi_set_speed(struct qspi_ctxt *qspi, uint32_t khz);
+int qspi_init(struct qspi_ctxt *qspi, uint32_t khz);
+int qspi_enable_linear(struct qspi_ctxt *qspi);
+int qspi_disable_linear(struct qspi_ctxt *qspi);
+void qspi_rd(struct qspi_ctxt *qspi, uint32_t cmd, uint32_t asize, uint32_t *data, uint32_t count);
+void qspi_wr(struct qspi_ctxt *qspi, uint32_t cmd, uint32_t asize, uint32_t *data, uint32_t count);
+void qspi_wr1(struct qspi_ctxt *qspi, uint32_t cmd);
+void qspi_wr2(struct qspi_ctxt *qspi, uint32_t cmd);
+void qspi_wr3(struct qspi_ctxt *qspi, uint32_t cmd);
+uint32_t qspi_rd1(struct qspi_ctxt *qspi, uint32_t cmd);
+
+/* set 0 for chip select */
+void qspi_cs(struct qspi_ctxt *qspi, unsigned int cs);
+
diff --git a/src/bsp/lk/platform/zynq/include/dev/spiflash.h b/src/bsp/lk/platform/zynq/include/dev/spiflash.h
new file mode 100644
index 0000000..270702b
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/include/dev/spiflash.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <sys/types.h>
+
+status_t spiflash_detect(void);
diff --git a/src/bsp/lk/platform/zynq/include/platform/fpga.h b/src/bsp/lk/platform/zynq/include/platform/fpga.h
new file mode 100644
index 0000000..de2d7dc
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/include/platform/fpga.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2014 Brian Swetland
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#include <sys/types.h>
+#include <stdbool.h>
+
+status_t zynq_program_fpga(paddr_t physaddr, size_t length);
+bool zync_fpga_config_done(void);
+void zynq_reset_fpga(void);
+
diff --git a/src/bsp/lk/platform/zynq/include/platform/gem.h b/src/bsp/lk/platform/zynq/include/platform/gem.h
new file mode 100644
index 0000000..6f4d9d5
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/include/platform/gem.h
@@ -0,0 +1,236 @@
+#pragma once
+#include <platform/zynq.h>
+
+struct pktbuf;
+
+typedef void (*gem_cb_t)(struct pktbuf *p);
+status_t gem_init(uintptr_t regsbase);
+void gem_set_callback(gem_cb_t rx);
+void gem_set_macaddr(uint8_t mac[6]);
+int gem_send_raw_pkt(struct pktbuf *p);
+
+void gem_disable(void);
+
+struct gem_regs {
+ uint32_t net_ctrl;
+ uint32_t net_cfg;
+ uint32_t net_status;
+ uint32_t ___reserved1;
+ uint32_t dma_cfg;
+ uint32_t tx_status;
+ uint32_t rx_qbar;
+ uint32_t tx_qbar;
+ uint32_t rx_status;
+ uint32_t intr_status;
+ uint32_t intr_en;
+ uint32_t intr_dis;
+ uint32_t intr_mask;
+ uint32_t phy_maint;
+ uint32_t rx_pauseq;
+ uint32_t tx_pauseq;
+ uint32_t ___reserved2[16];
+ uint32_t hash_bot;
+ uint32_t hash_top;
+ uint32_t spec_addr1_bot;
+ uint32_t spec_addr1_top;
+ uint32_t spec_addr2_bot;
+ uint32_t spec_addr2_top;
+ uint32_t spec_addr3_bot;
+ uint32_t spec_addr3_top;
+ uint32_t spec_addr4_bot;
+ uint32_t spec_addr4_top;
+ uint32_t type_id_match1;
+ uint32_t type_id_match2;
+ uint32_t type_id_match3;
+ uint32_t type_id_match4;
+ uint32_t wake_on_lan;
+ uint32_t ipg_stretch;
+ uint32_t stacked_vlan;
+ uint32_t tx_pfc_pause;
+ uint32_t spec_addr1_mask_bot;
+ uint32_t spec_addr1_mask_top;
+ uint32_t ___reserved3[11];
+ uint32_t module_id;
+ uint32_t octets_tx_bot;
+ uint32_t octets_tx_top;
+ uint32_t frames_tx;
+ uint32_t broadcast_frames_tx;
+ uint32_t multi_frames_tx;
+ uint32_t pause_frames_tx;
+ uint32_t frames_64b_tx;
+ uint32_t frames_65to127b_tx;
+ uint32_t frames_128to255b_tx;
+ uint32_t frames_256to511b_tx;
+ uint32_t frames_512to1023b_tx;
+ uint32_t frames_1024to1518b_tx;
+ uint32_t ___reserved4;
+ uint32_t tx_under_runs;
+ uint32_t single_collisn_frames;
+ uint32_t multi_collisn_frames;
+ uint32_t excessive_collisns;
+ uint32_t late_collisns;
+ uint32_t deferred_tx_frames;
+ uint32_t carrier_sense_errs;
+ uint32_t octets_rx_bot;
+ uint32_t octets_rx_top;
+ uint32_t frames_rx;
+ uint32_t bdcast_fames_rx;
+ uint32_t multi_frames_rx;
+ uint32_t pause_rx;
+ uint32_t frames_64b_rx;
+ uint32_t frames_65to127b_rx;
+ uint32_t frames_128to255b_rx;
+ uint32_t frames_256to511b_rx;
+ uint32_t frames_512to1023b_rx;
+ uint32_t frames_1024to1518b_rx;
+ uint32_t ___reserved5;
+ uint32_t undersz_rx;
+ uint32_t oversz_rx;
+ uint32_t jab_rx;
+ uint32_t fcs_errors;
+ uint32_t length_field_errors;
+ uint32_t rx_symbol_errors;
+ uint32_t align_errors;
+ uint32_t rx_resource_errors;
+ uint32_t rx_overrun_errors;
+ uint32_t ip_hdr_csum_errors;
+ uint32_t tcp_csum_errors;
+ uint32_t udp_csum_errors;
+ uint32_t ___reserved6[7];
+ uint32_t timer_strobe_s;
+ uint32_t timer_strobe_ns;
+ uint32_t timer_s;
+ uint32_t timer_ns;
+ uint32_t timer_adjust;
+ uint32_t timer_incr;
+ uint32_t ptp_tx_s;
+ uint32_t ptp_tx_ns;
+ uint32_t ptp_rx_s;
+ uint32_t ptp_rx_ns;
+ uint32_t ptp_peer_tx_s;
+ uint32_t ptp_peer_tx_ns;
+ uint32_t ptp_peer_rx_s;
+ uint32_t ptp_peer_rx_ns;
+ uint32_t ___reserved7[22];
+ uint32_t design_cfg2;
+ uint32_t design_cfg3;
+ uint32_t design_cfg4;
+ uint32_t design_cfg5;
+};
+
+/* net_ctrl */
+#define NET_CTRL_LOOP_EN (1 << 1)
+#define NET_CTRL_RX_EN (1 << 2)
+#define NET_CTRL_TX_EN (1 << 3)
+#define NET_CTRL_MD_EN (1 << 4)
+#define NET_CTRL_STATCLR (1 << 5)
+#define NET_CTRL_STATINC (1 << 6)
+#define NET_CTRL_STATW_EN (1 << 7)
+#define NET_CTRL_BACK_PRESSURE (1 << 8)
+#define NET_CTRL_START_TX (1 << 9)
+#define NET_CTRL_HALT_TX (1 << 10)
+#define NET_CTRL_PAUSE_TX (1 << 11)
+#define NET_CTRL_ZERO_PAUSE_TX (1 << 12)
+#define NET_CTRL_STR_RX_TIMESTAMP (1 << 15)
+#define NET_CTRL_EN_PFC_PRI_PAUSE_RX (1 << 16)
+#define NET_CTRL_TX_PFC_PRI_PAUSE_FRAME (1 << 17)
+#define NET_CTRL_FLUSH_NEXT_RX_DPRAM_PKT (1 << 18)
+/* net_cfg */
+#define NET_CFG_SPEED_100 (1)
+#define NET_CFG_FULL_DUPLEX (1 << 1)
+#define NET_CFG_DISC_NON_VLAN (1 << 2)
+#define NET_CFG_COPY_ALL (1 << 4)
+#define NET_CFG_NO_BCAST (1 << 5)
+#define NET_CFG_MULTI_HASH_EN (1 << 6)
+#define NET_CFG_UNI_HASH_EN (1 << 7)
+#define NET_CFG_RX_1536_BYTE (1 << 8)
+#define NET_CFG_EXT_ADDR_MATCH_EN (1 << 9)
+#define NET_CFG_GIGE_EN (1 << 10)
+#define NET_CFG_PCS_SEL (1 << 11)
+#define NET_CFG_RETRY_TEST (1 << 12)
+#define NET_CFG_PAUSE_EN (1 << 13)
+#define NET_CFG_RX_BUF_OFFSET(x) (x << 14)
+#define NET_CFG_LEN_ERR_FRAME_DISC (1 << 16)
+#define NET_CFG_FCS_REMOVE (1 << 17)
+#define NET_CFG_MDC_CLK_DIV(x) (x << 18)
+#define NET_CFG_DBUS_WIDTH(x) (x << 21)
+#define NET_CFG_DIS_CP_PAUSE_FRAME (1 << 23)
+#define NET_CFG_RX_CHKSUM_OFFLD_EN (1 << 24)
+#define NET_CFG_RX_HD_WHILE_TX (1 << 25)
+#define NET_CFG_IGNORE_RX_FCS (1 << 26)
+#define NET_CFG_SGMII_EN (1 << 27)
+#define NET_CFG_IPG_STRETCH_EN (1 << 28)
+#define NET_CFG_RX_BAD_PREAMBLE (1 << 29)
+#define NET_CFG_IGNORE_IPG_RX_ER (1 << 30)
+#define NET_CFG_UNIDIR_EN (1 << 31)
+/* net_status */
+#define NET_STATUS_PFC_PRI_PAUSE_NEG (1 << 6)
+#define NET_STATUS_PCS_AUTONEG_PAUSE_TX_RES (1 << 5)
+#define NET_STATUS_PCS_AUTONEG_PAUSE_RX_RES (1 << 4)
+#define NET_STATUS_PCS_AUTONEG_DUP_RES (1 << 3)
+#define NET_STATUS_PHY_MGMT_IDLE (1 << 2)
+#define NET_STATUS_MDIO_IN_PIN_STATUS (1 << 1)
+#define NET_STATUS_PCS_LINK_STATE (1 << 0)
+/* dma_cfg */
+#define DMA_CFG_AHB_FIXED_BURST_LEN(x) (x)
+#define DMA_CFG_AHB_ENDIAN_SWP_MGM (1 << 6)
+#define DMA_CFG_AHB_ENDIAN_SWP_PKT_EN (1 << 7)
+#define DMA_CFG_RX_PKTBUF_MEMSZ_SEL(x) (x << 8)
+#define DMA_CFG_TX_PKTBUF_MEMSZ_SEL (1 << 10)
+#define DMA_CFG_CSUM_GEN_OFFLOAD_EN (1 << 11)
+#define DMA_CFG_AHB_MEM_RX_BUF_SIZE(x) (x << 16)
+#define DMA_CFG_DISC_WHEN_NO_AHB (1 << 24)
+
+/* tx descriptor */
+#define TX_BUF_LEN(x) (x & 0x3FFF)
+#define TX_LAST_BUF (1 << 15)
+#define TX_CHKSUM_GEN_ERR(x) ((x >> 20) & 0x7)
+#define TX_NO_CRC (1 << 16)
+#define TX_LATE_COLLISION (1 << 26)
+#define TX_RETRY_EXCEEDED (1 << 29)
+#define TX_DESC_WRAP (1 << 30)
+#define TX_DESC_USED (1 << 31)
+
+/* rx descriptor */
+#define RX_DESC_USED (1 << 0)
+#define RX_DESC_WRAP (1 << 1)
+#define RX_BUF_LEN(x) (x & 0x1FFF)
+#define RX_FCS_BAD_FCS (1 << 13)
+#define RX_START_OF_FRAME (1 << 14)
+#define RX_END_OF_FRAME (1 << 15)
+#define RX_CFI (1 << 16)
+#define RX_VLAN_PRIO(x) ((x >> 17) & 0x7)
+#define RX_PRIO_TAG (1 << 20)
+#define RX_VLAN_DETECT (1 << 21)
+#define RX_CHKSUM_MATCH(x) ((1 >> 22) & 0x3)
+#define RX_SNAP_ENCODED (1 << 24)
+#define RX_ADDR_REG_MATCH(x) ((1 >> 25) & 0x3)
+#define RX_SPECIFIC_ADDR_MATCH (1 << 27)
+#define RX_EXT_MATCH (1 << 28)
+#define RX_UNICAST_MATCH (1 << 30)
+#define RX_MULTICAST_MATCH (1 << 31)
+
+#define INTR_MGMT_SENT (1 << 0)
+#define INTR_RX_COMPLETE (1 << 1)
+#define INTR_RX_USED_READ (1 << 2)
+#define INTR_TX_USED_READ (1 << 3)
+#define INTR_RETRY_EX (1 << 5)
+#define INTR_TX_CORRUPT (1 << 6)
+#define INTR_TX_COMPLETE (1 << 7)
+#define INTR_RX_OVERRUN (1 << 10)
+#define INTR_HRESP_NOT_OK (1 << 11)
+
+#define TX_STATUS_USED_READ (1)
+#define TX_STATUS_COLLISION (1 << 1)
+#define TX_STATUS_RETRY_LIMIT (1 << 2)
+#define TX_STATUS_GO (1 << 3)
+#define TX_STATUS_CORR_AHB (1 << 4)
+#define TX_STATUS_COMPLETE (1 << 5)
+#define TX_STATUS_UNDER_RUN (1 << 6)
+#define TX_STATUS_LATE_COLLISION (1 << 7)
+#define TX_STATUS_HRESP_NOT_OK (1 << 8)
+
+#define RX_STATUS_BUFFER_NOT_AVAIL (1)
+#define RX_STATUS_FRAME_RECD (1 << 1)
+#define RX_STATUS_RX_OVERRUN (1 << 2)
+#define RX_STATUS_HRESP_NOT_OK (1 << 3)
diff --git a/src/bsp/lk/platform/zynq/include/platform/gic.h b/src/bsp/lk/platform/zynq/include/platform/gic.h
new file mode 100644
index 0000000..bacf7b81
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/include/platform/gic.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __PLATFORM_GIC_H
+#define __PLATFORM_GIC_H
+
+#include <platform/zynq.h>
+
+#define GICBASE(n) (CPUPRIV_BASE)
+#define GICC_OFFSET (0x0100)
+#define GICD_OFFSET (0x1000)
+
+#endif
diff --git a/src/bsp/lk/platform/zynq/include/platform/gpio.h b/src/bsp/lk/platform/zynq/include/platform/gpio.h
new file mode 100644
index 0000000..f962f3e
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/include/platform/gpio.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2015 Christopher Anderson
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <platform/interrupts.h>
+#include <platform/zynq.h>
+
+/* GPIO registers are not indexed in a particularly convenient manner, but can be calculated
+ * via the GPIO bank */
+
+#define GPIO_MASK_DATA_BASE (GPIO_BASE + 0x0)
+#define GPIO_MASK_DATA_LSW(bank) (GPIO_MASK_DATA_BASE + (8 * bank))
+#define GPIO_MASK_DATA_MSW(bank) (GPIO_MASK_DATA_BASE + 4 + (8 * bank))
+
+#define GPIO_DATA_BASE (GPIO_BASE + 0x40)
+#define GPIO_DATA(bank) (GPIO_DATA_BASE + (4 * bank))
+
+#define GPIO_DATA_RO_BASE (GPIO_BASE + 0x60)
+#define GPIO_DATA_RO(bank) (GPIO_DATA_RO_BASE + (4 * bank))
+
+#define GPIO_REGS(bank) (GPIO_BASE + 0x204 + (0x40 * bank))
+#define GPIO_DIRM(bank) (GPIO_REGS(bank) + 0x0)
+#define GPIO_OEN(bank) (GPIO_REGS(bank) + 0x4)
+#define GPIO_INT_MASK(bank) (GPIO_REGS(bank) + 0x8)
+#define GPIO_INT_EN(bank) (GPIO_REGS(bank) + 0xC)
+#define GPIO_INT_DIS(bank) (GPIO_REGS(bank) + 0x10)
+#define GPIO_INT_STAT(bank) (GPIO_REGS(bank) + 0x14)
+#define GPIO_INT_TYPE(bank) (GPIO_REGS(bank) + 0x18)
+#define GPIO_INT_POLARITY(bank) (GPIO_REGS(bank) + 0x1C)
+#define GPIO_INT_ANY(bank) (GPIO_REGS(bank) + 0x20)
+
+void zynq_unmask_gpio_interrupt(unsigned gpio);
+void zynq_mask_gpio_interrupt(unsigned gpio);
+void zynq_gpio_init(void);
+void zynq_gpio_early_init(void);
+void register_gpio_int_handler(unsigned gpio, int_handler handler, void *args);
+void unregister_gpio_int_handler(unsigned gpio);
diff --git a/src/bsp/lk/platform/zynq/include/platform/zynq.h b/src/bsp/lk/platform/zynq/include/platform/zynq.h
new file mode 100644
index 0000000..bd43433
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/include/platform/zynq.h
@@ -0,0 +1,589 @@
+/*
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#pragma once
+
+#define ZYNQ_MIO_CNT 54
+
+/* memory addresses */
+/* assumes sram is mapped at 0 the first MB of sdram is covered by it */
+#define SDRAM_BASE (0x00100000)
+#define SDRAM_APERTURE_SIZE (0x3ff00000)
+#define SRAM_BASE (0x0)
+#define SRAM_BASE_HIGH (0xfffc0000)
+#define SRAM_APERTURE_SIZE (0x00040000)
+#define SRAM_SIZE (0x00040000)
+
+/* hardware base addresses */
+#define UART0_BASE (0xe0000000)
+#define UART1_BASE (0xe0001000)
+#define USB0_BASE (0xe0002000)
+#define USB1_BASE (0xe0003000)
+#define I2C0_BASE (0xe0004000)
+#define I2C1_BASE (0xe0005000)
+#define SPI0_BASE (0xe0006000)
+#define SPI1_BASE (0xe0007000)
+#define CAN0_BASE (0xe0008000)
+#define CAN1_BASE (0xe0009000)
+#define GPIO_BASE (0xe000a000)
+#define GEM0_BASE (0xe000b000) // gigabit eth controller
+#define GEM1_BASE (0xe000c000) // ""
+#define QSPI_BASE (0xe000d000)
+#define SMCC_BASE (0xe000e000) // PL353 shared memory controller
+
+#define SD0_BASE (0xe0100000)
+#define SD1_BASE (0xe0101000)
+
+#define SLCR_BASE (0xf8000000)
+#define TTC0_BASE (0xf8001000)
+#define TTC1_BASE (0xf8002000)
+#define DMAC0_NS_BASE (0xf8004000)
+#define DMAC0_S_BASE (0xf8003000)
+#define SWDT_BASE (0xf8005000)
+
+#define CPUPRIV_BASE (0xf8f00000)
+#define SCU_CONTROL_BASE (CPUPRIV_BASE + 0x0000)
+#define GIC_PROC_BASE (CPUPRIV_BASE + 0x0100)
+#define GLOBAL_TIMER_BASE (CPUPRIV_BASE + 0x0200)
+#define PRIV_TIMER_BASE (CPUPRIV_BASE + 0x0600)
+#define GIC_DISTRIB_BASE (CPUPRIV_BASE + 0x1000)
+#define L2CACHE_BASE (CPUPRIV_BASE + 0x2000)
+
+#define QSPI_LINEAR_BASE (0xfc000000)
+
+/* interrupts */
+#define TTC0_A_INT 42
+#define TTC0_B_INT 43
+#define TTC0_C_INT 44
+#define GPIO_INT 52
+#define USB0_INT 53
+#define ETH0_INT 54
+#define ETH0_WAKE_INT 55
+#define SDIO0_INT 56
+#define I2C0_INT 57
+#define SPI0_INT 58
+#define UART0_INT 59
+#define UART1_INT 82
+#define TTC1_A_INT 69
+#define TTC2_B_INT 70
+#define TTC3_C_INT 71
+#define ETH1_INT 77
+#define ETH1_WAKE_INT 78
+
+/* Perhipheral IRQs from fabric */
+#define F2P0_IRQ 61
+#define F2P1_IRQ 62
+#define F2P2_IRQ 63
+#define F2P3_IRQ 64
+#define F2P4_IRQ 65
+#define F2P5_IRQ 66
+#define F2P6_IRQ 67
+#define F2P7_IRQ 68
+
+#define F2P8_IRQ 84
+#define F2P9_IRQ 85
+#define F2P10_IRQ 86
+#define F2P11_IRQ 87
+#define F2P12_IRQ 88
+#define F2P13_IRQ 89
+#define F2P14_IRQ 90
+#define F2P15_IRQ 91
+
+#define MAX_INT 96
+
+#ifndef ASSEMBLY
+
+#include <reg.h>
+#include <compiler.h>
+#include <bits.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+/* Configuration values for each of the system PLLs. Refer to the TRM 25.10.4 */
+typedef struct {
+ uint32_t lock_cnt;
+ uint32_t cp;
+ uint32_t res;
+ uint32_t fdiv;
+} zynq_pll_cfg_t;
+
+typedef struct {
+ uint32_t arm_clk;
+ uint32_t ddr_clk;
+ uint32_t dci_clk;
+ uint32_t gem0_clk;
+ uint32_t gem0_rclk;
+ uint32_t gem1_clk;
+ uint32_t gem1_rclk;
+ uint32_t smc_clk;
+ uint32_t lqspi_clk;
+ uint32_t sdio_clk;
+ uint32_t uart_clk;
+ uint32_t spi_clk;
+ uint32_t can_clk;
+ uint32_t can_mioclk;
+ uint32_t usb0_clk;
+ uint32_t usb1_clk;
+ uint32_t pcap_clk;
+ uint32_t fpga0_clk;
+ uint32_t fpga1_clk;
+ uint32_t fpga2_clk;
+ uint32_t fpga3_clk;
+ uint32_t aper_clk;
+ uint32_t clk_621_true;
+} zynq_clk_cfg_t;
+
+typedef struct {
+ zynq_pll_cfg_t arm;
+ zynq_pll_cfg_t ddr;
+ zynq_pll_cfg_t io;
+} zynq_pll_cfg_tree_t;
+
+/* Configuration for the DDR controller and buffers. TRM Ch 10 */
+typedef struct {
+ uint32_t addr0;
+ uint32_t addr1;
+ uint32_t data0;
+ uint32_t data1;
+ uint32_t diff0;
+ uint32_t diff1;
+ bool ibuf_disable;
+ bool term_disable;
+} zynq_ddriob_cfg_t;;
+
+/* SLCR registers */
+struct slcr_regs {
+ uint32_t SCL; // Secure Configuration Lock
+ uint32_t SLCR_LOCK; // SLCR Write Protection Lock
+ uint32_t SLCR_UNLOCK; // SLCR Write Protection Unlock
+ uint32_t SLCR_LOCKSTA; // SLCR Write Protection Status
+ uint32_t ___reserved0[60];
+ uint32_t ARM_PLL_CTRL; // ARM PLL Control
+ uint32_t DDR_PLL_CTRL; // DDR PLL Control
+ uint32_t IO_PLL_CTRL; // IO PLL Control
+ uint32_t PLL_STATUS; // PLL Status
+ uint32_t ARM_PLL_CFG; // ARM PLL Configuration
+ uint32_t DDR_PLL_CFG; // DDR PLL Configuration
+ uint32_t IO_PLL_CFG; // IO PLL Configuration
+ uint32_t ___reserved1[1];
+ uint32_t ARM_CLK_CTRL; // CPU Clock Control
+ uint32_t DDR_CLK_CTRL; // DDR Clock Control
+ uint32_t DCI_CLK_CTRL; // DCI clock control
+ uint32_t APER_CLK_CTRL; // AMBA Peripheral Clock Control
+ uint32_t USB0_CLK_CTRL; // USB 0 ULPI Clock Control
+ uint32_t USB1_CLK_CTRL; // USB 1 ULPI Clock Control
+ uint32_t GEM0_RCLK_CTRL; // GigE 0 Rx Clock and Rx Signals Select
+ uint32_t GEM1_RCLK_CTRL; // GigE 1 Rx Clock and Rx Signals Select
+ uint32_t GEM0_CLK_CTRL; // GigE 0 Ref Clock Control
+ uint32_t GEM1_CLK_CTRL; // GigE 1 Ref Clock Control
+ uint32_t SMC_CLK_CTRL; // SMC Ref Clock Control
+ uint32_t LQSPI_CLK_CTRL; // Quad SPI Ref Clock Control
+ uint32_t SDIO_CLK_CTRL; // SDIO Ref Clock Control
+ uint32_t UART_CLK_CTRL; // UART Ref Clock Control
+ uint32_t SPI_CLK_CTRL; // SPI Ref Clock Control
+ uint32_t CAN_CLK_CTRL; // CAN Ref Clock Control
+ uint32_t CAN_MIOCLK_CTRL; // CAN MIO Clock Control
+ uint32_t DBG_CLK_CTRL; // SoC Debug Clock Control
+ uint32_t PCAP_CLK_CTRL; // PCAP Clock Control
+ uint32_t TOPSW_CLK_CTRL; // Central Interconnect Clock Control
+ uint32_t FPGA0_CLK_CTRL; // PL Clock 0 Output control
+ uint32_t FPGA0_THR_CTRL; // PL Clock 0 Throttle control
+ uint32_t FPGA0_THR_CNT; // PL Clock 0 Throttle Count control
+ uint32_t FPGA0_THR_STA; // PL Clock 0 Throttle Status read
+ uint32_t FPGA1_CLK_CTRL; // PL Clock 1 Output control
+ uint32_t FPGA1_THR_CTRL; // PL Clock 1 Throttle control
+ uint32_t FPGA1_THR_CNT; // PL Clock 1 Throttle Count
+ uint32_t FPGA1_THR_STA; // PL Clock 1 Throttle Status control
+ uint32_t FPGA2_CLK_CTRL; // PL Clock 2 output control
+ uint32_t FPGA2_THR_CTRL; // PL Clock 2 Throttle Control
+ uint32_t FPGA2_THR_CNT; // PL Clock 2 Throttle Count
+ uint32_t FPGA2_THR_STA; // PL Clock 2 Throttle Status
+ uint32_t FPGA3_CLK_CTRL; // PL Clock 3 output control
+ uint32_t FPGA3_THR_CTRL; // PL Clock 3 Throttle Control
+ uint32_t FPGA3_THR_CNT; // PL Clock 3 Throttle Count
+ uint32_t FPGA3_THR_STA; // PL Clock 3 Throttle Status
+ uint32_t ___reserved2[5];
+ uint32_t CLK_621_TRUE; // CPU Clock Ratio Mode select
+ uint32_t ___reserved3[14];
+ uint32_t PSS_RST_CTRL; // PS Software Reset Control
+ uint32_t DDR_RST_CTRL; // DDR Software Reset Control
+ uint32_t TOPSW_RST_CTRL; // Central Interconnect Reset Control
+ uint32_t DMAC_RST_CTRL; // DMAC Software Reset Control
+ uint32_t USB_RST_CTRL; // USB Software Reset Control
+ uint32_t GEM_RST_CTRL; // Gigabit Ethernet SW Reset Control
+ uint32_t SDIO_RST_CTRL; // SDIO Software Reset Control
+ uint32_t SPI_RST_CTRL; // SPI Software Reset Control
+ uint32_t CAN_RST_CTRL; // CAN Software Reset Control
+ uint32_t I2C_RST_CTRL; // I2C Software Reset Control
+ uint32_t UART_RST_CTRL; // UART Software Reset Control
+ uint32_t GPIO_RST_CTRL; // GPIO Software Reset Control
+ uint32_t LQSPI_RST_CTRL; // Quad SPI Software Reset Control
+ uint32_t SMC_RST_CTRL; // SMC Software Reset Control
+ uint32_t OCM_RST_CTRL; // OCM Software Reset Control
+ uint32_t ___reserved4[1];
+ uint32_t FPGA_RST_CTRL; // FPGA Software Reset Control
+ uint32_t A9_CPU_RST_CTRL; // CPU Reset and Clock control
+ uint32_t ___reserved5[1];
+ uint32_t RS_AWDT_CTRL; // Watchdog Timer Reset Control
+ uint32_t ___reserved6[2];
+ uint32_t REBOOT_STATUS; // Reboot Status, persistent
+ uint32_t BOOT_MODE; // Boot Mode Strapping Pins
+ uint32_t ___reserved7[40];
+ uint32_t APU_CTRL; // APU Control
+ uint32_t WDT_CLK_SEL; // SWDT clock source select
+ uint32_t ___reserved8[78];
+ uint32_t TZ_DMA_NS; // DMAC TrustZone Config
+ uint32_t TZ_DMA_IRQ_NS; // DMAC TrustZone Config for Interrupts
+ uint32_t TZ_DMA_PERIPH_NS; // DMAC TrustZone Config for Peripherals
+ uint32_t ___reserved9[57];
+ uint32_t PSS_IDCODE; // PS IDCODE
+ uint32_t ___reserved10[51];
+ uint32_t DDR_URGENT; // DDR Urgent Control
+ uint32_t ___reserved11[2];
+ uint32_t DDR_CAL_START; // DDR Calibration Start Triggers
+ uint32_t ___reserved12[1];
+ uint32_t DDR_REF_START; // DDR Refresh Start Triggers
+ uint32_t DDR_CMD_STA; // DDR Command Store Status
+ uint32_t DDR_URGENT_SEL; // DDR Urgent Select
+ uint32_t DDR_DFI_STATUS; // DDR DFI status
+ uint32_t ___reserved13[55];
+ uint32_t MIO_PIN_00; // MIO Pin 0 Control
+ uint32_t MIO_PIN_01; // MIO Pin 1 Control
+ uint32_t MIO_PIN_02; // MIO Pin 2 Control
+ uint32_t MIO_PIN_03; // MIO Pin 3 Control
+ uint32_t MIO_PIN_04; // MIO Pin 4 Control
+ uint32_t MIO_PIN_05; // MIO Pin 5 Control
+ uint32_t MIO_PIN_06; // MIO Pin 6 Control
+ uint32_t MIO_PIN_07; // MIO Pin 7 Control
+ uint32_t MIO_PIN_08; // MIO Pin 8 Control
+ uint32_t MIO_PIN_09; // MIO Pin 9 Control
+ uint32_t MIO_PIN_10; // MIO Pin 10 Control
+ uint32_t MIO_PIN_11; // MIO Pin 11 Control
+ uint32_t MIO_PIN_12; // MIO Pin 12 Control
+ uint32_t MIO_PIN_13; // MIO Pin 13 Control
+ uint32_t MIO_PIN_14; // MIO Pin 14 Control
+ uint32_t MIO_PIN_15; // MIO Pin 15 Control
+ uint32_t MIO_PIN_16; // MIO Pin 16 Control
+ uint32_t MIO_PIN_17; // MIO Pin 17 Control
+ uint32_t MIO_PIN_18; // MIO Pin 18 Control
+ uint32_t MIO_PIN_19; // MIO Pin 19 Control
+ uint32_t MIO_PIN_20; // MIO Pin 20 Control
+ uint32_t MIO_PIN_21; // MIO Pin 21 Control
+ uint32_t MIO_PIN_22; // MIO Pin 22 Control
+ uint32_t MIO_PIN_23; // MIO Pin 23 Control
+ uint32_t MIO_PIN_24; // MIO Pin 24 Control
+ uint32_t MIO_PIN_25; // MIO Pin 25 Control
+ uint32_t MIO_PIN_26; // MIO Pin 26 Control
+ uint32_t MIO_PIN_27; // MIO Pin 27 Control
+ uint32_t MIO_PIN_28; // MIO Pin 28 Control
+ uint32_t MIO_PIN_29; // MIO Pin 29 Control
+ uint32_t MIO_PIN_30; // MIO Pin 30 Control
+ uint32_t MIO_PIN_31; // MIO Pin 31 Control
+ uint32_t MIO_PIN_32; // MIO Pin 32 Control
+ uint32_t MIO_PIN_33; // MIO Pin 33 Control
+ uint32_t MIO_PIN_34; // MIO Pin 34 Control
+ uint32_t MIO_PIN_35; // MIO Pin 35 Control
+ uint32_t MIO_PIN_36; // MIO Pin 36 Control
+ uint32_t MIO_PIN_37; // MIO Pin 37 Control
+ uint32_t MIO_PIN_38; // MIO Pin 38 Control
+ uint32_t MIO_PIN_39; // MIO Pin 39 Control
+ uint32_t MIO_PIN_40; // MIO Pin 40 Control
+ uint32_t MIO_PIN_41; // MIO Pin 41 Control
+ uint32_t MIO_PIN_42; // MIO Pin 42 Control
+ uint32_t MIO_PIN_43; // MIO Pin 43 Control
+ uint32_t MIO_PIN_44; // MIO Pin 44 Control
+ uint32_t MIO_PIN_45; // MIO Pin 45 Control
+ uint32_t MIO_PIN_46; // MIO Pin 46 Control
+ uint32_t MIO_PIN_47; // MIO Pin 47 Control
+ uint32_t MIO_PIN_48; // MIO Pin 48 Control
+ uint32_t MIO_PIN_49; // MIO Pin 49 Control
+ uint32_t MIO_PIN_50; // MIO Pin 50 Control
+ uint32_t MIO_PIN_51; // MIO Pin 51 Control
+ uint32_t MIO_PIN_52; // MIO Pin 52 Control
+ uint32_t MIO_PIN_53; // MIO Pin 53 Control
+ uint32_t ___reserved14[11];
+ uint32_t MIO_LOOPBACK; // Loopback function within MIO
+ uint32_t ___reserved15[1];
+ uint32_t MIO_MST_TRI0; // MIO pin Tri-state Enables, 31:0
+ uint32_t MIO_MST_TRI1; // MIO pin Tri-state Enables, 53:32
+ uint32_t ___reserved16[7];
+ uint32_t SD0_WP_CD_SEL; // SDIO 0 WP CD select
+ uint32_t SD1_WP_CD_SEL; // SDIO 1 WP CD select
+ uint32_t ___reserved17[50];
+ uint32_t LVL_SHFTR_EN; // Level Shifters Enable
+ uint32_t ___reserved18[3];
+ uint32_t OCM_CFG; // OCM Address Mapping
+ uint32_t ___reserved19[66];
+ uint32_t RESERVED; // Reserved
+ uint32_t ___reserved20[56];
+ uint32_t GPIOB_CTRL; // PS IO Buffer Control
+ uint32_t GPIOB_CFG_CMOS18; // MIO GPIOB CMOS 1.8V config
+ uint32_t GPIOB_CFG_CMOS25; // MIO GPIOB CMOS 2.5V config
+ uint32_t GPIOB_CFG_CMOS33; // MIO GPIOB CMOS 3.3V config
+ uint32_t ___reserved21[1];
+ uint32_t GPIOB_CFG_HSTL; // MIO GPIOB HSTL config
+ uint32_t GPIOB_DRVR_BIAS_CTRL; // MIO GPIOB Driver Bias Control
+ uint32_t ___reserved22[9];
+ uint32_t DDRIOB_ADDR0; // DDR IOB Config for A[14:0], CKE and DRST_B
+ uint32_t DDRIOB_ADDR1; // DDR IOB Config for BA[2:0], ODT, CS_B, WE_B, RAS_B and CAS_B
+ uint32_t DDRIOB_DATA0; // DDR IOB Config for Data 15:0
+ uint32_t DDRIOB_DATA1; // DDR IOB Config for Data 31:16
+ uint32_t DDRIOB_DIFF0; // DDR IOB Config for DQS 1:0
+ uint32_t DDRIOB_DIFF1; // DDR IOB Config for DQS 3:2
+ uint32_t DDRIOB_CLOCK; // DDR IOB Config for Clock Output
+ uint32_t DDRIOB_DRIVE_SLEW_ADDR; // Drive and Slew controls for Address and Command pins of the DDR Interface
+ uint32_t DDRIOB_DRIVE_SLEW_DATA; // Drive and Slew controls for DQ pins of the DDR Interface
+ uint32_t DDRIOB_DRIVE_SLEW_DIFF; // Drive and Slew controls for DQS pins of the DDR Interface
+ uint32_t DDRIOB_DRIVE_SLEW_CLOCK; // Drive and Slew controls for Clock pins of the DDR Interface
+ uint32_t DDRIOB_DDR_CTRL; // DDR IOB Buffer Control
+ uint32_t DDRIOB_DCI_CTRL; // DDR IOB DCI Config
+ uint32_t DDRIOB_DCI_STATUS; // DDR IO Buffer DCI Status
+};
+
+/* Verify the entries match the TRM offset to validate the struct */
+STATIC_ASSERT(offsetof(struct slcr_regs, SCL) == 0x0);
+STATIC_ASSERT(offsetof(struct slcr_regs, DDRIOB_DCI_STATUS) == 0xb74);
+
+#define DDRC_CTRL 0xF8006000
+#define DDRC_MODE_STATUS 0xF8006054
+
+#define DDRC_CTRL_OUT_OF_RESET (1)
+#define DDRC_CTRL_BUS_WIDTH_16BIT (1 << 2)
+#define DDRC_CTRL_RDRW_IDLE_GAP(x) ((x & BIT_MASK(7) << 7)
+
+#define DDRC_STS_OPER_MODE(x) (x & BIT_MASK(3))
+#define DDRC_STS_SELF_REFRESH DDRC_STS_OPER_MODE(0x3)
+
+#define SLCR ((volatile struct slcr_regs *)SLCR_BASE)
+#define SLCR_REG(reg) (*REG32((uintptr_t)&SLCR->reg))
+
+/* ARM_PLL_CFG */
+#define PLL_CFG_PLL_RES(x) ((x & BIT_MASK(4)) << 4)
+#define PLL_CFG_PLL_CP(x) ((x & BIT_MASK(4)) << 8)
+#define PLL_CFG_LOCK_CNT(x) ((x & BIT_MASK(10)) << 12)
+
+/* DDR_PLL_CFG */
+
+/* ARM_PLL_CTRL and IO_PLL_CTRL */
+#define PLL_RESET (1)
+#define PLL_PWRDOWN (1 << 1)
+#define PLL_BYPASS_QUAL (1 << 3)
+#define PLL_BYPASS_FORCE (1 << 4)
+#define PLL_FDIV(x) ((x & BIT_MASK(7)) << 12)
+
+/* ARM_CLK_CTRL */
+#define ARM_CLK_CTRL_SRCSEL(x) ((x & BIT_MASK(2)) << 4)
+#define ARM_CLK_CTRL_DIVISOR(x) ((x & BIT_MASK(6)) << 8)
+#define ARM_CLK_CTRL_CPU_6OR4XCLKACT (1 << 24)
+#define ARM_CLK_CTRL_CPU_3OR2XCLKACT (1 << 25)
+#define ARM_CLK_CTRL_CPU_2XCLKACT (1 << 26)
+#define ARM_CLK_CTRL_CPU_1XCLKACT (1 << 27)
+#define ARM_CLK_CTRL_PERI_CLKACT (1 << 28)
+
+/* DDR_CLK_CTRL */
+#define DDR_CLK_CTRL_DDR_3XCLKACT (1)
+#define DDR_CLK_CTRL_DDR_2XCLKACT (1 << 1)
+#define DDR_CLK_CTRL_DDR_3XCLK_DIV(x) ((x & BIT_MASK(6)) << 20)
+#define DDR_CLK_CTRL_DDR_2XCLK_DIV(x) ((x & BIT_MASK(6)) << 26)
+
+/* PLL_STATUS */
+#define PLL_STATUS_ARM_PLL_LOCK (1)
+#define PLL_STATUS_DDR_PLL_LOCK (1 << 1)
+#define PLL_STATUS_IO_PLL_LOCK (1 << 2)
+#define PLL_STATUS_ARM_PLL_STABLE (1 << 3)
+#define PLL_STATUS_DDR_PLL_STABLE (1 << 4)
+#define PLL_STATUS_IO_PLL_STABLE (1 << 5)
+
+/* Generic clock control */
+#define CLK_CTRL_CLKACT (1)
+#define CLK_CTRL_CLKACT1 (1 << 1)
+#define CLK_CTRL_SRCSEL(x) ((x & BIT_MASK(2)) << 4)
+#define CLK_CTRL_DIVISOR0(x) ((x & BIT_MASK(6)) << 8)
+#define CLK_CTRL_DIVISOR1(x) ((x & BIT_MASK(6)) << 20)
+
+/* GEM clock control */
+#define GEM_CLK_CTRL_SRCSEL(x) ((x & BIT_MASK(3)) << 4)
+
+/* CLK 621 just has a single enable bit */
+#define CLK_621_ENABLE (1)
+
+/* AMBA Peripheral Clock Control */
+#define SMC_CPU_CLK_EN (1 << 24)
+#define LQSPI_CPU_CLK_EN (1 << 23)
+#define GPIO_CPU_CLK_EN (1 << 22)
+#define UART1_CPU_CLK_EN (1 << 21)
+#define UART0_CPU_CLK_EN (1 << 20)
+#define I2C1_CPU_CLK_EN (1 << 19)
+#define I2C0_CPU_CLK_EN (1 << 18)
+#define CAN1_CPU_CLK_EN (1 << 17)
+#define CAN0_CPU_CLK_EN (1 << 16)
+#define SPI1_CPU_CLK_EN (1 << 15)
+#define SPI0_CPU_CLK_EN (1 << 14)
+#define SDI1_CPU_CLK_EN (1 << 11)
+#define SDI0_CPU_CLK_EN (1 << 10)
+#define GEM1_CPU_CLK_EN (1 << 7)
+#define GEM0_CPU_CLK_EN (1 << 6)
+#define USB1_CPU_CLK_EN (1 << 3)
+#define USB0_CPU_CLK_EN (1 << 2)
+#define DMA_CPU_CLK_EN (1 << 0)
+
+/* GPIOB_CTRL */
+#define GPIOB_CTRL_VREF_09_EN (1 << 4)
+#define GPIOB_CTRL_VREF_EN (1)
+
+/* DDRIOB_ADDR */
+#define DDRIOB_PULLUP_EN (1 << 11)
+#define DDRIOB_OUTPUT_EN(x) ((x & BIT_MASK(2)) << 9)
+#define DDRIOB_TERM_DISABLE_MODE (1 << 8)
+#define DDRIOB_IBUF_DISABLE_MODE (1 << 7)
+#define DDRIOB_DCI_TYPE(x) ((x & BIT_MASK(2)) << 5)
+#define DDRIOB_TERM_EN (1 << 4)
+#define DDRIOB_DCI_UPDATE_B (1 << 3)
+#define DDRIOB_INP_TYPE(x) ((x & BIT_MASK(2)) << 1)
+
+/* SD1_WP_CD_SEL */
+#define SDIO0_WP_SEL(x) (x & BIT_MASK(6))
+#define SDIO0_CD_SEL(x) ((x & BIT_MASK(6)) << 16)
+
+/* MIO pin configuration */
+#define MIO_TRI_ENABLE (1)
+#define MIO_L0_SEL (1 << 1)
+#define MIO_L1_SEL (1 << 2)
+#define MIO_L2_SEL(x) ((x & BIT_MASK(2)) << 3)
+#define MIO_L2_SEL_MASK MIO_L2_SEL(0x3)
+#define MIO_L3_SEL(x) ((x & BIT_MASK(3)) << 5)
+#define MIO_L3_SEL_MASK MIO_L3_SEL(0x7)
+#define MIO_SPEED_FAST (1 << 8)
+#define MIO_IO_TYPE_LVCMOS18 (0x1 << 9)
+#define MIO_IO_TYPE_LVCMOS25 (0x2 << 9)
+#define MIO_IO_TYPE_LVCMOS33 (0x3 << 9)
+#define MIO_IO_TYPE_HSTL (0x4 << 9)
+#define MIO_IO_TYPE_MASK (0x7 << 9)
+#define MIO_PULLUP (1 << 12)
+#define MIO_DISABLE_RCVR (1 << 13)
+#define MIO_DEFAULT (0xFFFF0000)
+
+/* UART registers */
+#define UART_CR (0x00)
+#define UART_MR (0x04)
+#define UART_IER (0x08)
+#define UART_IDR (0x0c)
+#define UART_IMR (0x10)
+#define UART_ISR (0x14)
+#define UART_BAUDGEN (0x18)
+#define UART_RXTOUT (0x1c)
+#define UART_RXWM (0x20)
+#define UART_MODEMCR (0x24)
+#define UART_MODEMSR (0x28)
+#define UART_SR (0x2c)
+#define UART_FIFO (0x30)
+#define UART_BAUD_DIV (0x34)
+#define UART_FLOW_DELAY (0x38)
+#define UART_TX_FIFO_TRIGGER (0x44)
+
+#define NUM_UARTS 2
+
+#define UART_CR_RXRES (1)
+#define UART_CR_TXRES (1 << 1)
+#define UART_CR_RXEN (1 << 2)
+#define UART_CR_RXDIS (1 << 3)
+#define UART_CR_TXEN (1 << 4)
+#define UART_CR_TXDIS (1 << 5)
+#define UART_CR_RSTTO (1 << 6)
+#define UART_CR_STTBRK (1 << 7)
+#define UART_CR_STPBRK (1 << 8)
+
+#define UART_MR_CLKS_DIV8 (1)
+#define UART_MR_CHRL(x) ((x & BIT_MASK(2)) << 1)
+#define UART_MR_PAR(x) ((x & BIT_MASK(3)) << 3)
+#define UART_MR_NBSTOP(x) ((x & BIT_MASK(2)) << 6)
+#define UART_MR_CHMODE(x) ((x & BIT_MASK(2)) << 8)
+
+#define UART_BRG_DIV(x) (x & BIT_MASK(16))
+#define UART_BRD_DIV(x) (x & BIT_MASK(8))
+
+/* system watchdog timer */
+struct swdt_regs {
+ uint32_t MODE;
+ uint32_t CONTROL;
+ uint32_t RESTART;
+ uint32_t STATUS;
+};
+
+#define SWDT ((volatile struct swdt_regs *)SWDT_BASE)
+#define SWDT_REG(reg) (*REG32((uintptr_t)&SWDT->reg))
+
+/* zynq specific functions */
+static inline void zynq_slcr_unlock(void) { SLCR->SLCR_UNLOCK = 0xdf0d; }
+static inline void zynq_slcr_lock(void) { SLCR->SLCR_LOCK = 0x767b; }
+
+uint32_t zynq_get_arm_freq(void);
+uint32_t zynq_get_arm_timer_freq(void);
+uint32_t zynq_get_swdt_freq(void);
+void zynq_dump_clocks(void);
+
+enum zynq_clock_source {
+ PLL_IO = 0,
+ PLL_CPU = 2,
+ PLL_DDR = 3,
+};
+
+enum zynq_periph {
+ PERIPH_USB0,
+ PERIPH_USB1,
+ PERIPH_GEM0,
+ PERIPH_GEM1,
+ PERIPH_SMC,
+ PERIPH_LQSPI,
+ PERIPH_SDIO0,
+ PERIPH_SDIO1,
+ PERIPH_UART0,
+ PERIPH_UART1,
+ PERIPH_SPI0,
+ PERIPH_SPI1,
+ PERIPH_CAN0,
+ PERIPH_CAN1,
+ PERIPH_DBG,
+ PERIPH_PCAP,
+ PERIPH_FPGA0,
+ PERIPH_FPGA1,
+ PERIPH_FPGA2,
+ PERIPH_FPGA3,
+
+ _PERIPH_MAX,
+};
+
+status_t zynq_set_clock(enum zynq_periph, bool enable, enum zynq_clock_source, uint32_t divisor, uint32_t divisor2);
+uint32_t zynq_get_clock(enum zynq_periph);
+
+/* boot mode */
+#define ZYNQ_BOOT_MODE_JTAG (0)
+#define ZYNQ_BOOT_MODE_QSPI (1)
+#define ZYNQ_BOOT_MODE_NOR (2)
+#define ZYNQ_BOOT_MODE_NAND (4)
+#define ZYNQ_BOOT_MODE_SD (5)
+#define ZYNQ_BOOT_MODE_MASK (0x7) /* only interested in BOOT_MODE[2:0] */
+
+static inline uint32_t zynq_get_boot_mode(void) { return SLCR->BOOT_MODE & ZYNQ_BOOT_MODE_MASK; }
+
+#endif // !ASSEMBLY
+
diff --git a/src/bsp/lk/platform/zynq/mkbootheader.py b/src/bsp/lk/platform/zynq/mkbootheader.py
new file mode 100755
index 0000000..5178d08
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/mkbootheader.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+
+# generates Zynq bootrom header from input payout
+
+import sys, os, array
+
+if len(sys.argv) < 3:
+ print "not enough args, usage:"
+ print "%s <binfile> <outfile>" % sys.argv[0]
+ sys.exit(1)
+
+fin = open(sys.argv[1], "r+b")
+finsize = os.stat(sys.argv[1]).st_size
+fout = open(sys.argv[2], "w+b")
+
+header = array.array('I')
+
+# start generating header
+# from section 6.3.2 of Zynq-700 AP SoC Technical Reference Manual (v1.7)
+
+# vector table (8 words)
+for _ in range(0, 8):
+ header.append(0)
+
+# (0x20) width detection
+header.append(0xaa995566)
+
+# (0x24) identification 'XLNX'
+header.append(0x584c4e58)
+
+# (0x28) encryption status (not encrypted)
+header.append(0)
+
+# (0x2c) user defined
+header.append(0)
+
+# (0x30) source offset
+header.append(0x8c0)
+
+# (0x34) length of image
+header.append(finsize)
+
+# (0x38) reserved
+header.append(0)
+
+# (0x3c) start of execution (0)
+header.append(0)
+
+# (0x40) total image length (same as length of image for non secure)
+header.append(finsize)
+
+# (0x44) reserved
+header.append(0)
+
+# (0x48) header checksum
+sum = 0
+for i in header:
+ sum += i
+sum = ~sum
+header.append(sum & 0xffffffff)
+
+# user defined
+for _ in range(0x4c, 0xa0, 4):
+ header.append(0)
+
+# register init pairs (all ffs to cause it to skip)
+for _ in range(0xa0, 0x8a0, 4):
+ header.append(0xffffffff)
+
+# reserved
+for _ in range(0x8a0, 0x8c0, 4):
+ header.append(0)
+
+fout.write(header)
+
+# copy the input into the output
+while True:
+ buf = fin.read(1024)
+ if not buf:
+ break
+ fout.write(buf)
+
+fin.close()
+fout.close()
diff --git a/src/bsp/lk/platform/zynq/platform.c b/src/bsp/lk/platform/zynq/platform.c
new file mode 100644
index 0000000..91d16e9
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/platform.c
@@ -0,0 +1,528 @@
+/*
+ * Copyright (c) 2012-2015 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <err.h>
+#include <debug.h>
+#include <stdio.h>
+#include <string.h>
+#include <arch/arm/mmu.h>
+#include <kernel/vm.h>
+#include <dev/uart.h>
+#include <dev/interrupt/arm_gic.h>
+#include <dev/timer/arm_cortex_a9.h>
+#include <lib/console.h>
+#include <lib/watchdog.h>
+#include <platform.h>
+#include <platform/zynq.h>
+#include <platform/gem.h>
+#include <platform/timer.h>
+#include "platform_p.h"
+
+#if ZYNQ_SDRAM_INIT
+STATIC_ASSERT(SDRAM_SIZE != 0);
+#endif
+
+/* default timeout of the global hardware watchdog */
+#ifndef ZYNQ_WATCHDOG_TIMEOUT
+#define ZYNQ_WATCHDOG_TIMEOUT (1000) // 1 second
+#endif
+
+/* saved REBOOT_STATUS register */
+static uint32_t saved_reboot_status;
+
+/* target can specify this as the initial jam table to set up the soc */
+__WEAK void ps7_init(void) { }
+
+/* These should be defined in the target somewhere */
+extern const uint32_t zynq_mio_cfg[ZYNQ_MIO_CNT];
+extern const long zynq_ddr_cfg[];
+extern const uint32_t zynq_ddr_cfg_cnt;
+extern const zynq_pll_cfg_tree_t zynq_pll_cfg;
+extern const zynq_clk_cfg_t zynq_clk_cfg;
+extern const zynq_ddriob_cfg_t zynq_ddriob_cfg;
+
+static inline int reg_poll(uint32_t addr,uint32_t mask)
+{
+ uint32_t iters = UINT_MAX;
+ while (iters-- && !(*REG32(addr) & mask)) ;
+
+ if (iters) {
+ return 0;
+ }
+
+ return -1;
+}
+
+/* For each PLL we need to configure the cp / res / lock_cnt and then place the PLL in bypass
+ * before doing a reset to switch to the new values. Then bypass is removed to switch back to using
+ * the PLL once its locked.
+ */
+int zynq_pll_init(void) {
+ const zynq_pll_cfg_tree_t *cfg = &zynq_pll_cfg;
+
+ SLCR_REG(ARM_PLL_CFG) = PLL_CFG_LOCK_CNT(cfg->arm.lock_cnt) | PLL_CFG_PLL_CP(cfg->arm.cp) |
+ PLL_CFG_PLL_RES(cfg->arm.res);
+ SLCR_REG(ARM_PLL_CTRL) = PLL_FDIV(cfg->arm.fdiv) | PLL_BYPASS_FORCE | PLL_RESET;
+ SLCR_REG(ARM_PLL_CTRL) &= ~PLL_RESET;
+
+ if (reg_poll((uintptr_t)&SLCR->PLL_STATUS, PLL_STATUS_ARM_PLL_LOCK) == -1) {
+ return -1;
+ }
+
+ SLCR_REG(ARM_PLL_CTRL) &= ~PLL_BYPASS_FORCE;
+ SLCR_REG(ARM_CLK_CTRL) = zynq_clk_cfg.arm_clk;
+
+#if ZYNQ_SDRAM_INIT
+ SLCR_REG(DDR_PLL_CFG) = PLL_CFG_LOCK_CNT(cfg->ddr.lock_cnt) | PLL_CFG_PLL_CP(cfg->ddr.cp) |
+ PLL_CFG_PLL_RES(cfg->ddr.res);
+ SLCR_REG(DDR_PLL_CTRL) = PLL_FDIV(cfg->ddr.fdiv) | PLL_BYPASS_FORCE | PLL_RESET;
+ SLCR_REG(DDR_PLL_CTRL) &= ~PLL_RESET;
+
+ if (reg_poll((uintptr_t)&SLCR->PLL_STATUS, PLL_STATUS_DDR_PLL_LOCK) == -1) {
+ return -1;
+ }
+
+ SLCR_REG(DDR_PLL_CTRL) &= ~PLL_BYPASS_FORCE;
+ SLCR_REG(DDR_CLK_CTRL) = zynq_clk_cfg.ddr_clk;
+#elif SDRAM_SIZE == 0
+ /* if we're not using sdram and haven't been told to initialize sdram, stop the DDR pll */
+ SLCR_REG(DDR_CLK_CTRL) = 0;
+ SLCR_REG(DDR_PLL_CTRL) |= PLL_PWRDOWN;
+#endif
+ SLCR_REG(IO_PLL_CFG) = PLL_CFG_LOCK_CNT(cfg->io.lock_cnt) | PLL_CFG_PLL_CP(cfg->io.cp) |
+ PLL_CFG_PLL_RES(cfg->io.res);
+ SLCR_REG(IO_PLL_CTRL) = PLL_FDIV(cfg->io.fdiv) | PLL_BYPASS_FORCE | PLL_RESET;
+ SLCR_REG(IO_PLL_CTRL) &= ~PLL_RESET;
+
+ if (reg_poll((uintptr_t)&SLCR->PLL_STATUS, PLL_STATUS_IO_PLL_LOCK) == -1) {
+ return -1;
+ }
+
+ SLCR_REG(IO_PLL_CTRL) &= ~PLL_BYPASS_FORCE;
+ return 0;
+}
+
+int zynq_mio_init(void)
+{
+
+ /* This DDRIOB configuration applies to both zybo and uzed, but it's possible
+ * it may not work for all boards in the future. Just something to keep in mind
+ * with different memory configurations.
+ */
+ SLCR_REG(GPIOB_CTRL) = GPIOB_CTRL_VREF_EN;
+
+ for (size_t pin = 0; pin < countof(zynq_mio_cfg); pin++) {
+ if (zynq_mio_cfg[pin] != MIO_DEFAULT) {
+ SLCR_REG(MIO_PIN_00 + (pin * 4)) = zynq_mio_cfg[pin];
+ }
+ }
+
+ SLCR_REG(SD0_WP_CD_SEL) = SDIO0_WP_SEL(0x37) | SDIO0_CD_SEL(0x2F);
+
+ return 0;
+}
+
+void zynq_clk_init(void)
+{
+ SLCR_REG(DCI_CLK_CTRL) = zynq_clk_cfg.dci_clk;
+ SLCR_REG(GEM0_CLK_CTRL) = zynq_clk_cfg.gem0_clk;
+ SLCR_REG(GEM0_RCLK_CTRL) = zynq_clk_cfg.gem0_rclk;
+ SLCR_REG(GEM1_CLK_CTRL) = zynq_clk_cfg.gem1_clk;
+ SLCR_REG(GEM1_RCLK_CTRL) = zynq_clk_cfg.gem1_rclk;
+ SLCR_REG(SMC_CLK_CTRL) = zynq_clk_cfg.smc_clk;
+ SLCR_REG(LQSPI_CLK_CTRL) = zynq_clk_cfg.lqspi_clk;
+ SLCR_REG(SDIO_CLK_CTRL) = zynq_clk_cfg.sdio_clk;
+ SLCR_REG(UART_CLK_CTRL) = zynq_clk_cfg.uart_clk;
+ SLCR_REG(SPI_CLK_CTRL) = zynq_clk_cfg.spi_clk;
+ SLCR_REG(CAN_CLK_CTRL) = zynq_clk_cfg.can_clk;
+ SLCR_REG(CAN_MIOCLK_CTRL)= zynq_clk_cfg.can_mioclk;
+ SLCR_REG(USB0_CLK_CTRL) = zynq_clk_cfg.usb0_clk;
+ SLCR_REG(USB1_CLK_CTRL) = zynq_clk_cfg.usb1_clk;
+ SLCR_REG(PCAP_CLK_CTRL) = zynq_clk_cfg.pcap_clk;
+ SLCR_REG(FPGA0_CLK_CTRL) = zynq_clk_cfg.fpga0_clk;
+ SLCR_REG(FPGA1_CLK_CTRL) = zynq_clk_cfg.fpga1_clk;
+ SLCR_REG(FPGA2_CLK_CTRL) = zynq_clk_cfg.fpga2_clk;
+ SLCR_REG(FPGA3_CLK_CTRL) = zynq_clk_cfg.fpga3_clk;
+ SLCR_REG(APER_CLK_CTRL) = zynq_clk_cfg.aper_clk;
+ SLCR_REG(CLK_621_TRUE) = zynq_clk_cfg.clk_621_true;
+}
+
+#if ZYNQ_SDRAM_INIT
+void zynq_ddr_init(void)
+{
+ SLCR_REG(DDRIOB_ADDR0) = zynq_ddriob_cfg.addr0;
+ SLCR_REG(DDRIOB_ADDR1) = zynq_ddriob_cfg.addr1;
+ SLCR_REG(DDRIOB_DATA0) = zynq_ddriob_cfg.data0;
+ SLCR_REG(DDRIOB_DATA1) = zynq_ddriob_cfg.data1;
+ SLCR_REG(DDRIOB_DIFF0) = zynq_ddriob_cfg.diff0;
+ SLCR_REG(DDRIOB_DIFF1) = zynq_ddriob_cfg.diff1;
+ SLCR_REG(DDRIOB_CLOCK) = DDRIOB_OUTPUT_EN(0x3);
+
+ /* These register fields are not documented in the TRM. These
+ * values represent the defaults generated via the Zynq tools
+ */
+ SLCR_REG(DDRIOB_DRIVE_SLEW_ADDR) = 0x0018C61CU;
+ SLCR_REG(DDRIOB_DRIVE_SLEW_DATA) = 0x00F9861CU;
+ SLCR_REG(DDRIOB_DRIVE_SLEW_DIFF) = 0x00F9861CU;
+ SLCR_REG(DDRIOB_DRIVE_SLEW_CLOCK) = 0x00F9861CU;
+ SLCR_REG(DDRIOB_DDR_CTRL) = 0x00000E60U;
+ SLCR_REG(DDRIOB_DCI_CTRL) = 0x00000001U;
+ SLCR_REG(DDRIOB_DCI_CTRL) |= 0x00000020U;
+ SLCR_REG(DDRIOB_DCI_CTRL) |= 0x00000823U;
+
+ /* Write addresss / value pairs from target table */
+ for (size_t i = 0; i < zynq_ddr_cfg_cnt; i += 2) {
+ *REG32(zynq_ddr_cfg[i]) = zynq_ddr_cfg[i+1];
+ }
+
+ /* Wait for DCI done */
+ reg_poll((uintptr_t)&SLCR->DDRIOB_DCI_STATUS, 0x2000);
+
+ /* Bring ddr out of reset and wait until self refresh */
+ *REG32(DDRC_CTRL) |= DDRC_CTRL_OUT_OF_RESET;
+ reg_poll(DDRC_MODE_STATUS, DDRC_STS_SELF_REFRESH);
+
+ /* Switch timer to 64k */
+ *REG32(0XF8007000) = *REG32(0xF8007000) & ~0x20000000U;
+
+ if (zynq_ddriob_cfg.ibuf_disable) {
+ SLCR_REG(DDRIOB_DATA0) |= DDRIOB_IBUF_DISABLE_MODE;
+ SLCR_REG(DDRIOB_DATA1) |= DDRIOB_IBUF_DISABLE_MODE;
+ SLCR_REG(DDRIOB_DIFF0) |= DDRIOB_IBUF_DISABLE_MODE;
+ SLCR_REG(DDRIOB_DIFF1) |= DDRIOB_IBUF_DISABLE_MODE;
+ }
+
+ if (zynq_ddriob_cfg.term_disable) {
+ SLCR_REG(DDRIOB_DATA0) |= DDRIOB_TERM_DISABLE_MODE;
+ SLCR_REG(DDRIOB_DATA1) |= DDRIOB_TERM_DISABLE_MODE;
+ SLCR_REG(DDRIOB_DIFF0) |= DDRIOB_TERM_DISABLE_MODE;
+ SLCR_REG(DDRIOB_DIFF1) |= DDRIOB_TERM_DISABLE_MODE;
+ }
+}
+#endif
+
+STATIC_ASSERT(IS_ALIGNED(SDRAM_BASE, MB));
+STATIC_ASSERT(IS_ALIGNED(SDRAM_SIZE, MB));
+
+#if SDRAM_SIZE != 0
+/* if we have sdram, the first 1MB is covered by sram */
+#define RAM_SIZE (MB + (SDRAM_SIZE - MB))
+#else
+#define RAM_SIZE (MB)
+#endif
+
+/* initial memory mappings. parsed by start.S */
+struct mmu_initial_mapping mmu_initial_mappings[] = {
+ /* 1GB of sram + sdram space */
+ { .phys = SRAM_BASE,
+ .virt = KERNEL_BASE,
+ .size = RAM_SIZE,
+ .flags = 0,
+ .name = "memory" },
+
+ /* AXI fpga fabric bus 0 */
+ { .phys = 0x40000000,
+ .virt = 0x40000000,
+ .size = (128*1024*1024),
+ .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE,
+ .name = "axi0" },
+
+ /* AXI fpga fabric bus 1 */
+ { .phys = 0x80000000,
+ .virt = 0x80000000,
+ .size = (16*1024*1024),
+ .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE,
+ .name = "axi1" },
+ /* 0xe0000000 hardware devices */
+ { .phys = 0xe0000000,
+ .virt = 0xe0000000,
+ .size = 0x00300000,
+ .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE,
+ .name = "hw-e0000000" },
+
+ /* 0xe1000000 hardware devices */
+ { .phys = 0xe1000000,
+ .virt = 0xe1000000,
+ .size = 0x05000000,
+ .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE,
+ .name = "hw-e1000000" },
+
+ /* 0xf8000000 hardware devices */
+ { .phys = 0xf8000000,
+ .virt = 0xf8000000,
+ .size = 0x01000000,
+ .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE,
+ .name = "hw-f8000000" },
+
+ /* 0xfc000000 hardware devices */
+ { .phys = 0xfc000000,
+ .virt = 0xfc000000,
+ .size = 0x02000000,
+ .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE,
+ .name = "hw-fc000000" },
+
+ /* sram high aperture */
+ { .phys = 0xfff00000,
+ .virt = 0xfff00000,
+ .size = 0x00100000,
+ .flags = MMU_INITIAL_MAPPING_FLAG_DEVICE },
+
+ /* identity map to let the boot code run */
+ { .phys = SRAM_BASE,
+ .virt = SRAM_BASE,
+ .size = RAM_SIZE,
+ .flags = MMU_INITIAL_MAPPING_TEMPORARY },
+
+ /* null entry to terminate the list */
+ { 0 }
+};
+
+#if SDRAM_SIZE != 0
+static pmm_arena_t sdram_arena = {
+ .name = "sdram",
+ .base = SDRAM_BASE,
+ .size = SDRAM_SIZE - MB, /* first 1MB is covered by SRAM */
+ .flags = PMM_ARENA_FLAG_KMAP
+};
+#endif
+
+static pmm_arena_t sram_arena = {
+ .name = "sram",
+ .base = SRAM_BASE,
+ .size = SRAM_SIZE,
+ .priority = 1,
+ .flags = PMM_ARENA_FLAG_KMAP
+};
+
+void platform_init_mmu_mappings(void)
+{
+}
+
+void platform_early_init(void)
+{
+#if 0
+ ps7_init();
+#else
+ /* Unlock the registers and leave them that way */
+ zynq_slcr_unlock();
+ zynq_mio_init();
+ zynq_pll_init();
+ zynq_clk_init();
+#if ZYNQ_SDRAM_INIT
+ zynq_ddr_init();
+#endif
+#endif
+
+ /* Enable all level shifters */
+ SLCR_REG(LVL_SHFTR_EN) = 0xF;
+ /* FPGA SW reset (not documented, but mandatory) */
+ SLCR_REG(FPGA_RST_CTRL) = 0x0;
+
+ /* zynq manual says this is mandatory for cache init */
+ *REG32(SLCR_BASE + 0xa1c) = 0x020202;
+
+ /* save the reboot status register, clear bits we dont want to save */
+ saved_reboot_status = SLCR->REBOOT_STATUS;
+ SLCR->REBOOT_STATUS &= ~(0xff << 16);
+
+ /* early initialize the uart so we can printf */
+ uart_init_early();
+
+ /* initialize the interrupt controller */
+ arm_gic_init();
+ zynq_gpio_init();
+
+ /* initialize the timer block */
+ arm_cortex_a9_timer_init(CPUPRIV_BASE, zynq_get_arm_timer_freq());
+
+ /* initialize the hardware watchdog */
+ watchdog_hw_init(ZYNQ_WATCHDOG_TIMEOUT);
+
+ /* bump the 2nd cpu into our code space and remap the top SRAM block */
+ if (KERNEL_LOAD_OFFSET != 0) {
+ /* construct a trampoline to get the 2nd cpu up to the trap routine */
+
+ /* figure out the offset of the trampoline routine in physical space from address 0 */
+ extern void platform_reset(void);
+ addr_t tramp = (addr_t)&platform_reset;
+ tramp -= KERNEL_BASE;
+ tramp += MEMBASE;
+
+ /* stuff in a ldr pc, [nextaddrress], and a target address */
+ uint32_t *ptr = (uint32_t *)KERNEL_BASE;
+
+ ptr[0] = 0xe51ff004; // ldr pc, [pc, #-4]
+ ptr[1] = tramp;
+ arch_clean_invalidate_cache_range((addr_t)ptr, 8);
+ }
+
+ /* reset the 2nd cpu, letting it go through its reset vector (at 0x0 physical) */
+ SLCR_REG(A9_CPU_RST_CTRL) |= (1<<1); // reset cpu 1
+ spin(10);
+ SLCR_REG(A9_CPU_RST_CTRL) &= ~(1<<1); // unreset cpu 1
+
+ /* wait for the 2nd cpu to reset, go through the usual reset vector, and get trapped by our code */
+ /* see platform/zynq/reset.S */
+ extern volatile int __cpu_trapped;
+ uint count = 100000;
+ while (--count) {
+ arch_clean_invalidate_cache_range((addr_t)&__cpu_trapped, sizeof(__cpu_trapped));
+ if (__cpu_trapped != 0)
+ break;
+ }
+ if (count == 0) {
+ panic("ZYNQ: failed to trap 2nd cpu\n");
+ }
+
+ /* bounce the 4th sram region down to lower address */
+ SLCR_REG(OCM_CFG) &= ~0xf; /* all banks at low address */
+
+ /* add the main memory arena */
+#if !ZYNQ_CODE_IN_SDRAM && SDRAM_SIZE != 0
+ /* In the case of running from SRAM, and we are using SDRAM,
+ * there is a discontinuity between the end of SRAM (256K) and the start of SDRAM (1MB),
+ * so intentionally bump the boot-time allocator to start in the base of SDRAM.
+ */
+ extern uintptr_t boot_alloc_start;
+ extern uintptr_t boot_alloc_end;
+
+ boot_alloc_start = KERNEL_BASE + MB;
+ boot_alloc_end = KERNEL_BASE + MB;
+#endif
+
+#if SDRAM_SIZE != 0
+ pmm_add_arena(&sdram_arena);
+#endif
+ pmm_add_arena(&sram_arena);
+}
+
+void platform_init(void)
+{
+ uart_init();
+
+ /* enable if we want to see some hardware boot status */
+#if LK_DEBUGLEVEL > 0
+ printf("zynq boot status:\n");
+ printf("\tREBOOT_STATUS 0x%x\n", saved_reboot_status);
+ if (BIT(saved_reboot_status, 16)) printf("\t\tSWDT_RST\n");
+ if (BIT(saved_reboot_status, 17)) printf("\t\tAWDT0_RST\n");
+ if (BIT(saved_reboot_status, 18)) printf("\t\tAWDT1_RST\n");
+ if (BIT(saved_reboot_status, 19)) printf("\t\tSLC_RST\n");
+ if (BIT(saved_reboot_status, 20)) printf("\t\tDBG_RST\n");
+ if (BIT(saved_reboot_status, 21)) printf("\t\tSRST_B\n");
+ if (BIT(saved_reboot_status, 22)) printf("\t\tPOR\n");
+ printf("\tREBOOT_STATE 0x%lx\n", BITS_SHIFT(saved_reboot_status, 31, 24));
+ printf("\tboot mode 0x%x\n", zynq_get_boot_mode());
+#endif
+}
+
+void platform_quiesce(void)
+{
+#if ZYNQ_WITH_GEM_ETH
+ gem_disable();
+#endif
+
+ platform_stop_timer();
+
+ /* stop the 2nd cpu and hold in reset */
+ SLCR_REG(A9_CPU_RST_CTRL) |= (1<<1); // reset cpu 1
+}
+
+/* called from lkboot to see if we want to abort autobooting.
+ * having the BOOT_MODE pins set to JTAG should cause us to hang out in
+ * whatever binary is loaded at the time.
+ */
+bool platform_abort_autoboot(void)
+{
+ /* test BOOT_MODE pins to see if we want to skip the autoboot stuff */
+ uint32_t boot_mode = zynq_get_boot_mode();
+ if (boot_mode == ZYNQ_BOOT_MODE_JTAG) {
+ printf("ZYNQ: disabling autoboot due to JTAG/QSPI jumper being set to JTAG\n");
+ return true;
+ }
+
+ return false;
+}
+
+#if WITH_LIB_CONSOLE
+static int cmd_zynq(int argc, const cmd_args *argv)
+{
+ if (argc < 2) {
+notenoughargs:
+ printf("not enough arguments\n");
+usage:
+ printf("usage: %s <command>\n", argv[0].str);
+ printf("\tslcr lock\n");
+ printf("\tslcr unlock\n");
+ printf("\tslcr lockstatus\n");
+ printf("\tmio\n");
+ printf("\tclocks\n");
+ printf("\ttrip_watchdog\n");
+ return -1;
+ }
+
+ if (!strcmp(argv[1].str, "slcr")) {
+ if (argc < 3) goto notenoughargs;
+
+ bool print_lock_status = false;
+ if (!strcmp(argv[2].str, "lock")) {
+ zynq_slcr_lock();
+ print_lock_status = true;
+ } else if (!strcmp(argv[2].str, "unlock")) {
+ zynq_slcr_unlock();
+ print_lock_status = true;
+ } else if (print_lock_status || !strcmp(argv[2].str, "lockstatus")) {
+ printf("%s\n", (SLCR->SLCR_LOCKSTA & 0x1) ? "locked" : "unlocked");
+ } else {
+ goto usage;
+ }
+ } else if (!strcmp(argv[1].str, "mio")) {
+ printf("zynq mio:\n");
+ for (size_t i = 0; i < ZYNQ_MIO_CNT; i++) {
+ printf("\t%02u: 0x%08x", i, *REG32((uintptr_t)&SLCR->MIO_PIN_00 + (i * 4)));
+ if (i % 4 == 3 || i == 53) {
+ putchar('\n');
+ }
+ }
+ } else if (!strcmp(argv[1].str, "clocks")) {
+ zynq_dump_clocks();
+ } else if (!strcmp(argv[1].str, "trip_watchdog")) {
+ /* try to trip the watchdog by disabling interrupts for a while */
+ arch_disable_ints();
+ for (int i = 0; i < 20; i++) {
+ spin(250000);
+ printf("SWDT MODE 0x%x CONTROL 0x%x STATUS 0x%x\n", SWDT->MODE, SWDT->CONTROL, SWDT->STATUS);
+ }
+ arch_enable_ints();
+ } else {
+ goto usage;
+ }
+
+ return 0;
+}
+
+STATIC_COMMAND_START
+#if LK_DEBUGLEVEL > 1
+STATIC_COMMAND("zynq", "zynq configuration commands", &cmd_zynq)
+#endif
+STATIC_COMMAND_END(zynq);
+#endif // WITH_LIB_CONSOLE
diff --git a/src/bsp/lk/platform/zynq/platform_p.h b/src/bsp/lk/platform/zynq/platform_p.h
new file mode 100644
index 0000000..f91316a
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/platform_p.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2012 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef __PLATFORM_P_H
+#define __PLATFORM_P_H
+
+void platform_init_interrupts(void);
+void platform_init_timer(uint32_t freq);
+void zynq_gpio_init(void);
+
+#endif
+
diff --git a/src/bsp/lk/platform/zynq/qspi.c b/src/bsp/lk/platform/zynq/qspi.c
new file mode 100644
index 0000000..c04884f
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/qspi.c
@@ -0,0 +1,383 @@
+/*
+ * Copyright (c) 2014 Brian Swetland
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <dev/qspi.h>
+
+#include <debug.h>
+#include <assert.h>
+#include <compiler.h>
+#include <printf.h>
+#include <string.h>
+#include <reg.h>
+
+#include <lib/console.h>
+
+#include <platform/zynq.h>
+
+#define QSPI_CONFIG 0xE000D000
+#define CFG_IFMODE (1 << 31) // Inteligent Flash Mode
+#define CFG_LITTLE_ENDIAN (0 << 26)
+#define CFG_BIG_ENDIAN (1 << 26)
+#define CFG_HOLDB_DR (1 << 19) // set to 1 for dual/quad spi mode
+#define CFG_NO_MODIFY_MASK (1 << 17) // do not modify this bit
+#define CFG_MANUAL_START (1 << 16) // start transaction
+#define CFG_MANUAL_START_EN (1 << 15) // enable manual start mode
+#define CFG_MANUAL_CS_EN (1 << 14) // enable manual CS control
+#define CFG_MANUAL_CS (1 << 10) // directly drives n_ss_out if MANUAL_CS_EN==1
+#define CFG_FIFO_WIDTH_32 (3 << 6) // only valid setting
+#define CFG_BAUD_MASK (7 << 3)
+#define CFG_BAUD_DIV_2 (0 << 3)
+#define CFG_BAUD_DIV_4 (1 << 3)
+#define CFG_BAUD_DIV_8 (2 << 3)
+#define CFG_BAUD_DIV_16 (3 << 3)
+#define CFG_CPHA (1 << 2) // clock phase
+#define CFG_CPOL (1 << 1) // clock polarity
+#define CFG_MASTER_MODE (1 << 0) // only valid setting
+
+#define QSPI_IRQ_STATUS 0xE000D004 // ro status (write UNDERFLOW/OVERFLOW to clear)
+#define QSPI_IRQ_ENABLE 0xE000D008 // write 1s to set mask bits
+#define QSPI_IRQ_DISABLE 0xE000D00C // write 1s to clear mask bits
+#define QSPI_IRQ_MASK 0xE000D010 // ro mask value (1 = irq enabled)
+#define TX_UNDERFLOW (1 << 6)
+#define RX_FIFO_FULL (1 << 5)
+#define RX_FIFO_NOT_EMPTY (1 << 4)
+#define TX_FIFO_FULL (1 << 3)
+#define TX_FIFO_NOT_FULL (1 << 2)
+#define RX_OVERFLOW (1 << 0)
+
+#define QSPI_ENABLE 0xE000D014 // write 1 to enable
+
+#define QSPI_DELAY 0xE000D018
+#define QSPI_TXD0 0xE000D01C
+#define QSPI_RXDATA 0xE000D020
+#define QSPI_SLAVE_IDLE_COUNT 0xE000D024
+#define QSPI_TX_THRESHOLD 0xE000D028
+#define QSPI_RX_THRESHOLD 0xE000D02C
+#define QSPI_GPIO 0xE000D030
+#define QSPI_LPBK_DLY_ADJ 0xE000D038
+#define QSPI_TXD1 0xE000D080
+#define QSPI_TXD2 0xE000D084
+#define QSPI_TXD3 0xE000D088
+
+#define QSPI_LINEAR_CONFIG 0xE000D0A0
+#define LCFG_ENABLE (1 << 31) // enable linear quad spi mode
+#define LCFG_TWO_MEM (1 << 30)
+#define LCFG_SEP_BUS (1 << 29) // 0=shared 1=separate
+#define LCFG_U_PAGE (1 << 28)
+#define LCFG_MODE_EN (1 << 25) // send mode bits (required for dual/quad io)
+#define LCFG_MODE_ON (1 << 24) // only send instruction code for first read
+#define LCFG_MODE_BITS(n) (((n) & 0xFF) << 16)
+#define LCFG_DUMMY_BYTES(n) (((n) & 7) << 8)
+#define LCFG_INST_CODE(n) ((n) & 0xFF)
+
+#define QSPI_LINEAR_STATUS 0xE000D0A4
+#define QSPI_MODULE_ID 0xE000D0FC
+
+int qspi_set_speed(struct qspi_ctxt *qspi, uint32_t khz)
+{
+ uint32_t n;
+
+ if (khz >= 100000) {
+ n = CFG_BAUD_DIV_2;
+ khz = 100000;
+ } else if (khz >= 50000) {
+ n = CFG_BAUD_DIV_4;
+ khz = 50000;
+ } else if (khz >= 25000) {
+ n = CFG_BAUD_DIV_8;
+ khz = 25000;
+ } else {
+ return -1;
+ }
+
+ if (khz == qspi->khz)
+ return 0;
+
+ qspi->khz = khz;
+
+ writel(0, QSPI_ENABLE);
+ if (n == CFG_BAUD_DIV_2) {
+ writel(0x20, QSPI_LPBK_DLY_ADJ);
+ } else {
+ writel(0, QSPI_LPBK_DLY_ADJ);
+ }
+
+ qspi->cfg &= ~CFG_BAUD_MASK;
+ qspi->cfg |= n;
+
+ writel(qspi->cfg, QSPI_CONFIG);
+ writel(1, QSPI_ENABLE);
+
+ return 0;
+}
+
+int qspi_init(struct qspi_ctxt *qspi, uint32_t khz)
+{
+ writel(0, QSPI_ENABLE);
+ writel(0, QSPI_LINEAR_CONFIG);
+
+ // flush rx fifo
+ while (readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY)
+ readl(QSPI_RXDATA);
+
+ qspi->cfg = (readl(QSPI_CONFIG) & CFG_NO_MODIFY_MASK) |
+ CFG_IFMODE |
+ CFG_HOLDB_DR |
+ CFG_FIFO_WIDTH_32 |
+ CFG_CPHA | CFG_CPOL |
+ CFG_MASTER_MODE |
+ CFG_BAUD_DIV_2 |
+ CFG_MANUAL_START_EN | CFG_MANUAL_CS_EN | CFG_MANUAL_CS;
+
+ writel(qspi->cfg, QSPI_CONFIG);
+ qspi->khz = 100000;
+ qspi->linear_mode = false;
+
+ writel(1, QSPI_ENABLE);
+
+ // clear sticky irqs
+ writel(TX_UNDERFLOW | RX_OVERFLOW, QSPI_IRQ_STATUS);
+
+ return 0;
+}
+
+int qspi_enable_linear(struct qspi_ctxt *qspi)
+{
+ if (qspi->linear_mode)
+ return 0;
+
+ /* disable the controller */
+ writel(0, QSPI_ENABLE);
+ writel(0, QSPI_LINEAR_CONFIG);
+
+ /* put the controller in auto chip select mode and assert chip select */
+ qspi->cfg &= ~(CFG_MANUAL_START_EN | CFG_MANUAL_CS_EN | CFG_MANUAL_CS);
+ writel(qspi->cfg, QSPI_CONFIG);
+
+#if 1
+ // uses Quad I/O mode
+ // should be 0x82FF02EB according to xilinx manual for spansion flashes
+ writel(LCFG_ENABLE |
+ LCFG_MODE_EN |
+ LCFG_MODE_BITS(0xff) |
+ LCFG_DUMMY_BYTES(2) |
+ LCFG_INST_CODE(0xeb),
+ QSPI_LINEAR_CONFIG);
+#else
+ // uses Quad Output Read mode
+ // should be 0x8000016B according to xilinx manual for spansion flashes
+ writel(LCFG_ENABLE |
+ LCFG_MODE_BITS(0) |
+ LCFG_DUMMY_BYTES(1) |
+ LCFG_INST_CODE(0x6b),
+ QSPI_LINEAR_CONFIG);
+#endif
+
+ /* enable the controller */
+ writel(1, QSPI_ENABLE);
+
+ qspi->linear_mode = true;
+
+ DSB;
+
+ return 0;
+}
+
+int qspi_disable_linear(struct qspi_ctxt *qspi)
+{
+ if (!qspi->linear_mode)
+ return 0;
+
+ /* disable the controller */
+ writel(0, QSPI_ENABLE);
+ writel(0, QSPI_LINEAR_CONFIG);
+
+ /* put the controller back into manual chip select mode */
+ qspi->cfg |= (CFG_MANUAL_START_EN | CFG_MANUAL_CS_EN | CFG_MANUAL_CS);
+ writel(qspi->cfg, QSPI_CONFIG);
+
+ /* enable the controller */
+ writel(1, QSPI_ENABLE);
+
+ qspi->linear_mode = false;
+
+ DSB;
+
+ return 0;
+}
+
+void qspi_cs(struct qspi_ctxt *qspi, unsigned int cs)
+{
+ DEBUG_ASSERT(cs <= 1);
+
+ if (cs == 0)
+ qspi->cfg &= ~(CFG_MANUAL_CS);
+ else
+ qspi->cfg |= CFG_MANUAL_CS;
+ writel(qspi->cfg, QSPI_CONFIG);
+}
+
+static inline void qspi_xmit(struct qspi_ctxt *qspi)
+{
+ // start txn
+ writel(qspi->cfg | CFG_MANUAL_START, QSPI_CONFIG);
+
+ // wait for command to transmit and TX fifo to be empty
+ while ((readl(QSPI_IRQ_STATUS) & TX_FIFO_NOT_FULL) == 0) ;
+}
+
+static inline void qspi_flush_rx(void)
+{
+ while (!(readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY)) ;
+ readl(QSPI_RXDATA);
+}
+
+static const uint32_t TXFIFO[] = { QSPI_TXD1, QSPI_TXD2, QSPI_TXD3, QSPI_TXD0, QSPI_TXD0, QSPI_TXD0 };
+
+void qspi_rd(struct qspi_ctxt *qspi, uint32_t cmd, uint32_t asize, uint32_t *data, uint32_t count)
+{
+ uint32_t sent = 0;
+ uint32_t rcvd = 0;
+
+ DEBUG_ASSERT(qspi);
+ DEBUG_ASSERT(asize < 6);
+
+ qspi_cs(qspi, 0);
+
+ writel(cmd, TXFIFO[asize]);
+ qspi_xmit(qspi);
+
+ if (asize == 4) { // dummy byte
+ writel(0, QSPI_TXD1);
+ qspi_xmit(qspi);
+ qspi_flush_rx();
+ }
+
+ qspi_flush_rx();
+
+ while (rcvd < count) {
+ while (readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY) {
+ *data++ = readl(QSPI_RXDATA);
+ rcvd++;
+ }
+ while ((readl(QSPI_IRQ_STATUS) & TX_FIFO_NOT_FULL) && (sent < count)) {
+ writel(0, QSPI_TXD0);
+ sent++;
+ }
+ qspi_xmit(qspi);
+ }
+ qspi_cs(qspi, 1);
+}
+
+void qspi_wr(struct qspi_ctxt *qspi, uint32_t cmd, uint32_t asize, uint32_t *data, uint32_t count)
+{
+ uint32_t sent = 0;
+ uint32_t rcvd = 0;
+
+ DEBUG_ASSERT(qspi);
+ DEBUG_ASSERT(asize < 6);
+
+ qspi_cs(qspi, 0);
+
+ writel(cmd, TXFIFO[asize]);
+ qspi_xmit(qspi);
+
+ if (asize == 4) { // dummy byte
+ writel(0, QSPI_TXD1);
+ qspi_xmit(qspi);
+ qspi_flush_rx();
+ }
+
+ qspi_flush_rx();
+
+ while (rcvd < count) {
+ while (readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY) {
+ readl(QSPI_RXDATA); // discard
+ rcvd++;
+ }
+ while ((readl(QSPI_IRQ_STATUS) & TX_FIFO_NOT_FULL) && (sent < count)) {
+ writel(*data++, QSPI_TXD0);
+ sent++;
+ }
+ qspi_xmit(qspi);
+ }
+
+ qspi_cs(qspi, 1);
+}
+
+void qspi_wr1(struct qspi_ctxt *qspi, uint32_t cmd)
+{
+ DEBUG_ASSERT(qspi);
+
+ qspi_cs(qspi, 0);
+ writel(cmd, QSPI_TXD1);
+ qspi_xmit(qspi);
+
+ while (!(readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY)) ;
+
+ readl(QSPI_RXDATA);
+ qspi_cs(qspi, 1);
+}
+
+void qspi_wr2(struct qspi_ctxt *qspi, uint32_t cmd)
+{
+ DEBUG_ASSERT(qspi);
+
+ qspi_cs(qspi, 0);
+ writel(cmd, QSPI_TXD2);
+ qspi_xmit(qspi);
+
+ while (!(readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY)) ;
+
+ readl(QSPI_RXDATA);
+ qspi_cs(qspi, 1);
+}
+
+void qspi_wr3(struct qspi_ctxt *qspi, uint32_t cmd)
+{
+ DEBUG_ASSERT(qspi);
+
+ qspi_cs(qspi, 0);
+ writel(cmd, QSPI_TXD3);
+ qspi_xmit(qspi);
+
+ while (!(readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY)) ;
+
+ readl(QSPI_RXDATA);
+ qspi_cs(qspi, 1);
+}
+
+uint32_t qspi_rd1(struct qspi_ctxt *qspi, uint32_t cmd)
+{
+ qspi_cs(qspi, 0);
+ writel(cmd, QSPI_TXD2);
+ qspi_xmit(qspi);
+
+ while (!(readl(QSPI_IRQ_STATUS) & RX_FIFO_NOT_EMPTY)) ;
+
+ qspi_cs(qspi, 1);
+ return readl(QSPI_RXDATA);
+}
+
+// vim: set ts=4 sw=4 noexpandtab:
+
diff --git a/src/bsp/lk/platform/zynq/rules.mk b/src/bsp/lk/platform/zynq/rules.mk
new file mode 100644
index 0000000..77a8e49
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/rules.mk
@@ -0,0 +1,91 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+ARCH := arm
+ARM_CPU := cortex-a9-neon
+WITH_SMP ?= 1
+SMP_MAX_CPUS := 2
+
+MODULE_DEPS := \
+ lib/bio \
+ lib/cbuf \
+ lib/watchdog \
+ dev/cache/pl310 \
+ dev/interrupt/arm_gic \
+ dev/timer/arm_cortex_a9
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/clocks.c \
+ $(LOCAL_DIR)/debug.c \
+ $(LOCAL_DIR)/fpga.c \
+ $(LOCAL_DIR)/gpio.c \
+ $(LOCAL_DIR)/platform.c \
+ $(LOCAL_DIR)/qspi.c \
+ $(LOCAL_DIR)/spiflash.c \
+ $(LOCAL_DIR)/start.S \
+ $(LOCAL_DIR)/swdt.c \
+ $(LOCAL_DIR)/uart.c \
+
+# default to no sdram unless the target calls it out
+ZYNQ_SDRAM_SIZE ?= 0
+
+# default to having the gem ethernet controller
+ZYNQ_WITH_GEM_ETH ?= 1
+
+ifeq ($(ZYNQ_WITH_GEM_ETH),1)
+MODULE_SRCS += \
+ $(LOCAL_DIR)/gem.c \
+
+GLOBAL_DEFINES += \
+ ZYNQ_WITH_GEM_ETH=1 \
+ ARM_ARCH_WAIT_FOR_SECONDARIES=1
+
+# gem driver depends on minip interface
+MODULE_DEPS += \
+ lib/minip
+endif
+
+ifeq ($(ZYNQ_USE_SRAM),1)
+MEMBASE := 0x0
+MEMSIZE := 0x40000 # 4 * 64K
+
+GLOBAL_DEFINES += \
+ ZYNQ_CODE_IN_SRAM=1
+
+ifneq ($(ZYNQ_SDRAM_SIZE),0)
+GLOBAL_DEFINES += \
+ ZYNQ_SDRAM_INIT=1
+endif
+
+else
+MEMBASE := 0x00000000
+MEMSIZE ?= $(ZYNQ_SDRAM_SIZE) # 256MB
+KERNEL_LOAD_OFFSET := 0x00100000 # loaded 1MB into physical space
+
+# set a #define so system code can decide if it needs to reinitialize dram or not
+GLOBAL_DEFINES += \
+ ZYNQ_CODE_IN_SDRAM=1
+endif
+
+# put our kernel at 0xc0000000 so we can have axi bus 1 mapped at 0x80000000
+KERNEL_BASE = 0xc0000000
+
+GLOBAL_DEFINES += \
+ SDRAM_SIZE=$(ZYNQ_SDRAM_SIZE)
+
+LINKER_SCRIPT += \
+ $(BUILDDIR)/system-onesegment.ld
+
+# python script to generate the zynq's bootrom bootheader
+BOOTHEADERBIN := $(BUILDDIR)/BOOT.BIN
+MKBOOTHEADER := $(LOCAL_DIR)/mkbootheader.py
+EXTRA_BUILDDEPS += $(BOOTHEADERBIN)
+GENERATED += $(BOOTHEADERBIN)
+
+$(BOOTHEADERBIN): $(OUTBIN) $(MKBOOTHEADER)
+ @$(MKDIR)
+ $(NOECHO)echo generating $@; \
+ $(MKBOOTHEADER) $(OUTBIN) $@
+
+include make/module.mk
diff --git a/src/bsp/lk/platform/zynq/spiflash.c b/src/bsp/lk/platform/zynq/spiflash.c
new file mode 100644
index 0000000..b807c1d
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/spiflash.c
@@ -0,0 +1,582 @@
+/*
+ * Copyright (c) 2014 Brian Swetland
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <debug.h>
+#include <assert.h>
+#include <trace.h>
+#include <compiler.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <err.h>
+#include <string.h>
+#include <rand.h>
+#include <reg.h>
+#include <pow2.h>
+
+#include <lib/bio.h>
+#include <lib/console.h>
+#include <dev/qspi.h>
+#include <kernel/thread.h>
+
+#include <platform/zynq.h>
+
+#define LOCAL_TRACE 0
+
+// parameters specifically for the 16MB spansion S25FL128S flash
+#define PARAMETER_AREA_SIZE (128*1024)
+#define PAGE_PROGRAM_SIZE (256) // can be something else based on the part
+#define PAGE_ERASE_SLEEP_TIME (150) // amount of time before waiting to check if erase completed
+#define SECTOR_ERASE_SIZE (4096)
+#define LARGE_SECTOR_ERASE_SIZE (64*1024)
+
+#define STS_PROGRAM_ERR (1<<6)
+#define STS_ERASE_ERR (1<<5)
+#define STS_BUSY (1<<0)
+
+#define MAX_GEOMETRY_COUNT (2)
+
+struct spi_flash {
+ bool detected;
+
+ struct qspi_ctxt qspi;
+ bdev_t bdev;
+ bio_erase_geometry_info_t geometry[MAX_GEOMETRY_COUNT];
+
+ off_t size;
+};
+
+static struct spi_flash flash;
+
+static ssize_t spiflash_bdev_read(struct bdev *, void *buf, off_t offset, size_t len);
+static ssize_t spiflash_bdev_read_block(struct bdev *, void *buf, bnum_t block, uint count);
+static ssize_t spiflash_bdev_write_block(struct bdev *, const void *buf, bnum_t block, uint count);
+static ssize_t spiflash_bdev_erase(struct bdev *, off_t offset, size_t len);
+static int spiflash_ioctl(struct bdev *, int request, void *argp);
+
+// adjust 24 bit address to be correct-byte-order for 32bit qspi commands
+static uint32_t qspi_fix_addr(uint32_t addr)
+{
+ DEBUG_ASSERT((addr & ~(0x00ffffff)) == 0); // only dealing with 24bit addresses
+
+ return ((addr & 0xff) << 24) | ((addr&0xff00) << 8) | ((addr>>8) & 0xff00);
+}
+
+static void qspi_rd32(struct qspi_ctxt *qspi, uint32_t addr, uint32_t *data, uint32_t count)
+{
+ qspi_rd(qspi, qspi_fix_addr(addr) | 0x6B, 4, data, count);
+}
+
+static inline void qspi_wren(struct qspi_ctxt *qspi)
+{
+ qspi_wr1(qspi, 0x06);
+}
+
+static inline void qspi_clsr(struct qspi_ctxt *qspi)
+{
+ qspi_wr1(qspi, 0x30);
+}
+
+static inline uint32_t qspi_rd_cr1(struct qspi_ctxt *qspi)
+{
+ return qspi_rd1(qspi, 0x35) >> 24;
+}
+
+static inline uint32_t qspi_rd_status(struct qspi_ctxt *qspi)
+{
+ return qspi_rd1(qspi, 0x05) >> 24;
+}
+
+static inline void qspi_wr_status_cr1(struct qspi_ctxt *qspi, uint8_t status, uint8_t cr1)
+{
+ uint32_t cmd = (cr1 << 16) | (status << 8) | 0x01;
+
+ qspi_wren(qspi);
+ qspi_wr3(qspi, cmd);
+}
+
+static ssize_t qspi_erase_sector(struct qspi_ctxt *qspi, uint32_t addr)
+{
+ uint32_t cmd;
+ uint32_t status;
+ ssize_t toerase;
+
+ LTRACEF("addr 0x%x\n", addr);
+
+ DEBUG_ASSERT(qspi);
+
+ if (addr < PARAMETER_AREA_SIZE) {
+ // erase a small parameter sector (4K)
+ DEBUG_ASSERT(IS_ALIGNED(addr, SECTOR_ERASE_SIZE));
+ if (!IS_ALIGNED(addr, SECTOR_ERASE_SIZE))
+ return ERR_INVALID_ARGS;
+
+ cmd = 0x20;
+ toerase = SECTOR_ERASE_SIZE;
+ } else {
+ // erase a large sector (64k or 256k)
+ DEBUG_ASSERT(IS_ALIGNED(addr, LARGE_SECTOR_ERASE_SIZE));
+ if (!IS_ALIGNED(addr, LARGE_SECTOR_ERASE_SIZE))
+ return ERR_INVALID_ARGS;
+
+ cmd = 0xd8;
+ toerase = LARGE_SECTOR_ERASE_SIZE;
+ }
+
+ qspi_wren(qspi);
+ qspi_wr(qspi, qspi_fix_addr(addr) | cmd, 3, 0, 0);
+
+ thread_sleep(PAGE_ERASE_SLEEP_TIME);
+ while ((status = qspi_rd_status(qspi)) & STS_BUSY)
+ ;
+
+ LTRACEF("status 0x%x\n", status);
+ if (status & (STS_PROGRAM_ERR | STS_ERASE_ERR)) {
+ TRACEF("failed @ 0x%x\n", addr);
+ qspi_clsr(qspi);
+ return ERR_IO;
+ }
+
+ return toerase;
+}
+
+static ssize_t qspi_write_page(struct qspi_ctxt *qspi, uint32_t addr, const uint8_t *data)
+{
+ uint32_t oldkhz, status;
+
+ LTRACEF("addr 0x%x, data %p\n", addr, data);
+
+ DEBUG_ASSERT(qspi);
+ DEBUG_ASSERT(data);
+ DEBUG_ASSERT(IS_ALIGNED(addr, PAGE_PROGRAM_SIZE));
+
+ if (!IS_ALIGNED(addr, PAGE_PROGRAM_SIZE))
+ return ERR_INVALID_ARGS;
+
+ oldkhz = qspi->khz;
+ if (qspi_set_speed(qspi, 80000))
+ return ERR_IO;
+
+ qspi_wren(qspi);
+ qspi_wr(qspi, qspi_fix_addr(addr) | 0x32, 3, (uint32_t *)data, PAGE_PROGRAM_SIZE / 4);
+ qspi_set_speed(qspi, oldkhz);
+
+ while ((status = qspi_rd_status(qspi)) & STS_BUSY) ;
+
+ if (status & (STS_PROGRAM_ERR | STS_ERASE_ERR)) {
+ printf("qspi_write_page failed @ %x\n", addr);
+ qspi_clsr(qspi);
+ return ERR_IO;
+ }
+ return PAGE_PROGRAM_SIZE;
+}
+
+static ssize_t spiflash_read_cfi(void *buf, size_t len)
+{
+ DEBUG_ASSERT(len > 0 && (len % 4) == 0);
+
+ qspi_rd(&flash.qspi, 0x9f, 0, buf, len / 4);
+
+ if (len < 4)
+ return len;
+
+ /* look at byte 3 of the cfi, which says the total length of the cfi structure */
+ size_t cfi_len = ((uint8_t *)buf)[3];
+ if (cfi_len == 0)
+ cfi_len = 512;
+ else
+ cfi_len += 3;
+
+ return MIN(len, cfi_len);
+}
+
+static ssize_t spiflash_read_otp(void *buf, uint32_t addr, size_t len)
+{
+ DEBUG_ASSERT(len > 0 && (len % 4) == 0);
+
+ if (len > 1024)
+ len = 1024;
+
+ qspi_rd(&flash.qspi, 0x4b, 4, buf, len / 4);
+
+ if (len < 4)
+ return len;
+
+ return len;
+}
+
+status_t spiflash_detect(void)
+{
+ if (flash.detected)
+ return NO_ERROR;
+
+ qspi_init(&flash.qspi, 100000);
+
+ /* read and parse the cfi */
+ uint8_t *buf = calloc(1, 512);
+ ssize_t len = spiflash_read_cfi(buf, 512);
+ if (len < 4)
+ goto nodetect;
+
+ LTRACEF("looking at vendor/device id combination: %02x:%02x:%02x\n", buf[0], buf[1], buf[2]);
+
+ /* at the moment, we only support particular spansion flashes */
+ if (buf[0] != 0x01) goto nodetect;
+
+ if (buf[1] == 0x20 && buf[2] == 0x18) {
+ /* 128Mb version */
+ flash.size = 16*1024*1024;
+ } else if (buf[1] == 0x02 && buf[2] == 0x19) {
+ /* 256Mb version */
+ flash.size = 32*1024*1024;
+ } else {
+ TRACEF("unknown vendor/device id combination: %02x:%02x:%02x\n",
+ buf[0], buf[1], buf[2]);
+ goto nodetect;
+ }
+
+ /* Fill out our geometry info based on the CFI */
+ size_t region_count = buf[0x2C];
+ if (region_count > countof(flash.geometry)) {
+ TRACEF("erase region count (%zu) exceeds max allowed (%zu)\n",
+ region_count, countof(flash.geometry));
+ goto nodetect;
+ }
+
+ size_t offset = 0;
+ for (size_t i = 0; i < region_count; i++) {
+ const uint8_t* info = buf + 0x2D + (i << 2);
+ size_t pages = ((((size_t)info[1]) << 8) | info[0]) + 1;
+ size_t erase_size = ((((size_t)info[3]) << 8) | info[2]) << 8;
+
+ if (!ispow2(erase_size)) {
+ TRACEF("Region %zu page size (%zu) is not a power of 2\n",
+ i, erase_size);
+ goto nodetect;
+ }
+
+ flash.geometry[i].erase_size = erase_size;
+ flash.geometry[i].erase_shift = log2_uint(erase_size);
+ flash.geometry[i].start = offset;
+ flash.geometry[i].size = pages << flash.geometry[i].erase_shift;
+
+ size_t erase_mask = ((size_t)0x1 << flash.geometry[i].erase_shift) - 1;
+ if (offset & erase_mask) {
+ TRACEF("Region %zu not aligned to erase boundary (start %zu, erase size %zu)\n",
+ i, offset, erase_size);
+ goto nodetect;
+ }
+
+ offset += flash.geometry[i].size;
+ }
+
+ free(buf);
+
+ /* read the 16 byte random number out of the OTP area and add to the rand entropy pool */
+ uint32_t r[4];
+ memset(r, 0, sizeof(r));
+ spiflash_read_otp(r, 0, 16);
+
+ LTRACEF("OTP random %08x%08x%08x%08x\n", r[0], r[1], r[2], r[3]);
+ rand_add_entropy(r, sizeof(r));
+
+ flash.detected = true;
+
+ /* see if we're in serial mode */
+ uint32_t cr1 = qspi_rd_cr1(&flash.qspi);
+ if ((cr1 & (1<<1)) == 0) {
+ printf("spiflash: device not in quad mode, cannot use for read/write\n");
+ goto nouse;
+ }
+
+ /* construct the block device */
+ bio_initialize_bdev(&flash.bdev, "spi0",
+ PAGE_PROGRAM_SIZE, flash.size / PAGE_PROGRAM_SIZE,
+ region_count, flash.geometry, BIO_FLAGS_NONE);
+
+ /* override our block device hooks */
+ flash.bdev.read = &spiflash_bdev_read;
+ flash.bdev.read_block = &spiflash_bdev_read_block;
+ // flash.bdev.write has a default hook that will be okay
+ flash.bdev.write_block = &spiflash_bdev_write_block;
+ flash.bdev.erase = &spiflash_bdev_erase;
+ flash.bdev.ioctl = &spiflash_ioctl;
+
+ /* we erase to 0xff */
+ flash.bdev.erase_byte = 0xff;
+
+ bio_register_device(&flash.bdev);
+
+ LTRACEF("found flash of size 0x%llx\n", flash.size);
+
+nouse:
+ return NO_ERROR;
+
+nodetect:
+ LTRACEF("flash not found\n");
+
+ free(buf);
+ flash.detected = false;
+ return ERR_NOT_FOUND;
+}
+
+// bio layer hooks
+static ssize_t spiflash_bdev_read(struct bdev *bdev, void *buf, off_t offset, size_t len)
+{
+ LTRACEF("dev %p, buf %p, offset 0x%llx, len 0x%zx\n", bdev, buf, offset, len);
+
+ DEBUG_ASSERT(flash.detected);
+
+ len = bio_trim_range(bdev, offset, len);
+ if (len == 0)
+ return 0;
+
+ // XXX handle not mulitple of 4
+ qspi_rd32(&flash.qspi, offset, buf, len / 4);
+
+ return len;
+}
+
+static ssize_t spiflash_bdev_read_block(struct bdev *bdev, void *buf, bnum_t block, uint count)
+{
+ LTRACEF("dev %p, buf %p, block 0x%x, count %u\n", bdev, buf, block, count);
+
+ count = bio_trim_block_range(bdev, block, count);
+ if (count == 0)
+ return 0;
+
+ return spiflash_bdev_read(bdev, buf, block << bdev->block_shift, count << bdev->block_shift);
+}
+
+static ssize_t spiflash_bdev_write_block(struct bdev *bdev, const void *_buf, bnum_t block, uint count)
+{
+ LTRACEF("dev %p, buf %p, block 0x%x, count %u\n", bdev, _buf, block, count);
+
+ DEBUG_ASSERT(bdev->block_size == PAGE_PROGRAM_SIZE);
+
+ count = bio_trim_block_range(bdev, block, count);
+ if (count == 0)
+ return 0;
+
+ const uint8_t *buf = _buf;
+
+ ssize_t written = 0;
+ while (count > 0) {
+ ssize_t err = qspi_write_page(&flash.qspi, block * PAGE_PROGRAM_SIZE, buf);
+ if (err < 0)
+ return err;
+
+ buf += PAGE_PROGRAM_SIZE;
+ written += err;
+ block++;
+ count--;
+ }
+
+ return written;
+}
+
+static ssize_t spiflash_bdev_erase(struct bdev *bdev, off_t offset, size_t len)
+{
+ LTRACEF("dev %p, offset 0x%llx, len 0x%zx\n", bdev, offset, len);
+
+ len = bio_trim_range(bdev, offset, len);
+ if (len == 0)
+ return 0;
+
+ ssize_t erased = 0;
+ while (erased < (ssize_t)len) {
+ ssize_t err = qspi_erase_sector(&flash.qspi, offset);
+ if (err < 0)
+ return err;
+
+ erased += err;
+ offset += err;
+ }
+
+ return erased;
+}
+
+static int spiflash_ioctl(struct bdev *bdev, int request, void *argp)
+{
+ LTRACEF("dev %p, request %d, argp %p\n", bdev, request, argp);
+
+ int ret = NO_ERROR;
+ switch (request) {
+ case BIO_IOCTL_GET_MEM_MAP:
+ /* put the device into linear mode */
+ ret = qspi_enable_linear(&flash.qspi);
+ // Fallthrough.
+ case BIO_IOCTL_GET_MAP_ADDR:
+ if (argp)
+ *(void **)argp = (void *)QSPI_LINEAR_BASE;
+ break;
+ case BIO_IOCTL_PUT_MEM_MAP:
+ /* put the device back into regular mode */
+ ret = qspi_disable_linear(&flash.qspi);
+ break;
+ default:
+ ret = ERR_NOT_SUPPORTED;
+ }
+
+ return ret;
+}
+
+// debug tests
+int cmd_spiflash(int argc, const cmd_args *argv)
+{
+ if (argc < 2) {
+notenoughargs:
+ printf("not enough arguments\n");
+usage:
+ printf("usage:\n");
+#if LK_DEBUGLEVEL > 1
+ printf("\t%s detect\n", argv[0].str);
+ printf("\t%s cfi\n", argv[0].str);
+ printf("\t%s cr1\n", argv[0].str);
+ printf("\t%s otp\n", argv[0].str);
+ printf("\t%s linear [true/false]\n", argv[0].str);
+ printf("\t%s read <offset> <length>\n", argv[0].str);
+ printf("\t%s write <offset> <length> <address>\n", argv[0].str);
+ printf("\t%s erase <offset>\n", argv[0].str);
+#endif
+ printf("\t%s setquad (dangerous)\n", argv[0].str);
+ return ERR_INVALID_ARGS;
+ }
+
+#if LK_DEBUGLEVEL > 1
+ if (!strcmp(argv[1].str, "detect")) {
+ spiflash_detect();
+ } else if (!strcmp(argv[1].str, "cr1")) {
+ if (!flash.detected) {
+ printf("flash not detected\n");
+ return -1;
+ }
+
+ uint32_t cr1 = qspi_rd_cr1(&flash.qspi);
+ printf("cr1 0x%x\n", cr1);
+ } else if (!strcmp(argv[1].str, "cfi")) {
+ if (!flash.detected) {
+ printf("flash not detected\n");
+ return -1;
+ }
+
+ uint8_t *buf = calloc(1, 512);
+ ssize_t len = spiflash_read_cfi(buf, 512);
+ printf("returned cfi len %ld\n", len);
+
+ hexdump8(buf, len);
+
+ free(buf);
+ } else if (!strcmp(argv[1].str, "otp")) {
+ if (!flash.detected) {
+ printf("flash not detected\n");
+ return -1;
+ }
+
+ uint8_t *buf = calloc(1, 1024);
+ ssize_t len = spiflash_read_otp(buf, 0, 1024);
+ printf("spiflash_read_otp returns %ld\n", len);
+
+ hexdump8(buf, len);
+
+ free(buf);
+ } else if (!strcmp(argv[1].str, "linear")) {
+ if (argc < 3) goto notenoughargs;
+ if (!flash.detected) {
+ printf("flash not detected\n");
+ return -1;
+ }
+
+ if (argv[2].b)
+ qspi_enable_linear(&flash.qspi);
+ else
+ qspi_disable_linear(&flash.qspi);
+ } else if (!strcmp(argv[1].str, "read")) {
+ if (argc < 4) goto notenoughargs;
+ if (!flash.detected) {
+ printf("flash not detected\n");
+ return -1;
+ }
+
+ uint8_t *buf = calloc(1, argv[3].u);
+
+ qspi_rd32(&flash.qspi, argv[2].u, (uint32_t *)buf, argv[3].u / 4);
+
+ hexdump8(buf, argv[3].u);
+ free(buf);
+ } else if (!strcmp(argv[1].str, "write")) {
+ if (argc < 5) goto notenoughargs;
+ if (!flash.detected) {
+ printf("flash not detected\n");
+ return -1;
+ }
+
+ status_t err = qspi_write_page(&flash.qspi, argv[2].u, argv[4].p);
+ printf("write_page returns %d\n", err);
+ } else if (!strcmp(argv[1].str, "erase")) {
+ if (argc < 3) goto notenoughargs;
+ if (!flash.detected) {
+ printf("flash not detected\n");
+ return -1;
+ }
+
+ status_t err = qspi_erase_sector(&flash.qspi, argv[2].u);
+ printf("erase returns %d\n", err);
+ } else
+#endif
+ if (!strcmp(argv[1].str, "setquad")) {
+ if (!flash.detected) {
+ printf("flash not detected\n");
+ return -1;
+ }
+
+ uint32_t cr1 = qspi_rd_cr1(&flash.qspi);
+ printf("cr1 before 0x%x\n", cr1);
+
+ if (cr1 & (1<<1)) {
+ printf("flash already in quad mode\n");
+ return 0;
+ }
+
+ qspi_wr_status_cr1(&flash.qspi, 0, cr1 | (1<<1));
+
+ thread_sleep(500);
+ cr1 = qspi_rd_cr1(&flash.qspi);
+ printf("cr1 after 0x%x\n", cr1);
+ } else {
+ printf("unknown command\n");
+ goto usage;
+ }
+
+ return 0;
+}
+
+#if defined(WITH_LIB_CONSOLE)
+#include <lib/console.h>
+
+STATIC_COMMAND_START
+STATIC_COMMAND("spiflash", "spi flash manipulation utilities", cmd_spiflash)
+STATIC_COMMAND_END(qspi);
+
+#endif
+
+// vim: set ts=4 sw=4 noexpandtab:
diff --git a/src/bsp/lk/platform/zynq/start.S b/src/bsp/lk/platform/zynq/start.S
new file mode 100644
index 0000000..c97c524
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/start.S
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2014-2015 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <asm.h>
+#include <platform/zynq.h>
+
+/* code run at the very beginning of the system, attempting to trap the 2nd cpu */
+FUNCTION(platform_reset)
+ /* figure out our cpu number */
+ mrc p15, 0, r12, c0, c0, 5 /* MPIDR */
+
+ /* mask off the bottom 8 bits to test cpu number */
+ ubfx r12, r12, #0, #8
+
+ /* if we're the 0th cpu, continue to arm_reset */
+ teq r12, #0
+ beq arm_reset
+
+ /* bump the cpu counter */
+ adr r12, __cpu_trapped
+ mov r11, #1
+ str r11, [r12]
+ dsb
+
+#if !WITH_SMP
+0:
+ /* stay trapped here forever */
+ wfe
+ b 0b
+#else
+ /* pass on through the reset vector, where the arm arch code will trap the cpu */
+ b arm_reset
+#endif
+
+DATA(__cpu_trapped)
+ .word 0
+
+#if 0
+/* disabled for now */
+
+/* this code attempts to remap sram to 0xfffc0000 - 0xffffffff and
+ branch the cpu into the equivalent spot. Assumes the cpu is running
+ at the initial 0 based mapping */
+
+/* a spot of the top bank of OCM memory for us to run our code from
+ needs to be below where the second cpu is running (0xffffe00-0xfffffff0) */
+#define TARGET_SPOT 0xfffff800
+
+/* first piece of code run out of the reset vector. use
+ to relocate sram to the final location at 0xfffc0000
+ and switch to there */
+FUNCTION(platform_reset)
+ /* relocate the below code to TARGET_SPOT */
+ ldr r8, =TARGET_SPOT
+ adr r9, .Lcore_reloc_start
+ adr r10, .Lcore_reloc_end
+
+0:
+ ldr r12, [r9], #4
+ str r12, [r8], #4
+ cmp r9, r10
+ bne 0b
+
+ /* load constants we will need below */
+ ldr r8, =SLCR_BASE
+ ldr r9, =SCU_CONTROL_BASE
+
+ /* calculate the new return address this code will need to branch to */
+ adr r12, .Ldone
+ add r12, #0xfffc0000
+
+ ldr r10, =TARGET_SPOT
+ bx r10
+
+.Ldone:
+ b arm_reset
+
+.Lcore_reloc_start:
+ # use SCLR to map the sram blocks to the top of their segment
+ movw r10, #SLCR_UNLOCK_KEY
+ str r10, [r8, #SLCR_UNLOCK]
+
+ ldr r10, [r8, #OCM_CFG]
+ orr r10, #0xf
+ str r10, [r8, #OCM_CFG]
+
+ movw r10, #SLCR_LOCK_KEY
+ str r10, [r8, #SLCR_LOCK]
+
+ # tell the SCU to not filter first 1MB
+ mov r10, #0
+ str r10, [r9, #0x40] /* SCU filter start address */
+ dmb
+
+ bx r12
+.Lcore_reloc_end:
+
+.ltorg
+#endif
+
+
diff --git a/src/bsp/lk/platform/zynq/swdt.c b/src/bsp/lk/platform/zynq/swdt.c
new file mode 100644
index 0000000..88fc1dc
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/swdt.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2015 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <err.h>
+#include <debug.h>
+#include <trace.h>
+#include <stdio.h>
+#include <string.h>
+#include <lib/watchdog.h>
+#include <platform.h>
+#include <platform/zynq.h>
+
+/* Driver for the system wide watchdog timer for Zynq */
+
+#define LOCAL_TRACE 0
+
+/* three magic values for the MODE, CONTROL, and RESTART registers */
+#define SWDT_MODE_ZKEY (0xabc << 12)
+#define SWDT_CONTROL_CKEY (0x248 << 14)
+#define SWDT_RESTART_RSTKEY (0x1999)
+
+/* reserved field in the MODE register */
+#define SWDT_MODE_RESERVED (0x4 << 4)
+
+/* routines called from lib/watchdog */
+status_t platform_watchdog_init(lk_time_t target_timeout,
+ lk_time_t* recommended_pet_period)
+{
+ LTRACEF("target_timeout %u\n", (uint32_t)target_timeout);
+
+ /* make sure the swdt is stopped */
+ SWDT->MODE = SWDT_MODE_ZKEY | SWDT_MODE_RESERVED;
+
+ /* make sure swdt has the proper clock */
+ SLCR->WDT_CLK_SEL = 0; // cpu 1x
+
+ uint32_t swdt_clock = zynq_get_swdt_freq();
+
+ /* assuming a prescalar of / 4096, figure out the restart value */
+ uint32_t restart = ((swdt_clock / 4096) * target_timeout) / 1000;
+
+ /* make sure the restart value is <= 24 bits */
+ if (restart > 0x00ffffff)
+ restart = 0x00ffffff;
+
+ LTRACEF("restart value %u\n", restart);
+
+ /* the bottom 12 bits of restart are set to 0xfff by hardware */
+ restart |= 0xfff;
+
+ /* pet period is / 2 the computed restart value */
+ if (recommended_pet_period)
+ *recommended_pet_period = ((restart * 1000) / (swdt_clock / 4096)) / 2;
+
+ LTRACEF("recommended pet period %u\n", (uint32_t)*recommended_pet_period);
+
+ /* set up the swdt */
+
+ /* load counter restart (top 12 bits of restart count), pclk / 4096 */
+ SWDT->CONTROL = SWDT_CONTROL_CKEY | ((restart >> 12) << 2) | 3;
+
+ /* zero it out */
+ SWDT->RESTART = SWDT_RESTART_RSTKEY;
+
+ DMB;
+
+ return NO_ERROR;
+}
+
+void platform_watchdog_set_enabled(bool enabled)
+{
+ LTRACEF("enabled %u\n", enabled);
+
+ if (enabled) {
+ /* wide irq length, reset enable on counter zero, enable */
+ SWDT->MODE = SWDT_MODE_ZKEY | SWDT_MODE_RESERVED | (3 << 7) | (1<<1) | 1;
+
+ /* start it by petting it once, in case it already latched */
+ SWDT->RESTART = SWDT_RESTART_RSTKEY;
+ } else {
+ /* disable everything */
+ SWDT->MODE = SWDT_MODE_ZKEY | SWDT_MODE_RESERVED;
+ }
+ DMB;
+}
+
+void platform_watchdog_pet(void)
+{
+ //LTRACEF("pet\n");
+ SWDT->RESTART = SWDT_RESTART_RSTKEY;
+ DMB;
+}
+
diff --git a/src/bsp/lk/platform/zynq/timer.c b/src/bsp/lk/platform/zynq/timer.c
new file mode 100644
index 0000000..f983c96
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/timer.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <debug.h>
+#include <sys/types.h>
+#include <err.h>
+#include <stdio.h>
+#include <assert.h>
+#include <trace.h>
+#include <kernel/thread.h>
+#include <platform.h>
+#include <platform/interrupts.h>
+#include <platform/timer.h>
+#include <platform/zynq.h>
+#include "platform_p.h"
+
+/* unused, arm_cortex_a9_timer does timer duty */
+
+#if 0
+/* driver for Cadence triple timer counter (TTC) */
+
+#define LOCAL_TRACE 0
+
+#define TIMREG(reg) (*REG32(TTC0_BASE + (reg)))
+#define TIMREG16(reg) (*REG16(TTC0_BASE + (reg)))
+#define TIMREG8(reg) (*REG8(TTC0_BASE + (reg)))
+
+#define CLK_CTRL(n) (0x00 + (n) * 4)
+#define CNT_CTRL(n) (0x0c + (n) * 4)
+#define CNT_VAL(n) (0x18 + (n) * 4)
+#define INTERVAL_VAL(n) (0x24 + (n) * 4)
+#define MATCH_1(n) (0x30 + (n) * 4)
+#define MATCH_2(n) (0x3c + (n) * 4)
+#define MATCH_3(n) (0x48 + (n) * 4)
+#define ISR(n) (0x54 + (n) * 4)
+#define IEN(n) (0x60 + (n) * 4)
+#define EVT_CTRL(n) (0x6c + (n) * 4)
+#define EVT(n) (0x78 + (n) * 4)
+
+static platform_timer_callback t_callback;
+
+static volatile uint ticks = 0;
+static lk_time_t periodic_interval;
+
+status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval)
+{
+ enter_critical_section();
+
+ LTRACEF("callback %p, arg %p, interval %lu\n", callback, arg, interval);
+
+ t_callback = callback;
+
+ periodic_interval = interval;
+
+ uint32_t ticks = periodic_interval * 1000; /* timer is running close to 1Mhz */
+ ASSERT(ticks <= 0xffff);
+
+ TIMREG(IEN(0)) = (1<<0); // interval interrupt
+ TIMREG(INTERVAL_VAL(0)) = ticks;
+ TIMREG(CNT_CTRL(0)) = (1<<5) | (1<<4) | (1<<1); // no wave, reset, interval mode
+
+ unmask_interrupt(TTC0_A_INT);
+
+ exit_critical_section();
+
+ return NO_ERROR;
+}
+
+lk_bigtime_t current_time_hires(void)
+{
+ lk_bigtime_t time;
+
+ time = ticks * periodic_interval * 1000ULL;
+
+ return time;
+}
+
+lk_time_t current_time(void)
+{
+ lk_time_t time;
+
+ time = ticks * periodic_interval;
+
+ return time;
+}
+
+static enum handler_return platform_tick(void *arg)
+{
+ ticks++;
+
+ volatile uint32_t hole = TIMREG(ISR(0)); // ack the irq
+
+ if (t_callback) {
+ return t_callback(arg, current_time());
+ } else {
+ return INT_NO_RESCHEDULE;
+ }
+}
+
+void platform_init_timer(void)
+{
+ /* disable timers */
+ TIMREG(CNT_CTRL(0)) = 0x1;
+ TIMREG(CNT_CTRL(1)) = 0x1;
+ TIMREG(CNT_CTRL(2)) = 0x1;
+
+ TIMREG(CLK_CTRL(0)) = (6 << 1) | 1; // prescale 133Mhz/(2^7) == 1039062Hz (close to 1Mhz)
+
+ register_int_handler(TTC0_A_INT, &platform_tick, NULL);
+ register_int_handler(TTC0_B_INT, &platform_tick, NULL);
+ register_int_handler(TTC0_C_INT, &platform_tick, NULL);
+}
+#endif
+
+/* vim: set ts=4 sw=4 expandtab: */
diff --git a/src/bsp/lk/platform/zynq/uart.c b/src/bsp/lk/platform/zynq/uart.c
new file mode 100644
index 0000000..531c051
--- /dev/null
+++ b/src/bsp/lk/platform/zynq/uart.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2014 Travis Geiselbrecht
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files
+ * (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge,
+ * publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include <reg.h>
+#include <stdio.h>
+#include <trace.h>
+#include <assert.h>
+#include <lib/cbuf.h>
+#include <kernel/thread.h>
+#include <platform/interrupts.h>
+#include <platform/debug.h>
+#include <platform/zynq.h>
+
+#define RXBUF_SIZE 16
+
+static cbuf_t uart_rx_buf[NUM_UARTS];
+
+static inline uintptr_t uart_to_ptr(unsigned int n) { return (n == 0) ? UART0_BASE : UART1_BASE; }
+static inline uint uart_to_irq(unsigned int n) { return (n == 0) ? UART0_INT : UART1_INT; }
+
+#define UART_REG(base, reg) (*REG32((base) + (reg)))
+
+static enum handler_return uart_irq(void *arg)
+{
+ bool resched = false;
+ uint port = (uint)arg;
+ uintptr_t base = uart_to_ptr(port);
+
+ /* read interrupt status and mask */
+ uint32_t isr = UART_REG(base, UART_ISR);
+ isr &= UART_REG(base, UART_IMR);
+
+ if (isr & (1<<0)) { // rxtrig
+ UART_REG(base, UART_ISR) = (1<< 0);
+
+ while ((UART_REG(base, UART_SR) & (1<<1)) == 0) { // ~rempty
+ char c = UART_REG(base, UART_FIFO);
+ cbuf_write_char(&uart_rx_buf[port], c, false);
+
+ resched = true;
+ }
+ }
+
+ return resched ? INT_RESCHEDULE : INT_NO_RESCHEDULE;
+}
+
+void uart_init(void)
+{
+ for (uint i = 0; i < NUM_UARTS; i++) {
+ cbuf_initialize(&uart_rx_buf[i], RXBUF_SIZE);
+
+ uintptr_t base = uart_to_ptr(i);
+
+ // clear all irqs
+ UART_REG(base, UART_IDR) = 0xffffffff;
+
+ // set rx fifo trigger to 1
+ UART_REG(base, UART_RXWM) = 1;
+
+ // enable the receiver
+ // NOTE: must clear rxdis and set rxen in the same write
+ UART_REG(base, UART_CR) = (UART_REG(base, UART_CR) & ~(1<<3)) | (1 << 2);
+
+ // enable rx interrupt
+ UART_REG(base, UART_IER) = (1<<0); // rxtrig
+
+ // set up interrupt handler
+ register_int_handler(uart_to_irq(i), &uart_irq, (void *)i);
+ unmask_interrupt(uart_to_irq(i));
+ }
+}
+
+void uart_init_early(void)
+{
+ for (uint i = 0; i < NUM_UARTS; i++) {
+ uintptr_t base = uart_to_ptr(i);
+
+ UART_REG(base, UART_BAUD_DIV) = UART_BRD_DIV(6);
+ UART_REG(base, UART_BAUDGEN) = UART_BRG_DIV(0x3E);
+
+ // reset the tx/rx path
+ UART_REG(base, UART_CR) |= UART_CR_TXRES | UART_CR_RXRES;
+ while ((UART_REG(base, UART_CR) & (UART_CR_TXRES | UART_CR_RXRES)) != 0)
+ ;
+
+ // n81, clock select ref_clk
+ UART_REG(base, UART_MR) = UART_MR_PAR(0x4); // no parity
+
+ // no flow
+ UART_REG(base, UART_MODEMCR) = 0;
+
+ UART_REG(base, UART_CR) = UART_CR_TXEN;
+ }
+
+ /* Configuration for the serial console */
+ /*UART_REG(UART1_BASE, UART_CR) = 0x00000017;*/
+ /*UART_REG(UART1_BASE, UART_MR) = 0x00000020;*/
+}
+
+int uart_putc(int port, char c)
+{
+ DEBUG_ASSERT(port >= 0 && port < NUM_UARTS);
+
+ uintptr_t base = uart_to_ptr(port);
+
+ /* spin while fifo is full */
+ while (UART_REG(base, UART_SR) & (1<<4))
+ ;
+ UART_REG(base, UART_FIFO) = c;
+
+ return 1;
+}
+
+int uart_getc(int port, bool wait)
+{
+ DEBUG_ASSERT(port >= 0 && port < NUM_UARTS);
+
+ char c;
+ if (cbuf_read_char(&uart_rx_buf[port], &c, wait) == 1)
+ return c;
+
+ return -1;
+}
+
+void uart_flush_tx(int port)
+{
+ DEBUG_ASSERT(port >= 0 && port < NUM_UARTS);
+}
+
+void uart_flush_rx(int port)
+{
+ DEBUG_ASSERT(port >= 0 && port < NUM_UARTS);
+}
+
+void uart_init_port(int port, uint baud)
+{
+ DEBUG_ASSERT(port >= 0 && port < NUM_UARTS);
+
+ PANIC_UNIMPLEMENTED;
+}
+