1 /******************************************************************************
2 *
3 * Copyright (C) 2022-2023 Maxim Integrated Products, Inc. (now owned by
4 * Analog Devices, Inc.),
5 * Copyright (C) 2023-2024 Analog Devices, Inc.
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 ******************************************************************************/
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include "mxc_device.h"
25 #include "mxc_assert.h"
26 #include "mxc_errors.h"
27 #include "mxc_sys.h"
28 #include "usbhs_regs.h"
29 #include "usb.h"
30
31 #ifdef MAX32690
32 #include "fcr_regs.h"
33 static bool g_is_clock_locked = false;
34 #endif
35
36 #define USBHS_M31_CLOCK_RECOVERY
37
38 typedef enum {
39 SETUP_IDLE,
40 SETUP_NODATA,
41 SETUP_DATA_OUT,
42 SETUP_DATA_IN
43 } setup_phase_t;
44
45 /* storage for active endpoint data request objects */
46 static MXC_USB_Req_t *MXC_USB_Request[MXC_USBHS_NUM_EP];
47 /* endpoint sizes */
48 static unsigned int ep_size[MXC_USBHS_NUM_EP];
49 /* MUSBHSFC does not track SETUP in hardware, so instantiate state variable */
50 static setup_phase_t setup_phase = SETUP_IDLE;
51 /* Driver options passed in during MXC_USB_Init() */
52 static maxusb_cfg_options_t driver_opts;
53
get_fifo_ptr(unsigned int ep)54 static volatile uint8_t *get_fifo_ptr(unsigned int ep)
55 {
56 volatile uint32_t *ptr;
57
58 ptr = &MXC_USBHS->fifo0;
59 ptr += ep; /* Pointer math: multiplies ep by sizeof(uint32_t) */
60
61 return (volatile uint8_t *)ptr;
62 }
63
load_fifo(volatile uint8_t * fifoptr,uint8_t * dataptr,unsigned int len)64 static void load_fifo(volatile uint8_t *fifoptr, uint8_t *dataptr, unsigned int len)
65 {
66 volatile uint32_t *fifoptr_32 = (uint32_t *)fifoptr;
67 uint32_t *dataptr_32 = (uint32_t *)dataptr;
68 unsigned int len_32;
69
70 /* Calculate sizes to efficiently move data */
71 len_32 = len >> 2;
72 len &= 0x3;
73
74 /* Load word-sized chunks */
75 while (len_32--) {
76 *fifoptr_32 = *dataptr_32++;
77 }
78 dataptr = (uint8_t *)dataptr_32;
79 /* Load remainder as bytes */
80 while (len--) {
81 *fifoptr = *dataptr++;
82 }
83 }
84
unload_fifo(uint8_t * dataptr,volatile uint8_t * fifoptr,unsigned int len)85 static void unload_fifo(uint8_t *dataptr, volatile uint8_t *fifoptr, unsigned int len)
86 {
87 volatile uint32_t *fifoptr_32 = (uint32_t *)fifoptr;
88 uint32_t *dataptr_32 = (uint32_t *)dataptr;
89 unsigned int len_32;
90
91 /* Calculate sizes to efficiently move data */
92 len_32 = len >> 2;
93 len &= 0x3;
94
95 while (len_32--) {
96 *dataptr_32++ = *fifoptr_32;
97 }
98 dataptr = (uint8_t *)dataptr_32;
99 while (len--) {
100 *dataptr++ = *fifoptr;
101 }
102 }
103
MXC_USB_Init(maxusb_cfg_options_t * options)104 int MXC_USB_Init(maxusb_cfg_options_t *options)
105 {
106 int i;
107
108 /* Save the init options */
109 if (options) {
110 memcpy(&driver_opts, options, sizeof(maxusb_cfg_options_t));
111 } else {
112 driver_opts.enable_hs = 0;
113 }
114
115 /* Enable peripheral at the chip level */
116 if (driver_opts.init_callback) {
117 if (driver_opts.init_callback() != E_NO_ERROR) {
118 return -1;
119 }
120 }
121
122 /* Endpoint 0 is CONTROL, size fixed in hardware. Does not need configuration. */
123 ep_size[0] = 64;
124
125 /* Reset all other endpoints */
126 for (i = 1; i < MXC_USBHS_NUM_EP; i++) {
127 MXC_USB_ResetEp(i);
128 }
129
130 setup_phase = SETUP_IDLE;
131
132 /* Start out disconnected */
133 MXC_USBHS->power = 0;
134
135 /* Disable all interrupts */
136 MXC_USBHS->intrinen = 0;
137 MXC_USBHS->introuten = 0;
138 MXC_USBHS->intrusben = 0;
139
140 /* Unsuspend the MAC */
141 MXC_USBHS->mxm_suspend = 0;
142
143 /* Configure PHY */
144 #if 0
145 MXC_USBHS->xcfgi0 = (0x1 << 3) | (0x1 << 11);
146 MXC_USBHS->xcfgi1 = 0;
147 MXC_USBHS->xcfgi2 = 0x1 << (72-64);
148 MXC_USBHS->xcfgi3 = 0;
149 #endif
150 MXC_USBHS->m31_phy_xcfgi_31_0 = (0x1 << 3) | (0x1 << 11);
151 MXC_USBHS->m31_phy_xcfgi_63_32 = 0;
152 MXC_USBHS->m31_phy_xcfgi_95_64 = 0x1 << (72-64);
153 MXC_USBHS->m31_phy_xcfgi_127_96 = 0;
154
155
156 #ifdef USBHS_M31_CLOCK_RECOVERY
157 MXC_USBHS->m31_phy_noncry_rstb = 1;
158 MXC_USBHS->m31_phy_noncry_en = 1;
159 MXC_USBHS->m31_phy_outclksel = 0;
160 MXC_USBHS->m31_phy_coreclkin = 0;
161 MXC_USBHS->m31_phy_xtlsel = 2; /* Select 25 MHz clock */
162 #else
163 /* Use this option to feed the PHY a 30 MHz clock, which is them used as a PLL reference */
164 /* As it depends on the system core clock, this should probably be done at the SYS level */
165 MXC_USBHS->m31_phy_noncry_rstb = 0;
166 MXC_USBHS->m31_phy_noncry_en = 0;
167 MXC_USBHS->m31_phy_outclksel = 1;
168 MXC_USBHS->m31_phy_coreclkin = 1;
169 MXC_USBHS->m31_phy_xtlsel = 3; /* Select 30 MHz clock */
170 #endif
171 MXC_USBHS->m31_phy_pll_en = 1;
172 MXC_USBHS->m31_phy_oscouten = 1;
173
174 /* Reset PHY */
175 MXC_USBHS->m31_phy_ponrst = 0;
176 MXC_USBHS->m31_phy_ponrst = 1;
177
178 return 0;
179 }
180
181 #ifdef MAX32690
182
MXC_USB_LockClockSource(bool lock)183 int MXC_USB_LockClockSource(bool lock)
184 {
185 g_is_clock_locked = lock;
186 return E_NO_ERROR;
187 }
188
MXC_USB_SetClockSource(mxc_usb_clock_t clock_source)189 int MXC_USB_SetClockSource(mxc_usb_clock_t clock_source)
190 {
191 if (g_is_clock_locked) {
192 return E_BAD_STATE; // Clock source must be unlocked to set it.
193 }
194
195 // The USB peripheral's clock source is set in the FCR register bank.
196 // The actual clock source selected by each field value may vary between
197 // microcontrollers, so it is the responsibility of the implementer to define
198 // mxc_usb_clock_t correctly in the top-level "max32xxx.h" file. The enum values
199 // should match the field values when type-casted to an unsigned int.
200 if ((unsigned int)clock_source < 0 || (unsigned int)clock_source >= 3) {
201 return E_BAD_PARAM;
202 }
203
204 mxc_sys_system_clock_t current_sys_clk = (mxc_sys_system_clock_t)(MXC_GCR->clkctrl & MXC_F_GCR_CLKCTRL_SYSCLK_SEL);
205 if (current_sys_clk != MXC_SYS_CLOCK_IPO && clock_source == MXC_USB_CLOCK_SYS_DIV_10) {
206 return E_BAD_STATE; // System clock must be set to the IPO for the USB PHY to use the internal clock
207 }
208
209 MXC_SETFIELD(MXC_FCR->fctrl0, MXC_F_FCR_FCTRL0_USBCLKSEL, ((unsigned int)clock_source) << MXC_F_FCR_FCTRL0_USBCLKSEL_POS);
210
211 return E_NO_ERROR;
212 }
213
214 #endif
215
MXC_USB_Shutdown(void)216 int MXC_USB_Shutdown(void)
217 {
218 /* Disconnect and disable HS, too. */
219 MXC_USBHS->power = 0;
220
221 /* Disable all interrupts */
222 MXC_USBHS->intrinen = 0;
223 MXC_USBHS->introuten = 0;
224 MXC_USBHS->intrusben = 0;
225
226 /* Suspend the MAC */
227 MXC_USBHS->mxm_suspend = 3;
228
229 /* Shut down PHY */
230 MXC_USBHS->m31_phy_ponrst = 0;
231
232 /* Disable peripheral at chip level -- ignore failures */
233 if (driver_opts.shutdown_callback) {
234 driver_opts.shutdown_callback();
235 }
236
237 return 0;
238 }
239
MXC_USB_Connect(void)240 int MXC_USB_Connect(void)
241 {
242 /* Should high-speed negotiation be attempted? */
243 if (driver_opts.enable_hs) {
244 MXC_USBHS->power |= MXC_F_USBHS_POWER_HS_ENABLE;
245 } else {
246 MXC_USBHS->power &= ~MXC_F_USBHS_POWER_HS_ENABLE;
247 }
248
249 /* Connect to bus, if present */
250 MXC_USBHS->power |= MXC_F_USBHS_POWER_SOFTCONN;
251
252 setup_phase = SETUP_IDLE;
253
254 return 0;
255 }
256
MXC_USB_Disconnect(void)257 int MXC_USB_Disconnect(void)
258 {
259 /* Disconnect from bus */
260 MXC_USBHS->power &= ~MXC_F_USBHS_POWER_SOFTCONN;
261
262 setup_phase = SETUP_IDLE;
263
264 return 0;
265 }
266
MXC_USB_GetStatus(void)267 unsigned int MXC_USB_GetStatus(void)
268 {
269 int status = 0;
270
271 /* VBUS */
272 if (MXC_USBHS->mxm_reg_a4 & MXC_F_USBHS_MXM_REG_A4_VRST_VDDB_N_A) {
273 status |= MAXUSB_STATUS_VBUS_ON;
274 }
275
276 /* High-speed state */
277 if (MXC_USBHS->power & MXC_F_USBHS_POWER_HS_MODE) {
278 status |= MAXUSB_STATUS_HIGH_SPEED;
279 }
280
281 return status;
282 }
283
MXC_USB_ConfigEp(unsigned int ep,maxusb_ep_type_t type,unsigned int size)284 int MXC_USB_ConfigEp(unsigned int ep, maxusb_ep_type_t type, unsigned int size)
285 {
286 if (!ep || (ep >= MXC_USBHS_NUM_EP) || (size > MXC_USBHS_MAX_PACKET)) {
287 /* Can't configure this endpoint, invalid endpoint, or size too big */
288 return -1;
289 }
290
291 /* Default to disabled */
292 MXC_USB_ResetEp(ep);
293
294 /* Interrupts must be disabled while banked registers are accessed */
295 MXC_SYS_Crit_Enter();
296
297 /* Select register index for endpoint */
298 MXC_USBHS->index = ep;
299
300 switch (type) {
301 case MAXUSB_EP_TYPE_DISABLED:
302 break;
303 case MAXUSB_EP_TYPE_OUT:
304 MXC_USBHS->outcsrl = MXC_F_USBHS_OUTCSRL_CLRDATATOG;
305 MXC_USBHS->outcsru = MXC_F_USBHS_OUTCSRU_DPKTBUFDIS;
306 MXC_USBHS->outmaxp = size;
307 ep_size[ep] = size;
308 MXC_USBHS->introuten &= ~(1 << ep);
309 break;
310 case MAXUSB_EP_TYPE_IN:
311 MXC_USBHS->incsrl = MXC_F_USBHS_INCSRL_CLRDATATOG;
312 MXC_USBHS->incsru = MXC_F_USBHS_INCSRU_DPKTBUFDIS | MXC_F_USBHS_INCSRU_MODE;
313 MXC_USBHS->inmaxp = size;
314 ep_size[ep] = size;
315 MXC_USBHS->intrinen |= (1 << ep);
316 break;
317 default:
318 MXC_SYS_Crit_Exit();
319 return -1;
320 }
321
322 MXC_SYS_Crit_Exit();
323 return 0;
324 }
325
MXC_USB_IsConfigured(unsigned int ep)326 int MXC_USB_IsConfigured(unsigned int ep)
327 {
328 return !!(ep_size[ep]);
329 }
330
MXC_USB_Stall(unsigned int ep)331 int MXC_USB_Stall(unsigned int ep)
332 {
333 MXC_USB_Req_t *req;
334
335 if (!MXC_USB_IsConfigured(ep)) {
336 /* Can't stall an unconfigured endpoint */
337 return -1;
338 }
339
340 /* Interrupts must be disabled while banked registers are accessed */
341 MXC_SYS_Crit_Enter();
342
343 MXC_USBHS->index = ep;
344
345 if (ep == 0) {
346 MXC_USBHS->csr0 |= MXC_F_USBHS_CSR0_SERV_OUTPKTRDY | MXC_F_USBHS_CSR0_SEND_STALL;
347 setup_phase = SETUP_IDLE;
348 } else {
349 if (MXC_USBHS->incsru & MXC_F_USBHS_INCSRU_MODE) {
350 /* IN endpoint */
351 MXC_USBHS->incsrl |= MXC_F_USBHS_INCSRL_SENDSTALL;
352 } else {
353 /* Otherwise, must be OUT endpoint */
354 MXC_USBHS->outcsrl |= MXC_F_USBHS_OUTCSRL_SENDSTALL;
355 }
356 }
357
358 /* clear pending requests */
359 req = MXC_USB_Request[ep];
360 MXC_USB_Request[ep] = NULL;
361
362 if (req) {
363 /* complete pending requests with error */
364 req->error_code = -1;
365 if (req->callback) {
366 req->callback(req->cbdata);
367 }
368 }
369
370 MXC_SYS_Crit_Exit();
371 return 0;
372 }
373
MXC_USB_Unstall(unsigned int ep)374 int MXC_USB_Unstall(unsigned int ep)
375 {
376 if (!MXC_USB_IsConfigured(ep)) {
377 /* Can't unstall an unconfigured endpoint */
378 return -1;
379 }
380
381 /* Interrupts must be disabled while banked registers are accessed */
382 MXC_SYS_Crit_Enter();
383
384 MXC_USBHS->index = ep;
385
386 if (ep != 0) {
387 if (MXC_USBHS->incsru & MXC_F_USBHS_INCSRU_MODE) {
388 /* IN endpoint */
389 if (MXC_USBHS->incsrl & MXC_F_USBHS_INCSRL_INPKTRDY) {
390 /* Per musbhsfc_pg, only flush FIFO if IN packet loaded */
391 MXC_USBHS->incsrl = MXC_F_USBHS_INCSRL_CLRDATATOG | MXC_F_USBHS_INCSRL_FLUSHFIFO;
392 } else {
393 MXC_USBHS->incsrl = MXC_F_USBHS_INCSRL_CLRDATATOG;
394 }
395 } else {
396 /* Otherwise, must be OUT endpoint */
397 if (MXC_USBHS->outcsrl & MXC_F_USBHS_OUTCSRL_OUTPKTRDY) {
398 /* Per musbhsfc_pg, only flush FIFO if OUT packet is ready */
399 MXC_USBHS->outcsrl = MXC_F_USBHS_OUTCSRL_CLRDATATOG | MXC_F_USBHS_OUTCSRL_FLUSHFIFO;
400 } else {
401 MXC_USBHS->outcsrl = MXC_F_USBHS_OUTCSRL_CLRDATATOG;
402 }
403 }
404 }
405
406 MXC_SYS_Crit_Exit();
407 return 0;
408 }
409
MXC_USB_IsStalled(unsigned int ep)410 int MXC_USB_IsStalled(unsigned int ep)
411 {
412 unsigned int stalled;
413
414 MXC_USBHS->index = ep;
415
416 if (ep) {
417 if (MXC_USBHS->incsru & MXC_F_USBHS_INCSRU_MODE) {
418 /* IN endpoint */
419 stalled = !!(MXC_USBHS->incsrl & MXC_F_USBHS_INCSRL_SENDSTALL);
420 } else {
421 /* Otherwise, must be OUT endpoint */
422 stalled = !!(MXC_USBHS->outcsrl & MXC_F_USBHS_OUTCSRL_SENDSTALL);
423 }
424 } else {
425 /* Control (EP 0) */
426 stalled = !!(MXC_USBHS->csr0 & MXC_F_USBHS_CSR0_SEND_STALL);
427 }
428
429 return stalled;
430 }
431
MXC_USB_ResetEp(unsigned int ep)432 int MXC_USB_ResetEp(unsigned int ep)
433 {
434 MXC_USB_Req_t *req;
435
436 if (ep >= MXC_USBHS_NUM_EP) {
437 return -1;
438 }
439
440 /* Interrupts must be disabled while banked registers are accessed */
441 MXC_SYS_Crit_Enter();
442
443 /* clear pending requests */
444 req = MXC_USB_Request[ep];
445 MXC_USB_Request[ep] = NULL;
446
447 if (ep) {
448 ep_size[ep] = 0;
449
450 /* Select register index for endpoint */
451 MXC_USBHS->index = ep;
452
453 /* Default to disabled */
454 MXC_USBHS->intrinen &= ~(1 << ep);
455 MXC_USBHS->introuten &= ~(1 << ep);
456
457 if (MXC_USBHS->incsrl & MXC_F_USBHS_INCSRL_INPKTRDY) {
458 /* Per musbhsfc_pg, only flush FIFO if IN packet loaded */
459 MXC_USBHS->incsrl = MXC_F_USBHS_INCSRL_FLUSHFIFO;
460 }
461 MXC_USBHS->incsrl = MXC_F_USBHS_INCSRL_SENDSTALL;
462
463 MXC_USBHS->incsru = MXC_F_USBHS_INCSRU_DPKTBUFDIS;
464 MXC_USBHS->inmaxp = 0;
465
466 if (MXC_USBHS->outcsrl & MXC_F_USBHS_OUTCSRL_OUTPKTRDY) {
467 /* Per musbhsfc_pg, only flush FIFO if OUT packet is ready */
468 MXC_USBHS->outcsrl = MXC_F_USBHS_OUTCSRL_FLUSHFIFO;
469 }
470 MXC_USBHS->outcsrl = MXC_F_USBHS_OUTCSRL_SENDSTALL;
471
472 MXC_USBHS->outcsru = MXC_F_USBHS_OUTCSRU_DPKTBUFDIS;
473 MXC_USBHS->outmaxp = 0;
474
475 MXC_SYS_Crit_Exit();
476
477 /* We specifically do not complete SETUP callbacks, as this causes undesired SETUP status-stage STALLs */
478 if (req) {
479 /* complete pending requests with error */
480 req->error_code = -1;
481 if (req->callback) {
482 req->callback(req->cbdata);
483 }
484 }
485 } else {
486 MXC_SYS_Crit_Exit();
487 }
488
489 return 0;
490 }
491
MXC_USB_Ackstat(unsigned int ep)492 int MXC_USB_Ackstat(unsigned int ep)
493 {
494 uint32_t saved_index;
495
496 if (ep) {
497 /* Only valid for endpoint 0 */
498 return -1;
499 }
500
501 /* Interrupts must be disabled while banked registers are accessed */
502 MXC_SYS_Crit_Enter();
503
504 saved_index = MXC_USBHS->index;
505 MXC_USBHS->index = 0;
506
507 /* On this hardware, only setup transactions with no data stage need to be explicitly ACKed */
508 if (setup_phase == SETUP_NODATA) {
509 MXC_USBHS->csr0 |= MXC_F_USBHS_CSR0_SERV_OUTPKTRDY | MXC_F_USBHS_CSR0_DATA_END;
510 }
511
512 setup_phase = SETUP_IDLE;
513
514 MXC_USBHS->index = saved_index;
515
516 MXC_SYS_Crit_Exit();
517
518 return 0;
519 }
520
MXC_USB_DmaIsr(void)521 void MXC_USB_DmaIsr(void)
522 {
523 /* Not implemented */
524 }
525
526 /* sent packet done handler*/
event_in_data(uint32_t irqs)527 static void event_in_data(uint32_t irqs)
528 {
529 uint32_t ep, buffer_bit, data_left;
530 MXC_USB_Req_t *req;
531 unsigned int len;
532
533 /* Loop for each data endpoint */
534 for (ep = 0; ep < MXC_USBHS_NUM_EP; ep++) {
535 buffer_bit = (1 << ep);
536 if ((irqs & buffer_bit) == 0) { /* Not set, next Endpoint */
537 continue;
538 }
539
540 /* If an interrupt was received, but no request on this EP, ignore. */
541 if (!MXC_USB_Request[ep]) {
542 continue;
543 }
544
545 /* This function is called within interrupt context, so no need for a critical section */
546
547 MXC_USBHS->index = ep;
548
549 req = MXC_USB_Request[ep];
550 data_left = req->reqlen - req->actlen;
551
552 /* Check for more data left to transmit */
553 if (data_left) {
554 if (data_left >= ep_size[ep]) {
555 len = ep_size[ep];
556 } else {
557 len = data_left;
558 }
559
560 /* Fill FIFO with data */
561 load_fifo(get_fifo_ptr(ep), (req->data + req->actlen), len);
562 req->actlen += len;
563
564 if (!ep) {
565 if (MXC_USB_Request[ep]->actlen == MXC_USB_Request[ep]->reqlen) {
566 /* Implicit status-stage ACK, move state machine back to IDLE */
567 setup_phase = SETUP_IDLE;
568 MXC_USBHS->csr0 |= MXC_F_USBHS_CSR0_INPKTRDY | MXC_F_USBHS_CSR0_DATA_END;
569
570 /* free request */
571 MXC_USB_Request[ep] = NULL;
572
573 /* set done return value */
574 if (req->callback) {
575 req->callback(req->cbdata);
576 }
577 } else {
578 MXC_USBHS->csr0 |= MXC_F_USBHS_CSR0_INPKTRDY;
579 }
580 } else {
581 /* Arm for transmit to host */
582 MXC_USBHS->incsrl = MXC_F_USBHS_INCSRL_INPKTRDY;
583 }
584
585 } else {
586 /* all done sending data */
587
588 /* free request */
589 MXC_USB_Request[ep] = NULL;
590
591 /* set done return value */
592 if (req->callback) {
593 req->callback(req->cbdata);
594 }
595 }
596 }
597 }
598
599 /* received packet */
event_out_data(uint32_t irqs)600 static void event_out_data(uint32_t irqs)
601 {
602 uint32_t ep, buffer_bit, reqsize;
603 MXC_USB_Req_t *req;
604
605 /* Loop for each data endpoint */
606 for (ep = 0; ep < MXC_USBHS_NUM_EP; ep++) {
607 buffer_bit = (1 << ep);
608 if ((irqs & buffer_bit) == 0) {
609 continue;
610 }
611 /* If an interrupt was received, but no request on this EP, ignore. */
612 if (!MXC_USB_Request[ep]) {
613 continue;
614 }
615
616 req = MXC_USB_Request[ep];
617
618 /* This function is called within interrupt context, so no need for a critical section */
619
620 /* Select this endpoint for banked registers */
621 MXC_USBHS->index = ep;
622
623 if (!ep) {
624 if (!(MXC_USBHS->csr0 & MXC_F_USBHS_CSR0_OUTPKTRDY)) {
625 continue;
626 }
627 if (MXC_USBHS->count0 == 0) {
628 /* ZLP */
629 MXC_USB_Request[ep] = NULL;
630 /* Let the callback do the status stage */
631 /* call it done */
632 if (req->callback) {
633 req->callback(req->cbdata);
634 }
635 continue;
636 } else {
637 /* Write as much as we can to the request buffer */
638 reqsize = MXC_USBHS->count0;
639 if (reqsize > (req->reqlen - req->actlen)) {
640 reqsize = (req->reqlen - req->actlen);
641 }
642 }
643 } else {
644 if (!(MXC_USBHS->outcsrl & MXC_F_USBHS_OUTCSRL_OUTPKTRDY)) {
645 /* No packet on this endpoint? */
646 continue;
647 }
648 if (MXC_USBHS->outcount == 0) {
649 /* ZLP */
650 /* Clear out request */
651 MXC_USB_Request[ep] = NULL;
652
653 /* Signal to H/W that FIFO has been read */
654 MXC_USBHS->outcsrl &= ~MXC_F_USBHS_OUTCSRL_OUTPKTRDY;
655
656 /* Disable interrupt for this endpoint */
657 MXC_USBHS->introuten &= ~(1 << ep);
658
659 /* Complete request */
660 if (req->callback) {
661 req->callback(req->cbdata);
662 }
663 continue;
664
665 } else {
666 /* Write as much as we can to the request buffer */
667 reqsize = MXC_USBHS->outcount;
668 if (reqsize > (req->reqlen - req->actlen)) {
669 reqsize = (req->reqlen - req->actlen);
670 }
671 }
672 }
673
674 unload_fifo(&req->data[req->actlen], get_fifo_ptr(ep), reqsize);
675
676 req->actlen += reqsize;
677
678 if (!ep) {
679 if (req->actlen == req->reqlen) {
680 /* No more data */
681 MXC_USBHS->csr0 |= MXC_F_USBHS_CSR0_SERV_OUTPKTRDY | MXC_F_USBHS_CSR0_DATA_END;
682 /* Done */
683 MXC_USB_Request[ep] = NULL;
684
685 if (req->callback) {
686 req->callback(req->cbdata);
687 }
688 } else {
689 /* More data */
690 MXC_USBHS->csr0 |= MXC_F_USBHS_CSR0_SERV_OUTPKTRDY;
691 }
692 } else {
693 /* Signal to H/W that FIFO has been read */
694 MXC_USBHS->outcsrl &= ~MXC_F_USBHS_OUTCSRL_OUTPKTRDY;
695
696 if ((req->type == MAXUSB_TYPE_PKT) || (req->actlen == req->reqlen)) {
697 /* Done */
698 MXC_USB_Request[ep] = NULL;
699
700 /* Disable interrupt for this endpoint */
701 MXC_USBHS->introuten &= ~(1 << ep);
702
703 /* Complete request */
704 if (req->callback) {
705 req->callback(req->cbdata);
706 }
707 }
708 }
709 }
710 }
711
MXC_USB_IrqHandler(maxusb_usbio_events_t * evt)712 void MXC_USB_IrqHandler(maxusb_usbio_events_t *evt)
713 {
714 uint32_t saved_index;
715 uint32_t in_flags, out_flags, MXC_USB_flags, MXC_USB_mxm_flags;
716 int i, aborted = 0;
717 uint32_t intrusb, intrusben, intrin, intrinen, introut, introuten, mxm_int, mxm_int_en;
718
719 /* Save current index register */
720 saved_index = MXC_USBHS->index;
721
722 /* Note: Hardware clears these after read, so we must process them all or they are lost */
723 /* Order of volatile accesses must be separated for IAR */
724 intrusb = MXC_USBHS->intrusb;
725 intrusben = MXC_USBHS->intrusben;
726 MXC_USB_flags = intrusb & intrusben;
727
728 intrin = MXC_USBHS->intrin;
729 intrinen = MXC_USBHS->intrinen;
730 in_flags = intrin & intrinen;
731
732 introut = MXC_USBHS->introut;
733 introuten = MXC_USBHS->introuten;
734 out_flags = introut & introuten;
735
736 /* These USB interrupt flags are W1C. */
737 /* Order of volatile accesses must be separated for IAR */
738 mxm_int = MXC_USBHS->mxm_int;
739 mxm_int_en = MXC_USBHS->mxm_int_en;
740 MXC_USB_mxm_flags = mxm_int & mxm_int_en;
741 MXC_USBHS->mxm_int = MXC_USB_mxm_flags;
742
743 /* Map hardware-specific signals to generic stack events */
744 evt->dpact = !!(MXC_USB_flags & MXC_F_USBHS_INTRUSB_SOF_INT);
745 evt->rwudn = 0; /* Not supported by this hardware */
746 evt->bact = !!(MXC_USB_flags & MXC_F_USBHS_INTRUSB_SOF_INT);
747 evt->brst = !!(MXC_USB_flags & MXC_F_USBHS_INTRUSB_RESET_INT);
748 evt->susp = !!(MXC_USB_flags & MXC_F_USBHS_INTRUSB_SUSPEND_INT);
749 evt->novbus = !!(MXC_USB_mxm_flags & MXC_F_USBHS_MXM_INT_NOVBUS);
750 evt->vbus = !!(MXC_USB_mxm_flags & MXC_F_USBHS_MXM_INT_VBUS);
751 evt->brstdn = !!(MXC_USB_flags & MXC_F_USBHS_INTRUSB_RESET_INT); /* Hardware does not signal this, so simulate it */
752 evt->sudav = 0; /* Overwritten, if necessary, below */
753
754 /* Handle control state machine */
755 if (in_flags & MXC_F_USBHS_INTRIN_EP0_IN_INT) {
756 /* Select endpoint 0 */
757 MXC_USBHS->index = 0;
758 /* Process all error conditions */
759 if (MXC_USBHS->csr0 & MXC_F_USBHS_CSR0_SENT_STALL) {
760 /* Clear stall indication, go back to IDLE */
761 MXC_USBHS->csr0 &= ~(MXC_F_USBHS_CSR0_SENT_STALL);
762 /* Remove this from the IN flags so that it is not erroneously processed as data */
763 in_flags &= ~MXC_F_USBHS_INTRIN_EP0_IN_INT;
764 setup_phase = SETUP_IDLE;
765 aborted = 1;
766 }
767 if (MXC_USBHS->csr0 & MXC_F_USBHS_CSR0_SETUP_END) {
768 /* Abort pending requests, clear early end-of-control-transaction bit, go back to IDLE */
769 MXC_USBHS->csr0 |= (MXC_F_USBHS_CSR0_SERV_SETUP_END);
770 setup_phase = SETUP_IDLE;
771
772 /* Remove this from the IN flags so that it is not erroneously processed as data */
773 in_flags &= ~MXC_F_USBHS_INTRIN_EP0_IN_INT;
774 MXC_USB_ResetEp(0);
775 aborted = 1;
776 }
777 /* Now, check for a SETUP packet */
778 if (!aborted) {
779 if ((setup_phase == SETUP_IDLE) && (MXC_USBHS->csr0 & MXC_F_USBHS_CSR0_OUTPKTRDY)) {
780 /* Flag that we got a SETUP packet */
781 evt->sudav = 1;
782 /* Remove this from the IN flags so that it is not erroneously processed as data */
783 in_flags &= ~MXC_F_USBHS_INTRIN_EP0_IN_INT;
784 } else {
785 /* Otherwise, we are in endpoint 0 data IN/OUT */
786 /* Fix interrupt flags so that OUTs are processed properly */
787 if (setup_phase == SETUP_DATA_OUT) {
788 in_flags &= ~MXC_F_USBHS_INTRIN_EP0_IN_INT;
789 out_flags |= MXC_F_USBHS_INTRIN_EP0_IN_INT;
790 }
791 /* SETUP_NODATA is silently ignored by event_in_data() right now.. could fix this later */
792 }
793 }
794 }
795 /* do cleanup in cases of bus reset */
796 if (evt->brst) {
797 setup_phase = SETUP_IDLE;
798 /* kill any pending requests */
799 for (i = 0; i < MXC_USBHS_NUM_EP; i++) {
800 MXC_USB_ResetEp(i);
801 }
802 /* no need to process events after reset */
803 return;
804 }
805
806 if (in_flags) {
807 event_in_data(in_flags);
808 }
809
810 if (out_flags) {
811 event_out_data(out_flags);
812 }
813
814 /* Restore register index before exiting ISR */
815 MXC_USBHS->index = saved_index;
816 }
817
MXC_USB_IrqEnable(maxusb_event_t event)818 int MXC_USB_IrqEnable(maxusb_event_t event)
819 {
820 if (event >= MAXUSB_NUM_EVENTS) {
821 return -1;
822 }
823
824 switch (event) {
825 case MAXUSB_EVENT_BACT:
826 /* Bus Active */
827 MXC_USBHS->intrusben |= MXC_F_USBHS_INTRUSBEN_SOF_INT_EN;
828 break;
829
830 case MAXUSB_EVENT_BRST:
831 /* Bus Reset */
832 MXC_USBHS->intrusben |= MXC_F_USBHS_INTRUSBEN_RESET_INT_EN;
833 break;
834
835 case MAXUSB_EVENT_SUSP:
836 /* Suspend */
837 MXC_USBHS->power |= MXC_F_USBHS_POWER_EN_SUSPENDM;
838 MXC_USBHS->intrusben |= MXC_F_USBHS_INTRUSBEN_SUSPEND_INT_EN;
839 break;
840
841 case MAXUSB_EVENT_SUDAV:
842 /* Setup Data Available */
843 MXC_USBHS->intrinen |= MXC_F_USBHS_INTRINEN_EP0_INT_EN;
844 break;
845
846 case MAXUSB_EVENT_VBUS:
847 /* VBUS Detect */
848 MXC_USBHS->mxm_int_en |= MXC_F_USBHS_MXM_INT_EN_VBUS;
849 break;
850
851 case MAXUSB_EVENT_NOVBUS:
852 /* NOVBUS Detect */
853 MXC_USBHS->mxm_int_en |= MXC_F_USBHS_MXM_INT_EN_NOVBUS;
854 break;
855
856 default:
857 /* Other events not supported by this hardware */
858 break;
859 }
860
861 return 0;
862 }
863
MXC_USB_IrqDisable(maxusb_event_t event)864 int MXC_USB_IrqDisable(maxusb_event_t event)
865 {
866 if (event >= MAXUSB_NUM_EVENTS) {
867 return -1;
868 }
869
870 switch (event) {
871 case MAXUSB_EVENT_BACT:
872 /* Bus Active */
873 MXC_USBHS->intrusben &= ~MXC_F_USBHS_INTRUSBEN_SOF_INT_EN;
874 break;
875
876 case MAXUSB_EVENT_BRST:
877 /* Bus Reset */
878 MXC_USBHS->intrusben &= ~MXC_F_USBHS_INTRUSBEN_RESET_INT_EN;
879 break;
880
881 case MAXUSB_EVENT_SUSP:
882 /* Suspend */
883 MXC_USBHS->intrusben &= ~MXC_F_USBHS_INTRUSBEN_SUSPEND_INT_EN;
884 MXC_USBHS->power &= ~MXC_F_USBHS_POWER_EN_SUSPENDM;
885 break;
886
887 case MAXUSB_EVENT_SUDAV:
888 /* Setup Data Available */
889 MXC_USBHS->intrinen &= ~MXC_F_USBHS_INTRINEN_EP0_INT_EN;
890 break;
891
892 case MAXUSB_EVENT_VBUS:
893 /* VBUS Detect */
894 MXC_USBHS->mxm_int_en &= ~MXC_F_USBHS_MXM_INT_EN_VBUS;
895 break;
896
897 case MAXUSB_EVENT_NOVBUS:
898 /* NOVBUS Detect */
899 MXC_USBHS->mxm_int_en &= ~MXC_F_USBHS_MXM_INT_EN_NOVBUS;
900 break;
901
902 default:
903 /* Other events not supported by this hardware */
904 break;
905 }
906
907 return 0;
908 }
909
MXC_USB_IrqClear(maxusb_event_t event)910 int MXC_USB_IrqClear(maxusb_event_t event)
911 {
912 /* No-op on this hardware, as reading the interrupt flag register clears it */
913
914 return 0;
915 }
916
MXC_USB_GetSetup(MXC_USB_SetupPkt * sud)917 int MXC_USB_GetSetup(MXC_USB_SetupPkt *sud)
918 {
919 volatile uint8_t *fifoptr = (uint8_t *)&MXC_USBHS->fifo0;
920
921 /* Interrupts must be disabled while banked registers are accessed */
922 MXC_SYS_Crit_Enter();
923
924 /* Select endpoint 0 */
925 MXC_USBHS->index = 0;
926
927 if ((sud == NULL) || !(MXC_USBHS->csr0 & MXC_F_USBHS_CSR0_OUTPKTRDY)) {
928 MXC_SYS_Crit_Exit();
929 return -1;
930 }
931
932 /* Pull SETUP packet out of FIFO */
933 sud->bmRequestType = *fifoptr;
934 sud->bRequest = *fifoptr;
935 sud->wValue = *fifoptr;
936 sud->wValue += (*fifoptr) << 8;
937 sud->wIndex = *fifoptr;
938 sud->wIndex += (*fifoptr) << 8;
939 sud->wLength = *fifoptr;
940 sud->wLength += (*fifoptr) << 8;
941
942 /* Check for follow-on data and advance state machine */
943 if (sud->wLength > 0) {
944 #ifndef USE_ZEPHYR_USB_STACK
945 MXC_USBHS->csr0 |= MXC_F_USBHS_CSR0_SERV_OUTPKTRDY;
946 #endif
947 /* Determine if IN or OUT data follows */
948 if (sud->bmRequestType & RT_DEV_TO_HOST) {
949 setup_phase = SETUP_DATA_IN;
950 } else {
951 setup_phase = SETUP_DATA_OUT;
952 }
953 } else {
954 /* SERV_OUTPKTRDY is set using MXC_USB_Ackstat() */
955 setup_phase = SETUP_NODATA;
956 }
957
958 MXC_SYS_Crit_Exit();
959 return 0;
960 }
961
MXC_USB_SetFuncAddr(unsigned int addr)962 int MXC_USB_SetFuncAddr(unsigned int addr)
963 {
964 if (addr > 0x7f) {
965 return -1;
966 }
967
968 MXC_USBHS->faddr = addr;
969
970 return 0;
971 }
972
MXC_USB_GetRequest(unsigned int ep)973 MXC_USB_Req_t *MXC_USB_GetRequest(unsigned int ep)
974 {
975 return MXC_USB_Request[ep];
976 }
977
MXC_USB_RemoveRequest(MXC_USB_Req_t * req)978 int MXC_USB_RemoveRequest(MXC_USB_Req_t *req)
979 {
980 if (req->ep >= MXC_USBHS_NUM_EP) {
981 return -1;
982 }
983
984 if (MXC_USB_Request[req->ep] != req) {
985 return -1;
986 }
987
988 /* Delete request */
989 MXC_USB_Request[req->ep] = NULL;
990
991 /* complete pending request with error */
992 req->error_code = -1;
993 if (req->callback) {
994 req->callback(req->cbdata);
995 }
996
997 return 0;
998 }
999
MXC_USB_WriteEndpoint(MXC_USB_Req_t * req)1000 int MXC_USB_WriteEndpoint(MXC_USB_Req_t *req)
1001 {
1002 unsigned int ep = req->ep;
1003 unsigned int len = req->reqlen;
1004 unsigned int armed;
1005
1006 if (ep >= MXC_USBHS_NUM_EP) {
1007 return -1;
1008 }
1009
1010 /* EP must be enabled (configured) */
1011 if (!MXC_USB_IsConfigured(ep)) {
1012 return -1;
1013 }
1014
1015 /* Interrupts must be disabled while banked registers are accessed */
1016 MXC_SYS_Crit_Enter();
1017
1018 MXC_USBHS->index = ep;
1019
1020 /* if pending request; error */
1021 if (MXC_USB_Request[ep] || (MXC_USBHS->incsrl & MXC_F_USBHS_INCSRL_INPKTRDY)) {
1022 MXC_SYS_Crit_Exit();
1023 return -1;
1024 }
1025
1026 /* assign req object */
1027 MXC_USB_Request[ep] = req;
1028
1029 /* clear errors */
1030 req->error_code = 0;
1031
1032 /* Determine if DMA can be used for this transmit */
1033 armed = 0;
1034
1035 if (!armed) {
1036 /* EP0 or no free DMA channel found, fall back to PIO */
1037
1038 /* Determine how many bytes to be sent */
1039 if (len > ep_size[ep]) {
1040 len = ep_size[ep];
1041 }
1042 MXC_USB_Request[ep]->actlen = len;
1043
1044 load_fifo(get_fifo_ptr(ep), req->data, len);
1045
1046 if (!ep) {
1047 if (MXC_USB_Request[ep]->actlen == MXC_USB_Request[ep]->reqlen) {
1048 /* Implicit status-stage ACK, move state machine back to IDLE */
1049 setup_phase = SETUP_IDLE;
1050 MXC_USBHS->csr0 |= MXC_F_USBHS_CSR0_INPKTRDY | MXC_F_USBHS_CSR0_DATA_END;
1051 } else {
1052 MXC_USBHS->csr0 |= MXC_F_USBHS_CSR0_INPKTRDY;
1053 }
1054 } else {
1055 /* Arm for transmit to host */
1056 MXC_USBHS->incsrl = MXC_F_USBHS_INCSRL_INPKTRDY;
1057 }
1058 }
1059
1060 MXC_SYS_Crit_Exit();
1061 return 0;
1062 }
1063
MXC_USB_ReadEndpoint(MXC_USB_Req_t * req)1064 int MXC_USB_ReadEndpoint(MXC_USB_Req_t *req)
1065 {
1066 unsigned int ep = req->ep;
1067 uint32_t reqsize;
1068 unsigned int armed;
1069
1070 if (ep >= MXC_USBHS_NUM_EP) {
1071 return -1;
1072 }
1073
1074 /* Interrupts must be disabled while banked registers are accessed */
1075 MXC_SYS_Crit_Enter();
1076
1077 /* EP must be enabled (configured) and not stalled */
1078 if (!MXC_USB_IsConfigured(ep)) {
1079 MXC_SYS_Crit_Exit();
1080 return -1;
1081 }
1082
1083 if (MXC_USB_IsStalled(ep)) {
1084 MXC_SYS_Crit_Exit();
1085 return -1;
1086 }
1087
1088 /* if pending request; error */
1089 if (MXC_USB_Request[ep]) {
1090 MXC_SYS_Crit_Exit();
1091 return -1;
1092 }
1093
1094 /* clear errors */
1095 req->error_code = 0;
1096
1097 /* reset length */
1098 req->actlen = 0;
1099
1100 /* assign the req object */
1101 MXC_USB_Request[ep] = req;
1102
1103 /* Select endpoint */
1104 MXC_USBHS->index = ep;
1105
1106 /* Since the OUT interrupt for EP 0 doesn't really exist, only do this logic for other endpoints */
1107 if (ep) {
1108 armed = 0;
1109
1110 if (!armed) {
1111 /* EP0 or no free DMA channel found, fall back to PIO */
1112
1113 /* See if data already in FIFO for this EP */
1114 if (MXC_USBHS->outcsrl & MXC_F_USBHS_OUTCSRL_OUTPKTRDY) {
1115 reqsize = MXC_USBHS->outcount;
1116 if (reqsize > (req->reqlen - req->actlen)) {
1117 reqsize = (req->reqlen - req->actlen);
1118 }
1119
1120 unload_fifo(&req->data[req->actlen], get_fifo_ptr(ep), reqsize);
1121
1122 req->actlen += reqsize;
1123
1124 /* Signal to H/W that FIFO has been read */
1125 MXC_USBHS->outcsrl &= ~MXC_F_USBHS_OUTCSRL_OUTPKTRDY;
1126 if ((req->type == MAXUSB_TYPE_PKT) || (req->actlen == req->reqlen)) {
1127 /* Done with request, callback fires if configured */
1128 MXC_SYS_Crit_Exit();
1129 MXC_USB_Request[ep] = NULL;
1130
1131 if (req->callback) {
1132 req->callback(req->cbdata);
1133 }
1134 return 0;
1135 } else {
1136 /* Not done, more data requested */
1137 MXC_USBHS->introuten |= (1 << ep);
1138 }
1139 } else {
1140 /* No data, will need an interrupt to service later */
1141 MXC_USBHS->introuten |= (1 << ep);
1142 }
1143 }
1144 }
1145
1146 MXC_SYS_Crit_Exit();
1147 return 0;
1148 }
1149
MXC_USB_RemoteWakeup(void)1150 void MXC_USB_RemoteWakeup(void)
1151 {
1152 if (driver_opts.delay_us) {
1153 MXC_USBHS->power |= MXC_F_USBHS_POWER_RESUME;
1154 driver_opts.delay_us(10000);
1155 MXC_USBHS->power &= ~MXC_F_USBHS_POWER_RESUME;
1156 }
1157 }
1158
MXC_USB_TestMode(unsigned int value)1159 int MXC_USB_TestMode(unsigned int value)
1160 {
1161 const uint8_t test_packet[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1162 0x00, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
1163 0xAA, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE,
1164 0xEE, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1165 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F, 0xBF, 0xDF,
1166 0xEF, 0xF7, 0xFB, 0xFD, 0xFC, 0x7E, 0xBF, 0xDF,
1167 0xEF, 0xF7, 0xFB, 0xFD, 0x7E};
1168
1169 switch (value) {
1170 case 0x01:
1171 /* Test_J */
1172 MXC_USBHS->testmode = MXC_F_USBHS_TESTMODE_TEST_J;
1173 break;
1174 case 0x02:
1175 /* Test_K */
1176 MXC_USBHS->testmode = MXC_F_USBHS_TESTMODE_TEST_K;
1177 break;
1178 case 0x03:
1179 /* Test_SE0_NAK */
1180 MXC_USBHS->testmode = MXC_F_USBHS_TESTMODE_TEST_SE0_NAK;
1181 break;
1182 case 0x04:
1183 /* Test_Packet */
1184 /* Load EP 0 with data provided by section 11.4 of musbhsfc_pg.pdf */
1185 /* sizeof() considered safe, since we use uint8_t explicitly */
1186 load_fifo(get_fifo_ptr(0), (uint8_t*)test_packet, sizeof(test_packet));
1187 MXC_USBHS->csr0 |= MXC_F_USBHS_CSR0_INPKTRDY;
1188 MXC_USBHS->testmode = MXC_F_USBHS_TESTMODE_TEST_PKT;
1189 break;
1190 default:
1191 /* Unsupported */
1192 return -1;
1193 }
1194
1195 return 0;
1196 }
1197