[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;
+}
+