commit 645ba0533fe9aada5ede5a86a14d4e261932f961
Author: Brian Swetland <swetland@frotz.net>
Date:   Tue, 18 Mar 2014 18:31:44 -0700
Initial checkin of new m3dev repository.
This is based on the tree from http://code.google.com/p/m3dev/
and a bunch of patches from myself, Travis, and Erik that have
existed in various development branches in various places.  On top
of that I started overhauling the build system, ensured everything
actually works on the m3debug boards again (and you can use a
m3debug board to completely re-image another m3debug board), removed
some random personal projects that were included and further tidied
things up.
Diffstat:
97 files changed, 11346 insertions(+), 0 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -0,0 +1,8 @@
+.*
+*~
+out
+bin
+local.mk
+tags
+TAGS
+cscope.out
diff --git a/AUTHORS b/AUTHORS
@@ -0,0 +1,8 @@
+Brian Swetland <swetland@frotz.net>
+  Created this mess in the first place.  Trying to tidy it up a bit.
+
+Travis Geiselbrecht <geist@foobox.com>
+  Build system wisdom, bugfixes, and the "super-m3debug" variant.
+
+Erik Gilling <konkers@konkers.net>
+  lpc134[567] support, library enhancements, and more bugfixes.
diff --git a/LICENSE b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/Makefile b/Makefile
@@ -0,0 +1,82 @@
+## Copyright 2011 Brian Swetland <swetland@frotz.net>
+## 
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+what_to_build:: all
+
+-include local.mk
+
+TOOLCHAIN ?= arm-none-eabi-
+
+TARGET_CC := $(TOOLCHAIN)gcc
+TARGET_LD := $(TOOLCHAIN)ld
+TARGET_AR := $(TOOLCHAIN)ar
+TARGET_OBJCOPY := $(TOOLCHAIN)objcopy
+TARGET_OBJDUMP := $(TOOLCHAIN)objdump
+
+TARGET_CFLAGS := -g -Os -Wall
+TARGET_CFLAGS += -Wno-unused-but-set-variable
+TARGET_CFLAGS += -I. -Iinclude
+TARGET_CFLAGS += -mcpu=cortex-m3 -mthumb -mthumb-interwork
+TARGET_CFLAGS += -ffunction-sections -fdata-sections 
+TARGET_CFLAGS += -fno-builtin -nostdlib
+
+# tell gcc there's not a full libc it can depend on
+# so it won't do thinks like printf("...") -> puts("...")
+TARGET_CFLAGS += -ffreestanding
+TARGET_LFLAGS := --gc-sections
+
+#TARGET_LIBGCC := $(shell $(TARGET_CC) $(TARGET_CFLAGS) -print-libgcc-file-name)
+
+UNAME := $(shell uname)
+UNAME_M := $(shell uname -m)
+
+HOST_CFLAGS := -g -O1 -Wall
+HOST_CFLAGS += -Itools -Iinclude
+
+ifeq ($(UNAME),Darwin)
+HOST_CFLAGS += -I/opt/local/include -L/opt/local/lib
+HOST_LIBS += -lusb-1.0
+endif
+ifeq ($(UNAME),Linux)
+HOST_CFLAGS += -DWITH_THUMB2_DISASSEMBLE=1
+HOST_LIBS += -lusb-1.0
+endif
+
+OUT := out
+BIN := bin
+OUT_HOST_OBJ := $(OUT)/_obj/host
+OUT_TARGET_OBJ := $(OUT)/_obj/target
+
+ALL :=
+DEPS :=
+
+# build system internals
+include build/build.mk
+
+# architecture configurations
+include $(wildcard arch/*/config.mk)
+
+# additional modules
+include $(wildcard */module.mk)
+
+clean::
+	@echo clean
+	@rm -rf $(OUT) $(BIN)
+
+# we generate .d as a side-effect of compiling. override generic rule:
+%.d:
+-include $(DEPS)
+
+all:: $(ALL)
+
diff --git a/README b/README
@@ -0,0 +1,95 @@
+
+===========================================================================
+          m3dev - tools for embedded ARM Cortex M3 development
+===========================================================================
+
+This project is a collection of (linux-centric) tools for working on
+small embedded systems based on the ARM Cortex M3 CPU.
+
+Original work was done with a STM32F103 part, and much more work has been
+done with LPC13xx series parts.
+
+Everything is either Apache 2 or BSD licensed, to allow for easy,
+no-strings-attached, inclusion in your own projects.  Share and enjoy!
+
+
+m3debug
+-------
+
+Firmware for the "m3debug" and "super-m3debug" boards which use the
+LPC1343 as an engine for the ARM Serial Wire Debug Protocol, in 
+conjunction with the debugger and gdb-bridge programs (described
+below).
+
+A schematic is provided for the original (pretty simplistic) m3debug
+board.
+
+
+swdp 
+----
+
+Firmware for the LeafLabs Maple board to act as a USB<->SW-DP bridge,
+to allow host software to use Serial Wire Debug Protocol to access target
+devices.  It should be usable on just about any STM32F103 board.
+
+GPIO 0 -> SWDIO
+GPIO 1 -> SWCLK
+GPIO 5 -> Activity LED
+
+This was the original prototype and has not been tested in a looong
+time now.
+
+
+debugger & gdb-bridge
+---------------------
+
+A simple standalone debugger and a bridge for the GDB remote protocol
+that commicate with a board running swdp.
+
+
+stm32boot
+---------
+
+A tool to download code to RAM or flash, via the stm32f1xx ROM serial
+bootloader.
+
+
+usbmon
+------
+
+Commandline tool to observe the linux usb stack and io transactions
+through /dev/usbmon*
+
+
+Included Third Party Software
+-----------------------------
+
+linenoise.[ch] 
+  excellent tiny commandline editor (BSD license)
+  https://github.com/antirez/linenoise
+
+
+Useful Documents
+----------------
+
+ARM DDI0337E Cortex M3 r1p1 Technical Reference Manual
+  Has useful details on SW-DP and debug peripherals that was removed
+  in later versions of the TRM as "redundant" with other documents.
+
+
+Historical Notes
+----------------
+
+The original work on this started in 2011 and has been tinkered with
+on a number of repositories by a number of people.  Trying to fold things
+back together and tidy up the build system, etc, ended up being a bit of
+a mess resulting in Brian declaring "history bankruptcy" and starting
+a fresh repository in 2014.
+
+Some random side projects have been removed.  An AUTHORS file has been
+added to provide credit for additional contributors to the codebase.
+
+The code base as of the initial checkin, built with gcc 4.8.2 and
+binutils 2.24 (http://github.com/travisg/toolchains) is verified to
+be able to use a m3debug board to flash the bootloader and app image
+to another m3debug board, which then can repeat the process.
diff --git a/arch/arm-cm3/context.S b/arch/arm-cm3/context.S
@@ -0,0 +1,56 @@
+/* context.S
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.syntax unified
+
+.global context_init
+.global handle_m3_svc
+.global handle_m3_pendsv
+
+/* context_init(void (*entry)(void), u32 new_psp, u32_new_msp); 
+ * - setup stack pointers
+ * - switch to PSP, remaining in Priv mode
+ * - jump to entrypoint
+ */
+context_init:
+	msr PSP, r1
+	msr MSP, r2
+	mrs r3, CONTROL
+	orr r3, r3, #2
+	msr CONTROL, r3
+	bx r0
+	b .
+
+handle_m3_pendsv:
+	/* obtain pointer to global state */
+	ldr r12, =(CONFIG_STACKTOP - 0x10)
+	/* safely make current_thread = next_thread */
+	cpsid i
+	ldr r1, [r12, #4]
+	ldr r0, [r12, #0]
+	str r1, [r12, #0]
+	cpsie i
+	/* save previous thread state */
+	mrs r2, psp
+	stmdb r2!, {r4-r11}
+	str r2, [r0, #0]
+	/* restore new thread state */
+	ldr r3, [r1, #0]
+	ldmia r3!, {r4-r11}
+	msr psp, r3
+	bx lr
+
diff --git a/arch/arm-cm3/include/arch/context.h b/arch/arm-cm3/include/arch/context.h
@@ -0,0 +1,72 @@
+/* context.h
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ARM_M3_CONTEXT_H_
+#define _ARM_M3_CONTEXT_H_
+
+struct thread {
+	void *sp;
+	u32 state;
+	struct thread *next;
+	struct thread *prev;
+};
+
+struct global_context {
+	struct thread *current_thread;
+	struct thread *next_thread;
+	u32 unused0;
+	u32 unused1;
+};
+
+#define GLOBAL_CONTEXT ((struct global_context *) (CONFIG_STACKTOP - 0x10))
+
+/* state saved on the stack by hw on handler entry */
+struct hw_state {
+	u32 r0;
+	u32 r1;
+	u32 r2;
+	u32 r3;
+	u32 r12;
+	u32 lr;
+	u32 pc;
+	u32 psr;
+};
+
+struct thread_state {
+	/* saved by software */
+	u32 r4;
+	u32 r5;
+	u32 r6;
+	u32 r7;
+	u32 r8;
+	u32 r9;
+	u32 r10;
+	u32 r11;
+	/* saved by hardware */
+	u32 r0;
+	u32 r1;
+	u32 r2;
+	u32 r3;
+	u32 r12;
+	u32 lr;
+	u32 pc;
+	u32 psr;
+};
+
+void context_init(void (*entry)(void), u32 psp, u32 msp);
+
+#endif
diff --git a/arch/arm-cm3/include/arch/cpu.h b/arch/arm-cm3/include/arch/cpu.h
@@ -0,0 +1,16 @@
+#ifndef _CPU_H_
+#define _CPU_H_
+
+static inline void disable_interrupts(void) {
+	asm("cpsid i" : : : "memory");
+}
+static inline void enable_interrupts(void) {
+	asm("cpsie i" : : : "memory");
+}
+
+void irq_enable(unsigned n);
+void irq_disable(unsigned n);
+
+void irq_set_base(unsigned vector_table_addr);
+
+#endif
diff --git a/arch/arm-cm3/include/arch/interrupts.h b/arch/arm-cm3/include/arch/interrupts.h
@@ -0,0 +1,46 @@
+/* interrupts.h
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ARCH_INTERRUPTS_H_
+#define _ARCH_INTERRUPTS_H_
+
+#define _IRQ(name) v_##name,
+
+enum {
+#if 0
+        v_reset = 1,
+        v_nmi,
+        v_hardfault,
+        v_mmu,
+        v_busfault,
+        v_usagefault,
+        v_reserved_a,
+        v_reserved_b,
+        v_reserved_c,
+        v_reserved_d,
+        v_svc,
+        v_debugmon,
+        v_reserved_e,
+        v_pendsv,
+        v_systick,
+#endif
+#include <arch/irqs.h>
+};
+
+#undef _IRQ
+
+#endif
diff --git a/arch/arm-cm3/nvic.c b/arch/arm-cm3/nvic.c
@@ -0,0 +1,65 @@
+/* nvic.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/io.h>
+
+#define NVIC_ICTR		0xE000E004
+#define NVIC_SET_ENABLE		0xE000E100
+#define NVIC_CLR_ENABLE		0xE000E180
+#define NVIC_SET_PENDING	0xE000E200
+#define NVIC_CLR_PENDING	0xE000E280
+#define NVIC_ACTIVE		0xE000E300
+#define NVIC_PRIORITY		0xE000E400
+#define NVIC_CPUID		0xE000ED00
+#define NVIC_ICSR		0xE000ED04
+#define NVIC_VTOR		0xE000ED08
+#define NVIC_ARCR		0xE000ED0C
+#define NVIC_SYS_CTL		0xE000ED10
+#define NVIC_CFG_CTL		0xE000ED14
+#define NVIC_HANDLER_PRIORITY	0xE000ED18
+#define NVIC_SW_IRQ_TRIGGER	0xE000EF00
+
+void irq_enable(unsigned n) {
+	writel(1 << (n & 31), NVIC_SET_ENABLE + (n >> 5) * 4);
+}
+
+void irq_disable(unsigned n) {
+	writel(1 << (n & 31), NVIC_CLR_ENABLE + (n >> 5) * 4);
+}
+
+void irq_assert(unsigned n) {
+	writel(1 << (n & 31), NVIC_SET_PENDING + (n >> 5) * 4);
+}
+
+void irq_deassert(unsigned n) {
+	writel(1 << (n & 31), NVIC_CLR_PENDING + (n >> 5) * 4);
+}
+
+void irq_set_prio(unsigned n, unsigned p) {
+	unsigned shift = (n & 3) * 8;
+	unsigned reg = NVIC_PRIORITY + (n >> 2) * 4;
+	unsigned val = readl(reg);
+	val = val & (~(0xFF << shift));
+	val = val | ((n & 0xFF) << shift);
+	writel(val, reg);
+}
+
+void irq_set_base(unsigned n) {
+	writel(n, NVIC_VTOR);
+}
+
diff --git a/arch/arm-cm3/start.S b/arch/arm-cm3/start.S
@@ -0,0 +1,84 @@
+/* start.S
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+.section .vectors
+
+.syntax unified
+
+.globl _start
+
+#define VECTOR(name) \
+__vector__##name: ; \
+	.long handle_##name + 1; \
+	.weak handle_##name ; \
+	.thumb_set handle_##name, deadloop
+
+#define _IRQ(name) VECTOR(irq_##name)
+
+_start:
+	.long (CONFIG_STACKTOP - 0x10)
+	.long reset + 1
+	VECTOR(m3_nmi)
+	VECTOR(m3_hardfault)
+	VECTOR(m3_mmu)
+	VECTOR(m3_busfault)
+	VECTOR(m3_usagefault)
+	VECTOR(m3_reserved_a)
+	VECTOR(m3_reserved_b)
+	VECTOR(m3_reserved_c)
+	VECTOR(m3_reserved_d)
+	VECTOR(m3_svc)
+	VECTOR(m3_debugmon)
+	VECTOR(m3_reserved_e)
+	VECTOR(m3_pendsv)
+	VECTOR(m3_systick)
+.globl  _irq_table
+_irq_table:	
+#include <arch/irqs.h>
+
+deadloop:
+	/* unlinked vectors point here */
+	b .
+
+reset:
+	ldr r1, =__data_init__
+	ldr r2, =__data_start__
+	ldr r3, =__data_end__
+	ldr r4, =__bss_end__
+	mov r5, #0
+	
+	/* if data init and start are the same, skip copy */
+	/* this simplifies building-for-ram */
+	cmp r1, r2
+	bne copydata
+	mov r2, r3
+	b zerobss
+
+copydata:
+	cmp r2, r3
+	beq zerobss
+	ldr r0, [r1], #4
+	str r0, [r2], #4
+	b copydata
+zerobss:
+	cmp r2, r4
+	beq tmp_main
+	str r5, [r2], #4
+	b zerobss
+
+tmp_main:
+	b main
diff --git a/arch/lpc13xx/config.mk b/arch/lpc13xx/config.mk
@@ -0,0 +1,43 @@
+
+#           name        arch       rambase    ramsize    flashbase  flashsize  linkscript
+$(call chip,lpc1342-ram,lpc13xx_v1,0x10000000,0x00001000,0x00000000,0x00000000,ram)
+$(call chip,lpc1342-rom,lpc13xx_v1,0x10000000,0x00001000,0x00000000,0x00004000,rom)
+
+$(call chip,lpc1343-ram,lpc13xx_v1,0x10000000,0x00002000,0x00000000,0x00000000,ram)
+$(call chip,lpc1343-rom,lpc13xx_v1,0x10000000,0x00002000,0x00000000,0x00008000,rom)
+$(call chip,lpc1343-blr,lpc13xx_v1,0x10001c00,0x00000400,0x00000000,0x00001000,rom)
+$(call chip,lpc1343-app,lpc13xx_v1,0x10000000,0x00002000,0x00001000,0x00007000,rom)
+
+$(call chip,lpc1345-rom,lpc13xx_v2,0x10000000,0x00002000,0x00000000,0x00008000,rom)
+$(call chip,lpc1347-rom,lpc13xx_v2,0x10000000,0x00002000,0x00000000,0x00008000,rom)
+
+
+ARCH_lpc13xx_v1_CFLAGS := -Iarch/lpc13xx/include
+ARCH_lpc13xx_v1_CFLAGS += -Iarch/lpc13xx/include-v1
+ARCH_lpc13xx_v1_CFLAGS += -Iarch/arm-cm3/include
+ARCH_lpc13xx_v1_CFLAGS += -DCONFIG_STACKTOP=0x10001f00
+ARCH_lpc13xx_v1_START := arch/arm-cm3/start.o
+ARCH_lpc13xx_v1_CONFIG := LPC13XX_V1=1
+
+ARCH_lpc13xx_v2_CFLAGS := -Iarch/lpc13xx/include
+ARCH_lpc13xx_v2_CFLAGS += -Iarch/lpc13xx/include-v2
+ARCH_lpc13xx_v2_CFLAGS += -Iarch/arm-cm3/include
+ARCH_lpc13xx_v2_CFLAGS += -DCONFIG_STACKTOP=0x10001f00
+ARCH_lpc13xx_v2_START := arch/arm-cm3/start.o
+ARCH_lpc13xx_v2_CONFIG := LPC13XX_V2=1
+
+ARCH_lpc13xx_OBJS := \
+	arch/lpc13xx/init.o \
+	arch/lpc13xx/iap.o \
+	arch/lpc13xx/reboot.o \
+	arch/lpc13xx/serial.o
+
+ARCH_lpc13xx_v1_OBJS := \
+	$(ARCH_lpc13xx_OBJS) \
+	arch/lpc13xx/gpio-v1.o \
+	arch/lpc13xx/usb-v1.o
+
+ARCH_lpc13xx_v2_OBJS := \
+	$(ARCH_lpc13xx_OBJS) \
+	arch/lpc13xx/gpio-v2.o \
+	arch/lpc13xx/usb-v2.o
diff --git a/arch/lpc13xx/gpio-v1.c b/arch/lpc13xx/gpio-v1.c
@@ -0,0 +1,88 @@
+/* gpio.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/lib.h>
+#include <fw/io.h>
+
+#include <arch/hardware.h>
+
+void gpio_cfg_dir(unsigned n, unsigned cfg) {
+	unsigned addr = GPIODIR(0) | (n & 0x30000);
+	n &= 0xFFF;
+	if (cfg & GPIO_CFG_OUT) {
+		writel(readl(addr) | n, addr);
+	} else {
+		writel(readl(addr) & (~n), addr);
+	}
+}
+
+void gpio_cfg_irq(unsigned n, unsigned cfg) {
+	unsigned off = (n & 0x30000);
+	unsigned addr;
+	n &= 0xFFF;
+
+	addr = GPIOBOTHEDGES(0) + off;
+	if (cfg & GPIO_CFG_BOTH) {
+		writel(readl(addr) | n, addr);
+	} else {
+		writel(readl(addr) & (~n), addr);
+		addr = GPIOPOLARITY(0) + off;
+		if (cfg & GPIO_CFG_NEGATIVE) {
+			writel(readl(addr) & (~n), addr);
+		} else {
+			writel(readl(addr) | n, addr);
+		}
+	}
+
+	addr = GPIOLEVEL(0) + off;
+	if (cfg & GPIO_CFG_EDGE) {
+		writel(readl(addr) & (~n), addr);
+	} else {
+		writel(readl(addr) | n, addr);
+	}
+}
+
+int gpio_irq_check(unsigned n) {
+	unsigned off = (n & 0x30000);
+	return (readl(GPIORAWISR(0) + off) & (n & 0xFFF)) != 0;
+}
+
+void gpio_irq_clear(unsigned n) {
+	unsigned off = (n & 0x30000);
+	writel(n & 0xFFF, GPIOICR(0) + off);
+}
+
+void gpio_set(unsigned n) {
+	unsigned addr = GPIOBASE(0) | (n & 0x30000) | ((n & 0xFFF) << 2);
+	writel(0xFFFFFFFF, addr);
+}
+
+void gpio_clr(unsigned n) {
+	unsigned addr = GPIOBASE(0) | (n & 0x30000) | ((n & 0xFFF) << 2);
+	writel(0, addr);
+}
+
+void gpio_wr(unsigned n, unsigned val) {
+	unsigned addr = GPIOBASE(0) | (n & 0x30000) | ((n & 0xFFF) << 2);
+	writel(val ? 0xFFFFFFFF : 0, addr);
+}
+
+int gpio_rd(unsigned n) {
+	unsigned addr = GPIODATA(0) | (n & 0x30000);
+	n &= 0xFFF;
+	return (readl(addr) & n) != 0;
+}
diff --git a/arch/lpc13xx/gpio-v2.c b/arch/lpc13xx/gpio-v2.c
@@ -0,0 +1,94 @@
+/* gpio.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/lib.h>
+#include <fw/io.h>
+
+#include <arch/hardware.h>
+
+void gpio_cfg_dir(unsigned n, unsigned cfg) {
+	unsigned addr = GPIO_DIR(GPIO_PORT(n));
+	unsigned val = readl(addr);
+
+	if (cfg & GPIO_CFG_OUT)
+		val |= 1 << GPIO_NUM(n);
+	else
+		val &= ~(1 << GPIO_NUM(n));
+
+	writel(val, addr);
+}
+
+void gpio_cfg_irq(unsigned n, unsigned cfg) {
+	unsigned irq;
+	irq = (cfg >> 8) & 0x7;
+
+	if (cfg & GPIO_CFG_EDGE)
+		clr_set_reg(GPIO_ISEL, 1 << irq, 0);
+	else
+		clr_set_reg(GPIO_ISEL, 0, 1 << irq);
+
+	if (cfg & GPIO_CFG_POSITIVE)
+		writel(1 << irq, GPIO_SIENR);
+	else
+		writel(1 << irq, GPIO_CIENR);
+
+
+	if (cfg & GPIO_CFG_NEGATIVE)
+		writel(1 << irq, GPIO_SIENF);
+	else
+		writel(1 << irq, GPIO_CIENF);
+
+	writel(GPIO_PORT(n) * 24 + SCB_PINTSEL_INTPIN(GPIO_NUM(n)),
+	       SCB_PINTSEL(irq));
+}
+
+int gpio_irq_check(unsigned n) {
+	asm("b .");
+	return 0;
+#if 0
+	unsigned off = (n & 0x30000);
+	return (readl(GPIORAWISR(0) + off) & (n & 0xFFF)) != 0;
+#endif
+}
+
+void gpio_irq_clear(unsigned n) {
+	asm("b .");
+#if 0
+	unsigned off = (n & 0x30000);
+	writel(n & 0xFFF, GPIOICR(0) + off);
+#endif
+}
+
+void gpio_set(unsigned n) {
+	writel(1 << GPIO_NUM(n), GPIO_SET(GPIO_PORT(n)));
+}
+
+void gpio_clr(unsigned n) {
+	writel(1 << GPIO_NUM(n), GPIO_CLR(GPIO_PORT(n)));
+}
+
+void gpio_wr(unsigned n, unsigned val) {
+	/* TODO: use the word set regs */
+	if (val)
+		gpio_set(n);
+	else
+		gpio_clr(n);
+}
+
+int gpio_rd(unsigned n) {
+	return !!(readl(GPIO_W(GPIO_PORT(n) * 32 + GPIO_NUM(n))));
+}
diff --git a/arch/lpc13xx/iap.c b/arch/lpc13xx/iap.c
@@ -0,0 +1,48 @@
+#include <fw/types.h>
+
+#include <arch/cpu.h>
+#include <arch/iap.h>
+
+#define IAP_OP_PREPARE		50
+#define IAP_OP_WRITE		51
+#define IAP_OP_ERASE_SECTOR	52
+#define IAP_OP_BLANK_CHECK	53
+#define IAP_OP_READ_PART_ID	54
+#define IAP_OP_READ_BOOT_VERS	55
+#define IAP_OP_COMPARE		56
+#define IAP_OP_REINVOKE_ISP	57
+#define IAP_OP_READ_UID		58
+#define IAP_OP_ERASE_PAGE	59
+#define IAP_OP_EEPROM_WRITE	61
+#define IAP_OP_EEPROM_READ	62
+
+void (*romcall)(u32 *in, u32 *out) = (void*) 0x1fff1ff1;
+
+static int iap_eeprom_op(u32 op, void *buf, unsigned addr, int len)
+{
+	u32 in[5];
+	u32 out[4];
+
+	in[0] = op;
+	in[1] = addr;
+	in[2] = (u32) buf;
+	in[3] = len;
+	in[4] = 48000;
+
+	disable_interrupts();
+	romcall(in, out);
+	enable_interrupts();
+
+	return -((int)out[0]);
+}
+
+int iap_eeprom_read(void *buf, unsigned addr, int len)
+{
+	return iap_eeprom_op(IAP_OP_EEPROM_READ, buf, addr, len);
+}
+
+int iap_eeprom_write(unsigned addr, void *buf, int len)
+{
+	return iap_eeprom_op(IAP_OP_EEPROM_WRITE, buf, addr, len);
+}
+
diff --git a/arch/lpc13xx/include-v1/arch/iocon.h b/arch/lpc13xx/include-v1/arch/iocon.h
@@ -0,0 +1,72 @@
+
+#define IOCON_FUNC_0		(0 << 0)
+#define IOCON_FUNC_1		(1 << 0)
+#define IOCON_FUNC_2		(2 << 0)
+#define IOCON_FUNC_3		(3 << 0)
+#define IOCON_PULL_DOWN		(1 << 3)
+#define IOCON_PULL_UP		(2 << 3)
+#define IOCON_REPEATER		(3 << 3)
+#define IOCON_HYSTERESIS	(1 << 5)
+#define IOCON_ANALOG		(3 << 6)
+#define IOCON_DIGITAL		(2 << 6)
+#define IOCON_PSEUDO_OPENDRAIN	(1 << 10)
+
+#define IOCON_PIO0_0	0x4004400C /* RESETn / PIO */
+#define IOCON_PIO0_1	0x40044010 /* PIO / CLKOUT / CT32B0_MAT2 / USB_FTOGGLE */
+#define IOCON_PIO0_2	0x4004401C /* PIO / SSEL0 / CT16B0_CAP0 */
+#define IOCON_PIO0_3	0x4004402C /* PIO / USB_VBUS */
+#define IOCON_PIO0_4	0x40044030 /* PIO / I2C_SCL */
+#define IOCON_PIO0_5	0x40044034 /* PIO / I2C_SDA */
+#define IOCON_PIO0_6	0x4004404C /* PIO / USB_CONNECTn / SCK0+ */
+#define IOCON_PIO0_7	0x40044050 /* PIO / CTSn */
+#define IOCON_PIO0_8	0x40044060 /* PIO / MISO0 / CT16B0_MAT0 */
+#define IOCON_PIO0_9	0x40044064 /* PIO / MOSI0 / CT16B0_MAT1 / SWO */
+#define IOCON_PIO0_10	0x40044068 /* SWCLK / PIO / SCK0+ / CT16B0_MAT2 */
+#define IOCON_PIO0_11	0x40044074 /* reserved / PIO / AD0 / CT32B0_MAT3 */
+
+#define IOCON_PIO1_0	0x40044078 /* reserved / PIO / AD1 / CT32B1_CAP0 */
+#define IOCON_PIO1_1	0x4004407C /* reserved / PIO / AD2 / CT32B1_MAT0 */
+#define IOCON_PIO1_2	0x40044080 /* reserved / PIO / AD3 / CT32B1_MAT1 */
+#define IOCON_PIO1_3	0x40044090 /* SWDIO / PIO / AD4 / CT32B1_MAT2 */
+#define IOCON_PIO1_4	0x40044094 /* PIO / AD5 / CT32B1_MAT3 */
+#define IOCON_PIO1_5	0x400440A0 /* PIO / RTSn / CT32B0_CAP0 */
+#define IOCON_PIO1_6	0x400440A4 /* PIO / RXD / CT32B0_MAT0 */
+#define IOCON_PIO1_7	0x400440A8 /* PIO / TXD / CT32B0_MAT1 */
+#define IOCON_PIO1_8	0x40044014 /* PIO / CT16B1_CAP0 */
+#define IOCON_PIO1_9	0x40044038 /* PIO / CT16B1_MAT0 */
+#define IOCON_PIO1_10	0x4004406C /* PIO / AD6 / CT16B1_MAT1 */
+#define IOCON_PIO1_11	0x40044098 /* PIO / AD7 */
+
+#define IOCON_PIO2_0	0x40044008 /* PIO / DTRn / SSEL1 */
+#define IOCON_PIO2_1	0x40044028 /* PIO / DSRn+ / SCK1 */
+#define IOCON_PIO2_2	0x4004405C /* PIO / DCDn+ / MISO1 */
+#define IOCON_PIO2_3	0x4004408C /* PIO / RIn+ / MOSI1 */
+#define IOCON_PIO2_4	0x40044040 /* PIO */
+#define IOCON_PIO2_5	0x40044044 /* PIO */
+#define IOCON_PIO2_6	0x40044000
+#define IOCON_PIO2_7	0x40044020 /* PIO */
+#define IOCON_PIO2_8	0x40044024 /* PIO */
+#define IOCON_PIO2_9	0x40044054 /* PIO */
+#define IOCON_PIO2_10	0x40044058 /* PIO */
+#define IOCON_PIO2_11	0x40044070 /* PIO / SCK0+ */
+
+#define IOCON_PIO3_0	0x40044084 /* PIO / DTRn */
+#define IOCON_PIO3_1	0x40044088 /* PIO / DSRn+ */
+#define IOCON_PIO3_2	0x4004409C /* PIO / DCDn+ */
+#define IOCON_PIO3_3	0x400440AC /* PIO / RIn+ */
+#define IOCON_PIO3_4	0x4004403C /* PIO */
+#define IOCON_PIO3_5	0x40044048 /* PIO */
+
+#define IOCON_SCK0_LOC		0x400440B0 /* + where is SCK0? */
+#define IOCON_SCK0_PIO0_10	0
+#define IOCON_SCK0_PIO2_11	1
+#define IOCON_SCK0_PIO0_6	2
+#define IOCON_DSR_LOC		0x400440B4
+#define IOCON_DSR_PIO2_1	0
+#define IOCON_DSR_PIO3_1	1
+#define IOCON_DCD_LOC		0x400440B8
+#define IOCON_DCD_PIO2_2	0
+#define IOCON_DCD_PIO3_2	1
+#define IOCON_RI_LOC		0x400440BC
+#define IOCON_RI_PIO2_3		0
+#define IOCON_RI_PIO3_3		1
diff --git a/arch/lpc13xx/include-v1/arch/irqs.h b/arch/lpc13xx/include-v1/arch/irqs.h
@@ -0,0 +1,60 @@
+#ifdef _IRQ
+	_IRQ(wakeup_0_0)
+	_IRQ(wakeup_0_1)
+	_IRQ(wakeup_0_2)
+	_IRQ(wakeup_0_3)
+	_IRQ(wakeup_0_4)
+	_IRQ(wakeup_0_5)
+	_IRQ(wakeup_0_6)
+	_IRQ(wakeup_0_7)
+	_IRQ(wakeup_0_8)
+	_IRQ(wakeup_0_9)
+	_IRQ(wakeup_0_10)
+	_IRQ(wakeup_0_11)
+	_IRQ(wakeup_1_0)
+	_IRQ(wakeup_1_1)
+	_IRQ(wakeup_1_2)
+	_IRQ(wakeup_1_3)
+	_IRQ(wakeup_1_4)
+	_IRQ(wakeup_1_5)
+	_IRQ(wakeup_1_6)
+	_IRQ(wakeup_1_7)
+	_IRQ(wakeup_1_8)
+	_IRQ(wakeup_1_9)
+	_IRQ(wakeup_1_10)
+	_IRQ(wakeup_1_11)
+	_IRQ(wakeup_2_0)
+	_IRQ(wakeup_2_1)
+	_IRQ(wakeup_2_2)
+	_IRQ(wakeup_2_3)
+	_IRQ(wakeup_2_4)
+	_IRQ(wakeup_2_5)
+	_IRQ(wakeup_2_6)
+	_IRQ(wakeup_2_7)
+	_IRQ(wakeup_2_8)
+	_IRQ(wakeup_2_9)
+	_IRQ(wakeup_2_10)
+	_IRQ(wakeup_2_11)
+	_IRQ(wakeup_3_0)
+	_IRQ(wakeup_3_1)
+	_IRQ(wakeup_3_2)
+	_IRQ(wakeup_3_3)
+	_IRQ(i2c0)
+	_IRQ(ct16b0)
+	_IRQ(ct16b1)
+	_IRQ(ct32b0)
+	_IRQ(ct32b1)
+	_IRQ(ssp0)
+	_IRQ(uart)
+	_IRQ(usb_irq)
+	_IRQ(usb_fiq)
+	_IRQ(adc)
+	_IRQ(wdt)
+	_IRQ(bod)
+	_IRQ(reserved0)
+	_IRQ(gpio3)
+	_IRQ(gpio2)
+	_IRQ(gpio1)
+	_IRQ(gpio0)
+	_IRQ(ssp1)
+#endif
diff --git a/arch/lpc13xx/include-v2/arch/iocon.h b/arch/lpc13xx/include-v2/arch/iocon.h
@@ -0,0 +1,262 @@
+
+#define IOCON_FUNC_0		(0 << 0)
+#define IOCON_FUNC_1		(1 << 0)
+#define IOCON_FUNC_2		(2 << 0)
+#define IOCON_FUNC_3		(3 << 0)
+#define IOCON_PULL_DOWN		(1 << 3)
+#define IOCON_PULL_UP		(2 << 3)
+#define IOCON_REPEATER		(3 << 3)
+#define IOCON_HYSTERESIS	(1 << 5)
+#define IOCON_ANALOG		(0 << 7)
+#define IOCON_DIGITAL		(1 << 7)
+#define IOCON_PSEUDO_OPENDRAIN	(1 << 10)
+
+#define IOCON_PIO0_0		0x40044000
+#define   IOCON_PIO0_0_FUNC_RESET		IOCON_FUNC_0
+#define   IOCON_PIO0_0_FUNC_PIO0_0		IOCON_FUNC_1
+
+#define IOCON_PIO0_1		0x40044004
+#define   IOCON_PIO0_1_FUNC_PIO0_1		IOCON_FUNC_0
+#define   IOCON_PIO0_1_FUNC_CLKOUT		IOCON_FUNC_1
+#define   IOCON_PIO0_1_FUNC_CT32B0_MAT2	IOCON_FUNC_2
+#define   IOCON_PIO0_1_FUNC_USB_FTOGGLE	IOCON_FUNC_3
+
+#define IOCON_PIO0_2		0x40044008
+#define   IOCON_PIO0_2_FUNC_PIO0_2		IOCON_FUNC_0
+#define   IOCON_PIO0_2_FUNC_SSEL0		IOCON_FUNC_1
+#define   IOCON_PIO0_2_FUNC_CT16B0_CAP0	IOCON_FUNC_2
+
+#define IOCON_PIO0_3		0x4004400c
+#define   IOCON_PIO0_3_FUNC_PIO0_3		IOCON_FUNC_0
+#define   IOCON_PIO0_3_FUNC_USB_VBUS		IOCON_FUNC_1
+
+#define IOCON_PIO0_4		0x40044010
+#define   IOCON_PIO0_4_FUNC_PIO0_4		IOCON_FUNC_0
+#define   IOCON_PIO0_4_FUNC_I2C_SCL		IOCON_FUNC_1
+
+#define IOCON_PIO0_5		0x40044014
+#define   IOCON_PIO0_5_FUNC_PIO0_5		IOCON_FUNC_0
+#define   IOCON_PIO0_5_FUNC_I2C_SDA		IOCON_FUNC_1
+
+#define IOCON_PIO0_6		0x40044018
+#define   IOCON_PIO0_6_FUNC_PIO0_6		IOCON_FUNC_0
+#define   IOCON_PIO0_6_FUNC_USB_CONNECT	IOCON_FUNC_1
+#define   IOCON_PIO0_6_FUNC_SCK0		IOCON_FUNC_2
+
+#define IOCON_PIO0_7		0x4004401c
+#define   IOCON_PIO0_7_FUNC_PIO0_7		IOCON_FUNC_0
+#define   IOCON_PIO0_7_FUNC_CTS		IOCON_FUNC_1
+
+#define IOCON_PIO0_8		0x40044020
+#define   IOCON_PIO0_8_FUNC_PIO0_8		IOCON_FUNC_0
+#define   IOCON_PIO0_8_FUNC_MISO0		IOCON_FUNC_1
+#define   IOCON_PIO0_8_FUNC_CT16B0_MAT0	IOCON_FUNC_2
+#define   IOCON_PIO0_8_FUNC_ARM_TRACE_CLK	IOCON_FUNC_3
+
+#define IOCON_PIO0_9		0x40044024
+#define   IOCON_PIO0_9_FUNC_PIO0_9		IOCON_FUNC_0
+#define   IOCON_PIO0_9_FUNC_MOSI0		IOCON_FUNC_1
+#define   IOCON_PIO0_9_FUNC_CT16B0_MAT1	IOCON_FUNC_2
+#define   IOCON_PIO0_9_FUNC_ARM_TRACE_SWV	IOCON_FUNC_3
+
+#define IOCON_PIO0_10		0x40044028
+#define   IOCON_PIO0_10_FUNC_SWCLK		IOCON_FUNC_0
+#define   IOCON_PIO0_10_FUNC_PIO0_10		IOCON_FUNC_1
+#define   IOCON_PIO0_10_FUNC_SCK0		IOCON_FUNC_2
+#define   IOCON_PIO0_10_FUNC_CT16B0_MAT2	IOCON_FUNC_3
+
+#define IOCON_PIO0_11		0x4004402c
+#define   IOCON_PIO0_11_FUNC_TDI		IOCON_FUNC_0
+#define   IOCON_PIO0_11_FUNC_PIO0_11		IOCON_FUNC_1
+#define   IOCON_PIO0_11_FUNC_AD0		IOCON_FUNC_2
+#define   IOCON_PIO0_11_FUNC_CT32B0_MAT3	IOCON_FUNC_3
+
+#define IOCON_PIO0_12		0x40044030
+#define   IOCON_PIO0_12_FUNC_TMS		IOCON_FUNC_0
+#define   IOCON_PIO0_12_FUNC_PIO0_12		IOCON_FUNC_1
+#define   IOCON_PIO0_12_FUNC_AD1		IOCON_FUNC_2
+#define   IOCON_PIO0_12_FUNC_CT32B1_CAP0	IOCON_FUNC_3
+
+#define IOCON_PIO0_13		0x40044034
+#define   IOCON_PIO0_13_FUNC_TDO		IOCON_FUNC_0
+#define   IOCON_PIO0_13_FUNC_PIO0_13		IOCON_FUNC_1
+#define   IOCON_PIO0_13_FUNC_AD2		IOCON_FUNC_2
+#define   IOCON_PIO0_13_FUNC_CT32B1_MAT0	IOCON_FUNC_3
+
+#define IOCON_PIO0_14		0x40044038
+#define   IOCON_PIO0_14_FUNC_TRST		IOCON_FUNC_0
+#define   IOCON_PIO0_14_FUNC_PIO0_14		IOCON_FUNC_1
+#define   IOCON_PIO0_14_FUNC_AD3		IOCON_FUNC_2
+#define   IOCON_PIO0_14_FUNC_CT32B1_MAT1	IOCON_FUNC_3
+
+#define IOCON_PIO0_15		0x4004403c
+#define   IOCON_PIO0_15_FUNC_SWDIO		IOCON_FUNC_0
+#define   IOCON_PIO0_15_FUNC_PIO0_15		IOCON_FUNC_1
+#define   IOCON_PIO0_15_FUNC_AD4		IOCON_FUNC_2
+#define   IOCON_PIO0_15_FUNC_CT32B1_MAT2	IOCON_FUNC_3
+
+#define IOCON_PIO0_16		0x40044040
+#define   IOCON_PIO0_16_FUNC_PIO0_16		IOCON_FUNC_0
+#define   IOCON_PIO0_16_FUNC_AD5		IOCON_FUNC_1
+#define   IOCON_PIO0_16_FUNC_CT32B1_MAT3	IOCON_FUNC_2
+
+#define IOCON_PIO0_17		0x40044044
+#define   IOCON_PIO0_17_FUNC_PIO0_17		IOCON_FUNC_0
+#define   IOCON_PIO0_17_FUNC_RTS		IOCON_FUNC_1
+#define   IOCON_PIO0_17_FUNC_CT32B0_CAP0	IOCON_FUNC_2
+#define   IOCON_PIO0_17_FUNC_SCLK		IOCON_FUNC_3
+
+#define IOCON_PIO0_18		0x40044048
+#define   IOCON_PIO0_18_FUNC_PIO0_18		IOCON_FUNC_0
+#define   IOCON_PIO0_18_FUNC_RXD		IOCON_FUNC_1
+#define   IOCON_PIO0_18_FUNC_CT32B0_MAT0	IOCON_FUNC_2
+
+#define IOCON_PIO0_19		0x4004404c
+#define   IOCON_PIO0_19_FUNC_PIO0_19		IOCON_FUNC_0
+#define   IOCON_PIO0_19_FUNC_TXD		IOCON_FUNC_1
+#define   IOCON_PIO0_19_FUNC_CT32B0_MAT1	IOCON_FUNC_2
+
+#define IOCON_PIO0_20		0x40044050
+#define   IOCON_PIO0_20_FUNC_PIO0_20		IOCON_FUNC_0
+#define   IOCON_PIO0_20_FUNC_CT16B1_CAP0	IOCON_FUNC_1
+
+#define IOCON_PIO0_21		0x40044054
+#define   IOCON_PIO0_21_FUNC_PIO0_21		IOCON_FUNC_0
+#define   IOCON_PIO0_21_FUNC_CT16B1_MAT0	IOCON_FUNC_1
+#define   IOCON_PIO0_21_FUNC_MOSI1		IOCON_FUNC_2
+
+#define IOCON_PIO0_22		0x40044058
+#define   IOCON_PIO0_22_FUNC_PIO0_22		IOCON_FUNC_0
+#define   IOCON_PIO0_22_FUNC_AD6		IOCON_FUNC_1
+#define   IOCON_PIO0_22_FUNC_CT16B1_MAT1	IOCON_FUNC_2
+#define   IOCON_PIO0_22_FUNC_MISO1		IOCON_FUNC_3
+
+#define IOCON_PIO0_23		0x4004405c
+#define   IOCON_PIO0_23_FUNC_PIO0_32		IOCON_FUNC_0
+#define   IOCON_PIO0_23_FUNC_AD7		IOCON_FUNC_1
+
+#define IOCON_PIO1_0		0x40044060
+#define   IOCON_PIO1_0_FUNC_PIO1_0		IOCON_FUNC_0
+#define   IOCON_PIO1_0_FUNC_CT32B1_MAT0	IOCON_FUNC_1
+
+#define IOCON_PIO1_1		0x40044064
+#define   IOCON_PIO1_1_FUNC_PIO1_1		IOCON_FUNC_0
+#define   IOCON_PIO1_1_FUNC_CT32B1_MAT1	IOCON_FUNC_1
+
+#define IOCON_PIO1_2		0x40044068
+#define   IOCON_PIO1_2_FUNC_PIO1_2		IOCON_FUNC_0
+#define   IOCON_PIO1_2_FUNC_CT32B1_MAT2	IOCON_FUNC_1
+
+#define IOCON_PIO1_3		0x4004406c
+#define   IOCON_PIO1_3_FUNC_PIO1_3		IOCON_FUNC_0
+#define   IOCON_PIO1_3_FUNC_CT32B1_MAT3	IOCON_FUNC_1
+
+#define IOCON_PIO1_4		0x40044070
+#define   IOCON_PIO1_4_FUNC_PIO1_4		IOCON_FUNC_0
+#define   IOCON_PIO1_4_FUNC_CT32B1_CAP0	IOCON_FUNC_1
+
+#define IOCON_PIO1_5		0x40044074
+#define   IOCON_PIO1_5_FUNC_PIO1_5		IOCON_FUNC_0
+#define   IOCON_PIO1_5_FUNC_CT32B1_CAP1	IOCON_FUNC_1
+
+#define IOCON_PIO1_7		0x4004407c
+#define   IOCON_PIO1_7_FUNC_PIO1_7		IOCON_FUNC_0
+
+#define IOCON_PIO1_8		0x40044080
+#define   IOCON_PIO1_8_FUNC_PIO1_8		IOCON_FUNC_0
+
+#define IOCON_PIO1_10		0x40044088
+#define   IOCON_PIO1_10_FUNC_PIO1_10		IOCON_FUNC_0
+
+#define IOCON_PIO1_11		0x4004408c
+#define   IOCON_PIO1_11_FUNC_PIO1_11		IOCON_FUNC_0
+
+#define IOCON_PIO1_13		0x40044094
+#define   IOCON_PIO1_13_FUNC_PIO1_13		IOCON_FUNC_0
+#define   IOCON_PIO1_13_FUNC_DTR		IOCON_FUNC_1
+#define   IOCON_PIO1_13_FUNC_CT16B0_MAT0	IOCON_FUNC_2
+#define   IOCON_PIO1_13_FUNC_TXD		IOCON_FUNC_3
+
+#define IOCON_PIO1_14		0x40044098
+#define   IOCON_PIO1_14_FUNC_PIO1_14		IOCON_FUNC_0
+#define   IOCON_PIO1_14_FUNC_DSR		IOCON_FUNC_1
+#define   IOCON_PIO1_14_FUNC_CT16B0_MAT1	IOCON_FUNC_2
+#define   IOCON_PIO1_14_FUNC_RXD		IOCON_FUNC_3
+
+#define IOCON_PIO1_15		0x4004409c
+#define   IOCON_PIO1_15_FUNC_PIO1_15		IOCON_FUNC_0
+#define   IOCON_PIO1_15_FUNC_DCD		IOCON_FUNC_1
+#define   IOCON_PIO1_15_FUNC_CT16B0_MAT2	IOCON_FUNC_2
+#define   IOCON_PIO1_15_FUNC_SCK1		IOCON_FUNC_3
+
+#define IOCON_PIO1_16		0x400440a0
+#define   IOCON_PIO1_16_FUNC_PIO1_16		IOCON_FUNC_0
+#define   IOCON_PIO1_16_FUNC_RI		IOCON_FUNC_1
+#define   IOCON_PIO1_16_FUNC_CT16B0_CAP0	IOCON_FUNC_2
+
+#define IOCON_PIO1_17		0x400440a4
+#define   IOCON_PIO1_17_FUNC_PIO1_17		IOCON_FUNC_0
+#define   IOCON_PIO1_17_FUNC_CT16B0_CAP1	IOCON_FUNC_1
+#define   IOCON_PIO1_17_FUNC_RXD		IOCON_FUNC_2
+
+#define IOCON_PIO1_18		0x400440a8
+#define   IOCON_PIO1_18_FUNC_PIO1_18		IOCON_FUNC_0
+#define   IOCON_PIO1_18_FUNC_CT16B1_CAP1	IOCON_FUNC_1
+#define   IOCON_PIO1_18_FUNC_TXD		IOCON_FUNC_2
+
+#define IOCON_PIO1_19		0x400440ac
+#define   IOCON_PIO1_19_FUNC_PIO1_19		IOCON_FUNC_0
+#define   IOCON_PIO1_19_FUNC_DTR		IOCON_FUNC_1
+#define   IOCON_PIO1_19_FUNC_SSEL1		IOCON_FUNC_2
+
+#define IOCON_PIO1_20		0x400440b0
+#define   IOCON_PIO1_20_FUNC_PIO1_20		IOCON_FUNC_0
+#define   IOCON_PIO1_20_FUNC_DSR		IOCON_FUNC_1
+#define   IOCON_PIO1_20_FUNC_SCK1		IOCON_FUNC_2
+
+#define IOCON_PIO1_21		0x400440b4
+#define   IOCON_PIO1_21_FUNC_PIO1_21		IOCON_FUNC_0
+#define   IOCON_PIO1_21_FUNC_DCD		IOCON_FUNC_1
+#define   IOCON_PIO1_21_FUNC_MISO1		IOCON_FUNC_2
+
+#define IOCON_PIO1_22		0x400440b8
+#define   IOCON_PIO1_22_FUNC_PIO1_22		IOCON_FUNC_0
+#define   IOCON_PIO1_22_FUNC_RI		IOCON_FUNC_1
+#define   IOCON_PIO1_22_FUNC_MOSI1		IOCON_FUNC_2
+
+#define IOCON_PIO1_23		0x400440bc
+#define   IOCON_PIO1_23_FUNC_PIO1_23		IOCON_FUNC_0
+#define   IOCON_PIO1_23_FUNC_CT16B1_MAT1	IOCON_FUNC_1
+#define   IOCON_PIO1_23_FUNC_SSEL1		IOCON_FUNC_2
+
+#define IOCON_PIO1_24		0x400440c0
+#define   IOCON_PIO1_24_FUNC_PIO1_24		IOCON_FUNC_0
+#define   IOCON_PIO1_24_FUNC_CT32B0_MAT0	IOCON_FUNC_1
+
+#define IOCON_PIO1_25		0x400440c4
+#define   IOCON_PIO1_25_FUNC_PIO1_25		IOCON_FUNC_0
+#define   IOCON_PIO1_25_FUNC_CT32B0_MAT1	IOCON_FUNC_1
+
+#define IOCON_PIO1_26		0x400440c8
+#define   IOCON_PIO1_26_FUNC_PIO1_26		IOCON_FUNC_0
+#define   IOCON_PIO1_26_FUNC_CT32B0_MAT2	IOCON_FUNC_1
+#define   IOCON_PIO1_26_FUNC_RXD		IOCON_FUNC_2
+
+#define IOCON_PIO1_27		0x400440cc
+#define   IOCON_PIO1_27_FUNC_PIO1_27		IOCON_FUNC_0
+#define   IOCON_PIO1_27_FUNC_CT32B0_MAT3	IOCON_FUNC_1
+#define   IOCON_PIO1_27_FUNC_TXD		IOCON_FUNC_2
+
+#define IOCON_PIO1_28		0x400440d0
+#define   IOCON_PIO1_28_FUNC_PIO1_28		IOCON_FUNC_0
+#define   IOCON_PIO1_28_FUNC_CT32B0_CAP0	IOCON_FUNC_1
+#define   IOCON_PIO1_28_FUNC_SCLK		IOCON_FUNC_2
+
+#define IOCON_PIO1_29		0x400440d4
+#define   IOCON_PIO1_29_FUNC_PIO1_29		IOCON_FUNC_0
+#define   IOCON_PIO1_29_FUNC_SCK0		IOCON_FUNC_1
+#define   IOCON_PIO1_29_FUNC_CT32B0_CAP1	IOCON_FUNC_2
+
+#define IOCON_PIO1_31		0x400440dc
+#define   IOCON_PIO1_31_FUNC_PIO1_31		IOCON_FUNC_0
+
diff --git a/arch/lpc13xx/include-v2/arch/irqs.h b/arch/lpc13xx/include-v2/arch/irqs.h
@@ -0,0 +1,34 @@
+#ifdef _IRQ
+/* 0 */ _IRQ(pin_int0)
+/* 1 */ _IRQ(pin_int1)
+/* 2 */ _IRQ(pin_int2)
+/* 3 */ _IRQ(pin_int3)
+/* 4 */ _IRQ(pin_int4)
+/* 5 */ _IRQ(pin_int5)
+/* 6 */ _IRQ(pin_int6)
+/* 7 */ _IRQ(pin_int7)
+/* 8 */ _IRQ(gint0)
+/* 9 */ _IRQ(gint1)
+/* 10 */ _IRQ(reserved_10)
+/* 11 */ _IRQ(reserved_11)
+/* 12 */ _IRQ(rit)
+/* 13 */ _IRQ(reserved_13)
+/* 14 */ _IRQ(ssp1)
+/* 15 */ _IRQ(i2c)
+/* 16 */ _IRQ(ct16b0)
+/* 17 */ _IRQ(ct16b1)
+/* 18 */ _IRQ(ct32b0)
+/* 19 */ _IRQ(ct32b1)
+/* 20 */ _IRQ(ssp0)
+/* 21 */ _IRQ(usart)
+/* 22 */ _IRQ(usb_irq)
+/* 23 */ _IRQ(usb_fiq)
+/* 24 */ _IRQ(adc)
+/* 25 */ _IRQ(wwdt)
+/* 26 */ _IRQ(bod)
+/* 27 */ _IRQ(flash)
+/* 28 */ _IRQ(reserved_28)
+/* 29 */ _IRQ(reserved_29)
+/* 30 */ _IRQ(usb_wakeup)
+/* 31 */ _IRQ(reserved_31)
+#endif
diff --git a/arch/lpc13xx/include/arch/hardware.h b/arch/lpc13xx/include/arch/hardware.h
@@ -0,0 +1,255 @@
+#ifndef _LPC13XX_HARDWARE_H_
+#define _LPC13XX_HARDWARE_H_
+
+#include <fw/io.h>
+
+#include <arch/iocon.h>
+
+#define IOCONSET(port, pin, func, flags) do {		\
+	writel(IOCON_PIO ## port ## _ ## pin ## _FUNC_ ## func | (flags), \
+	       IOCON_PIO ## port ## _ ## pin); \
+} while (0)
+
+#define SYS_CLK_CTRL		0x40048080
+#define SYS_CLK_SYS		(1 << 0)
+#define SYS_CLK_ROM		(1 << 1)
+#define SYS_CLK_RAM		(1 << 2)
+#define SYS_CLK_RAM0		(1 << 2)
+#define SYS_CLK_FLASHREG	(1 << 3)
+#define SYS_CLK_FLASHARRAY	(1 << 4)
+#define SYS_CLK_I2C		(1 << 5)
+#define SYS_CLK_GPIO		(1 << 6)
+#define SYS_CLK_CT16B0		(1 << 7)
+#define SYS_CLK_CT16B1		(1 << 8)
+#define SYS_CLK_CT32B0		(1 << 9)
+#define SYS_CLK_CT32B1		(1 << 10)
+#define SYS_CLK_SSP0		(1 << 11)
+#define SYS_CLK_UART		(1 << 12) /* MUST CONFIG IOCON FIRST */
+#define SYS_CLK_ADC		(1 << 13)
+#define SYS_CLK_USB_REG		(1 << 14)
+#define SYS_CLK_WDT		(1 << 15)
+#define SYS_CLK_IOCON		(1 << 16)
+#define SYS_CLK_SSP1		(1 << 18)
+#define SYS_CLK_PINT		(1 << 19)
+#define SYS_CLK_GROUP0INT	(1 << 23)
+#define SYS_CLK_GROUP1INT	(1 << 24)
+#define SYS_CLK_RAM1		(1 << 26)
+#define SYS_CLK_USBSRAM		(1 << 27)
+
+#define SYS_DIV_AHB		0x40048078
+#define SYS_DIV_SSP0		0x40048094 /* 0 = off, 1... = PCLK/n */
+#define SYS_DIV_UART		0x40048098
+#define SYS_DIV_SSP1		0x4004809C
+#define SYS_DIV_TRACE		0x400480AC
+#define SYS_DIV_SYSTICK		0x400480B0
+
+#define CLKOUT_SELECT		0x400480E0
+#define CLKOUT_IRC		0
+#define CLKOUT_SYSTEM		1
+#define CLKOUT_WATCHDOG		2
+#define CLKOUT_MAIN		3
+#define CLKOUT_UPDATE		0x400480E4 /* write 0, then 1 to update */
+
+#define SCB_PINTSEL(n)		(0x40048178 + (n) * 4)
+#define  SCB_PINTSEL_INTPIN(x)		((x) & 0x1f)
+#define  SCB_PINTSEL_PORTSEL(x)		(((x) & 0x1) << 5)
+
+#define PRESETCTRL		0x40048004
+#define SSP0_RST_N		(1 << 0)
+#define I2C_RST_N		(1 << 1)
+#define SSP1_RST_N		(1 << 2)
+
+#define SSP_CR0			0x00
+#define SSP_CR1			0x04
+#define SSP_DR			0x08 /* data */
+#define SSP_SR			0x0C /* status */
+#define SSP_CPSR		0x10 /* clock prescale 2..254 bit0=0 always */
+#define SSP_IMSC		0x14 /* IRQ mask set/clear */
+#define SSP_RIS			0x18 /* IRQ raw status */
+#define SSP_MIS			0x1C /* IRQ masked status */
+#define SSP_ICR			0x20 /* IRQ clear */
+
+#define SSP0_CR0		0x40040000
+#define SSP0_CR1		0x40040004
+#define SSP0_DR			0x40040008 /* data */
+#define SSP0_SR			0x4004000C /* status */
+#define SSP0_CPSR		0x40040010 /* clock prescale 2..254 bit0=0 always */
+#define SSP0_IMSC		0x40040014 /* IRQ mask set/clear */
+#define SSP0_RIS		0x40040018 /* IRQ raw status */
+#define SSP0_MIS		0x4004001C /* IRQ masked status */
+#define SSP0_ICR		0x40040020 /* IRQ clear */
+
+#define SSP_CR0_BITS(n)		((n) - 1) /* valid for n=4..16 */
+#define SSP_CR0_FRAME_SPI	(0 << 4)
+#define SSP_CR0_FRAME_TI	(1 << 4)
+#define SSP_CR0_FRAME_MICROWIRE	(2 << 4)
+#define SSP_CR0_CLK_LOW		(0 << 6) /* clock idle is low */
+#define SSP_CR0_CLK_HIGH	(1 << 6) /* clock idle is high */
+#define SSP_CR0_CPOL		(1 << 6)
+#define SSP_CR0_PHASE0		(0 << 7) /* capture on clock change from idle */
+#define SSP_CR0_PHASE1		(1 << 7) /* capture on clock change to idle */
+#define SSP_CR0_CPHA		(1 << 7)
+#define SSP_CR0_CLOCK_RATE(n)	(((n) - 1) << 8)
+
+#define SSP_CR1_LOOPBACK	(1 << 0)
+#define SSP_CR1_ENABLE		(1 << 1)
+#define SSP_CR1_MASTER		(0 << 2)
+#define SSP_CR1_SLAVE		(1 << 2)
+#define SSP_CR1_OUTPUT_DISABLE	(1 << 3) /* only valid in SLAVE mode */
+
+#define SSP_XMIT_EMPTY		(1 << 0)
+#define SSP_XMIT_NOT_FULL	(1 << 1)
+#define SSP_RECV_NOT_EMPTY	(1 << 2)
+#define SSP_RECV_FULL		(1 << 3)
+#define SSP_BUSY		(1 << 4)
+
+#define SSP_SR_TFE		(1 << 0)
+#define SSP_SR_TNF		(1 << 1)
+#define SSP_SR_RNE		(1 << 2)
+#define SSP_SR_RFF		(1 << 3)
+#define SSP_SR_BSY		(1 << 4)
+
+#define SSP_INT_RO		(1 << 0)
+#define SSP_INT_RT		(1 << 1)
+#define SSP_INT_RX		(1 << 2)
+#define SSP_INT_TX		(1 << 3)
+
+/* SSP bitrate = SYSCLK / SYS_DIV_SSPn / SSP_CR0_CLOCK_RATE */
+
+#ifdef LPC13XX_V2
+#define GPIO_ISEL		0x4004C000
+#define GPIO_IENR		0x4004C004
+#define GPIO_SIENR		0x4004C008
+#define GPIO_CIENR		0x4004C00C
+#define GPIO_IENF		0x4004C010
+#define GPIO_SIENF		0x4004C014
+#define GPIO_CIENF		0x4004C018
+#define GPIO_RISE		0x4004C01C
+#define GPIO_FALL		0x4004C020
+#define GPIO_IST		0x4004C024
+
+#define GPIO_BASE		0x50000000
+#define GPIO_B(n)		(GPIO_BASE + (n))
+#define GPIO_W(n)		(GPIO_BASE + 0x1000 + (n) * 4)
+#define GPIO_DIR(n)		(GPIO_BASE + 0x2000 + (n) * 4)
+#define GPIO_MASK(n)		(GPIO_BASE + 0x2080 + (n) * 4)
+#define GPIO_PIN(n)		(GPIO_BASE + 0x2100 + (n) * 4)
+#define GPIO_MPIN(n)		(GPIO_BASE + 0x2180 + (n) * 4)
+#define GPIO_SET(n)		(GPIO_BASE + 0x2200 + (n) * 4)
+#define GPIO_CLR(n)		(GPIO_BASE + 0x2280 + (n) * 4)
+#define GPIO_NOT(n)		(GPIO_BASE + 0x2300 + (n) * 4)
+#else
+#define GPIOBASE(n)		(0x50000000 + ((n) << 16))
+#define GPIODATA(n)		(GPIOBASE(n) + 0x3FFC)
+#define GPIODIR(n)		(GPIOBASE(n) + 0x8000) /* 0 = input, 1 = output */
+#define GPIOIER(n)		(GPIOBASE(n) + 0x8010) /* 1 = irq enabled */
+#define GPIOLEVEL(n)		(GPIOBASE(n) + 0x8004) /* 0 = edge, 1 = level irq */
+#define GPIOBOTHEDGES(n)	(GPIOBASE(n) + 0x8008) /* 1 = trigger on both edges */
+#define GPIOPOLARITY(n)		(GPIOBASE(n) + 0x800C) /* 0 = low/falling, 1 = high/rising */
+#define GPIORAWISR(n)		(GPIOBASE(n) + 0x8014) /* 1 if triggered */
+#define GPIOISR(n)		(GPIOBASE(n) + 0x8018) /* 1 if triggered and enabled */
+#define GPIOICR(n)		(GPIOBASE(n) + 0x801C) /* write 1 to clear, 2 clock delay */
+#endif
+
+/* these registers survive powerdown / warm reboot */
+#define GPREG0			0x40038004
+#define GPREG1			0x40038008
+#define GPREG2			0x4003800C
+#define GPREG3			0x40038010
+#define GPREG4			0x40038014
+
+
+#define TM32B0IR		0x40014000
+#define TM32B0TCR		0x40014004
+#define TM32B0TC		0x40014008 /* increments every PR PCLKs */
+#define TM32B0PR		0x4001400C
+#define TM32B0PC		0x40014010 /* increments every PCLK */
+#define TM32B0MCR		0x40014014
+#define TM32B0MR0		0x40014018
+#define TM32B0MR1		0x4001401C
+#define TM32B0MR2		0x40014020
+#define TM32B0MR3		0x40014024
+#define TM32B0CCR		0x40014028
+#define TM32B0CR0		0x4001402C
+#define TM32B0EMR		0x4001403C
+
+#define TM32B1IR		0x40018000
+#define TM32B1TCR		0x40018004
+#define TM32B1TC		0x40018008 /* increments every PR PCLKs */
+#define TM32B1PR		0x4001800C
+#define TM32B1PC		0x40018010 /* increments every PCLK */
+#define TM32B1MCR		0x40018014
+#define TM32B1MR0		0x40018018
+#define TM32B1MR1		0x4001801C
+#define TM32B1MR2		0x40018020
+#define TM32B1MR3		0x40018024
+#define TM32B1CCR		0x40018028
+#define TM32B1CR0		0x4001802C
+#define TM32B1EMR		0x4001803C
+
+#define TM32TCR_ENABLE		1
+#define TM32TCR_RESET		2
+
+#define TM32_IR_MRINT(n)	(1 << (n))
+#define TM32_IR_MR0INT		TM32_IR_MRINT(0)
+#define TM32_IR_MR1INT		TM32_IR_MRINT(1)
+#define TM32_IR_MR2INT		TM32_IR_MRINT(2)
+#define TM32_IR_MR3INT		TM32_IR_MRINT(3)
+#define TM32_IR_CR0INT		(1 << 4)
+#define TM32_IR_CR1INT		(1 << 5)
+
+
+/* Match Control Register (MCR) actions for each Match Register */
+#define TM32_M_IRQ(n)		(1 << (((n) * 3) + 0))
+#define TM32_M_RESET(n)		(1 << (((n) * 3) + 1))
+#define TM32_M_STOP(n)		(1 << (((n) * 3) + 2))
+
+#define TM32_M0_IRQ		(TM32_M_IRQ(0))
+#define TM32_M0_RESET		(TM32_M_RESET(0))
+#define TM32_M0_STOP		(TM32_M_STOP(0))
+
+#define TM32_M1_IRQ		(TM32_M_IRQ(1))
+#define TM32_M1_RESET		(TM32_M_RESET(1))
+#define TM32_M1_STOP		(TM32_M_STOP(1))
+
+#define TM32_M2_IRQ		(TM32_M_IRQ(2))
+#define TM32_M2_RESET		(TM32_M_RESET(2))
+#define TM32_M2_STOP		(TM32_M_STOP(2))
+
+#define TM32_M3_IRQ		(TM32_M_IRQ(3))
+#define TM32_M3_RESET		(TM32_M_RESET(3))
+#define TM32_M3_STOP		(TM32_M_STOP(3))
+
+/* External Match Control (EMC) actions for each Match Register */
+#define TM32_EMC0_CLEAR		(1 << 4)
+#define TM32_EMC0_SET		(2 << 4)
+#define TM32_EMC0_TOGGLE	(3 << 4)
+#define TM32_EMC1_CLEAR		(1 << 6)
+#define TM32_EMC1_SET		(2 << 6)
+#define TM32_EMC1_TOGGLE	(3 << 6)
+#define TM32_EMC2_CLEAR		(1 << 8)
+#define TM32_EMC2_SET		(2 << 8)
+#define TM32_EMC2_TOGGLE	(3 << 8)
+#define TM32_EMC3_CLEAR		(1 << 10)
+#define TM32_EMC3_SET		(2 << 10)
+#define TM32_EMC3_TOGGLE	(3 << 10)
+
+#ifdef LPC13XX_V2
+#define MKGPIO(bank,num)	(((bank) << 16) | (num))
+#define GPIO_PORT(gpio)		((gpio) >> 16)
+#define GPIO_NUM(gpio)		((gpio) & 0xffff)
+#else
+#define MKGPIO(bank,num)	(((bank) << 16) | (1 << (num)))
+#define GPIO_PORT(gpio)		((gpio) >> 16)
+#define GPIO_NUM(gpio)		((gpio) & 0xffff)
+#endif
+
+/* serial */
+
+void core_48mhz_init(void);
+void ssp0_init(void);
+unsigned ssp0_set_clock(unsigned khz);
+void serial_init(unsigned sysclk_mhz, unsigned baud);
+void serial_start_async_rx(void (*async_cb)(char c));
+
+#endif
diff --git a/arch/lpc13xx/include/arch/iap.h b/arch/lpc13xx/include/arch/iap.h
@@ -0,0 +1,7 @@
+#ifndef __ARCH_LPC13XX_IAP_H__
+#define __ARCH_LPC13XX_IAP_H__
+
+int iap_eeprom_read(void *buf, unsigned addr, int len);
+int iap_eeprom_write(unsigned addr, void *buf, int len);
+
+#endif /* __ARCH_LPC13XX_IAP_H__ */
diff --git a/arch/lpc13xx/include/arch/ssp.h b/arch/lpc13xx/include/arch/ssp.h
@@ -0,0 +1,19 @@
+#ifndef _ARCH_LCP13XX_SSP_H_
+#define _ARCH_LCP13XX_SSP_H_
+
+struct spi_dev;
+
+#define SPI_CPOL_0	0
+#define SPI_CPOL_1	1
+
+#define SPI_CPHA_0	0
+#define SPI_CPHA_1	1
+
+struct spi_dev *ssp_init(unsigned n, int bits, bool cpol, bool cpha,
+			 int prescale, int clkdiv, int rate);
+void spi_xmit(struct spi_dev *spi, void *out_buf, int out_len,
+	      void *in_buf, int in_len,
+	      void (*cb)(void *data), void *cb_data);
+
+
+#endif /* _ARCH_LCP13XX_SSP_H_ */
diff --git a/arch/lpc13xx/init.c b/arch/lpc13xx/init.c
@@ -0,0 +1,117 @@
+/* init.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/io.h>
+#include <arch/hardware.h>
+
+void core_48mhz_init(void) {
+        /* switch to IRC if we aren't on it already */
+        if ((readl(0x40048070) & 3) != 0) {
+                writel(0, 0x40048070);
+                writel(0, 0x40048074);
+                writel(1, 0x40048074);
+        }
+
+        /* power down SYS PLL */
+        writel(readl(0x40048238) | (1 << 7), 0x40048238);
+
+        /* configure SYS PLL */
+        writel(0x23, 0x40048008); /* MSEL=4, PSEL=2, OUT=48MHz */
+//      writel(0x25, 0x40048008); /* MSEL=6, PSEL=2, OUT=72MHz */
+
+        /* power up SYS PLL */
+        writel(readl(0x40048238) & (~(1 << 7)), 0x40048238);
+
+        /* wait for SYS PLL to lock */
+        while(!(readl(0x4004800c) & 1)) ;
+
+
+        /* select SYS PLL OUT for MAIN CLK */
+        writel(3, 0x40048070);
+        writel(0, 0x40048074);
+        writel(1, 0x40048074);
+
+        /* select Main clock for USB CLK */
+        writel(1, 0x400480C0);
+        writel(0, 0x400480C4);
+        writel(1, 0x400480C4);
+
+        /* set USB clock divider to 1 */
+        writel(1, 0x400480C8);
+
+	/* clock to GPIO and MUX blocks */
+	writel(readl(SYS_CLK_CTRL) | SYS_CLK_IOCON | SYS_CLK_GPIO, SYS_CLK_CTRL);
+}
+
+void ssp0_init(void) {
+        /* assert reset, disable clock */
+        writel(readl(PRESETCTRL) & (~SSP0_RST_N), PRESETCTRL);
+        writel(0, SYS_DIV_SSP0);
+
+        /* enable SSP0 clock */
+        writel(readl(SYS_CLK_CTRL) | SYS_CLK_SSP0, SYS_CLK_CTRL);
+
+        /* SSP0 PCLK = SYSCLK / 3 (16MHz) */
+        writel(3, SYS_DIV_SSP0);
+
+        /* deassert reset */
+        writel(readl(PRESETCTRL) | SSP0_RST_N, PRESETCTRL);
+
+        writel(2, SSP0_CPSR); /* prescale = PCLK/2 */
+        writel(SSP_CR0_BITS(16) | SSP_CR0_FRAME_SPI |
+                SSP_CR0_CLK_HIGH | SSP_CR0_PHASE1 |
+                SSP_CR0_CLOCK_RATE(1),
+                SSP0_CR0);
+        writel(SSP_CR1_ENABLE | SSP_CR1_MASTER, SSP0_CR1);
+
+	/* configure io mux */
+/* XXX: this is board specific */
+        writel(IOCON_SCK0_PIO2_11, IOCON_SCK0_LOC);
+        writel(IOCON_FUNC_1 | IOCON_DIGITAL, IOCON_PIO0_8); /* MISO */
+        writel(IOCON_FUNC_1 | IOCON_DIGITAL, IOCON_PIO0_9); /* MOSI */
+        writel(IOCON_FUNC_1 | IOCON_DIGITAL, IOCON_PIO2_11); /* SCK */
+}
+
+static struct {
+        u16 khz;
+        u16 div;
+} ssp_clocks[] = {
+        { 24000, 1, },
+        { 12000, 2, },
+        {  8000, 3, },
+        {  6000, 4, },
+        {  4000, 6, },
+        {  3000, 8, },
+        {  2000, 12, },
+        {  1000, 24, }, 
+};
+
+unsigned ssp0_set_clock(unsigned khz) {
+        int n;
+        if (khz < 1000)
+                khz = 1000;
+        for (n = 0; n < (sizeof(ssp_clocks)/sizeof(ssp_clocks[0])); n++) {
+                if (ssp_clocks[n].khz <= khz) {
+                        writel(ssp_clocks[n].div, SYS_DIV_SSP0);
+                        return ssp_clocks[n].khz;
+                }
+        }
+        return 0;
+}
+
+
diff --git a/arch/lpc13xx/reboot.c b/arch/lpc13xx/reboot.c
@@ -0,0 +1,39 @@
+/* reboot.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/lib.h>
+#include <fw/io.h>
+
+#include <arch/hardware.h>
+
+void reboot(void) {
+	/* select IRC */
+	writel(0, 0x400480D0);
+	/* enable */
+	writel(0, 0x400480D4);
+	writel(1, 0x400480D4);
+	/* DIV = 1 */
+	writel(1, 0x400480D8);
+	writel(readl(SYS_CLK_CTRL) | SYS_CLK_WDT, SYS_CLK_CTRL);
+	/* ENABLE and RESET */
+	writel(3, 0x40004000);
+	/* FEED */
+	writel(0xAA, 0x40004008);
+	writel(0x55, 0x40004008);
+	for (;;) ;
+}
diff --git a/arch/lpc13xx/serial.c b/arch/lpc13xx/serial.c
@@ -0,0 +1,91 @@
+/* serial.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/io.h>
+#include <fw/lib.h>
+
+#include <arch/hardware.h>
+
+static void (*serial_async_cb)(char c);
+
+/* only works for multiples of 12MHz and 115.2kbaud right now */
+void serial_init(unsigned sysclk_mhz, unsigned baud) {
+
+	if (sysclk_mhz % 12000000)
+		return;
+	if (baud != 115200)
+		return;
+
+/* XXX: this is board specific */
+	writel(IOCON_FUNC_1 | IOCON_DIGITAL, IOCON_PIO1_6); /* RXD */
+	writel(IOCON_FUNC_1 | IOCON_DIGITAL, IOCON_PIO1_7); /* TXD */
+
+	writel(sysclk_mhz / 12000000, SYS_DIV_UART); /* SYS / n -> 12MHz */
+	writel(readl(SYS_CLK_CTRL) | SYS_CLK_UART, SYS_CLK_CTRL);
+
+	writel(0x80, 0x4000800C); /* DLAB=1 */
+	writel(0x04, 0x40008000); /* DLL=4 */
+	writel(0x00, 0x40008004); /* DLM=0 */
+	writel(0x85, 0x40008028); /* DIVADDVAL=5, MULVAL=8 */
+	writel(0x03, 0x4000800C); /* DLAB=0, 8N1 */
+	writel(0x00, 0x40008010); /* no flow control */
+	writel(1, 0x40008008); /* enable FIFO */
+	writel(1, 0x40008008); /* enable FIFO */
+}
+
+void serial_putc(unsigned c) {
+	/* wait until xmit holding register is empty */
+	while (!(readl(0x40008014) & (1 << 5))) ;
+	writel(c, 0x40008000);
+}
+
+void serial_start_async_rx(void (*async_cb)(char c))
+{
+	serial_async_cb = async_cb;
+
+	writel((1<<2) | (1<<0), 0x40008004); /* IER=1, receive data avail */
+}
+
+void handle_irq_uart(void)
+{
+	do {
+		u32 lsr = readl(0x40008014);
+		if (lsr & (1<<1)) {
+			serial_async_cb('O');
+		}
+		if (lsr & (1<<2)) {
+			serial_async_cb('P');
+		}
+		if (lsr & (1<<3)) {
+			serial_async_cb('F');
+		}
+		if (lsr & (1<<4)) {
+			serial_async_cb('B');
+		}
+		if (lsr & (1<<7)) {
+			serial_async_cb('E');
+		}
+		if (lsr & (1<<0)) {
+			char c = readl(0x40008000);
+			if (serial_async_cb)
+				serial_async_cb(c);
+			continue; /* we got a char, try again */
+		}
+	} while (0);
+}
+
diff --git a/arch/lpc13xx/ssp.c b/arch/lpc13xx/ssp.c
@@ -0,0 +1,200 @@
+#include <fw/io.h>
+#include <fw/lib.h>
+#include <fw/types.h>
+
+#include <arch/cpu.h>
+#include <arch/hardware.h>
+#include <arch/interrupts.h>
+#include <arch/ssp.h>
+
+void printu(const char *fmt, ...);
+
+struct spi_cfg {
+	unsigned addr;
+	unsigned rst;
+	unsigned clk;
+	unsigned div;
+	unsigned irq;
+};
+
+struct spi_dev {
+	const struct spi_cfg *cfg;
+
+	u8	bits;
+
+	void	*in_buf;
+	u16	in_len;
+	u16	in_pos;
+
+	void	*out_buf;
+	u16	out_len;
+	u16	out_pos;
+
+	void (*cb)(void *data);
+	void	*cb_data;
+};
+
+const struct spi_cfg spi_cfgs[] = {
+	[0] = {
+		.addr = 0x40040000,
+		.rst = SSP0_RST_N,
+		.clk = SYS_CLK_SSP0,
+		.div = SYS_DIV_SSP0,
+		.irq = v_ssp0,
+	},
+	[1] = {
+		.addr = 0x40058000,
+		.rst = SSP1_RST_N,
+		.clk = SYS_CLK_SSP1,
+		.div = SYS_DIV_SSP1,
+		.irq = v_ssp1,
+	},
+};
+
+struct spi_dev spi_devs[] = {
+	[0] = {.cfg = &spi_cfgs[0]},
+	[1] = {.cfg = &spi_cfgs[1]},
+};
+
+static inline unsigned spi_readl(struct spi_dev *spi, unsigned reg)
+{
+	return readl(spi->cfg->addr + reg);
+}
+
+static inline void spi_writel(struct spi_dev *spi, unsigned val, unsigned reg)
+{
+	writel(val, spi->cfg->addr + reg);
+}
+
+static inline void spi_clr_set_reg(struct spi_dev *spi, unsigned reg,
+				   unsigned clr, unsigned set)
+{
+	clr_set_reg(spi->cfg->addr + reg, clr, set);
+}
+
+void handle_spi_xmit(struct spi_dev *spi)
+{
+	u16 data;
+
+	/*
+	 * Drain the rx fifo. Make sure that we read as many words as we wrote.
+	 * If the in_buf len is less that out_len, throw away the word.
+	 */
+	while(spi->in_pos < spi->out_len &&
+	      (spi_readl(spi, SSP_SR) & SSP_SR_RNE)) {
+		data = spi_readl(spi, SSP_DR);
+
+		if (spi->in_pos < spi->in_len) {
+			if (spi->bits == 8)
+				((u8 *)spi->in_buf)[spi->in_pos] = data;
+			else
+				((u16 *)spi->in_buf)[spi->in_pos] = data;
+		}
+		spi->in_pos++;
+	}
+
+	if (spi->in_pos < spi->out_len)
+		spi_clr_set_reg(spi, SSP_IMSC, 0, SSP_INT_RT | SSP_INT_RX);
+	else
+		spi_clr_set_reg(spi, SSP_IMSC, SSP_INT_RT | SSP_INT_RX, 0);
+
+	/* fill the TX fifo */
+	while(spi->out_pos < spi->out_len &&
+	      (spi_readl(spi, SSP_SR) & SSP_SR_TNF)) {
+		if (spi->bits == 8)
+			data = ((u8 *)spi->out_buf)[spi->out_pos];
+		else
+			data = ((u16 *)spi->out_buf)[spi->out_pos];
+		spi_writel(spi, data, SSP_DR);
+		spi->out_pos++;
+	}
+
+	if (spi->in_pos < spi->out_len)
+		spi_clr_set_reg(spi, SSP_IMSC, 0, SSP_INT_TX);
+	else
+		spi_clr_set_reg(spi, SSP_IMSC, SSP_INT_TX, 0);
+
+	if (spi->in_pos < spi->out_len && spi->cb)
+		spi->cb(spi->cb_data);
+}
+
+void handle_spi_irq(struct spi_dev *spi)
+{
+	unsigned status = spi_readl(spi, SSP_RIS);
+
+	handle_spi_xmit(spi);
+
+	spi_writel(spi, status & 0x3, SSP_RIS);
+}
+
+void handle_irq_ssp0(void)
+{
+	handle_spi_irq(&spi_devs[0]);
+}
+
+void handle_irq_ssp1(void)
+{
+	handle_spi_irq(&spi_devs[1]);
+}
+
+struct spi_dev *ssp_init(unsigned n, int bits, bool cpol, bool cpha,
+				 int prescale, int clkdiv, int rate)
+{
+	struct spi_dev *spi;
+	const struct spi_cfg *cfg;
+	if (n > ARRAY_SIZE(spi_devs))
+		return NULL;
+
+	spi = &spi_devs[n];
+	cfg = spi->cfg;
+	spi->bits = bits;
+
+	irq_enable(cfg->irq);
+
+	/* assert reset, disable clock */
+	writel(readl(PRESETCTRL) & ~cfg->rst, PRESETCTRL);
+	writel(0, cfg->div);
+
+	/* enable SSP0 clock */
+	writel(readl(SYS_CLK_CTRL) | cfg->clk, SYS_CLK_CTRL);
+
+	/* SSP0 PCLK = SYSCLK / 3 (16MHz) */
+	writel(clkdiv, cfg->div);
+
+	/* deassert reset */
+	writel(readl(PRESETCTRL) | cfg->rst, PRESETCTRL);
+
+	spi_writel(spi, prescale, SSP_CPSR); /* prescale = PCLK/2 */
+	spi_writel(spi, SSP_CR0_BITS(bits) | SSP_CR0_FRAME_SPI |
+		   (cpol ? SSP_CR0_CPOL : 0) |
+		   (cpha ? SSP_CR0_CPHA : 0) |
+		   SSP_CR0_CLOCK_RATE(rate),
+		   SSP_CR0);
+        spi_writel(spi, SSP_CR1_ENABLE | SSP_CR1_MASTER, SSP_CR1);
+
+	return spi;
+}
+
+void spi_xmit(struct spi_dev *spi, void *out_buf, int out_len,
+	      void *in_buf, int in_len,
+	      void (*cb)(void *data), void *cb_data)
+{
+	/* wait for spi to idle */
+	while (spi_readl(spi, SSP_SR) & SSP_SR_BSY) {
+	}
+
+	spi->in_buf = in_buf;
+	spi->in_len = in_len;
+	spi->in_pos = 0;
+
+	spi->out_buf = out_buf;
+	spi->out_len = out_len;
+	spi->out_pos = 0;
+
+	spi->cb = cb;
+	spi->cb_data = cb_data;
+
+	disable_interrupts();
+	handle_spi_xmit(spi);
+	enable_interrupts();
+}
diff --git a/arch/lpc13xx/usb-v1.c b/arch/lpc13xx/usb-v1.c
@@ -0,0 +1,604 @@
+/* usb.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/lib.h>
+#include <fw/io.h>
+
+#include <arch/hardware.h>
+#include <arch/cpu.h>
+#include <protocol/usb.h>
+
+#include "usb-v1.h"
+
+#if CONFIG_USB_TRACE
+#define P(x...) printx(x)
+#else
+#define P(x...)
+#endif
+
+#ifndef CONFIG_USB_USE_IRQS
+#define enable_interrupts() do {} while (0)
+#define disable_interrupts() do {} while (0)
+#endif
+
+static volatile unsigned msec_counter = 0;
+
+void usb_handle_irq(void);
+
+static u8 _dev00[] = {
+	18,		/* size */
+	DSC_DEVICE,
+	0x10, 0x01,	/* version */
+	0xFF,		/* class */
+	0x00,		/* subclass */
+	0x00,		/* protocol */
+	0x40,		/* maxpacket0 */
+	0xd1, 0x18,	/* VID */
+	0x02, 0x65,	/* PID */
+	0x10, 0x01,	/* version */
+	0x00,		/* manufacturer string */
+	0x00,		/* product string */
+	0x00,		/* serialno string */
+	0x01,		/* configurations */
+};
+
+static u8 _cfg00[] = {
+	9,
+	DSC_CONFIG,
+#if CONFIG_USB_2ND_IFC
+	0x37, 0x00,	/* total length */
+	0x02,		/* ifc count */
+#else
+	0x20, 0x00,	/* total length */
+	0x01,		/* ifc count */
+#endif
+	0x01,		/* configuration value */
+	0x00,		/* configuration string */
+	0x80,		/* attributes */
+	50,		/* mA/2 */
+
+	9,
+	DSC_INTERFACE,
+	0x00,		/* interface number */
+	0x00,		/* alt setting */
+	0x02,		/* ept count */
+	0xFF,		/* class */
+	0x00,		/* subclass */
+	0x00,		/* protocol */
+	0x00,		/* interface string */
+
+	7,
+	DSC_ENDPOINT,
+	0x81,		/* address */
+	0x02,		/* bulk */
+	0x40, 0x00,	/* max packet size */
+	0x00,		/* interval */
+
+	7,
+	DSC_ENDPOINT,
+	0x01,		/* address */
+	0x02,		/* bulk */
+	0x40, 0x00,	/* max packet size */
+	0x00,		/* interval */
+
+#if CONFIG_USB_2ND_IFC
+	9,
+	DSC_INTERFACE,
+	0x01,		/* interface number */
+	0x00,		/* alt setting */
+	0x02,		/* ept count */
+	0xFF,		/* class */
+	0x00,		/* subclass */
+	0x00,		/* protocol */
+	0x00,		/* interface string */
+
+	7,
+	DSC_ENDPOINT,
+	0x82,		/* address */
+	0x02,		/* bulk */
+	0x40, 0x00,	/* max packet size */
+	0x00,		/* interval */
+
+	7,
+	DSC_ENDPOINT,
+	0x02,		/* address */
+	0x02,		/* bulk */
+	0x40, 0x00,	/* max packet size */
+	0x00,		/* interval */
+#endif
+};
+
+#if CONFIG_USB_STRINGS
+const static u8 lang_id[] = {
+	4,
+	DSC_STRING,
+	0x09, 0x04
+};
+
+static u16 mfg_string[24] = {
+};
+
+static u16 prod_string[24] = {
+};
+#endif
+
+static void write_sie_cmd(unsigned cmd) {
+	writel(USB_INT_CC_EMPTY, USB_INT_CLEAR);
+	writel(cmd | USB_OP_COMMAND, USB_CMD_CODE);
+	while (!(readl(USB_INT_STATUS) & USB_INT_CC_EMPTY)) ;
+	writel(USB_INT_CC_EMPTY, USB_INT_CLEAR);
+}
+
+static void write_sie(unsigned cmd, unsigned data) {
+	write_sie_cmd(cmd);
+	writel((data << 16) | USB_OP_WRITE, USB_CMD_CODE);
+	while (!(readl(USB_INT_STATUS) & USB_INT_CC_EMPTY)) ;
+	writel(USB_INT_CC_EMPTY, USB_INT_CLEAR);
+}
+
+static unsigned read_sie(unsigned cmd) {
+	unsigned n;
+	write_sie_cmd(cmd);
+	writel(cmd | USB_OP_READ, USB_CMD_CODE);
+	while (!(readl(USB_INT_STATUS) & USB_INT_CD_FULL)) ;
+	n = readl(USB_CMD_DATA);
+	writel(USB_INT_CC_EMPTY | USB_INT_CD_FULL, USB_INT_CLEAR);
+	return n;
+}
+
+static void usb_ep0_send(const void *_data, int len, int rlen) {
+	const u32 *data = _data;
+
+	if (len > rlen)
+		len = rlen;
+	writel(USB_CTRL_WR_EN | USB_CTRL_EP_NUM(0), USB_CTRL);
+	writel(len, USB_TX_PLEN);
+	while (len > 0) {
+		writel(*data++, USB_TX_DATA);
+		len -= 4;
+	}
+	writel(0, USB_CTRL);
+	write_sie_cmd(USB_CC_SEL_EPT(1));
+	write_sie_cmd(USB_CC_VAL_BUFFER);
+}
+
+static void usb_ep0_send0(void) {
+	writel(USB_CTRL_WR_EN | USB_CTRL_EP_NUM(0), USB_CTRL);
+	writel(0, USB_TX_PLEN);
+	writel(0, USB_CTRL);
+	write_sie_cmd(USB_CC_SEL_EPT(1));
+	write_sie_cmd(USB_CC_VAL_BUFFER);
+}
+
+#define EP0_TX_ACK_ADDR	0 /* sending ACK, then changing address */
+#define EP0_TX_ACK	1 /* sending ACK */
+#define EP0_RX_ACK	2 /* receiving ACK */
+#define EP0_TX		3 /* sending data */
+#define EP0_RX		4 /* receiving data */
+#define EP0_IDLE	5 /* waiting for SETUP */
+
+static u8 ep0state;
+static u8 newaddr;
+
+static volatile int _usb_online = 0;
+
+static void usb_ep0_rx_setup(void) {
+	unsigned c,n;
+	u16 req, val, idx, len;
+
+	do {
+		writel(USB_CTRL_RD_EN | USB_CTRL_EP_NUM(0), USB_CTRL);
+		/* to ensure PLEN is valid */
+		asm("nop"); asm("nop"); asm("nop");
+		asm("nop"); asm("nop"); asm("nop");
+		c = readl(USB_RX_PLEN) & 0x1FF;
+		n = readl(USB_RX_DATA);
+		req = n;
+		val = n >> 16;
+		n = readl(USB_RX_DATA);
+		idx = n;
+		len = n >> 16;
+		writel(0, USB_CTRL);
+		/* bit 1 will be set if another SETUP arrived... */
+		n = read_sie(USB_CC_CLR_BUFFER);
+	} while (n & 1);
+
+	switch (req) {
+	case GET_STATUS: {
+		u16 status = 0;
+		usb_ep0_send(&status, sizeof(status), len);
+		ep0state = EP0_RX;
+		break;
+	}
+	case GET_DESCRIPTOR:
+		if (val == 0x0100) {
+			usb_ep0_send(_dev00, sizeof(_dev00), len);
+		} else if (val == 0x0200) {
+			usb_ep0_send(_cfg00, sizeof(_cfg00), len);
+#if CONFIG_USB_STRINGS
+		} else if (val == 0x0300) {
+			usb_ep0_send(lang_id, sizeof(lang_id), len);
+		} else if (val == 0x0301) {
+			usb_ep0_send(mfg_string, mfg_string[0] & 0xff, len);
+		} else if (val == 0x0302) {
+			usb_ep0_send(prod_string, prod_string[0] & 0xff, len);
+#endif
+		} else {
+			goto stall;
+		}
+		ep0state = EP0_RX;
+		break;
+	case SET_ADDRESS:
+		usb_ep0_send0();
+		ep0state = EP0_TX_ACK_ADDR;
+		newaddr = val & 0x7F;
+		break;
+	case SET_CONFIGURATION:
+		write_sie(USB_CC_CONFIG_DEV, (val == 1) ? 1 : 0);
+		_usb_online = (val == 1);
+		usb_ep0_send0();
+		if (usb_online_cb)
+			usb_online_cb(_usb_online);
+		ep0state = EP0_TX_ACK;
+		break;
+	default:
+		goto stall;
+	}
+
+	return;
+
+stall:
+	/* stall */
+	write_sie(USB_CC_SET_EPT(0), 0x80);
+	//P("? %h %h %h %h\n", req, val, idx, len);
+}
+
+static void usb_ep0_rx(void) {
+	unsigned n;
+	n = read_sie(USB_CC_CLR_EPT(0));
+	if (n & 1) {
+		usb_ep0_rx_setup();
+	} else {
+	}
+}
+
+void usb_ep0_tx(void) {
+	unsigned n;
+	n = read_sie(USB_CC_CLR_EPT(1));
+	if (ep0state == EP0_TX_ACK_ADDR) {
+		write_sie(USB_CC_SET_ADDR, 0x80 | newaddr);
+		write_sie(USB_CC_SET_ADDR, 0x80 | newaddr);
+	}
+	ep0state = EP0_IDLE;
+}
+
+int usb_recv(void *_data, int count) {
+	return usb_recv_timeout(_data, count, 0);
+}
+
+int usb_online(void) {
+	usb_handle_irq();
+	return _usb_online;
+}
+
+static int usb_epN_read(unsigned ep, void *_data, int len) {
+	unsigned n;
+	int sz;
+	u32 *data;
+
+	if (len > 64)
+		return -EFAIL;
+	if (!_usb_online)
+		return -ENODEV;
+
+	data = _data;
+
+	disable_interrupts();
+
+	n = read_sie(USB_CC_CLR_EPT(ep << 1));
+	if (!(n & 1)) {
+		enable_interrupts();
+		return -EBUSY;
+	}
+	
+	writel(USB_CTRL_RD_EN | USB_CTRL_EP_NUM(ep), USB_CTRL);
+	/* to ensure PLEN is valid */
+	asm("nop"); asm("nop"); asm("nop");
+	asm("nop"); asm("nop"); asm("nop");
+	sz = readl(USB_RX_PLEN) & 0x1FF;
+
+	if (sz > len)
+		sz = len;
+
+	while (len > 0) {
+		*data++ = readl(USB_RX_DATA);
+		len -= 4;
+	}
+
+	writel(0, USB_CTRL);
+	n = read_sie(USB_CC_CLR_BUFFER);
+
+	enable_interrupts();
+
+	return sz;
+}
+
+int usb_ep1_read(void *_data, int len) {
+	return usb_epN_read(1, _data, len);
+}
+
+#if CONFIG_USB_2ND_IFC
+int usb_ep2_read(void *_data, int len) {
+	return usb_epN_read(2, _data, len);
+}
+#endif
+
+int usb_recv_timeout(void *_data, int count, unsigned timeout) {
+	int r, rx;
+	u8 *data;
+
+	data = _data;
+	rx = 0;
+	msec_counter = 0;
+
+	/* if offline, wait for us to go online */
+	while (!_usb_online)
+		usb_handle_irq();
+
+	while (count > 0) {
+		r = usb_ep1_read(data, (count > 64) ? 64 : count);
+
+		if (r >= 0) {
+			rx += r;
+			data += r;
+			count -= r;
+			/* terminate on short packet */
+			if (r != 64)
+				break;
+		} else if (r == -EBUSY) {
+			if (timeout && (msec_counter > timeout))
+				return -ETIMEOUT;
+			usb_handle_irq();
+		} else {
+			return r;
+		}
+	}
+	return rx;
+}
+
+static int usb_epN_write(unsigned ep, void *_data, int len) {
+	unsigned n;
+	u32 *data;
+
+	if (len > 64)
+		return -EFAIL;
+	if (!_usb_online)
+		return -ENODEV;
+
+	disable_interrupts();
+
+	data = _data;
+	n = read_sie(USB_CC_CLR_EPT((ep << 1) + 1));
+	if (n & 1) {
+		enable_interrupts();
+		return -EBUSY;
+	}
+
+	writel(USB_CTRL_WR_EN | USB_CTRL_EP_NUM(ep), USB_CTRL);
+	writel(len, USB_TX_PLEN);
+	while (len > 0) {
+		writel(*data++, USB_TX_DATA);
+		len -= 4;
+	}
+	writel(0, USB_CTRL);
+
+	n = read_sie(USB_CC_SEL_EPT((ep << 1) + 1));
+	n = read_sie(USB_CC_VAL_BUFFER);
+
+	enable_interrupts();
+
+	return 0;
+}
+
+int usb_ep1_write(void *_data, int len) {
+	return usb_epN_write(1, _data, len);
+}
+
+#if CONFIG_USB_2ND_IFC
+int usb_ep2_write(void *_data, int len) {
+	return usb_epN_write(2, _data, len);
+}
+#endif
+
+int usb_xmit(void *_data, int len) {
+	int r, tx, xfer;
+	u8 *data;
+
+	data = _data;
+	tx = 0;
+
+	while (len > 0) {
+		xfer = (len > 64) ? 64 : len;
+		r = usb_ep1_write(data, xfer);
+		if (r < 0) {
+			if (r == -EBUSY) {
+				usb_handle_irq();
+				continue;
+			}
+			return r;
+		}
+		tx += xfer;
+		len -= xfer;
+		data += xfer;
+	}
+	return tx;
+}
+
+void usb_init(unsigned vid, unsigned pid, const char *mfg, const char *prod) {
+	unsigned n;
+
+	ep0state = EP0_IDLE;
+
+	_dev00[8] = vid;
+	_dev00[9] = vid >> 8;
+	_dev00[10] = pid;
+	_dev00[11] = pid >> 8;
+
+#if CONFIG_USB_STRINGS
+	if (mfg) {
+		int i;
+		for (i = 0; mfg[i] != 0 && i < ((sizeof(mfg_string) / 2) - 1); i++) {
+			mfg_string[i+1] = mfg[i];
+		}
+		mfg_string[0] = (DSC_STRING << 8) | ((i * 2) + 2);
+
+		_dev00[14] = 1;
+	}
+	if (prod) {
+		int i;
+		for (i = 0; prod[i] != 0 && i < ((sizeof(prod_string) / 2) - 1); i++) {
+			prod_string[i+1] = prod[i];
+		}
+		prod_string[0] = (DSC_STRING << 8) | ((i * 2) + 2);
+
+		_dev00[15] = 2;
+	}
+#endif
+
+	/* SYSCLK to USB REG domain */
+	writel(readl(SYS_CLK_CTRL) | SYS_CLK_USB_REG, SYS_CLK_CTRL);
+
+	/* power on USB PHY and USB PLL */
+	writel(readl(0x40048238) & (~(1 << 10)), 0x40048238);
+	writel(readl(0x40048238) & (~(1 << 8)), 0x40048238);
+
+	/* wait for power */
+	for (n = 0; n < 10000; n++) asm("nop");
+
+	/* configure external IO mapping */
+	writel(IOCON_FUNC_1 | IOCON_DIGITAL, IOCON_PIO0_3); /* USB_VBUS */
+	writel(IOCON_FUNC_1 | IOCON_DIGITAL, IOCON_PIO0_6); /* USB_CONNECTn */
+
+	write_sie(USB_CC_SET_ADDR, 0x80); /* ADDR=0, EN=1 */
+	write_sie(USB_CC_SET_DEV_STATUS, 0x01); /* CONNECT */
+
+	writel(USB_INT_EP0 | USB_INT_EP1, USB_INT_ENABLE);
+}
+
+void usb_stop(void) {
+	write_sie(USB_CC_SET_ADDR, 0);
+	write_sie(USB_CC_SET_DEV_STATUS, 0);
+}
+
+void usb_mask_ep1_rx_full(void) {
+	disable_interrupts();
+	writel(readl(USB_INT_ENABLE) & (~USB_INT_EP2), USB_INT_ENABLE);
+	enable_interrupts();
+}
+void usb_unmask_ep1_rx_full(void) {
+	disable_interrupts();
+	writel(readl(USB_INT_ENABLE) | USB_INT_EP2, USB_INT_ENABLE);
+	enable_interrupts();
+}
+ 
+void usb_mask_ep1_tx_empty(void) {
+	disable_interrupts();
+	writel(readl(USB_INT_ENABLE) & (~USB_INT_EP3), USB_INT_ENABLE);
+	enable_interrupts();
+}
+void usb_unmask_ep1_tx_empty(void) {
+	disable_interrupts();
+	writel(readl(USB_INT_ENABLE) | USB_INT_EP3, USB_INT_ENABLE);
+	enable_interrupts();
+}
+
+#if CONFIG_USB_2ND_IFC
+void usb_mask_ep2_rx_full(void) {
+	disable_interrupts();
+	writel(readl(USB_INT_ENABLE) & (~USB_INT_EP4), USB_INT_ENABLE);
+	enable_interrupts();
+}
+void usb_unmask_ep2_rx_full(void) {
+	disable_interrupts();
+	writel(readl(USB_INT_ENABLE) | USB_INT_EP4, USB_INT_ENABLE);
+	enable_interrupts();
+}
+ 
+void usb_mask_ep2_tx_empty(void) {
+	disable_interrupts();
+	writel(readl(USB_INT_ENABLE) & (~USB_INT_EP5), USB_INT_ENABLE);
+	enable_interrupts();
+}
+void usb_unmask_ep2_tx_empty(void) {
+	disable_interrupts();
+	writel(readl(USB_INT_ENABLE) | USB_INT_EP5, USB_INT_ENABLE);
+	enable_interrupts();
+}
+#endif
+
+void (*usb_ep1_rx_full_cb)(void);
+void (*usb_ep1_tx_empty_cb)(void);
+#if CONFIG_USB_2ND_IFC
+void (*usb_ep2_rx_full_cb)(void);
+void (*usb_ep2_tx_empty_cb)(void);
+#endif
+void (*usb_online_cb)(int online);
+
+volatile int USB_IRQ_COUNT = 0;
+
+void usb_handle_irq(void) {
+	unsigned n;
+
+	USB_IRQ_COUNT++;
+
+//	P("usb %x\n", USB_IRQ_COUNT);
+
+	n = readl(USB_INT_STATUS);
+	//writel(n & (USB_INT_EP0 | USB_INT_EP1), USB_INT_CLEAR);
+	writel(n, USB_INT_CLEAR);
+	if (n & USB_INT_FRAME)
+		msec_counter++;
+	if (n & USB_INT_EP0)
+		usb_ep0_rx();
+	if (n & USB_INT_EP1)
+		usb_ep0_tx();
+
+	/* ignore masked interrupts */
+	n &= readl(USB_INT_ENABLE);
+
+	if (n & ~(USB_INT_FRAME)) {
+//		P("usb n 0x%x\n", n);
+	}
+
+	if ((n & USB_INT_EP2) && usb_ep1_rx_full_cb)
+		usb_ep1_rx_full_cb();
+	if ((n & USB_INT_EP3) && usb_ep1_tx_empty_cb)
+		usb_ep1_tx_empty_cb();
+#if CONFIG_USB_2ND_IFC
+	if ((n & USB_INT_EP4) && usb_ep2_rx_full_cb)
+		usb_ep2_rx_full_cb();
+	if ((n & USB_INT_EP5) && usb_ep2_tx_empty_cb)
+		usb_ep2_tx_empty_cb();
+#endif
+}
+
+#if CONFIG_USB_USE_IRQS
+void handle_irq_usb_irq(void) {
+	usb_handle_irq(void) {
+}
+#endif
diff --git a/arch/lpc13xx/usb-v1.h b/arch/lpc13xx/usb-v1.h
@@ -0,0 +1,55 @@
+#ifndef __ARCH_LPC13XX_USB_V1_H
+#define __ARCH_LPC13XX_USB_V1_H
+
+#define USB_INT_STATUS		0x40020000
+#define USB_INT_ENABLE		0x40020004
+#define USB_INT_CLEAR		0x40020008
+#define USB_INT_SET		0x4002000C
+#define USB_CMD_CODE		0x40020010
+#define USB_CMD_DATA		0x40020014
+#define USB_RX_DATA		0x40020018
+#define USB_TX_DATA		0x4002001C
+#define USB_RX_PLEN		0x40020020
+#define USB_TX_PLEN		0x40020024
+#define USB_CTRL		0x40020028
+#define USB_FIQ_SELECT		0x4002002C
+
+#define USB_INT_FRAME		(1 << 0)
+#define USB_INT_EP0		(1 << 1)
+#define USB_INT_EP1		(1 << 2)
+#define USB_INT_EP2		(1 << 3)
+#define USB_INT_EP3		(1 << 4)
+#define USB_INT_EP4		(1 << 5)
+#define USB_INT_EP5		(1 << 6)
+#define USB_INT_EP6		(1 << 7)
+#define USB_INT_EP7		(1 << 8)
+#define USB_INT_DEV_STAT	(1 << 9) /* RESET, SUSPEND, CONNECT */
+#define USB_INT_CC_EMPTY	(1 << 10) /* can write CMD_CODE */
+#define USB_INT_CD_FULL		(1 << 11) /* can read CMD_DATA */
+#define USB_INT_RX_END		(1 << 12)
+#define USB_INT_TX_END		(1 << 13)
+
+#define USB_CTRL_RD_EN		(1 << 0)
+#define USB_CTRL_WR_EN		(1 << 1)
+#define USB_CTRL_EP_NUM(n)	(((n) & 0xF) << 2)
+
+#define USB_OP_WRITE		0x0100
+#define USB_OP_READ		0x0200
+#define USB_OP_COMMAND		0x0500
+
+#define USB_CC_SET_ADDR		0xD00000
+#define USB_CC_CONFIG_DEV	0xD80000
+#define USB_CC_SET_MODE		0xF30000
+#define USB_CC_RD_INT_STATUS	0xF40000
+#define USB_CC_RD_FRAME_NUM	0xF50000
+#define USB_CC_RD_CHIP_ID	0xFD0000
+#define USB_CC_SET_DEV_STATUS	0xFE0000
+#define USB_CC_GET_DEV_STATUS	0xFE0000
+#define USB_CC_GET_ERROR_CODE	0xFF0000
+#define USB_CC_SEL_EPT(n)	(((n) & 0xF) << 16)
+#define USB_CC_CLR_EPT(n)	((((n) & 0xF) | 0x40) << 16)
+#define USB_CC_SET_EPT(n)	((((n) & 0xF) | 0x40) << 16)
+#define USB_CC_CLR_BUFFER	0xF20000
+#define USB_CC_VAL_BUFFER	0xFA0000
+
+#endif
diff --git a/arch/lpc13xx/usb-v2.c b/arch/lpc13xx/usb-v2.c
@@ -0,0 +1,720 @@
+/* usb.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * Copyright 2013-2014 Erik Gillikg <konkers@konkers.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/io.h>
+#include <fw/lib.h>
+#include <fw/string.h>
+#include <fw/types.h>
+
+#include <arch/cpu.h>
+#include <arch/interrupts.h>
+#include <arch/hardware.h>
+
+#include <protocol/usb.h>
+
+#include "usb-v2.h"
+
+#undef DEBUG
+
+#ifdef DEBUG
+#define usb_debug(fmt, args...) do { \
+		printx(fmt, ##args); \
+	} while (0)
+
+static inline void dump_bytes(void *addr, int len)
+{
+
+	int i;
+	for (i = 0; i <len; i++) {
+		if (i != 0)
+			printx(" ");
+		printx("%b", ((u8 *)addr)[i]);
+	}
+	printx("\n");
+}
+
+#else
+#define usb_debug(fmt, args...) do { \
+	} while (0)
+static inline void dump_bytes(void *addr, int len)
+{
+}
+#endif
+
+static volatile unsigned msec_counter = 0;
+
+void usb_handle_irq(void);
+
+static u8 _dev00[] = {
+	18,		/* size */
+	DSC_DEVICE,
+	0x00, 0x01,	/* version */
+	0xFF,		/* class */
+	0x00,		/* subclass */
+	0x00,		/* protocol */
+	0x40,		/* maxpacket0 */
+	0xd1, 0x18,	/* VID */
+	0x02, 0x65,	/* PID */
+	0x00, 0x01,	/* version */
+	0x00,		/* manufacturer string */
+	0x00,		/* product string */
+	0x00,		/* serialno string */
+	0x01,		/* configurations */
+};
+
+static u8 _cfg00[] = {
+	9,
+	DSC_CONFIG,
+	0x20, 0x00,	/* total length */
+	0x01,		/* ifc count */
+	0x01,		/* configuration value */
+	0x00,		/* configuration string */
+	0x80,		/* attributes */
+	50,		/* mA/2 */
+
+	9,
+	DSC_INTERFACE,
+	0x00,		/* interface number */
+	0x00,		/* alt setting */
+	0x02,		/* ept count */
+	0xFF,		/* class */
+	0x00,		/* subclass */
+	0x00,		/* protocol */
+	0x00,		/* interface string */
+
+	7,
+	DSC_ENDPOINT,
+	0x81,		/* address */
+	0x02,		/* bulk */
+	0x40, 0x00,	/* max packet size */
+	0x00,		/* interval */
+
+	7,
+	DSC_ENDPOINT,
+	0x01,		/* address */
+	0x02,		/* bulk */
+	0x40, 0x00,	/* max packet size */
+	0x00,		/* interval */
+};
+
+const static u8 lang_id[] = {
+	4,
+	DSC_STRING,
+	0x09, 0x04
+};
+
+static u16 mfg_string[24] = {
+};
+
+static u16 prod_string[24] = {
+};
+
+static void *usb_sram_highwater = (void *) USB_SRAM;
+static u32 *usb_ep_list;
+static u8 *usb_setup_buf;
+
+struct usb_ep {
+	u8 addr;
+
+	u16 in_size;
+	u16 in_max_packet;
+	i16 in_len;
+	u16 in_pos;
+
+	u16 out_size;
+	u16 out_len;
+	u16 out_pos;
+
+	u8 *out_buf;
+	u8 *in_buf;
+	/* no pingpong buffers supported yet */
+};
+
+static struct usb_ep usb_ep_data[5];
+
+enum {
+	USB_STATE_OFFLINE = 0,
+	USB_STATE_ONLINE,
+};
+
+static volatile unsigned usb_frames;
+volatile int usb_state;
+
+void (*usb_ep1_rx_full_cb)(void);
+void (*usb_ep1_tx_empty_cb)(void);
+void (*usb_ep2_rx_full_cb)(void);
+void (*usb_ep2_tx_empty_cb)(void);
+void (*usb_online_cb)(int online);
+
+
+static void *usb_sram_alloc(int size, int align)
+{
+	void *highwater = usb_sram_highwater;
+	highwater += (align - ((unsigned)usb_sram_highwater % align)) % align;
+	if (highwater + size > (void *)(USB_SRAM + USB_SRAM_SIZE)) {
+		printx("can't allocate 0x%x bytes of USB SRAM\n", size);
+		return 0;
+	}
+	usb_sram_highwater = highwater + size;
+
+	return highwater;
+}
+
+static void usb_setup_ep(struct usb_ep *ep, u8 addr, u16 in_buf_size, u16 in_max_packet, u16 out_buf_size)
+{
+	ep->addr = addr;
+
+	ep->in_buf = usb_sram_alloc(in_buf_size, 64);
+	ep->in_size = in_buf_size;
+	ep->in_len = -1;
+	ep->in_max_packet = in_max_packet;
+	ep->out_buf = usb_sram_alloc(out_buf_size, 64);
+	ep->out_size = out_buf_size;
+}
+
+void usb_init(unsigned vid, unsigned pid, const char *mfg, const char *prod) {
+	unsigned n;
+
+	irq_enable(v_usb_irq);
+
+	usb_state = USB_STATE_OFFLINE;
+
+	_dev00[8] = vid;
+	_dev00[9] = vid >> 8;
+	_dev00[10] = pid;
+	_dev00[11] = pid >> 8;
+
+	if (mfg) {
+		int i;
+		for (i = 0; mfg[i] != 0 && i < ((sizeof(mfg_string) / 2) - 1); i++) {
+			mfg_string[i+1] = mfg[i];
+		}
+		mfg_string[0] = (DSC_STRING << 8) | ((i * 2) + 2);
+
+		_dev00[14] = 1;
+	}
+	if (prod) {
+		int i;
+		for (i = 0; prod[i] != 0 && i < ((sizeof(prod_string) / 2) - 1); i++) {
+			prod_string[i+1] = prod[i];
+		}
+		prod_string[0] = (DSC_STRING << 8) | ((i * 2) + 2);
+
+		_dev00[15] = 2;
+	}
+
+	/* SYSCLK to USB REG domain */
+	writel(readl(SYS_CLK_CTRL) | SYS_CLK_USB_REG | SYS_CLK_USBSRAM, SYS_CLK_CTRL);
+
+	writel(0, USB_DEVCMDSTAT);
+
+	/* power on USB PHY and USB PLL */
+	writel(readl(0x40048238) & (~(1 << 10)), 0x40048238);
+	writel(readl(0x40048238) & (~(1 << 8)), 0x40048238);
+
+	/* wait for power */
+	for (n = 0; n < 10000; n++) asm("nop");
+
+	usb_ep_list = usb_sram_alloc(0x50, 256);
+	usb_setup_buf = usb_sram_alloc(9, 64);
+
+	usb_setup_ep(&usb_ep_data[0], 0x0, 64, 64, 64);
+	usb_setup_ep(&usb_ep_data[1], 0x1, 128, 64, 64);
+
+	for (n = 0; n < 4; n++)
+		usb_ep_list[n] = 0;
+	for (n = 4; n < 20; n++)
+		usb_ep_list[n] = USB_EP_LIST_DISABLED;
+	for (n = 0; n < 9; n++)
+		usb_setup_buf[n] = 0;
+
+	usb_ep_list[EP_LIST_SETUP] = USB_EP_LIST_BUF_ADDR(usb_setup_buf);
+	writel((unsigned)usb_ep_list, USB_EPLISTSTART);
+	writel(USB_SRAM, USB_DATABUFSTART);
+
+
+	writel(0, USB_INTROUTING);
+	writel(USB_DEVCMDSTAT_DEV_ADDR(0) |
+	       USB_DEVCMDSTAT_DEV_EN |
+	       USB_DEVCMDSTAT_DCON |
+	       USB_DEVCMDSTAT_DSUS,
+	       USB_DEVCMDSTAT);
+
+	writel(USB_INT_DEV_INT |
+	       USB_INT_EP0OUT | USB_INT_EP0IN,
+	       USB_INTEN);
+}
+
+static void usb_send_in(struct usb_ep *ep)
+{
+//	unsigned ep_out_cmd = usb_ep_list[ep->addr * 4];
+	unsigned ep_cmd = usb_ep_list[ep->addr * 4 + 2];
+	ep_cmd = USB_EP_LIST_BUF_ADDR(ep->in_buf + ep->in_pos) |
+		USB_EP_LIST_BUF_SIZE(min(ep->in_len, ep->in_max_packet)) |
+		USB_EP_LIST_ACTIVE;
+
+//	if (ep->in_len >= ep->in_max_packet)
+//		ep_cmd |= USB_EP_LIST_STALL;
+//	ep_out_cmd = clr_set_bits(ep_out_cmd, USB_EP_LIST_ACTIVE, USB_EP_LIST_STALL);
+//	usb_ep_list[ep->addr * 4] = ep_out_cmd;
+	usb_ep_list[ep->addr * 4 + 2] = ep_cmd;
+
+	/* clear error code */
+	writel(0x0, USB_INFO);
+
+//	if (len)
+//		clr_set_reg(USB_DEVCMDSTAT, USB_DEVCMDSTAT_INTONNAK_CO, USB_DEVCMDSTAT_INTONNAK_CI);
+}
+
+static void usb_send_zlp(struct usb_ep *ep)
+{
+	ep->in_pos = 0;
+	ep->in_len = 0;
+	usb_send_in(ep);
+}
+
+static void usb_nak(struct usb_ep *ep)
+{
+	unsigned ep_out_cmd = usb_ep_list[ep->addr * 4];
+	unsigned ep_in_cmd = usb_ep_list[ep->addr * 4 + 2];
+
+	ep_out_cmd = clr_set_bits(ep_out_cmd, USB_EP_LIST_ACTIVE, USB_EP_LIST_STALL);
+	ep_in_cmd = clr_set_bits(ep_in_cmd, USB_EP_LIST_ACTIVE, USB_EP_LIST_STALL);
+
+	usb_ep_list[ep->addr * 4 + 2] = ep_in_cmd;
+	usb_ep_list[ep->addr * 4] = ep_out_cmd;
+}
+
+static int usb_ep_write(struct usb_ep *ep, const void *data, int len)
+{
+	if (len > ep->in_size)
+		return -EFAIL;
+
+	if (ep->in_len >= 0)
+		return -EBUSY;
+
+	memcpy(ep->in_buf, data, len);
+	ep->in_len = len;
+	ep->in_pos = 0;
+	usb_send_in(ep);
+
+	return 0;
+}
+
+static void usb_send_desc(const void *desc, int len)
+{
+	if (usb_ep_data[0].in_size < len) {
+		usb_debug("descriptor size (%x) larger than in buffer (%x)\n",
+		       len, usb_ep_data[0].in_size);
+		usb_nak(&usb_ep_data[0]);
+		return;
+	}
+	usb_ep_write(&usb_ep_data[0], desc, len);
+}
+
+
+static void usb_handle_get_desc(struct usb_setup_req *req)
+{
+	switch (req->wValue) {
+	case USB_DESC_VALUE(USB_DESC_DEVICE, 0):
+		usb_send_desc(_dev00, min(sizeof(_dev00),req->wLength));
+		break;
+
+	case USB_DESC_VALUE(USB_DESC_CONFIG, 0):
+		usb_send_desc(_cfg00, min(sizeof(_cfg00),req->wLength));
+		break;
+
+	case USB_DESC_VALUE(USB_DESC_STRING, 0):
+		usb_send_desc(lang_id, min(sizeof(lang_id),req->wLength));
+		break;
+
+	case USB_DESC_VALUE(USB_DESC_STRING, 1):
+		usb_send_desc(mfg_string, min(sizeof(mfg_string),req->wLength));
+		break;
+
+	case USB_DESC_VALUE(USB_DESC_STRING, 2):
+		usb_send_desc(prod_string, min(sizeof(prod_string),req->wLength));
+		break;
+
+	default:
+		usb_debug("unknown desc: %h\n", req->wValue);
+
+		usb_nak(&usb_ep_data[0]);
+		break;
+	}
+}
+
+static void usb_config_ep(struct usb_ep *ep)
+{
+	/* out buf 0 */
+//	usb_ep_list[ep->addr * 4] = USB_EP_LIST_TR;
+	usb_ep_list[ep->addr * 4] =  USB_EP_LIST_BUF_ADDR(ep->out_buf) |
+		USB_EP_LIST_BUF_SIZE(ep->out_size) |
+		USB_EP_LIST_ACTIVE | /* USB_EP_LIST_STALL | */ USB_EP_LIST_TR;
+
+	/* in buf 0 */
+	usb_ep_list[ep->addr * 4 + 2] =  USB_EP_LIST_TR; // USB_EP_LIST_STALL;
+
+	clr_set_reg(USB_INTEN, 0, 0x3 << (ep->addr * 2));
+}
+
+static void usb_handle_set_config(unsigned config)
+{
+        usb_debug("set_config(%d)\n", config);
+	if (config != 1) {
+		usb_nak(&usb_ep_data[0]);
+		return;
+	}
+	usb_config_ep(&usb_ep_data[1]);
+//	clr_set_reg(USB_DEVCMDSTAT, 0, USB_DEVCMDSTAT_INTONNAK_AI);
+
+	usb_send_zlp(&usb_ep_data[0]);
+	usb_state = USB_STATE_ONLINE;
+	if (usb_online_cb)
+		usb_online_cb(1);
+        printx("state = %d\n", usb_state);
+}
+static void usb_handle_setup_req(struct usb_setup_req *req)
+{
+	switch (req->bRequest) {
+	case USB_REQ_GET_DESCRIPTOR:
+		usb_handle_get_desc(req);
+		break;
+	case USB_REQ_SET_ADDRESS:
+		clr_set_reg(USB_DEVCMDSTAT, USB_DEVCMDSTAT_DEV_ADDR(~0),
+			    USB_DEVCMDSTAT_DEV_ADDR(req->wValue));
+		usb_send_zlp(&usb_ep_data[0]);
+		break;
+	case USB_REQ_GET_CONFIGURATION: {
+                u8 config = usb_state == USB_STATE_ONLINE;
+                usb_debug("get_config(%d)\n", config);
+                usb_ep_write(&usb_ep_data[0], &config, 1);
+                break;
+        }
+
+	case USB_REQ_SET_CONFIGURATION:
+		usb_handle_set_config(req->wValue);
+		break;
+
+	default:
+		usb_debug("unknown setup req:\n");
+		usb_debug("  bmRequestType: %b\n", req->bmRequestType);
+		usb_debug("     dir: %b\n", req->t.dir);
+		usb_debug("     type: %b\n", req->t.type);
+		usb_debug("     rcpt: %b\n", req->t.rcpt);
+		usb_debug("  bRequest:      %b\n", req->bRequest);
+		usb_debug("  wValue:        %h\n", req->wValue);
+		usb_debug("  wIndex:        %h\n", req->wIndex);
+		usb_debug("  wLength:       %h\n", req->wLength);
+
+		usb_nak(&usb_ep_data[0]);
+		break;
+	}
+}
+
+void usb_handle_dev_int(void)
+{
+	unsigned cmdstat = readl(USB_DEVCMDSTAT);
+	int n;
+
+	if (cmdstat & USB_DEVCMDSTAT_DCON_C) {
+	}
+	if (cmdstat & USB_DEVCMDSTAT_DSUS_C) {
+	}
+	if (cmdstat & USB_DEVCMDSTAT_DRES_C) {
+		for (n = 4; n < 20; n++)
+			usb_ep_list[n] = USB_EP_LIST_DISABLED;
+	}
+	writel(cmdstat, USB_DEVCMDSTAT);
+	writel(USB_INT_DEV_INT, USB_INTSTAT);
+}
+
+void usb_handle_ep0out(void)
+{
+	writel(USB_INT_EP0OUT, USB_INTSTAT);
+	unsigned cmdstat = readl(USB_DEVCMDSTAT);
+
+	if (cmdstat & USB_DEVCMDSTAT_SETUP) {
+		struct usb_setup_req *req;
+		usb_ep_list[EP_LIST_EP0_OUT] &= ~(USB_EP_LIST_ACTIVE | USB_EP_LIST_STALL);
+		usb_ep_list[EP_LIST_EP0_IN] &= ~(USB_EP_LIST_ACTIVE | USB_EP_LIST_STALL);
+
+		writel(USB_INT_EP0IN, USB_INTSTAT);
+		writel(cmdstat, USB_DEVCMDSTAT);
+
+		req = (struct usb_setup_req *)usb_setup_buf;
+		usb_handle_setup_req(req);
+	} else {
+		usb_ep_list[EP_LIST_EP0_OUT] &= ~(USB_EP_LIST_STALL);
+
+	}
+}
+
+void usb_handle_ep0in(void)
+{
+	writel(USB_INT_EP0IN, USB_INTSTAT);
+	unsigned ep_in_cmd = usb_ep_list[EP_LIST_EP0_IN];
+
+	if (usb_ep_data[0].in_len >= 0) {
+		unsigned ep_out_cmd = usb_ep_list[EP_LIST_EP0_OUT];
+
+		ep_out_cmd = USB_EP_LIST_BUF_ADDR(usb_ep_data[0].out_buf) |
+			USB_EP_LIST_BUF_SIZE(usb_ep_data[0].out_size) |
+			USB_EP_LIST_STALL | USB_EP_LIST_ACTIVE ;
+
+
+		usb_ep_list[EP_LIST_EP0_OUT] = ep_out_cmd;
+	}
+
+	ep_in_cmd = clr_set_bits(ep_in_cmd, USB_EP_LIST_ACTIVE, USB_EP_LIST_STALL);
+
+	usb_ep_list[EP_LIST_EP0_IN] = ep_in_cmd;
+	if (usb_ep_data[0].in_len >= 0) {
+		writel(USB_INT_EP0OUT, USB_INTSTAT);
+		usb_ep_data[0].in_len = -1;
+	}
+}
+
+static void usb_handle_in(struct usb_ep *ep, unsigned int_mask)
+{
+	int pos = ep->in_pos;
+	pos -= ep->in_max_packet;
+//	printx("ep%b in %x %b\n", ep->addr, usb_ep_list[ep->addr * 4 + 2],
+//	       USB_INFO_ERR_CODE(readl(USB_INFO)));
+
+	writel(int_mask, USB_INTSTAT);
+
+	if (pos < 0) {
+		ep->in_pos = 0;
+		ep->in_len = -1;
+		if (ep->addr == 0x01 && usb_ep1_tx_empty_cb)
+			usb_ep1_tx_empty_cb();
+		if (ep->addr == 0x02 && usb_ep2_tx_empty_cb)
+			usb_ep2_tx_empty_cb();
+	} else if (pos == 0) {
+		ep->in_pos = 0;
+		usb_send_zlp(ep);
+	} else {
+		ep->in_pos = pos;
+		usb_send_in(ep);
+	}
+}
+
+static void usb_handle_out(struct usb_ep *ep, unsigned int_mask)
+{
+	unsigned cmd = usb_ep_list[ep->addr * 4];
+
+//	usb_debug("usb_out ep%b %x\n", ep->addr, usb_ep_list[ep->addr * 4]);
+	ep->out_len = ep->out_size - USB_EP_LIST_GET_BUF_SIZE(cmd);
+	ep->out_pos = 0;
+
+	if (usb_ep1_rx_full_cb)
+		usb_ep1_rx_full_cb();
+
+	writel(int_mask, USB_INTSTAT);
+}
+
+static int usb_ep_read(struct usb_ep *ep, void *data, int max)
+{
+	int len = min(ep->out_len - ep->out_pos, max);
+
+	if (len == 0)
+		return -EBUSY;
+
+	memcpy(data, ep->out_buf + ep->out_pos, len);
+
+	/* XXX: not rentrant with the irq handler!*/
+	ep->out_pos += len;
+
+	if (ep->out_pos == ep->out_len) {
+		usb_ep_list[ep->addr * 4] =  USB_EP_LIST_BUF_ADDR(ep->out_buf) |
+			USB_EP_LIST_BUF_SIZE(ep->out_size) |
+			USB_EP_LIST_ACTIVE;// | USB_EP_LIST_STALL;
+	}
+
+	return len;
+}
+
+
+void handle_irq_usb_irq(void) {
+	unsigned status = readl(USB_INTSTAT);
+
+        status &= readl(USB_INTEN);
+
+	if (status & USB_INT_FRAME_INT) {
+		usb_frames++;
+		writel(USB_INT_FRAME_INT, USB_INTEN);
+	}
+
+
+	if (status & USB_INT_DEV_INT)
+		usb_handle_dev_int();
+	if (status & USB_INT_EP0OUT)
+		usb_handle_ep0out();
+	if (status & USB_INT_EP0IN)
+		usb_handle_ep0in();
+	if (status & USB_INT_EP1OUT)
+		usb_handle_out(&usb_ep_data[1], USB_INT_EP1OUT);
+	if (status & USB_INT_EP1IN)
+		usb_handle_in(&usb_ep_data[1], USB_INT_EP1IN);
+
+
+}
+
+void usb_handle_irq(void) {
+	handle_irq_usb_irq();
+}
+
+
+/* USB API */
+int usb_ep1_read(void *data, int max)
+{
+	return usb_ep_read(&usb_ep_data[1], data, max);
+}
+
+
+int usb_ep1_write(void *data, int len)
+{
+	return usb_ep_write(&usb_ep_data[1], data, len);
+}
+
+int usb_ep2_read(void *data, int max)
+{
+	return -1; // XXX real error
+}
+
+int usb_ep2_write(void *data, int len)
+{
+	return -1; // XXX real error
+}
+
+void usb_mask_ep1_rx_full(void)
+{
+
+}
+
+void usb_unmask_ep1_rx_full(void)
+{
+
+}
+
+void usb_mask_ep1_tx_empty(void)
+{
+
+}
+
+void usb_unmask_ep1_tx_empty(void)
+{
+
+}
+
+void usb_mask_ep2_rx_full(void)
+{
+
+}
+
+void usb_unmask_ep2_rx_full(void)
+{
+
+}
+
+void usb_mask_ep2_tx_empty(void)
+{
+
+}
+
+void usb_unmask_ep2_tx_empty(void)
+{
+
+}
+
+
+int usb_xmit(void *_data, int len) {
+	int r, tx, xfer;
+	u8 *data;
+
+	data = _data;
+	tx = 0;
+
+	while (len > 0) {
+		xfer = (len > 64) ? 64 : len;
+		r = usb_ep1_write(data, xfer);
+		if (r < 0) {
+			if (r == -EBUSY) {
+				usb_handle_irq();
+				continue;
+			}
+			return r;
+		}
+		tx += xfer;
+		len -= xfer;
+		data += xfer;
+	}
+	return tx;
+}
+
+int usb_recv_timeout(void *_data, int count, unsigned timeout) {
+	int r, rx;
+	u8 *data;
+
+	data = _data;
+	rx = 0;
+	usb_frames = 0;
+
+	/* if offline, wait for us to go online */
+	while (usb_state == USB_STATE_OFFLINE)
+		usb_handle_irq();
+
+	while (count > 0) {
+		r = usb_ep1_read(data, (count > 64) ? 64 : count);
+		if (r >= 0) {
+			rx += r;
+			data += r;
+			count -= r;
+			/* terminate on short packet */
+			if (r != 64)
+				break;
+		} else if (r == -EBUSY) {
+			if (timeout && (usb_frames > timeout))
+				return -ETIMEOUT;
+			usb_handle_irq();
+		} else {
+			return r;
+		}
+	}
+	return rx;
+}
+
+int usb_recv(void *_data, int count) {
+	return usb_recv_timeout(_data, count, 0);
+}
+
+int usb_online(void) {
+	return usb_state == USB_STATE_ONLINE;
+}
+
+void usb_stop(void) {
+	/* disable dev */
+	writel(0, USB_DEVCMDSTAT);
+
+	/* power off USB PHY and USB PLL */
+	clr_set_reg(0x40048238, (1 << 10) | (1 << 8), 0);
+
+	/* turn of SYSCLK to USB REG domain */
+	clr_set_reg(SYS_CLK_CTRL, SYS_CLK_USB_REG | SYS_CLK_USBSRAM, 0);
+}
diff --git a/arch/lpc13xx/usb-v2.h b/arch/lpc13xx/usb-v2.h
@@ -0,0 +1,92 @@
+#ifndef __ARCH_LPC13XX_USB_V2_H
+#define __ARCH_LPC13XX_USB_V2_H
+
+#define USB_BASE		0x40080000
+#define USB_SRAM		0x20004000
+#define USB_SRAM_SIZE		0x800
+
+#define USB_DEVCMDSTAT		(USB_BASE + 0x00)
+#define  USB_DEVCMDSTAT_DEV_ADDR(x)	((x) & 0x7f)
+#define  USB_DEVCMDSTAT_DEV_EN		(1 << 7)
+#define  USB_DEVCMDSTAT_SETUP		(1 << 8)
+#define  USB_DEVCMDSTAT_PLL_ON		(1 << 9)
+#define  USB_DEVCMDSTAT_LPM_SUP		(1 << 11)
+#define  USB_DEVCMDSTAT_INTONNAK_AO	(1 << 12)
+#define  USB_DEVCMDSTAT_INTONNAK_AI	(1 << 13)
+#define  USB_DEVCMDSTAT_INTONNAK_CO	(1 << 14)
+#define  USB_DEVCMDSTAT_INTONNAK_CI	(1 << 15)
+#define  USB_DEVCMDSTAT_DCON		(1 << 16)
+#define  USB_DEVCMDSTAT_DSUS		(1 << 17)
+#define  USB_DEVCMDSTAT_LPM_SUS		(1 << 19)
+#define  USB_DEVCMDSTAT_LPM_REWP	(1 << 20)
+#define  USB_DEVCMDSTAT_DCON_C		(1 << 24)
+#define  USB_DEVCMDSTAT_DSUS_C		(1 << 25)
+#define  USB_DEVCMDSTAT_DRES_C		(1 << 26)
+#define  USB_DEVCMDSTAT_VBUSDEBOUNCED	(1 << 28)
+
+#define USB_INFO		(USB_BASE + 0x04)
+#define  USB_INFO_FRAME_NR(reg)		((reg) & 0x7ff)
+#define  USB_INFO_ERR_CODE(reg)		(((reg) >> 11) & 0xf)
+
+#define USB_EPLISTSTART		(USB_BASE + 0x08) /* must be 256 byte aligned */
+#define USB_DATABUFSTART	(USB_BASE + 0x0C) /* musb be 0x400000 alignd */
+
+#define USB_LPM			(USB_BASE + 0x10)
+
+#define USB_EPSKIP		(USB_BASE + 0x14)
+#define USB_EPINUSE		(USB_BASE + 0x18)
+#define USB_EPBUFCFG		(USB_BASE + 0x1C)
+
+#define USB_INTSTAT		(USB_BASE + 0x20)
+#define USB_INTEN		(USB_BASE + 0x24)
+#define USB_INTSETSTAT		(USB_BASE + 0x28)
+#define USB_INTROUTING		(USB_BASE + 0x2C)
+#define  USB_INT_EP0OUT			(1 << 0)
+#define  USB_INT_EP0IN			(1 << 1)
+#define  USB_INT_EP1OUT			(1 << 2)
+#define  USB_INT_EP1IN			(1 << 3)
+#define  USB_INT_EP2OUT			(1 << 4)
+#define  USB_INT_EP2IN			(1 << 5)
+#define  USB_INT_EP3OUT			(1 << 6)
+#define  USB_INT_EP3IN			(1 << 7)
+#define  USB_INT_EP4OUT			(1 << 8)
+#define  USB_INT_EP4IN			(1 << 9)
+#define  USB_INT_FRAME_INT		(1 << 30)
+#define  USB_INT_DEV_INT		(1 << 31)
+
+#define USB_EPTOGGLE		(USB_BASE + 0x34)
+
+#define USB_EP_LIST_BUF_ADDR(addr)		(((unsigned)(addr) >> 6) & 0xffff)
+#define USB_EP_LIST_BUF_SIZE(size)		(((size) & 0x3ff) << 16)
+#define USB_EP_LIST_GET_BUF_SIZE(reg)		(((reg) >> 16) & 0x3ff)
+#define USB_EP_LIST_TYPE			(1 << 26) /* Endpoint Type */
+#define USB_EP_LIST_RF_TV			(1 << 27) /* Rate Feedback/Toggle value */
+#define USB_EP_LIST_TR				(1 << 28) /* Toggle Reset */
+#define USB_EP_LIST_STALL			(1 << 29) /* Stall */
+#define USB_EP_LIST_DISABLED			(1 << 30) /* Disabled */
+#define USB_EP_LIST_ACTIVE			(1 << 31) /* Active */
+
+enum {
+	EP_LIST_EP0_OUT = 0,
+	EP_LIST_SETUP,
+	EP_LIST_EP0_IN,
+	EP_LIST_RES,
+	EP_LIST_EP1_OUT0,
+	EP_LIST_EP1_OUT1,
+	EP_LIST_EP1_IN0,
+	EP_LIST_EP1_IN1,
+	EP_LIST_EP2_OUT0,
+	EP_LIST_EP2_OUT1,
+	EP_LIST_EP2_IN0,
+	EP_LIST_EP2_IN1,
+	EP_LIST_EP3_OUT0,
+	EP_LIST_EP3_OUT1,
+	EP_LIST_EP3_IN0,
+	EP_LIST_EP3_IN1,
+	EP_LIST_EP4_OUT0,
+	EP_LIST_EP4_OUT1,
+	EP_LIST_EP4_IN0,
+	EP_LIST_EP4_IN1,
+};
+
+#endif
diff --git a/arch/stm32f1xx/config.mk b/arch/stm32f1xx/config.mk
@@ -0,0 +1,14 @@
+
+#           name          arch      rambase    ramsize    flashbase  flashsize  linkscript
+$(call chip,stm32f103-rom,stm32f1xx,0x20000000,0x00005000,0x08000000,0x00020000,rom)
+
+
+ARCH_stm32f1xx_CFLAGS := -Iarch/stm32f1xx/include
+ARCH_stm32f1xx_CFLAGS += -Iarch/arm-cm3/include
+ARCH_stm32f1xx_CFLAGS += -DCONFIG_STACKTOP=0x20005000
+ARCH_stm32f1xx_START := arch/arm-cm3/start.o
+
+ARCH_stm32f1xx_OBJS := \
+	arch/stm32f1xx/gpio.o \
+	arch/stm32f1xx/serial.o \
+	arch/stm32f1xx/usb.o
diff --git a/arch/stm32f1xx/gpio.c b/arch/stm32f1xx/gpio.c
@@ -0,0 +1,51 @@
+/* gpio.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/lib.h>
+#include <fw/io.h>
+
+#include <arch/hardware.h>
+
+static unsigned gpio_cr[4] = {
+	GPIOA_BASE + GPIO_CRL,
+	GPIOA_BASE + GPIO_CRH,
+	GPIOB_BASE + GPIO_CRL,
+	GPIOB_BASE + GPIO_CRH,
+};
+
+void gpio_config(unsigned n, unsigned cfg)
+{
+	unsigned addr = gpio_cr[n >> 3];
+	unsigned shift = (n & 7) * 4;
+	unsigned val = readl(addr);
+	val = (val & (~(0xF << shift))) | (cfg << shift);
+	writel(val, addr);
+}
+
+void gpio_set(unsigned n)
+{
+	unsigned addr = (n > 15) ? GPIOB_BASE : GPIOA_BASE;
+	writel(1 << (n & 15), addr + GPIO_BSR);
+}
+
+void gpio_clr(unsigned n)
+{
+	unsigned addr = (n > 15) ? GPIOB_BASE : GPIOA_BASE;
+	writel(1 << (n & 15), addr + GPIO_BRR);
+}
+
diff --git a/arch/stm32f1xx/include/arch/hardware.h b/arch/stm32f1xx/include/arch/hardware.h
@@ -0,0 +1,219 @@
+/* hardware.h
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __STM32_HW_H_
+#define __STM32_HW_H_
+
+#define RCC_BASE	0x40021000
+#define DMA2_BASE	0x40020000
+#define DMA1_BASE	0x40020400
+
+#define USART1_BASE	0x40013800
+#define SPI1_BASE	0x40013000
+#define TIM1_BASE	0x40012C00
+#define ADC1_BASE	0x40012400
+
+#define GPIOD_BASE	0x40011400
+#define GPIOC_BASE	0x40011000
+#define GPIOB_BASE	0x40010C00
+#define GPIOA_BASE	0x40010800
+
+#define PWR_BASE	0x40007000
+#define BKP_BASE	0x40006C00
+#define USB_SRAM_BASE	0x40006000
+#define USB_BASE	0x40005C00
+#define I2C2_BASE	0x40005800
+#define I2C1_BASE	0x40005400
+
+
+#define RCC_CR		(RCC_BASE + 0x00)
+#define RCC_CFGR	(RCC_BASE + 0x04)
+#define RCC_CIR		(RCC_BASE + 0x08)
+#define RCC_APB2RSTR	(RCC_BASE + 0x0C)
+#define RCC_APB1RSTR	(RCC_BASE + 0x10)
+#define RCC_AHBENR	(RCC_BASE + 0x14)
+#define RCC_APB2ENR	(RCC_BASE + 0x18)
+#define RCC_APB1ENR	(RCC_BASE + 0x1C)
+#define RCC_BDCR	(RCC_BASE + 0x20)
+#define RCC_CSR		(RCC_BASE + 0x24)
+
+/* for RCC_APB2_{RSTR,ENR} */
+#define RCC_APB2_AFIO	(1 << 0)
+#define RCC_APB2_GPIOA	(1 << 2)
+#define RCC_APB2_GPIOB	(1 << 3)
+#define RCC_APB2_GPIOC	(1 << 4)
+#define RCC_APB2_TIM1	(1 << 11)
+#define RCC_APB2_SPI1	(1 << 12)
+#define RCC_APB2_USART1	(1 << 14)
+
+#define RCC_APB1_USB	(1 << 23)
+
+
+#define GPIO_CRL	0x00
+#define GPIO_CRH	0x04
+#define GPIO_IDR	0x08
+#define GPIO_ODR	0x0C
+#define GPIO_BSRR	0x10
+#define GPIO_BSR	0x10
+#define GPIO_BRR	0x14
+#define GPIO_LCKR	0x18
+
+/* base mode */
+#define GPIO_INPUT		0x00
+#define GPIO_OUTPUT_10MHZ	0x01
+#define GPIO_OUTPUT_2MHZ	0x02
+#define GPIO_OUTPUT_50MHZ	0x50
+/* input submodes */
+#define GPIO_ANALOG		0x00
+#define GPIO_FLOATING		0x04
+#define GPIO_PU_PD		0x08
+/* output submodes */
+#define GPIO_OUT_PUSH_PULL	0x00
+#define GPIO_OUT_OPEN_DRAIN	0x04
+#define GPIO_ALT_PUSH_PULL	0x08
+#define GPIO_ALT_OPEN_DRAIN	0x0C
+
+#define USART_SR		0x00
+#define USART_DR		0x04
+#define USART_BRR		0x08
+#define USART_CR1		0x0C
+#define USART_CR2		0x10
+#define USART_CR3		0x14
+#define USART_GTPR		0x18
+
+#define USART_SR_TXE		(1 << 7)
+#define USART_SR_RXNE		(1 << 5)
+
+#define USART_CR1_ENABLE	(1 << 13) // enable
+#define USART_CR1_9BIT		(1 << 12)
+#define USART_CR1_PARITY	(1 << 10)
+#define USART_CR1_ODD		(1 << 9)
+#define USART_CR1_TX_ENABLE	(1 << 3)
+#define USART_CR1_RX_ENABLE	(1 << 2)
+
+
+#define SPI_CR1			0x00
+#define SPI_CR2			0x04
+#define SPI_SR			0x08
+#define SPI_DR			0x0C
+
+#define SPI_CR1_BIDI_MODE	(1 << 15)
+#define SPI_CR1_BIDI_OE		(1 << 14)
+#define SPI_CR1_8BIT		(0 << 11)
+#define SPI_CR1_16BIT		(1 << 11)
+#define SPI_CR1_RX_ONLY		(1 << 10)
+#define SPI_CR1_SSM		(1 << 9) /* sw control of NSS */
+#define SPI_CR1_SSI		(1 << 8) 
+#define SPI_CR1_MSB_FIRST	(0 << 7)
+#define SPI_CR1_LSB_FIRST	(1 << 7)
+#define SPI_CR1_ENABLE		(1 << 6)
+#define SPI_CR1_CLKDIV_2	(0 << 3)
+#define SPI_CR1_CLKDIV_4	(1 << 3)
+#define SPI_CR1_CLKDIV_8	(2 << 3)
+#define SPI_CR1_CLKDIV_16	(3 << 3)
+#define SPI_CR1_CLKDIV_32	(4 << 3)
+#define SPI_CR1_CLKDIV_64	(5 << 3)
+#define SPI_CR1_CLKDIV_128	(6 << 3)
+#define SPI_CR1_CLKDIV_256	(7 << 3)
+#define SPI_CR1_MASTER		(1 << 2)
+#define SPI_CR1_CK_NEG		(0 << 1)
+#define SPI_CR1_CK_POS		(1 << 1)
+#define SPI_CR1_CK_PHASE0	(0 << 0)
+#define SPI_CR1_CK_PHASE1	(1 << 0)
+
+#define SPI_CR2_SS_OE		(1 << 2) /* enable in single-master mode */
+
+#define SPI_SR_BUSY		(1 << 7)
+#define SPI_SR_OVERRUN		(1 << 6)
+#define SPI_SR_MODE_FAULT	(1 << 5)
+#define SPI_SR_UNDERRUN		(1 << 3)
+#define SPI_SR_TX_EMPTY		(1 << 1)
+#define SPI_SR_RX_FULL		(1 << 0)
+
+
+#define USB_EPR(n)		(USB_BASE + (n * 4))
+#define USB_CR			(USB_BASE + 0x40)
+#define USB_ISR			(USB_BASE + 0x44)
+#define USB_FNR			(USB_BASE + 0x48)
+#define USB_DADDR		(USB_BASE + 0x4C)
+#define USB_BTABLE		(USB_BASE + 0x50)
+
+/* the *M bits apply to both CR (to enable) and ISR (to read) */
+#define USB_CTRM		(1 << 15)
+#define USB_PMAOVRM		(1 << 14)
+#define USB_ERRM		(1 << 13)
+#define USB_WKUPM		(1 << 12)
+#define USB_SUSPM		(1 << 11)
+#define USB_RESETM		(1 << 10)
+#define USB_SOFM		(1 << 9)
+#define USB_ESOFM		(1 << 8)
+
+#define USB_CR_RESUME		(1 << 4)
+#define USB_CR_FSUSP		(1 << 3)
+#define USB_CR_LP_MODE		(1 << 2)
+#define USB_CR_PDWN		(1 << 1)
+#define USB_CR_FRES		(1 << 0)
+
+#define USB_ISR_DIR		(1 << 4)
+#define USB_ISR_EP_MASK		0xF
+
+#define USB_DADDR_ENABLE	(1 << 7)
+
+#define USB_EPR_CTR_RX		(1 << 15) // R+W0C
+#define USB_EPR_DTOG_RX		(1 << 14) // T
+#define USB_EPR_RX_DISABLE	(0 << 12) // T
+#define USB_EPR_RX_STALL	(1 << 12) // T
+#define USB_EPR_RX_NAK		(2 << 12) // T
+#define USB_EPR_RX_VALID	(3 << 12) // T
+#define USB_EPR_SETUP		(1 << 11) // RO
+#define USB_EPR_TYPE_BULK	(0 << 9)  // RW
+#define USB_EPR_TYPE_CONTROL	(1 << 9)  // RW
+#define USB_EPR_TYPE_ISO	(2 << 9)  // RW
+#define USB_EPR_TYPE_INTERRRUPT	(3 << 9)  // RW
+#define USB_EPR_TYPE_MASK	(3 << 9)
+#define USB_EPR_DBL_BUF		(1 << 8)  // RW (for BULK)
+#define USB_EPR_STATUS_OUT	(1 << 8)  // RW (for CONTROL)
+#define USB_EPR_CTR_TX		(1 << 7)  // R+W0C
+#define USB_EPR_DTOG_TX		(1 << 6)  // T
+#define USB_EPR_TX_DISABLED	(0 << 4)  // T
+#define USB_EPR_TX_STALL	(1 << 4)  // T
+#define USB_EPR_TX_NAK		(2 << 4)  // T
+#define USB_EPR_TX_VALID	(3 << 4)  // T
+#define USB_EPR_ADDR_MASK	(0x0F)    // RW
+
+#define USB_ADDR_TX(n)		(USB_SRAM_BASE + ((n) * 16) + 0x00)
+#define USB_COUNT_TX(n)		(USB_SRAM_BASE + ((n) * 16) + 0x04)
+#define USB_ADDR_RX(n)		(USB_SRAM_BASE + ((n) * 16) + 0x08)
+#define USB_COUNT_RX(n)		(USB_SRAM_BASE + ((n) * 16) + 0x0C)
+
+#define USB_RX_SZ_8		((0 << 15) | (4 << 10))
+#define USB_RX_SZ_16		((0 << 15) | (8 << 10))
+#define USB_RX_SZ_32		((1 << 15) | (0 << 10))
+#define USB_RX_SZ_64		((1 << 15) | (1 << 10))
+#define USB_RX_SZ_128		((1 << 15) | (3 << 10))
+#define USB_RX_SZ_256		((1 << 15) | (7 << 10))
+
+#define _IRQ(name) i_##name ,
+enum {
+#include "irqs.h"
+};
+#undef _IRQ
+
+void gpio_config(unsigned n, unsigned cfg);
+
+#endif
+
diff --git a/arch/stm32f1xx/include/arch/irqs.h b/arch/stm32f1xx/include/arch/irqs.h
@@ -0,0 +1,63 @@
+/* irqs.h
+ * 
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifdef _IRQ
+	_IRQ(watchdog)
+	_IRQ(pvd)
+	_IRQ(tamper)
+	_IRQ(rtc)
+	_IRQ(flash)
+	_IRQ(rcc)
+	_IRQ(extio0)
+	_IRQ(extio1)
+	_IRQ(extio2)
+	_IRQ(extio3)
+	_IRQ(extio4)
+	_IRQ(dma1_ch1)
+	_IRQ(dma1_ch2)
+	_IRQ(dma1_ch3)
+	_IRQ(dma1_ch4)
+	_IRQ(dma1_ch5)
+	_IRQ(dma1_ch6)
+	_IRQ(dma1_ch7)
+	_IRQ(adc)
+	_IRQ(usb_hp)
+	_IRQ(usb_lp)
+	_IRQ(can1_rx1)
+	_IRQ(can1_sce)
+	_IRQ(extio)
+	_IRQ(tim1_brk)
+	_IRQ(tim1_up)
+	_IRQ(tim1_trg_com)
+	_IRQ(tim1_cc)
+	_IRQ(tim2)
+	_IRQ(tim3)
+	_IRQ(tim4)
+	_IRQ(i2c1_ev)
+	_IRQ(i2c1_er)
+	_IRQ(i2c2_ev)
+	_IRQ(i2c2_er)
+	_IRQ(spi1)
+	_IRQ(spi2)
+	_IRQ(usart1)
+	_IRQ(usart2)
+	_IRQ(usart3)
+	_IRQ(extio_10_15)
+	_IRQ(rtc_alarm)
+	_IRQ(usb_wakeup)
+#endif
+
diff --git a/arch/stm32f1xx/serial.c b/arch/stm32f1xx/serial.c
@@ -0,0 +1,49 @@
+/* serial.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/lib.h>
+#include <fw/io.h>
+
+#include <arch/hardware.h>
+
+static unsigned usart_calc_brr(unsigned pclk, unsigned baud)
+{
+	unsigned idiv, fdiv, tmp;
+	idiv = ((25 * pclk) / (4 * baud));
+	tmp = (idiv / 100) << 4;
+	fdiv = idiv - (100 * (tmp >> 4));
+	tmp |= ((((fdiv * 16) + 50) / 100)) & 0x0F;
+	return tmp;
+}
+
+void serial_init(unsigned sysclk, unsigned baud) {
+	writel(0, USART1_BASE + USART_CR1);
+	writel(0, USART1_BASE + USART_CR2);
+	writel(0, USART1_BASE + USART_CR3);
+	writel(1, USART1_BASE + USART_GTPR); /* divide pclk by 1 */
+	writel(usart_calc_brr(sysclk, baud), USART1_BASE + USART_BRR);
+	writel(USART_CR1_ENABLE | USART_CR1_PARITY | USART_CR1_9BIT |
+		USART_CR1_TX_ENABLE | USART_CR1_RX_ENABLE,
+		USART1_BASE + USART_CR1);
+}
+
+void serial_putc(unsigned c) {
+	while (!(readl(USART1_BASE + USART_SR) & USART_SR_TXE)) ;
+	writel(c, USART1_BASE + USART_DR);
+}
+
diff --git a/arch/stm32f1xx/usb.c b/arch/stm32f1xx/usb.c
@@ -0,0 +1,415 @@
+/* usb.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/lib.h>
+#include <fw/io.h>
+
+#include <arch/hardware.h>
+#include <protocol/usb.h>
+
+void usb_handle_irq(void);
+
+void irq_usb_lp(void) {
+	printx("IRQ USB LP\n");
+	for (;;) ;
+}
+void irq_usb_hp(void) {
+	printx("IRQ USB HP\n");
+	for (;;) ;
+}
+
+static volatile int _usb_online = 0;
+static void *ep1_rx_data;
+static volatile int ep1_rx_status;
+static volatile int ep1_tx_busy;
+
+static unsigned ep0rxb = USB_SRAM_BASE + 0x0040; /* 64 bytes */
+static unsigned ep0txb = USB_SRAM_BASE + 0x00c0; /* 64 bytes */
+static unsigned ep1rxb = USB_SRAM_BASE + 0x0140; /* 64 bytes */
+static unsigned ep1txb = USB_SRAM_BASE + 0x01c0; /* 64 bytes */
+
+#define ADDR2USB(n) (((n) & 0x3FF) >> 1)
+
+void usb_handle_reset(void) {
+	_usb_online = 0;
+	ep1_tx_busy = 0;
+	ep1_rx_status = -ENODEV;
+
+	writel(0, USB_BTABLE);
+	writel(ADDR2USB(ep0txb), USB_ADDR_TX(0));
+	writel(ADDR2USB(ep0rxb), USB_ADDR_RX(0));
+	writel(0, USB_COUNT_TX(0));
+	writel(USB_RX_SZ_64, USB_COUNT_RX(0));
+
+	writel(ADDR2USB(ep1txb), USB_ADDR_TX(1));
+	writel(ADDR2USB(ep1rxb), USB_ADDR_RX(1));
+	writel(0, USB_COUNT_TX(1));
+	writel(USB_RX_SZ_64, USB_COUNT_RX(1));
+
+	writel(0x0 | USB_EPR_TYPE_CONTROL |
+		USB_EPR_RX_NAK | USB_EPR_TX_NAK,
+		USB_EPR(0));
+
+	writel(0x1 | USB_EPR_TYPE_BULK |
+		USB_EPR_RX_NAK | USB_EPR_TX_NAK, 
+		USB_EPR(1));
+
+	writel(0x00 | USB_DADDR_ENABLE, USB_DADDR);
+}
+
+static u8 _dev00[] = {
+	18,		/* size */
+	DSC_DEVICE,
+	0x00, 0x01,	/* version */
+	0xFF,		/* class */
+	0x00,		/* subclass */
+	0x00,		/* protocol */
+	0x40,		/* maxpacket0 */
+	0xd1, 0x18,	/* VID */
+	0x02, 0x65,	/* PID */
+	0x00, 0x01,	/* version */
+	0x00,		/* manufacturer string */
+	0x00,		/* product string */
+	0x00,		/* serialno string */
+	0x01,		/* configurations */
+};
+
+static u8 _cfg00[] = {
+	9,
+	DSC_CONFIG,
+	0x20, 0x00,	/* total length */
+	0x01,		/* ifc count */
+	0x01,		/* configuration value */
+	0x00,		/* configuration string */
+	0x80,		/* attributes */
+	50,		/* mA/2 */
+
+	9,
+	DSC_INTERFACE,
+	0x00,		/* interface number */
+	0x00,		/* alt setting */
+	0x02,		/* ept count */
+	0xFF,		/* class */
+	0x00,		/* subclass */
+	0x00,		/* protocol */
+	0x00,		/* interface string */
+
+	7,
+	DSC_ENDPOINT,
+	0x81,		/* address */
+	0x02,		/* bulk */
+	0x40, 0x00,	/* max packet size */
+	0x00,		/* interval */
+
+	7,
+	DSC_ENDPOINT,
+	0x01,		/* address */
+	0x02,		/* bulk */
+	0x40, 0x00,	/* max packet size */
+	0x00,		/* interval */
+
+};
+
+static struct {
+	u16 id;
+	u16 len;
+	u8 *desc;
+} dtable[] = {
+	{ 0x0100, sizeof(_dev00), _dev00 },
+	{ 0x0200, sizeof(_cfg00), _cfg00 },
+};
+
+unsigned load_desc(unsigned id) {
+	unsigned n, len;
+	for (n = 0; n < (sizeof(dtable)/sizeof(dtable[0])); n++) {
+		if (id == dtable[n].id) {
+			u16 *src = (u16*) dtable[n].desc;
+			u32 *dst = (void*) ep0txb;
+			len = dtable[n].len;
+			n = (len & 1) + (len >> 1);
+			while (n--)
+				*dst++ = *src++;
+			return len;
+		}
+	}
+	printx("? %h\n", id);
+	return 0;
+}
+
+
+/* exclude T and W0C bits */
+#define EPMASK (USB_EPR_TYPE_MASK | USB_EPR_DBL_BUF | USB_EPR_ADDR_MASK)
+
+#define EP0_TX_ACK_ADDR	0 /* sending ACK, then changing address */
+#define EP0_TX_ACK	1 /* sending ACK */
+#define EP0_RX_ACK	2 /* receiving ACK */
+#define EP0_TX		3 /* sending data */
+#define EP0_RX		4 /* receiving data */
+#define EP0_IDLE	5 /* waiting for SETUP */
+
+static void ep0_recv_ack(unsigned n) {
+	writel((n & EPMASK) | USB_EPR_RX_STALL | USB_EPR_STATUS_OUT, USB_EPR(0));
+}
+static void ep0_send_ack(unsigned n) {
+	writel(0, USB_COUNT_TX(0));
+	writel((n & EPMASK) | USB_EPR_TX_STALL, USB_EPR(0));
+}
+
+static u8 ep0state = EP0_IDLE;
+static u8 newaddr;
+
+void usb_handle_ep0_tx(unsigned n) {
+	switch (ep0state) {
+	case EP0_TX_ACK_ADDR:
+		writel(newaddr | USB_DADDR_ENABLE, USB_DADDR);
+	case EP0_TX_ACK:
+		ep0state = EP0_IDLE;
+		writel((n & EPMASK), USB_EPR(0));
+		break;
+	case EP0_TX:
+		ep0state = EP0_RX_ACK;
+		ep0_recv_ack(n);
+		break;
+	}
+}
+
+void usb_handle_ep0_rx(unsigned n) {
+	switch (ep0state) {
+	case EP0_RX_ACK:
+		/* ack txn and make sure STATUS_OUT is cleared */
+		writel(((n & EPMASK) & (~USB_EPR_STATUS_OUT)) |
+			USB_EPR_CTR_TX, USB_EPR(0));
+		ep0state = EP0_IDLE;
+		break;
+	case EP0_RX:
+		;
+	}
+}
+
+void usb_handle_ep0_setup(unsigned n) {
+	u16 req, val, idx, len, x;
+
+	req = readl(ep0rxb + 0x00);
+	val = readl(ep0rxb + 0x04);
+	idx = readl(ep0rxb + 0x08);
+	len = readl(ep0rxb + 0x0C);
+	x = readl(USB_COUNT_RX(0));
+
+	/* release SETUP latch by acking RX */
+	writel((n & EPMASK), USB_EPR(0));
+
+	switch (req) {
+	case GET_DESCRIPTOR:
+		x = load_desc(val);
+		if (x == 0)
+			goto error;
+		if (x > len)
+			x = len;
+		ep0state = EP0_TX;
+		writel(x, USB_COUNT_TX(0));
+		writel((n & EPMASK) | USB_EPR_TX_STALL, USB_EPR(0));
+		return;
+	case SET_ADDRESS:
+		ep0state = EP0_TX_ACK_ADDR;
+		newaddr = val & 0x7F;
+		ep0_send_ack(n);
+		return;
+	case SET_CONFIGURATION:
+		ep0state = EP0_TX_ACK;
+		ep0_send_ack(n);
+		_usb_online = 1; /* TODO: check value */
+		return;	
+	}
+
+	/* unknown request */
+	printx("? %b %b %h %h %h\n", req, req >> 8, val, idx, len);
+
+error:
+	/* error, stall TX */
+	writel((n & EPMASK) | USB_EPR_TX_NAK | USB_EPR_TX_STALL, USB_EPR(0));
+}
+
+void usb_handle_ep0(void) {
+	unsigned n = readl(USB_EPR(0));
+	if (n & USB_EPR_SETUP) {
+		usb_handle_ep0_setup(n);
+	} else if (n & USB_EPR_CTR_TX) {
+		usb_handle_ep0_tx(n);
+	} else if (n & USB_EPR_CTR_RX) {
+		usb_handle_ep0_rx(n);
+	}
+}
+
+void usb_handle_ep1(void) {
+	unsigned n;
+	int len;
+
+	n = readl(USB_EPR(1));
+	if (n & USB_EPR_CTR_RX) {
+		/* first, clear RX CTR */
+		writel((n & EPMASK) | USB_EPR_CTR_TX, USB_EPR(1));
+
+		u32 *src = (void*) ep1rxb;
+		u16 *dst = (void*) ep1_rx_data;
+		len = readl(USB_COUNT_RX(1)) & 0x3FF;
+		ep1_rx_status = len;
+		while (len > 0) {
+			*dst++ = *src++;
+			len -= 2;
+		}
+	}
+	if (n & USB_EPR_CTR_TX) {
+		/* first, clear TX CTR */
+		writel((n & EPMASK) | USB_EPR_CTR_RX, USB_EPR(1));
+		ep1_tx_busy = 0;
+	}
+}
+
+int usb_recv(void *_data, int count) {
+	int r, rx = 0;
+	unsigned n;
+	u8 *data = _data;
+
+	while (!_usb_online)
+		usb_handle_irq();
+
+	while (count > 0) {
+		if (!_usb_online)
+			return -ENODEV;
+
+		ep1_rx_data = data;
+		ep1_rx_status = -EBUSY;
+
+		/* move from NAK to VALID, don't touch any other bits */
+		n = readl(USB_EPR(1)) & EPMASK;
+		writel(n | USB_EPR_CTR_RX | USB_EPR_CTR_TX | USB_EPR_RX_STALL, USB_EPR(1));
+
+		while (ep1_rx_status == -EBUSY)
+			usb_handle_irq();
+
+		r = ep1_rx_status;
+
+		if (r < 0)
+			return r;
+		if (r > count)
+			r = count;
+		data += r;
+		rx += r;
+		count -= r;	
+
+		/* terminate on short packet */
+		if (r != 64)
+			break;
+	}
+
+	return rx;
+}
+
+int usb_xmit(void *data, int len) {
+	int tx = 0;
+	int n;
+	u16 *src = data;
+
+	while (len > 0) {
+		u32 *dst = (void*) ep1txb;
+		int xfer = (len > 64) ? 64 : len;
+
+		if (!_usb_online)
+			return -ENODEV;
+
+		while (ep1_tx_busy)
+			usb_handle_irq();
+
+		writel(xfer, USB_COUNT_TX(1));
+		//printx("%x <- %x (%x)\n",dst, src, xfer);
+		len -= xfer;
+		tx += xfer;
+
+		while (xfer > 0) {
+			*dst++ = *src++;
+			xfer -= 2;
+		}
+
+		/* move from NAK to VALID, don't touch any other bits */
+		n = readl(USB_EPR(1)) & EPMASK;
+		writel(n | USB_EPR_CTR_RX | USB_EPR_CTR_TX | USB_EPR_TX_STALL, USB_EPR(1));
+
+		ep1_tx_busy = 1;
+
+	}
+
+	return tx;
+}
+
+void usb_init(unsigned vid, unsigned pid, const char *mfg_string, const char *prod_string) {
+	unsigned n;
+
+	_dev00[8] = vid;
+	_dev00[9] = vid >> 8;
+	_dev00[10] = pid;
+	_dev00[11] = pid >> 8;
+
+	/* enable GPIOC */
+	writel(readl(RCC_APB2ENR) | RCC_APB2_GPIOC, RCC_APB2ENR);
+
+	/* configure GPIOC-12 */
+	writel(1 << 12, GPIOC_BASE + GPIO_BSR);
+	n = readl(GPIOC_BASE + GPIO_CRH);
+	n = (n & 0xFFF0FFFF) | 0x00050000;
+	writel(n, GPIOC_BASE + GPIO_CRH);
+
+	printx("usb_init()\n");
+
+	/* enable USB clock */
+	writel(readl(RCC_APB1ENR) | RCC_APB1_USB, RCC_APB1ENR);
+
+	/* reset */
+	writel(USB_CR_PDWN | USB_CR_FRES, USB_CR);
+	for (n = 0; n < 100000; n++) asm("nop");
+	writel(~USB_CR_PDWN, USB_CR); /* power up analog block */
+	for (n = 0; n < 100000; n++) asm("nop");
+	writel(0, USB_CR);
+	writel(0, USB_ISR);
+
+	usb_handle_reset();
+
+	/* become active on the bus */
+	writel(1 << 12, GPIOC_BASE + GPIO_BRR);
+}
+
+void usb_handle_irq(void) {
+	unsigned n;
+	for (;;) {
+		n = readl(USB_ISR);
+		if (n & USB_RESETM) {
+			usb_handle_reset();
+			writel(~USB_RESETM, USB_ISR);
+			continue;
+		}
+		if (n & USB_CTRM) {
+			if ((n & 0x0F) == 0)
+				usb_handle_ep0();
+			if ((n & 0x0F) == 1)
+				usb_handle_ep1();
+			writel(~USB_CTRM, USB_ISR);
+			continue;
+		}
+		break;
+	}
+}
+
diff --git a/board/lpc-p1343.c b/board/lpc-p1343.c
@@ -0,0 +1,18 @@
+
+#include <fw/types.h>
+#include <arch/hardware.h>
+
+const u32 gpio_led0 = MKGPIO(3, 0);
+const u32 gpio_led1 = MKGPIO(3, 1);
+const u32 gpio_led2 = MKGPIO(3, 2);
+const u32 gpio_led3 = MKGPIO(3, 3);
+const u32 gpio_led4 = MKGPIO(2, 4);
+const u32 gpio_led5 = MKGPIO(2, 5);
+const u32 gpio_led6 = MKGPIO(2, 6);
+const u32 gpio_led7 = MKGPIO(2, 7);
+
+const u8 board_name[] = "LPC-P1343";
+
+void board_init(void) {
+	core_48mhz_init();
+}
diff --git a/board/m3debug.c b/board/m3debug.c
@@ -0,0 +1,29 @@
+
+#include <fw/types.h>
+#include <fw/lib.h>
+#include <arch/hardware.h>
+
+const u32 gpio_led0 = MKGPIO(0, 2);
+const u32 gpio_led1 = MKGPIO(2, 7);
+const u32 gpio_led2 = MKGPIO(2, 8);
+const u32 gpio_led3 = MKGPIO(2, 1);
+
+const u32 gpio_reset_n = MKGPIO(2, 10);
+
+const u8 board_name[] = "M3DEBUG";
+
+void board_init(void) {
+	core_48mhz_init();
+	gpio_cfg_dir(gpio_led0, GPIO_CFG_OUT);
+	gpio_cfg_dir(gpio_led1, GPIO_CFG_OUT);
+	gpio_cfg_dir(gpio_led2, GPIO_CFG_OUT);
+	gpio_cfg_dir(gpio_led3, GPIO_CFG_OUT);
+}
+
+void board_debug_led(int on) {
+	gpio_wr(gpio_led0, on);
+	gpio_wr(gpio_led1, on);
+	gpio_wr(gpio_led2, on);
+	gpio_wr(gpio_led3, on);
+}
+
diff --git a/board/m3radio1.c b/board/m3radio1.c
@@ -0,0 +1,14 @@
+
+#include <fw/types.h>
+#include <arch/hardware.h>
+
+const u32 gpio_led0 = MKGPIO(2, 4);
+const u32 gpio_led1 = MKGPIO(2, 5);
+const u32 gpio_led2 = MKGPIO(2, 9);
+const u32 gpio_led3 = MKGPIO(2, 7);
+
+const u8 board_name[] = "M3RADIO1";
+
+void board_init(void) {
+	core_48mhz_init();
+}
diff --git a/board/m3radio2.c b/board/m3radio2.c
@@ -0,0 +1,14 @@
+
+#include <fw/types.h>
+#include <arch/hardware.h>
+
+const u32 gpio_led0 = MKGPIO(3, 3);
+const u32 gpio_led1 = MKGPIO(2, 8);
+const u32 gpio_led2 = MKGPIO(0, 5);
+const u32 gpio_led3 = MKGPIO(1, 9);
+
+const u8 board_name[] = "M3RADIO2";
+
+void board_init(void) {
+	core_48mhz_init();
+}
diff --git a/build/build.mk b/build/build.mk
@@ -0,0 +1,57 @@
+## Copyright 2014 Brian Swetland <swetland@frotz.net>
+## 
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+# configuration header generation heavily inspired by travisg's lk build system
+
+# $(call chip,name,arch,rambase,ramsize,rombase,romsize,linkscript)
+define chip
+$(eval CHIP_$1_ARCH := $2) \
+$(eval CHIP_$1_RAMBASE := $3) \
+$(eval CHIP_$1_RAMSIZE := $4) \
+$(eval CHIP_$1_ROMBASE := $5) \
+$(eval CHIP_$1_ROMSIZE := $6) \
+$(eval CHIP_$1_LINKSCRIPT := build/generic-$7.ld) \
+$(eval CHIP_$1_DEPS := $(lastword $(MAKEFILE_LIST)))
+endef
+
+MKDIR = if [ ! -d $(dir $@) ]; then mkdir -p $(dir $@); fi
+
+QUIET ?= @
+
+SPACE :=
+SPACE +=
+COMMA := ,
+
+define underscorify
+$(subst /,_,$(subst \,_,$(subst .,_,$(subst -,_,$1))))
+endef
+
+define toupper
+$(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,$(subst k,K,$(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,$(subst u,U,$(subst v,V,$(subst w,W,$(subst x,X,$(subst y,Y,$(subst z,Z,$1))))))))))))))))))))))))))
+endef
+
+# (call make-config-header,outfile,configlist)
+define make-config-header
+echo "/* Machine Generated File - Do Not Edit */" >> $1.tmp ; \
+echo "#ifndef __$(call underscorify,$1)" >> $1.tmp ; \
+echo "#define __$(call underscorify,$1)" >> $1.tmp ; \
+$(foreach def,$2,echo "#define CONFIG_$(subst =, ,$(call underscorify,$(call toupper,$(def))))" >> $1.tmp ;) \
+echo "#endif" >> $1.tmp ; \
+mv $1.tmp $1
+endef
+
+start-module-mk = $(eval M_MAKEFILE := $(lastword $(MAKEFILE_LIST)))
+build-target-executable = $(eval include build/target-executable.mk)
+build-host-executable = $(eval include build/host-executable.mk)
+
diff --git a/build/generic-ram.ld b/build/generic-ram.ld
@@ -0,0 +1,32 @@
+
+/* RAM only binary layout */
+
+SECTIONS {
+	.text : {
+		. = ALIGN(4);
+		KEEP (*(.vectors))
+		*(.text)
+		*(.text.*)
+		*(.rodata)
+		*(.rodata.*)
+		. = ALIGN(4);
+		__data_init__ = . ;
+	} >RAM
+	.data : {
+		. = ALIGN(4);
+		__data_start__ = . ;
+		*(.data)
+		*(.data.*)
+		. = ALIGN(4);
+		__data_end__ = . ;
+	} >RAM
+	.bss : {
+		. = ALIGN(4);
+		__bss_start__ = . ;
+		*(.bss)
+		*(.bss.*)
+		*(COMMON)
+		. = ALIGN(4);
+		__bss_end__ = . ;
+	} >RAM
+}
diff --git a/build/generic-rom.ld b/build/generic-rom.ld
@@ -0,0 +1,33 @@
+
+/* ROM plus RAM binary layout */
+
+SECTIONS {
+	.text : {
+		. = ALIGN(4);
+		KEEP (*(.vectors))
+		*(.text)
+		*(.text.*)
+		*(.rodata)
+		*(.rodata.*)
+		. = ALIGN(4);
+		__data_init__ = . ;
+	} >FLASH
+	.data : AT(__data_init__) {
+		. = ALIGN(4);
+		__data_start__ = . ;
+		. = ALIGN(4);
+		*(.data)
+		*(.data.*)
+		. = ALIGN(4);
+		__data_end__ = . ;
+	} >RAM
+	.bss : {
+		. = ALIGN(4);
+		__bss_start__ = . ;
+		*(.bss)
+		*(.bss.*)
+		*(COMMON)
+		. = ALIGN(4);
+		__bss_end__ = . ;
+	} >RAM
+}
diff --git a/build/host-executable.mk b/build/host-executable.mk
@@ -0,0 +1,43 @@
+## Copyright 2011 Brian Swetland <swetland@frotz.net>
+## 
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+M_NAME := $(strip $(M_NAME))
+
+# sanity check
+ifeq "$(M_NAME)" ""
+$(error No module name specified)
+endif
+
+M_OBJS := $(addprefix $(OUT_HOST_OBJ)/$(M_NAME)/,$(M_OBJS))
+DEPS += $(M_OBJS:%o=%d)
+
+ALL += $(BIN)/$(M_NAME)
+
+$(OUT_HOST_OBJ)/$(M_NAME)/%.o: %.c
+	@$(MKDIR)
+	@echo compile $<
+	$(QUIET)$(CC) $(HOST_CFLAGS) -c $< -o $@ -MD -MT $@ -MF $(@:%o=%d)
+
+$(BIN)/$(M_NAME): _OBJS := $(M_OBJS)
+$(BIN)/$(M_NAME): _LIBS := $(M_LIBS) $(HOST_LIBS)
+$(BIN)/$(M_NAME): $(M_OBJS)
+	@$(MKDIR)
+	@echo link $@
+	$(QUIET)gcc $(HOST_CFLAGS) -o $@ $(_OBJS) $(_LIBS)
+
+$(info module $(M_NAME))
+
+M_LIBS :=
+M_OBJS :=
+M_NAME :=
diff --git a/build/target-executable.mk b/build/target-executable.mk
@@ -0,0 +1,119 @@
+## Copyright 2011 Brian Swetland <swetland@frotz.net>
+## 
+## Licensed under the Apache License, Version 2.0 (the "License");
+## you may not use this file except in compliance with the License.
+## You may obtain a copy of the License at
+##
+##     http://www.apache.org/licenses/LICENSE-2.0
+##
+## Unless required by applicable law or agreed to in writing, software
+## distributed under the License is distributed on an "AS IS" BASIS,
+## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+## See the License for the specific language governing permissions and
+## limitations under the License.
+
+M_NAME := $(strip $(M_NAME))
+M_CHIP := $(strip $(M_CHIP))
+
+M_ARCH := $(CHIP_$(M_CHIP)_ARCH)
+M_ARCH_CFLAGS := $(ARCH_$(M_ARCH)_CFLAGS)
+M_ARCH_OBJS := $(ARCH_$(M_ARCH)_OBJS)
+
+# sanity check
+ifeq "$(M_NAME)" ""
+$(error $(M_MAKEFILE): No module name specified)
+endif
+ifeq "$(M_ARCH)" ""
+$(error $(M_MAKEFILE): Module $(M_NAME): Unknown Chip: $(M_CHIP))
+endif
+ifeq "$(M_ARCH_CFLAGS)" ""
+$(error $(M_MAKEFILE): Module $(M_NAME): Unknown Architecture: $(M_ARCH))
+endif
+
+# architecture start glue goes first
+M_OBJS := $(ARCH_$(M_ARCH)_START) $(M_OBJS)
+M_OBJS := $(addprefix $(OUT_TARGET_OBJ)/$(M_NAME)/,$(M_OBJS))
+M_ARCH_OBJS := $(addprefix $(OUT_TARGET_OBJ)/$(M_NAME)/,$(M_ARCH_OBJS))
+
+DEPS += $(M_OBJS:%o=%d)
+
+M_OUT_BIN := $(OUT)/$(M_NAME).bin
+M_OUT_LST := $(OUT)/$(M_NAME).lst
+M_OUT_ELF := $(OUT)/$(M_NAME).elf
+
+ALL += $(M_OUT_BIN) $(M_OUT_LST) $(M_OUT_ELF)
+
+M_INCLUDE := $(OUT_TARGET_OBJ)/$(M_NAME)/include
+M_CONFIG_H := $(M_INCLUDE)/config.h
+M_ARCH_LIB := $(OUT_TARGET_OBJ)/$(M_NAME)/arch.a
+M_LINK_SCRIPT := $(OUT_TARGET_OBJ)/$(M_NAME)/script.ld
+
+# generate link script 
+$(M_LINK_SCRIPT): _RADDR := $(CHIP_$(M_CHIP)_RAMBASE)
+$(M_LINK_SCRIPT): _RSIZE := $(CHIP_$(M_CHIP)_RAMSIZE)
+$(M_LINK_SCRIPT): _FADDR := $(CHIP_$(M_CHIP)_ROMBASE)
+$(M_LINK_SCRIPT): _FSIZE := $(CHIP_$(M_CHIP)_ROMSIZE)
+$(M_LINK_SCRIPT): _SCRIPT := $(CHIP_$(M_CHIP)_LINKSCRIPT)
+$(M_LINK_SCRIPT): $(CHIP_$(M_CHIP)_DEPS)
+	@echo linkscript $@
+	@echo "MEMORY {" > $@
+	@echo " RAM (xrw) : ORIGIN = $(_RADDR), LENGTH = $(_RSIZE)" >> $@
+	@echo " FLASH (xr) : ORIGIN = $(_FADDR), LENGTH = $(_FSIZE)" >> $@
+	@echo "}" >> $@
+	@echo " INCLUDE \"$(_SCRIPT)\"" >> $@
+
+$(M_ARCH_LIB): $(M_ARCH_OBJS)
+	@$(MKDIR)
+	@echo archive $@
+	$(QUIET)rm -f $@
+	$(QUIET)$(TARGET_AR) cr $@ $^
+
+$(OUT_TARGET_OBJ)/$(M_NAME)/%.o: %.c
+	@$(MKDIR)
+	@echo compile $<
+	$(QUIET)$(TARGET_CC) $(TARGET_CFLAGS) $(_CFLAGS) -c $< -o $@ -MD -MT $@ -MF $(@:%o=%d)
+
+$(OUT_TARGET_OBJ)/$(M_NAME)/%.o: %.S
+	@$(MKDIR)
+	@echo assemble $<
+	$(QUIET)$(TARGET_CC) $(TARGET_CFLAGS) $(_CFLAGS) -c $< -o $@ -MD -MT $@ -MF $(@:%o=%d)
+
+# apply our flags to our objects
+$(M_OBJS): _CFLAGS := --include $(M_CONFIG_H) $(M_ARCH_CFLAGS) $(M_CFLAGS)
+$(M_ARCH_OBJS): _CFLAGS := --include $(M_CONFIG_H) $(M_ARCH_CFLAGS) $(M_CFLAGS)
+
+# objects depend on generated config header
+$(M_OBJS): $(M_CONFIG_H)
+$(M_ARCH_OBJS): $(M_CONFIG_H)
+
+# generate config header from module, chip, and arch config lists
+# generated config header depends on toplevel, module, and chip/arch makefiles
+$(M_CONFIG_H): _CFG := $(M_CONFIG) $(CHIP_$(M_CHIP)_CONFIG) $(ARCH_$(M_ARCH)_CONFIG)
+$(M_CONFIG_H): Makefile $(M_MAKEFILE) $(CHIP_$(M_CHIP)_DEPS)
+	@$(MKDIR)
+	@echo generate $@
+	@$(call make-config-header,$@,$(_CFG))
+
+$(M_OUT_BIN): $(M_OUT_ELF)
+	@echo create $@
+	$(QUIET)$(TARGET_OBJCOPY) --gap-fill=0xee -O binary $< $@
+
+$(M_OUT_LST): $(M_OUT_ELF)
+	@echo create $@
+	$(QUIET)$(TARGET_OBJDUMP) --source -d $< > $@
+
+$(M_OUT_ELF): _OBJS := $(M_OBJS)
+$(M_OUT_ELF): _LIBS := $(M_LIBS) $(M_ARCH_LIB)
+$(M_OUT_ELF): _LINK := $(M_LINK_SCRIPT)
+$(M_OUT_ELF): $(M_OBJS) $(M_ARCH_LIB) $(M_LINK_SCRIPT)
+	@echo link $@
+	$(QUIET)$(TARGET_LD) $(TARGET_LFLAGS) -Bstatic -T $(_LINK) $(_OBJS) $(_LIBS) -o $@
+
+$(info module $(M_NAME))
+
+M_OBJS :=
+M_NAME :=
+M_BASE :=
+M_LIBS :=
+M_CFLAGS :=
+M_CONFIG :=
diff --git a/docs/building-cross-compiler.txt b/docs/building-cross-compiler.txt
@@ -0,0 +1,64 @@
+
+New Instructions
+----------------
+
+Use http://github.com/travisg/toolchains
+
+1. % git clone https://github.com/travisg/toolchains.git
+2. % cd toolchains
+3. % ARCHES=arm ./doit
+4. wait while it downloads and builds the toolchain
+5. arrange for toolchains/arm-eabi-4.8.2-Linux-x86_64/bin to be in your
+   path or use local.mk to override the TOOLCHAIN build variable
+
+
+
+Old Instructions
+----------------
+
+This is a transcript of how I built the toolchain, not really intended 
+to be run as a script.
+
+TODO: see if it's smaller/faster/etc to build only for cortex-m3
+
+Inspired by http://www.hermann-uwe.de/blog/building-an-arm-cross-toolchain-with-binutils-gcc-newlib-and-gdb-from-source and the gcc online manuals.
+
+
+# install packages needed on ubuntu
+sudo apt-get install libgmp3-dev libmpc-dev libgmp3-dev zlib1g-dev build-essential libncurses5-dev texinfo
+
+# setup workspace
+mkdir /work/tools
+cd /work/tools
+
+# grab sources
+wget http://mirrors.kernel.org/gnu/binutils/binutils-2.22.tar.bz2
+wget http://mirrors.kernel.org/gnu/gcc/gcc-4.6.2/gcc-4.6.2.tar.bz2
+wget http://mirrors.kernel.org/gnu/gdb/gdb-7.3.1.tar.bz2
+tar -xjvf binutils-2.22.tar.bz2
+tar -xjvf gcc-4.6.2.tar.bz2
+tar -xjvf gdb-7.3.1.tar.bz2
+
+mkdir build 
+
+cd build
+../binutils-2.22/configure --target=arm-none-eabi --prefix=/work/tools/arm --enable-interwork --enable-multilib --with-gnu-as --with-gnu-ld --disable-nls
+make
+make install
+cd ..
+rm -rf build/*
+
+cd build
+../gcc-4.6.2/configure --target=arm-none-eabi --prefix=/work/tools/arm --enable-interwork --enable-multilib --enable-languages="c" --disable-nls --disable-shared --disable-threads --without-headers --with-gnu-as --with-gnu-ld  --with-system-zlib --disable-libssp --disable-libmudflap --disable-libgomp
+make
+make install
+cd ..
+rm -rf build/*
+
+cd build
+../gdb-7.3.1/configure --target=arm-none-eabi --prefix=/work/tools/arm --enable-interwork --enable-multilib
+make
+make install
+cd ..
+rm -rf build/*
+
diff --git a/docs/memory-barriers.txt b/docs/memory-barriers.txt
@@ -0,0 +1,185 @@
+
+http://infocenter.arm.com/help/topic/com.arm.doc.genc007826/Barrier_Litmus_Tests_and_Cookbook_A08.pdf
+
+-----
+
+http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.faqs/ka14041.html
+states:
+
+It is architecturally defined that software must perform a Data Memory 
+Barrier (DMB) operation:
+- between acquiring a resource, for example, through locking a mutex 
+  (MUTual EXclusion) or decrementing a semaphore, and making any access 
+  to that resource
+- before making a resource available, for example, through unlocking a 
+  mutex or incrementing a semaphore.
+
+-----
+
+To generate a scheduling barrier:
+   asm volatile ("" : : : "memory");
+
+To generate a hardware memory barrier:
+   __sync_synchronize();
+
+   This appears to issue a "dmb sy" on cortex-m3 in arm-eabi-none
+
+
+Based on these excerpts from a gcc-help thread.  References:
+http://gcc.gnu.org/ml/gcc-help/2011-04/msg00166.html
+http://gcc.gnu.org/ml/gcc-help/2011-04/msg00168.html
+http://gcc.gnu.org/ml/gcc-help/2011-04/msg00180.html
+
+From: Ian Lance Taylor <iant at google dot com>
+Date: Mon, 11 Apr 2011 14:42:07 -0700
+Subject: Re: full memory barrier?
+
+Hei Chan <structurechart at yahoo dot com> writes:
+
+> I am a little bit confused what asm volatile ("" : : : "memory") does.
+>
+> I searched online; many people said that it creates the "full memory barrier".
+>
+> I have a test code:
+> int main() {
+>         bool bar;
+>         asm volatile ("" : : : "memory");
+>         bar = true;
+>         return 1;
+> }
+>
+> Running g++ -c -g -Wa,-a,-ad foo.cpp gives me:
+>
+>    2:foo.cpp       ****         bool bar;
+>    3:foo.cpp       ****         asm volatile ("" : : : "memory");
+>   22                            .loc 1 3 0
+>    4:foo.cpp       ****         bar = true;
+>   23                            .loc 1 4 0
+>
+> It doesn't involve any fence instruction.
+>
+> Maybe I completely misunderstand the idea of "full memory barrier".
+
+The definition of "memory barrier" is ambiguous when looking at code
+written in a high-level language.
+
+The statement "asm volatile ("" : : : "memory");" is a compiler
+scheduling barrier for all expressions that load from or store values to
+memory.  That means something like a pointer dereference, an array
+index, or an access to a volatile variable.  It may or may not include a
+reference to a local variable, as a local variable need not be in
+memory.
+
+This kind of compiler scheduling barrier can be used in conjunction with
+a hardware memory barrier.  The compiler doesn't know that a hardware
+memory barrier is special, and it will happily move memory access
+instructions across the hardware barrier.  Therefore, if you want to use
+a hardware memory barrier in compiled code, you must use it along with a
+compiler scheduling barrier.
+
+On the other hand a compiler scheduling barrier can be useful even
+without a hardware memory barrier.  For example, in a coroutine based
+system with multiple light-weight threads running on a single processor,
+you need a compiler scheduling barrier, but you do not need a hardware
+memory barrier.
+
+gcc will generate a hardware memory barrier if you use the
+__sync_synchronize builtin function.  That function acts as both a
+hardware memory barrier and a compiler scheduling barrier.
+
+Ian
+
+-----
+
+From: Ian Lance Taylor <iant at google dot com>
+Date: Mon, 11 Apr 2011 15:20:27 -0700
+Subject: Re: full memory barrier?
+
+Hei Chan <structurechart at yahoo dot com> writes:
+
+> You mentioned the statement "is a compiler scheduling barrier for all 
+> expressions that load from or store values to memory".  Does "memory" mean the 
+> main memory?  Or does it include the CPU cache?
+
+I tried to explain what I meant by way of example.  It means pointer
+reference, array reference, volatile variable access.  Also I should
+have added global variable access.  In general it means memory from the
+point of view of the compiler.  The compiler doesn't know anything about
+the CPU cache.  When thinking about a "compiler scheduling barrier," you
+have to think about the world that the compiler sees, which is quite
+different from, though obviously related to, the world that the hardware
+sees.
+
+Ian
+
+-----
+
+From: Ian Lance Taylor <iant at google dot com>
+Date: Tue, 12 Apr 2011 15:36:58 -0700
+Subject: Re: full memory barrier?
+
+David Brown <david at westcontrol dot com> writes:
+
+> On 11/04/2011 23:42, Ian Lance Taylor wrote:
+>>
+>> The definition of "memory barrier" is ambiguous when looking at code
+>> written in a high-level language.
+>>
+>> The statement "asm volatile ("" : : : "memory");" is a compiler
+>> scheduling barrier for all expressions that load from or store values to
+>> memory.  That means something like a pointer dereference, an array
+>> index, or an access to a volatile variable.  It may or may not include a
+>> reference to a local variable, as a local variable need not be in
+>> memory.
+>>
+>
+> Is there any precise specifications for what counts as "memory" here?
+> As gcc gets steadily smarter, it gets harder to be sure that
+> order-specific code really is correctly ordered, while letting the
+> compiler do it's magic on the rest of the code.
+
+I'm not aware of a precise specification.  It would be something like
+the list I made above, to which I would add global variables.  But
+you're right, as the compiler gets smarter, it is increasingly able to
+lift things out of memory.  I suppose that in the extreme case, it's
+possible that only volatile variables count.
+
+
+> For example, if you have code like this:
+>
+> static int x;
+> void test(void) {
+> 	x = 1;
+> 	asm volatile ("" : : : "memory");
+> 	x = 2;
+> }
+>
+> The variable "x" is not volatile - can the compiler remove the
+> assignment "x = 1"?  Perhaps with aggressive optimisation, the
+> compiler will figure out how and when x is used, and discover that it
+> doesn't need to store it in memory at all, but can keep it in a
+> register (perhaps all uses have ended up inlined inside the same
+> function).  Then "x" is no longer in memory - will it still be
+> affected by the memory clobber?
+
+If the compiler manages to lift x into a register, then it will not be
+affected by the memory clobber, and, yes, the compiler would most likely
+remove the assignment "x = 1".
+
+
+> Also, is there any way to specify a more limited clobber than just
+> "memory", so that the compiler has as much freedom as possible?
+> Typical examples are to specify "clobbers" for just certain variables,
+> leaving others unaffected, or to distinguish between reads and writes.
+> For example, you might want to say "all writes should be completed by
+> this point, but data read into registers will stay valid".
+>
+> Some of this can be done with volatile accesses in different ways, but
+> not always optimally, and not always clearly.
+
+You can clobber certain variables by listing them in the output of the
+asm statement.  There is no way to distinguish between reads and writes.
+
+Ian
+
+
diff --git a/docs/notes.txt b/docs/notes.txt
@@ -0,0 +1,50 @@
+Cortex M3
+---------
+- All M3 vectors are interwork style and MUST have the low bit set.
+  Failure to do so will result in hardfaults on reset, etc.
+
+- You can start debugging from reset by asserting RESET, writing 1 to 
+  E000EDFC, then releasing RESET
+
+Linux Generic Serial Driver
+---------------------------
+- class/subclass/protocol 0xff,0x00,0x00 (device and interface)
+- 1 interface, 1 bulk in, 1 bulk out
+- must modprobe usbserial vendor=0x0525 product=0xA4A6
+- must rmmod usbserial before modprobing it if it was previously loaded
+- publishes as /dev/ttyUSBx
+
+STM32F1xx
+---------
+- clock config at boot:
+  - HSI (internal 8MHz osc) is on and ready
+  - SYSCLK = HSI (8MHz)
+  - AHB = SYSCLK / 1
+  - PCLK1 = HCLK / 1
+  - PCLK2 = HCLK / 1
+  - ADCCLK = PCLK2 / 2 (4MHz)
+
+LPC1343
+-------
+- how to trap just before the bootloader jumps to user flash
+  reset-stop
+  watch-rw 0
+  go
+
+gdb
+---
+making the disassembler behave:
+  set architecture armv4m
+  set arm fallback-mode thumb
+
+connecting/disconnecting:
+  target remote | out/gdb-bridge
+  disconnect
+
+turn on tracing for the remote wire protocol
+  set debug remote 1
+
+disassemble 4 instructions at pc
+  x/4i $pc
+
+
diff --git a/docs/todo.txt b/docs/todo.txt
@@ -0,0 +1,33 @@
+
+- debugger
+  - more script work (integration of erase/flash scripts, etc)
+  - flash support for stm32 parts
+  - async/background usb io (to allow for serial trace, ITM trace, etc)
+  - quieter script execution mode
+
+- m3debug
+  - do some more useful stuff with LEDs
+  - ITM support
+  - serial debug support (feed serial down through usb)
+
+- swdp
+  - fold things together with m3debug
+
+- lpc13boot
+  - reset to a more like-POR state before jumping to app
+  - provide a 'R'eboot protocol command
+  - ability to reflash the bootloader (maybe w/ some sanity check?)
+
+- lpc13xx
+  - clean up library code, more #defines, fewer magic numbers
+  - real baud selection code in serial_init()
+
+- stm32f
+  - usb feature parity
+
+- build system
+  - easy way to build app variants for multiple boards
+    (m3debug for m3debug, maple, etc)
+
+- arm m3
+  - get interrupts, threads, etc going
diff --git a/docs/usb-protocols.txt b/docs/usb-protocols.txt
@@ -0,0 +1,51 @@
+
+18d1:db00 - lpcboot / m3boot
+----------------------------
+- provides a pair of USB bulk endpoints, IN and OUT
+- write a three word (32b-LE) command sequence ( MAGIC, CMD, ARG)
+  - MAGIC is 0xDB00A5A5
+  - see below for CMD
+  - ARG should be zero if not specified
+- read a two word (32b-LE) response ( MAGIC, STATUS)
+  - MAGIC is 0xDB00A5A5
+  - nonzero STATUS is failure
+- read or write payload, as required
+
+- commands
+  - write to flash: CMD='W', ARG=size
+  - write to RAM (and execute): CMD='X', ARG=size
+  - erase flash: CMD='E'
+  - reboot: CMD='R'
+  - start code in flash: CMD='A'
+  - query device info: CMD='Q'
+
+struct device_info {
+        char part[16];		/* "LPC1343", "STM32F103", etc */
+        char board[16];		/* "M3DEBUG", "TESTBOARD123", etc */
+        uint32_t version;	/* protocol version: 0x0001000 -> V1.0 */
+        uint32_t ram_base;	/* start of SRAM */
+        uint32_t ram_size;	/* length of SRAM (minus 1K bootloader workspace) */
+        uint32_t rom_base;	/* start of FLASH (after 4K bootloader) */
+        uint32_t rom_size;	/* length of FLASH (minus 4K bootloader)
+        uint32_t ununsed0;	
+        uint32_t ununsed1;
+        uint32_t ununsed2;
+};
+
+
+18d1:db03 - remote swdp
+-----------------------
+- provides a pair of USB bulk endpoints, IN and OUT
+- see include/protocol/rswdp.h for details
+
+18d1:db05 - usb console
+-----------------------
+- provides a pair of USB bulk endpoints, IN and OUT
+- console output (device to host) provided via BULK IN
+- console input (host to device) provided via BULK OUT (format TBD)
+
+18d1:db04 - remote swdp + console
+---------------------------------
+- interface 0 provides remote swdp
+- interface 1 provides usb console
+
diff --git a/include/fw/io.h b/include/fw/io.h
@@ -0,0 +1,15 @@
+/* io.h */
+
+#ifndef _IO_H_
+#define _IO_H_
+
+#define readb(a) (*((volatile unsigned char *) (a)))
+#define writeb(v, a) (*((volatile unsigned char *) (a)) = (v))
+
+#define readw(a) (*((volatile unsigned short *) (a)))
+#define writew(v, a) (*((volatile unsigned short *) (a)) = (v))
+
+#define readl(a) (*((volatile unsigned int *) (a)))
+#define writel(v, a) (*((volatile unsigned int *) (a)) = (v))
+
+#endif
diff --git a/include/fw/lib.h b/include/fw/lib.h
@@ -0,0 +1,133 @@
+/* lib.h
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _FW_LIB_H_
+#define _FW_LIB_H_
+
+#include <fw/io.h>
+#include <fw/types.h>
+
+#define EFAIL		1
+#define EBUSY		2
+#define ENODEV		3
+#define ETIMEOUT	4
+
+#include <stdarg.h>
+
+void board_init(void);
+void board_debug_led(int on);
+void reboot(void);
+
+void vprintx(void (*_putchar_)(unsigned), const char *fmt, va_list ap);
+void printx(const char *fmt, ...);
+
+void serial_init(unsigned sysclk_mhz, unsigned baud);
+void serial_putc(unsigned c);
+
+#define GPIO_CFG_IN		0x0001
+#define GPIO_CFG_OUT		0x0002
+
+#define GPIO_CFG_POSITIVE	0x0001
+#define GPIO_CFG_NEGATIVE	0x0002
+#define GPIO_CFG_BOTH		0x0003
+#define GPIO_CFG_LEVEL		0x0000
+#define GPIO_CFG_EDGE		0x0004
+
+void gpio_cfg_dir(unsigned n, unsigned dir);
+void gpio_cfg_irq(unsigned n, unsigned cfg);
+void gpio_set(unsigned n);
+void gpio_clr(unsigned n);
+int gpio_rd(unsigned n);
+void gpio_wr(unsigned n, unsigned v);
+int gpio_irq_check(unsigned n);
+void gpio_irq_clear(unsigned n);
+
+/* init USB and attach */
+void usb_init(unsigned vid, unsigned pid, const char *mfg_string, const char *prod_string);
+
+/* detach from USB */
+void usb_stop(void);
+
+/* read up to len bytes on bulk in
+ * - stops on a short packet
+ * - returns bytes read
+ * - returns -ENODEV if USB went offline
+ */
+int usb_recv(void *data, int len);
+
+/* send len bytes on bulk out
+ * - returns bytes written
+ * - returns -ENODEV if USB went offline
+ */
+int usb_xmit(void *data, int len);
+
+/* same as usb_recv but returns -ETIMEOUT
+ * if msec milliseconds pass. 
+ * wait forever if msec == 0
+ */
+int usb_recv_timeout(void *data, int len, unsigned msec);
+
+/* check for host, return nonzero if we found it */
+int usb_online(void);
+
+
+/* low level interrupt-based USB interface */
+
+extern void (*usb_ep1_rx_full_cb)(void);
+extern void (*usb_ep1_tx_empty_cb)(void);
+extern void (*usb_ep2_rx_full_cb)(void);
+extern void (*usb_ep2_tx_empty_cb)(void);
+extern void (*usb_online_cb)(int online);
+
+int usb_ep1_read(void *data, int max);
+int usb_ep1_write(void *data, int len);
+int usb_ep2_read(void *data, int max);
+int usb_ep2_write(void *data, int len);
+
+void usb_mask_ep1_rx_full(void);
+void usb_unmask_ep1_rx_full(void);
+void usb_mask_ep1_tx_empty(void);
+void usb_unmask_ep1_tx_empty(void);
+void usb_mask_ep2_rx_full(void);
+void usb_unmask_ep2_rx_full(void);
+void usb_mask_ep2_tx_empty(void);
+void usb_unmask_ep2_tx_empty(void);
+
+extern void (*usb_console_rx_cb)(u8 *buf, int len);
+
+void usb_console_init(void);
+
+#define ARRAY_SIZE(a)  (sizeof((a))/sizeof(*(a)))
+
+#define max(a,b)  ((a) > (b) ? (a) : (b))
+#define min(a,b)  ((a) < (b) ? (a) : (b))
+
+static inline unsigned clr_set_bits(unsigned val, unsigned clr, unsigned set)
+{
+	val &= ~clr;
+	val |= set;
+	return val;
+}
+
+static inline void clr_set_reg(unsigned addr, unsigned clr, unsigned set)
+{
+	unsigned val = clr_set_bits(readl(addr), clr, set);
+	writel(val, addr);
+}
+
+#endif
+
diff --git a/include/fw/string.h b/include/fw/string.h
@@ -0,0 +1,4 @@
+
+void *memcpy(void *s1, const void *s2, int n);
+void *memset(void *b, int c, int len);
+void strcpy(char *d, char *s);
diff --git a/include/fw/types.h b/include/fw/types.h
@@ -0,0 +1,13 @@
+/* types.h */
+
+#ifndef _FW_TYPES_H_
+#define _FW_TYPES_H_
+
+typedef unsigned int u32;
+typedef signed int i32;
+typedef unsigned short u16;
+typedef signed short i16;
+typedef unsigned char u8;
+typedef signed char i8;
+
+#endif
diff --git a/include/protocol/rswdp.h b/include/protocol/rswdp.h
@@ -0,0 +1,129 @@
+/* rswdp.h - remote serial wire debug protocol
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* Remote Serial Wire Debug Protocol */
+
+#ifndef _RSWDP_PROTOCOL_H_
+#define _RSWDP_PROTOCOL_H_
+
+/* Basic framing:
+ * - host and device exchange "transactions" consisting of
+ *   some number of "messages".
+ * - each "message" has a 32bit header and may have 0 or more 
+ *   32bit words of payload 
+ * - a transaction may not exceed 4K (1024 words)
+ * - a transaction is sent in a series of USB BULK packets
+ * - the final packet must be a short packet unless the 
+ *   transaction is exactly 4K in length
+ * - packets must be a multiple of 4 bytes
+ * - the first message in a transaction must be 
+ *   CMD_TXN_START or CMD_TXN_ASYNC
+ */
+
+#define RSWD_MSG(cmd,op,n)	((((cmd)&0xFF) << 24) | (((op) & 0xFF)<<16) | ((n) & 0xFFFF))
+#define RSWD_MSG_CMD(n)		(((n) >> 24) & 0xFF)
+#define RSWD_MSG_OP(n)		(((n) >> 16) & 0xFF)
+#define RSWD_MSG_ARG(n)		((n) & 0xFFFF)
+
+#define RSWD_TXN_START(seq)	(0xAA770000 | ((seq) & 0xFFFF))
+#define RSWD_TXN_ASYNC		(0xAA001111)
+
+/* valid: either */
+#define CMD_NULL	0x00 /* used for padding */
+
+/* valid: host to target */
+#define CMD_SWD_WRITE	0x01 /* op=addr arg=count payload: data x count */
+#define CMD_SWD_READ	0x02 /* op=addr arg=count payload: data x count */
+#define CMD_SWD_DISCARD	0x03 /* op=addr arg=count payload: none (discards) */
+#define CMD_ATTACH	0x04 /* do swdp reset/connect handshake */
+#define CMD_RESET	0x05 /* arg=1 -> assert RESETn, otherwise deassert */ 
+#define CMD_DOWNLOAD	0x06 /* arg=wordcount, payload: addr x 1, data x n */
+#define CMD_EXECUTE	0x07 /* payload: addr x 1 */
+#define CMD_TRACE	0x08 /* op=tracebits n=0 */
+#define CMD_BOOTLOADER	0x09 /* return to bootloader for reflashing */
+#define CMD_SET_CLOCK	0x0A /* set SWCLK rate to n khz */
+
+/* valid: target to host */
+#define CMD_STATUS	0x10 /* op=errorcode, arg=commands since last TXN_START */
+#define CMD_SWD_DATA	0x11 /* op=0 arg=count, payload: data x count */
+
+/* valid: target to host async */
+#define CMD_DEBUG_PRINT	0x20 /* arg*4 bytes of ascii debug output */
+
+/* CMD_SWD_OP operations - combine for direct AP/DP io */
+#define OP_RD 0x00
+#define OP_WR 0x01
+#define OP_DP 0x00
+#define OP_AP 0x02
+#define OP_X0 0x00
+#define OP_X4 0x04
+#define OP_X8 0x08
+#define OP_XC 0x0C
+
+/* DP registers */
+#define DP_IDCODE	(OP_DP|OP_X0)
+#define DP_ABORT	(OP_DP|OP_X0)
+#define DP_DPCTRL	(OP_DP|OP_X4)
+#define DP_RESEND	(OP_DP|OP_X8)
+#define DP_SELECT	(OP_DP|OP_X8)
+#define DP_BUFFER	(OP_DP|OP_XC)
+
+/* AHB AP registers */
+#define AHB_CSW		0x00
+#define AHB_TAR		0x04 
+#define AHB_DRW		0x0C
+#define AHB_BD0		0x10
+#define AHB_BD1		0x14
+#define AHB_BD2		0x18
+#define AHB_BD3		0x20
+#define AHB_ROM_ADDR	0xF8
+#define AHB_IDR		0xFC
+
+#define AHB_CSW_MCORE		(0 << 29)
+#define AHB_CSW_MDEBUG		(1 << 29)
+#define AHB_CSW_USER		(0 << 25)
+#define AHB_CSW_PRIV		(1 << 25)
+#define AHB_CSW_DBG_EN		(1 << 6)
+#define AHB_CSW_INC_NONE	(0 << 4)
+#define AHB_CSW_INC_SINGLE	(1 << 4)
+#define AHB_CSWINC_PACKED	(2 << 4)
+#define AHB_CSW_8BIT		(0 << 0)
+#define AHB_CSW_16BIT		(1 << 0)
+#define AHB_CSW_32BIT		(2 << 0)
+
+/* Core Debug registers */
+#define CDBG_CSR		0xE000EDF0
+#define CDBG_REG_ADDR		0xE000EDF4
+#define CDBG_REG_DATA		0xE000EDF8
+#define CDBG_EMCR		0xE000EDFC
+
+#define CDBG_CSR_KEY		0xA05F0000
+#define CDBG_S_RESET_ST		(1 << 25)
+#define CDBG_S_RETIRE_ST	(1 << 24)
+#define CDBG_S_LOCKUP		(1 << 19)
+#define CDBG_S_SLEEP		(1 << 18)
+#define CDBG_S_HALT		(1 << 17)
+#define CDBG_S_REGRDY		(1 << 16)
+#define CDBG_C_SNAPSTALL	(1 << 5)
+#define CDBG_C_MASKINTS		(1 << 3)
+#define CDBG_C_STEP		(1 << 2)
+#define CDBG_C_HALT		(1 << 1)
+#define CDBG_C_DEBUGEN		(1 << 0)
+
+#define IDCODE_M3 0x1BA01477
+
+#endif
diff --git a/include/protocol/usb.h b/include/protocol/usb.h
@@ -0,0 +1,64 @@
+#ifndef _PROTOCOL_USB_H_
+#define _PROTOCOL_USB_H_
+
+#define DSC_DEVICE		0x01
+#define DSC_CONFIG		0x02
+#define DSC_STRING		0x03
+#define DSC_INTERFACE		0x04
+#define DSC_ENDPOINT		0x05
+
+#define GET_STATUS		0x0080
+#define SET_ADDRESS		0x0500
+#define GET_DESCRIPTOR		0x0680
+#define SET_DESCRIPTOR		0x0700
+#define GET_CONFIGURATION	0x0880
+#define SET_CONFIGURATION	0x0900
+#define GET_INTERFACE		0x0A81
+#define SET_INTERFACE		0x0B01
+
+struct usb_setup_req{
+	union {
+		u8 bmRequestType;
+		struct {
+			unsigned rcpt:4;
+			unsigned type:3;
+			unsigned dir:1;
+		} __attribute__((packed)) t ;
+	};
+	u8 bRequest;
+	u16 wValue;
+	u16 wIndex;
+	u16 wLength;
+} __attribute__((packed));
+
+#define USB_REQ_GET_STATUS		0
+#define USB_REQ_CLEAR_FEATURE		1
+#define USB_REQ_SET_FEATURE		3
+#define USB_REQ_SET_ADDRESS		5
+#define USB_REQ_GET_DESCRIPTOR		6
+#define USB_REQ_SET_DESCRIPTOR		7
+#define USB_REQ_GET_CONFIGURATION	8
+#define USB_REQ_SET_CONFIGURATION	9
+#define USB_REQ_GET_INTERFACE		10
+#define USB_REQ_SET_INTERFACE		11
+#define USB_REQ_SYNCH_FRAME		12
+#define USB_REQ_SET_SEL			48
+#define USB_REQ_SET_ISOCH_DELAY		49
+
+#define USB_DESC_DEVICE			1
+#define USB_DESC_CONFIG			2
+#define USB_DESC_STRING			3
+#define USB_DESC_INTERFACE		4
+#define USB_DESC_ENDPOINT		5
+#define USB_DESC_INTERFACE_POWER	8
+#define USB_DESC_OTG			9
+#define USB_DESC_DEBUG			10
+#define USB_DESC_INTERFACE_ASSOCIATION	11
+#define USB_DESC_BOS			15
+#define USB_DESC_DEVICE_CAPABILITY	16
+#define USB_DESC_SUPERSPEED_ENDPOINT	48
+
+#define USB_DESC_VALUE(type, index)	(((type) << 8) | ((index) & 0xff))
+
+#endif
+
diff --git a/libc/strcpy.c b/libc/strcpy.c
@@ -0,0 +1,4 @@
+
+void strcpy(char *d, char *s) {
+	while ((*d++ = *s++)) ;
+}
diff --git a/libfw/print.c b/libfw/print.c
@@ -0,0 +1,149 @@
+/* print.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/lib.h>
+
+static const char hex[16] = "0123456789ABCDEF";
+
+char *printhex(char *tmp, unsigned c, unsigned n)
+{
+	tmp[c] = 0;
+	while (c-- > 0) {
+		tmp[c] = hex[n & 0x0F];
+		n >>= 4;
+	}
+
+	return tmp;
+}
+
+char *print_unsigned(char *tmp, int len, unsigned n, int w)
+{
+	len--;
+	tmp[len] = '\0';
+
+	if (w < 0 && n == 0) {
+		tmp[--len] = '0';
+	} else {
+		while ((len >= 0) && (n || (w > 0))) {
+			tmp[--len] = hex[n % 10];
+			n /= 10;
+			w--;
+		}
+	}
+
+	return &tmp[len];
+}
+
+char *print_signed(char *tmp, int len, signed n, int w)
+{
+	char *s;
+
+	if (n >= 0)
+		return print_unsigned(tmp, len, n, w);
+
+	s = print_unsigned(tmp + 1, len - 1, n * -1, w - 1);
+	s--;
+	*s = '-';
+	return s;
+}
+
+char *print_ufixed(char *tmp, int len, unsigned n)
+{
+	char * s;
+	char * point;
+
+	n >>= 2;
+
+	/* XXX: does not account for overflow */
+	n *= 15625;
+	s = print_unsigned(tmp + 1, len - 1, (n + 50000) / 100000 % 10, 1);
+	point = s - 1;
+	s = print_unsigned(tmp, (s - tmp), n / 1000000, -1);
+	*point = '.';
+
+	return s;
+}
+
+char *print_fixed(char *tmp, int len, signed n)
+{
+	char * s;
+
+	if (n >= 0)
+		return print_ufixed(tmp, len, n);
+
+	s = print_ufixed(tmp + 1, len - 1, n * -1);
+	s--;
+	*s = '-';
+
+	return s;
+}
+
+void vprintx(void (*_putc)(unsigned), const char *fmt, va_list ap) {
+	unsigned c;
+	const char *s;
+	char tmp[16];
+
+	while ((c = *fmt++)) {
+		if (c != '%') {
+			_putc(c);
+			continue;
+		}
+
+		switch((c = *fmt++)) {
+		case 0:
+			return;
+
+		case 'x':
+			s = printhex(tmp, 8, va_arg(ap, unsigned));
+			break;
+
+		case 'h':
+			s = printhex(tmp, 4, va_arg(ap, unsigned));
+			break;
+
+		case 'b':
+			s = printhex(tmp, 2, va_arg(ap, unsigned));
+			break;
+
+		case 'u':
+			s = print_unsigned(tmp, sizeof(tmp), va_arg(ap, unsigned), -1);
+			break;
+
+		case 'd':
+			s = print_signed(tmp, sizeof(tmp), va_arg(ap, signed), -1);
+			break;
+
+		case 'f':
+			s = print_fixed(tmp, sizeof(tmp), va_arg(ap, signed));
+			break;
+
+		case 's':
+			s = va_arg(ap, const char *);
+			break;
+
+		case 'c':
+			c = va_arg(ap, unsigned); /* fall through */
+
+		default:
+			_putc(c); continue;
+		}
+
+		while (*s)
+			_putc(*s++);
+	}
+}
+
diff --git a/libfw/serialconsole.c b/libfw/serialconsole.c
@@ -0,0 +1,25 @@
+/* serialconsole.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/lib.h>
+
+void printx(const char *fmt, ...) {
+	va_list ap;
+	va_start(ap, fmt);
+	vprintx(serial_putc, fmt, ap);
+	va_end(ap);
+}
diff --git a/libfw/string.c b/libfw/string.c
@@ -0,0 +1,21 @@
+#include <fw/types.h>
+
+void *memcpy(void *s1, const void *s2, int n)
+{
+	u8 *dest = (u8 *) s1;
+	u8 *src = (u8 *) s2;
+	// TODO: arm-cm3 optimized version
+	while (n--)
+		*dest++ = *src++;
+
+	return s1;
+}
+
+void *memset(void *b, int c, int len)
+{
+	u8 *buff = b;
+	while (len--)
+		*buff++ = (u8) c;
+
+	return b;
+}
diff --git a/libfw/usbconsole.c b/libfw/usbconsole.c
@@ -0,0 +1,122 @@
+/* usbconsole.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/lib.h>
+
+#include <arch/cpu.h>
+
+#define UC_OUT_SZ 256
+#define UC_IN_SZ 32
+
+struct usbconsole {
+	volatile u32 out_head;
+	volatile u32 out_tail;
+	volatile u32 out_busy;	
+	volatile u32 in_count;
+	u8 *out_data;
+//	u8 *in_data;
+};
+
+/* buffers separate from main structure to save on flash size */
+static u8 uc_out_buffer[UC_OUT_SZ];
+//static u8 uc_in_buffer[UC_IN_SZ];
+
+static struct usbconsole the_usb_console = {
+	.out_head = 0,
+	.out_tail = 0,
+	.out_busy = 1,
+	.in_count = 0,
+	.out_data = uc_out_buffer,
+//	.in_data = uc_in_buffer,
+};
+
+static void uc_write(struct usbconsole *uc) {
+	u32 pos, len, rem;
+
+	pos = uc->out_tail & (UC_OUT_SZ - 1);
+	len = uc->out_head - uc->out_tail;
+	rem = UC_OUT_SZ - pos;
+	
+	if (len > rem)
+		len = rem;
+	if (len > 64)
+		len = 64;
+
+	disable_interrupts();
+	uc->out_busy = 1;
+	usb_ep1_write(uc->out_data + pos, len);
+	enable_interrupts();
+	uc->out_tail += len;
+}
+
+static void uc_putc(unsigned c) {
+	struct usbconsole *uc = &the_usb_console;
+	while ((uc->out_head - uc->out_tail) == UC_OUT_SZ) {
+		if (!uc->out_busy)
+			uc_write(uc);
+	}
+	uc->out_data[uc->out_head & (UC_OUT_SZ - 1)] = c;
+	uc->out_head++;
+}
+
+void printu(const char *fmt, ...) {
+	struct usbconsole *uc = &the_usb_console;
+        va_list ap;
+        va_start(ap, fmt);
+        vprintx(uc_putc, fmt, ap);
+        va_end(ap);
+	if ((uc->out_head - uc->out_tail) && (!uc->out_busy)) {
+		uc_write(uc);
+	}
+}
+
+void handle_ep1_tx_empty(void) {
+	struct usbconsole *uc = &the_usb_console;
+	uc->out_busy = 0;
+	if (uc->out_head - uc->out_tail)
+		uc_write(uc);
+}
+
+void (*usb_console_rx_cb)(u8 *buf, int len);
+
+void handle_ep1_rx_full(void) {
+//	struct usbconsole *uc = &the_usb_console;
+	u8 buf[65];
+	int len = usb_ep1_read(buf, 64);
+
+	if (usb_console_rx_cb)
+		usb_console_rx_cb(buf, len);
+}
+
+void handle_usb_online(int yes) {
+	if (yes) {
+		handle_ep1_tx_empty();
+	} else {
+		the_usb_console.out_busy = 1;
+	}
+}
+
+void usb_console_init(void) {
+	usb_ep1_tx_empty_cb = handle_ep1_tx_empty;
+	usb_ep1_rx_full_cb = handle_ep1_rx_full;
+	usb_online_cb = handle_usb_online;
+//	usb_init(0x18d1, 0xdb05, 0, 0);
+	usb_unmask_ep1_tx_empty();
+	usb_unmask_ep1_rx_full();
+}
+
diff --git a/lpc13boot/flash-lpc13boot.scr b/lpc13boot/flash-lpc13boot.scr
@@ -0,0 +1,67 @@
+attach
+reset-stop
+reset
+
+reset-stop
+watch-rw 0
+go
+
+wr 10000000 be00be00
+wr sp 10001fc0
+
+# prepare for write
+wr pc 1fff1ff1
+wr lr 10000001
+wr r0 10000010 
+wr r1 10000030
+wr 10000010 .50
+wr 10000014 0
+wr 10000018 0
+go
+
+# erase
+wr pc 1fff1ff1
+wr lr 10000001
+wr r0 10000010
+wr r1 10000030
+wr 10000010 .52
+wr 10000014 0
+wr 10000018 0
+wr 1000001c 2ee0
+go
+reset-stop
+watch-rw 0
+go
+
+# write breakpoint at 10000000 and setup SP
+wr 10000000 be00be00
+wr sp 10001fc0
+
+# prepare for write
+wr pc 1fff1ff1
+wr lr 10000001
+wr r0 10000010 
+wr r1 10000030
+wr 10000010 .50
+wr 10000014 0
+wr 10000018 0
+go
+
+#download out/m3debug.bin 10000800
+download out/lpc13boot.bin 10000800
+
+# write 
+wr pc 1fff1ff1
+wr lr 10000001
+wr r0 10000010
+wr r1 10000030
+wr 10000010 .51
+wr 10000014 00000000
+wr 10000018 10000800
+wr 1000001c 1000
+wr 10000020 2ee0
+go
+
+watch-pc ffffffff
+reset
+go
diff --git a/lpc13boot/main.c b/lpc13boot/main.c
@@ -0,0 +1,243 @@
+/* main.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/lib.h>
+#include <fw/io.h>
+#include <fw/string.h>
+
+#include <arch/hardware.h>
+
+extern u8 board_name[];
+
+#define RAM_BASE	0x10000000
+#define RAM_SIZE	(7 * 1024)
+
+#define ROM_BASE	0x00001000
+#define ROM_SIZE	(28 * 1024)
+
+void (*romcall)(u32 *in, u32 *out) = (void*) 0x1fff1ff1;
+
+/* { PREPARE, startpage, endpage } */
+#define OP_PREPARE	50
+/* { WRITE, dstaddr, srcaddr, bytes, sysclk_khz } */
+#define OP_WRITE	51
+/* { ERASE, startpage, endpage, sysclk_khz } */
+#define OP_ERASE	52
+
+struct device_info {
+	u8 part[16];
+	u8 board[16];
+	u32 version;
+	u32 ram_base;
+	u32 ram_size;
+	u32 rom_base;
+	u32 rom_size;
+	u32 unused0;
+	u32 unused1;
+	u32 unused2;
+};
+
+struct device_info DEVICE = {
+	.part = "LPC1343",
+	.version = 0x0001000,
+	.ram_base = RAM_BASE,
+	.ram_size = RAM_SIZE,
+	.rom_base = ROM_BASE,
+	.rom_size = ROM_SIZE,
+};
+
+void iap_erase_page(unsigned addr) {
+	u32 in[4];
+	u32 out[5];
+	in[0] = OP_PREPARE;
+	in[1] = (addr >> 12) & 0xF;
+	in[2] = (addr >> 12) & 0xF;
+	romcall(in, out);
+	in[0] = OP_ERASE;
+	in[1] = (addr >> 12) & 0xF;
+	in[2] = (addr >> 12) & 0xF;
+	in[3] = 48000;
+	romcall(in, out);
+}
+
+void iap_write_page(unsigned addr, void *src) {
+	u32 in[5];
+	u32 out[5];
+	in[0] = OP_PREPARE;
+	in[1] = (addr >> 12) & 0xF;
+	in[2] = (addr >> 12) & 0xF;
+	romcall(in, out);
+	in[0] = OP_WRITE;
+	in[1] = addr;
+	in[2] = (u32) src;
+	in[3] = 0x1000;
+	in[4] = 48000;
+	romcall(in, out);
+}
+
+static u32 buf[64];
+
+void start_image(u32 pc, u32 sp);
+
+void _boot_image(void *img) {
+	start_image(((u32*)img)[1], ((u32*)img)[0]);
+}
+
+void boot_image(void *img) {
+	board_debug_led(0);
+	usb_stop();
+
+	/* TODO: shut down various peripherals */
+	_boot_image(img);
+}
+
+void handle(u32 magic, u32 cmd, u32 arg) {
+	u32 reply[2];
+	u32 addr, xfer;
+	void *ram = (void*) RAM_BASE;
+
+	if (magic != 0xDB00A5A5)
+		return;
+
+	reply[0] = magic;
+	reply[1] = -1;
+
+	switch (cmd) {
+	case 'E':
+		iap_erase_page(0x1000);
+		reply[1] = 0;
+		break;
+	case 'W':
+		if (arg > ROM_SIZE)
+			break;
+		reply[1] = 0;
+		usb_xmit(reply, 8);
+		addr = ROM_BASE;
+		while (arg > 0) {
+			xfer = (arg > 4096) ? 4096 : arg;
+			usb_recv(ram, xfer);
+			iap_erase_page(addr);
+			iap_write_page(addr, ram);
+			addr += 4096;
+			arg -= xfer;
+		}
+		break;
+	case 'X':
+		if (arg > RAM_SIZE)
+			break;
+		reply[1] = 0;
+		usb_xmit(reply, 8);
+		usb_recv(ram, arg);
+		usb_xmit(reply, 8);
+
+		/* let last txn clear */
+		usb_recv_timeout(buf, 64, 10);
+
+		boot_image(ram);
+		break;
+	case 'Q':
+		reply[1] = 0;
+		usb_xmit(reply, 8);
+		usb_xmit(&DEVICE, sizeof(DEVICE));
+		return;
+	case 'A':
+		/* reboot-into-app -- signal to bootloader via GPREGn */
+		writel(0x12345678, GPREG0);
+		writel(0xA5A50001, GPREG1);
+	case 'R':
+		/* reboot "normally" */
+		reply[1] = 0;
+		usb_xmit(reply, 8);
+		usb_recv_timeout(buf, 64, 10);
+		reboot();
+	default:
+		break;
+	}
+	usb_xmit(reply, 8);
+}
+
+int main() {
+	int n, x, timeout;
+	u32 tmp;
+	u32 gpr0,gpr1;
+
+	/* sample GPREG and clear */
+	gpr0 = readl(GPREG0);
+	gpr1 = readl(GPREG1);
+	writel(0xBBBBBBBB, GPREG0);
+	writel(0xBBBBBBBB, GPREG1);
+
+	/* request to boot directly into the "app" image */
+	if ((gpr0 == 0x12345678) && (gpr1 == 0xA5A50001))
+		_boot_image((void*) 0x1000);
+
+	board_init();
+
+	usb_init(0x18d1, 0xdb00, "m3dev", "m3boot");
+
+	/* check for an app image and set a 3s timeout if it exists */
+	tmp = *((u32*) 0x1000);
+	if ((tmp != 0) && (tmp != 0xFFFFFFFF))
+		timeout = 30;
+	else
+		timeout = 0;
+
+	/* request to stay in the bootloader forever? */
+	if ((gpr0 == 0x12345678) && (gpr1 == 0xA5A50000))
+		timeout = 0;
+
+	strcpy((char*) DEVICE.board, (char*) board_name);
+
+	if (timeout) {
+		/* wait up to 1s to enumerate */
+		writel(readl(SYS_CLK_CTRL) | SYS_CLK_CT32B0, SYS_CLK_CTRL);
+		writel(48, TM32B0PR);
+		writel(TM32TCR_ENABLE, TM32B0TCR);
+		while (!usb_online() && (readl(TM32B0TC) < 2000000)) ;
+		writel(readl(SYS_CLK_CTRL) & (~SYS_CLK_CT32B0), SYS_CLK_CTRL);
+		if (!usb_online())
+			goto start_app;
+	}
+
+	x = 0;
+	for (;;) {
+		board_debug_led(x & 1);
+		x++;
+		n = usb_recv_timeout(buf, 64, 100);
+
+		if ((n == -ETIMEOUT) && timeout) {
+			timeout--;
+			if (timeout == 0)
+				break;
+		}
+
+		if (n == 12) {
+			timeout = 0;
+			handle(buf[0], buf[1], buf[2]);
+		}
+	}
+
+start_app:
+	/* warm reset into the app */
+	writel(0x12345678, GPREG0);
+	writel(0xA5A50001, GPREG1);
+	reboot();
+
+	return 0;
+}
+
diff --git a/lpc13boot/misc.S b/lpc13boot/misc.S
@@ -0,0 +1,8 @@
+
+.globl start_image
+
+.thumb_func
+start_image:
+	mov sp, r1
+	bx r0
+	b .
diff --git a/lpc13boot/module.mk b/lpc13boot/module.mk
@@ -0,0 +1,10 @@
+$(call start-module-mk)
+
+M_NAME := lpc13boot
+M_CHIP := lpc1343-blr
+M_OBJS := board/m3debug.o
+M_OBJS += lpc13boot/main.o
+M_OBJS += lpc13boot/misc.o
+M_OBJS += libc/strcpy.o
+$(call build-target-executable)
+
diff --git a/m3debug/m3debug-schematic.pdf b/m3debug/m3debug-schematic.pdf
Binary files differ.
diff --git a/m3debug/main.c b/m3debug/main.c
@@ -0,0 +1,343 @@
+/* main.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/lib.h>
+#include <fw/io.h>
+
+#include <arch/hardware.h>
+#include <arch/cpu.h>
+#include <arch/interrupts.h>
+
+#include <protocol/rswdp.h>
+#include "swdp.h"
+
+extern u32 gpio_led0;
+extern u32 gpio_led1;
+extern u32 gpio_led2;
+extern u32 gpio_led3;
+extern u32 gpio_reset_n;
+
+unsigned swdp_trace = 0;
+
+void reboot_bootloader(void) {
+	usb_stop();
+	writel(0x12345678, GPREG0);
+	writel(0xA5A50000, GPREG1);
+	reboot();
+}
+
+#define DEBUG_MAX 52
+static struct {
+	u32 txn;
+	u32 cmd;
+	u8 data[DEBUG_MAX];
+} pmsg = {
+	.txn = RSWD_TXN_ASYNC,
+	.cmd = RSWD_MSG(CMD_DEBUG_PRINT, 0, DEBUG_MAX/4),
+};
+static unsigned poff = 0;
+
+void flushu(void) {
+	while (poff < DEBUG_MAX)
+		pmsg.data[poff++] = 0;
+	usb_xmit(&pmsg, sizeof(pmsg));
+	poff = 0;
+}
+
+void _putu(unsigned c) {
+	pmsg.data[poff++] = c;
+	if (poff == DEBUG_MAX)
+		flushu();
+}
+
+void printu(const char *fmt, ...) {
+	va_list ap;
+	va_start(ap, fmt);
+	vprintx(_putu, fmt, ap);
+	va_end(ap);
+	if (poff)
+		flushu();
+}
+
+static u8 optable[16] = {
+	[OP_RD | OP_DP | OP_X0] = RD_IDCODE,
+	[OP_RD | OP_DP | OP_X4] = RD_DPCTRL,
+	[OP_RD | OP_DP | OP_X8] = RD_RESEND,
+	[OP_RD | OP_DP | OP_XC] = RD_BUFFER,
+	[OP_WR | OP_DP | OP_X0] = WR_ABORT,
+	[OP_WR | OP_DP | OP_X4] = WR_DPCTRL,
+	[OP_WR | OP_DP | OP_X8] = WR_SELECT,
+	[OP_WR | OP_DP | OP_XC] = WR_BUFFER,
+	[OP_RD | OP_AP | OP_X0] = RD_AP0,
+	[OP_RD | OP_AP | OP_X4] = RD_AP1,
+	[OP_RD | OP_AP | OP_X8] = RD_AP2,
+	[OP_RD | OP_AP | OP_XC] = RD_AP3,
+	[OP_WR | OP_AP | OP_X0] = WR_AP0,
+	[OP_WR | OP_AP | OP_X4] = WR_AP1,
+	[OP_WR | OP_AP | OP_X8] = WR_AP2,
+	[OP_WR | OP_AP | OP_XC] = WR_AP3,
+};
+
+/* TODO bounds checking -- we trust the host far too much */
+void process_txn(u32 txnid, u32 *rx, int rxc, u32 *tx) {
+	unsigned msg, op, n;
+	unsigned txc = 1;
+	unsigned count = 0;
+	unsigned status = 0;
+	void (*func)(void) = 0;
+
+	tx[0] = txnid;
+
+	while (rxc-- > 0) {
+		count++;
+		msg = *rx++;
+		op = RSWD_MSG_OP(msg);
+		n = RSWD_MSG_ARG(msg);
+#if CONFIG_M3DEBUG_TRACE
+		printx("> %b %b %h <\n", RSWD_MSG_CMD(msg), op, n);
+#endif
+		switch (RSWD_MSG_CMD(msg)) {
+		case CMD_NULL:
+			continue;
+		case CMD_SWD_WRITE:
+			while (n-- > 0) {
+				rxc--;
+				if (swdp_write(optable[op], *rx++)) {
+					status = 3;
+					goto done;
+				}
+			}
+			continue;
+		case CMD_SWD_READ:
+			tx[txc++] = RSWD_MSG(CMD_SWD_DATA, 0, n); 
+			while (n-- > 0) {
+				if (swdp_read(optable[op], tx + txc)) {
+					txc++;
+					while (n-- > 0)
+						tx[txc++] = 0xfefefefe;
+					status = 3;
+					goto done;
+				}
+				txc++;
+			}
+			continue;
+		case CMD_SWD_DISCARD:
+			while (n-- > 0) {
+				u32 tmp;
+				if (swdp_read(optable[op], &tmp)) {
+					status = 3;
+					goto done;
+				}
+			}
+			continue;
+		case CMD_ATTACH:
+			swdp_reset();
+			continue;
+		case CMD_RESET:
+			if (n == 0) {
+				/* deassert RESET */
+				gpio_cfg_dir(gpio_reset_n, GPIO_CFG_IN);
+			} else {
+				/* assert RESET */
+				gpio_cfg_dir(gpio_reset_n, GPIO_CFG_OUT);
+				gpio_clr(gpio_reset_n);
+			}
+			continue;
+		case CMD_DOWNLOAD: {
+			u32 *addr = (void*) *rx++;
+			rxc--;
+			while (n) {
+				*addr++ = *rx++;
+				rxc--;
+			}
+			continue;
+		}
+		case CMD_EXECUTE:
+			func = (void*) *rx++;
+			rxc--;
+			continue;
+		case CMD_TRACE:
+			swdp_trace = op;
+			continue;
+		case CMD_BOOTLOADER:
+			func = reboot_bootloader;
+			continue;
+		case CMD_SET_CLOCK:
+			n = ssp0_set_clock(n);
+			printu("swdp clock is now 0x%x KHz\n", n);
+			continue;
+		default:
+			printx("unknown command %b\n", RSWD_MSG_CMD(msg));
+			status = 1;
+			goto done;
+		}
+	}
+
+done:
+	tx[txc++] = RSWD_MSG(CMD_STATUS, status, count);
+
+	/* if we're about to send an even multiple of the packet size
+	 * (64), add a NULL op on the end to create a short packet at
+	 * the end.
+	 */
+	if ((txc & 0xf) == 0)
+		tx[txc++] = RSWD_MSG(CMD_NULL, 0, 0);
+
+#if CONFIG_M3DEBUG_TRACE
+	printx("[ send %x words ]\n", txc);
+	for (n = 0; n < txc; n+=4) {
+		printx("%x %x %x %x\n",
+			tx[n], tx[n+1], tx[n+2], tx[n+3]);
+	}
+#endif
+	usb_xmit(tx, txc * 4);
+
+	if (func) {
+		for (n = 0; n < 1000000; n++) asm("nop");
+		func();
+		for (;;) ;
+	}	
+}
+
+static u32 rxbuffer[512];
+static u32 txbuffer[512+1];
+
+#if CONFIG_M3DEBUG_SERIAL_IFC
+extern void _start(void);
+
+static void ep2_rx_full_cb(void)
+{
+	int i;
+
+	static char buf[64];
+	int len = usb_ep2_read(buf, sizeof(buf));
+	for (i = 0; i < len; i++) {
+		serial_putc(buf[i]);
+	}
+}
+
+static char outbuf[64];
+static volatile int outbufpos = 0;
+static volatile int ep2_tx_active = 0;
+
+static void queue_ep2_tx(void)
+{
+	if (outbufpos > 0 && !ep2_tx_active) {
+		ep2_tx_active = 1;
+		int oldbufpos = outbufpos;
+		outbufpos = 0;
+
+		// side effect of this routine is enabling interrupts
+		usb_ep2_write(outbuf, oldbufpos);
+	}
+}
+
+static void ep2_tx_empty_cb(void)
+{
+//	printx("tx empty\n");
+	disable_interrupts();
+
+	ep2_tx_active = 0;
+	queue_ep2_tx();
+
+	enable_interrupts();
+}
+
+static void serial_async_cb(char c)
+{
+	if (outbufpos >= sizeof(outbuf))
+		return;
+
+	disable_interrupts();
+
+	outbuf[outbufpos++] = c;
+
+//	serial_putc(c);
+	queue_ep2_tx();
+
+	enable_interrupts();
+}
+#endif
+
+int main() {
+	int rxc;
+
+	board_init();
+
+#if CONFIG_M3DEBUG_SERIAL_IFC
+#ifndef CONFIG_USB_2ND_IFC
+#error CONFIG_M3DEBUG_SERIAL_IFC requires CONFIG_USB_2ND_IFC
+#endif
+#ifndef CONFIG_USB_USE_IRQS
+#error CONFIG_M3DEBUG_SERIAL_IFC requires CONFIG_USB_USE_IRQS
+#endif
+	irq_set_base((unsigned) _start);
+	enable_interrupts();
+#endif
+
+	serial_init(48000000, 115200);
+	ssp0_init();
+
+#if CONFIG_M3DEBUG_TRACE
+	printx("[ rswdp agent v0.9 ]\n");
+	printx("[ built " __DATE__ " " __TIME__ " ]\n");
+#endif
+
+#if CONFIG_M3DEBUG_SERIAL_IFC
+	usb_init(0x18d1, 0xdb04, "m3dev", "super-m3debug");
+
+	usb_ep2_rx_full_cb = &ep2_rx_full_cb;
+	usb_ep2_tx_empty_cb = &ep2_tx_empty_cb;
+	usb_unmask_ep2_rx_full();
+	usb_unmask_ep2_tx_empty();
+
+	serial_start_async_rx(&serial_async_cb);
+	irq_enable(v_uart);
+#else
+	usb_init(0x18d1, 0xdb03, "m3dev", "m3debug");
+#endif
+
+	for (;;) {
+		gpio_clr(gpio_led0);
+		rxc = usb_recv(rxbuffer, sizeof(rxbuffer));
+		gpio_set(gpio_led0);
+
+#if CONFIG_M3DEBUG_TRACE
+		int n;
+		printx("[ recv %x words ]\n", rxc/4);
+		for (n = 0; n < (rxc/4); n+=4) {
+			printx("%x %x %x %x\n",
+				rxbuffer[n], rxbuffer[n+1], rxbuffer[n+2], rxbuffer[n+3]);
+		}
+#endif
+
+		if ((rxc < 4) || (rxc & 3)) {
+			printx("error, runt frame, or strange frame... %x\n", rxc);
+			continue;
+		}
+
+		rxc = rxc / 4;
+
+		if ((rxbuffer[0] & 0xFFFF0000) != 0xAA770000) {
+			printx("invalid frame %x\n", rxbuffer[0]);
+			continue;
+		}
+
+		process_txn(rxbuffer[0], rxbuffer + 1, rxc - 1, txbuffer);
+	}
+}
diff --git a/m3debug/module.mk b/m3debug/module.mk
@@ -0,0 +1,11 @@
+$(call start-module-mk)
+
+M_NAME := m3debug 
+M_CHIP := lpc1343-app
+M_OBJS += board/m3debug.o
+M_OBJS += m3debug/main.o
+M_OBJS += m3debug/swdp.o
+M_OBJS += libfw/print.o
+M_OBJS += libfw/serialconsole.o
+$(call build-target-executable)
+
diff --git a/m3debug/swdp.c b/m3debug/swdp.c
@@ -0,0 +1,175 @@
+/* swdp.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/io.h>
+#include <fw/lib.h>
+
+#include <arch/hardware.h>
+#include <protocol/rswdp.h>
+
+volatile unsigned count;
+
+unsigned data[8];
+
+static inline void DIR_OUT(unsigned n) {
+	writel(SSP_CR0_BITS(n) | SSP_CR0_FRAME_SPI |
+		SSP_CR0_CLK_HIGH | SSP_CR0_PHASE1 |
+		SSP_CR0_CLOCK_RATE(1),
+		SSP0_CR0);
+	writel(IOCON_FUNC_1 | IOCON_DIGITAL, IOCON_PIO0_9); /* MOSI */
+}
+
+static inline void DIR_IN(unsigned n) {
+	writel(IOCON_FUNC_0 | IOCON_DIGITAL, IOCON_PIO0_9); /* MOSI */
+	writel(SSP_CR0_BITS(n) | SSP_CR0_FRAME_SPI |
+		SSP_CR0_CLK_HIGH | SSP_CR0_PHASE0 |
+		SSP_CR0_CLOCK_RATE(1),
+		SSP0_CR0);
+}
+
+static inline void XMIT(unsigned n) {
+	writel(n, SSP0_DR);
+	while (readl(SSP0_SR) & SSP_BUSY);
+	readl(SSP0_DR);
+}
+
+static inline unsigned RECV(void) {
+	writel(0, SSP0_DR);
+	while (readl(SSP0_SR) & SSP_BUSY);
+	return readl(SSP0_DR);
+}
+
+void swdp_reset(void) {
+	/* clock out 64 1s */
+	DIR_OUT(16);
+	writel(0xFFFF, SSP0_DR);
+	writel(0xFFFF, SSP0_DR);
+	writel(0xFFFF, SSP0_DR);
+	writel(0xFFFF, SSP0_DR);
+	while (readl(SSP0_SR) & SSP_BUSY) ;
+	readl(SSP0_DR);
+	readl(SSP0_DR);
+	readl(SSP0_DR);
+	readl(SSP0_DR);
+
+	/* clock out 16bit init sequence */
+	writel(0b0111100111100111, SSP0_DR);
+	while (readl(SSP0_SR) & SSP_BUSY) ;
+	readl(SSP0_DR);
+
+	/* clock out 64 1s */
+	writel(0xFFFF, SSP0_DR);
+	writel(0xFFFF, SSP0_DR);
+	writel(0xFFFF, SSP0_DR);
+	writel(0xFFFF, SSP0_DR);
+	while (readl(SSP0_SR) & SSP_BUSY) ;
+	readl(SSP0_DR);
+	readl(SSP0_DR);
+	readl(SSP0_DR);
+	readl(SSP0_DR);
+}
+
+static unsigned revbits(unsigned n) {
+        n = ((n >>  1) & 0x55555555) | ((n <<  1) & 0xaaaaaaaa);
+        n = ((n >>  2) & 0x33333333) | ((n <<  2) & 0xcccccccc);
+        n = ((n >>  4) & 0x0f0f0f0f) | ((n <<  4) & 0xf0f0f0f0);
+        n = ((n >>  8) & 0x00ff00ff) | ((n <<  8) & 0xff00ff00);
+        n = ((n >> 16) & 0x0000ffff) | ((n << 16) & 0xffff0000);
+        return n;
+}
+
+/* returns 1 if the number of bits set in n is odd */
+static unsigned parity(unsigned n) {
+        n = (n & 0x55555555) + ((n & 0xaaaaaaaa) >> 1);
+        n = (n & 0x33333333) + ((n & 0xcccccccc) >> 2);
+        n = (n & 0x0f0f0f0f) + ((n & 0xf0f0f0f0) >> 4);
+        n = (n & 0x00ff00ff) + ((n & 0xff00ff00) >> 8);
+        n = (n & 0x0000ffff) + ((n & 0xffff0000) >> 16);
+        return n & 1;
+}
+
+volatile int count_rd_wait = 0;
+volatile int count_wr_wait = 0;
+
+int swdp_write(unsigned n, unsigned v) {
+	unsigned a, p;
+
+again:
+	/* clock out 8 0s and read sequence */
+	XMIT(n);
+
+	/* read X R0 R1 R2 */
+	DIR_IN(4);
+	a = RECV();
+	if ((a & 7) != 4) {
+		DIR_OUT(16);
+		if ((a & 7) == 2) {
+			count_wr_wait++;
+			goto again;
+		}
+		return -1;
+	}
+
+	p = parity(v);
+	v = revbits(v);
+
+	/* transmit X D0..D31 P */
+	DIR_OUT(9);
+	XMIT(0x100 | (v >> 24));
+	DIR_OUT(16);
+	XMIT(v >> 8);
+	DIR_OUT(9);
+	XMIT((v << 1) | p);
+
+	DIR_OUT(16);
+
+	return 0;
+}
+
+int swdp_read(unsigned n, unsigned *out) {
+	unsigned a, b, c;
+
+again:
+	/* clock out 8 0s and read sequence */
+	XMIT(n);
+
+	/* read X R0 R1 R2 */
+	DIR_IN(4);
+	a = RECV();
+	if ((a & 7) != 4) {
+		DIR_OUT(16);
+		if ((a & 7) == 2) {
+			count_rd_wait++;
+			goto again;
+		}
+		*out = 0xffffffff;
+		return -1;
+	}
+
+	/* D0..D31 P X */
+	DIR_IN(16);
+	a = RECV();
+	DIR_IN(8);
+	b = RECV();
+	DIR_IN(10);
+	c = RECV();
+
+	*out = revbits((a << 16) | (b << 8) | (c >> 2));
+	DIR_OUT(16);
+	return 0;
+}
diff --git a/m3debug/swdp.h b/m3debug/swdp.h
@@ -0,0 +1,53 @@
+/* swdp.h 
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _SWDP_H_
+#define _SWDP_H_
+
+void swdp_reset(void);
+int swdp_write(unsigned reg, unsigned val);
+int swdp_read(unsigned reg, unsigned *val);
+
+unsigned swdp_ap_write(unsigned reg, unsigned val);
+unsigned swdp_ap_read(unsigned reg);
+
+unsigned swdp_ahb_read(unsigned addr);
+void swdp_ahb_write(unsigned addr, unsigned value);
+
+/* swdp_read/write() register codes */
+#define RD_IDCODE	0xA5	// 10100101
+#define RD_DPCTRL	0xB1	// 10110001
+#define RD_RESEND	0xA9	// 10101001
+#define RD_BUFFER	0xBD	// 10111101
+
+#define WR_ABORT	0x81	// 10000001
+#define WR_DPCTRL	0x95	// 10010101
+#define WR_SELECT	0x8D	// 10001101
+#define WR_BUFFER	0x99	// 10011001
+
+#define RD_AP0		0xE1	// 11100001
+#define RD_AP1		0xF5	// 11110101
+#define RD_AP2		0xED	// 11101101
+#define RD_AP3		0xF9	// 11111001
+
+#define WR_AP0		0xC5	// 11000101
+#define WR_AP1		0xD1	// 11010001
+#define WR_AP2		0xC9	// 11001001
+#define WR_AP3		0xDD	// 11011101
+
+#endif
+
diff --git a/scripts/lpc13xx b/scripts/lpc13xx
@@ -0,0 +1,92 @@
+# scripts/lpc13xx
+#
+# Copyright 2011 Brian Swetland <swetland@frotz.net>
+# 
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# 32K flash in 1K pages at 0
+set flash-start 0
+set flash-size 8000
+set flash-block 1000
+
+# use ram at 10000000 for download buffer
+set flash-buffer 10000000
+
+# flash-setup
+# - attach and reset
+# - allow on-die ROM to run (important for correct flash init)
+# - regain control when it attempts to read the reset vector
+function flash-setup
+	attach
+	reset-stop
+	watch-rw 0
+	go
+
+	# write breakpoint at 10001000 and setup SP
+	wr 10001000 be00be00
+	wr sp 10001f00
+end
+
+# flash-erase <flash-addr>
+function flash-erase
+	set page $1 >> .12
+
+	# prepare for write
+	wr pc 1fff1ff1
+	wr lr 10001001
+	wr r0 10001010 
+	wr r1 10001030
+	wr 10001010 .50
+	wr 10001014 $page
+	wr 10001018 $page
+	go
+
+	# erase
+	wr pc 1fff1ff1
+	wr lr 10001001
+	wr r0 10001010
+	wr r1 10001030
+	wr 10001010 .52
+	wr 10001014 $page 
+	wr 10001018 $page
+	wr 1000101c 2ee0
+	go
+end
+
+# flash-write <flash-addr>
+function flash-write
+	set page $1 >> .12
+
+	# prepare for write
+	wr pc 1fff1ff1
+	wr lr 10001001
+	wr r0 10001010 
+	wr r1 10001030
+	wr 10001010 .50
+	wr 10001014 $page
+	wr 10001018 $page
+	go
+
+	# write 
+	wr pc 1fff1ff1
+	wr lr 10001001
+	wr r0 10001010
+	wr r1 10001030
+	wr 10001010 .51
+	wr 10001014 $1
+	wr 10001018 10000000
+	wr 1000101c 1000
+	wr 10001020 2ee0
+	go
+end
+
diff --git a/scripts/stm32f1xx b/scripts/stm32f1xx
@@ -0,0 +1,80 @@
+set flash-start 0
+set flash-size 40000
+set flash-block 800
+
+set flash-buffer 20008000
+
+set FLASH_ACR 40022000
+set FLASH_KEYR 40022004
+set FLASH_OPTKEYR 40022008
+set FLASH_SR 4002200c
+set FLASH_CR 40022010
+set FLASH_AR 40022014
+set FLASH_OBR 4002201c
+set FLASH_WRPR 40022020
+
+set FLASH_ENTRY_INIT 20001001
+set FLASH_ENTRY_ERASE_ALL 20001005
+set FLASH_ENTRY_ERASE_PAGE 20001009
+set FLASH_ENTRY_WRITE_PAGE 2000100d
+
+set FLASH_STACK 20008000
+
+function flash-setup
+	attach
+	reset-stop
+
+	download out/stm32f1xx_flash.bin 20001000
+	wr psr 01000000
+	wr pc $FLASH_ENTRY_INIT
+	wr 20007000 be00be00
+	wr lr 20007001
+	wr sp $FLASH_STACK
+
+	go
+	echo flash setup completed
+	dw $FLASH_ACR
+end
+
+function flash-erase-all
+	echo flash-erase-all
+	wr pc $FLASH_ENTRY_ERASE_ALL
+	wr 20007000 be00be00
+	wr lr 20007001
+	wr sp $FLASH_STACK
+
+	go
+	echo flash mass erase completed
+end
+
+function flash-erase
+	echo flash-erase args $1
+
+	wr r0 $1
+	wr pc $FLASH_ENTRY_ERASE_PAGE
+	wr 20007000 be00be00
+	wr lr 20007001
+	wr sp $FLASH_STACK
+
+	go
+	echo flash page erase completed
+end
+
+function flash-write
+	echo flash-write args $1
+
+	wr r0 $1
+	wr pc $FLASH_ENTRY_WRITE_PAGE
+	wr 20007000 be00be00
+	wr lr 20007001
+	wr sp $FLASH_STACK
+
+	go
+	echo flash page write completed
+end
+
+#flash-erase-all
+#dw 08000000
+
+#flash stm32.bin 08000000
+#dw 08000000
diff --git a/swdp/main.c b/swdp/main.c
@@ -0,0 +1,279 @@
+/* main.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/lib.h>
+#include <fw/io.h>
+
+#include <arch/hardware.h>
+
+#include <protocol/rswdp.h>
+#include "swdp.h"
+
+#define TRACE 0
+
+#define GPIO_LED	5
+#define GPIO_RESET_N	3
+
+extern unsigned swdp_trace;
+
+void clocks_to_72mhz() {
+	/* external oscillator enable */
+	writel(readl(RCC_CR) | 0x00010001, RCC_CR);
+	while ((readl(RCC_CR) & 0x00020000) == 0) ;
+
+	/* flash prefetch enable */
+	writel(0x12, 0x40022000);
+
+	/* configure PLL for 72MHz */
+	writel(readl(RCC_CFGR) | 0x001D0400, RCC_CFGR);
+	writel(readl(RCC_CR) | 0x01000000, RCC_CR);
+	while ((readl(RCC_CR) & 0x03000000) == 0) ;
+
+	/* set SYSCLK to PLL */
+	writel(readl(RCC_CFGR) | 2, RCC_CFGR);
+	while ((readl(RCC_CFGR) & 8) == 0) ;
+}
+
+extern u8 __bss_start__;
+extern u8 __bss_end__;
+void bss_init() {
+	u32 *bss, *end;
+	bss = (void*) &__bss_start__;
+	end = (void*) &__bss_end__;
+	while (bss < end)
+		*bss++ = 0;
+}
+
+#define DEBUG_MAX 52
+static struct {
+	u32 txn;
+	u32 cmd;
+	u8 data[DEBUG_MAX];
+} pmsg = {
+	.txn = RSWD_TXN_ASYNC,
+	.cmd = RSWD_MSG(CMD_DEBUG_PRINT, 0, DEBUG_MAX/4),
+};
+static unsigned poff = 0;
+
+void flushu(void) {
+	while (poff < DEBUG_MAX)
+		pmsg.data[poff++] = 0;
+	usb_xmit(&pmsg, sizeof(pmsg));
+	poff = 0;
+}
+
+void _putu(unsigned c) {
+	pmsg.data[poff++] = c;
+	if (poff == DEBUG_MAX)
+		flushu();
+}
+
+void printu(const char *fmt, ...) {
+	va_list ap;
+	va_start(ap, fmt);
+	vprintx(_putu, fmt, ap);
+	va_end(ap);
+	if (poff)
+		flushu();
+}
+
+static u8 optable[16] = {
+	[OP_RD | OP_DP | OP_X0] = RD_IDCODE,
+	[OP_RD | OP_DP | OP_X4] = RD_DPCTRL,
+	[OP_RD | OP_DP | OP_X8] = RD_RESEND,
+	[OP_RD | OP_DP | OP_XC] = RD_BUFFER,
+	[OP_WR | OP_DP | OP_X0] = WR_ABORT,
+	[OP_WR | OP_DP | OP_X4] = WR_DPCTRL,
+	[OP_WR | OP_DP | OP_X8] = WR_SELECT,
+	[OP_WR | OP_DP | OP_XC] = WR_BUFFER,
+	[OP_RD | OP_AP | OP_X0] = RD_AP0,
+	[OP_RD | OP_AP | OP_X4] = RD_AP1,
+	[OP_RD | OP_AP | OP_X8] = RD_AP2,
+	[OP_RD | OP_AP | OP_XC] = RD_AP3,
+	[OP_WR | OP_AP | OP_X0] = WR_AP0,
+	[OP_WR | OP_AP | OP_X4] = WR_AP1,
+	[OP_WR | OP_AP | OP_X8] = WR_AP2,
+	[OP_WR | OP_AP | OP_XC] = WR_AP3,
+};
+
+/* TODO bounds checking -- we trust the host far too much */
+void process_txn(u32 txnid, u32 *rx, int rxc, u32 *tx) {
+	unsigned msg, op, n;
+	unsigned txc = 1;
+	unsigned count = 0;
+	unsigned status = 0;
+	void (*func)(void) = 0;
+
+	tx[0] = txnid;
+
+	while (rxc-- > 0) {
+		count++;
+		msg = *rx++;
+		op = RSWD_MSG_OP(msg);
+		n = RSWD_MSG_ARG(msg);
+#if TRACE
+		printx("> %b %b %h <\n", RSWD_MSG_CMD(msg), op, n);
+#endif
+		switch (RSWD_MSG_CMD(msg)) {
+		case CMD_NULL:
+			continue;
+		case CMD_SWD_WRITE:
+			while (n-- > 0) {
+				rxc--;
+				if (swdp_write(optable[op], *rx++)) {
+					status = 3;
+					goto done;
+				}
+			}
+			continue;
+		case CMD_SWD_READ:
+			tx[txc++] = RSWD_MSG(CMD_SWD_DATA, 0, n); 
+			while (n-- > 0) {
+				if (swdp_read(optable[op], tx + txc)) {
+					txc++;
+					while (n-- > 0)
+						tx[txc++] = 0xfefefefe;
+					status = 3;
+					goto done;
+				}
+				txc++;
+			}
+			continue;
+		case CMD_SWD_DISCARD:
+			while (n-- > 0) {
+				u32 tmp;
+				if (swdp_read(optable[op], &tmp)) {
+					status = 3;
+					goto done;
+				}
+			}
+			continue;
+		case CMD_ATTACH:
+			swdp_reset();
+			continue;
+		case CMD_RESET:
+			if (n == 0) {
+				/* deassert RESET */
+				gpio_config(GPIO_RESET_N,
+					GPIO_INPUT | GPIO_FLOATING);
+			} else {
+				/* assert RESET */
+				gpio_clr(GPIO_RESET_N);
+				gpio_config(GPIO_RESET_N,
+					GPIO_OUTPUT_10MHZ | GPIO_ALT_PUSH_PULL);
+			}
+			continue;
+		case CMD_DOWNLOAD: {
+			u32 *addr = (void*) *rx++;
+			rxc--;
+			while (n) {
+				*addr++ = *rx++;
+				rxc--;
+			}
+			continue;
+		}
+		case CMD_EXECUTE:
+			func = (void*) *rx++;
+			rxc--;
+			continue;
+		case CMD_TRACE:
+			swdp_trace = op;
+			continue;
+		default:
+			printx("unknown command %b\n", RSWD_MSG_CMD(msg));
+			status = 1;
+			goto done;
+		}
+	}
+
+done:
+	tx[txc++] = RSWD_MSG(CMD_STATUS, status, count);
+	if ((txc & 0x3f) == 0)
+		tx[txc++] = RSWD_MSG(CMD_NULL, 0, 0);
+
+#if TRACE
+	printx("[ send %x words ]\n", txc);
+	for (n = 0; n < txc; n+=4) {
+		printx("%x %x %x %x\n",
+			tx[n], tx[n+1], tx[n+2], tx[n+3]);
+	}
+#endif
+	usb_xmit(tx, txc * 4);
+
+	if (func) {
+		func();
+		for (;;) ;
+	}	
+}
+
+static u32 rxbuffer[1024];
+static u32 txbuffer[1024];
+
+int main() {
+	int rxc;
+
+	writel(readl(RCC_APB2ENR) |
+		RCC_APB2_GPIOA | RCC_APB2_USART1,
+		RCC_APB2ENR);
+
+	gpio_config(9, GPIO_OUTPUT_10MHZ | GPIO_ALT_PUSH_PULL);
+	gpio_config(GPIO_LED, GPIO_OUTPUT_10MHZ | GPIO_OUT_PUSH_PULL);
+
+	clocks_to_72mhz();
+	bss_init();
+	serial_init(72000000, 115200);
+	printx("[ rswdp agent v0.9 ]\n");
+	printx("[ built " __DATE__ " " __TIME__ " ]\n");
+
+#if 0
+	irq_set_base(0x20001000);
+	irq_enable(i_usb_lp);
+	irq_enable(i_usb_hp);
+#endif
+
+	usb_init(0x18d1, 0xdb03, 0, 0);
+
+	for (;;) {
+		gpio_clr(GPIO_LED);
+		rxc = usb_recv(rxbuffer, sizeof(rxbuffer));
+		gpio_set(GPIO_LED);
+
+#if TRACE
+		int n;
+		printx("[ recv %x words ]\n", rxc/4);
+		for (n = 0; n < (rxc/4); n+=4) {
+			printx("%x %x %x %x\n",
+				rxbuffer[n], rxbuffer[n+1], rxbuffer[n+2], rxbuffer[n+3]);
+		}
+#endif
+
+		if ((rxc < 4) || (rxc & 3)) {
+			printx("error, runt frame, or strange frame... %x\n", rxc);
+			continue;
+		}
+
+		rxc = rxc / 4;
+
+		if ((rxbuffer[0] & 0xFFFF0000) != 0xAA770000) {
+			printx("invalid frame %x\n", rxbuffer[0]);
+			continue;
+		}
+
+		process_txn(rxbuffer[0], rxbuffer + 1, rxc - 1, txbuffer);
+	}
+}
diff --git a/swdp/module.mk b/swdp/module.mk
@@ -0,0 +1,9 @@
+$(call start-module-mk)
+
+M_NAME := swdp
+M_CHIP := stm32f103-rom
+M_OBJS := swdp/main.o
+M_OBJS += swdp/swdp.o
+M_OBJS += libfw/print.o
+M_OBJS += libfw/serialconsole.o
+$(call build-target-executable)
diff --git a/swdp/swdp.c b/swdp/swdp.c
@@ -0,0 +1,187 @@
+/* swdp.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <fw/types.h>
+#include <fw/lib.h>
+#include <fw/io.h>
+
+#include <arch/hardware.h>
+
+#include <protocol/rswdp.h>
+#include "swdp.h"
+
+unsigned swdp_trace = 0;
+
+void printu(const char *fmt, ...);
+
+/* NOTES
+ * host -> device:
+ *   host writes DATA on falling edge of CLOCK
+ *   device reads DATA on rising edge of CLOCK
+ * device -> host
+ *   device writes DATA on rising edge of CLOCK
+ *   host samples DATA on falling edge of CLOCK
+ * host parks (begins turnaround) by:
+ *   releasing bus between falling and rising clock
+ *   a turnaround cycle follows, in which the device does its first write on CK+
+ * host unparks (reclaims bus) by:
+ *   reasserting bus between falling and rising clock
+ */
+
+#define GPIO_CLK 1
+#define GPIO_DAT 0
+
+#define D0 (1 << (GPIO_DAT + 16))
+#define D1 (1 << GPIO_DAT)
+#define C0 (1 << (GPIO_CLK + 16))
+#define C1 (1 << (GPIO_CLK))
+
+#define GPIOA (GPIOA_BASE + GPIO_BSR)
+
+#define XMIT(data) writel(data | C0, GPIOA), writel(data | C1, GPIOA)
+
+#define D_OUT() gpio_config(GPIO_DAT, GPIO_OUTPUT_10MHZ | GPIO_OUT_PUSH_PULL)
+#define D_IN() gpio_config(GPIO_DAT, GPIO_INPUT | GPIO_PU_PD)
+
+static unsigned recv(unsigned count) {
+	unsigned n = 0;
+	unsigned bit = 1;
+
+	while (count-- > 0) {
+		writel(D1 | C0, GPIOA);
+		if (readl(GPIOA_BASE + GPIO_IDR) & (1 << GPIO_DAT))
+			n |= bit;
+		bit <<= 1;
+		writel(D1 | C1, GPIOA);
+	}
+	return n;
+}
+
+static void send(unsigned n, unsigned count) {
+	unsigned p = 0;
+	while (count-- > 0) {
+		p ^= (n & 1);
+		if (n & 1) {
+			XMIT(D1);
+		} else {
+			XMIT(D0);
+		}	
+		n >>= 1;
+	}
+	if (p) {
+		XMIT(D1);
+	} else {
+		XMIT(D0);
+	}
+}
+
+static void clock_high(int n) {
+	while (n-- > 0)
+		XMIT(D1);
+}
+static void clock_low(int n) {
+	while (n-- > 0)
+		XMIT(D0);
+}
+static void clock_jtag2swdp() {
+	XMIT(D0); XMIT(D1); XMIT(D1); XMIT(D1);
+	XMIT(D1); XMIT(D0); XMIT(D0); XMIT(D1);
+	XMIT(D1); XMIT(D1); XMIT(D1); XMIT(D0);
+	XMIT(D0); XMIT(D1); XMIT(D1); XMIT(D1);
+}
+
+static void puth(unsigned hdr, unsigned n, unsigned v) {
+	printu("%s %s %b %s %x\n",
+		(hdr & 0x20) ? "RD" : "WR",
+		(hdr & 0x40) ? "AP" : "DP",
+		(hdr >> 3) & 3,
+		(n == 1) ? "OK" : "XX",
+		v);
+}
+
+int swdp_read(unsigned hdr, unsigned *v) {
+	unsigned n,m,o;
+
+	gpio_clr(2);
+	for (n = 0; n < 8; n++) {
+		if (hdr & 0x80) {
+			XMIT(D1);
+		} else {
+			XMIT(D0);
+		}
+		hdr <<= 1;
+	}
+	D_IN();
+	XMIT(D1); // turnaround
+	
+	n = recv(3);
+	m = recv(32);
+	o = recv(1);
+	D_OUT();
+	XMIT(D1); // turnaround
+	clock_low(8);
+	gpio_set(2);
+
+	if (swdp_trace || (n != 1))
+		puth(hdr >> 8, n, m);
+
+	*v = m;
+	return (n == 1) ? 0 : -1;
+}
+
+int swdp_write(unsigned hdr, unsigned val) {
+	unsigned n;
+
+	for (n = 0; n < 8; n++) {
+		if (hdr & 0x80) {
+			XMIT(D1);
+		} else {
+			XMIT(D0);
+		}
+		hdr <<= 1;
+	}
+	D_IN();
+	XMIT(D1); // turnaround
+	
+	n = recv(3);
+	D_OUT();
+	XMIT(D1);
+	send(val, 32);
+	clock_low(8);
+
+	if (swdp_trace || (n != 1))
+		puth(hdr >> 8, n, val);
+
+	return (n == 1) ? 0 : -1;
+}
+
+void swdp_reset(void) {
+	gpio_set(GPIO_CLK);
+	gpio_set(GPIO_DAT);
+	gpio_config(GPIO_CLK, GPIO_OUTPUT_10MHZ | GPIO_OUT_PUSH_PULL);
+	gpio_config(GPIO_DAT, GPIO_OUTPUT_10MHZ | GPIO_OUT_PUSH_PULL);
+
+	/* tracing */
+	gpio_set(2);
+	gpio_config(2, GPIO_OUTPUT_2MHZ | GPIO_OUT_PUSH_PULL);
+
+	clock_high(64);
+	clock_jtag2swdp();
+	clock_high(64);
+	clock_low(8);
+}
+
diff --git a/swdp/swdp.h b/swdp/swdp.h
@@ -0,0 +1,53 @@
+/* swdp.h 
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _SWDP_H_
+#define _SWDP_H_
+
+void swdp_reset(void);
+int swdp_write(unsigned reg, unsigned val);
+int swdp_read(unsigned reg, unsigned *val);
+
+unsigned swdp_ap_write(unsigned reg, unsigned val);
+unsigned swdp_ap_read(unsigned reg);
+
+unsigned swdp_ahb_read(unsigned addr);
+void swdp_ahb_write(unsigned addr, unsigned value);
+
+/* swdp_read/write() register codes */
+#define RD_IDCODE	0xA5	// 10100101
+#define RD_DPCTRL	0xB1	// 10110001
+#define RD_RESEND	0xA9	// 10101001
+#define RD_BUFFER	0xBD	// 10111101
+
+#define WR_ABORT	0x81	// 10000001
+#define WR_DPCTRL	0x95	// 10010101
+#define WR_SELECT	0x8D	// 10001101
+#define WR_BUFFER	0x99	// 10011001
+
+#define RD_AP0		0xE1	// 11100001
+#define RD_AP1		0xF5	// 11110101
+#define RD_AP2		0xED	// 11101101
+#define RD_AP3		0xF9	// 11111001
+
+#define WR_AP0		0xC5	// 11000101
+#define WR_AP1		0xD1	// 11010001
+#define WR_AP2		0xC9	// 11001001
+#define WR_AP3		0xDD	// 11011101
+
+#endif
+
diff --git a/swdp/unused.c b/swdp/unused.c
@@ -0,0 +1,109 @@
+/* unused.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/* tidbits from when the device side was smarter */
+/* may be useful again someday */
+
+static unsigned prevaddr;
+
+static unsigned char ap_rr[4] = { RD_AP0, RD_AP1, RD_AP2, RD_AP3 };
+static unsigned char ap_wr[4] = { WR_AP0, WR_AP1, WR_AP2, WR_AP3 };
+
+unsigned swdp_ap_read(unsigned addr) {
+	if ((addr & 0xF0) != prevaddr) {
+		swdp_write(WR_SELECT, (addr & 0xF0) | (0 << 24));
+		prevaddr = addr & 0xF0;
+	}
+	swdp_read(ap_rr[(addr >> 2) & 3]);
+	return swdp_read(RD_BUFFER);
+}
+
+unsigned swdp_ap_write(unsigned addr, unsigned value) {
+	if ((addr & 0xF0) != prevaddr) {
+		swdp_write(WR_SELECT, (addr & 0xF0) | (0 << 24));
+		prevaddr = addr & 0xF0;
+	}
+	swdp_write(ap_wr[(addr >> 2) & 3], value);
+}
+
+void crap(void) {
+	/* invalidate AP address cache */
+	prevaddr = 0xFF;
+
+	printx("IDCODE= %x\n", idcode);
+
+	swdp_write(WR_ABORT, 0x1E); // clear any stale errors
+	swdp_write(WR_DPCTRL, (1 << 28) | (1 << 30)); // POWER UP
+	n = swdp_read(RD_DPCTRL);
+	printx("DPCTRL= %x\n", n);
+
+	/* configure for 32bit IO */
+	swdp_ap_write(AHB_CSW, AHB_CSW_MDEBUG | AHB_CSW_PRIV |
+		AHB_CSW_PRIV | AHB_CSW_DBG_EN | AHB_CSW_32BIT);
+
+	printx("ROMADDR= %x", swdp_ap_read(AHB_ROM_ADDR));
+}
+
+unsigned swdp_core_read(unsigned n) {
+	swdp_ahb_write(CDBG_REG_ADDR, n);
+	return swdp_ahb_read(CDBG_REG_DATA);
+}
+
+void swdp_core_halt(void) {
+	unsigned n = swdp_ahb_read(CDBG_CSR);
+	swdp_ahb_write(CDBG_CSR, 
+		CDBG_CSR_KEY | (n & 0x3F) |
+		CDBG_C_HALT | CDBG_C_DEBUGEN);
+}
+
+void swdp_core_resume(void) {
+	unsigned n = swdp_ahb_read(CDBG_CSR);
+	swdp_ahb_write(CDBG_CSR,
+		CDBG_CSR_KEY | (n & 0x3C));
+}
+
+unsigned swdp_ahb_read(unsigned addr) {
+	swdp_ap_write(AHB_TAR, addr);
+	return swdp_ap_read(AHB_DRW);
+}
+
+void swdp_ahb_write(unsigned addr, unsigned value) {
+	swdp_ap_write(AHB_TAR, addr);
+	swdp_ap_write(AHB_DRW, value);
+}
+
+void swdp_test(void) {
+	unsigned n;
+	swdp_reset();
+
+
+	for (n = 0x20000000; n < 0x20000020; n += 4) {
+		swdp_ap_write(AHB_TAR, n);
+		printx("%x: %x\n", n, swdp_ap_read(AHB_DRW));
+	}
+
+	swdp_ap_write(AHB_TAR, 0xE000EDF0);
+	swdp_ap_write(AHB_DRW, 0xA05F0003);
+
+	swdp_ap_write(AHB_TAR, 0x20000000);
+	swdp_ap_write(AHB_DRW, 0x12345678);
+	printx("DPCTRL= %x\n", swdp_read(RD_DPCTRL));
+	printx("DPCTRL= %x\n", swdp_read(RD_DPCTRL));
+	swdp_read(RD_BUFFER);
+	swdp_read(RD_BUFFER);
+}
+
diff --git a/tools/bless-lpc.c b/tools/bless-lpc.c
@@ -0,0 +1,43 @@
+/* debugger.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <fcntl.h>
+
+int main(int argc, char *argv[]) {
+	int fd;
+	uint32_t tmp, chk, n;
+
+	chk = 0;
+
+	if (argc != 2)
+		return -1;
+	if ((fd = open(argv[1], O_RDWR)) < 0)
+		return -1;
+	for (n = 0; n < 7; n++) {
+		if (read(fd, &tmp, 4) != 4)
+			return -1;
+		chk += tmp;
+	}
+	tmp = - chk;
+	n = write(fd, &tmp, 4);
+	close(fd);
+	return 0;
+}
diff --git a/tools/debugger-commands.c b/tools/debugger-commands.c
@@ -0,0 +1,560 @@
+/* debugger-commands.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include <fcntl.h>
+#include <sys/time.h>
+
+/* TODO
+ * - fault recovery (try dw 10000000 for example)
+ */
+
+#include <fw/types.h>
+#include <protocol/rswdp.h>
+#include "rswdp.h"
+
+#include "debugger.h"
+
+extern struct debugger_command debugger_commands[];
+
+long long now() {
+	struct timeval tv;
+	gettimeofday(&tv, 0);
+	return ((long long) tv.tv_usec) + ((long long) tv.tv_sec) * 1000000LL;
+}
+
+extern int disassemble_thumb2(u32 addr, u16 op0, u16 op1,
+		char *text, int len) __attribute__ ((weak));
+
+int disassemble(u32 addr) {
+	char text[128];
+	int r;
+	union {
+		u32 w[2];
+		u16 h[4];
+	} mem;
+
+#if WITH_THUMB2_DISASSEMBLE
+	if (!disassemble_thumb2)
+		return -1;
+
+	if (addr & 2) {
+		if (swdp_ahb_read32(addr & (~3), mem.w, 2))
+			return -1;
+		r = disassemble_thumb2(addr, mem.h[1], mem.h[2], text, 128);
+	} else {
+		if (swdp_ahb_read32(addr & (~3), mem.w, 1))
+			return -1;
+		r = disassemble_thumb2(addr, mem.h[0], mem.h[1], text, 128);
+	}
+	if (r > 0)
+		xprintf("%s\n", text);
+	return r;
+#else
+	return -1;
+#endif
+}
+
+int do_exit(int argc, param *argv) {
+	exit(0);
+	return 0;
+}
+
+int do_attach(int argc, param *argv) {
+	return swdp_reset();
+}
+
+static u32 lastregs[19];
+
+int do_regs(int argc, param *argv) {
+	if (swdp_core_read_all(lastregs))
+		return -1;
+
+	xprintf("r0 %08x r4 %08x r8 %08x ip %08x psr %08x\n",
+		lastregs[0], lastregs[4], lastregs[8],
+		lastregs[12], lastregs[16]);
+	xprintf("r1 %08x r5 %08x r9 %08x sp %08x msp %08x\n",
+		lastregs[1], lastregs[5], lastregs[9],
+		lastregs[13], lastregs[17]);
+	xprintf("r2 %08x r6 %08x 10 %08x lr %08x psp %08x\n",
+		lastregs[2], lastregs[6], lastregs[10],
+		lastregs[14], lastregs[18]);
+	xprintf("r3 %08x r7 %08x 11 %08x pc %08x\n",
+		lastregs[3], lastregs[7], lastregs[11],
+		lastregs[15]);
+	disassemble(lastregs[15]);
+	return 0;
+}
+
+int do_stop(int argc, param *argv) {
+	swdp_core_halt();
+	do_regs(0, 0);
+	return 0;
+}
+
+int do_resume(int argc, param *argv) {
+	swdp_core_resume();
+	if (swdp_core_wait_for_halt() == 0)
+		do_regs(0, 0);
+	return 0;
+}
+
+int do_step(int argc, param *argv) {
+	if (argc > 0) {
+		u32 pc;
+		do {
+			swdp_core_step();
+			swdp_core_wait_for_halt();
+			if (swdp_core_read(15, &pc)) {
+				xprintf("error\n");
+				return -1;
+			}
+			xprintf(".");
+			fflush(stdout);
+		} while (pc != argv[0].n);
+		xprintf("\n");
+	} else {
+		swdp_core_step();
+		swdp_core_wait_for_halt();
+	}
+	do_regs(0, 0);
+	return 0;
+}
+
+struct {
+	const char *name;
+	unsigned n;
+} core_regmap[] = {
+	{ "r0", 0 },
+	{ "r1", 1 },
+	{ "r2", 2 },
+	{ "r3", 3 },
+	{ "r4", 4 },
+	{ "r5", 5 },
+	{ "r6", 6 },
+	{ "r7", 7 },
+	{ "r8", 8 },
+	{ "r9", 9 },
+	{ "r10", 10 },
+	{ "r11", 11 },
+	{ "r12", 12 },
+	{ "r13", 13 }, { "sp", 13 },
+	{ "r14", 14 }, { "lr", 14 },
+	{ "r15", 15 }, { "pc", 15 },
+	{ "psr", 16 },
+	{ "msp", 17 },
+	{ "psp", 18 },
+};
+
+int read_memory_word(u32 addr, u32 *value) {
+	return swdp_ahb_read(addr, value);
+}
+
+int read_register(const char *name, u32 *value) {
+	int n;
+	for (n = 0; n < (sizeof(core_regmap) / sizeof(core_regmap[0])); n++) {
+		if (!strcasecmp(name, core_regmap[n].name)) {
+			if (swdp_core_read(core_regmap[n].n, value))
+				return -1;
+			return 0;
+		}
+	}
+	return ERROR_UNKNOWN;
+}
+
+int do_dr(int argc, param *argv) {
+	unsigned n;
+	u32 x = 0xeeeeeeee;
+	if (argc < 1)
+		return -1;
+	for (n = 0; n < (sizeof(core_regmap) / sizeof(core_regmap[0])); n++) {
+		if (!strcasecmp(argv[0].s, core_regmap[n].name)) {
+			swdp_core_read(core_regmap[n].n, &x);
+			xprintf("%s: %08x\n", argv[0].s, x);
+			return 0;
+		}
+	}
+	swdp_ahb_read(argv[0].n, &x);
+	xprintf("%08x: %08x\n", argv[0].n, x);
+	return 0;
+}
+
+int do_wr(int argc, param *argv) {
+	unsigned n;
+	if (argc < 2)
+		return -1;
+	for (n = 0; n < (sizeof(core_regmap) / sizeof(core_regmap[0])); n++) {
+		if (!strcasecmp(argv[0].s, core_regmap[n].name)) {
+			swdp_core_write(core_regmap[n].n, argv[1].n);
+			xprintf("%s<<%08x\n", argv[0].s, argv[1].n);
+			return 0;
+		}
+	}
+	swdp_ahb_write(argv[0].n, argv[1].n);
+	xprintf("%08x<<%08x\n", argv[0].n, argv[1].n);
+	return 0;
+}
+
+int do_text(int argc, param *argv) {
+	u8 data[1024], *x;
+	u32 addr;
+
+	if (argc < 1)
+		return -1;
+	addr = argv[0].n;
+	memset(data, 0, sizeof(data));
+	
+	if (swdp_ahb_read32(addr, (void*) data, sizeof(data)/4))
+		return -1;
+
+	data[sizeof(data)-1] = 0;
+	for (x = data; *x; x++) {
+		if ((*x >= ' ') && (*x < 128))
+			continue;
+		if (*x == '\n')
+			continue;
+		*x = '.';
+	}
+	fprintf(stderr,"%s\n", (char*) data);
+	return 0;
+}
+
+static u32 lastaddr = 0x20000000;
+static u32 lastcount = 0x40;
+
+int do_dw(int argc, param *argv) {
+	u32 data[4096];
+	u32 addr = lastaddr;
+	u32 count = lastcount;
+	unsigned n;
+
+	if (argc > 0) addr = argv[0].n;
+	if (argc > 1) count = argv[1].n;
+
+	memset(data, 0xee, 256 *4);
+
+	/* word align */
+	addr = (addr + 3) & ~3;
+	count = (count + 3) & ~3;
+	if (count > sizeof(data))
+		count = sizeof(data);
+
+	lastaddr = addr + count;
+	lastcount = count;
+
+	count /= 4;
+	if (swdp_ahb_read32(addr, data, count))
+		return -1;
+	for (n = 0; n < count; n++) {
+		if ((n & 3) == 0)
+			xprintf("\n%08x:", addr + (n << 2));
+		xprintf(" %08x", data[n]);
+	}
+	xprintf("\n");
+	return 0;
+}
+
+
+int do_db(int argc, param *argv) {
+	u32 addr, count;
+	u8 data[1024];
+	unsigned n;
+
+	if (argc < 2)
+		return -1;
+
+	addr = argv[0].n;
+	count = argv[1].n;
+
+	if (count > 1024)
+		count = 1024;
+
+	memset(data, 0xee, 1024);
+
+	swdp_ahb_write(AHB_CSW, AHB_CSW_MDEBUG | AHB_CSW_PRIV |
+		AHB_CSW_DBG_EN | AHB_CSW_8BIT);
+	for (n = 0; n < count; n++) {
+		u32 tmp;
+		if (swdp_ahb_read(addr + n, &tmp)) {
+			swdp_reset();
+			break;
+		}
+		data[n] = tmp >> (8 * (n & 3));
+	}
+
+	swdp_ahb_write(AHB_CSW, AHB_CSW_MDEBUG | AHB_CSW_PRIV |
+		AHB_CSW_DBG_EN | AHB_CSW_32BIT);
+
+	for (n = 0; n < count; n++) {
+		if ((n & 15) == 0)
+			xprintf("\n%08x:", addr + n);
+		xprintf(" %02x", data[n]);
+	}
+	xprintf("\n");
+	return 0;
+}
+
+int do_download(int argc, param *argv) {
+	u32 addr;
+	u8 data[512*1024];
+#if 0
+	u8 vrfy[512*1024];
+#endif
+	int fd, r;
+
+	long long t0, t1;
+
+	if (argc != 2) {
+		xprintf("error: usage: download <file> <addr>\n");
+		return -1;
+	}
+
+	fd = open(argv[0].s, O_RDONLY);
+	r = read(fd, data, sizeof(data));
+	if ((fd < 0) || (r < 0)) {
+		xprintf("error: cannot read '%s'\n", argv[0].s);
+		return -1;
+	}
+	r = (r + 3) & ~3;
+	addr = argv[1].n;
+
+	xprintf("sending %d bytes...\n", r);
+	t0 = now();
+	if (swdp_ahb_write32(addr, (void*) data, r / 4)) {
+		xprintf("error: failed to write data\n");
+		return -1;
+	}
+	t1 = now();
+	xprintf("%lld uS -> %lld B/s\n", (t1 - t0), 
+		(((long long)r) * 1000000LL) / (t1 - t0));
+
+#if 0
+	t0 = now();
+	if (swdp_ahb_read32(addr, (void*) vrfy, r / 4)) {
+		xprintf("error: verify read failed\n");
+		return -1;
+	}
+	t1 = now();
+	xprintf("%lld uS. %lld B/s.\n", (t1 - t0), 
+		(((long long)r) * 1000000LL) / (t1 - t0));
+	if (memcmp(data,vrfy,r)) {
+		xprintf("error: verify failed\n");
+		return -1;
+	}
+#endif
+	return 0;
+}
+
+int do_flash(int argc, param *argv) {
+	u32 addr, xfer, size;
+	u32 flash_buffer, flash_start, flash_size, flash_block;
+	u8 data[512*1024], *p;
+	int fd, r;
+
+	if (argc != 2) {
+		xprintf("error: usage: flash <file> <addr>\n");
+		return -1;
+	}
+
+	if (debugger_variable("flash-buffer", &flash_buffer) ||
+		debugger_variable("flash-start", &flash_start) ||
+		debugger_variable("flash-size", &flash_size) ||
+		debugger_variable("flash-block", &flash_block)) {
+		xprintf("error: flash script missing or invalid\n");
+		return -1;
+	}
+
+	addr = argv[1].n;
+
+	if (addr & (flash_block - 1)) {
+		xprintf("error: address unaligned with blocksize 0x%08x\n",
+			flash_block);
+		return -1;
+	}
+	if (addr < flash_start) {
+		xprintf("error: start address less than flash start (0x%08x)\n",
+			flash_start);
+		return -1;
+	}
+
+	/* adjust available size based on offset */
+	flash_size -= (addr - flash_start);
+	
+	memset(data, 0xff, sizeof(data));
+
+	fd = open(argv[0].s, O_RDONLY);
+	r = read(fd, data, sizeof(data));
+	if ((fd < 0) || (r < 0)) {
+		xprintf("error: cannot read '%s'\n", argv[0].s);
+		return -1;
+	}
+	r = (r + 3) & ~3;
+	size = r;
+	if (size > flash_size) {
+		xprintf("error: %d bytes does not fit in %d byte flash region\n",
+			size, flash_size);
+		return -1;
+	}
+
+	if (debugger_invoke("flash-setup", 0)) {
+		xprintf("error: flash setup failed (@0x%08x)\n", addr);
+		return -1;
+	}
+	p = data;
+	while (size > 0) {
+		xfer = (size > flash_block) ? flash_block : size;
+		if (swdp_ahb_write32(flash_buffer, (void*) p, xfer / 4)) {
+			xprintf("error: failed to write data\n");
+			return -1;
+		}
+		if (debugger_invoke("flash-erase", 1, addr)) {
+			xprintf("error: flash erase failed (@0x%08x)\n", addr);
+			return -1;
+		}
+		if (debugger_invoke("flash-write", 1, addr)) {
+			xprintf("error: flash write failed (@0x%08x)\n", addr);
+			return -1;
+		}
+		addr += xfer;
+		size -= xfer;
+		p += xfer;
+	}
+	return 0;
+}
+
+
+
+int do_reset(int argc, param *argv) {
+	swdp_core_halt();	
+	swdp_ahb_write(CDBG_EMCR, 0);
+	/* core reset and sys reset */
+	swdp_ahb_write(0xe000ed0c, 0x05fa0005);
+	swdp_ahb_write(CDBG_EMCR, 0);
+#if 0
+	if (argc > 0) {
+		swdp_target_reset(argv[0].n);
+	} else {
+		swdp_target_reset(1);
+		usleep(10000);
+		swdp_target_reset(0);
+	}
+#endif
+	return 0;
+}
+
+int do_reset_stop(int argc, param *argv) {
+	swdp_core_halt();
+	swdp_ahb_write(CDBG_EMCR, 1);
+#if 1
+	/* core reset and sys reset */
+	swdp_ahb_write(0xe000ed0c, 0x05fa0005);
+#else
+	swdp_target_reset(1);
+	usleep(10000);
+	swdp_target_reset(0);
+	usleep(10000);
+#endif
+	do_stop(0,0);
+	swdp_ahb_write(CDBG_EMCR, 0);
+	return 0;
+}
+
+int do_watch_pc(int argc, param *argv) {
+	if (argc < 1)
+		return -1;
+	return swdp_watchpoint_pc(0, argv[0].n);
+}
+
+int do_watch_rw(int argc, param *argv) {
+	if (argc < 1)
+		return -1;
+	return swdp_watchpoint_rw(0, argv[0].n);
+}
+
+int do_print(int argc, param *argv) {
+	while (argc-- > 0)
+		xprintf("%08x ", argv++[0].n);
+	xprintf("\n");
+	return 0;
+}
+
+int do_echo(int argc, param *argv) {
+	while (argc-- > 0) {
+		unsigned int argn = argv[0].n;
+		const char *arg = argv++[0].s;
+
+		if (arg[0] == '$') {
+			xprintf("%08x ", argn);
+		} else {
+			xprintf("%s ", arg, argn);
+		}
+	}
+	xprintf("\n");
+	return 0;
+}
+
+int do_bootloader(int argc, param *argv) {
+	return swdp_bootloader();
+}
+
+int do_setclock(int argc, param *argv) {
+	if (argc < 1)
+		return -1;
+	return swdp_set_clock(argv[0].n);
+}
+
+int do_help(int argc, param *argv) {
+	struct debugger_command *cmd;
+	for (cmd = debugger_commands; cmd->func != NULL; cmd++) {
+		xprintf("%-16s: %s\n", cmd->name, cmd->help);
+	}
+
+	return 0;
+}
+
+struct debugger_command debugger_commands[] = {
+	{ "exit",	"", do_exit,	"" },
+	{ "attach",	"", do_attach,	"attach/reattach to sw-dp" },
+	{ "regs",	"", do_regs,	"show cpu registers" },
+	{ "stop",	"", do_stop,	"halt cpu" },
+	{ "step",	"", do_step,	"single-step cpu" },
+	{ "go",		"", do_resume,	"resume cpu" },
+	{ "dw",		"", do_dw,	"dump words" },
+	{ "db",		"", do_db,	"dump bytes" },
+	{ "dr",		"", do_dr,	"dump register" },
+	{ "wr",		"", do_wr,	"write register" },
+	{ "download",	"", do_download,"download file to device" },
+	{ "flash",	"", do_flash,	"write file to device flash" },
+	{ "reset",	"", do_reset,	"reset target" },
+	{ "reset-stop",	"", do_reset_stop, "reset target and halt cpu" },
+	{ "watch-pc",	"", do_watch_pc, "set watchpoint at addr" },
+	{ "watch-rw",	"", do_watch_rw, "set watchpoint at addr" },
+	{ "print",	"", do_print,	"print numeric arguments" },
+	{ "echo",	"", do_echo, "echo command line" },
+	{ "bootloader", "", do_bootloader, "reboot into bootloader" },
+	{ "setclock",	"", do_setclock, "set clock rate (khz)" },
+	{ "text",	"", do_text, "dump text" },
+	{ "help",	"", do_help, "help" },
+	{ 0, 0, 0, 0 },
+};
+
diff --git a/tools/debugger-core.c b/tools/debugger-core.c
@@ -0,0 +1,386 @@
+/* debugger-core.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdarg.h>
+
+#include <fw/types.h>
+#include "debugger.h"
+
+static struct varinfo *all_variables = 0;
+static struct funcinfo *allfuncs = 0;
+
+static int frame_argc = 0;
+static param *frame_argv = 0;
+
+static void variable_set(const char *name, u32 value) {
+	struct varinfo *vi;
+	int len;
+	for (vi = all_variables; vi; vi = vi->next) {
+		if (!strcmp(name, vi->name)) {
+			vi->value = value;
+			return;
+		}
+	}
+	len = strlen(name) + 1;
+	vi = malloc(sizeof(*vi) + len);
+	memcpy(vi->name, name, len);
+	vi->value = value;
+	vi->next = all_variables;
+	all_variables = vi;
+}
+
+static int variable_get(const char *name, u32 *value) {
+	struct varinfo *vi;
+	for (vi = all_variables; vi; vi = vi->next) {
+		if (!strcmp(name, vi->name)) {
+			*value = vi->value;
+			return 0;
+		}
+	}
+	return -1;
+}
+
+int debugger_variable(const char *name, u32 *value) {
+	return variable_get(name, value);
+}
+
+static int do_script(int argc, param *argv) {
+	FILE *fp;
+	char *line,linebuf[256];
+	struct funcinfo *newfunc = 0;
+	struct funcline *lastline = 0;
+	const char *fname = argv[0].s;
+
+	if (argc != 1)
+		return -1;
+
+	if (!strcmp(fname, "-")) {
+		fp = stdin;
+	} else {
+		if (!(fp = fopen(argv[0].s, "r"))) {
+			xprintf("error: cannot open '%s'\n", argv[0].s);
+			return -1;
+		}
+	}
+
+	while (fgets(linebuf, sizeof(linebuf), fp)) {
+		line = linebuf;
+		while (isspace(*line))
+			line++;
+		if (iscntrl(line[0]) || line[0] == '#')
+			continue;
+		if (!strncmp(line, "function", 8) && isspace(line[8])) {
+			char *name, *x;
+			name = line + 9;
+			if (newfunc) {
+				xprintf("error: nested functions not allowed\n");
+				break;
+			}
+			while (isspace(*name))
+				name++;
+			x = name;
+			while (!isspace(*x) && *x)
+				x++;
+			*x = 0;
+			if (*name == 0) {
+				xprintf("error: functions must have names\n");
+				break;
+			}
+			newfunc = malloc(sizeof(*newfunc) + strlen(name) + 1);
+			if (newfunc == 0) {
+				xprintf("error: out of memory\n");
+				break;
+			}
+			strcpy(newfunc->name, name);
+			newfunc->next = 0;
+			newfunc->lines = 0;
+			continue;
+		}
+		if (newfunc) {
+			struct funcline *fl;
+			if (!strncmp(line, "end", 3) && isspace(line[3])) {
+				newfunc->next = allfuncs;
+				allfuncs = newfunc;
+				newfunc = 0;
+				lastline = 0;
+				continue;
+			}
+			fl = malloc(sizeof(*fl) + strlen(line) + 1);
+			if (fl == 0) {
+				xprintf("out of memory");
+				newfunc = 0;
+				if (fp != stdin)
+					fclose(fp);
+				return -1;
+			}
+			strcpy(fl->text, line);
+			fl->next = 0;
+			if (lastline) {
+				lastline->next = fl;
+			} else {
+				newfunc->lines = fl;
+			}
+			lastline = fl;
+		} else {
+			xprintf("script> %s", line);
+			if (debugger_command(line))
+				return -1;
+		}
+	}
+	if (fp != stdin)
+		fclose(fp);
+
+	if (newfunc)
+		newfunc = 0;
+	return 0;
+}
+
+static int do_set(int argc, param *argv) {
+	const char *name;
+	if ((argc != 2) && (argc != 4)) {
+		xprintf("error: set requires two or four arguments\n");
+		return -1;
+	}
+	name = argv[0].s;
+	if (*name == '$')
+		name++;
+	if (*name == 0) {
+		xprintf("error: empty name?!\n");
+		return -1;
+	}
+	if (!isalpha(*name)) {
+		xprintf("error: variable name must begin with a letter\n");
+		return -1;
+	}
+
+	if (argc == 4) {
+		u32 a, b, n;
+		const char *op;
+		a = argv[1].n;
+ 		op = argv[2].s;
+		b = argv[3].n;
+		if (!strcmp(op,"+")) {
+			n = a + b;
+		} else if (!strcmp(op, "-")) {
+			n = a - b;
+		} else if (!strcmp(op, "<<")) {
+			n = a << b;
+		} else if (!strcmp(op, ">>")) {
+			n = a >> b;
+		} else if (!strcmp(op, "*")) {
+			n = a * b;
+		} else if (!strcmp(op, "/")) {
+			if (b == 0) {
+				n = 0;
+			} else {
+				n = a / b;
+			}
+		} else {
+			xprintf("error: set <var> <a> <op> <b> requires op: + - * / << >>\n");
+			return -1;
+		}
+		variable_set(name, n);
+	} else {	
+		variable_set(name, argv[1].n);
+	}
+	return 0;
+}
+
+static int parse_number(const char *in, unsigned *out) {
+	u32 value;
+	char text[64];
+	char *obrack;
+	int r;
+
+	strncpy(text, in, sizeof(text));
+	text[sizeof(text)-1] = 0;
+
+	/* handle dereference forms */
+	obrack = strchr(text, '[');
+	if (obrack) {
+		unsigned base, index;
+		char *cbrack;
+		*obrack++ = 0;
+		cbrack = strchr(obrack, ']');
+		if (!cbrack)
+			return -1;
+		*cbrack = 0;
+		if (parse_number(text, &base))
+			return -1;
+		if (parse_number(obrack, &index))
+			return -1;
+		if (read_memory_word(base + index, &value))
+			return -1;
+		*out = value;
+		return 0;	
+	}
+
+	/* handle local $[0..9] and global $... variables */
+	if (text[0] == '$') {
+		if (isdigit(text[1]) && (text[2] == 0)) {
+			r = atoi(text + 1);
+			if (r > 0) {
+				if (r <= frame_argc) {
+					*out = frame_argv[r - 1].n;
+					return 0;
+				}
+			}
+			xprintf("no local variable %s\n", text);
+			*out = 0;
+			return 0;
+		}
+		if (variable_get(text + 1, &value) == 0) {
+			*out = value;
+		} else {
+			xprintf("undefined variable '%s'\n", text + 1);
+			*out = 0;
+		}
+		return 0;
+	}
+
+	/* handle registers */
+	r = read_register(text, &value);
+	if (r != ERROR_UNKNOWN) {
+		*out = value;
+		return r;
+	}
+
+	/* otherwise decimal or hex constants */
+	if (text[0] == '.') {
+		*out = strtoul(text + 1, 0, 10);
+	} else {
+		*out = strtoul(text, 0, 16);
+	}
+	return 0;
+}
+
+static int exec_function(struct funcinfo *f, int argc, param *argv) {
+	param *saved_argv;
+	int saved_argc;
+	struct funcline *line;
+	char text[256];
+	int r, n;
+
+	saved_argv = frame_argv;
+	saved_argc = frame_argc;
+	frame_argv = argv;
+	frame_argc = argc;
+
+	for (line = f->lines, n = 1; line; line = line->next, n++) {
+		strcpy(text, line->text);
+		r = debugger_command(text);
+		if (r) {
+			xprintf("error: %s: line %d\n", f->name, n);
+			goto done;
+		}
+	}
+	r = 0;
+done:
+	frame_argc = saved_argc;
+	frame_argv = saved_argv;
+	return r;
+}
+
+static int _debugger_exec(const char *cmd, unsigned argc, param *argv) {
+	struct funcinfo *f;
+	struct debugger_command *c;
+
+	/* core built-ins */
+	if (!strcasecmp(cmd, "set"))
+		return do_set(argc, argv);
+	if (!strcasecmp(cmd, "script"))
+		return do_script(argc, argv);
+
+	for (c = debugger_commands; c->name; c++) {
+		if (!strcasecmp(cmd, c->name)) {
+			return c->func(argc, argv);
+		}
+	}
+	for (f = allfuncs; f; f = f->next) {
+		if (!strcasecmp(cmd, f->name)) {
+			return exec_function(f, argc, argv);
+		}
+	}
+	return ERROR_UNKNOWN;
+}
+
+int debugger_invoke(const char *cmd, unsigned argc, ...) {
+	param arg[32];
+	unsigned n;
+	va_list ap;
+
+	if (argc > 31)
+		return -1;
+
+	va_start(ap, argc);
+	for (n = 0; n < argc; n++) {
+		arg[n].s = "";
+		arg[n].n = va_arg(ap, unsigned);
+	}
+	return _debugger_exec(cmd, argc, arg);
+}
+
+int debugger_command(char *line) {
+	param arg[32];
+	unsigned c, n = 0;
+	int r;
+
+	while ((c = *line)) {
+		if (c <= ' ') {
+			line++;
+			continue;
+		}
+		arg[n].s = line;
+		for (;;) {
+			if (c == 0) {
+				n++;
+				break;
+			} else if (c == '#') {
+				*line = 0;
+				break;
+			} else if (c <= ' ') {
+				*line++ = 0;
+				n++;
+				break;
+			}
+			c = *++line;
+		}
+	}
+
+	if (n == 0)
+		return 0;
+
+	for (c = 0; c < n; c++) {
+		if (parse_number(arg[c].s, &(arg[c].n))) {
+			xprintf("error: bad number: %s\n", arg[c].s);
+			return -1;
+		}
+	}
+
+	r = _debugger_exec(arg[0].s, n - 1, arg + 1);
+	if (r == ERROR_UNKNOWN) {
+		xprintf("unknown command: %s\n", arg[0].s);
+	}
+	return r;
+}
+
diff --git a/tools/debugger.c b/tools/debugger.c
@@ -0,0 +1,121 @@
+/* debugger.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <getopt.h>
+
+#include "linenoise.h"
+
+#include <fw/types.h>
+#include "rswdp.h"
+
+static const char *scriptfile = NULL;
+
+void xprintf(const char *fmt, ...) {
+	va_list ap;
+	va_start(ap, fmt);
+	vprintf(fmt, ap);
+	va_end(ap);
+}
+
+void debugger_command(char *line);
+
+static void handler(int n) {
+	swdp_interrupt();
+}
+
+static void usage(int argc, char **argv) {
+	fprintf(stderr, "usage: %s [-h] [-f script]\n", argv[0]);
+
+	exit(1);
+}
+
+int main(int argc, char **argv) {
+	char lastline[1024];
+	char *line;
+
+	/* args */
+	for(;;) {
+		int c;
+		int option_index = 0;
+
+		static struct option long_options[] = {
+			{"help", 0, 0, 'h'},
+			{"script", 1, 0, 'f'},
+			{0, 0, 0, 0},
+		};
+
+		c = getopt_long(argc, argv, "f:h", long_options, &option_index);
+		if(c == -1)
+			break;
+
+		switch(c) {
+			case 'f':
+				scriptfile = optarg;
+				break;
+			case 'h':
+				usage(argc, argv);
+				break;
+			default:
+				usage(argc, argv);
+				break;
+		}
+	}
+
+	lastline[0] = 0;
+
+	if (swdp_open()) {
+		fprintf(stderr,"could not find device\n");
+		return -1;
+	}
+
+	signal(SIGINT, handler);
+
+//	swdp_enable_tracing(1);
+
+	/*
+	 * if the user passed in a script file, pass it to the debug
+	 * command handler before starting the main loop
+	 */
+	if (scriptfile != NULL) {
+		char *buf = malloc(sizeof("script ") + strlen(scriptfile) + 1);
+
+		strcpy(buf, "script ");
+		strcat(buf, scriptfile);
+
+		debugger_command(buf);
+
+		free(buf);
+	}
+
+	while ((line = linenoise("debugger> ")) != NULL) {
+		if (line[0] == 0) {
+			strcpy(line, lastline);
+		} else {
+			linenoiseHistoryAdd(line);
+			strcpy(lastline, line);
+		}
+		debugger_command(line);
+	}
+	return 0;
+}
diff --git a/tools/debugger.h b/tools/debugger.h
@@ -0,0 +1,67 @@
+/* debugger.h
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _DEBUGGER_H_
+#define _DEBUGGER_H_
+
+#define printf __use_xprintf_in_debugger__
+extern void xprintf(const char *fmt, ...);
+
+#define ERROR		-1
+#define ERROR_UNKNOWN 	-2
+
+struct funcline {
+	struct funcline *next;
+	char text[0];
+};
+
+struct funcinfo {
+	struct funcinfo *next;
+	struct funcline *lines;
+	char name[0];
+};
+
+struct varinfo {
+	struct varinfo *next;
+	u32 value;
+	char name[0];
+};
+
+typedef struct {
+	const char *s;
+	unsigned n;
+} param;
+
+struct debugger_command {
+	const char *name;
+	const char *args;
+	int (*func)(int argc, param *argv);
+	const char *help;
+};
+
+/* provided by debugger-core.c */
+int debugger_command(char *line);
+int debugger_invoke(const char *cmd, unsigned argc, ...);
+int debugger_variable(const char *name, u32 *value);
+
+/* provided by debugger-commands.c */
+extern struct debugger_command debugger_commands[];
+int read_register(const char *name, u32 *value);
+int read_memory_word(u32 addr, u32 *value);
+
+#endif
+
diff --git a/tools/gdb-bridge.c b/tools/gdb-bridge.c
@@ -0,0 +1,286 @@
+/* gdb-bridge.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <fw/types.h>
+#include "rswdp.h"
+#include <protocol/rswdp.h>
+
+struct gdbcnxn {
+	int tx, rx;
+	unsigned chk;
+};
+
+int gdb_getc(struct gdbcnxn *gc) {
+	int r;
+	unsigned char c;
+	for (;;) {
+		r = read(gc->rx, &c, 1);
+		if (r <= 0) {
+			if (errno == EINTR)
+				continue;
+			return -1;
+		}
+		return c;
+	}
+}
+
+int gdb_putc(struct gdbcnxn *gc, unsigned n) {
+	unsigned char c = n;
+	int r;
+	gc->chk += c;
+	for (;;) {
+		r = write(gc->tx, &c, 1);
+		if (r <= 0) {
+			if (errno == EINTR)
+				continue;
+			return -1;
+		}
+		return 0;
+	}
+}
+int gdb_puts(struct gdbcnxn *gc, const char *s) {
+	int r;
+	while (*s) {
+		r = gdb_putc(gc, *s++);
+		if (r < 0) return r;
+	}
+	return 0;
+}
+int gdb_prologue(struct gdbcnxn *gc) {
+	int n = gdb_putc(gc, '$');
+	gc->chk = 0;
+	return n;
+}
+int gdb_epilogue(struct gdbcnxn *gc) {
+	char tmp[4];
+	sprintf(tmp,"#%02x", gc->chk & 0xff);
+	return gdb_puts(gc, tmp);
+}
+
+static char HEX[16] = "0123456789abcdef";
+int gdb_puthex(struct gdbcnxn *gc, void *ptr, unsigned len) {
+	unsigned char *data = ptr;
+	unsigned n;
+	char buf[1025];
+
+	n = 0;
+	while (len-- > 0) {
+		unsigned c = *data++;
+		buf[n++] = HEX[c >> 4];
+		buf[n++] = HEX[c & 15];
+		if (n == 1024) {
+			buf[n] = 0;
+			if (gdb_puts(gc, buf))
+				return -1;
+			n = 0;
+		}
+	}
+	if (n) {
+		buf[n] = 0;
+		return gdb_puts(gc, buf);
+	}
+	return 0;
+}
+
+int gdb_recv(struct gdbcnxn *gc, char *buf, int max) {
+	char *start = buf;
+	unsigned chk;
+	char tmp[3];
+	int c;
+
+again:
+	do {
+		c = gdb_getc(gc);
+		if (c < 0) goto fail;
+		if (c == 3) {
+			buf[0] = 's';
+			buf[1] = 0;
+			return 0;
+		}
+		if (c < 0x20)
+			fprintf(stderr,"! %02x !\n",c);
+	} while (c != '$');
+
+	chk = 0;
+	while (max > 1) {
+		c = gdb_getc(gc);
+		if (c == '#') {
+			*buf++ = 0;
+			c = gdb_getc(gc);
+			if (c < 0) goto fail;
+			tmp[0] = c;
+			c = gdb_getc(gc);
+			if (c < 0) goto fail;
+			tmp[1] = c;
+			c = strtoul(tmp, 0, 16);
+			if (c != (chk & 0xff)) {
+				gdb_putc(gc,'-');
+				fprintf(stderr,"PKT: BAD CHECKSUM\n");
+				goto again;
+			} else {
+				gdb_putc(gc,'+');
+				return 0;
+			}
+		} else {
+			chk += c;
+			*buf++ = c;
+		}
+	}
+	gdb_putc(gc,'-');
+	fprintf(stderr,"PKT: OVERFLOW\n");
+	goto again;
+
+fail:
+	*start = 0;
+	return -1;
+}
+
+unsigned unhex(char *x) {
+	unsigned n;
+	char t = x[2];
+	x[2] = 0;
+	n = strtoul(x, 0, 16);
+	x[2] = t;
+ 	return n;
+}
+
+static struct gdbcnxn *GC;
+
+void xprintf(const char *fmt, ...) {
+	char buf[256];
+	va_list ap;
+	va_start(ap, fmt);
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	buf[sizeof(buf)-1] = 0;
+	va_end(ap);
+	gdb_puthex(GC, buf, strlen(buf));
+}
+
+void debugger_command(char *line);
+
+void handle_ext_command(struct gdbcnxn *gc, char *cmd, char *args)
+{
+	if (!strcmp(cmd,"Rcmd")) {
+		char *p = args;
+		cmd = p;
+		while (p[0] && p[1]) {
+			*cmd++ = unhex(p);
+			p+=2;
+		}
+		*cmd = 0;
+		GC = gc;
+		debugger_command(args);
+	}
+}
+
+void handle_command(struct gdbcnxn *gc, char *cmd)
+{
+	union {
+		u32 w[256+2];
+		u16 h[512+4];
+		u8 b[1024+8];
+	} tmp;
+	unsigned n,x;
+
+	/* silent (no-response) commands */
+	switch (cmd[0]) {
+	case 'c':
+		swdp_core_resume();
+		return;
+	}
+
+	gdb_prologue(gc);
+	switch (cmd[0]) {
+	case '?':
+		gdb_puts(gc, "S00");
+		swdp_core_halt();
+		break;
+	case 'H':
+		gdb_puts(gc, "OK");
+		break;
+	case 'm':
+		if (sscanf(cmd + 1, "%x,%x", &x, &n) != 2)
+			break;
+
+		if (n > 1024)
+			n = 1024;
+		 
+		swdp_ahb_read32(x & (~3), tmp.w, ((n + 3) & (~3)) / 4);
+		gdb_puthex(gc, tmp.b + (x & 3), n);
+		break;	
+	case 'g':  {
+		u32 regs[19];
+		swdp_core_read_all(regs);
+		gdb_puthex(gc, regs, sizeof(regs));
+		break;
+	}
+	case 's':
+		swdp_core_step();
+		gdb_puts(gc, "S00");
+		break;
+	case 'q': {
+		char *args = ++cmd;
+		while (*args) {
+			if (*args == ',') {
+				*args++ = 0;
+				break;
+			}
+			args++;
+		}
+		handle_ext_command(gc, cmd, args);
+		break;
+		
+	}
+	}
+	gdb_epilogue(gc);
+}
+
+void handler(int n) {
+}
+
+int main(int argc, char **argv) {
+	struct gdbcnxn gc;
+	char cmd[32768];
+	gc.tx = 1;
+	gc.rx = 0;
+	gc.chk = 0;
+
+	signal(SIGINT, handler);
+	
+	fprintf(stderr,"[ debugport v1.0 ]\n");
+
+	if (swdp_open())
+		fprintf(stderr,"error: cannot find swdp board\n");
+
+	for (;;) {
+		if (gdb_recv(&gc, cmd, sizeof(cmd))) {
+			fprintf(stderr,"[ disconnect ]\n");
+			return 0;
+		}
+		//fprintf(stderr,"PKT: %s\n", cmd);
+		handle_command(&gc, cmd);
+	}
+	return 0;
+}
diff --git a/tools/linenoise.c b/tools/linenoise.c
@@ -0,0 +1,612 @@
+/* linenoise.c -- guerrilla line editing library against the idea that a
+ * line editing lib needs to be 20,000 lines of C code.
+ *
+ * You can find the latest source code at:
+ * 
+ *   http://github.com/antirez/linenoise
+ *
+ * Does a number of crazy assumptions that happen to be true in 99.9999% of
+ * the 2010 UNIX computers around.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *  *  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *  *  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * 
+ * ------------------------------------------------------------------------
+ *
+ * References:
+ * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
+ * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html
+ *
+ * Todo list:
+ * - Switch to gets() if $TERM is something we can't support.
+ * - Filter bogus Ctrl+<char> combinations.
+ * - Win32 support
+ *
+ * Bloat:
+ * - Completion?
+ * - History search like Ctrl+r in readline?
+ *
+ * List of escape sequences used by this program, we do everything just
+ * with three sequences. In order to be so cheap we may have some
+ * flickering effect with some slow terminal, but the lesser sequences
+ * the more compatible.
+ *
+ * CHA (Cursor Horizontal Absolute)
+ *    Sequence: ESC [ n G
+ *    Effect: moves cursor to column n
+ *
+ * EL (Erase Line)
+ *    Sequence: ESC [ n K
+ *    Effect: if n is 0 or missing, clear from cursor to end of line
+ *    Effect: if n is 1, clear from beginning of line to cursor
+ *    Effect: if n is 2, clear entire line
+ *
+ * CUF (CUrsor Forward)
+ *    Sequence: ESC [ n C
+ *    Effect: moves cursor forward of n chars
+ *
+ * The following are used to clear the screen: ESC [ H ESC [ 2 J
+ * This is actually composed of two sequences:
+ *
+ * cursorhome
+ *    Sequence: ESC [ H
+ *    Effect: moves the cursor to upper left corner
+ *
+ * ED2 (Clear entire screen)
+ *    Sequence: ESC [ 2 J
+ *    Effect: clear the whole screen
+ * 
+ */
+
+#include <termios.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include "linenoise.h"
+
+#define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100
+#define LINENOISE_MAX_LINE 4096
+static char *unsupported_term[] = {"dumb","cons25",NULL};
+static linenoiseCompletionCallback *completionCallback = NULL;
+
+static struct termios orig_termios; /* in order to restore at exit */
+static int rawmode = 0; /* for atexit() function to check if restore is needed*/
+static int atexit_registered = 0; /* register atexit just 1 time */
+static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN;
+static int history_len = 0;
+char **history = NULL;
+
+static void linenoiseAtExit(void);
+int linenoiseHistoryAdd(const char *line);
+
+static int isUnsupportedTerm(void) {
+    char *term = getenv("TERM");
+    int j;
+
+    if (term == NULL) return 0;
+    for (j = 0; unsupported_term[j]; j++)
+        if (!strcasecmp(term,unsupported_term[j])) return 1;
+    return 0;
+}
+
+static void freeHistory(void) {
+    if (history) {
+        int j;
+
+        for (j = 0; j < history_len; j++)
+            free(history[j]);
+        free(history);
+    }
+}
+
+static int enableRawMode(int fd) {
+    struct termios raw;
+
+    if (!isatty(STDIN_FILENO)) goto fatal;
+    if (!atexit_registered) {
+        atexit(linenoiseAtExit);
+        atexit_registered = 1;
+    }
+    if (tcgetattr(fd,&orig_termios) == -1) goto fatal;
+
+    raw = orig_termios;  /* modify the original mode */
+    /* input modes: no break, no CR to NL, no parity check, no strip char,
+     * no start/stop output control. */
+    raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+    /* output modes - disable post processing */
+    raw.c_oflag &= ~(OPOST);
+    /* control modes - set 8 bit chars */
+    raw.c_cflag |= (CS8);
+    /* local modes - choing off, canonical off, no extended functions,
+     * no signal chars (^Z,^C) */
+    raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
+    /* control chars - set return condition: min number of bytes and timer.
+     * We want read to return every single byte, without timeout. */
+    raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */
+
+    /* put terminal in raw mode after flushing */
+    if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal;
+    rawmode = 1;
+    return 0;
+
+fatal:
+    errno = ENOTTY;
+    return -1;
+}
+
+static void disableRawMode(int fd) {
+    /* Don't even check the return value as it's too late. */
+    if (rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1)
+        rawmode = 0;
+}
+
+/* At exit we'll try to fix the terminal to the initial conditions. */
+static void linenoiseAtExit(void) {
+    disableRawMode(STDIN_FILENO);
+    freeHistory();
+}
+
+static int getColumns(void) {
+    struct winsize ws;
+
+    if (ioctl(1, TIOCGWINSZ, &ws) == -1) return 80;
+    return ws.ws_col;
+}
+
+static void refreshLine(int fd, const char *prompt, char *buf, size_t len, size_t pos, size_t cols) {
+    char seq[64];
+    size_t plen = strlen(prompt);
+    
+    while((plen+pos) >= cols) {
+        buf++;
+        len--;
+        pos--;
+    }
+    while (plen+len > cols) {
+        len--;
+    }
+
+    /* Cursor to left edge */
+    snprintf(seq,64,"\x1b[0G");
+    if (write(fd,seq,strlen(seq)) == -1) return;
+    /* Write the prompt and the current buffer content */
+    if (write(fd,prompt,strlen(prompt)) == -1) return;
+    if (write(fd,buf,len) == -1) return;
+    /* Erase to right */
+    snprintf(seq,64,"\x1b[0K");
+    if (write(fd,seq,strlen(seq)) == -1) return;
+    /* Move cursor to original position. */
+    snprintf(seq,64,"\x1b[0G\x1b[%dC", (int)(pos+plen));
+    if (write(fd,seq,strlen(seq)) == -1) return;
+}
+
+static void beep() {
+    fprintf(stderr, "\x7");
+    fflush(stderr);
+}
+
+static void freeCompletions(linenoiseCompletions *lc) {
+    size_t i;
+    for (i = 0; i < lc->len; i++)
+        free(lc->cvec[i]);
+    if (lc->cvec != NULL)
+        free(lc->cvec);
+}
+
+static int completeLine(int fd, const char *prompt, char *buf, size_t buflen, size_t *len, size_t *pos, size_t cols) {
+    linenoiseCompletions lc = { 0, NULL };
+    int nread, nwritten;
+    char c = 0;
+
+    completionCallback(buf,&lc);
+    if (lc.len == 0) {
+        beep();
+    } else {
+        size_t stop = 0, i = 0;
+        size_t clen;
+
+        while(!stop) {
+            /* Show completion or original buffer */
+            if (i < lc.len) {
+                clen = strlen(lc.cvec[i]);
+                refreshLine(fd,prompt,lc.cvec[i],clen,clen,cols);
+            } else {
+                refreshLine(fd,prompt,buf,*len,*pos,cols);
+            }
+
+            nread = read(fd,&c,1);
+            if (nread <= 0) {
+                freeCompletions(&lc);
+                return -1;
+            }
+
+            switch(c) {
+                case 9: /* tab */
+                    i = (i+1) % (lc.len+1);
+                    if (i == lc.len) beep();
+                    break;
+                case 27: /* escape */
+                    /* Re-show original buffer */
+                    if (i < lc.len) {
+                        refreshLine(fd,prompt,buf,*len,*pos,cols);
+                    }
+                    stop = 1;
+                    break;
+                default:
+                    /* Update buffer and return */
+                    if (i < lc.len) {
+                        nwritten = snprintf(buf,buflen,"%s",lc.cvec[i]);
+                        *len = *pos = nwritten;
+                    }
+                    stop = 1;
+                    break;
+            }
+        }
+    }
+
+    freeCompletions(&lc);
+    return c; /* Return last read character */
+}
+
+void linenoiseClearScreen(void) {
+    if (write(STDIN_FILENO,"\x1b[H\x1b[2J",7) <= 0) {
+        /* nothing to do, just to avoid warning. */
+    }
+}
+
+static int linenoisePrompt(int fd, char *buf, size_t buflen, const char *prompt) {
+    size_t plen = strlen(prompt);
+    size_t pos = 0;
+    size_t len = 0;
+    size_t cols = getColumns();
+    int history_index = 0;
+
+    buf[0] = '\0';
+    buflen--; /* Make sure there is always space for the nulterm */
+
+    /* The latest history entry is always our current buffer, that
+     * initially is just an empty string. */
+    linenoiseHistoryAdd("");
+    
+    if (write(fd,prompt,plen) == -1) return -1;
+    while(1) {
+        char c;
+        int nread;
+        char seq[2], seq2[2];
+
+        nread = read(fd,&c,1);
+        if (nread <= 0) return len;
+
+        /* Only autocomplete when the callback is set. It returns < 0 when
+         * there was an error reading from fd. Otherwise it will return the
+         * character that should be handled next. */
+        if (c == 9 && completionCallback != NULL) {
+            c = completeLine(fd,prompt,buf,buflen,&len,&pos,cols);
+            /* Return on errors */
+            if (c < 0) return len;
+            /* Read next character when 0 */
+            if (c == 0) continue;
+        }
+
+        switch(c) {
+        case 13:    /* enter */
+            history_len--;
+            free(history[history_len]);
+            return (int)len;
+        case 3:     /* ctrl-c */
+            errno = EAGAIN;
+            return -1;
+        case 127:   /* backspace */
+        case 8:     /* ctrl-h */
+            if (pos > 0 && len > 0) {
+                memmove(buf+pos-1,buf+pos,len-pos);
+                pos--;
+                len--;
+                buf[len] = '\0';
+                refreshLine(fd,prompt,buf,len,pos,cols);
+            }
+            break;
+        case 4:     /* ctrl-d, remove char at right of cursor */
+            if (len > 1 && pos < (len-1)) {
+                memmove(buf+pos,buf+pos+1,len-pos);
+                len--;
+                buf[len] = '\0';
+                refreshLine(fd,prompt,buf,len,pos,cols);
+            } else if (len == 0) {
+                history_len--;
+                free(history[history_len]);
+                return -1;
+            }
+            break;
+        case 20:    /* ctrl-t */
+            if (pos > 0 && pos < len) {
+                int aux = buf[pos-1];
+                buf[pos-1] = buf[pos];
+                buf[pos] = aux;
+                if (pos != len-1) pos++;
+                refreshLine(fd,prompt,buf,len,pos,cols);
+            }
+            break;
+        case 2:     /* ctrl-b */
+            goto left_arrow;
+        case 6:     /* ctrl-f */
+            goto right_arrow;
+        case 16:    /* ctrl-p */
+            seq[1] = 65;
+            goto up_down_arrow;
+        case 14:    /* ctrl-n */
+            seq[1] = 66;
+            goto up_down_arrow;
+            break;
+        case 27:    /* escape sequence */
+            if (read(fd,seq,2) == -1) break;
+            if (seq[0] == 91 && seq[1] == 68) {
+left_arrow:
+                /* left arrow */
+                if (pos > 0) {
+                    pos--;
+                    refreshLine(fd,prompt,buf,len,pos,cols);
+                }
+            } else if (seq[0] == 91 && seq[1] == 67) {
+right_arrow:
+                /* right arrow */
+                if (pos != len) {
+                    pos++;
+                    refreshLine(fd,prompt,buf,len,pos,cols);
+                }
+            } else if (seq[0] == 91 && (seq[1] == 65 || seq[1] == 66)) {
+up_down_arrow:
+                /* up and down arrow: history */
+                if (history_len > 1) {
+                    /* Update the current history entry before to
+                     * overwrite it with tne next one. */
+                    free(history[history_len-1-history_index]);
+                    history[history_len-1-history_index] = strdup(buf);
+                    /* Show the new entry */
+                    history_index += (seq[1] == 65) ? 1 : -1;
+                    if (history_index < 0) {
+                        history_index = 0;
+                        break;
+                    } else if (history_index >= history_len) {
+                        history_index = history_len-1;
+                        break;
+                    }
+                    strncpy(buf,history[history_len-1-history_index],buflen);
+                    buf[buflen] = '\0';
+                    len = pos = strlen(buf);
+                    refreshLine(fd,prompt,buf,len,pos,cols);
+                }
+            } else if (seq[0] == 91 && seq[1] > 48 && seq[1] < 55) {
+                /* extended escape */
+                if (read(fd,seq2,2) == -1) break;
+                if (seq[1] == 51 && seq2[0] == 126) {
+                    /* delete */
+                    if (len > 0 && pos < len) {
+                        memmove(buf+pos,buf+pos+1,len-pos-1);
+                        len--;
+                        buf[len] = '\0';
+                        refreshLine(fd,prompt,buf,len,pos,cols);
+                    }
+                }
+            }
+            break;
+        default:
+            if (len < buflen) {
+                if (len == pos) {
+                    buf[pos] = c;
+                    pos++;
+                    len++;
+                    buf[len] = '\0';
+                    if (plen+len < cols) {
+                        /* Avoid a full update of the line in the
+                         * trivial case. */
+                        if (write(fd,&c,1) == -1) return -1;
+                    } else {
+                        refreshLine(fd,prompt,buf,len,pos,cols);
+                    }
+                } else {
+                    memmove(buf+pos+1,buf+pos,len-pos);
+                    buf[pos] = c;
+                    len++;
+                    pos++;
+                    buf[len] = '\0';
+                    refreshLine(fd,prompt,buf,len,pos,cols);
+                }
+            }
+            break;
+        case 21: /* Ctrl+u, delete the whole line. */
+            buf[0] = '\0';
+            pos = len = 0;
+            refreshLine(fd,prompt,buf,len,pos,cols);
+            break;
+        case 11: /* Ctrl+k, delete from current to end of line. */
+            buf[pos] = '\0';
+            len = pos;
+            refreshLine(fd,prompt,buf,len,pos,cols);
+            break;
+        case 1: /* Ctrl+a, go to the start of the line */
+            pos = 0;
+            refreshLine(fd,prompt,buf,len,pos,cols);
+            break;
+        case 5: /* ctrl+e, go to the end of the line */
+            pos = len;
+            refreshLine(fd,prompt,buf,len,pos,cols);
+            break;
+        case 12: /* ctrl+l, clear screen */
+            linenoiseClearScreen();
+            refreshLine(fd,prompt,buf,len,pos,cols);
+        }
+    }
+    return len;
+}
+
+static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) {
+    int fd = STDIN_FILENO;
+    int count;
+
+    if (buflen == 0) {
+        errno = EINVAL;
+        return -1;
+    }
+    if (!isatty(STDIN_FILENO)) {
+        if (fgets(buf, buflen, stdin) == NULL) return -1;
+        count = strlen(buf);
+        if (count && buf[count-1] == '\n') {
+            count--;
+            buf[count] = '\0';
+        }
+    } else {
+        if (enableRawMode(fd) == -1) return -1;
+        count = linenoisePrompt(fd, buf, buflen, prompt);
+        disableRawMode(fd);
+        printf("\n");
+    }
+    return count;
+}
+
+char *linenoise(const char *prompt) {
+    char buf[LINENOISE_MAX_LINE];
+    int count;
+
+    if (isUnsupportedTerm()) {
+        size_t len;
+
+        printf("%s",prompt);
+        fflush(stdout);
+        if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL;
+        len = strlen(buf);
+        while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) {
+            len--;
+            buf[len] = '\0';
+        }
+        return strdup(buf);
+    } else {
+        count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt);
+        if (count == -1) return NULL;
+        return strdup(buf);
+    }
+}
+
+/* Register a callback function to be called for tab-completion. */
+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) {
+    completionCallback = fn;
+}
+
+void linenoiseAddCompletion(linenoiseCompletions *lc, char *str) {
+    size_t len = strlen(str);
+    char *copy = malloc(len+1);
+    memcpy(copy,str,len+1);
+    lc->cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1));
+    lc->cvec[lc->len++] = copy;
+}
+
+/* Using a circular buffer is smarter, but a bit more complex to handle. */
+int linenoiseHistoryAdd(const char *line) {
+    char *linecopy;
+
+    if (history_max_len == 0) return 0;
+    if (history == NULL) {
+        history = malloc(sizeof(char*)*history_max_len);
+        if (history == NULL) return 0;
+        memset(history,0,(sizeof(char*)*history_max_len));
+    }
+    linecopy = strdup(line);
+    if (!linecopy) return 0;
+    if (history_len == history_max_len) {
+        free(history[0]);
+        memmove(history,history+1,sizeof(char*)*(history_max_len-1));
+        history_len--;
+    }
+    history[history_len] = linecopy;
+    history_len++;
+    return 1;
+}
+
+int linenoiseHistorySetMaxLen(int len) {
+    char **new;
+
+    if (len < 1) return 0;
+    if (history) {
+        int tocopy = history_len;
+
+        new = malloc(sizeof(char*)*len);
+        if (new == NULL) return 0;
+        if (len < tocopy) tocopy = len;
+        memcpy(new,history+(history_max_len-tocopy), sizeof(char*)*tocopy);
+        free(history);
+        history = new;
+    }
+    history_max_len = len;
+    if (history_len > history_max_len)
+        history_len = history_max_len;
+    return 1;
+}
+
+/* Save the history in the specified file. On success 0 is returned
+ * otherwise -1 is returned. */
+int linenoiseHistorySave(char *filename) {
+    FILE *fp = fopen(filename,"w");
+    int j;
+    
+    if (fp == NULL) return -1;
+    for (j = 0; j < history_len; j++)
+        fprintf(fp,"%s\n",history[j]);
+    fclose(fp);
+    return 0;
+}
+
+/* Load the history from the specified file. If the file does not exist
+ * zero is returned and no operation is performed.
+ *
+ * If the file exists and the operation succeeded 0 is returned, otherwise
+ * on error -1 is returned. */
+int linenoiseHistoryLoad(char *filename) {
+    FILE *fp = fopen(filename,"r");
+    char buf[LINENOISE_MAX_LINE];
+    
+    if (fp == NULL) return -1;
+
+    while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) {
+        char *p;
+        
+        p = strchr(buf,'\r');
+        if (!p) p = strchr(buf,'\n');
+        if (p) *p = '\0';
+        linenoiseHistoryAdd(buf);
+    }
+    fclose(fp);
+    return 0;
+}
diff --git a/tools/linenoise.h b/tools/linenoise.h
@@ -0,0 +1,56 @@
+/* linenoise.h -- guerrilla line editing library against the idea that a
+ * line editing lib needs to be 20,000 lines of C code.
+ *
+ * See linenoise.c for more information.
+ *
+ * ------------------------------------------------------------------------
+ *
+ * Copyright (c) 2010, Salvatore Sanfilippo <antirez at gmail dot com>
+ * Copyright (c) 2010, Pieter Noordhuis <pcnoordhuis at gmail dot com>
+ *
+ * All rights reserved.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * 
+ *  *  Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *
+ *  *  Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __LINENOISE_H
+#define __LINENOISE_H
+
+typedef struct linenoiseCompletions {
+  size_t len;
+  char **cvec;
+} linenoiseCompletions;
+
+typedef void(linenoiseCompletionCallback)(const char *, linenoiseCompletions *);
+void linenoiseSetCompletionCallback(linenoiseCompletionCallback *);
+void linenoiseAddCompletion(linenoiseCompletions *, char *);
+
+char *linenoise(const char *prompt);
+int linenoiseHistoryAdd(const char *line);
+int linenoiseHistorySetMaxLen(int len);
+int linenoiseHistorySave(char *filename);
+int linenoiseHistoryLoad(char *filename);
+void linenoiseClearScreen(void);
+
+#endif /* __LINENOISE_H */
diff --git a/tools/lpcboot.c b/tools/lpcboot.c
@@ -0,0 +1,148 @@
+/* lpcboot.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdint.h>
+
+#include "usb.h"
+
+int usage(void) {
+	fprintf(stderr,
+		"lpcboot usage\n-------------\n"
+		" lpcboot query         display information about target\n"
+		" lpcboot boot <app>    download app to ram and execute\n"
+		" lpcboot flash <app>   write app image to flash\n"
+		" lpcboot erase         erase app image\n"
+		" lpcboot reboot        reboot the bootloader\n"
+		" lpcboot app           reboot into the app in flash\n");
+	return -1;
+}
+
+struct device_info {
+	char part[16];
+	char board[16];
+	uint32_t version;
+	uint32_t ram_base;
+	uint32_t ram_size;
+	uint32_t rom_base;
+	uint32_t rom_size;
+	uint32_t ununsed0;
+	uint32_t ununsed1;
+	uint32_t ununsed2;
+};
+
+int main(int argc, char **argv) {
+	usb_handle *usb;
+	char buf[32768];
+	int fd, once = 1, sz = 0, dl = 0;
+	uint32_t cmd[3];
+	uint32_t rep[2];
+	struct device_info di;
+
+	if (argc < 2) 
+		return usage();
+
+	cmd[0] = 0xDB00A5A5;
+	if (!strcmp(argv[1],"flash")) {
+		dl = 1;
+		cmd[1] = 'W';
+	} else if (!strcmp(argv[1],"boot")) {
+		dl = 1;
+		cmd[1] = 'X';
+	} else if (!strcmp(argv[1],"erase")) {
+		cmd[1] = 'E';
+	} else if (!strcmp(argv[1],"query")) {
+		cmd[1] = 'Q';
+	} else if (!strcmp(argv[1],"reboot")) {
+		cmd[1] = 'R';
+	} else if (!strcmp(argv[1],"app")) {
+		cmd[1] = 'A';
+	} else {
+		return usage();
+	}
+
+	if (dl) {
+		if (argc < 3)
+			return usage();
+		fd = open(argv[2], O_RDONLY);
+		sz = read(fd, buf, sizeof(buf));
+		close(fd);
+		if ((fd < 0) || (sz < 1)) {
+			fprintf(stderr,"error: cannot read '%s'\n", argv[2]);
+			return -1;
+		}
+	}
+	cmd[2] = sz;
+
+	for (;;) {		
+		usb = usb_open(0x18d1, 0xdb00, 0);
+		if (usb == 0) {
+			if (once) {
+				fprintf(stderr,"waiting for device...\n");
+				once = 0;
+			}
+		} else {
+			break;
+		}
+	}
+
+	if (usb_write(usb, cmd, 12) != 12) {
+		fprintf(stderr,"io error\n");
+		return -1;
+	}
+	for (;;) {
+		if (usb_read(usb, rep, 8) != 8) {
+			fprintf(stderr,"io error\n");
+			return -1;
+		}
+		if (rep[0] != 0xDB00A5A5) {
+			fprintf(stderr,"protocol error\n");
+			return -1;
+		}
+		if (rep[1] != 0) {
+			fprintf(stderr,"%s failure\n", argv[1]);
+			return -1;
+		}
+		if (!strcmp(argv[1],"query")) {
+			if (usb_read(usb, &di, sizeof(di)) != sizeof(di)) {
+				fprintf(stderr,"io error\n");
+				return -1;
+			}
+			fprintf(stderr,"Part:  %s\n", di.part);
+			fprintf(stderr,"Board: %s\n", di.board);
+			fprintf(stderr,"RAM:   @%08x (%dKB)\n",
+				di.ram_base, di.ram_size / 1024);
+			fprintf(stderr,"Flash: @%08x (%dKB)\n",
+				di.rom_base, di.rom_size / 1024);
+			return 0;
+		}
+		if (!dl)
+			break;
+		fprintf(stderr,"sending %d bytes...\n", sz);
+		if (usb_write(usb, buf, sz) != sz) {
+			fprintf(stderr,"download failure %d\n", sz);
+			return -1;
+		}
+		dl = 0;
+	}
+	fprintf(stderr,"OKAY\n");
+	return 0;
+}
diff --git a/tools/module.mk b/tools/module.mk
@@ -0,0 +1,46 @@
+$(call start-module-mk)
+
+M_NAME := debugger
+M_OBJS := tools/debugger.o
+M_OBJS += tools/debugger-core.o
+M_OBJS += tools/debugger-commands.o
+M_OBJS += tools/rswdp.o
+M_OBJS += tools/linenoise.o
+M_OBJS += tools/usb.o
+$(call build-host-executable)
+
+M_NAME := gdb-bridge
+M_OBJS := tools/gdb-bridge.o
+M_OBJS += tools/debugger-core.o
+M_OBJS += tools/debugger-commands.o
+M_OBJS += tools/rswdp.o
+M_OBJS += tools/linenoise.o
+M_OBJS += tools/usb.o
+$(call build-host-executable)
+
+M_NAME := stm32boot
+M_OBJS := tools/stm32boot.o
+$(call build-host-executable)
+
+ifeq ($(UNAME),Linux)
+M_NAME := usbmon
+M_OBJS := tools/usbmon.o
+$(call build-host-executable)
+endif
+
+M_NAME := usbtest
+M_OBJS := tools/usbtest.o tools/usb.o
+$(call build-host-executable)
+
+M_NAME := bless-lpc
+M_OBJS := tools/bless-lpc.o
+$(call build-host-executable)
+
+M_NAME := lpcboot
+M_OBJS := tools/lpcboot.o tools/usb.o
+$(call build-host-executable)
+
+M_NAME := uconsole 
+M_OBJS := tools/uconsole.o tools/usb.o
+$(call build-host-executable)
+
diff --git a/tools/rswdp.c b/tools/rswdp.c
@@ -0,0 +1,576 @@
+/* rswdp.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "usb.h"
+
+#include <fw/types.h>
+#include <protocol/rswdp.h>
+#include "rswdp.h"
+
+static volatile int ATTN;
+
+void swdp_interrupt(void) {
+	ATTN++;
+	if (write(2, "\b\b*INTERRUPT*\n", 16)) { /* do nothing */ }
+}
+
+static u16 sequence = 1;
+
+static usb_handle *usb;
+
+#define MAXWORDS 512
+
+struct txn {
+	/* words to transmit */
+	u32 tx[MAXWORDS];
+	/* pointers to words for reply data */
+	u32 *rx[MAXWORDS];
+
+	/* words to send and receive */
+	unsigned txc;
+	unsigned rxc;
+
+	/* cached state */
+	u32 cache_apaddr;
+	u32 cache_ahbtar;
+
+	unsigned magic;
+};
+
+static void process_async(u32 *data, unsigned count) {
+	unsigned msg, n;
+	u32 tmp;
+
+	while (count-- > 0) {
+		msg = *data++;
+		switch (RSWD_MSG_CMD(msg)) {
+		case CMD_NULL:
+			continue;
+		case CMD_DEBUG_PRINT:
+		    //op = RSWD_MSG_OP(msg);
+		    n = RSWD_MSG_ARG(msg);
+			if (n > count)
+				return;
+			tmp = data[n];
+			data[n] = 0;
+			printf("%s",(char*) data);
+			data[n] = tmp;
+			data += n;
+			count -= n;
+			break;
+		default:
+			return;
+		}
+	}	
+}
+
+static int process_reply(struct txn *t, u32 *data, int count) {
+	unsigned msg, op, n, rxp, rxc;
+
+	rxc = t->rxc;
+	rxp = 0;
+
+	while (count-- > 0) {
+		msg = *data++;
+		op = RSWD_MSG_OP(msg);
+		n = RSWD_MSG_ARG(msg);
+
+		//fprintf(stderr,"[ %02x %02x %04x ]\n",RSWD_MSG_CMD(msg), op, n);
+		switch (RSWD_MSG_CMD(msg)) {
+		case CMD_NULL:
+			continue;
+		case CMD_SWD_DATA:
+			if (n > rxc) {
+				fprintf(stderr,"reply overrun (%d > %d)\n", n, rxc);
+				return -1;
+			}
+			while (n-- > 0) {
+				//printf("data %08x -> %p\n", data[0], t->rx[rxp]);
+				*(t->rx[rxp++]) = *data++;
+				rxc--;
+			}
+			continue;
+		case CMD_STATUS:
+			return op ? -op : 0;
+		default:
+			fprintf(stderr,"unknown command 0x%02x\n", RSWD_MSG_CMD(msg));
+			return -1;
+		}
+	}
+	return 0;
+}
+
+static int q_exec(struct txn *t) {
+	unsigned data[1028];
+	unsigned seq = sequence++;
+	int r;
+	u32 id;
+
+	if (t->magic != 0x12345678) {
+		fprintf(stderr,"FATAL: bogus txn magic\n");
+		exit(1);
+	}
+	t->magic = 0;
+
+	/* If we are a multiple of 64, and not exactly 4K,
+	 * add padding to ensure the target can detect the end of txn 
+	 */
+	if (((t->txc % 16) == 0) && (t->txc != MAXWORDS))
+		t->tx[t->txc++] = RSWD_MSG(CMD_NULL, 0, 0);
+
+	id = RSWD_TXN_START(seq);
+	t->tx[0] = id;
+
+	r = usb_write(usb, t->tx, t->txc * sizeof(u32));
+	if (r != (t->txc * sizeof(u32))) {
+		fprintf(stderr,"invoke: tx error\n");
+		return -1;
+	}
+	for (;;) {
+		r = usb_read(usb, data, 4096);
+		if (r <= 0) {
+			fprintf(stderr,"invoke: rx error\n");
+			return -1;
+		}
+		if (r & 3) {
+			fprintf(stderr,"invoke: framing error\n");
+			return -1;
+		}
+
+		if (data[0] == id) {
+			return process_reply(t, data + 1, (r / 4) - 1);
+		} else if (data[0] == RSWD_TXN_ASYNC) {
+			process_async(data + 1, (r / 4) - 1);
+		} else {
+			fprintf(stderr,"invoke: unexpected txn %08x (%d)\n", data[0], r);
+		}
+	}
+}
+
+static void q_check(struct txn *t, int n) {
+	if ((t->txc + n) >= MAXWORDS) {
+		fprintf(stderr,"FATAL: txn buffer overflow\n");
+		exit(1);
+	}
+
+}
+
+static void q_init(struct txn *t) {
+	t->magic = 0x12345678;
+	t->txc = 1;
+	t->rxc = 0;
+	t->cache_apaddr = 0xffffffff;
+	t->cache_ahbtar = 0xffffffff;
+}
+
+#define SWD_WR(a,n) RSWD_MSG(CMD_SWD_WRITE, OP_WR | (a), (n))
+#define SWD_RD(a,n) RSWD_MSG(CMD_SWD_READ, OP_RD | (a), (n))
+#define SWD_RX(a,n) RSWD_MSG(CMD_SWD_DISCARD, OP_RD | (a), (n))
+
+static void q_ap_select(struct txn *t, u32 addr) {
+	addr &= 0xF0;
+	if (t->cache_apaddr != addr) {
+		t->tx[t->txc++] = SWD_WR(DP_SELECT, 1);
+		t->tx[t->txc++] = addr;
+		t->cache_apaddr = addr;
+	}
+}
+
+static void q_ap_write(struct txn *t, u32 addr, u32 value) {
+	q_check(t, 3);
+	q_ap_select(t, addr);
+	t->tx[t->txc++] = SWD_WR(OP_AP | (addr & 0xC), 1);
+	t->tx[t->txc++] = value;
+}
+
+static void q_ap_read(struct txn *t, u32 addr, u32 *value) {
+	q_check(t, 4);
+	q_ap_select(t, addr);
+	t->tx[t->txc++] = SWD_RX(OP_AP | (addr & 0xC), 1);
+	t->tx[t->txc++] = SWD_RD(DP_BUFFER, 1);
+	t->rx[t->rxc++] = value;
+}
+
+static void q_ahb_write(struct txn *t, u32 addr, u32 value) {
+	if (t->cache_ahbtar != addr) {
+		q_ap_write(t, AHB_TAR, addr);
+		t->cache_ahbtar = addr;
+	}
+	q_ap_write(t, AHB_DRW, value);
+}
+
+static void q_ahb_read(struct txn *t, u32 addr, u32 *value) {
+	if (t->cache_ahbtar != addr) {
+		q_ap_write(t, AHB_TAR, addr);
+		t->cache_ahbtar = addr;
+	}
+	q_ap_read(t, AHB_DRW, value);
+}
+
+int swdp_ap_write(u32 addr, u32 value) {
+	struct txn t;
+	q_init(&t);
+	q_ap_write(&t, addr, value);
+	return q_exec(&t);
+}
+
+int swdp_ap_read(u32 addr, u32 *value) {
+	struct txn t;
+	q_init(&t);
+	q_ap_read(&t, addr, value);
+	return q_exec(&t);
+}
+
+int swdp_ahb_read(u32 addr, u32 *value) {
+	struct txn t;
+	q_init(&t);
+	q_ahb_read(&t, addr, value);
+	return q_exec(&t);
+}
+
+int swdp_ahb_write(u32 addr, u32 value) {
+	struct txn t;
+	q_init(&t);
+	q_ahb_write(&t, addr, value);
+	return q_exec(&t);
+}
+
+#if 0
+/* simpler but far less optimal. keeping against needing to debug */
+int swdp_ahb_read32(u32 addr, u32 *out, int count) {
+	struct txn t;
+	while (count > 0) {
+		int xfer = (count > 128) ? 128: count;
+		count -= xfer;
+		q_init(&t);
+		while (xfer-- > 0) {
+			q_ahb_read(&t, addr, out++);
+			addr += 4;
+		}
+		if (q_exec(&t))
+			return -1;
+	}
+	return 0;
+}
+
+int swdp_ahb_write32(u32 addr, u32 *in, int count) {
+	struct txn t;
+	while (count > 0) {
+		int xfer = (count > 128) ? 128: count;
+		count -= xfer;
+		q_init(&t);
+		while (xfer-- > 0) {
+			q_ahb_write(&t, addr, *in++);
+			addr += 4;
+		}
+		if (q_exec(&t))
+			return -1;
+	}
+	return 0;
+}
+#else
+#define MAXDATAWORDS (MAXWORDS - 16)
+/* 10 txns overhead per 128 read txns - 126KB/s on 72MHz STM32F
+ * 8 txns overhead per 128 write txns - 99KB/s on 72MHz STM32F
+ */
+int swdp_ahb_read32(u32 addr, u32 *out, int count) {
+	struct txn t;
+
+	while (count > 0) {
+		int xfer;
+
+		/* auto-inc wraps at 4K page boundaries -- limit max
+		 * transfer so we won't cross a page boundary
+		 */
+		xfer = (0x1000 - (addr & 0xFFF)) / 4;
+		if (xfer > count)
+			xfer = count;
+		if (xfer > MAXDATAWORDS)
+			xfer = MAXDATAWORDS;
+
+		count -= xfer;
+		q_init(&t);
+
+		/* setup before initial txn */
+		q_ap_write(&t, AHB_CSW,
+			AHB_CSW_MDEBUG | AHB_CSW_PRIV | AHB_CSW_INC_SINGLE |
+			AHB_CSW_DBG_EN | AHB_CSW_32BIT);
+
+		/* initial address */
+		q_ap_write(&t, AHB_TAR, addr);
+		addr += xfer * 4;
+
+		/* kick off first read, ignore result, as the
+		 * real result will show up during the *next* read
+		 */
+		t.tx[t.txc++] = SWD_RX(OP_AP | (AHB_DRW & 0xC), 1);
+		t.tx[t.txc++] = SWD_RD(OP_AP | (AHB_DRW & 0xC), xfer -1);
+		while (xfer-- > 1)
+			t.rx[t.rxc++] = out++;
+		t.tx[t.txc++] = SWD_RD(DP_BUFFER, 1);
+		t.rx[t.rxc++] = out++;
+
+		/* restore state after last batch */
+		if (count == 0)
+			q_ap_write(&t, AHB_CSW,
+				AHB_CSW_MDEBUG | AHB_CSW_PRIV |
+				AHB_CSW_DBG_EN | AHB_CSW_32BIT);
+
+		if (q_exec(&t))
+			return -1;
+	}
+	return 0;
+}
+
+int swdp_ahb_write32(u32 addr, u32 *in, int count) {
+	struct txn t;
+
+	while (count > 0) {
+		int xfer;
+
+		/* auto-inc wraps at 4K page boundaries -- limit max
+		 * transfer so we won't cross a page boundary
+		 */
+		xfer = (0x1000 - (addr & 0xFFF)) / 4;
+		if (xfer > count)
+			xfer = count;
+		if (xfer > MAXDATAWORDS)
+			xfer = MAXDATAWORDS;
+
+		count -= xfer;
+		q_init(&t);
+
+		/* setup before initial txn */
+		q_ap_write(&t, AHB_CSW,
+			AHB_CSW_MDEBUG | AHB_CSW_PRIV | AHB_CSW_INC_SINGLE |
+			AHB_CSW_DBG_EN | AHB_CSW_32BIT);
+
+		/* initial address */
+		q_ap_write(&t, AHB_TAR, addr);
+
+		t.tx[t.txc++] = SWD_WR(OP_AP | (AHB_DRW & 0xC), xfer);
+		addr += xfer * 4;
+		while (xfer-- > 0) 
+			t.tx[t.txc++] = *in++;
+
+		/* restore state after last batch */
+		if (count == 0)
+			q_ap_write(&t, AHB_CSW,
+				AHB_CSW_MDEBUG | AHB_CSW_PRIV |
+				AHB_CSW_DBG_EN | AHB_CSW_32BIT);
+
+		if (q_exec(&t))
+			return -1;
+	}
+	return 0;
+}
+#endif
+
+int swdp_core_write(u32 n, u32 v) {
+	struct txn t;
+	q_init(&t);
+	q_ahb_write(&t, CDBG_REG_DATA, v);
+	q_ahb_write(&t, CDBG_REG_ADDR, (n & 0x1F) | 0x10000);
+	return q_exec(&t);
+}
+
+int swdp_core_read(u32 n, u32 *v) {
+	struct txn t;
+	q_init(&t);
+	q_ahb_write(&t, CDBG_REG_ADDR, n & 0x1F);
+	q_ahb_read(&t, CDBG_REG_DATA, v);
+	return q_exec(&t);
+}
+
+int swdp_core_read_all(u32 *v) {
+	struct txn t;
+	unsigned n;
+	q_init(&t);
+	for (n = 0; n < 19; n++) {
+		q_ahb_write(&t, CDBG_REG_ADDR, n & 0x1F);
+		q_ahb_read(&t, CDBG_REG_DATA, v++);
+	}
+	return q_exec(&t);
+}
+
+int swdp_core_halt(void) {
+	return swdp_ahb_write(CDBG_CSR, CDBG_CSR_KEY | CDBG_C_HALT | CDBG_C_DEBUGEN);
+}
+
+int swdp_core_step(void) {
+	return swdp_ahb_write(CDBG_CSR, CDBG_CSR_KEY | CDBG_C_STEP | CDBG_C_DEBUGEN);
+}
+
+int swdp_core_wait_for_halt(void) {
+	int last = ATTN;
+	u32 csr;
+	for (;;) {
+		if (swdp_ahb_read(CDBG_CSR, &csr))
+			return -1;
+		if (csr & CDBG_S_HALT)
+			return 0;
+		if (ATTN != last)
+			return -2;
+	}
+}
+
+int swdp_ahb_wait_for_change(u32 addr, u32 oldval) {
+	int last = ATTN;
+	u32 val;
+	do {
+		if (swdp_ahb_read(addr, &val))
+			return -1;
+		if (ATTN != last)
+			return -2;
+	} while (val == oldval);
+	return 0;
+}
+
+int swdp_core_resume(void) {
+	/* must leave DEBUGEN on to halt on vector catch, breakpoints, etc */
+	return swdp_ahb_write(CDBG_CSR, CDBG_CSR_KEY | CDBG_C_DEBUGEN);
+}
+
+#define DWT_COMP(n) (0xE0001020 + (n) * 0x10)
+#define DWT_MASK(n) (0xE0001024 + (n) * 0x10)
+#define DWT_FUNC(n) (0xE0001028 + (n) * 0x10)
+
+#define FUNC_DISABLED	0x0
+#define FUNC_WATCH_PC	0x4
+#define FUNC_WATCH_RD	0x5
+#define FUNC_WATCH_WR	0x6
+#define FUNC_WATCH_RW	0x7
+
+int swdp_watchpoint(unsigned n, u32 addr, u32 func) {
+	struct txn t;
+
+	if (n > 3)
+		return -1;
+
+	q_init(&t);
+	/* enable DWT, enable all exception traps */
+	q_ahb_write(&t, CDBG_EMCR, 0x010007F1);
+	q_ahb_write(&t, DWT_FUNC(n), FUNC_DISABLED);
+	if (func != FUNC_DISABLED) {
+		q_ahb_write(&t, DWT_COMP(n), addr);
+		q_ahb_write(&t, DWT_MASK(n), 0);
+		q_ahb_write(&t, DWT_FUNC(n), func);
+	}
+	return q_exec(&t);
+}
+
+int swdp_watchpoint_pc(unsigned n, u32 addr) {
+	return swdp_watchpoint(n, addr, FUNC_WATCH_PC);
+}
+
+int swdp_watchpoint_rd(unsigned n, u32 addr) {
+	return swdp_watchpoint(n, addr, FUNC_WATCH_RD);
+}
+
+int swdp_watchpoint_wr(unsigned n, u32 addr) {
+	return swdp_watchpoint(n, addr, FUNC_WATCH_WR);
+}
+
+int swdp_watchpoint_rw(unsigned n, u32 addr) {
+	return swdp_watchpoint(n, addr, FUNC_WATCH_RW);
+}
+
+int swdp_bootloader(void) {
+	struct txn t;
+	q_init(&t);
+	t.tx[t.txc++] = RSWD_MSG(CMD_BOOTLOADER, 0, 0);
+	return q_exec(&t);
+}
+
+int swdp_reset(void) {
+	struct txn t;
+	u32 n, idcode;
+
+	q_init(&t);
+	t.tx[t.txc++] = RSWD_MSG(CMD_ATTACH, 0, 0);
+	t.tx[t.txc++] = SWD_RD(DP_IDCODE, 1);
+	t.rx[t.rxc++] = &idcode;
+	q_exec(&t);
+
+	fprintf(stderr,"IDCODE: %08x\n", idcode);
+
+	q_init(&t);
+ 	/* clear any stale errors */
+	t.tx[t.txc++] = SWD_WR(DP_ABORT, 1);
+	t.tx[t.txc++] = 0x1E;
+
+	/* power up */
+	t.tx[t.txc++] = SWD_WR(DP_DPCTRL, 1);
+	t.tx[t.txc++] = (1 << 28) | (1 << 30);
+	t.tx[t.txc++] = SWD_RD(DP_DPCTRL, 1);
+	t.rx[t.rxc++] = &n;
+
+	/* configure for 32bit IO */
+	q_ap_write(&t, AHB_CSW,
+		AHB_CSW_MDEBUG | AHB_CSW_PRIV |
+		AHB_CSW_PRIV | AHB_CSW_DBG_EN | AHB_CSW_32BIT);
+	if (q_exec(&t))
+		return -1;
+
+	fprintf(stderr,"DPCTRL: %08x\n", n);
+	return 0;
+}
+
+void swdp_enable_tracing(int yes) {
+	struct txn t;
+	q_init(&t);
+	t.tx[t.txc++] = RSWD_MSG(CMD_TRACE, yes, 0);
+	q_exec(&t);
+}
+
+void swdp_target_reset(int enable) {
+	struct txn t;
+	q_init(&t);
+	t.tx[t.txc++] = RSWD_MSG(CMD_RESET, 0, enable);
+	q_exec(&t);
+}
+
+int swdp_set_clock(unsigned khz) {
+	struct txn t;
+	if (khz > 0xFFFF)
+		return -1;
+	if (khz < 1000)
+		khz = 1000;
+	q_init(&t);
+	t.tx[t.txc++] = RSWD_MSG(CMD_SET_CLOCK, 0, khz);
+	return q_exec(&t);
+}
+	
+int swdp_open(void) {
+	usb = usb_open(0x18d1, 0xdb03, 0);
+	if (usb == 0) {
+		usb = usb_open(0x18d1, 0xdb04, 0);
+	}
+	if (usb == 0) {
+		fprintf(stderr,"could not find device\n");
+		return -1;
+	}
+
+	swdp_enable_tracing(0);
+	swdp_reset();
+	return 0;
+}
diff --git a/tools/rswdp.h b/tools/rswdp.h
@@ -0,0 +1,61 @@
+/* rswdp.h
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _RSWDP_H__
+#define _RSWDP_H__
+
+int swdp_ahb_read(u32 addr, u32 *value);
+int swdp_ahb_write(u32 addr, u32 value);
+
+/* bulk reads/writes (more efficient after ~3-4 words */
+int swdp_ahb_read32(u32 addr, u32 *out, int count);
+int swdp_ahb_write32(u32 addr, u32 *out, int count);
+
+/* return 0 when *addr != oldval, -1 on error, -2 on interrupt */
+int swdp_ahb_wait_for_change(u32 addr, u32 oldval);
+
+int swdp_core_halt(void);
+int swdp_core_step(void);
+int swdp_core_resume(void);
+
+/* return 0 when CPU halts, -1 if an error occurs, or -2 if interrupted */
+int swdp_core_wait_for_halt(void);
+void swdp_interrupt(void);
+
+/* access to CPU registers */
+int swdp_core_read(u32 n, u32 *v);
+int swdp_core_read_all(u32 *v);
+int swdp_core_write(u32 n, u32 v);
+
+int swdp_watchpoint_pc(unsigned n, u32 addr);
+int swdp_watchpoint_rd(unsigned n, u32 addr);
+int swdp_watchpoint_wr(unsigned n, u32 addr);
+int swdp_watchpoint_rw(unsigned n, u32 addr);
+
+int swdp_reset(void);
+
+int swdp_open(void);
+
+void swdp_enable_tracing(int yes);
+
+void swdp_target_reset(int enable);
+
+int swdp_bootloader(void);
+int swdp_set_clock(unsigned khz);
+
+#endif
+
diff --git a/tools/stm32boot.c b/tools/stm32boot.c
@@ -0,0 +1,381 @@
+/* stm32boot.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include <termios.h>
+#include <signal.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/types.h>
+
+#if 0
+int WRITE(int fd, void *ptr, int len) {
+	int r;
+	fprintf(stderr,"[");
+	for (r = 0; r < len; r++) 
+		fprintf(stderr," %02x", ((unsigned char *) ptr)[r]);
+	fprintf(stderr," ]\n");
+	return write(fd, ptr, len);
+}
+
+int READ(int fd, void *ptr, int len) {
+	int n, r;
+	r = read(fd, ptr, len);
+	if (r <= 0) {
+		fprintf(stderr,"<XX>\n");
+		return r;
+	}
+	fprintf(stderr,"<");
+	for (n = 0; n < len; n++) 
+		fprintf(stderr," %02x", ((unsigned char *) ptr)[n]);
+	fprintf(stderr," >\n");
+	return r;
+}
+
+#define read READ
+#define write WRITE
+#endif
+
+int openserial(const char *device, int speed)
+{
+	struct termios tio;
+	int fd;
+	fd = open(device, O_RDWR | O_NOCTTY);// | O_NDELAY);
+
+	if (fd < 0)
+		return -1;
+
+	if (tcgetattr(fd, &tio))
+		memset(&tio, 0, sizeof(tio));
+
+	tio.c_cflag = B57600 | CS8 | CLOCAL | CREAD | PARENB;
+	tio.c_ispeed = B57600;
+	tio.c_ospeed = B57600;
+	tio.c_iflag = IGNPAR;
+	tio.c_oflag = 0;
+	tio.c_lflag = 0; /* turn of CANON, ECHO*, etc */
+	tio.c_cc[VTIME] = 1;
+	tio.c_cc[VMIN] = 0;
+	tcsetattr(fd, TCSANOW, &tio);
+	tcflush(fd, TCIFLUSH);
+
+#ifdef __APPLE__
+	tio.c_cflag =  CS8 | CLOCAL | CREAD | PARENB;
+#else
+	tio.c_cflag =  speed | CS8 | CLOCAL | CREAD | PARENB;
+#endif
+	tio.c_ispeed = speed;
+	tio.c_ospeed = speed;
+
+	tcsetattr(fd, TCSANOW, &tio);
+	tcflush(fd, TCIFLUSH);
+	return fd;
+}
+
+#define ACK 0x79
+#define NAK 0x1F
+
+int readAck(int fd) { /* 3s timeout */
+	int n;
+	unsigned char x;
+	for (n = 0; n < 30; n++) {
+		if (read(fd, &x, 1) == 1) {
+			if (x == ACK)
+				return 0;
+			fprintf(stderr,"?? %02x\n",x);	
+			return -1;
+		}
+	}
+	fprintf(stderr,"*TIMEOUT*\n");
+	return -1;
+}
+
+int sendCommand(int fd, unsigned cmd) {
+	unsigned char data[2];
+	data[0] = cmd;
+	data[1] = cmd ^ 0xFF;
+	if (write(fd, data, 2) != 2)
+		return -1;
+	if (readAck(fd))
+		return -1;
+	return 0;
+}
+
+int sendAddress(int fd, unsigned addr) {
+	unsigned char data[5];
+	data[0] = addr >> 24;
+	data[1] = addr >> 16;
+	data[2] = addr >> 8;
+	data[3] = addr;
+	data[4] = data[0] ^ data[1] ^ data[2] ^ data[3];
+	if (write(fd, data, 5) != 5)
+		return -1;
+	if (readAck(fd))
+		return -1;
+	return 0;
+}
+
+int sendLength(int fd, unsigned len) {
+	unsigned char data[2];
+	if ((len < 1) || (len > 256))
+		return -1;
+	len--;
+	data[0] = len;
+	data[1] = len ^ 0xFF;
+	if (write(fd, data, 2) != 2)
+		return -1;
+	if (readAck(fd))
+		return -1;
+	return 0;
+}
+
+int sendData(int fd, void *ptr, unsigned len)
+{
+	unsigned char x;
+	unsigned char check;
+	unsigned char *data = ptr;
+	unsigned n;
+
+	if ((len < 1) || (len > 256))
+		return -1;
+	check = x = (len - 1);
+	for (n = 0; n < len; n++) {
+		check ^= data[n];
+	}
+	if (write(fd, &x, 1) != 1)
+		return -1;
+	if (write(fd, data, len) != len)
+		return -1;
+	if (write(fd, &check, 1) != 1)
+		return -1;
+	if (readAck(fd))
+		return -1;
+	return 0;	
+}
+
+int readData(int fd, void *ptr, int len)
+{
+	unsigned char *data = ptr;
+	int r;
+	while (len > 0) {
+		r = read(fd, data, len);
+		if (r <= 0) {
+			fprintf(stderr,"*UNDERFLOW*\n");
+			return -1;
+		}
+		len -= r;
+		data += r;
+	}
+	return 0;
+}
+
+int readMemory(int fd, unsigned addr, void *ptr, int len)
+{
+	unsigned char *data = ptr;
+	while (len > 0) {
+		int xfer = (len > 256) ? 256 : len;
+		fprintf(stderr,"read %04x at %08x -> %p\n", xfer, addr, data);
+		if (sendCommand(fd, 0x11))
+			return -1;
+		if (sendAddress(fd, addr))
+			return -1;
+		if (sendLength(fd, xfer))
+			return -1;
+		if (readData(fd, data, xfer))
+			return -1;
+		data += xfer;
+		len -= xfer;
+		addr += xfer;
+	}
+	return 0;
+}
+
+int writeMemory(int fd, unsigned addr, void *ptr, int len)
+{
+	unsigned char *data = ptr;
+	while (len > 0) {
+		int xfer = (len > 256) ? 256 : len;
+		if (sendCommand(fd, 0x31))
+			return -1;
+		if (sendAddress(fd, addr))
+			return -1;
+		if (sendData(fd, data, xfer))
+			return -1;
+		data += xfer;
+		len -= xfer;
+		addr += xfer;
+	}
+	return 0;
+}
+
+int eraseFlash(int fd)
+{
+	unsigned data[2] = { 0xFF, 0x00 };
+	if (sendCommand(fd, 0x43))
+		return -1;
+	if (write(fd, data, 2) != 2)
+		return -1;
+	if (readAck(fd))
+		return -1;
+	return 0;
+}
+
+int jumpToAddress(int fd, unsigned addr)
+{
+	if (sendCommand(fd, 0x21))
+		return -1;
+	if (sendAddress(fd, addr))
+		return -1;
+	return 0;
+}
+
+int usage(void)
+{
+	fprintf(stderr,
+		"usage: stm32boot [ erase | flash <file> | exec <file> ]\n");
+	return -1;
+}
+int main(int argc, char *argv[])
+{
+	int speed = B115200;
+	const char *device = "/dev/ttyUSB0";
+	unsigned char x;
+	int fd, n;
+	unsigned char buf[32768];
+
+	unsigned do_erase = 0;
+	unsigned do_write = 0;
+	unsigned do_exec = 0;
+	unsigned addr = 0;
+
+	if (argc < 2)
+		return usage();
+
+	if (!strcmp(argv[1],"erase")) {
+		do_erase = 1;
+	} else if (!strcmp(argv[1],"flash")) {
+		do_erase = 1;
+		do_write = 1;
+		addr = 0x08000000;
+	} else if (!strcmp(argv[1],"exec")) {
+		do_write = 1;
+		do_exec = 1;
+		addr = 0x20001000;
+	} else {
+		return usage();
+	}
+
+	if (do_write && argc != 3)
+		return usage();
+
+	fd = openserial(device, speed);
+	if (fd < 0) {
+		fprintf(stderr, "stderr open '%s'\n", device);
+		return -1;
+	}
+
+	n = TIOCM_DTR;
+	ioctl(fd, TIOCMBIS, &n);
+	usleep(2500);
+	ioctl(fd, TIOCMBIC, &n);
+	usleep(2500);
+
+	/* If the board just powered up, we need to send an ACK
+	 * to auto-baud and will get an ACK back.  If the board
+	 * is already up, two ACKs will get a NAK (invalid cmd).
+	 * Either way, we're talking!
+	 */
+	for (n = 0; n < 5; n++) {
+		unsigned char SYNC = 0x7F;
+		if (write(fd, &SYNC, 1)) { /* do nothing */ }
+		if (read(fd, &x, 1) != 1)
+			continue;
+		if ((x == 0x79) || (x == 0x1f))
+			break;
+	}
+	if (n == 5) {
+		fprintf(stderr,"sync failure\n");
+		return -1;
+	}
+
+#if 0
+	readMemory(fd, 0x1FFFF000, buf, 4096);
+	for (n = 0; n < 1024; n++) 
+		fprintf(stderr,"%02x ", buf[n]);
+	return 0;
+#endif
+	if (do_write) {
+		int fd2 = open(argv[2], O_RDONLY);
+		n = read(fd2, buf, sizeof(buf));
+		close(fd2);
+		if ((fd2 < 0) || (n <= 0)) {
+			fprintf(stderr,"cannot read '%s'\n", argv[2]);
+			return -1;
+		}
+		n += (n % 4);
+
+
+		if (do_erase) {
+			fprintf(stderr,"erasing flash...\n");
+			if (eraseFlash(fd)) {
+				fprintf(stderr,"erase failed\n");
+				return -1;
+			}
+		}
+
+		fprintf(stderr,"sending %d bytes...\n", n);
+		if (writeMemory(fd, addr, buf, n)) {
+			fprintf(stderr,"write failed\n");
+			return -1;
+		}
+		fprintf(stderr,"done\n");
+
+		if (do_exec) {
+			jumpToAddress(fd, addr);
+		} else {
+			return 0;
+		}
+	} else if (do_erase) {
+		if (eraseFlash(fd)) {
+			fprintf(stderr,"erase failed\n");
+			return -1;
+		}
+		fprintf(stderr,"flash erased\n");
+		return 0;
+	}
+	
+	for (;;) {
+		if (read(fd, &x, 1) == 1) {
+			if (x == 27) break;
+			if ((x < 0x20) || (x > 0x7f))
+				if ((x != 10) && (x != 13))
+					x = '.';
+			fprintf(stderr,"%c", x);
+		}
+	}
+	
+	return 0;
+}
diff --git a/tools/uconsole.c b/tools/uconsole.c
@@ -0,0 +1,58 @@
+/* uconsole.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdint.h>
+
+#include "usb.h"
+
+int main(int argc, char **argv) {
+	int r, once;
+	usb_handle *usb;
+	char buf[64];
+
+	once = 1;
+	for (;;) {
+		usb = usb_open(0x18d1, 0xdb05, 0);
+		if (usb == 0) {
+			usb = usb_open(0x18d1, 0xdb04, 1);
+		}
+		if (usb == 0) {
+			if (once) {
+				fprintf(stderr,"waiting for device...\n");
+				once = 0;
+			}
+		} else {
+			break;
+		}
+	}
+
+	fprintf(stderr,"connected\n");
+
+	for (;;) {
+		r = usb_read(usb, buf, 64);
+		if (r < 0)
+			break;
+		if (write(1, buf, r)) { /* do nothing */ }
+	}
+
+	return 0;
+}
diff --git a/tools/usb.c b/tools/usb.c
@@ -0,0 +1,107 @@
+/* usb.c
+ *
+ * Copyright 2014 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <libusb-1.0/libusb.h>
+
+#include "usb.h"
+
+struct usb_handle {
+	libusb_device_handle *dev;
+	unsigned ei;
+	unsigned eo;
+};
+
+static libusb_context *usb_ctx = NULL;
+
+usb_handle *usb_open(unsigned vid, unsigned pid, unsigned ifc) {
+	usb_handle *usb;
+	int r;
+
+	if (usb_ctx == NULL) {
+		if (libusb_init(&usb_ctx) < 0) {
+			usb_ctx = NULL;
+			return NULL;
+		}
+	}
+
+	usb = malloc(sizeof(usb_handle));
+	if (usb == 0) {
+		return NULL;
+	}
+
+	/* TODO: extract from descriptors */
+	switch (ifc) {
+	case 0:
+		usb->ei = 0x81;
+		usb->eo = 0x01;
+		break;
+	case 1:
+		usb->ei = 0x82;
+		usb->eo = 0x02;
+		break;
+	default:
+		goto fail;
+	}
+		
+	usb->dev = libusb_open_device_with_vid_pid(usb_ctx, vid, pid);
+	if (usb->dev == NULL) {
+		goto fail;
+	}
+	// This causes problems on re-attach.  Maybe need for OSX?
+	// On Linux it's completely happy without us explicitly setting a configuration.
+	//r = libusb_set_configuration(usb->dev, 1);
+	r = libusb_claim_interface(usb->dev, ifc);
+	if (r < 0) {
+		fprintf(stderr, "failed to claim interface #%d\n", ifc);
+		goto close_fail;
+	}
+	
+	return usb;
+
+close_fail:
+	libusb_close(usb->dev);
+fail:
+	free(usb);
+	return NULL;
+}
+
+void usb_close(usb_handle *usb) {
+	libusb_close(usb->dev);
+	free(usb);
+}
+
+int usb_read(usb_handle *usb, void *data, int len) {
+	int xfer = len;
+	int r = libusb_bulk_transfer(usb->dev, usb->ei, data, len, &xfer, 5000);
+	if (r < 0) {
+		return -1;
+	}
+	return xfer;
+}
+
+int usb_write(usb_handle *usb, const void *data, int len) {
+	int xfer = len;
+	int r = libusb_bulk_transfer(usb->dev, usb->eo, (void*) data, len, &xfer, 5000);
+	if (r < 0) {
+		return -1;
+	}
+	return xfer;
+}
+
diff --git a/tools/usb.h b/tools/usb.h
@@ -0,0 +1,30 @@
+/* usb.h
+ *
+ * Copyright 2014 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _USB_H_
+#define _USB_H_
+
+typedef struct usb_handle usb_handle;
+
+/* simple usb api for devices with bulk in+out interfaces */
+
+usb_handle *usb_open(unsigned vid, unsigned pid, unsigned ifc);
+void usb_close(usb_handle *usb);
+int usb_read(usb_handle *usb, void *data, int len);
+int usb_write(usb_handle *usb, const void *data, int len);
+
+#endif
diff --git a/tools/usbmon.c b/tools/usbmon.c
@@ -0,0 +1,129 @@
+/* usbmon.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <errno.h>
+
+#include <sys/ioctl.h>
+#include <linux/ioctl.h>
+
+typedef unsigned long long u64;
+typedef signed long long s64;
+typedef int s32;
+typedef unsigned short u16;
+
+#include "usbmon.h"
+
+static char _xfer[4] = "SICB";
+
+int main(int argc, char **argv) 
+{
+	unsigned char data[4096];
+	struct usbmon_packet hdr;
+	struct usbmon_get arg;
+	unsigned char filter_dev[128];
+	int fd, r, n;
+
+	memset(filter_dev, 0, sizeof(filter_dev));
+
+	fd = open("/dev/usbmon0", O_RDONLY);
+	if (fd < 0) 
+		return -1;
+
+	argc--;
+	argv++;
+	while (argc--) {
+		if (argv[0][0] == '-') {
+			switch(argv[0][1]) {
+			case 'x':
+				r = atoi(argv[0] + 2);
+				if ((r < 0) || (r > 127))
+					continue;
+				filter_dev[r] = 1;
+				break;
+			}
+		}
+		argv++;
+	}
+
+	arg.hdr = &hdr;
+	arg.data = data;
+	for (;;) {
+		arg.alloc = sizeof(data);
+		r = ioctl(fd, MON_IOCX_GET, &arg);
+		if (r < 0)
+			break;
+		if (filter_dev[hdr.devnum])
+			continue;
+		printf("%d.%03d.%03d %c %c%c %04x",
+			hdr.busnum, hdr.devnum, hdr.epnum & 0x7F,
+			hdr.type,
+			_xfer[hdr.xfer], (hdr.epnum & 0x80) ? 'i' : 'o',
+#if 0
+			hdr.flag_setup ? hdr.flag_setup : ' ',
+			hdr.flag_data ? hdr.flag_data : ' ',
+#endif
+			hdr.length);
+		if (hdr.type == 'S') {
+			if (hdr.xfer == 2) {
+				printf(" %02x %02x %02x%02x %02x%02x %02x%02x\n",
+					hdr.s.setup[0], hdr.s.setup[1],
+					hdr.s.setup[3], hdr.s.setup[2],
+					hdr.s.setup[5], hdr.s.setup[4],
+					hdr.s.setup[7], hdr.s.setup[6]);
+			} else {
+				goto dumpdata;
+			}
+	
+		} else {
+			switch (hdr.status) {
+			case 0:
+				printf(" OK\n");
+				break;
+			case -EPIPE:
+				printf(" STALLED\n");
+				break;
+			case -ENODEV:
+				printf(" DISCONNECTED\n");
+				break;
+			case -ETIMEDOUT:
+				printf(" TIMEDOUT\n");
+				break;
+			default:
+				printf(" %s\n", strerror(-hdr.status));
+			}
+		}
+		if (!hdr.len_cap) 
+			continue;
+		printf("                   ");
+dumpdata:
+		if (hdr.len_cap > sizeof(data))
+			hdr.len_cap = sizeof(data);
+		for (n = 0; n < hdr.len_cap; n++) 
+			printf((n & 3) ? "%02x" : " %02x",data[n]);
+		printf(" ");
+		for (n = 0; n < hdr.len_cap; n++)
+			putchar(((data[n] < 0x20) || (data[n] > 0x7F)) ? '.' : data[n]);
+		printf("\n");
+	}
+	return 0;	
+}
+
diff --git a/tools/usbmon.h b/tools/usbmon.h
@@ -0,0 +1,44 @@
+/* Based on Documentation/usbmon.txt in the Linux Kernel */
+
+#ifndef _USBMON_H_
+#define _USBMON_H_
+
+struct usbmon_packet {
+	u64 id;			/* URB ID */
+	unsigned char type;	/* 'S'ubmit 'C'allback 'E'rror */
+	unsigned char xfer;	/* ISO=0 INT=1 CTRL=2 BULK=3 */
+	unsigned char epnum;
+	unsigned char devnum;
+	u16 busnum;
+	char flag_setup;
+	char flag_data;
+	s64 ts_sec;
+	s32 ts_usec;
+	int status;
+	unsigned int length;
+	unsigned int len_cap;
+	union {
+		unsigned char setup[8];
+		struct iso_rec {
+			int error_count;
+			int numdesc;
+		} iso;
+	} s;
+	int interval;
+	int start_frame;
+	unsigned int xfer_flags;
+	unsigned int ndesc;
+};
+
+struct usbmon_get {
+	struct usbmon_packet *hdr;
+	void *data;
+	size_t alloc;
+};
+
+#define MON_IOC_MAGIC	0x92
+
+#define MON_IOCX_GET	_IOW(MON_IOC_MAGIC, 6, struct usbmon_get)
+#define MON_IOCX_GETX	_IOW(MON_IOC_MAGIC, 10, struct usbmon_get)
+
+#endif
diff --git a/tools/usbmon.o b/tools/usbmon.o
Binary files differ.
diff --git a/tools/usbtest.c b/tools/usbtest.c
@@ -0,0 +1,50 @@
+/* rswdp.c
+ *
+ * Copyright 2011 Brian Swetland <swetland@frotz.net>
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "usb.h"
+
+int main(int argc, char **argv) {
+	usb_handle *usb;
+	char buf[4096];
+	int n, sz = 64;
+
+	if (argc > 1) {
+		sz = atoi(argv[1]);
+		if ((sz < 1) || (sz > 4096))
+			return -1;
+	}
+
+	usb = usb_open(0x18d1, 0xdb01, 0);
+	if (usb == 0) {
+		fprintf(stderr, "cannot find device\n");
+		return -1;
+	}
+
+	for (n = 0; n < sz; n++)
+		buf[n] = n;
+
+	usb_write(usb, buf, sz);
+	return 0;
+}
+
+	
+