re-enabling serial
[clinton/Smoothieware.git] / gcc4mbed / samples / MSTest / USBDevice / USBDevice / USBBusInterface_LPC17_LPC23.cpp
1 // USBBusInterface_LPC17_LPC23.c
2 // USB Bus Interface for NXP LPC1768 and LPC2368
3 // Copyright (c) 2011 ARM Limited. All rights reserved.
4
5 #ifdef TARGET_LPC1768
6
7 #include "USBBusInterface.h"
8
9
10 // Get endpoint direction
11 #define IN_EP(endpoint) ((endpoint) & 1U ? true : false)
12 #define OUT_EP(endpoint) ((endpoint) & 1U ? false : true)
13
14 // Convert physical endpoint number to register bit
15 #define EP(endpoint) (1UL<<endpoint)
16
17 // Power Control for Peripherals register
18 #define PCUSB (1UL<<31)
19
20 // USB Clock Control register
21 #define DEV_CLK_EN (1UL<<1)
22 #define AHB_CLK_EN (1UL<<4)
23
24 // USB Clock Status register
25 #define DEV_CLK_ON (1UL<<1)
26 #define AHB_CLK_ON (1UL<<4)
27
28 // USB Device Interupt registers
29 #define FRAME (1UL<<0)
30 #define EP_FAST (1UL<<1)
31 #define EP_SLOW (1UL<<2)
32 #define DEV_STAT (1UL<<3)
33 #define CCEMPTY (1UL<<4)
34 #define CDFULL (1UL<<5)
35 #define RxENDPKT (1UL<<6)
36 #define TxENDPKT (1UL<<7)
37 #define EP_RLZED (1UL<<8)
38 #define ERR_INT (1UL<<9)
39
40 // USB Control register
41 #define RD_EN (1<<0)
42 #define WR_EN (1<<1)
43 #define LOG_ENDPOINT(endpoint) ((endpoint>>1)<<2)
44
45 // USB Receive Packet Length register
46 #define DV (1UL<<10)
47 #define PKT_RDY (1UL<<11)
48 #define PKT_LNGTH_MASK (0x3ff)
49
50 // Serial Interface Engine (SIE)
51 #define SIE_WRITE (0x01)
52 #define SIE_READ (0x02)
53 #define SIE_COMMAND (0x05)
54 #define SIE_CMD_CODE(phase, data) ((phase<<8)|(data<<16))
55
56 // SIE Command codes
57 #define SIE_CMD_SET_ADDRESS (0xD0)
58 #define SIE_CMD_CONFIGURE_DEVICE (0xD8)
59 #define SIE_CMD_SET_MODE (0xF3)
60 #define SIE_CMD_READ_FRAME_NUMBER (0xF5)
61 #define SIE_CMD_READ_TEST_REGISTER (0xFD)
62 #define SIE_CMD_SET_DEVICE_STATUS (0xFE)
63 #define SIE_CMD_GET_DEVICE_STATUS (0xFE)
64 #define SIE_CMD_GET_ERROR_CODE (0xFF)
65 #define SIE_CMD_READ_ERROR_STATUS (0xFB)
66
67 #define SIE_CMD_SELECT_ENDPOINT(endpoint) (0x00+endpoint)
68 #define SIE_CMD_SELECT_ENDPOINT_CLEAR_INTERRUPT(endpoint) (0x40+endpoint)
69 #define SIE_CMD_SET_ENDPOINT_STATUS(endpoint) (0x40+endpoint)
70
71 #define SIE_CMD_CLEAR_BUFFER (0xF2)
72 #define SIE_CMD_VALIDATE_BUFFER (0xFA)
73
74 // SIE Device Status register
75 #define SIE_DS_CON (1<<0)
76 #define SIE_DS_CON_CH (1<<1)
77 #define SIE_DS_SUS (1<<2)
78 #define SIE_DS_SUS_CH (1<<3)
79 #define SIE_DS_RST (1<<4)
80
81 // SIE Device Set Address register
82 #define SIE_DSA_DEV_EN (1<<7)
83
84 // SIE Configue Device register
85 #define SIE_CONF_DEVICE (1<<0)
86
87 // Select Endpoint register
88 #define SIE_SE_FE (1<<0)
89 #define SIE_SE_ST (1<<1)
90 #define SIE_SE_STP (1<<2)
91 #define SIE_SE_PO (1<<3)
92 #define SIE_SE_EPN (1<<4)
93 #define SIE_SE_B_1_FULL (1<<5)
94 #define SIE_SE_B_2_FULL (1<<6)
95
96 // Set Endpoint Status command
97 #define SIE_SES_ST (1<<0)
98 #define SIE_SES_DA (1<<5)
99 #define SIE_SES_RF_MO (1<<6)
100 #define SIE_SES_CND_ST (1<<7)
101
102
103 USBHAL * USBHAL::instance;
104
105 volatile int epComplete;
106 uint32_t endpointStallState;
107
108 static void SIECommand(uint32_t command) {
109 // The command phase of a SIE transaction
110 LPC_USB->USBDevIntClr = CCEMPTY;
111 LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_COMMAND, command);
112 while (!(LPC_USB->USBDevIntSt & CCEMPTY));
113 }
114
115 static void SIEWriteData(uint8_t data) {
116 // The data write phase of a SIE transaction
117 LPC_USB->USBDevIntClr = CCEMPTY;
118 LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_WRITE, data);
119 while (!(LPC_USB->USBDevIntSt & CCEMPTY));
120 }
121
122 static uint8_t SIEReadData(uint32_t command) {
123 // The data read phase of a SIE transaction
124 LPC_USB->USBDevIntClr = CDFULL;
125 LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_READ, command);
126 while (!(LPC_USB->USBDevIntSt & CDFULL));
127 return (uint8_t)LPC_USB->USBCmdData;
128 }
129
130 static void SIEsetDeviceStatus(uint8_t status) {
131 // Write SIE device status register
132 SIECommand(SIE_CMD_SET_DEVICE_STATUS);
133 SIEWriteData(status);
134 }
135
136 static uint8_t SIEgetDeviceStatus(void) {
137 // Read SIE device status register
138 SIECommand(SIE_CMD_GET_DEVICE_STATUS);
139 return SIEReadData(SIE_CMD_GET_DEVICE_STATUS);
140 }
141
142 void SIEsetAddress(uint8_t address) {
143 // Write SIE device address register
144 SIECommand(SIE_CMD_SET_ADDRESS);
145 SIEWriteData((address & 0x7f) | SIE_DSA_DEV_EN);
146 }
147
148 static uint8_t SIEselectEndpoint(uint8_t endpoint) {
149 // SIE select endpoint command
150 SIECommand(SIE_CMD_SELECT_ENDPOINT(endpoint));
151 return SIEReadData(SIE_CMD_SELECT_ENDPOINT(endpoint));
152 }
153
154 static uint8_t SIEclearBuffer(void) {
155 // SIE clear buffer command
156 SIECommand(SIE_CMD_CLEAR_BUFFER);
157 return SIEReadData(SIE_CMD_CLEAR_BUFFER);
158 }
159
160 static void SIEvalidateBuffer(void) {
161 // SIE validate buffer command
162 SIECommand(SIE_CMD_VALIDATE_BUFFER);
163 }
164
165 static void SIEsetEndpointStatus(uint8_t endpoint, uint8_t status) {
166 // SIE set endpoint status command
167 SIECommand(SIE_CMD_SET_ENDPOINT_STATUS(endpoint));
168 SIEWriteData(status);
169 }
170
171 static uint16_t SIEgetFrameNumber(void) __attribute__ ((unused));
172 static uint16_t SIEgetFrameNumber(void) {
173 // Read current frame number
174 uint16_t lowByte;
175 uint16_t highByte;
176
177 SIECommand(SIE_CMD_READ_FRAME_NUMBER);
178 lowByte = SIEReadData(SIE_CMD_READ_FRAME_NUMBER);
179 highByte = SIEReadData(SIE_CMD_READ_FRAME_NUMBER);
180
181 return (highByte << 8) | lowByte;
182 }
183
184 static void SIEconfigureDevice(void) {
185 // SIE Configure device command
186 SIECommand(SIE_CMD_CONFIGURE_DEVICE);
187 SIEWriteData(SIE_CONF_DEVICE);
188 }
189
190 static void SIEunconfigureDevice(void) {
191 // SIE Configure device command
192 SIECommand(SIE_CMD_CONFIGURE_DEVICE);
193 SIEWriteData(0);
194 }
195
196 static void SIEconnect(void) {
197 // Connect USB device
198 uint8_t status;
199
200 status = SIEgetDeviceStatus();
201 SIEsetDeviceStatus(status | SIE_DS_CON);
202 }
203
204
205 static void SIEdisconnect(void) {
206 // Disconnect USB device
207 uint8_t status;
208
209 status = SIEgetDeviceStatus();
210 SIEsetDeviceStatus(status & ~SIE_DS_CON);
211 }
212
213
214 static uint8_t selectEndpointClearInterrupt(uint8_t endpoint) {
215 // Implemented using using EP_INT_CLR.
216 LPC_USB->USBEpIntClr = EP(endpoint);
217 while (!(LPC_USB->USBDevIntSt & CDFULL));
218 return (uint8_t)LPC_USB->USBCmdData;
219 }
220
221
222
223
224
225 static void enableEndpointEvent(uint8_t endpoint) {
226 // Enable an endpoint interrupt
227 LPC_USB->USBEpIntEn |= EP(endpoint);
228 }
229
230 static void disableEndpointEvent(uint8_t endpoint) __attribute__ ((unused));
231 static void disableEndpointEvent(uint8_t endpoint) {
232 // Disable an endpoint interrupt
233 LPC_USB->USBEpIntEn &= ~EP(endpoint);
234 }
235
236 static volatile uint32_t __attribute__((used)) dummyRead;
237
238
239 uint32_t USBHAL::endpointReadcore(uint8_t endpoint, uint8_t *buffer) {
240 // Read from an OUT endpoint
241 uint32_t size;
242 uint32_t i;
243 uint32_t data = 0;
244 uint8_t offset;
245
246 LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | RD_EN;
247 while (!(LPC_USB->USBRxPLen & PKT_RDY));
248
249 size = LPC_USB->USBRxPLen & PKT_LNGTH_MASK;
250
251 offset = 0;
252
253 if (size > 0) {
254 for (i=0; i<size; i++) {
255 if (offset==0) {
256 // Fetch up to four bytes of data as a word
257 data = LPC_USB->USBRxData;
258 }
259
260 // extract a byte
261 *buffer = (data>>offset) & 0xff;
262 buffer++;
263
264 // move on to the next byte
265 offset = (offset + 8) % 32;
266 }
267 } else {
268 dummyRead = LPC_USB->USBRxData;
269 }
270
271 LPC_USB->USBCtrl = 0;
272
273 if ((endpoint >> 1) % 3 || (endpoint >> 1) == 0) {
274 SIEselectEndpoint(endpoint);
275 SIEclearBuffer();
276 }
277
278 return size;
279 }
280
281 static void endpointWritecore(uint8_t endpoint, uint8_t *buffer, uint32_t size) {
282 // Write to an IN endpoint
283 uint32_t temp, data;
284 uint8_t offset;
285
286 LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | WR_EN;
287
288 LPC_USB->USBTxPLen = size;
289 offset = 0;
290 data = 0;
291
292 if (size>0) {
293 do {
294 // Fetch next data byte into a word-sized temporary variable
295 temp = *buffer++;
296
297 // Add to current data word
298 temp = temp << offset;
299 data = data | temp;
300
301 // move on to the next byte
302 offset = (offset + 8) % 32;
303 size--;
304
305 if ((offset==0) || (size==0)) {
306 // Write the word to the endpoint
307 LPC_USB->USBTxData = data;
308 data = 0;
309 }
310 } while (size>0);
311 } else {
312 LPC_USB->USBTxData = 0;
313 }
314
315 // Clear WR_EN to cover zero length packet case
316 LPC_USB->USBCtrl=0;
317
318 SIEselectEndpoint(endpoint);
319 SIEvalidateBuffer();
320 }
321
322
323
324
325
326
327
328 USBHAL::USBHAL(void) {
329 // Disable IRQ
330 NVIC_DisableIRQ(USB_IRQn);
331
332 // Enable power to USB device controller
333 LPC_SC->PCONP |= PCUSB;
334
335 // Enable USB clocks
336 LPC_USB->USBClkCtrl |= DEV_CLK_EN | AHB_CLK_EN;
337 while (LPC_USB->USBClkSt != (DEV_CLK_ON | AHB_CLK_ON));
338
339 // Configure pins P0.29 and P0.30 to be USB D+ and USB D-
340 LPC_PINCON->PINSEL1 &= 0xc3ffffff;
341 LPC_PINCON->PINSEL1 |= 0x14000000;
342
343 // Disconnect USB device
344 SIEdisconnect();
345
346 // Configure pin P2.9 to be Connect
347 LPC_PINCON->PINSEL4 &= 0xfffcffff;
348 LPC_PINCON->PINSEL4 |= 0x00040000;
349
350 // Connect must be low for at least 2.5uS
351 wait(0.3);
352
353 // Set the maximum packet size for the control endpoints
354 realiseEndpoint(EP0IN, MAX_PACKET_SIZE_EP0, 0);
355 realiseEndpoint(EP0OUT, MAX_PACKET_SIZE_EP0, 0);
356
357 // Attach IRQ
358 instance = this;
359 NVIC_SetVector(USB_IRQn, (uint32_t)&_usbisr);
360 NVIC_EnableIRQ(USB_IRQn);
361
362 // Enable interrupts for device events and EP0
363 LPC_USB->USBDevIntEn = EP_SLOW | DEV_STAT | FRAME;
364 enableEndpointEvent(EP0IN);
365 enableEndpointEvent(EP0OUT);
366 }
367
368 USBHAL::~USBHAL(void) {
369 // Ensure device disconnected
370 SIEdisconnect();
371
372 // Disable USB interrupts
373 NVIC_DisableIRQ(USB_IRQn);
374 }
375
376 void USBHAL::connect(void) {
377 // Connect USB device
378 SIEconnect();
379 }
380
381 void USBHAL::disconnect(void) {
382 // Disconnect USB device
383 SIEdisconnect();
384 }
385
386 void USBHAL::configureDevice(void) {
387 SIEconfigureDevice();
388 }
389
390 void USBHAL::unconfigureDevice(void) {
391 SIEunconfigureDevice();
392 }
393
394 void USBHAL::setAddress(uint8_t address) {
395 SIEsetAddress(address);
396 }
397
398 void USBHAL::EP0setup(uint8_t *buffer) {
399 endpointReadcore(EP0OUT, buffer);
400 }
401
402 void USBHAL::EP0read(void) {
403 // Not required
404 }
405
406 uint32_t USBHAL::EP0getReadResult(uint8_t *buffer) {
407 return endpointReadcore(EP0OUT, buffer);
408 }
409
410 void USBHAL::EP0write(uint8_t *buffer, uint32_t size) {
411 endpointWritecore(EP0IN, buffer, size);
412 }
413
414 void USBHAL::EP0getWriteResult(void) {
415 // Not required
416 }
417
418 void USBHAL::EP0stall(void) {
419 // This will stall both control endpoints
420 stallEndpoint(EP0OUT);
421 }
422
423 EP_STATUS USBHAL::endpointRead(uint8_t endpoint, uint32_t maximumSize) {
424 return EP_PENDING;
425 }
426
427 EP_STATUS USBHAL::endpointReadResult(uint8_t endpoint, uint8_t * buffer, uint32_t *bytesRead) {
428
429 //for isochronous endpoint, we don't wait an interrupt
430 if ((endpoint >> 1) % 3 || (endpoint >> 1) == 0) {
431 if (!(epComplete & EP(endpoint)))
432 return EP_PENDING;
433 }
434
435 *bytesRead = endpointReadcore(endpoint, buffer);
436 epComplete &= ~EP(endpoint);
437 return EP_COMPLETED;
438 }
439
440 EP_STATUS USBHAL::endpointWrite(uint8_t endpoint, uint8_t *data, uint32_t size) {
441 if (getEndpointStallState(endpoint)) {
442 return EP_STALLED;
443 }
444
445 epComplete &= ~EP(endpoint);
446
447 endpointWritecore(endpoint, data, size);
448 return EP_PENDING;
449 }
450
451 EP_STATUS USBHAL::endpointWriteResult(uint8_t endpoint) {
452 if (epComplete & EP(endpoint)) {
453 epComplete &= ~EP(endpoint);
454 return EP_COMPLETED;
455 }
456
457 return EP_PENDING;
458 }
459
460 bool USBHAL::realiseEndpoint(uint8_t endpoint, uint32_t maxPacket, uint32_t flags) {
461 // Realise an endpoint
462 LPC_USB->USBDevIntClr = EP_RLZED;
463 LPC_USB->USBReEp |= EP(endpoint);
464 LPC_USB->USBEpInd = endpoint;
465 LPC_USB->USBMaxPSize = maxPacket;
466
467 while (!(LPC_USB->USBDevIntSt & EP_RLZED));
468 LPC_USB->USBDevIntClr = EP_RLZED;
469
470 // Clear stall state
471 endpointStallState &= ~EP(endpoint);
472
473 enableEndpointEvent(endpoint);
474 return true;
475 }
476
477 void USBHAL::stallEndpoint(uint8_t endpoint) {
478 // Stall an endpoint
479 if ( (endpoint==EP0IN) || (endpoint==EP0OUT) ) {
480 // Conditionally stall both control endpoints
481 SIEsetEndpointStatus(EP0OUT, SIE_SES_CND_ST);
482 } else {
483 SIEsetEndpointStatus(endpoint, SIE_SES_ST);
484
485 // Update stall state
486 endpointStallState |= EP(endpoint);
487 }
488 }
489
490 void USBHAL::unstallEndpoint(uint8_t endpoint) {
491 // Unstall an endpoint. The endpoint will also be reinitialised
492 SIEsetEndpointStatus(endpoint, 0);
493
494 // Update stall state
495 endpointStallState &= ~EP(endpoint);
496 }
497
498 bool USBHAL::getEndpointStallState(uint8_t endpoint) {
499 // Returns true if endpoint stalled
500 return endpointStallState & EP(endpoint);
501 }
502
503 void USBHAL::remoteWakeup(void) {
504 // Remote wakeup
505 uint8_t status;
506
507 // Enable USB clocks
508 LPC_USB->USBClkCtrl |= DEV_CLK_EN | AHB_CLK_EN;
509 while (LPC_USB->USBClkSt != (DEV_CLK_ON | AHB_CLK_ON));
510
511 status = SIEgetDeviceStatus();
512 SIEsetDeviceStatus(status & ~SIE_DS_SUS);
513 }
514
515
516
517
518
519 void USBHAL::_usbisr(void) {
520 instance->usbisr();
521 }
522
523
524 void USBHAL::usbisr(void) {
525 uint8_t devStat;
526
527 if (LPC_USB->USBDevIntSt & FRAME) {
528 // Start of frame event
529 SOF(SIEgetFrameNumber());
530 // Clear interrupt status flag
531 LPC_USB->USBDevIntClr = FRAME;
532 }
533
534 if (LPC_USB->USBDevIntSt & DEV_STAT) {
535 // Device Status interrupt
536 // Must clear the interrupt status flag before reading the device status from the SIE
537 LPC_USB->USBDevIntClr = DEV_STAT;
538
539 // Read device status from SIE
540 devStat = SIEgetDeviceStatus();
541
542 if (devStat & SIE_DS_RST) {
543 // Bus reset
544 busReset();
545 }
546 }
547
548 if (LPC_USB->USBDevIntSt & EP_SLOW) {
549 // (Slow) Endpoint Interrupt
550
551 // Process each endpoint interrupt
552 if (LPC_USB->USBEpIntSt & EP(EP0OUT)) {
553 if (selectEndpointClearInterrupt(EP0OUT) & SIE_SE_STP) {
554 // this is a setup packet
555 EP0setupCallback();
556 } else {
557 EP0out();
558 }
559 LPC_USB->USBDevIntClr = EP_SLOW;
560 }
561
562 if (LPC_USB->USBEpIntSt & EP(EP0IN)) {
563 selectEndpointClearInterrupt(EP0IN);
564 LPC_USB->USBDevIntClr = EP_SLOW;
565 EP0in();
566 }
567
568 // TODO: This should cover all endpoints, not just EP1,2,3:
569 if (LPC_USB->USBEpIntSt & EP(EP1IN)) {
570 selectEndpointClearInterrupt(EP1IN);
571 epComplete |= EP(EP1IN);
572 LPC_USB->USBDevIntClr = EP_SLOW;
573 if (EP1_IN_callback())
574 epComplete &= ~EP(EP1IN);
575 }
576
577 if (LPC_USB->USBEpIntSt & EP(EP1OUT)) {
578 selectEndpointClearInterrupt(EP1OUT);
579 epComplete |= EP(EP1OUT);
580 LPC_USB->USBDevIntClr = EP_SLOW;
581 if (EP1_OUT_callback())
582 epComplete &= ~EP(EP1OUT);
583 }
584
585 if (LPC_USB->USBEpIntSt & EP(EP2IN)) {
586 selectEndpointClearInterrupt(EP2IN);
587 epComplete |= EP(EP2IN);
588 LPC_USB->USBDevIntClr = EP_SLOW;
589 if (EP2_IN_callback())
590 epComplete &= ~EP(EP2IN);
591 }
592
593 if (LPC_USB->USBEpIntSt & EP(EP2OUT)) {
594 selectEndpointClearInterrupt(EP2OUT);
595 epComplete |= EP(EP2OUT);
596 LPC_USB->USBDevIntClr = EP_SLOW;
597 if (EP2_OUT_callback())
598 epComplete &= ~EP(EP2OUT);
599 }
600
601 if (LPC_USB->USBEpIntSt & EP(EP3IN)) {
602 selectEndpointClearInterrupt(EP3IN);
603 epComplete |= EP(EP3IN);
604 LPC_USB->USBDevIntClr = EP_SLOW;
605 if (EP3_IN_callback())
606 epComplete &= ~EP(EP3IN);
607 }
608
609 if (LPC_USB->USBEpIntSt & EP(EP3OUT)) {
610 selectEndpointClearInterrupt(EP3OUT);
611 epComplete |= EP(EP3OUT);
612 LPC_USB->USBDevIntClr = EP_SLOW;
613 if (EP3_OUT_callback())
614 epComplete &= ~EP(EP3OUT);
615 }
616 }
617 }
618
619 #endif