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