mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-19 20:14:04 +00:00
begin USB refactor with package machine/usb2
This commit is contained in:
@@ -0,0 +1,194 @@
|
||||
// +build mimxrt1062
|
||||
|
||||
package usb2
|
||||
|
||||
// Implementation of USB device controller interface (dci) for NXP iMXRT1062.
|
||||
|
||||
import (
|
||||
"device/arm"
|
||||
"device/nxp"
|
||||
"runtime/interrupt"
|
||||
"runtime/volatile"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// dciCount defines the number of USB cores to configure for device mode. It is
|
||||
// computed as the sum of all declared device configuration descriptors.
|
||||
const dciCount = descCDCACMConfigCount
|
||||
|
||||
// dciInterruptPriority defines the priority for all USB device interrupts.
|
||||
const dciInterruptPriority = 3
|
||||
|
||||
// deviceController implements USB device controller interface (dci).
|
||||
type deviceController struct {
|
||||
core *core // Parent USB core this instance is attached to
|
||||
port int // USB port index
|
||||
id int // deviceControllerInstance index
|
||||
|
||||
bus *nxp.USB_Type
|
||||
phy *nxp.USBPHY_Type
|
||||
irq interrupt.Interrupt
|
||||
|
||||
cri volatile.Register8 // set to 1 if in critical section, else 0
|
||||
ivm uintptr // interrupt state when entering critical section
|
||||
}
|
||||
|
||||
// deviceControllerInstance provides statically-allocated instances of each USB
|
||||
// device controller configured on this platform.
|
||||
var deviceControllerInstance [dciCount]deviceController
|
||||
|
||||
// initDCI initializes and assigns a free device controller instance to the
|
||||
// given USB port. Returns the initialized device controller or nil if no free
|
||||
// device controller instances remain.
|
||||
func initDCI(port int) (dci, status) {
|
||||
if 0 == dciCount {
|
||||
return nil, statusInvalidArgument // must have defined device descriptors
|
||||
}
|
||||
// Return the first instance whose assigned core is currently nil.
|
||||
for i := range deviceControllerInstance {
|
||||
if nil == deviceControllerInstance[i].core {
|
||||
// Initialize device controller.
|
||||
deviceControllerInstance[i].core = &coreInstance[port]
|
||||
deviceControllerInstance[i].port = port
|
||||
deviceControllerInstance[i].id = i
|
||||
switch port {
|
||||
case 0:
|
||||
deviceControllerInstance[i].bus = nxp.USB1
|
||||
deviceControllerInstance[i].phy = nxp.USBPHY1
|
||||
deviceControllerInstance[i].irq =
|
||||
interrupt.New(nxp.IRQ_USB_OTG1,
|
||||
func(interrupt.Interrupt) {
|
||||
coreInstance[0].dc.interrupt()
|
||||
})
|
||||
|
||||
case 1:
|
||||
deviceControllerInstance[i].bus = nxp.USB2
|
||||
deviceControllerInstance[i].phy = nxp.USBPHY2
|
||||
deviceControllerInstance[i].irq =
|
||||
interrupt.New(nxp.IRQ_USB_OTG2,
|
||||
func(interrupt.Interrupt) {
|
||||
//coreInstance[1].dc.interrupt()
|
||||
})
|
||||
}
|
||||
return &deviceControllerInstance[i], statusOK
|
||||
}
|
||||
}
|
||||
return nil, statusBusy // No free device controller instances available.
|
||||
}
|
||||
|
||||
func (dc *deviceController) init() status {
|
||||
|
||||
dc.bus.BURSTSIZE.Set(0x0404)
|
||||
|
||||
// if dc.phy.PWD.HasBits((nxp.USBPHY_PWD_RXPWDRX | nxp.USBPHY_PWD_RXPWDDIFF |
|
||||
// nxp.USBPHY_PWD_RXPWD1PT1 | nxp.USBPHY_PWD_RXPWDENV |
|
||||
// nxp.USBPHY_PWD_TXPWDV2I | nxp.USBPHY_PWD_TXPWDIBIAS |
|
||||
// nxp.USBPHY_PWD_TXPWDFS)) ||
|
||||
// dc.bus.USBMODE.HasBits(nxp.USB_USBMODE_CM_Msk) {
|
||||
// // reset controller if it was already enabled
|
||||
// dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST)
|
||||
// dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST)
|
||||
// for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) {
|
||||
// }
|
||||
// // clear interrupts
|
||||
// m := arm.DisableInterrupts()
|
||||
// switch dc.port {
|
||||
// case 0:
|
||||
// arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG1))
|
||||
// case 1:
|
||||
// arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG2))
|
||||
// }
|
||||
// dc.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_SFTRST)
|
||||
// }
|
||||
|
||||
// reset the controller
|
||||
dc.phy.CTRL_SET.Set(nxp.USBPHY_CTRL_SFTRST)
|
||||
dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RST)
|
||||
for dc.bus.USBCMD.HasBits(nxp.USB_USBCMD_RST) {
|
||||
}
|
||||
// clear interrupts
|
||||
m := arm.DisableInterrupts()
|
||||
switch dc.port {
|
||||
case 0:
|
||||
arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG1))
|
||||
case 1:
|
||||
arm.EnableInterrupts(m & ^uintptr(nxp.IRQ_USB_OTG2))
|
||||
}
|
||||
dc.phy.CTRL_CLR.Set(nxp.USBPHY_CTRL_CLKGATE | nxp.USBPHY_CTRL_SFTRST)
|
||||
dc.phy.PWD.Set(0)
|
||||
|
||||
// clear the controller mode field and set to device mode:
|
||||
// controller mode (CM) 0x0=idle, 0x2=device-only, 0x3=host-only
|
||||
dc.bus.USBMODE.ReplaceBits(nxp.USB_USBMODE_CM_CM_2,
|
||||
nxp.USB_USBMODE_CM_Msk>>nxp.USB_USBMODE_CM_Pos, nxp.USB_USBMODE_CM_Pos)
|
||||
|
||||
dc.bus.USBCMD.ClearBits(nxp.USB_USBCMD_ITC_Msk) // no interrupt threshold
|
||||
dc.bus.USBMODE.SetBits(nxp.USB_USBMODE_SLOM_Msk) // disable setup lockout
|
||||
dc.bus.USBMODE.ClearBits(nxp.USB_USBMODE_ES_Msk) // use little-endianness
|
||||
|
||||
// configure ENDPOINTLISTADDR
|
||||
|
||||
// enable interrupts
|
||||
dc.bus.USBINTR.Set(
|
||||
nxp.USB_USBINTR_UE_Msk | // bus enable
|
||||
nxp.USB_USBINTR_UEE_Msk | // bus error
|
||||
nxp.USB_USBINTR_PCE_Msk | // port change detect
|
||||
nxp.USB_USBINTR_URE_Msk | // bus reset
|
||||
nxp.USB_USBINTR_SLE) // sleep enable
|
||||
|
||||
// ensure D+ pulled down long enough for host to detect previous disconnect
|
||||
dc.udelay(5000)
|
||||
|
||||
return statusOK
|
||||
}
|
||||
|
||||
func (dc *deviceController) enable(enable bool) status {
|
||||
|
||||
dc.irq.SetPriority(dciInterruptPriority)
|
||||
dc.irq.Enable()
|
||||
|
||||
dc.bus.USBCMD.SetBits(nxp.USB_USBCMD_RS)
|
||||
|
||||
return statusOK
|
||||
}
|
||||
|
||||
func (dc *deviceController) critical(enter bool) status {
|
||||
if enter {
|
||||
// check if critical section already locked
|
||||
if dc.cri.Get() != 0 {
|
||||
return statusRetry
|
||||
}
|
||||
// lock critical section
|
||||
dc.cri.Set(1)
|
||||
// disable interrupts, storing state in receiver
|
||||
dc.ivm = arm.DisableInterrupts()
|
||||
} else {
|
||||
// ensure critical section is locked
|
||||
if dc.cri.Get() != 0 {
|
||||
// re-enable interrupts, using state stored in receiver
|
||||
arm.EnableInterrupts(dc.ivm)
|
||||
// unlock critical section
|
||||
dc.cri.Set(0)
|
||||
}
|
||||
}
|
||||
return statusOK
|
||||
}
|
||||
|
||||
func (dc *deviceController) interrupt() {
|
||||
// read and clear the interrupts that fired
|
||||
status := dc.bus.USBSTS.Get() & dc.bus.USBINTR.Get()
|
||||
dc.bus.USBSTS.Set(status)
|
||||
|
||||
println(strconv.FormatUint(uint64(status), 16))
|
||||
}
|
||||
|
||||
// udelay waits for the given number of microseconds before returning.
|
||||
// We cannot use the sleep timer from this context (import cycle), but we need
|
||||
// an approximate method to spin CPU cycles for short periods of time.
|
||||
//go:inline
|
||||
func (dc *deviceController) udelay(microsec uint32) {
|
||||
n := cycles(microsec, descCPUFrequencyHz)
|
||||
for i := uint32(0); i < n; i++ {
|
||||
arm.Asm(`nop`)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user