xref: /titanic_44/usr/src/uts/common/io/nge/nge_ndd.c (revision 2a9459bdd821c1cf59590a7a9069ac9c591e8a6b)
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