ampintc.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. /* $OpenBSD: ampintc.c,v 1.7 2015/07/15 21:09:40 jsg Exp $ */
  2. /*
  3. * Copyright (c) 2007,2009,2011 Dale Rahn <drahn@openbsd.org>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. /*
  18. * This driver implements the interrupt controller as specified in
  19. * DDI0407E_cortex_a9_mpcore_r2p0_trm with the
  20. * IHI0048A_gic_architecture_spec_v1_0 underlying specification
  21. */
  22. #include <sys/param.h>
  23. #include <sys/systm.h>
  24. #include <sys/queue.h>
  25. #include <sys/malloc.h>
  26. #include <sys/device.h>
  27. #include <sys/evcount.h>
  28. #include <arm/cpufunc.h>
  29. #include <machine/bus.h>
  30. #include <arm/cortex/cortex.h>
  31. /* offset from periphbase */
  32. #define ICP_ADDR 0x100
  33. #define ICP_SIZE 0x100
  34. #define ICD_ADDR 0x1000
  35. #define ICD_SIZE 0x1000
  36. #define ICD_A7_A15_ADDR 0x1000
  37. #define ICD_A7_A15_SIZE 0x1000
  38. #define ICP_A7_A15_ADDR 0x2000
  39. #define ICP_A7_A15_SIZE 0x1000
  40. /* registers */
  41. #define ICD_DCR 0x000
  42. #define ICD_DCR_ES 0x00000001
  43. #define ICD_DCR_ENS 0x00000002
  44. #define ICD_ICTR 0x004
  45. #define ICD_ICTR_LSPI_SH 11
  46. #define ICD_ICTR_LSPI_M 0x1f
  47. #define ICD_ICTR_CPU_SH 5
  48. #define ICD_ICTR_CPU_M 0x07
  49. #define ICD_ICTR_ITL_SH 0
  50. #define ICD_ICTR_ITL_M 0x1f
  51. #define ICD_IDIR 0x008
  52. #define ICD_DIR_PROD_SH 24
  53. #define ICD_DIR_PROD_M 0xff
  54. #define ICD_DIR_REV_SH 12
  55. #define ICD_DIR_REV_M 0xfff
  56. #define ICD_DIR_IMP_SH 0
  57. #define ICD_DIR_IMP_M 0xfff
  58. #define IRQ_TO_REG32(i) (((i) >> 5) & 0x7)
  59. #define IRQ_TO_REG32BIT(i) ((i) & 0x1f)
  60. #define IRQ_TO_REG4(i) (((i) >> 2) & 0x3f)
  61. #define IRQ_TO_REG4BIT(i) ((i) & 0x3)
  62. #define IRQ_TO_REG16(i) (((i) >> 4) & 0xf)
  63. #define IRQ_TO_REGBIT_S(i) 8
  64. #define IRQ_TO_REG4BIT_M(i) 8
  65. #define ICD_ISRn(i) (0x080 + (IRQ_TO_REG32(i) * 4))
  66. #define ICD_ISERn(i) (0x100 + (IRQ_TO_REG32(i) * 4))
  67. #define ICD_ICERn(i) (0x180 + (IRQ_TO_REG32(i) * 4))
  68. #define ICD_ISPRn(i) (0x200 + (IRQ_TO_REG32(i) * 4))
  69. #define ICD_ICPRn(i) (0x280 + (IRQ_TO_REG32(i) * 4))
  70. #define ICD_ABRn(i) (0x300 + (IRQ_TO_REG32(i) * 4))
  71. #define ICD_IPRn(i) (0x400 + (i))
  72. #define ICD_IPTRn(i) (0x800 + (i))
  73. #define ICD_ICRn(i) (0xC00 + (IRQ_TO_REG16(i) * 4))
  74. /*
  75. * what about (ppi|spi)_status
  76. */
  77. #define ICD_PPI 0xD00
  78. #define ICD_PPI_GTIMER (1 << 11)
  79. #define ICD_PPI_FIQ (1 << 12)
  80. #define ICD_PPI_PTIMER (1 << 13)
  81. #define ICD_PPI_PWDOG (1 << 14)
  82. #define ICD_PPI_IRQ (1 << 15)
  83. #define ICD_SPI_BASE 0xD04
  84. #define ICD_SPIn(i) (ICD_SPI_BASE + ((i) * 4))
  85. #define ICD_SGIR 0xF00
  86. #define ICD_PERIPH_ID_0 0xFD0
  87. #define ICD_PERIPH_ID_1 0xFD4
  88. #define ICD_PERIPH_ID_2 0xFD8
  89. #define ICD_PERIPH_ID_3 0xFDC
  90. #define ICD_PERIPH_ID_4 0xFE0
  91. #define ICD_PERIPH_ID_5 0xFE4
  92. #define ICD_PERIPH_ID_6 0xFE8
  93. #define ICD_PERIPH_ID_7 0xFEC
  94. #define ICD_COMP_ID_0 0xFEC
  95. #define ICD_COMP_ID_1 0xFEC
  96. #define ICD_COMP_ID_2 0xFEC
  97. #define ICD_COMP_ID_3 0xFEC
  98. #define ICD_SIZE 0x1000
  99. #define ICPICR 0x00
  100. #define ICPIPMR 0x04
  101. /* XXX - must left justify bits to 0 - 7 */
  102. #define ICMIPMR_SH 4
  103. #define ICPBPR 0x08
  104. #define ICPIAR 0x0C
  105. #define ICPIAR_IRQ_SH 0
  106. #define ICPIAR_IRQ_M 0x3ff
  107. #define ICPIAR_CPUID_SH 10
  108. #define ICPIAR_CPUID_M 0x7
  109. #define ICPIAR_NO_PENDING_IRQ ICPIAR_IRQ_M
  110. #define ICPEOIR 0x10
  111. #define ICPPRP 0x14
  112. #define ICPHPIR 0x18
  113. #define ICPIIR 0xFC
  114. #define ICP_SIZE 0x100
  115. /*
  116. * what about periph_id and component_id
  117. */
  118. #define AMPAMPINTC_SIZE 0x1000
  119. #define IRQ_ENABLE 1
  120. #define IRQ_DISABLE 0
  121. struct ampintc_softc {
  122. struct device sc_dev;
  123. struct intrq *sc_ampintc_handler;
  124. int sc_nintr;
  125. bus_space_tag_t sc_iot;
  126. bus_space_handle_t sc_d_ioh, sc_p_ioh;
  127. struct evcount sc_spur;
  128. };
  129. struct ampintc_softc *ampintc;
  130. struct intrhand {
  131. TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */
  132. int (*ih_func)(void *); /* handler */
  133. void *ih_arg; /* arg for handler */
  134. int ih_ipl; /* IPL_* */
  135. int ih_irq; /* IRQ number */
  136. struct evcount ih_count;
  137. char *ih_name;
  138. };
  139. struct intrq {
  140. TAILQ_HEAD(, intrhand) iq_list; /* handler list */
  141. int iq_irq; /* IRQ to mask while handling */
  142. int iq_levels; /* IPL_*'s this IRQ has */
  143. int iq_ist; /* share type */
  144. };
  145. int ampintc_match(struct device *, void *, void *);
  146. void ampintc_attach(struct device *, struct device *, void *);
  147. int ampintc_spllower(int);
  148. void ampintc_splx(int);
  149. int ampintc_splraise(int);
  150. void ampintc_setipl(int);
  151. void ampintc_calc_mask(void);
  152. void *ampintc_intr_establish(int, int, int (*)(void *), void *,
  153. char *);
  154. void *ampintc_intr_establish_ext(int, int, int (*)(void *), void *,
  155. char *);
  156. void ampintc_intr_disestablish(void *);
  157. void ampintc_irq_handler(void *);
  158. const char *ampintc_intr_string(void *);
  159. uint32_t ampintc_iack(void);
  160. void ampintc_eoi(uint32_t);
  161. void ampintc_set_priority(int, int);
  162. void ampintc_intr_enable(int);
  163. void ampintc_intr_disable(int);
  164. void ampintc_route(int, int , int);
  165. struct cfattach ampintc_ca = {
  166. sizeof (struct ampintc_softc), ampintc_match, ampintc_attach
  167. };
  168. struct cfdriver ampintc_cd = {
  169. NULL, "ampintc", DV_DULL
  170. };
  171. int
  172. ampintc_match(struct device *parent, void *cfdata, void *aux)
  173. {
  174. return (1);
  175. }
  176. paddr_t gic_dist_base, gic_cpu_base, gic_dist_size, gic_cpu_size;
  177. void
  178. ampintc_attach(struct device *parent, struct device *self, void *args)
  179. {
  180. struct ampintc_softc *sc = (struct ampintc_softc *)self;
  181. struct cortex_attach_args *ia = args;
  182. int i, nintr;
  183. bus_space_tag_t iot;
  184. bus_space_handle_t d_ioh, p_ioh;
  185. uint32_t icp, icpsize, icd, icdsize;
  186. ampintc = sc;
  187. arm_init_smask();
  188. iot = ia->ca_iot;
  189. icp = ia->ca_periphbase + ICP_ADDR;
  190. icpsize = ICP_SIZE;
  191. icd = ia->ca_periphbase + ICD_ADDR;
  192. icdsize = ICD_SIZE;
  193. if ((cputype & CPU_ID_CORTEX_A7_MASK) == CPU_ID_CORTEX_A7 ||
  194. (cputype & CPU_ID_CORTEX_A15_MASK) == CPU_ID_CORTEX_A15 ||
  195. (cputype & CPU_ID_CORTEX_A17_MASK) == CPU_ID_CORTEX_A17) {
  196. icp = ia->ca_periphbase + ICP_A7_A15_ADDR;
  197. icpsize = ICP_A7_A15_SIZE;
  198. icd = ia->ca_periphbase + ICD_A7_A15_ADDR;
  199. icdsize = ICD_A7_A15_SIZE;
  200. }
  201. /* exynos gic isn't at the expected offsets from periphbase */
  202. if (gic_cpu_base)
  203. icp = gic_cpu_base;
  204. if (gic_cpu_size)
  205. icpsize = gic_cpu_size;
  206. if (gic_dist_base)
  207. icd = gic_dist_base;
  208. if (gic_dist_size)
  209. icdsize = gic_dist_size;
  210. if (bus_space_map(iot, icp, icpsize, 0, &p_ioh))
  211. panic("ampintc_attach: ICP bus_space_map failed!");
  212. if (bus_space_map(iot, icd, icdsize, 0, &d_ioh))
  213. panic("ampintc_attach: ICD bus_space_map failed!");
  214. sc->sc_iot = iot;
  215. sc->sc_d_ioh = d_ioh;
  216. sc->sc_p_ioh = p_ioh;
  217. evcount_attach(&sc->sc_spur, "irq1023/spur", NULL);
  218. nintr = 32 * (bus_space_read_4(iot, d_ioh, ICD_ICTR) & ICD_ICTR_ITL_M);
  219. nintr += 32; /* ICD_ICTR + 1, irq 0-31 is SGI, 32+ is PPI */
  220. sc->sc_nintr = nintr;
  221. printf(" nirq %d\n", nintr);
  222. /* Disable all interrupts, clear all pending */
  223. for (i = 0; i < nintr/32; i++) {
  224. bus_space_write_4(iot, d_ioh, ICD_ICERn(i*32), ~0);
  225. bus_space_write_4(iot, d_ioh, ICD_ICPRn(i*32), ~0);
  226. }
  227. for (i = 0; i < nintr; i++) {
  228. /* lowest priority ?? */
  229. bus_space_write_1(iot, d_ioh, ICD_IPRn(i), 0xff);
  230. /* target no cpus */
  231. bus_space_write_1(iot, d_ioh, ICD_IPTRn(i), 0);
  232. }
  233. for (i = 2; i < nintr/16; i++) {
  234. /* irq 32 - N */
  235. bus_space_write_4(iot, d_ioh, ICD_ICRn(i*16), 0);
  236. }
  237. /* software reset of the part? */
  238. /* set protection bit (kernel only)? */
  239. /* XXX - check power saving bit */
  240. sc->sc_ampintc_handler = mallocarray(nintr,
  241. sizeof(*sc->sc_ampintc_handler), M_DEVBUF, M_ZERO | M_NOWAIT);
  242. for (i = 0; i < nintr; i++) {
  243. TAILQ_INIT(&sc->sc_ampintc_handler[i].iq_list);
  244. }
  245. ampintc_setipl(IPL_HIGH); /* XXX ??? */
  246. ampintc_calc_mask();
  247. /* insert self as interrupt handler */
  248. arm_set_intr_handler(ampintc_splraise, ampintc_spllower, ampintc_splx,
  249. ampintc_setipl, ampintc_intr_establish_ext,
  250. ampintc_intr_disestablish, ampintc_intr_string, ampintc_irq_handler);
  251. /* enable interrupts */
  252. bus_space_write_4(iot, d_ioh, ICD_DCR, 3);
  253. bus_space_write_4(iot, p_ioh, ICPICR, 1);
  254. enable_interrupts(I32_bit);
  255. }
  256. void
  257. ampintc_set_priority(int irq, int pri)
  258. {
  259. struct ampintc_softc *sc = ampintc;
  260. uint32_t prival;
  261. /*
  262. * We only use 16 (13 really) interrupt priorities,
  263. * and a CPU is only required to implement bit 4-7 of each field
  264. * so shift into the top bits.
  265. * also low values are higher priority thus IPL_HIGH - pri
  266. */
  267. prival = (IPL_HIGH - pri) << ICMIPMR_SH;
  268. bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPRn(irq), prival);
  269. }
  270. void
  271. ampintc_setipl(int new)
  272. {
  273. struct cpu_info *ci = curcpu();
  274. struct ampintc_softc *sc = ampintc;
  275. int psw;
  276. /* disable here is only to keep hardware in sync with ci->ci_cpl */
  277. psw = disable_interrupts(I32_bit);
  278. ci->ci_cpl = new;
  279. /* low values are higher priority thus IPL_HIGH - pri */
  280. bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPIPMR,
  281. (IPL_HIGH - new) << ICMIPMR_SH);
  282. restore_interrupts(psw);
  283. }
  284. void
  285. ampintc_intr_enable(int irq)
  286. {
  287. struct ampintc_softc *sc = ampintc;
  288. #ifdef DEBUG
  289. printf("enable irq %d register %x bitmask %08x\n",
  290. irq, ICD_ISERn(irq), 1 << IRQ_TO_REG32BIT(irq));
  291. #endif
  292. bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ISERn(irq),
  293. 1 << IRQ_TO_REG32BIT(irq));
  294. }
  295. void
  296. ampintc_intr_disable(int irq)
  297. {
  298. struct ampintc_softc *sc = ampintc;
  299. bus_space_write_4(sc->sc_iot, sc->sc_d_ioh, ICD_ICERn(irq),
  300. 1 << IRQ_TO_REG32BIT(irq));
  301. }
  302. void
  303. ampintc_calc_mask(void)
  304. {
  305. struct cpu_info *ci = curcpu();
  306. struct ampintc_softc *sc = ampintc;
  307. struct intrhand *ih;
  308. int irq;
  309. for (irq = 0; irq < sc->sc_nintr; irq++) {
  310. int max = IPL_NONE;
  311. int min = IPL_HIGH;
  312. TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list,
  313. ih_list) {
  314. if (ih->ih_ipl > max)
  315. max = ih->ih_ipl;
  316. if (ih->ih_ipl < min)
  317. min = ih->ih_ipl;
  318. }
  319. if (sc->sc_ampintc_handler[irq].iq_irq == max) {
  320. continue;
  321. }
  322. sc->sc_ampintc_handler[irq].iq_irq = max;
  323. if (max == IPL_NONE)
  324. min = IPL_NONE;
  325. #ifdef DEBUG_INTC
  326. if (min != IPL_NONE) {
  327. printf("irq %d to block at %d %d reg %d bit %d\n",
  328. irq, max, min, AMPINTC_IRQ_TO_REG(irq),
  329. AMPINTC_IRQ_TO_REGi(irq));
  330. }
  331. #endif
  332. /* Enable interrupts at lower levels, clear -> enable */
  333. /* Set interrupt priority/enable */
  334. if (min != IPL_NONE) {
  335. ampintc_set_priority(irq, min);
  336. ampintc_intr_enable(irq);
  337. ampintc_route(irq, IRQ_ENABLE, 0);
  338. } else {
  339. ampintc_intr_disable(irq);
  340. ampintc_route(irq, IRQ_DISABLE, 0);
  341. }
  342. }
  343. ampintc_setipl(ci->ci_cpl);
  344. }
  345. void
  346. ampintc_splx(int new)
  347. {
  348. struct cpu_info *ci = curcpu();
  349. if (ci->ci_ipending & arm_smask[new])
  350. arm_do_pending_intr(new);
  351. ampintc_setipl(new);
  352. }
  353. int
  354. ampintc_spllower(int new)
  355. {
  356. struct cpu_info *ci = curcpu();
  357. int old = ci->ci_cpl;
  358. ampintc_splx(new);
  359. return (old);
  360. }
  361. int
  362. ampintc_splraise(int new)
  363. {
  364. struct cpu_info *ci = curcpu();
  365. int old;
  366. old = ci->ci_cpl;
  367. /*
  368. * setipl must always be called because there is a race window
  369. * where the variable is updated before the mask is set
  370. * an interrupt occurs in that window without the mask always
  371. * being set, the hardware might not get updated on the next
  372. * splraise completely messing up spl protection.
  373. */
  374. if (old > new)
  375. new = old;
  376. ampintc_setipl(new);
  377. return (old);
  378. }
  379. uint32_t
  380. ampintc_iack(void)
  381. {
  382. uint32_t intid;
  383. struct ampintc_softc *sc = ampintc;
  384. intid = bus_space_read_4(sc->sc_iot, sc->sc_p_ioh, ICPIAR);
  385. return (intid);
  386. }
  387. void
  388. ampintc_eoi(uint32_t eoi)
  389. {
  390. struct ampintc_softc *sc = ampintc;
  391. bus_space_write_4(sc->sc_iot, sc->sc_p_ioh, ICPEOIR, eoi);
  392. }
  393. void
  394. ampintc_route(int irq, int enable, int cpu)
  395. {
  396. uint8_t val;
  397. struct ampintc_softc *sc = ampintc;
  398. val = bus_space_read_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq));
  399. if (enable == IRQ_ENABLE)
  400. val |= (1 << cpu);
  401. else
  402. val &= ~(1 << cpu);
  403. bus_space_write_1(sc->sc_iot, sc->sc_d_ioh, ICD_IPTRn(irq), val);
  404. }
  405. void
  406. ampintc_irq_handler(void *frame)
  407. {
  408. struct ampintc_softc *sc = ampintc;
  409. struct intrhand *ih;
  410. void *arg;
  411. uint32_t iack_val;
  412. int irq, pri, s;
  413. iack_val = ampintc_iack();
  414. //#define DEBUG_INTC
  415. #ifdef DEBUG_INTC
  416. if (iack_val != 27)
  417. printf("irq %d fired\n", iack_val);
  418. else {
  419. static int cnt = 0;
  420. if ((cnt++ % 100) == 0) {
  421. printf("irq %d fired * _100\n", iack_val);
  422. Debugger();
  423. }
  424. }
  425. #endif
  426. if (iack_val == 1023) {
  427. sc->sc_spur.ec_count++;
  428. return;
  429. }
  430. irq = iack_val & ((1 << sc->sc_nintr) - 1);
  431. pri = sc->sc_ampintc_handler[irq].iq_irq;
  432. s = ampintc_splraise(pri);
  433. TAILQ_FOREACH(ih, &sc->sc_ampintc_handler[irq].iq_list, ih_list) {
  434. if (ih->ih_arg != 0)
  435. arg = ih->ih_arg;
  436. else
  437. arg = frame;
  438. if (ih->ih_func(arg))
  439. ih->ih_count.ec_count++;
  440. }
  441. ampintc_eoi(iack_val);
  442. ampintc_splx(s);
  443. }
  444. void *
  445. ampintc_intr_establish_ext(int irqno, int level, int (*func)(void *),
  446. void *arg, char *name)
  447. {
  448. return ampintc_intr_establish(irqno+32, level, func, arg, name);
  449. }
  450. void *
  451. ampintc_intr_establish(int irqno, int level, int (*func)(void *),
  452. void *arg, char *name)
  453. {
  454. struct ampintc_softc *sc = ampintc;
  455. struct intrhand *ih;
  456. int psw;
  457. if (irqno < 0 || irqno >= sc->sc_nintr)
  458. panic("ampintc_intr_establish: bogus irqnumber %d: %s",
  459. irqno, name);
  460. /* no point in sleeping unless someone can free memory. */
  461. ih = (struct intrhand *)malloc (sizeof *ih, M_DEVBUF,
  462. cold ? M_NOWAIT : M_WAITOK);
  463. if (ih == NULL)
  464. panic("intr_establish: can't malloc handler info");
  465. ih->ih_func = func;
  466. ih->ih_arg = arg;
  467. ih->ih_ipl = level;
  468. ih->ih_irq = irqno;
  469. ih->ih_name = name;
  470. psw = disable_interrupts(I32_bit);
  471. TAILQ_INSERT_TAIL(&sc->sc_ampintc_handler[irqno].iq_list, ih, ih_list);
  472. if (name != NULL)
  473. evcount_attach(&ih->ih_count, name, &ih->ih_irq);
  474. #ifdef DEBUG_INTC
  475. printf("ampintc_intr_establish irq %d level %d [%s]\n", irqno, level,
  476. name);
  477. #endif
  478. ampintc_calc_mask();
  479. restore_interrupts(psw);
  480. return (ih);
  481. }
  482. void
  483. ampintc_intr_disestablish(void *cookie)
  484. {
  485. #if 0
  486. int psw;
  487. struct intrhand *ih = cookie;
  488. int irqno = ih->ih_irq;
  489. psw = disable_interrupts(I32_bit);
  490. TAILQ_REMOVE(&sc->sc_ampintc_handler[irqno].iq_list, ih, ih_list);
  491. if (ih->ih_name != NULL)
  492. evcount_detach(&ih->ih_count);
  493. free(ih, M_DEVBUF, 0);
  494. restore_interrupts(psw);
  495. #endif
  496. }
  497. const char *
  498. ampintc_intr_string(void *cookie)
  499. {
  500. struct intrhand *ih = (struct intrhand *)cookie;
  501. static char irqstr[1 + sizeof("ampintc irq ") + 4];
  502. snprintf(irqstr, sizeof irqstr, "ampintc irq %d", ih->ih_irq);
  503. return irqstr;
  504. }