loongarch_pch_pic.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. /* SPDX-License-Identifier: GPL-2.0-or-later */
  2. /*
  3. * QEMU Loongson 7A1000 I/O interrupt controller.
  4. *
  5. * Copyright (C) 2021 Loongson Technology Corporation Limited
  6. */
  7. #include "qemu/osdep.h"
  8. #include "qemu/bitops.h"
  9. #include "hw/irq.h"
  10. #include "hw/intc/loongarch_pch_pic.h"
  11. #include "trace.h"
  12. #include "qapi/error.h"
  13. static void pch_pic_update_irq(LoongArchPICCommonState *s, uint64_t mask,
  14. int level)
  15. {
  16. uint64_t val;
  17. int irq;
  18. if (level) {
  19. val = mask & s->intirr & ~s->int_mask;
  20. if (val) {
  21. irq = ctz64(val);
  22. s->intisr |= MAKE_64BIT_MASK(irq, 1);
  23. qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 1);
  24. }
  25. } else {
  26. /*
  27. * intirr means requested pending irq
  28. * do not clear pending irq for edge-triggered on lowering edge
  29. */
  30. val = mask & s->intisr & ~s->intirr;
  31. if (val) {
  32. irq = ctz64(val);
  33. s->intisr &= ~MAKE_64BIT_MASK(irq, 1);
  34. qemu_set_irq(s->parent_irq[s->htmsi_vector[irq]], 0);
  35. }
  36. }
  37. }
  38. static void pch_pic_irq_handler(void *opaque, int irq, int level)
  39. {
  40. LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
  41. uint64_t mask = 1ULL << irq;
  42. assert(irq < s->irq_num);
  43. trace_loongarch_pch_pic_irq_handler(irq, level);
  44. if (s->intedge & mask) {
  45. /* Edge triggered */
  46. if (level) {
  47. if ((s->last_intirr & mask) == 0) {
  48. /* marked pending on a rising edge */
  49. s->intirr |= mask;
  50. }
  51. s->last_intirr |= mask;
  52. } else {
  53. s->last_intirr &= ~mask;
  54. }
  55. } else {
  56. /* Level triggered */
  57. if (level) {
  58. s->intirr |= mask;
  59. s->last_intirr |= mask;
  60. } else {
  61. s->intirr &= ~mask;
  62. s->last_intirr &= ~mask;
  63. }
  64. }
  65. pch_pic_update_irq(s, mask, level);
  66. }
  67. static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr,
  68. unsigned size)
  69. {
  70. LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
  71. uint64_t val = 0;
  72. uint32_t offset = addr & 0xfff;
  73. switch (offset) {
  74. case PCH_PIC_INT_ID_LO:
  75. val = PCH_PIC_INT_ID_VAL;
  76. break;
  77. case PCH_PIC_INT_ID_HI:
  78. /*
  79. * With 7A1000 manual
  80. * bit 0-15 pch irqchip version
  81. * bit 16-31 irq number supported with pch irqchip
  82. */
  83. val = deposit32(PCH_PIC_INT_ID_VER, 16, 16, s->irq_num - 1);
  84. break;
  85. case PCH_PIC_INT_MASK_LO:
  86. val = (uint32_t)s->int_mask;
  87. break;
  88. case PCH_PIC_INT_MASK_HI:
  89. val = s->int_mask >> 32;
  90. break;
  91. case PCH_PIC_INT_EDGE_LO:
  92. val = (uint32_t)s->intedge;
  93. break;
  94. case PCH_PIC_INT_EDGE_HI:
  95. val = s->intedge >> 32;
  96. break;
  97. case PCH_PIC_HTMSI_EN_LO:
  98. val = (uint32_t)s->htmsi_en;
  99. break;
  100. case PCH_PIC_HTMSI_EN_HI:
  101. val = s->htmsi_en >> 32;
  102. break;
  103. case PCH_PIC_AUTO_CTRL0_LO:
  104. case PCH_PIC_AUTO_CTRL0_HI:
  105. case PCH_PIC_AUTO_CTRL1_LO:
  106. case PCH_PIC_AUTO_CTRL1_HI:
  107. break;
  108. default:
  109. break;
  110. }
  111. trace_loongarch_pch_pic_low_readw(size, addr, val);
  112. return val;
  113. }
  114. static uint64_t get_writew_val(uint64_t value, uint32_t target, bool hi)
  115. {
  116. uint64_t mask = 0xffffffff00000000;
  117. uint64_t data = target;
  118. return hi ? (value & ~mask) | (data << 32) : (value & mask) | data;
  119. }
  120. static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr,
  121. uint64_t value, unsigned size)
  122. {
  123. LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
  124. uint32_t offset, old_valid, data = (uint32_t)value;
  125. uint64_t old, int_mask;
  126. offset = addr & 0xfff;
  127. trace_loongarch_pch_pic_low_writew(size, addr, data);
  128. switch (offset) {
  129. case PCH_PIC_INT_MASK_LO:
  130. old = s->int_mask;
  131. s->int_mask = get_writew_val(old, data, 0);
  132. old_valid = (uint32_t)old;
  133. if (old_valid & ~data) {
  134. pch_pic_update_irq(s, (old_valid & ~data), 1);
  135. }
  136. if (~old_valid & data) {
  137. pch_pic_update_irq(s, (~old_valid & data), 0);
  138. }
  139. break;
  140. case PCH_PIC_INT_MASK_HI:
  141. old = s->int_mask;
  142. s->int_mask = get_writew_val(old, data, 1);
  143. old_valid = (uint32_t)(old >> 32);
  144. int_mask = old_valid & ~data;
  145. if (int_mask) {
  146. pch_pic_update_irq(s, int_mask << 32, 1);
  147. }
  148. int_mask = ~old_valid & data;
  149. if (int_mask) {
  150. pch_pic_update_irq(s, int_mask << 32, 0);
  151. }
  152. break;
  153. case PCH_PIC_INT_EDGE_LO:
  154. s->intedge = get_writew_val(s->intedge, data, 0);
  155. break;
  156. case PCH_PIC_INT_EDGE_HI:
  157. s->intedge = get_writew_val(s->intedge, data, 1);
  158. break;
  159. case PCH_PIC_INT_CLEAR_LO:
  160. if (s->intedge & data) {
  161. s->intirr &= (~data);
  162. pch_pic_update_irq(s, data, 0);
  163. s->intisr &= (~data);
  164. }
  165. break;
  166. case PCH_PIC_INT_CLEAR_HI:
  167. value <<= 32;
  168. if (s->intedge & value) {
  169. s->intirr &= (~value);
  170. pch_pic_update_irq(s, value, 0);
  171. s->intisr &= (~value);
  172. }
  173. break;
  174. case PCH_PIC_HTMSI_EN_LO:
  175. s->htmsi_en = get_writew_val(s->htmsi_en, data, 0);
  176. break;
  177. case PCH_PIC_HTMSI_EN_HI:
  178. s->htmsi_en = get_writew_val(s->htmsi_en, data, 1);
  179. break;
  180. case PCH_PIC_AUTO_CTRL0_LO:
  181. case PCH_PIC_AUTO_CTRL0_HI:
  182. case PCH_PIC_AUTO_CTRL1_LO:
  183. case PCH_PIC_AUTO_CTRL1_HI:
  184. break;
  185. default:
  186. break;
  187. }
  188. }
  189. static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr,
  190. unsigned size)
  191. {
  192. LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
  193. uint64_t val = 0;
  194. uint32_t offset = addr & 0xfff;
  195. switch (offset) {
  196. case STATUS_LO_START:
  197. val = (uint32_t)(s->intisr & (~s->int_mask));
  198. break;
  199. case STATUS_HI_START:
  200. val = (s->intisr & (~s->int_mask)) >> 32;
  201. break;
  202. case POL_LO_START:
  203. val = (uint32_t)s->int_polarity;
  204. break;
  205. case POL_HI_START:
  206. val = s->int_polarity >> 32;
  207. break;
  208. default:
  209. break;
  210. }
  211. trace_loongarch_pch_pic_high_readw(size, addr, val);
  212. return val;
  213. }
  214. static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr,
  215. uint64_t value, unsigned size)
  216. {
  217. LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
  218. uint32_t offset, data = (uint32_t)value;
  219. offset = addr & 0xfff;
  220. trace_loongarch_pch_pic_high_writew(size, addr, data);
  221. switch (offset) {
  222. case STATUS_LO_START:
  223. s->intisr = get_writew_val(s->intisr, data, 0);
  224. break;
  225. case STATUS_HI_START:
  226. s->intisr = get_writew_val(s->intisr, data, 1);
  227. break;
  228. case POL_LO_START:
  229. s->int_polarity = get_writew_val(s->int_polarity, data, 0);
  230. break;
  231. case POL_HI_START:
  232. s->int_polarity = get_writew_val(s->int_polarity, data, 1);
  233. break;
  234. default:
  235. break;
  236. }
  237. }
  238. static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr,
  239. unsigned size)
  240. {
  241. LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
  242. uint64_t val = 0;
  243. uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
  244. int64_t offset_tmp;
  245. switch (offset) {
  246. case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
  247. offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
  248. if (offset_tmp >= 0 && offset_tmp < 64) {
  249. val = s->htmsi_vector[offset_tmp];
  250. }
  251. break;
  252. case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
  253. offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
  254. if (offset_tmp >= 0 && offset_tmp < 64) {
  255. val = s->route_entry[offset_tmp];
  256. }
  257. break;
  258. default:
  259. break;
  260. }
  261. trace_loongarch_pch_pic_readb(size, addr, val);
  262. return val;
  263. }
  264. static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr,
  265. uint64_t data, unsigned size)
  266. {
  267. LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque);
  268. int32_t offset_tmp;
  269. uint32_t offset = (addr & 0xfff) + PCH_PIC_ROUTE_ENTRY_OFFSET;
  270. trace_loongarch_pch_pic_writeb(size, addr, data);
  271. switch (offset) {
  272. case PCH_PIC_HTMSI_VEC_OFFSET ... PCH_PIC_HTMSI_VEC_END:
  273. offset_tmp = offset - PCH_PIC_HTMSI_VEC_OFFSET;
  274. if (offset_tmp >= 0 && offset_tmp < 64) {
  275. s->htmsi_vector[offset_tmp] = (uint8_t)(data & 0xff);
  276. }
  277. break;
  278. case PCH_PIC_ROUTE_ENTRY_OFFSET ... PCH_PIC_ROUTE_ENTRY_END:
  279. offset_tmp = offset - PCH_PIC_ROUTE_ENTRY_OFFSET;
  280. if (offset_tmp >= 0 && offset_tmp < 64) {
  281. s->route_entry[offset_tmp] = (uint8_t)(data & 0xff);
  282. }
  283. break;
  284. default:
  285. break;
  286. }
  287. }
  288. static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = {
  289. .read = loongarch_pch_pic_low_readw,
  290. .write = loongarch_pch_pic_low_writew,
  291. .valid = {
  292. .min_access_size = 4,
  293. .max_access_size = 8,
  294. },
  295. .impl = {
  296. .min_access_size = 4,
  297. .max_access_size = 4,
  298. },
  299. .endianness = DEVICE_LITTLE_ENDIAN,
  300. };
  301. static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = {
  302. .read = loongarch_pch_pic_high_readw,
  303. .write = loongarch_pch_pic_high_writew,
  304. .valid = {
  305. .min_access_size = 4,
  306. .max_access_size = 8,
  307. },
  308. .impl = {
  309. .min_access_size = 4,
  310. .max_access_size = 4,
  311. },
  312. .endianness = DEVICE_LITTLE_ENDIAN,
  313. };
  314. static const MemoryRegionOps loongarch_pch_pic_reg8_ops = {
  315. .read = loongarch_pch_pic_readb,
  316. .write = loongarch_pch_pic_writeb,
  317. .valid = {
  318. .min_access_size = 1,
  319. .max_access_size = 1,
  320. },
  321. .impl = {
  322. .min_access_size = 1,
  323. .max_access_size = 1,
  324. },
  325. .endianness = DEVICE_LITTLE_ENDIAN,
  326. };
  327. static void loongarch_pch_pic_reset(DeviceState *d)
  328. {
  329. LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(d);
  330. int i;
  331. s->int_mask = -1;
  332. s->htmsi_en = 0x0;
  333. s->intedge = 0x0;
  334. s->intclr = 0x0;
  335. s->auto_crtl0 = 0x0;
  336. s->auto_crtl1 = 0x0;
  337. for (i = 0; i < 64; i++) {
  338. s->route_entry[i] = 0x1;
  339. s->htmsi_vector[i] = 0x0;
  340. }
  341. s->intirr = 0x0;
  342. s->intisr = 0x0;
  343. s->last_intirr = 0x0;
  344. s->int_polarity = 0x0;
  345. }
  346. static void loongarch_pic_realize(DeviceState *dev, Error **errp)
  347. {
  348. LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(dev);
  349. LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(dev);
  350. SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
  351. Error *local_err = NULL;
  352. lpc->parent_realize(dev, &local_err);
  353. if (local_err) {
  354. error_propagate(errp, local_err);
  355. return;
  356. }
  357. qdev_init_gpio_out(dev, s->parent_irq, s->irq_num);
  358. qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num);
  359. memory_region_init_io(&s->iomem32_low, OBJECT(dev),
  360. &loongarch_pch_pic_reg32_low_ops,
  361. s, PCH_PIC_NAME(.reg32_part1), 0x100);
  362. memory_region_init_io(&s->iomem8, OBJECT(dev), &loongarch_pch_pic_reg8_ops,
  363. s, PCH_PIC_NAME(.reg8), 0x2a0);
  364. memory_region_init_io(&s->iomem32_high, OBJECT(dev),
  365. &loongarch_pch_pic_reg32_high_ops,
  366. s, PCH_PIC_NAME(.reg32_part2), 0xc60);
  367. sysbus_init_mmio(sbd, &s->iomem32_low);
  368. sysbus_init_mmio(sbd, &s->iomem8);
  369. sysbus_init_mmio(sbd, &s->iomem32_high);
  370. }
  371. static void loongarch_pic_class_init(ObjectClass *klass, void *data)
  372. {
  373. DeviceClass *dc = DEVICE_CLASS(klass);
  374. LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass);
  375. device_class_set_legacy_reset(dc, loongarch_pch_pic_reset);
  376. device_class_set_parent_realize(dc, loongarch_pic_realize,
  377. &lpc->parent_realize);
  378. }
  379. static const TypeInfo loongarch_pic_types[] = {
  380. {
  381. .name = TYPE_LOONGARCH_PIC,
  382. .parent = TYPE_LOONGARCH_PIC_COMMON,
  383. .instance_size = sizeof(LoongarchPICState),
  384. .class_size = sizeof(LoongarchPICClass),
  385. .class_init = loongarch_pic_class_init,
  386. }
  387. };
  388. DEFINE_TYPES(loongarch_pic_types)