SAMA5D HSMCI: Fix a problem on card insertion/removal callback handling. Interrupts were being disable so that the callbacks occurred with interrupts disabled. This resulted in loss of some interrupts and some not-so-good behaviors. The solution is to perform all callbacks on the work thread unconditionally (2014-7-29).
This commit is contained in:
parent
9c80dfb31c
commit
4df0fbec04
@ -1571,7 +1571,6 @@ static int sam_hsmci_interrupt(struct sam_dev_s *priv)
|
||||
|
||||
priv->buffer = NULL;
|
||||
priv->remaining = 0;
|
||||
|
||||
sam_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
|
||||
}
|
||||
}
|
||||
@ -3081,6 +3080,8 @@ static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
|
||||
static void sam_callback(void *arg)
|
||||
{
|
||||
struct sam_dev_s *priv = (struct sam_dev_s*)arg;
|
||||
irqstate_t flags;
|
||||
int ret;
|
||||
|
||||
/* Is a callback registered? */
|
||||
|
||||
@ -3088,6 +3089,7 @@ static void sam_callback(void *arg)
|
||||
fvdbg("Callback %p(%p) cbevents: %02x cdstatus: %02x\n",
|
||||
priv->callback, priv->cbarg, priv->cbevents, priv->cdstatus);
|
||||
|
||||
flags = irqsave();
|
||||
if (priv->callback)
|
||||
{
|
||||
/* Yes.. Check for enabled callback events */
|
||||
@ -3121,26 +3123,39 @@ static void sam_callback(void *arg)
|
||||
|
||||
priv->cbevents = 0;
|
||||
|
||||
/* Callbacks cannot be performed in the context of an interrupt handler.
|
||||
* If we are in an interrupt handler, then queue the callback to be
|
||||
* performed later on the work thread.
|
||||
/* This function is called either from (1) the context of the calling
|
||||
* thread or from the the context of (2) card detection logic. The
|
||||
* caller may or may not have interrupts disabled (we have them
|
||||
& disabled here!).
|
||||
*
|
||||
* So to minimize the possibility of recursive behavior and to assure
|
||||
* that callback is always performed outside of the interrupt handling
|
||||
* context and with interrupts enabled, the callback is always performed
|
||||
* on the lower priority work thread.
|
||||
*/
|
||||
|
||||
if (up_interrupt_context())
|
||||
/* First cancel any existing work */
|
||||
|
||||
ret = work_cancel(LPWORK, &priv->cbwork);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* Yes.. queue it */
|
||||
/* NOTE: Currently, work_cancel only returns success */
|
||||
|
||||
fdbg("ERROR: Failed to cancel work: %d\n", ret);
|
||||
}
|
||||
|
||||
fllvdbg("Queuing callback to %p(%p)\n", priv->callback, priv->cbarg);
|
||||
(void)work_queue(LPWORK, &priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0);
|
||||
}
|
||||
else
|
||||
ret = work_queue(LPWORK, &priv->cbwork, (worker_t)priv->callback,
|
||||
priv->cbarg, 0);
|
||||
if (ret < 0)
|
||||
{
|
||||
/* No.. then just call the callback here */
|
||||
/* NOTE: Currently, work_queue only returns success */
|
||||
|
||||
fvdbg("Callback to %p(%p)\n", priv->callback, priv->cbarg);
|
||||
priv->callback(priv->cbarg);
|
||||
fdbg("ERROR: Failed to schedule work: %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
irqrestore(flags);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
@ -3342,6 +3357,9 @@ FAR struct sdio_dev_s *sdio_initialize(int slotno)
|
||||
* Returned Values:
|
||||
* None
|
||||
*
|
||||
* Assumptions:
|
||||
* May be called from an interrupt handler.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot)
|
||||
@ -3350,7 +3368,10 @@ void sdio_mediachange(FAR struct sdio_dev_s *dev, bool cardinslot)
|
||||
uint8_t cdstatus;
|
||||
irqstate_t flags;
|
||||
|
||||
/* Update card status */
|
||||
/* Update card status. Interrupts are disabled here because if we are
|
||||
* not called from an interrupt handler, then the following steps must
|
||||
* still be atomic.
|
||||
*/
|
||||
|
||||
flags = irqsave();
|
||||
cdstatus = priv->cdstatus;
|
||||
|
Loading…
x
Reference in New Issue
Block a user