andes_plic.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  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. #include <linux/err.h>
  20. /* pending register */
  21. #define PENDING_REG(base, hart) ((ulong)(base) + 0x1000 + ((hart) / 4) * 4)
  22. /* enable register */
  23. #define ENABLE_REG(base, hart) ((ulong)(base) + 0x2000 + (hart) * 0x80)
  24. /* claim register */
  25. #define CLAIM_REG(base, hart) ((ulong)(base) + 0x200004 + (hart) * 0x1000)
  26. #define ENABLE_HART_IPI (0x80808080)
  27. #define SEND_IPI_TO_HART(hart) (0x80 >> (hart))
  28. DECLARE_GLOBAL_DATA_PTR;
  29. static int init_plic(void);
  30. #define PLIC_BASE_GET(void) \
  31. do { \
  32. long *ret; \
  33. \
  34. if (!gd->arch.plic) { \
  35. ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
  36. if (IS_ERR(ret)) \
  37. return PTR_ERR(ret); \
  38. gd->arch.plic = ret; \
  39. init_plic(); \
  40. } \
  41. } while (0)
  42. static int enable_ipi(int hart)
  43. {
  44. unsigned int en;
  45. en = ENABLE_HART_IPI >> hart;
  46. writel(en, (void __iomem *)ENABLE_REG(gd->arch.plic, hart));
  47. return 0;
  48. }
  49. static int init_plic(void)
  50. {
  51. struct udevice *dev;
  52. ofnode node;
  53. int ret;
  54. u32 reg;
  55. ret = uclass_find_first_device(UCLASS_CPU, &dev);
  56. if (ret)
  57. return ret;
  58. if (ret == 0 && dev) {
  59. ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) {
  60. const char *device_type;
  61. device_type = ofnode_read_string(node, "device_type");
  62. if (!device_type)
  63. continue;
  64. if (strcmp(device_type, "cpu"))
  65. continue;
  66. /* skip if hart is marked as not available */
  67. if (!ofnode_is_available(node))
  68. continue;
  69. /* read hart ID of CPU */
  70. ret = ofnode_read_u32(node, "reg", &reg);
  71. if (ret == 0)
  72. enable_ipi(reg);
  73. }
  74. return 0;
  75. }
  76. return -ENODEV;
  77. }
  78. int riscv_send_ipi(int hart)
  79. {
  80. unsigned int ipi;
  81. PLIC_BASE_GET();
  82. ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
  83. writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plic,
  84. gd->arch.boot_hart));
  85. return 0;
  86. }
  87. int riscv_clear_ipi(int hart)
  88. {
  89. u32 source_id;
  90. PLIC_BASE_GET();
  91. source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plic, hart));
  92. writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plic, hart));
  93. return 0;
  94. }
  95. int riscv_get_ipi(int hart, int *pending)
  96. {
  97. PLIC_BASE_GET();
  98. *pending = readl((void __iomem *)PENDING_REG(gd->arch.plic,
  99. gd->arch.boot_hart));
  100. *pending = !!(*pending & SEND_IPI_TO_HART(hart));
  101. return 0;
  102. }
  103. static const struct udevice_id andes_plic_ids[] = {
  104. { .compatible = "riscv,plic1", .data = RISCV_SYSCON_PLIC },
  105. { }
  106. };
  107. U_BOOT_DRIVER(andes_plic) = {
  108. .name = "andes_plic",
  109. .id = UCLASS_SYSCON,
  110. .of_match = andes_plic_ids,
  111. .flags = DM_FLAG_PRE_RELOC,
  112. };