blob: 7daa8b713e2f4fc5d2b6d11f947ef2cdc5800981 [file] [log] [blame]
/* rswd.c
*
* Copyright 2011-2015 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 <reg.h>
#include <debug.h>
#include <string.h>
#include <stdlib.h>
#include <printf.h>
#include <platform.h>
#include "swd.h"
#include "rswdp.h"
void usb_xmit(void *data, unsigned len);
int usb_recv(void *data, unsigned len);
unsigned swdp_trace = 0;
// indicates host knows about v1.0 protocol features
unsigned host_version = 0;
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,
};
static const char *board_str = TARGET;
static const char *build_str = "fw v0.91 (" __DATE__ ", " __TIME__ ")";
static void _reboot(void) {
platform_halt(HALT_ACTION_REBOOT, HALT_REASON_SW_RESET);
}
#define MODE_SWD 0
#define MODE_JTAG 1
static unsigned mode = MODE_SWD;
/* 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_MDEBUG_TRACE
printf("> %02x %02x %04x <\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--;
status = swd_write(optable[op], *rx++);
if (status) {
goto done;
}
}
continue;
case CMD_SWD_READ:
tx[txc++] = RSWD_MSG(CMD_SWD_DATA, 0, n);
while (n-- > 0) {
status = swd_read(optable[op], tx + txc);
if (status) {
txc++;
while (n-- > 0)
tx[txc++] = 0xfefefefe;
goto done;
}
txc++;
}
continue;
case CMD_SWD_DISCARD:
while (n-- > 0) {
u32 tmp;
status = swd_read(optable[op], &tmp);
if (status) {
goto done;
}
}
continue;
case CMD_ATTACH:
if (mode != MODE_SWD) {
mode = MODE_SWD;
swd_init();
}
swd_reset();
continue;
case CMD_JTAG_IO:
if (mode != MODE_JTAG) {
mode = MODE_JTAG;
jtag_init();
}
tx[txc++] = RSWD_MSG(CMD_JTAG_DATA, 0, n);
while (n > 0) {
unsigned xfer = (n > 32) ? 32 : n;
jtag_io(xfer, rx[0], rx[1], tx + txc);
rx += 2;
rxc -= 2;
txc += 1;
n -= xfer;
}
continue;
case CMD_JTAG_VRFY:
if (mode != MODE_JTAG) {
mode = MODE_JTAG;
jtag_init();
}
// (n/32) x 4 words: TMS, TDI, DATA, MASK
while (n > 0) {
unsigned xfer = (n > 32) ? 32 : n;
jtag_io(xfer, rx[0], rx[1], tx + txc);
if ((tx[txc] & rx[3]) != rx[2]) {
status = ERR_BAD_MATCH;
goto done;
}
rx += 4;
rxc -= 4;
n -= xfer;
}
continue;
case CMD_JTAG_TX: {
unsigned tms = (op & 1) ? 0xFFFFFFFF : 0;
if (mode != MODE_JTAG) {
mode = MODE_JTAG;
jtag_init();
}
while (n > 0) {
unsigned xfer = (n > 32) ? 32 : n;
jtag_io(xfer, tms, rx[0], rx);
rx++;
rxc--;
n -= xfer;
}
continue;
}
case CMD_JTAG_RX: {
unsigned tms = (op & 1) ? 0xFFFFFFFF : 0;
unsigned tdi = (op & 2) ? 0xFFFFFFFF : 0;
if (mode != MODE_JTAG) {
mode = MODE_JTAG;
jtag_init();
}
tx[txc++] = RSWD_MSG(CMD_JTAG_DATA, 0, n);
while (n > 0) {
unsigned xfer = (n > 32) ? 32 : n;
jtag_io(xfer, tms, tdi, tx + txc);
txc++;
n -= xfer;
}
continue;
}
case CMD_RESET:
swd_hw_reset(n);
continue;
case CMD_DOWNLOAD: {
//u32 *addr = (void*) *rx++;
rxc--;
while (n) {
//*addr++ = *rx++;
rx++;
rxc--;
}
continue;
}
case CMD_EXECUTE:
//func = (void*) *rx++;
rxc--;
continue;
case CMD_TRACE:
swdp_trace = op;
continue;
case CMD_BOOTLOADER:
func = _reboot;
continue;
case CMD_SET_CLOCK:
n = swd_set_clock(n);
printf("swdp clock is now %d KHz\n", n);
if (host_version >= RSWD_VERSION_1_0) {
tx[txc++] = RSWD_MSG(CMD_CLOCK_KHZ, 0, n);
}
continue;
case CMD_SWO_CLOCK:
n = swo_set_clock(n);
printf("swo clock is now %d KHz\n", n);
continue;
case CMD_VERSION:
host_version = n;
tx[txc++] = RSWD_MSG(CMD_VERSION, 0, RSWD_VERSION);
n = strlen(board_str);
memcpy(tx + txc + 1, board_str, n + 1);
n = (n + 4) / 4;
tx[txc++] = RSWD_MSG(CMD_BOARD_STR, 0, n);
txc += n;
n = strlen(build_str);
memcpy(tx + txc + 1, build_str, n + 1);
n = (n + 4) / 4;
tx[txc++] = RSWD_MSG(CMD_BUILD_STR, 0, n);
txc += n;
tx[txc++] = RSWD_MSG(CMD_RX_MAXDATA, 0, 8192);
txc += n;
continue;
default:
printf("unknown command %02x\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_MDEBUG_TRACE
printf("[ send %d words ]\n", txc);
for (n = 0; n < txc; n+=4) {
printx("%08x %08x %08x %08x\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 (;;) ;
}
}
// io buffers in AHB SRAM
static u32 *rxbuffer = (void*) 0x20001000;
static u32 *txbuffer[2] = {(void*) 0x20003000, (void*) 0x20005000 };
#include <kernel/thread.h>
void handle_rswd(void) {
int rxc;
int toggle = 0;
#if CONFIG_MDEBUG_TRACE
printf("[ rswdp agent v0.9 ]\n");
printf("[ built " __DATE__ " " __TIME__ " ]\n");
#endif
for (;;) {
rxc = usb_recv(rxbuffer, 8192);
#if CONFIG_MDEBUG_TRACE
int n;
printx("[ recv %d words ]\n", rxc/4);
for (n = 0; n < (rxc/4); n+=4) {
printx("%08x %08x %08x %08x\n",
rxbuffer[n], rxbuffer[n+1],
rxbuffer[n+2], rxbuffer[n+3]);
}
#endif
if ((rxc < 4) || (rxc & 3)) {
printf("error, runt frame, or strange frame... %d\n", rxc);
continue;
}
rxc = rxc / 4;
if ((rxbuffer[0] & 0xFFFF0000) != 0xAA770000) {
printf("invalid frame %x\n", rxbuffer[0]);
continue;
}
process_txn(rxbuffer[0], rxbuffer + 1, rxc - 1, txbuffer[toggle]);
toggle ^= 1;
}
}