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