diff --git a/NOTES b/NOTES
new file mode 100644
index 0000000000000000000000000000000000000000..429d9f4ce8a6ae9e20d4456325a5d7c206c8c6fe
--- /dev/null
+++ b/NOTES
@@ -0,0 +1,27 @@
+General
+=======
+
+Patches and improvements are always welcome.
+Please consider that I barely had 3 months to prepare all of this from start to
+finish, so some things may not be particularly smart or pretty, or even
+complete.
+
+
+MPPB (v2)
+=======
+
+* NoC DMA channels silently fail and can not be reset when source and
+  destination of a transfer falls programmed within the same memory
+  (this may not be true, I have not tried programming zero-sized transfers yet)
+
+* There is no atomic mechanism that protects against concurrent programming
+  of NoC DMA channels. This means that a Xentium needs to either reserve one
+  or more channels for a time, or all channels must be assigned beforehand 
+
+* there is no MMU
+
+Xentium
+=======
+
+* Xentium-Clang cannot produce position independent executables, which makes
+  memory management less comfortable
diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig
index 8a327cc3b73a5593e2e9689d38c87eb077238583..71857b9b20fb9137489de75438d5e4dc2d8f3838 100644
--- a/arch/sparc/Kconfig
+++ b/arch/sparc/Kconfig
@@ -2,6 +2,22 @@ menu "SPARC Configuration"
 
 ### Arch specific settings
 
+choice 
+	prompt "Select CPU type"
+	default LEON3
+
+config LEON2
+	bool "LEON2"
+	help
+	 Configure for LEON2. 
+
+config LEON3
+	bool "LEON3"
+	help
+	 Configure for LEON2. 
+
+endchoice
+
 
 config MMU
 	bool "Enable SPARC Reference MMU support"
diff --git a/arch/sparc/include/asm/irq.h b/arch/sparc/include/asm/irq.h
new file mode 100644
index 0000000000000000000000000000000000000000..4c3394ec03ebd370e0f4f6c682aca77325864192
--- /dev/null
+++ b/arch/sparc/include/asm/irq.h
@@ -0,0 +1,28 @@
+/**
+ * @file arch/sparc/include/irq.h
+ */
+
+#ifndef _SPARC_IRQ_H_
+#define _SPARC_IRQ_H_
+
+#include <asm/leon_reg.h>
+
+
+void leon_irq_init(void);
+
+
+/* in the LEON3, interrupts 1-15 are primary, 16-31 are extended */
+#ifdef CONFIG_LEON3
+#define LEON_WANT_EIRQ(x) (x)
+#define LEON_REAL_EIRQ(x) (x)
+#endif /* CONFIG_LEON3 */
+
+/* in the LEON2, interrupts 1-15 are primary, 0-31 are extended, we treat them
+ * as IRLs 16...n */
+#ifdef CONFIG_LEON2
+#define LEON_WANT_EIRQ(x) (LEON2_IRL_SIZE + (x))
+#define LEON_REAL_EIRQ(x) ((x) - LEON2_IRL_SIZE)
+#endif /* CONFIG_LEON2 */
+
+
+#endif /* _SPARC_IRQ_H_ */
diff --git a/arch/sparc/include/asm/leon_reg.h b/arch/sparc/include/asm/leon_reg.h
new file mode 100644
index 0000000000000000000000000000000000000000..92ed092cf8d691af7a28dde819ae4d86fb21e9e6
--- /dev/null
+++ b/arch/sparc/include/asm/leon_reg.h
@@ -0,0 +1,190 @@
+/**
+ * @file arch/sparc/include/asm/leon_reg.h
+ *
+ * @author  Armin Luntzer (armin.luntzer@univie.ac.at)
+ * @date    2015
+ *
+ * @copyright GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ *
+ */
+
+#ifndef _SPARC_ASM_LEON_REG_H_
+#define _SPARC_ASM_LEON_REG_H_
+
+#include <kernel/types.h>
+
+
+
+#define LEON3_BASE_ADDRESS_APB		0x80000000
+
+#define LEON3_BASE_ADDRESS_FTMCTRL	0x80000000
+#define LEON3_BASE_ADDRESS_APBUART	0x80000100
+#define LEON3_BASE_ADDRESS_IRQMP	0x80000200
+#define LEON3_BASE_ADDRESS_GPTIMER	0x80000300
+#define LEON3_BASE_ADDRESS_GRGPIO_1	0x80000900
+#define LEON3_BASE_ADDRESS_GRGPIO_2	0x80000A00
+#define LEON3_BASE_ADDRESS_AHBSTAT	0x80000F00
+#define LEON3_BASE_ADDRESS_GRTIMER	0x80100600
+
+#define LEON3_IRL_SIZE	16 /* number of interrupts on primary */
+#define LEON3_EIRL_SIZE	16 /* number of interrupts on extended */
+
+
+
+
+
+#define LEON2_BASE_ADDRESS_IRQ		0x80000090
+#define LEON2_BASE_ADDRESS_EIRQ		0x800000B0
+
+#define LEON2_IRL_SIZE	16 /* number of interrupts on primary */
+#define LEON2_EIRL_SIZE	32 /* number of interrupts on extended */
+
+
+
+struct leon2_irqctrl_registermap {
+        uint32_t irq_mask;
+        uint32_t irq_pending;
+        uint32_t irq_force;
+        uint32_t irq_clear;
+};
+
+struct leon2_eirqctrl_registermap {
+        uint32_t eirq_mask;
+        uint32_t eirq_pending;
+        uint32_t eirq_status;
+        uint32_t eirq_clear;
+};
+
+
+
+
+
+/* FTMCTRL Memory Controller Registers [p. 44] */
+
+struct leon3_ftmctrl_registermap {
+	 uint32_t mcfg1;
+	 uint32_t mcfg2;
+	 uint32_t mcfg3;
+};
+
+struct leon3_apbuart_registermap {
+	 uint32_t data;
+	 uint32_t status;
+	 uint32_t ctrl;
+	 uint32_t scaler;
+};
+
+struct leon3_grgpio_registermap {
+	uint32_t ioport_data;
+	uint32_t ioport_output_value;
+	uint32_t ioport_direction;
+	uint32_t irq_mask;
+	uint32_t irq_polarity;
+	uint32_t irq_edge;
+};
+
+struct leon3_irqctrl_registermap {
+	uint32_t irq_level;			/* 0x00	*/
+	uint32_t irq_pending;			/* 0x04 */
+	uint32_t irq_force;			/* 0x08 */
+	uint32_t irq_clear;			/* 0x0C */
+	uint32_t mp_status;			/* 0x10 */
+	uint32_t mp_broadcast;			/* 0x14 */
+	uint32_t unused01;			/* 0x18 */
+	uint32_t unused02;			/* 0x1C */
+	uint32_t unused03;			/* 0x20 */
+	uint32_t unused04;			/* 0x24 */
+	uint32_t unused05;			/* 0x28 */
+	uint32_t unused06;			/* 0x2C */
+	uint32_t unused07;			/* 0x30 */
+	uint32_t unused08;			/* 0x34 */
+	uint32_t unused09;			/* 0x38 */
+	uint32_t unused10;			/* 0x3C */
+	uint32_t irq_mpmask[2];			/* 0x40 CPU 0 */
+						/* 0x44 CPU 1 */
+	uint32_t unused11;			/* 0x48 */
+	uint32_t unused12;			/* 0x4C */
+	uint32_t unused13;			/* 0x50 */
+	uint32_t unused14;			/* 0x54 */
+	uint32_t unused15;			/* 0x58 */
+	uint32_t unused16;			/* 0x5C */
+	uint32_t unused17;			/* 0x60 */
+	uint32_t unused18;			/* 0x64 */
+	uint32_t unused19;			/* 0x68 */
+	uint32_t unused20;			/* 0x6C */
+	uint32_t unused21;			/* 0x70 */
+	uint32_t unused22;			/* 0x74 */
+	uint32_t unused23;			/* 0x78 */
+	uint32_t unused24;			/* 0x7C */
+	uint32_t irq_mpforce[2];		/* 0x80 CPU 0*/
+						/* 0x84 CPU 1*/
+	uint32_t unused25;			/* 0x88 */
+	uint32_t unused26;			/* 0x8C */
+	uint32_t unused27;			/* 0x90 */
+	uint32_t unused28;			/* 0x94 */
+	uint32_t unused29;			/* 0x98 */
+	uint32_t unused30;			/* 0x9C */
+	uint32_t unused31;			/* 0xA0 */
+	uint32_t unused32;			/* 0xA4 */
+	uint32_t unused33;			/* 0xA8 */
+	uint32_t unused34;			/* 0xAC */
+	uint32_t unused35;			/* 0xB0 */
+	uint32_t unused36;			/* 0xB4 */
+	uint32_t unused37;			/* 0xB8 */
+	uint32_t unused38;			/* 0xBC */
+	uint32_t extended_irq_id[2];		/* 0xC0 CPU 0*/
+						/* 0xC4 CPU 1*/
+};
+
+
+
+struct leon3_ahbstat_registermap {
+	uint32_t status;
+	uint32_t failing_address;
+};
+
+
+
+struct gptimer_timer {
+	uint32_t value;
+	uint32_t reload;
+	uint32_t ctrl;
+	uint32_t unused;
+};
+
+struct gptimer_unit {
+	uint32_t scaler;
+	uint32_t scaler_reload;
+	uint32_t config;
+	uint32_t unused;
+	struct gptimer_timer timer[4];
+};
+
+struct grtimer_timer {
+	uint32_t value;
+	uint32_t reload;
+	uint32_t ctrl;
+	uint32_t latch_value;
+};
+
+struct grtimer_unit {
+	uint32_t scaler;
+	uint32_t scaler_reload;
+	uint32_t config;
+
+	uint32_t irq_select;
+
+	struct grtimer_timer timer[2];
+};
+
+#endif /* _SPARC_ASM_LEON_REG_H_ */
diff --git a/arch/sparc/include/asm/spinlock.h b/arch/sparc/include/asm/spinlock.h
new file mode 100644
index 0000000000000000000000000000000000000000..25c104948b8023aca44775b743b68856570bbeef
--- /dev/null
+++ b/arch/sparc/include/asm/spinlock.h
@@ -0,0 +1,212 @@
+/**
+ * @file    spinlock.h
+ * @author  Armin Luntzer (armin.luntzer@univie.ac.at)
+ * @date    December, 2013
+ * @brief   spin locking
+ *
+ * @copyright Armin Luntzer (armin.luntzer@univie.ac.at)
+ * @copyright David S. Miller (davem@caip.rutgers.edu), 1997  (some parts)
+ *
+ * @copyright
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _ARCH_SPARC_ASM_SPINLOCK_H_
+#define _ARCH_SPARC_ASM_SPINLOCK_H_
+
+#include <compiler.h>
+#include <kernel/types.h>
+
+#define __SPINLOCK
+struct spinlock {
+	uint8_t	 lock;
+	uint32_t lock_recursion;
+};
+
+
+/* the sparc PIL field	*/
+#define PSR_PIL 0x00000f00
+
+
+
+/**
+ * @brief save and disable the processor interrupt level state
+ * @return PSR
+ * @warning make sure to call a save/restore pair from within the same stack frame
+ */
+
+#define __spin_lock_save_irq __spin_lock_save_irq
+__attribute__((unused))
+static uint32_t spin_lock_save_irq(void)
+{
+	uint32_t psr;
+	uint32_t tmp;
+
+
+	__asm__ __volatile__(
+			"rd     %%psr, %0        \n\t"
+			"or        %0, %2, %1    \n\t"
+			"wr        %1,  0, %%psr \n\t"
+			"nop                     \n\t"
+			"nop                     \n\t"
+			"nop                     \n\t"
+			: "=&r" (psr), "=r" (tmp)
+			: "i"   (PSR_PIL)
+			: "memory");
+	return psr;
+}
+
+
+/**
+ * @brief Restore the processor interrupt level state
+ */
+
+#define __spin_lock_restore_irq __spin_lock_restore_irq
+__attribute__((unused))
+static void spin_lock_restore_irq(uint32_t psr)
+{
+	uint32_t tmp;
+
+	__asm__ __volatile__(
+			"rd     %%psr, %0        \n\t"
+			"and       %2, %1, %2    \n\t"
+			"andn      %0, %1, %0    \n\t"
+			"wr        %0, %2, %%psr \n\t"
+			"nop                     \n\t"
+			"nop                     \n\t"
+			"nop                     \n\t"
+			: "=&r" (tmp)
+			: "i"   (PSR_PIL), "r" (psr)
+			: "memory");
+}
+
+
+/**
+ * @brief	MPPB LEON-side spin lock
+ * @param 	lock a struct spinlock
+ *
+ * @warning	will silently fail AND deadlock everytime YOU are stupid
+ * @note	it is, however, save to use with interrupts (sort of)
+ */
+
+#define __spin_lock __spin_lock
+__attribute__((unused))
+static void spin_lock(struct spinlock *lock)
+{
+	uint32_t psr_flags;
+
+	if (unlikely(lock->lock_recursion))
+		return;
+
+	psr_flags = spin_lock_save_irq();
+
+	lock->lock_recursion = 1;
+
+	__asm__ __volatile__(
+			"1:                      \n\t"
+			"ldstub [%0], %%g2       \n\t"
+			"andcc  %%g2, %%g2, %%g2 \n\t"
+			"bnz,a    1b             \n\t"
+			" nop		         \n\n"
+			:
+			: "r" (&lock->lock)
+			: "g2", "memory", "cc");
+
+	lock->lock_recursion = 0;
+
+	spin_lock_restore_irq(psr_flags);
+}
+
+
+/**
+ * @brief	spin lock which does not care about interrupts
+ * @param 	lock a struct spinlock
+ */
+
+__attribute__((unused))
+static void spin_lock_raw(struct spinlock *lock)
+{
+	if (unlikely(lock->lock_recursion))
+		return;
+
+	lock->lock_recursion = 1;
+
+	__asm__ __volatile__(
+			"1:                      \n\t"
+			"ldstub [%0], %%g2       \n\t"
+			"andcc  %%g2, %%g2, %%g2 \n\t"
+			"bnz,a    1b             \n\t"
+			" nop		         \n\n"
+			:
+			: "r" (&lock->lock)
+			: "g2", "memory", "cc");
+
+	lock->lock_recursion = 0;
+}
+
+/**
+ * @brief   lock check
+ * @returns success or failure
+ */
+
+#define __spin_is_locked __spin_is_locked
+__attribute__((unused))
+static int spin_is_locked(struct spinlock *lock)
+{
+	return (lock->lock != 0);
+}
+
+
+/**
+ * @brief spins forever until lock opens
+ */
+
+#define __spin_unlock_wait __spin_unlock_wait
+__attribute__((unused))
+static void spin_unlock_wait(struct spinlock *lock)
+{
+	barrier();
+
+	while(spin_is_locked(lock));
+}
+
+/**
+ * @brief	spin lock
+ * @param 	lock a struct spinlock
+ * @return	success or failure
+ */
+
+#define __spin_try_lock __spin_try_lock
+__attribute__((unused))
+static int spin_try_lock(struct spinlock *lock)
+{
+	uint32_t retval;
+
+	__asm__ __volatile__("ldstub [%1], %0" : "=r" (retval) : "r"  (&lock->lock) : "memory");
+
+	return (retval == 0);
+}
+
+/**
+ * @brief	side spin-unlock
+ * @param 	lock a struct spinlock
+ */
+
+#define __spin_unlock __spin_unlock
+__attribute__((unused))
+static void spin_unlock(struct spinlock *lock)
+{
+	__asm__ __volatile__("swap [%0], %%g0       \n\t" : : "r" (&lock->lock) : "memory");
+}
+
+
+
+#endif
diff --git a/arch/sparc/include/srmmu.h b/arch/sparc/include/srmmu.h
index 32683574a2ba060ccb99cb13934f0a7667923977..b62cc4d2902d663503f5f0ef3053a8c117cd7fd6 100644
--- a/arch/sparc/include/srmmu.h
+++ b/arch/sparc/include/srmmu.h
@@ -9,11 +9,7 @@
 #define _SPARC_SRMMU_H_
 
 
