]>
Commit | Line | Data |
---|---|---|
80ff0fd3 DD |
1 | /********************************************************************** |
2 | * Author: Cavium Networks | |
3 | * | |
4 | * Contact: support@caviumnetworks.com | |
5 | * This file is part of the OCTEON SDK | |
6 | * | |
7 | * Copyright (c) 2003-2007 Cavium Networks | |
8 | * | |
9 | * This file is free software; you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License, Version 2, as | |
11 | * published by the Free Software Foundation. | |
12 | * | |
13 | * This file is distributed in the hope that it will be useful, but | |
14 | * AS-IS and WITHOUT ANY WARRANTY; without even the implied warranty | |
15 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, TITLE, or | |
16 | * NONINFRINGEMENT. See the GNU General Public License for more | |
17 | * details. | |
18 | * | |
19 | * You should have received a copy of the GNU General Public License | |
20 | * along with this file; if not, write to the Free Software | |
21 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
22 | * or visit http://www.gnu.org/licenses/. | |
23 | * | |
24 | * This file may also be available under a different license from Cavium. | |
25 | * Contact Cavium Networks for more information | |
26 | **********************************************************************/ | |
27 | #include <linux/kernel.h> | |
28 | #include <linux/netdevice.h> | |
80ff0fd3 DD |
29 | #include <net/dst.h> |
30 | ||
31 | #include <asm/octeon/octeon.h> | |
32 | ||
33 | #include "ethernet-defines.h" | |
34 | #include "octeon-ethernet.h" | |
80ff0fd3 DD |
35 | #include "ethernet-util.h" |
36 | ||
37 | #include "cvmx-spi.h" | |
38 | ||
39 | #include <asm/octeon/cvmx-npi-defs.h> | |
40 | #include "cvmx-spxx-defs.h" | |
41 | #include "cvmx-stxx-defs.h" | |
42 | ||
43 | static int number_spi_ports; | |
44 | static int need_retrain[2] = { 0, 0 }; | |
45 | ||
46 | static irqreturn_t cvm_oct_spi_rml_interrupt(int cpl, void *dev_id) | |
47 | { | |
48 | irqreturn_t return_status = IRQ_NONE; | |
49 | union cvmx_npi_rsl_int_blocks rsl_int_blocks; | |
50 | ||
51 | /* Check and see if this interrupt was caused by the GMX block */ | |
52 | rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS); | |
53 | if (rsl_int_blocks.s.spx1) { /* 19 - SPX1_INT_REG & STX1_INT_REG */ | |
54 | ||
55 | union cvmx_spxx_int_reg spx_int_reg; | |
56 | union cvmx_stxx_int_reg stx_int_reg; | |
57 | ||
58 | spx_int_reg.u64 = cvmx_read_csr(CVMX_SPXX_INT_REG(1)); | |
59 | cvmx_write_csr(CVMX_SPXX_INT_REG(1), spx_int_reg.u64); | |
60 | if (!need_retrain[1]) { | |
61 | ||
62 | spx_int_reg.u64 &= cvmx_read_csr(CVMX_SPXX_INT_MSK(1)); | |
63 | if (spx_int_reg.s.spf) | |
64 | pr_err("SPI1: SRX Spi4 interface down\n"); | |
65 | if (spx_int_reg.s.calerr) | |
66 | pr_err("SPI1: SRX Spi4 Calendar table " | |
67 | "parity error\n"); | |
68 | if (spx_int_reg.s.syncerr) | |
69 | pr_err("SPI1: SRX Consecutive Spi4 DIP4 " | |
70 | "errors have exceeded " | |
71 | "SPX_ERR_CTL[ERRCNT]\n"); | |
72 | if (spx_int_reg.s.diperr) | |
73 | pr_err("SPI1: SRX Spi4 DIP4 error\n"); | |
74 | if (spx_int_reg.s.tpaovr) | |
75 | pr_err("SPI1: SRX Selected port has hit " | |
76 | "TPA overflow\n"); | |
77 | if (spx_int_reg.s.rsverr) | |
78 | pr_err("SPI1: SRX Spi4 reserved control " | |
79 | "word detected\n"); | |
80 | if (spx_int_reg.s.drwnng) | |
81 | pr_err("SPI1: SRX Spi4 receive FIFO " | |
82 | "drowning/overflow\n"); | |
83 | if (spx_int_reg.s.clserr) | |
84 | pr_err("SPI1: SRX Spi4 packet closed on " | |
85 | "non-16B alignment without EOP\n"); | |
86 | if (spx_int_reg.s.spiovr) | |
87 | pr_err("SPI1: SRX Spi4 async FIFO overflow\n"); | |
88 | if (spx_int_reg.s.abnorm) | |
89 | pr_err("SPI1: SRX Abnormal packet " | |
90 | "termination (ERR bit)\n"); | |
91 | if (spx_int_reg.s.prtnxa) | |
92 | pr_err("SPI1: SRX Port out of range\n"); | |
93 | } | |
94 | ||
95 | stx_int_reg.u64 = cvmx_read_csr(CVMX_STXX_INT_REG(1)); | |
96 | cvmx_write_csr(CVMX_STXX_INT_REG(1), stx_int_reg.u64); | |
97 | if (!need_retrain[1]) { | |
98 | ||
99 | stx_int_reg.u64 &= cvmx_read_csr(CVMX_STXX_INT_MSK(1)); | |
100 | if (stx_int_reg.s.syncerr) | |
101 | pr_err("SPI1: STX Interface encountered a " | |
102 | "fatal error\n"); | |
103 | if (stx_int_reg.s.frmerr) | |
104 | pr_err("SPI1: STX FRMCNT has exceeded " | |
105 | "STX_DIP_CNT[MAXFRM]\n"); | |
106 | if (stx_int_reg.s.unxfrm) | |
107 | pr_err("SPI1: STX Unexpected framing " | |
108 | "sequence\n"); | |
109 | if (stx_int_reg.s.nosync) | |
110 | pr_err("SPI1: STX ERRCNT has exceeded " | |
111 | "STX_DIP_CNT[MAXDIP]\n"); | |
112 | if (stx_int_reg.s.diperr) | |
113 | pr_err("SPI1: STX DIP2 error on the Spi4 " | |
114 | "Status channel\n"); | |
115 | if (stx_int_reg.s.datovr) | |
116 | pr_err("SPI1: STX Spi4 FIFO overflow error\n"); | |
117 | if (stx_int_reg.s.ovrbst) | |
118 | pr_err("SPI1: STX Transmit packet burst " | |
119 | "too big\n"); | |
120 | if (stx_int_reg.s.calpar1) | |
121 | pr_err("SPI1: STX Calendar Table Parity " | |
122 | "Error Bank1\n"); | |
123 | if (stx_int_reg.s.calpar0) | |
124 | pr_err("SPI1: STX Calendar Table Parity " | |
125 | "Error Bank0\n"); | |
126 | } | |
127 | ||
128 | cvmx_write_csr(CVMX_SPXX_INT_MSK(1), 0); | |
129 | cvmx_write_csr(CVMX_STXX_INT_MSK(1), 0); | |
130 | need_retrain[1] = 1; | |
131 | return_status = IRQ_HANDLED; | |
132 | } | |
133 | ||
134 | if (rsl_int_blocks.s.spx0) { /* 18 - SPX0_INT_REG & STX0_INT_REG */ | |
135 | union cvmx_spxx_int_reg spx_int_reg; | |
136 | union cvmx_stxx_int_reg stx_int_reg; | |
137 | ||
138 | spx_int_reg.u64 = cvmx_read_csr(CVMX_SPXX_INT_REG(0)); | |
139 | cvmx_write_csr(CVMX_SPXX_INT_REG(0), spx_int_reg.u64); | |
140 | if (!need_retrain[0]) { | |
141 | ||
142 | spx_int_reg.u64 &= cvmx_read_csr(CVMX_SPXX_INT_MSK(0)); | |
143 | if (spx_int_reg.s.spf) | |
144 | pr_err("SPI0: SRX Spi4 interface down\n"); | |
145 | if (spx_int_reg.s.calerr) | |
146 | pr_err("SPI0: SRX Spi4 Calendar table " | |
147 | "parity error\n"); | |
148 | if (spx_int_reg.s.syncerr) | |
149 | pr_err("SPI0: SRX Consecutive Spi4 DIP4 " | |
150 | "errors have exceeded " | |
151 | "SPX_ERR_CTL[ERRCNT]\n"); | |
152 | if (spx_int_reg.s.diperr) | |
153 | pr_err("SPI0: SRX Spi4 DIP4 error\n"); | |
154 | if (spx_int_reg.s.tpaovr) | |
155 | pr_err("SPI0: SRX Selected port has hit " | |
156 | "TPA overflow\n"); | |
157 | if (spx_int_reg.s.rsverr) | |
158 | pr_err("SPI0: SRX Spi4 reserved control " | |
159 | "word detected\n"); | |
160 | if (spx_int_reg.s.drwnng) | |
161 | pr_err("SPI0: SRX Spi4 receive FIFO " | |
162 | "drowning/overflow\n"); | |
163 | if (spx_int_reg.s.clserr) | |
164 | pr_err("SPI0: SRX Spi4 packet closed on " | |
165 | "non-16B alignment without EOP\n"); | |
166 | if (spx_int_reg.s.spiovr) | |
167 | pr_err("SPI0: SRX Spi4 async FIFO overflow\n"); | |
168 | if (spx_int_reg.s.abnorm) | |
169 | pr_err("SPI0: SRX Abnormal packet " | |
170 | "termination (ERR bit)\n"); | |
171 | if (spx_int_reg.s.prtnxa) | |
172 | pr_err("SPI0: SRX Port out of range\n"); | |
173 | } | |
174 | ||
175 | stx_int_reg.u64 = cvmx_read_csr(CVMX_STXX_INT_REG(0)); | |
176 | cvmx_write_csr(CVMX_STXX_INT_REG(0), stx_int_reg.u64); | |
177 | if (!need_retrain[0]) { | |
178 | ||
179 | stx_int_reg.u64 &= cvmx_read_csr(CVMX_STXX_INT_MSK(0)); | |
180 | if (stx_int_reg.s.syncerr) | |
181 | pr_err("SPI0: STX Interface encountered a " | |
182 | "fatal error\n"); | |
183 | if (stx_int_reg.s.frmerr) | |
184 | pr_err("SPI0: STX FRMCNT has exceeded " | |
185 | "STX_DIP_CNT[MAXFRM]\n"); | |
186 | if (stx_int_reg.s.unxfrm) | |
187 | pr_err("SPI0: STX Unexpected framing " | |
188 | "sequence\n"); | |
189 | if (stx_int_reg.s.nosync) | |
190 | pr_err("SPI0: STX ERRCNT has exceeded " | |
191 | "STX_DIP_CNT[MAXDIP]\n"); | |
192 | if (stx_int_reg.s.diperr) | |
193 | pr_err("SPI0: STX DIP2 error on the Spi4 " | |
194 | "Status channel\n"); | |
195 | if (stx_int_reg.s.datovr) | |
196 | pr_err("SPI0: STX Spi4 FIFO overflow error\n"); | |
197 | if (stx_int_reg.s.ovrbst) | |
198 | pr_err("SPI0: STX Transmit packet burst " | |
199 | "too big\n"); | |
200 | if (stx_int_reg.s.calpar1) | |
201 | pr_err("SPI0: STX Calendar Table Parity " | |
202 | "Error Bank1\n"); | |
203 | if (stx_int_reg.s.calpar0) | |
204 | pr_err("SPI0: STX Calendar Table Parity " | |
205 | "Error Bank0\n"); | |
206 | } | |
207 | ||
208 | cvmx_write_csr(CVMX_SPXX_INT_MSK(0), 0); | |
209 | cvmx_write_csr(CVMX_STXX_INT_MSK(0), 0); | |
210 | need_retrain[0] = 1; | |
211 | return_status = IRQ_HANDLED; | |
212 | } | |
213 | ||
214 | return return_status; | |
215 | } | |
216 | ||
217 | static void cvm_oct_spi_enable_error_reporting(int interface) | |
218 | { | |
219 | union cvmx_spxx_int_msk spxx_int_msk; | |
220 | union cvmx_stxx_int_msk stxx_int_msk; | |
221 | ||
222 | spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface)); | |
223 | spxx_int_msk.s.calerr = 1; | |
224 | spxx_int_msk.s.syncerr = 1; | |
225 | spxx_int_msk.s.diperr = 1; | |
226 | spxx_int_msk.s.tpaovr = 1; | |
227 | spxx_int_msk.s.rsverr = 1; | |
228 | spxx_int_msk.s.drwnng = 1; | |
229 | spxx_int_msk.s.clserr = 1; | |
230 | spxx_int_msk.s.spiovr = 1; | |
231 | spxx_int_msk.s.abnorm = 1; | |
232 | spxx_int_msk.s.prtnxa = 1; | |
233 | cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64); | |
234 | ||
235 | stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface)); | |
236 | stxx_int_msk.s.frmerr = 1; | |
237 | stxx_int_msk.s.unxfrm = 1; | |
238 | stxx_int_msk.s.nosync = 1; | |
239 | stxx_int_msk.s.diperr = 1; | |
240 | stxx_int_msk.s.datovr = 1; | |
241 | stxx_int_msk.s.ovrbst = 1; | |
242 | stxx_int_msk.s.calpar1 = 1; | |
243 | stxx_int_msk.s.calpar0 = 1; | |
244 | cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64); | |
245 | } | |
246 | ||
247 | static void cvm_oct_spi_poll(struct net_device *dev) | |
248 | { | |
249 | static int spi4000_port; | |
250 | struct octeon_ethernet *priv = netdev_priv(dev); | |
251 | int interface; | |
252 | ||
253 | for (interface = 0; interface < 2; interface++) { | |
254 | ||
255 | if ((priv->port == interface * 16) && need_retrain[interface]) { | |
256 | ||
257 | if (cvmx_spi_restart_interface | |
258 | (interface, CVMX_SPI_MODE_DUPLEX, 10) == 0) { | |
259 | need_retrain[interface] = 0; | |
260 | cvm_oct_spi_enable_error_reporting(interface); | |
261 | } | |
262 | } | |
263 | ||
264 | /* | |
265 | * The SPI4000 TWSI interface is very slow. In order | |
266 | * not to bring the system to a crawl, we only poll a | |
267 | * single port every second. This means negotiation | |
268 | * speed changes take up to 10 seconds, but at least | |
269 | * we don't waste absurd amounts of time waiting for | |
270 | * TWSI. | |
271 | */ | |
272 | if (priv->port == spi4000_port) { | |
273 | /* | |
274 | * This function does nothing if it is called on an | |
275 | * interface without a SPI4000. | |
276 | */ | |
277 | cvmx_spi4000_check_speed(interface, priv->port); | |
278 | /* | |
279 | * Normal ordering increments. By decrementing | |
280 | * we only match once per iteration. | |
281 | */ | |
282 | spi4000_port--; | |
283 | if (spi4000_port < 0) | |
284 | spi4000_port = 10; | |
285 | } | |
286 | } | |
287 | } | |
288 | ||
289 | int cvm_oct_spi_init(struct net_device *dev) | |
290 | { | |
291 | int r; | |
292 | struct octeon_ethernet *priv = netdev_priv(dev); | |
293 | ||
294 | if (number_spi_ports == 0) { | |
295 | r = request_irq(OCTEON_IRQ_RML, cvm_oct_spi_rml_interrupt, | |
296 | IRQF_SHARED, "SPI", &number_spi_ports); | |
94f5659c KV |
297 | if (r) |
298 | return r; | |
80ff0fd3 DD |
299 | } |
300 | number_spi_ports++; | |
301 | ||
302 | if ((priv->port == 0) || (priv->port == 16)) { | |
303 | cvm_oct_spi_enable_error_reporting(INTERFACE(priv->port)); | |
304 | priv->poll = cvm_oct_spi_poll; | |
305 | } | |
306 | cvm_oct_common_init(dev); | |
307 | return 0; | |
308 | } | |
309 | ||
310 | void cvm_oct_spi_uninit(struct net_device *dev) | |
311 | { | |
312 | int interface; | |
313 | ||
314 | cvm_oct_common_uninit(dev); | |
315 | number_spi_ports--; | |
316 | if (number_spi_ports == 0) { | |
317 | for (interface = 0; interface < 2; interface++) { | |
318 | cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0); | |
319 | cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0); | |
320 | } | |
aabb89d6 | 321 | free_irq(OCTEON_IRQ_RML, &number_spi_ports); |
80ff0fd3 DD |
322 | } |
323 | } |