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