1 // USBBusInterface_LPC17_LPC23.c
2 // USB Bus Interface for NXP LPC1768 and LPC2368
3 // Copyright (c) 2011 ARM Limited. All rights reserved.
7 #include "USBBusInterface.h"
10 // Get endpoint direction
11 #define IN_EP(endpoint) ((endpoint) & 1U ? true : false)
12 #define OUT_EP(endpoint) ((endpoint) & 1U ? false : true)
14 // Convert physical endpoint number to register bit
15 #define EP(endpoint) (1UL<<endpoint)
17 // Power Control for Peripherals register
18 #define PCUSB (1UL<<31)
20 // USB Clock Control register
21 #define DEV_CLK_EN (1UL<<1)
22 #define AHB_CLK_EN (1UL<<4)
24 // USB Clock Status register
25 #define DEV_CLK_ON (1UL<<1)
26 #define AHB_CLK_ON (1UL<<4)
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)
40 // USB Control register
43 #define LOG_ENDPOINT(endpoint) ((endpoint>>1)<<2)
45 // USB Receive Packet Length register
47 #define PKT_RDY (1UL<<11)
48 #define PKT_LNGTH_MASK (0x3ff)
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))
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)
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)
71 #define SIE_CMD_CLEAR_BUFFER (0xF2)
72 #define SIE_CMD_VALIDATE_BUFFER (0xFA)
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)
81 // SIE Device Set Address register
82 #define SIE_DSA_DEV_EN (1<<7)
84 // SIE Configue Device register
85 #define SIE_CONF_DEVICE (1<<0)
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)
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)
103 USBHAL
* USBHAL::instance
;
105 volatile int epComplete
;
106 uint32_t endpointStallState
;
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
));
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
));
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
;
130 static void SIEsetDeviceStatus(uint8_t status
) {
131 // Write SIE device status register
132 SIECommand(SIE_CMD_SET_DEVICE_STATUS
);
133 SIEWriteData(status
);
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
);
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
);
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
));
154 static uint8_t SIEclearBuffer(void) {
155 // SIE clear buffer command
156 SIECommand(SIE_CMD_CLEAR_BUFFER
);
157 return SIEReadData(SIE_CMD_CLEAR_BUFFER
);
160 static void SIEvalidateBuffer(void) {
161 // SIE validate buffer command
162 SIECommand(SIE_CMD_VALIDATE_BUFFER
);
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
);
171 static uint16_t SIEgetFrameNumber(void) __attribute__ ((unused
));
172 static uint16_t SIEgetFrameNumber(void) {
173 // Read current frame number
177 SIECommand(SIE_CMD_READ_FRAME_NUMBER
);
178 lowByte
= SIEReadData(SIE_CMD_READ_FRAME_NUMBER
);
179 highByte
= SIEReadData(SIE_CMD_READ_FRAME_NUMBER
);
181 return (highByte
<< 8) | lowByte
;
184 static void SIEconfigureDevice(void) {
185 // SIE Configure device command
186 SIECommand(SIE_CMD_CONFIGURE_DEVICE
);
187 SIEWriteData(SIE_CONF_DEVICE
);
190 static void SIEunconfigureDevice(void) {
191 // SIE Configure device command
192 SIECommand(SIE_CMD_CONFIGURE_DEVICE
);
196 static void SIEconnect(void) {
197 // Connect USB device
200 status
= SIEgetDeviceStatus();
201 SIEsetDeviceStatus(status
| SIE_DS_CON
);
205 static void SIEdisconnect(void) {
206 // Disconnect USB device
209 status
= SIEgetDeviceStatus();
210 SIEsetDeviceStatus(status
& ~SIE_DS_CON
);
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
;
225 static void enableEndpointEvent(uint8_t endpoint
) {
226 // Enable an endpoint interrupt
227 LPC_USB
->USBEpIntEn
|= EP(endpoint
);
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
);
236 static volatile uint32_t __attribute__((used
)) dummyRead
;
239 uint32_t USBHAL::endpointReadcore(uint8_t endpoint
, uint8_t *buffer
) {
240 // Read from an OUT endpoint
246 LPC_USB
->USBCtrl
= LOG_ENDPOINT(endpoint
) | RD_EN
;
247 while (!(LPC_USB
->USBRxPLen
& PKT_RDY
));
249 size
= LPC_USB
->USBRxPLen
& PKT_LNGTH_MASK
;
254 for (i
=0; i
<size
; i
++) {
256 // Fetch up to four bytes of data as a word
257 data
= LPC_USB
->USBRxData
;
261 *buffer
= (data
>>offset
) & 0xff;
264 // move on to the next byte
265 offset
= (offset
+ 8) % 32;
268 dummyRead
= LPC_USB
->USBRxData
;
271 LPC_USB
->USBCtrl
= 0;
273 if ((endpoint
>> 1) % 3 || (endpoint
>> 1) == 0) {
274 SIEselectEndpoint(endpoint
);
281 static void endpointWritecore(uint8_t endpoint
, uint8_t *buffer
, uint32_t size
) {
282 // Write to an IN endpoint
286 LPC_USB
->USBCtrl
= LOG_ENDPOINT(endpoint
) | WR_EN
;
288 LPC_USB
->USBTxPLen
= size
;
294 // Fetch next data byte into a word-sized temporary variable
297 // Add to current data word
298 temp
= temp
<< offset
;
301 // move on to the next byte
302 offset
= (offset
+ 8) % 32;
305 if ((offset
==0) || (size
==0)) {
306 // Write the word to the endpoint
307 LPC_USB
->USBTxData
= data
;
312 LPC_USB
->USBTxData
= 0;
315 // Clear WR_EN to cover zero length packet case
318 SIEselectEndpoint(endpoint
);
328 USBHAL::USBHAL(void) {
330 NVIC_DisableIRQ(USB_IRQn
);
332 // Enable power to USB device controller
333 LPC_SC
->PCONP
|= PCUSB
;
336 LPC_USB
->USBClkCtrl
|= DEV_CLK_EN
| AHB_CLK_EN
;
337 while (LPC_USB
->USBClkSt
!= (DEV_CLK_ON
| AHB_CLK_ON
));
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;
343 // Disconnect USB device
346 // Configure pin P2.9 to be Connect
347 LPC_PINCON
->PINSEL4
&= 0xfffcffff;
348 LPC_PINCON
->PINSEL4
|= 0x00040000;
350 // Connect must be low for at least 2.5uS
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);
359 NVIC_SetVector(USB_IRQn
, (uint32_t)&_usbisr
);
360 NVIC_EnableIRQ(USB_IRQn
);
362 // Enable interrupts for device events and EP0
363 LPC_USB
->USBDevIntEn
= EP_SLOW
| DEV_STAT
| FRAME
;
364 enableEndpointEvent(EP0IN
);
365 enableEndpointEvent(EP0OUT
);
368 USBHAL::~USBHAL(void) {
369 // Ensure device disconnected
372 // Disable USB interrupts
373 NVIC_DisableIRQ(USB_IRQn
);
376 void USBHAL::connect(void) {
377 // Connect USB device
381 void USBHAL::disconnect(void) {
382 // Disconnect USB device
386 void USBHAL::configureDevice(void) {
387 SIEconfigureDevice();
390 void USBHAL::unconfigureDevice(void) {
391 SIEunconfigureDevice();
394 void USBHAL::setAddress(uint8_t address
) {
395 SIEsetAddress(address
);
398 void USBHAL::EP0setup(uint8_t *buffer
) {
399 endpointReadcore(EP0OUT
, buffer
);
402 void USBHAL::EP0read(void) {
406 uint32_t USBHAL::EP0getReadResult(uint8_t *buffer
) {
407 return endpointReadcore(EP0OUT
, buffer
);
410 void USBHAL::EP0write(uint8_t *buffer
, uint32_t size
) {
411 endpointWritecore(EP0IN
, buffer
, size
);
414 void USBHAL::EP0getWriteResult(void) {
418 void USBHAL::EP0stall(void) {
419 // This will stall both control endpoints
420 stallEndpoint(EP0OUT
);
423 EP_STATUS
USBHAL::endpointRead(uint8_t endpoint
, uint32_t maximumSize
) {
427 EP_STATUS
USBHAL::endpointReadResult(uint8_t endpoint
, uint8_t * buffer
, uint32_t *bytesRead
) {
429 //for isochronous endpoint, we don't wait an interrupt
430 if ((endpoint
>> 1) % 3 || (endpoint
>> 1) == 0) {
431 if (!(epComplete
& EP(endpoint
)))
435 *bytesRead
= endpointReadcore(endpoint
, buffer
);
436 epComplete
&= ~EP(endpoint
);
440 EP_STATUS
USBHAL::endpointWrite(uint8_t endpoint
, uint8_t *data
, uint32_t size
) {
441 if (getEndpointStallState(endpoint
)) {
445 epComplete
&= ~EP(endpoint
);
447 endpointWritecore(endpoint
, data
, size
);
451 EP_STATUS
USBHAL::endpointWriteResult(uint8_t endpoint
) {
452 if (epComplete
& EP(endpoint
)) {
453 epComplete
&= ~EP(endpoint
);
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
;
467 while (!(LPC_USB
->USBDevIntSt
& EP_RLZED
));
468 LPC_USB
->USBDevIntClr
= EP_RLZED
;
471 endpointStallState
&= ~EP(endpoint
);
473 enableEndpointEvent(endpoint
);
477 void USBHAL::stallEndpoint(uint8_t endpoint
) {
479 if ( (endpoint
==EP0IN
) || (endpoint
==EP0OUT
) ) {
480 // Conditionally stall both control endpoints
481 SIEsetEndpointStatus(EP0OUT
, SIE_SES_CND_ST
);
483 SIEsetEndpointStatus(endpoint
, SIE_SES_ST
);
485 // Update stall state
486 endpointStallState
|= EP(endpoint
);
490 void USBHAL::unstallEndpoint(uint8_t endpoint
) {
491 // Unstall an endpoint. The endpoint will also be reinitialised
492 SIEsetEndpointStatus(endpoint
, 0);
494 // Update stall state
495 endpointStallState
&= ~EP(endpoint
);
498 bool USBHAL::getEndpointStallState(uint8_t endpoint
) {
499 // Returns true if endpoint stalled
500 return endpointStallState
& EP(endpoint
);
503 void USBHAL::remoteWakeup(void) {
508 LPC_USB
->USBClkCtrl
|= DEV_CLK_EN
| AHB_CLK_EN
;
509 while (LPC_USB
->USBClkSt
!= (DEV_CLK_ON
| AHB_CLK_ON
));
511 status
= SIEgetDeviceStatus();
512 SIEsetDeviceStatus(status
& ~SIE_DS_SUS
);
519 void USBHAL::_usbisr(void) {
524 void USBHAL::usbisr(void) {
527 if (LPC_USB
->USBDevIntSt
& FRAME
) {
528 // Start of frame event
529 SOF(SIEgetFrameNumber());
530 // Clear interrupt status flag
531 LPC_USB
->USBDevIntClr
= FRAME
;
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
;
539 // Read device status from SIE
540 devStat
= SIEgetDeviceStatus();
542 if (devStat
& SIE_DS_RST
) {
548 if (LPC_USB
->USBDevIntSt
& EP_SLOW
) {
549 // (Slow) Endpoint Interrupt
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
559 LPC_USB
->USBDevIntClr
= EP_SLOW
;
562 if (LPC_USB
->USBEpIntSt
& EP(EP0IN
)) {
563 selectEndpointClearInterrupt(EP0IN
);
564 LPC_USB
->USBDevIntClr
= EP_SLOW
;
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
);
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
);
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
);
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
);
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
);
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
);