]>
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 | ||
84 | /* 1. Deassert rx_reset_core. */ | |
85 | /* 2. Program TRICN_CNFG registers. */ | |
86 | /* 3. Deassert rx_reset_link */ | |
87 | static int tricn_init(adapter_t *adapter) | |
88 | { | |
89 | int i = 0; | |
90 | int sme = 1; | |
91 | int stat = 0; | |
92 | int timeout = 0; | |
93 | int is_ready = 0; | |
94 | int dynamic_deskew = 0; | |
95 | ||
96 | if (dynamic_deskew) | |
97 | sme = 0; | |
98 | ||
99 | ||
100 | /* 1 */ | |
101 | timeout=1000; | |
102 | do { | |
559fb51b | 103 | stat = readl(adapter->regs + A_ESPI_RX_RESET); |
8199d3a7 CL |
104 | is_ready = (stat & 0x4); |
105 | timeout--; | |
106 | udelay(5); | |
107 | } while (!is_ready || (timeout==0)); | |
559fb51b | 108 | writel(0x2, adapter->regs + A_ESPI_RX_RESET); |
8199d3a7 CL |
109 | if (timeout==0) |
110 | { | |
111 | CH_ERR("ESPI : ERROR : Timeout tricn_init() \n"); | |
112 | t1_fatal_err(adapter); | |
113 | } | |
114 | ||
115 | /* 2 */ | |
116 | if (sme) { | |
117 | tricn_write(adapter, 0, 0, 0, TRICN_CNFG, 0x81); | |
118 | tricn_write(adapter, 0, 1, 0, TRICN_CNFG, 0x81); | |
119 | tricn_write(adapter, 0, 2, 0, TRICN_CNFG, 0x81); | |
120 | } | |
121 | for (i=1; i<= 8; i++) tricn_write(adapter, 0, 0, i, TRICN_CNFG, 0xf1); | |
122 | for (i=1; i<= 2; i++) tricn_write(adapter, 0, 1, i, TRICN_CNFG, 0xf1); | |
123 | for (i=1; i<= 3; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1); | |
124 | for (i=4; i<= 4; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1); | |
125 | for (i=5; i<= 5; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1); | |
126 | for (i=6; i<= 6; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1); | |
127 | for (i=7; i<= 7; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0x80); | |
128 | for (i=8; i<= 8; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1); | |
129 | ||
130 | /* 3 */ | |
559fb51b | 131 | writel(0x3, adapter->regs + A_ESPI_RX_RESET); |
8199d3a7 CL |
132 | |
133 | return 0; | |
134 | } | |
135 | ||
136 | void t1_espi_intr_enable(struct peespi *espi) | |
137 | { | |
559fb51b | 138 | u32 enable, pl_intr = readl(espi->adapter->regs + A_PL_ENABLE); |
8199d3a7 CL |
139 | |
140 | /* | |
141 | * Cannot enable ESPI interrupts on T1B because HW asserts the | |
142 | * interrupt incorrectly, namely the driver gets ESPI interrupts | |
143 | * but no data is actually dropped (can verify this reading the ESPI | |
144 | * drop registers). Also, once the ESPI interrupt is asserted it | |
145 | * cannot be cleared (HW bug). | |
146 | */ | |
147 | enable = t1_is_T1B(espi->adapter) ? 0 : ESPI_INTR_MASK; | |
559fb51b SB |
148 | writel(enable, espi->adapter->regs + A_ESPI_INTR_ENABLE); |
149 | writel(pl_intr | F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE); | |
8199d3a7 CL |
150 | } |
151 | ||
152 | void t1_espi_intr_clear(struct peespi *espi) | |
153 | { | |
559fb51b SB |
154 | writel(0xffffffff, espi->adapter->regs + A_ESPI_INTR_STATUS); |
155 | writel(F_PL_INTR_ESPI, espi->adapter->regs + A_PL_CAUSE); | |
8199d3a7 CL |
156 | } |
157 | ||
158 | void t1_espi_intr_disable(struct peespi *espi) | |
159 | { | |
559fb51b | 160 | u32 pl_intr = readl(espi->adapter->regs + A_PL_ENABLE); |
8199d3a7 | 161 | |
559fb51b SB |
162 | writel(0, espi->adapter->regs + A_ESPI_INTR_ENABLE); |
163 | writel(pl_intr & ~F_PL_INTR_ESPI, espi->adapter->regs + A_PL_ENABLE); | |
8199d3a7 CL |
164 | } |
165 | ||
166 | int t1_espi_intr_handler(struct peespi *espi) | |
167 | { | |
168 | u32 cnt; | |
559fb51b | 169 | u32 status = readl(espi->adapter->regs + A_ESPI_INTR_STATUS); |
8199d3a7 CL |
170 | |
171 | if (status & F_DIP4ERR) | |
172 | espi->intr_cnt.DIP4_err++; | |
173 | if (status & F_RXDROP) | |
174 | espi->intr_cnt.rx_drops++; | |
175 | if (status & F_TXDROP) | |
176 | espi->intr_cnt.tx_drops++; | |
177 | if (status & F_RXOVERFLOW) | |
178 | espi->intr_cnt.rx_ovflw++; | |
179 | if (status & F_RAMPARITYERR) | |
180 | espi->intr_cnt.parity_err++; | |
181 | if (status & F_DIP2PARITYERR) { | |
182 | espi->intr_cnt.DIP2_parity_err++; | |
183 | ||
184 | /* | |
185 | * Must read the error count to clear the interrupt | |
186 | * that it causes. | |
187 | */ | |
559fb51b | 188 | cnt = readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT); |
8199d3a7 CL |
189 | } |
190 | ||
191 | /* | |
192 | * For T1B we need to write 1 to clear ESPI interrupts. For T2+ we | |
193 | * write the status as is. | |
194 | */ | |
195 | if (status && t1_is_T1B(espi->adapter)) | |
196 | status = 1; | |
559fb51b | 197 | writel(status, espi->adapter->regs + A_ESPI_INTR_STATUS); |
8199d3a7 CL |
198 | return 0; |
199 | } | |
200 | ||
559fb51b | 201 | const struct espi_intr_counts *t1_espi_get_intr_counts(struct peespi *espi) |
8199d3a7 | 202 | { |
559fb51b | 203 | return &espi->intr_cnt; |
8199d3a7 CL |
204 | } |
205 | ||
559fb51b | 206 | static void espi_setup_for_pm3393(adapter_t *adapter) |
8199d3a7 CL |
207 | { |
208 | u32 wmark = t1_is_T1B(adapter) ? 0x4000 : 0x3200; | |
209 | ||
559fb51b SB |
210 | writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN0); |
211 | writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN1); | |
212 | writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN2); | |
213 | writel(0x1f4, adapter->regs + A_ESPI_SCH_TOKEN3); | |
214 | writel(0x100, adapter->regs + A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK); | |
215 | writel(wmark, adapter->regs + A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK); | |
216 | writel(3, adapter->regs + A_ESPI_CALENDAR_LENGTH); | |
217 | writel(0x08000008, adapter->regs + A_ESPI_TRAIN); | |
218 | writel(V_RX_NPORTS(1) | V_TX_NPORTS(1), adapter->regs + A_PORT_CONFIG); | |
8199d3a7 CL |
219 | } |
220 | ||
221 | /* T2 Init part -- */ | |
222 | /* 1. Set T_ESPI_MISCCTRL_ADDR */ | |
223 | /* 2. Init ESPI registers. */ | |
224 | /* 3. Init TriCN Hard Macro */ | |
225 | int t1_espi_init(struct peespi *espi, int mac_type, int nports) | |
226 | { | |
559fb51b SB |
227 | u32 cnt; |
228 | ||
8199d3a7 CL |
229 | u32 status_enable_extra = 0; |
230 | adapter_t *adapter = espi->adapter; | |
8199d3a7 CL |
231 | u32 status, burstval = 0x800100; |
232 | ||
233 | /* Disable ESPI training. MACs that can handle it enable it below. */ | |
559fb51b | 234 | writel(0, adapter->regs + A_ESPI_TRAIN); |
8199d3a7 CL |
235 | |
236 | if (is_T2(adapter)) { | |
559fb51b SB |
237 | writel(V_OUT_OF_SYNC_COUNT(4) | |
238 | V_DIP2_PARITY_ERR_THRES(3) | | |
239 | V_DIP4_THRES(1), adapter->regs + A_ESPI_MISC_CONTROL); | |
8199d3a7 CL |
240 | if (nports == 4) { |
241 | /* T204: maxburst1 = 0x40, maxburst2 = 0x20 */ | |
242 | burstval = 0x200040; | |
243 | } | |
244 | } | |
559fb51b | 245 | writel(burstval, adapter->regs + A_ESPI_MAXBURST1_MAXBURST2); |
8199d3a7 | 246 | |
559fb51b SB |
247 | switch (mac_type) { |
248 | case CHBT_MAC_PM3393: | |
8199d3a7 | 249 | espi_setup_for_pm3393(adapter); |
559fb51b SB |
250 | break; |
251 | default: | |
8199d3a7 | 252 | return -1; |
559fb51b | 253 | } |
8199d3a7 CL |
254 | |
255 | /* | |
256 | * Make sure any pending interrupts from the SPI are | |
257 | * Cleared before enabling the interrupt. | |
258 | */ | |
559fb51b SB |
259 | writel(ESPI_INTR_MASK, espi->adapter->regs + A_ESPI_INTR_ENABLE); |
260 | status = readl(espi->adapter->regs + A_ESPI_INTR_STATUS); | |
8199d3a7 | 261 | if (status & F_DIP2PARITYERR) { |
559fb51b | 262 | cnt = readl(espi->adapter->regs + A_ESPI_DIP2_ERR_COUNT); |
8199d3a7 CL |
263 | } |
264 | ||
265 | /* | |
266 | * For T1B we need to write 1 to clear ESPI interrupts. For T2+ we | |
267 | * write the status as is. | |
268 | */ | |
269 | if (status && t1_is_T1B(espi->adapter)) | |
270 | status = 1; | |
559fb51b | 271 | writel(status, espi->adapter->regs + A_ESPI_INTR_STATUS); |
8199d3a7 | 272 | |
559fb51b SB |
273 | writel(status_enable_extra | F_RXSTATUSENABLE, |
274 | adapter->regs + A_ESPI_FIFO_STATUS_ENABLE); | |
8199d3a7 CL |
275 | |
276 | if (is_T2(adapter)) { | |
277 | tricn_init(adapter); | |
278 | /* | |
279 | * Always position the control at the 1st port egress IN | |
280 | * (sop,eop) counter to reduce PIOs for T/N210 workaround. | |
281 | */ | |
559fb51b | 282 | espi->misc_ctrl = (readl(adapter->regs + A_ESPI_MISC_CONTROL) |
8199d3a7 CL |
283 | & ~MON_MASK) | (F_MONITORED_DIRECTION |
284 | | F_MONITORED_INTERFACE); | |
559fb51b | 285 | writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL); |
8199d3a7 CL |
286 | spin_lock_init(&espi->lock); |
287 | } | |
288 | ||
289 | return 0; | |
290 | } | |
291 | ||
292 | void t1_espi_destroy(struct peespi *espi) | |
293 | { | |
294 | kfree(espi); | |
295 | } | |
296 | ||
297 | struct peespi *t1_espi_create(adapter_t *adapter) | |
298 | { | |
299 | struct peespi *espi = kmalloc(sizeof(*espi), GFP_KERNEL); | |
300 | ||
301 | memset(espi, 0, sizeof(*espi)); | |
302 | ||
303 | if (espi) | |
304 | espi->adapter = adapter; | |
305 | return espi; | |
306 | } | |
307 | ||
308 | void t1_espi_set_misc_ctrl(adapter_t *adapter, u32 val) | |
309 | { | |
310 | struct peespi *espi = adapter->espi; | |
311 | ||
312 | if (!is_T2(adapter)) | |
313 | return; | |
314 | spin_lock(&espi->lock); | |
315 | espi->misc_ctrl = (val & ~MON_MASK) | | |
316 | (espi->misc_ctrl & MON_MASK); | |
559fb51b | 317 | writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL); |
8199d3a7 CL |
318 | spin_unlock(&espi->lock); |
319 | } | |
320 | ||
321 | u32 t1_espi_get_mon(adapter_t *adapter, u32 addr, u8 wait) | |
322 | { | |
8199d3a7 CL |
323 | u32 sel; |
324 | ||
559fb51b SB |
325 | struct peespi *espi = adapter->espi; |
326 | ||
8199d3a7 CL |
327 | if (!is_T2(adapter)) |
328 | return 0; | |
329 | sel = V_MONITORED_PORT_NUM((addr & 0x3c) >> 2); | |
330 | if (!wait) { | |
331 | if (!spin_trylock(&espi->lock)) | |
332 | return 0; | |
333 | } | |
334 | else | |
335 | spin_lock(&espi->lock); | |
336 | if ((sel != (espi->misc_ctrl & MON_MASK))) { | |
559fb51b SB |
337 | writel(((espi->misc_ctrl & ~MON_MASK) | sel), |
338 | adapter->regs + A_ESPI_MISC_CONTROL); | |
339 | sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3); | |
340 | writel(espi->misc_ctrl, adapter->regs + A_ESPI_MISC_CONTROL); | |
8199d3a7 CL |
341 | } |
342 | else | |
559fb51b | 343 | sel = readl(adapter->regs + A_ESPI_SCH_TOKEN3); |
8199d3a7 CL |
344 | spin_unlock(&espi->lock); |
345 | return sel; | |
346 | } |