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