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 void 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 ddi_ispec_t *ip = (ddi_ispec_t *)hdlp->ih_private; 80 volatile uint64_t *intr_mapping_reg; 81 volatile uint64_t mondo_vector; 82 int32_t r_upaid = -1; 83 int32_t slave = 0; 84 int32_t upa_portid; 85 int len, ret; 86 87 if ((upa_portid = ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 88 DDI_PROP_DONTPASS, "upa-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 ip->is_pil = 5; 94 hdlp->ih_pri = ip->is_pil; 95 } 96 97 /* 98 * Translate the interrupt property by stuffing in the 99 * portid for those devices which have a upa-portid. 100 */ 101 *ip->is_intr |= (UPAID_TO_IGN(upa_portid) << 6); 102 } 103 104 /* 105 * Hack to support the UPA slave devices before the 1275 106 * support for imap was introduced. 107 */ 108 if (ddi_getproplen(DDI_DEV_T_ANY, dip, NULL, "interrupt-map", 109 &len) != DDI_PROP_SUCCESS && ddi_getprop(DDI_DEV_T_ANY, 110 rdip, DDI_PROP_DONTPASS, "upa-interrupt-slave", 0) != 0 && 111 ddi_get_parent(rdip) == dip) { 112 slave = 1; 113 114 if ((r_upaid = ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 115 DDI_PROP_DONTPASS, "upa-portid", -1)) != -1) { 116 extern uint64_t *get_intr_mapping_reg(int, int); 117 118 if ((intr_mapping_reg = get_intr_mapping_reg( 119 r_upaid, 1)) == NULL) 120 return (DDI_FAILURE); 121 } else 122 return (DDI_FAILURE); 123 } 124 125 hdlp->ih_vector = *ip->is_intr; 126 if ((ret = i_ddi_add_ivintr(hdlp)) != DDI_SUCCESS) 127 return (ret); 128 129 /* 130 * Hack to support the UPA slave devices before the 1275 131 * support for imap was introduced. 132 */ 133 if (slave) { 134 /* 135 * Program the interrupt mapping register. 136 * Interrupts from the slave UPA devices are 137 * directed at the boot CPU until it is known 138 * that they can be safely redirected while 139 * running under load. 140 */ 141 mondo_vector = cpu0.cpu_id << IMR_TID_SHIFT; 142 mondo_vector |= (IMR_VALID | (uint64_t)*ip->is_intr); 143 144 /* Set the mapping register */ 145 *intr_mapping_reg = mondo_vector; 146 147 /* Flush write buffers */ 148 mondo_vector = *intr_mapping_reg; 149 } 150 151 return (ret); 152 } 153 154 /* 155 * rootnex_remove_intr_impl: 156 */ 157 /*ARGSUSED*/ 158 int 159 rootnex_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip, 160 ddi_intr_handle_impl_t *hdlp) 161 { 162 ddi_ispec_t *ip = (ddi_ispec_t *)hdlp->ih_private; 163 int32_t upa_portid; 164 int len; 165 166 if ((upa_portid = ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 167 DDI_PROP_DONTPASS, "upa-portid", -1)) != -1) { 168 /* 169 * Translate the interrupt property by stuffing in the 170 * portid for those devices which have a upa-portid. 171 */ 172 *ip->is_intr |= (UPAID_TO_IGN(upa_portid) << 6); 173 } 174 175 /* 176 * Hack to support the UPA slave devices before the 1275 177 * support for imap was introduced. 178 */ 179 if (ddi_getproplen(DDI_DEV_T_ANY, dip, NULL, "interrupt-map", 180 &len) != DDI_PROP_SUCCESS && ddi_getprop(DDI_DEV_T_ANY, 181 rdip, DDI_PROP_DONTPASS, "upa-interrupt-slave", 0) != 0) { 182 int32_t r_upaid = -1; 183 184 if ((r_upaid = ddi_prop_get_int(DDI_DEV_T_ANY, rdip, 185 DDI_PROP_DONTPASS, "upa-portid", -1)) != -1 && 186 ddi_get_parent(rdip) == dip) { 187 volatile uint64_t *intr_mapping_reg; 188 volatile uint64_t flush_data; 189 extern uint64_t *get_intr_mapping_reg(int, int); 190 191 if ((intr_mapping_reg = get_intr_mapping_reg( 192 r_upaid, 1)) == NULL) 193 return (DDI_SUCCESS); 194 195 /* Clear the mapping register */ 196 *intr_mapping_reg = 0x0ull; 197 198 /* Flush write buffers */ 199 flush_data = *intr_mapping_reg; 200 #ifdef lint 201 flush_data = flush_data; 202 #endif /* lint */ 203 } 204 } 205 206 hdlp->ih_vector = *ip->is_intr; 207 i_ddi_rem_ivintr(hdlp); 208 209 return (DDI_SUCCESS); 210 } 211 212 /* 213 * rootnex_get_intr_pri: 214 */ 215 /*ARGSUSED*/ 216 void 217 rootnex_get_intr_pri(dev_info_t *dip, dev_info_t *rdip, 218 ddi_intr_handle_impl_t *hdlp) 219 { 220 ddi_ispec_t *ip = (ddi_ispec_t *)hdlp->ih_private; 221 222 if (ddi_prop_get_int(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 223 "upa-portid", -1) != -1) { 224 if (ddi_getprop(DDI_DEV_T_ANY, rdip, DDI_PROP_DONTPASS, 225 "upa-interrupt-slave", 0) != 0) { 226 227 /* Give slave devices pri of 5. e.g. fb's */ 228 ip->is_pil = 5; 229 } 230 } 231 } 232 233 int 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 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 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 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