Upgraded GRUB2 to 2.00 release.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / bus / usb / uhci.c
blob8f60850de485bb62e0d66d3f58eb1390abc1f040
1 /* uhci.c - UHCI Support. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2008 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/dl.h>
21 #include <grub/mm.h>
22 #include <grub/misc.h>
23 #include <grub/usb.h>
24 #include <grub/usbtrans.h>
25 #include <grub/pci.h>
26 #include <grub/cpu/io.h>
27 #include <grub/time.h>
28 #include <grub/cpu/pci.h>
30 GRUB_MOD_LICENSE ("GPLv3+");
32 #define GRUB_UHCI_IOMASK (0x7FF << 5)
34 #define N_QH 256
35 #define N_TD 640
37 typedef enum
39 GRUB_UHCI_REG_USBCMD = 0x00,
40 GRUB_UHCI_REG_USBINTR = 0x04,
41 GRUB_UHCI_REG_FLBASEADD = 0x08,
42 GRUB_UHCI_REG_PORTSC1 = 0x10,
43 GRUB_UHCI_REG_PORTSC2 = 0x12,
44 GRUB_UHCI_REG_USBLEGSUP = 0xc0
45 } grub_uhci_reg_t;
47 /* R/WC legacy support bits */
48 #define GRUB_UHCI_LEGSUP_END_A20GATE (1 << 15)
49 #define GRUB_UHCI_TRAP_BY_64H_WSTAT (1 << 11)
50 #define GRUB_UHCI_TRAP_BY_64H_RSTAT (1 << 10)
51 #define GRUB_UHCI_TRAP_BY_60H_WSTAT (1 << 9)
52 #define GRUB_UHCI_TRAP_BY_60H_RSTAT (1 << 8)
54 /* Reset all legacy support - clear all R/WC bits and all R/W bits */
55 #define GRUB_UHCI_RESET_LEGSUP_SMI ( GRUB_UHCI_LEGSUP_END_A20GATE \
56 | GRUB_UHCI_TRAP_BY_64H_WSTAT \
57 | GRUB_UHCI_TRAP_BY_64H_RSTAT \
58 | GRUB_UHCI_TRAP_BY_60H_WSTAT \
59 | GRUB_UHCI_TRAP_BY_60H_RSTAT )
61 /* Some UHCI commands */
62 #define GRUB_UHCI_CMD_RUN_STOP (1 << 0)
63 #define GRUB_UHCI_CMD_HCRESET (1 << 1)
64 #define GRUB_UHCI_CMD_MAXP (1 << 7)
66 /* Important bits in structures */
67 #define GRUB_UHCI_LINK_TERMINATE 1
68 #define GRUB_UHCI_LINK_QUEUE_HEAD 2
70 enum
72 GRUB_UHCI_REG_PORTSC_CONNECT_CHANGED = 0x0002,
73 GRUB_UHCI_REG_PORTSC_PORT_ENABLED = 0x0004,
74 GRUB_UHCI_REG_PORTSC_RESUME = 0x0040,
75 GRUB_UHCI_REG_PORTSC_RESET = 0x0200,
76 GRUB_UHCI_REG_PORTSC_SUSPEND = 0x1000,
77 GRUB_UHCI_REG_PORTSC_RW = GRUB_UHCI_REG_PORTSC_PORT_ENABLED
78 | GRUB_UHCI_REG_PORTSC_RESUME | GRUB_UHCI_REG_PORTSC_RESET
79 | GRUB_UHCI_REG_PORTSC_SUSPEND,
80 /* These bits should not be written as 1 unless we really need it */
81 GRUB_UHCI_PORTSC_RWC = ((1 << 1) | (1 << 3) | (1 << 11) | (3 << 13))
84 /* UHCI Queue Head. */
85 struct grub_uhci_qh
87 /* Queue head link pointer which points to the next queue head. */
88 grub_uint32_t linkptr;
90 /* Queue element link pointer which points to the first data object
91 within the queue. */
92 grub_uint32_t elinkptr;
94 /* Queue heads are aligned on 16 bytes, pad so a queue head is 16
95 bytes so we can store many in a 4K page. */
96 grub_uint8_t pad[8];
97 } __attribute__ ((packed));
99 /* UHCI Transfer Descriptor. */
100 struct grub_uhci_td
102 /* Pointer to the next TD in the list. */
103 grub_uint32_t linkptr;
105 /* Control and status bits. */
106 grub_uint32_t ctrl_status;
108 /* All information required to transfer the Token packet. */
109 grub_uint32_t token;
111 /* A pointer to the data buffer, UHCI requires this pointer to be 32
112 bits. */
113 grub_uint32_t buffer;
115 /* Another linkptr that is not overwritten by the Host Controller.
116 This is GRUB specific. */
117 grub_uint32_t linkptr2;
119 /* 3 additional 32 bits words reserved for the Host Controller Driver. */
120 grub_uint32_t data[3];
121 } __attribute__ ((packed));
123 typedef volatile struct grub_uhci_td *grub_uhci_td_t;
124 typedef volatile struct grub_uhci_qh *grub_uhci_qh_t;
126 struct grub_uhci
128 int iobase;
129 volatile grub_uint32_t *framelist_virt;
130 grub_uint32_t framelist_phys;
131 struct grub_pci_dma_chunk *framelist_chunk;
133 /* N_QH Queue Heads. */
134 struct grub_pci_dma_chunk *qh_chunk;
135 volatile grub_uhci_qh_t qh_virt;
136 grub_uint32_t qh_phys;
138 /* N_TD Transfer Descriptors. */
139 struct grub_pci_dma_chunk *td_chunk;
140 volatile grub_uhci_td_t td_virt;
141 grub_uint32_t td_phys;
143 /* Free Transfer Descriptors. */
144 grub_uhci_td_t tdfree;
146 int qh_busy[N_QH];
148 struct grub_uhci *next;
151 static struct grub_uhci *uhci;
153 static grub_uint16_t
154 grub_uhci_readreg16 (struct grub_uhci *u, grub_uhci_reg_t reg)
156 return grub_inw (u->iobase + reg);
159 #if 0
160 static grub_uint32_t
161 grub_uhci_readreg32 (struct grub_uhci *u, grub_uhci_reg_t reg)
163 return grub_inl (u->iobase + reg);
165 #endif
167 static void
168 grub_uhci_writereg16 (struct grub_uhci *u,
169 grub_uhci_reg_t reg, grub_uint16_t val)
171 grub_outw (val, u->iobase + reg);
174 static void
175 grub_uhci_writereg32 (struct grub_uhci *u,
176 grub_uhci_reg_t reg, grub_uint32_t val)
178 grub_outl (val, u->iobase + reg);
181 static grub_err_t
182 grub_uhci_portstatus (grub_usb_controller_t dev,
183 unsigned int port, unsigned int enable);
186 /* Iterate over all PCI devices. Determine if a device is an UHCI
187 controller. If this is the case, initialize it. */
188 static int NESTED_FUNC_ATTR
189 grub_uhci_pci_iter (grub_pci_device_t dev,
190 grub_pci_id_t pciid __attribute__((unused)))
192 grub_uint32_t class_code;
193 grub_uint32_t class;
194 grub_uint32_t subclass;
195 grub_uint32_t interf;
196 grub_uint32_t base;
197 grub_uint32_t fp;
198 grub_pci_address_t addr;
199 struct grub_uhci *u;
200 int i;
202 addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
203 class_code = grub_pci_read (addr) >> 8;
205 interf = class_code & 0xFF;
206 subclass = (class_code >> 8) & 0xFF;
207 class = class_code >> 16;
209 /* If this is not an UHCI controller, just return. */
210 if (class != 0x0c || subclass != 0x03 || interf != 0x00)
211 return 0;
213 /* Set bus master - needed for coreboot or broken BIOSes */
214 addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
215 grub_pci_write_word(addr,
216 GRUB_PCI_COMMAND_BUS_MASTER | grub_pci_read_word(addr));
218 /* Determine IO base address. */
219 addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG4);
220 base = grub_pci_read (addr);
221 /* Stop if there is no IO space base address defined. */
222 if (! (base & 1))
223 return 0;
225 /* Allocate memory for the controller and register it. */
226 u = grub_zalloc (sizeof (*u));
227 if (! u)
228 return 1;
230 u->iobase = base & GRUB_UHCI_IOMASK;
232 /* Reset PIRQ and SMI */
233 addr = grub_pci_make_address (dev, GRUB_UHCI_REG_USBLEGSUP);
234 grub_pci_write_word(addr, GRUB_UHCI_RESET_LEGSUP_SMI);
235 /* Reset the HC */
236 grub_uhci_writereg16(u, GRUB_UHCI_REG_USBCMD, GRUB_UHCI_CMD_HCRESET);
237 grub_millisleep(5);
238 /* Disable interrupts and commands (just to be safe) */
239 grub_uhci_writereg16(u, GRUB_UHCI_REG_USBINTR, 0);
240 /* Finish HC reset, HC remains disabled */
241 grub_uhci_writereg16(u, GRUB_UHCI_REG_USBCMD, 0);
242 /* Read back to be sure PCI write is done */
243 grub_uhci_readreg16(u, GRUB_UHCI_REG_USBCMD);
245 /* Reserve a page for the frame list. */
246 u->framelist_chunk = grub_memalign_dma32 (4096, 4096);
247 if (! u->framelist_chunk)
248 goto fail;
249 u->framelist_virt = grub_dma_get_virt (u->framelist_chunk);
250 u->framelist_phys = grub_dma_get_phys (u->framelist_chunk);
252 grub_dprintf ("uhci",
253 "class=0x%02x 0x%02x interface 0x%02x base=0x%x framelist=%p\n",
254 class, subclass, interf, u->iobase, u->framelist_virt);
256 /* The QH pointer of UHCI is only 32 bits, make sure this
257 code works on on 64 bits architectures. */
258 u->qh_chunk = grub_memalign_dma32 (4096, sizeof(struct grub_uhci_qh) * N_QH);
259 if (! u->qh_chunk)
260 goto fail;
261 u->qh_virt = grub_dma_get_virt (u->qh_chunk);
262 u->qh_phys = grub_dma_get_phys (u->qh_chunk);
264 /* The TD pointer of UHCI is only 32 bits, make sure this
265 code works on on 64 bits architectures. */
266 u->td_chunk = grub_memalign_dma32 (4096, sizeof(struct grub_uhci_td) * N_TD);
267 if (! u->td_chunk)
268 goto fail;
269 u->td_virt = grub_dma_get_virt (u->td_chunk);
270 u->td_phys = grub_dma_get_phys (u->td_chunk);
272 grub_dprintf ("uhci", "QH=%p, TD=%p\n",
273 u->qh_virt, u->td_virt);
275 /* Link all Transfer Descriptors in a list of available Transfer
276 Descriptors. */
277 for (i = 0; i < N_TD; i++)
278 u->td_virt[i].linkptr = u->td_phys + (i + 1) * sizeof(struct grub_uhci_td);
279 u->td_virt[N_TD - 2].linkptr = 0;
280 u->tdfree = u->td_virt;
282 /* Setup the frame list pointers. Since no isochronous transfers
283 are and will be supported, they all point to the (same!) queue
284 head. */
285 fp = u->qh_phys & (~15);
286 /* Mark this as a queue head. */
287 fp |= 2;
288 for (i = 0; i < 1024; i++)
289 u->framelist_virt[i] = fp;
290 /* Program the framelist address into the UHCI controller. */
291 grub_uhci_writereg32 (u, GRUB_UHCI_REG_FLBASEADD, u->framelist_phys);
293 /* Make the Queue Heads point to each other. */
294 for (i = 0; i < N_QH; i++)
296 /* Point to the next QH. */
297 u->qh_virt[i].linkptr = ((u->qh_phys
298 + (i + 1) * sizeof(struct grub_uhci_qh))
299 & (~15));
301 /* This is a QH. */
302 u->qh_virt[i].linkptr |= GRUB_UHCI_LINK_QUEUE_HEAD;
304 /* For the moment, do not point to a Transfer Descriptor. These
305 are set at transfer time, so just terminate it. */
306 u->qh_virt[i].elinkptr = 1;
309 /* The last Queue Head should terminate. */
310 u->qh_virt[N_QH - 1].linkptr = 1;
312 /* Enable UHCI again. */
313 grub_uhci_writereg16 (u, GRUB_UHCI_REG_USBCMD,
314 GRUB_UHCI_CMD_RUN_STOP | GRUB_UHCI_CMD_MAXP);
316 /* UHCI is initialized and ready for transfers. */
317 grub_dprintf ("uhci", "UHCI initialized\n");
320 #if 0
322 int i;
323 for (i = 0; i < 10; i++)
325 grub_uint16_t frnum;
327 frnum = grub_uhci_readreg16 (u, 6);
328 grub_dprintf ("uhci", "Framenum=%d\n", frnum);
329 grub_millisleep (100);
332 #endif
334 /* Link to uhci now that initialisation is successful. */
335 u->next = uhci;
336 uhci = u;
338 return 0;
340 fail:
341 if (u)
343 grub_dma_free (u->qh_chunk);
344 grub_dma_free (u->framelist_chunk);
346 grub_free (u);
348 return 1;
351 static void
352 grub_uhci_inithw (void)
354 grub_pci_iterate (grub_uhci_pci_iter);
357 static grub_uhci_td_t
358 grub_alloc_td (struct grub_uhci *u)
360 grub_uhci_td_t ret;
362 /* Check if there is a Transfer Descriptor available. */
363 if (! u->tdfree)
364 return NULL;
366 ret = u->tdfree;
367 u->tdfree = grub_dma_phys2virt (u->tdfree->linkptr, u->td_chunk);
369 return ret;
372 static void
373 grub_free_td (struct grub_uhci *u, grub_uhci_td_t td)
375 td->linkptr = grub_dma_virt2phys (u->tdfree, u->td_chunk);
376 u->tdfree = td;
379 static void
380 grub_free_queue (struct grub_uhci *u, grub_uhci_qh_t qh, grub_uhci_td_t td,
381 grub_usb_transfer_t transfer, grub_size_t *actual)
383 int i; /* Index of TD in transfer */
385 u->qh_busy[qh - u->qh_virt] = 0;
387 *actual = 0;
389 /* Free the TDs in this queue and set last_trans. */
390 for (i=0; td; i++)
392 grub_uhci_td_t tdprev;
394 /* Check state of TD and possibly set last_trans */
395 if (transfer && (td->linkptr & 1))
396 transfer->last_trans = i;
398 *actual += (td->ctrl_status + 1) & 0x7ff;
400 /* Unlink the queue. */
401 tdprev = td;
402 td = grub_dma_phys2virt (td->linkptr2, u->td_chunk);
404 /* Free the TD. */
405 grub_free_td (u, tdprev);
409 static grub_uhci_qh_t
410 grub_alloc_qh (struct grub_uhci *u,
411 grub_transaction_type_t tr __attribute__((unused)))
413 int i;
414 grub_uhci_qh_t qh;
416 /* Look for a Queue Head for this transfer. Skip the first QH if
417 this is a Interrupt Transfer. */
418 #if 0
419 if (tr == GRUB_USB_TRANSACTION_TYPE_INTERRUPT)
420 i = 0;
421 else
422 #endif
423 i = 1;
425 for (; i < N_QH; i++)
427 if (!u->qh_busy[i])
428 break;
430 qh = &u->qh_virt[i];
431 if (i == N_QH)
433 grub_error (GRUB_ERR_OUT_OF_MEMORY,
434 "no free queue heads available");
435 return NULL;
438 u->qh_busy[qh - u->qh_virt] = 1;
440 return qh;
443 static grub_uhci_td_t
444 grub_uhci_transaction (struct grub_uhci *u, unsigned int endp,
445 grub_transfer_type_t type, unsigned int addr,
446 unsigned int toggle, grub_size_t size,
447 grub_uint32_t data, grub_usb_speed_t speed)
449 grub_uhci_td_t td;
450 static const unsigned int tf[] = { 0x69, 0xE1, 0x2D };
452 /* XXX: Check if data is <4GB. If it isn't, just copy stuff around.
453 This is only relevant for 64 bits architectures. */
455 /* Grab a free Transfer Descriptor and initialize it. */
456 td = grub_alloc_td (u);
457 if (! td)
459 grub_error (GRUB_ERR_OUT_OF_MEMORY,
460 "no transfer descriptors available for UHCI transfer");
461 return 0;
464 grub_dprintf ("uhci",
465 "transaction: endp=%d, type=%d, addr=%d, toggle=%d, size=%lu data=0x%x td=%p\n",
466 endp, type, addr, toggle, (unsigned long) size, data, td);
468 /* Don't point to any TD, just terminate. */
469 td->linkptr = 1;
471 /* Active! Only retry a transfer 3 times. */
472 td->ctrl_status = (1 << 23) | (3 << 27) |
473 ((speed == GRUB_USB_SPEED_LOW) ? (1 << 26) : 0);
475 /* If zero bytes are transmitted, size is 0x7FF. Otherwise size is
476 size-1. */
477 if (size == 0)
478 size = 0x7FF;
479 else
480 size = size - 1;
482 /* Setup whatever is required for the token packet. */
483 td->token = ((size << 21) | (toggle << 19) | (endp << 15)
484 | (addr << 8) | tf[type]);
486 td->buffer = data;
488 return td;
491 struct grub_uhci_transfer_controller_data
493 grub_uhci_qh_t qh;
494 grub_uhci_td_t td_first;
497 static grub_usb_err_t
498 grub_uhci_setup_transfer (grub_usb_controller_t dev,
499 grub_usb_transfer_t transfer)
501 struct grub_uhci *u = (struct grub_uhci *) dev->data;
502 grub_uhci_td_t td;
503 grub_uhci_td_t td_prev = NULL;
504 int i;
505 struct grub_uhci_transfer_controller_data *cdata;
507 cdata = grub_malloc (sizeof (*cdata));
508 if (!cdata)
509 return GRUB_USB_ERR_INTERNAL;
511 cdata->td_first = NULL;
513 /* Allocate a queue head for the transfer queue. */
514 cdata->qh = grub_alloc_qh (u, GRUB_USB_TRANSACTION_TYPE_CONTROL);
515 if (! cdata->qh)
517 grub_free (cdata);
518 return GRUB_USB_ERR_INTERNAL;
521 grub_dprintf ("uhci", "transfer, iobase:%08x\n", u->iobase);
523 for (i = 0; i < transfer->transcnt; i++)
525 grub_usb_transaction_t tr = &transfer->transactions[i];
527 td = grub_uhci_transaction (u, transfer->endpoint & 15, tr->pid,
528 transfer->devaddr, tr->toggle,
529 tr->size, tr->data,
530 transfer->dev->speed);
531 if (! td)
533 grub_size_t actual = 0;
534 /* Terminate and free. */
535 if (td_prev)
537 td_prev->linkptr2 = 0;
538 td_prev->linkptr = 1;
541 if (cdata->td_first)
542 grub_free_queue (u, cdata->qh, cdata->td_first, NULL, &actual);
544 grub_free (cdata);
545 return GRUB_USB_ERR_INTERNAL;
548 if (! cdata->td_first)
549 cdata->td_first = td;
550 else
552 td_prev->linkptr2 = grub_dma_virt2phys (td, u->td_chunk);
553 td_prev->linkptr = grub_dma_virt2phys (td, u->td_chunk);
554 td_prev->linkptr |= 4;
556 td_prev = td;
558 td_prev->linkptr2 = 0;
559 td_prev->linkptr = 1;
561 grub_dprintf ("uhci", "setup transaction %d\n", transfer->type);
563 /* Link it into the queue and terminate. Now the transaction can
564 take place. */
565 cdata->qh->elinkptr = grub_dma_virt2phys (cdata->td_first, u->td_chunk);
567 grub_dprintf ("uhci", "initiate transaction\n");
569 transfer->controller_data = cdata;
571 return GRUB_USB_ERR_NONE;
574 static grub_usb_err_t
575 grub_uhci_check_transfer (grub_usb_controller_t dev,
576 grub_usb_transfer_t transfer,
577 grub_size_t *actual)
579 struct grub_uhci *u = (struct grub_uhci *) dev->data;
580 grub_uhci_td_t errtd;
581 struct grub_uhci_transfer_controller_data *cdata = transfer->controller_data;
583 *actual = 0;
585 errtd = grub_dma_phys2virt (cdata->qh->elinkptr & ~0x0f, u->qh_chunk);
587 grub_dprintf ("uhci", ">t status=0x%02x data=0x%02x td=%p\n",
588 errtd->ctrl_status, errtd->buffer & (~15), errtd);
590 /* Check if the transaction completed. */
591 if (cdata->qh->elinkptr & 1)
593 grub_dprintf ("uhci", "transaction complete\n");
595 /* Place the QH back in the free list and deallocate the associated
596 TDs. */
597 cdata->qh->elinkptr = 1;
598 grub_free_queue (u, cdata->qh, cdata->td_first, transfer, actual);
599 grub_free (cdata);
600 return GRUB_USB_ERR_NONE;
603 grub_dprintf ("uhci", "t status=0x%02x\n", errtd->ctrl_status);
605 if (!(errtd->ctrl_status & (1 << 23)))
607 grub_usb_err_t err = GRUB_USB_ERR_NONE;
609 /* Check if the endpoint is stalled. */
610 if (errtd->ctrl_status & (1 << 22))
611 err = GRUB_USB_ERR_STALL;
613 /* Check if an error related to the data buffer occurred. */
614 else if (errtd->ctrl_status & (1 << 21))
615 err = GRUB_USB_ERR_DATA;
617 /* Check if a babble error occurred. */
618 else if (errtd->ctrl_status & (1 << 20))
619 err = GRUB_USB_ERR_BABBLE;
621 /* Check if a NAK occurred. */
622 else if (errtd->ctrl_status & (1 << 19))
623 err = GRUB_USB_ERR_NAK;
625 /* Check if a timeout occurred. */
626 else if (errtd->ctrl_status & (1 << 18))
627 err = GRUB_USB_ERR_TIMEOUT;
629 /* Check if a bitstuff error occurred. */
630 else if (errtd->ctrl_status & (1 << 17))
631 err = GRUB_USB_ERR_BITSTUFF;
633 if (err)
635 grub_dprintf ("uhci", "transaction failed\n");
637 /* Place the QH back in the free list and deallocate the associated
638 TDs. */
639 cdata->qh->elinkptr = 1;
640 grub_free_queue (u, cdata->qh, cdata->td_first, transfer, actual);
641 grub_free (cdata);
643 return err;
647 /* Fall through, no errors occurred, so the QH might be
648 updated. */
649 grub_dprintf ("uhci", "transaction fallthrough\n");
651 return GRUB_USB_ERR_WAIT;
654 static grub_usb_err_t
655 grub_uhci_cancel_transfer (grub_usb_controller_t dev,
656 grub_usb_transfer_t transfer)
658 struct grub_uhci *u = (struct grub_uhci *) dev->data;
659 grub_size_t actual;
660 struct grub_uhci_transfer_controller_data *cdata = transfer->controller_data;
662 grub_dprintf ("uhci", "transaction cancel\n");
664 /* Place the QH back in the free list and deallocate the associated
665 TDs. */
666 cdata->qh->elinkptr = 1;
667 grub_free_queue (u, cdata->qh, cdata->td_first, transfer, &actual);
668 grub_free (cdata);
670 return GRUB_USB_ERR_NONE;
673 static int
674 grub_uhci_iterate (int (*hook) (grub_usb_controller_t dev))
676 struct grub_uhci *u;
677 struct grub_usb_controller dev;
679 for (u = uhci; u; u = u->next)
681 dev.data = u;
682 if (hook (&dev))
683 return 1;
686 return 0;
689 static grub_err_t
690 grub_uhci_portstatus (grub_usb_controller_t dev,
691 unsigned int port, unsigned int enable)
693 struct grub_uhci *u = (struct grub_uhci *) dev->data;
694 int reg;
695 unsigned int status;
696 grub_uint64_t endtime;
698 grub_dprintf ("uhci", "portstatus, iobase:%08x\n", u->iobase);
700 grub_dprintf ("uhci", "enable=%d port=%d\n", enable, port);
702 if (port == 0)
703 reg = GRUB_UHCI_REG_PORTSC1;
704 else if (port == 1)
705 reg = GRUB_UHCI_REG_PORTSC2;
706 else
707 return grub_error (GRUB_ERR_OUT_OF_RANGE,
708 "UHCI Root Hub port does not exist");
710 status = grub_uhci_readreg16 (u, reg);
711 grub_dprintf ("uhci", "detect=0x%02x\n", status);
713 if (!enable) /* We don't need reset port */
715 /* Disable the port. */
716 grub_uhci_writereg16 (u, reg, 0 << 2);
717 grub_dprintf ("uhci", "waiting for the port to be disabled\n");
718 endtime = grub_get_time_ms () + 1000;
719 while ((grub_uhci_readreg16 (u, reg) & (1 << 2)))
720 if (grub_get_time_ms () > endtime)
721 return grub_error (GRUB_ERR_IO, "UHCI Timed out - disable");
723 status = grub_uhci_readreg16 (u, reg);
724 grub_dprintf ("uhci", ">3detect=0x%02x\n", status);
725 return GRUB_ERR_NONE;
728 /* Reset the port. */
729 status = grub_uhci_readreg16 (u, reg) & ~GRUB_UHCI_PORTSC_RWC;
730 grub_uhci_writereg16 (u, reg, status | (1 << 9));
731 grub_uhci_readreg16 (u, reg); /* Ensure it is writen... */
733 /* Wait for the reset to complete. XXX: How long exactly? */
734 grub_millisleep (50); /* For root hub should be nominaly 50ms */
735 status = grub_uhci_readreg16 (u, reg) & ~GRUB_UHCI_PORTSC_RWC;
736 grub_uhci_writereg16 (u, reg, status & ~(1 << 9));
737 grub_uhci_readreg16 (u, reg); /* Ensure it is writen... */
739 /* Note: some debug prints were removed because they affected reset/enable timing. */
741 grub_millisleep (1); /* Probably not needed at all or only few microsecs. */
743 /* Reset bits Connect & Enable Status Change */
744 status = grub_uhci_readreg16 (u, reg) & ~GRUB_UHCI_PORTSC_RWC;
745 grub_uhci_writereg16 (u, reg, status | (1 << 3) | GRUB_UHCI_REG_PORTSC_CONNECT_CHANGED);
746 grub_uhci_readreg16 (u, reg); /* Ensure it is writen... */
748 /* Enable the port. */
749 status = grub_uhci_readreg16 (u, reg) & ~GRUB_UHCI_PORTSC_RWC;
750 grub_uhci_writereg16 (u, reg, status | (1 << 2));
751 grub_uhci_readreg16 (u, reg); /* Ensure it is writen... */
753 endtime = grub_get_time_ms () + 1000;
754 while (! ((status = grub_uhci_readreg16 (u, reg)) & (1 << 2)))
755 if (grub_get_time_ms () > endtime)
756 return grub_error (GRUB_ERR_IO, "UHCI Timed out - enable");
758 /* Reset recovery time */
759 grub_millisleep (10);
761 /* Read final port status */
762 status = grub_uhci_readreg16 (u, reg);
763 grub_dprintf ("uhci", ">3detect=0x%02x\n", status);
766 return GRUB_ERR_NONE;
769 static grub_usb_speed_t
770 grub_uhci_detect_dev (grub_usb_controller_t dev, int port, int *changed)
772 struct grub_uhci *u = (struct grub_uhci *) dev->data;
773 int reg;
774 unsigned int status;
776 grub_dprintf ("uhci", "detect_dev, iobase:%08x\n", u->iobase);
778 if (port == 0)
779 reg = GRUB_UHCI_REG_PORTSC1;
780 else if (port == 1)
781 reg = GRUB_UHCI_REG_PORTSC2;
782 else
783 return GRUB_USB_SPEED_NONE;
785 status = grub_uhci_readreg16 (u, reg);
787 grub_dprintf ("uhci", "detect=0x%02x port=%d\n", status, port);
789 /* Connect Status Change bit - it detects change of connection */
790 if (status & (1 << 1))
792 *changed = 1;
793 /* Reset bit Connect Status Change */
794 grub_uhci_writereg16 (u, reg, (status & GRUB_UHCI_REG_PORTSC_RW)
795 | GRUB_UHCI_REG_PORTSC_CONNECT_CHANGED);
797 else
798 *changed = 0;
800 if (! (status & 1))
801 return GRUB_USB_SPEED_NONE;
802 else if (status & (1 << 8))
803 return GRUB_USB_SPEED_LOW;
804 else
805 return GRUB_USB_SPEED_FULL;
808 static int
809 grub_uhci_hubports (grub_usb_controller_t dev __attribute__((unused)))
811 /* The root hub has exactly two ports. */
812 return 2;
816 static struct grub_usb_controller_dev usb_controller =
818 .name = "uhci",
819 .iterate = grub_uhci_iterate,
820 .setup_transfer = grub_uhci_setup_transfer,
821 .check_transfer = grub_uhci_check_transfer,
822 .cancel_transfer = grub_uhci_cancel_transfer,
823 .hubports = grub_uhci_hubports,
824 .portstatus = grub_uhci_portstatus,
825 .detect_dev = grub_uhci_detect_dev
828 GRUB_MOD_INIT(uhci)
830 grub_uhci_inithw ();
831 grub_usb_controller_dev_register (&usb_controller);
832 grub_dprintf ("uhci", "registered\n");
835 GRUB_MOD_FINI(uhci)
837 struct grub_uhci *u;
839 /* Disable all UHCI controllers. */
840 for (u = uhci; u; u = u->next)
841 grub_uhci_writereg16 (u, GRUB_UHCI_REG_USBCMD, 0);
843 /* Unregister the controller. */
844 grub_usb_controller_dev_unregister (&usb_controller);