loongson_liointc.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. /*
  2. * QEMU Loongson Local I/O interrupt controller.
  3. *
  4. * Copyright (c) 2020 Huacai Chen <chenhc@lemote.com>
  5. * Copyright (c) 2020 Jiaxun Yang <jiaxun.yang@flygoat.com>
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation, either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  19. *
  20. */
  21. #include "qemu/osdep.h"
  22. #include "qemu/module.h"
  23. #include "qemu/log.h"
  24. #include "hw/irq.h"
  25. #include "hw/qdev-properties.h"
  26. #include "hw/intc/loongson_liointc.h"
  27. #define NUM_IRQS 32
  28. #define NUM_CORES 4
  29. #define NUM_IPS 4
  30. #define NUM_PARENTS (NUM_CORES * NUM_IPS)
  31. #define PARENT_COREx_IPy(x, y) (NUM_IPS * x + y)
  32. #define R_MAPPER_START 0x0
  33. #define R_MAPPER_END 0x20
  34. #define R_ISR R_MAPPER_END
  35. #define R_IEN 0x24
  36. #define R_IEN_SET 0x28
  37. #define R_IEN_CLR 0x2c
  38. #define R_ISR_SIZE 0x8
  39. #define R_START 0x40
  40. #define R_END (R_START + R_ISR_SIZE * NUM_CORES)
  41. struct loongson_liointc {
  42. SysBusDevice parent_obj;
  43. MemoryRegion mmio;
  44. qemu_irq parent_irq[NUM_PARENTS];
  45. uint8_t mapper[NUM_IRQS]; /* 0:3 for core, 4:7 for IP */
  46. uint32_t isr;
  47. uint32_t ien;
  48. uint32_t per_core_isr[NUM_CORES];
  49. /* state of the interrupt input pins */
  50. uint32_t pin_state;
  51. bool parent_state[NUM_PARENTS];
  52. };
  53. static void update_irq(struct loongson_liointc *p)
  54. {
  55. uint32_t irq, core, ip;
  56. uint32_t per_ip_isr[NUM_IPS] = {0};
  57. /* level triggered interrupt */
  58. p->isr = p->pin_state;
  59. /* Clear disabled IRQs */
  60. p->isr &= p->ien;
  61. /* Clear per_core_isr */
  62. for (core = 0; core < NUM_CORES; core++) {
  63. p->per_core_isr[core] = 0;
  64. }
  65. /* Update per_core_isr and per_ip_isr */
  66. for (irq = 0; irq < NUM_IRQS; irq++) {
  67. if (!(p->isr & (1 << irq))) {
  68. continue;
  69. }
  70. for (core = 0; core < NUM_CORES; core++) {
  71. if ((p->mapper[irq] & (1 << core))) {
  72. p->per_core_isr[core] |= (1 << irq);
  73. }
  74. }
  75. for (ip = 0; ip < NUM_IPS; ip++) {
  76. if ((p->mapper[irq] & (1 << (ip + 4)))) {
  77. per_ip_isr[ip] |= (1 << irq);
  78. }
  79. }
  80. }
  81. /* Emit IRQ to parent! */
  82. for (core = 0; core < NUM_CORES; core++) {
  83. for (ip = 0; ip < NUM_IPS; ip++) {
  84. int parent = PARENT_COREx_IPy(core, ip);
  85. if (p->parent_state[parent] !=
  86. (!!p->per_core_isr[core] && !!per_ip_isr[ip])) {
  87. p->parent_state[parent] = !p->parent_state[parent];
  88. qemu_set_irq(p->parent_irq[parent], p->parent_state[parent]);
  89. }
  90. }
  91. }
  92. }
  93. static uint64_t
  94. liointc_read(void *opaque, hwaddr addr, unsigned int size)
  95. {
  96. struct loongson_liointc *p = opaque;
  97. uint32_t r = 0;
  98. /* Mapper is 1 byte */
  99. if (size == 1 && addr < R_MAPPER_END) {
  100. r = p->mapper[addr];
  101. goto out;
  102. }
  103. /* Rest are 4 bytes */
  104. if (size != 4 || (addr % 4)) {
  105. goto out;
  106. }
  107. if (addr >= R_START && addr < R_END) {
  108. hwaddr offset = addr - R_START;
  109. int core = offset / R_ISR_SIZE;
  110. if (offset % R_ISR_SIZE) {
  111. goto out;
  112. }
  113. r = p->per_core_isr[core];
  114. goto out;
  115. }
  116. switch (addr) {
  117. case R_ISR:
  118. r = p->isr;
  119. break;
  120. case R_IEN:
  121. r = p->ien;
  122. break;
  123. default:
  124. break;
  125. }
  126. out:
  127. qemu_log_mask(CPU_LOG_INT, "%s: size=%d, addr=%"HWADDR_PRIx", val=%x\n",
  128. __func__, size, addr, r);
  129. return r;
  130. }
  131. static void
  132. liointc_write(void *opaque, hwaddr addr,
  133. uint64_t val64, unsigned int size)
  134. {
  135. struct loongson_liointc *p = opaque;
  136. uint32_t value = val64;
  137. qemu_log_mask(CPU_LOG_INT, "%s: size=%d, addr=%"HWADDR_PRIx", val=%x\n",
  138. __func__, size, addr, value);
  139. /* Mapper is 1 byte */
  140. if (size == 1 && addr < R_MAPPER_END) {
  141. p->mapper[addr] = value;
  142. goto out;
  143. }
  144. /* Rest are 4 bytes */
  145. if (size != 4 || (addr % 4)) {
  146. goto out;
  147. }
  148. if (addr >= R_START && addr < R_END) {
  149. hwaddr offset = addr - R_START;
  150. int core = offset / R_ISR_SIZE;
  151. if (offset % R_ISR_SIZE) {
  152. goto out;
  153. }
  154. p->per_core_isr[core] = value;
  155. goto out;
  156. }
  157. switch (addr) {
  158. case R_IEN_SET:
  159. p->ien |= value;
  160. break;
  161. case R_IEN_CLR:
  162. p->ien &= ~value;
  163. break;
  164. default:
  165. break;
  166. }
  167. out:
  168. update_irq(p);
  169. }
  170. static const MemoryRegionOps pic_ops = {
  171. .read = liointc_read,
  172. .write = liointc_write,
  173. .endianness = DEVICE_NATIVE_ENDIAN,
  174. .valid = {
  175. .min_access_size = 1,
  176. .max_access_size = 4
  177. }
  178. };
  179. static void irq_handler(void *opaque, int irq, int level)
  180. {
  181. struct loongson_liointc *p = opaque;
  182. p->pin_state &= ~(1 << irq);
  183. p->pin_state |= level << irq;
  184. update_irq(p);
  185. }
  186. static void loongson_liointc_init(Object *obj)
  187. {
  188. struct loongson_liointc *p = LOONGSON_LIOINTC(obj);
  189. int i;
  190. qdev_init_gpio_in(DEVICE(obj), irq_handler, 32);
  191. for (i = 0; i < NUM_PARENTS; i++) {
  192. sysbus_init_irq(SYS_BUS_DEVICE(obj), &p->parent_irq[i]);
  193. }
  194. memory_region_init_io(&p->mmio, obj, &pic_ops, p,
  195. TYPE_LOONGSON_LIOINTC, R_END);
  196. sysbus_init_mmio(SYS_BUS_DEVICE(obj), &p->mmio);
  197. }
  198. static const TypeInfo loongson_liointc_info = {
  199. .name = TYPE_LOONGSON_LIOINTC,
  200. .parent = TYPE_SYS_BUS_DEVICE,
  201. .instance_size = sizeof(struct loongson_liointc),
  202. .instance_init = loongson_liointc_init,
  203. };
  204. static void loongson_liointc_register_types(void)
  205. {
  206. type_register_static(&loongson_liointc_info);
  207. }
  208. type_init(loongson_liointc_register_types)