andes_plic.c 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2019, Rick Chen <rick@andestech.com>
  4. *
  5. * U-Boot syscon driver for Andes's Platform Level Interrupt Controller (PLIC).
  6. * The PLIC block holds memory-mapped claim and pending registers
  7. * associated with software interrupt.
  8. */
  9. #include <common.h>
  10. #include <dm.h>
  11. #include <dm/device-internal.h>
  12. #include <dm/lists.h>
  13. #include <dm/uclass-internal.h>
  14. #include <regmap.h>
  15. #include <syscon.h>
  16. #include <asm/io.h>
  17. #include <asm/syscon.h>
  18. #include <cpu.h>
  19. /* pending register */
  20. #define PENDING_REG(base, hart) ((ulong)(base) + 0x1000 + ((hart) / 4) * 4)
  21. /* enable register */
  22. #define ENABLE_REG(base, hart) ((ulong)(base) + 0x2000 + (hart) * 0x80)
  23. /* claim register */
  24. #define CLAIM_REG(base, hart) ((ulong)(base) + 0x200004 + (hart) * 0x1000)
  25. #define ENABLE_HART_IPI (0x80808080)
  26. #define SEND_IPI_TO_HART(hart) (0x80 >> (hart))
  27. DECLARE_GLOBAL_DATA_PTR;
  28. static int init_plic(void);
  29. #define PLIC_BASE_GET(void) \
  30. do { \
  31. long *ret; \
  32. \
  33. if (!gd->arch.plic) { \
  34. ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
  35. if (IS_ERR(ret)) \
  36. return PTR_ERR(ret); \
  37. gd->arch.plic = ret; \
  38. init_plic(); \
  39. } \
  40. } while (0)
  41. static int enable_ipi(int hart)
  42. {
  43. unsigned int en;
  44. en = ENABLE_HART_IPI >> hart;
  45. writel(en, (void __iomem *)ENABLE_REG(gd->arch.plic, hart));
  46. return 0;
  47. }
  48. static int init_plic(void)
  49. {
  50. struct udevice *dev;
  51. ofnode node;
  52. int ret;
  53. u32 reg;
  54. ret = uclass_find_first_device(UCLASS_CPU, &dev);
  55. if (ret)
  56. return ret;
  57. if (ret == 0 && dev) {
  58. ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) {
  59. const char *device_type;
  60. device_type = ofnode_read_string(node, "device_type");
  61. if (!device_type)
  62. continue;
  63. if (strcmp(device_type, "cpu"))
  64. continue;
  65. /* skip if hart is marked as not available */
  66. if (!ofnode_is_available(node))
  67. continue;
  68. /* read hart ID of CPU */
  69. ret = ofnode_read_u32(node, "reg", &reg);
  70. if (ret == 0)
  71. enable_ipi(reg);
  72. }
  73. return 0;
  74. }
  75. return -ENODEV;
  76. }
  77. int riscv_send_ipi(int hart)
  78. {
  79. unsigned int ipi;
  80. PLIC_BASE_GET();
  81. ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
  82. writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plic,
  83. gd->arch.boot_hart));
  84. return 0;
  85. }
  86. int riscv_clear_ipi(int hart)
  87. {
  88. u32 source_id;
  89. PLIC_BASE_GET();
  90. source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plic, hart));
  91. writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plic, hart));
  92. return 0;
  93. }
  94. int riscv_get_ipi(int hart, int *pending)
  95. {
  96. PLIC_BASE_GET();
  97. *pending = readl((void __iomem *)PENDING_REG(gd->arch.plic,
  98. gd->arch.boot_hart));
  99. *pending = !!(*pending & SEND_IPI_TO_HART(hart));
  100. return 0;
  101. }
  102. static const struct udevice_id andes_plic_ids[] = {
  103. { .compatible = "riscv,plic1", .data = RISCV_SYSCON_PLIC },
  104. { }
  105. };
  106. U_BOOT_DRIVER(andes_plic) = {
  107. .name = "andes_plic",
  108. .id = UCLASS_SYSCON,
  109. .of_match = andes_plic_ids,
  110. .flags = DM_FLAG_PRE_RELOC,
  111. };