ehci-pci.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. /* ehci.c - EHCI Support. */
  2. /*
  3. * GRUB -- GRand Unified Bootloader
  4. * Copyright (C) 2011 Free Software Foundation, Inc.
  5. *
  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.
  10. *
  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.
  15. *
  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/>.
  18. */
  19. #include <grub/pci.h>
  20. #include <grub/cpu/pci.h>
  21. #include <grub/cs5536.h>
  22. #include <grub/misc.h>
  23. #include <grub/mm.h>
  24. #include <grub/time.h>
  25. #include <grub/usb.h>
  26. #define GRUB_EHCI_PCI_SBRN_REG 0x60
  27. #define GRUB_EHCI_ADDR_MEM_MASK (~0xff)
  28. /* USBLEGSUP bits and related OS OWNED byte offset */
  29. enum
  30. {
  31. GRUB_EHCI_BIOS_OWNED = (1 << 16),
  32. GRUB_EHCI_OS_OWNED = (1 << 24)
  33. };
  34. /* PCI iteration function... */
  35. static int
  36. grub_ehci_pci_iter (grub_pci_device_t dev, grub_pci_id_t pciid,
  37. void *data __attribute__ ((unused)))
  38. {
  39. volatile grub_uint32_t *regs;
  40. grub_uint32_t base, base_h;
  41. grub_uint32_t eecp_offset;
  42. grub_uint32_t usblegsup = 0;
  43. grub_uint64_t maxtime;
  44. grub_uint32_t interf;
  45. grub_uint32_t subclass;
  46. grub_uint32_t class;
  47. grub_uint8_t release;
  48. grub_uint32_t class_code;
  49. grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: begin\n");
  50. if (pciid == GRUB_CS5536_PCIID)
  51. {
  52. grub_uint64_t basereg;
  53. basereg = grub_cs5536_read_msr (dev, GRUB_CS5536_MSR_USB_EHCI_BASE);
  54. if (!(basereg & GRUB_CS5536_MSR_USB_BASE_MEMORY_ENABLE))
  55. {
  56. /* Shouldn't happen. */
  57. grub_dprintf ("ehci", "No EHCI address is assigned\n");
  58. return 0;
  59. }
  60. base = (basereg & GRUB_CS5536_MSR_USB_BASE_ADDR_MASK);
  61. basereg |= GRUB_CS5536_MSR_USB_BASE_BUS_MASTER;
  62. basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_ENABLED;
  63. basereg &= ~GRUB_CS5536_MSR_USB_BASE_PME_STATUS;
  64. basereg &= ~GRUB_CS5536_MSR_USB_BASE_SMI_ENABLE;
  65. grub_cs5536_write_msr (dev, GRUB_CS5536_MSR_USB_EHCI_BASE, basereg);
  66. }
  67. else
  68. {
  69. grub_pci_address_t addr;
  70. addr = grub_pci_make_address (dev, GRUB_PCI_REG_CLASS);
  71. class_code = grub_pci_read (addr) >> 8;
  72. interf = class_code & 0xFF;
  73. subclass = (class_code >> 8) & 0xFF;
  74. class = class_code >> 16;
  75. /* If this is not an EHCI controller, just return. */
  76. if (class != 0x0c || subclass != 0x03 || interf != 0x20)
  77. return 0;
  78. grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: class OK\n");
  79. /* Check Serial Bus Release Number */
  80. addr = grub_pci_make_address (dev, GRUB_EHCI_PCI_SBRN_REG);
  81. release = grub_pci_read_byte (addr);
  82. if (release != 0x20)
  83. {
  84. grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: Wrong SBRN: %0x\n",
  85. release);
  86. return 0;
  87. }
  88. grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: bus rev. num. OK\n");
  89. /* Determine EHCI EHCC registers base address. */
  90. addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG0);
  91. base = grub_pci_read (addr);
  92. addr = grub_pci_make_address (dev, GRUB_PCI_REG_ADDRESS_REG1);
  93. base_h = grub_pci_read (addr);
  94. /* Stop if registers are mapped above 4G - GRUB does not currently
  95. * work with registers mapped above 4G */
  96. if (((base & GRUB_PCI_ADDR_MEM_TYPE_MASK) != GRUB_PCI_ADDR_MEM_TYPE_32)
  97. && (base_h != 0))
  98. {
  99. grub_dprintf ("ehci",
  100. "EHCI grub_ehci_pci_iter: registers above 4G are not supported\n");
  101. return 0;
  102. }
  103. base &= GRUB_PCI_ADDR_MEM_MASK;
  104. if (!base)
  105. {
  106. grub_dprintf ("ehci",
  107. "EHCI: EHCI is not mapped\n");
  108. return 0;
  109. }
  110. /* Set bus master - needed for coreboot, VMware, broken BIOSes etc. */
  111. addr = grub_pci_make_address (dev, GRUB_PCI_REG_COMMAND);
  112. grub_pci_write_word(addr,
  113. GRUB_PCI_COMMAND_MEM_ENABLED
  114. | GRUB_PCI_COMMAND_BUS_MASTER
  115. | grub_pci_read_word(addr));
  116. grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: 32-bit EHCI OK\n");
  117. }
  118. grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: iobase of EHCC: %08x\n",
  119. (base & GRUB_EHCI_ADDR_MEM_MASK));
  120. regs = grub_pci_device_map_range (dev,
  121. (base & GRUB_EHCI_ADDR_MEM_MASK),
  122. 0x100);
  123. /* Is there EECP ? */
  124. eecp_offset = (grub_le_to_cpu32 (regs[2]) >> 8) & 0xff;
  125. /* Determine and change ownership. */
  126. /* EECP offset valid in HCCPARAMS */
  127. /* Ownership can be changed via EECP only */
  128. if (pciid != GRUB_CS5536_PCIID && eecp_offset >= 0x40)
  129. {
  130. grub_pci_address_t pciaddr_eecp;
  131. pciaddr_eecp = grub_pci_make_address (dev, eecp_offset);
  132. usblegsup = grub_pci_read (pciaddr_eecp);
  133. if (usblegsup & GRUB_EHCI_BIOS_OWNED)
  134. {
  135. grub_boot_time ("Taking ownership of EHCI controller");
  136. grub_dprintf ("ehci",
  137. "EHCI grub_ehci_pci_iter: EHCI owned by: BIOS\n");
  138. /* Ownership change - set OS_OWNED bit */
  139. grub_pci_write (pciaddr_eecp, usblegsup | GRUB_EHCI_OS_OWNED);
  140. /* Ensure PCI register is written */
  141. grub_pci_read (pciaddr_eecp);
  142. /* Wait for finish of ownership change, EHCI specification
  143. * doesn't say how long it can take... */
  144. maxtime = grub_get_time_ms () + 1000;
  145. while ((grub_pci_read (pciaddr_eecp) & GRUB_EHCI_BIOS_OWNED)
  146. && (grub_get_time_ms () < maxtime));
  147. if (grub_pci_read (pciaddr_eecp) & GRUB_EHCI_BIOS_OWNED)
  148. {
  149. grub_dprintf ("ehci",
  150. "EHCI grub_ehci_pci_iter: EHCI change ownership timeout");
  151. /* Change ownership in "hard way" - reset BIOS ownership */
  152. grub_pci_write (pciaddr_eecp, GRUB_EHCI_OS_OWNED);
  153. /* Ensure PCI register is written */
  154. grub_pci_read (pciaddr_eecp);
  155. }
  156. }
  157. else if (usblegsup & GRUB_EHCI_OS_OWNED)
  158. /* XXX: What to do in this case - nothing ? Can it happen ? */
  159. grub_dprintf ("ehci", "EHCI grub_ehci_pci_iter: EHCI owned by: OS\n");
  160. else
  161. {
  162. grub_dprintf ("ehci",
  163. "EHCI grub_ehci_pci_iter: EHCI owned by: NONE\n");
  164. /* XXX: What to do in this case ? Can it happen ?
  165. * Is code below correct ? */
  166. /* Ownership change - set OS_OWNED bit */
  167. grub_pci_write (pciaddr_eecp, GRUB_EHCI_OS_OWNED);
  168. /* Ensure PCI register is written */
  169. grub_pci_read (pciaddr_eecp);
  170. }
  171. /* Disable SMI, just to be sure. */
  172. pciaddr_eecp = grub_pci_make_address (dev, eecp_offset + 4);
  173. grub_pci_write (pciaddr_eecp, 0);
  174. /* Ensure PCI register is written */
  175. grub_pci_read (pciaddr_eecp);
  176. }
  177. grub_dprintf ("ehci", "inithw: EHCI grub_ehci_pci_iter: ownership OK\n");
  178. grub_ehci_init_device (regs);
  179. return 0;
  180. }
  181. void
  182. grub_ehci_pci_scan (void)
  183. {
  184. grub_pci_iterate (grub_ehci_pci_iter, NULL);
  185. }