From 8b2dc155a0d39fd8c679dfdfe3201b216d7ff5c0 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Mon, 30 Jul 2018 16:56:19 -0600 Subject: [PATCH] drivers/usbhost/usbhost_max3421e.c: Fix several errors found in early testing. --- drivers/usbhost/usbhost_max3421e.c | 119 ++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 29 deletions(-) diff --git a/drivers/usbhost/usbhost_max3421e.c b/drivers/usbhost/usbhost_max3421e.c index 190d9b4770..0b7257aa7d 100644 --- a/drivers/usbhost/usbhost_max3421e.c +++ b/drivers/usbhost/usbhost_max3421e.c @@ -530,6 +530,10 @@ static inline void max3421e_int_enable(FAR struct max3421e_usbhost_s *priv, static inline void max3421e_int_disable(FAR struct max3421e_usbhost_s *priv, uint8_t irqset); static inline uint8_t max3421e_int_status(FAR struct max3421e_usbhost_s *priv); +static inline void max3421e_int_clear(FAR struct max3421e_usbhost_s *priv, + uint8_t irqset); +static void max3421e_int_wait(FAR struct max3421e_usbhost_s *priv, + uint8_t irqset, unsigned int usec); /* USB host controller operations **********************************************/ @@ -1506,7 +1510,7 @@ static void max3421e_put_sndfifo(FAR struct max3421e_usbhost_s *priv, for (i = 0; i < 2 && committed < priv->buflen && - (max3421e_getreg(priv, MAX3421E_USBHOST_HIRQ) & USBHOST_HIRQ_SNDBAVIRQ) == 1; + (max3421e_int_status(priv) & USBHOST_HIRQ_SNDBAVIRQ) == 1; i++) { /* Get the size of the biggest thing that we can put in the current @@ -3048,8 +3052,7 @@ static void max3421e_irqwork(FAR void *arg) /* Clear the pending HXFRDN interrupt */ - max3421e_putreg(priv, MAX3421E_USBHOST_HIRQ, - USBHOST_HIRQ_HXFRDNIRQ); + max3421e_int_clear(priv, USBHOST_HIRQ_HXFRDNIRQ); /* Check transfer status and terminate the transfer if any error * occurred. @@ -3072,8 +3075,7 @@ static void max3421e_irqwork(FAR void *arg) { /* Clear the pending CONNIRQ interrupt */ - max3421e_putreg(priv, MAX3421E_USBHOST_HIRQ, - USBHOST_HIRQ_CONNIRQ); + max3421e_int_clear(priv, USBHOST_HIRQ_CONNIRQ); /* Check if a peripheral device has been connected */ @@ -3097,8 +3099,7 @@ static void max3421e_irqwork(FAR void *arg) /* Clear the pending SNDBAV interrupt */ - max3421e_putreg(priv, MAX3421E_USBHOST_HIRQ, - USBHOST_HIRQ_SNDBAVIRQ); + max3421e_int_clear(priv, USBHOST_HIRQ_SNDBAVIRQ); /* Finish long transfer, possibly re-enabling the SNDBAV * interrupt (see max3421e_send_start) @@ -3122,8 +3123,7 @@ static void max3421e_irqwork(FAR void *arg) * will catch that the next time through this loop. */ - max3421e_putreg(priv, MAX3421E_USBHOST_HIRQ, - USBHOST_HIRQ_RCVDAVIRQ); + max3421e_int_clear(priv, USBHOST_HIRQ_RCVDAVIRQ); /* Handle the receipt of data */ @@ -3170,7 +3170,8 @@ static int max3421e_interrupt(int irq, FAR void *context, FAR void *arg) } /**************************************************************************** - * Name: max3421e_int_enable, max3421e_int_disable, and max3421e_int_status + * Name: max3421e_int_enable, max3421e_int_disable, max3421e_int_status, and + * max3421e_int_wait * * Description: * Respectively enable, disable, or get status of the USB host interrupt @@ -3185,6 +3186,8 @@ static int max3421e_interrupt(int irq, FAR void *context, FAR void *arg) * ****************************************************************************/ +/* Enable a set of interrupts */ + static inline void max3421e_int_enable(FAR struct max3421e_usbhost_s *priv, uint8_t irqset) { @@ -3192,6 +3195,8 @@ static inline void max3421e_int_enable(FAR struct max3421e_usbhost_s *priv, max3421e_putreg(priv, MAX3421E_USBHOST_HIEN, priv->irqset); } +/* Disable a set of interrupts */ + static inline void max3421e_int_disable(FAR struct max3421e_usbhost_s *priv, uint8_t irqset) { @@ -3199,11 +3204,41 @@ static inline void max3421e_int_disable(FAR struct max3421e_usbhost_s *priv, max3421e_putreg(priv, MAX3421E_USBHOST_HIEN, priv->irqset); } +/* Get the set of pending interrupts */ + static inline uint8_t max3421e_int_status(FAR struct max3421e_usbhost_s *priv) { return max3421e_getreg(priv, MAX3421E_USBHOST_HIRQ) & priv->irqset; } +/* Clear a set of pending interrupts */ + +static inline void max3421e_int_clear(FAR struct max3421e_usbhost_s *priv, + uint8_t irqset) +{ + max3421e_putreg(priv, MAX3421E_USBHOST_HIRQ, irqset); +} + +/* Wait until any interrupt from a set of interrupts occurs */ + +static void max3421e_int_wait(FAR struct max3421e_usbhost_s *priv, + uint8_t irqset, unsigned int usec) +{ + uint8_t regval; + + do + { + regval = max3421e_getreg(priv, MAX3421E_USBHOST_HIRQ); + regval &= irqset; + + if (regval == 0 && usec > 0) + { + usleep(usec); + } + } + while (regval == 0); +} + /**************************************************************************** * Name: max3421e_wait * @@ -3235,7 +3270,6 @@ static int max3421e_wait(FAR struct usbhost_connection_s *conn, FAR struct max3421e_connection_s *maxconn; FAR struct max3421e_usbhost_s *priv; struct usbhost_hubport_s *connport; - irqstate_t flags; maxconn = (FAR struct max3421e_connection_s *)conn; DEBUGASSERT(maxconn != NULL && maxconn->priv != NULL); @@ -3243,9 +3277,12 @@ static int max3421e_wait(FAR struct usbhost_connection_s *conn, /* Loop until a change in connection state is detected */ - flags = enter_critical_section(); for (; ; ) { + /* We must have exclusive access to the USB host hardware and state structures */ + + max3421e_takesem(&priv->exclsem); + /* Is there a change in the connection state of the single root hub * port? */ @@ -3262,9 +3299,10 @@ static int max3421e_wait(FAR struct usbhost_connection_s *conn, /* And return the root hub port */ *hport = connport; - leave_critical_section(flags); usbhost_vtrace1(MAX3421E_VTRACE1_CONNECTED2, connport->connected); + + max3421e_givesem(&priv->exclsem); return OK; } @@ -3279,10 +3317,11 @@ static int max3421e_wait(FAR struct usbhost_connection_s *conn, priv->hport = NULL; *hport = connport; - leave_critical_section(flags); usbhost_vtrace1(MAX3421E_VTRACE1_HUB_CONNECTED, connport->connected); + + max3421e_givesem(&priv->exclsem); return OK; } #endif @@ -3290,6 +3329,7 @@ static int max3421e_wait(FAR struct usbhost_connection_s *conn, /* Wait for the next connection event */ priv->pscwait = true; + max3421e_givesem(&priv->exclsem); max3421e_takesem(&priv->pscsem); } } @@ -3312,7 +3352,7 @@ static int max3421e_wait(FAR struct usbhost_connection_s *conn, * returned indicating the nature of the failure * * Assumptions: - * This function will *not* be called from an interrupt handler. + * The caller has the SPI bus locked. * ****************************************************************************/ @@ -3328,7 +3368,7 @@ static int max3421e_getspeed(FAR struct max3421e_usbhost_s *priv, * method first to be assured that a device is connected. */ - while (!priv->connected) + if (!priv->connected) { /* No, return an error */ @@ -3344,6 +3384,17 @@ static int max3421e_getspeed(FAR struct max3421e_usbhost_s *priv, nxsig_usleep(100*1000); + /* Make sure we are still connected */ + + if (!priv->connected) + { + /* No, return an error */ + + usbhost_trace1(MAX3421E_TRACE1_DEVDISCONN6, 0); + max3421e_givesem(&priv->exclsem); + return -ENODEV; + } + /* Stop SOF generation and reset the host port */ max3421e_busreset(priv); @@ -3399,10 +3450,17 @@ static int max3421e_enumerate(FAR struct usbhost_connection_s *conn, DEBUGASSERT(maxconn != NULL && maxconn->priv != NULL); priv = maxconn->priv; + /* We must have exclusive access to the USB host hardware and state structures */ + + max3421e_takesem(&priv->exclsem); + /* If this is a connection on the root hub, then we need to go to * little more effort to get the device speed. If it is a connection * on an external hub, then we already have that information. */ + + max3421e_lock(priv); + #warning REVISIT: Isnt this already done? #ifdef CONFIG_USBHOST_HUB @@ -3412,6 +3470,7 @@ static int max3421e_enumerate(FAR struct usbhost_connection_s *conn, ret = max3421e_getspeed(priv, conn, hport); if (ret < 0) { + max3421e_givesem(&priv->exclsem); return ret; } } @@ -3426,7 +3485,9 @@ static int max3421e_enumerate(FAR struct usbhost_connection_s *conn, usbhost_vtrace1(MAX3421E_VTRACE1_ENUMERATE, 0); priv->smstate = SMSTATE_ENUM; + ret = usbhost_enumerate(hport, &hport->devclass); + max3421e_unlock(priv); /* The enumeration may fail either because of some HCD interfaces failure * or because the device class is not supported. In either case, we just @@ -3444,9 +3505,13 @@ static int max3421e_enumerate(FAR struct usbhost_connection_s *conn, /* Set post-enumeration data toggles (assuming success) */ + max3421e_lock(priv); max3421e_modifyreg(priv, MAX3421E_USBHOST_HCTL, USBHOST_HCTL_TOGGLES_MASK, USBHOST_HCTL_RCVTOG0| USBHOST_HCTL_SNDTOG0); + max3421e_unlock(priv); + + max3421e_givesem(&priv->exclsem); return ret; } @@ -4339,13 +4404,14 @@ static void max3421e_busreset(FAR struct max3421e_usbhost_s *priv) max3421e_modifyreg(priv, MAX3421E_USBHOST_MODE, USBHOST_MODE_SOFKAENAB, 0); + /* Clear any pending bus event */ + + max3421e_int_clear(priv, USBHOST_HIRQ_BUSEVENTIRQ); + /* Perform the reset */ max3421e_modifyreg(priv, MAX3421E_USBHOST_HCTL, 0, USBHOST_HCTL_BUSRST); - while ((max3421e_getreg(priv, MAX3421E_USBHOST_HCTL) & MAX3421E_USBHOST_HCTL) == 0) - { - usleep(250); - } + max3421e_int_wait(priv, USBHOST_HIRQ_BUSEVENTIRQ, 250); } /**************************************************************************** @@ -4450,11 +4516,7 @@ static int max3421e_startsof(FAR struct max3421e_usbhost_s *priv) /* Wait for the first SOF received and 20ms has passed */ - while ((max3421e_getreg(priv, MAX3421E_USBHOST_HIRQ) & - USBHOST_HIRQ_FRAMEIRQ) == 0) - { - } - + max3421e_int_wait(priv, USBHOST_HIRQ_FRAMEIRQ, 0); usleep(20*1000); return OK; } @@ -4638,8 +4700,7 @@ static inline int max3421e_hw_initialize(FAR struct max3421e_usbhost_s *priv) max3421e_modifyreg(priv, MAX3421E_USBHOST_CPUCTL, USBHOST_CPUCTL_IE, 0); max3421e_putreg(priv, MAX3421E_USBHOST_HIEN, 0); - max3421e_putreg(priv, MAX3421E_USBHOST_HIRQ, 0xff); - + max3421e_int_clear(priv, 0xff); priv->irqset = 0; /* Configure as full-speed USB host */ @@ -4649,10 +4710,10 @@ static inline int max3421e_hw_initialize(FAR struct max3421e_usbhost_s *priv) USBHOST_MODE_HOST | USBHOST_MODE_DMPULLD | USBHOST_MODE_DPPULLDN); - /* Enable and clear the connection detected (CONDIRQ) interrupt */ + /* Clear and enable the connection detected (CONDIRQ) interrupt */ + max3421e_int_clear(priv, USBHOST_HIRQ_CONNIRQ); max3421e_int_enable(priv, USBHOST_HIRQ_CONNIRQ); - max3421e_putreg(priv, MAX3421E_USBHOST_HIRQ, USBHOST_HIRQ_CONNIRQ); /* Enable MAX3412E interrupts */