Difference between revisions of "Unit DWCOTG"
(6 intermediate revisions by the same user not shown) | |||
Line 7: | Line 7: | ||
'''USB Host Controller Driver for the Synopsys DesignWare Hi-Speed USB 2.0 On-The-Go Controller unit''' | '''USB Host Controller Driver for the Synopsys DesignWare Hi-Speed USB 2.0 On-The-Go Controller unit''' | ||
− | This is a USB Host Controller Driver (HCD) that interfaces with the Synopsys DesignWare Hi-Speed USB 2.0 On-The-Go Controller, henceforth abbreviated as "DWC". | + | This is a USB Host Controller Driver (HCD) that interfaces with the Synopsys DesignWare Hi-Speed USB 2.0 On-The-Go Controller, henceforth abbreviated as "DWC". This is the USB Host Controller used on the BCM2835 SoC used on the Raspberry Pi. |
Please note that there is no publicly available official documentation for this particular piece of hardware, and it uses its own custom host controller interface rather than a standard one such as EHCI. Therefore, this driver was written on a best-effort basis using several sources to glean the necessary hardware details, including the extremely complicated and difficult to understand vendor provided Linux driver. | Please note that there is no publicly available official documentation for this particular piece of hardware, and it uses its own custom host controller interface rather than a standard one such as EHCI. Therefore, this driver was written on a best-effort basis using several sources to glean the necessary hardware details, including the extremely complicated and difficult to understand vendor provided Linux driver. | ||
Line 30: | Line 30: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | | <code> | + | | <code>DWCOTG_USBHOST_DESCRIPTION = 'DWCOTG USB Host';</code> |
− | | | + | | Description of DWCOTG host |
+ | |- | ||
+ | |colspan="2"| | ||
+ | |- | ||
+ | | <code>DWC_MAX_CHANNELS = 16;</code> | ||
+ | | Maximum number of DWC host channels | ||
+ | |- | ||
+ | |colspan="2"| | ||
+ | |- | ||
+ | | <code>DWC_SCHEDULER_MAILSLOT_SIZE = SIZE_1K;</code> | ||
+ | | Mailslot size for USB request scheduler | ||
|- | |- | ||
|colspan="2"| | |colspan="2"| | ||
Line 216: | Line 226: | ||
|colspan="2"|TDWCRegisters: 0x000c : Core USB Configuration Register | |colspan="2"|TDWCRegisters: 0x000c : Core USB Configuration Register | ||
|- | |- | ||
− | | <code> | + | | <code>DWC_USB_CFG_TOUTCAL_MASK = (7 shl 0);</code> |
| style="width: 50%;"| | | style="width: 50%;"| | ||
+ | |- | ||
+ | | <code>DWC_USB_CFG_TOUTCAL_LIMIT = (7 shl 0);</code> | ||
+ | | | ||
|- | |- | ||
| <code>DWC_USB_CFG_PHYIF16 = (1 shl 3);</code> | | <code>DWC_USB_CFG_PHYIF16 = (1 shl 3);</code> | ||
Line 328: | Line 341: | ||
| <code>DWC_CORE_INTERRUPTS_HOST_CHANNEL_INTR = (1 shl 25);</code> | | <code>DWC_CORE_INTERRUPTS_HOST_CHANNEL_INTR = (1 shl 25);</code> | ||
| Bit 25 Channel interrupt occurred. Software must examine the Host All Channels Interrupt Register to determine which channel(s) have pending interrupts, then handle and clear the interrupts for these channels. | | Bit 25 Channel interrupt occurred. Software must examine the Host All Channels Interrupt Register to determine which channel(s) have pending interrupts, then handle and clear the interrupts for these channels. | ||
+ | |- | ||
+ | | <code>DWC_CORE_INTERRUPTS_DISCONNECT = (1 shl 29);</code> | ||
+ | | Bit 29 Disconnect interrupt indicated that a device has been disconnected from the root port. | ||
|- | |- | ||
|} | |} | ||
Line 469: | Line 485: | ||
|- | |- | ||
| <code>DWC_HWCFG2_OTG_ENABLE_IC_USB = (1 shl 31);</code> | | <code>DWC_HWCFG2_OTG_ENABLE_IC_USB = (1 shl 31);</code> | ||
+ | | | ||
+ | |- | ||
+ | |} | ||
+ | </div></div> | ||
+ | <br /> | ||
+ | <div class="toccolours mw-collapsible mw-collapsed" style="border: 1; font-family: arial; padding-top: 20px; padding-bottom: 15px;"> | ||
+ | <div style="font-size: 14px; padding-left: 12px;">'''DWC host configuration''' <code> DWC_HCFG_* </code></div> | ||
+ | <div class="mw-collapsible-content" style="text-align: left; padding-left: 5px;"> | ||
+ | {| class="wikitable" style="font-size: 14px; background: white;" | ||
+ | |- | ||
+ | |colspan="2"|TDWCRegisters: 0x0400 : Host Configuration Register | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FS_LS_PHY_CLK_SEL_MASK = (3 shl 0);</code> | ||
+ | | FS/LS Phy Clock Select | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FS_LS_PHY_CLK_SEL_SHIFT = 0;</code> | ||
+ | | style="width: 50%;"| | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FS_LS_PHY_CLK_SEL_30_60_MHZ = 0;</code> | ||
+ | | | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FS_LS_PHY_CLK_SEL_48_MHZ = 1;</code> | ||
+ | | | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FS_LS_PHY_CLK_SEL_6_MHZ = 2;</code> | ||
+ | | | ||
+ | |- | ||
+ | |colspan="2"| | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FS_LS_SUPPORT_ONLY = (1 shl 2);</code> | ||
+ | | FS/LS Only Support | ||
+ | |- | ||
+ | |colspan="2"| | ||
+ | |- | ||
+ | | <code>DWC_HCFG_ENABLE_32KHZ = (1 shl 7);</code> | ||
+ | | Enable 32-KHz Suspend Mode | ||
+ | |- | ||
+ | |colspan="2"| | ||
+ | |- | ||
+ | | <code>DWC_HCFG_RESUME_VALID_MASK = ($FF shl 8);</code> | ||
+ | | Resume Validation Period | ||
+ | |- | ||
+ | | <code>DWC_HCFG_RESUME_VALID_SHIFT = 8;</code> | ||
+ | | | ||
+ | |- | ||
+ | |colspan="2"| | ||
+ | |- | ||
+ | | <code>DWC_HCFG_DESC_DMA = (1 shl 23);</code> | ||
+ | | Enable Scatter/gather DMA in Host mode | ||
+ | |- | ||
+ | |colspan="2"| | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FRAME_LIST_ENTRIES_MASK = (3 shl 24);</code> | ||
+ | | Frame List Entries | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FRAME_LIST_ENTRIES_SHIFT = 24;</code> | ||
+ | | | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FRAME_LIST_ENTRIES_8 = (0 shl 24);</code> | ||
+ | | | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FRAME_LIST_ENTRIES_8_SIZE = 8;</code> | ||
+ | | | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FRAME_LIST_ENTRIES_16 = (1 shl 24);</code> | ||
+ | | | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FRAME_LIST_ENTRIES_16_SIZE = 16;</code> | ||
+ | | | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FRAME_LIST_ENTRIES_32 = (3 shl 24);</code> | ||
+ | | | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FRAME_LIST_ENTRIES_32_SIZE = 32;</code> | ||
+ | | | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FRAME_LIST_ENTRIES_64 = (3 shl 24);</code> | ||
+ | | | ||
+ | |- | ||
+ | | <code>DWC_HCFG_FRAME_LIST_ENTRIES_64_SIZE = 64;</code> | ||
+ | | | ||
+ | |- | ||
+ | |colspan="2"| | ||
+ | |- | ||
+ | | <code>DWC_HCFG_PERSCHED_ENA = (1 shl 26);</code> | ||
+ | | Enable Periodic Scheduling | ||
+ | |- | ||
+ | | <code>DWC_HCFG_MODE_CH_TIM_EN = (1 shl 31);</code> | ||
| | | | ||
|- | |- | ||
Line 977: | Line 1,081: | ||
|colspan="2"|''Host channel registers'' | |colspan="2"|''Host channel registers'' | ||
|- | |- | ||
− | | <code>HostChannels:array[0.. | + | | <code>HostChannels:array[0..DWC_MAX_CHANNELS - 1] of TDWCHostChannel;</code> |
| (0x0500 : Array of Host Channels) Each host channel can be used to execute an independent USB transfer or transaction simultaneously. A USB transfer may consist of multiple transactions, or packets. To avoid having to re-program the channel, it may be useful to use one channel for all transactions of a transfer before allowing other transfers to be scheduled on it. | | (0x0500 : Array of Host Channels) Each host channel can be used to execute an independent USB transfer or transaction simultaneously. A USB transfer may consist of multiple transactions, or packets. To avoid having to re-program the channel, it may be useful to use one channel for all transactions of a transfer before allowing other transfers to be scheduled on it. | ||
|- | |- | ||
− | | <code> | + | | <code>Reserved0x0700:array[1..((($800 - $500) - (DWC_MAX_CHANNELS * SizeOf(TDWCHostChannel))) div SizeOf(LongWord))] of LongWord;</code> |
− | | ( | + | | (0x0700) |
|- | |- | ||
|colspan="2"|''Device registers'' | |colspan="2"|''Device registers'' | ||
Line 988: | Line 1,092: | ||
| (0x0800) | | (0x0800) | ||
|- | |- | ||
− | | <code> | + | | <code>PowerClockControl:LongWord;</code> |
| (0x0e00 : Power and Clock Gating Control) | | (0x0e00 : Power and Clock Gating Control) | ||
|- | |- | ||
Line 1,048: | Line 1,152: | ||
| <code>Lock:TSpinHandle;</code> | | <code>Lock:TSpinHandle;</code> | ||
| Host lock (Differs from lock in Host portion) (Spin lock due to use by interrupt handler) | | Host lock (Differs from lock in Host portion) (Spin lock due to use by interrupt handler) | ||
+ | |- | ||
+ | | <code>IRQ:LongWord;</code> | ||
+ | | The IRQ assigned to this host | ||
+ | |- | ||
+ | | <code>PowerID:LongWord;</code> | ||
+ | | The Power ID required to power on this host | ||
|- | |- | ||
| <code>Registers:PDWCRegisters;</code> | | <code>Registers:PDWCRegisters;</code> | ||
| Memory mapped registers of the Synopsys DesignWare Hi-Speed USB 2.0 OTG Controller | | Memory mapped registers of the Synopsys DesignWare Hi-Speed USB 2.0 OTG Controller | ||
+ | |- | ||
+ | | <code>ChannelCount:LongWord;</code> | ||
+ | | The number of channels available on this host | ||
|- | |- | ||
| <code>SchedulerThread:TThreadHandle;</code> | | <code>SchedulerThread:TThreadHandle;</code> | ||
Line 1,063: | Line 1,176: | ||
|colspan="2"|''Channel Properties'' | |colspan="2"|''Channel Properties'' | ||
|- | |- | ||
− | | <code>DMABuffers:array[0.. | + | | <code>DMABuffers:array[0..DWC_MAX_CHANNELS - 1] of Pointer;</code> |
− | | DMA buffers allocated for each hardware channel (4 byte aligned/1 per channel) | + | | DMA buffers allocated for each hardware channel (4 byte aligned / 1 per channel) |
|- | |- | ||
− | | <code>ChannelRequests:array[0.. | + | | <code>ChannelRequests:array[0..DWC_MAX_CHANNELS - 1] of PUSBRequest;</code> |
| Current USB request pending on each hardware channel (or nil of no request is pending) | | Current USB request pending on each hardware channel (or nil of no request is pending) | ||
|- | |- | ||
Line 1,135: | Line 1,248: | ||
| <code>StartOfFrameInterruptCount:LongWord;</code> | | <code>StartOfFrameInterruptCount:LongWord;</code> | ||
| Number of start of frame interrupts received by the host controller | | Number of start of frame interrupts received by the host controller | ||
+ | |- | ||
+ | | <code>DisconnectInterruptCount:LongWord;</code> | ||
+ | | Number of disconnect interrupts received by the host controller | ||
|- | |- | ||
| <code>ResubmitCount:LongWord;</code> | | <code>ResubmitCount:LongWord;</code> | ||
Line 1,199: | Line 1,315: | ||
| Number of times a complete split transaction has been restarted at start split due to errors | | Number of times a complete split transaction has been restarted at start split due to errors | ||
|- | |- | ||
+ | |colspan="2"| | ||
+ | |- | ||
+ | | <code>TransferRestartCount:LongWord;</code> | ||
+ | | Number of times a transfer is restarted to continue or retry the transfer | ||
+ | |- | ||
+ | | <code>TransactionRestartCount:LongWord;</code> | ||
+ | | Number of times a transaction is restarted to continue or complete the transfer | ||
|- | |- | ||
|colspan="2"| | |colspan="2"| | ||
Line 1,229: | Line 1,352: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| None documented | | None documented | ||
|- | |- | ||
Line 1,237: | Line 1,360: | ||
'''DWCOTG functions''' | '''DWCOTG functions''' | ||
+ | |||
+ | <div class="toccolours mw-collapsible mw-collapsed" style="border: 1; font-family: arial; padding-top: 0px; padding-bottom: 15px;"> | ||
+ | <pre style="border: 0; padding-bottom:0px;">function DWCHostCreate(Address:PtrUInt; IRQ,PowerID:LongWord):PUSBHost;</pre> | ||
+ | <div style="font-size: 14px; padding-left: 12px;">'''Description:''' Create and register a new DWCOTG host which can be accessed using the USB API</div> | ||
+ | <div class="mw-collapsible-content" style="text-align: left; padding-left: 5px;"> | ||
+ | {| class="wikitable" style="font-size: 14px; background: white;" | ||
+ | |- | ||
+ | ! Address | ||
+ | | The address of the DWCOTG registers | ||
+ | |- | ||
+ | ! IRQ | ||
+ | | The interrupt number for the DWCOTG host | ||
+ | |- | ||
+ | ! PowerID | ||
+ | | The power ID value to power on the host using PowerOn (or POWER_ID_UNKNOWN if not applicable) | ||
+ | |- | ||
+ | ! Return | ||
+ | | Pointer to the new USB host or nil if the USB host could not be created | ||
+ | |- | ||
+ | |} | ||
+ | </div></div> | ||
+ | <br /> | ||
+ | <div class="toccolours mw-collapsible mw-collapsed" style="border: 1; font-family: arial; padding-top: 0px; padding-bottom: 15px;"> | ||
+ | <pre style="border: 0; padding-bottom:0px;">function DWCHostDestroy(Host:PUSBHost):LongWord;</pre> | ||
+ | <div style="font-size: 14px; padding-left: 12px;">'''Description:''' Stop, deregister and destroy a DWCOTG USB host created by this driver</div> | ||
+ | <div class="mw-collapsible-content" style="text-align: left; padding-left: 5px;"> | ||
+ | {| class="wikitable" style="font-size: 14px; background: white;" | ||
+ | |- | ||
+ | ! Host | ||
+ | | The USB host to destroy | ||
+ | |- | ||
+ | ! Return | ||
+ | | ERROR_SUCCESS if completed or another error code on failure | ||
+ | |- | ||
+ | |} | ||
+ | </div></div> | ||
+ | <br /> | ||
+ | |||
+ | '''DWCOTG USB functions''' | ||
<div class="toccolours mw-collapsible mw-collapsed" style="border: 1; font-family: arial; padding-top: 0px; padding-bottom: 15px;"> | <div class="toccolours mw-collapsible mw-collapsed" style="border: 1; font-family: arial; padding-top: 0px; padding-bottom: 15px;"> | ||
Line 1,244: | Line 1,406: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| See usb.pas for the documentation of this interface of the Host Controller Driver | | See usb.pas for the documentation of this interface of the Host Controller Driver | ||
|- | |- | ||
Line 1,256: | Line 1,418: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| See usb.pas for the documentation of this interface of the Host Controller Driver | | See usb.pas for the documentation of this interface of the Host Controller Driver | ||
|- | |- | ||
Line 1,268: | Line 1,430: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| None documented | | None documented | ||
|- | |- | ||
Line 1,280: | Line 1,442: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the Host lock | | Caller must hold the Host lock | ||
|- | |- | ||
Line 1,292: | Line 1,454: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
− | ! | + | ! Note |
| See usb.pas for the documentation of this interface of the Host Controller Driver | | See usb.pas for the documentation of this interface of the Host Controller Driver | ||
− | This Host Controller Driver implements this interface asynchronously, as intended. Furthermore, it uses a simplistic scheduling algorithm where it places requests into a single queue and executes them in the order they were submitted. | + | This Host Controller Driver implements this interface asynchronously, as intended. Furthermore, it uses a simplistic scheduling algorithm where it places requests into a single queue and executes them in the order they were submitted. Transfers that need to be retried, including periodic transfers that receive a NAK reply and split transactions that receive a NYET reply when doing the Complete Split transaction, are scheduled to be retried at an appropriate time by separate code that shortcuts the main queue when the timer expires. |
<br />Caller must hold the device lock. | <br />Caller must hold the device lock. | ||
|- | |- | ||
Line 1,309: | Line 1,471: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
− | ! | + | ! Note |
− | | See usb.pas for the documentation of | + | | See usb.pas for the documentation of this interface of the Host Controller Driver |
Caller must hold the device lock | Caller must hold the device lock | ||
|- | |- | ||
Line 1,325: | Line 1,487: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Request |
| USB transfer to resubmit | | USB transfer to resubmit | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
− | ! | + | ! Note |
| For periodic transfers (e.g. polling an interrupt endpoint), the exact time at which the transfer must be retried is specified by the bInterval member of the endpoint descriptor. For low and full-speed devices, bInterval specifies the number of millisconds to wait before the next poll, while for high-speed devices it specifies the exponent (plus one) of a power-of-two number of milliseconds to wait before the next poll. | | For periodic transfers (e.g. polling an interrupt endpoint), the exact time at which the transfer must be retried is specified by the bInterval member of the endpoint descriptor. For low and full-speed devices, bInterval specifies the number of millisconds to wait before the next poll, while for high-speed devices it specifies the exponent (plus one) of a power-of-two number of milliseconds to wait before the next poll. | ||
− | To actually implement delaying a transfer, we associate each transfer with a thread created on-demand. Each such thread simply enters a loop where it calls sleep() for the appropriate number of milliseconds, then retries the transfer. | + | To actually implement delaying a transfer, we associate each transfer with a thread created on-demand. Each such thread simply enters a loop where it calls sleep() for the appropriate number of milliseconds, then retries the transfer. A semaphore is needed to make the thread do nothing until the request has actually been resubmitted. |
− | <br />This code gets used to | + | <br />This code gets used to schedule polling of IN interrupt endpoints, including those on hubs and HID devices. Thus, polling of these devices for status changes (in the case of hubs) or new input (in the case of HID devices) is done in software. This wakes up the CPU a lot and wastes time and energy. But with USB 2.0, there is no way around this, other than by suspending the USB device which we don't support. |
|- | |- | ||
|} | |} | ||
Line 1,345: | Line 1,507: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWCOTG host to power on | | The DWCOTG host to power on | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
Line 1,360: | Line 1,522: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWCOTG host to power off | | The DWCOTG host to power off | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
Line 1,375: | Line 1,537: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
Line 1,387: | Line 1,549: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
Line 1,399: | Line 1,561: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| None documented | | None documented | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
|- | |- | ||
|} | |} | ||
Line 1,442: | Line 1,573: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| None documented | | None documented | ||
|- | |- | ||
Line 1,454: | Line 1,585: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWC host to get available channel from | | The DWC host to get available channel from | ||
|- | |- | ||
− | ! | + | ! Return |
| Channel number of the next available channel | | Channel number of the next available channel | ||
|- | |- | ||
Line 1,469: | Line 1,600: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWC host to mark available channel on | | The DWC host to mark available channel on | ||
|- | |- | ||
− | ! | + | ! Channel |
| The channel number to mark as available | | The channel number to mark as available | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
Line 1,487: | Line 1,618: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWC host to start the request on | | The DWC host to start the request on | ||
|- | |- | ||
− | ! | + | ! Channel |
| The channel number to start the request on | | The channel number to start the request on | ||
|- | |- | ||
− | ! | + | ! Request |
| USB request to start | | USB request to start | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the host lock | | Caller must hold the host lock | ||
Can be called by the interrupt handler | Can be called by the interrupt handler | ||
Line 1,509: | Line 1,640: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWC host to start the transaction on | | The DWC host to start the transaction on | ||
|- | |- | ||
− | ! | + | ! Channel |
| The host channel number to start the transaction on | | The host channel number to start the transaction on | ||
|- | |- | ||
− | ! | + | ! Request |
| USB request set up for the next transaction | | USB request set up for the next transaction | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the host lock | | Caller must hold the host lock | ||
Can be called by the interrupt handler | Can be called by the interrupt handler | ||
Line 1,531: | Line 1,662: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the Host lock | | Caller must hold the Host lock | ||
|- | |- | ||
Line 1,543: | Line 1,674: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the Host lock | | Caller must hold the Host lock | ||
|- | |- | ||
Line 1,555: | Line 1,686: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the Host lock | | Caller must hold the Host lock | ||
|- | |- | ||
Line 1,567: | Line 1,698: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the Host lock | | Caller must hold the Host lock | ||
|- | |- | ||
Line 1,579: | Line 1,710: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the Host lock | | Caller must hold the Host lock | ||
|- | |- | ||
Line 1,591: | Line 1,722: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWCOTG host for the change request | | The DWCOTG host for the change request | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the Host lock | | Caller must hold the Host lock | ||
Can be called by the interrupt handler | Can be called by the interrupt handler | ||
Line 1,607: | Line 1,738: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWCOTG host for the request | | The DWCOTG host for the request | ||
|- | |- | ||
− | ! | + | ! Request |
| The USB request to perform | | The USB request to perform | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the Host lock | | Caller must hold the Host lock | ||
|- | |- | ||
Line 1,628: | Line 1,759: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWCOTG host for the request | | The DWCOTG host for the request | ||
|- | |- | ||
− | ! | + | ! Request |
| The USB request to perform | | The USB request to perform | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the Host lock | | Caller must hold the Host lock | ||
|- | |- | ||
Line 1,649: | Line 1,780: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWCOTG host for the request | | The DWCOTG host for the request | ||
|- | |- | ||
− | ! | + | ! Request |
| Hub specific request to the root hub | | Hub specific request to the root hub | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the Host lock | | Caller must hold the Host lock | ||
|- | |- | ||
Line 1,670: | Line 1,801: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWCOTG host for the request | | The DWCOTG host for the request | ||
|- | |- | ||
− | ! | + | ! Request |
| Standard request to the root hub | | Standard request to the root hub | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
− | ! | + | ! Note |
| Caller must hold the Host lock | | Caller must hold the Host lock | ||
|- | |- | ||
Line 1,691: | Line 1,822: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| None documented | | None documented | ||
|- | |- | ||
Line 1,703: | Line 1,834: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| USB host to service submitted requests for | | USB host to service submitted requests for | ||
|- | |- | ||
− | ! | + | ! Note |
| This thread receives requests that have been submitted and schedules them on the next available channel. | | This thread receives requests that have been submitted and schedules them on the next available channel. | ||
This is a very simplistic scheduler that does not take into account bandwidth requirements or which endpoint a transfer is for. | This is a very simplistic scheduler that does not take into account bandwidth requirements or which endpoint a transfer is for. | ||
Line 1,719: | Line 1,850: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| USB host to service completed requests for | | USB host to service completed requests for | ||
|- | |- | ||
− | ! | + | ! Note |
| This thread receives completed requests which have either succeeded or failed and calls the completion handler which will call the registered callback for the request. | | This thread receives completed requests which have either succeeded or failed and calls the completion handler which will call the registered callback for the request. | ||
This thread also receives requests that need to be resubmitted and resubmits them for later processing by another thread. | This thread also receives requests that need to be resubmitted and resubmits them for later processing by another thread. | ||
Line 1,739: | Line 1,870: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Request |
| USB request to resubmit | | USB request to resubmit | ||
|- | |- | ||
− | ! | + | ! Note |
| An instance of this thread is created for each request that needs to be resubmitted, this thread then either waits for a predetermined number of milliseconds before scheduling the request on the next available channel. | | An instance of this thread is created for each request that needs to be resubmitted, this thread then either waits for a predetermined number of milliseconds before scheduling the request on the next available channel. | ||
Once the request has been resubmitted the thread waits for the semaphore to be signaled again. If the request is a periodic polling of an endpoint then it will be resubmitted continuously at the predetermined interval, otherwise the semaphore will be destroyed by the completion handler and the thread will terminate. | Once the request has been resubmitted the thread waits for the semaphore to be signaled again. If the request is a periodic polling of an endpoint then it will be resubmitted continuously at the predetermined interval, otherwise the semaphore will be destroyed by the completion handler and the thread will terminate. | ||
Line 1,755: | Line 1,886: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWC host where the interrupt occurred | | The DWC host where the interrupt occurred | ||
|- | |- | ||
Line 1,767: | Line 1,898: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWC host where the interrupt occurred | | The DWC host where the interrupt occurred | ||
|- | |- | ||
− | ! | + | ! Channel |
| DWC host channel where the channel interrupt occurred | | DWC host channel where the channel interrupt occurred | ||
|- | |- | ||
− | ! | + | ! Return |
| USB_STATUS_SUCCESS if completed or another error code on failure | | USB_STATUS_SUCCESS if completed or another error code on failure | ||
|- | |- | ||
− | ! | + | ! Note |
| Only called by DWCInterruptHandler | | Only called by DWCInterruptHandler | ||
Caller must hold the host lock | Caller must hold the host lock | ||
Line 1,789: | Line 1,920: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Request |
| The USB request currently scheduled on this channel | | The USB request currently scheduled on this channel | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWC host where the interrupt occurred | | The DWC host where the interrupt occurred | ||
|- | |- | ||
− | ! | + | ! Channel |
| DWC host channel where the channel interrupt occurred | | DWC host channel where the channel interrupt occurred | ||
|- | |- | ||
− | ! | + | ! Interrupts |
| The currently pending interrupts on this channel | | The currently pending interrupts on this channel | ||
|- | |- | ||
− | ! | + | ! Note |
| Only called by DWCChannelInterrupt | | Only called by DWCChannelInterrupt | ||
Caller must hold the host lock | Caller must hold the host lock | ||
Line 1,817: | Line 1,948: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Note |
| None documented | | None documented | ||
|- | |- | ||
Line 1,829: | Line 1,960: | ||
{| class="wikitable" style="font-size: 14px; background: white;" | {| class="wikitable" style="font-size: 14px; background: white;" | ||
|- | |- | ||
− | ! | + | ! Host |
| The DWCOTG host to calculate the interval for | | The DWCOTG host to calculate the interval for | ||
|- | |- | ||
− | ! | + | ! Note |
| The host frame interval register can only be modified when the port enabled bit is set in the host port control and status register | | The host frame interval register can only be modified when the port enabled bit is set in the host port control and status register | ||
Caller must hold the Host lock | Caller must hold the Host lock |
Latest revision as of 04:10, 23 March 2023
Return to Unit Reference
Contents
[hide]Description
USB Host Controller Driver for the Synopsys DesignWare Hi-Speed USB 2.0 On-The-Go Controller unit
This is a USB Host Controller Driver (HCD) that interfaces with the Synopsys DesignWare Hi-Speed USB 2.0 On-The-Go Controller, henceforth abbreviated as "DWC". This is the USB Host Controller used on the BCM2835 SoC used on the Raspberry Pi.
Please note that there is no publicly available official documentation for this particular piece of hardware, and it uses its own custom host controller interface rather than a standard one such as EHCI. Therefore, this driver was written on a best-effort basis using several sources to glean the necessary hardware details, including the extremely complicated and difficult to understand vendor provided Linux driver.
This file implements the Host Controller Driver Interface defined in TUSBHost. Most importantly, it implements a function to power on and start the host controller (HostStart) and a function to send and receive messages over the USB (HostSubmit).
The DWC is controlled by reading and writing to/from memory-mapped registers. The most important registers are the host channel registers. On this particular hardware, a "host channel", or simply "channel", is a set of registers to which software can read and write to cause transactions to take place on the USB. A fixed number of host channels exist, on the Raspberry Pi there are 8. From the software's perspective, transactions using different host channels can be executed at the same time.
Some of the host channel registers, as well as other registers, deal with interrupts. This driver makes heavy use of these and performs all USB transfers in an interrupt-driven manner. However, due to design flaws in this hardware and in USB 2.0 itself, "interrupt" and "isochronous" transfers still need to make use of software polling when checking for new data, even though each individual transfer is itself interrupt-driven. This means that, for example, if your USB mouse specifies a polling rate of 100 times per second, then it will, unfortunately, be polled 100 times per second in software. For more detail about how interrupts can be controlled on this particular hardware, see the comment above DWCSetupInterrupts. Another important concept is the idea of "packets", "transactions", and "transfers". A USB transfer, such as a single control message or bulk request, may need to be split into multiple packets if it exceeds the endpoint's maximum packet size. Unfortunately, this has to be dealt with explicitly in this code, as this hardware doesn't do it for us. But at least, from the viewpoint of this software, a "transaction" is essentially the same as a "packet".
The "On-The-Go" in the name of this hardware means that it supports the USB On-The-Go protocol, which allows it to act either as a host or a device. However, we only are concerned with it acting as a host, which simplifies our driver.
To simplify the USB core software, a useful design technique (as recommended by the USB 2.0 standard and used in other implementations such as Linux's) is to have the HCD present the root hub as a standard USB hub, even if the root hub is integrated with the host controller and does not appear as a standard hub at the hardware level. This is the case with the DWC, and we implement this design. Therefore, some code in this file deals with faking requests sent to the root hub.
Constants
DWC_*
DWC_USB_PID_*
DWC_RECEIVE_*
DWC_STATUS_*
DWC_COMPLETE_SPLIT_*
DWC_OTG_CTRL_*
DWC_AHB_*
DWC_USB_CFG_*
DWC_SOFT_RESET*
DWC_CORE_INTERRUPTS_*
DWC_VENDOR_ID_*
DWC_HWCFG2_*
DWC_HCFG_*
DWC_HFIR_*
DWC_HFNUM_*
DWC_HOST_PORT_CTRLSTATUS_*
DWC_HOST_CHANNEL_CHARACTERISTICS_*
DWC_HOST_CHANNEL_SPLIT_CONTROL_*
DWC_HOST_CHANNEL_INTERRUPTS_*
DWC_HOST_CHANNEL_INTERRUPT_MASK_*
DWC_HOST_CHANNEL_TRANSFER_*
Type definitions
DWC host channel
DWC registers
DWC root hub configuration
PDWCRootHubConfiguration = ^TDWCRootHubConfiguration;
TDWCRootHubConfiguration = packed record
DWC USB transfer
DWC USB host
Public variables
None defined
Function declarations
Initialization functions
DWCOTG functions
function DWCHostCreate(Address:PtrUInt; IRQ,PowerID:LongWord):PUSBHost;
function DWCHostDestroy(Host:PUSBHost):LongWord;
DWCOTG USB functions
function DWCHostStart(Host:PUSBHost):LongWord;
function DWCHostStop(Host:PUSBHost):LongWord;
function DWCHostReset(Host:PUSBHost):LongWord;
function DWCHostResetEx(Host:PDWCUSBHost):LongWord;
function DWCHostSubmit(Host:PUSBHost; Request:PUSBRequest):LongWord;
function DWCHostCancel(Host:PUSBHost; Request:PUSBRequest):LongWord;
function DWCHostResubmit(Host:PDWCUSBHost; Request:PUSBRequest):LongWord;
function DWCHostPowerOn(Host:PDWCUSBHost):LongWord;
function DWCHostPowerOff(Host:PDWCUSBHost):LongWord;
function DWCHostCheck(Host:PDWCUSBHost):LongWord;
function DWCHostInit(Host:PDWCUSBHost):LongWord;
function DWCHostSetup(Host:PDWCUSBHost):LongWord;
function DWCHostStartFrameInterrupt(Host:PDWCUSBHost; Enable:Boolean):LongWord;
function DWCAllocateChannel(Host:PDWCUSBHost):LongWord;
function DWCReleaseChannel(Host:PDWCUSBHost; Channel:LongWord):LongWord;
function DWCChannelStartTransfer(Host:PDWCUSBHost; Channel:LongWord; Request:PUSBRequest):LongWord;
function DWCChannelStartTransaction(Host:PDWCUSBHost; Channel:LongWord; Request:PUSBRequest):LongWord;
function DWCHostPortReset(Host:PDWCUSBHost):LongWord;
function DWCHostPortPowerOn(Host:PDWCUSBHost):LongWord;
function DWCHostPortGetStatus(Host:PDWCUSBHost):LongWord;
function DWCHostPortSetFeature(Host:PDWCUSBHost; Feature:Word):LongWord;
function DWCHostPortClearFeature(Host:PDWCUSBHost; Feature:Word):LongWord;
procedure DWCHostPortStatusChanged(Host:PDWCUSBHost);
function DWCRootHubRequest(Host:PDWCUSBHost; Request:PUSBRequest):LongWord;
function DWCRootHubControlRequest(Host:PDWCUSBHost; Request:PUSBRequest):LongWord;
function DWCRootHubClassRequest(Host:PDWCUSBHost; Request:PUSBRequest):LongWord;
function DWCRootHubStandardRequest(Host:PDWCUSBHost; Request:PUSBRequest):LongWord;
function DWCSchedulerStart(Host:PDWCUSBHost):LongWord;
function DWCSchedulerExecute(Host:PDWCUSBHost):PtrInt;
function DWCCompletionExecute(Host:PDWCUSBHost):PtrInt;
function DWCResubmitExecute(Request:PUSBRequest):PtrInt;
procedure DWCInterruptHandler(Host:PDWCUSBHost);
function DWCChannelInterrupt(Host:PDWCUSBHost; Channel:LongWord):LongWord;
function DWCChannelCompleted(Host:PDWCUSBHost; Request:PUSBRequest; Channel,Interrupts:LongWord):LongWord;
DWCOTG helper functions
function DWCDivRoundUp(Number,Denominator:LongWord):LongWord;
function DWCCalculateFrameInterval(Host:PDWCUSBHost):LongWord;
Return to Unit Reference