mirror of
https://github.com/andreili/katapult.git
synced 2025-08-23 19:34:06 +02:00
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:
parent
396a18be5e
commit
a253e73116
@ -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
87
src/rp2040/flash.c
Normal 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
11
src/rp2040/flash.h
Normal 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
|
@ -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
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user