mdebug

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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:
A.gitignore | 8++++++++
AAUTHORS | 8++++++++
ALICENSE | 202+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AMakefile | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
AREADME | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/arm-cm3/context.S | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/arm-cm3/include/arch/context.h | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/arm-cm3/include/arch/cpu.h | 16++++++++++++++++
Aarch/arm-cm3/include/arch/interrupts.h | 46++++++++++++++++++++++++++++++++++++++++++++++
Aarch/arm-cm3/nvic.c | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/arm-cm3/start.S | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/config.mk | 43+++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/gpio-v1.c | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/gpio-v2.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/iap.c | 48++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/include-v1/arch/iocon.h | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/include-v1/arch/irqs.h | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/include-v2/arch/iocon.h | 262+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/include-v2/arch/irqs.h | 34++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/include/arch/hardware.h | 255+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/include/arch/iap.h | 7+++++++
Aarch/lpc13xx/include/arch/ssp.h | 19+++++++++++++++++++
Aarch/lpc13xx/init.c | 117+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/reboot.c | 39+++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/serial.c | 91+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/ssp.c | 200+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/usb-v1.c | 604++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/usb-v1.h | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/usb-v2.c | 720+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/lpc13xx/usb-v2.h | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/stm32f1xx/config.mk | 14++++++++++++++
Aarch/stm32f1xx/gpio.c | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/stm32f1xx/include/arch/hardware.h | 219+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/stm32f1xx/include/arch/irqs.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/stm32f1xx/serial.c | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Aarch/stm32f1xx/usb.c | 415+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aboard/lpc-p1343.c | 18++++++++++++++++++
Aboard/m3debug.c | 29+++++++++++++++++++++++++++++
Aboard/m3radio1.c | 14++++++++++++++
Aboard/m3radio2.c | 14++++++++++++++
Abuild/build.mk | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abuild/generic-ram.ld | 32++++++++++++++++++++++++++++++++
Abuild/generic-rom.ld | 33+++++++++++++++++++++++++++++++++
Abuild/host-executable.mk | 43+++++++++++++++++++++++++++++++++++++++++++
Abuild/target-executable.mk | 119+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adocs/building-cross-compiler.txt | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adocs/memory-barriers.txt | 185+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adocs/notes.txt | 50++++++++++++++++++++++++++++++++++++++++++++++++++
Adocs/todo.txt | 33+++++++++++++++++++++++++++++++++
Adocs/usb-protocols.txt | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/fw/io.h | 15+++++++++++++++
Ainclude/fw/lib.h | 133+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/fw/string.h | 4++++
Ainclude/fw/types.h | 13+++++++++++++
Ainclude/protocol/rswdp.h | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ainclude/protocol/usb.h | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alibc/strcpy.c | 4++++
Alibfw/print.c | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alibfw/serialconsole.c | 25+++++++++++++++++++++++++
Alibfw/string.c | 21+++++++++++++++++++++
Alibfw/usbconsole.c | 122+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alpc13boot/flash-lpc13boot.scr | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alpc13boot/main.c | 243+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alpc13boot/misc.S | 8++++++++
Alpc13boot/module.mk | 10++++++++++
Am3debug/m3debug-schematic.pdf | 0
Am3debug/main.c | 343+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Am3debug/module.mk | 11+++++++++++
Am3debug/swdp.c | 175+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Am3debug/swdp.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/lpc13xx | 92+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ascripts/stm32f1xx | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aswdp/main.c | 279+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aswdp/module.mk | 9+++++++++
Aswdp/swdp.c | 187+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aswdp/swdp.h | 53+++++++++++++++++++++++++++++++++++++++++++++++++++++
Aswdp/unused.c | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/bless-lpc.c | 43+++++++++++++++++++++++++++++++++++++++++++
Atools/debugger-commands.c | 560+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/debugger-core.c | 386+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/debugger.c | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/debugger.h | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/gdb-bridge.c | 286+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/linenoise.c | 612+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/linenoise.h | 56++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/lpcboot.c | 148+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/module.mk | 46++++++++++++++++++++++++++++++++++++++++++++++
Atools/rswdp.c | 576+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/rswdp.h | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/stm32boot.c | 381+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/uconsole.c | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/usb.c | 107+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/usb.h | 30++++++++++++++++++++++++++++++
Atools/usbmon.c | 129+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/usbmon.h | 44++++++++++++++++++++++++++++++++++++++++++++
Atools/usbmon.o | 0
Atools/usbtest.c | 50++++++++++++++++++++++++++++++++++++++++++++++++++
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; +} + + +