From 122daf256bdfec6f2e822f513841885034e9060f Mon Sep 17 00:00:00 2001 From: Matt Campbell Date: Mon, 18 Aug 2025 10:46:25 -0400 Subject: [PATCH 1/3] Properly map IO and PAD register offsets for QSPI In the UART bootloader example the QSPI pins are used for normal IO and a `qspi_gpio_set_function()` function is used locally to setup the pins. This function didn't take into account that the IO and PAD register banks for the QSPI IOs are not in the same order. We correct this by adding a lookup table to map between the two. --- bootloaders/uart/uart_binary.c | 35 +++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/bootloaders/uart/uart_binary.c b/bootloaders/uart/uart_binary.c index e3f2f535b..85b85b859 100644 --- a/bootloaders/uart/uart_binary.c +++ b/bootloaders/uart/uart_binary.c @@ -14,7 +14,7 @@ #endif // Initialize the GPIO for the LED -void pico_led_init(void) { +static void pico_led_init(void) { #ifdef PICO_DEFAULT_LED_PIN // A device like Pico that uses a GPIO for the LED will define PICO_DEFAULT_LED_PIN // so we can use normal GPIO functionality to turn the led on and off @@ -24,15 +24,36 @@ void pico_led_init(void) { } // Turn the LED on or off -void pico_set_led(bool led_on) { +static void pico_set_led(bool led_on) { #if defined(PICO_DEFAULT_LED_PIN) // Just set the GPIO on or off gpio_put(PICO_DEFAULT_LED_PIN, led_on); #endif } +enum qspi_gpio { + // arbitrarily choose PAD register bank to set the order + QSPI_GPIO_SCLK = 0, + QSPI_GPIO_SD0 = 1, + QSPI_GPIO_SD1 = 2, + QSPI_GPIO_SD2 = 3, + QSPI_GPIO_SD3 = 4, + QSPI_GPIO_SS = 5, +}; + +// curiously the IO and PAD register banks for the QSPI GPIOs are not in the same order +// This look up table will map the PAD offset to the IO offset for the same pin +static const uint QSPI_GPIO_PAD_TO_IO_OFFSET[] = { + 0, // SCLK + 2, // SD0 + 3, // SD1 + 4, // SD2 + 5, // SD3 + 1, // SS +}; + // Set function for QSPI GPIO pin -void qspi_gpio_set_function(uint gpio, gpio_function_t fn) { +static void qspi_gpio_set_function(enum qspi_gpio gpio, gpio_function_t fn) { // Set input enable on, output disable off hw_write_masked(&pads_qspi_hw->io[gpio], PADS_QSPI_GPIO_QSPI_SD2_IE_BITS, @@ -40,7 +61,7 @@ void qspi_gpio_set_function(uint gpio, gpio_function_t fn) { ); // Zero all fields apart from fsel; we want this IO to do what the peripheral tells it. // This doesn't affect e.g. pullup/pulldown, as these are in pad controls. - io_qspi_hw->io[gpio].ctrl = fn << IO_QSPI_GPIO_QSPI_SD2_CTRL_FUNCSEL_LSB; + io_qspi_hw->io[QSPI_GPIO_PAD_TO_IO_OFFSET[gpio]].ctrl = fn << IO_QSPI_GPIO_QSPI_SD2_CTRL_FUNCSEL_LSB; // Remove pad isolation now that the correct peripheral is in control of the pad hw_clear_bits(&pads_qspi_hw->io[gpio], PADS_QSPI_GPIO_QSPI_SD2_ISO_BITS); @@ -49,9 +70,9 @@ void qspi_gpio_set_function(uint gpio, gpio_function_t fn) { int main() { pico_led_init(); - // SD2 is QSPI GPIO 3, SD3 is QSPI GPIO 4 - qspi_gpio_set_function(3, GPIO_FUNC_UART_AUX); - qspi_gpio_set_function(4, GPIO_FUNC_UART_AUX); + // SD2 is UART0 TX, SD3 is UART0 RX (same as the ROM UART bootloader) + qspi_gpio_set_function(QSPI_GPIO_SD2, GPIO_FUNC_UART_AUX); + qspi_gpio_set_function(QSPI_GPIO_SD3, GPIO_FUNC_UART_AUX); uart_init(uart0, 1000000); From a30d28fd11272455c271fbe72622c1255292f19c Mon Sep 17 00:00:00 2001 From: Matt Campbell Date: Wed, 20 Aug 2025 12:16:07 -0400 Subject: [PATCH 2/3] Use function defines from QSPI definitions --- bootloaders/uart/uart_binary.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bootloaders/uart/uart_binary.c b/bootloaders/uart/uart_binary.c index 85b85b859..dcfb7a4fd 100644 --- a/bootloaders/uart/uart_binary.c +++ b/bootloaders/uart/uart_binary.c @@ -71,8 +71,8 @@ int main() { pico_led_init(); // SD2 is UART0 TX, SD3 is UART0 RX (same as the ROM UART bootloader) - qspi_gpio_set_function(QSPI_GPIO_SD2, GPIO_FUNC_UART_AUX); - qspi_gpio_set_function(QSPI_GPIO_SD3, GPIO_FUNC_UART_AUX); + qspi_gpio_set_function(QSPI_GPIO_SD2, GPIO_FUNC1_UART_AUX); + qspi_gpio_set_function(QSPI_GPIO_SD3, GPIO_FUNC1_UART_AUX); uart_init(uart0, 1000000); From 6e58bb5a7fadb0f5ca043ee28cb3dfb38a7a95d6 Mon Sep 17 00:00:00 2001 From: Matt Campbell Date: Fri, 22 Aug 2025 10:52:49 -0400 Subject: [PATCH 3/3] remove 'static' per PR discussion --- bootloaders/uart/uart_binary.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bootloaders/uart/uart_binary.c b/bootloaders/uart/uart_binary.c index dcfb7a4fd..6359468d1 100644 --- a/bootloaders/uart/uart_binary.c +++ b/bootloaders/uart/uart_binary.c @@ -14,7 +14,7 @@ #endif // Initialize the GPIO for the LED -static void pico_led_init(void) { +void pico_led_init(void) { #ifdef PICO_DEFAULT_LED_PIN // A device like Pico that uses a GPIO for the LED will define PICO_DEFAULT_LED_PIN // so we can use normal GPIO functionality to turn the led on and off @@ -24,7 +24,7 @@ static void pico_led_init(void) { } // Turn the LED on or off -static void pico_set_led(bool led_on) { +void pico_set_led(bool led_on) { #if defined(PICO_DEFAULT_LED_PIN) // Just set the GPIO on or off gpio_put(PICO_DEFAULT_LED_PIN, led_on); @@ -43,7 +43,7 @@ enum qspi_gpio { // curiously the IO and PAD register banks for the QSPI GPIOs are not in the same order // This look up table will map the PAD offset to the IO offset for the same pin -static const uint QSPI_GPIO_PAD_TO_IO_OFFSET[] = { +const uint QSPI_GPIO_PAD_TO_IO_OFFSET[] = { 0, // SCLK 2, // SD0 3, // SD1 @@ -53,7 +53,7 @@ static const uint QSPI_GPIO_PAD_TO_IO_OFFSET[] = { }; // Set function for QSPI GPIO pin -static void qspi_gpio_set_function(enum qspi_gpio gpio, gpio_function_t fn) { +void qspi_gpio_set_function(enum qspi_gpio gpio, gpio_function_t fn) { // Set input enable on, output disable off hw_write_masked(&pads_qspi_hw->io[gpio], PADS_QSPI_GPIO_QSPI_SD2_IE_BITS,