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  }