]> git.proxmox.com Git - mirror_edk2.git/blame - EmbeddedPkg/Drivers/Isp1761UsbDxe/Isp1761UsbDxe.c
EmbeddedPkg/Isp1761UsbDxe: Driver for the NXP ISP1761's USB peripheral controller
[mirror_edk2.git] / EmbeddedPkg / Drivers / Isp1761UsbDxe / Isp1761UsbDxe.c
CommitLineData
dbfd80d8
OM
1/** @file\r
2\r
3 Copyright (c) 2013-2014, ARM Ltd. All rights reserved.<BR>\r
4\r
5 This program and the accompanying materials\r
6 are licensed and made available under the terms and conditions of the BSD License\r
7 which accompanies this distribution. The full text of the license may be found at\r
8 http://opensource.org/licenses/bsd-license.php\r
9\r
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
13**/\r
14\r
15#include <Library/TimerLib.h>\r
16#include <Library/DebugLib.h>\r
17#include <Library/UefiBootServicesTableLib.h>\r
18#include <Library/UefiDriverEntryPoint.h>\r
19#include <Library/IoLib.h>\r
20#include <Library/MemoryAllocationLib.h>\r
21\r
22#include <IndustryStandard/Usb.h>\r
23\r
24#include <Protocol/UsbDevice.h>\r
25\r
26#include "Isp1761UsbDxe.h"\r
27\r
28/*\r
29 Driver for using the NXP ISP1761 as a USB Peripheral controller.\r
30 Doesn't use USB OTG - just sets it in Pure Peripheral mode.\r
31\r
32 The ISP1582 datasheet has a little more info on the Peripheral controller\r
33 registers than the ISP1761 datasheet\r
34\r
35 We don't do string descriptors. They're optional.\r
36 We currently assume the device has one configuration, one interface, one IN\r
37 endpoint, and one OUT endpoint (plus the default control endpoint).\r
38\r
39 In fact, this driver is the minimum required to implement fastboot.\r
40*/\r
41\r
42// TODO Make sure the controller isn't sending empty packets when it shouldn't\r
43// (check behaviour in cases when Buffer Length isn't explcitly set)\r
44\r
45// ISP1582 Datasheet:\r
46// "Data transfers preceding the status stage must first be fully\r
47// completed before the STATUS bit can be set."\r
48// This variable stores whether some control data has been pended in the EP0TX\r
49// Tx buffer, so that when an EP0TX interrupt is received we can set the STATUS\r
50// bit to go to the Status stage of the control transfer.\r
51STATIC BOOLEAN mControlTxPending = FALSE;\r
52\r
53STATIC USB_DEVICE_DESCRIPTOR *mDeviceDescriptor;\r
54\r
55// The config descriptor, interface descriptor, and endpoint descriptors in a\r
56// buffer (in that order)\r
57STATIC VOID *mDescriptors;\r
58// Convenience pointers to those descriptors inside the buffer:\r
59STATIC USB_INTERFACE_DESCRIPTOR *mInterfaceDescriptor;\r
60STATIC USB_CONFIG_DESCRIPTOR *mConfigDescriptor;\r
61STATIC USB_ENDPOINT_DESCRIPTOR *mEndpointDescriptors;\r
62\r
63STATIC USB_DEVICE_RX_CALLBACK mDataReceivedCallback;\r
64STATIC USB_DEVICE_TX_CALLBACK mDataSentCallback;\r
65\r
66// The time between interrupt polls, in units of 100 nanoseconds\r
67// 10 Microseconds\r
68#define ISP1761_INTERRUPT_POLL_PERIOD 10000\r
69\r
70STATIC\r
71VOID\r
72SelectEndpoint (\r
73 IN UINT8 Endpoint\r
74 )\r
75{\r
76 // The DMA Endpoint Index must not point to the same as the\r
77 // Endpoint Index Register.\r
78 WRITE_REG32 (ISP1761_DMA_ENDPOINT_INDEX, ((Endpoint + 2) % ISP1761_NUM_ENDPOINTS));\r
79 WRITE_REG32 (ISP1761_ENDPOINT_INDEX, Endpoint);\r
80}\r
81\r
82// Enable going to the Data stage of a control transfer\r
83STATIC\r
84VOID\r
85DataStageEnable (\r
86 IN UINT8 Endpoint\r
87 )\r
88{\r
89 SelectEndpoint (Endpoint);\r
90 WRITE_REG32 (ISP1761_CTRL_FUNCTION, ISP1761_CTRL_FUNCTION_DSEN);\r
91}\r
92\r
93// Go to the Status stage of a successful control transfer\r
94STATIC\r
95VOID\r
96StatusAcknowledge (\r
97 IN UINT8 Endpoint\r
98)\r
99{\r
100 SelectEndpoint (Endpoint);\r
101 WRITE_REG32 (ISP1761_CTRL_FUNCTION, ISP1761_CTRL_FUNCTION_STATUS);\r
102}\r
103\r
104// Read the FIFO for the endpoint indexed by Endpoint, into the buffer pointed\r
105// at by Buffer, whose size is *Size bytes.\r
106//\r
107// If *Size is less than the number of bytes in the FIFO, return EFI_BUFFER_TOO_SMALL\r
108//\r
109// Update *Size with the number of bytes of data in the FIFO.\r
110STATIC\r
111EFI_STATUS\r
112ReadEndpointBuffer (\r
113 IN UINT8 Endpoint,\r
114 IN OUT UINTN *Size,\r
115 IN OUT VOID *Buffer\r
116 )\r
117{\r
118 UINT16 NumBytesAvailable;\r
119 UINT32 Val32;\r
120 UINTN Index;\r
121 UINTN NumBytesRead;\r
122\r
123 SelectEndpoint (Endpoint);\r
124\r
125 NumBytesAvailable = READ_REG16 (ISP1761_BUFFER_LENGTH);\r
126\r
127 if (NumBytesAvailable > *Size) {\r
128 *Size = NumBytesAvailable;\r
129 return EFI_BUFFER_TOO_SMALL;\r
130 }\r
131 *Size = NumBytesAvailable;\r
132\r
133 /* -- NB! --\r
134 The datasheet says the Data Port is 16 bits but it actually appears to\r
135 be 32 bits.\r
136 */\r
137\r
138 // Read 32-bit chunks\r
139 for (Index = 0; Index < NumBytesAvailable / 4; Index++) {\r
140 ((UINT32 *) Buffer)[Index] = READ_REG32 (ISP1761_DATA_PORT);\r
141 }\r
142\r
143 // Read remaining bytes\r
144\r
145 // Round NumBytesAvailable down to nearest power of 4\r
146 NumBytesRead = NumBytesAvailable & (~0x3);\r
147 if (NumBytesRead != NumBytesAvailable) {\r
148 Val32 = READ_REG32 (ISP1761_DATA_PORT);\r
149 // Copy each required byte of 32-bit word into buffer\r
150 for (Index = 0; Index < NumBytesAvailable % 4; Index++) {\r
151 ((UINT8 *) Buffer)[NumBytesRead + Index] = Val32 >> (Index * 8);\r
152 }\r
153 }\r
154 return EFI_SUCCESS;\r
155}\r
156\r
157/*\r
158 Write an endpoint buffer. Parameters:\r
159 Endpoint Endpoint index (see Endpoint Index Register in datasheet)\r
160 MaxPacketSize The MaxPacketSize this endpoint is configured for\r
161 Size The size of the Buffer\r
162 Buffer The data\r
163\r
164 Assumes MaxPacketSize is a multiple of 4.\r
165 (It seems that all valid values for MaxPacketSize _are_ multiples of 4)\r
166*/\r
167STATIC\r
168EFI_STATUS\r
169WriteEndpointBuffer (\r
170 IN UINT8 Endpoint,\r
171 IN UINTN MaxPacketSize,\r
172 IN UINTN Size,\r
173 IN CONST VOID *Buffer\r
174 )\r
175{\r
176 UINTN Index;\r
177 UINT32 *DwordBuffer;\r
178\r
179 DwordBuffer = (UINT32 *) Buffer;\r
180 SelectEndpoint (Endpoint);\r
181\r
182 /* -- NB! --\r
183 The datasheet says the Data Port is 16 bits but it actually appears to\r
184 be 32 bits.\r
185 */\r
186\r
187 // Write packets of size MaxPacketSize\r
188 while (Size > MaxPacketSize) {\r
189 for (Index = 0; Index < MaxPacketSize / 4; Index++) {\r
190 WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[Index]);\r
191 }\r
192 Size -= MaxPacketSize;\r
193 DwordBuffer += (MaxPacketSize / sizeof (UINT32));\r
194 }\r
195\r
196 // Write remaining data\r
197\r
198 if (Size > 0) {\r
199 WRITE_REG32 (ISP1761_BUFFER_LENGTH, Size);\r
200\r
201 while (Size > 4) {\r
202 WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[0]);\r
203 Size -= 4;\r
204 DwordBuffer++;\r
205 }\r
206\r
207 if (Size > 0) {\r
208 WRITE_REG32 (ISP1761_DATA_PORT, DwordBuffer[0]);\r
209 }\r
210 }\r
211\r
212 return EFI_SUCCESS;\r
213}\r
214\r
215STATIC\r
216EFI_STATUS\r
217HandleGetDescriptor (\r
218 IN USB_DEVICE_REQUEST *Request\r
219 )\r
220{\r
221 EFI_STATUS Status;\r
222 UINT8 DescriptorType;\r
223 UINTN ResponseSize;\r
224 VOID *ResponseData;\r
225\r
226 ResponseSize = 0;\r
227 ResponseData = NULL;\r
228 Status = EFI_SUCCESS;\r
229\r
230 // Pretty confused if bmRequestType is anything but this:\r
231 ASSERT (Request->RequestType == USB_DEV_GET_DESCRIPTOR_REQ_TYPE);\r
232\r
233 // Choose the response\r
234 DescriptorType = Request->Value >> 8;\r
235 switch (DescriptorType) {\r
236 case USB_DESC_TYPE_DEVICE:\r
237 DEBUG ((EFI_D_INFO, "USB: Got a request for device descriptor\n"));\r
238 ResponseSize = sizeof (USB_DEVICE_DESCRIPTOR);\r
239 ResponseData = mDeviceDescriptor;\r
240 break;\r
241 case USB_DESC_TYPE_CONFIG:\r
242 DEBUG ((EFI_D_INFO, "USB: Got a request for config descriptor\n"));\r
243 ResponseSize = mConfigDescriptor->TotalLength;\r
244 ResponseData = mDescriptors;\r
245 break;\r
246 case USB_DESC_TYPE_STRING:\r
247 DEBUG ((EFI_D_INFO, "USB: Got a request for String descriptor %d\n", Request->Value & 0xFF));\r
248 break;\r
249 default:\r
250 DEBUG ((EFI_D_INFO, "USB: Didn't understand request for descriptor 0x%04x\n", Request->Value));\r
251 Status = EFI_NOT_FOUND;\r
252 break;\r
253 }\r
254\r
255 // Send the response\r
256 if (ResponseData) {\r
257 ASSERT (ResponseSize != 0);\r
258\r
259 if (Request->Length < ResponseSize) {\r
260 // Truncate response\r
261 ResponseSize = Request->Length;\r
262 } else if (Request->Length > ResponseSize) {\r
263 DEBUG ((EFI_D_INFO, "USB: Info: ResponseSize < wLength\n"));\r
264 }\r
265\r
266 DataStageEnable (ISP1761_EP0TX);\r
267 Status = WriteEndpointBuffer (\r
268 ISP1761_EP0TX,\r
269 MAX_PACKET_SIZE_CONTROL,\r
270 ResponseSize,\r
271 ResponseData\r
272 );\r
273 if (!EFI_ERROR (Status)) {\r
274 // Setting this value should cause us to go to the Status stage on the\r
275 // next EP0TX interrupt\r
276 mControlTxPending = TRUE;\r
277 }\r
278 }\r
279\r
280 return EFI_SUCCESS;\r
281}\r
282\r
283STATIC\r
284EFI_STATUS\r
285HandleSetAddress (\r
286 IN USB_DEVICE_REQUEST *Request\r
287 )\r
288{\r
289 // Pretty confused if bmRequestType is anything but this:\r
290 ASSERT (Request->RequestType == USB_DEV_SET_ADDRESS_REQ_TYPE);\r
291 // USB Spec: "The USB device does not change its device address until after\r
292 // the Status stage of this request is completed successfully."\r
293 // ISP1582 datasheet: "The new device address is activated when the\r
294 // device receives an acknowledgment from the host for the empty packet\r
295 // token". (StatusAcknowledge causes an empty packet to be sent).\r
296 // So, we write the Address register _before_ acking the SET_ADDRESS.\r
297 DEBUG ((EFI_D_INFO, "USB: Setting address to %d\n", Request->Value));\r
298 WRITE_REG32 (ISP1761_ADDRESS, Request->Value | ISP1761_ADDRESS_DEVEN);\r
299 StatusAcknowledge (ISP1761_EP0TX);\r
300\r
301 return EFI_SUCCESS;\r
302}\r
303\r
304// Move the device to the Configured state.\r
305// (This code only supports one configuration for a device, so the configuration\r
306// index is ignored)\r
307STATIC\r
308EFI_STATUS\r
309HandleSetConfiguration (\r
310 IN USB_DEVICE_REQUEST *Request\r
311 )\r
312{\r
313 USB_ENDPOINT_DESCRIPTOR *EPDesc;\r
314 UINTN Index;\r
315 UINT8 EndpointIndex;\r
316\r
317 ASSERT (Request->RequestType == USB_DEV_SET_CONFIGURATION_REQ_TYPE);\r
318 DEBUG ((EFI_D_INFO, "USB: Setting configuration.\n"));\r
319\r
320 // Configure endpoints\r
321 for (Index = 0; Index < mInterfaceDescriptor->NumEndpoints; Index++) {\r
322 EPDesc = &mEndpointDescriptors[Index];\r
323\r
324 // To simplify for now, assume endpoints aren't "sparse", and are in order.\r
325 ASSERT ((EPDesc->EndpointAddress & 0xF) == ((Index / 2) + 1));\r
326\r
327 // Convert from USB endpoint index to ISP1761 endpoint Index\r
328 // USB: Endpoint number is bits [3:0], IN/OUT is bit [7]\r
329 // ISP1761: Endpoint number is bits [4:1], IN/OUT is bit [0]\r
330 EndpointIndex = ((EPDesc->EndpointAddress & 0xF) << 1) |\r
331 ((EPDesc->EndpointAddress & BIT7) >> 7);\r
332 SelectEndpoint (EndpointIndex);\r
333 // Set endpoint type (Bulk/Isochronous/Interrupt)\r
334 WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, EPDesc->MaxPacketSize);\r
335 // Hardware foible (bug?): Although the datasheet seems to suggest it should\r
336 // automatically be set to MaxPacketSize, the Buffer Length register appears\r
337 // to be reset to 0, which causes an empty packet to be sent in response to\r
338 // the first IN token of the session. The NOEMPKT field of the Endpoint Type\r
339 // register sounds like it might fix this problem, but it doesn't\r
340 // (it's "applicable only in the DMA mode").\r
341 WRITE_REG32 (ISP1761_BUFFER_LENGTH, EPDesc->MaxPacketSize);\r
342 WRITE_REG32 (ISP1761_ENDPOINT_TYPE, (EPDesc->Attributes & 0x3) |\r
343 ISP1761_ENDPOINT_TYPE_ENABLE);\r
344 }\r
345\r
346 StatusAcknowledge (ISP1761_EP0TX);\r
347 return EFI_SUCCESS;\r
348}\r
349\r
350STATIC\r
351EFI_STATUS\r
352HandleDeviceRequest (\r
353 IN USB_DEVICE_REQUEST *Request\r
354 )\r
355{\r
356 EFI_STATUS Status;\r
357\r
358 Status = EFI_SUCCESS;\r
359\r
360 switch (Request->Request) {\r
361 case USB_DEV_GET_DESCRIPTOR:\r
362 Status = HandleGetDescriptor (Request);\r
363 break;\r
364 case USB_DEV_SET_ADDRESS:\r
365 Status = HandleSetAddress (Request);\r
366 break;\r
367 case USB_DEV_SET_CONFIGURATION:\r
368 Status = HandleSetConfiguration (Request);\r
369 break;\r
370 default:\r
371 DEBUG ((EFI_D_ERROR,\r
372 "Didn't understand RequestType 0x%x Request 0x%x\n",\r
373 Request->RequestType, Request->Request));\r
374 Status = EFI_INVALID_PARAMETER;\r
375 break;\r
376 }\r
377\r
378 return Status;\r
379}\r
380\r
381// Instead of actually registering interrupt handlers, we poll the controller's\r
382// interrupt source register in this function.\r
383STATIC\r
384VOID\r
385CheckInterrupts (\r
386 IN EFI_EVENT Event,\r
387 IN VOID *Context\r
388 )\r
389{\r
390 UINT32 DcInterrupts;\r
391 UINTN NumBytes;\r
392 UINTN MoreBytes;\r
393 UINT8 Packet[512];\r
394 VOID *DataPacket;\r
395 UINT32 HandledInterrupts;\r
396 UINT32 UnhandledInterrupts;\r
397 EFI_STATUS Status;\r
398\r
399 // Set bits in HandledInterrupts to mark the interrupt source handled.\r
400 HandledInterrupts = 0;\r
401\r
402 WRITE_REG32 (ISP1761_DEVICE_UNLOCK, ISP1761_DEVICE_UNLOCK_MAGIC);\r
403\r
404 DcInterrupts = READ_REG32 (ISP1761_DC_INTERRUPT);\r
405 if (DcInterrupts & ISP1761_DC_INTERRUPT_SUSP) {\r
406 DEBUG ((EFI_D_INFO, "USB: Suspend\n"));\r
407 HandledInterrupts |= ISP1761_DC_INTERRUPT_SUSP;\r
408 }\r
409 if (DcInterrupts & ISP1761_DC_INTERRUPT_RESUME) {\r
410 DEBUG ((EFI_D_INFO, "USB: Resume\n"));\r
411 HandledInterrupts |= ISP1761_DC_INTERRUPT_RESUME;\r
412 }\r
413 if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0SETUP) {\r
414 NumBytes = 512;\r
415 ReadEndpointBuffer (0x20, &NumBytes, &Packet);\r
416 ASSERT (NumBytes == 8);\r
417 HandleDeviceRequest ((USB_DEVICE_REQUEST *) Packet);\r
418 HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0SETUP;\r
419 }\r
420 if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0RX) {\r
421 HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0RX;\r
422 }\r
423 if (DcInterrupts & ISP1761_DC_INTERRUPT_EP0TX) {\r
424 if (mControlTxPending) {\r
425 // We previously put some data in the Control Endpoint's IN (Tx) FIFO.\r
426 // We assume that that data has now been sent in response to the IN token\r
427 // that triggered this interrupt. We can therefore go to the Status stage\r
428 // of the control transfer.\r
429 StatusAcknowledge (ISP1761_EP0TX);\r
430 mControlTxPending = FALSE;\r
431 }\r
432 HandledInterrupts |= ISP1761_DC_INTERRUPT_EP0TX;\r
433 }\r
434 if (DcInterrupts & ISP1761_DC_INTERRUPT_EP1RX) {\r
435 NumBytes = 512;\r
436 DataPacket = AllocatePool (NumBytes);\r
437 Status = ReadEndpointBuffer (ISP1761_EP1RX, &NumBytes, DataPacket);\r
438 if (EFI_ERROR (Status) || NumBytes == 0) {\r
439 if (EFI_ERROR (Status)) {\r
440 DEBUG ((EFI_D_ERROR, "Couldn't read EP1RX data: %r\n", Status));\r
441 }\r
442 FreePool (DataPacket);\r
443 } else {\r
444 // Signal this event again so we poll again ASAP\r
445 gBS->SignalEvent (Event);\r
446 mDataReceivedCallback (NumBytes, DataPacket);\r
447 }\r
448 HandledInterrupts |= ISP1761_DC_INTERRUPT_EP1RX;\r
449 }\r
450 if (DcInterrupts & ISP1761_DC_INTERRUPT_EP1TX) {\r
451 mDataSentCallback (1);\r
452 HandledInterrupts |= ISP1761_DC_INTERRUPT_EP1TX;\r
453 }\r
454 if (DcInterrupts & (ISP1761_DC_INTERRUPT_SOF | ISP1761_DC_INTERRUPT_PSOF)) {\r
455 // Don't care about SOFs or pseudo-SOFs\r
456 HandledInterrupts |= (ISP1761_DC_INTERRUPT_SOF | ISP1761_DC_INTERRUPT_PSOF);\r
457 }\r
458 if (ISP1761_DC_INTERRUPT_BRESET) {\r
459 HandledInterrupts |= ISP1761_DC_INTERRUPT_BRESET;\r
460 }\r
461 if (ISP1761_DC_INTERRUPT_HS_STAT) {\r
462 HandledInterrupts |= ISP1761_DC_INTERRUPT_HS_STAT;\r
463 }\r
464 if (ISP1761_DC_INTERRUPT_VBUS) {\r
465 HandledInterrupts |= ISP1761_DC_INTERRUPT_VBUS;\r
466 }\r
467\r
468 UnhandledInterrupts = DcInterrupts & (~HandledInterrupts) & ISP1761_DC_INTERRUPT_MASK;\r
469 if (UnhandledInterrupts) {\r
470 DEBUG ((EFI_D_ERROR, "USB: Unhandled DC Interrupts: 0x%08x\n",\r
471 UnhandledInterrupts));\r
472 }\r
473\r
474 // Check if we received any more data while we were handling the interrupt.\r
475 SelectEndpoint (ISP1761_EP1RX);\r
476 MoreBytes = READ_REG16 (ISP1761_BUFFER_LENGTH);\r
477 if (MoreBytes) {\r
478 HandledInterrupts &= ~ISP1761_DC_INTERRUPT_EP1RX;\r
479 }\r
480\r
481 WRITE_REG32 (ISP1761_DC_INTERRUPT, HandledInterrupts);\r
482}\r
483\r
484EFI_STATUS\r
485Isp1761PeriphSend (\r
486 IN UINT8 EndpointIndex,\r
487 IN UINTN Size,\r
488 IN CONST VOID *Buffer\r
489 )\r
490{\r
491 return WriteEndpointBuffer (\r
492 (EndpointIndex << 1) | 0x1, //Convert to ISP1761 endpoint index, Tx\r
493 MAX_PACKET_SIZE_BULK,\r
494 Size,\r
495 Buffer\r
496 );\r
497}\r
498\r
499EFI_STATUS\r
500EFIAPI\r
501Isp1761PeriphStart (\r
502 IN USB_DEVICE_DESCRIPTOR *DeviceDescriptor,\r
503 IN VOID **Descriptors,\r
504 IN USB_DEVICE_RX_CALLBACK RxCallback,\r
505 IN USB_DEVICE_TX_CALLBACK TxCallback\r
506 )\r
507{\r
508 UINT16 OtgStatus;\r
509 UINT8 *Ptr;\r
510 EFI_STATUS Status;\r
511 EFI_EVENT TimerEvent;\r
512\r
513 ASSERT (DeviceDescriptor != NULL);\r
514 ASSERT (Descriptors[0] != NULL);\r
515 ASSERT (RxCallback != NULL);\r
516 ASSERT (TxCallback != NULL);\r
517\r
518 WRITE_REG32 (ISP1761_DEVICE_UNLOCK, ISP1761_DEVICE_UNLOCK_MAGIC);\r
519\r
520 WRITE_REG32 (ISP1761_SW_RESET_REG, ISP1761_SW_RESET_ALL);\r
521 while (READ_REG32 (ISP1761_SW_RESET_REG) & ISP1761_SW_RESET_ALL) {\r
522 //busy wait\r
523 }\r
524 WRITE_REG32 (ISP1761_MODE, ISP1761_MODE_SFRESET);\r
525 while (READ_REG32 (ISP1761_MODE) & ISP1761_MODE_SFRESET) {\r
526 //busy wait\r
527 }\r
528 DEBUG ((EFI_D_INFO, "USB: Software reset done\n"));\r
529\r
530 WRITE_REG32 (ISP1761_DC_INTERRUPT_ENABLE, 0x03FFFFFF);\r
531 WRITE_REG32 (ISP1761_OTG_INTERRUPT_ENABLE_RISE, 0x07FF);\r
532\r
533 WRITE_REG8 (ISP1761_ADDRESS, ISP1761_ADDRESS_DEVEN);\r
534 WRITE_REG8 (ISP1761_MODE, ISP1761_MODE_WKUPCS | ISP1761_MODE_CLKAON);\r
535\r
536 // Use port 1 as peripheral controller (magic - disagrees with datasheet)\r
537 WRITE_REG32 (ISP1761_OTG_CTRL_SET, 0xffff0000);\r
538 WRITE_REG32 (ISP1761_OTG_CTRL_SET, 0x000014d1);\r
539\r
540 OtgStatus = READ_REG16 (ISP1761_OTG_STATUS);\r
541 if ((OtgStatus & ISP1761_OTG_STATUS_B_SESS_END) != 0) {\r
542 DEBUG ((EFI_D_ERROR, "USB: Vbus not powered.\n"));\r
543 }\r
544 if ((OtgStatus & ISP1761_OTG_STATUS_A_B_SESS_VLD) == 0) {\r
545 DEBUG ((EFI_D_ERROR, "USB: Session not valid.\n"));\r
546 }\r
547\r
548 // Configure Control endpoints\r
549 SelectEndpoint (0x20);\r
550 WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);\r
551 WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);\r
552 SelectEndpoint (0x0);\r
553 WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);\r
554 WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);\r
555 SelectEndpoint (0x1);\r
556 WRITE_REG32 (ISP1761_ENDPOINT_MAX_PACKET_SIZE, MAX_PACKET_SIZE_CONTROL);\r
557 WRITE_REG32 (ISP1761_ENDPOINT_TYPE, ISP1761_ENDPOINT_TYPE_ENABLE);\r
558\r
559 // Interrupt on all ACK and NAK\r
560 WRITE_REG32 (ISP1761_INTERRUPT_CONFIG, ISP1761_INTERRUPT_CONFIG_ACK_ONLY);\r
561\r
562 mDeviceDescriptor = DeviceDescriptor;\r
563 mDescriptors = Descriptors[0];\r
564\r
565 // Right now we just support one configuration\r
566 ASSERT (mDeviceDescriptor->NumConfigurations == 1);\r
567 // ... and one interface\r
568 mConfigDescriptor = (USB_CONFIG_DESCRIPTOR *)mDescriptors;\r
569 ASSERT (mConfigDescriptor->NumInterfaces == 1);\r
570\r
571 Ptr = ((UINT8 *) mDescriptors) + sizeof (USB_CONFIG_DESCRIPTOR);\r
572 mInterfaceDescriptor = (USB_INTERFACE_DESCRIPTOR *) Ptr;\r
573 Ptr += sizeof (USB_INTERFACE_DESCRIPTOR);\r
574\r
575 mEndpointDescriptors = (USB_ENDPOINT_DESCRIPTOR *) Ptr;\r
576\r
577 mDataReceivedCallback = RxCallback;\r
578 mDataSentCallback = TxCallback;\r
579\r
580 // Register a timer event so CheckInterupts gets called periodically\r
581 Status = gBS->CreateEvent (\r
582 EVT_TIMER | EVT_NOTIFY_SIGNAL,\r
583 TPL_CALLBACK,\r
584 CheckInterrupts,\r
585 NULL,\r
586 &TimerEvent\r
587 );\r
588 ASSERT_EFI_ERROR (Status);\r
589 if (EFI_ERROR (Status)) {\r
590 return Status;\r
591 }\r
592\r
593 Status = gBS->SetTimer (\r
594 TimerEvent,\r
595 TimerPeriodic,\r
596 ISP1761_INTERRUPT_POLL_PERIOD\r
597 );\r
598 ASSERT_EFI_ERROR (Status);\r
599\r
600 return Status;\r
601}\r
602\r
603USB_DEVICE_PROTOCOL mUsbDevice = {\r
604 Isp1761PeriphStart,\r
605 Isp1761PeriphSend\r
606};\r
607\r
608\r
609EFI_STATUS\r
610EFIAPI\r
611Isp1761PeriphEntryPoint (\r
612 IN EFI_HANDLE ImageHandle,\r
613 IN EFI_SYSTEM_TABLE *SystemTable\r
614 )\r
615{\r
616 UINT32 DeviceId;\r
617 EFI_HANDLE Handle;\r
618\r
619 DeviceId = READ_REG32 (ISP1761_DEVICE_ID);\r
620\r
621 if (DeviceId != ISP1761_DEVICE_ID_VAL) {\r
622 DEBUG ((EFI_D_ERROR,\r
623 "ERROR: Read incorrect device ID for ISP1761: 0x%08x, expected 0x%08x\n",\r
624 DeviceId , ISP1761_DEVICE_ID_VAL\r
625 ));\r
626 return EFI_DEVICE_ERROR;\r
627 }\r
628\r
629 Handle = NULL;\r
630 return gBS->InstallProtocolInterface (\r
631 &Handle,\r
632 &gUsbDeviceProtocolGuid,\r
633 EFI_NATIVE_INTERFACE,\r
634 &mUsbDevice\r
635 );\r
636}\r