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 * sun4v root nexus driver 31 */ 32 #include <sys/conf.h> 33 #include <sys/cpuvar.h> 34 #include <sys/ivintr.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 extern uint_t root_phys_addr_lo_mask; 46 extern int rootnex_name_child(dev_info_t *child, char *name, int namelen); 47 extern int rootnex_ctl_uninitchild(dev_info_t *dip); 48 49 uint_t root_phys_addr_hi_mask = 0xfffffff; 50 51 #define BUS_ADDRTYPE_CONFIG 0xc 52 53 #define BUSTYPE_TO_ADDRTYPE(bustype) ((bustype >> 28) & 0xf) 54 55 static char *bus_addrtype[16] = { 56 "m", NULL, NULL, NULL, NULL, NULL, NULL, NULL, 57 "i", NULL, NULL, NULL, "", NULL, NULL, NULL 58 }; 59 60 /* 61 * config information 62 */ 63 int 64 rootnex_add_intr_impl(dev_info_t *dip, dev_info_t *rdip, 65 ddi_intr_handle_impl_t *hdlp); 66 67 int 68 rootnex_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip, 69 ddi_intr_handle_impl_t *hdlp); 70 71 int 72 rootnex_get_intr_pri(dev_info_t *dip, dev_info_t *rdip, 73 ddi_intr_handle_impl_t *hdlp); 74 75 ddi_iblock_cookie_t rootnex_err_ibc; 76 77 /* 78 * rootnex_add_intr_impl: 79 */ 80 /*ARGSUSED*/ 81 int 82 rootnex_add_intr_impl(dev_info_t *dip, dev_info_t *rdip, 83 ddi_intr_handle_impl_t *hdlp) 84 { 85 return (i_ddi_add_ivintr(hdlp)); 86 } 87 88 /* 89 * rootnex_remove_intr_impl: 90 */ 91 /*ARGSUSED*/ 92 int 93 rootnex_remove_intr_impl(dev_info_t *dip, dev_info_t *rdip, 94 ddi_intr_handle_impl_t *hdlp) 95 { 96 i_ddi_rem_ivintr(hdlp); 97 98 return (DDI_SUCCESS); 99 } 100 101 /* 102 * rootnex_get_intr_pri: 103 */ 104 /*ARGSUSED*/ 105 int 106 rootnex_get_intr_pri(dev_info_t *dip, dev_info_t *rdip, 107 ddi_intr_handle_impl_t *hdlp) 108 { 109 return (hdlp->ih_pri); 110 } 111 112 int 113 rootnex_ctl_reportdev_impl(dev_info_t *dev) 114 { 115 struct regspec *rp; 116 char buf[80]; 117 char *p = buf; 118 119 (void) sprintf(p, "%s%d at root", ddi_driver_name(dev), 120 ddi_get_instance(dev)); 121 p += strlen(p); 122 123 if (sparc_pd_getnreg(dev) > 0) { 124 rp = sparc_pd_getreg(dev, 0); 125 126 (void) strcpy(p, ": "); 127 p += strlen(p); 128 129 (void) snprintf(p, sizeof (buf) - (buf - p), 130 "0x%x 0x%x", rp->regspec_bustype & root_phys_addr_hi_mask, 131 rp->regspec_addr & root_phys_addr_lo_mask); 132 133 p += strlen(p); 134 } 135 136 /* 137 * This is where we need to print out the interrupt specifications 138 * for the FFB device and any UPA add-on devices. Not sure how to 139 * do this yet? 140 */ 141 cmn_err(CE_CONT, "?%s\n", buf); 142 return (DDI_SUCCESS); 143 } 144 145 int 146 rootnex_name_child_impl(dev_info_t *child, char *name, int namelen) 147 { 148 struct ddi_parent_private_data *pdptr; 149 uint_t addrtype; 150 uint_t addrlow; 151 struct regspec *rp; 152 153 extern uint_t root_phys_addr_lo_mask; 154 extern void make_ddi_ppd( 155 dev_info_t *, struct ddi_parent_private_data **); 156 157 /* 158 * Fill in parent-private data and this function returns to us 159 * an indication if it used "registers" to fill in the data. 160 */ 161 if (ddi_get_parent_data(child) == NULL) { 162 make_ddi_ppd(child, &pdptr); 163 ddi_set_parent_data(child, pdptr); 164 } 165 166 /* 167 * No reg property, return null string as address (e.g. pseudo) 168 */ 169 name[0] = '\0'; 170 if (sparc_pd_getnreg(child) == 0) { 171 return (DDI_SUCCESS); 172 } 173 rp = sparc_pd_getreg(child, 0); 174 ASSERT(rp != NULL); 175 176 /* 177 * Name node on behalf of child nexus. 178 */ 179 if (ddi_get_parent(child) != ddi_root_node()) { 180 (void) snprintf(name, namelen, "%x,%x", 181 rp->regspec_bustype, rp->regspec_addr); 182 return (DDI_SUCCESS); 183 } 184 185 /* 186 * On sun4v, the 'name' of a root node device depends upon 187 * the first reg property, which contains two 32-bit address 188 * cells as follows: 189 * 190 * bit# 63-32: ssss.hhhh.hhhh.hhhh.hhhh.hhhh.hhhh.hhhh 191 * bit# 31-00: llll.llll.llll.llll.llll.llll.llll.llll 192 * 193 * where 'ssss' defines the address type and naming convention 194 * as follows: 195 * 196 * 0000 -> cacheable address space 197 * foo@m<high-addr>[,<low-addr>] 198 * 1000 -> non-cacheable IO address space 199 * foo@i<high-addr>[,<low-addr>] 200 * 1100 -> non-cacheable configuration address space 201 * foo@<high-addr> 202 * 203 * where <hish-addr> is hex-ASCII reprensation of the hh...hh 204 * bits and <low-addr> is hex-ASCII represenation of the 205 * ll...ll bits. 206 * 207 * Note that the leading zeros are omitted here. Also, if the 208 * <low-addr> bits are zero, then the trailing component is 209 * omitted as well. 210 */ 211 addrtype = BUSTYPE_TO_ADDRTYPE(rp->regspec_bustype); 212 addrlow = rp->regspec_addr & root_phys_addr_lo_mask; 213 if (bus_addrtype[addrtype] == NULL) 214 cmn_err(CE_PANIC, "rootnex: wrong bustype: %x child: %s\n", 215 rp->regspec_bustype, ddi_node_name(child)); 216 else if (addrtype == BUS_ADDRTYPE_CONFIG || addrlow == 0) 217 (void) snprintf(name, namelen, "%s%x", 218 bus_addrtype[addrtype], 219 rp->regspec_bustype & root_phys_addr_hi_mask); 220 else 221 (void) snprintf(name, namelen, "%s%x,%x", 222 bus_addrtype[addrtype], 223 rp->regspec_bustype & root_phys_addr_hi_mask, addrlow); 224 225 return (DDI_SUCCESS); 226 } 227 228 229 int 230 rootnex_ctl_initchild_impl(dev_info_t *dip) 231 { 232 struct ddi_parent_private_data *pd; 233 char name[MAXNAMELEN]; 234 235 extern struct ddi_parent_private_data *init_regspec_64(dev_info_t *dip); 236 237 /* Name the child */ 238 (void) rootnex_name_child(dip, name, MAXNAMELEN); 239 ddi_set_name_addr(dip, name); 240 241 /* 242 * Try to merge .conf node. If merge is successful, return 243 * DDI_FAILURE to allow caller to remove this node. 244 */ 245 if (ndi_dev_is_persistent_node(dip) == 0 && 246 (ndi_merge_node(dip, rootnex_name_child) == DDI_SUCCESS)) { 247 (void) rootnex_ctl_uninitchild(dip); 248 return (DDI_FAILURE); 249 } 250 251 /* 252 * If there are no "reg"s in the child node, return. 253 */ 254 pd = init_regspec_64(dip); 255 if ((pd == NULL) || (pd->par_nreg == 0)) 256 return (DDI_SUCCESS); 257 258 return (DDI_SUCCESS); 259 } 260 261 /*ARGSUSED*/ 262 void 263 rootnex_ctl_uninitchild_impl(dev_info_t *dip) 264 { 265 } 266