-#include <stddef.h>
-
-
-/* XXX: not here */
-#define __BIG_ENDIAN_BITFIELD __BIG_ENDIAN_BITFIELD
+#include <kernel/types.h>
 
 
 #define SRMMU_CTRL_IMPL_MASK	0xf0000000
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile
index c84383837c306aae1ce44316c320a3388f3a504b..5d760a657b2659e0da6fe3361ca29f84b35a9dd9 100644
--- a/arch/sparc/kernel/Makefile
+++ b/arch/sparc/kernel/Makefile
@@ -15,5 +15,6 @@ obj-y += stacktrace.o
 obj-y += stack.o
 obj-y += traps/data_access_exception_trap.o
 obj-y += traps/data_access_exception.o
+obj-y += irq.o
 
 #libs-y                 += lib/
diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c
new file mode 100644
index 0000000000000000000000000000000000000000..8a2aadb6286693deed5d76fae058755d701f5326
--- /dev/null
+++ b/arch/sparc/kernel/irq.c
@@ -0,0 +1,1062 @@
+/**
+ * @file arch/sparc/kernel/irq.c
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @todo eventually replace catch_interrupt() libgloss/newlib functionality
+ *       with local/custom code and rework the globals
+ * @todo irq configuration should only be done through a syscall,
+ *       so traps are disabled
+ *
+ * @brief an IRQ manager that dispatches interrupts to registered
+ *	  handler functions
+ *
+ * Implements an interrupt handler that supports registration of a predefined
+ * arbitrary number of handler function per interrupt with immediate or deferred
+ * execution priorities. Callbacks are tracked in linked lists that  should
+ * always be allocated in a contiguous block of memory for proper cache
+ * hit rate.
+ *
+ * @image html sparc/sparc_irq_manager.png "IRQ manager"
+ *
+ * @note this implements the processor specific IRQ logic
+ *
+ * @todo once we have fully functional AMBA bus scan, we might want to rework
+ *	 the static interrupt assignments...
+ *
+ *
+ * @note We do NOT cache-bypass read from interrupt control registers when
+ *	 handling interrupts. It should not be necessary and can
+ *	 potentially save a few cycles.
+ */
+
+
+
+
+#include <kernel/irq.h>
+#include <kernel/kmem.h>
+#include <kernel/printk.h>
+
+#include <errno.h>
+#include <list.h>
+
+#include <asm/io.h>
+#include <asm/leon.h>
+#include <asm/leon_reg.h>
+#include <asm/spinlock.h>
+#include <asm/irq.h>
+
+
+struct irl_vector_elem {
+	irq_handler_t handler;
+	enum isr_exec_priority priority;
+	void *data;
+	struct list_head handler_node;
+};
+
+
+/* maximum number of interrupt handlers we will allow */
+#define IRL_POOL_SIZE	64
+/* the maximum queue size for deferred handlers  */
+#define IRL_QUEUE_SIZE	64
+
+
+
+#ifdef CONFIG_LEON2
+#define IRL_SIZE	LEON2_IRL_SIZE
+#define EIRL_SIZE	LEON2_IRL_SIZE
+
+static struct leon2_irqctrl_registermap  *leon_irqctrl_regs;
+static struct leon2_eirqctrl_registermap *leon_eirqctrl_regs;
+
+#endif /* CONFIG_LEON2 */
+
+#ifdef CONFIG_LEON3
+
+#define IRL_SIZE	LEON3_IRL_SIZE
+#define EIRL_SIZE	LEON3_IRL_SIZE
+
+static struct leon3_irqctrl_registermap *leon_irqctrl_regs;
+
+#endif /* CONFIG_LEON3 */
+
+
+static struct list_head	irl_pool_head;
+static struct list_head	irl_queue_head;
+static struct list_head	irq_queue_pool_head;
+
+static struct list_head	*irl_vector;
+static struct list_head	*eirl_vector;
+
+static struct irl_vector_elem *irl_pool;
+static struct irl_vector_elem *irl_queue_pool;
+
+
+static unsigned int leon_eirq;
+
+
+#ifdef CONFIG_IRQ_STATS_COLLECT
+
+#include <kernel/string.h>
+#include <kernel/sysctl.h>
+
+static struct {
+	uint32_t irl;
+	uint32_t eirl;
+	uint32_t irl_irq[IRL_SIZE];
+	uint32_t eirl_irq[EIRL_SIZE];
+} irqstat;
+
+
+static ssize_t irl_show(__attribute__((unused)) struct sysobj *sobj,
+			__attribute__((unused)) struct sobj_attribute *sattr,
+			char *buf)
+{
+	uint32_t irq;
+
+
+	if (!strcmp("irl", sattr->name))
+		return sprintf(buf, "%lu", irqstat.irl);
+
+	irq = atoi(sattr->name);
+
+	if (irq > IRL_SIZE)
+		return 0;
+
+	return sprintf(buf, "%lu", irqstat.irl_irq[irq]);
+}
+
+static ssize_t irl_store(__attribute__((unused)) struct sysobj *sobj,
+			 __attribute__((unused)) struct sobj_attribute *sattr,
+			 __attribute__((unused)) const char *buf,
+			 __attribute__((unused)) size_t len)
+{
+	uint32_t irq;
+
+
+	if (!strcmp("irl", sattr->name)) {
+		irqstat.irl = 0;
+		return 0;
+	}
+
+	irq = atoi(sattr->name);
+
+	if (irq > IRL_SIZE)
+		return 0;
+
+	irqstat.irl_irq[irq] = 0;
+
+	return 0;
+}
+
+static ssize_t eirl_show(__attribute__((unused)) struct sysobj *sobj,
+			 __attribute__((unused)) struct sobj_attribute *sattr,
+			 char *buf)
+{
+	uint32_t irq;
+
+
+	if (!strcmp("eirl", sattr->name))
+		return sprintf(buf, "%lu", irqstat.eirl);
+
+	irq = atoi(sattr->name);
+
+	if (irq > EIRL_SIZE)
+		return 0;
+
+	return sprintf(buf, "%lu", irqstat.eirl_irq[irq]);
+}
+
+static ssize_t eirl_store(__attribute__((unused)) struct sysobj *sobj,
+			  __attribute__((unused)) struct sobj_attribute *sattr,
+			  __attribute__((unused)) const char *buf,
+			  __attribute__((unused)) size_t len)
+{
+	uint32_t irq;
+
+
+	if (!strcmp("eirl", sattr->name)) {
+		irqstat.eirl = 0;
+		return 0;
+	}
+
+	irq = atoi(sattr->name);
+
+	if (irq > EIRL_SIZE)
+		return 0;
+
+	irqstat.eirl_irq[irq] = 0;
+
+	return 0;
+}
+
+__extension__
+static struct sobj_attribute irl_attr[] = {
+	__ATTR(irl,  irl_show, irl_store),
+	__ATTR(0,  irl_show, irl_store), __ATTR(1,  irl_show, irl_store),
+	__ATTR(2,  irl_show, irl_store), __ATTR(3,  irl_show, irl_store),
+	__ATTR(4,  irl_show, irl_store), __ATTR(5,  irl_show, irl_store),
+	__ATTR(6,  irl_show, irl_store), __ATTR(7,  irl_show, irl_store),
+	__ATTR(8,  irl_show, irl_store), __ATTR(9, irl_show, irl_store),
+	__ATTR(10, irl_show, irl_store), __ATTR(11, irl_show, irl_store),
+	__ATTR(12, irl_show, irl_store), __ATTR(13, irl_show, irl_store),
+	__ATTR(14, irl_show, irl_store), __ATTR(15, irl_show, irl_store),
+	};
+
+static struct sobj_attribute *irl_attributes[] = {
+	&irl_attr[0],  &irl_attr[1],  &irl_attr[2],  &irl_attr[3],
+	&irl_attr[4],  &irl_attr[5],  &irl_attr[6],  &irl_attr[7],
+	&irl_attr[8],  &irl_attr[9],  &irl_attr[10], &irl_attr[11],
+	&irl_attr[12], &irl_attr[13], &irl_attr[14], &irl_attr[15],
+	&irl_attr[16], NULL};
+
+__extension__
+static struct sobj_attribute eirl_attr[] = {
+	__ATTR(eirl,  eirl_show, eirl_store),
+	__ATTR(0,  eirl_show, eirl_store), __ATTR(1,  eirl_show, eirl_store),
+	__ATTR(2,  eirl_show, eirl_store), __ATTR(3,  eirl_show, eirl_store),
+	__ATTR(4,  eirl_show, eirl_store), __ATTR(5,  eirl_show, eirl_store),
+	__ATTR(6,  eirl_show, eirl_store), __ATTR(7,  eirl_show, eirl_store),
+	__ATTR(8,  eirl_show, eirl_store), __ATTR(9, eirl_show, eirl_store),
+	__ATTR(10, eirl_show, eirl_store), __ATTR(11, eirl_show, eirl_store),
+	__ATTR(12, eirl_show, eirl_store), __ATTR(13, eirl_show, eirl_store),
+	__ATTR(14, eirl_show, eirl_store), __ATTR(15,  eirl_show, eirl_store),
+#ifdef CONFIG_LEON2
+	__ATTR(16, eirl_show, eirl_store), __ATTR(17, eirl_show, eirl_store),
+	__ATTR(18, eirl_show, eirl_store), __ATTR(19, eirl_show, eirl_store),
+	__ATTR(20, eirl_show, eirl_store), __ATTR(21, eirl_show, eirl_store),
+	__ATTR(22, eirl_show, eirl_store), __ATTR(23, eirl_show, eirl_store),
+	__ATTR(24, eirl_show, eirl_store), __ATTR(25, eirl_show, eirl_store),
+	__ATTR(26, eirl_show, eirl_store), __ATTR(27, eirl_show, eirl_store),
+	__ATTR(28, eirl_show, eirl_store), __ATTR(29, eirl_show, eirl_store),
+	__ATTR(30, eirl_show, eirl_store), __ATTR(31, eirl_show, eirl_store),
+#endif /* CONFIG_LEON2 */
+};
+
+__extension__
+static struct sobj_attribute *eirl_attributes[] = {
+	&eirl_attr[0],  &eirl_attr[1],  &eirl_attr[2],  &eirl_attr[3],
+	&eirl_attr[4],  &eirl_attr[5],  &eirl_attr[6],  &eirl_attr[7],
+	&eirl_attr[8],  &eirl_attr[9],  &eirl_attr[10], &eirl_attr[11],
+	&eirl_attr[12], &eirl_attr[13], &eirl_attr[14], &eirl_attr[15],
+	&eirl_attr[16],
+
+#ifdef CONFIG_LEON2
+	&eirl_attr[17], &eirl_attr[18], &eirl_attr[19], &eirl_attr[20],
+	&eirl_attr[21], &eirl_attr[22], &eirl_attr[23], &eirl_attr[24],
+	&eirl_attr[25], &eirl_attr[26], &eirl_attr[27], &eirl_attr[28],
+	&eirl_attr[29], &eirl_attr[30], &eirl_attr[31], &eirl_attr[32],
+#endif /* CONFIG_LEON2 */
+	NULL};
+
+#endif /* CONFIG_IRQ_STATS_COLLECT */
+
+
+/* XXX we use BCC's libgloss implementation for now */
+extern int catch_interrupt (int func, int irq);
+
+
+/**
+ * @brief clear (acknowledge) a pending IRQ
+ *
+ * @param irq the interrupt to clear
+ */
+
+static void leon_clear_irq(unsigned int irq)
+{
+#ifdef CONFIG_LEON3
+	iowrite32be((1 << irq), &leon_irqctrl_regs->irq_clear);
+#endif /* CONFIG_LEON3 */
+
+#ifdef CONFIG_LEON2
+	if (irq < IRL_SIZE)
+		iowrite32be((1 << irq), &leon_irqctrl_regs->irq_clear);
+	else if (irq < (IRL_SIZE + EIRL_SIZE))
+#ifdef CONFIG_MPPB /* what what??? O.o */
+		iowrite32be((1 << LEON_REAL_EIRQ(irq)),
+			    &leon_eirqctrl_regs->eirq_status);
+#else
+	iowrite32be((1 << LEON_REAL_EIRQ(irq)),
+			    &leon_eirqctrl_regs->eirq_clear);
+#endif
+#endif /* CONFIG_LEON2 */
+}
+
+
+/**
+ * @brief unmask an IRQ
+ *
+ * @param irq the interrupt to unmask
+ * @param cpu the cpu for which the interrupt is to be unmasked
+ */
+
+static void leon_unmask_irq(unsigned int irq, unsigned int cpu)
+{
+	uint32_t mask;
+
+
+#ifdef CONFIG_LEON3
+	mask = ioread32be(&leon_irqctrl_regs->irq_mpmask[cpu]);
+	mask |= (1 << irq);
+	iowrite32be(mask, &leon_irqctrl_regs->irq_mpmask[cpu]);
+#endif /* CONFIG_LEON3 */
+
+#ifdef CONFIG_LEON2
+	if (irq < IRL_SIZE) {
+		mask = ioread32be(&leon_irqctrl_regs->irq_mask);
+		mask |= (1 << irq);
+		iowrite32be(mask, &leon_irqctrl_regs->irq_mask);
+	} else if (irq < (IRL_SIZE + EIRL_SIZE)) {
+		mask = ioread32be(&leon_eirqctrl_regs->eirq_mask);
+		mask |= (1 << LEON_REAL_EIRQ(irq));
+		iowrite32be(mask, &leon_eirqctrl_regs->eirq_mask);
+	}
+#endif /* CONFIG_LEON2 */
+}
+
+
+/**
+ * @brief mask an IRQ
+ *
+ * @param irq the interrupt to mask
+ * @param cpu the cpu for which the interrupt is to be masked
+ */
+
+static void leon_mask_irq(unsigned int irq, unsigned int cpu)
+{
+	uint32_t mask;
+
+
+#ifdef CONFIG_LEON3
+	mask = ioread32be(&leon_irqctrl_regs->irq_mpmask[cpu]);
+
+	mask &= ~(1 << irq);
+
+	iowrite32be(mask, &leon_irqctrl_regs->irq_mpmask[cpu]);
+#endif /* CONFIG_LEON3 */
+
+#ifdef CONFIG_LEON2
+	if (irq < IRL_SIZE) {
+		mask = ioread32be(&leon_irqctrl_regs->irq_mask);
+		mask &= ~(1 << irq);
+		iowrite32be(mask, &leon_irqctrl_regs->irq_mask);
+	} else if (irq < (IRL_SIZE + EIRL_SIZE)) {
+		mask = ioread32be(&leon_eirqctrl_regs->eirq_mask);
+		mask &= ~(1 << LEON_REAL_EIRQ(irq));
+		iowrite32be(mask, &leon_eirqctrl_regs->eirq_mask);
+	}
+#endif /* CONFIG_LEON2 */
+}
+
+/**
+ * @brief enable an interrupt
+ *
+ * @param irq the interrupt to enable
+ * @param cpu the cpu for which the interrupt is to be enabled
+ */
+
+static void leon_enable_irq(unsigned int irq, unsigned int cpu)
+{
+	leon_clear_irq(irq);
+
+	leon_unmask_irq(irq, cpu);
+}
+
+
+/**
+ * @brief disable an interrupt
+ *
+ * @param irq the interrupt to disable
+ * @param cpu the cpu for which the interrupt is to be disabled
+ */
+
+static void leon_disable_irq(unsigned int irq, unsigned int cpu)
+{
+	leon_clear_irq(irq);
+
+	leon_mask_irq(irq, cpu);
+}
+
+
+/**
+ * @brief queue a handler for delayed exectuion
+ *
+ * @param p_elem	a pointer to a struct IRQVectorElem
+ *
+ * @returns 0 on success
+ */
+
+static int leon_irq_queue(struct irl_vector_elem *p_elem)
+{
+	uint32_t psr_flags;
+	struct irl_vector_elem *p_queue;
+
+	if (unlikely(list_empty(&irq_queue_pool_head)))
+		return -EBUSY;
+
+
+	psr_flags = spin_lock_save_irq();
+
+	p_queue = list_entry((&irq_queue_pool_head)->next,
+			     struct irl_vector_elem, handler_node);
+
+	p_queue->handler  = p_elem->handler;
+	p_queue->priority = p_elem->priority;
+	p_queue->data     = p_elem->data;
+
+	list_move_tail(&p_queue->handler_node, &irl_queue_head);
+
+	spin_lock_restore_irq(psr_flags);
+
+	return 0;
+}
+
+
+/**
+ *
+ * @brief execute deferred (low-priority) handlers
+ */
+
+void leon_irq_queue_execute(void)
+{
+	struct irl_vector_elem *p_elem = NULL;
+	struct irl_vector_elem *p_tmp;
+
+
+	if (list_empty(&irl_queue_head))
+		return;
+
+	list_for_each_entry_safe(p_elem, p_tmp,
+				 &irl_queue_head, handler_node) {
+
+		list_del(&p_elem->handler_node);
+
+		if (likely(p_elem->handler)) {
+
+			if (p_elem->handler(p_elem->data))
+				leon_irq_queue(p_elem);
+			else
+				list_add_tail(&p_elem->handler_node,
+					      &irq_queue_pool_head);
+
+		} else {
+			list_add_tail(&p_elem->handler_node,
+				      &irq_queue_pool_head);
+		}
+	}
+}
+
+
+/**
+ * @brief he central interrupt handling routine
+ *
+ * @param irq an interrupt number
+ *
+ * @returns always 0
+ *
+ * @note handler return codes ignored for now
+ *
+ * XXX maybe we want to keep acking IRQs as in eirq_dispatch...
+ */
+
+static int leon_irq_dispatch(unsigned int irq)
+{
+	struct irl_vector_elem *p_elem;
+
+
+#ifdef CONFIG_IRQ_STATS_COLLECT
+	irqstat.irl++;
+	irqstat.irl_irq[irq]++;
+#endif /* CONFIG_IRQ_STATS_COLLECT */
+
+	list_for_each_entry(p_elem, &irl_vector[irq], handler_node) {
+		if (likely(p_elem->priority == ISR_PRIORITY_NOW))
+			p_elem->handler(p_elem->data);
+		else if (leon_irq_queue(p_elem) < 0)
+			p_elem->handler(p_elem->data);
+	}
+
+	return 0;
+}
+
+
+/**
+ * @brief the central interrupt handling routine for extended interrupts
+ *
+ * @param irq an extended interrupt number
+ *
+ * @returns always 0
+ *
+ * @note handler return codes ignored for now
+ */
+
+static int leon_eirq_dispatch(unsigned int irq)
+{
+	unsigned int eirq;
+#ifdef CONFIG_LEON3
+	unsigned int cpu;
+#endif /* CONFIG_LEON3 */
+
+	struct irl_vector_elem *p_elem;
+	struct irl_vector_elem *p_tmp;
+
+
+#ifdef CONFIG_IRQ_STATS_COLLECT
+	irqstat.irl++;
+	irqstat.irl_irq[irq]++;
+#endif /* CONFIG_IRQ_STATS_COLLECT */
+
+#ifdef CONFIG_LEON3
+	cpu = leon3_cpuid();
+#endif /* CONFIG_LEON3 */
+
+
+	/* keep acknowleding pending EIRQs */
+	/* XXX this is a potential death trap :) */
+	while (1) {
+
+#ifdef CONFIG_LEON3
+		/* no pending EIRQs remain */
+		if (!(leon_irqctrl_regs->irq_pending >> IRL_SIZE))
+			break;
+
+		eirq = leon_irqctrl_regs->extended_irq_id[cpu];
+#endif /* CONFIG_LEON3 */
+
+#ifdef CONFIG_LEON2
+		eirq = leon_eirqctrl_regs->eirq_status;
+
+
+		if (!(eirq & 0x20)) /* no pending EIRQs remain */
+			break;
+#endif /* CONFIG_LEON2 */
+
+		eirq &= 0x1F;	/* get the actual EIRQ number */
+
+		leon_clear_irq(LEON_WANT_EIRQ(eirq));
+
+
+#ifdef CONFIG_IRQ_STATS_COLLECT
+		irqstat.eirl++;
+		irqstat.eirl_irq[eirq]++;
+#endif /* CONFIG_IRQ_STATS_COLLECT */
+
+		list_for_each_entry_safe(p_elem, p_tmp, &eirl_vector[eirq],
+					 handler_node) {
+
+			/* deferred ISRs are executed immediately if
+			 * queueing fails
+			 */
+			if (likely(p_elem->priority == ISR_PRIORITY_NOW))
+				p_elem->handler(p_elem->data);
+			else if (leon_irq_queue(p_elem) < 0)
+				p_elem->handler(p_elem->data);
+		}
+	}
+
+
+	leon_clear_irq(irq);
+
+	return 0;
+}
+
+
+/**
+ * @brief	register a handler function to the primary interrupt line
+ *
+ * @param irq		an interrupt number
+ * @param priority	a handler priority (immediate or deferred)
+ * @param handler	a handler function
+ * @param data	a pointer to arbitrary user data; passed to
+ *			handler function
+ *
+ * @returns 0 on success
+ */
+
+int irl_register_handler(unsigned int irq,
+			  enum isr_exec_priority priority,
+			  irq_handler_t handler,
+			  void *data)
+{
+	uint32_t psr_flags;
+
+	struct irl_vector_elem *p_elem;
+
+
+	if (list_empty(&irl_pool_head))
+		return -ENOMEM;
+
+	if (irq > IRL_SIZE)
+		return -EINVAL;
+
+	if (!handler)
+		return -EINVAL;
+
+	psr_flags = spin_lock_save_irq();
+
+	p_elem = list_entry((&irl_pool_head)->next, struct irl_vector_elem,
+			    handler_node);
+
+	p_elem->handler = handler;
+	p_elem->priority = priority;
+	p_elem->data = data;
+
+	list_move_tail(&p_elem->handler_node, &irl_vector[irq]);
+
+	spin_lock_restore_irq(psr_flags);
+
+#ifdef CONFIG_LEON3
+	/* XXX for now, just register to the current CPU */
+	leon_enable_irq(irq, leon3_cpuid());
+#endif /* CONFIG_LEON3 */
+#ifdef CONFIG_LEON2
+	leon_enable_irq(irq, 0);
+#endif /* CONFIG_LEON2 */
+
+	/* XXX this is the call to whatever the low level handler is */
+	return catch_interrupt(((int) leon_irq_dispatch), irq);
+}
+
+
+/**
+ * @brief	register a handler function to the extended interrupt line
+ *
+ * @param	IRQ		an extended interrupt number
+ * @param	priority	a handler priority
+ * @param	handler	a handler function
+ * @param	data	a pointer to arbitrary user data
+ *
+ * @returns 0 on success
+ *
+ * XXX once this is confirmed working as planned, merge with function above,
+ *     we only need one of those
+ */
+
+static int eirl_register_handler(unsigned int irq,
+				 enum isr_exec_priority priority,
+				 irq_handler_t handler,
+				 void *data)
+{
+	uint32_t psr_flags;
+
+	struct irl_vector_elem *p_elem;
+
+
+	if (list_empty(&irl_pool_head))
+		return -ENOMEM;
+
+	if (irq > (IRL_SIZE + EIRL_SIZE))
+		return -EINVAL;
+
+	if (irq < IRL_SIZE)
+		return -EINVAL;
+
+	if (!handler)
+		return -EINVAL;
+
+	psr_flags = spin_lock_save_irq();
+
+	p_elem = list_entry((&irl_pool_head)->next, struct irl_vector_elem,
+		    handler_node);
+
+	p_elem->handler = handler;
+	p_elem->priority = priority;
+	p_elem->data = data;
+
+	list_move_tail(&p_elem->handler_node,
+		       &eirl_vector[LEON_REAL_EIRQ(irq)]);
+
+	spin_lock_restore_irq(psr_flags);
+#ifdef CONFIG_LEON3
+	/* XXX for now, just register to the current CPU */
+	leon_enable_irq(irq, leon3_cpuid());
+#endif /* CONFIG_LEON3 */
+#ifdef CONFIG_LEON2
+	leon_enable_irq(irq, 0);
+#endif /* CONFIG_LEON2 */
+
+	return 0;
+}
+
+
+/**
+ * @brief	de-register a handler function to the primary interrupt line
+ *
+ * @param	IRQ		an interrupt number
+ * @param	handler	a handler function
+ * @param	data	a pointer to arbitrary user data
+ *
+ * @note	in case of duplicate handlers, ALL encountered will
+ *		be removed
+ * @returns 0 on success
+ */
+
+static int irl_deregister_handler(unsigned int irq,
+				   irq_handler_t handler,
+				   void *data)
+{
+	uint32_t psr_flags;
+
+	struct irl_vector_elem *p_elem;
+	struct irl_vector_elem *p_tmp;
+
+
+	if (irq > 0xF)
+		return -EINVAL;
+
+	list_for_each_entry_safe(p_elem, p_tmp,
+				 &irl_vector[irq], handler_node) {
+
+		if (p_elem->handler == handler) {
+			if (p_elem->data == data) {
+
+				p_elem->handler  = NULL;
+				p_elem->data = NULL;
+				p_elem->priority = -1;
+
+				psr_flags = spin_lock_save_irq();
+				list_move_tail(&p_elem->handler_node,
+					       &irl_pool_head);
+				spin_lock_restore_irq(psr_flags);
+
+				if (irl_vector[irq].next == &irl_vector[irq])
+					leon_disable_irq(irq, leon3_cpuid());
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+
+/**
+ * @brief	de-register a handler function to the primary interrupt line
+ *
+ * @param	irq		an interrupt number
+ * @param	handler	a handler function
+ * @param	data	a pointer to arbitrary user data
+ *
+ * @note	in case of duplicate handlers, ALL encountered will
+ *		be removed
+ * @returns 0 on success
+ *
+ * XXX also think about merging with above
+ */
+
+static int eirl_deregister_handler(unsigned int irq,
+				   irq_handler_t handler,
+				   void *data)
+{
+	uint32_t psr_flags;
+
+	struct irl_vector_elem *p_elem;
+	struct irl_vector_elem *p_tmp;
+
+
+	/* XXX magic: extnded interrupts are 16...31 */
+	if (irq > 0x1F)
+		return -EINVAL;
+
+	if (irq < 0x10)
+		return -EINVAL;
+
+	list_for_each_entry_safe(p_elem, p_tmp,
+				 &eirl_vector[irq], handler_node) {
+
+		if (p_elem->handler == handler) {
+			if (p_elem->data == data) {
+
+				p_elem->handler  = NULL;
+				p_elem->data = NULL;
+				p_elem->priority = -1;
+
+				psr_flags = spin_lock_save_irq();
+				list_move_tail(&p_elem->handler_node,
+					       &irl_pool_head);
+				spin_lock_restore_irq(psr_flags);
+
+				if (eirl_vector[irq].next == &eirl_vector[irq])
+					leon_disable_irq(irq, leon3_cpuid());
+
+			}
+		}
+	}
+
+	return 0;
+}
+
+
+/**
+ * @brief initialise some of the internals
+ *
+ * @returns 0 on success
+ */
+
+static int irq_dispatch_init(void)
+{
+	size_t i;
+
+#ifdef CONFIG_IRQ_STATS_COLLECT
+	struct sysset *sset;
+	struct sysobj *sobj;
+#endif /* CONFIG_IRQ_STATS_COLLECT */
+
+
+	irl_vector = (struct list_head *)
+			kzalloc(IRL_SIZE * sizeof(struct list_head));
+	if (!irl_vector)
+		return -1;
+
+
+	eirl_vector = (struct list_head *)
+			kzalloc(EIRL_SIZE * sizeof(struct list_head));
+	if (!eirl_vector)
+		return -1;
+
+	irl_pool = (struct irl_vector_elem *)
+			kzalloc(IRL_POOL_SIZE * sizeof(struct irl_vector_elem));
+	if(!irl_pool)
+		return -1;
+
+
+	irl_queue_pool = (struct irl_vector_elem *)
+		kzalloc(IRL_QUEUE_SIZE * sizeof(struct irl_vector_elem));
+	if(!irl_queue_pool)
+		return -1;
+
+
+
+	INIT_LIST_HEAD(&irl_pool_head);
+	INIT_LIST_HEAD(&irl_queue_head);
+	INIT_LIST_HEAD(&irq_queue_pool_head);
+
+	for (i = 0; i < IRL_SIZE; i++)
+		INIT_LIST_HEAD(&irl_vector[i]);
+
+	for (i = 0; i < EIRL_SIZE; i++)
+		INIT_LIST_HEAD(&eirl_vector[i]);
+
+	for (i = 0; i < IRL_POOL_SIZE; i++)
+		list_add_tail(&irl_pool[i].handler_node, &irl_pool_head);
+
+	for (i = 0; i < IRL_QUEUE_SIZE; i++)
+		list_add_tail(&irl_queue_pool[i].handler_node,
+			      &irq_queue_pool_head);
+
+#ifdef CONFIG_IRQ_STATS_COLLECT
+	sset = sysset_create_and_add("irl", NULL, sys_set);
+
+	sobj = sysobj_create();
+
+	if (!sobj)
+		return -1;
+
+	sobj->sattr = irl_attributes;
+	sysobj_add(sobj, NULL, sset, "primary");
+
+	sobj = sysobj_create();
+
+	if (!sobj)
+		return -1;
+
+	sobj->sattr = eirl_attributes;
+	sysobj_add(sobj, NULL, sset, "secondary");
+#endif /* CONFIG_IRQ_STATS_COLLECT */
+
+	return 0;
+}
+
+
+/**
+ * @brief probe for extended IRQ controller
+ */
+
+static void leon_setup_eirq(void)
+{
+	unsigned int eirq;
+
+#ifdef CONFIG_LEON3
+	/* probe for extended IRQ controller, see GR712UM, p75 */
+	eirq = (ioread32be(&leon_irqctrl_regs->mp_status) >> 16) & 0xf;
+#endif /* CONFIG_LEON3 */
+#ifdef CONFIG_LEON2
+	eirq = 10;	/* yep... */
+#endif /* CONFIG_LEON2 */
+
+	if (!eirq || eirq > IRL_SIZE) {
+		pr_err("IRQ: extended irq controller invalid: %d\n", eirq);
+#if 1
+		BUG();
+#else
+		return;
+#endif
+	}
+
+	leon_eirq = eirq;
+
+	BUG_ON(catch_interrupt((int) leon_eirq_dispatch, leon_eirq));
+
+#ifdef CONFIG_LEON3
+	/* XXX for now, just register to the current CPU */
+	leon_enable_irq(leon_eirq, leon3_cpuid());
+#endif /* CONFIG_LEON3 */
+#ifdef CONFIG_LEON2
+	leon_enable_irq(leon_eirq, 0);
+#endif /* CONFIG_LEON2 */
+
+}
+
+
+/**
+ * @brief set interrupt hardware priority
+ *
+ * @param mask the interrupt selection mask to apply
+ *
+ * @param level selected interrupt level 1== HIGH, 0 == LOW
+ */
+
+__attribute__((unused))
+static void leon_irq_set_level(uint32_t irq_mask, uint32_t level)
+{
+	uint32_t flags;
+
+
+#ifdef CONFIG_LEON3
+	flags = ioread32be(&leon_irqctrl_regs->irq_level);
+#endif /* CONFIG_LEON3 */
+#ifdef CONFIG_LEON2
+	flags = ioread32be(&leon_irqctrl_regs->irq_mask) >> 17;
+#endif /* CONFIG_LEON2 */
+
+	if (!level)
+		flags &= ~irq_mask;
+	else
+		flags |= irq_mask;
+
+#ifdef CONFIG_LEON3
+	iowrite32be(flags, &leon_irqctrl_regs->irq_level);
+#endif /* CONFIG_LEON3 */
+#ifdef CONFIG_LEON2
+	iowrite32be(flags << 17, &leon_irqctrl_regs->irq_mask);
+#endif /* CONFIG_LEON2 */
+
+}
+
+/**
+ * @brief enable an IRQ
+ *
+ * @param data a struct irq_data
+ */
+
+static unsigned int enable_irq(struct irq_data *data)
+{
+	if (data->irq < IRL_SIZE)
+		return irl_register_handler(data->irq, data->priority,
+					     data->handler, data->data);
+
+	return eirl_register_handler(data->irq, data->priority,
+				     data->handler, data->data);
+}
+
+/**
+ * @brief disable an IRQ
+ *
+ * @param data a struct irq_data
+ */
+
+static void disable_irq(struct irq_data *data)
+{
+	if (data->irq > 0xF) {
+		eirl_deregister_handler(data->irq, data->handler, data->data);
+	} else if (data->irq < 0x10) {
+		irl_deregister_handler(data->irq, data->handler, data->data);
+	}
+}
+
+
+/**
+ * @brief mask an IRQ
+ */
+
+static void mask_irq(struct irq_data *data)
+{
+	leon_mask_irq(data->irq, leon3_cpuid());
+}
+
+
+/**
+ * @brief mask an IRQ
+ *
+ * @param data a struct irq_data
+ */
+
+static void unmask_irq(struct irq_data *data)
+{
+	leon_unmask_irq(data->irq, leon3_cpuid());
+}
+
+
+/**
+ * @brief mask an IRQ
+ */
+
+static void execute_deferred_irq(void)
+{
+	leon_irq_queue_execute();
+}
+
+
+
+/**
+ * the configuration for the high-level irq manager
+ */
+
+static struct irq_dev leon_irq = {
+	.irq_enable	= enable_irq,
+	.irq_disable	= disable_irq,
+	.irq_mask	= mask_irq,
+	.irq_unmask	= unmask_irq,
+	.irq_deferred	= execute_deferred_irq,
+};
+
+
+/**
+ * @brief initialise the IRQ subsystem on the LEON
+ */
+
+void leon_irq_init(void)
+{
+#ifdef CONFIG_LEON3
+	/* XXX should determine that from AMBA PNP scan */
+	leon_irqctrl_regs = (struct leon3_irqctrl_registermap *)
+						LEON3_BASE_ADDRESS_IRQMP;
+
+	/* mask all interrupts on this (boot) CPU */
+	iowrite32be(0, &leon_irqctrl_regs->irq_mpmask[leon3_cpuid()]);
+#endif /* CONFIG_LEON3 */
+#ifdef CONFIG_LEON2
+	leon_irqctrl_regs  = (struct leon2_irqctrl_registermap *)
+						LEON2_BASE_ADDRESS_IRQ;
+	leon_eirqctrl_regs = (struct leon2_eirqctrl_registermap *)
+						LEON2_BASE_ADDRESS_EIRQ;
+
+
+	iowrite32be(0, &leon_irqctrl_regs->irq_mask);
+#endif /* CONFIG_LEON2 */
+
+	irq_init(&leon_irq);
+
+	BUG_ON(irq_dispatch_init());
+
+	/* set up extended interrupt controller if found */
+	leon_setup_eirq();
+}
diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c
index f25f695fb311eea52d2df883cf4583718e79d899..f3ea4cc2c496ba9ea1032625ae26f0a31ffd23ec 100644
--- a/arch/sparc/kernel/setup.c
+++ b/arch/sparc/kernel/setup.c
@@ -6,6 +6,7 @@
 
 #include <init.h>
 #include <mm.h>
+#include <asm/irq.h>
 #include <compiler.h>
 
 #include <page.h>
@@ -61,7 +62,7 @@ static void reserve_kernel_stack(void)
 static void mem_init(void)
 {
 	sp_banks[0].base_addr = 0x40000000;
-	sp_banks[0].num_bytes = 0x00800000;
+	sp_banks[0].num_bytes = 0x02800000;
 
 #if (SPARC_PHYS_BANKS > 0)
 	sp_banks[1].base_addr = 0x60000000;
@@ -88,4 +89,6 @@ void setup_arch(void)
 	reserve_kernel_stack();
 
 	BUG_ON(stack_migrate(NULL, _kernel_stack_top));
+
+	leon_irq_init();
 }
diff --git a/include/asm-generic/io.h b/include/asm-generic/io.h
index 9e9cf3972678d8b1ecbe6e332826cbf74b8380d6..58bd95380cb76906414e7097238e23fa78ea6742 100644
--- a/include/asm-generic/io.h
+++ b/include/asm-generic/io.h
@@ -25,14 +25,15 @@
 #ifndef _ASM_GENERIC_IO_H_
 #define _ASM_GENERIC_IO_H_
 
-#include <stdint.h>
+#include <asm/io.h>
+#include <kernel/types.h>
 
 
 #ifndef __raw_readb
 #define __raw_readb __raw_readb
 static inline uint8_t __raw_readb(const volatile void *addr)
 {
-        return *(const volatile uint8_t *)addr;
+        return (*(const volatile uint8_t *) addr);
 }
 #endif
 
@@ -40,7 +41,7 @@ static inline uint8_t __raw_readb(const volatile void *addr)
 #define __raw_readw __raw_readw
 static inline uint16_t __raw_readw(const volatile void *addr)
 {
-        return *(const volatile uint16_t *)addr;
+        return (*(const volatile uint16_t *) addr);
 }
 #endif
 
@@ -48,7 +49,7 @@ static inline uint16_t __raw_readw(const volatile void *addr)
 #define __raw_readl __raw_readl
 static inline uint32_t __raw_readl(const volatile void *addr)
 {
-        return *(const volatile uint32_t *)addr;
+        return (*(const volatile uint32_t *) addr);
 }
 #endif
 
@@ -56,7 +57,7 @@ static inline uint32_t __raw_readl(const volatile void *addr)
 #define __raw_writeb __raw_writeb
 static inline void __raw_writeb(uint8_t w, volatile void *addr)
 {
-        *(volatile uint8_t *)addr = w;
+        (*(volatile uint8_t *) addr) = w;
 }
 #endif
 
@@ -64,7 +65,7 @@ static inline void __raw_writeb(uint8_t w, volatile void *addr)
 #define __raw_writew __raw_writew
 static inline void __raw_writew(uint16_t w, volatile void *addr)
 {
-        *(volatile uint16_t *)addr = w;
+        (*(volatile uint16_t *) addr) = w;
 }
 #endif
 
@@ -72,7 +73,7 @@ static inline void __raw_writew(uint16_t w, volatile void *addr)
 #define __raw_writel __raw_writel
 static inline void __raw_writel(uint32_t l, volatile void *addr)
 {
-        *(volatile uint32_t *)addr = l;
+        (*(volatile uint32_t *) addr) = l;
 }
 #endif
 
diff --git a/include/asm-generic/spinlock.h b/include/asm-generic/spinlock.h
new file mode 100644
index 0000000000000000000000000000000000000000..81c9280a397eaf7c292cdc12468175594db1c661
--- /dev/null
+++ b/include/asm-generic/spinlock.h
@@ -0,0 +1,86 @@
+/**
+ * @file    include/asm-generic/spinlock.h
+ *
+ * @copyright GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @note this is nothing but a placeholder...
+ */
+
+#ifndef _ASM_GENERIC_SPINLOCK_H_
+#define _ASM_GENERIC_SPINLOCK_H_
+
+#include <asm/spinlock.h>
+
+
+#ifndef __SPINLOCK
+#define __SPINLOCK
+struct spinlock {
+}
+#endif
+
+#ifndef __spin_lock_save_irq
+#define __spin_lock_save_irq __spin_lock_save_irq
+static unsigned long spin_lock_save_irq(void)
+{
+	return 0;
+}
+#endif
+
+
+#ifndef __spin_lock_restore_irq
+#define __spin_lock_restore_irq __spin_lock_restore_irq
+static void spin_lock_restore_irq(__attribute__((unused)) uint32_t psr)
+{
+}
+#endif
+
+
+#ifndef __spin_lock
+#define __spin_lock __spin_lock
+static void spin_lock(__attribute__((unused)) struct spinlock *lock)
+{
+}
+#endif
+
+
+#ifndef __spin_is_locked
+#define __spin_is_locked __spin_is_locked
+static int spin_is_locked(__attribute__((unused)) struct spinlock *lock)
+{
+	return 0;
+}
+#endif
+
+
+#ifndef __spin_unlock_wait
+#define __spin_unlock_wait __spin_unlock_wait
+static void spin_unlock_wait(__attribute__((unused)) struct spinlock *lock)
+{
+}
+#endif
+
+#ifndef __spin_try_lock
+#define __spin_try_lock __spin_try_lock
+static int spin_try_lock(__attribute__((unused)) struct spinlock *lock)
+{
+	return 0;
+}
+#endif
+
+#ifndef __spin_unlock
+#define __spin_unlock __spin_unlock
+static void spin_unlock(__attribute__((unused)) struct spinlock *lock)
+{
+}
+#endif
+
+#define /* _ASM_GENERIC_SPINLOCK_H_ */
diff --git a/include/kernel/export.h b/include/kernel/export.h
new file mode 100644
index 0000000000000000000000000000000000000000..0a0f4b85ed1dc10be4d110f7abbda388a98f6987
--- /dev/null
+++ b/include/kernel/export.h
@@ -0,0 +1,52 @@
+/**
+ * @file include/kernel/export.h
+ *
+ * @note derived from include/linux/export.h */
+#ifndef _KERNEL_EXPORT_H_
+#define _KERNEL_EXPORT_H_
+
+
+struct kernel_symbol {
+        unsigned long value;
+        const char *name;
+};
+
+
+#define __KERNEL_SYMBOL(x) x
+#define __KERNEL_SYMBOL_STR(x) #x
+
+
+/* Indirect, so macros are expanded before pasting. */
+#define KERNEL_SYMBOL(x) __KERNEL_SYMBOL(x)
+#define KERNEL_SYMBOL_STR(x) __KERNEL_SYMBOL_STR(x)
+
+#if 0
+/* For every exported symbol, place a struct in the __ksymtab section */
+#define ___EXPORT_SYMBOL(sym, sec)                                      \
+        extern typeof(sym) sym;                                         \
+        static const char __kstrtab_##sym[]                             \
+        __attribute__((section("__ksymtab_strings"), aligned(1)))       \
+        = KERNEL_SYMBOL_STR(sym);                                       \
+        static const struct kernel_symbol __ksymtab_##sym               \
+        __attribute__((used))                                           \
+        __attribute__((section("___ksymtab" sec "+" #sym), used))       \
+        = { (unsigned long)&sym, __kstrtab_##sym }
+#else
+#define ___EXPORT_SYMBOL(sym, sec)                                      \
+	extern typeof(sym) sym;                                         \
+	static const char __kstrtab_##sym[]                             \
+        = KERNEL_SYMBOL_STR(sym);                                       \
+        static const struct kernel_symbol __ksymtab_##sym               \
+        __attribute__((used))                                           \
+        = { (unsigned long)&sym, __kstrtab_##sym }
+
+#endif
+
+#define __EXPORT_SYMBOL ___EXPORT_SYMBOL
+
+#define EXPORT_SYMBOL(sym)                                      \
+        __EXPORT_SYMBOL(sym, "")
+
+
+
+#endif /* _KERNEL_EXPORT_H_ */
diff --git a/include/kernel/init.h b/include/kernel/init.h
index f5f8b1a6ea9cfb1787a5e80ca194c6e78a6f351f..1ea11195a63232208a36dc5dba728bf27ec72172 100644
--- a/include/kernel/init.h
+++ b/include/kernel/init.h
@@ -2,4 +2,16 @@
  * @file include/kernel/init.h
  */
 
+#ifndef _KERNEL_INIT_H_
+#define _KERNEL_INIT_H_
+
+typedef int (*initcall_t)(void);
+
+/* TODO: initcalls on startup for compiled-in modules */
+#define define_initcall(fn) \
+        static initcall_t _initcall_##fn __attribute__((used)) \
+        __attribute__((__section__(".initcall"))) = fn;
+
 void setup_arch(void);
+
+#endif /* _KERNEL_INIT_H_ */
diff --git a/include/kernel/irq.h b/include/kernel/irq.h
new file mode 100644
index 0000000000000000000000000000000000000000..dc3afdc1547f4e4f776dec1666c03f7b86d92b45
--- /dev/null
+++ b/include/kernel/irq.h
@@ -0,0 +1,62 @@
+/**
+ * @file    include/kernel/irq.h
+ * @author  Armin Luntzer (armin.luntzer@univie.ac.at)
+ * @date    December, 2013
+ *
+ * @copyright GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _KERNEL_IRQ_H_
+#define _KERNEL_IRQ_H_
+
+#include <kernel/types.h>
+
+
+enum irqreturn {
+        IRQ_NONE    = 0,
+        IRQ_HANDLED = 1
+};
+
+typedef enum irqreturn irqreturn_t;
+
+typedef irqreturn_t (*irq_handler_t)(void *);
+enum isr_exec_priority {ISR_PRIORITY_NOW, ISR_PRIORITY_DEFERRED};
+
+
+struct irq_data {
+        unsigned int irq;
+	enum isr_exec_priority priority;
+	irq_handler_t handler;
+	void *data;
+};
+
+struct irq_dev {
+        unsigned int (*irq_enable)   (struct irq_data *data);
+        void         (*irq_disable)  (struct irq_data *data);
+        void         (*irq_mask)     (struct irq_data *data);
+        void         (*irq_unmask)   (struct irq_data *data);
+        void         (*irq_deferred) (void);
+};
+
+
+
+void irq_init(struct irq_dev *dev);
+
+int irq_free(unsigned int irq, irq_handler_t handler, void *data);
+
+int irq_request(unsigned int irq, enum isr_exec_priority priority,
+		irq_handler_t handler, void *data);
+
+int irq_exec_deferred(void);
+
+#endif /* _KERNEL_IRQ_H_ */
diff --git a/include/kernel/kmem.h b/include/kernel/kmem.h
index 4332b4035b6d50a3c3a6aa37ed3323279450d760..39f8f91aea8ebdcfa0407c151465112a3276a8d3 100644
--- a/include/kernel/kmem.h
+++ b/include/kernel/kmem.h
@@ -8,6 +8,7 @@
 #include <stddef.h>
 
 void *kmalloc(size_t size);
+void *kzalloc(size_t size);
 void *kcalloc(size_t nmemb, size_t size);
 void *krealloc(void *ptr, size_t size);
 
diff --git a/include/kernel/module.h b/include/kernel/module.h
index 944b26dfac2f3b7300bec0847757fe305533c40f..281745ff9efe69f4138b1dc3fef6c656848d8f56 100644
--- a/include/kernel/module.h
+++ b/include/kernel/module.h
@@ -1,10 +1,19 @@
 #ifndef _KERNEL_MODULE_H_
 #define _KERNEL_MODULE_H_
 
-
+#include <kernel/init.h>
 #include <kernel/elf.h>
 
 
+
+#define module_init(initfunc)   \
+        int _module_init(void) __attribute__((alias(#initfunc)));
+
+#define module_exit(exitfunc)   \
+        int _module_exit(void) __attribute__((alias(#exitfunc)));
+
+
+
 struct module_section {
 	char *name;
 	unsigned long addr;
diff --git a/include/kernel/string.h b/include/kernel/string.h
new file mode 100644
index 0000000000000000000000000000000000000000..37e749021b6ea417addb524176adfa0f14dd6fff
--- /dev/null
+++ b/include/kernel/string.h
@@ -0,0 +1,20 @@
+/**
+ * @file include/kernel/string.h
+ */
+
+#ifndef _KERNEL_STRING_H_
+#define _KERNEL_STRING_H_
+
+int sprintf(char *str, const char *format, ...);
+int strcmp(const char *s1, const char *s2);
+char *strpbrk(const char *s, const char *accept);
+char *strsep(char **stringp, const char *delim);
+size_t strlen(const char *s);
+
+void *memcpy(void *dest, const void *src, size_t n);
+
+int isspace(int c);
+int atoi(const char *nptr);
+
+
+#endif /* _KERNEL_STRING_H_ */
diff --git a/include/kernel/types.h b/include/kernel/types.h
index 4463c12650b340b2cb72d295299fffcfd3208e54..e4f58b015b8c68b004d21b092d443c5808276398 100644
--- a/include/kernel/types.h
+++ b/include/kernel/types.h
@@ -5,6 +5,8 @@
 #ifndef _KERNEL_TYPES_H_
 #define _KERNEL_TYPES_H_
 
+#include <compiler.h>
+
 #include <stddef.h>
 
 #include <stdbool.h>
@@ -16,6 +18,12 @@
 #endif
 
 
+/* d'oh! -.- */
+#if defined(CONFIG_LEON2) || defined(CONFIG_LEON3)
+#define __BIG_ENDIAN_BITFIELD __BIG_ENDIAN_BITFIELD
+#endif
+
+
 /* BCC is at least 4.4.2 */
 #if defined(GCC_VERSION) && (GCC_VERSION >= 404020)
 #include <stdint.h>
@@ -36,6 +44,15 @@ typedef   signed short  int16_t;
 typedef unsigned char uint8_t;
 typedef   signed char  int8_t;
 
+compile_time_assert(sizeof(uint64_t) == 8, TYPES_UINT64_SIZE_INVALID);
+compile_time_assert(sizeof( int64_t) == 8, TYPES__INT64_SIZE_INVALID);
+compile_time_assert(sizeof(uint32_t) == 4, TYPES_UINT32_SIZE_INVALID);
+compile_time_assert(sizeof( int32_t) == 4, TYPES__INT32_SIZE_INVALID);
+compile_time_assert(sizeof(uint16_t) == 2, TYPES_UINT16_SIZE_INVALID);
+compile_time_assert(sizeof( int16_t) == 2, TYPES__INT16_SIZE_INVALID);
+compile_time_assert(sizeof(uint8_t)  == 1, TYPES_UINT_8_SIZE_INVALID);
+compile_time_assert(sizeof( int8_t)  == 1, TYPES__INT_8_SIZE_INVALID);
+
 #endif
 
 #ifndef __SIZEOF_LONG__
@@ -44,4 +61,7 @@ typedef   signed char  int8_t;
 
 
 
+
+
+
 #endif /* _KERNEL_TYPES_H_ */
diff --git a/include/mppb.h b/include/mppb.h
new file mode 100644
index 0000000000000000000000000000000000000000..8d9e53626c5a95cf44242ad632017c3a621c974e
--- /dev/null
+++ b/include/mppb.h
@@ -0,0 +1,28 @@
+/**
+ * @file include/mppb.h
+ */
+
+#ifndef _MPPB_H_
+#define _MPPB_H_
+
+
+
+#define MPPB_NOC_DMA_BASE_ADDR	0x30100000
+
+#define MPPB_SDRAM_AHB_BASE	0x40000000
+#define MPPB_SDRAM_AHB_SIZE	0x0FFFFFFF
+
+#define MPPB_SDRAM_NOC_BASE	0x50000000
+#define MPPB_SDRAM_NOC_SIZE	0x0FFFFFFF
+
+#define MPPB_SRAM_NOC_BASE	0x30000000
+#define MPPB_SRAM_NOC_SIZE	0x0003FFFF
+
+/* get the IRL for a channel, on the MPPB, this is equivalent to the channel
+ * number on the secondary interrupt controller
+ */
+#define MPPB_NOC_GET_DMA_IRL(chan) (chan)
+
+
+
+#endif /* _MPPB_H_ */
diff --git a/include/noc.h b/include/noc.h
new file mode 100644
index 0000000000000000000000000000000000000000..ba558d27e67e577e2005aa32e2a5da239c0d96d3
--- /dev/null
+++ b/include/noc.h
@@ -0,0 +1,37 @@
+/**
+ * @file include/noc.h
+ */
+
+#ifndef _NOC_H_
+#define _NOC_H_
+
+
+#ifdef CONFIG_MPPB
+
+#include <mppb.h>
+
+#define NOC_DMA_BASE_ADDR	MPPB_NOC_DMA_BASE_ADDR
+
+#define NOC_SCRATCH_BUFFER_BASE	MPPB_SRAM_NOC_BASE
+#define NOC_SCRATCH_BUFFER_SIZE	MPPB_SRAM_NOC_SIZE
+
+#define NOC_GET_DMA_IRL(chan) MPPB_NOC_GET_DMA_IRL(chan)
+
+#endif /* CONFIG_MPPB */
+
+
+#ifdef CONFIG_SSDP
+
+#include <ssdp.h>
+
+#define NOC_DMA_BASE_ADDR	SSDP_NOC_DMA_BASE_ADDR
+
+#define NOC_SCRATCH_BUFFER_BASE	SSDP_SRAM_NOC_BASE
+#define NOC_SCRATCH_BUFFER_SIZE	SSDP_SRAM_NOC_SIZE
+
+#define NOC_GET_DMA_IRL(chan) SSDP_NOC_GET_DMA_IRL(chan)
+
+#endif /* CONFIG_SSDP */
+
+
+#endif /* _NOC_H_ */
diff --git a/include/noc_dma.h b/include/noc_dma.h
new file mode 100644
index 0000000000000000000000000000000000000000..a66e61684e89f1af40746dd1ab6339fba3d1874d
--- /dev/null
+++ b/include/noc_dma.h
@@ -0,0 +1,115 @@
+/**
+ * @file include/noc_dma.h
+ */
+
+
+#ifndef _NOC_DMA_H_
+#define _NOC_DMA_H_
+
+#include <noc.h>
+#include <kernel/types.h>
+
+
+
+#define NOC_DMA_CHANNELS	8
+
+#define NOC_DMA_ACCESS_SIZE_8	0x0
+#define NOC_DMA_ACCESS_SIZE_16	0x1
+#define NOC_DMA_ACCESS_SIZE_32	0x2
+#define NOC_DMA_ACCESS_SIZE_64	0x3
+
+
+#define NOC_DMA_PRIORITY_LOW	0x0
+#define NOC_DMA_PRIORITY_HIGH	0x1
+
+#define NOC_DMA_IRQ_FWD_NONE	0x0
+#define NOC_DMA_IRQ_FWD_IN	0x1
+#define NOC_DMA_IRQ_FWD_OUT	0x2
+#define NOC_DMA_IRQ_FWD_BOTH	0x3
+
+
+#define NOC_DMA_STRIDE_MAX    32767
+#define NOC_DMA_STRIDE_MIN   -32767
+
+#define NOC_DMA_SIZE_MAX      65535
+
+#define NOC_DMA_CHANNEL_START	0x1
+#define NOC_DMA_CHANNEL_BUSY	NOC_DMA_CHANNEL_START
+
+
+#define NOC_DMA_STRIDE_SRC_OFFSET	16
+#define NOC_DMA_STRIDE_DST_MASK		0xffff
+
+#define NOC_DMA_SIZE_Y_OFFSET		16
+#define NOC_DMA_SIZE_X_MASK		0xffff
+
+
+#define NOC_DMA_STRIDES(src, dst)	((src << NOC_DMA_STRIDE_SRC_OFFSET) | \
+					 (dst &  NOC_DMA_STRIDE_DST_MASK))
+
+#define NOC_DMA_SIZES(x, y)		((y << NOC_DMA_SIZE_Y_OFFSET) | \
+					 (x &  NOC_DMA_SIZE_X_MASK))
+
+
+#ifdef CONFIG_NOC_DMA_TRANSFER_QUEUE_SIZE
+#define NOC_DMA_TRANSFER_QUEUE_SIZE CONFIG_NOC_DMA_TRANSFER_QUEUE_SIZE
+#else
+#define NOC_DMA_TRANSFER_QUEUE_SIZE 32
+#endif
+
+enum noc_dma_elem_size {BYTE       = NOC_DMA_ACCESS_SIZE_8,
+			HALFWORD   = NOC_DMA_ACCESS_SIZE_16,
+			WORD       = NOC_DMA_ACCESS_SIZE_32,
+			DOUBLEWORD = NOC_DMA_ACCESS_SIZE_64};
+
+enum noc_dma_priority {LOW  = NOC_DMA_PRIORITY_LOW,
+		       HIGH = NOC_DMA_PRIORITY_HIGH};
+
+enum noc_dma_irq_fwd {OFF  = NOC_DMA_IRQ_FWD_NONE,
+		      IN   = NOC_DMA_IRQ_FWD_IN,
+		      OUT  = NOC_DMA_IRQ_FWD_OUT,
+		      BOTH = NOC_DMA_IRQ_FWD_BOTH};
+
+struct noc_dma_transfer {
+
+	void *src;
+	void *dst;
+
+	uint16_t x_elem;		/* number of elements in x */
+	uint16_t y_elem;		/* number of elements in y */
+
+	int16_t  x_stride_src;		/* width of stride in source x */
+	int16_t  y_stride_src;		/* width of stride in source y */
+
+	int16_t  x_stride_dst;		/* width of stride in destination x */
+	int16_t  y_stride_dst;		/* width of stride in destination y */
+
+
+	uint16_t mtu;				/* maximum packet size */
+	enum noc_dma_elem_size elem_size;	/* the element type size */
+	enum noc_dma_irq_fwd irq_fwd;		/* irq notification mode */
+	enum noc_dma_priority priority;		/* transfer priority */
+
+	/* the caller may specify a callback with user data that is executed
+	 * on transfer completion
+	 */
+	int (*callback)(void *);
+	void *userdata;
+
+	struct list_head node;
+};
+
+int noc_dma_req_xfer(void *src, void *dst, uint16_t x_elem, uint16_t y_elem,
+		     enum noc_dma_elem_size elem_size,
+		     int16_t x_stride_src, int16_t x_stride_dst,
+		     int16_t y_stride_src, int16_t y_stride_dst,
+		     enum noc_dma_priority dma_priority, uint16_t mtu,
+		     int (*callback)(void *), void *userdata);
+
+int noc_dma_req_lin_xfer(void *src, void *dst,
+			 uint16_t elem, enum noc_dma_elem_size elem_size,
+			 enum noc_dma_priority dma_priority, uint16_t mtu,
+			 int (*callback)(void *), void *userdata);
+
+
+#endif /* _NOC_DMA_H_ */
diff --git a/include/ssdp.h b/include/ssdp.h
new file mode 100644
index 0000000000000000000000000000000000000000..7db4ed41e019fcd6c09b6e17809cb277c753ae3d
--- /dev/null
+++ b/include/ssdp.h
@@ -0,0 +1,29 @@
+/**
+ * @file include/ssdp.h
+ */
+
+#ifndef _SSDP_H_
+#define _SSDP_H_
+
+#warning "Configuration not yet known, using MPPBv2 as baseline"
+
+
+#define SSDP_NOC_DMA_BASE_ADDR	0x30100000
+
+#define SSDP_SDRAM_AHB_BASE	0x40000000
+#define SSDP_SDRAM_AHB_SIZE	0x0FFFFFFF
+
+#define SSDP_SDRAM_NOC_BASE	0x50000000
+#define SSDP_SDRAM_NOC_SIZE	0x0FFFFFFF
+
+#define SSDP_SRAM_NOC_BASE	0x30000000
+#define SSDP_SRAM_NOC_SIZE	0x0003FFFF
+
+/* get the IRL for a channel, on the SSDP, this is equivalent to the channel
+ * number on the secondary interrupt controller
+ */
+#define SSDP_NOC_GET_DMA_IRL(chan) (chan)
+
+
+
+#endif /* _SSDP_H_ */
diff --git a/init/Kconfig b/init/Kconfig
index 0c176d991eade6e5197d6c0d572e12632c5a7457..a10aa3e58d829140ab50624e7ae3477b40112e8b 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -16,7 +16,7 @@ config KALLSYMS
 	help
 	  Create a kernel image symbol lookup table for use in loadable modules.
 	  Note: If disabled, modules can still export symbols for other modules
-	  to use, but no kernel functions will be resolvable.	
+	  to use, but no kernel functions will be resolvable.
 	  If unsure, say Y.
 
 
diff --git a/init/main.c b/init/main.c
index faec954e2c8489101dcc4a3720d650c61549c9af..d4914a379b79af6c9a4a701b66bc650619b18612 100644
--- a/init/main.c
+++ b/init/main.c
@@ -14,7 +14,9 @@
 #include <kernel/printk.h>
 #include <kernel/kmem.h>
 #include <kernel/sbrk.h>
+#include <kernel/sysctl.h>
 
+#define MSG "MAIN: "
 
 void module_image_load_embedded(void);
 void *module_lookup_embedded(char *mod_name);
@@ -24,6 +26,9 @@ void *module_read_embedded(char *mod_name);
 static void kernel_init(void)
 {
 	setup_arch();
+#ifdef CONFIG_SYSCTL
+	sysctl_init();
+#endif
 }
 
 
@@ -39,14 +44,14 @@ int main(void)
 	/* load the embedded AR image */
 	module_image_load_embedded();
 
-
+#if 0
 	/* look up a kernel symbol */
 	printk("%s at %p\n", "printk", lookup_symbol("printk"));
-#if 0
-	/* look up a file in the embedded image */
-	printk("%s at %p\n", "testmodule.ko",
-	       module_lookup_embedded("testmodule.ko"));
 
+	/* look up a file in the embedded image */
+	printk("%s at %p\n", "noc_dma.ko",
+	       module_lookup_embedded("noc_dma.ko"));
+#endif
 	/* to load an arbitrary image, you may upload it via grmon, e.g.
 	 *	load -binary kernel/test.ko 0xA0000000
 	 * then load it:
@@ -59,13 +64,15 @@ int main(void)
 	 * it directly, until we have a MNA trap */
 
 
-	addr = module_read_embedded("testmodule.ko");
+	addr = module_read_embedded("noc_dma.ko");
+
+	pr_debug(MSG "noc_dma module address is %p\n", addr);
 
 	if (addr)
 		module_load(&m, addr);
-#endif
+#if 0
 	modules_list_loaded();
-
+#endif
 	return 0;
 }
 
diff --git a/init/modules-image.c b/init/modules-image.c
index dec2c27a2790fda6590b2ccbee2cb6011f91bd18..e00937b47968bdcdcb459ebaf1ad739f38fcfee6 100644
--- a/init/modules-image.c
+++ b/init/modules-image.c
@@ -18,8 +18,10 @@ void module_image_load_embedded(void)
 	ar_load(&_binary_modules_image_start,
 		(unsigned int)&_binary_modules_image_size, &mod_ar);
 
+#if 0
 	ar_list_files(&mod_ar);
 	ar_list_symbols(&mod_ar);
+#endif
 }
 
 
diff --git a/kernel/Kconfig b/kernel/Kconfig
index 3863ba2af5c0aad0f3f092e58c0ba7a340945d77..f6495b9f62f756729e972e873a598c73a586a570 100644
--- a/kernel/Kconfig
+++ b/kernel/Kconfig
@@ -18,3 +18,77 @@ config KERNEL_LEVEL
 	 7: informational
 	 8: debug
 
+config IRQ_STATS_COLLECT
+	bool "Enable IRQ statistics via sysctl"
+	depends on SYSCTL
+	default n
+	help
+	 Collect interrupt statistics and expose them in the
+	 systl tree.
+
+config SOC
+	bool "Enable System-On-Chip configurations"
+	default n
+
+choice
+	prompt "Select board type"
+	default MPPB
+	depends on SOC
+
+config MPPB
+	bool "MPPBv2"
+	depends on SOC && LEON2
+	help
+	 Configure parameters for the MPPBv2
+
+config SSDP
+	bool "SSDP"
+	depends on SOC && LEON3
+	help
+	 Configure parameters for the SSDP
+
+endchoice
+
+menu "System-on-Chip drivers"
+
+depends on SOC
+
+config NOC_DMA
+	tristate "NoC 2D DMA driver"
+	default m
+	depends on SOC && (MPPB || SSDP)
+	help
+	 Build the MPPBv2/SSDP NoC DMA driver.
+
+config NOC_DMA_TRANSFER_QUEUE_SIZE
+	int "NoC DMA transfer queue size"
+	default "32"
+	depends on NOC_DMA
+	help
+	 Configures the size of the DMA transfer queue.
+
+config NOC_DMA_STATS_COLLECT
+	bool "Enable NoC DMA statistics via sysctl"
+	depends on SYSCTL
+	default n
+	help
+	 Collect DMA transfer statistics and expose them in the
+	 systl tree.
+
+config NOC_DMA_SAME_MASTER_TRANSFER_WORKAROUND
+	bool "Enable workaround for NoC DMA transfers within same master"
+	default n
+	help
+	 Enable a workaround for NoC DMA transfers with the address space
+	 of the same master. On the MPPB this will result in a stuck channel,
+	 as the transfer never completes. If you say Y here, the requested
+	 transfer will be initialised as two transfers, one of high priority
+	 that copies the source data into a scratch buffer (the NoC SRAM tile)
+	 and a low priority transfer that copies the contents of the scratch
+	 buffer to the destination. Results may be unpredictable,
+	 you have been warned.
+
+endmenu
+
+
+
diff --git a/kernel/Makefile b/kernel/Makefile
index 25d52d8ec9a7b250800f24e6be0eb1f9d07f01c9..b163e872b5bd2602f4eaa45fc8c2e6a2f71e1395 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -3,3 +3,5 @@ obj-y += ksym.o
 obj-$(CONFIG_KERNEL_PRINTK) += printk.o
 obj-y += bitmap.o
 obj-y += module.o
+obj-$(CONFIG_NOC_DMA) += noc_dma.o
+obj-y += irq.o
diff --git a/kernel/irq.c b/kernel/irq.c
new file mode 100644
index 0000000000000000000000000000000000000000..3253f93c8e062d328da17e822a378258ea901411
--- /dev/null
+++ b/kernel/irq.c
@@ -0,0 +1,91 @@
+/**
+ * @file kernel/irq.c
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at)
+ *
+ * this implements the high-level IRQ logic (which is admittedly not very
+ * thought-through)
+ */
+
+
+#include <errno.h>
+#include <kernel/irq.h>
+#include <kernel/export.h>
+
+static struct irq_dev *irq_ctrl;
+
+
+/**
+ * @brief request a slot for an interrupt handler
+ */
+
+int irq_request(unsigned int irq, enum isr_exec_priority priority,
+		irq_handler_t handler, void *data)
+{
+	struct irq_data cfg;
+
+
+	if (!irq_ctrl)
+		return -EINVAL;
+
+	if (!irq_ctrl->irq_enable)
+		return -EINVAL;
+
+	cfg.irq      = irq;
+	cfg.priority = priority;
+	cfg.handler  = handler;
+	cfg.data     = data;
+
+	return irq_ctrl->irq_enable(&cfg);
+}
+EXPORT_SYMBOL(irq_request);
+
+/**
+ * @brief release an interrupt handler
+ */
+
+int irq_free(unsigned int irq, irq_handler_t handler, void *data)
+{
+	struct irq_data cfg;
+
+
+	if (!irq_ctrl)
+		return -EINVAL;
+
+	if (!irq_ctrl->irq_disable)
+		return -EINVAL;
+
+	cfg.irq      = irq;
+	cfg.handler  = handler;
+	cfg.data     = data;
+
+	irq_ctrl->irq_disable(&cfg);
+
+	return 0;
+}
+EXPORT_SYMBOL(irq_free);
+
+
+/**
+ * @brief execute deferred interrupt handlers
+ */
+
+int irq_exec_deferred(void)
+{
+	if (!irq_ctrl)
+		return -EINVAL;
+
+	if (irq_ctrl->irq_deferred)
+		irq_ctrl->irq_deferred();
+
+	return 0;
+}
+
+
+/**
+ * @brief initialise the IRQ system
+ */
+
+void irq_init(struct irq_dev *dev)
+{
+	irq_ctrl = dev;
+}
diff --git a/kernel/kmem.c b/kernel/kmem.c
index 46dcf8dd9b58ba6929461e596c7c9e17faa9c31b..2f89dc26a82d7c29232fc2ddf5b867601a4083b2 100644
--- a/kernel/kmem.c
+++ b/kernel/kmem.c
@@ -248,6 +248,24 @@ void *kcalloc(size_t nmemb, size_t size)
 }
 
 
+/**
+ * @brief allocates size bytes and returns a pointer to the allocated memory,
+ *	  suitably aligned for any built-in type. The memory is set to zero.
+ *
+ * @param size the number of bytes to allocate
+ *
+ * @returns a pointer or NULL on error or size == 0
+ *
+ * @note this should be preferred over kcalloc(n, 1), as it saves the extra
+ *	 argument and hence produces less code
+ */
+
+void *kzalloc(size_t size)
+{
+	return kcalloc(size, 1);
+}
+
+
 /**
  * @brief changes the size of the memory block pointed to by ptr to size bytes.
  *	  The contents will be unchanged in the range from the start of the
diff --git a/kernel/module.c b/kernel/module.c
index 22d36d6e1820f79df7441bbd5f4319e41f4bf893..bdab605808299a8d46e954fb7f4ad6a8f4f8090c 100644
--- a/kernel/module.c
+++ b/kernel/module.c
@@ -858,7 +858,11 @@ static int module_relocate(struct elf_module *m)
 							 symstr);
 
 
-						if (!(get_symbol_type(m, symstr) & STT_FUNC)) {
+						if (!(get_symbol_type(m, symstr) & STT_OBJECT)) {
+							pr_debug(MOD "\tERROR, object data resolution not yet implemented, symbol %s may not function as intended. If it is a variable, declare it static as a workaround\n", symstr);
+						}
+
+						if (!(get_symbol_type(m, symstr) & (STT_FUNC | STT_OBJECT))) {
 							pr_err(MOD "\tERROR, unresolved symbol %s\n", symstr);
 							return -1;
 						}
@@ -871,7 +875,7 @@ static int module_relocate(struct elf_module *m)
 
 					}
 
-					pr_info(MOD "\tSymbol %s at %lx\n", symstr, sym);
+					pr_debug(MOD "\tSymbol %s at %lx\n", symstr, sym);
 
 					apply_relocate_add(m, &relatab[i], sym);
 
diff --git a/kernel/noc_dma.c b/kernel/noc_dma.c
new file mode 100644
index 0000000000000000000000000000000000000000..4d9053866f8a6fdc16cab0c134c8279780b211b8
--- /dev/null
+++ b/kernel/noc_dma.c
@@ -0,0 +1,819 @@
+/**
+ * @file kernel/noc_dma.c
+ *
+ * @note parameter limits are usually not checked, as they are implicit by type
+ *
+ * @todo stuck channel detection (needs threading or at least a timer service)
+ *	 and reset (if possible, transfer size 0 maybe? need to test...)
+ */
+
+
+#include <kernel/module.h>
+#include <kernel/kernel.h>
+#include <kernel/printk.h>
+#include <kernel/string.h>
+#include <kernel/kmem.h>
+#include <kernel/sysctl.h>
+#include <kernel/types.h>
+#include <kernel/export.h>
+
+
+#include <asm/irq.h>
+#include <kernel/irq.h>
+
+#include <asm-generic/io.h>
+#include <errno.h>
+#include <list.h>
+
+#include <noc_dma.h>
+
+
+#if !defined(CONFIG_MPPB) && !defined(CONFIG_SSDP)
+#error "Unsupported platform"
+#endif
+
+#define MSG "NOC DMA: "
+
+
+__extension__
+struct noc_dma_channel_ctrl {
+	union {
+		uint32_t ctrl;
+#if defined(__BIG_ENDIAN_BITFIELD)
+		struct {
+			uint32_t max_pkt_sz :16;
+			uint32_t reserved   :11;
+			uint32_t acc_sz     :2;
+			uint32_t irq_fwd    :2;
+			uint32_t priority   :1;
+		}__attribute__((packed));
+#endif
+	};
+};
+
+/**
+ * NoC DMA channel configuration register layout
+ * @see MPPB datasheet v4.03, p63
+ */
+__extension__
+struct noc_dma_channel {
+
+	union {
+		uint32_t start;
+		uint32_t status;
+#if defined(__BIG_ENDIAN_BITFIELD)
+		struct {
+			uint32_t res01	     :31;	/* reserved */
+			uint32_t start_status:1;
+		}__attribute__((packed));
+#endif
+	};
+
+	union {
+		uint32_t control;
+#if defined(__BIG_ENDIAN_BITFIELD)
+		struct noc_dma_channel_ctrl ctrl;
+#endif
+	};
+
+	uint32_t dst;
+
+	uint32_t src;
+
+	union {
+		uint32_t sizes;
+		struct {
+			uint16_t sz_y;
+			uint16_t sz_x;
+		}__attribute__((packed));
+	};
+
+	union {
+		uint32_t strides_x;
+		struct {
+			uint16_t str_x_src;
+			uint16_t str_x_dst;
+		}__attribute__((packed));
+	};
+
+	union {
+		uint32_t strides_y;
+		struct {
+			uint16_t str_y_src;
+			uint16_t str_y_dst;
+		}__attribute__((packed));
+	};
+
+	uint32_t res02[9];
+
+}__attribute__((packed));
+
+/* make sure the structure has the same size as the register */
+compile_time_assert(sizeof(struct noc_dma_channel) == 0x40,
+		    NOC_DMA_CHANNEL_CONFIG_SIZE_INVALID);
+
+
+/* we use this to track channels in our channel pool */
+struct noc_dma_channel_node {
+
+	unsigned int irl;	/* the interrupt line of this channel */
+
+	struct noc_dma_channel	*channel;
+	struct noc_dma_transfer	*transfer;
+
+	struct list_head node;
+};
+
+
+
+
+
+/*
+ * XXX this is statically assigned for now. If we have an MMU or if we can
+ * detect the from something like AMBA PnP, we perhaps want
+ * to mmap/configure the address on initialisation
+ */
+static struct noc_dma_channels {
+	struct noc_dma_channel chan[NOC_DMA_CHANNELS];
+} *noc_dma = (struct noc_dma_channels *) NOC_DMA_BASE_ADDR;
+
+
+static struct list_head	dma_channel_pool_head;
+static struct list_head	dma_transfer_pool_head;
+static struct list_head	dma_transfer_queue_head;
+
+/* we could set this up at runtime, but what for? */
+static struct noc_dma_transfer dma_transfer_pool[NOC_DMA_TRANSFER_QUEUE_SIZE];
+
+static struct noc_dma_channel_node dma_channel_pool[NOC_DMA_CHANNELS];
+
+
+#ifdef CONFIG_NOC_DMA_STATS_COLLECT
+static struct {
+	unsigned int transfers_queued;
+	unsigned int transfers_requested;
+	unsigned int transfers_completed;
+
+	unsigned int bytes_requested;
+	unsigned int bytes_transferred;
+} noc_dma_stats;
+
+
+
+__extension__
+static ssize_t noc_dma_show(struct sysobj *sobj __attribute__((unused)),
+			    struct sobj_attribute *sattr,
+			    char *buf)
+{
+	if (!strcmp(sattr->name, "transfers_queued"))
+		return sprintf(buf, "%d", noc_dma_stats.transfers_queued);
+
+	if (!strcmp(sattr->name, "transfers_requested"))
+		return sprintf(buf, "%d", noc_dma_stats.transfers_requested);
+
+	if (!strcmp(sattr->name, "transfers_completed"))
+		return sprintf(buf, "%d", noc_dma_stats.transfers_completed);
+
+	if (!strcmp(sattr->name, "bytes_requested"))
+		return sprintf(buf, "%d", noc_dma_stats.bytes_requested);
+
+	if (!strcmp(sattr->name, "bytes_transferred"))
+		return sprintf(buf, "%d", noc_dma_stats.bytes_transferred);
+
+	return 0;
+}
+
+__extension__
+static ssize_t noc_dma_store(struct sysobj *sobj __attribute__((unused)),
+			     struct sobj_attribute *sattr,
+			     const char *buf __attribute__((unused)),
+			     size_t len __attribute__((unused)))
+{
+	if (!strcmp(sattr->name, "reset_stats")) {
+		noc_dma_stats.transfers_requested = 0;
+		noc_dma_stats.transfers_completed = 0;
+		noc_dma_stats.bytes_requested     = 0;
+		noc_dma_stats.bytes_transferred   = 0;
+	}
+
+	return 0;
+}
+
+__extension__
+static struct sobj_attribute noc_dma_attr[] = {
+	__ATTR(transfers_queued,    noc_dma_show, NULL),
+	__ATTR(transfers_requested, noc_dma_show, NULL),
+	__ATTR(transfers_completed, noc_dma_show, NULL),
+	__ATTR(bytes_requested,     noc_dma_show, NULL),
+	__ATTR(bytes_transferred,   noc_dma_show, NULL),
+	__ATTR(reset_stats,         NULL,         noc_dma_store)
+};
+
+__extension__
+static struct sobj_attribute *noc_dma_attributes[] = {
+	&noc_dma_attr[0], &noc_dma_attr[1], &noc_dma_attr[2],
+	&noc_dma_attr[3], &noc_dma_attr[4], &noc_dma_attr[5],
+	NULL};
+
+#endif /* CONFIG_NOC_DMA_STATS_COLLECT */
+
+
+
+
+/**
+ * @brief check if a NoC DMA channel is in use
+ *
+ * @param chan a struct noc_dma_channel
+ *
+ * @returns 0 if not busy
+ */
+
+static int noc_dma_channel_busy(struct noc_dma_channel *chan)
+{
+	return ioread32be(&chan->status) & NOC_DMA_CHANNEL_BUSY;
+}
+
+
+/**
+ * @brief set source and destination strides in x
+ *
+ * @param chan a struct noc_dma_channel
+ * @param stride_src the source stride
+ * @param stride_dst the destination stride
+ */
+
+static void noc_dma_set_strides_x(struct noc_dma_channel *chan,
+				  int16_t stride_src, int16_t stride_dst)
+{
+	iowrite32be(NOC_DMA_STRIDES(stride_src, stride_dst), &chan->strides_x);
+}
+
+
+/**
+ * @brief set source and destination strides in y
+ *
+ * @param chan a struct noc_dma_channel
+ * @param stride_src the source stride
+ * @param stride_dst the destination stride
+ */
+
+static void noc_dma_set_strides_y(struct noc_dma_channel *chan,
+				  int16_t stride_src, int16_t stride_dst)
+{
+	iowrite32be(NOC_DMA_STRIDES(stride_src, stride_dst), &chan->strides_y);
+}
+
+
+/**
+ * @brief set transfer sizes in x and y
+ *
+ * @param chan a struct noc_dma_channel
+ * @param size_x the number of elements to transfer in x
+ * @param size_y the number of elements to transfer in y
+ */
+
+static void noc_dma_set_sizes(struct noc_dma_channel *chan,
+			      int16_t size_x, uint16_t size_y)
+{
+	iowrite32be(NOC_DMA_SIZES(size_x, size_y), &chan->sizes);
+}
+
+
+/**
+ * @brief set transfer source address
+ *
+ * @param chan a struct noc_dma_channel
+ * @param src the source memory location
+ */
+
+static void noc_dma_set_src(struct noc_dma_channel *chan, void *src)
+{
+	iowrite32be((uint32_t) src, &chan->src);
+}
+
+
+/**
+ * @brief set transfer destination address
+ *
+ * @param chan a struct noc_dma_channel
+ * @param dst the destination memory location
+ */
+
+static void noc_dma_set_dst(struct noc_dma_channel *chan, void *dst)
+{
+	iowrite32be((uint32_t) dst, &chan->dst);
+}
+
+
+/**
+ * @brief start a DMA transfer
+ * @param chan a struct noc_dma_channel
+ *
+ * @returns 0 on success, -EBUSY if channel is active
+ */
+
+static int noc_dma_start_transfer(struct noc_dma_channel *chan)
+{
+	if (noc_dma_channel_busy(chan))
+		return -EBUSY;
+
+	iowrite32be(NOC_DMA_CHANNEL_START, &chan->start);
+
+	return 0;
+}
+
+
+/**
+ * @brief sets the channel configuration for a transfer
+ *
+ * @param chan a struct noc_dma_channel
+ * @param max_pkt_size the maximum packet size to use during the transfer
+ * @param elem_size the size of a transfer element
+ * @param irq_fwd the irq notification mode on transfer completion
+ * @param priority the transfer priority
+ *
+ * @returns 0 on success, -EBUSY if channel is active
+ */
+
+static int noc_dma_set_config(struct noc_dma_channel *chan,
+			      int16_t max_pkt_size,
+			      enum noc_dma_elem_size elem_size,
+			      enum noc_dma_irq_fwd irq_fwd,
+			      enum noc_dma_priority priority)
+{
+	struct noc_dma_channel_ctrl ctrl;
+
+
+	if (noc_dma_channel_busy(chan))
+		return -EBUSY;
+
+#if defined(__BIG_ENDIAN_BITFIELD)
+	ctrl.max_pkt_sz = max_pkt_size;
+	ctrl.acc_sz     = elem_size;
+	ctrl.irq_fwd    = irq_fwd;
+	ctrl.priority   = priority;
+#else
+#error "Not implemented."
+#endif
+
+	iowrite32be(ctrl.ctrl, &chan->ctrl);
+
+	return 0;
+}
+
+
+/**
+ * @brief initialises the data parameters for a one dimensional DMA transfer
+ *
+ * @param chan a struct noc_dma_channel
+ * @param chan a struct noc_dma_transfer
+ *
+ * @returns 0 on success, EINVAL on error, EBUSY if channel is busy
+ */
+
+static int noc_dma_init_transfer(struct noc_dma_channel  *c,
+				 struct noc_dma_transfer *t)
+{
+
+	if (noc_dma_channel_busy(c))
+		return -EBUSY;
+
+	/* src == dst memory will result in a stuck DMA channel */
+	/* TODO implement workaround via scratch buffer */
+	if (((int) t->dst & 0xF0000000) == ((int) t->src & 0xF0000000)) {
+		pr_err(MSG "Cannot transfer withing same memory\n");
+		return -EINVAL;
+	}
+
+	/* no need to verify stride/size limits, they are implicity by type */
+
+	noc_dma_set_src(c, t->src);
+	noc_dma_set_dst(c, t->dst);
+
+	noc_dma_set_strides_x(c, t->x_stride_src, t->x_stride_dst);
+	noc_dma_set_strides_y(c, t->y_stride_src, t->y_stride_dst);
+
+	noc_dma_set_sizes(c, t->x_elem, t->y_elem);
+
+	noc_dma_set_config(c, t->mtu, t->elem_size, t->irq_fwd, t->priority);
+
+	return 0;
+}
+
+
+static void noc_dma_release_transfer(struct noc_dma_channel_node *c,
+				     struct noc_dma_transfer *t)
+{
+	t->callback = NULL;
+	t->userdata = NULL;
+
+	c->transfer = NULL;
+
+	list_add_tail(&c->node, &dma_channel_pool_head);
+	list_add_tail(&t->node, &dma_transfer_pool_head);
+}
+
+
+/**
+ * @brief initialises the data parameters for a one dimensional DMA transfer
+ *
+ * @param chan a struct noc_dma_channel
+ * @param chan a struct noc_dma_transfer
+ *
+ * @returns 0 on success, EINVAL on error, EBUSY if channel is busy
+ */
+
+static int noc_dma_execute_transfer(struct noc_dma_channel  *c,
+				    struct noc_dma_transfer *t)
+{
+	int ret;
+
+	struct noc_dma_channel_node *n;
+
+
+	ret = noc_dma_init_transfer(c, t);
+	if (ret) {
+		n = container_of((struct noc_dma_channel **) &c,
+				 struct noc_dma_channel_node, channel);
+		noc_dma_release_transfer(n, t);
+		return ret;
+	}
+
+	ret = noc_dma_start_transfer(c);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+
+/**
+ * @brief reserve a NoC DMA channel
+ * @note  the channel will automatically deregister on interrupt
+ *
+ * @param t	a pointer to a struct noc_dma_transfer
+ *
+ * @warning	channel will never be released when a transfer gets stuck, but
+ * @todo	either rely on an (external) timer or write a timer management routine... (probably the best idea)
+ * @todo	(if so, make it tickless...)
+ */
+
+static inline
+struct noc_dma_channel *noc_dma_request_channel(struct noc_dma_transfer *t)
+{
+	struct noc_dma_channel_node *c;
+
+
+	if(unlikely(list_empty(&dma_channel_pool_head)))
+		return NULL;
+
+	c = list_entry((&dma_channel_pool_head)->next,
+		       struct noc_dma_channel_node, node);
+
+	list_del(&c->node);
+
+	/* link transfer to node of this DMA channel */
+	c->transfer = t;
+
+#ifdef CONFIG_NOC_DMA_STATS_COLLECT
+	noc_dma_stats.transfers_requested++;
+#endif
+	return c->channel;
+}
+
+
+/**
+ * @brief	program queued DMA transfers
+ * @note will program transfers until all available DMA channels are in use
+ *	 or queue is empty
+ */
+
+static inline void noc_dma_program_transfer(void)
+{
+	struct noc_dma_channel *chan;
+
+	struct noc_dma_transfer *p_elem = NULL;
+	struct noc_dma_transfer *p_tmp;
+
+
+	if(list_empty(&dma_transfer_queue_head))
+		return;
+
+
+	list_for_each_entry_safe(p_elem, p_tmp,
+				 &dma_transfer_queue_head, node) {
+
+		chan = noc_dma_request_channel(p_elem);
+
+		if (!chan)
+			break;
+
+		list_del(&p_elem->node);
+
+		noc_dma_execute_transfer(chan, p_elem);
+
+#ifdef CONFIG_NOC_DMA_STATS_COLLECT
+		noc_dma_stats.transfers_queued--;
+#endif
+	}
+}
+
+
+/**
+ * @brief callback function for NoC DMA IRQs
+ *
+ * @param userdata userdata pointer supplied by the IRQ system
+ *
+ * @note will execute a caller-provideable callback notification function
+ *
+ * @return always 0
+ *
+ *
+ */
+
+/* XXX the user-supplied callbacks should not be executed in the ISR
+ *     for obvious reasons. Need to fix that once we have threads.
+ */
+#warning "NoC DMA: user callbacks are executed in interrupt mode"
+
+static irqreturn_t noc_dma_service_irq_handler(void *userdata)
+{
+	struct noc_dma_transfer *t;
+	struct noc_dma_channel_node  *c;
+
+
+
+	/* every actual channel has its own node registered to the IRQ system */
+	c = (struct noc_dma_channel_node *) userdata;
+
+	t = c->transfer;
+
+	if (t->callback)
+		t->callback(t->userdata);
+
+	noc_dma_release_transfer(c, t);
+
+#ifdef CONFIG_NOC_DMA_STATS_COLLECT
+	noc_dma_stats.transfers_completed++;
+#endif
+	/* activate queued */
+	noc_dma_program_transfer();
+
+
+	return 0;
+}
+
+
+static void noc_dma_register_transfer(struct noc_dma_transfer *t)
+{
+	list_add_tail(&t->node, &dma_transfer_queue_head);
+	noc_dma_program_transfer();
+
+#ifdef CONFIG_NOC_DMA_STATS_COLLECT
+	noc_dma_stats.transfers_queued++;
+#endif
+}
+
+
+static struct noc_dma_transfer *noc_dma_get_transfer_slot(void)
+{
+	struct noc_dma_transfer *t;
+
+
+	if(unlikely(list_empty(&dma_transfer_pool_head)))
+		return NULL;
+
+	t = list_entry((&dma_transfer_pool_head)->next,
+		       struct noc_dma_transfer, node);
+
+	list_del(&t->node);
+
+	return t;
+}
+
+
+
+#ifdef CONFIG_NOC_DMA_STATS_COLLECT
+/**
+ * @brief sysctl node setup
+ */
+
+static int noc_dma_sysctl_setup(void)
+{
+	struct sysobj *sobj;
+
+
+	sobj = sysobj_create();
+	if (!sobj)
+		return -ENOMEM;
+
+	sobj->sattr = noc_dma_attributes;
+	sysobj_add(sobj, NULL, driver_set, "noc_dma");
+
+	return 0;
+}
+#endif /* CONFIG_NOC_DMA_STATS_COLLECT */
+
+
+/**
+ * @brief driver cleanup function
+ */
+
+static int noc_dma_exit(void)
+{
+	printk(MSG "module_exit() not implemented\n");
+
+	return 0;
+}
+
+
+/**
+ * @brief driver initialisation
+ */
+
+static int noc_dma_init(void)
+{
+	unsigned int i;
+
+
+
+	pr_info(MSG "initialising\n");
+
+
+	INIT_LIST_HEAD(&dma_channel_pool_head);
+	INIT_LIST_HEAD(&dma_transfer_pool_head);
+	INIT_LIST_HEAD(&dma_transfer_queue_head);
+
+	for(i = 0; i < NOC_DMA_CHANNELS; i++) {
+
+		dma_channel_pool[i].channel = &noc_dma->chan[i];
+		dma_channel_pool[i].irl     = NOC_GET_DMA_IRL(i);
+
+		list_add_tail(&dma_channel_pool[i].node,
+			      &dma_channel_pool_head);
+
+#ifdef CONFIG_SSDP
+#warning "SSDP DMA channel IRQ numbers unknown, using MPPB as baseline"
+#endif
+		/* DMA EIRQ numbers are 0...NOC_DMA_CHANNELS */
+		irq_request(LEON_WANT_EIRQ(i), ISR_PRIORITY_NOW,
+			    &noc_dma_service_irq_handler,
+			    &dma_channel_pool[i]);
+	}
+
+	for (i = 0; i < NOC_DMA_TRANSFER_QUEUE_SIZE; i++) {
+		list_add_tail(&dma_transfer_pool[i].node,
+			      &dma_transfer_pool_head);
+	}
+
+#ifdef CONFIG_NOC_DMA_STATS_COLLECT
+	if (noc_dma_sysctl_setup())
+		return -1;
+#endif
+
+	return 0;
+}
+
+module_init(noc_dma_init);
+module_exit(noc_dma_exit);
+/* XXX define_initcall(noc_dma_init); */
+
+
+
+/**
+ * @brief request an arbitrary DMA transfer
+ *
+ * @param src  the source address
+ * @param dst the destination address
+ *
+ * @param x_elem the number of elements in x
+ * @param y_elem the number of elements in y
+ * @param size the element size (BYTE, HALFWORD, WORD, DOUBLEWORD)
+ *
+ * @param x_stride_src the width of stride in source x
+ * @param x_stride_dst the width of stride in destination x
+ *
+ * @param y_stride_src the width of stride in source y
+ * @param y_stride_dst the width of stride in destination y
+ *
+ * @param mtu the maximum transfer unit of a NoC packet
+ *
+ * @param callback a user-supplied callback function, executed on successful
+ *	           transfer
+ * @param userdata a pointer to arbitrary userdata, passed to callback function
+ */
+
+int noc_dma_req_xfer(void *src, void *dst, uint16_t x_elem, uint16_t y_elem,
+		     enum noc_dma_elem_size elem_size,
+		     int16_t x_stride_src, int16_t x_stride_dst,
+		     int16_t y_stride_src, int16_t y_stride_dst,
+		     enum noc_dma_priority dma_priority, uint16_t mtu,
+		     int (*callback)(void *), void *userdata)
+{
+	struct noc_dma_transfer *t;
+
+
+
+	if (!src)
+		return -EINVAL;
+
+	if (!dst)
+		return -EINVAL;
+
+	if(!x_elem)
+		return -EINVAL;
+
+	if(!y_elem)
+		return -EINVAL;
+
+#ifdef CONFIG_NOC_DMA_SAME_MASTER_TRANSFER_WORKAROUND
+	if (((int) dst & 0xF0000000) == ((int) src & 0xF0000000)) {
+		int ret;
+
+		/* at least catch that -.- */
+		if (((int) src & 0xF0000000) ==
+		    (NOC_SCRATCH_BUFFER_BASE & 0xF0000000))
+			return -EINVAL;
+
+		pr_crit(MSG "Transfer withing same memory requested, enabling "
+			    "workaround via scratch buffer. The result of this "
+			    "transfer may not be as desired. You have been "
+			    "warned.\n");
+		ret = noc_dma_req_xfer(src, (void *) NOC_SCRATCH_BUFFER_BASE,
+				       x_elem, y_elem, elem_size,
+				       x_stride_src, x_stride_dst,
+				       y_stride_src, y_stride_dst,
+				       HIGH, mtu, NULL, NULL);
+		if (ret)
+		    return ret;
+
+		/* this would actually work fine, if we had enough scratch
+		 * buffer for all 8 channels or at least some management of it
+		 * let's mark this as TODO
+		 */
+		src = (void *) NOC_SCRATCH_BUFFER_BASE;
+		dma_priority = LOW;
+	}
+#endif
+
+	t = noc_dma_get_transfer_slot();
+	if(!t)
+		return -EBUSY;
+
+	t->src = src;
+	t->dst = dst;
+
+	t->x_elem = x_elem;
+	t->y_elem = y_elem;
+
+	t->elem_size = elem_size;
+
+	t->x_stride_src = x_stride_src;
+	t->x_stride_dst = x_stride_dst;
+
+	t->y_stride_src = y_stride_src;
+	t->y_stride_dst = y_stride_dst;
+
+	t->mtu = mtu;
+
+	/* transfers registered here will be auto-released */
+	t->irq_fwd = BOTH;
+
+	t->priority = dma_priority;
+
+	t->callback = callback;
+	t->userdata = userdata;
+
+	noc_dma_register_transfer(t);
+
+
+	return 0;
+}
+EXPORT_SYMBOL(noc_dma_req_xfer);
+
+
+/**
+ * @brief request a linear array DMA transfer
+ *
+ * @param src  the source address
+ * @param dst the destination address
+ *
+ * @param elem the number of elements
+ * @param size the element size (BYTE, HALFWORD, WORD, DOUBLEWORD)
+ *
+ * @param mtu the maximum transfer unit of a NoC packet
+ *
+ * @param callback a user-supplied callback function, executed on successful
+ *	           transfer
+ * @param userdata a pointer to arbitrary userdata, passed to callback function
+ */
+
+int noc_dma_req_lin_xfer(void *src, void *dst,
+			 uint16_t elem, enum noc_dma_elem_size elem_size,
+			 enum noc_dma_priority dma_priority, uint16_t mtu,
+			 int (*callback)(void *), void *userdata)
+{
+	return noc_dma_req_xfer(src, dst, elem, 1, elem_size,
+				1, 1, 1, 1, LOW, mtu, callback, userdata);
+}
+EXPORT_SYMBOL(noc_dma_req_lin_xfer);
diff --git a/lib/Makefile b/lib/Makefile
index c5c84f0d404b8b95c407b82f28495af0810f52ae..a0d140f8548b76aebc27a67a1597e5ebc55097bb 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -3,3 +3,4 @@ lib-$(CONFIG_MM)	+= mm.o
 lib-$(CONFIG_PAGE_MAP)	+= page.o
 lib-$(CONFIG_AR)	+= ar.o
 lib-$(CONFIG_CHUNK)	+= chunk.o
+lib-y			+= string.o
diff --git a/lib/string.c b/lib/string.c
new file mode 100644
index 0000000000000000000000000000000000000000..c1d537e351599c051d95af905247ae4282e1a1b6
--- /dev/null
+++ b/lib/string.c
@@ -0,0 +1,264 @@
+/*+
+ * @file lib/string.c
+ *
+ * @note some of theses are just wrappers
+ * @todo these functions are very generic, for performance, add architecture
+ * specific implementations
+ */
+
+
+#include <kernel/export.h>
+#include <kernel/types.h>
+
+
+/**
+ * @brief compare two strings s1 and s2
+ *
+ * @returns <0, 0 or > 0 if s1 is less than, matches or greater than s2
+ */
+
+#include <kernel/printk.h>
+int strcmp(const char *s1, const char *s2)
+{
+	unsigned char c1, c2;
+
+	do {
+		c1 = (*(s1++));
+		c2 = (*(s2++));
+
+		if (c1 != c2) {
+			if (c1 < c2)
+				return -1;
+			else
+				return  1;
+		}
+
+	} while (c1);
+
+	return 0;
+}
+EXPORT_SYMBOL(strcmp);
+
+/**
+ * @brief locate first occurrence of a set of characters in string accept
+ *
+ * @param s	 the string to be searched
+ * @param accept the characters to search for
+ *
+ * @returns a pointer to the character in s that matches one of the characters
+ *	    in accept
+ */
+
+char *strpbrk(const char *s, const char *accept)
+{
+	const char *c1, *c2;
+
+
+	for (c1 = s; *c1 != '\0'; c1++)
+		for (c2 = accept; (*c2) != '\0'; c2++)
+			if ((*c1) == (*c2))
+				return (char *) c1;
+
+	return NULL;
+}
+EXPORT_SYMBOL(strpbrk);
+
+
+
+/**
+ * @brief split a string into tokens
+ *
+ * @param stringp the string to be searched
+ * @param delim   the characters to search for
+ *
+ * @note stringp is updated to point past the token
+ *
+ * @returns the original location of stringp
+ */
+
+char *strsep(char **stringp, const char *delim)
+{
+	char *start;
+	char *end;
+
+
+	start = *stringp;
+
+	if (!start)
+		return NULL;
+
+	end = strpbrk(start, delim);
+
+	if (end) {
+		(*end)++;
+		(*end) = '\0';
+	}
+
+	*stringp = end;
+
+	return start;
+}
+EXPORT_SYMBOL(strsep);
+
+
+/**
+ * @brief calculate the length of a string
+ *
+ * @param s the string
+ *
+ * @returns the number of characters in s
+ */
+
+size_t strlen(const char *s)
+{
+	const char *c;
+
+
+	for (c = s; (*c) != '\0'; c++);
+
+	return c - s;
+}
+EXPORT_SYMBOL(strlen);
+
+
+/**
+ * @brief copy a memory area
+ *
+ * @param dest the destination memory area
+ * @param src the source memory area
+ * @param n the number of bytes to copy
+ *
+ * @returns a pointer to dest
+ */
+
+void *memcpy(void *dest, const void *src, size_t n)
+{
+	char *d;
+
+	const char *s;
+
+
+	d = dest;
+	s = src;
+
+	while (n--) {
+		(*d) = (*s);
+		d++;
+		s++;
+	}
+
+	return dest;
+}
+
+EXPORT_SYMBOL(memcpy);
+
+
+
+
+#include <stdarg.h>
+#include <limits.h>
+
+int vsnprintf(char *str, size_t size, const char *format, va_list ap);
+
+/**
+ * @brief print a string into a buffer
+ *
+ * @param str    the destination buffer
+ * @param format the format string buffer
+ * @param ...    arguments to the format string
+ *
+ * @return the number of characters written to buf
+ */
+
+int sprintf(char *str, const char *format, ...)
+{
+	int n;
+	va_list ap;
+
+
+	va_start(ap, format);
+	n = vsnprintf(str, INT_MAX, format, ap);
+	va_end(ap);
+
+	return n;
+}
+EXPORT_SYMBOL(sprintf);
+
+
+/**
+ * @brief check if a character is a white space
+ *
+ * @param c the character to test
+ *
+ * @returns 0 if not a space
+ *
+ * @note c must have the value of an unsigned char or EOF
+ *
+ */
+
+int isspace(int c)
+{
+	if (c == ' ')
+		return 1;
+	
+	if (c == '\t')
+		return 1;
+
+	if (c == '\n')
+		return 1;
+	
+	if (c == '\v')
+		return 1;
+
+        if (c == '\f')
+		return 1;
+
+       	if (c == '\r')
+		return 1;
+
+
+	return 0;
+}
+EXPORT_SYMBOL(isspace);
+
+/**
+ * @brief convert a string to an integer
+ *
+ * @param nptr the string to convert
+ *
+ * @returns res the converted integer
+ */
+
+int atoi(const char *nptr)
+{
+	unsigned int d;
+
+	int res = 0;
+	int neg= 0;
+
+
+	for (; isspace(*nptr); ++nptr);
+
+	if (*nptr == '-') {
+		nptr++;
+		neg = 1;
+	} else if (*nptr == '+') {
+		nptr++;
+	}
+
+	while (1) {
+		d = (*nptr) - '0';
+
+		if (d > 9)
+			break;	
+
+		res = res * 10 + (int) d;
+		nptr++;
+	}
+
+	if (neg)
+		res = -res;
+
+	return res;
+}
+EXPORT_SYMBOL(atoi);
diff --git a/lib/sysctl.c b/lib/sysctl.c
index 03394426c6bec39ac8ac5651d2c8d99364884039..34412d54a38298c1cd016363bf9a79dd814fd5cd 100644
--- a/lib/sysctl.c
+++ b/lib/sysctl.c
@@ -46,24 +46,23 @@
  * @note explicit references to linux source files are not always given
  */
 
+#include <kernel/printk.h>
+#include <kernel/kmem.h>
+#include <kernel/sysctl.h>
+#include <kernel/export.h>
+#include <kernel/string.h>
 
-#include <stdio.h>
-
-#include <stdlib.h>
-#include <string.h>
 #include <errno.h>
 
 #include <list.h>
 
-#include <kernel/sysctl.h>
-
 
+/* our standard sets */
 struct sysset *sys_set;
 struct sysset *driver_set;
 
 
 
-
 static struct sysset *sysset_get(struct sysset *s);
 
 
@@ -158,7 +157,7 @@ struct sysobj *sysobj_create(void)
 	struct sysobj *sobj;
 
 
-	sobj = malloc(sizeof(*sobj));
+	sobj = (struct sysobj *) kmalloc(sizeof(*sobj));
 
 	if (!sobj)
 		return NULL;
@@ -167,7 +166,7 @@ struct sysobj *sysobj_create(void)
 
 	return sobj;
 }
-
+EXPORT_SYMBOL(sysobj_create);
 
 /**
  * @brief add a sysobject and optionally set its sysset as parent
@@ -265,12 +264,12 @@ void sysobj_list_attr(struct sysobj *sobj)
 
 	sattr = sobj->sattr;
 
-	printf("{");
+	printk("{");
 	while ((*sattr)) {
-		printf(" %s", (*sattr)->name);
+		printk(" %s", (*sattr)->name);
 		sattr++;
 	}
-	printf(" }");
+	printk(" }");
 }
 
 
@@ -415,13 +414,11 @@ struct sysset *sysset_create(const char *name,
 {
         struct sysset *sysset;
 
-        sysset = malloc(sizeof(*sysset));
+        sysset = kcalloc(1, sizeof(*sysset));
 
         if (!sysset)
                 return NULL;
 
-	bzero(sysset, sizeof(*sysset));
-
 
         sysobj_set_name(&sysset->sobj, name);
 
@@ -470,7 +467,7 @@ struct sysset *sysset_create_and_add(const char *name,
 __extension__
 struct sysobj *sysset_find_obj(struct sysset *sysset, const char *path)
 {
-	char str[256];
+	char str[256]; /* XXX */
 	char *token;
 
         struct sysobj *s;
@@ -480,7 +477,9 @@ struct sysobj *sysset_find_obj(struct sysset *sysset, const char *path)
 
 	memcpy(str, path, strlen(path) + 1);
 
-	token = strtok(str, "/");
+	token = str;
+
+	strsep(&token, "/");
 
 	/* root node */
 	if(strcmp(sysobj_name(&sysset->sobj), token))
@@ -488,7 +487,7 @@ struct sysobj *sysset_find_obj(struct sysset *sysset, const char *path)
 
 	while (1) {
 
-		token = strtok(NULL, "/");
+		strsep(&token, "/");
 
 		if (!token)
 			return ret;
@@ -532,21 +531,21 @@ void sysset_show_tree(struct sysset *sysset)
 	rec++;
 
 	for(i = 0; i < rec; i++)
-		printf("    ");
+		printk("    ");
 
-	printf("|__ %s\n", sysobj_name(&sysset->sobj));
+	printk("|__ %s\n", sysobj_name(&sysset->sobj));
 
         list_for_each_entry(k, &sysset->list, entry) {
 
 		if (!k->child) {
 
 			for(i = 0; i < rec+1; i++)
-				printf("    ");
-			printf("|__ %s ", sysobj_name(k));
+				printk("    ");
+			printk("|__ %s ", sysobj_name(k));
 
 			sysobj_list_attr(k);
 
-			printf("\n");
+			printk("\n");
 
 		} else {
 			sysset_show_tree(container_of(k->child, struct sysset, sobj));
diff --git a/samples/noc_dma/noc_dma_demo.c b/samples/noc_dma/noc_dma_demo.c
new file mode 100644
index 0000000000000000000000000000000000000000..fd78022356bf754df4dc9a6eed43fd779bf65dea
--- /dev/null
+++ b/samples/noc_dma/noc_dma_demo.c
@@ -0,0 +1,79 @@
+#include <kernel/kmem.h>
+#include <kernel/kernel.h>
+#include <kernel/printk.h>
+#include <noc_dma.h>
+#include <noc.h>
+
+static void noc_dma_2d_print(unsigned long *p, size_t x_sz, size_t y_sz)
+{
+	size_t x;
+	size_t y;
+
+
+	for (y = 0; y < y_sz; y++) {
+		for (x = 0; x < x_sz; x++) {
+			if (p[x + y * x_sz])
+				printk("%02x ", p[x + y * x_sz]);
+			else
+				printk(" . ", p[x + y * x_sz]);
+		}
+		printk("\n");
+	}
+}
+
+
+/**
+ * @brief execute transfer types 1-4 and reproduce equivalent figure
+ * @see MPPB datasheet v4.03, p62, Tbl. 73
+ */
+
+void noc_dma_test_transfers(void)
+{
+	int i;
+
+	unsigned long *src;
+	unsigned long *dst;
+
+
+	const uint16_t x_size = 16;
+	const uint16_t y_size = 19;
+
+
+	src = kcalloc(x_size * y_size, sizeof(unsigned long));
+	BUG_ON(!src);
+
+	dst = (unsigned long *) NOC_SCRATCH_BUFFER_BASE;
+	BUG_ON(!dst);
+
+	/* initialise */
+	for (i = 0; i < (x_size * y_size); i++) {
+		src[i] = i + 1;
+		dst[i] = 0;
+	}
+	/* base */
+	BUG_ON(noc_dma_req_lin_xfer(src, (char *) dst + 0x0, 32, WORD, LOW, 256,
+				    NULL, NULL));
+	/* type 1 */
+	BUG_ON(noc_dma_req_xfer(src, (char *) dst + 0x100, 16, 1, WORD,
+				2, 1, 0, 0, LOW, 256,
+				NULL, NULL));
+
+	/* type 2 */
+	BUG_ON(noc_dma_req_xfer(src, (char *) dst + 0x1C0, 6, 2, WORD,
+				1, 1, 16, 16, LOW, 256,
+				NULL, NULL));
+
+	/* type 3 */
+	BUG_ON(noc_dma_req_xfer(src, (char *) dst + 0x2C0, 4, 2, WORD,
+				1, 16, 4, 1, LOW, 256,
+				NULL, NULL));
+	/* type 4 */
+	BUG_ON(noc_dma_req_xfer(src, (char *) dst + 0x44C, 4, 4, WORD,
+				1, -1, 4, 4, LOW, 256,
+				NULL, NULL));
+	printk("\n--- {DST} ---\n");
+	noc_dma_2d_print(dst, x_size, y_size);
+
+	kfree(src);
+}
+