xref: /onnv-gate/usr/src/uts/sun4u/opl/io/pcicmu/pcmu_cb.c (revision 2619:daf0055367bc)
1*1772Sjl139090 /*
2*1772Sjl139090  * CDDL HEADER START
3*1772Sjl139090  *
4*1772Sjl139090  * The contents of this file are subject to the terms of the
5*1772Sjl139090  * Common Development and Distribution License (the "License").
6*1772Sjl139090  * You may not use this file except in compliance with the License.
7*1772Sjl139090  *
8*1772Sjl139090  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*1772Sjl139090  * or http://www.opensolaris.org/os/licensing.
10*1772Sjl139090  * See the License for the specific language governing permissions
11*1772Sjl139090  * and limitations under the License.
12*1772Sjl139090  *
13*1772Sjl139090  * When distributing Covered Code, include this CDDL HEADER in each
14*1772Sjl139090  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*1772Sjl139090  * If applicable, add the following below this CDDL HEADER, with the
16*1772Sjl139090  * fields enclosed by brackets "[]" replaced with your own identifying
17*1772Sjl139090  * information: Portions Copyright [yyyy] [name of copyright owner]
18*1772Sjl139090  *
19*1772Sjl139090  * CDDL HEADER END
20*1772Sjl139090  */
21*1772Sjl139090 /*
22*1772Sjl139090  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23*1772Sjl139090  * Use is subject to license terms.
24*1772Sjl139090  */
25*1772Sjl139090 
26*1772Sjl139090 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*1772Sjl139090 
28*1772Sjl139090 /*
29*1772Sjl139090  * CMU-CH Control Block object
30*1772Sjl139090  */
31*1772Sjl139090 #include <sys/types.h>
32*1772Sjl139090 #include <sys/kmem.h>
33*1772Sjl139090 #include <sys/systm.h>
34*1772Sjl139090 #include <sys/async.h>
35*1772Sjl139090 #include <sys/sunddi.h>
36*1772Sjl139090 #include <sys/ddi_impldefs.h>
37*1772Sjl139090 #include <sys/pcicmu/pcicmu.h>
38*1772Sjl139090 #include <sys/machsystm.h>
39*1772Sjl139090 
40*1772Sjl139090 extern uint64_t	xc_tick_jump_limit;
41*1772Sjl139090 
42*1772Sjl139090 void
pcmu_cb_create(pcmu_t * pcmu_p)43*1772Sjl139090 pcmu_cb_create(pcmu_t *pcmu_p)
44*1772Sjl139090 {
45*1772Sjl139090 	pcmu_cb_t *pcb_p = (pcmu_cb_t *)
46*1772Sjl139090 	    kmem_zalloc(sizeof (pcmu_cb_t), KM_SLEEP);
47*1772Sjl139090 	mutex_init(&pcb_p->pcb_intr_lock, NULL, MUTEX_DRIVER, NULL);
48*1772Sjl139090 	pcmu_p->pcmu_cb_p = pcb_p;
49*1772Sjl139090 	pcb_p->pcb_pcmu_p = pcmu_p;
50*1772Sjl139090 	pcmu_cb_setup(pcmu_p);
51*1772Sjl139090 }
52*1772Sjl139090 
53*1772Sjl139090 void
pcmu_cb_destroy(pcmu_t * pcmu_p)54*1772Sjl139090 pcmu_cb_destroy(pcmu_t *pcmu_p)
55*1772Sjl139090 {
56*1772Sjl139090 	pcmu_cb_t *pcb_p = pcmu_p->pcmu_cb_p;
57*1772Sjl139090 
58*1772Sjl139090 	intr_dist_rem(pcmu_cb_intr_dist, pcb_p);
59*1772Sjl139090 	pcmu_cb_teardown(pcmu_p);
60*1772Sjl139090 	pcmu_p->pcmu_cb_p = NULL;
61*1772Sjl139090 	mutex_destroy(&pcb_p->pcb_intr_lock);
62*1772Sjl139090 	kmem_free(pcb_p, sizeof (pcmu_cb_t));
63*1772Sjl139090 }
64*1772Sjl139090 
65*1772Sjl139090 uint64_t
pcmu_cb_ino_to_map_pa(pcmu_cb_t * pcb_p,pcmu_ib_ino_t ino)66*1772Sjl139090 pcmu_cb_ino_to_map_pa(pcmu_cb_t *pcb_p, pcmu_ib_ino_t ino)
67*1772Sjl139090 {
68*1772Sjl139090 	return (pcb_p->pcb_map_pa + ((ino & 0x1f) << 3));
69*1772Sjl139090 }
70*1772Sjl139090 
71*1772Sjl139090 uint64_t
pcmu_cb_ino_to_clr_pa(pcmu_cb_t * pcb_p,pcmu_ib_ino_t ino)72*1772Sjl139090 pcmu_cb_ino_to_clr_pa(pcmu_cb_t *pcb_p, pcmu_ib_ino_t ino)
73*1772Sjl139090 {
74*1772Sjl139090 	return (pcb_p->pcb_clr_pa + ((ino & 0x1f) << 3));
75*1772Sjl139090 }
76*1772Sjl139090 
77*1772Sjl139090 static void
pcmu_cb_set_nintr_reg(pcmu_cb_t * pcb_p,pcmu_ib_ino_t ino,uint64_t value)78*1772Sjl139090 pcmu_cb_set_nintr_reg(pcmu_cb_t *pcb_p, pcmu_ib_ino_t ino, uint64_t value)
79*1772Sjl139090 {
80*1772Sjl139090 	uint64_t pa = pcmu_cb_ino_to_clr_pa(pcb_p, ino);
81*1772Sjl139090 
82*1772Sjl139090 	PCMU_DBG3(PCMU_DBG_CB|PCMU_DBG_CONT, NULL,
83*1772Sjl139090 		"pci-%x pcmu_cb_set_nintr_reg: ino=%x PA=%016llx\n",
84*1772Sjl139090 		pcb_p->pcb_pcmu_p->pcmu_id, ino, pa);
85*1772Sjl139090 
86*1772Sjl139090 	stdphysio(pa, value);
87*1772Sjl139090 	(void) lddphysio(pa);	/* flush the previous write */
88*1772Sjl139090 }
89*1772Sjl139090 
90*1772Sjl139090 /*
91*1772Sjl139090  * enable an internal interrupt source:
92*1772Sjl139090  * if an interrupt is shared by both sides, record it in pcb_inos[] and
93*1772Sjl139090  * cb will own its distribution.
94*1772Sjl139090  */
95*1772Sjl139090 void
pcmu_cb_enable_nintr(pcmu_t * pcmu_p,pcmu_cb_nintr_index_t idx)96*1772Sjl139090 pcmu_cb_enable_nintr(pcmu_t *pcmu_p, pcmu_cb_nintr_index_t idx)
97*1772Sjl139090 {
98*1772Sjl139090 	pcmu_cb_t *pcb_p = pcmu_p->pcmu_cb_p;
99*1772Sjl139090 	pcmu_ib_ino_t ino = PCMU_IB_MONDO_TO_INO(pcmu_p->pcmu_inos[idx]);
100*1772Sjl139090 	pcmu_ib_mondo_t mondo = PCMU_CB_INO_TO_MONDO(pcb_p, ino);
101*1772Sjl139090 	uint32_t cpu_id;
102*1772Sjl139090 	uint64_t reg, pa;
103*1772Sjl139090 	pcmu_ib_t *pib_p = pcb_p->pcb_pcmu_p->pcmu_ib_p;
104*1772Sjl139090 	volatile uint64_t *imr_p = ib_intr_map_reg_addr(pib_p, ino);
105*1772Sjl139090 
106*1772Sjl139090 	ASSERT(idx < CBNINTR_MAX);
107*1772Sjl139090 	pa = pcmu_cb_ino_to_map_pa(pcb_p, ino);
108*1772Sjl139090 
109*1772Sjl139090 	mutex_enter(&pcb_p->pcb_intr_lock);
110*1772Sjl139090 	cpu_id = intr_dist_cpuid();
111*1772Sjl139090 
112*1772Sjl139090 	cpu_id = u2u_translate_tgtid(pib_p->pib_pcmu_p, cpu_id, imr_p);
113*1772Sjl139090 
114*1772Sjl139090 	reg = ib_get_map_reg(mondo, cpu_id);
115*1772Sjl139090 	stdphysio(pa, reg);
116*1772Sjl139090 
117*1772Sjl139090 	ASSERT(pcb_p->pcb_inos[idx] == 0);
118*1772Sjl139090 	pcb_p->pcb_inos[idx] = ino;
119*1772Sjl139090 
120*1772Sjl139090 	pcmu_cb_set_nintr_reg(pcb_p, ino, PCMU_CLEAR_INTR_REG_IDLE);
121*1772Sjl139090 	mutex_exit(&pcb_p->pcb_intr_lock);
122*1772Sjl139090 
123*1772Sjl139090 	PCMU_DBG3(PCMU_DBG_CB|PCMU_DBG_CONT, NULL,
124*1772Sjl139090 	    "pci-%x pcmu_cb_enable_nintr: ino=%x cpu_id=%x\n",
125*1772Sjl139090 	    pcmu_p->pcmu_id, ino, cpu_id);
126*1772Sjl139090 	PCMU_DBG2(PCMU_DBG_CB|PCMU_DBG_CONT, NULL,
127*1772Sjl139090 	    "\tPA=%016llx data=%016llx\n", pa, reg);
128*1772Sjl139090 }
129*1772Sjl139090 
130*1772Sjl139090 static void
pcmu_cb_disable_nintr_reg(pcmu_cb_t * pcb_p,pcmu_ib_ino_t ino,int wait)131*1772Sjl139090 pcmu_cb_disable_nintr_reg(pcmu_cb_t *pcb_p, pcmu_ib_ino_t ino, int wait)
132*1772Sjl139090 {
133*1772Sjl139090 	uint64_t tmp, map_reg_pa = pcmu_cb_ino_to_map_pa(pcb_p, ino);
134*1772Sjl139090 	ASSERT(MUTEX_HELD(&pcb_p->pcb_intr_lock));
135*1772Sjl139090 
136*1772Sjl139090 	/* mark interrupt invalid in mapping register */
137*1772Sjl139090 	tmp = lddphysio(map_reg_pa) & ~PCMU_INTR_MAP_REG_VALID;
138*1772Sjl139090 	stdphysio(map_reg_pa, tmp);
139*1772Sjl139090 	(void) lddphysio(map_reg_pa);   /* flush previous write */
140*1772Sjl139090 
141*1772Sjl139090 	if (wait) {
142*1772Sjl139090 		hrtime_t start_time;
143*1772Sjl139090 		hrtime_t prev, curr, interval, jump;
144*1772Sjl139090 		hrtime_t intr_timeout;
145*1772Sjl139090 		uint64_t state_reg_pa = pcb_p->pcb_obsta_pa;
146*1772Sjl139090 		uint_t shift = (ino & 0x1f) << 1;
147*1772Sjl139090 
148*1772Sjl139090 		/* busy wait if there is interrupt being processed */
149*1772Sjl139090 		/* unless panic or timeout for interrupt pending is reached */
150*1772Sjl139090 
151*1772Sjl139090 		intr_timeout = pcmu_intrpend_timeout;
152*1772Sjl139090 		jump = TICK_TO_NSEC(xc_tick_jump_limit);
153*1772Sjl139090 		start_time = curr = gethrtime();
154*1772Sjl139090 		while ((((lddphysio(state_reg_pa) >> shift) &
155*1772Sjl139090 			PCMU_CLEAR_INTR_REG_MASK) ==
156*1772Sjl139090 			PCMU_CLEAR_INTR_REG_PENDING) && !panicstr) {
157*1772Sjl139090 			/*
158*1772Sjl139090 			 * If we have a really large jump in hrtime, it is most
159*1772Sjl139090 			 * probably because we entered the debugger (or OBP,
160*1772Sjl139090 			 * in general). So, we adjust the timeout accordingly
161*1772Sjl139090 			 * to prevent declaring an interrupt timeout. The
162*1772Sjl139090 			 * master-interrupt mechanism in OBP should deliver
163*1772Sjl139090 			 * the interrupts properly.
164*1772Sjl139090 			 */
165*1772Sjl139090 			prev = curr;
166*1772Sjl139090 			curr = gethrtime();
167*1772Sjl139090 			interval = curr - prev;
168*1772Sjl139090 			if (interval > jump)
169*1772Sjl139090 				intr_timeout += interval;
170*1772Sjl139090 			if (curr - start_time > intr_timeout) {
171*1772Sjl139090 				cmn_err(CE_WARN, "pcmu@%x "
172*1772Sjl139090 				    "pcmu_cb_disable_nintr_reg(%lx,%x) timeout",
173*1772Sjl139090 				    pcb_p->pcb_pcmu_p->pcmu_id, map_reg_pa,
174*1772Sjl139090 				    PCMU_CB_INO_TO_MONDO(pcb_p, ino));
175*1772Sjl139090 				break;
176*1772Sjl139090 			}
177*1772Sjl139090 		}
178*1772Sjl139090 	}
179*1772Sjl139090 }
180*1772Sjl139090 
181*1772Sjl139090 void
pcmu_cb_disable_nintr(pcmu_cb_t * pcb_p,pcmu_cb_nintr_index_t idx,int wait)182*1772Sjl139090 pcmu_cb_disable_nintr(pcmu_cb_t *pcb_p, pcmu_cb_nintr_index_t idx, int wait)
183*1772Sjl139090 {
184*1772Sjl139090 	pcmu_ib_t *pib_p = pcb_p->pcb_pcmu_p->pcmu_ib_p;
185*1772Sjl139090 	volatile uint64_t *imr_p;
186*1772Sjl139090 	pcmu_ib_ino_t ino = pcb_p->pcb_inos[idx];
187*1772Sjl139090 	ASSERT(idx < CBNINTR_MAX);
188*1772Sjl139090 	ASSERT(ino);
189*1772Sjl139090 
190*1772Sjl139090 	imr_p = ib_intr_map_reg_addr(pib_p, ino);
191*1772Sjl139090 	mutex_enter(&pcb_p->pcb_intr_lock);
192*1772Sjl139090 	pcmu_cb_disable_nintr_reg(pcb_p, ino, wait);
193*1772Sjl139090 	pcmu_cb_set_nintr_reg(pcb_p, ino, PCMU_CLEAR_INTR_REG_PENDING);
194*1772Sjl139090 	pcb_p->pcb_inos[idx] = 0;
195*1772Sjl139090 	mutex_exit(&pcb_p->pcb_intr_lock);
196*1772Sjl139090 	u2u_ittrans_cleanup((u2u_ittrans_data_t *)(pcb_p->pcb_ittrans_cookie),
197*1772Sjl139090 			imr_p);
198*1772Sjl139090 }
199*1772Sjl139090 
200*1772Sjl139090 void
pcmu_cb_clear_nintr(pcmu_cb_t * pcb_p,pcmu_cb_nintr_index_t idx)201*1772Sjl139090 pcmu_cb_clear_nintr(pcmu_cb_t *pcb_p, pcmu_cb_nintr_index_t idx)
202*1772Sjl139090 {
203*1772Sjl139090 	pcmu_ib_ino_t ino = pcb_p->pcb_inos[idx];
204*1772Sjl139090 	ASSERT(idx < CBNINTR_MAX);
205*1772Sjl139090 	ASSERT(ino);
206*1772Sjl139090 	pcmu_cb_set_nintr_reg(pcb_p, ino, PCMU_CLEAR_INTR_REG_IDLE);
207*1772Sjl139090 }
208*1772Sjl139090 
209*1772Sjl139090 void
pcmu_cb_intr_dist(void * arg)210*1772Sjl139090 pcmu_cb_intr_dist(void *arg)
211*1772Sjl139090 {
212*1772Sjl139090 	int i;
213*1772Sjl139090 	pcmu_cb_t *pcb_p = (pcmu_cb_t *)arg;
214*1772Sjl139090 
215*1772Sjl139090 	mutex_enter(&pcb_p->pcb_intr_lock);
216*1772Sjl139090 	for (i = 0; i < pcb_p->pcb_no_of_inos; i++) {
217*1772Sjl139090 		uint64_t mr_pa;
218*1772Sjl139090 		volatile uint64_t imr;
219*1772Sjl139090 		pcmu_ib_mondo_t mondo;
220*1772Sjl139090 		uint32_t cpu_id;
221*1772Sjl139090 		pcmu_ib_t *pib_p = pcb_p->pcb_pcmu_p->pcmu_ib_p;
222*1772Sjl139090 		volatile uint64_t *imr_p;
223*1772Sjl139090 
224*1772Sjl139090 		pcmu_ib_ino_t ino = pcb_p->pcb_inos[i];
225*1772Sjl139090 		if (!ino)	/* skip non-shared interrupts */
226*1772Sjl139090 			continue;
227*1772Sjl139090 
228*1772Sjl139090 		mr_pa = pcmu_cb_ino_to_map_pa(pcb_p, ino);
229*1772Sjl139090 		imr = lddphysio(mr_pa);
230*1772Sjl139090 		if (!PCMU_IB_INO_INTR_ISON(imr))
231*1772Sjl139090 			continue;
232*1772Sjl139090 
233*1772Sjl139090 		mondo = PCMU_CB_INO_TO_MONDO(pcb_p, ino);
234*1772Sjl139090 		cpu_id = intr_dist_cpuid();
235*1772Sjl139090 		imr_p = ib_intr_map_reg_addr(pib_p, ino);
236*1772Sjl139090 
237*1772Sjl139090 		cpu_id = u2u_translate_tgtid(pib_p->pib_pcmu_p, cpu_id, imr_p);
238*1772Sjl139090 
239*1772Sjl139090 		pcmu_cb_disable_nintr_reg(pcb_p, ino, PCMU_IB_INTR_WAIT);
240*1772Sjl139090 		stdphysio(mr_pa, ib_get_map_reg(mondo, cpu_id));
241*1772Sjl139090 		(void) lddphysio(mr_pa);	/* flush previous write */
242*1772Sjl139090 	}
243*1772Sjl139090 	mutex_exit(&pcb_p->pcb_intr_lock);
244*1772Sjl139090 }
245*1772Sjl139090 
246*1772Sjl139090 void
pcmu_cb_suspend(pcmu_cb_t * pcb_p)247*1772Sjl139090 pcmu_cb_suspend(pcmu_cb_t *pcb_p)
248*1772Sjl139090 {
249*1772Sjl139090 	int i, inos = pcb_p->pcb_no_of_inos;
250*1772Sjl139090 	ASSERT(!pcb_p->pcb_imr_save);
251*1772Sjl139090 	pcb_p->pcb_imr_save = kmem_alloc(inos * sizeof (uint64_t), KM_SLEEP);
252*1772Sjl139090 
253*1772Sjl139090 	/*
254*1772Sjl139090 	 * save the internal interrupts' mapping registers content
255*1772Sjl139090 	 *
256*1772Sjl139090 	 * The PBM IMR really doesn't need to be saved, as it is
257*1772Sjl139090 	 * different per side and is handled by pcmu_pbm_suspend/resume.
258*1772Sjl139090 	 * But it complicates the logic.
259*1772Sjl139090 	 */
260*1772Sjl139090 	for (i = 0; i < inos; i++) {
261*1772Sjl139090 		uint64_t pa;
262*1772Sjl139090 		pcmu_ib_ino_t ino = pcb_p->pcb_inos[i];
263*1772Sjl139090 		if (!ino)
264*1772Sjl139090 			continue;
265*1772Sjl139090 		pa = pcmu_cb_ino_to_map_pa(pcb_p, ino);
266*1772Sjl139090 		pcb_p->pcb_imr_save[i] = lddphysio(pa);
267*1772Sjl139090 	}
268*1772Sjl139090 }
269*1772Sjl139090 
270*1772Sjl139090 void
pcmu_cb_resume(pcmu_cb_t * pcb_p)271*1772Sjl139090 pcmu_cb_resume(pcmu_cb_t *pcb_p)
272*1772Sjl139090 {
273*1772Sjl139090 	int i;
274*1772Sjl139090 	for (i = 0; i < pcb_p->pcb_no_of_inos; i++) {
275*1772Sjl139090 		uint64_t pa;
276*1772Sjl139090 		pcmu_ib_ino_t ino = pcb_p->pcb_inos[i];
277*1772Sjl139090 		if (!ino)
278*1772Sjl139090 			continue;
279*1772Sjl139090 		pa = pcmu_cb_ino_to_map_pa(pcb_p, ino);
280*1772Sjl139090 		pcmu_cb_set_nintr_reg(pcb_p, ino, PCMU_CLEAR_INTR_REG_IDLE);
281*1772Sjl139090 		stdphysio(pa, pcb_p->pcb_imr_save[i]);	/* restore IMR */
282*1772Sjl139090 	}
283*1772Sjl139090 	kmem_free(pcb_p->pcb_imr_save,
284*1772Sjl139090 	    pcb_p->pcb_no_of_inos * sizeof (uint64_t));
285*1772Sjl139090 	pcb_p->pcb_imr_save = NULL;
286*1772Sjl139090 }
287