Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ struct USB2OTGUnit
UBYTE hc_WatchdogCount; /* Incremented each NakTimeout period while channel active */
UBYTE hc_SplitCSplitPending; /* SSPLIT was issued and request must not be resubmitted yet */
UBYTE hc_DeferCount; /* Consecutive watchdog defers; caps total defer time */
UBYTE hc_CsplitRetry; /* CSPLIT NYET retries this interval; caps to TT result window */
UBYTE hc_SplitState; /* periodic-split SM: USB2OTG_SPLIT_{IDLE,SS,CS} */
UWORD hc_SplitSSUframe; /* HFNUM&0x3fff uframe the SSPLIT was issued in */
struct IOUsbHWReq * hc_DiagReq; /* Last bulk request tracked on this channel */
ULONG hc_DiagStartFrame;
ULONG hc_DiagLastProgressFrame;
Expand Down Expand Up @@ -333,6 +336,11 @@ struct USB2OTGDevice
#define CHAN_INT_LAST CHAN_INT5
#define CHAN_BULK2 7

/* Periodic-split sequencer state (hc_SplitState). */
#define USB2OTG_SPLIT_IDLE 0 /* not a split, or split sequence finished */
#define USB2OTG_SPLIT_SS 1 /* SSPLIT issued, awaiting ACK */
#define USB2OTG_SPLIT_CS 2 /* CSPLIT issued/pending, awaiting completion */

/*
* Bulk-OUT per-packet throttle (busy-wait iterations before arming).
* Some flash devices with tiny SRAM buffers go silent (no NYET/NAK)
Expand Down
66 changes: 64 additions & 2 deletions arch/arm-native/soc/broadcom/2708/usb/usb2otg/usb2otg_intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -634,13 +634,26 @@ void FNAME_DEV(GlobalIRQHandler)(struct USB2OTGUnit *USBUnit, struct ExecBase *S
tmp |= 1 << 16; /* Set "do complete split" */
wr32le(USB2OTG_CHANNEL_REG(chan, SPLITCTRL), tmp);
USBUnit->hu_Channel[chan].hc_SplitCSplitPending = 1;
/* Fresh CSPLIT sequence — reset the NYET retry window. */
USBUnit->hu_Channel[chan].hc_CsplitRetry = 0;
USBUnit->hu_Channel[chan].hc_SplitState = USB2OTG_SPLIT_CS;

if (req->iouh_Req.io_Command == UHCMD_BULKXFER)
usb2otg_remove_bulk_queue_duplicates(USBUnit, req, chan);

D(bug("[USB2OTG] Completing split transaction in interrupt: chan=%d intr=%04x SPLITCTRL=%08x\n",
chan, intr, tmp));
FNAME_DEV(StartChannel)(USBUnit, chan, 1);

