xref: /illumos-gate/usr/src/uts/common/io/nge/nge_ndd.c (revision bea83d026ee1bd1b2a2419e1d0232f107a5d7d9b)
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 2007 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  * Validates the request to set a NGE parameter to a specific value.
170  * If the request is OK, the parameter is set.  Also the <info> field
171  * is incremented to show that the parameter was touched, even though
172  * it may have been set to the same value it already had.
173  */
174 static int
175 nge_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *credp)
176 {
177 	nd_param_t *ndp;
178 	long new_value;
179 	char *end;
180 
181 	_NOTE(ARGUNUSED(q, mp, credp))
182 	ndp = (nd_param_t *)cp;
183 	new_value = mi_strtol(value, &end, 10);
184 	if (end == value)
185 		return (EINVAL);
186 	if (new_value < ndp->ndp_min || new_value > ndp->ndp_max)
187 		return (EINVAL);
188 
189 	ndp->ndp_val = new_value;
190 	ndp->ndp_info += 1;
191 	return (0);
192 }
193 
194 /*
195  * Initialise the per-instance parameter array from the global prototype,
196  * and register each element with the named dispatch handler using nd_load()
197  */
198 static int
199 nge_param_register(nge_t *ngep)
200 {
201 	const nd_param_t *tmplp;
202 	dev_info_t *dip;
203 	nd_param_t *ndp;
204 	caddr_t *nddpp;
205 	pfi_t setfn;
206 	char *nm;
207 	int pval;
208 
209 	dip = ngep->devinfo;
210 	nddpp = &ngep->nd_data_p;
211 	ASSERT(*nddpp == NULL);
212 
213 	NGE_TRACE(("nge_param_register($%p)", (void *)ngep));
214 
215 	for (tmplp = nd_template; tmplp->ndp_name != NULL; ++tmplp) {
216 		/*
217 		 * Copy the template from nd_template[] into the
218 		 * proper slot in the per-instance parameters,
219 		 * then register the parameter with nd_load()
220 		 */
221 		ndp = &ngep->nd_params[tmplp->ndp_info];
222 		*ndp = *tmplp;
223 		nm = &ndp->ndp_name[0];
224 		setfn = nge_param_set;
225 		switch (*nm) {
226 		default:
227 		case '!':
228 			continue;
229 
230 		case '+':
231 		case '?':
232 			break;
233 
234 		case '=':
235 		case '-':
236 			setfn = NULL;
237 			break;
238 		}
239 
240 		if (!nd_load(nddpp, ++nm, nge_param_get, setfn, (caddr_t)ndp))
241 			goto nd_fail;
242 
243 		/*
244 		 * If the parameter is writable, and there's a property
245 		 * with the same name, and its value is in range, we use
246 		 * it to initialise the parameter.  If it exists but is
247 		 * out of range, it's ignored.
248 		 */
249 		if (setfn && NGE_PROP_EXISTS(dip, nm)) {
250 			pval = NGE_PROP_GET_INT(dip, nm);
251 			if (pval >= ndp->ndp_min && pval <= ndp->ndp_max)
252 				ndp->ndp_val = pval;
253 		}
254 	}
255 	return (DDI_SUCCESS);
256 
257 nd_fail:
258 	nd_free(nddpp);
259 	return (DDI_FAILURE);
260 }
261 
262 int
263 nge_nd_init(nge_t *ngep)
264 {
265 	int duplex;
266 	int speed;
267 	dev_info_t *dip;
268 
269 	NGE_TRACE(("nge_nd_init($%p)", (void *)ngep));
270 	/*
271 	 * Register all the per-instance properties, initialising
272 	 * them from the table above or from driver properties set
273 	 * in the .conf file
274 	 */
275 	if (nge_param_register(ngep) != DDI_SUCCESS)
276 		return (-1);
277 
278 	/*
279 	 * The link speed may be forced to 10, 100 or 1000 Mbps using
280 	 * the property "transfer-speed". This may be done in OBP by
281 	 * using the command "apply transfer-speed=<speed> <device>".
282 	 * The speed may be 10, 100 or 1000 - any other value will be
283 	 * ignored.  Note that this does *enables* autonegotiation, but
284 	 * restricts it to the speed specified by the property.
285 	 */
286 	dip = ngep->devinfo;
287 	if (NGE_PROP_EXISTS(dip, transfer_speed_propname)) {
288 
289 		speed = NGE_PROP_GET_INT(dip, transfer_speed_propname);
290 		nge_log(ngep, "%s property is %d",
291 		    transfer_speed_propname, speed);
292 
293 		switch (speed) {
294 		case 1000:
295 			ngep->param_adv_autoneg = 1;
296 			ngep->param_adv_1000fdx = 1;
297 			ngep->param_adv_1000hdx = 0;
298 			ngep->param_adv_100fdx = 0;
299 			ngep->param_adv_100hdx = 0;
300 			ngep->param_adv_10fdx = 0;
301 			ngep->param_adv_10hdx = 0;
302 			break;
303 
304 		case 100:
305 			ngep->param_adv_autoneg = 1;
306 			ngep->param_adv_1000fdx = 0;
307 			ngep->param_adv_1000hdx = 0;
308 			ngep->param_adv_100fdx = 1;
309 			ngep->param_adv_100hdx = 1;
310 			ngep->param_adv_10fdx = 0;
311 			ngep->param_adv_10hdx = 0;
312 			break;
313 
314 		case 10:
315 			ngep->param_adv_autoneg = 1;
316 			ngep->param_adv_1000fdx = 0;
317 			ngep->param_adv_1000hdx = 0;
318 			ngep->param_adv_100fdx = 0;
319 			ngep->param_adv_100hdx = 0;
320 			ngep->param_adv_10fdx = 1;
321 			ngep->param_adv_10hdx = 1;
322 			break;
323 
324 		default:
325 			break;
326 		}
327 	}
328 
329 	/*
330 	 * Also check the "speed" and "full-duplex" properties.  Setting
331 	 * these properties will override all other settings and *disable*
332 	 * autonegotiation, so both should be specified if either one is.
333 	 * Otherwise, the unspecified parameter will be set to a default
334 	 * value (1000Mb/s, full-duplex).
335 	 */
336 	if (NGE_PROP_EXISTS(dip, speed_propname) ||
337 	    NGE_PROP_EXISTS(dip, duplex_propname)) {
338 
339 		ngep->param_adv_autoneg = 0;
340 		ngep->param_adv_1000fdx = 1;
341 		ngep->param_adv_1000hdx = 0;
342 		ngep->param_adv_100fdx = 1;
343 		ngep->param_adv_100hdx = 1;
344 		ngep->param_adv_10fdx = 1;
345 		ngep->param_adv_10hdx = 1;
346 
347 		speed = NGE_PROP_GET_INT(dip, speed_propname);
348 		duplex = NGE_PROP_GET_INT(dip, duplex_propname);
349 		nge_log(ngep, "%s property is %d",
350 		    speed_propname, speed);
351 		nge_log(ngep, "%s property is %d",
352 		    duplex_propname, duplex);
353 
354 		switch (speed) {
355 		case 1000:
356 		default:
357 			ngep->param_adv_100fdx = 0;
358 			ngep->param_adv_100hdx = 0;
359 			ngep->param_adv_10fdx = 0;
360 			ngep->param_adv_10hdx = 0;
361 			break;
362 
363 		case 100:
364 			ngep->param_adv_1000fdx = 0;
365 			ngep->param_adv_1000hdx = 0;
366 			ngep->param_adv_10fdx = 0;
367 			ngep->param_adv_10hdx = 0;
368 			break;
369 
370 		case 10:
371 			ngep->param_adv_1000fdx = 0;
372 			ngep->param_adv_1000hdx = 0;
373 			ngep->param_adv_100fdx = 0;
374 			ngep->param_adv_100hdx = 0;
375 			break;
376 		}
377 
378 		switch (duplex) {
379 		default:
380 		case 1:
381 			ngep->param_adv_1000hdx = 0;
382 			ngep->param_adv_100hdx = 0;
383 			ngep->param_adv_10hdx = 0;
384 			break;
385 
386 		case 0:
387 			ngep->param_adv_1000fdx = 0;
388 			ngep->param_adv_100fdx = 0;
389 			ngep->param_adv_10fdx = 0;
390 			break;
391 		}
392 	}
393 
394 	return (0);
395 }
396 
397 enum ioc_reply
398 nge_nd_ioctl(nge_t *ngep, queue_t *wq, mblk_t *mp, struct iocblk *iocp)
399 {
400 	boolean_t ok;
401 	int cmd;
402 	NGE_TRACE(("nge_nd_ioctl($%p, $%p, $%p, $%p)",
403 	    (void *)ngep, (void *)wq, (void *)mp, (void *)iocp));
404 
405 	ASSERT(mutex_owned(ngep->genlock));
406 
407 	cmd = iocp->ioc_cmd;
408 	switch (cmd) {
409 	default:
410 		nge_error(ngep, "nge_nd_ioctl: invalid cmd 0x%x", cmd);
411 		return (IOC_INVAL);
412 
413 	case ND_GET:
414 		/*
415 		 * If nd_getset() returns B_FALSE, the command was
416 		 * not valid (e.g. unknown name), so we just tell the
417 		 * top-level ioctl code to send a NAK (with code EINVAL).
418 		 *
419 		 * Otherwise, nd_getset() will have built the reply to
420 		 * be sent (but not actually sent it), so we tell the
421 		 * caller to send the prepared reply.
422 		 */
423 		ok = nd_getset(wq, ngep->nd_data_p, mp);
424 		return (ok ? IOC_REPLY : IOC_INVAL);
425 
426 	case ND_SET:
427 		/*
428 		 * All adv_* parameters are locked (read-only) while
429 		 * the device is in any sort of loopback mode ...
430 		 */
431 		if (ngep->param_loop_mode != NGE_LOOP_NONE) {
432 			iocp->ioc_error = EBUSY;
433 			return (IOC_INVAL);
434 		}
435 
436 		ok = nd_getset(wq, ngep->nd_data_p, mp);
437 
438 		/*
439 		 * If nd_getset() returns B_FALSE, the command was
440 		 * not valid (e.g. unknown name), so we just tell
441 		 * the top-level ioctl code to send a NAK (with code
442 		 * EINVAL by default).
443 		 *
444 		 * Otherwise, nd_getset() will have built the reply to
445 		 * be sent - but that doesn't imply success!  In some
446 		 * cases, the reply it's built will have a non-zero
447 		 * error code in it (e.g. EPERM if not superuser).
448 		 * So, we also drop out in that case ...
449 		 */
450 		if (!ok)
451 			return (IOC_INVAL);
452 		if (iocp->ioc_error)
453 			return (IOC_REPLY);
454 
455 		/*
456 		 * OK, a successful 'set'.  Return IOC_RESTART_REPLY,
457 		 * telling the top-level ioctl code to update the PHY
458 		 * and restart the chip before sending our prepared reply
459 		 */
460 		return (IOC_RESTART_REPLY);
461 	}
462 }
463 
464 /* Free the Named Dispatch Table by calling nd_free */
465 void
466 nge_nd_cleanup(nge_t *ngep)
467 {
468 	NGE_TRACE(("nge_nd_cleanup($%p)", (void *)ngep));
469 	nd_free(&ngep->nd_data_p);
470 }
471