xref: /onnv-gate/usr/src/uts/sun4u/opl/io/pcicmu/pcmu_intr.c (revision 6587:a5df9c36b588)
11772Sjl139090 /*
21772Sjl139090  * CDDL HEADER START
31772Sjl139090  *
41772Sjl139090  * The contents of this file are subject to the terms of the
51772Sjl139090  * Common Development and Distribution License (the "License").
61772Sjl139090  * You may not use this file except in compliance with the License.
71772Sjl139090  *
81772Sjl139090  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91772Sjl139090  * or http://www.opensolaris.org/os/licensing.
101772Sjl139090  * See the License for the specific language governing permissions
111772Sjl139090  * and limitations under the License.
121772Sjl139090  *
131772Sjl139090  * When distributing Covered Code, include this CDDL HEADER in each
141772Sjl139090  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151772Sjl139090  * If applicable, add the following below this CDDL HEADER, with the
161772Sjl139090  * fields enclosed by brackets "[]" replaced with your own identifying
171772Sjl139090  * information: Portions Copyright [yyyy] [name of copyright owner]
181772Sjl139090  *
191772Sjl139090  * CDDL HEADER END
201772Sjl139090  */
211772Sjl139090 /*
22*6587Sjfrank  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
231772Sjl139090  * Use is subject to license terms.
241772Sjl139090  */
251772Sjl139090 
261772Sjl139090 #pragma ident	"%Z%%M%	%I%	%E% SMI"
271772Sjl139090 
281772Sjl139090 /*
291772Sjl139090  * CMU-CH nexus interrupt handling:
301772Sjl139090  *	PCI device interrupt handler wrapper
311772Sjl139090  *	pil lookup routine
321772Sjl139090  *	PCI device interrupt related initchild code
331772Sjl139090  */
341772Sjl139090 
351772Sjl139090 #include <sys/types.h>
361772Sjl139090 #include <sys/kmem.h>
371772Sjl139090 #include <sys/async.h>
381772Sjl139090 #include <sys/spl.h>
391772Sjl139090 #include <sys/sunddi.h>
401772Sjl139090 #include <sys/machsystm.h>
411772Sjl139090 #include <sys/ddi_impldefs.h>
421772Sjl139090 #include <sys/pcicmu/pcicmu.h>
431772Sjl139090 #include <sys/sdt.h>
441772Sjl139090 
451772Sjl139090 uint_t pcmu_intr_wrapper(caddr_t arg);
461772Sjl139090 
471772Sjl139090 /*
481772Sjl139090  * interrupt jabber:
491772Sjl139090  *
501772Sjl139090  * When an interrupt line is jabbering, every time the state machine for the
511772Sjl139090  * associated ino is idled, a new mondo will be sent and the ino will go into
521772Sjl139090  * the pending state again. The mondo will cause a new call to
531772Sjl139090  * pcmu_intr_wrapper() which normally idles the ino's state machine which would
541772Sjl139090  * precipitate another trip round the loop.
551772Sjl139090  * The loop can be broken by preventing the ino's state machine from being
561772Sjl139090  * idled when an interrupt line is jabbering. See the comment at the
571772Sjl139090  * beginning of pcmu_intr_wrapper() explaining how the 'interrupt jabber
581772Sjl139090  * protection' code does this.
591772Sjl139090  */
601772Sjl139090 
611772Sjl139090 
621772Sjl139090 /*
631772Sjl139090  * If the unclaimed interrupt count has reached the limit set by
641772Sjl139090  * pcmu_unclaimed_intr_max within the time limit, then all interrupts
651772Sjl139090  * on this ino is blocked by not idling the interrupt state machine.
661772Sjl139090  */
671772Sjl139090 static int
pcmu_spurintr(pcmu_ib_ino_info_t * ino_p)681772Sjl139090 pcmu_spurintr(pcmu_ib_ino_info_t *ino_p) {
691772Sjl139090 	int i;
701772Sjl139090 	ih_t *ih_p = ino_p->pino_ih_start;
711772Sjl139090 	pcmu_t *pcmu_p = ino_p->pino_ib_p->pib_pcmu_p;
721772Sjl139090 	char *err_fmt_str;
731772Sjl139090 
741772Sjl139090 	if (ino_p->pino_unclaimed > pcmu_unclaimed_intr_max) {
751772Sjl139090 		return (DDI_INTR_CLAIMED);
761772Sjl139090 	}
771772Sjl139090 	if (!ino_p->pino_unclaimed) {
781772Sjl139090 		ino_p->pino_spurintr_begin = ddi_get_lbolt();
791772Sjl139090 	}
801772Sjl139090 	ino_p->pino_unclaimed++;
811772Sjl139090 	if (ino_p->pino_unclaimed <= pcmu_unclaimed_intr_max) {
821772Sjl139090 		goto clear;
831772Sjl139090 	}
841772Sjl139090 	if (drv_hztousec(ddi_get_lbolt() - ino_p->pino_spurintr_begin)
851772Sjl139090 	    > pcmu_spurintr_duration) {
861772Sjl139090 		ino_p->pino_unclaimed = 0;
871772Sjl139090 		goto clear;
881772Sjl139090 	}
891772Sjl139090 	err_fmt_str = "%s%d: ino 0x%x blocked";
901772Sjl139090 	goto warn;
911772Sjl139090 clear:
921772Sjl139090 	/* clear the pending state */
931772Sjl139090 	PCMU_IB_INO_INTR_CLEAR(ino_p->pino_clr_reg);
941772Sjl139090 	err_fmt_str = "!%s%d: spurious interrupt from ino 0x%x";
951772Sjl139090 warn:
961772Sjl139090 	cmn_err(CE_WARN, err_fmt_str, NAMEINST(pcmu_p->pcmu_dip),
971772Sjl139090 	    ino_p->pino_ino);
981772Sjl139090 	for (i = 0; i < ino_p->pino_ih_size; i++, ih_p = ih_p->ih_next) {
991772Sjl139090 		cmn_err(CE_CONT, "!%s-%d#%x ", NAMEINST(ih_p->ih_dip),
1001772Sjl139090 		    ih_p->ih_inum);
1011772Sjl139090 	}
1021772Sjl139090 	cmn_err(CE_CONT, "!\n");
1031772Sjl139090 	return (DDI_INTR_CLAIMED);
1041772Sjl139090 }
1051772Sjl139090 
1061772Sjl139090 /*
1071772Sjl139090  * pcmu_intr_wrapper
1081772Sjl139090  *
1091772Sjl139090  * This routine is used as wrapper around interrupt handlers installed by child
1101772Sjl139090  * device drivers.  This routine invokes the driver interrupt handlers and
1111772Sjl139090  * examines the return codes.
1121772Sjl139090  * There is a count of unclaimed interrupts kept on a per-ino basis. If at
1131772Sjl139090  * least one handler claims the interrupt then the counter is halved and the
1141772Sjl139090  * interrupt state machine is idled. If no handler claims the interrupt then
1151772Sjl139090  * the counter is incremented by one and the state machine is idled.
1161772Sjl139090  * If the count ever reaches the limit value set by pcmu_unclaimed_intr_max
1171772Sjl139090  * then the interrupt state machine is not idled thus preventing any further
1181772Sjl139090  * interrupts on that ino. The state machine will only be idled again if a
1191772Sjl139090  * handler is subsequently added or removed.
1201772Sjl139090  *
1211772Sjl139090  * return value: DDI_INTR_CLAIMED if any handlers claimed the interrupt,
1221772Sjl139090  * DDI_INTR_UNCLAIMED otherwise.
1231772Sjl139090  */
1241772Sjl139090 uint_t
pcmu_intr_wrapper(caddr_t arg)1251772Sjl139090 pcmu_intr_wrapper(caddr_t arg)
1261772Sjl139090 {
1271772Sjl139090 	pcmu_ib_ino_info_t *ino_p = (pcmu_ib_ino_info_t *)arg;
1281772Sjl139090 	uint_t result = 0, r;
1291772Sjl139090 	ih_t *ih_p = ino_p->pino_ih_start;
1301772Sjl139090 	int i;
1311772Sjl139090 #ifdef	DEBUG
1321772Sjl139090 	pcmu_t *pcmu_p = ino_p->pino_ib_p->pib_pcmu_p;
1331772Sjl139090 #endif
1341772Sjl139090 
1351772Sjl139090 
1361772Sjl139090 	for (i = 0; i < ino_p->pino_ih_size; i++, ih_p = ih_p->ih_next) {
1371772Sjl139090 		dev_info_t *dip = ih_p->ih_dip;
1381772Sjl139090 		uint_t (*handler)() = ih_p->ih_handler;
1391772Sjl139090 		caddr_t arg1 = ih_p->ih_handler_arg1;
1401772Sjl139090 		caddr_t arg2 = ih_p->ih_handler_arg2;
1411772Sjl139090 
1421772Sjl139090 		if (ih_p->ih_intr_state == PCMU_INTR_STATE_DISABLE) {
1431772Sjl139090 			PCMU_DBG3(PCMU_DBG_INTR, pcmu_p->pcmu_dip,
1441772Sjl139090 			    "pcmu_intr_wrapper: %s%d interrupt %d is "
1451772Sjl139090 			    "disabled\n", ddi_driver_name(dip),
1461772Sjl139090 			    ddi_get_instance(dip), ino_p->pino_ino);
1471772Sjl139090 			continue;
1481772Sjl139090 		}
1491772Sjl139090 
1501772Sjl139090 		DTRACE_PROBE4(pcmu__interrupt__start, dev_info_t, dip,
1511772Sjl139090 		    void *, handler, caddr_t, arg1, caddr_t, arg2);
1521772Sjl139090 
1531772Sjl139090 		r = (*handler)(arg1, arg2);
1541772Sjl139090 		DTRACE_PROBE4(pcmu__interrupt__complete, dev_info_t, dip,
1551772Sjl139090 		    void *, handler, caddr_t, arg1, int, r);
1561772Sjl139090 
1571772Sjl139090 		result += r;
1581772Sjl139090 	}
1591772Sjl139090 
1601772Sjl139090 	if (!result) {
1611772Sjl139090 		return (pcmu_spurintr(ino_p));
1621772Sjl139090 	}
1631772Sjl139090 	ino_p->pino_unclaimed = 0;
1641772Sjl139090 	/* clear the pending state */
1651772Sjl139090 	PCMU_IB_INO_INTR_CLEAR(ino_p->pino_clr_reg);
1661772Sjl139090 	return (DDI_INTR_CLAIMED);
1671772Sjl139090 }
1681772Sjl139090 
1691772Sjl139090 int
pcmu_add_intr(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)1701772Sjl139090 pcmu_add_intr(dev_info_t *dip, dev_info_t *rdip, ddi_intr_handle_impl_t *hdlp)
1711772Sjl139090 {
1721772Sjl139090 	pcmu_t *pcmu_p = get_pcmu_soft_state(ddi_get_instance(dip));
1731772Sjl139090 	pcmu_ib_t *pib_p = pcmu_p->pcmu_ib_p;
1741772Sjl139090 	ih_t *ih_p;
1751772Sjl139090 	pcmu_ib_ino_t ino;
1761772Sjl139090 	pcmu_ib_ino_info_t *ino_p; /* pulse interrupts have no ino */
1771772Sjl139090 	pcmu_ib_mondo_t mondo;
1781772Sjl139090 	uint32_t cpu_id;
1791772Sjl139090 	int ret;
1801772Sjl139090 
1811772Sjl139090 	ino = PCMU_IB_MONDO_TO_INO(hdlp->ih_vector);
1821772Sjl139090 
1831772Sjl139090 	PCMU_DBG3(PCMU_DBG_A_INTX, dip, "pcmu_add_intr: rdip=%s%d ino=%x\n",
1841772Sjl139090 	    ddi_driver_name(rdip), ddi_get_instance(rdip), ino);
1851772Sjl139090 
1861772Sjl139090 	if (ino > pib_p->pib_max_ino) {
1871772Sjl139090 		PCMU_DBG1(PCMU_DBG_A_INTX, dip, "ino %x is invalid\n", ino);
1881772Sjl139090 		return (DDI_INTR_NOTFOUND);
1891772Sjl139090 	}
1901772Sjl139090 
1911772Sjl139090 	if ((mondo = PCMU_IB_INO_TO_MONDO(pcmu_p->pcmu_ib_p, ino)) == 0)
1921772Sjl139090 		goto fail1;
1931772Sjl139090 
1941772Sjl139090 	ino = PCMU_IB_MONDO_TO_INO(mondo);
1951772Sjl139090 
1961772Sjl139090 	mutex_enter(&pib_p->pib_ino_lst_mutex);
1971772Sjl139090 	ih_p = pcmu_ib_alloc_ih(rdip, hdlp->ih_inum,
1981772Sjl139090 	    hdlp->ih_cb_func, hdlp->ih_cb_arg1, hdlp->ih_cb_arg2);
1991772Sjl139090 
2001772Sjl139090 	if (ino_p = pcmu_ib_locate_ino(pib_p, ino)) {	/* sharing ino */
2011772Sjl139090 		uint32_t intr_index = hdlp->ih_inum;
2021772Sjl139090 		if (pcmu_ib_ino_locate_intr(ino_p, rdip, intr_index)) {
2031772Sjl139090 			PCMU_DBG1(PCMU_DBG_A_INTX, dip,
2041772Sjl139090 			    "dup intr #%d\n", intr_index);
2051772Sjl139090 			goto fail3;
2061772Sjl139090 		}
2071772Sjl139090 
2081772Sjl139090 		/*
2091772Sjl139090 		 * add default weight(0) to the cpu that we are
2101772Sjl139090 		 * already targeting
2111772Sjl139090 		 */
2121772Sjl139090 		cpu_id = ino_p->pino_cpuid;
2131772Sjl139090 		intr_dist_cpuid_add_device_weight(cpu_id, rdip, 0);
2141772Sjl139090 		pcmu_ib_ino_add_intr(pcmu_p, ino_p, ih_p);
2151772Sjl139090 		goto ino_done;
2161772Sjl139090 	}
2171772Sjl139090 
2181772Sjl139090 	ino_p = pcmu_ib_new_ino(pib_p, ino, ih_p);
2191772Sjl139090 	hdlp->ih_vector = mondo;
2201772Sjl139090 
2211772Sjl139090 	PCMU_DBG2(PCMU_DBG_A_INTX, dip, "pcmu_add_intr:  pil=0x%x mondo=0x%x\n",
2221772Sjl139090 	    hdlp->ih_pri, hdlp->ih_vector);
2231772Sjl139090 
2241772Sjl139090 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp,
2251772Sjl139090 	    (ddi_intr_handler_t *)pcmu_intr_wrapper, (caddr_t)ino_p, NULL);
2261772Sjl139090 
2271772Sjl139090 	ret = i_ddi_add_ivintr(hdlp);
2281772Sjl139090 
2291772Sjl139090 	/*
2301772Sjl139090 	 * Restore original interrupt handler
2311772Sjl139090 	 * and arguments in interrupt handle.
2321772Sjl139090 	 */
2331772Sjl139090 	DDI_INTR_ASSIGN_HDLR_N_ARGS(hdlp, ih_p->ih_handler,
2341772Sjl139090 	    ih_p->ih_handler_arg1, ih_p->ih_handler_arg2);
2351772Sjl139090 
2361772Sjl139090 	if (ret != DDI_SUCCESS) {
2371772Sjl139090 		goto fail4;
2381772Sjl139090 	}
2391772Sjl139090 	/* Save the pil for this ino */
2401772Sjl139090 	ino_p->pino_pil = hdlp->ih_pri;
2411772Sjl139090 
2421772Sjl139090 	/* clear and enable interrupt */
2431772Sjl139090 	PCMU_IB_INO_INTR_CLEAR(ino_p->pino_clr_reg);
2441772Sjl139090 
2451772Sjl139090 	/* select cpu for sharing and removal */
2461772Sjl139090 	cpu_id = pcmu_intr_dist_cpuid(pib_p, ino_p);
2471772Sjl139090 	ino_p->pino_cpuid = cpu_id;
2481772Sjl139090 	ino_p->pino_established = 1;
2491772Sjl139090 	intr_dist_cpuid_add_device_weight(cpu_id, rdip, 0);
2501772Sjl139090 
2511772Sjl139090 	cpu_id = u2u_translate_tgtid(pib_p->pib_pcmu_p,
2521772Sjl139090 	    cpu_id, ino_p->pino_map_reg);
2531772Sjl139090 	*ino_p->pino_map_reg = ib_get_map_reg(mondo, cpu_id);
2541772Sjl139090 	*ino_p->pino_map_reg;
2551772Sjl139090 ino_done:
2561772Sjl139090 	mutex_exit(&pib_p->pib_ino_lst_mutex);
2571772Sjl139090 done:
2581772Sjl139090 	PCMU_DBG2(PCMU_DBG_A_INTX, dip, "done! Interrupt 0x%x pil=%x\n",
259*6587Sjfrank 	    hdlp->ih_vector, hdlp->ih_pri);
2601772Sjl139090 	return (DDI_SUCCESS);
2611772Sjl139090 fail4:
2621772Sjl139090 	pcmu_ib_delete_ino(pib_p, ino_p);
2631772Sjl139090 fail3:
2641772Sjl139090 	if (ih_p->ih_config_handle)
2651772Sjl139090 		pci_config_teardown(&ih_p->ih_config_handle);
2661772Sjl139090 	mutex_exit(&pib_p->pib_ino_lst_mutex);
2671772Sjl139090 	kmem_free(ih_p, sizeof (ih_t));
2681772Sjl139090 fail1:
2691772Sjl139090 	PCMU_DBG2(PCMU_DBG_A_INTX, dip, "Failed! Interrupt 0x%x pil=%x\n",
270*6587Sjfrank 	    hdlp->ih_vector, hdlp->ih_pri);
2711772Sjl139090 	return (DDI_FAILURE);
2721772Sjl139090 }
2731772Sjl139090 
2741772Sjl139090 int
pcmu_remove_intr(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)2751772Sjl139090 pcmu_remove_intr(dev_info_t *dip, dev_info_t *rdip,
2761772Sjl139090     ddi_intr_handle_impl_t *hdlp)
2771772Sjl139090 {
2781772Sjl139090 	pcmu_t *pcmu_p = get_pcmu_soft_state(ddi_get_instance(dip));
2791772Sjl139090 	pcmu_ib_t *pib_p = pcmu_p->pcmu_ib_p;
2801772Sjl139090 	pcmu_ib_ino_t ino;
2811772Sjl139090 	pcmu_ib_mondo_t mondo;
2821772Sjl139090 	pcmu_ib_ino_info_t *ino_p;	/* non-pulse only */
2831772Sjl139090 	ih_t *ih_p;			/* non-pulse only */
2841772Sjl139090 
2851772Sjl139090 	ino = PCMU_IB_MONDO_TO_INO(hdlp->ih_vector);
2861772Sjl139090 
2871772Sjl139090 	PCMU_DBG3(PCMU_DBG_R_INTX, dip, "pcmu_rem_intr: rdip=%s%d ino=%x\n",
2881772Sjl139090 	    ddi_driver_name(rdip), ddi_get_instance(rdip), ino);
2891772Sjl139090 
2901772Sjl139090 	/* Translate the interrupt property */
2911772Sjl139090 	mondo = PCMU_IB_INO_TO_MONDO(pcmu_p->pcmu_ib_p, ino);
2921772Sjl139090 	if (mondo == 0) {
2931772Sjl139090 		PCMU_DBG1(PCMU_DBG_R_INTX, dip,
2941772Sjl139090 		    "can't get mondo for ino %x\n", ino);
2951772Sjl139090 		return (DDI_FAILURE);
2961772Sjl139090 	}
2971772Sjl139090 	ino = PCMU_IB_MONDO_TO_INO(mondo);
2981772Sjl139090 
2991772Sjl139090 	mutex_enter(&pib_p->pib_ino_lst_mutex);
3001772Sjl139090 	ino_p = pcmu_ib_locate_ino(pib_p, ino);
3011772Sjl139090 	if (!ino_p) {
3021772Sjl139090 		mutex_exit(&pib_p->pib_ino_lst_mutex);
3031772Sjl139090 		return (DDI_SUCCESS);
3041772Sjl139090 	}
3051772Sjl139090 
3061772Sjl139090 	ih_p = pcmu_ib_ino_locate_intr(ino_p, rdip, hdlp->ih_inum);
307*6587Sjfrank 	if (pcmu_ib_ino_rem_intr(pcmu_p, ino_p, ih_p) != DDI_SUCCESS) {
308*6587Sjfrank 		mutex_exit(&pib_p->pib_ino_lst_mutex);
309*6587Sjfrank 		return (DDI_FAILURE);
310*6587Sjfrank 	}
3111772Sjl139090 	intr_dist_cpuid_rem_device_weight(ino_p->pino_cpuid, rdip);
3121772Sjl139090 	if (ino_p->pino_ih_size == 0) {
3131772Sjl139090 		PCMU_IB_INO_INTR_PEND(ib_clear_intr_reg_addr(pib_p, ino));
3141772Sjl139090 		hdlp->ih_vector = mondo;
3151772Sjl139090 		i_ddi_rem_ivintr(hdlp);
3161772Sjl139090 		pcmu_ib_delete_ino(pib_p, ino_p);
3171772Sjl139090 	}
3181772Sjl139090 
3191772Sjl139090 	/* re-enable interrupt only if mapping register still shared */
3201772Sjl139090 	if (ino_p->pino_ih_size) {
3211772Sjl139090 		PCMU_IB_INO_INTR_ON(ino_p->pino_map_reg);
3221772Sjl139090 		*ino_p->pino_map_reg;
3231772Sjl139090 	}
3241772Sjl139090 	mutex_exit(&pib_p->pib_ino_lst_mutex);
3251772Sjl139090 	if (ino_p->pino_ih_size == 0) {
3261772Sjl139090 		kmem_free(ino_p, sizeof (pcmu_ib_ino_info_t));
3271772Sjl139090 	}
3281772Sjl139090 	PCMU_DBG1(PCMU_DBG_R_INTX, dip, "success! mondo=%x\n", mondo);
3291772Sjl139090 	return (DDI_SUCCESS);
3301772Sjl139090 }
3311772Sjl139090 
3321772Sjl139090 /*
3331772Sjl139090  * free the pcmu_inos array allocated during pcmu_intr_setup. the actual
3341772Sjl139090  * interrupts are torn down by their respective block destroy routines:
3351772Sjl139090  * cb_destroy, pcmu_pbm_destroy, and ib_destroy.
3361772Sjl139090  */
3371772Sjl139090 void
pcmu_intr_teardown(pcmu_t * pcmu_p)3381772Sjl139090 pcmu_intr_teardown(pcmu_t *pcmu_p)
3391772Sjl139090 {
3401772Sjl139090 	kmem_free(pcmu_p->pcmu_inos, pcmu_p->pcmu_inos_len);
3411772Sjl139090 	pcmu_p->pcmu_inos = NULL;
3421772Sjl139090 	pcmu_p->pcmu_inos_len = 0;
3431772Sjl139090 }
344