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