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