SAMA5 OHCI: Re-organize some endpoint list data structures.. Strange things happen when semaphores lie in DMA memory which is occasionally invalidated
This commit is contained in:
parent
10daf06976
commit
1fb80e0917
@ -171,6 +171,21 @@
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Private Types
|
* Private Types
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
/* This structure contains one endpoint list. The main reason for the existence
|
||||||
|
* of this structure is to contain the sem_t value associated with the ED. It
|
||||||
|
* doesn't work well within the ED itself because then the semaphore counter
|
||||||
|
* is subject to DMA cache operations (invalidate a modified semaphore count
|
||||||
|
* is fatal!).
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct sam_eplist_s
|
||||||
|
{
|
||||||
|
volatile bool wdhwait; /* TRUE: Thread is waiting for WDH interrupt */
|
||||||
|
sem_t wdhsem; /* Semaphore used to wait for Writeback Done Head event */
|
||||||
|
struct sam_ed_s *ed; /* Endpoint descriptor (ED) */
|
||||||
|
struct sam_gtd_s *tail; /* Tail transfer descriptor (TD) */
|
||||||
|
};
|
||||||
|
|
||||||
/* This structure retins the state of one root hub port */
|
/* This structure retins the state of one root hub port */
|
||||||
|
|
||||||
struct sam_rhport_s
|
struct sam_rhport_s
|
||||||
@ -184,13 +199,12 @@ struct sam_rhport_s
|
|||||||
|
|
||||||
/* Root hub port status */
|
/* Root hub port status */
|
||||||
|
|
||||||
volatile bool connected; /* Connected to device */
|
volatile bool connected; /* Connected to device */
|
||||||
volatile bool lowspeed; /* Low speed device attached. */
|
volatile bool lowspeed; /* Low speed device attached. */
|
||||||
uint8_t rhpndx; /* Root hub port index */
|
uint8_t rhpndx; /* Root hub port index */
|
||||||
bool ep0init; /* True: EP0 initialized */
|
bool ep0init; /* True: EP0 initialized */
|
||||||
|
|
||||||
struct sam_ed_s *edctrl; /* EP0 control ED */
|
struct sam_eplist_s ep0; /* EP0 endpoint list */
|
||||||
struct sam_gtd_s *tdtail; /* EP0 tail TD */
|
|
||||||
|
|
||||||
/* The bound device class driver */
|
/* The bound device class driver */
|
||||||
|
|
||||||
@ -203,14 +217,14 @@ struct sam_ohci_s
|
|||||||
{
|
{
|
||||||
/* Driver status */
|
/* Driver status */
|
||||||
|
|
||||||
volatile bool rhswait; /* TRUE: Thread is waiting for Root Hub Status change */
|
volatile bool rhswait; /* TRUE: Thread is waiting for Root Hub Status change */
|
||||||
|
|
||||||
#ifndef CONFIG_USBHOST_INT_DISABLE
|
#ifndef CONFIG_USBHOST_INT_DISABLE
|
||||||
uint8_t ininterval; /* Minimum periodic IN EP polling interval: 2, 4, 6, 16, or 32 */
|
uint8_t ininterval; /* Minimum periodic IN EP polling interval: 2, 4, 6, 16, or 32 */
|
||||||
uint8_t outinterval; /* Minimum periodic IN EP polling interval: 2, 4, 6, 16, or 32 */
|
uint8_t outinterval; /* Minimum periodic IN EP polling interval: 2, 4, 6, 16, or 32 */
|
||||||
#endif
|
#endif
|
||||||
sem_t exclsem; /* Support mutually exclusive access */
|
sem_t exclsem; /* Support mutually exclusive access */
|
||||||
sem_t rhssem; /* Semaphore to wait Writeback Done Head event */
|
sem_t rhssem; /* Semaphore to wait Writeback Done Head event */
|
||||||
|
|
||||||
/* Root hub ports */
|
/* Root hub ports */
|
||||||
|
|
||||||
@ -219,29 +233,25 @@ struct sam_ohci_s
|
|||||||
|
|
||||||
/* The OCHI expects the size of an endpoint descriptor to be 16 bytes.
|
/* The OCHI expects the size of an endpoint descriptor to be 16 bytes.
|
||||||
* However, the size allocated for an endpoint descriptor is 32 bytes. This
|
* However, the size allocated for an endpoint descriptor is 32 bytes. This
|
||||||
* extra 16-bytes is used by the OHCI host driver in order to maintain
|
* is necessary first because the Cortex-A5 cache line size is 32 bytes and
|
||||||
|
* tht is the smallest amount of memory that we can perform cache operations
|
||||||
|
* on. The 16-bytes is also used by the OHCI host driver in order to maintain
|
||||||
* additional endpoint-specific data.
|
* additional endpoint-specific data.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define SAMD_ED_PADSIZE (12 - sizeof(sem_t))
|
|
||||||
|
|
||||||
struct sam_ed_s
|
struct sam_ed_s
|
||||||
{
|
{
|
||||||
/* Hardware specific fields */
|
/* Hardware specific fields */
|
||||||
|
|
||||||
struct ohci_ed_s hw; /* 0-15 */
|
struct ohci_ed_s hw; /* 0-15 */
|
||||||
|
|
||||||
/* Software specific fields */
|
/* Software specific fields */
|
||||||
|
|
||||||
uint8_t xfrtype; /* 16: Transfer type. See SB_EP_ATTR_XFER_* in usb.h */
|
struct sam_eplist_s *eplist; /* 16-19: List structure associated with the ED */
|
||||||
uint8_t interval; /* 17: Periodic EP polling interval: 2, 4, 6, 16, or 32 */
|
uint8_t xfrtype; /* 20: Transfer type. See SB_EP_ATTR_XFER_* in usb.h */
|
||||||
volatile uint8_t tdstatus; /* 18: TD control status bits from last Writeback Done Head event */
|
uint8_t interval; /* 21: Periodic EP polling interval: 2, 4, 6, 16, or 32 */
|
||||||
volatile bool wdhwait; /* 19: TRUE: Thread is waiting for WDH interrupt */
|
volatile uint8_t tdstatus; /* 22: TD control status bits from last Writeback Done Head event */
|
||||||
sem_t wdhsem; /* 20-23: Semaphore used to wait for Writeback Done Head event */
|
uint8_t pad[9]; /* 23-31: Pad to 32-bytes */
|
||||||
|
|
||||||
/* Padding to 32-bytes follow. The amount of padding depends on the size of sem_t */
|
|
||||||
|
|
||||||
uint8_t pad[SAMD_ED_PADSIZE];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SIZEOF_SAM_ED_S 32
|
#define SIZEOF_SAM_ED_S 32
|
||||||
@ -256,12 +266,12 @@ struct sam_gtd_s
|
|||||||
{
|
{
|
||||||
/* Hardware specific fields */
|
/* Hardware specific fields */
|
||||||
|
|
||||||
struct ohci_gtd_s hw; /* 0-15 */
|
struct ohci_gtd_s hw; /* 0-15 */
|
||||||
|
|
||||||
/* Software specific fields */
|
/* Software specific fields */
|
||||||
|
|
||||||
struct sam_ed_s *ed; /* 16-19: Pointer to parent ED */
|
struct sam_ed_s *ed; /* 16-19: Pointer to parent ED */
|
||||||
uint8_t pad[12]; /* 20-31: Pad to 32 bytes */
|
uint8_t pad[12]; /* 20-31: Pad to 32 bytes */
|
||||||
};
|
};
|
||||||
|
|
||||||
#define SIZEOF_SAM_TD_S 32
|
#define SIZEOF_SAM_TD_S 32
|
||||||
@ -270,8 +280,8 @@ struct sam_gtd_s
|
|||||||
|
|
||||||
struct sam_list_s
|
struct sam_list_s
|
||||||
{
|
{
|
||||||
struct sam_list_s *flink; /* Link to next buffer in the list */
|
struct sam_list_s *flink; /* Link to next buffer in the list */
|
||||||
/* Variable length buffer data follows */
|
/* Variable length buffer data follows */
|
||||||
};
|
};
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -687,16 +697,10 @@ static struct sam_gtd_s *sam_tdalloc(void)
|
|||||||
static void sam_tdfree(struct sam_gtd_s *td)
|
static void sam_tdfree(struct sam_gtd_s *td)
|
||||||
{
|
{
|
||||||
struct sam_list_s *tdfree = (struct sam_list_s *)td;
|
struct sam_list_s *tdfree = (struct sam_list_s *)td;
|
||||||
|
DEBUGASSERT(td);
|
||||||
|
|
||||||
/* This should not happen but just to be safe, don't free the common, pre-
|
tdfree->flink = g_tdfree;
|
||||||
* allocated tail TD.
|
g_tdfree = tdfree;
|
||||||
*/
|
|
||||||
|
|
||||||
if (tdfree != NULL)
|
|
||||||
{
|
|
||||||
tdfree->flink = g_tdfree;
|
|
||||||
g_tdfree = tdfree;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -1212,15 +1216,15 @@ static int sam_enqueuetd(struct sam_rhport_s *rhport, struct sam_ed_s *ed,
|
|||||||
td = sam_tdalloc();
|
td = sam_tdalloc();
|
||||||
if (td != NULL)
|
if (td != NULL)
|
||||||
{
|
{
|
||||||
/* Skip processing of this ED */
|
/* Skip processing of this ED while we modify the TD list. */
|
||||||
|
|
||||||
ed->hw.ctrl |= ED_CONTROL_K;
|
ed->hw.ctrl |= ED_CONTROL_K;
|
||||||
cp15_coherent_dcache((uintptr_t)ed,
|
cp15_coherent_dcache((uintptr_t)ed,
|
||||||
(uintptr_t)ed + sizeof(struct sam_ed_s));
|
(uintptr_t)ed + sizeof(struct ohci_ed_s) - 1);
|
||||||
|
|
||||||
/* Get the tail ED for this root hub port */
|
/* Get the tail ED for this root hub port */
|
||||||
|
|
||||||
tdtail = rhport->tdtail;
|
tdtail = rhport->ep0.tail;
|
||||||
|
|
||||||
/* Get physical addresses to support the DMA */
|
/* Get physical addresses to support the DMA */
|
||||||
|
|
||||||
@ -1249,20 +1253,26 @@ static int sam_enqueuetd(struct sam_rhport_s *rhport, struct sam_ed_s *ed,
|
|||||||
ed->hw.headp = (uint32_t)phytd | ((ed->hw.headp) & ED_HEADP_C);
|
ed->hw.headp = (uint32_t)phytd | ((ed->hw.headp) & ED_HEADP_C);
|
||||||
ed->hw.tailp = (uint32_t)phytail;
|
ed->hw.tailp = (uint32_t)phytail;
|
||||||
|
|
||||||
/* Flush the buffer, the new TD, and the modified ED to RAM */
|
/* Flush the buffer (if there is one), the new TD, and the modified ED
|
||||||
|
* to RAM.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (buffer && buflen > 0)
|
||||||
|
{
|
||||||
|
cp15_coherent_dcache((uintptr_t)buffer,
|
||||||
|
(uintptr_t)buffer + buflen - 1);
|
||||||
|
}
|
||||||
|
|
||||||
cp15_coherent_dcache((uintptr_t)buffer,
|
|
||||||
(uintptr_t)buffer + buflen);
|
|
||||||
cp15_coherent_dcache((uintptr_t)tdtail,
|
cp15_coherent_dcache((uintptr_t)tdtail,
|
||||||
(uintptr_t)tdtail + sizeof(struct sam_gtd_s));
|
(uintptr_t)tdtail + sizeof(struct ohci_gtd_s) - 1);
|
||||||
cp15_coherent_dcache((uintptr_t)td,
|
cp15_coherent_dcache((uintptr_t)td,
|
||||||
(uintptr_t)td + sizeof(struct sam_gtd_s));
|
(uintptr_t)td + sizeof(struct ohci_gtd_s) - 1);
|
||||||
|
|
||||||
/* Resume processing of this ED */
|
/* Resume processing of this ED */
|
||||||
|
|
||||||
ed->hw.ctrl &= ~ED_CONTROL_K;
|
ed->hw.ctrl &= ~ED_CONTROL_K;
|
||||||
cp15_coherent_dcache((uintptr_t)ed,
|
cp15_coherent_dcache((uintptr_t)ed,
|
||||||
(uintptr_t)ed + sizeof(struct sam_ed_s));
|
(uintptr_t)ed + sizeof(struct ohci_ed_s) - 1);
|
||||||
ret = OK;
|
ret = OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1292,8 +1302,8 @@ static int sam_ep0enqueue(struct sam_rhport_s *rhport)
|
|||||||
uintptr_t physaddr;
|
uintptr_t physaddr;
|
||||||
uint32_t regval;
|
uint32_t regval;
|
||||||
|
|
||||||
DEBUGASSERT(rhport && !rhport->ep0init && rhport->edctrl == NULL &&
|
DEBUGASSERT(rhport && !rhport->ep0init && rhport->ep0.ed == NULL &&
|
||||||
rhport->tdtail == NULL);
|
rhport->ep0.tail == NULL);
|
||||||
|
|
||||||
/* Allocate a control ED and a tail TD */
|
/* Allocate a control ED and a tail TD */
|
||||||
|
|
||||||
@ -1308,13 +1318,13 @@ static int sam_ep0enqueue(struct sam_rhport_s *rhport)
|
|||||||
tdtail = sam_tdalloc();
|
tdtail = sam_tdalloc();
|
||||||
if (!tdtail)
|
if (!tdtail)
|
||||||
{
|
{
|
||||||
sam_edfree(rhport->edctrl);
|
sam_edfree(rhport->ep0.ed);
|
||||||
irqrestore(flags);
|
irqrestore(flags);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
rhport->edctrl = edctrl;
|
rhport->ep0.ed = edctrl;
|
||||||
rhport->tdtail = tdtail;
|
rhport->ep0.tail = tdtail;
|
||||||
|
|
||||||
/* ControlListEnable. This bit is cleared to disable the processing of the
|
/* ControlListEnable. This bit is cleared to disable the processing of the
|
||||||
* Control list. We should never modify the control list while CLE is set.
|
* Control list. We should never modify the control list while CLE is set.
|
||||||
@ -1329,17 +1339,15 @@ static int sam_ep0enqueue(struct sam_rhport_s *rhport)
|
|||||||
memset(tdtail, 0, sizeof(struct sam_gtd_s));
|
memset(tdtail, 0, sizeof(struct sam_gtd_s));
|
||||||
tdtail->ed = edctrl;
|
tdtail->ed = edctrl;
|
||||||
|
|
||||||
/* Initialize the control endpoint for this port */
|
/* Initialize the control endpoint for this port.
|
||||||
|
* Set up some default values (like max packetsize = 8).
|
||||||
memset(edctrl, 0, sizeof(struct sam_ed_s));
|
|
||||||
sem_init(&edctrl->wdhsem, 0, 0);
|
|
||||||
|
|
||||||
/* Set up some default values (like max packetsize = 8).
|
|
||||||
* NOTE that the SKIP bit is set until the first readl TD is added.
|
* NOTE that the SKIP bit is set until the first readl TD is added.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
memset(edctrl, 0, sizeof(struct sam_ed_s));
|
||||||
(void)sam_ep0configure(&rhport->drvr, 0, 8);
|
(void)sam_ep0configure(&rhport->drvr, 0, 8);
|
||||||
edctrl->hw.ctrl |= ED_CONTROL_K;
|
edctrl->hw.ctrl |= ED_CONTROL_K;
|
||||||
|
edctrl->eplist = &rhport->ep0;
|
||||||
|
|
||||||
/* Link the common tail TD to the ED's TD list */
|
/* Link the common tail TD to the ED's TD list */
|
||||||
|
|
||||||
@ -1362,9 +1370,9 @@ static int sam_ep0enqueue(struct sam_rhport_s *rhport)
|
|||||||
/* Flush the affected control ED and tail TD to RAM */
|
/* Flush the affected control ED and tail TD to RAM */
|
||||||
|
|
||||||
cp15_coherent_dcache((uintptr_t)edctrl,
|
cp15_coherent_dcache((uintptr_t)edctrl,
|
||||||
(uintptr_t)edctrl + sizeof(struct sam_ed_s));
|
(uintptr_t)edctrl + sizeof(struct ohci_ed_s) - 1);
|
||||||
cp15_coherent_dcache((uintptr_t)tdtail,
|
cp15_coherent_dcache((uintptr_t)tdtail,
|
||||||
(uintptr_t)tdtail + sizeof(struct sam_gtd_s));
|
(uintptr_t)tdtail + sizeof(struct ohci_gtd_s) - 1);
|
||||||
|
|
||||||
/* ControlListEnable. This bit is set to (re-)enable the processing of the
|
/* ControlListEnable. This bit is set to (re-)enable the processing of the
|
||||||
* Control list. Note: once enabled, it remains enabled and we may even
|
* Control list. Note: once enabled, it remains enabled and we may even
|
||||||
@ -1404,8 +1412,8 @@ static void sam_ep0dequeue(struct sam_rhport_s *rhport)
|
|||||||
uintptr_t physcurr;
|
uintptr_t physcurr;
|
||||||
uint32_t regval;
|
uint32_t regval;
|
||||||
|
|
||||||
DEBUGASSERT(rhport && rhport->ep0init && rhport->edctrl != NULL &&
|
DEBUGASSERT(rhport && rhport->ep0init && rhport->ep0.ed != NULL &&
|
||||||
rhport->tdtail != NULL);
|
rhport->ep0.tail != NULL);
|
||||||
|
|
||||||
/* ControlListEnable. This bit is cleared to disable the processing of the
|
/* ControlListEnable. This bit is cleared to disable the processing of the
|
||||||
* Control list. We should never modify the control list while CLE is set.
|
* Control list. We should never modify the control list while CLE is set.
|
||||||
@ -1420,7 +1428,7 @@ static void sam_ep0dequeue(struct sam_rhport_s *rhport)
|
|||||||
* precedessor).
|
* precedessor).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
edctrl = rhport->edctrl;
|
edctrl = rhport->ep0.ed;
|
||||||
physcurr = sam_getreg(SAM_USBHOST_CTRLHEADED);
|
physcurr = sam_getreg(SAM_USBHOST_CTRLHEADED);
|
||||||
|
|
||||||
for (curred = (struct sam_ed_s *)sam_virtramaddr(physcurr),
|
for (curred = (struct sam_ed_s *)sam_virtramaddr(physcurr),
|
||||||
@ -1445,7 +1453,7 @@ static void sam_ep0dequeue(struct sam_rhport_s *rhport)
|
|||||||
/* Flush the modified ED to RAM */
|
/* Flush the modified ED to RAM */
|
||||||
|
|
||||||
cp15_coherent_dcache((uintptr_t)preved,
|
cp15_coherent_dcache((uintptr_t)preved,
|
||||||
(uintptr_t)preved + sizeof(struct sam_ed_s));
|
(uintptr_t)preved + sizeof(struct ohci_ed_s) - 1);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1466,9 +1474,9 @@ static void sam_ep0dequeue(struct sam_rhport_s *rhport)
|
|||||||
}
|
}
|
||||||
irqrestore(flags);
|
irqrestore(flags);
|
||||||
|
|
||||||
/* Release any TDs that may still be attached to the ED */
|
/* Release any TDs that may still be attached to the ED. */
|
||||||
|
|
||||||
tdtail = rhport->tdtail;
|
tdtail = rhport->ep0.tail;
|
||||||
physcurr = edctrl->hw.headp;
|
physcurr = edctrl->hw.headp;
|
||||||
|
|
||||||
for (currtd = (struct sam_gtd_s *)sam_virtramaddr(physcurr);
|
for (currtd = (struct sam_gtd_s *)sam_virtramaddr(physcurr);
|
||||||
@ -1479,12 +1487,10 @@ static void sam_ep0dequeue(struct sam_rhport_s *rhport)
|
|||||||
sam_tdfree(currtd);
|
sam_tdfree(currtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset the control Ed and tail the common tail TD for this port.
|
/* Free the tail TD and the control ED allocated for this port */
|
||||||
* This is totally unnecessary but helps with debug.
|
|
||||||
*/
|
|
||||||
|
|
||||||
memset(tdtail, 0, sizeof(struct sam_gtd_s));
|
sam_tdfree(tdtail);
|
||||||
memset(edctrl, 0, sizeof(struct sam_ed_s));
|
sam_edfree(edctrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
@ -1500,19 +1506,26 @@ static void sam_ep0dequeue(struct sam_rhport_s *rhport)
|
|||||||
|
|
||||||
static int sam_wdhwait(struct sam_rhport_s *rhport, struct sam_ed_s *ed)
|
static int sam_wdhwait(struct sam_rhport_s *rhport, struct sam_ed_s *ed)
|
||||||
{
|
{
|
||||||
|
struct sam_eplist_s *eplist;
|
||||||
irqstate_t flags = irqsave();
|
irqstate_t flags = irqsave();
|
||||||
int ret = -ENODEV;
|
int ret = -ENODEV;
|
||||||
|
|
||||||
/* Is the device still connected? */
|
/* Is the device still connected? */
|
||||||
|
|
||||||
if (rhport->connected)
|
if (rhport->connected)
|
||||||
{
|
{
|
||||||
/* Yes.. then set wdhwait to indicate that we expect to be informed when
|
/* Yes.. Get the endpoint list associated with the ED */
|
||||||
* either (1) the device is disconnected, or (2) the transfer completed.
|
|
||||||
|
eplist = ed->eplist;
|
||||||
|
DEBUGASSERT(eplist);
|
||||||
|
|
||||||
|
/* Then set wdhwait to indicate that we expect to be informed when
|
||||||
|
* either (1) the device is disconnected, or (2) the transfer
|
||||||
|
* completed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
ed->wdhwait = true;
|
eplist->wdhwait = true;
|
||||||
ret = OK;
|
ret = OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
irqrestore(flags);
|
irqrestore(flags);
|
||||||
@ -1536,6 +1549,7 @@ static int sam_wdhwait(struct sam_rhport_s *rhport, struct sam_ed_s *ed)
|
|||||||
static int sam_ctrltd(struct sam_rhport_s *rhport, uint32_t dirpid,
|
static int sam_ctrltd(struct sam_rhport_s *rhport, uint32_t dirpid,
|
||||||
uint8_t *buffer, size_t buflen)
|
uint8_t *buffer, size_t buflen)
|
||||||
{
|
{
|
||||||
|
struct sam_eplist_s *eplist;
|
||||||
struct sam_ed_s *edctrl;
|
struct sam_ed_s *edctrl;
|
||||||
uint32_t toggle;
|
uint32_t toggle;
|
||||||
uint32_t regval;
|
uint32_t regval;
|
||||||
@ -1545,14 +1559,18 @@ static int sam_ctrltd(struct sam_rhport_s *rhport, uint32_t dirpid,
|
|||||||
* transfer.
|
* transfer.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
edctrl = rhport->edctrl;
|
edctrl = rhport->ep0.ed;
|
||||||
ret = sam_wdhwait(rhport, edctrl);
|
ret = sam_wdhwait(rhport, edctrl);
|
||||||
if (ret != OK)
|
if (ret != OK)
|
||||||
{
|
{
|
||||||
udbg("ERROR: Device disconnected\n");
|
udbg("ERROR: Device disconnected\n");
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Get the endpoint list structure for the ED */
|
||||||
|
|
||||||
|
eplist = &rhport->ep0;
|
||||||
|
|
||||||
/* Configure the toggle field in the TD */
|
/* Configure the toggle field in the TD */
|
||||||
|
|
||||||
if (dirpid == GTD_STATUS_DP_SETUP)
|
if (dirpid == GTD_STATUS_DP_SETUP)
|
||||||
@ -1578,16 +1596,14 @@ static int sam_ctrltd(struct sam_rhport_s *rhport, uint32_t dirpid,
|
|||||||
regval |= OHCI_CMDST_CLF;
|
regval |= OHCI_CMDST_CLF;
|
||||||
sam_putreg(regval, SAM_USBHOST_CMDST);
|
sam_putreg(regval, SAM_USBHOST_CMDST);
|
||||||
|
|
||||||
/* Wait for the Writeback Done Head interrupt */
|
/* Wait for the Writeback Done Head interrupt. Loop to handle any false
|
||||||
|
* alarm semaphore counts.
|
||||||
sam_takesem(&edctrl->wdhsem);
|
|
||||||
|
|
||||||
/* Invalidate the D cache to force the control ED to be reloaded from
|
|
||||||
* RAM.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
cp15_invalidate_dcache((uintptr_t)edctrl,
|
while (eplist->wdhwait)
|
||||||
(uintptr_t)edctrl + sizeof(struct sam_ed_s));
|
{
|
||||||
|
sam_takesem(&eplist->wdhsem);
|
||||||
|
}
|
||||||
|
|
||||||
/* Check the TD completion status bits */
|
/* Check the TD completion status bits */
|
||||||
|
|
||||||
@ -1604,7 +1620,7 @@ static int sam_ctrltd(struct sam_rhport_s *rhport, uint32_t dirpid,
|
|||||||
|
|
||||||
/* Make sure that there is no outstanding request on this endpoint */
|
/* Make sure that there is no outstanding request on this endpoint */
|
||||||
|
|
||||||
edctrl->wdhwait = false;
|
eplist->wdhwait = false;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1750,15 +1766,17 @@ static void sam_rhsc_interrupt(void)
|
|||||||
|
|
||||||
static void sam_wdh_interrupt(void)
|
static void sam_wdh_interrupt(void)
|
||||||
{
|
{
|
||||||
|
struct sam_eplist_s *eplist;
|
||||||
struct sam_gtd_s *td;
|
struct sam_gtd_s *td;
|
||||||
struct sam_gtd_s *next;
|
struct sam_gtd_s *next;
|
||||||
|
struct sam_ed_s *ed;
|
||||||
|
|
||||||
/* The host controller just wrote the list of finished TDs into the HCCA
|
/* The host controller just wrote the one finished TDs into the HCCA
|
||||||
* done head. This may include multiple packets that were transferred
|
* done head. This may include multiple packets that were transferred
|
||||||
* in the preceding frame.
|
* in the preceding frame.
|
||||||
*
|
*
|
||||||
* Remove the TD(s) from the Writeback Done Head in the HCCA and return
|
* Remove the TD from the Writeback Done Head in the HCCA and return
|
||||||
* them to the free list. Note that this is safe because the hardware
|
* it to the free list. Note that this is safe because the hardware
|
||||||
* will not modify the writeback done head again until the WDH bit is
|
* will not modify the writeback done head again until the WDH bit is
|
||||||
* cleared in the interrupt status register.
|
* cleared in the interrupt status register.
|
||||||
*/
|
*/
|
||||||
@ -1767,10 +1785,10 @@ static void sam_wdh_interrupt(void)
|
|||||||
|
|
||||||
# if 0 /* Apparently insufficient */
|
# if 0 /* Apparently insufficient */
|
||||||
cp15_invalidate_dcache((uintptr_t)&g_hcca.donehead,
|
cp15_invalidate_dcache((uintptr_t)&g_hcca.donehead,
|
||||||
(uintptr_t)&g_hcca.donehead + sizeof(uint32_t));
|
(uintptr_t)&g_hcca.donehead + sizeof(uint32_t) - 1);
|
||||||
#else
|
#else
|
||||||
cp15_invalidate_dcache((uintptr_t)&g_hcca,
|
cp15_invalidate_dcache((uintptr_t)&g_hcca,
|
||||||
(uintptr_t)&g_hcca + sizeof(struct ohci_hcca_s));
|
(uintptr_t)&g_hcca + sizeof(struct ohci_hcca_s) - 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Now read the done head */
|
/* Now read the done head */
|
||||||
@ -1783,15 +1801,29 @@ static void sam_wdh_interrupt(void)
|
|||||||
|
|
||||||
for (; td; td = next)
|
for (; td; td = next)
|
||||||
{
|
{
|
||||||
|
/* Invalidate the just-finished TD from D-cache to force it to be
|
||||||
|
* reloaded from memory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
cp15_invalidate_dcache((uintptr_t)td,
|
||||||
|
(uintptr_t)td + sizeof( struct ohci_gtd_s) - 1);
|
||||||
|
|
||||||
/* Get the ED in which this TD was enqueued */
|
/* Get the ED in which this TD was enqueued */
|
||||||
|
|
||||||
struct sam_ed_s *ed = td->ed;
|
ed = td->ed;
|
||||||
DEBUGASSERT(ed != NULL);
|
DEBUGASSERT(ed != NULL);
|
||||||
|
|
||||||
/* Invalidate the ED D-cache to force reload from memory */
|
/* Get the endpoint list that contains the ED */
|
||||||
|
|
||||||
|
eplist = ed->eplist;
|
||||||
|
DEBUGASSERT(eplist != NULL);
|
||||||
|
|
||||||
|
/* Also nvalidate the control ED so that it two will be re-read from
|
||||||
|
* memory.
|
||||||
|
*/
|
||||||
|
|
||||||
cp15_invalidate_dcache((uintptr_t)ed,
|
cp15_invalidate_dcache((uintptr_t)ed,
|
||||||
(uintptr_t)ed + sizeof( struct sam_ed_s));
|
(uintptr_t)ed + sizeof( struct ohci_ed_s) - 1);
|
||||||
|
|
||||||
/* Save the condition code from the (single) TD status/control
|
/* Save the condition code from the (single) TD status/control
|
||||||
* word.
|
* word.
|
||||||
@ -1816,10 +1848,10 @@ static void sam_wdh_interrupt(void)
|
|||||||
|
|
||||||
/* And wake up the thread waiting for the WDH event */
|
/* And wake up the thread waiting for the WDH event */
|
||||||
|
|
||||||
if (ed->wdhwait)
|
if (eplist->wdhwait)
|
||||||
{
|
{
|
||||||
sam_givesem(&ed->wdhsem);
|
sam_givesem(&eplist->wdhsem);
|
||||||
ed->wdhwait = false;
|
eplist->wdhwait = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2104,7 +2136,7 @@ static int sam_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr,
|
|||||||
funcaddr >= 0 && funcaddr <= SAM_USBHOST_NRHPORT &&
|
funcaddr >= 0 && funcaddr <= SAM_USBHOST_NRHPORT &&
|
||||||
maxpacketsize < 2048);
|
maxpacketsize < 2048);
|
||||||
|
|
||||||
edctrl = rhport->edctrl;
|
edctrl = rhport->ep0.ed;
|
||||||
|
|
||||||
/* We must have exclusive access to EP0 and the control list */
|
/* We must have exclusive access to EP0 and the control list */
|
||||||
|
|
||||||
@ -2127,7 +2159,7 @@ static int sam_ep0configure(FAR struct usbhost_driver_s *drvr, uint8_t funcaddr,
|
|||||||
/* Flush the modified control ED to RAM */
|
/* Flush the modified control ED to RAM */
|
||||||
|
|
||||||
cp15_coherent_dcache((uintptr_t)edctrl,
|
cp15_coherent_dcache((uintptr_t)edctrl,
|
||||||
(uintptr_t)edctrl + sizeof(struct sam_ed_s));
|
(uintptr_t)edctrl + sizeof(struct ohci_ed_s) - 1);
|
||||||
sam_givesem(&g_ohci.exclsem);
|
sam_givesem(&g_ohci.exclsem);
|
||||||
|
|
||||||
uvdbg("RHPort%d EP0 CTRL: %08x\n", rhport->rhpndx + 1, edctrl->hw.ctrl);
|
uvdbg("RHPort%d EP0 CTRL: %08x\n", rhport->rhpndx + 1, edctrl->hw.ctrl);
|
||||||
@ -2160,7 +2192,9 @@ static int sam_epalloc(FAR struct usbhost_driver_s *drvr,
|
|||||||
const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep)
|
const FAR struct usbhost_epdesc_s *epdesc, usbhost_ep_t *ep)
|
||||||
{
|
{
|
||||||
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
|
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
|
||||||
|
struct sam_eplist_s *eplist;
|
||||||
struct sam_ed_s *ed;
|
struct sam_ed_s *ed;
|
||||||
|
struct sam_gtd_s *td;
|
||||||
uintptr_t physaddr;
|
uintptr_t physaddr;
|
||||||
int ret = -ENOMEM;
|
int ret = -ENOMEM;
|
||||||
|
|
||||||
@ -2170,111 +2204,144 @@ static int sam_epalloc(FAR struct usbhost_driver_s *drvr,
|
|||||||
|
|
||||||
DEBUGASSERT(rhport && epdesc && ep && rhport->connected);
|
DEBUGASSERT(rhport && epdesc && ep && rhport->connected);
|
||||||
|
|
||||||
|
/* Allocate a container for the endpoint data */
|
||||||
|
|
||||||
|
eplist = (struct sam_eplist_s *)kzalloc(sizeof(struct sam_eplist_s));
|
||||||
|
if (!eplist)
|
||||||
|
{
|
||||||
|
udbg("ERROR: Failed to allocate EP list\n");
|
||||||
|
goto errout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialize the endpoint container */
|
||||||
|
|
||||||
|
sem_init(&eplist->wdhsem, 0, 0);
|
||||||
|
|
||||||
/* We must have exclusive access to the ED pool, the bulk list, the periodic list
|
/* We must have exclusive access to the ED pool, the bulk list, the periodic list
|
||||||
* and the interrupt table.
|
* and the interrupt table.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
sam_takesem(&g_ohci.exclsem);
|
sam_takesem(&g_ohci.exclsem);
|
||||||
|
|
||||||
/* Take the next ED from the beginning of the free list (if the list is
|
/* Allocate an ED and a tail TD for the new endpoint */
|
||||||
* non-empty.
|
|
||||||
*/
|
|
||||||
|
|
||||||
ed = sam_edalloc();
|
ed = sam_edalloc();
|
||||||
if (ed)
|
if (!ed)
|
||||||
{
|
{
|
||||||
/* Configure the endpoint descriptor. */
|
udbg("ERROR: Failed to allocate ED\n");
|
||||||
|
goto errout_with_semaphore;
|
||||||
memset((void*)ed, 0, sizeof(struct sam_ed_s));
|
|
||||||
ed->hw.ctrl = (uint32_t)(epdesc->funcaddr) << ED_CONTROL_FA_SHIFT |
|
|
||||||
(uint32_t)(epdesc->addr) << ED_CONTROL_EN_SHIFT |
|
|
||||||
(uint32_t)(epdesc->mxpacketsize) << ED_CONTROL_MPS_SHIFT;
|
|
||||||
|
|
||||||
/* Get the direction of the endpoint */
|
|
||||||
|
|
||||||
if (epdesc->in)
|
|
||||||
{
|
|
||||||
ed->hw.ctrl |= ED_CONTROL_D_IN;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ed->hw.ctrl |= ED_CONTROL_D_OUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for a low-speed device */
|
|
||||||
|
|
||||||
if (rhport->lowspeed)
|
|
||||||
{
|
|
||||||
ed->hw.ctrl |= ED_CONTROL_S;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set the transfer type */
|
|
||||||
|
|
||||||
ed->xfrtype = epdesc->xfrtype;
|
|
||||||
|
|
||||||
/* Special Case isochronous transfer types */
|
|
||||||
|
|
||||||
#if 0 /* Isochronous transfers not yet supported */
|
|
||||||
if (ed->xfrtype == USB_EP_ATTR_XFER_ISOC)
|
|
||||||
{
|
|
||||||
ed->hw.ctrl |= ED_CONTROL_F;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
uvdbg("EP%d CTRL: %08x\n", epdesc->addr, ed->hw.ctrl);
|
|
||||||
|
|
||||||
/* Initialize the semaphore that is used to wait for the endpoint
|
|
||||||
* WDH event.
|
|
||||||
*/
|
|
||||||
|
|
||||||
sem_init(&ed->wdhsem, 0, 0);
|
|
||||||
|
|
||||||
/* Link the tail TD to the ED's TD list */
|
|
||||||
|
|
||||||
physaddr = (uintptr_t)sam_physramaddr((uintptr_t)rhport->tdtail);
|
|
||||||
ed->hw.headp = physaddr;
|
|
||||||
ed->hw.tailp = physaddr;
|
|
||||||
|
|
||||||
/* Now add the endpoint descriptor to the appropriate list */
|
|
||||||
|
|
||||||
switch (ed->xfrtype)
|
|
||||||
{
|
|
||||||
case USB_EP_ATTR_XFER_BULK:
|
|
||||||
ret = sam_addbulked(ed);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USB_EP_ATTR_XFER_INT:
|
|
||||||
ret = sam_addinted(epdesc, ed);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USB_EP_ATTR_XFER_ISOC:
|
|
||||||
ret = sam_addisoced(epdesc, ed);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USB_EP_ATTR_XFER_CONTROL:
|
|
||||||
default:
|
|
||||||
ret = -EINVAL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Was the ED successfully added? */
|
|
||||||
|
|
||||||
if (ret != OK)
|
|
||||||
{
|
|
||||||
/* No.. destroy it and report the error */
|
|
||||||
|
|
||||||
udbg("ERROR: Failed to queue ED for transfer type: %d\n", ed->xfrtype);
|
|
||||||
sem_destroy(&ed->wdhsem);
|
|
||||||
sam_edfree(ed);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Yes.. return an opaque reference to the ED */
|
|
||||||
|
|
||||||
*ep = (usbhost_ep_t)ed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
td = sam_tdalloc();
|
||||||
|
if (!td)
|
||||||
|
{
|
||||||
|
udbg("ERROR: Failed to allocate TD\n");
|
||||||
|
goto errout_with_ed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Save the descriptors in the endpoint container */
|
||||||
|
|
||||||
|
eplist->ed = ed;
|
||||||
|
eplist->tail = td;
|
||||||
|
|
||||||
|
/* Configure the endpoint descriptor. */
|
||||||
|
|
||||||
|
memset((void*)ed, 0, sizeof(struct sam_ed_s));
|
||||||
|
ed->hw.ctrl = (uint32_t)(epdesc->funcaddr) << ED_CONTROL_FA_SHIFT |
|
||||||
|
(uint32_t)(epdesc->addr) << ED_CONTROL_EN_SHIFT |
|
||||||
|
(uint32_t)(epdesc->mxpacketsize) << ED_CONTROL_MPS_SHIFT;
|
||||||
|
|
||||||
|
/* Get the direction of the endpoint */
|
||||||
|
|
||||||
|
if (epdesc->in)
|
||||||
|
{
|
||||||
|
ed->hw.ctrl |= ED_CONTROL_D_IN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ed->hw.ctrl |= ED_CONTROL_D_OUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for a low-speed device */
|
||||||
|
|
||||||
|
if (rhport->lowspeed)
|
||||||
|
{
|
||||||
|
ed->hw.ctrl |= ED_CONTROL_S;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the transfer type */
|
||||||
|
|
||||||
|
ed->xfrtype = epdesc->xfrtype;
|
||||||
|
|
||||||
|
/* Special Case isochronous transfer types */
|
||||||
|
|
||||||
|
#if 0 /* Isochronous transfers not yet supported */
|
||||||
|
if (ed->xfrtype == USB_EP_ATTR_XFER_ISOC)
|
||||||
|
{
|
||||||
|
ed->hw.ctrl |= ED_CONTROL_F;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ed->eplist = eplist;
|
||||||
|
uvdbg("EP%d CTRL: %08x\n", epdesc->addr, ed->hw.ctrl);
|
||||||
|
|
||||||
|
/* Configure the tail descriptor. */
|
||||||
|
|
||||||
|
memset(td, 0, sizeof(struct sam_gtd_s));
|
||||||
|
td->ed = ed;
|
||||||
|
|
||||||
|
/* Link the tail TD to the ED's TD list */
|
||||||
|
|
||||||
|
physaddr = (uintptr_t)sam_physramaddr((uintptr_t)td);
|
||||||
|
ed->hw.headp = physaddr;
|
||||||
|
ed->hw.tailp = physaddr;
|
||||||
|
|
||||||
|
/* Now add the endpoint descriptor to the appropriate list */
|
||||||
|
|
||||||
|
switch (ed->xfrtype)
|
||||||
|
{
|
||||||
|
case USB_EP_ATTR_XFER_BULK:
|
||||||
|
ret = sam_addbulked(ed);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_EP_ATTR_XFER_INT:
|
||||||
|
ret = sam_addinted(epdesc, ed);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_EP_ATTR_XFER_ISOC:
|
||||||
|
ret = sam_addisoced(epdesc, ed);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_EP_ATTR_XFER_CONTROL:
|
||||||
|
default:
|
||||||
|
ret = -EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Was the ED successfully added? */
|
||||||
|
|
||||||
|
if (ret != OK)
|
||||||
|
{
|
||||||
|
/* No.. destroy it and report the error */
|
||||||
|
|
||||||
|
udbg("ERROR: Failed to queue ED for transfer type: %d\n", ed->xfrtype);
|
||||||
|
goto errout_with_td;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success.. return an opaque reference to the endpoint list container */
|
||||||
|
|
||||||
|
*ep = (usbhost_ep_t)eplist;
|
||||||
sam_givesem(&g_ohci.exclsem);
|
sam_givesem(&g_ohci.exclsem);
|
||||||
|
return OK;
|
||||||
|
|
||||||
|
errout_with_td:
|
||||||
|
sam_tdfree(td);
|
||||||
|
errout_with_ed:
|
||||||
|
sam_edfree(ed);
|
||||||
|
errout_with_semaphore:
|
||||||
|
sam_givesem(&g_ohci.exclsem);
|
||||||
|
kfree(eplist);
|
||||||
|
errout:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2301,14 +2368,17 @@ static int sam_epalloc(FAR struct usbhost_driver_s *drvr,
|
|||||||
static int sam_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
|
static int sam_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
|
||||||
{
|
{
|
||||||
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
|
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
|
||||||
struct sam_ed_s *ed = (struct sam_ed_s *)ep;
|
struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep;
|
||||||
int ret;
|
struct sam_ed_s *ed;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
DEBUGASSERT(rhport && eplist && eplist->ed && eplist->tail);
|
||||||
|
|
||||||
/* There should not be any pending, real TDs linked to this ED */
|
/* There should not be any pending, real TDs linked to this ED */
|
||||||
|
|
||||||
DEBUGASSERT(rhport && ed &&
|
ed = eplist->ed;
|
||||||
(ed->hw.headp & ED_HEADP_ADDR_MASK) ==
|
DEBUGASSERT((ed->hw.headp & ED_HEADP_ADDR_MASK) ==
|
||||||
sam_physramaddr((uintptr_t)rhport->tdtail));
|
sam_physramaddr((uintptr_t)rhport->ep0.tail));
|
||||||
|
|
||||||
/* We must have exclusive access to the ED pool, the bulk list, the periodic list
|
/* We must have exclusive access to the ED pool, the bulk list, the periodic list
|
||||||
* and the interrupt table.
|
* and the interrupt table.
|
||||||
@ -2316,20 +2386,20 @@ static int sam_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
|
|||||||
|
|
||||||
sam_takesem(&g_ohci.exclsem);
|
sam_takesem(&g_ohci.exclsem);
|
||||||
|
|
||||||
/* Remove the ED to the correct list depending on the trasfer type */
|
/* Remove the ED to the correct list depending on the transfer type */
|
||||||
|
|
||||||
switch (ed->xfrtype)
|
switch (ed->xfrtype)
|
||||||
{
|
{
|
||||||
case USB_EP_ATTR_XFER_BULK:
|
case USB_EP_ATTR_XFER_BULK:
|
||||||
ret = sam_rembulked(ed);
|
ret = sam_rembulked(eplist->ed);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case USB_EP_ATTR_XFER_INT:
|
case USB_EP_ATTR_XFER_INT:
|
||||||
ret = sam_reminted(ed);
|
ret = sam_reminted(eplist->ed);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case USB_EP_ATTR_XFER_ISOC:
|
case USB_EP_ATTR_XFER_ISOC:
|
||||||
ret = sam_remisoced(ed);
|
ret = sam_remisoced(eplist->ed);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case USB_EP_ATTR_XFER_CONTROL:
|
case USB_EP_ATTR_XFER_CONTROL:
|
||||||
@ -2338,13 +2408,15 @@ static int sam_epfree(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Destroy the semaphore */
|
/* Put the ED and tail TDs back into the free list */
|
||||||
|
|
||||||
sem_destroy(&ed->wdhsem);
|
sam_edfree(eplist->ed);
|
||||||
|
sam_tdfree(eplist->tail);
|
||||||
|
|
||||||
/* Put the ED back into the free list */
|
/* And free the container */
|
||||||
|
|
||||||
sam_edfree(ed);
|
sem_destroy(&eplist->wdhsem);
|
||||||
|
kfree(eplist);
|
||||||
sam_givesem(&g_ohci.exclsem);
|
sam_givesem(&g_ohci.exclsem);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -2581,8 +2653,7 @@ static int sam_ctrlin(FAR struct usbhost_driver_s *drvr,
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
sam_givesem(&g_ohci.exclsem);
|
sam_givesem(&g_ohci.exclsem);
|
||||||
cp15_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + len);
|
cp15_invalidate_dcache((uintptr_t)buffer, (uintptr_t)buffer + len - 1);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2664,7 +2735,8 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
|||||||
FAR uint8_t *buffer, size_t buflen)
|
FAR uint8_t *buffer, size_t buflen)
|
||||||
{
|
{
|
||||||
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
|
struct sam_rhport_s *rhport = (struct sam_rhport_s *)drvr;
|
||||||
struct sam_ed_s *ed = (struct sam_ed_s *)ep;
|
struct sam_eplist_s *eplist = (struct sam_eplist_s *)ep;
|
||||||
|
struct sam_ed_s *ed;
|
||||||
uint32_t dirpid;
|
uint32_t dirpid;
|
||||||
uint32_t regval;
|
uint32_t regval;
|
||||||
#if SAM_IOBUFFERS > 0
|
#if SAM_IOBUFFERS > 0
|
||||||
@ -2673,8 +2745,10 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
|||||||
bool in;
|
bool in;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
DEBUGASSERT(rhport && ed && buffer && buflen > 0);
|
DEBUGASSERT(rhport && eplist && eplist->ed && eplist->tail &&
|
||||||
|
buffer && buflen > 0);
|
||||||
|
|
||||||
|
ed = eplist->ed;
|
||||||
in = (ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN;
|
in = (ed->hw.ctrl & ED_CONTROL_D_MASK) == ED_CONTROL_D_IN;
|
||||||
|
|
||||||
uvdbg("EP%d %s toggle: %d maxpacket: %d buflen: %d\n",
|
uvdbg("EP%d %s toggle: %d maxpacket: %d buflen: %d\n",
|
||||||
@ -2727,14 +2801,19 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
|||||||
regval |= OHCI_CMDST_BLF;
|
regval |= OHCI_CMDST_BLF;
|
||||||
sam_putreg(regval, SAM_USBHOST_CMDST);
|
sam_putreg(regval, SAM_USBHOST_CMDST);
|
||||||
|
|
||||||
/* Wait for the Writeback Done Head interrupt */
|
/* Wait for the Writeback Done Head interrupt Loop to handle any false
|
||||||
|
* alarm semaphore counts.
|
||||||
|
*/
|
||||||
|
|
||||||
sam_takesem(&ed->wdhsem);
|
while (eplist->wdhwait)
|
||||||
|
{
|
||||||
|
sam_takesem(&eplist->wdhsem);
|
||||||
|
}
|
||||||
|
|
||||||
/* Invalidate the D cache to force the ED to be reloaded from RAM */
|
/* Invalidate the D cache to force the ED to be reloaded from RAM */
|
||||||
|
|
||||||
cp15_invalidate_dcache((uintptr_t)ed,
|
cp15_invalidate_dcache((uintptr_t)ed,
|
||||||
(uintptr_t)ed + sizeof(struct sam_ed_s));
|
(uintptr_t)ed + sizeof(struct ohci_ed_s) - 1);
|
||||||
|
|
||||||
/* Check the TD completion status bits */
|
/* Check the TD completion status bits */
|
||||||
|
|
||||||
@ -2747,7 +2826,7 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
|||||||
if (in)
|
if (in)
|
||||||
{
|
{
|
||||||
cp15_invalidate_dcache((uintptr_t)buffer,
|
cp15_invalidate_dcache((uintptr_t)buffer,
|
||||||
(uintptr_t)buffer + buflen);
|
(uintptr_t)buffer + buflen - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = OK;
|
ret = OK;
|
||||||
@ -2762,7 +2841,7 @@ static int sam_transfer(FAR struct usbhost_driver_s *drvr, usbhost_ep_t ep,
|
|||||||
errout:
|
errout:
|
||||||
/* Make sure that there is no outstanding request on this endpoint */
|
/* Make sure that there is no outstanding request on this endpoint */
|
||||||
|
|
||||||
ed->wdhwait = false;
|
eplist->wdhwait = false;
|
||||||
sam_givesem(&g_ohci.exclsem);
|
sam_givesem(&g_ohci.exclsem);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
@ -1188,6 +1188,11 @@ Configurations
|
|||||||
Application Configuration -> NSH Library
|
Application Configuration -> NSH Library
|
||||||
CONFIG_NSH_ARCHINIT=y : NSH board-initialization
|
CONFIG_NSH_ARCHINIT=y : NSH board-initialization
|
||||||
|
|
||||||
|
NOTE: When OHCI is selected, the SAMA5 will operate at 384MHz instead
|
||||||
|
of 396MHz. This is so that the PLL generates a frequency which is a
|
||||||
|
multiple of the 48MHz needed for OHCI. The delay loop calibration
|
||||||
|
values that are used will be off slightly because of this.
|
||||||
|
|
||||||
STATUS:
|
STATUS:
|
||||||
2013-7-19: This configuration (as do the others) run at 396MHz.
|
2013-7-19: This configuration (as do the others) run at 396MHz.
|
||||||
The SAMA5D3 can run at 536MHz. I still need to figure out the
|
The SAMA5D3 can run at 536MHz. I still need to figure out the
|
||||||
|
Loading…
Reference in New Issue
Block a user