xref: /illumos-gate/usr/src/uts/common/io/chxge/com/espi.c (revision 1f5207b7604fb44407eb4342aff613f7c4508508)
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"	/* espi.c */
27 
28 #include "common.h"
29 #include "regs.h"
30 #include "espi.h"
31 
32 struct peespi {
33 	adapter_t *adapter;
34 	struct espi_intr_counts intr_cnt;
35 	u32 misc_ctrl;
36 	SPINLOCK lock;
37 };
38 
39 #define ESPI_INTR_MASK (F_DIP4ERR | F_RXDROP | F_TXDROP | F_RXOVERFLOW | \
40 			F_RAMPARITYERR | F_DIP2PARITYERR)
41 #define MON_MASK  (V_MONITORED_PORT_NUM(3) | F_MONITORED_DIRECTION \
42 			| F_MONITORED_INTERFACE)
43 
44 #define TRICN_CNFG 14
45 #define TRICN_CMD_READ  0x11
46 #define TRICN_CMD_WRITE 0x21
47 #define TRICN_CMD_ATTEMPTS 10
48 
49 static int tricn_write(adapter_t *adapter, int bundle_addr, int module_addr,
50 		       int ch_addr, int reg_offset, u32 wr_data)
51 {
52 	int busy;
53 
54 	t1_write_reg_4(adapter, A_ESPI_CMD_ADDR, V_WRITE_DATA(wr_data) |
55 		       V_REGISTER_OFFSET(reg_offset) |
56 		       V_CHANNEL_ADDR(ch_addr) | V_MODULE_ADDR(module_addr) |
57 		       V_BUNDLE_ADDR(bundle_addr) |
58 		       V_SPI4_COMMAND(TRICN_CMD_WRITE));
59 	t1_write_reg_4(adapter, A_ESPI_GOSTAT, 0);
60 
61 	busy = t1_wait_op_done(adapter, A_ESPI_GOSTAT, F_ESPI_CMD_BUSY, 0,
62 			TRICN_CMD_ATTEMPTS, 0);
63 
64 	if (busy)
65 		CH_ERR("%s: TRICN write timed out\n", adapter_name(adapter));
66 
67 	return busy;
68 }
69 
70 #if 0
71 static int tricn_read(adapter_t *adapter, int bundle_addr, int module_addr,
72 		      int ch_addr, int reg_offset, u8 *rd_data)
73 {
74 	int busy, attempts = TRICN_CMD_ATTEMPTS;
75 	u32 status;
76 
77 	t1_write_reg_4(adapter, A_ESPI_CMD_ADDR,
78 		       V_REGISTER_OFFSET(reg_offset) |
79 		       V_CHANNEL_ADDR(ch_addr) | V_MODULE_ADDR(module_addr) |
80 		       V_BUNDLE_ADDR(bundle_addr) |
81 		       V_SPI4_COMMAND(TRICN_CMD_READ));
82 	t1_write_reg_4(adapter, A_ESPI_GOSTAT, 0);
83 
84 	do {
85 		status = t1_read_reg_4(adapter, A_ESPI_GOSTAT);
86 		busy = status & F_ESPI_CMD_BUSY;
87 	} while (busy && --attempts);
88 
89 	if (busy)
90 		CH_ERR("%s: TRICN read timed out\n", adapter_name(adapter));
91 	else
92 		*rd_data = G_READ_DATA(status);
93 	return busy;
94 }
95 #endif
96 
97 static int tricn_init(adapter_t *adapter)
98 {
99 	int i, sme = 1;
100 
101 	if (!(t1_read_reg_4(adapter, A_ESPI_RX_RESET) & F_RX_CLK_STATUS)) {
102 		CH_ERR("%s: ESPI clock not ready\n", adapter_name(adapter));
103 		return (-1);
104 	 }
105 
106 	t1_write_reg_4(adapter, A_ESPI_RX_RESET, F_ESPI_RX_CORE_RST);
107 
108 	if (sme) {
109 		(void) tricn_write(adapter, 0, 0, 0, TRICN_CNFG, 0x81);
110 		(void) tricn_write(adapter, 0, 1, 0, TRICN_CNFG, 0x81);
111 		(void) tricn_write(adapter, 0, 2, 0, TRICN_CNFG, 0x81);
112 	}
113 	for (i=1; i<= 8; i++) (void) tricn_write(adapter, 0, 0, i, TRICN_CNFG, 0xf1);
114 	for (i=1; i<= 2; i++) (void) tricn_write(adapter, 0, 1, i, TRICN_CNFG, 0xf1);
115 	for (i=1; i<= 3; i++) (void) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1);
116 	(void) tricn_write(adapter, 0, 2, 4, TRICN_CNFG, 0xf1);
117 	(void) tricn_write(adapter, 0, 2, 5, TRICN_CNFG, 0xe1);
118 	(void) tricn_write(adapter, 0, 2, 6, TRICN_CNFG, 0xf1);
119 	(void) tricn_write(adapter, 0, 2, 7, TRICN_CNFG, 0x80);
120 	(void) tricn_write(adapter, 0, 2, 8, TRICN_CNFG, 0xf1);
121 
122 	t1_write_reg_4(adapter, A_ESPI_RX_RESET, F_ESPI_RX_CORE_RST | F_ESPI_RX_LNK_RST);
123 
124 	return 0;
125 }
126 
127 void t1_espi_intr_enable(struct peespi *espi)
128 {
129 	u32 enable, pl_intr = t1_read_reg_4(espi->adapter, A_PL_ENABLE);
130 
131 	/*
132 	 * Cannot enable ESPI interrupts on T1B because HW asserts the
133 	 * interrupt incorrectly, namely the driver gets ESPI interrupts
134 	 * but no data is actually dropped (can verify this reading the ESPI
135 	 * drop registers).  Also, once the ESPI interrupt is asserted it
136 	 * cannot be cleared (HW bug).
137 	 */
138 	enable = t1_is_T1B(espi->adapter) ? 0 : ESPI_INTR_MASK;
139 	t1_write_reg_4(espi->adapter, A_ESPI_INTR_ENABLE, enable);
140 	t1_write_reg_4(espi->adapter, A_PL_ENABLE, pl_intr | F_PL_INTR_ESPI);
141 }
142 
143 void t1_espi_intr_clear(struct peespi *espi)
144 {
145 	(void) t1_read_reg_4(espi->adapter, A_ESPI_DIP2_ERR_COUNT);
146 	t1_write_reg_4(espi->adapter, A_ESPI_INTR_STATUS, 0xffffffff);
147 	t1_write_reg_4(espi->adapter, A_PL_CAUSE, F_PL_INTR_ESPI);
148 }
149 
150 void t1_espi_intr_disable(struct peespi *espi)
151 {
152 	u32 pl_intr = t1_read_reg_4(espi->adapter, A_PL_ENABLE);
153 
154 	t1_write_reg_4(espi->adapter, A_ESPI_INTR_ENABLE, 0);
155 	t1_write_reg_4(espi->adapter, A_PL_ENABLE, pl_intr & ~F_PL_INTR_ESPI);
156 }
157 
158 int t1_espi_intr_handler(struct peespi *espi)
159 {
160 	u32 status = t1_read_reg_4(espi->adapter, A_ESPI_INTR_STATUS);
161 
162 	if (status & F_DIP4ERR)
163 		espi->intr_cnt.DIP4_err++;
164 	if (status & F_RXDROP)
165 		espi->intr_cnt.rx_drops++;
166 	if (status & F_TXDROP)
167 		espi->intr_cnt.tx_drops++;
168 	if (status & F_RXOVERFLOW)
169 		espi->intr_cnt.rx_ovflw++;
170 	if (status & F_RAMPARITYERR)
171 		espi->intr_cnt.parity_err++;
172 	if (status & F_DIP2PARITYERR) {
173 		espi->intr_cnt.DIP2_parity_err++;
174 		(void) t1_read_reg_4(espi->adapter, A_ESPI_DIP2_ERR_COUNT);
175 	 }
176 
177 	/*
178 	 * For T1B we need to write 1 to clear ESPI interrupts.  For T2+ we
179 	 * write the status as is.
180 	 */
181 	if (status && t1_is_T1B(espi->adapter))
182 		status = 1;
183 	t1_write_reg_4(espi->adapter, A_ESPI_INTR_STATUS, status);
184 	return 0;
185 }
186 
187 const struct espi_intr_counts *t1_espi_get_intr_counts(struct peespi *espi)
188 {
189 	return &espi->intr_cnt;
190 }
191 
192 static void espi_setup_for_pm3393(adapter_t *adapter)
193 {
194 	u32 wmark = t1_is_T1B(adapter) ? 0x4000 : 0x3200;
195 
196 	t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN0, 0x1f4);
197 	t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN1, 0x1f4);
198 	t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN2, 0x1f4);
199 	t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN3, 0x1f4);
200 	t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK, 0x100);
201 	t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK, wmark);
202 	t1_write_reg_4(adapter, A_ESPI_CALENDAR_LENGTH, 3);
203 	t1_write_reg_4(adapter, A_ESPI_TRAIN, 0x08000008);
204 	t1_write_reg_4(adapter, A_PORT_CONFIG,
205 		       V_RX_NPORTS(1) | V_TX_NPORTS(1));
206 }
207 
208 static void espi_setup_for_vsc7321(adapter_t *adapter)
209 {
210 #ifdef CONFIG_CHELSIO_T1_COUGAR
211 	u32 wmark = t1_is_T1B(adapter) ? 0x4000 : 0x3200;
212 
213         t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN0, 0x1f4);
214         t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN1, 0x1f4);
215         t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN2, 0x1f4);
216 	t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN3, 0x1f4);
217         t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK, 0x100);
218         t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK, wmark);
219         t1_write_reg_4(adapter, A_ESPI_CALENDAR_LENGTH, 3);
220  	t1_write_reg_4(adapter, A_PORT_CONFIG,
221 		       V_RX_NPORTS(1) | V_TX_NPORTS(1));
222 #else
223         t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN0, 0x1f4);
224         t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN1, 0x1f401f4);
225         t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN2, 0x1f4);
226 	t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK, 0xa00);
227 	t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK, 0x1ff);
228 	t1_write_reg_4(adapter, A_ESPI_CALENDAR_LENGTH, 1);
229         t1_write_reg_4(adapter, A_PORT_CONFIG,
230                        V_RX_NPORTS(4) | V_TX_NPORTS(4));
231 #endif
232 	t1_write_reg_4(adapter, A_ESPI_TRAIN, 0x08000008);
233 }
234 
235 /*
236  * Note that T1B requires at least 2 ports for IXF1010 due to a HW bug.
237  */
238 static void espi_setup_for_ixf1010(adapter_t *adapter, int nports)
239 {
240 	t1_write_reg_4(adapter, A_ESPI_CALENDAR_LENGTH, 1);
241 	if (nports == 4) {
242 		if (is_T2(adapter)) {
243 			t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK,
244 				0xf00);
245 			t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK,
246 				0x3c0);
247 		} else {
248 			t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK,
249 				0x7ff);
250 			t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK,
251 				0x1ff);
252 		}
253 	} else {
254 		t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK,
255 			       0x1fff);
256 		t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK,
257 			       0x7ff);
258 	}
259 	t1_write_reg_4(adapter, A_PORT_CONFIG,
260 		       V_RX_NPORTS(nports) | V_TX_NPORTS(nports));
261 }
262 
263 int t1_espi_init(struct peespi *espi, int mac_type, int nports)
264 {
265 	u32 status_enable_extra = 0;
266 	adapter_t *adapter = espi->adapter;
267 
268 	/* Disable ESPI training.  MACs that can handle it enable it below. */
269 	t1_write_reg_4(adapter, A_ESPI_TRAIN, 0);
270 
271 	if (is_T2(adapter)) {
272 		t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL,
273 			       V_OUT_OF_SYNC_COUNT(4) |
274 			       V_DIP2_PARITY_ERR_THRES(3) | V_DIP4_THRES(1));
275         	t1_write_reg_4(adapter, A_ESPI_MAXBURST1_MAXBURST2,
276 			nports == 4 ? 0x200040 : 0x1000080);
277 	} else
278 		t1_write_reg_4(adapter, A_ESPI_MAXBURST1_MAXBURST2, 0x800100);
279 
280 	if (mac_type == CHBT_MAC_PM3393)
281 		espi_setup_for_pm3393(adapter);
282 	else if (mac_type == CHBT_MAC_VSC7321)
283 		espi_setup_for_vsc7321(adapter);
284 	else if (mac_type == CHBT_MAC_IXF1010) {
285 		status_enable_extra = F_INTEL1010MODE;
286 		espi_setup_for_ixf1010(adapter, nports);
287 	} else
288 		return -1;
289 
290 	t1_write_reg_4(adapter, A_ESPI_FIFO_STATUS_ENABLE,
291 		       status_enable_extra | F_RXSTATUSENABLE);
292 
293 	if (is_T2(adapter)) {
294 		(void) tricn_init(adapter);
295 		/*
296 		 * Always position the control at the 1st port egress IN
297 		 * (sop,eop) counter to reduce PIOs for T/N210 workaround.
298 		 */
299 		espi->misc_ctrl = t1_read_reg_4(adapter, A_ESPI_MISC_CONTROL);
300 		espi->misc_ctrl &= ~MON_MASK;
301 		espi->misc_ctrl |= F_MONITORED_DIRECTION;
302 		if (adapter->params.nports == 1)
303 			espi->misc_ctrl |= F_MONITORED_INTERFACE;
304 		t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL, espi->misc_ctrl);
305 		SPIN_LOCK_INIT(espi->lock);
306 	}
307 
308 	return 0;
309 }
310 
311 void t1_espi_destroy(struct peespi *espi)
312 {
313 	if (is_T2(espi->adapter)) {
314 		SPIN_LOCK_DESTROY(espi->lock);
315 	}
316 	t1_os_free((void *)espi, sizeof(*espi));
317 }
318 
319 struct peespi *t1_espi_create(adapter_t *adapter)
320 {
321 	struct peespi *espi = t1_os_malloc_wait_zero(sizeof(*espi));
322 
323 	if (espi)
324 		espi->adapter = adapter;
325 	return espi;
326 }
327 
328 void t1_espi_set_misc_ctrl(adapter_t *adapter, u32 val)
329 {
330 	struct peespi *espi = adapter->espi;
331 
332 	if (!is_T2(adapter))
333 		return;
334 	SPIN_LOCK(espi->lock);
335 	espi->misc_ctrl = (val & ~MON_MASK) |
336 		(espi->misc_ctrl & MON_MASK);
337 	t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL, espi->misc_ctrl);
338 	SPIN_UNLOCK(espi->lock);
339 }
340 
341 u32 t1_espi_get_mon(adapter_t *adapter, u32 addr, u8 wait)
342 {
343 	struct peespi *espi = adapter->espi;
344 	u32 sel;
345 
346 	if (!is_T2(adapter)) return 0;
347 	sel = V_MONITORED_PORT_NUM((addr & 0x3c) >> 2);
348 	if (!wait) {
349 		if (!SPIN_TRYLOCK(espi->lock))
350 			return 0;
351         }
352 	else
353 		SPIN_LOCK(espi->lock);
354 	if ((sel != (espi->misc_ctrl & MON_MASK))) {
355 		t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL,
356 			((espi->misc_ctrl & ~MON_MASK) | sel));
357 		sel = t1_read_reg_4(adapter, A_ESPI_SCH_TOKEN3);
358 		t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL,
359 			espi->misc_ctrl);
360         }
361 	else
362 		sel = t1_read_reg_4(adapter, A_ESPI_SCH_TOKEN3);
363 	SPIN_UNLOCK(espi->lock);
364 	return sel;
365 }
366 
367 /*
368  * This function is for T204 only.
369  * compare with t1_espi_get_mon(), it reads espiInTxSop[0 ~ 3] in
370  * one shot, since there is no per port counter on the out side.
371  */
372 int
373 t1_espi_get_mon_t204(adapter_t *adapter, u32 *valp, u8 wait)
374 {
375 	struct peespi *espi = adapter->espi;
376 	u8 i, nport = (u8)adapter->params.nports;
377 
378 	if (!wait) {
379 		if (!SPIN_TRYLOCK(espi->lock))
380 			return -1;
381 	} else
382 		SPIN_LOCK(espi->lock);
383 	if ((espi->misc_ctrl & MON_MASK) != F_MONITORED_DIRECTION ) {
384 		espi->misc_ctrl = (espi->misc_ctrl & ~MON_MASK) |
385 			F_MONITORED_DIRECTION;
386 		t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL, espi->misc_ctrl);
387 	}
388 	for (i = 0 ; i < nport; i++, valp++) {
389 		if (i) {
390 			t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL,
391 			(espi->misc_ctrl | V_MONITORED_PORT_NUM(i)));
392 		}
393 		*valp = t1_read_reg_4(adapter, A_ESPI_SCH_TOKEN3);
394 	}
395 
396 	t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL, espi->misc_ctrl);
397 
398 	SPIN_UNLOCK(espi->lock);
399 	return 0;
400 }
401