commit c55fc4018dc5b709bcaffbcd8cd7c05b9fc2b0b8 Author: Werner Almesberger Date: Fri Aug 13 08:46:38 2010 -0300 f32x/ - firmware uploader for the C2 protocol. From IDBG. diff --git a/f32x/Makefile b/f32x/Makefile new file mode 100644 index 0000000..b66cf7f --- /dev/null +++ b/f32x/Makefile @@ -0,0 +1,51 @@ +# +# f32x/Makefile - Build the C8051F326/7 Flash programmer +# +# Written 2008 by Werner Almesberger +# Copyright 2008 Werner Almesberger +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# + + +CC=arm-angstrom-linux-gnueabi-gcc + +CFLAGS=-Wall -Wshadow -g -O +LDFLAGS= + +PREFIX=/usr + +NAME=f32x +OBJS=f32x.o flash.o c2.o gpio.o rt.o boundary.o + +.PHONY: all install uninstall clean depend spotless + +all: $(NAME) + +$(NAME): $(OBJS) + +upload: + ssh lab neo 'cat \>f32x' .depend || \ + { rm -f .depend; exit 1; } + +ifeq (.depend,$(wildcard .depend)) +include .depend +endif + +clean: + rm -f $(OBJS) .depend + +spotless: clean + rm -f $(NAME) diff --git a/f32x/README b/f32x/README new file mode 100644 index 0000000..c8893b0 --- /dev/null +++ b/f32x/README @@ -0,0 +1,13 @@ +Targets: C8051F326/C8051F327 +Programmer: OpenMoko GTA01/GTA02 with a debug v2 board + +To do: +- support C8051F320/1 as well +- port to Ben NanoNote + +Signal DbgV3 Net GPIO V0: C2 V1: C2 +------ ----- ------- ---- ------ ------ +CLK 3 SPI_CLK0 E13 C2CK C2D +MOSI 4 SPI_MOSI0 E12 C2D - +MISO 5 SPI_MISO0 E11 - C2CK +nSS 6 SS0 G2 - - diff --git a/f32x/boundary.c b/f32x/boundary.c new file mode 100644 index 0000000..a82ade2 --- /dev/null +++ b/f32x/boundary.c @@ -0,0 +1,129 @@ +/* + * f32x/boundary.c - I/O pin access + * + * Written 2008 by Werner Almesberger + * Copyright 2008 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include +#include +#include + +#include "c2.h" +#include "boundary.h" + + +#define P0 0x80 +#define P0MDOUT 0xa4 +#define P2 0xa0 +#define P2MDOUT 0xa6 + + +static uint8_t reg_read(uint8_t addr) +{ + c2_addr_write(addr); + return c2_data_read(1); +} + + +static void reg_write(uint8_t addr, uint8_t value) +{ + c2_addr_write(addr); + c2_data_write(value, 1); +} + + +static const char *parse(const char *s, uint8_t *port, uint8_t *mode, int bits) +{ + int pos; + + *port = *mode = 0; + pos = 0; + while (pos != bits) { + switch (*s++) { + case '.': + continue; + case '1': + *port |= 1 << pos; + /* fall through */ + case '0': + *mode |= 1 << pos; + break; + case 'r': + case 'R': + *port |= 1 << pos; + break; + case 0: + fprintf(stderr, "not enough pin settings\n"); + exit(1); + default: + fprintf(stderr, "unrecognized pin setting \"%c\"\n", s[-1]); + exit(1); + } + pos++; + } + return s; +} + + +static void setup(const char *init) +{ + uint8_t p0, p0mdout, p2, p2mdout; + + init = parse(init, &p0, &p0mdout, 8); + init = parse(init, &p2, &p2mdout, 6); + if (*init) { + fprintf(stderr, "too many pin settings\n"); + exit(1); + } + reg_write(P0, p0); + reg_write(P0MDOUT, p0mdout); + reg_write(P2, p2); + reg_write(P2MDOUT, p2mdout); +} + + +static void print(uint8_t v, int bits) +{ + int pos; + + for (pos = 0; pos != bits; pos++) + putchar(v & (1 << pos) ? '1' : '0'); +} + + +static void scan(void) +{ + uint8_t p0, p2; + + p0 = reg_read(P0); + p2 = reg_read(P2); + print(p0, 8); + putchar('.'); + print(p2, 6); + putchar('\n'); +} + + +static void __attribute__((unused)) dump(void) +{ + fprintf(stderr, "GPIOCN %02x\n", reg_read(0xe2)); + fprintf(stderr, " P0 %02x\n", reg_read(P0)); + fprintf(stderr, " P0MDOUT %02x\n", reg_read(P0MDOUT)); + fprintf(stderr, " P2 %02x\n", reg_read(P2)); + fprintf(stderr, " P2MDOUT %02x\n", reg_read(P2MDOUT)); +} + + +void boundary(const char *init) +{ +//dump(); + setup(init); + scan(); +} diff --git a/f32x/boundary.h b/f32x/boundary.h new file mode 100644 index 0000000..a75eb5c --- /dev/null +++ b/f32x/boundary.h @@ -0,0 +1,19 @@ +/* + * f326xboundary.h - I/O pin access + * + * Written 2008 by Werner Almesberger + * Copyright 2008 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#ifndef BOUNDARY_H +#define BOUNDARY_H + +void boundary(const char *init); + +#endif /* !BOUNDARY_H */ diff --git a/f32x/c2.c b/f32x/c2.c new file mode 100644 index 0000000..0b4e738 --- /dev/null +++ b/f32x/c2.c @@ -0,0 +1,138 @@ +/* + * f32x/c2.c - Basic C2 messages + * + * Written 2008 by Werner Almesberger + * Copyright 2008 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include +#include + +#include "gpio.h" +#include "c2.h" + + +/* + * SPI GPIOs are the same on 2410 and 2442, so this should work on GTA01 and + * on GTA02. + */ + +#define C2CK 4, 11 /* E13 = SPI_MISO0 */ +#define C2D 4, 13 /* E12 = SPI_CLK0 */ + + +/* ----- Bit-level operations ---------------------------------------------- */ + + +static void c2_pulse(void) +{ + gpio_low(C2CK); + gpio_high(C2CK); +} + + +static void c2_send(uint32_t value, int bits) +{ + int i; + + for (i = 0; i != bits; i++) { + gpio_set(C2D, (value >> i) & 1); + c2_pulse(); + } +} + + +static uint32_t c2_recv(int bits) +{ + uint32_t v = 0; + int i; + + for (i = 0; i != bits; i++) { + v |= gpio_get(C2D) << i; + c2_pulse(); + } + return v; +} + + +/* ----- C2 Register read/write -------------------------------------------- */ + + +void c2_addr_write(uint8_t addr) +{ + c2_pulse(); + gpio_output(C2D); + c2_send(C2_ADDR_WRITE, 2); + c2_send(addr, 8); + gpio_input(C2D); + c2_pulse(); +} + + +uint8_t c2_addr_read(void) +{ + c2_pulse(); + gpio_output(C2D); + c2_send(C2_ADDR_READ, 2); + gpio_input(C2D); + c2_pulse(); + return c2_recv(8); +} + + +void c2_data_write(uint32_t data, int bytes) +{ + c2_pulse(); + gpio_output(C2D); + c2_send(C2_DATA_WRITE, 2); + c2_send(bytes-1, 2); + c2_send(data, 8*bytes); + gpio_input(C2D); + c2_pulse(); + while (!c2_recv(1)); +} + + +uint32_t c2_data_read(int bytes) +{ + c2_pulse(); + gpio_output(C2D); + c2_send(C2_DATA_READ, 2); + c2_send(bytes-1, 2); + gpio_input(C2D); + c2_pulse(); + while (!c2_recv(1)); + return c2_recv(8*bytes); +} + + +/* ----- C2 initialization ------------------------------------------------- */ + + +void c2_init(void) +{ + gpio_init(); + gpio_input(C2D); + gpio_output(C2CK); + gpio_low(C2CK); + usleep(20); + gpio_high(C2CK); + usleep(2); +} + + +void c2_reset(void) +{ + gpio_input(C2D); + gpio_low(C2CK); + usleep(20); +// gpio_input(C2CK); + gpio_output(C2CK); + gpio_high(C2CK); +} diff --git a/f32x/c2.h b/f32x/c2.h new file mode 100644 index 0000000..2fd8739 --- /dev/null +++ b/f32x/c2.h @@ -0,0 +1,35 @@ +/* + * f32x/c2.h - Basic C2 messages + * + * Written 2008 by Werner Almesberger + * Copyright 2008 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#ifndef C2_H +#define C2_H + + +#include + + +#define C2_DATA_READ 0 +#define C2_DATA_WRITE 1 +#define C2_ADDR_READ 2 +#define C2_ADDR_WRITE 3 + + +void c2_addr_write(uint8_t addr); +uint8_t c2_addr_read(void); +void c2_data_write(uint32_t data, int bytes); +uint32_t c2_data_read(int bytes) ; + +void c2_init(void); +void c2_reset(void); + +#endif /* !C2_H */ diff --git a/f32x/f32x.c b/f32x/f32x.c new file mode 100644 index 0000000..c631ce2 --- /dev/null +++ b/f32x/f32x.c @@ -0,0 +1,226 @@ +/* + * f32x/f32x.c - Simple C8051F326/7 Flash programmer + * + * Written 2008, 2009 by Werner Almesberger + * Copyright 2008, 2009 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include +#include +#include +#include +#include + +#include "c2.h" +#include "flash.h" +#include "boundary.h" + + +#define LOCK_BYTE 0x3dff + + +static size_t file_size; + + +static void dump(const char *title, void *data, size_t size) +{ + int i, j; + + fprintf(stderr, "%s:\n", title); + for (i = 0; i < size; i += 16) { + fprintf(stderr, " %04x", i); + for (j = 0; j != 16 && i+j < size; j++) + fprintf(stderr, " %02x", ((uint8_t *) data)[i+j]); + fprintf(stderr, "\n"); + } +} + + +static void flash_device(void *data, size_t size) +{ + int i; + size_t len; + uint8_t buf[256]; + + for (i = 0; i < size; i += 256) + fputc('-', stderr); + fputc('\r', stderr); + + flash_init(); + flash_device_erase(); + + for (i = 0; i < size; i += 256) { + fputc('*', stderr); + fflush(stderr); + len = size-i <= 256 ? size-i : 256; + flash_block_write(i, data+i, len); + } + fputc('\r', stderr); + + for (i = 0; i < size; i += 256) { + fputc('#', stderr); + fflush(stderr); + len = size-i <= 256 ? size-i : 256; + flash_block_read(i, buf, len); + if (memcmp(buf, data+i, len)) { + fprintf(stderr, "compare error at 0x%04x\n", i); + dump("Expected", data+i, len); + dump("Read", buf, len); + exit(1); + } + } + fputc('\n', stderr); +} + + +static void erase_flash(void) +{ + flash_init(); + flash_device_erase(); +} + + +static void dump_flash(size_t size) +{ + int i, j; + size_t len; + uint8_t buf[256], last[256]; + int skipping = 0; + + flash_init(); + for (i = 0; i < size; i += 16) { + len = size-i <= 16 ? size-i : 16; + flash_block_read(i, buf, len); + if (i && !memcmp(last, buf, len)) { + printf("%04x: *%c", i, skipping ? '\r' : '\n'); + fflush(stdout); + skipping = 1; + continue; + } + skipping = 0; + memcpy(last, buf, len); + printf("%04x:", i); + for (j = 0; j != len; j++) + printf(" %02x", buf[j]); + printf(" "); + for (j = 0; j != len; j++) + printf("%c", buf[j] >= ' ' && buf[j] <= '~' ? buf[j] : '.'); + putchar('\n'); + fflush(stdout); + } +} + + +static void identify(void) +{ + int i; + + c2_addr_write(0); + printf("Dev"); + for (i = 0; i != 10; i++) + printf(" 0x%02x", c2_data_read(1)); + c2_addr_write(1); + printf("\nRev"); + for (i = 0; i != 10; i++) + printf(" 0x%02x", c2_data_read(1)); + printf("\n"); +} + + +static void do_flash(const char *name) +{ + FILE *file; + uint8_t code[16384]; + + file = fopen(name, "r"); + if (!file) { + perror(name); + exit(1); + } + file_size = fread(code, 1, sizeof(code), file); + (void) fclose(file); + flash_device(code, file_size); +} + + +static void protect(void) +{ + uint8_t pages, lock_byte; + + pages = (file_size+511) >> 9; + printf("Protecting %d page%s\n", pages, pages == 1 ? "" : "s"); + lock_byte = ~pages; + flash_block_write(LOCK_BYTE, &lock_byte, 1); +} + + +static void usage(const char *name) +{ + fprintf(stderr, +"usage: %s [-p] file\n" +" %s -d\n" +" %s -e\n" +" %s -b pin_setup\n" +" %s\n\n" +" -b pin_setup\n" +" Perform a boundary scan. pin_setup sets all 14 pins in this order:\n" +" P0_0, P0_1, ..., P0_7, P2_0, ..., P2_5.\n" +" Pins can be set to 0, 1, or R (pull-up). Dots can be used to structure\n" +" the bit string. Prints what the pins read back (0 or 1) in the same\n" +" order, with a dot between P0 and P2.\n" +" -d dump Flash content\n" +" -e erase whole Flash\n" +" -p Protect the data after writing\n" +"Invocation without argument resets the F32x.\n" + , name, name, name, name, name); + exit(1); +} + + +int main(int argc, char **argv) +{ + c2_init(); + identify(); + + switch (argc) { + case 1: + /* just reset */ + break; + case 2: + if (!strcmp(argv[1], "-d")) + dump_flash(0x4000); + else if (!strcmp(argv[1], "-e")) + erase_flash(); + else if (*argv[1] == '-') + usage(*argv); + else { + do_flash(argv[1]); + identify(); + } + break; + case 3: + if (!strcmp(argv[1], "-p")) { + if (*argv[2] == '-') + usage(*argv); + do_flash(argv[2]); + protect(); + identify(); + break; + } + if (strcmp(argv[1], "-b")) + usage(*argv); + boundary(argv[2]); + break; + default: + usage(*argv); + } + c2_reset(); + + return 0; +} diff --git a/f32x/flash.c b/f32x/flash.c new file mode 100644 index 0000000..2a6c1d9 --- /dev/null +++ b/f32x/flash.c @@ -0,0 +1,157 @@ +/* + * f32x/flash.c - Flash programming and reading + * + * Written 2008 by Werner Almesberger + * Copyright 2008 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include +#include +#include +#include + +#include "c2.h" +#include "flash.h" + + +/* ----- Helper functions for common flash protocol idioms ----------------- */ + + +static uint8_t _c2_addr_read(void) +{ + uint8_t x; + + usleep(1000); + x = c2_addr_read(); +// fprintf(stderr, "got 0x%02x\n", x); + return x; +} + + +static void wait_busy(void) +{ + while (_c2_addr_read() & InBusy); +} + + +static void wait_ready(void) +{ + while (!(_c2_addr_read() & OutReady)); +} + + +static void fpdat_write(uint8_t value) +{ + c2_data_write(value, 1); + wait_busy(); +} + + +static uint8_t fpdat_read(void) +{ + wait_ready(); + return c2_data_read(1); +} + + +static void check_status(void) +{ + uint8_t status; + + status = fpdat_read(); + if (status != FLASH_STATUS_OK) { + fprintf(stderr, "status 0x%02x\n", status); + exit(1); + } +} + + +/* ----- Block/device-level flash operations ------------------------------- */ + + +void flash_device_erase(void) +{ + c2_addr_write(FPDAT); + fpdat_write(FLASH_DEVICE_ERASE); + check_status(); + fpdat_write(FLASH_ERASE_MAGIC1); + fpdat_write(FLASH_ERASE_MAGIC2); + fpdat_write(FLASH_ERASE_MAGIC3); + check_status(); +} + + +void flash_block_write(uint16_t addr, const void *data, size_t size) +{ + int i; + + if (!size) + return; + c2_addr_write(FPDAT); + fpdat_write(FLASH_BLOCK_WRITE); + check_status(); + fpdat_write(addr >> 8); + fpdat_write(addr); + fpdat_write(size); + check_status(); + for (i = 0; i != size; i++) + fpdat_write(((uint8_t *) data)[i]); +} + + +void flash_block_read(uint16_t addr, void *data, size_t size) +{ + int i; + + if (!size) + return; + c2_addr_write(FPDAT); + fpdat_write(FLASH_BLOCK_READ); + check_status(); + fpdat_write(addr >> 8); + fpdat_write(addr); + fpdat_write(size); + check_status(); + for (i = 0; i != size; i++) + ((uint8_t *) data)[i] = fpdat_read(); +} + + +void flash_init(void) +{ + c2_addr_write(FPCTL); + c2_data_write(FLASH_INIT_MAGIC1, 1); + c2_data_write(FLASH_INIT_MAGIC2, 1); + usleep(20000); +} + + +/* @@@ doesn't really seem to work */ + +uint8_t fp_reg_read(uint8_t addr) +{ + c2_addr_write(FPDAT); + fpdat_write(REG_READ); + check_status(); + fpdat_write(addr); + fpdat_write(1); + return fpdat_read(); +} + + +void fp_reg_write(uint8_t addr, uint8_t value) +{ + c2_addr_write(FPDAT); + fpdat_write(REG_WRITE); + check_status(); + fpdat_write(addr); + fpdat_write(1); + fpdat_write(value); +} + diff --git a/f32x/flash.h b/f32x/flash.h new file mode 100644 index 0000000..e8df30c --- /dev/null +++ b/f32x/flash.h @@ -0,0 +1,53 @@ +/* + * f32x/flash.h - Flash programming and reading + * + * Written 2008 by Werner Almesberger + * Copyright 2008 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#ifndef FLASH_H +#define FLASH_H + +#include + + +#define FPCTL 0x02 +#define FPDAT 0xb4 + +#define FLASH_INIT_MAGIC1 0x02 +#define FLASH_INIT_MAGIC2 0x01 + +#define FLASH_ERASE_MAGIC1 0xde +#define FLASH_ERASE_MAGIC2 0xad +#define FLASH_ERASE_MAGIC3 0xa5 + +#define FLASH_DEVICE_ERASE 0x03 +#define FLASH_BLOCK_READ 0x06 +#define FLASH_BLOCK_WRITE 0x07 +#define FLASH_PAGE_ERASE 0x08 +#define REG_READ 0x09 +#define REG_WRITE 0x0a + +#define FLASH_STATUS_OK 0x0d + +#define InBusy (1 << 1) +#define OutReady (1 << 0) + + +void flash_device_erase(void); +void flash_block_write(uint16_t addr, const void *data, size_t size); +void flash_block_read(uint16_t addr, void *data, size_t size); +void flash_init(void); + +/* @@@ doesn't really seem to work */ + +uint8_t fp_reg_read(uint8_t addr); +void fp_reg_write(uint8_t addr, uint8_t value); + +#endif /* !FLASH_H */ diff --git a/f32x/gpio.c b/f32x/gpio.c new file mode 100644 index 0000000..1648d84 --- /dev/null +++ b/f32x/gpio.c @@ -0,0 +1,40 @@ +/* + * f32x/gpio.c - Really primitive S3C244x GPIO access. Ports B-H only. + * + * Written 2008 by Werner Almesberger + * Copyright 2008 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include +#include +#include +#include + + +#define BASE 0x56000000 + + +volatile void *mem; + + +void gpio_init(void) +{ + int fd; + + fd = open("/dev/mem", O_RDWR); + if (fd < 0) { + perror("/dev/mem"); + exit(1); + } + mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, BASE); + if (mem == MAP_FAILED) { + perror("mmap"); + exit(1); + } +} diff --git a/f32x/gpio.h b/f32x/gpio.h new file mode 100644 index 0000000..9afeb21 --- /dev/null +++ b/f32x/gpio.h @@ -0,0 +1,74 @@ +/* + * f32x/gpio.h - Really primitive S3C244x GPIO access. Ports B-H only. + * + * Written 2008 by Werner Almesberger + * Copyright 2008 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * Ports are numbered 0 = A, 1 = B, ... + */ + + +#ifndef GPIO_H +#define GPIO_H + + +#include + + +volatile uint32_t *mem; + + +#define port_dat(port) mem[port*4+1] +#define port_con(port) mem[port*4] + + +static inline void gpio_high(unsigned port, unsigned bit) +{ + port_dat(port) |= 1 << bit; +} + + +static inline void gpio_low(unsigned port, unsigned bit) +{ + port_dat(port) &= ~(1 << bit); +} + + +static inline void gpio_set(unsigned port, unsigned bit, int value) +{ + if (value) + gpio_high(port, bit); + else + gpio_low(port, bit); +} + + +static inline int gpio_get(unsigned port, unsigned bit) +{ + return (port_dat(port) >> bit) & 1; +} + + +static inline void gpio_output(unsigned port, unsigned bit) +{ + port_con(port) = (port_con(port) & ~(3 << bit*2)) | (1 << bit*2); +} + + +static inline void gpio_input(unsigned port, unsigned bit) +{ + port_con(port) &= ~(3 << bit*2); +} + + +void gpio_init(void); + + +#endif /* !GPIO_H */ diff --git a/f32x/rt.c b/f32x/rt.c new file mode 100644 index 0000000..cb3ba06 --- /dev/null +++ b/f32x/rt.c @@ -0,0 +1,56 @@ +/* + * f32x/rt.c - Enable/disable real-time scheduling priority + * + * Written 2008 by Werner Almesberger + * Copyright 2008 Werner Almesberger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +/* + * The full ritual would also include mlock'ing, but we skip that. + */ + + +#include +#include +#include + + +static void realtimize(void) +{ + struct sched_param prm; + + prm.sched_priority = sched_get_priority_max(SCHED_FIFO); + if (prm.sched_priority < 0) { + perror("sched_get_priority_max SCHED_FIFO"); + exit(1); + } + if (sched_setscheduler(0, SCHED_FIFO, &prm) < 0) { + perror("sched_setscheduler SCHED_FIFO"); + exit(1); + } +} + + +static void unrealtime(void) +{ + struct sched_param prm = { .sched_priority = 0 }; + + if (sched_setscheduler(0, SCHED_OTHER, &prm) < 0) { + perror("sched_setscheduler SCHED_OTHER"); + exit(1); + } +} + + +void rt(int on) +{ + if (on) + realtimize(); + else + unrealtime(); +}