4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #pragma ident "%Z%%M% %I% %E% SMI"
29 * sun4u root nexus driver
32 #include <sys/cpuvar.h>
33 #include <sys/sysiosbus.h>
34 #include <sys/intreg.h>
35 #include <sys/ddi_subrdefs.h>
36 #include <sys/sunndi.h>
37 #include <sys/async.h>
39 /* Useful debugging Stuff */
40 #include <sys/nexusdebug.h>
41 #define ROOTNEX_MAP_DEBUG 0x1
42 #define ROOTNEX_INTR_DEBUG 0x2
47 extern uint_t root_phys_addr_lo_mask
;
48 extern int rootnex_name_child(dev_info_t
*child
, char *name
, int namelen
);
49 extern int rootnex_ctl_uninitchild(dev_info_t
*dip
);
51 uint_t root_phys_addr_hi_mask
= 0xffffffff;
57 rootnex_add_intr_impl(dev_info_t
*dip
, dev_info_t
*rdip
,
58 ddi_intr_handle_impl_t
*hdlp
);
61 rootnex_remove_intr_impl(dev_info_t
*dip
, dev_info_t
*rdip
,
62 ddi_intr_handle_impl_t
*hdlp
);
65 rootnex_get_intr_pri(dev_info_t
*dip
, dev_info_t
*rdip
,
66 ddi_intr_handle_impl_t
*hdlp
);
68 ddi_iblock_cookie_t rootnex_err_ibc
;
71 * rootnex_add_intr_impl:
75 rootnex_add_intr_impl(dev_info_t
*dip
, dev_info_t
*rdip
,
76 ddi_intr_handle_impl_t
*hdlp
)
78 volatile uint64_t *intr_mapping_reg
;
79 volatile uint64_t mondo_vector
;
85 if (((portid
= ddi_prop_get_int(DDI_DEV_T_ANY
, rdip
,
86 DDI_PROP_DONTPASS
, "upa-portid", -1)) != -1) ||
87 ((portid
= ddi_prop_get_int(DDI_DEV_T_ANY
, rdip
,
88 DDI_PROP_DONTPASS
, "portid", -1)) != -1)) {
89 if (ddi_getprop(DDI_DEV_T_ANY
, rdip
, DDI_PROP_DONTPASS
,
90 "upa-interrupt-slave", 0) != 0) {
92 /* Give slave devices pri of 5. e.g. fb's */
97 * Translate the interrupt property by stuffing in the
98 * portid for those devices which have a portid.
100 hdlp
->ih_vector
|= (UPAID_TO_IGN(portid
) << 6);
104 * Hack to support the UPA slave devices before the 1275
105 * support for imap was introduced.
107 if (ddi_getproplen(DDI_DEV_T_ANY
, dip
, NULL
, "interrupt-map",
108 &len
) != DDI_PROP_SUCCESS
&& ddi_getprop(DDI_DEV_T_ANY
,
109 rdip
, DDI_PROP_DONTPASS
, "upa-interrupt-slave", 0) != 0 &&
110 ddi_get_parent(rdip
) == dip
) {
113 if ((r_upaid
= ddi_prop_get_int(DDI_DEV_T_ANY
, rdip
,
114 DDI_PROP_DONTPASS
, "upa-portid", -1)) != -1) {
115 extern uint64_t *get_intr_mapping_reg(int, int);
117 if ((intr_mapping_reg
= get_intr_mapping_reg(
118 r_upaid
, 1)) == NULL
)
119 return (DDI_FAILURE
);
121 return (DDI_FAILURE
);
124 if ((ret
= i_ddi_add_ivintr(hdlp
)) != DDI_SUCCESS
)
128 * Hack to support the UPA slave devices before the 1275
129 * support for imap was introduced.
133 * Program the interrupt mapping register.
134 * Interrupts from the slave UPA devices are
135 * directed at the boot CPU until it is known
136 * that they can be safely redirected while
137 * running under load.
139 mondo_vector
= cpu0
.cpu_id
<< IMR_TID_SHIFT
;
140 mondo_vector
|= (IMR_VALID
| (uint64_t)hdlp
->ih_vector
);
142 /* Set the mapping register */
143 *intr_mapping_reg
= mondo_vector
;
145 /* Flush write buffers */
146 mondo_vector
= *intr_mapping_reg
;
153 * rootnex_remove_intr_impl:
157 rootnex_remove_intr_impl(dev_info_t
*dip
, dev_info_t
*rdip
,
158 ddi_intr_handle_impl_t
*hdlp
)
163 if (((portid
= ddi_prop_get_int(DDI_DEV_T_ANY
, rdip
,
164 DDI_PROP_DONTPASS
, "upa-portid", -1)) != -1) ||
165 ((portid
= ddi_prop_get_int(DDI_DEV_T_ANY
, rdip
,
166 DDI_PROP_DONTPASS
, "portid", -1)) != -1)) {
168 * Translate the interrupt property by stuffing in the
169 * portid for those devices which have a portid.
171 hdlp
->ih_vector
|= (UPAID_TO_IGN(portid
) << 6);
175 * Hack to support the UPA slave devices before the 1275
176 * support for imap was introduced.
178 if (ddi_getproplen(DDI_DEV_T_ANY
, dip
, NULL
, "interrupt-map",
179 &len
) != DDI_PROP_SUCCESS
&& ddi_getprop(DDI_DEV_T_ANY
,
180 rdip
, DDI_PROP_DONTPASS
, "upa-interrupt-slave", 0) != 0) {
181 int32_t r_upaid
= -1;
183 if ((r_upaid
= ddi_prop_get_int(DDI_DEV_T_ANY
, rdip
,
184 DDI_PROP_DONTPASS
, "upa-portid", -1)) != -1 &&
185 ddi_get_parent(rdip
) == dip
) {
186 volatile uint64_t *intr_mapping_reg
;
187 volatile uint64_t flush_data
;
188 extern uint64_t *get_intr_mapping_reg(int, int);
190 if ((intr_mapping_reg
= get_intr_mapping_reg(
191 r_upaid
, 1)) == NULL
)
192 return (DDI_SUCCESS
);
194 /* Clear the mapping register */
195 *intr_mapping_reg
= 0x0ull
;
197 /* Flush write buffers */
198 flush_data
= *intr_mapping_reg
;
200 flush_data
= flush_data
;
205 i_ddi_rem_ivintr(hdlp
);
207 return (DDI_SUCCESS
);
211 * rootnex_get_intr_pri:
215 rootnex_get_intr_pri(dev_info_t
*dip
, dev_info_t
*rdip
,
216 ddi_intr_handle_impl_t
*hdlp
)
218 int pri
= hdlp
->ih_pri
;
220 if (ddi_prop_get_int(DDI_DEV_T_ANY
, rdip
, DDI_PROP_DONTPASS
,
221 "upa-portid", -1) != -1) {
222 if (ddi_getprop(DDI_DEV_T_ANY
, rdip
, DDI_PROP_DONTPASS
,
223 "upa-interrupt-slave", 0) != 0) {
225 /* Give slave devices pri of 5. e.g. fb's */
234 rootnex_ctl_reportdev_impl(dev_info_t
*dev
)
243 (void) sprintf(p
, "%s%d at root", ddi_driver_name(dev
),
244 ddi_get_instance(dev
));
247 if ((n
= sparc_pd_getnreg(dev
)) > 0) {
248 rp
= sparc_pd_getreg(dev
, 0);
250 (void) strcpy(p
, ": ");
254 * This stuff needs to be fixed correctly for the FFB
255 * devices and the UPA add-on devices.
257 portid
= ddi_prop_get_int(DDI_DEV_T_ANY
, dev
,
258 DDI_PROP_DONTPASS
, "upa-portid", -1);
260 (void) sprintf(p
, "UPA 0x%x 0x%x%s",
262 rp
->regspec_addr
, (n
> 1 ? "" : " ..."));
264 portid
= ddi_prop_get_int(DDI_DEV_T_ANY
, dev
,
265 DDI_PROP_DONTPASS
, "portid", -1);
266 nodeid
= ddi_prop_get_int(DDI_DEV_T_ANY
, dev
,
267 DDI_PROP_DONTPASS
, "nodeid", -1);
268 if (portid
== -1 && nodeid
== -1)
269 printf("could not find portid "
270 "or nodeid property in %s\n",
271 DEVI(dev
)->devi_node_name
);
274 (void) sprintf(p
, "SAFARI 0x%x 0x%x%s",
277 root_phys_addr_lo_mask
,
278 (n
> 1 ? "" : " ..."));
280 (void) sprintf(p
, "SSM Node %d", nodeid
);
286 * This is where we need to print out the interrupt specifications
287 * for the FFB device and any UPA add-on devices. Not sure how to
290 cmn_err(CE_CONT
, "?%s\n", buf
);
291 return (DDI_SUCCESS
);
295 rootnex_name_child_impl(dev_info_t
*child
, char *name
, int namelen
)
297 struct ddi_parent_private_data
*pdptr
;
302 extern uint_t root_phys_addr_lo_mask
;
303 extern void make_ddi_ppd(
304 dev_info_t
*, struct ddi_parent_private_data
**);
307 * Fill in parent-private data and this function returns to us
308 * an indication if it used "registers" to fill in the data.
310 if (ddi_get_parent_data(child
) == NULL
) {
311 make_ddi_ppd(child
, &pdptr
);
312 ddi_set_parent_data(child
, pdptr
);
316 * No reg property, return null string as address (e.g. pseudo)
319 if (sparc_pd_getnreg(child
) == 0) {
320 return (DDI_SUCCESS
);
322 rp
= sparc_pd_getreg(child
, 0);
326 * Create portid property for fhc node under root(/fhc).
328 node_name
= ddi_node_name(child
);
329 if ((strcmp(node_name
, "fhc") == 0) ||
330 (strcmp(node_name
, "mem-unit") == 0) ||
331 (strcmp(node_name
, "central") == 0)) {
333 portid
= ((((rp
->regspec_bustype
) & 0x6) >> 1) |
334 (((rp
->regspec_bustype
) & 0xF0) >> 2) |
335 (((rp
->regspec_bustype
) & 0x8) << 3));
337 portid
= (rp
->regspec_bustype
>> 1) & 0x1f;
341 * The port-id must go on the hardware property list,
342 * otherwise, initchild may fail.
344 if (ndi_prop_update_int(DDI_DEV_T_NONE
, child
, "upa-portid",
345 portid
) != DDI_SUCCESS
)
347 "Error in creating upa-portid property for fhc.\n");
351 * Name node on behalf of child nexus.
353 if (ddi_get_parent(child
) != ddi_root_node()) {
354 (void) snprintf(name
, namelen
, "%x,%x",
355 rp
->regspec_bustype
, rp
->regspec_addr
);
356 return (DDI_SUCCESS
);
360 * On sun4u, the 'name' of children of the root node
361 * is foo@<upa-mid>,<offset>, which is derived from,
362 * but not identical to the physical address.
364 portid
= ddi_prop_get_int(DDI_DEV_T_ANY
, child
,
365 DDI_PROP_DONTPASS
, "upa-portid", -1);
367 portid
= ddi_prop_get_int(DDI_DEV_T_ANY
, child
,
368 DDI_PROP_DONTPASS
, "portid", -1);
369 nodeid
= ddi_prop_get_int(DDI_DEV_T_ANY
, child
,
370 DDI_PROP_DONTPASS
, "nodeid", -1);
373 * Do not log message, to handle cases where OBP version
374 * does not have "portid" property for the root i2c node.
376 * Platforms supporting root i2c node (potentially without
377 * "portid" property) are :
378 * SunBlade 1500, SunBlade 2500, V240, V250
380 if (portid
== -1 && nodeid
== -1 &&
381 strncmp(node_name
, "i2c", strlen("i2c")) != 0)
383 "could not find portid or nodeid property in %s\n",
384 DEVI(child
)->devi_node_name
);
386 (void) snprintf(name
, namelen
, "%x,0", nodeid
);
388 (void) snprintf(name
, namelen
, "%x,%x", portid
,
389 rp
->regspec_addr
& root_phys_addr_lo_mask
);
391 return (DDI_SUCCESS
);
395 rootnex_ctl_initchild_impl(dev_info_t
*dip
)
398 struct ddi_parent_private_data
*pd
;
399 char name
[MAXNAMELEN
];
401 extern struct ddi_parent_private_data
*init_regspec_64(dev_info_t
*dip
);
404 (void) rootnex_name_child(dip
, name
, MAXNAMELEN
);
405 ddi_set_name_addr(dip
, name
);
408 * Try to merge .conf node. If merge is successful, return
409 * DDI_FAILURE to allow caller to remove this node.
411 if (ndi_dev_is_persistent_node(dip
) == 0 &&
412 (ndi_merge_node(dip
, rootnex_name_child
) == DDI_SUCCESS
)) {
413 (void) rootnex_ctl_uninitchild(dip
);
414 return (DDI_FAILURE
);
418 * If there are no "reg"s in the child node, return.
420 pd
= init_regspec_64(dip
);
421 if ((pd
== NULL
) || (pd
->par_nreg
== 0))
422 return (DDI_SUCCESS
);
425 * If this is a slave device sitting on the UPA, we assume that
426 * This device can accept DMA accesses from other devices. We need
427 * to register this fact with the system by using the highest
428 * and lowest physical pfns of the devices register space. This
429 * will then represent a physical block of addresses that are valid
432 if ((ddi_getprop(DDI_DEV_T_ANY
, dip
, DDI_PROP_DONTPASS
, "upa-portid",
433 -1) != -1) && ddi_getprop(DDI_DEV_T_ANY
, dip
, DDI_PROP_DONTPASS
,
434 "upa-interrupt-slave", 0)) {
435 pfn_t lopfn
= (pfn_t
)-1;
438 extern void pf_set_dmacapable(pfn_t
, pfn_t
);
440 /* Scan the devices highest and lowest physical pfns */
441 for (i
= 0, rp
= pd
->par_reg
; i
< pd
->par_nreg
; i
++, rp
++) {
443 pfn_t tmphipfn
, tmplopfn
;
445 addr
= (unsigned long long)((unsigned long long)
446 rp
->regspec_bustype
<< 32);
447 addr
|= (uint64_t)rp
->regspec_addr
;
448 tmplopfn
= (pfn_t
)(addr
>> MMU_PAGESHIFT
);
449 addr
+= (uint64_t)(rp
->regspec_size
- 1);
450 tmphipfn
= (pfn_t
)(addr
>> MMU_PAGESHIFT
);
452 hipfn
= (tmphipfn
> hipfn
) ? tmphipfn
: hipfn
;
453 lopfn
= (tmplopfn
< lopfn
) ? tmplopfn
: lopfn
;
455 pf_set_dmacapable(hipfn
, lopfn
);
458 return (DDI_SUCCESS
);
462 rootnex_ctl_uninitchild_impl(dev_info_t
*dip
)
464 if ((ddi_getprop(DDI_DEV_T_ANY
, dip
, DDI_PROP_DONTPASS
, "upa-portid",
465 -1) != -1) && (ddi_getprop(DDI_DEV_T_ANY
, dip
, DDI_PROP_DONTPASS
,
466 "upa-interrupt-slave", 0))) {
468 extern void pf_unset_dmacapable(pfn_t
);
469 unsigned long long addr
;
471 struct ddi_parent_private_data
*pd
;
473 pd
= ddi_get_parent_data(dip
);
476 addr
= (unsigned long long) ((unsigned long long)
477 rp
->regspec_bustype
<< 32);
478 addr
|= (unsigned long long) rp
->regspec_addr
;
479 pfn
= (pfn_t
)(addr
>> MMU_PAGESHIFT
);
481 pf_unset_dmacapable(pfn
);