github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/machine/machine_rp2040_usb_fix_usb_device_enumeration.go (about) 1 //go:build rp2040 2 3 package machine 4 5 import ( 6 "device/arm" 7 "device/rp" 8 ) 9 10 // https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/pico_fix/rp2040_usb_device_enumeration/rp2040_usb_device_enumeration.c 11 // According to errata RP2040-E5: 12 // "It is safe (and inexpensive) to enable the software workaround even when using versions of RP2040 13 // which include the fix in hardware." 14 // So let us always use the software fix. 15 func fixRP2040UsbDeviceEnumeration() { 16 // After coming out of reset, the hardware expects 800us of LS_J (linestate J) time 17 // before it will move to the connected state. However on a hub that broadcasts packets 18 // for other devices this isn't the case. The plan here is to wait for the end of the bus 19 // reset, force an LS_J for 1ms and then switch control back to the USB phy. Unfortunately 20 // this requires us to use GPIO15 as there is no other way to force the input path. 21 // We only need to force DP as DM can be left at zero. It will be gated off by GPIO 22 // logic if it isn't func selected. 23 24 // Wait SE0 phase will call force ls_j phase which will call finish phase 25 hw_enumeration_fix_wait_se0() 26 } 27 28 func hw_enumeration_fix_wait_se0() { 29 // Wait for SE0 to end (i.e. the host to stop resetting). This reset can last quite long. 30 // 10-15ms so we are going to set a timer callback. 31 32 // if timer pool disabled, or no timer available, have to busy wait. 33 hw_enumeration_fix_busy_wait_se0() 34 } 35 36 const ( 37 LS_SE0 = 0b00 38 LS_J = 0b01 39 LS_K = 0b10 40 LS_SE1 = 0b11 41 ) 42 43 const ( 44 dp = 15 45 ) 46 47 var ( 48 gpioCtrlPrev uint32 49 padCtrlPrev uint32 50 ) 51 52 func hw_enumeration_fix_busy_wait_se0() { 53 for ((rp.USBCTRL_REGS.SIE_STATUS.Get() & rp.USBCTRL_REGS_SIE_STATUS_LINE_STATE_Msk) >> rp.USBCTRL_REGS_SIE_STATUS_LINE_STATE_Pos) == LS_SE0 { 54 } 55 56 // Now force LS_J (next stage of fix) 57 hw_enumeration_fix_force_ls_j() 58 } 59 60 func hw_enumeration_fix_force_ls_j() { 61 // DM must be 0 for this to work. This is true if it is selected 62 // to any other function. fn 8 on this pin is only for debug so shouldn't 63 // be selected 64 65 // Before changing any pin state, take a copy of the current gpio control register 66 gpioCtrlPrev = ioBank0.io[dp].ctrl.Get() 67 // Also take a copy of the pads register 68 padCtrlPrev = padsBank0.io[dp].Get() 69 70 // Enable bus keep and force pin to tristate, so USB DP muxing doesn't affect 71 // pin state 72 padsBank0.io[dp].SetBits(rp.PADS_BANK0_GPIO0_PUE | rp.PADS_BANK0_GPIO0_PDE) 73 ioBank0.io[dp].ctrl.ReplaceBits(rp.IO_BANK0_GPIO0_CTRL_OEOVER_DISABLE, rp.IO_BANK0_GPIO0_CTRL_OEOVER_Msk>>rp.IO_BANK0_GPIO0_CTRL_OEOVER_Pos, rp.IO_BANK0_GPIO0_CTRL_OEOVER_Pos) 74 75 // Select function 8 (USB debug muxing) without disturbing other controls 76 ioBank0.io[dp].ctrl.ReplaceBits(8, rp.IO_BANK0_GPIO0_CTRL_FUNCSEL_Msk>>rp.IO_BANK0_GPIO0_CTRL_FUNCSEL_Pos, rp.IO_BANK0_GPIO0_CTRL_FUNCSEL_Pos) 77 78 // J state is a differential 1 for a full speed device so 79 // DP = 1 and DM = 0. Don't actually need to set DM low as it 80 // is already gated assuming it isn't funcseld. 81 ioBank0.io[dp].ctrl.ReplaceBits(rp.IO_BANK0_GPIO1_CTRL_INOVER_HIGH, rp.IO_BANK0_GPIO1_CTRL_INOVER_Msk>>rp.IO_BANK0_GPIO1_CTRL_INOVER_Pos, rp.IO_BANK0_GPIO1_CTRL_INOVER_Pos) 82 83 // Force PHY pull up to stay before switching away from the phy 84 rp.USBCTRL_REGS.USBPHY_DIRECT.SetBits(rp.USBCTRL_REGS_USBPHY_DIRECT_DP_PULLUP_EN) 85 rp.USBCTRL_REGS.USBPHY_DIRECT_OVERRIDE.SetBits(rp.USBCTRL_REGS_USBPHY_DIRECT_OVERRIDE_DP_PULLUP_EN_OVERRIDE_EN) 86 87 // Switch to GPIO phy with LS_J forced 88 rp.USBCTRL_REGS.USB_MUXING.Set(rp.USBCTRL_REGS_USB_MUXING_TO_DIGITAL_PAD | rp.USBCTRL_REGS_USB_MUXING_SOFTCON) 89 90 // LS_J is now forced but while loop to wait ~800us here just to check 91 waitCycles(25000) 92 93 // if timer pool disabled, or no timer available, have to busy wait. 94 hw_enumeration_fix_finish() 95 96 } 97 98 func hw_enumeration_fix_finish() { 99 // Should think we are connected now 100 for (rp.USBCTRL_REGS.SIE_STATUS.Get() & rp.USBCTRL_REGS_SIE_STATUS_CONNECTED) != rp.USBCTRL_REGS_SIE_STATUS_CONNECTED { 101 } 102 103 // Switch back to USB phy 104 rp.USBCTRL_REGS.USB_MUXING.Set(rp.USBCTRL_REGS_USB_MUXING_TO_PHY | rp.USBCTRL_REGS_USB_MUXING_SOFTCON) 105 106 // Get rid of DP pullup override 107 rp.USBCTRL_REGS.USBPHY_DIRECT_OVERRIDE.ClearBits(rp.USBCTRL_REGS_USBPHY_DIRECT_OVERRIDE_DP_PULLUP_EN_OVERRIDE_EN) 108 109 // Finally, restore the gpio ctrl value back to GPIO15 110 ioBank0.io[dp].ctrl.Set(gpioCtrlPrev) 111 // Restore the pad ctrl value 112 padsBank0.io[dp].Set(padCtrlPrev) 113 } 114 115 func waitCycles(n int) { 116 for n > 0 { 117 arm.Asm("nop") 118 n-- 119 } 120 }