From a253e7311654da2534ba72430b7caf7c6bee58c9 Mon Sep 17 00:00:00 2001 From: sh83 Date: Fri, 21 Oct 2022 22:47:06 +0300 Subject: [PATCH] rp2040: Implemented canboot flash interface. Implemented canboot timer interface. Simplified chipid.c to use common flash interface. Signed-off-by: Alex Malishev --- src/rp2040/chipid.c | 96 +------------------------------------------ src/rp2040/flash.c | 87 +++++++++++++++++++++++++++++++++++++++ src/rp2040/flash.h | 11 +++++ src/rp2040/internal.h | 9 ---- src/rp2040/timer.c | 50 ++++++++-------------- 5 files changed, 118 insertions(+), 135 deletions(-) create mode 100644 src/rp2040/flash.c create mode 100644 src/rp2040/flash.h diff --git a/src/rp2040/chipid.c b/src/rp2040/chipid.c index 80182f6..520f5c2 100644 --- a/src/rp2040/chipid.c +++ b/src/rp2040/chipid.c @@ -6,14 +6,12 @@ #include // memcpy #include "autoconf.h" // CONFIG_USB_SERIAL_NUMBER_CHIPID +#include "hw_flash.h" #include "board/irq.h" // irq_disable, irq_enable #include "board/canserial.h" // canserial_set_uuid #include "generic/usb_cdc.h" // usb_fill_serial #include "generic/usbstd.h" // usb_string_descriptor #include "sched.h" // DECL_INIT -#include "hardware/structs/ioqspi.h" // ioqspi_hw -#include "hardware/structs/ssi.h" // ssi_hw -#include "internal.h" #define CHIP_UID_LEN 8 @@ -28,96 +26,6 @@ usbserial_get_serialid(void) return &cdc_chipid.desc; } -// Functions for reading out the flash chip ID. Adapted from the official -// Pi SDK. - -static void _ramfunc -flash_cs_force(int high) -{ - uint32_t field_val = high ? - IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH : - IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW; - hw_write_masked(&ioqspi_hw->io[1].ctrl, - field_val << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB, - IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS - ); -} - -// To re-enable XIP we need to call flash_enter_xip. It's available in the -// bootrom, but that version is a generic one that works for most devices and -// the tradeoff for that is enabling a low performance mode. -// Instead we copy out the boot2 XIP enabling stage, and save it in RAM -// so we can call it later on. - -#define BOOT2_SIZE 0x100 - -static uint8_t boot2_copy[BOOT2_SIZE] __aligned(16); - -static void -flash_enter_xip_prepare(void) -{ - void * volatile target = (void *)XIP_BASE; // Avoids warning - memcpy(boot2_copy, target, BOOT2_SIZE); - barrier(); -} - -static void _ramfunc -flash_enter_xip_perform(void) -{ - ((void (*)(void))boot2_copy+1)(); -} - -#define FLASH_RUID_CMD 0x4B -#define FLASH_RUID_DUMMY_BYTES 4 -#define FLASH_RUID_DATA_BYTES 8 -#define FLASH_RUID_TOTAL_BYTES (1+FLASH_RUID_DUMMY_BYTES+FLASH_RUID_DATA_BYTES) - -static void _ramfunc -read_unique_id(uint8_t *out) -{ - uint8_t txbuf[FLASH_RUID_TOTAL_BYTES] = {0}; - uint8_t rxbuf[FLASH_RUID_TOTAL_BYTES] = {0}; - - uint8_t *txptr = txbuf; - uint8_t *rxptr = rxbuf; - - int tx_remaining = FLASH_RUID_TOTAL_BYTES; - int rx_remaining = FLASH_RUID_TOTAL_BYTES; - - txbuf[0] = FLASH_RUID_CMD; - - // Set up flash so we can work with it without XIP getting in the way - flash_enter_xip_prepare(); - irq_disable(); - barrier(); - connect_internal_flash(); - flash_exit_xip(); - flash_cs_force(0); - - while (tx_remaining || rx_remaining) { - uint32_t flags = ssi_hw->sr; - int can_put = !!(flags & SSI_SR_TFNF_BITS); - int can_get = !!(flags & SSI_SR_RFNE_BITS); - if (can_put && tx_remaining) { - ssi_hw->dr0 = *txptr++; - tx_remaining--; - } - if (can_get && rx_remaining) { - *rxptr++ = (uint8_t)ssi_hw->dr0; - --rx_remaining; - } - } - - // Restore XIP - flash_cs_force(1); - flash_flush_cache(); - flash_enter_xip_perform(); - barrier(); - irq_enable(); - - memcpy(out, rxbuf+1+FLASH_RUID_DUMMY_BYTES, FLASH_RUID_DATA_BYTES); -} - void chipid_init(void) { @@ -125,7 +33,7 @@ chipid_init(void) return; uint8_t data[8] = {0}; - read_unique_id(data); + flash_get_unique_id(data); if (CONFIG_USB_SERIAL_NUMBER_CHIPID) usb_fill_serial(&cdc_chipid.desc, ARRAY_SIZE(cdc_chipid.data), data); diff --git a/src/rp2040/flash.c b/src/rp2040/flash.c new file mode 100644 index 0000000..07a96ef --- /dev/null +++ b/src/rp2040/flash.c @@ -0,0 +1,87 @@ +// Flash (IAP) functionality for RP2040 +// This file may be distributed under the terms of the GNU GPLv3 license. +#include "flash.h" + +#include // memcpy +#include "autoconf.h" // CONFIG_BLOCK_SIZE +#include "generic/irq.h" +#include "hw_flash.h" // flash_write_page + +#define MAX(a, b) ((a) > (b))?(a):(b) +#define PAGE_SIZE (MAX(CONFIG_BLOCK_SIZE, 256)) +#define SECTOR_SIZE 4096 + +// buffer to consolidate multiple block_size requests into one page size write +static uint32_t buffer_not_empty; // true if buffer hold some data +static uint32_t buffer_start_address; // buffer data should be written at this flash address +static uint8_t buffer[PAGE_SIZE] __aligned(4); // buffer data itseft + +static uint32_t page_write_count; + +static void +flush_buffer(void) +{ + if (!buffer_not_empty) { + return; + } + if ((buffer_start_address % SECTOR_SIZE) == 0) { + flash_range_erase(buffer_start_address - CONFIG_FLASH_START, SECTOR_SIZE); + } + flash_range_program(buffer_start_address - CONFIG_FLASH_START, buffer, PAGE_SIZE); + page_write_count += 1; + buffer_not_empty = 0; +} + +static void +ensure_buffer(uint32_t address) +{ + if (buffer_not_empty) { + if ((address >= buffer_start_address) && + (address + CONFIG_BLOCK_SIZE <= buffer_start_address + PAGE_SIZE)) { + // current buffer have space for the new data + return; + } else { + // flush existing data + flush_buffer(); + } + } + //prepare buffer + buffer_not_empty = 1; + // address should be multiple of PAGE_SIZE + buffer_start_address = (address / PAGE_SIZE) * PAGE_SIZE; + memset(buffer, 0xFF, PAGE_SIZE); +} + +static int +check_valid_flash_address(uint32_t address) +{ + if ((address % CONFIG_BLOCK_SIZE) != 0) { + return -1; + } + if (address < CONFIG_FLASH_START) { + return -2; + } + if (address + CONFIG_BLOCK_SIZE > CONFIG_FLASH_START + CONFIG_FLASH_SIZE) { + return -3; + } + return 0; +} + +int +flash_write_block(uint32_t block_address, uint32_t *data) +{ + int ret = check_valid_flash_address(block_address); + if (ret < 0) { + return ret; + } + ensure_buffer(block_address); + memcpy(&buffer[block_address - buffer_start_address], data, CONFIG_BLOCK_SIZE); + return 0; +} + +int +flash_complete(void) +{ + flush_buffer(); + return page_write_count; +} diff --git a/src/rp2040/flash.h b/src/rp2040/flash.h new file mode 100644 index 0000000..7811668 --- /dev/null +++ b/src/rp2040/flash.h @@ -0,0 +1,11 @@ +// Flash (IAP) functionality for RP2040 +// This file may be distributed under the terms of the GNU GPLv3 license. +#ifndef __RP2040_FLASH_H +#define __RP2040_FLASH_H + +#include + +int flash_write_block(uint32_t block_address, uint32_t *data); +int flash_complete(void); + +#endif diff --git a/src/rp2040/internal.h b/src/rp2040/internal.h index 8553278..8c48640 100644 --- a/src/rp2040/internal.h +++ b/src/rp2040/internal.h @@ -8,14 +8,5 @@ void enable_pclock(uint32_t reset_bit); int is_enabled_pclock(uint32_t reset_bit); uint32_t get_pclock_frequency(uint32_t reset_bit); void gpio_peripheral(uint32_t gpio, int func, int pull_up); -void reset_to_usb_boot(uint32_t gpio_activity_pin_mask - , uint32_t disable_interface_mask); -void connect_internal_flash(void); -void flash_exit_xip(void); -void flash_flush_cache(void); - -// Force a function to run from ram -#define UNIQSEC __FILE__ "." __stringify(__LINE__) -#define _ramfunc noinline __section(".ramfunc." UNIQSEC) #endif // internal.h diff --git a/src/rp2040/timer.c b/src/rp2040/timer.c index 5401933..c803f3e 100644 --- a/src/rp2040/timer.c +++ b/src/rp2040/timer.c @@ -4,11 +4,10 @@ // // This file may be distributed under the terms of the GNU GPLv3 license. +#include "autoconf.h" // CONFIG_CLOCK_FREQ #include "board/armcm_boot.h" // armcm_enable_irq #include "board/irq.h" // irq_disable #include "board/misc.h" // timer_read_time -#include "board/timer_irq.h" // timer_dispatch_many -#include "command.h" // DECL_SHUTDOWN #include "hardware/structs/resets.h" // RESETS_RESET_UART0_BITS #include "hardware/structs/timer.h" // RESETS_RESET_UART0_BITS #include "internal.h" // enable_pclock @@ -19,6 +18,22 @@ * Low level timer code ****************************************************************/ +// Return the number of clock ticks for a given number of microseconds +uint32_t +timer_from_us(uint32_t us) +{ + return us * (CONFIG_CLOCK_FREQ / 1000000); +} + +// Return true if time1 is before time2. Always use this function to +// compare times as regular C comparisons can fail if the counter +// rolls over. +uint8_t +timer_is_before(uint32_t time1, uint32_t time2) +{ + return (int32_t)(time1 - time2) < 0; +} + // Return the current time (in absolute clock ticks). uint32_t timer_read_time(void) @@ -26,45 +41,16 @@ timer_read_time(void) return timer_hw->timerawl; } -static inline void -timer_set(uint32_t next) -{ - timer_hw->alarm[0] = next; -} - -// Activate timer dispatch as soon as possible -void -timer_kick(void) -{ - timer_set(timer_read_time() + 50); -} - - /**************************************************************** * Setup and irqs ****************************************************************/ -// Hardware timer IRQ handler - dispatch software timers -void __aligned(16) -TIMER0_IRQHandler(void) -{ - irq_disable(); - timer_hw->intr = 1; - uint32_t next = timer_dispatch_many(); - timer_set(next); - irq_enable(); -} - void -timer_init(void) +timer_setup(void) { irq_disable(); enable_pclock(RESETS_RESET_TIMER_BITS); timer_hw->timelw = 0; timer_hw->timehw = 0; - armcm_enable_irq(TIMER0_IRQHandler, TIMER_IRQ_0_IRQn, 2); - timer_hw->inte = 1; - timer_kick(); irq_enable(); } -DECL_INIT(timer_init);