xref: /illumos-gate/usr/src/uts/common/io/cxgbe/t4nex/t4_mac.c (revision 2974b68dd4245e041051c854ef990e47c8271e71)
1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source. A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * This file is part of the Chelsio T4 support code.
14  *
15  * Copyright (C) 2010-2013 Chelsio Communications.  All rights reserved.
16  *
17  * This program is distributed in the hope that it will be useful, but WITHOUT
18  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19  * FITNESS FOR A PARTICULAR PURPOSE.  See the LICENSE file included in this
20  * release for licensing terms and conditions.
21  */
22 
23 #include <sys/ddi.h>
24 #include <sys/sunddi.h>
25 #include <sys/dlpi.h>
26 #include <sys/mac_provider.h>
27 #include <sys/mac_ether.h>
28 #include <sys/strsubr.h>
29 #include <sys/queue.h>
30 
31 #include "common/common.h"
32 #include "common/t4_regs.h"
33 
34 static int t4_mc_getstat(void *arg, uint_t stat, uint64_t *val);
35 static int t4_mc_start(void *arg);
36 static void t4_mc_stop(void *arg);
37 static int t4_mc_setpromisc(void *arg, boolean_t on);
38 static int t4_mc_multicst(void *arg, boolean_t add, const uint8_t *mcaddr);
39 static int t4_mc_unicst(void *arg, const uint8_t *ucaddr);
40 static mblk_t *t4_mc_tx(void *arg, mblk_t *m);
41 static boolean_t t4_mc_getcapab(void *arg, mac_capab_t cap, void *data);
42 static int t4_mc_setprop(void *arg, const char *name, mac_prop_id_t id,
43     uint_t size, const void *val);
44 static int t4_mc_getprop(void *arg, const char *name, mac_prop_id_t id,
45     uint_t size, void *val);
46 static void t4_mc_propinfo(void *arg, const char *name, mac_prop_id_t id,
47     mac_prop_info_handle_t ph);
48 
49 static int begin_synchronized_op(struct port_info *pi, int hold, int waitok);
50 static void end_synchronized_op(struct port_info *pi, int held);
51 static int t4_init_synchronized(struct port_info *pi);
52 static int t4_uninit_synchronized(struct port_info *pi);
53 static void propinfo(struct port_info *pi, const char *name,
54     mac_prop_info_handle_t ph);
55 static int getprop(struct port_info *pi, const char *name, uint_t size,
56     void *val);
57 static int setprop(struct port_info *pi, const char *name, const void *val);
58 
59 mac_callbacks_t t4_m_callbacks = {
60 	.mc_callbacks	= MC_GETCAPAB | MC_PROPERTIES,
61 	.mc_getstat	= t4_mc_getstat,
62 	.mc_start	= t4_mc_start,
63 	.mc_stop	= t4_mc_stop,
64 	.mc_setpromisc	= t4_mc_setpromisc,
65 	.mc_multicst	= t4_mc_multicst,
66 	.mc_unicst	= t4_mc_unicst,
67 	.mc_tx		= t4_mc_tx,
68 	.mc_getcapab	= t4_mc_getcapab,
69 	.mc_setprop	= t4_mc_setprop,
70 	.mc_getprop	= t4_mc_getprop,
71 	.mc_propinfo	= t4_mc_propinfo,
72 };
73 
74 #define	T4PROP_TMR_IDX "_holdoff_timer_idx"
75 #define	T4PROP_PKTC_IDX "_holdoff_pktc_idx"
76 #define	T4PROP_MTU "_mtu"
77 #define	T4PROP_HW_CSUM	"_hw_csum"
78 #define	T4PROP_HW_LSO	"_hw_lso"
79 #define	T4PROP_TX_PAUSE	"_tx_pause"
80 #define	T4PROP_RX_PAUSE	"_rx_pause"
81 
82 char *t4_priv_props[] = {
83 	T4PROP_TMR_IDX,
84 	T4PROP_PKTC_IDX,
85 #if MAC_VERSION == 1
86 	/* MAC_VERSION 1 doesn't seem to use MAC_PROP_MTU, hmmmm */
87 	T4PROP_MTU,
88 #endif
89 	T4PROP_HW_CSUM,
90 	T4PROP_HW_LSO,
91 	T4PROP_TX_PAUSE,
92 	T4PROP_RX_PAUSE,
93 	NULL
94 };
95 
96 static int
97 t4_mc_getstat(void *arg, uint_t stat, uint64_t *val)
98 {
99 	struct port_info *pi = arg;
100 	struct adapter *sc = pi->adapter;
101 	struct link_config *lc = &pi->link_cfg;
102 
103 #define	GET_STAT(name) \
104 	t4_read_reg64(sc, PORT_REG(pi->tx_chan, A_MPS_PORT_STAT_##name##_L))
105 
106 	switch (stat) {
107 	case MAC_STAT_IFSPEED:
108 		if (lc->link_ok != 0) {
109 			*val = lc->speed;
110 			*val *= 1000000;
111 		} else
112 			*val = 0;
113 		break;
114 
115 	case MAC_STAT_MULTIRCV:
116 		*val = GET_STAT(RX_PORT_MCAST);
117 		break;
118 
119 	case MAC_STAT_BRDCSTRCV:
120 		*val = GET_STAT(RX_PORT_BCAST);
121 		break;
122 
123 	case MAC_STAT_MULTIXMT:
124 		*val = GET_STAT(TX_PORT_MCAST);
125 		break;
126 
127 	case MAC_STAT_BRDCSTXMT:
128 		*val = GET_STAT(TX_PORT_BCAST);
129 		break;
130 
131 	case MAC_STAT_NORCVBUF:
132 		*val = 0;	/* TODO should come from rxq->nomem */
133 		break;
134 
135 	case MAC_STAT_IERRORS:
136 		*val = GET_STAT(RX_PORT_MTU_ERROR) +
137 		    GET_STAT(RX_PORT_MTU_CRC_ERROR) +
138 		    GET_STAT(RX_PORT_CRC_ERROR) +
139 		    GET_STAT(RX_PORT_LEN_ERROR) +
140 		    GET_STAT(RX_PORT_SYM_ERROR) +
141 		    GET_STAT(RX_PORT_LESS_64B);
142 		break;
143 
144 	case MAC_STAT_UNKNOWNS:
145 		return (ENOTSUP);
146 
147 	case MAC_STAT_NOXMTBUF:
148 		*val = GET_STAT(TX_PORT_DROP);
149 		break;
150 
151 	case MAC_STAT_OERRORS:
152 		*val = GET_STAT(TX_PORT_ERROR);
153 		break;
154 
155 	case MAC_STAT_COLLISIONS:
156 		return (ENOTSUP);
157 
158 	case MAC_STAT_RBYTES:
159 		*val = GET_STAT(RX_PORT_BYTES);
160 		break;
161 
162 	case MAC_STAT_IPACKETS:
163 		*val = GET_STAT(RX_PORT_FRAMES);
164 		break;
165 
166 	case MAC_STAT_OBYTES:
167 		*val = GET_STAT(TX_PORT_BYTES);
168 		break;
169 
170 	case MAC_STAT_OPACKETS:
171 		*val = GET_STAT(TX_PORT_FRAMES);
172 		break;
173 
174 	case ETHER_STAT_ALIGN_ERRORS:
175 		return (ENOTSUP);
176 
177 	case ETHER_STAT_FCS_ERRORS:
178 		*val = GET_STAT(RX_PORT_CRC_ERROR);
179 		break;
180 
181 	case ETHER_STAT_FIRST_COLLISIONS:
182 	case ETHER_STAT_MULTI_COLLISIONS:
183 	case ETHER_STAT_SQE_ERRORS:
184 	case ETHER_STAT_DEFER_XMTS:
185 	case ETHER_STAT_TX_LATE_COLLISIONS:
186 	case ETHER_STAT_EX_COLLISIONS:
187 		return (ENOTSUP);
188 
189 	case ETHER_STAT_MACXMT_ERRORS:
190 		*val = GET_STAT(TX_PORT_ERROR);
191 		break;
192 
193 	case ETHER_STAT_CARRIER_ERRORS:
194 		return (ENOTSUP);
195 
196 	case ETHER_STAT_TOOLONG_ERRORS:
197 		*val = GET_STAT(RX_PORT_MTU_ERROR);
198 		break;
199 
200 	case ETHER_STAT_MACRCV_ERRORS:
201 		*val = GET_STAT(RX_PORT_MTU_ERROR) +
202 		    GET_STAT(RX_PORT_MTU_CRC_ERROR) +
203 		    GET_STAT(RX_PORT_CRC_ERROR) +
204 		    GET_STAT(RX_PORT_LEN_ERROR) +
205 		    GET_STAT(RX_PORT_SYM_ERROR) +
206 		    GET_STAT(RX_PORT_LESS_64B);
207 		break;
208 
209 	case ETHER_STAT_XCVR_ADDR:
210 	case ETHER_STAT_XCVR_ID:
211 	case ETHER_STAT_XCVR_INUSE:
212 		return (ENOTSUP);
213 
214 	case ETHER_STAT_CAP_1000FDX:
215 		*val = !!(lc->supported & FW_PORT_CAP_SPEED_1G);
216 		break;
217 
218 	case ETHER_STAT_CAP_1000HDX:
219 		return (ENOTSUP);
220 
221 	case ETHER_STAT_CAP_100FDX:
222 		*val = !!(lc->supported & FW_PORT_CAP_SPEED_100M);
223 		break;
224 
225 	case ETHER_STAT_CAP_100HDX:
226 		return (ENOTSUP);
227 
228 	case ETHER_STAT_CAP_10FDX:
229 	case ETHER_STAT_CAP_10HDX:
230 		return (ENOTSUP);
231 
232 	case ETHER_STAT_CAP_ASMPAUSE:
233 		*val = 0;
234 		break;
235 
236 	case ETHER_STAT_CAP_PAUSE:
237 		*val = 1;
238 		break;
239 
240 	case ETHER_STAT_CAP_AUTONEG:
241 		*val = !!(lc->supported & FW_PORT_CAP_ANEG);
242 		break;
243 
244 	/*
245 	 * We have set flow control configuration based on tx_pause and rx_pause
246 	 * values supported through ndd. Now, we need to translate the settings
247 	 * we have in link_config structure to adv_cap_asmpause and
248 	 * adv_cap_pause.
249 	 *
250 	 * There are 4 combinations possible and the translation is as below:
251 	 * tx_pause = 0 => We don't send pause frames during Rx congestion
252 	 * tx_pause = 1 => We send pause frames during Rx congestion
253 	 * rx_pause = 0 => We ignore received pause frames
254 	 * rx_pause = 1 => We pause transmission when we receive pause frames
255 	 *
256 	 * +----------------------------+----------------------------------+
257 	 * |  tx_pause	|    rx_pause	| adv_cap_asmpause | adv_cap_pause |
258 	 * +-------------------------+-------------------------------------+
259 	 * |	0	|	0	|	0	   |	0	   |
260 	 * |	0	|	1	|	1	   |	0	   |
261 	 * |	1	|	0	|	1	   |	1	   |
262 	 * |	1	|	1	|	0	   |	1	   |
263 	 * +----------------------------+----------------------------------+
264 	 */
265 
266 	/* Advertised asymmetric pause capability */
267 	case ETHER_STAT_ADV_CAP_ASMPAUSE:
268 		*val = (((lc->requested_fc & PAUSE_TX) ? 1 : 0) ^
269 		    (lc->requested_fc & PAUSE_RX));
270 		break;
271 
272 	/* Advertised pause capability */
273 	case ETHER_STAT_ADV_CAP_PAUSE:
274 		*val = (lc->requested_fc & PAUSE_TX) ? 1 : 0;
275 		break;
276 
277 	case ETHER_STAT_ADV_CAP_1000FDX:
278 	case ETHER_STAT_ADV_CAP_1000HDX:
279 	case ETHER_STAT_ADV_CAP_100FDX:
280 	case ETHER_STAT_ADV_CAP_100HDX:
281 	case ETHER_STAT_ADV_CAP_10FDX:
282 	case ETHER_STAT_ADV_CAP_10HDX:
283 	case ETHER_STAT_ADV_CAP_AUTONEG:
284 		return (ENOTSUP);	/* TODO */
285 
286 	case ETHER_STAT_LP_CAP_1000FDX:
287 	case ETHER_STAT_LP_CAP_1000HDX:
288 	case ETHER_STAT_LP_CAP_100FDX:
289 	case ETHER_STAT_LP_CAP_100HDX:
290 	case ETHER_STAT_LP_CAP_10FDX:
291 	case ETHER_STAT_LP_CAP_10HDX:
292 	case ETHER_STAT_LP_CAP_ASMPAUSE:
293 	case ETHER_STAT_LP_CAP_PAUSE:
294 	case ETHER_STAT_LP_CAP_AUTONEG:
295 		return (ENOTSUP);
296 
297 	case ETHER_STAT_LINK_ASMPAUSE:
298 		*val = 0;
299 		break;
300 
301 	case ETHER_STAT_LINK_PAUSE:
302 		*val = 1;
303 		break;
304 
305 	case ETHER_STAT_LINK_AUTONEG:
306 		*val = lc->autoneg == AUTONEG_ENABLE;
307 		break;
308 
309 	case ETHER_STAT_LINK_DUPLEX:
310 		if (lc->link_ok != 0)
311 			*val = LINK_DUPLEX_FULL;
312 		else
313 			*val = LINK_DUPLEX_UNKNOWN;
314 		break;
315 
316 	default:
317 #ifdef DEBUG
318 		cxgb_printf(pi->dip, CE_NOTE, "stat %d not implemented.", stat);
319 #endif
320 		return (ENOTSUP);
321 	}
322 #undef GET_STAT
323 
324 	return (0);
325 }
326 
327 static int
328 t4_mc_start(void *arg)
329 {
330 	struct port_info *pi = arg;
331 	int rc;
332 
333 	rc = begin_synchronized_op(pi, 0, 1);
334 	if (rc != 0)
335 		return (rc);
336 	rc = t4_init_synchronized(pi);
337 	end_synchronized_op(pi, 0);
338 
339 	return (rc);
340 }
341 
342 static void
343 t4_mc_stop(void *arg)
344 {
345 	struct port_info *pi = arg;
346 
347 	while (begin_synchronized_op(pi, 0, 1) != 0)
348 		continue;
349 	(void) t4_uninit_synchronized(pi);
350 	end_synchronized_op(pi, 0);
351 }
352 
353 static int
354 t4_mc_setpromisc(void *arg, boolean_t on)
355 {
356 	struct port_info *pi = arg;
357 	struct adapter *sc = pi->adapter;
358 	int rc;
359 
360 	rc = begin_synchronized_op(pi, 1, 1);
361 	if (rc != 0)
362 		return (rc);
363 	rc = -t4_set_rxmode(sc, sc->mbox, pi->viid, -1, on ? 1 : 0, -1, -1, -1,
364 	    false);
365 	end_synchronized_op(pi, 1);
366 
367 	return (rc);
368 }
369 
370 /*
371  * TODO: Starts failing as soon as the 336 entry table fills up.  Need to use
372  * hash in that case.
373  */
374 static int
375 t4_mc_multicst(void *arg, boolean_t add, const uint8_t *mcaddr)
376 {
377 	struct port_info *pi = arg;
378 	struct adapter *sc = pi->adapter;
379 	struct fw_vi_mac_cmd c;
380 	int len16, rc;
381 
382 	len16 = howmany(sizeof (c.op_to_viid) + sizeof (c.freemacs_to_len16) +
383 	    sizeof (c.u.exact[0]), 16);
384 	c.op_to_viid = htonl(V_FW_CMD_OP(FW_VI_MAC_CMD) | F_FW_CMD_REQUEST |
385 	    F_FW_CMD_WRITE | V_FW_VI_MAC_CMD_VIID(pi->viid));
386 	c.freemacs_to_len16 = htonl(V_FW_CMD_LEN16(len16));
387 	c.u.exact[0].valid_to_idx = htons(F_FW_VI_MAC_CMD_VALID |
388 	    V_FW_VI_MAC_CMD_IDX(add ? FW_VI_MAC_ADD_MAC :
389 	    FW_VI_MAC_MAC_BASED_FREE));
390 	bcopy(mcaddr, &c.u.exact[0].macaddr, ETHERADDRL);
391 
392 	rc = begin_synchronized_op(pi, 1, 1);
393 	if (rc != 0)
394 		return (rc);
395 	rc = -t4_wr_mbox_meat(sc, sc->mbox, &c, len16 * 16, &c, true);
396 	end_synchronized_op(pi, 1);
397 	if (rc != 0)
398 		return (rc);
399 #ifdef DEBUG
400 	/*
401 	 * TODO: Firmware doesn't seem to return the correct index on removal
402 	 * (it gives back 0x3fd FW_VI_MAC_MAC_BASED_FREE unchanged. Remove this
403 	 * code once it is fixed.
404 	 */
405 	else {
406 		uint16_t idx;
407 
408 		idx = G_FW_VI_MAC_CMD_IDX(ntohs(c.u.exact[0].valid_to_idx));
409 		cxgb_printf(pi->dip, CE_NOTE,
410 		    "%02x:%02x:%02x:%02x:%02x:%02x %s %d", mcaddr[0],
411 		    mcaddr[1], mcaddr[2], mcaddr[3], mcaddr[4], mcaddr[5],
412 		    add ? "added at index" : "removed from index", idx);
413 	}
414 #endif
415 
416 	return (0);
417 }
418 
419 static int
420 t4_mc_unicst(void *arg, const uint8_t *ucaddr)
421 {
422 	struct port_info *pi = arg;
423 	struct adapter *sc = pi->adapter;
424 	int rc;
425 
426 	rc = begin_synchronized_op(pi, 1, 1);
427 	if (rc != 0)
428 		return (rc);
429 	rc = t4_change_mac(sc, sc->mbox, pi->viid, pi->xact_addr_filt, ucaddr,
430 	    true, true);
431 	if (rc < 0)
432 		rc = -rc;
433 	else {
434 		/* LINTED: E_CONSTANT_CONDITION */
435 		pi->xact_addr_filt = rc;
436 		rc = 0;
437 	}
438 	end_synchronized_op(pi, 1);
439 
440 	return (rc);
441 }
442 
443 static mblk_t *
444 t4_mc_tx(void *arg, mblk_t *m)
445 {
446 	struct port_info *pi = arg;
447 	struct adapter *sc = pi->adapter;
448 	struct sge_txq *txq = &sc->sge.txq[pi->first_txq];
449 
450 	return (t4_eth_tx(pi, txq, m));
451 }
452 
453 static boolean_t
454 t4_mc_getcapab(void *arg, mac_capab_t cap, void *data)
455 {
456 	struct port_info *pi = arg;
457 	boolean_t status = B_TRUE;
458 
459 	switch (cap) {
460 	case MAC_CAPAB_HCKSUM:
461 		if (pi->features & CXGBE_HW_CSUM) {
462 			uint32_t *d = data;
463 			*d = HCKSUM_INET_FULL_V4 | HCKSUM_IPHDRCKSUM;
464 		} else
465 			status = B_FALSE;
466 		break;
467 
468 	case MAC_CAPAB_LSO:
469 		/* Enabling LSO requires Checksum offloading */
470 		if (pi->features & CXGBE_HW_LSO &&
471 		    pi->features & CXGBE_HW_CSUM) {
472 			mac_capab_lso_t *d = data;
473 
474 			d->lso_flags = LSO_TX_BASIC_TCP_IPV4;
475 			d->lso_basic_tcp_ipv4.lso_max = 65535;
476 		} else
477 			status = B_FALSE;
478 		break;
479 
480 	default:
481 		status = B_FALSE; /* cap not supported */
482 	}
483 
484 	return (status);
485 }
486 
487 /* ARGSUSED */
488 static int
489 t4_mc_setprop(void *arg, const char *name, mac_prop_id_t id, uint_t size,
490     const void *val)
491 {
492 	struct port_info *pi = arg;
493 	struct adapter *sc = pi->adapter;
494 	struct link_config lc_copy, *lc = &pi->link_cfg;
495 	uint8_t v8 = *(uint8_t *)val;
496 	uint32_t v32 = *(uint32_t *)val;
497 	int old, new = 0, relink = 0, rx_mode = 0, rc = 0;
498 	link_flowctrl_t fc;
499 
500 	/*
501 	 * Save a copy of link_config. This can be used to restore link_config
502 	 * if t4_link_start() fails.
503 	 */
504 	bcopy(lc, &lc_copy, sizeof (struct link_config));
505 
506 	switch (id) {
507 	case MAC_PROP_AUTONEG:
508 		if (lc->supported & FW_PORT_CAP_ANEG) {
509 			old = lc->autoneg;
510 			new = v8 ? AUTONEG_ENABLE : AUTONEG_DISABLE;
511 			if (old != new) {
512 				/* LINTED: E_CONSTANT_CONDITION */
513 				lc->autoneg = new;
514 				relink = 1;
515 				if (new == AUTONEG_DISABLE) {
516 					/* Only 100M is available */
517 					lc->requested_speed =
518 					    FW_PORT_CAP_SPEED_100M;
519 					lc->advertising =
520 					    FW_PORT_CAP_SPEED_100M;
521 				} else {
522 					/*
523 					 * Advertise autonegotiation capability
524 					 * along with supported speeds
525 					 */
526 					lc->advertising |= (FW_PORT_CAP_ANEG |
527 					    (lc->supported &
528 					    (FW_PORT_CAP_SPEED_100M |
529 					    FW_PORT_CAP_SPEED_1G)));
530 					lc->requested_speed = 0;
531 				}
532 			}
533 		} else
534 			rc = ENOTSUP;
535 		break;
536 
537 	case MAC_PROP_MTU:
538 		if (v32 < 46 || v32 > MAX_MTU) {
539 			rc = EINVAL;
540 		} else if (v32 != pi->mtu) {
541 			pi->mtu = v32;
542 			(void) mac_maxsdu_update(pi->mh, v32);
543 			rx_mode = 1;
544 		}
545 
546 		break;
547 
548 	case MAC_PROP_FLOWCTRL:
549 		fc = *(link_flowctrl_t *)val;
550 		old = lc->requested_fc & (PAUSE_TX | PAUSE_RX);
551 
552 		if (fc == LINK_FLOWCTRL_BI)
553 			new = (PAUSE_TX | PAUSE_RX);
554 		else if (fc == LINK_FLOWCTRL_TX)
555 			new = PAUSE_TX;
556 		else if (fc == LINK_FLOWCTRL_RX)
557 			new = PAUSE_RX;
558 
559 		if (new != old) {
560 			lc->requested_fc &= ~(PAUSE_TX | PAUSE_RX);
561 			lc->requested_fc |= new;
562 			relink = 1;
563 		}
564 		break;
565 
566 	case MAC_PROP_EN_10GFDX_CAP:
567 		if (lc->supported & FW_PORT_CAP_ANEG && is_10G_port(pi)) {
568 			old = lc->advertising & FW_PORT_CAP_SPEED_10G;
569 			new = v8 ? FW_PORT_CAP_SPEED_10G : 0;
570 			if (new != old) {
571 				lc->advertising &= ~FW_PORT_CAP_SPEED_10G;
572 				lc->advertising |= new;
573 				relink = 1;
574 			}
575 		} else
576 			rc = ENOTSUP;
577 
578 		break;
579 
580 	case MAC_PROP_EN_1000FDX_CAP:
581 		/* Forced 1G */
582 		if (lc->autoneg == AUTONEG_ENABLE) {
583 			old = lc->advertising & FW_PORT_CAP_SPEED_1G;
584 			new = v8 ? FW_PORT_CAP_SPEED_1G : 0;
585 
586 			if (old != new) {
587 				lc->advertising &= ~FW_PORT_CAP_SPEED_1G;
588 				lc->advertising |= new;
589 				relink = 1;
590 			}
591 		} else
592 			rc = ENOTSUP;
593 		break;
594 
595 	case MAC_PROP_EN_100FDX_CAP:
596 		/* Forced 100M */
597 		if (lc->autoneg == AUTONEG_ENABLE) {
598 			old = lc->advertising & FW_PORT_CAP_SPEED_100M;
599 			new = v8 ? FW_PORT_CAP_SPEED_100M : 0;
600 			if (old != new) {
601 				lc->advertising &= ~FW_PORT_CAP_SPEED_100M;
602 				lc->advertising |= new;
603 				relink = 1;
604 			}
605 		} else
606 			rc = ENOTSUP;
607 		break;
608 
609 	case MAC_PROP_PRIVATE:
610 		rc = setprop(pi, name, val);
611 		break;
612 
613 	default:
614 		rc = ENOTSUP;
615 	}
616 
617 	if (isset(&sc->open_device_map, pi->port_id) != 0) {
618 		if (relink != 0) {
619 			t4_os_link_changed(pi->adapter, pi->port_id, 0);
620 			rc = begin_synchronized_op(pi, 1, 1);
621 			if (rc != 0)
622 				return (rc);
623 			rc = -t4_link_start(sc, sc->mbox, pi->tx_chan,
624 			    &pi->link_cfg);
625 			end_synchronized_op(pi, 1);
626 			if (rc != 0) {
627 				cxgb_printf(pi->dip, CE_WARN,
628 				    "start_link failed:%d", rc);
629 
630 				/* Restore link_config */
631 				bcopy(&lc_copy, lc,
632 				    sizeof (struct link_config));
633 			}
634 		}
635 
636 		if (rx_mode != 0) {
637 			rc = begin_synchronized_op(pi, 1, 1);
638 			if (rc != 0)
639 				return (rc);
640 			rc = -t4_set_rxmode(sc, sc->mbox, pi->viid, v32, -1,
641 			    -1, -1, -1, false);
642 			end_synchronized_op(pi, 1);
643 			if (rc != 0) {
644 				cxgb_printf(pi->dip, CE_WARN,
645 				    "set_rxmode failed: %d", rc);
646 			}
647 		}
648 	}
649 
650 	return (rc);
651 }
652 
653 static int
654 t4_mc_getprop(void *arg, const char *name, mac_prop_id_t id, uint_t size,
655     void *val)
656 {
657 	struct port_info *pi = arg;
658 	struct link_config *lc = &pi->link_cfg;
659 	uint8_t *u = val;
660 
661 	switch (id) {
662 	case MAC_PROP_DUPLEX:
663 		*(link_duplex_t *)val = lc->link_ok ? LINK_DUPLEX_FULL :
664 		    LINK_DUPLEX_UNKNOWN;
665 		break;
666 
667 	case MAC_PROP_SPEED:
668 		if (lc->link_ok != 0) {
669 			*(uint64_t *)val = lc->speed;
670 			*(uint64_t *)val *= 1000000;
671 		} else
672 			*(uint64_t *)val = 0;
673 		break;
674 
675 	case MAC_PROP_STATUS:
676 		*(link_state_t *)val = lc->link_ok ? LINK_STATE_UP :
677 		    LINK_STATE_DOWN;
678 		break;
679 
680 	case MAC_PROP_AUTONEG:
681 		*u = lc->autoneg == AUTONEG_ENABLE;
682 		break;
683 
684 	case MAC_PROP_MTU:
685 		*(uint32_t *)val = pi->mtu;
686 		break;
687 
688 	case MAC_PROP_FLOWCTRL:
689 		if ((lc->requested_fc & (PAUSE_TX | PAUSE_RX)) ==
690 		    (PAUSE_TX | PAUSE_RX))
691 			*(link_flowctrl_t *)val = LINK_FLOWCTRL_BI;
692 		else if (lc->requested_fc & PAUSE_TX)
693 			*(link_flowctrl_t *)val = LINK_FLOWCTRL_TX;
694 		else if (lc->requested_fc & PAUSE_RX)
695 			*(link_flowctrl_t *)val = LINK_FLOWCTRL_RX;
696 		else
697 			*(link_flowctrl_t *)val = LINK_FLOWCTRL_NONE;
698 		break;
699 
700 	case MAC_PROP_ADV_10GFDX_CAP:
701 	case MAC_PROP_EN_10GFDX_CAP:
702 		*u = !!(lc->advertising & FW_PORT_CAP_SPEED_10G);
703 		break;
704 
705 	case MAC_PROP_ADV_1000FDX_CAP:
706 	case MAC_PROP_EN_1000FDX_CAP:
707 		*u = !!(lc->advertising & FW_PORT_CAP_SPEED_1G);
708 		break;
709 
710 	case MAC_PROP_ADV_100FDX_CAP:
711 	case MAC_PROP_EN_100FDX_CAP:
712 		*u = !!(lc->advertising & FW_PORT_CAP_SPEED_100M);
713 		break;
714 
715 	case MAC_PROP_PRIVATE:
716 		return (getprop(pi, name, size, val));
717 
718 	default:
719 		return (ENOTSUP);
720 	}
721 
722 	return (0);
723 }
724 
725 static void
726 t4_mc_propinfo(void *arg, const char *name, mac_prop_id_t id,
727     mac_prop_info_handle_t ph)
728 {
729 	struct port_info *pi = arg;
730 	struct link_config *lc = &pi->link_cfg;
731 
732 	switch (id) {
733 	case MAC_PROP_DUPLEX:
734 	case MAC_PROP_SPEED:
735 	case MAC_PROP_STATUS:
736 		mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ);
737 		break;
738 
739 	case MAC_PROP_AUTONEG:
740 		if (lc->supported & FW_PORT_CAP_ANEG)
741 			mac_prop_info_set_default_uint8(ph, 1);
742 		else
743 			mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ);
744 		break;
745 
746 	case MAC_PROP_MTU:
747 		mac_prop_info_set_range_uint32(ph, 46, MAX_MTU);
748 		break;
749 
750 	case MAC_PROP_FLOWCTRL:
751 		mac_prop_info_set_default_link_flowctrl(ph, LINK_FLOWCTRL_BI);
752 		break;
753 
754 	case MAC_PROP_EN_10GFDX_CAP:
755 		if (lc->supported & FW_PORT_CAP_ANEG &&
756 		    lc->supported & FW_PORT_CAP_SPEED_10G)
757 			mac_prop_info_set_default_uint8(ph, 1);
758 		else
759 			mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ);
760 		break;
761 
762 	case MAC_PROP_EN_1000FDX_CAP:
763 		if (lc->supported & FW_PORT_CAP_ANEG &&
764 		    lc->supported & FW_PORT_CAP_SPEED_1G)
765 			mac_prop_info_set_default_uint8(ph, 1);
766 		else
767 			mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ);
768 		break;
769 
770 	case MAC_PROP_EN_100FDX_CAP:
771 		if (lc->supported & FW_PORT_CAP_ANEG &&
772 		    lc->supported & FW_PORT_CAP_SPEED_100M)
773 			mac_prop_info_set_default_uint8(ph, 1);
774 		else
775 			mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ);
776 		break;
777 
778 	case MAC_PROP_ADV_10GFDX_CAP:
779 	case MAC_PROP_ADV_1000FDX_CAP:
780 	case MAC_PROP_ADV_100FDX_CAP:
781 		mac_prop_info_set_perm(ph, MAC_PROP_PERM_READ);
782 		break;
783 
784 	case MAC_PROP_PRIVATE:
785 		propinfo(pi, name, ph);
786 		break;
787 
788 	default:
789 		break;
790 	}
791 }
792 
793 static int
794 begin_synchronized_op(struct port_info *pi, int hold, int waitok)
795 {
796 	struct adapter *sc = pi->adapter;
797 	int rc = 0;
798 
799 	ADAPTER_LOCK(sc);
800 	while (!IS_DOOMED(pi) && IS_BUSY(sc)) {
801 		if (!waitok) {
802 			rc = EBUSY;
803 			goto failed;
804 		} else if (cv_wait_sig(&sc->cv, &sc->lock) == 0) {
805 			rc = EINTR;
806 			goto failed;
807 		}
808 	}
809 	if (IS_DOOMED(pi) != 0) {	/* shouldn't happen on Solaris */
810 		rc = ENXIO;
811 		goto failed;
812 	}
813 	ASSERT(!IS_BUSY(sc));
814 	/* LINTED: E_CONSTANT_CONDITION */
815 	SET_BUSY(sc);
816 
817 	if (!hold)
818 		ADAPTER_UNLOCK(sc);
819 
820 	return (0);
821 failed:
822 	ADAPTER_UNLOCK(sc);
823 	return (rc);
824 }
825 
826 static void
827 end_synchronized_op(struct port_info *pi, int held)
828 {
829 	struct adapter *sc = pi->adapter;
830 
831 	if (!held)
832 		ADAPTER_LOCK(sc);
833 
834 	ADAPTER_LOCK_ASSERT_OWNED(sc);
835 	ASSERT(IS_BUSY(sc));
836 	/* LINTED: E_CONSTANT_CONDITION */
837 	CLR_BUSY(sc);
838 	cv_signal(&sc->cv);
839 	ADAPTER_UNLOCK(sc);
840 }
841 
842 static int
843 t4_init_synchronized(struct port_info *pi)
844 {
845 	struct adapter *sc = pi->adapter;
846 	int rc = 0;
847 
848 	ADAPTER_LOCK_ASSERT_NOTOWNED(sc);
849 
850 	if (isset(&sc->open_device_map, pi->port_id) != 0)
851 		return (0);	/* already running */
852 
853 	if (!(sc->flags & FULL_INIT_DONE) &&
854 	    ((rc = adapter_full_init(sc)) != 0))
855 		return (rc);	/* error message displayed already */
856 
857 	if (!(pi->flags & PORT_INIT_DONE)) {
858 		rc = port_full_init(pi);
859 		if (rc != 0)
860 			return (rc); /* error message displayed already */
861 	} else
862 		enable_port_queues(pi);
863 
864 	rc = -t4_set_rxmode(sc, sc->mbox, pi->viid, pi->mtu, 0, 0, 1, 0, false);
865 	if (rc != 0) {
866 		cxgb_printf(pi->dip, CE_WARN, "set_rxmode failed: %d", rc);
867 		goto done;
868 	}
869 	rc = t4_change_mac(sc, sc->mbox, pi->viid, pi->xact_addr_filt,
870 	    pi->hw_addr, true, true);
871 	if (rc < 0) {
872 		cxgb_printf(pi->dip, CE_WARN, "change_mac failed: %d", rc);
873 		rc = -rc;
874 		goto done;
875 	} else
876 		/* LINTED: E_ASSIGN_NARROW_CONV */
877 		pi->xact_addr_filt = rc;
878 
879 	rc = -t4_link_start(sc, sc->mbox, pi->tx_chan, &pi->link_cfg);
880 	if (rc != 0) {
881 		cxgb_printf(pi->dip, CE_WARN, "start_link failed: %d", rc);
882 		goto done;
883 	}
884 
885 	rc = -t4_enable_vi(sc, sc->mbox, pi->viid, true, true);
886 	if (rc != 0) {
887 		cxgb_printf(pi->dip, CE_WARN, "enable_vi failed: %d", rc);
888 		goto done;
889 	}
890 
891 	/* all ok */
892 	setbit(&sc->open_device_map, pi->port_id);
893 done:
894 	if (rc != 0)
895 		(void) t4_uninit_synchronized(pi);
896 
897 	return (rc);
898 }
899 
900 /*
901  * Idempotent.
902  */
903 static int
904 t4_uninit_synchronized(struct port_info *pi)
905 {
906 	struct adapter *sc = pi->adapter;
907 	int rc;
908 
909 	ADAPTER_LOCK_ASSERT_NOTOWNED(sc);
910 
911 	/*
912 	 * Disable the VI so that all its data in either direction is discarded
913 	 * by the MPS.  Leave everything else (the queues, interrupts, and 1Hz
914 	 * tick) intact as the TP can deliver negative advice or data that it's
915 	 * holding in its RAM (for an offloaded connection) even after the VI is
916 	 * disabled.
917 	 */
918 	rc = -t4_enable_vi(sc, sc->mbox, pi->viid, false, false);
919 	if (rc != 0) {
920 		cxgb_printf(pi->dip, CE_WARN, "disable_vi failed: %d", rc);
921 		return (rc);
922 	}
923 
924 	disable_port_queues(pi);
925 
926 	clrbit(&sc->open_device_map, pi->port_id);
927 
928 	pi->link_cfg.link_ok = 0;
929 	pi->link_cfg.speed = 0;
930 	mac_link_update(pi->mh, LINK_STATE_UNKNOWN);
931 
932 	return (0);
933 }
934 
935 static void
936 propinfo(struct port_info *pi, const char *name, mac_prop_info_handle_t ph)
937 {
938 	struct adapter *sc = pi->adapter;
939 	struct driver_properties *p = &sc->props;
940 	struct link_config *lc = &pi->link_cfg;
941 	int v;
942 	char str[16];
943 
944 	if (strcmp(name, T4PROP_TMR_IDX) == 0)
945 		v = is_10G_port(pi) ? p->tmr_idx_10g : p->tmr_idx_1g;
946 	else if (strcmp(name, T4PROP_PKTC_IDX) == 0)
947 		v = is_10G_port(pi) ? p->pktc_idx_10g : p->pktc_idx_1g;
948 	else if (strcmp(name, T4PROP_HW_CSUM) == 0)
949 		v = (pi->features & CXGBE_HW_CSUM) ? 1 : 0;
950 	else if (strcmp(name, T4PROP_HW_LSO) == 0)
951 		v = (pi->features & CXGBE_HW_LSO) ? 1 : 0;
952 	else if (strcmp(name, T4PROP_TX_PAUSE) == 0)
953 		v = (lc->fc & PAUSE_TX) ? 1 : 0;
954 	else if (strcmp(name, T4PROP_RX_PAUSE) == 0)
955 		v = (lc->fc & PAUSE_RX) ? 1 : 0;
956 #if MAC_VERSION == 1
957 	else if (strcmp(name, T4PROP_MTU) == 0)
958 		v = ETHERMTU;
959 #endif
960 	else
961 		return;
962 
963 	(void) snprintf(str, sizeof (str), "%d", v);
964 	mac_prop_info_set_default_str(ph, str);
965 }
966 
967 static int
968 getprop(struct port_info *pi, const char *name, uint_t size, void *val)
969 {
970 	struct link_config *lc = &pi->link_cfg;
971 	int v;
972 
973 	if (strcmp(name, T4PROP_TMR_IDX) == 0)
974 		v = pi->tmr_idx;
975 	else if (strcmp(name, T4PROP_PKTC_IDX) == 0)
976 		v = pi->pktc_idx;
977 	else if (strcmp(name, T4PROP_HW_CSUM) == 0)
978 		v = (pi->features & CXGBE_HW_CSUM) ? 1 : 0;
979 	else if (strcmp(name, T4PROP_HW_LSO) == 0)
980 		v = (pi->features & CXGBE_HW_LSO) ? 1 : 0;
981 	else if (strcmp(name, T4PROP_TX_PAUSE) == 0)
982 		v = (lc->fc & PAUSE_TX) ? 1 : 0;
983 	else if (strcmp(name, T4PROP_RX_PAUSE) == 0)
984 		v = (lc->fc & PAUSE_RX) ? 1 : 0;
985 #if MAC_VERSION == 1
986 	else if (strcmp(name, T4PROP_MTU) == 0)
987 		v = pi->mtu;
988 #endif
989 	else
990 		return (ENOTSUP);
991 
992 	(void) snprintf(val, size, "%d", v);
993 	return (0);
994 }
995 
996 static int
997 setprop(struct port_info *pi, const char *name, const void *val)
998 {
999 	struct adapter *sc = pi->adapter;
1000 	long v;
1001 	int i, rc = 0, relink = 0, rx_mode = 0;
1002 	struct sge_rxq *rxq;
1003 	struct link_config lc_old, *lc = &pi->link_cfg;
1004 
1005 	/*
1006 	 * Save a copy of link_config. This can be used to restore link_config
1007 	 * if t4_link_start() fails.
1008 	 */
1009 	bcopy(lc, &lc_old, sizeof (struct link_config));
1010 
1011 	(void) ddi_strtol(val, NULL, 0, &v);
1012 
1013 	if (strcmp(name, T4PROP_TMR_IDX) == 0) {
1014 		if (v < 0 || v >= SGE_NTIMERS)
1015 			return (EINVAL);
1016 		if (v == pi->tmr_idx)
1017 			return (0);
1018 
1019 		/* LINTED: E_ASSIGN_NARROW_CONV */
1020 		pi->tmr_idx = v;
1021 		for_each_rxq(pi, i, rxq) {
1022 			rxq->iq.intr_params = V_QINTR_TIMER_IDX(v) |
1023 			    V_QINTR_CNT_EN(pi->pktc_idx >= 0);
1024 		}
1025 
1026 	} else if (strcmp(name, T4PROP_PKTC_IDX) == 0) {
1027 		if (v >= SGE_NCOUNTERS)
1028 			return (EINVAL);
1029 		if (v == pi->pktc_idx || (v < 0 && pi->pktc_idx == -1))
1030 			return (0);
1031 
1032 		/* LINTED: E_ASSIGN_NARROW_CONV */
1033 		pi->pktc_idx = v < 0 ? -1 : v;
1034 		for_each_rxq(pi, i, rxq) {
1035 			rxq->iq.intr_params = V_QINTR_TIMER_IDX(pi->tmr_idx) |
1036 			    /* takes effect right away */
1037 			    V_QINTR_CNT_EN(v >= 0);
1038 			/* LINTED: E_ASSIGN_NARROW_CONV */
1039 			rxq->iq.intr_pktc_idx = v; /* this needs fresh plumb */
1040 		}
1041 	} else if (strcmp(name, T4PROP_HW_CSUM) == 0) {
1042 		if (v != 0 && v != 1)
1043 			return (EINVAL);
1044 		if (v == 1)
1045 			pi->features |= CXGBE_HW_CSUM;
1046 		else
1047 			pi->features &= ~CXGBE_HW_CSUM;
1048 	} else if (strcmp(name, T4PROP_HW_LSO) == 0) {
1049 		if (v != 0 && v != 1)
1050 			return (EINVAL);
1051 		if (v == 1)
1052 			pi->features |= CXGBE_HW_LSO;
1053 		else
1054 			pi->features &= ~CXGBE_HW_LSO;
1055 	} else if (strcmp(name, T4PROP_TX_PAUSE) == 0) {
1056 		if (v != 0 && v != 1)
1057 			return (EINVAL);
1058 
1059 		if (v != 0)
1060 			lc->requested_fc |= PAUSE_TX;
1061 		else
1062 			lc->requested_fc &= ~PAUSE_TX;
1063 
1064 		relink = 1;
1065 
1066 	} else if (strcmp(name, T4PROP_RX_PAUSE) == 0) {
1067 		if (v != 0 && v != 1)
1068 			return (EINVAL);
1069 
1070 		if (v != 0)
1071 			lc->requested_fc |= PAUSE_RX;
1072 		else
1073 			lc->requested_fc &= ~PAUSE_RX;
1074 
1075 		relink = 1;
1076 	}
1077 #if MAC_VERSION == 1
1078 	else if (strcmp(name, T4PROP_MTU) == 0) {
1079 		if (v < 46 || v > MAX_MTU)
1080 			return (EINVAL);
1081 		if (v == pi->mtu)
1082 			return (0);
1083 
1084 		pi->mtu = (int)v;
1085 		(void) mac_maxsdu_update(pi->mh, v);
1086 		rx_mode = 1;
1087 	}
1088 #endif
1089 	else
1090 		return (ENOTSUP);
1091 
1092 	if (!(relink || rx_mode))
1093 		return (0);
1094 
1095 	/* If we are here, either relink or rx_mode is 1 */
1096 	if (isset(&sc->open_device_map, pi->port_id) != 0) {
1097 		if (relink != 0) {
1098 			rc = begin_synchronized_op(pi, 1, 1);
1099 			if (rc != 0)
1100 				return (rc);
1101 			rc = -t4_link_start(sc, sc->mbox, pi->tx_chan, lc);
1102 			end_synchronized_op(pi, 1);
1103 			if (rc != 0) {
1104 				cxgb_printf(pi->dip, CE_WARN,
1105 				    "start_link failed:%d", rc);
1106 				/* Restore link_config */
1107 				bcopy(&lc_old, lc, sizeof (struct link_config));
1108 			}
1109 		} else if (rx_mode != 0) {
1110 			rc = begin_synchronized_op(pi, 1, 1);
1111 			if (rc != 0)
1112 				return (rc);
1113 			rc = -t4_set_rxmode(sc, sc->mbox, pi->viid, v, -1, -1,
1114 			    -1, -1, false);
1115 			end_synchronized_op(pi, 1);
1116 			if (rc != 0)  {
1117 				cxgb_printf(pi->dip, CE_WARN,
1118 				    "set_rxmode failed: %d", rc);
1119 			}
1120 		}
1121 		return (rc);
1122 	}
1123 
1124 	return (0);
1125 }
1126 
1127 void
1128 t4_mc_init(struct port_info *pi)
1129 {
1130 	pi->mc = &t4_m_callbacks;
1131 	pi->props = t4_priv_props;
1132 }
1133 
1134 void
1135 t4_os_link_changed(struct adapter *sc, int idx, int link_stat)
1136 {
1137 	struct port_info *pi = sc->port[idx];
1138 
1139 	mac_link_update(pi->mh, link_stat ? LINK_STATE_UP : LINK_STATE_DOWN);
1140 }
1141 
1142 /* ARGSUSED */
1143 void
1144 t4_mac_rx(struct port_info *pi, struct sge_rxq *rxq, mblk_t *m)
1145 {
1146 	mac_rx(pi->mh, NULL, m);
1147 }
1148