diff options
author | sago35 <[email protected]> | 2022-07-12 22:41:56 +0900 |
---|---|---|
committer | GitHub <[email protected]> | 2022-07-12 15:41:56 +0200 |
commit | 0bc7c2a61f194746f3dd916fd4d5c7195acd2ae1 (patch) | |
tree | d448f2c27215a9c42bbc28cce5c5adf8952ba4df /src | |
parent | d434058aefffeb450d71313f7144a82eceba60c8 (diff) | |
download | tinygo-0bc7c2a61f194746f3dd916fd4d5c7195acd2ae1.tar.gz tinygo-0bc7c2a61f194746f3dd916fd4d5c7195acd2ae1.zip |
rp2040: add support for usb (#2973)
* rp2040: add support for usb
Diffstat (limited to 'src')
-rw-r--r-- | src/machine/machine_rp2040_usb.go | 357 | ||||
-rw-r--r-- | src/machine/usb.go | 4 | ||||
-rw-r--r-- | src/machine/usb_descriptor.go | 4 | ||||
-rw-r--r-- | src/runtime/runtime_rp2040.go | 4 |
4 files changed, 364 insertions, 5 deletions
diff --git a/src/machine/machine_rp2040_usb.go b/src/machine/machine_rp2040_usb.go new file mode 100644 index 000000000..865b2bece --- /dev/null +++ b/src/machine/machine_rp2040_usb.go @@ -0,0 +1,357 @@ +//go:build rp2040 +// +build rp2040 + +package machine + +import ( + "device/arm" + "device/rp" + "runtime/interrupt" + "unsafe" +) + +var ( + sendOnEP0DATADONE struct { + offset int + data []byte + pid uint32 + } +) + +// Configure the USB peripheral. The config is here for compatibility with the UART interface. +func (dev *USBDevice) Configure(config UARTConfig) { + //// Clear any previous state in dpram just in case + usbDPSRAM.clear() + + //// Enable USB interrupt at processor + rp.USBCTRL_REGS.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.USBCTRL_REGS.USB_MUXING.Set(rp.USBCTRL_REGS_USB_MUXING_TO_PHY | rp.USBCTRL_REGS_USB_MUXING_SOFTCON) + + //// Force VBUS detect so the device thinks it is plugged into a host + rp.USBCTRL_REGS.USB_PWR.Set(rp.USBCTRL_REGS_USB_PWR_VBUS_DETECT | rp.USBCTRL_REGS_USB_PWR_VBUS_DETECT_OVERRIDE_EN) + + //// Enable the USB controller in device mode. + rp.USBCTRL_REGS.MAIN_CTRL.Set(rp.USBCTRL_REGS_MAIN_CTRL_CONTROLLER_EN) + + //// Enable an interrupt per EP0 transaction + rp.USBCTRL_REGS.SIE_CTRL.Set(rp.USBCTRL_REGS_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.USBCTRL_REGS.INTE.Set(rp.USBCTRL_REGS_INTE_BUFF_STATUS | + rp.USBCTRL_REGS_INTE_BUS_RESET | + rp.USBCTRL_REGS_INTE_SETUP_REQ) + + //// Present full speed device by enabling pull up on DP + rp.USBCTRL_REGS.SIE_CTRL.SetBits(rp.USBCTRL_REGS_SIE_CTRL_PULLUP_EN) +} + +func handleUSBIRQ(intr interrupt.Interrupt) { + status := rp.USBCTRL_REGS.INTS.Get() + + // Setup packet received + if (status & rp.USBCTRL_REGS_INTS_SETUP_REQ) > 0 { + rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_SETUP_REC) + setup := newUSBSetup(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? + sendStallViaEPIn(0) + } + + } + + // Buffer status, one or more buffers have completed + if (status & rp.USBCTRL_REGS_INTS_BUFF_STATUS) > 0 { + if sendOnEP0DATADONE.offset > 0 { + ep := uint32(0) + data := sendOnEP0DATADONE.data + count := len(data) - sendOnEP0DATADONE.offset + if ep == 0 && count > usbEndpointPacketSize { + count = usbEndpointPacketSize + } + + sendViaEPIn(ep, data[sendOnEP0DATADONE.offset:], count) + sendOnEP0DATADONE.offset += count + if sendOnEP0DATADONE.offset == len(data) { + sendOnEP0DATADONE.offset = 0 + } + } + + s2 := rp.USBCTRL_REGS.BUFF_STATUS.Get() + + // OUT (PC -> rp2040) + for i := 0; i < 16; i++ { + if s2&(1<<(i*2+1)) > 0 { + buf := handleEndpointRx(uint32(i)) + if usbRxHandler[i] != nil { + usbRxHandler[i](buf) + } + } + } + + // IN (rp2040 -> PC) + for i := 0; i < 16; i++ { + if s2&(1<<(i*2)) > 0 { + if usbTxHandler[i] != nil { + usbTxHandler[i]() + } + } + } + + rp.USBCTRL_REGS.BUFF_STATUS.Set(0xFFFFFFFF) + } + + // Bus is reset + if (status & rp.USBCTRL_REGS_INTS_BUS_RESET) > 0 { + rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_BUS_RESET) + initEndpoint(0, usb_ENDPOINT_TYPE_CONTROL) + } +} + +func initEndpoint(ep, config uint32) { + val := uint32(usbEpControlEnable) | uint32(usbEpControlInterruptPerBuff) + offset := ep*2*USBBufferLen + 0x100 + val |= offset + + switch config { + case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointIn: + val |= usbEpControlEndpointTypeInterrupt + usbDPSRAM.EPxControl[ep].In = val + + case usb_ENDPOINT_TYPE_BULK | usbEndpointOut: + val |= usbEpControlEndpointTypeBulk + usbDPSRAM.EPxControl[ep].Out = val + usbDPSRAM.EPxBufferControl[ep].Out = USBBufferLen & usbBuf0CtrlLenMask + usbDPSRAM.EPxBufferControl[ep].Out |= usbBuf0CtrlAvail + + case usb_ENDPOINT_TYPE_INTERRUPT | usbEndpointOut: + // TODO: not really anything, seems like... + + case usb_ENDPOINT_TYPE_BULK | usbEndpointIn: + val |= usbEpControlEndpointTypeBulk + usbDPSRAM.EPxControl[ep].In = val + + case usb_ENDPOINT_TYPE_CONTROL: + val |= usbEpControlEndpointTypeControl + usbDPSRAM.EPxBufferControl[ep].Out = usbBuf0CtrlAvail + + } +} + +func handleUSBSetAddress(setup USBSetup) bool { + sendUSBPacket(0, []byte{}, 0) + + // last, set the device address to that requested by host + // wait for transfer to complete + timeout := 3000 + rp.USBCTRL_REGS.SIE_STATUS.Set(rp.USBCTRL_REGS_SIE_STATUS_ACK_REC) + for (rp.USBCTRL_REGS.SIE_STATUS.Get() & rp.USBCTRL_REGS_SIE_STATUS_ACK_REC) == 0 { + timeout-- + if timeout == 0 { + return true + } + } + + rp.USBCTRL_REGS.ADDR_ENDP.Set(uint32(setup.WValueL) & rp.USBCTRL_REGS_ADDR_ENDP_ADDRESS_Msk) + + return true +} + +// SendUSBInPacket sends a packet for USB (interrupt in / bulk in). +func SendUSBInPacket(ep uint32, data []byte) bool { + sendUSBPacket(ep, data, 0) + return true +} + +//go:noinline +func sendUSBPacket(ep uint32, data []byte, maxsize uint16) { + count := len(data) + if 0 < int(maxsize) && int(maxsize) < count { + count = int(maxsize) + } + + if ep == 0 { + if count > usbEndpointPacketSize { + count = usbEndpointPacketSize + + sendOnEP0DATADONE.offset = count + sendOnEP0DATADONE.data = data + } else { + sendOnEP0DATADONE.offset = 0 + } + epXdata0[ep] = true + } + + sendViaEPIn(ep, data, count) +} + +func ReceiveUSBControlPacket() ([cdcLineInfoSize]byte, error) { + var b [cdcLineInfoSize]byte + return b, nil +} + +func handleEndpointRx(ep uint32) []byte { + ctrl := usbDPSRAM.EPxBufferControl[ep].Out + usbDPSRAM.EPxBufferControl[ep].Out = USBBufferLen & usbBuf0CtrlLenMask + sz := ctrl & usbBuf0CtrlLenMask + buf := make([]byte, sz) + copy(buf, usbDPSRAM.EPxBuffer[ep].Buffer0[:sz]) + + epXdata0[ep] = !epXdata0[ep] + if epXdata0[ep] { + usbDPSRAM.EPxBufferControl[ep].Out |= usbBuf0CtrlData1Pid + } + + usbDPSRAM.EPxBufferControl[ep].Out |= usbBuf0CtrlAvail + + return buf +} + +func SendZlp() { + sendUSBPacket(0, []byte{}, 0) +} + +func sendViaEPIn(ep uint32, data []byte, count int) { + // Prepare buffer control register value + val := uint32(count) | usbBuf0CtrlAvail + + // DATA0 or DATA1 + epXdata0[ep&0x7F] = !epXdata0[ep&0x7F] + if !epXdata0[ep&0x7F] { + val |= usbBuf0CtrlData1Pid + } + + // Mark as full + val |= usbBuf0CtrlFull + + copy(usbDPSRAM.EPxBuffer[ep&0x7F].Buffer0[:], data[:count]) + usbDPSRAM.EPxBufferControl[ep&0x7F].In = val +} + +func sendStallViaEPIn(ep uint32) { + // Prepare buffer control register value + if ep == 0 { + rp.USBCTRL_REGS.EP_STALL_ARM.Set(rp.USBCTRL_REGS_EP_STALL_ARM_EP0_IN) + } + val := uint32(usbBuf0CtrlFull) + usbDPSRAM.EPxBufferControl[ep&0x7F].In = val + val |= uint32(usbBuf0CtrlStall) + usbDPSRAM.EPxBufferControl[ep&0x7F].In = val +} + +// EnterBootloader should perform a system reset in preparation +// to switch to the bootloader to flash new firmware. +func EnterBootloader() { + arm.DisableInterrupts() + + // TODO: Perform magic reset into bootloader + + arm.SystemReset() +} + +type USBDPSRAM struct { + // Note that EPxControl[0] is not EP0Control but 8-byte setup data. + EPxControl [16]USBEndpointControlRegister + + EPxBufferControl [16]USBBufferControlRegister + + EPxBuffer [16]USBBuffer +} + +type USBEndpointControlRegister struct { + In uint32 + Out uint32 +} +type USBBufferControlRegister struct { + In uint32 + Out uint32 +} + +type USBBuffer struct { + Buffer0 [USBBufferLen]byte + Buffer1 [USBBufferLen]byte +} + +var ( + usbDPSRAM = (*USBDPSRAM)(unsafe.Pointer(uintptr(0x50100000))) + epXdata0 [16]bool +) + +func (d *USBDPSRAM) setupBytes() []byte { + var buf [8]byte + + buf[0] = byte(d.EPxControl[usb_CONTROL_ENDPOINT].In) + buf[1] = byte(d.EPxControl[usb_CONTROL_ENDPOINT].In >> 8) + buf[2] = byte(d.EPxControl[usb_CONTROL_ENDPOINT].In >> 16) + buf[3] = byte(d.EPxControl[usb_CONTROL_ENDPOINT].In >> 24) + buf[4] = byte(d.EPxControl[usb_CONTROL_ENDPOINT].Out) + buf[5] = byte(d.EPxControl[usb_CONTROL_ENDPOINT].Out >> 8) + buf[6] = byte(d.EPxControl[usb_CONTROL_ENDPOINT].Out >> 16) + buf[7] = byte(d.EPxControl[usb_CONTROL_ENDPOINT].Out >> 24) + + return buf[:] +} + +func (d *USBDPSRAM) clear() { + for i := 0; i < len(d.EPxControl); i++ { + d.EPxControl[i].In = 0 + d.EPxControl[i].Out = 0 + d.EPxBufferControl[i].In = 0 + d.EPxBufferControl[i].Out = 0 + } +} + +const ( + // DPRAM : Endpoint control register + usbEpControlEnable = 0x80000000 + usbEpControlDoubleBuffered = 0x40000000 + usbEpControlInterruptPerBuff = 0x20000000 + usbEpControlInterruptPerDoubleBuff = 0x10000000 + usbEpControlEndpointType = 0x0c000000 + usbEpControlInterruptOnStall = 0x00020000 + usbEpControlInterruptOnNak = 0x00010000 + usbEpControlBufferAddress = 0x0000ffff + + usbEpControlEndpointTypeControl = 0x00000000 + usbEpControlEndpointTypeISO = 0x04000000 + usbEpControlEndpointTypeBulk = 0x08000000 + usbEpControlEndpointTypeInterrupt = 0x0c000000 + + // Endpoint buffer control bits + usbBuf1CtrlFull = 0x80000000 + usbBuf1CtrlLast = 0x40000000 + usbBuf1CtrlData0Pid = 0x20000000 + usbBuf1CtrlData1Pid = 0x00000000 + usbBuf1CtrlSel = 0x10000000 + usbBuf1CtrlStall = 0x08000000 + usbBuf1CtrlAvail = 0x04000000 + usbBuf1CtrlLenMask = 0x03FF0000 + usbBuf0CtrlFull = 0x00008000 + usbBuf0CtrlLast = 0x00004000 + usbBuf0CtrlData0Pid = 0x00000000 + usbBuf0CtrlData1Pid = 0x00002000 + usbBuf0CtrlSel = 0x00001000 + usbBuf0CtrlStall = 0x00000800 + usbBuf0CtrlAvail = 0x00000400 + usbBuf0CtrlLenMask = 0x000003FF + + USBBufferLen = 64 +) diff --git a/src/machine/usb.go b/src/machine/usb.go index 3656edc7b..39f8177a5 100644 --- a/src/machine/usb.go +++ b/src/machine/usb.go @@ -1,5 +1,5 @@ -//go:build sam || nrf52840 -// +build sam nrf52840 +//go:build sam || nrf52840 || rp2040 +// +build sam nrf52840 rp2040 package machine diff --git a/src/machine/usb_descriptor.go b/src/machine/usb_descriptor.go index 4489a546b..b6acb9b62 100644 --- a/src/machine/usb_descriptor.go +++ b/src/machine/usb_descriptor.go @@ -1,5 +1,5 @@ -//go:build sam || nrf52840 -// +build sam nrf52840 +//go:build sam || nrf52840 || rp2040 +// +build sam nrf52840 rp2040 package machine diff --git a/src/runtime/runtime_rp2040.go b/src/runtime/runtime_rp2040.go index ad1e67fbe..089c91eda 100644 --- a/src/runtime/runtime_rp2040.go +++ b/src/runtime/runtime_rp2040.go @@ -5,8 +5,8 @@ package runtime import ( "device/arm" - "machine" + "machine/usb/cdc" ) // machineTicks is provided by package machine. @@ -76,6 +76,8 @@ func machineInit() func init() { machineInit() + cdc.EnableUSBCDC() + machine.USBDev.Configure(machine.UARTConfig{}) machine.InitSerial() } |