diff --git a/src/machine/machine_atsamd21_usb.go b/src/machine/machine_atsamd21_usb.go index 45ba18d454..45b6ba8241 100644 --- a/src/machine/machine_atsamd21_usb.go +++ b/src/machine/machine_atsamd21_usb.go @@ -51,7 +51,7 @@ func (dev *USBDevice) Configure(config UARTConfig) { sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos) // attach - sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH) + dev.Attach() // enable interrupt for end of reset sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST) @@ -68,6 +68,20 @@ func (dev *USBDevice) Configure(config UARTConfig) { dev.initcomplete = true } +// Attach connects the device to the USB bus, allowing the host to detect and +// enumerate it. It can be used together with Detach to delay enumeration +// until the USB configuration (device identifiers, classes, ...) is complete. +func (dev *USBDevice) Attach() { + sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH) +} + +// Detach disconnects the device from the USB bus. To the host this appears +// as if the device was unplugged. A subsequent Attach makes the host +// enumerate the device again. +func (dev *USBDevice) Detach() { + sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_DETACH) +} + func handlePadCalibration() { // Load Pad Calibration data from non-volatile memory // This requires registers that are not included in the SVD file. diff --git a/src/machine/machine_atsamd51_usb.go b/src/machine/machine_atsamd51_usb.go index 186ba4ce01..d10755db9c 100644 --- a/src/machine/machine_atsamd51_usb.go +++ b/src/machine/machine_atsamd51_usb.go @@ -51,7 +51,7 @@ func (dev *USBDevice) Configure(config UARTConfig) { sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_SPDCONF_FS << sam.USB_DEVICE_CTRLB_SPDCONF_Pos) // attach - sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH) + dev.Attach() // enable interrupt for end of reset sam.USB_DEVICE.INTENSET.SetBits(sam.USB_DEVICE_INTENSET_EORST) @@ -71,6 +71,20 @@ func (dev *USBDevice) Configure(config UARTConfig) { dev.initcomplete = true } +// Attach connects the device to the USB bus, allowing the host to detect and +// enumerate it. It can be used together with Detach to delay enumeration +// until the USB configuration (device identifiers, classes, ...) is complete. +func (dev *USBDevice) Attach() { + sam.USB_DEVICE.CTRLB.ClearBits(sam.USB_DEVICE_CTRLB_DETACH) +} + +// Detach disconnects the device from the USB bus. To the host this appears +// as if the device was unplugged. A subsequent Attach makes the host +// enumerate the device again. +func (dev *USBDevice) Detach() { + sam.USB_DEVICE.CTRLB.SetBits(sam.USB_DEVICE_CTRLB_DETACH) +} + func handlePadCalibration() { // Load Pad Calibration data from non-volatile memory // This requires registers that are not included in the SVD file. diff --git a/src/machine/machine_esp32c3_usb.go b/src/machine/machine_esp32c3_usb.go index 5d58c35ee2..4af895a52a 100644 --- a/src/machine/machine_esp32c3_usb.go +++ b/src/machine/machine_esp32c3_usb.go @@ -67,6 +67,11 @@ func (dev *USBDevice) SetStallEPOut(ep uint32) {} func (dev *USBDevice) ClearStallEPIn(ep uint32) {} func (dev *USBDevice) ClearStallEPOut(ep uint32) {} +// Attach and Detach are no-ops: the USB Serial/JTAG controller has no +// software-controlled soft-connect, it is always attached to the bus. +func (dev *USBDevice) Attach() {} +func (dev *USBDevice) Detach() {} + // initUSB is intentionally empty — the interp phase evaluates init() // functions at compile time and cannot access hardware registers. // Actual hardware setup is deferred to the first Configure() call. diff --git a/src/machine/machine_esp32c6_usb.go b/src/machine/machine_esp32c6_usb.go index aa9bc988bd..f05d54c5ae 100644 --- a/src/machine/machine_esp32c6_usb.go +++ b/src/machine/machine_esp32c6_usb.go @@ -66,6 +66,11 @@ func (dev *USBDevice) SetStallEPOut(ep uint32) {} func (dev *USBDevice) ClearStallEPIn(ep uint32) {} func (dev *USBDevice) ClearStallEPOut(ep uint32) {} +// Attach and Detach are no-ops: the USB Serial/JTAG controller has no +// software-controlled soft-connect, it is always attached to the bus. +func (dev *USBDevice) Attach() {} +func (dev *USBDevice) Detach() {} + // initUSB is intentionally empty — the interp phase evaluates init() // functions at compile time and cannot access hardware registers. // Actual hardware setup is deferred to the first Configure() call. diff --git a/src/machine/machine_esp32xx_usb.go b/src/machine/machine_esp32xx_usb.go index e9d7275d9e..7679f436cb 100644 --- a/src/machine/machine_esp32xx_usb.go +++ b/src/machine/machine_esp32xx_usb.go @@ -67,6 +67,11 @@ func (dev *USBDevice) SetStallEPOut(ep uint32) {} func (dev *USBDevice) ClearStallEPIn(ep uint32) {} func (dev *USBDevice) ClearStallEPOut(ep uint32) {} +// Attach and Detach are no-ops: the USB Serial/JTAG controller has no +// software-controlled soft-connect, it is always attached to the bus. +func (dev *USBDevice) Attach() {} +func (dev *USBDevice) Detach() {} + // initUSB is intentionally empty — the interp phase evaluates init() // functions at compile time and cannot access hardware registers. // Actual hardware setup is deferred to the first Configure() call. diff --git a/src/machine/machine_nrf52840_usb.go b/src/machine/machine_nrf52840_usb.go index 0dc222b372..90904d0dcb 100644 --- a/src/machine/machine_nrf52840_usb.go +++ b/src/machine/machine_nrf52840_usb.go @@ -22,6 +22,11 @@ var ( epinen uint32 epouten uint32 easyDMABusy volatile.Register8 + + // usbDetached keeps the device detached from the bus after Detach: the + // USB IRQ handler re-enables the DP pull-up on every power-ready event, + // which would otherwise silently undo a Detach. + usbDetached bool ) // enterCriticalSection is used to protect access to easyDMA - only one thing @@ -89,6 +94,23 @@ func (dev *USBDevice) Configure(config UARTConfig) { dev.initcomplete = true } +// Attach connects the device to the USB bus by enabling the DP pull-up, +// allowing the host to detect and enumerate it. It can be used together with +// Detach to delay enumeration until the USB configuration (device +// identifiers, classes, ...) is complete. +func (dev *USBDevice) Attach() { + usbDetached = false + nrf.USBD.USBPULLUP.Set(1) +} + +// Detach disconnects the device from the USB bus by disabling the DP pull-up. +// To the host this appears as if the device was unplugged. A subsequent +// Attach makes the host enumerate the device again. +func (dev *USBDevice) Detach() { + usbDetached = true + nrf.USBD.USBPULLUP.Set(0) +} + func handleUSBIRQ(interrupt.Interrupt) { if nrf.USBD.EVENTS_SOF.Get() == 1 { nrf.USBD.EVENTS_SOF.Set(0) @@ -103,7 +125,9 @@ func handleUSBIRQ(interrupt.Interrupt) { // Configure control endpoint initEndpoint(0, usb.ENDPOINT_TYPE_CONTROL) - nrf.USBD.USBPULLUP.Set(1) + if !usbDetached { + nrf.USBD.USBPULLUP.Set(1) + } usbConfiguration = 0 } diff --git a/src/machine/machine_rp2040_usb.go b/src/machine/machine_rp2040_usb.go index efa6440428..961f9bbd2f 100644 --- a/src/machine/machine_rp2040_usb.go +++ b/src/machine/machine_rp2040_usb.go @@ -43,9 +43,24 @@ func (dev *USBDevice) Configure(config UARTConfig) { rp.USBCTRL_REGS_INTE_SETUP_REQ) // Present full speed device by enabling pull up on DP + dev.Attach() +} + +// Attach connects the device to the USB bus by enabling the DP pull-up, +// allowing the host to detect and enumerate it. It can be used together with +// Detach to delay enumeration until the USB configuration (device +// identifiers, classes, ...) is complete. +func (dev *USBDevice) Attach() { rp.USBCTRL_REGS.SIE_CTRL.SetBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN) } +// Detach disconnects the device from the USB bus by disabling the DP pull-up. +// To the host this appears as if the device was unplugged. A subsequent +// Attach makes the host enumerate the device again. +func (dev *USBDevice) Detach() { + rp.USBCTRL_REGS.SIE_CTRL.ClearBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN) +} + func handleUSBIRQ(intr interrupt.Interrupt) { status := rp.USBCTRL_REGS.INTS.Get() diff --git a/src/machine/machine_rp2350_usb.go b/src/machine/machine_rp2350_usb.go index ca565738a5..1c4d1588ad 100644 --- a/src/machine/machine_rp2350_usb.go +++ b/src/machine/machine_rp2350_usb.go @@ -43,12 +43,27 @@ func (dev *USBDevice) Configure(config UARTConfig) { rp.USB_INTE_SETUP_REQ) // Present full speed device by enabling pull up on DP - rp.USB.SIE_CTRL.SetBits(rp.USB_SIE_CTRL_PULLUP_EN) + dev.Attach() // 12.7.2 Disable phy isolation rp.USB.SetMAIN_CTRL_PHY_ISO(0x0) } +// Attach connects the device to the USB bus by enabling the DP pull-up, +// allowing the host to detect and enumerate it. It can be used together with +// Detach to delay enumeration until the USB configuration (device +// identifiers, classes, ...) is complete. +func (dev *USBDevice) Attach() { + rp.USB.SIE_CTRL.SetBits(rp.USB_SIE_CTRL_PULLUP_EN) +} + +// Detach disconnects the device from the USB bus by disabling the DP pull-up. +// To the host this appears as if the device was unplugged. A subsequent +// Attach makes the host enumerate the device again. +func (dev *USBDevice) Detach() { + rp.USB.SIE_CTRL.ClearBits(rp.USB_SIE_CTRL_PULLUP_EN) +} + func handleUSBIRQ(intr interrupt.Interrupt) { status := rp.USB.INTS.Get() diff --git a/src/machine/machine_stm32_otgfs_usb.go b/src/machine/machine_stm32_otgfs_usb.go index 47f70936d5..639a301ee7 100644 --- a/src/machine/machine_stm32_otgfs_usb.go +++ b/src/machine/machine_stm32_otgfs_usb.go @@ -254,7 +254,7 @@ func (dev *USBDevice) Configure(config UARTConfig) { otgPower.PCGCCTL.Set(0) // Soft-disconnect now (after CSRST reset DCTL to its default connected state). - otgDevice.DCTL.SetBits(dctlSDIS) + dev.Detach() // ---- 7. Configure data FIFOs -------------------------------------------- @@ -311,11 +311,26 @@ func (dev *USBDevice) Configure(config UARTConfig) { // ---- 12. Connect to host (clear soft-disconnect) ----------------------- - otgDevice.DCTL.ClearBits(dctlSDIS) + dev.Attach() dev.initcomplete = true } +// Attach connects the device to the USB bus by releasing soft disconnect, +// allowing the host to detect and enumerate it. It can be used together with +// Detach to delay enumeration until the USB configuration (device +// identifiers, classes, ...) is complete. +func (dev *USBDevice) Attach() { + otgDevice.DCTL.ClearBits(dctlSDIS) +} + +// Detach disconnects the device from the USB bus by asserting soft +// disconnect. To the host this appears as if the device was unplugged. A +// subsequent Attach makes the host enumerate the device again. +func (dev *USBDevice) Detach() { + otgDevice.DCTL.SetBits(dctlSDIS) +} + // handleUSBIRQ is the OTG FS interrupt handler, dispatching on GINTSTS bits. func handleUSBIRQ(intr interrupt.Interrupt) { status := stm32.OTG_FS_GLOBAL.GINTSTS.Get() & diff --git a/src/machine/machine_stm32h7_usb.go b/src/machine/machine_stm32h7_usb.go index 7673819b01..e895600294 100644 --- a/src/machine/machine_stm32h7_usb.go +++ b/src/machine/machine_stm32h7_usb.go @@ -206,7 +206,7 @@ func (dev *USBDevice) Configure(config UARTConfig) { // Stay soft-disconnected until configuration is complete; CSRST left // DCTL at its default "connected" state. - usbOTG.DCTL.SetBits(DCTL_SDIS) + dev.Detach() // 5. Force device mode now that the core is out of reset. The mode // change takes effect only after up to 25 ms (RM0433); poll GINTSTS.CMOD @@ -265,9 +265,24 @@ func (dev *USBDevice) Configure(config UARTConfig) { dev.initcomplete = true // Release soft-disconnect: pulls D+ high, making device visible to host. + dev.Attach() +} + +// Attach connects the device to the USB bus by releasing soft disconnect, +// allowing the host to detect and enumerate it. It can be used together with +// Detach to delay enumeration until the USB configuration (device +// identifiers, classes, ...) is complete. +func (dev *USBDevice) Attach() { usbOTG.DCTL.ClearBits(DCTL_SDIS) } +// Detach disconnects the device from the USB bus by asserting soft +// disconnect. To the host this appears as if the device was unplugged. A +// subsequent Attach makes the host enumerate the device again. +func (dev *USBDevice) Detach() { + usbOTG.DCTL.SetBits(DCTL_SDIS) +} + func initEndpoint(ep, config uint32) { if ep == 0 { // Control endpoint