rp2040: Implemented canboot flash interface.

Implemented canboot timer interface.
Simplified chipid.c to use common flash interface.

Signed-off-by: Alex Malishev <malishev@gmail.com>
This commit is contained in:
sh83 2022-10-21 22:47:06 +03:00 committed by Eric Callahan
parent 396a18be5e
commit a253e73116
5 changed files with 118 additions and 135 deletions

View File

@ -6,14 +6,12 @@
#include <string.h> // 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);

87
src/rp2040/flash.c Normal file
View File

@ -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 <string.h> // 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;
}

11
src/rp2040/flash.h Normal file
View File

@ -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 <stdint.h>
int flash_write_block(uint32_t block_address, uint32_t *data);
int flash_complete(void);
#endif

View File

@ -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

View File

@ -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);