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