rp2040: add chipid support

The rp2040 doesn't have a chip ID, but the flash chip connected does. We
can get this ID by asking the flash chip directly, but doing so requires
disengaging the XIP layer, performing the interrogation of the flash
chip, and then re-enabling the XIP layer. This gives us a 64-bit unique
ID that we can use as our USB serial number.

Signed-off-by: Lasse Dalegaard <dalegaard@gmail.com>
This commit is contained in:
Lasse Dalegaard 2021-07-04 10:24:29 +02:00 committed by Kevin O'Connor
parent 0597210cb9
commit ba958468b7
5 changed files with 171 additions and 4 deletions

View File

@ -9,6 +9,7 @@ config RP2040_SELECT
select HAVE_GPIO_ADC select HAVE_GPIO_ADC
select HAVE_GPIO_BITBANGING select HAVE_GPIO_BITBANGING
select HAVE_STRICT_TIMING select HAVE_STRICT_TIMING
select HAVE_CHIPID
config BOARD_DIRECTORY config BOARD_DIRECTORY
string string

View File

@ -17,6 +17,7 @@ src-y += rp2040/main.c rp2040/gpio.c rp2040/adc.c generic/crc16_ccitt.c
src-y += generic/armcm_boot.c generic/armcm_irq.c generic/armcm_reset.c src-y += generic/armcm_boot.c generic/armcm_irq.c generic/armcm_reset.c
src-y += generic/timer_irq.c rp2040/timer.c rp2040/bootrom.c src-y += generic/timer_irq.c rp2040/timer.c rp2040/bootrom.c
src-$(CONFIG_USBSERIAL) += rp2040/usbserial.c generic/usb_cdc.c src-$(CONFIG_USBSERIAL) += rp2040/usbserial.c generic/usb_cdc.c
src-$(CONFIG_USBSERIAL) += rp2040/chipid.c
src-$(CONFIG_SERIAL) += rp2040/serial.c generic/serial_irq.c src-$(CONFIG_SERIAL) += rp2040/serial.c generic/serial_irq.c
# rp2040 stage2 building # rp2040 stage2 building

View File

@ -5,8 +5,17 @@
// 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 <stdint.h> // uint16_t, uint32_t, uintptr_t #include <stdint.h> // uint16_t, uint32_t, uintptr_t
#include <string.h> // memcpy
#include "compiler.h" // noinline, __section
static void * #define ROM_TABLE_CODE(c1, c2) ((c1) | ((c2) << 8))
// All functions in here need to be RAM-resident, as we may need
// to (especially for the flash functions) call while the XIP layer
// is unavailable.
#define noinline_ram noinline __section(".ramfunc.read_chip_id")
static void * noinline_ram
rom_func_lookup(uint32_t code) rom_func_lookup(uint32_t code)
{ {
// Table and lookup function are provided by the BOOTROM // Table and lookup function are provided by the BOOTROM
@ -16,10 +25,31 @@ rom_func_lookup(uint32_t code)
return fn(table, code); return fn(table, code);
} }
void void noinline_ram
reset_to_usb_boot(uint32_t gpio_activity_pin_mask reset_to_usb_boot(uint32_t gpio_activity_pin_mask
, uint32_t disable_interface_mask) , uint32_t disable_interface_mask)
{ {
void (*reset_to_usb_boot)(uint32_t, uint32_t) = rom_func_lookup(0x4255); void (*fn)(uint32_t, uint32_t) = rom_func_lookup(ROM_TABLE_CODE('U', 'B'));
reset_to_usb_boot(gpio_activity_pin_mask, disable_interface_mask); fn(gpio_activity_pin_mask, disable_interface_mask);
}
void noinline_ram
connect_internal_flash(void)
{
void (*fn)(void) = rom_func_lookup(ROM_TABLE_CODE('I', 'F'));
fn();
}
void noinline_ram
flash_exit_xip(void)
{
void (*fn)(void) = rom_func_lookup(ROM_TABLE_CODE('E', 'X'));
fn();
}
void noinline_ram
flash_flush_cache(void)
{
void (*fn)(void) = rom_func_lookup(ROM_TABLE_CODE('F', 'C'));
fn();
} }

132
src/rp2040/chipid.c Normal file
View File

@ -0,0 +1,132 @@
// Support for extracting the hardware chip id on rp2040
//
// Copyright (C) 2021 Lasse Dalegaard <dalegaard@gmail.com>
//
// This file may be distributed under the terms of the GNU GPLv3 license.
#define CHIP_UID_LEN 8
#include <string.h> // memcpy
#include "autoconf.h" // CONFIG_USB_SERIAL_NUMBER_CHIPID
#include "board/irq.h" // irq_disable, irq_enable
#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"
static struct {
struct usb_string_descriptor desc;
uint16_t data[CHIP_UID_LEN * 2];
} cdc_chipid;
struct usb_string_descriptor *
usbserial_get_serialid(void)
{
return &cdc_chipid.desc;
}
// Functions for reading out the flash chip ID. Adapted from the official
// Pi SDK.
static void noinline __section(".ramfunc.read_chip_id")
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 noinline __section(".ramfunc.read_chip_id")
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 noinline __section(".ramfunc.read_chip_id")
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)
{
if (!CONFIG_USB_SERIAL_NUMBER_CHIPID)
return;
uint8_t data[8] = {0};
read_unique_id(data);
usb_fill_serial(&cdc_chipid.desc, ARRAY_SIZE(cdc_chipid.data)
, data);
}
DECL_INIT(chipid_init);

View File

@ -10,5 +10,8 @@ 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 void reset_to_usb_boot(uint32_t gpio_activity_pin_mask
, uint32_t disable_interface_mask); , uint32_t disable_interface_mask);
void connect_internal_flash(void);
void flash_exit_xip(void);
void flash_flush_cache(void);
#endif // internal.h #endif // internal.h