| 891 |
theseven |
1 |
#include "global.h"
|
|
|
2 |
#include "synopsysotg.h"
|
|
|
3 |
#include "usb.h"
|
|
|
4 |
#include "timer.h"
|
|
|
5 |
#include "util.h"
|
|
|
6 |
|
|
|
7 |
#ifndef SYNOPSYSOTG_AHB_BURST_LEN
|
|
|
8 |
#define SYNOPSYSOTG_AHB_BURST_LEN 5
|
|
|
9 |
#endif
|
|
|
10 |
#ifndef SYNOPSYSOTG_AHB_THRESHOLD
|
|
|
11 |
#define SYNOPSYSOTG_AHB_THRESHOLD 8
|
|
|
12 |
#endif
|
|
|
13 |
#ifndef SYNOPSYSOTG_TURNAROUND
|
|
|
14 |
#define SYNOPSYSOTG_TURNAROUND 3
|
|
|
15 |
#endif
|
|
|
16 |
|
|
|
17 |
static void synopsysotg_flush_out_endpoint(const struct usb_instance* instance, int ep)
|
|
|
18 |
{
|
|
|
19 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
20 |
if (data->core->outep_regs[ep].doepctl.b.epena)
|
|
|
21 |
{
|
|
|
22 |
// We are waiting for an OUT packet on this endpoint, which might arrive any moment.
|
|
|
23 |
// Assert a global output NAK to avoid race conditions while shutting down the endpoint.
|
|
|
24 |
synopsysotg_target_disable_irq(instance);
|
|
|
25 |
data->core->dregs.dctl.b.sgoutnak = 1;
|
|
|
26 |
while (!(data->core->gregs.gintsts.b.goutnakeff));
|
|
|
27 |
union synopsysotg_depctl doepctl = { .b = { .snak = 1, .epdis = 1 } };
|
|
|
28 |
data->core->outep_regs[ep].doepctl = doepctl;
|
|
|
29 |
while (!(data->core->outep_regs[ep].doepint.b.epdisabled));
|
|
|
30 |
data->core->dregs.dctl.b.cgoutnak = 1;
|
|
|
31 |
synopsysotg_target_enable_irq(instance);
|
|
|
32 |
}
|
|
|
33 |
data->core->outep_regs[ep].doepctl.b.usbactep = 0;
|
|
|
34 |
// Reset the transfer size register. Not strictly necessary, but can't hurt.
|
|
|
35 |
data->core->outep_regs[ep].doeptsiz.d32 = 0;
|
|
|
36 |
}
|
|
|
37 |
|
|
|
38 |
static void synopsysotg_flush_in_endpoint(const struct usb_instance* instance, int ep)
|
|
|
39 |
{
|
|
|
40 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
41 |
if (data->core->inep_regs[ep].diepctl.b.epena)
|
|
|
42 |
{
|
|
|
43 |
// We are shutting down an endpoint that might still have IN packets in the FIFO.
|
|
|
44 |
// Disable the endpoint, wait for things to settle, and flush the relevant FIFO.
|
|
|
45 |
synopsysotg_target_disable_irq(instance);
|
|
|
46 |
data->core->inep_regs[ep].diepctl.b.snak = 1;
|
|
|
47 |
while (!(data->core->inep_regs[ep].diepint.b.inepnakeff));
|
|
|
48 |
data->core->inep_regs[ep].diepctl.b.epdis = 1;
|
|
|
49 |
while (!(data->core->inep_regs[ep].diepint.b.epdisabled));
|
|
|
50 |
if (ep) data->core->inep_regs[ep].diepctl.b.usbactep = 0;
|
|
|
51 |
synopsysotg_target_enable_irq(instance);
|
|
|
52 |
// Wait for any DMA activity to stop, to make sure nobody will touch the FIFO.
|
|
|
53 |
while (!data->core->gregs.grstctl.b.ahbidle);
|
|
|
54 |
// Flush it all the way down!
|
|
|
55 |
union synopsysotg_grstctl grstctl = { .b = { .txfnum = data->core->inep_regs[ep].diepctl.b.txfnum, .txfflsh = 1 } };
|
|
|
56 |
data->core->gregs.grstctl = grstctl;
|
|
|
57 |
while (data->core->gregs.grstctl.b.txfflsh);
|
|
|
58 |
}
|
|
|
59 |
else if (ep) data->core->inep_regs[ep].diepctl.b.usbactep = 0;
|
|
|
60 |
// Reset the transfer size register. Not strictly necessary, but can't hurt.
|
|
|
61 |
data->core->inep_regs[ep].dieptsiz.d32 = 0;
|
|
|
62 |
}
|
|
|
63 |
|
|
|
64 |
static void synopsysotg_flush_ints(const struct usb_instance* instance)
|
|
|
65 |
{
|
|
|
66 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
67 |
int i;
|
|
|
68 |
for (i = 0; i < 16; i++)
|
|
|
69 |
{
|
|
|
70 |
data->core->outep_regs[i].doepint = data->core->outep_regs[i].doepint;
|
|
|
71 |
data->core->inep_regs[i].diepint = data->core->inep_regs[i].diepint;
|
|
|
72 |
}
|
|
|
73 |
data->core->gregs.gintsts = data->core->gregs.gintsts;
|
|
|
74 |
}
|
|
|
75 |
|
|
|
76 |
static void synopsysotg_try_push(const struct usb_instance* instance, int ep)
|
|
|
77 |
{
|
|
|
78 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
79 |
struct synopsysotg_state* state = (struct synopsysotg_state*)instance->driver_state;
|
|
|
80 |
union synopsysotg_depctl depctl = data->core->inep_regs[ep].diepctl;
|
|
|
81 |
if (!depctl.b.epena || !depctl.b.usbactep || depctl.b.stall || depctl.b.naksts) return;
|
|
|
82 |
int bytesleft = data->core->inep_regs[ep].dieptsiz.b.xfersize;
|
|
|
83 |
if (!bytesleft) return;
|
|
|
84 |
int maxpacket = ep ? data->core->inep_regs[ep].diepctl.b.mps : 64;
|
|
|
85 |
union synopsysotg_hnptxsts fifospace = data->core->gregs.hnptxsts;
|
|
|
86 |
int words = (MIN(maxpacket, bytesleft) + 3) >> 2;
|
|
|
87 |
if (fifospace.b.nptxqspcavail && fifospace.b.nptxfspcavail << 2 >= words)
|
|
|
88 |
while (words--) data->core->dfifo[ep][0] = *state->endpoints[ep].txaddr++;
|
|
|
89 |
if (!words && bytesleft <= maxpacket) return;
|
|
|
90 |
data->core->gregs.gintmsk.b.nptxfempty = true;
|
|
|
91 |
}
|
|
|
92 |
|
|
|
93 |
void synopsysotg_start_rx(const struct usb_instance* instance, union usb_endpoint_number ep, void* buf, int size)
|
|
|
94 |
{
|
|
|
95 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
96 |
struct synopsysotg_state* state = (struct synopsysotg_state*)instance->driver_state;
|
|
|
97 |
|
|
|
98 |
// Find the appropriate set of endpoint registers
|
|
|
99 |
volatile struct synopsysotg_outepregs* regs = &data->core->outep_regs[ep.number];
|
|
|
100 |
// Calculate number of packets (if size == 0 an empty packet will be sent)
|
|
|
101 |
int maxpacket = regs->doepctl.b.mps;
|
|
|
102 |
int packets = (size + maxpacket - 1) / maxpacket;
|
|
|
103 |
if (!packets) packets = 1;
|
|
|
104 |
|
|
|
105 |
// Set up data destination
|
|
|
106 |
if (data->use_dma) regs->doepdma = buf;
|
|
|
107 |
else state->endpoints[ep.number].rxaddr = (uint32_t*)buf;
|
|
|
108 |
union synopsysotg_depxfrsiz deptsiz = { .b = { .pktcnt = packets, .xfersize = size } };
|
|
|
109 |
regs->doeptsiz = deptsiz;
|
|
|
110 |
|
|
|
111 |
// Flush CPU cache if necessary
|
|
|
112 |
if (data->use_dma) invalidate_dcache(buf, size);
|
|
|
113 |
|
|
|
114 |
// Enable the endpoint
|
|
|
115 |
union synopsysotg_depctl depctl = regs->doepctl;
|
|
|
116 |
depctl.b.epena = 1;
|
|
|
117 |
depctl.b.cnak = 1;
|
|
|
118 |
regs->doepctl = depctl;
|
|
|
119 |
}
|
|
|
120 |
|
|
|
121 |
void synopsysotg_start_tx(const struct usb_instance* instance, union usb_endpoint_number ep, const void* buf, int size)
|
|
|
122 |
{
|
|
|
123 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
124 |
struct synopsysotg_state* state = (struct synopsysotg_state*)instance->driver_state;
|
|
|
125 |
|
|
|
126 |
// Find the appropriate set of endpoint registers
|
|
|
127 |
volatile struct synopsysotg_inepregs* regs = &data->core->inep_regs[ep.number];
|
|
|
128 |
// Calculate number of packets (if size == 0 an empty packet will be sent)
|
|
|
129 |
int maxpacket = regs->diepctl.b.mps;
|
|
|
130 |
int packets = (size + maxpacket - 1) / maxpacket;
|
|
|
131 |
if (!packets) packets = 1;
|
|
|
132 |
|
|
|
133 |
// Set up data source
|
|
|
134 |
if (data->use_dma) regs->diepdma = buf;
|
|
|
135 |
else state->endpoints[ep.number].txaddr = (uint32_t*)buf;
|
|
|
136 |
union synopsysotg_depxfrsiz deptsiz = { .b = { .pktcnt = packets, .xfersize = size } };
|
|
|
137 |
regs->dieptsiz = deptsiz;
|
|
|
138 |
|
|
|
139 |
// Flush CPU cache if necessary
|
|
|
140 |
if (data->use_dma) clean_dcache(buf, size);
|
|
|
141 |
|
|
|
142 |
// Enable the endpoint
|
|
|
143 |
union synopsysotg_depctl depctl = regs->diepctl;
|
|
|
144 |
depctl.b.epena = 1;
|
|
|
145 |
depctl.b.cnak = 1;
|
|
|
146 |
regs->diepctl = depctl;
|
|
|
147 |
|
|
|
148 |
// Start pushing data into the FIFO (must be done after enabling the endpoint)
|
|
|
149 |
if (size && !data->use_dma)
|
|
|
150 |
{
|
|
|
151 |
if (data->shared_txfifo) synopsysotg_try_push(instance, ep.number);
|
|
|
152 |
else data->core->dregs.diepempmsk.ep.in |= (1 << ep.number);
|
|
|
153 |
}
|
|
|
154 |
}
|
|
|
155 |
|
|
|
156 |
int synopsysotg_get_stall(const struct usb_instance* instance, union usb_endpoint_number ep)
|
|
|
157 |
{
|
|
|
158 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
159 |
if (ep.direction == USB_ENDPOINT_DIRECTION_IN)
|
|
|
160 |
return !!data->core->inep_regs[ep.number].diepctl.b.stall;
|
|
|
161 |
return !!data->core->outep_regs[ep.number].doepctl.b.stall;
|
|
|
162 |
}
|
|
|
163 |
|
|
|
164 |
void synopsysotg_set_stall(const struct usb_instance* instance, union usb_endpoint_number ep, int stall)
|
|
|
165 |
{
|
|
|
166 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
167 |
if (ep.direction == USB_ENDPOINT_DIRECTION_IN)
|
|
|
168 |
{
|
|
|
169 |
data->core->inep_regs[ep.number].diepctl.b.stall = !!stall;
|
|
|
170 |
if (!stall) data->core->inep_regs[ep.number].diepctl.b.setd0pid = true;
|
|
|
171 |
}
|
|
|
172 |
else
|
|
|
173 |
{
|
|
|
174 |
data->core->outep_regs[ep.number].doepctl.b.stall = !!stall;
|
|
|
175 |
if (!stall) data->core->outep_regs[ep.number].doepctl.b.setd0pid = true;
|
|
|
176 |
}
|
|
|
177 |
|
|
|
178 |
}
|
|
|
179 |
|
|
|
180 |
void synopsysotg_set_address(const struct usb_instance* instance, uint8_t address)
|
|
|
181 |
{
|
|
|
182 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
183 |
data->core->dregs.dcfg.b.devaddr = address;
|
|
|
184 |
}
|
|
|
185 |
|
|
|
186 |
void synopsysotg_unconfigure_ep(const struct usb_instance* instance, union usb_endpoint_number ep)
|
|
|
187 |
{
|
|
|
188 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
189 |
if (ep.direction == USB_ENDPOINT_DIRECTION_IN)
|
|
|
190 |
{
|
|
|
191 |
// Kill any outstanding IN transfers
|
|
|
192 |
synopsysotg_flush_in_endpoint(instance, ep.number);
|
|
|
193 |
// Mask interrupts for this endpoint
|
|
|
194 |
data->core->dregs.daintmsk.ep.in &= ~(1 << ep.number);
|
|
|
195 |
}
|
|
|
196 |
else
|
|
|
197 |
{
|
|
|
198 |
// Kill any outstanding OUT transfers
|
|
|
199 |
synopsysotg_flush_out_endpoint(instance, ep.number);
|
|
|
200 |
// Mask interrupts for this endpoint
|
|
|
201 |
data->core->dregs.daintmsk.ep.out &= ~(1 << ep.number);
|
|
|
202 |
}
|
|
|
203 |
}
|
|
|
204 |
|
|
|
205 |
void synopsysotg_configure_ep(const struct usb_instance* instance, union usb_endpoint_number ep,
|
|
|
206 |
enum usb_endpoint_type type, int maxpacket)
|
|
|
207 |
{
|
|
|
208 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
209 |
|
|
|
210 |
// Write the new configuration and unmask interrupts for the endpoint.
|
|
|
211 |
// Reset data toggle to DATA0, as required by the USB specification.
|
|
|
212 |
int txfifo = data->shared_txfifo ? 0 : ep.number;
|
|
|
213 |
union synopsysotg_depctl depctl = { .b = { .usbactep = 1, .eptype = type, .mps = maxpacket,
|
|
|
214 |
.txfnum = txfifo, .setd0pid = 1, .nextep = (ep.number + 1) & 0xf } };
|
|
|
215 |
if (ep.direction == USB_ENDPOINT_DIRECTION_IN)
|
|
|
216 |
{
|
|
|
217 |
data->core->inep_regs[ep.number].diepctl = depctl;
|
|
|
218 |
data->core->dregs.daintmsk.ep.in |= (1 << ep.number);
|
|
|
219 |
}
|
|
|
220 |
else
|
|
|
221 |
{
|
|
|
222 |
data->core->outep_regs[ep.number].doepctl = depctl;
|
|
|
223 |
data->core->dregs.daintmsk.ep.out |= (1 << ep.number);
|
|
|
224 |
}
|
|
|
225 |
}
|
|
|
226 |
|
|
|
227 |
void synopsysotg_ep0_start_rx(const struct usb_instance* instance, int non_setup)
|
|
|
228 |
{
|
|
|
229 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
230 |
struct synopsysotg_state* state = (struct synopsysotg_state*)instance->driver_state;
|
|
|
231 |
|
|
|
232 |
// Set up data destination
|
|
|
233 |
if (data->use_dma) data->core->outep_regs[0].doepdma = instance->buffer;
|
|
|
234 |
else state->endpoints[0].rxaddr = (uint32_t*)instance->buffer;
|
|
|
235 |
union synopsysotg_dep0xfrsiz deptsiz = { .b = { .supcnt = 3, .pktcnt = !!non_setup, .xfersize = 64 } };
|
|
|
236 |
data->core->outep_regs[0].doeptsiz.d32 = deptsiz.d32;
|
|
|
237 |
|
|
|
238 |
// Flush CPU cache if necessary
|
|
|
239 |
if (data->use_dma) invalidate_dcache(instance->buffer, sizeof(instance->buffer));
|
|
|
240 |
|
|
|
241 |
// Enable the endpoint
|
|
|
242 |
union synopsysotg_depctl depctl = data->core->outep_regs[0].doepctl;
|
|
|
243 |
depctl.b.epena = 1;
|
|
|
244 |
depctl.b.cnak = non_setup;
|
|
|
245 |
data->core->outep_regs[0].doepctl = depctl;
|
|
|
246 |
}
|
|
|
247 |
|
|
|
248 |
void synopsysotg_ep0_start_tx(const struct usb_instance* instance, const void* buf, int len)
|
|
|
249 |
{
|
|
|
250 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
251 |
struct synopsysotg_state* state = (struct synopsysotg_state*)instance->driver_state;
|
|
|
252 |
|
|
|
253 |
if (len)
|
|
|
254 |
{
|
|
|
255 |
// Set up data source
|
|
|
256 |
if (data->use_dma) data->core->inep_regs[0].diepdma = buf;
|
|
|
257 |
else state->endpoints[0].txaddr = buf;
|
|
|
258 |
union synopsysotg_dep0xfrsiz deptsiz = { .b = { .pktcnt = (len + 63) >> 6, .xfersize = len } };
|
|
|
259 |
data->core->inep_regs[0].dieptsiz.d32 = deptsiz.d32;
|
|
|
260 |
}
|
|
|
261 |
else
|
|
|
262 |
{
|
|
|
263 |
// Set up the IN pipe for a zero-length packet
|
|
|
264 |
union synopsysotg_dep0xfrsiz deptsiz = { .b = { .pktcnt = 1 } };
|
|
|
265 |
data->core->inep_regs[0].dieptsiz.d32 = deptsiz.d32;
|
|
|
266 |
}
|
|
|
267 |
|
|
|
268 |
// Flush CPU cache if necessary
|
|
|
269 |
if (data->use_dma) clean_dcache(buf, len);
|
|
|
270 |
|
|
|
271 |
// Enable the endpoint
|
|
|
272 |
union synopsysotg_depctl depctl = data->core->inep_regs[0].diepctl;
|
|
|
273 |
depctl.b.epena = 1;
|
|
|
274 |
depctl.b.cnak = 1;
|
|
|
275 |
data->core->inep_regs[0].diepctl = depctl;
|
|
|
276 |
|
|
|
277 |
// Start pushing data into the FIFO (must be done after enabling the endpoint)
|
|
|
278 |
if (len && !data->use_dma)
|
|
|
279 |
{
|
|
|
280 |
if (data->shared_txfifo) synopsysotg_try_push(instance, 0);
|
|
|
281 |
else data->core->dregs.diepempmsk.ep.in |= 1;
|
|
|
282 |
}
|
|
|
283 |
}
|
|
|
284 |
|
|
|
285 |
static void synopsysotg_ep0_init(const struct usb_instance* instance)
|
|
|
286 |
{
|
|
|
287 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
288 |
|
|
|
289 |
// Make sure both EP0 pipes are active.
|
|
|
290 |
// (The hardware should take care of that, but who knows...)
|
|
|
291 |
union synopsysotg_depctl depctl = { .b = { .usbactep = 1, .nextep = data->core->inep_regs[0].diepctl.b.nextep } };
|
|
|
292 |
data->core->outep_regs[0].doepctl = depctl;
|
|
|
293 |
data->core->inep_regs[0].diepctl = depctl;
|
|
|
294 |
}
|
|
|
295 |
|
|
|
296 |
void synopsysotg_irq(const struct usb_instance* instance)
|
|
|
297 |
{
|
|
|
298 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
299 |
struct synopsysotg_state* state = (struct synopsysotg_state*)instance->driver_state;
|
|
|
300 |
|
|
|
301 |
union synopsysotg_gintsts gintsts = data->core->gregs.gintsts;
|
|
|
302 |
|
|
|
303 |
if (gintsts.b.usbreset)
|
|
|
304 |
{
|
|
|
305 |
data->core->dregs.dcfg.b.devaddr = 0;
|
|
|
306 |
usb_handle_bus_reset(instance, 0);
|
|
|
307 |
}
|
|
|
308 |
|
|
|
309 |
if (gintsts.b.enumdone)
|
|
|
310 |
{
|
|
|
311 |
usb_handle_bus_reset(instance, data->core->dregs.dsts.b.enumspd == 0);
|
|
|
312 |
synopsysotg_ep0_init(instance);
|
|
|
313 |
}
|
|
|
314 |
|
|
|
315 |
if (gintsts.b.rxstsqlvl && !data->use_dma)
|
|
|
316 |
{
|
|
|
317 |
// Device to memory part of the "software DMA" implementation, used to receive data if use_dma == 0.
|
|
|
318 |
// Handle one packet at a time, the IRQ will re-trigger if there's something left.
|
|
|
319 |
union synopsysotg_grxfsts rxsts = data->core->gregs.grxstsp;
|
|
|
320 |
int ep = rxsts.b.chnum;
|
|
|
321 |
int words = (rxsts.b.bcnt + 3) >> 2;
|
|
|
322 |
while (words--) *state->endpoints[ep].rxaddr++ = data->core->dfifo[0][0];
|
|
|
323 |
}
|
|
|
324 |
|
|
|
325 |
if (gintsts.b.nptxfempty && data->core->gregs.gintmsk.b.nptxfempty)
|
|
|
326 |
{
|
|
|
327 |
// Old style, "shared TX FIFO" memory to device part of the "software DMA" implementation,
|
|
|
328 |
// used to send data if use_dma == 0 and the device doesn't support one non-periodic TX FIFO per endpoint.
|
|
|
329 |
|
|
|
330 |
// First disable the IRQ, it will be re-enabled later if there is anything left to be done.
|
|
|
331 |
data->core->gregs.gintmsk.b.nptxfempty = false;
|
|
|
332 |
|
|
|
333 |
// Check all endpoints for anything to be transmitted
|
|
|
334 |
int ep;
|
|
|
335 |
for (ep = 0; ep < 16; ep++) synopsysotg_try_push(instance, ep);
|
|
|
336 |
}
|
|
|
337 |
|
|
|
338 |
if (gintsts.b.inepintr)
|
|
|
339 |
{
|
|
|
340 |
union synopsysotg_daint daint = data->core->dregs.daint;
|
|
|
341 |
int ep;
|
|
|
342 |
for (ep = 0; ep < 16; ep++)
|
|
|
343 |
if (daint.ep.in & (1 << ep))
|
|
|
344 |
{
|
|
|
345 |
union synopsysotg_diepintn epints = data->core->inep_regs[ep].diepint;
|
|
|
346 |
if (epints.b.emptyintr)
|
|
|
347 |
{
|
|
|
348 |
// Memory to device part of the "software DMA" implementation, used to transmit data if use_dma == 0.
|
|
|
349 |
union synopsysotg_depxfrsiz deptsiz = data->core->inep_regs[ep].dieptsiz;
|
|
|
350 |
if (!deptsiz.b.xfersize) data->core->dregs.diepempmsk.ep.in &= ~(1 << ep);
|
|
|
351 |
else
|
|
|
352 |
{
|
|
|
353 |
// Push data into the TX FIFO until we don't have anything left or the FIFO would overflow.
|
|
|
354 |
int left = (deptsiz.b.xfersize + 3) >> 2;
|
|
|
355 |
while (left)
|
|
|
356 |
{
|
|
|
357 |
int words = data->core->inep_regs[ep].dtxfsts.b.txfspcavail;
|
|
|
358 |
if (words > left) words = left;
|
|
|
359 |
if (!words) break;
|
|
|
360 |
left -= words;
|
|
|
361 |
while (words--) data->core->dfifo[ep][0] = *state->endpoints[ep].txaddr++;
|
|
|
362 |
}
|
|
|
363 |
}
|
|
|
364 |
}
|
|
|
365 |
union usb_endpoint_number epnum = { .direction = USB_ENDPOINT_DIRECTION_IN, .number = ep };
|
|
|
366 |
int bytesleft = data->core->inep_regs[ep].dieptsiz.b.xfersize;
|
|
|
367 |
if (epints.b.timeout) usb_handle_timeout(instance, epnum, bytesleft);
|
|
|
368 |
if (epints.b.xfercompl) usb_handle_xfer_complete(instance, epnum, bytesleft);
|
|
|
369 |
data->core->inep_regs[ep].diepint = epints;
|
|
|
370 |
}
|
|
|
371 |
}
|
|
|
372 |
|
|
|
373 |
if (gintsts.b.outepintr)
|
|
|
374 |
{
|
|
|
375 |
union synopsysotg_daint daint = data->core->dregs.daint;
|
|
|
376 |
int ep;
|
|
|
377 |
for (ep = 0; ep < 16; ep++)
|
|
|
378 |
if (daint.ep.out & (1 << ep))
|
|
|
379 |
{
|
|
|
380 |
union synopsysotg_doepintn epints = data->core->outep_regs[ep].doepint;
|
|
|
381 |
union usb_endpoint_number epnum = { .direction = USB_ENDPOINT_DIRECTION_OUT, .number = ep };
|
|
|
382 |
if (epints.b.setup)
|
|
|
383 |
{
|
|
|
384 |
if (data->use_dma) invalidate_dcache(instance->buffer, sizeof(instance->buffer));
|
|
|
385 |
synopsysotg_flush_in_endpoint(instance, ep);
|
|
|
386 |
usb_handle_setup_received(instance, epnum);
|
|
|
387 |
}
|
|
|
388 |
else if (epints.b.xfercompl)
|
|
|
389 |
{
|
|
|
390 |
int bytesleft = data->core->inep_regs[ep].dieptsiz.b.xfersize;
|
|
|
391 |
usb_handle_xfer_complete(instance, epnum, bytesleft);
|
|
|
392 |
}
|
|
|
393 |
data->core->outep_regs[ep].doepint = epints;
|
|
|
394 |
}
|
|
|
395 |
}
|
|
|
396 |
|
|
|
397 |
data->core->gregs.gintsts = gintsts;
|
|
|
398 |
}
|
|
|
399 |
|
|
|
400 |
void synopsysotg_init(const struct usb_instance* instance)
|
|
|
401 |
{
|
|
|
402 |
int i;
|
|
|
403 |
|
|
|
404 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
405 |
|
|
|
406 |
// Disable IRQ during setup
|
|
|
407 |
synopsysotg_target_disable_irq(instance);
|
|
|
408 |
|
|
|
409 |
// Enable OTG clocks
|
|
|
410 |
synopsysotg_target_enable_clocks(instance);
|
|
|
411 |
|
|
|
412 |
// Enable PHY clocks
|
|
|
413 |
union synopsysotg_pcgcctl pcgcctl = { .b = {} };
|
|
|
414 |
data->core->pcgcctl = pcgcctl;
|
|
|
415 |
|
|
|
416 |
// Configure PHY type (must be done before reset)
|
|
|
417 |
union synopsysotg_gccfg gccfg = { .b = { .disablevbussensing = 1, .pwdn = 0 } };
|
|
|
418 |
data->core->gregs.gccfg = gccfg;
|
|
|
419 |
union synopsysotg_gusbcfg gusbcfg = { .b = { .force_dev = 1, .usbtrdtim = SYNOPSYSOTG_TURNAROUND } };
|
|
|
420 |
if (data->phy_16bit) gusbcfg.b.phyif = 1;
|
|
|
421 |
else if (data->phy_ulpi) gusbcfg.b.ulpi_utmi_sel = 1;
|
|
|
422 |
else gusbcfg.b.physel = 1;
|
|
|
423 |
data->core->gregs.gusbcfg = gusbcfg;
|
|
|
424 |
|
|
|
425 |
// Reset the whole USB core
|
|
|
426 |
union synopsysotg_grstctl grstctl = { .b = { .csftrst = 1 } };
|
|
|
427 |
udelay(100);
|
|
|
428 |
while (!data->core->gregs.grstctl.b.ahbidle);
|
|
|
429 |
data->core->gregs.grstctl = grstctl;
|
|
|
430 |
while (data->core->gregs.grstctl.b.csftrst);
|
|
|
431 |
while (!data->core->gregs.grstctl.b.ahbidle);
|
|
|
432 |
|
|
|
433 |
// Soft disconnect
|
|
|
434 |
union synopsysotg_dctl dctl = { .b = { .sftdiscon = 1 } };
|
|
|
435 |
data->core->dregs.dctl = dctl;
|
|
|
436 |
|
|
|
437 |
// Configure the core
|
|
|
438 |
union synopsysotg_gahbcfg gahbcfg = { .b = { .dmaenable = data->use_dma, .hburstlen = SYNOPSYSOTG_AHB_BURST_LEN, .glblintrmsk = 1 } };
|
|
|
439 |
if (data->disable_double_buffering)
|
|
|
440 |
{
|
|
|
441 |
gahbcfg.b.nptxfemplvl_txfemplvl = 1;
|
|
|
442 |
gahbcfg.b.ptxfemplvl = 1;
|
|
|
443 |
}
|
|
|
444 |
data->core->gregs.gahbcfg = gahbcfg;
|
|
|
445 |
data->core->gregs.gusbcfg = gusbcfg;
|
|
|
446 |
gccfg.b.pwdn = 1;
|
|
|
447 |
data->core->gregs.gccfg = gccfg;
|
|
|
448 |
union synopsysotg_dcfg dcfg = { .b = { .nzstsouthshk = 1 } };
|
|
|
449 |
data->core->dregs.dcfg = dcfg;
|
|
|
450 |
|
|
|
451 |
// Configure the FIFOs
|
|
|
452 |
if (data->use_dma)
|
|
|
453 |
{
|
|
|
454 |
union synopsysotg_dthrctl dthrctl = { .b = { .arb_park_en = 1, .rx_thr_en = 1, .iso_thr_en = 0, .non_iso_thr_en = 0,
|
|
|
455 |
.rx_thr_len = SYNOPSYSOTG_AHB_THRESHOLD } };
|
|
|
456 |
data->core->dregs.dthrctl = dthrctl;
|
|
|
457 |
}
|
|
|
458 |
int addr = data->fifosize;
|
|
|
459 |
for (i = 0; i < 16; i++)
|
|
|
460 |
{
|
|
|
461 |
int size = data->txfifosize[i];
|
|
|
462 |
addr -= size;
|
|
|
463 |
if (size)
|
|
|
464 |
{
|
|
|
465 |
data->core->inep_regs[i].diepctl.b.nextep = (i + 1) & 0xf;
|
|
|
466 |
union synopsysotg_txfsiz fsiz = { .b = { .startaddr = addr, .depth = size } };
|
|
|
467 |
if (!i) data->core->gregs.dieptxf0_hnptxfsiz = fsiz;
|
|
|
468 |
else data->core->gregs.dieptxf[i - 1] = fsiz;
|
|
|
469 |
}
|
|
|
470 |
}
|
|
|
471 |
union synopsysotg_rxfsiz fsiz = { .b = { .depth = addr } };
|
|
|
472 |
data->core->gregs.grxfsiz = fsiz;
|
|
|
473 |
|
|
|
474 |
// Set up interrupts
|
|
|
475 |
union synopsysotg_doepintn doepmsk = { .b = { .xfercompl = 1, .setup = 1 } };
|
|
|
476 |
data->core->dregs.doepmsk = doepmsk;
|
|
|
477 |
union synopsysotg_diepintn diepmsk = { .b = { .xfercompl = 1, .timeout = 1 } };
|
|
|
478 |
data->core->dregs.diepmsk = diepmsk;
|
|
|
479 |
data->core->dregs.diepempmsk.d32 = 0;
|
|
|
480 |
union synopsysotg_daint daintmsk = { .ep = { .in = 0b0000000000000001, .out = 0b0000000000000001 } };
|
|
|
481 |
data->core->dregs.daintmsk = daintmsk;
|
|
|
482 |
union synopsysotg_gintmsk gintmsk = { .b = { .usbreset = 1, .enumdone = 1, .outepintr = 1, .inepintr = 1 } };
|
|
|
483 |
if (!data->use_dma) gintmsk.b.rxstsqlvl = 1;
|
|
|
484 |
data->core->gregs.gintmsk = gintmsk;
|
|
|
485 |
synopsysotg_flush_ints(instance);
|
|
|
486 |
synopsysotg_target_clear_irq(instance);
|
|
|
487 |
synopsysotg_target_enable_irq(instance);
|
|
|
488 |
|
|
|
489 |
// Soft reconnect
|
|
|
490 |
dctl.b.sftdiscon = 0;
|
|
|
491 |
data->core->dregs.dctl = dctl;
|
|
|
492 |
}
|
|
|
493 |
|
|
|
494 |
void synopsysotg_exit(const struct usb_instance* instance)
|
|
|
495 |
{
|
|
|
496 |
const struct synopsysotg_config* data = (const struct synopsysotg_config*)instance->driver_config;
|
|
|
497 |
|
|
|
498 |
// Soft disconnect
|
|
|
499 |
union synopsysotg_dctl dctl = { .b = { .sftdiscon = 1 } };
|
|
|
500 |
data->core->dregs.dctl = dctl;
|
|
|
501 |
|
|
|
502 |
// Disable IRQs
|
|
|
503 |
synopsysotg_target_disable_irq(instance);
|
|
|
504 |
|
|
|
505 |
// Disable clocks
|
|
|
506 |
synopsysotg_target_disable_clocks(instance);
|
|
|
507 |
}
|
|
|
508 |
|
|
|
509 |
int synopsysotg_get_max_transfer_size(const struct usb_instance* data, union usb_endpoint_number ep)
|
|
|
510 |
{
|
|
|
511 |
return 512;
|
|
|
512 |
}
|
|
|
513 |
|
|
|
514 |
const struct usb_driver synopsysotg_driver =
|
|
|
515 |
{
|
|
|
516 |
.init = synopsysotg_init,
|
|
|
517 |
.exit = synopsysotg_exit,
|
|
|
518 |
.ep0_start_rx = synopsysotg_ep0_start_rx,
|
|
|
519 |
.ep0_start_tx = synopsysotg_ep0_start_tx,
|
|
|
520 |
.start_rx = synopsysotg_start_rx,
|
|
|
521 |
.start_tx = synopsysotg_start_tx,
|
|
|
522 |
.get_stall = synopsysotg_get_stall,
|
|
|
523 |
.set_stall = synopsysotg_set_stall,
|
|
|
524 |
.set_address = synopsysotg_set_address,
|
|
|
525 |
.configure_ep = synopsysotg_configure_ep,
|
|
|
526 |
.unconfigure_ep = synopsysotg_unconfigure_ep,
|
|
|
527 |
.get_max_transfer_size = synopsysotg_get_max_transfer_size,
|
|
|
528 |
};
|
|
|
529 |
|