From 92a4261ad505f6dfab9b6574f842fdbcf43c4b5e Mon Sep 17 00:00:00 2001 From: Scott Shawcroft Date: Fri, 3 Dec 2021 13:15:24 -0800 Subject: [PATCH] Full UART support for RPI This adds PL011 UART support which is more plentiful. It also: * Fixes PI4 build by including .dtb files on the SD card. * Enables the activity LED as the status LED on PI4 and CM4 I/O. * Adds that LED as board.LED. Fixes #5650 and progress on #5629 --- main.c | 1 + ports/broadcom/Makefile | 2 +- .../boards/raspberrypi_cm4io/mpconfigboard.h | 2 + .../broadcom/boards/raspberrypi_cm4io/pins.c | 2 + .../boards/raspberrypi_pi4b/mpconfigboard.h | 2 + .../boards/raspberrypi_pi4b/mpconfigboard.mk | 2 +- ports/broadcom/boards/raspberrypi_pi4b/pins.c | 2 + ports/broadcom/common-hal/busio/I2C.c | 3 +- ports/broadcom/common-hal/busio/UART.c | 303 +++++++++++++++--- ports/broadcom/common-hal/busio/UART.h | 1 + ports/broadcom/firmware | 2 +- ports/broadcom/peripherals | 2 +- 12 files changed, 269 insertions(+), 55 deletions(-) diff --git a/main.c b/main.c index b1b482f985..c019237362 100644 --- a/main.c +++ b/main.c @@ -56,6 +56,7 @@ #include "supervisor/shared/safe_mode.h" #include "supervisor/shared/stack.h" #include "supervisor/shared/status_leds.h" +#include "supervisor/shared/tick.h" #include "supervisor/shared/traceback.h" #include "supervisor/shared/translate.h" #include "supervisor/shared/workflow.h" diff --git a/ports/broadcom/Makefile b/ports/broadcom/Makefile index 16b44f7501..401d601528 100644 --- a/ports/broadcom/Makefile +++ b/ports/broadcom/Makefile @@ -166,7 +166,7 @@ $(BUILD)/firmware.disk.img.zip: $(BUILD)/kernel8.img $(Q)parted -s $(BUILD)/circuitpython-disk.img mkpart primary fat32 0% 100% $(Q)mkfs.fat -F 32 -n BOOT --offset=2048 $(BUILD)/circuitpython-disk.img - $(Q)mcopy -i $(BUILD)/circuitpython-disk.img@@1M config.txt firmware/bootcode.bin firmware/fixup* firmware/start* :: + $(Q)mcopy -i $(BUILD)/circuitpython-disk.img@@1M config.txt firmware/bootcode.bin firmware/fixup* firmware/start* firmware/*.dtb :: $(Q)mcopy -i $(BUILD)/circuitpython-disk.img@@1M $(BUILD)/kernel8.img :: $(Q)zip $@ $(BUILD)/circuitpython-disk.img $(Q)rm $(BUILD)/circuitpython-disk.img diff --git a/ports/broadcom/boards/raspberrypi_cm4io/mpconfigboard.h b/ports/broadcom/boards/raspberrypi_cm4io/mpconfigboard.h index 4211568bfa..9acfb32626 100644 --- a/ports/broadcom/boards/raspberrypi_cm4io/mpconfigboard.h +++ b/ports/broadcom/boards/raspberrypi_cm4io/mpconfigboard.h @@ -2,3 +2,5 @@ #define DEFAULT_I2C_BUS_SCL (&pin_GPIO3) #define DEFAULT_I2C_BUS_SDA (&pin_GPIO2) + +#define MICROPY_HW_LED_STATUS (&pin_GPIO42) diff --git a/ports/broadcom/boards/raspberrypi_cm4io/pins.c b/ports/broadcom/boards/raspberrypi_cm4io/pins.c index 0dfbe4cbd5..db4eb91710 100644 --- a/ports/broadcom/boards/raspberrypi_cm4io/pins.c +++ b/ports/broadcom/boards/raspberrypi_cm4io/pins.c @@ -57,6 +57,8 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_D26), MP_ROM_PTR(&pin_GPIO26) }, { MP_ROM_QSTR(MP_QSTR_D27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)}, }; diff --git a/ports/broadcom/boards/raspberrypi_pi4b/mpconfigboard.h b/ports/broadcom/boards/raspberrypi_pi4b/mpconfigboard.h index be77d35ac7..2980b0f9a5 100644 --- a/ports/broadcom/boards/raspberrypi_pi4b/mpconfigboard.h +++ b/ports/broadcom/boards/raspberrypi_pi4b/mpconfigboard.h @@ -2,3 +2,5 @@ #define DEFAULT_I2C_BUS_SCL (&pin_GPIO3) #define DEFAULT_I2C_BUS_SDA (&pin_GPIO2) + +#define MICROPY_HW_LED_STATUS (&pin_GPIO42) diff --git a/ports/broadcom/boards/raspberrypi_pi4b/mpconfigboard.mk b/ports/broadcom/boards/raspberrypi_pi4b/mpconfigboard.mk index 4567e9c15e..67b1c1dc85 100644 --- a/ports/broadcom/boards/raspberrypi_pi4b/mpconfigboard.mk +++ b/ports/broadcom/boards/raspberrypi_pi4b/mpconfigboard.mk @@ -3,4 +3,4 @@ USB_PID = 0xF001 USB_PRODUCT = "Raspberry Pi 4B" USB_MANUFACTURER = "Raspberry Pi" -CHIP_VARIANT = bcm2711 +CHIP_VARIANT = "bcm2711" diff --git a/ports/broadcom/boards/raspberrypi_pi4b/pins.c b/ports/broadcom/boards/raspberrypi_pi4b/pins.c index 0dfbe4cbd5..db4eb91710 100644 --- a/ports/broadcom/boards/raspberrypi_pi4b/pins.c +++ b/ports/broadcom/boards/raspberrypi_pi4b/pins.c @@ -57,6 +57,8 @@ STATIC const mp_rom_map_elem_t board_global_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_D26), MP_ROM_PTR(&pin_GPIO26) }, { MP_ROM_QSTR(MP_QSTR_D27), MP_ROM_PTR(&pin_GPIO27) }, + { MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO42) }, + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display)}, }; diff --git a/ports/broadcom/common-hal/busio/I2C.c b/ports/broadcom/common-hal/busio/I2C.c index 9726f394d2..8e135ab4d7 100644 --- a/ports/broadcom/common-hal/busio/I2C.c +++ b/ports/broadcom/common-hal/busio/I2C.c @@ -72,7 +72,8 @@ void common_hal_busio_i2c_construct(busio_i2c_obj_t *self, uint8_t sda_alt = 0; for (scl_alt = 0; scl_alt < 6; scl_alt++) { if (scl->functions[scl_alt].type != PIN_FUNCTION_I2C || - i2c_in_use[scl->functions[scl_alt].index]) { + i2c_in_use[scl->functions[scl_alt].index] || + scl->functions[scl_alt].function != I2C_FUNCTION_SCL) { continue; } for (sda_alt = 0; sda_alt < 6; sda_alt++) { diff --git a/ports/broadcom/common-hal/busio/UART.c b/ports/broadcom/common-hal/busio/UART.c index 85dc36e755..b23aa85b92 100644 --- a/ports/broadcom/common-hal/busio/UART.c +++ b/ports/broadcom/common-hal/busio/UART.c @@ -41,7 +41,14 @@ #define NO_PIN 0xff -#define UART_INST(uart) (((uart) ? uart1 : uart0)) +// UART1 is a different peripheral than the rest so it is hardcoded below. +#if BCM_VERSION == 2711 +#define NUM_UART (6) +STATIC ARM_UART_PL011_Type *uart[NUM_UART] = {UART0, NULL, UART2, UART3, UART4, UART5}; +#else +#define NUM_UART (2) +STATIC ARM_UART_PL011_Type *uart[NUM_UART] = {UART0, NULL}; +#endif typedef enum { STATUS_FREE = 0, @@ -49,29 +56,62 @@ typedef enum { STATUS_NEVER_RESET } uart_status_t; -// The Broadcom chips have two different types of UARTs. UART1 is the "mini-UART" -// that is most available so we've implemented it first. The ARM PL011 UART -// support will be added later. We set NUM_UARTS to 2 here so that we can match -// the indexing even though UART0 isn't supported yet. We currently use this -// UART for debugging so we don't support user use of UART yet. -#define NUM_UARTS 2 - -static uart_status_t uart_status[NUM_UARTS]; +static uart_status_t uart_status[NUM_UART]; +static busio_uart_obj_t *active_uart[NUM_UART]; void reset_uart(void) { - for (uint8_t num = 0; num < NUM_UARTS; num++) { + bool any_pl011_active = false; + for (uint8_t num = 0; num < NUM_UART; num++) { if (uart_status[num] == STATUS_BUSY) { + if (num == 1) { + UART1->IER_b.DATA_READY = false; + UART1->CNTL = 0; + COMPLETE_MEMORY_READS; + AUX->ENABLES_b.UART_1 = false; + } else { + ARM_UART_PL011_Type *pl011 = uart[num]; + pl011->CR = 0; + } + active_uart[num] = NULL; uart_status[num] = STATUS_FREE; + } else { + any_pl011_active = any_pl011_active || (num != 1 && uart_status[num] == STATUS_NEVER_RESET); + } + } + if (!any_pl011_active) { + BP_DisableIRQ(UART_IRQn); + } + COMPLETE_MEMORY_READS; + if (AUX->ENABLES == 0) { + BP_DisableIRQ(AUX_IRQn); + } +} + +STATIC void fetch_all_from_fifo(busio_uart_obj_t *self) { + if (self->uart_id == 1) { + while (UART1->STAT_b.DATA_READY && ringbuf_num_empty(&self->ringbuf) > 0) { + int c = UART1->IO_b.DATA; + if (self->sigint_enabled && c == mp_interrupt_char) { + mp_sched_keyboard_interrupt(); + continue; + } + ringbuf_put(&self->ringbuf, c); + } + } else { + ARM_UART_PL011_Type *pl011 = uart[self->uart_id]; + while (!pl011->FR_b.RXFE && ringbuf_num_empty(&self->ringbuf) > 0) { + int c = pl011->DR_b.DATA; + if (self->sigint_enabled && c == mp_interrupt_char) { + mp_sched_keyboard_interrupt(); + continue; + } + ringbuf_put(&self->ringbuf, c); } } } -static busio_uart_obj_t *active_uarts[NUM_UARTS]; - void UART1_IRQHandler(void) { - while (UART1->STAT_b.DATA_READY && ringbuf_num_empty(&active_uarts[1]->ringbuf) > 0) { - ringbuf_put(&active_uarts[1]->ringbuf, (uint8_t)UART1->IO_b.DATA); - } + fetch_all_from_fifo(active_uart[1]); // We couldn't read all pending data (overrun) so clear the FIFO so that the interrupt // can finish. if (UART1->STAT_b.DATA_READY) { @@ -79,6 +119,33 @@ void UART1_IRQHandler(void) { } } +void pl011_IRQHandler(uint8_t index) { + fetch_all_from_fifo(active_uart[index]); + // Clear the interrupt in case we weren't able to clear it by emptying the + // FIFO. (This won't clear the FIFO.) + ARM_UART_PL011_Type *pl011 = uart[index]; + pl011->ICR = UART0_ICR_RXIC_Msk; +} + +void UART0_IRQHandler(void) { + pl011_IRQHandler(0); +} + +#if BCM_VERSION == 2711 +void UART2_IRQHandler(void) { + pl011_IRQHandler(2); +} +void UART3_IRQHandler(void) { + pl011_IRQHandler(3); +} +void UART4_IRQHandler(void) { + pl011_IRQHandler(4); +} +void UART5_IRQHandler(void) { + pl011_IRQHandler(5); +} +#endif + void common_hal_busio_uart_never_reset(busio_uart_obj_t *self) { uart_status[self->uart_id] = STATUS_NEVER_RESET; } @@ -103,14 +170,43 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, mp_raise_NotImplementedError(translate("RS485 Not yet supported on this device")); } - if (tx == &pin_GPIO14) { - if (rx == &pin_GPIO15) { - self->uart_id = 1; + size_t instance_index = NUM_UART; + BP_Function_Enum tx_alt = 0; + BP_Function_Enum rx_alt = 0; + BP_Function_Enum rts_alt = 0; + BP_Function_Enum cts_alt = 0; + for (size_t i = 0; i < NUM_UART; i++) { + if (uart_status[i] != STATUS_FREE) { + continue; } + if (tx != NULL) { + if (!pin_find_alt(tx, PIN_FUNCTION_UART, i, UART_FUNCTION_TXD, &tx_alt)) { + continue; + } + if (rts != NULL && !pin_find_alt(rts, PIN_FUNCTION_UART, i, UART_FUNCTION_RTS, &rts_alt)) { + continue; + } + } + if (rx != NULL) { + if (!pin_find_alt(rx, PIN_FUNCTION_UART, i, UART_FUNCTION_RXD, &rx_alt)) { + continue; + } + if (cts != NULL && !pin_find_alt(cts, PIN_FUNCTION_UART, i, UART_FUNCTION_CTS, &cts_alt)) { + continue; + } + } + instance_index = i; + break; + } + if (instance_index == NUM_UART) { + mp_raise_ValueError(translate("Invalid pins")); } self->rx_pin = rx; self->tx_pin = tx; + self->rts_pin = rts; + self->cts_pin = cts; + self->sigint_enabled = sigint_enabled; if (rx != NULL) { if (receiver_buffer != NULL) { @@ -129,8 +225,11 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, } } + active_uart[self->uart_id] = self; + + ARM_UART_PL011_Type *pl011 = uart[self->uart_id]; + if (self->uart_id == 1) { - active_uarts[1] = self; AUX->ENABLES_b.UART_1 = true; UART1->IER = 0; @@ -145,22 +244,80 @@ void common_hal_busio_uart_construct(busio_uart_obj_t *self, // Clear interrupts UART1->IIR = 0xff; - uint32_t source_clock = vcmailbox_get_clock_rate_measured(VCMAILBOX_CLOCK_CORE); - UART1->BAUD = ((source_clock / (baudrate * 8)) - 1); + common_hal_busio_uart_set_baudrate(self, baudrate); if (tx != NULL) { UART1->CNTL |= UART1_CNTL_TX_ENABLE_Msk; - gpio_set_pull(14, BP_PULL_NONE); - gpio_set_function(14, GPIO_GPFSEL1_FSEL14_TXD1); } if (rx != NULL) { UART1->CNTL |= UART1_CNTL_RX_ENABLE_Msk; - gpio_set_pull(15, BP_PULL_NONE); - gpio_set_function(15, GPIO_GPFSEL1_FSEL15_RXD1); } + } else { + // Ensure the UART is disabled as we configure it. + pl011->CR_b.UARTEN = false; + pl011->IMSC = 0; + pl011->ICR = 0x3ff; + + common_hal_busio_uart_set_baudrate(self, baudrate); + + uint32_t line_control = UART0_LCR_H_FEN_Msk; + line_control |= (bits - 5) << UART0_LCR_H_WLEN_Pos; + if (stop == 2) { + line_control |= UART0_LCR_H_STP2_Msk; + } + if (parity != BUSIO_UART_PARITY_NONE) { + line_control |= UART0_LCR_H_PEN_Msk; + } + if (parity == BUSIO_UART_PARITY_EVEN) { + line_control |= UART0_LCR_H_EPS_Msk; + } + pl011->LCR_H = line_control; + + uint32_t control = UART0_CR_UARTEN_Msk; + if (tx != NULL) { + control |= UART0_CR_TXE_Msk; + } + if (rx != NULL) { + control |= UART0_CR_RXE_Msk; + } + if (cts != NULL) { + control |= UART0_CR_CTSEN_Msk; + } + if (rts != NULL) { + control |= UART0_CR_RTSEN_Msk; + } + pl011->CR = control; + } + + // Setup the pins after waiting for UART stuff + COMPLETE_MEMORY_READS; + if (tx != NULL) { + gpio_set_pull(tx->number, BP_PULL_NONE); + gpio_set_function(tx->number, tx_alt); + } + if (rx != NULL) { + gpio_set_pull(rx->number, BP_PULL_NONE); + gpio_set_function(rx->number, rx_alt); + } + if (rts != NULL) { + gpio_set_pull(rts->number, BP_PULL_NONE); + gpio_set_function(rts->number, rts_alt); + } + if (cts != NULL) { + gpio_set_pull(cts->number, BP_PULL_NONE); + gpio_set_function(cts->number, cts_alt); + } + + // Turn on interrupts + COMPLETE_MEMORY_READS; + if (self->uart_id == 1) { UART1->IER_b.DATA_READY = true; // Never disable this in case the SPIs are used. They can each be // disabled at the peripheral itself. BP_EnableIRQ(AUX_IRQn); + } else { + pl011->IMSC_b.RXIM = true; + // Never disable this in case the other PL011 UARTs are used. + BP_EnableIRQ(UART_IRQn); } } @@ -176,8 +333,11 @@ void common_hal_busio_uart_deinit(busio_uart_obj_t *self) { UART1->IER_b.DATA_READY = false; UART1->CNTL = 0; AUX->ENABLES_b.UART_1 = false; - active_uarts[1] = NULL; + } else { + ARM_UART_PL011_Type *pl011 = uart[self->uart_id]; + pl011->CR = 0; } + active_uart[self->uart_id] = NULL; ringbuf_free(&self->ringbuf); uart_status[self->uart_id] = STATUS_FREE; common_hal_reset_pin(self->tx_pin); @@ -196,20 +356,46 @@ size_t common_hal_busio_uart_write(busio_uart_obj_t *self, const uint8_t *data, mp_raise_ValueError(translate("No TX pin")); } - if (self->uart_id == 1) { - COMPLETE_MEMORY_READS; - for (size_t i = 0; i < len; i++) { + COMPLETE_MEMORY_READS; + ARM_UART_PL011_Type *pl011 = uart[self->uart_id]; + for (size_t i = 0; i < len; i++) { + if (self->uart_id == 1) { // Wait for the FIFO to have space. while (!UART1->STAT_b.TX_READY) { RUN_BACKGROUND_TASKS; } UART1->IO = data[i]; + } else { + while (pl011->FR_b.TXFF) { + RUN_BACKGROUND_TASKS; + } + pl011->DR_b.DATA = data[i]; } - COMPLETE_MEMORY_READS; - return len; } + // Wait for the data to be shifted out + if (self->uart_id == 1) { + while (!UART1->STAT_b.TX_DONE) { + RUN_BACKGROUND_TASKS; + } + } else { + while (pl011->FR_b.BUSY) { + RUN_BACKGROUND_TASKS; + } + } + COMPLETE_MEMORY_READS; + return len; +} - return 0; +STATIC void disable_interrupt(busio_uart_obj_t *self) { + if (self->uart_id == 1) { + UART1->IER_b.DATA_READY = false; + } +} + +STATIC void enable_interrupt(busio_uart_obj_t *self) { + if (self->uart_id == 1) { + UART1->IER_b.DATA_READY = true; + } } // Read characters. @@ -225,9 +411,7 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t COMPLETE_MEMORY_READS; // Prevent conflict with uart irq. - if (self->uart_id == 1) { - UART1->IER_b.DATA_READY = false; - } + disable_interrupt(self); // Copy as much received data as available, up to len bytes. size_t total_read = ringbuf_get_n(&self->ringbuf, data, len); @@ -238,14 +422,11 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t uint64_t start_ticks = supervisor_ticks_ms64(); // Busy-wait until timeout or until we've read enough chars. while (len > 0 && (supervisor_ticks_ms64() - start_ticks < self->timeout_ms)) { - if (UART1->STAT_b.DATA_READY) { - // Read and advance. - data[total_read] = UART1->IO_b.DATA; - - // Adjust the counters. - len--; - total_read++; - + fetch_all_from_fifo(self); + size_t additional_read = ringbuf_get_n(&self->ringbuf, data + total_read, len); + len -= additional_read; + total_read += additional_read; + if (additional_read > 0) { // Reset the timeout on every character read. start_ticks = supervisor_ticks_ms64(); } @@ -260,14 +441,10 @@ size_t common_hal_busio_uart_read(busio_uart_obj_t *self, uint8_t *data, size_t // Now that we've emptied the ringbuf some, fill it up with anything in the // FIFO. This ensures that we'll empty the FIFO as much as possible and // reset the interrupt when we catch up. - while (UART1->STAT_b.DATA_READY && ringbuf_num_empty(&self->ringbuf) > 0) { - ringbuf_put(&self->ringbuf, (uint8_t)UART1->IO_b.DATA); - } + fetch_all_from_fifo(self); // Re-enable irq. - if (self->uart_id == 1) { - UART1->IER_b.DATA_READY = true; - } + enable_interrupt(self); COMPLETE_MEMORY_READS; if (total_read == 0) { @@ -283,6 +460,31 @@ uint32_t common_hal_busio_uart_get_baudrate(busio_uart_obj_t *self) { } void common_hal_busio_uart_set_baudrate(busio_uart_obj_t *self, uint32_t baudrate) { + if (self->uart_id == 1) { + uint32_t source_clock = vcmailbox_get_clock_rate_measured(VCMAILBOX_CLOCK_CORE); + UART1->BAUD = ((source_clock / (baudrate * 8)) - 1); + } else { + ARM_UART_PL011_Type *pl011 = uart[self->uart_id]; + bool reenable = false; + if (pl011->CR_b.UARTEN) { + pl011->CR_b.UARTEN = false; + reenable = true; + } + uint32_t source_clock = vcmailbox_get_clock_rate_measured(VCMAILBOX_CLOCK_UART); + uint32_t divisor = 16 * baudrate; + pl011->IBRD = source_clock / divisor; + // The fractional divisor is 64ths. + uint32_t remainder = source_clock % divisor; + uint32_t per_tick = (divisor / 64) + 1; + uint32_t adjust = 0; + if (remainder % per_tick > 0) { + adjust = 1; + } + pl011->FBRD = remainder / per_tick + adjust; + if (reenable) { + pl011->CR_b.UARTEN = true; + } + } self->baudrate = baudrate; } @@ -295,6 +497,7 @@ void common_hal_busio_uart_set_timeout(busio_uart_obj_t *self, mp_float_t timeou } uint32_t common_hal_busio_uart_rx_characters_available(busio_uart_obj_t *self) { + fetch_all_from_fifo(self); return ringbuf_num_filled(&self->ringbuf); } @@ -309,5 +512,5 @@ bool common_hal_busio_uart_ready_to_tx(busio_uart_obj_t *self) { if (self->uart_id == 1) { return UART1->STAT_b.TX_READY; } - return false; + return !uart[self->uart_id]->FR_b.TXFF; } diff --git a/ports/broadcom/common-hal/busio/UART.h b/ports/broadcom/common-hal/busio/UART.h index 8ef4073f8e..0590bc28b6 100644 --- a/ports/broadcom/common-hal/busio/UART.h +++ b/ports/broadcom/common-hal/busio/UART.h @@ -39,6 +39,7 @@ typedef struct { uint8_t uart_id; uint32_t baudrate; uint32_t timeout_ms; + bool sigint_enabled; ringbuf_t ringbuf; } busio_uart_obj_t; diff --git a/ports/broadcom/firmware b/ports/broadcom/firmware index 6a5207946e..bf96d0eda5 160000 --- a/ports/broadcom/firmware +++ b/ports/broadcom/firmware @@ -1 +1 @@ -Subproject commit 6a5207946edcd45813d1dd1572ca8bd8101b68b6 +Subproject commit bf96d0eda5952595d717fedb797aeb168483e9fa diff --git a/ports/broadcom/peripherals b/ports/broadcom/peripherals index 2e7b56bbe9..a0666ce987 160000 --- a/ports/broadcom/peripherals +++ b/ports/broadcom/peripherals @@ -1 +1 @@ -Subproject commit 2e7b56bbe941311e54fba66f0b32336bfea4388d +Subproject commit a0666ce987ae96a58e587a04b9f89c7c5707dd13