thead_plic.c 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. // SPDX-License-Identifier: GPL-2.0+
  2. /*
  3. * Copyright (C) 2022, Liuw <lw312886@alibaba-inc.com>
  4. *
  5. * U-Boot syscon driver for Thead's Platform Level Interrupt Controller (PLIC)
  6. */
  7. #include <common.h>
  8. #include <dm.h>
  9. #include <dm/device-internal.h>
  10. #include <dm/lists.h>
  11. #include <dm/uclass-internal.h>
  12. #include <regmap.h>
  13. #include <syscon.h>
  14. #include <asm/csr.h>
  15. #include <asm/io.h>
  16. #include <asm/syscon.h>
  17. #include <asm/global_data.h>
  18. #include <cpu.h>
  19. #include <linux/err.h>
  20. #include <asm/arch-thead/light-plic.h>
  21. DECLARE_GLOBAL_DATA_PTR;
  22. #define PLIC_BASE_GET(void) \
  23. do { \
  24. long *ret; \
  25. \
  26. if (!gd->arch.plic) { \
  27. ret = syscon_get_first_range(RISCV_SYSCON_PLIC); \
  28. gd->arch.plic = ret; \
  29. } \
  30. } while (0)
  31. irq_handler_t irq_table[MAX_IRQ_NUM];
  32. void __iomem *plic_base = NULL;
  33. void external_interrupt(struct pt_regs *regs)
  34. {
  35. void __iomem *claim;
  36. irq_handler_t handler;
  37. u32 irq_num;
  38. debug("[%s,%d]\n", __func__, __LINE__);
  39. if (!plic_base)
  40. return;
  41. debug("[%s,%d]\n", __func__, __LINE__);
  42. claim = PLIC_CLAIM_REG(plic_base, 0);
  43. while ((irq_num = readl(claim))) {
  44. if (irq_num >= MAX_IRQ_NUM)
  45. debug("Cannot find irq:%d\n", irq_num);
  46. else {
  47. handler = irq_table[irq_num];
  48. if (handler)
  49. handler();
  50. writel(irq_num, claim);
  51. }
  52. }
  53. debug("[%s,%d]\n", __func__, __LINE__);
  54. }
  55. static void plic_toggle(void __iomem *enable_base, int hwirq, int enable)
  56. {
  57. u32 __iomem *reg = enable_base + (hwirq / 32) * sizeof(u32);
  58. u32 hwirq_mask = 1 << (hwirq % 32);
  59. if (enable)
  60. writel(readl(reg) | hwirq_mask, reg);
  61. else
  62. writel(readl(reg) & ~hwirq_mask, reg);
  63. debug("[%s,%d][0x%lx] = 0x%x\n", __func__, __LINE__,
  64. (unsigned long)reg, readl(reg));
  65. }
  66. static void plic_set_threshold(void __iomem *thre_base, u32 threshold)
  67. {
  68. writel(threshold, thre_base);
  69. debug("[%s,%d][0x%lx] = 0x%x\n", __func__, __LINE__,
  70. (unsigned long)thre_base, readl(thre_base));
  71. }
  72. static void plic_set_irq_priority(void __iomem *prio_base, int prio)
  73. {
  74. writel(prio, prio_base);
  75. }
  76. int irq_handler_register(int irq, irq_handler_t handler)
  77. {
  78. if (irq < 0 || irq >= MAX_IRQ_NUM) {
  79. debug("invalid irq number to register\n");
  80. return -EINVAL;
  81. }
  82. irq_table[irq] = handler;
  83. return 0;
  84. }
  85. void arch_local_irq_enable(void)
  86. {
  87. csr_set(CSR_MIE, MIE_MEIE);
  88. csr_set(CSR_MSTATUS, SR_MIE);
  89. }
  90. void arch_local_irq_disable(void)
  91. {
  92. csr_clear(CSR_MIE, MIE_MEIE);
  93. csr_clear(CSR_MSTATUS, SR_MIE);
  94. }
  95. void irq_priority_set(int irq_id)
  96. {
  97. plic_set_irq_priority(PLIC_PRIO_REG(gd->arch.plic, irq_id), 4);
  98. }
  99. void irq_enable(int hwirq)
  100. {
  101. plic_toggle(PLIC_ENABLE_REG(gd->arch.plic, 0), hwirq, 1);
  102. }
  103. void irq_disable(int hwirq)
  104. {
  105. plic_toggle(PLIC_ENABLE_REG(gd->arch.plic, 0), hwirq, 0);
  106. }
  107. int plic_init()
  108. {
  109. PLIC_BASE_GET();
  110. if (IS_ERR(gd->arch.plic))
  111. return PTR_ERR(gd->arch.plic);
  112. plic_base = gd->arch.plic;
  113. debug("THEAD PLIC BASE: 0x%lx\n", (unsigned long)gd->arch.plic);
  114. plic_set_threshold(PLIC_THRESHOLD_REG(gd->arch.plic, 0), 0);
  115. arch_local_irq_enable(); //enale the global interrupt
  116. return 0;
  117. }
  118. static const struct udevice_id thead_plic_ids[] = {
  119. { .compatible = "riscv,plic0", .data = RISCV_SYSCON_PLIC},
  120. { }
  121. };
  122. U_BOOT_DRIVER(thead_plic) = {
  123. .name = "thead_light_plic",
  124. .id = UCLASS_SYSCON,
  125. .of_match = thead_plic_ids,
  126. };