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