mirror of
https://github.com/andreili/katapult.git
synced 2025-08-24 03:44: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 <string.h> // memcpy
|
||||||
#include "autoconf.h" // CONFIG_USB_SERIAL_NUMBER_CHIPID
|
#include "autoconf.h" // CONFIG_USB_SERIAL_NUMBER_CHIPID
|
||||||
|
#include "hw_flash.h"
|
||||||
#include "board/irq.h" // irq_disable, irq_enable
|
#include "board/irq.h" // irq_disable, irq_enable
|
||||||
#include "board/canserial.h" // canserial_set_uuid
|
#include "board/canserial.h" // canserial_set_uuid
|
||||||
#include "generic/usb_cdc.h" // usb_fill_serial
|
#include "generic/usb_cdc.h" // usb_fill_serial
|
||||||
#include "generic/usbstd.h" // usb_string_descriptor
|
#include "generic/usbstd.h" // usb_string_descriptor
|
||||||
#include "sched.h" // DECL_INIT
|
#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
|
#define CHIP_UID_LEN 8
|
||||||
|
|
||||||
@ -28,96 +26,6 @@ usbserial_get_serialid(void)
|
|||||||
return &cdc_chipid.desc;
|
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
|
void
|
||||||
chipid_init(void)
|
chipid_init(void)
|
||||||
{
|
{
|
||||||
@ -125,7 +33,7 @@ chipid_init(void)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
uint8_t data[8] = {0};
|
uint8_t data[8] = {0};
|
||||||
read_unique_id(data);
|
flash_get_unique_id(data);
|
||||||
|
|
||||||
if (CONFIG_USB_SERIAL_NUMBER_CHIPID)
|
if (CONFIG_USB_SERIAL_NUMBER_CHIPID)
|
||||||
usb_fill_serial(&cdc_chipid.desc, ARRAY_SIZE(cdc_chipid.data), data);
|
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);
|
int is_enabled_pclock(uint32_t reset_bit);
|
||||||
uint32_t get_pclock_frequency(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 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
|
#endif // internal.h
|
||||||
|
@ -4,11 +4,10 @@
|
|||||||
//
|
//
|
||||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
// 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/armcm_boot.h" // armcm_enable_irq
|
||||||
#include "board/irq.h" // irq_disable
|
#include "board/irq.h" // irq_disable
|
||||||
#include "board/misc.h" // timer_read_time
|
#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/resets.h" // RESETS_RESET_UART0_BITS
|
||||||
#include "hardware/structs/timer.h" // RESETS_RESET_UART0_BITS
|
#include "hardware/structs/timer.h" // RESETS_RESET_UART0_BITS
|
||||||
#include "internal.h" // enable_pclock
|
#include "internal.h" // enable_pclock
|
||||||
@ -19,6 +18,22 @@
|
|||||||
* Low level timer code
|
* 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).
|
// Return the current time (in absolute clock ticks).
|
||||||
uint32_t
|
uint32_t
|
||||||
timer_read_time(void)
|
timer_read_time(void)
|
||||||
@ -26,45 +41,16 @@ timer_read_time(void)
|
|||||||
return timer_hw->timerawl;
|
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
|
* 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
|
void
|
||||||
timer_init(void)
|
timer_setup(void)
|
||||||
{
|
{
|
||||||
irq_disable();
|
irq_disable();
|
||||||
enable_pclock(RESETS_RESET_TIMER_BITS);
|
enable_pclock(RESETS_RESET_TIMER_BITS);
|
||||||
timer_hw->timelw = 0;
|
timer_hw->timelw = 0;
|
||||||
timer_hw->timehw = 0;
|
timer_hw->timehw = 0;
|
||||||
armcm_enable_irq(TIMER0_IRQHandler, TIMER_IRQ_0_IRQn, 2);
|
|
||||||
timer_hw->inte = 1;
|
|
||||||
timer_kick();
|
|
||||||
irq_enable();
|
irq_enable();
|
||||||
}
|
}
|
||||||
DECL_INIT(timer_init);
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user