From 460be286c4d5ea2bc02ac186caa7c2db3570e032 Mon Sep 17 00:00:00 2001 From: schulz Date: Wed, 30 Jan 2019 20:03:13 +0000 Subject: [PATCH] handle split and intr transfers properly git-svn-id: https://svn.aros.org/svn/aros/trunk/AROS@55645 fb15a70f-31f2-0310-bbcc-cdcc74a49acc --- .../soc/broadcom/2708/usb/usb2otg/usb2otg_device.c | 257 +++++++++++++++++++-- 1 file changed, 243 insertions(+), 14 deletions(-) diff --git a/arch/arm-native/soc/broadcom/2708/usb/usb2otg/usb2otg_device.c b/arch/arm-native/soc/broadcom/2708/usb/usb2otg/usb2otg_device.c index d47a6019d6..966cc019e3 100644 --- a/arch/arm-native/soc/broadcom/2708/usb/usb2otg/usb2otg_device.c +++ b/arch/arm-native/soc/broadcom/2708/usb/usb2otg/usb2otg_device.c @@ -3,7 +3,7 @@ $Id$ */ -#define DEBUG 1 +#define DEBUG 0 #include #include @@ -25,39 +25,259 @@ AROS_INTP(FNAME_DEV(NakTimeoutInt)); IPTR __arm_periiobase __attribute__((used)) = 0 ; +ULONG last_frame = 0; + +static void DumpChannelRegs(int channel) +{ + D(bug("CHARBASE=%08x\n", rd32le(USB2OTG_CHANNEL_REG(channel, CHARBASE)))); + D(bug("SPLITCTRL=%08x\n", rd32le(USB2OTG_CHANNEL_REG(channel, SPLITCTRL)))); + D(bug("INTR=%08x\n", rd32le(USB2OTG_CHANNEL_REG(channel, INTR)))); + D(bug("INTRMASK=%08x\n", rd32le(USB2OTG_CHANNEL_REG(channel, INTRMASK)))); + D(bug("TRANSSIZE=%08x\n", rd32le(USB2OTG_CHANNEL_REG(channel, TRANSSIZE)))); + D(bug("DMAADR=%08x\n", rd32le(USB2OTG_CHANNEL_REG(channel, DMAADDR)))); +} + static void GlobalIRQHandler(struct USB2OTGUnit *USBUnit, struct ExecBase *SysBase) { volatile unsigned int otg_RegVal; + struct USB2OTGDevice * USB2OTGBase = USBUnit->hu_USB2OTGBase; + ULONG frnm = (rd32le(USB2OTG_HOSTFRAMENO) & 0x3fff) >> 3; // D(bug("[USB2OTG] %s: Received USB interrupt for Unit @ 0x%p\n", // __PRETTY_FUNCTION__, USBUnit)); otg_RegVal = rd32le(USB2OTG_INTR); + wr32le(USB2OTG_INTR, otg_RegVal); if (otg_RegVal & USB2OTG_INTRCORE_DMASTARTOFFRAME) { + struct IOUsbHWReq *req, *next; + wr32le(USB2OTG_INTR, USB2OTG_INTRCORE_DMASTARTOFFRAME); + + if (frnm < last_frame) + { + D(bug("[USB2OTG] SOF, frame wrap %d %d\n", frnm, last_frame)); + } + + if (frnm != last_frame) + { + ForeachNodeSafe(&USBUnit->hu_IntXFerQueue, req, next) + { + ULONG last_handled = (ULONG)req->iouh_DriverPrivate1; + ULONG next_to_handle = (ULONG)req->iouh_DriverPrivate2; + + /* Is it time to handle the request? If yes, move it to Scheduled list */ + if (frnm == next_to_handle) + { + //D(bug("[USB2OTG] schedule INT request on time, last=%d, next=%d, frnm=%d\n", last_handled, next_to_handle, frnm)); + REMOVE(req); + ADDTAIL(&USBUnit->hu_IntXFerScheduled, req); + } + /* + If the request is overdued several scenarios are possible: + 1. frnm is larger than "last" and "next" + 0......last....next...frnm....2047 + 2. frnm is smaller than "last" and "next" + 0..frnm.....last....next......2047 + 3. frnm is bigger than "next" but smaller than "last" + 0..next....frnm......last.....2047 + */ + else if ( + (frnm > next_to_handle && frnm > last_handled) || + (frnm < last_handled && frnm > next_to_handle) || + (frnm > next_to_handle && frnm < last_handled) + ) + { + //D(bug("[USB2OTG] overdued INT request, scheduling. last=%d, next=%d, frnm=%d\n", last_handled, next_to_handle, frnm)); + REMOVE(req); + ADDTAIL(&USBUnit->hu_IntXFerScheduled, req); + } + } + + /* If the Scheduled list is not empty process it now */ + if (!IsListEmpty(&USBUnit->hu_IntXFerScheduled)) + FNAME_DEV(ScheduleIntTDs)(USBUnit); + + last_frame = frnm; + } } if (otg_RegVal & USB2OTG_INTRCORE_HOSTCHANNEL) { - volatile unsigned int otg_ChanVal; + unsigned int otg_ChanVal; + int chan; + otg_ChanVal = rd32le(USB2OTG_HOSTINTR); wr32le(USB2OTG_HOSTINTR, otg_ChanVal); -#if (0) - for (chan = 0; chan < ... ; chan ++) - { - if (otg_ChanVal & (1 << chan)) - { - *((volatile unsigned int *)USB2OTG_HOST_CHANBASE + (chan * USB2OTG_HOST_CHANREGSIZE) + 0x0c) = 0; - D(bug("[USB2OTG] %s: Host Channel #%d Interrupt\n", - __PRETTY_FUNCTION__, chan)); - } - } -#endif + + //D(bug("[USB2OTG] HOSTCHANNEL %x frnm %d\n", otg_ChanVal, frnm)); + + for (chan = 0; chan < 8; chan++) + { + if (otg_ChanVal & (1 << chan)) + { + struct IOUsbHWReq * req = USBUnit->hu_InProgressXFer[chan]; + uint32_t tmp = rd32le(USB2OTG_CHANNEL_REG(chan, INTR)); + + if (req) + { + int do_split = rd32le(USB2OTG_CHANNEL_REG(chan, SPLITCTRL)) & 0x80010000; + /* + Channel closed with ACK but not comleted yet and split is active. Complete split now, do not continue with + normal processing of this channel + */ + if (tmp == 0x22 && do_split) + { + + uint32_t split = rd32le(USB2OTG_CHANNEL_REG(chan, SPLITCTRL)); + //D(bug("[USB2OTG] Completing split transaction in interrupt. SPLITCTRL=%08x\n", split)); + + split |= 1 << 16; /* Set "do complete split" */ + wr32le(USB2OTG_CHANNEL_REG(chan, SPLITCTRL), split); + + /* Clear interrupt flags */ + wr32le(USB2OTG_CHANNEL_REG(chan, INTR), 0x7ff); + + /* Enable channel again */ + tmp = rd32le(USB2OTG_CHANNEL_REG(chan, CHARBASE)); + tmp |= USB2OTG_HOSTCHAR_ENABLE; + wr32le(USB2OTG_CHANNEL_REG(chan, CHARBASE), tmp); + } + else if (tmp == 0x42 && do_split == 0x80010000) + { + ULONG last = (ULONG)req->iouh_DriverPrivate1; + ULONG thresh = (last + (ULONG)req->iouh_Interval / 2) % 2047; + + /* Clear interrupt flags */ + wr32le(USB2OTG_CHANNEL_REG(chan, INTR), 0x7ff); + + if ((chan >= CHAN_INT1 && chan <= CHAN_INT3) && + ((frnm > thresh && frnm > last) || + (frnm < thresh && frnm < last) || + (frnm > thresh && frnm < last))) + { + //D(bug("[USB2OTG] restarting transaction... %d, %d, %d\n", frnm, req->iouh_DriverPrivate1, req->iouh_DriverPrivate2)); + + /* Disable channel */ + wr32le(USB2OTG_CHANNEL_REG(chan, CHARBASE), 0); + + /* Put transfer back into queue */ + if (req->iouh_Req.io_Command == UHCMD_CONTROLXFER) + ADDHEAD(&USBUnit->hu_CtrlXFerQueue, req); + if (req->iouh_Req.io_Command == UHCMD_BULKXFER) + ADDHEAD(&USBUnit->hu_BulkXFerQueue, req); + if (req->iouh_Req.io_Command == UHCMD_INTXFER) + ADDHEAD(&USBUnit->hu_IntXFerQueue, req); + + /* Mark channel free */ + USBUnit->hu_InProgressXFer[chan] = NULL; + } + else + { + //D(bug("!!! Channel %d, Restarting CSPLIT phase! %d, %d, %d\n", chan, frnm, req->iouh_DriverPrivate1, req->iouh_DriverPrivate2)); + /* Enable channel again */ + tmp = rd32le(USB2OTG_CHANNEL_REG(chan, CHARBASE)); + tmp |= USB2OTG_HOSTCHAR_ENABLE; + wr32le(USB2OTG_CHANNEL_REG(chan, CHARBASE), tmp); + } + + } + else if (tmp == 0x12 && do_split) + //&& (chan < CHAN_INT1 || chan > CHAN_INT3)) + { + /* NAK response from split transaction. Restart the request from beginning */ + /* D(bug("[USB2OTG] Restarting split transaction\n")); + + DumpChannelRegs(chan);*/ + + /* Clear interrupt flags */ + wr32le(USB2OTG_CHANNEL_REG(chan, INTR), 0x7ff); + + /* Disable channel */ + wr32le(USB2OTG_CHANNEL_REG(chan, CHARBASE), 0); + + /* Put transfer back into queue */ + if (req->iouh_Req.io_Command == UHCMD_CONTROLXFER) + ADDHEAD(&USBUnit->hu_CtrlXFerQueue, req); + if (req->iouh_Req.io_Command == UHCMD_BULKXFER) + ADDHEAD(&USBUnit->hu_BulkXFerQueue, req); + if (req->iouh_Req.io_Command == UHCMD_INTXFER) + ADDHEAD(&USBUnit->hu_IntXFerQueue, req); + + /* Mark channel free */ + USBUnit->hu_InProgressXFer[chan] = NULL; + } + else + { + /* Ignore NAK on INT channels when reporting closed channel */ + if (tmp != 0x12 || (chan < CHAN_INT1 && chan > CHAN_INT3)) + D(bug("[USB2OTG] Channel %d closed. INTR=%08x\n", chan, tmp)); + + if (tmp == 0x23) + { + /* Toggle PID */ + USBUnit->hu_PIDBits[req->iouh_DevAddr] ^= (2 << (2 * req->iouh_Endpoint)); + req->iouh_Actual = req->iouh_Length; + req->iouh_Req.io_Error = 0; + } + else if (tmp & 0x80) + { + req->iouh_Actual = 0; + req->iouh_Req.io_Error = UHIOERR_TIMEOUT; + DumpChannelRegs(chan); + } + else if (tmp & 0x08) + { + req->iouh_Actual = 0; + req->iouh_Req.io_Error = UHIOERR_STALL; + DumpChannelRegs(chan); + } + else if (tmp & 0x10) + { + /* In case of INT requests NAK is silently ignored. Just put the request back to the IntXferQueue */ + if (chan >= CHAN_INT1 && chan <= CHAN_INT3) + { + ADDTAIL(&USBUnit->hu_IntXFerQueue, req); + req = NULL; + } + else + { + req->iouh_Actual = 0; + req->iouh_Req.io_Error = UHIOERR_NAK; + DumpChannelRegs(chan); + } + } + else if (tmp & 0x100) + { + req->iouh_Actual = 0; + req->iouh_Req.io_Error = UHIOERR_BABBLE; + DumpChannelRegs(chan); + } + else if (tmp & 0x400) + { + req->iouh_Actual = 0; + req->iouh_Req.io_Error = UHIOERR_HOSTERROR; + DumpChannelRegs(chan); + } + + USBUnit->hu_InProgressXFer[chan] = NULL; + if (req) FNAME_DEV(TermIO)(req, USB2OTGBase); + + wr32le(USB2OTG_CHANNEL_REG(chan, INTR), tmp); + } + } + } + } } - wr32le(USB2OTG_INTR, otg_RegVal); +FNAME_DEV(Cause)(USB2OTGBase, &USBUnit->hu_PendingInt); +/* + if (USBUnit->hu_CtrlXFerQueue.lh_Head->ln_Succ) + { + D(bug("[USB2OTG] [0x%p:PEND] Process CtrlXFer ..\n", USBUnit)); + FNAME_DEV(ScheduleCtrlTDs)(USBUnit); + }*/ } /* @@ -199,6 +419,8 @@ static int FNAME_DEV(Init)(LIBBASETYPEPTR USB2OTGBase) if ((USB2OTGBase->hd_Unit = AllocPooled(USB2OTGBase->hd_MemPool, sizeof(struct USB2OTGUnit))) != NULL) { + int i; + D(bug("[USB2OTG] %s: Unit Allocated at 0x%p\n", __PRETTY_FUNCTION__, USB2OTGBase->hd_Unit)); @@ -206,6 +428,7 @@ static int FNAME_DEV(Init)(LIBBASETYPEPTR USB2OTGBase) NewList(&USB2OTGBase->hd_Unit->hu_CtrlXFerQueue); NewList(&USB2OTGBase->hd_Unit->hu_IntXFerQueue); + NewList(&USB2OTGBase->hd_Unit->hu_IntXFerScheduled); NewList(&USB2OTGBase->hd_Unit->hu_IsoXFerQueue); NewList(&USB2OTGBase->hd_Unit->hu_BulkXFerQueue); NewList(&USB2OTGBase->hd_Unit->hu_TDQueue); @@ -236,6 +459,9 @@ static int FNAME_DEV(Init)(LIBBASETYPEPTR USB2OTGBase) USB2OTGBase->hd_Unit->hu_OperatingMode = (otg_OperatingMode == (USB2OTG_USBHOSTMODE|USB2OTG_USBDEVICEMODE)) ? 0 : otg_OperatingMode; + for (i=0; i < 128; i++) + USB2OTGBase->hd_Unit->hu_PIDBits[i] = 0; + #if (0) D(bug("[USB2OTG] %s: Unit Mode %d\n", __PRETTY_FUNCTION__, USB2OTGBase->hd_Unit->hu_OperatingMode)); @@ -613,6 +839,8 @@ AROS_LH1(LONG, FNAME_DEV(AbortIO), void FNAME_DEV(Cause)(LIBBASETYPEPTR USB2OTGBase, struct Interrupt *interrupt) { + Cause(interrupt); + #if 0 /* this is a workaround for the original Cause() function missing tailed calls */ Disable(); @@ -633,4 +861,5 @@ void FNAME_DEV(Cause)(LIBBASETYPEPTR USB2OTGBase, struct Interrupt *interrupt) interrupt->is_Node.ln_Type = NT_INTERRUPT; } Enable(); + #endif } -- 2.11.4.GIT