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> | |
048316be | 29 | #include <linux/interrupt.h> |
80ff0fd3 DD |
30 | #include <net/dst.h> |
31 | ||
32 | #include <asm/octeon/octeon.h> | |
33 | ||
34 | #include "ethernet-defines.h" | |
35 | #include "octeon-ethernet.h" | |
80ff0fd3 DD |
36 | #include "ethernet-util.h" |
37 | ||
af866496 | 38 | #include <asm/octeon/cvmx-spi.h> |
80ff0fd3 DD |
39 | |
40 | #include <asm/octeon/cvmx-npi-defs.h> | |
af866496 DD |
41 | #include <asm/octeon/cvmx-spxx-defs.h> |
42 | #include <asm/octeon/cvmx-stxx-defs.h> | |
80ff0fd3 DD |
43 | |
44 | static int number_spi_ports; | |
45 | static int need_retrain[2] = { 0, 0 }; | |
46 | ||
47 | static irqreturn_t cvm_oct_spi_rml_interrupt(int cpl, void *dev_id) | |
48 | { | |
49 | irqreturn_t return_status = IRQ_NONE; | |
50 | union cvmx_npi_rsl_int_blocks rsl_int_blocks; | |
51 | ||
52 | /* Check and see if this interrupt was caused by the GMX block */ | |
53 | rsl_int_blocks.u64 = cvmx_read_csr(CVMX_NPI_RSL_INT_BLOCKS); | |
54 | if (rsl_int_blocks.s.spx1) { /* 19 - SPX1_INT_REG & STX1_INT_REG */ | |
55 | ||
56 | union cvmx_spxx_int_reg spx_int_reg; | |
57 | union cvmx_stxx_int_reg stx_int_reg; | |
58 | ||
59 | spx_int_reg.u64 = cvmx_read_csr(CVMX_SPXX_INT_REG(1)); | |
60 | cvmx_write_csr(CVMX_SPXX_INT_REG(1), spx_int_reg.u64); | |
61 | if (!need_retrain[1]) { | |
62 | ||
63 | spx_int_reg.u64 &= cvmx_read_csr(CVMX_SPXX_INT_MSK(1)); | |
64 | if (spx_int_reg.s.spf) | |
65 | pr_err("SPI1: SRX Spi4 interface down\n"); | |
66 | if (spx_int_reg.s.calerr) | |
d7c9fde4 | 67 | pr_err("SPI1: SRX Spi4 Calendar table parity error\n"); |
80ff0fd3 | 68 | if (spx_int_reg.s.syncerr) |
d7c9fde4 | 69 | pr_err("SPI1: SRX Consecutive Spi4 DIP4 errors have exceeded SPX_ERR_CTL[ERRCNT]\n"); |
80ff0fd3 DD |
70 | if (spx_int_reg.s.diperr) |
71 | pr_err("SPI1: SRX Spi4 DIP4 error\n"); | |
72 | if (spx_int_reg.s.tpaovr) | |
d7c9fde4 | 73 | pr_err("SPI1: SRX Selected port has hit TPA overflow\n"); |
80ff0fd3 | 74 | if (spx_int_reg.s.rsverr) |
d7c9fde4 | 75 | pr_err("SPI1: SRX Spi4 reserved control word detected\n"); |
80ff0fd3 | 76 | if (spx_int_reg.s.drwnng) |
d7c9fde4 | 77 | pr_err("SPI1: SRX Spi4 receive FIFO drowning/overflow\n"); |
80ff0fd3 | 78 | if (spx_int_reg.s.clserr) |
d7c9fde4 | 79 | pr_err("SPI1: SRX Spi4 packet closed on non-16B alignment without EOP\n"); |
80ff0fd3 DD |
80 | if (spx_int_reg.s.spiovr) |
81 | pr_err("SPI1: SRX Spi4 async FIFO overflow\n"); | |
82 | if (spx_int_reg.s.abnorm) | |
d7c9fde4 | 83 | pr_err("SPI1: SRX Abnormal packet termination (ERR bit)\n"); |
80ff0fd3 DD |
84 | if (spx_int_reg.s.prtnxa) |
85 | pr_err("SPI1: SRX Port out of range\n"); | |
86 | } | |
87 | ||
88 | stx_int_reg.u64 = cvmx_read_csr(CVMX_STXX_INT_REG(1)); | |
89 | cvmx_write_csr(CVMX_STXX_INT_REG(1), stx_int_reg.u64); | |
90 | if (!need_retrain[1]) { | |
91 | ||
92 | stx_int_reg.u64 &= cvmx_read_csr(CVMX_STXX_INT_MSK(1)); | |
93 | if (stx_int_reg.s.syncerr) | |
d7c9fde4 | 94 | pr_err("SPI1: STX Interface encountered a fatal error\n"); |
80ff0fd3 | 95 | if (stx_int_reg.s.frmerr) |
d7c9fde4 | 96 | pr_err("SPI1: STX FRMCNT has exceeded STX_DIP_CNT[MAXFRM]\n"); |
80ff0fd3 | 97 | if (stx_int_reg.s.unxfrm) |
d7c9fde4 | 98 | pr_err("SPI1: STX Unexpected framing sequence\n"); |
80ff0fd3 | 99 | if (stx_int_reg.s.nosync) |
d7c9fde4 | 100 | pr_err("SPI1: STX ERRCNT has exceeded STX_DIP_CNT[MAXDIP]\n"); |
80ff0fd3 | 101 | if (stx_int_reg.s.diperr) |
d7c9fde4 | 102 | pr_err("SPI1: STX DIP2 error on the Spi4 Status channel\n"); |
80ff0fd3 DD |
103 | if (stx_int_reg.s.datovr) |
104 | pr_err("SPI1: STX Spi4 FIFO overflow error\n"); | |
105 | if (stx_int_reg.s.ovrbst) | |
d7c9fde4 | 106 | pr_err("SPI1: STX Transmit packet burst too big\n"); |
80ff0fd3 | 107 | if (stx_int_reg.s.calpar1) |
d7c9fde4 | 108 | pr_err("SPI1: STX Calendar Table Parity Error Bank1\n"); |
80ff0fd3 | 109 | if (stx_int_reg.s.calpar0) |
d7c9fde4 | 110 | pr_err("SPI1: STX Calendar Table Parity Error Bank0\n"); |
80ff0fd3 DD |
111 | } |
112 | ||
113 | cvmx_write_csr(CVMX_SPXX_INT_MSK(1), 0); | |
114 | cvmx_write_csr(CVMX_STXX_INT_MSK(1), 0); | |
115 | need_retrain[1] = 1; | |
116 | return_status = IRQ_HANDLED; | |
117 | } | |
118 | ||
119 | if (rsl_int_blocks.s.spx0) { /* 18 - SPX0_INT_REG & STX0_INT_REG */ | |
120 | union cvmx_spxx_int_reg spx_int_reg; | |
121 | union cvmx_stxx_int_reg stx_int_reg; | |
122 | ||
123 | spx_int_reg.u64 = cvmx_read_csr(CVMX_SPXX_INT_REG(0)); | |
124 | cvmx_write_csr(CVMX_SPXX_INT_REG(0), spx_int_reg.u64); | |
125 | if (!need_retrain[0]) { | |
126 | ||
127 | spx_int_reg.u64 &= cvmx_read_csr(CVMX_SPXX_INT_MSK(0)); | |
128 | if (spx_int_reg.s.spf) | |
129 | pr_err("SPI0: SRX Spi4 interface down\n"); | |
130 | if (spx_int_reg.s.calerr) | |
d7c9fde4 | 131 | pr_err("SPI0: SRX Spi4 Calendar table parity error\n"); |
80ff0fd3 | 132 | if (spx_int_reg.s.syncerr) |
d7c9fde4 | 133 | pr_err("SPI0: SRX Consecutive Spi4 DIP4 errors have exceeded SPX_ERR_CTL[ERRCNT]\n"); |
80ff0fd3 DD |
134 | if (spx_int_reg.s.diperr) |
135 | pr_err("SPI0: SRX Spi4 DIP4 error\n"); | |
136 | if (spx_int_reg.s.tpaovr) | |
d7c9fde4 | 137 | pr_err("SPI0: SRX Selected port has hit TPA overflow\n"); |
80ff0fd3 | 138 | if (spx_int_reg.s.rsverr) |
d7c9fde4 | 139 | pr_err("SPI0: SRX Spi4 reserved control word detected\n"); |
80ff0fd3 | 140 | if (spx_int_reg.s.drwnng) |
d7c9fde4 | 141 | pr_err("SPI0: SRX Spi4 receive FIFO drowning/overflow\n"); |
80ff0fd3 | 142 | if (spx_int_reg.s.clserr) |
d7c9fde4 | 143 | pr_err("SPI0: SRX Spi4 packet closed on non-16B alignment without EOP\n"); |
80ff0fd3 DD |
144 | if (spx_int_reg.s.spiovr) |
145 | pr_err("SPI0: SRX Spi4 async FIFO overflow\n"); | |
146 | if (spx_int_reg.s.abnorm) | |
d7c9fde4 | 147 | pr_err("SPI0: SRX Abnormal packet termination (ERR bit)\n"); |
80ff0fd3 DD |
148 | if (spx_int_reg.s.prtnxa) |
149 | pr_err("SPI0: SRX Port out of range\n"); | |
150 | } | |
151 | ||
152 | stx_int_reg.u64 = cvmx_read_csr(CVMX_STXX_INT_REG(0)); | |
153 | cvmx_write_csr(CVMX_STXX_INT_REG(0), stx_int_reg.u64); | |
154 | if (!need_retrain[0]) { | |
155 | ||
156 | stx_int_reg.u64 &= cvmx_read_csr(CVMX_STXX_INT_MSK(0)); | |
157 | if (stx_int_reg.s.syncerr) | |
d7c9fde4 | 158 | pr_err("SPI0: STX Interface encountered a fatal error\n"); |
80ff0fd3 | 159 | if (stx_int_reg.s.frmerr) |
d7c9fde4 | 160 | pr_err("SPI0: STX FRMCNT has exceeded STX_DIP_CNT[MAXFRM]\n"); |
80ff0fd3 | 161 | if (stx_int_reg.s.unxfrm) |
d7c9fde4 | 162 | pr_err("SPI0: STX Unexpected framing sequence\n"); |
80ff0fd3 | 163 | if (stx_int_reg.s.nosync) |
d7c9fde4 | 164 | pr_err("SPI0: STX ERRCNT has exceeded STX_DIP_CNT[MAXDIP]\n"); |
80ff0fd3 | 165 | if (stx_int_reg.s.diperr) |
d7c9fde4 | 166 | pr_err("SPI0: STX DIP2 error on the Spi4 Status channel\n"); |
80ff0fd3 DD |
167 | if (stx_int_reg.s.datovr) |
168 | pr_err("SPI0: STX Spi4 FIFO overflow error\n"); | |
169 | if (stx_int_reg.s.ovrbst) | |
d7c9fde4 | 170 | pr_err("SPI0: STX Transmit packet burst too big\n"); |
80ff0fd3 | 171 | if (stx_int_reg.s.calpar1) |
d7c9fde4 | 172 | pr_err("SPI0: STX Calendar Table Parity Error Bank1\n"); |
80ff0fd3 | 173 | if (stx_int_reg.s.calpar0) |
d7c9fde4 | 174 | pr_err("SPI0: STX Calendar Table Parity Error Bank0\n"); |
80ff0fd3 DD |
175 | } |
176 | ||
177 | cvmx_write_csr(CVMX_SPXX_INT_MSK(0), 0); | |
178 | cvmx_write_csr(CVMX_STXX_INT_MSK(0), 0); | |
179 | need_retrain[0] = 1; | |
180 | return_status = IRQ_HANDLED; | |
181 | } | |
182 | ||
183 | return return_status; | |
184 | } | |
185 | ||
186 | static void cvm_oct_spi_enable_error_reporting(int interface) | |
187 | { | |
188 | union cvmx_spxx_int_msk spxx_int_msk; | |
189 | union cvmx_stxx_int_msk stxx_int_msk; | |
190 | ||
191 | spxx_int_msk.u64 = cvmx_read_csr(CVMX_SPXX_INT_MSK(interface)); | |
192 | spxx_int_msk.s.calerr = 1; | |
193 | spxx_int_msk.s.syncerr = 1; | |
194 | spxx_int_msk.s.diperr = 1; | |
195 | spxx_int_msk.s.tpaovr = 1; | |
196 | spxx_int_msk.s.rsverr = 1; | |
197 | spxx_int_msk.s.drwnng = 1; | |
198 | spxx_int_msk.s.clserr = 1; | |
199 | spxx_int_msk.s.spiovr = 1; | |
200 | spxx_int_msk.s.abnorm = 1; | |
201 | spxx_int_msk.s.prtnxa = 1; | |
202 | cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), spxx_int_msk.u64); | |
203 | ||
204 | stxx_int_msk.u64 = cvmx_read_csr(CVMX_STXX_INT_MSK(interface)); | |
205 | stxx_int_msk.s.frmerr = 1; | |
206 | stxx_int_msk.s.unxfrm = 1; | |
207 | stxx_int_msk.s.nosync = 1; | |
208 | stxx_int_msk.s.diperr = 1; | |
209 | stxx_int_msk.s.datovr = 1; | |
210 | stxx_int_msk.s.ovrbst = 1; | |
211 | stxx_int_msk.s.calpar1 = 1; | |
212 | stxx_int_msk.s.calpar0 = 1; | |
213 | cvmx_write_csr(CVMX_STXX_INT_MSK(interface), stxx_int_msk.u64); | |
214 | } | |
215 | ||
216 | static void cvm_oct_spi_poll(struct net_device *dev) | |
217 | { | |
218 | static int spi4000_port; | |
219 | struct octeon_ethernet *priv = netdev_priv(dev); | |
220 | int interface; | |
221 | ||
222 | for (interface = 0; interface < 2; interface++) { | |
223 | ||
224 | if ((priv->port == interface * 16) && need_retrain[interface]) { | |
225 | ||
226 | if (cvmx_spi_restart_interface | |
227 | (interface, CVMX_SPI_MODE_DUPLEX, 10) == 0) { | |
228 | need_retrain[interface] = 0; | |
229 | cvm_oct_spi_enable_error_reporting(interface); | |
230 | } | |
231 | } | |
232 | ||
233 | /* | |
234 | * The SPI4000 TWSI interface is very slow. In order | |
235 | * not to bring the system to a crawl, we only poll a | |
236 | * single port every second. This means negotiation | |
237 | * speed changes take up to 10 seconds, but at least | |
238 | * we don't waste absurd amounts of time waiting for | |
239 | * TWSI. | |
240 | */ | |
241 | if (priv->port == spi4000_port) { | |
242 | /* | |
243 | * This function does nothing if it is called on an | |
244 | * interface without a SPI4000. | |
245 | */ | |
246 | cvmx_spi4000_check_speed(interface, priv->port); | |
247 | /* | |
248 | * Normal ordering increments. By decrementing | |
249 | * we only match once per iteration. | |
250 | */ | |
251 | spi4000_port--; | |
252 | if (spi4000_port < 0) | |
253 | spi4000_port = 10; | |
254 | } | |
255 | } | |
256 | } | |
257 | ||
258 | int cvm_oct_spi_init(struct net_device *dev) | |
259 | { | |
260 | int r; | |
261 | struct octeon_ethernet *priv = netdev_priv(dev); | |
262 | ||
263 | if (number_spi_ports == 0) { | |
264 | r = request_irq(OCTEON_IRQ_RML, cvm_oct_spi_rml_interrupt, | |
265 | IRQF_SHARED, "SPI", &number_spi_ports); | |
94f5659c KV |
266 | if (r) |
267 | return r; | |
80ff0fd3 DD |
268 | } |
269 | number_spi_ports++; | |
270 | ||
271 | if ((priv->port == 0) || (priv->port == 16)) { | |
272 | cvm_oct_spi_enable_error_reporting(INTERFACE(priv->port)); | |
273 | priv->poll = cvm_oct_spi_poll; | |
274 | } | |
275 | cvm_oct_common_init(dev); | |
276 | return 0; | |
277 | } | |
278 | ||
279 | void cvm_oct_spi_uninit(struct net_device *dev) | |
280 | { | |
281 | int interface; | |
282 | ||
283 | cvm_oct_common_uninit(dev); | |
284 | number_spi_ports--; | |
285 | if (number_spi_ports == 0) { | |
286 | for (interface = 0; interface < 2; interface++) { | |
287 | cvmx_write_csr(CVMX_SPXX_INT_MSK(interface), 0); | |
288 | cvmx_write_csr(CVMX_STXX_INT_MSK(interface), 0); | |
289 | } | |
aabb89d6 | 290 | free_irq(OCTEON_IRQ_RML, &number_spi_ports); |
80ff0fd3 DD |
291 | } |
292 | } |