1 /*!
2     \file    drv_usb_host.c
3     \brief   USB host mode low level driver
4 
5     \version 2020-08-04, V1.1.0, firmware for GD32VF103
6 */
7 
8 /*
9     Copyright (c) 2020, GigaDevice Semiconductor Inc.
10 
11     Redistribution and use in source and binary forms, with or without modification,
12 are permitted provided that the following conditions are met:
13 
14     1. Redistributions of source code must retain the above copyright notice, this
15        list of conditions and the following disclaimer.
16     2. Redistributions in binary form must reproduce the above copyright notice,
17        this list of conditions and the following disclaimer in the documentation
18        and/or other materials provided with the distribution.
19     3. Neither the name of the copyright holder nor the names of its contributors
20        may be used to endorse or promote products derived from this software without
21        specific prior written permission.
22 
23     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
25 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
26 IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
27 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
28 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
32 OF SUCH DAMAGE.
33 */
34 
35 #include "drv_usb_hw.h"
36 #include "drv_usb_core.h"
37 #include "drv_usb_host.h"
38 
39 const uint32_t PIPE_DPID[2] = {
40     PIPE_DPID_DATA0,
41     PIPE_DPID_DATA1
42 };
43 
44 /*!
45     \brief      initializes USB core for host mode
46     \param[in]  pudev: pointer to selected usb host
47     \param[out] none
48     \retval     operation status
49 */
usb_host_init(usb_core_driver * pudev)50 usb_status usb_host_init (usb_core_driver *pudev)
51 {
52     uint32_t i = 0U, inten = 0U;
53 
54     uint32_t nptxfifolen = 0U;
55     uint32_t ptxfifolen = 0U;
56 
57     /* restart the PHY Clock */
58     *pudev->regs.PWRCLKCTL = 0U;
59 
60     /* support FS/LS only */
61     pudev->regs.hr->HCTL &= ~HCTL_SPDFSLS;
62 
63     /* configure data FIFOs size */
64 #ifdef USB_FS_CORE
65     if (USB_CORE_ENUM_FS == pudev->bp.core_enum) {
66         /* set Rx FIFO size */
67         pudev->regs.gr->GRFLEN = USB_RX_FIFO_FS_SIZE;
68 
69         /* set non-periodic Tx FIFO size and address */
70         nptxfifolen |= USB_RX_FIFO_FS_SIZE;
71         nptxfifolen |= USB_HTX_NPFIFO_FS_SIZE << 16U;
72         pudev->regs.gr->DIEP0TFLEN_HNPTFLEN = nptxfifolen;
73 
74         /* set periodic Tx FIFO size and address */
75         ptxfifolen |= USB_RX_FIFO_FS_SIZE + USB_HTX_NPFIFO_FS_SIZE;
76         ptxfifolen |= USB_HTX_PFIFO_FS_SIZE << 16U;
77         pudev->regs.gr->HPTFLEN = ptxfifolen;
78     }
79 #endif /* USB_FS_CORE */
80 
81 #ifdef USE_OTG_MODE
82 
83     /* clear host set hnp enable in the usb_otg control register */
84     pudev->regs.gr->GOTGCS &= ~GOTGCS_HHNPEN;
85 
86 #endif /* USE_OTG_MODE */
87 
88     /* make sure the FIFOs are flushed */
89 
90     /* flush all Tx FIFOs in device or host mode */
91     usb_txfifo_flush (&pudev->regs, 0x10U);
92 
93     /* flush the entire Rx FIFO */
94     usb_rxfifo_flush (&pudev->regs);
95 
96     /* disable all interrupts */
97     pudev->regs.gr->GINTEN = 0U;
98 
99     /* clear any pending USB OTG interrupts */
100     pudev->regs.gr->GOTGINTF = 0xFFFFFFFFU;
101 
102     /* enable the USB wakeup and suspend interrupts */
103     pudev->regs.gr->GINTF = 0xBFFFFFFFU;
104 
105     /* clear all pending host channel interrupts */
106     for (i = 0U; i < pudev->bp.num_pipe; i++) {
107         pudev->regs.pr[i]->HCHINTF = 0xFFFFFFFFU;
108         pudev->regs.pr[i]->HCHINTEN = 0U;
109     }
110 
111 #ifndef USE_OTG_MODE
112     usb_portvbus_switch (pudev, 1U);
113 #endif /* USE_OTG_MODE */
114 
115     pudev->regs.gr->GINTEN = GINTEN_WKUPIE | GINTEN_SPIE;
116 
117     /* enable host_mode-related interrupts */
118     if (USB_USE_FIFO == pudev->bp.transfer_mode) {
119         inten = GINTEN_RXFNEIE;
120     }
121 
122     inten |= GINTEN_SESIE | GINTEN_HPIE | GINTEN_HCIE | GINTEN_ISOINCIE;
123 
124     pudev->regs.gr->GINTEN |= inten;
125 
126     inten = GINTEN_DISCIE | GINTEN_SOFIE;
127 
128     pudev->regs.gr->GINTEN &= ~inten;
129 
130     return USB_OK;
131 }
132 
133 /*!
134     \brief      control the VBUS to power
135     \param[in]  pudev: pointer to selected usb host
136     \param[in]  state: VBUS state
137     \param[out] none
138     \retval     none
139 */
usb_portvbus_switch(usb_core_driver * pudev,uint8_t state)140 void usb_portvbus_switch (usb_core_driver *pudev, uint8_t state)
141 {
142     uint32_t port = 0U;
143 
144     /* enable or disable the external charge pump */
145     usb_vbus_drive (state);
146 
147     /* turn on the host port power. */
148     port = usb_port_read (pudev);
149 
150     if (!(port & HPCS_PP) && (1U == state)) {
151         port |= HPCS_PP;
152     }
153 
154     if ((port & HPCS_PP) && (0U == state)) {
155         port &= ~HPCS_PP;
156     }
157 
158     *pudev->regs.HPCS = port;
159 
160     usb_mdelay (200U);
161 }
162 
163 /*!
164     \brief      reset host port
165     \param[in]  pudev: pointer to usb device
166     \param[out] none
167     \retval     operation status
168 */
usb_port_reset(usb_core_driver * pudev)169 uint32_t usb_port_reset (usb_core_driver *pudev)
170 {
171     __IO uint32_t port = usb_port_read (pudev);
172 
173     *pudev->regs.HPCS = port | HPCS_PRST;
174 
175     usb_mdelay(20U); /* see note */
176 
177     *pudev->regs.HPCS = port & ~HPCS_PRST;
178 
179     usb_mdelay(20U);
180 
181     return 1U;
182 }
183 
184 /*!
185     \brief      initialize host pipe
186     \param[in]  pudev: pointer to usb device
187     \param[in]  pipe_num: host pipe number which is in (0..7)
188     \param[out] none
189     \retval     operation status
190 */
usb_pipe_init(usb_core_driver * pudev,uint8_t pipe_num)191 usb_status usb_pipe_init (usb_core_driver *pudev, uint8_t pipe_num)
192 {
193     usb_status status = USB_OK;
194 
195     __IO uint32_t pp_ctl = 0U;
196     __IO uint32_t pp_inten = HCHINTEN_TFIE;
197 
198     usb_pipe *pp = &pudev->host.pipe[pipe_num];
199 
200     /* clear old interrupt conditions for this host channel */
201     pudev->regs.pr[pipe_num]->HCHINTF = 0xFFFFFFFFU;
202 
203     if (USB_USE_DMA == pudev->bp.transfer_mode) {
204         pp_inten |= HCHINTEN_DMAERIE;
205     }
206 
207     if (pp->ep.dir) {
208         pp_inten |= HCHINTEN_BBERIE;
209     }
210 
211     /* enable channel interrupts required for this transfer */
212     switch (pp->ep.type) {
213     case USB_EPTYPE_CTRL:
214     case USB_EPTYPE_BULK:
215         pp_inten |= HCHINTEN_STALLIE | HCHINTEN_USBERIE \
216                     | HCHINTEN_DTERIE | HCHINTEN_NAKIE;
217 
218         if (!pp->ep.dir) {
219             pp_inten |= HCHINTEN_NYETIE;
220 
221             if (pp->ping) {
222                 pp_inten |= HCHINTEN_ACKIE;
223             }
224         }
225         break;
226 
227     case USB_EPTYPE_INTR:
228         pp_inten |= HCHINTEN_STALLIE | HCHINTEN_USBERIE | HCHINTEN_DTERIE \
229                      | HCHINTEN_NAKIE | HCHINTEN_REQOVRIE;
230         break;
231 
232     case USB_EPTYPE_ISOC:
233         pp_inten |= HCHINTEN_REQOVRIE | HCHINTEN_ACKIE;
234 
235         if (pp->ep.dir) {
236             pp_inten |= HCHINTEN_USBERIE;
237         }
238         break;
239 
240     default:
241         break;
242     }
243 
244     pudev->regs.pr[pipe_num]->HCHINTEN = pp_inten;
245 
246     /* enable the top level host channel interrupt */
247     pudev->regs.hr->HACHINTEN |= 1U << pipe_num;
248 
249     /* make sure host channel interrupts are enabled */
250     pudev->regs.gr->GINTEN |= GINTEN_HCIE;
251 
252     /* program the host channel control register */
253     pp_ctl |= PIPE_CTL_DAR(pp->dev_addr);
254     pp_ctl |= PIPE_CTL_EPNUM(pp->ep.num);
255     pp_ctl |= PIPE_CTL_EPDIR(pp->ep.dir);
256     pp_ctl |= PIPE_CTL_EPTYPE(pp->ep.type);
257     pp_ctl |= PIPE_CTL_LSD(pp->dev_speed == PORT_SPEED_LOW);
258 
259     pp_ctl |= pp->ep.mps;
260     pp_ctl |= ((uint32_t)(pp->ep.type == USB_EPTYPE_INTR) << 29U) & HCHCTL_ODDFRM;
261 
262     pudev->regs.pr[pipe_num]->HCHCTL = pp_ctl;
263 
264     return status;
265 }
266 
267 /*!
268     \brief      prepare host channel for transferring packets
269     \param[in]  pudev: pointer to usb device
270     \param[in]  pipe_num: host pipe number which is in (0..7)
271     \param[out] none
272     \retval     operation status
273 */
usb_pipe_xfer(usb_core_driver * pudev,uint8_t pipe_num)274 usb_status usb_pipe_xfer (usb_core_driver *pudev, uint8_t pipe_num)
275 {
276     usb_status status = USB_OK;
277 
278     uint16_t dword_len = 0U;
279     uint16_t packet_count = 0U;
280 
281     __IO uint32_t pp_ctl = 0U;
282 
283     usb_pipe *pp = &pudev->host.pipe[pipe_num];
284 
285     uint16_t max_packet_len = pp->ep.mps;
286 
287     /* compute the expected number of packets associated to the transfer */
288     if (pp->xfer_len > 0U) {
289         packet_count = (uint16_t)((pp->xfer_len + max_packet_len - 1U) / max_packet_len);
290 
291         if (packet_count > HC_MAX_PACKET_COUNT) {
292             packet_count = HC_MAX_PACKET_COUNT;
293             pp->xfer_len = (uint16_t)(packet_count * max_packet_len);
294         }
295     } else {
296         packet_count = 1U;
297     }
298 
299     if (pp->ep.dir) {
300         pp->xfer_len = (uint16_t)(packet_count * max_packet_len);
301     }
302 
303     /* initialize the host channel transfer information */
304     pudev->regs.pr[pipe_num]->HCHLEN = pp->xfer_len | pp->DPID | PIPE_XFER_PCNT(packet_count);
305 
306     if (USB_USE_DMA == pudev->bp.transfer_mode) {
307         pudev->regs.pr[pipe_num]->HCHDMAADDR = (unsigned int)pp->xfer_buf;
308     }
309 
310     pp_ctl = pudev->regs.pr[pipe_num]->HCHCTL;
311 
312     if (usb_frame_even(pudev)) {
313         pp_ctl |= HCHCTL_ODDFRM;
314     } else {
315         pp_ctl &= ~HCHCTL_ODDFRM;
316     }
317 
318     /* set host channel enabled */
319     pp_ctl |= HCHCTL_CEN;
320     pp_ctl &= ~HCHCTL_CDIS;
321 
322     pudev->regs.pr[pipe_num]->HCHCTL = pp_ctl;
323 
324     if (USB_USE_FIFO == pudev->bp.transfer_mode) {
325         if ((0U == pp->ep.dir) && (pp->xfer_len > 0U)) {
326             switch (pp->ep.type) {
327             /* non-periodic transfer */
328             case USB_EPTYPE_CTRL:
329             case USB_EPTYPE_BULK:
330                 dword_len = (uint16_t)((pp->xfer_len + 3U) / 4U);
331 
332                 /* check if there is enough space in fifo space */
333                 if (dword_len > (pudev->regs.gr->HNPTFQSTAT & HNPTFQSTAT_NPTXFS)) {
334                     /* need to process data in nptxfempty interrupt */
335                     pudev->regs.gr->GINTEN |= GINTEN_NPTXFEIE;
336                 }
337                 break;
338 
339             /* periodic transfer */
340             case USB_EPTYPE_INTR:
341             case USB_EPTYPE_ISOC:
342                 dword_len = (uint16_t)((pp->xfer_len + 3U) / 4U);
343 
344                 /* check if there is enough space in fifo space */
345                 if (dword_len > (pudev->regs.hr->HPTFQSTAT & HPTFQSTAT_PTXFS)) {
346                     /* need to process data in ptxfempty interrupt */
347                     pudev->regs.gr->GINTEN |= GINTEN_PTXFEIE;
348                 }
349                 break;
350 
351             default:
352                 break;
353             }
354 
355             /* write packet into the tx fifo. */
356             usb_txfifo_write (&pudev->regs, pp->xfer_buf, pipe_num, (uint16_t)pp->xfer_len);
357         }
358     }
359 
360     return status;
361 }
362 
363 /*!
364     \brief      halt pipe
365     \param[in]  pudev: pointer to usb device
366     \param[in]  pipe_num: host pipe number which is in (0..7)
367     \param[out] none
368     \retval     operation status
369 */
usb_pipe_halt(usb_core_driver * pudev,uint8_t pipe_num)370 usb_status usb_pipe_halt (usb_core_driver *pudev, uint8_t pipe_num)
371 {
372     __IO uint32_t pp_ctl = pudev->regs.pr[pipe_num]->HCHCTL;
373 
374     uint8_t ep_type = (uint8_t)((pp_ctl & HCHCTL_EPTYPE) >> 18U);
375 
376     pp_ctl |= HCHCTL_CEN | HCHCTL_CDIS;
377 
378     switch (ep_type) {
379     case USB_EPTYPE_CTRL:
380     case USB_EPTYPE_BULK:
381         if (0U == (pudev->regs.gr->HNPTFQSTAT & HNPTFQSTAT_NPTXFS)) {
382             pp_ctl &= ~HCHCTL_CEN;
383         }
384         break;
385 
386     case USB_EPTYPE_INTR:
387     case USB_EPTYPE_ISOC:
388         if (0U == (pudev->regs.hr->HPTFQSTAT & HPTFQSTAT_PTXFS)) {
389             pp_ctl &= ~HCHCTL_CEN;
390         }
391         break;
392 
393     default:
394         break;
395     }
396 
397     pudev->regs.pr[pipe_num]->HCHCTL = pp_ctl;
398 
399     return USB_OK;
400 }
401 
402 /*!
403     \brief      configure host pipe to do ping operation
404     \param[in]  pudev: pointer to usb device
405     \param[in]  pipe_num: host pipe number which is in (0..7)
406     \param[out] none
407     \retval     operation status
408 */
usb_pipe_ping(usb_core_driver * pudev,uint8_t pipe_num)409 usb_status usb_pipe_ping (usb_core_driver *pudev, uint8_t pipe_num)
410 {
411     uint32_t pp_ctl = 0U;
412 
413     pudev->regs.pr[pipe_num]->HCHLEN = HCHLEN_PING | (HCHLEN_PCNT & (1U << 19U));
414 
415     pp_ctl = pudev->regs.pr[pipe_num]->HCHCTL;
416 
417     pp_ctl |= HCHCTL_CEN;
418     pp_ctl &= ~HCHCTL_CDIS;
419 
420     pudev->regs.pr[pipe_num]->HCHCTL = pp_ctl;
421 
422     return USB_OK;
423 }
424 
425 /*!
426     \brief      stop the USB host and clean up FIFO
427     \param[in]  pudev: pointer to usb device
428     \param[out] none
429     \retval     none
430 */
usb_host_stop(usb_core_driver * pudev)431 void usb_host_stop (usb_core_driver *pudev)
432 {
433     uint32_t i;
434     __IO uint32_t pp_ctl = 0U;
435 
436     pudev->regs.hr->HACHINTEN = 0x0U;
437     pudev->regs.hr->HACHINT = 0xFFFFFFFFU;
438 
439     /* flush out any leftover queued requests. */
440     for (i = 0U; i < pudev->bp.num_pipe; i++) {
441         pp_ctl = pudev->regs.pr[i]->HCHCTL;
442 
443         pp_ctl &= ~(HCHCTL_CEN | HCHCTL_EPDIR);
444         pp_ctl |= HCHCTL_CDIS;
445 
446         pudev->regs.pr[i]->HCHCTL = pp_ctl;
447     }
448 
449     /* flush the FIFO */
450     usb_rxfifo_flush (&pudev->regs);
451     usb_txfifo_flush (&pudev->regs, 0x10U);
452 }
453