aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-3.18/0092-dwc_otg-introduce-fiq_fsm_spin-un-lock.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/brcm2708/patches-3.18/0092-dwc_otg-introduce-fiq_fsm_spin-un-lock.patch')
-rwxr-xr-xtarget/linux/brcm2708/patches-3.18/0092-dwc_otg-introduce-fiq_fsm_spin-un-lock.patch322
1 files changed, 322 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-3.18/0092-dwc_otg-introduce-fiq_fsm_spin-un-lock.patch b/target/linux/brcm2708/patches-3.18/0092-dwc_otg-introduce-fiq_fsm_spin-un-lock.patch
new file mode 100755
index 0000000000..0562a8cf0f
--- /dev/null
+++ b/target/linux/brcm2708/patches-3.18/0092-dwc_otg-introduce-fiq_fsm_spin-un-lock.patch
@@ -0,0 +1,322 @@
+From 424f79f35a94611f73182f19a7711174b756b052 Mon Sep 17 00:00:00 2001
+From: P33M <P33M@github.com>
+Date: Fri, 26 Sep 2014 11:32:09 +0100
+Subject: [PATCH 092/114] dwc_otg: introduce fiq_fsm_spin(un|)lock()
+
+SMP safety for the FIQ relies on register read-modify write cycles being
+completed in the correct order. Several places in the DWC code modify
+registers also touched by the FIQ. Protect these by a bare-bones lock
+mechanism.
+
+This also makes it possible to run the FIQ and IRQ handlers on different
+cores.
+---
+ .../usb/host/dwc_common_port/dwc_common_linux.c | 6 ---
+ drivers/usb/host/dwc_otg/dwc_otg_cil.c | 10 -----
+ drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 46 +++++++++++++++++++++-
+ drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h | 16 +++++++-
+ drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 23 ++++++++++-
+ drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 9 ++++-
+ 6 files changed, 88 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/usb/host/dwc_common_port/dwc_common_linux.c b/drivers/usb/host/dwc_common_port/dwc_common_linux.c
+index 5c50a8b..b802042 100644
+--- a/drivers/usb/host/dwc_common_port/dwc_common_linux.c
++++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c
+@@ -580,13 +580,7 @@ void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value)
+
+ void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask)
+ {
+- unsigned long flags;
+-
+- local_irq_save(flags);
+- local_fiq_disable();
+ writel((readl(reg) & ~clear_mask) | set_mask, reg);
+- local_fiq_enable();
+- local_irq_restore(flags);
+ }
+
+ #if 0
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_cil.c b/drivers/usb/host/dwc_otg/dwc_otg_cil.c
+index 6a32c5c..e40060f 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_cil.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_cil.c
+@@ -2244,9 +2244,7 @@ void dwc_otg_core_host_init(dwc_otg_core_if_t * core_if)
+ */
+ void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+ {
+- uint32_t intr_enable;
+ hcintmsk_data_t hc_intr_mask;
+- gintmsk_data_t gintmsk = {.d32 = 0 };
+ hcchar_data_t hcchar;
+ hcsplt_data_t hcsplt;
+
+@@ -2348,14 +2346,6 @@ void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
+ }
+ DWC_WRITE_REG32(&hc_regs->hcintmsk, hc_intr_mask.d32);
+
+- /* Enable the top level host channel interrupt. */
+- intr_enable = (1 << hc_num);
+- DWC_MODIFY_REG32(&host_if->host_global_regs->haintmsk, 0, intr_enable);
+-
+- /* Make sure host channel interrupts are enabled. */
+- gintmsk.b.hcintr = 1;
+- DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
+-
+ /*
+ * Program the HCCHARn register with the endpoint characteristics for
+ * the current transfer.
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c
+index 284f902..84618a5 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c
+@@ -75,6 +75,46 @@ void notrace _fiq_print(enum fiq_debug_level dbg_lvl, volatile struct fiq_state
+ }
+
+ /**
++ * fiq_fsm_spin_lock() - ARMv6+ bare bones spinlock
++ * Must be called with local interrupts and FIQ disabled.
++ */
++inline void fiq_fsm_spin_lock(fiq_lock_t *lock)
++{
++ unsigned long tmp;
++ uint32_t newval;
++ fiq_lock_t lockval;
++ smp_mb__before_spinlock();
++ /* Nested locking, yay. If we are on the same CPU as the fiq, then the disable
++ * will be sufficient. If we are on a different CPU, then the lock protects us. */
++ prefetchw(&lock->slock);
++ asm volatile (
++ "1: ldrex %0, [%3]\n"
++ " add %1, %0, %4\n"
++ " strex %2, %1, [%3]\n"
++ " teq %2, #0\n"
++ " bne 1b"
++ : "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
++ : "r" (&lock->slock), "I" (1 << 16)
++ : "cc");
++
++ while (lockval.tickets.next != lockval.tickets.owner) {
++ wfe();
++ lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);
++ }
++ smp_mb();
++}
++
++/**
++ * fiq_fsm_spin_unlock() - ARMv6+ bare bones spinunlock
++ */
++inline void fiq_fsm_spin_unlock(fiq_lock_t *lock)
++{
++ smp_mb();
++ lock->tickets.owner++;
++ dsb_sev();
++}
++
++/**
+ * fiq_fsm_restart_channel() - Poke channel enable bit for a split transaction
+ * @channel: channel to re-enable
+ */
+@@ -1142,6 +1182,7 @@ void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels)
+ gintsts_handled.d32 = 0;
+ haint_handled.d32 = 0;
+
++ fiq_fsm_spin_lock(&state->lock);
+ gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS);
+ gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK);
+ gintsts.d32 &= gintmsk.d32;
+@@ -1231,7 +1272,7 @@ void notrace dwc_otg_fiq_fsm(struct fiq_state *state, int num_channels)
+
+ }
+ state->fiq_done++;
+- mb();
++ fiq_fsm_spin_unlock(&state->lock);
+ }
+
+
+@@ -1253,6 +1294,7 @@ void notrace dwc_otg_fiq_nop(struct fiq_state *state)
+ gintmsk_data_t gintmsk;
+ hfnum_data_t hfnum;
+
++ fiq_fsm_spin_lock(&state->lock);
+ hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM);
+ gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS);
+ gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK);
+@@ -1290,5 +1332,5 @@ void notrace dwc_otg_fiq_nop(struct fiq_state *state)
+
+ }
+ state->fiq_done++;
+- mb();
++ fiq_fsm_spin_unlock(&state->lock);
+ }
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h
+index 5c7707f..8455324 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h
++++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h
+@@ -120,7 +120,6 @@ typedef struct {
+ volatile void* intstat;
+ } mphi_regs_t;
+
+-
+ enum fiq_debug_level {
+ FIQDBG_SCHED = (1 << 0),
+ FIQDBG_INT = (1 << 1),
+@@ -128,6 +127,16 @@ enum fiq_debug_level {
+ FIQDBG_PORTHUB = (1 << 3),
+ };
+
++typedef struct {
++ union {
++ uint32_t slock;
++ struct _tickets {
++ uint16_t owner;
++ uint16_t next;
++ } tickets;
++ };
++} fiq_lock_t;
++
+ struct fiq_state;
+
+ extern void _fiq_print (enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...);
+@@ -324,6 +333,7 @@ struct fiq_channel_state {
+ * It contains top-level state information.
+ */
+ struct fiq_state {
++ fiq_lock_t lock;
+ mphi_regs_t mphi_regs;
+ void *dwc_regs_base;
+ dma_addr_t dma_base;
+@@ -342,6 +352,10 @@ struct fiq_state {
+ struct fiq_channel_state channel[0];
+ };
+
++extern void fiq_fsm_spin_lock(fiq_lock_t *lock);
++
++extern void fiq_fsm_spin_unlock(fiq_lock_t *lock);
++
+ extern int fiq_fsm_too_late(struct fiq_state *st, int n);
+
+ extern int fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n);
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
+index 68d4f3b..124ac16 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
+@@ -1184,6 +1184,9 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+ dwc_otg_qtd_t *qtd;
+ dwc_otg_hcd_urb_t *urb;
+ void* ptr = NULL;
++ uint32_t intr_enable;
++ unsigned long flags;
++ gintmsk_data_t gintmsk = { .d32 = 0, };
+
+ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
+
+@@ -1409,6 +1412,20 @@ static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh)
+ hc->desc_list_addr = qh->desc_list_dma;
+
+ dwc_otg_hc_init(hcd->core_if, hc);
++
++ local_irq_save(flags);
++ local_fiq_disable();
++ fiq_fsm_spin_lock(&hcd->fiq_state->lock);
++ /* Enable the top level host channel interrupt. */
++ intr_enable = (1 << hc->hc_num);
++ DWC_MODIFY_REG32(&hcd->core_if->host_if->host_global_regs->haintmsk, 0, intr_enable);
++
++ /* Make sure host channel interrupts are enabled. */
++ gintmsk.b.hcintr = 1;
++ DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
++ local_fiq_enable();
++ local_irq_restore(flags);
+ hc->qh = qh;
+ }
+
+@@ -1659,6 +1676,7 @@ int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
+ fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32);
+ hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
+ local_fiq_disable();
++ fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+ DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32);
+ DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32);
+ DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32);
+@@ -1676,6 +1694,7 @@ int fiq_fsm_queue_isoc_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
+ }
+ mb();
+ st->hcchar_copy.b.chen = 0;
++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
+ local_fiq_enable();
+ return 0;
+ }
+@@ -1811,7 +1830,7 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
+ DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32);
+
+ local_fiq_disable();
+- mb();
++ fiq_fsm_spin_lock(&hcd->fiq_state->lock);
+
+ if (hc->ep_type & 0x1) {
+ hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
+@@ -1909,7 +1928,7 @@ int fiq_fsm_queue_split_transaction(dwc_otg_hcd_t *hcd, dwc_otg_qh_t *qh)
+ st->hcchar_copy.b.chen = 1;
+ DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32);
+ }
+- mb();
++ fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
+ local_fiq_enable();
+ return 0;
+ }
+diff --git a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
+index a5566bc..ee35196 100644
+--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
+@@ -101,6 +101,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+ if (dwc_otg_is_host_mode(core_if)) {
+ if (fiq_enable) {
+ local_fiq_disable();
++ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
+ /* Pull in from the FIQ's disabled mask */
+ gintmsk.d32 = gintmsk.d32 | ~(dwc_otg_hcd->fiq_state->gintmsk_saved.d32);
+ dwc_otg_hcd->fiq_state->gintmsk_saved.d32 = ~0;
+@@ -116,8 +117,10 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+ }
+ gintsts.d32 &= gintmsk.d32;
+
+- if (fiq_enable)
++ if (fiq_enable) {
++ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
+ local_fiq_enable();
++ }
+
+ if (!gintsts.d32) {
+ goto exit_handler_routine;
+@@ -200,6 +203,7 @@ exit_handler_routine:
+ gintmsk_data_t gintmsk_new;
+ haintmsk_data_t haintmsk_new;
+ local_fiq_disable();
++ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
+ gintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->gintmsk_saved.d32;
+ if(fiq_fsm_enable)
+ haintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->haintmsk_saved.d32;
+@@ -222,6 +226,7 @@ exit_handler_routine:
+ haintmsk.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk);
+ /* Re-enable interrupts that the FIQ masked (first time round) */
+ FIQ_WRITE(dwc_otg_hcd->fiq_state->dwc_regs_base + GINTMSK, gintmsk.d32);
++ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
+ local_fiq_enable();
+
+ if ((jiffies / HZ) > last_time) {
+@@ -633,8 +638,10 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd)
+ {
+ /* check the mask? */
+ local_fiq_disable();
++ fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
+ haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint);
+ dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0;
++ fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
+ local_fiq_enable();
+ }
+
+--
+1.8.3.2
+