1 /* 2 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 /* 7 * This file may contain confidential information of Nvidia 8 * and should not be distributed in source form without approval 9 * from Sun Legal. 10 */ 11 12 #pragma ident "%Z%%M% %I% %E% SMI" 13 14 #include "nge.h" 15 16 #undef NGE_DBG 17 #define NGE_DBG NGE_DBG_NDD 18 19 static char transfer_speed_propname[] = "transfer-speed"; 20 static char speed_propname[] = "speed"; 21 static char duplex_propname[] = "full-duplex"; 22 23 /* 24 * Notes: 25 * The first character of the <name> field encodes the read/write 26 * status of the parameter: 27 * '=' => read-only, 28 * '-' => read-only and forced to 0 on serdes 29 * '+' => read/write, 30 * '?' => read/write on copper, read-only and 0 on serdes 31 * '!' => invisible! 32 * 33 * For writable parameters, we check for a driver property with the 34 * same name; if found, and its value is in range, we initialise 35 * the parameter from the property, overriding the default in the 36 * table below. 37 * 38 * A NULL in the <name> field terminates the array. 39 * 40 * The <info> field is used here to provide the index of the 41 * parameter to be initialised; thus it doesn't matter whether 42 * this table is kept ordered or not. 43 * 44 * The <info> field in the per-instance copy, on the other hand, 45 * is used to count assignments so that we can tell when a magic 46 * parameter has been set via ndd (see nge_param_set()). 47 */ 48 static const nd_param_t nd_template[] = { 49 /* info min max init r/w+name */ 50 51 /* Our hardware capabilities */ 52 { PARAM_AUTONEG_CAP, 0, 1, 1, "=autoneg_cap" }, 53 { PARAM_PAUSE_CAP, 0, 1, 1, "=pause_cap" }, 54 { PARAM_ASYM_PAUSE_CAP, 0, 1, 1, "=asym_pause_cap" }, 55 { PARAM_1000FDX_CAP, 0, 1, 1, "=1000fdx_cap" }, 56 { PARAM_1000HDX_CAP, 0, 1, 0, "=1000hdx_cap" }, 57 { PARAM_100T4_CAP, 0, 1, 0, "=100T4_cap" }, 58 { PARAM_100FDX_CAP, 0, 1, 1, "-100fdx_cap" }, 59 { PARAM_100HDX_CAP, 0, 1, 1, "-100hdx_cap" }, 60 { PARAM_10FDX_CAP, 0, 1, 1, "-10fdx_cap" }, 61 { PARAM_10HDX_CAP, 0, 1, 1, "-10hdx_cap" }, 62 63 /* Our advertised capabilities */ 64 { PARAM_ADV_AUTONEG_CAP, 0, 1, 1, "+adv_autoneg_cap" }, 65 { PARAM_ADV_PAUSE_CAP, 0, 1, 1, "+adv_pause_cap" }, 66 { PARAM_ADV_ASYM_PAUSE_CAP, 0, 1, 1, "+adv_asym_pause_cap" }, 67 { PARAM_ADV_1000FDX_CAP, 0, 1, 1, "+adv_1000fdx_cap" }, 68 { PARAM_ADV_1000HDX_CAP, 0, 1, 0, "=adv_1000hdx_cap" }, 69 { PARAM_ADV_100T4_CAP, 0, 1, 0, "=adv_100T4_cap" }, 70 { PARAM_ADV_100FDX_CAP, 0, 1, 1, "?adv_100fdx_cap" }, 71 { PARAM_ADV_100HDX_CAP, 0, 1, 1, "?adv_100hdx_cap" }, 72 { PARAM_ADV_10FDX_CAP, 0, 1, 1, "?adv_10fdx_cap" }, 73 { PARAM_ADV_10HDX_CAP, 0, 1, 1, "?adv_10hdx_cap" }, 74 75 /* Partner's advertised capabilities */ 76 { PARAM_LP_AUTONEG_CAP, 0, 1, 0, "-lp_autoneg_cap" }, 77 { PARAM_LP_PAUSE_CAP, 0, 1, 0, "-lp_pause_cap" }, 78 { PARAM_LP_ASYM_PAUSE_CAP, 0, 1, 0, "-lp_asym_pause_cap" }, 79 { PARAM_LP_1000FDX_CAP, 0, 1, 0, "-lp_1000fdx_cap" }, 80 { PARAM_LP_1000HDX_CAP, 0, 1, 0, "-lp_1000hdx_cap" }, 81 { PARAM_LP_100T4_CAP, 0, 1, 0, "-lp_100T4_cap" }, 82 { PARAM_LP_100FDX_CAP, 0, 1, 0, "-lp_100fdx_cap" }, 83 { PARAM_LP_100HDX_CAP, 0, 1, 0, "-lp_100hdx_cap" }, 84 { PARAM_LP_10FDX_CAP, 0, 1, 0, "-lp_10fdx_cap" }, 85 { PARAM_LP_10HDX_CAP, 0, 1, 0, "-lp_10hdx_cap" }, 86 87 /* Current operating modes */ 88 { PARAM_LINK_STATUS, 0, 1, 0, "-link_status" }, 89 { PARAM_LINK_SPEED, 0, 1000, 0, "-link_speed" }, 90 { PARAM_LINK_DUPLEX, -1, 1, -1, "-link_duplex" }, 91 92 { PARAM_LINK_AUTONEG, 0, 1, 0, "-link_autoneg" }, 93 { PARAM_LINK_RX_PAUSE, 0, 1, 0, "-link_rx_pause" }, 94 { PARAM_LINK_TX_PAUSE, 0, 1, 0, "-link_tx_pause" }, 95 96 /* Loopback status */ 97 { PARAM_LOOP_MODE, 0, 5, 0, "-loop_mode" }, 98 99 /* TX Bcopy threshold */ 100 { PARAM_TXBCOPY_THRESHOLD, 0, NGE_MAX_SDU, NGE_TX_COPY_SIZE, 101 "+tx_bcopy_threshold" }, 102 103 /* RX Bcopy threshold */ 104 { PARAM_RXBCOPY_THRESHOLD, 0, NGE_MAX_SDU, NGE_RX_COPY_SIZE, 105 "+rx_bcopy_threshold" }, 106 107 /* Max packet received per interrupt */ 108 { PARAM_RECV_MAX_PACKET, 0, NGE_RECV_SLOTS_DESC_1024, 32, 109 "+recv_max_packet" }, 110 /* Terminator */ 111 { PARAM_COUNT, 0, 0, 0, NULL } 112 }; 113 114 115 /* ============== NDD Support Functions =============== */ 116 117 /* 118 * Extracts the value from the nge parameter array and prints 119 * the parameter value. cp points to the required parameter. 120 */ 121 static int 122 nge_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *credp) 123 { 124 nd_param_t *ndp; 125 126 _NOTE(ARGUNUSED(q, credp)) 127 ndp = (nd_param_t *)cp; 128 (void) mi_mpprintf(mp, "%d", ndp->ndp_val); 129 130 return (0); 131 } 132 133 /* 134 * Validates the request to set a NGE parameter to a specific value. 135 * If the request is OK, the parameter is set. Also the <info> field 136 * is incremented to show that the parameter was touched, even though 137 * it may have been set to the same value it already had. 138 */ 139 static int 140 nge_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *credp) 141 { 142 nd_param_t *ndp; 143 long new_value; 144 char *end; 145 146 _NOTE(ARGUNUSED(q, mp, credp)) 147 ndp = (nd_param_t *)cp; 148 new_value = mi_strtol(value, &end, 10); 149 if (end == value) 150 return (EINVAL); 151 if (new_value < ndp->ndp_min || new_value > ndp->ndp_max) 152 return (EINVAL); 153 154 ndp->ndp_val = new_value; 155 ndp->ndp_info += 1; 156 return (0); 157 } 158 159 /* 160 * Initialise the per-instance parameter array from the global prototype, 161 * and register each element with the named dispatch handler using nd_load() 162 */ 163 static int 164 nge_param_register(nge_t *ngep) 165 { 166 const nd_param_t *tmplp; 167 dev_info_t *dip; 168 nd_param_t *ndp; 169 caddr_t *nddpp; 170 pfi_t setfn; 171 char *nm; 172 int pval; 173 174 dip = ngep->devinfo; 175 nddpp = &ngep->nd_data_p; 176 ASSERT(*nddpp == NULL); 177 178 NGE_TRACE(("nge_param_register($%p)", (void *)ngep)); 179 180 for (tmplp = nd_template; tmplp->ndp_name != NULL; ++tmplp) { 181 /* 182 * Copy the template from nd_template[] into the 183 * proper slot in the per-instance parameters, 184 * then register the parameter with nd_load() 185 */ 186 ndp = &ngep->nd_params[tmplp->ndp_info]; 187 *ndp = *tmplp; 188 nm = &ndp->ndp_name[0]; 189 setfn = nge_param_set; 190 switch (*nm) { 191 default: 192 case '!': 193 continue; 194 195 case '+': 196 case '?': 197 break; 198 199 case '=': 200 case '-': 201 setfn = NULL; 202 break; 203 } 204 205 if (!nd_load(nddpp, ++nm, nge_param_get, setfn, (caddr_t)ndp)) 206 goto nd_fail; 207 208 /* 209 * If the parameter is writable, and there's a property 210 * with the same name, and its value is in range, we use 211 * it to initialise the parameter. If it exists but is 212 * out of range, it's ignored. 213 */ 214 if (setfn && NGE_PROP_EXISTS(dip, nm)) { 215 pval = NGE_PROP_GET_INT(dip, nm); 216 if (pval >= ndp->ndp_min && pval <= ndp->ndp_max) 217 ndp->ndp_val = pval; 218 } 219 } 220 return (DDI_SUCCESS); 221 222 nd_fail: 223 nd_free(nddpp); 224 return (DDI_FAILURE); 225 } 226 227 int 228 nge_nd_init(nge_t *ngep) 229 { 230 int duplex; 231 int speed; 232 dev_info_t *dip; 233 234 NGE_TRACE(("nge_nd_init($%p)", (void *)ngep)); 235 /* 236 * Register all the per-instance properties, initialising 237 * them from the table above or from driver properties set 238 * in the .conf file 239 */ 240 if (nge_param_register(ngep) != DDI_SUCCESS) 241 return (-1); 242 243 /* 244 * The link speed may be forced to 10, 100 or 1000 Mbps using 245 * the property "transfer-speed". This may be done in OBP by 246 * using the command "apply transfer-speed=<speed> <device>". 247 * The speed may be 10, 100 or 1000 - any other value will be 248 * ignored. Note that this does *enables* autonegotiation, but 249 * restricts it to the speed specified by the property. 250 */ 251 dip = ngep->devinfo; 252 if (NGE_PROP_EXISTS(dip, transfer_speed_propname)) { 253 254 speed = NGE_PROP_GET_INT(dip, transfer_speed_propname); 255 nge_log(ngep, "%s property is %d", 256 transfer_speed_propname, speed); 257 258 switch (speed) { 259 case 1000: 260 ngep->param_adv_autoneg = 1; 261 ngep->param_adv_1000fdx = 1; 262 ngep->param_adv_1000hdx = 0; 263 ngep->param_adv_100fdx = 0; 264 ngep->param_adv_100hdx = 0; 265 ngep->param_adv_10fdx = 0; 266 ngep->param_adv_10hdx = 0; 267 break; 268 269 case 100: 270 ngep->param_adv_autoneg = 1; 271 ngep->param_adv_1000fdx = 0; 272 ngep->param_adv_1000hdx = 0; 273 ngep->param_adv_100fdx = 1; 274 ngep->param_adv_100hdx = 1; 275 ngep->param_adv_10fdx = 0; 276 ngep->param_adv_10hdx = 0; 277 break; 278 279 case 10: 280 ngep->param_adv_autoneg = 1; 281 ngep->param_adv_1000fdx = 0; 282 ngep->param_adv_1000hdx = 0; 283 ngep->param_adv_100fdx = 0; 284 ngep->param_adv_100hdx = 0; 285 ngep->param_adv_10fdx = 1; 286 ngep->param_adv_10hdx = 1; 287 break; 288 289 default: 290 break; 291 } 292 } 293 294 /* 295 * Also check the "speed" and "full-duplex" properties. Setting 296 * these properties will override all other settings and *disable* 297 * autonegotiation, so both should be specified if either one is. 298 * Otherwise, the unspecified parameter will be set to a default 299 * value (1000Mb/s, full-duplex). 300 */ 301 if (NGE_PROP_EXISTS(dip, speed_propname) || 302 NGE_PROP_EXISTS(dip, duplex_propname)) { 303 304 ngep->param_adv_autoneg = 0; 305 ngep->param_adv_1000fdx = 1; 306 ngep->param_adv_1000hdx = 0; 307 ngep->param_adv_100fdx = 1; 308 ngep->param_adv_100hdx = 1; 309 ngep->param_adv_10fdx = 1; 310 ngep->param_adv_10hdx = 1; 311 312 speed = NGE_PROP_GET_INT(dip, speed_propname); 313 duplex = NGE_PROP_GET_INT(dip, duplex_propname); 314 nge_log(ngep, "%s property is %d", 315 speed_propname, speed); 316 nge_log(ngep, "%s property is %d", 317 duplex_propname, duplex); 318 319 switch (speed) { 320 case 1000: 321 default: 322 ngep->param_adv_100fdx = 0; 323 ngep->param_adv_100hdx = 0; 324 ngep->param_adv_10fdx = 0; 325 ngep->param_adv_10hdx = 0; 326 break; 327 328 case 100: 329 ngep->param_adv_1000fdx = 0; 330 ngep->param_adv_1000hdx = 0; 331 ngep->param_adv_10fdx = 0; 332 ngep->param_adv_10hdx = 0; 333 break; 334 335 case 10: 336 ngep->param_adv_1000fdx = 0; 337 ngep->param_adv_1000hdx = 0; 338 ngep->param_adv_100fdx = 0; 339 ngep->param_adv_100hdx = 0; 340 break; 341 } 342 343 switch (duplex) { 344 default: 345 case 1: 346 ngep->param_adv_1000hdx = 0; 347 ngep->param_adv_100hdx = 0; 348 ngep->param_adv_10hdx = 0; 349 break; 350 351 case 0: 352 ngep->param_adv_1000fdx = 0; 353 ngep->param_adv_100fdx = 0; 354 ngep->param_adv_10fdx = 0; 355 break; 356 } 357 } 358 359 return (0); 360 } 361 362 enum ioc_reply 363 nge_nd_ioctl(nge_t *ngep, queue_t *wq, mblk_t *mp, struct iocblk *iocp) 364 { 365 boolean_t ok; 366 int cmd; 367 NGE_TRACE(("nge_nd_ioctl($%p, $%p, $%p, $%p)", 368 (void *)ngep, (void *)wq, (void *)mp, (void *)iocp)); 369 370 ASSERT(mutex_owned(ngep->genlock)); 371 372 cmd = iocp->ioc_cmd; 373 switch (cmd) { 374 default: 375 nge_error(ngep, "nge_nd_ioctl: invalid cmd 0x%x", cmd); 376 return (IOC_INVAL); 377 378 case ND_GET: 379 /* 380 * If nd_getset() returns B_FALSE, the command was 381 * not valid (e.g. unknown name), so we just tell the 382 * top-level ioctl code to send a NAK (with code EINVAL). 383 * 384 * Otherwise, nd_getset() will have built the reply to 385 * be sent (but not actually sent it), so we tell the 386 * caller to send the prepared reply. 387 */ 388 ok = nd_getset(wq, ngep->nd_data_p, mp); 389 return (ok ? IOC_REPLY : IOC_INVAL); 390 391 case ND_SET: 392 /* 393 * All adv_* parameters are locked (read-only) while 394 * the device is in any sort of loopback mode ... 395 */ 396 if (ngep->param_loop_mode != NGE_LOOP_NONE) { 397 iocp->ioc_error = EBUSY; 398 return (IOC_INVAL); 399 } 400 401 ok = nd_getset(wq, ngep->nd_data_p, mp); 402 403 /* 404 * If nd_getset() returns B_FALSE, the command was 405 * not valid (e.g. unknown name), so we just tell 406 * the top-level ioctl code to send a NAK (with code 407 * EINVAL by default). 408 * 409 * Otherwise, nd_getset() will have built the reply to 410 * be sent - but that doesn't imply success! In some 411 * cases, the reply it's built will have a non-zero 412 * error code in it (e.g. EPERM if not superuser). 413 * So, we also drop out in that case ... 414 */ 415 if (!ok) 416 return (IOC_INVAL); 417 if (iocp->ioc_error) 418 return (IOC_REPLY); 419 420 /* 421 * OK, a successful 'set'. Return IOC_RESTART_REPLY, 422 * telling the top-level ioctl code to update the PHY 423 * and restart the chip before sending our prepared reply 424 */ 425 return (IOC_RESTART_REPLY); 426 } 427 } 428 429 /* Free the Named Dispatch Table by calling nd_free */ 430 void 431 nge_nd_cleanup(nge_t *ngep) 432 { 433 NGE_TRACE(("nge_nd_cleanup($%p)", (void *)ngep)); 434 nd_free(&ngep->nd_data_p); 435 } 436