brcm2708: refresh patches
[openwrt/openwrt.git] / target / linux / brcm2708 / patches-3.18 / 0092-dwc_otg-introduce-fiq_fsm_spin-un-lock.patch
1 From 424f79f35a94611f73182f19a7711174b756b052 Mon Sep 17 00:00:00 2001
2 From: P33M <P33M@github.com>
3 Date: Fri, 26 Sep 2014 11:32:09 +0100
4 Subject: [PATCH 092/114] dwc_otg: introduce fiq_fsm_spin(un|)lock()
5
6 SMP safety for the FIQ relies on register read-modify write cycles being
7 completed in the correct order. Several places in the DWC code modify
8 registers also touched by the FIQ. Protect these by a bare-bones lock
9 mechanism.
10
11 This also makes it possible to run the FIQ and IRQ handlers on different
12 cores.
13 ---
14 .../usb/host/dwc_common_port/dwc_common_linux.c | 6 ---
15 drivers/usb/host/dwc_otg/dwc_otg_cil.c | 10 -----
16 drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c | 46 +++++++++++++++++++++-
17 drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h | 16 +++++++-
18 drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 23 ++++++++++-
19 drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 9 ++++-
20 6 files changed, 88 insertions(+), 22 deletions(-)
21
22 --- a/drivers/usb/host/dwc_common_port/dwc_common_linux.c
23 +++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c
24 @@ -580,13 +580,7 @@ void DWC_WRITE_REG64(uint64_t volatile *
25
26 void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask)
27 {
28 - unsigned long flags;
29 -
30 - local_irq_save(flags);
31 - local_fiq_disable();
32 writel((readl(reg) & ~clear_mask) | set_mask, reg);
33 - local_fiq_enable();
34 - local_irq_restore(flags);
35 }
36
37 #if 0
38 --- a/drivers/usb/host/dwc_otg/dwc_otg_cil.c
39 +++ b/drivers/usb/host/dwc_otg/dwc_otg_cil.c
40 @@ -2244,9 +2244,7 @@ void dwc_otg_core_host_init(dwc_otg_core
41 */
42 void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc)
43 {
44 - uint32_t intr_enable;
45 hcintmsk_data_t hc_intr_mask;
46 - gintmsk_data_t gintmsk = {.d32 = 0 };
47 hcchar_data_t hcchar;
48 hcsplt_data_t hcsplt;
49
50 @@ -2348,14 +2346,6 @@ void dwc_otg_hc_init(dwc_otg_core_if_t *
51 }
52 DWC_WRITE_REG32(&hc_regs->hcintmsk, hc_intr_mask.d32);
53
54 - /* Enable the top level host channel interrupt. */
55 - intr_enable = (1 << hc_num);
56 - DWC_MODIFY_REG32(&host_if->host_global_regs->haintmsk, 0, intr_enable);
57 -
58 - /* Make sure host channel interrupts are enabled. */
59 - gintmsk.b.hcintr = 1;
60 - DWC_MODIFY_REG32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
61 -
62 /*
63 * Program the HCCHARn register with the endpoint characteristics for
64 * the current transfer.
65 --- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c
66 +++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.c
67 @@ -75,6 +75,46 @@ void notrace _fiq_print(enum fiq_debug_l
68 }
69
70 /**
71 + * fiq_fsm_spin_lock() - ARMv6+ bare bones spinlock
72 + * Must be called with local interrupts and FIQ disabled.
73 + */
74 +inline void fiq_fsm_spin_lock(fiq_lock_t *lock)
75 +{
76 + unsigned long tmp;
77 + uint32_t newval;
78 + fiq_lock_t lockval;
79 + smp_mb__before_spinlock();
80 + /* Nested locking, yay. If we are on the same CPU as the fiq, then the disable
81 + * will be sufficient. If we are on a different CPU, then the lock protects us. */
82 + prefetchw(&lock->slock);
83 + asm volatile (
84 + "1: ldrex %0, [%3]\n"
85 + " add %1, %0, %4\n"
86 + " strex %2, %1, [%3]\n"
87 + " teq %2, #0\n"
88 + " bne 1b"
89 + : "=&r" (lockval), "=&r" (newval), "=&r" (tmp)
90 + : "r" (&lock->slock), "I" (1 << 16)
91 + : "cc");
92 +
93 + while (lockval.tickets.next != lockval.tickets.owner) {
94 + wfe();
95 + lockval.tickets.owner = ACCESS_ONCE(lock->tickets.owner);
96 + }
97 + smp_mb();
98 +}
99 +
100 +/**
101 + * fiq_fsm_spin_unlock() - ARMv6+ bare bones spinunlock
102 + */
103 +inline void fiq_fsm_spin_unlock(fiq_lock_t *lock)
104 +{
105 + smp_mb();
106 + lock->tickets.owner++;
107 + dsb_sev();
108 +}
109 +
110 +/**
111 * fiq_fsm_restart_channel() - Poke channel enable bit for a split transaction
112 * @channel: channel to re-enable
113 */
114 @@ -1142,6 +1182,7 @@ void notrace dwc_otg_fiq_fsm(struct fiq_
115 gintsts_handled.d32 = 0;
116 haint_handled.d32 = 0;
117
118 + fiq_fsm_spin_lock(&state->lock);
119 gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS);
120 gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK);
121 gintsts.d32 &= gintmsk.d32;
122 @@ -1231,7 +1272,7 @@ void notrace dwc_otg_fiq_fsm(struct fiq_
123
124 }
125 state->fiq_done++;
126 - mb();
127 + fiq_fsm_spin_unlock(&state->lock);
128 }
129
130
131 @@ -1253,6 +1294,7 @@ void notrace dwc_otg_fiq_nop(struct fiq_
132 gintmsk_data_t gintmsk;
133 hfnum_data_t hfnum;
134
135 + fiq_fsm_spin_lock(&state->lock);
136 hfnum.d32 = FIQ_READ(state->dwc_regs_base + HFNUM);
137 gintsts.d32 = FIQ_READ(state->dwc_regs_base + GINTSTS);
138 gintmsk.d32 = FIQ_READ(state->dwc_regs_base + GINTMSK);
139 @@ -1290,5 +1332,5 @@ void notrace dwc_otg_fiq_nop(struct fiq_
140
141 }
142 state->fiq_done++;
143 - mb();
144 + fiq_fsm_spin_unlock(&state->lock);
145 }
146 --- a/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h
147 +++ b/drivers/usb/host/dwc_otg/dwc_otg_fiq_fsm.h
148 @@ -120,7 +120,6 @@ typedef struct {
149 volatile void* intstat;
150 } mphi_regs_t;
151
152 -
153 enum fiq_debug_level {
154 FIQDBG_SCHED = (1 << 0),
155 FIQDBG_INT = (1 << 1),
156 @@ -128,6 +127,16 @@ enum fiq_debug_level {
157 FIQDBG_PORTHUB = (1 << 3),
158 };
159
160 +typedef struct {
161 + union {
162 + uint32_t slock;
163 + struct _tickets {
164 + uint16_t owner;
165 + uint16_t next;
166 + } tickets;
167 + };
168 +} fiq_lock_t;
169 +
170 struct fiq_state;
171
172 extern void _fiq_print (enum fiq_debug_level dbg_lvl, volatile struct fiq_state *state, char *fmt, ...);
173 @@ -324,6 +333,7 @@ struct fiq_channel_state {
174 * It contains top-level state information.
175 */
176 struct fiq_state {
177 + fiq_lock_t lock;
178 mphi_regs_t mphi_regs;
179 void *dwc_regs_base;
180 dma_addr_t dma_base;
181 @@ -342,6 +352,10 @@ struct fiq_state {
182 struct fiq_channel_state channel[0];
183 };
184
185 +extern void fiq_fsm_spin_lock(fiq_lock_t *lock);
186 +
187 +extern void fiq_fsm_spin_unlock(fiq_lock_t *lock);
188 +
189 extern int fiq_fsm_too_late(struct fiq_state *st, int n);
190
191 extern int fiq_fsm_tt_in_use(struct fiq_state *st, int num_channels, int n);
192 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
193 +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c
194 @@ -1184,6 +1184,9 @@ static void assign_and_init_hc(dwc_otg_h
195 dwc_otg_qtd_t *qtd;
196 dwc_otg_hcd_urb_t *urb;
197 void* ptr = NULL;
198 + uint32_t intr_enable;
199 + unsigned long flags;
200 + gintmsk_data_t gintmsk = { .d32 = 0, };
201
202 qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list);
203
204 @@ -1409,6 +1412,20 @@ static void assign_and_init_hc(dwc_otg_h
205 hc->desc_list_addr = qh->desc_list_dma;
206
207 dwc_otg_hc_init(hcd->core_if, hc);
208 +
209 + local_irq_save(flags);
210 + local_fiq_disable();
211 + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
212 + /* Enable the top level host channel interrupt. */
213 + intr_enable = (1 << hc->hc_num);
214 + DWC_MODIFY_REG32(&hcd->core_if->host_if->host_global_regs->haintmsk, 0, intr_enable);
215 +
216 + /* Make sure host channel interrupts are enabled. */
217 + gintmsk.b.hcintr = 1;
218 + DWC_MODIFY_REG32(&hcd->core_if->core_global_regs->gintmsk, 0, gintmsk.d32);
219 + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
220 + local_fiq_enable();
221 + local_irq_restore(flags);
222 hc->qh = qh;
223 }
224
225 @@ -1659,6 +1676,7 @@ int fiq_fsm_queue_isoc_transaction(dwc_o
226 fiq_print(FIQDBG_INT, hcd->fiq_state, "%08x", st->hcdma_copy.d32);
227 hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
228 local_fiq_disable();
229 + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
230 DWC_WRITE_REG32(&hc_regs->hctsiz, st->hctsiz_copy.d32);
231 DWC_WRITE_REG32(&hc_regs->hcsplt, st->hcsplt_copy.d32);
232 DWC_WRITE_REG32(&hc_regs->hcdma, st->hcdma_copy.d32);
233 @@ -1676,6 +1694,7 @@ int fiq_fsm_queue_isoc_transaction(dwc_o
234 }
235 mb();
236 st->hcchar_copy.b.chen = 0;
237 + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
238 local_fiq_enable();
239 return 0;
240 }
241 @@ -1811,7 +1830,7 @@ int fiq_fsm_queue_split_transaction(dwc_
242 DWC_WRITE_REG32(&hc_regs->hcintmsk, st->hcintmsk_copy.d32);
243
244 local_fiq_disable();
245 - mb();
246 + fiq_fsm_spin_lock(&hcd->fiq_state->lock);
247
248 if (hc->ep_type & 0x1) {
249 hfnum.d32 = DWC_READ_REG32(&hcd->core_if->host_if->host_global_regs->hfnum);
250 @@ -1909,7 +1928,7 @@ int fiq_fsm_queue_split_transaction(dwc_
251 st->hcchar_copy.b.chen = 1;
252 DWC_WRITE_REG32(&hc_regs->hcchar, st->hcchar_copy.d32);
253 }
254 - mb();
255 + fiq_fsm_spin_unlock(&hcd->fiq_state->lock);
256 local_fiq_enable();
257 return 0;
258 }
259 --- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
260 +++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c
261 @@ -101,6 +101,7 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_
262 if (dwc_otg_is_host_mode(core_if)) {
263 if (fiq_enable) {
264 local_fiq_disable();
265 + fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
266 /* Pull in from the FIQ's disabled mask */
267 gintmsk.d32 = gintmsk.d32 | ~(dwc_otg_hcd->fiq_state->gintmsk_saved.d32);
268 dwc_otg_hcd->fiq_state->gintmsk_saved.d32 = ~0;
269 @@ -116,8 +117,10 @@ int32_t dwc_otg_hcd_handle_intr(dwc_otg_
270 }
271 gintsts.d32 &= gintmsk.d32;
272
273 - if (fiq_enable)
274 + if (fiq_enable) {
275 + fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
276 local_fiq_enable();
277 + }
278
279 if (!gintsts.d32) {
280 goto exit_handler_routine;
281 @@ -200,6 +203,7 @@ exit_handler_routine:
282 gintmsk_data_t gintmsk_new;
283 haintmsk_data_t haintmsk_new;
284 local_fiq_disable();
285 + fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
286 gintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->gintmsk_saved.d32;
287 if(fiq_fsm_enable)
288 haintmsk_new.d32 = *(volatile uint32_t *)&dwc_otg_hcd->fiq_state->haintmsk_saved.d32;
289 @@ -222,6 +226,7 @@ exit_handler_routine:
290 haintmsk.d32 = DWC_READ_REG32(&core_if->host_if->host_global_regs->haintmsk);
291 /* Re-enable interrupts that the FIQ masked (first time round) */
292 FIQ_WRITE(dwc_otg_hcd->fiq_state->dwc_regs_base + GINTMSK, gintmsk.d32);
293 + fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
294 local_fiq_enable();
295
296 if ((jiffies / HZ) > last_time) {
297 @@ -633,8 +638,10 @@ int32_t dwc_otg_hcd_handle_hc_intr(dwc_o
298 {
299 /* check the mask? */
300 local_fiq_disable();
301 + fiq_fsm_spin_lock(&dwc_otg_hcd->fiq_state->lock);
302 haint.b2.chint |= ~(dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint);
303 dwc_otg_hcd->fiq_state->haintmsk_saved.b2.chint = ~0;
304 + fiq_fsm_spin_unlock(&dwc_otg_hcd->fiq_state->lock);
305 local_fiq_enable();
306 }
307