xref: /illumos-gate/usr/src/uts/sun4u/io/pci/pci_ib.c (revision f910463c)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23*f910463cSgovinda  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
287c478bd9Sstevel@tonic-gate 
297c478bd9Sstevel@tonic-gate /*
307c478bd9Sstevel@tonic-gate  * PCI Interrupt Block (RISCx) implementation
317c478bd9Sstevel@tonic-gate  *	initialization
327c478bd9Sstevel@tonic-gate  *	interrupt enable/disable/clear and mapping register manipulation
337c478bd9Sstevel@tonic-gate  */
347c478bd9Sstevel@tonic-gate 
357c478bd9Sstevel@tonic-gate #include <sys/types.h>
367c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
377c478bd9Sstevel@tonic-gate #include <sys/async.h>
387c478bd9Sstevel@tonic-gate #include <sys/systm.h>		/* panicstr */
397c478bd9Sstevel@tonic-gate #include <sys/spl.h>
407c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
417c478bd9Sstevel@tonic-gate #include <sys/machsystm.h>	/* intr_dist_add */
427c478bd9Sstevel@tonic-gate #include <sys/ddi_impldefs.h>
437c478bd9Sstevel@tonic-gate #include <sys/clock.h>
447c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h>
457c478bd9Sstevel@tonic-gate #include <sys/pci/pci_obj.h>
467c478bd9Sstevel@tonic-gate 
477c478bd9Sstevel@tonic-gate #ifdef _STARFIRE
487c478bd9Sstevel@tonic-gate #include <sys/starfire.h>
497c478bd9Sstevel@tonic-gate #endif /* _STARFIRE */
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate /*LINTLIBRARY*/
527c478bd9Sstevel@tonic-gate static uint_t ib_intr_reset(void *arg);
537c478bd9Sstevel@tonic-gate 
547c478bd9Sstevel@tonic-gate void
557c478bd9Sstevel@tonic-gate ib_create(pci_t *pci_p)
567c478bd9Sstevel@tonic-gate {
577c478bd9Sstevel@tonic-gate 	dev_info_t *dip = pci_p->pci_dip;
587c478bd9Sstevel@tonic-gate 	ib_t *ib_p;
597c478bd9Sstevel@tonic-gate 	uintptr_t a;
607c478bd9Sstevel@tonic-gate 	int i;
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate 	/*
637c478bd9Sstevel@tonic-gate 	 * Allocate interrupt block state structure and link it to
647c478bd9Sstevel@tonic-gate 	 * the pci state structure.
657c478bd9Sstevel@tonic-gate 	 */
667c478bd9Sstevel@tonic-gate 	ib_p = kmem_zalloc(sizeof (ib_t), KM_SLEEP);
677c478bd9Sstevel@tonic-gate 	pci_p->pci_ib_p = ib_p;
687c478bd9Sstevel@tonic-gate 	ib_p->ib_pci_p = pci_p;
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate 	a = pci_ib_setup(ib_p);
717c478bd9Sstevel@tonic-gate 
727c478bd9Sstevel@tonic-gate 	/*
737c478bd9Sstevel@tonic-gate 	 * Determine virtual addresses of interrupt mapping, clear and diag
747c478bd9Sstevel@tonic-gate 	 * registers that have common offsets.
757c478bd9Sstevel@tonic-gate 	 */
767c478bd9Sstevel@tonic-gate 	ib_p->ib_slot_clear_intr_regs =
777c478bd9Sstevel@tonic-gate 		a + COMMON_IB_SLOT_CLEAR_INTR_REG_OFFSET;
787c478bd9Sstevel@tonic-gate 	ib_p->ib_intr_retry_timer_reg =
797c478bd9Sstevel@tonic-gate 		(uint64_t *)(a + COMMON_IB_INTR_RETRY_TIMER_OFFSET);
807c478bd9Sstevel@tonic-gate 	ib_p->ib_slot_intr_state_diag_reg =
817c478bd9Sstevel@tonic-gate 		(uint64_t *)(a + COMMON_IB_SLOT_INTR_STATE_DIAG_REG);
827c478bd9Sstevel@tonic-gate 	ib_p->ib_obio_intr_state_diag_reg =
837c478bd9Sstevel@tonic-gate 		(uint64_t *)(a + COMMON_IB_OBIO_INTR_STATE_DIAG_REG);
847c478bd9Sstevel@tonic-gate 
857c478bd9Sstevel@tonic-gate 	if (CHIP_TYPE(pci_p) != PCI_CHIP_XMITS) {
867c478bd9Sstevel@tonic-gate 		ib_p->ib_upa_imr[0] = (volatile uint64_t *)
877c478bd9Sstevel@tonic-gate 				(a + COMMON_IB_UPA0_INTR_MAP_REG_OFFSET);
887c478bd9Sstevel@tonic-gate 		ib_p->ib_upa_imr[1] = (volatile uint64_t *)
897c478bd9Sstevel@tonic-gate 				(a + COMMON_IB_UPA1_INTR_MAP_REG_OFFSET);
907c478bd9Sstevel@tonic-gate 	}
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	DEBUG2(DBG_ATTACH, dip, "ib_create: slot_imr=%x, slot_cir=%x\n",
937c478bd9Sstevel@tonic-gate 		ib_p->ib_slot_intr_map_regs, ib_p->ib_obio_intr_map_regs);
947c478bd9Sstevel@tonic-gate 	DEBUG2(DBG_ATTACH, dip, "ib_create: obio_imr=%x, obio_cir=%x\n",
957c478bd9Sstevel@tonic-gate 		ib_p->ib_slot_clear_intr_regs, ib_p->ib_obio_clear_intr_regs);
967c478bd9Sstevel@tonic-gate 	DEBUG2(DBG_ATTACH, dip, "ib_create: upa0_imr=%x, upa1_imr=%x\n",
977c478bd9Sstevel@tonic-gate 		ib_p->ib_upa_imr[0], ib_p->ib_upa_imr[1]);
987c478bd9Sstevel@tonic-gate 	DEBUG3(DBG_ATTACH, dip,
997c478bd9Sstevel@tonic-gate 		"ib_create: retry_timer=%x, obio_diag=%x slot_diag=%x\n",
1007c478bd9Sstevel@tonic-gate 		ib_p->ib_intr_retry_timer_reg,
1017c478bd9Sstevel@tonic-gate 		ib_p->ib_obio_intr_state_diag_reg,
1027c478bd9Sstevel@tonic-gate 		ib_p->ib_slot_intr_state_diag_reg);
1037c478bd9Sstevel@tonic-gate 
1047c478bd9Sstevel@tonic-gate 	ib_p->ib_ino_lst = (ib_ino_info_t *)NULL;
1057c478bd9Sstevel@tonic-gate 	mutex_init(&ib_p->ib_intr_lock, NULL, MUTEX_DRIVER, NULL);
1067c478bd9Sstevel@tonic-gate 	mutex_init(&ib_p->ib_ino_lst_mutex, NULL, MUTEX_DRIVER, NULL);
1077c478bd9Sstevel@tonic-gate 
1087c478bd9Sstevel@tonic-gate 	DEBUG1(DBG_ATTACH, dip, "ib_create: numproxy=%x\n",
1097c478bd9Sstevel@tonic-gate 		pci_p->pci_numproxy);
1107c478bd9Sstevel@tonic-gate 	for (i = 1; i <= pci_p->pci_numproxy; i++) {
1117c478bd9Sstevel@tonic-gate 		set_intr_mapping_reg(pci_p->pci_id,
1127c478bd9Sstevel@tonic-gate 			(uint64_t *)ib_p->ib_upa_imr[i - 1], i);
1137c478bd9Sstevel@tonic-gate 	}
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate 	ib_configure(ib_p);
1167c478bd9Sstevel@tonic-gate 	bus_func_register(BF_TYPE_RESINTR, ib_intr_reset, ib_p);
1177c478bd9Sstevel@tonic-gate }
1187c478bd9Sstevel@tonic-gate 
1197c478bd9Sstevel@tonic-gate void
1207c478bd9Sstevel@tonic-gate ib_destroy(pci_t *pci_p)
1217c478bd9Sstevel@tonic-gate {
1227c478bd9Sstevel@tonic-gate 	ib_t *ib_p = pci_p->pci_ib_p;
1237c478bd9Sstevel@tonic-gate 	dev_info_t *dip = pci_p->pci_dip;
1247c478bd9Sstevel@tonic-gate 
1257c478bd9Sstevel@tonic-gate 	DEBUG0(DBG_IB, dip, "ib_destroy\n");
1267c478bd9Sstevel@tonic-gate 	bus_func_unregister(BF_TYPE_RESINTR, ib_intr_reset, ib_p);
1277c478bd9Sstevel@tonic-gate 
1287c478bd9Sstevel@tonic-gate 	intr_dist_rem_weighted(ib_intr_dist_all, ib_p);
1297c478bd9Sstevel@tonic-gate 	mutex_destroy(&ib_p->ib_ino_lst_mutex);
1307c478bd9Sstevel@tonic-gate 	mutex_destroy(&ib_p->ib_intr_lock);
1317c478bd9Sstevel@tonic-gate 
1327c478bd9Sstevel@tonic-gate 	ib_free_ino_all(ib_p);
1337c478bd9Sstevel@tonic-gate 
1347c478bd9Sstevel@tonic-gate 	kmem_free(ib_p, sizeof (ib_t));
1357c478bd9Sstevel@tonic-gate 	pci_p->pci_ib_p = NULL;
1367c478bd9Sstevel@tonic-gate }
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate void
1397c478bd9Sstevel@tonic-gate ib_configure(ib_t *ib_p)
1407c478bd9Sstevel@tonic-gate {
1417c478bd9Sstevel@tonic-gate 	/* XXX could be different between psycho and schizo */
1427c478bd9Sstevel@tonic-gate 	*ib_p->ib_intr_retry_timer_reg = pci_intr_retry_intv;
1437c478bd9Sstevel@tonic-gate }
1447c478bd9Sstevel@tonic-gate 
1457c478bd9Sstevel@tonic-gate /*
1467c478bd9Sstevel@tonic-gate  * can only used for psycho internal interrupts thermal, power,
1477c478bd9Sstevel@tonic-gate  * ue, ce, pbm
1487c478bd9Sstevel@tonic-gate  */
1497c478bd9Sstevel@tonic-gate void
1507c478bd9Sstevel@tonic-gate ib_intr_enable(pci_t *pci_p, ib_ino_t ino)
1517c478bd9Sstevel@tonic-gate {
1527c478bd9Sstevel@tonic-gate 	ib_t *ib_p = pci_p->pci_ib_p;
1537c478bd9Sstevel@tonic-gate 	ib_mondo_t mondo = IB_INO_TO_MONDO(ib_p, ino);
1547c478bd9Sstevel@tonic-gate 	volatile uint64_t *imr_p = ib_intr_map_reg_addr(ib_p, ino);
1557c478bd9Sstevel@tonic-gate 	uint_t cpu_id;
1567c478bd9Sstevel@tonic-gate 
1577c478bd9Sstevel@tonic-gate 	/*
1587c478bd9Sstevel@tonic-gate 	 * Determine the cpu for the interrupt.
1597c478bd9Sstevel@tonic-gate 	 */
1607c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_intr_lock);
1617c478bd9Sstevel@tonic-gate 	cpu_id = intr_dist_cpuid();
1627c478bd9Sstevel@tonic-gate #ifdef _STARFIRE
1637c478bd9Sstevel@tonic-gate 	cpu_id = pc_translate_tgtid(IB2CB(ib_p)->cb_ittrans_cookie, cpu_id,
1647c478bd9Sstevel@tonic-gate 		IB_GET_MAPREG_INO(ino));
1657c478bd9Sstevel@tonic-gate #endif /* _STARFIRE */
1667c478bd9Sstevel@tonic-gate 	DEBUG2(DBG_IB, pci_p->pci_dip,
1677c478bd9Sstevel@tonic-gate 		"ib_intr_enable: ino=%x cpu_id=%x\n", ino, cpu_id);
1687c478bd9Sstevel@tonic-gate 
1697c478bd9Sstevel@tonic-gate 	*imr_p = ib_get_map_reg(mondo, cpu_id);
1707c478bd9Sstevel@tonic-gate 	IB_INO_INTR_CLEAR(ib_clear_intr_reg_addr(ib_p, ino));
1717c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_intr_lock);
1727c478bd9Sstevel@tonic-gate }
1737c478bd9Sstevel@tonic-gate 
1747c478bd9Sstevel@tonic-gate /*
1757c478bd9Sstevel@tonic-gate  * Disable the interrupt via its interrupt mapping register.
1767c478bd9Sstevel@tonic-gate  * Can only be used for internal interrupts: thermal, power, ue, ce, pbm.
1777c478bd9Sstevel@tonic-gate  * If called under interrupt context, wait should be set to 0
1787c478bd9Sstevel@tonic-gate  */
1797c478bd9Sstevel@tonic-gate void
1807c478bd9Sstevel@tonic-gate ib_intr_disable(ib_t *ib_p, ib_ino_t ino, int wait)
1817c478bd9Sstevel@tonic-gate {
1827c478bd9Sstevel@tonic-gate 	volatile uint64_t *imr_p = ib_intr_map_reg_addr(ib_p, ino);
1837c478bd9Sstevel@tonic-gate 	volatile uint64_t *state_reg_p = IB_INO_INTR_STATE_REG(ib_p, ino);
1847c478bd9Sstevel@tonic-gate 	hrtime_t start_time;
1857c478bd9Sstevel@tonic-gate 
1867c478bd9Sstevel@tonic-gate 	/* disable the interrupt */
1877c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_intr_lock);
1887c478bd9Sstevel@tonic-gate 	IB_INO_INTR_OFF(imr_p);
1897c478bd9Sstevel@tonic-gate 	*imr_p;	/* flush previous write */
1907c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_intr_lock);
1917c478bd9Sstevel@tonic-gate 
1927c478bd9Sstevel@tonic-gate 	if (!wait)
1937c478bd9Sstevel@tonic-gate 		goto wait_done;
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	start_time = gethrtime();
1967c478bd9Sstevel@tonic-gate 	/* busy wait if there is interrupt being processed */
1977c478bd9Sstevel@tonic-gate 	while (IB_INO_INTR_PENDING(state_reg_p, ino) && !panicstr) {
1987c478bd9Sstevel@tonic-gate 		if (gethrtime() - start_time > pci_intrpend_timeout) {
1997c478bd9Sstevel@tonic-gate 			pbm_t *pbm_p = ib_p->ib_pci_p->pci_pbm_p;
2007c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s:%s: ib_intr_disable timeout %x",
2017c478bd9Sstevel@tonic-gate 				pbm_p->pbm_nameinst_str,
2027c478bd9Sstevel@tonic-gate 				pbm_p->pbm_nameaddr_str, ino);
2037c478bd9Sstevel@tonic-gate 				break;
2047c478bd9Sstevel@tonic-gate 		}
2057c478bd9Sstevel@tonic-gate 	}
2067c478bd9Sstevel@tonic-gate wait_done:
2077c478bd9Sstevel@tonic-gate 	IB_INO_INTR_PEND(ib_clear_intr_reg_addr(ib_p, ino));
2087c478bd9Sstevel@tonic-gate #ifdef _STARFIRE
2097c478bd9Sstevel@tonic-gate 	pc_ittrans_cleanup(IB2CB(ib_p)->cb_ittrans_cookie,
210f47a9c50Smathue 	    (volatile uint64_t *)(uintptr_t)ino);
2117c478bd9Sstevel@tonic-gate #endif /* _STARFIRE */
2127c478bd9Sstevel@tonic-gate }
2137c478bd9Sstevel@tonic-gate 
2147c478bd9Sstevel@tonic-gate /* can only used for psycho internal interrupts thermal, power, ue, ce, pbm */
2157c478bd9Sstevel@tonic-gate void
2167c478bd9Sstevel@tonic-gate ib_nintr_clear(ib_t *ib_p, ib_ino_t ino)
2177c478bd9Sstevel@tonic-gate {
2187c478bd9Sstevel@tonic-gate 	uint64_t *clr_reg = ib_clear_intr_reg_addr(ib_p, ino);
2197c478bd9Sstevel@tonic-gate 	IB_INO_INTR_CLEAR(clr_reg);
2207c478bd9Sstevel@tonic-gate }
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate /*
2237c478bd9Sstevel@tonic-gate  * distribute PBM and UPA interrupts. ino is set to 0 by caller if we
2247c478bd9Sstevel@tonic-gate  * are dealing with UPA interrupts (without inos).
2257c478bd9Sstevel@tonic-gate  */
2267c478bd9Sstevel@tonic-gate void
2277c478bd9Sstevel@tonic-gate ib_intr_dist_nintr(ib_t *ib_p, ib_ino_t ino, volatile uint64_t *imr_p)
2287c478bd9Sstevel@tonic-gate {
2297c478bd9Sstevel@tonic-gate 	volatile uint64_t imr = *imr_p;
2307c478bd9Sstevel@tonic-gate 	uint32_t cpu_id;
2317c478bd9Sstevel@tonic-gate 
2327c478bd9Sstevel@tonic-gate 	if (!IB_INO_INTR_ISON(imr))
2337c478bd9Sstevel@tonic-gate 		return;
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate 	cpu_id = intr_dist_cpuid();
2367c478bd9Sstevel@tonic-gate 
2377c478bd9Sstevel@tonic-gate #ifdef _STARFIRE
2387c478bd9Sstevel@tonic-gate 	if (ino) {
2397c478bd9Sstevel@tonic-gate 		cpu_id = pc_translate_tgtid(IB2CB(ib_p)->cb_ittrans_cookie,
2407c478bd9Sstevel@tonic-gate 			cpu_id, IB_GET_MAPREG_INO(ino));
2417c478bd9Sstevel@tonic-gate 	}
2427c478bd9Sstevel@tonic-gate #else /* _STARFIRE */
2437c478bd9Sstevel@tonic-gate 	if (ib_map_reg_get_cpu(*imr_p) == cpu_id)
2447c478bd9Sstevel@tonic-gate 		return;
2457c478bd9Sstevel@tonic-gate #endif /* _STARFIRE */
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	*imr_p = ib_get_map_reg(IB_IMR2MONDO(imr), cpu_id);
2487c478bd9Sstevel@tonic-gate 	imr = *imr_p;	/* flush previous write */
2497c478bd9Sstevel@tonic-gate }
2507c478bd9Sstevel@tonic-gate 
2517851eb82Sschwartz /*
2527851eb82Sschwartz  * Converts into nsec, ticks logged with a given CPU.  Adds nsec to ih.
2537851eb82Sschwartz  */
2547851eb82Sschwartz /*ARGSUSED*/
2557851eb82Sschwartz void
2567851eb82Sschwartz ib_cpu_ticks_to_ih_nsec(ib_t *ib_p, ih_t *ih_p, uint32_t cpu_id)
2577851eb82Sschwartz {
2587851eb82Sschwartz 	extern kmutex_t pciintr_ks_template_lock;
2597851eb82Sschwartz 	hrtime_t ticks;
2607851eb82Sschwartz 
2617851eb82Sschwartz 	/*
2627851eb82Sschwartz 	 * Because we are updating two fields in ih_t we must lock
2637851eb82Sschwartz 	 * pciintr_ks_template_lock to prevent someone from reading the
2647851eb82Sschwartz 	 * kstats after we set ih_ticks to 0 and before we increment
2657851eb82Sschwartz 	 * ih_nsec to compensate.
2667851eb82Sschwartz 	 *
2677851eb82Sschwartz 	 * We must also protect against the interrupt arriving and incrementing
2687851eb82Sschwartz 	 * ih_ticks between the time we read it and when we reset it to 0.
2697851eb82Sschwartz 	 * To do this we use atomic_swap.
2707851eb82Sschwartz 	 */
2717851eb82Sschwartz 
2727851eb82Sschwartz 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
2737851eb82Sschwartz 
2747851eb82Sschwartz 	mutex_enter(&pciintr_ks_template_lock);
2757851eb82Sschwartz 	ticks = atomic_swap_64(&ih_p->ih_ticks, 0);
2767851eb82Sschwartz 	ih_p->ih_nsec += (uint64_t)tick2ns(ticks, cpu_id);
2777851eb82Sschwartz 	mutex_exit(&pciintr_ks_template_lock);
2787851eb82Sschwartz }
2797851eb82Sschwartz 
2807c478bd9Sstevel@tonic-gate static void
2817c478bd9Sstevel@tonic-gate ib_intr_dist(ib_t *ib_p, ib_ino_info_t *ino_p)
2827c478bd9Sstevel@tonic-gate {
2837c478bd9Sstevel@tonic-gate 	uint32_t cpu_id = ino_p->ino_cpuid;
2847c478bd9Sstevel@tonic-gate 	ib_ino_t ino = ino_p->ino_ino;
2857c478bd9Sstevel@tonic-gate 	volatile uint64_t imr, *imr_p, *state_reg;
2867c478bd9Sstevel@tonic-gate 	hrtime_t start_time;
2877c478bd9Sstevel@tonic-gate 
2887c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
2897c478bd9Sstevel@tonic-gate 	imr_p = ib_intr_map_reg_addr(ib_p, ino);
2907c478bd9Sstevel@tonic-gate 	state_reg = IB_INO_INTR_STATE_REG(ib_p, ino);
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate #ifdef _STARFIRE
2937c478bd9Sstevel@tonic-gate 	/*
2947c478bd9Sstevel@tonic-gate 	 * For Starfire it is a pain to check the current target for
2957c478bd9Sstevel@tonic-gate 	 * the mondo since we have to read the PC asics ITTR slot
2967c478bd9Sstevel@tonic-gate 	 * assigned to this mondo. It will be much easier to assume
2977c478bd9Sstevel@tonic-gate 	 * the current target is always different and do the target
2987c478bd9Sstevel@tonic-gate 	 * reprogram all the time.
2997c478bd9Sstevel@tonic-gate 	 */
3007c478bd9Sstevel@tonic-gate 	cpu_id = pc_translate_tgtid(IB2CB(ib_p)->cb_ittrans_cookie, cpu_id,
3017c478bd9Sstevel@tonic-gate 		IB_GET_MAPREG_INO(ino));
3027c478bd9Sstevel@tonic-gate #else
3037c478bd9Sstevel@tonic-gate 	if (ib_map_reg_get_cpu(*imr_p) == cpu_id) /* same cpu, no reprog */
3047c478bd9Sstevel@tonic-gate 		return;
3057c478bd9Sstevel@tonic-gate #endif /* _STARFIRE */
3067c478bd9Sstevel@tonic-gate 
3077c478bd9Sstevel@tonic-gate 	/* disable interrupt, this could disrupt devices sharing our slot */
3087c478bd9Sstevel@tonic-gate 	IB_INO_INTR_OFF(imr_p);
3097c478bd9Sstevel@tonic-gate 	imr = *imr_p;	/* flush previous write */
3107c478bd9Sstevel@tonic-gate 
3117c478bd9Sstevel@tonic-gate 	/* busy wait if there is interrupt being processed */
3127c478bd9Sstevel@tonic-gate 	start_time = gethrtime();
3137c478bd9Sstevel@tonic-gate 	while (IB_INO_INTR_PENDING(state_reg, ino) && !panicstr) {
3147c478bd9Sstevel@tonic-gate 		if (gethrtime() - start_time > pci_intrpend_timeout) {
3157c478bd9Sstevel@tonic-gate 			pbm_t *pbm_p = ib_p->ib_pci_p->pci_pbm_p;
3167c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s:%s: ib_intr_dist(%p,%x) timeout",
3177c478bd9Sstevel@tonic-gate 				pbm_p->pbm_nameinst_str,
3187c478bd9Sstevel@tonic-gate 				pbm_p->pbm_nameaddr_str,
3197c478bd9Sstevel@tonic-gate 				imr_p, IB_INO_TO_MONDO(ib_p, ino));
3207c478bd9Sstevel@tonic-gate 			break;
3217c478bd9Sstevel@tonic-gate 		}
3227c478bd9Sstevel@tonic-gate 	}
3237c478bd9Sstevel@tonic-gate 	*imr_p = ib_get_map_reg(IB_IMR2MONDO(imr), cpu_id);
3247c478bd9Sstevel@tonic-gate 	imr = *imr_p;	/* flush previous write */
3257c478bd9Sstevel@tonic-gate }
3267c478bd9Sstevel@tonic-gate 
3277c478bd9Sstevel@tonic-gate /*
3287c478bd9Sstevel@tonic-gate  * Redistribute interrupts of the specified weight. The first call has a weight
3297c478bd9Sstevel@tonic-gate  * of weight_max, which can be used to trigger initialization for
3307c478bd9Sstevel@tonic-gate  * redistribution. The inos with weight [weight_max, inf.) should be processed
3317c478bd9Sstevel@tonic-gate  * on the "weight == weight_max" call.  This first call is followed by calls
3327c478bd9Sstevel@tonic-gate  * of decreasing weights, inos of that weight should be processed.  The final
3337c478bd9Sstevel@tonic-gate  * call specifies a weight of zero, this can be used to trigger processing of
3347c478bd9Sstevel@tonic-gate  * stragglers.
3357c478bd9Sstevel@tonic-gate  */
3367c478bd9Sstevel@tonic-gate void
3377c478bd9Sstevel@tonic-gate ib_intr_dist_all(void *arg, int32_t weight_max, int32_t weight)
3387c478bd9Sstevel@tonic-gate {
3397c478bd9Sstevel@tonic-gate 	ib_t *ib_p = (ib_t *)arg;
3407c478bd9Sstevel@tonic-gate 	pci_t *pci_p = ib_p->ib_pci_p;
3417c478bd9Sstevel@tonic-gate 	ib_ino_info_t *ino_p;
3427c478bd9Sstevel@tonic-gate 	ih_t *ih_lst;
3437c478bd9Sstevel@tonic-gate 	int32_t dweight;
3447c478bd9Sstevel@tonic-gate 	int i;
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	if (weight == 0) {
3477c478bd9Sstevel@tonic-gate 		mutex_enter(&ib_p->ib_intr_lock);
3487c478bd9Sstevel@tonic-gate 		if (CHIP_TYPE(pci_p) != PCI_CHIP_XMITS) {
3497c478bd9Sstevel@tonic-gate 			for (i = 0; i < 2; i++)
3507c478bd9Sstevel@tonic-gate 				ib_intr_dist_nintr(ib_p, 0,
3517c478bd9Sstevel@tonic-gate 				    ib_p->ib_upa_imr[i]);
3527c478bd9Sstevel@tonic-gate 		}
3537c478bd9Sstevel@tonic-gate 		mutex_exit(&ib_p->ib_intr_lock);
3547c478bd9Sstevel@tonic-gate 	}
3557c478bd9Sstevel@tonic-gate 
3567c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_ino_lst_mutex);
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	/* Perform special processing for first call of a redistribution. */
3597c478bd9Sstevel@tonic-gate 	if (weight == weight_max) {
3607c478bd9Sstevel@tonic-gate 		for (ino_p = ib_p->ib_ino_lst; ino_p; ino_p = ino_p->ino_next) {
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 			/*
3637c478bd9Sstevel@tonic-gate 			 * Clear ino_established of each ino on first call.
3647c478bd9Sstevel@tonic-gate 			 * The ino_established field may be used by a pci
3657c478bd9Sstevel@tonic-gate 			 * nexus driver's pci_intr_dist_cpuid implementation
3667c478bd9Sstevel@tonic-gate 			 * when detection of established pci slot-cpu binding
3677c478bd9Sstevel@tonic-gate 			 * for multi function pci cards.
3687c478bd9Sstevel@tonic-gate 			 */
3697c478bd9Sstevel@tonic-gate 			ino_p->ino_established = 0;
3707c478bd9Sstevel@tonic-gate 
3717c478bd9Sstevel@tonic-gate 			/*
3727c478bd9Sstevel@tonic-gate 			 * recompute the ino_intr_weight based on the device
3737c478bd9Sstevel@tonic-gate 			 * weight of all devinfo nodes sharing the ino (this
3747c478bd9Sstevel@tonic-gate 			 * will allow us to pick up new weights established by
3757c478bd9Sstevel@tonic-gate 			 * i_ddi_set_intr_weight()).
3767c478bd9Sstevel@tonic-gate 			 */
3777c478bd9Sstevel@tonic-gate 			ino_p->ino_intr_weight = 0;
3787c478bd9Sstevel@tonic-gate 			for (i = 0, ih_lst = ino_p->ino_ih_head;
3797c478bd9Sstevel@tonic-gate 			    i < ino_p->ino_ih_size;
3807c478bd9Sstevel@tonic-gate 			    i++, ih_lst = ih_lst->ih_next) {
3817c478bd9Sstevel@tonic-gate 				dweight = i_ddi_get_intr_weight(ih_lst->ih_dip);
3827c478bd9Sstevel@tonic-gate 				if (dweight > 0)
3837c478bd9Sstevel@tonic-gate 					ino_p->ino_intr_weight += dweight;
3847c478bd9Sstevel@tonic-gate 			}
3857c478bd9Sstevel@tonic-gate 		}
3867c478bd9Sstevel@tonic-gate 	}
3877c478bd9Sstevel@tonic-gate 
3887c478bd9Sstevel@tonic-gate 	for (ino_p = ib_p->ib_ino_lst; ino_p; ino_p = ino_p->ino_next) {
3897c478bd9Sstevel@tonic-gate 		uint32_t orig_cpuid;
3907c478bd9Sstevel@tonic-gate 
3917c478bd9Sstevel@tonic-gate 		/*
3927c478bd9Sstevel@tonic-gate 		 * Get the weight of the ino and determine if we are going to
3937c478bd9Sstevel@tonic-gate 		 * process call.  We wait until an ib_intr_dist_all call of
3947c478bd9Sstevel@tonic-gate 		 * the proper weight occurs to support redistribution of all
3957c478bd9Sstevel@tonic-gate 		 * heavy weighted interrupts first (across all nexus driver
3967c478bd9Sstevel@tonic-gate 		 * instances).  This is done to ensure optimal
3977c478bd9Sstevel@tonic-gate 		 * INTR_WEIGHTED_DIST behavior.
3987c478bd9Sstevel@tonic-gate 		 */
3997c478bd9Sstevel@tonic-gate 		if ((weight == ino_p->ino_intr_weight) ||
4007c478bd9Sstevel@tonic-gate 		    ((weight >= weight_max) &&
4017c478bd9Sstevel@tonic-gate 		    (ino_p->ino_intr_weight >= weight_max))) {
4027c478bd9Sstevel@tonic-gate 			/* select cpuid to target and mark ino established */
4037c478bd9Sstevel@tonic-gate 			orig_cpuid = ino_p->ino_cpuid;
4047c478bd9Sstevel@tonic-gate 			if (cpu[orig_cpuid] == NULL)
4057c478bd9Sstevel@tonic-gate 				orig_cpuid = CPU->cpu_id;
4067c478bd9Sstevel@tonic-gate 			ino_p->ino_cpuid = pci_intr_dist_cpuid(ib_p, ino_p);
4077c478bd9Sstevel@tonic-gate 			ino_p->ino_established = 1;
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 			/* Add device weight of ino devinfos to targeted cpu. */
4107c478bd9Sstevel@tonic-gate 			for (i = 0, ih_lst = ino_p->ino_ih_head;
4117c478bd9Sstevel@tonic-gate 			    i < ino_p->ino_ih_size;
4127c478bd9Sstevel@tonic-gate 			    i++, ih_lst = ih_lst->ih_next) {
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 				dweight = i_ddi_get_intr_weight(ih_lst->ih_dip);
4157c478bd9Sstevel@tonic-gate 				intr_dist_cpuid_add_device_weight(
4167c478bd9Sstevel@tonic-gate 				    ino_p->ino_cpuid, ih_lst->ih_dip, dweight);
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 				/*
4197c478bd9Sstevel@tonic-gate 				 * different cpus may have different clock
4207c478bd9Sstevel@tonic-gate 				 * speeds. to account for this, whenever an
4217c478bd9Sstevel@tonic-gate 				 * interrupt is moved to a new CPU, we
4227c478bd9Sstevel@tonic-gate 				 * convert the accumulated ticks into nsec,
4237c478bd9Sstevel@tonic-gate 				 * based upon the clock rate of the prior
4247c478bd9Sstevel@tonic-gate 				 * CPU.
4257c478bd9Sstevel@tonic-gate 				 *
4267c478bd9Sstevel@tonic-gate 				 * It is possible that the prior CPU no longer
4277c478bd9Sstevel@tonic-gate 				 * exists. In this case, fall back to using
4287c478bd9Sstevel@tonic-gate 				 * this CPU's clock rate.
4297c478bd9Sstevel@tonic-gate 				 *
4307c478bd9Sstevel@tonic-gate 				 * Note that the value in ih_ticks has already
4317c478bd9Sstevel@tonic-gate 				 * been corrected for any power savings mode
4327c478bd9Sstevel@tonic-gate 				 * which might have been in effect.
4337c478bd9Sstevel@tonic-gate 				 */
4347c478bd9Sstevel@tonic-gate 
4357851eb82Sschwartz 				ib_cpu_ticks_to_ih_nsec(ib_p, ih_lst,
4367851eb82Sschwartz 				    orig_cpuid);
4377c478bd9Sstevel@tonic-gate 			}
4387c478bd9Sstevel@tonic-gate 
4397c478bd9Sstevel@tonic-gate 			/* program the hardware */
4407c478bd9Sstevel@tonic-gate 			ib_intr_dist(ib_p, ino_p);
4417c478bd9Sstevel@tonic-gate 		}
4427c478bd9Sstevel@tonic-gate 	}
4437c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_ino_lst_mutex);
4447c478bd9Sstevel@tonic-gate }
4457c478bd9Sstevel@tonic-gate 
4467c478bd9Sstevel@tonic-gate /*
4477c478bd9Sstevel@tonic-gate  * Reset interrupts to IDLE.  This function is called during
4487c478bd9Sstevel@tonic-gate  * panic handling after redistributing interrupts; it's needed to
4497c478bd9Sstevel@tonic-gate  * support dumping to network devices after 'sync' from OBP.
4507c478bd9Sstevel@tonic-gate  *
4517c478bd9Sstevel@tonic-gate  * N.B.  This routine runs in a context where all other threads
4527c478bd9Sstevel@tonic-gate  * are permanently suspended.
4537c478bd9Sstevel@tonic-gate  */
4547c478bd9Sstevel@tonic-gate static uint_t
4557c478bd9Sstevel@tonic-gate ib_intr_reset(void *arg)
4567c478bd9Sstevel@tonic-gate {
4577c478bd9Sstevel@tonic-gate 	ib_t *ib_p = (ib_t *)arg;
4587c478bd9Sstevel@tonic-gate 	ib_ino_t ino;
4597c478bd9Sstevel@tonic-gate 	uint64_t *clr_reg;
4607c478bd9Sstevel@tonic-gate 
4617c478bd9Sstevel@tonic-gate 	/*
4627c478bd9Sstevel@tonic-gate 	 * Note that we only actually care about interrupts that are
4637c478bd9Sstevel@tonic-gate 	 * potentially from network devices.
4647c478bd9Sstevel@tonic-gate 	 */
4657c478bd9Sstevel@tonic-gate 	for (ino = 0; ino <= ib_p->ib_max_ino; ino++) {
4667c478bd9Sstevel@tonic-gate 		clr_reg = ib_clear_intr_reg_addr(ib_p, ino);
4677c478bd9Sstevel@tonic-gate 		IB_INO_INTR_CLEAR(clr_reg);
4687c478bd9Sstevel@tonic-gate 	}
4697c478bd9Sstevel@tonic-gate 
4707c478bd9Sstevel@tonic-gate 	return (BF_NONE);
4717c478bd9Sstevel@tonic-gate }
4727c478bd9Sstevel@tonic-gate 
4737c478bd9Sstevel@tonic-gate void
4747c478bd9Sstevel@tonic-gate ib_suspend(ib_t *ib_p)
4757c478bd9Sstevel@tonic-gate {
4767c478bd9Sstevel@tonic-gate 	ib_ino_info_t *ip;
4777c478bd9Sstevel@tonic-gate 	pci_t *pci_p = ib_p->ib_pci_p;
4787c478bd9Sstevel@tonic-gate 
4797c478bd9Sstevel@tonic-gate 	/* save ino_lst interrupts' mapping registers content */
4807c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_ino_lst_mutex);
4817c478bd9Sstevel@tonic-gate 	for (ip = ib_p->ib_ino_lst; ip; ip = ip->ino_next)
4827c478bd9Sstevel@tonic-gate 		ip->ino_map_reg_save = *ip->ino_map_reg;
4837c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_ino_lst_mutex);
4847c478bd9Sstevel@tonic-gate 
4857c478bd9Sstevel@tonic-gate 	if (CHIP_TYPE(pci_p) != PCI_CHIP_XMITS) {
4867c478bd9Sstevel@tonic-gate 		ib_p->ib_upa_imr_state[0] = *ib_p->ib_upa_imr[0];
4877c478bd9Sstevel@tonic-gate 		ib_p->ib_upa_imr_state[1] = *ib_p->ib_upa_imr[1];
4887c478bd9Sstevel@tonic-gate 	}
4897c478bd9Sstevel@tonic-gate }
4907c478bd9Sstevel@tonic-gate 
4917c478bd9Sstevel@tonic-gate void
4927c478bd9Sstevel@tonic-gate ib_resume(ib_t *ib_p)
4937c478bd9Sstevel@tonic-gate {
4947c478bd9Sstevel@tonic-gate 	ib_ino_info_t *ip;
4957c478bd9Sstevel@tonic-gate 	pci_t *pci_p = ib_p->ib_pci_p;
4967c478bd9Sstevel@tonic-gate 
4977c478bd9Sstevel@tonic-gate 	/* restore ino_lst interrupts' mapping registers content */
4987c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_ino_lst_mutex);
4997c478bd9Sstevel@tonic-gate 	for (ip = ib_p->ib_ino_lst; ip; ip = ip->ino_next) {
5007c478bd9Sstevel@tonic-gate 		IB_INO_INTR_CLEAR(ip->ino_clr_reg);	 /* set intr to idle */
5017c478bd9Sstevel@tonic-gate 		*ip->ino_map_reg = ip->ino_map_reg_save; /* restore IMR */
5027c478bd9Sstevel@tonic-gate 	}
5037c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_ino_lst_mutex);
5047c478bd9Sstevel@tonic-gate 
5057c478bd9Sstevel@tonic-gate 	if (CHIP_TYPE(pci_p) != PCI_CHIP_XMITS) {
5067c478bd9Sstevel@tonic-gate 		*ib_p->ib_upa_imr[0] = ib_p->ib_upa_imr_state[0];
5077c478bd9Sstevel@tonic-gate 		*ib_p->ib_upa_imr[1] = ib_p->ib_upa_imr_state[1];
5087c478bd9Sstevel@tonic-gate 	}
5097c478bd9Sstevel@tonic-gate }
5107c478bd9Sstevel@tonic-gate 
5117c478bd9Sstevel@tonic-gate /*
5127c478bd9Sstevel@tonic-gate  * locate ino_info structure on ib_p->ib_ino_lst according to ino#
5137c478bd9Sstevel@tonic-gate  * returns NULL if not found.
5147c478bd9Sstevel@tonic-gate  */
5157c478bd9Sstevel@tonic-gate ib_ino_info_t *
5167c478bd9Sstevel@tonic-gate ib_locate_ino(ib_t *ib_p, ib_ino_t ino_num)
5177c478bd9Sstevel@tonic-gate {
5187c478bd9Sstevel@tonic-gate 	ib_ino_info_t *ino_p = ib_p->ib_ino_lst;
5197c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
5207c478bd9Sstevel@tonic-gate 
5217c478bd9Sstevel@tonic-gate 	for (; ino_p && ino_p->ino_ino != ino_num; ino_p = ino_p->ino_next);
5227c478bd9Sstevel@tonic-gate 	return (ino_p);
5237c478bd9Sstevel@tonic-gate }
5247c478bd9Sstevel@tonic-gate 
5257c478bd9Sstevel@tonic-gate #define	IB_INO_TO_SLOT(ino) (IB_IS_OBIO_INO(ino) ? 0xff : ((ino) & 0x1f) >> 2)
5267c478bd9Sstevel@tonic-gate 
5277c478bd9Sstevel@tonic-gate ib_ino_info_t *
5287c478bd9Sstevel@tonic-gate ib_new_ino(ib_t *ib_p, ib_ino_t ino_num, ih_t *ih_p)
5297c478bd9Sstevel@tonic-gate {
5307c478bd9Sstevel@tonic-gate 	ib_ino_info_t *ino_p = kmem_alloc(sizeof (ib_ino_info_t), KM_SLEEP);
5317c478bd9Sstevel@tonic-gate 	ino_p->ino_ino = ino_num;
5327c478bd9Sstevel@tonic-gate 	ino_p->ino_slot_no = IB_INO_TO_SLOT(ino_num);
5337c478bd9Sstevel@tonic-gate 	ino_p->ino_ib_p = ib_p;
5347c478bd9Sstevel@tonic-gate 	ino_p->ino_clr_reg = ib_clear_intr_reg_addr(ib_p, ino_num);
5357c478bd9Sstevel@tonic-gate 	ino_p->ino_map_reg = ib_intr_map_reg_addr(ib_p, ino_num);
5367c478bd9Sstevel@tonic-gate 	ino_p->ino_unclaimed = 0;
5377c478bd9Sstevel@tonic-gate 
5387c478bd9Sstevel@tonic-gate 	/*
5397c478bd9Sstevel@tonic-gate 	 * cannot disable interrupt since we might share slot
5407c478bd9Sstevel@tonic-gate 	 * IB_INO_INTR_OFF(ino_p->ino_map_reg);
5417c478bd9Sstevel@tonic-gate 	 */
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 	ih_p->ih_next = ih_p;
5447c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_head = ih_p;
5457c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_tail = ih_p;
5467c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_start = ih_p;
5477c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_size = 1;
5487c478bd9Sstevel@tonic-gate 
5497c478bd9Sstevel@tonic-gate 	ino_p->ino_next = ib_p->ib_ino_lst;
5507c478bd9Sstevel@tonic-gate 	ib_p->ib_ino_lst = ino_p;
5517c478bd9Sstevel@tonic-gate 	return (ino_p);
5527c478bd9Sstevel@tonic-gate }
5537c478bd9Sstevel@tonic-gate 
5547c478bd9Sstevel@tonic-gate /* the ino_p is retrieved by previous call to ib_locate_ino() */
5557c478bd9Sstevel@tonic-gate void
5567c478bd9Sstevel@tonic-gate ib_delete_ino(ib_t *ib_p, ib_ino_info_t *ino_p)
5577c478bd9Sstevel@tonic-gate {
5587c478bd9Sstevel@tonic-gate 	ib_ino_info_t *list = ib_p->ib_ino_lst;
5597c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
5607c478bd9Sstevel@tonic-gate 	if (list == ino_p)
5617c478bd9Sstevel@tonic-gate 		ib_p->ib_ino_lst = list->ino_next;
5627c478bd9Sstevel@tonic-gate 	else {
5637c478bd9Sstevel@tonic-gate 		for (; list->ino_next != ino_p; list = list->ino_next);
5647c478bd9Sstevel@tonic-gate 		list->ino_next = ino_p->ino_next;
5657c478bd9Sstevel@tonic-gate 	}
5667c478bd9Sstevel@tonic-gate }
5677c478bd9Sstevel@tonic-gate 
5687c478bd9Sstevel@tonic-gate /* free all ino when we are detaching */
5697c478bd9Sstevel@tonic-gate void
5707c478bd9Sstevel@tonic-gate ib_free_ino_all(ib_t *ib_p)
5717c478bd9Sstevel@tonic-gate {
5727c478bd9Sstevel@tonic-gate 	ib_ino_info_t *tmp = ib_p->ib_ino_lst;
5737c478bd9Sstevel@tonic-gate 	ib_ino_info_t *next = NULL;
5747c478bd9Sstevel@tonic-gate 	while (tmp) {
5757c478bd9Sstevel@tonic-gate 		next = tmp->ino_next;
5767c478bd9Sstevel@tonic-gate 		kmem_free(tmp, sizeof (ib_ino_info_t));
5777c478bd9Sstevel@tonic-gate 		tmp = next;
5787c478bd9Sstevel@tonic-gate 	}
5797c478bd9Sstevel@tonic-gate }
5807c478bd9Sstevel@tonic-gate 
5817c478bd9Sstevel@tonic-gate void
5827c478bd9Sstevel@tonic-gate ib_ino_add_intr(pci_t *pci_p, ib_ino_info_t *ino_p, ih_t *ih_p)
5837c478bd9Sstevel@tonic-gate {
5847c478bd9Sstevel@tonic-gate 	ib_ino_t ino = ino_p->ino_ino;
5857c478bd9Sstevel@tonic-gate 	ib_t *ib_p = ino_p->ino_ib_p;
5867c478bd9Sstevel@tonic-gate 	volatile uint64_t *state_reg = IB_INO_INTR_STATE_REG(ib_p, ino);
5877c478bd9Sstevel@tonic-gate 	hrtime_t start_time;
5887c478bd9Sstevel@tonic-gate 
5897c478bd9Sstevel@tonic-gate 	ASSERT(ib_p == pci_p->pci_ib_p);
5907c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ib_p->ib_ino_lst_mutex));
5917c478bd9Sstevel@tonic-gate 
5927c478bd9Sstevel@tonic-gate 	/* disable interrupt, this could disrupt devices sharing our slot */
5937c478bd9Sstevel@tonic-gate 	IB_INO_INTR_OFF(ino_p->ino_map_reg);
5947c478bd9Sstevel@tonic-gate 	*ino_p->ino_map_reg;
5957c478bd9Sstevel@tonic-gate 
5967c478bd9Sstevel@tonic-gate 	/* do NOT modify the link list until after the busy wait */
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	/*
5997c478bd9Sstevel@tonic-gate 	 * busy wait if there is interrupt being processed.
6007c478bd9Sstevel@tonic-gate 	 * either the pending state will be cleared by the interrupt wrapper
6017c478bd9Sstevel@tonic-gate 	 * or the interrupt will be marked as blocked indicating that it was
6027c478bd9Sstevel@tonic-gate 	 * jabbering.
6037c478bd9Sstevel@tonic-gate 	 */
6047c478bd9Sstevel@tonic-gate 	start_time = gethrtime();
6057c478bd9Sstevel@tonic-gate 	while ((ino_p->ino_unclaimed <= pci_unclaimed_intr_max) &&
6067c478bd9Sstevel@tonic-gate 		IB_INO_INTR_PENDING(state_reg, ino) && !panicstr) {
6077c478bd9Sstevel@tonic-gate 		if (gethrtime() - start_time > pci_intrpend_timeout) {
6087c478bd9Sstevel@tonic-gate 			pbm_t *pbm_p = pci_p->pci_pbm_p;
6097c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s:%s: ib_ino_add_intr %x timeout",
6107c478bd9Sstevel@tonic-gate 				pbm_p->pbm_nameinst_str,
6117c478bd9Sstevel@tonic-gate 				pbm_p->pbm_nameaddr_str, ino);
6127c478bd9Sstevel@tonic-gate 			break;
6137c478bd9Sstevel@tonic-gate 		}
6147c478bd9Sstevel@tonic-gate 	}
6157c478bd9Sstevel@tonic-gate 
6167c478bd9Sstevel@tonic-gate 	/* link up pci_ispec_t portion of the ppd */
6177c478bd9Sstevel@tonic-gate 	ih_p->ih_next = ino_p->ino_ih_head;
6187c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_tail->ih_next = ih_p;
6197c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_tail = ih_p;
6207c478bd9Sstevel@tonic-gate 
6217c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_start = ino_p->ino_ih_head;
6227c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_size++;
6237c478bd9Sstevel@tonic-gate 
6247c478bd9Sstevel@tonic-gate 	/*
6257c478bd9Sstevel@tonic-gate 	 * if the interrupt was previously blocked (left in pending state)
6267c478bd9Sstevel@tonic-gate 	 * because of jabber we need to clear the pending state in case the
6277c478bd9Sstevel@tonic-gate 	 * jabber has gone away.
6287c478bd9Sstevel@tonic-gate 	 */
6297c478bd9Sstevel@tonic-gate 	if (ino_p->ino_unclaimed > pci_unclaimed_intr_max) {
6307c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
6317c478bd9Sstevel@tonic-gate 		    "%s%d: ib_ino_add_intr: ino 0x%x has been unblocked",
6327c478bd9Sstevel@tonic-gate 		    ddi_driver_name(pci_p->pci_dip),
6337c478bd9Sstevel@tonic-gate 		    ddi_get_instance(pci_p->pci_dip),
6347c478bd9Sstevel@tonic-gate 		    ino_p->ino_ino);
6357c478bd9Sstevel@tonic-gate 		ino_p->ino_unclaimed = 0;
6367c478bd9Sstevel@tonic-gate 		IB_INO_INTR_CLEAR(ino_p->ino_clr_reg);
6377c478bd9Sstevel@tonic-gate 	}
6387c478bd9Sstevel@tonic-gate 
6397c478bd9Sstevel@tonic-gate 	/* re-enable interrupt */
6407c478bd9Sstevel@tonic-gate 	IB_INO_INTR_ON(ino_p->ino_map_reg);
6417c478bd9Sstevel@tonic-gate 	*ino_p->ino_map_reg;
6427c478bd9Sstevel@tonic-gate }
6437c478bd9Sstevel@tonic-gate 
6447c478bd9Sstevel@tonic-gate /*
6457c478bd9Sstevel@tonic-gate  * removes pci_ispec_t from the ino's link list.
6467c478bd9Sstevel@tonic-gate  * uses hardware mutex to lock out interrupt threads.
6477c478bd9Sstevel@tonic-gate  * Side effects: interrupt belongs to that ino is turned off on return.
6487c478bd9Sstevel@tonic-gate  * if we are sharing PCI slot with other inos, the caller needs
6497c478bd9Sstevel@tonic-gate  * to turn it back on.
6507c478bd9Sstevel@tonic-gate  */
6517c478bd9Sstevel@tonic-gate void
6527c478bd9Sstevel@tonic-gate ib_ino_rem_intr(pci_t *pci_p, ib_ino_info_t *ino_p, ih_t *ih_p)
6537c478bd9Sstevel@tonic-gate {
6547c478bd9Sstevel@tonic-gate 	int i;
6557c478bd9Sstevel@tonic-gate 	ib_ino_t ino = ino_p->ino_ino;
6567c478bd9Sstevel@tonic-gate 	ih_t *ih_lst = ino_p->ino_ih_head;
6577c478bd9Sstevel@tonic-gate 	volatile uint64_t *state_reg =
6587c478bd9Sstevel@tonic-gate 		IB_INO_INTR_STATE_REG(ino_p->ino_ib_p, ino);
6597c478bd9Sstevel@tonic-gate 	hrtime_t start_time;
6607c478bd9Sstevel@tonic-gate 
6617c478bd9Sstevel@tonic-gate 	ASSERT(MUTEX_HELD(&ino_p->ino_ib_p->ib_ino_lst_mutex));
6627c478bd9Sstevel@tonic-gate 	/* disable interrupt, this could disrupt devices sharing our slot */
6637c478bd9Sstevel@tonic-gate 	IB_INO_INTR_OFF(ino_p->ino_map_reg);
6647c478bd9Sstevel@tonic-gate 	*ino_p->ino_map_reg;
6657c478bd9Sstevel@tonic-gate 
6667c478bd9Sstevel@tonic-gate 	/* do NOT modify the link list until after the busy wait */
6677c478bd9Sstevel@tonic-gate 
6687c478bd9Sstevel@tonic-gate 	/*
6697c478bd9Sstevel@tonic-gate 	 * busy wait if there is interrupt being processed.
6707c478bd9Sstevel@tonic-gate 	 * either the pending state will be cleared by the interrupt wrapper
6717c478bd9Sstevel@tonic-gate 	 * or the interrupt will be marked as blocked indicating that it was
6727c478bd9Sstevel@tonic-gate 	 * jabbering.
6737c478bd9Sstevel@tonic-gate 	 */
6747c478bd9Sstevel@tonic-gate 	start_time = gethrtime();
6757c478bd9Sstevel@tonic-gate 	while ((ino_p->ino_unclaimed <= pci_unclaimed_intr_max) &&
6767c478bd9Sstevel@tonic-gate 		IB_INO_INTR_PENDING(state_reg, ino) && !panicstr) {
6777c478bd9Sstevel@tonic-gate 		if (gethrtime() - start_time > pci_intrpend_timeout) {
6787c478bd9Sstevel@tonic-gate 			pbm_t *pbm_p = pci_p->pci_pbm_p;
6797c478bd9Sstevel@tonic-gate 			cmn_err(CE_WARN, "%s:%s: ib_ino_rem_intr %x timeout",
6807c478bd9Sstevel@tonic-gate 				pbm_p->pbm_nameinst_str,
6817c478bd9Sstevel@tonic-gate 				pbm_p->pbm_nameaddr_str, ino);
6827c478bd9Sstevel@tonic-gate 			break;
6837c478bd9Sstevel@tonic-gate 		}
6847c478bd9Sstevel@tonic-gate 	}
6857c478bd9Sstevel@tonic-gate 
6867c478bd9Sstevel@tonic-gate 	if (ino_p->ino_ih_size == 1) {
6877c478bd9Sstevel@tonic-gate 		if (ih_lst != ih_p)
6887c478bd9Sstevel@tonic-gate 			goto not_found;
6897c478bd9Sstevel@tonic-gate 		/* no need to set head/tail as ino_p will be freed */
6907c478bd9Sstevel@tonic-gate 		goto reset;
6917c478bd9Sstevel@tonic-gate 	}
6927c478bd9Sstevel@tonic-gate 
6937c478bd9Sstevel@tonic-gate 	/*
6947c478bd9Sstevel@tonic-gate 	 * if the interrupt was previously blocked (left in pending state)
6957c478bd9Sstevel@tonic-gate 	 * because of jabber we need to clear the pending state in case the
6967c478bd9Sstevel@tonic-gate 	 * jabber has gone away.
6977c478bd9Sstevel@tonic-gate 	 */
6987c478bd9Sstevel@tonic-gate 	if (ino_p->ino_unclaimed > pci_unclaimed_intr_max) {
6997c478bd9Sstevel@tonic-gate 		cmn_err(CE_WARN,
7007c478bd9Sstevel@tonic-gate 		    "%s%d: ib_ino_rem_intr: ino 0x%x has been unblocked",
7017c478bd9Sstevel@tonic-gate 		    ddi_driver_name(pci_p->pci_dip),
7027c478bd9Sstevel@tonic-gate 		    ddi_get_instance(pci_p->pci_dip),
7037c478bd9Sstevel@tonic-gate 		    ino_p->ino_ino);
7047c478bd9Sstevel@tonic-gate 		ino_p->ino_unclaimed = 0;
7057c478bd9Sstevel@tonic-gate 		IB_INO_INTR_CLEAR(ino_p->ino_clr_reg);
7067c478bd9Sstevel@tonic-gate 	}
7077c478bd9Sstevel@tonic-gate 
7087c478bd9Sstevel@tonic-gate 	/* search the link list for ih_p */
7097c478bd9Sstevel@tonic-gate 	for (i = 0;
7107c478bd9Sstevel@tonic-gate 		(i < ino_p->ino_ih_size) && (ih_lst->ih_next != ih_p);
7117c478bd9Sstevel@tonic-gate 		i++, ih_lst = ih_lst->ih_next);
7127c478bd9Sstevel@tonic-gate 	if (ih_lst->ih_next != ih_p)
7137c478bd9Sstevel@tonic-gate 		goto not_found;
7147c478bd9Sstevel@tonic-gate 
7157c478bd9Sstevel@tonic-gate 	/* remove ih_p from the link list and maintain the head/tail */
7167c478bd9Sstevel@tonic-gate 	ih_lst->ih_next = ih_p->ih_next;
7177c478bd9Sstevel@tonic-gate 	if (ino_p->ino_ih_head == ih_p)
7187c478bd9Sstevel@tonic-gate 		ino_p->ino_ih_head = ih_p->ih_next;
7197c478bd9Sstevel@tonic-gate 	if (ino_p->ino_ih_tail == ih_p)
7207c478bd9Sstevel@tonic-gate 		ino_p->ino_ih_tail = ih_lst;
7217c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_start = ino_p->ino_ih_head;
7227c478bd9Sstevel@tonic-gate reset:
7237c478bd9Sstevel@tonic-gate 	if (ih_p->ih_config_handle)
7247c478bd9Sstevel@tonic-gate 		pci_config_teardown(&ih_p->ih_config_handle);
7257c478bd9Sstevel@tonic-gate 	if (ih_p->ih_ksp != NULL)
7267c478bd9Sstevel@tonic-gate 		kstat_delete(ih_p->ih_ksp);
7277c478bd9Sstevel@tonic-gate 	kmem_free(ih_p, sizeof (ih_t));
7287c478bd9Sstevel@tonic-gate 	ino_p->ino_ih_size--;
7297c478bd9Sstevel@tonic-gate 
7307c478bd9Sstevel@tonic-gate 	return;
7317c478bd9Sstevel@tonic-gate not_found:
7327c478bd9Sstevel@tonic-gate 	DEBUG2(DBG_R_INTX, ino_p->ino_ib_p->ib_pci_p->pci_dip,
7337c478bd9Sstevel@tonic-gate 		"ino_p=%x does not have ih_p=%x\n", ino_p, ih_p);
7347c478bd9Sstevel@tonic-gate }
7357c478bd9Sstevel@tonic-gate 
7367c478bd9Sstevel@tonic-gate ih_t *
7377c478bd9Sstevel@tonic-gate ib_ino_locate_intr(ib_ino_info_t *ino_p, dev_info_t *rdip, uint32_t inum)
7387c478bd9Sstevel@tonic-gate {
7397c478bd9Sstevel@tonic-gate 	ih_t *ih_lst = ino_p->ino_ih_head;
7407c478bd9Sstevel@tonic-gate 	int i;
7417c478bd9Sstevel@tonic-gate 	for (i = 0; i < ino_p->ino_ih_size; i++, ih_lst = ih_lst->ih_next) {
7427c478bd9Sstevel@tonic-gate 		if (ih_lst->ih_dip == rdip &&
7437c478bd9Sstevel@tonic-gate 		    ih_lst->ih_inum == inum)
7447c478bd9Sstevel@tonic-gate 			return (ih_lst);
7457c478bd9Sstevel@tonic-gate 	}
7467c478bd9Sstevel@tonic-gate 	return ((ih_t *)NULL);
7477c478bd9Sstevel@tonic-gate }
7487c478bd9Sstevel@tonic-gate 
7497c478bd9Sstevel@tonic-gate ih_t *
7507c478bd9Sstevel@tonic-gate ib_alloc_ih(dev_info_t *rdip, uint32_t inum,
7517851eb82Sschwartz 	uint_t (*int_handler)(caddr_t int_handler_arg1,
7527851eb82Sschwartz 	caddr_t int_handler_arg2),
7537851eb82Sschwartz 	caddr_t int_handler_arg1,
7547851eb82Sschwartz 	caddr_t int_handler_arg2)
7557c478bd9Sstevel@tonic-gate {
7567c478bd9Sstevel@tonic-gate 	ih_t *ih_p;
7577c478bd9Sstevel@tonic-gate 
7587c478bd9Sstevel@tonic-gate 	ih_p = kmem_alloc(sizeof (ih_t), KM_SLEEP);
7597c478bd9Sstevel@tonic-gate 	ih_p->ih_dip = rdip;
7607c478bd9Sstevel@tonic-gate 	ih_p->ih_inum = inum;
7617c478bd9Sstevel@tonic-gate 	ih_p->ih_intr_state = PCI_INTR_STATE_DISABLE;
7627c478bd9Sstevel@tonic-gate 	ih_p->ih_handler = int_handler;
7637c478bd9Sstevel@tonic-gate 	ih_p->ih_handler_arg1 = int_handler_arg1;
7647c478bd9Sstevel@tonic-gate 	ih_p->ih_handler_arg2 = int_handler_arg2;
7657c478bd9Sstevel@tonic-gate 	ih_p->ih_config_handle = NULL;
7667c478bd9Sstevel@tonic-gate 	ih_p->ih_nsec = 0;
7677c478bd9Sstevel@tonic-gate 	ih_p->ih_ticks = 0;
7686d44af1bSesolom 	ih_p->ih_ksp = NULL;
7697c478bd9Sstevel@tonic-gate 
7707c478bd9Sstevel@tonic-gate 	return (ih_p);
7717c478bd9Sstevel@tonic-gate }
7727c478bd9Sstevel@tonic-gate 
7737c478bd9Sstevel@tonic-gate int
7747c478bd9Sstevel@tonic-gate ib_update_intr_state(pci_t *pci_p, dev_info_t *rdip,
7757851eb82Sschwartz 	ddi_intr_handle_impl_t *hdlp, uint_t new_intr_state)
7767c478bd9Sstevel@tonic-gate {
7777c478bd9Sstevel@tonic-gate 	ib_t		*ib_p = pci_p->pci_ib_p;
7787c478bd9Sstevel@tonic-gate 	ib_ino_info_t	*ino_p;
7797c478bd9Sstevel@tonic-gate 	ib_mondo_t	mondo;
7807c478bd9Sstevel@tonic-gate 	ih_t		*ih_p;
7817c478bd9Sstevel@tonic-gate 	int		ret = DDI_FAILURE;
7827c478bd9Sstevel@tonic-gate 
783*f910463cSgovinda 	/*
784*f910463cSgovinda 	 * For PULSE interrupts, pci driver don't allocate
785*f910463cSgovinda 	 * ib_ino_info_t and ih_t data structures and also,
786*f910463cSgovinda 	 * not maintains any interrupt state information.
787*f910463cSgovinda 	 * So, just return success from here.
788*f910463cSgovinda 	 */
789*f910463cSgovinda 	if (hdlp->ih_vector & PCI_PULSE_INO) {
790*f910463cSgovinda 		DEBUG0(DBG_IB, ib_p->ib_pci_p->pci_dip,
791*f910463cSgovinda 		    "ib_update_intr_state: PULSE interrupt, return success\n");
792*f910463cSgovinda 
793*f910463cSgovinda 		return (DDI_SUCCESS);
794*f910463cSgovinda 	}
795*f910463cSgovinda 
7967c478bd9Sstevel@tonic-gate 	mutex_enter(&ib_p->ib_ino_lst_mutex);
7977c478bd9Sstevel@tonic-gate 
7987c478bd9Sstevel@tonic-gate 	if ((mondo = pci_xlate_intr(pci_p->pci_dip, rdip, pci_p->pci_ib_p,
799a195726fSgovinda 	    IB_MONDO_TO_INO(hdlp->ih_vector))) == 0) {
8007c478bd9Sstevel@tonic-gate 		mutex_exit(&ib_p->ib_ino_lst_mutex);
8017c478bd9Sstevel@tonic-gate 		return (ret);
8027c478bd9Sstevel@tonic-gate 	}
8037c478bd9Sstevel@tonic-gate 
8047c478bd9Sstevel@tonic-gate 	if (ino_p = ib_locate_ino(ib_p, IB_MONDO_TO_INO(mondo))) {
8057c478bd9Sstevel@tonic-gate 		if (ih_p = ib_ino_locate_intr(ino_p, rdip, hdlp->ih_inum)) {
8067c478bd9Sstevel@tonic-gate 			ih_p->ih_intr_state = new_intr_state;
8077c478bd9Sstevel@tonic-gate 			ret = DDI_SUCCESS;
8087c478bd9Sstevel@tonic-gate 		}
8097c478bd9Sstevel@tonic-gate 	}
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 	mutex_exit(&ib_p->ib_ino_lst_mutex);
8127c478bd9Sstevel@tonic-gate 	return (ret);
8137c478bd9Sstevel@tonic-gate }
8147851eb82Sschwartz 
8157851eb82Sschwartz /*
8167851eb82Sschwartz  * Return the dips or number of dips associated with a given interrupt block.
8177851eb82Sschwartz  * Size of dips array arg is passed in as dips_ret arg.
8187851eb82Sschwartz  * Number of dips returned is returned in dips_ret arg.
8197851eb82Sschwartz  * Array of dips gets returned in the dips argument.
8207851eb82Sschwartz  * Function returns number of dips existing for the given interrupt block.
8217851eb82Sschwartz  *
8227851eb82Sschwartz  */
8237851eb82Sschwartz uint8_t
8247851eb82Sschwartz ib_get_ino_devs(
8257851eb82Sschwartz 	ib_t *ib_p, uint32_t ino, uint8_t *devs_ret, pcitool_intr_dev_t *devs)
8267851eb82Sschwartz {
8277851eb82Sschwartz 	ib_ino_info_t *ino_p;
8287851eb82Sschwartz 	ih_t *ih_p;
8297851eb82Sschwartz 	uint32_t num_devs = 0;
8307851eb82Sschwartz 	int i;
8317851eb82Sschwartz 
8327851eb82Sschwartz 	mutex_enter(&ib_p->ib_ino_lst_mutex);
8337851eb82Sschwartz 	ino_p = ib_locate_ino(ib_p, ino);
8347851eb82Sschwartz 	if (ino_p != NULL) {
8357851eb82Sschwartz 		num_devs = ino_p->ino_ih_size;
8367851eb82Sschwartz 		for (i = 0, ih_p = ino_p->ino_ih_head;
8377851eb82Sschwartz 		    ((i < ino_p->ino_ih_size) && (i < *devs_ret));
8387851eb82Sschwartz 		    i++, ih_p = ih_p->ih_next) {
8397851eb82Sschwartz 			(void) strncpy(devs[i].driver_name,
8407851eb82Sschwartz 			    ddi_driver_name(ih_p->ih_dip), MAXMODCONFNAME-1);
8417851eb82Sschwartz 			devs[i].driver_name[MAXMODCONFNAME] = '\0';
8427851eb82Sschwartz 			(void) ddi_pathname(ih_p->ih_dip, devs[i].path);
8437851eb82Sschwartz 			devs[i].dev_inst = ddi_get_instance(ih_p->ih_dip);
8447851eb82Sschwartz 		}
8457851eb82Sschwartz 		*devs_ret = i;
8467851eb82Sschwartz 	}
8477851eb82Sschwartz 
8487851eb82Sschwartz 	mutex_exit(&ib_p->ib_ino_lst_mutex);
8497851eb82Sschwartz 
8507851eb82Sschwartz 	return (num_devs);
8517851eb82Sschwartz }
8527851eb82Sschwartz 
8537851eb82Sschwartz void ib_log_new_cpu(ib_t *ib_p, uint32_t old_cpu_id, uint32_t new_cpu_id,
8547851eb82Sschwartz 	uint32_t ino)
8557851eb82Sschwartz {
8567851eb82Sschwartz 	ib_ino_info_t *ino_p;
8577851eb82Sschwartz 
8587851eb82Sschwartz 	mutex_enter(&ib_p->ib_ino_lst_mutex);
8597851eb82Sschwartz 
8607851eb82Sschwartz 	/* Log in OS data structures the new CPU. */
8617851eb82Sschwartz 	ino_p = ib_locate_ino(ib_p, ino);
8627851eb82Sschwartz 	if (ino_p != NULL) {
8637851eb82Sschwartz 
8647851eb82Sschwartz 		/* Log in OS data structures the new CPU. */
8657851eb82Sschwartz 		ino_p->ino_cpuid = new_cpu_id;
8667851eb82Sschwartz 
8677851eb82Sschwartz 		/* Account for any residual time to be logged for old cpu. */
8687851eb82Sschwartz 		ib_cpu_ticks_to_ih_nsec(ib_p, ino_p->ino_ih_head, old_cpu_id);
8697851eb82Sschwartz 	}
8707851eb82Sschwartz 
8717851eb82Sschwartz 	mutex_exit(&ib_p->ib_ino_lst_mutex);
8727851eb82Sschwartz }
873