]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_LPC1768_GCC_Rowley/LPCUSB/usbhw_lpc.c
Add FreeRTOS-Plus directory.
[freertos] / FreeRTOS / Demo / CORTEX_LPC1768_GCC_Rowley / LPCUSB / usbhw_lpc.c
1 /*\r
2         LPCUSB, an USB device driver for LPC microcontrollers\r
3         Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)\r
4 \r
5         Redistribution and use in source and binary forms, with or without\r
6         modification, are permitted provided that the following conditions are met:\r
7 \r
8         1. Redistributions of source code must retain the above copyright\r
9            notice, this list of conditions and the following disclaimer.\r
10         2. Redistributions in binary form must reproduce the above copyright\r
11            notice, this list of conditions and the following disclaimer in the\r
12            documentation and/or other materials provided with the distribution.\r
13         3. The name of the author may not be used to endorse or promote products\r
14            derived from this software without specific prior written permission.\r
15 \r
16         THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\r
17         IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\r
18         OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\r
19         IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\r
20         INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\r
21         NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\r
22         DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\r
23         THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\r
24         (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF\r
25         THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
26 */\r
27 \r
28 \r
29 /** @file\r
30         USB hardware layer\r
31  */\r
32 \r
33 \r
34 #include "usbdebug.h"\r
35 #include "usbhw_lpc.h"\r
36 #include "usbapi.h"\r
37 \r
38 /** Installed device interrupt handler */\r
39 static TFnDevIntHandler *_pfnDevIntHandler = NULL;\r
40 /** Installed endpoint interrupt handlers */\r
41 static TFnEPIntHandler  *_apfnEPIntHandlers[16];\r
42 /** Installed frame interrupt handlers */\r
43 static TFnFrameHandler  *_pfnFrameHandler = NULL;\r
44 \r
45 /** convert from endpoint address to endpoint index */\r
46 #define EP2IDX(bEP)     ((((bEP)&0xF)<<1)|(((bEP)&0x80)>>7))\r
47 /** convert from endpoint index to endpoint address */\r
48 #define IDX2EP(idx)     ((((idx)<<7)&0x80)|(((idx)>>1)&0xF))\r
49 \r
50 \r
51 \r
52 /**\r
53         Local function to wait for a device interrupt (and clear it)\r
54 \r
55         @param [in]     dwIntr          Bitmask of interrupts to wait for\r
56  */\r
57 static void Wait4DevInt(unsigned long dwIntr)\r
58 {\r
59         // wait for specific interrupt\r
60         while ((USB->USBDevIntSt & dwIntr) != dwIntr);\r
61         // clear the interrupt bits\r
62         USB->USBDevIntClr = dwIntr;\r
63 }\r
64 \r
65 \r
66 /**\r
67         Local function to send a command to the USB protocol engine\r
68 \r
69         @param [in]     bCmd            Command to send\r
70  */\r
71 static void USBHwCmd(unsigned char bCmd)\r
72 {\r
73         // clear CDFULL/CCEMTY\r
74         USB->USBDevIntClr = CDFULL | CCEMTY;\r
75         // write command code\r
76         USB->USBCmdCode = 0x00000500 | (bCmd << 16);\r
77         Wait4DevInt(CCEMTY);\r
78 }\r
79 \r
80 \r
81 /**\r
82         Local function to send a command + data to the USB protocol engine\r
83 \r
84         @param [in]     bCmd            Command to send\r
85         @param [in]     bData           Data to send\r
86  */\r
87 static void USBHwCmdWrite(unsigned char bCmd, unsigned short bData)\r
88 {\r
89         // write command code\r
90         USBHwCmd(bCmd);\r
91 \r
92         // write command data\r
93         USB->USBCmdCode = 0x00000100 | (bData << 16);\r
94         Wait4DevInt(CCEMTY);\r
95 }\r
96 \r
97 \r
98 /**\r
99         Local function to send a command to the USB protocol engine and read data\r
100 \r
101         @param [in]     bCmd            Command to send\r
102 \r
103         @return the data\r
104  */\r
105 static unsigned char USBHwCmdRead(unsigned char bCmd)\r
106 {\r
107         // write command code\r
108         USBHwCmd(bCmd);\r
109 \r
110         // get data\r
111         USB->USBCmdCode = 0x00000200 | (bCmd << 16);\r
112         Wait4DevInt(CDFULL);\r
113         return USB->USBCmdData;\r
114 }\r
115 \r
116 \r
117 /**\r
118         'Realizes' an endpoint, meaning that buffer space is reserved for\r
119         it. An endpoint needs to be realised before it can be used.\r
120 \r
121         From experiments, it appears that a USB reset causes USBReEP to\r
122         re-initialise to 3 (= just the control endpoints).\r
123         However, a USB bus reset does not disturb the USBMaxPSize settings.\r
124 \r
125         @param [in]     idx                     Endpoint index\r
126         @param [in] wMaxPSize   Maximum packet size for this endpoint\r
127  */\r
128 static void USBHwEPRealize(int idx, unsigned short wMaxPSize)\r
129 {\r
130         USB->USBReEP |= (1 << idx);\r
131         USB->USBEpInd = idx;\r
132         USB->USBMaxPSize = wMaxPSize;\r
133         Wait4DevInt(EP_RLZED);\r
134 }\r
135 \r
136 \r
137 /**\r
138         Enables or disables an endpoint\r
139 \r
140         @param [in]     idx             Endpoint index\r
141         @param [in]     fEnable TRUE to enable, FALSE to disable\r
142  */\r
143 static void USBHwEPEnable(int idx, BOOL fEnable)\r
144 {\r
145         USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fEnable ? 0 : EP_DA);\r
146 }\r
147 \r
148 \r
149 /**\r
150         Configures an endpoint and enables it\r
151 \r
152         @param [in]     bEP                             Endpoint number\r
153         @param [in]     wMaxPacketSize  Maximum packet size for this EP\r
154  */\r
155 void USBHwEPConfig(unsigned char bEP, unsigned short wMaxPacketSize)\r
156 {\r
157         int idx;\r
158 \r
159         idx = EP2IDX(bEP);\r
160 \r
161         // realise EP\r
162         USBHwEPRealize(idx, wMaxPacketSize);\r
163 \r
164         // enable EP\r
165         USBHwEPEnable(idx, TRUE);\r
166 }\r
167 \r
168 \r
169 /**\r
170         Registers an endpoint event callback\r
171 \r
172         @param [in]     bEP                             Endpoint number\r
173         @param [in]     pfnHandler              Callback function\r
174  */\r
175 void USBHwRegisterEPIntHandler(unsigned char bEP, TFnEPIntHandler *pfnHandler)\r
176 {\r
177         int idx;\r
178 \r
179         idx = EP2IDX(bEP);\r
180 \r
181         ASSERT(idx<32);\r
182 \r
183         /* add handler to list of EP handlers */\r
184         _apfnEPIntHandlers[idx / 2] = pfnHandler;\r
185 \r
186         /* enable EP interrupt */\r
187         USB->USBEpIntEn |= (1 << idx);\r
188         USB->USBDevIntEn |= EP_SLOW;\r
189 \r
190         DBG("Registered handler for EP 0x%x\n", bEP);\r
191 }\r
192 \r
193 \r
194 /**\r
195         Registers an device status callback\r
196 \r
197         @param [in]     pfnHandler      Callback function\r
198  */\r
199 void USBHwRegisterDevIntHandler(TFnDevIntHandler *pfnHandler)\r
200 {\r
201         _pfnDevIntHandler = pfnHandler;\r
202 \r
203         // enable device interrupt\r
204         USB->USBDevIntEn |= DEV_STAT;\r
205 \r
206         DBG("Registered handler for device status\n");\r
207 }\r
208 \r
209 \r
210 /**\r
211         Registers the frame callback\r
212 \r
213         @param [in]     pfnHandler      Callback function\r
214  */\r
215 void USBHwRegisterFrameHandler(TFnFrameHandler *pfnHandler)\r
216 {\r
217         _pfnFrameHandler = pfnHandler;\r
218 \r
219         // enable device interrupt\r
220         USB->USBDevIntEn |= FRAME;\r
221 \r
222         DBG("Registered handler for frame\n");\r
223 }\r
224 \r
225 \r
226 /**\r
227         Sets the USB address.\r
228 \r
229         @param [in]     bAddr           Device address to set\r
230  */\r
231 void USBHwSetAddress(unsigned char bAddr)\r
232 {\r
233         USBHwCmdWrite(CMD_DEV_SET_ADDRESS, DEV_EN | bAddr);\r
234 }\r
235 \r
236 \r
237 /**\r
238         Connects or disconnects from the USB bus\r
239 \r
240         @param [in]     fConnect        If TRUE, connect, otherwise disconnect\r
241  */\r
242 void USBHwConnect(BOOL fConnect)\r
243 {\r
244         USBHwCmdWrite(CMD_DEV_STATUS, fConnect ? CON : 0);\r
245 }\r
246 \r
247 \r
248 /**\r
249         Enables interrupt on NAK condition\r
250 \r
251         For IN endpoints a NAK is generated when the host wants to read data\r
252         from the device, but none is available in the endpoint buffer.\r
253         For OUT endpoints a NAK is generated when the host wants to write data\r
254         to the device, but the endpoint buffer is still full.\r
255 \r
256         The endpoint interrupt handlers can distinguish regular (ACK) interrupts\r
257         from NAK interrupt by checking the bits in their bEPStatus argument.\r
258 \r
259         @param [in]     bIntBits        Bitmap indicating which NAK interrupts to enable\r
260  */\r
261 void USBHwNakIntEnable(unsigned char bIntBits)\r
262 {\r
263         USBHwCmdWrite(CMD_DEV_SET_MODE, bIntBits);\r
264 }\r
265 \r
266 \r
267 /**\r
268         Gets the status from a specific endpoint.\r
269 \r
270         @param [in]     bEP             Endpoint number\r
271         @return Endpoint status byte (containing EP_STATUS_xxx bits)\r
272  */\r
273 unsigned char   USBHwEPGetStatus(unsigned char bEP)\r
274 {\r
275         int idx = EP2IDX(bEP);\r
276 \r
277         return USBHwCmdRead(CMD_EP_SELECT | idx);\r
278 }\r
279 \r
280 \r
281 /**\r
282         Sets the stalled property of an endpoint\r
283 \r
284         @param [in]     bEP             Endpoint number\r
285         @param [in]     fStall  TRUE to stall, FALSE to unstall\r
286  */\r
287 void USBHwEPStall(unsigned char bEP, BOOL fStall)\r
288 {\r
289         int idx = EP2IDX(bEP);\r
290 \r
291         USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fStall ? EP_ST : 0);\r
292 }\r
293 \r
294 \r
295 /**\r
296         Writes data to an endpoint buffer\r
297 \r
298         @param [in]     bEP             Endpoint number\r
299         @param [in]     pbBuf   Endpoint data\r
300         @param [in]     iLen    Number of bytes to write\r
301 \r
302         @return TRUE if the data was successfully written or <0 in case of error.\r
303 */\r
304 int USBHwEPWrite(unsigned char bEP, unsigned char *pbBuf, int iLen)\r
305 {\r
306         int idx;\r
307 \r
308         idx = EP2IDX(bEP);\r
309 \r
310         // set write enable for specific endpoint\r
311         USB->USBCtrl = WR_EN | ((bEP & 0xF) << 2);\r
312 \r
313         // set packet length\r
314         USB->USBTxPLen = iLen;\r
315 \r
316         // write data\r
317         while (USB->USBCtrl & WR_EN) {\r
318                 USB->USBTxData = (pbBuf[3] << 24) | (pbBuf[2] << 16) | (pbBuf[1] << 8) | pbBuf[0];\r
319                 pbBuf += 4;\r
320         }\r
321 \r
322         // select endpoint and validate buffer\r
323         USBHwCmd(CMD_EP_SELECT | idx);\r
324         USBHwCmd(CMD_EP_VALIDATE_BUFFER);\r
325 \r
326         return iLen;\r
327 }\r
328 \r
329 \r
330 /**\r
331         Reads data from an endpoint buffer\r
332 \r
333         @param [in]     bEP             Endpoint number\r
334         @param [in]     pbBuf   Endpoint data\r
335         @param [in]     iMaxLen Maximum number of bytes to read\r
336 \r
337         @return the number of bytes available in the EP (possibly more than iMaxLen),\r
338         or <0 in case of error.\r
339  */\r
340 int USBHwEPRead(unsigned char bEP, unsigned char *pbBuf, int iMaxLen)\r
341 {\r
342         unsigned int i, idx;\r
343         unsigned long   dwData, dwLen;\r
344 \r
345         idx = EP2IDX(bEP);\r
346 \r
347         // set read enable bit for specific endpoint\r
348         USB->USBCtrl = RD_EN | ((bEP & 0xF) << 2);\r
349 \r
350         // wait for PKT_RDY\r
351         do {\r
352                 dwLen = USB->USBRxPLen;\r
353         } while ((dwLen & PKT_RDY) == 0);\r
354 \r
355         // packet valid?\r
356         if ((dwLen & DV) == 0) {\r
357                 return -1;\r
358         }\r
359 \r
360         // get length\r
361         dwLen &= PKT_LNGTH_MASK;\r
362 \r
363         // get data\r
364         dwData = 0;\r
365         for (i = 0; i < dwLen; i++) {\r
366                 if ((i % 4) == 0) {\r
367                         dwData = USB->USBRxData;\r
368                 }\r
369                 if ((pbBuf != NULL) && ((int)i < iMaxLen)) {\r
370                         pbBuf[i] = dwData & 0xFF;\r
371                 }\r
372                 dwData >>= 8;\r
373         }\r
374 \r
375         // make sure RD_EN is clear\r
376         USB->USBCtrl = 0;\r
377 \r
378         // select endpoint and clear buffer\r
379         USBHwCmd(CMD_EP_SELECT | idx);\r
380         USBHwCmd(CMD_EP_CLEAR_BUFFER);\r
381 \r
382         return dwLen;\r
383 }\r
384 \r
385 \r
386 /**\r
387         Sets the 'configured' state.\r
388 \r
389         All registered endpoints are 'realised' and enabled, and the\r
390         'configured' bit is set in the device status register.\r
391 \r
392         @param [in]     fConfigured     If TRUE, configure device, else unconfigure\r
393  */\r
394 void USBHwConfigDevice(BOOL fConfigured)\r
395 {\r
396         // set configured bit\r
397         USBHwCmdWrite(CMD_DEV_CONFIG, fConfigured ? CONF_DEVICE : 0);\r
398 }\r
399 \r
400 \r
401 /**\r
402         USB interrupt handler\r
403 \r
404         @todo Get all 11 bits of frame number instead of just 8\r
405 \r
406         Endpoint interrupts are mapped to the slow interrupt\r
407  */\r
408 void USBHwISR(void)\r
409 {\r
410         unsigned long   dwStatus;\r
411         unsigned long dwIntBit;\r
412         unsigned char   bEPStat, bDevStat, bStat;\r
413         int i;\r
414         unsigned short  wFrame;\r
415 \r
416         // handle device interrupts\r
417         dwStatus = USB->USBDevIntSt;\r
418 \r
419         // frame interrupt\r
420         if (dwStatus & FRAME) {\r
421                 // clear int\r
422                 USB->USBDevIntClr = FRAME;\r
423                 // call handler\r
424                 if (_pfnFrameHandler != NULL) {\r
425                         wFrame = USBHwCmdRead(CMD_DEV_READ_CUR_FRAME_NR);\r
426                         _pfnFrameHandler(wFrame);\r
427                 }\r
428         }\r
429 \r
430         // device status interrupt\r
431         if (dwStatus & DEV_STAT) {\r
432                 /*      Clear DEV_STAT interrupt before reading DEV_STAT register.\r
433                         This prevents corrupted device status reads, see\r
434                         LPC2148 User manual revision 2, 25 july 2006.\r
435                 */\r
436                 USB->USBDevIntClr = DEV_STAT;\r
437                 bDevStat = USBHwCmdRead(CMD_DEV_STATUS);\r
438                 if (bDevStat & (CON_CH | SUS_CH | RST)) {\r
439                         // convert device status into something HW independent\r
440                         bStat = ((bDevStat & CON) ? DEV_STATUS_CONNECT : 0) |\r
441                                         ((bDevStat & SUS) ? DEV_STATUS_SUSPEND : 0) |\r
442                                         ((bDevStat & RST) ? DEV_STATUS_RESET : 0);\r
443                         // call handler\r
444                         if (_pfnDevIntHandler != NULL) {\r
445                                 _pfnDevIntHandler(bStat);\r
446                         }\r
447                 }\r
448         }\r
449 \r
450         // endpoint interrupt\r
451         if (dwStatus & EP_SLOW) {\r
452                 // clear EP_SLOW\r
453                 USB->USBDevIntClr = EP_SLOW;\r
454                 // check all endpoints\r
455                 for (i = 0; i < 32; i++) {\r
456                         dwIntBit = (1 << i);\r
457                         if (USB->USBEpIntSt & dwIntBit) {\r
458                                 // clear int (and retrieve status)\r
459                                 USB->USBEpIntClr = dwIntBit;\r
460                                 Wait4DevInt(CDFULL);\r
461                                 bEPStat = USB->USBCmdData;\r
462                                 // convert EP pipe stat into something HW independent\r
463                                 bStat = ((bEPStat & EPSTAT_FE) ? EP_STATUS_DATA : 0) |\r
464                                                 ((bEPStat & EPSTAT_ST) ? EP_STATUS_STALLED : 0) |\r
465                                                 ((bEPStat & EPSTAT_STP) ? EP_STATUS_SETUP : 0) |\r
466                                                 ((bEPStat & EPSTAT_EPN) ? EP_STATUS_NACKED : 0) |\r
467                                                 ((bEPStat & EPSTAT_PO) ? EP_STATUS_ERROR : 0);\r
468                                 // call handler\r
469                                 if (_apfnEPIntHandlers[i / 2] != NULL) {\r
470                                         _apfnEPIntHandlers[i / 2](IDX2EP(i), bStat);\r
471                                 }\r
472                         }\r
473                 }\r
474         }\r
475 }\r
476 \r
477 \r
478 \r
479 /**\r
480         Initialises the USB hardware\r
481 \r
482 \r
483         @return TRUE if the hardware was successfully initialised\r
484  */\r
485 BOOL USBHwInit(void)\r
486 {\r
487         // P2.9 -> USB_CONNECT\r
488         PINCON->PINSEL4 &= ~0x000C0000;\r
489         PINCON->PINSEL4 |= 0x00040000;\r
490 \r
491         // P1.18 -> USB_UP_LED\r
492         // P1.30 -> VBUS\r
493         PINCON->PINSEL3 &= ~0x30000030;\r
494         PINCON->PINSEL3 |= 0x20000010;\r
495 \r
496         // P0.29 -> USB_D+\r
497         // P0.30 -> USB_D-\r
498         PINCON->PINSEL1 &= ~0x3C000000;\r
499         PINCON->PINSEL1 |= 0x14000000;\r
500 \r
501         // enable PUSB\r
502         SC->PCONP |= (1 << 31);\r
503 \r
504         USB->OTGClkCtrl = 0x12;                   /* Dev clock, AHB clock enable  */\r
505         while ((USB->OTGClkSt & 0x12) != 0x12);\r
506 \r
507         // disable/clear all interrupts for now\r
508         USB->USBDevIntEn = 0;\r
509         USB->USBDevIntClr = 0xFFFFFFFF;\r
510         USB->USBDevIntPri = 0;\r
511 \r
512         USB->USBEpIntEn = 0;\r
513         USB->USBEpIntClr = 0xFFFFFFFF;\r
514         USB->USBEpIntPri = 0;\r
515 \r
516         // by default, only ACKs generate interrupts\r
517         USBHwNakIntEnable(0);\r
518 \r
519         return TRUE;\r
520 }\r
521 \r