xref: /illumos-gate/usr/src/uts/common/io/chxge/com/ch_mac.c (revision d39a76e7)
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 (C) 2003-2005 Chelsio Communications.  All rights reserved.
24  */
25 
26 #pragma ident	"%Z%%M%	%I%	%E% SMI"	/* ch_mac.c */
27 
28 #include "gmac.h"
29 #include "regs.h"
30 #include "fpga_defs.h"
31 
32 #define	MAC_CSR_INTERFACE_GMII	0x0
33 #define	MAC_CSR_INTERFACE_TBI	0x1
34 #define	MAC_CSR_INTERFACE_MII	0x2
35 #define	MAC_CSR_INTERFACE_RMII	0x3
36 
37 /* Chelsio's MAC statistics. */
38 struct mac_statistics {
39 
40 	/* Transmit */
41 	u32 TxFramesTransmittedOK;
42 	u32 TxReserved1;
43 	u32 TxReserved2;
44 	u32 TxOctetsTransmittedOK;
45 	u32 TxFramesWithDeferredXmissions;
46 	u32 TxLateCollisions;
47 	u32 TxFramesAbortedDueToXSCollisions;
48 	u32 TxFramesLostDueToIntMACXmitError;
49 	u32 TxReserved3;
50 	u32 TxMulticastFrameXmittedOK;
51 	u32 TxBroadcastFramesXmittedOK;
52 	u32 TxFramesWithExcessiveDeferral;
53 	u32 TxPAUSEMACCtrlFramesTransmitted;
54 
55 	/* Receive */
56 	u32 RxFramesReceivedOK;
57 	u32 RxFrameCheckSequenceErrors;
58 	u32 RxAlignmentErrors;
59 	u32 RxOctetsReceivedOK;
60 	u32 RxFramesLostDueToIntMACRcvError;
61 	u32 RxMulticastFramesReceivedOK;
62 	u32 RxBroadcastFramesReceivedOK;
63 	u32 RxInRangeLengthErrors;
64 	u32 RxTxOutOfRangeLengthField;
65 	u32 RxFrameTooLongErrors;
66 	u32 RxPAUSEMACCtrlFramesReceived;
67 };
68 
69 static int static_aPorts[] = {
70 	FPGA_GMAC_INTERRUPT_PORT0,
71 	FPGA_GMAC_INTERRUPT_PORT1,
72 	FPGA_GMAC_INTERRUPT_PORT2,
73 	FPGA_GMAC_INTERRUPT_PORT3
74 };
75 
76 struct _cmac_instance {
77 	u32 index;
78 };
79 
80 static int mac_intr_enable(struct cmac *mac)
81 {
82 	u32 mac_intr;
83 
84 	if (t1_is_asic(mac->adapter)) {
85 		/* ASIC */
86 		/*EMPTY*/
87 		/* We don't use the on chip MAC for ASIC products. */
88 	} else {
89 		/* FPGA */
90 
91 		/* Set parent gmac interrupt. */
92 		mac_intr = t1_read_reg_4(mac->adapter, A_PL_ENABLE);
93 		mac_intr |= FPGA_PCIX_INTERRUPT_GMAC;
94 		t1_write_reg_4(mac->adapter, A_PL_ENABLE, mac_intr);
95 
96 		mac_intr = t1_read_reg_4(mac->adapter,
97 			FPGA_GMAC_ADDR_INTERRUPT_ENABLE);
98 		mac_intr |= static_aPorts[mac->instance->index];
99 		t1_write_reg_4(mac->adapter,
100 			FPGA_GMAC_ADDR_INTERRUPT_ENABLE, mac_intr);
101 	}
102 
103 	return (0);
104 }
105 
106 static int mac_intr_disable(struct cmac *mac)
107 {
108 	u32 mac_intr;
109 
110 	if (t1_is_asic(mac->adapter)) {
111 		/* ASIC */
112 		/*EMPTY*/
113 		/* We don't use the on chip MAC for ASIC products. */
114 	} else {
115 		/* FPGA */
116 
117 		/* Set parent gmac interrupt. */
118 		mac_intr = t1_read_reg_4(mac->adapter, A_PL_ENABLE);
119 		mac_intr &= ~FPGA_PCIX_INTERRUPT_GMAC;
120 		t1_write_reg_4(mac->adapter, A_PL_ENABLE, mac_intr);
121 
122 		mac_intr = t1_read_reg_4(mac->adapter,
123 			FPGA_GMAC_ADDR_INTERRUPT_ENABLE);
124 		mac_intr &= ~(static_aPorts[mac->instance->index]);
125 		t1_write_reg_4(mac->adapter,
126 			FPGA_GMAC_ADDR_INTERRUPT_ENABLE, mac_intr);
127 	}
128 
129 	return (0);
130 }
131 
132 static int mac_intr_clear(struct cmac *mac)
133 {
134 	u32 mac_intr;
135 
136 	if (t1_is_asic(mac->adapter)) {
137 		/* ASIC */
138 		/*EMPTY*/
139 		/* We don't use the on chip MAC for ASIC products. */
140 	} else {
141 		/* FPGA */
142 
143 		/* Set parent gmac interrupt. */
144 		t1_write_reg_4(mac->adapter, A_PL_CAUSE,
145 			FPGA_PCIX_INTERRUPT_GMAC);
146 
147 		mac_intr = t1_read_reg_4(mac->adapter,
148 			FPGA_GMAC_ADDR_INTERRUPT_CAUSE);
149 		mac_intr |= (static_aPorts[mac->instance->index]);
150 		t1_write_reg_4(mac->adapter,
151 			FPGA_GMAC_ADDR_INTERRUPT_CAUSE, mac_intr);
152 	}
153 
154 	return (0);
155 }
156 
157 static int mac_get_address(struct cmac *mac, u8 addr[6])
158 {
159 	u32 data32_lo, data32_hi;
160 
161 	data32_lo = t1_read_reg_4(mac->adapter,
162 			MAC_REG_IDLO(mac->instance->index));
163 	data32_hi = t1_read_reg_4(mac->adapter,
164 			MAC_REG_IDHI(mac->instance->index));
165 
166 	addr[0] = (u8) ((data32_hi >> 8) & 0xFF);
167 	addr[1] = (u8) ((data32_hi) & 0xFF);
168 	addr[2] = (u8) ((data32_lo >> 24) & 0xFF);
169 	addr[3] = (u8) ((data32_lo >> 16) & 0xFF);
170 	addr[4] = (u8) ((data32_lo >> 8) & 0xFF);
171 	addr[5] = (u8) ((data32_lo) & 0xFF);
172 	return (0);
173 }
174 
175 static int mac_reset(struct cmac *mac)
176 {
177 	u32 data32;
178 	int mac_in_reset, time_out = 100;
179 	int idx = mac->instance->index;
180 
181 	data32 = t1_read_reg_4(mac->adapter, MAC_REG_CSR(idx));
182 	t1_write_reg_4(mac->adapter, MAC_REG_CSR(idx),
183 		data32 | F_MAC_RESET);
184 
185 	do {
186 		data32 = t1_read_reg_4(mac->adapter,
187 			MAC_REG_CSR(idx));
188 		mac_in_reset = data32 & F_MAC_RESET;
189 		if (mac_in_reset)
190 			DELAY_US(1);
191 	} while (mac_in_reset && --time_out);
192 
193 	if (mac_in_reset) {
194 		CH_ERR("%s: MAC %d reset timed out\n",
195 			adapter_name(mac->adapter), idx);
196 		return (2);
197 	}
198 
199 	return (0);
200 }
201 
202 static int mac_set_rx_mode(struct cmac *mac, struct t1_rx_mode *rm)
203 {
204 	u32 val;
205 
206 	val = t1_read_reg_4(mac->adapter,
207 			    MAC_REG_CSR(mac->instance->index));
208 	val &= ~(F_MAC_PROMISC | F_MAC_MC_ENABLE);
209 	val |= V_MAC_PROMISC(t1_rx_mode_promisc(rm) != 0);
210 	val |= V_MAC_MC_ENABLE(t1_rx_mode_allmulti(rm) != 0);
211 	t1_write_reg_4(mac->adapter,
212 		MAC_REG_CSR(mac->instance->index), val);
213 
214 	return (0);
215 }
216 
217 static int mac_set_speed_duplex_fc(struct cmac *mac, int speed, int duplex,
218 	int fc)
219 {
220 	u32 data32;
221 
222 	data32 = t1_read_reg_4(mac->adapter,
223 		MAC_REG_CSR(mac->instance->index));
224 	data32 &= ~(F_MAC_HALF_DUPLEX | V_MAC_SPEED(M_MAC_SPEED) |
225 		V_INTERFACE(M_INTERFACE) | F_MAC_TX_PAUSE_ENABLE |
226 		F_MAC_RX_PAUSE_ENABLE);
227 
228 	switch (speed) {
229 	case SPEED_10:
230 	case SPEED_100:
231 		data32 |= V_INTERFACE(MAC_CSR_INTERFACE_MII);
232 		data32 |= V_MAC_SPEED(speed == SPEED_10 ? 0 : 1);
233 		break;
234 	case SPEED_1000:
235 		data32 |= V_INTERFACE(MAC_CSR_INTERFACE_GMII);
236 		data32 |= V_MAC_SPEED(2);
237 		break;
238 	}
239 
240 	if (duplex >= 0)
241 		data32 |= V_MAC_HALF_DUPLEX(duplex == DUPLEX_HALF);
242 
243 	if (fc >= 0) {
244 		data32 |= V_MAC_RX_PAUSE_ENABLE((fc & PAUSE_RX) != 0);
245 		data32 |= V_MAC_TX_PAUSE_ENABLE((fc & PAUSE_TX) != 0);
246 	}
247 
248 	t1_write_reg_4(mac->adapter,
249 		MAC_REG_CSR(mac->instance->index), data32);
250 	return (0);
251 }
252 
253 static int mac_enable(struct cmac *mac, int which)
254 {
255 	u32 val;
256 
257 	val = t1_read_reg_4(mac->adapter,
258 			    MAC_REG_CSR(mac->instance->index));
259 	if (which & MAC_DIRECTION_RX)
260 		val |= F_MAC_RX_ENABLE;
261 	if (which & MAC_DIRECTION_TX)
262 		val |= F_MAC_TX_ENABLE;
263 	t1_write_reg_4(mac->adapter,
264 		MAC_REG_CSR(mac->instance->index), val);
265 	return (0);
266 }
267 
268 static int mac_disable(struct cmac *mac, int which)
269 {
270 	u32 val;
271 
272 	val = t1_read_reg_4(mac->adapter,
273 		MAC_REG_CSR(mac->instance->index));
274 	if (which & MAC_DIRECTION_RX)
275 		val &= ~F_MAC_RX_ENABLE;
276 	if (which & MAC_DIRECTION_TX)
277 		val &= ~F_MAC_TX_ENABLE;
278 	t1_write_reg_4(mac->adapter,
279 		MAC_REG_CSR(mac->instance->index), val);
280 	return (0);
281 }
282 
283 int
284 mac_set_ifs(struct cmac *mac, u32 mode)
285 {
286 	t1_write_reg_4(mac->adapter,
287 		MAC_REG_IFS(mac->instance->index), mode);
288 
289 	return (0);
290 }
291 
292 int
293 mac_enable_isl(struct cmac *mac)
294 {
295 	u32 data32 = t1_read_reg_4(mac->adapter,
296 		MAC_REG_CSR(mac->instance->index));
297 	data32 |= F_MAC_RX_ENABLE | F_MAC_TX_ENABLE;
298 	t1_write_reg_4(mac->adapter,
299 		MAC_REG_CSR(mac->instance->index), data32);
300 
301 	return (0);
302 }
303 
304 static int mac_set_mtu(struct cmac *mac, int mtu)
305 {
306 	if (mtu > 9600)
307 		return (-EINVAL);
308 	t1_write_reg_4(mac->adapter,
309 		MAC_REG_LARGEFRAMELENGTH(mac->instance->index),
310 		mtu + 14 + 4);
311 	return (0);
312 }
313 
314 /* ARGSUSED */
315 static const struct cmac_statistics *mac_update_statistics(struct cmac *mac,
316 	int flag)
317 {
318 	struct mac_statistics st;
319 	u32 *p = (u32 *) & st, i;
320 
321 	t1_write_reg_4(mac->adapter,
322 		MAC_REG_RMCNT(mac->instance->index), 0);
323 	for (i = 0; i < sizeof (st) / sizeof (u32); i++)
324 		*p++ = t1_read_reg_4(mac->adapter,
325 			MAC_REG_RMDATA(mac->instance->index));
326 
327 	/* XXX convert stats */
328 	return (&mac->stats);
329 }
330 
331 static void mac_destroy(struct cmac *mac)
332 {
333 	t1_os_free((void *)mac, sizeof (*mac) + sizeof (cmac_instance));
334 }
335 
336 #ifdef C99_NOT_SUPPORTED
337 static struct cmac_ops chelsio_mac_ops = {
338 	mac_destroy,
339 	mac_reset,
340 	mac_intr_enable,
341 	mac_intr_disable,
342 	mac_intr_clear,
343 	NULL,
344 	mac_enable,
345 	mac_disable,
346 	NULL,
347 	NULL,
348 	mac_set_mtu,
349 	mac_set_rx_mode,
350 	mac_set_speed_duplex_fc,
351 	NULL,
352 	mac_update_statistics,
353 	mac_get_address,
354 	NULL
355 };
356 #else
357 static struct cmac_ops chelsio_mac_ops = {
358 	.destroy		= mac_destroy,
359 	.reset			= mac_reset,
360 	.interrupt_enable	= mac_intr_enable,
361 	.interrupt_disable	= mac_intr_disable,
362 	.interrupt_clear	= mac_intr_clear,
363 	.enable			= mac_enable,
364 	.disable		= mac_disable,
365 	.set_mtu		= mac_set_mtu,
366 	.set_rx_mode		= mac_set_rx_mode,
367 	.set_speed_duplex_fc	= mac_set_speed_duplex_fc,
368 	.macaddress_get		= mac_get_address,
369 	.statistics_update	= mac_update_statistics,
370 };
371 #endif
372 
373 static struct cmac *mac_create(adapter_t *adapter, int index)
374 {
375 	struct cmac *mac;
376 	u32 data32;
377 
378 	if (index >= 4)
379 		return (NULL);
380 
381 	mac = t1_os_malloc_wait_zero(sizeof (*mac) + sizeof (cmac_instance));
382 	if (!mac)
383 		return (NULL);
384 
385 	mac->ops = &chelsio_mac_ops;
386 	mac->instance = (cmac_instance *) (mac + 1);
387 
388 	mac->instance->index = index;
389 	mac->adapter = adapter;
390 
391 	data32 = t1_read_reg_4(adapter, MAC_REG_CSR(mac->instance->index));
392 	data32 &= ~(F_MAC_RESET | F_MAC_PROMISC | F_MAC_PROMISC |
393 		    F_MAC_LB_ENABLE | F_MAC_RX_ENABLE | F_MAC_TX_ENABLE);
394 	data32 |= F_MAC_JUMBO_ENABLE;
395 	t1_write_reg_4(adapter, MAC_REG_CSR(mac->instance->index), data32);
396 
397 	/* Initialize the random backoff seed. */
398 	data32 = 0x55aa + (3 * index);
399 	t1_write_reg_4(adapter,
400 		MAC_REG_GMRANDBACKOFFSEED(mac->instance->index), data32);
401 
402 	/* Check to see if the mac address needs to be set manually. */
403 	data32 = t1_read_reg_4(adapter, MAC_REG_IDLO(mac->instance->index));
404 	if (data32 == 0 || data32 == 0xffffffff) {
405 		/*
406 		 * Add a default MAC address if we can't read one.
407 		 */
408 		t1_write_reg_4(adapter, MAC_REG_IDLO(mac->instance->index),
409 			0x43FFFFFF - index);
410 		t1_write_reg_4(adapter, MAC_REG_IDHI(mac->instance->index),
411 			0x0007);
412 	}
413 
414 	(void) mac_set_mtu(mac, 1500);
415 	return (mac);
416 }
417 
418 struct gmac t1_chelsio_mac_ops = {
419 	0,
420 	mac_create
421 };
422