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