Files
tinygo/src/machine/machine_rp2350_usb.go
T
2025-06-11 07:46:02 +02:00

152 lines
3.9 KiB
Go

//go:build rp2350
package machine
import (
"device/rp"
"machine/usb"
"runtime/interrupt"
)
// Configure the USB peripheral. The config is here for compatibility with the UART interface.
func (dev *USBDevice) Configure(config UARTConfig) {
// Reset usb controller
resetBlock(rp.RESETS_RESET_USBCTRL)
unresetBlockWait(rp.RESETS_RESET_USBCTRL)
// Clear any previous state in dpram just in case
_usbDPSRAM.clear()
// Enable USB interrupt at processor
rp.USB.INTE.Set(0)
intr := interrupt.New(rp.IRQ_USBCTRL_IRQ, handleUSBIRQ)
intr.SetPriority(0x00)
intr.Enable()
irqSet(rp.IRQ_USBCTRL_IRQ, true)
// Mux the controller to the onboard usb phy
rp.USB.USB_MUXING.Set(rp.USB_USB_MUXING_TO_PHY | rp.USB_USB_MUXING_SOFTCON)
// Force VBUS detect so the device thinks it is plugged into a host
rp.USB.USB_PWR.Set(rp.USB_USB_PWR_VBUS_DETECT | rp.USB_USB_PWR_VBUS_DETECT_OVERRIDE_EN)
// Enable the USB controller in device mode.
rp.USB.MAIN_CTRL.Set(rp.USB_MAIN_CTRL_CONTROLLER_EN)
// Enable an interrupt per EP0 transaction
rp.USB.SIE_CTRL.Set(rp.USB_SIE_CTRL_EP0_INT_1BUF)
// Enable interrupts for when a buffer is done, when the bus is reset,
// and when a setup packet is received
rp.USB.INTE.Set(rp.USB_INTE_BUFF_STATUS |
rp.USB_INTE_BUS_RESET |
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)
// 12.7.2 Disable phy isolation
rp.USB.SetMAIN_CTRL_PHY_ISO(0x0)
}
func handleUSBIRQ(intr interrupt.Interrupt) {
status := rp.USB.INTS.Get()
// Setup packet received
if (status & rp.USB_INTS_SETUP_REQ) > 0 {
rp.USB.SIE_STATUS.Set(rp.USB_SIE_STATUS_SETUP_REC)
setup := usb.NewSetup(_usbDPSRAM.setupBytes())
ok := false
if (setup.BmRequestType & usb.REQUEST_TYPE) == usb.REQUEST_STANDARD {
// Standard Requests
ok = handleStandardSetup(setup)
} else {
// Class Interface Requests
if setup.WIndex < uint16(len(usbSetupHandler)) && usbSetupHandler[setup.WIndex] != nil {
ok = usbSetupHandler[setup.WIndex](setup)
}
}
if !ok {
// Stall endpoint?
USBDev.SetStallEPIn(0)
}
}
// Buffer status, one or more buffers have completed
if (status & rp.USB_INTS_BUFF_STATUS) > 0 {
if sendOnEP0DATADONE.offset > 0 {
ep := uint32(0)
data := sendOnEP0DATADONE.data
count := len(data) - sendOnEP0DATADONE.offset
if ep == 0 && count > usb.EndpointPacketSize {
count = usb.EndpointPacketSize
}
sendViaEPIn(ep, data[sendOnEP0DATADONE.offset:], count)
sendOnEP0DATADONE.offset += count
if sendOnEP0DATADONE.offset == len(data) {
sendOnEP0DATADONE.offset = 0
}
}
s2 := rp.USB.BUFF_STATUS.Get()
// OUT (PC -> rp2350)
for i := 0; i < 16; i++ {
if s2&(1<<(i*2+1)) > 0 {
buf := handleEndpointRx(uint32(i))
if usbRxHandler[i] == nil || usbRxHandler[i](buf) {
AckUsbOutTransfer(uint32(i))
}
}
}
// IN (rp2350 -> PC)
for i := 0; i < 16; i++ {
if s2&(1<<(i*2)) > 0 {
if usbTxHandler[i] != nil {
usbTxHandler[i]()
}
}
}
rp.USB.BUFF_STATUS.Set(s2)
}
// Bus is reset
if (status & rp.USB_INTS_BUS_RESET) > 0 {
rp.USB.SIE_STATUS.Set(rp.USB_SIE_STATUS_BUS_RESET)
//fixRP2040UsbDeviceEnumeration()
rp.USB.ADDR_ENDP.Set(0)
initEndpoint(0, usb.ENDPOINT_TYPE_CONTROL)
}
}
func handleUSBSetAddress(setup usb.Setup) bool {
// Using 570μs timeout which is exactly the same as SAMD21.
const ackTimeout = 570
rp.USB.SIE_STATUS.Set(rp.USB_SIE_STATUS_ACK_REC)
sendUSBPacket(0, []byte{}, 0)
// Wait for transfer to complete with a timeout.
t := timer.timeElapsed()
for (rp.USB.SIE_STATUS.Get() & rp.USB_SIE_STATUS_ACK_REC) == 0 {
if dt := timer.timeElapsed() - t; dt >= ackTimeout {
return false
}
}
// Set the device address to that requested by host.
rp.USB.ADDR_ENDP.Set(uint32(setup.WValueL) & rp.USB_ADDR_ENDP_ADDRESS_Msk)
return true
}
func armEPZeroStall() {
rp.USB.EP_STALL_ARM.Set(rp.USB_EP_STALL_ARM_EP0_IN)
}