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