/*
* Periodic INT split: issue the CSPLIT one microframe
* later (delayed_channel[] ticks per SOF) so it lands in
* the TT result window instead of hammering the same
* uframe. Bulk keeps the immediate re-arm.
*/
if (chan >= CHAN_INT1 && chan <= CHAN_INT_LAST)
delayed_channel[chan] = 1;
else
FNAME_DEV(StartChannel)(USBUnit, chan, 1);
}
else if ((do_split == USB2OTG_HCSPLT_CSPLIT) &&
(req->iouh_Req.io_Command == UHCMD_BULKXFER) &&
Expand Down Expand Up @@ -714,7 +727,36 @@ void FNAME_DEV(GlobalIRQHandler)(struct USB2OTGUnit *USBUnit, struct ExecBase *S
else
{
if (chan >= CHAN_INT1 && chan <= CHAN_INT_LAST)
FNAME_DEV(StartChannel)(USBUnit, chan, 1);
{
/*
* CSPLIT NYET = TT has not finished the
* LS/FS transaction yet. Retry the CSPLIT
* one microframe later (delayed_channel
* ticks per SOF = per microframe in HS)
* rather than re-arming in the same uframe.
* Bound to ~8 uframes from the SSPLIT
* (FreeBSD DWC_OTG_TT_SLOT_MAX); past that
* the TT result window is gone, so requeue
* for the next interval.
*/
if (USBUnit->hu_Channel[chan].hc_CsplitRetry < 8)
{
USBUnit->hu_Channel[chan].hc_CsplitRetry++;
delayed_channel[chan] = 1;
}
else
{
ULONG interval = req->iouh_Interval;
usb2otg_halt_channel_preserve_char(chan);
if ((req->iouh_Flags & UHFF_SPLITTRANS) && interval < 2)
interval = 2;
ULONG next = (frnm + interval) & 0x7ff;
req->iouh_DriverPrivate1 = (APTR)((frnm << 16) | next);
ADDHEAD(&USBUnit->hu_IntXFerQueue, req);
USBUnit->hu_Channel[chan].hc_Request = NULL;
req = NULL;
}
}
else
delayed_channel[chan] = 16;
}
Expand Down Expand Up @@ -1676,6 +1718,25 @@ static BOOL usb2otg_process_naktimeout(struct USB2OTGUnit *otg_Unit)
continue;
}

/*
* SOF scheduler owns active periodic-split channels: a CSPLIT
* re-arm is pending within microframes (delayed_channel != 0),
* so the channel is not stuck. Defer — the watchdog's lost-IRQ
* recovery would otherwise race the SS->CS handoff and corrupt
* CompSplt. The sequencer's bounded retry/requeue is the backstop.
*/
if (chan >= CHAN_INT1 && chan <= CHAN_INT_LAST &&
otg_Unit->hu_Channel[chan].hc_SplitState != USB2OTG_SPLIT_IDLE &&
delayed_channel[chan] != 0)
{
#if defined(__AROSEXEC_SMP__)
KrnSpinUnLock(&otg_Unit->hu_Lock);
#endif
Enable();
otg_Unit->hu_Channel[chan].hc_WatchdogCount = 0;
continue;
}

ULONG charbase = rd32le(USB2OTG_CHANNEL_REG(chan, CHARBASE));

if (!(charbase & USB2OTG_HOSTCHAR_ENABLE))
Expand Down Expand Up @@ -2061,6 +2122,7 @@ static BOOL usb2otg_process_naktimeout(struct USB2OTGUnit *otg_Unit)
{
otg_Unit->hu_Channel[chan].hc_DeferCount++;
otg_Unit->hu_Channel[chan].hc_WatchdogCount = 0;

D(
{
struct USB2OTGChannel *hc = &otg_Unit->hu_Channel[chan];
Expand Down
32 changes: 30 additions & 2 deletions arch/arm-native/soc/broadcom/2708/usb/usb2otg/usb2otg_schedule.c
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,17 @@ BOOL FNAME_DEV(SetupChannel)(struct USB2OTGUnit *otg_Unit, int chan)

/* Fresh request: clear watchdog defer counter. */
otg_Unit->hu_Channel[chan].hc_DeferCount = 0;
otg_Unit->hu_Channel[chan].hc_CsplitRetry = 0;

/*
* A fresh arm is always a START-split — clear any CSPLIT-pending left
* over from a prior interval that requeued mid-split. If it leaks set,
* the splitpos logic below picks XactPos=0 (CSPLIT) instead of ALL,
* producing a malformed periodic SSPLIT that halts with bare CHHLTD.
* CSPLIT is only ever armed via StartChannel(quick=1) from the IRQ.
*/
otg_Unit->hu_Channel[chan].hc_SplitCSplitPending = 0;
otg_Unit->hu_Channel[chan].hc_SplitState = USB2OTG_SPLIT_IDLE;

/*
* Inherit PING state from per-EP bitmap (channel-local flag does
Expand Down Expand Up @@ -464,8 +475,13 @@ BOOL FNAME_DEV(SetupChannel)(struct USB2OTGUnit *otg_Unit, int chan)
/* If split transaction requested limit transfer size to max packet size or 188 bytes, whichever is less */
if (req->iouh_Flags & UHFF_SPLITTRANS)
{
if (req->iouh_Req.io_Command == UHCMD_INTXFER ||
req->iouh_Req.io_Command == UHCMD_ISOXFER)
/*
* HCCHAR.EC/MC = transactions per microframe; only high-bandwidth
* HS endpoints use >1. A low-/full-speed INT split must be EC=1
* (matches Linux dwc2 multi_count=1) — EC=3 makes the core expect
* 3 transactions and deschedule the periodic split (bare CHHLTD).
*/
if (req->iouh_Req.io_Command == UHCMD_ISOXFER)
reg |= USB2OTG_HOSTCHAR_EC(3);
else
reg |= USB2OTG_HOSTCHAR_EC(1);
Expand All @@ -492,6 +508,18 @@ BOOL FNAME_DEV(SetupChannel)(struct USB2OTGUnit *otg_Unit, int chan)
((req->iouh_SplitHubPort & 0x0f)));
}

/*
* Periodic-split sequencer: a fresh INT split arm is a START-split.
* Record the phase and the microframe it is issued in so the SS->CS
* handoff can be paced and the watchdog can defer to this scheduler.
*/
if (req->iouh_Req.io_Command == UHCMD_INTXFER)
{
otg_Unit->hu_Channel[chan].hc_SplitState = USB2OTG_SPLIT_SS;
otg_Unit->hu_Channel[chan].hc_SplitSSUframe =
(UWORD)(rd32le(USB2OTG_HOSTFRAMENO) & 0x3fff);
}

if (xfer_size > req->iouh_MaxPktSize)
xfer_size = req->iouh_MaxPktSize;

Expand Down