serial_coreboot.c 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * UART support for U-Boot when launched from Coreboot
  4. *
  5. * Copyright 2019 Google LLC
  6. */
  7. #define LOG_CATGEGORY UCLASS_SERIAL
  8. #include <common.h>
  9. #include <dm.h>
  10. #include <log.h>
  11. #include <ns16550.h>
  12. #include <serial.h>
  13. #include <acpi/acpi_table.h>
  14. #include <asm/cb_sysinfo.h>
  15. DECLARE_GLOBAL_DATA_PTR;
  16. static int read_dbg2(struct ns16550_plat *plat)
  17. {
  18. struct acpi_table_header *tab;
  19. struct acpi_dbg2_header *hdr;
  20. struct acpi_dbg2_device *dbg;
  21. struct acpi_gen_regaddr *addr;
  22. u32 *addr_size;
  23. log_debug("Looking for DBG2 in ACPI tables\n");
  24. if (!gd->acpi_start) {
  25. log_debug("No ACPI tables\n");
  26. return -ENOENT;
  27. }
  28. tab = acpi_find_table("DBG2");
  29. if (!tab) {
  30. log_debug("No DBG2 table\n");
  31. return -ENOENT;
  32. }
  33. hdr = container_of(tab, struct acpi_dbg2_header, header);
  34. /* We only use the first device, but check that there is at least one */
  35. if (!hdr->devices_count) {
  36. log_debug("No devices\n");
  37. return -ENOENT;
  38. }
  39. if (hdr->devices_offset >= tab->length) {
  40. log_debug("Invalid offset\n");
  41. return -EINVAL;
  42. }
  43. dbg = (void *)hdr + hdr->devices_offset;
  44. if (dbg->revision) {
  45. log_debug("Invalid revision %d\n", dbg->revision);
  46. return -EINVAL;
  47. }
  48. if (!dbg->address_count) {
  49. log_debug("No addresses\n");
  50. return -EINVAL;
  51. }
  52. if (dbg->port_type != ACPI_DBG2_SERIAL_PORT) {
  53. log_debug("Not a serial port\n");
  54. return -EPROTOTYPE;
  55. }
  56. if (dbg->port_subtype != ACPI_DBG2_16550_COMPATIBLE) {
  57. log_debug("Incompatible serial port\n");
  58. return -EPROTOTYPE;
  59. }
  60. if (dbg->base_address_offset >= dbg->length ||
  61. dbg->address_size_offset >= dbg->length) {
  62. log_debug("Invalid base address/size offsets %d, %d\n",
  63. dbg->base_address_offset, dbg->address_size_offset);
  64. return -EINVAL;
  65. }
  66. addr_size = (void *)dbg + dbg->address_size_offset;
  67. if (!*addr_size) {
  68. log_debug("Zero address size\n");
  69. return -EINVAL;
  70. }
  71. addr = (void *)dbg + dbg->base_address_offset;
  72. if (addr->space_id != ACPI_ADDRESS_SPACE_MEMORY) {
  73. log_debug("Incompatible space %d\n", addr->space_id);
  74. return -EPROTOTYPE;
  75. }
  76. plat->base = addr->addrl;
  77. /* ACPI_ACCESS_SIZE_DWORD_ACCESS is 3; we want 2 */
  78. plat->reg_shift = addr->access_size - 1;
  79. plat->reg_width = 4; /* coreboot sets bit_width to 0 */
  80. plat->clock = 1843200;
  81. plat->fcr = UART_FCR_DEFVAL;
  82. plat->flags = 0;
  83. log_debug("Collected UART from ACPI DBG2 table\n");
  84. return 0;
  85. }
  86. static int coreboot_of_to_plat(struct udevice *dev)
  87. {
  88. struct ns16550_plat *plat = dev_get_plat(dev);
  89. struct cb_serial *cb_info = lib_sysinfo.serial;
  90. int ret = -ENOENT;
  91. if (cb_info) {
  92. plat->base = cb_info->baseaddr;
  93. plat->reg_shift = cb_info->regwidth == 4 ? 2 : 0;
  94. plat->reg_width = cb_info->regwidth;
  95. plat->clock = cb_info->input_hertz;
  96. plat->fcr = UART_FCR_DEFVAL;
  97. plat->flags = 0;
  98. if (cb_info->type == CB_SERIAL_TYPE_IO_MAPPED)
  99. plat->flags |= NS16550_FLAG_IO;
  100. ret = 0;
  101. } else if (IS_ENABLED(CONFIG_COREBOOT_SERIAL_FROM_DBG2)) {
  102. ret = read_dbg2(plat);
  103. }
  104. if (ret) {
  105. /*
  106. * Returning an error will cause U-Boot to complain that
  107. * there is no UART, which may panic. So stay silent and
  108. * pray that the video console will work.
  109. */
  110. log_debug("Cannot detect UART\n");
  111. }
  112. return 0;
  113. }
  114. static const struct udevice_id coreboot_serial_ids[] = {
  115. { .compatible = "coreboot-serial" },
  116. { },
  117. };
  118. U_BOOT_DRIVER(coreboot_uart) = {
  119. .name = "coreboot_uart",
  120. .id = UCLASS_SERIAL,
  121. .of_match = coreboot_serial_ids,
  122. .priv_auto = sizeof(struct ns16550),
  123. .plat_auto = sizeof(struct ns16550_plat),
  124. .of_to_plat = coreboot_of_to_plat,
  125. .probe = ns16550_serial_probe,
  126. .ops = &ns16550_serial_ops,
  127. .flags = DM_FLAG_PRE_RELOC,
  128. };