andes_plicsw.c 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  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' PLICSW
  6. * The PLICSW block is an Andes-specific design for software interrupts,
  7. * contains memory-mapped priority, enable, claim and pending registers
  8. * similar to RISC-V PLIC.
  9. */
  10. #include <common.h>
  11. #include <dm.h>
  12. #include <asm/global_data.h>
  13. #include <dm/device-internal.h>
  14. #include <dm/lists.h>
  15. #include <dm/uclass-internal.h>
  16. #include <regmap.h>
  17. #include <syscon.h>
  18. #include <asm/io.h>
  19. #include <asm/syscon.h>
  20. #include <cpu.h>
  21. #include <linux/err.h>
  22. /* pending register */
  23. #define PENDING_REG(base, hart) ((ulong)(base) + 0x1000 + ((hart) / 4) * 4)
  24. /* enable register */
  25. #define ENABLE_REG(base, hart) ((ulong)(base) + 0x2000 + (hart) * 0x80)
  26. /* claim register */
  27. #define CLAIM_REG(base, hart) ((ulong)(base) + 0x200004 + (hart) * 0x1000)
  28. /* priority register */
  29. #define PRIORITY_REG(base) ((ulong)(base) + PLICSW_PRIORITY_BASE)
  30. #define ENABLE_HART_IPI (0x01010101)
  31. #define SEND_IPI_TO_HART(hart) (0x1 << (hart))
  32. #define PLICSW_PRIORITY_BASE 0x4
  33. #define PLICSW_INTERRUPT_PER_HART 0x8
  34. DECLARE_GLOBAL_DATA_PTR;
  35. static int enable_ipi(int hart)
  36. {
  37. unsigned int en;
  38. en = ENABLE_HART_IPI << hart;
  39. writel(en, (void __iomem *)ENABLE_REG(gd->arch.plicsw, hart));
  40. writel(en, (void __iomem *)ENABLE_REG(gd->arch.plicsw + 0x4, hart));
  41. return 0;
  42. }
  43. static void init_priority_ipi(int hart_num)
  44. {
  45. uint32_t *priority = (void *)PRIORITY_REG(gd->arch.plicsw);
  46. for (int i = 0; i < hart_num * PLICSW_INTERRUPT_PER_HART; i++) {
  47. writel(1, &priority[i]);
  48. }
  49. return;
  50. }
  51. int riscv_init_ipi(void)
  52. {
  53. int ret;
  54. int hart_num = 0;
  55. long *base = syscon_get_first_range(RISCV_SYSCON_PLICSW);
  56. ofnode node;
  57. struct udevice *dev;
  58. u32 reg;
  59. if (IS_ERR(base))
  60. return PTR_ERR(base);
  61. gd->arch.plicsw = base;
  62. ret = uclass_find_first_device(UCLASS_CPU, &dev);
  63. if (ret)
  64. return ret;
  65. else if (!dev)
  66. return -ENODEV;
  67. ofnode_for_each_subnode(node, dev_ofnode(dev->parent)) {
  68. const char *device_type;
  69. device_type = ofnode_read_string(node, "device_type");
  70. if (!device_type)
  71. continue;
  72. if (strcmp(device_type, "cpu"))
  73. continue;
  74. /* skip if hart is marked as not available */
  75. if (!ofnode_is_enabled(node))
  76. continue;
  77. /* read hart ID of CPU */
  78. ret = ofnode_read_u32(node, "reg", &reg);
  79. if (ret == 0)
  80. enable_ipi(reg);
  81. hart_num++;
  82. }
  83. init_priority_ipi(hart_num);
  84. return 0;
  85. }
  86. int riscv_send_ipi(int hart)
  87. {
  88. unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
  89. writel(ipi, (void __iomem *)PENDING_REG(gd->arch.plicsw,
  90. gd->arch.boot_hart));
  91. return 0;
  92. }
  93. int riscv_clear_ipi(int hart)
  94. {
  95. u32 source_id;
  96. source_id = readl((void __iomem *)CLAIM_REG(gd->arch.plicsw, hart));
  97. writel(source_id, (void __iomem *)CLAIM_REG(gd->arch.plicsw, hart));
  98. return 0;
  99. }
  100. int riscv_get_ipi(int hart, int *pending)
  101. {
  102. unsigned int ipi = (SEND_IPI_TO_HART(hart) << (8 * gd->arch.boot_hart));
  103. *pending = readl((void __iomem *)PENDING_REG(gd->arch.plicsw,
  104. gd->arch.boot_hart));
  105. *pending = !!(*pending & ipi);
  106. return 0;
  107. }
  108. static const struct udevice_id andes_plicsw_ids[] = {
  109. { .compatible = "andestech,plicsw", .data = RISCV_SYSCON_PLICSW },
  110. { }
  111. };
  112. U_BOOT_DRIVER(andes_plicsw) = {
  113. .name = "andes_plicsw",
  114. .id = UCLASS_SYSCON,
  115. .of_match = andes_plicsw_ids,
  116. .flags = DM_FLAG_PRE_RELOC,
  117. };