xref: /illumos-gate/usr/src/uts/sun4u/io/mach_rootnex.c (revision 4bc0a2ef)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * sun4u root nexus driver
31  */
32 #include <sys/conf.h>
33 #include <sys/cpuvar.h>
34 #include <sys/sysiosbus.h>
35 #include <sys/intreg.h>
36 #include <sys/ddi_subrdefs.h>
37 #include <sys/sunndi.h>
38 #include <sys/async.h>
39 
40 /* Useful debugging Stuff */
41 #include <sys/nexusdebug.h>
42 #define	ROOTNEX_MAP_DEBUG		0x1
43 #define	ROOTNEX_INTR_DEBUG		0x2
44 
45 /*
46  * Extern declarations
47  */
48 extern uint_t	root_phys_addr_lo_mask;
49 extern int rootnex_name_child(dev_info_t *child, char *name, int namelen);
50 extern int rootnex_ctl_uninitchild(dev_info_t *dip);
51 
52 uint_t	root_phys_addr_hi_mask = 0xffffffff;
53 
54 /*
55  * config information
56  */
57 int
58 rootnex_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
59     ddi_intr_handle_impl_t *hdlp);
60 
61 int
62 rootnex_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
63     ddi_intr_handle_impl_t *hdlp);
64 
65 int
66 rootnex_get_intr_pri(dev_info_t *dip, dev_info_t *rdip,
67     ddi_intr_handle_impl_t *hdlp);
68 
69 ddi_iblock_cookie_t rootnex_err_ibc;
70 
71 /*
72  * rootnex_add_intr_impl:
73  */
74 /*ARGSUSED*/
75 int
76 rootnex_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
77     ddi_intr_handle_impl_t *hdlp)
78 {
79 	volatile uint64_t	*intr_mapping_reg;
80 	volatile uint64_t	mondo_vector;
81 	int32_t			r_upaid = -1;
82 	int32_t			slave = 0;
83 	int32_t			upa_portid;
84 	int			len, ret;
85 
86 	if ((upa_portid = ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
87 	    DDI_PROP_DONTPASS, "upa-portid", -1)) != -1) {
88 		if (ddi_getprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
89 		    "upa-interrupt-slave", 0) != 0) {
90 
91 			/* Give slave devices pri of 5. e.g. fb's */
92 			hdlp->ih_pri = 5;
93 		}
94 
95 		/*
96 		 * Translate the interrupt property by stuffing in the
97 		 * portid for those devices which have a upa-portid.
98 		 */
99 		hdlp->ih_vector |= (UPAID_TO_IGN(upa_portid) << 6);
100 	}
101 
102 	/*
103 	 * Hack to support the UPA slave devices before the 1275
104 	 * support for imap was introduced.
105 	 */
106 	if (ddi_getproplen(DDI_DEV_T_ANY, dip, NULL, "interrupt-map",
107 	    &len) != DDI_PROP_SUCCESS && ddi_getprop(DDI_DEV_T_ANY,
108 	    rdip, DDI_PROP_DONTPASS, "upa-interrupt-slave", 0) != 0 &&
109 	    ddi_get_parent(rdip) == dip) {
110 		slave = 1;
111 
112 		if ((r_upaid = ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
113 		    DDI_PROP_DONTPASS, "upa-portid", -1)) != -1) {
114 			extern uint64_t *get_intr_mapping_reg(int, int);
115 
116 			if ((intr_mapping_reg = get_intr_mapping_reg(
117 			    r_upaid, 1)) == NULL)
118 				return (DDI_FAILURE);
119 		} else
120 			return (DDI_FAILURE);
121 	}
122 
123 	if ((ret = i_ddi_add_ivintr(hdlp)) != DDI_SUCCESS)
124 		return (ret);
125 
126 	/*
127 	 * Hack to support the UPA slave devices before the 1275
128 	 * support for imap was introduced.
129 	 */
130 	if (slave) {
131 		/*
132 		 * Program the interrupt mapping register.
133 		 * Interrupts from the slave UPA devices are
134 		 * directed at the boot CPU until it is known
135 		 * that they can be safely redirected while
136 		 * running under load.
137 		 */
138 		mondo_vector = cpu0.cpu_id << IMR_TID_SHIFT;
139 		mondo_vector |= (IMR_VALID | (uint64_t)hdlp->ih_vector);
140 
141 		/* Set the mapping register */
142 		*intr_mapping_reg = mondo_vector;
143 
144 		/* Flush write buffers */
145 		mondo_vector = *intr_mapping_reg;
146 	}
147 
148 	return (ret);
149 }
150 
151 /*
152  * rootnex_remove_intr_impl:
153  */
154 /*ARGSUSED*/
155 int
156 rootnex_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
157     ddi_intr_handle_impl_t *hdlp)
158 {
159 	int32_t		upa_portid;
160 	int		len;
161 
162 	if ((upa_portid = ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
163 	    DDI_PROP_DONTPASS, "upa-portid", -1)) != -1) {
164 		/*
165 		 * Translate the interrupt property by stuffing in the
166 		 * portid for those devices which have a upa-portid.
167 		 */
168 		hdlp->ih_vector |= (UPAID_TO_IGN(upa_portid) << 6);
169 	}
170 
171 	/*
172 	 * Hack to support the UPA slave devices before the 1275
173 	 * support for imap was introduced.
174 	 */
175 	if (ddi_getproplen(DDI_DEV_T_ANY, dip, NULL, "interrupt-map",
176 	    &len) != DDI_PROP_SUCCESS && ddi_getprop(DDI_DEV_T_ANY,
177 	    rdip, DDI_PROP_DONTPASS, "upa-interrupt-slave", 0) != 0) {
178 		int32_t r_upaid = -1;
179 
180 		if ((r_upaid = ddi_prop_get_int(DDI_DEV_T_ANY, rdip,
181 		    DDI_PROP_DONTPASS, "upa-portid", -1)) != -1 &&
182 		    ddi_get_parent(rdip) == dip) {
183 			volatile uint64_t *intr_mapping_reg;
184 			volatile uint64_t flush_data;
185 			extern uint64_t *get_intr_mapping_reg(int, int);
186 
187 			if ((intr_mapping_reg = get_intr_mapping_reg(
188 			    r_upaid, 1)) == NULL)
189 				return (DDI_SUCCESS);
190 
191 			/* Clear the mapping register */
192 			*intr_mapping_reg = 0x0ull;
193 
194 			/* Flush write buffers */
195 			flush_data = *intr_mapping_reg;
196 #ifdef lint
197 			flush_data = flush_data;
198 #endif /* lint */
199 		}
200 	}
201 
202 	i_ddi_rem_ivintr(hdlp);
203 
204 	return (DDI_SUCCESS);
205 }
206 
207 /*
208  * rootnex_get_intr_pri:
209  */
210 /*ARGSUSED*/
211 int
212 rootnex_get_intr_pri(dev_info_t *dip, dev_info_t *rdip,
213     ddi_intr_handle_impl_t *hdlp)
214 {
215 	int	pri = hdlp->ih_pri;
216 
217 	if (ddi_prop_get_int(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
218 	    "upa-portid", -1) != -1) {
219 		if (ddi_getprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS,
220 		    "upa-interrupt-slave", 0) != 0) {
221 
222 			/* Give slave devices pri of 5. e.g. fb's */
223 			pri = 5;
224 		}
225 	}
226 
227 	return (pri);
228 }
229 
230 int
231 rootnex_ctl_reportdev_impl(dev_info_t *dev)
232 {
233 	struct regspec *rp;
234 	char buf[80];
235 	char *p = buf;
236 	register int n;
237 	int	portid;
238 	int	nodeid;
239 
240 	(void) sprintf(p, "%s%d at root", ddi_driver_name(dev),
241 	    ddi_get_instance(dev));
242 	p += strlen(p);
243 
244 	if ((n = sparc_pd_getnreg(dev)) > 0) {
245 		rp = sparc_pd_getreg(dev, 0);
246 
247 		(void) strcpy(p, ": ");
248 		p += strlen(p);
249 
250 		/*
251 		 * This stuff needs to be fixed correctly for the FFB
252 		 * devices and the UPA add-on devices.
253 		 */
254 		portid = ddi_prop_get_int(DDI_DEV_T_ANY, dev,
255 		    DDI_PROP_DONTPASS, "upa-portid", -1);
256 		if (portid != -1)
257 			(void) sprintf(p, "UPA 0x%x 0x%x%s",
258 			    portid,
259 			    rp->regspec_addr, (n > 1 ? "" : " ..."));
260 		else {
261 			portid = ddi_prop_get_int(DDI_DEV_T_ANY, dev,
262 			    DDI_PROP_DONTPASS, "portid", -1);
263 			nodeid = ddi_prop_get_int(DDI_DEV_T_ANY, dev,
264 			    DDI_PROP_DONTPASS, "nodeid", -1);
265 			if (portid == -1 && nodeid == -1)
266 				printf("could not find portid "
267 				    "or nodeid property in %s\n",
268 				    DEVI(dev)->devi_node_name);
269 
270 			if (portid != -1)
271 				(void) sprintf(p, "SAFARI 0x%x 0x%x%s",
272 				    portid,
273 				    rp->regspec_addr &
274 				    root_phys_addr_lo_mask,
275 				    (n > 1 ? "" : " ..."));
276 			if (nodeid != -1)
277 				(void) sprintf(p, "SSM Node %d", nodeid);
278 		}
279 		p += strlen(p);
280 	}
281 
282 	/*
283 	 * This is where we need to print out the interrupt specifications
284 	 * for the FFB device and any UPA add-on devices.  Not sure how to
285 	 * do this yet?
286 	 */
287 	cmn_err(CE_CONT, "?%s\n", buf);
288 	return (DDI_SUCCESS);
289 }
290 
291 int
292 rootnex_name_child_impl(dev_info_t *child, char *name, int namelen)
293 {
294 	struct ddi_parent_private_data *pdptr;
295 	int portid, nodeid;
296 	char *node_name;
297 	struct regspec *rp;
298 
299 	extern uint_t root_phys_addr_lo_mask;
300 	extern void make_ddi_ppd(
301 	    dev_info_t *, struct ddi_parent_private_data **);
302 
303 	/*
304 	 * Fill in parent-private data and this function returns to us
305 	 * an indication if it used "registers" to fill in the data.
306 	 */
307 	if (ddi_get_parent_data(child) == NULL) {
308 		make_ddi_ppd(child, &pdptr);
309 		ddi_set_parent_data(child, pdptr);
310 	}
311 
312 	/*
313 	 * No reg property, return null string as address (e.g. pseudo)
314 	 */
315 	name[0] = '\0';
316 	if (sparc_pd_getnreg(child) == 0) {
317 		return (DDI_SUCCESS);
318 	}
319 	rp = sparc_pd_getreg(child, 0);
320 	ASSERT(rp != NULL);
321 
322 	/*
323 	 * Create portid property for fhc node under root(/fhc).
324 	 */
325 	node_name = ddi_node_name(child);
326 	if ((strcmp(node_name, "fhc") == 0) ||
327 	    (strcmp(node_name, "mem-unit") == 0) ||
328 	    (strcmp(node_name, "central") == 0)) {
329 #ifdef  _STARFIRE
330 		portid = ((((rp->regspec_bustype) & 0x6) >> 1) |
331 		    (((rp->regspec_bustype) & 0xF0) >> 2) |
332 		    (((rp->regspec_bustype) & 0x8) << 3));
333 #else
334 		portid = (rp->regspec_bustype >> 1) & 0x1f;
335 #endif
336 
337 		/*
338 		 * The port-id must go on the hardware property list,
339 		 * otherwise, initchild may fail.
340 		 */
341 		if (ndi_prop_update_int(DDI_DEV_T_NONE, child, "upa-portid",
342 		    portid) != DDI_SUCCESS)
343 			cmn_err(CE_WARN,
344 			    "Error in creating upa-portid property for fhc.\n");
345 	}
346 
347 	/*
348 	 * Name node on behalf of child nexus.
349 	 */
350 	if (ddi_get_parent(child) != ddi_root_node()) {
351 		(void) snprintf(name, namelen, "%x,%x",
352 		    rp->regspec_bustype, rp->regspec_addr);
353 		return (DDI_SUCCESS);
354 	}
355 
356 	/*
357 	 * On sun4u, the 'name' of children of the root node
358 	 * is foo@<upa-mid>,<offset>, which is derived from,
359 	 * but not identical to the physical address.
360 	 */
361 	portid = ddi_prop_get_int(DDI_DEV_T_ANY, child,
362 	    DDI_PROP_DONTPASS, "upa-portid", -1);
363 	if (portid == -1)
364 		portid = ddi_prop_get_int(DDI_DEV_T_ANY, child,
365 		    DDI_PROP_DONTPASS, "portid", -1);
366 	nodeid = ddi_prop_get_int(DDI_DEV_T_ANY, child,
367 	    DDI_PROP_DONTPASS, "nodeid", -1);
368 
369 	/*
370 	 * Do not log message, to handle cases where OBP version
371 	 * does not have "portid" property for the root i2c node.
372 	 *
373 	 * Platforms supporting root i2c node (potentially without
374 	 * "portid" property) are :
375 	 *	SunBlade 1500, SunBlade 2500, V240, V250
376 	 */
377 	if (portid == -1 && nodeid == -1 &&
378 	    strncmp(node_name, "i2c", strlen("i2c")) != 0)
379 		cmn_err(CE_WARN,
380 		    "could not find portid or nodeid property in %s\n",
381 		    DEVI(child)->devi_node_name);
382 	if (nodeid != -1)
383 		(void) snprintf(name, namelen, "%x,0", nodeid);
384 	else
385 		(void) snprintf(name, namelen, "%x,%x", portid,
386 		    rp->regspec_addr & root_phys_addr_lo_mask);
387 
388 	return (DDI_SUCCESS);
389 }
390 
391 int
392 rootnex_ctl_initchild_impl(dev_info_t *dip)
393 {
394 	struct regspec *rp;
395 	struct ddi_parent_private_data *pd;
396 	char name[MAXNAMELEN];
397 
398 	extern struct ddi_parent_private_data *init_regspec_64(dev_info_t *dip);
399 
400 	/* Name the child */
401 	(void) rootnex_name_child(dip, name, MAXNAMELEN);
402 	ddi_set_name_addr(dip, name);
403 
404 	/*
405 	 * Try to merge .conf node. If merge is successful, return
406 	 * DDI_FAILURE to allow caller to remove this node.
407 	 */
408 	if (ndi_dev_is_persistent_node(dip) == 0 &&
409 	    (ndi_merge_node(dip, rootnex_name_child) == DDI_SUCCESS)) {
410 		(void) rootnex_ctl_uninitchild(dip);
411 		return (DDI_FAILURE);
412 	}
413 
414 	/*
415 	 * If there are no "reg"s in the child node, return.
416 	 */
417 	pd = init_regspec_64(dip);
418 	if ((pd == NULL) || (pd->par_nreg == 0))
419 		return (DDI_SUCCESS);
420 
421 	/*
422 	 * If this is a slave device sitting on the UPA, we assume that
423 	 * This device can accept DMA accesses from other devices.  We need
424 	 * to register this fact with the system by using the highest
425 	 * and lowest physical pfns of the devices register space.  This
426 	 * will then represent a physical block of addresses that are valid
427 	 * for DMA accesses.
428 	 */
429 	if ((ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "upa-portid",
430 	    -1) != -1) && ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
431 	    "upa-interrupt-slave", 0)) {
432 		pfn_t lopfn = (pfn_t)-1;
433 		pfn_t hipfn = 0;
434 		int i;
435 		extern void pf_set_dmacapable(pfn_t, pfn_t);
436 
437 		/* Scan the devices highest and lowest physical pfns */
438 		for (i = 0, rp = pd->par_reg; i < pd->par_nreg; i++, rp++) {
439 			uint64_t addr;
440 			pfn_t tmphipfn, tmplopfn;
441 
442 			addr = (unsigned long long)((unsigned long long)
443 			    rp->regspec_bustype << 32);
444 			addr |= (uint64_t)rp->regspec_addr;
445 			tmplopfn = (pfn_t)(addr >> MMU_PAGESHIFT);
446 			addr += (uint64_t)(rp->regspec_size - 1);
447 			tmphipfn = (pfn_t)(addr >> MMU_PAGESHIFT);
448 
449 			hipfn = (tmphipfn > hipfn) ? tmphipfn : hipfn;
450 			lopfn = (tmplopfn < lopfn) ? tmplopfn : lopfn;
451 		}
452 		pf_set_dmacapable(hipfn, lopfn);
453 	}
454 
455 	return (DDI_SUCCESS);
456 }
457 
458 void
459 rootnex_ctl_uninitchild_impl(dev_info_t *dip)
460 {
461 	if ((ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, "upa-portid",
462 	    -1) != -1) && (ddi_getprop(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
463 	    "upa-interrupt-slave", 0))) {
464 		struct regspec *rp;
465 		extern void pf_unset_dmacapable(pfn_t);
466 		unsigned long long addr;
467 		pfn_t pfn;
468 		struct ddi_parent_private_data *pd;
469 
470 		pd = ddi_get_parent_data(dip);
471 		ASSERT(pd != NULL);
472 		rp = pd->par_reg;
473 		addr = (unsigned long long) ((unsigned long long)
474 		    rp->regspec_bustype << 32);
475 		addr |= (unsigned long long) rp->regspec_addr;
476 		pfn = (pfn_t)(addr >> MMU_PAGESHIFT);
477 
478 		pf_unset_dmacapable(pfn);
479 	}
480 }
481