[Feature]add MT2731_MP2_MR2_SVN388 baseline version
Change-Id: Ief04314834b31e27effab435d3ca8ba33b499059
diff --git a/src/bsp/lk/dev/cache/pl310/include/dev/cache/pl310.h b/src/bsp/lk/dev/cache/pl310/include/dev/cache/pl310.h
new file mode 100644
index 0000000..c012f78
--- /dev/null
+++ b/src/bsp/lk/dev/cache/pl310/include/dev/cache/pl310.h
@@ -0,0 +1,38 @@
+/*
+ * 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>
+#include <stdbool.h>
+
+status_t pl310_set_enable(bool enable);
+
+/* operations */
+void pl310_invalidate(void);
+void pl310_flush_invalidate(void);
+void pl310_sync_range(void);
+void pl310_clean_range(addr_t start, size_t len);
+void pl310_clean_invalidate_range(addr_t start, size_t len);
+void pl310_invalidate_range(addr_t start, size_t len);
+
+void pl310_pin_cache_range(addr_t start, size_t len);
diff --git a/src/bsp/lk/dev/cache/pl310/pl310.c b/src/bsp/lk/dev/cache/pl310/pl310.c
new file mode 100644
index 0000000..13c5916
--- /dev/null
+++ b/src/bsp/lk/dev/cache/pl310/pl310.c
@@ -0,0 +1,234 @@
+/*
+ * 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/cache/pl310.h>
+
+#include <assert.h>
+#include <trace.h>
+#include <err.h>
+#include <reg.h>
+#include <stdlib.h>
+#include <arch.h>
+#include <arch/arm/mmu.h>
+#include <dev/cache/pl310_config.h>
+#include <lk/init.h>
+
+/* configuration of the pl310 comes from #define space */
+#ifndef PL310_BASE
+#error need to define PL310_BASE
+#endif
+
+#define LOCAL_TRACE 0
+
+#define PL310_REG(reg) (*REG32(PL310_BASE + (reg)))
+
+/* registers */
+#define REG0_CACHE_ID 0x000
+#define REG0_CACHE_TYPE 0x004
+#define REG1_CONTROL 0x100
+#define REG1_AUX_CONTROL 0x104
+#define REG1_TAG_RAM_CONTROL 0x108
+#define REG1_DATA_RAM_CONTROL 0x10c
+#define REG2_EV_COUNTER_CTRL 0x200
+#define REG2_EV_COUNTER1_CFG 0x204
+#define REG2_EV_COUNTER0_CFG 0x208
+#define REG2_EV_COUNTER1 0x20c
+#define REG2_EV_COUNTER0 0x210
+#define REG2_INT_MASK 0x214
+#define REG2_INT_MASK_STATUS 0x218
+#define REG2_INT_RAW_STATUS 0x21c
+#define REG2_INT_CLEAR 0x220
+#define REG7_CACHE_SYNC 0x730
+#define REG7_INV_PA 0x770
+#define REG7_INV_WAY 0x77c
+#define REG7_CLEAN_PA 0x7b0
+#define REG7_CLEAN_INDEX 0x7b8
+#define REG7_CLEAN_WAY 0x7bc
+#define REG7_CLEAN_INV_PA 0x7f0
+#define REG7_CLEAN_INV_INDEX 0x7f8
+#define REG7_CLEAN_INV_WAY 0x7fc
+#define REG9_D_LOCKDOWN0 0x900
+#define REG9_I_LOCKDOWN0 0x904
+#define REG9_D_LOCKDOWN1 0x908
+#define REG9_I_LOCKDOWN1 0x90c
+#define REG9_D_LOCKDOWN2 0x910
+#define REG9_I_LOCKDOWN2 0x914
+#define REG9_D_LOCKDOWN3 0x918
+#define REG9_I_LOCKDOWN3 0x91c
+#define REG9_D_LOCKDOWN4 0x920
+#define REG9_I_LOCKDOWN4 0x924
+#define REG9_D_LOCKDOWN5 0x928
+#define REG9_I_LOCKDOWN5 0x92c
+#define REG9_D_LOCKDOWN6 0x930
+#define REG9_I_LOCKDOWN6 0x934
+#define REG9_D_LOCKDOWN7 0x938
+#define REG9_I_LOCKDOWN7 0x93c
+#define REG9_LOCK_LINE_EN 0x950
+#define REG9_UNLOCK_WAY 0x954
+#define REG12_ADDR_FILTERING_START 0xc00
+#define REG12_ADDR_FILTERING_END 0xc04
+#define REG15_DEBUG_CTRL 0xf40
+#define REG15_PREFETCH_CTRL 0xf60
+#define REG15_POWER_CTRL 0xf80
+
+static inline bool pl310_enabled(void)
+{
+ return !!(PL310_REG(REG1_CONTROL) & 1);
+}
+
+static void pl310_init(uint level)
+{
+ /* make sure it's already disabled */
+ DEBUG_ASSERT(!pl310_enabled());
+
+ /* set tag and data ram latency */
+ PL310_REG(REG1_TAG_RAM_CONTROL) = PL310_TAG_RAM_LATENCY;
+ PL310_REG(REG1_DATA_RAM_CONTROL) = PL310_DATA_RAM_LATENCY;
+
+ /* configure */
+ /* early BRESP enable, instruction/data prefetch, exclusive cache, full line of zero */
+ PL310_REG(REG1_AUX_CONTROL) |= (1<<30)|(1<<29)|(1<<28)|(1<<12)|(1<<0);
+
+ /* flush all the ways */
+ PL310_REG(REG7_INV_WAY) = 0xffff;
+}
+
+/* run just before arch_early_init so the L2 is ready to go when
+ * the arch code starts up the caching system.
+ */
+LK_INIT_HOOK(pl310_init, pl310_init, LK_INIT_LEVEL_ARCH_EARLY - 1);
+
+status_t pl310_set_enable(bool enable)
+{
+ LTRACEF("enable %d\n", enable);
+
+ if (enable) {
+ if ((PL310_REG(REG1_CONTROL) & 1) == 0) {
+ /* if disabled */
+ pl310_invalidate();
+ PL310_REG(REG1_CONTROL) = 1;
+ }
+ } else {
+ if ((PL310_REG(REG1_CONTROL) & 1) == 1) {
+ /* if enabled */
+ pl310_flush_invalidate();
+ PL310_REG(REG1_CONTROL) = 0;
+ /* this seems to not always latch on the first try */
+ while (PL310_REG(REG1_CONTROL) & 1) {
+ PL310_REG(REG1_CONTROL) = 0;
+ }
+ }
+ }
+
+ return NO_ERROR;
+}
+
+void pl310_invalidate(void)
+{
+ if (unlikely(!pl310_enabled()))
+ return;
+ PL310_REG(REG7_INV_WAY) = 0xffff;
+ while (PL310_REG(REG7_INV_WAY) != 0)
+ ;
+}
+
+void pl310_flush_invalidate(void)
+{
+ if (unlikely(!pl310_enabled()))
+ return;
+ PL310_REG(REG7_CLEAN_INV_WAY) = 0xffff;
+ while (PL310_REG(REG7_CLEAN_INV_WAY) != 0)
+ ;
+}
+
+void pl310_sync_range(void)
+{
+ if (unlikely(!pl310_enabled()))
+ return;
+
+ PL310_REG(REG7_CACHE_SYNC) = 1;
+}
+
+#define PL310_LOOP_BODY(reg) \
+ if (unlikely(!pl310_enabled())) \
+ return; \
+ \
+ addr_t pa = 0; \
+ uint32_t last_pa_page = 1; \
+ addr_t last_va = start + len; \
+ start &= ~(CACHE_LINE - 1); \
+ while (start < last_va) { \
+ if (unlikely(pa / PAGE_SIZE != last_pa_page)) { \
+ /* get the physical address */ \
+ if (unlikely(arm_vtop(start, &pa))) \
+ return; \
+ last_pa_page = pa / PAGE_SIZE; \
+ } \
+ PL310_REG(reg) = pa; \
+ \
+ pa += CACHE_LINE; \
+ start += CACHE_LINE; \
+ } \
+\
+ PL310_REG(REG7_CACHE_SYNC) = 1;
+
+void pl310_clean_range(addr_t start, size_t len)
+{
+ LTRACEF("start 0x%lx, len %zd\n", start, len);
+ PL310_LOOP_BODY(REG7_CLEAN_PA);
+}
+
+void pl310_clean_invalidate_range(addr_t start, size_t len)
+{
+ LTRACEF("start 0x%lx, len %zd\n", start, len);
+ PL310_LOOP_BODY(REG7_CLEAN_INV_PA);
+}
+
+void pl310_invalidate_range(addr_t start, size_t len)
+{
+ LTRACEF("start 0x%lx, len %zd\n", start, len);
+ PL310_LOOP_BODY(REG7_INV_PA);
+}
+
+void pl310_pin_cache_range(addr_t start, size_t len)
+{
+ len = ROUNDUP(len, CACHE_LINE);
+
+ arch_disable_ints();
+
+ arch_clean_invalidate_cache_range(start, len);
+
+ PL310_REG(REG9_LOCK_LINE_EN) = 1;
+ DSB;
+
+ while (len > 0) {
+ asm volatile("pld [%0]" :: "r"(start) : "memory");
+ start += CACHE_LINE;
+ len -= CACHE_LINE;
+ }
+
+ DSB;
+ PL310_REG(REG9_LOCK_LINE_EN) = 0;
+
+ arch_enable_ints();
+}
+
diff --git a/src/bsp/lk/dev/cache/pl310/rules.mk b/src/bsp/lk/dev/cache/pl310/rules.mk
new file mode 100644
index 0000000..36fef28
--- /dev/null
+++ b/src/bsp/lk/dev/cache/pl310/rules.mk
@@ -0,0 +1,10 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+GLOBAL_DEFINES +=
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/pl310.c
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/class/block_api.c b/src/bsp/lk/dev/class/block_api.c
new file mode 100644
index 0000000..014eaba
--- /dev/null
+++ b/src/bsp/lk/dev/class/block_api.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2013 Corey Tabaka
+ *
+ * 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 <dev/class/block.h>
+
+ssize_t class_block_get_size(struct device *dev)
+{
+ struct block_ops *ops = device_get_driver_ops(dev, struct block_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->get_block_size)
+ return ops->get_block_size(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+ssize_t class_block_get_count(struct device *dev)
+{
+ struct block_ops *ops = device_get_driver_ops(dev, struct block_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->get_block_count)
+ return ops->get_block_count(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+ssize_t class_block_write(struct device *dev, off_t offset, const void *buf, size_t count)
+{
+ struct block_ops *ops = device_get_driver_ops(dev, struct block_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->write)
+ return ops->write(dev, offset, buf, count);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+ssize_t class_block_read(struct device *dev, off_t offset, void *buf, size_t count)
+{
+ struct block_ops *ops = device_get_driver_ops(dev, struct block_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->read)
+ return ops->read(dev, offset, buf, count);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t class_block_flush(struct device *dev)
+{
+ struct block_ops *ops = device_get_driver_ops(dev, struct block_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->flush)
+ return ops->flush(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
diff --git a/src/bsp/lk/dev/class/fb_api.c b/src/bsp/lk/dev/class/fb_api.c
new file mode 100644
index 0000000..314442e
--- /dev/null
+++ b/src/bsp/lk/dev/class/fb_api.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013 Corey Tabaka
+ *
+ * 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 <dev/class/fb.h>
+
+status_t class_fb_set_mode(struct device *dev, size_t width, size_t height, size_t bpp)
+{
+ struct fb_ops *ops = device_get_driver_ops(dev, struct fb_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->set_mode)
+ return ops->set_mode(dev, width, height, bpp);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t class_fb_get_info(struct device *dev, struct fb_info *info)
+{
+ struct fb_ops *ops = device_get_driver_ops(dev, struct fb_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->get_info)
+ return ops->get_info(dev, info);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t class_fb_update(struct device *dev)
+{
+ struct fb_ops *ops = device_get_driver_ops(dev, struct fb_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->update)
+ return ops->update(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t class_fb_update_region(struct device *dev, size_t x, size_t y, size_t width, size_t height)
+{
+ struct fb_ops *ops = device_get_driver_ops(dev, struct fb_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->update_region)
+ return ops->update_region(dev, x, y, width, height);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
diff --git a/src/bsp/lk/dev/class/i2c_api.c b/src/bsp/lk/dev/class/i2c_api.c
new file mode 100644
index 0000000..60e2a8a
--- /dev/null
+++ b/src/bsp/lk/dev/class/i2c_api.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (c) 2013 Corey Tabaka
+ *
+ * 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 <dev/class/i2c.h>
+
+status_t class_i2c_write(struct device *dev, uint8_t addr, const void *buf, size_t len)
+{
+ struct i2c_ops *ops = device_get_driver_ops(dev, struct i2c_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->write)
+ return ops->write(dev, addr, buf, len);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t class_i2c_read(struct device *dev, uint8_t addr, void *buf, size_t len)
+{
+ struct i2c_ops *ops = device_get_driver_ops(dev, struct i2c_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->read)
+ return ops->read(dev, addr, buf, len);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t class_i2c_write_reg(struct device *dev, uint8_t addr, uint8_t reg, uint8_t value)
+{
+ struct i2c_ops *ops = device_get_driver_ops(dev, struct i2c_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->write_reg)
+ return ops->write_reg(dev, addr, reg, value);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t class_i2c_read_reg(struct device *dev, uint8_t addr, uint8_t reg, void *value)
+{
+ struct i2c_ops *ops = device_get_driver_ops(dev, struct i2c_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->read_reg)
+ return ops->read_reg(dev, addr, reg, value);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
diff --git a/src/bsp/lk/dev/class/netif_api.c b/src/bsp/lk/dev/class/netif_api.c
new file mode 100644
index 0000000..87d617b
--- /dev/null
+++ b/src/bsp/lk/dev/class/netif_api.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2013 Corey Tabaka
+ *
+ * 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 <dev/class/netif.h>
+
+status_t class_netif_set_state(struct device *dev, struct netstack_state *state)
+{
+ struct netif_ops *ops = device_get_driver_ops(dev, struct netif_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->set_state)
+ return ops->set_state(dev, state);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+ssize_t class_netif_get_hwaddr(struct device *dev, void *buf, size_t max_len)
+{
+ struct netif_ops *ops = device_get_driver_ops(dev, struct netif_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->get_hwaddr)
+ return ops->get_hwaddr(dev, buf, max_len);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+ssize_t class_netif_get_mtu(struct device *dev)
+{
+ struct netif_ops *ops = device_get_driver_ops(dev, struct netif_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->get_mtu)
+ return ops->get_mtu(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t class_netif_set_status(struct device *dev, bool up)
+{
+ struct netif_ops *ops = device_get_driver_ops(dev, struct netif_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->set_status)
+ return ops->set_status(dev, up);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t class_netif_output(struct device *dev, struct pbuf *p)
+{
+ struct netif_ops *ops = device_get_driver_ops(dev, struct netif_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->output)
+ return ops->output(dev, p);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t class_netif_mcast_filter(struct device *dev, const uint8_t *mac, int action)
+{
+ struct netif_ops *ops = device_get_driver_ops(dev, struct netif_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->mcast_filter)
+ return ops->mcast_filter(dev, mac, action);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
diff --git a/src/bsp/lk/dev/class/spi_api.c b/src/bsp/lk/dev/class/spi_api.c
new file mode 100644
index 0000000..9453dc3
--- /dev/null
+++ b/src/bsp/lk/dev/class/spi_api.c
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013 Corey Tabaka
+ *
+ * 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 <dev/class/spi.h>
+
+ssize_t class_spi_transaction(struct device *dev, struct spi_transaction *txn, size_t count)
+{
+ struct spi_ops *ops = device_get_driver_ops(dev, struct spi_ops, std);
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->transaction)
+ return ops->transaction(dev, txn, count);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
diff --git a/src/bsp/lk/dev/class/uart_api.c b/src/bsp/lk/dev/class/uart_api.c
new file mode 100644
index 0000000..31e2c9d
--- /dev/null
+++ b/src/bsp/lk/dev/class/uart_api.c
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2013 Corey Tabaka
+ *
+ * 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 <dev/class/uart.h>
+
+ssize_t class_uart_read(struct device *dev, void *buf, size_t len)
+{
+ struct uart_ops *ops = device_get_driver_ops(dev, struct uart_ops, std);
+
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->read)
+ return ops->read(dev, buf, len);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+ssize_t class_uart_write(struct device *dev, const void *buf, size_t len)
+{
+ struct uart_ops *ops = device_get_driver_ops(dev, struct uart_ops, std);
+
+ if (!ops)
+ return ERR_NOT_CONFIGURED;
+
+ if (ops->write)
+ return ops->write(dev, buf, len);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
diff --git a/src/bsp/lk/dev/dev.c b/src/bsp/lk/dev/dev.c
new file mode 100644
index 0000000..5288b51
--- /dev/null
+++ b/src/bsp/lk/dev/dev.c
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2008 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.
+ */
+
+void dev_init(void)
+{
+
+}
+
diff --git a/src/bsp/lk/dev/devices.ld b/src/bsp/lk/dev/devices.ld
new file mode 100644
index 0000000..1e16e65
--- /dev/null
+++ b/src/bsp/lk/dev/devices.ld
@@ -0,0 +1,8 @@
+SECTIONS {
+ .devices : {
+ __devices = .;
+ KEEP (*(.devices))
+ __devices_end = .;
+ }
+}
+INSERT AFTER .data;
diff --git a/src/bsp/lk/dev/driver.c b/src/bsp/lk/dev/driver.c
new file mode 100644
index 0000000..4b31ac1
--- /dev/null
+++ b/src/bsp/lk/dev/driver.c
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2012 Corey Tabaka
+ *
+ * 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/driver.h>
+#include <assert.h>
+#include <err.h>
+#include <trace.h>
+
+extern struct device __devices[];
+extern struct device __devices_end[];
+
+status_t device_init_all(void)
+{
+ status_t res = NO_ERROR;
+
+ struct device *dev = __devices;
+ while (dev != __devices_end) {
+ status_t code = device_init(dev);
+
+ if (code < 0) {
+ TRACEF("Driver init failed for driver \"%s\", device \"%s\", reason %d\n",
+ dev->driver->type, dev->name, code);
+
+ res = code;
+ }
+
+ dev++;
+ }
+
+ return res;
+}
+
+status_t device_fini_all(void)
+{
+ status_t res = NO_ERROR;
+
+ struct device *dev = __devices;
+ while (dev != __devices_end) {
+ status_t code = device_fini(dev);
+
+ if (code < 0) {
+ TRACEF("Driver fini failed for driver \"%s\", device \"%s\", reason %d\n",
+ dev->driver->type, dev->name, code);
+
+ res = code;
+ }
+
+ dev++;
+ }
+
+ return res;
+}
+
+status_t device_init(struct device *dev)
+{
+ if (!dev)
+ return ERR_INVALID_ARGS;
+
+ DEBUG_ASSERT(dev->driver);
+
+ const struct driver_ops *ops = dev->driver->ops;
+
+ if (ops && ops->init)
+ return ops->init(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t device_fini(struct device *dev)
+{
+ if (!dev)
+ return ERR_INVALID_ARGS;
+
+ DEBUG_ASSERT(dev->driver);
+
+ const struct driver_ops *ops = dev->driver->ops;
+
+ if (ops && ops->fini)
+ return ops->fini(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t device_suspend(struct device *dev)
+{
+ if (!dev)
+ return ERR_NOT_SUPPORTED;
+
+ DEBUG_ASSERT(dev->driver);
+
+ const struct driver_ops *ops = dev->driver->ops;
+
+ if (ops && ops->suspend)
+ return ops->suspend(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
+status_t device_resume(struct device *dev)
+{
+ if (!dev)
+ return ERR_NOT_SUPPORTED;
+
+ DEBUG_ASSERT(dev->driver);
+
+ const struct driver_ops *ops = dev->driver->ops;
+
+ if (ops && ops->resume)
+ return ops->resume(dev);
+ else
+ return ERR_NOT_SUPPORTED;
+}
+
diff --git a/src/bsp/lk/dev/drivers.ld b/src/bsp/lk/dev/drivers.ld
new file mode 100644
index 0000000..62b7fff
--- /dev/null
+++ b/src/bsp/lk/dev/drivers.ld
@@ -0,0 +1,8 @@
+SECTIONS {
+ .drivers : {
+ __drivers = .;
+ KEEP (*(.drivers))
+ __drivers_end = .;
+ }
+}
+INSERT AFTER .rodata;
diff --git a/src/bsp/lk/dev/fbcon/fbcon.c b/src/bsp/lk/dev/fbcon/fbcon.c
new file mode 100644
index 0000000..796fc6f
--- /dev/null
+++ b/src/bsp/lk/dev/fbcon/fbcon.c
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2008, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <debug.h>
+#include <err.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <dev/fbcon.h>
+
+#include "font5x12.h"
+
+struct pos {
+ int x;
+ int y;
+};
+
+static struct fbcon_config *config = NULL;
+
+#define RGB565_BLUE 0x001f
+#define RGB565_WHITE 0xffff
+
+#define FONT_WIDTH 5
+#define FONT_HEIGHT 12
+
+static uint16_t BGCOLOR;
+static uint16_t FGCOLOR;
+
+static struct pos cur_pos;
+static struct pos max_pos;
+
+static void fbcon_drawglyph(uint16_t *pixels, uint16_t paint, unsigned stride,
+ unsigned *glyph)
+{
+ unsigned x, y, data;
+ stride -= FONT_WIDTH;
+
+ data = glyph[0];
+ for (y = 0; y < (FONT_HEIGHT / 2); ++y) {
+ for (x = 0; x < FONT_WIDTH; ++x) {
+ if (data & 1)
+ *pixels = paint;
+ data >>= 1;
+ pixels++;
+ }
+ pixels += stride;
+ }
+
+ data = glyph[1];
+ for (y = 0; y < (FONT_HEIGHT / 2); y++) {
+ for (x = 0; x < FONT_WIDTH; x++) {
+ if (data & 1)
+ *pixels = paint;
+ data >>= 1;
+ pixels++;
+ }
+ pixels += stride;
+ }
+}
+
+static void fbcon_flush(void)
+{
+ if (config->update_start)
+ config->update_start();
+ if (config->update_done)
+ while (!config->update_done());
+}
+
+/* TODO: Take stride into account */
+static void fbcon_scroll_up(void)
+{
+ unsigned short *dst = config->base;
+ unsigned short *src = dst + (config->width * FONT_HEIGHT);
+ unsigned count = config->width * (config->height - FONT_HEIGHT);
+
+ while (count--) {
+ *dst++ = *src++;
+ }
+
+ count = config->width * FONT_HEIGHT;
+ while (count--) {
+ *dst++ = BGCOLOR;
+ }
+
+ fbcon_flush();
+}
+
+/* TODO: take stride into account */
+static void fbcon_clear(void)
+{
+ uint16_t *dst = config->base;
+ unsigned count = config->width * config->height;
+
+ cur_pos.x = 0;
+ cur_pos.y = 0;
+
+ while (count--)
+ *dst++ = BGCOLOR;
+}
+
+
+static void fbcon_set_colors(unsigned bg, unsigned fg)
+{
+ BGCOLOR = bg;
+ FGCOLOR = fg;
+}
+
+void fbcon_putc(char c)
+{
+ uint16_t *pixels;
+
+ /* ignore anything that happens before fbcon is initialized */
+ if (!config)
+ return;
+
+ if ((unsigned char)c > 127)
+ return;
+ if ((unsigned char)c < 32) {
+ if (c == '\n')
+ goto newline;
+ else if (c == '\r')
+ cur_pos.x = 0;
+ return;
+ }
+
+ pixels = config->base;
+ pixels += cur_pos.y * FONT_HEIGHT * config->width;
+ pixels += cur_pos.x * (FONT_WIDTH + 1);
+ fbcon_drawglyph(pixels, FGCOLOR, config->stride,
+ font5x12 + (c - 32) * 2);
+
+ cur_pos.x++;
+ if (cur_pos.x < max_pos.x)
+ return;
+
+newline:
+ cur_pos.y++;
+ cur_pos.x = 0;
+ if (cur_pos.y >= max_pos.y) {
+ cur_pos.y = max_pos.y - 1;
+ fbcon_scroll_up();
+ } else
+ fbcon_flush();
+}
+
+void fbcon_setup(struct fbcon_config *_config)
+{
+ uint32_t bg;
+ uint32_t fg;
+
+ ASSERT(_config);
+
+ config = _config;
+
+ switch (config->format) {
+ case FB_FORMAT_RGB565:
+ bg = RGB565_BLUE;
+ fg = RGB565_WHITE;
+ break;
+
+ default:
+ dprintf(CRITICAL, "unknown framebuffer pixel format\n");
+ ASSERT(0);
+ break;
+ }
+
+ fbcon_set_colors(bg, fg);
+
+ fbcon_clear();
+ fbcon_flush();
+
+ cur_pos.x = 0;
+ cur_pos.y = 0;
+ max_pos.x = config->width / (FONT_WIDTH+1);
+ max_pos.y = (config->height - 1) / FONT_HEIGHT;
+}
diff --git a/src/bsp/lk/dev/fbcon/font5x12.h b/src/bsp/lk/dev/fbcon/font5x12.h
new file mode 100644
index 0000000..a7d09aa
--- /dev/null
+++ b/src/bsp/lk/dev/fbcon/font5x12.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+unsigned font5x12[] = {
+ 0x00000000, 0x00000000,
+ 0x08421080, 0x00020084,
+ 0x00052940, 0x00000000,
+ 0x15f52800, 0x0000295f,
+ 0x1c52f880, 0x00023e94,
+ 0x08855640, 0x0004d542,
+ 0x04528800, 0x000b2725,
+ 0x00021080, 0x00000000,
+ 0x04211088, 0x00821042,
+ 0x10841082, 0x00221108,
+ 0x09575480, 0x00000000,
+ 0x3e420000, 0x00000084,
+ 0x00000000, 0x00223000,
+ 0x3e000000, 0x00000000,
+ 0x00000000, 0x00471000,
+ 0x08844200, 0x00008442,
+ 0x2318a880, 0x00022a31,
+ 0x08429880, 0x000f9084,
+ 0x1108c5c0, 0x000f8444,
+ 0x1c4443e0, 0x00074610,
+ 0x14a62100, 0x000423e9,
+ 0x26d087e0, 0x00074610,
+ 0x1e10c5c0, 0x00074631,
+ 0x088443e0, 0x00010844,
+ 0x1d18c5c0, 0x00074631,
+ 0x3d18c5c0, 0x00074610,
+ 0x08e20000, 0x00471000,
+ 0x08e20000, 0x00223000,
+ 0x02222200, 0x00082082,
+ 0x01f00000, 0x000003e0,
+ 0x20820820, 0x00008888,
+ 0x1108c5c0, 0x00020084,
+ 0x2b98c5c0, 0x000f05b5,
+ 0x2318a880, 0x0008c63f,
+ 0x1d2949e0, 0x0007ca52,
+ 0x0210c5c0, 0x00074421,
+ 0x252949e0, 0x0007ca52,
+ 0x1e1087e0, 0x000f8421,
+ 0x1e1087e0, 0x00008421,
+ 0x0210c5c0, 0x00074639,
+ 0x3f18c620, 0x0008c631,
+ 0x084211c0, 0x00071084,
+ 0x10842380, 0x00032508,
+ 0x0654c620, 0x0008c525,
+ 0x02108420, 0x000f8421,
+ 0x2b5dc620, 0x0008c631,
+ 0x2b59ce20, 0x0008c739,
+ 0x2318c5c0, 0x00074631,
+ 0x1f18c5e0, 0x00008421,
+ 0x2318c5c0, 0x01075631,
+ 0x1f18c5e0, 0x0008c525,
+ 0x1c10c5c0, 0x00074610,
+ 0x084213e0, 0x00021084,
+ 0x2318c620, 0x00074631,
+ 0x1518c620, 0x0002114a,
+ 0x2b18c620, 0x000556b5,
+ 0x08a54620, 0x0008c54a,
+ 0x08a54620, 0x00021084,
+ 0x088443e0, 0x000f8442,
+ 0x0421084e, 0x00e10842,
+ 0x08210420, 0x00084108,
+ 0x1084210e, 0x00e42108,
+ 0x0008a880, 0x00000000,
+ 0x00000000, 0x01f00000,
+ 0x00000104, 0x00000000,
+ 0x20e00000, 0x000b663e,
+ 0x22f08420, 0x0007c631,
+ 0x22e00000, 0x00074421,
+ 0x23e84200, 0x000f4631,
+ 0x22e00000, 0x0007443f,
+ 0x1e214980, 0x00010842,
+ 0x22e00000, 0x1d187a31,
+ 0x26d08420, 0x0008c631,
+ 0x08601000, 0x00071084,
+ 0x10c02000, 0x0c94a108,
+ 0x0a908420, 0x0008a4a3,
+ 0x084210c0, 0x00071084,
+ 0x2ab00000, 0x0008d6b5,
+ 0x26d00000, 0x0008c631,
+ 0x22e00000, 0x00074631,
+ 0x22f00000, 0x0210be31,
+ 0x23e00000, 0x21087a31,
+ 0x26d00000, 0x00008421,
+ 0x22e00000, 0x00074506,
+ 0x04f10800, 0x00064842,
+ 0x23100000, 0x000b6631,
+ 0x23100000, 0x00022951,
+ 0x23100000, 0x000556b5,
+ 0x15100000, 0x0008a884,
+ 0x23100000, 0x1d185b31,
+ 0x11f00000, 0x000f8444,
+ 0x06421098, 0x01821084,
+ 0x08421080, 0x00021084,
+ 0x30421083, 0x00321084,
+ 0x0004d640, 0x00000000,
+ 0x00000000, 0x00000000,
+};
diff --git a/src/bsp/lk/dev/fbcon/rules.mk b/src/bsp/lk/dev/fbcon/rules.mk
new file mode 100644
index 0000000..18e1082
--- /dev/null
+++ b/src/bsp/lk/dev/fbcon/rules.mk
@@ -0,0 +1,8 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/fbcon.c
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/gpio/debug.c b/src/bsp/lk/dev/gpio/debug.c
new file mode 100644
index 0000000..9a32ae5
--- /dev/null
+++ b/src/bsp/lk/dev/gpio/debug.c
@@ -0,0 +1,100 @@
+/*
+ * 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 <dev/gpio.h>
+#include <lib/console.h>
+#include <string.h>
+#include <stdio.h>
+
+#if WITH_LIB_CONSOLE
+
+struct flag_labels {
+ unsigned id;
+ const char *label;
+};
+
+struct flag_labels gpio_flag_labels[] = {
+ { GPIO_INPUT, "input" },
+ { GPIO_OUTPUT, "output" },
+ { GPIO_LEVEL, "level" },
+ { GPIO_EDGE, "edge" },
+ { GPIO_RISING, "rising" },
+ { GPIO_FALLING, "falling" },
+ { GPIO_HIGH, "high" },
+ { GPIO_LOW, "low" },
+ { GPIO_PULLUP, "pullup" },
+ { GPIO_PULLDOWN, "pulldown" },
+};
+
+static unsigned int get_flag_value(const char *str)
+{
+ for (unsigned i = 0; i < countof(gpio_flag_labels); i++) {
+ if (!strcmp(str, gpio_flag_labels[i].label)) {
+ return gpio_flag_labels[i].id;
+ }
+ }
+
+ return 0;
+}
+
+/* Ideally this would be generic, but different platforms have varying manners of handling gpio
+ * numbers / banks etc. Nvidia uses A-F, ST uses # and ports, Xilinx uses #s and banks. Etc.
+ */
+static int cmd_gpio(int argc, const cmd_args *argv)
+{
+ if (argc == 4 && !strcmp(argv[1].str,"set")) {
+ unsigned int gpio = argv[2].u;
+ unsigned int value = argv[3].u;
+
+ gpio_set(gpio, value);
+ } else if (argc == 3 && !strcmp(argv[1].str,"get")) {
+ unsigned int gpio = argv[2].u;
+
+ printf("gpio %u: %d\n", gpio, gpio_get(gpio));
+ } else if (argc >= 3 && !strcmp(argv[1].str,"cfg")) {
+ unsigned int gpio = argv[2].u;
+ unsigned int flags = 0;
+
+ for (int i = 3; i < argc; i++) {
+ flags |= get_flag_value(argv[i].str);
+ }
+
+ gpio_config(gpio, flags);
+ } else {
+ printf("gpio set <gpio #> <value>\n");
+ printf("gpio get <gpio #>\n");
+ printf("gpio cfg <gpio #> [flags: ");
+ for (unsigned int i = 0; i < countof(gpio_flag_labels); i++) {
+ printf("%s ", gpio_flag_labels[i].label);
+ }
+ putchar(']');
+ putchar('\n');
+ }
+
+ return 0;
+}
+STATIC_COMMAND_START
+STATIC_COMMAND("gpio", "commands for manipulating system gpios", &cmd_gpio)
+STATIC_COMMAND_END(gpio);
+
+#endif
diff --git a/src/bsp/lk/dev/gpio/rules.mk b/src/bsp/lk/dev/gpio/rules.mk
new file mode 100644
index 0000000..fcee316
--- /dev/null
+++ b/src/bsp/lk/dev/gpio/rules.mk
@@ -0,0 +1,9 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/debug.c
+
+include make/module.mk
+
diff --git a/src/bsp/lk/dev/gpio_i2c/gpio_i2c.c b/src/bsp/lk/dev/gpio_i2c/gpio_i2c.c
new file mode 100644
index 0000000..b182930
--- /dev/null
+++ b/src/bsp/lk/dev/gpio_i2c/gpio_i2c.c
@@ -0,0 +1,287 @@
+/* vim: set expandtab ts=4 sw=4 tw=100: */
+/*
+ * Copyright (c) 2013 Google Inc.
+ *
+ * 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 <compiler.h>
+#include <debug.h>
+#include <printf.h>
+#include <err.h>
+
+#include <dev/gpio.h>
+#include <dev/gpio_i2c.h>
+#include <kernel/mutex.h>
+
+#if (!(defined(GPIO_I2C_BUS_COUNT)) || (GPIO_I2C_BUS_COUNT <= 0))
+#error ERROR: Must define GPIO_I2C_BUS_COUNT
+#endif
+
+typedef struct gpio_i2c_state {
+ mutex_t lock;
+ const gpio_i2c_info_t* info;
+} gpio_i2c_state_t;
+
+static gpio_i2c_state_t gpio_i2c_states[GPIO_I2C_BUS_COUNT];
+
+/******************************************************************************
+ *
+ * Internal implementation.
+ *
+ ******************************************************************************/
+static inline void send_start(const gpio_i2c_info_t* i)
+{
+ gpio_config(i->sda, GPIO_OUTPUT);
+ spin_cycles(i->qcd);
+ gpio_config(i->scl, GPIO_OUTPUT);
+ spin_cycles(i->hcd);
+}
+
+static inline void send_stop(const gpio_i2c_info_t* i)
+{
+ gpio_config(i->sda, GPIO_OUTPUT);
+ gpio_config(i->scl, GPIO_INPUT);
+ spin_cycles(i->qcd);
+ gpio_config(i->sda, GPIO_INPUT);
+}
+
+static inline void send_restart(const gpio_i2c_info_t* i)
+{
+ gpio_config(i->scl, GPIO_INPUT);
+ spin_cycles(i->qcd);
+ send_start(i);
+}
+
+static inline void send_nack(const gpio_i2c_info_t* i)
+{
+ spin_cycles(i->hcd);
+ gpio_config(i->scl, GPIO_INPUT);
+ spin_cycles(i->hcd);
+ gpio_config(i->scl, GPIO_OUTPUT);
+ gpio_config(i->sda, GPIO_INPUT);
+}
+
+static inline void send_ack(const gpio_i2c_info_t* i)
+{
+ gpio_config(i->sda, GPIO_OUTPUT);
+ send_nack(i);
+}
+
+static inline bool send_byte(const gpio_i2c_info_t* i, uint32_t b)
+{
+ bool ret;
+
+ for (size_t j = 0; j < 8; ++j) {
+ if (b & 0x80)
+ gpio_config(i->sda, GPIO_INPUT);
+ else
+ gpio_config(i->sda, GPIO_OUTPUT);
+ b <<= 1;
+ /* setup time for data (the time between when data becomes stable and
+ * clock becomes a stable high) is spec'ed to be 250ns for 100KHz i2c
+ * and 100nsec for 400KHz i2c. If any micro running LK needs to spin
+ * here in order to hit that timing, they are welcome to add a spin
+ * right here.
+ */
+ spin_cycles(i->hcd);
+ gpio_config(i->scl, GPIO_INPUT);
+ spin_cycles(i->hcd);
+ gpio_config(i->scl, GPIO_OUTPUT);
+ }
+
+ gpio_config(i->sda, GPIO_INPUT);
+ spin_cycles(i->hcd);
+ gpio_config(i->scl, GPIO_INPUT);
+ spin_cycles(i->hcd);
+ ret = (0 == gpio_get(i->sda));
+ gpio_config(i->scl, GPIO_OUTPUT);
+ spin_cycles(i->hcd);
+
+ return ret;
+}
+
+static inline void recv_byte(const gpio_i2c_info_t* i, uint8_t* b)
+{
+ uint32_t tmp = 0;
+
+ for (size_t j = 0; j < 7; ++j) {
+ gpio_config(i->scl, GPIO_INPUT);
+ spin_cycles(i->hcd);
+ if (gpio_get(i->sda))
+ tmp |= 1;
+ tmp <<= 1;
+ gpio_config(i->scl, GPIO_OUTPUT);
+ spin_cycles(i->hcd);
+ }
+
+ gpio_config(i->scl, GPIO_INPUT);
+ spin_cycles(i->hcd);
+ if (gpio_get(i->sda))
+ tmp |= 1;
+ gpio_config(i->scl, GPIO_OUTPUT);
+
+ *b = (uint8_t)tmp;
+}
+
+static status_t gpio_i2c_tx_common(gpio_i2c_state_t* s,
+ uint8_t address,
+ const uint8_t* reg,
+ const void* buf,
+ size_t cnt)
+{
+ const gpio_i2c_info_t* i = s->info;
+ status_t ret = ERR_I2C_NACK;
+
+ DEBUG_ASSERT(buf || !cnt);
+
+ mutex_acquire(&s->lock);
+ send_start(i);
+ if (!send_byte(i, address << 1))
+ goto finished;
+
+ if ((NULL != reg) && !send_byte(i, *reg))
+ goto finished;
+
+ for (size_t j = 0; j < cnt; ++j)
+ if (!send_byte(i, ((const uint8_t*)buf)[j]))
+ goto finished;
+
+ ret = NO_ERROR;
+
+finished:
+ send_stop(i);
+ mutex_release(&s->lock);
+ return ret;
+}
+
+static status_t gpio_i2c_rx_common(gpio_i2c_state_t* s,
+ uint8_t address,
+ const uint8_t* reg,
+ void* buf,
+ size_t cnt)
+{
+ const gpio_i2c_info_t* i = s->info;
+ status_t ret = ERR_I2C_NACK;
+
+ DEBUG_ASSERT(buf && cnt);
+
+ address <<= 1;
+
+ mutex_acquire(&s->lock);
+ send_start(i);
+ if (!send_byte(i, address | (!reg ? 0x1 : 0x0)))
+ goto finished;
+
+ if (NULL != reg) {
+ if (!send_byte(i, *reg))
+ goto finished;
+
+ send_restart(i);
+
+ if (!send_byte(i, address | 0x1))
+ goto finished;
+ }
+
+ recv_byte(i, buf++);
+ for (size_t j = 0; j < (cnt - 1); ++j) {
+ send_ack(i);
+ recv_byte(i, buf++);
+ }
+ send_nack(i);
+ ret = NO_ERROR;
+
+finished:
+ send_stop(i);
+ mutex_release(&s->lock);
+ return ret;
+}
+
+void gpio_i2c_add_bus(uint32_t bus_id, const gpio_i2c_info_t* info)
+{
+ gpio_i2c_state_t* s = gpio_i2c_states + bus_id;
+
+ DEBUG_ASSERT(info);
+ DEBUG_ASSERT(bus_id < GPIO_I2C_BUS_COUNT);
+ DEBUG_ASSERT(!s->info);
+
+ gpio_config(info->scl, GPIO_INPUT);
+ gpio_config(info->sda, GPIO_INPUT);
+ gpio_set(info->scl, 0);
+ gpio_set(info->sda, 0);
+
+ mutex_init(&s->lock);
+ s->info = info;
+}
+
+/******************************************************************************
+*
+* LK facing API
+*
+* *****************************************************************************/
+void gpio_i2c_init_early(void) { }
+void gpio_i2c_init(void) { }
+
+status_t gpio_i2c_transmit(int bus, uint8_t address, const void* buf, size_t cnt)
+{
+ gpio_i2c_state_t* s = gpio_i2c_states + bus;
+ if (((unsigned)bus >= countof(gpio_i2c_states)) || !s->info)
+ return ERR_NOT_FOUND;
+
+ return gpio_i2c_tx_common(s, address, NULL, buf, cnt);
+}
+
+status_t gpio_i2c_receive(int bus, uint8_t address, void* buf, size_t cnt)
+{
+ gpio_i2c_state_t* s = gpio_i2c_states + bus;
+ if (((unsigned)bus >= countof(gpio_i2c_states)) || !s->info)
+ return ERR_NOT_FOUND;
+
+ return gpio_i2c_rx_common(s, address, NULL, buf, cnt);
+}
+
+status_t gpio_i2c_write_reg_bytes(int bus, uint8_t address, uint8_t reg, const uint8_t* buf, size_t cnt)
+{
+ gpio_i2c_state_t* s = gpio_i2c_states + bus;
+ if (((unsigned)bus >= countof(gpio_i2c_states)) || !s->info)
+ return ERR_NOT_FOUND;
+
+ return gpio_i2c_tx_common(s, address, ®, buf, cnt);
+}
+
+status_t gpio_i2c_read_reg_bytes(int bus, uint8_t address, uint8_t reg, uint8_t* buf, size_t cnt)
+{
+ gpio_i2c_state_t* s = gpio_i2c_states + bus;
+ if (((unsigned)bus >= countof(gpio_i2c_states)) || !s->info) {
+ return ERR_NOT_FOUND;
+ }
+
+ return gpio_i2c_rx_common(s, address, ®, buf, cnt);
+}
+
+void i2c_init_early(void) __WEAK_ALIAS("gpio_i2c_init_early");
+void i2c_init(void) __WEAK_ALIAS("gpio_i2c_init");
+status_t i2c_transmit(int, uint8_t, const void*, size_t) __WEAK_ALIAS("gpio_i2c_transmit");
+status_t i2c_receive(int, uint8_t, void*, size_t) __WEAK_ALIAS("gpio_i2c_receive");
+status_t i2c_write_reg_bytes(int, uint8_t, uint8_t,
+ const uint8_t*, size_t) __WEAK_ALIAS("gpio_i2c_write_reg_bytes");
+status_t i2c_read_reg_bytes(int, uint8_t, uint8_t,
+ uint8_t*, size_t) __WEAK_ALIAS("gpio_i2c_read_reg_bytes");
diff --git a/src/bsp/lk/dev/gpio_i2c/rules.mk b/src/bsp/lk/dev/gpio_i2c/rules.mk
new file mode 100644
index 0000000..57dbbca
--- /dev/null
+++ b/src/bsp/lk/dev/gpio_i2c/rules.mk
@@ -0,0 +1,11 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS := \
+ $(LOCAL_DIR)/gpio_i2c.c
+
+MODULE_DEFINES += \
+ GPIO_I2C_BUS_COUNT=$(GPIO_I2C_BUS_COUNT)
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/interrupt/arm_gic/arm_gic.c b/src/bsp/lk/dev/interrupt/arm_gic/arm_gic.c
new file mode 100644
index 0000000..b16a603
--- /dev/null
+++ b/src/bsp/lk/dev/interrupt/arm_gic/arm_gic.c
@@ -0,0 +1,618 @@
+/*
+ * 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 <assert.h>
+#include <bits.h>
+#include <err.h>
+#include <sys/types.h>
+#include <debug.h>
+#include <dev/interrupt/arm_gic.h>
+#include <reg.h>
+#include <kernel/thread.h>
+#include <kernel/debug.h>
+#include <lk/init.h>
+#include <platform/interrupts.h>
+#include <arch/ops.h>
+#include <platform/gic.h>
+#include <trace.h>
+#if WITH_LIB_SM
+#include <lib/sm.h>
+#include <lib/sm/sm_err.h>
+#endif
+
+#define LOCAL_TRACE 0
+
+#if ARCH_ARM
+#include <arch/arm.h>
+#define iframe arm_iframe
+#define IFRAME_PC(frame) ((frame)->pc)
+#endif
+#if ARCH_ARM64
+#include <arch/arm64.h>
+#define iframe arm64_iframe_short
+#define IFRAME_PC(frame) ((frame)->elr)
+#endif
+
+static status_t arm_gic_set_secure_locked(u_int irq, bool secure);
+
+static spin_lock_t gicd_lock;
+#if WITH_LIB_SM
+#define GICD_LOCK_FLAGS SPIN_LOCK_FLAG_IRQ_FIQ
+#else
+#define GICD_LOCK_FLAGS SPIN_LOCK_FLAG_INTERRUPTS
+#endif
+#define GIC_MAX_PER_CPU_INT 32
+
+#if WITH_LIB_SM
+static bool arm_gic_non_secure_interrupts_frozen;
+
+static bool arm_gic_interrupt_change_allowed(int irq)
+{
+ if (!arm_gic_non_secure_interrupts_frozen)
+ return true;
+
+ TRACEF("change to interrupt %d ignored after booting ns\n", irq);
+ return false;
+}
+
+static void suspend_resume_fiq(bool resume_gicc, bool resume_gicd);
+#else
+static bool arm_gic_interrupt_change_allowed(int irq)
+{
+ return true;
+}
+
+static void suspend_resume_fiq(bool resume_gicc, bool resume_gicd)
+{
+}
+#endif
+
+
+struct int_handler_struct {
+ int_handler handler;
+ void *arg;
+};
+
+static struct int_handler_struct int_handler_table_per_cpu[GIC_MAX_PER_CPU_INT][SMP_MAX_CPUS];
+static struct int_handler_struct int_handler_table_shared[MAX_INT-GIC_MAX_PER_CPU_INT];
+
+static struct int_handler_struct *get_int_handler(unsigned int vector, uint cpu)
+{
+ if (vector < GIC_MAX_PER_CPU_INT)
+ return &int_handler_table_per_cpu[vector][cpu];
+ else
+ return &int_handler_table_shared[vector - GIC_MAX_PER_CPU_INT];
+}
+
+void register_int_handler(unsigned int vector, int_handler handler, void *arg)
+{
+ struct int_handler_struct *h;
+ uint cpu = arch_curr_cpu_num();
+
+ spin_lock_saved_state_t state;
+
+ if (vector >= MAX_INT)
+ panic("register_int_handler: vector out of range %d\n", vector);
+
+ spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS);
+
+ if (arm_gic_interrupt_change_allowed(vector)) {
+ h = get_int_handler(vector, cpu);
+ h->handler = handler;
+ h->arg = arg;
+ }
+
+ spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS);
+}
+
+#define GICREG(gic, reg) (*REG32(GICBASE(gic) + (reg)))
+
+/* main cpu regs */
+#define GICC_CTLR (GICC_OFFSET + 0x0000)
+#define GICC_PMR (GICC_OFFSET + 0x0004)
+#define GICC_BPR (GICC_OFFSET + 0x0008)
+#define GICC_IAR (GICC_OFFSET + 0x000c)
+#define GICC_EOIR (GICC_OFFSET + 0x0010)
+#define GICC_RPR (GICC_OFFSET + 0x0014)
+#define GICC_HPPIR (GICC_OFFSET + 0x0018)
+#define GICC_APBR (GICC_OFFSET + 0x001c)
+#define GICC_AIAR (GICC_OFFSET + 0x0020)
+#define GICC_AEOIR (GICC_OFFSET + 0x0024)
+#define GICC_AHPPIR (GICC_OFFSET + 0x0028)
+#define GICC_APR(n) (GICC_OFFSET + 0x00d0 + (n) * 4)
+#define GICC_NSAPR(n) (GICC_OFFSET + 0x00e0 + (n) * 4)
+#define GICC_IIDR (GICC_OFFSET + 0x00fc)
+#define GICC_DIR (GICC_OFFSET + 0x1000)
+
+/* distribution regs */
+#define GICD_CTLR (GICD_OFFSET + 0x000)
+#define GICD_TYPER (GICD_OFFSET + 0x004)
+#define GICD_IIDR (GICD_OFFSET + 0x008)
+#define GICD_IGROUPR(n) (GICD_OFFSET + 0x080 + (n) * 4)
+#define GICD_ISENABLER(n) (GICD_OFFSET + 0x100 + (n) * 4)
+#define GICD_ICENABLER(n) (GICD_OFFSET + 0x180 + (n) * 4)
+#define GICD_ISPENDR(n) (GICD_OFFSET + 0x200 + (n) * 4)
+#define GICD_ICPENDR(n) (GICD_OFFSET + 0x280 + (n) * 4)
+#define GICD_ISACTIVER(n) (GICD_OFFSET + 0x300 + (n) * 4)
+#define GICD_ICACTIVER(n) (GICD_OFFSET + 0x380 + (n) * 4)
+#define GICD_IPRIORITYR(n) (GICD_OFFSET + 0x400 + (n) * 4)
+#define GICD_ITARGETSR(n) (GICD_OFFSET + 0x800 + (n) * 4)
+#define GICD_ICFGR(n) (GICD_OFFSET + 0xc00 + (n) * 4)
+#define GICD_NSACR(n) (GICD_OFFSET + 0xe00 + (n) * 4)
+#define GICD_SGIR (GICD_OFFSET + 0xf00)
+#define GICD_CPENDSGIR(n) (GICD_OFFSET + 0xf10 + (n) * 4)
+#define GICD_SPENDSGIR(n) (GICD_OFFSET + 0xf20 + (n) * 4)
+
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+#define GIC_REG_COUNT(bit_per_reg) DIV_ROUND_UP(MAX_INT, (bit_per_reg))
+#define DEFINE_GIC_SHADOW_REG(name, bit_per_reg, init_val, init_from) \
+ uint32_t (name)[GIC_REG_COUNT(bit_per_reg)] = { \
+ [(init_from / bit_per_reg) ... \
+ (GIC_REG_COUNT(bit_per_reg) - 1)] = (init_val) \
+ }
+
+#if WITH_LIB_SM
+static DEFINE_GIC_SHADOW_REG(gicd_igroupr, 32, ~0U, 0);
+#endif
+static DEFINE_GIC_SHADOW_REG(gicd_itargetsr, 4, 0x01010101, 32);
+
+static void gic_set_enable(uint vector, bool enable)
+{
+ int reg = vector / 32;
+ uint32_t mask = 1ULL << (vector % 32);
+
+ if (enable)
+ GICREG(0, GICD_ISENABLER(reg)) = mask;
+ else
+ GICREG(0, GICD_ICENABLER(reg)) = mask;
+}
+
+static void arm_gic_init_percpu(uint level)
+{
+#if WITH_LIB_SM
+ GICREG(0, GICC_CTLR) = 0xb; // enable GIC0 and select fiq mode for secure
+ GICREG(0, GICD_IGROUPR(0)) = ~0U; /* GICD_IGROUPR0 is banked */
+#else
+ GICREG(0, GICC_CTLR) = 1; // enable GIC0
+#endif
+ GICREG(0, GICC_PMR) = 0xFF; // unmask interrupts at all priority levels
+}
+
+LK_INIT_HOOK_FLAGS(arm_gic_init_percpu,
+ arm_gic_init_percpu,
+ LK_INIT_LEVEL_PLATFORM_EARLY, LK_INIT_FLAG_SECONDARY_CPUS);
+
+static void arm_gic_suspend_cpu(uint level)
+{
+ suspend_resume_fiq(false, false);
+}
+
+LK_INIT_HOOK_FLAGS(arm_gic_suspend_cpu, arm_gic_suspend_cpu,
+ LK_INIT_LEVEL_PLATFORM, LK_INIT_FLAG_CPU_SUSPEND);
+
+static void arm_gic_resume_cpu(uint level)
+{
+ spin_lock_saved_state_t state;
+ bool resume_gicd = false;
+
+ spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS);
+ if (!(GICREG(0, GICD_CTLR) & 1)) {
+ dprintf(SPEW, "%s: distibutor is off, calling arm_gic_init instead\n", __func__);
+ arm_gic_init();
+ resume_gicd = true;
+ } else {
+ arm_gic_init_percpu(0);
+ }
+ spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS);
+ suspend_resume_fiq(true, resume_gicd);
+}
+
+LK_INIT_HOOK_FLAGS(arm_gic_resume_cpu, arm_gic_resume_cpu,
+ LK_INIT_LEVEL_PLATFORM, LK_INIT_FLAG_CPU_RESUME);
+
+static int arm_gic_max_cpu(void)
+{
+ return (GICREG(0, GICD_TYPER) >> 5) & 0x7;
+}
+
+void arm_gic_init(void)
+{
+ int i;
+
+ for (i = 0; i < MAX_INT; i+= 32) {
+ GICREG(0, GICD_ICENABLER(i / 32)) = ~0;
+ GICREG(0, GICD_ICPENDR(i / 32)) = ~0;
+ }
+
+ if (arm_gic_max_cpu() > 0) {
+ /* Set external interrupts to target cpu 0 */
+ for (i = 32; i < MAX_INT; i += 4) {
+ GICREG(0, GICD_ITARGETSR(i / 4)) = gicd_itargetsr[i / 4];
+ }
+ }
+
+ GICREG(0, GICD_CTLR) = 1; // enable GIC0
+#if WITH_LIB_SM
+ GICREG(0, GICD_CTLR) = 3; // enable GIC0 ns interrupts
+ /*
+ * Iterate through all IRQs and set them to non-secure
+ * mode. This will allow the non-secure side to handle
+ * all the interrupts we don't explicitly claim.
+ */
+ for (i = 32; i < MAX_INT; i += 32) {
+ u_int reg = i / 32;
+ GICREG(0, GICD_IGROUPR(reg)) = gicd_igroupr[reg];
+ }
+#endif
+ arm_gic_init_percpu(0);
+}
+
+static status_t arm_gic_set_secure_locked(u_int irq, bool secure)
+{
+#if WITH_LIB_SM
+ int reg = irq / 32;
+ uint32_t mask = 1ULL << (irq % 32);
+
+ if (irq >= MAX_INT)
+ return ERR_INVALID_ARGS;
+
+ if (secure)
+ GICREG(0, GICD_IGROUPR(reg)) = (gicd_igroupr[reg] &= ~mask);
+ else
+ GICREG(0, GICD_IGROUPR(reg)) = (gicd_igroupr[reg] |= mask);
+ LTRACEF("irq %d, secure %d, GICD_IGROUP%d = %x\n",
+ irq, secure, reg, GICREG(0, GICD_IGROUPR(reg)));
+#endif
+ return NO_ERROR;
+}
+
+static status_t arm_gic_set_target_locked(u_int irq, u_int cpu_mask, u_int enable_mask)
+{
+ u_int reg = irq / 4;
+ u_int shift = 8 * (irq % 4);
+ u_int old_val;
+ u_int new_val;
+
+ cpu_mask = (cpu_mask & 0xff) << shift;
+ enable_mask = (enable_mask << shift) & cpu_mask;
+
+ old_val = GICREG(0, GICD_ITARGETSR(reg));
+ new_val = (gicd_itargetsr[reg] & ~cpu_mask) | enable_mask;
+ GICREG(0, GICD_ITARGETSR(reg)) = gicd_itargetsr[reg] = new_val;
+ LTRACEF("irq %i, GICD_ITARGETSR%d %x => %x (got %x)\n",
+ irq, reg, old_val, new_val, GICREG(0, GICD_ITARGETSR(reg)));
+
+ return NO_ERROR;
+}
+
+static status_t arm_gic_get_priority(u_int irq)
+{
+ u_int reg = irq / 4;
+ u_int shift = 8 * (irq % 4);
+ return (GICREG(0, GICD_IPRIORITYR(reg)) >> shift) & 0xff;
+}
+
+static status_t arm_gic_set_priority_locked(u_int irq, uint8_t priority)
+{
+ u_int reg = irq / 4;
+ u_int shift = 8 * (irq % 4);
+ u_int mask = 0xff << shift;
+ uint32_t regval;
+
+ regval = GICREG(0, GICD_IPRIORITYR(reg));
+ LTRACEF("irq %i, old GICD_IPRIORITYR%d = %x\n", irq, reg, regval);
+ regval = (regval & ~mask) | ((uint32_t)priority << shift);
+ GICREG(0, GICD_IPRIORITYR(reg)) = regval;
+ LTRACEF("irq %i, new GICD_IPRIORITYR%d = %x, req %x\n",
+ irq, reg, GICREG(0, GICD_IPRIORITYR(reg)), regval);
+
+ return 0;
+}
+
+status_t arm_gic_sgi(u_int irq, u_int flags, u_int cpu_mask)
+{
+ u_int val =
+ ((flags & ARM_GIC_SGI_FLAG_TARGET_FILTER_MASK) << 24) |
+ ((cpu_mask & 0xff) << 16) |
+ ((flags & ARM_GIC_SGI_FLAG_NS) ? (1U << 15) : 0) |
+ (irq & 0xf);
+
+ if (irq >= 16)
+ return ERR_INVALID_ARGS;
+
+ LTRACEF("GICD_SGIR: %x\n", val);
+
+ GICREG(0, GICD_SGIR) = val;
+
+ return NO_ERROR;
+}
+
+status_t mask_interrupt(unsigned int vector)
+{
+ if (vector >= MAX_INT)
+ return ERR_INVALID_ARGS;
+
+ if (arm_gic_interrupt_change_allowed(vector))
+ gic_set_enable(vector, false);
+
+ return NO_ERROR;
+}
+
+status_t unmask_interrupt(unsigned int vector)
+{
+ if (vector >= MAX_INT)
+ return ERR_INVALID_ARGS;
+
+ if (arm_gic_interrupt_change_allowed(vector))
+ gic_set_enable(vector, true);
+
+ return NO_ERROR;
+}
+
+static
+enum handler_return __platform_irq(struct iframe *frame)
+{
+ // get the current vector
+ uint32_t iar = GICREG(0, GICC_IAR);
+ unsigned int vector = iar & 0x3ff;
+
+ if (vector >= 0x3fe) {
+ // spurious
+ return INT_NO_RESCHEDULE;
+ }
+
+ THREAD_STATS_INC(interrupts);
+ KEVLOG_IRQ_ENTER(vector);
+
+ uint cpu = arch_curr_cpu_num();
+
+ LTRACEF_LEVEL(2, "iar 0x%x cpu %u currthread %p vector %d pc 0x%lx\n", iar, cpu,
+ get_current_thread(), vector, (uintptr_t)IFRAME_PC(frame));
+
+ // deliver the interrupt
+ enum handler_return ret;
+
+ ret = INT_NO_RESCHEDULE;
+ struct int_handler_struct *handler = get_int_handler(vector, cpu);
+ if (handler->handler)
+ ret = handler->handler(handler->arg);
+
+ GICREG(0, GICC_EOIR) = iar;
+
+ LTRACEF_LEVEL(2, "cpu %u exit %d\n", cpu, ret);
+
+ KEVLOG_IRQ_EXIT(vector);
+
+ return ret;
+}
+
+enum handler_return platform_irq(struct iframe *frame)
+{
+#if WITH_LIB_SM
+ uint32_t ahppir = GICREG(0, GICC_AHPPIR);
+ uint32_t pending_irq = ahppir & 0x3ff;
+ struct int_handler_struct *h;
+ uint cpu = arch_curr_cpu_num();
+
+ LTRACEF("ahppir %d\n", ahppir);
+ if (pending_irq < MAX_INT && get_int_handler(pending_irq, cpu)->handler) {
+ enum handler_return ret = 0;
+ uint32_t irq;
+ uint8_t old_priority;
+ spin_lock_saved_state_t state;
+
+ spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS);
+
+ /* Temporarily raise the priority of the interrupt we want to
+ * handle so another interrupt does not take its place before
+ * we can acknowledge it.
+ */
+ old_priority = arm_gic_get_priority(pending_irq);
+ arm_gic_set_priority_locked(pending_irq, 0);
+ DSB;
+ irq = GICREG(0, GICC_AIAR) & 0x3ff;
+ arm_gic_set_priority_locked(pending_irq, old_priority);
+
+ spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS);
+
+ LTRACEF("irq %d\n", irq);
+ if (irq < MAX_INT && (h = get_int_handler(pending_irq, cpu))->handler)
+ ret = h->handler(h->arg);
+ else
+ TRACEF("unexpected irq %d != %d may get lost\n", irq, pending_irq);
+ GICREG(0, GICC_AEOIR) = irq;
+ return ret;
+ }
+ return sm_handle_irq();
+#else
+ return __platform_irq(frame);
+#endif
+}
+
+void platform_fiq(struct iframe *frame)
+{
+#if WITH_LIB_SM
+ sm_handle_fiq();
+#else
+ PANIC_UNIMPLEMENTED;
+#endif
+}
+
+#if WITH_LIB_SM
+static status_t arm_gic_get_next_irq_locked(u_int min_irq, bool per_cpu)
+{
+ u_int irq;
+ u_int max_irq = per_cpu ? GIC_MAX_PER_CPU_INT : MAX_INT;
+ uint cpu = arch_curr_cpu_num();
+
+ if (!per_cpu && min_irq < GIC_MAX_PER_CPU_INT)
+ min_irq = GIC_MAX_PER_CPU_INT;
+
+ for (irq = min_irq; irq < max_irq; irq++)
+ if (get_int_handler(irq, cpu)->handler)
+ return irq;
+
+ return SM_ERR_END_OF_INPUT;
+}
+
+long smc_intc_get_next_irq(smc32_args_t *args)
+{
+ status_t ret;
+ spin_lock_saved_state_t state;
+
+ spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS);
+
+ arm_gic_non_secure_interrupts_frozen = true;
+ ret = arm_gic_get_next_irq_locked(args->params[0], args->params[1]);
+ LTRACEF("min_irq %d, per_cpu %d, ret %d\n",
+ args->params[0], args->params[1], ret);
+
+ spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS);
+
+ return ret;
+}
+
+static u_long enabled_fiq_mask[BITMAP_NUM_WORDS(MAX_INT)];
+
+static void bitmap_update_locked(u_long *bitmap, u_int bit, bool set)
+{
+ u_long mask = 1UL << BITMAP_BIT_IN_WORD(bit);
+
+ bitmap += BITMAP_WORD(bit);
+ if (set)
+ *bitmap |= mask;
+ else
+ *bitmap &= ~mask;
+}
+
+long smc_intc_request_fiq(smc32_args_t *args)
+{
+ u_int fiq = args->params[0];
+ bool enable = args->params[1];
+ spin_lock_saved_state_t state;
+
+ dprintf(SPEW, "%s: fiq %d, enable %d\n", __func__, fiq, enable);
+ spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS);
+
+ arm_gic_set_secure_locked(fiq, true);
+ arm_gic_set_target_locked(fiq, ~0, ~0);
+ arm_gic_set_priority_locked(fiq, 0);
+
+ gic_set_enable(fiq, enable);
+ bitmap_update_locked(enabled_fiq_mask, fiq, enable);
+
+ dprintf(SPEW, "%s: fiq %d, enable %d done\n", __func__, fiq, enable);
+
+ spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS);
+
+ return NO_ERROR;
+}
+
+static u_int current_fiq[8] = { 0x3ff, 0x3ff, 0x3ff, 0x3ff, 0x3ff, 0x3ff, 0x3ff, 0x3ff };
+
+static bool update_fiq_targets(u_int cpu, bool enable, u_int triggered_fiq, bool resume_gicd)
+{
+ u_int i, j;
+ u_long mask;
+ u_int fiq;
+ bool smp = arm_gic_max_cpu() > 0;
+ bool ret = false;
+
+ spin_lock(&gicd_lock); /* IRQs and FIQs are already masked */
+ for (i = 0; i < BITMAP_NUM_WORDS(MAX_INT); i++) {
+ mask = enabled_fiq_mask[i];
+ while (mask) {
+ j = _ffz(~mask);
+ mask &= ~(1UL << j);
+ fiq = i * BITMAP_BITS_PER_WORD + j;
+ if (fiq == triggered_fiq)
+ ret = true;
+ LTRACEF("cpu %d, irq %i, enable %d\n", cpu, fiq, enable);
+ if (smp)
+ arm_gic_set_target_locked(fiq, 1U << cpu, enable ? ~0 : 0);
+ if (!smp || resume_gicd)
+ gic_set_enable(fiq, enable);
+ }
+ }
+ spin_unlock(&gicd_lock);
+ return ret;
+}
+
+static void suspend_resume_fiq(bool resume_gicc, bool resume_gicd)
+{
+ u_int cpu = arch_curr_cpu_num();
+
+ ASSERT(cpu < 8);
+
+ update_fiq_targets(cpu, resume_gicc, ~0, resume_gicd);
+}
+
+status_t sm_intc_fiq_enter(void)
+{
+ u_int cpu = arch_curr_cpu_num();
+ u_int irq = GICREG(0, GICC_IAR) & 0x3ff;
+ bool fiq_enabled;
+
+ ASSERT(cpu < 8);
+
+ LTRACEF("cpu %d, irq %i\n", cpu, irq);
+
+ if (irq >= 1020) {
+ LTRACEF("spurious fiq: cpu %d, old %d, new %d\n", cpu, current_fiq[cpu], irq);
+ return ERR_NO_MSG;
+ }
+
+ fiq_enabled = update_fiq_targets(cpu, false, irq, false);
+ GICREG(0, GICC_EOIR) = irq;
+
+ if (current_fiq[cpu] != 0x3ff) {
+ dprintf(INFO, "more than one fiq active: cpu %d, old %d, new %d\n", cpu, current_fiq[cpu], irq);
+ return ERR_ALREADY_STARTED;
+ }
+
+ if (!fiq_enabled) {
+ dprintf(INFO, "got disabled fiq: cpu %d, new %d\n", cpu, irq);
+ return ERR_NOT_READY;
+ }
+
+ current_fiq[cpu] = irq;
+
+ return 0;
+}
+
+void sm_intc_fiq_exit(void)
+{
+ u_int cpu = arch_curr_cpu_num();
+
+ ASSERT(cpu < 8);
+
+ LTRACEF("cpu %d, irq %i\n", cpu, current_fiq[cpu]);
+ if (current_fiq[cpu] == 0x3ff) {
+ dprintf(INFO, "%s: no fiq active, cpu %d\n", __func__, cpu);
+ return;
+ }
+ update_fiq_targets(cpu, true, current_fiq[cpu], false);
+ current_fiq[cpu] = 0x3ff;
+}
+#endif
+
+/* vim: set ts=4 sw=4 noexpandtab: */
diff --git a/src/bsp/lk/dev/interrupt/arm_gic/include/dev/interrupt/arm_gic.h b/src/bsp/lk/dev/interrupt/arm_gic/include/dev/interrupt/arm_gic.h
new file mode 100644
index 0000000..ee0fe61
--- /dev/null
+++ b/src/bsp/lk/dev/interrupt/arm_gic/include/dev/interrupt/arm_gic.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2013, Google Inc. All rights reserved.
+ *
+ * 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 __DEV_INTERRUPT_ARM_GIC_H
+#define __DEV_INTERRUPT_ARM_GIC_H
+
+#include <sys/types.h>
+
+void arm_gic_init(void);
+
+enum {
+ /* Ignore cpu_mask and forward interrupt to all CPUs other than the current cpu */
+ ARM_GIC_SGI_FLAG_TARGET_FILTER_NOT_SENDER = 0x1,
+ /* Ignore cpu_mask and forward interrupt to current CPU only */
+ ARM_GIC_SGI_FLAG_TARGET_FILTER_SENDER = 0x2,
+ ARM_GIC_SGI_FLAG_TARGET_FILTER_MASK = 0x3,
+
+ /* Only forward the interrupt to CPUs that has the interrupt configured as group 1 (non-secure) */
+ ARM_GIC_SGI_FLAG_NS = 0x4,
+};
+status_t arm_gic_sgi(u_int irq, u_int flags, u_int cpu_mask);
+
+#endif
+
diff --git a/src/bsp/lk/dev/interrupt/arm_gic/rules.mk b/src/bsp/lk/dev/interrupt/arm_gic/rules.mk
new file mode 100644
index 0000000..d77ca24
--- /dev/null
+++ b/src/bsp/lk/dev/interrupt/arm_gic/rules.mk
@@ -0,0 +1,8 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/arm_gic.c
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/interrupt/arm_gic_v3/arm_gic.c b/src/bsp/lk/dev/interrupt/arm_gic_v3/arm_gic.c
new file mode 100644
index 0000000..7e95771
--- /dev/null
+++ b/src/bsp/lk/dev/interrupt/arm_gic_v3/arm_gic.c
@@ -0,0 +1,1115 @@
+/*
+ * 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 <dev/interrupt/arm_gic.h>
+#include <reg.h>
+#include <kernel/thread.h>
+#include <kernel/debug.h>
+#include <lk/init.h>
+#include <platform/interrupts.h>
+#include <platform/mt_irq.h>
+#include <platform/mt_reg_base.h>
+#include <arch/ops.h>
+#include <platform/gic.h>
+#include <trace.h>
+
+#if WITH_LIB_SM
+#include <lib/sm.h>
+#include <lib/sm/sm_err.h>
+#endif
+
+#define LOCAL_TRACE 0
+
+#if ARCH_ARM
+#include <arch/arm.h>
+#define iframe arm_iframe
+#define IFRAME_PC(frame) ((frame)->pc)
+#endif
+#if ARCH_ARM64
+#include <arch/arm64.h>
+#define iframe arm64_iframe_short
+#define IFRAME_PC(frame) ((frame)->elr)
+#endif
+
+/* helpers for later ICC encode macros
+ * Indirect stringification. Doing two levels allows the parameter to be a
+ * macro itself. For example, compile with -DFOO=bar, __stringify(FOO)
+ * converts to "bar".
+ */
+#define __stringify_1(x) #x
+#define __stringify(x) __stringify_1(x)
+
+#define GIC_READ(a) readl(a)
+#define GIC_WRITE(a, v) writel(v, a)
+
+static status_t arm_gic_set_secure_locked(u_int irq, bool secure);
+static spin_lock_t gicd_lock;
+
+#if WITH_LIB_SM
+#define GICD_LOCK_FLAGS SPIN_LOCK_FLAG_IRQ_FIQ
+#else
+#define GICD_LOCK_FLAGS SPIN_LOCK_FLAG_INTERRUPTS
+#endif
+
+#define GIC_MAX_PER_CPU_INT 32
+
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+#define GIC_REG_COUNT(bit_per_reg) DIV_ROUND_UP(MAX_INT, (bit_per_reg))
+#define DEFINE_GIC_SHADOW_REG(name, bit_per_reg, init_val, init_from) \
+ uint32_t (name)[GIC_REG_COUNT(bit_per_reg)] = { \
+ [(init_from / bit_per_reg) ... \
+ (GIC_REG_COUNT(bit_per_reg) - 1)] = (init_val) \
+ }
+
+__asm__ (
+ " .irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30\n"
+ " .equ __reg_num_x\\num, \\num\n"
+ " .endr\n"
+ " .equ __reg_num_xzr, 31\n"
+ "\n"
+ " .macro mrs_s, rt, sreg\n"
+ " .inst 0xd5300000|(\\sreg)|(__reg_num_\\rt)\n"
+ " .endm\n"
+ "\n"
+ " .macro msr_s, sreg, rt\n"
+ " .inst 0xd5100000|(\\sreg)|(__reg_num_\\rt)\n"
+ " .endm\n"
+);
+
+/* since gcc not support most ARMv8 ICC sysreg in asm,
+ * we learn Linux's way to encode them */
+#define sys_reg(op0, op1, crn, crm, op2) \
+ ((((op0)-2)<<19)|((op1)<<16)|((crn)<<12)|((crm)<<8)|((op2)<<5))
+
+#if WITH_LIB_SM
+static bool arm_gic_non_secure_interrupts_frozen;
+
+static bool arm_gic_interrupt_change_allowed(int irq)
+{
+ if (!arm_gic_non_secure_interrupts_frozen)
+ return true;
+
+ TRACEF("change to interrupt %d ignored after booting ns\n", irq);
+ return false;
+}
+
+static void suspend_resume_fiq(bool resume_gicc, bool resume_gicd);
+#else
+static bool arm_gic_interrupt_change_allowed(int irq)
+{
+ return true;
+}
+
+static void suspend_resume_fiq(bool resume_gicc, bool resume_gicd)
+{
+}
+#endif
+
+#define _DEFINE_SYSREG_READ_FUNC(_name, _reg_name) \
+static inline uint64_t read_ ## _name(void) \
+{ \
+ uint64_t v; \
+ __asm__ volatile ("mrs %0, " #_reg_name : "=r" (v)); \
+ return v; \
+}
+
+#define _DEFINE_SYSREG_WRITE_FUNC(_name, _reg_name) \
+static inline void write_ ## _name(uint64_t v) \
+{ \
+ __asm__ volatile ("msr " #_reg_name ", %0" : : "r" (v)); \
+}
+
+#define DEFINE_SYSOP_FUNC(_op) \
+static inline void _op(void) \
+{ \
+ __asm__ (#_op); \
+}
+
+/* Define read & write function for system register */
+#define DEFINE_SYSREG_RW_FUNCS(_name) \
+ _DEFINE_SYSREG_READ_FUNC(_name, _name) \
+ _DEFINE_SYSREG_WRITE_FUNC(_name, _name)
+
+DEFINE_SYSREG_RW_FUNCS(scr_el3)
+
+/* Define read & write function for renamed system register */
+#define DEFINE_RENAME_SYSREG_RW_FUNCS(_name, _reg_name) \
+ _DEFINE_SYSREG_READ_FUNC(_name, _reg_name) \
+ _DEFINE_SYSREG_WRITE_FUNC(_name, _reg_name)
+
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el3, S3_6_C12_C12_5)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el2, S3_4_C12_C9_5)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el1, S3_0_C12_C12_5)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_pmr_el1, S3_0_C4_C6_0)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_grpen1_el3, S3_6_C12_C12_7)
+DEFINE_RENAME_SYSREG_RW_FUNCS(icc_grpen1_el1, S3_0_C12_C12_7)
+
+DEFINE_SYSOP_FUNC(isb)
+
+/*******************************************************************************
+ * This function does some minimal GICv3 configuration. The Firmware itself does
+ * not fully support GICv3 at this time and relies on GICv2 emulation as
+ * provided by GICv3. This function allows software (like Linux) in later stages
+ * to use full GICv3 features.
+ ******************************************************************************/
+static void gicv3_cpuif_setup(void)
+{
+ /* set all SGI/PPI as non-secure GROUP1 by default.
+ rdist_base + 64K == SGI_base */
+ GIC_WRITE(GIC_REDIS_BASE+SZ_64K+GICE_V3_IGROUP0, 0xffffffff);
+ GIC_WRITE(GIC_REDIS_BASE+SZ_64K+GICE_V3_IGRPMOD0, 0x0);
+}
+
+static void mt_git_dist_rwp(void)
+{
+ /*
+ * check GICD_CTLR.RWP for done check
+ */
+ while (GIC_READ(GIC_DIST_BASE + GICD_CTLR) & GICD_CTLR_RWP) {
+
+ }
+}
+
+struct int_handler_struct {
+ int_handler handler;
+ void *arg;
+};
+
+static struct int_handler_struct int_handler_table_per_cpu[GIC_MAX_PER_CPU_INT][SMP_MAX_CPUS];
+static struct int_handler_struct int_handler_table_shared[MAX_INT-GIC_MAX_PER_CPU_INT];
+
+static struct int_handler_struct *get_int_handler(unsigned int vector, uint cpu)
+{
+ if (vector < GIC_MAX_PER_CPU_INT)
+ return &int_handler_table_per_cpu[vector][cpu];
+ else
+ return &int_handler_table_shared[vector - GIC_MAX_PER_CPU_INT];
+}
+
+void register_int_handler(unsigned int vector, int_handler handler, void *arg)
+{
+ struct int_handler_struct *h;
+ uint cpu = arch_curr_cpu_num();
+
+ spin_lock_saved_state_t state;
+
+ if (vector >= MAX_INT)
+ panic("register_int_handler: vector out of range %d\n", vector);
+
+ spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS);
+
+ if (arm_gic_interrupt_change_allowed(vector)) {
+ h = get_int_handler(vector, cpu);
+ h->handler = handler;
+ h->arg = arg;
+ }
+
+ spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS);
+}
+
+#if WITH_LIB_SM
+static DEFINE_GIC_SHADOW_REG(gicd_igroupr, 32, ~0U, 0);
+#endif
+static DEFINE_GIC_SHADOW_REG(gicd_itargetsr, 4, 0x01010101, 32);
+
+static void gic_set_enable(uint vector, bool enable)
+{
+ int reg = vector / 32;
+ uint32_t mask = 1ULL << (vector % 32);
+
+ if (vector < 32) {
+ if (enable)
+ GIC_WRITE(GIC_REDIS_BASE + SZ_64K + GICD_ISENABLER, mask);
+ else
+ GIC_WRITE(GIC_REDIS_BASE + SZ_64K + GICD_ICENABLER, mask);
+
+ } else {
+ if (enable)
+ GIC_WRITE(GIC_DIST_BASE + GICD_ISENABLER + reg * 4, mask);
+ else
+ GIC_WRITE(GIC_DIST_BASE + GICD_ICENABLER + reg * 4, mask);
+ }
+}
+
+static void arm_gic_init_percpu(uint level)
+{
+#if WITH_LIB_SM
+ GIC_WRITE(GIC_REDIS_BASE + GICC_CTLR, 0xb); // enable GIC0 and select fiq mode for secure
+ GIC_WRITE(GIC_DIST_BASE + GICD_IGROUPR, ~0U); /* GICD_IGROUPR0 is banked */
+#else
+ GIC_WRITE(GIC_REDIS_BASE + GICC_CTLR, 1); // enable GIC0
+#endif
+ GIC_WRITE(GIC_REDIS_BASE + GICC_PMR, 0xFF); // unmask interrupts at all priority levels
+}
+
+LK_INIT_HOOK_FLAGS(arm_gic_init_percpu,
+ arm_gic_init_percpu,
+ LK_INIT_LEVEL_PLATFORM_EARLY, LK_INIT_FLAG_SECONDARY_CPUS);
+
+static void arm_gic_suspend_cpu(uint level)
+{
+ suspend_resume_fiq(false, false);
+}
+
+LK_INIT_HOOK_FLAGS(arm_gic_suspend_cpu, arm_gic_suspend_cpu,
+ LK_INIT_LEVEL_PLATFORM, LK_INIT_FLAG_CPU_SUSPEND);
+
+static void arm_gic_resume_cpu(uint level)
+{
+ spin_lock_saved_state_t state;
+ bool resume_gicd = false;
+
+ spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS);
+ if (!(GIC_READ(GIC_DIST_BASE + GICD_CTLR) & 1)) {
+ dprintf(SPEW, "%s: distibutor is off, calling arm_gic_init instead\n", __func__);
+ arm_gic_init();
+ resume_gicd = true;
+ } else {
+ arm_gic_init_percpu(0);
+ }
+ spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS);
+ suspend_resume_fiq(true, resume_gicd);
+}
+
+LK_INIT_HOOK_FLAGS(arm_gic_resume_cpu, arm_gic_resume_cpu,
+ LK_INIT_LEVEL_PLATFORM, LK_INIT_FLAG_CPU_RESUME);
+
+static int arm_gic_max_cpu(void)
+{
+ return (GIC_READ(GIC_DIST_BASE + GICD_TYPER) >> 5) & 0x7;
+}
+
+/*******************************************************************************
+ * Enable secure interrupts and use FIQs to route them. Disable legacy bypass
+ * and set the priority mask register to allow all interrupts to trickle in.
+ ******************************************************************************/
+void arm_gic_redist_init(void)
+{
+ /* Set all SGI/PPI as secure GROUP1.
+ rdist_base + 64K == SGI_base */
+ GIC_WRITE(GIC_REDIS_BASE + SZ_64K + GICE_V3_IGROUP0, 0x0);
+ GIC_WRITE(GIC_REDIS_BASE + SZ_64K + GICE_V3_IGRPMOD0, 0xffffffff);
+}
+
+static void arm_gic_distif_init(void)
+{
+ unsigned int i, ctrl, irq_set;
+
+ /* Disable the distributor before going further */
+ ctrl = GIC_READ(GIC_DIST_BASE + GICD_CTLR);
+ ctrl &= ~(GICD_CTLR_ENABLE_GRP0 | GICD_CTLR_ENGRP1NS | GICD_CTLR_ENGRP1S);
+ GIC_WRITE(GIC_DIST_BASE + GICD_CTLR, ctrl);
+
+ mt_git_dist_rwp();
+
+ /*
+ * Mark out non-secure SPI interrupts. The number of interrupts is
+ * calculated as 32 * (IT_LINES + 1). We do 32 at a time.
+ */
+ irq_set = (GIC_READ(GIC_DIST_BASE + GICD_TYPER)&IT_LINES_NO_MASK) + 1;
+ irq_set = irq_set * 32;
+
+ /* Set all SPI as secure group1 */
+ for (i = 32; i < irq_set; i += 32) {
+ GIC_WRITE(GIC_DIST_BASE + GICD_IGROUPR + i * 4 / 32, 0x0);
+ GIC_WRITE(GIC_DIST_BASE + GICD_IGRPMODR + i * 4 / 32, 0xffffffff);
+ }
+
+ /*
+ * Set all global interrupts to be level triggered, active low.
+ */
+ for (i = 32; i < irq_set; i += 16) {
+ GIC_WRITE(GIC_DIST_BASE + GICD_ICFGR + i * 4 / 16, 0);
+ }
+
+ /*
+ * Set all global interrupts to this CPU only.
+ */
+ if (arm_gic_max_cpu() > 0) {
+ /* Set external interrupts to target cpu 0 */
+ for (i = 32; i < irq_set; i += 4) {
+ GIC_READ(GIC_DIST_BASE + GICD_ITARGETSR + (i / 4) * 4) = gicd_itargetsr[i / 4];
+ }
+ }
+
+ /*
+ * Set priority on all interrupts.
+ */
+ for (i = 0; i < irq_set; i += 4) {
+ GIC_WRITE(GIC_DIST_BASE + GICD_IPRIORITYR + i * 4 / 4, 0xA0A0A0A0);
+ }
+
+ /*
+ * Disable all interrupts.
+ */
+ for (i = 0; i < irq_set; i += 32) {
+ GIC_WRITE(GIC_DIST_BASE + GICD_ICENABLER + i * 4 / 32, 0xFFFFFFFF);
+ }
+
+ /*
+ * Clear all active status
+ */
+ for (i = 0; i < irq_set; i += 32) {
+ GIC_WRITE(GIC_DIST_BASE + GICD_ICACTIVER + i * 4 / 32, 0xFFFFFFFF);
+ }
+
+ /*
+ * Clear all pending status
+ */
+ for (i = 0; i < irq_set; i += 32) {
+ GIC_WRITE(GIC_DIST_BASE + GICD_ICPENDR + i * 4 / 32, 0xFFFFFFFF);
+ }
+
+ /* Enable all groups & ARE */
+ ctrl = GICD_CTLR_ENABLE_GRP0 | GICD_CTLR_ENGRP1NS | GICD_CTLR_ENGRP1S |
+ GICD_CTLR_ARE | GICD_CTLR_ARE_NS;
+ GIC_WRITE(GIC_DIST_BASE + GICD_CTLR, ctrl);
+
+ mt_git_dist_rwp();
+}
+
+void clear_sec_pol_ctl_en(void)
+{
+ unsigned int i;
+
+ /* total 19 polarity ctrl registers */
+ for (i = 0; i <= NR_INT_POL_CTL-1; i++) {
+ GIC_WRITE((SEC_POL_CTL_EN0 + (i * 4)), 0);
+ }
+}
+
+#if ARCH_ARM64
+void gic_setup(void)
+{
+ uint64_t val, scr_val;
+
+#if GICV3_SUPPORT_GIC600
+ /* Power on redisdtibuter */
+ gicv3_rdistif_on();
+#endif
+
+ /* GIC V3 redistributor initialization (all CPUs) */
+ val = GIC_READ(GIC_REDIS_BASE_PHY + GICR_V3_WAKER);
+ val &= ~GICR_V3_WAKER_ProcessorSleep;
+ GIC_WRITE(GIC_REDIS_BASE_PHY + GICR_V3_WAKER, val);
+ while ((GIC_READ(GIC_REDIS_BASE_PHY + GICR_V3_WAKER) & GICR_V3_WAKER_ChildrenAsleep));
+
+ /*
+ * We need to set SCR_EL3.NS in order to see GICv3 non-secure state.
+ * Restore SCR_EL3.NS again before exit.
+ */
+ scr_val = read_scr_el3();
+ write_scr_el3(scr_val | SCR_NS_BIT);
+ isb(); /* ensure NS=1 takes effect before accessing ICC_SRE_EL2 */
+
+ /* GIC V3 CPU interface initialization (all CPUs) */
+ val = read_icc_sre_el3();
+ write_icc_sre_el3(val | ICC_SRE_EN_BIT | ICC_SRE_SRE_BIT);
+ isb(); /* before enable lower SRE, be sure SRE in el3 takes effect */
+
+ write_icc_grpen1_el3(0x1LL);
+
+ val = read_icc_sre_el2();
+ write_icc_sre_el2(val | ICC_SRE_EN_BIT | ICC_SRE_SRE_BIT);
+ isb(); /* before enable lower SRE, be sure SRE in el2 takes effect */
+
+ write_icc_pmr_el1(GIC_PRI_MASK);
+ isb(); /* commite ICC_* changes before setting NS=0 */
+
+ /* Restore SCR_EL3 */
+ write_scr_el3(scr_val);
+ isb(); /* ensure NS=0 takes effect immediately */
+
+ write_icc_grpen1_el1(0x1LL);
+
+ /* MUST set secure copy of icc_sre_el1 as SRE_SRE to enable FIQ,
+ * see GICv3 spec 4.6.4 FIQ Enable
+ */
+ val = read_icc_sre_el1();
+ write_icc_sre_el1(val | ICC_SRE_SRE_BIT);
+ isb(); /* before we can touch other ICC_* system registers, make sure this have effect */
+}
+#endif
+
+#if ARCH_ARM
+static void gic_dist_wait_for_rwp(void)
+{
+ while ((*(volatile unsigned int *)(GIC_DIST_BASE + GIC_DIST_CTRL)) & GICD_CTLR_RWP)
+ isb();
+}
+
+static void gic_redist_wait_for_rwp(void)
+{
+ while ((*(volatile unsigned int *)(GIC_REDIS_BASE + GICR_V3_CTLR)) & GICR_V3_CTLR_RWP)
+ isb();
+}
+
+static void gic_enable_sre(void)
+{
+ uint32_t val;
+
+ /* for suspend */
+ /* Enable ICC_MSRE */
+ asm volatile("cps #22");
+ asm volatile("mrc p15, 6, %0, c12, c12, 5" : "=r" (val));
+ val |= (ICC_SRE_SRE_BIT | ICC_SRE_EN_BIT);
+ asm volatile("mcr p15, 6, %0, c12, c12, 5" : : "r" (val));
+ asm volatile("cps #19");
+ isb();
+
+ asm volatile("MRC p15, 0, %0, c12, c12, 5" : "=r" (val));
+ val |= ICC_SRE_SRE_BIT;
+ asm volatile("MCR p15, 0, %0, c12, c12, 5" : : "r" (val));
+ isb();
+}
+
+/* Our default, arbitrary priority value. Linux only uses one anyway. */
+#define DEFAULT_PMR_VALUE 0xf0
+
+/* Low level accessors */
+static void gic_write_pmr(uint32_t val)
+{
+ asm volatile("MCR p15, 0, %0, c4, c6, 0" : : "r" (val));
+}
+
+#define ICC_CTLR_EL1_EOImode_drop_dir (0U << 1)
+static void gic_write_ctlr(uint32_t val)
+{
+ asm volatile("MCR p15, 0, %0, c12, c12, 4" : : "r" (val));
+ isb();
+}
+
+static void gic_write_grpen1(uint32_t val)
+{
+ asm volatile("MCR p15, 0, %0, c12, c12, 7" : : "r" (val));
+ asm volatile("MCR p15, 0, %0, c12, c12, 6" : : "r" (val));
+ isb();
+}
+
+void gic_setup(void)
+{
+ uint32_t gic_irqs, i, val;
+ uint32_t mpidr, affinity;
+
+ /*
+ * Find out how many interrupts are supported.
+ * The GIC only supports up to 1020 interrupt sources (SGI+PPI+SPI)
+ */
+ gic_irqs = *(volatile unsigned int *)(GIC_DIST_BASE + GICD_TYPER) & 0x1f;
+ gic_irqs = (gic_irqs + 1) * 32;
+ if (gic_irqs > 1020)
+ gic_irqs = 1020;
+
+ /* Disable the distributor */
+ GIC_WRITE(GIC_DIST_BASE + GICD_CTLR, 0);
+ gic_dist_wait_for_rwp();
+
+ /*
+ * Set all global interrupts to be level triggered, active low.
+ */
+ for (i = 32; i < gic_irqs; i += 16)
+ GIC_WRITE(GIC_DIST_BASE + GIC_DIST_CONFIG + i / 4, 0);
+
+ /*
+ * Set priority on all global interrupts.
+ */
+ for (i = 32; i < gic_irqs; i += 4)
+ GIC_WRITE(GIC_DIST_BASE + GIC_DIST_PRI + i, 0xa0a0a0a0);
+
+ /*
+ * Set all interrupts to G1S. Leave the PPI and SGIs alone
+ * as they are set by redistributor registers.
+ */
+ for (i = 32; i < gic_irqs; i += 32)
+ GIC_WRITE(GIC_DIST_BASE + GICD_IGRPMODR + i / 8, 0xffffffff);
+
+ /*
+ * Disable all interrupts. Leave the PPI and SGIs alone
+ * as they are enabled by redistributor registers.
+ */
+ for (i = 32; i < gic_irqs; i += 32)
+ GIC_WRITE(GIC_DIST_BASE + GIC_DIST_ENABLE_CLEAR + i / 8, 0xffffffff);
+
+ gic_dist_wait_for_rwp();
+
+ /* FIXME */
+ /* Enable distributor with ARE, Group1 */
+ GIC_WRITE(GIC_DIST_BASE + GIC_DIST_CTRL,
+ GICD_CTLR_ARE | GICD_CTLR_ENGRP1S |
+ GICD_CTLR_ENGRP1NS | GICD_CTLR_ENABLE_GRP0);
+
+ /*
+ * Set all global interrupts to the boot CPU only. ARE must be
+ * enabled.
+ */
+ mpidr = 0x0;
+ affinity = mpidr;
+ /*
+ affinity = (MPIDR_AFFINITY_LEVEL(mpidr, 3) << 32 |
+ MPIDR_AFFINITY_LEVEL(mpidr, 2) << 16 |
+ MPIDR_AFFINITY_LEVEL(mpidr, 1) << 8 |
+ MPIDR_AFFINITY_LEVEL(mpidr, 0));
+ */
+ for (i = 32; i < gic_irqs; i++)
+ GIC_WRITE(GIC_DIST_BASE + GICD_IROUTER + i * 8, affinity);
+
+#if GICV3_SUPPORT_GIC600
+ /* Power on redisdtibuter */
+ gicv3_rdistif_on();
+#endif
+
+ /* Wake up this CPU redistributor */
+ val = GIC_READ(GIC_REDIS_BASE + GICR_V3_WAKER);
+ val &= ~GICR_WAKER_ProcessorSleep;
+ GIC_WRITE(GIC_REDIS_BASE + GICR_V3_WAKER, val);
+
+ while ((GIC_READ(GIC_REDIS_BASE + GICR_V3_WAKER)) &
+ GICR_WAKER_ChildrenAsleep)
+ ;
+
+ /*
+ * Deal with the banked PPI and SGI interrupts - disable all
+ * PPI interrupts, ensure all SGI interrupts are enabled.
+ */
+ GIC_WRITE(GIC_REDIS_BASE + SZ_64K + GIC_DIST_ENABLE_CLEAR, 0xffff0000);
+ GIC_WRITE(GIC_REDIS_BASE + SZ_64K + GIC_DIST_ENABLE_SET, 0x0000ffff);
+
+ /*
+ * Set priority on PPI and SGI interrupts
+ */
+ for (i = 0; i < 32; i += 4)
+ GIC_WRITE(GIC_REDIS_BASE + SZ_64K + GIC_DIST_PRI + i, 0xa0a0a0a0);
+
+ gic_redist_wait_for_rwp();
+
+ /* Enable system registers */
+ gic_enable_sre();
+
+ /* Set priority mask register */
+ gic_write_pmr(DEFAULT_PMR_VALUE);
+
+ /* EOI deactivates interrupt too (mode 0) */
+ gic_write_ctlr(ICC_CTLR_EL1_EOImode_drop_dir);
+
+ /* ... and let's hit the road... */
+ gic_write_grpen1(1);
+
+ isb();
+}
+#endif
+
+void arm_gic_init(void)
+{
+#if ARCH_ARM
+ /* for ARCH_ARM64, gic_setup will be called in platform_el3_init in start.s */
+ LTRACEF("[LK GIC] before gic_setup\n");
+ gic_setup();
+#endif
+
+ LTRACEF("[LK GIC] before arm_gic_cpuif_setup\n");
+ arm_gic_redist_init();
+
+ LTRACEF("[LK GIC] before arm_gic_distif_init\n");
+ arm_gic_distif_init();
+
+#if WITH_LIB_SM
+ GIC_WRITE(GIC_DIST_BASE + GICD_CTLR, 3); // enable GIC0 ns interrupts
+ /*
+ * Iterate through all IRQs and set them to non-secure
+ * mode. This will allow the non-secure side to handle
+ * all the interrupts we don't explicitly claim.
+ */
+ for (i = 32; i < MAX_INT; i += 32) {
+ u_int reg = i / 32;
+ GIC_WRITE(GIC_DIST_BASE + GICD_IGROUPR + reg * 4, gicd_igroupr[reg]);
+ }
+#endif
+ arm_gic_init_percpu(0);
+
+ LTRACEF("[LK GIC] before clear_sec_pol_ctl_en\n");
+ clear_sec_pol_ctl_en();
+
+}
+
+static status_t arm_gic_set_secure_locked(u_int irq, bool secure)
+{
+#if WITH_LIB_SM
+ int reg = irq / 32;
+ uint32_t mask = 1ULL << (irq % 32);
+
+ if (irq >= MAX_INT)
+ return ERR_INVALID_ARGS;
+
+ if (secure)
+ GIC_WEITE(GIC_DIST_BASE + GICD_IGROUPR + reg * 4, (gicd_igroupr[reg] &= ~mask));
+ else
+ GIC_WRITE(GIC_DIST_BASE + GICD_IGROUPR + reg * 4, ((gicd_igroupr[reg] |= mask));
+ LTRACEF("irq %d, secure %d, GICD_IGROUP%d = %x\n",
+ irq, secure, reg, GIC_READ(GIC_DIST_BASE + GICD_IGROUPR + reg * 4);
+#endif
+ return NO_ERROR;
+}
+
+static status_t arm_gic_set_target_locked(u_int irq, u_int cpu_mask, u_int enable_mask)
+{
+ u_int reg = irq / 4;
+ u_int shift = 8 * (irq % 4);
+ u_int old_val;
+ u_int new_val;
+
+ cpu_mask = (cpu_mask & 0xff) << shift;
+ enable_mask = (enable_mask << shift) & cpu_mask;
+
+ old_val = GIC_READ(GIC_DIST_BASE + GICD_ITARGETSR + reg * 4);
+ new_val = (gicd_itargetsr[reg] & ~cpu_mask) | enable_mask;
+ GIC_WRITE(GIC_DIST_BASE + GICD_ITARGETSR + reg * 4, new_val);
+ gicd_itargetsr[reg] = new_val;
+ LTRACEF("irq %i, GICD_ITARGETSR%d %x => %x (got %x)\n",
+ irq, reg, old_val, new_val, GIC_READ(GIC_DIST_BASE + GICD_ITARGETSR + reg));
+
+ return NO_ERROR;
+}
+
+static status_t arm_gic_get_priority(u_int irq)
+{
+ u_int reg = irq / 4;
+ u_int shift = 8 * (irq % 4);
+ return (GIC_READ(GIC_DIST_BASE + GICD_IPRIORITYR + reg * 4) >> shift) & 0xff;
+}
+
+static status_t arm_gic_set_priority_locked(u_int irq, uint8_t priority)
+{
+ u_int reg = irq / 4;
+ u_int shift = 8 * (irq % 4);
+ u_int mask = 0xff << shift;
+ uint32_t regval;
+
+ regval = GIC_READ(GIC_DIST_BASE + GICD_IPRIORITYR + reg);
+ LTRACEF("irq %i, old GICD_IPRIORITYR%d = %x\n", irq, reg, regval);
+ regval = (regval & ~mask) | ((uint32_t)priority << shift);
+ GIC_READ(GIC_DIST_BASE + GICD_IPRIORITYR + reg * 4) = regval;
+ LTRACEF("irq %i, new GICD_IPRIORITYR%d = %x, req %x\n",
+ irq, reg, GIC_READ(GIC_DIST_BASE + GICD_IPRIORITYR + reg * 4), regval);
+
+ return 0;
+}
+
+status_t arm_gic_sgi(u_int irq, u_int flags, u_int cpu_mask)
+{
+ u_int val =
+ ((flags & ARM_GIC_SGI_FLAG_TARGET_FILTER_MASK) << 24) |
+ ((cpu_mask & 0xff) << 16) |
+ ((flags & ARM_GIC_SGI_FLAG_NS) ? (1U << 15) : 0) |
+ (irq & 0xf);
+
+ if (irq >= 16)
+ return ERR_INVALID_ARGS;
+
+ LTRACEF("GICD_SGIR: %x\n", val);
+
+ GIC_WRITE(GIC_DIST_BASE + GICD_SGIR, val);
+
+ return NO_ERROR;
+}
+
+status_t mask_interrupt(unsigned int vector)
+{
+ if (vector >= MAX_INT)
+ return ERR_INVALID_ARGS;
+
+ if (arm_gic_interrupt_change_allowed(vector))
+ gic_set_enable(vector, false);
+
+ return NO_ERROR;
+}
+
+status_t unmask_interrupt(unsigned int vector)
+{
+ if (vector >= MAX_INT)
+ return ERR_INVALID_ARGS;
+
+ if (arm_gic_interrupt_change_allowed(vector))
+ gic_set_enable(vector, true);
+
+ return NO_ERROR;
+}
+
+#if ARCH_ARM64
+static uint64_t gicc_read_hppir1_el1(void)
+{
+ uint64_t val = 0;
+ __asm__ volatile("mrs_s %0, " __stringify(ICC_HPPIR1_EL1) : "=r" (val));
+ return val;
+}
+#endif
+
+#if ARCH_ARM
+static uint32_t gicc_read_hppir1_el1(void)
+{
+ uint32_t val = 0;
+ /* aarch32 ICC_HPPIR1, 1111 000 1100 1100 010 */
+ __asm__ volatile("MRC p15, 0, %0, c12, c12, 2" : "=r" (val));
+ return val;
+}
+#endif
+
+uint32_t arm_gic_get_pending_interrupt_id(void)
+{
+ return gicc_read_hppir1_el1();
+}
+
+#if ARCH_ARM64
+uint64_t gicc_read_iar1_el1(void)
+{
+ u64 irqstat = 0;
+ __asm__ volatile("mrs_s %0, " __stringify(ICC_IAR1_EL1) : "=r" (irqstat));
+
+ return irqstat;
+}
+#endif
+
+#if ARCH_ARM
+uint32_t gicc_read_iar1_el1(void)
+{
+ uint32_t val = 0;
+ /* aarch32 ICC_IAR1, 1111 000 1100 1100 000 */
+ __asm__ volatile("MRC p15, 0, %0, c12, c12, 0" : "=r" (val));
+ return val;
+}
+#endif
+
+
+uint64_t arm_gic_acknowledge_irq(void)
+{
+ return gicc_read_iar1_el1();
+}
+
+static void gicc_write_eoi1_el1(uint32_t irq)
+{
+#if ARCH_ARM64
+ __asm__ volatile("msr_s " __stringify(ICC_EOIR1_EL1) ", %0" : : "r" (irq));
+#endif
+#if ARCH_ARM
+ /* aarch32 ICC_EOIR1, 1111 000 1100 1100 001 */
+ __asm__ volatile("MCR p15, 0, %0, c12, c12, 1" : : "r" (irq));
+#endif
+
+}
+
+static void arm_gic_end_of_irq(uint32_t id)
+{
+ gicc_write_eoi1_el1(id);
+}
+
+#if ARCH_ARM64
+uint64_t gicc_read_iar0_el1(void)
+{
+ u64 irqstat = 0;
+ __asm__ volatile("mrs_s %0, " __stringify(ICC_IAR0_EL1) : "=r" (irqstat));
+
+ return irqstat;
+}
+#endif
+
+#if ARCH_ARM
+uint32_t gicc_read_iar0_el1(void)
+{
+ uint32_t val = 0;
+ /* aarch32 ICC_IAR0, 1111 000 1100 1000 000 */
+ __asm__ volatile("MRC p15, 0, %0, c12, c8, 0" : "=r" (val));
+ return val;
+}
+#endif
+
+uint64_t arm_gic_acknowledge_fiq(void)
+{
+ return gicc_read_iar0_el1();
+}
+
+static void gicc_write_eoi0_el1(uint32_t irq)
+{
+#if ARCH_ARM64
+ __asm__ volatile("msr_s " __stringify(ICC_EOIR0_EL1) ", %0" : : "r" (irq));
+#endif
+#if ARCH_ARM
+ /* aarch32 ICC_EOIR0, 1111 000 1100 1000 001 */
+ __asm__ volatile("MCR p15, 0, %0, c12, c8, 1" : : "r" (irq));
+#endif
+}
+
+static void arm_gic_end_of_fiq(uint32_t id)
+{
+ gicc_write_eoi0_el1(id);
+}
+
+static
+enum handler_return __platform_irq(struct iframe *frame)
+{
+ // get the current vector
+ uint32_t iar = arm_gic_acknowledge_irq();
+ unsigned int vector = iar & 0x3ff;
+
+ if (vector >= 0x3fe) {
+ // spurious
+ return INT_NO_RESCHEDULE;
+ }
+
+ THREAD_STATS_INC(interrupts);
+ KEVLOG_IRQ_ENTER(vector);
+
+ uint cpu = arch_curr_cpu_num();
+
+ LTRACEF_LEVEL(2, "iar 0x%x cpu %u currthread %p vector %d pc 0x%lx\n", iar, cpu,
+ get_current_thread(), vector, (uintptr_t)IFRAME_PC(frame));
+
+ // deliver the interrupt
+ enum handler_return ret;
+
+ ret = INT_NO_RESCHEDULE;
+ struct int_handler_struct *handler = get_int_handler(vector, cpu);
+ if (handler->handler)
+ ret = handler->handler(handler->arg);
+
+ arm_gic_end_of_irq(iar);
+
+ LTRACEF_LEVEL(2, "cpu %u exit %d\n", cpu, ret);
+
+ KEVLOG_IRQ_EXIT(vector);
+
+ return ret;
+}
+
+enum handler_return platform_irq(struct iframe *frame)
+{
+#if WITH_LIB_SM
+ uint32_t ahppir = arm_gic_get_pending_interrupt_id();
+ uint32_t pending_irq = ahppir & 0x3ff;
+ struct int_handler_struct *h;
+ uint cpu = arch_curr_cpu_num();
+
+ LTRACEF("ahppir %d\n", ahppir);
+ if (pending_irq < MAX_INT && get_int_handler(pending_irq, cpu)->handler) {
+ enum handler_return ret = 0;
+ uint32_t irq;
+ uint8_t old_priority;
+ spin_lock_saved_state_t state;
+
+ spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS);
+
+ /* Temporarily raise the priority of the interrupt we want to
+ * handle so another interrupt does not take its place before
+ * we can acknowledge it.
+ */
+ old_priority = arm_gic_get_priority(pending_irq);
+ arm_gic_set_priority_locked(pending_irq, 0);
+ DSB;
+ irq = arm_gic_acknowledge_irq() & 0x3ff;
+ arm_gic_set_priority_locked(pending_irq, old_priority);
+
+ spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS);
+
+ LTRACEF("irq %d\n", irq);
+ if (irq < MAX_INT && (h = get_int_handler(pending_irq, cpu))->handler)
+ ret = h->handler(h->arg);
+ else
+ TRACEF("unexpected irq %d != %d may get lost\n", irq, pending_irq);
+ arm_gic_end_of_fiq(irq);
+
+ return ret;
+ }
+ return sm_handle_irq();
+#else
+ return __platform_irq(frame);
+#endif
+}
+
+void platform_fiq(struct iframe *frame)
+{
+#if WITH_LIB_SM
+ sm_handle_fiq();
+#else
+ PANIC_UNIMPLEMENTED;
+#endif
+}
+
+#if WITH_LIB_SM
+static status_t arm_gic_get_next_irq_locked(u_int min_irq, bool per_cpu)
+{
+ u_int irq;
+ u_int max_irq = per_cpu ? GIC_MAX_PER_CPU_INT : MAX_INT;
+ uint cpu = arch_curr_cpu_num();
+
+ if (!per_cpu && min_irq < GIC_MAX_PER_CPU_INT)
+ min_irq = GIC_MAX_PER_CPU_INT;
+
+ for (irq = min_irq; irq < max_irq; irq++)
+ if (get_int_handler(irq, cpu)->handler)
+ return irq;
+
+ return SM_ERR_END_OF_INPUT;
+}
+
+long smc_intc_get_next_irq(smc32_args_t *args)
+{
+ status_t ret;
+ spin_lock_saved_state_t state;
+
+ spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS);
+
+ arm_gic_non_secure_interrupts_frozen = true;
+ ret = arm_gic_get_next_irq_locked(args->params[0], args->params[1]);
+ LTRACEF("min_irq %d, per_cpu %d, ret %d\n",
+ args->params[0], args->params[1], ret);
+
+ spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS);
+
+ return ret;
+}
+
+static u_long enabled_fiq_mask[BITMAP_NUM_WORDS(MAX_INT)];
+
+static void bitmap_update_locked(u_long *bitmap, u_int bit, bool set)
+{
+ u_long mask = 1UL << BITMAP_BIT_IN_WORD(bit);
+
+ bitmap += BITMAP_WORD(bit);
+ if (set)
+ *bitmap |= mask;
+ else
+ *bitmap &= ~mask;
+}
+
+long smc_intc_request_fiq(smc32_args_t *args)
+{
+ u_int fiq = args->params[0];
+ bool enable = args->params[1];
+ spin_lock_saved_state_t state;
+
+ dprintf(SPEW, "%s: fiq %d, enable %d\n", __func__, fiq, enable);
+ spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS);
+
+ arm_gic_set_secure_locked(fiq, true);
+ arm_gic_set_target_locked(fiq, ~0, ~0);
+ arm_gic_set_priority_locked(fiq, 0);
+
+ gic_set_enable(fiq, enable);
+ bitmap_update_locked(enabled_fiq_mask, fiq, enable);
+
+ dprintf(SPEW, "%s: fiq %d, enable %d done\n", __func__, fiq, enable);
+
+ spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS);
+
+ return NO_ERROR;
+}
+
+static u_int current_fiq[8] = { 0x3ff, 0x3ff, 0x3ff, 0x3ff, 0x3ff, 0x3ff, 0x3ff, 0x3ff };
+
+static bool update_fiq_targets(u_int cpu, bool enable, u_int triggered_fiq, bool resume_gicd)
+{
+ u_int i, j;
+ u_long mask;
+ u_int fiq;
+ bool smp = arm_gic_max_cpu() > 0;
+ bool ret = false;
+
+ spin_lock(&gicd_lock); /* IRQs and FIQs are already masked */
+ for (i = 0; i < BITMAP_NUM_WORDS(MAX_INT); i++) {
+ mask = enabled_fiq_mask[i];
+ while (mask) {
+ j = _ffz(~mask);
+ mask &= ~(1UL << j);
+ fiq = i * BITMAP_BITS_PER_WORD + j;
+ if (fiq == triggered_fiq)
+ ret = true;
+ LTRACEF("cpu %d, irq %i, enable %d\n", cpu, fiq, enable);
+ if (smp)
+ arm_gic_set_target_locked(fiq, 1U << cpu, enable ? ~0 : 0);
+ if (!smp || resume_gicd)
+ gic_set_enable(fiq, enable);
+ }
+ }
+ spin_unlock(&gicd_lock);
+ return ret;
+}
+
+static void suspend_resume_fiq(bool resume_gicc, bool resume_gicd)
+{
+ u_int cpu = arch_curr_cpu_num();
+
+ ASSERT(cpu < 8);
+
+ update_fiq_targets(cpu, resume_gicc, ~0, resume_gicd);
+}
+
+status_t sm_intc_fiq_enter(void)
+{
+ u_int cpu = arch_curr_cpu_num();
+ u_int irq = arm_gic_acknowledge_fiq() & 0x3ff;
+ bool fiq_enabled;
+
+ ASSERT(cpu < 8);
+
+ LTRACEF("cpu %d, irq %i\n", cpu, irq);
+
+ if (irq >= 1020) {
+ LTRACEF("spurious fiq: cpu %d, old %d, new %d\n", cpu, current_fiq[cpu], irq);
+ return ERR_NO_MSG;
+ }
+
+ fiq_enabled = update_fiq_targets(cpu, false, irq, false);
+ arm_gic_end_of_fiq(irq);
+
+ if (current_fiq[cpu] != 0x3ff) {
+ dprintf(INFO, "more than one fiq active: cpu %d, old %d, new %d\n", cpu, current_fiq[cpu], irq);
+ return ERR_ALREADY_STARTED;
+ }
+
+ if (!fiq_enabled) {
+ dprintf(INFO, "got disabled fiq: cpu %d, new %d\n", cpu, irq);
+ return ERR_NOT_READY;
+ }
+
+ current_fiq[cpu] = irq;
+
+ return 0;
+}
+
+void sm_intc_fiq_exit(void)
+{
+ u_int cpu = arch_curr_cpu_num();
+
+ ASSERT(cpu < 8);
+
+ LTRACEF("cpu %d, irq %i\n", cpu, current_fiq[cpu]);
+ if (current_fiq[cpu] == 0x3ff) {
+ dprintf(INFO, "%s: no fiq active, cpu %d\n", __func__, cpu);
+ return;
+ }
+ update_fiq_targets(cpu, true, current_fiq[cpu], false);
+ current_fiq[cpu] = 0x3ff;
+}
+#endif
+
+/* vim: set ts=4 sw=4 noexpandtab: */
diff --git a/src/bsp/lk/dev/interrupt/arm_gic_v3/gic600.c b/src/bsp/lk/dev/interrupt/arm_gic_v3/gic600.c
new file mode 100644
index 0000000..7e2f2bf
--- /dev/null
+++ b/src/bsp/lk/dev/interrupt/arm_gic_v3/gic600.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+/*
+ * Driver for GIC-600 specific features. This driver only overrides
+ * APIs that are different to those generic ones in GICv3 driver.
+ *
+ * GIC-600 supports independently power-gating redistributor interface.
+ */
+
+#include <assert.h>
+#include <dev/interrupt/arm_gic.h>
+#include <platform/mt_reg_base.h>
+#include <reg.h>
+
+/* GIC-600 specific register offsets */
+#define GICR_PWRR 0x24
+
+/* GICR_PWRR fields */
+#define PWRR_RDPD_SHIFT 0
+#define PWRR_RDAG_SHIFT 1
+#define PWRR_RDGPD_SHIFT 2
+#define PWRR_RDGPO_SHIFT 3
+
+#define PWRR_RDPD (1 << PWRR_RDPD_SHIFT)
+#define PWRR_RDAG (1 << PWRR_RDAG_SHIFT)
+#define PWRR_RDGPD (1 << PWRR_RDGPD_SHIFT)
+#define PWRR_RDGPO (1 << PWRR_RDGPO_SHIFT)
+
+/*
+ * Values to write to GICR_PWRR register to power redistributor
+ * for operating through the core (GICR_PWRR.RDAG = 0)
+ */
+#define PWRR_ON (0 << PWRR_RDPD_SHIFT)
+#define PWRR_OFF (1 << PWRR_RDPD_SHIFT)
+
+/* GIC-600 specific accessor functions */
+static void gicr_write_pwrr(uintptr_t base, unsigned int val)
+{
+ GIC_WRITE(base + GICR_PWRR,PWRR_ON);
+}
+
+static uint32_t gicr_read_pwrr(uintptr_t base)
+{
+ return GIC_READ(base + GICR_PWRR);
+}
+
+static void gicr_wait_group_not_in_transit(uintptr_t base)
+{
+ /* Check group not transitioning: RDGPD == RDGPO */
+ while (((gicr_read_pwrr(base) & PWRR_RDGPD) >> PWRR_RDGPD_SHIFT) !=
+ ((gicr_read_pwrr(base) & PWRR_RDGPO) >> PWRR_RDGPO_SHIFT))
+ ;
+}
+
+static void gic600_pwr_on(uintptr_t base)
+{
+ do { /* Wait until group not transitioning */
+ gicr_wait_group_not_in_transit(base);
+
+ /* Power on redistributor */
+ gicr_write_pwrr(base, PWRR_ON);
+
+ /*
+ * Wait until the power on state is reflected.
+ * If RDPD == 0 then powered on.
+ */
+ } while ((gicr_read_pwrr(base) & PWRR_RDPD) != PWRR_ON);
+}
+
+static void gic600_pwr_off(uintptr_t base)
+{
+
+ /* Wait until group not transitioning */
+ gicr_wait_group_not_in_transit(base);
+
+ /* Power off redistributor */
+ gicr_write_pwrr(base, PWRR_OFF);
+
+ /*
+ * If this is the last man, turning this redistributor frame off will
+ * result in the group itself being powered off and RDGPD = 1.
+ * In that case, wait as long as it's in transition, or has aborted
+ * the transition altogether for any reason.
+ */
+ if ((gicr_read_pwrr(base) & PWRR_RDGPD) != 0) {
+ /* Wait until group not transitioning */
+ gicr_wait_group_not_in_transit(base);
+ }
+}
+
+/*
+ * Power off GIC-600 redistributor
+ */
+void gicv3_rdistif_off(void)
+{
+ uintptr_t gicr_base;
+
+ gicr_base = GIC_REDIS_BASE;
+ assert(gicr_base);
+
+ /* Attempt to power redistributor off */
+ gic600_pwr_off(gicr_base);
+}
+
+/*
+ * Power on GIC-600 redistributor
+ */
+void gicv3_rdistif_on(void)
+{
+ uintptr_t gicr_base;
+
+ gicr_base = GIC_REDIS_BASE;
+ assert(gicr_base);
+
+ /* Power redistributor on */
+ gic600_pwr_on(gicr_base);
+}
+
+/* vim: set ts=4 sw=4 noexpandtab: */
diff --git a/src/bsp/lk/dev/interrupt/arm_gic_v3/include/dev/interrupt/arm_gic.h b/src/bsp/lk/dev/interrupt/arm_gic_v3/include/dev/interrupt/arm_gic.h
new file mode 100644
index 0000000..3802032
--- /dev/null
+++ b/src/bsp/lk/dev/interrupt/arm_gic_v3/include/dev/interrupt/arm_gic.h
@@ -0,0 +1,196 @@
+/*
+ * Copyright (c) 2013, Google Inc. All rights reserved.
+ *
+ * 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 __DEV_INTERRUPT_ARM_GIC_H
+#define __DEV_INTERRUPT_ARM_GIC_H
+
+#include <sys/types.h>
+
+#define ICC_IAR1_EL1 sys_reg(3, 0, 12, 12, 0)
+#define ICC_EOIR1_EL1 sys_reg(3, 0, 12, 12, 1)
+#define ICC_IAR0_EL1 sys_reg(3, 0, 12, 8, 0)
+#define ICC_EOIR0_EL1 sys_reg(3, 0, 12, 8, 1)
+#define ICC_HPPIR1_EL1 sys_reg(3, 0, 12, 12, 2)
+
+#define GICD_CTLR_ENABLE_GRP0 (1 << 0)
+#define GICD_CTLR_ENGRP1NS (1 << 1)
+#define GICD_CTLR_ENGRP1S (1 << 2)
+#define GICD_CTLR_ARE (1 << 4)
+#define GICD_CTLR_ARE_NS (1 << 5)
+#define GICD_CTLR_DS (1 << 6)
+#define GICD_CTLR_E1NWF (1 << 7)
+#define GICD_CTLR_RWP (1 << 31)
+
+#define GICR_WAKER_ProcessorSleep (1 << 1)
+#define GICR_WAKER_ChildrenAsleep (1 << 2)
+
+/* GICD_TYPER bit definitions */
+#define IT_LINES_NO_MASK (0x1f)
+#define ENABLE_GRP0 (1 << 0)
+#define ENABLE_GRP1 (1 << 1)
+
+/* Mask for the priority field common to all GIC interfaces */
+#define GIC_PRI_MASK 0xff
+
+/* GICC_CTLR bit definitions */
+#define EOI_MODE_NS (1 << 10)
+#define EOI_MODE_S (1 << 9)
+#define IRQ_BYP_DIS_GRP1 (1 << 8)
+#define FIQ_BYP_DIS_GRP1 (1 << 7)
+#define IRQ_BYP_DIS_GRP0 (1 << 6)
+#define FIQ_BYP_DIS_GRP0 (1 << 5)
+#define CBPR (1 << 4)
+#define FIQ_EN (1 << 3)
+#define ACK_CTL (1 << 2)
+
+/* GICv3 ICC_SRE register bit definitions*/
+#define ICC_SRE_EN (1 << 3)
+#define ICC_SRE_SRE (1 << 0)
+
+/* GICC_IIDR bit masks and shifts */
+#define GICC_IIDR_PID_SHIFT 20
+#define GICC_IIDR_ARCH_SHIFT 16
+#define GICC_IIDR_REV_SHIFT 12
+#define GICC_IIDR_IMP_SHIFT 0
+
+#define GICC_IIDR_PID_MASK 0xfff
+#define GICC_IIDR_ARCH_MASK 0xf
+#define GICC_IIDR_REV_MASK 0xf
+#define GICC_IIDR_IMP_MASK 0xfff
+
+#define SZ_64K (0x00010000)
+#define INT_POL_SECCTL_NUM 20
+#define NR_INT_POL_CTL (20)
+
+/* main cpu regs */
+#define GICC_CTLR 0x0000
+#define GICC_PMR 0x0004
+#define GICC_BPR 0x0008
+#define GICC_IAR 0x000c
+#define GICC_EOIR 0x0010
+#define GICC_RPR 0x0014
+#define GICC_HPPIR 0x0018
+#define GICC_APBR 0x001c
+#define GICC_AIAR 0x0020
+#define GICC_AEOIR 0x0024
+#define GICC_AHPPIR 0x0028
+#define GICC_APR 0x00d0
+#define GICC_NSAPR 0x00e0
+#define GICC_IIDR 0x00fc
+#define GICC_DIR 0x1000
+
+/* distribution regs */
+#define GICD_CTLR 0x000
+#define GICD_TYPER 0x004
+#define GICD_IIDR 0x008
+#define GICD_STATUSR 0x010
+#define GICD_SEIR 0x068
+#define GICD_IGROUPR 0x080
+#define GICD_ISENABLER 0x100
+#define GICD_ICENABLER 0x180
+#define GICD_ISPENDR 0x200
+#define GICD_ICPENDR 0x280
+#define GICD_ISACTIVER 0x300
+#define GICD_ICACTIVER 0x380
+#define GICD_IPRIORITYR 0x400
+#define GICD_ITARGETSR 0x800
+#define GICD_ICFGR 0xc00
+#define GICD_IGRPMODR 0xd00
+#define GICD_NSACR 0xe00
+#define GICD_SGIR 0xf00
+#define GICD_CPENDSGIR 0xf10
+#define GICD_SPENDSGIR 0xf20
+#define GICD_IROUTER 0x6000
+#define GICD_PIDR2 0xFFE8
+
+/*
+ * Re-Distributor registers, offsets from RD_base
+ */
+#define GICR_V3_CTLR GICD_CTLR
+#define GICR_V3_IIDR 0x0004
+#define GICR_V3_TYPER 0x0008
+#define GICR_V3_CTLR_RWP 0x0008
+#define GICR_V3_STATUSR GICD_STATUSR
+#define GICR_V3_WAKER 0x0014
+#define GICR_V3_SETLPIR 0x0040
+#define GICR_V3_CLRLPIR 0x0048
+#define GICR_V3_SEIR GICD_SEIR
+#define GICR_V3_PROPBASER 0x0070
+#define GICR_V3_PENDBASER 0x0078
+#define GICE_V3_IGROUP0 0x0080
+#define GICR_V3_INVLPIR 0x00A0
+#define GICR_V3_INVALLR 0x00B0
+#define GICR_V3_SYNCR 0x00C0
+#define GICR_V3_MOVLPIR 0x0100
+#define GICR_V3_MOVALLR 0x0110
+#define GICE_V3_IGRPMOD0 0x0d00
+#define GICR_V3_PIDR2 GICD_PIDR2
+
+#define GIC_V3_PIDR2_ARCH_MASK 0xf0
+#define GIC_V3_PIDR2_ARCH_GICv3 0x30
+#define GIC_V3_PIDR2_ARCH_GICv4 0x40
+
+#define GICR_V3_WAKER_ProcessorSleep (1U << 1)
+#define GICR_V3_WAKER_ChildrenAsleep (1U << 2)
+
+/*******************************************************************************
+ * GICv3 CPU interface registers & constants
+ ******************************************************************************/
+/* SCR bit definitions */
+#define SCR_NS_BIT (1U << 0)
+
+/* ICC_SRE bit definitions*/
+#define ICC_SRE_EN_BIT (1 << 3)
+#define ICC_SRE_DIB_BIT (1 << 2)
+#define ICC_SRE_DFB_BIT (1 << 1)
+#define ICC_SRE_SRE_BIT (1 << 0)
+
+#define GIC_READ(a) readl(a)
+#define GIC_WRITE(a, v) writel(v, a)
+
+enum {
+ /* Ignore cpu_mask and forward interrupt to all CPUs other than the current cpu */
+ ARM_GIC_SGI_FLAG_TARGET_FILTER_NOT_SENDER = 0x1,
+ /* Ignore cpu_mask and forward interrupt to current CPU only */
+ ARM_GIC_SGI_FLAG_TARGET_FILTER_SENDER = 0x2,
+ ARM_GIC_SGI_FLAG_TARGET_FILTER_MASK = 0x3,
+ /*
+ * Only forward the interrupt to CPUs that has the interrupt
+ * configured as group 1 (non-secure)
+ */
+ ARM_GIC_SGI_FLAG_NS = 0x4,
+};
+status_t arm_gic_sgi(u_int irq, u_int flags, u_int cpu_mask);
+
+void arm_gic_init(void);
+
+/* For lk called in EL3, MMU disabled context. */
+void gic_setup(void);
+
+#if GICV3_SUPPORT_GIC600
+void gicv3_rdistif_off(void);
+void gicv3_rdistif_on(void);
+#endif
+
+#endif
+
+/* vim: set ts=4 sw=4 noexpandtab: */
diff --git a/src/bsp/lk/dev/interrupt/arm_gic_v3/rules.mk b/src/bsp/lk/dev/interrupt/arm_gic_v3/rules.mk
new file mode 100644
index 0000000..a361ce6
--- /dev/null
+++ b/src/bsp/lk/dev/interrupt/arm_gic_v3/rules.mk
@@ -0,0 +1,14 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/arm_gic.c
+
+ifeq ($(GICV3_SUPPORT_GIC600),1)
+MODULE_SRCS += \
+ $(LOCAL_DIR)/gic600.c
+endif
+
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/interrupt/or1k_pic/or1k_pic.c b/src/bsp/lk/dev/interrupt/or1k_pic/or1k_pic.c
new file mode 100644
index 0000000..12fcadf
--- /dev/null
+++ b/src/bsp/lk/dev/interrupt/or1k_pic/or1k_pic.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2015 Stefan Kristiansson
+ *
+ * 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 <kernel/thread.h>
+#include <platform/interrupts.h>
+#include <platform/pic.h>
+#include <arch/or1k.h>
+
+static spin_lock_t gicd_lock;
+#if WITH_LIB_SM
+#define GICD_LOCK_FLAGS SPIN_LOCK_FLAG_IRQ_FIQ
+#else
+#define GICD_LOCK_FLAGS SPIN_LOCK_FLAG_INTERRUPTS
+#endif
+
+struct int_handler_struct {
+ int_handler handler;
+ void *arg;
+};
+
+static struct int_handler_struct int_handler_table[MAX_INT];
+
+void register_int_handler(unsigned int vector, int_handler handler, void *arg)
+{
+ spin_lock_saved_state_t state;
+
+ if (vector >= MAX_INT)
+ panic("%s: vector out of range %d\n", __FUNCTION__, vector);
+
+ spin_lock_save(&gicd_lock, &state, GICD_LOCK_FLAGS);
+
+ int_handler_table[vector].handler = handler;
+ int_handler_table[vector].arg = arg;
+
+ spin_unlock_restore(&gicd_lock, state, GICD_LOCK_FLAGS);
+}
+
+status_t mask_interrupt(unsigned int vector)
+{
+ if (vector >= MAX_INT)
+ return ERR_INVALID_ARGS;
+
+ mtspr(OR1K_SPR_PIC_PICMR_ADDR, mfspr(OR1K_SPR_PIC_PICMR_ADDR) & ~(1 << vector));
+
+ return NO_ERROR;
+}
+
+status_t unmask_interrupt(unsigned int vector)
+{
+ if (vector >= MAX_INT)
+ return ERR_INVALID_ARGS;
+
+ mtspr(OR1K_SPR_PIC_PICMR_ADDR, mfspr(OR1K_SPR_PIC_PICMR_ADDR) | (1 << vector));
+
+ return NO_ERROR;
+}
+
+enum handler_return platform_irq(void)
+{
+ enum handler_return ret = INT_NO_RESCHEDULE;
+
+ uint irq = __builtin_ffs(mfspr(OR1K_SPR_PIC_PICSR_ADDR)) - 1;
+
+ if (irq < MAX_INT && int_handler_table[irq].handler)
+ ret = int_handler_table[irq].handler(int_handler_table[irq].arg);
+
+ return ret;
+}
diff --git a/src/bsp/lk/dev/interrupt/or1k_pic/rules.mk b/src/bsp/lk/dev/interrupt/or1k_pic/rules.mk
new file mode 100644
index 0000000..1148b7f
--- /dev/null
+++ b/src/bsp/lk/dev/interrupt/or1k_pic/rules.mk
@@ -0,0 +1,8 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/or1k_pic.c
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/keys/gpio_keypad.c b/src/bsp/lk/dev/keys/gpio_keypad.c
new file mode 100644
index 0000000..f0b8d12
--- /dev/null
+++ b/src/bsp/lk/dev/keys/gpio_keypad.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2009, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <assert.h>
+#include <bits.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dev/keys.h>
+#include <dev/gpio.h>
+#include <dev/gpio_keypad.h>
+#include <kernel/event.h>
+#include <kernel/timer.h>
+
+struct gpio_kp {
+ struct gpio_keypad_info *keypad_info;
+ struct timer timer;
+ event_t full_scan;
+ int current_output;
+ unsigned int some_keys_pressed:2;
+ unsigned long keys_pressed[0];
+};
+
+/* TODO: Support multiple keypads? */
+static struct gpio_kp *keypad;
+
+static void check_output(struct gpio_kp *kp, int out, int polarity)
+{
+ struct gpio_keypad_info *kpinfo = kp->keypad_info;
+ int key_index;
+ int in;
+ int gpio;
+ int changed = 0;
+
+ key_index = out * kpinfo->ninputs;
+ for (in = 0; in < kpinfo->ninputs; in++, key_index++) {
+ gpio = kpinfo->input_gpios[in];
+ changed = 0;
+ if (gpio_get(gpio) ^ !polarity) {
+ if (kp->some_keys_pressed < 3)
+ kp->some_keys_pressed++;
+ changed = !bitmap_set(kp->keys_pressed, key_index);
+ } else {
+ changed = bitmap_clear(kp->keys_pressed, key_index);
+ }
+ if (changed) {
+ int state = bitmap_test(kp->keys_pressed, key_index);
+ keys_post_event(kpinfo->keymap[key_index], state);
+ }
+ }
+
+ /* sets up the right state for the next poll cycle */
+ gpio = kpinfo->output_gpios[out];
+ if (kpinfo->flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set(gpio, !polarity);
+ else
+ gpio_config(gpio, GPIO_INPUT);
+}
+
+static enum handler_return
+gpio_keypad_timer_func(struct timer *timer, time_t now, void *arg)
+{
+ struct gpio_kp *kp = keypad;
+ struct gpio_keypad_info *kpinfo = kp->keypad_info;
+ int polarity = !!(kpinfo->flags & GPIOKPF_ACTIVE_HIGH);
+ int out;
+ int gpio;
+
+ out = kp->current_output;
+ if (out == kpinfo->noutputs) {
+ out = 0;
+ kp->some_keys_pressed = 0;
+ } else {
+ check_output(kp, out, polarity);
+ out++;
+ }
+
+ kp->current_output = out;
+ if (out < kpinfo->noutputs) {
+ gpio = kpinfo->output_gpios[out];
+ if (kpinfo->flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set(gpio, polarity);
+ else
+ gpio_config(gpio, polarity ? GPIO_OUTPUT : 0);
+ timer_set_oneshot(timer, kpinfo->settle_time,
+ gpio_keypad_timer_func, NULL);
+ goto done;
+ }
+
+ if (/*!kp->use_irq*/ 1 || kp->some_keys_pressed) {
+ event_signal(&kp->full_scan, false);
+ timer_set_oneshot(timer, kpinfo->poll_time,
+ gpio_keypad_timer_func, NULL);
+ goto done;
+ }
+
+#if 0
+ /* No keys are pressed, reenable interrupt */
+ for (out = 0; out < kpinfo->noutputs; out++) {
+ if (gpio_keypad_flags & GPIOKPF_DRIVE_INACTIVE)
+ gpio_set(kpinfo->output_gpios[out], polarity);
+ else
+ gpio_config(kpinfo->output_gpios[out], polarity ? GPIO_OUTPUT : 0);
+ }
+ for (in = 0; in < kpinfo->ninputs; in++)
+ enable_irq(gpio_to_irq(kpinfo->input_gpios[in]));
+ return INT_RESCHEDULE;
+#endif
+
+done:
+ return INT_RESCHEDULE;
+}
+
+void gpio_keypad_init(struct gpio_keypad_info *kpinfo)
+{
+ int key_count;
+ int output_val;
+ int output_cfg;
+ int i;
+ int len;
+
+ ASSERT(kpinfo->keymap && kpinfo->input_gpios && kpinfo->output_gpios);
+ key_count = kpinfo->ninputs * kpinfo->noutputs;
+
+ len = sizeof(struct gpio_kp) + (sizeof(unsigned long) *
+ BITMAP_NUM_WORDS(key_count));
+ keypad = malloc(len);
+ ASSERT(keypad);
+
+ memset(keypad, 0, len);
+ keypad->keypad_info = kpinfo;
+
+ output_val = (!!(kpinfo->flags & GPIOKPF_ACTIVE_HIGH)) ^
+ (!!(kpinfo->flags & GPIOKPF_DRIVE_INACTIVE));
+ output_cfg = kpinfo->flags & GPIOKPF_DRIVE_INACTIVE ? GPIO_OUTPUT : 0;
+ for (i = 0; i < kpinfo->noutputs; i++) {
+ gpio_set(kpinfo->output_gpios[i], output_val);
+ gpio_config(kpinfo->output_gpios[i], output_cfg);
+ }
+ for (i = 0; i < kpinfo->ninputs; i++)
+ gpio_config(kpinfo->input_gpios[i], GPIO_INPUT);
+
+ keypad->current_output = kpinfo->noutputs;
+
+ event_init(&keypad->full_scan, false, EVENT_FLAG_AUTOUNSIGNAL);
+ timer_initialize(&keypad->timer);
+ timer_set_oneshot(&keypad->timer, 0, gpio_keypad_timer_func, NULL);
+
+ /* wait for the keypad to complete one full scan */
+ event_wait(&keypad->full_scan);
+}
diff --git a/src/bsp/lk/dev/keys/keys.c b/src/bsp/lk/dev/keys/keys.c
new file mode 100644
index 0000000..3e7c452
--- /dev/null
+++ b/src/bsp/lk/dev/keys/keys.c
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2008, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google, Inc. nor the names of its contributors
+ * may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <bits.h>
+#include <debug.h>
+#include <string.h>
+#include <dev/keys.h>
+
+static unsigned long key_bitmap[BITMAP_NUM_WORDS(MAX_KEYS)];
+
+void keys_init(void)
+{
+ memset(key_bitmap, 0, sizeof(key_bitmap));
+}
+
+void keys_post_event(uint16_t code, int16_t value)
+{
+ if (code >= MAX_KEYS) {
+ dprintf(INFO, "Invalid keycode posted: %d\n", code);
+ return;
+ }
+
+ /* TODO: Implement an actual event queue if it becomes necessary */
+ if (value)
+ bitmap_set(key_bitmap, code);
+ else
+ bitmap_clear(key_bitmap, code);
+
+// dprintf(INFO, "key state change: %d %d\n", code, value);
+}
+
+int keys_get_state(uint16_t code)
+{
+ if (code >= MAX_KEYS) {
+ dprintf(INFO, "Invalid keycode requested: %d\n", code);
+ return -1;
+ }
+ return bitmap_test(key_bitmap, code);
+}
diff --git a/src/bsp/lk/dev/keys/rules.mk b/src/bsp/lk/dev/keys/rules.mk
new file mode 100644
index 0000000..8a30ea0
--- /dev/null
+++ b/src/bsp/lk/dev/keys/rules.mk
@@ -0,0 +1,14 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/keys.c
+
+ifeq ($(KEYS_USE_GPIO_KEYPAD),1)
+MODULE_SRCS += \
+ $(LOCAL_DIR)/gpio_keypad.c
+endif
+
+include make/module.mk
+
diff --git a/src/bsp/lk/dev/net/pcnet/pcnet.c b/src/bsp/lk/dev/net/pcnet/pcnet.c
new file mode 100644
index 0000000..b2dbada
--- /dev/null
+++ b/src/bsp/lk/dev/net/pcnet/pcnet.c
@@ -0,0 +1,636 @@
+/*
+ * Copyright (c) 2013 Corey Tabaka
+ * 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 <reg.h>
+#include <err.h>
+#include <pcnet.h>
+#include <debug.h>
+#include <trace.h>
+#include <assert.h>
+#include <arch/x86.h>
+#include <platform/pc.h>
+#include <platform/pcnet.h>
+#include <platform/interrupts.h>
+#include <kernel/thread.h>
+#include <kernel/mutex.h>
+#include <kernel/event.h>
+#include <dev/class/netif.h>
+#include <dev/pci.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <string.h>
+#include <lwip/pbuf.h>
+#include <lk/init.h>
+
+#define LOCAL_TRACE 0
+
+#define PCNET_INIT_TIMEOUT 20000
+#define MAX_PACKET_SIZE 1518
+
+#define QEMU_IRQ_BUG_WORKAROUND 1
+
+struct pcnet_state {
+ int irq;
+ addr_t base;
+
+ uint8_t padr[6];
+
+ struct init_block_32 *ib;
+
+ struct rd_style3 *rd;
+ struct td_style3 *td;
+
+ struct pbuf **rx_buffers;
+ struct pbuf **tx_buffers;
+
+ /* queue accounting */
+ int rd_head;
+ int td_head;
+ int td_tail;
+
+ int rd_count;
+ int td_count;
+
+ int tx_pending;
+
+ mutex_t tx_lock;
+
+ /* bottom half state */
+ event_t event;
+ event_t initialized;
+ bool done;
+
+ struct netstack_state *netstack_state;
+};
+
+static status_t pcnet_init(struct device *dev);
+static status_t pcnet_read_pci_config(struct device *dev, pci_location_t *loc);
+
+static enum handler_return pcnet_irq_handler(void *arg);
+
+static int pcnet_thread(void *arg);
+static bool pcnet_service_tx(struct device *dev);
+static bool pcnet_service_rx(struct device *dev);
+
+static status_t pcnet_set_state(struct device *dev, struct netstack_state *state);
+static ssize_t pcnet_get_hwaddr(struct device *dev, void *buf, size_t max_len);
+static ssize_t pcnet_get_mtu(struct device *dev);
+
+static status_t pcnet_output(struct device *dev, struct pbuf *p);
+
+static struct netif_ops pcnet_ops = {
+ .std = {
+ .init = pcnet_init,
+ },
+
+ .set_state = pcnet_set_state,
+ .get_hwaddr = pcnet_get_hwaddr,
+ .get_mtu = pcnet_get_mtu,
+
+ .output = pcnet_output,
+};
+
+DRIVER_EXPORT(netif, &pcnet_ops.std);
+
+static inline uint32_t pcnet_read_csr(struct device *dev, uint8_t rap)
+{
+ struct pcnet_state *state = dev->state;
+
+ outpd(state->base + REG_RAP, rap);
+ return inpd(state->base + REG_RDP);
+}
+
+static inline void pcnet_write_csr(struct device *dev, uint8_t rap, uint16_t data)
+{
+ struct pcnet_state *state = dev->state;
+
+ outpd(state->base + REG_RAP, rap);
+ outpd(state->base + REG_RDP, data);
+}
+
+static inline uint32_t pcnet_read_bcr(struct device *dev, uint8_t rap)
+{
+ struct pcnet_state *state = dev->state;
+
+ outpd(state->base + REG_RAP, rap);
+ return inpd(state->base + REG_BDP);
+}
+
+static inline void pcnet_write_bcr(struct device *dev, uint8_t rap, uint16_t data)
+{
+ struct pcnet_state *state = dev->state;
+
+ outpd(state->base + REG_RAP, rap);
+ outpd(state->base + REG_BDP, data);
+}
+
+static status_t pcnet_init(struct device *dev)
+{
+ status_t res = NO_ERROR;
+ pci_location_t loc;
+ int i;
+
+ const struct platform_pcnet_config *config = dev->config;
+
+ if (!config)
+ return ERR_NOT_CONFIGURED;
+
+ if (pci_find_pci_device(&loc, config->device_id, config->vendor_id, config->index) != _PCI_SUCCESSFUL)
+ return ERR_NOT_FOUND;
+
+ struct pcnet_state *state = calloc(1, sizeof(struct pcnet_state));
+ if (!state)
+ return ERR_NO_MEMORY;
+
+ dev->state = state;
+
+ res = pcnet_read_pci_config(dev, &loc);
+ if (res)
+ goto error;
+
+ for (i=0; i < 6; i++)
+ state->padr[i] = inp(state->base + i);
+
+ LTRACEF("MAC: %02x:%02x:%02x:%02x:%02x:%02x\n", state->padr[0], state->padr[1], state->padr[2],
+ state->padr[3], state->padr[4], state->padr[5]);
+
+ /* put the controller into 32bit wide mode by performing a 32bit write to CSR0 */
+ outpd(state->base + 0, 0);
+
+ /* stop the controller for configuration */
+ pcnet_write_csr(dev, 0, CSR0_STOP);
+
+ /* setup 32bit (style 3) structures, burst, all CSR4 bits valid, TDM1[29] is ADD_FCS */
+ pcnet_write_csr(dev, 58, 3);
+
+ /* DMA plus enable */
+ pcnet_write_csr(dev, 4, pcnet_read_csr(dev, 4) | CSR4_DMAPLUS);
+
+ /* allocate 128 tx and 128 rx descriptor rings */
+ state->td_count = 128;
+ state->rd_count = 128;
+ state->td = memalign(16, state->td_count * DESC_SIZE);
+ state->rd = memalign(16, state->rd_count * DESC_SIZE);
+
+ state->rx_buffers = calloc(state->rd_count, sizeof(struct pbuf *));
+ state->tx_buffers = calloc(state->td_count, sizeof(struct pbuf *));
+
+ state->tx_pending = 0;
+
+ if (!state->td || !state->rd || !state->tx_buffers || !state->rx_buffers) {
+ res = ERR_NO_MEMORY;
+ goto error;
+ }
+
+ memset(state->td, 0, state->td_count * DESC_SIZE);
+ memset(state->rd, 0, state->rd_count * DESC_SIZE);
+
+ /* allocate temporary init block space */
+ state->ib = memalign(4, sizeof(struct init_block_32));
+ if (!state->ib) {
+ res = ERR_NO_MEMORY;
+ goto error;
+ }
+
+ LTRACEF("Init block addr: %p\n", state->ib);
+
+ /* setup init block */
+ state->ib->tlen = 7; // 128 descriptors
+ state->ib->rlen = 7; // 128 descriptors
+ state->ib->mode = 0;
+
+ state->ib->ladr = ~0;
+ state->ib->tdra = (uint32_t) state->td;
+ state->ib->rdra = (uint32_t) state->rd;
+
+ memcpy(state->ib->padr, state->padr, 6);
+
+ /* load the init block address */
+ pcnet_write_csr(dev, 1, (uint32_t) state->ib);
+ pcnet_write_csr(dev, 2, (uint32_t) state->ib >> 16);
+
+ /* setup receive descriptors */
+ for (i=0; i < state->rd_count; i++) {
+ //LTRACEF("Allocating pbuf %d\n", i);
+ struct pbuf *p = pbuf_alloc(PBUF_RAW, MAX_PACKET_SIZE, PBUF_RAM);
+
+ state->rd[i].rbadr = (uint32_t) p->payload;
+ state->rd[i].bcnt = -p->tot_len;
+ state->rd[i].ones = 0xf;
+ state->rd[i].own = 1;
+
+ state->rx_buffers[i] = p;
+ }
+
+ mutex_init(&state->tx_lock);
+
+ state->done = false;
+ event_init(&state->event, false, EVENT_FLAG_AUTOUNSIGNAL);
+ event_init(&state->initialized, false, 0);
+
+ /* start up a thread to process packet activity */
+ thread_resume(thread_create("[pcnet bh]", pcnet_thread, dev, DEFAULT_PRIORITY,
+ DEFAULT_STACK_SIZE));
+
+ register_int_handler(state->irq, pcnet_irq_handler, dev);
+ unmask_interrupt(state->irq);
+
+#if QEMU_IRQ_BUG_WORKAROUND
+ register_int_handler(INT_BASE + 15, pcnet_irq_handler, dev);
+ unmask_interrupt(INT_BASE + 15);
+#endif
+
+ /* wait for initialization to complete */
+ res = event_wait_timeout(&state->initialized, PCNET_INIT_TIMEOUT);
+ if (res) {
+ /* TODO: cancel bottom half thread and tear down device instance */
+ LTRACEF("Failed to wait for IDON: %d\n", res);
+ return res;
+ }
+
+ LTRACE_EXIT;
+ return res;
+
+error:
+ LTRACEF("Error: %d\n", res);
+
+ if (state) {
+ free(state->td);
+ free(state->rd);
+ free(state->ib);
+ free(state->tx_buffers);
+ free(state->rx_buffers);
+ }
+
+ free(state);
+
+ return res;
+}
+
+static status_t pcnet_read_pci_config(struct device *dev, pci_location_t *loc)
+{
+ status_t res = NO_ERROR;
+ pci_config_t config;
+ uint8_t *buf = (uint8_t *) &config;
+ unsigned i;
+
+ DEBUG_ASSERT(dev->state);
+
+ struct pcnet_state *state = dev->state;
+
+ for (i=0; i < sizeof(config); i++)
+ pci_read_config_byte(loc, i, buf + i);
+
+ LTRACEF("Resources:\n");
+
+ for (i=0; i < countof(config.base_addresses); i++) {
+ if (config.base_addresses[i] & 0x1) {
+ LTRACEF(" BAR %d I/O REG: %04x\n", i, config.base_addresses[i] & ~0x3);
+
+ state->base = config.base_addresses[i] & ~0x3;
+ break;
+ }
+ }
+
+ if (!state->base) {
+ res = ERR_NOT_CONFIGURED;
+ goto error;
+ }
+
+ if (config.interrupt_line != 0xff) {
+ LTRACEF(" IRQ %u\n", config.interrupt_line);
+
+ state->irq = config.interrupt_line + INT_BASE;
+ } else {
+ res = ERR_NOT_CONFIGURED;
+ goto error;
+ }
+
+ LTRACEF("Command: %04x\n", config.command);
+ LTRACEF("Status: %04x\n", config.status);
+
+ pci_write_config_half(loc, PCI_CONFIG_COMMAND,
+ (config.command | PCI_COMMAND_IO_EN | PCI_COMMAND_BUS_MASTER_EN) & ~PCI_COMMAND_MEM_EN);
+
+error:
+ return res;
+}
+
+static enum handler_return pcnet_irq_handler(void *arg)
+{
+ struct device *dev = arg;
+ struct pcnet_state *state = dev->state;
+
+ mask_interrupt(state->irq);
+
+#if QEMU_IRQ_BUG_WORKAROUND
+ mask_interrupt(INT_BASE + 15);
+#endif
+
+ event_signal(&state->event, false);
+
+ return INT_RESCHEDULE;
+}
+
+static int pcnet_thread(void *arg)
+{
+ DEBUG_ASSERT(arg);
+
+ struct device *dev = arg;
+ struct pcnet_state *state = dev->state;
+
+ /* kick off init, enable ints, and start operation */
+ pcnet_write_csr(dev, 0, CSR0_INIT | CSR0_IENA | CSR0_STRT);
+
+ while (!state->done) {
+ LTRACEF("Waiting for event.\n");
+ //event_wait_timeout(&state->event, 5000);
+ event_wait(&state->event);
+
+ int csr0 = pcnet_read_csr(dev, 0);
+
+ /* disable interrupts at the controller */
+ pcnet_write_csr(dev, 0, csr0 & ~CSR0_IENA);
+
+ LTRACEF("CSR0 = %04x\n", csr0);
+
+#if LOCAL_TRACE
+ if (csr0 & CSR0_RINT) TRACEF("RINT\n");
+ if (csr0 & CSR0_TINT) TRACEF("TINT\n");
+#endif
+
+ if (csr0 & CSR0_IDON) {
+ LTRACEF("IDON\n");
+
+ /* free the init block that we no longer need */
+ free(state->ib);
+ state->ib = NULL;
+
+ event_signal(&state->initialized, true);
+ }
+
+ if (csr0 & CSR0_ERR) {
+ LTRACEF("ERR\n");
+
+ /* TODO: handle errors, though not many need it */
+
+ /* clear flags, preserve necessary enables */
+ pcnet_write_csr(dev, 0, csr0 & (CSR0_TXON | CSR0_RXON | CSR0_IENA));
+ }
+
+ bool again = !!(csr0 & (CSR0_RINT | CSR0_TINT));
+ while (again) {
+ again = pcnet_service_tx(dev) | pcnet_service_rx(dev);
+ }
+
+ /* enable interrupts at the controller */
+ pcnet_write_csr(dev, 0, CSR0_IENA);
+ unmask_interrupt(state->irq);
+
+#if QEMU_IRQ_BUG_WORKAROUND
+ unmask_interrupt(INT_BASE + 15);
+#endif
+ }
+
+ return 0;
+}
+
+static bool pcnet_service_tx(struct device *dev)
+{
+ LTRACE_ENTRY;
+
+ struct pcnet_state *state = dev->state;
+
+ mutex_acquire(&state->tx_lock);
+
+ struct td_style3 *td = &state->td[state->td_tail];
+
+ if (state->tx_pending && td->own == 0) {
+ struct pbuf *p = state->tx_buffers[state->td_tail];
+ DEBUG_ASSERT(p);
+
+ state->tx_buffers[state->td_tail] = NULL;
+
+ LTRACEF("Retiring packet: td_tail=%d p=%p tot_len=%u\n", state->td_tail, p, p->tot_len);
+
+ state->tx_pending--;
+ state->td_tail = (state->td_tail + 1) % state->td_count;
+
+ if (td->err) {
+ LTRACEF("Descriptor error status encountered\n");
+ hexdump8(td, sizeof(*td));
+ }
+
+ mutex_release(&state->tx_lock);
+
+ pbuf_free(p);
+
+ LTRACE_EXIT;
+ return true;
+ } else {
+ mutex_release(&state->tx_lock);
+
+#if 0
+ LTRACEF("Nothing to do for TX.\n");
+ for (int i=0; i < state->td_count; i++)
+ printf("%d ", state->td[i].own);
+ printf("\n");
+#endif
+
+ LTRACE_EXIT;
+ return false;
+ }
+}
+
+static bool pcnet_service_rx(struct device *dev)
+{
+ LTRACE_ENTRY;
+
+ struct pcnet_state *state = dev->state;
+
+ struct rd_style3 *rd = &state->rd[state->rd_head];
+
+ if (rd->own == 0) {
+ struct pbuf *p = state->rx_buffers[state->rd_head];
+ DEBUG_ASSERT(p);
+
+ LTRACEF("Processing RX descriptor %d\n", state->rd_head);
+
+ if (rd->err) {
+ LTRACEF("Descriptor error status encountered\n");
+ hexdump8(rd, sizeof(*rd));
+ } else {
+ if (rd->mcnt <= p->tot_len) {
+
+ pbuf_realloc(p, rd->mcnt);
+
+#if LOCAL_TRACE
+ LTRACEF("payload=%p len=%u\n", p->payload, p->tot_len);
+ hexdump8(p->payload, p->tot_len);
+#endif
+
+ class_netstack_input(dev, state->netstack_state, p);
+
+ p = state->rx_buffers[state->rd_head] = pbuf_alloc(PBUF_RAW, MAX_PACKET_SIZE, PBUF_RAM);
+ } else {
+ LTRACEF("RX packet size error: mcnt = %u, buf len = %u\n", rd->mcnt, p->tot_len);
+ }
+ }
+
+ memset(rd, 0, sizeof(*rd));
+ memset(p->payload, 0, p->tot_len);
+
+ rd->rbadr = (uint32_t) p->payload;
+ rd->bcnt = -p->tot_len;
+ rd->ones = 0xf;
+ rd->own = 1;
+
+ state->rd_head = (state->rd_head + 1) % state->rd_count;
+
+ LTRACE_EXIT;
+ return true;
+ } else {
+#if 0
+ LTRACEF("Nothing to do for RX: rd_head=%d.\n", state->rd_head);
+ for (int i=0; i < state->rd_count; i++)
+ printf("%d ", state->rd[i].own);
+ printf("\n");
+#endif
+ }
+
+ LTRACE_EXIT;
+ return false;
+}
+
+static status_t pcnet_set_state(struct device *dev, struct netstack_state *netstack_state)
+{
+ if (!dev)
+ return ERR_INVALID_ARGS;
+
+ if (!dev->state)
+ return ERR_NOT_CONFIGURED;
+
+ struct pcnet_state *state = dev->state;
+
+ state->netstack_state = netstack_state;
+
+ return NO_ERROR;
+}
+
+static ssize_t pcnet_get_hwaddr(struct device *dev, void *buf, size_t max_len)
+{
+ if (!dev || !buf)
+ return ERR_INVALID_ARGS;
+
+ if (!dev->state)
+ return ERR_NOT_CONFIGURED;
+
+ struct pcnet_state *state = dev->state;
+
+ memcpy(buf, state->padr, MIN(sizeof(state->padr), max_len));
+
+ return sizeof(state->padr);
+}
+
+static ssize_t pcnet_get_mtu(struct device *dev)
+{
+ if (!dev)
+ return ERR_INVALID_ARGS;
+
+ return 1500;
+}
+
+static status_t pcnet_output(struct device *dev, struct pbuf *p)
+{
+ LTRACE_ENTRY;
+
+ if (!dev || !p)
+ return ERR_INVALID_ARGS;
+
+ if (!dev->state)
+ return ERR_NOT_CONFIGURED;
+
+ status_t res = NO_ERROR;
+ struct pcnet_state *state = dev->state;
+
+ mutex_acquire(&state->tx_lock);
+
+ struct td_style3 *td = &state->td[state->td_head];
+
+ if (td->own) {
+ LTRACEF("TX descriptor ring full\n");
+ res = ERR_NOT_READY; // maybe this should be ERR_NOT_ENOUGH_BUFFER?
+ goto done;
+ }
+
+ pbuf_ref(p);
+ p = pbuf_coalesce(p, PBUF_RAW);
+
+#if LOCAL_TRACE
+ LTRACEF("Queuing packet: td_head=%d p=%p tot_len=%u\n", state->td_head, p, p->tot_len);
+ hexdump8(p->payload, p->tot_len);
+#endif
+
+ /* clear flags */
+ memset(td, 0, sizeof(*td));
+
+ td->tbadr = (uint32_t) p->payload;
+ td->bcnt = -p->tot_len;
+ td->stp = 1;
+ td->enp = 1;
+ td->add_no_fcs = 1;
+ td->ones = 0xf;
+
+ state->tx_buffers[state->td_head] = p;
+ state->tx_pending++;
+
+ state->td_head = (state->td_head + 1) % state->td_count;
+
+ td->own = 1;
+
+ /* trigger tx */
+ pcnet_write_csr(dev, 0, CSR0_TDMD);
+
+done:
+ mutex_release(&state->tx_lock);
+ LTRACE_EXIT;
+ return res;
+}
+
+static const struct platform_pcnet_config pcnet0_config = {
+ .vendor_id = 0x1022,
+ .device_id = 0x2000,
+ .index = 0,
+};
+
+DEVICE_INSTANCE(netif, pcnet0, &pcnet0_config);
+
+static void pcnet_init_hook(uint level)
+{
+ device_init(device_get_by_name(netif, pcnet0));
+ class_netif_add(device_get_by_name(netif, pcnet0));
+}
+
+LK_INIT_HOOK(pcnet, &pcnet_init_hook, LK_INIT_LEVEL_PLATFORM);
+
diff --git a/src/bsp/lk/dev/net/pcnet/rules.mk b/src/bsp/lk/dev/net/pcnet/rules.mk
new file mode 100644
index 0000000..979a611
--- /dev/null
+++ b/src/bsp/lk/dev/net/pcnet/rules.mk
@@ -0,0 +1,10 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/pcnet.c
+
+MODULE_DEPS := lib/lwip
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/net/smc91c96/include/dev/net/smc91c96.h b/src/bsp/lk/dev/net/smc91c96/include/dev/net/smc91c96.h
new file mode 100644
index 0000000..acd543e
--- /dev/null
+++ b/src/bsp/lk/dev/net/smc91c96/include/dev/net/smc91c96.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2008 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 _DEV_NET_SMC91C96_H
+#define _DEV_NET_SMC91C96_H
+
+void smc91c96_init(void);
+
+#endif
+
diff --git a/src/bsp/lk/dev/net/smc91c96/rules.mk b/src/bsp/lk/dev/net/smc91c96/rules.mk
new file mode 100644
index 0000000..4f237b5
--- /dev/null
+++ b/src/bsp/lk/dev/net/smc91c96/rules.mk
@@ -0,0 +1,8 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/smc91c96.c
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/net/smc91c96/smc91c96.c b/src/bsp/lk/dev/net/smc91c96/smc91c96.c
new file mode 100644
index 0000000..9c902e0
--- /dev/null
+++ b/src/bsp/lk/dev/net/smc91c96/smc91c96.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2008 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 <sys/types.h>
+#include <debug.h>
+#include <trace.h>
+#include <stdio.h>
+#include <dev/net/smc91c96.h>
+#include "smc91c96_p.h"
+
+#if !defined(SMC91C96_BASE_ADDR) || !defined(SMC91C96_IRQ)
+#error need to define SMC91C96_BASE_ADDR and SMC91C96_IRQ in project
+#endif
+
+static addr_t smc91c96_base = SMC91C96_BASE_ADDR;
+static uint8_t mac_addr[6];
+
+#define SMC_REG16(reg) ((volatile uint16_t *)(smc91c96_base + (reg)))
+#define SMC_REG8(reg) ((volatile uint8_t *)(smc91c96_base + (reg)))
+
+static inline void smc_bank(int bank)
+{
+ *SMC_REG16(SMC_BSR) = bank;
+}
+
+void smc91c96_init(void)
+{
+ int i;
+
+ TRACE;
+
+ // try to detect it
+ if ((*SMC_REG16(SMC_BSR) & 0xff00) != 0x3300) {
+ TRACEF("didn't see smc91c96 chip at 0x%x\n", (unsigned int)smc91c96_base);
+ }
+
+ // read revision
+ smc_bank(3);
+ TRACEF("detected, revision 0x%x\n", *SMC_REG16(SMC_REV));
+
+ // read in the mac address
+ smc_bank(1);
+ for (i=0; i < 6; i++) {
+ mac_addr[i] = *SMC_REG8(SMC_IAR0 + i);
+ }
+ TRACEF("mac address %02x:%02x:%02x:%02x:%02x:%02x\n",
+ mac_addr[0], mac_addr[1], mac_addr[2],
+ mac_addr[3], mac_addr[4], mac_addr[5]);
+
+ smc_bank(0);
+}
+
diff --git a/src/bsp/lk/dev/net/smc91c96/smc91c96_p.h b/src/bsp/lk/dev/net/smc91c96/smc91c96_p.h
new file mode 100644
index 0000000..9816d8f
--- /dev/null
+++ b/src/bsp/lk/dev/net/smc91c96/smc91c96_p.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2008 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 __SMC91C96_P_H
+#define __SMC91C96_P_H
+
+// LAN91C96 stuffs
+
+/* registers */
+
+#define SMC_BSR 14
+
+/* bank 0 */
+#define SMC_TCR 0
+#define SMC_EPHSR 2
+#define SMC_RCR 4
+#define SMC_ECR 6
+#define SMC_MIR 8
+#define SMC_MCR 10
+
+/* bank 1 */
+#define SMC_CR 0
+#define SMC_BAR 2
+#define SMC_IAR0 4
+#define SMC_IAR1 5
+#define SMC_IAR2 6
+#define SMC_IAR3 7
+#define SMC_IAR4 8
+#define SMC_IAR5 9
+#define SMC_GPR 10
+#define SMC_CTR 12
+
+/* bank 2 */
+#define SMC_MMUCR 0
+#define SMC_AUTOTX 1
+#define SMC_PNR 2
+#define SMC_ARR 3
+#define SMC_FIFO 4
+#define SMC_PTR 6
+#define SMC_DATA0 8
+#define SMC_DATA1 10
+#define SMC_IST 12
+#define SMC_ACK 12
+#define SMC_MSK 13
+
+/* bank 3 */
+#define SMC_MT0 0
+#define SMC_MT1 1
+#define SMC_MT2 2
+#define SMC_MT3 3
+#define SMC_MT4 4
+#define SMC_MT5 5
+#define SMC_MT6 6
+#define SMC_MT7 7
+#define SMC_MGMT 8
+#define SMC_REV 10
+#define SMC_ERCV 12
+
+
+#endif
+
diff --git a/src/bsp/lk/dev/pmic/twl4030/include/dev/twl4030.h b/src/bsp/lk/dev/pmic/twl4030/include/dev/twl4030.h
new file mode 100644
index 0000000..f863221
--- /dev/null
+++ b/src/bsp/lk/dev/pmic/twl4030/include/dev/twl4030.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2008 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 __DEV_TWL4030_H
+#define __DEV_TWL4030_H
+
+#include <sys/types.h>
+
+void twl4030_init(void);
+
+/* USB parts of the pmic */
+int twl4030_usb_reset(void);
+int twl4030_set_usb_pullup(bool pullup);
+int twl4030_init_hs(void);
+
+#endif
+
diff --git a/src/bsp/lk/dev/pmic/twl4030/rules.mk b/src/bsp/lk/dev/pmic/twl4030/rules.mk
new file mode 100644
index 0000000..a5646b2
--- /dev/null
+++ b/src/bsp/lk/dev/pmic/twl4030/rules.mk
@@ -0,0 +1,8 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/twl4030.c
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/pmic/twl4030/twl4030.c b/src/bsp/lk/dev/pmic/twl4030/twl4030.c
new file mode 100644
index 0000000..5cc2625
--- /dev/null
+++ b/src/bsp/lk/dev/pmic/twl4030/twl4030.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 2008 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 <trace.h>
+#include <sys/types.h>
+#include <dev/i2c.h>
+#include <dev/twl4030.h>
+#include "twl4030_hw.h"
+
+// XXX move to target specific setup
+#define TWL_I2C_BUS 0
+
+void twl4030_init(void)
+{
+}
+
+static int twl4030_usb_write(uint8_t address, uint8_t data)
+{
+ return i2c_write_reg(TWL_I2C_BUS, TWL_USB_ADDR, address, data);
+}
+
+static int twl4030_usb_read(uint8_t address)
+{
+ uint8_t data;
+
+ int err = i2c_read_reg(TWL_I2C_BUS, TWL_USB_ADDR, address, &data);
+ if (err < 0)
+ return err;
+
+ return data;
+}
+
+static int twl4030_usb_set_bits(uint8_t reg, uint8_t bits)
+{
+ return twl4030_usb_write(reg + 1, bits);
+}
+
+static int twl4030_usb_clear_bits(uint8_t reg, uint8_t bits)
+{
+ return twl4030_usb_write(reg + 2, bits);
+}
+
+static void twl4030_i2c_access(bool on)
+{
+ int val;
+
+ if ((val = twl4030_usb_read(PHY_CLK_CTRL)) >= 0) {
+ if (on) {
+ /* enable DPLL to access PHY registers over I2C */
+ val |= REQ_PHY_DPLL_CLK;
+ twl4030_usb_write(PHY_CLK_CTRL, (uint8_t)val);
+
+ while (!(twl4030_usb_read(PHY_CLK_CTRL_STS) & PHY_DPLL_CLK)) {
+ spin(10);
+ }
+ if (!(twl4030_usb_read(PHY_CLK_CTRL_STS) & PHY_DPLL_CLK))
+ printf("Timeout setting T2 HSUSB " "PHY DPLL clock\n");
+ } else {
+ /* let ULPI control the DPLL clock */
+ val &= ~REQ_PHY_DPLL_CLK;
+ twl4030_usb_write(PHY_CLK_CTRL, (uint8_t)val);
+ }
+ }
+ return;
+}
+
+int twl4030_usb_reset(void)
+{
+ TRACE_ENTRY;
+#if 0
+ twl4030_usb_clear_bits(OTG_CTRL, DMPULLDOWN | DPPULLDOWN);
+ twl4030_usb_clear_bits(USB_INT_EN_RISE, ~0);
+ twl4030_usb_clear_bits(USB_INT_EN_FALL, ~0);
+ twl4030_usb_clear_bits(MCPC_IO_CTRL, ~TXDTYP);
+ twl4030_usb_set_bits(MCPC_IO_CTRL, TXDTYP);
+ twl4030_usb_clear_bits(OTHER_FUNC_CTRL, (BDIS_ACON_EN | FIVEWIRE_MODE));
+ twl4030_usb_clear_bits(OTHER_IFC_CTRL, ~0);
+ twl4030_usb_clear_bits(OTHER_INT_EN_RISE, ~0);
+ twl4030_usb_clear_bits(OTHER_INT_EN_FALL, ~0);
+ twl4030_usb_clear_bits(OTHER_IFC_CTRL2, ~0);
+ twl4030_usb_clear_bits(REG_CTRL_EN, ULPI_I2C_CONFLICT_INTEN);
+ twl4030_usb_clear_bits(OTHER_FUNC_CTRL2, VBAT_TIMER_EN);
+#endif
+
+ /* Enable writing to power configuration registers */
+ i2c_write_reg(TWL_I2C_BUS, TWL_PM_RECEIVER_ADDR, PROTECT_KEY, 0xC0);
+ i2c_write_reg(TWL_I2C_BUS, TWL_PM_RECEIVER_ADDR, PROTECT_KEY, 0x0C);
+
+ /* put VUSB3V1 LDO in active state */
+ i2c_write_reg(TWL_I2C_BUS, TWL_PM_RECEIVER_ADDR, VUSB_DEDICATED2, 0);
+
+ /* input to VUSB3V1 LDO is from VBAT, not VBUS */
+ i2c_write_reg(TWL_I2C_BUS, TWL_PM_RECEIVER_ADDR, VUSB_DEDICATED1, 0x14);
+
+ /* turn on 3.1V regulator */
+ i2c_write_reg(TWL_I2C_BUS, TWL_PM_RECEIVER_ADDR, VUSB3V1_DEV_GRP, 0x20);
+ i2c_write_reg(TWL_I2C_BUS, TWL_PM_RECEIVER_ADDR, VUSB3V1_TYPE, 0);
+
+ /* turn on 1.5V regulator */
+ i2c_write_reg(TWL_I2C_BUS, TWL_PM_RECEIVER_ADDR, VUSB1V5_DEV_GRP, 0x20);
+ i2c_write_reg(TWL_I2C_BUS, TWL_PM_RECEIVER_ADDR, VUSB1V5_TYPE, 0);
+
+ /* turn on 1.8V regulator */
+ i2c_write_reg(TWL_I2C_BUS, TWL_PM_RECEIVER_ADDR, VUSB1V8_DEV_GRP, 0x20);
+ i2c_write_reg(TWL_I2C_BUS, TWL_PM_RECEIVER_ADDR, VUSB1V8_TYPE, 0);
+
+ /* disable access to power configuration registers */
+ i2c_write_reg(TWL_I2C_BUS, TWL_PM_RECEIVER_ADDR, PROTECT_KEY, 0);
+
+ /* turn on the phy */
+ uint8_t pwr = twl4030_usb_read(PHY_PWR_CTRL);
+ pwr &= ~PHYPWD;
+ twl4030_usb_write(PHY_PWR_CTRL, pwr);
+ twl4030_usb_write(PHY_CLK_CTRL,
+ twl4030_usb_read(PHY_CLK_CTRL) |
+ (CLOCKGATING_EN | CLK32K_EN));
+
+ /* set DPLL i2c access mode */
+ twl4030_i2c_access(true);
+ /* set ulpi mode */
+ twl4030_usb_clear_bits(IFC_CTRL, CARKITMODE);
+ twl4030_usb_set_bits(POWER_CTRL, OTG_ENAB);
+ twl4030_usb_write(FUNC_CTRL, XCVRSELECT_HS); // set high speed mode
+// twl4030_usb_write(FUNC_CTRL, XCVRSELECT_FS); // set full speed mode
+ twl4030_i2c_access(false);
+
+ return 0;
+}
+
+int twl4030_init_hs(void)
+{
+ return 0;
+}
+
+int twl4030_set_usb_pullup(bool pullup)
+{
+ TRACE_ENTRY;
+
+ if (pullup) {
+ twl4030_usb_clear_bits(OTG_CTRL, DPPULLDOWN);
+ twl4030_usb_set_bits(FUNC_CTRL, TERMSELECT);
+ } else {
+ twl4030_usb_clear_bits(FUNC_CTRL, TERMSELECT);
+ twl4030_usb_set_bits(OTG_CTRL, DPPULLDOWN);
+ }
+
+ return 0;
+}
+
+
diff --git a/src/bsp/lk/dev/pmic/twl4030/twl4030_hw.h b/src/bsp/lk/dev/pmic/twl4030/twl4030_hw.h
new file mode 100644
index 0000000..1b37f93
--- /dev/null
+++ b/src/bsp/lk/dev/pmic/twl4030/twl4030_hw.h
@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2008 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 __TWL4030_HW_H
+#define __TWL4030_HW_H
+
+/* TWL i2c addresses */
+#define TWL_I2C_ADDR0 0x48
+#define TWL_I2C_ADDR1 0x49
+#define TWL_I2C_ADDR2 0x4a
+#define TWL_I2C_ADDR3 0x4b
+
+#define TWL_USB_ADDR TWL_I2C_ADDR0
+#define TWL_INTBR_ADDR TWL_I2C_ADDR1
+#define TWL_PM_RECEIVER_ADDR TWL_I2C_ADDR3
+
+/* TWL registers */
+#define PROTECT_KEY 0x44
+#define VAUX1_DEV_GRP 0x72
+#define VAUX1_TYPE 0x73
+#define VAUX1_REMAP 0x74
+#define VAUX1_DEDICATED 0x75
+#define VAUX2_DEV_GRP 0x76
+#define VAUX2_TYPE 0x77
+#define VAUX2_REMAP 0x78
+#define VAUX2_DEDICATED 0x79
+#define VAUX3_DEV_GRP 0x7a
+#define VAUX3_TYPE 0x7b
+#define VAUX3_REMAP 0x7c
+#define VAUX3_DEDICATED 0x7d
+#define VAUX4_DEV_GRP 0x7e
+#define VAUX4_TYPE 0x7f
+#define VAUX4_REMAP 0x80
+#define VAUX4_DEDICATED 0x81
+#define VMMC1_DEV_GRP 0x82
+#define VMMC1_TYPE 0x83
+#define VMMC1_REMAP 0x84
+#define VMMC1_DEDICATED 0x85
+#define VMMC2_DEV_GRP 0x86
+#define VMMC2_TYPE 0x87
+#define VMMC2_REMAP 0x88
+#define VMMC2_DEDICATED 0x89
+#define VPLL1_DEV_GRP 0x8a
+#define VPLL1_TYPE 0x8b
+#define VPLL1_REMAP 0x8c
+#define VPLL1_DEDICATED 0x8d
+#define VPLL2_DEV_GRP 0x8e
+#define VPLL2_TYPE 0x8f
+#define VPLL2_REMAP 0x90
+#define VPLL2_DEDICATED 0x91
+#define VDAC_DEV_GRP 0x96
+#define VDAC_DEDICATED 0x99
+#define VDD2_DEV_GRP 0xBE
+#define VDD2_TYPE 0xBF
+#define VDD2_REMAP 0xC0
+#define VDD2_CFG 0xC1
+#define VSIM_DEV_GRP 0x92
+#define VSIM_TYPE 0x93
+#define VSIM_REMAP 0x94
+#define VSIM_DEDICATED 0x95
+#define PMBR1 0x92
+
+#define SECONDS_REG 0x1C
+#define MINUTES_REG 0x1D
+#define ALARM_SECONDS_REG 0x23
+#define ALARM_MINUTES_REG 0x24
+#define ALARM_HOURS_REG 0x25
+
+#define RTC_STATUS_REG 0x2A
+#define RTC_INTERRUPTS_REG 0x2B
+
+#define PWR_ISR1 0x2E
+#define PWR_IMR1 0x2F
+#define PWR_ISR2 0x30
+#define PWR_IMR2 0x31
+#define PWR_EDR1 0x33
+
+#define CFG_PWRANA2 0x3F
+#define RTC_INTERRUPTS_REG 0x2B
+#define STS_HW_CONDITIONS 0x45
+
+#define P1_SW_EVENTS 0x46
+#define P2_SW_EVENTS 0x47
+#define P3_SW_EVENTS 0x48
+
+#define VDD1_TRIM1 0x62
+#define VDD1_TRIM2 0x63
+#define VDD1_VFLOOR 0xBB
+#define VDD1_VROOF 0xBC
+#define PB_CFG 0x4A
+#define PB_WORD_MSB 0x4B
+#define PB_WORD_LSB 0x4C
+
+#define VSIM_REMAP 0x94
+#define VDAC_REMAP 0x98
+#define VINTANA1_DEV_GRP 0x9A
+#define VINTANA1_REMAP 0x9C
+#define VINTANA2_REMAP 0xA0
+#define VINTANA2_DEV_GRP 0x9E
+#define VINTDIG_DEV_GRP 0xA2
+#define VINTDIG_REMAP 0xA4
+#define VIO_DEV_GRP 0xA6
+#define VIO_REMAP 0xA8
+#define VDD1_REMAP 0xB2
+#define VDD2_REMAP 0xC0
+#define REGEN_REMAP 0xDC
+#define NRESPWRON_REMAP 0xDF
+#define CLKEN_REMAP 0xE2
+#define SYSEN_REMAP 0xE5
+#define HFCLKOUT_REMAP 0xE8
+#define HFCLKOUT_DEV_GRP 0xE6
+#define T32KCLKOUT_REMAP 0xEB
+#define TRITON_RESET_REMAP 0xEE
+#define MAINREF_REMAP 0xF1
+#define VIBRA_CTL 0x45
+
+
+#define VUSB1V5_DEV_GRP 0xCC
+#define VUSB1V5_TYPE 0xCD
+#define VUSB1V5_REMAP 0xCE
+#define VUSB1V8_DEV_GRP 0xCF
+#define VUSB1V8_TYPE 0xD0
+#define VUSB1V8_REMAP 0xD1
+#define VUSB3V1_DEV_GRP 0xD2
+#define VUSB3V1_TYPE 0xD3
+#define VUSB3V1_REMAP 0xD4
+#define VUSBCP_DEV_GRP 0xD5
+#define VUSBCP_TYPE 0xD6
+#define VUSBCP_REMAP 0xD7
+#define VUSB_DEDICATED1 0xD8
+#define VUSB_DEDICATED2 0xD9
+
+/* USB registers */
+#define VENDOR_ID_LO 0x0
+#define VENDOR_ID_HI 0x1
+#define PRODUCT_ID_LO 0x2
+#define PRODUCT_ID_HI 0x3
+#define FUNC_CTRL 0x4
+#define FUNC_CTRL_SET 0x5
+#define FUNC_CTRL_CLR 0x6
+# define SUSPENDM (1 << 6)
+# define RESET (1 << 5)
+# define OPMODE_MASK (3 << 3) /* bits 3 and 4 */
+# define OPMODE_NORMAL (0 << 3)
+# define OPMODE_NONDRIVING (1 << 3)
+# define OPMODE_DISABLE_BIT_NRZI (2 << 3)
+# define TERMSELECT (1 << 2)
+# define XCVRSELECT_MASK (3 << 0) /* bits 0 and 1 */
+# define XCVRSELECT_HS (0 << 0)
+# define XCVRSELECT_FS (1 << 0)
+# define XCVRSELECT_LS (2 << 0)
+# define XCVRSELECT_FS4LS (3 << 0)
+#define IFC_CTRL 0x7
+#define IFC_CTRL_SET 0x8
+#define IFC_CTRL_CLR 0x9
+# define INTERFACE_PROTECT_DISABLE (1 << 7)
+# define AUTORESUME (1 << 4)
+# define CLOCKSUSPENDM (1 << 3)
+# define CARKITMODE (1 << 2)
+# define FSLSSERIALMODE_3PIN (1 << 1)
+#define OTG_CTRL 0xa
+#define OTG_CTRL_SET 0xb
+#define OTG_CTRL_CLR 0xc
+#define DRVVBUS (1 << 5)
+#define CHRGVBUS (1 << 4)
+#define DISCHRGVBUS (1 << 3)
+#define DMPULLDOWN (1 << 2)
+#define DPPULLDOWN (1 << 1)
+#define IDPULLUP (1 << 0)
+#define USB_INT_EN_RISE 0xd
+#define USB_INT_EN_RISE_SET 0xe
+#define USB_INT_EN_RISE_CLR 0xf
+#define USB_INT_EN_FALL 0x10
+#define USB_INT_EN_FALL_SET 0x11
+#define USB_INT_EN_FALL_CLR 0x12
+# define HOSTDISCONNECT (1 << 0)
+#define USB_INT_STS 0x13
+#define USB_INT_LATCH 0x14
+#define USB_DEBUG 0x15
+#define SCRATCH_REG 0x16
+#define SCRATCH_REG_SET 0x17
+#define SCRATCH_REG_CLR 0x18
+#define CARKIT_CTRL 0x19
+#define CARKIT_CTRL_SET 0x1a
+#define CARKIT_CTRL_CLR 0x1b
+#define MICEN (1 << 6)
+#define SPKRIGHTEN (1 << 5)
+#define SPKLEFTEN (1 << 4)
+#define RXDEN (1 << 3)
+#define TXDEN (1 << 2)
+#define IDGNDDRV (1 << 1)
+#define CARKITPWR (1 << 0)
+#define CARKIT_INT_DELAY 0x1c
+#define CARKIT_INT_EN 0x1d
+#define CARKIT_INT_EN_SET 0x1e
+#define CARKIT_INT_EN_CLR 0x1f
+#define CARKIT_INT_STS 0x20
+#define CARKIT_INT_LATCH 0x21
+#define CARKIT_PLS_CTRL 0x22
+#define CARKIT_PLS_CTRL_SET 0x23
+#define CARKIT_PLS_CTRL_CLR 0x24
+# define SPKRRIGHT_BIASEN (1 << 3)
+# define SPKRLEFT_BIASEN (1 << 2)
+# define RXPLSEN (1 << 1)
+# define TXPLSEN (1 << 0)
+#define TRANS_POS_WIDTH 0x25
+#define TRANS_NEG_WIDTH 0x26
+#define RCV_PLTY_RECOVERY 0x27
+#define MCPC_CTRL 0x30
+#define MCPC_CTRL_SET 0x31
+#define MCPC_CTRL_CLR 0x32
+#define RTSOL (1 << 7)
+#define EXTSWR (1 << 6)
+#define EXTSWC (1 << 5)
+#define VOICESW (1 << 4)
+#define OUT64K (1 << 3)
+#define RTSCTSSW (1 << 2)
+#define HS_UART (1 << 0)
+#define MCPC_IO_CTRL 0x033
+#define MCPC_IO_CTRL_SET 0x034
+#define MCPC_IO_CTRL_CLR 0x035
+#define MICBIASEN (1<< 5)
+#define CTS_NPU (1 << 4)
+#define RXD_PU (1 << 3)
+#define TXDTYP (1 << 2)
+#define CTSTYP (1 << 1)
+#define RTSTYP (1 << 0)
+#define MCPC_CTRL2 0x036
+#define MCPC_CTRL2_SET 0x037
+#define MCPC_CTRL2_CLR 0x038
+# define MCPC_CK_EN (1 << 0)
+#define OTHER_FUNC_CTRL 0x080
+#define OTHER_FUNC_CTRL_SET 0x081
+#define OTHER_FUNC_CTRL_CLR 0x082
+#define BDIS_ACON_EN (1<< 4)
+#define FIVEWIRE_MODE (1 << 2)
+#define OTHER_IFC_CTRL 0x083
+#define OTHER_IFC_CTRL_SET 0x084
+#define OTHER_IFC_CTRL_CLR 0x085
+# define OE_INT_EN (1 << 6)
+# define CEA2011_MODE (1 << 5)
+# define FSLSSERIALMODE_4PIN (1 << 4)
+# define HIZ_ULPI_60MHZ_OUT (1 << 3)
+# define HIZ_ULPI (1 << 2)
+# define ALT_INT_REROUTE (1 << 0)
+#define OTHER_INT_EN_RISE 0x086
+#define OTHER_INT_EN_RISE_SET 0x087
+#define OTHER_INT_EN_RISE_CLR 0x088
+#define OTHER_INT_EN_FALL 0x089
+#define OTHER_INT_EN_FALL_SET 0x08A
+#define OTHER_INT_EN_FALL_CLR 0x08B
+#define OTHER_INT_STS 0x8C
+#define OTHER_INT_LATCH 0x8D
+#define ID_INT_EN_RISE 0x08E
+#define ID_INT_EN_RISE_SET 0x08F
+#define ID_INT_EN_RISE_CLR 0x090
+#define ID_INT_EN_FALL 0x091
+#define ID_INT_EN_FALL_SET 0x092
+#define ID_INT_EN_FALL_CLR 0x093
+#define ID_INT_STS 0x094
+#define ID_INT_LATCH 0x95
+#define ID_STATUS 0x96
+#define CARKIT_SM_1_INT_EN 0x097
+#define CARKIT_SM_1_INT_EN_SET 0x098
+#define CARKIT_SM_1_INT_EN_CLR 0x099
+#define CARKIT_SM_1_INT_STS 0x09A
+#define CARKIT_SM_1_INT_LATCH 0x9B
+#define CARKIT_SM_2_INT_EN 0x09C
+#define CARKIT_SM_2_INT_EN_SET 0x09D
+#define CARKIT_SM_2_INT_EN_CLR 0x09E
+#define CARKIT_SM_2_INT_STS 0x09F
+#define CARKIT_SM_2_INT_LATCH 0xA0
+#define CARKIT_SM_CTRL 0x0A1
+#define CARKIT_SM_CTRL_SET 0x0A2
+#define CARKIT_SM_CTRL_CLR 0x0A3
+#define CARKIT_SM_CMD 0x0A4
+#define CARKIT_SM_CMD_SET 0x0A5
+#define CARKIT_SM_CMD_CLR 0x0A6
+#define CARKIT_SM_CMD_STS 0xA7
+#define CARKIT_SM_STATUS 0xA8
+#define CARKIT_SM_NEXT_STATUS 0xA9
+#define CARKIT_SM_ERR_STATUS 0xAA
+#define CARKIT_SM_CTRL_STATE 0xAB
+#define POWER_CTRL 0xAC
+#define POWER_CTRL_SET 0xAD
+#define POWER_CTRL_CLR 0xAE
+# define OTG_ENAB (1 << 5)
+#define OTHER_IFC_CTRL2 0xAF
+#define OTHER_IFC_CTRL2_SET 0xB0
+#define OTHER_IFC_CTRL2_CLR 0xB1
+# define ULPI_TXEN_POL (1 << 3)
+# define ULPI_4PIN_2430 (1 << 2)
+#define REG_CTRL_EN 0xB2
+#define REG_CTRL_EN_SET 0xB3
+#define REG_CTRL_EN_CLR 0xB4
+#define REG_CTRL_ERROR 0xB5
+#define ULPI_I2C_CONFLICT_INTEN (1 << 0)
+#define OTHER_FUNC_CTRL2 0xB8
+#define OTHER_FUNC_CTRL2_SET 0xB9
+#define OTHER_FUNC_CTRL2_CLR 0xBA
+#define VBAT_TIMER_EN (1 << 0)
+#define CARKIT_ANA_CTRL 0xBB
+#define CARKIT_ANA_CTRL_SET 0xBC
+#define CARKIT_ANA_CTRL_CLR 0xBD
+#define VBUS_DEBOUNCE 0xC0
+#define ID_DEBOUNCE 0xC1
+#define TPH_DP_CON_MIN 0xC2
+#define TPH_DP_CON_MAX 0xC3
+#define TCR_DP_CON_MIN 0xC4
+#define TCR_DP_CON_MAX 0xC5
+#define TPH_DP_PD_SHORT 0xC6
+#define TPH_CMD_DLY 0xC7
+#define TPH_DET_RST 0xC8
+#define TPH_AUD_BIAS 0xC9
+#define TCR_UART_DET_MIN 0xCA
+#define TCR_UART_DET_MAX 0xCB
+#define TPH_ID_INT_PW 0xCD
+#define TACC_ID_INT_WAIT 0xCE
+#define TACC_ID_INT_PW 0xCF
+#define TPH_CMD_WAIT 0xD0
+#define TPH_ACK_WAIT 0xD1
+#define TPH_DP_DISC_DET 0xD2
+#define VBAT_TIMER 0xD3
+#define CARKIT_4W_DEBUG 0xE0
+#define CARKIT_5W_DEBUG 0xE1
+#define CARKIT_5W_DEBUG 0xE1
+#define TEST_CTRL_CLR 0xEB
+#define TEST_CARKIT_SET 0xEC
+#define TEST_CARKIT_CLR 0xED
+#define TEST_POWER_SET 0xEE
+#define TEST_POWER_CLR 0xEF
+#define TEST_ULPI 0xF0
+#define TXVR_EN_TEST_SET 0xF2
+#define TXVR_EN_TEST_CLR 0xF3
+#define VBUS_EN_TEST 0xF4
+#define ID_EN_TEST 0xF5
+#define PSM_EN_TEST_SET 0xF6
+#define PSM_EN_TEST_CLR 0xF7
+#define PHY_TRIM_CTRL 0xFC
+#define PHY_PWR_CTRL 0xFD
+# define PHYPWD (1 << 0)
+#define PHY_CLK_CTRL 0xFE
+# define CLOCKGATING_EN (1 << 2)
+# define CLK32K_EN (1 << 1)
+# define REQ_PHY_DPLL_CLK (1 << 0)
+#define PHY_CLK_CTRL_STS 0xFF
+# define PHY_DPLL_CLK (1 << 0)
+
+#endif
+
diff --git a/src/bsp/lk/dev/rules.mk b/src/bsp/lk/dev/rules.mk
new file mode 100644
index 0000000..e05ff66
--- /dev/null
+++ b/src/bsp/lk/dev/rules.mk
@@ -0,0 +1,17 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/dev.c \
+ $(LOCAL_DIR)/driver.c \
+ $(LOCAL_DIR)/class/block_api.c \
+ $(LOCAL_DIR)/class/i2c_api.c \
+ $(LOCAL_DIR)/class/spi_api.c \
+ $(LOCAL_DIR)/class/uart_api.c \
+ $(LOCAL_DIR)/class/fb_api.c \
+ $(LOCAL_DIR)/class/netif_api.c \
+
+EXTRA_LINKER_SCRIPTS += $(LOCAL_DIR)/devices.ld $(LOCAL_DIR)/drivers.ld
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/timer/arm_cortex_a9/arm_cortex_a9_timer.c b/src/bsp/lk/dev/timer/arm_cortex_a9/arm_cortex_a9_timer.c
new file mode 100644
index 0000000..ed06d54
--- /dev/null
+++ b/src/bsp/lk/dev/timer/arm_cortex_a9/arm_cortex_a9_timer.c
@@ -0,0 +1,227 @@
+/*
+ * 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/timer/arm_cortex_a9.h>
+
+#include <debug.h>
+#include <sys/types.h>
+#include <err.h>
+#include <stdio.h>
+#include <assert.h>
+#include <trace.h>
+#include <lib/fixed_point.h>
+#include <kernel/thread.h>
+#include <kernel/spinlock.h>
+#include <platform.h>
+#include <platform/interrupts.h>
+#include <platform/timer.h>
+#include <lk/init.h>
+
+/* driver for cortex-a9's private timer */
+#define LOCAL_TRACE 0
+
+/* interrupts for cortex-a9 timer and watchdog */
+#define CPU_GLOB_TIMER_INT 27
+#define CPU_PRIV_TIMER_INT 29
+#define CPU_WATCHDOG_INT 30
+
+/* offsets into the per cpu private scu base */
+#define GLOBAL_TIMER_OFFSET (0x0200)
+#define PRIV_TIMER_OFFSET (0x0600)
+
+#define TIMREG(reg) (*REG32(scu_control_base + PRIV_TIMER_OFFSET + (reg)))
+
+#define TIMER_LOAD (0x00)
+#define TIMER_COUNTER (0x04)
+#define TIMER_CONTROL (0x08)
+#define TIMER_ISR (0x0c)
+#define WDOG_LOAD (0x20)
+#define WDOG_COUNTER (0x24)
+#define WDOG_CONTROL (0x28)
+#define WDOG_ISR (0x2c)
+
+#define GTIMREG(reg) (*REG32(scu_control_base + GLOBAL_TIMER_OFFSET + (reg)))
+
+#define GTIMER_COUNT_LO (0x00)
+#define GTIMER_COUNT_HI (0x04)
+#define GTIMER_CONTROL (0x08)
+#define GTIMER_ISR (0x0c)
+#define GTIMER_COMPARE_LO (0x10)
+#define GTIMER_COMPARE_HI (0x14)
+#define GTIMER_INCREMENT (0x18)
+
+static platform_timer_callback t_callback;
+static addr_t scu_control_base;
+static spin_lock_t lock = SPIN_LOCK_INITIAL_VALUE;
+
+static lk_time_t periodic_interval;
+static lk_time_t oneshot_interval;
+static uint32_t timer_freq;
+static struct fp_32_64 timer_freq_msec_conversion;
+static struct fp_32_64 timer_freq_usec_conversion_inverse;
+static struct fp_32_64 timer_freq_msec_conversion_inverse;
+
+static void arm_cortex_a9_timer_init_percpu(uint level);
+
+uint64_t get_global_val(void)
+{
+ uint32_t lo, hi;
+
+retry:
+ hi = GTIMREG(GTIMER_COUNT_HI);
+ lo = GTIMREG(GTIMER_COUNT_LO);
+ if (GTIMREG(GTIMER_COUNT_HI) != hi)
+ goto retry;
+
+ return ((uint64_t)hi << 32 | lo);
+}
+
+lk_bigtime_t current_time_hires(void)
+{
+ lk_bigtime_t time;
+
+ time = u64_mul_u64_fp32_64(get_global_val(), timer_freq_usec_conversion_inverse);
+
+ return time;
+}
+
+lk_time_t current_time(void)
+{
+ lk_time_t time;
+
+ time = u32_mul_u64_fp32_64(get_global_val(), timer_freq_msec_conversion_inverse);
+
+ return time;
+}
+
+status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval)
+{
+ LTRACEF("callback %p, arg %p, interval %u\n", callback, arg, interval);
+
+ uint64_t ticks = u64_mul_u64_fp32_64(interval, timer_freq_msec_conversion);
+ if (unlikely(ticks == 0))
+ ticks = 1;
+ if (unlikely(ticks > 0xffffffff))
+ ticks = 0xffffffff;
+
+ spin_lock_saved_state_t state;
+ spin_lock_irqsave(&lock, state);
+
+ t_callback = callback;
+
+ periodic_interval = interval;
+
+ // disable timer
+ TIMREG(TIMER_CONTROL) = 0;
+
+ TIMREG(TIMER_LOAD) = ticks;
+ TIMREG(TIMER_CONTROL) = (1<<2) | (1<<1) | (1<<0); // irq enable, autoreload, enable
+
+ spin_unlock_irqrestore(&lock, state);
+
+ return NO_ERROR;
+}
+
+status_t platform_set_oneshot_timer (platform_timer_callback callback, void *arg, lk_time_t interval)
+{
+ LTRACEF("callback %p, arg %p, timeout %u\n", callback, arg, interval);
+
+ uint64_t ticks = u64_mul_u64_fp32_64(interval, timer_freq_msec_conversion);
+ if (unlikely(ticks == 0))
+ ticks = 1;
+ if (unlikely(ticks > 0xffffffff))
+ ticks = 0xffffffff;
+
+ spin_lock_saved_state_t state;
+ spin_lock_irqsave(&lock, state);
+
+ t_callback = callback;
+ oneshot_interval = interval;
+
+ // disable timer
+ TIMREG(TIMER_CONTROL) = 0;
+
+ TIMREG(TIMER_LOAD) = ticks;
+ TIMREG(TIMER_CONTROL) = (1<<2) | (1<<0) | (1<<0); // irq enable, oneshot, enable
+
+ spin_unlock_irqrestore(&lock, state);
+
+ return NO_ERROR;
+}
+
+void platform_stop_timer(void)
+{
+ LTRACE;
+
+ TIMREG(TIMER_CONTROL) = 0;
+}
+
+static enum handler_return platform_tick(void *arg)
+{
+ LTRACE;
+
+ TIMREG(TIMER_ISR) = 1; // ack the irq
+
+ if (t_callback) {
+ return t_callback(arg, current_time());
+ } else {
+ return INT_NO_RESCHEDULE;
+ }
+}
+
+void arm_cortex_a9_timer_init(addr_t _scu_control_base, uint32_t freq)
+{
+ scu_control_base = _scu_control_base;
+
+ arm_cortex_a9_timer_init_percpu(0);
+
+ /* save the timer frequency for later calculations */
+ timer_freq = freq;
+
+ /* precompute the conversion factor for global time to real time */
+ fp_32_64_div_32_32(&timer_freq_msec_conversion, timer_freq, 1000);
+ fp_32_64_div_32_32(&timer_freq_usec_conversion_inverse, 1000000, timer_freq);
+ fp_32_64_div_32_32(&timer_freq_msec_conversion_inverse, 1000, timer_freq);
+}
+
+static void arm_cortex_a9_timer_init_percpu(uint level)
+{
+ /* disable timer */
+ TIMREG(TIMER_CONTROL) = 0;
+
+ /* kill the watchdog */
+ TIMREG(WDOG_CONTROL) = 0;
+
+ /* ack any irqs that may be pending */
+ TIMREG(TIMER_ISR) = 1;
+
+ /* register the platform tick on each cpu */
+ register_int_handler(CPU_PRIV_TIMER_INT, &platform_tick, NULL);
+ unmask_interrupt(CPU_PRIV_TIMER_INT);
+}
+
+/* secondary cpu initialize the timer just before the kernel starts with interrupts enabled */
+LK_INIT_HOOK_FLAGS(arm_cortex_a9_timer_init_percpu,
+ arm_cortex_a9_timer_init_percpu,
+ LK_INIT_LEVEL_THREADING - 1, LK_INIT_FLAG_SECONDARY_CPUS);
+
+/* vim: set ts=4 sw=4 expandtab: */
diff --git a/src/bsp/lk/dev/timer/arm_cortex_a9/include/dev/timer/arm_cortex_a9.h b/src/bsp/lk/dev/timer/arm_cortex_a9/include/dev/timer/arm_cortex_a9.h
new file mode 100644
index 0000000..053e811
--- /dev/null
+++ b/src/bsp/lk/dev/timer/arm_cortex_a9/include/dev/timer/arm_cortex_a9.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>
+
+void arm_cortex_a9_timer_init(addr_t scu_control_base, uint32_t freq);
diff --git a/src/bsp/lk/dev/timer/arm_cortex_a9/rules.mk b/src/bsp/lk/dev/timer/arm_cortex_a9/rules.mk
new file mode 100644
index 0000000..1345ddf
--- /dev/null
+++ b/src/bsp/lk/dev/timer/arm_cortex_a9/rules.mk
@@ -0,0 +1,14 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+GLOBAL_DEFINES += \
+ PLATFORM_HAS_DYNAMIC_TIMER=1
+
+MODULE_DEPS += \
+ lib/fixed_point
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/arm_cortex_a9_timer.c
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/timer/arm_generic/arm_generic_timer.c b/src/bsp/lk/dev/timer/arm_generic/arm_generic_timer.c
new file mode 100644
index 0000000..c7b329e
--- /dev/null
+++ b/src/bsp/lk/dev/timer/arm_generic/arm_generic_timer.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 2013, Google Inc. All rights reserved.
+ *
+ * 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 <arch/ops.h>
+#include <assert.h>
+#include <lk/init.h>
+#include <platform.h>
+#include <platform/interrupts.h>
+#include <platform/timer.h>
+#include <trace.h>
+
+#define LOCAL_TRACE 0
+
+#include <lib/fixed_point.h>
+
+#if ARCH_ARM64
+
+/* CNTFRQ AArch64 register */
+#define TIMER_REG_CNTFRQ cntfrq_el0
+
+/* CNTP AArch64 registers */
+#define TIMER_REG_CNTP_CTL cntp_ctl_el0
+#define TIMER_REG_CNTP_CVAL cntp_cval_el0
+#define TIMER_REG_CNTP_TVAL cntp_tval_el0
+#define TIMER_REG_CNTPCT cntpct_el0
+
+/* CNTPS AArch64 registers */
+#define TIMER_REG_CNTPS_CTL cntps_ctl_el1
+#define TIMER_REG_CNTPS_CVAL cntps_cval_el1
+#define TIMER_REG_CNTPS_TVAL cntps_tval_el1
+#define TIMER_REG_CNTPSCT cntpct_el0
+
+/* CNTV AArch64 registers */
+#define TIMER_REG_CNTV_CTL cntv_ctl_el0
+#define TIMER_REG_CNTV_CVAL cntv_cval_el0
+#define TIMER_REG_CNTV_TVAL cntv_tval_el0
+#define TIMER_REG_CNTVCT cntvct_el0
+
+#define READ_TIMER_REG32(reg) ARM64_READ_SYSREG(reg)
+#define READ_TIMER_REG64(reg) ARM64_READ_SYSREG(reg)
+#define WRITE_TIMER_REG32(reg, val) ARM64_WRITE_SYSREG(reg, (uint64_t)val)
+#define WRITE_TIMER_REG64(reg, val) ARM64_WRITE_SYSREG(reg, val)
+
+#else
+
+/* CNTFRQ AArch32 register */
+#define TIMER_REG_CNTFRQ "c0, 0"
+
+/* CNTP AArch32 registers */
+#define TIMER_REG_CNTP_CTL "c2, 1"
+#define TIMER_REG_CNTP_CVAL "2"
+#define TIMER_REG_CNTP_TVAL "c2, 0"
+#define TIMER_REG_CNTPCT "0"
+
+/* CNTPS AArch32 registers are banked and accessed though CNTP */
+#define CNTPS CNTP
+
+/* CNTV AArch32 registers */
+#define TIMER_REG_CNTV_CTL "c3, 1"
+#define TIMER_REG_CNTV_CVAL "3"
+#define TIMER_REG_CNTV_TVAL "c3, 0"
+#define TIMER_REG_CNTVCT "1"
+
+#define READ_TIMER_REG32(reg) \
+({ \
+ uint32_t _val; \
+ __asm__ volatile("mrc p15, 0, %0, c14, " reg : "=r" (_val)); \
+ _val; \
+})
+
+#define READ_TIMER_REG64(reg) \
+({ \
+ uint64_t _val; \
+ __asm__ volatile("mrrc p15, " reg ", %0, %H0, c14" : "=r" (_val)); \
+ _val; \
+})
+
+#define WRITE_TIMER_REG32(reg, val) \
+({ \
+ __asm__ volatile("mcr p15, 0, %0, c14, " reg :: "r" (val)); \
+ ISB; \
+})
+
+#define WRITE_TIMER_REG64(reg, val) \
+({ \
+ __asm__ volatile("mcrr p15, " reg ", %0, %H0, c14" :: "r" (val)); \
+ ISB; \
+})
+
+#endif
+
+#ifndef TIMER_ARM_GENERIC_SELECTED
+#define TIMER_ARM_GENERIC_SELECTED CNTP
+#endif
+
+#define COMBINE3(a,b,c) a ## b ## c
+#define XCOMBINE3(a,b,c) COMBINE3(a, b, c)
+
+#define SELECTED_TIMER_REG(reg) XCOMBINE3(TIMER_REG_, TIMER_ARM_GENERIC_SELECTED, reg)
+#define TIMER_REG_CTL SELECTED_TIMER_REG(_CTL)
+#define TIMER_REG_CVAL SELECTED_TIMER_REG(_CVAL)
+#define TIMER_REG_TVAL SELECTED_TIMER_REG(_TVAL)
+#define TIMER_REG_CT SELECTED_TIMER_REG(CT)
+
+
+static platform_timer_callback t_callback;
+static int timer_irq;
+
+struct fp_32_64 cntpct_per_ms;
+struct fp_32_64 ms_per_cntpct;
+struct fp_32_64 us_per_cntpct;
+
+static uint64_t lk_time_to_cntpct(lk_time_t lk_time)
+{
+ return u64_mul_u32_fp32_64(lk_time, cntpct_per_ms);
+}
+
+static lk_time_t cntpct_to_lk_time(uint64_t cntpct)
+{
+ return u32_mul_u64_fp32_64(cntpct, ms_per_cntpct);
+}
+
+static lk_bigtime_t cntpct_to_lk_bigtime(uint64_t cntpct)
+{
+ return u64_mul_u64_fp32_64(cntpct, us_per_cntpct);
+}
+
+static uint32_t read_cntfrq(void)
+{
+ uint32_t cntfrq;
+
+ cntfrq = READ_TIMER_REG32(TIMER_REG_CNTFRQ);
+ LTRACEF("cntfrq: 0x%08x, %u\n", cntfrq, cntfrq);
+ return cntfrq;
+}
+
+static uint32_t read_cntp_ctl(void)
+{
+ uint32_t cntp_ctl;
+
+ cntp_ctl = READ_TIMER_REG32(TIMER_REG_CTL);
+ return cntp_ctl;
+}
+
+static void write_cntp_ctl(uint32_t cntp_ctl)
+{
+ LTRACEF_LEVEL(3, "cntp_ctl: 0x%x %x\n", cntp_ctl, read_cntp_ctl());
+ WRITE_TIMER_REG32(TIMER_REG_CTL, cntp_ctl);
+}
+
+static void write_cntp_cval(uint64_t cntp_cval)
+{
+ LTRACEF_LEVEL(3, "cntp_cval: 0x%016llx, %llu\n", cntp_cval, cntp_cval);
+ WRITE_TIMER_REG64(TIMER_REG_CVAL, cntp_cval);
+}
+
+static void write_cntp_tval(int32_t cntp_tval)
+{
+ LTRACEF_LEVEL(3, "cntp_tval: 0x%08x, %d\n", cntp_tval, cntp_tval);
+ WRITE_TIMER_REG32(TIMER_REG_TVAL, cntp_tval);
+}
+
+static uint64_t read_cntpct(void)
+{
+ uint64_t cntpct;
+
+ cntpct = READ_TIMER_REG64(TIMER_REG_CT);
+ LTRACEF_LEVEL(3, "cntpct: 0x%016llx, %llu\n", cntpct, cntpct);
+ return cntpct;
+}
+
+static enum handler_return platform_tick(void *arg)
+{
+ write_cntp_ctl(0);
+ if (t_callback) {
+ return t_callback(arg, current_time());
+ } else {
+ return INT_NO_RESCHEDULE;
+ }
+}
+
+status_t platform_set_oneshot_timer(platform_timer_callback callback, void *arg, lk_time_t interval)
+{
+ uint64_t cntpct_interval = lk_time_to_cntpct(interval);
+
+ ASSERT(arg == NULL);
+
+ t_callback = callback;
+ if (cntpct_interval <= INT_MAX)
+ write_cntp_tval(cntpct_interval);
+ else
+ write_cntp_cval(read_cntpct() + cntpct_interval);
+ write_cntp_ctl(1);
+
+ return 0;
+}
+
+void platform_stop_timer(void)
+{
+ write_cntp_ctl(0);
+}
+
+lk_bigtime_t current_time_hires(void)
+{
+ return cntpct_to_lk_bigtime(read_cntpct());
+}
+
+lk_time_t current_time(void)
+{
+ return cntpct_to_lk_time(read_cntpct());
+}
+
+static uint32_t abs_int32(int32_t a)
+{
+ return (a > 0) ? a : -a;
+}
+
+static uint64_t abs_int64(int64_t a)
+{
+ return (a > 0) ? a : -a;
+}
+
+static void test_time_conversion_check_result(uint64_t a, uint64_t b, uint64_t limit, bool is32)
+{
+ if (a != b) {
+ uint64_t diff = is32 ? abs_int32(a - b) : abs_int64(a - b);
+ if (diff <= limit)
+ LTRACEF("ROUNDED by %llu (up to %llu allowed)\n", diff, limit);
+ else
+ TRACEF("FAIL, off by %llu\n", diff);
+ }
+}
+
+static void test_lk_time_to_cntpct(uint32_t cntfrq, lk_time_t lk_time)
+{
+ uint64_t cntpct = lk_time_to_cntpct(lk_time);
+ uint64_t expected_cntpct = ((uint64_t)cntfrq * lk_time + 500) / 1000;
+
+ test_time_conversion_check_result(cntpct, expected_cntpct, 1, false);
+ LTRACEF_LEVEL(2, "lk_time_to_cntpct(%u): got %llu, expect %llu\n", lk_time, cntpct, expected_cntpct);
+}
+
+static void test_cntpct_to_lk_time(uint32_t cntfrq, lk_time_t expected_lk_time, uint32_t wrap_count)
+{
+ lk_time_t lk_time;
+ uint64_t cntpct;
+
+ cntpct = (uint64_t)cntfrq * expected_lk_time / 1000;
+ if ((uint64_t)cntfrq * wrap_count > UINT_MAX)
+ cntpct += (((uint64_t)cntfrq << 32) / 1000) * wrap_count;
+ else
+ cntpct += (((uint64_t)(cntfrq * wrap_count) << 32) / 1000);
+ lk_time = cntpct_to_lk_time(cntpct);
+
+ test_time_conversion_check_result(lk_time, expected_lk_time, (1000 + cntfrq - 1) / cntfrq, true);
+ LTRACEF_LEVEL(2, "cntpct_to_lk_time(%llu): got %u, expect %u\n", cntpct, lk_time, expected_lk_time);
+}
+
+static void test_cntpct_to_lk_bigtime(uint32_t cntfrq, uint64_t expected_s)
+{
+ lk_bigtime_t expected_lk_bigtime = expected_s * 1000 * 1000;
+ uint64_t cntpct = (uint64_t)cntfrq * expected_s;
+ lk_bigtime_t lk_bigtime = cntpct_to_lk_bigtime(cntpct);
+
+ test_time_conversion_check_result(lk_bigtime, expected_lk_bigtime, (1000 * 1000 + cntfrq - 1) / cntfrq, false);
+ LTRACEF_LEVEL(2, "cntpct_to_lk_bigtime(%llu): got %llu, expect %llu\n", cntpct, lk_bigtime, expected_lk_bigtime);
+}
+
+static void test_time_conversions(uint32_t cntfrq)
+{
+ test_lk_time_to_cntpct(cntfrq, 0);
+ test_lk_time_to_cntpct(cntfrq, 1);
+ test_lk_time_to_cntpct(cntfrq, INT_MAX);
+ test_lk_time_to_cntpct(cntfrq, INT_MAX + 1U);
+ test_lk_time_to_cntpct(cntfrq, ~0);
+ test_cntpct_to_lk_time(cntfrq, 0, 0);
+ test_cntpct_to_lk_time(cntfrq, INT_MAX, 0);
+ test_cntpct_to_lk_time(cntfrq, INT_MAX + 1U, 0);
+ test_cntpct_to_lk_time(cntfrq, ~0, 0);
+ test_cntpct_to_lk_time(cntfrq, 0, 1);
+ test_cntpct_to_lk_time(cntfrq, 0, 7);
+ test_cntpct_to_lk_time(cntfrq, 0, 70);
+ test_cntpct_to_lk_time(cntfrq, 0, 700);
+ test_cntpct_to_lk_bigtime(cntfrq, 0);
+ test_cntpct_to_lk_bigtime(cntfrq, 1);
+ test_cntpct_to_lk_bigtime(cntfrq, 60 * 60 * 24);
+ test_cntpct_to_lk_bigtime(cntfrq, 60 * 60 * 24 * 365);
+ test_cntpct_to_lk_bigtime(cntfrq, 60 * 60 * 24 * (365 * 10 + 2));
+ test_cntpct_to_lk_bigtime(cntfrq, 60ULL * 60 * 24 * (365 * 100 + 2));
+}
+
+static void arm_generic_timer_init_conversion_factors(uint32_t cntfrq)
+{
+ fp_32_64_div_32_32(&cntpct_per_ms, cntfrq, 1000);
+ fp_32_64_div_32_32(&ms_per_cntpct, 1000, cntfrq);
+ fp_32_64_div_32_32(&us_per_cntpct, 1000 * 1000, cntfrq);
+ LTRACEF("cntpct_per_ms: %08x.%08x%08x\n", cntpct_per_ms.l0, cntpct_per_ms.l32, cntpct_per_ms.l64);
+ LTRACEF("ms_per_cntpct: %08x.%08x%08x\n", ms_per_cntpct.l0, ms_per_cntpct.l32, ms_per_cntpct.l64);
+ LTRACEF("us_per_cntpct: %08x.%08x%08x\n", us_per_cntpct.l0, us_per_cntpct.l32, us_per_cntpct.l64);
+}
+
+void arm_generic_timer_init(int irq, uint32_t freq_override)
+{
+ uint32_t cntfrq;
+
+ if (freq_override == 0) {
+ cntfrq = read_cntfrq();
+
+ if (!cntfrq) {
+ TRACEF("Failed to initialize timer, frequency is 0\n");
+ return;
+ }
+ } else {
+ cntfrq = freq_override;
+ }
+
+#if LOCAL_TRACE
+ LTRACEF("Test min cntfrq\n");
+ arm_generic_timer_init_conversion_factors(1);
+ test_time_conversions(1);
+ LTRACEF("Test max cntfrq\n");
+ arm_generic_timer_init_conversion_factors(~0);
+ test_time_conversions(~0);
+ LTRACEF("Set actual cntfrq\n");
+#endif
+ arm_generic_timer_init_conversion_factors(cntfrq);
+ test_time_conversions(cntfrq);
+
+ LTRACEF("register irq %d on cpu %d\n", irq, arch_curr_cpu_num());
+ register_int_handler(irq, &platform_tick, NULL);
+ unmask_interrupt(irq);
+
+ timer_irq = irq;
+}
+
+static void arm_generic_timer_init_secondary_cpu(uint level)
+{
+ LTRACEF("register irq %d on cpu %d\n", timer_irq, arch_curr_cpu_num());
+ register_int_handler(timer_irq, &platform_tick, NULL);
+ unmask_interrupt(timer_irq);
+}
+
+/* secondary cpu initialize the timer just before the kernel starts with interrupts enabled */
+LK_INIT_HOOK_FLAGS(arm_generic_timer_init_secondary_cpu,
+ arm_generic_timer_init_secondary_cpu,
+ LK_INIT_LEVEL_THREADING - 1, LK_INIT_FLAG_SECONDARY_CPUS);
+
+static void arm_generic_timer_resume_cpu(uint level)
+{
+ /* Always trigger a timer interrupt on each cpu for now */
+ write_cntp_tval(0);
+ write_cntp_ctl(1);
+}
+
+LK_INIT_HOOK_FLAGS(arm_generic_timer_resume_cpu, arm_generic_timer_resume_cpu,
+ LK_INIT_LEVEL_PLATFORM, LK_INIT_FLAG_CPU_RESUME);
+
+/* vim: set noexpandtab: */
diff --git a/src/bsp/lk/dev/timer/arm_generic/include/dev/timer/arm_generic.h b/src/bsp/lk/dev/timer/arm_generic/include/dev/timer/arm_generic.h
new file mode 100644
index 0000000..368d43c
--- /dev/null
+++ b/src/bsp/lk/dev/timer/arm_generic/include/dev/timer/arm_generic.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2013, Google Inc. All rights reserved.
+ *
+ * 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 __DEV_TIMER_ARM_GENERIC_H
+#define __DEV_TIMER_ARM_GENERIC_H
+
+#include <sys/types.h>
+
+/* if freq_override != 0, use that as the operating frequency instead of CNTFRQ register */
+void arm_generic_timer_init(int irq, uint32_t freq_override);
+
+#endif
+
diff --git a/src/bsp/lk/dev/timer/arm_generic/rules.mk b/src/bsp/lk/dev/timer/arm_generic/rules.mk
new file mode 100644
index 0000000..7331312
--- /dev/null
+++ b/src/bsp/lk/dev/timer/arm_generic/rules.mk
@@ -0,0 +1,14 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+GLOBAL_DEFINES += \
+ PLATFORM_HAS_DYNAMIC_TIMER=1
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/arm_generic_timer.c
+
+MODULE_DEPS += \
+ lib/fixed_point
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/timer/or1k_ticktimer/include/dev/timer/or1k_ticktimer.h b/src/bsp/lk/dev/timer/or1k_ticktimer/include/dev/timer/or1k_ticktimer.h
new file mode 100644
index 0000000..55e51ae
--- /dev/null
+++ b/src/bsp/lk/dev/timer/or1k_ticktimer/include/dev/timer/or1k_ticktimer.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015 Stefan Kristiansson
+ *
+ * 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>
+
+void or1k_ticktimer_init(uint32_t freq);
diff --git a/src/bsp/lk/dev/timer/or1k_ticktimer/or1k_ticktimer.c b/src/bsp/lk/dev/timer/or1k_ticktimer/or1k_ticktimer.c
new file mode 100644
index 0000000..1dc64e6
--- /dev/null
+++ b/src/bsp/lk/dev/timer/or1k_ticktimer/or1k_ticktimer.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 2015 Stefan Kristiansson
+ *
+ * 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 <trace.h>
+#include <err.h>
+#include <kernel/thread.h>
+#include <platform/timer.h>
+#include <platform/debug.h>
+#include <sys/types.h>
+#include <arch/or1k.h>
+
+#define LOCAL_TRACE 0
+
+static platform_timer_callback timer_cb;
+static void *timer_arg;
+
+static uint32_t timer_freq;
+
+static volatile uint64_t ticks = 0;
+
+status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, lk_time_t interval)
+{
+ LTRACEF("cb %p, arg %p, interval %ld\n", callback, arg, interval);
+
+ uint32_t ttmr = (uint64_t)timer_freq * interval / 1000;
+ LTRACEF("count 0x%x\n", ttmr);
+
+ timer_cb = callback;
+ timer_arg = arg;
+
+ /* disable timer before doing changes */
+ mtspr(OR1K_SPR_TICK_TTMR_ADDR, 0);
+ /* reset timer counter */
+ mtspr(OR1K_SPR_TICK_TTCR_ADDR, 0);
+ /* enable timer with given interval in 'restart' mode */
+ ttmr = OR1K_SPR_TICK_TTMR_MODE_SET(ttmr | OR1K_SPR_TICK_TTMR_IE_MASK,
+ OR1K_SPR_TICK_TTMR_MODE_RESTART);
+ mtspr(OR1K_SPR_TICK_TTMR_ADDR, ttmr);
+
+ return NO_ERROR;
+}
+
+lk_bigtime_t current_time_hires(void)
+{
+ return (lk_bigtime_t)ticks * 10000;
+}
+
+lk_time_t current_time(void)
+{
+ return (lk_time_t)ticks * 10;
+}
+
+enum handler_return platform_tick(void)
+{
+ ticks++;
+
+ /* clear pending interrupt flag */
+ mtspr(OR1K_SPR_TICK_TTMR_ADDR,
+ mfspr(OR1K_SPR_TICK_TTMR_ADDR) & ~(OR1K_SPR_TICK_TTMR_IP_MASK));
+
+ return timer_cb(timer_arg, ticks * 10);
+}
+
+void or1k_ticktimer_init(uint32_t freq)
+{
+ timer_freq = freq;
+ /* disable timer */
+ mtspr(OR1K_SPR_TICK_TTMR_ADDR, 0);
+ /* reset timer counter */
+ mtspr(OR1K_SPR_TICK_TTCR_ADDR, 0);
+ /* clear pending interrupt flag */
+ mtspr(OR1K_SPR_TICK_TTMR_ADDR,
+ mfspr(OR1K_SPR_TICK_TTMR_ADDR) & ~(OR1K_SPR_TICK_TTMR_IP_MASK));
+}
diff --git a/src/bsp/lk/dev/timer/or1k_ticktimer/rules.mk b/src/bsp/lk/dev/timer/or1k_ticktimer/rules.mk
new file mode 100644
index 0000000..5525f65
--- /dev/null
+++ b/src/bsp/lk/dev/timer/or1k_ticktimer/rules.mk
@@ -0,0 +1,11 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+#GLOBAL_DEFINES += \
+# PLATFORM_HAS_DYNAMIC_TIMER=1
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/or1k_ticktimer.c
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/usb/rules.mk b/src/bsp/lk/dev/usb/rules.mk
new file mode 100644
index 0000000..a66753d
--- /dev/null
+++ b/src/bsp/lk/dev/usb/rules.mk
@@ -0,0 +1,8 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/usb.c
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/usb/usb.c b/src/bsp/lk/dev/usb/usb.c
new file mode 100644
index 0000000..5c8948a
--- /dev/null
+++ b/src/bsp/lk/dev/usb/usb.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 2008-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 <debug.h>
+#include <trace.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <list.h>
+#include <dev/usbc.h>
+#include <dev/usb.h>
+#include <lk/init.h>
+
+#define LOCAL_TRACE 0
+
+#define MAX_STRINGS 8
+static struct {
+ bool active;
+ uint8_t active_config;
+
+ usb_config *config;
+
+ struct list_node cb_list;
+
+ usb_string strings[MAX_STRINGS];
+} usb;
+
+typedef struct {
+ struct list_node node;
+ usb_callback_t cb;
+ void *cookie;
+} usb_callback_container_t;
+
+static void usb_do_callbacks(usb_callback_op_t op, const union usb_callback_args *args);
+
+static void append_desc_data(usb_descriptor *desc, const void *dat, size_t len)
+{
+ uint8_t *ptr = malloc(desc->len + len);
+
+ memcpy(ptr, desc->desc, desc->len);
+ memcpy(ptr + desc->len, dat, len);
+
+ /* free the old buffer if it wasn't marked static */
+ if ((desc->flags & USB_DESC_FLAG_STATIC) == 0)
+ free(desc->desc);
+ desc->flags &= ~USB_DESC_FLAG_STATIC;
+
+ desc->desc = ptr;
+ desc->len += len;
+}
+
+/* returns the interface number assigned */
+static int usb_append_interface(usb_descriptor *desc, const uint8_t *int_descr, size_t len)
+{
+ uint8_t *ptr = malloc(len);
+ int interface_num;
+
+ // create a temporary copy of the interface
+ memcpy(ptr, int_descr, len);
+
+ // find the last interface used
+ interface_num = ((uint8_t *)desc->desc)[4]; // current interface
+
+ // patch our interface descriptor with the new id
+ ptr[2] = interface_num;
+
+ // append it to our config desriptor
+ append_desc_data(desc, ptr, len);
+ free(ptr);
+
+ // patch the total length of the config descriptor and set the number of interfaces
+ ((uint16_t *)desc->desc)[1] += len;
+ interface_num++;
+ ((uint8_t *)desc->desc)[4] = interface_num;
+
+ return interface_num - 1;
+}
+
+int usb_append_interface_highspeed(const uint8_t *int_descr, size_t len)
+{
+ return usb_append_interface(&usb.config->highspeed.config, int_descr, len);
+}
+
+int usb_append_interface_lowspeed(const uint8_t *int_descr, size_t len)
+{
+ return usb_append_interface(&usb.config->lowspeed.config, int_descr, len);
+}
+
+void usb_set_string_descriptor(usb_descriptor *desc, const char *string)
+{
+ int len = strlen(string);
+ ushort *data;
+ int datalen = len * 2 + 2;
+
+ data = malloc(datalen);
+
+ /* write length field */
+ data[0] = 0x0300 + datalen;
+
+ /* copy the string into the uint16_t based usb string */
+ int i;
+ for (i = 0; i < len; i++) {
+ data[i + 1] = string[i];
+ }
+
+ desc->desc = (void *)data;
+ desc->len = datalen;
+}
+
+static void set_usb_id(uint16_t vendor, uint16_t product)
+{
+ // patch the current configuration to with the vendor/product id
+ ((uint16_t *)usb.config->lowspeed.device.desc)[4] = vendor;
+ ((uint16_t *)usb.config->lowspeed.device.desc)[5] = product;
+
+ ((uint16_t *)usb.config->highspeed.device.desc)[4] = vendor;
+ ((uint16_t *)usb.config->highspeed.device.desc)[5] = product;
+}
+
+status_t usb_add_string(const char *string, uint8_t id)
+{
+ uint i;
+ size_t len = strlen(string);
+
+ uint16_t *strbuf = malloc(len * 2 + 2);
+ if (!strbuf)
+ return ERR_NO_MEMORY;
+
+ /* build the usb string descriptor */
+ strbuf[0] = 0x300 | (len * 2 + 2);
+ for (i = 0; i < len; i++) {
+ strbuf[i + 1] = (uint16_t)string[i];
+ }
+
+ /* find a slot to put it */
+ for (i = 0; i < MAX_STRINGS; i++) {
+ if (usb.strings[i].id == 0) {
+ usb.strings[i].string.desc = strbuf;
+ usb.strings[i].string.len = len * 2 + 2;
+ usb.strings[i].id = id;
+ return NO_ERROR;
+ }
+ }
+
+ /* couldn't find a spot */
+ free(strbuf);
+ return ERR_NO_MEMORY;
+}
+
+static void usb_set_active_config(uint8_t config)
+{
+ if (config != usb.active_config) {
+ usb.active_config = config;
+ if (usb.active_config != 0) {
+ printf("usb online\n");
+ usb_do_callbacks(USB_CB_ONLINE, NULL);
+ } else {
+ printf("usb offline\n");
+ usb_do_callbacks(USB_CB_OFFLINE, NULL);
+ }
+ }
+}
+
+status_t usb_register_callback(usb_callback_t cb, void *cookie)
+{
+ DEBUG_ASSERT(cb);
+
+ usb_callback_container_t *c = malloc(sizeof(usb_callback_container_t));
+ if (!c)
+ return ERR_NO_MEMORY;
+
+ c->cb = cb;
+ c->cookie = cookie;
+ list_add_tail(&usb.cb_list, &c->node);
+
+ return NO_ERROR;
+}
+
+static void usb_do_callbacks(usb_callback_op_t op, const union usb_callback_args *args)
+{
+ usb_callback_container_t *c;
+ list_for_every_entry(&usb.cb_list, c, usb_callback_container_t, node) {
+ c->cb(c->cookie, op, args);
+ }
+}
+
+status_t usbc_callback(usb_callback_op_t op, const union usb_callback_args *args)
+{
+ LTRACEF("op %d, args %p\n", op, args);
+
+ /* start looking for specific things to handle */
+ if (op == USB_CB_SETUP_MSG) {
+ bool setup_handled = false;
+ const struct usb_setup *setup = args->setup;
+ DEBUG_ASSERT(setup);
+ LTRACEF("SETUP: req_type=%#x req=%#x value=%#x index=%#x len=%#x\n",
+ setup->request_type, setup->request, setup->value, setup->index, setup->length);
+
+ if ((setup->request_type & TYPE_MASK) == TYPE_STANDARD) {
+ switch (setup->request) {
+ case SET_ADDRESS:
+ LTRACEF("SET_ADDRESS 0x%x\n", setup->value);
+ usbc_ep0_ack();
+ usbc_set_address(setup->value);
+ setup_handled = true;
+ break;
+ case SET_FEATURE:
+ case CLEAR_FEATURE:
+ LTRACEF("SET/CLEAR_FEATURE, feature 0x%x\n", setup->value);
+ usbc_ep0_ack();
+ setup_handled = true;
+ break;
+ case SET_DESCRIPTOR:
+ LTRACEF("SET_DESCRIPTOR\n");
+ usbc_ep0_stall();
+ setup_handled = true;
+ break;
+ case GET_DESCRIPTOR: {
+ if ((setup->request_type & RECIP_MASK) == RECIP_DEVICE) {
+ /* handle device descriptor fetches */
+
+ /* Get the right descriptors based on current speed */
+ const struct usb_descriptor_speed *speed;
+ if (usbc_is_highspeed()) {
+ speed = &usb.config->highspeed;
+ } else {
+ speed = &usb.config->lowspeed;
+ }
+
+ switch (setup->value) {
+ case 0x100: /* device */
+ LTRACEF("got GET_DESCRIPTOR, device descriptor\n");
+ usbc_ep0_send(speed->device.desc, speed->device.len,
+ setup->length);
+ break;
+ case 0x200: /* CONFIGURATION */
+ LTRACEF("got GET_DESCRIPTOR, config descriptor\n");
+ usbc_ep0_send(speed->config.desc, speed->config.len,
+ setup->length);
+ break;
+ case 0x300: /* Language ID */
+ LTRACEF("got GET_DESCRIPTOR, language id\n");
+ usbc_ep0_send(usb.config->langid.desc,
+ usb.config->langid.len, setup->length);
+ break;
+ case (0x301)...(0x3ff): {
+ /* string descriptor, search our list for a match */
+ uint i;
+ bool found = false;
+ uint8_t id = setup->value & 0xff;
+ for (i = 0; i < MAX_STRINGS; i++) {
+ if (usb.strings[i].id == id) {
+ usbc_ep0_send(usb.strings[i].string.desc,
+ usb.strings[i].string.len,
+ setup->length);
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ /* couldn't find one, stall */
+ usbc_ep0_stall();
+ }
+ break;
+ }
+ case 0x600: /* DEVICE QUALIFIER */
+ LTRACEF("got GET_DESCRIPTOR, device qualifier\n");
+ usbc_ep0_send(speed->device_qual.desc,
+ speed->device_qual.len, setup->length);
+ break;
+ case 0xa00:
+ /* we aint got one of these */
+ LTRACEF("got GET_DESCRIPTOR, debug descriptor\n");
+ usbc_ep0_stall();
+ break;
+ default:
+ LTRACEF("unhandled descriptor %#x\n", setup->value);
+ // stall
+ break;
+ }
+ setup_handled = true;
+ }
+ break;
+ }
+
+ case SET_CONFIGURATION:
+ LTRACEF("SET_CONFIGURATION %d\n", setup->value);
+ usbc_ep0_ack();
+ usb_set_active_config(setup->value);
+ break;
+
+ case GET_CONFIGURATION:
+ LTRACEF("GET_CONFIGURATION\n");
+ usbc_ep0_send(&usb.active_config, 1, setup->length);
+ break;
+
+ case SET_INTERFACE:
+ LTRACEF("SET_INTERFACE %d\n", setup->value);
+ usbc_ep0_ack();
+ break;
+
+ case GET_INTERFACE: {
+ static uint8_t i = 1;
+ LTRACEF("GET_INTERFACE\n");
+ usbc_ep0_send(&i, 1, setup->length);
+ break;
+ }
+
+ case GET_STATUS: {
+ static uint16_t i = 1; // self powered
+ LTRACEF("GET_STATUS\n");
+ usbc_ep0_send(&i, 2, setup->length);
+ break;
+ }
+ default:
+ LTRACEF("unhandled standard request 0x%x\n", setup->request);
+ }
+ } else {
+ LTRACEF("unhandled nonstandard request 0x%x\n", setup->request);
+ }
+
+ if (!setup_handled) {
+ usb_do_callbacks(op, args);
+ }
+ } else if (op == USB_CB_RESET) {
+ usb_do_callbacks(op, args);
+
+ usb.active_config = 0;
+ usb_do_callbacks(USB_CB_OFFLINE, args);
+ } else {
+ // other non setup messages, pass them down to anyone else
+ usb_do_callbacks(op, args);
+ }
+
+ return NO_ERROR;
+}
+
+status_t usb_setup(usb_config *config)
+{
+ DEBUG_ASSERT(config);
+ DEBUG_ASSERT(usb.active == false);
+
+ usb.config = config;
+
+ return NO_ERROR;
+}
+
+status_t usb_start(void)
+{
+ DEBUG_ASSERT(usb.config);
+ DEBUG_ASSERT(usb.active == false);
+
+ // go online
+ usbc_set_active(true);
+ usb.active = true;
+
+ return NO_ERROR;
+}
+
+status_t usb_stop(void)
+{
+ DEBUG_ASSERT(usb.active == true);
+
+ usb.active = false;
+ usbc_set_active(false);
+
+ return NO_ERROR;
+}
+
+static void usb_init(uint level)
+{
+ list_initialize(&usb.cb_list);
+}
+
+LK_INIT_HOOK(usb, usb_init, LK_INIT_LEVEL_THREADING);
diff --git a/src/bsp/lk/dev/virtio/block/include/dev/virtio/block.h b/src/bsp/lk/dev/virtio/block/include/dev/virtio/block.h
new file mode 100644
index 0000000..042121d
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/block/include/dev/virtio/block.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.
+ */
+#pragma once
+
+#include <compiler.h>
+#include <sys/types.h>
+#include <dev/virtio.h>
+
+status_t virtio_block_init(struct virtio_device *dev, uint32_t host_features) __NONNULL();
+
+ssize_t virtio_block_read_write(struct virtio_device *dev, void *buf, off_t offset, size_t len, bool write) __NONNULL();
+
diff --git a/src/bsp/lk/dev/virtio/block/rules.mk b/src/bsp/lk/dev/virtio/block/rules.mk
new file mode 100644
index 0000000..f744bcb
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/block/rules.mk
@@ -0,0 +1,13 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/virtio-block.c \
+
+MODULE_DEPS += \
+ dev/virtio \
+ lib/bio
+
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/virtio/block/virtio-block.c b/src/bsp/lk/dev/virtio/block/virtio-block.c
new file mode 100644
index 0000000..61db486
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/block/virtio-block.c
@@ -0,0 +1,340 @@
+/*
+ * 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 <dev/virtio/block.h>
+
+#include <debug.h>
+#include <assert.h>
+#include <trace.h>
+#include <compiler.h>
+#include <list.h>
+#include <err.h>
+#include <kernel/thread.h>
+#include <kernel/event.h>
+#include <kernel/mutex.h>
+#include <kernel/vm.h>
+#include <lib/bio.h>
+
+#define LOCAL_TRACE 0
+
+struct virtio_blk_config {
+ uint64_t capacity;
+ uint32_t size_max;
+ uint32_t seg_max;
+ struct virtio_blk_geometry {
+ uint16_t cylinders;
+ uint8_t heads;
+ uint8_t sectors;
+ } geometry;
+ uint32_t blk_size;
+} __PACKED;
+
+struct virtio_blk_req {
+ uint32_t type;
+ uint32_t ioprio;
+ uint64_t sector;
+} __PACKED;
+
+#define VIRTIO_BLK_F_BARRIER (1<<0)
+#define VIRTIO_BLK_F_SIZE_MAX (1<<1)
+#define VIRTIO_BLK_F_SEG_MAX (1<<2)
+#define VIRTIO_BLK_F_GEOMETRY (1<<4)
+#define VIRTIO_BLK_F_RO (1<<5)
+#define VIRTIO_BLK_F_BLK_SIZE (1<<6)
+#define VIRTIO_BLK_F_SCSI (1<<7)
+#define VIRTIO_BLK_F_FLUSH (1<<9)
+#define VIRTIO_BLK_F_TOPOLOGY (1<<10)
+#define VIRTIO_BLK_F_CONFIG_WCE (1<<11)
+
+#define VIRTIO_BLK_T_IN 0
+#define VIRTIO_BLK_T_OUT 1
+#define VIRTIO_BLK_T_FLUSH 4
+
+#define VIRTIO_BLK_S_OK 0
+#define VIRTIO_BLK_S_IOERR 1
+#define VIRTIO_BLK_S_UNSUPP 2
+
+static enum handler_return virtio_block_irq_driver_callback(struct virtio_device *dev, uint ring, const struct vring_used_elem *e);
+static ssize_t virtio_bdev_read_block(struct bdev *bdev, void *buf, bnum_t block, uint count);
+static ssize_t virtio_bdev_write_block(struct bdev *bdev, const void *buf, bnum_t block, uint count);
+
+struct virtio_block_dev {
+ struct virtio_device *dev;
+
+ mutex_t lock;
+ event_t io_event;
+
+ /* bio block device */
+ bdev_t bdev;
+
+ /* one blk_req structure for io, not crossing a page boundary */
+ struct virtio_blk_req *blk_req;
+ paddr_t blk_req_phys;
+
+ /* one uint8_t response word */
+ uint8_t blk_response;
+ paddr_t blk_response_phys;
+};
+
+status_t virtio_block_init(struct virtio_device *dev, uint32_t host_features)
+{
+ LTRACEF("dev %p, host_features 0x%x\n", dev, host_features);
+
+ /* allocate a new block device */
+ struct virtio_block_dev *bdev = malloc(sizeof(struct virtio_block_dev));
+ if (!bdev)
+ return ERR_NO_MEMORY;
+
+ mutex_init(&bdev->lock);
+ event_init(&bdev->io_event, false, EVENT_FLAG_AUTOUNSIGNAL);
+
+ bdev->dev = dev;
+ dev->priv = bdev;
+
+ bdev->blk_req = memalign(sizeof(struct virtio_blk_req), sizeof(struct virtio_blk_req));
+#if WITH_KERNEL_VM
+ arch_mmu_query((vaddr_t)bdev->blk_req, &bdev->blk_req_phys, NULL);
+#else
+ bdev->blk_freq_phys = (uint64_t)(uintptr_t)bdev->blk_req;
+#endif
+ LTRACEF("blk_req structure at %p (0x%lx phys)\n", bdev->blk_req, bdev->blk_req_phys);
+
+#if WITH_KERNEL_VM
+ arch_mmu_query((vaddr_t)&bdev->blk_response, &bdev->blk_response_phys, NULL);
+#else
+ bdev->blk_response_phys = (uint64_t)(uintptr_t)&bdev->blk_response;
+#endif
+
+ /* make sure the device is reset */
+ virtio_reset_device(dev);
+
+ volatile struct virtio_blk_config *config = (struct virtio_blk_config *)dev->config_ptr;
+
+ LTRACEF("capacity 0x%llx\n", config->capacity);
+ LTRACEF("size_max 0x%x\n", config->size_max);
+ LTRACEF("seg_max 0x%x\n", config->seg_max);
+ LTRACEF("blk_size 0x%x\n", config->blk_size);
+
+ /* ack and set the driver status bit */
+ virtio_status_acknowledge_driver(dev);
+
+ // XXX check features bits and ack/nak them
+
+ /* allocate a virtio ring */
+ virtio_alloc_ring(dev, 0, 256);
+
+ /* set our irq handler */
+ dev->irq_driver_callback = &virtio_block_irq_driver_callback;
+
+ /* set DRIVER_OK */
+ virtio_status_driver_ok(dev);
+
+ /* construct the block device */
+ static uint8_t found_index = 0;
+ char buf[16];
+ snprintf(buf, sizeof(buf), "virtio%u", found_index++);
+ bio_initialize_bdev(&bdev->bdev, buf,
+ config->blk_size, config->capacity,
+ 0, NULL, BIO_FLAGS_NONE);
+
+ /* override our block device hooks */
+ bdev->bdev.read_block = &virtio_bdev_read_block;
+ bdev->bdev.write_block = &virtio_bdev_write_block;
+
+ bio_register_device(&bdev->bdev);
+
+ printf("found virtio block device of size %lld\n", config->capacity * config->blk_size);
+
+ return NO_ERROR;
+}
+
+static enum handler_return virtio_block_irq_driver_callback(struct virtio_device *dev, uint ring, const struct vring_used_elem *e)
+{
+ struct virtio_block_dev *bdev = (struct virtio_block_dev *)dev->priv;
+
+ LTRACEF("dev %p, ring %u, e %p, id %u, len %u\n", dev, ring, e, e->id, e->len);
+
+ /* parse our descriptor chain, add back to the free queue */
+ uint16_t i = e->id;
+ for (;;) {
+ int next;
+ struct vring_desc *desc = virtio_desc_index_to_desc(dev, ring, i);
+
+ //virtio_dump_desc(desc);
+
+ if (desc->flags & VRING_DESC_F_NEXT) {
+ next = desc->next;
+ } else {
+ /* end of chain */
+ next = -1;
+ }
+
+ virtio_free_desc(dev, ring, i);
+
+ if (next < 0)
+ break;
+ i = next;
+ }
+
+ /* signal our event */
+ event_signal(&bdev->io_event, false);
+
+ return INT_RESCHEDULE;
+}
+
+ssize_t virtio_block_read_write(struct virtio_device *dev, void *buf, off_t offset, size_t len, bool write)
+{
+ struct virtio_block_dev *bdev = (struct virtio_block_dev *)dev->priv;
+
+ uint16_t i;
+ struct vring_desc *desc;
+ paddr_t pa;
+ vaddr_t va = (vaddr_t)buf;
+
+ LTRACEF("dev %p, buf %p, offset 0x%llx, len %zu\n", dev, buf, offset, len);
+
+ mutex_acquire(&bdev->lock);
+
+ /* set up the request */
+ bdev->blk_req->type = write ? VIRTIO_BLK_T_OUT : VIRTIO_BLK_T_IN;
+ bdev->blk_req->ioprio = 0;
+ bdev->blk_req->sector = offset / 512;
+ LTRACEF("blk_req type %u ioprio %u sector %llu\n",
+ bdev->blk_req->type, bdev->blk_req->ioprio, bdev->blk_req->sector);
+
+ /* put together a transfer */
+ desc = virtio_alloc_desc_chain(dev, 0, 3, &i);
+ LTRACEF("after alloc chain desc %p, i %u\n", desc, i);
+
+ // XXX not cache safe.
+ // At the moment only tested on arm qemu, which doesn't emulate cache.
+
+ /* set up the descriptor pointing to the head */
+ desc->addr = bdev->blk_req_phys;
+ desc->len = sizeof(struct virtio_blk_req);
+ desc->flags |= VRING_DESC_F_NEXT;
+
+ /* set up the descriptor pointing to the buffer */
+ desc = virtio_desc_index_to_desc(dev, 0, desc->next);
+#if WITH_KERNEL_VM
+ /* translate the first buffer */
+ arch_mmu_query(va, &pa, NULL);
+ desc->addr = (uint64_t)pa;
+ /* desc->len is filled in below */
+#else
+ desc->addr = (uint64_t)(uintptr_t)buf;
+ desc->len = len;
+#endif
+ desc->flags |= write ? 0 : VRING_DESC_F_WRITE; /* mark buffer as write-only if its a block read */
+ desc->flags |= VRING_DESC_F_NEXT;
+
+#if WITH_KERNEL_VM
+ /* see if we need to add more descriptors due to scatter gather */
+ paddr_t next_pa = PAGE_ALIGN(pa + 1);
+ desc->len = MIN(next_pa - pa, len);
+ LTRACEF("first descriptor va 0x%lx desc->addr 0x%llx desc->len %u\n", va, desc->addr, desc->len);
+ len -= desc->len;
+ while (len > 0) {
+ /* amount of source buffer handled by this iteration of the loop */
+ size_t len_tohandle = MIN(len, PAGE_SIZE);
+
+ /* translate the next page in the buffer */
+ va = PAGE_ALIGN(va + 1);
+ arch_mmu_query(va, &pa, NULL);
+ LTRACEF("va now 0x%lx, pa 0x%lx, next_pa 0x%lx, remaining len %zu\n", va, pa, next_pa, len);
+
+ /* is the new translated physical address contiguous to the last one? */
+ if (next_pa == pa) {
+ LTRACEF("extending last one by %zu bytes\n", len_tohandle);
+ desc->len += len_tohandle;
+ } else {
+ uint16_t next_i = virtio_alloc_desc(dev, 0);
+ struct vring_desc *next_desc = virtio_desc_index_to_desc(dev, 0, next_i);
+ DEBUG_ASSERT(next_desc);
+
+ LTRACEF("doesn't extend, need new desc, allocated desc %i (%p)\n", next_i, next_desc);
+
+ /* fill this descriptor in and put it after the last one but before the response descriptor */
+ next_desc->addr = (uint64_t)pa;
+ next_desc->len = len_tohandle;
+ next_desc->flags = write ? 0 : VRING_DESC_F_WRITE; /* mark buffer as write-only if its a block read */
+ next_desc->flags |= VRING_DESC_F_NEXT;
+ next_desc->next = desc->next;
+ desc->next = next_i;
+
+ desc = next_desc;
+ }
+ len -= len_tohandle;
+ next_pa += PAGE_SIZE;
+ }
+#endif
+
+ /* set up the descriptor pointing to the response */
+ desc = virtio_desc_index_to_desc(dev, 0, desc->next);
+ desc->addr = bdev->blk_response_phys;
+ desc->len = 1;
+ desc->flags = VRING_DESC_F_WRITE;
+
+ /* submit the transfer */
+ virtio_submit_chain(dev, 0, i);
+
+ /* kick it off */
+ virtio_kick(dev, 0);
+
+ /* wait for the transfer to complete */
+ event_wait(&bdev->io_event);
+
+ LTRACEF("status 0x%hhx\n", bdev->blk_response);
+
+ mutex_release(&bdev->lock);
+
+ return len;
+}
+
+static ssize_t virtio_bdev_read_block(struct bdev *bdev, void *buf, bnum_t block, uint count)
+{
+ struct virtio_block_dev *dev = containerof(bdev, struct virtio_block_dev, bdev);
+
+ LTRACEF("dev %p, buf %p, block 0x%x, count %u\n", bdev, buf, block, count);
+
+ if (virtio_block_read_write(dev->dev, buf, (off_t)block * dev->bdev.block_size,
+ count * dev->bdev.block_size, false) == 0) {
+ return count * dev->bdev.block_size;
+ } else {
+ return ERR_IO;
+ }
+}
+
+static ssize_t virtio_bdev_write_block(struct bdev *bdev, const void *buf, bnum_t block, uint count)
+{
+ struct virtio_block_dev *dev = containerof(bdev, struct virtio_block_dev, bdev);
+
+ LTRACEF("dev %p, buf %p, block 0x%x, count %u\n", bdev, buf, block, count);
+
+ if (virtio_block_read_write(dev->dev, (void *)buf, (off_t)block * dev->bdev.block_size,
+ count * dev->bdev.block_size, true) == 0) {
+ return count * dev->bdev.block_size;
+ } else {
+ return ERR_IO;
+ }
+}
+
diff --git a/src/bsp/lk/dev/virtio/gpu/include/dev/virtio/gpu.h b/src/bsp/lk/dev/virtio/gpu/include/dev/virtio/gpu.h
new file mode 100644
index 0000000..2300697
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/gpu/include/dev/virtio/gpu.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <compiler.h>
+#include <sys/types.h>
+#include <dev/virtio.h>
+
+status_t virtio_gpu_init(struct virtio_device *dev, uint32_t host_features) __NONNULL();
+
+status_t virtio_gpu_start(struct virtio_device *dev) __NONNULL();
+
diff --git a/src/bsp/lk/dev/virtio/gpu/rules.mk b/src/bsp/lk/dev/virtio/gpu/rules.mk
new file mode 100644
index 0000000..8210208
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/gpu/rules.mk
@@ -0,0 +1,12 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/virtio-gpu.c \
+
+MODULE_DEPS += \
+ dev/virtio \
+ lib/gfx
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/virtio/gpu/virtio-gpu.c b/src/bsp/lk/dev/virtio/gpu/virtio-gpu.c
new file mode 100644
index 0000000..d9b24c7
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/gpu/virtio-gpu.c
@@ -0,0 +1,577 @@
+/*
+ * 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 <dev/virtio/gpu.h>
+
+#include <debug.h>
+#include <assert.h>
+#include <trace.h>
+#include <compiler.h>
+#include <list.h>
+#include <err.h>
+#include <string.h>
+#include <kernel/thread.h>
+#include <kernel/event.h>
+#include <kernel/mutex.h>
+#include <kernel/vm.h>
+#include <lib/gfx.h>
+#include <dev/display.h>
+
+#include "virtio_gpu.h"
+
+#define LOCAL_TRACE 0
+
+static enum handler_return virtio_gpu_irq_driver_callback(struct virtio_device *dev, uint ring, const struct vring_used_elem *e);
+static enum handler_return virtio_gpu_config_change_callback(struct virtio_device *dev);
+static int virtio_gpu_flush_thread(void *arg);
+
+struct virtio_gpu_dev {
+ struct virtio_device *dev;
+
+ mutex_t lock;
+ event_t io_event;
+
+ void *gpu_request;
+ paddr_t gpu_request_phys;
+
+ /* a saved copy of the display */
+ struct virtio_gpu_display_one pmode;
+ int pmode_id;
+
+ /* resource id that is set as scanout */
+ uint32_t display_resource_id;
+
+ /* next resource id */
+ uint32_t next_resource_id;
+
+ event_t flush_event;
+
+ /* framebuffer */
+ void *fb;
+};
+
+static struct virtio_gpu_dev *the_gdev;
+
+static status_t send_command_response(struct virtio_gpu_dev *gdev, const void *cmd, size_t cmd_len, void **_res, size_t res_len)
+{
+ DEBUG_ASSERT(gdev);
+ DEBUG_ASSERT(cmd);
+ DEBUG_ASSERT(_res);
+ DEBUG_ASSERT(cmd_len + res_len < PAGE_SIZE);
+
+ LTRACEF("gdev %p, cmd %p, cmd_len %zu, res %p, res_len %zu\n", gdev, cmd, cmd_len, _res, res_len);
+
+ uint16_t i;
+ struct vring_desc *desc = virtio_alloc_desc_chain(gdev->dev, 0, 2, &i);
+ DEBUG_ASSERT(desc);
+
+ memcpy(gdev->gpu_request, cmd, cmd_len);
+
+ desc->addr = gdev->gpu_request_phys;
+ desc->len = cmd_len;
+ desc->flags |= VRING_DESC_F_NEXT;
+
+ /* set the second descriptor to the response with the write bit set */
+ desc = virtio_desc_index_to_desc(gdev->dev, 0, desc->next);
+ DEBUG_ASSERT(desc);
+
+ void *res = (void *)((uint8_t *)gdev->gpu_request + cmd_len);
+ *_res = res;
+ paddr_t res_phys = gdev->gpu_request_phys + cmd_len;
+ memset(res, 0, res_len);
+
+ desc->addr = res_phys;
+ desc->len = res_len;
+ desc->flags = VRING_DESC_F_WRITE;
+
+ /* submit the transfer */
+ virtio_submit_chain(gdev->dev, 0, i);
+
+ /* kick it off */
+ virtio_kick(gdev->dev, 0);
+
+ /* wait for result */
+ event_wait(&gdev->io_event);
+
+ return NO_ERROR;
+}
+
+static status_t get_display_info(struct virtio_gpu_dev *gdev)
+{
+ status_t err;
+
+ LTRACEF("gdev %p\n", gdev);
+
+ DEBUG_ASSERT(gdev);
+
+ /* grab a lock to keep this single message at a time */
+ mutex_acquire(&gdev->lock);
+
+ /* construct the get display info message */
+ struct virtio_gpu_ctrl_hdr req;
+ memset(&req, 0, sizeof(req));
+ req.type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO;
+
+ /* send the message and get a response */
+ struct virtio_gpu_resp_display_info *info;
+ err = send_command_response(gdev, &req, sizeof(req), (void **)&info, sizeof(*info));
+ DEBUG_ASSERT(err == NO_ERROR);
+ if (err < NO_ERROR) {
+ mutex_release(&gdev->lock);
+ return ERR_NOT_FOUND;
+ }
+
+ /* we got response */
+ if (info->hdr.type != VIRTIO_GPU_RESP_OK_DISPLAY_INFO) {
+ mutex_release(&gdev->lock);
+ return ERR_NOT_FOUND;
+ }
+
+ LTRACEF("response:\n");
+ for (uint i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; i++) {
+ if (info->pmodes[i].enabled) {
+ LTRACEF("%u: x %u y %u w %u h %u flags 0x%x\n", i,
+ info->pmodes[i].r.x, info->pmodes[i].r.y, info->pmodes[i].r.width, info->pmodes[i].r.height,
+ info->pmodes[i].flags);
+ if (gdev->pmode_id < 0) {
+ /* save the first valid pmode we see */
+ memcpy(&gdev->pmode, &info->pmodes[i], sizeof(gdev->pmode));
+ gdev->pmode_id = i;
+ }
+ }
+ }
+
+ /* release the lock */
+ mutex_release(&gdev->lock);
+
+ return NO_ERROR;
+}
+
+static status_t allocate_2d_resource(struct virtio_gpu_dev *gdev, uint32_t *resource_id, uint32_t width, uint32_t height)
+{
+ status_t err;
+
+ LTRACEF("gdev %p\n", gdev);
+
+ DEBUG_ASSERT(gdev);
+ DEBUG_ASSERT(resource_id);
+
+ /* grab a lock to keep this single message at a time */
+ mutex_acquire(&gdev->lock);
+
+ /* construct the request */
+ struct virtio_gpu_resource_create_2d req;
+ memset(&req, 0, sizeof(req));
+
+ req.hdr.type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D;
+ req.resource_id = gdev->next_resource_id++;
+ *resource_id = req.resource_id;
+ req.format = VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM;
+ req.width = width;
+ req.height = height;
+
+ /* send the command and get a response */
+ struct virtio_gpu_ctrl_hdr *res;
+ err = send_command_response(gdev, &req, sizeof(req), (void **)&res, sizeof(*res));
+ DEBUG_ASSERT(err == NO_ERROR);
+
+ /* see if we got a valid response */
+ LTRACEF("response type 0x%x\n", res->type);
+ err = (res->type == VIRTIO_GPU_RESP_OK_NODATA) ? NO_ERROR : ERR_NO_MEMORY;
+
+ /* release the lock */
+ mutex_release(&gdev->lock);
+
+ return err;
+}
+
+static status_t attach_backing(struct virtio_gpu_dev *gdev, uint32_t resource_id, void *ptr, size_t buf_len)
+{
+ status_t err;
+
+ LTRACEF("gdev %p, resource_id %u, ptr %p, buf_len %zu\n", gdev, resource_id, ptr, buf_len);
+
+ DEBUG_ASSERT(gdev);
+ DEBUG_ASSERT(ptr);
+
+ /* grab a lock to keep this single message at a time */
+ mutex_acquire(&gdev->lock);
+
+ /* construct the request */
+ struct {
+ struct virtio_gpu_resource_attach_backing req;
+ struct virtio_gpu_mem_entry mem;
+ } req;
+ memset(&req, 0, sizeof(req));
+
+ req.req.hdr.type = VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING;
+ req.req.resource_id = resource_id;
+ req.req.nr_entries = 1;
+
+ paddr_t pa;
+ arch_mmu_query((vaddr_t)ptr, &pa, NULL);
+ req.mem.addr = pa;
+ req.mem.length = buf_len;
+
+ /* send the command and get a response */
+ struct virtio_gpu_ctrl_hdr *res;
+ err = send_command_response(gdev, &req, sizeof(req), (void **)&res, sizeof(*res));
+ DEBUG_ASSERT(err == NO_ERROR);
+
+ /* see if we got a valid response */
+ LTRACEF("response type 0x%x\n", res->type);
+ err = (res->type == VIRTIO_GPU_RESP_OK_NODATA) ? NO_ERROR : ERR_NO_MEMORY;
+
+ /* release the lock */
+ mutex_release(&gdev->lock);
+
+ return err;
+}
+
+static status_t set_scanout(struct virtio_gpu_dev *gdev, uint32_t scanout_id, uint32_t resource_id, uint32_t width, uint32_t height)
+{
+ status_t err;
+
+ LTRACEF("gdev %p, scanout_id %u, resource_id %u, width %u, height %u\n", gdev, scanout_id, resource_id, width, height);
+
+ /* grab a lock to keep this single message at a time */
+ mutex_acquire(&gdev->lock);
+
+ /* construct the request */
+ struct virtio_gpu_set_scanout req;
+ memset(&req, 0, sizeof(req));
+
+ req.hdr.type = VIRTIO_GPU_CMD_SET_SCANOUT;
+ req.r.x = req.r.y = 0;
+ req.r.width = width;
+ req.r.height = height;
+ req.scanout_id = scanout_id;
+ req.resource_id = resource_id;
+
+ /* send the command and get a response */
+ struct virtio_gpu_ctrl_hdr *res;
+ err = send_command_response(gdev, &req, sizeof(req), (void **)&res, sizeof(*res));
+ DEBUG_ASSERT(err == NO_ERROR);
+
+ /* see if we got a valid response */
+ LTRACEF("response type 0x%x\n", res->type);
+ err = (res->type == VIRTIO_GPU_RESP_OK_NODATA) ? NO_ERROR : ERR_NO_MEMORY;
+
+ /* release the lock */
+ mutex_release(&gdev->lock);
+
+ return err;
+}
+
+static status_t flush_resource(struct virtio_gpu_dev *gdev, uint32_t resource_id, uint32_t width, uint32_t height)
+{
+ status_t err;
+
+ LTRACEF("gdev %p, resource_id %u, width %u, height %u\n", gdev, resource_id, width, height);
+
+ /* grab a lock to keep this single message at a time */
+ mutex_acquire(&gdev->lock);
+
+ /* construct the request */
+ struct virtio_gpu_resource_flush req;
+ memset(&req, 0, sizeof(req));
+
+ req.hdr.type = VIRTIO_GPU_CMD_RESOURCE_FLUSH;
+ req.r.x = req.r.y = 0;
+ req.r.width = width;
+ req.r.height = height;
+ req.resource_id = resource_id;
+
+ /* send the command and get a response */
+ struct virtio_gpu_ctrl_hdr *res;
+ err = send_command_response(gdev, &req, sizeof(req), (void **)&res, sizeof(*res));
+ DEBUG_ASSERT(err == NO_ERROR);
+
+ /* see if we got a valid response */
+ LTRACEF("response type 0x%x\n", res->type);
+ err = (res->type == VIRTIO_GPU_RESP_OK_NODATA) ? NO_ERROR : ERR_NO_MEMORY;
+
+ /* release the lock */
+ mutex_release(&gdev->lock);
+
+ return err;
+}
+
+static status_t transfer_to_host_2d(struct virtio_gpu_dev *gdev, uint32_t resource_id, uint32_t width, uint32_t height)
+{
+ status_t err;
+
+ LTRACEF("gdev %p, resource_id %u, width %u, height %u\n", gdev, resource_id, width, height);
+
+ /* grab a lock to keep this single message at a time */
+ mutex_acquire(&gdev->lock);
+
+ /* construct the request */
+ struct virtio_gpu_transfer_to_host_2d req;
+ memset(&req, 0, sizeof(req));
+
+ req.hdr.type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D;
+ req.r.x = req.r.y = 0;
+ req.r.width = width;
+ req.r.height = height;
+ req.offset = 0;
+ req.resource_id = resource_id;
+
+ /* send the command and get a response */
+ struct virtio_gpu_ctrl_hdr *res;
+ err = send_command_response(gdev, &req, sizeof(req), (void **)&res, sizeof(*res));
+ DEBUG_ASSERT(err == NO_ERROR);
+
+ /* see if we got a valid response */
+ LTRACEF("response type 0x%x\n", res->type);
+ err = (res->type == VIRTIO_GPU_RESP_OK_NODATA) ? NO_ERROR : ERR_NO_MEMORY;
+
+ /* release the lock */
+ mutex_release(&gdev->lock);
+
+ return err;
+}
+
+status_t virtio_gpu_start(struct virtio_device *dev)
+{
+ status_t err;
+
+ LTRACEF("dev %p\n", dev);
+
+ struct virtio_gpu_dev *gdev = (struct virtio_gpu_dev *)dev->priv;
+
+ /* get the display info and see if we find a valid pmode */
+ err = get_display_info(gdev);
+ if (err < 0) {
+ LTRACEF("failed to get display info\n");
+ return err;
+ }
+
+ if (gdev->pmode_id < 0) {
+ LTRACEF("we failed to find a pmode, exiting\n");
+ return ERR_NOT_FOUND;
+ }
+
+ /* allocate a resource */
+ err = allocate_2d_resource(gdev, &gdev->display_resource_id, gdev->pmode.r.width, gdev->pmode.r.height);
+ if (err < 0) {
+ LTRACEF("failed to allocate 2d resource\n");
+ return err;
+ }
+
+ /* attach a backing store to the resource */
+ size_t len = gdev->pmode.r.width * gdev->pmode.r.height * 4;
+ gdev->fb = pmm_alloc_kpages(ROUNDUP(len, PAGE_SIZE) / PAGE_SIZE, NULL);
+ if (!gdev->fb) {
+ TRACEF("failed to allocate framebuffer, wanted 0x%zx bytes\n", len);
+ return ERR_NO_MEMORY;
+ }
+
+ printf("virtio-gpu: framebuffer at %p, 0x%zx bytes\n", gdev->fb, len);
+
+ err = attach_backing(gdev, gdev->display_resource_id, gdev->fb, len);
+ if (err < 0) {
+ LTRACEF("failed to attach backing store\n");
+ return err;
+ }
+
+ /* attach this resource as a scanout */
+ err = set_scanout(gdev, gdev->pmode_id, gdev->display_resource_id, gdev->pmode.r.width, gdev->pmode.r.height);
+ if (err < 0) {
+ LTRACEF("failed to set scanout\n");
+ return err;
+ }
+
+ /* create the flush thread */
+ thread_t *t;
+ t = thread_create("virtio gpu flusher", &virtio_gpu_flush_thread, (void *)gdev, HIGH_PRIORITY, DEFAULT_STACK_SIZE);
+ thread_detach_and_resume(t);
+
+ /* kick it once */
+ event_signal(&gdev->flush_event, true);
+
+ LTRACE_EXIT;
+
+ return NO_ERROR;
+}
+
+
+static void dump_gpu_config(const volatile struct virtio_gpu_config *config)
+{
+ LTRACEF("events_read 0x%x\n", config->events_read);
+ LTRACEF("events_clear 0x%x\n", config->events_clear);
+ LTRACEF("num_scanouts 0x%x\n", config->num_scanouts);
+ LTRACEF("reserved 0x%x\n", config->reserved);
+}
+
+status_t virtio_gpu_init(struct virtio_device *dev, uint32_t host_features)
+{
+ LTRACEF("dev %p, host_features 0x%x\n", dev, host_features);
+
+ /* allocate a new gpu device */
+ struct virtio_gpu_dev *gdev = malloc(sizeof(struct virtio_gpu_dev));
+ if (!gdev)
+ return ERR_NO_MEMORY;
+
+ mutex_init(&gdev->lock);
+ event_init(&gdev->io_event, false, EVENT_FLAG_AUTOUNSIGNAL);
+ event_init(&gdev->flush_event, false, EVENT_FLAG_AUTOUNSIGNAL);
+
+ gdev->dev = dev;
+ dev->priv = gdev;
+
+ gdev->pmode_id = -1;
+ gdev->next_resource_id = 1;
+
+ /* allocate memory for a gpu request */
+#if WITH_KERNEL_VM
+ gdev->gpu_request = pmm_alloc_kpage();
+ gdev->gpu_request_phys = kvaddr_to_paddr(gdev->gpu_request);
+#else
+ gdev->gpu_request = malloc(sizeof(struct virtio_gpu_resp_display_info)); // XXX get size better
+ gdev->gpu_request_phys = (paddr_t)gdev->gpu_request;
+#endif
+
+ /* make sure the device is reset */
+ virtio_reset_device(dev);
+
+ volatile struct virtio_gpu_config *config = (struct virtio_gpu_config *)dev->config_ptr;
+ dump_gpu_config(config);
+
+ /* ack and set the driver status bit */
+ virtio_status_acknowledge_driver(dev);
+
+ // XXX check features bits and ack/nak them
+
+ /* allocate a virtio ring */
+ virtio_alloc_ring(dev, 0, 16);
+
+ /* set our irq handler */
+ dev->irq_driver_callback = &virtio_gpu_irq_driver_callback;
+ dev->config_change_callback = &virtio_gpu_config_change_callback;
+
+ /* set DRIVER_OK */
+ virtio_status_driver_ok(dev);
+
+ /* save the main device we've found */
+ the_gdev = gdev;
+
+ printf("found virtio gpu device\n");
+
+ return NO_ERROR;
+}
+
+static enum handler_return virtio_gpu_irq_driver_callback(struct virtio_device *dev, uint ring, const struct vring_used_elem *e)
+{
+ struct virtio_gpu_dev *gdev = (struct virtio_gpu_dev *)dev->priv;
+
+ LTRACEF("dev %p, ring %u, e %p, id %u, len %u\n", dev, ring, e, e->id, e->len);
+
+ /* parse our descriptor chain, add back to the free queue */
+ uint16_t i = e->id;
+ for (;;) {
+ int next;
+ struct vring_desc *desc = virtio_desc_index_to_desc(dev, ring, i);
+
+ //virtio_dump_desc(desc);
+
+ if (desc->flags & VRING_DESC_F_NEXT) {
+ next = desc->next;
+ } else {
+ /* end of chain */
+ next = -1;
+ }
+
+ virtio_free_desc(dev, ring, i);
+
+ if (next < 0)
+ break;
+ i = next;
+ }
+
+ /* signal our event */
+ event_signal(&gdev->io_event, false);
+
+ return INT_RESCHEDULE;
+}
+
+static enum handler_return virtio_gpu_config_change_callback(struct virtio_device *dev)
+{
+ struct virtio_gpu_dev *gdev = (struct virtio_gpu_dev *)dev->priv;
+
+ LTRACEF("gdev %p\n", gdev);
+
+ volatile struct virtio_gpu_config *config = (struct virtio_gpu_config *)dev->config_ptr;
+ dump_gpu_config(config);
+
+ return INT_RESCHEDULE;
+}
+
+static int virtio_gpu_flush_thread(void *arg)
+{
+ struct virtio_gpu_dev *gdev = (struct virtio_gpu_dev *)arg;
+ status_t err;
+
+ for (;;) {
+ event_wait(&gdev->flush_event);
+
+ /* transfer to host 2d */
+ err = transfer_to_host_2d(gdev, gdev->display_resource_id, gdev->pmode.r.width, gdev->pmode.r.height);
+ if (err < 0) {
+ LTRACEF("failed to flush resource\n");
+ continue;
+ }
+
+ /* resource flush */
+ err = flush_resource(gdev, gdev->display_resource_id, gdev->pmode.r.width, gdev->pmode.r.height);
+ if (err < 0) {
+ LTRACEF("failed to flush resource\n");
+ continue;
+ }
+ }
+
+ return 0;
+}
+
+void virtio_gpu_gfx_flush(uint starty, uint endy)
+{
+ event_signal(&the_gdev->flush_event, !arch_ints_disabled());
+}
+
+status_t display_get_info(struct display_info *info)
+{
+ memset(info, 0, sizeof(*info));
+
+ if (!the_gdev)
+ return ERR_NOT_FOUND;
+
+ info->framebuffer = the_gdev->fb;
+ info->format = GFX_FORMAT_RGB_x888;
+ info->width = the_gdev->pmode.r.width;
+ info->height = the_gdev->pmode.r.height;
+ info->stride = info->width;
+ info->flush = virtio_gpu_gfx_flush;
+
+ return NO_ERROR;
+}
+
+
diff --git a/src/bsp/lk/dev/virtio/gpu/virtio_gpu.h b/src/bsp/lk/dev/virtio/gpu/virtio_gpu.h
new file mode 100644
index 0000000..19ed6d2
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/gpu/virtio_gpu.h
@@ -0,0 +1,208 @@
+/*
+ * Virtio GPU Device
+ *
+ * Copyright Red Hat, Inc. 2013-2014
+ *
+ * Authors:
+ * Dave Airlie <airlied@redhat.com>
+ * Gerd Hoffmann <kraxel@redhat.com>
+ *
+ * This header is BSD licensed so anyone can use the definitions
+ * to implement compatible drivers/servers:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* taken from qemu sources */
+
+#ifndef VIRTIO_GPU_HW_H
+#define VIRTIO_GPU_HW_H
+
+#include <stdint.h>
+
+enum virtio_gpu_ctrl_type {
+ VIRTIO_GPU_UNDEFINED = 0,
+
+ /* 2d commands */
+ VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100,
+ VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
+ VIRTIO_GPU_CMD_RESOURCE_UNREF,
+ VIRTIO_GPU_CMD_SET_SCANOUT,
+ VIRTIO_GPU_CMD_RESOURCE_FLUSH,
+ VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
+ VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
+ VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING,
+
+ /* cursor commands */
+ VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300,
+ VIRTIO_GPU_CMD_MOVE_CURSOR,
+
+ /* success responses */
+ VIRTIO_GPU_RESP_OK_NODATA = 0x1100,
+ VIRTIO_GPU_RESP_OK_DISPLAY_INFO,
+
+ /* error responses */
+ VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200,
+ VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY,
+ VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID,
+ VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID,
+ VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID,
+ VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER,
+};
+
+#define VIRTIO_GPU_FLAG_FENCE (1 << 0)
+
+struct virtio_gpu_ctrl_hdr {
+ uint32_t type;
+ uint32_t flags;
+ uint64_t fence_id;
+ uint32_t ctx_id;
+ uint32_t padding;
+};
+
+/* data passed in the cursor vq */
+
+struct virtio_gpu_cursor_pos {
+ uint32_t scanout_id;
+ uint32_t x;
+ uint32_t y;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_CMD_UPDATE_CURSOR, VIRTIO_GPU_CMD_MOVE_CURSOR */
+struct virtio_gpu_update_cursor {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_cursor_pos pos; /* update & move */
+ uint32_t resource_id; /* update only */
+ uint32_t hot_x; /* update only */
+ uint32_t hot_y; /* update only */
+ uint32_t padding;
+};
+
+/* data passed in the control vq, 2d related */
+
+struct virtio_gpu_rect {
+ uint32_t x;
+ uint32_t y;
+ uint32_t width;
+ uint32_t height;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_UNREF */
+struct virtio_gpu_resource_unref {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t resource_id;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: create a 2d resource with a format */
+struct virtio_gpu_resource_create_2d {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t resource_id;
+ uint32_t format;
+ uint32_t width;
+ uint32_t height;
+};
+
+/* VIRTIO_GPU_CMD_SET_SCANOUT */
+struct virtio_gpu_set_scanout {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ uint32_t scanout_id;
+ uint32_t resource_id;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_FLUSH */
+struct virtio_gpu_resource_flush {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ uint32_t resource_id;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: simple transfer to_host */
+struct virtio_gpu_transfer_to_host_2d {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_rect r;
+ uint64_t offset;
+ uint32_t resource_id;
+ uint32_t padding;
+};
+
+struct virtio_gpu_mem_entry {
+ uint64_t addr;
+ uint32_t length;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING */
+struct virtio_gpu_resource_attach_backing {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t resource_id;
+ uint32_t nr_entries;
+};
+
+/* VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING */
+struct virtio_gpu_resource_detach_backing {
+ struct virtio_gpu_ctrl_hdr hdr;
+ uint32_t resource_id;
+ uint32_t padding;
+};
+
+/* VIRTIO_GPU_RESP_OK_DISPLAY_INFO */
+#define VIRTIO_GPU_MAX_SCANOUTS 16
+struct virtio_gpu_resp_display_info {
+ struct virtio_gpu_ctrl_hdr hdr;
+ struct virtio_gpu_display_one {
+ struct virtio_gpu_rect r;
+ uint32_t enabled;
+ uint32_t flags;
+ } pmodes[VIRTIO_GPU_MAX_SCANOUTS];
+};
+
+#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0)
+
+struct virtio_gpu_config {
+ uint32_t events_read;
+ uint32_t events_clear;
+ uint32_t num_scanouts;
+ uint32_t reserved;
+};
+
+/* simple formats for fbcon/X use */
+enum virtio_gpu_formats {
+ VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1,
+ VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2,
+ VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3,
+ VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4,
+
+ VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67,
+ VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68,
+
+ VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121,
+ VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134,
+};
+
+#endif
diff --git a/src/bsp/lk/dev/virtio/include/dev/virtio.h b/src/bsp/lk/dev/virtio/include/dev/virtio.h
new file mode 100644
index 0000000..3403602
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/include/dev/virtio.h
@@ -0,0 +1,87 @@
+/*
+ * 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 <compiler.h>
+#include <assert.h>
+#include <list.h>
+#include <sys/types.h>
+#include <dev/virtio/virtio_ring.h>
+
+/* detect a virtio mmio hardware block
+ * returns number of devices found */
+int virtio_mmio_detect(void *ptr, uint count, const uint irqs[]);
+
+#define MAX_VIRTIO_RINGS 4
+
+struct virtio_mmio_config;
+
+struct virtio_device {
+ bool valid;
+
+ uint index;
+ uint irq;
+
+ volatile struct virtio_mmio_config *mmio_config;
+ void *config_ptr;
+
+ void *priv; /* a place for the driver to put private data */
+
+ enum handler_return (*irq_driver_callback)(struct virtio_device *dev, uint ring, const struct vring_used_elem *e);
+ enum handler_return (*config_change_callback)(struct virtio_device *dev);
+
+ /* virtio rings */
+ uint32_t active_rings_bitmap;
+ struct vring ring[MAX_VIRTIO_RINGS];
+};
+
+void virtio_reset_device(struct virtio_device *dev);
+void virtio_status_acknowledge_driver(struct virtio_device *dev);
+void virtio_status_driver_ok(struct virtio_device *dev);
+
+/* api used by devices to interact with the virtio bus */
+status_t virtio_alloc_ring(struct virtio_device *dev, uint index, uint16_t len) __NONNULL();
+
+/* add a descriptor at index desc_index to the free list on ring_index */
+void virtio_free_desc(struct virtio_device *dev, uint ring_index, uint16_t desc_index);
+
+/* allocate a descriptor off the free list, 0xffff is error */
+uint16_t virtio_alloc_desc(struct virtio_device *dev, uint ring_index);
+
+/* allocate a descriptor chain the free list */
+struct vring_desc *virtio_alloc_desc_chain(struct virtio_device *dev, uint ring_index, size_t count, uint16_t *start_index);
+
+static inline struct vring_desc *virtio_desc_index_to_desc(struct virtio_device *dev, uint ring_index, uint16_t desc_index)
+{
+ DEBUG_ASSERT(desc_index != 0xffff);
+ return &dev->ring[ring_index].desc[desc_index];
+}
+
+void virtio_dump_desc(const struct vring_desc *desc);
+
+/* submit a chain to the avail list */
+void virtio_submit_chain(struct virtio_device *dev, uint ring_index, uint16_t desc_index);
+
+void virtio_kick(struct virtio_device *dev, uint ring_idnex);
+
+
diff --git a/src/bsp/lk/dev/virtio/include/dev/virtio/virtio_ring.h b/src/bsp/lk/dev/virtio/include/dev/virtio/virtio_ring.h
new file mode 100644
index 0000000..bf68bf9
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/include/dev/virtio/virtio_ring.h
@@ -0,0 +1,176 @@
+/* taken from linux source 3.15 at include/uapi/linux/virtio_ring.h */
+#ifndef _UAPI_LINUX_VIRTIO_RING_H
+#define _UAPI_LINUX_VIRTIO_RING_H
+/* An interface for efficient virtio implementation, currently for use by KVM
+ * and lguest, but hopefully others soon. Do NOT change this since it will
+ * break existing servers and clients.
+ *
+ * This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL IBM OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Copyright Rusty Russell IBM Corporation 2007. */
+#include <stdint.h>
+#include <pow2.h>
+
+/* This marks a buffer as continuing via the next field. */
+#define VRING_DESC_F_NEXT 1
+/* This marks a buffer as write-only (otherwise read-only). */
+#define VRING_DESC_F_WRITE 2
+/* This means the buffer contains a list of buffer descriptors. */
+#define VRING_DESC_F_INDIRECT 4
+
+/* The Host uses this in used->flags to advise the Guest: don't kick me when
+ * you add a buffer. It's unreliable, so it's simply an optimization. Guest
+ * will still kick if it's out of buffers. */
+#define VRING_USED_F_NO_NOTIFY 1
+/* The Guest uses this in avail->flags to advise the Host: don't interrupt me
+ * when you consume a buffer. It's unreliable, so it's simply an
+ * optimization. */
+#define VRING_AVAIL_F_NO_INTERRUPT 1
+
+/* We support indirect buffer descriptors */
+#define VIRTIO_RING_F_INDIRECT_DESC 28
+
+/* The Guest publishes the used index for which it expects an interrupt
+ * at the end of the avail ring. Host should ignore the avail->flags field. */
+/* The Host publishes the avail index for which it expects a kick
+ * at the end of the used ring. Guest should ignore the used->flags field. */
+#define VIRTIO_RING_F_EVENT_IDX 29
+
+/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */
+struct vring_desc {
+ /* Address (guest-physical). */
+ uint64_t addr;
+ /* Length. */
+ uint32_t len;
+ /* The flags as indicated above. */
+ uint16_t flags;
+ /* We chain unused descriptors via this, too */
+ uint16_t next;
+};
+
+struct vring_avail {
+ uint16_t flags;
+ uint16_t idx;
+ uint16_t ring[];
+};
+
+/* u32 is used here for ids for padding reasons. */
+struct vring_used_elem {
+ /* Index of start of used descriptor chain. */
+ uint32_t id;
+ /* Total length of the descriptor chain which was used (written to) */
+ uint32_t len;
+};
+
+struct vring_used {
+ uint16_t flags;
+ uint16_t idx;
+ struct vring_used_elem ring[];
+};
+
+struct vring {
+ uint32_t num;
+ uint32_t num_mask;
+
+ uint16_t free_list; /* head of a free list of descriptors per ring. 0xffff is NULL */
+ uint16_t free_count;
+
+ uint16_t last_used;
+
+ struct vring_desc *desc;
+
+ struct vring_avail *avail;
+
+ struct vring_used *used;
+};
+
+/* The standard layout for the ring is a continuous chunk of memory which looks
+ * like this. We assume num is a power of 2.
+ *
+ * struct vring
+ * {
+ * // The actual descriptors (16 bytes each)
+ * struct vring_desc desc[num];
+ *
+ * // A ring of available descriptor heads with free-running index.
+ * uint16_t avail_flags;
+ * uint16_t avail_idx;
+ * uint16_t available[num];
+ * uint16_t used_event_idx;
+ *
+ * // Padding to the next align boundary.
+ * char pad[];
+ *
+ * // A ring of used descriptor heads with free-running index.
+ * uint16_t used_flags;
+ * uint16_t used_idx;
+ * struct vring_used_elem used[num];
+ * uint16_t avail_event_idx;
+ * };
+ */
+/* We publish the used event index at the end of the available ring, and vice
+ * versa. They are at the end for backwards compatibility. */
+#define vring_used_event(vr) ((vr)->avail->ring[(vr)->num])
+#define vring_avail_event(vr) (*(uint16_t *)&(vr)->used->ring[(vr)->num])
+
+static inline void vring_init(struct vring *vr, unsigned int num, void *p,
+ unsigned long align)
+{
+ vr->num = num;
+ vr->num_mask = (1 << log2_uint(num)) - 1;
+ vr->free_list = 0xffff;
+ vr->free_count = 0;
+ vr->last_used = 0;
+ vr->desc = p;
+ vr->avail = p + num*sizeof(struct vring_desc);
+ vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + sizeof(uint16_t)
+ + align-1) & ~(align - 1));
+}
+
+static inline unsigned vring_size(unsigned int num, unsigned long align)
+{
+ return ((sizeof(struct vring_desc) * num + sizeof(uint16_t) * (3 + num)
+ + align - 1) & ~(align - 1))
+ + sizeof(uint16_t) * 3 + sizeof(struct vring_used_elem) * num;
+}
+
+/* The following is used with USED_EVENT_IDX and AVAIL_EVENT_IDX */
+/* Assuming a given event_idx value from the other size, if
+ * we have just incremented index from old to new_idx,
+ * should we trigger an event? */
+static inline int vring_need_event(uint16_t event_idx, uint16_t new_idx, uint16_t old)
+{
+ /* Note: Xen has similar logic for notification hold-off
+ * in include/xen/interface/io/ring.h with req_event and req_prod
+ * corresponding to event_idx + 1 and new_idx respectively.
+ * Note also that req_event and req_prod in Xen start at 1,
+ * event indexes in virtio start at 0. */
+ return (uint16_t)(new_idx - event_idx - 1) < (uint16_t)(new_idx - old);
+}
+
+#endif /* _UAPI_LINUX_VIRTIO_RING_H */
+
diff --git a/src/bsp/lk/dev/virtio/net/include/dev/virtio/net.h b/src/bsp/lk/dev/virtio/net/include/dev/virtio/net.h
new file mode 100644
index 0000000..48ac6a0
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/net/include/dev/virtio/net.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <compiler.h>
+#include <sys/types.h>
+#include <dev/virtio.h>
+
+status_t virtio_net_init(struct virtio_device *dev, uint32_t host_features) __NONNULL();
+status_t virtio_net_start(void);
+
+/* return the count of virtio interfaces found */
+int virtio_net_found(void);
+
+status_t virtio_net_get_mac_addr(uint8_t mac_addr[6]);
+
+struct pktbuf;
+extern status_t virtio_net_send_minip_pkt(struct pktbuf *p);
+
diff --git a/src/bsp/lk/dev/virtio/net/rules.mk b/src/bsp/lk/dev/virtio/net/rules.mk
new file mode 100644
index 0000000..2f782d2
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/net/rules.mk
@@ -0,0 +1,12 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/virtio-net.c
+
+MODULE_DEPS += \
+ dev/virtio \
+ lib/minip
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/virtio/net/virtio-net.c b/src/bsp/lk/dev/virtio/net/virtio-net.c
new file mode 100644
index 0000000..2d0102d
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/net/virtio-net.c
@@ -0,0 +1,473 @@
+/*
+ * 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 <dev/virtio/net.h>
+
+#include <debug.h>
+#include <assert.h>
+#include <trace.h>
+#include <compiler.h>
+#include <list.h>
+#include <string.h>
+#include <err.h>
+#include <kernel/thread.h>
+#include <kernel/event.h>
+#include <kernel/spinlock.h>
+#include <kernel/vm.h>
+#include <lib/pktbuf.h>
+#include <lib/minip.h>
+
+#define LOCAL_TRACE 0
+
+struct virtio_net_config {
+ uint8_t mac[6];
+ uint16_t status;
+ uint16_t max_virtqueue_pairs;
+} __PACKED;
+
+struct virtio_net_hdr {
+ uint8_t flags;
+ uint8_t gso_type;
+ uint16_t hdr_len;
+ uint16_t gso_size;
+ uint16_t csum_start;
+ uint16_t csum_offset;
+ uint16_t num_buffers; // unused in tx
+} __PACKED;
+
+#define VIRTIO_NET_F_CSUM (1<<0)
+#define VIRTIO_NET_F_GUEST_CSUM (1<<1)
+#define VIRTIO_NET_F_CTRL_GUEST_OFFLOADS (1<<2)
+#define VIRTIO_NET_F_MAC (1<<5)
+#define VIRTIO_NET_F_GSO (1<<6)
+#define VIRTIO_NET_F_GUEST_TSO4 (1<<7)
+#define VIRTIO_NET_F_GUEST_TSO6 (1<<8)
+#define VIRTIO_NET_F_GUEST_ECN (1<<9)
+#define VIRTIO_NET_F_GUEST_UFO (1<<10)
+#define VIRTIO_NET_F_HOST_TSO4 (1<<11)
+#define VIRTIO_NET_F_HOST_TSO6 (1<<12)
+#define VIRTIO_NET_F_HOST_ECN (1<<13)
+#define VIRTIO_NET_F_HOST_UFO (1<<14)
+#define VIRTIO_NET_F_MRG_RXBUF (1<<15)
+#define VIRTIO_NET_F_STATUS (1<<16)
+#define VIRTIO_NET_F_CTRL_VQ (1<<17)
+#define VIRTIO_NET_F_CTRL_RX (1<<18)
+#define VIRTIO_NET_F_CTRL_VLAN (1<<19)
+#define VIRTIO_NET_F_GUEST_ANNOUNCE (1<<21)
+#define VIRTIO_NET_F_MQ (1<<22)
+#define VIRTIO_NET_F_CTRL_MAC_ADDR (1<<23)
+
+#define VIRTIO_NET_S_LINK_UP (1<<0)
+#define VIRTIO_NET_S_ANNOUNCE (1<<1)
+
+#define TX_RING_SIZE 16
+#define RX_RING_SIZE 16
+
+#define RING_RX 0
+#define RING_TX 1
+
+#define VIRTIO_NET_MSS 1514
+
+struct virtio_net_dev {
+ struct virtio_device *dev;
+ bool started;
+
+ struct virtio_net_config *config;
+
+ spin_lock_t lock;
+ event_t rx_event;
+
+ /* list of active tx/rx packets to be freed at irq time */
+ pktbuf_t *pending_tx_packet[TX_RING_SIZE];
+ pktbuf_t *pending_rx_packet[RX_RING_SIZE];
+
+ uint tx_pending_count;
+ struct list_node completed_rx_queue;
+};
+
+static enum handler_return virtio_net_irq_driver_callback(struct virtio_device *dev, uint ring, const struct vring_used_elem *e);
+static int virtio_net_rx_worker(void *arg);
+static status_t virtio_net_queue_rx(struct virtio_net_dev *ndev, pktbuf_t *p);
+
+// XXX remove need for this
+static struct virtio_net_dev *the_ndev;
+
+static void dump_feature_bits(uint32_t feature)
+{
+ printf("virtio-net host features (0x%x):", feature);
+ if (feature & VIRTIO_NET_F_CSUM) printf(" CSUM");
+ if (feature & VIRTIO_NET_F_GUEST_CSUM) printf(" GUEST_CSUM");
+ if (feature & VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) printf(" CTRL_GUEST_OFFLOADS");
+ if (feature & VIRTIO_NET_F_MAC) printf(" MAC");
+ if (feature & VIRTIO_NET_F_GSO) printf(" GSO");
+ if (feature & VIRTIO_NET_F_GUEST_TSO4) printf(" GUEST_TSO4");
+ if (feature & VIRTIO_NET_F_GUEST_TSO6) printf(" GUEST_TSO6");
+ if (feature & VIRTIO_NET_F_GUEST_ECN) printf(" GUEST_ECN");
+ if (feature & VIRTIO_NET_F_GUEST_UFO) printf(" GUEST_UFO");
+ if (feature & VIRTIO_NET_F_HOST_TSO4) printf(" HOST_TSO4");
+ if (feature & VIRTIO_NET_F_HOST_TSO6) printf(" HOST_TSO6");
+ if (feature & VIRTIO_NET_F_HOST_ECN) printf(" HOST_ECN");
+ if (feature & VIRTIO_NET_F_HOST_UFO) printf(" HOST_UFO");
+ if (feature & VIRTIO_NET_F_MRG_RXBUF) printf(" MRG_RXBUF");
+ if (feature & VIRTIO_NET_F_STATUS) printf(" STATUS");
+ if (feature & VIRTIO_NET_F_CTRL_VQ) printf(" CTRL_VQ");
+ if (feature & VIRTIO_NET_F_CTRL_RX) printf(" CTRL_RX");
+ if (feature & VIRTIO_NET_F_CTRL_VLAN) printf(" CTRL_VLAN");
+ if (feature & VIRTIO_NET_F_GUEST_ANNOUNCE) printf(" GUEST_ANNOUNCE");
+ if (feature & VIRTIO_NET_F_MQ) printf(" MQ");
+ if (feature & VIRTIO_NET_F_CTRL_MAC_ADDR) printf(" CTRL_MAC_ADDR");
+ printf("\n");
+}
+
+status_t virtio_net_init(struct virtio_device *dev, uint32_t host_features)
+{
+ LTRACEF("dev %p, host_features 0x%x\n", dev, host_features);
+
+ /* allocate a new net device */
+ struct virtio_net_dev *ndev = calloc(1, sizeof(struct virtio_net_dev));
+ if (!ndev)
+ return ERR_NO_MEMORY;
+
+ ndev->dev = dev;
+ dev->priv = ndev;
+ ndev->started = false;
+
+ ndev->lock = SPIN_LOCK_INITIAL_VALUE;
+ event_init(&ndev->rx_event, false, EVENT_FLAG_AUTOUNSIGNAL);
+ list_initialize(&ndev->completed_rx_queue);
+
+ ndev->config = (struct virtio_net_config *)dev->config_ptr;
+
+ /* ack and set the driver status bit */
+ virtio_status_acknowledge_driver(dev);
+
+ // XXX check features bits and ack/nak them
+ dump_feature_bits(host_features);
+
+ /* set our irq handler */
+ dev->irq_driver_callback = &virtio_net_irq_driver_callback;
+
+ /* set DRIVER_OK */
+ virtio_status_driver_ok(dev);
+
+ /* allocate a pair of virtio rings */
+ virtio_alloc_ring(dev, RING_RX, RX_RING_SIZE); // rx
+ virtio_alloc_ring(dev, RING_TX, TX_RING_SIZE); // tx
+
+ the_ndev = ndev;
+
+ return NO_ERROR;
+}
+
+status_t virtio_net_start(void)
+{
+ if (the_ndev->started)
+ return ERR_ALREADY_STARTED;
+
+ the_ndev->started = true;
+
+ /* start the rx worker thread */
+ thread_resume(thread_create("virtio_net_rx", &virtio_net_rx_worker, (void *)the_ndev, HIGH_PRIORITY, DEFAULT_STACK_SIZE));
+
+ /* queue up a bunch of rxes */
+ for (uint i = 0; i < RX_RING_SIZE - 1; i++) {
+ pktbuf_t *p = pktbuf_alloc();
+ if (p) {
+ virtio_net_queue_rx(the_ndev, p);
+ }
+ }
+
+ return NO_ERROR;
+}
+
+static status_t virtio_net_queue_tx_pktbuf(struct virtio_net_dev *ndev, pktbuf_t *p2)
+{
+ struct virtio_device *vdev = ndev->dev;
+
+ uint16_t i;
+ pktbuf_t *p;
+
+ DEBUG_ASSERT(ndev);
+
+ p = pktbuf_alloc();
+ if (!p)
+ return ERR_NO_MEMORY;
+
+ /* point our header to the base of the first pktbuf */
+ struct virtio_net_hdr *hdr = pktbuf_append(p, sizeof(struct virtio_net_hdr) - 2);
+ memset(hdr, 0, p->dlen);
+
+ spin_lock_saved_state_t state;
+ spin_lock_irqsave(&ndev->lock, state);
+
+ /* only queue if we have enough tx descriptors */
+ if (ndev->tx_pending_count + 2 > TX_RING_SIZE)
+ goto nodesc;
+
+ /* allocate a chain of descriptors for our transfer */
+ struct vring_desc *desc = virtio_alloc_desc_chain(vdev, RING_TX, 2, &i);
+ if (!desc) {
+ spin_unlock_irqrestore(&ndev->lock, state);
+
+nodesc:
+ TRACEF("out of virtio tx descriptors, tx_pending_count %u\n", ndev->tx_pending_count);
+ pktbuf_free(p, true);
+
+ return ERR_NO_MEMORY;
+ }
+
+ ndev->tx_pending_count += 2;
+
+ /* save a pointer to our pktbufs for the irq handler to free */
+ LTRACEF("saving pointer to pkt in index %u and %u\n", i, desc->next);
+ DEBUG_ASSERT(ndev->pending_tx_packet[i] == NULL);
+ DEBUG_ASSERT(ndev->pending_tx_packet[desc->next] == NULL);
+ ndev->pending_tx_packet[i] = p;
+ ndev->pending_tx_packet[desc->next] = p2;
+
+ /* set up the descriptor pointing to the header */
+ desc->addr = pktbuf_data_phys(p);
+ desc->len = p->dlen;
+ desc->flags |= VRING_DESC_F_NEXT;
+
+ /* set up the descriptor pointing to the buffer */
+ desc = virtio_desc_index_to_desc(vdev, RING_TX, desc->next);
+ desc->addr = pktbuf_data_phys(p2);
+ desc->len = p2->dlen;
+ desc->flags = 0;
+
+ /* submit the transfer */
+ virtio_submit_chain(vdev, RING_TX, i);
+
+ /* kick it off */
+ virtio_kick(vdev, RING_TX);
+
+ spin_unlock_irqrestore(&ndev->lock, state);
+
+ return NO_ERROR;
+}
+
+/* variant of the above function that copies the buffer into a pktbuf before sending */
+static status_t virtio_net_queue_tx(struct virtio_net_dev *ndev, const void *buf, size_t len)
+{
+ DEBUG_ASSERT(ndev);
+ DEBUG_ASSERT(buf);
+
+ pktbuf_t *p = pktbuf_alloc();
+ if (!p)
+ return ERR_NO_MEMORY;
+
+ /* copy the outgoing packet into the pktbuf */
+ p->data = p->buffer;
+ p->dlen = len;
+ memcpy(p->data, buf, len);
+
+ /* call through to the variant of the function that takes a pre-populated pktbuf */
+ status_t err = virtio_net_queue_tx_pktbuf(ndev, p);
+ if (err < 0) {
+ pktbuf_free(p, true);
+ }
+
+ return err;
+}
+
+static status_t virtio_net_queue_rx(struct virtio_net_dev *ndev, pktbuf_t *p)
+{
+ struct virtio_device *vdev = ndev->dev;
+
+ DEBUG_ASSERT(ndev);
+ DEBUG_ASSERT(p);
+
+ /* point our header to the base of the pktbuf */
+ p->data = p->buffer;
+ struct virtio_net_hdr *hdr = (struct virtio_net_hdr *)p->data;
+ memset(hdr, 0, sizeof(struct virtio_net_hdr) - 2);
+
+ p->dlen = sizeof(struct virtio_net_hdr) - 2 + VIRTIO_NET_MSS;
+
+ spin_lock_saved_state_t state;
+ spin_lock_irqsave(&ndev->lock, state);
+
+ /* allocate a chain of descriptors for our transfer */
+ uint16_t i;
+ struct vring_desc *desc = virtio_alloc_desc_chain(vdev, RING_RX, 1, &i);
+ DEBUG_ASSERT(desc); /* shouldn't be possible not to have a descriptor ready */
+
+ /* save a pointer to our pktbufs for the irq handler to use */
+ DEBUG_ASSERT(ndev->pending_rx_packet[i] == NULL);
+ ndev->pending_rx_packet[i] = p;
+
+ /* set up the descriptor pointing to the header */
+ desc->addr = pktbuf_data_phys(p);
+ desc->len = p->dlen;
+ desc->flags = VRING_DESC_F_WRITE;
+
+ /* submit the transfer */
+ virtio_submit_chain(vdev, RING_RX, i);
+
+ /* kick it off */
+ virtio_kick(vdev, RING_RX);
+
+ spin_unlock_irqrestore(&ndev->lock, state);
+
+ return NO_ERROR;
+}
+
+static enum handler_return virtio_net_irq_driver_callback(struct virtio_device *dev, uint ring, const struct vring_used_elem *e)
+{
+ struct virtio_net_dev *ndev = (struct virtio_net_dev *)dev->priv;
+
+ LTRACEF("dev %p, ring %u, e %p, id %u, len %u\n", dev, ring, e, e->id, e->len);
+
+ spin_lock(&ndev->lock);
+
+ /* parse our descriptor chain, add back to the free queue */
+ uint16_t i = e->id;
+ for (;;) {
+ int next;
+ struct vring_desc *desc = virtio_desc_index_to_desc(dev, ring, i);
+
+ if (desc->flags & VRING_DESC_F_NEXT) {
+ next = desc->next;
+ } else {
+ /* end of chain */
+ next = -1;
+ }
+
+ virtio_free_desc(dev, ring, i);
+
+ if (ring == RING_RX) {
+ /* put the freed rx buffer in a queue */
+ pktbuf_t *p = ndev->pending_rx_packet[i];
+ ndev->pending_rx_packet[i] = NULL;
+
+ DEBUG_ASSERT(p);
+ LTRACEF("rx pktbuf %p filled\n", p);
+
+ /* trim the pktbuf according to the written length in the used element descriptor */
+ if (e->len > (sizeof(struct virtio_net_hdr) - 2 + VIRTIO_NET_MSS)) {
+ TRACEF("bad used len on RX %u\n", e->len);
+ p->dlen = 0;
+ } else {
+ p->dlen = e->len;
+ }
+
+ list_add_tail(&ndev->completed_rx_queue, &p->list);
+ } else { // ring == RING_TX
+ /* free the pktbuf associated with the tx packet we just consumed */
+ pktbuf_t *p = ndev->pending_tx_packet[i];
+ ndev->pending_tx_packet[i] = NULL;
+ ndev->tx_pending_count--;
+
+ DEBUG_ASSERT(p);
+ LTRACEF("freeing pktbuf %p\n", p);
+
+ pktbuf_free(p, false);
+ }
+
+ if (next < 0)
+ break;
+ i = next;
+ }
+
+ spin_unlock(&ndev->lock);
+
+ /* if rx ring, signal our event */
+ if (ring == 0) {
+ event_signal(&ndev->rx_event, false);
+ }
+
+ return INT_RESCHEDULE;
+}
+
+static int virtio_net_rx_worker(void *arg)
+{
+ struct virtio_net_dev *ndev = (struct virtio_net_dev *)arg;
+
+ for (;;) {
+ event_wait(&ndev->rx_event);
+
+ /* pull some packets from the received queue */
+ for (;;) {
+ spin_lock_saved_state_t state;
+ spin_lock_irqsave(&ndev->lock, state);
+
+ pktbuf_t *p = list_remove_head_type(&ndev->completed_rx_queue, pktbuf_t, list);
+
+ spin_unlock_irqrestore(&ndev->lock, state);
+
+ if (!p)
+ break; /* nothing left in the queue, go back to waiting */
+
+ LTRACEF("got packet len %u\n", p->dlen);
+
+ /* process our packet */
+ struct virtio_net_hdr *hdr = pktbuf_consume(p, sizeof(struct virtio_net_hdr) - 2);
+ if (hdr) {
+ /* call up into the stack */
+ minip_rx_driver_callback(p);
+ }
+
+ /* requeue the pktbuf in the rx queue */
+ virtio_net_queue_rx(ndev, p);
+ }
+ }
+ return 0;
+}
+
+int virtio_net_found(void)
+{
+ return the_ndev ? 1 : 0;
+}
+
+status_t virtio_net_get_mac_addr(uint8_t mac_addr[6])
+{
+ if (!the_ndev)
+ return ERR_NOT_FOUND;
+
+ memcpy(mac_addr, the_ndev->config->mac, 6);
+
+ return NO_ERROR;
+}
+
+status_t virtio_net_send_minip_pkt(pktbuf_t *p)
+{
+ LTRACEF("p %p, dlen %u, flags 0x%x\n", p, p->dlen, p->flags);
+
+ DEBUG_ASSERT(p && p->dlen);
+
+ if ((p->flags & PKTBUF_FLAG_EOF) == 0) {
+ /* can't handle multi part packets yet */
+ PANIC_UNIMPLEMENTED;
+
+ return ERR_NOT_IMPLEMENTED;
+ }
+
+ /* hand the pktbuf off to the nic, it owns the pktbuf from now on out unless it fails */
+ status_t err = virtio_net_queue_tx_pktbuf(the_ndev, p);
+ if (err < 0) {
+ pktbuf_free(p, true);
+ }
+
+ return err;
+}
+
diff --git a/src/bsp/lk/dev/virtio/rules.mk b/src/bsp/lk/dev/virtio/rules.mk
new file mode 100644
index 0000000..22c8c0b
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/rules.mk
@@ -0,0 +1,8 @@
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MODULE_SRCS += \
+ $(LOCAL_DIR)/virtio.c
+
+include make/module.mk
diff --git a/src/bsp/lk/dev/virtio/virtio.c b/src/bsp/lk/dev/virtio/virtio.c
new file mode 100644
index 0000000..5449834
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/virtio.c
@@ -0,0 +1,414 @@
+/*
+ * 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 <dev/virtio.h>
+#include <dev/virtio/virtio_ring.h>
+
+#include <debug.h>
+#include <assert.h>
+#include <trace.h>
+#include <compiler.h>
+#include <list.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pow2.h>
+#include <lk/init.h>
+#include <kernel/thread.h>
+#include <kernel/vm.h>
+#include <platform/interrupts.h>
+
+#include "virtio_priv.h"
+
+#if WITH_DEV_VIRTIO_BLOCK
+#include <dev/virtio/block.h>
+#endif
+#if WITH_DEV_VIRTIO_NET
+#include <dev/virtio/net.h>
+#endif
+#if WITH_DEV_VIRTIO_GPU
+#include <dev/virtio/gpu.h>
+#endif
+
+#define LOCAL_TRACE 0
+
+static struct virtio_device *devices;
+
+static void dump_mmio_config(const volatile struct virtio_mmio_config *mmio)
+{
+ printf("mmio at %p\n", mmio);
+ printf("\tmagic 0x%x\n", mmio->magic);
+ printf("\tversion 0x%x\n", mmio->version);
+ printf("\tdevice_id 0x%x\n", mmio->device_id);
+ printf("\tvendor_id 0x%x\n", mmio->vendor_id);
+ printf("\thost_features 0x%x\n", mmio->host_features);
+ printf("\tguest_page_size %u\n", mmio->guest_page_size);
+ printf("\tqnum %u\n", mmio->queue_num);
+ printf("\tqnum_max %u\n", mmio->queue_num_max);
+ printf("\tqnum_align %u\n", mmio->queue_align);
+ printf("\tqnum_pfn %u\n", mmio->queue_pfn);
+ printf("\tstatus 0x%x\n", mmio->status);
+}
+
+void virtio_dump_desc(const struct vring_desc *desc)
+{
+ printf("vring descriptor %p\n", desc);
+ printf("\taddr 0x%llx\n", desc->addr);
+ printf("\tlen 0x%x\n", desc->len);
+ printf("\tflags 0x%hhx\n", desc->flags);
+ printf("\tnext 0x%hhx\n", desc->next);
+}
+
+static enum handler_return virtio_mmio_irq(void *arg)
+{
+ struct virtio_device *dev = (struct virtio_device *)arg;
+ LTRACEF("dev %p, index %u\n", dev, dev->index);
+
+ uint32_t irq_status = dev->mmio_config->interrupt_status;
+ LTRACEF("status 0x%x\n", irq_status);
+
+ enum handler_return ret = INT_NO_RESCHEDULE;
+ if (irq_status & 0x1) { /* used ring update */
+ // XXX is this safe?
+ dev->mmio_config->interrupt_ack = 0x1;
+
+ /* cycle through all the active rings */
+ for (uint r = 0; r < MAX_VIRTIO_RINGS; r++) {
+ if ((dev->active_rings_bitmap & (1<<r)) == 0)
+ continue;
+
+ struct vring *ring = &dev->ring[r];
+ LTRACEF("ring %u: used flags 0x%hhx idx 0x%hhx last_used %u\n", r, ring->used->flags, ring->used->idx, ring->last_used);
+
+ uint cur_idx = ring->used->idx;
+ for (uint i = ring->last_used; i != (cur_idx & ring->num_mask); i = (i + 1) & ring->num_mask) {
+ LTRACEF("looking at idx %u\n", i);
+
+ // process chain
+ struct vring_used_elem *used_elem = &ring->used->ring[i];
+ LTRACEF("id %u, len %u\n", used_elem->id, used_elem->len);
+
+ DEBUG_ASSERT(dev->irq_driver_callback);
+ ret |= dev->irq_driver_callback(dev, r, used_elem);
+
+ ring->last_used = (ring->last_used + 1) & ring->num_mask;
+ }
+ }
+ }
+ if (irq_status & 0x2) { /* config change */
+ dev->mmio_config->interrupt_ack = 0x2;
+
+ if (dev->config_change_callback) {
+ ret |= dev->config_change_callback(dev);
+ }
+ }
+
+ LTRACEF("exiting irq\n");
+
+ return ret;
+}
+
+int virtio_mmio_detect(void *ptr, uint count, const uint irqs[])
+{
+ LTRACEF("ptr %p, count %u\n", ptr, count);
+
+ DEBUG_ASSERT(ptr);
+ DEBUG_ASSERT(irqs);
+ DEBUG_ASSERT(!devices);
+
+ /* allocate an array big enough to hold a list of devices */
+ devices = calloc(count, sizeof(struct virtio_device));
+ if (!devices)
+ return ERR_NO_MEMORY;
+
+ int found = 0;
+ for (uint i = 0; i < count; i++) {
+ volatile struct virtio_mmio_config *mmio = (struct virtio_mmio_config *)((uint8_t *)ptr + i * 0x200);
+ struct virtio_device *dev = &devices[i];
+
+ dev->index = i;
+ dev->irq = irqs[i];
+
+ mask_interrupt(irqs[i]);
+ register_int_handler(irqs[i], &virtio_mmio_irq, (void *)dev);
+
+ LTRACEF("looking at magic 0x%x version 0x%x did 0x%x vid 0x%x\n",
+ mmio->magic, mmio->version, mmio->device_id, mmio->vendor_id);
+
+ if (mmio->magic != VIRTIO_MMIO_MAGIC) {
+ continue;
+ }
+
+#if LOCAL_TRACE
+ if (mmio->device_id != 0) {
+ dump_mmio_config(mmio);
+ }
+#endif
+
+#if WITH_DEV_VIRTIO_BLOCK
+ if (mmio->device_id == 2) { // block device
+ LTRACEF("found block device\n");
+
+ dev->mmio_config = mmio;
+ dev->config_ptr = (void *)mmio->config;
+
+ status_t err = virtio_block_init(dev, mmio->host_features);
+ if (err >= 0) {
+ // good device
+ dev->valid = true;
+
+ if (dev->irq_driver_callback)
+ unmask_interrupt(dev->irq);
+
+ // XXX quick test code, remove
+#if 0
+ uint8_t buf[512];
+ memset(buf, 0x99, sizeof(buf));
+ virtio_block_read_write(dev, buf, 0, sizeof(buf), false);
+ hexdump8_ex(buf, sizeof(buf), 0);
+
+ buf[0]++;
+ virtio_block_read_write(dev, buf, 0, sizeof(buf), true);
+
+ virtio_block_read_write(dev, buf, 0, sizeof(buf), false);
+ hexdump8_ex(buf, sizeof(buf), 0);
+#endif
+ }
+
+ }
+#endif // WITH_DEV_VIRTIO_BLOCK
+#if WITH_DEV_VIRTIO_NET
+ if (mmio->device_id == 1) { // network device
+ LTRACEF("found net device\n");
+
+ dev->mmio_config = mmio;
+ dev->config_ptr = (void *)mmio->config;
+
+ status_t err = virtio_net_init(dev, mmio->host_features);
+ if (err >= 0) {
+ // good device
+ dev->valid = true;
+
+ if (dev->irq_driver_callback)
+ unmask_interrupt(dev->irq);
+ }
+ }
+#endif // WITH_DEV_VIRTIO_NET
+#if WITH_DEV_VIRTIO_GPU
+ if (mmio->device_id == 0x10) { // virtio-gpu
+ LTRACEF("found gpu device\n");
+
+ dev->mmio_config = mmio;
+ dev->config_ptr = (void *)mmio->config;
+
+ status_t err = virtio_gpu_init(dev, mmio->host_features);
+ if (err >= 0) {
+ // good device
+ dev->valid = true;
+
+ if (dev->irq_driver_callback)
+ unmask_interrupt(dev->irq);
+
+ virtio_gpu_start(dev);
+ }
+ }
+#endif // WITH_DEV_VIRTIO_GPU
+
+ if (dev->valid)
+ found++;
+ }
+
+ return found;
+}
+
+void virtio_free_desc(struct virtio_device *dev, uint ring_index, uint16_t desc_index)
+{
+ LTRACEF("dev %p ring %u index %u free_count %u\n", dev, ring_index, desc_index, dev->ring[ring_index].free_count);
+ dev->ring[ring_index].desc[desc_index].next = dev->ring[ring_index].free_list;
+ dev->ring[ring_index].free_list = desc_index;
+ dev->ring[ring_index].free_count++;
+}
+
+uint16_t virtio_alloc_desc(struct virtio_device *dev, uint ring_index)
+{
+ if (dev->ring[ring_index].free_count == 0)
+ return 0xffff;
+
+ DEBUG_ASSERT(dev->ring[ring_index].free_list != 0xffff);
+
+ uint16_t i = dev->ring[ring_index].free_list;
+ struct vring_desc *desc = &dev->ring[ring_index].desc[i];
+ dev->ring[ring_index].free_list = desc->next;
+
+ dev->ring[ring_index].free_count--;
+
+ return i;
+}
+
+struct vring_desc *virtio_alloc_desc_chain(struct virtio_device *dev, uint ring_index, size_t count, uint16_t *start_index)
+{
+ if (dev->ring[ring_index].free_count < count)
+ return NULL;
+
+ /* start popping entries off the chain */
+ struct vring_desc *last = 0;
+ uint16_t last_index = 0;
+ while (count > 0) {
+ uint16_t i = dev->ring[ring_index].free_list;
+ struct vring_desc *desc = &dev->ring[ring_index].desc[i];
+
+ dev->ring[ring_index].free_list = desc->next;
+ dev->ring[ring_index].free_count--;
+
+ if (last) {
+ desc->flags = VRING_DESC_F_NEXT;
+ desc->next = last_index;
+ } else {
+ // first one
+ desc->flags = 0;
+ desc->next = 0;
+ }
+ last = desc;
+ last_index = i;
+ count--;
+ }
+
+ if (start_index)
+ *start_index = last_index;
+
+ return last;
+}
+
+void virtio_submit_chain(struct virtio_device *dev, uint ring_index, uint16_t desc_index)
+{
+ LTRACEF("dev %p, ring %u, desc %u\n", dev, ring_index, desc_index);
+
+ /* add the chain to the available list */
+ struct vring_avail *avail = dev->ring[ring_index].avail;
+
+ avail->ring[avail->idx & dev->ring[ring_index].num_mask] = desc_index;
+ DSB;
+ avail->idx++;
+
+#if LOCAL_TRACE
+ hexdump(avail, 16);
+#endif
+}
+
+void virtio_kick(struct virtio_device *dev, uint ring_index)
+{
+ LTRACEF("dev %p, ring %u\n", dev, ring_index);
+
+ dev->mmio_config->queue_notify = ring_index;
+ DSB;
+}
+
+status_t virtio_alloc_ring(struct virtio_device *dev, uint index, uint16_t len)
+{
+ LTRACEF("dev %p, index %u, len %u\n", dev, index, len);
+
+ DEBUG_ASSERT(dev);
+ DEBUG_ASSERT(len > 0 && ispow2(len));
+ DEBUG_ASSERT(index < MAX_VIRTIO_RINGS);
+
+ if (len == 0 || !ispow2(len))
+ return ERR_INVALID_ARGS;
+
+ struct vring *ring = &dev->ring[index];
+
+ /* allocate a ring */
+ size_t size = vring_size(len, PAGE_SIZE);
+ LTRACEF("need %zu bytes\n", size);
+
+#if WITH_KERNEL_VM
+ void *vptr;
+ status_t err = vmm_alloc_contiguous(vmm_get_kernel_aspace(), "virtio_ring", size, &vptr, 0, 0, ARCH_MMU_FLAG_UNCACHED_DEVICE);
+ if (err < 0)
+ return ERR_NO_MEMORY;
+
+ LTRACEF("allocated virtio_ring at va %p\n", vptr);
+
+ /* compute the physical address */
+ paddr_t pa;
+ err = arch_mmu_query((vaddr_t)vptr, &pa, NULL);
+ if (err < 0) {
+ return ERR_NO_MEMORY;
+ }
+
+ LTRACEF("virtio_ring at pa 0x%lx\n", pa);
+#else
+ void *vptr = memalign(PAGE_SIZE, size);
+ if (!vptr)
+ return ERR_NO_MEMORY;
+
+ LTRACEF("ptr %p\n", vptr);
+ memset(vptr, 0, size);
+
+ /* compute the physical address */
+ paddr_t pa = (paddr_t)vptr;
+#endif
+
+ /* initialize the ring */
+ vring_init(ring, len, vptr, PAGE_SIZE);
+ dev->ring[index].free_list = 0xffff;
+ dev->ring[index].free_count = 0;
+
+ /* add all the descriptors to the free list */
+ for (uint i = 0; i < len; i++) {
+ virtio_free_desc(dev, index, i);
+ }
+
+ /* register the ring with the device */
+ DEBUG_ASSERT(dev->mmio_config);
+ dev->mmio_config->guest_page_size = PAGE_SIZE;
+ dev->mmio_config->queue_sel = index;
+ dev->mmio_config->queue_num = len;
+ dev->mmio_config->queue_align = PAGE_SIZE;
+ dev->mmio_config->queue_pfn = pa / PAGE_SIZE;
+
+ /* mark the ring active */
+ dev->active_rings_bitmap |= (1 << index);
+
+ return NO_ERROR;
+}
+
+void virtio_reset_device(struct virtio_device *dev)
+{
+ dev->mmio_config->status = 0;
+}
+
+void virtio_status_acknowledge_driver(struct virtio_device *dev)
+{
+ dev->mmio_config->status |= VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER;
+}
+
+void virtio_status_driver_ok(struct virtio_device *dev)
+{
+ dev->mmio_config->status |= VIRTIO_STATUS_DRIVER_OK;
+}
+
+void virtio_init(uint level)
+{
+}
+
+LK_INIT_HOOK(virtio, &virtio_init, LK_INIT_LEVEL_THREADING);
+
diff --git a/src/bsp/lk/dev/virtio/virtio_priv.h b/src/bsp/lk/dev/virtio/virtio_priv.h
new file mode 100644
index 0000000..28437c8
--- /dev/null
+++ b/src/bsp/lk/dev/virtio/virtio_priv.h
@@ -0,0 +1,65 @@
+/*
+ * 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 <compiler.h>
+#include <stdint.h>
+
+struct virtio_mmio_config {
+/* 0x00 */ uint32_t magic;
+ uint32_t version;
+ uint32_t device_id;
+ uint32_t vendor_id;
+/* 0x10 */ uint32_t host_features;
+ uint32_t host_features_sel;
+ uint32_t __reserved0[2];
+/* 0x20 */ uint32_t guest_features;
+ uint32_t guest_features_sel;
+ uint32_t guest_page_size;
+ uint32_t __reserved1[1];
+/* 0x30 */ uint32_t queue_sel;
+ uint32_t queue_num_max;
+ uint32_t queue_num;
+ uint32_t queue_align;
+/* 0x40 */ uint32_t queue_pfn;
+ uint32_t __reserved2[3];
+/* 0x50 */ uint32_t queue_notify;
+ uint32_t __reserved3[3];
+/* 0x60 */ uint32_t interrupt_status;
+ uint32_t interrupt_ack;
+ uint32_t __reserved4[2];
+/* 0x70 */ uint32_t status;
+ uint8_t __reserved5[0x8c];
+/* 0x100 */ uint32_t config[0];
+};
+
+STATIC_ASSERT(sizeof(struct virtio_mmio_config) == 0x100);
+
+#define VIRTIO_MMIO_MAGIC 0x74726976 // 'virt'
+
+#define VIRTIO_STATUS_ACKNOWLEDGE (1<<0)
+#define VIRTIO_STATUS_DRIVER (1<<1)
+#define VIRTIO_STATUS_DRIVER_OK (1<<2)
+#define VIRTIO_STATUS_FEATURES_OK (1<<3)
+#define VIRTIO_STATUS_DEVICE_NEEDS_RESET (1<<6)
+#define VIRTIO_STATUS_FAILED (1<<7)