123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329 |
- /**
- * Copyright (C) 2021, Mate Kukri <km@mkukri.xyz>
- * Copyright (C) 2023, 2024, Riku Viitanen <riku.viitanen@protonmail.com>
- * Based on "pico-serprog" by Thomas Roth <code@stacksmashing.net>
- *
- * Licensed under GPLv3
- *
- * Also based on stm32-vserprog:
- * https://github.com/dword1511/stm32-vserprog
- */
- #define DESCRIPTION "SPI flash chip programmer using Flashprog's serprog protocol"
- #define WEBSITE "https://codeberg.org/Riku_V/pico-serprog/"
- #include "pico/stdlib.h"
- #include "pico/binary_info.h"
- #include "hardware/spi.h"
- #include "tusb.h"
- #include "serprog.h"
- #define CDC_ITF 0 /* USB CDC interface no */
- #define SPI_IF spi0 /* Which PL022 to use */
- #define SPI_CS_0 5 /* The default CS pin */
- #define SPI_MISO 4
- #define SPI_MOSI 3
- #define SPI_SCK 2
- uint8_t spi_enabled = 0;
- uint cs_pin = SPI_CS_0;
- #define NUM_CS_AVAILABLE 4 /* Number of usable chip selects */
- uint baud = 12000000; /* Default to 12MHz */
- static const char progname[16] = "pico-serprog";
- /* Map of supported serprog commands */
- static const uint32_t cmdmap[8] = {
- (1 << S_CMD_NOP) |
- (1 << S_CMD_Q_IFACE) |
- (1 << S_CMD_Q_CMDMAP) |
- (1 << S_CMD_Q_PGMNAME) |
- (1 << S_CMD_Q_SERBUF) |
- (1 << S_CMD_Q_BUSTYPE) |
- (1 << S_CMD_SYNCNOP) |
- (1 << S_CMD_O_SPIOP) |
- (1 << S_CMD_S_BUSTYPE) |
- (1 << S_CMD_S_SPI_FREQ) |
- (1 << S_CMD_S_PIN_STATE) |
- (1 << S_CMD_S_SPI_CS)
- };
- static void use_cs(uint pin) {
- gpio_put(pin, 1);
- gpio_set_dir(pin, GPIO_OUT);
- gpio_set_drive_strength(pin, GPIO_DRIVE_STRENGTH_12MA);
- }
- static void pullup_cs(uint pin) {
- gpio_set_dir(pin, GPIO_IN);
- gpio_pull_up(pin);
- }
- static void enable_spi() {
- #ifdef PICO_DEFAULT_LED_PIN
- /* Setup status LED */
- gpio_init(PICO_DEFAULT_LED_PIN);
- gpio_set_dir(PICO_DEFAULT_LED_PIN, GPIO_OUT);
- #endif
- /* Setup default CS as output, others as inputs with pull-ups */
- for (uint8_t i = SPI_CS_0+1; i<SPI_CS_0+NUM_CS_AVAILABLE; i++) {
- gpio_init(i);
- pullup_cs(i);
- }
- gpio_init(cs_pin);
- use_cs(cs_pin);
- /* Setup PL022 */
- spi_init(SPI_IF, baud);
- gpio_set_function(SPI_MISO, GPIO_FUNC_SPI);
- gpio_set_function(SPI_MOSI, GPIO_FUNC_SPI);
- gpio_set_function(SPI_SCK, GPIO_FUNC_SPI);
- gpio_set_drive_strength(SPI_MISO, GPIO_DRIVE_STRENGTH_12MA);
- gpio_set_drive_strength(SPI_MOSI, GPIO_DRIVE_STRENGTH_12MA);
- gpio_set_drive_strength(SPI_SCK, GPIO_DRIVE_STRENGTH_12MA);
- spi_enabled = 1;
- }
- static void disable_pin(uint pin) {
- gpio_init(pin); /* Set pin to SIO input */
- gpio_set_pulls(pin, 0, 0); /* Disable all pulls */
- }
- static void disable_spi() {
- for (uint8_t i=0; i<NUM_CS_AVAILABLE; i++)
- disable_pin(SPI_CS_0 + i);
- disable_pin(SPI_MISO);
- disable_pin(SPI_MOSI);
- disable_pin(SPI_SCK);
- /* Disable SPI peripheral */
- spi_deinit(SPI_IF);
- spi_enabled = 0;
- }
- static inline void cs_select(uint cs_pin) {
- asm volatile("nop \n nop \n nop"); /* FIXME */
- gpio_put(cs_pin, 0);
- asm volatile("nop \n nop \n nop"); /* FIXME */
- }
- static inline void cs_deselect(uint cs_pin) {
- asm volatile("nop \n nop \n nop"); /* FIXME */
- gpio_put(cs_pin, 1);
- asm volatile("nop \n nop \n nop"); /* FIXME */
- }
- static void wait_for_read(void) {
- do
- tud_task();
- while (!tud_cdc_n_available(CDC_ITF));
- }
- static inline void readbytes_blocking(void *b, uint32_t len) {
- while (len) {
- wait_for_read();
- uint32_t r = tud_cdc_n_read(CDC_ITF, b, len);
- b += r;
- len -= r;
- }
- }
- static inline uint8_t readbyte_blocking(void) {
- wait_for_read();
- uint8_t b;
- tud_cdc_n_read(CDC_ITF, &b, 1);
- return b;
- }
- static void wait_for_write(void) {
- do {
- tud_task();
- } while (!tud_cdc_n_write_available(CDC_ITF));
- }
- static inline void sendbytes_blocking(const void *b, uint32_t len) {
- while (len) {
- wait_for_write();
- uint32_t w = tud_cdc_n_write(CDC_ITF, b, len);
- b += w;
- len -= w;
- }
- }
- static inline void sendbyte_blocking(uint8_t b) {
- wait_for_write();
- tud_cdc_n_write(CDC_ITF, &b, 1);
- }
- void s_cmd_s_bustype() {
- /* If SPI is among the requsted bus types we succeed,
- * fail otherwise */
- if ((uint8_t) readbyte_blocking() & (1 << 3))
- sendbyte_blocking(S_ACK);
- else
- sendbyte_blocking(S_NAK);
- }
- void s_cmd_o_spiop() {
- static uint8_t buf[4096];
- uint32_t wlen, rlen;
- readbytes_blocking(&wlen, 3);
- readbytes_blocking(&rlen, 3);
- cs_select(cs_pin);
- while (wlen) {
- uint32_t cur = MIN(wlen, sizeof buf);
- readbytes_blocking(buf, cur);
- spi_write_blocking(SPI_IF, buf, cur);
- wlen -= cur;
- }
- sendbyte_blocking(S_ACK);
- while (rlen) {
- uint32_t cur = MIN(rlen, sizeof buf);
- spi_read_blocking(SPI_IF, 0, buf, cur);
- sendbytes_blocking(buf, cur);
- rlen -= cur;
- }
- cs_deselect(cs_pin);
- }
- void s_cmd_s_spi_freq() {
- uint32_t want_baud;
- readbytes_blocking(&want_baud, 4);
- if (want_baud) {
- /* Set frequency */
- baud = spi_set_baudrate(SPI_IF, want_baud);
- /* Send back actual value */
- sendbyte_blocking(S_ACK);
- sendbytes_blocking(&baud, 4);
- } else {
- /* 0 Hz is reserved */
- sendbyte_blocking(S_NAK);
- }
- }
- void s_cmd_s_pin_state() {
- if (readbyte_blocking())
- enable_spi();
- else
- disable_spi();
- sendbyte_blocking(S_ACK);
- }
- void s_cmd_s_spi_cs() {
- uint8_t cs = readbyte_blocking();
- if (cs >= NUM_CS_AVAILABLE)
- sendbyte_blocking(S_NAK);
- return;
- cs += SPI_CS_0;
- if (spi_enabled) {
- if (cs_pin != cs) {
- pullup_cs(cs_pin);
- use_cs(cs);
- }
- }
- cs_pin = cs;
- sendbyte_blocking(S_ACK);
- }
- static void command_loop() {
- while (1) {
- uint8_t cmd = readbyte_blocking();
- #ifdef PICO_DEFAULT_LED_PIN
- gpio_put(PICO_DEFAULT_LED_PIN, 1);
- #endif
- switch (cmd) {
- case S_CMD_NOP:
- sendbyte_blocking(S_ACK);
- break;
- case S_CMD_Q_IFACE:
- sendbyte_blocking(S_ACK);
- sendbyte_blocking(0x01);
- sendbyte_blocking(0x00);
- break;
- case S_CMD_Q_CMDMAP:
- sendbyte_blocking(S_ACK);
- sendbytes_blocking((uint8_t *)cmdmap, sizeof(cmdmap));
- break;
- case S_CMD_Q_PGMNAME:
- sendbyte_blocking(S_ACK);
- sendbytes_blocking(progname, sizeof(progname));
- break;
- case S_CMD_Q_SERBUF:
- sendbyte_blocking(S_ACK);
- sendbyte_blocking(0xFF);
- sendbyte_blocking(0xFF);
- break;
- case S_CMD_Q_BUSTYPE:
- sendbyte_blocking(S_ACK);
- sendbyte_blocking((1 << 3)); /* SPI */
- break;
- case S_CMD_SYNCNOP:
- sendbyte_blocking(S_NAK);
- sendbyte_blocking(S_ACK);
- break;
- case S_CMD_S_BUSTYPE:
- s_cmd_s_bustype();
- break;
- case S_CMD_O_SPIOP:
- s_cmd_o_spiop();
- break;
- case S_CMD_S_SPI_FREQ:
- s_cmd_s_spi_freq();
- break;
- case S_CMD_S_PIN_STATE:
- s_cmd_s_pin_state();
- break;
- case S_CMD_S_SPI_CS:
- s_cmd_s_spi_cs();
- break;
- default:
- sendbyte_blocking(S_NAK);
- }
- tud_cdc_n_write_flush(CDC_ITF);
- #ifdef PICO_DEFAULT_LED_PIN
- gpio_put(PICO_DEFAULT_LED_PIN, 0);
- #endif
- }
- }
- int main() {
- /* Metadata for picotool */
- bi_decl(bi_program_description(DESCRIPTION));
- bi_decl(bi_program_url(WEBSITE));
- #ifdef PICO_DEFAULT_LED_PIN
- bi_decl(bi_1pin_with_name(PICO_DEFAULT_LED_PIN, "Activity LED"));
- #endif
- bi_decl(bi_1pin_with_name(SPI_MISO, "MISO"));
- bi_decl(bi_1pin_with_name(SPI_MOSI, "MOSI"));
- bi_decl(bi_1pin_with_name(SPI_SCK, "SCK"));
- bi_decl(bi_1pin_with_name(SPI_CS_0, "CS_0 (default)"));
- bi_decl(bi_1pin_with_name(SPI_CS_0+1, "CS_1"));
- bi_decl(bi_1pin_with_name(SPI_CS_0+2, "CS_2"));
- bi_decl(bi_1pin_with_name(SPI_CS_0+3, "CS_3"));
- /* Setup USB */
- tusb_init();
- /* Setup PL022 SPI */
- enable_spi(baud);
- command_loop();
- }
|