summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsakumisu <[email protected]>2022-03-20 21:19:03 +0800
committersakumisu <[email protected]>2022-03-20 21:19:03 +0800
commit0c39b0a2bdd90a01eec43e7028175fc006c2392e (patch)
tree16e218733466539d2ad469b0851851cfffc5b318
parent214e2f39b3f7d19bd98c6bb648a8e2ea11f15b93 (diff)
add musb host porting
-rw-r--r--port/musb/usb_hc_musb.c1051
1 files changed, 1051 insertions, 0 deletions
diff --git a/port/musb/usb_hc_musb.c b/port/musb/usb_hc_musb.c
new file mode 100644
index 00000000..977f3380
--- /dev/null
+++ b/port/musb/usb_hc_musb.c
@@ -0,0 +1,1051 @@
+#include "usbh_core.h"
+#include "usb_musb_reg.h"
+
+#ifdef USB_MUSB_SUNXI
+
+#define SUNXI_SRAMC_BASE 0x01c00000
+#define SUNXI_USB0_BASE 0x01c13000
+
+#define USBC_REG_o_PHYCTL 0x0404
+
+#ifndef USB_BASE
+#define USB_BASE (SUNXI_USB0_BASE)
+#endif
+
+#ifndef USBH_IRQHandler
+#define USBH_IRQHandler USB_INT_Handler //use actual usb irq name instead
+void USBH_IRQHandler(int, void *);
+#endif
+
+#define MUSB_IND_TXMAP_OFFSET 0x80
+#define MUSB_IND_TXCSRL_OFFSET 0x82
+#define MUSB_IND_TXCSRH_OFFSET 0x83
+#define MUSB_IND_RXMAP_OFFSET 0x84
+#define MUSB_IND_RXCSRL_OFFSET 0x86
+#define MUSB_IND_RXCSRH_OFFSET 0x87
+#define MUSB_IND_RXCOUNT_OFFSET 0x88
+#define MUSB_IND_TXTYPE_OFFSET 0x8C
+#define MUSB_IND_TXINTERVAL_OFFSET 0x8D
+#define MUSB_IND_RXTYPE_OFFSET 0x8E
+#define MUSB_IND_RXINTERVAL_OFFSET 0x8F
+#define MUSB_FIFO_OFFSET 0x00
+#else
+#ifndef USBH_IRQHandler
+#define USBH_IRQHandler USB_INT_Handler
+#endif
+
+#ifndef USB_BASE
+#define USB_BASE (0x40086400UL)
+#endif
+
+#define MUSB_IND_TXMAP_OFFSET 0x10
+#define MUSB_IND_TXCSRL_OFFSET 0x12
+#define MUSB_IND_TXCSRH_OFFSET 0x13
+#define MUSB_IND_RXMAP_OFFSET 0x14
+#define MUSB_IND_RXCSRL_OFFSET 0x16
+#define MUSB_IND_RXCSRH_OFFSET 0x17
+#define MUSB_IND_RXCOUNT_OFFSET 0x18
+#define MUSB_IND_TXTYPE_OFFSET 0x1A
+#define MUSB_IND_TXINTERVAL_OFFSET 0x1B
+#define MUSB_IND_RXTYPE_OFFSET 0x1C
+#define MUSB_IND_RXINTERVAL_OFFSET 0x1D
+#define MUSB_FIFO_OFFSET 0x20
+#endif
+
+#ifndef CONFIG_USBHOST_CHANNELS
+#define CONFIG_USBHOST_CHANNELS 8
+#endif
+
+#define USB ((USB0_Type *)USB_BASE)
+
+#define HWREG(x) \
+ (*((volatile uint32_t *)(x)))
+#define HWREGH(x) \
+ (*((volatile uint16_t *)(x)))
+#define HWREGB(x) \
+ (*((volatile uint8_t *)(x)))
+
+#define USB_TXMAPx_BASE (USB_BASE + MUSB_IND_TXMAP_OFFSET)
+#define USB_RXMAPx_BASE (USB_BASE + MUSB_IND_RXMAP_OFFSET)
+#define USB_TXCSRLx_BASE (USB_BASE + MUSB_IND_TXCSRL_OFFSET)
+#define USB_RXCSRLx_BASE (USB_BASE + MUSB_IND_RXCSRL_OFFSET)
+#define USB_TXCSRHx_BASE (USB_BASE + MUSB_IND_TXCSRH_OFFSET)
+#define USB_RXCSRHx_BASE (USB_BASE + MUSB_IND_RXCSRH_OFFSET)
+#define USB_RXCOUNTx_BASE (USB_BASE + MUSB_IND_RXCOUNT_OFFSET)
+#define USB_FIFO_BASE(ep_idx) (USB_BASE + MUSB_FIFO_OFFSET + 0x4 * ep_idx)
+#define USB_TXTYPEx_BASE (USB_BASE + MUSB_IND_TXTYPE_OFFSET)
+#define USB_RXTYPEx_BASE (USB_BASE + MUSB_IND_RXTYPE_OFFSET)
+#define USB_TXINTERVALx_BASE (USB_BASE + MUSB_IND_TXINTERVAL_OFFSET)
+#define USB_RXINTERVALx_BASE (USB_BASE + MUSB_IND_RXINTERVAL_OFFSET)
+
+#define USB_TXADDR_BASE(ep_idx) (&USB->TXFUNCADDR0 + 0x8 * ep_idx)
+#define USB_TXHUBADDR_BASE(ep_idx) (&USB->TXFUNCADDR0 + 0x8 * ep_idx + 2)
+#define USB_TXHUBPORT_BASE(ep_idx) (&USB->TXFUNCADDR0 + 0x8 * ep_idx + 3)
+#define USB_RXADDR_BASE(ep_idx) (&USB->TXFUNCADDR0 + 0x8 * ep_idx + 4)
+#define USB_RXHUBADDR_BASE(ep_idx) (&USB->TXFUNCADDR0 + 0x8 * ep_idx + 6)
+#define USB_RXHUBPORT_BASE(ep_idx) (&USB->TXFUNCADDR0 + 0x8 * ep_idx + 7)
+
+typedef enum {
+ USB_EP0_STATE_SETUP = 0x0, /**< SETUP DATA */
+ USB_EP0_STATE_IN_DATA, /**< IN DATA */
+ USB_EP0_STATE_IN_STATUS, /**< IN status*/
+ USB_EP0_STATE_OUT_DATA, /**< OUT DATA */
+ USB_EP0_STATE_OUT_STATUS, /**< OUT status */
+ USB_EP0_STATE_IN_DATA_C, /**< IN status*/
+ USB_EP0_STATE_IN_STATUS_C, /**< IN DATA */
+} ep0_state_t;
+
+struct usb_musb_chan {
+ usb_osal_sem_t waitsem; /* Channel wait semaphore */
+ volatile bool waiter; /* True: Thread is waiting for a channel event */
+ volatile int result; /* The result of the transfer */
+ bool inuse; /* True: This channel is "in use" */
+ uint16_t mps;
+ uint8_t interval;
+ uint8_t hubaddr;
+ uint8_t hubport;
+ uint8_t *buffer;
+ uint32_t buflen;
+ bool in; /* True: IN endpoint */
+ volatile uint16_t xfrd; /* Bytes transferred (at end of transfer) */
+#ifdef CONFIG_USBHOST_ASYNCH
+ usbh_asynch_callback_t callback; /* Transfer complete callback */
+ void *arg; /* Argument that accompanies the callback */
+#endif
+};
+
+struct usb_musb_priv {
+ volatile bool connected; /* Connected to device */
+ volatile bool pscwait; /* True: Thread is waiting for a port event */
+ usb_osal_sem_t exclsem; /* Support mutually exclusive access */
+ volatile uint32_t fifo_size_offset;
+ struct usb_musb_chan chan[CONFIG_USBHOST_CHANNELS];
+} g_usbhost;
+
+volatile uint8_t usb_ep0_state = USB_EP0_STATE_SETUP;
+
+/* get current active ep */
+static uint8_t USBC_GetActiveEp(void)
+{
+ return USB->EPIDX;
+}
+
+/* set the active ep */
+static void USBC_SelectActiveEp(uint8_t ep_index)
+{
+ USB->EPIDX = ep_index;
+}
+
+static void usb_musb_fifo_flush(uint8_t ep)
+{
+ uint8_t ep_idx = ep & 0x7f;
+ if (ep_idx == 0) {
+ if ((HWREGB(USB_TXCSRLx_BASE) & (USB_CSRL0_RXRDY | USB_CSRL0_TXRDY)) != 0)
+ HWREGB(USB_RXCSRLx_BASE) |= USB_CSRH0_FLUSH;
+ } else {
+ if (ep & 0x80) {
+ if (HWREGB(USB_TXCSRLx_BASE) & USB_TXCSRL1_TXRDY)
+ HWREGB(USB_TXCSRLx_BASE) |= USB_TXCSRL1_FLUSH;
+ } else {
+ if (HWREGB(USB_RXCSRLx_BASE) & USB_RXCSRL1_RXRDY)
+ HWREGB(USB_RXCSRLx_BASE) |= USB_RXCSRL1_FLUSH;
+ }
+ }
+}
+
+void usb_musb_ep_status_clear(uint32_t ep_idx, uint32_t flags)
+{
+ if (ep_idx == USB_EP_0) {
+ HWREGB(USB_TXCSRLx_BASE) &= ~flags;
+ } else {
+ HWREGB(USB_TXCSRLx_BASE) &= ~flags;
+ HWREGB(USB_RXCSRLx_BASE) &= ~flags;
+ }
+}
+
+static void usb_musb_write_packet(uint8_t ep_idx, uint8_t *buffer, uint16_t len)
+{
+ uint32_t *buf32;
+ uint8_t *buf8;
+ uint32_t count32;
+ uint32_t count8;
+ int i;
+
+ if ((uint32_t)buffer & 0x03) {
+ buf8 = buffer;
+ for (i = 0; i < len; i++) {
+ HWREGB(USB_FIFO_BASE(ep_idx)) = *buf8++;
+ }
+ } else {
+ count32 = len >> 2;
+ count8 = len & 0x03;
+
+ buf32 = (uint32_t *)buffer;
+
+ while (count32--) {
+ HWREG(USB_FIFO_BASE(ep_idx)) = *buf32++;
+ }
+
+ buf8 = (uint8_t *)buf32;
+
+ while (count8--) {
+ HWREGB(USB_FIFO_BASE(ep_idx)) = *buf8++;
+ }
+ }
+}
+
+static void usb_musb_read_packet(uint8_t ep_idx, uint8_t *buffer, uint16_t len)
+{
+ uint32_t *buf32;
+ uint8_t *buf8;
+ uint32_t count32;
+ uint32_t count8;
+ int i;
+
+ if ((uint32_t)buffer & 0x03) {
+ buf8 = buffer;
+ for (i = 0; i < len; i++) {
+ *buf8++ = HWREGB(USB_FIFO_BASE(ep_idx));
+ }
+ } else {
+ count32 = len >> 2;
+ count8 = len & 0x03;
+
+ buf32 = (uint32_t *)buffer;
+
+ while (count32--) {
+ *buf32++ = HWREG(USB_FIFO_BASE(ep_idx));
+ }
+
+ buf8 = (uint8_t *)buf32;
+
+ while (count8--) {
+ *buf8++ = HWREGB(USB_FIFO_BASE(ep_idx));
+ }
+ }
+}
+
+static uint32_t usb_musb_get_fifo_size(uint16_t mps, uint16_t *used)
+{
+ uint32_t size;
+
+ for (uint8_t i = USB_TXFIFOSZ_SIZE_8; i <= USB_TXFIFOSZ_SIZE_2048; i++) {
+ size = (8 << i);
+ if (mps <= size) {
+ *used = size;
+ return i;
+ }
+ }
+
+ *used = 0;
+ return USB_TXFIFOSZ_SIZE_8;
+}
+
+/****************************************************************************
+ * Name: usb_synopsys_chan_alloc
+ *
+ * Description:
+ * Allocate a channel.
+ *
+ ****************************************************************************/
+
+static int usb_musb_chan_alloc(struct usb_musb_priv *priv)
+{
+ int chidx;
+
+ /* Search the table of channels */
+
+ for (chidx = 2; chidx < CONFIG_USBHOST_CHANNELS; chidx++) {
+ /* Is this channel available? */
+ if (!priv->chan[chidx].inuse) {
+ /* Yes... make it "in use" and return the index */
+
+ priv->chan[chidx].inuse = true;
+ return chidx;
+ }
+ }
+
+ /* All of the channels are "in-use" */
+
+ return -EBUSY;
+}
+
+/****************************************************************************
+ * Name: usb_synopsys_chan_free
+ *
+ * Description:
+ * Free a previoiusly allocated channel.
+ *
+ ****************************************************************************/
+
+static void usb_musb_chan_free(struct usb_musb_priv *priv, int chidx)
+{
+ /* Mark the channel available */
+ priv->chan[chidx].inuse = false;
+}
+
+/****************************************************************************
+ * Name: usb_synopsys_chan_freeall
+ *
+ * Description:
+ * Free all channels.
+ *
+ ****************************************************************************/
+
+static inline void usb_musb_chan_freeall(struct usb_musb_priv *priv)
+{
+ uint8_t chidx;
+
+ /* Free all host channels */
+
+ for (chidx = 2; chidx < CONFIG_USBHOST_CHANNELS; chidx++) {
+ usb_musb_chan_free(priv, chidx);
+ }
+}
+
+/****************************************************************************
+ * Name: usb_musb_chan_waitsetup
+ *
+ * Description:
+ * Set the request for the transfer complete event well BEFORE enabling
+ * the transfer (as soon as we are absolutely committed to the transfer).
+ * We do this to minimize race conditions. This logic would have to be
+ * expanded if we want to have more than one packet in flight at a time!
+ *
+ * Assumptions:
+ * Called from a normal thread context BEFORE the transfer has been
+ * started.
+ *
+ ****************************************************************************/
+
+static int usb_musb_chan_waitsetup(struct usb_musb_priv *priv,
+ struct usb_musb_chan *chan)
+{
+ uint32_t flags;
+ int ret = -ENODEV;
+
+ flags = usb_osal_enter_critical_section();
+
+ /* Is the device still connected? */
+
+ if (priv->connected) {
+ /* Yes.. then set waiter to indicate that we expect to be informed
+ * when either (1) the device is disconnected, or (2) the transfer
+ * completed.
+ */
+
+ chan->waiter = true;
+ chan->result = -EBUSY;
+ chan->xfrd = 0;
+#ifdef CONFIG_USBHOST_ASYNCH
+ chan->callback = NULL;
+ chan->arg = NULL;
+#endif
+ ret = 0;
+ }
+ usb_osal_leave_critical_section(flags);
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usb_musb_chan_asynchsetup
+ *
+ * Description:
+ * Set the request for the transfer complete event well BEFORE enabling
+ * the transfer (as soon as we are absolutely committed to the to avoid
+ * transfer). We do this to minimize race conditions. This logic would
+ * have to be expanded if we want to have more than one packet in flight
+ * at a time!
+ *
+ * Assumptions:
+ * Might be called from the level of an interrupt handler
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_USBHOST_ASYNCH
+static int usb_musb_chan_asynchsetup(struct usb_musb_priv *priv,
+ struct usb_musb_chan *chan,
+ usbh_asynch_callback_t callback, void *arg)
+{
+ uint32_t flags;
+ int ret = -ENODEV;
+
+ flags = usb_osal_enter_critical_section();
+ /* Is the device still connected? */
+
+ if (priv->connected) {
+ /* Yes.. then set waiter to indicate that we expect to be informed
+ * when either (1) the device is disconnected, or (2) the transfer
+ * completed.
+ */
+
+ chan->waiter = false;
+ chan->result = -EBUSY;
+ chan->xfrd = 0;
+ chan->callback = callback;
+ chan->arg = arg;
+ ret = 0;
+ }
+
+ usb_osal_leave_critical_section(flags);
+ return ret;
+}
+#endif
+
+/****************************************************************************
+ * Name: usb_musb_chan_wait
+ *
+ * Description:
+ * Wait for a transfer on a channel to complete.
+ *
+ * Assumptions:
+ * Called from a normal thread context
+ *
+ ****************************************************************************/
+
+static int usb_musb_chan_wait(struct usb_musb_priv *priv, struct usb_musb_chan *chan)
+{
+ int ret;
+
+ /* Loop, testing for an end of transfer condition. The channel 'result'
+ * was set to EBUSY and 'waiter' was set to true before the transfer;
+ * 'waiter' will be set to false and 'result' will be set appropriately
+ * when the transfer is completed.
+ */
+
+ if (chan->waiter) {
+ ret = usb_osal_sem_take(chan->waitsem);
+ if (ret < 0) {
+ return ret;
+ }
+ }
+
+ /* The transfer is complete re-enable interrupts and return the result */
+ ret = chan->result;
+ return ret;
+}
+
+/****************************************************************************
+ * Name: usb_musb_chan_wakeup
+ *
+ * Description:
+ * A channel transfer has completed... wakeup any threads waiting for the
+ * transfer to complete.
+ *
+ * Assumptions:
+ * This function is called from the transfer complete interrupt handler for
+ * the channel. Interrupts are disabled.
+ *
+ ****************************************************************************/
+
+static void usb_musb_chan_wakeup(struct usb_musb_priv *priv, struct usb_musb_chan *chan)
+{
+ usbh_asynch_callback_t callback;
+ void *arg;
+ int nbytes;
+
+ /* Is the transfer complete? */
+
+ if (chan->result != -EBUSY) {
+ /* Is there a thread waiting for this transfer to complete? */
+
+ if (chan->waiter) {
+ /* Wake'em up! */
+ chan->waiter = false;
+ usb_osal_sem_give(chan->waitsem);
+ }
+#ifdef CONFIG_USBHOST_ASYNCH
+ /* No.. is an asynchronous callback expected when the transfer
+ * completes?
+ */
+ else if (chan->callback) {
+ /* Handle continuation of IN/OUT pipes */
+ if (chan->in) {
+ callback = chan->callback;
+ arg = chan->arg;
+ nbytes = chan->xfrd;
+ chan->callback = NULL;
+ chan->arg = NULL;
+ if (chan->result < 0) {
+ nbytes = chan->result;
+ }
+
+ callback(arg, nbytes);
+
+ } else {
+ callback = chan->callback;
+ arg = chan->arg;
+ nbytes = chan->xfrd;
+ chan->callback = NULL;
+ chan->arg = NULL;
+ if (chan->result < 0) {
+ nbytes = chan->result;
+ }
+
+ callback(arg, nbytes);
+ }
+ }
+#endif
+ }
+}
+
+__WEAK void usb_hc_low_level_init(void)
+{
+}
+
+int usb_hc_init(void)
+{
+ g_usbhost.connected = 0;
+ g_usbhost.pscwait = 0;
+ g_usbhost.fifo_size_offset = 0;
+
+ usb_hc_low_level_init();
+
+ g_usbhost.exclsem = usb_osal_mutex_create();
+
+ USB->IE = USB_IE_RESET | USB_IE_CONN | USB_IE_DISCON |
+ USB_IE_RESUME | USB_IE_SUSPND |
+ USB_IE_BABBLE | USB_IE_SESREQ | USB_IE_VBUSERR;
+
+ USB->TXIE = USB_TXIE_EP0;
+ USB->RXIE = 0;
+
+ USB->DEVCTL |= USB_DEVCTL_SESSION;
+ return 0;
+}
+
+int usbh_reset_port(const uint8_t port)
+{
+ USB->POWER |= USB_POWER_RESET;
+ usb_osal_msleep(20);
+ USB->POWER &= ~(USB_POWER_RESET);
+ usb_osal_msleep(20);
+ return 0;
+}
+
+uint8_t usbh_get_port_speed(const uint8_t port)
+{
+ uint8_t speed;
+
+ if (USB->POWER & USB_POWER_HSMODE)
+ speed = USB_SPEED_HIGH;
+ else if (USB->DEVCTL & USB_DEVCTL_FSDEV)
+ speed = USB_SPEED_FULL;
+ else if (USB->DEVCTL & USB_DEVCTL_LSDEV)
+ speed = USB_SPEED_LOW;
+
+ return speed;
+}
+
+int usbh_ep0_reconfigure(usbh_epinfo_t ep, uint8_t dev_addr, uint8_t ep_mps, uint8_t speed)
+{
+ int ret;
+ uint8_t ep0 = (uint8_t)ep;
+ struct usb_musb_chan *chan;
+
+ ret = usb_osal_mutex_take(g_usbhost.exclsem);
+ if (ret < 0) {
+ return ret;
+ }
+
+ USBC_SelectActiveEp(ep0);
+ HWREGB(USB_TXADDR_BASE(ep0)) = dev_addr;
+
+ if (speed == USB_SPEED_HIGH) {
+ USB->TYPE0 = USB_TYPE0_SPEED_HIGH;
+ } else if (speed == USB_SPEED_FULL) {
+ USB->TYPE0 = USB_TYPE0_SPEED_FULL;
+ } else if (speed == USB_SPEED_LOW) {
+ USB->TYPE0 = USB_TYPE0_SPEED_LOW;
+ }
+
+ chan = &g_usbhost.chan[ep0];
+ chan->mps = ep_mps;
+
+ usb_osal_mutex_give(g_usbhost.exclsem);
+ return ret;
+}
+
+int usbh_ep_alloc(usbh_epinfo_t *ep, const struct usbh_endpoint_cfg *ep_cfg)
+{
+ struct usbh_hubport *hport;
+ struct usb_musb_chan *chan;
+ uint32_t chidx;
+ uint16_t used = 0;
+ uint8_t fifo_size;
+
+ hport = ep_cfg->hport;
+
+ uint32_t old_index = USBC_GetActiveEp();
+
+ if (ep_cfg->ep_type == USB_ENDPOINT_TYPE_CONTROL) {
+ if (hport->parent == NULL) {
+ chan = &g_usbhost.chan[0];
+ memset(chan, 0, sizeof(struct usb_musb_chan));
+
+ chan->waitsem = usb_osal_sem_create(0);
+
+ USBC_SelectActiveEp(0);
+
+ HWREGB(USB_TXINTERVALx_BASE) = 0;
+ HWREGB(USB_TXHUBADDR_BASE(0)) = 0;
+ HWREGB(USB_TXHUBPORT_BASE(0)) = 0;
+
+ *ep = (usbh_epinfo_t)0;
+ } else {
+ }
+ } else {
+ chidx = usb_musb_chan_alloc(&g_usbhost);
+
+ chan = &g_usbhost.chan[chidx];
+ memset(chan, 0, sizeof(struct usb_musb_chan));
+ chan->inuse = true;
+ chan->interval = ep_cfg->ep_interval;
+ chan->mps = ep_cfg->ep_mps;
+
+ chan->waitsem = usb_osal_sem_create(0);
+
+ USBC_SelectActiveEp(chidx);
+
+ uint32_t temp = ep_cfg->ep_addr & 0x7f;
+
+ if (hport->speed == USB_SPEED_HIGH) {
+ temp |= USB_TXTYPE1_SPEED_HIGH;
+ } else if (hport->speed == USB_SPEED_FULL) {
+ temp |= USB_TXTYPE1_SPEED_FULL;
+ } else if (hport->speed == USB_SPEED_LOW) {
+ temp |= USB_TXTYPE1_SPEED_LOW;
+ }
+
+ switch (ep_cfg->ep_type) {
+ case 0x00:
+ temp |= USB_TXTYPE1_PROTO_CTRL;
+ break;
+ case 0x01:
+ temp |= USB_TXTYPE1_PROTO_ISOC;
+ break;
+ case 0x02:
+ temp |= USB_TXTYPE1_PROTO_BULK;
+ break;
+ case 0x03:
+ temp |= USB_TXTYPE1_PROTO_INT;
+ break;
+ }
+
+ if (ep_cfg->ep_addr & 0x80) {
+ chan->in = 1;
+
+ HWREGB(USB_RXADDR_BASE(chidx)) = hport->dev_addr;
+ HWREGB(USB_RXHUBADDR_BASE(chidx)) = 0;
+ HWREGB(USB_RXHUBPORT_BASE(chidx)) = 0;
+
+ HWREGB(USB_RXTYPEx_BASE) = temp;
+ HWREGH(USB_RXMAPx_BASE) = ep_cfg->ep_mps;
+ HWREGB(USB_RXINTERVALx_BASE) = ep_cfg->ep_interval;
+
+ HWREGB(USB_RXCSRHx_BASE) = 0;
+ HWREGB(USB_RXCSRLx_BASE) |= USB_RXCSRL1_CLRDT;
+
+ usb_musb_ep_status_clear(chidx, USB_HOST_IN_STATUS);
+
+ fifo_size = usb_musb_get_fifo_size(ep_cfg->ep_mps, &used);
+
+ USB->RXFIFOADD = g_usbhost.fifo_size_offset >> 3;
+ USB->RXFIFOSZ = fifo_size & 0x0f;
+
+ g_usbhost.fifo_size_offset += used;
+
+ USB->RXIE |= (1 << chidx);
+ } else {
+ chan->in = 0;
+ HWREGB(USB_TXADDR_BASE(chidx)) = hport->dev_addr;
+ HWREGB(USB_TXHUBADDR_BASE(chidx)) = 0;
+ HWREGB(USB_TXHUBPORT_BASE(chidx)) = 0;
+
+ HWREGB(USB_TXTYPEx_BASE) = temp;
+ HWREGH(USB_TXMAPx_BASE) = ep_cfg->ep_mps;
+ HWREGB(USB_TXINTERVALx_BASE) = ep_cfg->ep_interval;
+
+ HWREGB(USB_TXCSRHx_BASE) = 0;
+ usb_musb_ep_status_clear(chidx, USB_HOST_OUT_STATUS);
+ HWREGB(USB_TXCSRLx_BASE) |= USB_TXCSRL1_CLRDT;
+
+ fifo_size = usb_musb_get_fifo_size(ep_cfg->ep_mps, &used);
+
+ USB->TXFIFOADD = g_usbhost.fifo_size_offset >> 3;
+ USB->TXFIFOSZ = fifo_size & 0x0f;
+
+ g_usbhost.fifo_size_offset += used;
+
+ USB->TXIE |= (1 << chidx);
+ }
+
+ *ep = (usbh_epinfo_t)chidx;
+ }
+
+ USBC_SelectActiveEp(old_index);
+ return 0;
+}
+
+int usbh_ep_free(usbh_epinfo_t ep)
+{
+ usb_musb_chan_free(&g_usbhost, (int)ep);
+ usb_osal_sem_delete(g_usbhost.chan[(int)ep].waitsem);
+ return 0;
+}
+
+int usbh_control_transfer(usbh_epinfo_t ep, struct usb_setup_packet *setup, uint8_t *buffer)
+{
+ int ret;
+ struct usb_musb_chan *chan;
+ uint8_t ep0 = (uint8_t)ep;
+
+ ret = usb_osal_mutex_take(g_usbhost.exclsem);
+ if (ret < 0) {
+ return ret;
+ }
+
+ chan = &g_usbhost.chan[0];
+
+ usb_musb_chan_waitsetup(&g_usbhost, chan);
+
+ usb_musb_write_packet(0, (uint8_t *)setup, 8);
+
+ if (setup->wLength && buffer) {
+ if (setup->bmRequestType & 0x80) {
+ usb_ep0_state = USB_EP0_STATE_IN_DATA;
+ } else {
+ usb_ep0_state = USB_EP0_STATE_OUT_DATA;
+ }
+ chan->buffer = buffer;
+ chan->buflen = setup->wLength;
+ } else {
+ usb_ep0_state = USB_EP0_STATE_IN_STATUS;
+ }
+
+ USBC_SelectActiveEp(0);
+ USB->CSRL0 = USB_CSRL0_TXRDY | USB_CSRL0_SETUP;
+
+ ret = usb_musb_chan_wait(&g_usbhost, chan);
+
+ if (ret < 0) {
+ goto errout_with_mutex;
+ }
+
+ usb_osal_mutex_give(g_usbhost.exclsem);
+ return 0;
+errout_with_mutex:
+ usb_osal_mutex_give(g_usbhost.exclsem);
+ return ret;
+}
+
+int usbh_ep_bulk_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen)
+{
+ int ret;
+ struct usb_musb_chan *chan;
+ uint8_t chidx = (uint8_t)ep;
+
+ ret = usb_osal_mutex_take(g_usbhost.exclsem);
+ if (ret < 0) {
+ return ret;
+ }
+
+ chan = &g_usbhost.chan[chidx];
+ usb_musb_chan_waitsetup(&g_usbhost, chan);
+
+ if (chan->in) {
+ chan->buffer = buffer;
+ chan->buflen = buflen;
+ USBC_SelectActiveEp(chidx);
+ HWREGB(USB_RXCSRLx_BASE) = USB_RXCSRL1_REQPKT;
+ } else {
+ chan->buffer = buffer;
+ chan->buflen = buflen;
+ if (buflen > chan->mps) {
+ buflen = chan->mps;
+ }
+
+ usb_musb_write_packet(chidx, (uint8_t *)buffer, buflen);
+ USBC_SelectActiveEp(chidx);
+ HWREGB(USB_TXCSRLx_BASE) = USB_TXCSRL1_TXRDY;
+ chan->buffer += buflen;
+ chan->buflen -= buflen;
+ }
+ ret = usb_musb_chan_wait(&g_usbhost, chan);
+ if (ret < 0) {
+ goto errout_with_mutex;
+ }
+ usb_osal_mutex_give(g_usbhost.exclsem);
+
+ return buflen;
+errout_with_mutex:
+ usb_osal_mutex_give(g_usbhost.exclsem);
+ return ret;
+}
+
+int usbh_ep_intr_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen)
+{
+ int ret;
+
+ ret = usb_osal_mutex_take(g_usbhost.exclsem);
+ if (ret < 0) {
+ return ret;
+ }
+
+ usb_osal_mutex_give(g_usbhost.exclsem);
+ return ret;
+}
+
+int usbh_ep_bulk_async_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen, usbh_asynch_callback_t callback, void *arg)
+{
+ int ret;
+
+ ret = usb_osal_mutex_take(g_usbhost.exclsem);
+ if (ret < 0) {
+ return ret;
+ }
+
+ usb_osal_mutex_give(g_usbhost.exclsem);
+
+ return ret;
+}
+
+int usbh_ep_intr_async_transfer(usbh_epinfo_t ep, uint8_t *buffer, uint32_t buflen, usbh_asynch_callback_t callback, void *arg)
+{
+ int ret;
+
+ ret = usb_osal_mutex_take(g_usbhost.exclsem);
+ if (ret < 0) {
+ return ret;
+ }
+
+ usb_osal_mutex_give(g_usbhost.exclsem);
+
+ return ret;
+}
+
+int usb_ep_cancel(usbh_epinfo_t ep)
+{
+ return 0;
+}
+
+void handle_ep0(void)
+{
+ uint8_t ep0_status = USB->CSRL0;
+
+ if (ep0_status & USB_HOST_EP0_ERROR) {
+ usb_musb_ep_status_clear(0, USB_HOST_EP0_ERROR);
+ usb_musb_fifo_flush(0);
+ usb_ep0_state = USB_EP0_STATE_SETUP;
+ g_usbhost.chan[0].result = -EIO;
+ goto chan_wait;
+ }
+
+ if (ep0_status & USB_HOST_EP0_RX_STALL) {
+ usb_musb_ep_status_clear(0, ep0_status & USB_HOST_IN_STATUS);
+ usb_ep0_state = USB_EP0_STATE_SETUP;
+ g_usbhost.chan[0].result = -EPERM;
+ goto chan_wait;
+ }
+ switch (usb_ep0_state) {
+ case USB_EP0_STATE_SETUP:
+ break;
+ case USB_EP0_STATE_IN_DATA:
+ USB->CSRL0 = USB_RXCSRL1_REQPKT;
+ usb_ep0_state = USB_EP0_STATE_IN_DATA_C;
+
+ break;
+ case USB_EP0_STATE_IN_DATA_C:
+ if (USB->CSRL0 & USB_CSRL0_RXRDY) {
+ uint32_t size = g_usbhost.chan[0].buflen;
+ if (size > g_usbhost.chan[0].mps) {
+ size = g_usbhost.chan[0].mps;
+ }
+
+ size = MIN(size, USB->COUNT0);
+
+ usb_musb_read_packet(0, g_usbhost.chan[0].buffer, size);
+ USB->CSRL0 &= ~USB_CSRL0_RXRDY;
+
+ g_usbhost.chan[0].buffer += size;
+ g_usbhost.chan[0].buflen -= size;
+
+ if ((size < g_usbhost.chan[0].mps) || (g_usbhost.chan[0].buflen == 0)) {
+ usb_ep0_state = USB_EP0_STATE_IN_STATUS_C;
+ USB->CSRL0 = USB_CSRL0_TXRDY | USB_CSRL0_STATUS;
+ } else {
+ USB->CSRL0 = USB_RXCSRL1_REQPKT;
+ }
+ }
+ break;
+ case USB_EP0_STATE_IN_STATUS_C:
+ if (ep0_status & (USB_HOST_EP0_RXPKTRDY | USB_HOST_EP0_STATUS)) {
+ usb_musb_ep_status_clear(0, (USB_HOST_EP0_RXPKTRDY | USB_HOST_EP0_STATUS));
+ }
+
+ usb_ep0_state = USB_EP0_STATE_SETUP;
+ g_usbhost.chan[0].result = 0;
+ goto chan_wait;
+
+ break;
+ case USB_EP0_STATE_IN_STATUS:
+ USB->CSRL0 = USB_CSRL0_REQPKT | USB_CSRL0_STATUS;
+ usb_ep0_state = USB_EP0_STATE_IN_STATUS_C;
+ break;
+
+ case USB_EP0_STATE_OUT_DATA: {
+ uint32_t size = g_usbhost.chan[0].buflen;
+
+ if (size > g_usbhost.chan[0].mps) {
+ size = g_usbhost.chan[0].mps;
+ }
+
+ usb_musb_write_packet(0, g_usbhost.chan[0].buffer, size);
+
+ g_usbhost.chan[0].buffer += size;
+ g_usbhost.chan[0].buflen -= size;
+
+ if (size == g_usbhost.chan[0].mps) {
+ USB->CSRL0 = USB_CSRL0_TXRDY;
+ } else {
+ USB->CSRL0 = USB_CSRL0_TXRDY;
+ usb_ep0_state = USB_EP0_STATE_IN_STATUS;
+ }
+ }
+
+ break;
+ }
+ return;
+chan_wait:
+ usb_musb_chan_wakeup(&g_usbhost, &g_usbhost.chan[0]);
+}
+void USBH_IRQHandler(void)
+{
+ uint32_t is;
+ uint32_t txis;
+ uint32_t rxis;
+ uint32_t ep_status;
+ struct usb_musb_chan *chan;
+ uint8_t chidx;
+ uint8_t old_ep_idx;
+
+ is = USB->IS;
+ txis = USB->TXIS;
+ rxis = USB->RXIS;
+
+ old_ep_idx = USBC_GetActiveEp();
+ if (is & USB_IS_CONN) {
+ if (!g_usbhost.connected) {
+ g_usbhost.connected = true;
+ extern void usbh_event_notify_handler(uint8_t event, uint8_t rhport);
+ usbh_event_notify_handler(USBH_EVENT_ATTACHED, 1);
+ }
+ }
+
+ if (is & USB_IS_DISCON) {
+ if (g_usbhost.connected) {
+ g_usbhost.connected = false;
+
+ for (uint8_t chnum = 0; chnum < CONFIG_USBHOST_CHANNELS; chnum++) {
+ if (g_usbhost.chan[chnum].waiter) {
+ /* Wake'em up! */
+ g_usbhost.chan[chnum].waiter = false;
+ usb_osal_sem_give(g_usbhost.chan[chnum].waitsem);
+ }
+ }
+
+ extern void usbh_event_notify_handler(uint8_t event, uint8_t rhport);
+ usbh_event_notify_handler(USBH_EVENT_REMOVED, 1);
+ }
+ }
+
+ if (is & USB_IS_SOF) {
+ }
+
+ txis &= USB->TXIE;
+ /* Handle EP0 interrupt */
+ if (txis & USB_TXIE_EP0) {
+ txis &= ~USB_TXIE_EP0;
+ USB->TXIS = USB_TXIE_EP0; // clear isr flag
+ USBC_SelectActiveEp(0);
+ handle_ep0();
+ }
+
+ while (txis) {
+ chidx = __builtin_ctz(txis);
+ txis &= ~(1 << chidx);
+ USB->TXIS = (1 << chidx); // clear isr flag
+
+ chan = &g_usbhost.chan[chidx];
+
+ USBC_SelectActiveEp(chidx);
+
+ ep_status = HWREGB(USB_TXCSRLx_BASE);
+ usb_musb_ep_status_clear(chidx, ep_status | USB_HOST_OUT_STATUS);
+
+ if (ep_status & USB_HOST_OUT_STALL) {
+ chan->result = -EPERM;
+ usb_musb_ep_status_clear(chidx, USB_HOST_OUT_STALL);
+ usb_musb_chan_wakeup(&g_usbhost, chan);
+ } else if (ep_status & USB_HOST_OUT_ERROR) {
+ chan->result = -EIO;
+ usb_musb_ep_status_clear(chidx, USB_HOST_OUT_ERROR);
+ usb_musb_chan_wakeup(&g_usbhost, chan);
+ } else {
+ uint32_t size = chan->buflen;
+
+ if (size) {
+ if (size > chan->mps) {
+ size = chan->mps;
+ }
+ usb_musb_write_packet(chidx, chan->buffer, size);
+ HWREGB(USB_TXCSRLx_BASE) = USB_TXCSRL1_TXRDY;
+ chan->buffer += size;
+ chan->buflen -= size;
+
+ } else {
+ chan->result = 0;
+ usb_musb_chan_wakeup(&g_usbhost, chan);
+ }
+ }
+ }
+
+ rxis &= USB->RXIE;
+ while (rxis) {
+ chidx = __builtin_ctz(rxis);
+ rxis &= ~(1 << chidx);
+ USB->RXIS = (1 << chidx); // clear isr flag
+ chan = &g_usbhost.chan[chidx];
+
+ USBC_SelectActiveEp(chidx);
+
+ ep_status = HWREGB(USB_RXCSRLx_BASE);
+
+ if (ep_status & USB_HOST_IN_STALL) {
+ usb_musb_ep_status_clear(chidx, USB_HOST_IN_STALL);
+ usb_musb_chan_wakeup(&g_usbhost, chan);
+ } else if (ep_status & USB_HOST_IN_ERROR) {
+ usb_musb_ep_status_clear(chidx, USB_HOST_IN_ERROR);
+ usb_musb_chan_wakeup(&g_usbhost, chan);
+ } else if (ep_status & USB_RXCSRL1_RXRDY) {
+ uint32_t size = chan->buflen;
+ if (size > chan->mps) {
+ size = chan->mps;
+ }
+ size = MIN(size, HWREG(USB_RXCOUNTx_BASE));
+
+ usb_musb_read_packet(chidx, chan->buffer, size);
+ HWREGB(USB_RXCSRLx_BASE) &= ~USB_RXCSRL1_RXRDY;
+ chan->buffer += size;
+ chan->buflen -= size;
+
+ if (chan->buflen == 0) {
+ chan->result = 0;
+ usb_musb_chan_wakeup(&g_usbhost, chan);
+ } else {
+ HWREGB(USB_RXCSRLx_BASE) = USB_RXCSRL1_REQPKT;
+ }
+ }
+ }
+ USBC_SelectActiveEp(old_ep_idx);
+} \ No newline at end of file