rp2040: Implement workaround for USB errata "rp2040-e5"

The rp2040 USB may not connect after a reset.  Implementation the
recommended workaround.

Signed-off-by: Lasse Dalegaard <dalegaard@gmail.com>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2022-06-07 11:43:19 -04:00
parent c30e5f847c
commit ea4f6d6a77
2 changed files with 105 additions and 3 deletions

View File

@ -8,6 +8,12 @@ All dates in this document are approximate.
## Changes
20220612: The rp2040 micro-controller now has a workaround for the
"rp2040-e5" USB errata. This should make initial USB connections more
reliable. However, it may result in a change in behavior for the
gpio15 pin. It is unlikely the gpio15 behavior change will be
noticeable.
20220407: The temperature_fan `pid_integral_max` config option has
been removed (it was deprecated on 20210612).

View File

@ -7,9 +7,13 @@
#include <string.h> // memcpy
#include "board/armcm_boot.h" // armcm_enable_irq
#include "board/io.h" // writeb
#include "board/misc.h" // timer_read_time
#include "board/usb_cdc.h" // usb_notify_ep0
#include "board/usb_cdc_ep.h" // USB_CDC_EP_BULK_IN
#include "board/usbstd.h" // USB_ENDPOINT_XFER_INT
#include "hardware/regs/sysinfo.h" // SYSINFO_CHIP_ID_OFFSET
#include "hardware/structs/iobank0.h" // iobank0_hw
#include "hardware/structs/padsbank0.h" // padsbank0_hw
#include "hardware/structs/resets.h" // RESETS_RESET_USBCTRL_BITS
#include "hardware/structs/usb.h" // usb_hw
#include "internal.h" // enable_pclock
@ -164,6 +168,89 @@ usb_request_bootloader(void)
}
/****************************************************************
* USB Errata workaround
****************************************************************/
// The rp2040 USB has an errata causing it to sometimes not connect
// after a reset. The following code has extracts from the PICO SDK.
static struct task_wake usb_errata_wake;
// Workaround for rp2040-e5 errata
void
usb_errata_task(void)
{
if (!sched_check_wake(&usb_errata_wake))
return;
if (usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS)
// Already connected - workaround not needed
return;
// Wait for not in SE0 state
if (!(usb_hw->sie_status & USB_SIE_STATUS_LINE_STATE_BITS)) {
sched_wake_task(&usb_errata_wake);
return;
}
// Backup GPIO15 pad state
uint32_t dp = 15;
uint32_t gpio_ctrl_prev = iobank0_hw->io[dp].ctrl;
uint32_t pad_ctrl_prev = padsbank0_hw->io[dp];
// Enable bus keep
hw_write_masked(&padsbank0_hw->io[dp],
PADS_BANK0_GPIO15_PUE_BITS | PADS_BANK0_GPIO15_PDE_BITS,
PADS_BANK0_GPIO15_PUE_BITS | PADS_BANK0_GPIO15_PDE_BITS);
// Disable pad output
hw_write_masked(&iobank0_hw->io[dp].ctrl,
0x2 << IO_BANK0_GPIO15_CTRL_OEOVER_LSB,
IO_BANK0_GPIO15_CTRL_OEOVER_BITS);
// Enable USB debug muxing function
hw_write_masked(&iobank0_hw->io[dp].ctrl,
8 << IO_BANK0_GPIO15_CTRL_FUNCSEL_LSB,
IO_BANK0_GPIO15_CTRL_FUNCSEL_BITS);
// Set input override
hw_write_masked(&iobank0_hw->io[dp].ctrl,
0x3 << IO_BANK0_GPIO15_CTRL_INOVER_LSB,
IO_BANK0_GPIO15_CTRL_INOVER_BITS);
// PHY pullups need to stay on
hw_set_alias(usb_hw)->phy_direct = USB_USBPHY_DIRECT_DP_PULLUP_EN_BITS;
hw_set_alias(usb_hw)->phy_direct_override =
USB_USBPHY_DIRECT_OVERRIDE_DP_PULLUP_EN_OVERRIDE_EN_BITS;
// Switch from USB PHY to GPIO PHY, now with J forced
usb_hw->muxing = (USB_USB_MUXING_TO_DIGITAL_PAD_BITS
| USB_USB_MUXING_SOFTCON_BITS);
// Wait 1ms
uint32_t endtime = timer_read_time() + timer_from_us(1000);
while (timer_is_before(timer_read_time(), endtime))
;
// Verify in connected state
endtime += timer_from_us(1000);
for (;;) {
if (usb_hw->sie_status & USB_SIE_STATUS_CONNECTED_BITS)
break;
if (timer_is_before(endtime, timer_read_time()))
// Something went wrong - restore state and continue anyway
break;
}
// Switch back to USB phy
usb_hw->muxing = USB_USB_MUXING_TO_PHY_BITS | USB_USB_MUXING_SOFTCON_BITS;
// Unset PHY pullup overrides
hw_clear_alias(usb_hw)->phy_direct_override =
USB_USBPHY_DIRECT_OVERRIDE_DP_PULLUP_EN_OVERRIDE_EN_BITS;
// Restore GPIO control states
iobank0_hw->io[dp].ctrl = gpio_ctrl_prev;
padsbank0_hw->io[dp] = pad_ctrl_prev;
}
DECL_TASK(usb_errata_task);
/****************************************************************
* Setup and interrupts
****************************************************************/
@ -191,6 +278,10 @@ USB_Handler(void)
}
}
}
if (ints & USB_INTS_BUS_RESET_BITS) {
usb_hw->sie_status = USB_SIE_STATUS_BUS_RESET_BITS;
sched_wake_task(&usb_errata_wake);
}
}
static void
@ -228,15 +319,20 @@ usbserial_init(void)
| USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN_BITS);
usb_hw->main_ctrl = USB_MAIN_CTRL_CONTROLLER_EN_BITS;
// Check if usb errata workaround needed
enable_pclock(RESETS_RESET_SYSINFO_BITS);
uint32_t chip_id = *((io_ro_32*)(SYSINFO_BASE + SYSINFO_CHIP_ID_OFFSET));
uint32_t version = ((chip_id & SYSINFO_CHIP_ID_REVISION_BITS)
>> SYSINFO_CHIP_ID_REVISION_LSB);
// Enable irqs
usb_hw->sie_ctrl = USB_SIE_CTRL_EP0_INT_1BUF_BITS;
usb_hw->inte = USB_INTE_BUFF_STATUS_BITS | USB_INTE_SETUP_REQ_BITS;
usb_hw->inte = (USB_INTE_BUFF_STATUS_BITS | USB_INTE_SETUP_REQ_BITS
| (version == 1 ? USB_INTE_BUS_RESET_BITS: 0));
armcm_enable_irq(USB_Handler, USBCTRL_IRQ_IRQn, 1);
// Enable USB pullup
usb_hw->sie_ctrl = (USB_SIE_CTRL_EP0_INT_1BUF_BITS
| USB_SIE_CTRL_PULLUP_EN_BITS);
// XXX - errata reset workaround??
}
DECL_INIT(usbserial_init);