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 (the "License").
6 * You may not use this file except in compliance with the License.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 /*
29 * sun4u root nexus driver
30 */
31 #include <sys/conf.h>
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>
38
39 /* Useful debugging Stuff */
40 #include <sys/nexusdebug.h>
41 #define ROOTNEX_MAP_DEBUG 0x1
42 #define ROOTNEX_INTR_DEBUG 0x2
43
44 /*
45 * Extern declarations
46 */
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);
50
51 uint_t root_phys_addr_hi_mask = 0xffffffff;
52
53 /*
54 * config information
55 */
56 int
57 rootnex_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
58 ddi_intr_handle_impl_t *hdlp);
59
60 int
61 rootnex_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
62 ddi_intr_handle_impl_t *hdlp);
63
64 int
65 rootnex_get_intr_pri(dev_info_t *dip, dev_info_t *rdip,
66 ddi_intr_handle_impl_t *hdlp);
67
68 ddi_iblock_cookie_t rootnex_err_ibc;
69
70 /*
71 * rootnex_add_intr_impl:
72 */
73 /*ARGSUSED*/
74 int
rootnex_add_intr_impl(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)75 rootnex_add_intr_impl(dev_info_t *dip, dev_info_t *rdip,
76 ddi_intr_handle_impl_t *hdlp)
77 {
78 volatile uint64_t *intr_mapping_reg;
79 volatile uint64_t mondo_vector;
80 int32_t r_upaid = -1;
81 int32_t slave = 0;
82 int32_t portid;
83 int len, ret;
84
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) {
91
92 /* Give slave devices pri of 5. e.g. fb's */
93 hdlp->ih_pri = 5;
94 }
95
96 /*
97 * Translate the interrupt property by stuffing in the
98 * portid for those devices which have a portid.
99 */
100 hdlp->ih_vector |= (UPAID_TO_IGN(portid) << 6);
101 }
102
103 /*
104 * Hack to support the UPA slave devices before the 1275
105 * support for imap was introduced.
106 */
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) {
111 slave = 1;
112
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);
116
117 if ((intr_mapping_reg = get_intr_mapping_reg(
118 r_upaid, 1)) == NULL)
119 return (DDI_FAILURE);
120 } else
121 return (DDI_FAILURE);
122 }
123
124 if ((ret = i_ddi_add_ivintr(hdlp)) != DDI_SUCCESS)
125 return (ret);
126
127 /*
128 * Hack to support the UPA slave devices before the 1275
129 * support for imap was introduced.
130 */
131 if (slave) {
132 /*
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.
138 */
139 mondo_vector = cpu0.cpu_id << IMR_TID_SHIFT;
140 mondo_vector |= (IMR_VALID | (uint64_t)hdlp->ih_vector);
141
142 /* Set the mapping register */
143 *intr_mapping_reg = mondo_vector;
144
145 /* Flush write buffers */
146 mondo_vector = *intr_mapping_reg;
147 }
148
149 return (ret);
150 }
151
152 /*
153 * rootnex_remove_intr_impl:
154 */
155 /*ARGSUSED*/
156 int
rootnex_remove_intr_impl(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)157 rootnex_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip,
158 ddi_intr_handle_impl_t *hdlp)
159 {
160 int32_t portid;
161 int len;
162
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)) {
167 /*
168 * Translate the interrupt property by stuffing in the
169 * portid for those devices which have a portid.
170 */
171 hdlp->ih_vector |= (UPAID_TO_IGN(portid) << 6);
172 }
173
174 /*
175 * Hack to support the UPA slave devices before the 1275
176 * support for imap was introduced.
177 */
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;
182
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);
189
190 if ((intr_mapping_reg = get_intr_mapping_reg(
191 r_upaid, 1)) == NULL)
192 return (DDI_SUCCESS);
193
194 /* Clear the mapping register */
195 *intr_mapping_reg = 0x0ull;
196
197 /* Flush write buffers */
198 flush_data = *intr_mapping_reg;
199 #ifdef lint
200 flush_data = flush_data;
201 #endif /* lint */
202 }
203 }
204
205 i_ddi_rem_ivintr(hdlp);
206
207 return (DDI_SUCCESS);
208 }
209
210 /*
211 * rootnex_get_intr_pri:
212 */
213 /*ARGSUSED*/
214 int
rootnex_get_intr_pri(dev_info_t * dip,dev_info_t * rdip,ddi_intr_handle_impl_t * hdlp)215 rootnex_get_intr_pri(dev_info_t *dip, dev_info_t *rdip,
216 ddi_intr_handle_impl_t *hdlp)
217 {
218 int pri = hdlp->ih_pri;
219
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) {
224
225 /* Give slave devices pri of 5. e.g. fb's */
226 pri = 5;
227 }
228 }
229
230 return (pri);
231 }
232
233 int
rootnex_ctl_reportdev_impl(dev_info_t * dev)234 rootnex_ctl_reportdev_impl(dev_info_t *dev)
235 {
236 struct regspec *rp;
237 char buf[80];
238 char *p = buf;
239 register int n;
240 int portid;
241 int nodeid;
242
243 (void) sprintf(p, "%s%d at root", ddi_driver_name(dev),
244 ddi_get_instance(dev));
245 p += strlen(p);
246
247 if ((n = sparc_pd_getnreg(dev)) > 0) {
248 rp = sparc_pd_getreg(dev, 0);
249
250 (void) strcpy(p, ": ");
251 p += strlen(p);
252
253 /*
254 * This stuff needs to be fixed correctly for the FFB
255 * devices and the UPA add-on devices.
256 */
257 portid = ddi_prop_get_int(DDI_DEV_T_ANY, dev,
258 DDI_PROP_DONTPASS, "upa-portid", -1);
259 if (portid != -1)
260 (void) sprintf(p, "UPA 0x%x 0x%x%s",
261 portid,
262 rp->regspec_addr, (n > 1 ? "" : " ..."));
263 else {
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);
272
273 if (portid != -1)
274 (void) sprintf(p, "SAFARI 0x%x 0x%x%s",
275 portid,
276 rp->regspec_addr &
277 root_phys_addr_lo_mask,
278 (n > 1 ? "" : " ..."));
279 if (nodeid != -1)
280 (void) sprintf(p, "SSM Node %d", nodeid);
281 }
282 p += strlen(p);
283 }
284
285 /*
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
288 * do this yet?
289 */
290 cmn_err(CE_CONT, "?%s\n", buf);
291 return (DDI_SUCCESS);
292 }
293
294 int
rootnex_name_child_impl(dev_info_t * child,char * name,int namelen)295 rootnex_name_child_impl(dev_info_t *child, char *name, int namelen)
296 {
297 struct ddi_parent_private_data *pdptr;
298 int portid, nodeid;
299 char *node_name;
300 struct regspec *rp;
301
302 extern uint_t root_phys_addr_lo_mask;
303 extern void make_ddi_ppd(
304 dev_info_t *, struct ddi_parent_private_data **);
305
306 /*
307 * Fill in parent-private data and this function returns to us
308 * an indication if it used "registers" to fill in the data.
309 */
310 if (ddi_get_parent_data(child) == NULL) {
311 make_ddi_ppd(child, &pdptr);
312 ddi_set_parent_data(child, pdptr);
313 }
314
315 /*
316 * No reg property, return null string as address (e.g. pseudo)
317 */
318 name[0] = '\0';
319 if (sparc_pd_getnreg(child) == 0) {
320 return (DDI_SUCCESS);
321 }
322 rp = sparc_pd_getreg(child, 0);
323 ASSERT(rp != NULL);
324
325 /*
326 * Create portid property for fhc node under root(/fhc).
327 */
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)) {
332 #ifdef _STARFIRE
333 portid = ((((rp->regspec_bustype) & 0x6) >> 1) |
334 (((rp->regspec_bustype) & 0xF0) >> 2) |
335 (((rp->regspec_bustype) & 0x8) << 3));
336 #else
337 portid = (rp->regspec_bustype >> 1) & 0x1f;
338 #endif
339
340 /*
341 * The port-id must go on the hardware property list,
342 * otherwise, initchild may fail.
343 */
344 if (ndi_prop_update_int(DDI_DEV_T_NONE, child, "upa-portid",
345 portid) != DDI_SUCCESS)
346 cmn_err(CE_WARN,
347 "Error in creating upa-portid property for fhc.\n");
348 }
349
350 /*
351 * Name node on behalf of child nexus.
352 */
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);
357 }
358
359 /*
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.
363 */
364 portid = ddi_prop_get_int(DDI_DEV_T_ANY, child,
365 DDI_PROP_DONTPASS, "upa-portid", -1);
366 if (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);
371
372 /*
373 * Do not log message, to handle cases where OBP version
374 * does not have "portid" property for the root i2c node.
375 *
376 * Platforms supporting root i2c node (potentially without
377 * "portid" property) are :
378 * SunBlade 1500, SunBlade 2500, V240, V250
379 */
380 if (portid == -1 && nodeid == -1 &&
381 strncmp(node_name, "i2c", strlen("i2c")) != 0)
382 cmn_err(CE_WARN,
383 "could not find portid or nodeid property in %s\n",
384 DEVI(child)->devi_node_name);
385 if (nodeid != -1)
386 (void) snprintf(name, namelen, "%x,0", nodeid);
387 else
388 (void) snprintf(name, namelen, "%x,%x", portid,
389 rp->regspec_addr & root_phys_addr_lo_mask);
390
391 return (DDI_SUCCESS);
392 }
393
394 int
rootnex_ctl_initchild_impl(dev_info_t * dip)395 rootnex_ctl_initchild_impl(dev_info_t *dip)
396 {
397 struct regspec *rp;
398 struct ddi_parent_private_data *pd;
399 char name[MAXNAMELEN];
400
401 extern struct ddi_parent_private_data *init_regspec_64(dev_info_t *dip);
402
403 /* Name the child */
404 (void) rootnex_name_child(dip, name, MAXNAMELEN);
405 ddi_set_name_addr(dip, name);
406
407 /*
408 * Try to merge .conf node. If merge is successful, return
409 * DDI_FAILURE to allow caller to remove this node.
410 */
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);
415 }
416
417 /*
418 * If there are no "reg"s in the child node, return.
419 */
420 pd = init_regspec_64(dip);
421 if ((pd == NULL) || (pd->par_nreg == 0))
422 return (DDI_SUCCESS);
423
424 /*
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
430 * for DMA accesses.
431 */
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;
436 pfn_t hipfn = 0;
437 int i;
438 extern void pf_set_dmacapable(pfn_t, pfn_t);
439
440 /* Scan the devices highest and lowest physical pfns */
441 for (i = 0, rp = pd->par_reg; i < pd->par_nreg; i++, rp++) {
442 uint64_t addr;
443 pfn_t tmphipfn, tmplopfn;
444
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);
451
452 hipfn = (tmphipfn > hipfn) ? tmphipfn : hipfn;
453 lopfn = (tmplopfn < lopfn) ? tmplopfn : lopfn;
454 }
455 pf_set_dmacapable(hipfn, lopfn);
456 }
457
458 return (DDI_SUCCESS);
459 }
460
461 void
rootnex_ctl_uninitchild_impl(dev_info_t * dip)462 rootnex_ctl_uninitchild_impl(dev_info_t *dip)
463 {
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))) {
467 struct regspec *rp;
468 extern void pf_unset_dmacapable(pfn_t);
469 unsigned long long addr;
470 pfn_t pfn;
471 struct ddi_parent_private_data *pd;
472
473 pd = ddi_get_parent_data(dip);
474 ASSERT(pd != NULL);
475 rp = pd->par_reg;
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);
480
481 pf_unset_dmacapable(pfn);
482 }
483 }
484