aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorsago35 <[email protected]>2022-07-12 22:41:56 +0900
committerGitHub <[email protected]>2022-07-12 15:41:56 +0200
commit0bc7c2a61f194746f3dd916fd4d5c7195acd2ae1 (patch)
treed448f2c27215a9c42bbc28cce5c5adf8952ba4df /src
parentd434058aefffeb450d71313f7144a82eceba60c8 (diff)
downloadtinygo-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.go357
-rw-r--r--src/machine/usb.go4
-rw-r--r--src/machine/usb_descriptor.go4
-rw-r--r--src/runtime/runtime_rp2040.go4
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()
}