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