Commit | Line | Data |
---|---|---|
8199d3a7 CL |
1 | /***************************************************************************** |
2 | * * | |
3 | * File: espi.c * | |
559fb51b SB |
4 | * $Revision: 1.14 $ * |
5 | * $Date: 2005/05/14 00:59:32 $ * | |
8199d3a7 CL |
6 | * Description: * |
7 | * Ethernet SPI functionality. * | |
8 | * part of the Chelsio 10Gb Ethernet Driver. * | |
9 | * * | |
10 | * This program is free software; you can redistribute it and/or modify * | |
11 | * it under the terms of the GNU General Public License, version 2, as * | |
12 | * published by the Free Software Foundation. * | |
13 | * * | |
14 | * You should have received a copy of the GNU General Public License along * | |
15 | * with this program; if not, write to the Free Software Foundation, Inc., * | |
16 | * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * | |
17 | * * | |
18 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * | |
19 | * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * | |
20 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * | |
21 | * * | |
22 | * http://www.chelsio.com * | |
23 | * * | |
24 | * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. * | |
25 | * All rights reserved. * | |
26 | * * | |
27 | * Maintainers: maintainers@chelsio.com * | |
28 | * * | |
29 | * Authors: Dimitrios Michailidis <dm@chelsio.com> * | |
30 | * Tina Yang <tainay@chelsio.com> * | |
31 | * Felix Marti <felix@chelsio.com> * | |
32 | * Scott Bardone <sbardone@chelsio.com> * | |
33 | * Kurt Ottaway <kottaway@chelsio.com> * | |
34 | * Frank DiMambro <frank@chelsio.com> * | |
35 | * * | |
36 | * History: * | |
37 | * * | |
38 | ****************************************************************************/ | |
39 | ||
40 | #include "common.h" | |
41 | #include "regs.h" | |
42 | #include "espi.h" | |
43 | ||
44 | struct peespi { | |
45 | adapter_t *adapter; | |
46 | struct espi_intr_counts intr_cnt; | |
47 | u32 misc_ctrl; | |
48 | spinlock_t lock; | |
49 | }; | |
50 | ||
51 | #define ESPI_INTR_MASK (F_DIP4ERR | F_RXDROP | F_TXDROP | F_RXOVERFLOW | \ | |
52 | F_RAMPARITYERR | F_DIP2PARITYERR) | |
53 | #define MON_MASK (V_MONITORED_PORT_NUM(3) | F_MONITORED_DIRECTION \ | |
54 | | F_MONITORED_INTERFACE) | |
55 | ||
56 | #define TRICN_CNFG 14 | |
57 | #define TRICN_CMD_READ 0x11 | |
58 | #define TRICN_CMD_WRITE 0x21 | |
59 | #define TRICN_CMD_ATTEMPTS 10 | |
60 | ||
61 | static int tricn_write(adapter_t *adapter, int bundle_addr, int module_addr, | |
62 | int ch_addr, int reg_offset, u32 wr_data) | |
63 | { | |
64 | int busy, attempts = TRICN_CMD_ATTEMPTS; | |
65 | ||
559fb51b SB |
66 | writel(V_WRITE_DATA(wr_data) | |
67 | V_REGISTER_OFFSET(reg_offset) | | |
68 | V_CHANNEL_ADDR(ch_addr) | V_MODULE_ADDR(module_addr) | | |
69 | V_BUNDLE_ADDR(bundle_addr) | | |
70 | V_SPI4_COMMAND(TRICN_CMD_WRITE), | |
71 | adapter->regs + A_ESPI_CMD_ADDR); | |
72 | writel(0, adapter->regs + A_ESPI_GOSTAT); | |
8199d3a7 CL |
73 | |
74 | do { | |
559fb51b | 75 | busy = readl(adapter->regs + A_ESPI_GOSTAT) & F_ESPI_CMD_BUSY; |
8199d3a7 CL |
76 | } while (busy && --attempts); |
77 | ||
78 | if (busy) | |
79 | CH_ERR("%s: TRICN write timed out\n", adapter->name); | |
80 | ||
81 | return busy; | |
82 | } | |
83 | ||
8199d3a7 CL |
84 | static int tricn_init(adapter_t *adapter) |
85 | { | |
f1d3d38a | 86 | int i, sme = 1; |
8199d3a7 | 87 | |
f1d3d38a SH |
88 | if (!(readl(adapter->regs + A_ESPI_RX_RESET) & F_RX_CLK_STATUS)) { |
89 | CH_ERR("%s: ESPI clock not ready\n", adapter->name); | |
90 | return -1; | |
8199d3a7 CL |
91 | } |
92 | ||
f1d3d38a SH |
93 | writel(F_ESPI_RX_CORE_RST, adapter->regs + A_ESPI_RX_RESET); |
94 | ||
95 | if (sme) { | |
96 | tricn_write(adapter, 0, 0, 0, TRICN_CNFG, 0x81); | |
97 | tricn_write(adapter, 0, 1, 0, TRICN_CNFG, 0x81); | |
98 | tricn_write(adapter, 0, 2, 0, TRICN_CNFG, 0x81); | |
99 | } | |
100 | for (i = 1; i <= 8; i++) | |
101 | tricn_write(adapter, 0, 0, i, TRICN_CNFG, 0xf1); | |
102 | for (i = 1; i <= 2; i++) | |
103 | tricn_write(adapter, 0, 1, i, TRICN_CNFG, 0xf1); | |
104 | for (i = 1; i <= 3; i++) | |
105 | tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1); | |
106 | tricn_write(adapter, 0, 2, 4, TRICN_CNFG, 0xf1); | |
107 | tricn_write(adapter, 0, 2, 5, TRICN_CNFG, 0xe1); | |
108 | tricn_write(adapter, 0, 2, 6, TRICN_CNFG, 0xf1); | |
109 | tricn_write(adapter, 0, 2, 7, TRICN_CNFG, 0x80); | |
110 | tricn_write(adapter, 0, 2, 8, TRICN_CNFG, 0xf1); | |
111 | ||
112 | writel(F_ESPI_RX_CORE_RST | F_ESPI_RX_LNK_RST, | |
113 | adapter->regs + A_ESPI_RX_RESET); | |
8199d3a7 CL |
114 | |
115 | return 0; | |
116 | } | |
117 | ||
118 | void t1_espi_intr_enable(struct peespi *espi) | |
119 | { | |
559fb51b | 120 | u32 enable, pl_intr = readl(espi->adapter->regs + A_PL_ENABLE); |
8199d3a7 CL |
121 | |
122 | /* | |
123 | * Cannot enable ESPI interrupts on T1B because HW asserts the | |
124 | * interrupt incorrectly, namely the driver gets ESPI interrupts | |
125 | * but no data is actually dropped (can verify this reading the ESPI | |
126 | * drop registers). Also, once the ESPI interrupt is asserted it | |
127 | * cannot be cleared (HW bug). | |
128 | */ | |
129 | enable = t1_is_T1B(espi->adapter) ? 0 : ESPI_INTR_MASK; | |
559fb51b SB |
130 | writel(enable, espi->adapter->regs + A_ESPI_INTR_ENABLE); |
131 | writel(pl_intr | F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE); | |
8199d3a7 CL |
132 | } |
133 | ||
134 | void t1_espi_intr_clear(struct peespi *espi) | |
135 | { | |
f1d3d38a | 136 | readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT); |
559fb51b SB |
137 | writel(0xffffffff, espi->adapter->regs + A_ESPI_INTR_STATUS); |
138 | writel(F_PL_INTR_ESPI, espi->adapter->regs + A_PL_CAUSE); | |
8199d3a7 CL |
139 | } |
140 | ||
141 | void t1_espi_intr_disable(struct peespi *espi) | |
142 | { | |
559fb51b | 143 | u32 pl_intr = readl(espi->adapter->regs + A_PL_ENABLE); |
8199d3a7 | 144 | |
559fb51b SB |
145 | writel(0, espi->adapter->regs + A_ESPI_INTR_ENABLE); |
146 | writel(pl_intr & ~F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE); | |
8199d3a7 CL |
147 | } |
148 | ||
149 | int t1_espi_intr_handler(struct peespi *espi) | |
150 | { | |
559fb51b | 151 | u32 status = readl(espi->adapter->regs + A_ESPI_INTR_STATUS); |
8199d3a7 CL |
152 | |
153 | if (status & F_DIP4ERR) | |
154 | espi->intr_cnt.DIP4_err++; | |
155 | if (status & F_RXDROP) | |
156 | espi->intr_cnt.rx_drops++; | |
157 | if (status & F_TXDROP) | |
158 | espi->intr_cnt.tx_drops++; | |
159 | if (status & F_RXOVERFLOW) | |
160 | espi->intr_cnt.rx_ovflw++; | |
161 | if (status & F_RAMPARITYERR) | |
162 | espi->intr_cnt.parity_err++; | |
163 | if (status & F_DIP2PARITYERR) { | |
164 | espi->intr_cnt.DIP2_parity_err++; | |
165 | ||
166 | /* | |
167 | * Must read the error count to clear the interrupt | |
168 | * that it causes. | |
169 | */ | |
f1d3d38a | 170 | readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT); |
8199d3a7 CL |
171 | } |
172 | ||
173 | /* | |
174 | * For T1B we need to write 1 to clear ESPI interrupts. For T2+ we | |
175 | * write the status as is. | |
176 | */ | |
177 | if (status && t1_is_T1B(espi->adapter)) | |
178 | status = 1; | |
559fb51b | 179 | writel(status, espi->adapter->regs + A_ESPI_INTR_STATUS); |
8199d3a7 CL |
180 | return 0; |
181 | } | |
182 | ||
559fb51b | 183 | const struct espi_intr_counts *t1_espi_get_intr_counts(struct peespi *espi) |
8199d3a7 | 184 | { |
11e5a202 | 185 | return &espi->intr_cnt; |
8199d3a7 CL |
186 | } |
187 | ||
559fb51b | 188 | static void espi_setup_for_pm3393(adapter_t *adapter) |
8199d3a7 CL |
189 | { |
190 | u32 wmark = t1_is_T1B(adapter) ? 0x4000 : 0x3200; | |
191 | ||
559fb51b SB |
192 | writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN0); |
193 | writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN1); | |
194 | writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN2); | |
195 | writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN3); | |
196 | writel(0x100, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK); | |
197 | writel(wmark, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK); | |
198 | writel(3, adapter->regs + A_ESPI_CALENDAR_LENGTH); | |
199 | writel(0x08000008, adapter->regs + A_ESPI_TRAIN); | |
200 | writel(V_RX_NPORTS(1) | V_TX_NPORTS(1), adapter->regs + A_PORT_CONFIG); | |
8199d3a7 CL |
201 | } |
202 | ||
f1d3d38a SH |
203 | static void espi_setup_for_vsc7321(adapter_t *adapter) |
204 | { | |
356bd146 FR |
205 | writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN0); |
206 | writel(0x1f401f4, adapter->regs + A_ESPI_SCH_TOKEN1); | |
207 | writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN2); | |
f1d3d38a SH |
208 | writel(0xa00, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK); |
209 | writel(0x1ff, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK); | |
210 | writel(1, adapter->regs + A_ESPI_CALENDAR_LENGTH); | |
211 | writel(V_RX_NPORTS(4) | V_TX_NPORTS(4), adapter->regs + A_PORT_CONFIG); | |
212 | ||
213 | writel(0x08000008, adapter->regs + A_ESPI_TRAIN); | |
214 | } | |
215 | ||
216 | /* | |
217 | * Note that T1B requires at least 2 ports for IXF1010 due to a HW bug. | |
218 | */ | |
219 | static void espi_setup_for_ixf1010(adapter_t *adapter, int nports) | |
8199d3a7 | 220 | { |
f1d3d38a SH |
221 | writel(1, adapter->regs + A_ESPI_CALENDAR_LENGTH); |
222 | if (nports == 4) { | |
223 | if (is_T2(adapter)) { | |
224 | writel(0xf00, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK); | |
225 | writel(0x3c0, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK); | |
226 | } else { | |
227 | writel(0x7ff, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK); | |
228 | writel(0x1ff, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK); | |
229 | } | |
230 | } else { | |
231 | writel(0x1fff, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK); | |
232 | writel(0x7ff, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK); | |
233 | } | |
234 | writel(V_RX_NPORTS(nports) | V_TX_NPORTS(nports), adapter->regs + A_PORT_CONFIG); | |
559fb51b | 235 | |
f1d3d38a SH |
236 | } |
237 | ||
238 | int t1_espi_init(struct peespi *espi, int mac_type, int nports) | |
239 | { | |
8199d3a7 CL |
240 | u32 status_enable_extra = 0; |
241 | adapter_t *adapter = espi->adapter; | |
8199d3a7 CL |
242 | |
243 | /* Disable ESPI training. MACs that can handle it enable it below. */ | |
559fb51b | 244 | writel(0, adapter->regs + A_ESPI_TRAIN); |
8199d3a7 CL |
245 | |
246 | if (is_T2(adapter)) { | |
559fb51b SB |
247 | writel(V_OUT_OF_SYNC_COUNT(4) | |
248 | V_DIP2_PARITY_ERR_THRES(3) | | |
249 | V_DIP4_THRES(1), adapter->regs + A_ESPI_MISC_CONTROL); | |
356bd146 | 250 | writel(nports == 4 ? 0x200040 : 0x1000080, |
f1d3d38a SH |
251 | adapter->regs + A_ESPI_MAXBURST1_MAXBURST2); |
252 | } else | |
356bd146 | 253 | writel(0x800100, adapter->regs + A_ESPI_MAXBURST1_MAXBURST2); |
8199d3a7 | 254 | |
f1d3d38a | 255 | if (mac_type == CHBT_MAC_PM3393) |
8199d3a7 | 256 | espi_setup_for_pm3393(adapter); |
f1d3d38a SH |
257 | else if (mac_type == CHBT_MAC_VSC7321) |
258 | espi_setup_for_vsc7321(adapter); | |
259 | else if (mac_type == CHBT_MAC_IXF1010) { | |
260 | status_enable_extra = F_INTEL1010MODE; | |
261 | espi_setup_for_ixf1010(adapter, nports); | |
262 | } else | |
8199d3a7 | 263 | return -1; |
8199d3a7 | 264 | |
559fb51b SB |
265 | writel(status_enable_extra | F_RXSTATUSENABLE, |
266 | adapter->regs + A_ESPI_FIFO_STATUS_ENABLE); | |
8199d3a7 CL |
267 | |
268 | if (is_T2(adapter)) { | |
269 | tricn_init(adapter); | |
270 | /* | |
271 | * Always position the control at the 1st port egress IN | |
272 | * (sop,eop) counter to reduce PIOs for T/N210 workaround. | |
273 | */ | |
f1d3d38a SH |
274 | espi->misc_ctrl = readl(adapter->regs + A_ESPI_MISC_CONTROL); |
275 | espi->misc_ctrl &= ~MON_MASK; | |
276 | espi->misc_ctrl |= F_MONITORED_DIRECTION; | |
277 | if (adapter->params.nports == 1) | |
278 | espi->misc_ctrl |= F_MONITORED_INTERFACE; | |
559fb51b | 279 | writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL); |
8199d3a7 CL |
280 | spin_lock_init(&espi->lock); |
281 | } | |
282 | ||
283 | return 0; | |
284 | } | |
285 | ||
286 | void t1_espi_destroy(struct peespi *espi) | |
287 | { | |
288 | kfree(espi); | |
289 | } | |
290 | ||
291 | struct peespi *t1_espi_create(adapter_t *adapter) | |
292 | { | |
2d66806d | 293 | struct peespi *espi = kzalloc(sizeof(*espi), GFP_KERNEL); |
8199d3a7 CL |
294 | |
295 | if (espi) | |
296 | espi->adapter = adapter; | |
297 | return espi; | |
298 | } | |
299 | ||
68d579fb | 300 | #if 0 |
8199d3a7 CL |
301 | void t1_espi_set_misc_ctrl(adapter_t *adapter, u32 val) |
302 | { | |
303 | struct peespi *espi = adapter->espi; | |
304 | ||
c697f83e FR |
305 | if (!is_T2(adapter)) |
306 | return; | |
8199d3a7 CL |
307 | spin_lock(&espi->lock); |
308 | espi->misc_ctrl = (val & ~MON_MASK) | | |
309 | (espi->misc_ctrl & MON_MASK); | |
559fb51b | 310 | writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL); |
8199d3a7 CL |
311 | spin_unlock(&espi->lock); |
312 | } | |
68d579fb | 313 | #endif /* 0 */ |
8199d3a7 CL |
314 | |
315 | u32 t1_espi_get_mon(adapter_t *adapter, u32 addr, u8 wait) | |
316 | { | |
559fb51b | 317 | struct peespi *espi = adapter->espi; |
f1d3d38a | 318 | u32 sel; |
559fb51b | 319 | |
8199d3a7 CL |
320 | if (!is_T2(adapter)) |
321 | return 0; | |
f1d3d38a | 322 | |
8199d3a7 CL |
323 | sel = V_MONITORED_PORT_NUM((addr & 0x3c) >> 2); |
324 | if (!wait) { | |
325 | if (!spin_trylock(&espi->lock)) | |
326 | return 0; | |
f1d3d38a | 327 | } else |
8199d3a7 | 328 | spin_lock(&espi->lock); |
f1d3d38a | 329 | |
8199d3a7 | 330 | if ((sel != (espi->misc_ctrl & MON_MASK))) { |
559fb51b SB |
331 | writel(((espi->misc_ctrl & ~MON_MASK) | sel), |
332 | adapter->regs + A_ESPI_MISC_CONTROL); | |
333 | sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3); | |
334 | writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL); | |
f1d3d38a | 335 | } else |
559fb51b | 336 | sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3); |
8199d3a7 CL |
337 | spin_unlock(&espi->lock); |
338 | return sel; | |
339 | } | |
f1d3d38a SH |
340 | |
341 | /* | |
342 | * This function is for T204 only. | |
343 | * compare with t1_espi_get_mon(), it reads espiInTxSop[0 ~ 3] in | |
344 | * one shot, since there is no per port counter on the out side. | |
345 | */ | |
356bd146 | 346 | int t1_espi_get_mon_t204(adapter_t *adapter, u32 *valp, u8 wait) |
f1d3d38a | 347 | { |
356bd146 | 348 | struct peespi *espi = adapter->espi; |
f1d3d38a SH |
349 | u8 i, nport = (u8)adapter->params.nports; |
350 | ||
356bd146 FR |
351 | if (!wait) { |
352 | if (!spin_trylock(&espi->lock)) | |
353 | return -1; | |
354 | } else | |
355 | spin_lock(&espi->lock); | |
f1d3d38a | 356 | |
356bd146 | 357 | if ((espi->misc_ctrl & MON_MASK) != F_MONITORED_DIRECTION) { |
f1d3d38a SH |
358 | espi->misc_ctrl = (espi->misc_ctrl & ~MON_MASK) | |
359 | F_MONITORED_DIRECTION; | |
356bd146 FR |
360 | writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL); |
361 | } | |
f1d3d38a SH |
362 | for (i = 0 ; i < nport; i++, valp++) { |
363 | if (i) { | |
364 | writel(espi->misc_ctrl | V_MONITORED_PORT_NUM(i), | |
365 | adapter->regs + A_ESPI_MISC_CONTROL); | |
366 | } | |
356bd146 FR |
367 | *valp = readl(adapter->regs + A_ESPI_SCH_TOKEN3); |
368 | } | |
f1d3d38a | 369 | |
356bd146 FR |
370 | writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL); |
371 | spin_unlock(&espi->lock); | |
372 | return 0; | |
f1d3d38a | 373 | } |